1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-30 06:29:32 +00:00

ORG rework, part 8

Implemented address region pre-labels.  These are useful if the code is
relocating a block from address A to address B, because the code that
does the copying refers to both the "before" address and the "after"
address.  Previously you'd give the block the "after" address and the
"before" would just appears as hex, because it's effectively an
external address.

Pre-labels are shown on screen with their address, but no other fields.
Showing the address makes it easy to see the label's value, which isn't
always obvious right before a .arstart.  The labels are suppressed if the
address value evaluates to non-addressable.

This defines a new type of symbol, which is external and always global
in scope.  Pre-labels affect label localization and must go through
the usual remapping to handle clashes with opcode mnemonics and the
use of leading underscores.  Cross-references are computed, but are
associated with the file offset rather than the label line itself.

Added a new filter to the Symbols window ("PreL").

Implemented label input and checking in the address editor.  Generally
added highlighting of relevant error labels.
This commit is contained in:
Andy McFadden 2021-10-04 20:41:19 -07:00
parent e8608770b9
commit d2326c389f
18 changed files with 361 additions and 94 deletions

View File

@ -191,8 +191,19 @@ namespace CommonUtil {
/// Is the end point floating? /// Is the end point floating?
/// </summary> /// </summary>
public bool IsFloating { public bool IsFloating {
get { return Length == FLOATING_LEN; }
}
/// <summary>
/// Does this region have a valid pre-label?
/// </summary>
/// <remarks>
/// This only checks for the existence of the label and whether the parent region
/// is non-addressable. It does not verify the label's syntax.
/// </remarks>
public bool HasValidPreLabel {
get { get {
return Length == FLOATING_LEN; return !string.IsNullOrEmpty(PreLabel) && PreLabelAddress != NON_ADDR;
} }
} }

View File

@ -31,6 +31,7 @@ namespace PluginCommon {
public enum Source { public enum Source {
Unknown = 0, Unknown = 0,
User, User,
AddrPreLabel,
Project, Project,
Platform Platform
} }

View File

@ -84,6 +84,7 @@ namespace SourceGen {
public const string SYMWIN_SHOW_AUTO = "symwin-show-auto"; public const string SYMWIN_SHOW_AUTO = "symwin-show-auto";
public const string SYMWIN_SHOW_PROJECT = "symwin-show-project"; public const string SYMWIN_SHOW_PROJECT = "symwin-show-project";
public const string SYMWIN_SHOW_PLATFORM = "symwin-show-platform"; public const string SYMWIN_SHOW_PLATFORM = "symwin-show-platform";
public const string SYMWIN_SHOW_ADDR_PRE_LABELS = "symwin-show-addr-pre-labels";
public const string SYMWIN_SHOW_CONST = "symwin-show-const"; public const string SYMWIN_SHOW_CONST = "symwin-show-const";
public const string SYMWIN_SHOW_ADDR = "symwin-show-addr"; public const string SYMWIN_SHOW_ADDR = "symwin-show-addr";
public const string SYMWIN_SORT_ASCENDING = "symwin-sort-ascending"; public const string SYMWIN_SORT_ASCENDING = "symwin-sort-ascending";

View File

@ -579,6 +579,10 @@ namespace SourceGen.AsmGen {
nextAddress = 0; nextAddress = 0;
} }
if (change.IsStart) { if (change.IsStart) {
if (change.Region.HasValidPreLabel) {
string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel);
OutputLine(labelStr, string.Empty, string.Empty, string.Empty);
}
if (mPcDepth == 0 && mFirstIsOpen) { if (mPcDepth == 0 && mFirstIsOpen) {
mPcDepth++; mPcDepth++;

View File

@ -540,6 +540,18 @@ namespace SourceGen.AsmGen {
// IGenerator // IGenerator
public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) { public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) {
if (change.IsStart && change.Region.HasValidPreLabel) {
// Need to output the previous ORG, if any, then a label on a line by itself.
if (mNextAddress >= 0) {
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(mNextAddress, 4),
string.Empty);
}
string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel);
OutputLine(labelStr, string.Empty, string.Empty, string.Empty);
}
int nextAddress = change.Address; int nextAddress = change.Address;
if (nextAddress == Address.NON_ADDR) { if (nextAddress == Address.NON_ADDR) {
// Start non-addressable regions at zero to ensure they don't overflow bank. // Start non-addressable regions at zero to ensure they don't overflow bank.
@ -555,6 +567,7 @@ namespace SourceGen.AsmGen {
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(mNextAddress, 4), SourceFormatter.FormatHexValue(mNextAddress, 4),
string.Empty); string.Empty);
mNextAddress = -1;
} }
// IGenerator // IGenerator

View File

@ -106,7 +106,7 @@ namespace SourceGen.AsmGen {
{ "EquDirective", "equ" }, { "EquDirective", "equ" },
{ "VarDirective", "equ" }, { "VarDirective", "equ" },
{ "ArStartDirective", "org" }, { "ArStartDirective", "org" },
//ArEndDirective { "ArEndDirective", "org_end" }, // not actually used
//RegWidthDirective //RegWidthDirective
//DataBankDirective //DataBankDirective
{ "DefineData1", "dfb" }, { "DefineData1", "dfb" },
@ -491,6 +491,18 @@ namespace SourceGen.AsmGen {
// IGenerator // IGenerator
public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) { public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) {
if (change.IsStart && change.Region.HasValidPreLabel) {
// Need to output the previous ORG, if any, then a label on a line by itself.
if (mNextAddress >= 0) {
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(mNextAddress, 4),
string.Empty);
}
string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel);
OutputLine(labelStr, string.Empty, string.Empty, string.Empty);
}
int nextAddress = change.Address; int nextAddress = change.Address;
if (nextAddress == Address.NON_ADDR) { if (nextAddress == Address.NON_ADDR) {
// Start non-addressable regions at zero to ensure they don't overflow bank. // Start non-addressable regions at zero to ensure they don't overflow bank.
@ -502,10 +514,12 @@ namespace SourceGen.AsmGen {
// IGenerator // IGenerator
public void FlushArDirectives() { public void FlushArDirectives() {
// TODO(someday): handle IsRelative // TODO(someday): handle IsRelative
Debug.Assert(mNextAddress >= 0);
OutputLine(string.Empty, OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(mNextAddress, 4), SourceFormatter.FormatHexValue(mNextAddress, 4),
string.Empty); string.Empty);
mNextAddress = -1;
} }
// IGenerator // IGenerator

View File

@ -59,7 +59,7 @@ namespace SourceGen.AsmGen {
public AssemblerQuirks Quirks { get; private set; } public AssemblerQuirks Quirks { get; private set; }
// IGenerator // IGenerator
public LabelLocalizer Localizer { get; private set; } public LabelLocalizer Localizer { get { return mLocalizer; } }
public int StartOffset { public int StartOffset {
get { get {
@ -97,6 +97,11 @@ namespace SourceGen.AsmGen {
/// </summary> /// </summary>
private StringBuilder mLineBuilder = new StringBuilder(100); private StringBuilder mLineBuilder = new StringBuilder(100);
/// <summary>
/// Label localization helper.
/// </summary>
private LabelLocalizer mLocalizer;
/// <summary> /// <summary>
/// Stream to send the output to. /// Stream to send the output to.
/// </summary> /// </summary>
@ -289,10 +294,10 @@ namespace SourceGen.AsmGen {
string msg = string.Format(Res.Strings.PROGRESS_GENERATING_FMT, pathName); string msg = string.Format(Res.Strings.PROGRESS_GENERATING_FMT, pathName);
worker.ReportProgress(0, msg); worker.ReportProgress(0, msg);
Localizer = new LabelLocalizer(Project); mLocalizer = new LabelLocalizer(Project);
Localizer.LocalPrefix = "_"; mLocalizer.LocalPrefix = "_";
Localizer.QuirkNoOpcodeMnemonics = true; mLocalizer.QuirkNoOpcodeMnemonics = true;
Localizer.Analyze(); mLocalizer.Analyze();
bool needLongAddress = Project.FileDataLength > 65536 + (mHasPrgHeader ? 2 : 0); bool needLongAddress = Project.FileDataLength > 65536 + (mHasPrgHeader ? 2 : 0);
string extraOptions = string.Empty + string extraOptions = string.Empty +
@ -681,6 +686,10 @@ namespace SourceGen.AsmGen {
nextAddress = 0; nextAddress = 0;
} }
if (change.IsStart) { if (change.IsStart) {
if (change.Region.HasValidPreLabel) {
string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel);
OutputLine(labelStr, string.Empty, string.Empty, string.Empty);
}
if (mPcDepth == 0 && mFirstIsOpen) { if (mPcDepth == 0 && mFirstIsOpen) {
mPcDepth++; mPcDepth++;

View File

@ -65,8 +65,8 @@ offset. For example, the LDA instruction could reference a label at $2008 as "L
The assembler cares about the symbolic references, not the actual offsets or addresses. For The assembler cares about the symbolic references, not the actual offsets or addresses. For
this reason we can ignore references to an address with a label if those references don't this reason we can ignore references to an address with a label if those references don't
actually use the label. (One consequence of this is that formatting an operand as hex actually use the label. (One consequence of this is that formatting an operand as hex
eliminates it from the set of things for us to consider. Also, ORG directives have no effect eliminates it from the set of things for us to consider. Also, address range changes have
on the localizer.) no effect on the localizer unless there's a pre-label defined.)
Labels that are marked as global, but to which there are no references, could in theory be Labels that are marked as global, but to which there are no references, could in theory be
elided. To do this we would have to omit them from the generated code, which would be elided. To do this we would have to omit them from the generated code, which would be
@ -264,44 +264,34 @@ namespace SourceGen.AsmGen {
continue; continue;
} }
string newLabel = sym.LabelWithoutTag; RemapGlobalSymbol(sym, allGlobalLabels, opNames, remapUnders);
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);
} }
// Remap any project/platform symbols that clash with opcode mnemonics. // Remap any project/platform symbols that clash with opcode mnemonics or have
// leading underscores that aren't allowed.
foreach (DefSymbol defSym in mProject.ActiveDefSymbolList) { foreach (DefSymbol defSym in mProject.ActiveDefSymbolList) {
if (opNames != null && opNames.ContainsKey(defSym.Label.ToUpperInvariant())) { //if (opNames != null && opNames.ContainsKey(defSym.Label.ToUpperInvariant())) {
// Clashed with mnemonic. Uniquify it. // // Clashed with mnemonic. Uniquify it.
Debug.WriteLine("Renaming clashing def sym: " + defSym.Label); // Debug.WriteLine("Renaming clashing def sym: " + defSym.Label);
string newLabel = MakeUnique(defSym.Label, allGlobalLabels); // string newLabel = MakeUnique(defSym.Label, allGlobalLabels);
LabelMap[defSym.Label] = newLabel; // LabelMap[defSym.Label] = newLabel;
allGlobalLabels.Add(newLabel, newLabel); // allGlobalLabels.Add(newLabel, newLabel);
//}
RemapGlobalSymbol(defSym, allGlobalLabels, opNames, remapUnders);
}
// Remap any address region pre-labels with inappropriate values.
IEnumerator<CommonUtil.AddressMap.AddressChange> addrIter =
mProject.AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
CommonUtil.AddressMap.AddressChange change = addrIter.Current;
if (!change.IsStart || !change.Region.HasValidPreLabel) {
continue;
} }
Symbol sym = new Symbol(change.Region.PreLabel, change.Region.PreLabelAddress,
Symbol.Source.AddrPreLabel, Symbol.Type.ExternalAddr,
Symbol.LabelAnnotation.None);
RemapGlobalSymbol(sym, allGlobalLabels, opNames, remapUnders);
} }
// //
@ -353,6 +343,48 @@ namespace SourceGen.AsmGen {
} }
} }
/// <summary>
/// Remaps a global label, handling promotion from local, leading underscores, and
/// clashes with opcode mnemonics.
/// </summary>
/// <param name="sym">Symbol to rename.</param>
/// <param name="allGlobalLabels">List of all global labels, used when uniquifing
/// a "promoted" local. May be updated.</param>
/// <param name="opNames">List of opcode mnemonics for configured CPU. Will be
/// null if the assembler allows labels to be the same as opcodes.</param>
/// <param name="remapUnders">True if leading underscores are not allowed (because
/// they're used to indicate local labels).</param>
private void RemapGlobalSymbol(Symbol sym, Dictionary<string, string> allGlobalLabels,
Dictionary<string, Asm65.OpDef> opNames, bool remapUnders) {
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);
}
/// <summary> /// <summary>
/// Generates the initial mGlobalFlags and mGlobalLabels lists, as well as the /// Generates the initial mGlobalFlags and mGlobalLabels lists, as well as the
/// full cross-reference pair list. /// full cross-reference pair list.
@ -407,6 +439,19 @@ namespace SourceGen.AsmGen {
} }
} }
} }
// Add the AddressRegion pre-labels to the list, as globals. We may need to
// re-map the label if it matches a mnemonic.
IEnumerator<CommonUtil.AddressMap.AddressChange> addrIter =
mProject.AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
CommonUtil.AddressMap.AddressChange change = addrIter.Current;
if (!change.IsStart || !change.Region.HasValidPreLabel) {
continue;
}
mGlobalFlags[change.Region.Offset] = true;
mGlobalLabels.Add(new OffsetLabel(change.Region.Offset, change.Region.PreLabel));
}
} }
/// <summary> /// <summary>

