diff --git a/data/unknown_001050.rodata.s b/data/unknown_001050.rodata.s deleted file mode 100644 index 8c2947bc..00000000 --- a/data/unknown_001050.rodata.s +++ /dev/null @@ -1,28 +0,0 @@ -.include "macros.inc" - -.macro .aligned_text ascii - .asciz "\ascii" - .balign 4 -.endm - -.section .rodata - -.aligned_text "amSndPlay: Illegal sound effects table index\n" -.aligned_text "amSndPlayDirect: Somebody tried to play illegal sound %d\n" -.aligned_text "Invalid midi sequence index\n" - -glabel D_800E49DC -.float 60000000.0 - -glabel D_800E49E0 -.float 46875.0 - -glabel D_800E49E4 -.float 46875.0 - -glabel D_800E49E8 -.float 120000.0 - -glabel D_800E49EC -.float 60000000.0 - diff --git a/data/unknown_003260.rodata.s b/data/unknown_003260.rodata.s deleted file mode 100644 index ed364ad6..00000000 --- a/data/unknown_003260.rodata.s +++ /dev/null @@ -1,40 +0,0 @@ -.include "macros.inc" - -.macro .aligned_text ascii - .asciz "\ascii" - .balign 4 -.endm - -.section .rodata - -.aligned_text "audio manager: RCP audio interface bug caused DMA from bad address - move audiomgr.c in the makelist!\n" -.aligned_text "audio: ai out of samples\n" -.aligned_text "OH DEAR - No audio DMA buffers left\n" -.aligned_text "Dma not done\n" - -.word 0x00000000 - -.aligned_text "Bad soundState: voices =%d, states free =%d, states busy =%d, type %d data %x\n" -.aligned_text "playing a playing sound\n" -.aligned_text "Nonsense sndp event\n" -.aligned_text "Sound state allocate failed - sndId %d\n" -.aligned_text "Don't worry - game should cope OK\n" -.aligned_text "WARNING: Attempt to stop NULL sound aborted\n" - -glabel D_800E4BB0 -.word 0x800035C4 -.word 0x80003A0C -.word 0x80004090 -.word 0x80003B2C -.word 0x80004090 -.word 0x80004090 -.word 0x80004090 -.word 0x80003C8C -.word 0x80004090 -.word 0x80004090 -.word 0x80004090 -.word 0x80004090 -.word 0x80004090 -.word 0x80004090 -.word 0x80004090 -.word 0x80003BA4 diff --git a/include/types.h b/include/types.h index 04b872e3..a6ff2f94 100644 --- a/include/types.h +++ b/include/types.h @@ -39,4 +39,12 @@ typedef s32 ptrdiff_t; //#include //#endif +// Temporary until all the appropriate functions are decompiled. +typedef struct FloatLiteral { + float floatLiteral; +} FloatLiteral; +typedef struct DoubleLiteral { + double doubleLiteral; +} DoubleLiteral; + #endif diff --git a/src/unknown_001050.c b/src/unknown_001050.c index 47223c40..af6dcb31 100644 --- a/src/unknown_001050.c +++ b/src/unknown_001050.c @@ -38,14 +38,21 @@ u8 D_800DC670 = 0; /************ .rodata ************/ -extern f32 D_800E49DC; -extern f32 D_800E49EC; -extern u8 D_800EBF60; +// Debug strings +const char D_800E4950[] = "amSndPlay: Illegal sound effects table index\n"; +const char D_800E4980[] = "amSndPlayDirect: Somebody tried to play illegal sound %d\n"; +const char D_800E49BC[] = "Invalid midi sequence index\n"; + +const FloatLiteral D_800E49DC = { 60000000.0f }; +const FloatLiteral D_800E49E0 = { 46875.0f }; +const FloatLiteral D_800E49E4 = { 46875.0f }; +const FloatLiteral D_800E49E8 = { 120000.0f }; /*********************************/ /************ .bss ************/ +extern u8 D_800EBF60; extern ALHeap gALHeap; /* Unknown size */ @@ -539,11 +546,10 @@ void func_800014BC(f32 arg0) { musicSetTempo((s32)((f32)(u32)(musicGetTempo() & 0xFF) * arg0)); } - void musicSetTempo(s32 tempo){ if(tempo != 0){ f32 inv_tempo = (1.0f/tempo); - alCSPSetTempo(gMusicPlayer,(s32)(inv_tempo*D_800E49DC)); + alCSPSetTempo(gMusicPlayer,(s32)(inv_tempo*D_800E49DC.floatLiteral)); D_80115D30 = tempo; } } @@ -557,8 +563,6 @@ u8 musicIsPlaying(void) { } -extern f32 D_800E49E0; -extern f32 D_800E49E8; extern f32 D_80115D34; #if 1 @@ -617,7 +621,7 @@ void func_80001784(u8 a0){ void sfxSetTempo(s32 tempo){ f32 inv_tempo = (1.0f/tempo); - alCSPSetTempo(gSndFxPlayer,(s32)(inv_tempo*D_800E49EC)); + alCSPSetTempo(gSndFxPlayer,(s32)(inv_tempo * 60000000.0f)); } diff --git a/src/unknown_003260.c b/src/unknown_003260.c index 3b6c1a54..dbfc6a6b 100644 --- a/src/unknown_003260.c +++ b/src/unknown_003260.c @@ -98,6 +98,31 @@ s32 D_800DC6C4 = 0; // Currently unknown, might be a different type. /*******************************/ +/************ .rodata ************/ + +// Debug strings +const char D_800E49F0[] = "audio manager: RCP audio interface bug caused DMA from bad address - move audiomgr.c in the makelist!\n"; +const char D_800E4A58[] = "audio: ai out of samples\n"; +const char D_800E4A74[] = "OH DEAR - No audio DMA buffers left\n"; +const char D_800E4A9C[] = "Dma not done\n"; +const char D_800E4AAC[] = ""; +const char D_800E49B0[] = "Bad soundState: voices =%d, states free =%d, states busy =%d, type %d data %x\n"; +const char D_800E4B00[] = "playing a playing sound\n"; +const char D_800E4B1C[] = "Nonsense sndp event\n"; +const char D_800E4B34[] = "Sound state allocate failed - sndId %d\n"; +const char D_800E4B5C[] = "Don't worry - game should cope OK\n"; +const char D_800E4B80[] = "WARNING: Attempt to stop NULL sound aborted\n"; + +// Jump table for _handleEvent +const u32 D_800E4BB0[] = { + 0x800035C4, 0x80003A0C, 0x80004090, 0x80003B2C, + 0x80004090, 0x80004090, 0x80004090, 0x80003C8C, + 0x80004090, 0x80004090, 0x80004090, 0x80004090, + 0x80004090, 0x80004090, 0x80004090, 0x80003BA4 +}; + +/*******************************/ + /************ .bss ************/ extern s32 D_80115F90; diff --git a/tools/asm_processor/asm_processor.py b/tools/asm_processor/asm_processor.py old mode 100755 new mode 100644 index 04b94316..302668d7 --- a/tools/asm_processor/asm_processor.py +++ b/tools/asm_processor/asm_processor.py @@ -81,6 +81,9 @@ R_MIPS_26 = 4 R_MIPS_HI16 = 5 R_MIPS_LO16 = 6 +MIPS_DEBUG_ST_STATIC = 2 +MIPS_DEBUG_ST_STATIC_PROC = 14 + class ElfHeader: """ @@ -132,14 +135,19 @@ class Symbol: } Elf32_Sym; """ - def __init__(self, data, strtab): + def __init__(self, data, strtab, name=None): self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx = struct.unpack('>IIIBBH', data) assert self.st_shndx != SHN_XINDEX, "too many sections (SHN_XINDEX not supported)" self.bind = st_info >> 4 self.type = st_info & 15 - self.name = strtab.lookup_str(self.st_name) + self.name = name if name is not None else strtab.lookup_str(self.st_name) self.visibility = self.st_other & 3 + @staticmethod + def from_parts(st_name, st_value, st_size, st_info, st_other, st_shndx, strtab, name): + header = struct.pack('>IIIBBH', st_name, st_value, st_size, st_info, st_other, st_shndx) + return Symbol(header, strtab, name) + def to_bin(self): st_info = (self.bind << 4) | self.type return struct.pack('>IIIBBH', self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx) @@ -185,7 +193,7 @@ class Section: if self.sh_entsize != 0: assert self.sh_size % self.sh_entsize == 0 if self.sh_type == SHT_NOBITS: - self.data = '' + self.data = b'' else: self.data = data[self.sh_offset:self.sh_offset + self.sh_size] self.index = index @@ -466,6 +474,9 @@ class GlobalAsmBlock: self.fail(".ascii with no string", real_line) return ret + num_parts if z else ret + def align2(self): + while self.fn_section_sizes[self.cur_section] % 2 != 0: + self.fn_section_sizes[self.cur_section] += 1 def align4(self): while self.fn_section_sizes[self.cur_section] % 4 != 0: @@ -553,6 +564,9 @@ class GlobalAsmBlock: self.add_sized(self.count_quoted_size(line, z, real_line, output_enc), real_line) elif line.startswith('.byte'): self.add_sized(len(line.split(',')), real_line) + elif line.startswith('.half'): + self.align2() + self.add_sized(2*len(line.split(',')), real_line) elif line.startswith('.'): # .macro, ... self.fail("asm directive not supported", real_line) @@ -708,7 +722,7 @@ float_regexpr = re.compile(r"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f") def repl_float_hex(m): return str(struct.unpack(">I", struct.pack(">f", float(m.group(0).strip().rstrip("f"))))[0]) -def parse_source(f, opt, framepointer, input_enc, output_enc, print_source=None): +def parse_source(f, opt, framepointer, input_enc, output_enc, out_dependencies, print_source=None): if opt in ['O2', 'O1']: if framepointer: min_instr_count = 6 @@ -741,7 +755,9 @@ def parse_source(f, opt, framepointer, input_enc, output_enc, print_source=None) global_asm = None asm_functions = [] - output_lines = [] + output_lines = [ + '#line 1 "' + f.name + '"' + ] is_cutscene_data = False @@ -770,6 +786,7 @@ def parse_source(f, opt, framepointer, input_enc, output_enc, print_source=None) elif ((line.startswith('GLOBAL_ASM("') or line.startswith('#pragma GLOBAL_ASM("')) and line.endswith('")')): fname = line[line.index('(') + 2 : -2] + out_dependencies.append(fname) global_asm = GlobalAsmBlock(fname) with open(fname, encoding=input_enc) as f: for line2 in f: @@ -778,16 +795,17 @@ def parse_source(f, opt, framepointer, input_enc, output_enc, print_source=None) output_lines[-1] = ''.join(src) asm_functions.append(fn) global_asm = None - elif ((line.startswith('#include "')) and line.endswith('" EARLY')): + elif line.startswith('#include "') and line.endswith('" EARLY'): # C includes qualified with EARLY (i.e. #include "file.c" EARLY) will be # processed recursively when encountered fpath = os.path.dirname(f.name) - fname = line[line.index(' ') + 2 : -7] + fname = os.path.join(fpath, line[line.index(' ') + 2 : -7]) + out_dependencies.append(fname) include_src = StringIO() - with open(fpath + os.path.sep + fname, encoding=input_enc) as include_file: - parse_source(include_file, opt, framepointer, input_enc, output_enc, include_src) + with open(fname, encoding=input_enc) as include_file: + parse_source(include_file, opt, framepointer, input_enc, output_enc, out_dependencies, include_src) + include_src.write('#line ' + str(line_no + 1) + ' "' + f.name + '"') output_lines[-1] = include_src.getvalue() - include_src.write('#line ' + str(line_no) + '\n') include_src.close() else: # This is a hack to replace all floating-point numbers in an array of a particular type @@ -842,6 +860,7 @@ def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc): # simplicity we pad with nops/.space so that addresses match exactly, so we # don't have to fix up relocations/symbol references. all_text_glabels = set() + func_sizes = {} for function in functions: ifdefed = False for sectype, (temp_name, size) in function.data.items(): @@ -864,6 +883,8 @@ def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc): else: asm.append('.space {}'.format(loc - prev_loc)) to_copy[sectype].append((loc, size, temp_name, function.fn_desc)) + if function.text_glabels: + func_sizes[function.text_glabels[0]] = size prev_locs[sectype] = loc + size if not ifdefed: all_text_glabels.update(function.text_glabels) @@ -907,6 +928,7 @@ def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc): asm_objfile = ElfFile(f.read()) # Remove some clutter from objdump output + mdebug_section = objfile.find_section('.mdebug') objfile.drop_irrelevant_sections() # Unify reginfo sections @@ -1008,7 +1030,7 @@ def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc): # Move over symbols, deleting the temporary function labels. # Sometimes this naive procedure results in duplicate symbols, or UNDEF # symbols that are also defined the same .o file. Hopefully that's fine. - # Skip over local symbols that aren't used relocated against, to avoid + # Skip over local symbols that aren't relocated against, to avoid # conflicts. new_local_syms = [s for s in objfile.symtab.local_symbols() if not is_temp_name(s.name)] new_global_syms = [s for s in objfile.symtab.global_symbols() if not is_temp_name(s.name)] @@ -1026,6 +1048,8 @@ def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc): # glabel's aren't marked as functions, making objdump output confusing. Fix that. if s.name in all_text_glabels: s.type = STT_FUNC + if s.name in func_sizes: + s.st_size = func_sizes[s.name] if objfile.sections[s.st_shndx].name == '.rodata' and s.st_value in moved_late_rodata: s.st_value = moved_late_rodata[s.st_value] s.st_name += strtab_adj @@ -1033,6 +1057,57 @@ def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc): new_local_syms.append(s) else: new_global_syms.append(s) + + # Add static symbols from .mdebug, so they can be referred to from GLOBAL_ASM + local_sym_replacements = {} + if mdebug_section: + strtab_index = len(objfile.symtab.strtab.data) + new_strtab_data = [] + ifd_max, cb_fd_offset = struct.unpack('>II', mdebug_section.data[18*4 : 20*4]) + cb_sym_offset, = struct.unpack('>I', mdebug_section.data[9*4 : 10*4]) + cb_ss_offset, = struct.unpack('>I', mdebug_section.data[15*4 : 16*4]) + for i in range(ifd_max): + offset = cb_fd_offset + 18*4*i + iss_base, _, isym_base, csym = struct.unpack('>IIII', objfile.data[offset + 2*4 : offset + 6*4]) + for j in range(csym): + offset2 = cb_sym_offset + 12 * (isym_base + j) + iss, value, st_sc_index = struct.unpack('>III', objfile.data[offset2 : offset2 + 12]) + st = (st_sc_index >> 26) + sc = (st_sc_index >> 21) & 0x1f + if st in [MIPS_DEBUG_ST_STATIC, MIPS_DEBUG_ST_STATIC_PROC]: + symbol_name_offset = cb_ss_offset + iss_base + iss + symbol_name_offset_end = objfile.data.find(b'\0', symbol_name_offset) + assert symbol_name_offset_end != -1 + symbol_name = objfile.data[symbol_name_offset : symbol_name_offset_end + 1] + symbol_name_str = symbol_name[:-1].decode('latin1') + section_name = {1: '.text', 2: '.data', 3: '.bss', 15: '.rodata'}[sc] + section = objfile.find_section(section_name) + symtype = STT_FUNC if sc == 1 else STT_OBJECT + sym = Symbol.from_parts( + st_name=strtab_index, + st_value=value, + st_size=0, + st_info=(STB_LOCAL << 4 | symtype), + st_other=STV_DEFAULT, + st_shndx=section.index, + strtab=objfile.symtab.strtab, + name=symbol_name_str) + local_sym_replacements[symbol_name_str] = len(new_local_syms) + strtab_index += len(symbol_name) + new_strtab_data.append(symbol_name) + new_local_syms.append(sym) + objfile.symtab.strtab.data += b''.join(new_strtab_data) + + # To get the linker to use the local symbols, we have to get rid of UNDEF + # global ones. + newer_global_syms = [] + for s in new_global_syms: + if s.st_shndx == SHN_UNDEF and s.name in local_sym_replacements: + s.new_index = local_sym_replacements[s.name] + else: + newer_global_syms.append(s) + new_global_syms = newer_global_syms + new_syms = new_local_syms + new_global_syms for i, s in enumerate(new_syms): s.new_index = i @@ -1095,7 +1170,7 @@ def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc): except: pass -def run_wrapped(argv, outfile): +def run_wrapped(argv, outfile, functions): parser = argparse.ArgumentParser(description="Pre-process .c files and post-process .o files to enable embedding assembly into C.") parser.add_argument('filename', help="path to .c code") parser.add_argument('--post-process', dest='objfile', help="path to .o file to post-process") @@ -1118,12 +1193,15 @@ def run_wrapped(argv, outfile): if args.objfile is None: with open(args.filename, encoding=args.input_enc) as f: - parse_source(f, opt=opt, framepointer=args.framepointer, input_enc=args.input_enc, output_enc=args.output_enc, print_source=outfile) + deps = [] + functions = parse_source(f, opt=opt, framepointer=args.framepointer, input_enc=args.input_enc, output_enc=args.output_enc, out_dependencies=deps, print_source=outfile) + return functions, deps else: if args.assembler is None: raise Failure("must pass assembler command") - with open(args.filename, encoding=args.input_enc) as f: - functions = parse_source(f, opt=opt, framepointer=args.framepointer, input_enc=args.input_enc, output_enc=args.output_enc) + if functions is None: + with open(args.filename, encoding=args.input_enc) as f: + functions = parse_source(f, opt=opt, framepointer=args.framepointer, input_enc=args.input_enc, out_dependencies=[], output_enc=args.output_enc) if not functions: return asm_prelude = b'' @@ -1132,9 +1210,9 @@ def run_wrapped(argv, outfile): asm_prelude = f.read() fixup_objfile(args.objfile, functions, asm_prelude, args.assembler, args.output_enc) -def run(argv, outfile=sys.stdout.buffer): +def run(argv, outfile=sys.stdout.buffer, functions=None): try: - run_wrapped(argv, outfile) + return run_wrapped(argv, outfile, functions) except Failure as e: print("Error:", e, file=sys.stderr) sys.exit(1) diff --git a/tools/asm_processor/compile.sh b/tools/asm_processor/compile.sh new file mode 100644 index 00000000..f125ff89 --- /dev/null +++ b/tools/asm_processor/compile.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e +set -o pipefail +INPUT="$1" +OUTPUT="${INPUT%.c}.o" + +CC="$QEMU_IRIX -silent -L $IRIX_ROOT $IRIX_ROOT/usr/bin/cc" +CFLAGS="-Wab,-r4300_mul -non_shared -G 0 -Xcpluscomm -fullwarn -wlint -woff 819,820,852,821 -signed -DVERSION_JP=1 -mips2" # -I include +AS="mips-linux-gnu-as" +ASFLAGS="-march=vr4300 -mabi=32 --defsym VERSION_JP=1" # -I include +set +e +OPTFLAGS=$(grep '^// COMPILE-FLAGS: ' $INPUT | sed 's#^// COMPILE-FLAGS: ##') +set -e +if [[ -z "$OPTFLAGS" ]]; then + OPTFLAGS="-g" +fi + +python3 asm_processor.py "$OPTFLAGS" "$INPUT" | $CC -c $CFLAGS include-stdin.c -o "$OUTPUT" "$OPTFLAGS" +python3 asm_processor.py "$OPTFLAGS" "$INPUT" --post-process "$OUTPUT" --assembler "$AS $ASFLAGS" --asm-prelude prelude.s diff --git a/tools/asm_processor/include-stdin.c b/tools/asm_processor/include-stdin.c new file mode 100644 index 00000000..c21aa6f6 --- /dev/null +++ b/tools/asm_processor/include-stdin.c @@ -0,0 +1,2 @@ +// (this is used for piping input to the IRIX compiler without needing to make a temporary .c file) +#include "/dev/stdin" diff --git a/tools/asm_processor/prelude.s b/tools/asm_processor/prelude.s new file mode 100644 index 00000000..5118a5c6 --- /dev/null +++ b/tools/asm_processor/prelude.s @@ -0,0 +1,8 @@ +.set noat +.set noreorder +.set gp=64 +.macro glabel label + .global \label + \label: +.endm +