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: + + - - - + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +