Generation of HTML is extremely fast, but compressing thousands
of frames for wireframe animated GIFs can take a little while.
Sharing bitmaps between threads required two changes: (1) bitmaps
need to be "frozen" after being drawn; (2) you can't use Path because
BackgroundWorker isn't a STAThread. You can, however, use a
DrawingVisual / DrawingContext to do the rendering. Which is really
what I should have been doing all along; I just didn't know the
approach existed until I was forced to go looking for it.
Also, we now do a "run finalizers" call before generating an animated
GIF. Without it things explode after more than 10K GDI objects have
been allocated.
There's no "standard" coordinate system, so the choice is arbitrary.
However, an examination of the Transporter mesh in Elite revealed
that the mesh was designed for a left-handed coordinate system. We
can compensate for that trivially in the Elite visualizer, but we
might as well match what they're doing. (The only change required
in the code is a couple of sign changes on the Z coordinate, and an
update to the rotation matrix.)
This also downsizes Matrix44 to Matrix33, exposes the rotation mode
enum, and adds a left-handed ZYX rotation mode.
This does mean that meshes that put the front at +Z will show their
backsides initially, since we're now oriented as if we're flying
the ships rather than facing them. I considered adding a 180-degree
Y rotation (with a tweak to the rotation matrix handedness to correct
the first rotation axis) to have them facing by default, but figured
that might be confusing since +Z is supposed to be away.
Anybody who really wants it to be the other way can trivially flip
the coordinates in their visualizer (negate xc/zc).
The Z coordinates in the visualization test project were flipped so
that the design is still facing the viewer at rotation (0,0,0).
Elite has a level-of-detail cutoff in the mesh data. This change
provides a way for the visualization generator to exclude vertices
and edges that should not be rendered based on the desired LOD.
Experimented with different orders of rotation for wireframe viewer.
Made perspective projection the default behavior. Removed animation
parameters from the stored Visualization when it's not animated.
The visualization editor uses the parameters from the most recent
edit as the defaults when creating a new visualization. This change
extends the behavior to the view controls for wireframes.
Also, tweak the perspective projection scaling to fill out the area
a bit more, and change the visualization editor to use the grid's
size when setting the path dimensions.
Also, note gimbal lock.
Remember how object references from plugins are proxy objects that
time out if you don't access them for a while? I didn't either.
This reshuffles the code to keep WireframeObject references rather
than IVisualizationWireframe.
Handle the remaining visualization editor UI controls, except for
the "test" button. Save/restore wireframe animations in the
project file. Changed the preview from a 1-pixel-wide line drawn
by a path half the window size to a 2-pixel-wide line drawn by a
path the exact window size.
Moved X/Y/Z rotation out of the plugin, since it has nothing to do
with the plugin at all. (Backface removal and perspective projection
are somewhat based on the data contents, as is the choice for
whether or not they should be options.)
Added sliders for X/Y/Z rotation. Much more fun that way.
Renamed VisualizationAnimation to VisBitmapAnimation, as we're not
going to use it for wireframe animation. Created a new class to
hold wireframe animation data, which is really just a reference to
the IVisualizationWireframe so we can generate an animated GIF
without having to pry open the plugin again.
Renamed the "frame-delay-msec" parameter, which should start with
an underscore to ensure it doesn't clash with plugin parameters.
If we don't find it with an underscore we check again without for
backward compatibility.
We extract the data from the wireframe visualization, perform a
trivial transform, and display it. The perspective vs.
orthographic flag in the parameters is respected. (No rotation or
backface removal yet.)
Also, increased the thumbnail sizes in the visualization set editor
list from 48x48 to 64x64, because the nearest-pixel-scaled 48x48
looks nasty when used for wireframes.
I did a bunch of experiments to characterize line drawing. Long
story short: end points are inclusive, and coordinates should be
offset by +0.5 to avoid anti-aliasing effects.
Added some more plumbing. Updated visualization set edit dialog,
which now does word-wrapping correctly in the buttons. Added Alt+V
as the hotkey for Create/Edit Visualization Set, which allows you
to double-tap it to leap into the visualization editor.
Experimented with Path drawing, which looks like it could do just
what we need.
Also, show the file size in KB in the code/data/junk breakdown at the
bottom of the window. (Technically it's KiB, but that looked funny.)
These were being overlooked because they didn't actually cause
anything to happen (a no-op .ORG sets the address to what it would
already have been). The assembly source generator works in a way
that causes them to be skipped, so everybody was happy.
This seemed like the sort of thing that was likely to cause problems
down the road, however, so we now split regions correctly when a
no-op .ORG is encountered. This affects the uncategorized data
analyzer and selection grouping.
This changed the behavior of the 2004-numeric-types test, which was
visibly weird in the UI but generated correct output.
Added the 2024-ui-edge-cases test to provide a place to exercise
edge cases when testing the UI by hand. It has some value for the
automated regression test, so it's included there.
Also, changed the AddressMapEntry objects to be immutable. This
is handy when passing lists of them around.
Added a new category "sprite sheet", which is essentially a more
generalized version of the bitmap font renderer. It has the full
set of options for col/row/cell stride and colors. (Issue #74,
issue #75)
Added a flag that flips the high bits on bitmaps. Sometimes data
is stored with the high bit clear, but the high bit is set as it's
rendered. (Issue #76)
Also, fixed the keyboard shortcuts in the Edit Visualization Set
window, which were 'N' for both "New ___" items. (Issue #57)
Added accelerator keys to Mixed and Null strings. (Issue #67)
Added units to string counts. (Issue #68) Added proper handling
for plural/singular for bytes and strings. Changed N/A indicator
from "xx" to "--".
Added "show undocumented opcodes" checkbox, so you can choose
whether or not to see them at all. (Issue #60)
Added formatter call for the instruction mnemonics so they get
capitalized when the app is configured for upper-case opcodes.
(Issue #59)
Fix a bug where the instruction chart and ASCII chart were writing
their modes to the same setting, stomping each other.
Also, pluralized a button in the file concatenator.
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.
When editing an instruction operand, if you click "edit project
symbol", we need an initial value for the label. If you started
typing something in the instruction operand symbol field, we use
that. Unfortunately we were trying to use that even when it was
invalid, which caused an assertion to go off in the DefSymbol
constructor.
Double-clicking the opcode of an instruction that references a
local variable (e.g. "LDA ]foo") moves the selection to the line
that declares the variable. This wasn't working in the case where
the local var was annotated (e.g. "LDA ]foo?").
We want to be able to declare a symbol for a struct or buffer that
spans the entire width, and then declare more-specific items within
it that take precedence. This worked for everything but the very
first byte, because on an exact match we were resolving the conflict
alphabetically.
Now, if one is wider than the other, we use the narrower definition.
Updated 2021-external-symbols with some additional test cases.
The VisParamDescrs specify a type and a default value. If the value
has the wrong type, things would blow up in the editor. We now
check the type at plugin load time, and refuse to load the plugin at
all if an entry has a bad type.
The DisplayList update function was mis-handling the case where
there were no previous lines. This caused assertions to fire for
the case where you add a header comment to a project with no
existing header comment or EQUs.
We're doing this for user labels but not for project/platform
symbols. So if you have a constant named "BCC" you can't assemble
your code with certain assemblers. Now we rename it automatically.
Added a quick test to 2007-labels-and-symbols. (No change to ACME,
which barfs on the test.)
The "is the .junk alignment directive correct" was returning true
for subtype=None (not aligned), which caused execution to go down
the wrong path and irritate an assert.
We're generating names that nothing links to. The names aren't
guaranteed unique, so they're of dubious value anyway.
Also, fixed the Atari 2600 visualizer script filename in sys defs.
In 1.5.0-dev1, as part of changes to the way label localization
works, the local variable de-duplicator started checking against a
filtered copy of the symbol table. Unfortunately it never
re-generated the table, so a long-lived LocalVariableLookup (like
the one used by LineListGen) would set up the dup map wrong and
be inconsistent with other parts of the program.
We now regenerate the table on every Reset().
The de-duplication stuff also had problems when opcodes and
operands were double-clicked on. When the opcode is clicked, the
selection should jump to the appropriate variable declaration, but
it wasn't being found because the label generated in the list was
in its original form. Fixed.
When an instruction operand is double-clicked, the instruction operand
editor opens with an "edit variable" shortcut. This was showing
the de-duplicated name, which isn't necessarily a bad thing, but it
was passing that value on to the DefSymbol editor, which thought it
was being asked to create a new entry. Fixed. (Entering the editor
through the LvTable editor works correctly, with nary a de-duplicated
name in sight. You'll be forced to rename it because it'll fail the
uniqueness test.)
References to de-duplicated local variables were getting lost when
the symbol's label was replaced (due largely to a convenient but
flawed shortcut: xrefs are attached to DefSymbol objects). Fixed by
linking the XrefSets.
Given the many issues and their relative subtlety, I decided to make
the modified names more obvious, and went back to the "_DUPn" naming
strategy. (I'm also considering just making it an error and
discarding conflicting entries during analysis... this is much more
complicated than I expected it to be.)
Quick tests can be performed in 2019-local-variables:
- go to +000026, double-click on the opcode, confirm sel change
- go to +000026, double-click on the operand, confirm orig name
shown in shortcut and that shortcut opens editor with orig name
- go to +00001a, down a line, click on PROJ_ZERO_DUP1 and confirm
that it has a single reference (from +000026)
- double-click on var table and confirm editing entry
The list of EQUs at the top of the file is sorted, by type, then
value, then name. This adds width as an additional check, so that
if you have overlapping items the widest comes first.
This is nice when you have a general entry for a block of data, and
then specific entries for some locations within the block.
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.
When a project is opened, the main window layout subtly changes.
Of particular note: the vertical splitters below the references and
symbols windows shift upward 1 pixel when a project is opened, and
back down a pixel when the project is closed. So if you close the
app while a project is open, the settings file gets updated with the
new values for the sliders. If you restart the app a lot the effect
becomes noticeably fairly quickly.
I'm not yet sure what's causing this. I'm currently working around
the issue by not updating the window sizes in the settings file if
they're off by only one pixel.