mirror of
https://github.com/fadden/6502bench.git
synced 2025-01-02 18:30:41 +00:00
Label rework, part 5
Implemented assembly source generation of non-unique local labels. The new 2023-non-unique-labels test exercises various edge cases (though we're still missing local variable interaction). The format of uniquified labels changed slightly, so the expected output of 2012-label-localizer needed to be updated. This changes the "no opcode mnemonics" and "mask leading underscores" functions into integrated parts of the label localization process.
This commit is contained in:
parent
4e08810278
commit
88c56616f7
@ -224,8 +224,8 @@ namespace SourceGen.AsmGen {
|
|||||||
// While '.' labels are limited to the current zone, '@' labels are visible
|
// While '.' labels are limited to the current zone, '@' labels are visible
|
||||||
// between global labels. (This is poorly documented.)
|
// between global labels. (This is poorly documented.)
|
||||||
mLocalizer.LocalPrefix = "@";
|
mLocalizer.LocalPrefix = "@";
|
||||||
|
mLocalizer.QuirkNoOpcodeMnemonics = true;
|
||||||
mLocalizer.Analyze();
|
mLocalizer.Analyze();
|
||||||
mLocalizer.FixOpcodeLabels();
|
|
||||||
|
|
||||||
// Use UTF-8 encoding, without a byte-order mark.
|
// Use UTF-8 encoding, without a byte-order mark.
|
||||||
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
||||||
|
@ -218,9 +218,9 @@ namespace SourceGen.AsmGen {
|
|||||||
|
|
||||||
mLocalizer = new LabelLocalizer(Project);
|
mLocalizer = new LabelLocalizer(Project);
|
||||||
mLocalizer.LocalPrefix = "@";
|
mLocalizer.LocalPrefix = "@";
|
||||||
mLocalizer.QuirkVariablesEndScope = true;
|
mLocalizer.QuirkVariablesEndScope = true; // https://github.com/cc65/cc65/issues/938
|
||||||
|
mLocalizer.QuirkNoOpcodeMnemonics = true;
|
||||||
mLocalizer.Analyze();
|
mLocalizer.Analyze();
|
||||||
mLocalizer.FixOpcodeLabels();
|
|
||||||
|
|
||||||
// Use UTF-8 encoding, without a byte-order mark.
|
// Use UTF-8 encoding, without a byte-order mark.
|
||||||
using (StreamWriter sw = new StreamWriter(cfgName, false, new UTF8Encoding(false))) {
|
using (StreamWriter sw = new StreamWriter(cfgName, false, new UTF8Encoding(false))) {
|
||||||
|
@ -191,8 +191,8 @@ namespace SourceGen.AsmGen {
|
|||||||
|
|
||||||
mLocalizer = new LabelLocalizer(Project);
|
mLocalizer = new LabelLocalizer(Project);
|
||||||
mLocalizer.LocalPrefix = ":";
|
mLocalizer.LocalPrefix = ":";
|
||||||
|
// don't need to set QuirkNoOpcodeMnemonics
|
||||||
mLocalizer.Analyze();
|
mLocalizer.Analyze();
|
||||||
//mLocalizer.FixOpcodeLabels();
|
|
||||||
|
|
||||||
// Use UTF-8 encoding, without a byte-order mark.
|
// Use UTF-8 encoding, without a byte-order mark.
|
||||||
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
||||||
|
@ -230,9 +230,8 @@ namespace SourceGen.AsmGen {
|
|||||||
|
|
||||||
mLocalizer = new LabelLocalizer(Project);
|
mLocalizer = new LabelLocalizer(Project);
|
||||||
mLocalizer.LocalPrefix = "_";
|
mLocalizer.LocalPrefix = "_";
|
||||||
|
mLocalizer.QuirkNoOpcodeMnemonics = true;
|
||||||
mLocalizer.Analyze();
|
mLocalizer.Analyze();
|
||||||
mLocalizer.MaskLeadingUnderscores();
|
|
||||||
mLocalizer.FixOpcodeLabels();
|
|
||||||
|
|
||||||
// Use UTF-8 encoding, without a byte-order mark.
|
// Use UTF-8 encoding, without a byte-order mark.
|
||||||
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
||||||
|
@ -40,7 +40,7 @@ but this would cause an error:
|
|||||||
because the local symbol table is cleared when a global symbol is encountered.
|
because the local symbol table is cleared when a global symbol is encountered.
|
||||||
|
|
||||||
Another common form allows backward references to labels that don't go out of scope until
|
Another common form allows backward references to labels that don't go out of scope until
|
||||||
they're re-used. This is useful for short loops.
|
they're re-used. This is useful for short loops. (We use this for variables.)
|
||||||
|
|
||||||
As a further limitation, assemblers seem to want the first label encountered in a program
|
As a further limitation, assemblers seem to want the first label encountered in a program
|
||||||
to be global.
|
to be global.
|
||||||
@ -78,6 +78,10 @@ name. This must be applied to both labels and operands.
|
|||||||
|
|
||||||
namespace SourceGen.AsmGen {
|
namespace SourceGen.AsmGen {
|
||||||
public class LabelLocalizer {
|
public class LabelLocalizer {
|
||||||
|
// Prefix string to use for labels that start with '_' when generating code for
|
||||||
|
// assemblers that assign a special meaning to leading underscores.
|
||||||
|
private const string NO_UNDER_PFX = "X";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A pairing of an offset with a label string. (Essentially mAnattribs[n].Symbol
|
/// A pairing of an offset with a label string. (Essentially mAnattribs[n].Symbol
|
||||||
/// with all the fluff trimmed away.)
|
/// with all the fluff trimmed away.)
|
||||||
@ -85,8 +89,8 @@ namespace SourceGen.AsmGen {
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The label string isn't actually all that useful, since we can pull it back out
|
/// The label string isn't actually all that useful, since we can pull it back out
|
||||||
/// of anattrib, but it makes life a little easier during debugging. These get
|
/// of anattrib, but it makes life a little easier during debugging. These get
|
||||||
/// put into a List, so switching to a plain int offset doesn't necessarily help us
|
/// put into a List, so simply storing a plain int offset it's much better (in terms
|
||||||
/// much because the ints get boxed.
|
/// of memory and allocations) because the ints get boxed.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private class OffsetLabel {
|
private class OffsetLabel {
|
||||||
public int Offset { get; private set; }
|
public int Offset { get; private set; }
|
||||||
@ -136,6 +140,12 @@ namespace SourceGen.AsmGen {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool QuirkVariablesEndScope { get; set; }
|
public bool QuirkVariablesEndScope { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set this if global variables are not allowed to have the same name as an opcode
|
||||||
|
/// mnemonic.
|
||||||
|
/// </summary>
|
||||||
|
public bool QuirkNoOpcodeMnemonics { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Project reference.
|
/// Project reference.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -175,6 +185,8 @@ namespace SourceGen.AsmGen {
|
|||||||
public void Analyze() {
|
public void Analyze() {
|
||||||
Debug.Assert(LocalPrefix.Length > 0);
|
Debug.Assert(LocalPrefix.Length > 0);
|
||||||
|
|
||||||
|
// Init global flags list. An entry is set if the associated offset has a global
|
||||||
|
// label. It will be false if the entry has a local label, or no label.
|
||||||
mGlobalFlags.SetAll(false);
|
mGlobalFlags.SetAll(false);
|
||||||
|
|
||||||
// Currently we only support the "local labels have scope that ends at a global
|
// Currently we only support the "local labels have scope that ends at a global
|
||||||
@ -191,34 +203,142 @@ namespace SourceGen.AsmGen {
|
|||||||
//
|
//
|
||||||
// The current algorithm uses a straightforward O(n^2) approach.
|
// The current algorithm uses a straightforward O(n^2) approach.
|
||||||
|
|
||||||
|
//
|
||||||
// Step 1: generate source/target pairs and global label list
|
// Step 1: generate source/target pairs and global label list
|
||||||
|
//
|
||||||
GenerateLists();
|
GenerateLists();
|
||||||
|
|
||||||
|
//
|
||||||
// Step 2: walk through the list of global symbols, identifying source/target
|
// Step 2: walk through the list of global symbols, identifying source/target
|
||||||
// pairs that cross them. If a pair matches, the target label is added to the
|
// pairs that cross them. If a pair matches, the target label is added to the
|
||||||
// mGlobalLabels list, and removed from the pair list.
|
// end of the mGlobalLabels list, and removed from the pair list.
|
||||||
|
//
|
||||||
|
// When we're done, mGlobalFlags[] will identify the offsets with global labels.
|
||||||
|
//
|
||||||
for (int index = 0; index < mGlobalLabels.Count; index++) {
|
for (int index = 0; index < mGlobalLabels.Count; index++) {
|
||||||
FindIntersectingPairs(mGlobalLabels[index]);
|
FindIntersectingPairs(mGlobalLabels[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: for each local label, add an entry to the map with the appropriate
|
// We're done with these. Take out the trash.
|
||||||
// local-label syntax.
|
mGlobalLabels.Clear();
|
||||||
|
mOffsetPairs.Clear();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Step 3: remap global labels. There are three reasons we might need to do this:
|
||||||
|
// (1) It has a leading underscore AND LocalPrefix is '_'.
|
||||||
|
// (2) The label matches an opcode mnemonic (case-insensitive) AND NoOpcodeMnemonics
|
||||||
|
// is set.
|
||||||
|
// (3) It's a non-unique local that got promoted to global.
|
||||||
|
//
|
||||||
|
// In each case we need to modify the label to meet the assembler requirements, and
|
||||||
|
// then modify the label until it's unique.
|
||||||
|
//
|
||||||
LabelMap = new Dictionary<string, string>();
|
LabelMap = new Dictionary<string, string>();
|
||||||
|
Dictionary<string, string> allGlobalLabels = new Dictionary<string, string>();
|
||||||
|
bool remapUnders = (LocalPrefix == "_");
|
||||||
|
|
||||||
|
Dictionary<string, Asm65.OpDef> opNames = null;
|
||||||
|
if (QuirkNoOpcodeMnemonics) {
|
||||||
|
// Create a searchable list of opcode names using the current CPU definition.
|
||||||
|
// (All tested assemblers that failed on opcode names only did so for names
|
||||||
|
// that were part of the current definition, e.g. "TSB" was accepted as a label
|
||||||
|
// when the CPU was set to 6502.)
|
||||||
|
opNames = new Dictionary<string, Asm65.OpDef>();
|
||||||
|
Asm65.CpuDef cpuDef = mProject.CpuDef;
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
Asm65.OpDef op = cpuDef.GetOpDef(i);
|
||||||
|
// There may be multiple entries with the same name (e.g. "NOP"). That's fine.
|
||||||
|
opNames[op.Mnemonic.ToUpperInvariant()] = op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < mProject.FileDataLength; i++) {
|
for (int i = 0; i < mProject.FileDataLength; i++) {
|
||||||
if (mGlobalFlags[i]) {
|
if (!mGlobalFlags[i]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
||||||
if (sym == null) {
|
if (sym == null) {
|
||||||
|
// Should only happen when we insert a dummy global label for the
|
||||||
|
// "variables end scope" quirk.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelMap[sym.Label] = LocalPrefix + sym.Label;
|
string newLabel = sym.LabelWithoutTag;
|
||||||
|
if (remapUnders && newLabel[0] == '_') {
|
||||||
|
newLabel = NO_UNDER_PFX + newLabel;
|
||||||
|
// This could cause a conflict with an existing label. It's rare but
|
||||||
|
// possible.
|
||||||
|
if (allGlobalLabels.ContainsKey(newLabel)) {
|
||||||
|
newLabel = MakeUnique(newLabel, allGlobalLabels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opNames != null && opNames.ContainsKey(newLabel.ToUpperInvariant())) {
|
||||||
|
// Clashed with mnemonic. Uniquify it.
|
||||||
|
newLabel = MakeUnique(newLabel, allGlobalLabels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We might have remapped something earlier and it happens to match this label.
|
||||||
|
// If so, we can either remap the current label, or remap the previous label
|
||||||
|
// a little harder. The easiest thing to do is remap the current label.
|
||||||
|
if (allGlobalLabels.ContainsKey(newLabel)) {
|
||||||
|
newLabel = MakeUnique(newLabel, allGlobalLabels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've changed it, add it to the map.
|
||||||
|
if (newLabel != sym.Label) {
|
||||||
|
LabelMap[sym.Label] = newLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
allGlobalLabels.Add(newLabel, newLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take out the trash.
|
//
|
||||||
mGlobalLabels.Clear();
|
// Step 4: remap local labels. There are two operations here.
|
||||||
mOffsetPairs.Clear();
|
//
|
||||||
|
// For each pair of global labels that have locals between them, we need to walk
|
||||||
|
// through the locals and confirm that they don't clash with each other. If they
|
||||||
|
// do, we need to uniquify them within the local scope. (This is only an issue
|
||||||
|
// for non-unique locals.)
|
||||||
|
//
|
||||||
|
// Once a unique name has been found, we add an entry to LabelMap that has the
|
||||||
|
// label with the LocalPrefix and without the non-unique tag.
|
||||||
|
//
|
||||||
|
// We also need to deal with symbols with a leading underscore when
|
||||||
|
// LocalPrefix is '_'.
|
||||||
|
//
|
||||||
|
int startGlobal = -1;
|
||||||
|
int numLocals = 0;
|
||||||
|
|
||||||
|
// Allocate a Dictionary here and pass it through so we don't have to allocate
|
||||||
|
// a new one each time.
|
||||||
|
Dictionary<string, string> scopedLocals = new Dictionary<string, string>();
|
||||||
|
for (int i = 0; i < mProject.FileDataLength; i++) {
|
||||||
|
if (mGlobalFlags[i]) {
|
||||||
|
if (startGlobal < 0) {
|
||||||
|
// very first one
|
||||||
|
startGlobal = i;
|
||||||
|
continue;
|
||||||
|
} else if (numLocals > 0) {
|
||||||
|
// There were locals following the previous global. Process them.
|
||||||
|
ProcessLocals(startGlobal, i, scopedLocals);
|
||||||
|
startGlobal = i;
|
||||||
|
numLocals = 0;
|
||||||
|
} else {
|
||||||
|
// Two adjacent globals.
|
||||||
|
startGlobal = i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not a global. Is there a local symbol here?
|
||||||
|
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
||||||
|
if (sym != null) {
|
||||||
|
numLocals++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (numLocals != 0) {
|
||||||
|
// do the last bit
|
||||||
|
ProcessLocals(startGlobal, mProject.FileDataLength, scopedLocals);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -237,10 +357,11 @@ namespace SourceGen.AsmGen {
|
|||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
for (int offset = 0; offset < mProject.FileDataLength; offset++) {
|
for (int offset = 0; offset < mProject.FileDataLength; offset++) {
|
||||||
|
// Find all user labels and auto labels.
|
||||||
Symbol sym = mProject.GetAnattrib(offset).Symbol;
|
Symbol sym = mProject.GetAnattrib(offset).Symbol;
|
||||||
|
|
||||||
// In cc65, variable declarations end the local label scope. We insert a
|
// In cc65, variable declarations end the local label scope. We insert a
|
||||||
// fake global symbol if we counter a table with a nonzero number of entries.
|
// fake global symbol if we encounter a table with a nonzero number of entries.
|
||||||
if (QuirkVariablesEndScope &&
|
if (QuirkVariablesEndScope &&
|
||||||
mProject.LvTables.TryGetValue(offset, out LocalVariableTable value) &&
|
mProject.LvTables.TryGetValue(offset, out LocalVariableTable value) &&
|
||||||
value.Count > 0) {
|
value.Count > 0) {
|
||||||
@ -253,7 +374,7 @@ namespace SourceGen.AsmGen {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first || sym.SymbolType != Symbol.Type.LocalOrGlobalAddr) {
|
if (first || !sym.CanBeLocal) {
|
||||||
first = false;
|
first = false;
|
||||||
mGlobalFlags[offset] = true;
|
mGlobalFlags[offset] = true;
|
||||||
mGlobalLabels.Add(new OffsetLabel(offset, sym.Label));
|
mGlobalLabels.Add(new OffsetLabel(offset, sym.Label));
|
||||||
@ -285,12 +406,11 @@ namespace SourceGen.AsmGen {
|
|||||||
private void FindIntersectingPairs(OffsetLabel glabel) {
|
private void FindIntersectingPairs(OffsetLabel glabel) {
|
||||||
Debug.Assert(mGlobalFlags[glabel.Offset]);
|
Debug.Assert(mGlobalFlags[glabel.Offset]);
|
||||||
|
|
||||||
int globOffset = glabel.Offset;
|
int glabOffset = glabel.Offset;
|
||||||
for (int i = 0; i < mOffsetPairs.Count; i++) {
|
for (int i = 0; i < mOffsetPairs.Count; i++) {
|
||||||
OffsetPair pair = mOffsetPairs[i];
|
OffsetPair pair = mOffsetPairs[i];
|
||||||
|
|
||||||
// If the destination was marked global earlier, remove and ignore this entry.
|
// If the destination was marked global earlier, remove the entry and move on.
|
||||||
// Note this also means that pair.DstOffset != label.Offset.
|
|
||||||
if (mGlobalFlags[pair.DstOffset]) {
|
if (mGlobalFlags[pair.DstOffset]) {
|
||||||
mOffsetPairs.RemoveAt(i);
|
mOffsetPairs.RemoveAt(i);
|
||||||
i--;
|
i--;
|
||||||
@ -301,15 +421,16 @@ namespace SourceGen.AsmGen {
|
|||||||
// offsets.
|
// offsets.
|
||||||
//
|
//
|
||||||
// If the reference source is itself a global label, it can reference local
|
// If the reference source is itself a global label, it can reference local
|
||||||
// labels forward, but not backward. We need to take that into account for
|
// labels forward, but not backward (i.e. if it crosses itself, the destination
|
||||||
// the case where label.Offset==pair.SrcOffset.
|
// must be made global). We need to take that into account for the case where
|
||||||
|
// label.Offset==pair.SrcOffset.
|
||||||
bool intersect;
|
bool intersect;
|
||||||
if (pair.SrcOffset < pair.DstOffset) {
|
if (pair.SrcOffset < pair.DstOffset) {
|
||||||
// Forward reference. src==glob is ok
|
// Forward reference. src==glab is ok
|
||||||
intersect = pair.SrcOffset < globOffset && pair.DstOffset >= globOffset;
|
intersect = pair.SrcOffset < glabOffset && pair.DstOffset >= glabOffset;
|
||||||
} else {
|
} else {
|
||||||
// Backward reference. src==glob is bad
|
// Backward reference. src==glab is bad
|
||||||
intersect = pair.SrcOffset >= globOffset && pair.DstOffset <= globOffset;
|
intersect = pair.SrcOffset >= glabOffset && pair.DstOffset <= glabOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intersect) {
|
if (intersect) {
|
||||||
@ -329,163 +450,67 @@ namespace SourceGen.AsmGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adjusts the label map so that only local variables start with an underscore ('_').
|
/// Generates map entries for local labels defined between the two globals.
|
||||||
/// This is necessary for assemblers like 64tass that use a leading underscore to
|
|
||||||
/// indicate that a label should be local.
|
|
||||||
///
|
|
||||||
/// Only call this if underscores are used to indicate local labels.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <param name="startGlobal">Offset of first global.</param>
|
||||||
/// This may be called even if label localization is disabled. In that case we just
|
/// <param name="endGlobal">Offset of second global. If this range is at the end of the
|
||||||
/// create an empty label map and populate as needed.
|
/// file, this offset may be one past the end.</param>
|
||||||
/// </remarks>
|
/// <param name="scopedLocals">Work object (minor alloc optimization).</param>
|
||||||
public void MaskLeadingUnderscores() {
|
private void ProcessLocals(int startGlobal, int endGlobal,
|
||||||
bool allGlobal = false;
|
Dictionary<string, string> scopedLocals) {
|
||||||
if (LabelMap == null) {
|
//Debug.WriteLine("ProcessLocals: +" + startGlobal.ToString("x6") +
|
||||||
allGlobal = true;
|
// " - +" + endGlobal.ToString("x6"));
|
||||||
LabelMap = new Dictionary<string, string>();
|
scopedLocals.Clear();
|
||||||
}
|
bool remapUnders = (LocalPrefix == "_");
|
||||||
|
|
||||||
// Throw out the original local label generation. We're going to redo the
|
for (int i = startGlobal + 1; i < endGlobal; i++) {
|
||||||
// label map generation step.
|
Debug.Assert(!mGlobalFlags[i]);
|
||||||
LabelMap.Clear();
|
|
||||||
|
|
||||||
// Use this to test for uniqueness. We add all labels here as we go, not just the
|
|
||||||
// ones being remapped. For each label we either add the original or the localized
|
|
||||||
// form.
|
|
||||||
SortedList<string, string> allLabels = new SortedList<string, string>();
|
|
||||||
|
|
||||||
for (int i = 0; i < mProject.FileDataLength; i++) {
|
|
||||||
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
||||||
if (sym == null) {
|
if (sym == null) {
|
||||||
// No label at this offset.
|
continue; // no label here
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string newLabel;
|
string newLabel = sym.LabelWithoutTag;
|
||||||
if (allGlobal || mGlobalFlags[i]) {
|
if (remapUnders && newLabel[0] == '_') {
|
||||||
// Global symbol. Don't let it start with '_'.
|
newLabel = LocalPrefix + NO_UNDER_PFX + newLabel;
|
||||||
if (sym.Label.StartsWith("_")) {
|
|
||||||
// There's an underscore here that was added by the user. Stick some
|
|
||||||
// other character in front.
|
|
||||||
newLabel = "X" + sym.Label;
|
|
||||||
} else {
|
|
||||||
// No change needed.
|
|
||||||
newLabel = sym.Label;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Local symbol.
|
newLabel = LocalPrefix + newLabel;
|
||||||
if (sym.Label.StartsWith("_")) {
|
|
||||||
// The original starts with one or more underscores. Adding another
|
|
||||||
// will create a "__" label, which is reserved in 64tass.
|
|
||||||
newLabel = "_X" + sym.Label;
|
|
||||||
} else {
|
|
||||||
newLabel = "_" + sym.Label;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure it's unique.
|
if (scopedLocals.ContainsKey(newLabel)) {
|
||||||
string uniqueLabel = newLabel;
|
newLabel = MakeUnique(newLabel, scopedLocals);
|
||||||
int uval = 0;
|
|
||||||
while (allLabels.ContainsKey(uniqueLabel)) {
|
|
||||||
uval++;
|
|
||||||
uniqueLabel = newLabel + uval.ToString();
|
|
||||||
}
|
}
|
||||||
allLabels.Add(uniqueLabel, uniqueLabel);
|
|
||||||
|
|
||||||
// If it's different, add it to the label map.
|
// Map from the original symbol label to the local form. This works for
|
||||||
if (sym.Label != uniqueLabel) {
|
// unique and non-unique locals.
|
||||||
LabelMap.Add(sym.Label, uniqueLabel);
|
LabelMap[sym.Label] = newLabel;
|
||||||
}
|
|
||||||
|
scopedLocals.Add(newLabel, newLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.WriteLine("UMAP: allcount=" + allLabels.Count + " mapcount=" + LabelMap.Count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remaps labels that match opcode names. Updated names will be added to LabelMap.
|
/// Alters a label to make it unique. This may be called with a label that is unique
|
||||||
/// This should be run after localization and underscore concealment have finished.
|
/// but illegal (e.g. an instruction mnemonic), so we guarantee that the label returned
|
||||||
|
/// is different from the argument.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Most assemblers don't like it if you create a label with the same name as an
|
/// We can't put a '_' at the front or an 'L' at the end (LDAL), since that could run
|
||||||
/// opcode, e.g. "jmp LSR" doesn't work. We can use the label map to work around
|
/// afoul of the things we're trying to work around. We don't want to mess with the
|
||||||
/// the issue.
|
/// start of the string since it may or may not have the LocalPrefix on it.
|
||||||
///
|
|
||||||
/// Most assemblers regard mnemonics as case-insensitive, even if labels are
|
|
||||||
/// case-sensitive, so we want to remap both "lsr" and "LSR".
|
|
||||||
///
|
|
||||||
/// This doesn't really have anything to do with label localization other than that
|
|
||||||
/// we're updating the label remap table.
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void FixOpcodeLabels() {
|
/// <param name="label">Label to uniquify.</param>
|
||||||
if (LabelMap == null) {
|
/// <param name="allLabels">Dictionary to uniquify against.</param>
|
||||||
LabelMap = new Dictionary<string, string>();
|
/// <returns>Modified label</returns>
|
||||||
}
|
private static string MakeUnique(string label, Dictionary<string, string> allLabels) {
|
||||||
|
int uval = 0;
|
||||||
|
string uniqueLabel;
|
||||||
|
do {
|
||||||
|
uval++;
|
||||||
|
uniqueLabel = label + uval.ToString();
|
||||||
|
} while (allLabels.ContainsKey(uniqueLabel));
|
||||||
|
|
||||||
// Create a searchable list of opcode names using the current CPU definition.
|
return uniqueLabel;
|
||||||
// (All tested assemblers that failed on opcode names only did so for names
|
|
||||||
// that were part of the current definition, e.g. "TSB" was accepted as a label
|
|
||||||
// when the CPU was set to 6502.)
|
|
||||||
Dictionary<string, Asm65.OpDef> opnames = new Dictionary<string, Asm65.OpDef>();
|
|
||||||
Asm65.CpuDef cpuDef = mProject.CpuDef;
|
|
||||||
for (int i = 0; i < 256; i++) {
|
|
||||||
Asm65.OpDef op = cpuDef.GetOpDef(i);
|
|
||||||
// There may be multiple entries with the same name (e.g. "NOP"). That's fine.
|
|
||||||
opnames[op.Mnemonic.ToUpperInvariant()] = op;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a list of all labels, for uniqueness testing. If a label has been
|
|
||||||
// remapped, we add the remapped entry.
|
|
||||||
// (All tested assemblers that failed on opcode names only did so for names
|
|
||||||
// in their non-localized form. While "LSR" failed, "@LSR", "_LSR", ".LSR", etc.
|
|
||||||
// were accepted. So if it was remapped by the localizer, we don't need to
|
|
||||||
// worry about it.)
|
|
||||||
SortedList<string, string> allLabels = new SortedList<string, string>();
|
|
||||||
for (int i = 0; i < mProject.FileDataLength; i++) {
|
|
||||||
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
|
||||||
if (sym == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
LabelMap.TryGetValue(sym.Label, out string mapLabel);
|
|
||||||
if (mapLabel != null) {
|
|
||||||
allLabels.Add(mapLabel, mapLabel);
|
|
||||||
} else {
|
|
||||||
allLabels.Add(sym.Label, sym.Label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now run through the list of labels, looking for any that match opcode
|
|
||||||
// mnemonics.
|
|
||||||
for (int i = 0; i < mProject.FileDataLength; i++) {
|
|
||||||
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
|
||||||
if (sym == null) {
|
|
||||||
// No label at this offset.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
string cmpLabel = sym.Label;
|
|
||||||
if (LabelMap.TryGetValue(sym.Label, out string mapLabel)) {
|
|
||||||
cmpLabel = mapLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opnames.ContainsKey(cmpLabel.ToUpperInvariant())) {
|
|
||||||
//Debug.WriteLine("Remapping label (op mnemonic): " + sym.Label);
|
|
||||||
|
|
||||||
int uval = 0;
|
|
||||||
string uniqueLabel;
|
|
||||||
do {
|
|
||||||
uval++;
|
|
||||||
uniqueLabel = cmpLabel + "_" + uval.ToString();
|
|
||||||
} while (allLabels.ContainsKey(uniqueLabel));
|
|
||||||
|
|
||||||
allLabels.Add(uniqueLabel, uniqueLabel);
|
|
||||||
LabelMap.Add(sym.Label, uniqueLabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LabelMap.Count == 0) {
|
|
||||||
// didn't do anything, lose the table
|
|
||||||
LabelMap = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
SourceGen/SGTestData/2023-non-unique-labels
Normal file
BIN
SourceGen/SGTestData/2023-non-unique-labels
Normal file
Binary file not shown.
149
SourceGen/SGTestData/2023-non-unique-labels.dis65
Normal file
149
SourceGen/SGTestData/2023-non-unique-labels.dis65
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
### 6502bench SourceGen dis65 v1.0 ###
|
||||||
|
{
|
||||||
|
"_ContentVersion":3,"FileDataLength":154,"FileDataCrc32":-1096720699,"ProjectProps":{
|
||||||
|
"CpuName":"6502","IncludeUndocumentedInstr":false,"TwoByteBrk":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{
|
||||||
|
"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true},
|
||||||
|
"PlatformSymbolFileIdentifiers":[],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{
|
||||||
|
}},
|
||||||
|
"AddressMap":[{
|
||||||
|
"Offset":0,"Addr":4096}],"TypeHints":[{
|
||||||
|
"Low":0,"High":0,"Hint":"Code"},
|
||||||
|
{
|
||||||
|
"Low":149,"High":152,"Hint":"InlineData"}],"StatusFlagOverrides":{
|
||||||
|
},
|
||||||
|
"Comments":{
|
||||||
|
},
|
||||||
|
"LongComments":{
|
||||||
|
},
|
||||||
|
"Notes":{
|
||||||
|
},
|
||||||
|
"UserLabels":{
|
||||||
|
"2":{
|
||||||
|
"Label":"L1000","Value":4098,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"12":{
|
||||||
|
"Label":"loop1","Value":4108,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"17":{
|
||||||
|
"Label":"loop1","Value":4113,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"20":{
|
||||||
|
"Label":"global1","Value":4116,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"23":{
|
||||||
|
"Label":"loop","Value":4119,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"25":{
|
||||||
|
"Label":"loop","Value":4121,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"34":{
|
||||||
|
"Label":"global2","Value":4130,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"35":{
|
||||||
|
"Label":"loop","Value":4131,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"36":{
|
||||||
|
"Label":"global3","Value":4132,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"47":{
|
||||||
|
"Label":"fwd1","Value":4143,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"48":{
|
||||||
|
"Label":"fwd2","Value":4144,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"49":{
|
||||||
|
"Label":"global4","Value":4145,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"52":{
|
||||||
|
"Label":"loop","Value":4148,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"53":{
|
||||||
|
"Label":"global5","Value":4149,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"57":{
|
||||||
|
"Label":"global6","Value":4153,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"58":{
|
||||||
|
"Label":"spin1","Value":4154,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"61":{
|
||||||
|
"Label":"spin2","Value":4157,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"65":{
|
||||||
|
"Label":"spin1","Value":4161,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"90":{
|
||||||
|
"Label":"skip","Value":4186,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"91":{
|
||||||
|
"Label":"global_","Value":4187,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"92":{
|
||||||
|
"Label":"_global","Value":4188,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"94":{
|
||||||
|
"Label":"__","Value":4190,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"99":{
|
||||||
|
"Label":"___","Value":4195,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"101":{
|
||||||
|
"Label":"__","Value":4197,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"105":{
|
||||||
|
"Label":"anno","Value":4201,"Source":"User","Type":"LocalOrGlobalAddr","LabelAnno":"Uncertain"},
|
||||||
|
"107":{
|
||||||
|
"Label":"T106B","Value":4203,"Source":"User","Type":"LocalOrGlobalAddr","LabelAnno":"Generated"},
|
||||||
|
"115":{
|
||||||
|
"Label":"skip","Value":4211,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"116":{
|
||||||
|
"Label":"JMP","Value":4212,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"119":{
|
||||||
|
"Label":"JMP0","Value":4215,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"122":{
|
||||||
|
"Label":"JMP1","Value":4218,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"125":{
|
||||||
|
"Label":"JMP","Value":4221,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"128":{
|
||||||
|
"Label":"JMP0","Value":4224,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"131":{
|
||||||
|
"Label":"JMP1","Value":4227,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"134":{
|
||||||
|
"Label":"JMP","Value":4230,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||||
|
"137":{
|
||||||
|
"Label":"jmp","Value":4233,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"140":{
|
||||||
|
"Label":"Jmp","Value":4236,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"143":{
|
||||||
|
"Label":"BRA","Value":4239,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"146":{
|
||||||
|
"Label":"brl","Value":4242,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||||
|
"149":{
|
||||||
|
"Label":"LDAL","Value":4245,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"}},
|
||||||
|
"OperandFormats":{
|
||||||
|
"58":{
|
||||||
|
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin2§00003d","Part":"Low"}},
|
||||||
|
"61":{
|
||||||
|
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§00003a","Part":"Low"}},
|
||||||
|
"65":{
|
||||||
|
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§00003a","Part":"Low"}},
|
||||||
|
"68":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§000041","Part":"Low"}},
|
||||||
|
"70":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§00003a","Part":"Low"}},
|
||||||
|
"72":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin2§00003d","Part":"Low"}},
|
||||||
|
"74":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§00003a","Part":"High"}},
|
||||||
|
"76":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin2§00003d","Part":"High"}},
|
||||||
|
"80":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§00003a","Part":"Low"}},
|
||||||
|
"82":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin2§00003d","Part":"Low"}},
|
||||||
|
"84":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§000041","Part":"Low"}},
|
||||||
|
"86":{
|
||||||
|
"Length":1,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§00003a","Part":"Low"}},
|
||||||
|
"87":{
|
||||||
|
"Length":1,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin2§00003d","Part":"Low"}},
|
||||||
|
"88":{
|
||||||
|
"Length":1,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin1§00003a","Part":"High"}},
|
||||||
|
"89":{
|
||||||
|
"Length":1,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"spin2§00003d","Part":"High"}},
|
||||||
|
"113":{
|
||||||
|
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||||
|
"Label":"T106B","Part":"Low"}}},
|
||||||
|
"LvTables":{
|
||||||
|
}}
|
@ -64,9 +64,9 @@ _X_uname2
|
|||||||
nop
|
nop
|
||||||
lda #$00
|
lda #$00
|
||||||
_AND bne _AND ;local
|
_AND bne _AND ;local
|
||||||
JMP_1 bne JMP_1 ;global
|
JMP1 bne JMP1 ;global
|
||||||
jmp_1 bne jmp_1
|
jmp1 bne jmp1
|
||||||
TSB_1 bne TSB_1
|
TSB1 bne TSB1
|
||||||
XCE bne XCE
|
XCE bne XCE
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
@ -62,9 +62,9 @@ EXCESSIVELY_LONG_LABEL
|
|||||||
nop
|
nop
|
||||||
lda #$00
|
lda #$00
|
||||||
@AND bne @AND ;local
|
@AND bne @AND ;local
|
||||||
JMP_1 bne JMP_1 ;global
|
JMP1 bne JMP1 ;global
|
||||||
jmp_1 bne jmp_1
|
jmp1 bne jmp1
|
||||||
TSB_1 bne TSB_1
|
TSB1 bne TSB1
|
||||||
XCE bne XCE
|
XCE bne XCE
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
@ -63,9 +63,9 @@ EXCESSIVELY_LONG_LABEL:
|
|||||||
nop
|
nop
|
||||||
lda #$00
|
lda #$00
|
||||||
@AND: bne @AND ;local
|
@AND: bne @AND ;local
|
||||||
JMP_1: bne JMP_1 ;global
|
JMP1: bne JMP1 ;global
|
||||||
jmp_1: bne jmp_1
|
jmp1: bne jmp1
|
||||||
TSB_1: bne TSB_1
|
TSB1: bne TSB1
|
||||||
XCE: bne XCE
|
XCE: bne XCE
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
.cpu "6502"
|
||||||
|
* = $1000
|
||||||
|
L1000 lda #$00
|
||||||
|
_L1000 lda #$01
|
||||||
|
ldx L1000
|
||||||
|
ldy _L1000
|
||||||
|
ldx #$02
|
||||||
|
loop1 dex
|
||||||
|
bne loop1
|
||||||
|
ldx #$03
|
||||||
|
_loop1 dex
|
||||||
|
bne _loop1
|
||||||
|
global1 nop
|
||||||
|
ldx #$04
|
||||||
|
_loop ldy #$05
|
||||||
|
_loop1 dey
|
||||||
|
bne _loop1
|
||||||
|
dex
|
||||||
|
bne _loop
|
||||||
|
jmp loop
|
||||||
|
|
||||||
|
global2 .byte $ea
|
||||||
|
|
||||||
|
loop nop
|
||||||
|
global3 nop
|
||||||
|
ldx #$06
|
||||||
|
ldy #$07
|
||||||
|
dex
|
||||||
|
beq _fwd1
|
||||||
|
dey
|
||||||
|
beq _fwd2
|
||||||
|
_fwd1 nop
|
||||||
|
_fwd2 nop
|
||||||
|
global4 nop
|
||||||
|
ldx #$08
|
||||||
|
loop2 dex
|
||||||
|
global5 nop
|
||||||
|
bne loop2
|
||||||
|
nop
|
||||||
|
global6 nop
|
||||||
|
_spin1 jsr _spin2
|
||||||
|
_spin2 jsr _spin1
|
||||||
|
nop
|
||||||
|
_spin11 lda _spin1+7
|
||||||
|
beq _spin11
|
||||||
|
lda #<_spin1
|
||||||
|
ldx #<_spin2
|
||||||
|
lda #>_spin1
|
||||||
|
ldx #>_spin2
|
||||||
|
bne _skip
|
||||||
|
|
||||||
|
.word _spin1
|
||||||
|
.word _spin2
|
||||||
|
.word _spin11
|
||||||
|
.byte <_spin1
|
||||||
|
.byte <_spin2
|
||||||
|
.byte >_spin1
|
||||||
|
.byte >_spin2
|
||||||
|
|
||||||
|
_skip nop
|
||||||
|
global_ nop
|
||||||
|
X_global ldx #$40
|
||||||
|
X__ dex
|
||||||
|
bne X__
|
||||||
|
beq X___
|
||||||
|
|
||||||
|
X___ ldx #$41
|
||||||
|
_X__ dex
|
||||||
|
bne _X__
|
||||||
|
nop
|
||||||
|
_anno lda #$42
|
||||||
|
_T106B lda _anno
|
||||||
|
clc
|
||||||
|
bcc _skip
|
||||||
|
|
||||||
|
.word _T106B
|
||||||
|
|
||||||
|
_skip nop
|
||||||
|
JMP1 lda JMP1
|
||||||
|
JMP0 lda JMP0
|
||||||
|
JMP11 lda JMP11
|
||||||
|
_JMP lda _JMP
|
||||||
|
_JMP0 lda _JMP0
|
||||||
|
_JMP1 lda _JMP1
|
||||||
|
_JMP2 lda _JMP2
|
||||||
|
jmp1 lda jmp1
|
||||||
|
Jmp1 lda Jmp1
|
||||||
|
BRA lda BRA
|
||||||
|
brl lda brl
|
||||||
|
LDAL .byte $af
|
||||||
|
.byte $95
|
||||||
|
.byte $10
|
||||||
|
.byte $00
|
||||||
|
rts
|
||||||
|
|
@ -0,0 +1,94 @@
|
|||||||
|
org $1000
|
||||||
|
L1000 lda #$00
|
||||||
|
:L1000 lda #$01
|
||||||
|
ldx L1000
|
||||||
|
ldy :L1000
|
||||||
|
ldx #$02
|
||||||
|
loop1 dex
|
||||||
|
bne loop1
|
||||||
|
ldx #$03
|
||||||
|
:loop1 dex
|
||||||
|
bne :loop1
|
||||||
|
global1 nop
|
||||||
|
ldx #$04
|
||||||
|
:loop ldy #$05
|
||||||
|
:loop1 dey
|
||||||
|
bne :loop1
|
||||||
|
dex
|
||||||
|
bne :loop
|
||||||
|
jmp loop
|
||||||
|
|
||||||
|
global2 dfb $ea
|
||||||
|
|
||||||
|
loop nop
|
||||||
|
global3 nop
|
||||||
|
ldx #$06
|
||||||
|
ldy #$07
|
||||||
|
dex
|
||||||
|
beq :fwd1
|
||||||
|
dey
|
||||||
|
beq :fwd2
|
||||||
|
:fwd1 nop
|
||||||
|
:fwd2 nop
|
||||||
|
global4 nop
|
||||||
|
ldx #$08
|
||||||
|
loop2 dex
|
||||||
|
global5 nop
|
||||||
|
bne loop2
|
||||||
|
nop
|
||||||
|
global6 nop
|
||||||
|
:spin1 jsr :spin2
|
||||||
|
:spin2 jsr :spin1
|
||||||
|
nop
|
||||||
|
:spin11 lda :spin1+7
|
||||||
|
beq :spin11
|
||||||
|
lda #<:spin1
|
||||||
|
ldx #<:spin2
|
||||||
|
lda #>:spin1
|
||||||
|
ldx #>:spin2
|
||||||
|
bne :skip
|
||||||
|
|
||||||
|
dw :spin1
|
||||||
|
dw :spin2
|
||||||
|
dw :spin11
|
||||||
|
dfb <:spin1
|
||||||
|
dfb <:spin2
|
||||||
|
dfb >:spin1
|
||||||
|
dfb >:spin2
|
||||||
|
|
||||||
|
:skip nop
|
||||||
|
global_ nop
|
||||||
|
_global ldx #$40
|
||||||
|
__ dex
|
||||||
|
bne __
|
||||||
|
beq ___
|
||||||
|
|
||||||
|
___ ldx #$41
|
||||||
|
:__ dex
|
||||||
|
bne :__
|
||||||
|
nop
|
||||||
|
:anno lda #$42
|
||||||
|
:T106B lda :anno
|
||||||
|
clc
|
||||||
|
bcc :skip
|
||||||
|
|
||||||
|
dw :T106B
|
||||||
|
|
||||||
|
:skip nop
|
||||||
|
JMP lda JMP
|
||||||
|
JMP0 lda JMP0
|
||||||
|
JMP1 lda JMP1
|
||||||
|
:JMP lda :JMP
|
||||||
|
:JMP0 lda :JMP0
|
||||||
|
:JMP1 lda :JMP1
|
||||||
|
:JMP2 lda :JMP2
|
||||||
|
jmp lda jmp
|
||||||
|
Jmp lda Jmp
|
||||||
|
BRA lda BRA
|
||||||
|
brl lda brl
|
||||||
|
LDAL dfb $af
|
||||||
|
dfb $95
|
||||||
|
dfb $10
|
||||||
|
dfb $00
|
||||||
|
rts
|
||||||
|
|
95
SourceGen/SGTestData/Expected/2023-non-unique-labels_acme.S
Normal file
95
SourceGen/SGTestData/Expected/2023-non-unique-labels_acme.S
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
!cpu 6502
|
||||||
|
* = $1000
|
||||||
|
L1000 lda #$00
|
||||||
|
@L1000 lda #$01
|
||||||
|
ldx L1000
|
||||||
|
ldy @L1000
|
||||||
|
ldx #$02
|
||||||
|
loop1 dex
|
||||||
|
bne loop1
|
||||||
|
ldx #$03
|
||||||
|
@loop1 dex
|
||||||
|
bne @loop1
|
||||||
|
global1 nop
|
||||||
|
ldx #$04
|
||||||
|
@loop ldy #$05
|
||||||
|
@loop1 dey
|
||||||
|
bne @loop1
|
||||||
|
dex
|
||||||
|
bne @loop
|
||||||
|
jmp loop
|
||||||
|
|
||||||
|
global2 !byte $ea
|
||||||
|
|
||||||
|
loop nop
|
||||||
|
global3 nop
|
||||||
|
ldx #$06
|
||||||
|
ldy #$07
|
||||||
|
dex
|
||||||
|
beq @fwd1
|
||||||
|
dey
|
||||||
|
beq @fwd2
|
||||||
|
@fwd1 nop
|
||||||
|
@fwd2 nop
|
||||||
|
global4 nop
|
||||||
|
ldx #$08
|
||||||
|
loop2 dex
|
||||||
|
global5 nop
|
||||||
|
bne loop2
|
||||||
|
nop
|
||||||
|
global6 nop
|
||||||
|
@spin1 jsr @spin2
|
||||||
|
@spin2 jsr @spin1
|
||||||
|
nop
|
||||||
|
@spin11 lda @spin1+7
|
||||||
|
beq @spin11
|
||||||
|
lda #<@spin1
|
||||||
|
ldx #<@spin2
|
||||||
|
lda #>@spin1
|
||||||
|
ldx #>@spin2
|
||||||
|
bne @skip
|
||||||
|
|
||||||
|
!word @spin1
|
||||||
|
!word @spin2
|
||||||
|
!word @spin11
|
||||||
|
!byte <@spin1
|
||||||
|
!byte <@spin2
|
||||||
|
!byte >@spin1
|
||||||
|
!byte >@spin2
|
||||||
|
|
||||||
|
@skip nop
|
||||||
|
global_ nop
|
||||||
|
_global ldx #$40
|
||||||
|
__ dex
|
||||||
|
bne __
|
||||||
|
beq ___
|
||||||
|
|
||||||
|
___ ldx #$41
|
||||||
|
@__ dex
|
||||||
|
bne @__
|
||||||
|
nop
|
||||||
|
@anno lda #$42
|
||||||
|
@T106B lda @anno
|
||||||
|
clc
|
||||||
|
bcc @skip
|
||||||
|
|
||||||
|
!word @T106B
|
||||||
|
|
||||||
|
@skip nop
|
||||||
|
JMP1 lda JMP1
|
||||||
|
JMP0 lda JMP0
|
||||||
|
JMP11 lda JMP11
|
||||||
|
@JMP lda @JMP
|
||||||
|
@JMP0 lda @JMP0
|
||||||
|
@JMP1 lda @JMP1
|
||||||
|
@JMP2 lda @JMP2
|
||||||
|
jmp1 lda jmp1
|
||||||
|
Jmp1 lda Jmp1
|
||||||
|
BRA lda BRA
|
||||||
|
brl lda brl
|
||||||
|
LDAL !byte $af
|
||||||
|
!byte $95
|
||||||
|
!byte $10
|
||||||
|
!byte $00
|
||||||
|
rts
|
||||||
|
|
96
SourceGen/SGTestData/Expected/2023-non-unique-labels_cc65.S
Normal file
96
SourceGen/SGTestData/Expected/2023-non-unique-labels_cc65.S
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
.setcpu "6502"
|
||||||
|
; .segment "SEG000"
|
||||||
|
.org $1000
|
||||||
|
L1000: lda #$00
|
||||||
|
@L1000: lda #$01
|
||||||
|
ldx L1000
|
||||||
|
ldy @L1000
|
||||||
|
ldx #$02
|
||||||
|
loop1: dex
|
||||||
|
bne loop1
|
||||||
|
ldx #$03
|
||||||
|
@loop1: dex
|
||||||
|
bne @loop1
|
||||||
|
global1: nop
|
||||||
|
ldx #$04
|
||||||
|
@loop: ldy #$05
|
||||||
|
@loop1: dey
|
||||||
|
bne @loop1
|
||||||
|
dex
|
||||||
|
bne @loop
|
||||||
|
jmp loop
|
||||||
|
|
||||||
|
global2: .byte $ea
|
||||||
|
|
||||||
|
loop: nop
|
||||||
|
global3: nop
|
||||||
|
ldx #$06
|
||||||
|
ldy #$07
|
||||||
|
dex
|
||||||
|
beq @fwd1
|
||||||
|
dey
|
||||||
|
beq @fwd2
|
||||||
|
@fwd1: nop
|
||||||
|
@fwd2: nop
|
||||||
|
global4: nop
|
||||||
|
ldx #$08
|
||||||
|
loop2: dex
|
||||||
|
global5: nop
|
||||||
|
bne loop2
|
||||||
|
nop
|
||||||
|
global6: nop
|
||||||
|
@spin1: jsr @spin2
|
||||||
|
@spin2: jsr @spin1
|
||||||
|
nop
|
||||||
|
@spin11: lda @spin1+7
|
||||||
|
beq @spin11
|
||||||
|
lda #<@spin1
|
||||||
|
ldx #<@spin2
|
||||||
|
lda #>@spin1
|
||||||
|
ldx #>@spin2
|
||||||
|
bne @skip
|
||||||
|
|
||||||
|
.word @spin1
|
||||||
|
.word @spin2
|
||||||
|
.word @spin11
|
||||||
|
.byte <@spin1
|
||||||
|
.byte <@spin2
|
||||||
|
.byte >@spin1
|
||||||
|
.byte >@spin2
|
||||||
|
|
||||||
|
@skip: nop
|
||||||
|
global_: nop
|
||||||
|
_global: ldx #$40
|
||||||
|
__: dex
|
||||||
|
bne __
|
||||||
|
beq ___
|
||||||
|
|
||||||
|
___: ldx #$41
|
||||||
|
@__: dex
|
||||||
|
bne @__
|
||||||
|
nop
|
||||||
|
@anno: lda #$42
|
||||||
|
@T106B: lda @anno
|
||||||
|
clc
|
||||||
|
bcc @skip
|
||||||
|
|
||||||
|
.word @T106B
|
||||||
|
|
||||||
|
@skip: nop
|
||||||
|
JMP1: lda JMP1
|
||||||
|
JMP0: lda JMP0
|
||||||
|
JMP11: lda JMP11
|
||||||
|
@JMP: lda @JMP
|
||||||
|
@JMP0: lda @JMP0
|
||||||
|
@JMP1: lda @JMP1
|
||||||
|
@JMP2: lda @JMP2
|
||||||
|
jmp1: lda jmp1
|
||||||
|
Jmp1: lda Jmp1
|
||||||
|
BRA: lda BRA
|
||||||
|
brl: lda brl
|
||||||
|
LDAL: .byte $af
|
||||||
|
.byte $95
|
||||||
|
.byte $10
|
||||||
|
.byte $00
|
||||||
|
rts
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
# 6502bench SourceGen generated linker script for 2023-non-unique-labels
|
||||||
|
MEMORY {
|
||||||
|
MAIN: file=%O, start=%S, size=65536;
|
||||||
|
# MEM000: file=%O, start=$1000, size=154;
|
||||||
|
}
|
||||||
|
SEGMENTS {
|
||||||
|
CODE: load=MAIN, type=rw;
|
||||||
|
# SEG000: load=MEM000, type=rw;
|
||||||
|
}
|
||||||
|
FEATURES {}
|
||||||
|
SYMBOLS {}
|
@ -3,6 +3,8 @@
|
|||||||
;
|
;
|
||||||
; Assembler: Merlin 32
|
; Assembler: Merlin 32
|
||||||
|
|
||||||
|
; NOTE: configure for plain 6502
|
||||||
|
|
||||||
org $1000
|
org $1000
|
||||||
|
|
||||||
; Test conflict with auto-label.
|
; Test conflict with auto-label.
|
||||||
@ -47,7 +49,7 @@ global3 nop ;EDIT
|
|||||||
:fwd1 nop ;EDIT
|
:fwd1 nop ;EDIT
|
||||||
:fwd2 nop ;EDIT
|
:fwd2 nop ;EDIT
|
||||||
|
|
||||||
; Test loop with a global in the middle.
|
; Test loop with an unreferenced global in the middle.
|
||||||
global4 nop ;EDIT
|
global4 nop ;EDIT
|
||||||
ldx #$08
|
ldx #$08
|
||||||
gloop dex ;EDIT: local, name "loop"
|
gloop dex ;EDIT: local, name "loop"
|
||||||
@ -57,12 +59,13 @@ global5 nop
|
|||||||
nop
|
nop
|
||||||
|
|
||||||
; Test symbolic references.
|
; Test symbolic references.
|
||||||
|
; NOTE: start them as spin1/spin2/spin3, then rename spin3 to spin1
|
||||||
global6 nop
|
global6 nop
|
||||||
:spin1 jsr :spin2 ;EDIT: local, name "spin1", operand ref to ":spin2"
|
:spin1 jsr :spin2 ;EDIT: local, name "spin1", operand ref to ":spin2"
|
||||||
:spin2 jsr :spin1 ;EDIT: local, name "spin2", operand ref to ":spin1"
|
:spin2 jsr :spin1 ;EDIT: local, name "spin2", operand ref to ":spin1"
|
||||||
nop
|
nop
|
||||||
:spin3 lda :spin3 ;EDIT: local, name "spin1", operand ref to ":spin1"
|
:spin3 lda :spin3 ;EDIT: local, name "spin1", operand ref to ":spin1" (will be adjusted)
|
||||||
beq :spin3 ;EDIT: operand ref to ":spin1"
|
beq :spin3 ;EDIT: operand ref to ":spin1" (NOT adjusted)
|
||||||
|
|
||||||
lda #<:spin1
|
lda #<:spin1
|
||||||
ldx #<:spin2
|
ldx #<:spin2
|
||||||
@ -84,7 +87,7 @@ global6 nop
|
|||||||
|
|
||||||
; Semi-related: test labels that are nothing but underscores.
|
; Semi-related: test labels that are nothing but underscores.
|
||||||
global_ nop
|
global_ nop
|
||||||
ldx #$40
|
_global ldx #$40
|
||||||
__ dex
|
__ dex
|
||||||
bne __
|
bne __
|
||||||
beq ___
|
beq ___
|
||||||
@ -97,10 +100,26 @@ ___ ldx #$41
|
|||||||
; Semi-related: test annotations (mostly to confirm that the suffix chars
|
; Semi-related: test annotations (mostly to confirm that the suffix chars
|
||||||
; aren't appearing in the assembly output)
|
; aren't appearing in the assembly output)
|
||||||
anno lda #$42 ;EDIT: add '?'
|
anno lda #$42 ;EDIT: add '?'
|
||||||
anno1 lda anno
|
anno1 lda anno ;NOTE: do not label, let table gen do it
|
||||||
|
clc
|
||||||
|
bcc :skip
|
||||||
|
dw anno1 ;EDIT: use table generator to get annotation
|
||||||
|
:skip nop
|
||||||
|
|
||||||
|
; Semi-related: test opcode name labels (which are illegal for assemblers
|
||||||
|
; other than Merlin 32). We're configured for plain 6502, so it should
|
||||||
|
; remap some but not others.
|
||||||
|
JMP lda JMP ;EDIT set label (should be remapped)
|
||||||
|
JMP0 lda JMP0 ;EDIT set label
|
||||||
|
JMP1 lda JMP1 ;EDIT set label
|
||||||
|
:JMP lda :JMP ;EDIT set label
|
||||||
|
:JMP0 lda :JMP0 ;EDIT set label
|
||||||
|
:JMP1 lda :JMP1 ;EDIT set label
|
||||||
|
:JMP_ lda :JMP_ ;EDIT set label :JMP
|
||||||
|
jmp lda jmp ;EDIT set label
|
||||||
|
Jmp lda Jmp ;EDIT set label
|
||||||
|
BRA lda BRA ;EDIT set label (should NOT be remapped)
|
||||||
|
brl lda brl ;EDIT set label (should NOT be remapped)
|
||||||
|
LDAL ldal LDAL ;EDIT set label (should NOT be remapped)
|
||||||
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
dw anno1 ;EDIT: use table generator
|
|
||||||
|
|
||||||
|
@ -153,6 +153,11 @@ namespace SourceGen {
|
|||||||
get { return SymbolType == Type.NonUniqueLocalAddr; }
|
get { return SymbolType == Type.NonUniqueLocalAddr; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool CanBeLocal {
|
||||||
|
get { return SymbolType == Type.LocalOrGlobalAddr ||
|
||||||
|
SymbolType == Type.NonUniqueLocalAddr; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the symbol represents a constant value.
|
/// True if the symbol represents a constant value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user