diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..e2fe1323 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,44 @@ +name: Build + +# Build on every branch push, tag push, and pull request change: +on: [push, pull_request_target] + +jobs: + build_repo: + name: Build repo + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Install package requirements + run: sudo apt-get install -y git make git build-essential binutils-mips-linux-gnu python3 python3-pip + + - name: Install Python dependencies + run: python3 -m pip install -r ./tools/requirements-python.txt + + - name: Update submodules + run: git submodule update --init --recursive + + - name: Get extra dependencies + uses: actions/checkout@v4 + with: + repository: ${{ secrets.SF64_DEPENDENCIES }} + token: ${{ secrets.SF64_TOKEN }} + path: sf64-secret + + - name: Get the dependency + run: cp -r sf64-secret/baserom.us.z64 ./ + + - name: Compile + run: make init + +# - name: Upload frogress +# if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} +# run: python3 tools/upload_frogress.py --apikey ${{ secrets.PROGRESS_API_KEY }} diff --git a/.github/workflows/format.yaml b/.github/workflows/format.yaml new file mode 100644 index 00000000..3e6d933f --- /dev/null +++ b/.github/workflows/format.yaml @@ -0,0 +1,19 @@ +name: Check Format + +# Build on every branch push, tag push, and pull request change: +on: [push, pull_request] + +jobs: + build: + name: Check format + runs-on: ubuntu-latest + + steps: + - name: Checkout reposistory + uses: actions/checkout@v4 + + - name: Install package requirements + run: sudo apt-get install -y python3 python3-pip clang-format-14 clang-tidy-14 + + - name: Check format + run: ./tools/check_format.sh diff --git a/.vscode/settings.json b/.vscode/settings.json index 4c4b984f..ec06a287 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,6 +14,5 @@ "tools/gfxsotn/**": false, }, "files.associations": { - "functions.h": "c" }, } \ No newline at end of file diff --git a/Makefile b/Makefile index badd35e2..ace0d144 100644 --- a/Makefile +++ b/Makefile @@ -191,6 +191,9 @@ build/src/main/1EB50.o: OPTFLAGS := -O1 -g0 build/src/libultra/2BDF0.o: OPTFLAGS := -O1 -g0 build/src/libultra/2C700.o: OPTFLAGS := -O1 -g0 build/src/libultra/2D300.o: OPTFLAGS := -O1 -g0 +build/src/libultra/io/controller.o: OPTFLAGS := -O1 -g0 +build/src/libultra/libc/string.o: OPTFLAGS := -O2 -g0 +build/src/libultra/libc/ldiv.o: OPTFLAGS := -O2 -g0 # cc & asm-processor build/src/%.o: CC := $(ASM_PROC) $(ASM_PROC_FLAGS) $(CC) -- $(AS) $(ASFLAGS) -- @@ -198,14 +201,15 @@ build/src/%.o: CC := $(ASM_PROC) $(ASM_PROC_FLAGS) $(CC) -- $(AS) $(ASFLAGS) -- all: uncompressed init: - $(MAKE) clean - $(MAKE) decompress - $(MAKE) extract -j $(N_THREADS) - $(MAKE) all -j $(N_THREADS) - $(MAKE) compressed + @$(MAKE) clean + @$(MAKE) decompress + @$(MAKE) extract -j $(N_THREADS) + @$(MAKE) all -j $(N_THREADS) + @$(MAKE) compressed uncompressed: $(ROM) ifneq ($(COMPARE),0) + @echo MD5 CHECK: @md5sum $(ROM) @md5sum -c $(TARGET).$(VERSION).uncompressed.md5 endif @@ -223,9 +227,11 @@ decompress: $(BASEROM) @$(PYTHON) $(COMPTOOL) -de $(COMPTOOL_DIR) -m $(MIO0) $(BASEROM) $(BASEROM_UNCOMPRESSED) extract: - $(RM) -r asm/$(VERSION) bin/$(VERSION) - $(CAT) yamls/$(VERSION)/header.yaml yamls/$(VERSION)/makerom.yaml yamls/$(VERSION)/main.yaml yamls/$(VERSION)/overlays.yaml > $(SPLAT_YAML) - $(SPLAT) $(SPLAT_YAML) + @$(RM) -r asm/$(VERSION) bin/$(VERSION) + @echo "Unifying yamls..." + @$(CAT) yamls/$(VERSION)/header.yaml yamls/$(VERSION)/makerom.yaml yamls/$(VERSION)/main.yaml yamls/$(VERSION)/overlays.yaml > $(SPLAT_YAML) + @echo "Extracting..." + @$(SPLAT) $(SPLAT_YAML) clean: @git clean -fdx asm/ @@ -249,22 +255,25 @@ expected: #### Various Recipes #### -$(ROM): $(ELF) - $(OBJCOPY) -O binary $< $@ - +# Final ROM $(ROMC): $(BASEROM_UNCOMPRESSED) @echo "Compressing ROM..." @$(PYTHON) $(COMPTOOL) -c $(ROM) $(ROMC) -# TODO: update rom header checksum +# Uncompressed ROM +$(ROM): $(ELF) + @echo "ELF->ROM:" + $(OBJCOPY) -O binary $< $@ -# TODO: avoid using auto/undefined +# Link $(ELF): $(LIBULTRA_O) $(O_FILES) $(LD_SCRIPT) $(BUILD_DIR)/linker_scripts/$(VERSION)/hardware_regs.ld $(BUILD_DIR)/linker_scripts/$(VERSION)/undefined_syms.ld $(BUILD_DIR)/linker_scripts/$(VERSION)/pif_syms.ld $(BUILD_DIR)/linker_scripts/$(VERSION)/auto/undefined_syms_auto.ld $(BUILD_DIR)/linker_scripts/$(VERSION)/auto/undefined_funcs_auto.ld + @echo "Linking..." $(LD) $(LDFLAGS) -T $(LD_SCRIPT) \ -T $(BUILD_DIR)/linker_scripts/$(VERSION)/hardware_regs.ld -T $(BUILD_DIR)/linker_scripts/$(VERSION)/undefined_syms.ld -T $(BUILD_DIR)/linker_scripts/$(VERSION)/pif_syms.ld \ -T $(BUILD_DIR)/linker_scripts/$(VERSION)/auto/undefined_syms_auto.ld -T $(BUILD_DIR)/linker_scripts/$(VERSION)/auto/undefined_funcs_auto.ld \ -Map $(LD_MAP) -o $@ +# PreProcessor $(BUILD_DIR)/%.ld: %.ld $(CPP) $(CPPFLAGS) $(BUILD_DEFINES) $(IINC) $< > $@ @@ -289,4 +298,4 @@ $(BUILD_DIR)/%.o: %.c # Print target for debugging print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true -.PHONY: all uncompressed compressed clean init extract format checkformat decompress \ No newline at end of file +.PHONY: all uncompressed compressed clean init extract expected format checkformat decompress \ No newline at end of file diff --git a/README.md b/README.md index 95f3349d..e6ea1d7e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is a WIP **decompilation** of ***Starfox 64***. The purpose of the project It currently builds the following ROM: -* starfox64.us.z64 `md5: 741a94eee093c4c8684e66b89f8685e8` +* starfox64.us.z64 `MD5: 741a94eee093c4c8684e66b89f8685e8` **This repo does not include any assets or assembly code necessary for compiling the ROM. A prior copy of the game is required to extract the required assets.** @@ -34,7 +34,7 @@ Under Debian / Ubuntu (which we recommend using), you can install them with the ```bash sudo apt update -sudo apt install make git build-essential binutils-mips-linux-gnu python3 python3-pip libpng-dev +sudo apt install make git build-essential binutils-mips-linux-gnu python3 python3-pip ``` #### 2. Clone the repository diff --git a/include/PR/controller.h b/include/PR/controller.h new file mode 100644 index 00000000..d2b0b812 --- /dev/null +++ b/include/PR/controller.h @@ -0,0 +1,51 @@ +#ifndef PR_CONTROLLER_H +#define PR_CONTROLLER_H + +#include "global.h" +#include "PR/os.h" + +typedef struct { + /* 0x00 */ u8 align; + /* 0x01 */ u8 txsize; + /* 0x02 */ u8 rxsize; + /* 0x03 */ u8 poll; + /* 0x04 */ u8 typeh; + /* 0x05 */ u8 typel; + /* 0x06 */ u8 status; + /* 0x07 */ u8 align1; +} __OSContRequestHeader; // size = 0x8 + +// Joybus commands +#define CONT_CMD_REQUEST_STATUS 0 +#define CONT_CMD_READ_BUTTON 1 +#define CONT_CMD_READ_PAK 2 +#define CONT_CMD_WRITE_PAK 3 +#define CONT_CMD_READ_EEPROM 4 +#define CONT_CMD_WRITE_EEPROM 5 +#define CONT_CMD_READ36_VOICE 9 +#define CONT_CMD_WRITE20_VOICE 10 +#define CONT_CMD_READ2_VOICE 11 +#define CONT_CMD_WRITE4_VOICE 12 +#define CONT_CMD_SWRITE_VOICE 13 +#define CONT_CMD_CHANNEL_RESET 0xFD +#define CONT_CMD_RESET 0xFF + +#define UNK(n) 500000 * (u64)(n) / 1000000 + +typedef struct +{ + u32 ramarray[15]; + u32 pifstatus; +} OSPifRam; + +extern u32 __osContinitialized; +extern u8 __osMaxControllers; +extern u8 __osContLastCmd; +extern OSPifRam __osContPifRam; +extern OSMesgQueue __osEepromTimerQ; +extern OSMesg __osEepromTimerMsg; + +void __osPackRequestData(u8 poll); +void __osContGetInitData(u8* pattern, OSContStatus* data); + +#endif diff --git a/include/PR/os.h b/include/PR/os.h index d6ec937a..073232e7 100644 --- a/include/PR/os.h +++ b/include/PR/os.h @@ -877,7 +877,7 @@ extern int osStopTimer(OSTimer *); /* Controller interface */ -extern s32 osContInit(OSMesgQueue *, u8 *, OSContStatus *); +extern s32 osContInit(OSMesgQueue * mq, u8 * bitpattern, OSContStatus * data); extern s32 osContReset(OSMesgQueue *, OSContStatus *); extern s32 osContStartQuery(OSMesgQueue *); extern s32 osContStartReadData(OSMesgQueue *); diff --git a/include/PR/ultratypes.h b/include/PR/ultratypes.h index 85e44f2e..6af7c9ff 100644 --- a/include/PR/ultratypes.h +++ b/include/PR/ultratypes.h @@ -57,6 +57,8 @@ typedef volatile long long vs64; /* signed 64-bit */ typedef float f32; /* single prec floating point */ typedef double f64; /* double prec floating point */ +typedef u32 size_t; + #endif /* _LANGUAGE_C */ diff --git a/include/libc/stddef.h b/include/libc/stddef.h new file mode 100644 index 00000000..1dc74697 --- /dev/null +++ b/include/libc/stddef.h @@ -0,0 +1,14 @@ +#ifndef LIBC_STDDEF_H +#define LIBC_STDDEF_H + +#include "PR/ultratypes.h" + +typedef s32 ptrdiff_t; + +#ifdef __GNUC__ +#define offsetof(structure, member) __builtin_offsetof (structure, member) +#else +#define offsetof(structure, member) ((size_t)&(((structure*)0)->member)) +#endif + +#endif /* STDDEF_H */ \ No newline at end of file diff --git a/include/libc/stdlib.h b/include/libc/stdlib.h new file mode 100644 index 00000000..ec15b4f6 --- /dev/null +++ b/include/libc/stdlib.h @@ -0,0 +1,28 @@ +#ifndef LIBC_STDLIB_H +#define LIBC_STDLIB_H + +#include "libc/stddef.h" + +typedef struct { + /* 0x0 */ int quot; + /* 0x4 */ int rem; +} div_t; + +typedef struct { + /* 0x0 */ long quot; + /* 0x4 */ long rem; +} ldiv_t; + +typedef struct { + /* 0x0 */ long long quot; + /* 0x8 */ long long rem; +} lldiv_t; + +typedef int ssize_t; + +typedef long wchar_t; + +ldiv_t ldiv(long numer, long denom); +lldiv_t lldiv(long long numer, long long denom); + +#endif /* STDLIB_H */ diff --git a/include/macros.h b/include/macros.h index 5a7124ff..424291b7 100644 --- a/include/macros.h +++ b/include/macros.h @@ -1,6 +1,6 @@ #ifndef MACROS_H #define MACROS_H - +#define ARRAY_COUNT(arr) (s32)(sizeof(arr) / sizeof(arr[0])) #endif // MACROS_H diff --git a/src/libultra/io/controller.c b/src/libultra/io/controller.c index ba278426..90bf1169 100644 --- a/src/libultra/io/controller.c +++ b/src/libultra/io/controller.c @@ -1,7 +1,87 @@ -#include "common.h" +#include "global.h" +#include "PR/controller.h" -#pragma GLOBAL_ASM("asm/us/nonmatchings/libultra/io/controller/osContInit.s") +s32 osContInit(OSMesgQueue* mq, u8* bitpattern, OSContStatus* data) { + OSMesg dummy; + s32 ret = 0; + OSTime t; + OSTimer mytimer; + OSMesgQueue timerMesgQueue; -#pragma GLOBAL_ASM("asm/us/nonmatchings/libultra/io/controller/__osContGetInitData.s") + if (__osContinitialized != 0) { + return 0; + } -#pragma GLOBAL_ASM("asm/us/nonmatchings/libultra/io/controller/__osPackRequestData.s") + __osContinitialized = 1; + t = osGetTime(); + + if (t < UNK(osClockRate)) { + osCreateMesgQueue(&timerMesgQueue, &dummy, 1); + osSetTimer(&mytimer, UNK(osClockRate) - t, 0, &timerMesgQueue, &dummy); + osRecvMesg(&timerMesgQueue, &dummy, OS_MESG_BLOCK); + } + __osMaxControllers = 4; + + __osPackRequestData(CONT_CMD_REQUEST_STATUS); + + ret = __osSiRawStartDma(OS_WRITE, __osContPifRam.ramarray); + osRecvMesg(mq, &dummy, OS_MESG_BLOCK); + + ret = __osSiRawStartDma(OS_READ, __osContPifRam.ramarray); + osRecvMesg(mq, &dummy, OS_MESG_BLOCK); + + __osContGetInitData(bitpattern, data); + __osContLastCmd = CONT_CMD_REQUEST_STATUS; + __osSiCreateAccessQueue(); + osCreateMesgQueue(&__osEepromTimerQ, &__osEepromTimerMsg, 1); + + return ret; +} + +void __osContGetInitData(u8* pattern, OSContStatus* data) { + u8* ptr; + __OSContRequestHeader requestHeader; + s32 i; + u8 bits; + + bits = 0; + ptr = (u8*)__osContPifRam.ramarray; + for (i = 0; i < __osMaxControllers; i++, ptr += sizeof(requestHeader), data++) { + requestHeader = *(__OSContRequestHeader*)ptr; + data->errno = (requestHeader.rxsize & 0xC0) >> 4; + if (data->errno == 0) { + data->type = requestHeader.typel << 8 | requestHeader.typeh; + data->status = requestHeader.status; + + bits |= 1 << i; + } + } + *pattern = bits; +} + +void __osPackRequestData(u8 poll) { + u8* ptr; + __OSContRequestHeader requestHeader; + s32 i; + + for (i = 0; i < 16; i++) { + __osContPifRam.ramarray[i] = 0; + } + + __osContPifRam.pifstatus = CONT_CMD_READ_BUTTON; + ptr = (u8*)__osContPifRam.ramarray; + requestHeader.align = 255; + requestHeader.txsize = 1; + requestHeader.rxsize = 3; + requestHeader.poll = poll; + requestHeader.typeh = 255; + requestHeader.typel = 255; + requestHeader.status = 255; + requestHeader.align1 = 255; + + for (i = 0; i < __osMaxControllers; i++) { + *(__OSContRequestHeader*)ptr = requestHeader; + ptr += sizeof(requestHeader); + } + *ptr = 254; +} diff --git a/src/libultra/libc/ldiv.c b/src/libultra/libc/ldiv.c index a9f3099e..657071f6 100644 --- a/src/libultra/libc/ldiv.c +++ b/src/libultra/libc/ldiv.c @@ -1,5 +1,28 @@ -#include "common.h" +#include "global.h" +#include "libc/stdlib.h" -#pragma GLOBAL_ASM("asm/us/nonmatchings/libultra/libc/ldiv/lldiv.s") +lldiv_t lldiv(long long numer, long long denom) { + lldiv_t val; -#pragma GLOBAL_ASM("asm/us/nonmatchings/libultra/libc/ldiv/ldiv.s") + val.quot = numer / denom; + val.rem = numer - denom * val.quot; + if ((val.quot < 0) && (val.rem > 0)) { + val.quot++; + val.rem -= denom; + } + + return val; +} + +ldiv_t ldiv(long numer, long denom) { + ldiv_t val; + + val.quot = numer / denom; + val.rem = numer - denom * val.quot; + if ((val.quot < 0) && (val.rem > 0)) { + val.quot++; + val.rem -= denom; + } + + return val; +} diff --git a/src/libultra/libc/string.c b/src/libultra/libc/string.c index 5561b1b9..f35bb375 100644 --- a/src/libultra/libc/string.c +++ b/src/libultra/libc/string.c @@ -1,7 +1,38 @@ -#include "common.h" +#include "global.h" -#pragma GLOBAL_ASM("asm/us/nonmatchings/libultra/libc/string/memcpy.s") +void* memcpy(void* s1, const void* s2, size_t n) { + unsigned char* su1 = (unsigned char*)s1; + const unsigned char* su2 = (const unsigned char*)s2; -#pragma GLOBAL_ASM("asm/us/nonmatchings/libultra/libc/string/strlen.s") + while (n > 0) { + *su1 = *su2; + su1++; + su2++; + n--; + } -#pragma GLOBAL_ASM("asm/us/nonmatchings/libultra/libc/string/strchr.s") + return s1; +} + +size_t strlen(const char* s) { + const char* sc = s; + + while (*sc != '\0') { + sc++; + } + + return (size_t)(sc - s); +} + +const char* strchr(const char* s, int c) { + const unsigned char ch = c; + + while (*s != ch) { + if (*s == '\0') { + return NULL; + } + s++; + } + + return s; +} diff --git a/tools/check_format.sh b/tools/check_format.sh new file mode 100755 index 00000000..051e5a7b --- /dev/null +++ b/tools/check_format.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +STATUSOLD=`git status --porcelain` +./tools/format.py -j +if [ $? -ne 0 ] +then + echo "Formatter failed. Exiting." + exit -1 +fi +STATUSNEW=`git status --porcelain` + +if [ "${STATUSOLD}" != "${STATUSNEW}" ]; +then + echo "" + echo "Misformatted files found. Run 'make format' and verify codegen is not impacted." + echo "" + diff --unified=0 --label "Old git status" <(echo "${STATUSOLD}") --label "New git status" <(echo "${STATUSNEW}") + echo "" + echo "Exiting." + exit 1 +fi + +exit 0 diff --git a/tools/permuter_settings.toml b/tools/permuter_settings.toml index d9acc8df..e86aa08a 100644 --- a/tools/permuter_settings.toml +++ b/tools/permuter_settings.toml @@ -1 +1,4 @@ -compiler_type = "ido" \ No newline at end of file +compiler_type = "ido" + +[decompme.compilers] +"tools/ido_recomp/linux/5.3/cc" = "ido5.3" \ No newline at end of file