Commit Graph

58 Commits

Author SHA1 Message Date
nmlgc c96952f47b [Research] Discover how pointer constness affects pointer parameter codegen
In most cases, this discovery will mean that we have to bite the bullet
and remove `const` from pointer parameters if the generated ASM
wouldn't match otherwise. That by itself doesn't really simplify the
code, but at least we get to remove a single bloated `reinterpret_cast`
from one function call already.

Part of P0278, funded by Yanga.
2024-04-12 00:29:09 +02:00
nmlgc 45c9e71533 [Maintenance] Fix another bunch of accumulated typos and dead code
Part of P0239, funded by Ember2528.
2023-04-28 22:21:21 +02:00
nmlgc 15a047fc74 [Decompilation] [th05] Lasers: Ray rendering
A year ago, I would have considered this undecompilable. Turns out that
this function really benefitted from a decompilation against all odds,
especially with these 4 different views on the same 8 local variables.

Part of P0228, funded by [Anonymous] and nrook.
2023-01-17 11:53:47 +01:00
nmlgc 990588a4ea [Research] Finally figure out relativity of near references
Biggest decompilation breakthrough of the year already? Completely
removes the need for the "set near pointer to far function" hack, and
solves quite a few upcoming issues.
The TASM manuals did have a few pages on the topic of segments and
groups, but there's absolutely nothing about it in the TCC ones beyond
the three-sentence description of `#pragma codeseg` and that completely
unhelpful "🤷" of an explanation for the `Fixup overflow` error…

Part of P0227, funded by nrook.
2023-01-16 12:05:29 +01:00
nmlgc 690b4df14b [Maintenance] Fix another bunch of accumulated typos and dead code
Part of P0216, funded by JonathKane.
2022-08-16 01:38:12 +02:00
nmlgc 36930a2604 [Decompilation] REYHI*.DAT: Loading only the high score
Still finding more obscure Turbo C++ code generation details.

Part of P0213, funded by Ember2528 and GhostRiderCog.
2022-08-11 23:21:22 +02:00
nmlgc 2db58cb3d9 [Maintenance] [th01] Boss entities: Rewrite the entity array as 5 variables
Nooooo, the static initialization code proves that `boss_entities`
couldn't have been an array in ZUN's code! 😢 Which in turn explains
why YuugenMagan's and Elis' code looks the way it does. We could go
into undefined behavior territory and just cast `boss_entity_0` to a
pointer, but let's better not annoy future port authors just for my
personal convenience. In turn, we need to get a bit clever with some of
Elis' code to replace the now wholly redundant `elis_entity_t` type,
but it's not all too bad.

Part of P0207, funded by GhostPhanom.
2022-08-08 20:11:33 +02:00
nmlgc f85587dda4 [Decompilation] [th01] Stage objects: Main obstacle update/render function
In which ZUN needs a hack to make columns of vertical bumper bars work
at all with the way the Orb's X velocity is implemented… and may have
just deliberately ensured that the Orb can definitely loop infinitely
between two bumpers.

(Those two wrong NOPCALLs aren't worth working around. It would
basically require taking apart the entire function, and they'll be gone
by the next two commits anyway.)

Part of P0203, funded by [Anonymous] and GhostRiderCog.
2022-07-10 13:28:27 +02:00
nmlgc 124f68946a [Maintenance] [th01] Boss entities: Force `bos_image` through a getter method
Noooo, SinGyoku will in fact prove the existence of this getter method
via the given code generation detail. Might as well go fully consistent
then and exclusively use the methods, ending the previous party time.

Part of P0201, funded by Ember2528 and Yanga.
2022-06-24 22:50:24 +02:00
nmlgc 5f5b1a5132 [Research] Revise the section about inlining
And move it under the C++ chapter, as both top-level `inline` and
(obviously) class methods are only supported in C++ mode.

Part of P0201, funded by Ember2528 and Yanga.
2022-06-24 22:49:52 +02:00
nmlgc cceb09fc3e [Decompilation] [th01] Sariel: Pattern 12/16
The one where Sariel fires vertical 3-stacks with two spawners moving
from between the two bottom edges of the playfield, followed by
randomly raining bullets from two spawners moving between the two top
edges of the playfield. Final pattern of Sariel's first form, and
surprisingly fair by never spawning pellets on top of Reimu.

Also, about time I found a way of bypassing the -Z -3 function
parameter optimization in order to keep using nice structures like
these.

