mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-30 14:31:13 +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
|
||||
// between global labels. (This is poorly documented.)
|
||||
mLocalizer.LocalPrefix = "@";
|
||||
mLocalizer.QuirkNoOpcodeMnemonics = true;
|
||||
mLocalizer.Analyze();
|
||||
mLocalizer.FixOpcodeLabels();
|
||||
|
||||
// Use UTF-8 encoding, without a byte-order mark.
|
||||
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
||||
|
@ -218,9 +218,9 @@ namespace SourceGen.AsmGen {
|
||||
|
||||
mLocalizer = new LabelLocalizer(Project);
|
||||
mLocalizer.LocalPrefix = "@";
|
||||
mLocalizer.QuirkVariablesEndScope = true;
|
||||
mLocalizer.QuirkVariablesEndScope = true; // https://github.com/cc65/cc65/issues/938
|
||||
mLocalizer.QuirkNoOpcodeMnemonics = true;
|
||||
mLocalizer.Analyze();
|
||||
mLocalizer.FixOpcodeLabels();
|
||||
|
||||
// Use UTF-8 encoding, without a byte-order mark.
|
||||
using (StreamWriter sw = new StreamWriter(cfgName, false, new UTF8Encoding(false))) {
|
||||
|
@ -191,8 +191,8 @@ namespace SourceGen.AsmGen {
|
||||
|
||||
mLocalizer = new LabelLocalizer(Project);
|
||||
mLocalizer.LocalPrefix = ":";
|
||||
// don't need to set QuirkNoOpcodeMnemonics
|
||||
mLocalizer.Analyze();
|
||||
//mLocalizer.FixOpcodeLabels();
|
||||
|
||||
// Use UTF-8 encoding, without a byte-order mark.
|
||||
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
|
||||
|
@ -230,9 +230,8 @@ namespace SourceGen.AsmGen {
|
||||
|
||||
mLocalizer = new LabelLocalizer(Project);
|
||||
mLocalizer.LocalPrefix = "_";
|
||||
mLocalizer.QuirkNoOpcodeMnemonics = true;
|
||||
mLocalizer.Analyze();
|
||||
mLocalizer.MaskLeadingUnderscores();
|
||||
mLocalizer.FixOpcodeLabels();
|
||||
|
||||
// Use UTF-8 encoding, without a byte-order mark.
|
||||
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.
|
||||
|
||||
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
|
||||
to be global.
|
||||
@ -78,6 +78,10 @@ name. This must be applied to both labels and operands.
|
||||
|
||||
namespace SourceGen.AsmGen {
|
||||
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>
|
||||
/// A pairing of an offset with a label string. (Essentially mAnattribs[n].Symbol
|
||||
/// with all the fluff trimmed away.)
|
||||
@ -85,8 +89,8 @@ namespace SourceGen.AsmGen {
|
||||
/// <remarks>
|
||||
/// 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
|
||||
/// put into a List, so switching to a plain int offset doesn't necessarily help us
|
||||
/// much because the ints get boxed.
|
||||
/// put into a List, so simply storing a plain int offset it's much better (in terms
|
||||
/// of memory and allocations) because the ints get boxed.
|
||||
/// </remarks>
|
||||
private class OffsetLabel {
|
||||
public int Offset { get; private set; }
|
||||
@ -136,6 +140,12 @@ namespace SourceGen.AsmGen {
|
||||
/// </summary>
|
||||
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>
|
||||
/// Project reference.
|
||||
/// </summary>
|
||||
@ -175,6 +185,8 @@ namespace SourceGen.AsmGen {
|
||||
public void Analyze() {
|
||||
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);
|
||||
|
||||
// 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.
|
||||
|
||||
//
|
||||
// Step 1: generate source/target pairs and global label list
|
||||
//
|
||||
GenerateLists();
|
||||
|
||||
//
|
||||
// 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
|
||||
// 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++) {
|
||||
FindIntersectingPairs(mGlobalLabels[index]);
|
||||
}
|
||||
|
||||
// Step 3: for each local label, add an entry to the map with the appropriate
|
||||
// local-label syntax.
|
||||
// We're done with these. Take out the trash.
|
||||
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>();
|
||||
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++) {
|
||||
if (mGlobalFlags[i]) {
|
||||
if (!mGlobalFlags[i]) {
|
||||
continue;
|
||||
}
|
||||
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
||||
if (sym == null) {
|
||||
// Should only happen when we insert a dummy global label for the
|
||||
// "variables end scope" quirk.
|
||||
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();
|
||||
mOffsetPairs.Clear();
|
||||
//
|
||||
// Step 4: remap local labels. There are two operations here.
|
||||
//
|
||||
// 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>
|
||||
@ -237,10 +357,11 @@ namespace SourceGen.AsmGen {
|
||||
bool first = true;
|
||||
|
||||
for (int offset = 0; offset < mProject.FileDataLength; offset++) {
|
||||
// Find all user labels and auto labels.
|
||||
Symbol sym = mProject.GetAnattrib(offset).Symbol;
|
||||
|
||||
// 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 &&
|
||||
mProject.LvTables.TryGetValue(offset, out LocalVariableTable value) &&
|
||||
value.Count > 0) {
|
||||
@ -253,7 +374,7 @@ namespace SourceGen.AsmGen {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (first || sym.SymbolType != Symbol.Type.LocalOrGlobalAddr) {
|
||||
if (first || !sym.CanBeLocal) {
|
||||
first = false;
|
||||
mGlobalFlags[offset] = true;
|
||||
mGlobalLabels.Add(new OffsetLabel(offset, sym.Label));
|
||||
@ -285,12 +406,11 @@ namespace SourceGen.AsmGen {
|
||||
private void FindIntersectingPairs(OffsetLabel glabel) {
|
||||
Debug.Assert(mGlobalFlags[glabel.Offset]);
|
||||
|
||||
int globOffset = glabel.Offset;
|
||||
int glabOffset = glabel.Offset;
|
||||
for (int i = 0; i < mOffsetPairs.Count; i++) {
|
||||
OffsetPair pair = mOffsetPairs[i];
|
||||
|
||||
// If the destination was marked global earlier, remove and ignore this entry.
|
||||
// Note this also means that pair.DstOffset != label.Offset.
|
||||
// If the destination was marked global earlier, remove the entry and move on.
|
||||
if (mGlobalFlags[pair.DstOffset]) {
|
||||
mOffsetPairs.RemoveAt(i);
|
||||
i--;
|
||||
@ -301,15 +421,16 @@ namespace SourceGen.AsmGen {
|
||||
// offsets.
|
||||
//
|
||||
// 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
|
||||
// the case where label.Offset==pair.SrcOffset.
|
||||
// labels forward, but not backward (i.e. if it crosses itself, the destination
|
||||
// must be made global). We need to take that into account for the case where
|
||||
// label.Offset==pair.SrcOffset.
|
||||
bool intersect;
|
||||
if (pair.SrcOffset < pair.DstOffset) {
|
||||
// Forward reference. src==glob is ok
|
||||
intersect = pair.SrcOffset < globOffset && pair.DstOffset >= globOffset;
|
||||
// Forward reference. src==glab is ok
|
||||
intersect = pair.SrcOffset < glabOffset && pair.DstOffset >= glabOffset;
|
||||
} else {
|
||||
// Backward reference. src==glob is bad
|
||||
intersect = pair.SrcOffset >= globOffset && pair.DstOffset <= globOffset;
|
||||
// Backward reference. src==glab is bad
|
||||
intersect = pair.SrcOffset >= glabOffset && pair.DstOffset <= glabOffset;
|
||||
}
|
||||
|
||||
if (intersect) {
|
||||
@ -329,163 +450,67 @@ namespace SourceGen.AsmGen {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the label map so that only local variables start with an underscore ('_').
|
||||
/// 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.
|
||||
/// Generates map entries for local labels defined between the two globals.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This may be called even if label localization is disabled. In that case we just
|
||||
/// create an empty label map and populate as needed.
|
||||
/// </remarks>
|
||||
public void MaskLeadingUnderscores() {
|
||||
bool allGlobal = false;
|
||||
if (LabelMap == null) {
|
||||
allGlobal = true;
|
||||
LabelMap = new Dictionary<string, string>();
|
||||
}
|
||||
/// <param name="startGlobal">Offset of first global.</param>
|
||||
/// <param name="endGlobal">Offset of second global. If this range is at the end of the
|
||||
/// file, this offset may be one past the end.</param>
|
||||
/// <param name="scopedLocals">Work object (minor alloc optimization).</param>
|
||||
private void ProcessLocals(int startGlobal, int endGlobal,
|
||||
Dictionary<string, string> scopedLocals) {
|
||||
//Debug.WriteLine("ProcessLocals: +" + startGlobal.ToString("x6") +
|
||||
// " - +" + endGlobal.ToString("x6"));
|
||||
scopedLocals.Clear();
|
||||
bool remapUnders = (LocalPrefix == "_");
|
||||
|
||||
// Throw out the original local label generation. We're going to redo the
|
||||
// label map generation step.
|
||||
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++) {
|
||||
for (int i = startGlobal + 1; i < endGlobal; i++) {
|
||||
Debug.Assert(!mGlobalFlags[i]);
|
||||
Symbol sym = mProject.GetAnattrib(i).Symbol;
|
||||
if (sym == null) {
|
||||
// No label at this offset.
|
||||
continue;
|
||||
continue; // no label here
|
||||
}
|
||||
|
||||
string newLabel;
|
||||
if (allGlobal || mGlobalFlags[i]) {
|
||||
// Global symbol. Don't let it start with '_'.
|
||||
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;
|
||||
}
|
||||
string newLabel = sym.LabelWithoutTag;
|
||||
if (remapUnders && newLabel[0] == '_') {
|
||||
newLabel = LocalPrefix + NO_UNDER_PFX + newLabel;
|
||||
} else {
|
||||
// Local symbol.
|
||||
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;
|
||||
}
|
||||
newLabel = LocalPrefix + newLabel;
|
||||
}
|
||||
|
||||
// Make sure it's unique.
|
||||
string uniqueLabel = newLabel;
|
||||
int uval = 0;
|
||||
while (allLabels.ContainsKey(uniqueLabel)) {
|
||||
uval++;
|
||||
uniqueLabel = newLabel + uval.ToString();
|
||||
if (scopedLocals.ContainsKey(newLabel)) {
|
||||
newLabel = MakeUnique(newLabel, scopedLocals);
|
||||
}
|
||||
allLabels.Add(uniqueLabel, uniqueLabel);
|
||||
|
||||
// If it's different, add it to the label map.
|
||||
if (sym.Label != uniqueLabel) {
|
||||
LabelMap.Add(sym.Label, uniqueLabel);
|
||||
}
|
||||
// Map from the original symbol label to the local form. This works for
|
||||
// unique and non-unique locals.
|
||||
LabelMap[sym.Label] = newLabel;
|
||||
|
||||
scopedLocals.Add(newLabel, newLabel);
|
||||
}
|
||||
|
||||
Debug.WriteLine("UMAP: allcount=" + allLabels.Count + " mapcount=" + LabelMap.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remaps labels that match opcode names. Updated names will be added to LabelMap.
|
||||
/// This should be run after localization and underscore concealment have finished.
|
||||
/// Alters a label to make it unique. This may be called with a label that is unique
|
||||
/// but illegal (e.g. an instruction mnemonic), so we guarantee that the label returned
|
||||
/// is different from the argument.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Most assemblers don't like it if you create a label with the same name as an
|
||||
/// opcode, e.g. "jmp LSR" doesn't work. We can use the label map to work around
|
||||
/// the issue.
|
||||
///
|
||||
/// 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.
|
||||
/// We can't put a '_' at the front or an 'L' at the end (LDAL), since that could run
|
||||
/// afoul of the things we're trying to work around. We don't want to mess with the
|
||||
/// start of the string since it may or may not have the LocalPrefix on it.
|
||||
/// </remarks>
|
||||
public void FixOpcodeLabels() {
|
||||
if (LabelMap == null) {
|
||||
LabelMap = new Dictionary<string, string>();
|
||||
}
|
||||
/// <param name="label">Label to uniquify.</param>
|
||||
/// <param name="allLabels">Dictionary to uniquify against.</param>
|
||||
/// <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.
|
||||
// (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;
|
||||
}
|
||||
return uniqueLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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
|
||||
lda #$00
|
||||
_AND bne _AND ;local
|
||||
JMP_1 bne JMP_1 ;global
|
||||
jmp_1 bne jmp_1
|
||||
TSB_1 bne TSB_1
|
||||
JMP1 bne JMP1 ;global
|
||||
jmp1 bne jmp1
|
||||
TSB1 bne TSB1
|
||||
XCE bne XCE
|
||||
rts
|
||||
|
||||
|
@ -62,9 +62,9 @@ EXCESSIVELY_LONG_LABEL
|
||||
nop
|
||||
lda #$00
|
||||
@AND bne @AND ;local
|
||||
JMP_1 bne JMP_1 ;global
|
||||
jmp_1 bne jmp_1
|
||||
TSB_1 bne TSB_1
|
||||
JMP1 bne JMP1 ;global
|
||||
jmp1 bne jmp1
|
||||
TSB1 bne TSB1
|
||||
XCE bne XCE
|
||||
rts
|
||||
|
||||
|
@ -63,9 +63,9 @@ EXCESSIVELY_LONG_LABEL:
|
||||
nop
|
||||
lda #$00
|
||||
@AND: bne @AND ;local
|
||||
JMP_1: bne JMP_1 ;global
|
||||
jmp_1: bne jmp_1
|
||||
TSB_1: bne TSB_1
|
||||
JMP1: bne JMP1 ;global
|
||||
jmp1: bne jmp1
|
||||
TSB1: bne TSB1
|
||||
XCE: bne XCE
|
||||
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
|
||||
|
||||
; NOTE: configure for plain 6502
|
||||
|
||||
org $1000
|
||||
|
||||
; Test conflict with auto-label.
|
||||
@ -47,7 +49,7 @@ global3 nop ;EDIT
|
||||
:fwd1 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
|
||||
ldx #$08
|
||||
gloop dex ;EDIT: local, name "loop"
|
||||
@ -57,12 +59,13 @@ global5 nop
|
||||
nop
|
||||
|
||||
; Test symbolic references.
|
||||
; NOTE: start them as spin1/spin2/spin3, then rename spin3 to spin1
|
||||
global6 nop
|
||||
:spin1 jsr :spin2 ;EDIT: local, name "spin1", operand ref to ":spin2"
|
||||
:spin2 jsr :spin1 ;EDIT: local, name "spin2", operand ref to ":spin1"
|
||||
nop
|
||||
:spin3 lda :spin3 ;EDIT: local, name "spin1", operand ref to ":spin1"
|
||||
beq :spin3 ;EDIT: 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" (NOT adjusted)
|
||||
|
||||
lda #<:spin1
|
||||
ldx #<:spin2
|
||||
@ -84,7 +87,7 @@ global6 nop
|
||||
|
||||
; Semi-related: test labels that are nothing but underscores.
|
||||
global_ nop
|
||||
ldx #$40
|
||||
_global ldx #$40
|
||||
__ dex
|
||||
bne __
|
||||
beq ___
|
||||
@ -97,10 +100,26 @@ ___ ldx #$41
|
||||
; Semi-related: test annotations (mostly to confirm that the suffix chars
|
||||
; aren't appearing in the assembly output)
|
||||
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
|
||||
|
||||
dw anno1 ;EDIT: use table generator
|
||||
|
||||
|
@ -153,6 +153,11 @@ namespace SourceGen {
|
||||
get { return SymbolType == Type.NonUniqueLocalAddr; }
|
||||
}
|
||||
|
||||
public bool CanBeLocal {
|
||||
get { return SymbolType == Type.LocalOrGlobalAddr ||
|
||||
SymbolType == Type.NonUniqueLocalAddr; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the symbol represents a constant value.
|
||||
/// </summary>
|
||||
|
Loading…
Reference in New Issue
Block a user