Add offset to PlSymbol

It's useful for extension scripts to be able to get the file offset
of symbols in non-addressable regions.  One example of this is CHR
ROM data for an NES cartridge.  However, we were getting the offset
by doing an address-to-offset mapping on the plugin side, which by
definition doesn't work for non-addressable memory.

So we now add the offset to PlSymbol objects for user labels and
address region pre-labels.  The NES visualizer has been updated to
use the new field.

Also, fixed a bogus complaint about bank overruns for non-addressable
regions.
This commit is contained in:
Andy McFadden 2021-10-23 21:26:25 -07:00
parent 1258dd89cb
commit 6e9ff395d2
6 changed files with 103 additions and 47 deletions

View File

@ -81,6 +81,11 @@ namespace PluginCommon {
/// </summary>
public string Tag { get; private set; }
/// <summary>
/// Symbol offset, for user labels and pre-labels; -1 otherwise.
/// </summary>
public int Offset { get; private set; }
/// <summary>
/// Nullary constructor, for deserialization.
@ -92,14 +97,20 @@ namespace PluginCommon {
/// </summary>
/// <param name="label">Symbol label.</param>
/// <param name="value">Symbol value.</param>
/// <param name="width">Width, for platform/project symbols.</param>
/// <param name="source">Symbol source.</param>
/// <param name="type">Symbol type.</param>
/// <param name="tag">Symbol group tag.</param>
public PlSymbol(string label, int value, int width, Source source, Type type, string tag) {
/// <param name="offset">Offset, for user labels and pre-labels; -1 otherwise.</param>
public PlSymbol(string label, int value, int width, Source source, Type type, string tag,
int offset) {
Label = label;
Value = value;
Width = width;
SymbolSource = source;
SymbolType = type;
Tag = tag;
Offset = offset;
}
/// <summary>

View File

@ -407,6 +407,10 @@ namespace SourceGen.AsmGen {
if (attr.Length != instrBytes) {
// This instruction has another instruction inside it. Throw out what we
// computed and just output as bytes.
// TODO: in some odd situations we can split something that doesn't need
// to be split (see note at end of #107). Working around the problem at
// this stage is a little awkward because I think we need to check for the
// presence of labels on one or more later lines.
gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr);
} else if (isPcRelBankWrap && gen.Quirks.NoPcRelBankWrap) {
// Some assemblers have trouble generating PC-relative operands that wrap

View File

@ -759,7 +759,7 @@ namespace SourceGen {
IEnumerator<AddressMap.AddressChange> addrIter = AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
AddressMap.AddressChange change = addrIter.Current;
if (!change.IsStart) {
if (!change.IsStart || change.Address == Address.NON_ADDR) {
continue;
}
AddressMap.AddressRegion region = change.Region;
@ -1552,46 +1552,7 @@ namespace SourceGen {
}
}
// Create a mapping from label (which must be unique) to file offset. This
// is different from UserLabels (which only has user-created labels, and is
// sorted by offset) and SymbolTable (which has constants and platform symbols,
// and uses the address as the value rather than the offset).
SortedList<string, int> labelList = new SortedList<string, int>(mFileData.Length,
Asm65.Label.LABEL_COMPARER);
for (int offset = 0; offset < mAnattribs.Length; offset++) {
Anattrib attr = mAnattribs[offset];
if (attr.Symbol != null) {
try {
labelList.Add(attr.Symbol.Label, offset);
} catch (ArgumentException ex) {
// Duplicate UserLabel entries are stripped when projects are loaded, but
// it might be possible to cause this by hiding/unhiding a label (e.g.
// using a code start tag to place it in the middle of an instruction).
// Just ignore the duplicate.
Debug.WriteLine("Xref ignoring duplicate label '" + attr.Symbol.Label +
"': " + ex.Message);
}
}
}
// Add all valid address region pre-labels. Duplicates of user labels will be
// rejected. Note the references will appear on the line for the next file offset,
// not the pre-label itself, because we need to associate it with a file offset.
IEnumerator<AddressMap.AddressChange> addrIter = AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
AddressMap.AddressChange change = addrIter.Current;
if (!change.IsStart) {
continue;
}
if (change.Region.HasValidPreLabel) {
try {
labelList.Add(change.Region.PreLabel, change.Region.Offset);
} catch (ArgumentException ex) {
Debug.WriteLine("Xref ignoring pre-label duplicate '" +
change.Region.PreLabel + "': " + ex.Message);
}
}
}
SortedList<string, int> labelList = CreateLabelToOffsetMap();
// No particular reason to do this here, but it's convenient.
ByteCounts.Reset();
@ -1802,6 +1763,54 @@ namespace SourceGen {
return xset; // will be null if not found
}
/// <summary>
/// Creates a mapping from label (which must be unique) to file offset. This
/// is different from UserLabels (which only has user-created labels, and is
/// sorted by offset) and SymbolTable (which has constants and platform symbols,
/// and uses the address as the value rather than the offset).
///
/// We use Anattribs to ensure that we only include visible labels.
/// </summary>
private SortedList<string, int> CreateLabelToOffsetMap() {
SortedList<string, int> labelList = new SortedList<string, int>(mFileData.Length,
Asm65.Label.LABEL_COMPARER);
for (int offset = 0; offset < mAnattribs.Length; offset++) {
Anattrib attr = mAnattribs[offset];
if (attr.Symbol != null) {
try {
labelList.Add(attr.Symbol.Label, offset);
} catch (ArgumentException ex) {
// Duplicate UserLabel entries are stripped when projects are loaded, but
// it might be possible to cause this by hiding/unhiding a label (e.g.
// using a code start tag to place it in the middle of an instruction).
// Just ignore the duplicate.
Debug.WriteLine("Xref ignoring duplicate label '" + attr.Symbol.Label +
"': " + ex.Message);
}
}
}
// Add all valid address region pre-labels. Duplicates of user labels will be
// rejected. Note the references will appear on the line for the next file offset,
// not the pre-label itself, because we need to associate it with a file offset.
IEnumerator<AddressMap.AddressChange> addrIter = AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
AddressMap.AddressChange change = addrIter.Current;
if (!change.IsStart) {
continue;
}
if (change.Region.HasValidPreLabel) {
try {
labelList.Add(change.Region.PreLabel, change.Region.Offset);
} catch (ArgumentException ex) {
Debug.WriteLine("Xref ignoring pre-label duplicate '" +
change.Region.PreLabel + "': " + ex.Message);
}
}
}
return labelList;
}
/// <summary>
/// Replaces generic auto-labels with fancier versions generated from xrefs.
/// </summary>

View File

@ -3177,7 +3177,7 @@ namespace SourceGen {
public void GoToLabel(Symbol sym) {
int offset = mProject.FindLabelOffsetByName(sym.Label);
if (offset >= 0) {
// TODO(someday): jump to correct line for address region pre-labels
// TODO(someday): jump to symbol line, not arstart, for address region pre-labels
GoToLocation(new NavStack.Location(offset, 0, NavStack.GoToMode.JumpToCodeData),
true);
} else {

View File

@ -115,8 +115,7 @@ namespace RuntimeData.Nintendo {
foreach (PlSymbol sym in plSyms) {
if (sym.Label == CHR_ROM) {
int addr = sym.Value;
mChrRomOffset = mAddrTrans.AddressToOffset(0, addr);
mChrRomOffset = sym.Offset;
break;
}
}

View File

@ -327,18 +327,51 @@ namespace SourceGen.Sandbox {
/// <summary>
/// Gathers a list of symbols from the project's symbol table.
/// </summary>
/// <remarks>
/// Remember that we need to set this up before code analysis runs, so many of the
/// secondary data structures (like Anattribs) won't be available.
/// </remarks>
private List<PlSymbol> GeneratePlSymbolList() {
List<PlSymbol> plSymbols = new List<PlSymbol>();
SymbolTable symTab = mProject.SymbolTable;
// UserLabels maps offset to Symbol. Create the reverse mapping.
Dictionary<Symbol, int> symbolOffsets =
new Dictionary<Symbol, int>(mProject.UserLabels.Count);
foreach (KeyValuePair<int, Symbol> kvp in mProject.UserLabels) {
symbolOffsets[kvp.Value] = kvp.Key;
}
// Add in the address region pre-labels.
IEnumerator<AddressMap.AddressChange> addrIter = mProject.AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
AddressMap.AddressChange change = addrIter.Current;
if (!change.IsStart) {
continue;
}
if (change.Region.HasValidPreLabel) {
Symbol newSym = new Symbol(change.Region.PreLabel,
change.Region.PreLabelAddress, Symbol.Source.AddrPreLabel,
Symbol.Type.ExternalAddr, Symbol.LabelAnnotation.None);
symbolOffsets[newSym] = change.Region.Offset;
}
}
foreach (Symbol sym in symTab) {
PlSymbol.Source plsSource;
int symOff, offset = -1;
switch (sym.SymbolSource) {
case Symbol.Source.User:
plsSource = PlSymbol.Source.User;
if (symbolOffsets.TryGetValue(sym, out symOff)) {
offset = symOff;
}
break;
case Symbol.Source.AddrPreLabel:
plsSource = PlSymbol.Source.AddrPreLabel;
if (symbolOffsets.TryGetValue(sym, out symOff)) {
offset = symOff;
}
break;
case Symbol.Source.Project:
plsSource = PlSymbol.Source.Project;
@ -381,8 +414,8 @@ namespace SourceGen.Sandbox {
tag = defSym.Tag;
}
plSymbols.Add(new PlSymbol(sym.Label, sym.Value, width, plsSource, plsType, tag));
plSymbols.Add(new PlSymbol(sym.Label, sym.Value, width, plsSource, plsType, tag,
offset));
}
return plSymbols;