Part of P0179, funded by Ember2528.
2022-01-31 07:18:43 +01:00
nmlgc 3a751d59d4 [Decompilation] [th01] Sariel: 2×2 particle system (wavy)
> use 64-bit floating-point for particles that move on strictly
  one-dimensional lines
> use integer pixels for particles with a sine wave motion

Part of P0177, funded by Yanga.
2022-01-31 07:18:43 +01:00
nmlgc a78c4ff0f3 [Decompilation] [th01] Sariel: Background transition animation
One of the hardest decompilations in a long while:
• Required new research into code generation for pointer arithmetic
• ZUN needlessly calculating intermediate results over and over again,
  making the function tedious to read
• GRCG reads in TCR mode, which I haven't seen so far either

Part of P0176, funded by Ember2528.
2022-01-31 07:18:42 +01:00
nmlgc 6d572b33cf [Decompilation] [th01] Player: Main control and rendering function
Yup, what a giant, unstructured mess. Enjoy the 85 conditional
branches.

Completes P0163, funded by Ember2528.
2021-10-20 10:59:36 +02:00
nmlgc 81dd96e4f4 [Research] Discover how `SCOPY@` disables Turbo C++ stack cleanup optimization
Yup, it's a compiler bug, and it removes a small bit of freedom as far
as decompilation order is concerned. In particular, this means that we
can't do TH01's continue and pause menus before having decompiled the
bomb animation.
Would have been nice to pad out the previous push with those, but
instead, I had to spend way too much time figuring *this* out…

Completes P0161, funded by [Anonymous].
2021-10-09 23:28:22 +02:00
nmlgc 44d1389835 [Research] Discover how producing line numbers (`-y`) slightly optimizes jumps
Yes, seriously. Not something we actually need right now, but who
knows!

Part of P0161, funded by [Anonymous].
2021-10-09 23:27:59 +02:00
nmlgc a317cb49fc [Maintenance] Fix another bunch of accumulated typos
Part of P0161, funded by [Anonymous].
2021-10-09 23:26:02 +02:00
nmlgc 36068acb02 [Decompilation] [th01] Stage objects: Card score popup rendering
Part of P0159, funded by Yanga.
2021-09-28 18:05:25 +02:00
nmlgc 9c7981fc59 [Decompilation] [th01] Konngara: Pattern 10/12
The one with lasers fired from the sword across the whole playfield,
either from left to right or from right to left, together with aimed
3-way spreads, every 10 frames.

Part of P0155, funded by Ember2528.
2021-08-22 18:50:20 +02:00
nmlgc 4d24ca53bd [Decompilation] [th04/th05] Bullets: Update function
… (24 + (difficulty * 8) + rank) in TH04, and (42 + (difficulty * 8))
in TH05. Also, TH05 only doesn't have TH04's bullet zap animation
because ZUN didn't consistently use constants…

Completes P0151, funded by Blue Bolt and -Tom-.
2021-07-31 20:19:33 +02:00
nmlgc 18e755d99b [Decompilation] [th04] Bullets: Regular spawn function
Part of P0150, funded by Blue Bolt.
2021-07-31 20:19:32 +02:00
nmlgc 2c52bb5ef3 [Decompilation] [th04] Bullets: Velocity and angle calculation
`switch` statements compiling to binary searches if the range of values
is nasty enough? That's so cool. Apart from a few places in TH02,
this is the only place in PC-98 Touhou to show off that Turbo C++
optimization.

That code's still unexpectedly janky for what you'd expect from the 4th
game in the series, though.

Part of P0150, funded by Blue Bolt.
2021-07-31 16:15:32 +02:00
nmlgc db05bffca3 [Maintenance] Turn motion_t into a template
motion_t is also used for certain animations in MAINE.EXE, so not all
instances refer to entities in playfield space. Explicitly specifying
the latter now allows us to gain…

Part of P0149, funded by Blue Bolt, Ember2528, and -Tom-.
2021-07-31 09:33:50 +02:00
nmlgc 6233a3c3e3 [Research] Assigning near functions from other groups to near function pointers
Yup, P0148 didn't actually solve the issue it was meant to solve, and
I still had to research this workaround.

Part of P0149, funded by Blue Bolt, Ember2528, and -Tom-.
2021-07-31 09:33:49 +02:00
nmlgc aae96aec45 [Decompilation] Find out how to bypass TCC's optimization of 0 immediates
By deferring that immediate 0 to link time. 🤦

