From 39b7b20144e89251065689ed0c41b972859f3707 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Thu, 16 Sep 2021 17:02:19 -0700 Subject: [PATCH] ORG rework, part 1 This is the first step toward changing the address region map from a linear list to a hierarchy. See issue #107 for the plan. The AddressMap class has been rewritten to support the new approach. The rest of the project has been updated to conform to the new API, but feature-wise is unchanged. While the map class supports nested regions with explicit lengths, the rest of the application still assumes a series of non-overlapping regions with "floating" lengths. The Set Address dialog is currently non-functional. All of the output for cc65 changed because generation of segment comments has been removed. Some of the output for ACME changed as well, because we no longer follow "* = addr" with a redundant pseudopc statement. ACME and 65tass have similar approaches to placing things in memory, and so now have similar implementations. --- CommonUtil/AddressMap.cs | 1488 ++++++++++++++--- PluginCommon/PluginManager.cs | 5 +- SourceGen/AsmGen/AsmAcme.cs | 97 +- SourceGen/AsmGen/AsmCc65.cs | 56 +- SourceGen/AsmGen/AsmMerlin32.cs | 8 +- SourceGen/AsmGen/AsmTass64.cs | 83 +- SourceGen/AsmGen/GenCommon.cs | 47 +- SourceGen/AsmGen/IGenerator.cs | 6 +- SourceGen/CodeAnalysis.cs | 30 +- SourceGen/DataAnalysis.cs | 2 +- SourceGen/DisasmProject.cs | 56 +- SourceGen/LineListGen.cs | 2 +- SourceGen/MainController.cs | 69 +- SourceGen/ProjectFile.cs | 16 +- SourceGen/Res/Strings.xaml | 1 + SourceGen/Res/Strings.xaml.cs | 2 + .../Expected/10000-allops-value-6502_cc65.S | 1 - .../Expected/10000-allops-value-6502_cc65.cfg | 2 - .../Expected/10001-allops-value-65C02_cc65.S | 1 - .../10001-allops-value-65C02_cc65.cfg | 2 - .../Expected/10002-allops-value-65816_cc65.S | 1 - .../10002-allops-value-65816_cc65.cfg | 2 - .../Expected/10003-allops-value-W65C02_cc65.S | 1 - .../10003-allops-value-W65C02_cc65.cfg | 2 - .../Expected/10010-allops-zero-6502_cc65.S | 1 - .../Expected/10010-allops-zero-6502_cc65.cfg | 2 - .../Expected/10011-allops-zero-65C02_cc65.S | 1 - .../Expected/10011-allops-zero-65C02_cc65.cfg | 2 - .../Expected/10012-allops-zero-65816_cc65.S | 1 - .../Expected/10012-allops-zero-65816_cc65.cfg | 2 - .../Expected/10013-allops-zero-W65C02_cc65.S | 1 - .../10013-allops-zero-W65C02_cc65.cfg | 2 - .../10020-embedded-instructions_cc65.S | 1 - .../10020-embedded-instructions_cc65.cfg | 2 - .../10022-embedded-instructions_cc65.S | 1 - .../10022-embedded-instructions_cc65.cfg | 2 - .../Expected/10030-flags-and-branches_cc65.S | 1 - .../10030-flags-and-branches_cc65.cfg | 2 - .../Expected/10032-flags-and-branches_cc65.S | 1 - .../10032-flags-and-branches_cc65.cfg | 2 - .../Expected/10040-data-recognition_cc65.S | 1 - .../Expected/10040-data-recognition_cc65.cfg | 2 - .../Expected/20000-numeric-types_acme.S | 4 +- .../Expected/20000-numeric-types_cc65.S | 3 - .../Expected/20000-numeric-types_cc65.cfg | 6 - .../Expected/20010-string-types_cc65.S | 1 - .../Expected/20010-string-types_cc65.cfg | 2 - .../Expected/20020-operand-formats_cc65.S | 1 - .../Expected/20020-operand-formats_cc65.cfg | 2 - .../Expected/20022-operand-formats_cc65.S | 1 - .../Expected/20022-operand-formats_cc65.cfg | 2 - .../Expected/20030-labels-and-symbols_acme.S | 4 +- .../Expected/20030-labels-and-symbols_cc65.S | 2 - .../20030-labels-and-symbols_cc65.cfg | 4 - .../Expected/20032-labels-and-symbols_cc65.S | 2 - .../20032-labels-and-symbols_cc65.cfg | 4 - .../Expected/20040-address-changes_acme.S | 4 +- .../Expected/20040-address-changes_cc65.S | 15 - .../Expected/20040-address-changes_cc65.cfg | 30 - .../Expected/20042-address-changes_cc65.S | 14 - .../Expected/20042-address-changes_cc65.cfg | 28 - .../Expected/20050-branches-and-banks_acme.S | 4 +- .../Expected/20050-branches-and-banks_cc65.S | 4 - .../20050-branches-and-banks_cc65.cfg | 8 - .../Expected/20052-branches-and-banks_acme.S | 4 +- .../Expected/20052-branches-and-banks_cc65.S | 5 - .../20052-branches-and-banks_cc65.cfg | 10 - .../Expected/20060-target-adjustment_acme.S | 4 +- .../Expected/20060-target-adjustment_cc65.S | 2 - .../Expected/20060-target-adjustment_cc65.cfg | 4 - .../Expected/20062-target-adjustment_cc65.S | 1 - .../Expected/20062-target-adjustment_cc65.cfg | 2 - .../SGTestData/Expected/20070-hinting_cc65.S | 1 - .../Expected/20070-hinting_cc65.cfg | 2 - .../Expected/20081-label-localizer_cc65.S | 1 - .../Expected/20081-label-localizer_cc65.cfg | 2 - .../Expected/20090-notes-and-comments_cc65.S | 1 - .../20090-notes-and-comments_cc65.cfg | 2 - .../SGTestData/Expected/20100-label-dp_acme.S | 4 +- .../SGTestData/Expected/20100-label-dp_cc65.S | 2 - .../Expected/20100-label-dp_cc65.cfg | 4 - .../SGTestData/Expected/20102-label-dp_acme.S | 4 +- .../SGTestData/Expected/20102-label-dp_cc65.S | 2 - .../Expected/20102-label-dp_cc65.cfg | 4 - .../SGTestData/Expected/20110-64k-nops_cc65.S | 1 - .../Expected/20110-64k-nops_cc65.cfg | 2 - .../Expected/20120-char-encoding-a_cc65.S | 1 - .../Expected/20120-char-encoding-a_cc65.cfg | 2 - .../Expected/20122-char-encoding_cc65.S | 1 - .../Expected/20122-char-encoding_cc65.cfg | 2 - .../Expected/20130-char-encoding-p_cc65.S | 1 - .../Expected/20130-char-encoding-p_cc65.cfg | 2 - .../Expected/20140-char-encoding-s_cc65.S | 1 - .../Expected/20140-char-encoding-s_cc65.cfg | 2 - .../Expected/20150-local-variables_acme.S | 4 +- .../Expected/20150-local-variables_cc65.S | 2 - .../Expected/20150-local-variables_cc65.cfg | 4 - .../Expected/20152-local-variables_acme.S | 4 +- .../Expected/20152-local-variables_cc65.S | 2 - .../Expected/20152-local-variables_cc65.cfg | 4 - .../Expected/20162-cycle-counts-65816_cc65.S | 1 - .../20162-cycle-counts-65816_cc65.cfg | 2 - .../Expected/20170-external-symbols_cc65.S | 1 - .../Expected/20170-external-symbols_cc65.cfg | 2 - .../Expected/20172-external-symbols_cc65.S | 1 - .../Expected/20172-external-symbols_cc65.cfg | 2 - .../Expected/20182-extension-scripts_acme.S | 4 +- .../Expected/20182-extension-scripts_cc65.S | 3 - .../Expected/20182-extension-scripts_cc65.cfg | 6 - .../Expected/20190-non-unique-labels_cc65.S | 1 - .../Expected/20190-non-unique-labels_cc65.cfg | 2 - .../Expected/20200-ui-edge-cases_acme.S | 4 +- .../Expected/20200-ui-edge-cases_cc65.S | 3 - .../Expected/20200-ui-edge-cases_cc65.cfg | 6 - .../Expected/20212-reloc-data_cc65.S | 6 - .../Expected/20212-reloc-data_cc65.cfg | 12 - .../Expected/20222-data-bank_acme.S | 4 +- .../Expected/20222-data-bank_cc65.S | 5 - .../Expected/20222-data-bank_cc65.cfg | 10 - .../SGTestData/Expected/20233-rockwell_cc65.S | 1 - .../Expected/20233-rockwell_cc65.cfg | 2 - .../Expected/20240-large-overlay_cc65.S | 8 - .../Expected/20240-large-overlay_cc65.cfg | 16 - SourceGen/Sandbox/ScriptManager.cs | 6 +- SourceGen/Tools/Omf/Loader.cs | 2 +- SourceGen/WpfGui/EditAddress.xaml.cs | 2 +- SourceGen/WpfGui/EditDataBank.xaml.cs | 2 +- 127 files changed, 1644 insertions(+), 718 deletions(-) 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; }