We weren't altering the status flags after a BRK because of the
assumption that a BRK was a crash. For an inline BRK, such as a SOS
call, execution continues. We need to mark NVZC indeterminate or
we may incorrectly handle conditional branches that follow.
The BRK instruction now uses the same flag updater as JSR, since it's
effectively a subroutine call to unknown code. If execution doesn't
continue across the BRK then the flags don't matter.
Updated 20182-extension-scripts to exercise this.
Long operands, such as strings and bulk data, can span multiple lines.
SourceGen wraps them at 64 characters, which is fine for assembly
output but occasionally annoying on screen: if the operand column is
wide enough to show the entire value, the comment column is pushed
pretty far to the right.
This change makes the width configurable, as 32/48/64 characters,
with a pop-up in app settings.
The assemblers are all wired to 64 characters, though we could make
this configurable as well with an assembler-specific setting.
Some things have moved around a bit in app settings. The Asm Config
tab now comes last. Having it sandwiched in the middle of tabs that
altered the on-screen display didn't make much sense. The Display
Format is now explicitly for opcodes and operands, and is split into
two columns. The left column is managed by the "quick set" feature,
the right column is independent.
Changed basic data item from an "extended enum" to a class, so we can
keep track of where things come from (useful for the display list).
Finished edit dialog. Added serialization to project file.
The test wasn't correctly excluding instructions, so it was possible
to create a situation where a two-byte data item had an instruction
starting in the second byte.
We also weren't checking the length of the instruction to ensure that
it was wider than the reloc data. This could get weird for an
immediate constant when the M/X flags are wrong. When in doubt, don't
overwrite.
The decision of how to handle indeterminate M/X flag values is made in
StatusFlags. This provides consistent behavior throughout the app.
This was being done for M/X but not for E.
This change also renames the M/X tests, prefixing them with "Is" to
emphasize that they are boolean rather than tri-state.
There should be no change in behavior from this.
Two basic problems:
(1) cc65, being a one-pass assembler, can't tell if a forward-referenced
label is 16-bit or 24-bit. If the operand is potentially ambiguous,
such as "LDA label", we need to add an operand width disambiguator.
(The existing tests managed to only do backward references.)
(2) 64tass wants the labels on JMP/JSR absolute operands to have 24-bit
values that match the current program bank. This is the opposite of
cc65, which requires 16-bit values. We need to distinguish PBR vs.
DBR instructions (i.e. "LDA abs" vs. "JMP abs") and handle them
differently when formatting for "Common".
Merlin32 doesn't care, and ACME doesn't work at all, so neither of
those needed updating.
The 20052-branches-and-banks test was expanded to cover the problematic
cases.
Changed bank-start comments to notes, added a summary to the top-of-file
comment.
Also, fixed a bug where the app settings dialog wasn't identifying
display settings as a preset for 64tass and cc65.
My original goal was to add a sign-extended decimal format, but that
turned out to be awkward. It works for data items and instructions
with immediate operands (e.g. "LDA #-1"), but is either wrong or
useless for address operands, since most assemblers treat integers
as 32-bit values. (LDA -1 is not LDA $FFFF, it's LDA $FFFFFFFF,
which is not useful unless your asm is doing an implicit mod.)
There's also a bit of variability in how assemblers treat negative
values, so I'm shelving the idea for now. I'm keeping the updated
tests, which are now split into 6502 / 65816 parts.
Also, updated the formatter to output all decimal values as unsigned.
Most assemblers were fine with negative values, but 64tass .dword
insists on positive. Rather than make the opcode conditional on the
value's range, we now just always output unsigned decimal, which
all current assemblers accept.
Sometimes it's useful to know whether an address referenced by a
function is a direct access, or is being used as a base address.
(I'm somewhat undecided on this one, since it clutters up the list
a bit. Giving it a try.)
The code was making an unwarranted assumption about how the flags
were being set. For example, ORA #$00 can't know if the previous
contents of the accumulator were nonzero, only that the instruction
hasn't made them nonzero, but instead of marking the Z-flag
"indeterminate" it was leaving the flag in its previous state. This
produces incorrect results if the previous instruction didn't set
its flags from the accumulator contents, e.g. it was an LDX.
Test 1003-flags-and-branches has been updated to test these states.
For some reason FormatHexValue was creating format strings on
demand. In a recent change, FormatAdjustment started using them.
So if the first 4-digit hex value printed by the program was a
large adjustment, you could end up with a default-formatted adjustment
pretending to be hex.
Now we just create the 4 format strings in the Formatter constructor.
For nonzero values we were leaving Z=prev, which is wrong when Z=0
because the AND result might be zero. Now if Z=1 we leave it alone,
but if Z=0 we now set it to Z=?.
Test 1003-flags-and-branches was testing for the (incorrect)
behavior, so we're now running into a BRK. This is fine.
We emit address adjustments like "LDA thing+1", which are usually
small values. Sometimes they're large, e.g. "LDA thing-61440",
which is harder to understand than "LDA thing-$F000". So now we
show small adjustments in decimal, and large adjustments in hex.
The current definition of "small" is abs(adjust) < 256.
The tool allows you to cut a piece out of a file by specifying an
offset and a length. A pair of hex dumps helps you verify that the
positions are correct.
Also, minor cleanups elsewhere.
(1) Added an option to limit the number of bytes per line. This is
handy for things like bitmaps, where you might want to put (say) 3
or 8 bytes per line to reflect the structure.
(2) Added an application setting that determines whether the screen
listing shows Merlin/ACME dense hex (20edfd) or 64tass/cc65 hex bytes
($20,$ed,$fd). Made the setting part of the assembler-driven display
definitions. Updated 64tass+cc65 to use ".byte" as their dense hex
pseudo-op, and to use the updated formatter code. No changes to
regression test output.
(Changes were requested in issue #42.)
Also, added a resize gripper to the bottom-right corner of the main
window. (These seem to have generally fallen out of favor, but I
like having it there.)
Continue development of non-unique labels. The actual labels are
still unique, because we append a uniquifier tag, which gets added
and removed behind the scenes. We're currently using the six-digit
hex file offset because this is only used for internal address
symbols.
The label editor and most of the formatters have been updated. We
can't yet assemble code that includes non-unique labels, but older
stuff hasn't been broken.
This removes the "disable label localization" property, since that's
fundamentally incompatible with what we're doing, and adds a non-
unique label prefix setting so you can put '@' or ':' in front of
your should-be-local labels.
Also, fixed a field name typo.
This adds the concept of label annotations. The primary driver of
the feature is the desire to note that sometimes you know what a
thing is, but sometimes you're just taking an educated guess.
Instead of writing "high_score_maybe", you can now write "high_score?",
which is more compact and consistent. The annotations are stripped
off when generating source code, making them similar to Notes.
I also created a "Generated" annotation for the labels that are
synthesized by the address table formatter, but don't modify the
label for them, because there's not much need to remind the user
that "T1234" was generated by algorithm.
This also lays some of the groundwork for non-unique labels.
The "affected flags" constants were incorrect for BIT, BRK, COP,
RTI, XCE, and the undocmented instructions ANE, DCP, and SAX. The
constants are used for the changed-flag summary shown in the info
window and the instruction chart.
Of greater import: the status flag updater for BIT was incorrectly
marking N/V/C as indeterminate instead of N/V/Z. The undocmented
instructions ANE, DCP, and SAX were also incorrect.
The cycle counts shown in line comments are computed correctly, but
the counts shown in the info window and instruction chart were
displaying the full set of modifiers, ignoring the CPU type. That's
okay for the info window, which spells the modifiers out, though
it'd be better if the bits were explicitly marked as being applicable
to the current CPU or a different one.
This adds a window that displays all of the instructions for a
given CPU in a summary grid. Undocumented instructions are
included, but shown in grey italics.
Also, tweaked AppSettings to not mark itself as dirty if a "set"
operation doesn't actually change anything.
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.)
Memory-mapped I/O locations can have different behavior when read
vs. written. This is part 1 of a change to allow two different
symbols to represent the same address, based on I/O direction.
This also adds a set of address masks for systems like the Atari
2600 that map hardware addresses to multiple locations.
This change updates the data structures, .sym65 file reader,
project serialization, and DefSymbol editor.
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.
The ability to give explicit widths to local variables worked out
pretty well, so we're going to try adding the same thing to project
and platform symbols.
The first step is to allow widths to be specified in platform files,
and set with the project symbol editor. The DefSymbol editor is
also used for local variables, so a bit of dancing is required.
For platform/project symbols the width is optional, and is totally
ignored for constants. (For variables, constants are used for the
StackRel args, so the width is meaningful and required.)
We also now show the symbol's type (address or constant) and width
in the listing. This gets really distracting when overused, so we
only show it when the width is explicitly set. The default width
is 1, which most things will be, so users can make an aesthetic
choice there. (The place where widths make very little sense is when
the symbol represents a code entry point, rather than a data item.)
The maximum width of a local variable is now 256, but it's not
allowed to overlap with other variables or run of the end of the
direct page. The maximum width of a platform/project symbol is
65536, with bank-wrap behavior TBD.
The local variable table editor now refers to stack-relative
constants as such, rather than simply "constant", to make it clear
that it's not just defining an 8-bit constant.
Widths have been added to a handful of Apple II platform defs.
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.
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.
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.
Implemented local variable editing. Operands that have a local
variable reference, or are eligible to have one, can now be edited
directly from the instruction operand edit dialog.
Also, updated the code list double-click handler so that, if you
double-click on the opcode of an instruction that uses a local
variable reference, the selection and view will jump to the place
where that variable was defined.
Also, tweaked the way the References window refers to references
to an address that didn't use a symbol at that address. Updated
the explanation in the manual, which was a bit confusing.
Also, fixed some odds and ends in the manual.
Also, fixed a nasty infinite recursion bug (issue #47).
Variables are now handled properly end-to-end, except for label
uniquification. So cc65 and ACME can't yet handle a file that
redefines a local variable.
This required a bunch of plumbing, but I think it came out okay.
We now generate FormatDescriptors with WeakSymbolRefs for direct
page references that match variable table entries.
LocalVariableTable got a rewrite. We need to be unique in both
name and address, but for the address we have to take the width into
account as well. We also want to sort the display by address
rather than name. (Some people might want it sorted by name, but
we can worry about that some other time.)
Updated the DefSymbol editor to require value uniqueness. Note
addresses and constants exist in separate namespaces.
The various symbols are added to the SymbolTable so that uniqueness
checks work correctly. This also allows the operand generation to
appear to work, but it doesn't yet handle redefinition of symbols.
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 PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
The 64tass generator now uses the "default text encoding" project
property to determine how readable text should be encoded. For
example, if the property is set to PETSCII, an ASCII-to-PETSCII
encoding table is generated at the top of the output file.
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.
A delimiter definition is four strings (prefix, open, close, suffix)
that are concatenated with the character or string data to form an
operand. A delimiter set is a collection of delimiter definitions,
with separate entries for each character encoding.
This is a convenient way to configure Formatter objects, import and
export data from the app settings file, and manage the UI needed to
allow the user to customize how things look.
The full set of options didn't fit on the first app settings tab, so
there's now a separate tab just for specifying character and string
delimiters. (This might be overkill, but there are various plausible
scenarios that make use of it.)
The delimiters for on-screen display of strings can now be
configured.
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 code that searches for character strings in uncategorized data
now recognizes the C64 encodings when selected in the project
properties.
The new code avoids some redundant comparisons when runs of
printable characters are found. I suspect the new implementation
loses on overall performance because we're now calling through
delegates instead of testing characters directly, but I haven't
tested for that.
It's not quite the same as the character encoding -- sometimes we
want a mix of things -- so it gets its own enum. The value is
saved to the project file, but not actually used yet.
Also, moved some combo box strings into XAML resources.
Remove left/right arrow PNGs. Remove duplicate copies of icon.
Tweak some comments. Set application icon. Adjust padding on
group boxes in first tab of app settings.
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.
This generalizes the string pseudo-operand formatter, moving it into
the Asm65 library. The assembly source generators have been updated
to use it. This makes the individual generators simpler, and by
virtue of avoiding "test runs" should make them slightly faster.
This also introduces byte-to-character converters, though we're
currently still only supporting low/high ASCII.
Regression test output is unchanged.
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.