1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-11 17:29:29 +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?
/// </summary>
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 {
return Length == FLOATING_LEN;
return !string.IsNullOrEmpty(PreLabel) && PreLabelAddress != NON_ADDR;
}
}

View File

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

View File

@ -84,6 +84,7 @@ namespace SourceGen {
public const string SYMWIN_SHOW_AUTO = "symwin-show-auto";
public const string SYMWIN_SHOW_PROJECT = "symwin-show-project";
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_ADDR = "symwin-show-addr";
public const string SYMWIN_SORT_ASCENDING = "symwin-sort-ascending";

View File

@ -579,6 +579,10 @@ namespace SourceGen.AsmGen {
nextAddress = 0;
}
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) {
mPcDepth++;

View File

@ -540,6 +540,18 @@ namespace SourceGen.AsmGen {
// IGenerator
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;
if (nextAddress == Address.NON_ADDR) {
// 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.FormatHexValue(mNextAddress, 4),
string.Empty);
mNextAddress = -1;
}
// IGenerator

View File

@ -106,7 +106,7 @@ namespace SourceGen.AsmGen {
{ "EquDirective", "equ" },
{ "VarDirective", "equ" },
{ "ArStartDirective", "org" },
//ArEndDirective
{ "ArEndDirective", "org_end" }, // not actually used
//RegWidthDirective
//DataBankDirective
{ "DefineData1", "dfb" },
@ -491,6 +491,18 @@ namespace SourceGen.AsmGen {
// IGenerator
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;
if (nextAddress == Address.NON_ADDR) {
// Start non-addressable regions at zero to ensure they don't overflow bank.
@ -502,10 +514,12 @@ namespace SourceGen.AsmGen {
// IGenerator
public void FlushArDirectives() {
// TODO(someday): handle IsRelative
Debug.Assert(mNextAddress >= 0);
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(mNextAddress, 4),
string.Empty);
mNextAddress = -1;
}
// IGenerator

View File

@ -59,7 +59,7 @@ namespace SourceGen.AsmGen {
public AssemblerQuirks Quirks { get; private set; }
// IGenerator
public LabelLocalizer Localizer { get; private set; }
public LabelLocalizer Localizer { get { return mLocalizer; } }
public int StartOffset {
get {
@ -97,6 +97,11 @@ namespace SourceGen.AsmGen {
/// </summary>
private StringBuilder mLineBuilder = new StringBuilder(100);
/// <summary>
/// Label localization helper.
/// </summary>
private LabelLocalizer mLocalizer;
/// <summary>
/// Stream to send the output to.
/// </summary>
@ -289,10 +294,10 @@ namespace SourceGen.AsmGen {
string msg = string.Format(Res.Strings.PROGRESS_GENERATING_FMT, pathName);
worker.ReportProgress(0, msg);
Localizer = new LabelLocalizer(Project);
Localizer.LocalPrefix = "_";
Localizer.QuirkNoOpcodeMnemonics = true;
Localizer.Analyze();
mLocalizer = new LabelLocalizer(Project);
mLocalizer.LocalPrefix = "_";
mLocalizer.QuirkNoOpcodeMnemonics = true;
mLocalizer.Analyze();
bool needLongAddress = Project.FileDataLength > 65536 + (mHasPrgHeader ? 2 : 0);
string extraOptions = string.Empty +
@ -681,6 +686,10 @@ namespace SourceGen.AsmGen {
nextAddress = 0;
}
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) {
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
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
eliminates it from the set of things for us to consider. Also, ORG directives have no effect
on the localizer.)
eliminates it from the set of things for us to consider. Also, address range changes have
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
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;
}
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);
RemapGlobalSymbol(sym, allGlobalLabels, opNames, remapUnders);
}
// 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) {
if (opNames != null && opNames.ContainsKey(defSym.Label.ToUpperInvariant())) {
// Clashed with mnemonic. Uniquify it.
Debug.WriteLine("Renaming clashing def sym: " + defSym.Label);
string newLabel = MakeUnique(defSym.Label, allGlobalLabels);
LabelMap[defSym.Label] = newLabel;
allGlobalLabels.Add(newLabel, newLabel);
//if (opNames != null && opNames.ContainsKey(defSym.Label.ToUpperInvariant())) {
// // Clashed with mnemonic. Uniquify it.
// Debug.WriteLine("Renaming clashing def sym: " + defSym.Label);
// string newLabel = MakeUnique(defSym.Label, allGlobalLabels);
// LabelMap[defSym.Label] = 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>
/// Generates the initial mGlobalFlags and mGlobalLabels lists, as well as the
/// 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>

View File

@ -828,6 +828,9 @@ namespace SourceGen {
SymbolTable.Clear();
MergePlatformProjectSymbols();
// Merge in any address region pre-labels.
MergeAddressPreLabels();
// Merge user labels into the symbol table, overwriting platform/project symbols
// where they conflict. Labels whose values are out of sync (because of a change
// to the address map) are updated as part of this.
@ -1174,7 +1177,7 @@ namespace SourceGen {
///
/// 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),
/// 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,
/// 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>
/// 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.
@ -1384,7 +1408,8 @@ namespace SourceGen {
}
/// <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
/// 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.
ByteCounts.Reset();
@ -2611,22 +2650,33 @@ namespace SourceGen {
/// <summary>
/// Finds a label by name. SymbolTable must be populated.
/// </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>
/// <returns>File offset associated with label, or -1 if not found.</returns>
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)) {
return -1;
}
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;
}
for (int i = 0; i < mAnattribs.Length; i++) {

View File

@ -458,19 +458,26 @@ namespace SourceGen {
return parts;
}
public static FormattedParts CreateDirective(string opstr, string addrStr) {
public static FormattedParts CreateDirective(string opstr, string operandStr) {
FormattedParts parts = new FormattedParts();
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;
}
public static FormattedParts CreateFullDirective(string label, string opstr,
string addrStr, string comment) {
string operandStr, string comment) {
FormattedParts parts = new FormattedParts();
parts.Label = label;
parts.Opcode = opstr;
parts.Operand = addrStr;
parts.Operand = operandStr;
parts.Comment = comment;
return parts;
}

View File

@ -1081,7 +1081,16 @@ namespace SourceGen {
spaceAdded = false; // next one will need a blank line
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++);
string addrStr;
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
// 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) {
AddressMap.AddressChange change = addrIter.Current;
// Range starts/ends shouldn't be embedded in something.
@ -1283,13 +1301,6 @@ namespace SourceGen {
(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) {
// Suppress the .arend at offset +000001.
addrIter.MoveNext();
@ -1299,11 +1310,12 @@ namespace SourceGen {
if (!change.IsStart) {
// Show the start address to make it easier to pair them visually.
string floatStr = change.Region.IsFloating ? "~" : "";
string addrStr;
if (change.Region.Address == Address.NON_ADDR) {
addrStr = "\u2191 " + Address.NON_ADDR_STR;
addrStr = "\u2191 " + floatStr + Address.NON_ADDR_STR;
} else {
addrStr = "\u2191 " +
addrStr = "\u2191 " + floatStr +
mFormatter.FormatHexValue(change.Region.Address, 4);
}
@ -1654,12 +1666,14 @@ namespace SourceGen {
while (addrIter.Current != null && addrIter.Current.Offset == offset) {
AddressMap.AddressChange change = addrIter.Current;
if (count == 0) {
// Could be pre-label or .arstart that follows it. Both go to the same place.
isSynth = change.IsSynthetic;
return change.Region;
}
if (change.IsStart && !string.IsNullOrEmpty(change.Region.PreLabel)) {
if (change.IsStart && change.Region.HasValidPreLabel) {
count--;
if (count == 0) {
// This is the .arstart following the pre-label.
isSynth = change.IsSynthetic;
return change.Region;
}

View File

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

View File

@ -36,6 +36,7 @@ namespace SourceGen {
// can have the same value.
Unknown = 0,
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
Platform, // external address or const, from platform definition file
Auto, // auto-generated internal address label
@ -192,6 +193,7 @@ namespace SourceGen {
switch (SymbolSource) {
case Source.Auto: sc = 'A'; break;
case Source.User: sc = 'U'; break;
case Source.AddrPreLabel: sc = 'R'; break;
case Source.Platform: sc = 'P'; break;
case Source.Project: sc = 'J'; break;
case Source.Variable: sc = 'V'; break;

View File

@ -173,7 +173,8 @@ limitations under the License.
</TextBox>
</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"/>
</StackPanel>
@ -195,9 +196,12 @@ limitations under the License.
<TextBlock Text=")"/>
</StackPanel>
<TextBlock Text="• Must be valid label syntax; may not be a local label" Margin="0,4,0,0"/>
<TextBlock Text="• Must not be a duplicate of an existing label"/>
<TextBlock Text="• Will not appear if parent region is non-addressable"/>
<TextBlock Name="validSyntaxLabel" Margin="0,4,0,0"
Text="• Must be valid label syntax; may not be a local label"/>
<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>
</GroupBox>
</StackPanel>

View File

@ -20,6 +20,7 @@ using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using Asm65;
using CommonUtil;
@ -198,12 +199,26 @@ namespace SourceGen.WpfGui {
}
private bool mCanDeleteRegion;
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string 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>
/// Result for option #1.
/// </summary>
@ -277,6 +292,8 @@ namespace SourceGen.WpfGui {
// Editing an existing region.
CanDeleteRegion = true;
ShowExistingRegion = true;
mOrigPreLabel = curRegion.PreLabel;
mParentNonAddr = (curRegion.PreLabelAddress == Address.NON_ADDR);
if (curRegion.Address == Address.NON_ADDR) {
AddressText = Address.NON_ADDR_STR;
@ -354,8 +371,13 @@ namespace SourceGen.WpfGui {
// a floating region. Default changes for single-item selections.
CanDeleteRegion = 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;
UseRelativeAddressing = false;
@ -386,6 +408,8 @@ namespace SourceGen.WpfGui {
mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion1.ActualLength));
mPreLabelAddress = newRegion1.PreLabelAddress;
mParentNonAddr = (newRegion1.PreLabelAddress == Address.NON_ADDR);
} else {
option1Summ = string.Empty;
if (ares1 == AddressMap.AddResult.StraddleExisting) {
@ -406,6 +430,8 @@ namespace SourceGen.WpfGui {
mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion2.ActualLength));
mPreLabelAddress = newRegion2.PreLabelAddress;
mParentNonAddr = (newRegion2.PreLabelAddress == Address.NON_ADDR);
} else {
option2Summ = string.Empty;
option2Msg = (string)FindResource("str_CreateFloatingFail");
@ -510,8 +536,53 @@ namespace SourceGen.WpfGui {
/// for TextBox is LostFocus.
/// </remarks>
private void UpdateControls() {
IsValid = EnableAttributeControls && ParseAddress(out int unused);
// TODO(org): check pre-label syntax
bool addrOkay = ParseAddress(out int unused);
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) {

View File

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

View File

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