View File

@ -828,6 +828,9 @@ namespace SourceGen {
SymbolTable.Clear(); SymbolTable.Clear();
MergePlatformProjectSymbols(); MergePlatformProjectSymbols();
// Merge in any address region pre-labels.
MergeAddressPreLabels();
// Merge user labels into the symbol table, overwriting platform/project symbols // Merge user labels into the symbol table, overwriting platform/project symbols
// where they conflict. Labels whose values are out of sync (because of a change // where they conflict. Labels whose values are out of sync (because of a change
// to the address map) are updated as part of this. // to the address map) are updated as part of this.
@ -1174,7 +1177,7 @@ namespace SourceGen {
/// ///
/// This should be done before any other symbol assignment or generation, so that user /// This should be done before any other symbol assignment or generation, so that user
/// labels take precedence (by virtue of overwriting the earlier platform symbols), /// labels take precedence (by virtue of overwriting the earlier platform symbols),
/// and auto label generation can propery generate a unique label. /// and auto label generation can properly generate a unique label.
/// ///
/// Within platform symbol loading, later symbols should replace earlier symbols, /// Within platform symbol loading, later symbols should replace earlier symbols,
/// so that ordering of platform files behaves in an intuitive fashion. /// so that ordering of platform files behaves in an intuitive fashion.
@ -1202,6 +1205,27 @@ namespace SourceGen {
} }
} }
/// <summary>
/// Merges symbols from AddressMap into SymbolTable. Existing entries with matching
/// labels will be replaced.
/// </summary>
/// <remarks>
/// These are external symbols, with a higher precedence than project symbols.
/// </remarks>
private void MergeAddressPreLabels() {
IEnumerator<AddressMap.AddressChange> addrIter = AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
AddressMap.AddressRegion region = addrIter.Current.Region;
if (addrIter.Current.IsStart && region.HasValidPreLabel) {
// Generate a DefSymbol for it, and add it to the symbol table.
Symbol sym = new Symbol(region.PreLabel, region.PreLabelAddress,
Symbol.Source.AddrPreLabel, Symbol.Type.ExternalAddr,
Symbol.LabelAnnotation.None);
SymbolTable[sym.Label] = sym;
}
}
}
/// <summary> /// <summary>
/// Returns true if a symbol with a matching label appears in the project symbols /// Returns true if a symbol with a matching label appears in the project symbols
/// or any of the platform lists. This operates on the raw lists, not SymbolTable. /// or any of the platform lists. This operates on the raw lists, not SymbolTable.
@ -1384,7 +1408,8 @@ namespace SourceGen {
} }
/// <summary> /// <summary>
/// Generates references to symbols in the project/platform symbol tables. /// Generates references to symbols in the project/platform symbol tables. Also picks
/// up references to address region pre-labels.
/// ///
/// For each instruction or data item that appears to reference an address, and /// For each instruction or data item that appears to reference an address, and
/// does not have a target offset, look for a matching address in the symbol tables. /// does not have a target offset, look for a matching address in the symbol tables.
@ -1548,6 +1573,20 @@ namespace SourceGen {
} }
} }
} }
// Add all address region pre-labels, regardless of whether or not their parent
// is non-addressable. 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 associated it with a file offset.
foreach (AddressMap.AddressMapEntry ent in AddrMap) {
if (!string.IsNullOrEmpty(ent.PreLabel)) {
try {
labelList.Add(ent.PreLabel, ent.Offset);
} catch (ArgumentException ex) {
Debug.WriteLine("Xref ignoring pre-label duplicate '" + ent.PreLabel +
"': " + ex.Message);
}
}
}
// No particular reason to do this here, but it's convenient. // No particular reason to do this here, but it's convenient.
ByteCounts.Reset(); ByteCounts.Reset();
@ -2611,22 +2650,33 @@ namespace SourceGen {
/// <summary> /// <summary>
/// Finds a label by name. SymbolTable must be populated. /// Finds a label by name. SymbolTable must be populated.
/// </summary> /// </summary>
/// <remarks>
/// We're interested in user labels and auto-generated labels. Do a lookup in
/// SymbolTable to find the symbol, then if it's user or auto, we do a second
/// search to find the file offset it's associated with. The second search
/// requires a linear walk through anattribs; if we do this often we'll want to
/// maintain a symbol-to-offset structure.
///
/// We're also interested in address region pre-labels. Those are technically
/// external symbols, but they appear in the label field.
///
/// This will not find "hidden" labels, i.e. labels that are in the middle of an
/// instruction or multi-byte data area, because those are removed from SymbolTable.
/// </remarks>
/// <param name="name">Label to find.</param> /// <param name="name">Label to find.</param>
/// <returns>File offset associated with label, or -1 if not found.</returns> /// <returns>File offset associated with label, or -1 if not found.</returns>
public int FindLabelOffsetByName(string name) { public int FindLabelOffsetByName(string name) {
// We're interested in user labels and auto-generated labels. Do a lookup in
// SymbolTable to find the symbol, then if it's user or auto, we do a second
// search to find the file offset it's associated with. The second search
// requires a linear walk through anattribs; if we do this often we'll want to
// maintain a symbol-to-offset structure.
//
// This will not find "hidden" labels, i.e. labels that are in the middle of an
// instruction or multi-byte data area, because those are removed from SymbolTable.
if (!SymbolTable.TryGetValue(name, out Symbol sym)) { if (!SymbolTable.TryGetValue(name, out Symbol sym)) {
return -1; return -1;
} }
if (!sym.IsInternalLabel) { if (!sym.IsInternalLabel) {
if (sym.SymbolSource == Symbol.Source.AddrPreLabel) {
foreach (AddressMap.AddressMapEntry ent in AddrMap) {
if (ent.PreLabel == sym.Label) {
return ent.Offset;
}
}
}
return -1; return -1;
} }
for (int i = 0; i < mAnattribs.Length; i++) { for (int i = 0; i < mAnattribs.Length; i++) {

View File

@ -458,19 +458,26 @@ namespace SourceGen {
return parts; return parts;
} }
public static FormattedParts CreateDirective(string opstr, string addrStr) { public static FormattedParts CreateDirective(string opstr, string operandStr) {
FormattedParts parts = new FormattedParts(); FormattedParts parts = new FormattedParts();
parts.Opcode = opstr; parts.Opcode = opstr;
parts.Operand = addrStr; parts.Operand = operandStr;
return parts;
}
public static FormattedParts CreatePreLabelDirective(string addrStr, string labelStr) {
FormattedParts parts = new FormattedParts();
parts.Addr = addrStr;
parts.Label = labelStr;
return parts; return parts;
} }
public static FormattedParts CreateFullDirective(string label, string opstr, public static FormattedParts CreateFullDirective(string label, string opstr,
string addrStr, string comment) { string operandStr, string comment) {
FormattedParts parts = new FormattedParts(); FormattedParts parts = new FormattedParts();
parts.Label = label; parts.Label = label;
parts.Opcode = opstr; parts.Opcode = opstr;
parts.Operand = addrStr; parts.Operand = operandStr;
parts.Comment = comment; parts.Comment = comment;
return parts; return parts;
} }

View File

@ -1081,7 +1081,16 @@ namespace SourceGen {
spaceAdded = false; // next one will need a blank line spaceAdded = false; // next one will need a blank line
multiStart = true; // unless it's another .arstart immediately multiStart = true; // unless it's another .arstart immediately
// TODO(org): pre-label (address / label only, logically part of ORG) if (region.HasValidPreLabel) {
Line preLine =
new Line(offset, 0, Line.Type.ArStartDirective, arSubLine++);
string preAddrStr = mFormatter.FormatAddress(region.PreLabelAddress,
!mProject.CpuDef.HasAddr16);
preLine.Parts = FormattedParts.CreatePreLabelDirective(preAddrStr,
region.PreLabel);
lines.Add(preLine);
}
Line newLine = new Line(offset, 0, Line.Type.ArStartDirective, arSubLine++); Line newLine = new Line(offset, 0, Line.Type.ArStartDirective, arSubLine++);
string addrStr; string addrStr;
if (region.Address == Address.NON_ADDR) { if (region.Address == Address.NON_ADDR) {
@ -1274,6 +1283,15 @@ namespace SourceGen {
// Check for address region ends, which will be positioned one byte before the // Check for address region ends, which will be positioned one byte before the
// updated offset (unless they somehow got embedded inside something else). // updated offset (unless they somehow got embedded inside something else).
//
// The .arend can only appear on the same offset as .arstart in a single-byte
// region, and we can't have more than one of those at the same offset.
// If this is not a single-byte region, we need to reset the sub-line count.
// (Alternatively: track the offset of the last .arstart, and reset subline
// if they differ.)
if (addrIter.Current != null && addrIter.Current.Region.Length != 1) {
arSubLine = 0;
}
while (addrIter.Current != null && addrIter.Current.Offset < offset) { while (addrIter.Current != null && addrIter.Current.Offset < offset) {
AddressMap.AddressChange change = addrIter.Current; AddressMap.AddressChange change = addrIter.Current;
// Range starts/ends shouldn't be embedded in something. // Range starts/ends shouldn't be embedded in something.
@ -1283,13 +1301,6 @@ namespace SourceGen {
(offset - 1).ToString("x6")); (offset - 1).ToString("x6"));
} }
// The .arend can only appear on the same offset as .arstart in a single-byte
// region, and we can't have more than one of those at the same offset.
// If this is not a single-byte region, we need to reset the sub-line count.
if (change.Region.Length != 1) {
arSubLine = 0;
}
if (change.Region.Offset == 0 && hasPrgHeader) { if (change.Region.Offset == 0 && hasPrgHeader) {
// Suppress the .arend at offset +000001. // Suppress the .arend at offset +000001.
addrIter.MoveNext(); addrIter.MoveNext();
@ -1299,11 +1310,12 @@ namespace SourceGen {
if (!change.IsStart) { if (!change.IsStart) {
// Show the start address to make it easier to pair them visually. // Show the start address to make it easier to pair them visually.
string floatStr = change.Region.IsFloating ? "~" : "";
string addrStr; string addrStr;
if (change.Region.Address == Address.NON_ADDR) { if (change.Region.Address == Address.NON_ADDR) {
addrStr = "\u2191 " + Address.NON_ADDR_STR; addrStr = "\u2191 " + floatStr + Address.NON_ADDR_STR;
} else { } else {
addrStr = "\u2191 " + addrStr = "\u2191 " + floatStr +
mFormatter.FormatHexValue(change.Region.Address, 4); mFormatter.FormatHexValue(change.Region.Address, 4);
} }
@ -1654,12 +1666,14 @@ namespace SourceGen {
while (addrIter.Current != null && addrIter.Current.Offset == offset) { while (addrIter.Current != null && addrIter.Current.Offset == offset) {
AddressMap.AddressChange change = addrIter.Current; AddressMap.AddressChange change = addrIter.Current;
if (count == 0) { if (count == 0) {
// Could be pre-label or .arstart that follows it. Both go to the same place.
isSynth = change.IsSynthetic; isSynth = change.IsSynthetic;
return change.Region; return change.Region;
} }
if (change.IsStart && !string.IsNullOrEmpty(change.Region.PreLabel)) { if (change.IsStart && change.Region.HasValidPreLabel) {
count--; count--;
if (count == 0) { if (count == 0) {
// This is the .arstart following the pre-label.
isSynth = change.IsSynthetic; isSynth = change.IsSynthetic;
return change.Region; return change.Region;
} }

View File

@ -324,6 +324,7 @@ namespace SourceGen {
settings.SetBool(AppSettings.SYMWIN_SHOW_NON_UNIQUE, false); settings.SetBool(AppSettings.SYMWIN_SHOW_NON_UNIQUE, false);
settings.SetBool(AppSettings.SYMWIN_SHOW_PROJECT, true); settings.SetBool(AppSettings.SYMWIN_SHOW_PROJECT, true);
settings.SetBool(AppSettings.SYMWIN_SHOW_PLATFORM, false); settings.SetBool(AppSettings.SYMWIN_SHOW_PLATFORM, false);
settings.SetBool(AppSettings.SYMWIN_SHOW_ADDR_PRE_LABELS, true);
settings.SetBool(AppSettings.SYMWIN_SHOW_AUTO, false); settings.SetBool(AppSettings.SYMWIN_SHOW_AUTO, false);
settings.SetBool(AppSettings.SYMWIN_SHOW_ADDR, true); settings.SetBool(AppSettings.SYMWIN_SHOW_ADDR, true);
settings.SetBool(AppSettings.SYMWIN_SHOW_CONST, true); settings.SetBool(AppSettings.SYMWIN_SHOW_CONST, true);
@ -549,6 +550,8 @@ namespace SourceGen {
settings.GetBool(AppSettings.SYMWIN_SHOW_PROJECT, false); settings.GetBool(AppSettings.SYMWIN_SHOW_PROJECT, false);
mMainWin.SymFilterPlatformSymbols = mMainWin.SymFilterPlatformSymbols =
settings.GetBool(AppSettings.SYMWIN_SHOW_PLATFORM, false); settings.GetBool(AppSettings.SYMWIN_SHOW_PLATFORM, false);
mMainWin.SymFilterAddrPreLabels =
settings.GetBool(AppSettings.SYMWIN_SHOW_ADDR_PRE_LABELS, false);
mMainWin.SymFilterConstants = mMainWin.SymFilterConstants =
settings.GetBool(AppSettings.SYMWIN_SHOW_CONST, false); settings.GetBool(AppSettings.SYMWIN_SHOW_CONST, false);
mMainWin.SymFilterAddresses = mMainWin.SymFilterAddresses =
@ -1698,10 +1701,12 @@ namespace SourceGen {
} else { } else {
if (mProject.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) { if (mProject.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) {
if (sym.SymbolSource == Symbol.Source.User || if (sym.SymbolSource == Symbol.Source.User ||
sym.SymbolSource == Symbol.Source.Auto) { sym.SymbolSource == Symbol.Source.Auto ||
sym.SymbolSource == Symbol.Source.AddrPreLabel) {
int labelOffset = mProject.FindLabelOffsetByName(dfd.SymbolRef.Label); int labelOffset = mProject.FindLabelOffsetByName(dfd.SymbolRef.Label);
if (labelOffset >= 0) { if (labelOffset >= 0) {
if (!testOnly) { if (!testOnly) {
// TODO(org): jump to .arstart
GoToLocation(new NavStack.Location(labelOffset, 0, false), GoToLocation(new NavStack.Location(labelOffset, 0, false),
GoToMode.JumpToCodeData, true); GoToMode.JumpToCodeData, true);
} }
@ -1894,9 +1899,12 @@ namespace SourceGen {
if (curRegion == null) { if (curRegion == null) {
// No entry, create a new one. Use the current address as the default value, // No entry, create a new one. Use the current address as the default value,
// unless the region is non-addressable. // unless the region is non-addressable.
int addr = addrMap.OffsetToAddress(firstOffset); Anattrib attr = mProject.GetAnattrib(firstOffset);
if (addr == Address.NON_ADDR) { int addr;
addr = 0; if (attr.IsNonAddressable) {
addr = Address.NON_ADDR;
} else {
addr = attr.Address;
} }
// Create a prototype entry with the various values. // Create a prototype entry with the various values.
@ -3101,16 +3109,12 @@ namespace SourceGen {
/// </summary> /// </summary>
/// <param name="sym">Label symbol.</param> /// <param name="sym">Label symbol.</param>
public void GoToLabel(Symbol sym) { public void GoToLabel(Symbol sym) {
if (sym.IsInternalLabel) { int offset = mProject.FindLabelOffsetByName(sym.Label);
int offset = mProject.FindLabelOffsetByName(sym.Label); if (offset >= 0) {
if (offset >= 0) { GoToLocation(new NavStack.Location(offset, 0, false),
GoToLocation(new NavStack.Location(offset, 0, false), GoToMode.JumpToCodeData, true);
GoToMode.JumpToCodeData, true);
} else {
Debug.WriteLine("DClick symbol: " + sym + ": label not found");
}
} else { } else {
Debug.WriteLine("DClick symbol: " + sym + ": not label"); Debug.WriteLine("DClick symbol: " + sym + ": label not found");
} }
} }

View File

@ -332,17 +332,20 @@ namespace SourceGen.Sandbox {
foreach (Symbol sym in symTab) { foreach (Symbol sym in symTab) {
PlSymbol.Source plsSource; PlSymbol.Source plsSource;
switch (sym.SymbolSource) { switch (sym.SymbolSource) {
case Symbol.Source.Platform: case Symbol.Source.User:
plsSource = PlSymbol.Source.Platform; plsSource = PlSymbol.Source.User;
break;
case Symbol.Source.AddrPreLabel:
plsSource = PlSymbol.Source.AddrPreLabel;
break; break;
case Symbol.Source.Project: case Symbol.Source.Project:
plsSource = PlSymbol.Source.Project; plsSource = PlSymbol.Source.Project;
break; break;
case Symbol.Source.User: case Symbol.Source.Platform:
plsSource = PlSymbol.Source.User; plsSource = PlSymbol.Source.Platform;
break; break;
case Symbol.Source.Variable:
case Symbol.Source.Auto: case Symbol.Source.Auto:
case Symbol.Source.Variable:
// don't forward these to plugins // don't forward these to plugins
continue; continue;
default: default:

View File

@ -36,6 +36,7 @@ namespace SourceGen {
// can have the same value. // can have the same value.
Unknown = 0, Unknown = 0,
User, // user-defined; only used for internal address labels User, // user-defined; only used for internal address labels
AddrPreLabel, // external address, from address region pre-label
Project, // external address or const, from project configuration file Project, // external address or const, from project configuration file
Platform, // external address or const, from platform definition file Platform, // external address or const, from platform definition file
Auto, // auto-generated internal address label Auto, // auto-generated internal address label
@ -192,6 +193,7 @@ namespace SourceGen {
switch (SymbolSource) { switch (SymbolSource) {
case Source.Auto: sc = 'A'; break; case Source.Auto: sc = 'A'; break;
case Source.User: sc = 'U'; break; case Source.User: sc = 'U'; break;
case Source.AddrPreLabel: sc = 'R'; break;
case Source.Platform: sc = 'P'; break; case Source.Platform: sc = 'P'; break;
case Source.Project: sc = 'J'; break; case Source.Project: sc = 'J'; break;
case Source.Variable: sc = 'V'; break; case Source.Variable: sc = 'V'; break;

View File

@ -173,7 +173,8 @@ limitations under the License.
</TextBox> </TextBox>
</StackPanel> </StackPanel>
<TextBlock Text="• Enter 16-bit or 24-bit address, e.g. $1000 or 01/be00" Margin="0,4,0,0"/> <TextBlock Name="enterAddressLabel" Margin="0,4,0,0"
Text="• Enter 16-bit or 24-bit address, e.g. $1000 or 01/be00"/>
<TextBlock Text="• Enter 'NA' to mark the region non-addressable"/> <TextBlock Text="• Enter 'NA' to mark the region non-addressable"/>
</StackPanel> </StackPanel>
@ -195,9 +196,12 @@ limitations under the License.
<TextBlock Text=")"/> <TextBlock Text=")"/>
</StackPanel> </StackPanel>
<TextBlock Text="• Must be valid label syntax; may not be a local label" Margin="0,4,0,0"/> <TextBlock Name="validSyntaxLabel" Margin="0,4,0,0"
<TextBlock Text="• Must not be a duplicate of an existing label"/> Text="• Must be valid label syntax; may not be a local label"/>
<TextBlock Text="• Will not appear if parent region is non-addressable"/> <TextBlock Name="notDuplicateLabel"
Text="• Must not be a duplicate of an existing label"/>
<TextBlock Name="parentNonAddrLabel"
Text="• Will not appear if parent region is non-addressable"/>
</StackPanel> </StackPanel>
</GroupBox> </GroupBox>
</StackPanel> </StackPanel>

View File

@ -20,6 +20,7 @@ using System.Runtime.CompilerServices;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Media;
using Asm65; using Asm65;
using CommonUtil; using CommonUtil;
@ -198,12 +199,26 @@ namespace SourceGen.WpfGui {
} }
private bool mCanDeleteRegion; private bool mCanDeleteRegion;
// INotifyPropertyChanged implementation // INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "") { private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
// Non-error color for labels.
private Brush mDefaultLabelColor = SystemColors.WindowTextBrush;
/// <summary>
/// Initial value for pre-label.
/// </summary>
private string mOrigPreLabel;
/// <summary>
/// True if parent region is non-addressable.
/// </summary>
private bool mParentNonAddr;
/// <summary> /// <summary>
/// Result for option #1. /// Result for option #1.
/// </summary> /// </summary>
@ -277,6 +292,8 @@ namespace SourceGen.WpfGui {
// Editing an existing region. // Editing an existing region.
CanDeleteRegion = true; CanDeleteRegion = true;
ShowExistingRegion = true; ShowExistingRegion = true;
mOrigPreLabel = curRegion.PreLabel;
mParentNonAddr = (curRegion.PreLabelAddress == Address.NON_ADDR);
if (curRegion.Address == Address.NON_ADDR) { if (curRegion.Address == Address.NON_ADDR) {
AddressText = Address.NON_ADDR_STR; AddressText = Address.NON_ADDR_STR;
@ -354,8 +371,13 @@ namespace SourceGen.WpfGui {
// a floating region. Default changes for single-item selections. // a floating region. Default changes for single-item selections.
CanDeleteRegion = false; CanDeleteRegion = false;
ShowExistingRegion = false; ShowExistingRegion = false;
mOrigPreLabel = string.Empty;
AddressText = Asm65.Address.AddressToString(newEntry.Address, false); if (newEntry.Address == Address.NON_ADDR) {
AddressText = Address.NON_ADDR_STR;
} else {
AddressText = Asm65.Address.AddressToString(newEntry.Address, false);
}
PreLabelText = string.Empty; PreLabelText = string.Empty;
UseRelativeAddressing = false; UseRelativeAddressing = false;
@ -386,6 +408,8 @@ namespace SourceGen.WpfGui {
mFormatter.FormatOffset24(newEntry.Offset), mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion1.ActualLength)); FormatLength(newRegion1.ActualLength));
mPreLabelAddress = newRegion1.PreLabelAddress; mPreLabelAddress = newRegion1.PreLabelAddress;
mParentNonAddr = (newRegion1.PreLabelAddress == Address.NON_ADDR);
} else { } else {
option1Summ = string.Empty; option1Summ = string.Empty;
if (ares1 == AddressMap.AddResult.StraddleExisting) { if (ares1 == AddressMap.AddResult.StraddleExisting) {
@ -406,6 +430,8 @@ namespace SourceGen.WpfGui {
mFormatter.FormatOffset24(newEntry.Offset), mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion2.ActualLength)); FormatLength(newRegion2.ActualLength));
mPreLabelAddress = newRegion2.PreLabelAddress; mPreLabelAddress = newRegion2.PreLabelAddress;
mParentNonAddr = (newRegion2.PreLabelAddress == Address.NON_ADDR);
} else { } else {
option2Summ = string.Empty; option2Summ = string.Empty;
option2Msg = (string)FindResource("str_CreateFloatingFail"); option2Msg = (string)FindResource("str_CreateFloatingFail");
@ -510,8 +536,53 @@ namespace SourceGen.WpfGui {
/// for TextBox is LostFocus. /// for TextBox is LostFocus.
/// </remarks> /// </remarks>
private void UpdateControls() { private void UpdateControls() {
IsValid = EnableAttributeControls && ParseAddress(out int unused); bool addrOkay = ParseAddress(out int unused);
// TODO(org): check pre-label syntax if (addrOkay) {
enterAddressLabel.Foreground = mDefaultLabelColor;
} else {
enterAddressLabel.Foreground = Brushes.Red;
}
bool preLabelOkay = PreLabelTextChanged();
IsValid = EnableAttributeControls && addrOkay && preLabelOkay;
}
/// <summary>
/// Validates pre-label.
/// </summary>
private bool PreLabelTextChanged() {
string label = PreLabelText;
parentNonAddrLabel.Foreground = mDefaultLabelColor;
if (string.IsNullOrEmpty(label)) {
return true;
}
if (mParentNonAddr) {
parentNonAddrLabel.Foreground = Brushes.Blue;
}
// Check syntax. We don't care about the details.
bool isValid = Asm65.Label.ValidateLabelDetail(label, out bool unused1,
out bool unused2);
if (!isValid) {
validSyntaxLabel.Foreground = Brushes.Red;
return false;
} else {
validSyntaxLabel.Foreground = mDefaultLabelColor;
}
// Check for duplicates.
notDuplicateLabel.Foreground = mDefaultLabelColor;
if (label == mOrigPreLabel) {
return true;
}
if (mProject.SymbolTable.TryGetValue(label, out Symbol sym)) {
notDuplicateLabel.Foreground = Brushes.Red;
return false;
}
return true;
} }
private void OkButton_Click(object sender, RoutedEventArgs e) { private void OkButton_Click(object sender, RoutedEventArgs e) {

View File

@ -803,6 +803,8 @@ limitations under the License.
IsChecked="{Binding SymFilterProjectSymbols}"/> IsChecked="{Binding SymFilterProjectSymbols}"/>
<ToggleButton Content="Plat" Width="40" Margin="2,2" <ToggleButton Content="Plat" Width="40" Margin="2,2"
IsChecked="{Binding SymFilterPlatformSymbols}"/> IsChecked="{Binding SymFilterPlatformSymbols}"/>
<ToggleButton Content="PreL" Width="40" Margin="2,2"
IsChecked="{Binding SymFilterAddrPreLabels}"/>
<ToggleButton Content="Auto" Width="40" Margin="2,2" <ToggleButton Content="Auto" Width="40" Margin="2,2"
IsChecked="{Binding SymFilterAutoLabels}"/> IsChecked="{Binding SymFilterAutoLabels}"/>
<ToggleButton Content="Addr" Width="40" Margin="2,2" <ToggleButton Content="Addr" Width="40" Margin="2,2"

View File

@ -1769,6 +1769,16 @@ namespace SourceGen.WpfGui {
OnPropertyChanged(); OnPropertyChanged();
} }
} }
private bool mSymFilterAddrPreLabels;
public bool SymFilterAddrPreLabels {
get { return mSymFilterAddrPreLabels; }
set {
mSymFilterAddrPreLabels = value;
AppSettings.Global.SetBool(AppSettings.SYMWIN_SHOW_ADDR_PRE_LABELS, value);
SymbolsListFilterChanged();
OnPropertyChanged();
}
}
private bool mSymFilterAutoLabels; private bool mSymFilterAutoLabels;
public bool SymFilterAutoLabels { public bool SymFilterAutoLabels {
get { return mSymFilterAutoLabels; } get { return mSymFilterAutoLabels; }
@ -1834,6 +1844,7 @@ namespace SourceGen.WpfGui {
} }
SymbolsListItem sli = (SymbolsListItem)item; SymbolsListItem sli = (SymbolsListItem)item;
// TODO: this should also work for project/platform symbols that have EQU directives
mMainCtrl.GoToLabel(sli.Sym); mMainCtrl.GoToLabel(sli.Sym);
codeListView.Focus(); codeListView.Focus();
} }
@ -1847,6 +1858,7 @@ namespace SourceGen.WpfGui {
(SymFilterNonUniqueLabels != true && sli.Sym.IsNonUnique) || (SymFilterNonUniqueLabels != true && sli.Sym.IsNonUnique) ||
(SymFilterProjectSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Project) || (SymFilterProjectSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Project) ||
(SymFilterPlatformSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Platform) || (SymFilterPlatformSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Platform) ||
(SymFilterAddrPreLabels != true && sli.Sym.SymbolSource == Symbol.Source.AddrPreLabel) ||
(SymFilterAutoLabels != true && sli.Sym.SymbolSource == Symbol.Source.Auto) || (SymFilterAutoLabels != true && sli.Sym.SymbolSource == Symbol.Source.Auto) ||
(SymFilterAddresses != true && !sli.Sym.IsConstant) || (SymFilterAddresses != true && !sli.Sym.IsConstant) ||
(SymFilterConstants != true && sli.Sym.IsConstant) || (SymFilterConstants != true && sli.Sym.IsConstant) ||