Implemented address region pre-labels. These are useful if the code is
relocating a block from address A to address B, because the code that
does the copying refers to both the "before" address and the "after"
address. Previously you'd give the block the "after" address and the
"before" would just appears as hex, because it's effectively an
external address.
Pre-labels are shown on screen with their address, but no other fields.
Showing the address makes it easy to see the label's value, which isn't
always obvious right before a .arstart. The labels are suppressed if the
address value evaluates to non-addressable.
This defines a new type of symbol, which is external and always global
in scope. Pre-labels affect label localization and must go through
the usual remapping to handle clashes with opcode mnemonics and the
use of leading underscores. Cross-references are computed, but are
associated with the file offset rather than the label line itself.
Added a new filter to the Symbols window ("PreL").
Implemented label input and checking in the address editor. Generally
added highlighting of relevant error labels.
Implemented "is relative" flag. This only affects source code
generation, replacing ".arstart <addr>" with ".arstart *+<value>".
Only output by 64tass and ACME generators.
Added a bold-text summary to radio buttons in address region edit
dialog. This makes it much easier to see what you're doing. Added
a warning to the label edit dialog when a label is being placed in
a non-addressable region.
Modified double-click behavior for .arstart/.arend to jump to the
other end when the opcode is clicked on. This matches the behavior
of instructions with address operands.
Reordered Actions menu, putting "edit operand" at the top.
Fixed AddressMap entry collision testing.
Fixed PRG issue with multiple address regions at offset +000002.
Added regression tests. Most of the complicated stuff with regions
is tested by unit tests inside AddressMap, but we still need to
exercise nested region code generation.
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.
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.
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)
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.
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.
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.
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.
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.
This experimental feature applied platform symbols to the project,
setting labels where the platform symbol's address matched an internal
address. The feature now applies project symbols as well, and has
been renamed to "apply external symbols".
We now report the number of labels set.
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.
Another chapter in the never-ending AppDomain security saga.
If a computer goes to sleep while SourceGen is running with a project
open, life gets confusing when the system wakes up. The keep-alive
timer fires and a ping is sent to the remote AppDomain, successfully.
At the same time, the lease expires on the remote side, and the objects
are discarded (apparently without bothering to query the ILease object).
This failure mode is 100% repeatable.
Since we can't prevent sandbox objects from disappearing, we have to
detect and recover from the problem. Fortunately we don't keep any
necessary state on the plugin side, so we can just tear the whole
thing down and recreate it.
The various methods in ScriptManager now do a "health check" before
making calls into the plugin AppDomain. If the ping attempt fails,
the AppDomain is "rebooted" by destroying it and creating a new one,
reloading all plugins that were in there before. The plugin binaries
*should* still be in the PluginDllCache directory since the ping failure
was due to objects being discarded, not AppDomain shutdown, and Windows
doesn't let you mess with files that hold executable code.
A new "reboot security sandbox" option has been added to the DEBUG
menu to facilitate testing.
The PluginManager's Ping() method gets called more often, but not to
the extent that performance will be affected.
This change also adds a finalizer to DisasmProject, since we're relying
on it to shut down the ScriptManager, and it's relying on callers to
invoke its cleanup function. The finalizer throws an assertion if the
cleanup function doesn't get called.
(Issue #82)
Changed "Use Keep-Alive Hack" to "Disable Keep-Alive Hack" to emphasize
that it defaults to enabled. Added a menu item for "Disable Security
Sandbox". Added a warning to both that tells the user that they must
reopen the current project for the change to take effect.
Note neither of these is persisted in app settings.
When formatting a table of 16-bit addresses in 65816 code, the bank
byte was always being set to zero. However, for "JMP (addr,X)", the
program bank is used. We now default to that behavior.
The choice can be overridden as before (select 24-bit addresses with a
constant value for the bank byte).
The old values were pretty optimistic in terms of the length of labels.
Short labels in all caps are very retro but sort of annoying to read,
so most disassemblies use longer ones. The new defaults are more
accommodating for the way labels are actually used.
This is still an "experimental" feature, but it's getting expanded
a bit. The implementation now lives in its own class. An "export"
feature that generates SGEC data has been added. The file extension
has been changed from ".sgec" to ".txt" to make it simpler to edit
under Windows.
Implemented "smart" PLB handling. If we see PHK/PLB, or 8-bit
LDA imm/PHA/PLB, we create a data bank change item. The feature
can be disabled with a project property.
Added a "fake" assembler pseudo-op for DBR changes. Display entries
in line list.
Added entry to double-click handler so that you can double-click on
a PLB instruction operand to open the data bank editor.
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.
On the 65816, 16-bit data access instructions (e.g. LDA abs) are
expanded to 24 bits by merging in the Data Bank Register (B). The
value of the register is difficult to determine via static analysis,
so we need a way to annotate the disassembly with the correct value.
Without this, the mapping of address to file offset will sometimes
be incorrect.
This change adds the basic data structures and "fixup" function, a
functional but incomplete editor, and source for a new test case.
This was a relatively lightweight change to confirm the usefulness
of relocation data. The results were very positive.
The relatively superficial integration of the data into the data
analysis process causes some problems, e.g. the cross-reference table
entries show an offset because the code analyzer's computed operand
offset doesn't match the value of the label. The feature should be
considered experimental
The feature can be enabled or disabled with a project property. The
results were sufficiently useful and non-annoying to make the setting
enabled by default.
The handful of 6502-based Atari coin-op systems were very different
from each other, so having a dedicated entry doesn't make sense.
Also, enable word-wrap in the New Project text box that holds the
system description.
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.
It's nice to be able to save images from the visualization editor
for display elsewhere. This can be done during HTML export, but
that's inconvenient when you just want one image, and doesn't allow
the output size to be specified.
This change adds an Export button to the Edit Visualization dialog.
The current bitmap, wireframe, or wireframe animation can be saved
to a GIF image. A handful of sizes can be selected from a pop-up
menu.
If you double-click on the opcode of an instruction whose operand is
an address or equate, the selection jumps to that address. This
feature is now available in the Navigate menu, with the keyboard
shortcut Ctrl+J.
While testing the feature I noticed that the keyboard focus wasn't
following the selection, so if you jumped to an address and then
used the up/down arrows, you jumped back to the previous location.
(This was true when double-clicking an opcode to jump; it was just
less noticeable since the next action was likely mouse-based.) This
has been fixed by updating the ListView item focus when we jump to a
new location.
See also issue #63 and issue #72.
Some tests were duplicated between VisWireframe and the code that
consumed the data. We now expose the Validate function as a public
interface, and invoke it from WireframeObject. Failed validation
results in a null object being returned, which was previously allowed
but not actually checked for.
If you double-click a project symbol declaration, the symbol editor
opens. I found that I was double-clicking on the comment field and
typing with the expectation that the comment would be updated, but
it was actually setting the initial focus to the label field.
With this change the symbol editor will focus the label, value, or
comment field based on which column was double-clicked.
The behavior for Actions > Edit Project Symbol and other paths to the
symbol editor are unchanged.
Also, disabled a wayward assert.
SourceGen Edit Commands is a feature that allows you to generate
commands into a file and have SourceGen apply them to the current
project. I'm not expecting this to be used by anyone but me, so
for now I'm just adding an entry to the debug menu that can read
comments out of a file.
Also, fixed a bug in the re-centering min/max code that prevented
it from working on trivial shapes.
Also, renamed the atari-avg visualizer to atari-avg-bz, with the
expectation that one day somebody might want to create a variant
for newer games.