mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-28 16:31:17 +00:00
Allow explicit widths in project/platform symbols, part 3
Implement multi-byte project/platform symbols by filling out a table of addresses. Each symbol is "painted" into the table, replacing an existing entry if the new entry has higher priority. This allows us to handle overlapping entries, giving boosted priority to platform symbols that are defined in .sym65 files loaded later. The bounds on project/platform symbols are now rigidly defined. If the "nearby" feature is enabled, references to SYM-1 will be picked up, but we won't go hunting for SYM+1 unless the symbol is at least two bytes wide. The cost of adding a symbol to the symbol table is about the same, but we don't have a quick way to remove a symbol. Previously, if two platform symbols had the same value, the symbol with the alphabetically lowest label would win. Now, the symbol defined in the most-recently-loaded file wins. (If you define two symbols with the same value in the same file, it's still resolved alphabetically.) This allows the user to pick the winner by arranging the load order of the platform symbol files. Platform symbols now keep a reference to the file ident of the symbol file that defined them, so we can show the symbols's source in the Info panel. These changes altered the behavior of test 2008-address-changes, which includes some tests on external addresses that are close to labeled internal addresses. The previous behavior essentially treated user labels as being 3 bytes wide and extending outside the file bounds, which was mildly convenient on occasion but felt a little skanky. (We could do with a way to define external symbols relative to internal symbols, for things like the source address of code that gets relocated.) Also, re-enabled some unit tests. Also, added a bit of identifying stuff to CrashLog.txt.
This commit is contained in:
parent
6c9b8fd0e6
commit
0d9814d993
@ -20,6 +20,12 @@ using System.Linq;
|
||||
|
||||
namespace CommonUtil {
|
||||
public static class Misc {
|
||||
/// <summary>
|
||||
/// Application identifier. This is an arbitrary string set by the application. It
|
||||
/// will be included in the crash dump.
|
||||
/// </summary>
|
||||
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:");
|
||||
|
@ -19,9 +19,10 @@ using System.Diagnostics;
|
||||
namespace CommonUtil {
|
||||
/// <summary>
|
||||
/// Version number container. Instances are immutable.
|
||||
///
|
||||
/// See https://semver.org/ for explanation of system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See https://semver.org/ for explanation of system.
|
||||
/// </remarks>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// Pre-release version.
|
||||
/// Pre-release version. Always zero when PreReleaseType is Final.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -59,14 +59,27 @@ namespace SourceGen {
|
||||
/// <summary>
|
||||
/// Platform symbols only: tag used to organize symbols into groups. Used by
|
||||
/// extension scripts.
|
||||
///
|
||||
/// Not serialized.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public string Tag { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public int LoadOrdinal { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string FileIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cross-reference data, generated by the analyzer.
|
||||
/// </summary>
|
||||
@ -146,6 +159,23 @@ namespace SourceGen {
|
||||
Tag = tag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor. Used for platform symbol files.
|
||||
/// </summary>
|
||||
/// <param name="loadOrdinal">Indicates the order in which the defining platform
|
||||
/// symbol file was loaded. Higher numbers indicate later loading, which translates
|
||||
/// to higher priority.</param>
|
||||
/// <param name="fileIdent">Platform symbol file identifier, for the Info panel.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructor. Used for deserialization, when we have a FormatDescriptor and a Symbol.
|
||||
/// </summary>
|
||||
|
@ -533,7 +533,7 @@ namespace SourceGen {
|
||||
///
|
||||
/// Failures here will be reported to the user but aren't fatal.
|
||||
/// </summary>
|
||||
/// <returns>String with all warnings from load process.</returns>
|
||||
/// <returns>Multi-line string with all warnings from load process.</returns>
|
||||
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.
|
||||
/// </summary>
|
||||
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.
|
||||
/// </summary>
|
||||
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 {
|
||||
|
@ -229,6 +229,10 @@ namespace SourceGen {
|
||||
/// to this point so we can report fatal errors directly to the user.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -71,12 +71,13 @@ namespace SourceGen {
|
||||
/// <summary>
|
||||
/// Loads platform symbols.
|
||||
/// </summary>
|
||||
/// <param name="fileIdent">Relative pathname of file to open.</param>
|
||||
/// <param name="fileIdent">External file identifier of symbol file.</param>
|
||||
/// <param name="projectDir">Full path to project directory.</param>
|
||||
/// <param name="loadOrdinal">Platform file load order.</param>
|
||||
/// <param name="report">Report of warnings and errors.</param>
|
||||
/// <returns>True on success (no errors), false on failure.</returns>
|
||||
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
|
||||
|
BIN
SourceGen/SGTestData/2021-external-symbols
Normal file
BIN
SourceGen/SGTestData/2021-external-symbols
Normal file
Binary file not shown.
49
SourceGen/SGTestData/2021-external-symbols-1.sym65
Normal file
49
SourceGen/SGTestData/2021-external-symbols-1.sym65
Normal file
@ -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
|
13
SourceGen/SGTestData/2021-external-symbols-2.sym65
Normal file
13
SourceGen/SGTestData/2021-external-symbols-2.sym65
Normal file
@ -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
|
10
SourceGen/SGTestData/2021-external-symbols-3.sym65
Normal file
10
SourceGen/SGTestData/2021-external-symbols-3.sym65
Normal file
@ -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
|
36
SourceGen/SGTestData/2021-external-symbols.dis65
Normal file
36
SourceGen/SGTestData/2021-external-symbols.dis65
Normal file
@ -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}}}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
100
SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S
Normal file
100
SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S
Normal file
@ -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
|
||||
|
@ -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
|
||||
|
101
SourceGen/SGTestData/Expected/2021-external-symbols_acme.S
Normal file
101
SourceGen/SGTestData/Expected/2021-external-symbols_acme.S
Normal file
@ -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
|
||||
|
101
SourceGen/SGTestData/Expected/2021-external-symbols_cc65.S
Normal file
101
SourceGen/SGTestData/Expected/2021-external-symbols_cc65.S
Normal file
@ -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
|
||||
|
11
SourceGen/SGTestData/Expected/2021-external-symbols_cc65.cfg
Normal file
11
SourceGen/SGTestData/Expected/2021-external-symbols_cc65.cfg
Normal file
@ -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 {}
|
112
SourceGen/SGTestData/Source/2021-external-symbols.S
Normal file
112
SourceGen/SGTestData/Source/2021-external-symbols.S
Normal file
@ -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
|
@ -82,7 +82,8 @@ namespace SourceGen {
|
||||
public int Value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public Source SymbolSource { get; private set; }
|
||||
|
||||
|
@ -31,42 +31,18 @@ namespace SourceGen {
|
||||
new SortedList<string, Symbol>(Asm65.Label.LABEL_COMPARER);
|
||||
|
||||
/// <summary>
|
||||
/// Same content, but ordered by value. Note the key and the value are the same object.
|
||||
/// </summary>
|
||||
private SortedList<Symbol, Symbol> mSymbolsByValue =
|
||||
new SortedList<Symbol, Symbol>(new CompareByValue());
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
private class CompareByValue : IComparer<Symbol> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
/// <remarks>
|
||||
/// For efficiency on larger data files, we may want to break this up by bank. That
|
||||
/// way we can do a partial update.
|
||||
/// </remarks>
|
||||
private Dictionary<int, Symbol> mSymbolsByAddress =
|
||||
new Dictionary<int, Symbol>();
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public int ChangeSerial { get; private set; }
|
||||
//public int ChangeSerial { get; private set; }
|
||||
|
||||
|
||||
public SymbolTable() { }
|
||||
@ -96,15 +72,14 @@ namespace SourceGen {
|
||||
/// </summary>
|
||||
public void Clear() {
|
||||
mSymbols.Clear();
|
||||
mSymbolsByValue.Clear();
|
||||
ChangeSerial++;
|
||||
mSymbolsByAddress.Clear();
|
||||
//ChangeSerial++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in the table.
|
||||
/// </summary>
|
||||
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++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the table for symbols with matching address values. Ignores constants and
|
||||
/// variables.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to find.</param>
|
||||
/// <returns>First matching symbol found, or null if nothing matched.</returns>
|
||||
public Symbol FindNonVariableByAddress(int value) {
|
||||
// Get sorted list of values. This is documented as efficient.
|
||||
IList<Symbol> 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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value associated with the key.
|
||||
/// </summary>
|
||||
@ -226,8 +147,140 @@ namespace SourceGen {
|
||||
/// </summary>
|
||||
public void Remove(Symbol sym) {
|
||||
mSymbols.Remove(sym.Label);
|
||||
mSymbolsByValue.Remove(sym);
|
||||
ChangeSerial++;
|
||||
RemoveAddressTableEntry(sym);
|
||||
//ChangeSerial++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="sym">Symbol to add.</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces an entry in the address table. Must be called AFTER the by-label list
|
||||
/// has been updated.
|
||||
/// </summary>
|
||||
/// <param name="oldSym">Symbol being replaced.</param>
|
||||
/// <param name="newSym">New symbol.</param>
|
||||
private void ReplaceAddressTableEntry(Symbol oldSym, Symbol newSym) {
|
||||
RemoveAddressTableEntry(oldSym);
|
||||
AddAddressTableEntry(newSym);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an entry from the address table. Must be called AFTER the by-label list
|
||||
/// has been updated.
|
||||
/// </summary>
|
||||
/// <param name="sym">Symbol to remove.</param>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates the entire by-address table, from the contents of the by-label list.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
private void RegenerateAddressTable() {
|
||||
Debug.WriteLine("SymbolTable: regenerating address table");
|
||||
mSymbolsByAddress.Clear();
|
||||
|
||||
foreach (KeyValuePair<string, Symbol> kvp in mSymbols) {
|
||||
AddAddressTableEntry(kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the table for symbols with matching address values. Ignores constants and
|
||||
/// variables.
|
||||
/// </summary>
|
||||
/// <param name="addr">Address to find.</param>
|
||||
/// <returns>First matching symbol found, or null if nothing matched.</returns>
|
||||
public Symbol FindNonVariableByAddress(int addr) {
|
||||
mSymbolsByAddress.TryGetValue(addr, out Symbol sym);
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user