From bd11aea4a49ae69dd610c8a474e015c4d27589a8 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Wed, 16 Oct 2019 17:32:30 -0700 Subject: [PATCH] External symbol I/O direction and address mask, part 3 (of 3) Added regression tests. Improved error messages. Updated documentation. --- SourceGen/PlatformSymbols.cs | 27 +++++++- SourceGen/Res/Strings.xaml | 9 ++- SourceGen/Res/Strings.xaml.cs | 14 +++- SourceGen/RuntimeData/Help/advanced.html | 62 +++++++++++++++--- SourceGen/RuntimeData/Help/editors.html | 25 +++---- SourceGen/RuntimeData/Help/intro.html | 15 +++++ SourceGen/SGTestData/2021-external-symbols | Bin 222 -> 329 bytes .../SGTestData/2021-external-symbols-1.sym65 | 50 ++++++++++++++ .../SGTestData/2021-external-symbols-2.sym65 | 28 ++++++++ .../SGTestData/2021-external-symbols.dis65 | 2 +- .../Expected/2021-external-symbols_64tass.S | 59 +++++++++++++++-- .../Expected/2021-external-symbols_Merlin32.S | 59 +++++++++++++++-- .../Expected/2021-external-symbols_acme.S | 59 +++++++++++++++-- .../Expected/2021-external-symbols_cc65.S | 59 +++++++++++++++-- .../Expected/2021-external-symbols_cc65.cfg | 2 +- .../SGTestData/Source/2021-external-symbols.S | 49 ++++++++++++++ 16 files changed, 473 insertions(+), 46 deletions(-) diff --git a/SourceGen/PlatformSymbols.cs b/SourceGen/PlatformSymbols.cs index ef37b4d..63a593d 100644 --- a/SourceGen/PlatformSymbols.cs +++ b/SourceGen/PlatformSymbols.cs @@ -144,9 +144,9 @@ namespace SourceGen { if (line.StartsWith(TAG_CMD)) { tag = ParseTag(line); } else if (line.StartsWith(MULTI_MASK_CMD)) { - if (!ParseMask(line, out multiMask)) { + if (!ParseMask(line, out multiMask, out string badMaskMsg)) { report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning, - Res.Strings.ERR_INVALID_MASK); + badMaskMsg); } //Debug.WriteLine("Mask is now " + mask.ToString("x6")); } else { @@ -268,12 +268,15 @@ namespace SourceGen { /// Line to parse. /// Parsed mask value, or null if the line was empty. /// True if the mask was parsed successfully. - private bool ParseMask(string line, out DefSymbol.MultiAddressMask multiMask) { + private bool ParseMask(string line, out DefSymbol.MultiAddressMask multiMask, + out string badMaskMsg) { Debug.Assert(line.StartsWith(MULTI_MASK_CMD)); const int MIN = 0; const int MAX = 0x00ffff; + badMaskMsg = Res.Strings.ERR_INVALID_MULTI_MASK; multiMask = null; + string maskStr = line.Substring(MULTI_MASK_CMD.Length).Trim(); if (string.IsNullOrEmpty(maskStr)) { // empty line, disable mask @@ -293,16 +296,34 @@ namespace SourceGen { if (!Asm65.Number.TryParseInt(cmpMaskStr, out cmpMask, out ignoredBase) || cmpMask < MIN || cmpMask > MAX) { Debug.WriteLine("Bad cmpMask: " + cmpMaskStr); + badMaskMsg = Res.Strings.ERR_INVALID_COMPARE_MASK; return false; } if (!Asm65.Number.TryParseInt(cmpValueStr, out cmpValue, out ignoredBase) || cmpValue < MIN || cmpValue > MAX) { Debug.WriteLine("Bad cmpValue: " + cmpValueStr); + badMaskMsg = Res.Strings.ERR_INVALID_COMPARE_VALUE; return false; } if (!Asm65.Number.TryParseInt(addrMaskStr, out addrMask, out ignoredBase) || addrMask < MIN || addrMask > MAX) { Debug.WriteLine("Bad addrMask: " + addrMaskStr); + badMaskMsg = Res.Strings.ERR_INVALID_ADDRESS_MASK; + return false; + } + + // The two masks should not overlap: one represents bits that must be in a + // specific state for a match to exist, the other indicates which bits are used + // to select a specific register. This should be a warning. + if ((cmpMask & ~addrMask) != cmpMask) { + Debug.WriteLine("Warning: cmpMask/addrMask overlap"); + badMaskMsg = Res.Strings.ERR_INVALID_CMP_ADDR_OVERLAP; + return false; + } + // If cmpValue has bits set that aren't in cmpMask, we will never find a match. + if ((cmpValue & ~cmpMask) != 0) { + Debug.WriteLine("cmpValue has unexpected bits set"); + badMaskMsg = Res.Strings.ERR_INVALID_CMP_EXTRA_BITS; return false; } diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml index 6c04dfa..4edefc6 100644 --- a/SourceGen/Res/Strings.xaml +++ b/SourceGen/Res/Strings.xaml @@ -61,9 +61,14 @@ limitations under the License. File not found: {0} Failed reading {0}: {1}. Cannot write to read-only file {0}. + Invalid MULTI_MASK AddressMask + MULTI_MASK CompareMask and AddressMask overlap + MULTI_MASK CompareValue has bits not in CompareMask + Invalid MULTI_MASK CompareMask + Invalid MULTI_MASK CompareValue Could not convert value to integer Key value is out of range - Invalid mask value + Invalid MULTI_MASK line Invalid width value - INVALID DEFINITION Unable to load config file @@ -72,7 +77,7 @@ limitations under the License. Unable to load project file Unable to save project file [File was too large for preview window] - Symbol value is incompatible with multi-mask + Symbol value is incompatible with current multi-mask Symbol files and extension scripts must live in the application runtime directory ({0}) or project directory ({1}). File {2} lives elsewhere. File Not In Runtime Directory All files (*.*)|*.* diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs index c667cdc..6461abd 100644 --- a/SourceGen/Res/Strings.xaml.cs +++ b/SourceGen/Res/Strings.xaml.cs @@ -103,12 +103,22 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_ErrFileReadFailedFmt"); public static string ERR_FILE_READ_ONLY_FMT = (string)Application.Current.FindResource("str_ErrFileReadOnlyFmt"); + public static string ERR_INVALID_ADDRESS_MASK = + (string)Application.Current.FindResource("str_ErrInvalidAddressMask"); + public static string ERR_INVALID_CMP_ADDR_OVERLAP = + (string)Application.Current.FindResource("str_ErrInvalidCmpAddrOverlap"); + public static string ERR_INVALID_CMP_EXTRA_BITS = + (string)Application.Current.FindResource("str_ErrInvalidCmpExtraBits"); + public static string ERR_INVALID_COMPARE_MASK = + (string)Application.Current.FindResource("str_ErrInvalidCompareMask"); + public static string ERR_INVALID_COMPARE_VALUE = + (string)Application.Current.FindResource("str_ErrInvalidCompareValue"); public static string ERR_INVALID_INT_VALUE = (string)Application.Current.FindResource("str_ErrInvalidIntValue"); public static string ERR_INVALID_KEY_VALUE = (string)Application.Current.FindResource("str_ErrInvalidKeyValue"); - public static string ERR_INVALID_MASK = - (string)Application.Current.FindResource("str_ErrInvalidMask"); + public static string ERR_INVALID_MULTI_MASK = + (string)Application.Current.FindResource("str_ErrInvalidMultiMask"); public static string ERR_INVALID_WIDTH = (string)Application.Current.FindResource("str_ErrInvalidWidth"); public static string ERR_INVALID_SYSDEF = diff --git a/SourceGen/RuntimeData/Help/advanced.html b/SourceGen/RuntimeData/Help/advanced.html index e1f9c3a..1a92ed5 100644 --- a/SourceGen/RuntimeData/Help/advanced.html +++ b/SourceGen/RuntimeData/Help/advanced.html @@ -27,12 +27,14 @@ matters.

Platform symbol files consist of comments, commands, and symbols. Blank lines, and lines that begin with a semicolon (';'), are ignored. Lines -that begin with an asterisk ('*') are commands. Two are currently +that begin with an asterisk ('*') are commands. Three are currently defined:

Tags can be used by extension scripts to identify a subset of symbols. @@ -44,30 +46,70 @@ are treated as untagged.

All other lines are symbols, which have the form:

-  label {=|@} value [width] [;comment]
+  LABEL {=|@|<|>} VALUE [WIDTH] [;COMMENT]
 
-

Labels must be at least two characters long, begin with a letter or +

The LABEL must be at least two characters long, begin with a letter or underscore, and consist entirely of alphanumeric ASCII characters (A-Z, a-z, 0-9) and the underscore ('_'). (This is the same format required for line labels in SourceGen.)

-

Use '@' for address values, and '=' for constants. The only important -difference between them is that address values will be applied automatically -to operands that reference addresses outside the scope of the file. -Constants are never applied automatically.

+

The next token can be one of:

+ +

If an instruction references an address, and that address is outside +the bounds of the file, the list of address symbols (i.e. everything +that's not a constant) will be scanned for a match. +If found, the symbol is applied automatically. You normally want to +use '@', but can use '<' and '>' for memory-mapped I/O locations +that have different behavior depending on whether they are read or +written.

-

The value is a number in decimal, hexadecimal (with a leading '$'), or +

The VALUE is a number in decimal, hexadecimal (with a leading '$'), or binary (with a leading '%'). The numeric base will be recorded and used when formatting the symbol in generated output, so use whichever form is most appropriate. Values are unsigned 24-bit numbers.

-

The width is optional, and ignored for constants. It must be a +

The WIDTH is optional, and ignored for constants. It must be a decimal or hexadecimal value between 1 and 65536, inclusive. If omitted, the default width is 1.

-

The comment is optional. If present, it will be saved and used as the +

The COMMENT is optional. If present, it will be saved and used as the end-of-line comment on the .EQ directive if the symbol is used.

+

Using MULTI_MASK

+ +

The multi-address mask is used for systems like the Atari 2600, where +RAM, ROM, and I/O registers appear at multiple addresses. The hardware +looks for certain address lines to be set or clear, and if the pattern +matches, another set of bits is examined to determine which register or +RAM address is being accessed.

+ +

For example, suppose the access pattern for a set of registers is +???0 ??1? 1??x xxxx (where '?' can be any value, 0/1 must +be that value, and 'x' means the bit is used to determine the register). +So any value between $0280-029f matches, as does $23c0-23df, but +$0480 and $1280 don't. The register number is found in the low five bits.

+

The corresponding MULTI_MASK line, with values specifed in binary, +would be:

+
*MULTI_MASK %0001001010000000 %0000001010000000 %0000000000011111
+

The values are CompareMask, CompareValue, and AddressMask. To +determine if an address is in the register set, we check to see if +(address & CompareMask) == CompareValue. If so, we can +extract the register number with (address & AddressMask).

+ +

We don't want to have a huge collection of equates at the top of the +file, so whatever value is used in the symbol declaration is considered +the "canonical" value. All other matching values are expressed as an +offset.

+

All values must fall between 0 and $00FFFFFF. The set bits in +CompareMask and AddressMask must not overlap, and CompareValue must not +have any bits set that aren't also set in CompareMask.

+ +

Creating a Project-Specific Symbol File

To create a platform symbol file for your project, just create a new diff --git a/SourceGen/RuntimeData/Help/editors.html b/SourceGen/RuntimeData/Help/editors.html index b56ff2b..395044b 100644 --- a/SourceGen/RuntimeData/Help/editors.html +++ b/SourceGen/RuntimeData/Help/editors.html @@ -295,6 +295,11 @@ to the note in the code list and in the "Notes" window.

Edit Project Symbol

This is used to edit the properties of a project symbol.

+

Symbols marked as "address" will be applied automatically when an +operand references an address outside the scope of the data file. They +will not be applied to addresses inside the data file. Symbols +marked as "constant" are not applied automatically, and must be +explicitly specified as an operand.

The label must meet the criteria for symbols (see All About Symbols), and must not have the same name as another project symbol. It can overlap @@ -302,19 +307,17 @@ with platform symbols and user labels.

The value may be entered in decimal, hexadecimal, or binary. The numeric base you choose will be remembered, so that the value will be displayed the same way when used in a .EQ directive.

-

You can optionally provide a width. For example, if the address is -of a two-byte pointer or a 64-byte buffer, you would set the width field -to cause all references to any location in that range to be set to the -symbol. Widths may be entered in hex or decimal. If the field -is left blank, a width of 1 is assumed. Overlapping symbols are allowed. -The width is ignored for constants.

+

You can optionally provide a width for address symbols. For example, +if the address is of a two-byte pointer or a 64-byte buffer, you would +set the width field to cause all references to any location in that range +to be set to the symbol. Widths may be entered in hex or decimal. If +the field is left blank, a width of 1 is assumed. Overlapping symbols +are allowed. The width is ignored for constants.

If you enter a comment, it will be placed at the end of the line of the .EQ directive.

-

Symbols marked as "address" will be applied automatically when an -operand references an address outside the scope of the data file. They -will not be applied to addresses inside the data file. Symbols -marked as "constant" are not applied automatically, and must be -explicitly specified as an operand.

+

For address symbols that represent a memory-mapped I/O location, it +can be useful to have different symbols for reads and writes. Use +the Read/Write checkboxes to specify the desired behavior.

Create/Edit Local Variable Table

diff --git a/SourceGen/RuntimeData/Help/intro.html b/SourceGen/RuntimeData/Help/intro.html index d4cd6ca..cd5eb1e 100644 --- a/SourceGen/RuntimeData/Help/intro.html +++ b/SourceGen/RuntimeData/Help/intro.html @@ -144,6 +144,15 @@ instructions. If you don't know what state the flags are in, you can't know whether LDA #value is two bytes or three, and the disassembly of the instruction stream will come out wrong.

+

Some addresses correspond to memory-mapped I/O, rather than RAM or ROM. +Accessing the address can have side effects, like changing between text +and graphics modes. Sometimes reading and writing have different effects. +For example, on later models of the Apple II, reading from +$C000 returns the most recently hit key, while writing to $C000 disables +80 columns.

+

On a few systems, such as the Atari 2600, RAM, ROM, and registers can +appear at multiple locations, "mirrored" across the address space.

+

Character Encoding

The American Standard Code for Information Interchange (ASCII) was @@ -459,6 +468,12 @@ a 4-byte symbol in the middle of a 256-byte symbol, the 4-byte symbol will be visible because the start point is closer to the addresses it covers than the start of the 256-byte range.

+

Platform symbols can be designated for reading, writing, or both. +Normally you'd want both, but if an address is a memory-mapped I/O +location that has different behavior for reads and writes, you'd want +to define two different symbols, and have the correct one applied +based on the access type.

+

Project symbols behave like platform symbols, but they are defined in the project file itself. The editor will prevent you from creating two symbols with the same name. If two symbols have the same diff --git a/SourceGen/SGTestData/2021-external-symbols b/SourceGen/SGTestData/2021-external-symbols index 0f01d0cd503370fe66b4f23f8ef3a699152bfafd..b1d9202bba3b99a8fab52d6edc7f10ddcd06ea43 100644 GIT binary patch delta 135 zcmWN{y%EAN3 $5001 2 ;W + + +; +; MULTI_MASK tests. +; +; The behavior of overlapping masks is not currently defined, so we don't test +; that scenario. +; + +; overlaps with multi range in second symbol file +AlsoMoreMultiZero @ $c110 ;winner + +*MULTI_MASK $ff00 $c000 $000f ;$c000-c00f, repeats $c010-c01f, etc. to $c0ff +MultiZero @ $c000 +AlsoMultiZero @ $c010 ;wins (alphabetically) +MultiOne @ $c021 +; Test: C000, C010, C020, C0F0 +; Test: C001, C011, C021 +; Test: C002, C012, C022 + +MultiRead < $c004 3 ;$c004/5/6, read-only +MultiWrite > $c005 3 ;$c005/6/7, write-only +; Test: read C003 C004 C005 C006 C007 +; Test: write C004 C005 C006 C007 C008 + + +; +; Invalid values. These cause a warning at load time, and the symbol will +; be ignored. +; + +; Not in range. +MultiInvalid @ $1234 + +; Not all covered addresses are inside the masked range. +TooLong @ $c0f8 $a + +; +; Badly-formed MULTI_MASK entries. These cause a warning at load time, and +; the directive will be ignored. +; +*MULTI_MASK $fffff $ffff $ffff ;range +*MULTI_MASK $ffff $fffff $ffff ;range +*MULTI_MASK $ffff $ffff $fffff ;range + +*MULTI_MASK diff --git a/SourceGen/SGTestData/2021-external-symbols-2.sym65 b/SourceGen/SGTestData/2021-external-symbols-2.sym65 index b9407f5..388ef04 100644 --- a/SourceGen/SGTestData/2021-external-symbols-2.sym65 +++ b/SourceGen/SGTestData/2021-external-symbols-2.sym65 @@ -11,3 +11,31 @@ SameValB_A @ $2110 SameValC_B @ $2120 SepOver1 @ $3100 4 ;$3100-3103, inclusive + +; I/O direction test -- replace part of the write-only range +WriteOnly2 > $5002 + +; +; MULTI_MASK tests. +; + +; This overlaps with an earlier declaration, but *only* for address $c010, +; not for all occurrences. +NonMultiOver @ $c010 ;winner + + +*MULTI_MASK $ff00 $c100 $000f ;$c100-c10f, repeats $c110-c11f, etc. to $c1ff + +; Symbol in previous file overlaps with this. +; Test: C100, C110, C120 +MoreMultiZero @ $c100 + + +; +; More erroneous masks. These are in a separate file mostly to test how +; errors in multiple files are reported. +; +*MULTI_MASK $fff0 $000f $000f ;CompareValue has bits not in CompareMask +*MULTI_MASK $fff0 $fff0 $00ff ;AddressMask and CompareMask overlap + +*MULTI_MASK diff --git a/SourceGen/SGTestData/2021-external-symbols.dis65 b/SourceGen/SGTestData/2021-external-symbols.dis65 index 00a6715..f5965fa 100644 --- a/SourceGen/SGTestData/2021-external-symbols.dis65 +++ b/SourceGen/SGTestData/2021-external-symbols.dis65 @@ -1,6 +1,6 @@ ### 6502bench SourceGen dis65 v1.0 ### { -"_ContentVersion":2,"FileDataLength":222,"FileDataCrc32":-233099313,"ProjectProps":{ +"_ContentVersion":2,"FileDataLength":329,"FileDataCrc32":-573118187,"ProjectProps":{ "CpuName":"6502","IncludeUndocumentedInstr":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{ "AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true}, "PlatformSymbolFileIdentifiers":["PROJ:2021-external-symbols-1.sym65","PROJ:2021-external-symbols-2.sym65","PROJ:2021-external-symbols-3.sym65"],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{ diff --git a/SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S b/SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S index f33b9fe..6832803 100644 --- a/SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S +++ b/SourceGen/SGTestData/Expected/2021-external-symbols_64tass.S @@ -18,15 +18,25 @@ Over2a = $3006 ;$3006 Over3 = $3006 ;$3006-300c SepOver1 = $3100 ;$3100-3103, inclusive SepOver2 = $3102 ;$3102-3105, inclusive +ReadOnly = $5000 ;R +WriteOnly = $5001 ;W +WriteOnly2 = $5002 +MultiRead = $c004 ;$c004/5/6, read-only +MultiWrite = $c005 ;$c005/6/7, write-only +AlsoMultiZero = $c010 ;wins (alphabetically) +NonMultiOver = $c010 ;winner +MultiOne = $c021 +MoreMultiZero = $c100 +AlsoMoreMultiZero = $c110 ;winner BankWrap = $fff0 * = $1000 L1000 lda CodeWrap+255 ldx L1000 ldy L1000+1 - lda L10DD - lda CodeWrap+478 - lda CodeWrap+485 + lda L1148 + lda CodeWrap+585 + lda CodeWrap+592 nop lda $1ffe lda SameName1-1 @@ -103,5 +113,46 @@ LocalVar .var $41 lda $4001 lda BankWrap+8 lda