The single underscore version is actually slightly more supported among
the compilers I've seen so far. Also added the exact list now.
Part of P0183, funded by Yanga and [Anonymous].
Boom! Decompilable after all. And look what that made us finally point
out: In all 4 games that use this function, its return value is
undefined if BGM is inactive. (That is, if the user disabled it, or if
no FM sound board is installed.)
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].
It's not a kaja_func_t if it's shifted left by 8 bits. Why is it
shifted left by 8 bits to begin with, though? Why not just pass a
kaja_func_t, and assign it to AH? Arrrrgh.
Part of P0139, funded by [Anonymous].
Hardcoding these *might* have been acceptable if the numbers actually
matched the sizes defined in GAME.BAT, but they don't. With PMD's
AH=22h function, there's really no excuse though.
About time I looked into this, and expressed that constant as an inline
function that can easily be replaced with a proper implementation.
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].
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].
Might look uglier, but has the advantage of not generating an empty
segment with the default name… *and* the default padding, which will
really come in handy with the following breakthrough.
Part of P0137, funded by [Anonymous].
A decompilation of ZUN-written ASM that was almost worth it, for once!
Too bad that those aren't the <string.h> intrinsics that the
Wolfenstein 3D disassembly hinted at, though.
Part of P0135, funded by [Anonymous].
Turns out that ARG RETURNS is only really necessary in DEFCONV
functions, which are explicitly declared to use either the C or PASCAL
calling convention. In functions without such a declaration, ARG by
itself works just fine, and won't emit any instructions on its own.
The parameter lists for PASCAL functions still have to be reversed in
that case, though… oh well, let's just comment these cases to hopefully
reduce the confusion.
Part of P0134, funded by [Anonymous].
`cPtrSize` is simply the wrong constant for calculating parameter
offsets on the stack, because it corresponds to the memory model's
default distance, not the function's distance. Luckily, ARG has a
RETURNS clause, and if you declare all parameters in there, ARG won't
emit that pesky and unnecessary `ENTER 0, 0` instruction. Big discovery
right there!
Sadly, ARG is unusable for ZUN's silly functions that keep the base
pointer in BX. TASM declares the resulting equates as `[BP+offset]`,
and it's apparently impossible to only get `offset` out of such an
equate later.
So, rather than staying with numbers, let's reimplement ARG for these
functions instead. This way, we can even abstract away the stack clear
size for the `RET` instructions.
It's a bit rough around the edges though, forcing you to explicitly
specify the function distance, and to pass the parameters in reverse
order compared to the C declaration (thankfully, all of these use the
PASCAL calling convention). It also doesn't work with more complex
types yet. But certainly better than numbers.
Part of P0134, funded by [Anonymous].
Umm… but this can't be in the same translation unit as frame_delay(),
because OP.EXE has cdg_put_nocolors() inbetween, which means we'd have
to compile it twice.
What probably happened there: ZUN originally wrote this in C when
frame_delay() was still next to it, then generated ASM from it,
tinkered with that, and ultimately only linked that ASM into the final
game, with the NOPCALL still in there. That might very well be the one
temporary NOPCALL workaround we can never get rid of…
Oh well, at least we got lucky with the padding, and can keep the
cdg_put_nocolors() decompilation from the last commit.
Part of P0133, funded by [Anonymous].
And that's the intended way to play back the hidden DEMO5.REC. Unlock
the Extra Stage with all 4 characters, then hold the left and right
arrow keys in the main menu while waiting the usual demo replay.
For a recording of that replay, see
https://www.youtube.com/watch?v=iP2ywlW2u4U
Completes P0119, funded by [Anonymous] and -Tom-.
Wow, this is the first time we're about to call any of these from C
land in ≥TH03? Found no built-in way to just uppercase an identifier
in TASM, so apparently we have to spell out the names in both lower-
and uppercase.
So, let's go back to regular, non-macro PUBLIC / PROC / ENDP code
wherever we can – for all functions introduced in ≥TH03, and for
everything that takes no parameters. It's simply not worth the
trouble.
Part of P0114, funded by Lmocinemod.
And with all possible .COM executables decompiled, this set of changes
reaches an acceptable scope, allowing us to *finally*…
Part of P0077, funded by Splashman and -Tom-.
Oh hey, guarding declarations with complicated types via #ifdef limits
the header files we additionally have to #include!
Part of P0076, funded by [Anonymous] and -Tom-.
That should make this convoluted copypasta a bit easier to read. And
sure, I could have done something about the loop as well, but
SHOT_FUNC_INIT already hides enough control flow behind a macro…
Part of P0037, funded by zorg.
Oh, right, these functions can have parameters. So, let's turn snd_kaja_func()
into a macro that combines the function number and the parameter into the AX
value for the driver.
And renaming them all to the short filenames they will be decompiled to for
consistency. These functions aren't really immediately hardware-related, as
we've established earlier in the decompilation.