[Decompilation] [th05] snd_load()

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].
This commit is contained in:
nmlgc 2021-02-23 17:46:23 +01:00
parent 903f5b55de
commit 01c92da1ac
14 changed files with 162 additions and 121 deletions

View File

@ -152,17 +152,17 @@ bin\th05\res_kso.com: th05\res_kso.cpp
$**
| masters.lib
bin\th05\op.exe: th05\op010.cpp bin\th05\op.obj th05\op011.cpp th05\m_char.cpp bin\th05\snd_kaja.obj bin\th05\pi_cpp_1.obj bin\th05\pi_asm_1.obj bin\th05\pi_cpp_2.obj bin\th05\pi_asm_2.obj bin\th05\initop.obj bin\th05\input_s.obj bin\th05\inp_h_w.obj bin\th05\snd_dlym.obj th05\cdg_p_nc.cpp bin\th05\frmdelay.obj bin\th04\cdg_load.obj bin\th05\egcrect.obj bin\hfliplut.obj
bin\th05\op.exe: th05\op010.cpp bin\th05\op.obj th05\op011.cpp th05\m_char.cpp bin\th05\snd_load.obj bin\th05\snd_kaja.obj bin\th05\pi_cpp_1.obj bin\th05\pi_asm_1.obj bin\th05\pi_cpp_2.obj bin\th05\pi_asm_2.obj bin\th05\initop.obj bin\th05\input_s.obj bin\th05\inp_h_w.obj bin\th05\snd_dlym.obj th05\cdg_p_nc.cpp bin\th05\frmdelay.obj bin\th04\cdg_load.obj bin\th05\egcrect.obj bin\hfliplut.obj
$(CC) $(CFLAGS) $(LARGE_LFLAGS) -DGAME=5 -DBINARY='O' -3 -Z -nbin\th05\ -eOP.EXE @&&|
$**
|
bin\th05\main.exe: bin\th05\main.obj th05\main010.cpp th05\main011.cpp th05\p_common.cpp th05\p_reimu.cpp th05\p_marisa.cpp th05\p_mima.cpp th05\p_yuuka.cpp bin\th05\player.obj bin\th05\hud_bar.obj bin\th05\scoreupd.obj th05\main012.cpp th05\main013.cpp bin\hfliplut.obj bin\th05\bullet.obj bin\th05\snd_kaja.obj bin\th05\initmain.obj bin\th05\input_s.obj bin\th05\inp_h_w.obj bin\th05\frmdelay.obj bin\th04\cdg_load.obj th05\main031.cpp th05\main032.cpp th05\main033.cpp th05\main034.cpp
bin\th05\main.exe: bin\th05\main.obj th05\main010.cpp th05\main011.cpp th05\p_common.cpp th05\p_reimu.cpp th05\p_marisa.cpp th05\p_mima.cpp th05\p_yuuka.cpp bin\th05\player.obj bin\th05\hud_bar.obj bin\th05\scoreupd.obj th05\main012.cpp th05\main013.cpp bin\hfliplut.obj bin\th05\bullet.obj bin\th05\snd_load.obj bin\th05\snd_kaja.obj bin\th05\initmain.obj bin\th05\input_s.obj bin\th05\inp_h_w.obj bin\th05\frmdelay.obj bin\th04\cdg_load.obj th05\main031.cpp th05\main032.cpp th05\main033.cpp th05\main034.cpp
$(CC) $(CFLAGS) $(LARGE_LFLAGS) -3 -Z -DGAME=5 -DBINARY='M' -nbin\th05\ -eMAIN.EXE @&&|
$**
|
bin\th05\maine.exe: bin\th05\maine.obj th05\maine011.cpp th05\regist.cpp th05\staff.cpp bin\th05\snd_kaja.obj bin\th05\pi_cpp_1.obj bin\th05\pi_asm_1.obj bin\th05\pi_cpp_2.obj bin\th05\pi_asm_2.obj bin\th05\initmain.obj bin\th05\input_s.obj bin\th05\inp_h_w.obj bin\th05\snd_dlym.obj bin\th05\frmdelay.obj bin\th04\cdg_load.obj bin\th05\egcrect.obj bin\hfliplut.obj
bin\th05\maine.exe: bin\th05\maine.obj th05\maine011.cpp th05\regist.cpp th05\staff.cpp bin\th05\snd_load.obj bin\th05\snd_kaja.obj bin\th05\pi_cpp_1.obj bin\th05\pi_asm_1.obj bin\th05\pi_cpp_2.obj bin\th05\pi_asm_2.obj bin\th05\initmain.obj bin\th05\input_s.obj bin\th05\inp_h_w.obj bin\th05\snd_dlym.obj bin\th05\frmdelay.obj bin\th04\cdg_load.obj bin\th05\egcrect.obj bin\hfliplut.obj
$(CC) $(CFLAGS) $(LARGE_LFLAGS) -DGAME=5 -DBINARY='E' -Z -nbin\th05\ -eMAINE.EXE @&&|
$**
|

