From f4e4ac842d9560f57db6e62b0b4fab00380eeaf8 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sat, 6 Oct 2018 15:13:20 -0700 Subject: [PATCH] First cut of split-address table formatter Allows specification of table data in various ways, for 16-bit and 24-bit addresses. Shows a preview so you can see if the addresses look about right. Adds permanent labels at target offsets if none are present. Optionally sets code hints. Works beautifully on the A2-Amper-fdraw example, but needs some additional testing, documentation, etc. Dialog is more complicated that I would have liked, mostly because of 65816 support, but I think it'll do. (issue #10) --- .../AppForms/FormatSplitAddress.Designer.cs | 178 +++++--- SourceGen/AppForms/FormatSplitAddress.cs | 391 +++++++++++++++++- SourceGen/AppForms/ProjectView.cs | 49 ++- SourceGen/DataAnalysis.cs | 2 +- SourceGen/Properties/Resources.Designer.cs | 9 + SourceGen/Properties/Resources.resx | 3 + SourceGen/SymbolTable.cs | 7 +- 7 files changed, 548 insertions(+), 91 deletions(-) diff --git a/SourceGen/AppForms/FormatSplitAddress.Designer.cs b/SourceGen/AppForms/FormatSplitAddress.Designer.cs index 277878d..07ea09f 100644 --- a/SourceGen/AppForms/FormatSplitAddress.Designer.cs +++ b/SourceGen/AppForms/FormatSplitAddress.Designer.cs @@ -23,15 +23,16 @@ /// the contents of this method with the code editor. /// private void InitializeComponent() { - System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem(new string[] { - "123456", + System.Windows.Forms.ListViewItem listViewItem5 = new System.Windows.Forms.ListViewItem(new string[] { + "12/3456", + "+123456", "(+) T_123456"}, -1); this.cancelButton = new System.Windows.Forms.Button(); this.okButton = new System.Windows.Forms.Button(); this.selectionInfoLabel = new System.Windows.Forms.Label(); this.addressCharacteristicsGroup = new System.Windows.Forms.GroupBox(); - this.returnAddrCheckBox = new System.Windows.Forms.CheckBox(); - this.width24Button = new System.Windows.Forms.RadioButton(); + this.pushRtsCheckBox = new System.Windows.Forms.CheckBox(); + this.width24Radio = new System.Windows.Forms.RadioButton(); this.width16Radio = new System.Windows.Forms.RadioButton(); this.lowByteGroupBox = new System.Windows.Forms.GroupBox(); this.lowThirdPartRadio = new System.Windows.Forms.RadioButton(); @@ -51,8 +52,11 @@ this.addrColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.symbolColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.outputPreviewGroupBox = new System.Windows.Forms.GroupBox(); + this.invalidConstantLabel = new System.Windows.Forms.Label(); + this.incompatibleSelectionLabel = new System.Windows.Forms.Label(); this.addCodeHintCheckBox = new System.Windows.Forms.CheckBox(); this.optionsGroupBox = new System.Windows.Forms.GroupBox(); + this.offsetColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.addressCharacteristicsGroup.SuspendLayout(); this.lowByteGroupBox.SuspendLayout(); this.highByteGroupBox.SuspendLayout(); @@ -65,10 +69,10 @@ // this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancelButton.Location = new System.Drawing.Point(479, 465); + this.cancelButton.Location = new System.Drawing.Point(461, 461); this.cancelButton.Name = "cancelButton"; this.cancelButton.Size = new System.Drawing.Size(75, 23); - this.cancelButton.TabIndex = 0; + this.cancelButton.TabIndex = 8; this.cancelButton.Text = "Cancel"; this.cancelButton.UseVisualStyleBackColor = true; // @@ -76,10 +80,10 @@ // this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; - this.okButton.Location = new System.Drawing.Point(398, 465); + this.okButton.Location = new System.Drawing.Point(380, 461); this.okButton.Name = "okButton"; this.okButton.Size = new System.Drawing.Size(75, 23); - this.okButton.TabIndex = 1; + this.okButton.TabIndex = 7; this.okButton.Text = "OK"; this.okButton.UseVisualStyleBackColor = true; this.okButton.Click += new System.EventHandler(this.okButton_Click); @@ -89,42 +93,44 @@ this.selectionInfoLabel.AutoSize = true; this.selectionInfoLabel.Location = new System.Drawing.Point(13, 13); this.selectionInfoLabel.Name = "selectionInfoLabel"; - this.selectionInfoLabel.Size = new System.Drawing.Size(207, 13); - this.selectionInfoLabel.TabIndex = 2; - this.selectionInfoLabel.Text = "There are {0} bytes selected in {1} groups."; + this.selectionInfoLabel.Size = new System.Drawing.Size(213, 13); + this.selectionInfoLabel.TabIndex = 0; + this.selectionInfoLabel.Text = "There are {0} bytes selected, in {1} group(s)"; // // addressCharacteristicsGroup // - this.addressCharacteristicsGroup.Controls.Add(this.returnAddrCheckBox); - this.addressCharacteristicsGroup.Controls.Add(this.width24Button); + this.addressCharacteristicsGroup.Controls.Add(this.pushRtsCheckBox); + this.addressCharacteristicsGroup.Controls.Add(this.width24Radio); this.addressCharacteristicsGroup.Controls.Add(this.width16Radio); - this.addressCharacteristicsGroup.Location = new System.Drawing.Point(16, 44); + this.addressCharacteristicsGroup.Location = new System.Drawing.Point(16, 40); this.addressCharacteristicsGroup.Name = "addressCharacteristicsGroup"; - this.addressCharacteristicsGroup.Size = new System.Drawing.Size(222, 90); - this.addressCharacteristicsGroup.TabIndex = 3; + this.addressCharacteristicsGroup.Size = new System.Drawing.Size(204, 90); + this.addressCharacteristicsGroup.TabIndex = 1; this.addressCharacteristicsGroup.TabStop = false; this.addressCharacteristicsGroup.Text = "Address Characteristics"; // - // returnAddrCheckBox + // pushRtsCheckBox // - this.returnAddrCheckBox.AutoSize = true; - this.returnAddrCheckBox.Location = new System.Drawing.Point(7, 68); - this.returnAddrCheckBox.Name = "returnAddrCheckBox"; - this.returnAddrCheckBox.Size = new System.Drawing.Size(170, 17); - this.returnAddrCheckBox.TabIndex = 2; - this.returnAddrCheckBox.Text = "Return addresses (address - 1)"; - this.returnAddrCheckBox.UseVisualStyleBackColor = true; + this.pushRtsCheckBox.AutoSize = true; + this.pushRtsCheckBox.Location = new System.Drawing.Point(7, 68); + this.pushRtsCheckBox.Name = "pushRtsCheckBox"; + this.pushRtsCheckBox.Size = new System.Drawing.Size(167, 17); + this.pushRtsCheckBox.TabIndex = 2; + this.pushRtsCheckBox.Text = "Push for RTS/RTL (target - 1)"; + this.pushRtsCheckBox.UseVisualStyleBackColor = true; + this.pushRtsCheckBox.CheckedChanged += new System.EventHandler(this.pushRtsCheckBox_CheckedChanged); // - // width24Button + // width24Radio // - this.width24Button.AutoSize = true; - this.width24Button.Location = new System.Drawing.Point(7, 44); - this.width24Button.Name = "width24Button"; - this.width24Button.Size = new System.Drawing.Size(51, 17); - this.width24Button.TabIndex = 1; - this.width24Button.TabStop = true; - this.width24Button.Text = "24-bit"; - this.width24Button.UseVisualStyleBackColor = true; + this.width24Radio.AutoSize = true; + this.width24Radio.Location = new System.Drawing.Point(7, 44); + this.width24Radio.Name = "width24Radio"; + this.width24Radio.Size = new System.Drawing.Size(51, 17); + this.width24Radio.TabIndex = 1; + this.width24Radio.TabStop = true; + this.width24Radio.Text = "24-bit"; + this.width24Radio.UseVisualStyleBackColor = true; + this.width24Radio.CheckedChanged += new System.EventHandler(this.widthRadio_CheckedChanged); // // width16Radio // @@ -136,16 +142,17 @@ this.width16Radio.TabStop = true; this.width16Radio.Text = "16-bit"; this.width16Radio.UseVisualStyleBackColor = true; + this.width16Radio.CheckedChanged += new System.EventHandler(this.widthRadio_CheckedChanged); // // lowByteGroupBox // this.lowByteGroupBox.Controls.Add(this.lowThirdPartRadio); this.lowByteGroupBox.Controls.Add(this.lowSecondPartRadio); this.lowByteGroupBox.Controls.Add(this.lowFirstPartRadio); - this.lowByteGroupBox.Location = new System.Drawing.Point(13, 140); + this.lowByteGroupBox.Location = new System.Drawing.Point(13, 136); this.lowByteGroupBox.Name = "lowByteGroupBox"; - this.lowByteGroupBox.Size = new System.Drawing.Size(225, 94); - this.lowByteGroupBox.TabIndex = 4; + this.lowByteGroupBox.Size = new System.Drawing.Size(207, 94); + this.lowByteGroupBox.TabIndex = 2; this.lowByteGroupBox.TabStop = false; this.lowByteGroupBox.Text = "Low Byte"; // @@ -159,6 +166,7 @@ this.lowThirdPartRadio.TabStop = true; this.lowThirdPartRadio.Text = "Third part of selection"; this.lowThirdPartRadio.UseVisualStyleBackColor = true; + this.lowThirdPartRadio.CheckedChanged += new System.EventHandler(this.lowByte_CheckedChanged); // // lowSecondPartRadio // @@ -170,6 +178,7 @@ this.lowSecondPartRadio.TabStop = true; this.lowSecondPartRadio.Text = "Second part of selection"; this.lowSecondPartRadio.UseVisualStyleBackColor = true; + this.lowSecondPartRadio.CheckedChanged += new System.EventHandler(this.lowByte_CheckedChanged); // // lowFirstPartRadio // @@ -181,6 +190,7 @@ this.lowFirstPartRadio.TabStop = true; this.lowFirstPartRadio.Text = "First part of selection"; this.lowFirstPartRadio.UseVisualStyleBackColor = true; + this.lowFirstPartRadio.CheckedChanged += new System.EventHandler(this.lowByte_CheckedChanged); // // highByteGroupBox // @@ -189,10 +199,10 @@ this.highByteGroupBox.Controls.Add(this.highThirdPartRadio); this.highByteGroupBox.Controls.Add(this.highSecondPartRadio); this.highByteGroupBox.Controls.Add(this.highFirstPartRadio); - this.highByteGroupBox.Location = new System.Drawing.Point(13, 240); + this.highByteGroupBox.Location = new System.Drawing.Point(13, 236); this.highByteGroupBox.Name = "highByteGroupBox"; - this.highByteGroupBox.Size = new System.Drawing.Size(225, 120); - this.highByteGroupBox.TabIndex = 5; + this.highByteGroupBox.Size = new System.Drawing.Size(207, 120); + this.highByteGroupBox.TabIndex = 3; this.highByteGroupBox.TabStop = false; this.highByteGroupBox.Text = "High Byte"; // @@ -203,6 +213,7 @@ this.highConstantTextBox.Name = "highConstantTextBox"; this.highConstantTextBox.Size = new System.Drawing.Size(93, 20); this.highConstantTextBox.TabIndex = 4; + this.highConstantTextBox.TextChanged += new System.EventHandler(this.highConstantTextBox_TextChanged); // // highConstantRadio // @@ -214,6 +225,7 @@ this.highConstantRadio.TabStop = true; this.highConstantRadio.Text = "Constant:"; this.highConstantRadio.UseVisualStyleBackColor = true; + this.highConstantRadio.CheckedChanged += new System.EventHandler(this.highByte_CheckedChanged); // // highThirdPartRadio // @@ -225,6 +237,7 @@ this.highThirdPartRadio.TabStop = true; this.highThirdPartRadio.Text = "Third part of selection"; this.highThirdPartRadio.UseVisualStyleBackColor = true; + this.highThirdPartRadio.CheckedChanged += new System.EventHandler(this.highByte_CheckedChanged); // // highSecondPartRadio // @@ -236,6 +249,7 @@ this.highSecondPartRadio.TabStop = true; this.highSecondPartRadio.Text = "Second part of selection"; this.highSecondPartRadio.UseVisualStyleBackColor = true; + this.highSecondPartRadio.CheckedChanged += new System.EventHandler(this.highByte_CheckedChanged); // // highFirstPartRadio // @@ -247,16 +261,17 @@ this.highFirstPartRadio.TabStop = true; this.highFirstPartRadio.Text = "First part of selection"; this.highFirstPartRadio.UseVisualStyleBackColor = true; + this.highFirstPartRadio.CheckedChanged += new System.EventHandler(this.highByte_CheckedChanged); // // bankByteGroupBox // this.bankByteGroupBox.Controls.Add(this.bankConstantTextBox); this.bankByteGroupBox.Controls.Add(this.bankNthPartRadio); this.bankByteGroupBox.Controls.Add(this.bankConstantRadio); - this.bankByteGroupBox.Location = new System.Drawing.Point(13, 367); + this.bankByteGroupBox.Location = new System.Drawing.Point(13, 362); this.bankByteGroupBox.Name = "bankByteGroupBox"; - this.bankByteGroupBox.Size = new System.Drawing.Size(225, 70); - this.bankByteGroupBox.TabIndex = 6; + this.bankByteGroupBox.Size = new System.Drawing.Size(207, 70); + this.bankByteGroupBox.TabIndex = 4; this.bankByteGroupBox.TabStop = false; this.bankByteGroupBox.Text = "Bank Byte"; // @@ -266,7 +281,8 @@ this.bankConstantTextBox.MaxLength = 10; this.bankConstantTextBox.Name = "bankConstantTextBox"; this.bankConstantTextBox.Size = new System.Drawing.Size(93, 20); - this.bankConstantTextBox.TabIndex = 5; + this.bankConstantTextBox.TabIndex = 2; + this.bankConstantTextBox.TextChanged += new System.EventHandler(this.bankConstantTextBox_TextChanged); // // bankNthPartRadio // @@ -274,10 +290,11 @@ this.bankNthPartRadio.Location = new System.Drawing.Point(10, 19); this.bankNthPartRadio.Name = "bankNthPartRadio"; this.bankNthPartRadio.Size = new System.Drawing.Size(120, 17); - this.bankNthPartRadio.TabIndex = 2; + this.bankNthPartRadio.TabIndex = 0; this.bankNthPartRadio.TabStop = true; this.bankNthPartRadio.Text = "Nth part of selection"; this.bankNthPartRadio.UseVisualStyleBackColor = true; + this.bankNthPartRadio.CheckedChanged += new System.EventHandler(this.bankByte_CheckedChanged); // // bankConstantRadio // @@ -289,6 +306,7 @@ this.bankConstantRadio.TabStop = true; this.bankConstantRadio.Text = "Constant:"; this.bankConstantRadio.UseVisualStyleBackColor = true; + this.bankConstantRadio.CheckedChanged += new System.EventHandler(this.bankByte_CheckedChanged); // // outputPreviewListView // @@ -296,38 +314,67 @@ | System.Windows.Forms.AnchorStyles.Right))); this.outputPreviewListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.addrColumnHeader, + this.offsetColumnHeader, this.symbolColumnHeader}); + this.outputPreviewListView.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.outputPreviewListView.FullRowSelect = true; this.outputPreviewListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; this.outputPreviewListView.Items.AddRange(new System.Windows.Forms.ListViewItem[] { - listViewItem2}); + listViewItem5}); this.outputPreviewListView.Location = new System.Drawing.Point(6, 19); this.outputPreviewListView.MultiSelect = false; this.outputPreviewListView.Name = "outputPreviewListView"; this.outputPreviewListView.Size = new System.Drawing.Size(290, 369); - this.outputPreviewListView.TabIndex = 7; + this.outputPreviewListView.TabIndex = 0; this.outputPreviewListView.UseCompatibleStateImageBehavior = false; this.outputPreviewListView.View = System.Windows.Forms.View.Details; // // addrColumnHeader // this.addrColumnHeader.Text = "Addr"; + this.addrColumnHeader.Width = 52; // // symbolColumnHeader // this.symbolColumnHeader.Text = "Symbol"; - this.symbolColumnHeader.Width = 200; + this.symbolColumnHeader.Width = 174; // // outputPreviewGroupBox // this.outputPreviewGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.outputPreviewGroupBox.Controls.Add(this.invalidConstantLabel); + this.outputPreviewGroupBox.Controls.Add(this.incompatibleSelectionLabel); this.outputPreviewGroupBox.Controls.Add(this.outputPreviewListView); - this.outputPreviewGroupBox.Location = new System.Drawing.Point(252, 44); + this.outputPreviewGroupBox.Location = new System.Drawing.Point(234, 40); this.outputPreviewGroupBox.Name = "outputPreviewGroupBox"; - this.outputPreviewGroupBox.Size = new System.Drawing.Size(302, 394); - this.outputPreviewGroupBox.TabIndex = 8; + this.outputPreviewGroupBox.Size = new System.Drawing.Size(302, 393); + this.outputPreviewGroupBox.TabIndex = 6; this.outputPreviewGroupBox.TabStop = false; - this.outputPreviewGroupBox.Text = "Output Preview"; + this.outputPreviewGroupBox.Text = "Generated Addresses"; + // + // invalidConstantLabel + // + this.invalidConstantLabel.AutoSize = true; + this.invalidConstantLabel.BackColor = System.Drawing.SystemColors.Window; + this.invalidConstantLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.invalidConstantLabel.ForeColor = System.Drawing.Color.Red; + this.invalidConstantLabel.Location = new System.Drawing.Point(101, 200); + this.invalidConstantLabel.Name = "invalidConstantLabel"; + this.invalidConstantLabel.Size = new System.Drawing.Size(100, 16); + this.invalidConstantLabel.TabIndex = 2; + this.invalidConstantLabel.Text = "Invalid constant"; + // + // incompatibleSelectionLabel + // + this.incompatibleSelectionLabel.AutoSize = true; + this.incompatibleSelectionLabel.BackColor = System.Drawing.SystemColors.Window; + this.incompatibleSelectionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.incompatibleSelectionLabel.ForeColor = System.Drawing.Color.Red; + this.incompatibleSelectionLabel.Location = new System.Drawing.Point(43, 177); + this.incompatibleSelectionLabel.Name = "incompatibleSelectionLabel"; + this.incompatibleSelectionLabel.Size = new System.Drawing.Size(216, 16); + this.incompatibleSelectionLabel.TabIndex = 1; + this.incompatibleSelectionLabel.Text = "Options incompatible with selection"; // // addCodeHintCheckBox // @@ -336,26 +383,33 @@ this.addCodeHintCheckBox.AutoSize = true; this.addCodeHintCheckBox.Location = new System.Drawing.Point(10, 19); this.addCodeHintCheckBox.Name = "addCodeHintCheckBox"; - this.addCodeHintCheckBox.Size = new System.Drawing.Size(123, 17); - this.addCodeHintCheckBox.TabIndex = 8; - this.addCodeHintCheckBox.Text = "Add code entry hints"; + this.addCodeHintCheckBox.Size = new System.Drawing.Size(165, 17); + this.addCodeHintCheckBox.TabIndex = 0; + this.addCodeHintCheckBox.Text = "Add code entry hint if needed"; this.addCodeHintCheckBox.UseVisualStyleBackColor = true; // // optionsGroupBox // this.optionsGroupBox.Controls.Add(this.addCodeHintCheckBox); - this.optionsGroupBox.Location = new System.Drawing.Point(13, 444); + this.optionsGroupBox.Location = new System.Drawing.Point(13, 439); this.optionsGroupBox.Name = "optionsGroupBox"; - this.optionsGroupBox.Size = new System.Drawing.Size(225, 43); - this.optionsGroupBox.TabIndex = 9; + this.optionsGroupBox.Size = new System.Drawing.Size(207, 43); + this.optionsGroupBox.TabIndex = 5; this.optionsGroupBox.TabStop = false; this.optionsGroupBox.Text = "Options"; // + // offsetColumnHeader + // + this.offsetColumnHeader.Text = "Offset"; + this.offsetColumnHeader.Width = 55; + // // FormatSplitAddress // + this.AcceptButton = this.okButton; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(566, 500); + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(548, 496); this.Controls.Add(this.optionsGroupBox); this.Controls.Add(this.outputPreviewGroupBox); this.Controls.Add(this.bankByteGroupBox); @@ -382,6 +436,7 @@ this.bankByteGroupBox.ResumeLayout(false); this.bankByteGroupBox.PerformLayout(); this.outputPreviewGroupBox.ResumeLayout(false); + this.outputPreviewGroupBox.PerformLayout(); this.optionsGroupBox.ResumeLayout(false); this.optionsGroupBox.PerformLayout(); this.ResumeLayout(false); @@ -396,8 +451,8 @@ private System.Windows.Forms.Label selectionInfoLabel; private System.Windows.Forms.GroupBox addressCharacteristicsGroup; private System.Windows.Forms.RadioButton width16Radio; - private System.Windows.Forms.RadioButton width24Button; - private System.Windows.Forms.CheckBox returnAddrCheckBox; + private System.Windows.Forms.RadioButton width24Radio; + private System.Windows.Forms.CheckBox pushRtsCheckBox; private System.Windows.Forms.GroupBox lowByteGroupBox; private System.Windows.Forms.RadioButton lowFirstPartRadio; private System.Windows.Forms.RadioButton lowSecondPartRadio; @@ -418,5 +473,8 @@ private System.Windows.Forms.GroupBox outputPreviewGroupBox; private System.Windows.Forms.CheckBox addCodeHintCheckBox; private System.Windows.Forms.GroupBox optionsGroupBox; + private System.Windows.Forms.Label incompatibleSelectionLabel; + private System.Windows.Forms.Label invalidConstantLabel; + private System.Windows.Forms.ColumnHeader offsetColumnHeader; } } \ No newline at end of file diff --git a/SourceGen/AppForms/FormatSplitAddress.cs b/SourceGen/AppForms/FormatSplitAddress.cs index 75edcd6..7c7f5db 100644 --- a/SourceGen/AppForms/FormatSplitAddress.cs +++ b/SourceGen/AppForms/FormatSplitAddress.cs @@ -20,54 +20,409 @@ using System.Windows.Forms; using Asm65; using CommonUtil; +using CommonWinForms; namespace SourceGen.AppForms { public partial class FormatSplitAddress : Form { /// - /// Result set that describes the formatting to perform. Not all regions will have - /// the same format, e.g. the "mixed ASCII" mode will alternate strings and bytes - /// (rather than a dedicated "mixed ASCII" format type). + /// Format descriptors to apply. /// - public SortedList Results { get; private set; } + public SortedList NewFormatDescriptors { get; private set; } + + /// + /// User labels to apply. + /// + public Dictionary NewUserLabels { get; private set; } + + /// + /// All target offsets found. The list may contain redundant entries. + /// + public List AllTargetOffsets { get; private set; } /// /// Selected offsets. An otherwise contiguous range of offsets can be broken up /// by user-specified labels and address discontinuities, so this needs to be /// processed by range. /// - public TypedRangeSet Selection { private get; set; } + private TypedRangeSet mSelection; /// - /// Raw file data. + /// Project reference. /// - private byte[] mFileData; - - /// - /// Symbol table to use when resolving symbolic values. - /// - private SymbolTable mSymbolTable; + private DisasmProject mProject; /// /// Formatter to use when displaying addresses and hex values. /// - private Asm65.Formatter mFormatter; + private Formatter mFormatter; + + /// + /// Set to prevent controls from going nuts while initializing. + /// + private bool mInitializing; + + /// + /// Set to true when valid output is available. + /// + private bool mOutputReady; - public FormatSplitAddress(byte[] fileData, SymbolTable symbolTable, + public FormatSplitAddress(DisasmProject project, TypedRangeSet selection, Formatter formatter) { InitializeComponent(); - mFileData = fileData; - mSymbolTable = symbolTable; + mProject = project; mFormatter = formatter; + mSelection = selection; + + mOutputReady = false; } private void FormatSplitAddress_Load(object sender, EventArgs e) { + mInitializing = true; + string fmt = selectionInfoLabel.Text; + selectionInfoLabel.Text = string.Format(fmt, mSelection.Count, mSelection.RangeCount); + + width16Radio.Checked = true; + lowFirstPartRadio.Checked = true; + highSecondPartRadio.Checked = true; + bankNthPartRadio.Checked = true; + + incompatibleSelectionLabel.Visible = invalidConstantLabel.Visible = false; + + if (mProject.CpuDef.HasAddr16) { + // Disable the 24-bit option. Having 16-bit selected will disable the rest. + width24Radio.Enabled = false; + } + + outputPreviewListView.SetDoubleBuffered(true); + + mInitializing = false; + UpdateControls(); } - private void okButton_Click(object sender, EventArgs e) { - Results = new SortedList(); + private void okButton_Click(object sender, EventArgs e) { } + + private void UpdateControls() { + if (mInitializing) { + return; + } + mInitializing = true; // no re-entry + + lowThirdPartRadio.Enabled = width24Radio.Checked; + highThirdPartRadio.Enabled = width24Radio.Checked; + bankByteGroupBox.Enabled = width24Radio.Checked; + + lowSecondPartRadio.Enabled = true; + + // If the user selects "constant" for high byte or bank byte, then there is no + // 3rd part available for low/high, so we need to turn those back off. + if (width24Radio.Checked) { + bool haveThree = !(highConstantRadio.Checked || bankConstantRadio.Checked); + lowThirdPartRadio.Enabled = haveThree; + highThirdPartRadio.Enabled = haveThree; + + // If "constant" is selected for high byte *and* bank byte, then there's no + // 2nd part available for low. + if (highConstantRadio.Checked && bankConstantRadio.Checked) { + lowSecondPartRadio.Enabled = false; + } + } else { + // For 16-bit address, if high byte is constant, then there's no second + // part for the low byte. + if (highConstantRadio.Checked) { + lowSecondPartRadio.Enabled = false; + } + } + + // Was a now-invalidated radio button selected before? + if (!lowThirdPartRadio.Enabled && lowThirdPartRadio.Checked) { + // low now invalid, switch to whatever high isn't using + if (highFirstPartRadio.Checked) { + lowSecondPartRadio.Checked = true; + } else { + lowFirstPartRadio.Checked = true; + } + } + if (!highThirdPartRadio.Enabled && highThirdPartRadio.Checked) { + // high now invalid, switch to whatever low isn't using + if (lowFirstPartRadio.Checked) { + highSecondPartRadio.Checked = true; + } else { + highFirstPartRadio.Checked = true; + } + } + if (!lowSecondPartRadio.Enabled && lowSecondPartRadio.Checked) { + // Should only happen when high part is constant. + Debug.Assert(highFirstPartRadio.Checked == false); + lowFirstPartRadio.Checked = true; + } + + mInitializing = false; + UpdatePreview(); + + okButton.Enabled = mOutputReady; + } + + private void widthRadio_CheckedChanged(object sender, EventArgs e) { + UpdateControls(); + } + + private void pushRtsCheckBox_CheckedChanged(object sender, EventArgs e) { + UpdateControls(); + } + + private void lowByte_CheckedChanged(object sender, EventArgs e) { + // If we conflict with the high byte, change the high byte. + if (lowFirstPartRadio.Checked && highFirstPartRadio.Checked) { + highSecondPartRadio.Checked = true; + } else if (lowSecondPartRadio.Checked && highSecondPartRadio.Checked) { + highFirstPartRadio.Checked = true; + } else if (lowThirdPartRadio.Checked && highThirdPartRadio.Checked) { + highFirstPartRadio.Checked = true; + } + UpdateControls(); + } + + private void highByte_CheckedChanged(object sender, EventArgs e) { + // If we conflict with the low byte, change the low byte. + if (lowFirstPartRadio.Checked && highFirstPartRadio.Checked) { + lowSecondPartRadio.Checked = true; + } else if (lowSecondPartRadio.Checked && highSecondPartRadio.Checked) { + lowFirstPartRadio.Checked = true; + } else if (lowThirdPartRadio.Checked && highThirdPartRadio.Checked) { + lowFirstPartRadio.Checked = true; + } + UpdateControls(); + } + + private void bankByte_CheckedChanged(object sender, EventArgs e) { + UpdateControls(); + } + + private void highConstantTextBox_TextChanged(object sender, EventArgs e) { + highConstantRadio.Checked = true; + UpdateControls(); + } + + private void bankConstantTextBox_TextChanged(object sender, EventArgs e) { + bankConstantRadio.Checked = true; + UpdateControls(); + } + + private void UpdatePreview() { + mOutputReady = false; + + int minDiv; + + if (width16Radio.Checked) { + if (highConstantRadio.Checked) { + minDiv = 1; + } else { + minDiv = 2; + } + } else { + if (highConstantRadio.Checked) { + if (bankConstantRadio.Checked) { + minDiv = 1; + } else { + minDiv = 2; + } + } else { + if (bankConstantRadio.Checked) { + minDiv = 2; + } else { + minDiv = 3; + } + } + } + + incompatibleSelectionLabel.Visible = invalidConstantLabel.Visible = false; + + try { + // Start by clearing the previous contents of the list. If something goes + // wrong, we want to show the error messages on an empty list. + outputPreviewListView.BeginUpdate(); + outputPreviewListView.Items.Clear(); + + if ((mSelection.Count % minDiv) != 0) { + incompatibleSelectionLabel.Visible = true; + return; + } + + int highConstant = -1; + if (highConstantRadio.Checked) { + if (!Number.TryParseInt(highConstantTextBox.Text, out highConstant, + out int unused) || (highConstant != (byte) highConstant)) { + invalidConstantLabel.Visible = true; + return; + } + } + + int bankConstant = -1; + if (bankConstantRadio.Enabled && bankConstantRadio.Checked) { + if (!Number.TryParseInt(bankConstantTextBox.Text, out bankConstant, + out int unused) || (bankConstant != (byte) bankConstant)) { + invalidConstantLabel.Visible = true; + return; + } + } + + // Looks valid, generate format list. + GenerateFormats(minDiv, highConstant, bankConstant); + } finally { + outputPreviewListView.EndUpdate(); + } + } + + private void GenerateFormats(int div, int highConst, int bankConst) { + SortedList newDfds = new SortedList(); + Dictionary newLabels = new Dictionary(); + List targetOffsets = new List(); + + // Identify the offset where each set of data starts. + int span = mSelection.Count / div; + int lowOff, highOff, bankOff; + + if (lowFirstPartRadio.Checked) { + lowOff = 0; + } else if (lowSecondPartRadio.Checked) { + lowOff = span; + } else if (lowThirdPartRadio.Checked) { + lowOff = span * 2; + } else { + Debug.Assert(false); + lowOff = -1; + } + if (highFirstPartRadio.Checked) { + highOff = 0; + } else if (highSecondPartRadio.Checked) { + highOff = span; + } else if (highThirdPartRadio.Checked) { + highOff = span * 2; + } else { + highOff = -1; // use constant + } + if (width24Radio.Checked) { + if (bankNthPartRadio.Checked) { + // Use whichever part isn't being used by the other two. + if (lowOff != 0 && highOff != 0) { + bankOff = 0; + } else if (lowOff != span && highOff != span) { + bankOff = span; + } else { + Debug.Assert(lowOff != span * 2 && highOff != span * 2); + bankOff = span * 2; + } + } else { + bankOff = -1; // use constant + } + } else { + bankOff = -1; // use constant + bankConst = 0; // always bank 0 + } + + Debug.WriteLine("Extract from low=" + lowOff + " high=" + highOff + + " bank=" + bankOff); + + // The TypedRangeSet doesn't have an index operation, so copy the values into + // an array. + int[] offsets = new int[mSelection.Count]; + int index = 0; + foreach (TypedRangeSet.Tuple tup in mSelection) { + offsets[index++] = tup.Value; + } + + int adj = 0; + if (pushRtsCheckBox.Checked) { + adj = 1; + } + + // Walk through the file data, generating addresses as we go. + byte[] fileData = mProject.FileData; + for (int i = 0; i < span; i++) { + byte low, high, bank; + + low = fileData[offsets[lowOff + i]]; + if (highOff >= 0) { + high = fileData[offsets[highOff + i]]; + } else { + high = (byte) highConst; + } + if (bankOff >= 0) { + bank = fileData[offsets[bankOff + i]]; + } else { + bank = (byte) bankConst; + } + + int addr = ((bank << 16) | (high << 8) | low) + adj; + Debug.WriteLine("GOT " + i + ": " + addr.ToString("x6")); + + int targetOffset = mProject.AddrMap.AddressToOffset(offsets[0], addr); + if (targetOffset < 0) { + // Address not within file bounds. + // TODO(maybe): look for matching platform/project symbols + AddPreviewItem(addr, -1, Properties.Resources.INVALID_ADDRESS); + } else { + // Note the same target offset may appear more than once. + targetOffsets.Add(targetOffset); + + // If there's a user-defined label there already, use it. Otherwise, we'll + // need to generate one. + string targetLabel; + if (mProject.UserLabels.TryGetValue(targetOffset, out Symbol sym)) { + AddPreviewItem(addr, targetOffset, sym.Label); + targetLabel = sym.Label; + } else { + AddPreviewItem(addr, targetOffset, "(+)"); + // Generate a symbol that's unique vs. the symbol table. We don't need + // it to be unique vs. the labels we're generating here, because we + // won't generate identical labels for different addresses, and we do + // want to generate a single label if more than one table entry refers + // to the same target. + Symbol tmpSym = SymbolTable.GenerateUniqueForAddress(addr, + mProject.SymbolTable, "T"); + // tmpSym was returned as an auto-label, make it a user label instead + tmpSym = new Symbol(tmpSym.Label, tmpSym.Value, Symbol.Source.User, + Symbol.Type.LocalOrGlobalAddr); + newLabels[targetOffset] = tmpSym; // overwrites previous + targetLabel = tmpSym.Label; + } + + // Now we need to create format descriptors for the addresses where we + // extracted the low, high, and bank values. + newDfds.Add(offsets[lowOff + i], FormatDescriptor.Create(1, + new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Low), false)); + if (highOff >= 0) { + newDfds.Add(offsets[highOff + i], FormatDescriptor.Create(1, + new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.High), false)); + } + if (bankOff >= 0) { + newDfds.Add(offsets[bankOff + i], FormatDescriptor.Create(1, + new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Bank), false)); + } + } + } + + NewFormatDescriptors = newDfds; + NewUserLabels = newLabels; + AllTargetOffsets = targetOffsets; + + mOutputReady = true; + } + + private void AddPreviewItem(int addr, int offset, string label) { + ListViewItem lvi = new ListViewItem(mFormatter.FormatAddress(addr, + !mProject.CpuDef.HasAddr16)); + if (offset >= 0) { + lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, + mFormatter.FormatOffset24(offset))); + } else { + lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, "---")); + } + lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, label)); + outputPreviewListView.Items.Add(lvi); } } } diff --git a/SourceGen/AppForms/ProjectView.cs b/SourceGen/AppForms/ProjectView.cs index f1b731d..02f9323 100644 --- a/SourceGen/AppForms/ProjectView.cs +++ b/SourceGen/AppForms/ProjectView.cs @@ -2225,7 +2225,7 @@ namespace SourceGen.AppForms { } formatSplitAddressTableToolStripMenuItem.Enabled = - (entityCounts.mDataLines > 1 && entityCounts.mCodeLines == 0); + (entityCounts.mDataLines > 0 && entityCounts.mCodeLines == 0); toggleSingleBytesToolStripMenuItem.Enabled = (entityCounts.mDataLines > 0 && entityCounts.mCodeLines == 0); @@ -2618,27 +2618,56 @@ namespace SourceGen.AppForms { } private void FormatSplitAddressTable_Click(object sender, EventArgs e) { - FormatSplitAddress dlg = new FormatSplitAddress(mProject.FileData, - mProject.SymbolTable, mOutputFormatter); - - TypedRangeSet trs = dlg.Selection = GroupedOffsetSetFromSelected(); + TypedRangeSet trs = GroupedOffsetSetFromSelected(); if (trs.Count == 0) { // shouldn't happen Debug.Assert(false, "FormatSplitAddressTable found nothing to edit"); - dlg.Dispose(); return; } - // TODO: check to see if the selection is eligible for treatment as a - // split-address table. If not, show a dialog explaining why. + FormatSplitAddress dlg = new FormatSplitAddress(mProject, trs, mOutputFormatter); dlg.ShowDialog(); if (dlg.DialogResult == DialogResult.OK) { - ChangeSet cs = mProject.GenerateFormatMergeSet(dlg.Results); + // Start with the format descriptors. + ChangeSet cs = mProject.GenerateFormatMergeSet(dlg.NewFormatDescriptors); + + // Add in the user labels. + foreach (KeyValuePair kvp in dlg.NewUserLabels) { + Symbol oldUserValue = null; + if (mProject.UserLabels.ContainsKey(kvp.Key)) { + Debug.Assert(false, "should not be replacing label"); + oldUserValue = mProject.UserLabels[kvp.Key]; + } + UndoableChange uc = UndoableChange.CreateLabelChange(kvp.Key, + oldUserValue, kvp.Value); + cs.Add(uc); + } + + + // Apply code hints. + TypedRangeSet newSet = new TypedRangeSet(); + TypedRangeSet undoSet = new TypedRangeSet(); + + foreach (int offset in dlg.AllTargetOffsets) { + if (!mProject.GetAnattrib(offset).IsInstruction) { + CodeAnalysis.TypeHint oldType = mProject.TypeHints[offset]; + if (oldType == CodeAnalysis.TypeHint.Code) { + continue; // already set + } + undoSet.Add(offset, (int)oldType); + newSet.Add(offset, (int)CodeAnalysis.TypeHint.Code); + } + } + if (newSet.Count != 0) { + cs.Add(UndoableChange.CreateTypeHintChange(undoSet, newSet)); + } + + // Finally, apply the change. if (cs.Count != 0) { ApplyUndoableChanges(cs); } else { - Debug.WriteLine("No change to data formats"); + Debug.WriteLine("No changes found"); } } diff --git a/SourceGen/DataAnalysis.cs b/SourceGen/DataAnalysis.cs index 322e8e3..d924803 100644 --- a/SourceGen/DataAnalysis.cs +++ b/SourceGen/DataAnalysis.cs @@ -292,7 +292,7 @@ namespace SourceGen { // up with a user or platform label that matches an auto label, so we // need to do some renaming in that case. Shouldn't happen often. Symbol sym = SymbolTable.GenerateUniqueForAddress(mAnattribs[targetOffset].Address, - mProject.SymbolTable); + mProject.SymbolTable, "L"); mAnattribs[targetOffset].Symbol = sym; // This will throw if the symbol already exists. That is the desired // behavior, as that would be a bug. diff --git a/SourceGen/Properties/Resources.Designer.cs b/SourceGen/Properties/Resources.Designer.cs index 4ecec92..4a19e49 100644 --- a/SourceGen/Properties/Resources.Designer.cs +++ b/SourceGen/Properties/Resources.Designer.cs @@ -582,6 +582,15 @@ namespace SourceGen.Properties { } } + /// + /// Looks up a localized string similar to (unknown address). + /// + internal static string INVALID_ADDRESS { + get { + return ResourceManager.GetString("INVALID_ADDRESS", resourceCulture); + } + } + /// /// Looks up a localized string similar to Legal Stuff. /// diff --git a/SourceGen/Properties/Resources.resx b/SourceGen/Properties/Resources.resx index f9f9f9c..c24a9a3 100644 --- a/SourceGen/Properties/Resources.resx +++ b/SourceGen/Properties/Resources.resx @@ -291,6 +291,9 @@ Symbol files: + + (unknown address) + Legal Stuff diff --git a/SourceGen/SymbolTable.cs b/SourceGen/SymbolTable.cs index 31b7779..d847ed9 100644 --- a/SourceGen/SymbolTable.cs +++ b/SourceGen/SymbolTable.cs @@ -216,10 +216,13 @@ namespace SourceGen { /// Generates a unique address symbol. Does not add the symbol to the list. /// /// Address label will be applied to + /// Symbol table. + /// Prefix to use; must start with a letter. /// Newly-created, unique symbol. - public static Symbol GenerateUniqueForAddress(int addr, SymbolTable symbols) { + public static Symbol GenerateUniqueForAddress(int addr, SymbolTable symbols, + string prefix) { // $1234 == L1234, $05/1234 == L51234. - string label = "L" + addr.ToString("X4"); + string label = prefix + addr.ToString("X4"); // always upper-case if (symbols.TryGetValue(label, out Symbol unused)) { const int MAX_RENAME = 999; string baseLabel = label;