Sometimes there's a bunch of junk in the binary that isn't used for
anything. Often it's there to make things line up at the start of
a page boundary.
This adds a ".junk" directive that tells the disassembler that it
can safely disregard the contents of a region. If the region ends
on a power-of-two boundary, an alignment value can be specified.
The assembly source generators will output an alignment directive
when possible, a .fill directive when appropriate, and a .dense
directive when all else fails. Because we're required to regenerate
the original data file, it's not always possible to avoid generating
a hex dump.
Sort of silly to have every handler immediately pull the operand out
of the file data. (This is arguably less efficient, since we now
have to serialize the argument across the AppDomain boundary, but
we should be okay spending a few extra nanoseconds here.)
We were failing to update properly when a label changed if the label
was one that a plugin cared about. The problem is that a label
add/remove operation skips the code analysis, and a label edit skips
everything but the display update. Plugins only run during the code
analysis pass, so changes weren't being reflected in the display
list until something caused it to refresh.
The solution is to ask the plugin if the label being changed is one
that it cares about. This allows the plugin to use the same
wildcard-match logic that it uses elsewhere.
For efficiency, and to reduce clutter in plugins that don't care
about symbols, a new interface class has been created to handle the
"here are the symbols" call and the "do you care about this label"
call.
The program in Examples/Scripts has been updated to show a very
simple single-call plugin and a slightly more complex multi-call
plugin.
Changed the sort order on EQU lines so that constants come before
address definitions. This caused trivial changes to three of the
regression tests.
Added the ability to jump directly to an EQU line when an opcode
is double-clicked on.
Early data sheets listed BRK as one byte, but RTI after a BRK skips
the following byte, effectively making BRK a 2-byte instruction.
Sometimes, such as when diassembling Apple /// SOS code, it's handy
to treat it that way explicitly.
This change makes two-byte BRKs optional, controlled by a checkbox
in the project settings. In the system definitions it defaults to
true for Apple ///, false for all others.
ACME doesn't allow BRK to have an arg, and cc65 only allows it for
65816 code (?), so it's emitted as a hex blob for those assemblers.
Anyone wishing to target those assemblers should stick to 1-byte mode.
Extension scripts have to switch between formatting one byte of
inline data and formatting an instruction with a one-byte operand.
A helper function has been added to the plugin Util class.
To get some regression test coverage, 2022-extension-scripts has
been configured to use two-byte BRK.
Also, added/corrected some SOS constants.
See also issue #44.
Also exercise various formatting options.
Also, fix a bug where the code that applies project/platform symbols
to numeric references was ignoring inline data items.
The current AddressMap is now passed into the plugin manager, which
wraps it in an AddressTranslate object and passes that to the
plugins at Prepare() time. This allows plugins to convert addresses
to offsets, making it possible to format complex structures.
This breaks existing plugins.
Extension scripts (a/k/a "plugins") can now apply any data format
supported by FormatDescriptor to inline data. In particular, it can
now handle variable-length inline strings. The code analyzer
verifies the string structure (e.g. null-terminated strings have
exactly one null byte, at the very end).
Added PluginException to carry an exception back to the plugin code,
for occasions when they're doing something so wrong that we just
want to smack them.
Added test 2022-extension-scripts to exercise the feature.
We were providing platform symbols to plugins through the PlatSym
list, which allowed them to find constants and well-known addresses.
We now pass all project symbols and user labels in as well. The
name "PlatSym" is no longer accurate, so the class has been renamed.
Also, added a bunch of things to the problem list viewer, and
added some more info to the Info panel.
Also, added a minor test to 2011-hinting that does not affect the
output (which is the point).
Handle situation where a symbol wraps around a bank. Updated
2021-external-symbols for that, and to test the behavior when file
data and an external symbol overlap.
The bank-wrap test turned up a bug in Merlin 32. A workaround has
been added.
Updated documentation to explain widths.
Implement multi-byte project/platform symbols by filling out a table
of addresses. Each symbol is "painted" into the table, replacing
an existing entry if the new entry has higher priority. This allows
us to handle overlapping entries, giving boosted priority to platform
symbols that are defined in .sym65 files loaded later.
The bounds on project/platform symbols are now rigidly defined. If
the "nearby" feature is enabled, references to SYM-1 will be picked
up, but we won't go hunting for SYM+1 unless the symbol is at least
two bytes wide.
The cost of adding a symbol to the symbol table is about the same,
but we don't have a quick way to remove a symbol.
Previously, if two platform symbols had the same value, the symbol
with the alphabetically lowest label would win. Now, the symbol
defined in the most-recently-loaded file wins. (If you define two
symbols with the same value in the same file, it's still resolved
alphabetically.) This allows the user to pick the winner by
arranging the load order of the platform symbol files.
Platform symbols now keep a reference to the file ident of the
symbol file that defined them, so we can show the symbols's source
in the Info panel.
These changes altered the behavior of test 2008-address-changes,
which includes some tests on external addresses that are close to
labeled internal addresses. The previous behavior essentially
treated user labels as being 3 bytes wide and extending outside the
file bounds, which was mildly convenient on occasion but felt a
little skanky. (We could do with a way to define external symbols
relative to internal symbols, for things like the source address of
code that gets relocated.)
Also, re-enabled some unit tests.
Also, added a bit of identifying stuff to CrashLog.txt.
In a recent survey, three out of four cross assemblers surveyed
recommended not using opcode mnemonics to their patients who use
labels. We now remap labels like "AND" and "jmp", using the label
map that's part of the label localizer.
We skip the step for Merlin 32, which is perfectly happy to assemble
"JMP JMP JMP".
Also, fixed a bug in MaskLeadingUnderscores that could hang the
source generator thread.
Most assemblers end local label scope when a global label is
encountered. cc65 takes this one step further by ending local label
scope when constants or variables are defined. So, if we have a
variable table with a nonzero number of entries, we want to create
a fake global label at that point to end the scope.
Merlin 32 won't let you write " LDA #',' ". For some reason the
comma causes an error. IGenerator now has a "tweak operand format"
interface that lets us fix that.
If you set things up just right, it's possible for flag status
changes to fail to get merged.
Added a regression test to 1003-flags-and-branches.
Also, tweaked the instruction operand editor to be a bit smoother
from the keyboard: added alt-key shortcuts, and put the focus on the
OK button after creating/editing a label so you can just hit the
return key twice.
If a line has a comment with a cycle count and nothing else, it was
getting an extra space or two on the end.
Also, added a few end-of-line comments to the 2020 test to show how
they interact with the cycle counts.
Cycle counting is CPU-specific. The 2020 test exercises the
65816, but there are things unique to 6502 and 65C02 that should
also be checked if we want to be thorough.
No changes to the test itself.
A ".dd2 <address>" item would get linked to an internal label, but
references to external addresses weren't doing the appropriate
search through the platform/project symbol list.
This change altered the output of the 2019-local-variables test.
The previous behavior was restored by disabling "nearby" symbol
matching in the project properties.
Updated the "lookup symbol by address" function to ignore local
variables.
Also, minor updates to Applesoft and F8-ROM symbol tables.
If you play games with code hints you can create a data operand that
overlaps with code. This causes problems (see issue #45). We now
check for that situation and ignore overlapping data descriptors.
Added a regression test to 2011-hinting.
If a symbol is defined at <addr>, and we counter STA <addr>-1,Y,
we want to use the symbol in the operand. This worked for labels
but not project/platform symbols.
Also, fixed a crash that happened if you tried to delete an auto
label.
Ported the column width stuff from EditAppSettings, which it turns
out can be simplified slightly.
Moved the clipboard copy code out into its own class.
Disabled "File > Print", which has never done anything and isn't
likely to do anything in the near future.
Also, added a note to 2019-local-variables about a test case it
should probably have.
Unlike 64tass and Merlin, which allow you to redefine symbols, ACME
uses "zones" that provide scope for local variables. This means
that, at the point of a local variable table definition, we have to
start a new zone and output the full set of active symbols, not just
the newly-defined ones. (If you set the "clear previous" flag in
the LvTable there's no difference.)
We could do a bit better by only outputting the symbols that are
actually used within the zone, similar to what we do for global
project/platform symbols, but that's a bunch of work for questionable
benefit.
After thrashing around a bit, I had to choose between making the
uniquifier more complicated, or making de-duplication a separate
step. Since I don't really expect duplicates to be a thing, I went
with the latter.
Updated the regression test.
This hits most of the edge cases, but doesn't exercise the two
duplicate name situations (var name same as user label, var name
same as project/platform symbol).
Also, fixed a bug in the EditDefSymbol uniqueness check where it
was comparing a symbol to itself.
Previously, we used the default character encoding from the project
properties to determine how strings and character constants in the
entire source file should be encoded. Now we switch between
encodings as needed. The default character encoding is no longer
relevant.
High ASCII is now an actual encoding, rather than acting like ASCII
that sometimes doesn't work. Because we can do high ASCII character
operands with "| $80", we don't output a .enc to switch from ASCII
to high ASCII unless we need to generate a string. (If we're
already in high ASCII mode, the "| $80" isn't required but won't
hurt anything.)
We now do a scan up front to see if ASCII or high ASCII is needed,
and only output the .cdefs for the encodings that are actually used.
The only gap in the matrix is high ASCII DCI strings -- the ".shift"
pseudo-op rejects text if the string doesn't start with the high
bit clear.
I didn't think it made sense, but I found something that used it,
so apparently it's a thing. This updates the operand editor to
let you choose PETSCII+DCI, and updates the assemblers to handle
it correctly (really just 64tass, since the others either don't
have a DCI directive or don't deal with PETSCII at all).
Changed the char-encoding sample from "bad dcI" to "pet dcI", and
updated the documentation.
The documentation for 64tass says you're required to pass "--ascii"
when the source file is ASCII (as opposed to PETSCII). We were
ignoring this, but it turns out that everything works a bit better
if we don't.
So we now pass "--ascii" on the command line, and add a two-line
character encoding definition to every file that is generated with
ASCII as the default encoding. The sg_petscii and sg_screen
encodings go away, as PETSCII is now the default, and we can use the
built-in "screen" encoding.
All tests use the same data file and nearly the same project file.
The only difference is the default text encoding property setting.
For "-a" it's ASCII, for "-p" it's PETSCII, for "-s" it's C64 screen
code. Right now this only affects the code generated for 64tass.
The test itself is a collection of strings and characters in the
supported character encodings. How these are handled varies
significantly between assemblers.
Both dialogs got a couple extra radio buttons for selection of
single character operands. The data operand editor got a combo box
that lets you specify how it scans for viable strings.
Various string scanning methods were made more generic. This got a
little strange with auto-detection of low/high ASCII, but that was
mostly a matter of keeping the previous code around as a special
case.
Made C64 Screen Code DCI strings a thing that works.
We weren't checking to see if character operands matched their
delimiters, so bad code like "LDA #'''" was being generated.
There wasn't a test for this in 2006-operand-formats, so the test
has been updated with single and double quotes in low and high ASCII.
The previous functions just grabbed 62 characters and slapped quotes
on the ends, but that doesn't work if we want to show strings with
embedded control characters. This change replaces the simple
formatter with the one used to generate assembly source code. This
increases the cost of refreshing the display list, so a cache will
need to be added in a future change.
Converters for C64 PETSCII and C64 Screen Code have been defined.
The results of changing the auto-scan encoding can now be viewed.
The string operand formatter was using a single delimiter, but for
the on-screen version we want open-quote and close-quote, and might
want to identify some encodings with a prefix. The formatter now
takes a class that defines the various parts. (It might be worth
replacing the delimiter patterns recently added for single-character
operands with this, so we don't have two mechanisms for very nearly
the same thing.)
While working on this change I remembered why there were two kinds
of "reverse" in the old Merlin 32 string operand generator: what you
want for assembly code is different from what you want on screen.
The ReverseMode enum has been resurrected.
The previous code output a character in single-quotes if it was
standard ASCII, double-quotes if high ASCII, or hex if it was neither
of those. If a flag was set, high ASCII would also be output as
hex.
The new system takes the character value and an encoding identifier.
The identifier selects the character converter and delimiter
pattern, and puts the two together to generate the operand.
While doing this I realized that I could trivially support high
ASCII character arguments in all assemblers by setting the delimiter
pattern to "'#' | $80".
In FormatDescriptor, I had previously renamed the "Ascii" sub-type
"LowAscii" so it wouldn't be confused, but I dislike filling the
project file with "LowAscii" when "Ascii" is more accurate and less
confusing. So I switched it back, and we now check the project
file version number when deciding what to do with an ASCII item.
The CharEncoding tests/converters were also renamed.
Moved the default delimiter patterns to the string table.
Widened the delimiter pattern input fields slightly. Added a read-
only TextBox with assorted non-typewriter quotes and things so
people have something to copy text from.
DCI is handled with the ".shift" pseudo-op. The .null, .ptext,
and .shift operators all work correctly with escaped characters,
so we no longer redo those.
This was the result of the earlier change to eliminate "reverse DCI"
strings. On further examination, it doesn't seem like we can do
much better than a hex dump without more work than the situation
merits. So hex dump it is.
During a discussion with the cc65 developers, I became convinced that
generating "MVN $01,$02" is wrong, and "MVN #$01,#$02" is correct.
64tass, cc65, and Merlin 32 all accept this syntax; only ACME does
not. Operands without a leading '#' should be treated as 24-bit
values, and have the bank byte extracted.
This change updates the on-screen display and assembled output to
include the '#'. The ACME generator uses a Quirk to suppress the
hash mark. (It doesn't currently accept values larger than 8 bits,
so there's no ambiguity.)
There's no easy way to make non-zero-bank 65816 code work, so I'm
punting and just generating a whole-file hex dump for those. This
renders tests 2007 and 2009 useless, so I'm hesitant to claim that
ACME support is fully functional.
WDM <arg> now works. MVN/MVP are still broken. Correct code is
generated for whichever version of the assembler is configured.
Regression tests updated for new version.
Also, fixed a UI bug where manual edits to the assembler path were
being ignored.
The 65816 definition makes it a two-byte instruction, like COP. On
the 6502 it acted like a two-byte instruction, but in practice very
few assemblers treat it that way. Very few humans, for that matter.
So it's now treated as a single byte instruction, with the following
byte encoded as a data value.
Instead of providing no-op CheckJsr/CheckJsl, plugins now declare
which calls they support by defining interfaces on the plugin class.
I added a CheckBrk call for code like Apple /// SOS calls, which
use BRK as an OS call mechanism. The formatting doesn't work quite
right yet because I've been treating BRK as a two-byte instruction.
Hardly anything else does, and I think it's time I stopped (but not
in this commit).
Note: THIS BREAKS ALL PLUGINS that use the inline JSR/JSL feature,
which is pretty much all of them.
This worked, sort of. The problem is that SourceGen will revert to
hex output in certain situations, such as a broken symbolic
reference. There happens to be one in the ZIPPY example, and it's
on a relative branch.
The goal with the segment stuff is to allow cc65 to treat the
source as relocatable code. In that context, a relative branch to
an absolute address doesn't make any sense, so the assembler reports
a range error.
We don't currently have a mechanism that guarantees no references
are broken (and no affordance for finding them), so we can't make
this mode the default yet.
Instead, we continue to use the generic config, but generate the
correct set of lines as comments.
(issue #39)
The system configuration you get with "-t none" works for smaller
files but fails for larger ones. This updates the generator to
produce a source file and linker script pair. (I kinda saw this
one coming -- it's why the gen/asm dialog has a combo box for the
file preview -- so it didn't require that much work.)
This currently generates a fixed script for a generic system with
64KiB of RAM, using .ORGs to set the addresses as before.
With this change, assembling a file with 65536 NOPs succeeds.
(issue #39)
If you double-click on the opcode of "JSR label", the code view
selection jumps to the label. This now works for partial operands,
e.g. "LDA #<label".
Some changes to the find-label-offset code affected the cc65 "is it
a forward reference to a direct-page label" logic. The regression
test now correctly identifies an instruction that refers to itself
as not being a forward reference.
The cc65 assembler runs in a single pass, which means forward
address references default to 16 bits. For zero-page references
we have to add an explicit width disambiguator. (This is an
unusual situation that only occurs if you have a zero-page .ORG
in the file after code that references it.)
With this change, 2014-label-dp passes, and no other regression
tests were affected.
(issue #40)
The 2014-label-dp test now passes. Prior regression tests are
unaffected.
Also, renamed an IGenerator interface to more accurately reflect
its role.
(issue #37)
This is primarily to exercise a Merlin 32 failure (issue #37).
However, it also exercises a problem with cc65 (issue #40).
Currently, only 64tass can assemble this project.
To avoid confusing the assembler, expressions with a leading
parenthesis like "(foo & $ffff) + 1" are prefixed with a "0+". This
is not necessary if the operand begins with a '#'.
(issue #16)
We now insert parenthesis as needed. This can cause problems in
some situations, so we always prefix parenthetical expressions with
"0+", which looks goofy and is unnecessary for immediate operands.
But it does generate working source code.
Renamed the "simple" expression mode to "common", as it's not
particularly simple but is what you'd expect most assemblers to do.
(OTOH, life has been full of surprises.)
(issue #16)
Gave cc65 its own expression generator, as the precedence table seems
atypical if not unique. Configured 64tass to use the "simple"
expression mode.
Added some operations on a 32-bit constant to 2007-labels-and-symbols
to exercise the current worst-case expression (shift + AND + add).
Tweaked the Merlin expression generator to handle it.
(issue #16)
Correctly handle pre-existing underscores and avoidance of
"reserved" labels.
Also, add more underscores to 2012-label-localizer to exercise
the code.
(issue #16)
Most tests pass, but 2007-labels-and-symbols fails because the
expressions recognized by 64tass don't match up with either of the
other assemblers.
This is currently using a workaround for the local label syntax.
64tass uses '_' as the prefix, which is unfortunate since SourceGen
explicitly allowed underscores in labels. (So does 64tass for that
matter, but it treats labels specially when the '_' comes first.)
We will need to rename any non-local user labels that start with '_'.
(issue #16)
There are some useful interactions between C/N and maybe Z. Added
a quick test to 1003-flags-and-branches.
Also, updated the 2008-address-changes tests. Change b37d3dba
extended the nearby-target range of out-of-file symbols by one, so
one line that didn't get an operand label now does.
These *almost* match what cc65 has, and are accepted as primary or
aliases by 64tass.
This combines the LAX and LXA operations. LXA is the immediate
form of LAX, and behaves somewhat differently (and is unstable).
I was treating them as two separate operations with independent
mnemonics, but that doesn't seem to be the preferred way to
handle it.
The cc65 generator wasn't generating LAX before; now it does. This
required nudging the width disambiguator, as LAX is a second
example of an instruction with both DP,Y and ABS,Y operands.
(issue #20)