… Wait, we *can* decompile functions with both parameters, no stack
frame, and a Pascal calling convention?! Good that I already forgot how
impossible it was to decompile the first function I looked at that fell
into this category!
Part of P0148, funded by [Anonymous].
Including the pointless DOS I/O variation in TH05's MAIN.EXE.
I'm slowly running out of characters to remove from the first segment
name in that file, though…
Part of P0148, funded by [Anonymous].
Last one! Done with the SHARED segment for the forseeable future! 🎉
Sure, not the best C++ code either, but still by far the sanest
implementation of this function in any of the 4 games.
Completes P0139, funded by [Anonymous].
Segment alignment issues once again… but that completes the SHARED
segments of all TH03 and TH05 binaries, for now!
Part of P0139, funded by [Anonymous].
4 games, 4 different versions of this function. Interestingly,
moving the game-specific differences to inline functions makes it
obvious that this function was only intended for BGM, not sound
effects.
Part of P0139, funded by [Anonymous].
We've been using the three-letter extension names for everything else
that isn't .DAT, so it's more consistent to do the same for .MPN,
however particularly nice and readable its FOURCC may be. 8.3 filenames
also really appreciate that they'll have to accommodate one fewer
letter.
Part of P0138, funded by [Anonymous] and Blue Bolt.
Segment alignment forces us to do all of those at once… but now, we've
not only caught up with the segment split point in TH04's OP.EXE and
MAINE.EXE, but also decompiled all instances of DEFCONV functions!
Part of P0138, funded by [Anonymous] and Blue Bolt.
Boom! Clever segment renaming allows us to link the same .OBJ into 12
binaries.
(Well, 10 for now, due to alignment issues in TH04's OP.EXE and
MAINE.EXE.)
Part of P0138, funded by [Anonymous] and Blue Bolt.
So, we have a problem. The ridiculously optimized graph_putsa_fx() used
in TH04 and TH05 swaps out the target of two CALL instructions at
run-time… because there *really* wasn't any free register left for an
indirect CALL, eh? Therefore, the necessary relative addresses have to
be calculated at assembly time, by subtracting the target function
label from the call site label.
Unfortunately, the resulting values are stored in the .DATA segment,
which we can't move out right now. Declaring the labels as EXTERN
wouldn't work either, since the linker can't do fancy arithmetic and is
limited to simply replacing address placeholders with one single
address. This is explained pretty well at:
http://computer-programming-forum.com/46-asm/48f3d4a463bb38d1.htm
Which means we're stuck, and can't separate out this function for the
foreseeable future.
So, time to approach the SHARED segment from the top instead, to at
least get everything around graph_putsa_fx() done now. 🤷
vram_planes_set() is the first common function there. But since it was
introduced in TH01, we've got some maintenance to do for that game
first…
Part of P0138, funded by [Anonymous] and Blue Bolt.
Exhibit B for the theory that ZUN did *not* set the default calling
convention to `pascal` for TH03.
Part of P0138, funded by [Anonymous] and Blue Bolt.
Exhibit A for the theory that ZUN did *not* set the default calling
convention to `pascal` for TH03.
… Then again, at this point, it's way more likely that ZUN simply
didn't have a unified build setup for any of the games, and rather
pieced them together with manually compiled .OBJ files.
Part of P0137, funded by [Anonymous].
Second previously undecompilable translation unit, second creative
workaround for the workaround. We can't compile snd_se_play() with -WX,
as that function needs a stack frame, and it's also illegal to disable
-WX in the middle of a translation unit. But since we only need word
alignment in front of snd_se_reset() *and* that function is identical
in all 4 games, it makes sense to move it to its own translation unit.
And then you notice that the TH02/TH03 and TH04/TH05 versions of the
other two functions are basically identical. The small differences can
easily be moved out to inline functions, leaving us with a single
implementation file for all 4 games. Nice!
Part of P0137, funded by [Anonymous].
Lovely. Turns out that all it needed to motivate the previous research
was one function that is simply too precious to be kept in ASM…
Part of P0137, funded by [Anonymous].
Finishing this push with some semi-maintenance… and yet another `inline`
function replacing a `#define` ASM macro with fully idiomatic C++ code.
Completes P0136, funded by [Anonymous].
Reason: Self-modifying. -.-
Also, why no GRCG? Would have allowed blitting via REP MOVSD… Might as
well optimize all the way if you're going the ASM route to begin with.
Part of P0136, funded by [Anonymous].
Eh, REP MOVSD is used too inconsistently across the games to justify
replacing these macros with an `inline` function. Still can use a
custom one here to make the register usage a bit more explicit, though.
Part of P0136, funded by [Anonymous].
Reason: Self-modifying. -.-
The TH05 version *might* be decompilable into a mess. Don't have time
for that right now, though.
Part of P0136, funded by [Anonymous].