From 75d603b9014dae7ce4bd95e0d6314038f207f29e Mon Sep 17 00:00:00 2001 From: Anghelo Carvajal Date: Thu, 29 Jan 2026 08:13:59 -0300 Subject: [PATCH 1/5] create_config: Avoid emitting relocations for addresses paired by using ori --- CHANGELOG.md | 5 +++++ README.md | 2 +- pyproject.toml | 2 +- src/splat/__init__.py | 2 +- src/splat/scripts/create_config.py | 8 ++++---- src/splat/util/n64/rominfo.py | 23 ++++++++++++++++++++--- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e873fcb3..46acbbb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # splat Release Notes +### 0.37.3 + +* create_config: Avoid emitting relocations for addresses paired by using `ori` + ### 0.37.2 + * Add new option `sort_segments_by_vram_dependency` to help with non-matching builds for binaries with complicated memory layouts. See the wiki for details. * Fix create_config missing bss segments due to unsigned LO instructions. diff --git a/README.md b/README.md index 315943ba..4a8d33d6 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The brackets corresponds to the optional dependencies to install while installin If you use a `requirements.txt` file in your repository, then you can add this library with the following line: ```txt -splat64[mips]>=0.37.2,<1.0.0 +splat64[mips]>=0.37.3,<1.0.0 ``` ### Optional dependencies diff --git a/pyproject.toml b/pyproject.toml index 07b7a43c..9c4d794e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "splat64" # Should be synced with src/splat/__init__.py -version = "0.37.2" +version = "0.37.3" description = "A binary splitting tool to assist with decompilation and modding projects" readme = "README.md" license = {file = "LICENSE"} diff --git a/src/splat/__init__.py b/src/splat/__init__.py index 87fbd9cb..301b77a9 100644 --- a/src/splat/__init__.py +++ b/src/splat/__init__.py @@ -1,7 +1,7 @@ __package_name__ = __name__ # Should be synced with pyproject.toml -__version__ = "0.37.2" +__version__ = "0.37.3" __author__ = "ethteck" from . import util as util diff --git a/src/splat/scripts/create_config.py b/src/splat/scripts/create_config.py index 11425f7a..9b9bff8d 100644 --- a/src/splat/scripts/create_config.py +++ b/src/splat/scripts/create_config.py @@ -194,7 +194,7 @@ def create_n64_config(rom_path: Path): # Write reloc_addrs.txt file reloc_addrs = [] - if rom.entrypoint_info.bss_start_address is not None: + if rom.entrypoint_info.bss_start_address is not None and not rom.entrypoint_info.bss_start_address.ori: reloc_addrs.append( f"rom:0x{rom.entrypoint_info.bss_start_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_START" ) @@ -202,7 +202,7 @@ def create_n64_config(rom_path: Path): f"rom:0x{rom.entrypoint_info.bss_start_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_START" ) reloc_addrs.append("") - if rom.entrypoint_info.bss_size is not None: + if rom.entrypoint_info.bss_size is not None and not rom.entrypoint_info.bss_size.ori: reloc_addrs.append( f"rom:0x{rom.entrypoint_info.bss_size.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_SIZE" ) @@ -210,7 +210,7 @@ def create_n64_config(rom_path: Path): f"rom:0x{rom.entrypoint_info.bss_size.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_SIZE" ) reloc_addrs.append("") - if rom.entrypoint_info.bss_end_address is not None: + if rom.entrypoint_info.bss_end_address is not None and not rom.entrypoint_info.bss_end_address.ori: reloc_addrs.append( f"rom:0x{rom.entrypoint_info.bss_end_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_END" ) @@ -218,7 +218,7 @@ def create_n64_config(rom_path: Path): f"rom:0x{rom.entrypoint_info.bss_end_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_END" ) reloc_addrs.append("") - if rom.entrypoint_info.stack_top is not None: + if rom.entrypoint_info.stack_top is not None and not rom.entrypoint_info.stack_top.ori: reloc_addrs.append( '// This entry corresponds to the "stack top", which is the end of the array used as the stack for the main segment.' ) diff --git a/src/splat/util/n64/rominfo.py b/src/splat/util/n64/rominfo.py index 04b6c07e..511d714a 100755 --- a/src/splat/util/n64/rominfo.py +++ b/src/splat/util/n64/rominfo.py @@ -74,13 +74,14 @@ class EntryAddressInfo: value: int rom_hi: int rom_lo: int + ori: bool @staticmethod def new( - value: Optional[int], hi: Optional[int], lo: Optional[int] + value: Optional[int], hi: Optional[int], lo: Optional[int], ori: bool ) -> Optional["EntryAddressInfo"]: if value is not None and hi is not None and lo is not None: - return EntryAddressInfo(value, hi, lo) + return EntryAddressInfo(value, hi, lo, ori) return None @@ -94,6 +95,7 @@ class N64EntrypointInfo: main_address: Optional[EntryAddressInfo] stack_top: Optional[EntryAddressInfo] traditional_entrypoint: bool + ori_entrypoint: bool def segment_size(self) -> int: if self.data_size is not None: @@ -120,6 +122,10 @@ def parse_rom_bytes( completed_pair = [False for _ in range(32)] hi_assignments: List[Optional[int]] = [None for _ in range(32)] lo_assignments: List[Optional[int]] = [None for _ in range(32)] + # We need to track if something was paired using an ori instead of an + # addiu or similar, because if that's the case we can't emit normal + # relocations in the generated symbol_addrs file for it. + ori_assignments: List[bool] = [False for _ in range(32)] register_bss_address: Optional[int] = None register_bss_size: Optional[int] = None @@ -130,6 +136,7 @@ def parse_rom_bytes( bss_end_address: Optional[EntryAddressInfo] = None traditional_entrypoint = True + ori_entrypoint = False decrementing_bss_routine = True data_size: Optional[int] = None func_call_target: Optional[EntryAddressInfo] = None @@ -163,6 +170,9 @@ def parse_rom_bytes( ) completed_pair[insn.rt.value] = True lo_assignments[insn.rt.value] = current_rom + if insn.isUnsigned(): + ori_assignments[insn.rt.value] = True + ori_entrypoint = True elif insn.doesStore(): if insn.rt == rabbitizer.RegGprO32.zero: # Try to detect the zero-ing bss algorithm @@ -208,11 +218,13 @@ def parse_rom_bytes( register_values[insn.rs.value], hi_assignments[insn.rs.value], lo_assignments[insn.rs.value], + ori_assignments[insn.rs.value], ) bss_end_address = EntryAddressInfo.new( register_values[insn.rt.value], hi_assignments[insn.rt.value], lo_assignments[insn.rt.value], + ori_assignments[insn.rt.value], ) elif insn.isFunctionCall(): @@ -221,7 +233,7 @@ def parse_rom_bytes( # entrypoint to actual code. traditional_entrypoint = False func_call_target = EntryAddressInfo( - insn.getInstrIndexAsVram(), current_rom, current_rom + insn.getInstrIndexAsVram(), current_rom, current_rom, False ) elif insn.uniqueId == rabbitizer.InstrId.cpu_break: @@ -254,12 +266,14 @@ def parse_rom_bytes( register_values[register_bss_address], hi_assignments[register_bss_address], lo_assignments[register_bss_address], + ori_assignments[register_bss_address], ) if register_bss_size is not None: bss_size = EntryAddressInfo.new( register_values[register_bss_size], hi_assignments[register_bss_size], lo_assignments[register_bss_size], + ori_assignments[register_bss_size], ) if register_main_address is not None: @@ -267,6 +281,7 @@ def parse_rom_bytes( register_values[register_main_address], hi_assignments[register_main_address], lo_assignments[register_main_address], + ori_assignments[register_main_address], ) else: main_address = None @@ -275,6 +290,7 @@ def parse_rom_bytes( register_values[rabbitizer.RegGprO32.sp.value], hi_assignments[rabbitizer.RegGprO32.sp.value], lo_assignments[rabbitizer.RegGprO32.sp.value], + ori_assignments[rabbitizer.RegGprO32.sp.value], ) if not traditional_entrypoint: @@ -299,6 +315,7 @@ def parse_rom_bytes( main_address, stack_top, traditional_entrypoint, + ori_entrypoint, ) From 938eebca64e6f096fcfe1afd8ebfaf5c93e1352a Mon Sep 17 00:00:00 2001 From: Anghelo Carvajal Date: Thu, 29 Jan 2026 08:48:15 -0300 Subject: [PATCH 2/5] Update tests --- src/splat/scripts/create_config.py | 33 +- src/splat/util/n64/rominfo.py | 12 +- test_n64_entrypoints.py | 521 +++++++++++++++++++++++++---- 3 files changed, 495 insertions(+), 71 deletions(-) diff --git a/src/splat/scripts/create_config.py b/src/splat/scripts/create_config.py index 9b9bff8d..3ac4df57 100644 --- a/src/splat/scripts/create_config.py +++ b/src/splat/scripts/create_config.py @@ -194,7 +194,23 @@ def create_n64_config(rom_path: Path): # Write reloc_addrs.txt file reloc_addrs = [] - if rom.entrypoint_info.bss_start_address is not None and not rom.entrypoint_info.bss_start_address.ori: + if ( + rom.entrypoint_info.main_address is not None + and not rom.entrypoint_info.main_address.ori + and rom.entrypoint_info.main_address.rom_hi + != rom.entrypoint_info.main_address.rom_lo + ): + reloc_addrs.append( + f"rom:0x{rom.entrypoint_info.main_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main" + ) + reloc_addrs.append( + f"rom:0x{rom.entrypoint_info.main_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main" + ) + reloc_addrs.append("") + if ( + rom.entrypoint_info.bss_start_address is not None + and not rom.entrypoint_info.bss_start_address.ori + ): reloc_addrs.append( f"rom:0x{rom.entrypoint_info.bss_start_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_START" ) @@ -202,7 +218,10 @@ def create_n64_config(rom_path: Path): f"rom:0x{rom.entrypoint_info.bss_start_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_START" ) reloc_addrs.append("") - if rom.entrypoint_info.bss_size is not None and not rom.entrypoint_info.bss_size.ori: + if ( + rom.entrypoint_info.bss_size is not None + and not rom.entrypoint_info.bss_size.ori + ): reloc_addrs.append( f"rom:0x{rom.entrypoint_info.bss_size.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_SIZE" ) @@ -210,7 +229,10 @@ def create_n64_config(rom_path: Path): f"rom:0x{rom.entrypoint_info.bss_size.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_SIZE" ) reloc_addrs.append("") - if rom.entrypoint_info.bss_end_address is not None and not rom.entrypoint_info.bss_end_address.ori: + if ( + rom.entrypoint_info.bss_end_address is not None + and not rom.entrypoint_info.bss_end_address.ori + ): reloc_addrs.append( f"rom:0x{rom.entrypoint_info.bss_end_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_END" ) @@ -218,7 +240,10 @@ def create_n64_config(rom_path: Path): f"rom:0x{rom.entrypoint_info.bss_end_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_END" ) reloc_addrs.append("") - if rom.entrypoint_info.stack_top is not None and not rom.entrypoint_info.stack_top.ori: + if ( + rom.entrypoint_info.stack_top is not None + and not rom.entrypoint_info.stack_top.ori + ): reloc_addrs.append( '// This entry corresponds to the "stack top", which is the end of the array used as the stack for the main segment.' ) diff --git a/src/splat/util/n64/rominfo.py b/src/splat/util/n64/rominfo.py index 511d714a..772c273d 100755 --- a/src/splat/util/n64/rominfo.py +++ b/src/splat/util/n64/rominfo.py @@ -78,10 +78,10 @@ class EntryAddressInfo: @staticmethod def new( - value: Optional[int], hi: Optional[int], lo: Optional[int], ori: bool + value: Optional[int], hi: Optional[int], lo: Optional[int], ori: Optional[int] ) -> Optional["EntryAddressInfo"]: if value is not None and hi is not None and lo is not None: - return EntryAddressInfo(value, hi, lo, ori) + return EntryAddressInfo(value, hi, lo, ori == lo) return None @@ -125,7 +125,7 @@ def parse_rom_bytes( # We need to track if something was paired using an ori instead of an # addiu or similar, because if that's the case we can't emit normal # relocations in the generated symbol_addrs file for it. - ori_assignments: List[bool] = [False for _ in range(32)] + ori_assignments: List[Optional[int]] = [None for _ in range(32)] register_bss_address: Optional[int] = None register_bss_size: Optional[int] = None @@ -171,7 +171,7 @@ def parse_rom_bytes( completed_pair[insn.rt.value] = True lo_assignments[insn.rt.value] = current_rom if insn.isUnsigned(): - ori_assignments[insn.rt.value] = True + ori_assignments[insn.rt.value] = current_rom ori_entrypoint = True elif insn.doesStore(): if insn.rt == rabbitizer.RegGprO32.zero: @@ -281,7 +281,7 @@ def parse_rom_bytes( register_values[register_main_address], hi_assignments[register_main_address], lo_assignments[register_main_address], - ori_assignments[register_main_address], + ori_assignments[register_main_address], ) else: main_address = None @@ -290,7 +290,7 @@ def parse_rom_bytes( register_values[rabbitizer.RegGprO32.sp.value], hi_assignments[rabbitizer.RegGprO32.sp.value], lo_assignments[rabbitizer.RegGprO32.sp.value], - ori_assignments[rabbitizer.RegGprO32.sp.value], + ori_assignments[rabbitizer.RegGprO32.sp.value], ) if not traditional_entrypoint: diff --git a/test_n64_entrypoints.py b/test_n64_entrypoints.py index 4efb5091..c918a4e2 100644 --- a/test_n64_entrypoints.py +++ b/test_n64_entrypoints.py @@ -180,10 +180,10 @@ def test_traditional_a(self): lui(T1, UHI(BSS_SIZE)), addiu(T0, T0, LO(BSS_START)), ori(T1, T1, LO(BSS_SIZE)), - addi(T1, T1, 0xFFF8), + addi(T1, T1, -0x8), sw(ZERO, 0, T0), sw(ZERO, 4, T0), - bnez(T1, 0xFFFC), + bnez(T1, -0x4), addi(T0, T0, 8), lui(T2, HI(MAIN_ADDR)), lui(SP, HI(STACK_TOP)), @@ -192,11 +192,26 @@ def test_traditional_a(self): addiu(SP, SP, LO(STACK_TOP)), ] info = parse(words, vram=0x80246000) + self.assertTrue(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_size is not None self.assertEqual(info.bss_size.value, BSS_SIZE) + self.assertTrue(info.bss_size.ori) + self.assertIsNone(info.bss_end_address) def test_traditional_a_li(self): @@ -207,10 +222,10 @@ def test_traditional_a_li(self): lui(T0, HI(BSS_START)), addiu(T0, T0, LO(BSS_START)), addiu(T1, ZERO, LO(BSS_SIZE)), - addi(T1, T1, 0xFFF8), + addi(T1, T1, -0x8), sw(ZERO, 0, T0), sw(ZERO, 4, T0), - bnez(T1, 0xFFFC), + bnez(T1, -0x4), addi(T0, T0, 8), lui(T2, HI(MAIN_ADDR)), lui(SP, HI(STACK_TOP)), @@ -219,24 +234,36 @@ def test_traditional_a_li(self): addiu(SP, SP, LO(STACK_TOP)), ] info = parse(words) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + self.assertIsNone(info.bss_size) def test_traditional_a_li_ori(self): """bss_size loaded via ori $t1,$zero,imm.""" - BSS_SIZE = BSS_SIZE_SMALL # TODO: parse bss_size from li/ori pattern + BSS_SIZE = BSS_SIZE_SMALL # TODO: parse bss_size from plain ori pattern words = [ lui(T0, HI(BSS_START)), addiu(T0, T0, LO(BSS_START)), ori(T1, ZERO, LO(BSS_SIZE)), - addi(T1, T1, 0xFFF8), + addi(T1, T1, -0x8), sw(ZERO, 0, T0), sw(ZERO, 4, T0), - bnez(T1, 0xFFFC), + bnez(T1, -0x4), addi(T0, T0, 8), lui(T2, HI(MAIN_ADDR)), lui(SP, HI(STACK_TOP)), @@ -245,10 +272,22 @@ def test_traditional_a_li_ori(self): addiu(SP, SP, LO(STACK_TOP)), ] info = parse(words) + self.assertTrue(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + self.assertIsNone(info.bss_size) def test_traditional_b(self): @@ -261,8 +300,8 @@ def test_traditional_b(self): sw(ZERO, 0, T0), sw(ZERO, 4, T0), addi(T0, T0, 8), - addi(T1, T1, 0xFFF8), - bnez(T1, 0xFFFB), + addi(T1, T1, -0x8), + bnez(T1, -0x5), nop(), lui(T2, HI(MAIN_ADDR)), addiu(T2, T2, LO(MAIN_ADDR)), @@ -271,11 +310,25 @@ def test_traditional_b(self): addiu(SP, SP, LO(STACK_TOP)), ] info = parse(words) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_size is not None self.assertEqual(info.bss_size.value, BSS_SIZE) + self.assertFalse(info.bss_size.ori) def test_traditional_b_nop(self): """traditional_b with nop as jr delay slot.""" @@ -287,8 +340,8 @@ def test_traditional_b_nop(self): sw(ZERO, 0, T0), sw(ZERO, 4, T0), addi(T0, T0, 8), - addi(T1, T1, 0xFFF8), - bnez(T1, 0xFFFB), + addi(T1, T1, -0x8), + bnez(T1, -0x5), nop(), lui(T2, HI(MAIN_ADDR)), addiu(T2, T2, LO(MAIN_ADDR)), @@ -298,11 +351,25 @@ def test_traditional_b_nop(self): nop(), ] info = parse(words, vram=0x80000450) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_size is not None self.assertEqual(info.bss_size.value, BSS_SIZE) + self.assertFalse(info.bss_size.ori) def test_traditional_b_ori_sp(self): """traditional_b with ori for $sp lo half.""" @@ -314,8 +381,8 @@ def test_traditional_b_ori_sp(self): sw(ZERO, 0, T0), sw(ZERO, 4, T0), addi(T0, T0, 8), - addi(T1, T1, 0xFFF8), - bnez(T1, 0xFFFB), + addi(T1, T1, -0x8), + bnez(T1, -0x5), nop(), lui(T2, HI(MAIN_ADDR)), addiu(T2, T2, LO(MAIN_ADDR)), @@ -324,11 +391,25 @@ def test_traditional_b_ori_sp(self): ori(SP, SP, LO(STACK_TOP)), ] info = parse(words, vram=0x80300000) + self.assertTrue(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_size is not None self.assertEqual(info.bss_size.value, BSS_SIZE) + self.assertFalse(info.bss_size.ori) def test_traditional_c(self): """lui order swapped (lui/lui/addiu/addiu).""" @@ -340,8 +421,8 @@ def test_traditional_c(self): sw(ZERO, 0, T0), sw(ZERO, 4, T0), addi(T0, T0, 8), - addi(T1, T1, 0xFFF8), - bnez(T1, 0xFFFB), + addi(T1, T1, -0x8), + bnez(T1, -0x5), nop(), lui(T2, HI(MAIN_ADDR)), lui(SP, HI(STACK_TOP)), @@ -351,11 +432,25 @@ def test_traditional_c(self): nop(), ] info = parse(words, vram=0x80180000) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_size is not None self.assertEqual(info.bss_size.value, BSS_SIZE) + self.assertFalse(info.bss_size.ori) def test_traditional_d(self): """$sp set before BSS loop.""" @@ -367,8 +462,8 @@ def test_traditional_d(self): sw(ZERO, 0, T0), sw(ZERO, 4, T0), addi(T0, T0, 8), - addi(T1, T1, 0xFFF8), - bnez(T1, 0xFFFB), + addi(T1, T1, -0x8), + bnez(T1, -0x5), nop(), lui(SP, HI(STACK_TOP)), addiu(SP, SP, LO(STACK_TOP)), @@ -378,11 +473,25 @@ def test_traditional_d(self): nop(), ] info = parse(words, vram=0x80125C00) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_size is not None self.assertEqual(info.bss_size.value, BSS_SIZE) + self.assertFalse(info.bss_size.ori) def test_traditional_d_ori(self): """bss_size uses ori for lo half (lui+ori pair).""" @@ -394,8 +503,8 @@ def test_traditional_d_ori(self): sw(ZERO, 0, T0), sw(ZERO, 4, T0), addi(T0, T0, 8), - addi(T1, T1, 0xFFF8), - bnez(T1, 0xFFFB), + addi(T1, T1, -0x8), + bnez(T1, -0x5), nop(), lui(SP, HI(STACK_TOP)), addiu(SP, SP, LO(STACK_TOP)), @@ -405,11 +514,25 @@ def test_traditional_d_ori(self): nop(), ] info = parse(words) + self.assertTrue(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_size is not None self.assertEqual(info.bss_size.value, BSS_SIZE) + self.assertTrue(info.bss_size.ori) def test_traditional_d_bgtz(self): """uses bgtz instead of bnez for BSS loop.""" @@ -423,8 +546,8 @@ def test_traditional_d_bgtz(self): sw(ZERO, 0, T0), sw(ZERO, 4, T0), addi(T0, T0, 8), - addi(T1, T1, 0xFFF8), - bgtz(T1, 0xFFFB), + addi(T1, T1, -0x8), + bgtz(T1, -0x5), nop(), lui(T2, HI(MAIN_ADDR)), addiu(T2, T2, LO(MAIN_ADDR)), @@ -438,10 +561,22 @@ def test_traditional_d_bgtz(self): nop(), ] info = parse(words, vram=0x80025C00) + self.assertFalse(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + self.assertIsNone(info.bss_size) def test_direct_jump(self): @@ -454,10 +589,20 @@ def test_direct_jump(self): addiu(SP, SP, LO(STACK_TOP)), ] info = parse(words) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + self.assertIsNone(info.bss_start_address) + self.assertIsNone(info.bss_size) @@ -477,19 +622,34 @@ def test_sltu_clear(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), jal(MAIN_ADDR), nop(), break_(), ] info = parse(words, vram=0x80100000) + self.assertFalse(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) + self.assertIsNone(info.bss_size) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) self.assertEqual(info.entry_size, 60) def test_sltu_clear_ori_sp(self): @@ -505,18 +665,33 @@ def test_sltu_clear_ori_sp(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), jal(MAIN_ADDR), nop(), break_(), ] info = parse(words, vram=0x80100400) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + self.assertEqual(info.entry_size, 60) def test_sltu_clear_ori_sp_double(self): @@ -535,8 +710,8 @@ def test_sltu_clear_ori_sp_double(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), lui(T0, HI(BSS2_START)), addiu(T0, T0, LO(BSS2_START)), lui(T1, HI(BSS2_END)), @@ -545,17 +720,31 @@ def test_sltu_clear_ori_sp_double(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), jal(MAIN_ADDR), nop(), ] info = parse(words, vram=0x8004B400) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) def test_sltu_clear_ori_sp_double_gp(self): """sltu with ori $sp, double BSS clear, and $gp setup.""" @@ -570,8 +759,8 @@ def test_sltu_clear_ori_sp_double_gp(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), lui(T0, HI(BSS_START)), addiu(T0, T0, LO(BSS_START)), lui(T1, HI(BSS_START)), @@ -580,19 +769,33 @@ def test_sltu_clear_ori_sp_double_gp(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), lui(GP, 0x0000), addiu(GP, GP, 0x0000), jal(MAIN_ADDR), nop(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) def test_sltu_clear_ori_sp_t2(self): """sltu with ori $sp, TLB setup after loop (no jal).""" @@ -610,8 +813,8 @@ def test_sltu_clear_ori_sp_t2(self): nop(), addiu(T0, T0, 4), sltu(T2, T0, T1), - bnez(T2, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(T2, -0x3), + sw(ZERO, -0x4, T0), addiu(A0, ZERO, TLB_COUNT), mfc0(T0, 10), mtc0(A0, 0), @@ -624,11 +827,23 @@ def test_sltu_clear_ori_sp_t2(self): nop(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + self.assertIsNone(info.main_address) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) def test_sltu_clear_double(self): """sltu with double BSS clear, addiu $sp.""" @@ -643,8 +858,8 @@ def test_sltu_clear_double(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), lui(T0, HI(BSS_START)), addiu(T0, T0, LO(BSS_START)), lui(T1, HI(BSS_START)), @@ -653,17 +868,31 @@ def test_sltu_clear_double(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), jal(MAIN_ADDR), nop(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) def test_sltu_clear_double_gp(self): """sltu with double BSS clear and $gp.""" @@ -678,8 +907,8 @@ def test_sltu_clear_double_gp(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), lui(T0, HI(BSS_START)), addiu(T0, T0, LO(BSS_START)), lui(T1, HI(BSS_START)), @@ -688,19 +917,33 @@ def test_sltu_clear_double_gp(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), lui(GP, 0x0000), addiu(GP, GP, 0x0000), jal(MAIN_ADDR), nop(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) def test_sltu_clear_jal(self): """sltu without beq guard, jal + break.""" @@ -713,18 +956,32 @@ def test_sltu_clear_jal(self): addiu(T1, T1, LO(BSS_END)), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), jal(MAIN_ADDR), nop(), break_(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) self.assertEqual(info.entry_size, 52) def test_sltu_clear_size(self): @@ -743,18 +1000,32 @@ def test_sltu_clear_size(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), jal(MAIN_ADDR), nop(), break_(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) self.assertEqual(info.entry_size, 64) def test_sltu_clear_tlb(self): @@ -773,8 +1044,8 @@ def test_sltu_clear_tlb(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), addiu(A0, ZERO, TLB_COUNT), mfc0(T0, 10), mtc0(A0, 0), @@ -787,11 +1058,23 @@ def test_sltu_clear_tlb(self): nop(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + self.assertIsNone(info.main_address) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) def test_sltu_clear_magic(self): """sltu with magic constant (FACEFACE) store after BSS clear.""" @@ -808,8 +1091,8 @@ def test_sltu_clear_magic(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), lui(T0, HI(BSS_START)), addiu(T0, T0, LO(BSS_START)), lui(AT, UHI(MAGIC)), @@ -821,11 +1104,25 @@ def test_sltu_clear_magic(self): break_(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) self.assertEqual(info.entry_size, 84) @@ -841,9 +1138,18 @@ def test_sn64_jal(self): nop(), ] info = parse(words, vram=0x80200400) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) + self.assertIsNone(info.bss_start_address) def test_sn64_jal_addiu(self): @@ -856,9 +1162,18 @@ def test_sn64_jal_addiu(self): break_(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertFalse(info.stack_top.ori) + self.assertIsNone(info.bss_start_address) self.assertEqual(info.entry_size, 20) @@ -882,9 +1197,15 @@ def test_sn64_tlb(self): nop(), ] info = parse(words) + self.assertTrue(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + self.assertIsNone(info.main_address) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) def test_sn64_tlb_li(self): """SN64 TLB with li for loop counter.""" @@ -906,9 +1227,15 @@ def test_sn64_tlb_li(self): nop(), ] info = parse(words, vram=0x80300000) + self.assertTrue(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + self.assertIsNone(info.main_address) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) class TestSpecialEntrypoints(unittest.TestCase): @@ -931,18 +1258,32 @@ def test_excitebike(self): nop(), addiu(T0, T0, 4), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), - sw(ZERO, 0xFFFC, T0), + bnez(AT, -0x3), + sw(ZERO, -0x4, T0), jal(MAIN_ADDR), nop(), break_(), ] info = parse(words, vram=0x80100400) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) self.assertEqual(info.entry_size, 68) def test_factor5_jump(self): @@ -955,9 +1296,19 @@ def test_factor5_jump(self): ori(SP, SP, LO(STACK_TOP)), ] info = parse(words) + self.assertTrue(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + self.assertIsNone(info.main_address) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) + + self.assertIsNone(info.bss_start_address) + self.assertIsNone(info.bss_size) + self.assertIsNone(info.bss_end_address) def test_factor5_cache(self): """multiple jals, last one overrides main_address.""" @@ -990,9 +1341,17 @@ def test_factor5_cache(self): nop(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) def test_vigilante8(self): """sltu loop + j instruction (not tracked as main).""" @@ -1007,17 +1366,29 @@ def test_vigilante8(self): addiu(V1, V1, LO(BSS_END)), sw(ZERO, 0, V0), sltu(AT, V0, V1), - bnez(AT, 0xFFFD), + bnez(AT, -0x3), addiu(V0, V0, 4), j(MAIN_ADDR), nop(), ] info = parse(words, vram=0x80125800) + self.assertTrue(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + self.assertIsNone(info.main_address) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) + + assert info.bss_start_address is not None self.assertEqual(info.bss_start_address.value, BSS_START) + self.assertFalse(info.bss_start_address.ori) + + assert info.bss_end_address is not None self.assertEqual(info.bss_end_address.value, BSS_END) + self.assertFalse(info.bss_end_address.ori) def test_acclaim_jump(self): """bare j instruction (not recognized by parser).""" @@ -1027,8 +1398,12 @@ def test_acclaim_jump(self): nop(), ] info = parse(words) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + self.assertIsNone(info.main_address) + self.assertIsNone(info.stack_top) def test_army_men(self): @@ -1050,7 +1425,7 @@ def test_army_men(self): addiu(A1, A1, LO(STACK_TOP)), addu(SP, A1, ZERO), addu(FP, A1, ZERO), - addiu(GP, ZERO, 0xFFFF), + addiu(GP, ZERO, -0x1), lui(A0, HI(RANGE1_START)), addiu(A0, A0, LO(RANGE1_START)), lui(A0, HI(RANGE1_END)), @@ -1086,18 +1461,30 @@ def test_army_men(self): nop(), ] info = parse(words) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + self.assertIsNone(info.stack_top) def test_empty_entry(self): """All nops (no meaningful code at entrypoint).""" words = [nop()] * 16 info = parse(words, vram=0x80190000) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + self.assertIsNone(info.main_address) + self.assertIsNone(info.stack_top) + self.assertIsNone(info.bss_start_address) + self.assertIsNone(info.bss_size) def test_cheat_device(self): @@ -1129,22 +1516,22 @@ def test_cheat_device(self): sw(AT, 0, V0), addiu(V1, V1, 4), addiu(V0, V0, 4), - addiu(T0, T0, 0xFFFC), - bgtz(T0, 0xFFF9), + addiu(T0, T0, -0x4), + bgtz(T0, -0x7), nop(), lui(T0, UHI(CACHE1_START)), addiu(T1, T0, CACHE1_MID_OFFSET), addiu(T1, T1, CACHE1_END_ADJUST), cache(1, 0, T0), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), + bnez(AT, -0x3), addiu(T0, T0, CACHE1_LINE), lui(T0, UHI(CACHE2_START)), addiu(T1, T0, CACHE2_MID_OFFSET), addiu(T1, T1, CACHE2_END_ADJUST), cache(0, 0, T0), sltu(AT, T0, T1), - bnez(AT, 0xFFFD), + bnez(AT, -0x3), addiu(T0, T0, CACHE2_LINE), lui(SP, UHI(STACK_TOP)), ori(SP, SP, LO(STACK_TOP)), @@ -1156,9 +1543,17 @@ def test_cheat_device(self): nop(), ] info = parse(words, vram=0x80280000) + self.assertFalse(info.traditional_entrypoint) + self.assertTrue(info.ori_entrypoint) + + assert info.main_address is not None self.assertEqual(info.main_address.value, MAIN_ADDR) + self.assertFalse(info.main_address.ori) + + assert info.stack_top is not None self.assertEqual(info.stack_top.value, STACK_TOP) + self.assertTrue(info.stack_top.ori) def test_cheat_device_bal(self): """bgezal (BAL) + DMA loop.""" @@ -1179,13 +1574,17 @@ def test_cheat_device_bal(self): sw(AT, 0, V0), addiu(V1, V1, 4), addiu(V0, V0, 4), - addiu(T0, T0, 0xFFFC), - bgtz(T0, 0xFFF7), + addiu(T0, T0, -0x4), + bgtz(T0, -0x9), nop(), ] info = parse(words, vram=0x80401000) + self.assertTrue(info.traditional_entrypoint) + self.assertFalse(info.ori_entrypoint) + self.assertIsNone(info.main_address) + self.assertIsNone(info.stack_top) From 50432be00ba6d3beb7d0ed6c9ef2e826049db342 Mon Sep 17 00:00:00 2001 From: Anghelo Carvajal Date: Thu, 29 Jan 2026 08:57:02 -0300 Subject: [PATCH 3/5] Cleanup --- .github/workflows/lint.yml | 20 ++-------- src/splat/scripts/create_config.py | 64 ++++++++++-------------------- 2 files changed, 26 insertions(+), 58 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 05369174..ff3eda3a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -45,14 +45,8 @@ jobs: pip install -r requirements.txt pip install types-PyYAML - - name: ruff split - run: ruff check split.py - - - name: ruff create_config - run: ruff check create_config.py - - - name: ruff test - run: ruff check test.py + - name: ruff + run: ruff check *.py mypy_splat_checks: runs-on: ubuntu-latest @@ -94,11 +88,5 @@ jobs: pip install -r requirements.txt pip install types-PyYAML - - name: mypy split - run: mypy --show-column-numbers --hide-error-context split.py - - - name: mypy create_config - run: mypy --show-column-numbers --hide-error-context create_config.py - - - name: mypy test - run: mypy --show-column-numbers --hide-error-context test.py + - name: mypy + run: mypy --show-column-numbers --hide-error-context *.py diff --git a/src/splat/scripts/create_config.py b/src/splat/scripts/create_config.py index 3ac4df57..a7eb31ad 100644 --- a/src/splat/scripts/create_config.py +++ b/src/splat/scripts/create_config.py @@ -193,53 +193,33 @@ def create_n64_config(rom_path: Path): file_presets.write_all_files() # Write reloc_addrs.txt file - reloc_addrs = [] - if ( - rom.entrypoint_info.main_address is not None - and not rom.entrypoint_info.main_address.ori - and rom.entrypoint_info.main_address.rom_hi - != rom.entrypoint_info.main_address.rom_lo - ): - reloc_addrs.append( - f"rom:0x{rom.entrypoint_info.main_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main" - ) - reloc_addrs.append( - f"rom:0x{rom.entrypoint_info.main_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main" - ) - reloc_addrs.append("") - if ( - rom.entrypoint_info.bss_start_address is not None - and not rom.entrypoint_info.bss_start_address.ori - ): - reloc_addrs.append( - f"rom:0x{rom.entrypoint_info.bss_start_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_START" - ) - reloc_addrs.append( - f"rom:0x{rom.entrypoint_info.bss_start_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_START" - ) - reloc_addrs.append("") - if ( - rom.entrypoint_info.bss_size is not None - and not rom.entrypoint_info.bss_size.ori - ): - reloc_addrs.append( - f"rom:0x{rom.entrypoint_info.bss_size.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_SIZE" - ) - reloc_addrs.append( - f"rom:0x{rom.entrypoint_info.bss_size.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_SIZE" - ) - reloc_addrs.append("") - if ( - rom.entrypoint_info.bss_end_address is not None - and not rom.entrypoint_info.bss_end_address.ori - ): + reloc_addrs: list[str] = [] + + addresses_info: list[tuple[rominfo.EntryAddressInfo | None, str]] = [ + (rom.entrypoint_info.main_address, "main"), + (rom.entrypoint_info.bss_start_address, "main_BSS_START"), + (rom.entrypoint_info.bss_size, "main_BSS_SIZE"), + (rom.entrypoint_info.bss_end_address, "main_BSS_END"), + ] + + for addr_info, sym_name in addresses_info: + if addr_info is None : + continue + if addr_info.ori: + # Avoid emitting relocations for `ori`s since `%lo` doesn't support it. + continue + if addr_info.rom_hi == addr_info.rom_lo: + # hi and lo may be the same for the "main" address, i.e. a direct jal. + continue + reloc_addrs.append( - f"rom:0x{rom.entrypoint_info.bss_end_address.rom_hi:06X} reloc:MIPS_HI16 symbol:main_BSS_END" + f"rom:0x{addr_info.rom_hi:06X} reloc:MIPS_HI16 symbol:{sym_name}" ) reloc_addrs.append( - f"rom:0x{rom.entrypoint_info.bss_end_address.rom_lo:06X} reloc:MIPS_LO16 symbol:main_BSS_END" + f"rom:0x{addr_info.rom_lo:06X} reloc:MIPS_LO16 symbol:{sym_name}" ) reloc_addrs.append("") + if ( rom.entrypoint_info.stack_top is not None and not rom.entrypoint_info.stack_top.ori From cf27a840e8aca321887d4b203936176e29665f07 Mon Sep 17 00:00:00 2001 From: Anghelo Carvajal Date: Thu, 29 Jan 2026 09:08:17 -0300 Subject: [PATCH 4/5] forgor to push --- .github/workflows/lint.yml | 2 +- src/splat/scripts/create_config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ff3eda3a..87e43d4e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -46,7 +46,7 @@ jobs: pip install types-PyYAML - name: ruff - run: ruff check *.py + run: ruff check . mypy_splat_checks: runs-on: ubuntu-latest diff --git a/src/splat/scripts/create_config.py b/src/splat/scripts/create_config.py index a7eb31ad..b6284bdb 100644 --- a/src/splat/scripts/create_config.py +++ b/src/splat/scripts/create_config.py @@ -203,7 +203,7 @@ def create_n64_config(rom_path: Path): ] for addr_info, sym_name in addresses_info: - if addr_info is None : + if addr_info is None: continue if addr_info.ori: # Avoid emitting relocations for `ori`s since `%lo` doesn't support it. From eb3d02b8ffd950a554895b3d35046bf8abc4d601 Mon Sep 17 00:00:00 2001 From: Anghelo Carvajal Date: Thu, 29 Jan 2026 09:14:52 -0300 Subject: [PATCH 5/5] a --- src/splat/scripts/create_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/splat/scripts/create_config.py b/src/splat/scripts/create_config.py index b6284bdb..eed507cd 100644 --- a/src/splat/scripts/create_config.py +++ b/src/splat/scripts/create_config.py @@ -195,7 +195,7 @@ def create_n64_config(rom_path: Path): # Write reloc_addrs.txt file reloc_addrs: list[str] = [] - addresses_info: list[tuple[rominfo.EntryAddressInfo | None, str]] = [ + addresses_info: list[tuple[Optional[rominfo.EntryAddressInfo], str]] = [ (rom.entrypoint_info.main_address, "main"), (rom.entrypoint_info.bss_start_address, "main_BSS_START"), (rom.entrypoint_info.bss_size, "main_BSS_SIZE"),