diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs index db7d85e..245b675 100644 --- a/CommonUtil/AddressMap.cs +++ b/CommonUtil/AddressMap.cs @@ -1,5 +1,5 @@ /* - * Copyright 2019 faddenSoft + * Copyright 2021 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,323 +17,1435 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Text; + namespace CommonUtil { /// - /// Map file offsets to 65xx addresses and vice-versa. Useful for sources with - /// multiple ORG directives. - /// - /// It's possible to generate code that would overlap once relocated at run time, - /// which means a given address can map to multiple offsets (overlays, bank-switched - /// RAM, etc). For this reason it's useful to know the offset of the referring code - /// when evaluating a reference, so that "local" matches take priority. + /// Map file offsets to 65xx addresses and vice-versa. Supports multiple regions + /// with overlapping address ranges. /// /// - /// This was part of the main SourceGen application, but I want to share it with - /// the extension script mechanism. + /// The basic structure is a list of regions, identified by start offset and length, that + /// specify the memory address. + /// + /// This gets complicated because it's possible to have multiple regions that are assembled + /// to occupy the same address range (because of overlays or bank-switching). Some regions + /// may be nested inside other regions. A reference to a given address could potentially + /// resolve to multiple offsets. Any address-to-offset lookup will need to take into + /// account the location of the reference, so that references can be resolved in the region + /// with the appropriate scope. + /// + /// There are three basic API modes: + /// (1) Structural. Add, modify, and remove regions. Needed by the "edit region" dialog. + /// This matches exactly with the contents of the project file. + /// (2) Hierarchical. Used when converting an offset to an address, which can't be + /// accomplished with a simple map because we need to take into account the offset of + /// the reference. The tree best represents the relationship between regions. + /// (3) Linear. When generating assembly sources or the display list, we need to identify + /// the lines that have an address change event (even if the address doesn't change). + /// This will be done as we walk through the code. For easy interaction with an + /// iterator, we flatten it out. + /// + /// These are different enough that it's best to use three different data structures. The + /// list of regions is the primary structure, and the other two are generated from it. Changes + /// to the map are very infrequent, and analyzing the file contents may hit the map + /// frequently, so we want to optimize for "read" accesses. + /// + /// A region can be uniquely identified by {offset,length}. There can be multiple regions + /// starting at a given offset, or ending at a given offset, but we disallow regions that + /// are 100% overlapping. This assertion is complicated slightly by the existence of + /// regions with "floating" end points. + /// + /// It is valid for parts of the file to have no address mapping. This is useful for things + /// like system file headers that are part of the file but wouldn't be part of the source + /// code (such as the C64 PRG address header), or data not addressable by the 6502 (such as + /// 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. + /// + /// For design notes, see https://github.com/fadden/6502bench/issues/107 /// public class AddressMap : IEnumerable { - public const int NO_ENTRY_ADDR = -1; // address value indicating no entry + private const int OFFSET_MAX = (1 << 24) - 1; // max valid offset (16MB file) + private const int ADDR_MAX = (1 << 24) - 1; // max valid addr (24-bit address space) + + /// + /// Length value to use for regions with a floating end point. + /// + public const int FLOATING_LEN = -1024; + + /// + /// Address value to use for non-addressable regions of the file, such as file headers + /// stripped by the system loader or chunks loaded into non-addressable memory. + /// + public const int NON_ADDR = -1025; + + #region Structural /// /// Code starting at the specified offset will have the specified address. - /// - /// The entries are held in the list in order, sorted by offset, with no gaps. - /// This makes the "length" field redundant, as it can be computed by - /// (entry[N+1].mOffset - entry[N].mOffset), with a special case for the last - /// entry in the list. It's convenient to maintain it explicitly however, as - /// the list is read far more often than it is updated. - /// + /// + /// 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. + /// /// Instances are immutable. /// [Serializable] public class AddressMapEntry { + // Offset at which region starts. public int Offset { get; private set; } - public int Addr { get; private set; } + // Length of region; invalid for a "floating" end point. public int Length { get; private set; } + // 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.) + public bool IsRelative { get; private set; } - public AddressMapEntry(int offset, int addr, int len) { + // 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) { Offset = offset; - Addr = addr; Length = len; + Address = addr; + IsFloating = isFloating; + IsRelative = isRelative; + } + public override string ToString() { + return "[AddrMapEnt: +" + Offset.ToString("x6") + " len=$" + Length.ToString("x4") + + " addr=$" + Address.ToString("x4") + " isFloat=" + IsFloating + + " isRel=" + IsRelative + "]"; } } /// - /// Total length, in bytes, spanned by this map. + /// Total length, in bytes, of the file spanned by this map. /// - private int mTotalLength; + private int mSpanLength; /// /// List of definitions, in sorted order. /// - private List mAddrList = new List(); + private List mRegionList = new List(); + /// - /// Constructor. + /// Constructor. Creates an empty map. /// - /// Total length, in bytes, spanned by this map. + /// Total length, in bytes, of the file spanned by this map. public AddressMap(int length) { - /// There must always be at least one entry, defining the target address - /// for file offset 0. This can be changed, but can't be removed. - mTotalLength = length; - mAddrList.Add(new AddressMapEntry(0, 0, length)); + mSpanLength = length; + Regenerate(); } /// - /// Constructor. + /// Constructor. Creates a map from a list of entries. /// + /// Total length, in bytes, of the file spanned by this map. /// List of AddressMapEntry. - public AddressMap(List entries) { - mTotalLength = entries[entries.Count - 1].Offset + entries[entries.Count - 1].Length; + public AddressMap(int length, List entries) { + mSpanLength = length; + + // Add entries one at a time, rather than just cloning the list, to ensure correctness. + // (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) { - mAddrList.Add(ent); + AddResult result = AddRegion(ent.Offset, ent.Length, ent.Address, ent.IsRelative); + if (result != AddResult.Okay) { + throw new Exception("Unable to add entry (" + result + "): " + ent); + } } - DebugValidate(); + Debug.Assert(entries.Count == mRegionList.Count); + Regenerate(); + } + + public void Clear() { + mRegionList.Clear(); + Regenerate(); } /// - /// Returns a copy of the list of entries. + /// Generates a copy of the list of entries, suitable for passing to a constructor. /// - /// - public List GetEntryList() { - List newList = new List(mAddrList.Count); - foreach (AddressMapEntry ent in mAddrList) { + /// 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) { newList.Add(ent); } + spanLength = mSpanLength; return newList; } // IEnumerable public IEnumerator GetEnumerator() { - return ((IEnumerable)mAddrList).GetEnumerator(); + return ((IEnumerable)mRegionList).GetEnumerator(); } // IEnumerable IEnumerator IEnumerable.GetEnumerator() { - return ((IEnumerable)mAddrList).GetEnumerator(); - } - - /// - /// Returns the Nth entry in the address map. - /// - public AddressMapEntry this[int i] { - get { return mAddrList[i]; } + return ((IEnumerable)mRegionList).GetEnumerator(); } /// /// Number of entries in the address map. /// - public int Count { get { return mAddrList.Count; } } + public int RegionCount { get { return mRegionList.Count; } } /// - /// Returns the Address value of the address map entry associated with the specified - /// offset, or NO_ENTRY_ADDR if there is no address map entry there. The offset must - /// match exactly. + /// Error codes for AddRegion(). /// - public int Get(int offset) { - foreach (AddressMapEntry ad in mAddrList) { - if (ad.Offset == offset) { - return ad.Addr; - } - } - return NO_ENTRY_ADDR; + public enum AddResult { + Unknown = 0, + Okay, // success! + InvalidValue, // offset, length, or address parameter is invalid + OverlapExisting, // new region overlaps existing region exactly + OverlapFloating, // new start matches existing; one or both are floating + StraddleExisting, // new region straddles one or more existing regions + }; + + /// + /// Validate offset/length/addr arguments. + /// + /// + /// We need to verify: + /// - offset >= 0 + /// - offset < total length of file + /// - either length is floating, or: + /// - length > 0 + /// - length < total length of file + /// - offset + length < total length of file + /// - either address is NON_ADDR, or: + /// - addr > 0 + /// - addr <= ADDR_MAX + /// + /// True if everything looks good. + private bool ValidateArgs(int offset, int length, int addr) { + return 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); } /// - /// Returns the index of the address map entry that contains the given offset. - /// We assume the offset is valid. + /// Adds a new region. /// - private int IndexForOffset(int offset) { - for (int i = 1; i < mAddrList.Count; i++) { - if (mAddrList[i].Offset > offset) { - return i - 1; + /// File offset of region start. + /// Length of region, or -1 for a floating end point. + /// Address of region start. + /// 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)) { + Debug.WriteLine("AddRegion: invalid arg"); + return AddResult.InvalidValue; + } + + AddressMapEntry newEntry = new AddressMapEntry(offset, length, addr, + length == FLOATING_LEN, isRelative); + + // Empty list? + if (mRegionList.Count == 0) { + mRegionList.Add(newEntry); + Regenerate(); + return AddResult.Okay; + } + + // Find insertion point. + int insIdx; + for (insIdx = 0; insIdx < mRegionList.Count; insIdx++) { + AddressMapEntry ent = mRegionList[insIdx]; + if (ent.Offset > offset) { + // Insert before this one. + break; + } else if (ent.Offset == offset) { + // We share a start point with this entry. See if we fit inside it or + // wrap around it. + if (length == FLOATING_LEN || ent.Length == FLOATING_LEN) { + // Can't share a start point with a variable-length region. + return AddResult.OverlapFloating; + } else if (ent.Length == length) { + // Same offset/length as existing entry. + return AddResult.OverlapExisting; + } else if (ent.Length < length) { + // New region is larger, would become parent, so insert before this. + break; + } else { + // New region is smaller and will be a child of this entry, so we want + // to insert *after* this point. Loop again to see if the following + // entry is also a parent for this new one. + Debug.Assert(ent.Length > length); + } } } - return mAddrList.Count - 1; + // The insertion index indicates the entry we want to insert before. We need to + // confirm that the new block doesn't straddle the blocks on either side. If we're + // inserting into a bunch of blocks with coincident start points, it's possible for + // the blocks appearing before and after to share the same start offset. + + 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]; + if (ent.Offset == offset) { + // Previous is our parent. These things were checked earlier. + Debug.Assert(length != FLOATING_LEN && ent.Length != FLOATING_LEN); + Debug.Assert(ent.Length > length); + } else { + // Existing block starts before this new one. The existing block must either + // be floating, be completely before this, or completely envelop this. + Debug.Assert(ent.Offset < offset); + if (ent.Length == FLOATING_LEN) { + // sibling -- must end before us + } else if (ent.Offset + ent.Length <= offset) { + // sibling + } else if (length == FLOATING_LEN) { + // existing is parent, we stop at their end + } else if (ent.Offset + ent.Length >= offset + length) { + // existing is parent, ending at or after our end + } else { + // whoops + return AddResult.StraddleExisting; + } + } + } + if (insIdx < mRegionList.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]; + if (ent.Offset == offset) { + // Following block is our child. These things were checked earlier. + Debug.Assert(length != FLOATING_LEN && ent.Length != FLOATING_LEN); + Debug.Assert(ent.Length < length); + } else { + // Existing block starts after this new one. The existing block must either + // be floating, be completely after this, or be completely enveloped by this. + Debug.Assert(ent.Offset > offset); + if (ent.Length == FLOATING_LEN) { + // child or sibling, depending on start offset + } else if (offset + length <= ent.Offset) { + // sibling + } else if (length == FLOATING_LEN) { + // sibling + } else if (ent.Offset + ent.Length <= offset + length) { + // existing is child, ending at or before our end + } else { + // whoops + return AddResult.StraddleExisting; + } + } + } + + mRegionList.Insert(insIdx, newEntry); + Regenerate(); + return AddResult.Okay; } /// - /// Adds, updates, or removes a map entry. + /// Edits the region with the specified offset/len, changing the values of addr and isRel. /// - /// File offset at which the address changes. - /// 24-bit address. - public void Set(int offset, int addr) { - Debug.Assert(offset >= 0); - if (addr == NO_ENTRY_ADDR) { - if (offset != 0) { // ignore attempts to remove entry at offset zero - Remove(offset); - } - return; + /// Offset of region to edit. + /// Length of region to edit. + /// New value for address. + /// 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)) { + throw new Exception("Bad EditRegion args +" + offset.ToString("x6") + + " " + length + " $" + addr); } - Debug.Assert(addr >= 0 && addr < 0x01000000); // 24-bit address space - int i; - for (i = 0; i < mAddrList.Count; i++) { - AddressMapEntry ad = mAddrList[i]; - if (ad.Offset == offset) { - // update existing - mAddrList[i] = new AddressMapEntry(ad.Offset, addr, ad.Length); - return; - } else if (ad.Offset > offset) { - // The i'th entry is one past the interesting part. + int idx = FindRegion(offset, length); + if (idx < 0) { + return false; + } + mRegionList[idx] = new AddressMapEntry(offset, length, addr, length == FLOATING_LEN, + isRelative); + Regenerate(); + return true; + } + + /// + /// Removes the region with the specified offset/len. + /// + /// 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) { + if (!ValidateArgs(offset, length, 0)) { + throw new Exception("Bad RemoveRegion args +" + offset.ToString("x6") + + " " + length); + } + + int idx = FindRegion(offset, length); + if (idx < 0) { + return false; + } + mRegionList.RemoveAt(idx); + Regenerate(); + return true; + } + + /// + /// Finds a region 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) { + for (int i = 0; i < mRegionList.Count; i++) { + if (mRegionList[i].Offset == offset && mRegionList[i].Length == length) { + return i; + } + } + return -1; + } + + // Returns true if adding the specified region is a valid action. + // ??? do we want to do this, or just ask "does region exist"? Depends on + // flow in edit dialog. + //public bool CanAddRegion(int offset, int length) { + // return false; + //} + + /// + /// 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. + /// + /// File offset. + /// 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]); + } + if (mRegionList[i].Offset > offset) { + // Regions are in sorted order, we're done. break; } } - - // Carve a chunk out of the previous entry. - AddressMapEntry prev = mAddrList[i - 1]; - int prevOldLen = prev.Length; - int prevNewLen = offset - prev.Offset; - mAddrList[i - 1] = new AddressMapEntry(prev.Offset, prev.Addr, prevNewLen); - - mAddrList.Insert(i, - new AddressMapEntry(offset, addr, prevOldLen - prevNewLen)); - - DebugValidate(); + return regions; } /// - /// Removes an entry from the set. + /// Regenerates sub-structures after every change. /// - /// The initial offset of the mapping to remove. This - /// must be the initial value, not a mid-range value. - /// True if something was removed. - public bool Remove(int offset) { - if (offset == 0) { - throw new Exception("Not allowed to remove entry 0"); + private void Regenerate() { + GenerateTree(); + GenerateLinear(); + Debug.Assert(DebugValidate()); + } + + /// + /// Performs internal consistency checks. Prints a message and returns false on failure. + /// + private bool DebugValidate() { + bool result = true; + result &= DebugValidateStructural(); + result &= DebugValidateHierarchical(); + return result; + } + + private bool DebugValidateStructural() { + int lastStart = -1; + int lastLength = OFFSET_MAX + 1; + for (int i = 0; i < mRegionList.Count; i++) { + AddressMapEntry ent = mRegionList[i]; + + // Do basic range checks on arguments. + if (ent.Offset < 0 || ent.Offset > OFFSET_MAX) { + Debug.WriteLine("Bad offset +" + ent.Offset.ToString("x6")); + return false; + } + if (ent.Length <= 0 && ent.Length != FLOATING_LEN) { + Debug.WriteLine("Bad length " + ent.Length); + return false; + } + if (ent.Length > OFFSET_MAX || (long)ent.Offset + (long)ent.Length > OFFSET_MAX) { + Debug.WriteLine("Bad length +" + ent.Offset.ToString("x6") + + " len=" + ent.Length); + return false; + } + if ((ent.Address < 0 && ent.Address != NON_ADDR) || ent.Address > ADDR_MAX) { + Debug.WriteLine("Bad address $" + ent.Address.ToString("x4")); + return false; + } + + // Compare to EOF. + if (ent.Length != FLOATING_LEN && ent.Offset + ent.Length > mSpanLength) { + Debug.WriteLine("Entry exceeds file bounds"); + return false; + } + + // Verify ordering. + if (ent.Offset < lastStart) { + Debug.WriteLine("Bad sort: start"); + return false; + } else if (ent.Offset == lastStart) { + if (ent.Length == FLOATING_LEN || lastLength == FLOATING_LEN) { + Debug.WriteLine("Overlapping float and non-float"); + return false; + } + if (ent.Length == lastLength) { + Debug.WriteLine("Overlapping regions"); + return false; + } else if (ent.Length > lastLength) { + Debug.WriteLine("Bad sort: end"); + return false; + } + } + lastStart = ent.Offset; + lastLength = ent.Length; + } + return true; + } + + public override string ToString() { + return "[AddressMap: " + mRegionList.Count + " entries]"; + } + + #endregion Structural + + #region Hierarchical + + /// + /// Tree data structure. Only visible internally. + /// + /// + /// 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. + /// + private class TreeNode { + public AddressMapEntry Entry { get; set; } + public TreeNode Parent { get; set; } + public List Children { get; set; } + + public TreeNode(AddressMapEntry entry, TreeNode parent) { + Entry = entry; + Parent = parent; + // all other fields null/false + } + } + + /// + /// Top of the hierarchy. The topmost node is a no-address node that spans the entire + /// file. + /// + private TreeNode mTopNode; + + + /// + /// Generates a tree that spans the entire region. + /// + private void GenerateTree() { + // 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); + + // Generate the children of this node. + int index = -1; + GenerateChildren(topNode, ref index); + + if (index != mRegionList.Count) { + Debug.Assert(false, "Not all regions traversed"); } - for (int i = 1; i < mAddrList.Count; i++) { - if (mAddrList[i].Offset == offset) { - // Add the length to the previous entry. - AddressMapEntry prev = mAddrList[i - 1]; - mAddrList[i - 1] = new AddressMapEntry(prev.Offset, prev.Addr, - prev.Length + mAddrList[i].Length); + // Replace previous tree. + mTopNode = topNode; + } - mAddrList.RemoveAt(i); - DebugValidate(); - return true; + /// + /// Generates a tree node for the specified region. This might be a single item, or + /// the top of a tree. + /// + /// Parent of this node. May be null for top-level entries. + /// On entry, index of current (parent) node. On exit, index of + /// region that is past the tree spanned by this node. + /// Newly-created node. + private void GenerateChildren(TreeNode parent, ref int index) { + List children = new List(); + + index++; + while (index < mRegionList.Count) { + AddressMapEntry childEnt = mRegionList[index]; + + if (childEnt.Offset >= parent.Entry.Offset + parent.Entry.Length) { + // Starts after end of parent, not a child. + break; + } + + 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; + index++; + if (index < mRegionList.Count) { + // Check next sibling. + int sibStart = mRegionList[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)); + + // "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); + children.Add(thisNode); + + // Check for grandchildren. "index" will point to first entry beyond this + // child and its descendants. + GenerateChildren(thisNode, ref index); } } - return false; + + // Set child list if it's non-empty. + if (children.Count > 0) { + parent.Children = children; + } } - /// - /// Returns true if the given address falls into the range spanned by the - /// address map entry. - /// - /// Address map entry index. - /// Address to check. - /// - private bool IndexContainsAddress(int index, int addr) { - return addr >= mAddrList[index].Addr && - addr < mAddrList[index].Addr + mAddrList[index].Length; - } + /* + Thoughts on AddressToOffset optimization... + + We can create a simple linear range map, but we have to do it separately for + every node in the tree (i.e. every unique srcOffset). We can do this on demand. + + The idea would be to find the leaf node for the source offset, add the address + range for that node, and then expand outward as we would do when attempting to + resolve an address. As we traverse each node we add the address ranges to the + set, but we don't replace existing entries. (In some cases a single entry may + generate multiple disjoint ranges if it overlaps several things.) + + Once the map is generated, we store a reference to it in the tree node, and then + use that for all future lookups. Since changes to the tree are rare, and we only + generate these tables on the first series of lookups after a change, the overhead + of generating these should be small. Since it's a list of address ranges + (similar in principle to TypedRangeSet), it shouldn't be very large, even for + larger address spaces. + */ /// /// Determines the file offset that best contains the specified target address. /// + /// + /// Algorithm: + /// - Start in the node that contains the source offset. + /// - Loop: + /// - Recursively scan all children of the current node, in order of increasing Offset. + /// - Check the current node. If it matches, we're done. + /// - Move up to the parent. + /// - If we run off the top of the tree, return -1. + /// + /// We're doing a depth-first search, checking the children before the current node. + /// + /// Because each node holds an arbitrary address range, we need to search all of them. + /// There is no early-exit for the not-found case. + /// + /// We can't simply compare the Address/Length values to check for a match, because + /// children may have created "holes". If the address falls in a node's range, we need + /// to walk the child list and see if the address is present. + /// /// Offset of the address reference. /// Address to look up. /// The file offset, or -1 if the address falls outside the file. public int AddressToOffset(int srcOffset, int targetAddr) { - if (mAddrList.Count == 1) { - // Trivial case. - if (IndexContainsAddress(0, targetAddr)) { - Debug.Assert(targetAddr >= mAddrList[0].Addr); - return targetAddr - mAddrList[0].Addr; - } else { + TreeNode startNode = OffsetToNode(srcOffset, mTopNode); + + TreeNode ignoreNode = null; + while (true) { + int offset = FindAddress(startNode, ignoreNode, targetAddr); + if (offset >= 0) { + // Return the offset we found. + return offset; + } + + // Didn't find it. Move up one level, but ignore the branch we've already checked. + ignoreNode = startNode; + startNode = startNode.Parent; + if (startNode == null) { return -1; } } + } - // We have multiple, potentially overlapping address ranges. Start by - // looking for a match in the srcOffset range; if that fails, scan - // forward from the start. - int srcOffIndex = IndexForOffset(srcOffset); - if (IndexContainsAddress(srcOffIndex, targetAddr)) { - Debug.Assert(targetAddr >= mAddrList[srcOffIndex].Addr); - return (targetAddr - mAddrList[srcOffIndex].Addr) + mAddrList[srcOffIndex].Offset; - } - - for (int i = 0; i < mAddrList.Count; i++) { - if (i == srcOffIndex) { - // optimization -- we already checked this one - continue; - } - if (IndexContainsAddress(i, targetAddr)) { - Debug.Assert(targetAddr >= mAddrList[i].Addr); - return (targetAddr - mAddrList[i].Addr) + mAddrList[i].Offset; + /// + /// Finds a matching address range, starting from a specific point in the tree and + /// searching downward. One child can be ignored. + /// + /// Start point. + /// Child to ignore (because it was examined earlier). + /// Address to find. + /// Offset, or -1 if not found. + private int FindAddress(TreeNode node, TreeNode ignore, int targetAddr) { + if (node.Children != null) { + foreach (TreeNode childNode in node.Children) { + if (childNode == ignore) { + continue; + } + int offset = FindAddress(childNode, null, targetAddr); + if (offset >= 0) { + // Found match in child, return that. + return offset; + } } } - return -1; + // Wasn't in any of the children, see if it's in this node. + AddressMapEntry ent = node.Entry; + if (ent.Address == NON_ADDR) { + // Non-addressable space. + return -1; + } + if (targetAddr < ent.Address || targetAddr >= ent.Address + ent.Length) { + // Outside our range of addresses, return failure. + return -1; + } + + // 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 + foreach (TreeNode childNode in node.Children) { + AddressMapEntry childEnt = childNode.Entry; + int childStartPosn = childEnt.Offset - ent.Offset; + int childEndPosn = childStartPosn + childEnt.Length; + + if (childStartPosn > subPosn) { + // Child is past the target, it's not in a hole; no need to check + // additional children because the children are sorted by Offset. + break; + } else if (subPosn >= childStartPosn && subPosn < childEndPosn) { + // Target is in a hole occupied by the child. No good. + return -1; + } + } + } + return ent.Offset + (targetAddr - ent.Address); } /// /// Converts a file offset to an address. /// /// File offset. - /// 24-bit address. + /// 24-bit address, which may be NON_ADDR. public int OffsetToAddress(int offset) { - int srcOffIndex = IndexForOffset(offset); - return mAddrList[srcOffIndex].Addr + (offset - mAddrList[srcOffIndex].Offset); + if (offset < 0 || offset >= mSpanLength) { + // Invalid offset. Could throw or return an error. + Debug.WriteLine("Warning: OffsetToAddress invalid offset +" + + offset.ToString("x6")); + return NON_ADDR; + } + + // Scan tree to find appropriate node. The tree is guaranteed to cover all offsets. + TreeNode node = OffsetToNode(offset, mTopNode); + + // 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); + } + return ourAddr; } /// - /// Checks to see if the specified range of offsets is in a single address range. Use - /// this to see if something crosses an address-change boundary. This does not - /// handle no-op address changes specially. + /// Recursively descends into the tree to find the node that contains the offset. /// + /// File offset. + /// Node to examine. + /// Matching node. + 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) { + // It's in or below this child. Check it with tail recursion. + return OffsetToNode(offset, child); + } + } + } + return node; + } + + /// + /// 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 + /// does not smooth over no-op address changes. + /// + /// + /// This is NOT intended to say whether the sequence of addresses has a hiccup. The goal + /// is to identify multi-byte elements that have a .ORG statement in the middle. + /// + /// We can do this in a couple of different ways: + /// 1. Find the node that holds the offset, confirm that it spans offset+length, and + /// then check to see if there are any children that start between the two. + /// 2. Walk through the linear list and see if there are any events between offset + /// and offset+length. + /// Walking the linear list is simpler but likely slower. + /// /// Start offset. /// Length of region. - /// True if the data area is unbroken. - public bool IsSingleAddrRange(int offset, int length) { - Debug.Assert(offset >= 0 && offset < mTotalLength); - Debug.Assert(length > 0 && offset + length <= mTotalLength); - return (IndexForOffset(offset) == IndexForOffset(offset + length - 1)); + /// True if the range of offsets is unbroken. + public bool IsRangeUnbroken(int offset, int length) { + if (!ValidateArgs(offset, length, 0)) { + 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); + int lastOffset = offset + length - 1; // offset of last byte in range + if (lastOffset >= ent.Offset + ent.Length) { + // end of region is not in this node + return false; + } + + // 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; + + if (childEnt.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) { + // Target is in a hole occupied by the child. No good. + return false; + } + } + } + + return true; } + private bool DebugValidateHierarchical() { + if (mTopNode.Entry.Offset != 0 || mTopNode.Entry.Length != mSpanLength) { + Debug.WriteLine("Malformed top node"); + return false; + } + + int nodeCount = 0; + if (mTopNode.Children != null) { + DebugValidateHierarchy(mTopNode.Children, 0, mSpanLength, ref nodeCount); + } + + // Check node count. It should have one entry for every entry in the region list + // (we don't count mTopNode). + if (nodeCount != mRegionList.Count) { + Debug.WriteLine("Hierarchical is missing entries: nodeCount=" + nodeCount + + " regionCount=" + mRegionList.Count); + return false; + } + return true; + } + + 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 + + nodeCount++; + + if (node.Entry.Offset < startOffset || + node.Entry.Offset + node.Entry.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)) { + return false; + } + } + } + return true; + } + + #endregion Hierarchical + + #region Linear /// - /// Internal consistency checks. + /// Ordered list of change events. /// - private void DebugValidate() { - if (mAddrList.Count < 1) { - throw new Exception("AddressMap: empty"); - } - if (mAddrList[0].Offset != 0) { - throw new Exception("AddressMap: bad offset 0"); - } + private List mChangeList = new List(); - if (mAddrList.Count == 1) { - if (mAddrList[0].Length != mTotalLength) { - throw new Exception("AddressMap: single entry len bad"); - } - } else { - int totalLen = 0; - for (int i = 0; i < mAddrList.Count; i++) { - AddressMapEntry ent = mAddrList[i]; - if (i != 0) { - if (ent.Offset != mAddrList[i - 1].Offset + mAddrList[i - 1].Length) { - throw new Exception("Bad offset step to " + i); - } + /// + /// Address change "event". + /// + /// Instances are immutable. + /// + public class AddressChange { + // True if this is a region start, false if a region end. + public bool IsStart { get; private set; } + + // Offset at which change occurs. For end points, this at the offset AFTER + // the last offset in a region. + public int Offset { get; private set; } + + // Address at Offset after change. For a region-end change, this is an address + // in the parent's range. + public int Address { get; private set; } + + // Reference to the AddressMapEntry that generated this entry. The reference + // will be the same for the "start" and "end" entries. + public AddressMapEntry Entry { get; private set; } + + public AddressChange(bool isStart, int offset, int addr, AddressMapEntry ent) { + IsStart = isStart; + Offset = offset; + Address = addr; + Entry = ent; + } + } + + /// + /// Generates a linear list of changes, using the data from the hierarchical representation. + /// + private void GenerateLinear() { + // The top layer is treated specially, because we don't want to show the outer + // no-address zone. Instead, we synthesize fake zones in the gaps. + List changeList = new List(); + int startOffset = 0; + int extraNodes = 0; + + if (mTopNode.Children != null) { + foreach (TreeNode node in mTopNode.Children) { + Debug.Assert(node.Entry.Length > 0); // all floaters should be resolved + + if (node.Entry.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)); + extraNodes++; } - totalLen += ent.Length; + AddChangeEntry(changeList, node, NON_ADDR); + + startOffset = node.Entry.Offset + node.Entry.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)); + extraNodes++; + } + + if (changeList.Count != (mRegionList.Count + extraNodes) * 2) { + Debug.Assert(false, "Incorrect linear count: regions*2=" + (mRegionList.Count * 2) + + " extraNodes=" + extraNodes + " changeList=" + changeList.Count); + } + + mChangeList = changeList; + } + + public IEnumerator AddressChangeIterator { + get { return mChangeList.GetEnumerator(); } + } + + /// + /// Recursively adds tree nodes. + /// + /// List to which changes are added. + /// Node to add + /// Address at which node's start offset appears in + /// parent's region. + private void AddChangeEntry(List changeList, TreeNode node, + int parentStartAddr) { + Debug.Assert(node.Entry.Length != FLOATING_LEN); + int nextAddr = NON_ADDR; + if (parentStartAddr != NON_ADDR) { + nextAddr = parentStartAddr + node.Entry.Length; + } + AddressChange startChange = new AddressChange(true, + node.Entry.Offset, node.Entry.Address, node.Entry); + AddressChange endChange = new AddressChange(false, + node.Entry.Offset + node.Entry.Length, nextAddr, node.Entry); + + changeList.Add(startChange); + int curAddr = node.Entry.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; + } + AddChangeEntry(changeList, childNode, mySpaceAddr); + } + } + changeList.Add(endChange); + } + + private const string CRLF = "\r\n"; + + /// + /// Formats the address map for debugging. (Does not use Asm65.Formatter, so is not + /// suitable for display to the user.) + /// + public string FormatAddressMap() { + StringBuilder sb = new StringBuilder(); + int depth = 0; + AddressChange prevChange = null; + + sb.AppendLine("Address map, len=$" + mSpanLength.ToString("x4")); + IEnumerator iter = this.AddressChangeIterator; + while (iter.MoveNext()) { + AddressChange change = iter.Current; + if (change.IsStart) { + if (prevChange != null && change.Offset != prevChange.Offset) { + // Start of region at new offset. Output address info for space + // between previous start or end. + PrintAddressInfo(sb, depth, prevChange.Address, + change.Offset - prevChange.Offset); + } + + // Start following end, or start following start after a gap. + PrintDepthLines(sb, depth); + sb.Append("+- +" + change.Offset.ToString("x6") + " START ("); + PrintAddress(sb, change.Address); + sb.Append(")"); + sb.Append(CRLF); + + depth++; + } else { + Debug.Assert(prevChange != null); + depth--; + + if (change.Offset != prevChange.Offset) { + // End of region at new offset. Output address info for space + // between previous start or end. + PrintAddressInfo(sb, depth + 1, prevChange.Address, + change.Offset - prevChange.Offset); + } + + PrintDepthLines(sb, depth); + sb.Append("+- +" + change.Offset.ToString("x6") + " END (now "); + PrintAddress(sb, change.Address); + sb.Append(")"); + sb.Append(CRLF); } - if (totalLen != mTotalLength) { - throw new Exception("AddressMap: bad length sum (" + totalLen + " vs " + - mTotalLength + ")"); - } + prevChange = change; + } + Debug.Assert(depth == 0); + + return sb.ToString(); + } + + private static void PrintDepthLines(StringBuilder sb, int depth) { + while (depth-- > 0) { + sb.Append("| "); } } - public override string ToString() { - return "[AddressMap: " + mAddrList.Count + " entries]"; + private static void PrintAddressInfo(StringBuilder sb, int depth, + int startAddr, int length) { + PrintDepthLines(sb, depth); + sb.Append(' '); + if (startAddr == NON_ADDR) { + sb.Append("-NA-"); + } else { + PrintAddress(sb, startAddr); + sb.Append(" - "); + PrintAddress(sb, startAddr + length - 1); + } + sb.Append(" (length=$" + length.ToString("x4") + "/" + length + " bytes)"); + sb.Append(CRLF); } + + private static void PrintAddress(StringBuilder sb, int addr) { + if (addr == NON_ADDR) { + sb.Append("-NA-"); + } else { + sb.Append("$"); + sb.Append(addr.ToString("x4")); + } + } + + #endregion Linear + + #region Unit tests + + private static void Test_Expect(AddResult expected, ref bool result, AddResult actual) { + if (expected != actual) { + Debug.WriteLine("test failed (expected=" + expected + ", actual=" + actual + ")"); + result = false; + } + } + private static void Test_Expect(bool expected, ref bool result, bool actual) { + if (expected != actual) { + Debug.WriteLine("test failed (expected=" + expected + ", actual=" + actual + ")"); + result = false; + } + } + + private static void Test_Expect(int expected, ref bool result, int actual) { + if (expected != actual) { + Debug.WriteLine("test failed (expected=$" + expected.ToString("x4") + "/" + + expected + ", actual=$" + actual.ToString("x4") + "/" + actual + ")"); + result = false; + } + } + + private static bool Test_SimpleLinear() { + const int mapLen = 0x8000; + AddressMap map = new AddressMap(mapLen); + bool result = true; + + const int off0 = 0x000000; + const int len0 = 0x0200; + const int adr0 = 0x1000; + const int off1 = 0x000200; + const int len1 = 0x0500; + const int adr1 = 0x1200; + const int off2 = 0x000700; + const int len2 = 0x0300; + const int adr2 = 0x1700; + + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(off0, len0, adr0, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(off1, len1, adr1, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(off2, len2, adr2, false)); + result &= map.DebugValidate(); + + Test_Expect(AddResult.OverlapExisting, ref result, + map.AddRegion(off0, len0, 0x1000, false)); + Test_Expect(AddResult.OverlapFloating, ref result, + map.AddRegion(off0, FLOATING_LEN, 0x1000, false)); + Test_Expect(AddResult.StraddleExisting, ref result, + map.AddRegion(off0 + 1, len0, 0x1000, false)); + Test_Expect(AddResult.InvalidValue, ref result, + map.AddRegion(off0, mapLen + 1, 0x1000, false)); + + // One region to wrap them all. Add then remove. + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(off0, mapLen, 0x0000, false)); + Test_Expect(true, ref result, map.RemoveRegion(off0, mapLen)); + Test_Expect(false, ref result, map.RemoveRegion(off0, mapLen)); + + Test_Expect(adr0, ref result, map.OffsetToAddress(off0)); + Test_Expect(adr1, ref result, map.OffsetToAddress(off1)); + Test_Expect(adr2, ref result, map.OffsetToAddress(off2)); + Test_Expect(adr0 + 0x100, ref result, map.OffsetToAddress(off0 + 0x100)); + Test_Expect(NON_ADDR, ref result, map.OffsetToAddress(0x004000)); // hole in map + Test_Expect(NON_ADDR, ref result, map.OffsetToAddress(mapLen)); // bad offset + + Test_Expect(0x000000, ref result, map.AddressToOffset(0x000000, 0x1000)); + Test_Expect(0x000000, ref result, map.AddressToOffset(0x000200, 0x1000)); + Test_Expect(0x000000, ref result, map.AddressToOffset(0x000700, 0x1000)); + Test_Expect(0x000250, ref result, map.AddressToOffset(0x000000, 0x1250)); + Test_Expect(0x000250, ref result, map.AddressToOffset(0x000200, 0x1250)); + Test_Expect(0x000250, ref result, map.AddressToOffset(0x000700, 0x1250)); + Test_Expect(0x0009ff, ref result, map.AddressToOffset(0x0001ff, 0x19ff)); + Test_Expect(0x0009ff, ref result, map.AddressToOffset(0x0006ff, 0x19ff)); + Test_Expect(0x0009ff, ref result, map.AddressToOffset(0x0009ff, 0x19ff)); + Test_Expect(-1, ref result, map.AddressToOffset(0x000000, 0x7000)); + + result &= map.DebugValidate(); + return result; + } + + private static bool Test_SimpleFloatGap() { + const int mapLen = 0x8000; + AddressMap map = new AddressMap(mapLen); + bool result = true; + + const int off0 = 0x001000; + const int len0 = FLOATING_LEN; + const int adr0 = 0x1000; + const int off1 = 0x004000; + const int len1 = 0x3000; + const int adr1 = 0x1200; + const int off2 = 0x005000; + const int len2 = 0x0100; + const int adr2 = NON_ADDR; + + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(off0, len0, adr0, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(off1, len1, adr1, false)); + + // 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)); + + Test_Expect(adr0, ref result, map.OffsetToAddress(off0)); + Test_Expect(adr1, ref result, map.OffsetToAddress(off1)); + Test_Expect(adr2, ref result, map.OffsetToAddress(off2)); + Test_Expect(adr0 + 1, ref result, map.OffsetToAddress(off0 + 1)); + Test_Expect(adr1 + len2, ref result, map.OffsetToAddress(off1 + len2)); + Test_Expect(NON_ADDR, ref result, map.OffsetToAddress(off1 + len1)); + + Test_Expect(-1, ref result, map.AddressToOffset(0x000000, 0x0000)); + Test_Expect(0x001005, ref result, map.AddressToOffset(0x000000, 0x1005)); + // Find the "correct" $21ff. + Test_Expect(0x0021ff, ref result, map.AddressToOffset(0x000000, 0x21ff)); + Test_Expect(0x004fff, ref result, map.AddressToOffset(0x004000, 0x21ff)); + // There's only one $2205. + Test_Expect(0x002205, ref result, map.AddressToOffset(0x000000, 0x2205)); + Test_Expect(0x002205, ref result, map.AddressToOffset(0x004000, 0x2205)); + + result &= map.DebugValidate(); + return result; + } + + private static bool Test_Nested() { + AddressMap map = new AddressMap(0x8000); + bool result = true; + // Nested with shared start offset. + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x000100, 0x0400, 0x4000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x000100, 0x0100, 0x7000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x000100, 0x0300, 0x5000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x000100, 0x0200, 0x6000, false)); + // Add a couple of floaters. + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x0000ff, FLOATING_LEN, 0x30ff, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x000101, FLOATING_LEN, 0x3101, false)); + Test_Expect(AddResult.OverlapFloating, ref result, + map.AddRegion(0x000100, FLOATING_LEN, 0x3100, false)); + + // Nested with shared end offset. + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x000fff, FLOATING_LEN, 0x3fff, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x001200, 0x0200, 0x6000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x001000, 0x0400, 0x4000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x001100, 0x0300, 0x5000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x001300, 0x0100, 0x7000, false)); + // Single-byte region at start and end. + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x001200, 1, 0x8200, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x0013ff, 1, 0x83ff, 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)); + + // 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)); + + // Try floater then overlap. + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x004000, FLOATING_LEN, 0x8000, false)); + Test_Expect(AddResult.OverlapFloating, ref result, + map.AddRegion(0x004000, 0x100, 0x8000, false)); + Test_Expect(true, ref result, map.RemoveRegion(0x004000, FLOATING_LEN)); + + Test_Expect(0x30ff, ref result, map.OffsetToAddress(0x0000ff)); + Test_Expect(0x7000, ref result, map.OffsetToAddress(0x000100)); + Test_Expect(0x3101, ref result, map.OffsetToAddress(0x000101)); + Test_Expect(0x5000, ref result, map.OffsetToAddress(0x001100)); + Test_Expect(0x7000, ref result, map.OffsetToAddress(0x001300)); + + // The first chunk has $5000, but it's a shared start with children. So we'll + // find it in the second chunk. + Test_Expect(0x001100, ref result, map.AddressToOffset(0x000000, 0x5000)); + // It's also in the 3rd/4th chunks, so we'll find it there if we start there. + Test_Expect(0x002100, ref result, map.AddressToOffset(0x002300, 0x5000)); + Test_Expect(0x003100, ref result, map.AddressToOffset(0x003000, 0x5000)); + + result &= map.DebugValidate(); + return result; + } + + private static bool Test_Cross() { + const int mapLen = 0x4000; + 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(0x003105, ref result, map.AddressToOffset(0x000000, 0xf105)); + Test_Expect(0x003105, ref result, map.AddressToOffset(0x002100, 0xf105)); + Test_Expect(0x003105, ref result, map.AddressToOffset(0x003100, 0xf105)); + + Test_Expect(0x002105, ref result, map.AddressToOffset(0x000000, 0xe105)); + Test_Expect(0x002105, ref result, map.AddressToOffset(0x002100, 0xe105)); + Test_Expect(0x002105, ref result, map.AddressToOffset(0x003100, 0xe105)); + + // $8105 doesn't exist in the second chunk because there's a hole there. We + // find it in the first chunk instead. + Test_Expect(0x000105, ref result, map.AddressToOffset(0x000000, 0x8105)); + Test_Expect(0x000105, ref result, map.AddressToOffset(0x002000, 0x8105)); + + // $8400 exists in the first chunk, and in a child of the second chunk. If + // we start anywhere in the second chunk we'll find the second address. + Test_Expect(0x000400, ref result, map.AddressToOffset(0x000000, 0x8400)); + Test_Expect(0x002400, ref result, map.AddressToOffset(0x002000, 0x8400)); + Test_Expect(0x002400, ref result, map.AddressToOffset(0x002100, 0x8400)); + Test_Expect(0x002400, ref result, map.AddressToOffset(0x003100, 0x8400)); + + Test_Expect(0x001100, ref result, map.AddressToOffset(0x000000, 0x9100)); + + result &= map.DebugValidate(); + return result; + } + + private static bool Test_Pyramids() { + const int mapLen = 0xc000; + AddressMap map = new AddressMap(mapLen); + bool result = true; + + // Pyramid shape, all regions start at same address except last. + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x000000, 0x6000, 0x8000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x001000, 0x4000, 0x8000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x002000, 0x2000, 0x7fff, false)); + + // Second pyramid. + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x006000, 0x6000, 0x8000, false)); + Test_Expect(AddResult.Okay, ref result, + map.AddRegion(0x007000, 0x4000, 0x8000, false)); + 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 + + // Children take priority over the start node. + Test_Expect(0x002001, ref result, map.AddressToOffset(0x000000, 0x8000)); + Test_Expect(0x003000, ref result, map.AddressToOffset(0x000000, 0x8fff)); + Test_Expect(0x002001, ref result, map.AddressToOffset(0x001000, 0x8000)); + Test_Expect(0x003000, ref result, map.AddressToOffset(0x001000, 0x8fff)); + Test_Expect(0x002001, ref result, map.AddressToOffset(0x002000, 0x8000)); + Test_Expect(0x002000, ref result, map.AddressToOffset(0x000000, 0x7fff)); + + Test_Expect(0x005000, ref result, map.AddressToOffset(0x000000, 0xd000)); + Test_Expect(0x005000, ref result, map.AddressToOffset(0x003000, 0xd000)); + + Test_Expect(-1, ref result, map.AddressToOffset(0x000000, 0xc000)); + Test_Expect(-1, ref result, map.AddressToOffset(0x000000, 0xcfff)); + + Test_Expect(0x008000, ref result, map.AddressToOffset(0x006000, 0x8000)); + Test_Expect(0x008000, ref result, map.AddressToOffset(0x007000, 0x8000)); + Test_Expect(0x008000, ref result, map.AddressToOffset(0x008000, 0x8000)); + Test_Expect(0x008000, ref result, map.AddressToOffset(0x00bfff, 0x8000)); + + // $7fff doesn't exist in second chunk, so we have to go back to first to find it. + Test_Expect(0x002000, ref result, map.AddressToOffset(0x008000, 0x7fff)); + Test_Expect(-1, ref result, map.AddressToOffset(0x008000, 0xa000)); + + // inside + Test_Expect(true, ref result, map.IsRangeUnbroken(0x000000, 1)); + Test_Expect(true, ref result, map.IsRangeUnbroken(0x007000, 0x0800)); + // at edges + Test_Expect(true, ref result, map.IsRangeUnbroken(0x000ffe, 2)); + Test_Expect(true, ref result, map.IsRangeUnbroken(0x001000, 2)); + Test_Expect(true, ref result, map.IsRangeUnbroken(0x007000, 0x1000)); + // crossing edge + Test_Expect(false, ref result, map.IsRangeUnbroken(0x000fff, 2)); + // fully encapsulating + Test_Expect(false, ref result, map.IsRangeUnbroken(0x005500, 0x1000)); + + result &= map.DebugValidate(); + return result; + } + + public static bool Test() { + bool ok = true; + ok &= Test_SimpleLinear(); + ok &= Test_SimpleFloatGap(); + ok &= Test_Nested(); + ok &= Test_Cross(); + ok &= Test_Pyramids(); + + Debug.WriteLine("AddressMap: test complete (ok=" + ok + ")"); + return ok; + } + + #endregion Unit tests } } diff --git a/PluginCommon/PluginManager.cs b/PluginCommon/PluginManager.cs index 14da3b2..39b3826 100644 --- a/PluginCommon/PluginManager.cs +++ b/PluginCommon/PluginManager.cs @@ -179,11 +179,12 @@ namespace PluginCommon { /// Invokes the Prepare() method on all active plugins. /// /// Reference to host object providing app services. + /// Length of data spanned by address map. /// Serialized AddressMap entries. /// SymbolTable contents, converted to PlSymbol. - public void PreparePlugins(IApplication appRef, + public void PreparePlugins(IApplication appRef, int spanLength, List addrEntries, List plSyms) { - AddressMap addrMap = new AddressMap(addrEntries); + AddressMap addrMap = new AddressMap(spanLength, addrEntries); AddressTranslate addrTrans = new AddressTranslate(addrMap); foreach (KeyValuePair kvp in mActivePlugins) { IPlugin ipl = kvp.Value; diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index 3f9ff09..f39a263 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -92,6 +92,20 @@ namespace SourceGen.AsmGen { /// private StreamWriter mOutStream; + /// + /// Output mode; determines how ORG is handled. + /// + private enum OutputMode { + Unknown = 0, Loadable = 1, Streamable = 2 + } + private OutputMode mOutputMode; + + /// + /// Current pseudo-PC depth. 0 is the "real" PC. + /// + private int mPcDepth; + private bool mFirstIsOpen; + /// /// Holds detected version of configured assembler. /// @@ -101,9 +115,6 @@ namespace SourceGen.AsmGen { private static CommonUtil.Version V0_96_4 = new CommonUtil.Version(0, 96, 4); private static CommonUtil.Version V0_97 = new CommonUtil.Version(0, 97); - // Set if we're inside a "pseudopc" block, which will need to be closed. - private bool mInPseudoPcBlock; - // v0.97 started treating '\' in constants as an escape character. private bool mBackslashEscapes = true; @@ -191,6 +202,20 @@ namespace SourceGen.AsmGen { AssemblerConfig config = AssemblerConfig.GetConfig(settings, AssemblerInfo.Id.Acme); mColumnWidths = (int[])config.ColumnWidths.Clone(); + + // ACME wants the entire file to be loadable into a 64KB memory area. If the + // initial address is too large, a file smaller than 64KB might overrun the bank + // boundary and cause a failure. In that case we want to set the initial address + // to zero and "stream" the rest. + int firstAddr = project.AddrMap.OffsetToAddress(0); + if (firstAddr == AddressMap.NON_ADDR) { + firstAddr = 0; + } + if (firstAddr + project.FileDataLength > 65536) { + mOutputMode = OutputMode.Streamable; + } else { + mOutputMode = OutputMode.Loadable; + } } /// @@ -244,6 +269,9 @@ namespace SourceGen.AsmGen { mLocalizer.QuirkNoOpcodeMnemonics = true; mLocalizer.Analyze(); + mPcDepth = 0; + mFirstIsOpen = true; + // Use UTF-8 encoding, without a byte-order mark. using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) { mOutStream = sw; @@ -258,16 +286,15 @@ namespace SourceGen.AsmGen { // don't try OutputLine(SourceFormatter.FullLineCommentDelimiter + "ACME can't handle 65816 code that lives outside bank zero"); - int orgAddr = Project.AddrMap.Get(0); - OutputOrgDirective(0, orgAddr); + int orgAddr = Project.AddrMap.OffsetToAddress(0); + AddressMap.AddressMapEntry fakeEnt = new AddressMap.AddressMapEntry(0, + Project.FileData.Length, orgAddr, false, false); + OutputOrgDirective(fakeEnt, true); OutputDenseHex(0, Project.FileData.Length, string.Empty, string.Empty); + OutputOrgDirective(fakeEnt, false); } else { GenCommon.Generate(this, sw, worker); } - - if (mInPseudoPcBlock) { - OutputLine(string.Empty, CLOSE_PSEUDOPC, string.Empty, string.Empty); - } } mOutStream = null; @@ -283,7 +310,7 @@ namespace SourceGen.AsmGen { return false; } foreach (AddressMap.AddressMapEntry ent in Project.AddrMap) { - if (ent.Addr > 0xffff) { + if (ent.Address > 0xffff) { return true; } } @@ -541,29 +568,39 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(int offset, int address) { - // If there's only one address range, just set the "real" PC. If there's more - // than one we can run out of space if the source file has a chunk in high memory - // followed by a chunk in low memory, because the "real" PC determines when the - // 64KB bank is overrun. - if (offset == 0) { - // first one - if (Project.AddrMap.Count == 1) { - OutputLine("*", "=", SourceFormatter.FormatHexValue(address, 4), string.Empty); - return; + public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) { + // This is similar in operation to the AsmTass64 implementation. See comments there. + Debug.Assert(mPcDepth >= 0); + if (isStart) { + if (mPcDepth == 0 && mFirstIsOpen) { + mPcDepth++; + + // Set the "real" PC for the first address change. If we're in "loadable" + // mode, just set "*=". If we're in "streaming" mode, we set "*=" to zero + // and then use a pseudo-PC. + if (mOutputMode == OutputMode.Loadable) { + OutputLine("*", "=", SourceFormatter.FormatHexValue(addrEntry.Address, 4), + string.Empty); + return; + } else { + // set the real PC to address zero to ensure we get a full 64KB + OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); + } + } + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), + SourceFormatter.FormatHexValue(addrEntry.Address, 4) + " {", string.Empty); + mPcDepth++; + } else { + mPcDepth--; + if (mPcDepth > 0 || !mFirstIsOpen) { + // close previous block + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(CLOSE_PSEUDOPC), + string.Empty, string.Empty); } else { - // set the real PC to address zero to ensure we get a full 64KB - OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); + // mark initial "*=" region as closed, but don't output anything + mFirstIsOpen = false; } } - - if (mInPseudoPcBlock) { - // close previous block - OutputLine(string.Empty, CLOSE_PSEUDOPC, string.Empty, string.Empty); - } - OutputLine(string.Empty, sDataOpNames.OrgDirective, - SourceFormatter.FormatHexValue(address, 4) + " {", string.Empty); - mInPseudoPcBlock = true; } // IGenerator diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index 21d842b..1059828 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -250,18 +250,20 @@ namespace SourceGen.AsmGen { sw.WriteLine("MEMORY {"); sw.WriteLine(" MAIN: file=%O, start=%S, size=65536;"); - for (int i = 0; i < Project.AddrMap.Count; i++) { - AddressMap.AddressMapEntry ame = Project.AddrMap[i]; - sw.WriteLine(string.Format("# MEM{0:D3}: file=%O, start=${1:x4}, size={2};", - i, ame.Addr, ame.Length)); - } + //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;"); - for (int i = 0; i < Project.AddrMap.Count; i++) { - sw.WriteLine(string.Format("# SEG{0:D3}: load=MEM{0:D3}, type=rw;", i)); - } + //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 {}"); @@ -559,27 +561,31 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(int offset, int address) { - // 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 == offset) { - break; - } - index++; + public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) { + if (!isStart) { + return; } - 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()); + //// 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), - SourceFormatter.FormatHexValue(address, 4), string.Empty); + SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); } // IGenerator diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 59d07f0..4c55bfc 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -484,9 +484,11 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(int offset, int address) { - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), - SourceFormatter.FormatHexValue(address, 4), string.Empty); + public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) { + if (isStart) { + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), + SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); + } } // IGenerator diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 5fcb6ba..84c43fb 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -102,11 +102,6 @@ namespace SourceGen.AsmGen { /// private StreamWriter mOutStream; - /// - /// If we output a ".logical", we will need a ".here" eventually. - /// - private bool mNeedHereOp; - /// /// What encoding are we currently set up for. /// @@ -115,10 +110,16 @@ namespace SourceGen.AsmGen { /// /// Output mode; determines how ORG is handled. /// - private enum TassOutputMode { + private enum OutputMode { Unknown = 0, Loadable = 1, Streamable = 2 } - private TassOutputMode mOutputMode; + private OutputMode mOutputMode; + + /// + /// Current pseudo-PC depth. 0 is the "real" PC. + /// + private int mPcDepth; + private bool mFirstIsOpen; /// /// Holds detected version of configured assembler. @@ -220,13 +221,13 @@ namespace SourceGen.AsmGen { // of offset +000002. bool hasPrgHeader = GenCommon.HasPrgHeader(project); int offAdj = hasPrgHeader ? 2 : 0; - int startAddr = project.AddrMap.Get(offAdj); + int startAddr = project.AddrMap.OffsetToAddress(offAdj); if (startAddr + project.FileDataLength - offAdj > 65536) { // Does not fit into memory at load address. - mOutputMode = TassOutputMode.Streamable; + mOutputMode = OutputMode.Streamable; mHasPrgHeader = false; } else { - mOutputMode = TassOutputMode.Loadable; + mOutputMode = OutputMode.Loadable; mHasPrgHeader = hasPrgHeader; } //Debug.WriteLine("startAddr=$" + startAddr.ToString("x6") + @@ -298,6 +299,9 @@ namespace SourceGen.AsmGen { (needLongAddress ? AsmTass64.LONG_ADDRESS : string.Empty) + (mHasPrgHeader ? string.Empty : AsmTass64.NOSTART); + mPcDepth = 0; + mFirstIsOpen = true; + // Use UTF-8 encoding, without a byte-order mark. using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) { mOutStream = sw; @@ -309,11 +313,6 @@ namespace SourceGen.AsmGen { } GenCommon.Generate(this, sw, worker); - - if (mNeedHereOp) { - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(HERE_PSEUDO_OP), - string.Empty, string.Empty); - } } mOutStream = null; @@ -654,34 +653,58 @@ namespace SourceGen.AsmGen { } // IGenerator - public void OutputOrgDirective(int offset, int address) { + public void OutputOrgDirective(AddressMap.AddressMapEntry 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 - // the output file, having a distinct compile offset isn't very useful. We want + // the output file, having a distinct compile offset isn't useful here. We want // to set it once before the first line of code, then leave it alone. // // Any subsequent ORG changes are made to the program counter, and take the form - // of a pair of ops (.logical to open, .here to end). Omitting the .here + // of a pair of ops (".logical " to open, ".here" to end). Omitting the .here // causes an error. // // If this is a "streamable" file, meaning it won't actually load into 64K of RAM // without wrapping around, then we skip the "* = addr" (same as "* = 0") and just // start with ".logical" segments. - Debug.Assert(offset >= StartOffset); - if (offset == StartOffset && mOutputMode == TassOutputMode.Loadable) { - // Set the "compile offset" to the initial address. - OutputLine("*", "=", - SourceFormatter.FormatHexValue(Project.AddrMap.Get(StartOffset), 4), - string.Empty); - } else { - if (mNeedHereOp) { - OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(HERE_PSEUDO_OP), - string.Empty, string.Empty); + // + // The assembler's approach is best represented by having an address region that + // spans the entire file, with one or more "logical" regions inside. In practice + // (especially for multi-bank 65816 code) that may not be the case, but the + // assembler is still expecting us to start with a "* =" and then fit everything + // inside that. So we treat the first region specially, whether or not it wraps + // the rest of the file. + Debug.Assert(mPcDepth >= 0); + if (isStart) { + if (mPcDepth == 0 && mFirstIsOpen) { + mPcDepth++; + + // Set the "real" PC for the first address change. If we're in "loadable" + // mode, just set "*=". If we're in "streaming" mode, we set "*=" to zero + // and then use a pseudo-PC. + if (mOutputMode == OutputMode.Loadable) { + OutputLine("*", "=", + SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); + return; + } else { + // Set the real PC to address zero to ensure we get a full 64KB. The + // assembler assumes this as a default, so it can be omitted. + //OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); + } } OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), - SourceFormatter.FormatHexValue(address, 4), string.Empty); - mNeedHereOp = true; + 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), + string.Empty, string.Empty); + } else { + // mark initial "*=" region as closed, but don't output anything + mFirstIsOpen = false; + } } } diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs index 8a55161..839957e 100644 --- a/SourceGen/AsmGen/GenCommon.cs +++ b/SourceGen/AsmGen/GenCommon.cs @@ -51,6 +51,14 @@ namespace SourceGen.AsmGen { int lastProgress = 0; + // Create an address map iterator and advance it to match gen.StartOffset. + IEnumerator addrIter = proj.AddrMap.AddressChangeIterator; + while (addrIter.MoveNext()) { + if (addrIter.Current.IsStart && addrIter.Current.Offset >= offset) { + break; + } + } + while (offset < proj.FileData.Length) { Anattrib attr = proj.GetAnattrib(offset); @@ -68,10 +76,12 @@ namespace SourceGen.AsmGen { } } - // Check for address change. - int orgAddr = proj.AddrMap.Get(offset); - if (orgAddr >= 0) { - gen.OutputOrgDirective(offset, orgAddr); + // 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); + addrIter.MoveNext(); + change = addrIter.Current; } List lvars = lvLookup.GetVariablesDefinedAtOffset(offset); @@ -132,6 +142,20 @@ namespace SourceGen.AsmGen { //System.Threading.Thread.Sleep(500); } } + + // 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.Entry, change.IsStart); + addrIter.MoveNext(); + change = addrIter.Current; + } } private static void GenerateHeader(IGenerator gen, StreamWriter sw) { @@ -533,14 +557,25 @@ namespace SourceGen.AsmGen { //Debug.WriteLine("PRG test: +0/1 has label"); return false; } + // The first part of the address map should be a two-byte region, either added + // explicitly or a hole left at the start of the file. Address doesn't matter. + IEnumerator iter = project.AddrMap.AddressChangeIterator; + if (!iter.MoveNext()) { + Debug.Assert(false); + return false; + } + AddressMap.AddressChange change = iter.Current; + if (change.Entry.Length != 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.Get(2) == AddressMap.NO_ENTRY_ADDR) { + if (project.AddrMap.GetRegions(0x000002).Count == 0) { //Debug.WriteLine("PRG test: no ORG at +2"); return false; } // See if the address at offset 2 matches the value at 0/1. int value01 = project.FileData[0] | (project.FileData[1] << 8); - int addr2 = project.AddrMap.OffsetToAddress(2); + int addr2 = project.AddrMap.OffsetToAddress(0x000002); if (value01 != addr2) { //Debug.WriteLine("PRG test: +0/1 value is " + value01.ToString("x4") + // ", address at +2 is " + addr2); diff --git a/SourceGen/AsmGen/IGenerator.cs b/SourceGen/AsmGen/IGenerator.cs index 2269a75..249498b 100644 --- a/SourceGen/AsmGen/IGenerator.cs +++ b/SourceGen/AsmGen/IGenerator.cs @@ -168,9 +168,9 @@ namespace SourceGen.AsmGen { /// /// Outputs a code origin directive. /// - /// Offset of code targeted to new address. - /// 24-bit address. - void OutputOrgDirective(int offset, int address); + /// Address map entry object. + /// True if we're outputing a region-start directive. + void OutputOrgDirective(CommonUtil.AddressMap.AddressMapEntry addrEntry, bool isStart); /// /// Notify the assembler of a change in register width. diff --git a/SourceGen/CodeAnalysis.cs b/SourceGen/CodeAnalysis.cs index 82be70a..c1a1571 100644 --- a/SourceGen/CodeAnalysis.cs +++ b/SourceGen/CodeAnalysis.cs @@ -355,13 +355,20 @@ namespace SourceGen { /// Sets the address for every byte in the input. /// private void SetAddresses() { - // The AddressMap will have at least one entry, will start at offset 0, and - // will exactly span the file. - foreach (AddressMap.AddressMapEntry ent in mAddrMap) { - int addr = ent.Addr; - for (int i = ent.Offset; i < ent.Offset + ent.Length; i++) { - mAnattribs[i].Address = addr++; + IEnumerator addrIter = mAddrMap.AddressChangeIterator; + addrIter.MoveNext(); + int addr = 0; + + for (int offset = 0; offset < mAnattribs.Length; offset++) { + // Process all change events at this offset. + AddressMap.AddressChange change = addrIter.Current; + while (change != null && change.Offset == offset) { + addr = change.Address; + addrIter.MoveNext(); + change = addrIter.Current; } + + mAnattribs[offset].Address = addr++; } } @@ -1105,7 +1112,7 @@ namespace SourceGen { " label='" + label + "'; file length is" + mFileData.Length); } - if (!mAddrMap.IsSingleAddrRange(offset, length)) { + if (!mAddrMap.IsRangeUnbroken(offset, length)) { LogW(offset, "SIDF: format crosses address map boundary (len=" + length + ")"); return false; } @@ -1362,7 +1369,7 @@ namespace SourceGen { dbrChanges[kvp.Key] = kvp.Value; } - // Create an array for fast access. + // Create a full-file array for fast access. short[] bval = new short[mAnattribs.Length]; Misc.Memset(bval, DbrValue.UNKNOWN); foreach (KeyValuePair kvp in dbrChanges) { @@ -1370,12 +1377,17 @@ namespace SourceGen { } // Run through file, updating instructions as needed. - short curVal = (byte)(mAddrMap.Get(0) >> 16); // start with B=K + // TODO(org): this is wrong if the file starts with a non-addr region; can walk + // through anattribs and set to mAnattribs[].Address of first instruction; maybe + // just init to DbrValue.UNKNOWN and set it inside the loop on first relevant instr + int firstAddr = mAddrMap.OffsetToAddress(0); + short curVal = (byte)(firstAddr >> 16); // start with B=K for (int offset = 0; offset < mAnattribs.Length; offset++) { if (bval[offset] != DbrValue.UNKNOWN) { curVal = bval[offset]; } if (!mAnattribs[offset].UsesDataBankReg) { + // Not a relevant instruction, move on to next. continue; } Debug.Assert(mAnattribs[offset].IsInstructionStart); diff --git a/SourceGen/DataAnalysis.cs b/SourceGen/DataAnalysis.cs index e001d27..41272be 100644 --- a/SourceGen/DataAnalysis.cs +++ b/SourceGen/DataAnalysis.cs @@ -682,7 +682,7 @@ namespace SourceGen { // Check to see if we just crossed an address change. if (offset < mAnattribs.Length && - !mProject.AddrMap.IsSingleAddrRange(offset - 1, 2)) { + !mProject.AddrMap.IsRangeUnbroken(offset - 1, 2)) { // Must be an ORG here. End region and scan. AnalyzeRange(startOffset, offset - 1); startOffset = -1; diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index deaf04a..250d9e1 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -270,7 +270,8 @@ namespace SourceGen { ProjectPathName = string.Empty; AddrMap = new AddressMap(fileDataLen); - AddrMap.Set(0, 0x1000); // default load address to $1000; override later + // set default load address to $1000; override later + AddrMap.AddRegion(0x000000, fileDataLen, 0x1000, false); // Default value is "no tag". AnalyzerTags = new CodeAnalysis.AnalyzerTag[fileDataLen]; @@ -387,6 +388,7 @@ namespace SourceGen { ProjectProps.EntryFlags = SystemDefaults.GetEntryFlags(sysDef); // Configure the load address. + AddrMap.Clear(); if (SystemDefaults.GetFirstWordIsLoadAddr(sysDef) && mFileData.Length > 2) { // First two bytes are the load address, with the actual file data starting // at +000002. We need to assign an address to the bytes, but don't want them @@ -396,8 +398,13 @@ namespace SourceGen { // So we just give it an offset of (start - 2) and leave it to the user to // update if necessary. int loadAddr = RawData.GetWord(mFileData, 0, 2, false); - AddrMap.Set(0, loadAddr < 2 ? 0 : loadAddr - 2); - AddrMap.Set(2, loadAddr); + // TODO(org): use NON_ADDR for first two bytes + AddressMap.AddResult addRes = + AddrMap.AddRegion(0, 2, loadAddr < 2 ? 0 : loadAddr - 2, false); + Debug.Assert(addRes == AddressMap.AddResult.Okay); + addRes = AddrMap.AddRegion(2, mFileData.Length - 2, loadAddr, false); + Debug.Assert(addRes == AddressMap.AddResult.Okay); + OperandFormats[0] = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE, FormatDescriptor.SubType.None); Comments[0] = Res.Strings.LOAD_ADDRESS; @@ -405,7 +412,9 @@ namespace SourceGen { AnalyzerTags[2] = CodeAnalysis.AnalyzerTag.Code; } else { int loadAddr = SystemDefaults.GetLoadAddress(sysDef); - AddrMap.Set(0, loadAddr); + AddressMap.AddResult addRes = + AddrMap.AddRegion(0, mFileData.Length, loadAddr, false); + Debug.Assert(addRes == AddressMap.AddResult.Okay); } foreach (string str in sysDef.SymbolFiles) { @@ -750,11 +759,16 @@ namespace SourceGen { /// Checks to see if any part of the address map runs across a bank boundary. /// private void ValidateAddressMap() { - foreach (AddressMap.AddressMapEntry entry in AddrMap) { - if ((entry.Addr & 0xff0000) != ((entry.Addr + entry.Length - 1) & 0xff0000)) { + // Use the change list, because the region list can have "floating" length values. + IEnumerator addrIter = AddrMap.AddressChangeIterator; + while (addrIter.MoveNext()) { + AddressMap.AddressChange change = addrIter.Current; + AddressMap.AddressMapEntry entry = change.Entry; + if (change.IsStart && + (entry.Address & 0xff0000) != ((entry.Address + entry.Length - 1) & 0xff0000)) { string fmt = Res.Strings.MSG_BANK_OVERRUN_DETAIL_FMT; - int firstNext = (entry.Addr & 0xff0000) + 0x010000; - int badOffset = entry.Offset + (firstNext - entry.Addr); + int firstNext = (entry.Address & 0xff0000) + 0x010000; + int badOffset = entry.Offset + (firstNext - entry.Address); Messages.Add(new MessageList.MessageEntry( MessageList.MessageEntry.SeverityLevel.Error, entry.Offset, @@ -1035,7 +1049,7 @@ namespace SourceGen { continue; } - if (!AddrMap.IsSingleAddrRange(offset, dfd.Length)) { + if (!AddrMap.IsRangeUnbroken(offset, dfd.Length)) { string msg = "descriptor straddles address change; len=" + dfd.Length; genLog.LogE("+" + offset.ToString("x6") + ": " + msg); Messages.Add(new MessageList.MessageEntry( @@ -2130,13 +2144,27 @@ namespace SourceGen { //} break; case UndoableChange.ChangeType.SetAddress: { + // TODO(org): rewrite this + AddressMap addrMap = AddrMap; - if (addrMap.Get(offset) != (int)oldValue) { - Debug.WriteLine("GLITCH: old address value mismatch (" + - addrMap.Get(offset) + " vs " + (int)oldValue + ")"); - Debug.Assert(false); + if ((int)oldValue == AddressMap.NON_ADDR) { + // adding new entry + if (addrMap.AddRegion(offset, AddressMap.FLOATING_LEN, + (int)newValue, false) != 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)) { + Debug.Assert(false, "failed removing region"); + } + } else { + // updating existing entry + if (!addrMap.EditRegion(offset, AddressMap.FLOATING_LEN, + (int) newValue, false)) { + Debug.Assert(false, "failed editing region"); + } } - addrMap.Set(offset, (int)newValue); Debug.WriteLine("Map offset +" + offset.ToString("x6") + " to $" + ((int)newValue).ToString("x6")); diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 7fd99d3..5f32516 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -1194,7 +1194,7 @@ namespace SourceGen { } Line topLine = lines[index]; Line newLine = new Line(topLine.FileOffset, 0, Line.Type.OrgDirective); - string addrStr = mFormatter.FormatHexValue(ent.Addr, 4); + string addrStr = mFormatter.FormatHexValue(ent.Address, 4); newLine.Parts = FormattedParts.CreateDirective( mFormatter.FormatPseudoOp(mPseudoOpNames.OrgDirective), addrStr); lines.Insert(index, newLine); diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index ed6053e..f49f631 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -259,6 +259,8 @@ namespace SourceGen { /// to this point so we can report fatal errors directly to the user. /// public void WindowLoaded() { + // Run library unit tests. + Debug.Assert(CommonUtil.AddressMap.Test()); Debug.Assert(CommonUtil.RangeSet.Test()); Debug.Assert(CommonUtil.TypedRangeSet.Test()); Debug.Assert(CommonUtil.Version.Test()); @@ -1803,12 +1805,16 @@ namespace SourceGen { int lastOffset = CodeLineList[lastIndex].FileOffset; int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan; int nextAddr; + 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 may not be a single *line*) is selected, or the + // 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.NO_ENTRY_ADDR; + 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 @@ -1829,7 +1835,7 @@ namespace SourceGen { nextAddr = cloneMap.OffsetToAddress(nextOffset); } #else - int fileStartAddr = mProject.AddrMap.OffsetToAddress(0); + int fileStartAddr = addrMap.OffsetToAddress(0); nextAddr = ((fileStartAddr + nextOffset) & 0xffff) | (fileStartAddr & 0xff0000); #endif } @@ -1849,37 +1855,40 @@ namespace SourceGen { ChangeSet cs = new ChangeSet(1); - if (mProject.AddrMap.Get(firstOffset) != dlg.NewAddress) { - // Added / removed / changed existing entry. - // - // We allow creation of an apparently redundant address override, because - // sometimes it's helpful to add one to "anchor" an area before relocating - // something that appears earlier in the file. - int prevAddress = mProject.AddrMap.Get(firstOffset); - UndoableChange uc = UndoableChange.CreateAddressChange(firstOffset, - prevAddress, dlg.NewAddress); - cs.Add(uc); - Debug.WriteLine("EditAddress: changing addr at offset +" + - firstOffset.ToString("x6") + " to $" + dlg.NewAddress.ToString("x4")); - } + // TODO(org): I'm just commenting this out for now; needs to be totally redone + + + //if (addrMap.Get(firstOffset) != dlg.NewAddress) { + // // Added / removed / changed existing entry. + // // + // // We allow creation of an apparently redundant address override, because + // // sometimes it's helpful to add one to "anchor" an area before relocating + // // something that appears earlier in the file. + // int prevAddress = addrMap.Get(firstOffset); + // UndoableChange uc = UndoableChange.CreateAddressChange(firstOffset, + // prevAddress, dlg.NewAddress); + // cs.Add(uc); + // Debug.WriteLine("EditAddress: changing addr at offset +" + + // firstOffset.ToString("x6") + " to $" + dlg.NewAddress.ToString("x4")); + //} // We want to create an entry for the chunk that follows the selected area. // We don't modify the trailing address if an entry already exists. // (Note the "can edit" code prevented us from being called if there's an // address map entry in the middle of the selected area.) // - // If they're removing an existing entry, don't add a new entry at the end. - if (nextAddr >= 0 && dlg.NewAddress != AddressMap.NO_ENTRY_ADDR && - mProject.AddrMap.Get(nextOffset) == AddressMap.NO_ENTRY_ADDR) { - // We don't screen for redundant entries here. That should only happen if - // they select a range and then don't change the address. Maybe it's useful? - int prevAddress = mProject.AddrMap.Get(nextOffset); - UndoableChange uc = UndoableChange.CreateAddressChange(nextOffset, - prevAddress, nextAddr); - cs.Add(uc); - Debug.WriteLine("EditAddress: setting trailing addr at offset +" + - nextOffset.ToString("x6") + " to $" + nextAddr.ToString("x4")); - } + //// If they're removing an existing entry, don't add a new entry at the end. + //if (nextAddr >= 0 && dlg.NewAddress != AddressMap.NO_ENTRY_ADDR && + // addrMap.Get(nextOffset) == AddressMap.NO_ENTRY_ADDR) { + // // We don't screen for redundant entries here. That should only happen if + // // they select a range and then don't change the address. Maybe it's useful? + // int prevAddress = addrMap.Get(nextOffset); + // UndoableChange uc = UndoableChange.CreateAddressChange(nextOffset, + // prevAddress, nextAddr); + // cs.Add(uc); + // Debug.WriteLine("EditAddress: setting trailing addr at offset +" + + // nextOffset.ToString("x6") + " to $" + nextAddr.ToString("x4")); + //} if (cs.Count > 0) { ApplyUndoableChanges(cs); @@ -2614,7 +2623,7 @@ namespace SourceGen { // This must match what GroupedOffsetSetFromSelected() does. if (!mProject.UserLabels.ContainsKey(nextOffset) && !mProject.HasCommentNoteOrVis(nextOffset) && - mProject.AddrMap.IsSingleAddrRange(nextOffset - 1, 2)) { + mProject.AddrMap.IsRangeUnbroken(nextOffset - 1, 2)) { // Good to go. Debug.WriteLine("Grabbing second byte from +" + nextOffset.ToString("x6")); trs.Add(nextOffset, rng.Type); @@ -3606,7 +3615,7 @@ namespace SourceGen { // + expectedAddr.ToString("x4")); expectedAddr = attr.Address; groupNum++; - } else if (offset > 0 && !mProject.AddrMap.IsSingleAddrRange(offset - 1, 2)) { + } else if (offset > 0 && !mProject.AddrMap.IsRangeUnbroken(offset - 1, 2)) { // Was the previous byte in a different address range? This is only // strictly necessary if the previous byte was in the selection set (which // it won't be if the selection starts at the beginning of an address diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs index e507d1d..930ceb7 100644 --- a/SourceGen/ProjectFile.cs +++ b/SourceGen/ProjectFile.cs @@ -246,7 +246,7 @@ namespace SourceGen { public SerAddressMap() { } public SerAddressMap(AddressMap.AddressMapEntry ent) { Offset = ent.Offset; - Addr = ent.Addr; + Addr = ent.Address; } } public class SerTypeHintRange { @@ -668,8 +668,20 @@ namespace SourceGen { } // Deserialize address map. + proj.AddrMap.Clear(); foreach (SerAddressMap addr in spf.AddressMap) { - proj.AddrMap.Set(addr.Offset, addr.Addr); + // TODO(org): serialize length and isRelative + int length = CommonUtil.AddressMap.FLOATING_LEN; + + AddressMap.AddResult addResult = proj.AddrMap.AddRegion(addr.Offset, + length, addr.Addr, false); + if (addResult != CommonUtil.AddressMap.AddResult.Okay) { + string msg = "off=+" + addr.Offset.ToString("x6") + " addr=$" + + addr.Addr.ToString("x4") + " len=" + + (length == CommonUtil.AddressMap.FLOATING_LEN ? "(floating)" : length.ToString()); + string errMsg = string.Format(Res.Strings.ERR_BAD_ADDRESS_REGION_FMT, msg); + report.Add(FileLoadItem.Type.Warning, errMsg); + } } // Deserialize analyzer tags (formerly known as "type hints"). The default value diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml index 9ba1846..a81acba 100644 --- a/SourceGen/Res/Strings.xaml +++ b/SourceGen/Res/Strings.xaml @@ -47,6 +47,7 @@ limitations under the License. addr const stkrl + Bad address region {0} Unknown I/O direction in symbol Bad format descriptor at +{0:x6}. Bad format descriptor type diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs index 49ab7d7..764cfb1 100644 --- a/SourceGen/Res/Strings.xaml.cs +++ b/SourceGen/Res/Strings.xaml.cs @@ -75,6 +75,8 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_EquConstant"); public static string EQU_STACK_RELATIVE = (string)Application.Current.FindResource("str_EquStackRelative"); + public static string ERR_BAD_ADDRESS_REGION_FMT = + (string)Application.Current.FindResource("str_ErrBadAddressRegionFmt"); public static string ERR_BAD_DEF_SYMBOL_DIR = (string)Application.Current.FindResource("str_ErrBadDefSymbolDir"); public static string ERR_BAD_FD_FMT = diff --git a/SourceGen/SGTestData/Expected/10000-allops-value-6502_cc65.S b/SourceGen/SGTestData/Expected/10000-allops-value-6502_cc65.S index 14e3c85..7273ec5 100644 --- a/SourceGen/SGTestData/Expected/10000-allops-value-6502_cc65.S +++ b/SourceGen/SGTestData/Expected/10000-allops-value-6502_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502X" -; .segment "SEG000" .org $1000 jsr L1035 jsr L1038 diff --git a/SourceGen/SGTestData/Expected/10000-allops-value-6502_cc65.cfg b/SourceGen/SGTestData/Expected/10000-allops-value-6502_cc65.cfg index 2df3772..20e466d 100644 --- a/SourceGen/SGTestData/Expected/10000-allops-value-6502_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10000-allops-value-6502_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10000-allops-value-6502 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=598; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10001-allops-value-65C02_cc65.S b/SourceGen/SGTestData/Expected/10001-allops-value-65C02_cc65.S index df5bf26..1c7be6a 100644 --- a/SourceGen/SGTestData/Expected/10001-allops-value-65C02_cc65.S +++ b/SourceGen/SGTestData/Expected/10001-allops-value-65C02_cc65.S @@ -1,5 +1,4 @@ .setcpu "65C02" -; .segment "SEG000" .org $1000 jsr L1014 jsr L108A diff --git a/SourceGen/SGTestData/Expected/10001-allops-value-65C02_cc65.cfg b/SourceGen/SGTestData/Expected/10001-allops-value-65C02_cc65.cfg index b828bb6..cdeec22 100644 --- a/SourceGen/SGTestData/Expected/10001-allops-value-65C02_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10001-allops-value-65C02_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10001-allops-value-65C02 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=489; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10002-allops-value-65816_cc65.S b/SourceGen/SGTestData/Expected/10002-allops-value-65816_cc65.S index 60166b9..184c706 100644 --- a/SourceGen/SGTestData/Expected/10002-allops-value-65816_cc65.S +++ b/SourceGen/SGTestData/Expected/10002-allops-value-65816_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/10002-allops-value-65816_cc65.cfg b/SourceGen/SGTestData/Expected/10002-allops-value-65816_cc65.cfg index ec0095d..85fb414 100644 --- a/SourceGen/SGTestData/Expected/10002-allops-value-65816_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10002-allops-value-65816_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10002-allops-value-65816 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=588; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10003-allops-value-W65C02_cc65.S b/SourceGen/SGTestData/Expected/10003-allops-value-W65C02_cc65.S index d5f55b6..e168a09 100644 --- a/SourceGen/SGTestData/Expected/10003-allops-value-W65C02_cc65.S +++ b/SourceGen/SGTestData/Expected/10003-allops-value-W65C02_cc65.S @@ -1,5 +1,4 @@ .setcpu "65C02" -; .segment "SEG000" .org $1000 jsr L1017 jsr L1099 diff --git a/SourceGen/SGTestData/Expected/10003-allops-value-W65C02_cc65.cfg b/SourceGen/SGTestData/Expected/10003-allops-value-W65C02_cc65.cfg index 20bd9fd..f780e84 100644 --- a/SourceGen/SGTestData/Expected/10003-allops-value-W65C02_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10003-allops-value-W65C02_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10003-allops-value-W65C02 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=541; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10010-allops-zero-6502_cc65.S b/SourceGen/SGTestData/Expected/10010-allops-zero-6502_cc65.S index c729141..6ff3a02 100644 --- a/SourceGen/SGTestData/Expected/10010-allops-zero-6502_cc65.S +++ b/SourceGen/SGTestData/Expected/10010-allops-zero-6502_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502X" -; .segment "SEG000" .org $1000 jsr L1035 jsr L1038 diff --git a/SourceGen/SGTestData/Expected/10010-allops-zero-6502_cc65.cfg b/SourceGen/SGTestData/Expected/10010-allops-zero-6502_cc65.cfg index 00f32ff..862feb7 100644 --- a/SourceGen/SGTestData/Expected/10010-allops-zero-6502_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10010-allops-zero-6502_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10010-allops-zero-6502 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=598; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10011-allops-zero-65C02_cc65.S b/SourceGen/SGTestData/Expected/10011-allops-zero-65C02_cc65.S index 20dd594..19fa055 100644 --- a/SourceGen/SGTestData/Expected/10011-allops-zero-65C02_cc65.S +++ b/SourceGen/SGTestData/Expected/10011-allops-zero-65C02_cc65.S @@ -1,5 +1,4 @@ .setcpu "65C02" -; .segment "SEG000" .org $1000 jsr L1014 jsr L108A diff --git a/SourceGen/SGTestData/Expected/10011-allops-zero-65C02_cc65.cfg b/SourceGen/SGTestData/Expected/10011-allops-zero-65C02_cc65.cfg index 99fa289..f2c0c7b 100644 --- a/SourceGen/SGTestData/Expected/10011-allops-zero-65C02_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10011-allops-zero-65C02_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10011-allops-zero-65C02 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=489; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10012-allops-zero-65816_cc65.S b/SourceGen/SGTestData/Expected/10012-allops-zero-65816_cc65.S index 2f974d0..63223ce 100644 --- a/SourceGen/SGTestData/Expected/10012-allops-zero-65816_cc65.S +++ b/SourceGen/SGTestData/Expected/10012-allops-zero-65816_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/10012-allops-zero-65816_cc65.cfg b/SourceGen/SGTestData/Expected/10012-allops-zero-65816_cc65.cfg index 7f7009f..8e18bce 100644 --- a/SourceGen/SGTestData/Expected/10012-allops-zero-65816_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10012-allops-zero-65816_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10012-allops-zero-65816 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=588; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10013-allops-zero-W65C02_cc65.S b/SourceGen/SGTestData/Expected/10013-allops-zero-W65C02_cc65.S index c2ab767..7efe5f2 100644 --- a/SourceGen/SGTestData/Expected/10013-allops-zero-W65C02_cc65.S +++ b/SourceGen/SGTestData/Expected/10013-allops-zero-W65C02_cc65.S @@ -1,5 +1,4 @@ .setcpu "65C02" -; .segment "SEG000" .org $1000 jsr L1017 jsr L1099 diff --git a/SourceGen/SGTestData/Expected/10013-allops-zero-W65C02_cc65.cfg b/SourceGen/SGTestData/Expected/10013-allops-zero-W65C02_cc65.cfg index 4369149..ac5a7f5 100644 --- a/SourceGen/SGTestData/Expected/10013-allops-zero-W65C02_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10013-allops-zero-W65C02_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10013-allops-zero-W65C02 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=541; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10020-embedded-instructions_cc65.S b/SourceGen/SGTestData/Expected/10020-embedded-instructions_cc65.S index 0594f42..79b114b 100644 --- a/SourceGen/SGTestData/Expected/10020-embedded-instructions_cc65.S +++ b/SourceGen/SGTestData/Expected/10020-embedded-instructions_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502X" -; .segment "SEG000" .org $1000 jsr L100F jsr L1017 diff --git a/SourceGen/SGTestData/Expected/10020-embedded-instructions_cc65.cfg b/SourceGen/SGTestData/Expected/10020-embedded-instructions_cc65.cfg index 7325f97..8117d20 100644 --- a/SourceGen/SGTestData/Expected/10020-embedded-instructions_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10020-embedded-instructions_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10020-embedded-instructions MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=82; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10022-embedded-instructions_cc65.S b/SourceGen/SGTestData/Expected/10022-embedded-instructions_cc65.S index 50dbefe..126fdd3 100644 --- a/SourceGen/SGTestData/Expected/10022-embedded-instructions_cc65.S +++ b/SourceGen/SGTestData/Expected/10022-embedded-instructions_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/10022-embedded-instructions_cc65.cfg b/SourceGen/SGTestData/Expected/10022-embedded-instructions_cc65.cfg index 2ae844f..33f24de 100644 --- a/SourceGen/SGTestData/Expected/10022-embedded-instructions_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10022-embedded-instructions_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10022-embedded-instructions MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=33; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10030-flags-and-branches_cc65.S b/SourceGen/SGTestData/Expected/10030-flags-and-branches_cc65.S index e164f6d..67d8f49 100644 --- a/SourceGen/SGTestData/Expected/10030-flags-and-branches_cc65.S +++ b/SourceGen/SGTestData/Expected/10030-flags-and-branches_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502X" -; .segment "SEG000" .org $1000 clv cld diff --git a/SourceGen/SGTestData/Expected/10030-flags-and-branches_cc65.cfg b/SourceGen/SGTestData/Expected/10030-flags-and-branches_cc65.cfg index d2fa51d..2a5b723 100644 --- a/SourceGen/SGTestData/Expected/10030-flags-and-branches_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10030-flags-and-branches_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10030-flags-and-branches MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=193; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10032-flags-and-branches_cc65.S b/SourceGen/SGTestData/Expected/10032-flags-and-branches_cc65.S index 890bde6..6da02a4 100644 --- a/SourceGen/SGTestData/Expected/10032-flags-and-branches_cc65.S +++ b/SourceGen/SGTestData/Expected/10032-flags-and-branches_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/10032-flags-and-branches_cc65.cfg b/SourceGen/SGTestData/Expected/10032-flags-and-branches_cc65.cfg index fd70fc3..6d89600 100644 --- a/SourceGen/SGTestData/Expected/10032-flags-and-branches_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10032-flags-and-branches_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10032-flags-and-branches MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=180; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/10040-data-recognition_cc65.S b/SourceGen/SGTestData/Expected/10040-data-recognition_cc65.S index e862851..674b6ac 100644 --- a/SourceGen/SGTestData/Expected/10040-data-recognition_cc65.S +++ b/SourceGen/SGTestData/Expected/10040-data-recognition_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502X" -; .segment "SEG000" .org $1000 lda L10AC ora L10BC diff --git a/SourceGen/SGTestData/Expected/10040-data-recognition_cc65.cfg b/SourceGen/SGTestData/Expected/10040-data-recognition_cc65.cfg index 5553474..7cb6cf4 100644 --- a/SourceGen/SGTestData/Expected/10040-data-recognition_cc65.cfg +++ b/SourceGen/SGTestData/Expected/10040-data-recognition_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 10040-data-recognition MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=196; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S b/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S index 5912ce9..69ed4b0 100644 --- a/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S +++ b/SourceGen/SGTestData/Expected/20000-numeric-types_acme.S @@ -1,8 +1,7 @@ ;Project file was edited to get all big-endian data types, and to have an ;incorrect .junk alignment directive. !cpu 6502 -* = $0000 - !pseudopc $1000 { +* = $1000 bit L1448 jsr L14A8 nop @@ -61,7 +60,6 @@ LABEL !hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff !fill 2,$dd ;incorrect alignment !align 255,0,$00 !fill 8,$82 - } ;!pseudopc !pseudopc $1408 { !fill 8,$82 ;note no-op .ORG !fill 8,$83 diff --git a/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.S b/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.S index 32edd7f..65ad205 100644 --- a/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.S +++ b/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.S @@ -1,7 +1,6 @@ ;Project file was edited to get all big-endian data types, and to have an ;incorrect .junk alignment directive. .setcpu "6502" -; .segment "SEG000" .org $1000 bit L1448 jsr L14A8 @@ -63,11 +62,9 @@ LABEL: .byte $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$aa,$bb,$cc,$dd,$ee,$ff .res 2,$dd ;incorrect alignment .res 140,$00 .res 8,$82 -; .segment "SEG001" .org $1408 .res 8,$82 ;note no-op .ORG .res 8,$83 -; .segment "SEG002" .org $1428 .res 8,$83 ;meaningful .ORG .res 8,$84 diff --git a/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.cfg b/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.cfg index 5aa65fb..abf8cab 100644 --- a/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20000-numeric-types_cc65.cfg @@ -1,15 +1,9 @@ # 6502bench SourceGen generated linker script for 20000-numeric-types MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=1032; -# MEM001: file=%O, start=$1408, size=16; -# MEM002: file=%O, start=$1428, size=152; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20010-string-types_cc65.S b/SourceGen/SGTestData/Expected/20010-string-types_cc65.S index 83801a6..99490fe 100644 --- a/SourceGen/SGTestData/Expected/20010-string-types_cc65.S +++ b/SourceGen/SGTestData/Expected/20010-string-types_cc65.S @@ -1,6 +1,5 @@ ;Project file was edited to get zero-length strings and reverse DCI. .setcpu "6502" -; .segment "SEG000" .org $1000 rts diff --git a/SourceGen/SGTestData/Expected/20010-string-types_cc65.cfg b/SourceGen/SGTestData/Expected/20010-string-types_cc65.cfg index de91b84..7c82594 100644 --- a/SourceGen/SGTestData/Expected/20010-string-types_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20010-string-types_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20010-string-types MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=3132; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20020-operand-formats_cc65.S b/SourceGen/SGTestData/Expected/20020-operand-formats_cc65.S index c967835..d4aa968 100644 --- a/SourceGen/SGTestData/Expected/20020-operand-formats_cc65.S +++ b/SourceGen/SGTestData/Expected/20020-operand-formats_cc65.S @@ -1,6 +1,5 @@ ;Project file was edited to force ASCII formatting for some operands. .setcpu "6502" -; .segment "SEG000" .org $1000 lda $01 lda $0102 diff --git a/SourceGen/SGTestData/Expected/20020-operand-formats_cc65.cfg b/SourceGen/SGTestData/Expected/20020-operand-formats_cc65.cfg index b8edb7a..843d189 100644 --- a/SourceGen/SGTestData/Expected/20020-operand-formats_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20020-operand-formats_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20020-operand-formats MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=165; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20022-operand-formats_cc65.S b/SourceGen/SGTestData/Expected/20022-operand-formats_cc65.S index 4899dd9..8cc699c 100644 --- a/SourceGen/SGTestData/Expected/20022-operand-formats_cc65.S +++ b/SourceGen/SGTestData/Expected/20022-operand-formats_cc65.S @@ -1,6 +1,5 @@ ;Project file was edited to force ASCII formatting for some operands. .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/20022-operand-formats_cc65.cfg b/SourceGen/SGTestData/Expected/20022-operand-formats_cc65.cfg index 408a655..bb39eaa 100644 --- a/SourceGen/SGTestData/Expected/20022-operand-formats_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20022-operand-formats_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20022-operand-formats MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=62; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20030-labels-and-symbols_acme.S b/SourceGen/SGTestData/Expected/20030-labels-and-symbols_acme.S index 5cc0712..34d52e5 100644 --- a/SourceGen/SGTestData/Expected/20030-labels-and-symbols_acme.S +++ b/SourceGen/SGTestData/Expected/20030-labels-and-symbols_acme.S @@ -9,8 +9,7 @@ absh = $feed plataddr = $3000 ;address only in platform file projalsa = $3200 ;same val as projalso -* = $0000 - !pseudopc $2345 { +* = $2345 start lda #zip lda #zip+16 lda #zip-192 @@ -59,7 +58,6 @@ start lda #zip @L23A3 jmp @L1000_1 - } ;!pseudopc !pseudopc $1000 { @L1000_1 nop @L1000 nop diff --git a/SourceGen/SGTestData/Expected/20030-labels-and-symbols_cc65.S b/SourceGen/SGTestData/Expected/20030-labels-and-symbols_cc65.S index b9019ad..4d52898 100644 --- a/SourceGen/SGTestData/Expected/20030-labels-and-symbols_cc65.S +++ b/SourceGen/SGTestData/Expected/20030-labels-and-symbols_cc65.S @@ -9,7 +9,6 @@ absh = $feed plataddr = $3000 ;address only in platform file projalsa = $3200 ;same val as projalso -; .segment "SEG000" .org $2345 start: lda #zip lda #zip+16 @@ -59,7 +58,6 @@ start: lda #zip @L23A3: jmp @L1000_1 -; .segment "SEG001" .org $1000 @L1000_1: nop @L1000: nop diff --git a/SourceGen/SGTestData/Expected/20030-labels-and-symbols_cc65.cfg b/SourceGen/SGTestData/Expected/20030-labels-and-symbols_cc65.cfg index cf76195..866807b 100644 --- a/SourceGen/SGTestData/Expected/20030-labels-and-symbols_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20030-labels-and-symbols_cc65.cfg @@ -1,13 +1,9 @@ # 6502bench SourceGen generated linker script for 20030-labels-and-symbols MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$2345, size=97; -# MEM001: file=%O, start=$1000, size=355; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20032-labels-and-symbols_cc65.S b/SourceGen/SGTestData/Expected/20032-labels-and-symbols_cc65.S index 760d2d9..37ee728 100644 --- a/SourceGen/SGTestData/Expected/20032-labels-and-symbols_cc65.S +++ b/SourceGen/SGTestData/Expected/20032-labels-and-symbols_cc65.S @@ -11,7 +11,6 @@ thirty2 = $12345678 ;32-bit constant test plataddr = $3000 ;address only in platform file projalsa = $3200 ;same val as projalso -; .segment "SEG000" .org $012345 .a8 .i8 @@ -126,7 +125,6 @@ start: clc @nextchunk: jml @L1000_1 -; .segment "SEG001" .org $1000 @L1000_1: nop @L1000: nop diff --git a/SourceGen/SGTestData/Expected/20032-labels-and-symbols_cc65.cfg b/SourceGen/SGTestData/Expected/20032-labels-and-symbols_cc65.cfg index 91e5948..e812a66 100644 --- a/SourceGen/SGTestData/Expected/20032-labels-and-symbols_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20032-labels-and-symbols_cc65.cfg @@ -1,13 +1,9 @@ # 6502bench SourceGen generated linker script for 20032-labels-and-symbols MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$12345, size=279; -# MEM001: file=%O, start=$1000, size=416; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20040-address-changes_acme.S b/SourceGen/SGTestData/Expected/20040-address-changes_acme.S index 3aab4e6..cbc3bb0 100644 --- a/SourceGen/SGTestData/Expected/20040-address-changes_acme.S +++ b/SourceGen/SGTestData/Expected/20040-address-changes_acme.S @@ -1,9 +1,7 @@ !cpu 6502 -* = $0000 - !pseudopc $1000 { +* = $1000 !word $1000 ;PRG-style header - } ;!pseudopc !pseudopc $1000 { jsr L1100 jsr L1107 diff --git a/SourceGen/SGTestData/Expected/20040-address-changes_cc65.S b/SourceGen/SGTestData/Expected/20040-address-changes_cc65.S index 6ca2af2..332eba2 100644 --- a/SourceGen/SGTestData/Expected/20040-address-changes_cc65.S +++ b/SourceGen/SGTestData/Expected/20040-address-changes_cc65.S @@ -1,15 +1,12 @@ .setcpu "6502" -; .segment "SEG000" .org $1000 .word $1000 ;PRG-style header -; .segment "SEG001" .org $1000 jsr L1100 jsr L1107 jmp L2000 -; .segment "SEG002" .org $1100 L1100: bit L1100 L1103: lda #$11 @@ -18,7 +15,6 @@ L1107: ldy #$11 clv bvc L1103 -; .segment "SEG003" .org $1100 @L1100_0: bit @L1100_0 lda #$22 @@ -26,7 +22,6 @@ L1107: ldy #$11 ldy #$22 jmp @L1105 -; .segment "SEG004" .org $1100 @L1100_1: bit @L1100_1 lda #$33 @@ -35,20 +30,17 @@ L1107: ldy #$11 sec bcs @L1107_0 -; .segment "SEG005" .org $2000 L2000: bit L2000 beq $2018 bne @L2020 -; .segment "SEG006" .org $2020 @L2020: bit @L2020 beq $2028 bne L2080 offend: nop -; .segment "SEG007" .org $2080 L2080: bit L2080 lda offend @@ -63,22 +55,18 @@ L2080: bit L2080 beq @L2100 .byte $ad -; .segment "SEG008" .org $2100 @L2100: nop nop jmp @L3000 -; .segment "SEG009" .org $2800 .byte $00 .byte $28 .res 14,$00 -; .segment "SEG010" .org $2820 .res 18,$00 -; .segment "SEG011" .org $3000 @L3000: bit @L3000 lda #$44 @@ -88,7 +76,6 @@ L2080: bit L2080 ulabel: .byte $00 .byte $01 -; .segment "SEG012" .org $3100 .byte $02 @@ -101,7 +88,6 @@ fwd: bit fwd beq @L3182 .byte $ea .byte $ea -; .segment "SEG013" .org $3180 .byte $00 .byte $01 @@ -116,7 +102,6 @@ fwd: bit fwd label1: .byte $ea .byte $ea -; .segment "SEG014" .org $3200 L3200: bit L3200 .byte $00 diff --git a/SourceGen/SGTestData/Expected/20040-address-changes_cc65.cfg b/SourceGen/SGTestData/Expected/20040-address-changes_cc65.cfg index 31120e0..6679fb2 100644 --- a/SourceGen/SGTestData/Expected/20040-address-changes_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20040-address-changes_cc65.cfg @@ -1,39 +1,9 @@ # 6502bench SourceGen generated linker script for 20040-address-changes MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=2; -# MEM001: file=%O, start=$1000, size=9; -# MEM002: file=%O, start=$1100, size=12; -# MEM003: file=%O, start=$1100, size=12; -# MEM004: file=%O, start=$1100, size=12; -# MEM005: file=%O, start=$2000, size=7; -# MEM006: file=%O, start=$2020, size=8; -# MEM007: file=%O, start=$2080, size=32; -# MEM008: file=%O, start=$2100, size=5; -# MEM009: file=%O, start=$2800, size=16; -# MEM010: file=%O, start=$2820, size=18; -# MEM011: file=%O, start=$3000, size=14; -# MEM012: file=%O, start=$3100, size=23; -# MEM013: file=%O, start=$3180, size=19; -# MEM014: file=%O, start=$3200, size=5; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; -# SEG003: load=MEM003, type=rw; -# SEG004: load=MEM004, type=rw; -# SEG005: load=MEM005, type=rw; -# SEG006: load=MEM006, type=rw; -# SEG007: load=MEM007, type=rw; -# SEG008: load=MEM008, type=rw; -# SEG009: load=MEM009, type=rw; -# SEG010: load=MEM010, type=rw; -# SEG011: load=MEM011, type=rw; -# SEG012: load=MEM012, type=rw; -# SEG013: load=MEM013, type=rw; -# SEG014: load=MEM014, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20042-address-changes_cc65.S b/SourceGen/SGTestData/Expected/20042-address-changes_cc65.S index d56d36a..af1cc11 100644 --- a/SourceGen/SGTestData/Expected/20042-address-changes_cc65.S +++ b/SourceGen/SGTestData/Expected/20042-address-changes_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $021000 .a8 .i8 @@ -10,7 +9,6 @@ jsr L21107 & $ffff jmp L22000 & $ffff -; .segment "SEG001" .org $021100 L21100: bit L21100 & $ffff L21103: lda #$11 @@ -19,7 +17,6 @@ L21107: ldy #$11 per L21103 bra L21103 -; .segment "SEG002" .org $021100 @L21100_0: bit @L21100_0 & $ffff lda #$22 @@ -28,7 +25,6 @@ L21107: ldy #$11 per @L21105 jmp @L21105 & $ffff -; .segment "SEG003" .org $021100 @L21100_1: bit @L21100_1 & $ffff lda #$33 @@ -37,20 +33,17 @@ L21107: ldy #$11 per @L21107_0 bra @L21107_0 -; .segment "SEG004" .org $022000 L22000: bit L22000 & $ffff beq $022018 bra @L22020 -; .segment "SEG005" .org $022020 @L22020: bit @L22020 & $ffff beq $022029 brl @L22080 @offend: nop -; .segment "SEG006" .org $022080 @L22080: bit @L22080 & $ffff lda @offend & $ffff @@ -65,22 +58,18 @@ L22000: bit L22000 & $ffff beq @L22100 .byte $ad -; .segment "SEG007" .org $022100 @L22100: nop nop jmp @L23000 & $ffff -; .segment "SEG008" .org $022800 .byte $00 .byte $28 .res 14,$00 -; .segment "SEG009" .org $022820 .res 18,$00 -; .segment "SEG010" .org $023000 @L23000: bit @L23000 & $ffff lda #$44 @@ -90,7 +79,6 @@ L22000: bit L22000 & $ffff @ulabel: .byte $00 .byte $01 -; .segment "SEG011" .org $023100 .byte $02 @@ -103,7 +91,6 @@ L22000: bit L22000 & $ffff beq @L23182 .byte $ea .byte $ea -; .segment "SEG012" .org $023180 .byte $00 .byte $01 @@ -117,7 +104,6 @@ L22000: bit L22000 & $ffff @label1: .byte $ea .byte $ea -; .segment "SEG013" .org $023200 @L23200: bit @L23200 & $ffff .byte $00 diff --git a/SourceGen/SGTestData/Expected/20042-address-changes_cc65.cfg b/SourceGen/SGTestData/Expected/20042-address-changes_cc65.cfg index b8ef32f..cda5fa6 100644 --- a/SourceGen/SGTestData/Expected/20042-address-changes_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20042-address-changes_cc65.cfg @@ -1,37 +1,9 @@ # 6502bench SourceGen generated linker script for 20042-address-changes MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$21000, size=13; -# MEM001: file=%O, start=$21100, size=14; -# MEM002: file=%O, start=$21100, size=15; -# MEM003: file=%O, start=$21100, size=14; -# MEM004: file=%O, start=$22000, size=7; -# MEM005: file=%O, start=$22020, size=9; -# MEM006: file=%O, start=$22080, size=32; -# MEM007: file=%O, start=$22100, size=5; -# MEM008: file=%O, start=$22800, size=16; -# MEM009: file=%O, start=$22820, size=18; -# MEM010: file=%O, start=$23000, size=14; -# MEM011: file=%O, start=$23100, size=23; -# MEM012: file=%O, start=$23180, size=18; -# MEM013: file=%O, start=$23200, size=5; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; -# SEG003: load=MEM003, type=rw; -# SEG004: load=MEM004, type=rw; -# SEG005: load=MEM005, type=rw; -# SEG006: load=MEM006, type=rw; -# SEG007: load=MEM007, type=rw; -# SEG008: load=MEM008, type=rw; -# SEG009: load=MEM009, type=rw; -# SEG010: load=MEM010, type=rw; -# SEG011: load=MEM011, type=rw; -# SEG012: load=MEM012, type=rw; -# SEG013: load=MEM013, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20050-branches-and-banks_acme.S b/SourceGen/SGTestData/Expected/20050-branches-and-banks_acme.S index 884c806..e9a014f 100644 --- a/SourceGen/SGTestData/Expected/20050-branches-and-banks_acme.S +++ b/SourceGen/SGTestData/Expected/20050-branches-and-banks_acme.S @@ -1,9 +1,7 @@ !cpu 6502 -* = $0000 - !pseudopc $1000 { +* = $1000 jmp L0000 - } ;!pseudopc !pseudopc $0000 { L0000 bit+2 L0000 L0003 lda+1 L0000 diff --git a/SourceGen/SGTestData/Expected/20050-branches-and-banks_cc65.S b/SourceGen/SGTestData/Expected/20050-branches-and-banks_cc65.S index e8aca36..13b586e 100644 --- a/SourceGen/SGTestData/Expected/20050-branches-and-banks_cc65.S +++ b/SourceGen/SGTestData/Expected/20050-branches-and-banks_cc65.S @@ -1,9 +1,7 @@ .setcpu "6502" -; .segment "SEG000" .org $1000 jmp L0000 -; .segment "SEG001" .org $0000 L0000: bit a:L0000 L0003: lda L0000 @@ -21,12 +19,10 @@ L0012: lda lodat+1 clc .byte $90,$a9 -; .segment "SEG002" .org $0080 L0080: bit a:L0080 jmp LFFC6 -; .segment "SEG003" .org $ffc0 LFFC0: bit LFFC0 LFFC3: clc diff --git a/SourceGen/SGTestData/Expected/20050-branches-and-banks_cc65.cfg b/SourceGen/SGTestData/Expected/20050-branches-and-banks_cc65.cfg index 1c1e1ba..35db8f4 100644 --- a/SourceGen/SGTestData/Expected/20050-branches-and-banks_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20050-branches-and-banks_cc65.cfg @@ -1,17 +1,9 @@ # 6502bench SourceGen generated linker script for 20050-branches-and-banks MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=3; -# MEM001: file=%O, start=$0000, size=23; -# MEM002: file=%O, start=$0080, size=6; -# MEM003: file=%O, start=$ffc0, size=7; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; -# SEG003: load=MEM003, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20052-branches-and-banks_acme.S b/SourceGen/SGTestData/Expected/20052-branches-and-banks_acme.S index a2c23e1..9ee17f4 100644 --- a/SourceGen/SGTestData/Expected/20052-branches-and-banks_acme.S +++ b/SourceGen/SGTestData/Expected/20052-branches-and-banks_acme.S @@ -1,6 +1,5 @@ ;ACME can't handle 65816 code that lives outside bank zero -* = $0000 - !pseudopc $1000 { +* = $1000 !hex 18fbe2305c000044000102cf000044af000044ad0000a50030f562b2ffd0b082 !hex a9ff1700170044cfc0ff44f005303c8239005c0020002c0020f41700f44400d0 !hex 03dc0810ea201220201520200f20225634125c103254cf1032548006eaea6016 @@ -10,4 +9,3 @@ !hex 7d3254eaea60200e20eac23008a90000e230a90028a9eaeae23008a900c230a9 !hex 000028a9eaeac230eaad0e20ad2220200e20202220fc0e20d0037c0e2020cbed !hex adcbedd0037ccbedea6b - } ;!pseudopc diff --git a/SourceGen/SGTestData/Expected/20052-branches-and-banks_cc65.S b/SourceGen/SGTestData/Expected/20052-branches-and-banks_cc65.S index 7fba5fd..7630a5d 100644 --- a/SourceGen/SGTestData/Expected/20052-branches-and-banks_cc65.S +++ b/SourceGen/SGTestData/Expected/20052-branches-and-banks_cc65.S @@ -2,7 +2,6 @@ zero = $00 longsym = $123456 -; .segment "SEG000" .org $1000 .a8 .i8 @@ -15,7 +14,6 @@ lodat: .byte $00 .byte $01 .byte $02 -; .segment "SEG001" .org $440000 L440000: cmp L440000 L440004: lda L440000 @@ -29,7 +27,6 @@ L440004: lda L440000 dat44: .word dat44 & $ffff .faraddr dat44 -; .segment "SEG002" .org $44ffc0 L44FFC0: cmp L44FFC0 high44: beq @L44FFCB @@ -38,7 +35,6 @@ high44: beq @L44FFCB @L44FFCB: jml @L2000 -; .segment "SEG003" .org $2000 @L2000: bit @L2000 pea dat44 & $ffff @@ -53,7 +49,6 @@ j2: jsr j2+3 jsl longsym jml bank54 -; .segment "SEG004" .org $543210 bank54: cmp bank54 bra L54321C diff --git a/SourceGen/SGTestData/Expected/20052-branches-and-banks_cc65.cfg b/SourceGen/SGTestData/Expected/20052-branches-and-banks_cc65.cfg index cff2daf..000b0f7 100644 --- a/SourceGen/SGTestData/Expected/20052-branches-and-banks_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20052-branches-and-banks_cc65.cfg @@ -1,19 +1,9 @@ # 6502bench SourceGen generated linker script for 20052-branches-and-banks MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=11; -# MEM001: file=%O, start=$440000, size=28; -# MEM002: file=%O, start=$44ffc0, size=15; -# MEM003: file=%O, start=$2000, size=32; -# MEM004: file=%O, start=$543210, size=180; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; -# SEG003: load=MEM003, type=rw; -# SEG004: load=MEM004, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20060-target-adjustment_acme.S b/SourceGen/SGTestData/Expected/20060-target-adjustment_acme.S index cc683e4..78806ce 100644 --- a/SourceGen/SGTestData/Expected/20060-target-adjustment_acme.S +++ b/SourceGen/SGTestData/Expected/20060-target-adjustment_acme.S @@ -1,6 +1,5 @@ !cpu 6502 -* = $0000 - !pseudopc $1000 { +* = $1000 load11 lda #$11 @L1002 ldx #$22 @load33 ldy #$33 @@ -47,7 +46,6 @@ load11 lda #$11 !byte $80 @dat81 !byte $81 - } ;!pseudopc !pseudopc $2000 { @L2000 !byte $82 !byte $83 diff --git a/SourceGen/SGTestData/Expected/20060-target-adjustment_cc65.S b/SourceGen/SGTestData/Expected/20060-target-adjustment_cc65.S index 5838a2a..479faff 100644 --- a/SourceGen/SGTestData/Expected/20060-target-adjustment_cc65.S +++ b/SourceGen/SGTestData/Expected/20060-target-adjustment_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502" -; .segment "SEG000" .org $1000 load11: lda #$11 @L1002: ldx #$22 @@ -47,7 +46,6 @@ load11: lda #$11 .byte $80 @dat81: .byte $81 -; .segment "SEG001" .org $2000 @L2000: .byte $82 .byte $83 diff --git a/SourceGen/SGTestData/Expected/20060-target-adjustment_cc65.cfg b/SourceGen/SGTestData/Expected/20060-target-adjustment_cc65.cfg index ef319d9..e77e7fb 100644 --- a/SourceGen/SGTestData/Expected/20060-target-adjustment_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20060-target-adjustment_cc65.cfg @@ -1,13 +1,9 @@ # 6502bench SourceGen generated linker script for 20060-target-adjustment MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=133; -# MEM001: file=%O, start=$2000, size=58; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20062-target-adjustment_cc65.S b/SourceGen/SGTestData/Expected/20062-target-adjustment_cc65.S index 9128c8f..d955272 100644 --- a/SourceGen/SGTestData/Expected/20062-target-adjustment_cc65.S +++ b/SourceGen/SGTestData/Expected/20062-target-adjustment_cc65.S @@ -1,6 +1,5 @@ ;6502bench SourceGen v1.7.3-dev2 .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/20062-target-adjustment_cc65.cfg b/SourceGen/SGTestData/Expected/20062-target-adjustment_cc65.cfg index 66cf999..8c944c0 100644 --- a/SourceGen/SGTestData/Expected/20062-target-adjustment_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20062-target-adjustment_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20062-target-adjustment MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=23; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20070-hinting_cc65.S b/SourceGen/SGTestData/Expected/20070-hinting_cc65.S index 25e6cd5..cbfddfe 100644 --- a/SourceGen/SGTestData/Expected/20070-hinting_cc65.S +++ b/SourceGen/SGTestData/Expected/20070-hinting_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502" -; .segment "SEG000" .org $1000 .byte $03 .byte $02 diff --git a/SourceGen/SGTestData/Expected/20070-hinting_cc65.cfg b/SourceGen/SGTestData/Expected/20070-hinting_cc65.cfg index 626f749..1a837a9 100644 --- a/SourceGen/SGTestData/Expected/20070-hinting_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20070-hinting_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20070-hinting MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=72; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20081-label-localizer_cc65.S b/SourceGen/SGTestData/Expected/20081-label-localizer_cc65.S index fcbea3b..f555bde 100644 --- a/SourceGen/SGTestData/Expected/20081-label-localizer_cc65.S +++ b/SourceGen/SGTestData/Expected/20081-label-localizer_cc65.S @@ -1,7 +1,6 @@ .setcpu "65C02" REALLYLONGLABELNAME = $8888 ;that's a long name -; .segment "SEG000" .org $1000 nop _start: lda @start diff --git a/SourceGen/SGTestData/Expected/20081-label-localizer_cc65.cfg b/SourceGen/SGTestData/Expected/20081-label-localizer_cc65.cfg index ec96ed3..e95c929 100644 --- a/SourceGen/SGTestData/Expected/20081-label-localizer_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20081-label-localizer_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20081-label-localizer MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=103; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20090-notes-and-comments_cc65.S b/SourceGen/SGTestData/Expected/20090-notes-and-comments_cc65.S index c79f288..53c06b5 100644 --- a/SourceGen/SGTestData/Expected/20090-notes-and-comments_cc65.S +++ b/SourceGen/SGTestData/Expected/20090-notes-and-comments_cc65.S @@ -19,7 +19,6 @@ plataddr = $3000 ;address only in platform file ;Short, unboxed comment here!! ;Two spaces after. More hyp- ;hens? -; .segment "SEG000" .org $1000 lda #$01 ;Comment! ;Comment rulers can be helpful in findin the edges of notes. Comments are hyph- diff --git a/SourceGen/SGTestData/Expected/20090-notes-and-comments_cc65.cfg b/SourceGen/SGTestData/Expected/20090-notes-and-comments_cc65.cfg index c6d6b68..5b09b5d 100644 --- a/SourceGen/SGTestData/Expected/20090-notes-and-comments_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20090-notes-and-comments_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20090-notes-and-comments MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=98; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20100-label-dp_acme.S b/SourceGen/SGTestData/Expected/20100-label-dp_acme.S index 4565e15..d8c7740 100644 --- a/SourceGen/SGTestData/Expected/20100-label-dp_acme.S +++ b/SourceGen/SGTestData/Expected/20100-label-dp_acme.S @@ -1,6 +1,5 @@ !cpu 6510 -* = $0000 - !pseudopc $1000 { +* = $1000 jsr L1035 jsr L1038 jsr L1059 @@ -295,7 +294,6 @@ L1238 isc (L0080),y sbc+2 L0086,x inc+2 L0086,x isc+2 L0086,x - } ;!pseudopc !pseudopc $0080 { L0080 bit+1 @L0082 @L0082 bit+1 @L0082 diff --git a/SourceGen/SGTestData/Expected/20100-label-dp_cc65.S b/SourceGen/SGTestData/Expected/20100-label-dp_cc65.S index ae50bb5..f8b8d17 100644 --- a/SourceGen/SGTestData/Expected/20100-label-dp_cc65.S +++ b/SourceGen/SGTestData/Expected/20100-label-dp_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502X" -; .segment "SEG000" .org $1000 jsr L1035 jsr L1038 @@ -295,7 +294,6 @@ L1238: isc (L0080),y sbc a:L0086,x inc a:L0086,x isc a:L0086,x -; .segment "SEG001" .org $0080 L0080: bit z:@L0082 @L0082: bit @L0082 diff --git a/SourceGen/SGTestData/Expected/20100-label-dp_cc65.cfg b/SourceGen/SGTestData/Expected/20100-label-dp_cc65.cfg index 7b1b152..b11f918 100644 --- a/SourceGen/SGTestData/Expected/20100-label-dp_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20100-label-dp_cc65.cfg @@ -1,13 +1,9 @@ # 6502bench SourceGen generated linker script for 20100-label-dp MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=598; -# MEM001: file=%O, start=$0080, size=9; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20102-label-dp_acme.S b/SourceGen/SGTestData/Expected/20102-label-dp_acme.S index 371b974..30553c2 100644 --- a/SourceGen/SGTestData/Expected/20102-label-dp_acme.S +++ b/SourceGen/SGTestData/Expected/20102-label-dp_acme.S @@ -1,6 +1,5 @@ !cpu 65816 -* = $0000 - !pseudopc $1000 { +* = $1000 !as !rs sec @@ -286,7 +285,6 @@ L11FC cmp+2 L0086,x sbc+2 L0086,x inc+2 L0086,x sbc+3 L0089,x - } ;!pseudopc !pseudopc $0080 { L0080 bit+1 @L0082 @L0082 bit+1 @L0082 diff --git a/SourceGen/SGTestData/Expected/20102-label-dp_cc65.S b/SourceGen/SGTestData/Expected/20102-label-dp_cc65.S index 167e0c3..223618e 100644 --- a/SourceGen/SGTestData/Expected/20102-label-dp_cc65.S +++ b/SourceGen/SGTestData/Expected/20102-label-dp_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 @@ -286,7 +285,6 @@ L11FC: cmp a:L0086,x sbc a:L0086,x inc a:L0086,x sbc f:L0089,x -; .segment "SEG001" .org $0080 L0080: bit z:@L0082 @L0082: bit @L0082 diff --git a/SourceGen/SGTestData/Expected/20102-label-dp_cc65.cfg b/SourceGen/SGTestData/Expected/20102-label-dp_cc65.cfg index bccce59..5fa2af3 100644 --- a/SourceGen/SGTestData/Expected/20102-label-dp_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20102-label-dp_cc65.cfg @@ -1,13 +1,9 @@ # 6502bench SourceGen generated linker script for 20102-label-dp MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=588; -# MEM001: file=%O, start=$0080, size=13; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20110-64k-nops_cc65.S b/SourceGen/SGTestData/Expected/20110-64k-nops_cc65.S index 0fe9e86..edd9d6c 100644 --- a/SourceGen/SGTestData/Expected/20110-64k-nops_cc65.S +++ b/SourceGen/SGTestData/Expected/20110-64k-nops_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502" -; .segment "SEG000" .org $0000 nop nop diff --git a/SourceGen/SGTestData/Expected/20110-64k-nops_cc65.cfg b/SourceGen/SGTestData/Expected/20110-64k-nops_cc65.cfg index d8449c9..c9e4974 100644 --- a/SourceGen/SGTestData/Expected/20110-64k-nops_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20110-64k-nops_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20110-64k-nops MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$0000, size=65536; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20120-char-encoding-a_cc65.S b/SourceGen/SGTestData/Expected/20120-char-encoding-a_cc65.S index ded807f..3965cf6 100644 --- a/SourceGen/SGTestData/Expected/20120-char-encoding-a_cc65.S +++ b/SourceGen/SGTestData/Expected/20120-char-encoding-a_cc65.S @@ -1,6 +1,5 @@ ;Projected edited to format non-char operands as chars. .setcpu "6502" -; .segment "SEG000" .org $1000 lda #'A' lda #'A' | $80 diff --git a/SourceGen/SGTestData/Expected/20120-char-encoding-a_cc65.cfg b/SourceGen/SGTestData/Expected/20120-char-encoding-a_cc65.cfg index 5f4cb6f..df5f867 100644 --- a/SourceGen/SGTestData/Expected/20120-char-encoding-a_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20120-char-encoding-a_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20120-char-encoding-a MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=1417; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20122-char-encoding_cc65.S b/SourceGen/SGTestData/Expected/20122-char-encoding_cc65.S index da3bea1..7d64516 100644 --- a/SourceGen/SGTestData/Expected/20122-char-encoding_cc65.S +++ b/SourceGen/SGTestData/Expected/20122-char-encoding_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/20122-char-encoding_cc65.cfg b/SourceGen/SGTestData/Expected/20122-char-encoding_cc65.cfg index 9e7a7cf..c0c87a0 100644 --- a/SourceGen/SGTestData/Expected/20122-char-encoding_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20122-char-encoding_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20122-char-encoding MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=19; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20130-char-encoding-p_cc65.S b/SourceGen/SGTestData/Expected/20130-char-encoding-p_cc65.S index 60e8bba..462d6d5 100644 --- a/SourceGen/SGTestData/Expected/20130-char-encoding-p_cc65.S +++ b/SourceGen/SGTestData/Expected/20130-char-encoding-p_cc65.S @@ -1,6 +1,5 @@ ;Projected edited to format non-char operands as chars. .setcpu "6502" -; .segment "SEG000" .org $1000 lda #'A' lda #'A' | $80 diff --git a/SourceGen/SGTestData/Expected/20130-char-encoding-p_cc65.cfg b/SourceGen/SGTestData/Expected/20130-char-encoding-p_cc65.cfg index dfbe7bb..f29f7fb 100644 --- a/SourceGen/SGTestData/Expected/20130-char-encoding-p_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20130-char-encoding-p_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20130-char-encoding-p MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=1417; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20140-char-encoding-s_cc65.S b/SourceGen/SGTestData/Expected/20140-char-encoding-s_cc65.S index 18e5686..2a7355d 100644 --- a/SourceGen/SGTestData/Expected/20140-char-encoding-s_cc65.S +++ b/SourceGen/SGTestData/Expected/20140-char-encoding-s_cc65.S @@ -1,6 +1,5 @@ ;Projected edited to format non-char operands as chars. .setcpu "6502" -; .segment "SEG000" .org $1000 lda #'A' lda #'A' | $80 diff --git a/SourceGen/SGTestData/Expected/20140-char-encoding-s_cc65.cfg b/SourceGen/SGTestData/Expected/20140-char-encoding-s_cc65.cfg index e3e6e84..6fe10c9 100644 --- a/SourceGen/SGTestData/Expected/20140-char-encoding-s_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20140-char-encoding-s_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20140-char-encoding-s MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=1417; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20150-local-variables_acme.S b/SourceGen/SGTestData/Expected/20150-local-variables_acme.S index 5977b1f..1ac3ca1 100644 --- a/SourceGen/SGTestData/Expected/20150-local-variables_acme.S +++ b/SourceGen/SGTestData/Expected/20150-local-variables_acme.S @@ -5,8 +5,7 @@ CONST_ZERO = $f0 ;project const PROJ_ZERO = $00 ;project addr PROJ_ONE = $01 ;project addr -* = $0000 - !pseudopc $1000 { +* = $1000 ldy PROJ_ZERO lda (PROJ_ONE),y sta $03 ;could be PROJ_ONE+2, but "nearby" is off @@ -186,7 +185,6 @@ L103C lda #$fe jsr DPCODE rts - } ;!pseudopc !pseudopc $0080 { DPCODE nop lda+1 DPCODE diff --git a/SourceGen/SGTestData/Expected/20150-local-variables_cc65.S b/SourceGen/SGTestData/Expected/20150-local-variables_cc65.S index 539c900..befb248 100644 --- a/SourceGen/SGTestData/Expected/20150-local-variables_cc65.S +++ b/SourceGen/SGTestData/Expected/20150-local-variables_cc65.S @@ -5,7 +5,6 @@ CONST_ZERO = $f0 ;project const PROJ_ZERO = $00 ;project addr PROJ_ONE = $01 ;project addr -; .segment "SEG000" .org $1000 ldy PROJ_ZERO lda (PROJ_ONE),y @@ -99,7 +98,6 @@ DPNOP .set $80 ;same as org jsr DPCODE rts -; .segment "SEG001" .org $0080 DPCODE: nop lda DPCODE diff --git a/SourceGen/SGTestData/Expected/20150-local-variables_cc65.cfg b/SourceGen/SGTestData/Expected/20150-local-variables_cc65.cfg index 7dd034c..8a03da0 100644 --- a/SourceGen/SGTestData/Expected/20150-local-variables_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20150-local-variables_cc65.cfg @@ -1,13 +1,9 @@ # 6502bench SourceGen generated linker script for 20150-local-variables MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=126; -# MEM001: file=%O, start=$0080, size=25; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20152-local-variables_acme.S b/SourceGen/SGTestData/Expected/20152-local-variables_acme.S index 2b5c8b2..1a2308c 100644 --- a/SourceGen/SGTestData/Expected/20152-local-variables_acme.S +++ b/SourceGen/SGTestData/Expected/20152-local-variables_acme.S @@ -5,8 +5,7 @@ CONST_ZERO = $f0 ;project const PROJ_ZERO = $00 ;project addr PROJ_ONE = $01 ;project addr -* = $0000 - !pseudopc $1000 { +* = $1000 !as !rs ldy PROJ_ZERO @@ -188,7 +187,6 @@ L103C lda #$fe jsr DPCODE rts - } ;!pseudopc !pseudopc $0080 { DPCODE nop lda+1 DPCODE diff --git a/SourceGen/SGTestData/Expected/20152-local-variables_cc65.S b/SourceGen/SGTestData/Expected/20152-local-variables_cc65.S index 697983e..1e22824 100644 --- a/SourceGen/SGTestData/Expected/20152-local-variables_cc65.S +++ b/SourceGen/SGTestData/Expected/20152-local-variables_cc65.S @@ -5,7 +5,6 @@ CONST_ZERO = $f0 ;project const PROJ_ZERO = $00 ;project addr PROJ_ONE = $01 ;project addr -; .segment "SEG000" .org $1000 .a8 .i8 @@ -101,7 +100,6 @@ DPNOP .set $80 ;same as org jsr DPCODE rts -; .segment "SEG001" .org $0080 DPCODE: nop lda DPCODE diff --git a/SourceGen/SGTestData/Expected/20152-local-variables_cc65.cfg b/SourceGen/SGTestData/Expected/20152-local-variables_cc65.cfg index b8d33a4..3aa4720 100644 --- a/SourceGen/SGTestData/Expected/20152-local-variables_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20152-local-variables_cc65.cfg @@ -1,13 +1,9 @@ # 6502bench SourceGen generated linker script for 20152-local-variables MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=126; -# MEM001: file=%O, start=$0080, size=111; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20162-cycle-counts-65816_cc65.S b/SourceGen/SGTestData/Expected/20162-cycle-counts-65816_cc65.S index 8ef3238..d486caf 100644 --- a/SourceGen/SGTestData/Expected/20162-cycle-counts-65816_cc65.S +++ b/SourceGen/SGTestData/Expected/20162-cycle-counts-65816_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $10f0 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/20162-cycle-counts-65816_cc65.cfg b/SourceGen/SGTestData/Expected/20162-cycle-counts-65816_cc65.cfg index fdb654f..2694ddf 100644 --- a/SourceGen/SGTestData/Expected/20162-cycle-counts-65816_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20162-cycle-counts-65816_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20162-cycle-counts-65816 MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$10f0, size=126; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20170-external-symbols_cc65.S b/SourceGen/SGTestData/Expected/20170-external-symbols_cc65.S index 1068270..1378fa4 100644 --- a/SourceGen/SGTestData/Expected/20170-external-symbols_cc65.S +++ b/SourceGen/SGTestData/Expected/20170-external-symbols_cc65.S @@ -38,7 +38,6 @@ MoreMultiZero = $c100 AlsoMoreMultiZero = $c110 ;winner BankWrap = $fff0 -; .segment "SEG000" .org $1000 L1000: lda CodeWrap+255 ldx L1000 diff --git a/SourceGen/SGTestData/Expected/20170-external-symbols_cc65.cfg b/SourceGen/SGTestData/Expected/20170-external-symbols_cc65.cfg index ca810ca..66df45c 100644 --- a/SourceGen/SGTestData/Expected/20170-external-symbols_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20170-external-symbols_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20170-external-symbols MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=353; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20172-external-symbols_cc65.S b/SourceGen/SGTestData/Expected/20172-external-symbols_cc65.S index 10a64a6..d457a60 100644 --- a/SourceGen/SGTestData/Expected/20172-external-symbols_cc65.S +++ b/SourceGen/SGTestData/Expected/20172-external-symbols_cc65.S @@ -9,7 +9,6 @@ hiaddr = $e0c030 addr_e2 = $e2c030 addr_e3 = $e3c030 -; .segment "SEG000" .org $1000 .a8 .i8 diff --git a/SourceGen/SGTestData/Expected/20172-external-symbols_cc65.cfg b/SourceGen/SGTestData/Expected/20172-external-symbols_cc65.cfg index 3a1ab21..72068c1 100644 --- a/SourceGen/SGTestData/Expected/20172-external-symbols_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20172-external-symbols_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20172-external-symbols MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=258; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S b/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S index bffe456..5ae107f 100644 --- a/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S +++ b/SourceGen/SGTestData/Expected/20182-extension-scripts_acme.S @@ -3,8 +3,7 @@ PrintInlineL1String = $011000 PrintInlineL2String = $012000 PrintInlineDciString = $013000 -* = $0000 - !pseudopc $1000 { +* = $1000 !as !rs clc @@ -102,7 +101,6 @@ Next1 jsr L10BA !fill 289,$00 - } ;!pseudopc !pseudopc $1800 { L1800 jsr PrintInlineNullString per $8778 diff --git a/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.S b/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.S index 2958a9e..dd81e5b 100644 --- a/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.S +++ b/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.S @@ -3,7 +3,6 @@ PrintInlineL1String = $011000 PrintInlineL2String = $012000 PrintInlineDciString = $013000 -; .segment "SEG000" .org $1000 .a8 .i8 @@ -105,7 +104,6 @@ Next1: jsr L10BA .res 289,$00 -; .segment "SEG001" .org $1800 L1800: jsr PrintInlineNullString per $8778 @@ -115,7 +113,6 @@ L1800: jsr PrintInlineNullString .byte $6e .byte $20 .byte $01 -; .segment "SEG002" .org $1840 .byte "string" .byte $00 diff --git a/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.cfg b/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.cfg index fc2a598..5050c53 100644 --- a/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20182-extension-scripts_cc65.cfg @@ -1,15 +1,9 @@ # 6502bench SourceGen generated linker script for 20182-extension-scripts MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=512; -# MEM001: file=%O, start=$1800, size=11; -# MEM002: file=%O, start=$1840, size=21; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20190-non-unique-labels_cc65.S b/SourceGen/SGTestData/Expected/20190-non-unique-labels_cc65.S index 577d395..ea72427 100644 --- a/SourceGen/SGTestData/Expected/20190-non-unique-labels_cc65.S +++ b/SourceGen/SGTestData/Expected/20190-non-unique-labels_cc65.S @@ -1,5 +1,4 @@ .setcpu "6502" -; .segment "SEG000" .org $1000 L1000: lda #$00 @L1000: lda #$01 diff --git a/SourceGen/SGTestData/Expected/20190-non-unique-labels_cc65.cfg b/SourceGen/SGTestData/Expected/20190-non-unique-labels_cc65.cfg index 363f3e1..d7ef73a 100644 --- a/SourceGen/SGTestData/Expected/20190-non-unique-labels_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20190-non-unique-labels_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20190-non-unique-labels MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=180; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_acme.S b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_acme.S index e5abe21..b714551 100644 --- a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_acme.S +++ b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_acme.S @@ -1,10 +1,8 @@ !cpu 6502 -* = $0000 - !pseudopc $2000 { +* = $2000 jmp L2100 !text "hello, " ;string should be split by no-op addr change - } ;!pseudopc !pseudopc $200a { !text "world" !byte $80 diff --git a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_cc65.S b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_cc65.S index aff9ea9..fea5485 100644 --- a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_cc65.S +++ b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_cc65.S @@ -1,15 +1,12 @@ .setcpu "6502" -; .segment "SEG000" .org $2000 jmp L2100 .byte "hello, " ;string should be split by no-op addr change -; .segment "SEG001" .org $200a .byte "world" .byte $80 -; .segment "SEG002" .org $2100 L2100: lda #$00 sta addr1-1 ;edit this operand diff --git a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_cc65.cfg b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_cc65.cfg index 823fa3d..8beae5e 100644 --- a/SourceGen/SGTestData/Expected/20200-ui-edge-cases_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20200-ui-edge-cases_cc65.cfg @@ -1,15 +1,9 @@ # 6502bench SourceGen generated linker script for 20200-ui-edge-cases MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$2000, size=10; -# MEM001: file=%O, start=$200a, size=6; -# MEM002: file=%O, start=$2100, size=34; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.S b/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.S index a534c09..ff322e2 100644 --- a/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.S +++ b/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $030000 .a16 .i16 @@ -76,7 +75,6 @@ L30000: clc @L3008E: rts -; .segment "SEG001" .org $04ffe0 @L4FFE0: .faraddr @L4FFE0 .byte $00 @@ -108,7 +106,6 @@ L30000: clc .byte $19 .byte $1a .byte $1b -; .segment "SEG002" .org $050000 .byte $1c .byte $1d @@ -116,7 +113,6 @@ L30000: clc .byte $1f .byte " !",$22,"#$%&'()*+,-./" -; .segment "SEG003" .org $023456 .a8 .i8 @@ -132,7 +128,6 @@ L30000: clc @L23477: nop rts -; .segment "SEG004" .org $080000 @L80000: lda @L80000 lda a:@L80019 & $ffff @@ -154,6 +149,5 @@ L30000: clc .byte $00 @L80026: .byte "This is a test." .byte $00 -; .segment "SEG005" .org $060000 .byte "hello, world!" diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.cfg b/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.cfg index 474aca6..6320b92 100644 --- a/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.cfg @@ -1,21 +1,9 @@ # 6502bench SourceGen generated linker script for 20212-reloc-data MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$30000, size=143; -# MEM001: file=%O, start=$4ffe0, size=32; -# MEM002: file=%O, start=$50000, size=20; -# MEM003: file=%O, start=$23456, size=35; -# MEM004: file=%O, start=$80000, size=54; -# MEM005: file=%O, start=$60000, size=13; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; -# SEG003: load=MEM003, type=rw; -# SEG004: load=MEM004, type=rw; -# SEG005: load=MEM005, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20222-data-bank_acme.S b/SourceGen/SGTestData/Expected/20222-data-bank_acme.S index d80b9d3..b89ed2b 100644 --- a/SourceGen/SGTestData/Expected/20222-data-bank_acme.S +++ b/SourceGen/SGTestData/Expected/20222-data-bank_acme.S @@ -1,9 +1,7 @@ ;ACME can't handle 65816 code that lives outside bank zero -* = $0000 - !pseudopc $1000 { +* = $1000 !hex 18fbe230ad0010ad00204babad0010ad0020a90248abad0010ad0020a208aa48 !hex ab2200200222004002a90248ab220030034bab2c00105c0f40023c10602e2002 !hex af002002ad0020a200202b20202520202820eaa90348abad2830c230a9a90048 !hex ababe2306b6c3a10dc3d107c3020ea602e20af003003ad0020202030f0184bab !hex ea8000ad30206b2830af0040028b4babad0020ad0030ab6b60 - } ;!pseudopc diff --git a/SourceGen/SGTestData/Expected/20222-data-bank_cc65.S b/SourceGen/SGTestData/Expected/20222-data-bank_cc65.S index 726c682..4a6f2d1 100644 --- a/SourceGen/SGTestData/Expected/20222-data-bank_cc65.S +++ b/SourceGen/SGTestData/Expected/20222-data-bank_cc65.S @@ -1,5 +1,4 @@ .setcpu "65816" -; .segment "SEG000" .org $1000 .a8 .i8 @@ -38,7 +37,6 @@ L103A: .word @L103C L103D: .faraddr L2202E -; .segment "SEG001" .org $022000 bank2: lda bank2 lda bank2 & $ffff @@ -74,14 +72,12 @@ L2202E: nop bank2addr: .word L2202E & $ffff -; .segment "SEG002" .org $033000 bank3: lda bank3 lda bank2 & $ffff jsr @L33020 & $ffff beq @L33024 phk -; .segment "SEG003" .org $033020 @L33020: plb nop @@ -92,7 +88,6 @@ bank3: lda bank3 L33028: .word L33028 & $ffff -; .segment "SEG004" .org $024000 L24000: lda L24000 phb diff --git a/SourceGen/SGTestData/Expected/20222-data-bank_cc65.cfg b/SourceGen/SGTestData/Expected/20222-data-bank_cc65.cfg index 7a2c4d9..94dcab8 100644 --- a/SourceGen/SGTestData/Expected/20222-data-bank_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20222-data-bank_cc65.cfg @@ -1,19 +1,9 @@ # 6502bench SourceGen generated linker script for 20222-data-bank MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=64; -# MEM001: file=%O, start=$22000, size=50; -# MEM002: file=%O, start=$33000, size=13; -# MEM003: file=%O, start=$33020, size=10; -# MEM004: file=%O, start=$24000, size=16; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; -# SEG003: load=MEM003, type=rw; -# SEG004: load=MEM004, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20233-rockwell_cc65.S b/SourceGen/SGTestData/Expected/20233-rockwell_cc65.S index 57bf0de..1ff4900 100644 --- a/SourceGen/SGTestData/Expected/20233-rockwell_cc65.S +++ b/SourceGen/SGTestData/Expected/20233-rockwell_cc65.S @@ -1,7 +1,6 @@ .setcpu "65C02" G_DP = $20 -; .segment "SEG000" .org $1000 bbr0 $10,L1004 rts diff --git a/SourceGen/SGTestData/Expected/20233-rockwell_cc65.cfg b/SourceGen/SGTestData/Expected/20233-rockwell_cc65.cfg index fcafbed..8356762 100644 --- a/SourceGen/SGTestData/Expected/20233-rockwell_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20233-rockwell_cc65.cfg @@ -1,11 +1,9 @@ # 6502bench SourceGen generated linker script for 20233-rockwell MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$1000, size=44; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20240-large-overlay_cc65.S b/SourceGen/SGTestData/Expected/20240-large-overlay_cc65.S index 747537a..ea28714 100644 --- a/SourceGen/SGTestData/Expected/20240-large-overlay_cc65.S +++ b/SourceGen/SGTestData/Expected/20240-large-overlay_cc65.S @@ -1,26 +1,18 @@ .setcpu "6502" -; .segment "SEG000" .org $8000 .byte $ea .res 8191,$00 -; .segment "SEG001" .org $8000 .res 8192,$01 -; .segment "SEG002" .org $8000 .res 8192,$02 -; .segment "SEG003" .org $8000 .res 8192,$03 -; .segment "SEG004" .org $8000 .res 8192,$04 -; .segment "SEG005" .org $8000 .res 8192,$05 -; .segment "SEG006" .org $8000 .res 8192,$06 -; .segment "SEG007" .org $8000 .res 8192,$07 diff --git a/SourceGen/SGTestData/Expected/20240-large-overlay_cc65.cfg b/SourceGen/SGTestData/Expected/20240-large-overlay_cc65.cfg index d7476ab..75c6ce0 100644 --- a/SourceGen/SGTestData/Expected/20240-large-overlay_cc65.cfg +++ b/SourceGen/SGTestData/Expected/20240-large-overlay_cc65.cfg @@ -1,25 +1,9 @@ # 6502bench SourceGen generated linker script for 20240-large-overlay MEMORY { MAIN: file=%O, start=%S, size=65536; -# MEM000: file=%O, start=$8000, size=8192; -# MEM001: file=%O, start=$8000, size=8192; -# MEM002: file=%O, start=$8000, size=8192; -# MEM003: file=%O, start=$8000, size=8192; -# MEM004: file=%O, start=$8000, size=8192; -# MEM005: file=%O, start=$8000, size=8192; -# MEM006: file=%O, start=$8000, size=8192; -# MEM007: file=%O, start=$8000, size=8192; } SEGMENTS { CODE: load=MAIN, type=rw; -# SEG000: load=MEM000, type=rw; -# SEG001: load=MEM001, type=rw; -# SEG002: load=MEM002, type=rw; -# SEG003: load=MEM003, type=rw; -# SEG004: load=MEM004, type=rw; -# SEG005: load=MEM005, type=rw; -# SEG006: load=MEM006, type=rw; -# SEG007: load=MEM007, type=rw; } FEATURES {} SYMBOLS {} diff --git a/SourceGen/Sandbox/ScriptManager.cs b/SourceGen/Sandbox/ScriptManager.cs index b777101..c144eac 100644 --- a/SourceGen/Sandbox/ScriptManager.cs +++ b/SourceGen/Sandbox/ScriptManager.cs @@ -272,8 +272,10 @@ namespace SourceGen.Sandbox { } } else { CheckHealth(); - List addrEnts = mProject.AddrMap.GetEntryList(); - DomainMgr.PluginMgr.PreparePlugins(appRef, addrEnts, plSyms); + int spanLength; + List addrEnts = + mProject.AddrMap.GetEntryList(out spanLength); + DomainMgr.PluginMgr.PreparePlugins(appRef, spanLength, addrEnts, plSyms); } } diff --git a/SourceGen/Tools/Omf/Loader.cs b/SourceGen/Tools/Omf/Loader.cs index 423577c..4f228d6 100644 --- a/SourceGen/Tools/Omf/Loader.cs +++ b/SourceGen/Tools/Omf/Loader.cs @@ -607,7 +607,7 @@ namespace SourceGen.Tools.Omf { while (true) { // Generate an ORG directive. - int origAddr = proj.AddrMap.Get(bufOffset); + int origAddr = proj.AddrMap.OffsetToAddress(bufOffset); UndoableChange uc = UndoableChange.CreateAddressChange(bufOffset, origAddr, addr); cs.Add(uc); diff --git a/SourceGen/WpfGui/EditAddress.xaml.cs b/SourceGen/WpfGui/EditAddress.xaml.cs index a44b9f4..92c833b 100644 --- a/SourceGen/WpfGui/EditAddress.xaml.cs +++ b/SourceGen/WpfGui/EditAddress.xaml.cs @@ -164,7 +164,7 @@ namespace SourceGen.WpfGui { private void OkButton_Click(object sender, RoutedEventArgs e) { if (AddressText.Length == 0) { - NewAddress = CommonUtil.AddressMap.NO_ENTRY_ADDR; + NewAddress = CommonUtil.AddressMap.NON_ADDR; } else { bool ok = Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out int addr); Debug.Assert(ok); diff --git a/SourceGen/WpfGui/EditDataBank.xaml.cs b/SourceGen/WpfGui/EditDataBank.xaml.cs index 93ce234..fa2607a 100644 --- a/SourceGen/WpfGui/EditDataBank.xaml.cs +++ b/SourceGen/WpfGui/EditDataBank.xaml.cs @@ -99,7 +99,7 @@ namespace SourceGen.WpfGui { bool[] done = new bool[256]; foreach (AddressMap.AddressMapEntry ent in mProject.AddrMap) { - byte bank = (byte)(ent.Addr >> 16); + byte bank = (byte)(ent.Address >> 16); if (done[bank]) { continue; }