"Physics". Not only did ZUN restrict the X velocity to the 5 discrete
states of -8, -4, 0, 4, and 8 (because hey, unaligned blitting is slow
anyway?), but gravity is also only applied every 5 frames.
We're still missing quite a bit of usage code, but these are the core
functions. One of which turned out to be undecompilable, due to… a
rigorously defined instruction order when performing arithmetic between
`double`s and `float`s?! Still, spelling out all this stuff in ASM
seems much better than somehow splitting the data segment, just so that
we can immediately use literals there.
Part of P0097, funded by Ember2528.
… Wow, the orb is *actually* only ever displayed at byte-aligned X
coordinates, divisible by 8. It's only thanks to the constant spinning
that its movement appears at least *somewhat* smooth.
(And yeah, this is purely a rendering issue; internally, its position
*is* tracked at pixel precision.)
Part of P0097, funded by Ember2528.
So even TH01 wasn't 100% C++ after all. Turns out that this function
was the only instance in all of REIIDEN.EXE where ReC98 previously had
different encodings for identical x86 instructions.
Part of P0096, funded by Ember2528.
Surprise, it's the (terribly suboptimal) setgsta() example function
from the PC-9801 Programmers' Bible! 100% identical, so ZUN must have
read that book as well.
Used for screen shaking effects, as well as the scrolling backgrounds
at the start of the Final Boss stages.
Completes P0095, funded by Yanga.
No idea why ZUN split the 6 slots into 4 with a regular alpha plane,
and 2 with a negated alpha plane. The latter are only used for Sariel's
animations.
Part of P0095, funded by Yanga.
> "Sure, this is a PI push, but this second-to-last set of PI false
positives so small, let's immediately decompile it ^.^"
> Time taken: 22 minutes
Nice when this works out!
Part of P0095, funded by Yanga.
Too bad we still have to bother with renaming the function via #define
in the few cases where an executable contains two or more copies of
this function. We can't just make it `static`, since TH02 MAINE.EXE
actually does far-call it from a different segment (and thus,
translation unit).
Part of P0094, funded by Yanga.
The REIIDEN.EXE version (which is only shown when game-overing) has a
completely invisible timeout that force-enters a high score name after
1000... *keyboard inputs*? Not frames? Why. Like, how do even you
realistically to such a number.
(Best guess: It's a hidden easter egg to amuse players who place
drinking glasses on cursor keys. Or beer bottles.)
And hey, that initialization of the name variable with ASCII spaces.
The only actually meaningful byte-wise access… except that it's not,
because ZUN could have just used the Shift-JIS ideographic space for
the exact same effect.
Completes P0093, funded by Ember2528.
Now with POSIX file I/O in both executables. And the confirmation that
the name array is indeed exclusively accessed per-byte.
Part of P0093, funded by Ember2528.
It's exactly as terrible as you would have expected after hearing
"alphabet cursor actually stored as on-screen position". Nothing gained
in reducing redundancy any further here, any meaningful change would
pretty much have to rewrite the entire thing.
Part of P0093, funded by Ember2528.
In which inline functions are apparently the only way to trick Turbo
C++ into not actually optimizing away the useless `AND AL, 0FFh`.
But come on. Just accessing the high score name characters in chunks of
16-bit Shift-JIS codepoints (which is what they are!) instead of
breaking them down to bytes *everywhere* would have been both more
readable (no swapping of Shift-JIS literals, no bitshifts), more
efficient (this isn't running on an 8-bit CPU after all), and less of a
waste of my time…
Completes P0092, funded by Yanga.
Lol, the internal alphabet cursor position is actually stored as the
on-screen top-left position of the selected character, which this
function then maps back to a character index. At least that gets rid of
quite a bunch of PI false positives now.
Part of P0092, funded by Yanga.
With ternary operator expressions straight out of Jigoku.
(TL note: Jigoku means hell.)
Nice to see that Turbo C++ apparently has no nesting limit on function
call inlining in general, though!
Part of P0092, funded by Yanga.
The only TH01 executable not supporting the numpad?
And that's the point where you just rewrite the distinct input_prev_*
values as a single array, because they're local to input_sense()
anyway.
Part of P0091, funded by Ember2528.
Yes, TH01's memory info screen will recurse into itself for every 3
frames the PgUp key is held, requiring one additional PgDown press per
recursion to actually get out of it.
You can, of course, also crash the system via a stack overflow this
way, if that's your thing.
Part of P0091, funded by Ember2528.
Of which we can express precisely *nothing* as an inline function,
because Turbo C++ always emits a useless `JMP SHORT $+2` at the end of
such an inlined function if it contains nested `if` statements. This is
also what forced some of the functions in 90252cc to be expressed as
macros. By now, this is clear enough to be documented separately.
And to warrant this separate commit.
Completes P0090, funded by Yanga.
Starting with the odd one out, the one that doesn't use master.lib and
has two input sense functions: one for the main menu, and one for the
option window.
Both of which also immediately perform the ring arithmetic on the menu
cursor variable… because there's nothing else to be done with these
inputs in OP.EXE? Separating input sensing from processing apparently
wasn't all too obvious of a thought, and it's only truly done in TH02
and later.
Part of P0090, funded by Yanga.
That's… pretty specific. The only thing on the main menu with this
color is the "1996 ZUN" text at the bottom… probably part of an
effect that we never got to see. Every other idea would be baseless
speculation, given that the snapped buffer isn't used anywhere else.
Part of P0090, funded by Yanga.
Adding op/, main/, and end/ directories does nicely cover a great
majority of the "not really further classifiable slices" implied in
d56bd45.
Part of P0086, funded by [Anonymous] and Blue Bolt.
Again, 11 necessary workarounds, vs. forcing byte aligment in at least
18 places, and that number would have significantly grown in the
future.
Part of P0085, funded by -Tom-.
5 enums where code generation wants an `int`, vs. 11 cases where using
the minimum size is exactly the right default. So it's way more
idiomatic to force those 5 to 16 bits via a dummy element… except that
we can't give it a single, consistent name, because you can't redeclare
the same element in a different enum later.
Oh well, let's have this ugly naming convention instead, which makes it
totally clear that the force element not, in fact, a valid value of
that enum.
Part of P0085, funded by -Tom-.
Which repurpose the .PTN image slots to store the background of
frequently updated VRAM sections, like all the numbers in the HUD.
Future games would simply use the text RAM and gaiji for numbers. Which
would have worked just fine for TH01 as well (especially since all the
functions we've seen so far are aligned to the 8-pixel byte grid), but
it looks as if ZUN simply wasn't aware of gaiji during the development
of TH01.
Part of P0083, funded by Yanga.