Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
Updated project file format to save the new map entries.
Tweaked appearance of .arend directives to show the .arstart address
in the operand field. This makes it easier to match them up on screen.
Also, add a synthetic comment on auto-generated .arstart entries.
Added .arstart/.arend to the things that respond to Jump to Operand
(Ctrl+J). Selecting one jumps to the other end. (Well, it jumps
to the code nearest the other, which will do for now.)
Added a menu item to display a text rendering of the address map.
Helpful when things get complicated.
Modified the linear map iterator to return .arend items with the offset
of the last byte in the region, rather than the first byte of the
following region. While the "exclusive end" approach is pretty
common, it caused problems when updating the line list, because it
meant that the .arend directives were outside the range of offsets
being updated (and, for directives at the end of the file, outside
the file itself). This was painful to deal with for partial updates.
Changing this required some relatively subtle changes and annoyed some
of the debug assertions, such as the one where all Line items have
offsets that match the start of a line, but it's the cleaner approach.
Reimplemented "set address" dialog as the Address Region Editor. The
new dialog configures itself differently depending on whether the user
appears to be trying to create, edit, or resize a region. Each mode
has two options, to allow the user to choose between floating and fixed
end points.
The old dialog would allow you to delete an address override by erasing
the address field. Now there's an explicit "delete region" button.
Changed the SetAddress undoable change function to use AddressMapEntry
objects.
We now show detailed information on .arstart/.arend in the Info window
when the lines are selected.
PRG files are now created without specifying a region for the first
two bytes, so the load address exists in a NON_ADDR hole. Fixed a
couple of issues to make that look right.
Split ".org" into ".arstart" and ".arend" (address range start/end).
Address range ends are now shown in the code list view, and the
pseudo-op can be edited in app settings. Address range starts are
now shown after notes and long comments, rather than before, which
brings the on-screen display in sync with generated code.
Reworked the address range editor UI to include the new features.
The implementation is fully broken.
More changes to the AddressMap API, putting the resolved region length
into a separate ActualLength field. Added FindRegion(). Renamed
some things.
Code generation changed slightly: the blank line before a region-end
line now comes after it, and ACME's "} ;!pseudopc" is now just "}".
This required minor updates to some of the regression test results.
AddressMap API reshuffle. Added "pre-label" to class and API. Split
AddressMapEntry into two parts to make it clear when FLOATING_LEN
has been resolved.
Updated display line list generator to use in-line linear map
traversal. Previous approach was to walk through the list of regions
in a second pass, inserting .ORG directives, but that was awkward
and is no longer needed.
This is the first step toward changing the address region map from a
linear list to a hierarchy. See issue #107 for the plan.
The AddressMap class has been rewritten to support the new approach.
The rest of the project has been updated to conform to the new API,
but feature-wise is unchanged. While the map class supports
nested regions with explicit lengths, the rest of the application
still assumes a series of non-overlapping regions with "floating"
lengths.
The Set Address dialog is currently non-functional.
All of the output for cc65 changed because generation of segment
comments has been removed. Some of the output for ACME changed as
well, because we no longer follow "* = addr" with a redundant
pseudopc statement. ACME and 65tass have similar approaches to
placing things in memory, and so now have similar implementations.
The data operand editor determines low vs. high ASCII formatting by
examining the first byte of string data. Unfortunately the test was
broken, and for strings with a 1- or 2-byte length, was testing the
length byte instead of the character data. This is now fixed.
This also changes the way empty strings are handled. Before, they
were allowed but not counted, so you couldn't create an empty string
by itself, but could do it if it were part of a larger group. This
was unnecessarily restrictive. Empty L1/L2/null-term strings are now
allowed.
This means that a buffer full of $00 can be formatted as a big pile
of empty strings, which seems a bit ridiculous but there's no good
reason to obstruct it.
(issue #110)
If you select a line that refers to another line, the target line's
address is highlighted. If you select multiple lines, the highlight
is removed. The code that removes the highlight was inadvertently
resetting the selection, making it impossible to shift-click a
range that ended with a highlighted line.
The bottom of the main window shows the total bytes in the project
along with how many are code, data, and junk. The junk figure wasn't
updating if you changed a data item to junk or vice-versa, because a
simple format change doesn't require reanalyzing the file.
To make the counter "live", we need to tell the updater to refresh the
values whenever a format changes to or from "junk".
If you select a note or long comment and delete it, the selection is
lost, because the selected item no longer exists. This is inconvenient
if you're working with the keyboard, because it moves the keyboard
position to the top of the file.
If the previous selection was non-empty, and the new selection is
empty, we want to select the line that would have appeared below the
item that was deleted. Making this work exactly right is a bunch of
work, but we can make it work mostly right by selecting the first line
associated with the offset of the previous selection.
It's useful to have an example of an extension script that handles
multiple types of things. It's also good to show that scripts can
handle data types other than strings, and can chase an address to
format data items elsewhere in the code.
This required updating the tutorial binary, adding the new script,
and updating the tutorial text and associated screen shots.
If a DCI string ended with a string delimiter or non-ASCII character
(e.g. a PETSCII char with no ASCII equivalent), the code generator
output the last byte as a hex value. This caused an error because it
was outputting the raw hex value, with the high bit already set, which
the assembler did not expect.
This change corrects the behavior for code generation and on-screen
display, and adds a few samples to the regression test suite.
(see issue #102)
On the 65816, if you say "JSR foo" from bank $12, but "foo" is an
address in bank 0, most assemblers will conclude that you're forming
a 16-bit argument with a 16-bit address and assemble happily. 64tass
halts with an error. Up until v1.55 or so, you could fake it out
by supplying a large offset.
This no longer works. The preferred way to say "no really I mean to
do this" is to append ",k" to the operand. We now do that as needed.
I didn't want to define a new ExpressionMode for 64tass just to
support an operand modifier that should probably never actually get
generated (you can't call across banks with JSR!), so this is
implemented with a quirk and an op flag.
64tass v1.56.2625 is now the default.
(issue #104)
This is another attempt to fix the ListView keyboard position
behavior. Basic problem: if you change something in the ListView,
the keyboard position is lost, and WPF doesn't expose a nice way to
save and restore it. It appears the way to set the position is by
calling Focus() on the specific item you want to have as the "current"
keyboard position, but you can only do that at certain times.
This attempt removes the grid-splitter resize hack, in favor of just
setting a "needs refocus" flag when we restore the selection set.
This causes Focus() to be called from the StatusChanged callback on
the next event with status="containers generated".
During testing I noticed some other odd behavior: if you used "goto"
to jump to an address, up/down arrows would change focus to a
different control (menu items, grid splitters, etc). The problem
there was that we were setting focus to the ListView control rather
than to a ListViewItem, so arrow keys were in control-traversal mode
rather than list-walk mode. That is also fixed.
(Issue #105)
The DCI string format uses character values where the high bit of the
last byte differs from the rest of the string. Usually all the high
bits are clear except on the last byte, but SourceGen generally allows
either polarity.
This gets a little uncertain with single-character strings, because
SourceGen can't auto-detect DCI very effectively. A series of bytes
with the high bit set could be a single high-ASCII string or a series
of single-byte DCI strings.
The motivation for allowing them is C64 PETSCII. While ASCII allows
"high ASCII" as an escape hatch, PETSCII doesn't have that option, so
there's no way to mark the data as a character or a string. We still
want to do a bit of screening, but if the user specifies a non-ASCII
character set and the selected bytes have their high bits set, we
want to just treat the whole set as 1-byte DCI.
Some minor adjustments were needed for a couple of validity checks
that expected longer strings.
This adds some short DCI strings in different character sets to the
char-encoding regression tests.
(for issue #102)
The code for formatting an address table allows you to specify that
code start tags should be placed on all targets. However, unnecessary
tags are undesirable, and it's not necessary to add a tag if the
target is already treated as executable code. So the implementation
tested to see if the target address was already an instruction.
The code was incorrectly testing for "is instruction", rather than "is
instruction start", which meant that if the table entry pointed at an
instruction embedded inside another instruction we would conclude that
the tag wasn't necessary, when in fact it was. Not only weren't we
getting a useful table entry, we were adding a symbolic reference to a
hidden label.
(issue #103)
When formatting one or more strings with the Edit Data Operand dialog,
the code must determine which options to present. If the selected
bytes appear to represent one or more null-terminated strings, that
option is enabled in the UI.
The "format recognizers" enforce some strict rules, e.g. null-
terminated strings must end in $00, and also try to confirm that the
data looks like a printable string. The algorithm rejects strings
with "illegal" characters in them. This is simpler on some systems
than others. For example, C64 PETSCII defines quite a few control
characters in ways that make them useful for embedding in printable
strings.
The "recognizers" are only used by the operand edit feature, not as
part of an automated string detector, so there's no real upside in
overriding the user's desire to form a string with arbitrary bytes.
This removes the quick rejection from the four recognizers (null-term,
len8, len16, dci). It does not alter the high-level code, which
still insists on a certain percentage of the string being printable;
that may be worth revisiting as well.
(issue #100)
64tass wants to place its output into a 64KB region of memory,
starting at the address "*" is set to, and continuing without
wrapping around the end of the bank. Some files aren't meant to be
handled that way, so we need to generate the output differently.
If the file's output fits nicely, it's considered "loadable", and
is generated in the usual way. If it doesn't, it's treated as
"streamable", and the initial "* = addr" directive is omitted
(leaving "*" at zero), and we go straight to ".logical" directives.
65816 code with an initial address outside bank 0 is treated as
"streamable" whether or not the contents fit nicely in the designated
64K area. This caused a minor change to a few of the 65816 tests.
A new test, 20240-large-overlay, exercises "streamable" by creating
a file with eight overlapping 8KB segments that load at $8000.
While the file as a whole fits in 64KB, it wouldn't if loaded at
the desired start address.
Also, updated the regression test harness to report assembler
failure independently of overall test failure. This makes it easier
to confirm that (say) ACME v0.96.4 still works with the code we
generate, even though it doesn't match the expected output (which
was generated for v0.97).
(problem was raised in issue #98)
The initial implementation was testing the byte value rather than
the converted value, so backslashes were getting through in high
ASCII strings. PETSCII and C64 screen codes don't really have a
backslash so it's not really an issue there.
The new implementation handles high ASCII correctly. The various
201n0-char-encoding-x regression tests have been updated to verify
this.
The code generator outputs an optional comment specifying which
version of which assembler the code was generated for. This was
handled inconsistently and, for the most part, incorrectly. We now
report the correct version.
Two things changed: (1) string literals can now hold backslash
escapes like "\n"; (2) MVN/MVP operands can now be prefixed with '#'.
The former was a breaking change because any string with "\" must
be changed to "\\". This is now handled by the string operand
formatter.
Also, improved test harness output. Show the assembler versions at
the end, and include assembler failure messages in the collected
output.
If you have multiple overlapping segments (say, four 8KB chunks
that all load at $8000), and you use the "goto" command to jump
to address $8100, it should try to jump to that address within
whichever segment you happen to be working (based on the current
line selection). If that address doesn't exist in the current
segment, it's okay to punt and jump to the first occurrence of that
address in the file.
The existing code was always jumping to the first instance.
(related to issue #98)
Code generated by one of the C compilers sets up the stack frame and
then maps the direct page on top of it. If the value at the top of
the stack is 16 bits, it will be referenced via address $ff. The
local variable editor was regarding this as illegal, because lvars are
currently only defined for direct page data, and the value doesn't
entirely fit there (unless you're doing an indirect JMP on an NMOS
6502, in which case it wraps around to $00... but let's ignore that).
The actual max width of a local variable is 257 because of the
possibility of a 16-bit access at $ff.
Older versions of SourceGen don't seem to have an issue when they
encounter this situation, as worrying about (start+width) is really
just an editor affectation. The access itself is still a direct-page
operation. You won't be able to edit the entry without reducing the
length, but otherwise everything works. I don't think there's a need
to bump the file version.
Added a compiled C implementation of strlen(). The most interesting
part about this is that it references a 16-bit value via direct-page
address $ff, which means you'd want a local variable with
address=$ff and width=2. The current UI prevents this.
We were using a very simple regex pattern for the label part, and
not performing additional validation checks later. This allowed
a symbol that started with a number (e.g. "4ALL") to get much farther
than it should have.
This change modifies the regex pattern to match only valid label
syntax.
Replaced the link at the top of the manual. Remove reference to
old tutorial doc. Added an obsolescence notice to the top of the
old tutorial. Updated tutorial message and link in README.
Also, fixed sidenav style.
If you changed the width of a column, and then clicked the "toggle
display of cycle counts" button in the toolbar, the column width
would revert. The problem appears unique to that toolbar button,
so for now the fix is localized there. The more general fix is to
ensure that column width changes don't get stomped, but that's a
larger change.
There's no need to use XHTML Transitional. The only change outside
the template was to use "id" for anchors instead of "name", as the
latter is deprecated.
The calculations were wrong for certain situations, generating
answers that were useless or that caused a false-positive overflow
error.
This adds a couple of simple regression tests, modeled after layout
of the Lode Runner sprite sheet (which worked fine before) and the
Empire II EWS3 font (which failed).
This also bumps up some of the arbitrary limits in the visualizer.
(issue #94)
The $Cxxx I/O locations are mapped into banks $E0/E1, and are usually
configured to appear in banks $00/01 as well. Direct access to
locations in banks $E0/E1 is common in 16-bit code, but we only had
definitions for $E0.
This adds a clone of definitions for $E1, and renames the symbols
to be _E0/_E1 instead of _GS.
This can also be solved with MULTI_MASK, but that will always use
$E0 as the base address, so references to $E1/Cxxx will have a large
adjustment added ("+$10000"), which is kind of ugly.
Note we still don't have definitions for $01/Cxxx. I'll add those
if I run into them in 16-bit code. (That might be a reasonable use
of MULTI_MASK; feels less ugly somehow.)
The test for max allowed value was assuming 16-bit addresses.
We had no tests for 24-bit values, so this adds a 65816-specific
version of 20170-external-symbols.
Generate a 6502 test from the 65816 version by substituting the
16-bit instructions with 8-bit no-ops. There's a lot of project
edits and weird stuff in the test, so this was much easier than
starting over.
The 65816 variant is largely unchanged, though it could now be
stripped down to the stack-offset instructions.
Split into 6502/65816 portions. The 6502 version is the original
with a few in-place substitutions (e.g. JMP for BRL). The 65816
version is only needed to exercise special handling of PEA/PER.
We have a single character-encoding test that is cloned 3x so we can
exercise the different values for the project's default character
set. It was a 65816 test because it tested 16-bit immediate char
operands, but that's a very small part of it.
The 65816-specific portion is now 20122-char-encoding. The rest is
now 201{2,3,4}0-char-encoding-X.
Tests 10022-embedded-instructions and 10032-flags-and-branches were
a mix of 6502 and 65816 code. The 6502 code has been separated into
its own file, so that the tests can be run on 8-bit-only assemblers.
We append an assembler identifier to generated code. For Merlin 32,
this was "_Merlin32". All of the other assemblers use a lower-case
string, which makes Merlin look a little weird, so it has been
changed to "_merlin32".
Windows filesystems are generally case-insensitive, so this won't
likely affect anything.
A few tweaks:
- Test now requires an ORG on offset +000002, not just a correct
address.
- Suppress on-screen display of the initial ORG directive when
a PRG file is detected. Subtle, but helpful.
- In new project setup, fix initial address for PRG projects that
load at $0000.
- In new project setup, add a "load address" comment to the first line.
Also, fix some out-of-date documentation.
(issue #90)
The 10042-data-recognition test has no 65816-specific content, so it
should be named 10040-data-recognition.
Also, remove header comment from 20102-label-dp.
C64 PRG files are pretty common. Their salient feature is that they
start with a 16-bit value that is used as the load address. The
value is commonly generated by the assembler itself, rather than
explicitly added to the source file.
Not all assemblers know what a PRG file is, and some of them handle
it in ways that are difficult to guarantee in SourceGen. ACME adds
the 16-bit header when the output file name ends in ".prg", cc65
uses a modified config file, 64tass uses a different command-line
option, and Merlin 32 has no idea what they are.
This change adds PRG file detection and handling to the 64tass code
generator. Doing so required making a few changes to the gen/asm
interfaces, because we now need to have the generator pass additional
flags to the assembler, and sometimes we need code generation to
start somewhere other than offset zero. Overall the changes were
pretty minor.
The 20042-address-changes test needed a 6502-only variant. A new test
(20040-address-changes) has been added and given a PRG header. As
part of this change the 65816 variant was changed to use addresses
in bank 2, which uncovered a code generation bug that this change
also fixes.
The 64tass --long-address flag doesn't appear to be necessary for
files <= 65536 bytes long, so we no longer emit it for those.
(issue #90)
One of the most confusing things you can do is select a bunch of
lines and apply a code start tag (nee "code hint"). We now ask for
confirmation when applying start/stop hints to multiple lines.
(issue #89)
Variables, types, and comments have been updated to reflect the new
naming scheme.
The project file serialization code is untouched, because the data
is output as serialized enumerated values. Adding a string conversion
layer didn't seem worthwhile.
No changes in behavior.
(issue #89)
Before:
Hint As Code Entry Point
Hint As Data Start
Hint As Inline Data
Remove Hints
After:
Tag Address As Code Start Point
Tag Address As Code Stop Point
Tag Bytes As Inline Data
Remove Analyzer Tags
The goal is to reduce confusion. The old nomenclature was causing
problems because it's inaccurate -- they're directives, not hints --
and made it look like you need to mark data items explicitly. The
new action names emphasize the idea that you should be tagging a
single address for start/stop, not blanketing a region.
This change updates the user interface, manual, and tutorials, but
does not change how the items are referred to in code, and does not
change how the program works.
(issue #89)
Modified the asm source generators and on-screen display to show the
DP arg for BBR/BBS as hex. The instructions are otherwise treated
as relative branches, e.g. the DP arg doesn't get factored into the
cross-reference table.
ACME/cc65 put the bit number in the mnemonic, 64tass wants it to be
in the first argument, and Merlin32 wants nothing to do with any of
this because it's incompatible with the 65816.
Added an "all ops" test for W65C02.
Created the "all ops" tests for W65C02. Filled in enough of the
necessary infrastructure to be able to create the project and
disassemble the file, though we're not yet handling the instructions
correctly.
We were claiming W65C02S, but it turns out that CPU has the Rockwell
extensions and the STP/WAI instructions. We need to change existing
references to be "WDC 65C02", and add a new CPU definition for the
actual W65C02S chip.
This adds the new CPU definition, the instruction definitions for
the Rockwell extensions, and updates the selectors in project properties
and the instruction chart tool.
This change shouldn't affect any existing projects. Still more to do
before W65C02 works though, mostly because the Rockwell instructions
introduced a new two-argument address mode that has to be handled in
various places.
Sometimes you just want to turn cycle counts on for a bit, and going
through app settings is tiresome. Now there's a toolbar checkbox
for it. The icon isn't ideal, but it'll do.
Renaming a user label doesn't cause a re-analysis, just a display
update, because nothing structural is changing. However, that's not
quite true when you have a reference to a non-existent label (e.g.
"LDA hoser"), and you rename a label to match (e.g. change "blah"
to "hoser"). The most obvious consequence was that the Message list,
which enumerates the broken symbolic references, was not being
updated.
We now identify broken references during the refactoring rename, and
change the reanalysis mode accordingly.
There is a deeper problem, where undoing the label rename does the
wrong thing with the previously-broken symbolic references (in the
earlier example, it "undoes" them to "blah" rather than back to
"hoser"). I added some notes about that, but it's harder to fix.
Also, clean up some code that was still treating ReanalysisScope as
if it were bit flags.
Consider:
LDA $00 loads a value from address $00
LDA $00,X might load from $00, or might not
LDA ($00),Y dereferences $00 as a 16-bit pointer
LDA ($00,X) dereferences a pointer, not necessarily from $00
When perusing the cross-reference list, it's useful to be able to
tell whether an instruction is accessing the location, using it as a
base address, or deferencing it as a pointer. We now show "ptr" in
the list for pointer dereferences. (We already showed "idx" for
indexed accesses.)
When editing local variables, the data grid generally has the input
focus, so hitting Enter doesn't close the dialog. Rather than play
games with the focus, just take Ctrl+Enter as a shortcut to close
the dialog (same as notes and long comments).
(I found myself hitting Ctrl+Enter automatically, and being annoyed
when it didn't work, so I figured I'd make it official.)
The "show cycle counts in comments" setting is the only one that
affects both the on-screen display and generated source code. This
felt a little weird, so it's now two independent settings. This
also provided an opportunity to move it to the initial tab, so it's
easier to toggle on and off. Overall it feels less confusing to have
two settings for essentially the same thing, because code generation
is distinct from everything else.
The "spaces between bytes" setting was moved to the Display Format
tab, which seems a better fit.
Documentation and tutorial have been updated.
Also did a little bit of cleanup in EditAppSettings.
- Added SOS parameter block formatting.
- Normalized SOS call names to values in SOS Reference Manual.
- Added SOS call error code constants.
- (from robjustice) Added more to A3-IO.sym65.
Also, rearranged the ProDOS code slightly.
(issue #85)
Inline BRK instructions have a problem similar to the one fixed
for JSR/JSL back in 63d7a487, but the same fix won't work because
JSR/JSL are assumed "continue", while BRK is assumed "no-continue",
and must therefore set a no-no-continue flag. For now, we just
re-evaluate the BRK on every visit to the code.
A review of the previous fix revealed an opportunity to use the
NoContinueScript flag on subsequent visits to improve consistency.
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.
Added explicit widths to the 6502 vectors.
Two changes to Apple II hi-res visualization:
(1) Allow the row stride to be any value >= 1. This is useful
when data is stored in column-major order, i.e. it's a two-byte-wide
shape, with all of the data for the first column stored before the
data for the second column. (Set the row stride to 1, and the
column stride to the bitmap height.)
(2) Modify the layout of grids (sprite sheets and fonts), so that
we're closer to square when the item counts is low. Otherwise the
thumbnail just looks like a dashed line. (This one is strictly
cosmetic.)
Added a bunch of Applesoft entry points, and updated the F8ROM
definitions.
Added a visualizer for Applesoft shape table shapes that are not part
of an actual shape table.