From b6a0ddbeb6883961d4c7c7f02ca7d7dc9cec93ef Mon Sep 17 00:00:00 2001 From: Mark Street <22226349+mkst@users.noreply.github.com> Date: Sat, 18 May 2024 11:41:00 +0100 Subject: [PATCH] Add mwccgap (#1131) OK. Here goes. Version 0.0.1 alpha of the MWCC Global Assembly Processor (mwccgap). It's currently very simple/limited, but it appears to work for `src/servant/tt_000/10E8.c`. There is lot more that can be done to improve mwccgap - i.e. supporting .rodata migration would be a good addition, but let's see how far we can get with it in it's current state. Note that the Makefile could do with some improvements - we don't nede to use mwccgap for any C file that *dont* have INCLUDE_ASM macros (it's a waste of time) so these could be ignored, i.e. for SSSV I do something like this to find the files that need fixing up: ``` GLOBAL_ASM_C_FILES := $(shell $(GREP) GLOBAL_ASM $(SRC_DIR) /dev/null) ``` .. although this is perhaps too simple given that SOTN has a mix of PSP and PSX functions (and therefore there may be INCLUDE_ASM for a PSX function but none for PSP functions... --- .gitmodules | 3 + Makefile | 6 +- Makefile.psp.mk | 57 ++++++++------ config/splat.pspeu.tt_000.yaml | 8 +- diff_settings.py | 2 +- src/servant/tt_000/10E8.c | 22 +++--- tools/asm-differ | 2 +- tools/mwccgap | 1 + tools/mwcpp.py | 120 ------------------------------ tools/mwcpp_test.py | 132 --------------------------------- 10 files changed, 62 insertions(+), 291 deletions(-) create mode 160000 tools/mwccgap delete mode 100755 tools/mwcpp.py delete mode 100644 tools/mwcpp_test.py diff --git a/.gitmodules b/.gitmodules index 9dac35d93..cf9225ead 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "tools/maspsx"] path = tools/maspsx url = https://github.com/mkst/maspsx.git +[submodule "tools/mwccgap"] + path = tools/mwccgap + url = https://github.com/mkst/mwccgap.git diff --git a/Makefile b/Makefile index f83a3a54a..17084fbfc 100644 --- a/Makefile +++ b/Makefile @@ -15,12 +15,12 @@ LD := $(CROSS)ld CPP := $(CROSS)cpp OBJCOPY := $(CROSS)objcopy AS_FLAGS += -Iinclude -march=r3000 -mtune=r3000 -no-pad-sections -O1 -G0 -PSXCC_FLAGS := -quiet -mcpu=3000 -fgnu-linker -mgas -gcoff +PSXCC_FLAGS := -quiet -mcpu=3000 -fgnu-linker -mgas -gcoff CC_FLAGS += -G0 -w -O2 -funsigned-char -fpeephole -ffunction-cse -fpcc-struct-return -fcommon -fverbose-asm -msoft-float -g CPP_FLAGS += -Iinclude -Iinclude/psxsdk -undef -Wall -fno-builtin CPP_FLAGS += -Dmips -D__GNUC__=2 -D__OPTIMIZE__ -D__mips__ -D__mips -Dpsx -D__psx__ -D__psx -D_PSYQ -D__EXTENSIONS__ -D_MIPSEL -D_LANGUAGE_C -DLANGUAGE_C -DNO_LOGS -DHACKS -DUSE_INCLUDE_ASM CPP_FLAGS += -D_internal_version_$(VERSION) -DSOTN_STR -LD_FLAGS := -nostdlib --no-check-sections +LD_FLAGS := -nostdlib --no-check-sections # Directories ASM_DIR := asm/$(VERSION) @@ -62,7 +62,7 @@ M2C_DIR := $(TOOLS_DIR)/m2c M2C_APP := $(M2C_DIR)/m2c.py M2C := $(PYTHON) $(M2C_APP) M2C_ARGS := -P 4 -SOTNSTR := $(PYTHON) $(TOOLS_DIR)/sotn_str/sotn_str.py process +SOTNSTR := $(PYTHON) $(TOOLS_DIR)/sotn_str/sotn_str.py process MASPSX_DIR := $(TOOLS_DIR)/maspsx MASPSX_APP := $(MASPSX_DIR)/maspsx.py MASPSX := $(PYTHON) $(MASPSX_APP) --expand-div --aspsx-version=2.34 diff --git a/Makefile.psp.mk b/Makefile.psp.mk index 080fd57d1..88c41f231 100644 --- a/Makefile.psp.mk +++ b/Makefile.psp.mk @@ -1,16 +1,24 @@ -GNUASPSP := mipsel-linux-gnu-as -I include/ -G0 -march=r6000 -mabi=eabi -MWASPSP := bin/wibo bin/asm_psp_elf.exe -gnu -ASPSP := $(GNUASPSP) +WIBO := bin/wibo +MWCCPSP := bin/mwccpsp.exe -GNULDPSP := mipsel-linux-gnu-ld -MWLDPSP := bin/wibo bin/mwldpsp.exe -partial -nostdlib -msgstyle gcc -sym full,elf -g -LDPSP := $(GNULDPSP) +GNUASPSP := mipsel-linux-gnu-as -I include/ -G0 -march=r6000 -mabi=eabi +MWASPSP := $(WIBO) bin/asm_psp_elf.exe -gnu +ASPSP := $(GNUASPSP) -PSP_BUILD_DIR := build/pspeu -CCPSP := MWCIncludes=bin/ bin/wibo bin/mwccpsp.exe -PSP_EU_TARGETS := tt_000 -SPLAT_PIP := splat split -MWCPP_APP := python3 tools/mwcpp.py +GNULDPSP := mipsel-linux-gnu-ld +MWLDPSP := $(WIBO) bin/mwldpsp.exe -partial -nostdlib -msgstyle gcc -sym full,elf -g +LDPSP := $(GNULDPSP) + +MWCCGAP_DIR := $(TOOLS_DIR)/mwccgap +MWCCGAP_APP := $(MWCCGAP_DIR)/mwccgap.py +MWCCGAP := $(PYTHON) $(MWCCGAP_APP) + +PSP_BUILD_DIR := build/pspeu +CCPSP := MWCIncludes=bin/ $(WIBO) $(MWCCPSP) +PSP_EU_TARGETS := tt_000 +SPLAT_PIP := splat split + +MWCCPSP_FLAGS := -gccinc -Iinclude -D_internal_version_$(VERSION) -O0 -c -lang c -sdatathreshold 0 define list_src_files_psp $(foreach dir,$(ASM_DIR)/$(1),$(wildcard $(dir)/**.s)) @@ -27,15 +35,25 @@ build_pspeu: tt_000_psp extract_pspeu: $(addprefix $(PSP_BUILD_DIR)/,$(addsuffix .ld,$(PSP_EU_TARGETS))) -bin/wibo: +$(WIBO): wget -O $@ https://github.com/decompals/wibo/releases/download/0.6.13/wibo - sha256sum --check bin/wibo.sha256 - chmod +x bin/wibo -bin/mwccpsp.exe: bin/wibo bin/mwccpsp_3.0.1_147 + sha256sum --check $(WIBO).sha256 + chmod +x $(WIBO) +$(MWCCPSP): $(WIBO) bin/mwccpsp_3.0.1_147 -$(PSP_BUILD_DIR)/%.c.o: %.c bin/mwccpsp.exe +$(MWCCGAP_APP): + git submodule init $(MWCCGAP_DIR) + git submodule update $(MWCCGAP_DIR) + +$(PSP_BUILD_DIR)/%.c.o: %.c $(MWCCPSP) $(MWCCGAP_APP) mkdir -p $(dir $@) - $(MWCPP_APP) $< -o $<.post.c && (($(CCPSP) -gccinc -Iinclude -D_internal_version_$(VERSION) -O0 -c -lang c -sdatathreshold 0 -o $@ $<.post.c && rm $<.post.c) || (rm $<.post.c && exit 1)) + if grep -q INCLUDE_ASM $<; then \ + $(MWCCGAP) $< $@ --mwcc-path $(MWCCPSP) --use-wibo --wibo-path $(WIBO) --asm-dir-prefix asm/pspeu $(MWCCPSP_FLAGS) ; \ + else \ + $(CCPSP) $< -o $@ $(MWCCPSP_FLAGS) ; \ + fi + + $(PSP_BUILD_DIR)/asm/psp%.s.o: asm/psp%.s mkdir -p $(dir $@) $(ASPSP) -o $@ $< @@ -45,7 +63,7 @@ $(PSP_BUILD_DIR)/assets/servant/tt_000/header.bin.o: assets/servant/tt_000/heade mkdir -p $(dir $@) mipsel-linux-gnu-ld -r -b binary -o $@ $< -tt_000_psp: $(PSP_BUILD_DIR)/tt_000.bin +tt_000_psp: $(PSP_BUILD_DIR)/tt_000.bin $(PSP_BUILD_DIR)/assets/servant/tt_000/header.bin.o $(PSP_BUILD_DIR)/tt_%.bin: $(PSP_BUILD_DIR)/tt_%.elf $(OBJCOPY) -O binary $< $@ @@ -53,6 +71,3 @@ $(PSP_BUILD_DIR)/tt_%.ld: $(CONFIG_DIR)/splat.pspeu.tt_%.yaml $(PSX_BASE_SYMS) $ $(SPLAT_PIP) $< $(PSP_BUILD_DIR)/tt_%.elf: $(PSP_BUILD_DIR)/tt_%.ld $$(call list_o_files_psp,servant/tt_$$*) $(call link,tt_$*,$@) - -# cannot remove it for some reason? makefile bug? -_ignoreme_tt_000: $(PSP_BUILD_DIR)/src/servant/tt_000_psp/80.c.o $(PSP_BUILD_DIR)/asm/pspeu/servant/tt_000/data/4C80.data.s.o $(BUILD_DIR)/asm/pspeu/servant/tt_000/data/5E00.rodata.s.o diff --git a/config/splat.pspeu.tt_000.yaml b/config/splat.pspeu.tt_000.yaml index 4cf2fa536..1b68e2922 100644 --- a/config/splat.pspeu.tt_000.yaml +++ b/config/splat.pspeu.tt_000.yaml @@ -24,7 +24,13 @@ options: - ".rodata" - ".bss" ld_bss_is_noload: True - # disasm_unknown: True + disasm_unknown: True + asm_inc_header: | + .set noat /* allow manual use of $at */ + .set noreorder /* don't insert nops after branches */ + .include "macro.inc" +sha1: c8c34ac1d46b31e2e5336df271aa2409f44c9d01 + segments: - [0x0, bin, header] - name: tt_000 diff --git a/diff_settings.py b/diff_settings.py index ef3a7103c..4e10dfd42 100644 --- a/diff_settings.py +++ b/diff_settings.py @@ -95,5 +95,5 @@ def apply(config, args): apply_psx_base(config, version, name) else: apply_psx_bin(config, version, name) - config["arch"] = "mipsel" + config["arch"] = "mipsel:4000" if version == "pspeu" else "mipsel" config["objdump_executable"] = "mipsel-linux-gnu-objdump" diff --git a/src/servant/tt_000/10E8.c b/src/servant/tt_000/10E8.c index 6f705da02..1989f208d 100644 --- a/src/servant/tt_000/10E8.c +++ b/src/servant/tt_000/10E8.c @@ -5,6 +5,11 @@ #define SFX_BAT_SCREECH SOUND_BAT_SCREECH #define SFX_BAT_NOTIFY SE_UI_OVERWRITE_MSG +#ifdef VERSION_PSP +#undef INCLUDE_ASM +#define INCLUDE_ASM(FOLDER, NAME) +#endif + #ifndef VERSION_PSP s32 D_801748D8[0x80]; Collider D_80174AD8; @@ -67,18 +72,8 @@ ServantDesc g_ServantDesc = { #endif #ifdef VERSION_PSP -extern ServantDesc g_ServantDesc; extern s32 D_80174D3C; - -void DestroyEntity(); -s32 func_80174864(void); -s32 func_801746A0(s32 arg0); -void ProcessEvent(); -void CreateEventEntity(Entity* entityParent, s32 entityId, s32 params); - -void func_80173F74(); -void func_80173F30(); - +void DestroyEntity(Entity* entity); #endif void func_801710E8(Entity* entity, AnimationFrame* anim) { @@ -197,7 +192,10 @@ s32 func_801713C8(Entity* entity) { return 0; if (entity->hitPoints >= 0x7000) return 0; - return entity->hitPoints > 0; + if (entity->hitPoints <= 0) + return 0; + + return 1; } #endif diff --git a/tools/asm-differ b/tools/asm-differ index e8bddb025..f5fc9026f 160000 --- a/tools/asm-differ +++ b/tools/asm-differ @@ -1 +1 @@ -Subproject commit e8bddb02576e73aecb90f813bfb452417c37b257 +Subproject commit f5fc9026f0b164966ea7bc864335216f9f02ba02 diff --git a/tools/mwccgap b/tools/mwccgap new file mode 160000 index 000000000..63417712b --- /dev/null +++ b/tools/mwccgap @@ -0,0 +1 @@ +Subproject commit 63417712bcf77aaa24b83e0e899493de42393ef7 diff --git a/tools/mwcpp.py b/tools/mwcpp.py deleted file mode 100755 index 55be64fde..000000000 --- a/tools/mwcpp.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import re -import sys -from typing import TextIO - -r_lui = r"\s*lui\s*\$(.*),\s\%hi\((.*)\)" -r_lui_addiu_combo = r"\s*addiu\s*\$(.*),\s\$(.*),\s\%lo\((.*)\)" -r_hilo = r"\/\*\s*\w*\s*\w*\s*(\w*)\s*\*\/\s*\w*\s*\$\w*,.*(?:%hi|%lo).*" -r_hi = r"%hi\((.*)\)" -r_lo = r"%lo\((.*)\)\(|%lo\((.*)\)" -r_jalr = r"jalr\s*\$([a-z][a-z0-9])" -r_jlabel = r"jlabel \.(.*)" - - -def process_asm_line(asm_f: TextIO, line: str) -> str | None: - if line == "": - return None - - # skip glabel - if line.startswith("glabel"): - return "" - - # skip .size - if line.startswith(".size"): - return "" - - match_jlabel = re.search(r_jlabel, line) - if match_jlabel: - label_name = match_jlabel.group(1) - line = f"{label_name}:\n" - - # do not use '.' on label names - line = line.replace(".L", "L") - - # remove trailing hash comment - if "#" in line: - line = line.split("#")[0] + "\n" - - # jalr needs two arguments - jalr_match = re.search(r_jalr, line) - if jalr_match: - reg_name = jalr_match.group(1) - return line.replace(reg_name, f"ra, ${reg_name}") - - r_hilo_match = re.search(r_hilo, line) - if r_hilo_match: - raw_data = r_hilo_match.group(1) - if len(raw_data) == 8: - return f".word 0x{raw_data[6:8]}{raw_data[4:6]}{raw_data[2:4]}{raw_data[0:2]}\n" - - # return unpatched assembly line - return line - - -def include_asm(c_line: str, out: TextIO, version: str) -> None: - match = re.search(r'\s*INCLUDE_ASM\(\s*"(.*)",\s*(\w*)\)', c_line) - if match: - base_path = match.group(1) - func_name = match.group(2) - out.write(f"asm void {func_name}() {{\n") - with open(f"asm/{version}/{base_path}/{func_name}.s", "r") as f: - try: - while True: - line = f.readline() - patched_line = process_asm_line(f, line) - if patched_line == None: - break - out.write(patched_line) - except Exception as ex: - print(f"line {line} caused an exception") - raise ex - out.write(f"}}\n") - else: - out.write(c_line) - - -def process_file(file_name_in: str, out: TextIO, version: str) -> None: - with open(file_name_in, "r") as fin: - while True: - line = fin.readline() - if not line: - return None - if line.startswith("INCLUDE_ASM"): - try: - include_asm(line, out, version) - except Exception as ex: - print(f"{line} caused an exception") - raise - else: - out.write(line) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Pre-process Metrowerks C files to overcome certain compiler limitations" - ) - parser.add_argument( - "--version", required=False, type=str, default="pspeu", help="Game version" - ) - parser.add_argument( - "input", - type=str, - help="Input C file to pre-process", - ) - parser.add_argument( - "-o", - "--output", - type=str, - help="Output file, stdout if not specified", - ) - args = parser.parse_args() - if args.version == None: - args.version = "pspeu" - if args.output: - with open(args.output, "w") as fout: - process_file(args.input, fout, args.version) - else: - process_file(args.input, sys.stdout, args.version) diff --git a/tools/mwcpp_test.py b/tools/mwcpp_test.py deleted file mode 100644 index b3cf6d3a9..000000000 --- a/tools/mwcpp_test.py +++ /dev/null @@ -1,132 +0,0 @@ -import os -import random -import shutil -import unittest - -from mwcpp import process_file - - -class TestProcessFile(unittest.TestCase): - @classmethod - def setUp(self) -> None: - self.seed = f"{random.randint(0, 100000)}" - self.c_file_name = f"test_file_{self.seed}.c" - self.asm_dir_name = f"asm/{self.seed}" - self.output_name = f"test_processed_file_{self.seed}.c" - os.makedirs(f"{self.asm_dir_name}/ovl") - return super().setUp(self) - - @classmethod - def tearDown(self) -> None: - shutil.rmtree(self.asm_dir_name) - os.remove(self.c_file_name) - os.remove(self.output_name) - return super().tearDown(self) - - def helper_process_lines(self, assembly): - with open(self.c_file_name, "w") as f: - f.write(f'INCLUDE_ASM("ovl", func_name)') - with open(f"{self.asm_dir_name}/ovl/func_name.s", "w") as f: - f.write(assembly) - with open(self.output_name, "w") as f: - process_file(self.c_file_name, f, self.seed) - with open(self.output_name, "r") as f: - return "".join(f.readlines()) - - def test_process_basic_include_asm(self): - with open(self.c_file_name, "w") as f: - f.writelines( - [ - "ignore this line\n", - "// this too\n" f'INCLUDE_ASM("ovl", func_name)\n', - f'// INCLUDE_ASM("ovl", func_name)\n', - "", - ] - ) - with open(f"{self.asm_dir_name}/ovl/func_name.s", "w") as f: - f.write("glabel function_name_should_be_skipped\n") - f.write("/* 0 */ nop\n") - f.write(".size this_part_should_be_skipped\n") - with open(self.output_name, "w") as f: - process_file(self.c_file_name, f, self.seed) - with open(self.output_name, "r") as f: - lines = "".join(f.readlines()) - - self.assertEqual( - lines, - """ignore this line -// this too -asm void func_name() { -/* 0 */ nop -} -// INCLUDE_ASM("ovl", func_name) -""", - ) - - def test_fix_jalr(self): - self.assertEqual( - self.helper_process_lines( - """jalr $v0 -""" - ), - """asm void func_name() { -jalr $ra, $v0 -} -""", - ) - - def test_fix_jlabel(self): - self.assertEqual( - self.helper_process_lines( - """jlabel .LHELLO -""" - ), - """asm void func_name() { -LHELLO: -} -""", - ) - - def test_fix_lo_hi(self): - self.assertEqual( - self.helper_process_lines( - """/* XXXX 09012348 DEADBEEF */ lui $v1, %hi(D_92EFFDE) -/* XXXX 09012344 BADC0FFE */ lh $a0, %lo(D_92EFFDE) -""" - ), - """asm void func_name() { -.word 0xEFBEADDE -.word 0xFE0FDCBA -} -""", - ) - - def test_fix_dot_on_label_names(self): - self.assertEqual( - self.helper_process_lines( - """jmp .LABEL -.LABEL: -""" - ), - """asm void func_name() { -jmp LABEL -LABEL: -} -""", - ) - - def test_remove_trailing_hash_comment(self): - self.assertEqual( - self.helper_process_lines( - """nop# comment -""" - ), - """asm void func_name() { -nop -} -""", - ) - - -if __name__ == "__main__": - unittest.main()