From 5f472b60cf3d36bdffea81a26ecaf836003e37d1 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Wed, 22 Sep 2021 14:39:39 -0700 Subject: [PATCH] ORG rework, part 3 Split ".org" into ".arstart" and ".arend" (address range start/end). Address range ends are now shown in the code list view, and the pseudo-op can be edited in app settings. Address range starts are now shown after notes and long comments, rather than before, which brings the on-screen display in sync with generated code. Reworked the address range editor UI to include the new features. The implementation is fully broken. More changes to the AddressMap API, putting the resolved region length into a separate ActualLength field. Added FindRegion(). Renamed some things. Code generation changed slightly: the blank line before a region-end line now comes after it, and ACME's "} ;!pseudopc" is now just "}". This required minor updates to some of the regression test results. --- Asm65/Address.cs | 8 +- CommonUtil/AddressMap.cs | 403 ++++++++++++------ SourceGen/AsmGen/AsmAcme.cs | 26 +- SourceGen/AsmGen/AsmCc65.cs | 53 +-- SourceGen/AsmGen/AsmMerlin32.cs | 11 +- SourceGen/AsmGen/AsmTass64.cs | 15 +- SourceGen/AsmGen/GenCommon.cs | 46 +- SourceGen/AsmGen/IGenerator.cs | 8 +- SourceGen/DisasmProject.cs | 25 +- SourceGen/Exporter.cs | 9 +- SourceGen/LineListGen.cs | 106 +++-- SourceGen/MainController.cs | 65 +-- SourceGen/ProjectFile.cs | 2 +- SourceGen/PseudoOp.cs | 11 +- .../Expected/20000-numeric-types_acme.S | 4 +- .../Expected/20030-labels-and-symbols_acme.S | 2 +- .../Expected/20032-labels-and-symbols_acme.S | 2 +- .../Expected/20040-address-changes_64tass.S | 6 +- .../Expected/20040-address-changes_acme.S | 28 +- .../Expected/20042-address-changes_64tass.S | 6 +- .../Expected/20042-address-changes_acme.S | 2 +- .../Expected/20050-branches-and-banks_acme.S | 6 +- .../20052-branches-and-banks_64tass.S | 2 +- .../Expected/20060-target-adjustment_acme.S | 2 +- .../SGTestData/Expected/20100-label-dp_acme.S | 2 +- .../SGTestData/Expected/20102-label-dp_acme.S | 2 +- .../Expected/20150-local-variables_acme.S | 2 +- .../Expected/20152-local-variables_acme.S | 2 +- .../Expected/20182-extension-scripts_acme.S | 4 +- .../Expected/20200-ui-edge-cases_64tass.S | 2 +- .../Expected/20200-ui-edge-cases_acme.S | 4 +- .../Expected/20212-reloc-data_64tass.S | 2 +- .../Expected/20212-reloc-data_acme.S | 2 +- .../Expected/20222-data-bank_64tass.S | 4 +- .../Expected/20240-large-overlay_acme.S | 16 +- SourceGen/WpfGui/EditAddress.xaml | 115 +++-- SourceGen/WpfGui/EditAddress.xaml.cs | 261 +++++++----- SourceGen/WpfGui/EditAppSettings.xaml | 143 ++++--- SourceGen/WpfGui/EditAppSettings.xaml.cs | 3 +- SourceGen/WpfGui/EditDataOperand.xaml.cs | 4 - 40 files changed, 830 insertions(+), 586 deletions(-) diff --git a/Asm65/Address.cs b/Asm65/Address.cs index 204ca50..955855d 100644 --- a/Asm65/Address.cs +++ b/Asm65/Address.cs @@ -17,12 +17,16 @@ using System; using System.Diagnostics; namespace Asm65 { + /// + /// Memory address primitives. + /// public static class Address { /// /// Converts a 16- or 24-bit address to a string. /// - /// - /// + /// Address + /// If true, force 24-bit output mode. + /// Formatted string. public static string AddressToString(int addr, bool always24) { if (!always24 && addr < 65536) { return addr.ToString("x4"); diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs index b661401..d2609c0 100644 --- a/CommonUtil/AddressMap.cs +++ b/CommonUtil/AddressMap.cs @@ -83,6 +83,11 @@ namespace CommonUtil { /// public const int NON_ADDR = -1025; + /// + /// Address value to use for an invalid address. + /// + public const int INVALID_ADDR = -2048; + #region Structural /// @@ -141,6 +146,27 @@ namespace CommonUtil { " addr=$" + Address.ToString("x4") + " preLab='" + PreLabel + "' isRel=" + IsRelative + "]"; } + + public static bool operator ==(AddressMapEntry a, AddressMapEntry b) { + if (ReferenceEquals(a, b)) { + return true; // same object, or both null + } + if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) { + return false; // one is null + } + // All fields must be equal. + return a.Offset == b.Offset && a.Length == b.Length && a.Address == b.Address && + a.PreLabel == b.PreLabel && a.IsRelative == b.IsRelative; + } + public static bool operator !=(AddressMapEntry a, AddressMapEntry b) { + return !(a == b); + } + public override bool Equals(object obj) { + return obj is AddressMapEntry && this == (AddressMapEntry)obj; + } + public override int GetHashCode() { + return Offset ^ Length ^ Address ^ PreLabel.GetHashCode() ^ (IsRelative ? 1 : 0); + } } /// @@ -154,52 +180,49 @@ namespace CommonUtil { /// public class AddressRegion : AddressMapEntry { /// - /// Is the end point floating? + /// Actual length (after FLOATING_LEN is resolved). /// - /// - /// (In the structural list this is redundant with the FLOATING_LEN Length value, - /// but in the other structures a new instance that has the actual length is created.) - /// - public bool IsFloating { get; private set; } + public int ActualLength { get; private set; } /// /// Address associated with pre-label. /// public int PreLabelAddress { get; private set; } + /// + /// Is the end point floating? + /// + public bool IsFloating { + get { + return Length == FLOATING_LEN; + } + } + + /// /// Full constructor. /// - public AddressRegion(int offset, int len, int addr, bool isFloating, - string preLabel, int prevAddr, bool isRelative) + public AddressRegion(int offset, int len, int addr, string preLabel, bool isRelative, + int actualLen, int preLabelAddr) : base(offset, len, addr, preLabel, isRelative) { - IsFloating = isFloating; - PreLabelAddress = prevAddr; + ActualLength = actualLen; + PreLabelAddress = preLabelAddr; - Debug.Assert(Length != FLOATING_LEN); + Debug.Assert(ActualLength != FLOATING_LEN); } /// - /// Basic constructor. + /// Basic constructor. Not for use when len==FLOATING_LEN. /// public AddressRegion(int offset, int len, int addr) - : this(offset, len, addr, addr == FLOATING_LEN ? true : false, - string.Empty, NON_ADDR, false) { - } - - /// - /// Construct from AddressRegion. - /// - public AddressRegion(AddressMapEntry region) - : this(region.Offset, region.Length, region.Address, - region.Address == FLOATING_LEN ? true : false, - region.PreLabel, NON_ADDR, region.IsRelative) { + : this(offset, len, addr, string.Empty, false, len, NON_ADDR) { + Debug.Assert(len != FLOATING_LEN); } public override string ToString() { - return "[AddrRegion: +" + Offset.ToString("x6") + " len=$" + Length.ToString("x4") + - " addr=$" + Address.ToString("x4") + " isFloat=" + IsFloating + - " isRel=" + IsRelative + "]"; + return "[AddrRegion: +" + Offset.ToString("x6") + " len=$" + + Length.ToString("x4") + " addr=$" + Address.ToString("x4") + + " actualLen=$" + ActualLength.ToString("x4") + " isRel=" + IsRelative + "]"; } } @@ -235,7 +258,7 @@ namespace CommonUtil { // (Shouldn't be necessary since we're only doing this to pass the address map to // plugins, but... better safe.) foreach (AddressMapEntry ent in entries) { - AddResult result = AddRegion(ent.Offset, ent.Length, ent.Address, ent.PreLabel, + AddResult result = AddEntry(ent.Offset, ent.Length, ent.Address, ent.PreLabel, ent.IsRelative); if (result != AddResult.Okay) { throw new Exception("Unable to add entry (" + result + "): " + ent); @@ -277,10 +300,10 @@ namespace CommonUtil { /// /// Number of entries in the address map. /// - public int RegionCount { get { return mMapEntries.Count; } } + public int EntryCount { get { return mMapEntries.Count; } } /// - /// Error codes for AddRegion(). + /// Error codes for AddEntry(). /// public enum AddResult { Unknown = 0, @@ -319,18 +342,30 @@ namespace CommonUtil { } /// - /// Adds a new region. + /// Adds a new entry to the map. /// /// File offset of region start. /// Length of region, or FLOATING_LEN for a floating end point. /// Address of region start. /// Failure code. - public AddResult AddRegion(int offset, int length, int addr) { - return AddRegion(offset, length, addr, string.Empty, false); + public AddResult AddEntry(int offset, int length, int addr) { + return AddEntry(offset, length, addr, string.Empty, false); } /// - /// Adds a new region. + /// Adds a new entry to the map. + /// + /// Entry object. + /// Failure code. + public AddResult AddEntry(AddressMapEntry entry) { + // Slightly inefficient to extract the fields and reassemble them, which we can + // avoid since instances are immutable, but it's not worth coding around. + return AddEntry(entry.Offset, entry.Length, entry.Address, entry.PreLabel, + entry.IsRelative); + } + + /// + /// Adds a new entry to the map. /// /// File offset of region start. /// Length of region, or FLOATING_LEN for a floating end point. @@ -339,10 +374,10 @@ namespace CommonUtil { /// True if code generator should output relative /// assembler directive operand. /// Failure code. - public AddResult AddRegion(int offset, int length, int addr, string preLabel, + public AddResult AddEntry(int offset, int length, int addr, string preLabel, bool isRelative) { if (!ValidateArgs(offset, length, addr, preLabel)) { - Debug.WriteLine("AddRegion: invalid arg"); + Debug.WriteLine("AddEntry: invalid arg"); return AddResult.InvalidValue; } int insIdx; @@ -472,13 +507,13 @@ namespace CommonUtil { /// Pre-block label. /// New value for IsRelative. /// True if a region was edited, false otherwise. - public bool EditRegion(int offset, int length, int addr, string preLabel, bool isRelative) { + public bool EditEntry(int offset, int length, int addr, string preLabel, bool isRelative) { if (!ValidateArgs(offset, length, addr, preLabel)) { throw new Exception("Bad EditRegion args +" + offset.ToString("x6") + " " + length + " $" + addr); } - int idx = FindRegion(offset, length); + int idx = FindEntry(offset, length); if (idx < 0) { return false; } @@ -493,13 +528,13 @@ namespace CommonUtil { /// Offset of region to remove. /// Length of region to remove. /// True if a region was removed, false otherwise. - public bool RemoveRegion(int offset, int length) { + public bool RemoveEntry(int offset, int length) { if (!ValidateArgs(offset, length, 0, string.Empty)) { throw new Exception("Bad RemoveRegion args +" + offset.ToString("x6") + " " + length); } - int idx = FindRegion(offset, length); + int idx = FindEntry(offset, length); if (idx < 0) { return false; } @@ -509,12 +544,12 @@ namespace CommonUtil { } /// - /// Finds a region with a matching offset and length. + /// Finds an entry with a matching offset and length. /// /// Offset to match. /// Length to match (may be FLOATING_LEN). - /// Index of matching region, or -1 if not found. - private int FindRegion(int offset, int length) { + /// Index of matching entry, or -1 if not found. + private int FindEntry(int offset, int length) { for (int i = 0; i < mMapEntries.Count; i++) { if (mMapEntries[i].Offset == offset && mMapEntries[i].Length == length) { return i; @@ -531,26 +566,11 @@ namespace CommonUtil { //} /// - /// Gets the first region with the specified offset and length. - /// - /// - /// - /// - //public AddressMapEntry GetFirstRegion(int offset, int length) { - // int idx = FindRegion(offset, length); - // if (idx < 0) { - // return null; - // } else { - // return mRegionList[idx]; - // } - //} - - /// - /// Gets a list of the regions with the specified offset value. + /// Gets a list of the entries with the specified offset value. /// /// File offset. /// List of entries; may be empty. - public List GetRegions(int offset) { + public List GetEntries(int offset) { List regions = new List(); for (int i = 0; i < mMapEntries.Count; i++) { if (mMapEntries[i].Offset == offset) { @@ -653,7 +673,7 @@ namespace CommonUtil { /// reconstructed whenever a change is made. /// /// We need to resolve floating lengths and pre-label addresses, so the tree holds - /// AddressMapEntry rather than AddressRegion. + /// AddressRegion rather than AddressMapEntry. /// private class TreeNode { public AddressRegion Region { get; set; } @@ -682,8 +702,8 @@ namespace CommonUtil { // Create a "fake" node that spans the file, so that any region not covered // explicitly is caught here. It also avoids the need to special-case the top // part of the file. - AddressRegion globalReg = new AddressRegion(0, mSpanLength, NON_ADDR, false, - string.Empty, NON_ADDR, false); + AddressRegion globalReg = new AddressRegion(0, mSpanLength, NON_ADDR, string.Empty, + false, mSpanLength, NON_ADDR); TreeNode topNode = new TreeNode(globalReg, null); // Generate the children of this node. @@ -730,7 +750,7 @@ namespace CommonUtil { // // Regions with floating ends can't have children, so we don't need to // check for sub-regions. - int nextStart = parent.Region.Offset + parent.Region.Length; + int nextStart = parent.Region.Offset + parent.Region.ActualLength; index++; if (index < mMapEntries.Count) { // Check next sibling. @@ -740,16 +760,16 @@ namespace CommonUtil { } } AddressRegion fixedReg = new AddressRegion(childEnt.Offset, - nextStart - childEnt.Offset, childEnt.Address, true, - childEnt.PreLabel, preLabelAddr, childEnt.IsRelative); + FLOATING_LEN, childEnt.Address, childEnt.PreLabel, childEnt.IsRelative, + nextStart - childEnt.Offset, preLabelAddr); children.Add(new TreeNode(fixedReg, parent)); // "index" now points to entry past the child we just added. } else { // Add this region to the list, and check for descendants. AddressRegion newReg = new AddressRegion(childEnt.Offset, - childEnt.Length, childEnt.Address, false, - childEnt.PreLabel, preLabelAddr, childEnt.IsRelative); + childEnt.Length, childEnt.Address, childEnt.PreLabel, + childEnt.IsRelative, childEnt.Length, preLabelAddr); TreeNode thisNode = new TreeNode(newReg, parent); children.Add(thisNode); @@ -857,7 +877,7 @@ namespace CommonUtil { // Non-addressable space. return -1; } - if (targetAddr < region.Address || targetAddr >= region.Address + region.Length) { + if (targetAddr < region.Address || targetAddr >= region.Address + region.ActualLength) { // Outside our range of addresses, return failure. return -1; } @@ -869,7 +889,7 @@ namespace CommonUtil { foreach (TreeNode childNode in node.Children) { AddressRegion childReg = childNode.Region; int childStartPosn = childReg.Offset - region.Offset; - int childEndPosn = childStartPosn + childReg.Length; + int childEndPosn = childStartPosn + childReg.ActualLength; if (childStartPosn > subPosn) { // Child is past the target, it's not in a hole; no need to check @@ -904,7 +924,7 @@ namespace CommonUtil { int ourAddr = NON_ADDR; if (node.Region.Address != NON_ADDR) { ourAddr = node.Region.Address + (offset - node.Region.Offset); - Debug.Assert(ourAddr < node.Region.Address + node.Region.Length); + Debug.Assert(ourAddr < node.Region.Address + node.Region.ActualLength); } return ourAddr; } @@ -919,7 +939,8 @@ namespace CommonUtil { if (node.Children != null) { foreach (TreeNode child in node.Children) { AddressRegion childReg = child.Region; - if (offset >= childReg.Offset && offset < childReg.Offset + childReg.Length) { + if (offset >= childReg.Offset && + offset < childReg.Offset + childReg.ActualLength) { // It's in or below this child. Check it with tail recursion. return OffsetToNode(offset, child); } @@ -928,6 +949,48 @@ namespace CommonUtil { return node; } + /// + /// Finds a region with a matching offset and length. + /// + /// + /// We want the AddressRegion object, not the AddressMapEntry, so we need to walk through + /// the tree to find it. + /// + /// Region start offset. + /// Region length. May be FLOATING_LEN. + /// Region found, or null if not. + public AddressRegion FindRegion(int offset, int length) { + if (!ValidateArgs(offset, length, 0, string.Empty)) { + Debug.Assert(false, "Invalid args to FindRegion"); + return null; + } + TreeNode curNode = mTopNode; + while (curNode != null) { + // Check for exact match. + if (curNode.Region.Offset == offset && curNode.Region.Length == length) { + // found it + return curNode.Region; + } + + // Look for a child that includes the offset. + if (curNode.Children == null) { + // Not found, bail. + break; + } + TreeNode foundNode = null; + foreach (TreeNode childNode in curNode.Children) { + if (offset >= childNode.Region.Offset && + offset < childNode.Region.Offset + childNode.Region.ActualLength) { + foundNode = childNode; + break; + } + } + curNode = foundNode; + } + + return null; + } + /// /// Checks to see if the specified range of offsets is in an uninterrupted address /// range. Use this to see if something crosses an address-change boundary. This @@ -955,9 +1018,9 @@ namespace CommonUtil { TreeNode node = OffsetToNode(offset, mTopNode); AddressRegion region = node.Region; - Debug.Assert(offset >= region.Offset && offset < region.Offset + region.Length); + Debug.Assert(offset >= region.Offset && offset < region.Offset + region.ActualLength); int lastOffset = offset + length - 1; // offset of last byte in range - if (lastOffset >= region.Offset + region.Length) { + if (lastOffset >= region.Offset + region.ActualLength) { // end of region is not in this node return false; } @@ -971,7 +1034,7 @@ namespace CommonUtil { // Child is past the target, so range is not in a hole; no need to check // additional children because the children are sorted by Offset. break; - } else if (offset <= childReg.Offset + childReg.Length - 1 && + } else if (offset <= childReg.Offset + childReg.ActualLength - 1 && lastOffset >= childReg.Offset) { // Target is in a hole occupied by the child. No good. return false; @@ -983,7 +1046,7 @@ namespace CommonUtil { } private bool DebugValidateHierarchical() { - if (mTopNode.Region.Offset != 0 || mTopNode.Region.Length != mSpanLength) { + if (mTopNode.Region.Offset != 0 || mTopNode.Region.ActualLength != mSpanLength) { Debug.WriteLine("Malformed top node"); return false; } @@ -1006,19 +1069,19 @@ namespace CommonUtil { private bool DebugValidateHierarchy(List nodeList, int startOffset, int nextOffset, ref int nodeCount) { foreach (TreeNode node in nodeList) { - Debug.Assert(node.Region.Length >= 0); // no floaters + Debug.Assert(node.Region.ActualLength >= 0); nodeCount++; if (node.Region.Offset < startOffset || - node.Region.Offset + node.Region.Length > nextOffset) { + node.Region.Offset + node.Region.ActualLength > nextOffset) { Debug.WriteLine("Child node did not fit in parent bounds"); return false; } if (node.Children != null) { // Descend recursively. if (!DebugValidateHierarchy(node.Children, node.Region.Offset, - node.Region.Offset + node.Region.Length, ref nodeCount)) { + node.Region.Offset + node.Region.ActualLength, ref nodeCount)) { return false; } } @@ -1076,7 +1139,7 @@ namespace CommonUtil { if (mTopNode.Children != null) { foreach (TreeNode node in mTopNode.Children) { - Debug.Assert(node.Region.Length > 0); // all floaters should be resolved + Debug.Assert(node.Region.ActualLength > 0); if (node.Region.Offset != startOffset) { // Insert a no-address zone here. @@ -1090,7 +1153,7 @@ namespace CommonUtil { AddChangeEntry(changeList, node, NON_ADDR); - startOffset = node.Region.Offset + node.Region.Length; + startOffset = node.Region.Offset + node.Region.ActualLength; } } @@ -1125,15 +1188,15 @@ namespace CommonUtil { /// parent's region. private void AddChangeEntry(List changeList, TreeNode node, int parentStartAddr) { - Debug.Assert(node.Region.Length != FLOATING_LEN); + Debug.Assert(node.Region.ActualLength != FLOATING_LEN); int nextAddr = NON_ADDR; if (parentStartAddr != NON_ADDR) { - nextAddr = parentStartAddr + node.Region.Length; + nextAddr = parentStartAddr + node.Region.ActualLength; } AddressChange startChange = new AddressChange(true, node.Region.Offset, node.Region.Address, node.Region); AddressChange endChange = new AddressChange(false, - node.Region.Offset + node.Region.Length, nextAddr, node.Region); + node.Region.Offset + node.Region.ActualLength, nextAddr, node.Region); changeList.Add(startChange); int curAddr = node.Region.Address; @@ -1278,6 +1341,80 @@ namespace CommonUtil { } } + private static bool Test_Primitives() { + bool result = true; + + AddressMapEntry ent1 = new AddressMapEntry(0, 1, 2, "three", true); + AddressMapEntry ent2 = new AddressMapEntry(0, 1, 2, "three", true); + AddressMapEntry ent3 = new AddressMapEntry(0, 1, 2, "three-A", true); + + result &= ent1 == ent2; + result &= ent1 != ent3; + result &= ent1.Equals(ent2); + Test_Expect(true, ref result, result); + + AddressRegion reg1 = new AddressRegion(ent1.Offset, ent1.Length, ent1.Address, + ent1.PreLabel, ent1.IsRelative, ent1.Length, 0); + AddressRegion reg2 = new AddressRegion(ent2.Offset, ent2.Length, ent2.Address, + ent2.PreLabel, ent2.IsRelative, ent2.Length, 0); + AddressRegion reg3 = new AddressRegion(ent3.Offset, ent3.Length, ent3.Address, + ent3.PreLabel, ent3.IsRelative, ent3.Length, 0); + + result &= reg1 == reg2; + result &= reg1 == ent1; + result &= reg1 == ent2; + result &= reg1 != ent3; + result &= reg3 != ent1; + result &= reg1.Equals(ent2); + result &= ent3 != reg1; + Test_Expect(true, ref result, result); + + result &= ent1.GetHashCode() == ent2.GetHashCode(); + result &= ent2.GetHashCode() != ent3.GetHashCode(); + Test_Expect(true, ref result, result); + + return result; + } + + private static bool Test_Find() { + const int mapLen = 0x1000; + AddressMap map = new AddressMap(mapLen); + bool result = true; + + const int off0 = 0x000100; + const int len0 = 0x0f00; + const int adr0 = 0x2100; + const int off1 = 0x000200; + const int len1 = 0x0400; + const int adr1 = 0x2200; + const int off2 = 0x000400; + const int len2 = FLOATING_LEN; + const int adr2 = 0x2400; + + AddressMapEntry ent0 = new AddressMapEntry(off0, len0, adr0, string.Empty, false); + AddressMapEntry ent1 = new AddressMapEntry(off1, len1, adr1, string.Empty, false); + AddressMapEntry ent2 = new AddressMapEntry(off2, len2, adr2, string.Empty, false); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(ent0)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(ent1)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(ent2)); + + AddressRegion reg; + reg = map.FindRegion(off0, len0); + Test_Expect(true, ref result, reg == ent0); + reg = map.FindRegion(off1, len1); + Test_Expect(true, ref result, reg == ent1); + reg = map.FindRegion(off2, len2); + Test_Expect(true, ref result, reg == ent2); + + // Look for non-existent regions. + reg = map.FindRegion(0x000000, 0x100); + Test_Expect(true, ref result, reg == null); + reg = map.FindRegion(off0, len1); + Test_Expect(true, ref result, reg == null); + + return result; + } + private static bool Test_SimpleLinear() { const int mapLen = 0x8000; AddressMap map = new AddressMap(mapLen); @@ -1294,27 +1431,27 @@ namespace CommonUtil { const int adr2 = 0x1700; Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off0, len0, adr0)); + map.AddEntry(off0, len0, adr0)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off1, len1, adr1)); + map.AddEntry(off1, len1, adr1)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off2, len2, adr2)); + map.AddEntry(off2, len2, adr2)); result &= map.DebugValidate(); Test_Expect(AddResult.OverlapExisting, ref result, - map.AddRegion(off0, len0, 0x1000)); + map.AddEntry(off0, len0, 0x1000)); Test_Expect(AddResult.OverlapFloating, ref result, - map.AddRegion(off0, FLOATING_LEN, 0x1000)); + map.AddEntry(off0, FLOATING_LEN, 0x1000)); Test_Expect(AddResult.StraddleExisting, ref result, - map.AddRegion(off0 + 1, len0, 0x1000)); + map.AddEntry(off0 + 1, len0, 0x1000)); Test_Expect(AddResult.InvalidValue, ref result, - map.AddRegion(off0, mapLen + 1, 0x1000)); + map.AddEntry(off0, mapLen + 1, 0x1000)); // One region to wrap them all. Add then remove. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off0, mapLen, 0x0000)); - Test_Expect(true, ref result, map.RemoveRegion(off0, mapLen)); - Test_Expect(false, ref result, map.RemoveRegion(off0, mapLen)); + map.AddEntry(off0, mapLen, 0x0000)); + Test_Expect(true, ref result, map.RemoveEntry(off0, mapLen)); + Test_Expect(false, ref result, map.RemoveEntry(off0, mapLen)); Test_Expect(adr0, ref result, map.OffsetToAddress(off0)); Test_Expect(adr1, ref result, map.OffsetToAddress(off1)); @@ -1354,16 +1491,16 @@ namespace CommonUtil { const int adr2 = NON_ADDR; Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off0, len0, adr0)); + map.AddEntry(off0, len0, adr0)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off1, len1, adr1)); + map.AddEntry(off1, len1, adr1)); // Try to remove the implicit no-address zone. - Test_Expect(false, ref result, map.RemoveRegion(0, off0)); + Test_Expect(false, ref result, map.RemoveEntry(0, off0)); // Add non-addressable area into the middle of the second region. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off2, len2, adr2)); + map.AddEntry(off2, len2, adr2)); Test_Expect(adr0, ref result, map.OffsetToAddress(off0)); Test_Expect(adr1, ref result, map.OffsetToAddress(off1)); @@ -1390,56 +1527,56 @@ namespace CommonUtil { bool result = true; // Nested with shared start offset. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000100, 0x0400, 0x4000, "preA0", false)); + map.AddEntry(0x000100, 0x0400, 0x4000, "preA0", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000100, 0x0100, 0x7000, "preA1", false)); + map.AddEntry(0x000100, 0x0100, 0x7000, "preA1", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000100, 0x0300, 0x5000, "preA2", false)); + map.AddEntry(0x000100, 0x0300, 0x5000, "preA2", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000100, 0x0200, 0x6000, "preA3", false)); + map.AddEntry(0x000100, 0x0200, 0x6000, "preA3", false)); // Add a couple of floaters. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x0000ff, FLOATING_LEN, 0x30ff, "preA4", false)); + map.AddEntry(0x0000ff, FLOATING_LEN, 0x30ff, "preA4", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000101, FLOATING_LEN, 0x3101, "preA5", false)); + map.AddEntry(0x000101, FLOATING_LEN, 0x3101, "preA5", false)); Test_Expect(AddResult.OverlapFloating, ref result, - map.AddRegion(0x000100, FLOATING_LEN, 0x3100, "preA6", false)); + map.AddEntry(0x000100, FLOATING_LEN, 0x3100, "preA6", false)); // Nested with shared end offset. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000fff, FLOATING_LEN, 0x3fff, "preB0", false)); + map.AddEntry(0x000fff, FLOATING_LEN, 0x3fff, "preB0", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001200, 0x0200, 0x6000, "preB1", false)); + map.AddEntry(0x001200, 0x0200, 0x6000, "preB1", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001000, 0x0400, 0x4000, "preB2", false)); + map.AddEntry(0x001000, 0x0400, 0x4000, "preB2", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001100, 0x0300, 0x5000, "preB3", false)); + map.AddEntry(0x001100, 0x0300, 0x5000, "preB3", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001300, 0x0100, 0x7000, "preB4", false)); + map.AddEntry(0x001300, 0x0100, 0x7000, "preB4", false)); // Single-byte region at start and end. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001200, 1, 0x8200, "preB5", false)); + map.AddEntry(0x001200, 1, 0x8200, "preB5", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x0013ff, 1, 0x83ff, "preB6", false)); + map.AddEntry(0x0013ff, 1, 0x83ff, "preB6", false)); // Nested with no common edge, building from outside-in. - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002000, 0x0800, 0x4000)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002100, 0x0600, 0x5000)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002200, 0x0400, 0x6000)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002300, 0x0200, 0x7000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002000, 0x0800, 0x4000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002100, 0x0600, 0x5000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002200, 0x0400, 0x6000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002300, 0x0200, 0x7000)); // Nested with no common edge, building from inside-out. - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003300, 0x0200, 0x7000)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003200, 0x0400, 0x6000)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003100, 0x0600, 0x5000)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003000, 0x0800, 0x4000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003300, 0x0200, 0x7000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003200, 0x0400, 0x6000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003100, 0x0600, 0x5000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003000, 0x0800, 0x4000)); // Try floater then overlap. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x004000, FLOATING_LEN, 0x8000)); + map.AddEntry(0x004000, FLOATING_LEN, 0x8000)); Test_Expect(AddResult.OverlapFloating, ref result, - map.AddRegion(0x004000, 0x100, 0x8000)); - Test_Expect(true, ref result, map.RemoveRegion(0x004000, FLOATING_LEN)); + map.AddEntry(0x004000, 0x100, 0x8000)); + Test_Expect(true, ref result, map.RemoveEntry(0x004000, FLOATING_LEN)); Test_Expect(0x30ff, ref result, map.OffsetToAddress(0x0000ff)); Test_Expect(0x7000, ref result, map.OffsetToAddress(0x000100)); @@ -1466,10 +1603,10 @@ namespace CommonUtil { AddressMap map = new AddressMap(mapLen); bool result = true; - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x000000, 0x2000, 0x8000)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002000, 0x2000, 0x8000)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002100, 0x0200, 0xe100)); - Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003100, 0x0200, 0xf100)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000000, 0x2000, 0x8000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002000, 0x2000, 0x8000)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002100, 0x0200, 0xe100)); + Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003100, 0x0200, 0xf100)); Test_Expect(0x003105, ref result, map.AddressToOffset(0x000000, 0xf105)); Test_Expect(0x003105, ref result, map.AddressToOffset(0x002100, 0xf105)); @@ -1504,19 +1641,19 @@ namespace CommonUtil { // Pyramid shape, all regions start at same address except last. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000000, 0x6000, 0x8000)); + map.AddEntry(0x000000, 0x6000, 0x8000)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001000, 0x4000, 0x8000)); + map.AddEntry(0x001000, 0x4000, 0x8000)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x002000, 0x2000, 0x7fff)); + map.AddEntry(0x002000, 0x2000, 0x7fff)); // Second pyramid. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x006000, 0x6000, 0x8000)); + map.AddEntry(0x006000, 0x6000, 0x8000)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x007000, 0x4000, 0x8000)); + map.AddEntry(0x007000, 0x4000, 0x8000)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x008000, 0x2000, 0x8000)); + map.AddEntry(0x008000, 0x2000, 0x8000)); // Children take priority over the start node. Test_Expect(0x002001, ref result, map.AddressToOffset(0x000000, 0x8000)); @@ -1559,6 +1696,8 @@ namespace CommonUtil { public static bool Test() { bool ok = true; + ok &= Test_Primitives(); + ok &= Test_Find(); ok &= Test_SimpleLinear(); ok &= Test_SimpleFloatGap(); ok &= Test_Nested(); diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index 7e31cdf..d75a0ac 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -37,7 +37,6 @@ namespace SourceGen.AsmGen { // makefile rules. Since ".S" is pretty universal for assembly language sources, // I'm sticking with that. private const string ASM_FILE_SUFFIX = "_acme.S"; // must start with underscore - private const string CLOSE_PSEUDOPC = "} ;!pseudopc"; // IGenerator public DisasmProject Project { get; private set; } @@ -124,7 +123,8 @@ namespace SourceGen.AsmGen { new PseudoOp.PseudoOpNames(new Dictionary { { "EquDirective", "=" }, //VarDirective - { "OrgDirective", "!pseudopc" }, + { "ArStartDirective", "!pseudopc" }, + { "ArEndDirective", "}" }, //RegWidthDirective // !al, !as, !rl, !rs //DataBankDirective { "DefineData1", "!byte" }, @@ -286,12 +286,12 @@ namespace SourceGen.AsmGen { // don't try OutputLine(SourceFormatter.FullLineCommentDelimiter + "ACME can't handle 65816 code that lives outside bank zero"); - int orgAddr = Project.AddrMap.OffsetToAddress(0); - AddressMap.AddressRegion fakeEnt = new AddressMap.AddressRegion(0, - Project.FileData.Length, orgAddr); - OutputOrgDirective(fakeEnt, true); + int firstAddr = Project.AddrMap.OffsetToAddress(0); + AddressMap.AddressRegion fakeRegion = new AddressMap.AddressRegion(0, + Project.FileData.Length, firstAddr); + OutputArDirective(fakeRegion, true); OutputDenseHex(0, Project.FileData.Length, string.Empty, string.Empty); - OutputOrgDirective(fakeEnt, false); + OutputArDirective(fakeRegion, false); } else { GenCommon.Generate(this, sw, worker); } @@ -568,7 +568,7 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { + public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) { // This is similar in operation to the AsmTass64 implementation. See comments there. Debug.Assert(mPcDepth >= 0); if (isStart) { @@ -587,15 +587,19 @@ namespace SourceGen.AsmGen { OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); } } - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), - SourceFormatter.FormatHexValue(addrEntry.Address, 4) + " {", string.Empty); + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), + SourceFormatter.FormatHexValue(addrEntry.Address, 4) + " {", + string.Empty); mPcDepth++; } else { mPcDepth--; if (mPcDepth > 0 || !mFirstIsOpen) { // close previous block - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(CLOSE_PSEUDOPC), + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective), string.Empty, string.Empty); + //";" + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective)); } else { // mark initial "*=" region as closed, but don't output anything mFirstIsOpen = false; diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index be5e599..760d47c 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -106,7 +106,8 @@ namespace SourceGen.AsmGen { new PseudoOp.PseudoOpNames(new Dictionary { { "EquDirective", "=" }, { "VarDirective", ".set" }, - { "OrgDirective", ".org" }, + { "ArStartDirective", ".org" }, + //ArEndDirective //RegWidthDirective // .a8, .a16, .i8, .i16 //DataBankDirective { "DefineData1", ".byte" }, @@ -250,20 +251,10 @@ namespace SourceGen.AsmGen { sw.WriteLine("MEMORY {"); sw.WriteLine(" MAIN: file=%O, start=%S, size=65536;"); - //int i = 0; - //foreach (AddressMap.AddressMapEntry ame in Project.AddrMap) { - // sw.WriteLine(string.Format("# MEM{0:D3}: file=%O, start=${1:x4}, size={2};", - // i, ame.Address, ame.Length)); - // i++; - //} sw.WriteLine("}"); sw.WriteLine("SEGMENTS {"); sw.WriteLine(" CODE: load=MAIN, type=rw;"); - //foreach (AddressMap.AddressMapEntry ame in Project.AddrMap) { - // sw.WriteLine(string.Format("# SEG{0:D3}: load=MEM{0:D3}, type=rw;", i)); - // i++; - //} sw.WriteLine("}"); sw.WriteLine("FEATURES {}"); @@ -493,24 +484,6 @@ namespace SourceGen.AsmGen { OutputLine(labelStr, opcodeStr, operandStr, commentStr); labelStr = commentStr = string.Empty; } - - //StringBuilder sb = new StringBuilder(MAX_OPERAND_LEN); - //int maxPerLine = MAX_OPERAND_LEN / 4; - //int numChunks = (length + maxPerLine - 1) / maxPerLine; - //for (int chunk = 0; chunk < numChunks; chunk++) { - // int chunkStart = chunk * maxPerLine; - // int chunkEnd = Math.Min((chunk + 1) * maxPerLine, length); - // for (int i = chunkStart; i < chunkEnd; i++) { - // if (i != chunkStart) { - // sb.Append(','); - // } - // sb.Append(formatter.FormatHexValue(data[offset + i], 2)); - // } - - // OutputLine(labelStr, opcodeStr, sb.ToString(), commentStr); - // labelStr = commentStr = string.Empty; - // sb.Clear(); - //} } /// @@ -561,30 +534,12 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { + public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) { if (!isStart) { return; } - //// Linear search for offset. List should be small, so this should be quick. - //int index = 0; - //foreach (AddressMap.AddressMapEntry ame in Project.AddrMap) { - // if (ame.Offset == addrEntry.Offset && ame.Length == addrEntry.Length) { - // break; - // } - // index++; - //} - - //mLineBuilder.Clear(); - //TextUtil.AppendPaddedString(mLineBuilder, ";", 0); - //// using +1 to make it look like the comment ';' shifted it over - //TextUtil.AppendPaddedString(mLineBuilder, SourceFormatter.FormatPseudoOp(".segment"), - // mColumnWidths[0] + 1); - //TextUtil.AppendPaddedString(mLineBuilder, string.Format("\"SEG{0:D3}\"", index), - // mColumnWidths[0] + mColumnWidths[1] + 1); - //OutputLine(mLineBuilder.ToString()); - - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); } diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 6eb583f..437d1f5 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -100,7 +100,8 @@ namespace SourceGen.AsmGen { new PseudoOp.PseudoOpNames(new Dictionary { { "EquDirective", "equ" }, { "VarDirective", "equ" }, - { "OrgDirective", "org" }, + { "ArStartDirective", "org" }, + //ArEndDirective //RegWidthDirective //DataBankDirective { "DefineData1", "dfb" }, @@ -484,10 +485,12 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { + public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) { if (isStart) { - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), - SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), + SourceFormatter.FormatHexValue(addrEntry.Address, 4), + string.Empty); } } diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 21d8d64..32d0063 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -137,7 +137,8 @@ namespace SourceGen.AsmGen { new PseudoOp.PseudoOpNames(new Dictionary { { "EquDirective", "=" }, { "VarDirective", ".var" }, - { "OrgDirective", ".logical" }, + { "ArStartDirective", ".logical" }, + { "ArEndDirective", ".here" }, //RegWidthDirective // .as, .al, .xs, .xl //DataBankDirective { "DefineData1", ".byte" }, @@ -158,7 +159,6 @@ namespace SourceGen.AsmGen { //StrLen16 { "StrDci", ".shift" } }); - private const string HERE_PSEUDO_OP = ".here"; // IGenerator @@ -653,7 +653,7 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { + public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) { // 64tass separates the "compile offset", which determines where the output fits // into the generated binary, and "program counter", which determines the code // the assembler generates. Since we need to explicitly specify every byte in @@ -692,14 +692,17 @@ namespace SourceGen.AsmGen { //OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); } } - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), - SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), + SourceFormatter.FormatHexValue(addrEntry.Address, 4), + string.Empty); mPcDepth++; } else { mPcDepth--; if (mPcDepth > 0 || !mFirstIsOpen) { // close previous block - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(HERE_PSEUDO_OP), + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective), string.Empty, string.Empty); } else { // mark initial "*=" region as closed, but don't output anything diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs index a177f17..390db74 100644 --- a/SourceGen/AsmGen/GenCommon.cs +++ b/SourceGen/AsmGen/GenCommon.cs @@ -76,12 +76,16 @@ namespace SourceGen.AsmGen { } } - // Check for address changes. There may be more than one at a given offset. + // Check for address range starts. There may be more than one at a given offset. AddressMap.AddressChange change = addrIter.Current; while (change != null && change.Offset == offset) { - gen.OutputOrgDirective(change.Region, change.IsStart); - addrIter.MoveNext(); - change = addrIter.Current; + if (change.IsStart) { + gen.OutputArDirective(change.Region, change.IsStart); + addrIter.MoveNext(); + change = addrIter.Current; + } else { + break; + } } List lvars = lvLookup.GetVariablesDefinedAtOffset(offset); @@ -130,6 +134,22 @@ namespace SourceGen.AsmGen { offset += attr.Length; } + // Check for address region ends. There may be more than one at a given offset. + // The end-region offset is listed as the byte *following* the instruction + // or data item, so it should match the updated offset. + // + // If we encounter a region start, we'll handle that at the top of the next + // loop iteration. + while (change != null && change.Offset == offset) { + if (!change.IsStart) { + gen.OutputArDirective(change.Region, change.IsStart); + addrIter.MoveNext(); + change = addrIter.Current; + } else { + break; + } + } + // Update progress meter. We don't want to spam it, so just ping it 10x. int curProgress = (offset * 10) / proj.FileData.Length; if (lastProgress != curProgress) { @@ -143,19 +163,7 @@ namespace SourceGen.AsmGen { } } - // Close off any open ORG blocks. - while (addrIter.Current != null) { - AddressMap.AddressChange change = addrIter.Current; - if (change.Offset != offset) { - Debug.WriteLine("Closing offset mismatch: change=+" + - change.Offset.ToString("x6") + ", cur offset=+" + offset.ToString("x6")); - Debug.Assert(false); - } - Debug.Assert(change.Offset == offset); - gen.OutputOrgDirective(change.Region, change.IsStart); - addrIter.MoveNext(); - change = addrIter.Current; - } + Debug.Assert(offset == proj.FileDataLength); } private static void GenerateHeader(IGenerator gen, StreamWriter sw) { @@ -565,11 +573,11 @@ namespace SourceGen.AsmGen { return false; } AddressMap.AddressChange change = iter.Current; - if (change.Region.Length != 2) { + if (change.Region.ActualLength != 2) { Debug.WriteLine("PRG test: first entry is not a two-byte region"); } // Confirm there's an address map entry at offset 2. - if (project.AddrMap.GetRegions(0x000002).Count == 0) { + if (project.AddrMap.GetEntries(0x000002).Count == 0) { //Debug.WriteLine("PRG test: no ORG at +2"); return false; } diff --git a/SourceGen/AsmGen/IGenerator.cs b/SourceGen/AsmGen/IGenerator.cs index 361fccd..4f1cdf5 100644 --- a/SourceGen/AsmGen/IGenerator.cs +++ b/SourceGen/AsmGen/IGenerator.cs @@ -166,11 +166,11 @@ namespace SourceGen.AsmGen { LocalVariableTable allDefs); /// - /// Outputs a code origin directive. + /// Outputs an address region directive. /// - /// Address map entry object. - /// True if we're outputing a region-start directive. - void OutputOrgDirective(CommonUtil.AddressMap.AddressRegion addrEntry, bool isStart); + /// Address region object. + /// True if this is the start of a region. + void OutputArDirective(CommonUtil.AddressMap.AddressRegion region, bool isStart); /// /// Notify the assembler of a change in register width. diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index 4b5c619..fed6cf8 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -271,7 +271,7 @@ namespace SourceGen { AddrMap = new AddressMap(fileDataLen); // set default load address to $1000; override later - AddrMap.AddRegion(0x000000, fileDataLen, 0x1000); + AddrMap.AddEntry(0x000000, fileDataLen, 0x1000); // Default value is "no tag". AnalyzerTags = new CodeAnalysis.AnalyzerTag[fileDataLen]; @@ -400,9 +400,9 @@ namespace SourceGen { int loadAddr = RawData.GetWord(mFileData, 0, 2, false); // TODO(org): use NON_ADDR for first two bytes AddressMap.AddResult addRes = - AddrMap.AddRegion(0, 2, loadAddr < 2 ? 0 : loadAddr - 2); + AddrMap.AddEntry(0, 2, loadAddr < 2 ? 0 : loadAddr - 2); Debug.Assert(addRes == AddressMap.AddResult.Okay); - addRes = AddrMap.AddRegion(2, mFileData.Length - 2, loadAddr); + addRes = AddrMap.AddEntry(2, mFileData.Length - 2, loadAddr); Debug.Assert(addRes == AddressMap.AddResult.Okay); OperandFormats[0] = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE, @@ -413,7 +413,7 @@ namespace SourceGen { } else { int loadAddr = SystemDefaults.GetLoadAddress(sysDef); AddressMap.AddResult addRes = - AddrMap.AddRegion(0, mFileData.Length, loadAddr); + AddrMap.AddEntry(0, mFileData.Length, loadAddr); Debug.Assert(addRes == AddressMap.AddResult.Okay); } @@ -756,16 +756,19 @@ namespace SourceGen { } /// - /// Checks to see if any part of the address map runs across a bank boundary. + /// Checks to see if any section of the address map runs across a bank boundary. /// private void ValidateAddressMap() { - // Use the change list, because the map entry list can have "floating" length values. + // Use the change list, because the map entry list can have "floating" length values + // (which don't automatically stop at bank boundaries). IEnumerator addrIter = AddrMap.AddressChangeIterator; while (addrIter.MoveNext()) { AddressMap.AddressChange change = addrIter.Current; + if (!change.IsStart) { + continue; + } AddressMap.AddressRegion region = change.Region; - if (change.IsStart && - (region.Address & 0xff0000) != ((region.Address + region.Length - 1) & 0xff0000)) { + if ((region.Address & 0xff0000) != ((region.Address + region.ActualLength - 1) & 0xff0000)) { string fmt = Res.Strings.MSG_BANK_OVERRUN_DETAIL_FMT; int firstNext = (region.Address & 0xff0000) + 0x010000; int badOffset = region.Offset + (firstNext - region.Address); @@ -2149,18 +2152,18 @@ namespace SourceGen { AddressMap addrMap = AddrMap; if ((int)oldValue == AddressMap.NON_ADDR) { // adding new entry - if (addrMap.AddRegion(offset, AddressMap.FLOATING_LEN, + if (addrMap.AddEntry(offset, AddressMap.FLOATING_LEN, (int)newValue) != AddressMap.AddResult.Okay) { Debug.Assert(false, "failed adding region"); } } else if ((int)newValue == AddressMap.NON_ADDR) { // removing existing entry - if (!addrMap.RemoveRegion(offset, AddressMap.FLOATING_LEN)) { + if (!addrMap.RemoveEntry(offset, AddressMap.FLOATING_LEN)) { Debug.Assert(false, "failed removing region"); } } else { // updating existing entry - if (!addrMap.EditRegion(offset, AddressMap.FLOATING_LEN, + if (!addrMap.EditEntry(offset, AddressMap.FLOATING_LEN, (int) newValue, string.Empty, false)) { Debug.Assert(false, "failed editing region"); } diff --git a/SourceGen/Exporter.cs b/SourceGen/Exporter.cs index c7346ab..2ab8ef8 100644 --- a/SourceGen/Exporter.cs +++ b/SourceGen/Exporter.cs @@ -353,10 +353,12 @@ namespace SourceGen { case LineListGen.Line.Type.EquDirective: case LineListGen.Line.Type.RegWidthDirective: case LineListGen.Line.Type.DataBankDirective: - case LineListGen.Line.Type.OrgDirective: + case LineListGen.Line.Type.ArStartDirective: + case LineListGen.Line.Type.ArEndDirective: case LineListGen.Line.Type.LocalVariableTable: if (parts.IsLongComment) { - // This happens for long comments embedded in LV tables. + // This happens for long comments generated for LV tables (e.g. "empty + // variable table"). TextUtil.AppendPaddedString(sb, parts.Comment, mColStart[(int)Col.Label]); break; } @@ -714,7 +716,8 @@ namespace SourceGen { case LineListGen.Line.Type.EquDirective: case LineListGen.Line.Type.RegWidthDirective: case LineListGen.Line.Type.DataBankDirective: - case LineListGen.Line.Type.OrgDirective: + case LineListGen.Line.Type.ArStartDirective: + case LineListGen.Line.Type.ArEndDirective: case LineListGen.Line.Type.LocalVariableTable: if (parts.IsLongComment) { // This happens for long comments embedded in LV tables, e.g. diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 6fd3dfe..e7a4daf 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -92,7 +92,7 @@ namespace SourceGen { /// One of these per line of output in the display. It should be possible to draw /// all of the output without needing to refer back to the project data. (Currently /// making an exception for some selection-dependent field highlighting.) - /// + /// /// Base fields are immutable, but the Parts property is set after creation. /// public class Line { @@ -116,14 +116,15 @@ namespace SourceGen { Blank = 1 << 4, // Assembler directives. - OrgDirective = 1 << 5, - EquDirective = 1 << 6, - RegWidthDirective = 1 << 7, - DataBankDirective = 1 << 8, + ArStartDirective = 1 << 5, + ArEndDirective = 1 << 6, + EquDirective = 1 << 7, + RegWidthDirective = 1 << 8, + DataBankDirective = 1 << 9, // Additional metadata. - LocalVariableTable = 1 << 9, - VisualizationSet = 1 << 10, + LocalVariableTable = 1 << 10, + VisualizationSet = 1 << 11, } /// @@ -535,7 +536,8 @@ namespace SourceGen { // Nothing to do. parts = FormattedParts.CreateBlankLine(); break; - case Line.Type.OrgDirective: + case Line.Type.ArStartDirective: + case Line.Type.ArEndDirective: case Line.Type.RegWidthDirective: case Line.Type.DataBankDirective: case Line.Type.LongComment: @@ -989,29 +991,53 @@ namespace SourceGen { int offset = startOffset; while (offset <= endOffset) { - bool blankAdded = false; + bool spaceAdded = false; Anattrib attr = mProject.GetAnattrib(offset); if (attr.IsInstructionStart && offset > 0 && mProject.GetAnattrib(offset - 1).IsData) { // Transition from data to code. (Don't add blank line for inline data.) lines.Add(GenerateBlankLine(offset)); - blankAdded = true; + spaceAdded = true; } else if (mProject.VisualizationSets.ContainsKey(offset) && !addBlank) { // Blank line before visualization set helps keep image visually grouped // with its data. lines.Add(GenerateBlankLine(offset)); - blankAdded = true; + spaceAdded = true; } else if (addBlank) { // Previous instruction wanted to be followed by a blank line. lines.Add(GenerateBlankLine(offset)); - blankAdded = true; + spaceAdded = true; } addBlank = false; - // Start with address region changes. + // Insert long comments and notes. These may span multiple display lines, + // and require word-wrap, so it's easiest just to render them fully here. + // Set "spaceAdded" to true so .arstart doesn't try to add one after the comment. + // + // TODO: integrate into FormattedOperandCache so we don't have to + // regenerate them unless they change. Use the MLC as the dependency. + if (mProject.Notes.TryGetValue(offset, out MultiLineComment noteData)) { + List formatted = noteData.FormatText(mFormatter, "NOTE: "); + StringListToLines(formatted, offset, Line.Type.Note, + noteData.BackgroundColor, NoteColorMultiplier, lines); + spaceAdded = true; + } + if (mProject.LongComments.TryGetValue(offset, out MultiLineComment longComment)) { + List formatted = longComment.FormatText(mFormatter, string.Empty); + StringListToLines(formatted, offset, Line.Type.LongComment, + longComment.BackgroundColor, NoteColorMultiplier, lines); + spaceAdded = true; + } + if (mProject.VisualizationSets.TryGetValue(offset, out VisualizationSet visSet)) { + lines.Add(new Line(offset, 0, Line.Type.VisualizationSet)); + spaceAdded = true; + } + + // Handle address region starts. while (addrIter.Current != null && addrIter.Current.Offset <= offset) { AddressMap.AddressChange change = addrIter.Current; - Debug.Assert(change.Offset == offset); // shouldn't be embedded in something + // Range starts/ends shouldn't be embedded in something. + Debug.Assert(change.Offset == offset); AddressMap.AddressRegion region = change.Region; if (region.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) { @@ -1024,40 +1050,22 @@ namespace SourceGen { if (change.IsStart) { // Blank line above ORG directive, except at top of file or when we've // already added one for another reason. - if (region.Offset != 0 && !blankAdded) { + if (region.Offset != 0 && !spaceAdded) { lines.Add(GenerateBlankLine(offset)); } - blankAdded = false; // next one will need a blank line + spaceAdded = false; // next one will need a blank line // TODO(org): pre-label (address / label only, logically part of ORG) - Line newLine = new Line(offset, 0, Line.Type.OrgDirective); + Line newLine = new Line(offset, 0, Line.Type.ArStartDirective); string addrStr = mFormatter.FormatHexValue(region.Address, 4); newLine.Parts = FormattedParts.CreateDirective( - mFormatter.FormatPseudoOp(mPseudoOpNames.OrgDirective), addrStr); + mFormatter.FormatPseudoOp(mPseudoOpNames.ArStartDirective), addrStr); lines.Add(newLine); + addrIter.MoveNext(); } else { - // TODO(org) + // Next entry is an end marker. + break; } - - addrIter.MoveNext(); - } - - // Insert long comments and notes. These may span multiple display lines, - // and require word-wrap, so it's easiest just to render them fully here. - // TODO: integrate into FormattedOperandCache so we don't have to - // regenerate them unless they change. Use the MLC as the dependency. - if (mProject.Notes.TryGetValue(offset, out MultiLineComment noteData)) { - List formatted = noteData.FormatText(mFormatter, "NOTE: "); - StringListToLines(formatted, offset, Line.Type.Note, - noteData.BackgroundColor, NoteColorMultiplier, lines); - } - if (mProject.LongComments.TryGetValue(offset, out MultiLineComment longComment)) { - List formatted = longComment.FormatText(mFormatter, string.Empty); - StringListToLines(formatted, offset, Line.Type.LongComment, - longComment.BackgroundColor, NoteColorMultiplier, lines); - } - if (mProject.VisualizationSets.TryGetValue(offset, out VisualizationSet visSet)) { - lines.Add(new Line(offset, 0, Line.Type.VisualizationSet)); } // Local variable tables come next. Defer rendering. @@ -1210,6 +1218,26 @@ namespace SourceGen { } offset += attr.Length; } + + // Check for address region ends, which will be positioned at the updated offset + // (unless they somehow got embedded inside something else). + while (addrIter.Current != null && addrIter.Current.Offset <= offset) { + AddressMap.AddressChange change = addrIter.Current; + // Range starts/ends shouldn't be embedded in something. + Debug.Assert(change.Offset == offset); + + if (!change.IsStart) { + // NOTE: last end(s) are at an offset outside file bounds. + Line newLine = new Line(offset, 0, Line.Type.ArEndDirective); + newLine.Parts = FormattedParts.CreateDirective( + mFormatter.FormatPseudoOp(mPseudoOpNames.ArEndDirective), + string.Empty); + lines.Add(newLine); + addrIter.MoveNext(); + } else { + break; + } + } } } diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index f49f631..7b27634 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -1530,10 +1530,11 @@ namespace SourceGen { EditProjectSymbol((CodeListColumn)col); } break; - case LineListGen.Line.Type.OrgDirective: + case LineListGen.Line.Type.ArStartDirective: if (CanEditAddress()) { EditAddress(); } + // TODO(org): handle ArEndDirective break; case LineListGen.Line.Type.RegWidthDirective: if (CanEditStatusFlags()) { @@ -1771,7 +1772,8 @@ namespace SourceGen { LineListGen.Line selLine = CodeLineList[selIndex]; if (selLine.LineType != LineListGen.Line.Type.Code && selLine.LineType != LineListGen.Line.Type.Data && - selLine.LineType != LineListGen.Line.Type.OrgDirective) { + selLine.LineType != LineListGen.Line.Type.ArStartDirective) { + // TODO(org): handle ArEnd return false; } @@ -1803,55 +1805,31 @@ namespace SourceGen { int lastIndex = mMainWin.CodeListView_GetLastSelectedIndex(); int firstOffset = CodeLineList[selIndex].FileOffset; int lastOffset = CodeLineList[lastIndex].FileOffset; - int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan; - int nextAddr; + //int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan; AddressMap addrMap = mProject.AddrMap; // TODO(org): rewrite this - need to identify the specific .ORG statement since // there can now be several at a single offset - if (firstOffset == lastOffset || nextOffset == mProject.FileDataLength) { - // Single item (which might not be a single *line*) is selected, or the - // last selected item is the end of the file. - nextOffset = -1; - nextAddr = AddressMap.NON_ADDR; - } else { - // Compute "nextAddr". If there's an existing entry at nextOffset, we use - // that. If not, we use the "load address", which is determined by the very - // first address. - // - // I tried this by just removing the selected entry and seeing what the address - // would be without it, useful for relocations inside relocations. This worked - // poorly when relocations were chained, i.e. two consecutive blocks were - // relocated to different places. The end address of the second block gets - // set based on the first address of the first block, which doesn't seem useful. -#if false - nextAddr = mProject.AddrMap.Get(nextOffset); - if (nextAddr == AddressMap.NO_ENTRY_ADDR) { - AddressMap cloneMap = new AddressMap(mProject.AddrMap.GetEntryList()); - if (firstOffset != 0) { - cloneMap.Remove(firstOffset); - } - nextAddr = cloneMap.OffsetToAddress(nextOffset); - } -#else - int fileStartAddr = addrMap.OffsetToAddress(0); - nextAddr = ((fileStartAddr + nextOffset) & 0xffff) | (fileStartAddr & 0xff0000); -#endif + int addr = addrMap.OffsetToAddress(firstOffset); + int newLen = lastOffset - firstOffset; + if (newLen == 0) { + newLen = 123; } - - EditAddress dlg = new EditAddress(mMainWin, firstOffset, nextOffset, nextAddr, + AddressMap.AddressMapEntry newEntry = new AddressMap.AddressMapEntry(firstOffset, + newLen /*DEBUG - replace*/, addr, string.Empty, false); + EditAddress dlg = new EditAddress(mMainWin, newEntry, true, newLen, mProject, mFormatter); if (dlg.ShowDialog() != true) { return; } - if (firstOffset == 0 && dlg.NewAddress < 0) { - // Not allowed. The AddressMap will just put it back, which confuses - // the undo operation. - Debug.WriteLine("EditAddress: not allowed to remove address at offset +000000"); - return; - } + //if (firstOffset == 0 && dlg.NewAddress < 0) { + // // Not allowed. The AddressMap will just put it back, which confuses + // // the undo operation. + // Debug.WriteLine("EditAddress: not allowed to remove address at offset +000000"); + // return; + //} ChangeSet cs = new ChangeSet(1); @@ -3899,8 +3877,11 @@ namespace SourceGen { case LineListGen.Line.Type.Blank: lineTypeStr = "blank line"; break; - case LineListGen.Line.Type.OrgDirective: - lineTypeStr = "address directive"; + case LineListGen.Line.Type.ArStartDirective: + lineTypeStr = "address range start directive"; + break; + case LineListGen.Line.Type.ArEndDirective: + lineTypeStr = "address range end directive"; break; case LineListGen.Line.Type.RegWidthDirective: lineTypeStr = "register width directive"; diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs index 86470df..42a3d7c 100644 --- a/SourceGen/ProjectFile.cs +++ b/SourceGen/ProjectFile.cs @@ -673,7 +673,7 @@ namespace SourceGen { // TODO(org): serialize length, isRelative, and preLabel int length = CommonUtil.AddressMap.FLOATING_LEN; - AddressMap.AddResult addResult = proj.AddrMap.AddRegion(addr.Offset, + AddressMap.AddResult addResult = proj.AddrMap.AddEntry(addr.Offset, length, addr.Addr); if (addResult != CommonUtil.AddressMap.AddResult.Okay) { string msg = "off=+" + addr.Offset.ToString("x6") + " addr=$" + diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs index 13c4fbb..04a8c90 100644 --- a/SourceGen/PseudoOp.cs +++ b/SourceGen/PseudoOp.cs @@ -64,7 +64,8 @@ namespace SourceGen { public class PseudoOpNames { public string EquDirective { get; private set; } public string VarDirective { get; private set; } - public string OrgDirective { get; private set; } + public string ArStartDirective { get; private set; } + public string ArEndDirective { get; private set; } public string RegWidthDirective { get; private set; } public string DataBankDirective { get; private set; } @@ -115,7 +116,8 @@ namespace SourceGen { } return a.EquDirective == b.EquDirective && a.VarDirective == b.VarDirective && - a.OrgDirective == b.OrgDirective && + a.ArStartDirective == b.ArStartDirective && + a.ArEndDirective == b.ArEndDirective && a.RegWidthDirective == b.RegWidthDirective && a.DataBankDirective == b.DataBankDirective && a.DefineData1 == b.DefineData1 && @@ -145,7 +147,7 @@ namespace SourceGen { public override int GetHashCode() { // should be enough return (EquDirective == null ? 0 : EquDirective.GetHashCode()) ^ - (OrgDirective == null ? 0 : OrgDirective.GetHashCode()) ^ + (ArStartDirective == null ? 0 : ArStartDirective.GetHashCode()) ^ (DefineData1 == null ? 0 : DefineData1.GetHashCode()) ^ (Fill == null ? 0 : Fill.GetHashCode()); } @@ -226,7 +228,8 @@ namespace SourceGen { new PseudoOpNames(new Dictionary { { "EquDirective", ".eq" }, { "VarDirective", ".var" }, - { "OrgDirective", ".org" }, + { "ArStartDirective", ".arstart" }, + { "ArEndDirective", ".arend" }, { "RegWidthDirective", ".rwid" }, { "DataBankDirective", ".dbank" }, diff --git a/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S b/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S index 69ed4b0..65415a8 100644 --- a/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S +++ b/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S @@ -63,7 +63,7 @@ LABEL !hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff !pseudopc $1408 { !fill 8,$82 ;note no-op .ORG !fill 8,$83 - } ;!pseudopc + } !pseudopc $1428 { !fill 8,$83 ;meaningful .ORG !fill 8,$84 @@ -84,4 +84,4 @@ L14A8 !fill 8,$8b !fill 8,$8c !byte %10001100 !fill 7,$8c - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20030-labels-and-symbols_acme.S b/SourceGen/SGTestData/Expected/20030-labels-and-symbols_acme.S index 34d52e5..794febe 100644 --- a/SourceGen/SGTestData/Expected/20030-labels-and-symbols_acme.S +++ b/SourceGen/SGTestData/Expected/20030-labels-and-symbols_acme.S @@ -106,4 +106,4 @@ calls jsr L1015 L1160 adc #BMI1 rts - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20032-labels-and-symbols_acme.S b/SourceGen/SGTestData/Expected/20032-labels-and-symbols_acme.S index 85e248c..5a94abd 100644 --- a/SourceGen/SGTestData/Expected/20032-labels-and-symbols_acme.S +++ b/SourceGen/SGTestData/Expected/20032-labels-and-symbols_acme.S @@ -23,4 +23,4 @@ !hex 656172206f6e20746865206669727374206c696e652e20205468652071756963 !hex 6b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920 !hex 646f67732ea97aa959a934c230a97b56a95a34a9341260 - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20040-address-changes_64tass.S b/SourceGen/SGTestData/Expected/20040-address-changes_64tass.S index 3c3476e..2e802a7 100644 --- a/SourceGen/SGTestData/Expected/20040-address-changes_64tass.S +++ b/SourceGen/SGTestData/Expected/20040-address-changes_64tass.S @@ -57,8 +57,8 @@ L2080 bit L2080 lda $00 beq _L2100 .byte $ad - .here + .logical $2100 _L2100 nop nop @@ -72,8 +72,8 @@ _L2100 nop .here .logical $2820 .fill 18,$00 - .here + .logical $3000 _L3000 bit _L3000 lda #$44 @@ -110,8 +110,8 @@ _L3182 bit _L3182 label1 .byte $ea .byte $ea - .here + .logical $3200 L3200 bit L3200 .byte $00 diff --git a/SourceGen/SGTestData/Expected/20040-address-changes_acme.S b/SourceGen/SGTestData/Expected/20040-address-changes_acme.S index cbc3bb0..bb38993 100644 --- a/SourceGen/SGTestData/Expected/20040-address-changes_acme.S +++ b/SourceGen/SGTestData/Expected/20040-address-changes_acme.S @@ -7,7 +7,7 @@ jsr L1107 jmp L2000 - } ;!pseudopc + } !pseudopc $1100 { L1100 bit L1100 L1103 lda #$11 @@ -16,7 +16,7 @@ L1107 ldy #$11 clv bvc L1103 - } ;!pseudopc + } !pseudopc $1100 { @L1100_0 bit @L1100_0 lda #$22 @@ -24,7 +24,7 @@ L1107 ldy #$11 ldy #$22 jmp @L1105 - } ;!pseudopc + } !pseudopc $1100 { @L1100_1 bit @L1100_1 lda #$33 @@ -33,20 +33,20 @@ L1107 ldy #$11 sec bcs @L1107_0 - } ;!pseudopc + } !pseudopc $2000 { L2000 bit L2000 beq $2018 bne @L2020 - } ;!pseudopc + } !pseudopc $2020 { @L2020 bit @L2020 beq $2028 bne L2080 offend nop - } ;!pseudopc + } !pseudopc $2080 { L2080 bit L2080 lda offend @@ -60,23 +60,23 @@ L2080 bit L2080 lda $00 beq @L2100 !byte $ad + } - } ;!pseudopc !pseudopc $2100 { @L2100 nop nop jmp @L3000 - } ;!pseudopc + } !pseudopc $2800 { !byte $00 !byte $28 !fill 14,$00 - } ;!pseudopc + } !pseudopc $2820 { !fill 18,$00 + } - } ;!pseudopc !pseudopc $3000 { @L3000 bit @L3000 lda #$44 @@ -86,7 +86,7 @@ L2080 bit L2080 ulabel !byte $00 !byte $01 - } ;!pseudopc + } !pseudopc $3100 { !byte $02 @@ -99,7 +99,7 @@ fwd bit fwd beq @L3182 !byte $ea !byte $ea - } ;!pseudopc + } !pseudopc $3180 { !byte $00 !byte $01 @@ -113,10 +113,10 @@ fwd bit fwd label1 !byte $ea !byte $ea + } - } ;!pseudopc !pseudopc $3200 { L3200 bit L3200 !byte $00 !byte $01 ;execution continues off end of file - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20042-address-changes_64tass.S b/SourceGen/SGTestData/Expected/20042-address-changes_64tass.S index 0816df0..d51d36b 100644 --- a/SourceGen/SGTestData/Expected/20042-address-changes_64tass.S +++ b/SourceGen/SGTestData/Expected/20042-address-changes_64tass.S @@ -63,8 +63,8 @@ _L22080 bit _L22080 & $ffff lda $00 beq _L22100 .byte $ad - .here + .logical $022100 _L22100 nop nop @@ -78,8 +78,8 @@ _L22100 nop .here .logical $022820 .fill 18,$00 - .here + .logical $023000 _L23000 bit _L23000 & $ffff lda #$44 @@ -115,8 +115,8 @@ _L23182 bit _L23182 & $ffff _label1 .byte $ea .byte $ea - .here + .logical $023200 _L23200 bit _L23200 & $ffff .byte $00 diff --git a/SourceGen/SGTestData/Expected/20042-address-changes_acme.S b/SourceGen/SGTestData/Expected/20042-address-changes_acme.S index 26e1087..6e80a44 100644 --- a/SourceGen/SGTestData/Expected/20042-address-changes_acme.S +++ b/SourceGen/SGTestData/Expected/20042-address-changes_acme.S @@ -8,4 +8,4 @@ !hex 0000000000000000000000000000002c0030a944a244a04482f5000001022c01 !hex 31ad0c30ad0d30ad0e30ad0f30ad0031f06deaea00012c8231ad9031ad9131ad !hex 00328070eaea2c00320001 - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20050-branches-and-banks_acme.S b/SourceGen/SGTestData/Expected/20050-branches-and-banks_acme.S index e9a014f..32fae05 100644 --- a/SourceGen/SGTestData/Expected/20050-branches-and-banks_acme.S +++ b/SourceGen/SGTestData/Expected/20050-branches-and-banks_acme.S @@ -19,12 +19,12 @@ L0012 lda+1 lodat+1 clc bcc LFFC0 - } ;!pseudopc + } !pseudopc $0080 { L0080 bit+2 L0080 jmp LFFC6 - } ;!pseudopc + } !pseudopc $ffc0 { LFFC0 bit LFFC0 LFFC3 clc @@ -32,4 +32,4 @@ LFFC3 clc LFFC6 rts - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20052-branches-and-banks_64tass.S b/SourceGen/SGTestData/Expected/20052-branches-and-banks_64tass.S index 7750ff5..b281aac 100644 --- a/SourceGen/SGTestData/Expected/20052-branches-and-banks_64tass.S +++ b/SourceGen/SGTestData/Expected/20052-branches-and-banks_64tass.S @@ -26,8 +26,8 @@ L440004 lda L440000 dat44 .word dat44 & $ffff .long dat44 - .here + .logical $44ffc0 L44FFC0 cmp L44FFC0 high44 beq _L44FFCB diff --git a/SourceGen/SGTestData/Expected/20060-target-adjustment_acme.S b/SourceGen/SGTestData/Expected/20060-target-adjustment_acme.S index 78806ce..cd705af 100644 --- a/SourceGen/SGTestData/Expected/20060-target-adjustment_acme.S +++ b/SourceGen/SGTestData/Expected/20060-target-adjustment_acme.S @@ -81,4 +81,4 @@ load11 lda #$11 lda @L2017 rts - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20100-label-dp_acme.S b/SourceGen/SGTestData/Expected/20100-label-dp_acme.S index d8c7740..155eb1d 100644 --- a/SourceGen/SGTestData/Expected/20100-label-dp_acme.S +++ b/SourceGen/SGTestData/Expected/20100-label-dp_acme.S @@ -299,4 +299,4 @@ L0080 bit+1 @L0082 @L0082 bit+1 @L0082 bit+1 @L0082 L0086 bit+2 L0086 - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20102-label-dp_acme.S b/SourceGen/SGTestData/Expected/20102-label-dp_acme.S index 30553c2..0030d43 100644 --- a/SourceGen/SGTestData/Expected/20102-label-dp_acme.S +++ b/SourceGen/SGTestData/Expected/20102-label-dp_acme.S @@ -291,4 +291,4 @@ L0080 bit+1 @L0082 bit+1 @L0082 L0086 bit+2 L0086 L0089 lda+3 L0089 - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20150-local-variables_acme.S b/SourceGen/SGTestData/Expected/20150-local-variables_acme.S index 1ac3ca1..cd8cec0 100644 --- a/SourceGen/SGTestData/Expected/20150-local-variables_acme.S +++ b/SourceGen/SGTestData/Expected/20150-local-variables_acme.S @@ -223,4 +223,4 @@ DPCODE nop beq @SPLIT2 rts - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20152-local-variables_acme.S b/SourceGen/SGTestData/Expected/20152-local-variables_acme.S index 1a2308c..280ee4e 100644 --- a/SourceGen/SGTestData/Expected/20152-local-variables_acme.S +++ b/SourceGen/SGTestData/Expected/20152-local-variables_acme.S @@ -294,4 +294,4 @@ L00E6 bit .ARG+2 bit $0100 rts - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S b/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S index 5ae107f..396872f 100644 --- a/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S +++ b/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S @@ -110,7 +110,7 @@ L1800 jsr PrintInlineNullString !byte $6e !byte $20 !byte $01 - } ;!pseudopc + } !pseudopc $1840 { !text "string" !byte $00 @@ -123,4 +123,4 @@ L1848 jsl PrintInlineL2String L184F jsr PrintInlineNullString adc $6e !byte $64 - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_64tass.S b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_64tass.S index a9d0a0b..7e44b62 100644 --- a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_64tass.S +++ b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_64tass.S @@ -8,8 +8,8 @@ .logical $200a .text "world" .byte $80 - .here + .logical $2100 L2100 lda #$00 sta addr1-1 ;edit this operand diff --git a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_acme.S b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_acme.S index b714551..9158e77 100644 --- a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_acme.S +++ b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_acme.S @@ -6,8 +6,8 @@ !pseudopc $200a { !text "world" !byte $80 + } - } ;!pseudopc !pseudopc $2100 { L2100 lda #$00 sta addr1-1 ;edit this operand @@ -20,4 +20,4 @@ addr1 !text "!?---" L2121 rts - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_64tass.S b/SourceGen/SGTestData/Expected/20212-reloc-data_64tass.S index 87ea74a..fdf2e0c 100644 --- a/SourceGen/SGTestData/Expected/20212-reloc-data_64tass.S +++ b/SourceGen/SGTestData/Expected/20212-reloc-data_64tass.S @@ -116,8 +116,8 @@ _L4FFE0 .long _L4FFE0 .byte $1e .byte $1f .text " !",$22,"#$%&'()*+,-./" - .here + .logical $023456 .as .xs diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_acme.S b/SourceGen/SGTestData/Expected/20212-reloc-data_acme.S index c8e86d1..e6de493 100644 --- a/SourceGen/SGTestData/Expected/20212-reloc-data_acme.S +++ b/SourceGen/SGTestData/Expected/20212-reloc-data_acme.S @@ -11,4 +11,4 @@ !hex 00000000ea60af000008ad1900eaaf000001af000002af000003af0000086b19 !hex 00080056340200800010080054686973206973206120746573742e0068656c6c !hex 6f2c20776f726c6421 - } ;!pseudopc + } diff --git a/SourceGen/SGTestData/Expected/20222-data-bank_64tass.S b/SourceGen/SGTestData/Expected/20222-data-bank_64tass.S index b1522c2..393c385 100644 --- a/SourceGen/SGTestData/Expected/20222-data-bank_64tass.S +++ b/SourceGen/SGTestData/Expected/20222-data-bank_64tass.S @@ -71,8 +71,8 @@ L2202E nop rts bank2addr .word L2202E & $ffff - .here + .logical $033000 bank3 lda bank3 lda bank2 & $ffff @@ -89,8 +89,8 @@ _L33024 lda $2030 rtl L33028 .word L33028 & $ffff - .here + .logical $024000 L24000 lda L24000 phb diff --git a/SourceGen/SGTestData/Expected/20240-large-overlay_acme.S b/SourceGen/SGTestData/Expected/20240-large-overlay_acme.S index 7e57f80..7884903 100644 --- a/SourceGen/SGTestData/Expected/20240-large-overlay_acme.S +++ b/SourceGen/SGTestData/Expected/20240-large-overlay_acme.S @@ -3,25 +3,25 @@ !pseudopc $8000 { !byte $ea !fill 8191,$00 - } ;!pseudopc + } !pseudopc $8000 { !fill 8192,$01 - } ;!pseudopc + } !pseudopc $8000 { !fill 8192,$02 - } ;!pseudopc + } !pseudopc $8000 { !fill 8192,$03 - } ;!pseudopc + } !pseudopc $8000 { !fill 8192,$04 - } ;!pseudopc + } !pseudopc $8000 { !fill 8192,$05 - } ;!pseudopc + } !pseudopc $8000 { !fill 8192,$06 - } ;!pseudopc + } !pseudopc $8000 { !fill 8192,$07 - } ;!pseudopc + } diff --git a/SourceGen/WpfGui/EditAddress.xaml b/SourceGen/WpfGui/EditAddress.xaml index 978981d..ed2ab69 100644 --- a/SourceGen/WpfGui/EditAddress.xaml +++ b/SourceGen/WpfGui/EditAddress.xaml @@ -19,60 +19,99 @@ limitations under the License. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:SourceGen.WpfGui" mc:Ignorable="d" - Title="Set Address" + Title="Edit Address Range" SizeToContent="WidthAndHeight" ResizeMode="NoResize" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" ContentRendered="Window_ContentRendered"> + + Creating new address range, with properties: + Editing existing address range, with properties: + Resizing existing address range, with properties: + + - - - + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +