From d4c481839e174a273129a88ef183934ee13b4218 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Fri, 17 Sep 2021 15:29:35 -0700 Subject: [PATCH] ORG rework, part 2 AddressMap API reshuffle. Added "pre-label" to class and API. Split AddressMapEntry into two parts to make it clear when FLOATING_LEN has been resolved. Updated display line list generator to use in-line linear map traversal. Previous approach was to walk through the list of regions in a second pass, inserting .ORG directives, but that was awkward and is no longer needed. --- CommonUtil/AddressMap.cs | 519 ++++++++++++++++++++------------ SourceGen/AsmGen/AsmAcme.cs | 6 +- SourceGen/AsmGen/AsmCc65.cs | 2 +- SourceGen/AsmGen/AsmMerlin32.cs | 2 +- SourceGen/AsmGen/AsmTass64.cs | 2 +- SourceGen/AsmGen/GenCommon.cs | 6 +- SourceGen/AsmGen/IGenerator.cs | 2 +- SourceGen/DisasmProject.cs | 24 +- SourceGen/LineListGen.cs | 112 ++++--- SourceGen/ProjectFile.cs | 4 +- 10 files changed, 397 insertions(+), 282 deletions(-) diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs index 245b675..b661401 100644 --- a/CommonUtil/AddressMap.cs +++ b/CommonUtil/AddressMap.cs @@ -63,6 +63,9 @@ namespace CommonUtil { /// the CHR graphics block in NES programs). The most significant impact this has on /// SourceGen is that we never resolve address-to-offset lookups in such a region. /// + /// It is valid for the map to be completely empty, or for there to be ranges of offsets + /// for which there is no entry. We can use a catch-all non-addressable region for this. + /// /// For design notes, see https://github.com/fadden/6502bench/issues/107 /// public class AddressMap : IEnumerable { @@ -83,48 +86,118 @@ namespace CommonUtil { #region Structural /// - /// Code starting at the specified offset will have the specified address. + /// Address map entry definition. /// /// The entries are held in the list in order, sorted primarily by increasing start offset, /// secondarily by decreasing end offset. If there are multiple regions at the same - /// offset, the larger (parent) region will appear first (convenient if you're pushing - /// things onto a stack as you traverse the list). - /// - /// It is valid for the map to be completely empty, or for there to be ranges of offsets - /// for which there is no entry. + /// offset, the larger (parent) region will appear first. /// /// Instances are immutable. /// [Serializable] public class AddressMapEntry { - // Offset at which region starts. + /// + /// Offset at which region starts. + /// public int Offset { get; private set; } - // Length of region; invalid for a "floating" end point. + + /// + /// Length of region, or FLOATING_LEN if the end point is floating. + /// public int Length { get; private set; } - // Address to map start of region to. + + /// + /// Address to map start of region to. + /// public int Address { get; private set; } - // Is the end point floating? - public bool IsFloating { get; private set; } - // Should we try to generate this with a relative .ORG statement? (This is strictly - // for code generation, and has no effect on anything here.) + + /// + /// If non-empty, label to add right before the start block. + /// + public string PreLabel { get; private set; } + + /// + /// Should we try to generate the directive with an operand that is relative to + /// the current PC? + /// + /// + /// (This is strictly for code generation, and has no effect on anything here.) + /// public bool IsRelative { get; private set; } - // THOUGHT: we need to be able to do certain info queries from edit dialog. We - // can uniquely identify a node in the region list by offset/len, but that gets - // funny with floating lengths. We can use a "was floating" flag here to make - // it possible to match the offset/float_len to a node in the hierarchical tree. - // (add another constructor) - - public AddressMapEntry(int offset, int len, int addr, bool isFloating, - bool isRelative) { + /// + /// Constructor. + /// + public AddressMapEntry(int offset, int len, int addr, string preLabel, bool isRelative) { Offset = offset; Length = len; Address = addr; - IsFloating = isFloating; + PreLabel = preLabel; IsRelative = isRelative; } + public override string ToString() { return "[AddrMapEnt: +" + Offset.ToString("x6") + " len=$" + Length.ToString("x4") + + " addr=$" + Address.ToString("x4") + " preLab='" + PreLabel + + "' isRel=" + IsRelative + "]"; + } + } + + /// + /// Address map entry augmented with computed values. Instances of these are used for + /// the hierarchical and linear views. + /// + /// The Length property always holds the actual length (never FLOATING_LEN). The + /// IsFloating property indicates whether it was initially floating. + /// + /// Instances are immutable. + /// + public class AddressRegion : AddressMapEntry { + /// + /// Is the end point floating? + /// + /// + /// (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; } + + /// + /// Address associated with pre-label. + /// + public int PreLabelAddress { get; private set; } + + /// + /// Full constructor. + /// + public AddressRegion(int offset, int len, int addr, bool isFloating, + string preLabel, int prevAddr, bool isRelative) + : base(offset, len, addr, preLabel, isRelative) { + IsFloating = isFloating; + PreLabelAddress = prevAddr; + + Debug.Assert(Length != FLOATING_LEN); + } + + /// + /// Basic constructor. + /// + 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) { + } + + public override string ToString() { + return "[AddrRegion: +" + Offset.ToString("x6") + " len=$" + Length.ToString("x4") + " addr=$" + Address.ToString("x4") + " isFloat=" + IsFloating + " isRel=" + IsRelative + "]"; } @@ -138,7 +211,7 @@ namespace CommonUtil { /// /// List of definitions, in sorted order. /// - private List mRegionList = new List(); + private List mMapEntries = new List(); /// @@ -162,17 +235,18 @@ 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.IsRelative); + AddResult result = AddRegion(ent.Offset, ent.Length, ent.Address, ent.PreLabel, + ent.IsRelative); if (result != AddResult.Okay) { throw new Exception("Unable to add entry (" + result + "): " + ent); } } - Debug.Assert(entries.Count == mRegionList.Count); + Debug.Assert(entries.Count == mMapEntries.Count); Regenerate(); } public void Clear() { - mRegionList.Clear(); + mMapEntries.Clear(); Regenerate(); } @@ -182,8 +256,8 @@ namespace CommonUtil { /// Receives the map's span length. /// Copy of list. public List GetEntryList(out int spanLength) { - List newList = new List(mRegionList.Count); - foreach (AddressMapEntry ent in mRegionList) { + List newList = new List(mMapEntries.Count); + foreach (AddressMapEntry ent in mMapEntries) { newList.Add(ent); } spanLength = mSpanLength; @@ -192,18 +266,18 @@ namespace CommonUtil { // IEnumerable public IEnumerator GetEnumerator() { - return ((IEnumerable)mRegionList).GetEnumerator(); + return ((IEnumerable)mMapEntries).GetEnumerator(); } // IEnumerable IEnumerator IEnumerable.GetEnumerator() { - return ((IEnumerable)mRegionList).GetEnumerator(); + return ((IEnumerable)mMapEntries).GetEnumerator(); } /// /// Number of entries in the address map. /// - public int RegionCount { get { return mRegionList.Count; } } + public int RegionCount { get { return mMapEntries.Count; } } /// /// Error codes for AddRegion(). @@ -231,10 +305,14 @@ namespace CommonUtil { /// - either address is NON_ADDR, or: /// - addr > 0 /// - addr <= ADDR_MAX + /// - preLabel is not null + /// + /// We might want to limit the length to fit within a single 64K bank, unless it's + /// a non-addressable region. That would probably be better as a warning than an error. /// /// True if everything looks good. - private bool ValidateArgs(int offset, int length, int addr) { - return offset >= 0 && offset < mSpanLength && + private bool ValidateArgs(int offset, int length, int addr, string preLabel) { + return preLabel != null && offset >= 0 && offset < mSpanLength && (length != FLOATING_LEN ? offset + length <= mSpanLength : true) && ((length > 0 && length <= mSpanLength) || length == FLOATING_LEN) && ((addr >= 0 && addr <= ADDR_MAX) || addr == NON_ADDR); @@ -244,31 +322,61 @@ namespace CommonUtil { /// Adds a new region. /// /// File offset of region start. - /// Length of region, or -1 for a floating end point. + /// 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); + } + + /// + /// Adds a new region. + /// + /// File offset of region start. + /// Length of region, or FLOATING_LEN for a floating end point. + /// Address of region start. + /// Pre-region label. /// True if code generator should output relative /// assembler directive operand. /// Failure code. - public AddResult AddRegion(int offset, int length, int addr, bool isRelative) { - if (!ValidateArgs(offset, length, addr)) { + public AddResult AddRegion(int offset, int length, int addr, string preLabel, + bool isRelative) { + if (!ValidateArgs(offset, length, addr, preLabel)) { Debug.WriteLine("AddRegion: invalid arg"); return AddResult.InvalidValue; } + int insIdx; + AddResult result = FindAddIndex(offset, length, out insIdx); + if (result == AddResult.Okay) { + AddressMapEntry newEntry = new AddressMapEntry(offset, length, addr, + preLabel, isRelative); + mMapEntries.Insert(insIdx, newEntry); + Regenerate(); + } + return result; + } - AddressMapEntry newEntry = new AddressMapEntry(offset, length, addr, - length == FLOATING_LEN, isRelative); + /// + /// Determines whether a new region with the specified offset and length can be added. + /// If it can, returns the list index at which it should be placed. + /// + /// File offset of region start. + /// Length of region, or -1 for a floating end point. + /// Index at which new region should be added. + /// Failure code. + private AddResult FindAddIndex(int offset, int length, out int outInsIdx) { + outInsIdx = -1; // Empty list? - if (mRegionList.Count == 0) { - mRegionList.Add(newEntry); - Regenerate(); + if (mMapEntries.Count == 0) { + outInsIdx = 0; return AddResult.Okay; } // Find insertion point. int insIdx; - for (insIdx = 0; insIdx < mRegionList.Count; insIdx++) { - AddressMapEntry ent = mRegionList[insIdx]; + for (insIdx = 0; insIdx < mMapEntries.Count; insIdx++) { + AddressMapEntry ent = mMapEntries[insIdx]; if (ent.Offset > offset) { // Insert before this one. break; @@ -301,7 +409,7 @@ namespace CommonUtil { if (insIdx > 0) { // Check previous block. We know that its offset is <= the new offset, so // either its a parent or a sibling. - AddressMapEntry ent = mRegionList[insIdx - 1]; + AddressMapEntry ent = mMapEntries[insIdx - 1]; if (ent.Offset == offset) { // Previous is our parent. These things were checked earlier. Debug.Assert(length != FLOATING_LEN && ent.Length != FLOATING_LEN); @@ -324,10 +432,10 @@ namespace CommonUtil { } } } - if (insIdx < mRegionList.Count) { + if (insIdx < mMapEntries.Count) { // Check following block. We know that its offset is >= the new offset, so it's // either a child or a sibling. - AddressMapEntry ent = mRegionList[insIdx]; + AddressMapEntry ent = mMapEntries[insIdx]; if (ent.Offset == offset) { // Following block is our child. These things were checked earlier. Debug.Assert(length != FLOATING_LEN && ent.Length != FLOATING_LEN); @@ -351,8 +459,7 @@ namespace CommonUtil { } } - mRegionList.Insert(insIdx, newEntry); - Regenerate(); + outInsIdx = insIdx; return AddResult.Okay; } @@ -362,10 +469,11 @@ namespace CommonUtil { /// Offset of region to edit. /// Length of region to edit. /// New value for address. + /// Pre-block label. /// New value for IsRelative. /// True if a region was edited, false otherwise. - public bool EditRegion(int offset, int length, int addr, bool isRelative) { - if (!ValidateArgs(offset, length, addr)) { + public bool EditRegion(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); } @@ -374,8 +482,7 @@ namespace CommonUtil { if (idx < 0) { return false; } - mRegionList[idx] = new AddressMapEntry(offset, length, addr, length == FLOATING_LEN, - isRelative); + mMapEntries[idx] = new AddressMapEntry(offset, length, addr, preLabel, isRelative); Regenerate(); return true; } @@ -387,7 +494,7 @@ namespace CommonUtil { /// Length of region to remove. /// True if a region was removed, false otherwise. public bool RemoveRegion(int offset, int length) { - if (!ValidateArgs(offset, length, 0)) { + if (!ValidateArgs(offset, length, 0, string.Empty)) { throw new Exception("Bad RemoveRegion args +" + offset.ToString("x6") + " " + length); } @@ -396,7 +503,7 @@ namespace CommonUtil { if (idx < 0) { return false; } - mRegionList.RemoveAt(idx); + mMapEntries.RemoveAt(idx); Regenerate(); return true; } @@ -408,8 +515,8 @@ namespace CommonUtil { /// Length to match (may be FLOATING_LEN). /// Index of matching region, or -1 if not found. private int FindRegion(int offset, int length) { - for (int i = 0; i < mRegionList.Count; i++) { - if (mRegionList[i].Offset == offset && mRegionList[i].Length == length) { + for (int i = 0; i < mMapEntries.Count; i++) { + if (mMapEntries[i].Offset == offset && mMapEntries[i].Length == length) { return i; } } @@ -445,11 +552,11 @@ namespace CommonUtil { /// List of entries; may be empty. public List GetRegions(int offset) { List regions = new List(); - for (int i = 0; i < mRegionList.Count; i++) { - if (mRegionList[i].Offset == offset) { - regions.Add(mRegionList[i]); + for (int i = 0; i < mMapEntries.Count; i++) { + if (mMapEntries[i].Offset == offset) { + regions.Add(mMapEntries[i]); } - if (mRegionList[i].Offset > offset) { + if (mMapEntries[i].Offset > offset) { // Regions are in sorted order, we're done. break; } @@ -479,8 +586,8 @@ namespace CommonUtil { private bool DebugValidateStructural() { int lastStart = -1; int lastLength = OFFSET_MAX + 1; - for (int i = 0; i < mRegionList.Count; i++) { - AddressMapEntry ent = mRegionList[i]; + for (int i = 0; i < mMapEntries.Count; i++) { + AddressMapEntry ent = mMapEntries[i]; // Do basic range checks on arguments. if (ent.Offset < 0 || ent.Offset > OFFSET_MAX) { @@ -531,7 +638,7 @@ namespace CommonUtil { } public override string ToString() { - return "[AddressMap: " + mRegionList.Count + " entries]"; + return "[AddressMap: " + mMapEntries.Count + " entries]"; } #endregion Structural @@ -545,16 +652,16 @@ namespace CommonUtil { /// Modifications are rare and trees are expected to be small, so the entire tree is /// reconstructed whenever a change is made. /// - /// We can reference the AddressMapEntry objects from the structural list unless it - /// has a floating length. If it does, we create a new one with the actual length. + /// We need to resolve floating lengths and pre-label addresses, so the tree holds + /// AddressMapEntry rather than AddressRegion. /// private class TreeNode { - public AddressMapEntry Entry { get; set; } + public AddressRegion Region { get; set; } public TreeNode Parent { get; set; } public List Children { get; set; } - public TreeNode(AddressMapEntry entry, TreeNode parent) { - Entry = entry; + public TreeNode(AddressRegion region, TreeNode parent) { + Region = region; Parent = parent; // all other fields null/false } @@ -562,7 +669,8 @@ namespace CommonUtil { /// /// Top of the hierarchy. The topmost node is a no-address node that spans the entire - /// file. + /// file. If the region list is empty or has holes, this catches everything that falls + /// through. /// private TreeNode mTopNode; @@ -574,14 +682,15 @@ 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. - AddressMapEntry globalEnt = new AddressMapEntry(0, mSpanLength, NON_ADDR, false, false); - TreeNode topNode = new TreeNode(globalEnt, null); + AddressRegion globalReg = new AddressRegion(0, mSpanLength, NON_ADDR, false, + string.Empty, NON_ADDR, false); + TreeNode topNode = new TreeNode(globalReg, null); // Generate the children of this node. int index = -1; GenerateChildren(topNode, ref index); - if (index != mRegionList.Count) { + if (index != mMapEntries.Count) { Debug.Assert(false, "Not all regions traversed"); } @@ -601,37 +710,47 @@ namespace CommonUtil { List children = new List(); index++; - while (index < mRegionList.Count) { - AddressMapEntry childEnt = mRegionList[index]; + while (index < mMapEntries.Count) { + AddressMapEntry childEnt = mMapEntries[index]; - if (childEnt.Offset >= parent.Entry.Offset + parent.Entry.Length) { - // Starts after end of parent, not a child. + if (childEnt.Offset >= parent.Region.Offset + parent.Region.Length) { + // Starts after end of parent, is not a child. Done with children. break; } + // Compute the address for the pre-label. + int preLabelAddr = NON_ADDR; + if (parent != null && parent.Region.Address != NON_ADDR) { + preLabelAddr = parent.Region.Address + childEnt.Offset - parent.Region.Offset; + } + if (childEnt.Length == FLOATING_LEN) { // Compute actual length. We stop at the end of the parent, or at the start // of the following region, whichever comes first. // // Regions with floating ends can't have children, so we don't need to // check for sub-regions. - int nextStart = parent.Entry.Offset + parent.Entry.Length; + int nextStart = parent.Region.Offset + parent.Region.Length; index++; - if (index < mRegionList.Count) { + if (index < mMapEntries.Count) { // Check next sibling. - int sibStart = mRegionList[index].Offset; + int sibStart = mMapEntries[index].Offset; if (sibStart < nextStart) { nextStart = sibStart; } } - AddressMapEntry fixedEnt = new AddressMapEntry(childEnt.Offset, - nextStart - childEnt.Offset, childEnt.Address, true, childEnt.IsRelative); - children.Add(new TreeNode(fixedEnt, parent)); + AddressRegion fixedReg = new AddressRegion(childEnt.Offset, + nextStart - childEnt.Offset, childEnt.Address, true, + childEnt.PreLabel, preLabelAddr, childEnt.IsRelative); + 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. - TreeNode thisNode = new TreeNode(childEnt, parent); + AddressRegion newReg = new AddressRegion(childEnt.Offset, + childEnt.Length, childEnt.Address, false, + childEnt.PreLabel, preLabelAddr, childEnt.IsRelative); + TreeNode thisNode = new TreeNode(newReg, parent); children.Add(thisNode); // Check for grandchildren. "index" will point to first entry beyond this @@ -733,12 +852,12 @@ namespace CommonUtil { } // Wasn't in any of the children, see if it's in this node. - AddressMapEntry ent = node.Entry; - if (ent.Address == NON_ADDR) { + AddressRegion region = node.Region; + if (region.Address == NON_ADDR) { // Non-addressable space. return -1; } - if (targetAddr < ent.Address || targetAddr >= ent.Address + ent.Length) { + if (targetAddr < region.Address || targetAddr >= region.Address + region.Length) { // Outside our range of addresses, return failure. return -1; } @@ -746,11 +865,11 @@ namespace CommonUtil { // We span the correct range of addresses. See if the requested address // falls into a hole spanned by a child. if (node.Children != null) { - int subPosn = targetAddr - ent.Address; // position of target inside node + int subPosn = targetAddr - region.Address; // position of target inside node foreach (TreeNode childNode in node.Children) { - AddressMapEntry childEnt = childNode.Entry; - int childStartPosn = childEnt.Offset - ent.Offset; - int childEndPosn = childStartPosn + childEnt.Length; + AddressRegion childReg = childNode.Region; + int childStartPosn = childReg.Offset - region.Offset; + int childEndPosn = childStartPosn + childReg.Length; if (childStartPosn > subPosn) { // Child is past the target, it's not in a hole; no need to check @@ -762,7 +881,7 @@ namespace CommonUtil { } } } - return ent.Offset + (targetAddr - ent.Address); + return region.Offset + (targetAddr - region.Address); } /// @@ -783,9 +902,9 @@ namespace CommonUtil { // Calculate address in this node. int ourAddr = NON_ADDR; - if (node.Entry.Address != NON_ADDR) { - ourAddr = node.Entry.Address + (offset - node.Entry.Offset); - Debug.Assert(ourAddr < node.Entry.Address + node.Entry.Length); + if (node.Region.Address != NON_ADDR) { + ourAddr = node.Region.Address + (offset - node.Region.Offset); + Debug.Assert(ourAddr < node.Region.Address + node.Region.Length); } return ourAddr; } @@ -799,8 +918,8 @@ namespace CommonUtil { private TreeNode OffsetToNode(int offset, TreeNode node) { if (node.Children != null) { foreach (TreeNode child in node.Children) { - AddressMapEntry childEnt = child.Entry; - if (offset >= childEnt.Offset && offset < childEnt.Offset + childEnt.Length) { + AddressRegion childReg = child.Region; + if (offset >= childReg.Offset && offset < childReg.Offset + childReg.Length) { // It's in or below this child. Check it with tail recursion. return OffsetToNode(offset, child); } @@ -829,16 +948,16 @@ namespace CommonUtil { /// Length of region. /// True if the range of offsets is unbroken. public bool IsRangeUnbroken(int offset, int length) { - if (!ValidateArgs(offset, length, 0)) { + if (!ValidateArgs(offset, length, 0, string.Empty)) { Debug.Assert(false, "Invalid args to IsUnbrokenRange"); return true; // most ranges are unbroken, so just go with that } TreeNode node = OffsetToNode(offset, mTopNode); - AddressMapEntry ent = node.Entry; - Debug.Assert(offset >= ent.Offset && offset < ent.Offset + ent.Length); + AddressRegion region = node.Region; + Debug.Assert(offset >= region.Offset && offset < region.Offset + region.Length); int lastOffset = offset + length - 1; // offset of last byte in range - if (lastOffset >= ent.Offset + ent.Length) { + if (lastOffset >= region.Offset + region.Length) { // end of region is not in this node return false; } @@ -846,14 +965,14 @@ namespace CommonUtil { // The specified range fits inside this node. See if it's interrupted by a child. if (node.Children != null) { foreach (TreeNode childNode in node.Children) { - AddressMapEntry childEnt = childNode.Entry; + AddressRegion childReg = childNode.Region; - if (childEnt.Offset > lastOffset) { + if (childReg.Offset > lastOffset) { // 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 <= childEnt.Offset + childEnt.Length - 1 && - lastOffset >= childEnt.Offset) { + } else if (offset <= childReg.Offset + childReg.Length - 1 && + lastOffset >= childReg.Offset) { // Target is in a hole occupied by the child. No good. return false; } @@ -864,7 +983,7 @@ namespace CommonUtil { } private bool DebugValidateHierarchical() { - if (mTopNode.Entry.Offset != 0 || mTopNode.Entry.Length != mSpanLength) { + if (mTopNode.Region.Offset != 0 || mTopNode.Region.Length != mSpanLength) { Debug.WriteLine("Malformed top node"); return false; } @@ -876,9 +995,9 @@ namespace CommonUtil { // Check node count. It should have one entry for every entry in the region list // (we don't count mTopNode). - if (nodeCount != mRegionList.Count) { + if (nodeCount != mMapEntries.Count) { Debug.WriteLine("Hierarchical is missing entries: nodeCount=" + nodeCount + - " regionCount=" + mRegionList.Count); + " regionCount=" + mMapEntries.Count); return false; } return true; @@ -887,19 +1006,19 @@ namespace CommonUtil { private bool DebugValidateHierarchy(List nodeList, int startOffset, int nextOffset, ref int nodeCount) { foreach (TreeNode node in nodeList) { - Debug.Assert(node.Entry.Length >= 0); // no floaters + Debug.Assert(node.Region.Length >= 0); // no floaters nodeCount++; - if (node.Entry.Offset < startOffset || - node.Entry.Offset + node.Entry.Length > nextOffset) { + if (node.Region.Offset < startOffset || + node.Region.Offset + node.Region.Length > nextOffset) { Debug.WriteLine("Child node did not fit in parent bounds"); return false; } if (node.Children != null) { // Descend recursively. - if (!DebugValidateHierarchy(node.Children, node.Entry.Offset, - node.Entry.Offset + node.Entry.Length, ref nodeCount)) { + if (!DebugValidateHierarchy(node.Children, node.Region.Offset, + node.Region.Offset + node.Region.Length, ref nodeCount)) { return false; } } @@ -933,15 +1052,15 @@ namespace CommonUtil { // in the parent's range. public int Address { get; private set; } - // Reference to the AddressMapEntry that generated this entry. The reference + // Reference to the AddressRegion that generated this entry. The reference // will be the same for the "start" and "end" entries. - public AddressMapEntry Entry { get; private set; } + public AddressRegion Region { get; private set; } - public AddressChange(bool isStart, int offset, int addr, AddressMapEntry ent) { + public AddressChange(bool isStart, int offset, int addr, AddressRegion region) { IsStart = isStart; Offset = offset; Address = addr; - Entry = ent; + Region = region; } } @@ -957,36 +1076,36 @@ namespace CommonUtil { if (mTopNode.Children != null) { foreach (TreeNode node in mTopNode.Children) { - Debug.Assert(node.Entry.Length > 0); // all floaters should be resolved + Debug.Assert(node.Region.Length > 0); // all floaters should be resolved - if (node.Entry.Offset != startOffset) { + if (node.Region.Offset != startOffset) { // Insert a no-address zone here. - Debug.Assert(node.Entry.Offset > startOffset); - AddressMapEntry tmpEnt = new AddressMapEntry(startOffset, - node.Entry.Offset - startOffset, NON_ADDR, false, false); - changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpEnt)); - changeList.Add(new AddressChange(false, node.Entry.Offset, NON_ADDR, tmpEnt)); + Debug.Assert(node.Region.Offset > startOffset); + AddressRegion tmpReg = new AddressRegion(startOffset, + node.Region.Offset - startOffset, NON_ADDR); + changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpReg)); + changeList.Add(new AddressChange(false, node.Region.Offset, NON_ADDR, tmpReg)); extraNodes++; } AddChangeEntry(changeList, node, NON_ADDR); - startOffset = node.Entry.Offset + node.Entry.Length; + startOffset = node.Region.Offset + node.Region.Length; } } // Finish with a no-address zone if there's a gap. if (startOffset != mSpanLength) { Debug.Assert(startOffset < mSpanLength); - AddressMapEntry tmpEnt = new AddressMapEntry(startOffset, - mSpanLength - startOffset, NON_ADDR, false, false); - changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpEnt)); - changeList.Add(new AddressChange(false, mSpanLength, NON_ADDR, tmpEnt)); + AddressRegion tmpReg = new AddressRegion(startOffset, + mSpanLength - startOffset, NON_ADDR); + changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpReg)); + changeList.Add(new AddressChange(false, mSpanLength, NON_ADDR, tmpReg)); extraNodes++; } - if (changeList.Count != (mRegionList.Count + extraNodes) * 2) { - Debug.Assert(false, "Incorrect linear count: regions*2=" + (mRegionList.Count * 2) + + if (changeList.Count != (mMapEntries.Count + extraNodes) * 2) { + Debug.Assert(false, "Incorrect linear count: regions*2=" + (mMapEntries.Count * 2) + " extraNodes=" + extraNodes + " changeList=" + changeList.Count); } @@ -1006,25 +1125,25 @@ namespace CommonUtil { /// parent's region. private void AddChangeEntry(List changeList, TreeNode node, int parentStartAddr) { - Debug.Assert(node.Entry.Length != FLOATING_LEN); + Debug.Assert(node.Region.Length != FLOATING_LEN); int nextAddr = NON_ADDR; if (parentStartAddr != NON_ADDR) { - nextAddr = parentStartAddr + node.Entry.Length; + nextAddr = parentStartAddr + node.Region.Length; } AddressChange startChange = new AddressChange(true, - node.Entry.Offset, node.Entry.Address, node.Entry); + node.Region.Offset, node.Region.Address, node.Region); AddressChange endChange = new AddressChange(false, - node.Entry.Offset + node.Entry.Length, nextAddr, node.Entry); + node.Region.Offset + node.Region.Length, nextAddr, node.Region); changeList.Add(startChange); - int curAddr = node.Entry.Address; + int curAddr = node.Region.Address; if (node.Children != null) { foreach (TreeNode childNode in node.Children) { int mySpaceAddr = NON_ADDR; if (curAddr != NON_ADDR) { // Adjust address in parent space by difference between start of // parent and start of this node. - mySpaceAddr = curAddr + childNode.Entry.Offset - node.Entry.Offset; + mySpaceAddr = curAddr + childNode.Region.Offset - node.Region.Offset; } AddChangeEntry(changeList, childNode, mySpaceAddr); } @@ -1051,13 +1170,26 @@ namespace CommonUtil { if (prevChange != null && change.Offset != prevChange.Offset) { // Start of region at new offset. Output address info for space // between previous start or end. + sb.Append(" "); PrintAddressInfo(sb, depth, prevChange.Address, change.Offset - prevChange.Offset); } // Start following end, or start following start after a gap. + if (!string.IsNullOrEmpty(change.Region.PreLabel)) { + sb.Append(" "); + PrintDepthLines(sb, depth); + sb.Append("| pre='" + change.Region.PreLabel + "' "); + if (change.Region.PreLabelAddress != NON_ADDR) { + sb.Append("$" + change.Region.PreLabelAddress.ToString("x4")); + } else { + sb.Append("(non-addr)"); + } + sb.Append(CRLF); + } + sb.Append("+" + change.Offset.ToString("x6")); PrintDepthLines(sb, depth); - sb.Append("+- +" + change.Offset.ToString("x6") + " START ("); + sb.Append("+- " + "START ("); PrintAddress(sb, change.Address); sb.Append(")"); sb.Append(CRLF); @@ -1070,12 +1202,14 @@ namespace CommonUtil { if (change.Offset != prevChange.Offset) { // End of region at new offset. Output address info for space // between previous start or end. + sb.Append(" "); PrintAddressInfo(sb, depth + 1, prevChange.Address, change.Offset - prevChange.Offset); } + sb.Append("+" + change.Offset.ToString("x6")); PrintDepthLines(sb, depth); - sb.Append("+- +" + change.Offset.ToString("x6") + " END (now "); + sb.Append("+- " + "END (now "); PrintAddress(sb, change.Address); sb.Append(")"); sb.Append(CRLF); @@ -1089,6 +1223,7 @@ namespace CommonUtil { } private static void PrintDepthLines(StringBuilder sb, int depth) { + sb.Append(" "); while (depth-- > 0) { sb.Append("| "); } @@ -1159,25 +1294,25 @@ namespace CommonUtil { const int adr2 = 0x1700; Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off0, len0, adr0, false)); + map.AddRegion(off0, len0, adr0)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off1, len1, adr1, false)); + map.AddRegion(off1, len1, adr1)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off2, len2, adr2, false)); + map.AddRegion(off2, len2, adr2)); result &= map.DebugValidate(); Test_Expect(AddResult.OverlapExisting, ref result, - map.AddRegion(off0, len0, 0x1000, false)); + map.AddRegion(off0, len0, 0x1000)); Test_Expect(AddResult.OverlapFloating, ref result, - map.AddRegion(off0, FLOATING_LEN, 0x1000, false)); + map.AddRegion(off0, FLOATING_LEN, 0x1000)); Test_Expect(AddResult.StraddleExisting, ref result, - map.AddRegion(off0 + 1, len0, 0x1000, false)); + map.AddRegion(off0 + 1, len0, 0x1000)); Test_Expect(AddResult.InvalidValue, ref result, - map.AddRegion(off0, mapLen + 1, 0x1000, false)); + map.AddRegion(off0, mapLen + 1, 0x1000)); // One region to wrap them all. Add then remove. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off0, mapLen, 0x0000, false)); + map.AddRegion(off0, mapLen, 0x0000)); Test_Expect(true, ref result, map.RemoveRegion(off0, mapLen)); Test_Expect(false, ref result, map.RemoveRegion(off0, mapLen)); @@ -1219,16 +1354,16 @@ namespace CommonUtil { const int adr2 = NON_ADDR; Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off0, len0, adr0, false)); + map.AddRegion(off0, len0, adr0)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off1, len1, adr1, false)); + map.AddRegion(off1, len1, adr1)); // Try to remove the implicit no-address zone. Test_Expect(false, ref result, map.RemoveRegion(0, off0)); // Add non-addressable area into the middle of the second region. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(off2, len2, adr2, false)); + map.AddRegion(off2, len2, adr2)); Test_Expect(adr0, ref result, map.OffsetToAddress(off0)); Test_Expect(adr1, ref result, map.OffsetToAddress(off1)); @@ -1255,63 +1390,55 @@ namespace CommonUtil { bool result = true; // Nested with shared start offset. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000100, 0x0400, 0x4000, false)); + map.AddRegion(0x000100, 0x0400, 0x4000, "preA0", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000100, 0x0100, 0x7000, false)); + map.AddRegion(0x000100, 0x0100, 0x7000, "preA1", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000100, 0x0300, 0x5000, false)); + map.AddRegion(0x000100, 0x0300, 0x5000, "preA2", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000100, 0x0200, 0x6000, false)); + map.AddRegion(0x000100, 0x0200, 0x6000, "preA3", false)); // Add a couple of floaters. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x0000ff, FLOATING_LEN, 0x30ff, false)); + map.AddRegion(0x0000ff, FLOATING_LEN, 0x30ff, "preA4", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000101, FLOATING_LEN, 0x3101, false)); + map.AddRegion(0x000101, FLOATING_LEN, 0x3101, "preA5", false)); Test_Expect(AddResult.OverlapFloating, ref result, - map.AddRegion(0x000100, FLOATING_LEN, 0x3100, false)); + map.AddRegion(0x000100, FLOATING_LEN, 0x3100, "preA6", false)); // Nested with shared end offset. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000fff, FLOATING_LEN, 0x3fff, false)); + map.AddRegion(0x000fff, FLOATING_LEN, 0x3fff, "preB0", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001200, 0x0200, 0x6000, false)); + map.AddRegion(0x001200, 0x0200, 0x6000, "preB1", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001000, 0x0400, 0x4000, false)); + map.AddRegion(0x001000, 0x0400, 0x4000, "preB2", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001100, 0x0300, 0x5000, false)); + map.AddRegion(0x001100, 0x0300, 0x5000, "preB3", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001300, 0x0100, 0x7000, false)); + map.AddRegion(0x001300, 0x0100, 0x7000, "preB4", false)); // Single-byte region at start and end. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001200, 1, 0x8200, false)); + map.AddRegion(0x001200, 1, 0x8200, "preB5", false)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x0013ff, 1, 0x83ff, false)); + map.AddRegion(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, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x002100, 0x0600, 0x5000, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x002200, 0x0400, 0x6000, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x002300, 0x0200, 0x7000, false)); + 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)); // Nested with no common edge, building from inside-out. - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x003300, 0x0200, 0x7000, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x003200, 0x0400, 0x6000, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x003100, 0x0600, 0x5000, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x003000, 0x0800, 0x4000, false)); + 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)); // Try floater then overlap. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x004000, FLOATING_LEN, 0x8000, false)); + map.AddRegion(0x004000, FLOATING_LEN, 0x8000)); Test_Expect(AddResult.OverlapFloating, ref result, - map.AddRegion(0x004000, 0x100, 0x8000, false)); + map.AddRegion(0x004000, 0x100, 0x8000)); Test_Expect(true, ref result, map.RemoveRegion(0x004000, FLOATING_LEN)); Test_Expect(0x30ff, ref result, map.OffsetToAddress(0x0000ff)); @@ -1327,6 +1454,9 @@ namespace CommonUtil { Test_Expect(0x002100, ref result, map.AddressToOffset(0x002300, 0x5000)); Test_Expect(0x003100, ref result, map.AddressToOffset(0x003000, 0x5000)); + string mapStr = map.FormatAddressMap(); // DEBUG - format the map and + Debug.WriteLine(mapStr); // DEBUG - print it to the console + result &= map.DebugValidate(); return result; } @@ -1336,14 +1466,10 @@ namespace CommonUtil { AddressMap map = new AddressMap(mapLen); bool result = true; - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000000, 0x2000, 0x8000, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x002000, 0x2000, 0x8000, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x002100, 0x0200, 0xe100, false)); - Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x003100, 0x0200, 0xf100, false)); + 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(0x003105, ref result, map.AddressToOffset(0x000000, 0xf105)); Test_Expect(0x003105, ref result, map.AddressToOffset(0x002100, 0xf105)); @@ -1378,22 +1504,19 @@ namespace CommonUtil { // Pyramid shape, all regions start at same address except last. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x000000, 0x6000, 0x8000, false)); + map.AddRegion(0x000000, 0x6000, 0x8000)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x001000, 0x4000, 0x8000, false)); + map.AddRegion(0x001000, 0x4000, 0x8000)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x002000, 0x2000, 0x7fff, false)); + map.AddRegion(0x002000, 0x2000, 0x7fff)); // Second pyramid. Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x006000, 0x6000, 0x8000, false)); + map.AddRegion(0x006000, 0x6000, 0x8000)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x007000, 0x4000, 0x8000, false)); + map.AddRegion(0x007000, 0x4000, 0x8000)); Test_Expect(AddResult.Okay, ref result, - map.AddRegion(0x008000, 0x2000, 0x8000, false)); - - string mapStr = map.FormatAddressMap(); // DEBUG - format the map and - Debug.WriteLine(mapStr); // DEBUG - print it to the console + map.AddRegion(0x008000, 0x2000, 0x8000)); // Children take priority over the start node. Test_Expect(0x002001, ref result, map.AddressToOffset(0x000000, 0x8000)); diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index f39a263..7e31cdf 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -287,8 +287,8 @@ namespace SourceGen.AsmGen { OutputLine(SourceFormatter.FullLineCommentDelimiter + "ACME can't handle 65816 code that lives outside bank zero"); int orgAddr = Project.AddrMap.OffsetToAddress(0); - AddressMap.AddressMapEntry fakeEnt = new AddressMap.AddressMapEntry(0, - Project.FileData.Length, orgAddr, false, false); + AddressMap.AddressRegion fakeEnt = new AddressMap.AddressRegion(0, + Project.FileData.Length, orgAddr); OutputOrgDirective(fakeEnt, true); OutputDenseHex(0, Project.FileData.Length, string.Empty, string.Empty); OutputOrgDirective(fakeEnt, false); @@ -568,7 +568,7 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) { + public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { // This is similar in operation to the AsmTass64 implementation. See comments there. Debug.Assert(mPcDepth >= 0); if (isStart) { diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index 1059828..be5e599 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -561,7 +561,7 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) { + public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { if (!isStart) { return; } diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 4c55bfc..6eb583f 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -484,7 +484,7 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) { + public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { if (isStart) { OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 84c43fb..21d8d64 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -653,7 +653,7 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) { + public void OutputOrgDirective(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 diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs index 839957e..a177f17 100644 --- a/SourceGen/AsmGen/GenCommon.cs +++ b/SourceGen/AsmGen/GenCommon.cs @@ -79,7 +79,7 @@ namespace SourceGen.AsmGen { // Check for address changes. There may be more than one at a given offset. AddressMap.AddressChange change = addrIter.Current; while (change != null && change.Offset == offset) { - gen.OutputOrgDirective(change.Entry, change.IsStart); + gen.OutputOrgDirective(change.Region, change.IsStart); addrIter.MoveNext(); change = addrIter.Current; } @@ -152,7 +152,7 @@ namespace SourceGen.AsmGen { Debug.Assert(false); } Debug.Assert(change.Offset == offset); - gen.OutputOrgDirective(change.Entry, change.IsStart); + gen.OutputOrgDirective(change.Region, change.IsStart); addrIter.MoveNext(); change = addrIter.Current; } @@ -565,7 +565,7 @@ namespace SourceGen.AsmGen { return false; } AddressMap.AddressChange change = iter.Current; - if (change.Entry.Length != 2) { + if (change.Region.Length != 2) { Debug.WriteLine("PRG test: first entry is not a two-byte region"); } // Confirm there's an address map entry at offset 2. diff --git a/SourceGen/AsmGen/IGenerator.cs b/SourceGen/AsmGen/IGenerator.cs index 249498b..361fccd 100644 --- a/SourceGen/AsmGen/IGenerator.cs +++ b/SourceGen/AsmGen/IGenerator.cs @@ -170,7 +170,7 @@ namespace SourceGen.AsmGen { /// /// Address map entry object. /// True if we're outputing a region-start directive. - void OutputOrgDirective(CommonUtil.AddressMap.AddressMapEntry addrEntry, bool isStart); + void OutputOrgDirective(CommonUtil.AddressMap.AddressRegion addrEntry, bool isStart); /// /// Notify the assembler of a change in register width. diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index 250d9e1..4b5c619 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, false); + AddrMap.AddRegion(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, false); + AddrMap.AddRegion(0, 2, loadAddr < 2 ? 0 : loadAddr - 2); Debug.Assert(addRes == AddressMap.AddResult.Okay); - addRes = AddrMap.AddRegion(2, mFileData.Length - 2, loadAddr, false); + addRes = AddrMap.AddRegion(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, false); + AddrMap.AddRegion(0, mFileData.Length, loadAddr); Debug.Assert(addRes == AddressMap.AddResult.Okay); } @@ -759,19 +759,19 @@ namespace SourceGen { /// Checks to see if any part of the address map runs across a bank boundary. /// private void ValidateAddressMap() { - // Use the change list, because the region list can have "floating" length values. + // Use the change list, because the map entry list can have "floating" length values. IEnumerator addrIter = AddrMap.AddressChangeIterator; while (addrIter.MoveNext()) { AddressMap.AddressChange change = addrIter.Current; - AddressMap.AddressMapEntry entry = change.Entry; + AddressMap.AddressRegion region = change.Region; if (change.IsStart && - (entry.Address & 0xff0000) != ((entry.Address + entry.Length - 1) & 0xff0000)) { + (region.Address & 0xff0000) != ((region.Address + region.Length - 1) & 0xff0000)) { string fmt = Res.Strings.MSG_BANK_OVERRUN_DETAIL_FMT; - int firstNext = (entry.Address & 0xff0000) + 0x010000; - int badOffset = entry.Offset + (firstNext - entry.Address); + int firstNext = (region.Address & 0xff0000) + 0x010000; + int badOffset = region.Offset + (firstNext - region.Address); Messages.Add(new MessageList.MessageEntry( MessageList.MessageEntry.SeverityLevel.Error, - entry.Offset, + region.Offset, MessageList.MessageEntry.MessageType.BankOverrun, string.Format(fmt, "+" + badOffset.ToString("x6")), MessageList.MessageEntry.ProblemResolution.None)); @@ -2150,7 +2150,7 @@ namespace SourceGen { if ((int)oldValue == AddressMap.NON_ADDR) { // adding new entry if (addrMap.AddRegion(offset, AddressMap.FLOATING_LEN, - (int)newValue, false) != AddressMap.AddResult.Okay) { + (int)newValue) != AddressMap.AddResult.Okay) { Debug.Assert(false, "failed adding region"); } } else if ((int)newValue == AddressMap.NON_ADDR) { @@ -2161,7 +2161,7 @@ namespace SourceGen { } else { // updating existing entry if (!addrMap.EditRegion(offset, AddressMap.FLOATING_LEN, - (int) newValue, false)) { + (int) newValue, string.Empty, false)) { Debug.Assert(false, "failed editing region"); } } diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 5f32516..6fd3dfe 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -944,7 +944,8 @@ namespace SourceGen { // Assume variables may have changed. mLvLookup.Reset(); - // Find the previous status flags for M/X tracking. + // Find the previous status flags for M/X tracking. This is for synthesizing + // long/short pseudo-op directives. StatusFlags prevFlags = StatusFlags.AllIndeterminate; if (mProject.CpuDef.HasEmuFlag) { for (int scanoff = startOffset - 1; scanoff >= 0; scanoff--) { @@ -966,6 +967,8 @@ namespace SourceGen { // a no-continue instruction (e.g. JMP) followed by an instruction with a label. // When we rename the label, we don't want the blank to disappear during the // partial-list generation. + // + // (Blank lines should always be added ABOVE things rather than AFTER things.) bool addBlank = false; if (startOffset > 0) { int baseOff = DataAnalysis.GetBaseOperandOffset(mProject, startOffset - 1); @@ -976,25 +979,69 @@ namespace SourceGen { } } + // Create an address map iterator and advance it to match gen.StartOffset. + IEnumerator addrIter = mProject.AddrMap.AddressChangeIterator; + while (addrIter.MoveNext()) { + if (addrIter.Current.IsStart && addrIter.Current.Offset >= startOffset) { + break; + } + } + int offset = startOffset; while (offset <= endOffset) { + bool blankAdded = 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)); - } else if (mProject.VisualizationSets.ContainsKey(offset) && !addBlank && - offset != 0) { + blankAdded = true; + } else if (mProject.VisualizationSets.ContainsKey(offset) && !addBlank) { // Blank line before visualization set helps keep image visually grouped - // with its data. (Slightly weird things happen with .ORG at the start of - // the file; don't try to add a blank there.) + // with its data. lines.Add(GenerateBlankLine(offset)); + blankAdded = true; } else if (addBlank) { // Previous instruction wanted to be followed by a blank line. lines.Add(GenerateBlankLine(offset)); + blankAdded = true; } addBlank = false; + // Start with address region changes. + while (addrIter.Current != null && addrIter.Current.Offset <= offset) { + AddressMap.AddressChange change = addrIter.Current; + Debug.Assert(change.Offset == offset); // shouldn't be embedded in something + + AddressMap.AddressRegion region = change.Region; + if (region.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) { + // Suppress the ORG at offset zero. We know there's another one + // at offset +000002, and that it matches the value at +0/1. + addrIter.MoveNext(); + continue; + } + + 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) { + lines.Add(GenerateBlankLine(offset)); + } + blankAdded = 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); + string addrStr = mFormatter.FormatHexValue(region.Address, 4); + newLine.Parts = FormattedParts.CreateDirective( + mFormatter.FormatPseudoOp(mPseudoOpNames.OrgDirective), addrStr); + lines.Add(newLine); + } else { + // TODO(org) + } + + 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 @@ -1164,61 +1211,6 @@ namespace SourceGen { offset += attr.Length; } } - - // See if there were any address shifts in this section. If so, go back and - // insert an ORG statement as the first entry for the offset. We're expecting to - // have very few AddressMap entries (usually just one), so it's more efficient to - // process them here and walk through the sub-list than it is to ping the address map - // at every line. - // - // It should not be possible for an address map change to appear in the middle - // of an instruction or data item. - foreach (AddressMap.AddressMapEntry ent in mProject.AddrMap) { - if (ent.Offset < startOffset || ent.Offset > endOffset) { - continue; - } - if (ent.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) { - // Suppress the ORG at offset zero. We know there's another one - // at offset +000002, and that it matches the value at +0/1. - continue; - } - int index = FindLineByOffset(lines, ent.Offset); - if (index < 0) { - Debug.WriteLine("Couldn't find offset " + ent.Offset + - " in range we just generated"); - Debug.Assert(false); - continue; - } - if (lines[index].LineType == Line.Type.Blank) { - index++; - } - Line topLine = lines[index]; - Line newLine = new Line(topLine.FileOffset, 0, Line.Type.OrgDirective); - string addrStr = mFormatter.FormatHexValue(ent.Address, 4); - newLine.Parts = FormattedParts.CreateDirective( - mFormatter.FormatPseudoOp(mPseudoOpNames.OrgDirective), addrStr); - lines.Insert(index, newLine); - - // Prepend a blank line if the previous line wasn't already blank, and this - // isn't the ORG at the start of the file. (This may temporarily do - // double-spacing if we do a partial update, because we won't be able to - // "see" the previous line. Harmless.) - // TODO(maybe): consider always adding blanks, and doing a fix-up pass afterward. - // (but keep in mind that blank lines should always come above things) - // - // Interesting case: - // .dd2 $1000 - // - // .org $1234 - // .dd2 $aabb ;comment - // We need to include "index == 0" or we'll lose the blank when the comment - // is edited. - if (ent.Offset != 0 && - (index == 0 || (index > 0 && lines[index-1].LineType != Line.Type.Blank))){ - Line blankLine = GenerateBlankLine(topLine.FileOffset); - lines.Insert(index, blankLine); - } - } } /// diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs index 930ceb7..86470df 100644 --- a/SourceGen/ProjectFile.cs +++ b/SourceGen/ProjectFile.cs @@ -670,11 +670,11 @@ namespace SourceGen { // Deserialize address map. proj.AddrMap.Clear(); foreach (SerAddressMap addr in spf.AddressMap) { - // TODO(org): serialize length and isRelative + // TODO(org): serialize length, isRelative, and preLabel int length = CommonUtil.AddressMap.FLOATING_LEN; AddressMap.AddResult addResult = proj.AddrMap.AddRegion(addr.Offset, - length, addr.Addr, false); + length, addr.Addr); if (addResult != CommonUtil.AddressMap.AddResult.Okay) { string msg = "off=+" + addr.Offset.ToString("x6") + " addr=$" + addr.Addr.ToString("x4") + " len=" +