From c90c7d52c4fd8111459ac7ce4e537bcb35b3293f Mon Sep 17 00:00:00 2001 From: nmlgc Date: Wed, 6 Apr 2022 20:12:44 +0200 Subject: [PATCH] [Decompilation] [th04] Stage 4 Marisa: Point-reflected movement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the function that causes Marisa's `Divide Error` crash when called during a 4-frame window during the end of certain patterns. Just like with Kurumi's division by zero, it's just as undefined here what should happen instead, and any possible fix can only ever be a fan-fiction interpretation of the code. And since the community will have an easier time understanding and debating a C++-level hack instead of an ASM-level one, it makes sense to first decompile this function… …and document it, which is actually much harder! Part of P0189, funded by Arandui and Lmocinemod. --- Makefile.mak | 2 +- th04/boss_4m.cpp | 1 + th04/main/boss/b4m.cpp | 59 +++++++++++++++ th04_main.asm | 168 +++++++++++------------------------------ 4 files changed, 105 insertions(+), 125 deletions(-) create mode 100644 th04/boss_4m.cpp create mode 100644 th04/main/boss/b4m.cpp diff --git a/Makefile.mak b/Makefile.mak index 3efd5357..2497a985 100644 --- a/Makefile.mak +++ b/Makefile.mak @@ -126,7 +126,7 @@ bin\th04\op.exe: bin\th04\op.obj th04\m_char.cpp bin\th01\vplanset.obj bin\th02\ $** | -bin\th04\main.exe: bin\th04\main.obj bin\th04\slowdown.obj th04\ems.cpp th04\playfld.cpp th04\f_dialog.cpp th04\dialog.cpp bin\th04\player_p.obj bin\th04\scoreupd.obj th04\hud_ovrl.cpp bin\th04\cfg_lres.obj bin\th01\vplanset.obj bin\th03\vector2.obj bin\th02\frmdely1.obj bin\th03\hfliplut.obj th04\mpn_free.cpp bin\th04\input_w.obj th04\mpn_l_i.cpp bin\th04\vector.obj bin\th04\snd_pmdr.obj bin\th04\snd_mmdr.obj bin\th04\snd_kaja.obj bin\th04\snd_mode.obj bin\th04\snd_load.obj bin\th04\cdg_put.obj bin\th04\exit.obj bin\th04\initmain.obj bin\th04\cdg_p_na.obj bin\th04\cdg_p_pr.obj bin\th04\input_s.obj bin\th04\snd_se_r.obj bin\th04\snd_se.obj bin\th04\cdg_load.obj th04\gather.cpp bin\th04\scrolly3.obj bin\th04\motion_3.obj th04\mb_dft.cpp bin\th04\it_spl_u.obj th04\bullet_u.cpp th04\bullet_a.cpp th04\boss.cpp th04\boss_4r.cpp th04\boss_x2.cpp +bin\th04\main.exe: bin\th04\main.obj bin\th04\slowdown.obj th04\ems.cpp th04\playfld.cpp th04\f_dialog.cpp th04\dialog.cpp bin\th04\player_p.obj bin\th04\scoreupd.obj th04\hud_ovrl.cpp bin\th04\cfg_lres.obj bin\th01\vplanset.obj bin\th03\vector2.obj bin\th02\frmdely1.obj bin\th03\hfliplut.obj th04\mpn_free.cpp bin\th04\input_w.obj th04\mpn_l_i.cpp bin\th04\vector.obj bin\th04\snd_pmdr.obj bin\th04\snd_mmdr.obj bin\th04\snd_kaja.obj bin\th04\snd_mode.obj bin\th04\snd_load.obj bin\th04\cdg_put.obj bin\th04\exit.obj bin\th04\initmain.obj bin\th04\cdg_p_na.obj bin\th04\cdg_p_pr.obj bin\th04\input_s.obj bin\th04\snd_se_r.obj bin\th04\snd_se.obj bin\th04\cdg_load.obj th04\gather.cpp bin\th04\scrolly3.obj bin\th04\motion_3.obj th04\mb_dft.cpp bin\th04\it_spl_u.obj th04\boss_4m.cpp th04\bullet_u.cpp th04\bullet_a.cpp th04\boss.cpp th04\boss_4r.cpp th04\boss_x2.cpp $(CC) $(CFLAGS) $(LARGE_LFLAGS) -DGAME=4 -DBINARY='M' -3 -Z -nbin\th04\ -eMAIN.EXE @&&| $** | diff --git a/th04/boss_4m.cpp b/th04/boss_4m.cpp new file mode 100644 index 00000000..c4418bee --- /dev/null +++ b/th04/boss_4m.cpp @@ -0,0 +1 @@ +#include "th04/main/boss/b4m.cpp" diff --git a/th04/main/boss/b4m.cpp b/th04/main/boss/b4m.cpp new file mode 100644 index 00000000..c22fcb94 --- /dev/null +++ b/th04/main/boss/b4m.cpp @@ -0,0 +1,59 @@ +/// Stage 4 Boss - Marisa +/// --------------------- + +#include "platform.h" +#include "pc98.h" +#include "th01/math/subpixel.hpp" +#include "th04/math/motion.hpp" +extern "C" { +#include "th04/main/playfld.hpp" +} +#include "th04/main/phase.hpp" +#include "th04/main/boss/boss.hpp" + +/// State +/// ----- + +#define flystep_pointreflected_frame boss_statebyte[13] +/// ----- + +// On [flystep_pointreflected_frame] 0, this function sets up [boss] movement +// towards the point reflection of Marisa's position across a fixed position +// near the top of the sealed moon in the background. The velocity is +// calculated to reach this exact point at [duration - 12], with Marisa braking +// on the last 12 frames by halving that velocity each frame. Every call to +// this function, including the one on frame 0, then applies this velocity to +// [boss]. +// [duration] values <12 will move Marisa into the opposite direction instead. +// Returns `true` if the function was called for [duration] frames. +// +// ZUN bug: Not defined for [duration] values of 12 or 13, which will crash the +// game with a division by zero ("Divide Error"). The two patterns that pass a +// variable [duration] to this function also only happen to call this function +// every 4 frames rather than every frame, introducing additional jerkiness. +bool pascal near marisa_flystep_pointreflected(int duration) +{ + enum { + POINT_X = TO_SP(PLAYFIELD_W / 2), + POINT_Y = TO_SP((PLAYFIELD_H * 7) / 23), + BRAKE_DURATION = 12, + }; + if(flystep_pointreflected_frame == 0) { + boss.pos.velocity.x.v = ( + (POINT_X - boss.pos.cur.x) / ((duration / 2) - (BRAKE_DURATION / 2)) + ); + boss.pos.velocity.y.v = ( + (POINT_Y - boss.pos.cur.y) / ((duration / 2) - (BRAKE_DURATION / 2)) + ); + } + flystep_pointreflected_frame++; + if(flystep_pointreflected_frame >= (duration - BRAKE_DURATION)) { + boss.pos.velocity.x.v /= 2; + boss.pos.velocity.y.v /= 2; + } + if(flystep_pointreflected_frame >= duration) { + return true; + } + boss.pos.update_seg3(); + return false; +} diff --git a/th04_main.asm b/th04_main.asm index fe9415ce..277f319e 100644 --- a/th04_main.asm +++ b/th04_main.asm @@ -45,7 +45,7 @@ include th04/main/enemy/enemy.inc main_01 group SLOWDOWN_TEXT, ma_TEXT, EMS_TEXT, mai_TEXT, PLAYFLD_TEXT, main_TEXT, DIALOG_TEXT, main__TEXT, PLAYER_P_TEXT, main_0_TEXT, HUD_OVRL_TEXT, main_01_TEXT, main_012_TEXT, CFG_LRES_TEXT, main_013_TEXT g_SHARED group SHARED, SHARED_ -main_03 group GATHER_TEXT, SCROLLY3_TEXT, MOTION_3_TEXT, main_032_TEXT, IT_SPL_U_TEXT, main_033_TEXT, MB_DFT_TEXT, main_034_TEXT, BULLET_U_TEXT, BULLET_A_TEXT, main_035_TEXT, BOSS_TEXT, main_036_TEXT +main_03 group GATHER_TEXT, SCROLLY3_TEXT, MOTION_3_TEXT, main_032_TEXT, IT_SPL_U_TEXT, BOSS_4M_TEXT, main_033_TEXT, MB_DFT_TEXT, main_034_TEXT, BULLET_U_TEXT, BULLET_A_TEXT, main_035_TEXT, BOSS_TEXT, main_036_TEXT ; =========================================================================== @@ -12081,7 +12081,7 @@ IT_SPL_U_TEXT segment word public 'CODE' use16 @item_splashes_update$qv procdesc pascal near IT_SPL_U_TEXT ends -main_033_TEXT segment word public 'CODE' use16 +BOSS_4M_TEXT segment word public 'CODE' use16 ; =============== S U B R O U T I N E ======================================= @@ -16758,84 +16758,11 @@ loc_16B7D: retn marisa_16AE9 endp + @MARISA_FLYSTEP_POINTREFLECTED$QI procdesc pascal near \ + duration:word +BOSS_4M_TEXT ends -; =============== S U B R O U T I N E ======================================= - -; Attributes: bp-based frame - -marisa_16B85 proc near - -arg_0 = word ptr 4 - - push bp - mov bp, sp - push si - mov si, [bp+arg_0] - cmp _boss_statebyte[13].BSB_motion_lerp_frames, 0 - jnz short loc_16BC5 - mov ax, si - cwd - sub ax, dx - sar ax, 1 - add ax, -6 - push ax - mov ax, (192 shl 4) - sub ax, _boss_pos.cur.x - cwd - pop bx - idiv bx - mov _boss_pos.velocity.x, ax - mov ax, si - cwd - sub ax, dx - sar ax, 1 - add ax, -6 - push ax - mov ax, (112 shl 4) - sub ax, _boss_pos.cur.y - cwd - pop bx - idiv bx - mov _boss_pos.velocity.y, ax - -loc_16BC5: - inc _boss_statebyte[13].BSB_motion_lerp_frames - mov al, _boss_statebyte[13].BSB_motion_lerp_frames - mov ah, 0 - lea dx, [si-0Ch] - cmp ax, dx - jl short loc_16BEB - mov ax, _boss_pos.velocity.x - cwd - sub ax, dx - sar ax, 1 - mov _boss_pos.velocity.x, ax - mov ax, _boss_pos.velocity.y - cwd - sub ax, dx - sar ax, 1 - mov _boss_pos.velocity.y, ax - -loc_16BEB: - mov al, _boss_statebyte[13].BSB_motion_lerp_frames - mov ah, 0 - cmp ax, si - jl short loc_16BF8 - mov al, 1 - jmp short loc_16C00 -; --------------------------------------------------------------------------- - -loc_16BF8: - push offset _boss_pos - call @PlayfieldMotion@update_seg3$qv - mov al, 0 - -loc_16C00: - pop si - pop bp - retn 2 -marisa_16B85 endp - +main_033_TEXT segment byte public 'CODE' use16 ; =============== S U B R O U T I N E ======================================= @@ -17314,8 +17241,7 @@ loc_16FC6: mov ah, 0 mov dx, 160 sub dx, ax - push dx - call marisa_16B85 + call @marisa_flystep_pointreflected$qi pascal, dx mov _bullet_template.spawn_type, BST_BULLET16 mov _bullet_template.patnum, PAT_BULLET16_D_BLUE mov _bullet_template.speed, (3 shl 4) + 4 @@ -17463,8 +17389,7 @@ loc_17102: mov ah, 0 mov dx, 160 sub dx, ax - push dx - call marisa_16B85 + call @marisa_flystep_pointreflected$qi pascal, dx mov _bullet_template.spawn_type, BST_BULLET16 mov _bullet_template.patnum, PAT_BULLET16_N_STAR mov _bullet_template.BT_group, BG_SPREAD_AIMED @@ -17628,8 +17553,7 @@ loc_17266: ; --------------------------------------------------------------------------- loc_1726E: - push 60h - call marisa_16B85 + call @marisa_flystep_pointreflected$qi pascal, 96 cmp _boss_statebyte[15].BSB_subpattern_num, 0 jnz short loc_1729F mov _boss_statebyte[15].BSB_subpattern_num, 1 @@ -17776,8 +17700,7 @@ loc_173CB: ; --------------------------------------------------------------------------- loc_173D3: - push 40h - call marisa_16B85 + call @marisa_flystep_pointreflected$qi pascal, 64 or al, al jnz loc_1747D mov ax, _boss_phase_frame @@ -18025,8 +17948,7 @@ loc_1760D: ; --------------------------------------------------------------------------- loc_17615: - push 80h - call marisa_16B85 + call @marisa_flystep_pointreflected$qi pascal, 128 cmp _boss_statebyte[15].BSB_bitless_pattern_started, 0 jnz short loc_17634 call randring2_next16_and pascal, 1Fh @@ -18164,8 +18086,7 @@ loc_17794: ; --------------------------------------------------------------------------- loc_1779E: - push 0A0h - call marisa_16B85 + call @marisa_flystep_pointreflected$qi pascal, 160 cmp _boss_statebyte[15].BSB_bitless_pattern_started, 0 jnz short loc_177B8 call randring2_next16_and pascal, 1Fh @@ -18359,8 +18280,7 @@ loc_17958: ; --------------------------------------------------------------------------- loc_1795F: - push 48h ; 'H' - call marisa_16B85 + call @marisa_flystep_pointreflected$qi pascal, 72 or al, al jnz short loc_179A8 mov ax, _boss_phase_frame @@ -18489,7 +18409,7 @@ loc_17A75: mov byte_25674, 0 mov byte_2566E, 0Ah mov byte_2566F, 0 - mov _boss_statebyte[13].BSB_motion_lerp_frames, 0 + mov _boss_statebyte[13].BSB_flystep_pointreflected_frame, 0 jmp loc_17CA4 ; --------------------------------------------------------------------------- @@ -18567,7 +18487,7 @@ loc_17B1E: cmp _boss_phase_frame, 64 jl short loc_17B98 ; default inc _boss_mode_change - mov _boss_statebyte[13].BSB_motion_lerp_frames, 0 + mov _boss_statebyte[13].BSB_flystep_pointreflected_frame, 0 cmp byte_2566F, 0 jnz short loc_17B62 cmp byte_25672, 0 @@ -32388,35 +32308,35 @@ byte_2D00E db ? include th04/main/boss/funcs[bss].asm boss_statebyte_t union - BSB_angle_mirror_y db ? - BSB_bitless_pattern_started db ? - BSB_cluster_angle db ? - BSB_delta_angle_between_rings db ? - BSB_direction db ? - BSB_gengetsu_started db ? - BSB_motion_lerp_frames db ? - BSB_orb_count db ? - BSB_orb_interval db ? - BSB_origin_offset_x db ? ; pixel_t - BSB_phase_frame db ? - BSB_pattern_num_prev db ? - BSB_patterns_done db ? - BSB_pellet_stack_angle db ? - BSB_thicklaser_radius db ? - BSB_spread db ? - BSB_spread_angle db ? - BSB_spread_angle_range db ? - BSB_spread_delta_angle db ? - BSB_spread_interval db ? - BSB_spread_speed db ? - BSB_spread_turns_max db ? - BSB_spin_ring db ? - BSB_stack db ? - BSB_stack_left_angle db ? - BSB_stack_right_angle db ? - BSB_stacks_fired db ? - BSB_subpattern_id db ? - BSB_subpattern_num db ? + BSB_angle_mirror_y db ? + BSB_bitless_pattern_started db ? + BSB_cluster_angle db ? + BSB_delta_angle_between_rings db ? + BSB_direction db ? + BSB_gengetsu_started db ? + BSB_flystep_pointreflected_frame db ? + BSB_orb_count db ? + BSB_orb_interval db ? + BSB_origin_offset_x db ? ; pixel_t + BSB_phase_frame db ? + BSB_pattern_num_prev db ? + BSB_patterns_done db ? + BSB_pellet_stack_angle db ? + BSB_thicklaser_radius db ? + BSB_spread db ? + BSB_spread_angle db ? + BSB_spread_angle_range db ? + BSB_spread_delta_angle db ? + BSB_spread_interval db ? + BSB_spread_speed db ? + BSB_spread_turns_max db ? + BSB_spin_ring db ? + BSB_stack db ? + BSB_stack_left_angle db ? + BSB_stack_right_angle db ? + BSB_stacks_fired db ? + BSB_subpattern_id db ? + BSB_subpattern_num db ? boss_statebyte_t ends public _boss_statebyte