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++;
}