diff --git a/CommonUtil/Misc.cs b/CommonUtil/Misc.cs
index d441a6a..4180ee2 100644
--- a/CommonUtil/Misc.cs
+++ b/CommonUtil/Misc.cs
@@ -20,6 +20,12 @@ using System.Linq;
namespace CommonUtil {
public static class Misc {
+ ///
+ /// Application identifier. This is an arbitrary string set by the application. It
+ /// will be included in the crash dump.
+ ///
+ public static string AppIdent { get; set; } = "(app ident unset)";
+
// Given a type, dump all namespaces found in the same assembly.
// https://stackoverflow.com/a/1549216/294248
public static void DumpNamespacesInAssembly(Type type) {
@@ -50,6 +56,10 @@ namespace CommonUtil {
try {
using (StreamWriter writer = new StreamWriter(CRASH_PATH, true)) {
writer.WriteLine("*** " + DateTime.Now.ToLocalTime() + " ***");
+ writer.WriteLine(" App: " + AppIdent);
+ writer.WriteLine(" OS: " +
+ System.Runtime.InteropServices.RuntimeInformation.OSDescription);
+ writer.WriteLine(string.Empty);
while (ex != null) {
writer.WriteLine(ex.GetType().FullName + ": " + ex.Message);
writer.WriteLine("Trace:");
diff --git a/CommonUtil/Version.cs b/CommonUtil/Version.cs
index 4d9655d..2822e8e 100644
--- a/CommonUtil/Version.cs
+++ b/CommonUtil/Version.cs
@@ -19,9 +19,10 @@ using System.Diagnostics;
namespace CommonUtil {
///
/// Version number container. Instances are immutable.
- ///
- /// See https://semver.org/ for explanation of system.
///
+ ///
+ /// See https://semver.org/ for explanation of system.
+ ///
public struct Version {
// Must be in ascending order, e.g. Alpha release comes before Beta.
public enum PreRelType { Dev, Alpha, Beta, Final };
@@ -47,7 +48,7 @@ namespace CommonUtil {
public PreRelType PreReleaseType { get; private set; }
///
- /// Pre-release version.
+ /// Pre-release version. Always zero when PreReleaseType is Final.
///
public int PreRelease { get; private set; }
@@ -109,6 +110,7 @@ namespace CommonUtil {
}
+ // this is a struct, so no need for null checks
public static bool operator ==(Version a, Version b) {
return a.Major == b.Major && a.Minor == b.Minor && a.Patch == b.Patch &&
a.PreReleaseType == b.PreReleaseType && a.PreRelease == b.PreRelease;
@@ -185,7 +187,7 @@ namespace CommonUtil {
ok &= (checkVers > new Version(1, 2, 2));
ok &= (checkVers < new Version(1, 3, 1));
- Debug.WriteLine("Version struct test complete (ok=" + ok + ")");
+ Debug.WriteLine("Version struct: test complete (ok=" + ok + ")");
return ok;
}
}
diff --git a/SourceGen/DataAnalysis.cs b/SourceGen/DataAnalysis.cs
index d00c059..174edd3 100644
--- a/SourceGen/DataAnalysis.cs
+++ b/SourceGen/DataAnalysis.cs
@@ -393,7 +393,10 @@ namespace SourceGen {
if (addrDiff == targetOffset - probeOffset) {
targetOffset = probeOffset;
} else {
- Debug.WriteLine("NOT probing past address boundary change");
+ Debug.WriteLine("NOT probing past address boundary change (src=+" +
+ srcOffset.ToString("x6") +
+ " targ=+" + targetOffset.ToString("x6") +
+ " probe=+" + probeOffset.ToString("x6") + ")");
}
break;
}
diff --git a/SourceGen/DefSymbol.cs b/SourceGen/DefSymbol.cs
index c172bfb..9af52ff 100644
--- a/SourceGen/DefSymbol.cs
+++ b/SourceGen/DefSymbol.cs
@@ -59,14 +59,27 @@ namespace SourceGen {
///
/// Platform symbols only: tag used to organize symbols into groups. Used by
/// extension scripts.
+ ///
+ /// Not serialized.
///
- ///
- /// This is not included in DefSymbol serialization because symbols with tags are
- /// not stored in the project file. It's only set when symbols are parsed out of
- /// platform symbol files.
- ///
public string Tag { get; private set; }
+ ///
+ /// Platform symbols only: this indicates the position of the defining platform symbol
+ /// file in the set of symbol files. Higher numbers mean higher priority.
+ ///
+ /// Not serialized.
+ ///
+ public int LoadOrdinal { get; private set; }
+
+ ///
+ /// Platform symbols only: external file identifier for the platform symbol file that
+ /// defined this symbol. Can be displayed to the user in the Info panel.
+ ///
+ /// Not serialized.
+ ///
+ public string FileIdentifier { get; private set; }
+
///
/// Cross-reference data, generated by the analyzer.
///
@@ -146,6 +159,23 @@ namespace SourceGen {
Tag = tag;
}
+ ///
+ /// Constructor. Used for platform symbol files.
+ ///
+ /// Indicates the order in which the defining platform
+ /// symbol file was loaded. Higher numbers indicate later loading, which translates
+ /// to higher priority.
+ /// Platform symbol file identifier, for the Info panel.
+ public DefSymbol(string label, int value, Source source, Type type,
+ FormatDescriptor.SubType formatSubType, string comment, string tag, int width,
+ bool widthSpecified, int loadOrdinal, string fileIdent)
+ : this(label, value, source, type, formatSubType, comment, tag, width,
+ widthSpecified) {
+ LoadOrdinal = loadOrdinal;
+ FileIdentifier = fileIdent;
+ }
+
+
///
/// Constructor. Used for deserialization, when we have a FormatDescriptor and a Symbol.
///
diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs
index 8cfe7a8..000d610 100644
--- a/SourceGen/DisasmProject.cs
+++ b/SourceGen/DisasmProject.cs
@@ -533,7 +533,7 @@ namespace SourceGen {
///
/// Failures here will be reported to the user but aren't fatal.
///
- /// String with all warnings from load process.
+ /// Multi-line string with all warnings from load process.
public string LoadExternalFiles() {
TaskTimer timer = new TaskTimer();
timer.StartTask("Total");
@@ -548,15 +548,18 @@ namespace SourceGen {
// Load the platform symbols first.
timer.StartTask("Platform Symbols");
PlatformSyms.Clear();
+ int loadOrdinal = 0;
foreach (string fileIdent in ProjectProps.PlatformSymbolFileIdentifiers) {
PlatformSymbols ps = new PlatformSymbols();
- bool ok = ps.LoadFromFile(fileIdent, projectDir, out FileLoadReport report);
+ bool ok = ps.LoadFromFile(fileIdent, projectDir, loadOrdinal,
+ out FileLoadReport report);
if (ok) {
PlatformSyms.Add(ps);
}
if (report.Count > 0) {
sb.Append(report.Format());
}
+ loadOrdinal++;
}
timer.EndTask("Platform Symbols");
@@ -664,6 +667,7 @@ namespace SourceGen {
reanalysisTimer.StartTask("SymbolTable init");
SymbolTable.Clear();
MergePlatformProjectSymbols();
+
// Merge user labels into the symbol table, overwriting platform/project symbols
// where they conflict. Labels whose values are out of sync (because of a change
// to the address map) are updated as part of this.
@@ -911,7 +915,8 @@ namespace SourceGen {
/// so that ordering of platform files behaves in an intuitive fashion.
///
private void MergePlatformProjectSymbols() {
- // Start by pulling in the platform symbols.
+ // Start by pulling in the platform symbols. The list in PlatformSymbols is in
+ // order, so we can just overwrite earlier symbols with matching labels.
foreach (PlatformSymbols ps in PlatformSyms) {
foreach (Symbol sym in ps) {
SymbolTable[sym.Label] = sym;
@@ -1080,7 +1085,8 @@ namespace SourceGen {
/// This works pretty well for addresses, but is a little rough for constants.
///
/// Call this after the code and data analysis passes have completed. This doesn't
- /// interact with labels, so the ordering there doesn't matter.
+ /// interact with labels, so the ordering there doesn't matter. This should come after
+ /// local variable resolution, so that those have priority.
///
private void GeneratePlatformSymbolRefs() {
bool checkNearby = ProjectProps.AnalysisParams.SeekNearbyTargets;
@@ -1132,25 +1138,11 @@ namespace SourceGen {
}
if (address >= 0) {
- // If we didn't find it, check addr-1. This is very helpful when working
- // with pointers, because it gets us references to "PTR+1" when "PTR" is
- // defined. (It's potentially helpful in labeling the "near side" of an
- // address map split as well, since the first byte past is an external
- // address, and a label at the end of the current region will be offset
- // from by this.)
- if (sym == null && (address & 0xffff) > 0 && checkNearby) {
- sym = SymbolTable.FindNonVariableByAddress(address - 1);
- }
- // If that didn't work, try addr-2. Good for 24-bit addresses and jump
- // vectors that start with a JMP instruction.
- if (sym == null && (address & 0xffff) > 1 && checkNearby) {
- sym = SymbolTable.FindNonVariableByAddress(address - 2);
- }
- // Still nothing, try addr+1. Sometimes indexed addressing will use
- // "STA addr-1,y". This will also catch "STA addr-1" when addr is the
- // very start of a segment, which means we're actually finding a label
- // reference rather than project/platform symbol; only works if the
- // location already has a label.
+ // If we didn't find it, see if addr+1 has a label. Sometimes indexed
+ // addressing will use "STA addr-1,y". This will also catch "STA addr-1"
+ // when addr is the very start of a segment, which means we're actually
+ // finding a label reference rather than project/platform symbol; only
+ // works if the location already has a label.
if (sym == null && (address & 0xffff) < 0xffff && checkNearby) {
sym = SymbolTable.FindNonVariableByAddress(address + 1);
if (sym != null && sym.SymbolSource != Symbol.Source.Project &&
@@ -1790,7 +1782,7 @@ namespace SourceGen {
if (newValue == null) {
// We're removing a user label.
UserLabels.Remove(offset);
- SymbolTable.Remove((Symbol)oldValue); // unnecessary?
+ SymbolTable.Remove((Symbol)oldValue); // unnecessary? will regen
Debug.Assert(uc.ReanalysisRequired ==
UndoableChange.ReanalysisScope.DataOnly);
} else {
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 530dee3..6733a4b 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -229,6 +229,10 @@ namespace SourceGen {
/// to this point so we can report fatal errors directly to the user.
///
public void WindowLoaded() {
+ Debug.Assert(CommonUtil.RangeSet.Test());
+ Debug.Assert(CommonUtil.TypedRangeSet.Test());
+ Debug.Assert(CommonUtil.Version.Test());
+
if (RuntimeDataAccess.GetDirectory() == null) {
MessageBox.Show(Res.Strings.RUNTIME_DIR_NOT_FOUND,
Res.Strings.RUNTIME_DIR_NOT_FOUND_CAPTION,
@@ -3309,7 +3313,8 @@ namespace SourceGen {
if (defSym.SymbolSource == Symbol.Source.Project) {
sourceStr = "project symbol definition";
} else if (defSym.SymbolSource == Symbol.Source.Platform) {
- sourceStr = "platform symbol file";
+ sourceStr = "platform symbol file (#" + defSym.LoadOrdinal +
+ ":" + defSym.FileIdentifier + ")";
} else {
sourceStr = "???";
}
@@ -3602,6 +3607,6 @@ namespace SourceGen {
ScriptManager.UseKeepAliveHack = !ScriptManager.UseKeepAliveHack;
}
- #endregion
+ #endregion Debug features
}
}
diff --git a/SourceGen/PlatformSymbols.cs b/SourceGen/PlatformSymbols.cs
index f6a2469..3600f37 100644
--- a/SourceGen/PlatformSymbols.cs
+++ b/SourceGen/PlatformSymbols.cs
@@ -71,12 +71,13 @@ namespace SourceGen {
///
/// Loads platform symbols.
///
- /// Relative pathname of file to open.
+ /// External file identifier of symbol file.
/// Full path to project directory.
+ /// Platform file load order.
/// Report of warnings and errors.
/// True on success (no errors), false on failure.
- public bool LoadFromFile(string fileIdent, string projectDir, out FileLoadReport report) {
- // These files shouldn't be enormous. Do it the easy way.
+ public bool LoadFromFile(string fileIdent, string projectDir, int loadOrdinal,
+ out FileLoadReport report) {
report = new FileLoadReport(fileIdent);
ExternalFile ef = ExternalFile.CreateFromIdent(fileIdent);
@@ -85,13 +86,14 @@ namespace SourceGen {
CommonUtil.Properties.Resources.ERR_FILE_NOT_FOUND + ": " + fileIdent);
return false;
}
-
string pathName = ef.GetPathName(projectDir);
if (pathName == null) {
report.Add(FileLoadItem.Type.Error,
Res.Strings.ERR_BAD_IDENT + ": " + fileIdent);
return false;
}
+
+ // These files shouldn't be enormous. Just read the entire thing into a string array.
string[] lines;
try {
lines = File.ReadAllLines(pathName);
@@ -168,7 +170,7 @@ namespace SourceGen {
FormatDescriptor.GetSubTypeForBase(numBase);
DefSymbol symDef = new DefSymbol(label, value, Symbol.Source.Platform,
isConst ? Symbol.Type.Constant : Symbol.Type.ExternalAddr,
- subType, comment, tag, width, width > 0);
+ subType, comment, tag, width, width > 0, loadOrdinal, fileIdent);
if (mSymbols.ContainsKey(label)) {
// This is very easy to do -- just define the same symbol twice
// in the same file. We don't really need to do anything about
diff --git a/SourceGen/SGTestData/2021-external-symbols b/SourceGen/SGTestData/2021-external-symbols
new file mode 100644
index 0000000..582f592
Binary files /dev/null and b/SourceGen/SGTestData/2021-external-symbols differ
diff --git a/SourceGen/SGTestData/2021-external-symbols-1.sym65 b/SourceGen/SGTestData/2021-external-symbols-1.sym65
new file mode 100644
index 0000000..8228744
--- /dev/null
+++ b/SourceGen/SGTestData/2021-external-symbols-1.sym65
@@ -0,0 +1,49 @@
+; Copyright 2019 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+
+*SYNOPSIS Symbol set 1 for test 2021-external-symbols
+
+; Platform symbols aren't applied to file data.
+PlatStart @ $1000
+
+; SameName2 and SameName3 are replaced by later file
+SameName1 @ $2000
+SameName2 @ $2010
+SameName3 @ $2020
+
+; Symbols with the same values but different names are defined
+; in later files. Names are chosen to not provide a strict
+; alphabetical progression.
+SameValA_C @ $2100
+SameValB_B @ $2110
+SameValC_A @ $2120
+
+; Test overlap with project symbol. Declare at $2202(4b), and $220a(1b).
+ChkProj1 @ $2200 4
+ChkProj2 @ $2204 4
+
+; Overlapping regions, defined within a single platform file. We
+; should always use the most-recently-defined symbol. When all
+; else is equal, alphabetical.
+Over1 @ $3000 16 ;$3000-300f, inclusive
+Over2 @ $3002 8 ;$3002-3009
+Over3 @ $3006 7 ;$3006-300c
+Over2a @ $3006 1 ;$3006
+
+; Expected result:
+; $3000-3001: Over1
+; $3002-3005: Over2
+; $3006 : Over4
+; $3007-300c: Over3
+; $300d-300f: Over1
+
+; Overlapping regions defined in multiple files. The later definition
+; takes priority. So while SepOver1 would normally end at $3102,
+; instead it steps on the first two bytes of SepOver2.
+SepOver2 @ $3102 4 ;$3102-3105, inclusive
+
+; Test overlap with local variable. Declare at $41(2b).
+OverVar @ $40 4
+
+; Width specifiers on constants should be ignored.
+FatConst = $4000 8
diff --git a/SourceGen/SGTestData/2021-external-symbols-2.sym65 b/SourceGen/SGTestData/2021-external-symbols-2.sym65
new file mode 100644
index 0000000..b9407f5
--- /dev/null
+++ b/SourceGen/SGTestData/2021-external-symbols-2.sym65
@@ -0,0 +1,13 @@
+; Copyright 2019 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+
+*SYNOPSIS Symbol set 2 for test 2021-external-symbols
+
+; override 2nd and 3rd
+SameName2 @ $2011
+SameName3 @ $2021
+
+SameValB_A @ $2110
+SameValC_B @ $2120
+
+SepOver1 @ $3100 4 ;$3100-3103, inclusive
diff --git a/SourceGen/SGTestData/2021-external-symbols-3.sym65 b/SourceGen/SGTestData/2021-external-symbols-3.sym65
new file mode 100644
index 0000000..c149562
--- /dev/null
+++ b/SourceGen/SGTestData/2021-external-symbols-3.sym65
@@ -0,0 +1,10 @@
+; Copyright 2019 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+
+*SYNOPSIS Symbol set 3 for test 2021-external-symbols
+
+; override 3rd
+SameName3 @ $2022
+
+SameValA_A @ $2100
+SameValC_C @ $2120
diff --git a/SourceGen/SGTestData/2021-external-symbols.dis65 b/SourceGen/SGTestData/2021-external-symbols.dis65
new file mode 100644
index 0000000..00c62a5
--- /dev/null
+++ b/SourceGen/SGTestData/2021-external-symbols.dis65
@@ -0,0 +1,36 @@
+### 6502bench SourceGen dis65 v1.0 ###
+{
+"_ContentVersion":2,"FileDataLength":205,"FileDataCrc32":178586750,"ProjectProps":{
+"CpuName":"6502","IncludeUndocumentedInstr":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{
+"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true},
+"PlatformSymbolFileIdentifiers":["PROJ:2021-external-symbols-1.sym65","PROJ:2021-external-symbols-2.sym65","PROJ:2021-external-symbols-3.sym65"],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{
+"ProjSim1":{
+"DataDescriptor":{
+"Length":4,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
+"Comment":"","HasWidth":true,"Label":"ProjSim1","Value":8706,"Source":"Project","Type":"ExternalAddr"},
+"ProjSim2":{
+"DataDescriptor":{
+"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
+"Comment":"","HasWidth":true,"Label":"ProjSim2","Value":8714,"Source":"Project","Type":"ExternalAddr"}}},
+"AddressMap":[{
+"Offset":0,"Addr":4096}],"TypeHints":[{
+"Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{
+},
+"Comments":{
+},
+"LongComments":{
+},
+"Notes":{
+},
+"UserLabels":{
+},
+"OperandFormats":{
+"195":{
+"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
+"Label":"FatConst","Part":"Low"}}},
+"LvTables":{
+"180":{
+"Variables":[{
+"DataDescriptor":{
+"Length":2,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
+"Comment":"","HasWidth":true,"Label":"LocalVar","Value":65,"Source":"Variable","Type":"ExternalAddr"}],"ClearPrevious":false}}}
diff --git a/SourceGen/SGTestData/Expected/2008-address-changes_64tass.S b/SourceGen/SGTestData/Expected/2008-address-changes_64tass.S
index 99e9878..7719e78 100644
--- a/SourceGen/SGTestData/Expected/2008-address-changes_64tass.S
+++ b/SourceGen/SGTestData/Expected/2008-address-changes_64tass.S
@@ -44,7 +44,7 @@ L2000 bit L2000
.here
.logical $2020
L2020 bit L2020
- beq offend+1
+ beq $2029
brl L2080
offend nop
@@ -53,8 +53,8 @@ offend nop
L2080 bit L2080
lda offend
jsr offend
- lda offend+1
- jsr offend+1
+ lda $2029
+ jsr $2029
lda L2080-1
jsr L2080-1
lda L2080
@@ -95,7 +95,7 @@ L3100 .byte $02
fwd bit fwd
lda ulabel
lda ulabel+1
- lda ulabel+2
+ lda $300e
lda $300f
lda L3100
beq L3182
diff --git a/SourceGen/SGTestData/Expected/2008-address-changes_Merlin32.S b/SourceGen/SGTestData/Expected/2008-address-changes_Merlin32.S
index a31ce7c..72fbb73 100644
--- a/SourceGen/SGTestData/Expected/2008-address-changes_Merlin32.S
+++ b/SourceGen/SGTestData/Expected/2008-address-changes_Merlin32.S
@@ -37,7 +37,7 @@ L2000 bit L2000
org $2020
L2020 bit L2020
- beq offend+1
+ beq $2029
brl L2080
offend nop
@@ -45,8 +45,8 @@ offend nop
L2080 bit L2080
lda offend
jsr offend
- lda offend+1
- jsr offend+1
+ lda $2029
+ jsr $2029
lda L2080-1
jsr L2080-1
lda L2080
@@ -82,7 +82,7 @@ L3100 dfb $02
fwd bit fwd
lda ulabel
lda ulabel+1
- lda ulabel+2
+ lda $300e
lda $300f
lda L3100
beq L3182
diff --git a/SourceGen/SGTestData/Expected/2008-address-changes_acme.S b/SourceGen/SGTestData/Expected/2008-address-changes_acme.S
index 4c0d479..dd73c74 100644
--- a/SourceGen/SGTestData/Expected/2008-address-changes_acme.S
+++ b/SourceGen/SGTestData/Expected/2008-address-changes_acme.S
@@ -44,7 +44,7 @@ L2000 bit L2000
} ;!pseudopc
!pseudopc $2020 {
L2020 bit L2020
- beq offend+1
+ beq $2029
brl L2080
offend nop
@@ -53,8 +53,8 @@ offend nop
L2080 bit L2080
lda offend
jsr offend
- lda offend+1
- jsr offend+1
+ lda $2029
+ jsr $2029
lda L2080-1
jsr L2080-1
lda L2080
@@ -95,7 +95,7 @@ L3100 !byte $02
fwd bit fwd
lda ulabel
lda ulabel+1
- lda ulabel+2
+ lda $300e
lda $300f
lda L3100
beq L3182
diff --git a/SourceGen/SGTestData/Expected/2008-address-changes_cc65.S b/SourceGen/SGTestData/Expected/2008-address-changes_cc65.S
index 6c68a60..38f0596 100644
--- a/SourceGen/SGTestData/Expected/2008-address-changes_cc65.S
+++ b/SourceGen/SGTestData/Expected/2008-address-changes_cc65.S
@@ -46,7 +46,7 @@ L2000: bit L2000
; .segment "SEG005"
.org $2020
L2020: bit L2020
- beq offend+1
+ beq $2029
brl L2080
offend: nop
@@ -55,8 +55,8 @@ offend: nop
L2080: bit L2080
lda offend
jsr offend
- lda offend+1
- jsr offend+1
+ lda $2029
+ jsr $2029
lda L2080-1
jsr L2080-1
lda L2080
@@ -97,7 +97,7 @@ L3100: .byte $02
fwd: bit fwd
lda ulabel
lda ulabel+1
- lda ulabel+2
+ lda $300e
lda $300f
lda L3100
beq L3182
diff --git a/SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S b/SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S
new file mode 100644
index 0000000..be32668
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S
@@ -0,0 +1,100 @@
+ .cpu "6502"
+OverVar = $40
+PlatStart = $1000
+SameName1 = $2000
+SameName2 = $2011
+SameName3 = $2022
+SameValA_A = $2100
+SameValB_A = $2110
+SameValC_C = $2120
+ChkProj1 = $2200
+ProjSim1 = $2202
+ChkProj2 = $2204
+ProjSim2 = $220a
+Over1 = $3000 ;$3000-300f, inclusive
+Over2 = $3002 ;$3002-3009
+Over2a = $3006 ;$3006
+Over3 = $3006 ;$3006-300c
+SepOver1 = $3100 ;$3100-3103, inclusive
+SepOver2 = $3102 ;$3102-3105, inclusive
+FatConst = $4000
+
+* = $1000
+L1000 lda PlatStart-1
+ ldx L1000
+ ldy L1000+1
+ nop
+ lda $1ffe
+ lda SameName1-1
+ lda SameName1
+ lda $200f
+ lda SameName2-1
+ lda SameName2
+ lda $201f
+ lda $2020
+ lda SameName3-1
+ lda SameName3
+ nop
+ lda SameValA_A
+ lda SameValB_A
+ lda SameValC_C
+ nop
+ lda $21fe
+ lda ChkProj1-1
+ lda ChkProj1
+ lda ChkProj1+1
+ lda ProjSim1
+ lda ProjSim1+1
+ lda ProjSim1+2
+ lda ProjSim1+3
+ lda ChkProj2+2
+ lda ChkProj2+3
+ lda $2208
+ lda ProjSim2-1
+ lda ProjSim2
+ lda $220b
+ nop
+ lda $2ffe
+ lda Over1-1
+ lda Over1
+ lda Over1+1
+ lda Over2
+ lda Over2+1
+ lda Over2+2
+ lda Over2+3
+ lda Over2a
+ lda Over3+1
+ lda Over3+2
+ lda Over3+3
+ lda Over3+4
+ lda Over3+5
+ lda Over3+6
+ lda Over1+13
+ lda Over1+14
+ lda Over1+15
+ lda $3010
+ nop
+ lda $30fe
+ lda SepOver1-1
+ lda SepOver1
+ lda SepOver1+1
+ lda SepOver1+2
+ lda SepOver1+3
+ lda SepOver2+2
+ lda SepOver2+3
+ lda $3106
+ nop
+LocalVar .var $41
+ ldx $3e
+ ldx OverVar-1
+ ldx OverVar
+ ldx LocalVar
+ ldx LocalVar+1
+ ldx OverVar+3
+ ldx $44
+ nop
+ lda FatConst-1
+ lda $4000
+ lda $4001
+ rts
+
diff --git a/SourceGen/SGTestData/Expected/2021-external-symbols_Merlin32.S b/SourceGen/SGTestData/Expected/2021-external-symbols_Merlin32.S
new file mode 100644
index 0000000..696ea3d
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/2021-external-symbols_Merlin32.S
@@ -0,0 +1,99 @@
+OverVar equ $40
+PlatStart equ $1000
+SameName1 equ $2000
+SameName2 equ $2011
+SameName3 equ $2022
+SameValA_A equ $2100
+SameValB_A equ $2110
+SameValC_C equ $2120
+ChkProj1 equ $2200
+ProjSim1 equ $2202
+ChkProj2 equ $2204
+ProjSim2 equ $220a
+Over1 equ $3000 ;$3000-300f, inclusive
+Over2 equ $3002 ;$3002-3009
+Over2a equ $3006 ;$3006
+Over3 equ $3006 ;$3006-300c
+SepOver1 equ $3100 ;$3100-3103, inclusive
+SepOver2 equ $3102 ;$3102-3105, inclusive
+FatConst equ $4000
+
+ org $1000
+L1000 lda PlatStart-1
+ ldx L1000
+ ldy L1000+1
+ nop
+ lda $1ffe
+ lda SameName1-1
+ lda SameName1
+ lda $200f
+ lda SameName2-1
+ lda SameName2
+ lda $201f
+ lda $2020
+ lda SameName3-1
+ lda SameName3
+ nop
+ lda SameValA_A
+ lda SameValB_A
+ lda SameValC_C
+ nop
+ lda $21fe
+ lda ChkProj1-1
+ lda ChkProj1
+ lda ChkProj1+1
+ lda ProjSim1
+ lda ProjSim1+1
+ lda ProjSim1+2
+ lda ProjSim1+3
+ lda ChkProj2+2
+ lda ChkProj2+3
+ lda $2208
+ lda ProjSim2-1
+ lda ProjSim2
+ lda $220b
+ nop
+ lda $2ffe
+ lda Over1-1
+ lda Over1
+ lda Over1+1
+ lda Over2
+ lda Over2+1
+ lda Over2+2
+ lda Over2+3
+ lda Over2a
+ lda Over3+1
+ lda Over3+2
+ lda Over3+3
+ lda Over3+4
+ lda Over3+5
+ lda Over3+6
+ lda Over1+13
+ lda Over1+14
+ lda Over1+15
+ lda $3010
+ nop
+ lda $30fe
+ lda SepOver1-1
+ lda SepOver1
+ lda SepOver1+1
+ lda SepOver1+2
+ lda SepOver1+3
+ lda SepOver2+2
+ lda SepOver2+3
+ lda $3106
+ nop
+]LocalVar equ $41
+ ldx $3e
+ ldx OverVar-1
+ ldx OverVar
+ ldx ]LocalVar
+ ldx ]LocalVar+1
+ ldx OverVar+3
+ ldx $44
+ nop
+ lda FatConst-1
+ lda $4000
+ lda $4001
+ rts
+
diff --git a/SourceGen/SGTestData/Expected/2021-external-symbols_acme.S b/SourceGen/SGTestData/Expected/2021-external-symbols_acme.S
new file mode 100644
index 0000000..99aa920
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/2021-external-symbols_acme.S
@@ -0,0 +1,101 @@
+ !cpu 6502
+OverVar = $40
+PlatStart = $1000
+SameName1 = $2000
+SameName2 = $2011
+SameName3 = $2022
+SameValA_A = $2100
+SameValB_A = $2110
+SameValC_C = $2120
+ChkProj1 = $2200
+ProjSim1 = $2202
+ChkProj2 = $2204
+ProjSim2 = $220a
+Over1 = $3000 ;$3000-300f, inclusive
+Over2 = $3002 ;$3002-3009
+Over2a = $3006 ;$3006
+Over3 = $3006 ;$3006-300c
+SepOver1 = $3100 ;$3100-3103, inclusive
+SepOver2 = $3102 ;$3102-3105, inclusive
+FatConst = $4000
+
+* = $1000
+L1000 lda PlatStart-1
+ ldx L1000
+ ldy L1000+1
+ nop
+ lda $1ffe
+ lda SameName1-1
+ lda SameName1
+ lda $200f
+ lda SameName2-1
+ lda SameName2
+ lda $201f
+ lda $2020
+ lda SameName3-1
+ lda SameName3
+ nop
+ lda SameValA_A
+ lda SameValB_A
+ lda SameValC_C
+ nop
+ lda $21fe
+ lda ChkProj1-1
+ lda ChkProj1
+ lda ChkProj1+1
+ lda ProjSim1
+ lda ProjSim1+1
+ lda ProjSim1+2
+ lda ProjSim1+3
+ lda ChkProj2+2
+ lda ChkProj2+3
+ lda $2208
+ lda ProjSim2-1
+ lda ProjSim2
+ lda $220b
+ nop
+ lda $2ffe
+ lda Over1-1
+ lda Over1
+ lda Over1+1
+ lda Over2
+ lda Over2+1
+ lda Over2+2
+ lda Over2+3
+ lda Over2a
+ lda Over3+1
+ lda Over3+2
+ lda Over3+3
+ lda Over3+4
+ lda Over3+5
+ lda Over3+6
+ lda Over1+13
+ lda Over1+14
+ lda Over1+15
+ lda $3010
+ nop
+ lda $30fe
+ lda SepOver1-1
+ lda SepOver1
+ lda SepOver1+1
+ lda SepOver1+2
+ lda SepOver1+3
+ lda SepOver2+2
+ lda SepOver2+3
+ lda $3106
+ nop
+ !zone Z0000b4
+.LocalVar = $41
+ ldx $3e
+ ldx OverVar-1
+ ldx OverVar
+ ldx .LocalVar
+ ldx .LocalVar+1
+ ldx OverVar+3
+ ldx $44
+ nop
+ lda FatConst-1
+ lda $4000
+ lda $4001
+ rts
+
diff --git a/SourceGen/SGTestData/Expected/2021-external-symbols_cc65.S b/SourceGen/SGTestData/Expected/2021-external-symbols_cc65.S
new file mode 100644
index 0000000..490975a
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/2021-external-symbols_cc65.S
@@ -0,0 +1,101 @@
+ .setcpu "6502"
+OverVar = $40
+PlatStart = $1000
+SameName1 = $2000
+SameName2 = $2011
+SameName3 = $2022
+SameValA_A = $2100
+SameValB_A = $2110
+SameValC_C = $2120
+ChkProj1 = $2200
+ProjSim1 = $2202
+ChkProj2 = $2204
+ProjSim2 = $220a
+Over1 = $3000 ;$3000-300f, inclusive
+Over2 = $3002 ;$3002-3009
+Over2a = $3006 ;$3006
+Over3 = $3006 ;$3006-300c
+SepOver1 = $3100 ;$3100-3103, inclusive
+SepOver2 = $3102 ;$3102-3105, inclusive
+FatConst = $4000
+
+; .segment "SEG000"
+ .org $1000
+L1000: lda PlatStart-1
+ ldx L1000
+ ldy L1000+1
+ nop
+ lda $1ffe
+ lda SameName1-1
+ lda SameName1
+ lda $200f
+ lda SameName2-1
+ lda SameName2
+ lda $201f
+ lda $2020
+ lda SameName3-1
+ lda SameName3
+ nop
+ lda SameValA_A
+ lda SameValB_A
+ lda SameValC_C
+ nop
+ lda $21fe
+ lda ChkProj1-1
+ lda ChkProj1
+ lda ChkProj1+1
+ lda ProjSim1
+ lda ProjSim1+1
+ lda ProjSim1+2
+ lda ProjSim1+3
+ lda ChkProj2+2
+ lda ChkProj2+3
+ lda $2208
+ lda ProjSim2-1
+ lda ProjSim2
+ lda $220b
+ nop
+ lda $2ffe
+ lda Over1-1
+ lda Over1
+ lda Over1+1
+ lda Over2
+ lda Over2+1
+ lda Over2+2
+ lda Over2+3
+ lda Over2a
+ lda Over3+1
+ lda Over3+2
+ lda Over3+3
+ lda Over3+4
+ lda Over3+5
+ lda Over3+6
+ lda Over1+13
+ lda Over1+14
+ lda Over1+15
+ lda $3010
+ nop
+ lda $30fe
+ lda SepOver1-1
+ lda SepOver1
+ lda SepOver1+1
+ lda SepOver1+2
+ lda SepOver1+3
+ lda SepOver2+2
+ lda SepOver2+3
+ lda $3106
+ nop
+LocalVar .set $41
+ ldx $3e
+ ldx OverVar-1
+ ldx OverVar
+ ldx LocalVar
+ ldx LocalVar+1
+ ldx OverVar+3
+ ldx $44
+ nop
+ lda FatConst-1
+ lda $4000
+ lda $4001
+ rts
+
diff --git a/SourceGen/SGTestData/Expected/2021-external-symbols_cc65.cfg b/SourceGen/SGTestData/Expected/2021-external-symbols_cc65.cfg
new file mode 100644
index 0000000..3e2eea4
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/2021-external-symbols_cc65.cfg
@@ -0,0 +1,11 @@
+# 6502bench SourceGen generated linker script for 2021-external-symbols
+MEMORY {
+ MAIN: file=%O, start=%S, size=65536;
+# MEM000: file=%O, start=$1000, size=205;
+}
+SEGMENTS {
+ CODE: load=MAIN, type=rw;
+# SEG000: load=MEM000, type=rw;
+}
+FEATURES {}
+SYMBOLS {}
diff --git a/SourceGen/SGTestData/Source/2021-external-symbols.S b/SourceGen/SGTestData/Source/2021-external-symbols.S
new file mode 100644
index 0000000..7d7104b
--- /dev/null
+++ b/SourceGen/SGTestData/Source/2021-external-symbols.S
@@ -0,0 +1,112 @@
+; Copyright 2019 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Assembler: Merlin 32
+
+; EDIT: the project must include the three platform symbol files.
+
+ org $1000
+
+; make sure platform symbols don't get set for file data
+; DO NOT set a user label here
+Start lda Start-1 ;PlatStart-1
+ ldx Start ;auto
+ ldy Start+1 ;auto+1
+
+ nop
+
+; test overlapping labels (multiple sym files)
+ lda $1ffe ;should have no symbol
+ lda $1fff ;should be SameName1-1
+ lda $2000 ;should be SameName1
+
+ lda $200f ;should have no symbol
+ lda $2010 ;should be SameName2-1
+ lda $2011 ;should be SameName2
+
+ lda $201f ;should have no symbol
+ lda $2020 ;should have no symbol
+ lda $2021 ;should be sym-1
+ lda $2022 ;should be SameName3
+
+ nop
+
+; test overlapping values (multiple sym files)
+ lda $2100 ;should be SameValA_A
+ lda $2110 ;should be SameValB_A
+ lda $2120 ;should be SameValC_C
+
+ nop
+
+; test overlap with project symbol
+; EDIT: define project symbols ProjSym1 at $2202(4b) and ProjSim2 at $220a(1b)
+ lda $21fe ;should have no symbol
+ lda $21ff ;SYM-1
+ lda $2200 ;ChkProj1
+ lda $2201 ;ChkProj1+1
+ lda $2202 ;ProjSym
+ lda $2203 ;ProjSym1+1
+ lda $2204 ;ProjSym1+2
+ lda $2205 ;ProjSym1+3
+ lda $2206 ;ChkProj2+2
+ lda $2207 ;ChkProj2+3
+ lda $2208 ;should have no symbol
+ lda $2209 ;ProjSym2-1
+ lda $220a ;ProjSym2
+ lda $220b ;should have no symbol
+
+ nop
+
+; test overlapping regions, single file
+ lda $2ffe ;should have no symbol
+ lda $2fff ;Over1-1
+ lda $3000 ;Over1
+ lda $3001 ;Over1+1
+ lda $3002 ;Over2
+ lda $3003 ;Over2+1
+ lda $3004 ;Over2+2
+ lda $3005 ;Over2+3
+ lda $3006 ;Over2a
+ lda $3007 ;Over3+1
+ lda $3008 ;Over3+2
+ lda $3009 ;Over3+3
+ lda $300a ;Over3+4
+ lda $300b ;Over3+5
+ lda $300c ;Over3+6
+ lda $300d ;Over1+13
+ lda $300e ;Over1+14
+ lda $300f ;Over1+15
+ lda $3010 ;should have no symbol
+
+ nop
+
+; test overlapping regions, multiple platform files
+ lda $30fe ;should have no symbol
+ lda $30ff ;SepOver1-1
+ lda $3100 ;SepOver1
+ lda $3101 ;SepOver1+1
+ lda $3102 ;SepOver1+2
+ lda $3103 ;SepOver1+3
+ lda $3104 ;SepOver2+2
+ lda $3105 ;SepOver2+3
+ lda $3106 ;should have no symbol
+
+ nop
+
+; test overlap with local variable
+; EDIT: create variable LocalVar at $41(2b)
+ ldx $3e ;should have no symbol
+ ldx $3f ;should be OverVar-1
+ ldx $40 ;should be OverVar
+ ldx $41 ;should be LocalVar
+ ldx $42 ;should be LocalVar+1
+ ldx $43 ;should be OverVar+3
+ ldx $44 ;should have no symbol
+
+ nop
+
+ lda $3fff ;EDIT: change to "FatConst"
+ lda $4000 ;should have no symbol
+ lda $4001 ;should have no symbol
+
+ rts
diff --git a/SourceGen/Symbol.cs b/SourceGen/Symbol.cs
index 26737e5..70c6c57 100644
--- a/SourceGen/Symbol.cs
+++ b/SourceGen/Symbol.cs
@@ -82,7 +82,8 @@ namespace SourceGen {
public int Value { get; private set; }
///
- /// Symbol origin, e.g. auto-generated or entered by user.
+ /// Symbol origin, e.g. auto-generated or entered by user. Enum values are in
+ /// priority order.
///
public Source SymbolSource { get; private set; }
diff --git a/SourceGen/SymbolTable.cs b/SourceGen/SymbolTable.cs
index 2c03f39..da5830b 100644
--- a/SourceGen/SymbolTable.cs
+++ b/SourceGen/SymbolTable.cs
@@ -31,42 +31,18 @@ namespace SourceGen {
new SortedList(Asm65.Label.LABEL_COMPARER);
///
- /// Same content, but ordered by value. Note the key and the value are the same object.
- ///
- private SortedList mSymbolsByValue =
- new SortedList(new CompareByValue());
-
- ///
- /// Compare two symbols, primarily by value, secondarily by source, and tertiarily
- /// by label. The primary SortedList guarantees that the label is unique, so we
- /// should never have two equal Symbols in the list.
+ /// By-address lookup table. Because symbols can span more than one byte, there may
+ /// be more than one entry per symbol here. If two symbols cover the same address,
+ /// only the highest-priority symbol is kept, so not all symbols are represented here.
///
- /// The type comparison ensures that project symbols appear before platform symbols,
- /// so that you can "overwrite" a platform symbol with the same value.
- ///
- /// TODO(someday): sort by symbol file load order, so you can choose which set of
- /// symbols gets to represent a given address. Mostly useful for zero-page variables.
+ /// This does not contain constants or local variables.
///
- private class CompareByValue : IComparer {
- public int Compare(Symbol a, Symbol b) {
- if (a.Value < b.Value) {
- return -1;
- } else if (a.Value > b.Value) {
- return 1;
- }
-
- if ((int)a.SymbolSource < (int)b.SymbolSource) {
- return -1;
- } else if ((int)a.SymbolSource > (int)b.SymbolSource) {
- return 1;
- }
-
- // Equal values, check string. We'll get a match on Remove or when
- // replacing an entry with itself, but no two Symbols in the list
- // should have the same label.
- return Asm65.Label.LABEL_COMPARER.Compare(a.Label, b.Label);
- }
- }
+ ///
+ /// For efficiency on larger data files, we may want to break this up by bank. That
+ /// way we can do a partial update.
+ ///
+ private Dictionary mSymbolsByAddress =
+ new Dictionary();
///
/// This is incremented whenever the contents of the symbol table change. External
@@ -75,7 +51,7 @@ namespace SourceGen {
///
/// We could theoretically miss something at the 2^32 rollover. Not worried.
///
- public int ChangeSerial { get; private set; }
+ //public int ChangeSerial { get; private set; }
public SymbolTable() { }
@@ -96,15 +72,14 @@ namespace SourceGen {
///
public void Clear() {
mSymbols.Clear();
- mSymbolsByValue.Clear();
- ChangeSerial++;
+ mSymbolsByAddress.Clear();
+ //ChangeSerial++;
}
///
/// Returns the number of symbols in the table.
///
public int Count() {
- Debug.Assert(mSymbolsByValue.Count == mSymbols.Count);
return mSymbols.Count;
}
@@ -116,86 +91,32 @@ namespace SourceGen {
// If Symbol with matching label is in list, this will throw an exception,
// and the by-value add won't happen.
mSymbols.Add(sym.Label, sym);
- mSymbolsByValue.Add(sym, sym);
- ChangeSerial++;
+ AddAddressTableEntry(sym);
+ //ChangeSerial++;
}
///
- /// Finds the specified symbol by label. Throws an exception if it's not found.
+ /// get: Finds the specified symbol by label. Throws an exception if it's not found.
///
- /// Adds the specified symbol to the list, or replaces it if it's already present.
+ /// set: Adds the specified symbol to the list, or replaces it if it's already present.
///
public Symbol this[string key] {
get {
- Debug.Assert(mSymbolsByValue.Count == mSymbols.Count);
return mSymbols[key];
}
set {
- // Replacing {"foo", 1} with ("foo", 2} works correctly for mSymbols, because
- // the label is the unique key. For mSymbolsByValue we have to explicitly
- // remove it, because the entire Symbol is used as the key.
mSymbols.TryGetValue(key, out Symbol oldValue);
- if (oldValue != null) {
- mSymbolsByValue.Remove(oldValue);
- }
mSymbols[key] = value;
- mSymbolsByValue[value] = value;
- ChangeSerial++;
- }
- }
-
- ///
- /// Searches the table for symbols with matching address values. Ignores constants and
- /// variables.
- ///
- /// Value to find.
- /// First matching symbol found, or null if nothing matched.
- public Symbol FindNonVariableByAddress(int value) {
- // Get sorted list of values. This is documented as efficient.
- IList values = mSymbolsByValue.Values;
-
- //for (int i = 0; i < values.Count; i++) {
- // if (values[i].Value == value && values[i].SymbolType != Symbol.Type.Constant) {
- // return values[i];
- // }
- //}
-
- int low = 0;
- int high = values.Count - 1;
- while (low <= high) {
- int mid = (low + high) / 2;
- Symbol midValue = values[mid];
-
- if (midValue.Value == value) {
- // found a match, walk back to find first match
- while (mid > 0 && values[mid - 1].Value == value) {
- mid--;
- }
- // now skip past constants and variables
- while (mid < values.Count && (values[mid].SymbolType == Symbol.Type.Constant ||
- values[mid].SymbolSource == Symbol.Source.Variable)) {
- //Debug.WriteLine("disregarding " + values[mid]);
- mid++;
- }
- if (mid < values.Count && values[mid].Value == value) {
- return values[mid];
- }
- //Debug.WriteLine("Found value " + value + " but only constants");
- return null;
- } else if (midValue.Value < value) {
- // move the low end in
- low = mid + 1;
+ if (oldValue != null) {
+ ReplaceAddressTableEntry(oldValue, value);
} else {
- // move the high end in
- Debug.Assert(midValue.Value > value);
- high = mid - 1;
+ AddAddressTableEntry(value);
}
+ //ChangeSerial++;
}
-
- // not found
- return null;
}
+
///
/// Gets the value associated with the key.
///
@@ -226,8 +147,140 @@ namespace SourceGen {
///
public void Remove(Symbol sym) {
mSymbols.Remove(sym.Label);
- mSymbolsByValue.Remove(sym);
- ChangeSerial++;
+ RemoveAddressTableEntry(sym);
+ //ChangeSerial++;
+ }
+
+ ///
+ /// Adds a symbol to the address table. All affected addresses are updated. If an
+ /// existing symbol is already present at an address, the new or old symbol will be
+ /// selected in priority order.
+ ///
+ /// Symbol to add.
+ private void AddAddressTableEntry(Symbol sym) {
+ if (sym.SymbolType == Symbol.Type.Constant) {
+ return;
+ }
+ if (sym.SymbolSource == Symbol.Source.Variable) {
+ return;
+ }
+
+ int width = 1;
+ if (sym is DefSymbol) {
+ width = ((DefSymbol)sym).DataDescriptor.Length;
+ }
+ // we could restore some older behavior by giving user labels a width of 3, but
+ // we'd have to make sure that they didn't win for addresses outside the file
+
+ for (int i = 0; i < width; i++) {
+ // see if there's already something here
+ mSymbolsByAddress.TryGetValue(sym.Value + i, out Symbol curSym);
+ mSymbolsByAddress[sym.Value + i] = (curSym == null) ? sym :
+ HighestPriority(sym, curSym);
+ }
+ }
+
+ private Symbol HighestPriority(Symbol sym1, Symbol sym2) {
+ // First determinant is symbol source. User labels have highest priority, then
+ // project symbols, then platform symbols, then auto labels.
+ if ((int)sym1.SymbolSource < (int)sym2.SymbolSource) {
+ return sym1;
+ } else if ((int)sym1.SymbolSource > (int)sym2.SymbolSource) {
+ return sym2;
+ }
+
+ // Same source. Are they platform symbols?
+ if (sym1.SymbolSource == Symbol.Source.Platform) {
+ // Sort by file load order. Symbols from files loaded later, which will have
+ // a higher ordinal, have priority.
+ int lo1 = ((DefSymbol)sym1).LoadOrdinal;
+ int lo2 = ((DefSymbol)sym2).LoadOrdinal;
+ if (lo1 > lo2) {
+ return sym1;
+ } else if (lo1 < lo2) {
+ return sym2;
+ }
+ }
+
+ // Same source, so this is e.g. two project symbol definitions that overlap. We
+ // handle this by selecting whichever one was defined closer to the target address,
+ // i.e. whichever one has the higher value.
+ if (sym1.Value > sym2.Value) {
+ return sym1;
+ } else if (sym1.Value < sym2.Value) {
+ return sym2;
+ }
+
+ // In the absence of anything better, we select them alphabetically. (If they have
+ // the same name, value, and source, there's not much to distinguish them anyway.)
+ if (Asm65.Label.LABEL_COMPARER.Compare(sym1.Label, sym2.Label) < 0) {
+ return sym1;
+ } else {
+ return sym2;
+ }
+ }
+
+ ///
+ /// Replaces an entry in the address table. Must be called AFTER the by-label list
+ /// has been updated.
+ ///
+ /// Symbol being replaced.
+ /// New symbol.
+ private void ReplaceAddressTableEntry(Symbol oldSym, Symbol newSym) {
+ RemoveAddressTableEntry(oldSym);
+ AddAddressTableEntry(newSym);
+ }
+
+ ///
+ /// Removes an entry from the address table. Must be called AFTER the by-label list
+ /// has been updated.
+ ///
+ /// Symbol to remove.
+ private void RemoveAddressTableEntry(Symbol sym) {
+ // Easiest thing to do is just regenerate the table. Since we don't track
+ // constants or variables, we can just ignore those.
+ if (sym.SymbolType == Symbol.Type.Constant) {
+ return;
+ }
+ if (sym.SymbolSource == Symbol.Source.Variable) {
+ return;
+ }
+ if (sym.SymbolSource == Symbol.Source.User || sym.SymbolSource == Symbol.Source.Auto) {
+ // These have a width of 1 and can't overlap with anything meaningful... even
+ // if there's a project symbol for the address, it won't be used, because it's
+ // an in-file address. So we can just remove the entry.
+ mSymbolsByAddress.Remove(sym.Value);
+ }
+ RegenerateAddressTable();
+ }
+
+ ///
+ /// Regenerates the entire by-address table, from the contents of the by-label list.
+ ///
+ ///
+ /// This is a little painful, but if a symbol gets removed we don't have a way to
+ /// restore lower-priority items. If this becomes a performance issue we can create
+ /// an ordered list of symbols at each address, but with a few hundred symbols this
+ /// should take very little time.
+ ///
+ private void RegenerateAddressTable() {
+ Debug.WriteLine("SymbolTable: regenerating address table");
+ mSymbolsByAddress.Clear();
+
+ foreach (KeyValuePair kvp in mSymbols) {
+ AddAddressTableEntry(kvp.Value);
+ }
+ }
+
+ ///
+ /// Searches the table for symbols with matching address values. Ignores constants and
+ /// variables.
+ ///
+ /// Address to find.
+ /// First matching symbol found, or null if nothing matched.
+ public Symbol FindNonVariableByAddress(int addr) {
+ mSymbolsByAddress.TryGetValue(addr, out Symbol sym);
+ return sym;
}
}
}
diff --git a/SourceGen/WpfGui/MainWindow.xaml.cs b/SourceGen/WpfGui/MainWindow.xaml.cs
index 6ab6a59..acd3253 100644
--- a/SourceGen/WpfGui/MainWindow.xaml.cs
+++ b/SourceGen/WpfGui/MainWindow.xaml.cs
@@ -162,8 +162,10 @@ namespace SourceGen.WpfGui {
Debug.WriteLine("START at " + DateTime.Now.ToLocalTime());
InitializeComponent();
+ // Prep the crash handler.
+ Misc.AppIdent = "6502bench SourceGen v" + App.ProgramVersion.ToString();
AppDomain.CurrentDomain.UnhandledException +=
- new UnhandledExceptionEventHandler(CommonUtil.Misc.CrashReporter);
+ new UnhandledExceptionEventHandler(Misc.CrashReporter);
listViewSetSelectedItems = codeListView.GetType().GetMethod("SetSelectedItems",
BindingFlags.NonPublic | BindingFlags.Instance);