diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs index cfb55c9..8d6a640 100644 --- a/CommonUtil/AddressMap.cs +++ b/CommonUtil/AddressMap.cs @@ -191,8 +191,19 @@ namespace CommonUtil { /// Is the end point floating? /// public bool IsFloating { + get { return Length == FLOATING_LEN; } + } + + /// + /// Does this region have a valid pre-label? + /// + /// + /// 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. + /// + public bool HasValidPreLabel { get { - return Length == FLOATING_LEN; + return !string.IsNullOrEmpty(PreLabel) && PreLabelAddress != NON_ADDR; } } diff --git a/PluginCommon/PlSymbol.cs b/PluginCommon/PlSymbol.cs index d3c96f4..fe28f17 100644 --- a/PluginCommon/PlSymbol.cs +++ b/PluginCommon/PlSymbol.cs @@ -31,6 +31,7 @@ namespace PluginCommon { public enum Source { Unknown = 0, User, + AddrPreLabel, Project, Platform } diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs index f6851e3..a2d1331 100644 --- a/SourceGen/AppSettings.cs +++ b/SourceGen/AppSettings.cs @@ -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"; diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index b8d0248..ad3d97f 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -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++; diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index 239561c..954ada4 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -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 diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 83a7f0c..05d6224 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -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 diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 75fd6aa..3c406d0 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -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 { /// private StringBuilder mLineBuilder = new StringBuilder(100); + /// + /// Label localization helper. + /// + private LabelLocalizer mLocalizer; + /// /// Stream to send the output to. /// @@ -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++; diff --git a/SourceGen/AsmGen/LabelLocalizer.cs b/SourceGen/AsmGen/LabelLocalizer.cs index c6863dc..bb17034 100644 --- a/SourceGen/AsmGen/LabelLocalizer.cs +++ b/SourceGen/AsmGen/LabelLocalizer.cs @@ -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 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 { } } + /// + /// Remaps a global label, handling promotion from local, leading underscores, and + /// clashes with opcode mnemonics. + /// + /// Symbol to rename. + /// List of all global labels, used when uniquifing + /// a "promoted" local. May be updated. + /// List of opcode mnemonics for configured CPU. Will be + /// null if the assembler allows labels to be the same as opcodes. + /// True if leading underscores are not allowed (because + /// they're used to indicate local labels). + private void RemapGlobalSymbol(Symbol sym, Dictionary allGlobalLabels, + Dictionary 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); + } + /// /// 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 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)); + } } /// diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index e621665..cdec73b 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -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 { } } + /// + /// Merges symbols from AddressMap into SymbolTable. Existing entries with matching + /// labels will be replaced. + /// + /// + /// These are external symbols, with a higher precedence than project symbols. + /// + private void MergeAddressPreLabels() { + IEnumerator 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; + } + } + } + /// /// 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 { } /// - /// 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 { /// /// Finds a label by name. SymbolTable must be populated. /// + /// + /// 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. + /// /// Label to find. /// File offset associated with label, or -1 if not found. 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++) { diff --git a/SourceGen/DisplayList.cs b/SourceGen/DisplayList.cs index cec5e43..13c6762 100644 --- a/SourceGen/DisplayList.cs +++ b/SourceGen/DisplayList.cs @@ -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; } diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 37e7b48..3ceb91c 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -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; } diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index 3a1fe61..4e9a8be 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -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 { /// /// Label symbol. 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"); } } diff --git a/SourceGen/Sandbox/ScriptManager.cs b/SourceGen/Sandbox/ScriptManager.cs index c144eac..06a0023 100644 --- a/SourceGen/Sandbox/ScriptManager.cs +++ b/SourceGen/Sandbox/ScriptManager.cs @@ -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: diff --git a/SourceGen/Symbol.cs b/SourceGen/Symbol.cs index 15f778e..a6ef73d 100644 --- a/SourceGen/Symbol.cs +++ b/SourceGen/Symbol.cs @@ -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; diff --git a/SourceGen/WpfGui/EditAddress.xaml b/SourceGen/WpfGui/EditAddress.xaml index 19c95ea..4c5214e 100644 --- a/SourceGen/WpfGui/EditAddress.xaml +++ b/SourceGen/WpfGui/EditAddress.xaml @@ -173,7 +173,8 @@ limitations under the License. - + @@ -195,9 +196,12 @@ limitations under the License. - - - + + + diff --git a/SourceGen/WpfGui/EditAddress.xaml.cs b/SourceGen/WpfGui/EditAddress.xaml.cs index 0b7b780..f43fba3 100644 --- a/SourceGen/WpfGui/EditAddress.xaml.cs +++ b/SourceGen/WpfGui/EditAddress.xaml.cs @@ -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; + + /// + /// Initial value for pre-label. + /// + private string mOrigPreLabel; + + /// + /// True if parent region is non-addressable. + /// + private bool mParentNonAddr; + /// /// Result for option #1. /// @@ -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. /// 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; + } + + /// + /// Validates pre-label. + /// + 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) { diff --git a/SourceGen/WpfGui/MainWindow.xaml b/SourceGen/WpfGui/MainWindow.xaml index ba1a156..a0c77c2 100644 --- a/SourceGen/WpfGui/MainWindow.xaml +++ b/SourceGen/WpfGui/MainWindow.xaml @@ -803,6 +803,8 @@ limitations under the License. IsChecked="{Binding SymFilterProjectSymbols}"/> +