Part of P0146, funded by -Tom- and Ember2528.
2021-06-09 23:12:04 +02:00
nmlgc 794c6ab55c [Maintenance] Fix another bunch of accumulated typos
Part of P0139, funded by [Anonymous].
2021-05-11 18:47:49 +02:00
nmlgc 0f75a77dee [Research] Discover a workaround to word-align code segments from Turbo C++
Turns out that this is one of the effects of the -WX option ("Create
DPMI application")… along with generally messing up code generation.
Nothing we can't work around though, luckily! Finally getting to cross
that off the list of reasons that prevent decompilation.

Part of P0137, funded by [Anonymous].
2021-04-03 20:19:41 +02:00
nmlgc 152ecaa496 [Separate translation units] [th05] Music Room piano (undecompilable functions)
Reasons:
• piano_fm_part_put_raw(): SI register referenced and not saved on
  the stack
• piano_current_note_from(): Would be decompilable… into a mess.
  Not worth adding a separate translation unit just for it.
• piano_part_keys_put_raw(): DI register saved before the SI register
• piano_pressed_key_put(): DI register referenced and not immediately
  saved on the stack
• piano_label_put_raw(): SI and DI registered referenced and not saved
  on the stack
• grcg_setcolor_direct_seg1_raw(): Let's procrastinate this one until
  we have to reference all of these instances in C land.

And we could have even emitted that PIANO_KEY_PRESSED_TOP pixel data
into the code segment, by using `#pragma option -z` to give identical
names to both the code and the data segment. At least we can decompile
the first two functions here.

Part of P0135, funded by [Anonymous].
2021-03-19 23:23:06 +01:00
nmlgc 84d4914a3b [Separate translation units] [th02] snd_mmd_resident() (undecompilable)
Reason: Wants to be word-aligned, and the previous version in OP.EXE,
game_exit(), is not, despite having an even length :(
Oh well, at least I'm confident enough about it by now to document it.
And out of all decompilations to be thrown away, this is a pretty
dispensable one.

Part of P0133, funded by [Anonymous].
2021-01-31 15:21:11 +01:00
nmlgc 21d82fab04 [Maintenance] Declare the input bitflags in a consistent way
Turns out the inlining behavior of `const` variables at global scope
that we've been relying on lately is actually exclusive to C++ mode…
once again!

Part of P0133, funded by [Anonymous].
2021-01-30 19:13:11 +01:00
nmlgc 8b0165738a [Decompilation] [th03] .MRS: Byte-aligned, opaque blitting
Containing not one, but two decompilation innovations, one of which
works around a compiler bug using C++ template functions…

Completes P0126, funded by [Anonymous] and Blue Bolt.
2020-11-16 20:01:32 +01:00
nmlgc 967bb8b633 [Decompilation] [th03] Input mode and delay functions
Nice to see that Borland C++ optimizes bit-tests to cover just the high
or low byte of a word if possible, and that these don't have to be
two-byte structures after all.

Completes P0114, funded by Lmocinemod.
2020-09-07 21:18:40 +02:00
nmlgc 2ef3db3052 [Maintenance] Remove the `operator =` overloads for Subpixels
At least we've now documented their negative effects.

Part of P0110, funded by [Anonymous] and Blue Bolt.
2020-08-19 20:10:08 +02:00
nmlgc 52def53fd3 [Decompilation] [th01] Boss entities: Clamped movement + unblitting + blitting
A function which does so many individual things that I can't even
summarize it in a comment without spelling out each line of code. And
then, this entire clamped movement system is only used for Kikuri's
soul sprites. The usage code for every other entity just parties on
the [cur_left] and [cur_top] members, and passes a delta of (0, 0) to
this function. 🤷

Part of P0108, funded by Yanga.
2020-08-12 18:02:18 +02:00
nmlgc de688ddc20 [Decompilation] [th01] Boss entities: Byte-aligned unblitting + blitting
And we're back to not using functions, and needing our own VRAM access
macros…

Part of P0107, funded by Yanga.
2020-08-12 17:51:43 +02:00
nmlgc 816ba8342c [Decompilation] [th01] Background + foreground VRAM blit functions
Yes, functions! Those would have been nice earlier!
Also documented another overly specific Borland C++ code generation
quirk with regard to assigning expression results to elements of 16-bit
arrays…

Part of P0107, funded by Yanga.
2020-08-12 17:51:42 +02:00
nmlgc 72538610ba [Decompilation] [th01] .GRC: Load function
Still no idea what those 7 unknown bytes in the .GRC and .BOS headers
could be.

Part of P0105, funded by Yanga.
2020-08-12 16:19:39 +02:00
nmlgc edb70162e0 [Decompilation] [th01] Pellets: Manager class constructor
Note how Turbo C++ auto-generates that call to `operator new`, which
you don't see in the decompilation anymore. So yeah, as soon as you add
a constructor, Turbo C++ enforces heap allocation for any instance of
that class, even function-local ones that would otherwise be
stack-allocated.

That's where the bad reputation of C++ comes from, I guess?

Part of P0102, funded by Yanga.
2020-07-12 16:23:12 +02:00
nmlgc 442a92d32b [Decompilation] [th01] Player shots: Spawn function
Continuing to learn new things about Turbo C++'s code generation!

Part of P0098, funded by Yanga.
2020-06-13 21:15:31 +02:00
nmlgc 51de73bcc9 [Decompilation] [th01] Orb physics
"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.
2020-06-13 21:15:27 +02:00
nmlgc 026fff63a5 [Decompilation] [th01] Ending picture loading and display
Aww, how far we've come with inlining and helpful macros.

Part of P0094, funded by Yanga.
2020-05-25 15:18:44 +02:00
nmlgc 753e1b670a [Decompilation] Change graph_move_byterect_interpage() page parameters to int
Turbo C++ doesn't do the 32-bit PUSH LARGE optimization for 8-bit
parameters?

Part of P0094, funded by Yanga.
2020-05-25 15:15:41 +02:00
nmlgc 57be510056 [Decompilation] [th01] High score menu: Initial table rendering
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.
2020-05-25 15:03:55 +02:00
nmlgc 07dab293ad [Decompilation] [th01] Input declarations shared between REIIDEN and FUUIN
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.
2020-05-12 15:05:49 +02:00
nmlgc 00050d0e5e [Decompilation] [th01] VRAM text typing
Yeah, that 8×16 text RAM grid is so restricting.

Part of P0082, funded by Ember2528.
2020-03-13 19:09:12 +01:00
nmlgc ebd214bbb2 [Decompilation] [th05] RES_KSO.COM
And of course, TH05 ruins the consistency once again. Sure, the added
file error handling is nice, but we also have changes in the playful
messages (lol), and now need a third distinct optimization barrier
(🤦)… But as it turns out, inlined calls to empty functions work as
well. They also seem closer to what ZUN might have actually written
there, given that their function body could have been removed by the
preprocessor, similar to the logging functions in the Windows Touhou
games. (With the difference that the latter infamously *aren't*
inlined…)

Part of P0077, funded by Splashman and -Tom-.
2020-02-23 17:53:17 +01:00
nmlgc b8ca607c38 [Decompilation] [th02] Get zun_res1.c right
4½ years after aa56a7c, it turns out that the correct decompilation
involves… no-ops generated by assigning variables that just happen to
be in registers to themselves?! Which does get optimized out, but only
after TCC folded identical tail code in all branches of a function,
thus effectively functioning as an optimization barrier.

The initial attempt used register pseudovariables, but this definitely
is the best possible way this could work – portable, and doesn't
unnecessarily shred the code into tiny inlined functions pieces. The
mindblowing thing here is that ZUN could have actually written this to
have additional, albeit unnecessary, lines to place breakpoints on. But
that means he must have chosen those two local variables in SI and DI
completely by chance… 🤯

The best thing though? ~#pragma inline is gone~

Part of P0076, funded by [Anonymous] and -Tom-.
2020-02-23 17:12:35 +01:00
nmlgc a1f36ffa04 [Reverse-engineering] [th03] Demo initialization
Something small that came out of the resident structure review.
Including more research to suggest that we probably can't ever use C++
constructors for anything in ReC98.

Part of P0076, funded by [Anonymous] and -Tom-.
2020-02-23 16:58:30 +01:00
nmlgc ff777a099e [Decompilation] [th03] Add a type for the paletted playchar IDs
Much better than the previous incorrect comment, since the char_id()
method now easily generates the correct sequence of conversion
instructions, seen all throughout the ASM. Even within array
subscripts!

Part of P0076, funded by [Anonymous] and -Tom-.
2020-02-23 16:56:14 +01:00
nmlgc a6d292a62c [Decompilation] [th01] graph_putsa_fx
TH01's (original) version also replicates the PC-98 text RAM's reverse
and underline attributes. Which was removed in later games,
interestingly and inconsistently enough.

Part of P0068, funded by Yanga.
2020-01-14 22:04:12 +01:00