View File

@ -77,6 +77,7 @@ extern "C" {
int MASTER_RET bgm_init(int bufsiz);
void MASTER_RET bgm_finish(void);
int MASTER_RET bgm_read_sdata(const char MASTER_PTR *fn);
int MASTER_RET bgm_sound(int num);
// ----------------------------------------

View File

@ -5,7 +5,7 @@
#include "libs/kaja/kaja.h"
#include "th02/snd/snd.h"
extern char snd_load_fn[13];
extern char snd_load_fn[SND_FN_LEN];
void snd_load(const char *fn, kaja_func_t func)
{

View File

@ -1,4 +1,3 @@
; Shorter symbols for the [func] parameter of snd_load()
SND_LOAD_SONG equ (KAJA_GET_SONG_ADDRESS shl 8)
SND_LOAD_SE equ (PMD_GET_SE_ADDRESS shl 8)

View File

@ -49,15 +49,18 @@ void snd_delay_until_volume(uint8_t volume);
void snd_delay_until_measure(int measure);
#endif
// Shorter symbols for the [func] parameter of snd_load()
#define SND_LOAD_SONG (kaja_func_t)(KAJA_GET_SONG_ADDRESS << 8)
#define SND_LOAD_SE (kaja_func_t)(PMD_GET_SE_ADDRESS << 8)
#define SND_FN_LEN 13
#if defined(PMD) && (GAME <= 3) /* requires kaja.h */
// Loads a song in .M format ([func] = SND_LOAD_SONG) or a sound effect
// bank in EFC format ([func] = SND_LOAD_SE) into the respective work
// buffer of the sound driver. If MIDI is used, 'md' is appended to the
// file name.
void DEFCONV snd_load(const char *fn, kaja_func_t func);
void DEFCONV snd_load(const char fn[SND_FN_LEN], kaja_func_t func);
#endif
void snd_se_reset(void);

View File

@ -1,14 +1,3 @@
; Loads a song ([func] = SND_LOAD_SONG) or a sound effect bank ([func] =
; SND_LOAD_SE) into the respective work buffer of the sound driver. [fn] must
; not have any extension. Depending on snd_bgm_mode and snd_se_mode, the
; following file is loaded:
; • '[fn].m26' if SND_BGM_FM26
; • '[fn].m86' if SND_BGM_FM86
; • '[fn].mmd' if SND_BGM_MIDI
; • '[fn].efc' if SND_SE_FM
; • '[fn].efs' if SND_SE_BEEP (using master.lib's BGM driver)
; void __stdcall snd_load(int func, const char *fn)
snd_load proc
arg @@func:word, @@fn:ptr

View File

@ -30,3 +30,25 @@ static inline bool snd_is_active() {
// Checks the requested BGM and SE modes against the available hardware and
// sets [snd_se_mode] and [snd_bgm_mode] accordingly. Returns [snd_bgm_mode].
unsigned char pascal snd_determine_modes(int req_bgm_mode, int req_se_mode);
// Loads a song ([func] = SND_LOAD_SONG) or a sound effect bank ([func] =
// SND_LOAD_SE) into the respective work buffer of the sound driver. [fn] must
// not have any extension. Depending on [snd_bgm_mode], [snd_se_mode], and
// game, the following file is loaded:
//
// | | TH04 | TH05 |
// |----------------+------------+-----------|
// | [snd_bgm_mode] | [func] = SND_LOAD_SONG |
// |----------------+------------+-----------|
// | SND_BGM_FM26 | [fn].m26 | [fn].m |
// | SND_BGM_FM86 | [fn].m86 | [fn].m2 |
// | SND_BGM_MIDI | [fn].mmd | [fn].md | (yes, see TH05's load[data].asm)
// |----------------+------------+-----------|
// | [snd_se_mode] | [func] = SND_LOAD_SE |
// |----------------+------------+-----------|
// | SND_SE_FM | [fn].efc | [fn].efc |
// | SND_SE_BEEP | [fn].efs | [fn].efs | (using master.lib's BGM driver)
//
// Note that the TH05 version will infinitely loop if neither the file for the
// current [snd_bgm_mode] nor "[fn].m" exist.
void pascal snd_load(const char fn[SND_FN_LEN], int16_t func);

View File

@ -1,97 +0,0 @@
; Loads a song ([func] = SND_LOAD_SONG) or a sound effect bank ([func] =
; SND_LOAD_SE) into the respective work buffer of the sound driver. [fn] must
; not have any extension. Depending on snd_bgm_mode and snd_se_mode, the
; following file is loaded:
; • '[fn].m' if SND_BGM_FM26
; • '[fn].m2' if SND_BGM_FM86
; • '[fn].md' if SND_BGM_MIDI (yes, see this game's snd_load[data].asm)
; • '[fn].efc' if SND_SE_FM
; • '[fn].efs' if SND_SE_BEEP (using master.lib's BGM driver)
; Note that the TH05 version will infinitely loop if neither the file for the
; current snd_bgm_mode nor '[fn].m' exist.
; void __stdcall snd_load(int func, const char *fn)
snd_load proc
arg @@func:word, @@fn:far ptr
push bp
mov bp, sp
push si
push di
mov dx, ds
mov es, dx
mov di, offset _snd_load_fn
lds si, @@fn
mov bp, @@func
mov cx, SND_LOAD_FN_LEN
rep movsb
mov ds, dx
mov di, offset _snd_load_fn
dec cx
xor ax, ax
repne scasb
dec di
mov byte ptr [di], '.'
inc di
cmp bp, SND_LOAD_SE
jnz short @@song
cmp _snd_se_mode, SND_SE_OFF
jz short @@ret
xor bx, bx
cmp _snd_se_mode, SND_SE_BEEP
jnz short @@set_ext
mov dword ptr [di], 'sfe'
call bgm_finish
call bgm_init pascal, 2048
call bgm_read_sdata pascal, ds, offset _snd_load_fn
jmp short @@ret
@@song:
cmp _snd_bgm_mode, SND_BGM_OFF
jz short @@ret
kajacall KAJA_SONG_STOP
movzx bx, _snd_bgm_mode
shl bx, 2
@@set_ext:
mov eax, dword ptr aSND_LOAD_EXT[bx]
@@open:
mov [di], eax
mov dx, offset _snd_load_fn
mov ax, 3D00h
int 21h
jnb short @@which_driver?
cmp ax, 2 ; ENOFILE, from <errno.h>
jnz short @@ret
mov eax, dword ptr aSND_LOAD_EXT_FM26
jmp short @@open
@@which_driver?:
mov bx, ax
mov ax, bp
cmp ah, KAJA_GET_SONG_ADDRESS
jnz short @@PMD
cmp _snd_bgm_mode, SND_BGM_MIDI
jnz short @@PMD
int 61h
jmp short @@read
@@PMD:
int 60h
@@read:
mov ax, 3F00h
mov cx, 0FFFFh
int 21h
mov ah, 3Eh
int 21h
push es
pop ds
@@ret:
pop di
pop si
pop bp
ret 6
snd_load endp

121
th05/snd/load.cpp Normal file
View File

@ -0,0 +1,121 @@
#pragma codeseg SHARED_
#pragma option -3
extern "C" {
#include <errno.h>
#include "platform.h"
#include "x86real.h"
#include "decomp.h"
#include "master.hpp"
#include "libs/kaja/kaja.h"
#include "th05/snd/snd.h"
extern char snd_load_fn[SND_FN_LEN];
extern const char SND_LOAD_EXT[4][4];
void pascal snd_load(const char fn[SND_FN_LEN], int16_t func)
{
#define _DI reinterpret_cast<char near *>(_DI)
#define func_local _BP
#define ext _EAX
__asm { mov dx, ds; }
// memcpy(snd_load_fn, fn, sizeof(SND_LOAD_FN));
_ES = _DX;
_DI = snd_load_fn;
__asm { lds si, fn; }
func_local = func;
_CX = sizeof(snd_load_fn);
__asm { rep movsb; }
// _DI = strchr(str, '\0');
_DS = _DX;
_DI = snd_load_fn;
_CX--; // = -1
_AX = '\0';
__asm { repne scasb; }
_DI--;
*(_DI) = '.';
_DI++;
if(func_local == SND_LOAD_SE) {
// Only not decompilable because the jump distance happens to exactly
// be 127 bytes, for which Turbo C++ doesn't emit short jumps anymore.
__asm {
cmp snd_se_mode, SND_SE_OFF;
jz short ret;
}
_BX = 0;
if(snd_se_mode == SND_SE_BEEP) {
*reinterpret_cast<uint32_t near *>(_DI) = 0x00736665; // "efs\0"
bgm_finish();
bgm_init(2048);
bgm_read_sdata(snd_load_fn);
return;
}
} else {
if(snd_bgm_mode == SND_BGM_OFF) {
return;
}
/* TODO: Replace with the decompiled expressions
* snd_kaja_func(KAJA_SONG_STOP, 0);
* _BX = (snd_bgm_mode << 2);
* Since snd_kaja_interrupt() is undecompilable, this can never work
* with the original translation unit structure. */
__asm {
push (KAJA_SONG_STOP shl 8);
push cs;
call near ptr snd_kaja_interrupt;
db 0x0F, 0xB6, 0x1E; // MOVZX BX,
dw offset snd_bgm_mode; // snd_bgm_mode
shl bx, 2
}
}
ext = *reinterpret_cast<const int32_t *>(&SND_LOAD_EXT[0][_BX]);
// ZUN bug: Infinite loop if neither the file for the current
// [snd_bgm_mode] nor "[fn].m" exist.
while(1) {
*reinterpret_cast<uint32_t near *>(_DI) = ext;
// DOS file open
reinterpret_cast<char near *>(_DX) = snd_load_fn;
_AX = 0x3D00;
geninterrupt(0x21);
if(!FLAGS_CARRY) {
break;
}
if(_AX != ENOFILE) {
return;
}
ext = *reinterpret_cast<const int32_t *>(SND_LOAD_EXT[SND_BGM_FM26]);
}
_BX = _AX; // Unnecessary, just remove this line
_AX = func_local;
if((_AH == KAJA_GET_SONG_ADDRESS) && (snd_bgm_mode == SND_BGM_MIDI)) {
geninterrupt(MMD);
} else {
geninterrupt(PMD);
}
// DOS file read; song data address is in DS:DX
_AX = 0x3F00;
_CX = 0xFFFF;
geninterrupt(0x21);
// DOS file close
_AH = 0x3E;
geninterrupt(0x21);
__asm { push es; }
__asm { pop ds; }
ret:
#undef func_local
#undef ext
#undef _DI
}
}

View File

@ -1,4 +1,6 @@
aSND_LOAD_EXT db 'efc',0
aSND_LOAD_EXT_FM26 db 'm',0,0,0
aSND_LOAD_EXT_FM86 db 'm2',0,'m' ; Yes, that's how it's addressed.
aSND_LOAD_EXT_MIDI db 'md',0
public _SND_LOAD_EXT
_SND_LOAD_EXT label dword
db 'efc',0
db 'm',0,0,0
db 'm2',0,'m' ; Yes, that's how it's addressed.
db 'md',0

1
th05/snd_load.cpp Normal file
View File

@ -0,0 +1 @@
#include "th05/snd/load.cpp"

View File

@ -10561,7 +10561,7 @@ include th04/formats/cdg_put.asm
include th02/exit.asm
include th04/math/vector1_at.asm
include th04/math/vector2_at.asm
include th05/snd/load.asm
extern SND_LOAD:proc
extern SND_KAJA_INTERRUPT:proc
extern GAME_INIT_MAIN:proc
extern _input_reset_sense:proc

View File

@ -7536,7 +7536,7 @@ include th02/exit.asm
include th04/math/vector1_at.asm
include th04/math/vector2_at.asm
include th04/bgimage_put_rect.asm
include th05/snd/load.asm
extern SND_LOAD:proc
extern SND_KAJA_INTERRUPT:proc
extern PI_PUT_QUARTER_MASKED_8:proc
extern PI_LOAD:proc

View File

@ -2541,7 +2541,7 @@ include th05/music/piano.asm
GRCG_SETCOLOR_DIRECT_DEF 1
db 0
include th04/bgimage_put_rect.asm
include th05/snd/load.asm
extern SND_LOAD:proc
extern SND_KAJA_INTERRUPT:proc
extern PI_PUT_MASKED_8:proc
extern PI_LOAD:proc