From dfbfbcc2189cf9961551d3bc3d9711b0bf731056 Mon Sep 17 00:00:00 2001 From: luckytyphlosion <10688458+luckytyphlosion@users.noreply.github.com> Date: Wed, 23 Nov 2022 12:42:52 -0500 Subject: [PATCH] Partial untested work on buildsystem. --- Makefile | 79 ++ asm/arm9.s | 584 ---------- asm/dtcm.s | 0 asm/itcm.s | 0 binutils.mk | 14 + common.mk | 210 ++++ lib/asm/crt0.s | 312 +++++ lib/syscall/Makefile | 32 + lib/syscall/asm/_secure.s | 265 +++++ lib/syscall/asm/macros/function.inc | 24 + main.lsf | 757 ++++++++++++ platform.mk | 38 + rom.rsf | 32 + sub/ichneumon_sub.sbin | Bin 0 -> 161788 bytes sub/ichneumon_sub_defs.sbin | Bin 0 -> 16 bytes tools/asm_processor/.gitattributes | 1 + tools/asm_processor/asm_processor.py | 1265 +++++++++++++++++++++ tools/asm_processor/compile.sh | 30 + tools/fixrom/.gitignore | 1 + tools/fixrom/Makefile | 13 + tools/fixrom/fixrom.c | 190 ++++ tools/gen_fx_consts/.gitignore | 1 + tools/gen_fx_consts/Makefile | 31 + tools/gen_fx_consts/fx.h | 31 + tools/gen_fx_consts/fx_const.csv | 862 ++++++++++++++ tools/gen_fx_consts/fx_data.c | 128 +++ tools/gen_fx_consts/fx_encode.c | 28 + tools/gen_fx_consts/global.h | 27 + tools/gen_fx_consts/main.c | 86 ++ tools/mwasmarm_patcher/.gitignore | 1 + tools/mwasmarm_patcher/Makefile | 14 + tools/mwasmarm_patcher/mwasmarm_patcher.c | 254 +++++ 32 files changed, 4726 insertions(+), 584 deletions(-) create mode 100644 Makefile create mode 100644 asm/dtcm.s create mode 100644 asm/itcm.s create mode 100644 binutils.mk create mode 100644 common.mk create mode 100644 lib/asm/crt0.s create mode 100644 lib/syscall/Makefile create mode 100644 lib/syscall/asm/_secure.s create mode 100644 lib/syscall/asm/macros/function.inc create mode 100644 main.lsf create mode 100644 platform.mk create mode 100644 rom.rsf create mode 100644 sub/ichneumon_sub.sbin create mode 100644 sub/ichneumon_sub_defs.sbin create mode 100644 tools/asm_processor/.gitattributes create mode 100644 tools/asm_processor/asm_processor.py create mode 100644 tools/asm_processor/compile.sh create mode 100644 tools/fixrom/.gitignore create mode 100644 tools/fixrom/Makefile create mode 100644 tools/fixrom/fixrom.c create mode 100644 tools/gen_fx_consts/.gitignore create mode 100644 tools/gen_fx_consts/Makefile create mode 100644 tools/gen_fx_consts/fx.h create mode 100644 tools/gen_fx_consts/fx_const.csv create mode 100644 tools/gen_fx_consts/fx_data.c create mode 100644 tools/gen_fx_consts/fx_encode.c create mode 100644 tools/gen_fx_consts/global.h create mode 100644 tools/gen_fx_consts/main.c create mode 100644 tools/mwasmarm_patcher/.gitignore create mode 100644 tools/mwasmarm_patcher/Makefile create mode 100644 tools/mwasmarm_patcher/mwasmarm_patcher.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..02b3155a2e --- /dev/null +++ b/Makefile @@ -0,0 +1,79 @@ +MWCCVER := 2.0/sp2p2 +PROC := arm946e +PROC_S := arm5te +PROC_LD := v5te +LCF_TEMPLATE := ARM9-TS.lcf.template +LIBS := -Llib -lsyscall -nostdlib +OPTFLAGS := -O4,p + +# Config +BUILD_DIR := build/platinum +NEFNAME := main +buildname := platinum +TITLE_NAME := POKEMON PL +GAME_CODE := CPUE +SECURE_CRC := 0xF8B8 +GF_DEFINES := +GLB_DEFINES := -DSDK_ARM9 -DSDK_CODE_ARM +DEFINES = $(GF_DEFINES) $(GLB_DEFINES) + +ALL_BUILDDIRS := $(BUILD_DIR)/lib +include common.mk + +ROM := $(BUILD_DIR)/poke$(buildname).nds +BANNER := $(ROM:%.nds=%.bnr) +BANNER_SPEC := $(buildname)/banner.bsf +ICON_PNG := $(buildname)/icon.png +HEADER_TEMPLATE := $(buildname)/rom_header_template.sbin + +.PHONY: main libsyscall sdk sdk9 sdk7 +.PRECIOUS: $(ROM) + +MAKEFLAGS += --no-print-directory + +all: $(ROM) + +tidy: + $(RM) -r $(BUILD_DIR) + $(RM) -r $(PROJECT_CLEAN_TARGETS) + $(RM) $(ROM) + +clean: tidy clean-tools + @$(MAKE) -C lib/syscall clean + +sdk9 sdk7: sdk +main filesystem: | sdk9 + +main: $(SBIN) $(ELF) + +ROMSPEC := rom.rsf +MAKEROM_FLAGS := $(DEFINES) + +$(NEF): libsyscall + +libsyscall: + $(MAKE) -C lib/syscall all install INSTALL_PREFIX=$(abspath $(WORK_DIR)/$(BUILD_DIR)) GAME_CODE=$(GAME_CODE) + +$(BUILD_DIR)/component.files: main ; + +$(HEADER_TEMPLATE): ; + +$(ROM): $(ROMSPEC) filesystem main_lz $(BANNER) + $(WINE) $(MAKEROM) $(MAKEROM_FLAGS) -DBUILD_DIR=$(BUILD_DIR) -DNITROFS_FILES="$(NITROFS_FILES:files/%=%)" -DTITLE_NAME="$(TITLE_NAME)" -DBNR="$(BANNER)" -DHEADER_TEMPLATE="$(HEADER_TEMPLATE)" $< $@ + $(FIXROM) $@ --secure-crc $(SECURE_CRC) --game-code $(GAME_CODE) +ifeq ($(COMPARE),1) + $(SHA1SUM) -c $(buildname)/rom.sha1 +endif + +$(BANNER): $(BANNER_SPEC) $(ICON_PNG:%.png=%.nbfp) $(ICON_PNG:%.png=%.nbfc) + $(WINE) $(MAKEBNR) $< $@ + +# TODO: move to NitroSDK makefile +FX_CONST_H := $(WORK_DIR)/lib/include/nitro/fx/fx_const.h +PROJECT_CLEAN_TARGETS += $(FX_CONST_H) +$(FX_CONST_H): $(TOOLSDIR)/gen_fx_consts/fx_const.csv + $(MKFXCONST) $@ +sdk: $(FX_CONST_H) +$(WORK_DIR)/include/global.h: $(FX_CONST_H) ; + +.PHONY: diff --git a/asm/arm9.s b/asm/arm9.s index fb915acb8f..dbe48bd339 100644 --- a/asm/arm9.s +++ b/asm/arm9.s @@ -1,591 +1,7 @@ .include "macros/function.inc" - .text - arm_func_start sub_02000000 -sub_02000000: ; 0x02000000 - udf #0xfdef - udf #0xfdef - udf #0xfdef - mrchi p14, #4, sp, c2, c15, #7 - ldrtgt ip, [r8], #-0xb26 - sbfxls fp, pc, #0x19, #4 - stclhi p4, c10, [r6, #0x150]! - orrlo r0, r5, #0x7c00000 - arm_func_end sub_02000000 -_02000020: - .byte 0xB7, 0x48, 0xEC, 0xF8, 0x19, 0xE8, 0x5C, 0x73, 0x66, 0x43, 0x66, 0xC4, 0x63, 0x36, 0x44, 0x36 - .byte 0x24, 0x40, 0x84, 0xCC, 0xDD, 0x94, 0x08, 0xA2, 0x8A, 0x12, 0xE2, 0x43, 0xD5, 0xF4, 0x95, 0x8C - .byte 0xA8, 0x64, 0xCD, 0xDB, 0xA7, 0x11, 0xD1, 0x9A, 0xD9, 0x2E, 0x63, 0x3F, 0xA0, 0xFF, 0xB2, 0x5C - .byte 0xAD, 0xFB, 0xBC, 0x93, 0x5F, 0xD0, 0x8B, 0x60, 0xEB, 0x1B, 0xE3, 0x46, 0x69, 0xBB, 0xED, 0x62 - .byte 0x17, 0xD5, 0x67, 0x98, 0xBB, 0xD2, 0x0B, 0xF9, 0x61, 0x5A, 0x71, 0xB4, 0xCD, 0xC7, 0xE4, 0x78 - .byte 0x36, 0x22, 0x91, 0x9A, 0xE6, 0x82, 0xB0, 0xD5, 0x63, 0x0E - - non_word_aligned_thumb_func_start sub_0200007A -sub_0200007A: ; 0x0200007A - svc #0xd - bx lr - thumb_func_end sub_0200007A -_0200007E: - .byte 0x7F, 0x2D - .byte 0x47, 0xDA, 0xA7, 0xEC, 0xAB, 0xA8, 0xE3, 0xD7, 0x4E, 0xBD, 0x2C, 0x3B, 0xE9, 0x58, 0x1E, 0x93 - .byte 0x0B, 0x9D, 0x95, 0x92, 0x78, 0x17, 0x5A, 0xC5, 0x64, 0xB0, 0xA7, 0xC3, 0xE3, 0xBA, 0x71, 0xC9 - .byte 0xE4, 0xDA, 0x75, 0x3A, 0x80, 0xDB, 0xE7, 0xFB, 0x8F, 0xDF, 0x76, 0x0C, 0xA8, 0x78, 0xD8, 0xC6 - .byte 0xA4, 0xA1, 0x20, 0x62, 0xB9, 0x0C, 0xEA, 0x82, 0x20, 0x99, 0x7F, 0x1E, 0xCB, 0xF2, 0x77, 0x39 - .byte 0x4C, 0xD0, 0xFC, 0x57, 0xF6, 0xB2, 0x78, 0x51, 0x82, 0x9C, 0x9D, 0xE7, 0x70, 0xD8, 0x17, 0x6A - .byte 0x25, 0xE9, 0x22, 0xAF, 0x05, 0xAB, 0xEE, 0x06, 0xB6, 0x34, 0xBC, 0x40, 0xDC, 0x05, 0x82, 0x21 - - thumb_func_start sub_020000E0 -sub_020000E0: ; 0x020000E0 - svc #0x12 - bx lr - thumb_func_end sub_020000E0 -_020000E4: - .byte 0x7F, 0xF0, 0x5D, 0x95, 0x2F, 0x8F, 0x87, 0xC6, 0x6F, 0x88, 0x36, 0x73 - .byte 0xC7, 0x57, 0x82, 0x11, 0x0D, 0x58, 0x3E, 0x8C, 0x0D, 0xE1, 0xE9, 0xD8, 0x13, 0x49, 0x97, 0x11 - .byte 0xB0, 0x7E, 0x30, 0x42, 0x1D, 0x23, 0x96, 0x72, 0xCE, 0x3B, 0x65, 0x29, 0xBD, 0x19, 0xAF, 0x06 - .byte 0xC0, 0x52, 0x70, 0xDA, 0xF0, 0xBB, 0x61, 0x07, 0x69, 0x6B, 0xAB, 0x01, 0xCE, 0x09, 0x28, 0x15 - .byte 0x13, 0x96, 0xEF, 0xAD, 0xB4, 0xDC, 0xCD, 0x91, 0xF5, 0x33, 0x76, 0x1E, 0x0D, 0x8F, 0xBC, 0x1A - .byte 0x26, 0x4C, 0x03, 0x81, 0x2F, 0xE1, 0x2A, 0x69, 0xC9, 0x60, 0xEC, 0xDC, 0x69, 0x44, 0xCE, 0x63 - .byte 0xC8, 0x8A, 0x7C, 0xDF, 0x28, 0x33, 0x06, 0x72, 0x3D, 0xF1, 0x5C, 0xC8, 0xA7, 0x43, 0x83, 0xA3 - .byte 0x88, 0x4E, 0xAE, 0x16, 0x3B, 0xBE, 0x54, 0x24, 0x6A, 0x7B, 0x2C, 0x20, 0xFD, 0xB8, 0xAA, 0xE0 - - thumb_func_start sub_02000160 -sub_02000160: ; 0x02000160 - svc #0xe - bx lr - thumb_func_end sub_02000160 -_02000164: - .byte 0x98, 0xE1, 0xD6, 0x85, 0xD9, 0xD9, 0x57, 0x08, 0xE5, 0x99, 0x06, 0x63 - .byte 0x8C, 0xE6, 0x69, 0xDB, 0x02, 0x7B, 0x75, 0xCA, 0xD1, 0xFE, 0x42, 0x50, 0x23, 0x7B, 0x3E, 0x1B - .byte 0xC6, 0xC4, 0x07, 0x81, 0x83, 0x53, 0x07, 0x39, 0x0B, 0xBB, 0xBC, 0x86, 0x67, 0x9A, 0x30, 0x8F - .byte 0x6B, 0x82, 0xAE, 0x00, 0x22, 0xAE, 0xFA, 0x60, 0xDA, 0x7D, 0x6A, 0x20, 0xB5, 0x60, 0xAC, 0xC5 - .byte 0xD6, 0x4B, 0x91, 0x8E, 0xA1, 0x87, 0x91, 0x2D, 0x7D, 0x20, 0x62, 0x01, 0x93, 0xAE, 0x1E, 0x10 - .byte 0xEE, 0xF1, 0x85, 0x35, 0x54, 0x82, 0x84, 0xB7, 0x67, 0xDF, 0x57, 0x04, 0xDD, 0x59, 0xAC, 0xF4 - .byte 0xC8, 0x42, 0x01, 0xDE - - thumb_func_start sub_020001C4 -sub_020001C4: ; 0x020001C4 - svc #9 - bx lr - thumb_func_end sub_020001C4 -_020001C8: - .byte 0xF8, 0x4B, 0xB2, 0xDA, 0xE3, 0xE8, 0xA0, 0x05 - .byte 0x8F, 0x8D, 0x10, 0x77, 0x7E, 0x0D, 0x52, 0xE4, 0x66, 0x59, 0x8D, 0x91, 0x35, 0x57, 0x20, 0xA7 - .byte 0xBB, 0x6E, 0x35, 0xF9, 0x73, 0x0A, 0x73, 0xFD, 0x3A, 0x2D, 0x68, 0x12, 0x9C, 0xFC, 0x46, 0x4A - .byte 0x78, 0x42, 0x42, 0x90, 0xA9, 0xF8, 0x92, 0x35, 0x48, 0x77, 0x89, 0x6E, 0x0A, 0x80, 0xB0, 0xD8 - .byte 0xDE, 0x38, 0xE8, 0xF9, 0xBC, 0xF2, 0x73, 0xCC, 0x00, 0xAB, 0xC9, 0x68, 0x6E, 0x67, 0x87, 0xD0 - .byte 0x16, 0xE8, 0x5C, 0xCC, 0x1C, 0x59, 0xBA, 0xEC, 0xA8, 0xBB, 0x67, 0x76, 0x21, 0xD1, 0xF9, 0xB6 - .byte 0xA3, 0xFC, 0xA3, 0xB0, 0xF3, 0xAC, 0x94, 0xEF, 0x8B, 0x87, 0x7D, 0xA3, 0x50, 0x31, 0x6A, 0xE1 - .byte 0x96, 0xFB, 0xF7, 0x2B, 0xC1, 0x43, 0xFC, 0xAF, 0xD4, 0xD8, 0x78, 0x96, 0x25, 0x1F, 0x2E, 0x12 - .byte 0x73, 0x77, 0x20, 0x94, 0x4C, 0xCA - - non_word_aligned_thumb_func_start sub_02000246 -sub_02000246: ; 0x02000246 - mov r2, #0 - svc #5 - bx lr - thumb_func_end sub_02000246 -_0200024C: - .byte 0xF2, 0xF2, 0x65, 0x7C - .byte 0x41, 0x43, 0x9D, 0xA1, 0x09, 0xE9, 0xD5, 0xAB, 0x6E, 0x1F, 0x30, 0x19, 0xFA, 0xCD, 0x4A, 0x8D - .byte 0x42, 0x80, 0x1F, 0x8C, 0x81, 0x09, 0x0C, 0x0C, 0x2C, 0x91, 0x3F, 0x2B, 0x98, 0x70, 0xE8, 0x0D - .byte 0x8F, 0x82, 0x82, 0x8E, 0x6A, 0x02, 0x64, 0x7D, 0xCC, 0xD2, 0xC6, 0x08, 0xC8, 0xE8, 0x9E, 0xB5 - .byte 0x08, 0x9F, 0xDF, 0xD8, 0x1A, 0x48, 0xF6, 0xA4, 0x11, 0xE0, 0xD6, 0xE1, 0xF0, 0xD5, 0x66, 0xF4 - .byte 0xFC, 0x3B, 0x69, 0x83, 0x3E, 0x63, 0x14, 0xE9, 0x54, 0xB5, 0x2F, 0xC0, 0xB9, 0x78, 0x8C, 0x88 - .byte 0x19, 0x8A, 0x09, 0x06, 0xA6, 0x48, 0x30, 0xC6, 0xA3, 0xA7, 0x62, 0xE8, 0xE0, 0x3A, 0x9C, 0xF4 - .byte 0x7E, 0xE0, 0x02, 0xF1, 0x9F, 0x74, 0x4C, 0xC3, 0xE9, 0xA9, 0x1A, 0x0A - - thumb_func_start sub_020002BC -sub_020002BC: ; 0x020002BC - svc #0x11 - bx lr - thumb_func_end sub_020002BC -_020002C0: - .byte 0x84, 0x8D, 0x66, 0x56, 0xAD, 0xC7, 0xD0, 0xDF, 0x6B, 0x85, 0x96, 0x97, 0x9E, 0x86, 0x6F, 0x36 - .byte 0xCB, 0x9F, 0x63, 0x5A, 0x81, 0xCE, 0x0E, 0xEC, 0xD3, 0xD9, 0xFB, 0x68, 0x56, 0x7A, 0xA9, 0x9F - .byte 0xA4, 0x91, 0xA1, 0x3C, 0x82, 0x19, 0x34, 0xC4, 0x6C, 0xD5, 0x7A, 0x49, 0xAD, 0xE6, 0x93, 0x1F - .byte 0xD4, 0x70, 0x8F, 0xD1, 0x45, 0x98, 0xAB, 0x3E, 0x60, 0xB4, 0x24, 0xD2, 0x79, 0x0F, 0x4F, 0x5E - .byte 0xD1, 0x21, 0x42, 0xCE, 0xC5, 0x6C, 0x3A, 0xDE, 0xC3, 0x62, 0xE4, 0x79, 0xCF, 0xA4, 0xF6, 0xE4 - .byte 0x05, 0x1E, 0x9A, 0x64, 0xBC, 0x90 - - non_word_aligned_thumb_func_start sub_02000316 -sub_02000316: ; 0x02000316 - svc #6 - bx lr - thumb_func_end sub_02000316 -_0200031A: - .byte 0xE9, 0x0E, 0xEB, 0xAE, 0xE7, 0x7D - .byte 0xBE, 0x6D, 0x80, 0xF6, 0xFD, 0x65, 0xFF, 0x34, 0x6B, 0x87, 0x74, 0x07, 0xB4, 0x58, 0xC4, 0x20 - .byte 0xF9, 0xAB, 0xF1, 0xBE, 0xC9, 0x37, 0x22, 0x2E, 0xBB, 0xE7, 0xB9, 0x60, 0x75, 0x0B, 0x70, 0xFF - .byte 0xE3, 0x1D, 0x8A, 0x4D, 0x70, 0x72, 0xA3, 0x07, 0x18, 0xE1, 0x4F, 0xC7, 0x13, 0x0C, 0x6F, 0x66 - .byte 0x63, 0x5A, 0x6F, 0x9A, 0x1D, 0xDE, 0x4C, 0xB2, 0x01, 0xA3, 0x00, 0xD2, 0xA0, 0xF3, 0x07, 0xA2 - .byte 0xF1, 0xFF, 0x90, 0xCE, 0x19, 0x2B, 0x2E, 0x99, 0xB3, 0x7B, 0x89, 0x82, 0xA5, 0xB8, 0x27, 0x31 - .byte 0xEF, 0x1D, 0xA5, 0x60, 0x81, 0xF7, 0xF2, 0xDE, 0xE1, 0x48, 0x67, 0xFF, 0xD4, 0xF1, 0xFD, 0xEA - .byte 0x04, 0x08, 0x89, 0x95, 0xE5, 0xA6, 0x4B, 0xA4, 0xB8, 0x36, 0x45, 0x22, 0x06, 0xED, 0xA0, 0xBE - .byte 0x40, 0xA4, 0x14, 0x9C, 0x64, 0x17, 0xF3, 0x81, 0x36, 0x48 - - non_word_aligned_thumb_func_start sub_0200039A -sub_0200039A: ; 0x0200039A - svc #0x14 - bx lr - thumb_func_end sub_0200039A -_0200039E: - .byte 0xCD, 0x88 - .byte 0x7F, 0x75, 0x2F, 0x90, 0x3F, 0x6E, 0x4B, 0xFD, 0xE9, 0xE3, 0x2B, 0xC6, 0xA0, 0xA9, 0xB3, 0xB8 - .byte 0x7A, 0xBD, 0x31, 0x3B, 0x04, 0x00, 0x12, 0x0B, 0xF7, 0x2A, 0x10, 0x6E, 0x5B, 0x20, 0xDE, 0x43 - .byte 0xA6, 0xBC, 0x29, 0x85, 0x94, 0x3D, 0x8F, 0x55, 0x8A, 0xC5, 0x44, 0xFF, 0x78, 0xBA, 0x7A, 0xA8 - .byte 0x1A, 0x90, 0x51, 0xDD, 0xF0, 0xF7, 0x99, 0x57, 0x42, 0xCA, 0x38, 0x29, 0xEF, 0xA1, 0x1D, 0xE7 - .byte 0xA9, 0x9F, 0xAB, 0xD8, 0x82, 0x41, 0x93, 0xD6, 0x95, 0x50, 0x24, 0xC2, 0x2D, 0xA3, 0x03, 0xDC - .byte 0x18, 0xD9, 0xE2, 0x3C, 0x19, 0x76, 0x9F, 0x35, 0xEE, 0x15, 0x81, 0xB4, 0xD5, 0x98, 0xDA, 0x4B - .byte 0x17, 0x94, 0xC8, 0x7D, 0x77, 0x97, 0x09, 0xDF, 0x08, 0x1C, 0x70, 0x47, 0xC5, 0x28, 0x19, 0xFA - .byte 0x99, 0xF2, 0xD3, 0x66, 0x98, 0xF7, 0xE7, 0x25, 0xA8, 0x9E, 0x04, 0x58, 0xA7, 0x1E, 0x80, 0xD2 - .byte 0x89, 0x8B, 0x8C, 0x9A, 0xDB, 0xEC, 0xC2, 0x64, 0x59, 0xFF, 0xAE, 0xC1, 0xD8, 0x8E, 0x42, 0x24 - .byte 0x3A, 0x72, 0x47, 0xF7, 0x5E, 0x56, 0x27, 0xE8, 0xC9, 0x85, 0x46, 0xAF, 0x2F, 0x42, 0xC4, 0x0C - .byte 0x43, 0xCA, 0x50, 0x37, 0x25, 0xC7, 0x64, 0xE2, 0x9C, 0xD5, 0x27, 0x1D, 0x49, 0xFC, 0xBE, 0xD2 - .byte 0x7B, 0x21, 0x21, 0x65, 0x8D, 0x5E, 0xAC, 0x80, 0xFF, 0xD1, 0x76, 0x5B, 0x3D, 0x64, 0x31, 0x59 - .byte 0x06, 0x03, 0x95, 0x34, 0x13, 0xA7, 0x42, 0xF6, 0x50, 0x0B - - non_word_aligned_thumb_func_start sub_0200046A -sub_0200046A: ; 0x0200046A - svc #0xf - bx lr - thumb_func_end sub_0200046A -_0200046E: - .byte 0x0C, 0x9B - .byte 0xF4, 0xF6, 0x89, 0x7B, 0xD0, 0x91, 0x6B, 0x58, 0x33, 0x76, 0x52, 0xBC, 0xEC, 0x81, 0x89, 0xD2 - .byte 0xFB, 0x4B, 0xE1, 0x85, 0x49, 0x57, 0xCC, 0xD3, 0x6F, 0x12, 0xE1, 0xEB, 0xFC, 0x58, 0x09, 0x81 - .byte 0xBE, 0x65, 0x5F, 0x36, 0xCE, 0x1F, 0xF1, 0x41, 0x0A, 0xB1, 0xA4, 0x65, 0xF5, 0xA0, 0xAA, 0x78 - .byte 0x61, 0x33, 0xBD, 0x6D, 0x27, 0x1A, 0x26, 0x08, 0xD0, 0xF5, 0x3E, 0x39, 0xC1, 0x98, 0x19, 0x58 - .byte 0x8C, 0x14, 0xD1, 0xAE, 0x10, 0xAB, 0x3A, 0xD0, 0x41, 0xF8, 0xCB, 0x40, 0x54, 0x27, 0x4C, 0xEF - .byte 0xD5, 0x54, 0x19, 0xBF - - thumb_func_start sub_020004C4 -sub_020004C4: ; 0x020004C4 - svc #0x15 - bx lr - thumb_func_end sub_020004C4 -_020004C8: - .byte 0x66, 0x02, 0x85, 0x2F, 0xD4, 0xDF, 0x62, 0x16 - .byte 0x62, 0x3B, 0xEE, 0xB9, 0x9E, 0xB9, 0x3C, 0x1D, 0xD2, 0x60, 0x9F, 0x20, 0x5A, 0xF0, 0xA7, 0xA0 - .byte 0xE4, 0xAB, 0x60, 0x4B, 0x4F, 0xA8, 0xE7, 0x07, 0x10, 0x1C, 0x5C, 0x17, 0x07, 0x11, 0xE3, 0xAA - .byte 0x37, 0x07, 0xEB, 0x11, 0x10, 0x34, 0x9D, 0xAE, 0x30, 0xF2, 0x5D, 0xB9, 0x39, 0xA8, 0xA4, 0x22 - .byte 0x12, 0x69, 0x21, 0x85, 0xA1, 0xEC, 0x13, 0xE1, 0xEF, 0xF8, 0xE9, 0xA5, 0xF2, 0xBF, 0xF6, 0x21 - .byte 0x2C, 0x45, 0x80, 0xDF, 0x1C, 0x6D, 0x7C, 0x89, 0xEA, 0xBE, 0xEA, 0x37, 0xBC, 0x88, 0x59, 0x52 - .byte 0x59, 0x93 - - non_word_aligned_thumb_func_start sub_02000522 -sub_02000522: ; 0x02000522 - svc #3 - bx lr - thumb_func_end sub_02000522 -_02000526: - .byte 0x2D, 0x0E, 0xA1, 0x2B, 0x37, 0xE7, 0xA6, 0x05, 0x06, 0xAC - .byte 0x3C, 0x78, 0x3E, 0x0D, 0xC3, 0x9F, 0xC9, 0xCA, 0xB6, 0x80, 0x2D, 0x34, 0xE7, 0x23, 0x07, 0xC7 - .byte 0x14, 0x1E, 0xCC, 0x24, 0xF3, 0xA2, 0x7C, 0x51, 0x4D, 0xE4, 0xB3, 0xCF, 0x0E, 0x5C, 0x0D, 0x43 - .byte 0x61, 0xCC, 0xD6, 0x96, 0x0A, 0xE2, 0xDC, 0xD9, 0x3F, 0x0F, 0x25, 0xE4, 0x4D, 0xE7, 0x95, 0xF7 - .byte 0xAE, 0xB0, 0x1D, 0x8A, 0x69, 0xCE, 0x72, 0x09, 0xEE, 0x00, 0x40, 0x43, 0xC5, 0x93, 0xC6, 0xB2 - - thumb_func_start sub_02000570 -sub_02000570: ; 0x02000570 - svc #0xb - bx lr - thumb_func_end sub_02000570 -_02000574: - .byte 0xBC, 0x15, 0x93, 0xAD, 0x09, 0xF3, 0xFB, 0xBA, 0xAB, 0x28, 0xCE, 0xA1 - .byte 0xDF, 0x1A, 0xD7, 0xE9, 0x80, 0xD7, 0xDD, 0xC4, 0x0D, 0xAC, 0xB2, 0x18, 0x50, 0x27, 0x88, 0x6A - .byte 0xBD, 0x44, 0x9A, 0x4C, 0xF5, 0xA4, 0x9A, 0xC5, 0x2E, 0x40, 0x20, 0x6E, 0xCE, 0xE3, 0x5E, 0x49 - .byte 0x17, 0x47, 0x47, 0xAE, 0x90, 0xD9, 0xAD, 0xDF, 0xB5, 0x3B, 0xFF, 0x7B, 0x3C, 0x9C, 0x76, 0xC8 - .byte 0x99, 0x4E, 0x6F, 0x1C, 0x66, 0x8E, 0xB0, 0x86, 0x16, 0x9A, 0xD3, 0xAC, 0x6D, 0x1E, 0xD2, 0xF6 - .byte 0xE5, 0x92, 0x1F, 0xAC, 0x25, 0x61, 0x47, 0x74, 0xA1, 0x43, 0xF9, 0x55, 0x92, 0x89, 0xF5, 0x24 - .byte 0xD6, 0x21, 0x92, 0x1E, 0xDD, 0x88, 0x72, 0x94, 0x42, 0x86, 0x45, 0x28, 0xCA, 0xE4, 0x89, 0xBF - .byte 0x15, 0xDF, 0x9F, 0x5D - - thumb_func_start sub_020005E4 -sub_020005E4: ; 0x020005E4 - svc #0x13 - bx lr - thumb_func_end sub_020005E4 -_020005E8: - .byte 0x1A, 0x2D, 0x0F, 0xF7, 0x0F, 0x7E, 0xB5, 0x68 - .byte 0x9E, 0x56, 0x70, 0x6D, 0x69, 0x08, 0x51, 0x97, 0x54, 0x30, 0x97, 0x32, 0x78, 0xD1, 0x62, 0x94 - .byte 0xFD, 0x6E, 0x67, 0x1D, 0x37, 0x9E, 0x2F, 0xFE, 0x4A, 0x38, 0x15, 0xE8, 0x5E, 0xAF, 0xC8, 0x57 - .byte 0x81, 0x44, 0x16, 0x6B, 0x6C, 0x4B, 0x57, 0x7D, 0xAC, 0x33, 0xDA, 0x4F, 0x4C, 0x74, 0xB9, 0x94 - .byte 0x1B, 0xF5, 0x76, 0x5B, 0x4A, 0xEF, 0x49, 0x7D, 0x55, 0xFB, 0x56, 0xE8, 0x4B, 0x8C, 0x03, 0x75 - .byte 0x1D, 0xFE, 0x9C, 0x8F, 0x58, 0x7B, 0x21, 0x76, 0xCE, 0xBF, 0xDA, 0xAA, 0xE6, 0xE2, 0xFE, 0x52 - .byte 0xAC, 0x3D, 0x48, 0x2B, 0x9D, 0x12, 0xE4, 0x67, 0xDA, 0x53 - - non_word_aligned_thumb_func_start sub_0200064A -sub_0200064A: ; 0x0200064A - svc #0x10 - bx lr - thumb_func_end sub_0200064A -_0200064E: - .byte 0xB9, 0x2F - .byte 0x79, 0x01, 0xAA, 0x32, 0xD5, 0x52, 0x94, 0xA4, 0xA9, 0x32, 0xBD, 0xCE, 0x70, 0xFF, 0xC6, 0x26 - .byte 0x02, 0x23, 0x0A, 0x59, 0x7A, 0x60, 0xFE, 0xC1, 0x3B, 0x5B, 0xC3, 0x6F, 0xA5, 0x43, 0xB1, 0x95 - .byte 0x7B, 0x94, 0x5C, 0xFA, 0x49, 0x06, 0x46, 0x81, 0x17, 0x13, 0x58, 0xEC, 0x13, 0xBF, 0xD1, 0x2E - .byte 0x96, 0x3D, 0x41, 0xEA, 0x0A, 0xA1, 0xE5, 0xDC, 0x8D, 0xC4, 0xEE, 0x19, 0x5F, 0x0C, 0x04, 0x04 - .byte 0x23, 0x0F, 0x5B, 0xF0, 0xCE, 0x91, 0x34, 0x7F, 0x46, 0xDD, 0x9E, 0xE4, 0x7B, 0xDF - - non_word_aligned_thumb_func_start sub_0200069E -sub_0200069E: ; 0x0200069E - mov r2, #0 - svc #4 - bx lr - thumb_func_end sub_0200069E -_020006A4: - .byte 0xE6, 0x8A, 0xFF, 0x50, 0x99, 0x98, 0xAD, 0x97, 0xA0, 0x42, 0x3F, 0x17 - .byte 0xED, 0x3B, 0x69, 0xBD, 0x2E, 0xB2, 0xF1, 0x4A, 0x4E, 0xCE, 0x4B, 0x96, 0xC9, 0xA7, 0xFD, 0x1E - .byte 0xFB, 0xDA, 0x55, 0xE2, 0x1F, 0xEC, 0x89, 0x23, 0xB6, 0xED, 0xA5, 0xC9, 0xF7, 0x3A, 0x32, 0xE8 - .byte 0x8C, 0xA2, 0x17, 0x6A, 0x47, 0x6A, 0x86, 0x43, 0xDA, 0xA5, 0x3D, 0x8A, 0x24, 0x44, 0xBA, 0xEB - .byte 0xAB, 0x09, 0x4C, 0x9C, 0x26, 0x96, 0xFC, 0x4D, 0xC8, 0xA3, 0xF8, 0x1C, 0x7E, 0xDD, 0xBF, 0xBC - .byte 0xD9, 0xAA, 0x21, 0x23, 0xB0, 0x79, 0x80, 0x1C, 0xF6, 0x3C, 0xE7, 0xEF, 0xDC, 0x16, 0x54, 0xEB - .byte 0xFC, 0xA2, 0x36, 0x8D, 0x3B, 0x80, 0x76, 0x6B - - thumb_func_start sub_02000708 -sub_02000708: ; 0x02000708 - svc #0 - bx lr - thumb_func_end sub_02000708 -_0200070C: - .byte 0xE0, 0x35, 0x0E, 0xD6 - .byte 0xF5, 0xE2, 0xAC, 0x6E, 0xAE, 0x5E, 0xD1, 0xB4, 0xD5, 0x0A, 0xAA, 0x00, 0x1A, 0x17, 0x29, 0x2A - .byte 0x6C, 0x32, 0x7C, 0x7C, 0x5E, 0x12, 0xF9, 0x35, 0x5E, 0x12, 0x52, 0xDE, 0xCB, 0x00, 0x64, 0x60 - .byte 0xD6, 0x8A, 0xB9, 0xC9, 0x0C, 0x66, 0x10, 0xA4, 0xD3, 0xED, 0xD3, 0x75, 0x4E, 0x9A, 0xF9, 0xFE - .byte 0x77, 0x47, 0x8E, 0x85, 0xCB, 0xCD, 0x20, 0xC1, 0xCD, 0x7E, 0x12, 0xC3, 0xFF, 0xFD, 0x52, 0x87 - .byte 0x5E, 0x78, 0xC1, 0xFC, 0xB0, 0x92, 0x63, 0xE9, 0xED, 0xC9, 0x6F, 0x8D, 0xBB, 0x3E, 0xC8, 0x93 - .byte 0x04, 0x09, 0x8B, 0xE8, 0xA6, 0x3B, 0xCE, 0x0D, 0xB1, 0x67, 0x66, 0xC8, 0xDB, 0xD3, 0xF5, 0x2F - .byte 0x8F, 0x4D, 0x6A, 0x99, 0x7B, 0xFA, 0x28, 0x74, 0x09, 0xF6, 0xF1, 0xD1, 0x8C, 0x15, 0xE0, 0x2C - .byte 0x59, 0xA0, 0xA6, 0xCC, 0x77, 0x45, 0x64, 0x48, 0x7A, 0x05 - - non_word_aligned_thumb_func_start sub_0200078A -sub_0200078A: ; 0x0200078A - svc #0xc - bx lr - thumb_func_end sub_0200078A -_0200078E: - .byte 0x59, 0x2B - .byte 0xE6, 0x44, 0x0B, 0xBA, 0x2A, 0xEC, 0x68, 0x4F, 0xE9, 0xF6, 0x2B, 0x5D, 0xFC, 0x5B, 0xEF, 0x02 - .byte 0xC8, 0x25, 0x05, 0xC8, 0xF7, 0x0B, 0x1A, 0xA9, 0x2E, 0x50, 0x96, 0x85, 0xAF, 0x8B, 0xE9, 0x9D - .byte 0x10, 0x4E, 0x25, 0xE6, 0xF7, 0x16, 0xE5, 0xF0, 0x3C, 0x13, 0x8D, 0x87, 0xD4, 0x18, 0x48, 0xF9 - .byte 0x85, 0x3A, 0x45, 0x68, 0x0A, 0x00, 0x73, 0xF6, 0x51, 0x07, 0x50, 0x86, 0x18, 0x76, 0x0C, 0x25 - .byte 0x3C, 0xF0, 0xFD, 0x8B, 0xB6, 0x13, 0xC9, 0xC4, 0xA2, 0x83, 0x5B, 0xE7, 0x7E, 0xC8, 0x54, 0xA3 - .byte 0x1D, 0xDF, 0x6C, 0x43, 0xD5, 0x52, 0xAA, 0x18, 0xC4, 0x6F, 0x29, 0xD2, 0x1B, 0x66, 0x82, 0xBE - .byte 0x30, 0xD4, 0x9A, 0xEB, 0x94, 0x23, 0xB6, 0x7F, 0xAC, 0x6B, 0x96, 0x95, 0xB9, 0x14, 0x00, 0x00 - - arm_func_start sub_02000800 -sub_02000800: ; 0x02000800 - mov ip, #0x4000000 - str ip, [ip, #0x208] -_02000808: - ldrh r0, [ip, #6] - cmp r0, #0 - bne _02000808 - bl sub_02000AB0 - mov r0, #0x13 - msr cpsr_c, r0 - ldr r0, _02000930 ; =0x027E0000 - add r0, r0, #0x3fc0 - mov sp, r0 - mov r0, #0x12 - msr cpsr_c, r0 - ldr r0, _02000930 ; =0x027E0000 - add r0, r0, #0x3fc0 - sub r0, r0, #0x40 - sub sp, r0, #4 - tst sp, #4 - beq _02000850 - b _02000854 -_02000850: - sub sp, sp, #4 -_02000854: - ldr r1, _02000934 ; =0x00000800 - sub r1, r0, r1 - mov r0, #0x1f - msr cpsr_fsxc, r0 - sub sp, r1, #4 - mov r0, #0 - ldr r1, _02000930 ; =0x027E0000 - mov r2, #0x4000 - bl sub_02000954 - mov r0, #0 - ldr r1, _02000938 ; =0x05000000 - mov r2, #0x400 - bl sub_02000954 - mov r0, #0x200 - ldr r1, _0200093C ; =0x07000000 - mov r2, #0x400 - bl sub_02000954 - ldr r1, _02000940 ; =0x02000BA0 - ldr r0, [r1, #0x14] - bl sub_02000970 - bl sub_02000A1C - ldr r0, _02000940 ; =0x02000BA0 - ldr r1, [r0, #0xc] - ldr r2, [r0, #0x10] - mov r3, r1 - mov r0, #0 -_020008BC: - cmp r1, r2 - blo _020008C8 - b _020008CC -_020008C8: - str r0, [r1, #0], #4 -_020008CC: - blo _020008BC - bic r1, r3, #0x1f -_020008D4: - mcr p15, #0, r0, c7, c10, #4 - mcr p15, #0, r1, c7, c5, #1 - mcr p15, #0, r1, c7, c14, #1 - add r1, r1, #0x20 - cmp r1, r2 - blt _020008D4 - ldr r1, _02000944 ; =0x027FFF9C - str r0, [r1, #0] - ldr r1, _02000930 ; =0x027E0000 - add r1, r1, #0x3fc0 - add r1, r1, #0x3c - ldr r0, _02000948 ; =0x01FF8000 - str r0, [r1, #0] - bl sub_020E28B8 - bl sub_02000B98 - bl sub_020E402C - ldr r1, _0200094C ; =sub_02000C88 - ldr lr, _02000950 ; =0xFFFF0000 - tst sp, #4 - bne _02000928 - b _0200092C -_02000928: - sub sp, sp, #4 -_0200092C: - bx r1 - ; .align 2, 0 -_02000930: .word 0x027E0000 -_02000934: .word 0x00000800 -_02000938: .word 0x05000000 -_0200093C: .word 0x07000000 -_02000940: .word 0x02000BA0 -_02000944: .word 0x027FFF9C -_02000948: .word 0x01FF8000 -_0200094C: .word sub_02000C88 -_02000950: .word 0xFFFF0000 - arm_func_end sub_02000800 - - arm_func_start sub_02000954 -sub_02000954: ; 0x02000954 - add ip, r1, r2 -_02000958: - cmp r1, ip - blt _02000964 - b _02000968 -_02000964: - stmia r1!, {r0} -_02000968: - blt _02000958 - bx lr - arm_func_end sub_02000954 - - arm_func_start sub_02000970 -sub_02000970: ; 0x02000970 - cmp r0, #0 - beq _02000A18 - stmfd sp!, {r4, r5, r6, r7} - ldmdb r0, {r1, r2} - add r2, r0, r2 - sub r3, r0, r1, lsr #24 - bic r1, r1, #0xff000000 - sub r1, r0, r1 - mov r4, r2 -_02000994: - cmp r3, r1 - ble _020009F4 - ldrb r5, [r3, #-1]! - mov r6, #8 -_020009A4: - sub r6, r6, #1 - blt _02000994 - tst r5, #0x80 - bne _020009C0 - ldrb r0, [r3, #-1]! - strb r0, [r2, #-1]! - b _020009E8 -_020009C0: - ldrb ip, [r3, #-1]! - ldrb r7, [r3, #-1]! - orr r7, r7, ip, lsl #8 - bic r7, r7, #0xf000 - add r7, r7, #2 - add ip, ip, #0x20 -_020009D8: - ldrb r0, [r2, r7] - strb r0, [r2, #-1]! - sub ip, ip, #0x10 - bge _020009D8 -_020009E8: - cmp r3, r1 - lsl r5, r5, #1 - bgt _020009A4 -_020009F4: - mov r0, #0 - bic r3, r1, #0x1f -_020009FC: - mcr p15, #0, r0, c7, c10, #4 - mcr p15, #0, r3, c7, c5, #1 - mcr p15, #0, r3, c7, c14, #1 - add r3, r3, #0x20 - cmp r3, r4 - blt _020009FC - ldmia sp!, {r4, r5, r6, r7} -_02000A18: - bx lr - arm_func_end sub_02000970 - - arm_func_start sub_02000A1C -sub_02000A1C: ; 0x02000A1C - ldr r0, _02000AA8 ; =0x02000BA0 - ldr r1, [r0, #0] - ldr r2, [r0, #4] - ldr r3, [r0, #8] -_02000A2C: - cmp r1, r2 - beq _02000AA4 - ldr r5, [r1, #0], #4 - ldr r7, [r1, #0], #4 - add r6, r5, r7 - mov r4, r5 -_02000A44: - cmp r4, r6 - bmi _02000A50 - b _02000A54 -_02000A50: - ldr r7, [r3, #0], #4 -_02000A54: - bmi _02000A5C - b _02000A60 -_02000A5C: - str r7, [r4, #0], #4 -_02000A60: - bmi _02000A44 - ldr r7, [r1, #0], #4 - add r6, r4, r7 - mov r7, #0 -_02000A70: - cmp r4, r6 - blo _02000A7C - b _02000A80 -_02000A7C: - str r7, [r4, #0], #4 -_02000A80: - blo _02000A70 - bic r4, r5, #0x1f -_02000A88: - mcr p15, #0, r7, c7, c10, #4 - mcr p15, #0, r4, c7, c5, #1 - mcr p15, #0, r4, c7, c14, #1 - add r4, r4, #0x20 - cmp r4, r6 - blt _02000A88 - b _02000A2C -_02000AA4: - b _02000AAC - ; .align 2, 0 -_02000AA8: .word 0x02000BA0 - arm_func_end sub_02000A1C -_02000AAC: - bx lr - - arm_func_start sub_02000AB0 -sub_02000AB0: ; 0x02000AB0 - mrc p15, #0, r0, c1, c0, #0 - ldr r1, _02000B68 ; =0x000F9005 - bic r0, r0, r1 - mcr p15, #0, r0, c1, c0, #0 - mov r0, #0 - mcr p15, #0, r0, c7, c5, #0 - mcr p15, #0, r0, c7, c6, #0 - mcr p15, #0, r0, c7, c10, #4 - ldr r0, _02000B6C ; =0x04000033 - mcr p15, #0, r0, c6, c0, #0 - ldr r0, _02000B70 ; =0x0200002D - mcr p15, #0, r0, c6, c1, #0 - ldr r0, _02000B74 ; =0x027E0021 - mcr p15, #0, r0, c6, c2, #0 - ldr r0, _02000B78 ; =0x08000035 - mcr p15, #0, r0, c6, c3, #0 - ldr r0, _02000B7C ; =0x027E0000 - orr r0, r0, #0x1a - orr r0, r0, #1 - mcr p15, #0, r0, c6, c4, #0 - ldr r0, _02000B80 ; =0x0100002F - mcr p15, #0, r0, c6, c5, #0 - ldr r0, _02000B84 ; =0xFFFF001D - mcr p15, #0, r0, c6, c6, #0 - ldr r0, _02000B88 ; =0x027FF017 - mcr p15, #0, r0, c6, c7, #0 - mov r0, #0x20 - mcr p15, #0, r0, c9, c1, #1 - ldr r0, _02000B7C ; =0x027E0000 - orr r0, r0, #0xa - mcr p15, #0, r0, c9, c1, #0 - mov r0, #0x42 - mcr p15, #0, r0, c2, c0, #1 - mov r0, #0x42 - mcr p15, #0, r0, c2, c0, #0 - mov r0, #2 - mcr p15, #0, r0, c3, c0, #0 - ldr r0, _02000B8C ; =0x05100011 - mcr p15, #0, r0, c5, c0, #3 - ldr r0, _02000B90 ; =0x15111011 - mcr p15, #0, r0, c5, c0, #2 - mrc p15, #0, r0, c1, c0, #0 - ldr r1, _02000B94 ; =0x0005707D - orr r0, r0, r1 - mcr p15, #0, r0, c1, c0, #0 - bx lr - ; .align 2, 0 -_02000B68: .word 0x000F9005 -_02000B6C: .word 0x04000033 -_02000B70: .word 0x0200002D -_02000B74: .word 0x027E0021 -_02000B78: .word 0x08000035 -_02000B7C: .word 0x027E0000 -_02000B80: .word 0x0100002F -_02000B84: .word 0xFFFF001D -_02000B88: .word 0x027FF017 -_02000B8C: .word 0x05100011 -_02000B90: .word 0x15111011 -_02000B94: .word 0x0005707D - arm_func_end sub_02000AB0 - - arm_func_start sub_02000B98 -sub_02000B98: ; 0x02000B98 - bx lr - arm_func_end sub_02000B98 - - arm_func_start sub_02000B9C -sub_02000B9C: ; 0x02000B9C - bx lr - arm_func_end sub_02000B9C -_02000BA0: - .byte 0xE0, 0x23, 0x10, 0x02, 0xF8, 0x23, 0x10, 0x02, 0x20, 0x1D, 0x10, 0x02, 0x20, 0x1D, 0x10, 0x02 - .byte 0x80, 0x0D, 0x1D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x31, 0x75, 0x02, 0x04, 0x21, 0x06, 0xC0, 0xDE - .byte 0xDE, 0xC0, 0x06, 0x21, 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x4E, 0x49, 0x4E, 0x54, 0x45, 0x4E, 0x44 - .byte 0x4F, 0x3A, 0x44, 0x57, 0x43, 0x32, 0x2E, 0x32, 0x2E, 0x33, 0x30, 0x30, 0x30, 0x38, 0x2E, 0x30 - .byte 0x38, 0x30, 0x36, 0x33, 0x30, 0x2E, 0x31, 0x39, 0x30, 0x36, 0x5F, 0x44, 0x57, 0x43, 0x5F, 0x32 - .byte 0x5F, 0x32, 0x5F, 0x50, 0x4C, 0x55, 0x53, 0x38, 0x5D, 0x00, 0x00, 0x00, 0x5B, 0x53, 0x44, 0x4B - .byte 0x2B, 0x4E, 0x49, 0x4E, 0x54, 0x45, 0x4E, 0x44, 0x4F, 0x3A, 0x42, 0x41, 0x43, 0x4B, 0x55, 0x50 - .byte 0x5D, 0x00, 0x00, 0x00, 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x41, 0x62, 0x69, 0x6F, 0x73, 0x73, 0x6F - .byte 0x3A, 0x6C, 0x69, 0x62, 0x56, 0x43, 0x54, 0x20, 0x31, 0x2E, 0x33, 0x2E, 0x31, 0x5D, 0x00, 0x00 - .byte 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x4E, 0x49, 0x4E, 0x54, 0x45, 0x4E, 0x44, 0x4F, 0x3A, 0x57, 0x69 - .byte 0x46, 0x69, 0x32, 0x2E, 0x31, 0x2E, 0x33, 0x30, 0x30, 0x30, 0x33, 0x2E, 0x30, 0x37, 0x30, 0x39 - .byte 0x32, 0x30, 0x30, 0x32, 0x32, 0x39, 0x5D, 0x00, 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x55, 0x42, 0x49 - .byte 0x51, 0x55, 0x49, 0x54, 0x4F, 0x55, 0x53, 0x3A, 0x43, 0x50, 0x53, 0x5D, 0x00, 0x00, 0x00, 0x00 - .byte 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x55, 0x42, 0x49, 0x51, 0x55, 0x49, 0x54, 0x4F, 0x55, 0x53, 0x3A - .byte 0x53, 0x53, 0x4C, 0x5D, 0x00, 0x00, 0x00, 0x00 - thumb_func_start sub_02000C88 sub_02000C88: ; 0x02000C88 push {r3, r4, r5, r6, r7, lr} diff --git a/asm/dtcm.s b/asm/dtcm.s new file mode 100644 index 0000000000..e69de29bb2 diff --git a/asm/itcm.s b/asm/itcm.s new file mode 100644 index 0000000000..e69de29bb2 diff --git a/binutils.mk b/binutils.mk new file mode 100644 index 0000000000..2c8dea8134 --- /dev/null +++ b/binutils.mk @@ -0,0 +1,14 @@ +# Supply a GNU ARM Embedded toolchain for objcopy, ar, etc. + +ifdef DEVKITARM +TOOLCHAIN := $(DEVKITARM) +endif + +ifdef TOOLCHAIN +export PATH := $(TOOLCHAIN)/bin:$(PATH) +endif + +PREFIX := arm-none-eabi- + +OBJCOPY := $(PREFIX)objcopy +AR := $(PREFIX)ar diff --git a/common.mk b/common.mk new file mode 100644 index 0000000000..6d1f50cdcf --- /dev/null +++ b/common.mk @@ -0,0 +1,210 @@ +## Common defines for ARM9 and ARM7 Makefiles ## + +COMPARE ?= 1 + +default: all + +PROJECT_ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +# Because mwldarm expects absolute paths to be WIN32 paths, +# all paths referring up from BUILD_DIR must be relative. +WORK_DIR := $(shell realpath --relative-to $(CURDIR) $(PROJECT_ROOT)) +$(shell mkdir -p $(BUILD_DIR)) +BACK_REL := $(shell realpath --relative-to $(BUILD_DIR) $(CURDIR)) + +TOOLSDIR := $(WORK_DIR)/tools + +include $(WORK_DIR)/platform.mk +include $(WORK_DIR)/binutils.mk + +# NitroSDK tools +MWCC = $(TOOLSDIR)/mwccarm/$(MWCCVER)/mwccarm.exe +MWAS = $(TOOLSDIR)/mwccarm/$(MWCCVER)/mwasmarm.exe +MWLD = $(BACK_REL)/$(TOOLSDIR)/mwccarm/$(MWCCVER)/mwldarm.exe +MAKEROM := $(TOOLSDIR)/bin/makerom.exe +MAKELCF := $(TOOLSDIR)/bin/makelcf.exe +MAKEBNR := $(TOOLSDIR)/bin/makebanner.exe + +export LM_LICENSE_FILE := $(TOOLSDIR)/mwccarm/license.dat + +# Native tools +FIXROM := $(TOOLSDIR)/fixrom/fixrom$(EXE) +ASPATCH := $(TOOLSDIR)/mwasmarm_patcher/mwasmarm_patcher$(EXE) + +# Other tools +NTRMERGE := $(TOOLSDIR)/ntr_merge_elf/ntr_merge_elf.sh +ASM_PROCESSOR := $(TOOLSDIR)/asm_processor/compile.sh + +NATIVE_TOOLS := \ + $(FIXROM) \ + $(ASPATCH) + +TOOLDIRS := $(foreach tool,$(NATIVE_TOOLS),$(dir $(tool))) + +# Directories +NITROSDK_SRC_SUBDIRS := os + +LIB_SUBDIRS := cw NitroSDK NitroSystem NitroDWC NitroWiFi libCPS libVCT +SRC_SUBDIR := src +ASM_SUBDIR := asm +LIB_SRC_SUBDIR := lib/src $(LIB_SUBDIRS:%=lib/%/src) $(NITROSDK_SRC_SUBDIRS:%=lib/NitroSDK/src/%) +LIB_ASM_SUBDIR := lib/asm $(LIB_SUBDIRS:%=lib/%/asm) +ALL_SUBDIRS := $(SRC_SUBDIR) $(ASM_SUBDIR) $(LIB_SRC_SUBDIR) $(LIB_ASM_SUBDIR) + +SRC_BUILDDIR := $(addprefix $(BUILD_DIR)/,$(SRC_SUBDIR)) +ASM_BUILDDIR := $(addprefix $(BUILD_DIR)/,$(ASM_SUBDIR)) +LIB_SRC_BUILDDIR := $(addprefix $(BUILD_DIR)/,$(LIB_SRC_SUBDIR)) +LIB_ASM_BUILDDIR := $(addprefix $(BUILD_DIR)/,$(LIB_ASM_SUBDIR)) + +C_SRCS := $(foreach dname,$(SRC_SUBDIR),$(wildcard $(dname)/*.c)) +ASM_SRCS := $(foreach dname,$(ASM_SUBDIR),$(wildcard $(dname)/*.s)) +# asm processor should only be necessary for libcrypto +GLOBAL_ASM_SRCS := +LIB_C_SRCS := $(foreach dname,$(LIB_SRC_SUBDIR),$(wildcard $(dname)/*.c)) +LIB_ASM_SRCS := $(foreach dname,$(LIB_ASM_SUBDIR),$(wildcard $(dname)/*.s)) +ALL_SRCS := $(C_SRCS) $(ASM_SRCS) $(LIB_C_SRCS) $(LIB_ASM_SRCS) + +C_OBJS = $(C_SRCS:%.c=$(BUILD_DIR)/%.o) +ASM_OBJS = $(ASM_SRCS:%.s=$(BUILD_DIR)/%.o) +GLOBAL_ASM_OBJS = $(GLOBAL_ASM_SRCS:%.c=$(BUILD_DIR)/%.o) +LIB_C_OBJS = $(LIB_C_SRCS:%.c=$(BUILD_DIR)/%.o) +LIB_ASM_OBJS = $(LIB_ASM_SRCS:%.s=$(BUILD_DIR)/%.o) +ALL_GAME_OBJS = $(C_OBJS) $(ASM_OBJS) +ALL_LIB_OBJS = $(LIB_C_OBJS) $(LIB_ASM_OBJS) +ALL_OBJS = $(ALL_GAME_OBJS) $(ALL_LIB_OBJS) + +$(ALL_LIB_OBJS): DEFINES = $(GLB_DEFINES) + +ALL_BUILDDIRS := $(sort $(ALL_BUILDDIRS) $(foreach obj,$(ALL_OBJS),$(dir $(obj)))) + +NEF := $(BUILD_DIR)/$(NEFNAME).nef +ELF := $(NEF:%.nef=%.elf) +LCF := $(NEF:%.nef=%.lcf) +RESPONSE := $(NEF:%.nef=%.response) +SBIN := $(NEF:%.nef=%.sbin) +XMAP := $(NEF).xMAP + +EXCCFLAGS := -Cpp_exceptions off + +MWCFLAGS = $(DEFINES) $(OPTFLAGS) -enum int -lang c99 $(EXCCFLAGS) -gccext,on -proc $(PROC) -msgstyle gcc -gccinc -i ./include -i ./include/library -i $(WORK_DIR)/files -I$(WORK_DIR)/lib/include -ipa file -interworking -inline on,noauto -char signed +MWASFLAGS = $(DEFINES) -proc $(PROC_S) -gccinc -i . -i ./include -i $(WORK_DIR)/files -I$(WORK_DIR)/lib/include -DSDK_ASM +MWLDFLAGS := -w off -proc $(PROC) -nopic -nopid -interworking -map closure,unused -symtab sort -m _start -msgstyle gcc +ARFLAGS := rcS + +$(C_OBJS): MWCFLAGS += -include global.h + +MW_COMPILE = $(WINE) $(MWCC) $(MWCFLAGS) +MW_ASSEMBLE = $(WINE) $(MWAS) $(MWASFLAGS) + +export MWCIncludes := lib/include + +LSF := $(addsuffix .lsf,$(NEFNAME)) +ifneq ($(LSF),) +OVERLAYS := $(shell $(GREP) -o "^Overlay \w+" $(LSF) | cut -d' ' -f2) +else +OVERLAYS := +endif + +# Make sure build directories exist before compiling anything +DUMMY := $(shell mkdir -p $(ALL_BUILDDIRS)) + +.SECONDARY: +.SECONDEXPANSION: +.DELETE_ON_ERROR: +.PHONY: all tidy clean tools clean-tools $(TOOLDIRS) +.PRECIOUS: $(SBIN) +.NOTPARALLEL: + +.PHONY: $(MWAS) +$(MWAS): + $(ASPATCH) -q $@ + +all: tools + +ifeq ($(NODEP),) +ifneq ($(WINPATH),) +PROJECT_ROOT_NT := $(shell $(WINPATH) -w $(PROJECT_ROOT) | $(SED) 's/\\/\//g') +define fixdep +$(SED) -i 's/\r//g; s/\\/\//g; s/\/$$/\\/g; s#$(PROJECT_ROOT_NT)#$(PROJECT_ROOT)#g' $(1) +touch -r $(1:%.d=%.o) $(1) +endef +else +define fixdep +$(SED) -i 's/\r//g; s/\\/\//g; s/\/$$/\\/g' $(1) +touch -r $(1:%.d=%.o) $(1) +endef +endif +DEPFLAGS := -gccdep -MD +DEPFILES := $(ALL_OBJS:%.o=%.d) +MW_COMPILE += $(DEPFLAGS) +$(GLOBAL_ASM_OBJS): BUILD_C := $(ASM_PROCESSOR) "$(MW_COMPILE)" "$(MW_ASSEMBLE)" +BUILD_C ?= $(MW_COMPILE) -c -o + +$(DEPFILES): + +$(BUILD_DIR)/lib/NitroSDK/%.o: MWCCVER := 2.0/sp2p3 + +$(BUILD_DIR)/%.o: %.c +$(BUILD_DIR)/%.o: %.c $(BUILD_DIR)/%.d + $(BUILD_C) $@ $< + @$(call fixdep,$(BUILD_DIR)/$*.d) + +$(BUILD_DIR)/%.o: %.s +$(BUILD_DIR)/%.o: %.s $(BUILD_DIR)/%.d + $(WINE) $(MWAS) $(MWASFLAGS) $(DEPFLAGS) -o $@ $< + @$(call fixdep,$(BUILD_DIR)/$*.d) + +include $(wildcard $(DEPFILES)) +else +$(GLOBAL_ASM_OBJS): BUILD_C := $(ASM_PROCESSOR) "$(MW_COMPILE)" "$(MW_ASSEMBLE)" +BUILD_C ?= $(MW_COMPILE) -c -o + +$(BUILD_DIR)/%.o: %.c + $(BUILD_C) $@ $< + +$(BUILD_DIR)/%.o: %.s + $(WINE) $(MWAS) $(MWASFLAGS) -o $@ $< +endif + +$(NATIVE_TOOLS): tools + +tools: $(TOOLDIRS) $(MWAS) + +$(TOOLDIRS): + @$(MAKE) -C $@ + +clean-tools: + $(foreach tool,$(TOOLDIRS),$(MAKE) -C $(tool) clean;) + +$(LCF): $(LSF) $(LCF_TEMPLATE) + $(WINE) $(MAKELCF) $(MAKELCF_FLAGS) $^ $@ +ifeq ($(PROC),arm946e) + $(SED) -i '1i KEEP_SECTION\n{\n\t.exceptix\n}' $@ +else + $(SED) -i '/\} > check\.WORKRAM/a SDK_SUBPRIV_ARENA_LO = SDK_SUBPRIV_ARENA_LO + SDK_AUTOLOAD.EXT_WRAM.SIZE + SDK_AUTOLOAD.EXT_WRAM.BSS_SIZE;' $@ +endif + +RESPONSE_TEMPLATE := $(PROJECT_ROOT)/mwldarm.response.template +RESPONSE_TEMPLATE_NT := $(PROJECT_ROOT_NT)/mwldarm.response.template + +$(RESPONSE): $(LSF) $(RESPONSE_TEMPLATE) + $(WINE) $(MAKELCF) $(MAKELCF_FLAGS) $< $(RESPONSE_TEMPLATE_NT) $@ + +# Locate crt0.o +CRT0_OBJ := lib/asm/crt0.o + +$(NEF): $(LCF) $(RESPONSE) $(ALL_OBJS) + cd $(BUILD_DIR) && LM_LICENSE_FILE=$(BACK_REL)/$(LM_LICENSE_FILE) $(WINE) $(MWLD) $(MWLDFLAGS) $(LIBS) -o $(BACK_REL)/$(NEF) $(LCF:$(BUILD_DIR)/%=%) @$(RESPONSE:$(BUILD_DIR)/%=%) $(CRT0_OBJ) + +.INTERMEDIATE: $(BUILD_DIR)/obj.list + +$(SBIN): build/%.sbin: build/%.nef + +ifeq ($(COMPARE),1) + $(SHA1SUM) --quiet -c $*.sha1 +endif + +$(ELF): %.elf: %.nef + $(NTRMERGE) $* + +print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true diff --git a/lib/asm/crt0.s b/lib/asm/crt0.s new file mode 100644 index 0000000000..7051c9b26f --- /dev/null +++ b/lib/asm/crt0.s @@ -0,0 +1,312 @@ + .include "asm/macros/function.inc" + + arm_func_start sub_02000800 +sub_02000800: ; 0x02000800 + mov ip, #0x4000000 + str ip, [ip, #0x208] +_02000808: + ldrh r0, [ip, #6] + cmp r0, #0 + bne _02000808 + bl sub_02000AB0 + mov r0, #0x13 + msr cpsr_c, r0 + ldr r0, _02000930 ; =0x027E0000 + add r0, r0, #0x3fc0 + mov sp, r0 + mov r0, #0x12 + msr cpsr_c, r0 + ldr r0, _02000930 ; =0x027E0000 + add r0, r0, #0x3fc0 + sub r0, r0, #0x40 + sub sp, r0, #4 + tst sp, #4 + beq _02000850 + b _02000854 +_02000850: + sub sp, sp, #4 +_02000854: + ldr r1, _02000934 ; =0x00000800 + sub r1, r0, r1 + mov r0, #0x1f + msr cpsr_fsxc, r0 + sub sp, r1, #4 + mov r0, #0 + ldr r1, _02000930 ; =0x027E0000 + mov r2, #0x4000 + bl sub_02000954 + mov r0, #0 + ldr r1, _02000938 ; =0x05000000 + mov r2, #0x400 + bl sub_02000954 + mov r0, #0x200 + ldr r1, _0200093C ; =0x07000000 + mov r2, #0x400 + bl sub_02000954 + ldr r1, _02000940 ; =0x02000BA0 + ldr r0, [r1, #0x14] + bl sub_02000970 + bl sub_02000A1C + ldr r0, _02000940 ; =0x02000BA0 + ldr r1, [r0, #0xc] + ldr r2, [r0, #0x10] + mov r3, r1 + mov r0, #0 +_020008BC: + cmp r1, r2 + blo _020008C8 + b _020008CC +_020008C8: + str r0, [r1, #0], #4 +_020008CC: + blo _020008BC + bic r1, r3, #0x1f +_020008D4: + mcr p15, #0, r0, c7, c10, #4 + mcr p15, #0, r1, c7, c5, #1 + mcr p15, #0, r1, c7, c14, #1 + add r1, r1, #0x20 + cmp r1, r2 + blt _020008D4 + ldr r1, _02000944 ; =0x027FFF9C + str r0, [r1, #0] + ldr r1, _02000930 ; =0x027E0000 + add r1, r1, #0x3fc0 + add r1, r1, #0x3c + ldr r0, _02000948 ; =0x01FF8000 + str r0, [r1, #0] + bl sub_020E28B8 + bl sub_02000B98 + bl sub_020E402C + ldr r1, _0200094C ; =sub_02000C88 + ldr lr, _02000950 ; =0xFFFF0000 + tst sp, #4 + bne _02000928 + b _0200092C +_02000928: + sub sp, sp, #4 +_0200092C: + bx r1 + ; .align 2, 0 +_02000930: .word 0x027E0000 +_02000934: .word 0x00000800 +_02000938: .word 0x05000000 +_0200093C: .word 0x07000000 +_02000940: .word 0x02000BA0 +_02000944: .word 0x027FFF9C +_02000948: .word 0x01FF8000 +_0200094C: .word sub_02000C88 +_02000950: .word 0xFFFF0000 + arm_func_end sub_02000800 + + arm_func_start sub_02000954 +sub_02000954: ; 0x02000954 + add ip, r1, r2 +_02000958: + cmp r1, ip + blt _02000964 + b _02000968 +_02000964: + stmia r1!, {r0} +_02000968: + blt _02000958 + bx lr + arm_func_end sub_02000954 + + arm_func_start sub_02000970 +sub_02000970: ; 0x02000970 + cmp r0, #0 + beq _02000A18 + stmfd sp!, {r4, r5, r6, r7} + ldmdb r0, {r1, r2} + add r2, r0, r2 + sub r3, r0, r1, lsr #24 + bic r1, r1, #0xff000000 + sub r1, r0, r1 + mov r4, r2 +_02000994: + cmp r3, r1 + ble _020009F4 + ldrb r5, [r3, #-1]! + mov r6, #8 +_020009A4: + sub r6, r6, #1 + blt _02000994 + tst r5, #0x80 + bne _020009C0 + ldrb r0, [r3, #-1]! + strb r0, [r2, #-1]! + b _020009E8 +_020009C0: + ldrb ip, [r3, #-1]! + ldrb r7, [r3, #-1]! + orr r7, r7, ip, lsl #8 + bic r7, r7, #0xf000 + add r7, r7, #2 + add ip, ip, #0x20 +_020009D8: + ldrb r0, [r2, r7] + strb r0, [r2, #-1]! + sub ip, ip, #0x10 + bge _020009D8 +_020009E8: + cmp r3, r1 + lsl r5, r5, #1 + bgt _020009A4 +_020009F4: + mov r0, #0 + bic r3, r1, #0x1f +_020009FC: + mcr p15, #0, r0, c7, c10, #4 + mcr p15, #0, r3, c7, c5, #1 + mcr p15, #0, r3, c7, c14, #1 + add r3, r3, #0x20 + cmp r3, r4 + blt _020009FC + ldmia sp!, {r4, r5, r6, r7} +_02000A18: + bx lr + arm_func_end sub_02000970 + + arm_func_start sub_02000A1C +sub_02000A1C: ; 0x02000A1C + ldr r0, _02000AA8 ; =0x02000BA0 + ldr r1, [r0, #0] + ldr r2, [r0, #4] + ldr r3, [r0, #8] +_02000A2C: + cmp r1, r2 + beq _02000AA4 + ldr r5, [r1, #0], #4 + ldr r7, [r1, #0], #4 + add r6, r5, r7 + mov r4, r5 +_02000A44: + cmp r4, r6 + bmi _02000A50 + b _02000A54 +_02000A50: + ldr r7, [r3, #0], #4 +_02000A54: + bmi _02000A5C + b _02000A60 +_02000A5C: + str r7, [r4, #0], #4 +_02000A60: + bmi _02000A44 + ldr r7, [r1, #0], #4 + add r6, r4, r7 + mov r7, #0 +_02000A70: + cmp r4, r6 + blo _02000A7C + b _02000A80 +_02000A7C: + str r7, [r4, #0], #4 +_02000A80: + blo _02000A70 + bic r4, r5, #0x1f +_02000A88: + mcr p15, #0, r7, c7, c10, #4 + mcr p15, #0, r4, c7, c5, #1 + mcr p15, #0, r4, c7, c14, #1 + add r4, r4, #0x20 + cmp r4, r6 + blt _02000A88 + b _02000A2C +_02000AA4: + b _02000AAC + ; .align 2, 0 +_02000AA8: .word 0x02000BA0 + arm_func_end sub_02000A1C +_02000AAC: + bx lr + + arm_func_start sub_02000AB0 +sub_02000AB0: ; 0x02000AB0 + mrc p15, #0, r0, c1, c0, #0 + ldr r1, _02000B68 ; =0x000F9005 + bic r0, r0, r1 + mcr p15, #0, r0, c1, c0, #0 + mov r0, #0 + mcr p15, #0, r0, c7, c5, #0 + mcr p15, #0, r0, c7, c6, #0 + mcr p15, #0, r0, c7, c10, #4 + ldr r0, _02000B6C ; =0x04000033 + mcr p15, #0, r0, c6, c0, #0 + ldr r0, _02000B70 ; =0x0200002D + mcr p15, #0, r0, c6, c1, #0 + ldr r0, _02000B74 ; =0x027E0021 + mcr p15, #0, r0, c6, c2, #0 + ldr r0, _02000B78 ; =0x08000035 + mcr p15, #0, r0, c6, c3, #0 + ldr r0, _02000B7C ; =0x027E0000 + orr r0, r0, #0x1a + orr r0, r0, #1 + mcr p15, #0, r0, c6, c4, #0 + ldr r0, _02000B80 ; =0x0100002F + mcr p15, #0, r0, c6, c5, #0 + ldr r0, _02000B84 ; =0xFFFF001D + mcr p15, #0, r0, c6, c6, #0 + ldr r0, _02000B88 ; =0x027FF017 + mcr p15, #0, r0, c6, c7, #0 + mov r0, #0x20 + mcr p15, #0, r0, c9, c1, #1 + ldr r0, _02000B7C ; =0x027E0000 + orr r0, r0, #0xa + mcr p15, #0, r0, c9, c1, #0 + mov r0, #0x42 + mcr p15, #0, r0, c2, c0, #1 + mov r0, #0x42 + mcr p15, #0, r0, c2, c0, #0 + mov r0, #2 + mcr p15, #0, r0, c3, c0, #0 + ldr r0, _02000B8C ; =0x05100011 + mcr p15, #0, r0, c5, c0, #3 + ldr r0, _02000B90 ; =0x15111011 + mcr p15, #0, r0, c5, c0, #2 + mrc p15, #0, r0, c1, c0, #0 + ldr r1, _02000B94 ; =0x0005707D + orr r0, r0, r1 + mcr p15, #0, r0, c1, c0, #0 + bx lr + ; .align 2, 0 +_02000B68: .word 0x000F9005 +_02000B6C: .word 0x04000033 +_02000B70: .word 0x0200002D +_02000B74: .word 0x027E0021 +_02000B78: .word 0x08000035 +_02000B7C: .word 0x027E0000 +_02000B80: .word 0x0100002F +_02000B84: .word 0xFFFF001D +_02000B88: .word 0x027FF017 +_02000B8C: .word 0x05100011 +_02000B90: .word 0x15111011 +_02000B94: .word 0x0005707D + arm_func_end sub_02000AB0 + + arm_func_start sub_02000B98 +sub_02000B98: ; 0x02000B98 + bx lr + arm_func_end sub_02000B98 + + arm_func_start sub_02000B9C +sub_02000B9C: ; 0x02000B9C + bx lr + arm_func_end sub_02000B9C +_02000BA0: + .byte 0xE0, 0x23, 0x10, 0x02, 0xF8, 0x23, 0x10, 0x02, 0x20, 0x1D, 0x10, 0x02, 0x20, 0x1D, 0x10, 0x02 + .byte 0x80, 0x0D, 0x1D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x31, 0x75, 0x02, 0x04, 0x21, 0x06, 0xC0, 0xDE + .byte 0xDE, 0xC0, 0x06, 0x21, 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x4E, 0x49, 0x4E, 0x54, 0x45, 0x4E, 0x44 + .byte 0x4F, 0x3A, 0x44, 0x57, 0x43, 0x32, 0x2E, 0x32, 0x2E, 0x33, 0x30, 0x30, 0x30, 0x38, 0x2E, 0x30 + .byte 0x38, 0x30, 0x36, 0x33, 0x30, 0x2E, 0x31, 0x39, 0x30, 0x36, 0x5F, 0x44, 0x57, 0x43, 0x5F, 0x32 + .byte 0x5F, 0x32, 0x5F, 0x50, 0x4C, 0x55, 0x53, 0x38, 0x5D, 0x00, 0x00, 0x00, 0x5B, 0x53, 0x44, 0x4B + .byte 0x2B, 0x4E, 0x49, 0x4E, 0x54, 0x45, 0x4E, 0x44, 0x4F, 0x3A, 0x42, 0x41, 0x43, 0x4B, 0x55, 0x50 + .byte 0x5D, 0x00, 0x00, 0x00, 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x41, 0x62, 0x69, 0x6F, 0x73, 0x73, 0x6F + .byte 0x3A, 0x6C, 0x69, 0x62, 0x56, 0x43, 0x54, 0x20, 0x31, 0x2E, 0x33, 0x2E, 0x31, 0x5D, 0x00, 0x00 + .byte 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x4E, 0x49, 0x4E, 0x54, 0x45, 0x4E, 0x44, 0x4F, 0x3A, 0x57, 0x69 + .byte 0x46, 0x69, 0x32, 0x2E, 0x31, 0x2E, 0x33, 0x30, 0x30, 0x30, 0x33, 0x2E, 0x30, 0x37, 0x30, 0x39 + .byte 0x32, 0x30, 0x30, 0x32, 0x32, 0x39, 0x5D, 0x00, 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x55, 0x42, 0x49 + .byte 0x51, 0x55, 0x49, 0x54, 0x4F, 0x55, 0x53, 0x3A, 0x43, 0x50, 0x53, 0x5D, 0x00, 0x00, 0x00, 0x00 + .byte 0x5B, 0x53, 0x44, 0x4B, 0x2B, 0x55, 0x42, 0x49, 0x51, 0x55, 0x49, 0x54, 0x4F, 0x55, 0x53, 0x3A + .byte 0x53, 0x53, 0x4C, 0x5D, 0x00, 0x00, 0x00, 0x00 diff --git a/lib/syscall/Makefile b/lib/syscall/Makefile new file mode 100644 index 0000000000..9634e80701 --- /dev/null +++ b/lib/syscall/Makefile @@ -0,0 +1,32 @@ +GAME_CODE ?= arm9 +MWCCVER := 2.0/sp2p3 +PROC := arm946e +PROC_S := arm5te +PROC_LD := v5te +BUILD_DIR := build + +include ../../common.mk + +MWLDFLAGS := -library -nodead -w off -proc $(PROC_LD) +LIB := $(BUILD_DIR)/libsyscall.a + +.PHONY: all install + +INSTALL_PREFIX ?= $(WORK_DIR)/lib/ARM9-TS +INSTALL_LIBDIR := $(INSTALL_PREFIX)/lib + +all: $(LIB) + +ASM_SRCS := asm/_secure.s + +$(LIB): $(ALL_OBJS) + cd $(BUILD_DIR) && LM_LICENSE_FILE=$(BACK_REL)/$(LM_LICENSE_FILE) $(WINE) $(MWLD) $(MWLDFLAGS) -o $(BACK_REL)/$(LIB) $(ALL_OBJS:$(BUILD_DIR)/%=%) + +install: all + $(shell mkdir -p $(INSTALL_LIBDIR)) + cp $(LIB) $(INSTALL_LIBDIR)/ + +tidy: + $(RM) -r $(BUILD_DIR) + +clean: tidy diff --git a/lib/syscall/asm/_secure.s b/lib/syscall/asm/_secure.s new file mode 100644 index 0000000000..5144e44ec3 --- /dev/null +++ b/lib/syscall/asm/_secure.s @@ -0,0 +1,265 @@ + .include "asm/macros/function.inc" + + .global SecureAreaStart +SecureAreaStart: + .byte 0xFF, 0xDE, 0xFF, 0xE7, 0xFF, 0xDE, 0xFF, 0xE7, 0xFF, 0xDE, 0xFF, 0xE7, 0xFF, 0xDE, 0x92, 0x8E + .byte 0x26, 0xCB, 0x38, 0xC4, 0xDF, 0xBC, 0xA3, 0x97, 0x54, 0xA4, 0xE6, 0x8D, 0x1F, 0x05, 0x85, 0x33 + .byte 0xB7, 0x48, 0xEC, 0xF8, 0x19, 0xE8, 0x5C, 0x73, 0x66, 0x43, 0x66, 0xC4, 0x63, 0x36, 0x44, 0x36 + .byte 0x24, 0x40, 0x84, 0xCC, 0xDD, 0x94, 0x08, 0xA2, 0x8A, 0x12, 0xE2, 0x43, 0xD5, 0xF4, 0x95, 0x8C + .byte 0xA8, 0x64, 0xCD, 0xDB, 0xA7, 0x11, 0xD1, 0x9A, 0xD9, 0x2E, 0x63, 0x3F, 0xA0, 0xFF, 0xB2, 0x5C + .byte 0xAD, 0xFB, 0xBC, 0x93, 0x5F, 0xD0, 0x8B, 0x60, 0xEB, 0x1B, 0xE3, 0x46, 0x69, 0xBB, 0xED, 0x62 + .byte 0x17, 0xD5, 0x67, 0x98, 0xBB, 0xD2, 0x0B, 0xF9, 0x61, 0x5A, 0x71, 0xB4, 0xCD, 0xC7, 0xE4, 0x78 + .byte 0x36, 0x22, 0x91, 0x9A, 0xE6, 0x82, 0xB0, 0xD5, 0x63, 0x0E + + non_word_aligned_thumb_func_start sub_0200007A +sub_0200007A: ; 0x0200007A + swi #0xd + bx lr + thumb_func_end sub_0200007A +_0200007E: + .byte 0x7F, 0x2D + .byte 0x47, 0xDA, 0xA7, 0xEC, 0xAB, 0xA8, 0xE3, 0xD7, 0x4E, 0xBD, 0x2C, 0x3B, 0xE9, 0x58, 0x1E, 0x93 + .byte 0x0B, 0x9D, 0x95, 0x92, 0x78, 0x17, 0x5A, 0xC5, 0x64, 0xB0, 0xA7, 0xC3, 0xE3, 0xBA, 0x71, 0xC9 + .byte 0xE4, 0xDA, 0x75, 0x3A, 0x80, 0xDB, 0xE7, 0xFB, 0x8F, 0xDF, 0x76, 0x0C, 0xA8, 0x78, 0xD8, 0xC6 + .byte 0xA4, 0xA1, 0x20, 0x62, 0xB9, 0x0C, 0xEA, 0x82, 0x20, 0x99, 0x7F, 0x1E, 0xCB, 0xF2, 0x77, 0x39 + .byte 0x4C, 0xD0, 0xFC, 0x57, 0xF6, 0xB2, 0x78, 0x51, 0x82, 0x9C, 0x9D, 0xE7, 0x70, 0xD8, 0x17, 0x6A + .byte 0x25, 0xE9, 0x22, 0xAF, 0x05, 0xAB, 0xEE, 0x06, 0xB6, 0x34, 0xBC, 0x40, 0xDC, 0x05, 0x82, 0x21 + + thumb_func_start sub_020000E0 +sub_020000E0: ; 0x020000E0 + swi #0x12 + bx lr + thumb_func_end sub_020000E0 +_020000E4: + .byte 0x7F, 0xF0, 0x5D, 0x95, 0x2F, 0x8F, 0x87, 0xC6, 0x6F, 0x88, 0x36, 0x73 + .byte 0xC7, 0x57, 0x82, 0x11, 0x0D, 0x58, 0x3E, 0x8C, 0x0D, 0xE1, 0xE9, 0xD8, 0x13, 0x49, 0x97, 0x11 + .byte 0xB0, 0x7E, 0x30, 0x42, 0x1D, 0x23, 0x96, 0x72, 0xCE, 0x3B, 0x65, 0x29, 0xBD, 0x19, 0xAF, 0x06 + .byte 0xC0, 0x52, 0x70, 0xDA, 0xF0, 0xBB, 0x61, 0x07, 0x69, 0x6B, 0xAB, 0x01, 0xCE, 0x09, 0x28, 0x15 + .byte 0x13, 0x96, 0xEF, 0xAD, 0xB4, 0xDC, 0xCD, 0x91, 0xF5, 0x33, 0x76, 0x1E, 0x0D, 0x8F, 0xBC, 0x1A + .byte 0x26, 0x4C, 0x03, 0x81, 0x2F, 0xE1, 0x2A, 0x69, 0xC9, 0x60, 0xEC, 0xDC, 0x69, 0x44, 0xCE, 0x63 + .byte 0xC8, 0x8A, 0x7C, 0xDF, 0x28, 0x33, 0x06, 0x72, 0x3D, 0xF1, 0x5C, 0xC8, 0xA7, 0x43, 0x83, 0xA3 + .byte 0x88, 0x4E, 0xAE, 0x16, 0x3B, 0xBE, 0x54, 0x24, 0x6A, 0x7B, 0x2C, 0x20, 0xFD, 0xB8, 0xAA, 0xE0 + + thumb_func_start sub_02000160 +sub_02000160: ; 0x02000160 + swi #0xe + bx lr + thumb_func_end sub_02000160 +_02000164: + .byte 0x98, 0xE1, 0xD6, 0x85, 0xD9, 0xD9, 0x57, 0x08, 0xE5, 0x99, 0x06, 0x63 + .byte 0x8C, 0xE6, 0x69, 0xDB, 0x02, 0x7B, 0x75, 0xCA, 0xD1, 0xFE, 0x42, 0x50, 0x23, 0x7B, 0x3E, 0x1B + .byte 0xC6, 0xC4, 0x07, 0x81, 0x83, 0x53, 0x07, 0x39, 0x0B, 0xBB, 0xBC, 0x86, 0x67, 0x9A, 0x30, 0x8F + .byte 0x6B, 0x82, 0xAE, 0x00, 0x22, 0xAE, 0xFA, 0x60, 0xDA, 0x7D, 0x6A, 0x20, 0xB5, 0x60, 0xAC, 0xC5 + .byte 0xD6, 0x4B, 0x91, 0x8E, 0xA1, 0x87, 0x91, 0x2D, 0x7D, 0x20, 0x62, 0x01, 0x93, 0xAE, 0x1E, 0x10 + .byte 0xEE, 0xF1, 0x85, 0x35, 0x54, 0x82, 0x84, 0xB7, 0x67, 0xDF, 0x57, 0x04, 0xDD, 0x59, 0xAC, 0xF4 + .byte 0xC8, 0x42, 0x01, 0xDE + + thumb_func_start sub_020001C4 +sub_020001C4: ; 0x020001C4 + swi #9 + bx lr + thumb_func_end sub_020001C4 +_020001C8: + .byte 0xF8, 0x4B, 0xB2, 0xDA, 0xE3, 0xE8, 0xA0, 0x05 + .byte 0x8F, 0x8D, 0x10, 0x77, 0x7E, 0x0D, 0x52, 0xE4, 0x66, 0x59, 0x8D, 0x91, 0x35, 0x57, 0x20, 0xA7 + .byte 0xBB, 0x6E, 0x35, 0xF9, 0x73, 0x0A, 0x73, 0xFD, 0x3A, 0x2D, 0x68, 0x12, 0x9C, 0xFC, 0x46, 0x4A + .byte 0x78, 0x42, 0x42, 0x90, 0xA9, 0xF8, 0x92, 0x35, 0x48, 0x77, 0x89, 0x6E, 0x0A, 0x80, 0xB0, 0xD8 + .byte 0xDE, 0x38, 0xE8, 0xF9, 0xBC, 0xF2, 0x73, 0xCC, 0x00, 0xAB, 0xC9, 0x68, 0x6E, 0x67, 0x87, 0xD0 + .byte 0x16, 0xE8, 0x5C, 0xCC, 0x1C, 0x59, 0xBA, 0xEC, 0xA8, 0xBB, 0x67, 0x76, 0x21, 0xD1, 0xF9, 0xB6 + .byte 0xA3, 0xFC, 0xA3, 0xB0, 0xF3, 0xAC, 0x94, 0xEF, 0x8B, 0x87, 0x7D, 0xA3, 0x50, 0x31, 0x6A, 0xE1 + .byte 0x96, 0xFB, 0xF7, 0x2B, 0xC1, 0x43, 0xFC, 0xAF, 0xD4, 0xD8, 0x78, 0x96, 0x25, 0x1F, 0x2E, 0x12 + .byte 0x73, 0x77, 0x20, 0x94, 0x4C, 0xCA + + non_word_aligned_thumb_func_start sub_02000246 +sub_02000246: ; 0x02000246 + mov r2, #0 + swi #5 + bx lr + thumb_func_end sub_02000246 +_0200024C: + .byte 0xF2, 0xF2, 0x65, 0x7C + .byte 0x41, 0x43, 0x9D, 0xA1, 0x09, 0xE9, 0xD5, 0xAB, 0x6E, 0x1F, 0x30, 0x19, 0xFA, 0xCD, 0x4A, 0x8D + .byte 0x42, 0x80, 0x1F, 0x8C, 0x81, 0x09, 0x0C, 0x0C, 0x2C, 0x91, 0x3F, 0x2B, 0x98, 0x70, 0xE8, 0x0D + .byte 0x8F, 0x82, 0x82, 0x8E, 0x6A, 0x02, 0x64, 0x7D, 0xCC, 0xD2, 0xC6, 0x08, 0xC8, 0xE8, 0x9E, 0xB5 + .byte 0x08, 0x9F, 0xDF, 0xD8, 0x1A, 0x48, 0xF6, 0xA4, 0x11, 0xE0, 0xD6, 0xE1, 0xF0, 0xD5, 0x66, 0xF4 + .byte 0xFC, 0x3B, 0x69, 0x83, 0x3E, 0x63, 0x14, 0xE9, 0x54, 0xB5, 0x2F, 0xC0, 0xB9, 0x78, 0x8C, 0x88 + .byte 0x19, 0x8A, 0x09, 0x06, 0xA6, 0x48, 0x30, 0xC6, 0xA3, 0xA7, 0x62, 0xE8, 0xE0, 0x3A, 0x9C, 0xF4 + .byte 0x7E, 0xE0, 0x02, 0xF1, 0x9F, 0x74, 0x4C, 0xC3, 0xE9, 0xA9, 0x1A, 0x0A + + thumb_func_start sub_020002BC +sub_020002BC: ; 0x020002BC + swi #0x11 + bx lr + thumb_func_end sub_020002BC +_020002C0: + .byte 0x84, 0x8D, 0x66, 0x56, 0xAD, 0xC7, 0xD0, 0xDF, 0x6B, 0x85, 0x96, 0x97, 0x9E, 0x86, 0x6F, 0x36 + .byte 0xCB, 0x9F, 0x63, 0x5A, 0x81, 0xCE, 0x0E, 0xEC, 0xD3, 0xD9, 0xFB, 0x68, 0x56, 0x7A, 0xA9, 0x9F + .byte 0xA4, 0x91, 0xA1, 0x3C, 0x82, 0x19, 0x34, 0xC4, 0x6C, 0xD5, 0x7A, 0x49, 0xAD, 0xE6, 0x93, 0x1F + .byte 0xD4, 0x70, 0x8F, 0xD1, 0x45, 0x98, 0xAB, 0x3E, 0x60, 0xB4, 0x24, 0xD2, 0x79, 0x0F, 0x4F, 0x5E + .byte 0xD1, 0x21, 0x42, 0xCE, 0xC5, 0x6C, 0x3A, 0xDE, 0xC3, 0x62, 0xE4, 0x79, 0xCF, 0xA4, 0xF6, 0xE4 + .byte 0x05, 0x1E, 0x9A, 0x64, 0xBC, 0x90 + + non_word_aligned_thumb_func_start sub_02000316 +sub_02000316: ; 0x02000316 + swi #6 + bx lr + thumb_func_end sub_02000316 +_0200031A: + .byte 0xE9, 0x0E, 0xEB, 0xAE, 0xE7, 0x7D + .byte 0xBE, 0x6D, 0x80, 0xF6, 0xFD, 0x65, 0xFF, 0x34, 0x6B, 0x87, 0x74, 0x07, 0xB4, 0x58, 0xC4, 0x20 + .byte 0xF9, 0xAB, 0xF1, 0xBE, 0xC9, 0x37, 0x22, 0x2E, 0xBB, 0xE7, 0xB9, 0x60, 0x75, 0x0B, 0x70, 0xFF + .byte 0xE3, 0x1D, 0x8A, 0x4D, 0x70, 0x72, 0xA3, 0x07, 0x18, 0xE1, 0x4F, 0xC7, 0x13, 0x0C, 0x6F, 0x66 + .byte 0x63, 0x5A, 0x6F, 0x9A, 0x1D, 0xDE, 0x4C, 0xB2, 0x01, 0xA3, 0x00, 0xD2, 0xA0, 0xF3, 0x07, 0xA2 + .byte 0xF1, 0xFF, 0x90, 0xCE, 0x19, 0x2B, 0x2E, 0x99, 0xB3, 0x7B, 0x89, 0x82, 0xA5, 0xB8, 0x27, 0x31 + .byte 0xEF, 0x1D, 0xA5, 0x60, 0x81, 0xF7, 0xF2, 0xDE, 0xE1, 0x48, 0x67, 0xFF, 0xD4, 0xF1, 0xFD, 0xEA + .byte 0x04, 0x08, 0x89, 0x95, 0xE5, 0xA6, 0x4B, 0xA4, 0xB8, 0x36, 0x45, 0x22, 0x06, 0xED, 0xA0, 0xBE + .byte 0x40, 0xA4, 0x14, 0x9C, 0x64, 0x17, 0xF3, 0x81, 0x36, 0x48 + + non_word_aligned_thumb_func_start sub_0200039A +sub_0200039A: ; 0x0200039A + swi #0x14 + bx lr + thumb_func_end sub_0200039A +_0200039E: + .byte 0xCD, 0x88 + .byte 0x7F, 0x75, 0x2F, 0x90, 0x3F, 0x6E, 0x4B, 0xFD, 0xE9, 0xE3, 0x2B, 0xC6, 0xA0, 0xA9, 0xB3, 0xB8 + .byte 0x7A, 0xBD, 0x31, 0x3B, 0x04, 0x00, 0x12, 0x0B, 0xF7, 0x2A, 0x10, 0x6E, 0x5B, 0x20, 0xDE, 0x43 + .byte 0xA6, 0xBC, 0x29, 0x85, 0x94, 0x3D, 0x8F, 0x55, 0x8A, 0xC5, 0x44, 0xFF, 0x78, 0xBA, 0x7A, 0xA8 + .byte 0x1A, 0x90, 0x51, 0xDD, 0xF0, 0xF7, 0x99, 0x57, 0x42, 0xCA, 0x38, 0x29, 0xEF, 0xA1, 0x1D, 0xE7 + .byte 0xA9, 0x9F, 0xAB, 0xD8, 0x82, 0x41, 0x93, 0xD6, 0x95, 0x50, 0x24, 0xC2, 0x2D, 0xA3, 0x03, 0xDC + .byte 0x18, 0xD9, 0xE2, 0x3C, 0x19, 0x76, 0x9F, 0x35, 0xEE, 0x15, 0x81, 0xB4, 0xD5, 0x98, 0xDA, 0x4B + .byte 0x17, 0x94, 0xC8, 0x7D, 0x77, 0x97, 0x09, 0xDF, 0x08, 0x1C, 0x70, 0x47, 0xC5, 0x28, 0x19, 0xFA + .byte 0x99, 0xF2, 0xD3, 0x66, 0x98, 0xF7, 0xE7, 0x25, 0xA8, 0x9E, 0x04, 0x58, 0xA7, 0x1E, 0x80, 0xD2 + .byte 0x89, 0x8B, 0x8C, 0x9A, 0xDB, 0xEC, 0xC2, 0x64, 0x59, 0xFF, 0xAE, 0xC1, 0xD8, 0x8E, 0x42, 0x24 + .byte 0x3A, 0x72, 0x47, 0xF7, 0x5E, 0x56, 0x27, 0xE8, 0xC9, 0x85, 0x46, 0xAF, 0x2F, 0x42, 0xC4, 0x0C + .byte 0x43, 0xCA, 0x50, 0x37, 0x25, 0xC7, 0x64, 0xE2, 0x9C, 0xD5, 0x27, 0x1D, 0x49, 0xFC, 0xBE, 0xD2 + .byte 0x7B, 0x21, 0x21, 0x65, 0x8D, 0x5E, 0xAC, 0x80, 0xFF, 0xD1, 0x76, 0x5B, 0x3D, 0x64, 0x31, 0x59 + .byte 0x06, 0x03, 0x95, 0x34, 0x13, 0xA7, 0x42, 0xF6, 0x50, 0x0B + + non_word_aligned_thumb_func_start sub_0200046A +sub_0200046A: ; 0x0200046A + swi #0xf + bx lr + thumb_func_end sub_0200046A +_0200046E: + .byte 0x0C, 0x9B + .byte 0xF4, 0xF6, 0x89, 0x7B, 0xD0, 0x91, 0x6B, 0x58, 0x33, 0x76, 0x52, 0xBC, 0xEC, 0x81, 0x89, 0xD2 + .byte 0xFB, 0x4B, 0xE1, 0x85, 0x49, 0x57, 0xCC, 0xD3, 0x6F, 0x12, 0xE1, 0xEB, 0xFC, 0x58, 0x09, 0x81 + .byte 0xBE, 0x65, 0x5F, 0x36, 0xCE, 0x1F, 0xF1, 0x41, 0x0A, 0xB1, 0xA4, 0x65, 0xF5, 0xA0, 0xAA, 0x78 + .byte 0x61, 0x33, 0xBD, 0x6D, 0x27, 0x1A, 0x26, 0x08, 0xD0, 0xF5, 0x3E, 0x39, 0xC1, 0x98, 0x19, 0x58 + .byte 0x8C, 0x14, 0xD1, 0xAE, 0x10, 0xAB, 0x3A, 0xD0, 0x41, 0xF8, 0xCB, 0x40, 0x54, 0x27, 0x4C, 0xEF + .byte 0xD5, 0x54, 0x19, 0xBF + + thumb_func_start sub_020004C4 +sub_020004C4: ; 0x020004C4 + swi #0x15 + bx lr + thumb_func_end sub_020004C4 +_020004C8: + .byte 0x66, 0x02, 0x85, 0x2F, 0xD4, 0xDF, 0x62, 0x16 + .byte 0x62, 0x3B, 0xEE, 0xB9, 0x9E, 0xB9, 0x3C, 0x1D, 0xD2, 0x60, 0x9F, 0x20, 0x5A, 0xF0, 0xA7, 0xA0 + .byte 0xE4, 0xAB, 0x60, 0x4B, 0x4F, 0xA8, 0xE7, 0x07, 0x10, 0x1C, 0x5C, 0x17, 0x07, 0x11, 0xE3, 0xAA + .byte 0x37, 0x07, 0xEB, 0x11, 0x10, 0x34, 0x9D, 0xAE, 0x30, 0xF2, 0x5D, 0xB9, 0x39, 0xA8, 0xA4, 0x22 + .byte 0x12, 0x69, 0x21, 0x85, 0xA1, 0xEC, 0x13, 0xE1, 0xEF, 0xF8, 0xE9, 0xA5, 0xF2, 0xBF, 0xF6, 0x21 + .byte 0x2C, 0x45, 0x80, 0xDF, 0x1C, 0x6D, 0x7C, 0x89, 0xEA, 0xBE, 0xEA, 0x37, 0xBC, 0x88, 0x59, 0x52 + .byte 0x59, 0x93 + + non_word_aligned_thumb_func_start sub_02000522 +sub_02000522: ; 0x02000522 + swi #3 + bx lr + thumb_func_end sub_02000522 +_02000526: + .byte 0x2D, 0x0E, 0xA1, 0x2B, 0x37, 0xE7, 0xA6, 0x05, 0x06, 0xAC + .byte 0x3C, 0x78, 0x3E, 0x0D, 0xC3, 0x9F, 0xC9, 0xCA, 0xB6, 0x80, 0x2D, 0x34, 0xE7, 0x23, 0x07, 0xC7 + .byte 0x14, 0x1E, 0xCC, 0x24, 0xF3, 0xA2, 0x7C, 0x51, 0x4D, 0xE4, 0xB3, 0xCF, 0x0E, 0x5C, 0x0D, 0x43 + .byte 0x61, 0xCC, 0xD6, 0x96, 0x0A, 0xE2, 0xDC, 0xD9, 0x3F, 0x0F, 0x25, 0xE4, 0x4D, 0xE7, 0x95, 0xF7 + .byte 0xAE, 0xB0, 0x1D, 0x8A, 0x69, 0xCE, 0x72, 0x09, 0xEE, 0x00, 0x40, 0x43, 0xC5, 0x93, 0xC6, 0xB2 + + thumb_func_start sub_02000570 +sub_02000570: ; 0x02000570 + swi #0xb + bx lr + thumb_func_end sub_02000570 +_02000574: + .byte 0xBC, 0x15, 0x93, 0xAD, 0x09, 0xF3, 0xFB, 0xBA, 0xAB, 0x28, 0xCE, 0xA1 + .byte 0xDF, 0x1A, 0xD7, 0xE9, 0x80, 0xD7, 0xDD, 0xC4, 0x0D, 0xAC, 0xB2, 0x18, 0x50, 0x27, 0x88, 0x6A + .byte 0xBD, 0x44, 0x9A, 0x4C, 0xF5, 0xA4, 0x9A, 0xC5, 0x2E, 0x40, 0x20, 0x6E, 0xCE, 0xE3, 0x5E, 0x49 + .byte 0x17, 0x47, 0x47, 0xAE, 0x90, 0xD9, 0xAD, 0xDF, 0xB5, 0x3B, 0xFF, 0x7B, 0x3C, 0x9C, 0x76, 0xC8 + .byte 0x99, 0x4E, 0x6F, 0x1C, 0x66, 0x8E, 0xB0, 0x86, 0x16, 0x9A, 0xD3, 0xAC, 0x6D, 0x1E, 0xD2, 0xF6 + .byte 0xE5, 0x92, 0x1F, 0xAC, 0x25, 0x61, 0x47, 0x74, 0xA1, 0x43, 0xF9, 0x55, 0x92, 0x89, 0xF5, 0x24 + .byte 0xD6, 0x21, 0x92, 0x1E, 0xDD, 0x88, 0x72, 0x94, 0x42, 0x86, 0x45, 0x28, 0xCA, 0xE4, 0x89, 0xBF + .byte 0x15, 0xDF, 0x9F, 0x5D + + thumb_func_start sub_020005E4 +sub_020005E4: ; 0x020005E4 + swi #0x13 + bx lr + thumb_func_end sub_020005E4 +_020005E8: + .byte 0x1A, 0x2D, 0x0F, 0xF7, 0x0F, 0x7E, 0xB5, 0x68 + .byte 0x9E, 0x56, 0x70, 0x6D, 0x69, 0x08, 0x51, 0x97, 0x54, 0x30, 0x97, 0x32, 0x78, 0xD1, 0x62, 0x94 + .byte 0xFD, 0x6E, 0x67, 0x1D, 0x37, 0x9E, 0x2F, 0xFE, 0x4A, 0x38, 0x15, 0xE8, 0x5E, 0xAF, 0xC8, 0x57 + .byte 0x81, 0x44, 0x16, 0x6B, 0x6C, 0x4B, 0x57, 0x7D, 0xAC, 0x33, 0xDA, 0x4F, 0x4C, 0x74, 0xB9, 0x94 + .byte 0x1B, 0xF5, 0x76, 0x5B, 0x4A, 0xEF, 0x49, 0x7D, 0x55, 0xFB, 0x56, 0xE8, 0x4B, 0x8C, 0x03, 0x75 + .byte 0x1D, 0xFE, 0x9C, 0x8F, 0x58, 0x7B, 0x21, 0x76, 0xCE, 0xBF, 0xDA, 0xAA, 0xE6, 0xE2, 0xFE, 0x52 + .byte 0xAC, 0x3D, 0x48, 0x2B, 0x9D, 0x12, 0xE4, 0x67, 0xDA, 0x53 + + non_word_aligned_thumb_func_start sub_0200064A +sub_0200064A: ; 0x0200064A + swi #0x10 + bx lr + thumb_func_end sub_0200064A +_0200064E: + .byte 0xB9, 0x2F + .byte 0x79, 0x01, 0xAA, 0x32, 0xD5, 0x52, 0x94, 0xA4, 0xA9, 0x32, 0xBD, 0xCE, 0x70, 0xFF, 0xC6, 0x26 + .byte 0x02, 0x23, 0x0A, 0x59, 0x7A, 0x60, 0xFE, 0xC1, 0x3B, 0x5B, 0xC3, 0x6F, 0xA5, 0x43, 0xB1, 0x95 + .byte 0x7B, 0x94, 0x5C, 0xFA, 0x49, 0x06, 0x46, 0x81, 0x17, 0x13, 0x58, 0xEC, 0x13, 0xBF, 0xD1, 0x2E + .byte 0x96, 0x3D, 0x41, 0xEA, 0x0A, 0xA1, 0xE5, 0xDC, 0x8D, 0xC4, 0xEE, 0x19, 0x5F, 0x0C, 0x04, 0x04 + .byte 0x23, 0x0F, 0x5B, 0xF0, 0xCE, 0x91, 0x34, 0x7F, 0x46, 0xDD, 0x9E, 0xE4, 0x7B, 0xDF + + non_word_aligned_thumb_func_start sub_0200069E +sub_0200069E: ; 0x0200069E + mov r2, #0 + swi #4 + bx lr + thumb_func_end sub_0200069E +_020006A4: + .byte 0xE6, 0x8A, 0xFF, 0x50, 0x99, 0x98, 0xAD, 0x97, 0xA0, 0x42, 0x3F, 0x17 + .byte 0xED, 0x3B, 0x69, 0xBD, 0x2E, 0xB2, 0xF1, 0x4A, 0x4E, 0xCE, 0x4B, 0x96, 0xC9, 0xA7, 0xFD, 0x1E + .byte 0xFB, 0xDA, 0x55, 0xE2, 0x1F, 0xEC, 0x89, 0x23, 0xB6, 0xED, 0xA5, 0xC9, 0xF7, 0x3A, 0x32, 0xE8 + .byte 0x8C, 0xA2, 0x17, 0x6A, 0x47, 0x6A, 0x86, 0x43, 0xDA, 0xA5, 0x3D, 0x8A, 0x24, 0x44, 0xBA, 0xEB + .byte 0xAB, 0x09, 0x4C, 0x9C, 0x26, 0x96, 0xFC, 0x4D, 0xC8, 0xA3, 0xF8, 0x1C, 0x7E, 0xDD, 0xBF, 0xBC + .byte 0xD9, 0xAA, 0x21, 0x23, 0xB0, 0x79, 0x80, 0x1C, 0xF6, 0x3C, 0xE7, 0xEF, 0xDC, 0x16, 0x54, 0xEB + .byte 0xFC, 0xA2, 0x36, 0x8D, 0x3B, 0x80, 0x76, 0x6B + + thumb_func_start sub_02000708 +sub_02000708: ; 0x02000708 + swi #0 + bx lr + thumb_func_end sub_02000708 +_0200070C: + .byte 0xE0, 0x35, 0x0E, 0xD6 + .byte 0xF5, 0xE2, 0xAC, 0x6E, 0xAE, 0x5E, 0xD1, 0xB4, 0xD5, 0x0A, 0xAA, 0x00, 0x1A, 0x17, 0x29, 0x2A + .byte 0x6C, 0x32, 0x7C, 0x7C, 0x5E, 0x12, 0xF9, 0x35, 0x5E, 0x12, 0x52, 0xDE, 0xCB, 0x00, 0x64, 0x60 + .byte 0xD6, 0x8A, 0xB9, 0xC9, 0x0C, 0x66, 0x10, 0xA4, 0xD3, 0xED, 0xD3, 0x75, 0x4E, 0x9A, 0xF9, 0xFE + .byte 0x77, 0x47, 0x8E, 0x85, 0xCB, 0xCD, 0x20, 0xC1, 0xCD, 0x7E, 0x12, 0xC3, 0xFF, 0xFD, 0x52, 0x87 + .byte 0x5E, 0x78, 0xC1, 0xFC, 0xB0, 0x92, 0x63, 0xE9, 0xED, 0xC9, 0x6F, 0x8D, 0xBB, 0x3E, 0xC8, 0x93 + .byte 0x04, 0x09, 0x8B, 0xE8, 0xA6, 0x3B, 0xCE, 0x0D, 0xB1, 0x67, 0x66, 0xC8, 0xDB, 0xD3, 0xF5, 0x2F + .byte 0x8F, 0x4D, 0x6A, 0x99, 0x7B, 0xFA, 0x28, 0x74, 0x09, 0xF6, 0xF1, 0xD1, 0x8C, 0x15, 0xE0, 0x2C + .byte 0x59, 0xA0, 0xA6, 0xCC, 0x77, 0x45, 0x64, 0x48, 0x7A, 0x05 + + non_word_aligned_thumb_func_start sub_0200078A +sub_0200078A: ; 0x0200078A + swi #0xc + bx lr + thumb_func_end sub_0200078A +_0200078E: + .byte 0x59, 0x2B + .byte 0xE6, 0x44, 0x0B, 0xBA, 0x2A, 0xEC, 0x68, 0x4F, 0xE9, 0xF6, 0x2B, 0x5D, 0xFC, 0x5B, 0xEF, 0x02 + .byte 0xC8, 0x25, 0x05, 0xC8, 0xF7, 0x0B, 0x1A, 0xA9, 0x2E, 0x50, 0x96, 0x85, 0xAF, 0x8B, 0xE9, 0x9D + .byte 0x10, 0x4E, 0x25, 0xE6, 0xF7, 0x16, 0xE5, 0xF0, 0x3C, 0x13, 0x8D, 0x87, 0xD4, 0x18, 0x48, 0xF9 + .byte 0x85, 0x3A, 0x45, 0x68, 0x0A, 0x00, 0x73, 0xF6, 0x51, 0x07, 0x50, 0x86, 0x18, 0x76, 0x0C, 0x25 + .byte 0x3C, 0xF0, 0xFD, 0x8B, 0xB6, 0x13, 0xC9, 0xC4, 0xA2, 0x83, 0x5B, 0xE7, 0x7E, 0xC8, 0x54, 0xA3 + .byte 0x1D, 0xDF, 0x6C, 0x43, 0xD5, 0x52, 0xAA, 0x18, 0xC4, 0x6F, 0x29, 0xD2, 0x1B, 0x66, 0x82, 0xBE + .byte 0x30, 0xD4, 0x9A, 0xEB, 0x94, 0x23, 0xB6, 0x7F, 0xAC, 0x6B, 0x96, 0x95, 0xB9, 0x14, 0x00, 0x00 diff --git a/lib/syscall/asm/macros/function.inc b/lib/syscall/asm/macros/function.inc new file mode 100644 index 0000000000..6b9e5f40e8 --- /dev/null +++ b/lib/syscall/asm/macros/function.inc @@ -0,0 +1,24 @@ + .macro arm_func_start name + .balign 4, 0 + .global \name + .arm + .endm + + .macro arm_func_end name + .size \name, .-\name + .endm + + .macro thumb_func_start name + .balign 4, 0 + .global \name + .thumb + .endm + + .macro non_word_aligned_thumb_func_start name + .global \name + .thumb + .endm + + .macro thumb_func_end name + .size \name, .-\name + .endm diff --git a/main.lsf b/main.lsf new file mode 100644 index 0000000000..83a6591795 --- /dev/null +++ b/main.lsf @@ -0,0 +1,757 @@ +############################################################ +### Linker spec file for Pokemon Platinum US ### +### Pass this to the SDK's makelcf.exe utility ### +### along with the ARM9-TS template. ### +### WIP ### +############################################################ + +Static main +{ + Address 0x02000000 + StackSize 0 2048 + Object asm/arm9.o +} + +Autoload ITCM +{ + Address 0x01FF8000 + Object asm/itcm.o +} + +Autoload DTCM +{ + Address 0x027E0000 + Object asm/dtcm.o (.dtcm) + Object asm/dtcm.o (.bss) +} + +Overlay overlay0 +{ + After main + Object asm/overlay0.o +} + +Overlay overlay1 +{ + After main + Object asm/overlay1.o +} + +Overlay overlay2 +{ + After overlay1 + Object asm/overlay2.o +} + +Overlay overlay3 +{ + After ITCM + Object asm/overlay3.o +} + +Overlay overlay4 +{ + After main + Object asm/overlay4.o + +Overlay overlay5 +{ + After main + Object asm/overlay5.o +} + +Overlay overlay6 +{ + After overlay5 + Object asm/overlay6.o +} + +Overlay overlay7 +{ + After overlay6 + Object asm/overlay7.o +} + +Overlay overlay8 +{ + After overlay6 + Object asm/overlay8.o +} + +Overlay overlay9 +{ + After overlay6 + Object asm/overlay9.o +} + +Overlay overlay10 +{ + After overlay4 + Object asm/overlay10.o +} + +Overlay overlay11 +{ + After overlay4 + Object asm/overlay11.o +} + +Overlay overlay12 +{ + After overlay11 + Object asm/overlay12.o +} + +Overlay overlay13 +{ + After overlay11 + Object asm/overlay13.o +} + +Overlay overlay14 +{ + After overlay11 + Object asm/overlay14.o +} + +Overlay overlay15 +{ + After main + Object asm/overlay15.o +} + +Overlay overlay16 +{ + After overlay12 + Object asm/overlay16.o +} + +Overlay overlay17 +{ + After overlay12 + Object asm/overlay17.o +} + +Overlay overlay18 +{ + After overlay4 + Object asm/overlay18.o +} + +Overlay overlay19 +{ + After main + Object asm/overlay19.o +} + +Overlay overlay20 +{ + After main + Object asm/overlay20.o +} + +Overlay overlay21 +{ + After main + Object asm/overlay21.o +} + +Overlay overlay22 +{ + After overlay17 + Object asm/overlay22.o +} + +Overlay overlay23 +{ + After overlay5 + Object asm/overlay23.o +} + +Overlay overlay24 +{ + After overlay9 + Object asm/overlay24.o +} + +Overlay overlay25 +{ + After overlay9 + Object asm/overlay25.o +} + +Overlay overlay26 +{ + After overlay25 + Object asm/overlay26.o +} + +Overlay overlay27 +{ + After overlay25 + Object asm/overlay27.o +} + +Overlay overlay28 +{ + After overlay25 + Object asm/overlay28.o +} + +Overlay overlay29 +{ + After overlay25 + Object asm/overlay29.o +} + +Overlay overlay30 +{ + After overlay25 + Object asm/overlay30.o +} + +Overlay overlay31 +{ + After overlay25 + Object asm/overlay31.o +} + +Overlay overlay32 +{ + After overlay25 + Object asm/overlay32.o +} + +Overlay overlay33 +{ + After overlay25 + Object asm/overlay33.o +} + +Overlay overlay34 +{ + After overlay25 + Object asm/overlay34.o +} + +Overlay overlay35 +{ + After overlay25 + Object asm/overlay35.o +} + +Overlay overlay36 +{ + After overlay25 + Object asm/overlay36.o +} + +Overlay overlay37 +{ + After overlay25 + Object asm/overlay37.o +} + +Overlay overlay38 +{ + After overlay25 + Object asm/overlay38.o +} + +Overlay overlay39 +{ + After overlay25 + Object asm/overlay39.o +} + +Overlay overlay40 +{ + After overlay25 + Object asm/overlay40.o +} + +Overlay overlay41 +{ + After overlay25 + Object asm/overlay41.o +} + +Overlay overlay42 +{ + After overlay25 + Object asm/overlay42.o +} + +Overlay overlay43 +{ + After overlay25 + Object asm/overlay43.o +} + +Overlay overlay44 +{ + After overlay25 + Object asm/overlay44.o +} + +Overlay overlay45 +{ + After overlay25 + Object asm/overlay45.o +} + +Overlay overlay46 +{ + After overlay25 + Object asm/overlay46.o +} + +Overlay overlay47 +{ + After overlay25 + Object asm/overlay47.o +} + +Overlay overlay48 +{ + After overlay25 + Object asm/overlay48.o +} + +Overlay overlay49 +{ + After overlay25 + Object asm/overlay49.o +} + +Overlay overlay50 +{ + After overlay25 + Object asm/overlay50.o +} + +Overlay overlay51 +{ + After overlay25 + Object asm/overlay51.o +} + +Overlay overlay52 +{ + After overlay25 + Object asm/overlay52.o +} + +Overlay overlay53 +{ + After overlay25 + Object asm/overlay53.o +} + +Overlay overlay54 +{ + After overlay25 + Object asm/overlay54.o +} + +Overlay overlay55 +{ + After overlay25 + Object asm/overlay55.o +} + +Overlay overlay56 +{ + After overlay25 + Object asm/overlay56.o +} + +Overlay overlay57 +{ + After main + Object asm/overlay57.o +} + +Overlay overlay58 +{ + After main + Object asm/overlay58.o +} + +Overlay overlay59 +{ + After main + Object asm/overlay59.o +} + +Overlay overlay60 +{ + After overlay4 + Object asm/overlay60.o +} + +Overlay overlay61 +{ + After overlay60 + Object asm/overlay61.o +} + +Overlay overlay62 +{ + After overlay61 + Object asm/overlay62.o +} + +Overlay overlay63 +{ + After overlay60 + Object asm/overlay63.o +} + +Overlay overlay64 +{ + After overlay63 + Object asm/overlay64.o +} + +Overlay overlay65 +{ + After overlay63 + Object asm/overlay65.o +} + +Overlay overlay66 +{ + After overlay63 + Object asm/overlay66.o +} + +Overlay overlay67 +{ + After overlay66 + Object asm/overlay67.o +} + +Overlay overlay68 +{ + After overlay66 + Object asm/overlay68.o +} + +Overlay overlay69 +{ + After overlay66 + Object asm/overlay69.o +} + +Overlay overlay70 +{ + After overlay66 + Object asm/overlay70.o +} + +Overlay overlay71 +{ + After overlay12 + Object asm/overlay71.o +} + +Overlay overlay72 +{ + After overlay71 + Object asm/overlay72.o +} + +Overlay overlay73 +{ + After main + Object asm/overlay73.o +} + +Overlay overlay74 +{ + After main + Object asm/overlay74.o +} + +Overlay overlay75 +{ + After main + Object asm/overlay75.o +} + +Overlay overlay76 +{ + After overlay12 + Object asm/overlay76.o +} + +Overlay overlay77 +{ + After main + Object asm/overlay77.o +} + +Overlay overlay78 +{ + After main + Object asm/overlay78.o +} + +Overlay overlay79 +{ + After main + Object asm/overlay79.o +} + +Overlay overlay80 +{ + After main + Object asm/overlay80.o +} + +Overlay overlay81 +{ + After main + Object asm/overlay81.o +} + +Overlay overlay82 +{ + After overlay12 + Object asm/overlay82.o +} + +Overlay overlay83 +{ + After overlay82 + Object asm/overlay83.o +} + +Overlay overlay84 +{ + After overlay82 + Object asm/overlay84.o +} + +Overlay overlay85 +{ + After overlay84 + Object asm/overlay85.o +} + +Overlay overlay86 +{ + After overlay12 + Object asm/overlay86.o +} + +Overlay overlay87 +{ + After main + Object asm/overlay87.o +} + +Overlay overlay88 +{ + After overlay12 + Object asm/overlay88.o +} + +Overlay overlay89 +{ + After main + Object asm/overlay89.o +} + +Overlay overlay90 +{ + After main + Object asm/overlay90.o +} + +Overlay overlay91 +{ + After main + Object asm/overlay91.o +} + +Overlay overlay92 +{ + After main + Object asm/overlay92.o +} + +Overlay overlay93 +{ + After main + Object asm/overlay93.o +} + +Overlay overlay94 +{ + After overlay12 + Object asm/overlay94.o +} + +Overlay overlay95 +{ + After overlay94 + Object asm/overlay95.o +} + +Overlay overlay96 +{ + After overlay12 + Object asm/overlay96.o +} + +Overlay overlay97 +{ + After overlay60 + Object asm/overlay97.o +} + +Overlay overlay98 +{ + After overlay94 + Object asm/overlay98.o +} + +Overlay overlay99 +{ + After main + Object asm/overlay99.o +} + +Overlay overlay100 +{ + After main + Object asm/overlay100.o +} + +Overlay overlay101 +{ + After main + Object asm/overlay101.o +} + +Overlay overlay102 +{ + After main + Object asm/overlay102.o +} + +Overlay overlay103 +{ + After overlay63 + Object asm/overlay103.o +} + +Overlay overlay104 +{ + After overlay63 + Object asm/overlay104.o +} + +Overlay overlay105 +{ + After overlay104 + Object asm/overlay105.o +} + +Overlay overlay106 +{ + After overlay104 + Object asm/overlay106.o +} + +Overlay overlay107 +{ + After overlay104 + Object asm/overlay107.o +} + +Overlay overlay108 +{ + After overlay104 + Object asm/overlay108.o +} + +Overlay overlay109 +{ + After main + Object asm/overlay109.o +} + +Overlay overlay110 +{ + After main + Object asm/overlay110.o +} + +Overlay overlay111 +{ + After main + Object asm/overlay111.o + +Overlay overlay112 +{ + After overlay66 + Object asm/overlay112.o +} + +Overlay overlay113 +{ + After overlay66 + Object asm/overlay113.o +} + +Overlay overlay114 +{ + After overlay66 + Object asm/overlay114.o +} + +Overlay overlay115 +{ + After overlay114 + Object asm/overlay115.o +} + +Overlay overlay116 +{ + After overlay114 + Object asm/overlay116.o +} + +Overlay overlay117 +{ + After overlay114 + Object asm/overlay117.o +} + +Overlay overlay118 +{ + After main + Object asm/overlay118.o +} + +Overlay overlay119 +{ + After main + Object asm/overlay119.o +} + +Overlay overlay120 +{ + After main + Object asm/overlay120.o +} + +Overlay overlay121 +{ + After main + Object asm/overlay121.o +} + diff --git a/platform.mk b/platform.mk new file mode 100644 index 0000000000..2eb7182806 --- /dev/null +++ b/platform.mk @@ -0,0 +1,38 @@ + +WSLENV ?= no +ifeq ($(WSLENV),) +NOWINE = 1 +else +NOWINE = 0 +endif + +ifeq ($(OS),Windows_NT) +EXE := .exe +WINE := +GREP := grep -P +SED := sed -r +SHA1SUM := sha1sum +MKTEMP := mktemp +else +EXE := +WINE := wine +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) +GREP := grep -E +SED := gsed -r +SHA1SUM := shasum +MKTEMP := gmktemp +else +GREP := grep -P +SED := sed -r +SHA1SUM := sha1sum +MKTEMP := mktemp +endif +endif + +ifeq ($(NOWINE),1) +WINE := +WINPATH := wslpath +else +WINPATH := winepath +endif diff --git a/rom.rsf b/rom.rsf new file mode 100644 index 0000000000..f547bf9a45 --- /dev/null +++ b/rom.rsf @@ -0,0 +1,32 @@ +Arm9 +{ + Static $(BUILD_DIR)/main.sbin + OverlayDefs $(BUILD_DIR)/main_defs.sbin + OverlayTable $(BUILD_DIR)/main_table.sbin + Nef $(BUILD_DIR)/main.nef +} + +Arm7 +{ + Static sub/ichneumon_sub.sbin + OverlayDefs sub/ichneumon_sub_defs.sbin +} + +Property +{ + RomHeaderTemplate "$(HEADER_TEMPLATE)" + TitleName "$(TITLE_NAME)" + MakerCode "01" + RemasterVersion 0 + RomSpeedType 1TROM + RomSize 1G + RomFootPadding TRUE + BannerFile $(BNR) +} + +RomSpec +{ + Segment All + HostRoot files/ + File $(NITROFS_FILES) +} diff --git a/sub/ichneumon_sub.sbin b/sub/ichneumon_sub.sbin new file mode 100644 index 0000000000000000000000000000000000000000..58a0356bd0fc61c3c3c54319ad946a2017676a1a GIT binary patch literal 161788 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 bA@rx-U;2*lhBrCWKi9rSGHV>`UG4t@^&x2b literal 0 HcmV?d00001 diff --git a/sub/ichneumon_sub_defs.sbin b/sub/ichneumon_sub_defs.sbin new file mode 100644 index 0000000000000000000000000000000000000000..390d6153a5fd7a07510f319eedb7590fe881db37 GIT binary patch literal 16 VcmZQzuwY^U;y>j~3>}OXOaK!;0<8c5 literal 0 HcmV?d00001 diff --git a/tools/asm_processor/.gitattributes b/tools/asm_processor/.gitattributes new file mode 100644 index 0000000000..75c2cbe529 --- /dev/null +++ b/tools/asm_processor/.gitattributes @@ -0,0 +1 @@ +compile.sh text eol=lf diff --git a/tools/asm_processor/asm_processor.py b/tools/asm_processor/asm_processor.py new file mode 100644 index 0000000000..83cef1b7fd --- /dev/null +++ b/tools/asm_processor/asm_processor.py @@ -0,0 +1,1265 @@ +#!/usr/bin/env python3 +import argparse +import tempfile +import struct +import copy +import sys +import re +import os +from collections import namedtuple, defaultdict +from io import StringIO + +MAX_FN_SIZE = 3000 +SLOW_CHECKS = False + +EI_NIDENT = 16 +EI_CLASS = 4 +EI_DATA = 5 +EI_VERSION = 6 +EI_OSABI = 7 +EI_ABIVERSION = 8 +STN_UNDEF = 0 + +SHN_UNDEF = 0 +SHN_ABS = 0xfff1 +SHN_COMMON = 0xfff2 +SHN_XINDEX = 0xffff +SHN_LORESERVE = 0xff00 + +STT_NOTYPE = 0 +STT_OBJECT = 1 +STT_FUNC = 2 +STT_SECTION = 3 +STT_FILE = 4 +STT_COMMON = 5 +STT_TLS = 6 + +STB_LOCAL = 0 +STB_GLOBAL = 1 +STB_WEAK = 2 + +STV_DEFAULT = 0 +STV_INTERNAL = 1 +STV_HIDDEN = 2 +STV_PROTECTED = 3 + +SHT_NULL = 0 +SHT_PROGBITS = 1 +SHT_SYMTAB = 2 +SHT_STRTAB = 3 +SHT_RELA = 4 +SHT_HASH = 5 +SHT_DYNAMIC = 6 +SHT_NOTE = 7 +SHT_NOBITS = 8 +SHT_REL = 9 +SHT_SHLIB = 10 +SHT_DYNSYM = 11 +SHT_INIT_ARRAY = 14 +SHT_FINI_ARRAY = 15 +SHT_PREINIT_ARRAY = 16 +SHT_GROUP = 17 +SHT_SYMTAB_SHNDX = 18 +SHT_MIPS_GPTAB = 0x70000003 +SHT_MIPS_DEBUG = 0x70000005 +SHT_MIPS_REGINFO = 0x70000006 +SHT_MIPS_OPTIONS = 0x7000000d + +SHF_WRITE = 0x1 +SHF_ALLOC = 0x2 +SHF_EXECINSTR = 0x4 +SHF_MERGE = 0x10 +SHF_STRINGS = 0x20 +SHF_INFO_LINK = 0x40 +SHF_LINK_ORDER = 0x80 +SHF_OS_NONCONFORMING = 0x100 +SHF_GROUP = 0x200 +SHF_TLS = 0x400 + +R_MIPS_32 = 2 +R_MIPS_26 = 4 +R_MIPS_HI16 = 5 +R_MIPS_LO16 = 6 + + +class ElfHeader: + """ + typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; + } Elf32_Ehdr; + """ + + def __init__(self, data): + self.e_ident = data[:EI_NIDENT] + self.e_type, self.e_machine, self.e_version, self.e_entry, self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx = struct.unpack('> 4 + self.type = st_info & 15 + self.name = strtab.lookup_str(self.st_name) + self.visibility = self.st_other & 3 + + def to_bin(self): + st_info = (self.bind << 4) | self.type + return struct.pack('> 8 + self.rel_type = self.r_info & 0xff + + def to_bin(self): + self.r_info = (self.sym_index << 8) | self.rel_type + if self.sh_type == SHT_REL: + return struct.pack(' 0: + # Generate late rodata by emitting unique float constants. + # This requires 3 instructions for each 4 bytes of rodata. + # If we know alignment, we can use doubles, which give 3 + # instructions for 8 bytes of rodata. + size = self.fn_section_sizes['.late_rodata'] // 2 + skip_next = False + needs_double = (self.late_rodata_alignment != 0) + for i in range(size): + if skip_next: + skip_next = False + continue + # Jump tables give 9 instructions for >= 5 words of rodata, and should be + # emitted when: + # - -O2 or -O2 -g3 are used, which give the right codegen + # - we have emitted our first .float/.double (to ensure that we find the + # created rodata in the binary) + # - we have emitted our first .double, if any (to ensure alignment of doubles + # in shifted rodata sections) + # - we have at least 5 words of rodata left to emit (otherwise IDO does not + # generate a jump table) + # - we have at least 10 more instructions to go in this function (otherwise our + # function size computation will be wrong since the delay slot goes unused) + if (not needs_double and state.use_jtbl_for_rodata and i >= 1 and + size - i >= 5 and num_instr - len(late_rodata_fn_output) >= 10): + cases = " ".join("case {}:".format(case) for case in range(size - i)) + late_rodata_fn_output.append("switch (*(volatile int*)0) { " + cases + " ; }") + late_rodata_fn_output.extend([""] * 8) + jtbl_rodata_size = (size - i) * 4 + break + dummy_bytes = state.next_late_rodata_hex() + late_rodata_dummy_bytes.append(dummy_bytes) + if self.late_rodata_alignment == 4 * ((i + 1) % 2 + 1) and i + 1 < size: + dummy_bytes2 = state.next_late_rodata_hex() + late_rodata_dummy_bytes.append(dummy_bytes2) + fval, = struct.unpack(' 0 or late_rodata_fn_output: + text_name = state.make_name('func') + src[0] = 'int {}(void) {{ return '.format(text_name) + instr_count = self.fn_section_sizes['.text'] // 2 + src[self.num_lines] = '((volatile void *) 0); }; ' if instr_count > 1 else '; }; ' + if instr_count < state.min_instr_count: + self.fail("too short .text block") + tot_emitted = 0 + tot_skipped = 0 + fn_emitted = 0 + fn_skipped = 0 + rodata_stack = late_rodata_fn_output[::-1] + for (line, count) in self.fn_ins_inds: + for _ in range(count): + if (fn_emitted > MAX_FN_SIZE and instr_count - tot_emitted > state.min_instr_count and + (not rodata_stack or rodata_stack[-1])): + # Don't let functions become too large. When a function reaches 284 + # instructions, and -O2 -framepointer flags are passed, the IRIX + # compiler decides it is a great idea to start optimizing more. + fn_emitted = 0 + fn_skipped = 0 + src[line] += '((volatile void *) 0); }} int {}(void) {{ return '.format(state.make_name('large_func')) + if fn_skipped < state.skip_instr_count: + fn_skipped += 1 + tot_skipped += 1 + elif rodata_stack: + src[line] += rodata_stack.pop() + else: + src[line] += '*(int *)' + tot_emitted += 1 + fn_emitted += 1 + if rodata_stack: + size = len(late_rodata_fn_output) // 3 + available = instr_count - tot_skipped + self.fail( + "late rodata to text ratio is too high: {} / {} must be <= 1/3\n" + "add .late_rodata_alignment (4|8) to the .late_rodata " + "block to double the allowed ratio." + .format(size, available)) + + init_name = None + if self.fn_section_sizes['.init'] > 0 or late_rodata_fn_output: + init_name = state.make_name('func') + src[0] = 'int {}(void) {{ return '.format(init_name) + instr_count = self.fn_section_sizes['.init'] // 2 + src[self.num_lines] = '((volatile void *) 0); }; ' if instr_count else '; }; ' + if instr_count < state.min_instr_count: + self.fail("too short .init block") + tot_emitted = 0 + tot_skipped = 0 + fn_emitted = 0 + fn_skipped = 0 + rodata_stack = late_rodata_fn_output[::-1] + for (line, count) in self.fn_ins_inds: + for _ in range(count): + if (fn_emitted > MAX_FN_SIZE and instr_count - tot_emitted > state.min_instr_count and + (not rodata_stack or rodata_stack[-1])): + # Don't let functions become too large. When a function reaches 284 + # instructions, and -O2 -framepointer flags are passed, the IRIX + # compiler decides it is a great idea to start optimizing more. + fn_emitted = 0 + fn_skipped = 0 + src[line] += '((volatile void *) 0); }} int {}(void) {{ return '.format(state.make_name('large_func')) + if fn_skipped < state.skip_instr_count: + fn_skipped += 1 + tot_skipped += 1 + elif rodata_stack: + src[line] += rodata_stack.pop() + else: + src[line] += '*(int *)' + tot_emitted += 1 + fn_emitted += 1 + if rodata_stack: + size = len(late_rodata_fn_output) // 3 + available = instr_count - tot_skipped + self.fail( + "late rodata to init ratio is too high: {} / {} must be <= 1/3\n" + "add .late_rodata_alignment (4|8) to the .late_rodata " + "block to double the allowed ratio." + .format(size, available)) + + rodata_name = None + if self.fn_section_sizes['.rodata'] > 0: + rodata_name = state.make_name('rodata') + src[self.num_lines] += f" const char {rodata_name}[{self.fn_section_sizes['.rodata']}] = {{1}};" + + data_name = None + if self.fn_section_sizes['.data'] > 0: + data_name = state.make_name('data') + src[self.num_lines] += f" char {data_name}[{self.fn_section_sizes['.data']}] = {{1}};" + + bss_name = None + if self.fn_section_sizes['.bss'] > 0: + bss_name = state.make_name('bss') + src[self.num_lines] += f" char {bss_name}[{self.fn_section_sizes['.bss']}];" + + sdata_name = None # sdata is like data but small + if self.fn_section_sizes['.sdata'] > 0: + sdata_code = "" + for i in range(self.fn_section_sizes['.sdata']): + sdata_name = state.make_name('sdata') + sdata_code += f" char {sdata_name} = 1;" + src[self.num_lines] += sdata_code + + sdata2_name = None # sdata2 is like rodata but small + if self.fn_section_sizes['.sdata2'] > 0: + sdata2_code = "" + for i in range(self.fn_section_sizes['.sdata2']): + sdata2_name = state.make_name('sdata2') + sdata2_code += f" const char {sdata2_name} = 1;" + src[self.num_lines] += sdata2_code + + sbss_name = None # Similarly, sbss is like uninitialized data but small + if self.fn_section_sizes['.sbss'] > 0: + sbss_code = "" + for i in range(self.fn_section_sizes['.sbss']): + sbss_name = state.make_name('sbss') + sbss_code += f" char {sbss_name};" + src[self.num_lines] += sbss_code + + """ sbss2 is currently borked + sbss2_name = None # Similarly, sbss2 is like uninitialized rodata but small + if self.fn_section_sizes['.sbss2'] > 0: + sbss2_code = "" + for i in range(self.fn_section_sizes['.sbss2']): + sbss2_name = state.make_name('sbss2') + sbss2_code += f" const char {sbss2_name};" + src[self.num_lines] += sbss2_code + """ + + fn = Function( + text_glabels=self.text_glabels, + asm_conts=self.asm_conts, + late_rodata_dummy_bytes=late_rodata_dummy_bytes, + jtbl_rodata_size=jtbl_rodata_size, + late_rodata_asm_conts=self.late_rodata_asm_conts, + fn_desc=self.fn_desc, + data={ + '.text': (text_name, self.fn_section_sizes['.text']), + '.data': (data_name, self.fn_section_sizes['.data']), + '.rodata': (rodata_name, self.fn_section_sizes['.rodata']), + '.bss': (bss_name, self.fn_section_sizes['.bss']), + '.sdata': (sdata_name, self.fn_section_sizes['.sdata']), + '.sdata2': (sdata2_name, self.fn_section_sizes['.sdata2']), + '.sbss': (sbss_name, self.fn_section_sizes['.sbss']), + #'.sbss2': (sbss2_name, self.fn_section_sizes['.sbss2']), + }) + return src, fn + +cutscene_data_regexpr = re.compile(r"CutsceneData (.|\n)*\[\] = {") +float_regexpr = re.compile(r"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f") + +def repl_float_hex(m): + return str(struct.unpack(" 0 + n_text = objfile.text_section_index(temp_name) + loc = objfile.symtab.find_symbol(temp_name) + if loc is None: + ifdefed = True + break + loc = loc[1] + prev_loc = prev_locs[sectype + (str(n_text) if sectype == '.text' else '')] + if loc < prev_loc: + raise Failure("Wrongly computed size for section {} (diff {}). This is an asm-processor bug!".format(sectype + (str(n_text) if sectype == '.text' else ''), prev_loc- loc)) + if loc != prev_loc: + asm.append('.section ' + sectype) + if sectype == '.text': + for i in range((loc - prev_loc) // 2): + asm.append('nop') + else: + asm.append('.space {}'.format(loc - prev_loc)) + to_copy[sectype + (str(n_text) if sectype == '.text' else '')].append((loc, size, temp_name, function.fn_desc, [])) + prev_locs[sectype + (str(n_text) if sectype == '.text' else '')] = loc + size + if not ifdefed: + all_text_glabels.update(function.text_glabels) + all_late_rodata_dummy_bytes.append(function.late_rodata_dummy_bytes) + all_jtbl_rodata_size.append(function.jtbl_rodata_size) + late_rodata_asm.append(function.late_rodata_asm_conts) + for sectype, (temp_name, size) in function.data.items(): + if temp_name is not None: + asm.append('.section ' + sectype) + asm.append('.global ' + temp_name + '_asm_start') + asm.append(temp_name + '_asm_start:') + asm.append('.section .text') + for line in function.asm_conts: + asm.append(line) + for sectype, (temp_name, size) in function.data.items(): + if temp_name is not None: + #asm.append('.section ' + sectype) + asm.append('.global ' + temp_name + '_asm_end') + asm.append(temp_name + '_asm_end:') + + if any(late_rodata_asm): + late_rodata_source_name_start = '_asmpp_late_rodata_start' + late_rodata_source_name_end = '_asmpp_late_rodata_end' + asm.append('.rdata') + asm.append('glabel {}'.format(late_rodata_source_name_start)) + for conts in late_rodata_asm: + asm.extend(conts) + asm.append('glabel {}'.format(late_rodata_source_name_end)) + + o_file = open("asm_processor_temp.o", 'w').close() # Create temp file. tempfile module isn't working for me. + o_name = "asm_processor_temp.o" + + s_file = open("asm_processor_temp.s", 'wb') # Ditto. + s_name = "asm_processor_temp.s" + try: + s_file.write(asm_prelude + b'\n') + for line in asm: + s_file.write(line.encode(output_enc) + b'\n') + s_file.close() + ret = os.system(assembler + " " + s_name + " -o " + o_name) + if ret != 0: + raise Failure("failed to assemble") + with open(o_name, 'rb') as f: + asm_objfile = ElfFile(f.read()) + + # Remove some clutter from objdump output + objfile.drop_irrelevant_sections() + + """ + # Unify reginfo sections + target_reginfo = objfile.find_section('.reginfo') + source_reginfo_data = list(asm_objfile.find_section('.reginfo').data) + data = list(target_reginfo.data) + for i in range(20): + data[i] |= source_reginfo_data[i] + target_reginfo.data = bytes(data) + """ + + # Move over section contents + modified_text_positions = set() + jtbl_rodata_positions = set() + last_rodata_pos = 0 + n_text = 0 + for sec in objfile.sections: + sectype = sec.name + if not to_copy[sectype + (str(n_text) if sectype == '.text' else '')]: + if sectype == '.text': + n_text += 1 + continue + # This should work as long as you NONMATCH whole functions rather than asm fragments + func = to_copy[sectype + str(n_text) if sectype == '.text' else ''][0][2] + asm_n_text = asm_objfile.text_section_index(func + '_asm_start') + source = asm_objfile.find_section(sectype, asm_n_text if sectype == '.text' else 0) + assert source is not None, "didn't find source section: " + sectype + for (pos, count, temp_name, fn_desc, fn_name) in to_copy[sectype + (str(n_text) if sectype == '.text' else '')]: + loc1 = asm_objfile.symtab.find_symbol_in_section(temp_name + '_asm_start', source) + loc2 = asm_objfile.symtab.find_symbol_in_section(temp_name + '_asm_end', source) + assert loc1 == pos, "assembly and C files don't line up for section " + sectype + ", " + fn_desc + # Since we are nonmatching whole functions, we don't need to insert the correct + # amount of padding into the src file. We don't actually need to insert padding + # at all. We can just plop the asm's text section into the objfile. + # if loc2 - loc1 != count: + # raise Failure("incorrectly computed size for section " + sectype + ", " + fn_desc + ". If using .double, make sure to provide explicit alignment padding.") + if sectype == '.bss' or sectype == '.sbss2': + continue + target = objfile.find_section(sectype, n_text if sectype == '.text' else 0) + assert target is not None, "missing target section of type " + sectype + data = list(target.data) + for (pos, count, _, _, _) in to_copy[sectype + (str(n_text) if sectype == '.text' else '')]: + # mwasmarm 4-aligns text sections, so make sure to copy exactly `count` bytes + data[pos:pos + count] = source.data[pos:pos + count] + if sectype == '.text': + assert count % 2 == 0 + assert pos % 2 == 0 + for i in range(count // 2): + modified_text_positions.add(pos + 2 * i) + elif sectype == '.rodata': + last_rodata_pos = pos + count + target.data = bytes(data) + if sectype == '.text': + n_text += 1 + + # Move over late rodata. This is heuristic, sadly, since I can't think + # of another way of doing it. + moved_late_rodata = {} + if any(all_late_rodata_dummy_bytes) or any(all_jtbl_rodata_size): + source = asm_objfile.find_section('.rodata', 0) + target = objfile.find_section('.rodata', 0) + source_pos = asm_objfile.symtab.find_symbol_in_section(late_rodata_source_name_start, source) + source_end = asm_objfile.symtab.find_symbol_in_section(late_rodata_source_name_end, source) + if source_end - source_pos != sum(map(len, all_late_rodata_dummy_bytes)) * 2 + sum(all_jtbl_rodata_size): + raise Failure("computed wrong size of .late_rodata") + new_data = list(target.data) + for dummy_bytes_list, jtbl_rodata_size in zip(all_late_rodata_dummy_bytes, all_jtbl_rodata_size): + for index, dummy_bytes in enumerate(dummy_bytes_list): + pos = target.data.index(dummy_bytes, last_rodata_pos) + # This check is nice, but makes time complexity worse for large files: + if SLOW_CHECKS and target.data.find(dummy_bytes, pos + 2) != -1: + raise Failure("multiple occurrences of late_rodata hex magic. Change asm-processor to use something better than 0xE0123456!") + if index == 0 and len(dummy_bytes_list) > 1 and target.data[pos+2:pos+8] == b'\0\0\0\0': + # Ugly hack to handle double alignment for non-matching builds. + # We were told by .late_rodata_alignment (or deduced from a .double) + # that a function's late_rodata started out 4 (mod 8), and emitted + # a float and then a double. But it was actually 0 (mod 8), so our + # double was moved by 4 bytes. To make them adjacent to keep jump + # tables correct, move the float by 4 bytes as well. + new_data[pos:pos+2] = b'\0\0\0\0' + pos += 2 + new_data[pos:pos+2] = source.data[source_pos:source_pos+2] + moved_late_rodata[source_pos] = pos + last_rodata_pos = pos + 2 + source_pos += 2 + if jtbl_rodata_size > 0: + assert dummy_bytes_list, "should always have dummy bytes before jtbl data" + pos = last_rodata_pos + new_data[pos : pos + jtbl_rodata_size] = \ + source.data[source_pos : source_pos + jtbl_rodata_size] + for i in range(0, jtbl_rodata_size, 2): + moved_late_rodata[source_pos + i] = pos + i + jtbl_rodata_positions.add(pos + i) + last_rodata_pos += jtbl_rodata_size + source_pos += jtbl_rodata_size + target.data = bytes(new_data) + + # Merge strtab data. + strtab_adj = len(objfile.symtab.strtab.data) + objfile.symtab.strtab.data += asm_objfile.symtab.strtab.data + + # Find relocated symbols + relocated_symbols = set() + for obj in [asm_objfile, objfile]: + for sec in obj.sections: + for reltab in sec.relocated_by: + for rel in reltab.relocations: + relocated_symbols.add(obj.symtab.symbol_entries[rel.sym_index]) + + # 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 + # 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)] + n_text = 0 + for i, s in enumerate(asm_objfile.symtab.symbol_entries): + is_local = (i < asm_objfile.symtab.sh_info) + if is_local and s not in relocated_symbols: + continue + if is_temp_name(s.name): + continue + if s.st_shndx not in [SHN_UNDEF, SHN_ABS]: + section_name = asm_objfile.sections[s.st_shndx].name + if section_name not in SECTIONS: + raise Failure("generated assembly .o must only have symbols for .text, .data, .rodata, .sdata, .sdata2, .sbss, ABS and UNDEF, but found " + section_name) + if section_name == '.sbss2': #! I'm not sure why this isn't working + continue + obj_n_text = objfile.text_section_index(temp_name) + s.st_shndx = objfile.find_section(section_name, obj_n_text if section_name == '.text' else 0).index + if section_name == '.text': + n_text += 1 + # 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 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 + if is_local: + new_local_syms.append(s) + else: + new_global_syms.append(s) + new_syms = new_local_syms + new_global_syms + for i, s in enumerate(new_syms): + s.new_index = i + objfile.symtab.data = b''.join(s.to_bin() for s in new_syms) + objfile.symtab.sh_info = len(new_local_syms) + + # Move over relocations + n_text = 0 + for sec in objfile.sections: + sectype = sec.name + # This should work as long as you NONMATCH whole functions rather than asm fragments + target = objfile.find_section(sectype, n_text if sectype == '.text' else 0) + + if target is not None: + # fixup relocation symbol indices, since we butchered them above + for reltab in target.relocated_by: + nrels = [] + for rel in reltab.relocations: + if (sectype == '.rodata' and rel.r_offset in jtbl_rodata_positions) or sectype == ".sbss2": + # don't include relocations for late_rodata dummy code + continue + # hopefully we don't have relocations for local or + # temporary symbols, so new_index exists + rel.sym_index = objfile.symtab.symbol_entries[rel.sym_index].new_index + nrels.append(rel) + reltab.relocations = nrels + reltab.data = b''.join(rel.to_bin() for rel in nrels) + + if not to_copy[sectype + (str(n_text) if sectype == '.text' else '')]: + if sectype == '.text': + n_text += 1 + continue + + func = to_copy[sectype + str(n_text) if sectype == '.text' else ''][0][2] + asm_n_text = asm_objfile.text_section_index(func + '_asm_start') + source = asm_objfile.find_section(sectype, asm_n_text if sectype == '.text' else 0) + if not source: + if sectype == '.text': + n_text += 1 + continue + + target_reltab = objfile.find_section('.rel' + sectype, n_text if sectype == '.text' else 0) + target_reltaba = objfile.find_section('.rela' + sectype, n_text if sectype == '.text' else 0) + for reltab in source.relocated_by: + for rel in reltab.relocations: + rel.sym_index = asm_objfile.symtab.symbol_entries[rel.sym_index].new_index + if sectype == '.rodata' and rel.r_offset in moved_late_rodata: + rel.r_offset = moved_late_rodata[rel.r_offset] + new_data = b''.join(rel.to_bin() for rel in reltab.relocations) + if reltab.sh_type == SHT_REL: + target_reltab = objfile.add_section('.rel' + sectype, + sh_type=SHT_REL, sh_flags=0, + sh_link=objfile.symtab.index, sh_info=target.index, + sh_addralign=4, sh_entsize=8, data=b'') + target_reltab.data += new_data + else: + # Always append as a separate .rela.text section + target_reltaba = objfile.add_section('.rela' + sectype, + sh_type=SHT_RELA, sh_flags=0, + sh_link=objfile.symtab.index, sh_info=target.index, + sh_addralign=4, sh_entsize=12, data=b'') + target_reltaba.data += new_data + if sectype == '.text': + n_text += 1 + + objfile.write(objfile_name) + finally: + s_file.close() + #os.remove(s_name) + try: + pass + #os.remove(o_name) + except: + pass + +def run_wrapped(argv, outfile): + 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") + parser.add_argument('--assembler', dest='assembler', help="assembler command (e.g. \"mips-linux-gnu-as -march=vr4300 -mabi=32\")") + parser.add_argument('--asm-prelude', dest='asm_prelude', help="path to a file containing a prelude to the assembly file (with .set and .macro directives, e.g.)") + parser.add_argument('--input-enc', default='latin1', help="Input encoding (default: latin1)") + parser.add_argument('--output-enc', default='latin1', help="Output encoding (default: latin1)") + parser.add_argument('-framepointer', dest='framepointer', action='store_true') + parser.add_argument('-g3', dest='g3', action='store_true') + group = parser.add_mutually_exclusive_group(required=False) + group.add_argument('-O1', dest='opt', action='store_const', const='O1') + group.add_argument('-O2', dest='opt', action='store_const', const='O2') + group.add_argument('-g', dest='opt', action='store_const', const='g') + args = parser.parse_args(argv) + opt = args.opt + if args.g3: + if opt != 'O2': + raise Failure("-g3 is only supported together with -O2") + opt = 'g3' + + 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) + 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 not functions: + return + asm_prelude = b'' + if args.asm_prelude: + with open(args.asm_prelude, 'rb') as f: + asm_prelude = f.read() + fixup_objfile(args.objfile, functions, asm_prelude, args.assembler, args.output_enc) + +def run(argv, outfile=sys.stdout.buffer): + try: + run_wrapped(argv, outfile) + except Failure as e: + print("Error:", e, file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + run(sys.argv[1:]) diff --git a/tools/asm_processor/compile.sh b/tools/asm_processor/compile.sh new file mode 100644 index 0000000000..b27d7a3277 --- /dev/null +++ b/tools/asm_processor/compile.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +CC="$1" +AS="$2" +OBJ="$3" +SRC="$4" + +BUILD_FILENAME=${OBJ%.o} + +PADDED_SRC="$(mktemp --suffix=.c padded-XXXXXX)" +PADDED_OBJ="$(mktemp --suffix=.o padded-XXXXXX)" +PADDED_D="$(mktemp --suffix=.d padded-XXXXXX)" + +# Create a .c file replacing the nonmatching function with volatile int writes, +# and compile. +./tools/asm_processor/asm_processor.py "$SRC" --assembler "$AS" > "$PADDED_SRC" +$CC -c "$PADDED_SRC" -o "$PADDED_OBJ" + +# Inject the matching assembly into the padded obj file. +./tools/asm_processor/asm_processor.py "$SRC" --post-process "$PADDED_OBJ" --assembler "$AS" + +arm-none-eabi-objcopy --remove-section .comment "$PADDED_OBJ" "$OBJ" +if [[ $CC == *"gccdep"* ]]; then + cp "$PADDED_D" "${BUILD_FILENAME}.d" +fi + +rm padded-* +rm output.txt +rm asm_processor_temp.s +rm asm_processor_temp.o diff --git a/tools/fixrom/.gitignore b/tools/fixrom/.gitignore new file mode 100644 index 0000000000..8ab48d98f9 --- /dev/null +++ b/tools/fixrom/.gitignore @@ -0,0 +1 @@ +fixrom diff --git a/tools/fixrom/Makefile b/tools/fixrom/Makefile new file mode 100644 index 0000000000..7a3b5e0da9 --- /dev/null +++ b/tools/fixrom/Makefile @@ -0,0 +1,13 @@ +CC := gcc +CFLAGS := -O3 + +.PHONY: all clean + +all: fixrom + @: + +fixrom: fixrom.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + $(RM) fixrom fixrom.exe diff --git a/tools/fixrom/fixrom.c b/tools/fixrom/fixrom.c new file mode 100644 index 0000000000..6c5f5da12e --- /dev/null +++ b/tools/fixrom/fixrom.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include + +#define HEADER_SIZE 0x4000 + +uint8_t RomHeader[HEADER_SIZE]; + +static inline noreturn __attribute__((format(printf, 1, 2))) void fatal_error(const char * message, ...) +{ + va_list va_args; + va_start(va_args, message); + fputs("Error: ", stderr); + vfprintf(stderr, message, va_args); + fputc('\n', stderr); + va_end(va_args); + exit(EXIT_FAILURE); +} + +static inline void OffsetCheck(int offset) +{ + if (offset < 0 || offset >= HEADER_SIZE) + { + fatal_error("illegal access to header+%d", offset); + } +} + +static inline uint16_t HeaderReadU16LE(int offset) +{ + OffsetCheck(offset); + return RomHeader[offset] | + (RomHeader[offset + 1] << 8); +} + +static inline uint32_t HeaderReadU32LE(int offset) +{ + OffsetCheck(offset); + return RomHeader[offset] | + (RomHeader[offset + 1] << 8) | + (RomHeader[offset + 2] << 16) | + (RomHeader[offset + 3] << 24); +} + +static inline void HeaderWriteU16LE(int offset, uint16_t value) +{ + OffsetCheck(offset); + RomHeader[offset] = value; + RomHeader[offset + 1] = value >> 8; +} +static inline void HeaderWriteU32LE(int offset, uint32_t value) +{ + OffsetCheck(offset); + RomHeader[offset] = value; + RomHeader[offset + 1] = value >> 8; + RomHeader[offset + 2] = value >> 16; + RomHeader[offset + 3] = value >> 24; +} + +static uint16_t Calc_CRC16(uint8_t * data, size_t length, uint16_t crc) +{ + static uint16_t CrcTable[16] = { + 0x0000, + 0xCC01, + 0xD801, + 0x1400, + 0xF001, + 0x3C00, + 0x2800, + 0xE401, + 0xA001, + 0x6C00, + 0x7800, + 0xB401, + 0x5000, + 0x9C01, + 0x8801, + 0x4400, + }; + + uint16_t x = 0; + uint16_t y; + uint16_t bit = 0; + uint8_t * end = data + length; + while (data < end) + { + if (bit == 0) + { + x = data[0] | (data[1] << 8); + } + y = CrcTable[crc & 15]; + crc >>= 4; + crc ^= y; + y = CrcTable[(x >> bit) & 15]; + crc ^= y; + bit += 4; + if (bit == 16) + { + data += 2; + bit = 0; + } + } + return crc; +} + +int main(int argc, char ** argv) +{ + uint16_t secure_crc = 0xFFFF; + char game_code[4] = "NTRJ"; + int override_crc = 0; + int override_code = 0; + FILE * rom = NULL; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--secure-crc") == 0) + { + if (override_crc) + { + fatal_error("multiple --secure-crc options specified"); + } + char * endptr; + unsigned long secure_crc_l = strtoul(argv[++i], &endptr, 0); + if (secure_crc_l == 0 && endptr == argv[i]) + { + fatal_error("argument to --secure-crc must be an integer"); + } + if (secure_crc_l >= 0x10000) + { + fatal_error("argument to --secure-crc must be a 16-bit integer"); + } + secure_crc = secure_crc_l; + override_crc = 1; + } + else if (strcmp(argv[i], "--game-code") == 0) + { + if (override_code) + { + fatal_error("multiple --game-code options specified"); + } + if (strlen(argv[++i]) > 4) + { + fatal_error("argument to --game-code must be 4 characters or fewer"); + } + strncpy(game_code, argv[i], 4); + override_code = 1; + } + else + { + if (rom != NULL) + { + fatal_error("unrecognized %s argument: %s", argv[i][0] == '-' ? "flag" : "positional", argv[i]); + } + rom = fopen(argv[i], "r+b"); + if (rom == NULL) + { + fatal_error(argv[i][0] == '-' ? "unrecognized flag argument: %s" : "unable to open file '%s' for reading", argv[i]); + } + } + } + + if (fread(RomHeader, 1, HEADER_SIZE, rom) != HEADER_SIZE) + { + fatal_error("error reading the ROM header"); + } + + if (override_crc) + { + HeaderWriteU16LE(0x6C, secure_crc); + } + + if (override_code) + { + memcpy(RomHeader + 0xC, game_code, 4); + } + + uint16_t header_crc = Calc_CRC16((uint8_t *)RomHeader, 0x15E, 0xFFFF); + HeaderWriteU16LE(0x15E, header_crc); + + fseek(rom, 0, SEEK_SET); + if (fwrite(RomHeader, 1, HEADER_SIZE, rom) != HEADER_SIZE) + { + fatal_error("error writing the ROM header"); + } + + fclose(rom); + return EXIT_SUCCESS; +} diff --git a/tools/gen_fx_consts/.gitignore b/tools/gen_fx_consts/.gitignore new file mode 100644 index 0000000000..d091085c0e --- /dev/null +++ b/tools/gen_fx_consts/.gitignore @@ -0,0 +1 @@ +gen_fx_consts diff --git a/tools/gen_fx_consts/Makefile b/tools/gen_fx_consts/Makefile new file mode 100644 index 0000000000..9c9d3f4ef8 --- /dev/null +++ b/tools/gen_fx_consts/Makefile @@ -0,0 +1,31 @@ +CC = gcc +CFLAGS := -O3 -g -DNDEBUG -DSOURCE_DIR="\"$(CURDIR)\"" + +SRCS := main.c fx_encode.c fx_data.c +OBJS := $(SRCS:%.c=%.o) + +prog := gen_fx_consts + +.PHONY: all clean + +DEPDIR := .deps +DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d + +all: $(prog) + @: + +clean: ; $(RM) -r $(DEPDIR) $(OBJS) $(prog) $(prog).exe + +$(prog): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $^ + +$(OBJS): %.o: %.c +$(OBJS): %.o: %.c $(DEPDIR)/%.d | $(DEPDIR) + $(CC) $(CFLAGS) $(DEPFLAGS) -c -o $@ $< + +$(DEPDIR): ; mkdir -p $@ + +DEPFILES := $(SRCS:%.c=$(DEPDIR)/%.d) +$(DEPFILES): + +include $(wildcard $(DEPFILES)) diff --git a/tools/gen_fx_consts/fx.h b/tools/gen_fx_consts/fx.h new file mode 100644 index 0000000000..93984e02d0 --- /dev/null +++ b/tools/gen_fx_consts/fx.h @@ -0,0 +1,31 @@ +#ifndef GUARD_FX_H +#define GUARD_FX_H + +#include + +struct FxSpec { + const char *name; + int intpart; + int fracpart; +}; + +struct FxConst { + char *name; + const struct FxSpec *fxtype; + double value; +}; + +extern struct FxConst *gFxConstTable; +extern size_t gNFxConstTable; + +// Init and delete the above two objects +void FxConstTableInit(void); +void FxConstTableEnd(void); + +// Generates the integer value of the fixed-point value represented by FxConst* +long long MakeFix(const struct FxConst *constdef); + +// For FxSpec*, get the width in bits. +int GetFxWidth(const struct FxSpec *fxtype); + +#endif //GUARD_FX_H diff --git a/tools/gen_fx_consts/fx_const.csv b/tools/gen_fx_consts/fx_const.csv new file mode 100644 index 0000000000..713aaa33f4 --- /dev/null +++ b/tools/gen_fx_consts/fx_const.csv @@ -0,0 +1,862 @@ +#name,type,value +E,fx64c,2.7182818284590452354 +LOG2E,fx64c,1.4426950408889634074 +LOG10E,fx64c,0.43429448190325182765 +LN2,fx64c,0.69314718055994530942 +LN10,fx64c,2.30258509299404568402 +PI,fx64c,3.14159265358979323846 +TWOPI,fx64c,6.28318530717958647692 +PI_2,fx64c,1.57079632679489661923 +PI_4,fx64c,0.78539816339744830962 +3PI_4,fx64c,2.3561944901923448370 +SQRTPI,fx64c,1.77245385090551602793 +1_PI,fx64c,0.31830988618379067154 +2_PI,fx64c,0.63661977236758134308 +2_SQRTPI,fx64c,1.12837916709551257390 +SQRT2,fx64c,1.41421356237309504880 +SQRT1_2,fx64c,0.70710678118654752440 +SQRT3,fx64c,1.73205080756887719000 +SQRT1_3,fx64c,0.57735026918962576450914878050196 +IVLN10,fx64c,0.43429448190325182765 +LOG2_E,fx64c,0.693147180559945309417 +INVLN2,fx64c,1.4426950408889633870 +1_360,fx64c,0.0027777777777777777778 +TWOPI_360,fx64c,0.0174532925199432957692 +360_TWOPI,fx64c,57.295779513082320876846 +65536_TWOPI,fx64c,10430.378350470452724958342593503 +65536_360,fx64c,182.04444444444444444444444444444 +TWOPI_65536,fx64c,0.0000958737992428525768572998046875 +360_65536,fx64c,0.0054931640625 +1_100,fx64c,0.01000000000000000000 +2_100,fx64c,0.02000000000000000000 +3_100,fx64c,0.03000000000000000000 +4_100,fx64c,0.04000000000000000000 +5_100,fx64c,0.05000000000000000000 +6_100,fx64c,0.06000000000000000000 +7_100,fx64c,0.07000000000000000000 +8_100,fx64c,0.08000000000000000000 +9_100,fx64c,0.09000000000000000000 +10_100,fx64c,0.10000000000000000000 +11_100,fx64c,0.11000000000000000000 +12_100,fx64c,0.12000000000000000000 +13_100,fx64c,0.13000000000000000000 +14_100,fx64c,0.14000000000000000000 +15_100,fx64c,0.15000000000000000000 +16_100,fx64c,0.16000000000000000000 +17_100,fx64c,0.17000000000000000000 +18_100,fx64c,0.18000000000000000000 +19_100,fx64c,0.19000000000000000000 +20_100,fx64c,0.20000000000000000000 +21_100,fx64c,0.21000000000000000000 +22_100,fx64c,0.22000000000000000000 +23_100,fx64c,0.23000000000000000000 +24_100,fx64c,0.24000000000000000000 +25_100,fx64c,0.25000000000000000000 +26_100,fx64c,0.26000000000000000000 +27_100,fx64c,0.27000000000000000000 +28_100,fx64c,0.28000000000000000000 +29_100,fx64c,0.29000000000000000000 +30_100,fx64c,0.30000000000000000000 +31_100,fx64c,0.31000000000000000000 +32_100,fx64c,0.32000000000000000000 +33_100,fx64c,0.33000000000000000000 +34_100,fx64c,0.34000000000000000000 +35_100,fx64c,0.35000000000000000000 +36_100,fx64c,0.36000000000000000000 +37_100,fx64c,0.37000000000000000000 +38_100,fx64c,0.38000000000000000000 +39_100,fx64c,0.39000000000000000000 +40_100,fx64c,0.40000000000000000000 +41_100,fx64c,0.41000000000000000000 +42_100,fx64c,0.42000000000000000000 +43_100,fx64c,0.43000000000000000000 +44_100,fx64c,0.44000000000000000000 +45_100,fx64c,0.45000000000000000000 +46_100,fx64c,0.46000000000000000000 +47_100,fx64c,0.47000000000000000000 +48_100,fx64c,0.48000000000000000000 +49_100,fx64c,0.49000000000000000000 +50_100,fx64c,0.50000000000000000000 +51_100,fx64c,0.51000000000000000000 +52_100,fx64c,0.52000000000000000000 +53_100,fx64c,0.53000000000000000000 +54_100,fx64c,0.54000000000000000000 +55_100,fx64c,0.55000000000000000000 +56_100,fx64c,0.56000000000000000000 +57_100,fx64c,0.57000000000000000000 +58_100,fx64c,0.58000000000000000000 +59_100,fx64c,0.59000000000000000000 +60_100,fx64c,0.60000000000000000000 +61_100,fx64c,0.61000000000000000000 +62_100,fx64c,0.62000000000000000000 +63_100,fx64c,0.63000000000000000000 +64_100,fx64c,0.64000000000000000000 +65_100,fx64c,0.65000000000000000000 +66_100,fx64c,0.66000000000000000000 +67_100,fx64c,0.67000000000000000000 +68_100,fx64c,0.68000000000000000000 +69_100,fx64c,0.69000000000000000000 +70_100,fx64c,0.70000000000000000000 +71_100,fx64c,0.71000000000000000000 +72_100,fx64c,0.72000000000000000000 +73_100,fx64c,0.73000000000000000000 +74_100,fx64c,0.74000000000000000000 +75_100,fx64c,0.75000000000000000000 +76_100,fx64c,0.76000000000000000000 +77_100,fx64c,0.77000000000000000000 +78_100,fx64c,0.78000000000000000000 +79_100,fx64c,0.79000000000000000000 +80_100,fx64c,0.80000000000000000000 +81_100,fx64c,0.81000000000000000000 +82_100,fx64c,0.82000000000000000000 +83_100,fx64c,0.83000000000000000000 +84_100,fx64c,0.84000000000000000000 +85_100,fx64c,0.85000000000000000000 +86_100,fx64c,0.86000000000000000000 +87_100,fx64c,0.87000000000000000000 +88_100,fx64c,0.88000000000000000000 +89_100,fx64c,0.89000000000000000000 +90_100,fx64c,0.90000000000000000000 +91_100,fx64c,0.91000000000000000000 +92_100,fx64c,0.92000000000000000000 +93_100,fx64c,0.93000000000000000000 +94_100,fx64c,0.94000000000000000000 +95_100,fx64c,0.95000000000000000000 +96_100,fx64c,0.96000000000000000000 +97_100,fx64c,0.97000000000000000000 +98_100,fx64c,0.98000000000000000000 +99_100,fx64c,0.99000000000000000000 +ONE,fx64c,1.00000000000000000000 +HALF,fx64c,0.50000000000000000000 +ONE,fx32,1.00000000000000000000 +HALF,fx32,0.50000000000000000000 +SQRT2,fx32,1.41421356237309504880 +SQRT1_2,fx32,0.70710678118654752440 +SQRT3,fx32,1.73205080756887719000 +SQRT1_3,fx32,0.57735026918962576450914878050196 +ONE,fx16,1.00000000000000000000 +HALF,fx16,0.50000000000000000000 +SQRT2,fx16,1.41421356237309504880 +SQRT1_2,fx16,0.70710678118654752440 +SQRT3,fx16,1.73205080756887719000 +SQRT1_3,fx16,0.57735026918962576450914878050196 +SIN0,fx32,0 +SIN1,fx32,0.0174524064372835 +SIN2,fx32,0.034899496702501 +SIN3,fx32,0.0523359562429438 +SIN4,fx32,0.0697564737441253 +SIN5,fx32,0.0871557427476582 +SIN6,fx32,0.104528463267653 +SIN7,fx32,0.121869343405147 +SIN8,fx32,0.139173100960065 +SIN9,fx32,0.156434465040231 +SIN10,fx32,0.17364817766693 +SIN11,fx32,0.190808995376545 +SIN12,fx32,0.207911690817759 +SIN13,fx32,0.224951054343865 +SIN14,fx32,0.241921895599668 +SIN15,fx32,0.258819045102521 +SIN16,fx32,0.275637355816999 +SIN17,fx32,0.292371704722737 +SIN18,fx32,0.309016994374947 +SIN19,fx32,0.325568154457157 +SIN20,fx32,0.342020143325669 +SIN21,fx32,0.3583679495453 +SIN22,fx32,0.374606593415912 +SIN23,fx32,0.390731128489274 +SIN24,fx32,0.4067366430758 +SIN25,fx32,0.422618261740699 +SIN26,fx32,0.438371146789077 +SIN27,fx32,0.453990499739547 +SIN28,fx32,0.469471562785891 +SIN29,fx32,0.484809620246337 +SIN30,fx32,0.5 +SIN31,fx32,0.515038074910054 +SIN32,fx32,0.529919264233205 +SIN33,fx32,0.544639035015027 +SIN34,fx32,0.559192903470747 +SIN35,fx32,0.573576436351046 +SIN36,fx32,0.587785252292473 +SIN37,fx32,0.601815023152048 +SIN38,fx32,0.615661475325658 +SIN39,fx32,0.629320391049837 +SIN40,fx32,0.642787609686539 +SIN41,fx32,0.656059028990507 +SIN42,fx32,0.669130606358858 +SIN43,fx32,0.681998360062498 +SIN44,fx32,0.694658370458997 +SIN45,fx32,0.707106781186547 +SIN46,fx32,0.719339800338651 +SIN47,fx32,0.73135370161917 +SIN48,fx32,0.743144825477394 +SIN49,fx32,0.754709580222772 +SIN50,fx32,0.766044443118978 +SIN51,fx32,0.777145961456971 +SIN52,fx32,0.788010753606722 +SIN53,fx32,0.798635510047293 +SIN54,fx32,0.809016994374947 +SIN55,fx32,0.819152044288992 +SIN56,fx32,0.829037572555042 +SIN57,fx32,0.838670567945424 +SIN58,fx32,0.848048096156426 +SIN59,fx32,0.857167300702112 +SIN60,fx32,0.866025403784439 +SIN61,fx32,0.874619707139396 +SIN62,fx32,0.882947592858927 +SIN63,fx32,0.891006524188368 +SIN64,fx32,0.898794046299167 +SIN65,fx32,0.90630778703665 +SIN66,fx32,0.913545457642601 +SIN67,fx32,0.92050485345244 +SIN68,fx32,0.927183854566787 +SIN69,fx32,0.933580426497202 +SIN70,fx32,0.939692620785908 +SIN71,fx32,0.945518575599317 +SIN72,fx32,0.951056516295154 +SIN73,fx32,0.956304755963035 +SIN74,fx32,0.961261695938319 +SIN75,fx32,0.965925826289068 +SIN76,fx32,0.970295726275996 +SIN77,fx32,0.974370064785235 +SIN78,fx32,0.978147600733806 +SIN79,fx32,0.981627183447664 +SIN80,fx32,0.984807753012208 +SIN81,fx32,0.987688340595138 +SIN82,fx32,0.99026806874157 +SIN83,fx32,0.992546151641322 +SIN84,fx32,0.994521895368273 +SIN85,fx32,0.996194698091746 +SIN86,fx32,0.997564050259824 +SIN87,fx32,0.998629534754574 +SIN88,fx32,0.999390827019096 +SIN89,fx32,0.999847695156391 +SIN90,fx32,1 +SIN91,fx32,0.999847695156391 +SIN92,fx32,0.999390827019096 +SIN93,fx32,0.998629534754574 +SIN94,fx32,0.997564050259824 +SIN95,fx32,0.996194698091746 +SIN96,fx32,0.994521895368273 +SIN97,fx32,0.992546151641322 +SIN98,fx32,0.99026806874157 +SIN99,fx32,0.987688340595138 +SIN100,fx32,0.984807753012208 +SIN101,fx32,0.981627183447664 +SIN102,fx32,0.978147600733806 +SIN103,fx32,0.974370064785235 +SIN104,fx32,0.970295726275996 +SIN105,fx32,0.965925826289068 +SIN106,fx32,0.961261695938319 +SIN107,fx32,0.956304755963036 +SIN108,fx32,0.951056516295154 +SIN109,fx32,0.945518575599317 +SIN110,fx32,0.939692620785908 +SIN111,fx32,0.933580426497202 +SIN112,fx32,0.927183854566787 +SIN113,fx32,0.92050485345244 +SIN114,fx32,0.913545457642601 +SIN115,fx32,0.90630778703665 +SIN116,fx32,0.898794046299167 +SIN117,fx32,0.891006524188368 +SIN118,fx32,0.882947592858927 +SIN119,fx32,0.874619707139396 +SIN120,fx32,0.866025403784439 +SIN121,fx32,0.857167300702112 +SIN122,fx32,0.848048096156426 +SIN123,fx32,0.838670567945424 +SIN124,fx32,0.829037572555042 +SIN125,fx32,0.819152044288992 +SIN126,fx32,0.809016994374947 +SIN127,fx32,0.798635510047293 +SIN128,fx32,0.788010753606722 +SIN129,fx32,0.777145961456971 +SIN130,fx32,0.766044443118978 +SIN131,fx32,0.754709580222772 +SIN132,fx32,0.743144825477394 +SIN133,fx32,0.731353701619171 +SIN134,fx32,0.719339800338651 +SIN135,fx32,0.707106781186548 +SIN136,fx32,0.694658370458997 +SIN137,fx32,0.681998360062498 +SIN138,fx32,0.669130606358858 +SIN139,fx32,0.656059028990507 +SIN140,fx32,0.642787609686539 +SIN141,fx32,0.629320391049837 +SIN142,fx32,0.615661475325658 +SIN143,fx32,0.601815023152048 +SIN144,fx32,0.587785252292473 +SIN145,fx32,0.573576436351046 +SIN146,fx32,0.559192903470747 +SIN147,fx32,0.544639035015027 +SIN148,fx32,0.529919264233205 +SIN149,fx32,0.515038074910054 +SIN150,fx32,0.5 +SIN151,fx32,0.484809620246337 +SIN152,fx32,0.469471562785891 +SIN153,fx32,0.453990499739547 +SIN154,fx32,0.438371146789077 +SIN155,fx32,0.422618261740699 +SIN156,fx32,0.4067366430758 +SIN157,fx32,0.390731128489274 +SIN158,fx32,0.374606593415912 +SIN159,fx32,0.3583679495453 +SIN160,fx32,0.342020143325669 +SIN161,fx32,0.325568154457157 +SIN162,fx32,0.309016994374948 +SIN163,fx32,0.292371704722737 +SIN164,fx32,0.275637355816999 +SIN165,fx32,0.258819045102521 +SIN166,fx32,0.241921895599668 +SIN167,fx32,0.224951054343865 +SIN168,fx32,0.207911690817759 +SIN169,fx32,0.190808995376545 +SIN170,fx32,0.17364817766693 +SIN171,fx32,0.156434465040231 +SIN172,fx32,0.139173100960065 +SIN173,fx32,0.121869343405148 +SIN174,fx32,0.104528463267654 +SIN175,fx32,0.0871557427476582 +SIN176,fx32,0.0697564737441255 +SIN177,fx32,0.0523359562429438 +SIN178,fx32,0.0348994967025011 +SIN179,fx32,0.0174524064372834 +SIN180,fx32,1.22464679914735e-16 +SIN181,fx32,-0.0174524064372836 +SIN182,fx32,-0.0348994967025009 +SIN183,fx32,-0.0523359562429436 +SIN184,fx32,-0.0697564737441253 +SIN185,fx32,-0.0871557427476579 +SIN186,fx32,-0.104528463267653 +SIN187,fx32,-0.121869343405147 +SIN188,fx32,-0.139173100960066 +SIN189,fx32,-0.156434465040231 +SIN190,fx32,-0.17364817766693 +SIN191,fx32,-0.190808995376545 +SIN192,fx32,-0.20791169081776 +SIN193,fx32,-0.224951054343865 +SIN194,fx32,-0.241921895599668 +SIN195,fx32,-0.258819045102521 +SIN196,fx32,-0.275637355816999 +SIN197,fx32,-0.292371704722737 +SIN198,fx32,-0.309016994374947 +SIN199,fx32,-0.325568154457157 +SIN200,fx32,-0.342020143325669 +SIN201,fx32,-0.3583679495453 +SIN202,fx32,-0.374606593415912 +SIN203,fx32,-0.390731128489274 +SIN204,fx32,-0.4067366430758 +SIN205,fx32,-0.422618261740699 +SIN206,fx32,-0.438371146789077 +SIN207,fx32,-0.453990499739547 +SIN208,fx32,-0.469471562785891 +SIN209,fx32,-0.484809620246337 +SIN210,fx32,-0.5 +SIN211,fx32,-0.515038074910054 +SIN212,fx32,-0.529919264233205 +SIN213,fx32,-0.544639035015027 +SIN214,fx32,-0.559192903470747 +SIN215,fx32,-0.573576436351046 +SIN216,fx32,-0.587785252292473 +SIN217,fx32,-0.601815023152048 +SIN218,fx32,-0.615661475325658 +SIN219,fx32,-0.629320391049838 +SIN220,fx32,-0.642787609686539 +SIN221,fx32,-0.656059028990507 +SIN222,fx32,-0.669130606358858 +SIN223,fx32,-0.681998360062498 +SIN224,fx32,-0.694658370458997 +SIN225,fx32,-0.707106781186547 +SIN226,fx32,-0.719339800338651 +SIN227,fx32,-0.73135370161917 +SIN228,fx32,-0.743144825477394 +SIN229,fx32,-0.754709580222772 +SIN230,fx32,-0.766044443118978 +SIN231,fx32,-0.777145961456971 +SIN232,fx32,-0.788010753606722 +SIN233,fx32,-0.798635510047293 +SIN234,fx32,-0.809016994374947 +SIN235,fx32,-0.819152044288992 +SIN236,fx32,-0.829037572555042 +SIN237,fx32,-0.838670567945424 +SIN238,fx32,-0.848048096156426 +SIN239,fx32,-0.857167300702112 +SIN240,fx32,-0.866025403784438 +SIN241,fx32,-0.874619707139396 +SIN242,fx32,-0.882947592858927 +SIN243,fx32,-0.891006524188368 +SIN244,fx32,-0.898794046299167 +SIN245,fx32,-0.90630778703665 +SIN246,fx32,-0.913545457642601 +SIN247,fx32,-0.92050485345244 +SIN248,fx32,-0.927183854566787 +SIN249,fx32,-0.933580426497202 +SIN250,fx32,-0.939692620785908 +SIN251,fx32,-0.945518575599317 +SIN252,fx32,-0.951056516295154 +SIN253,fx32,-0.956304755963035 +SIN254,fx32,-0.961261695938319 +SIN255,fx32,-0.965925826289068 +SIN256,fx32,-0.970295726275996 +SIN257,fx32,-0.974370064785235 +SIN258,fx32,-0.978147600733806 +SIN259,fx32,-0.981627183447664 +SIN260,fx32,-0.984807753012208 +SIN261,fx32,-0.987688340595138 +SIN262,fx32,-0.99026806874157 +SIN263,fx32,-0.992546151641322 +SIN264,fx32,-0.994521895368273 +SIN265,fx32,-0.996194698091746 +SIN266,fx32,-0.997564050259824 +SIN267,fx32,-0.998629534754574 +SIN268,fx32,-0.999390827019096 +SIN269,fx32,-0.999847695156391 +SIN270,fx32,-1 +SIN271,fx32,-0.999847695156391 +SIN272,fx32,-0.999390827019096 +SIN273,fx32,-0.998629534754574 +SIN274,fx32,-0.997564050259824 +SIN275,fx32,-0.996194698091746 +SIN276,fx32,-0.994521895368273 +SIN277,fx32,-0.992546151641322 +SIN278,fx32,-0.99026806874157 +SIN279,fx32,-0.987688340595138 +SIN280,fx32,-0.984807753012208 +SIN281,fx32,-0.981627183447664 +SIN282,fx32,-0.978147600733806 +SIN283,fx32,-0.974370064785235 +SIN284,fx32,-0.970295726275997 +SIN285,fx32,-0.965925826289068 +SIN286,fx32,-0.961261695938319 +SIN287,fx32,-0.956304755963035 +SIN288,fx32,-0.951056516295154 +SIN289,fx32,-0.945518575599317 +SIN290,fx32,-0.939692620785908 +SIN291,fx32,-0.933580426497202 +SIN292,fx32,-0.927183854566787 +SIN293,fx32,-0.92050485345244 +SIN294,fx32,-0.913545457642601 +SIN295,fx32,-0.90630778703665 +SIN296,fx32,-0.898794046299167 +SIN297,fx32,-0.891006524188368 +SIN298,fx32,-0.882947592858927 +SIN299,fx32,-0.874619707139396 +SIN300,fx32,-0.866025403784439 +SIN301,fx32,-0.857167300702112 +SIN302,fx32,-0.848048096156426 +SIN303,fx32,-0.838670567945424 +SIN304,fx32,-0.829037572555042 +SIN305,fx32,-0.819152044288992 +SIN306,fx32,-0.809016994374948 +SIN307,fx32,-0.798635510047293 +SIN308,fx32,-0.788010753606722 +SIN309,fx32,-0.777145961456971 +SIN310,fx32,-0.766044443118978 +SIN311,fx32,-0.754709580222772 +SIN312,fx32,-0.743144825477395 +SIN313,fx32,-0.73135370161917 +SIN314,fx32,-0.719339800338651 +SIN315,fx32,-0.707106781186548 +SIN316,fx32,-0.694658370458998 +SIN317,fx32,-0.681998360062498 +SIN318,fx32,-0.669130606358858 +SIN319,fx32,-0.656059028990507 +SIN320,fx32,-0.64278760968654 +SIN321,fx32,-0.629320391049838 +SIN322,fx32,-0.615661475325658 +SIN323,fx32,-0.601815023152048 +SIN324,fx32,-0.587785252292473 +SIN325,fx32,-0.573576436351046 +SIN326,fx32,-0.559192903470747 +SIN327,fx32,-0.544639035015027 +SIN328,fx32,-0.529919264233205 +SIN329,fx32,-0.515038074910054 +SIN330,fx32,-0.5 +SIN331,fx32,-0.484809620246337 +SIN332,fx32,-0.469471562785891 +SIN333,fx32,-0.453990499739547 +SIN334,fx32,-0.438371146789078 +SIN335,fx32,-0.422618261740699 +SIN336,fx32,-0.4067366430758 +SIN337,fx32,-0.390731128489274 +SIN338,fx32,-0.374606593415912 +SIN339,fx32,-0.358367949545301 +SIN340,fx32,-0.342020143325669 +SIN341,fx32,-0.325568154457157 +SIN342,fx32,-0.309016994374948 +SIN343,fx32,-0.292371704722737 +SIN344,fx32,-0.275637355816999 +SIN345,fx32,-0.258819045102521 +SIN346,fx32,-0.241921895599668 +SIN347,fx32,-0.224951054343865 +SIN348,fx32,-0.20791169081776 +SIN349,fx32,-0.190808995376545 +SIN350,fx32,-0.17364817766693 +SIN351,fx32,-0.156434465040231 +SIN352,fx32,-0.139173100960066 +SIN353,fx32,-0.121869343405147 +SIN354,fx32,-0.104528463267653 +SIN355,fx32,-0.0871557427476583 +SIN356,fx32,-0.0697564737441256 +SIN357,fx32,-0.0523359562429444 +SIN358,fx32,-0.0348994967025008 +SIN359,fx32,-0.0174524064372836 +COS0,fx32,1 +COS1,fx32,0.999847695156391 +COS2,fx32,0.999390827019096 +COS3,fx32,0.998629534754574 +COS4,fx32,0.997564050259824 +COS5,fx32,0.996194698091746 +COS6,fx32,0.994521895368273 +COS7,fx32,0.992546151641322 +COS8,fx32,0.99026806874157 +COS9,fx32,0.987688340595138 +COS10,fx32,0.984807753012208 +COS11,fx32,0.981627183447664 +COS12,fx32,0.978147600733806 +COS13,fx32,0.974370064785235 +COS14,fx32,0.970295726275996 +COS15,fx32,0.965925826289068 +COS16,fx32,0.961261695938319 +COS17,fx32,0.956304755963035 +COS18,fx32,0.951056516295154 +COS19,fx32,0.945518575599317 +COS20,fx32,0.939692620785908 +COS21,fx32,0.933580426497202 +COS22,fx32,0.927183854566787 +COS23,fx32,0.92050485345244 +COS24,fx32,0.913545457642601 +COS25,fx32,0.90630778703665 +COS26,fx32,0.898794046299167 +COS27,fx32,0.891006524188368 +COS28,fx32,0.882947592858927 +COS29,fx32,0.874619707139396 +COS30,fx32,0.866025403784439 +COS31,fx32,0.857167300702112 +COS32,fx32,0.848048096156426 +COS33,fx32,0.838670567945424 +COS34,fx32,0.829037572555042 +COS35,fx32,0.819152044288992 +COS36,fx32,0.809016994374947 +COS37,fx32,0.798635510047293 +COS38,fx32,0.788010753606722 +COS39,fx32,0.777145961456971 +COS40,fx32,0.766044443118978 +COS41,fx32,0.754709580222772 +COS42,fx32,0.743144825477394 +COS43,fx32,0.73135370161917 +COS44,fx32,0.719339800338651 +COS45,fx32,0.707106781186548 +COS46,fx32,0.694658370458997 +COS47,fx32,0.681998360062498 +COS48,fx32,0.669130606358858 +COS49,fx32,0.656059028990507 +COS50,fx32,0.642787609686539 +COS51,fx32,0.629320391049838 +COS52,fx32,0.615661475325658 +COS53,fx32,0.601815023152048 +COS54,fx32,0.587785252292473 +COS55,fx32,0.573576436351046 +COS56,fx32,0.559192903470747 +COS57,fx32,0.544639035015027 +COS58,fx32,0.529919264233205 +COS59,fx32,0.515038074910054 +COS60,fx32,0.5 +COS61,fx32,0.484809620246337 +COS62,fx32,0.469471562785891 +COS63,fx32,0.453990499739547 +COS64,fx32,0.438371146789077 +COS65,fx32,0.422618261740699 +COS66,fx32,0.4067366430758 +COS67,fx32,0.390731128489274 +COS68,fx32,0.374606593415912 +COS69,fx32,0.3583679495453 +COS70,fx32,0.342020143325669 +COS71,fx32,0.325568154457157 +COS72,fx32,0.309016994374947 +COS73,fx32,0.292371704722737 +COS74,fx32,0.275637355816999 +COS75,fx32,0.258819045102521 +COS76,fx32,0.241921895599668 +COS77,fx32,0.224951054343865 +COS78,fx32,0.207911690817759 +COS79,fx32,0.190808995376545 +COS80,fx32,0.17364817766693 +COS81,fx32,0.156434465040231 +COS82,fx32,0.139173100960065 +COS83,fx32,0.121869343405147 +COS84,fx32,0.104528463267653 +COS85,fx32,0.0871557427476581 +COS86,fx32,0.0697564737441252 +COS87,fx32,0.052335956242944 +COS88,fx32,0.0348994967025011 +COS89,fx32,0.0174524064372836 +COS90,fx32,6.12323399573677e-17 +COS91,fx32,-0.0174524064372835 +COS92,fx32,-0.034899496702501 +COS93,fx32,-0.0523359562429438 +COS94,fx32,-0.0697564737441253 +COS95,fx32,-0.0871557427476582 +COS96,fx32,-0.104528463267654 +COS97,fx32,-0.121869343405147 +COS98,fx32,-0.139173100960065 +COS99,fx32,-0.156434465040231 +COS100,fx32,-0.17364817766693 +COS101,fx32,-0.190808995376545 +COS102,fx32,-0.207911690817759 +COS103,fx32,-0.224951054343865 +COS104,fx32,-0.241921895599668 +COS105,fx32,-0.258819045102521 +COS106,fx32,-0.275637355816999 +COS107,fx32,-0.292371704722737 +COS108,fx32,-0.309016994374947 +COS109,fx32,-0.325568154457157 +COS110,fx32,-0.342020143325669 +COS111,fx32,-0.3583679495453 +COS112,fx32,-0.374606593415912 +COS113,fx32,-0.390731128489274 +COS114,fx32,-0.4067366430758 +COS115,fx32,-0.422618261740699 +COS116,fx32,-0.438371146789078 +COS117,fx32,-0.453990499739547 +COS118,fx32,-0.469471562785891 +COS119,fx32,-0.484809620246337 +COS120,fx32,-0.5 +COS121,fx32,-0.515038074910054 +COS122,fx32,-0.529919264233205 +COS123,fx32,-0.544639035015027 +COS124,fx32,-0.559192903470747 +COS125,fx32,-0.573576436351046 +COS126,fx32,-0.587785252292473 +COS127,fx32,-0.601815023152048 +COS128,fx32,-0.615661475325658 +COS129,fx32,-0.629320391049837 +COS130,fx32,-0.642787609686539 +COS131,fx32,-0.656059028990507 +COS132,fx32,-0.669130606358858 +COS133,fx32,-0.681998360062498 +COS134,fx32,-0.694658370458997 +COS135,fx32,-0.707106781186547 +COS136,fx32,-0.719339800338651 +COS137,fx32,-0.73135370161917 +COS138,fx32,-0.743144825477394 +COS139,fx32,-0.754709580222772 +COS140,fx32,-0.766044443118978 +COS141,fx32,-0.777145961456971 +COS142,fx32,-0.788010753606722 +COS143,fx32,-0.798635510047293 +COS144,fx32,-0.809016994374947 +COS145,fx32,-0.819152044288992 +COS146,fx32,-0.829037572555042 +COS147,fx32,-0.838670567945424 +COS148,fx32,-0.848048096156426 +COS149,fx32,-0.857167300702112 +COS150,fx32,-0.866025403784439 +COS151,fx32,-0.874619707139396 +COS152,fx32,-0.882947592858927 +COS153,fx32,-0.891006524188368 +COS154,fx32,-0.898794046299167 +COS155,fx32,-0.90630778703665 +COS156,fx32,-0.913545457642601 +COS157,fx32,-0.92050485345244 +COS158,fx32,-0.927183854566787 +COS159,fx32,-0.933580426497202 +COS160,fx32,-0.939692620785908 +COS161,fx32,-0.945518575599317 +COS162,fx32,-0.951056516295154 +COS163,fx32,-0.956304755963036 +COS164,fx32,-0.961261695938319 +COS165,fx32,-0.965925826289068 +COS166,fx32,-0.970295726275996 +COS167,fx32,-0.974370064785235 +COS168,fx32,-0.978147600733806 +COS169,fx32,-0.981627183447664 +COS170,fx32,-0.984807753012208 +COS171,fx32,-0.987688340595138 +COS172,fx32,-0.99026806874157 +COS173,fx32,-0.992546151641322 +COS174,fx32,-0.994521895368273 +COS175,fx32,-0.996194698091746 +COS176,fx32,-0.997564050259824 +COS177,fx32,-0.998629534754574 +COS178,fx32,-0.999390827019096 +COS179,fx32,-0.999847695156391 +COS180,fx32,-1 +COS181,fx32,-0.999847695156391 +COS182,fx32,-0.999390827019096 +COS183,fx32,-0.998629534754574 +COS184,fx32,-0.997564050259824 +COS185,fx32,-0.996194698091746 +COS186,fx32,-0.994521895368273 +COS187,fx32,-0.992546151641322 +COS188,fx32,-0.99026806874157 +COS189,fx32,-0.987688340595138 +COS190,fx32,-0.984807753012208 +COS191,fx32,-0.981627183447664 +COS192,fx32,-0.978147600733806 +COS193,fx32,-0.974370064785235 +COS194,fx32,-0.970295726275996 +COS195,fx32,-0.965925826289068 +COS196,fx32,-0.961261695938319 +COS197,fx32,-0.956304755963035 +COS198,fx32,-0.951056516295154 +COS199,fx32,-0.945518575599317 +COS200,fx32,-0.939692620785908 +COS201,fx32,-0.933580426497202 +COS202,fx32,-0.927183854566787 +COS203,fx32,-0.92050485345244 +COS204,fx32,-0.913545457642601 +COS205,fx32,-0.90630778703665 +COS206,fx32,-0.898794046299167 +COS207,fx32,-0.891006524188368 +COS208,fx32,-0.882947592858927 +COS209,fx32,-0.874619707139396 +COS210,fx32,-0.866025403784439 +COS211,fx32,-0.857167300702112 +COS212,fx32,-0.848048096156426 +COS213,fx32,-0.838670567945424 +COS214,fx32,-0.829037572555042 +COS215,fx32,-0.819152044288992 +COS216,fx32,-0.809016994374948 +COS217,fx32,-0.798635510047293 +COS218,fx32,-0.788010753606722 +COS219,fx32,-0.777145961456971 +COS220,fx32,-0.766044443118978 +COS221,fx32,-0.754709580222772 +COS222,fx32,-0.743144825477394 +COS223,fx32,-0.731353701619171 +COS224,fx32,-0.719339800338651 +COS225,fx32,-0.707106781186548 +COS226,fx32,-0.694658370458997 +COS227,fx32,-0.681998360062499 +COS228,fx32,-0.669130606358858 +COS229,fx32,-0.656059028990507 +COS230,fx32,-0.642787609686539 +COS231,fx32,-0.629320391049838 +COS232,fx32,-0.615661475325658 +COS233,fx32,-0.601815023152048 +COS234,fx32,-0.587785252292473 +COS235,fx32,-0.573576436351046 +COS236,fx32,-0.559192903470747 +COS237,fx32,-0.544639035015027 +COS238,fx32,-0.529919264233205 +COS239,fx32,-0.515038074910054 +COS240,fx32,-0.5 +COS241,fx32,-0.484809620246337 +COS242,fx32,-0.469471562785891 +COS243,fx32,-0.453990499739547 +COS244,fx32,-0.438371146789078 +COS245,fx32,-0.422618261740699 +COS246,fx32,-0.4067366430758 +COS247,fx32,-0.390731128489274 +COS248,fx32,-0.374606593415912 +COS249,fx32,-0.358367949545301 +COS250,fx32,-0.342020143325669 +COS251,fx32,-0.325568154457157 +COS252,fx32,-0.309016994374948 +COS253,fx32,-0.292371704722737 +COS254,fx32,-0.275637355816999 +COS255,fx32,-0.258819045102521 +COS256,fx32,-0.241921895599668 +COS257,fx32,-0.224951054343865 +COS258,fx32,-0.20791169081776 +COS259,fx32,-0.190808995376545 +COS260,fx32,-0.17364817766693 +COS261,fx32,-0.156434465040231 +COS262,fx32,-0.139173100960066 +COS263,fx32,-0.121869343405147 +COS264,fx32,-0.104528463267653 +COS265,fx32,-0.0871557427476582 +COS266,fx32,-0.0697564737441256 +COS267,fx32,-0.0523359562429443 +COS268,fx32,-0.0348994967025008 +COS269,fx32,-0.0174524064372835 +COS270,fx32,-1.83697019872103e-16 +COS271,fx32,0.0174524064372831 +COS272,fx32,0.0348994967025013 +COS273,fx32,0.0523359562429439 +COS274,fx32,0.0697564737441252 +COS275,fx32,0.0871557427476579 +COS276,fx32,0.104528463267653 +COS277,fx32,0.121869343405148 +COS278,fx32,0.139173100960065 +COS279,fx32,0.156434465040231 +COS280,fx32,0.17364817766693 +COS281,fx32,0.190808995376545 +COS282,fx32,0.207911690817759 +COS283,fx32,0.224951054343865 +COS284,fx32,0.241921895599667 +COS285,fx32,0.25881904510252 +COS286,fx32,0.275637355816999 +COS287,fx32,0.292371704722737 +COS288,fx32,0.309016994374947 +COS289,fx32,0.325568154457156 +COS290,fx32,0.342020143325669 +COS291,fx32,0.3583679495453 +COS292,fx32,0.374606593415912 +COS293,fx32,0.390731128489273 +COS294,fx32,0.4067366430758 +COS295,fx32,0.4226182617407 +COS296,fx32,0.438371146789077 +COS297,fx32,0.453990499739547 +COS298,fx32,0.46947156278589 +COS299,fx32,0.484809620246337 +COS300,fx32,0.5 +COS301,fx32,0.515038074910054 +COS302,fx32,0.529919264233205 +COS303,fx32,0.544639035015027 +COS304,fx32,0.559192903470747 +COS305,fx32,0.573576436351046 +COS306,fx32,0.587785252292473 +COS307,fx32,0.601815023152048 +COS308,fx32,0.615661475325659 +COS309,fx32,0.629320391049838 +COS310,fx32,0.642787609686539 +COS311,fx32,0.656059028990507 +COS312,fx32,0.669130606358858 +COS313,fx32,0.681998360062499 +COS314,fx32,0.694658370458997 +COS315,fx32,0.707106781186547 +COS316,fx32,0.719339800338651 +COS317,fx32,0.731353701619171 +COS318,fx32,0.743144825477394 +COS319,fx32,0.754709580222772 +COS320,fx32,0.766044443118978 +COS321,fx32,0.777145961456971 +COS322,fx32,0.788010753606722 +COS323,fx32,0.798635510047293 +COS324,fx32,0.809016994374947 +COS325,fx32,0.819152044288992 +COS326,fx32,0.829037572555042 +COS327,fx32,0.838670567945424 +COS328,fx32,0.848048096156426 +COS329,fx32,0.857167300702112 +COS330,fx32,0.866025403784438 +COS331,fx32,0.874619707139396 +COS332,fx32,0.882947592858927 +COS333,fx32,0.891006524188368 +COS334,fx32,0.898794046299167 +COS335,fx32,0.90630778703665 +COS336,fx32,0.913545457642601 +COS337,fx32,0.92050485345244 +COS338,fx32,0.927183854566787 +COS339,fx32,0.933580426497202 +COS340,fx32,0.939692620785908 +COS341,fx32,0.945518575599317 +COS342,fx32,0.951056516295154 +COS343,fx32,0.956304755963035 +COS344,fx32,0.961261695938319 +COS345,fx32,0.965925826289068 +COS346,fx32,0.970295726275996 +COS347,fx32,0.974370064785235 +COS348,fx32,0.978147600733806 +COS349,fx32,0.981627183447664 +COS350,fx32,0.984807753012208 +COS351,fx32,0.987688340595138 +COS352,fx32,0.99026806874157 +COS353,fx32,0.992546151641322 +COS354,fx32,0.994521895368273 +COS355,fx32,0.996194698091746 +COS356,fx32,0.997564050259824 +COS357,fx32,0.998629534754574 +COS358,fx32,0.999390827019096 +COS359,fx32,0.999847695156391 diff --git a/tools/gen_fx_consts/fx_data.c b/tools/gen_fx_consts/fx_data.c new file mode 100644 index 0000000000..a6f94bb78e --- /dev/null +++ b/tools/gen_fx_consts/fx_data.c @@ -0,0 +1,128 @@ +#include +#include +#include "fx.h" +#include "global.h" + +const struct FxSpec fx32 = {"fx32", 19, 12}; +const struct FxSpec fx64 = {"fx64", 51, 12}; +const struct FxSpec fx64c = {"fx64c", 31, 32}; +const struct FxSpec fx16 = {"fx16", 3, 12}; + +const struct FxSpec *const sFxSpecPtrs[] = { + &fx32, + &fx64, + &fx64c, + &fx16, +}; + +struct FxConst *gFxConstTable = NULL; +size_t gNFxConstTable = 0; + +void FxConstTableInit(void) { + FILE *file; + char *contents; + char *ptr; + char *linetok; + char *linetok_r; + char *commatok; + char *commatok_r; + char *_realpath; + int i, j, k; + long fsize; + long readsz; + static const char filename[] = SOURCE_DIR "/fx_const.csv"; + + atexit(FxConstTableEnd); + + file = fopen(filename, "r"); + if (file == NULL) { + fatal_error("Unable to open file '%s' for reading", filename); + } + fseek(file, 0, SEEK_END); + fsize = ftell(file); + fseek(file, 0, SEEK_SET); + contents = malloc(fsize + 1); + if (contents == NULL) { + fclose(file); + fatal_error("Unable to allocate CSV read buffer"); + } + readsz = fread(contents, 1, fsize, file); + fclose(file); + if (readsz != fsize) { + free(contents); + fatal_error("Read error"); + } + contents[fsize] = 0; + ptr = contents - 1; + gNFxConstTable = 0; + do { + ptr++; + gNFxConstTable += (*ptr != '#' && *ptr != '\n'); + ptr = strchr(ptr, '\n'); + } while (ptr != NULL); + if (gNFxConstTable == 0) { + free(contents); + fatal_error("Malformatted CSV file"); + } + gFxConstTable = malloc(gNFxConstTable * sizeof(struct FxConst)); + if (gFxConstTable == NULL) { + free(contents); + fatal_error("Unable to allocate gFxConstTable"); + } + ptr = contents; + for (i = 0; i < gNFxConstTable; i++) { + linetok = strtok_r(ptr, "\n", &linetok_r); + ptr = NULL; + if (linetok == NULL) { + gNFxConstTable = i; + break; + } + if (*linetok == '#') { + i--; + continue; + } + for (j = 0; j < 3; j++) { + commatok = strtok_r(linetok, ",", &commatok_r); + linetok = NULL; + if (commatok == NULL) { + free(contents); + fatal_error("Malformatted CSV file at line %d col %d", i, j); + } + switch (j) { + case 0: + gFxConstTable[i].name = strdup(commatok); + break; + case 1: + for (k = 0; k < len(sFxSpecPtrs); k++) { + if (strcmp(commatok, sFxSpecPtrs[k]->name) == 0) { + break; + } + } + if (k == len(sFxSpecPtrs)) { + free(contents); + fatal_error("Invalid fx type %s", commatok); + } + gFxConstTable[i].fxtype = sFxSpecPtrs[k]; + break; + case 2: + gFxConstTable[i].value = strtod(commatok, NULL); + break; + } + } + } + free(contents); +} + +void FxConstTableEnd(void) { + int i; + if (gFxConstTable != NULL) { + for (i = 0; i < gNFxConstTable; i++) { + if (gFxConstTable[i].name != NULL) { + free(gFxConstTable[i].name); + } + } + free(gFxConstTable); + gFxConstTable = NULL; + } + gNFxConstTable = 0; +} diff --git a/tools/gen_fx_consts/fx_encode.c b/tools/gen_fx_consts/fx_encode.c new file mode 100644 index 0000000000..8110753d78 --- /dev/null +++ b/tools/gen_fx_consts/fx_encode.c @@ -0,0 +1,28 @@ +#include +#include "fx.h" + +long long MakeFix(const struct FxConst *constdef) { + double ret_di, ret_df, value_abs; + long long ret_ii, ret_if, ret_val; + + if (constdef->value == 0) { + return 0LL; + } + + value_abs = constdef->value < 0 ? -constdef->value : constdef->value; + ret_di = floor(value_abs); + ret_df = value_abs - ret_di; + ret_ii = (long long)value_abs * (1LL << constdef->fxtype->fracpart); + ret_if = (long long)(ret_df * (double)(1LL << constdef->fxtype->fracpart) + 0.5); + ret_ii &= ~((1LL << constdef->fxtype->fracpart) - 1); + ret_if &= (1LL << constdef->fxtype->fracpart) - 1; + ret_val = ret_ii + ret_if; + if (constdef->value < 0) { + ret_val = -ret_val; + } + return ret_val; +} + +int GetFxWidth(const struct FxSpec *fxtype) { + return fxtype->fracpart + fxtype->intpart + 1; +} diff --git a/tools/gen_fx_consts/global.h b/tools/gen_fx_consts/global.h new file mode 100644 index 0000000000..b4509536fe --- /dev/null +++ b/tools/gen_fx_consts/global.h @@ -0,0 +1,27 @@ +#ifndef GUARD_GLOBAL_H +#define GUARD_GLOBAL_H + +#include +#include +#include +#include + +#define len(a) ((sizeof(a))/(sizeof(*(a)))) + +static inline noreturn __attribute__((format(printf, 1, 2))) void fatal_error(const char *fmt, ...) { + va_list va_args; + va_start(va_args, fmt); + vfprintf(stderr, fmt, va_args); + va_end(va_args); + fputc('\n', stderr); + exit(1); +} + +#ifndef __clang__ +#undef strlcat +#define strlcat(d,s,n) strncat(d,s,(n)-strlen(d)-1) +#undef strlcpy +#define strlcpy(d,s,n) strncpy(d,s,(n)-1) +#endif //__clang__ + +#endif //GUARD_GLOBAL_H diff --git a/tools/gen_fx_consts/main.c b/tools/gen_fx_consts/main.c new file mode 100644 index 0000000000..19787ced2a --- /dev/null +++ b/tools/gen_fx_consts/main.c @@ -0,0 +1,86 @@ +#include +#include +#include "fx.h" +#include "global.h" + +#define NAMEBUF_SIZ 32 + +static inline void usage(FILE *dest) { + fprintf(dest, "USAGE: gen_fx_consts FILENAME\n\n" + "FILENAME Path to write C header. Guard will be generated\n" + " automatically from the path.\n"); +} + +int main(int argc, char ** argv) { + int i; + char namebuf[NAMEBUF_SIZ] = ""; + char *header_guard; + FILE *outfile; + + FxConstTableInit(); + if (argc < 2) { + outfile = stdout; + header_guard = "NITRO_FX_FX_CONST_H_"; + } else { + outfile = fopen(argv[1], "w"); + if (outfile == NULL) { + fatal_error("Unable to open file '%s' for writing", argv[1]); + } + header_guard = strdup(argv[1]); + if (header_guard == NULL) { + fclose(outfile); + fatal_error("Unable to allocate temp buffer for header guard"); + } + for (i = 0; header_guard[i]; i++) { + switch (header_guard[i]) { + case '.': + case '/': + header_guard[i] = '_'; + break; + default: + header_guard[i] = toupper(header_guard[i]); + break; + } + } + } + fprintf(outfile, "#ifndef %s_\n" + "#define %s_\n\n", header_guard, header_guard); + for (i = 0; i < gNFxConstTable; i++) { + long long value; + int nbyte; + char *suffix; + char *fmtstr; + int j; + value = MakeFix(&gFxConstTable[i]); + nbyte = GetFxWidth(gFxConstTable[i].fxtype) / 8; + switch (nbyte) { + case 2: + suffix = ""; + fmtstr = "#define %-32s ((%s) 0x%04hx%s) // %.12f\n"; + value &= 0xFFFFLL; + break; + case 4: + suffix = "L"; + fmtstr = "#define %-32s ((%s) 0x%08lx%s) // %.12f\n"; + value &= 0xFFFFFFFFLL; + break; + case 8: + suffix = "LL"; + fmtstr = "#define %-32s ((%s) 0x%016llx%s) // %.12f\n"; + break; + default: + fatal_error("invalid integer width: %d", nbyte); + } + snprintf(namebuf, NAMEBUF_SIZ, "%s_%s", gFxConstTable[i].fxtype->name, gFxConstTable[i].name); + for (j = 0; j < NAMEBUF_SIZ && namebuf[j]; j++) { + namebuf[j] = toupper(namebuf[j]); + } + fprintf(outfile, fmtstr, namebuf, gFxConstTable[i].fxtype->name, value, suffix, gFxConstTable[i].value); + } + fprintf(outfile, "\n#endif //%s_\n", header_guard); + if (argc >= 2) { + fclose(outfile); + free(header_guard); + } + return 0; +} diff --git a/tools/mwasmarm_patcher/.gitignore b/tools/mwasmarm_patcher/.gitignore new file mode 100644 index 0000000000..c9b889c594 --- /dev/null +++ b/tools/mwasmarm_patcher/.gitignore @@ -0,0 +1 @@ +mwasmarm_patcher diff --git a/tools/mwasmarm_patcher/Makefile b/tools/mwasmarm_patcher/Makefile new file mode 100644 index 0000000000..ffdf82d980 --- /dev/null +++ b/tools/mwasmarm_patcher/Makefile @@ -0,0 +1,14 @@ +.PHONY: all clean + +CC := gcc +CFLAGS := -O3 + +.PHONY: all clean + +all: mwasmarm_patcher + @: + +clean: ; $(RM) mwasmarm_patcher$(EXE) + +mwasmarm_patcher: mwasmarm_patcher.c + $(CC) $(CFLAGS) -o $@ $< diff --git a/tools/mwasmarm_patcher/mwasmarm_patcher.c b/tools/mwasmarm_patcher/mwasmarm_patcher.c new file mode 100644 index 0000000000..1d7aa2044f --- /dev/null +++ b/tools/mwasmarm_patcher/mwasmarm_patcher.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include + +// mwasmarm patcher v1.2 +// Patches the Metrowerk C compiler assembler to stop the line ending bug and the 0x400 incbin bug. + +// Changelog: +// v1.1: Added patch definitions and looped over them to find the matching +// definition as well as the version. +// v1.2: Switched to array system for applying multiple patches for compiler +// versions and added 0x400 incbin fix for each version. + +struct PatchPair { + int offsetPatch; + int newByte; +}; + +struct PatchDef { + char *version; + char *sha1before; + char *sha1after; + struct PatchPair *patches; +}; + +// Patch definitions for each of the respective assembler versions. +struct PatchPair g12BasePatches[] = { + { 0x57614, 0x5 }, + { 0xD47, 0x8D }, + {0} +}; + +struct PatchPair g20BasePatches[] = { + { 0x57644, 0x5 }, + { 0xD47, 0x8D }, + {0} +}; + +struct PatchPair g20sp2p4Patches[] = { + { 0x57834, 0x5 }, + { 0xD47, 0x8D }, + {0} +}; + +// Table of definitions for each assembler version +struct PatchDef gPatchDefs[] = { + // mwasmarm 1.2/base definition + { + "mwasmarm 1.2/base", + "87f942cc0a0e90e73550d8d6f3fffcdeb5f69fa5", + "3395ac5decf49135d892e93a3e6dd38676025983", + g12BasePatches + }, + // mwasmarm 2.0/base definition + { + "mwasmarm 2.0/base", + "9d63877c776245129b4727b41d3e9e63cfc9cd28", + "ef75c3fb9f8d90cb4881386c41d8dc3ab4de7153", + g20BasePatches + }, + // mwasmarm 2.0/sp2p4 definition + { + "mwasmarm 2.0/sp2p4", + "448cb0c7f1ace4393e9a9562f819f7a9f049be83", + "caa84dd90b1987ab7b42749bd5c9dcfdcfef59f3", + g20sp2p4Patches + }, + {0} +}; + +// --------------------------------------------------------- +// Credit to ax6 for implementation of sha1 hash functions +// --------------------------------------------------------- + +void sha1_process_block (const unsigned char * block, uint32_t * state); + +unsigned char * calculate_sha1 (const void * data, unsigned length) { + uint32_t state[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}; + const char * current; + unsigned remaining; + for (current = data, remaining = length; remaining >= 64; current += 64, remaining -= 64) sha1_process_block((const uint8_t *)current, state); + // technically only {0} is necessary, but better safe than sorry + unsigned char last_block[64] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(last_block, current, remaining); + last_block[remaining] = 0x80; + if (remaining >= 56) { + sha1_process_block(last_block, state); + memset(last_block, 0, 64); + } + unsigned long long bit_length = ((unsigned long long) length) << 3; + for (remaining = 5; remaining; remaining --) { + last_block[58 + remaining] = bit_length; + bit_length >>= 8; + } + sha1_process_block(last_block, state); + unsigned char * result = malloc(20); + for (remaining = 0; remaining < 20; remaining ++) result[remaining] = state[remaining >> 2] >> ((~remaining & 3) << 3); + return result; +} + +static inline unsigned sha1_rotate (unsigned value, unsigned count) { + return (value << count) | (value >> (32 - count)); +} + +void sha1_process_block (const unsigned char * block, uint32_t * state) { + uint32_t words[80]; + unsigned pos, temp, count, a, b, c, d, e; + // constants used by SHA-1; they are actually simply the square roots of 2, 3, 5 and 10 as a fixed-point number (2.30 format) + const uint32_t hash_constants[4] = {0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6}; + memset(words, 0, 16 * sizeof(uint32_t)); + for (pos = 0; pos < 64; pos ++) words[pos >> 2] = (words[pos >> 2] << 8) | block[pos]; + for (pos = 16; pos < 80; pos ++) words[pos] = sha1_rotate(words[pos - 3] ^ words[pos - 8] ^ words[pos - 14] ^ words[pos - 16], 1); + a = *state; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + for (pos = 0; pos < 4; pos ++) for (count = 0; count < 20; count ++) { + temp = sha1_rotate(a, 5) + e + words[pos * 20 + count] + hash_constants[pos]; + switch (pos) { + case 0: + temp += (b & c) | (~b & d); + break; + case 2: + temp += (b & c) | (b & d) | (c & d); + break; + default: + temp += b ^ c ^ d; + } + e = d; + d = c; + c = sha1_rotate(b, 30); + b = a; + a = temp; + } + *state += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + +// --------------------------------------------------------- +// ax6 code end +// --------------------------------------------------------- + +__attribute__((format(printf, 1, 2))) +void fatal_printf(char *str, ...) { + va_list args; + va_start(args, str); + vprintf(str, args); + va_end(args); + exit(1); +} + +// return size in bytes +int get_file_size (FILE * fp) { + int curpos = ftell(fp); + fseek(fp, 0, SEEK_END); + int result = ftell(fp); + fseek(fp, curpos, SEEK_SET); + return result; +} + +#define SHA_DIGEST_LENGTH 20 + +void print_help(void) { + printf("Usage:\n" + "\tmwasmarm_patcher [OPTIONS] FILENAME\n\n" + "Arguments:\n" + "\tFILENAME: path to MWASMARM.exe program\n\n" + "OPTIONS:\n" + "\t-q/--quietly: Suppress verbose output\n" + "\t-h/--help: Print this message and exit\n"); +} + +int main(int argc, char *argv[]) { + int quietly = 0; + char* filename = NULL; + for (int i = 1; i < argc; i++) + { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quietly") == 0) + quietly = 1; + else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + print_help(); + exit(0); + } + else + { + print_help(); + fatal_printf("Unrecognized option: %s\n", argv[i]); + } + } else if (filename != NULL) { + print_help(); + fatal_printf("Excess filename supplied\n"); + } + else + filename = argv[i]; + } + if (filename == NULL) { + print_help(); + fatal_printf("Missing required argument: filename\n"); + } else { + // Open the file and read it's sha1 hash. + FILE *f = fopen(filename, "rb+"); + if (f == NULL) { + fatal_printf("ERROR: No file detected\n"); + } + int fsize = get_file_size(f); + unsigned char *string = malloc(fsize + 1); + if (string == NULL) { + fatal_printf("ERROR: Failed to allocate string variable\n"); + } + int readvar = fread(string, 1, fsize, f); // var to surpress warning + + // Check if sha1 matches either known assembler hashes. + unsigned char *sha1 = calculate_sha1(string, fsize); + if (sha1 == NULL) { + fatal_printf("ERROR: Failed to retrieve sha1 hash\n"); + } + free(string); + + char buf[(SHA_DIGEST_LENGTH*2)+1]; + for (int i=0; i < SHA_DIGEST_LENGTH; i++) { + sprintf(&(buf[i*2]), "%02x", sha1[i]); + } + free(sha1); + + // loop over each patch definition to attempt to locate a supported version and, if + // needed, apply the patch definitions. + for (int i = 0; gPatchDefs[i].sha1before != NULL; i++) { + // check if already patched for the current loop. + if (!strcmp(buf, gPatchDefs[i].sha1after)) { + if (!quietly) printf("Supported patched version detected (%s): no action needed\n", gPatchDefs[i].version); + return 0; + } else if(!strcmp(buf, gPatchDefs[i].sha1before)) { + // we found an unpatched version: apply the patches. + for (int j = 0; gPatchDefs[i].patches[j].offsetPatch != 0; j++) { + fseek(f, gPatchDefs[i].patches[j].offsetPatch, SEEK_SET); + fputc(gPatchDefs[i].patches[j].newByte, f); + } + if (!quietly) printf("Supported unpatched version detected (%s): assembler patched\n", gPatchDefs[i].version); + return 0; + } + } + // Unable to locate supported version, quitting + fatal_printf("ERROR: Unsupported mwasmarm.exe version\n"); + } + return 0; +}