diff --git a/Asm65/Number.cs b/Asm65/Number.cs index ae97999..50261c3 100644 --- a/Asm65/Number.cs +++ b/Asm65/Number.cs @@ -19,7 +19,8 @@ using System.Diagnostics; namespace Asm65 { public class Number { /// - /// Parses an integer in a variety of formats (hex, decimal, binary). + /// Parses an integer in a variety of formats (hex, decimal, binary). We allow + /// hex to be identified with a leading '$' as well as "0x". /// /// Trim whitespace before calling here. /// diff --git a/SourceGen/DefSymbol.cs b/SourceGen/DefSymbol.cs index 8bbddd2..c172bfb 100644 --- a/SourceGen/DefSymbol.cs +++ b/SourceGen/DefSymbol.cs @@ -18,25 +18,39 @@ using System.Diagnostics; namespace SourceGen { /// - /// Subclass of Symbol used for symbols defined in the platform or project. + /// Subclass of Symbol used for symbols defined in a platform symbol file, in the project + /// symbol table, or in a local variable table. /// /// Instances are immutable, except for the Xrefs field. /// /// - /// The Xrefs field isn't really part of the object. It's just convenient to reference + /// The Xrefs field isn't really part of the object. It's just convenient to access /// them from here. /// public class DefSymbol : Symbol { - // width to use when width doesn't matter; use 1 to try to get a prefab object - public const int NO_WIDTH = 1; + // Absolute min/max width. Zero-page variables are more limited, because they're not + // allowed to wrap around the end of the page. public const int MIN_WIDTH = 1; - public const int MAX_WIDTH = 8; + public const int MAX_WIDTH = 65536; + + // Value to pass to the FormatDescriptor when no width is given. + private const int DEFAULT_WIDTH = 1; /// /// Data format descriptor. /// public FormatDescriptor DataDescriptor { get; private set; } + /// + /// True if a width was specified for this symbol. + /// + /// + /// All symbols have a positive width, stored in the FormatDescriptor Length property. + /// We may not want to display widths that haven't been explicitly set, however, so we + /// keep track here. + /// + public bool HasWidth { get; private set; } + /// /// User-supplied comment. /// @@ -65,6 +79,8 @@ namespace SourceGen { // NOTE: might be nice to identify the symbol's origin, e.g. which platform // symbol file it was defined in. This could then be stored in a // DisplayList line, for benefit of the Info panel. + // NOTE: if this also gets us the load order, we can properly select symbols + // by address when multiple platform symbols have the same value /// @@ -79,7 +95,7 @@ namespace SourceGen { } /// - /// Constructor. + /// Constructor. Limited form, used in a couple of places. /// /// Symbol's label. /// Symbol's value. @@ -87,14 +103,13 @@ namespace SourceGen { /// Symbol type. /// Format descriptor sub-type, so we know how the /// user wants the value to be displayed. - /// End-of-line comment. - /// Symbol tag, used for grouping platform symbols. public DefSymbol(string label, int value, Source source, Type type, - FormatDescriptor.SubType formatSubType, string comment, string tag) - : this(label, value, source, type, formatSubType, comment, tag, NO_WIDTH) { } + FormatDescriptor.SubType formatSubType) + : this(label, value, source, type, formatSubType, + string.Empty, string.Empty, -1, false) { } /// - /// Constructor. Used for local variables, which have a meaningful width. + /// Constructor. General form. /// /// Symbol's label. /// Symbol's value. @@ -105,31 +120,52 @@ namespace SourceGen { /// End-of-line comment. /// Symbol tag, used for grouping platform symbols. /// Variable width. + /// True if width was explicitly specified. If this is + /// false, the value of the "width" argument is ignored. public DefSymbol(string label, int value, Source source, Type type, - FormatDescriptor.SubType formatSubType, string comment, string tag, int width) + FormatDescriptor.SubType formatSubType, string comment, string tag, int width, + bool widthSpecified) : this(label, value, source, type) { Debug.Assert(comment != null); Debug.Assert(tag != null); + if (widthSpecified && type == Type.Constant && source != Source.Variable) { + // non-variable constants don't have a width; override arg + Debug.WriteLine("Overriding constant DefSymbol width"); + widthSpecified = false; + } + HasWidth = widthSpecified; + if (!widthSpecified) { + width = DEFAULT_WIDTH; + } + Debug.Assert(width >= MIN_WIDTH && width <= MAX_WIDTH); + DataDescriptor = FormatDescriptor.Create(width, FormatDescriptor.Type.NumericLE, formatSubType); - Comment = comment; Tag = tag; } /// - /// Constructs a DefSymbol from a Symbol and a format descriptor. This is used - /// for project symbols. + /// Constructor. Used for deserialization, when we have a FormatDescriptor and a Symbol. /// /// Base symbol. /// Format descriptor. + /// Set if a width was explicitly specified. /// End-of-line comment. - public DefSymbol(Symbol sym, FormatDescriptor dfd, string comment) + public DefSymbol(Symbol sym, FormatDescriptor dfd, bool widthSpecified, string comment) : this(sym.Label, sym.Value, sym.SymbolSource, sym.SymbolType) { Debug.Assert(comment != null); + if (widthSpecified && sym.SymbolType == Type.Constant && + sym.SymbolSource != Source.Variable) { + // non-variable constants don't have a width; override arg + Debug.WriteLine("Overriding constant DefSymbol width"); + widthSpecified = false; + } + DataDescriptor = dfd; + HasWidth = widthSpecified; Comment = comment; Tag = string.Empty; } @@ -143,7 +179,7 @@ namespace SourceGen { public DefSymbol(DefSymbol defSym, string label) : this(label, defSym.Value, defSym.SymbolSource, defSym.SymbolType, defSym.DataDescriptor.FormatSubType, defSym.Comment, defSym.Tag, - defSym.DataDescriptor.Length) { } + defSym.DataDescriptor.Length, defSym.HasWidth) { } /// /// Determines whether a symbol overlaps with a region. Useful for variables. diff --git a/SourceGen/FormatDescriptor.cs b/SourceGen/FormatDescriptor.cs index 2dbc227..7b0cdb2 100644 --- a/SourceGen/FormatDescriptor.cs +++ b/SourceGen/FormatDescriptor.cs @@ -162,7 +162,7 @@ namespace SourceGen { /// /// Constructor for base type data item. /// - /// Length, in bytes. + /// Length, in bytes. /// Format type. /// Format sub-type. private FormatDescriptor(int length, Type fmt, SubType subFmt) { diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 95817cb..bb4c0d9 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -840,6 +840,7 @@ namespace SourceGen { string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, PseudoOp.FormatNumericOpFlags.None); + valueStr = PseudoOp.AnnotateEquDirective(formatter, valueStr, defSym); string comment = formatter.FormatEolComment(defSym.Comment); FormattedParts parts = FormattedParts.CreateEquDirective(defSym.Label, formatter.FormatPseudoOp(opNames.EquDirective), @@ -1344,6 +1345,7 @@ namespace SourceGen { string addrStr = PseudoOp.FormatNumericOperand(mFormatter, mProject.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, PseudoOp.FormatNumericOpFlags.None); + addrStr = PseudoOp.AnnotateEquDirective(mFormatter, addrStr, defSym); string comment = mFormatter.FormatEolComment(defSym.Comment); return FormattedParts.CreateEquDirective( mFormatter.FormatVariableLabel(defSym.Label), diff --git a/SourceGen/PlatformSymbols.cs b/SourceGen/PlatformSymbols.cs index 109e1af..f6a2469 100644 --- a/SourceGen/PlatformSymbols.cs +++ b/SourceGen/PlatformSymbols.cs @@ -34,23 +34,23 @@ namespace SourceGen { /// Regex pattern for name/value pairs in symbol file. /// /// Alphanumeric ASCII + underscore for label, which must start at beginning of line. - /// Value is somewhat arbitrary, but ends if we see a comment delimiter (semicolon). - /// Spaces are allowed between tokens. + /// Value is somewhat arbitrary, but ends if we see a comment delimiter (semicolon) or + /// whitespace. Width is decimal or hex. Spaces are allowed between tokens. /// /// Group 1 is the name, group 2 is '=' or '@', group 3 is the value, group 4 is - /// the comment (optional). + /// the symbol width (optional), group 5 is the comment (optional). /// + /// + /// If you want to make sense of this, I highly recommend https://regex101.com/ . + /// private const string NAME_VALUE_PATTERN = - @"^([A-Za-z0-9_]+)\s*([@=])\s*([^\ ;]+)\s*(;.*)?$"; + @"^([A-Za-z0-9_]+)\s*([@=])\s*([^\ ;]+)\s*([0-9\$]+)?\s*(;.*)?$"; private static Regex sNameValueRegex = new Regex(NAME_VALUE_PATTERN); private const string TAG_CMD = "*TAG"; /// /// List of symbols. We keep them sorted by label because labels must be unique. - /// - /// Idea: we could retain the end-of-line comments, and add them as comments in the - /// EQU section of the disassembly. /// private SortedList mSymbols = new SortedList(Asm65.Label.LABEL_COMPARER); @@ -139,11 +139,27 @@ namespace SourceGen { (1 << 24) - 1, out value); badParseMsg = CommonUtil.Properties.Resources.ERR_INVALID_ADDRESS; } + + int width = -1; + string widthStr = matches[0].Groups[4].Value; + if (parseOk && !string.IsNullOrEmpty(widthStr)) { + parseOk = Asm65.Number.TryParseInt(widthStr, out width, + out int ignoredBase); + if (parseOk) { + if (width < DefSymbol.MIN_WIDTH || width > DefSymbol.MAX_WIDTH) { + parseOk = false; + } + } else { + badParseMsg = + CommonUtil.Properties.Resources.ERR_INVALID_NUMERIC_CONSTANT; + } + } + if (!parseOk) { report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning, badParseMsg); } else { - string comment = matches[0].Groups[4].Value; + string comment = matches[0].Groups[5].Value; if (comment.Length > 0) { // remove ';' comment = comment.Substring(1); @@ -152,7 +168,7 @@ namespace SourceGen { FormatDescriptor.GetSubTypeForBase(numBase); DefSymbol symDef = new DefSymbol(label, value, Symbol.Source.Platform, isConst ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, - subType, comment, tag); + subType, comment, tag, width, width > 0); if (mSymbols.ContainsKey(label)) { // This is very easy to do -- just define the same symbol twice // in the same file. We don't really need to do anything about diff --git a/SourceGen/ProblemList.cs b/SourceGen/ProblemList.cs index 81b9404..f29f9f7 100644 --- a/SourceGen/ProblemList.cs +++ b/SourceGen/ProblemList.cs @@ -119,7 +119,7 @@ namespace SourceGen { } public void DebugDump() { - Debug.WriteLine("Problem list:"); + Debug.WriteLine("Problem list (" + mList.Count + " entries):"); foreach (ProblemEntry entry in mList) { Debug.WriteLine(entry); } diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs index 6230f2c..8f2590c 100644 --- a/SourceGen/ProjectFile.cs +++ b/SourceGen/ProjectFile.cs @@ -311,10 +311,13 @@ namespace SourceGen { public class SerDefSymbol : SerSymbol { public SerFormatDescriptor DataDescriptor { get; set; } public string Comment { get; set; } + public bool HasWidth { get; set; } + // Tag not relevant, Xrefs not recorded public SerDefSymbol() { } public SerDefSymbol(DefSymbol defSym) : base(defSym) { DataDescriptor = new SerFormatDescriptor(defSym.DataDescriptor); + HasWidth = defSym.HasWidth; Comment = defSym.Comment; } } @@ -736,7 +739,7 @@ namespace SourceGen { return false; } - outDefSym = new DefSymbol(sym, dfd, serDefSym.Comment); + outDefSym = new DefSymbol(sym, dfd, serDefSym.HasWidth, serDefSym.Comment); return true; } @@ -837,13 +840,20 @@ namespace SourceGen { outLvt = new LocalVariableTable(); outLvt.ClearPrevious = serTable.ClearPrevious; foreach (SerDefSymbol serDef in serTable.Variables) { + // Force the "has width" field to true for local variables, because it's + // non-optional there. This is really only needed for loading projects + // created in v1.3, which didn't have the "has width" property. + serDef.HasWidth = true; if (!CreateDefSymbol(serDef, contentVersion, report, out DefSymbol defSym)) { return false; } if (!defSym.IsVariable) { - // not expected to happen + // not expected to happen; skip it Debug.WriteLine("Found local variable with bad source: " + defSym.SymbolSource); + string str = string.Format(Res.Strings.ERR_BAD_LOCAL_VARIABLE_FMT, + defSym); + report.Add(FileLoadItem.Type.Warning, str); continue; } outLvt.AddOrReplace(defSym); diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs index 2edd3f7..82d1c83 100644 --- a/SourceGen/PseudoOp.cs +++ b/SourceGen/PseudoOp.cs @@ -370,6 +370,43 @@ namespace SourceGen { return po; } + /// + /// Adds an additional annotation to an EQU directive, indicating whether the symbol + /// is a constant or an address, and (if address) how many bytes it spans. + /// + /// Formatter object. + /// Formatted operand string. + /// Project/platform/variable symbol. + /// + public static string AnnotateEquDirective(Formatter formatter, string operand, + DefSymbol defSym) { + string typeStr; + if (defSym.SymbolType == Symbol.Type.Constant) { + if (defSym.SymbolSource == Symbol.Source.Variable) { + typeStr = Res.Strings.EQU_STACK_RELATIVE; + } else { + typeStr = Res.Strings.EQU_CONSTANT; + } + } else { + typeStr = Res.Strings.EQU_ADDRESS; + } + + string msgStr = null; + if (defSym.HasWidth) { + msgStr = typeStr + "/" + defSym.DataDescriptor.Length; + } else if (defSym.SymbolType == Symbol.Type.Constant) { + // not entirely convinced we want this, but there's currently no other way + // to tell the difference between an address and a constant from the code list + msgStr = typeStr; + } + + if (msgStr == null) { + return operand; + } else { + return operand + " {" + msgStr + "}"; + } + } + /// /// Converts a collection of bytes that represent a string into an array of formatted /// string operands. diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml index 5e6198c..ce1ac4a 100644 --- a/SourceGen/Res/Strings.xaml +++ b/SourceGen/Res/Strings.xaml @@ -23,6 +23,7 @@ limitations under the License. Addr Const + StkRl [latest version] output DOES NOT match data file output matches data file @@ -39,10 +40,14 @@ limitations under the License. “#” pet:“#” scr:“#” + addr + const + stkrl Bad format descriptor at +{0:x6}. Bad format descriptor type Bad file length Invalid file identifier + Bad local variable {0} Invalid local variable table at +{0:x6} Bad range Unknown Source or Type in symbol diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs index 8213616..9fd40c4 100644 --- a/SourceGen/Res/Strings.xaml.cs +++ b/SourceGen/Res/Strings.xaml.cs @@ -27,6 +27,8 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_AbbrevAddress"); public static string ABBREV_CONSTANT = (string)Application.Current.FindResource("str_AbbrevConstant"); + public static string ABBREV_STACK_RELATIVE = + (string)Application.Current.FindResource("str_AbbrevStackRelative"); public static string ASM_LATEST_VERSION = (string)Application.Current.FindResource("str_AsmLatestVersion"); public static string ASM_MATCH_FAILURE = @@ -59,6 +61,12 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_ClipformatAssemblerSource"); public static string CLIPFORMAT_DISASSEMBLY = (string)Application.Current.FindResource("str_ClipformatDisassembly"); + public static string EQU_ADDRESS = + (string)Application.Current.FindResource("str_EquAddress"); + public static string EQU_CONSTANT = + (string)Application.Current.FindResource("str_EquConstant"); + public static string EQU_STACK_RELATIVE = + (string)Application.Current.FindResource("str_EquStackRelative"); public static string ERR_BAD_FD_FMT = (string)Application.Current.FindResource("str_ErrBadFdFmt"); public static string ERR_BAD_FD_FORMAT = @@ -67,6 +75,8 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_ErrBadFileLength"); public static string ERR_BAD_IDENT = (string)Application.Current.FindResource("str_ErrBadIdent"); + public static string ERR_BAD_LOCAL_VARIABLE_FMT = + (string)Application.Current.FindResource("str_ErrBadLocalVariableFmt"); public static string ERR_BAD_LV_TABLE_FMT = (string)Application.Current.FindResource("str_ErrBadLvTableFmt"); public static string ERR_BAD_RANGE = diff --git a/SourceGen/RuntimeData/Apple/Applesoft.sym65 b/SourceGen/RuntimeData/Apple/Applesoft.sym65 index 9d48cf9..0559110 100644 --- a/SourceGen/RuntimeData/Apple/Applesoft.sym65 +++ b/SourceGen/RuntimeData/Apple/Applesoft.sym65 @@ -3,10 +3,11 @@ ; ; Sources: ; What's Where in the Apple, by William F. Luebbert +; S-C DocuMentor: Applesoft, by Bob Sander-Cederlof *SYNOPSIS Applesoft BASIC addresses and constants -BAS_USRVEC @ $0A ;USR() command vector +BAS_USRVEC @ $0A 3 ;USR() command vector BAS_CHARAC @ $0D ;used by string utility BAS_ENDCHR @ $0E ;used by string utility BAS_VALTYP @ $11 ;flag for last FAC operation ($00=num, $FF=str) @@ -16,41 +17,41 @@ BAS_COUNTH @ $1D ;hi-res high-order byte of step for line BAS_HBASL @ $26 ;base address for hi-res drawing (lo part) BAS_HBASH @ $27 ;base address for hi-res drawing (hi part) BAS_HMASK @ $30 ;hi-res graphics on-the-fly bit mask -BAS_LINNUM @ $50 ;line number (2b) -BAS_TEMPPT @ $52 ;temporary point (2b) -BAS_INDEX @ $5E ;temp (stack) pointer for moving strings (2b) -BAS_TEXTTAB @ $67 ;pointer to start of Applesoft program (2b) -BAS_VARTAB @ $69 ;pointer to start of Applesoft variables (2b) -BAS_ARYTAB @ $6B ;pointer to start of Applesoft array space (2b) -BAS_STREND @ $6D ;pointer to end of numeric storage (2b) -BAS_FRETOP @ $6F ;pointer to end of string storage (2b) -BAS_MEMSIZE @ $73 ;HIMEM (2b) -BAS_CURLIN @ $75 ;current line number (2b) -BAS_OLDLIN @ $77 ;last line executed (2b) +BAS_LINNUM @ $50 2 ;line number (2b) +BAS_TEMPPT @ $52 2 ;temporary point (2b) +BAS_INDEX @ $5E 2 ;temp (stack) pointer for moving strings (2b) +BAS_TEXTTAB @ $67 2 ;pointer to start of Applesoft program (2b) +BAS_VARTAB @ $69 2 ;pointer to start of Applesoft variables (2b) +BAS_ARYTAB @ $6B 2 ;pointer to start of Applesoft array space (2b) +BAS_STREND @ $6D 2 ;pointer to end of numeric storage (2b) +BAS_FRETOP @ $6F 2 ;pointer to end of string storage (2b) +BAS_MEMSIZE @ $73 2 ;HIMEM (2b) +BAS_CURLIN @ $75 2 ;current line number (2b) +BAS_OLDLIN @ $77 2 ;last line executed (2b) BAS_OLDTEXT @ $79 ;old text pointer -BAS_DATLIN @ $7B ;current line # from which data is being read (2b) -BAS_DATPTR @ $7D ;points to mem from which data is being read (2b) -BAS_VARNAM @ $81 ;holds last-used variable's name (2b) -BAS_VARPNT @ $83 ;pointer to last-used variable's value (2b) -BAS_FORPNT @ $85 ;general pointer (2b) -BAS_JMPADRS @ $90 ;jump address; $90 is set to $4C (3b) +BAS_DATLIN @ $7B 2 ;current line # from which data is being read (2b) +BAS_DATPTR @ $7D 2 ;points to mem from which data is being read (2b) +BAS_VARNAM @ $81 2 ;holds last-used variable's name (2b) +BAS_VARPNT @ $83 2 ;pointer to last-used variable's value (2b) +BAS_FORPNT @ $85 2 ;general pointer (2b) +BAS_JMPADRS @ $90 3 ;jump address; $90 is set to $4C (3b) BAS_TEMP1 @ $93 ;fp math register -BAS_HIGHDS @ $94 ;block copy pointer (2b) -BAS_HIGHTR @ $96 ;block copy pointer (2b) +BAS_HIGHDS @ $94 2 ;block copy pointer (2b) +BAS_HIGHTR @ $96 2 ;block copy pointer (2b) BAS_TEMP2 @ $98 ;fp math register -BAS_LOWTR @ $9B ;general pointer (2b) -BAS_FAC @ $9D ;floating point accumulator (6b) +BAS_LOWTR @ $9B 2 ;general pointer (2b) +BAS_FAC @ $9D 6 ;floating point accumulator (6b) BAS_FACMO = $A0 ;middle-order byte of mantissa of FAC BAS_FACLO = $A1 ;low-order byte of mantissa of FAC BAS_FACSIGN @ $A2 ;single byte sign of FAC -BAS_ARG @ $A5 ;secondary floating point accumulator (6b) -BAS_STRNG1 @ $AB ;pointer to a string (2b) -BAS_STRNG2 @ $AD ;pointer to a string (2b) -BAS_PRGEND @ $AF ;pointer to end of program (2b) +BAS_ARG @ $A5 6 ;secondary floating point accumulator (6b) +BAS_STRNG1 @ $AB 2 ;pointer to a string (2b) +BAS_STRNG2 @ $AD 2 ;pointer to a string (2b) +BAS_PRGEND @ $AF 2 ;pointer to end of program (2b) BAS_CHRGET @ $B1 ;get next character or Applesoft token BAS_CHRGOT @ $B7 ;get next, but don't advance TXTPTR -BAS_TXTPTR @ $B8 ;points at next char or token -BAS_RND @ $B8 ;floating point random number (5b) +BAS_TXTPTR @ $B8 2 ;points at next char or token (2b) +BAS_RND @ $C9 5 ;floating point random number (5b) BAS_AUTORUN @ $D6 ;set to $80 to auto-run BAS_ERRFLG @ $D8 ;$80 if onerr active BAS_HPAG @ $E6 ;hi-res page to draw on ($20 or $40) @@ -59,7 +60,7 @@ BAS_FIRST @ $F0 ;used for lo-res plot coordinates BAS_ORMASK @ $F3 ;mask for output control BAS_REMSTK @ $F8 ;stack pointer saved before each statement -BAS_AMPERV @ $03F5 ;JMP to function that handles Applesoft '&' cmds +BAS_AMPERV @ $03F5 3 ;JMP to function that handles Applesoft '&' cmds ; ; Useful Applesoft routines. diff --git a/SourceGen/RuntimeData/Apple/F8-ROM.sym65 b/SourceGen/RuntimeData/Apple/F8-ROM.sym65 index 21c6b4e..cd31c37 100644 --- a/SourceGen/RuntimeData/Apple/F8-ROM.sym65 +++ b/SourceGen/RuntimeData/Apple/F8-ROM.sym65 @@ -35,12 +35,12 @@ MON_RNDH @ $4F ;high byte of KEYIN "random" value MON_SPDBYT @ $F1 ;text output speed limiter -MON_BRKV @ $03F0 ;address of BRK handler -MON_SOFTEVEC @ $03F2 ;address of RESET handler +MON_BRKV @ $03F0 2 ;address of BRK handler +MON_SOFTEVEC @ $03F2 2 ;address of RESET handler MON_PWREDUP @ $03F4 ;power-up RESET checksum -MON_USRADDR @ $03F8 ;jump to function that handles monitor Ctrl-Y -MON_NMIVEC @ $03FB ;jump to function that handles NMI -MON_IRQADDR @ $03FE ;address of IRQ handler +MON_USRADDR @ $03F8 3 ;jump to function that handles monitor Ctrl-Y +MON_NMIVEC @ $03FB 3 ;jump to function that handles NMI +MON_IRQADDR @ $03FE 2 ;address of IRQ handler MON_PLOT @ $F800 ;lo-res plot at X=Y-reg, Y=Acc MON_PLOT1 @ $F80E ;lo-res plot at X=Y-reg, Y per GBASL/H @@ -101,7 +101,7 @@ MON_CLREOP @ $FC42 ;clear screen from cursor to end of page MON_HOME @ $FC58 ;clear screen and reset text output to top-left MON_CR @ $FC62 ;perform a carriage return MON_LF @ $FC66 ;perform a line feed -MON_SCROLL @ $FC70 ;scroll up 1 line +MON_SCROLL @ $FC70 ;scroll up one line MON_CLREOL @ $FC9C ;clear to end of line MON_CLREOLZ @ $FC9E MON_WAIT @ $FCA8 ;delay for (26 + 27*Acc + 5*(Acc*Acc))/2 cycles diff --git a/SourceGen/Symbol.cs b/SourceGen/Symbol.cs index a4315e2..26737e5 100644 --- a/SourceGen/Symbol.cs +++ b/SourceGen/Symbol.cs @@ -104,9 +104,9 @@ namespace SourceGen { /// Constructs immutable object. /// /// Label string. Syntax assumed valid. - /// User-defined or auto-generated? + /// Symbol value. + /// User-defined, auto-generated, ? /// Type of symbol this is. - /// user-defined. public Symbol(string label, int value, Source source, Type type) { Debug.Assert(!string.IsNullOrEmpty(label)); Label = label; diff --git a/SourceGen/WpfGui/EditDefSymbol.xaml b/SourceGen/WpfGui/EditDefSymbol.xaml index 0d5b061..4acf0b3 100644 --- a/SourceGen/WpfGui/EditDefSymbol.xaml +++ b/SourceGen/WpfGui/EditDefSymbol.xaml @@ -19,13 +19,21 @@ limitations under the License. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:SourceGen.WpfGui" mc:Ignorable="d" Title="Edit Symbol" - SizeToContent="Height" Width="320" ResizeMode="NoResize" + SizeToContent="Height" Width="340" ResizeMode="NoResize" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" Loaded="Window_Loaded" ContentRendered="Window_ContentRendered"> + + + • Decimal or hex value, 1-{0} + Constant + Stack-Relative Offset + + @@ -41,42 +49,44 @@ limitations under the License. - + - - + + - + - - - + + + - - + + - + + - + - + - + diff --git a/SourceGen/WpfGui/EditDefSymbol.xaml.cs b/SourceGen/WpfGui/EditDefSymbol.xaml.cs index eee86f4..3ae7a4e 100644 --- a/SourceGen/WpfGui/EditDefSymbol.xaml.cs +++ b/SourceGen/WpfGui/EditDefSymbol.xaml.cs @@ -61,6 +61,12 @@ namespace SourceGen.WpfGui { } private string mWidth; + public string WidthLimitLabel { + get { return mWidthLimitLabel; } + set { mWidthLimitLabel = value; OnPropertyChanged(); } + } + private string mWidthLimitLabel; + public string Comment { get { return mComment; } set { mComment = value; OnPropertyChanged(); } @@ -79,6 +85,12 @@ namespace SourceGen.WpfGui { } private bool mIsConstant; + public string ConstantLabel { + get { return mConstantLabel; } + set { mConstantLabel = value; OnPropertyChanged(); } + } + private string mConstantLabel; + public bool ReadOnlyValueAndType { get { return mReadOnlyValueAndType; } set { mReadOnlyValueAndType = value; OnPropertyChanged(); } @@ -113,6 +125,11 @@ namespace SourceGen.WpfGui { /// private bool mIsVariable; + /// + /// Set to true if the width value is optional. + /// + private bool mIsWidthOptional; + // Saved off at dialog load time. private Brush mDefaultLabelColor; @@ -135,6 +152,9 @@ namespace SourceGen.WpfGui { /// Constructor, for editing a local variable, or editing a project symbol with /// the value field locked. /// + /// + /// TODO(someday): disable the "constant" radio button unless CPU=65816. + /// public EditDefSymbol(Window owner, Formatter formatter, SortedList defList, DefSymbol defSym, SymbolTable symbolTable, bool isVariable, bool lockValueAndType) { @@ -150,16 +170,24 @@ namespace SourceGen.WpfGui { mReadOnlyValueAndType = lockValueAndType; Label = Value = VarWidth = Comment = string.Empty; + + // hide the inappropriate stuff + int maxWidth; if (isVariable) { - widthEntry1.Visibility = widthEntry2.Visibility = labelUniqueLabel.Visibility = - Visibility.Visible; - projectLabelUniqueLabel.Visibility = Visibility.Collapsed; - } else { - widthEntry1.Visibility = widthEntry2.Visibility = labelUniqueLabel.Visibility = + projectLabelUniqueLabel.Visibility = widthOptionalLabel.Visibility = Visibility.Collapsed; - labelUniqueLabel.Visibility = Visibility.Collapsed; - valueRangeLabel.Visibility = valueUniqueLabel.Visibility = Visibility.Collapsed; + ConstantLabel = (string)FindResource("str_VariableConstant"); + maxWidth = 256; + } else { + labelUniqueLabel.Visibility = varValueRangeLabel.Visibility = + varValueUniqueLabel.Visibility = Visibility.Collapsed; + ConstantLabel = (string)FindResource("str_ProjectConstant"); + maxWidth = 65536; } + mIsWidthOptional = !isVariable; + + string fmt = (string)FindResource("str_WidthLimitFmt"); + WidthLimitLabel = string.Format(fmt, maxWidth); } private void Window_Loaded(object sender, RoutedEventArgs e) { @@ -169,7 +197,9 @@ namespace SourceGen.WpfGui { Label = mOldSym.Label; Value = mNumFormatter.FormatValueInBase(mOldSym.Value, mOldSym.DataDescriptor.NumBase); - VarWidth = mOldSym.DataDescriptor.Length.ToString(); + if (mOldSym.HasWidth) { + VarWidth = mOldSym.DataDescriptor.Length.ToString(); + } Comment = mOldSym.Comment; if (mOldSym.SymbolType == Symbol.Type.Constant) { @@ -190,6 +220,9 @@ namespace SourceGen.WpfGui { labelTextBox.Focus(); } + /// + /// Validates input and updates controls appropriately. + /// private void UpdateControls() { if (!IsLoaded) { return; @@ -227,12 +260,19 @@ namespace SourceGen.WpfGui { //} bool widthValid = true; - int thisWidth = 0; - if (widthEntry1.Visibility == Visibility.Visible) { - if (!int.TryParse(VarWidth, out thisWidth) || - thisWidth < DefSymbol.MIN_WIDTH || thisWidth > DefSymbol.MAX_WIDTH) { - widthValid = false; - } + int thisWidth = -1; + if (IsConstant && !mIsVariable) { + // width field is ignored + } else if (string.IsNullOrEmpty(VarWidth)) { + // blank field is okay if the width is optional + widthValid = mIsWidthOptional; + } else if (!Asm65.Number.TryParseInt(VarWidth, out thisWidth, out int unusedBase) || + thisWidth < DefSymbol.MIN_WIDTH || thisWidth > DefSymbol.MAX_WIDTH || + (mIsVariable && thisWidth > 256)) { + // All widths must be between 1 and 65536. For a variable, the full thing must + // fit on zero page without wrapping. We test for 256 here so that we highlight + // the "bad width" label, rather than the "it doesn't fit on the page" label. + widthValid = false; } bool valueRangeValid = true; @@ -262,8 +302,8 @@ namespace SourceGen.WpfGui { labelUniqueLabel.Foreground = projectLabelUniqueLabel.Foreground = labelUnique ? mDefaultLabelColor : Brushes.Red; valueNotesLabel.Foreground = valueValid ? mDefaultLabelColor : Brushes.Red; - valueRangeLabel.Foreground = valueRangeValid ? mDefaultLabelColor : Brushes.Red; - valueUniqueLabel.Foreground = valueUniqueValid ? mDefaultLabelColor : Brushes.Red; + varValueRangeLabel.Foreground = valueRangeValid ? mDefaultLabelColor : Brushes.Red; + varValueUniqueLabel.Foreground = valueUniqueValid ? mDefaultLabelColor : Brushes.Red; widthNotesLabel.Foreground = widthValid ? mDefaultLabelColor : Brushes.Red; IsValid = labelValid && labelUnique && valueValid && valueRangeValid && @@ -284,15 +324,18 @@ namespace SourceGen.WpfGui { private void OkButton_Click(object sender, RoutedEventArgs e) { ParseValue(out int value, out int numBase); FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase); - int width = DefSymbol.NO_WIDTH; - if (!string.IsNullOrEmpty(VarWidth)) { - width = int.Parse(VarWidth); + int width = -1; + if (IsConstant && !mIsVariable) { + // width field is ignored, don't bother parsing + } else if (!string.IsNullOrEmpty(VarWidth)) { + bool ok = Asm65.Number.TryParseInt(VarWidth, out width, out int unusedNumBase); + Debug.Assert(ok); } NewSym = new DefSymbol(Label, value, mIsVariable ? Symbol.Source.Variable : Symbol.Source.Project, IsConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, - subType, Comment, string.Empty, width); + subType, Comment, string.Empty, width, width > 0); DialogResult = true; } diff --git a/SourceGen/WpfGui/EditInstructionOperand.xaml.cs b/SourceGen/WpfGui/EditInstructionOperand.xaml.cs index 44bcd4a..f92f39c 100644 --- a/SourceGen/WpfGui/EditInstructionOperand.xaml.cs +++ b/SourceGen/WpfGui/EditInstructionOperand.xaml.cs @@ -961,8 +961,7 @@ namespace SourceGen.WpfGui { symName = SymbolLabel; // may not be valid, but it doesn't have to be } origSym = new DefSymbol(symName, mOperandValue, Symbol.Source.Project, - Symbol.Type.ExternalAddr, FormatDescriptor.SubType.None, - string.Empty, string.Empty); + Symbol.Type.ExternalAddr, FormatDescriptor.SubType.None); } EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, @@ -1147,7 +1146,7 @@ namespace SourceGen.WpfGui { // dialog will handle it. initialVar = new DefSymbol("VAR", mOperandValue, Symbol.Source.Variable, symType, FormatDescriptor.SubType.None, - string.Empty, string.Empty, 1); + string.Empty, string.Empty, 1, true); } EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, diff --git a/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs b/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs index 0ab29fd..1e37638 100644 --- a/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs +++ b/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs @@ -161,7 +161,7 @@ namespace SourceGen.WpfGui { DefSymbol defSym = mWorkTable[i]; string typeStr; if (defSym.SymbolType == Symbol.Type.Constant) { - typeStr = Res.Strings.ABBREV_CONSTANT; + typeStr = Res.Strings.ABBREV_STACK_RELATIVE; } else { typeStr = Res.Strings.ABBREV_ADDRESS; } diff --git a/SourceGen/WpfGui/EditProjectProperties.xaml.cs b/SourceGen/WpfGui/EditProjectProperties.xaml.cs index cebc6d8..81ec225 100644 --- a/SourceGen/WpfGui/EditProjectProperties.xaml.cs +++ b/SourceGen/WpfGui/EditProjectProperties.xaml.cs @@ -538,14 +538,13 @@ namespace SourceGen.WpfGui { } // Import all user labels that were marked as "global export". These become - // external-address project symbols. + // external-address project symbols with unspecified width. int foundCount = 0; foreach (KeyValuePair kvp in newProject.UserLabels) { if (kvp.Value.SymbolType == Symbol.Type.GlobalAddrExport) { Symbol sym = kvp.Value; DefSymbol defSym = new DefSymbol(sym.Label, sym.Value, Symbol.Source.Project, - Symbol.Type.ExternalAddr, FormatDescriptor.SubType.None, - string.Empty, string.Empty); + Symbol.Type.ExternalAddr, FormatDescriptor.SubType.None); mWorkProps.ProjectSyms[defSym.Label] = defSym; foundCount++; }