From d59fbee932dd17e8c7b746859b2f42baaa5e5f60 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:37:57 -0400 Subject: [PATCH 01/17] Remove unused C versions of gu functions which will conflict with ASM versions under the new build system This is a holdover from the decomp, as it must support older libultra versions which used these functions; as we're basing this project off of the final 2.0L, they gotta go --- src/gu/mtxcatf.c | 60 ---------------------------------------------- src/gu/normalize.c | 22 ----------------- src/gu/scale.c | 30 ----------------------- src/gu/translate.c | 29 ---------------------- 4 files changed, 141 deletions(-) delete mode 100644 src/gu/mtxcatf.c delete mode 100644 src/gu/normalize.c delete mode 100644 src/gu/scale.c delete mode 100644 src/gu/translate.c diff --git a/src/gu/mtxcatf.c b/src/gu/mtxcatf.c deleted file mode 100644 index 77acfba5..00000000 --- a/src/gu/mtxcatf.c +++ /dev/null @@ -1,60 +0,0 @@ - -/* - * Copyright 1995, Silicon Graphics, Inc. - * ALL RIGHTS RESERVED - * - * UNPUBLISHED -- Rights reserved under the copyright laws of the United - * States. Use of a copyright notice is precautionary only and does not - * imply publication or disclosure. - * - * U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND: - * Use, duplication or disclosure by the Government is subject to restrictions - * as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights - * in Technical Data and Computer Software clause at DFARS 252.227-7013 and/or - * in similar or successor clauses in the FAR, or the DOD or NASA FAR - * Supplement. Contractor/manufacturer is Silicon Graphics, Inc., - * 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311. - * - * THE CONTENT OF THIS WORK CONTAINS CONFIDENTIAL AND PROPRIETARY - * INFORMATION OF SILICON GRAPHICS, INC. ANY DUPLICATION, MODIFICATION, - * DISTRIBUTION, OR DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY - * PROHIBITED WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF SILICON - * GRAPHICS, INC. - * - */ - -/* - * File: mtxcatf.c - * Creator: hsa@sgi.com - * Create Date: Thu Nov 2 13:03:02 PST 1995 - * - */ - -#include "guint.h" - -void guMtxCatF(float mf[4][4], float nf[4][4], float res[4][4]) { - int i, j, k; - float temp[4][4]; - - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - temp[i][j] = 0.0; - for (k = 0; k < 4; k++) { - temp[i][j] += mf[i][k] * nf[k][j]; - } - } - } - - /* make sure we handle case where result is an input */ - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - res[i][j] = temp[i][j]; - } - } -} - -void guMtxXFMF(float mf[4][4], float x, float y, float z, float* ox, float* oy, float* oz) { - *ox = mf[0][0] * x + mf[1][0] * y + mf[2][0] * z + mf[3][0]; - *oy = mf[0][1] * x + mf[1][1] * y + mf[2][1] * z + mf[3][1]; - *oz = mf[0][2] * x + mf[1][2] * y + mf[2][2] * z + mf[3][2]; -} diff --git a/src/gu/normalize.c b/src/gu/normalize.c deleted file mode 100644 index 82185a74..00000000 --- a/src/gu/normalize.c +++ /dev/null @@ -1,22 +0,0 @@ -/************************************************************************** - * * - * Copyright (C) 1994, Silicon Graphics, Inc. * - * * - * These coded instructions, statements, and computer programs contain * - * unpublished proprietary information of Silicon Graphics, Inc., and * - * are protected by Federal copyright law. They may not be disclosed * - * to third parties or copied or duplicated in any form, in whole or * - * in part, without the prior written consent of Silicon Graphics, Inc. * - * * - **************************************************************************/ - -#include "guint.h" - -void guNormalize(float* x, float* y, float* z) { - float m; - - m = 1 / sqrtf((*x) * (*x) + (*y) * (*y) + (*z) * (*z)); - *x *= m; - *y *= m; - *z *= m; -} diff --git a/src/gu/scale.c b/src/gu/scale.c deleted file mode 100644 index 594d043c..00000000 --- a/src/gu/scale.c +++ /dev/null @@ -1,30 +0,0 @@ -/************************************************************************** - * * - * Copyright (C) 1994, Silicon Graphics, Inc. * - * * - * These coded instructions, statements, and computer programs contain * - * unpublished proprietary information of Silicon Graphics, Inc., and * - * are protected by Federal copyright law. They may not be disclosed * - * to third parties or copied or duplicated in any form, in whole or * - * in part, without the prior written consent of Silicon Graphics, Inc. * - * * - **************************************************************************/ - -#include "guint.h" - -void guScaleF(float mf[4][4], float x, float y, float z) { - guMtxIdentF(mf); - - mf[0][0] = x; - mf[1][1] = y; - mf[2][2] = z; - mf[3][3] = 1; -} - -void guScale(Mtx* m, float x, float y, float z) { - Matrix mf; - - guScaleF(mf, x, y, z); - - guMtxF2L(mf, m); -} diff --git a/src/gu/translate.c b/src/gu/translate.c deleted file mode 100644 index 8ae25383..00000000 --- a/src/gu/translate.c +++ /dev/null @@ -1,29 +0,0 @@ -/************************************************************************** - * * - * Copyright (C) 1994, Silicon Graphics, Inc. * - * * - * These coded instructions, statements, and computer programs contain * - * unpublished proprietary information of Silicon Graphics, Inc., and * - * are protected by Federal copyright law. They may not be disclosed * - * to third parties or copied or duplicated in any form, in whole or * - * in part, without the prior written consent of Silicon Graphics, Inc. * - * * - **************************************************************************/ - -#include "guint.h" - -void guTranslateF(float mf[4][4], float x, float y, float z) { - guMtxIdentF(mf); - - mf[3][0] = x; - mf[3][1] = y; - mf[3][2] = z; -} - -void guTranslate(Mtx* m, float x, float y, float z) { - Matrix mf; - - guTranslateF(mf, x, y, z); - - guMtxF2L(mf, m); -} From df9393964cca182961b548cab95fb33410b74ce7 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:38:34 -0400 Subject: [PATCH 02/17] Comment out bugged unused debug code Will be removed completely by #12 soon enough anyway, but this will break CI if I don't remove this --- src/os/initialize_kmc.c | 3 ++- src/os/initialize_msp.c | 2 ++ src/rmon/rmoncmds.c | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/os/initialize_kmc.c b/src/os/initialize_kmc.c index 4328da9d..3032ac4c 100644 --- a/src/os/initialize_kmc.c +++ b/src/os/initialize_kmc.c @@ -82,6 +82,7 @@ static void* kmc_proutSyncPrintf(void* str, const char* buf, int n) { extern u32 __kmc_pt_mode; void __osInitialize_kmc(void) { +/* if (!__kmc_pt_mode) { int (*fnc)(); unsigned int* src; @@ -131,8 +132,8 @@ void __osInitialize_kmc(void) { } __printfunc = kmc_proutSyncPrintf; } +*/ } - int __checkHardware_kmc(void) { volatile unsigned int* mon = (unsigned*)0xBFF00000; diff --git a/src/os/initialize_msp.c b/src/os/initialize_msp.c index c86d7e8f..31460be3 100644 --- a/src/os/initialize_msp.c +++ b/src/os/initialize_msp.c @@ -82,6 +82,7 @@ static void* msp_proutSyncPrintf(void* str, const char* buf, int n) { extern u32 __kmc_pt_mode; void __osInitialize_msp(void) { + /* if (!__kmc_pt_mode) { int (*fnc)(); unsigned int* src; @@ -131,6 +132,7 @@ void __osInitialize_msp(void) { } __printfunc = msp_proutSyncPrintf; } + */ } int __checkHardware_msp(void) { diff --git a/src/rmon/rmoncmds.c b/src/rmon/rmoncmds.c index 3992801f..325e4e51 100644 --- a/src/rmon/rmoncmds.c +++ b/src/rmon/rmoncmds.c @@ -25,7 +25,7 @@ static FUNPTR dispatchTable[] = { NotImplemented, NotImplemented, NotImplemented, NotImplemented, __rmonGetSRegs, __rmonSetSRegs, __rmonGetVRegs, __rmonSetVRegs, NotImplemented, }; - +/* int __rmonExecute(KKHeader* request) { int retval; KKHeader reply; @@ -42,5 +42,5 @@ int __rmonExecute(KKHeader* request) { } return retval; } - +*/ #endif From 0d2ee3d0f6b464dc85855efb8cbc5e02c99937c4 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:38:47 -0400 Subject: [PATCH 03/17] C++ fix in ultra64.h --- include/ultra64.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/ultra64.h b/include/ultra64.h index 053db38e..09168f16 100644 --- a/include/ultra64.h +++ b/include/ultra64.h @@ -22,6 +22,9 @@ #ifndef _ULTRA64_H_ #define _ULTRA64_H_ +#ifdef __cplusplus +extern "C" { +#endif #include #include #include @@ -36,5 +39,8 @@ #include #include #include - +#ifdef __cplusplus +} #endif + +#endif \ No newline at end of file From 3fbecde01ff9f9bfa1d5ad631a1921a00d52f16c Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:40:04 -0400 Subject: [PATCH 04/17] New build system Based off my personal "template" featuring makedepend and support for automatically building and installing all 3 library versions, along with colors Still needs cleanup to make sure I've brought over everything from the previous makefile --- Makefile | 226 +++++++++++++++++++++++++++++++++++----------------- MakefileOld | 133 +++++++++++++++++++++++++++++++ install.mk | 32 ++++++++ 3 files changed, 317 insertions(+), 74 deletions(-) create mode 100644 MakefileOld create mode 100644 install.mk diff --git a/Makefile b/Makefile index 223f47cf..7f4e6da8 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,41 @@ -# One of: -# libgultra_rom, libgultra_d, libgultra -TARGET ?= libgultra_rom -VERSION ?= L -VERBOSE ?= 0 - -# Use handwritten ASM implementations of select `gu` functions -MGU_ASM ?= 1 +# Makefile to build libultra include util.mk -ifeq ($(VERBOSE), 0) -V=@ -else -V= +# Preprocessor definitions + +WORKING_DIR := $(shell pwd) + +DEFINES = MODERN_CC=1 + +SRC_DIRS := + +# Whether to hide commands or not +VERBOSE ?= 0 +ifeq ($(VERBOSE),0) + V := @ +endif + +# Whether to colorize build messages +COLOR ?= 1 + +# VERSION - selects the version of the library to build +# libultra - standard library +# libultra_d - debug library +# libultra_rom - final ROM library +VERSION ?= libultra_rom +$(eval $(call validate-option,VERSION,libultra libultra_d libultra_rom)) + +ifeq ($(VERSION),libultra) + OPT_FLAGS := -Os -ggdb3 -ffast-math -fno-unsafe-math-optimizations + DEFINES += NDEBUG=1 +else ifeq ($(VERSION),libultra_d) + OPT_FLAGS := -Og -ggdb3 -ffast-math -fno-unsafe-math-optimizations + DEFINES += _DEBUG=1 +else ifeq ($(VERSION),libultra_rom) + OPT_FLAGS := -Os -ggdb3 -ffast-math -fno-unsafe-math-optimizations + DEFINES += NDEBUG=1 + DEFINES += _FINALROM=1 endif # detect prefix for MIPS toolchain @@ -36,98 +59,153 @@ else $(error Unable to detect a suitable MIPS toolchain installed) endif -BUILD_ROOT := build -BUILD_DIR := $(BUILD_ROOT)/ -BUILD_AR := $(BUILD_DIR)/$(TARGET).a +TARGET := $(VERSION) -WORKING_DIR := $(shell pwd) +ifeq ($(filter clean,$(MAKECMDGOALS)),) + $(info ==== Build Options ====) + $(info Version: $(VERSION)) + $(info =======================) +endif -CPP := cpp -P -AR := $(CROSS)ar +#==============================================================================# +# Target Executable and Sources # +#==============================================================================# +BUILD_DIR_BASE := build +# BUILD_DIR is the location where all build artifacts are placed +BUILD_DIR := $(BUILD_DIR_BASE)/$(VERSION) +LIB := $(BUILD_DIR)/$(TARGET).a -VERSION_DEFINE := -DBUILD_VERSION_STRING=\"2.0$(VERSION)\" +# Directories containing source files +SRC_DIRS += $(shell find src -type d) -ifeq ($(findstring _d,$(TARGET)),_d) -DEBUGFLAG := -D_DEBUG -else -DEBUGFLAG := -DNDEBUG -endif +C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) +S_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.s)) + +# Object files +O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.o)) \ + $(foreach file,$(S_FILES),$(BUILD_DIR)/$(file:.s=.o)) + +# Automatic dependency files +DEP_FILES := $(O_FILES:.o=.d) + +#==============================================================================# +# Compiler Options # +#==============================================================================# + +AS := $(CROSS)as +CC := $(CROSS)gcc +CPP := $(CROSS)cpp +LD := $(CROSS)ld +AR := $(CROSS)ar -AS := $(CROSS)gcc -x assembler-with-cpp -CC := $(CROSS)gcc +# Do NOT depend on system-installed headers! If you need to make a header change, +# test it in your source first! +INCLUDE_DIRS += $(WORKING_DIR)/include $(WORKING_DIR)/include/PR $(WORKING_DIR)/include/compiler/modern_gcc $(BUILD_DIR) $(BUILD_DIR)/include $(WORKING_DIR)/src $(WORKING_DIR) + +GBIDEFINE := -DF3DEX_GBI_2 + +C_DEFINES = $(foreach d,$(DEFINES),-D$(d)) +DEF_INC_CFLAGS = $(foreach i,$(INCLUDE_DIRS),-I$(i)) $(C_DEFINES) WARNINGS := -Wall -Wextra -Wno-format-security -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable -Wno-builtin-declaration-mismatch WARNINGS += -Wno-int-conversion -Wno-incompatible-pointer-types -Wno-implicit-function-declaration # TODO: Try adjusting code to remove these -CFLAGS := -G 0 -c -nostdinc -march=vr4300 -mfix4300 -mabi=32 -mno-abicalls -mdivide-breaks -fno-PIC -fno-common -ffreestanding -fbuiltin -fno-builtin-sinf -fno-builtin-cosf -funsigned-char $(WARNINGS) +CFLAGS := -G 0 -c -nostdinc -march=vr4300 -mfix4300 -mabi=32 -mips3 -mno-abicalls -mdivide-breaks -fno-PIC -fno-common -ffreestanding -fbuiltin -fno-builtin-sinf -fno-builtin-cosf -funsigned-char $(WARNINGS) CFLAGS += -fno-strict-aliasing # TODO: Try adjusting code to remove this -ASFLAGS := -w -nostdinc -c -G 0 -march=vr4300 -mabi=32 -mgp32 -mfp32 -DMIPSEB -D_LANGUAGE_ASSEMBLY -D_MIPS_SIM=1 -D_ULTRA64 -CPPFLAGS = -DMODERN_CC -D_MIPS_SZLONG=32 -D__USE_ISOC99 $(GBIDEFINE) $(VERSION_DEFINE) $(DEBUGFLAG) -IINC = -I . -I $(WORKING_DIR)/include -I $(WORKING_DIR)/include/compiler/modern_gcc -I $(WORKING_DIR)/include/PR -MIPS_VERSION := -mips3 -ASOPTFLAGS := - -ifeq ($(findstring _d,$(TARGET)),_d) -OPTFLAGS := -Og -ggdb3 -ffast-math -fno-unsafe-math-optimizations -else -OPTFLAGS := -Os -ggdb3 -ffast-math -fno-unsafe-math-optimizations -endif +CFLAGS += -D_MIPS_SZLONG=32 -D__USE_ISOC99 $(C_DEFINES) $(DEF_INC_CFLAGS) -ifeq ($(findstring _rom,$(TARGET)),_rom) -CPPFLAGS += -D_FINALROM -endif +# C preprocessor flags +CPPFLAGS := -P -Wno-trigraphs $(DEF_INC_CFLAGS) -SRC_DIRS := $(shell find src -type d) -C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) -S_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.s)) +# tools +PRINT = printf -# Versions J and below used the C matrix math implementations -MGU_MATRIX_FILES := mtxcatf normalize scale translate -ifeq ($(MGU_ASM), 1) -C_FILES := $(filter-out $(addprefix src/gu/,$(MGU_MATRIX_FILES:=.c)),$(C_FILES)) -else -S_FILES := $(filter-out $(addprefix src/mgu/,$(MGU_MATRIX_FILES:=.s)),$(S_FILES)) +ifeq ($(COLOR),1) +NO_COL := \033[0m +RED := \033[0;31m +GREEN := \033[0;32m +BLUE := \033[0;34m +YELLOW := \033[0;33m +BLINK := \033[33;5m endif -C_O_FILES := $(foreach f,$(C_FILES:.c=.o),$(BUILD_DIR)/$f) -S_O_FILES := $(foreach f,$(S_FILES:.s=.o),$(BUILD_DIR)/$f) -O_FILES := $(S_O_FILES) $(C_O_FILES) - -$(shell mkdir -p src $(foreach dir,$(SRC_DIRS),$(BUILD_DIR)/$(dir))) +# Common build print status function +define print + @$(PRINT) "$(GREEN)$(1) $(YELLOW)$(2)$(GREEN) -> $(BLUE)$(3)$(NO_COL)\n" +endef -.PHONY: all clean distclean setup -all: $(BUILD_AR) +#==============================================================================# +# Main Targets # +#==============================================================================# -$(BUILD_AR): $(O_FILES) - @printf " [AR] $@\n" - $(V)$(AR) rcs $@ $^ +# Default target +default: $(LIB) clean: - $(RM) -rf $(BUILD_DIR) + $(RM) -r $(BUILD_DIR_BASE) -distclean: - $(RM) -rf extracted/ $(BUILD_ROOT) +ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS)) -GBIDEFINE := -DF3DEX_GBI +# Make sure build directory exists before compiling anything +DUMMY != mkdir -p $(ALL_DIRS) +$(BUILD_DIR)/src/voice/%.o: CFLAGS += -I$(WORKING_DIR)/src/voice +$(BUILD_DIR)/src/voice/%.o: DEFINES += LANG_JAPANESE=1 $(BUILD_DIR)/src/gu/parse_gbi.o: GBIDEFINE := -DF3D_GBI $(BUILD_DIR)/src/gu/us2dex_emu.o: GBIDEFINE := $(BUILD_DIR)/src/gu/us2dex2_emu.o: GBIDEFINE := $(BUILD_DIR)/src/sp/sprite.o: GBIDEFINE := -DF3D_GBI $(BUILD_DIR)/src/sp/spriteex.o: GBIDEFINE := $(BUILD_DIR)/src/sp/spriteex2.o: GBIDEFINE := -$(BUILD_DIR)/src/voice/%.o: OPTFLAGS += -DLANG_JAPANESE -I$(WORKING_DIR)/src -I$(WORKING_DIR)/src/voice -$(BUILD_DIR)/src/voice/%.o: CC := $(WORKING_DIR)/tools/compile_sjis.py -D__CC=$(CC) -D__BUILD_DIR=$(BUILD_DIR) + +#==============================================================================# +# Compilation Recipes # +#==============================================================================# + +# Compile C code +$(BUILD_DIR)/src/voice/%.o: src/voice/%.c + $(call print,Compiling:,$<,$@) + $(V)tools/compile_sjis.py -D__CC=$(CC) -D__BUILD_DIR=$(BUILD_DIR) -c $(CFLAGS) -MMD -MF $(BUILD_DIR)/src/voice/$*.d -o $@ $< $(BUILD_DIR)/%.o: %.c - @printf " [CC] $<\n" - $(V)$(CC) $(CFLAGS) $(MIPS_VERSION) $(CPPFLAGS) $(OPTFLAGS) $< $(IINC) -o $@ - $(V)tools/set_o32abi_bit.py $@ + $(call print,Compiling:,$<,$@) + $(V)$(CC) -c $(CFLAGS) $(GBIDEFINE) -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< +$(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c + $(call print,Compiling:,$<,$@) + $(V)$(CC) -c $(CFLAGS) $(GBIDEFINE) -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< +# Assemble assembly code $(BUILD_DIR)/%.o: %.s - @printf " [AS] $<\n" - $(V)$(AS) $(ASFLAGS) $(MIPS_VERSION) $(CPPFLAGS) $(ASOPTFLAGS) $< $(IINC) -o $@ - $(V)tools/set_o32abi_bit.py $@ + $(call print,Assembling:,$<,$@) + $(V)$(CC) -c $(CFLAGS) $(foreach i,$(INCLUDE_DIRS),-Wa,-I$(i)) -x assembler-with-cpp -DMIPSEB -D_LANGUAGE_ASSEMBLY -D_ULTRA64 -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< + +# Creating final library file +$(LIB): $(O_FILES) + @$(PRINT) "$(GREEN)Creating $(VERSION): $(BLUE)$@ $(NO_COL)\n" + $(V)$(AR) rcs -o $@ $(O_FILES) + +all: $(BUILD_DIR_BASE)/libultra.a $(BUILD_DIR_BASE)/libultra_d.a $(BUILD_DIR_BASE)/libultra_rom.a + +$(BUILD_DIR_BASE)/libultra.a: + $(V)$(MAKE) VERSION=libultra + $(V)cp $(BUILD_DIR_BASE)/libultra/libultra.a $(BUILD_DIR_BASE) + +$(BUILD_DIR_BASE)/libultra_d.a: + $(V)$(MAKE) VERSION=libultra_d + $(V)cp $(BUILD_DIR_BASE)/libultra_d/libultra_d.a $(BUILD_DIR_BASE) + +$(BUILD_DIR_BASE)/libultra_rom.a: + $(V)$(MAKE) VERSION=libultra_rom + $(V)cp $(BUILD_DIR_BASE)/libultra_rom/libultra_rom.a $(BUILD_DIR_BASE) + +include install.mk + +.PHONY: clean default all install pkginstall +# with no prerequisites, .SECONDARY causes no intermediate target to be removed +.SECONDARY: + +# Remove built-in rules, to improve performance +MAKEFLAGS += --no-builtin-rules + +-include $(DEP_FILES) -# Disable built-in rules -.SUFFIXES: print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true diff --git a/MakefileOld b/MakefileOld new file mode 100644 index 00000000..223f47cf --- /dev/null +++ b/MakefileOld @@ -0,0 +1,133 @@ +# One of: +# libgultra_rom, libgultra_d, libgultra +TARGET ?= libgultra_rom +VERSION ?= L +VERBOSE ?= 0 + +# Use handwritten ASM implementations of select `gu` functions +MGU_ASM ?= 1 + +include util.mk + +ifeq ($(VERBOSE), 0) +V=@ +else +V= +endif + +# detect prefix for MIPS toolchain +ifneq ($(call find-command,mips64-elf-ld),) + CROSS := mips64-elf- +else ifneq ($(call find-command,mips-n64-ld),) + CROSS := mips-n64- +else ifneq ($(call find-command,mips64-ld),) + CROSS := mips64- +else ifneq ($(call find-command,mips-linux-gnu-ld),) + CROSS := mips-linux-gnu- +else ifneq ($(call find-command,mips64-linux-gnu-ld),) + CROSS := mips64-linux-gnu- +else ifneq ($(call find-command,mips64-none-elf-ld),) + CROSS := mips64-none-elf- +else ifneq ($(call find-command,mips-ld),) + CROSS := mips- +else ifneq ($(call find-command,mips-suse-linux-ld ),) + CROSS := mips-suse-linux- +else + $(error Unable to detect a suitable MIPS toolchain installed) +endif + +BUILD_ROOT := build +BUILD_DIR := $(BUILD_ROOT)/ +BUILD_AR := $(BUILD_DIR)/$(TARGET).a + +WORKING_DIR := $(shell pwd) + +CPP := cpp -P +AR := $(CROSS)ar + +VERSION_DEFINE := -DBUILD_VERSION_STRING=\"2.0$(VERSION)\" + +ifeq ($(findstring _d,$(TARGET)),_d) +DEBUGFLAG := -D_DEBUG +else +DEBUGFLAG := -DNDEBUG +endif + +AS := $(CROSS)gcc -x assembler-with-cpp +CC := $(CROSS)gcc + +WARNINGS := -Wall -Wextra -Wno-format-security -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable -Wno-builtin-declaration-mismatch +WARNINGS += -Wno-int-conversion -Wno-incompatible-pointer-types -Wno-implicit-function-declaration # TODO: Try adjusting code to remove these +CFLAGS := -G 0 -c -nostdinc -march=vr4300 -mfix4300 -mabi=32 -mno-abicalls -mdivide-breaks -fno-PIC -fno-common -ffreestanding -fbuiltin -fno-builtin-sinf -fno-builtin-cosf -funsigned-char $(WARNINGS) +CFLAGS += -fno-strict-aliasing # TODO: Try adjusting code to remove this +ASFLAGS := -w -nostdinc -c -G 0 -march=vr4300 -mabi=32 -mgp32 -mfp32 -DMIPSEB -D_LANGUAGE_ASSEMBLY -D_MIPS_SIM=1 -D_ULTRA64 +CPPFLAGS = -DMODERN_CC -D_MIPS_SZLONG=32 -D__USE_ISOC99 $(GBIDEFINE) $(VERSION_DEFINE) $(DEBUGFLAG) +IINC = -I . -I $(WORKING_DIR)/include -I $(WORKING_DIR)/include/compiler/modern_gcc -I $(WORKING_DIR)/include/PR +MIPS_VERSION := -mips3 +ASOPTFLAGS := + +ifeq ($(findstring _d,$(TARGET)),_d) +OPTFLAGS := -Og -ggdb3 -ffast-math -fno-unsafe-math-optimizations +else +OPTFLAGS := -Os -ggdb3 -ffast-math -fno-unsafe-math-optimizations +endif + +ifeq ($(findstring _rom,$(TARGET)),_rom) +CPPFLAGS += -D_FINALROM +endif + +SRC_DIRS := $(shell find src -type d) +C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) +S_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.s)) + +# Versions J and below used the C matrix math implementations +MGU_MATRIX_FILES := mtxcatf normalize scale translate +ifeq ($(MGU_ASM), 1) +C_FILES := $(filter-out $(addprefix src/gu/,$(MGU_MATRIX_FILES:=.c)),$(C_FILES)) +else +S_FILES := $(filter-out $(addprefix src/mgu/,$(MGU_MATRIX_FILES:=.s)),$(S_FILES)) +endif + +C_O_FILES := $(foreach f,$(C_FILES:.c=.o),$(BUILD_DIR)/$f) +S_O_FILES := $(foreach f,$(S_FILES:.s=.o),$(BUILD_DIR)/$f) +O_FILES := $(S_O_FILES) $(C_O_FILES) + +$(shell mkdir -p src $(foreach dir,$(SRC_DIRS),$(BUILD_DIR)/$(dir))) + +.PHONY: all clean distclean setup +all: $(BUILD_AR) + +$(BUILD_AR): $(O_FILES) + @printf " [AR] $@\n" + $(V)$(AR) rcs $@ $^ + +clean: + $(RM) -rf $(BUILD_DIR) + +distclean: + $(RM) -rf extracted/ $(BUILD_ROOT) + +GBIDEFINE := -DF3DEX_GBI + +$(BUILD_DIR)/src/gu/parse_gbi.o: GBIDEFINE := -DF3D_GBI +$(BUILD_DIR)/src/gu/us2dex_emu.o: GBIDEFINE := +$(BUILD_DIR)/src/gu/us2dex2_emu.o: GBIDEFINE := +$(BUILD_DIR)/src/sp/sprite.o: GBIDEFINE := -DF3D_GBI +$(BUILD_DIR)/src/sp/spriteex.o: GBIDEFINE := +$(BUILD_DIR)/src/sp/spriteex2.o: GBIDEFINE := +$(BUILD_DIR)/src/voice/%.o: OPTFLAGS += -DLANG_JAPANESE -I$(WORKING_DIR)/src -I$(WORKING_DIR)/src/voice +$(BUILD_DIR)/src/voice/%.o: CC := $(WORKING_DIR)/tools/compile_sjis.py -D__CC=$(CC) -D__BUILD_DIR=$(BUILD_DIR) + +$(BUILD_DIR)/%.o: %.c + @printf " [CC] $<\n" + $(V)$(CC) $(CFLAGS) $(MIPS_VERSION) $(CPPFLAGS) $(OPTFLAGS) $< $(IINC) -o $@ + $(V)tools/set_o32abi_bit.py $@ + +$(BUILD_DIR)/%.o: %.s + @printf " [AS] $<\n" + $(V)$(AS) $(ASFLAGS) $(MIPS_VERSION) $(CPPFLAGS) $(ASOPTFLAGS) $< $(IINC) -o $@ + $(V)tools/set_o32abi_bit.py $@ + +# Disable built-in rules +.SUFFIXES: +print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true diff --git a/install.mk b/install.mk new file mode 100644 index 00000000..90a38849 --- /dev/null +++ b/install.mk @@ -0,0 +1,32 @@ +PRHFILES = include/PR/os_internal_error.h include/PR/os_internal_thread.h include/PR/rsp.h \ + include/PR/os_time.h include/PR/uportals.h include/PR/os_gio.h include/PR/ramrom.h include/PR/ucode_debug.h \ + include/PR/os_cache.h include/PR/os_cont.h include/PR/os_system.h include/PR/os_libc.h include/PR/os_pfs.h \ + include/PR/os_eeprom.h include/PR/sptask.h include/PR/R4300.h include/PR/PRimage.h \ + include/PR/gu.h include/PR/gt.h include/PR/os_ai.h include/PR/abi.h include/PR/os_rdp.h include/PR/os_internal_rsp.h \ + include/PR/os_error.h include/PR/os_si.h include/PR/rsp_ipc.h include/PR/os_internal_reg.h include/PR/trace.h \ + include/PR/os_internal.h include/PR/os_message.h include/PR/os_internal_gio.h include/PR/os_motor.h \ + include/PR/os_internal_host.h include/PR/os_rsp.h include/PR/os_internal_si.h include/PR/gs2dex.h \ + include/PR/region.h include/PR/rcp.h include/PR/sptaskoff.h include/PR/gtoff.h include/PR/ultralog.h \ + include/PR/os_tlb.h include/PR/os_thread.h include/PR/os_internal_flash.h \ + include/PR/rmon.h include/PR/os_internal_tlb.h include/PR/sp.h include/PR/os_exception.h include/PR/ucode.h \ + include/PR/os_debug.h include/PR/os_vi.h include/PR/gbi.h include/PR/libaudio.h include/PR/os_internal_debug.h \ + include/PR/os_host.h include/PR/os_gbpak.h include/PR/os_version.h include/PR/os_convert.h include/PR/os_internal_exception.h \ + include/PR/ultratypes.h include/PR/rdb.h include/PR/os_pi.h include/PR/os.h include/PR/os_voice.h include/PR/ultraerror.h include/PR/os_flash.h \ + include/PR/mbi.h include/PR/gzsort.h include/PR/sched.h include/PR/os_reg.h + +install: all + $(V)mkdir -p /opt/crashsdk/mips64-elf/lib/ /opt/crashsdk/mips64-elf/include/ /opt/crashsdk/mips64-elf/include/PR + @$(PRINT) "$(GREEN)Copying libultra.a$(NO_COL)\n" + $(V)cp $(BUILD_DIR_BASE)/libultra.a /opt/crashsdk/mips64-elf/lib/libultra.a + @$(PRINT) "$(GREEN)Copying libultra_d.a$(NO_COL)\n" + $(V)cp $(BUILD_DIR_BASE)/libultra_d.a /opt/crashsdk/mips64-elf/lib/libultra_d.a + @$(PRINT) "$(GREEN)Copying libultra_rom.a$(NO_COL)\n" + $(V)cp $(BUILD_DIR_BASE)/libultra_rom.a /opt/crashsdk/mips64-elf/lib/libultra_rom.a + @$(PRINT) "$(GREEN)Copying assert.h$(NO_COL)\n" + $(V)cp include/assert.h /opt/crashsdk/mips64-elf/include/assert.h + @$(PRINT) "$(GREEN)Copying ultra64.h$(NO_COL)\n" + $(V)cp include/ultra64.h /opt/crashsdk/mips64-elf/include/ultra64.h + @$(PRINT) "$(GREEN)Copying ultrahost.h$(NO_COL)\n" + $(V)cp include/ultrahost.h /opt/crashsdk/mips64-elf/include/ultrahost.h + @$(PRINT) "$(GREEN)Copying PR headers$(NO_COL)\n" + $(V)$(foreach var,$(PRHFILES),cp $(var) /opt/crashsdk/mips64-elf/include/PR/;) From 21abc18f022933cb3e9597b6615507736faa1928 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:49:03 -0400 Subject: [PATCH 05/17] Fix clang_format --- include/ultra64.h | 2 +- src/os/initialize_kmc.c | 94 ++++++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/include/ultra64.h b/include/ultra64.h index 09168f16..6bcb680e 100644 --- a/include/ultra64.h +++ b/include/ultra64.h @@ -43,4 +43,4 @@ extern "C" { } #endif -#endif \ No newline at end of file +#endif diff --git a/src/os/initialize_kmc.c b/src/os/initialize_kmc.c index 3032ac4c..58cf228f 100644 --- a/src/os/initialize_kmc.c +++ b/src/os/initialize_kmc.c @@ -82,57 +82,57 @@ static void* kmc_proutSyncPrintf(void* str, const char* buf, int n) { extern u32 __kmc_pt_mode; void __osInitialize_kmc(void) { -/* - if (!__kmc_pt_mode) { - int (*fnc)(); - unsigned int* src; - unsigned int* dst; - unsigned int monadr; - volatile unsigned int* mon; - volatile unsigned int* stat; - - stat = (unsigned*)0xbff08004; - mon = (unsigned*)0xBFF00000; - if (*mon != 0x4B4D4300) { - return; - } + /* + if (!__kmc_pt_mode) { + int (*fnc)(); + unsigned int* src; + unsigned int* dst; + unsigned int monadr; + volatile unsigned int* mon; + volatile unsigned int* stat; + + stat = (unsigned*)0xbff08004; + mon = (unsigned*)0xBFF00000; + if (*mon != 0x4B4D4300) { + return; + } - src = (unsigned*)__ptExceptionPreamble; - dst = (unsigned*)E_VEC; - *dst++ = *src++; - *dst++ = *src++; - *dst++ = *src++; - src += 2; - dst += 2; - *dst++ = *src++; - *dst++ = *src++; - *dst++ = *src++; - - osWritebackDCache(E_VEC, 0x24); - osInvalICache(E_VEC, 0x24); - - __kmc_pt_mode = TRUE; - - if ((*stat & 0x10) == 0) { - monadr = *(mon + 1); - if (monadr != 0xBFF00000) { - unsigned int* src; - unsigned int* dst = monadr | 0x20000000; - unsigned int ct = 0x2000 / 4; - - src = 0xBFF00000; - - while (ct != 0) { - *dst++ = *src++; - ct--; + src = (unsigned*)__ptExceptionPreamble; + dst = (unsigned*)E_VEC; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + src += 2; + dst += 2; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + + osWritebackDCache(E_VEC, 0x24); + osInvalICache(E_VEC, 0x24); + + __kmc_pt_mode = TRUE; + + if ((*stat & 0x10) == 0) { + monadr = *(mon + 1); + if (monadr != 0xBFF00000) { + unsigned int* src; + unsigned int* dst = monadr | 0x20000000; + unsigned int ct = 0x2000 / 4; + + src = 0xBFF00000; + + while (ct != 0) { + *dst++ = *src++; + ct--; + } } + fnc = monadr + 8; + fnc(0x4B4D4300, 0); } - fnc = monadr + 8; - fnc(0x4B4D4300, 0); + __printfunc = kmc_proutSyncPrintf; } - __printfunc = kmc_proutSyncPrintf; - } -*/ + */ } int __checkHardware_kmc(void) { volatile unsigned int* mon = (unsigned*)0xBFF00000; From 12e678c0018e9b580ef4d2771ec523ebe1a0e482 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:49:36 -0400 Subject: [PATCH 06/17] CI fixes to account for the non-decomp build system --- .github/workflows/ci_gcc.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci_gcc.yml b/.github/workflows/ci_gcc.yml index 448b9337..5df45f6c 100644 --- a/.github/workflows/ci_gcc.yml +++ b/.github/workflows/ci_gcc.yml @@ -1,6 +1,6 @@ # CI file for GCC builds -name: Build GCC libgultra +name: Build GCC libultra # Build on every branch push, tag push, and pull request change: on: [push, pull_request_target] @@ -13,8 +13,7 @@ jobs: strategy: fail-fast: false matrix: - version: [L] # [H, I, I_patch, J, K, L] - suffix: [~, _d, _rom] + version: [[libultra, libultra_d, libultra_rom]] steps: - name: Checkout repository @@ -28,11 +27,8 @@ jobs: - name: Verify formatting on all files run: python3 tools/check_format.py --verbose - - name: Setup - run: make setup -j $(nproc) TARGET=libgultra${{ matrix.suffix }} VERSION=${{ matrix.version }} - - - name: Build libgultra${{ matrix.suffix }} ${{ matrix.version }} - run: make -j $(nproc) TARGET=libgultra${{ matrix.suffix }} VERSION=${{ matrix.version }} + - name: Build ${{ matrix.version }} + run: make -j $(nproc) VERSION=${{ matrix.version }} - name: 'Upload Artifact' uses: actions/upload-artifact@v4 From 8b23ae0f64570589921cc7365b499470dd704b7a Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:54:37 -0400 Subject: [PATCH 07/17] Remove old makefile --- MakefileOld | 133 ---------------------------------------------------- 1 file changed, 133 deletions(-) delete mode 100644 MakefileOld diff --git a/MakefileOld b/MakefileOld deleted file mode 100644 index 223f47cf..00000000 --- a/MakefileOld +++ /dev/null @@ -1,133 +0,0 @@ -# One of: -# libgultra_rom, libgultra_d, libgultra -TARGET ?= libgultra_rom -VERSION ?= L -VERBOSE ?= 0 - -# Use handwritten ASM implementations of select `gu` functions -MGU_ASM ?= 1 - -include util.mk - -ifeq ($(VERBOSE), 0) -V=@ -else -V= -endif - -# detect prefix for MIPS toolchain -ifneq ($(call find-command,mips64-elf-ld),) - CROSS := mips64-elf- -else ifneq ($(call find-command,mips-n64-ld),) - CROSS := mips-n64- -else ifneq ($(call find-command,mips64-ld),) - CROSS := mips64- -else ifneq ($(call find-command,mips-linux-gnu-ld),) - CROSS := mips-linux-gnu- -else ifneq ($(call find-command,mips64-linux-gnu-ld),) - CROSS := mips64-linux-gnu- -else ifneq ($(call find-command,mips64-none-elf-ld),) - CROSS := mips64-none-elf- -else ifneq ($(call find-command,mips-ld),) - CROSS := mips- -else ifneq ($(call find-command,mips-suse-linux-ld ),) - CROSS := mips-suse-linux- -else - $(error Unable to detect a suitable MIPS toolchain installed) -endif - -BUILD_ROOT := build -BUILD_DIR := $(BUILD_ROOT)/ -BUILD_AR := $(BUILD_DIR)/$(TARGET).a - -WORKING_DIR := $(shell pwd) - -CPP := cpp -P -AR := $(CROSS)ar - -VERSION_DEFINE := -DBUILD_VERSION_STRING=\"2.0$(VERSION)\" - -ifeq ($(findstring _d,$(TARGET)),_d) -DEBUGFLAG := -D_DEBUG -else -DEBUGFLAG := -DNDEBUG -endif - -AS := $(CROSS)gcc -x assembler-with-cpp -CC := $(CROSS)gcc - -WARNINGS := -Wall -Wextra -Wno-format-security -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable -Wno-builtin-declaration-mismatch -WARNINGS += -Wno-int-conversion -Wno-incompatible-pointer-types -Wno-implicit-function-declaration # TODO: Try adjusting code to remove these -CFLAGS := -G 0 -c -nostdinc -march=vr4300 -mfix4300 -mabi=32 -mno-abicalls -mdivide-breaks -fno-PIC -fno-common -ffreestanding -fbuiltin -fno-builtin-sinf -fno-builtin-cosf -funsigned-char $(WARNINGS) -CFLAGS += -fno-strict-aliasing # TODO: Try adjusting code to remove this -ASFLAGS := -w -nostdinc -c -G 0 -march=vr4300 -mabi=32 -mgp32 -mfp32 -DMIPSEB -D_LANGUAGE_ASSEMBLY -D_MIPS_SIM=1 -D_ULTRA64 -CPPFLAGS = -DMODERN_CC -D_MIPS_SZLONG=32 -D__USE_ISOC99 $(GBIDEFINE) $(VERSION_DEFINE) $(DEBUGFLAG) -IINC = -I . -I $(WORKING_DIR)/include -I $(WORKING_DIR)/include/compiler/modern_gcc -I $(WORKING_DIR)/include/PR -MIPS_VERSION := -mips3 -ASOPTFLAGS := - -ifeq ($(findstring _d,$(TARGET)),_d) -OPTFLAGS := -Og -ggdb3 -ffast-math -fno-unsafe-math-optimizations -else -OPTFLAGS := -Os -ggdb3 -ffast-math -fno-unsafe-math-optimizations -endif - -ifeq ($(findstring _rom,$(TARGET)),_rom) -CPPFLAGS += -D_FINALROM -endif - -SRC_DIRS := $(shell find src -type d) -C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) -S_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.s)) - -# Versions J and below used the C matrix math implementations -MGU_MATRIX_FILES := mtxcatf normalize scale translate -ifeq ($(MGU_ASM), 1) -C_FILES := $(filter-out $(addprefix src/gu/,$(MGU_MATRIX_FILES:=.c)),$(C_FILES)) -else -S_FILES := $(filter-out $(addprefix src/mgu/,$(MGU_MATRIX_FILES:=.s)),$(S_FILES)) -endif - -C_O_FILES := $(foreach f,$(C_FILES:.c=.o),$(BUILD_DIR)/$f) -S_O_FILES := $(foreach f,$(S_FILES:.s=.o),$(BUILD_DIR)/$f) -O_FILES := $(S_O_FILES) $(C_O_FILES) - -$(shell mkdir -p src $(foreach dir,$(SRC_DIRS),$(BUILD_DIR)/$(dir))) - -.PHONY: all clean distclean setup -all: $(BUILD_AR) - -$(BUILD_AR): $(O_FILES) - @printf " [AR] $@\n" - $(V)$(AR) rcs $@ $^ - -clean: - $(RM) -rf $(BUILD_DIR) - -distclean: - $(RM) -rf extracted/ $(BUILD_ROOT) - -GBIDEFINE := -DF3DEX_GBI - -$(BUILD_DIR)/src/gu/parse_gbi.o: GBIDEFINE := -DF3D_GBI -$(BUILD_DIR)/src/gu/us2dex_emu.o: GBIDEFINE := -$(BUILD_DIR)/src/gu/us2dex2_emu.o: GBIDEFINE := -$(BUILD_DIR)/src/sp/sprite.o: GBIDEFINE := -DF3D_GBI -$(BUILD_DIR)/src/sp/spriteex.o: GBIDEFINE := -$(BUILD_DIR)/src/sp/spriteex2.o: GBIDEFINE := -$(BUILD_DIR)/src/voice/%.o: OPTFLAGS += -DLANG_JAPANESE -I$(WORKING_DIR)/src -I$(WORKING_DIR)/src/voice -$(BUILD_DIR)/src/voice/%.o: CC := $(WORKING_DIR)/tools/compile_sjis.py -D__CC=$(CC) -D__BUILD_DIR=$(BUILD_DIR) - -$(BUILD_DIR)/%.o: %.c - @printf " [CC] $<\n" - $(V)$(CC) $(CFLAGS) $(MIPS_VERSION) $(CPPFLAGS) $(OPTFLAGS) $< $(IINC) -o $@ - $(V)tools/set_o32abi_bit.py $@ - -$(BUILD_DIR)/%.o: %.s - @printf " [AS] $<\n" - $(V)$(AS) $(ASFLAGS) $(MIPS_VERSION) $(CPPFLAGS) $(ASOPTFLAGS) $< $(IINC) -o $@ - $(V)tools/set_o32abi_bit.py $@ - -# Disable built-in rules -.SUFFIXES: -print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true From ab1b2bab416b6a5b86323651f57bb62f59d92d19 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:55:39 -0400 Subject: [PATCH 08/17] Remove decomp tools --- tools/ar.py | 476 ---- tools/asm_differ/.gitignore | 2 - tools/asm_differ/.gitrepo | 12 - tools/asm_differ/.pre-commit-config.yaml | 6 - tools/asm_differ/LICENSE | 24 - tools/asm_differ/README.md | 56 - tools/asm_differ/diff-stylesheet.css | 67 - tools/asm_differ/diff.py | 2923 ---------------------- tools/asm_differ/diff_settings.py | 11 - tools/asm_differ/mypy.ini | 17 - tools/asm_differ/screenshot.png | Bin 99842 -> 0 bytes tools/check_format.py | 83 - tools/disassemble_elf.py | 312 --- tools/fix_objfile.py | 68 - tools/libdiff.py | 150 -- tools/libelf.py | 1181 --------- tools/m2ctx.py | 76 - tools/mdebug.py | 938 ------- tools/mips_isa.py | 1309 ---------- tools/patch_ar_meta.py | 90 - tools/print_mdebug.py | 589 ----- tools/set_o32abi_bit.py | 25 - tools/strip_debug.sh | 10 - tools/util.py | 44 - 24 files changed, 8469 deletions(-) delete mode 100755 tools/ar.py delete mode 100644 tools/asm_differ/.gitignore delete mode 100644 tools/asm_differ/.gitrepo delete mode 100644 tools/asm_differ/.pre-commit-config.yaml delete mode 100644 tools/asm_differ/LICENSE delete mode 100644 tools/asm_differ/README.md delete mode 100644 tools/asm_differ/diff-stylesheet.css delete mode 100755 tools/asm_differ/diff.py delete mode 100644 tools/asm_differ/diff_settings.py delete mode 100644 tools/asm_differ/mypy.ini delete mode 100644 tools/asm_differ/screenshot.png delete mode 100644 tools/check_format.py delete mode 100755 tools/disassemble_elf.py delete mode 100755 tools/fix_objfile.py delete mode 100755 tools/libdiff.py delete mode 100755 tools/libelf.py delete mode 100755 tools/m2ctx.py delete mode 100644 tools/mdebug.py delete mode 100644 tools/mips_isa.py delete mode 100755 tools/patch_ar_meta.py delete mode 100755 tools/print_mdebug.py delete mode 100755 tools/set_o32abi_bit.py delete mode 100755 tools/strip_debug.sh delete mode 100644 tools/util.py diff --git a/tools/ar.py b/tools/ar.py deleted file mode 100755 index 0f3e5743..00000000 --- a/tools/ar.py +++ /dev/null @@ -1,476 +0,0 @@ -#!/usr/bin/env python3 -# -# ar -# - -from genericpath import isfile -import os, struct, time -from dataclasses import dataclass -from libelf import ElfFile, SB_GLOBAL, SHN_UND - -class Archive: - @dataclass - class ArchiveFileRecord: - """ - AR file headers + data - - Offset Length Name Format - 0 16 File identifier ASCII - 16 12 File modification timestamp (in seconds) Decimal - 28 6 Owner ID Decimal - 34 6 Group ID Decimal - 40 8 File mode (type and permission) Octal - 48 10 File size in bytes Decimal - 58 2 Ending characters 0x60 0x0A - """ - name : str - time : int - uid : int - gid : int - mode : int - size : int - data : bytes - - def __init__(self): - self.files = [] # List[ArchiveFileRecord] - self.armap_entries = None - self.time = int(time.time()) - - def add_data(self, name, time, uid, gid, mode, size, data): - self.files.append(Archive.ArchiveFileRecord(name, time, uid, gid, mode, size, data)) - - def add_file(self, file_path): - if not os.path.isfile(file_path): - print(f"Error: No file named {file_path}") - return - - st = os.stat(file_path) - name = os.path.basename(file_path) - time = int(st.st_mtime) - uid = st.st_uid - gid = st.st_gid - mode = st.st_mode - size = st.st_size - - data = None - with open(file_path, "rb") as infile: - data = bytes(infile.read()) - - self.add_data(name, time, uid, gid, mode, size, data) - - def add_ar(self, file_path): - for file in Archive.from_file(file_path).files: - self.add_data(file) - - def build_image(self): - def add_bin(ba, bin): - for b in bin: - ba.append(b) - - def add_str(ba, s, pad_to_len=-1): - if pad_to_len != -1: - s = f"{s:{pad_to_len}}" - s = s.encode("ASCII") - for c in s: - ba.append(c) - - def add_pad(ba): - if len(ba) % 2 != 0: - ba.append(0) - - def add_ar_hdr(ba, name, time, uid, gid, mode, length): - add_str(ba, name, 16) - add_str(ba, str(time), 12) - add_str(ba, str(uid), 6) - add_str(ba, str(gid), 6) - add_str(ba, oct(mode)[2:], 8) - add_str(ba, str(length), 10) - add_str(ba, "`\n") - - b = bytearray() - - # MAGIC - - add_str(b, "!\n") - - # ARMAP - - armap_data= bytearray() - - armap_entries_files = [] - - num_entries = 0 - armap_size = 4 - armap_string_data = bytearray() - for i,file in enumerate(self.files): - elf = ElfFile(file.data) - if elf.symtab is not None: - for sym in elf.symtab.symbol_entries: - if sym.st_shndx != SHN_UND and sym.bind == SB_GLOBAL: - num_entries += 1 - armap_size += 4 + len(sym.name) + 1 - armap_string_data.extend(sym.name.encode("latin1") + b'\0') - armap_entries_files.append(i) - - armap_data.extend(struct.pack(">I", num_entries)) - armap_data.extend([0] * 4 * num_entries) # defer writing file positions until files are emplaced later - armap_data.extend(armap_string_data) - - if len(armap_data) % 4 != 0: - alsiz = (len(armap_data) + 3) & ~3 - armap_data.extend([0] * (alsiz - len(armap_data))) - - current_time = int(time.time()) - - add_ar_hdr(b, "/", current_time, 0, 0, 0, len(armap_data)) - armap_offsets_start = len(b) + 4 - add_bin(b, armap_data) - add_pad(b) - - # LONG STRINGS - - long_strings = "" - - file_names = [] - flag = False - for _,file in enumerate(self.files): - if len(file.name) >= 16: - fname = f"{file.name}/\n" - ind = len(long_strings) - long_strings += fname - else: - fname = f"{file.name}/" - ind = None - file_names.append((fname, ind)) - - # Weird hack - if len(long_strings) != 0 and not flag: - flag = True - long_strings += "/\n" - - long_strings += "/" - - add_ar_hdr(b, "//", current_time, 0, 0, 0, len(long_strings)) - add_str(b, long_strings) - add_pad(b) - - # FILES - - armap_pos = 0 - for i,(file,(fname,ind)) in enumerate(zip(self.files, file_names)): - file_pos = len(b) - add_ar_hdr(b, f"/{ind}" if ind is not None else fname, file.time, file.uid, file.gid, file.mode, file.size) - add_bin(b, file.data) - add_pad(b) - - # Patch the armap with file locations - while armap_pos < len(armap_entries_files) and armap_entries_files[armap_pos] == i: - b[armap_offsets_start+armap_pos*4:armap_offsets_start+armap_pos*4+4] = struct.pack(">I", file_pos) - armap_pos += 1 - - return b - - def write(self, file_path): - ar = self.build_image() - with open(file_path, "wb") as outfile: - outfile.write(ar) - - @staticmethod - def from_image(ar_data): - long_strings = None - ar = Archive() - - assert ar_data[:8].decode("ASCII") == "!\n" , "Not an archive file? Bad file magic value" - - i = 8 - while i < len(ar_data): - file_name = ar_data[i:][ 0:][:16].decode("ASCII").strip() - file_time = int(ar_data[i:][16:][:12].decode("ASCII").strip()) - file_uid = int(ar_data[i:][28:][: 6].decode("ASCII").strip()) - file_gid = int(ar_data[i:][34:][: 6].decode("ASCII").strip()) - file_mode = int(ar_data[i:][40:][: 8].decode("ASCII").strip(), 8) - file_size = int(ar_data[i:][48:][:10].decode("ASCII").strip()) - end = ar_data[i:][58:][:2].decode("ASCII") - assert end == "`\n" - - data = ar_data[i:][60:][:file_size] - assert len(data) == file_size - - if file_name == '/': - """ - "armap" - The special filename "/" denotes that this entry contains a symbol lookup table used by some libraries - to speed up file access. - The symbol table is comprised of three contiguous parts: - - A 32-bit Big Endian integer recording the number of symbol entries. - - A list of 32-bit Big Endian integers for each symbol entry, recording the position within the - archive of the header of the file containing the symbol. - - A list of null-terminated strings, the symbol names for each symbol entry. - - We rebuild this from scratch on write out. - """ - ar.time = file_time - assert file_uid == 0 - assert file_gid == 0 - assert file_mode == 0 - - # Code to interpret the armap, currently unused - """ - armap_n_syms = struct.unpack(">I", data[0:4])[0] - offsets = [i[0] for i in struct.iter_unpack(">I", data[4:4+4*armap_n_syms])] - - strings = [] - ofs = 4 + 4 * armap_n_syms - for _ in range(armap_n_syms): - to = data.find(b'\0', ofs) - assert to != -1 - string = data[ofs:to].decode('latin1') - strings.append(string) - ofs += len(string) + 1 - assert all([b == 0 for b in data[ofs:]]) - assert len(strings) == len(offsets) - - ar.armap_entries = list(zip(offsets, strings)) - """ - elif file_name == '//': - """ - Long string table. Strings larger than 16 are placed here and referenced from the header by /. - - We rebuild this from scratch on write out. - """ - assert file_time == ar.time - assert file_uid == 0 - assert file_gid == 0 - assert file_mode == 0 - long_strings = data.decode("ASCII") - else: - """ - Normal files. - """ - if file_name.startswith("/"): - assert long_strings is not None - # Fetch the name from the long string table - file_name = long_strings[int(file_name[1:]):].split("\n")[0] - # Add file - ar.add_data(file_name[:-1], file_time, file_uid, file_gid, file_mode, file_size, data) - - if file_size % 2 != 0: - file_size += 1 - i += 60 + file_size - - return ar - - @staticmethod - def from_file(file_path): - ar_file = None - with open(file_path, "rb") as infile: - ar_file = infile.read() - - return Archive.from_image(ar_file) - -def ar_usage(progname): - print(f"Usage: {progname} [-]{{dmpqrstx}}[abcDfilMNoOPsSTuvV] [member-name] [count] archive-file file...") - print(f" commands:") - print(f" d - delete file(s) from the archive") - print(f" m[ab] - move file(s) in the archive") - print(f" p - print file(s) found in the archive") - print(f" q[f] - quick append file(s) to the archive") - print(f" r[ab][f][u] - replace existing or insert new file(s) into the archive") - print(f" s - act as ranlib") - print(f" t[O][v] - display contents of the archive") - print(f" x[o] - extract file(s) from the archive") - print(f" command specific modifiers:") - print(f" [a] - put file(s) after [member-name]") - print(f" [b] - put file(s) before [member-name] (same as [i])") - print(f" [D] - use zero for timestamps and uids/gids (default)") - print(f" [U] - use actual timestamps and uids/gids") - print(f" [N] - use instance [count] of name") - print(f" [f] - truncate inserted file names") - print(f" [P] - use full path names when matching") - print(f" [o] - preserve original dates") - print(f" [O] - display offsets of files in the archive") - print(f" [u] - only replace files that are newer than current archive contents") - print(f" generic modifiers:") - print(f" [c] - do not warn if the library had to be created") - print(f" [s] - create an archive index (cf. ranlib)") - print(f" [S] - do not build a symbol table") - print(f" [v] - be verbose") - print(f" [V] - display the version number") - print(f" @ - read options from ") - print(f" --output=DIRNAME - specify the output directory for extraction operations") - return 1 - -def ar(argv): - if len(argv) < 2: - return ar_usage(argv[0]) - - progname = argv[0] - create_ok = False - make_ar_idx = False - no_symtab = False - verbose = False - - def verbose_print(msg): - if verbose: - print(msg) - - def dcmd(modifiers, args): - if modifiers != "": - print(f"{progname}: bad modifiers -- '{modifiers}'") - return ar_usage(progname) - - ar_file = args[0] - o_files = args[1:] - - ar = Archive.from_file(ar_file) - for file in ar.files: - if file.name in o_files: - ar.files.remove(file) - ar.write(ar_file) - return 0 - - def mcmd(modifiers, args): - print("") - return 1 - - def pcmd(modifiers, args): - if modifiers != "": - print(f"{progname}: bad modifiers -- '{modifiers}'") - return ar_usage(progname) - if len(args) != 1: - print(f"{progname} p: bad args") - return ar_usage(progname) - - ar_file = args[0] - - ar = Archive.from_file(ar_file) - for file in ar.files: - print(file.data) - return 0 - - def qcmd(modifiers, args): - print("") - return 1 - - def rcmd(modifiers, args): - if modifiers !="": - print("") - if len(args) < 2: - print(f"{progname} r: bad args") - return ar_usage(progname) - - ar_file = args[0] - o_files = args[1:] - - ar = Archive() - if os.path.isfile(ar_file): - ar = Archive.from_file(ar_file) - else: - if not create_ok: - print(f"Warning: Created file {ar_file}") - ar = Archive() - - for o_file in o_files: - ar.add_file(o_file) - - ar.write(ar_file) - - def scmd(modifiers, args): - print("") - return 1 - - def tcmd(modifiers, args): - # TODO modifiers - - if len(args) != 1: - print(f"{progname} t: bad args") - return ar_usage(progname) - - ar_file = args[0] - - ar = Archive.from_file(ar_file) - for file in ar.files: - print(file.name) - return 0 - - def xcmd(modifiers, args): - if modifiers not in ('', 'o'): - print(f"{progname}: bad modifiers -- '{modifiers}'") - return ar_usage(progname) - if len(args) not in (1, 3) or not (args[0].startswith("--output") or args[1].startswith("--output")): - print(f"{progname} t: bad args") - return ar_usage(progname) - - original_times = modifiers == 'o' - - if args[0].startswith("--output"): - out_dir = args[1] - ar_file = args[2] - elif args[1].startswith("--output"): - out_dir = args[2] - ar_file = args[0] - else: - ar_file = args[0] - out_dir = "" - - # Create dir if not exists - if not os.path.exists(out_dir): - os.makedirs(out_dir, exist_ok=True) - - if not os.path.isdir(out_dir): - print(f"Output directory {out_dir} is a file") - return ar_usage(progname) - - # Extract files to destination - ar = Archive.from_file(ar_file) - for file in ar.files: - opath =os.path.join(out_dir, file.name) - with open(opath, "wb") as ofile: - ofile.write(file.data) - - if not original_times: - t = time.time() - os.utime(opath, (t, t)) - # TODO patch with original dates etc. - - return 0 - - argtbl = { - 'd': dcmd, # delete file(s) from the archive - 'm': mcmd, # [ab] move file(s) in the archive - 'p': pcmd, # print file(s) found in the archive - 'q': qcmd, # [f] quick append file(s) to the archive - 'r': rcmd, # [ab][f][u] replace existing or insert new file(s) into the archive - 's': scmd, # act as ranlib - 't': tcmd, # [O][v] display contents of the archive - 'x': xcmd, # [o] extract file(s) from the archive - } - - arg1 = argv[1] - if arg1[0] == '-': - arg1 = arg1[1:] - cmd = arg1[0] - - if cmd not in argtbl: - print(f"{progname}: invalid option -- '{cmd}'") - return ar_usage(argv[0]) - - modifiers = arg1[1:] - - create_ok = "c" in modifiers - modifiers = modifiers.replace("c","") - make_ar_idx = "s" in modifiers - modifiers = modifiers.replace("s","") - no_symtab = "S" in modifiers - modifiers = modifiers.replace("S","") - verbose = "v" in modifiers - modifiers = modifiers.replace("v","") - - if "V" in modifiers: - print(f"{progname} v1.0") - - return argtbl[cmd](modifiers, argv[2:]) - -if __name__ == '__main__': - import sys - sys.exit(ar(sys.argv)) diff --git a/tools/asm_differ/.gitignore b/tools/asm_differ/.gitignore deleted file mode 100644 index eb176dc5..00000000 --- a/tools/asm_differ/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.mypy_cache/ -__pycache__/ diff --git a/tools/asm_differ/.gitrepo b/tools/asm_differ/.gitrepo deleted file mode 100644 index 2e999c85..00000000 --- a/tools/asm_differ/.gitrepo +++ /dev/null @@ -1,12 +0,0 @@ -; DO NOT EDIT (unless you know what you are doing) -; -; This subdirectory is a git "subrepo", and this file is maintained by the -; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme -; -[subrepo] - remote = https://github.com/simonlindholm/asm-differ - branch = main - commit = 4b38c884c1efdc3bfa8b14f13015a69368a8d3a2 - parent = 32a1a8061de197c4f10d4904cd72a22dd7cf905c - method = merge - cmdver = 0.4.3 diff --git a/tools/asm_differ/.pre-commit-config.yaml b/tools/asm_differ/.pre-commit-config.yaml deleted file mode 100644 index 6695f71a..00000000 --- a/tools/asm_differ/.pre-commit-config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -repos: -- repo: https://github.com/psf/black - rev: 20.8b1 - hooks: - - id: black - language_version: python3.6 diff --git a/tools/asm_differ/LICENSE b/tools/asm_differ/LICENSE deleted file mode 100644 index cf1ab25d..00000000 --- a/tools/asm_differ/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to diff --git a/tools/asm_differ/README.md b/tools/asm_differ/README.md deleted file mode 100644 index 4a7f329b..00000000 --- a/tools/asm_differ/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# asm-differ - -Nice differ for assembly code. Currently supports MIPS, PPC, AArch64, and ARM32; should be easy to hack to support other instruction sets. - -![](screenshot.png) - -## Dependencies - -- Python >= 3.6 -- `python3 -m pip install --user colorama watchdog python-Levenshtein` (also `dataclasses` if on 3.6) - -## Usage - -Create a file `diff_settings.sh` in some directory (see the one in this repo for an example). Then from that directory, run - -```bash -/path/to/diff.sh [flags] (function|rom addr) -``` - -Recommended flags are `-mwo` (automatically run `make` on source file changes, and include symbols in diff). See `--help` for more details. - -### Tab completion - -[argcomplete](https://kislyuk.github.io/argcomplete/) can be optionally installed (with `python3 -m pip install argcomplete`) to enable tab completion in a bash shell, completing options and symbol names using the linker map. It also requires a bit more setup: - -If invoking the script **exactly** as `./diff.py`, the following should be added to the `.bashrc` according to argcomplete's instructions: - -```bash -eval "$(register-python-argcomplete ./diff.py)" -``` - -If that doesn't work, run `register-python-argcomplete ./diff.py` in your terminal and copy the output to `.bashrc`. - -If setup correctly (don't forget to restart the shell), `complete | grep ./diff.py` should output: - -```bash -complete -o bashdefault -o default -o nospace -F _python_argcomplete ./diff.py -``` - -Note for developers or for general troubleshooting: run `export _ARC_DEBUG=` to enable debug output during tab-completion, it may show otherwise silenced errors. Use `unset _ARC_DEBUG` or restart the terminal to disable. - -### Contributing - -Contributions are very welcome! Some notes on workflow: - -`black` is used for code formatting. You can either run `black diff.py` manually, or set up a pre-commit hook: -```bash -pip install pre-commit black -pre-commit install -``` - -Type annotations are used for all Python code. `mypy` should pass without any errors. - -PRs that skip the above are still welcome, however. - -The targeted Python version is 3.6. There are currently no tests. diff --git a/tools/asm_differ/diff-stylesheet.css b/tools/asm_differ/diff-stylesheet.css deleted file mode 100644 index 79da120d..00000000 --- a/tools/asm_differ/diff-stylesheet.css +++ /dev/null @@ -1,67 +0,0 @@ -table.diff { - border: none; - font-family: Monospace; - white-space: pre; -} -tr.data-ref { - background-color: gray; -} -.immediate { - color: lightblue; -} -.stack { - color: yellow; -} -.register { - color: yellow; -} -.delay-slot { - font-weight: bold; - color: gray; -} -.diff-change { - color: lightblue; -} -.diff-add { - color: green; -} -.diff-remove { - color: red; -} -.source-filename { - font-weight: bold; -} -.source-function { - font-weight: bold; - text-decoration: underline; -} -.source-other { - font-style: italic; -} -.rotation-0 { - color: magenta; -} -.rotation-1 { - color: cyan; -} -.rotation-2 { - color: green; -} -.rotation-3 { - color: red; -} -.rotation-4 { - color: yellow; -} -.rotation-5 { - color: pink; -} -.rotation-6 { - color: blue; -} -.rotation-7 { - color: lime; -} -.rotation-8 { - color: gray; -} diff --git a/tools/asm_differ/diff.py b/tools/asm_differ/diff.py deleted file mode 100755 index 8ed764f9..00000000 --- a/tools/asm_differ/diff.py +++ /dev/null @@ -1,2923 +0,0 @@ -#!/usr/bin/env python3 -# PYTHON_ARGCOMPLETE_OK -import argparse -import sys -from typing import ( - Any, - Callable, - Dict, - Iterator, - List, - Match, - NoReturn, - Optional, - Pattern, - Set, - Tuple, - Type, - Union, -) - - -def fail(msg: str) -> NoReturn: - print(msg, file=sys.stderr) - sys.exit(1) - - -def static_assert_unreachable(x: NoReturn) -> NoReturn: - raise Exception("Unreachable! " + repr(x)) - - -# ==== COMMAND-LINE ==== - -if __name__ == "__main__": - # Prefer to use diff_settings.py from the current working directory - sys.path.insert(0, ".") - try: - import diff_settings - except ModuleNotFoundError: - fail("Unable to find diff_settings.py in the same directory.") - sys.path.pop(0) - - try: - import argcomplete - except ModuleNotFoundError: - argcomplete = None - - parser = argparse.ArgumentParser( - description="Diff MIPS, PPC, AArch64, or ARM32 assembly." - ) - - start_argument = parser.add_argument( - "start", - help="Function name or address to start diffing from.", - ) - - if argcomplete: - - def complete_symbol( - prefix: str, parsed_args: argparse.Namespace, **kwargs: object - ) -> List[str]: - if not prefix or prefix.startswith("-"): - # skip reading the map file, which would - # result in a lot of useless completions - return [] - config: Dict[str, Any] = {} - diff_settings.apply(config, parsed_args) # type: ignore - mapfile = config.get("mapfile") - if not mapfile: - return [] - completes = [] - with open(mapfile) as f: - data = f.read() - # assume symbols are prefixed by a space character - search = f" {prefix}" - pos = data.find(search) - while pos != -1: - # skip the space character in the search string - pos += 1 - # assume symbols are suffixed by either a space - # character or a (unix-style) line return - spacePos = data.find(" ", pos) - lineReturnPos = data.find("\n", pos) - if lineReturnPos == -1: - endPos = spacePos - elif spacePos == -1: - endPos = lineReturnPos - else: - endPos = min(spacePos, lineReturnPos) - if endPos == -1: - match = data[pos:] - pos = -1 - else: - match = data[pos:endPos] - pos = data.find(search, endPos) - completes.append(match) - return completes - - setattr(start_argument, "completer", complete_symbol) - - parser.add_argument( - "end", - nargs="?", - help="Address to end diff at.", - ) - parser.add_argument( - "-o", - dest="diff_obj", - action="store_true", - help="""Diff .o files rather than a whole binary. This makes it possible to - see symbol names. (Recommended)""", - ) - parser.add_argument( - "-e", - "--elf", - dest="diff_elf_symbol", - metavar="SYMBOL", - help="""Diff a given function in two ELFs, one being stripped and the other - one non-stripped. Requires objdump from binutils 2.33+.""", - ) - parser.add_argument( - "-c", - "--source", - dest="show_source", - action="store_true", - help="Show source code (if possible). Only works with -o or -e.", - ) - parser.add_argument( - "-C", - "--source-old-binutils", - dest="source_old_binutils", - action="store_true", - help="""Tweak --source handling to make it work with binutils < 2.33. - Implies --source.""", - ) - parser.add_argument( - "-j", - "--section", - dest="diff_section", - default=".text", - metavar="SECTION", - help="Diff restricted to a given output section.", - ) - parser.add_argument( - "-L", - "--line-numbers", - dest="show_line_numbers", - action="store_const", - const=True, - help="""Show source line numbers in output, when available. May be enabled by - default depending on diff_settings.py.""", - ) - parser.add_argument( - "--no-line-numbers", - dest="show_line_numbers", - action="store_const", - const=False, - help="Hide source line numbers in output.", - ) - parser.add_argument( - "--inlines", - dest="inlines", - action="store_true", - help="Show inline function calls (if possible). Only works with -o or -e.", - ) - parser.add_argument( - "--base-asm", - dest="base_asm", - metavar="FILE", - help="Read assembly from given file instead of configured base img.", - ) - parser.add_argument( - "--write-asm", - dest="write_asm", - metavar="FILE", - help="Write the current assembly output to file, e.g. for use with --base-asm.", - ) - parser.add_argument( - "-m", - "--make", - dest="make", - action="store_true", - help="Automatically run 'make' on the .o file or binary before diffing.", - ) - parser.add_argument( - "-l", - "--skip-lines", - dest="skip_lines", - metavar="LINES", - type=int, - default=0, - help="Skip the first LINES lines of output.", - ) - parser.add_argument( - "-s", - "--stop-jr-ra", - dest="stop_jrra", - action="store_true", - help="""Stop disassembling at the first 'jr ra'. Some functions have - multiple return points, so use with care!""", - ) - parser.add_argument( - "-i", - "--ignore-large-imms", - dest="ignore_large_imms", - action="store_true", - help="Pretend all large enough immediates are the same.", - ) - parser.add_argument( - "-I", - "--ignore-addr-diffs", - dest="ignore_addr_diffs", - action="store_true", - help="Ignore address differences. Currently only affects AArch64 and ARM32.", - ) - parser.add_argument( - "-B", - "--no-show-branches", - dest="show_branches", - action="store_false", - help="Don't visualize branches/branch targets.", - ) - parser.add_argument( - "-S", - "--base-shift", - dest="base_shift", - metavar="N", - type=str, - default="0", - help="""Diff position N in our img against position N + shift in the base img. - Arithmetic is allowed, so e.g. |-S "0x1234 - 0x4321"| is a reasonable - flag to pass if it is known that position 0x1234 in the base img syncs - up with position 0x4321 in our img. Not supported together with -o.""", - ) - parser.add_argument( - "-w", - "--watch", - dest="watch", - action="store_true", - help="""Automatically update when source/object files change. - Recommended in combination with -m.""", - ) - parser.add_argument( - "-3", - "--threeway=prev", - dest="threeway", - action="store_const", - const="prev", - help="""Show a three-way diff between target asm, current asm, and asm - prior to -w rebuild. Requires -w.""", - ) - parser.add_argument( - "-b", - "--threeway=base", - dest="threeway", - action="store_const", - const="base", - help="""Show a three-way diff between target asm, current asm, and asm - when diff.py was started. Requires -w.""", - ) - parser.add_argument( - "--width", - dest="column_width", - metavar="COLS", - type=int, - default=50, - help="Sets the width of the left and right view column.", - ) - parser.add_argument( - "--algorithm", - dest="algorithm", - default="levenshtein", - choices=["levenshtein", "difflib"], - help="""Diff algorithm to use. Levenshtein gives the minimum diff, while difflib - aims for long sections of equal opcodes. Defaults to %(default)s.""", - ) - parser.add_argument( - "--max-size", - "--max-lines", - metavar="LINES", - dest="max_lines", - type=int, - default=1024, - help="The maximum length of the diff, in lines.", - ) - parser.add_argument( - "--no-pager", - dest="no_pager", - action="store_true", - help="""Disable the pager; write output directly to stdout, then exit. - Incompatible with --watch.""", - ) - parser.add_argument( - "--format", - choices=("color", "plain", "html", "json"), - default="color", - help="Output format, default is color. --format=html or json implies --no-pager.", - ) - parser.add_argument( - "-U", - "--compress-matching", - metavar="N", - dest="compress_matching", - type=int, - help="""Compress streaks of matching lines, leaving N lines of context - around non-matching parts.""", - ) - parser.add_argument( - "-V", - "--compress-sameinstr", - metavar="N", - dest="compress_sameinstr", - type=int, - help="""Compress streaks of lines with same instructions (but possibly - different regalloc), leaving N lines of context around other parts.""", - ) - - # Project-specific flags, e.g. different versions/make arguments. - add_custom_arguments_fn = getattr(diff_settings, "add_custom_arguments", None) - if add_custom_arguments_fn: - add_custom_arguments_fn(parser) - - if argcomplete: - argcomplete.autocomplete(parser) - -# ==== IMPORTS ==== - -# (We do imports late to optimize auto-complete performance.) - -import abc -import ast -from collections import Counter, defaultdict -from dataclasses import asdict, dataclass, field, replace -import difflib -import enum -import html -import itertools -import json -import os -import queue -import re -import string -import struct -import subprocess -import threading -import time -import traceback - - -MISSING_PREREQUISITES = ( - "Missing prerequisite python module {}. " - "Run `python3 -m pip install --user colorama watchdog python-Levenshtein cxxfilt` to install prerequisites (cxxfilt only needed with --source)." -) - -try: - from colorama import Back, Fore, Style - import watchdog -except ModuleNotFoundError as e: - fail(MISSING_PREREQUISITES.format(e.name)) - -# ==== CONFIG ==== - - -@dataclass -class ProjectSettings: - arch_str: str - objdump_executable: str - build_command: List[str] - map_format: str - mw_build_dir: str - baseimg: Optional[str] - myimg: Optional[str] - mapfile: Optional[str] - source_directories: Optional[List[str]] - source_extensions: List[str] - show_line_numbers_default: bool - disassemble_all: bool - - -@dataclass -class Compress: - context: int - same_instr: bool - - -@dataclass -class Config: - arch: "ArchSettings" - - # Build/objdump options - diff_obj: bool - make: bool - source_old_binutils: bool - diff_section: str - inlines: bool - max_function_size_lines: int - max_function_size_bytes: int - - # Display options - formatter: "Formatter" - threeway: Optional[str] - base_shift: int - skip_lines: int - compress: Optional[Compress] - show_branches: bool - show_line_numbers: bool - show_source: bool - stop_jrra: bool - ignore_large_imms: bool - ignore_addr_diffs: bool - algorithm: str - - # Score options - score_stack_differences = True - penalty_stackdiff = 1 - penalty_regalloc = 5 - penalty_reordering = 60 - penalty_insertion = 100 - penalty_deletion = 100 - - -def create_project_settings(settings: Dict[str, Any]) -> ProjectSettings: - return ProjectSettings( - arch_str=settings.get("arch", "mips"), - baseimg=settings.get("baseimg"), - myimg=settings.get("myimg"), - mapfile=settings.get("mapfile"), - build_command=settings.get( - "make_command", ["make", *settings.get("makeflags", [])] - ), - source_directories=settings.get("source_directories"), - source_extensions=settings.get( - "source_extensions", [".c", ".h", ".cpp", ".hpp", ".s"] - ), - objdump_executable=get_objdump_executable(settings.get("objdump_executable")), - map_format=settings.get("map_format", "gnu"), - mw_build_dir=settings.get("mw_build_dir", "build/"), - show_line_numbers_default=settings.get("show_line_numbers_default", True), - disassemble_all=settings.get("disassemble_all", False) - ) - - -def create_config(args: argparse.Namespace, project: ProjectSettings) -> Config: - arch = get_arch(project.arch_str) - - formatter: Formatter - if args.format == "plain": - formatter = PlainFormatter(column_width=args.column_width) - elif args.format == "color": - formatter = AnsiFormatter(column_width=args.column_width) - elif args.format == "html": - formatter = HtmlFormatter() - elif args.format == "json": - formatter = JsonFormatter(arch_str=arch.name) - else: - raise ValueError(f"Unsupported --format: {args.format}") - - compress = None - if args.compress_matching is not None: - compress = Compress(args.compress_matching, False) - if args.compress_sameinstr is not None: - if compress is not None: - raise ValueError( - "Cannot pass both --compress-matching and --compress-sameinstr" - ) - compress = Compress(args.compress_sameinstr, True) - - show_line_numbers = args.show_line_numbers - if show_line_numbers is None: - show_line_numbers = project.show_line_numbers_default - - return Config( - arch=arch, - # Build/objdump options - diff_obj=args.diff_obj, - make=args.make, - source_old_binutils=args.source_old_binutils, - diff_section=args.diff_section, - inlines=args.inlines, - max_function_size_lines=args.max_lines, - max_function_size_bytes=args.max_lines * 4, - # Display options - formatter=formatter, - threeway=args.threeway, - base_shift=eval_int( - args.base_shift, "Failed to parse --base-shift (-S) argument as an integer." - ), - skip_lines=args.skip_lines, - compress=compress, - show_branches=args.show_branches, - show_line_numbers=show_line_numbers, - show_source=args.show_source or args.source_old_binutils, - stop_jrra=args.stop_jrra, - ignore_large_imms=args.ignore_large_imms, - ignore_addr_diffs=args.ignore_addr_diffs, - algorithm=args.algorithm, - ) - - -def get_objdump_executable(objdump_executable: Optional[str]) -> str: - if objdump_executable is not None: - return objdump_executable - - objdump_candidates = [ - "mips-linux-gnu-objdump", - "mips64-elf-objdump", - "mips-elf-objdump", - ] - for objdump_cand in objdump_candidates: - try: - subprocess.check_call( - [objdump_cand, "--version"], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - return objdump_cand - except subprocess.CalledProcessError: - pass - except FileNotFoundError: - pass - - return fail( - f"Missing binutils; please ensure {' or '.join(objdump_candidates)} exists, or configure objdump_executable." - ) - - -def get_arch(arch_str: str) -> "ArchSettings": - for settings in ARCH_SETTINGS: - if arch_str == settings.name: - return settings - raise ValueError(f"Unknown architecture: {arch_str}") - - -BUFFER_CMD: List[str] = ["tail", "-c", str(10 ** 9)] - -# -S truncates long lines instead of wrapping them -# -R interprets color escape sequences -# -i ignores case when searching -# -c something about how the screen gets redrawn; I don't remember the purpose -# -#6 makes left/right arrow keys scroll by 6 characters -LESS_CMD: List[str] = ["less", "-SRic", "-#6"] - -DEBOUNCE_DELAY: float = 0.1 - -# ==== FORMATTING ==== - - -@enum.unique -class BasicFormat(enum.Enum): - NONE = enum.auto() - IMMEDIATE = enum.auto() - STACK = enum.auto() - REGISTER = enum.auto() - DELAY_SLOT = enum.auto() - DIFF_CHANGE = enum.auto() - DIFF_ADD = enum.auto() - DIFF_REMOVE = enum.auto() - SOURCE_FILENAME = enum.auto() - SOURCE_FUNCTION = enum.auto() - SOURCE_LINE_NUM = enum.auto() - SOURCE_OTHER = enum.auto() - - -@dataclass(frozen=True) -class RotationFormat: - group: str - index: int - key: str - - -Format = Union[BasicFormat, RotationFormat] -FormatFunction = Callable[[str], Format] - - -class Text: - segments: List[Tuple[str, Format]] - - def __init__(self, line: str = "", f: Format = BasicFormat.NONE) -> None: - self.segments = [(line, f)] if line else [] - - def reformat(self, f: Format) -> "Text": - return Text(self.plain(), f) - - def plain(self) -> str: - return "".join(s for s, f in self.segments) - - def __repr__(self) -> str: - return f"" - - def __bool__(self) -> bool: - return any(s for s, f in self.segments) - - def __str__(self) -> str: - # Use Formatter.apply(...) instead - return NotImplemented - - def __eq__(self, other: object) -> bool: - return NotImplemented - - def __add__(self, other: Union["Text", str]) -> "Text": - if isinstance(other, str): - other = Text(other) - result = Text() - # If two adjacent segments have the same format, merge their lines - if ( - self.segments - and other.segments - and self.segments[-1][1] == other.segments[0][1] - ): - result.segments = ( - self.segments[:-1] - + [(self.segments[-1][0] + other.segments[0][0], self.segments[-1][1])] - + other.segments[1:] - ) - else: - result.segments = self.segments + other.segments - return result - - def __radd__(self, other: Union["Text", str]) -> "Text": - if isinstance(other, str): - other = Text(other) - return other + self - - def finditer(self, pat: Pattern[str]) -> Iterator[Match[str]]: - """Replacement for `pat.finditer(text)` that operates on the inner text, - and returns the exact same matches as `Text.sub(pat, ...)`.""" - for chunk, f in self.segments: - for match in pat.finditer(chunk): - yield match - - def sub(self, pat: Pattern[str], sub_fn: Callable[[Match[str]], "Text"]) -> "Text": - result = Text() - for chunk, f in self.segments: - i = 0 - for match in pat.finditer(chunk): - start, end = match.start(), match.end() - assert i <= start <= end <= len(chunk) - sub = sub_fn(match) - if i != start: - result.segments.append((chunk[i:start], f)) - result.segments.extend(sub.segments) - i = end - if chunk[i:]: - result.segments.append((chunk[i:], f)) - return result - - def ljust(self, column_width: int) -> "Text": - length = sum(len(x) for x, _ in self.segments) - return self + " " * max(column_width - length, 0) - - -@dataclass -class TableMetadata: - headers: Tuple[Text, ...] - current_score: int - max_score: int - previous_score: Optional[int] - - -class Formatter(abc.ABC): - @abc.abstractmethod - def apply_format(self, chunk: str, f: Format) -> str: - """Apply the formatting `f` to `chunk` and escape the contents.""" - ... - - @abc.abstractmethod - def table(self, meta: TableMetadata, lines: List[Tuple["OutputLine", ...]]) -> str: - """Format a multi-column table with metadata""" - ... - - def apply(self, text: Text) -> str: - return "".join(self.apply_format(chunk, f) for chunk, f in text.segments) - - @staticmethod - def outputline_texts(lines: Tuple["OutputLine", ...]) -> Tuple[Text, ...]: - return tuple([lines[0].base or Text()] + [line.fmt2 for line in lines[1:]]) - - -@dataclass -class PlainFormatter(Formatter): - column_width: int - - def apply_format(self, chunk: str, f: Format) -> str: - return chunk - - def table(self, meta: TableMetadata, lines: List[Tuple["OutputLine", ...]]) -> str: - rows = [meta.headers] + [self.outputline_texts(ls) for ls in lines] - return "\n".join( - "".join(self.apply(x.ljust(self.column_width)) for x in row) for row in rows - ) - - -@dataclass -class AnsiFormatter(Formatter): - # Additional ansi escape codes not in colorama. See: - # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters - STYLE_UNDERLINE = "\x1b[4m" - STYLE_NO_UNDERLINE = "\x1b[24m" - STYLE_INVERT = "\x1b[7m" - - BASIC_ANSI_CODES = { - BasicFormat.NONE: "", - BasicFormat.IMMEDIATE: Fore.LIGHTBLUE_EX, - BasicFormat.STACK: Fore.YELLOW, - BasicFormat.REGISTER: Fore.YELLOW, - BasicFormat.DELAY_SLOT: Fore.LIGHTBLACK_EX, - BasicFormat.DIFF_CHANGE: Fore.LIGHTBLUE_EX, - BasicFormat.DIFF_ADD: Fore.GREEN, - BasicFormat.DIFF_REMOVE: Fore.RED, - BasicFormat.SOURCE_FILENAME: Style.DIM + Style.BRIGHT, - BasicFormat.SOURCE_FUNCTION: Style.DIM + Style.BRIGHT + STYLE_UNDERLINE, - BasicFormat.SOURCE_LINE_NUM: Fore.LIGHTBLACK_EX, - BasicFormat.SOURCE_OTHER: Style.DIM, - } - - BASIC_ANSI_CODES_UNDO = { - BasicFormat.NONE: "", - BasicFormat.SOURCE_FILENAME: Style.NORMAL, - BasicFormat.SOURCE_FUNCTION: Style.NORMAL + STYLE_NO_UNDERLINE, - BasicFormat.SOURCE_OTHER: Style.NORMAL, - } - - ROTATION_ANSI_COLORS = [ - Fore.MAGENTA, - Fore.CYAN, - Fore.GREEN, - Fore.RED, - Fore.LIGHTYELLOW_EX, - Fore.LIGHTMAGENTA_EX, - Fore.LIGHTCYAN_EX, - Fore.LIGHTGREEN_EX, - Fore.LIGHTBLACK_EX, - ] - - column_width: int - - def apply_format(self, chunk: str, f: Format) -> str: - if f == BasicFormat.NONE: - return chunk - undo_ansi_code = Fore.RESET - if isinstance(f, BasicFormat): - ansi_code = self.BASIC_ANSI_CODES[f] - undo_ansi_code = self.BASIC_ANSI_CODES_UNDO.get(f, undo_ansi_code) - elif isinstance(f, RotationFormat): - ansi_code = self.ROTATION_ANSI_COLORS[ - f.index % len(self.ROTATION_ANSI_COLORS) - ] - else: - static_assert_unreachable(f) - return f"{ansi_code}{chunk}{undo_ansi_code}" - - def table(self, meta: TableMetadata, lines: List[Tuple["OutputLine", ...]]) -> str: - rows = [(meta.headers, False)] + [ - (self.outputline_texts(line), line[1].is_data_ref) for line in lines - ] - return "\n".join( - "".join( - (self.STYLE_INVERT if is_data_ref else "") - + self.apply(x.ljust(self.column_width)) - for x in row - ) - for (row, is_data_ref) in rows - ) - - -@dataclass -class HtmlFormatter(Formatter): - rotation_formats: int = 9 - - def apply_format(self, chunk: str, f: Format) -> str: - chunk = html.escape(chunk) - if f == BasicFormat.NONE: - return chunk - if isinstance(f, BasicFormat): - class_name = f.name.lower().replace("_", "-") - data_attr = "" - elif isinstance(f, RotationFormat): - class_name = f"rotation-{f.index % self.rotation_formats}" - rotation_key = html.escape(f"{f.group};{f.key}", quote=True) - data_attr = f'data-rotation="{rotation_key}"' - else: - static_assert_unreachable(f) - return f"{chunk}" - - def table(self, meta: TableMetadata, lines: List[Tuple["OutputLine", ...]]) -> str: - def table_row(line: Tuple[Text, ...], is_data_ref: bool, cell_el: str) -> str: - tr_attrs = " class='data-ref'" if is_data_ref else "" - output_row = f" " - for cell in line: - cell_html = self.apply(cell) - output_row += f"<{cell_el}>{cell_html}" - output_row += "\n" - return output_row - - output = "\n" - output += " \n" - output += table_row(meta.headers, False, "th") - output += " \n" - output += " \n" - output += "".join( - table_row(self.outputline_texts(line), line[1].is_data_ref, "td") - for line in lines - ) - output += " \n" - output += "
\n" - return output - - -@dataclass -class JsonFormatter(Formatter): - arch_str: str - - def apply_format(self, chunk: str, f: Format) -> str: - # This method is unused by this formatter - return NotImplemented - - def table(self, meta: TableMetadata, rows: List[Tuple["OutputLine", ...]]) -> str: - def serialize_format(s: str, f: Format) -> Dict[str, Any]: - if f == BasicFormat.NONE: - return {"text": s} - elif isinstance(f, BasicFormat): - return {"text": s, "format": f.name.lower()} - elif isinstance(f, RotationFormat): - attrs = asdict(f) - attrs.update( - { - "text": s, - "format": "rotation", - } - ) - return attrs - else: - static_assert_unreachable(f) - - def serialize(text: Optional[Text]) -> List[Dict[str, Any]]: - if text is None: - return [] - return [serialize_format(s, f) for s, f in text.segments] - - is_threeway = len(meta.headers) == 3 - - output: Dict[str, Any] = {} - output["arch_str"] = self.arch_str - output["header"] = { - name: serialize(h) - for h, name in zip(meta.headers, ("base", "current", "previous")) - } - output["current_score"] = meta.current_score - output["max_score"] = meta.max_score - if meta.previous_score is not None: - output["previous_score"] = meta.previous_score - output_rows: List[Dict[str, Any]] = [] - for row in rows: - output_row: Dict[str, Any] = {} - output_row["key"] = row[0].key2 - output_row["is_data_ref"] = row[1].is_data_ref - iters = [ - ("base", row[0].base, row[0].line1), - ("current", row[1].fmt2, row[1].line2), - ] - if is_threeway: - iters.append(("previous", row[2].fmt2, row[2].line2)) - if all(line is None for _, _, line in iters): - # Skip rows that were only for displaying source code - continue - for column_name, text, line in iters: - column: Dict[str, Any] = {} - column["text"] = serialize(text) - if line: - if line.line_num is not None: - column["line"] = line.line_num - if line.branch_target is not None: - column["branch"] = line.branch_target - if line.source_lines: - column["src"] = line.source_lines - if line.comment is not None: - column["src_comment"] = line.comment - if line.source_line_num is not None: - column["src_line"] = line.source_line_num - if line or column["text"]: - output_row[column_name] = column - output_rows.append(output_row) - output["rows"] = output_rows - return json.dumps(output) - - -def format_fields( - pat: Pattern[str], - out1: Text, - out2: Text, - color1: FormatFunction, - color2: Optional[FormatFunction] = None, -) -> Tuple[Text, Text]: - diffs = [ - of.group() != nf.group() - for (of, nf) in zip(out1.finditer(pat), out2.finditer(pat)) - ] - - it = iter(diffs) - - def maybe_color(color: FormatFunction, s: str) -> Text: - return Text(s, color(s)) if next(it, False) else Text(s) - - out1 = out1.sub(pat, lambda m: maybe_color(color1, m.group())) - it = iter(diffs) - out2 = out2.sub(pat, lambda m: maybe_color(color2 or color1, m.group())) - - return out1, out2 - - -def symbol_formatter(group: str, base_index: int) -> FormatFunction: - symbol_formats: Dict[str, Format] = {} - - def symbol_format(s: str) -> Format: - # TODO: it would be nice to use a unique Format for each symbol, so we could - # add extra UI elements in the HTML version - f = symbol_formats.get(s) - if f is None: - index = len(symbol_formats) + base_index - f = RotationFormat(key=s, index=index, group=group) - symbol_formats[s] = f - return f - - return symbol_format - - -# ==== LOGIC ==== - -ObjdumpCommand = Tuple[List[str], str, Optional[str]] - - -def maybe_eval_int(expr: str) -> Optional[int]: - try: - ret = ast.literal_eval(expr) - if not isinstance(ret, int): - raise Exception("not an integer") - return ret - except Exception: - return None - - -def eval_int(expr: str, emsg: str) -> int: - ret = maybe_eval_int(expr) - if ret is None: - fail(emsg) - return ret - - -def eval_line_num(expr: str) -> Optional[int]: - expr = expr.strip().replace(":", "") - if expr == "": - return None - return int(expr, 16) - - -def run_make(target: str, project: ProjectSettings) -> None: - subprocess.check_call(project.build_command + [target]) - - -def run_make_capture_output( - target: str, project: ProjectSettings -) -> "subprocess.CompletedProcess[bytes]": - return subprocess.run( - project.build_command + [target], - stderr=subprocess.PIPE, - stdout=subprocess.PIPE, - ) - - -def restrict_to_function(dump: str, fn_name: str) -> str: - try: - ind = dump.index("\n", dump.index(f"<{fn_name}>:")) - return dump[ind + 1 :] - except ValueError: - return "" - - -def serialize_data_references(references: List[Tuple[int, int, str]]) -> str: - return "".join( - f"DATAREF {text_offset} {from_offset} {from_section}\n" - for (text_offset, from_offset, from_section) in references - ) - - -def maybe_get_objdump_source_flags(config: Config) -> List[str]: - flags = [] - - if config.show_line_numbers or config.show_source: - flags.append("--line-numbers") - - if config.show_source: - flags.append("--source") - - if not config.source_old_binutils: - flags.append("--source-comment=│ ") - - if config.inlines: - flags.append("--inlines") - - return flags - - -def run_objdump(cmd: ObjdumpCommand, config: Config, project: ProjectSettings) -> str: - flags, target, restrict = cmd - try: - out = subprocess.run( - [project.objdump_executable] + config.arch.arch_flags + flags + [target], - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - ).stdout - except subprocess.CalledProcessError as e: - print(e.stdout) - print(e.stderr) - if "unrecognized option '--source-comment" in e.stderr: - fail("** Try using --source-old-binutils instead of --source **") - raise e - - obj_data: Optional[bytes] = None - if config.diff_obj: - with open(target, "rb") as f: - obj_data = f.read() - - return preprocess_objdump_out(restrict, obj_data, out, config) - - -def preprocess_objdump_out( - restrict: Optional[str], obj_data: Optional[bytes], objdump_out: str, config: Config -) -> str: - """ - Preprocess the output of objdump into a format that `process()` expects. - This format is suitable for saving to disk with `--write-asm`. - - - Optionally filter the output to a single function (`restrict`) - - Otherwise, strip objdump header (7 lines) - - Prepend .data references ("DATAREF" lines) when working with object files - """ - out = objdump_out - - if restrict is not None: - out = restrict_to_function(out, restrict) - else: - for i in range(7): - out = out[out.find("\n") + 1 :] - out = out.rstrip("\n") - - if obj_data: - out = serialize_data_references(parse_elf_data_references(obj_data, config)) + out - - return out - - -def search_map_file( - fn_name: str, project: ProjectSettings, config: Config -) -> Tuple[Optional[str], Optional[int]]: - if not project.mapfile: - fail(f"No map file configured; cannot find function {fn_name}.") - - try: - with open(project.mapfile) as f: - contents = f.read() - except Exception: - fail(f"Failed to open map file {project.mapfile} for reading.") - - if project.map_format == "gnu": - lines = contents.split("\n") - - try: - cur_objfile = None - ram_to_rom = None - cands = [] - last_line = "" - for line in lines: - if line.startswith(" " + config.diff_section): - cur_objfile = line.split()[3] - if "load address" in line: - tokens = last_line.split() + line.split() - ram = int(tokens[1], 0) - rom = int(tokens[5], 0) - ram_to_rom = rom - ram - if line.endswith(" " + fn_name): - ram = int(line.split()[0], 0) - if cur_objfile is not None and ram_to_rom is not None: - cands.append((cur_objfile, ram + ram_to_rom)) - last_line = line - except Exception as e: - traceback.print_exc() - fail(f"Internal error while parsing map file") - - if len(cands) > 1: - fail(f"Found multiple occurrences of function {fn_name} in map file.") - if len(cands) == 1: - return cands[0] - elif project.map_format == "mw": - section_pattern = re.escape(config.diff_section) - find = re.findall( - re.compile( - # ram elf rom - r" \S+ \S+ (\S+) (\S+) . " - + fn_name - # object name - + r"(?: \(entry of " + section_pattern + r"\))? \t(\S+)" - ), - contents, - ) - if len(find) > 1: - fail(f"Found multiple occurrences of function {fn_name} in map file.") - if len(find) == 1: - rom = int(find[0][1], 16) - objname = find[0][2] - # The metrowerks linker map format does not contain the full object path, - # so we must complete it manually. - objfiles = [ - os.path.join(dirpath, f) - for dirpath, _, filenames in os.walk(project.mw_build_dir) - for f in filenames - if f == objname - ] - if len(objfiles) > 1: - all_objects = "\n".join(objfiles) - fail( - f"Found multiple objects of the same name {objname} in {project.mw_build_dir}, " - f"cannot determine which to diff against: \n{all_objects}" - ) - if len(objfiles) == 1: - objfile = objfiles[0] - # TODO Currently the ram-rom conversion only works for diffing ELF - # executables, but it would likely be more convenient to diff DOLs. - # At this time it is recommended to always use -o when running the diff - # script as this mode does not make use of the ram-rom conversion. - return objfile, rom - else: - fail(f"Linker map format {project.map_format} unrecognised.") - return None, None - - -def parse_elf_data_references(data: bytes, config: Config) -> List[Tuple[int, int, str]]: - e_ident = data[:16] - if e_ident[:4] != b"\x7FELF": - return [] - - SHT_SYMTAB = 2 - SHT_REL = 9 - SHT_RELA = 4 - - is_32bit = e_ident[4] == 1 - is_little_endian = e_ident[5] == 1 - str_end = "<" if is_little_endian else ">" - str_off = "I" if is_32bit else "Q" - sym_size = {"B": 1, "H": 2, "I": 4, "Q": 8} - - def read(spec: str, offset: int) -> Tuple[int, ...]: - spec = spec.replace("P", str_off) - size = struct.calcsize(spec) - return struct.unpack(str_end + spec, data[offset : offset + size]) - - ( - e_type, - e_machine, - e_version, - e_entry, - e_phoff, - e_shoff, - e_flags, - e_ehsize, - e_phentsize, - e_phnum, - e_shentsize, - e_shnum, - e_shstrndx, - ) = read("HHIPPPIHHHHHH", 16) - if e_type != 1: # relocatable - return [] - assert e_shoff != 0 - assert e_shnum != 0 # don't support > 0xFF00 sections - assert e_shstrndx != 0 - - @dataclass - class Section: - sh_name: int - sh_type: int - sh_flags: int - sh_addr: int - sh_offset: int - sh_size: int - sh_link: int - sh_info: int - sh_addralign: int - sh_entsize: int - - sections = [ - Section(*read("IIPPPPIIPP", e_shoff + i * e_shentsize)) for i in range(e_shnum) - ] - shstr = sections[e_shstrndx] - sec_name_offs = [shstr.sh_offset + s.sh_name for s in sections] - sec_names = [data[offset : data.index(b"\0", offset)] for offset in sec_name_offs] - - symtab_sections = [i for i in range(e_shnum) if sections[i].sh_type == SHT_SYMTAB] - assert len(symtab_sections) == 1 - symtab = sections[symtab_sections[0]] - - section_name = config.diff_section.encode("utf-8") - text_sections = [i for i in range(e_shnum) if sec_names[i] == section_name and sections[i].sh_size != 0] - if len(text_sections) != 1: - return [] - text_section = text_sections[0] - - ret: List[Tuple[int, int, str]] = [] - for s in sections: - if s.sh_type == SHT_REL or s.sh_type == SHT_RELA: - if s.sh_info == text_section: - # Skip section_name -> section_name references - continue - sec_name = sec_names[s.sh_info].decode("latin1") - if sec_name == ".mwcats.text": - # Skip Metrowerks CATS Utility section - continue - sec_base = sections[s.sh_info].sh_offset - for i in range(0, s.sh_size, s.sh_entsize): - if s.sh_type == SHT_REL: - r_offset, r_info = read("PP", s.sh_offset + i) - else: - r_offset, r_info, r_addend = read("PPP", s.sh_offset + i) - - if is_32bit: - r_sym = r_info >> 8 - r_type = r_info & 0xFF - sym_offset = symtab.sh_offset + symtab.sh_entsize * r_sym - st_name, st_value, st_size, st_info, st_other, st_shndx = read( - "IIIBBH", sym_offset - ) - else: - r_sym = r_info >> 32 - r_type = r_info & 0xFFFFFFFF - sym_offset = symtab.sh_offset + symtab.sh_entsize * r_sym - st_name, st_info, st_other, st_shndx, st_value, st_size = read( - "IBBHQQ", sym_offset - ) - if st_shndx == text_section: - if s.sh_type == SHT_REL: - if e_machine == 8 and r_type == 2: # R_MIPS_32 - (r_addend,) = read("I", sec_base + r_offset) - else: - continue - text_offset = (st_value + r_addend) & 0xFFFFFFFF - ret.append((text_offset, r_offset, sec_name)) - return ret - - -def dump_elf( - start: str, - end: Optional[str], - diff_elf_symbol: str, - config: Config, - project: ProjectSettings, -) -> Tuple[str, ObjdumpCommand, ObjdumpCommand]: - if not project.baseimg or not project.myimg: - fail("Missing myimg/baseimg in config.") - if config.base_shift: - fail("--base-shift not compatible with -e") - - start_addr = eval_int(start, "Start address must be an integer expression.") - - if end is not None: - end_addr = eval_int(end, "End address must be an integer expression.") - else: - end_addr = start_addr + config.max_function_size_bytes - - flags1 = [ - f"--start-address={start_addr}", - f"--stop-address={end_addr}", - ] - - if project.disassemble_all: - disassemble_flag = "-D" - else: - disassemble_flag = "-d" - - flags2 = [ - f"--disassemble={diff_elf_symbol}", - ] - - objdump_flags = [disassemble_flag, "-rz", "-j", config.diff_section] - return ( - project.myimg, - (objdump_flags + flags1, project.baseimg, None), - ( - objdump_flags + flags2 + maybe_get_objdump_source_flags(config), - project.myimg, - None, - ), - ) - - -def dump_objfile( - start: str, end: Optional[str], config: Config, project: ProjectSettings -) -> Tuple[str, ObjdumpCommand, ObjdumpCommand]: - if config.base_shift: - fail("--base-shift not compatible with -o") - if end is not None: - fail("end address not supported together with -o") - if start.startswith("0"): - fail("numerical start address not supported with -o; pass a function name") - - objfile, _ = search_map_file(start, project, config) - if not objfile: - fail("Not able to find .o file for function.") - - if config.make: - run_make(objfile, project) - - if not os.path.isfile(objfile): - fail(f"Not able to find .o file for function: {objfile} is not a file.") - - refobjfile = "expected/" + objfile - if not os.path.isfile(refobjfile): - fail(f'Please ensure an OK .o file exists at "{refobjfile}".') - - if project.disassemble_all: - disassemble_flag = "-D" - else: - disassemble_flag = "-d" - - objdump_flags = [disassemble_flag, "-rz", "-j", config.diff_section] - return ( - objfile, - (objdump_flags, refobjfile, start), - (objdump_flags + maybe_get_objdump_source_flags(config), objfile, start), - ) - - -def dump_binary( - start: str, end: Optional[str], config: Config, project: ProjectSettings -) -> Tuple[str, ObjdumpCommand, ObjdumpCommand]: - if not project.baseimg or not project.myimg: - fail("Missing myimg/baseimg in config.") - if config.make: - run_make(project.myimg, project) - start_addr = maybe_eval_int(start) - if start_addr is None: - _, start_addr = search_map_file(start, project, config) - if start_addr is None: - fail("Not able to find function in map file.") - if end is not None: - end_addr = eval_int(end, "End address must be an integer expression.") - else: - end_addr = start_addr + config.max_function_size_bytes - objdump_flags = ["-Dz", "-bbinary"] + ["-EB" if config.arch.big_endian else "-EL"] - flags1 = [ - f"--start-address={start_addr + config.base_shift}", - f"--stop-address={end_addr + config.base_shift}", - ] - flags2 = [f"--start-address={start_addr}", f"--stop-address={end_addr}"] - return ( - project.myimg, - (objdump_flags + flags1, project.baseimg, None), - (objdump_flags + flags2, project.myimg, None), - ) - -# Example: "ldr r4, [pc, #56] ; (4c )" -ARM32_LOAD_POOL_PATTERN = r"(ldr\s+r([0-9]|1[0-3]),\s+\[pc,.*;\s*)(\([a-fA-F0-9]+.*\))" - - -# The base class is a no-op. -class AsmProcessor: - def __init__(self, config: Config) -> None: - self.config = config - - def process_reloc(self, row: str, prev: str) -> str: - return prev - - def normalize(self, mnemonic: str, row: str) -> str: - """This should be called exactly once for each line.""" - arch = self.config.arch - row = self._normalize_arch_specific(mnemonic, row) - if self.config.ignore_large_imms and mnemonic not in arch.branch_instructions: - row = re.sub(self.config.arch.re_large_imm, "", row) - return row - - def _normalize_arch_specific(self, mnemonic: str, row: str) -> str: - return row - - def post_process(self, lines: List["Line"]) -> None: - return - - -class AsmProcessorMIPS(AsmProcessor): - def process_reloc(self, row: str, prev: str) -> str: - arch = self.config.arch - if "R_MIPS_NONE" in row: - # GNU as emits no-op relocations immediately after real ones when - # assembling with -mabi=64. Return without trying to parse 'imm' as an - # integer. - return prev - before, imm, after = parse_relocated_line(prev) - repl = row.split()[-1] - if imm != "0": - # MIPS uses relocations with addends embedded in the code as immediates. - # If there is an immediate, show it as part of the relocation. Ideally - # we'd show this addend in both %lo/%hi, but annoyingly objdump's output - # doesn't include enough information to pair up %lo's and %hi's... - # TODO: handle unambiguous cases where all addends for a symbol are the - # same, or show "+???". - mnemonic = prev.split()[0] - if ( - mnemonic in arch.instructions_with_address_immediates - and not imm.startswith("0x") - ): - imm = "0x" + imm - repl += "+" + imm if int(imm, 0) > 0 else imm - if "R_MIPS_LO16" in row: - repl = f"%lo({repl})" - elif "R_MIPS_HI16" in row: - # Ideally we'd pair up R_MIPS_LO16 and R_MIPS_HI16 to generate a - # correct addend for each, but objdump doesn't give us the order of - # the relocations, so we can't find the right LO16. :( - repl = f"%hi({repl})" - elif "R_MIPS_26" in row: - # Function calls - pass - elif "R_MIPS_PC16" in row: - # Branch to glabel. This gives confusing output, but there's not much - # we can do here. - pass - elif "R_MIPS_GPREL16" in row: - repl = f"%gp_rel({repl})" - else: - assert False, f"unknown relocation type '{row}' for line '{prev}'" - return before + repl + after - - -class AsmProcessorPPC(AsmProcessor): - def process_reloc(self, row: str, prev: str) -> str: - arch = self.config.arch - assert any( - r in row for r in ["R_PPC_REL24", "R_PPC_ADDR16", "R_PPC_EMB_SDA21"] - ), f"unknown relocation type '{row}' for line '{prev}'" - before, imm, after = parse_relocated_line(prev) - repl = row.split()[-1] - if "R_PPC_REL24" in row: - # function calls - pass - elif "R_PPC_ADDR16_HI" in row: - # absolute hi of addr - repl = f"{repl}@h" - elif "R_PPC_ADDR16_HA" in row: - # adjusted hi of addr - repl = f"{repl}@ha" - elif "R_PPC_ADDR16_LO" in row: - # lo of addr - repl = f"{repl}@l" - elif "R_PPC_ADDR16" in row: - # 16-bit absolute addr - if "+0x7" in repl: - # remove the very large addends as they are an artifact of (label-_SDA(2)_BASE_) - # computations and are unimportant in a diff setting. - if int(repl.split("+")[1], 16) > 0x70000000: - repl = repl.split("+")[0] - elif "R_PPC_EMB_SDA21" in row: - # small data area - pass - return before + repl + after - - -class AsmProcessorARM32(AsmProcessor): - def process_reloc(self, row: str, prev: str) -> str: - arch = self.config.arch - before, imm, after = parse_relocated_line(prev) - repl = row.split()[-1] - return before + repl + after - - def _normalize_arch_specific(self, mnemonic: str, row: str) -> str: - if self.config.ignore_addr_diffs: - row = self._normalize_bl(mnemonic, row) - row = self._normalize_data_pool(row) - return row - - def _normalize_bl(self, mnemonic: str, row: str) -> str: - if mnemonic != "bl": - return row - - row, _ = split_off_address(row) - return row + "" - - def _normalize_data_pool(self, row: str) -> str: - pool_match = re.search(ARM32_LOAD_POOL_PATTERN, row) - return pool_match.group(1) if pool_match else row - - def post_process(self, lines: List["Line"]) -> None: - lines_by_line_number = {} - for line in lines: - lines_by_line_number[line.line_num] = line - for line in lines: - if line.data_pool_addr is None: - continue - - # Add data symbol and its address to the line. - line_original = lines_by_line_number[line.data_pool_addr].original - value = line_original.split()[1] - addr = "{:x}".format(line.data_pool_addr) - line.original = line.normalized_original + f"={value} ({addr})" - - -class AsmProcessorAArch64(AsmProcessor): - def __init__(self, config: Config) -> None: - super().__init__(config) - self._adrp_pair_registers: Set[str] = set() - - def _normalize_arch_specific(self, mnemonic: str, row: str) -> str: - if self.config.ignore_addr_diffs: - row = self._normalize_adrp_differences(mnemonic, row) - row = self._normalize_bl(mnemonic, row) - return row - - def _normalize_bl(self, mnemonic: str, row: str) -> str: - if mnemonic != "bl": - return row - - row, _ = split_off_address(row) - return row + "" - - def _normalize_adrp_differences(self, mnemonic: str, row: str) -> str: - """Identifies ADRP + LDR/ADD pairs that are used to access the GOT and - suppresses any immediate differences. - - Whenever an ADRP is seen, the destination register is added to the set of registers - that are part of an ADRP + LDR/ADD pair. Registers are removed from the set as soon - as they are used for an LDR or ADD instruction which completes the pair. - - This method is somewhat crude but should manage to detect most such pairs. - """ - row_parts = row.split("\t", 1) - if mnemonic == "adrp": - self._adrp_pair_registers.add(row_parts[1].strip().split(",")[0]) - row, _ = split_off_address(row) - return row + "" - elif mnemonic == "ldr": - for reg in self._adrp_pair_registers: - # ldr xxx, [reg] - # ldr xxx, [reg, ] - if f", [{reg}" in row_parts[1]: - self._adrp_pair_registers.remove(reg) - return normalize_imms(row, AARCH64_SETTINGS) - elif mnemonic == "add": - for reg in self._adrp_pair_registers: - # add reg, reg, - if row_parts[1].startswith(f"{reg}, {reg}, "): - self._adrp_pair_registers.remove(reg) - return normalize_imms(row, AARCH64_SETTINGS) - - return row - - -@dataclass -class ArchSettings: - name: str - re_int: Pattern[str] - re_comment: Pattern[str] - re_reg: Pattern[str] - re_sprel: Pattern[str] - re_large_imm: Pattern[str] - re_imm: Pattern[str] - re_reloc: Pattern[str] - branch_instructions: Set[str] - instructions_with_address_immediates: Set[str] - forbidden: Set[str] = field(default_factory=lambda: set(string.ascii_letters + "_")) - arch_flags: List[str] = field(default_factory=list) - branch_likely_instructions: Set[str] = field(default_factory=set) - proc: Type[AsmProcessor] = AsmProcessor - big_endian: Optional[bool] = True - delay_slot_instructions: Set[str] = field(default_factory=set) - -MIPS_BRANCH_LIKELY_INSTRUCTIONS = { - "beql", - "bnel", - "beqzl", - "bnezl", - "bgezl", - "bgtzl", - "blezl", - "bltzl", - "bc1tl", - "bc1fl", -} -MIPS_BRANCH_INSTRUCTIONS = MIPS_BRANCH_LIKELY_INSTRUCTIONS.union( - { - "b", - "beq", - "bne", - "beqz", - "bnez", - "bgez", - "bgtz", - "blez", - "bltz", - "bc1t", - "bc1f", - } -) - -ARM32_PREFIXES = {"b", "bl"} -ARM32_CONDS = { - "", - "eq", - "ne", - "cs", - "cc", - "mi", - "pl", - "vs", - "vc", - "hi", - "ls", - "ge", - "lt", - "gt", - "le", - "al", -} -ARM32_SUFFIXES = {"", ".n", ".w"} -ARM32_BRANCH_INSTRUCTIONS = { - f"{prefix}{cond}{suffix}" - for prefix in ARM32_PREFIXES - for cond in ARM32_CONDS - for suffix in ARM32_SUFFIXES -} - -AARCH64_BRANCH_INSTRUCTIONS = { - "b", - "b.eq", - "b.ne", - "b.cs", - "b.hs", - "b.cc", - "b.lo", - "b.mi", - "b.pl", - "b.vs", - "b.vc", - "b.hi", - "b.ls", - "b.ge", - "b.lt", - "b.gt", - "b.le", - "cbz", - "cbnz", - "tbz", - "tbnz", -} - -PPC_BRANCH_INSTRUCTIONS = { - "b", - "beq", - "beq+", - "beq-", - "bne", - "bne+", - "bne-", - "blt", - "blt+", - "blt-", - "ble", - "ble+", - "ble-", - "bdnz", - "bdnz+", - "bdnz-", - "bge", - "bge+", - "bge-", - "bgt", - "bgt+", - "bgt-", -} - -MIPS_SETTINGS = ArchSettings( - name="mips", - re_int=re.compile(r"[0-9]+"), - re_comment=re.compile(r"<.*?>"), - re_reg=re.compile( - r"\$?\b(a[0-7]|t[0-9]|s[0-8]|at|v[01]|f[12]?[0-9]|f3[01]|kt?[01]|fp|ra|zero)\b" - ), - re_sprel=re.compile(r"(?<=,)([0-9]+|0x[0-9a-f]+)\(sp\)"), - re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"), - re_imm=re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi)\([^)]*\)"), - re_reloc=re.compile(r"R_MIPS_"), - arch_flags=["-m", "mips:4300"], - branch_likely_instructions=MIPS_BRANCH_LIKELY_INSTRUCTIONS, - branch_instructions=MIPS_BRANCH_INSTRUCTIONS, - instructions_with_address_immediates=MIPS_BRANCH_INSTRUCTIONS.union({"jal", "j"}), - delay_slot_instructions=MIPS_BRANCH_INSTRUCTIONS.union({"j", "jal", "jr", "jalr"}), - proc=AsmProcessorMIPS, -) - -MIPSEL_SETTINGS = replace(MIPS_SETTINGS, name="mipsel", big_endian=False) - -ARM32_SETTINGS = ArchSettings( - name="arm32", - re_int=re.compile(r"[0-9]+"), - re_comment=re.compile(r"(<.*?>|//.*$)"), - # Includes: - # - General purpose registers: r0..13 - # - Frame pointer registers: lr (r14), pc (r15) - # - VFP/NEON registers: s0..31, d0..31, q0..15, fpscr, fpexc, fpsid - # SP should not be in this list. - re_reg=re.compile( - r"\$?\b([rq][0-9]|[rq]1[0-5]|pc|lr|[ds][12]?[0-9]|[ds]3[01]|fp(scr|exc|sid))\b" - ), - re_sprel=re.compile(r"sp, #-?(0x[0-9a-fA-F]+|[0-9]+)\b"), - re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"), - re_imm=re.compile(r"(?|//.*$)"), - # GPRs and FP registers: X0-X30, W0-W30, [BHSDVQ]0..31 - # (FP registers may be followed by data width and number of elements, e.g. V0.4S) - # The zero registers and SP should not be in this list. - re_reg=re.compile(r"\$?\b([bhsdvq]([12]?[0-9]|3[01])(\.\d\d?[bhsdvq])?|[xw][12]?[0-9]|[xw]30)\b"), - re_sprel=re.compile(r"sp, #-?(0x[0-9a-fA-F]+|[0-9]+)\b"), - re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"), - re_imm=re.compile(r"(?|//.*$)"), - re_reg=re.compile(r"\$?\b([rf][0-9]+)\b"), - re_sprel=re.compile(r"(?<=,)(-?[0-9]+|-?0x[0-9a-f]+)\(r1\)"), - re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"), - re_imm=re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(r1)|[^@]*@(ha|h|lo)"), - re_reloc=re.compile(r"R_PPC_"), - branch_instructions=PPC_BRANCH_INSTRUCTIONS, - instructions_with_address_immediates=PPC_BRANCH_INSTRUCTIONS.union({"bl"}), - proc=AsmProcessorPPC, -) - -ARCH_SETTINGS = [ - MIPS_SETTINGS, - MIPSEL_SETTINGS, - ARM32_SETTINGS, - ARMEL_SETTINGS, - AARCH64_SETTINGS, - PPC_SETTINGS, -] - - -def hexify_int(row: str, pat: Match[str], arch: ArchSettings) -> str: - full = pat.group(0) - if len(full) <= 1: - # leave one-digit ints alone - return full - start, end = pat.span() - if start and row[start - 1] in arch.forbidden: - return full - if end < len(row) and row[end] in arch.forbidden: - return full - return hex(int(full)) - - -def parse_relocated_line(line: str) -> Tuple[str, str, str]: - for c in ",\t ": - if c in line: - ind2 = line.rindex(c) - break - else: - raise Exception(f"failed to parse relocated line: {line}") - before = line[: ind2 + 1] - after = line[ind2 + 1 :] - ind2 = after.find("(") - if ind2 == -1: - imm, after = after, "" - else: - imm, after = after[:ind2], after[ind2:] - if imm == "0x0": - imm = "0" - return before, imm, after - - -def pad_mnemonic(line: str) -> str: - if "\t" not in line: - return line - mn, args = line.split("\t", 1) - return f"{mn:<7s} {args}" - - -@dataclass -class Line: - mnemonic: str - diff_row: str - original: str - normalized_original: str - scorable_line: str - line_num: Optional[int] = None - branch_target: Optional[int] = None - data_pool_addr: Optional[int] = None - source_filename: Optional[str] = None - source_line_num: Optional[int] = None - source_lines: List[str] = field(default_factory=list) - comment: Optional[str] = None - - -def process(dump: str, config: Config) -> List[Line]: - arch = config.arch - processor = arch.proc(config) - skip_next = False - source_lines = [] - source_filename = None - source_line_num = None - - i = 0 - num_instr = 0 - data_refs: Dict[int, Dict[str, List[int]]] = defaultdict(lambda: defaultdict(list)) - output: List[Line] = [] - stop_after_delay_slot = False - lines = dump.split("\n") - while i < len(lines): - row = lines[i] - i += 1 - - if not row: - continue - - if re.match(r"^[0-9a-f]+ <.*>:$", row): - continue - - if row.startswith("DATAREF"): - parts = row.split(" ", 3) - text_offset = int(parts[1]) - from_offset = int(parts[2]) - from_section = parts[3] - data_refs[text_offset][from_section].append(from_offset) - continue - - if config.diff_obj and num_instr >= config.max_function_size_lines: - output.append( - Line( - mnemonic="...", - diff_row="...", - original="...", - normalized_original="...", - scorable_line="...", - ) - ) - break - - if not re.match(r"^\s+[0-9a-f]+:\s+", row): - # This regex is conservative, and assumes the file path does not contain "weird" - # characters like colons, tabs, or angle brackets. - if re.match( - r"^[^ \t<>:][^\t<>:]*:[0-9]+( \(discriminator [0-9]+\))?$", row - ): - source_filename, _, tail = row.rpartition(":") - source_line_num = int(tail.partition(" ")[0]) - source_lines.append(row) - continue - - # If the instructions loads a data pool symbol, extract the address of - # the symbol. - data_pool_addr = None - pool_match = re.search(ARM32_LOAD_POOL_PATTERN, row) - if pool_match: - offset = pool_match.group(3).split(" ")[0][1:] - data_pool_addr = int(offset, 16) - - m_comment = re.search(arch.re_comment, row) - comment = m_comment[0] if m_comment else None - row = re.sub(arch.re_comment, "", row) - line_num_str = row.split(":")[0] - row = row.rstrip() - tabs = row.split("\t") - row = "\t".join(tabs[2:]) - line_num = eval_line_num(line_num_str.strip()) - - if line_num in data_refs: - refs = data_refs[line_num] - ref_str = "; ".join( - section_name + "+" + ",".join(hex(off) for off in offs) - for section_name, offs in refs.items() - ) - output.append( - Line( - mnemonic="", - diff_row="", - original=ref_str, - normalized_original=ref_str, - scorable_line="", - ) - ) - - if "\t" in row: - row_parts = row.split("\t", 1) - else: - # powerpc-eabi-objdump doesn't use tabs - row_parts = [part.lstrip() for part in row.split(" ", 1)] - mnemonic = row_parts[0].strip() - - if mnemonic not in arch.instructions_with_address_immediates: - row = re.sub(arch.re_int, lambda m: hexify_int(row, m, arch), row) - - # Let 'original' be 'row' with relocations applied, while we continue - # transforming 'row' into a coarser version that ignores registers and - # immediates. - original = row - - while i < len(lines): - reloc_row = lines[i] - if re.search(arch.re_reloc, reloc_row): - original = processor.process_reloc(reloc_row, original) - else: - break - i += 1 - - normalized_original = processor.normalize(mnemonic, original) - - scorable_line = normalized_original - if not config.score_stack_differences: - scorable_line = re.sub(arch.re_sprel, "addr(sp)", scorable_line) - if mnemonic in arch.branch_instructions: - # Replace the final argument with "" - scorable_line = re.sub(r"[^, \t]+$", "", scorable_line) - - if skip_next: - skip_next = False - row = "" - mnemonic = "" - scorable_line = "" - if mnemonic in arch.branch_likely_instructions: - skip_next = True - - row = re.sub(arch.re_reg, "", row) - row = re.sub(arch.re_sprel, "addr(sp)", row) - row_with_imm = row - if mnemonic in arch.instructions_with_address_immediates: - row = row.strip() - row, _ = split_off_address(row) - row += "" - else: - row = normalize_imms(row, arch) - - branch_target = None - if mnemonic in arch.branch_instructions: - branch_target = int(row_parts[1].strip().split(",")[-1], 16) - if mnemonic in arch.branch_likely_instructions: - branch_target -= 4 - - output.append( - Line( - mnemonic=mnemonic, - diff_row=row, - original=original, - normalized_original=normalized_original, - scorable_line=scorable_line, - line_num=line_num, - branch_target=branch_target, - data_pool_addr=data_pool_addr, - source_filename=source_filename, - source_line_num=source_line_num, - source_lines=source_lines, - comment=comment, - ) - ) - num_instr += 1 - source_lines = [] - - if config.stop_jrra and mnemonic == "jr" and row_parts[1].strip() == "ra": - stop_after_delay_slot = True - elif stop_after_delay_slot: - break - - processor.post_process(output) - return output - - -def normalize_imms(row: str, arch: ArchSettings) -> str: - return re.sub(arch.re_imm, "", row) - - -def normalize_stack(row: str, arch: ArchSettings) -> str: - return re.sub(arch.re_sprel, "addr(sp)", row) - - -def imm_matches_everything(row: str, arch: ArchSettings) -> bool: - # (this should probably be arch-specific) - return "(." in row - - -def split_off_address(line: str) -> Tuple[str, str]: - """Split e.g. 'beqz $r0,1f0' into 'beqz $r0,' and '1f0'.""" - parts = line.split(",") - if len(parts) < 2: - parts = line.split(None, 1) - off = len(line) - len(parts[-1]) - return line[:off], line[off:] - - -def diff_sequences_difflib( - seq1: List[str], seq2: List[str] -) -> List[Tuple[str, int, int, int, int]]: - differ = difflib.SequenceMatcher(a=seq1, b=seq2, autojunk=False) - return differ.get_opcodes() - - -def diff_sequences( - seq1: List[str], seq2: List[str], algorithm: str -) -> List[Tuple[str, int, int, int, int]]: - if ( - algorithm != "levenshtein" - or len(seq1) * len(seq2) > 4 * 10 ** 8 - or len(seq1) + len(seq2) >= 0x110000 - ): - return diff_sequences_difflib(seq1, seq2) - - # The Levenshtein library assumes that we compare strings, not lists. Convert. - # (Per the check above we know we have fewer than 0x110000 unique elements, so chr() works.) - remapping: Dict[str, str] = {} - - def remap(seq: List[str]) -> str: - seq = seq[:] - for i in range(len(seq)): - val = remapping.get(seq[i]) - if val is None: - val = chr(len(remapping)) - remapping[seq[i]] = val - seq[i] = val - return "".join(seq) - - rem1 = remap(seq1) - rem2 = remap(seq2) - import Levenshtein - - ret: List[Tuple[str, int, int, int, int]] = Levenshtein.opcodes(rem1, rem2) - return ret - - -def diff_lines( - lines1: List[Line], - lines2: List[Line], - algorithm: str, -) -> List[Tuple[Optional[Line], Optional[Line]]]: - ret = [] - for (tag, i1, i2, j1, j2) in diff_sequences( - [line.mnemonic for line in lines1], - [line.mnemonic for line in lines2], - algorithm, - ): - for line1, line2 in itertools.zip_longest(lines1[i1:i2], lines2[j1:j2]): - if tag == "replace": - if line1 is None: - tag = "insert" - elif line2 is None: - tag = "delete" - elif tag == "insert": - assert line1 is None - elif tag == "delete": - assert line2 is None - ret.append((line1, line2)) - - return ret - - -def score_diff_lines( - lines: List[Tuple[Optional[Line], Optional[Line]]], config: Config -) -> int: - # This logic is copied from `scorer.py` from the decomp permuter project - # https://github.com/simonlindholm/decomp-permuter/blob/main/src/scorer.py - score = 0 - deletions = [] - insertions = [] - - def lo_hi_match(old: str, new: str) -> bool: - # TODO: Make this arch-independent, like `imm_matches_everything()` - old_lo = old.find("%lo") - old_hi = old.find("%hi") - new_lo = new.find("%lo") - new_hi = new.find("%hi") - - if old_lo != -1 and new_lo != -1: - old_idx = old_lo - new_idx = new_lo - elif old_hi != -1 and new_hi != -1: - old_idx = old_hi - new_idx = new_hi - else: - return False - - if old[:old_idx] != new[:new_idx]: - return False - - old_inner = old[old_idx + 4 : -1] - new_inner = new[new_idx + 4 : -1] - return old_inner.startswith(".") or new_inner.startswith(".") - - def diff_sameline(old: str, new: str) -> None: - nonlocal score - if old == new: - return - - if lo_hi_match(old, new): - return - - ignore_last_field = False - if config.score_stack_differences: - oldsp = re.search(config.arch.re_sprel, old) - newsp = re.search(config.arch.re_sprel, new) - if oldsp and newsp: - oldrel = int(oldsp.group(1) or "0", 0) - newrel = int(newsp.group(1) or "0", 0) - score += abs(oldrel - newrel) * config.penalty_stackdiff - ignore_last_field = True - - # Probably regalloc difference, or signed vs unsigned - - # Compare each field in order - newfields, oldfields = new.split(","), old.split(",") - if ignore_last_field: - newfields = newfields[:-1] - oldfields = oldfields[:-1] - for nf, of in zip(newfields, oldfields): - if nf != of: - score += config.penalty_regalloc - # Penalize any extra fields - score += abs(len(newfields) - len(oldfields)) * config.penalty_regalloc - - def diff_insert(line: str) -> None: - # Reordering or totally different codegen. - # Defer this until later when we can tell. - insertions.append(line) - - def diff_delete(line: str) -> None: - deletions.append(line) - - # Find the end of the last long streak of matching mnemonics, if it looks - # like the objdump output was truncated. This is used to skip scoring - # misaligned lines at the end of the diff. - last_mismatch = -1 - max_index = None - lines_were_truncated = False - for index, (line1, line2) in enumerate(lines): - if (line1 and line1.original == "...") or (line2 and line2.original == "..."): - lines_were_truncated = True - if line1 and line2 and line1.mnemonic == line2.mnemonic: - if index - last_mismatch >= 50: - max_index = index - else: - last_mismatch = index - if not lines_were_truncated: - max_index = None - - for index, (line1, line2) in enumerate(lines): - if max_index is not None and index > max_index: - break - if line1 and line2 and line1.mnemonic == line2.mnemonic: - diff_sameline(line1.scorable_line, line2.scorable_line) - else: - if line1: - diff_delete(line1.scorable_line) - if line2: - diff_insert(line2.scorable_line) - - insertions_co = Counter(insertions) - deletions_co = Counter(deletions) - for item in insertions_co + deletions_co: - ins = insertions_co[item] - dels = deletions_co[item] - common = min(ins, dels) - score += ( - (ins - common) * config.penalty_insertion - + (dels - common) * config.penalty_deletion - + config.penalty_reordering * common - ) - - return score - - -@dataclass(frozen=True) -class OutputLine: - base: Optional[Text] = field(compare=False) - fmt2: Text = field(compare=False) - key2: Optional[str] - boring: bool = field(compare=False) - is_data_ref: bool = field(compare=False) - line1: Optional[Line] = field(compare=False) - line2: Optional[Line] = field(compare=False) - - -@dataclass(frozen=True) -class Diff: - lines: List[OutputLine] - score: int - max_score: int - - -def trim_nops(lines: List[Line], arch: ArchSettings) -> List[Line]: - lines = lines[:] - while lines and lines[-1].mnemonic == "nop" and (len(lines) == 1 or lines[-2].mnemonic not in arch.delay_slot_instructions): - lines.pop() - return lines - -def do_diff(lines1: List[Line], lines2: List[Line], config: Config) -> Diff: - if config.show_source: - import cxxfilt - arch = config.arch - fmt = config.formatter - output: List[OutputLine] = [] - - sc1 = symbol_formatter("base-reg", 0) - sc2 = symbol_formatter("my-reg", 0) - sc3 = symbol_formatter("base-stack", 4) - sc4 = symbol_formatter("my-stack", 4) - sc5 = symbol_formatter("base-branch", 0) - sc6 = symbol_formatter("my-branch", 0) - bts1: Set[int] = set() - bts2: Set[int] = set() - - if config.show_branches: - for (lines, btset, sc) in [ - (lines1, bts1, sc5), - (lines2, bts2, sc6), - ]: - for line in lines: - bt = line.branch_target - if bt is not None: - btset.add(bt) - sc(str(bt)) - - lines1 = trim_nops(lines1, arch) - lines2 = trim_nops(lines2, arch) - - diffed_lines = diff_lines(lines1, lines2, config.algorithm) - score = score_diff_lines(diffed_lines, config) - max_score = len(lines1) * config.penalty_deletion - - line_num_base = -1 - line_num_offset = 0 - line_num_2to1 = {} - for (line1, line2) in diffed_lines: - if line1 is not None and line1.line_num is not None: - line_num_base = line1.line_num - line_num_offset = 0 - else: - line_num_offset += 1 - if line2 is not None and line2.line_num is not None: - line_num_2to1[line2.line_num] = (line_num_base, line_num_offset) - - for (line1, line2) in diffed_lines: - line_color1 = line_color2 = sym_color = BasicFormat.NONE - line_prefix = " " - is_data_ref = False - out1 = Text() if not line1 else Text(pad_mnemonic(line1.original)) - out2 = Text() if not line2 else Text(pad_mnemonic(line2.original)) - if line1 and line2 and line1.diff_row == line2.diff_row: - if line1.diff_row == "": - if line1.normalized_original != line2.normalized_original: - line_prefix = "i" - sym_color = BasicFormat.DIFF_CHANGE - out1 = out1.reformat(sym_color) - out2 = out2.reformat(sym_color) - is_data_ref = True - elif ( - line1.normalized_original == line2.normalized_original - and line2.branch_target is None - ): - # Fast path: no coloring needed. We don't include branch instructions - # in this case because we need to check that their targets line up in - # the diff, and don't just happen to have the are the same address - # by accident. - pass - elif line1.diff_row == "": - # Don't draw attention to differing branch-likely delay slots: they - # typically mirror the branch destination - 1 so the real difference - # is elsewhere. Still, do mark them as different to avoid confusion. - # No need to consider branches because delay slots can't branch. - out1 = out1.reformat(BasicFormat.DELAY_SLOT) - out2 = out2.reformat(BasicFormat.DELAY_SLOT) - else: - mnemonic = line1.original.split()[0] - branchless1, address1 = out1.plain(), "" - branchless2, address2 = out2.plain(), "" - if mnemonic in arch.instructions_with_address_immediates: - branchless1, address1 = split_off_address(branchless1) - branchless2, address2 = split_off_address(branchless2) - - out1 = Text(branchless1) - out2 = Text(branchless2) - out1, out2 = format_fields( - arch.re_imm, out1, out2, lambda _: BasicFormat.IMMEDIATE - ) - - if line2.branch_target is not None: - target = line2.branch_target - line2_target = line_num_2to1.get(line2.branch_target) - if line2_target is None: - # If the target is outside the disassembly, extrapolate. - # This only matters near the bottom. - assert line2.line_num is not None - line2_line = line_num_2to1[line2.line_num] - line2_target = (line2_line[0] + (target - line2.line_num), 0) - - # Set the key for three-way diffing to a normalized version. - norm2, norm_branch2 = split_off_address(line2.normalized_original) - if norm_branch2 != "": - line2.normalized_original = norm2 + str(line2_target) - same_target = line2_target == (line1.branch_target, 0) - else: - # Do a naive comparison for non-branches (e.g. function calls). - same_target = address1 == address2 - - if normalize_imms(branchless1, arch) == normalize_imms( - branchless2, arch - ): - if imm_matches_everything(branchless2, arch): - # ignore differences due to %lo(.rodata + ...) vs symbol - out1 = out1.reformat(BasicFormat.NONE) - out2 = out2.reformat(BasicFormat.NONE) - elif line2.branch_target is not None and same_target: - # same-target branch, don't color - pass - else: - # must have an imm difference (or else we would have hit the - # fast path) - sym_color = BasicFormat.IMMEDIATE - line_prefix = "i" - else: - out1, out2 = format_fields(arch.re_sprel, out1, out2, sc3, sc4) - if normalize_stack(branchless1, arch) == normalize_stack( - branchless2, arch - ): - # only stack differences (luckily stack and imm - # differences can't be combined in MIPS, so we - # don't have to think about that case) - sym_color = BasicFormat.STACK - line_prefix = "s" - else: - # reg differences and maybe imm as well - out1, out2 = format_fields(arch.re_reg, out1, out2, sc1, sc2) - line_color1 = line_color2 = sym_color = BasicFormat.REGISTER - line_prefix = "r" - - if same_target: - address_imm_fmt = BasicFormat.NONE - else: - address_imm_fmt = BasicFormat.IMMEDIATE - out1 += Text(address1, address_imm_fmt) - out2 += Text(address2, address_imm_fmt) - elif line1 and line2: - line_prefix = "|" - line_color1 = line_color2 = sym_color = BasicFormat.DIFF_CHANGE - out1 = out1.reformat(line_color1) - out2 = out2.reformat(line_color2) - elif line1: - line_prefix = "<" - line_color1 = sym_color = BasicFormat.DIFF_REMOVE - out1 = out1.reformat(line_color1) - out2 = Text() - elif line2: - line_prefix = ">" - line_color2 = sym_color = BasicFormat.DIFF_ADD - out1 = Text() - out2 = out2.reformat(line_color2) - - if config.show_source and line2 and line2.comment: - out2 += f" {line2.comment}" - - def format_part( - out: Text, - line: Optional[Line], - line_color: Format, - btset: Set[int], - sc: FormatFunction, - ) -> Optional[Text]: - if line is None: - return None - if line.line_num is None: - return out - in_arrow = Text(" ") - out_arrow = Text() - if config.show_branches: - if line.line_num in btset: - in_arrow = Text("~>", sc(str(line.line_num))) - if line.branch_target is not None: - out_arrow = " " + Text("~>", sc(str(line.branch_target))) - formatted_line_num = Text(hex(line.line_num)[2:] + ":", line_color) - return formatted_line_num + " " + in_arrow + " " + out + out_arrow - - part1 = format_part(out1, line1, line_color1, bts1, sc5) - part2 = format_part(out2, line2, line_color2, bts2, sc6) - - if config.show_source and line2: - for source_line in line2.source_lines: - line_format = BasicFormat.SOURCE_OTHER - if config.source_old_binutils: - if source_line and re.fullmatch(".*\.c(?:pp)?:\d+", source_line): - line_format = BasicFormat.SOURCE_FILENAME - elif source_line and source_line.endswith("():"): - line_format = BasicFormat.SOURCE_FUNCTION - try: - source_line = cxxfilt.demangle( - source_line[:-3], external_only=False - ) - except: - pass - else: - # File names and function names - if source_line and source_line[0] != "│": - line_format = BasicFormat.SOURCE_FILENAME - # Function names - if source_line.endswith("():"): - line_format = BasicFormat.SOURCE_FUNCTION - try: - source_line = cxxfilt.demangle( - source_line[:-3], external_only=False - ) - except: - pass - padding = " " * 7 if config.show_line_numbers else " " * 2 - output.append( - OutputLine( - base=None, - fmt2=padding + Text(source_line, line_format), - key2=source_line, - boring=True, - is_data_ref=False, - line1=None, - line2=None, - ) - ) - - key2 = line2.normalized_original if line2 else None - boring = False - if line_prefix == " ": - boring = True - elif config.compress and config.compress.same_instr and line_prefix in "irs": - boring = True - - if config.show_line_numbers: - if line2 and line2.source_line_num is not None: - num_color = ( - BasicFormat.SOURCE_LINE_NUM - if sym_color == BasicFormat.NONE - else sym_color - ) - num2 = Text(f"{line2.source_line_num:5}", num_color) - else: - num2 = Text(" " * 5) - else: - num2 = Text() - - fmt2 = Text(line_prefix, sym_color) + num2 + " " + (part2 or Text()) - - output.append( - OutputLine( - base=part1, - fmt2=fmt2, - key2=key2, - boring=boring, - is_data_ref=is_data_ref, - line1=line1, - line2=line2, - ) - ) - - output = output[config.skip_lines :] - return Diff(lines=output, score=score, max_score=max_score) - - -def chunk_diff_lines( - diff: List[OutputLine], -) -> List[Union[List[OutputLine], OutputLine]]: - """Chunk a diff into an alternating list like A B A B ... A, where: - * A is a List[OutputLine] of insertions, - * B is a single non-insertion OutputLine, with .base != None.""" - cur_right: List[OutputLine] = [] - chunks: List[Union[List[OutputLine], OutputLine]] = [] - for output_line in diff: - if output_line.base is not None: - chunks.append(cur_right) - chunks.append(output_line) - cur_right = [] - else: - cur_right.append(output_line) - chunks.append(cur_right) - return chunks - - -def compress_matching( - li: List[Tuple[OutputLine, ...]], context: int -) -> List[Tuple[OutputLine, ...]]: - ret: List[Tuple[OutputLine, ...]] = [] - matching_streak: List[Tuple[OutputLine, ...]] = [] - context = max(context, 0) - - def flush_matching() -> None: - if len(matching_streak) <= 2 * context + 1: - ret.extend(matching_streak) - else: - ret.extend(matching_streak[:context]) - skipped = len(matching_streak) - 2 * context - filler = OutputLine( - base=Text(f"<{skipped} lines>", BasicFormat.SOURCE_OTHER), - fmt2=Text(), - key2=None, - boring=False, - is_data_ref=False, - line1=None, - line2=None, - ) - columns = len(matching_streak[0]) - ret.append(tuple([filler] * columns)) - if context > 0: - ret.extend(matching_streak[-context:]) - matching_streak.clear() - - for line in li: - if line[0].boring: - matching_streak.append(line) - else: - flush_matching() - ret.append(line) - - flush_matching() - return ret - - -def align_diffs( - old_diff: Diff, new_diff: Diff, config: Config -) -> Tuple[TableMetadata, List[Tuple[OutputLine, ...]]]: - meta: TableMetadata - diff_lines: List[Tuple[OutputLine, ...]] - padding = " " * 7 if config.show_line_numbers else " " * 2 - - if config.threeway: - meta = TableMetadata( - headers=( - Text("TARGET"), - Text(f"{padding}CURRENT ({new_diff.score})"), - Text(f"{padding}PREVIOUS ({old_diff.score})"), - ), - current_score=new_diff.score, - max_score=new_diff.max_score, - previous_score=old_diff.score, - ) - old_chunks = chunk_diff_lines(old_diff.lines) - new_chunks = chunk_diff_lines(new_diff.lines) - diff_lines = [] - empty = OutputLine(Text(), Text(), None, True, False, None, None) - assert len(old_chunks) == len(new_chunks), "same target" - for old_chunk, new_chunk in zip(old_chunks, new_chunks): - if isinstance(old_chunk, list): - assert isinstance(new_chunk, list) - if not old_chunk and not new_chunk: - # Most of the time lines sync up without insertions/deletions, - # and there's no interdiffing to be done. - continue - differ = difflib.SequenceMatcher( - a=old_chunk, b=new_chunk, autojunk=False - ) - for (tag, i1, i2, j1, j2) in differ.get_opcodes(): - if tag in ["equal", "replace"]: - for i, j in zip(range(i1, i2), range(j1, j2)): - diff_lines.append((empty, new_chunk[j], old_chunk[i])) - if tag in ["insert", "replace"]: - for j in range(j1 + i2 - i1, j2): - diff_lines.append((empty, new_chunk[j], empty)) - if tag in ["delete", "replace"]: - for i in range(i1 + j2 - j1, i2): - diff_lines.append((empty, empty, old_chunk[i])) - else: - assert isinstance(new_chunk, OutputLine) - # old_chunk.base and new_chunk.base have the same text since - # both diffs are based on the same target, but they might - # differ in color. Use the new version. - diff_lines.append((new_chunk, new_chunk, old_chunk)) - diff_lines = [ - (base, new, old if old != new else empty) for base, new, old in diff_lines - ] - else: - meta = TableMetadata( - headers=( - Text("TARGET"), - Text(f"{padding}CURRENT ({new_diff.score})"), - ), - current_score=new_diff.score, - max_score=new_diff.max_score, - previous_score=None, - ) - diff_lines = [(line, line) for line in new_diff.lines] - if config.compress: - diff_lines = compress_matching(diff_lines, config.compress.context) - return meta, diff_lines - - -def debounced_fs_watch( - targets: List[str], - outq: "queue.Queue[Optional[float]]", - config: Config, - project: ProjectSettings, -) -> None: - import watchdog.events - import watchdog.observers - - class WatchEventHandler(watchdog.events.FileSystemEventHandler): - def __init__( - self, queue: "queue.Queue[float]", file_targets: List[str] - ) -> None: - self.queue = queue - self.file_targets = file_targets - - def on_modified(self, ev: object) -> None: - if isinstance(ev, watchdog.events.FileModifiedEvent): - self.changed(ev.src_path) - - def on_moved(self, ev: object) -> None: - if isinstance(ev, watchdog.events.FileMovedEvent): - self.changed(ev.dest_path) - - def should_notify(self, path: str) -> bool: - for target in self.file_targets: - if os.path.normpath(path) == target: - return True - if config.make and any( - path.endswith(suffix) for suffix in project.source_extensions - ): - return True - return False - - def changed(self, path: str) -> None: - if self.should_notify(path): - self.queue.put(time.time()) - - def debounce_thread() -> NoReturn: - listenq: "queue.Queue[float]" = queue.Queue() - file_targets: List[str] = [] - event_handler = WatchEventHandler(listenq, file_targets) - observer = watchdog.observers.Observer() - observed = set() - for target in targets: - if os.path.isdir(target): - observer.schedule(event_handler, target, recursive=True) - else: - file_targets.append(os.path.normpath(target)) - target = os.path.dirname(target) or "." - if target not in observed: - observed.add(target) - observer.schedule(event_handler, target) - observer.start() - while True: - t = listenq.get() - more = True - while more: - delay = t + DEBOUNCE_DELAY - time.time() - if delay > 0: - time.sleep(delay) - # consume entire queue - more = False - try: - while True: - t = listenq.get(block=False) - more = True - except queue.Empty: - pass - outq.put(t) - - th = threading.Thread(target=debounce_thread, daemon=True) - th.start() - - -class Display: - basedump: str - mydump: str - last_refresh_key: object - config: Config - emsg: Optional[str] - last_diff_output: Optional[Diff] - pending_update: Optional[str] - ready_queue: "queue.Queue[None]" - watch_queue: "queue.Queue[Optional[float]]" - less_proc: "Optional[subprocess.Popen[bytes]]" - - def __init__(self, basedump: str, mydump: str, config: Config) -> None: - self.config = config - self.base_lines = process(basedump, config) - self.mydump = mydump - self.emsg = None - self.last_refresh_key = None - self.last_diff_output = None - - def run_diff(self) -> Tuple[str, object]: - if self.emsg is not None: - return (self.emsg, self.emsg) - - my_lines = process(self.mydump, self.config) - diff_output = do_diff(self.base_lines, my_lines, self.config) - last_diff_output = self.last_diff_output or diff_output - if self.config.threeway != "base" or not self.last_diff_output: - self.last_diff_output = diff_output - - meta, diff_lines = align_diffs(last_diff_output, diff_output, self.config) - output = self.config.formatter.table(meta, diff_lines) - refresh_key = ( - [line.key2 for line in diff_output.lines], - diff_output.score, - ) - return (output, refresh_key) - - def run_less( - self, output: str - ) -> "Tuple[subprocess.Popen[bytes], subprocess.Popen[bytes]]": - # Pipe the output through 'tail' and only then to less, to ensure the - # write call doesn't block. ('tail' has to buffer all its input before - # it starts writing.) This also means we don't have to deal with pipe - # closure errors. - buffer_proc = subprocess.Popen( - BUFFER_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE - ) - less_proc = subprocess.Popen(LESS_CMD, stdin=buffer_proc.stdout) - assert buffer_proc.stdin - assert buffer_proc.stdout - buffer_proc.stdin.write(output.encode()) - buffer_proc.stdin.close() - buffer_proc.stdout.close() - return (buffer_proc, less_proc) - - def run_sync(self) -> None: - output, _ = self.run_diff() - proca, procb = self.run_less(output) - procb.wait() - proca.wait() - - def run_async(self, watch_queue: "queue.Queue[Optional[float]]") -> None: - self.watch_queue = watch_queue - self.ready_queue = queue.Queue() - self.pending_update = None - output, refresh_key = self.run_diff() - self.last_refresh_key = refresh_key - dthread = threading.Thread(target=self.display_thread, args=(output,)) - dthread.start() - self.ready_queue.get() - - def display_thread(self, initial_output: str) -> None: - proca, procb = self.run_less(initial_output) - self.less_proc = procb - self.ready_queue.put(None) - while True: - ret = procb.wait() - proca.wait() - self.less_proc = None - if ret != 0: - # fix the terminal - os.system("tput reset") - if ret != 0 and self.pending_update is not None: - # killed by program with the intent to refresh - output = self.pending_update - self.pending_update = None - proca, procb = self.run_less(output) - self.less_proc = procb - self.ready_queue.put(None) - else: - # terminated by user, or killed - self.watch_queue.put(None) - self.ready_queue.put(None) - break - - def progress(self, msg: str) -> None: - # Write message to top-left corner - sys.stdout.write("\x1b7\x1b[1;1f{}\x1b8".format(msg + " ")) - sys.stdout.flush() - - def update(self, text: str, error: bool) -> None: - if not error and not self.emsg and text == self.mydump: - self.progress("Unchanged. ") - return - if not error: - self.mydump = text - self.emsg = None - else: - self.emsg = text - output, refresh_key = self.run_diff() - if refresh_key == self.last_refresh_key: - self.progress("Unchanged. ") - return - self.last_refresh_key = refresh_key - self.pending_update = output - if not self.less_proc: - return - self.less_proc.kill() - self.ready_queue.get() - - def terminate(self) -> None: - if not self.less_proc: - return - self.less_proc.kill() - self.ready_queue.get() - - -def main() -> None: - args = parser.parse_args() - - # Apply project-specific configuration. - settings: Dict[str, Any] = {} - diff_settings.apply(settings, args) # type: ignore - project = create_project_settings(settings) - - try: - config = create_config(args, project) - except ValueError as e: - fail(str(e)) - - if config.algorithm == "levenshtein": - try: - import Levenshtein - except ModuleNotFoundError as e: - fail(MISSING_PREREQUISITES.format(e.name)) - - if config.show_source: - try: - import cxxfilt - except ModuleNotFoundError as e: - fail(MISSING_PREREQUISITES.format(e.name)) - - if config.threeway and not args.watch: - fail("Threeway diffing requires -w.") - - if args.diff_elf_symbol: - make_target, basecmd, mycmd = dump_elf( - args.start, args.end, args.diff_elf_symbol, config, project - ) - elif config.diff_obj: - make_target, basecmd, mycmd = dump_objfile( - args.start, args.end, config, project - ) - else: - make_target, basecmd, mycmd = dump_binary(args.start, args.end, config, project) - - map_build_target_fn = getattr(diff_settings, "map_build_target", None) - if map_build_target_fn: - make_target = map_build_target_fn(make_target=make_target) - - if args.write_asm is not None: - mydump = run_objdump(mycmd, config, project) - with open(args.write_asm, "w") as f: - f.write(mydump) - print(f"Wrote assembly to {args.write_asm}.") - sys.exit(0) - - if args.base_asm is not None: - with open(args.base_asm) as f: - basedump = f.read() - else: - basedump = run_objdump(basecmd, config, project) - - mydump = run_objdump(mycmd, config, project) - - display = Display(basedump, mydump, config) - - if args.no_pager or args.format in ("html", "json"): - print(display.run_diff()[0]) - elif not args.watch: - display.run_sync() - else: - if not args.make: - yn = input( - "Warning: watch-mode (-w) enabled without auto-make (-m). " - "You will have to run make manually. Ok? (Y/n) " - ) - if yn.lower() == "n": - return - if args.make: - watch_sources = None - watch_sources_for_target_fn = getattr( - diff_settings, "watch_sources_for_target", None - ) - if watch_sources_for_target_fn: - watch_sources = watch_sources_for_target_fn(make_target) - watch_sources = watch_sources or project.source_directories - if not watch_sources: - fail("Missing source_directories config, don't know what to watch.") - else: - watch_sources = [make_target] - q: "queue.Queue[Optional[float]]" = queue.Queue() - debounced_fs_watch(watch_sources, q, config, project) - display.run_async(q) - last_build = 0.0 - try: - while True: - t = q.get() - if t is None: - break - if t < last_build: - continue - last_build = time.time() - if args.make: - display.progress("Building...") - ret = run_make_capture_output(make_target, project) - if ret.returncode != 0: - display.update( - ret.stderr.decode("utf-8-sig", "replace") - or ret.stdout.decode("utf-8-sig", "replace"), - error=True, - ) - continue - mydump = run_objdump(mycmd, config, project) - display.update(mydump, error=False) - except KeyboardInterrupt: - display.terminate() - - -if __name__ == "__main__": - main() diff --git a/tools/asm_differ/diff_settings.py b/tools/asm_differ/diff_settings.py deleted file mode 100644 index 183b96b7..00000000 --- a/tools/asm_differ/diff_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -def apply(config, args): - config["baseimg"] = "target.bin" - config["myimg"] = "source.bin" - config["mapfile"] = "build.map" - config["source_directories"] = ["."] - # config["show_line_numbers_default"] = True - # config["arch"] = "mips" - # config["map_format"] = "gnu" # gnu or mw - # config["mw_build_dir"] = "build/" # only needed for mw map format - # config["makeflags"] = [] - # config["objdump_executable"] = "" diff --git a/tools/asm_differ/mypy.ini b/tools/asm_differ/mypy.ini deleted file mode 100644 index 138b9393..00000000 --- a/tools/asm_differ/mypy.ini +++ /dev/null @@ -1,17 +0,0 @@ -[mypy] -check_untyped_defs = True -disallow_any_generics = True -disallow_incomplete_defs = True -disallow_untyped_calls = True -disallow_untyped_decorators = True -disallow_untyped_defs = True -no_implicit_optional = True -warn_redundant_casts = True -warn_return_any = True -warn_unused_ignores = True -ignore_missing_imports = True -python_version = 3.6 -files = diff.py - -[mypy-diff_settings] -ignore_errors = True diff --git a/tools/asm_differ/screenshot.png b/tools/asm_differ/screenshot.png deleted file mode 100644 index 3230555328cd9acdf388ce1626415ceb78760535..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99842 zcmd431yG#Zx~@w?g1b9Gg1b8j?(Ul4?yezdaCdiicXxMpXxtjt+nIC8T?|I+RzvN^@;bCxKz`(%Z#l?gaz`)*jgWgV15TGr};=9eDAJ8^p z>h@q@2))1G@8YNtaKXSnfr$%!RdPu^S#woWo_ig*yyF8fki`5Th6sk)PK;JWNis|Z zsu^+aDCNg#=!G|=0-dGpsRFN!*d=R5V@HWtjq$!zFb{tE^mFtRRO-tUcq%diOwffV zZ^T4S^`iOdq5b%}ds!X2tgI|Lksp!(6cH8H*o@VW-y=UH*`6J5CQL_(Z*_qu*{tga7bR<#a{)OOf4fWVsTgfN-|O~^HU_AgLGLcgL@F$H4n zWr;G-r0}(Es%!V>?c=6wf1ONQ`%w_<1L{LxpY?I@Q-aQ`neWb-rQz3gs#AUH&UsU< zY)xdWjZP#W(P)lq3pRdOxv<-GjZ?%dMuI)7(gO+PE$QRg!DbZbrc6Ovt=^Fq766-5)c zGoJ(ITpZQBar&%xo?I~-vuyJnJBZ*?jigQ@u}@?ogV3@)qEfx%#l}N|r64mG?-`8F zC|c7jt0s+N&^*JzapP;uL>btv30uhMTA6lvAKR9Z$Y&$&h6d2+Y>bJ=mZsGoa5s;2HN!v8s<;Ip1Pf~_@MNjk&d(l7Sf(N7P_`YCW+gYaKmom&3px9qBtjw3|*?;4C zWYSRGs%><_9@!+@vPfgbZQ1gw9dAhe97l8AtHUX3V!}u0`Y2W}g)=yRVqpgc^@(8# zyTX-TaYA|I>_d|(sgQU~Dt+Q^E9jyoI)Is(F}N?#8#r6M#|}oXTZaGM8>l!KbYZx+ zWFc2-%4&qLqa(Fjac)$@qY-EyBh#wN){174%ry zAqm9t3_bi5842voGw*BplO2k@d(jWyeuN{uoyPw$4`s@$wLCykf6TeBCMWjPzM z%;z6e7*CsAvzNXpNt)f;I+bb;H+x3iZG0TgDY@m+4`3xfTljS^I7TWLRB1HU(Z zcp=R2bPm{VtswGqVfo{lV~W`6#Hw<|7CWS}pz0RHc)XngJ_zMzS9$roDh95$b*p|w zq)2{m^ou2Tw9Oy(K1l~rqilZCk-}CTnLCkx8!9EaE=xK^^?Fm6<1LCdA&rsbu6}vZ$nN3VBk^*_{vTVn6%)|Ofsb!l z-*oP1^F5~!k3DN19EQGb@0und49n(;=)6EeiR zlh&o_FGPZ(o|0;5UT~(mr&Fkd)XGI0nJ|x)-?~F^KIEw+NSN(%%0NP(pkay0O>$;W z2XFC3WM6#KN9QK&m-571d0|efRqK`yQuxeijWy)jBR(EtCmZvZzC4o6}C?EOp=3f`~j66SNK z#;z<<$zn6Qs?!WPyIE2h0xrRMcWcJ|H3KjBP(j@yx?3CDj;;G`S;^`WMZdxlosn?rLJheNxys8Q zPKs6=j$^7Jcn6;C7;TxGO38>&4YAW9A*H3?*-)p=Q1gG6$fkec4&SV4pq(Gije6=GAa zLLusT?mQz}LF+S#H+lJY5*k(8Id^*^zRg;|9^GOICQy+X2xWW+ zJU5l{#Xfg}3w-33rRTArnXJ3Y^4UYkR-B!#82O^HkS?XWikPq>%aF!rIAl(BsmVW< zb}Pm>&}s*-W403IT%z$kHco1EFXov=AD(~Xj6HvGXG{KLS{pMVy{Y@gRZY0p5O>Ur z>+Z}KqTZN)%+r-wyVKZKacHABdkz z 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 diff --git a/tools/check_format.py b/tools/check_format.py deleted file mode 100644 index bc3e8ff0..00000000 --- a/tools/check_format.py +++ /dev/null @@ -1,83 +0,0 @@ -# SPDX-FileCopyrightText: 2024 zeldaret -# SPDX-License-Identifier: CC0-1.0 - -import subprocess -import argparse -import difflib -import multiprocessing -import glob -import os.path -import sys - -sys.path.insert(0, os.curdir) -import format - -sys.path.pop(0) - - -def get_git_status(): - return subprocess.check_output("git status --porcelain".split(), text=True) - - -def get_modified_files_to_format(compare_to): - modified_files_str = subprocess.check_output( - ["git", "diff", "--name-only", compare_to], text=True - ) - modified_files = set(modified_files_str.splitlines()) - - all_src_files, all_extra_files = format.list_files_to_format() - # Split modified_files between source files and extra files (see format.py) - # This also filters out deleted files that no longer exist - modified_src_files_existing = list(modified_files.intersection(all_src_files)) - modified_extra_files_existing = list(modified_files.intersection(all_extra_files)) - - return modified_src_files_existing, modified_extra_files_existing - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--verbose", action="store_true") - parser.add_argument("--compare-to", dest="compare_to") - args = parser.parse_args() - - if args.compare_to: - src_files, extra_files = get_modified_files_to_format(args.compare_to) - if args.verbose: - print("Formatting specific files:") - print(len(src_files), src_files) - print(len(extra_files), extra_files) - if not src_files and not extra_files: - if args.verbose: - print("Nothing to format") - exit(0) - else: - src_files, extra_files = format.list_files_to_format() - - nb_jobs = multiprocessing.cpu_count() - - git_status_pre = get_git_status() - - format.format_files(src_files, extra_files, nb_jobs) - - git_status_post = get_git_status() - - if git_status_pre != git_status_post: - print( - "Misformatted files found." - " Run ./format.py and verify codegen is not impacted." - ) - for l in difflib.unified_diff( - git_status_pre.splitlines(), - git_status_post.splitlines(), - "Old git status", - "New git status", - ): - print(l) - - with open("changes.patch", "w+") as patchf: - patchf.write(subprocess.check_output("git diff".split(), text=True)) - exit(1) - - -if __name__ == "__main__": - main() diff --git a/tools/disassemble_elf.py b/tools/disassemble_elf.py deleted file mode 100755 index 1fa58a81..00000000 --- a/tools/disassemble_elf.py +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/env python3 -# -# ELF disassembler that attempts to be matching -# - -import argparse, struct, sys - -from libelf import * -from mdebug import * -from mips_isa import * -from util import * - -def debug_log(msg): - print(msg, file=sys.stderr) - -class MipsDisasm: - """ - """ - - def __init__(self, elf_file) -> None: - self.elf_file = elf_file - mdebug_section = elf_file.find_section_by_type(SHT_MIPS_DEBUG) - if mdebug_section is not None: - self.mdebug = mdebug_section - self.has_mdebug = True - else: - self.has_mdebug = False - self.cur_file = None - self.comment_section_pos = 1 - self.section_local_labels = {} - - def add_section_local_label(self, section, offset): - if section not in self.section_local_labels: - self.section_local_labels.update({section : set()}) - self.section_local_labels[section].add(offset) - - def advance_file(self): - seen_cur_file = False - for sym in self.elf_file.symtab.symbol_entries: - if sym.type == ST_FILE: - if seen_cur_file or self.cur_file is None: - self.cur_file = sym - break - elif self.cur_file == sym: - seen_cur_file = True - return self.cur_file is not None - - def disassemble_all_sections(self): - print(MipsDisasm.asm_prelude()) - - # debug_log("Name Type Addr Off Size ES Flg Lk Inf Al") - for section in self.elf_file.sections: - local_labels = self.section_local_labels.get(section.name, None) - # debug_log(section) - if section.name in ['', '.strtab', '.shstrtab', '.symtab', '.reginfo', '.comment', '.note', '.options', '.mdebug', '.gptab.data', '.gptab.bss'] or \ - (section.sh_type == SHT_REL or section.sh_type == SHT_RELA): - continue - if section.sh_size == 0: - continue - print("") - print(MipsDisasm.begin_section(section)) - if section.is_executable(): - self.disassemble_exec(section) - elif section.sh_type == SHT_PROGBITS: - # TODO kmc as doesn't support incbin, byte array this - # print(f".incbin \"libultra.a\", 0x{section.sh_offset:08X}, 0x{section.sh_size:X}") - first = True - for i,b in enumerate(section.data): - if local_labels is not None and i in local_labels: - if not first: - print("") - print(f".{section.name[1].upper()}_{i:08X}:") - print(" .byte ", end='') - first = True - elif first: - print(" .byte ", end='') - if not first: - print(", ", end='') - print(f"0x{int(b):02X}", end='') - first = False - print("") - elif section.sh_type == SHT_NOBITS: - print(f".skip 0x{section.sh_size:X}") - else: - assert False, f"Unhandled section: {section.name}" - # debug_log("/// UNHANDLED ///") - - def pass_section(self, section): - pass - - @staticmethod - def asm_prelude(): - return f""".include "macro.inc" -#include "regdef.h" - -// assembler directives -.set noat // allow manual use of $at -.set noreorder // don't insert nops after branches""" - - @staticmethod - def begin_section(section): - section_flags = section.flags_str().lower().replace(' ', '') - if section_flags != "": - section_flags = f", \"{section_flags}\"" - section_type = "" - if section.sh_type == SHT_PROGBITS: - section_type = ", @progbits" - elif section.sh_type == SHT_NOBITS: - section_type = ", @nobits" - if section_type != "" and section_flags == "": - section_flags = ", \"\"" - - return f""".section {section.name}{section_flags}{section_type} -.balign {section.sh_addralign} -""" - - def get_label_name(self, addr, pdr=None, optional=False): - if pdr is not None: - sym = pdr.lookup_sym(addr, EcoffSt.LABEL) - if sym is not None: - return sym.name - if not optional: - return f".L{addr:08X}" - else: - return None - - def get_comment_string(self, start): - comment_section = self.elf_file.find_section_by_name(".comment") - end = comment_section.data.find(b'\0', start) - if end == -1: - return None, None - comment = comment_section.data[start:end].decode("ASCII") - return comment, end + 1 - - def print_end(self, vaddr, eof): - ends = eof.get(vaddr, None) - if ends is not None: - for sym in ends: - print(f" .type {sym.name}, @{'function' if sym.type == ST_FUNC else 'object'}") - if sym.st_size != 0: - print(f" .size {sym.name}, . - {sym.name}") - print(f" .end {sym.name}\n") - - def disassemble_exec(self, section): - raw_insns = as_word_list(section.data) - insns = [decode_insn(raw, section.sh_addr + j * 4) for j,raw in enumerate(raw_insns)] - - # enumerate branch labels - branch_labels = set() - - for i,insn in enumerate(insns): - if insn.id in MIPS_BRANCH_INSNS or insn.id == MIPS_INS_J: - branch_labels.add(insn.target if insn.id == MIPS_INS_J else insn.offset) - - eof = {} # vaddr : name - def add_end(vaddr, sym): - if vaddr not in eof: - eof[vaddr] = set() - eof[vaddr].add(sym) - - cur_fdr = None - cur_pdr = None - for i,insn in enumerate(insns): - mnemonic = insn.mnemonic - op_str = insn.op_str - - # Update mdebug info - src_inf = "" - if self.has_mdebug: - # Get new fdr if there is one - fdr = self.mdebug.fdr_foraddr(i * 4, extensions=('.c', '.s')) - if fdr is not None: - # debug_log(fdr.name) - cur_fdr = fdr - - # Get new pdr if there is one - if cur_fdr is not None: - pdr = cur_fdr.pdr_foraddr(i * 4) - if pdr is not None: - # debug_log(pdr) - cur_pdr = pdr - - # Line numbers - if cur_pdr is not None: - asm_line = i - cur_pdr.addr//4 - if asm_line < len(cur_pdr.lines): - src_inf = f" {cur_pdr.lines[asm_line]:4}" - else: - src_inf = " PADDING" - - # Symbols for this address - syms = section.get_sym(i * 4) - # if len(syms) != 0: - # debug_log("\n".join([str(sym) for sym in syms])) - - # Print end - self.print_end(insn.vaddr, eof) - - # Print symbol - for sym in syms: - if sym.name == "gcc2_compiled.": - print(f"// compiler generated") - if self.cur_file is None: - print(f".version \"01.01\"") - if self.advance_file(): - print(f".file 1 \"{self.cur_file.name}\"") - - comment_string = None - while comment_string != "\"GCC: (GNU) 2.7.2\"": - comment_string, self.comment_section_pos = self.get_comment_string(self.comment_section_pos) - if comment_string is None: - break - print(f".ident \"{comment_string}\"") - - if sym.bind == SB_GLOBAL: - print(f"glabel {sym.name}") - else: - print(f"{sym.name}:") - - if sym.st_size != 0: - print(f" .ent {sym.name}") - add_end(insn.vaddr + sym.st_size, sym) - else: - print(f" .type {sym.name}, @{'function' if sym.type == ST_FUNC else 'object'}\n") - - # Print branch labels - lbl = self.get_label_name(insn.vaddr, pdr=cur_pdr, optional=not insn.vaddr in branch_labels) - if lbl is not None: - print(f"{lbl}:") - - # Relocations for this address - rels = section.get_rel(i * 4) - assert len(rels) < 2 # There should never be more than 1 relocation for a single address, right? - # if len(rels) != 0: - # debug_log("\n".join([str(rel) for rel in rels])) - - # Apply relocation - if len(rels) != 0: - rel = rels[0] - if rel.rel_type == R_MIPS_26: - if insn.id == MIPS_INS_JAL: - op_str = rel.relocated_symbol.name - if op_str == ".text" and cur_fdr is not None: - pdr = cur_fdr.pdr_foraddr(insn.target) - if pdr is not None: - op_str = pdr.name - elif insn.id != MIPS_INS_J: # Branch labels for j instructions are also R_MIPS_26 relocations - assert False , f"Got unexpected R_MIPS_26 relocation {insn.id}" - elif rel.rel_type == R_MIPS_HI16: - assert insn.id in [MIPS_INS_LUI] - rel_name = rel.relocated_symbol.name - if rel.relocated_symbol.type == ST_SECTION: - rel_name = f".{rel_name[1].upper()}_00000000" - if cur_fdr is not None: - pass - - op_str = f"{insn.abi.gpr_names[insn.rt]}, %hi({rel_name})" - elif rel.rel_type == R_MIPS_LO16: - # Ideally this should be in the elf code so the relocations don't look identical - addend = insn.imm - rel_name = rel.relocated_symbol.name - if rel.relocated_symbol.type == ST_SECTION: - rel_name = f".{rel_name[1].upper()}_{addend:08X}" - self.add_section_local_label(rel.relocated_symbol.name, addend) - addend = 0 - addend_str = f" + 0x{addend:X}" if addend != 0 else "" - - if insn.id == MIPS_INS_ADDIU: - op_str = f"{insn.abi.gpr_names[insn.rt]}, {insn.abi.gpr_names[insn.rs]}, %lo({rel_name}{addend_str})" - elif insn.id in MIPS_LOAD_STORE_INSNS: - if insn.id in MIPS_FP_LOAD_STORE_INSNS: - op_str = f"{insn.abi.cop1_names[insn.ft]}, " - else: - op_str = f"{insn.abi.gpr_names[insn.rt]}, " - op_str += f"%lo({rel_name}{addend_str})({insn.abi.gpr_names[insn.base]})" - else: - assert False - else: - assert False - - # Apply branch labels - if insn.id in MIPS_BRANCH_INSNS: - op_str_parts = [] - for field in insn.fields: - if field == 'offset': - op_str_parts.append(self.get_label_name(insn.offset, cur_pdr)) - else: - op_str_parts.append(insn.format_field(field)) - op_str = ", ".join(op_str_parts) - elif insn.id == MIPS_INS_J: - op_str = self.get_label_name(insn.target, cur_pdr) - - print(f"/* {section.sh_offset + i * 4:06X} {insn.vaddr:08X} {insn.raw:08X}{src_inf} */ {mnemonic:12}{op_str:35}".rstrip()) - self.print_end(section.sh_addr + section.sh_size, eof) - -def main(): - parser = argparse.ArgumentParser(description="Disassemble relocatable ELF object.") - parser.add_argument("filepath", help="path to the ELF file") - # TODO unimplemented optionals - parser.add_argument("--compiler", help="original compiler that produced the ELF (IDO or GCC, IDO default)", default="IDO") - parser.add_argument("--strenc", help="string encoding, default is EUC-JP for IDO and SJIS for GCC") - args = parser.parse_args() - - elf_file = None - with open(args.filepath, "rb") as elf: - elf_file = ElfFile(bytearray(elf.read())) - - disassembler = MipsDisasm(elf_file) - disassembler.disassemble_all_sections() - -if __name__ == '__main__': - main() diff --git a/tools/fix_objfile.py b/tools/fix_objfile.py deleted file mode 100755 index 52a79bcd..00000000 --- a/tools/fix_objfile.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -# -# Fixes garbage data between sections and optionally rodata section flags -# - -import argparse -from libelf import * - -def fix_section_flags(elf): - for section in elf.sections: - # Unset Alloc flag in .rodata section - if section.name == ".rodata": - section.sh_flags &= ~SHF_ALLOC - -def fix_garbage(elf, original): - # Begin with the original data, and paste header and section data over it - result = original - if len(original) < len(elf.data): - result.extend([0]*(len(elf.data) - len(original))) - else: - result = result[:len(elf.data)] - - # NOTE: This only supports the elf header, program headers, section headers and section data at this time - - # emplace elf header - hdr = elf.elf_header.to_bin() - result[0:len(hdr)] = hdr - - # emplace program headers - for i,proghdr in enumerate(elf.progheaders): - offset = elf.elf_header.e_phoff + i * elf.elf_header.e_phentsize - result[offset:offset+elf.elf_header.e_phentsize] = proghdr.to_bin() - - # emplace section headers and section data - for i,section in enumerate(elf.sections): - offset = elf.elf_header.e_shoff + i * elf.elf_header.e_shentsize - section_header, section_data = section.to_bin() - result[offset:offset+elf.elf_header.e_shentsize] = section_header - if section_data is not None: - result[section.sh_offset:section.sh_offset+section.sh_size] = section_data - - return result - -def main(): - parser = argparse.ArgumentParser(description="Disassemble relocatable ELF object.") - parser.add_argument("compiled", help="path to the compiled ELF file") - parser.add_argument("original", help="path to the original ELF file") - parser.add_argument("--fix-section-flags", help="", action="store_true") - args = parser.parse_args() - - elf = None - with open(args.compiled, "rb") as elf_file: - elf = ElfFile(bytearray(elf_file.read())) - - original = None - with open(args.original, "rb") as original_file: - original = bytearray(original_file.read()) - - if args.fix_section_flags: - fix_section_flags(elf) - - result_data = fix_garbage(elf, original) - - with open(args.compiled, "wb") as elf_file: - elf_file.write(result_data) - -if __name__ == '__main__': - main() diff --git a/tools/libdiff.py b/tools/libdiff.py deleted file mode 100755 index ac6b1391..00000000 --- a/tools/libdiff.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env python3 -from typing import Any, Dict, Optional -import json -import logging -import subprocess -import tempfile -import pathlib -import sys -import queue - -import asm_differ.diff as asm_differ - -MAX_FUNC_SIZE_LINES = 5000 - -class AsmDifferWrapper: - @staticmethod - def create_config(arch: asm_differ.ArchSettings) -> asm_differ.Config: - return asm_differ.Config( - arch=arch, - # Build/objdump options - diff_obj=True, - make=False, - source_old_binutils=True, - diff_section=".text", - inlines=False, - max_function_size_lines=MAX_FUNC_SIZE_LINES, - max_function_size_bytes=MAX_FUNC_SIZE_LINES * 4, - # Display options - formatter=asm_differ.AnsiFormatter(column_width=50), - threeway=None, - base_shift=0, - skip_lines=0, - compress=None, - show_branches=True, - show_line_numbers=False, - show_source=False, - stop_jrra=False, - ignore_large_imms=False, - ignore_addr_diffs=False, - algorithm="levenshtein", - ) - - @staticmethod - def run_objdump(target_data: bytes, config: asm_differ.Config, label: Optional[str]) -> Optional[str]: - flags = [ - "--disassemble", - "--disassemble-zeroes", - "--line-numbers", - "--reloc", - ] - - with tempfile.TemporaryDirectory() as tempdir: - target_path = pathlib.Path(tempdir) / "out.s" - target_path.write_bytes(target_data) - - start_addr = 0 - - if label: - nm_command = "mips-linux-gnu-nm" - - if nm_command: - try: - nm_proc = subprocess.run( - [nm_command] + [target_path], - capture_output=True, - universal_newlines=True - ) - except subprocess.CalledProcessError as e: - logger.error(f"Error running nm: {e}") - logger.error(e.stderr) - - if nm_proc.stdout: - for line in nm_proc.stdout.splitlines(): - if label in line: - start_addr = int(line.split()[0], 16) - break - else: - # logger.error(f"No nm command for {platform}") - return None - - flags.append(f"--start-address={start_addr}") - - objdump_command = "mips-linux-gnu-objdump" - cmds = [objdump_command] + config.arch.arch_flags + flags + [target_path] - - try: - objdump_proc = subprocess.run( - [objdump_command] + config.arch.arch_flags + flags + [target_path], - capture_output=True, - universal_newlines=True - ) - except subprocess.CalledProcessError as e: - # logger.error(e) - # logger.error(e.stderr) - return None - - out = objdump_proc.stdout - return out - - @staticmethod - def diff(target_elf: bytes, compiled_elf: bytes, diff_label:Optional[str]) -> Dict[str, Any]: - arch = asm_differ.get_arch("mips") - - config = AsmDifferWrapper.create_config(arch) - - # Base - if len(target_elf) == 0: - print("Base asm empty") - return - - basedump = AsmDifferWrapper.run_objdump(target_elf, config, diff_label) - if not basedump: - print("Error running asm-differ on basedump") - return - - if len(compiled_elf) == 0: - print("Creation of compilation elf_object failed") - return - - mydump = AsmDifferWrapper.run_objdump(compiled_elf, config, diff_label) - if not mydump: - print("Error running asm-differ") - return - - # Preprocess the dumps - basedump = asm_differ.preprocess_objdump_out(None, target_elf, basedump, config) - mydump = asm_differ.preprocess_objdump_out(None, compiled_elf, mydump, config) - - try: - display = asm_differ.Display(basedump, mydump, config) - except Exception: - print("Error running asm-differ") - return - - # Print the output - display.run_sync() - -if __name__ == "__main__": - if len(sys.argv) != 3 and len(sys.argv) != 4: - print(f"Usage: {sys.argv[0]} [path/to/target.o] [path/to/compiled.o] [function name (optional)]") - sys.exit(0) - target = pathlib.Path(sys.argv[1]) - compiled = pathlib.Path(sys.argv[2]) - if len(sys.argv) == 4: - label = sys.argv[3] - else: - label = None - target_bytes = target.read_bytes() - compiled_bytes = compiled.read_bytes() - AsmDifferWrapper.diff(target_bytes, compiled_bytes, label) diff --git a/tools/libelf.py b/tools/libelf.py deleted file mode 100755 index 11c4c037..00000000 --- a/tools/libelf.py +++ /dev/null @@ -1,1181 +0,0 @@ -#!/usr/bin/env python3 -# -# MIPS ELF library -# - -import struct - -from mdebug import EcoffHDRR, EcoffFdr, EcoffPdr, EcoffLiner, EcoffSymr - -# ===================================================================================================== -# Utility -# ===================================================================================================== - -def align_as(value, align): - if align == 0: - return value - while (value % align != 0): - value += 1 - return value - -# ===================================================================================================== -# ELF Identity -# ===================================================================================================== - -# Offsets into e_ident -EI_MAG0 = 0x00 # Magic char 0 , 0x7F -EI_MAG1 = 0x01 # Magic char 1 , 0x45 -EI_MAG2 = 0x02 # Magic char 2 , 0x4C -EI_MAG3 = 0x03 # Magic char 3 , 0x46 -EI_CLASS = 0x04 # -EI_DATA = 0x05 # -EI_VERSION = 0x06 # -EI_OSABI = 0x07 # -EI_ABIVERSION = 0x08 # -EI_PAD = 0x09 # -EI_NIDENT = 0x10 # - -# Values for e_ident[EI_DATA] -EI_DATA_LE = 1 # little endian -EI_DATA_BE = 2 # big endian - -EI_DATA_V = { - EI_DATA_LE : 'Little Endian', - EI_DATA_BE : 'Big Endian' -} - -# Values for e_ident[EI_CLASS] -EI_CLASS_32 = 1 # 32-bit -EI_CLASS_64 = 2 # 64-bit - -EI_CLASS_V = { - EI_CLASS_32 : 'ELF32', - EI_CLASS_64 : 'ELF64' -} - -# Values for e_ident[EI_OSABI] -EI_OSABI_V = { - 0x00 : 'UNIX / System V', - 0x01 : 'HP-UX', - 0x02 : 'NetBSD', - 0x03 : 'Linux', - 0x04 : 'GNU Hurd', - 0x06 : 'Solaris', - 0x07 : 'AIX', - 0x08 : 'IRIX', - 0x09 : 'FreeBSD', - 0x0A : 'Tru64', - 0x0B : 'Novell Modesto', - 0x0C : 'OpenBSD', - 0x0D : 'OpenVMS', - 0x0E : 'NonStop Kernel', - 0x0F : 'AROS', - 0x10 : 'Fenix OS', - 0x11 : 'CloudABI', - 0x12 : 'Stratus Technologies OpenVOS' -} - -# ===================================================================================================== -# ELF Types -# ===================================================================================================== - -ET_NONE = 0x0000 # -ET_REL = 0x0001 # relocatable -ET_EXEC = 0x0002 # -ET_DYN = 0x0003 # -ET_CORE = 0x0004 # -ET_LOOS = 0xFE00 # -ET_HIOS = 0xFEFF # -ET_LOPROC = 0xFF00 # -ET_HIPROC = 0xFFFF # - -E_TYPE = { - ET_NONE : 'ET_NONE', - ET_REL : 'ET_REL', - ET_EXEC : 'ET_EXEC', - ET_DYN : 'ET_DYN', - ET_CORE : 'ET_CORE', - ET_LOOS : 'ET_LOOS', - ET_HIOS : 'ET_HIOS', - ET_LOPROC : 'ET_LOPROC', - ET_HIPROC : 'ET_HIPROC' -} - -# ===================================================================================================== -# ELF Machines -# ===================================================================================================== - -EM_UNSPECIFIED = 0x00 -EM_ATNT_WE_32100 = 0x01 -EM_SPARC = 0x02 -EM_X86 = 0x03 -EM_MOTOROLA_68000 = 0x04 -EM_MOTOROLA_88000 = 0x05 -EM_INTEL_MCU = 0x06 -EM_INTEL_80860 = 0x07 -EM_MIPS = 0x08 -EM_IBM_SYSTEM_370 = 0x09 -EM_MIPS_RS3000_LE = 0x0A -EM_RESERVED_xB = 0x0B -EM_RESERVED_xC = 0x0C -EM_RESERVED_xD = 0x0D -EM_HEWLETT_PACKARD = 0x0E -EM_RESERVED_xF = 0x0F -EM_INTEL_80960 = 0x13 -EM_POWERPC = 0x14 -EM_POWERPC_64 = 0x15 -EM_5390 = 0x16 -EM_ARM = 0x28 -EM_SUPERH = 0x2A -EM_IA64 = 0x32 -EM_AMD64 = 0x3E -EM_TMS320C6000 = 0x8C -EM_ARM64 = 0xB7 -EM_RISC_V = 0xF3 - -E_MACHINE = { - EM_UNSPECIFIED : 'Unspecified', - EM_ATNT_WE_32100 : 'AT&T WE 32100', - EM_SPARC : 'SPARC', - EM_X86 : 'x86', - EM_MOTOROLA_68000 : 'Motorola 68000 (M68K)', - EM_MOTOROLA_88000 : 'Motorola 88000 (M88K)', - EM_INTEL_MCU : 'Intel MCU', - EM_INTEL_80860 : 'Intel 80860', - EM_MIPS : 'MIPS', - EM_IBM_SYSTEM_370 : 'IBM_System/370', - EM_MIPS_RS3000_LE : 'MIPS RS3000 Little-endian', - EM_RESERVED_xB : 'Reserved', - EM_RESERVED_xC : 'Reserved', - EM_RESERVED_xD : 'Reserved', - EM_HEWLETT_PACKARD : 'Hewlett-Packard PA-RISC', - EM_RESERVED_xF : 'Reserved', - EM_INTEL_80960 : 'Intel 80960', - EM_POWERPC : 'PowerPC', - EM_POWERPC_64 : 'PowerPC (64-bit)', - EM_5390 : 'S390, including S390x', - EM_ARM : 'ARM (up to ARMv7/Aarch32)', - EM_IA64 : 'SuperH', - EM_IA64 : 'IA-64', - EM_AMD64 : 'amd64', - EM_TMS320C6000 : 'TMS320C6000 Family', - EM_ARM64 : 'ARM 64-bits (ARMv8/Aarch64)', - EM_RISC_V : 'RISC-V' -} - -# ===================================================================================================== -# Program Types -# ===================================================================================================== - -PT_NULL = 0x00000000 # Program header table entry unused -PT_LOAD = 0x00000001 # Loadable segment -PT_DYNAMIC = 0x00000002 # Dynamic linking information -PT_INTERP = 0x00000003 # Interpreter information -PT_NOTE = 0x00000004 # Auxiliary information -PT_SHLIB = 0x00000005 # Reserved -PT_PHDR = 0x00000006 # Segment containing the program header table itself -PT_TLS = 0x00000007 # Thread-Local storage template -PT_LOOS = 0x60000000 # Inclusive reserved range for processor-specific semantics -PT_HIOS = 0x6FFFFFFF # ^ -PT_LOPROC = 0x70000000 # Inclusive reserved range for processor-specific semantics -PT_HIPROC = 0x7FFFFFFF # ^ - -P_TYPE = { - PT_NULL : 'PT_NULL' , - PT_LOAD : 'PT_LOAD' , - PT_DYNAMIC : 'PT_DYNAMIC', - PT_INTERP : 'PT_INTERP' , - PT_NOTE : 'PT_NOTE' , - PT_SHLIB : 'PT_SHLIB' , - PT_PHDR : 'PT_PHDR' , - PT_TLS : 'PT_TLS' , - PT_LOOS : 'PT_LOOS' , - PT_HIOS : 'PT_HIOS' , - PT_LOPROC : 'PT_LOPROC' , - PT_HIPROC : 'PT_HIPROC' -} - -# ===================================================================================================== -# Program Flags ( May be platform specific ! ) -# ===================================================================================================== - -PF_E = 1 << 0 # Execute -PF_W = 1 << 1 # Write -PF_R = 1 << 2 # Read - -# ===================================================================================================== -# Symbol Info -# ===================================================================================================== - -# Symbol Types - -ST_NOTYPE = 0 -ST_OBJECT = 1 -ST_FUNC = 2 -ST_SECTION = 3 -ST_FILE = 4 - -SYM_TYPE = { - ST_NOTYPE : 'NOTYPE', - ST_OBJECT : 'OBJECT', - ST_FUNC : 'FUNC', - ST_SECTION : 'SECTION', - ST_FILE : 'FILE' -} - -# Symbol Bind - -SB_LOCAL = 0 -SB_GLOBAL = 1 -SB_WEAK = 2 - -SYM_BIND = { - SB_LOCAL : 'LOCAL', - SB_GLOBAL : 'GLOBAL', - SB_WEAK : 'WEAK' -} - -# Symbol Visibility - -SV_DEFAULT = 0 - -SYM_VIS = { - SV_DEFAULT : 'DEFAULT' -} - -# NDX - -SHN_UND = 0x0000 -SHN_ABS = 0xFFF1 -SHN_COMMON = 0xFFF2 -SHN_XINDEX = 0xFFFF -SHN_LORESERVE = 0xFF00 - -SYM_NDX = { - SHN_UND : 'UND', - SHN_ABS : 'ABS', - SHN_COMMON : 'COMMON', - SHN_XINDEX : 'XINDEX', - SHN_LORESERVE : 'LORESERVE' -} - -# ===================================================================================================== -# Relocation Type -# ===================================================================================================== - -# EM_MIPS -R_MIPS_32 = 2 # Write the 32 bit address of the symbol -R_MIPS_26 = 4 # Write the 26 bit address of the symbol divided by four (for relocating branch instructions). Fail if address won't fit -R_MIPS_HI16 = 5 # Write the high 16 bits of the address of the symbol -R_MIPS_LO16 = 6 # Write the low 16 bits of the address of the symbol - -# EM_POWERPC -R_PPC_NONE = 0 # Do nothing. Skip this entry -R_PPC_ADDR32 = 1 # Write the 32 bit address of the symbol -R_PPC_ADDR24 = 2 # Write the 24 bit address of the symbol divided by four shifted up 2 bits to the 32 bit value (for relocating branch instructions). Fail if address won't fit -R_PPC_ADDR16 = 3 # Write the 16 bit address of the symbol. Fail if address more than 16 bits -R_PPC_ADDR16_LO = 4 # Write the low 16 bits of the address of the symbol -R_PPC_ADDR16_HI = 5 # Write the high 16 bits of the address of the symbol -R_PPC_ADDR16_HA = 6 # Write the high 16 bits of the address of the symbol plus 0x8000 -R_PPC_ADDR14 = 7 # Write the 14 bits of the address of the symbol divided by four shifted up 2 bits to the 32 bit value (for relocating conditional branch instructions). Fail if address won't fit -R_PPC_REL24 = 10 # Write the 24 bit address of the symbol minus the address of the relocation divided by four shifted up 2 bits to the 32 bit value (for relocating relative branch instructions). Fail if address won't fit -R_PPC_REL14 = 11 # Write the 14 bit address of the symbol minus the address of the relocation divided by four shifted up 2 bits to the 32 bit value (for relocating conditional relative branch instructions). Fail if address won't fit -R_PPC_EMB_SDA21 = 109 # Small Data Area - -# Gamecube/Wii custom relocations -R_DOLPHIN_NOP = 201 # Do nothing. Skip this entry. Carry the address of the symbol to the next entry -R_DOLPHIN_SECTION = 202 # Change which section relocations are being applied to. Set the offset into the section to 0 -R_DOLPHIN_END = 203 # Stop parsing the relocation table -R_DOLPHIN_MRKREF = 204 # Unknown - -RELOC_TYPE = { - EM_MIPS : { - R_MIPS_32 : 'R_MIPS_32', - R_MIPS_26 : 'R_MIPS_26', - R_MIPS_HI16 : 'R_MIPS_HI16', - R_MIPS_LO16 : 'R_MIPS_LO16' - }, - EM_POWERPC : { - R_PPC_NONE : 'R_PPC_NONE', - R_PPC_ADDR32 : 'R_PPC_ADDR32', - R_PPC_ADDR24 : 'R_PPC_ADDR24', - R_PPC_ADDR16 : 'R_PPC_ADDR16', - R_PPC_ADDR16_LO : 'R_PPC_ADDR16_LO', - R_PPC_ADDR16_HI : 'R_PPC_ADDR16_HI', - R_PPC_ADDR16_HA : 'R_PPC_ADDR16_HA', - R_PPC_ADDR14 : 'R_PPC_ADDR14', - 8 : 'R_PPC_ADDR14', - 9 : 'R_PPC_ADDR14', - R_PPC_REL24 : 'R_PPC_REL24', - R_PPC_REL14 : 'R_PPC_REL14', - 12 : 'R_PPC_REL14', - 13 : 'R_PPC_REL14', - R_PPC_EMB_SDA21 : 'R_PPC_EMB_SDA21', - R_DOLPHIN_NOP : 'R_DOLPHIN_NOP', - R_DOLPHIN_SECTION : 'R_DOLPHIN_SECTION', - R_DOLPHIN_END : 'R_DOLPHIN_END', - R_DOLPHIN_MRKREF : 'R_DOLPHIN_MRKREF' - } -} - -# ===================================================================================================== -# Section Types -# ===================================================================================================== - -SHT_NULL = 0x00000000 # Section header table entry unused -SHT_PROGBITS = 0x00000001 # Program data -SHT_SYMTAB = 0x00000002 # Symbol table -SHT_STRTAB = 0x00000003 # String table -SHT_RELA = 0x00000004 # Relocation entries with addends -SHT_HASH = 0x00000005 # Symbol hash table -SHT_DYNAMIC = 0x00000006 # Dynamic linking information -SHT_NOTE = 0x00000007 # Notes -SHT_NOBITS = 0x00000008 # Program space with no data (bss) -SHT_REL = 0x00000009 # Relocation entries, no addends -SHT_SHLIB = 0x0000000A # Reserved -SHT_DYNSYM = 0x0000000B # Dynamic linker symbol table -SHT_INIT_ARRAY = 0x0000000E # Array of constructors -SHT_FINI_ARRAY = 0x0000000F # Array of destructors -SHT_PREINIT_ARRAY = 0x00000010 # Array of pre-constructors -SHT_GROUP = 0x00000011 # Section group -SHT_SYMTAB_SHNDX = 0x00000012 # Extended section indices -SHT_NUM = 0x00000013 # Number of defined types. -SHT_LOOS = 0x60000000 # Start OS-specific. - -# MIPS specific -SHT_MIPS_DEBUG = 0x70000005 # .mdebug -SHT_MIPS_REGINFO = 0x70000006 # .reginfo -SHT_MIPS_OPTIONS = 0x7000000D # .options - -SH_TYPE = { - SHT_NULL : 'SHT_NULL', - SHT_PROGBITS : 'SHT_PROGBITS', - SHT_SYMTAB : 'SHT_SYMTAB', - SHT_STRTAB : 'SHT_STRTAB', - SHT_RELA : 'SHT_RELA', - SHT_HASH : 'SHT_HASH', - SHT_DYNAMIC : 'SHT_DYNAMIC', - SHT_NOTE : 'SHT_NOTE', - SHT_NOBITS : 'SHT_NOBITS', - SHT_REL : 'SHT_REL', - SHT_SHLIB : 'SHT_SHLIB', - SHT_DYNSYM : 'SHT_DYNSYM', - SHT_INIT_ARRAY : 'SHT_INIT_ARRAY', - SHT_FINI_ARRAY : 'SHT_FINI_ARRAY', - SHT_PREINIT_ARRAY : 'SHT_PREINIT_ARRAY', - SHT_GROUP : 'SHT_GROUP', - SHT_SYMTAB_SHNDX : 'SHT_SYMTAB_SHNDX', - SHT_NUM : 'SHT_NUM', - SHT_LOOS : 'SHT_LOOS', - - SHT_MIPS_DEBUG : 'SHT_MIPS_DEBUG', - SHT_MIPS_REGINFO : 'SHT_MIPS_REGINFO', - SHT_MIPS_OPTIONS : 'SHT_MIPS_OPTIONS', -} - -# ===================================================================================================== -# Section Flags -# ===================================================================================================== - -SHF_WRITE = 1 << 0 # Writable -SHF_ALLOC = 1 << 1 # Occupies memory during execution -SHF_EXECINSTR = 1 << 2 # Executable -SHF_MERGE = 1 << 4 # Might be merged -SHF_STRINGS = 1 << 5 # Contains null-terminated strings -SHF_INFO_LINK = 1 << 6 # 'sh_info' contains SHT index -SHF_LINK_ORDER = 1 << 7 # Preserve order after combining -SHF_OS_NONCONFORMING = 1 << 8 # Non-standard OS specific handling required -SHF_GROUP = 1 << 9 # Section is member of a group -SHF_TLS = 1 << 10 # Section hold thread-local data - -SHF_MASKOS = 0x0ff00000 # OS-specific -SHF_MASKPROC = 0xf0000000 # Processor-specific -SHF_ORDERED = 0x04000000 # Special ordering requirement (Solaris) -SHF_EXCLUDE = 0x08000000 # Section is excluded unless referenced or allocated (Solaris) - -SH_FLAG = { - SHF_WRITE : 'SHF_WRITE', - SHF_ALLOC : 'SHF_ALLOC', - SHF_EXECINSTR : 'SHF_EXECINSTR', - SHF_MERGE : 'SHF_MERGE', - SHF_STRINGS : 'SHF_STRINGS', - SHF_INFO_LINK : 'SHF_INFO_LINK', - SHF_LINK_ORDER : 'SHF_LINK_ORDER', - SHF_OS_NONCONFORMING : 'SHF_OS_NONCONFORMING', - SHF_GROUP : 'SHF_GROUP', - SHF_TLS : 'SHF_TLS', - SHF_MASKOS : 'SHF_MASKOS', - SHF_MASKPROC : 'SHF_MASKPROC', - SHF_ORDERED : 'SHF_ORDERED', - SHF_EXCLUDE : 'SHF_EXCLUDE' -} - -# ===================================================================================================== -# ELF Header -# ===================================================================================================== - -ELF32_BIG_STRUCT = '>HHIIIIIHHHHHH' -ELF32_LITTLE_STRUCT = 'IIIBBH', data) - assert self.st_shndx != SHN_XINDEX, "too many sections (SHN_XINDEX not supported)" - self.bind = st_info >> 4 - self.type = st_info & 0xF - self.visibility = self.st_other & 3 - self.parent_section = elf_file.sections[self.st_shndx] if self.st_shndx < SHN_LORESERVE else None - - self.name = name - if self.type == ST_SECTION: - self.name = elf_file.shstrtab.lookup_str(self.parent_section.sh_name) - if self.name == None: - self.name = elf_file.symtab.strtab.lookup_str(self.st_name) - - @staticmethod - def from_parts(st_name, st_value, st_size, st_info, st_other, st_shndx, strtab, name): - header = struct.pack('>IIIBBH', st_name, st_value, st_size, st_info, st_other, st_shndx) - return Symbol(header, strtab, name) - - def to_bin(self): - st_info = (self.bind << 4) | self.type - return struct.pack('>IIIBBH', self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx) - - def section_offset(self): - return self.st_value - self.parent_section.sh_addr - - def padded_size_n(self, n): - return align_as(self.st_size, n) - - def padded_size(self): - return self.padded_size_n(self.parent_section.sh_addralign) - - def __str__(self): - # Num: Value Size Type Bind Vis Ndx Name - ndx = ' ' + (SYM_NDX[self.st_shndx] if self.st_shndx in SYM_NDX.keys() else self.parent_section.name) - out = f"{self.st_value:08X} {self.st_size:06X} {SYM_TYPE[self.type]:7} {SYM_BIND[self.bind]:6} {SYM_VIS[self.visibility]:7} {ndx:12} {self.name}" - return out - -# ===================================================================================================== -# Relocation -# ===================================================================================================== - -class Relocation: - """ - typedef struct { - Elf32_Word r_offset; - struct { - Elf32_Word sym_index : 24; - Elf32_Word rel_type : 8; - } r_info; - Elf32_Word r_addend; - } Elf32_Reloc; - """ - - def __init__(self, data, elf_file, target_section, sh_type): - self.sh_type = sh_type - if sh_type == SHT_REL: - self.r_offset, self.r_info = struct.unpack('>II', data) - self.r_addend = 0 - else: - self.r_offset, self.r_info, self.r_addend = struct.unpack('>III', data) - self.sym_index = self.r_info >> 8 - self.rel_type = self.r_info & 0xff - self.relocated_symbol = elf_file.symtab.symbol_entries[self.sym_index] - self.elf_machine = elf_file.elf_header.e_machine - self.target_section = target_section - # TODO obtain the addend if the target section is executable - - def to_bin(self): - self.r_info = (self.sym_index << 8) | self.rel_type - if self.sh_type == SHT_REL: - return struct.pack('>II', self.r_offset, self.r_info) - else: - return struct.pack('>III', self.r_offset, self.r_info, self.r_addend) - - def relocated_symbol_in_section(self, symtab, section): - return symtab.lookup_symbol_in_section(symtab.symbol_entries[self.sym_index].st_value, section) - - def __str__(self): - relocated_symbol = self.relocated_symbol - # Offset Info Type Sym.Value Sym. Name + Addend - # 80135560 00091201 R_MY_RELOC_TYPE 800f39e0 ...data.0 + 0 - out = f"{self.r_offset:08X} {self.r_info:08X} {RELOC_TYPE[self.elf_machine][self.rel_type]:17} {relocated_symbol.st_value:08X} {relocated_symbol.name} + 0x{self.r_addend:X}" - return out - -# ===================================================================================================== -# Section -# ===================================================================================================== - -class Section: - """ - Describes a section - - typedef struct { - Elf32_Word sh_name; - Elf32_Word sh_type; - Elf32_Word sh_flags; - Elf32_Addr sh_addr; - Elf32_Off sh_offset; - Elf32_Word sh_size; - Elf32_Word sh_link; - Elf32_Word sh_info; - Elf32_Word sh_addralign; - Elf32_Word sh_entsize; - } Elf32_Shdr; - -x00 x00 4 4 sh_name Offset to string in the .shstrtab section representing this section's name - -x04 x04 4 4 sh_type See SH_TYPE - -x08 x08 4 8 sh_flags See SH_FLAG - -x0C x10 4 8 sh_addr Virtual address of the section in memory (for loaded sections) - -x10 x18 4 8 sh_offset Offset of the section in the file image - -x14 x20 4 8 sh_size Size in bytes of the section in the file image - -x18 x28 4 4 sh_link Contains the section index of an associated section. This field is used for several purposes depending on section type - -x1C x2C 4 4 sh_info Contains extra information about the section. This field is used for several purposes, depending on section type - -x20 x30 4 8 sh_addralign Contains the required alignment of the section. This field must be a power of two - -x24 x38 4 8 sh_entsize Contains the size, in bytes, of each entry, for sections that contain fixed-size entries. Zero otherwise - -x28 x40 END - """ - - def __init__(self, header, elf_file, index): - self.late_init_done = False - self.sh_name, self.sh_type, \ - self.sh_flags, self.sh_addr, \ - self.sh_offset, self.sh_size, \ - self.sh_link, self.sh_info, \ - self.sh_addralign, self.sh_entsize = struct.unpack('>IIIIIIIIII', header) - assert not self.sh_flags & SHF_LINK_ORDER - if self.sh_entsize != 0: - assert self.sh_size % self.sh_entsize == 0 - if self.sh_type in [SHT_NULL, SHT_NOBITS]: - self.data = None - else: - self.data = elf_file.data[self.sh_offset:][:self.sh_size] - self.index = index - self.relocated_by = [] - self.elf_file = elf_file - - def late_init(self): - self.late_init_done = True - - @staticmethod - def from_parts(sh_name, sh_type, sh_flags, sh_link, sh_info, sh_addralign, sh_entsize, sh_addr, sh_offset, data, index): - header = struct.pack('>IIIIIIIIII', sh_name, sh_type, sh_flags, sh_addr, sh_offset, \ - len(data), sh_link, sh_info, sh_addralign, sh_entsize) - return Section(header, data, index) - - def to_bin(self): - if self.sh_type not in [SHT_NULL, SHT_NOBITS]: - self.sh_size = len(self.data) - header = struct.pack('>IIIIIIIIII', self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset, self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize) - return header, self.data - - def get_rel(self, addr): - relocs = [] - for reloc_section in self.relocated_by: - reloc = reloc_section.find_reloc(addr) - if reloc is not None: - relocs.append(reloc) - return relocs - - def get_sym(self, addr): - symtab = self.elf_file.symtab - symbols = [] - for symbol in symtab.symbol_entries: - if symbol.st_value == addr and symbol.st_shndx == self.index and symbol.type != ST_SECTION: - symbols.append(symbol) - return symbols - - def is_rel(self): - return self.sh_type == SHT_REL or self.sh_type == SHT_RELA - - def is_allocable(self): - return (self.sh_flags & SHF_ALLOC) == SHF_ALLOC - - def is_executable(self): - return (self.sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR - - def is_writable(self): - return (self.sh_flags & SHF_WRITE) == SHF_WRITE - - def padded_size(self): - return align_as(self.sh_size, self.sh_addralign) - - def padded_size_4(self): - return align_as(self.sh_size, 4) - - def flags_str(self): - flags = "" - flags += "W" if self.is_writable() else " " - flags += "A" if self.is_allocable() else " " - flags += "X" if self.is_executable() else " " - return flags - - def __str__(self): - s_type = SH_TYPE[self.sh_type].replace('SHT_','') if self.sh_type in SH_TYPE.keys() else str(self.sh_type) - out = f"{self.name:12}{s_type:12} " - out += f"{self.sh_addr:08X} " - out += f"{self.sh_offset:06X} " - out += f"{self.sh_size:06X} " - out += f"{self.sh_entsize:2X} " - out += f"{self.flags_str():3} " - out += f"{self.sh_link:2} " - out += f"{self.sh_info:3} " - out += f"{self.sh_addralign:5X} " - return out - -class SymtabSection(Section): - """ - Symbol Table Section - """ - - def __init__(self, header, elf_file, index): - super().__init__(header, elf_file, index) - assert self.sh_entsize == 16 - - def late_init(self): - if self.late_init_done: - return - super().late_init() - self.strtab = self.elf_file.sections[self.sh_link] - self.symbol_entries = [] - for i in range(0, self.sh_size, self.sh_entsize): - self.symbol_entries.append(Symbol(self.data[i:i+self.sh_entsize], self.elf_file)) - - def to_bin(self): - header, _ = super().to_bin() - data = bytearray() - for sym in self.symbol_entries: - data.extend(sym.to_bin()) - return header, data - - def find_symbol(self, name): - for s in self.symbol_entries: - if s.name == name: - return (s.st_shndx, s.st_value) - return None - - def lookup_symbol(self, vaddr): - found = [] - for s in self.symbol_entries: - if s.st_value == vaddr and s.type != ST_SECTION: - found.append(s) - if len(found) != 0: - found.sort(reverse=True, key=(lambda s : s.type)) - return found[0] - return None - - def lookup_symbol_for_section(self, vaddr, shndx): - found = [] - for s in self.symbol_entries: - if s.st_value == vaddr and s.type != ST_SECTION and s.st_shndx == shndx: - found.append(s) - if len(found) != 0: - found.sort(reverse=True, key=(lambda s : s.type)) - return found[0] - return None - - def lookup_symbol_in_section(self, vaddr, section): - found = [] - for s in self.symbol_entries: - if s.st_value == vaddr and s.type != ST_SECTION and s.st_shndx == section.index: - found.append(s) - if len(found) != 0: - found.sort(reverse=True, key=(lambda s : s.type)) - return found[0] - return None - - def find_symbol_in_section(self, name, section): - pos = self.find_symbol(name) - assert pos is not None - assert pos[0] == section.index - return pos[1] - - def local_symbols(self): - return self.symbol_entries[:self.sh_info] - - def global_symbols(self): - return self.symbol_entries[self.sh_info:] - -class StrtabSection(Section): - """ - String Table Section - """ - def __init__(self, header, data, index): - super().__init__(header, data, index) - - def lookup_str(self, index): - to = self.data.find(b'\0', index) - assert to != -1 - return self.data[index:to].decode('latin1') - -class RelocationSection(Section): - """ - Relocation Section - """ - def __init__(self, header, data, index): - super().__init__(header, data, index) - - def late_init(self): - if self.late_init_done: - return - super().late_init() - self.rel_target = self.elf_file.sections[self.sh_info] - self.rel_target.relocated_by.append(self) - self.relocations = [] - for i in range(0, self.sh_size, self.sh_entsize): - self.relocations.append(Relocation(self.data[i:][:self.sh_entsize], self.elf_file, self.rel_target, self.sh_type)) - - def to_bin(self): - header, _ = super().to_bin() - data = bytearray() - for rel in self.relocations: - data.extend(rel.to_bin()) - return header, data - - def lookup_reloc(self, vaddr): - for r in self.relocations: - if r.r_offset - r.r_offset % 4 == vaddr: - return r - return None - - def find_reloc(self, vaddr): - for r in self.relocations: - if r.r_offset == vaddr: - return r - return None - - def lookup_jtbl_reloc(self, vaddr, symtab): - for r in self.relocations: - r_sym = r.relocated_symbol(symtab) - if r_sym.st_value + r.r_addend == vaddr: - return r - return None - -class MdebugSection(Section): - """ - MIPS Debugging Section - """ - def __init__(self, header, elf_file, index): - super().__init__(header, elf_file, index) - self.parent = self.elf_file - self.hdrr = EcoffHDRR(self.data) - - self.fdrs = [] - for i in range(self.hdrr.ifdMax): - fdr = EcoffFdr.from_binary(self, i) - self.fdrs.append(fdr) - - def late_init(self): - if self.late_init_done: - return - super().late_init() - for fdr in self.fdrs: - fdr.late_init() - - def fdr_forname(self, filename): - for fdr in self.fdrs: - # remove path and file ext - normalized_name = ".".join(fdr.name.split("/")[-1].split(".")[:-1]) - - if normalized_name == filename: - return fdr - return None - - def fdr_foraddr(self, addr, extensions=('.c')): - for fdr in self.fdrs: - if fdr.adr == addr and any((fdr.name.endswith(ext) for ext in extensions)): - return fdr - return None - - def read_string(self, index): - to = self.elf_file.data.find(b'\0', self.hdrr.cbSsOffset + index) - assert to != -1 - return self.elf_file.data[self.hdrr.cbSsOffset + index:to].decode("ASCII") - - def read_ext_string(self, index): - to = self.elf_file.data.find(b'\0', self.hdrr.cbSsExtOffset + index) - assert to != -1 - return self.elf_file.data[self.hdrr.cbSsExtOffset + index:to].decode("ASCII") - -class ReginfoSection(Section): - """ - MIPS Register Information Section - """ - def __init__(self, header, elf_file, index): - super().__init__(header, elf_file, index) - -# ===================================================================================================== -# Elf File -# ===================================================================================================== - -class ElfFile: - def __init__(self, data): - def init_section(i): - offset = self.elf_header.e_shoff + i * self.elf_header.e_shentsize - section_type = struct.unpack(">I", data[offset + 4:][:4])[0] - header_data = data[offset:][:self.elf_header.e_shentsize] - - if section_type == SHT_REL or section_type == SHT_RELA: - return RelocationSection(header_data, self, i) - elif section_type == SHT_SYMTAB: - return SymtabSection(header_data, self, i) - elif section_type == SHT_STRTAB: - return StrtabSection(header_data, self, i) - elif section_type == SHT_MIPS_DEBUG: - return MdebugSection(header_data, self, i) - elif section_type == SHT_MIPS_REGINFO: - return ReginfoSection(header_data, self, i) - else: - return Section(header_data, self, i) - - self.data = data - self.elf_header = ElfHeader(data[0:52]) - - num_progheaders = self.elf_header.e_phnum - num_sections = self.elf_header.e_shnum - - # Init program headers - self.progheaders = [] - for i in range(num_progheaders): - offset = self.elf_header.e_phoff + i * self.elf_header.e_phentsize - self.progheaders.append(ProgramHeader(data[offset:][:self.elf_header.e_phentsize], self.elf_header.e_ident[EI_CLASS])) - - # Init sections - self.sections = [] - for i in range(num_sections): - self.sections.append(init_section(i)) - - # Init shstrtab and name sections - self.shstrtab = self.sections[self.elf_header.e_shstrndx] - assert isinstance(self.shstrtab, StrtabSection) - - for s in self.sections: - s.name = self.shstrtab.lookup_str(s.sh_name) - - # Init symtab - symtab = None - for s in self.sections: - if s.sh_type == SHT_SYMTAB: - assert not symtab , "Found more than one symtab section?" - symtab = s - self.symtab = symtab - if self.symtab is not None: - self.symtab.late_init() - - # Late Init sections - for s in self.sections: - s.late_init() - - def find_section_by_name(self, name): - for s in self.sections: - if s.name == name: - return s - return None - - def find_section_by_type(self, type): - for s in self.sections: - if s.sh_type == type: - return s - return None - -### Tests - -if __name__ == "__main__": - import sys - - elf_file = None - - with open(sys.argv[1], "rb") as elf: - elf_file = ElfFile(bytearray(elf.read())) - - # Header Info Test - print(elf_file.elf_header) - # Program Headers Info Test - print("") - print("Program Headers:") - print(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align") - for phdr in elf_file.progheaders: - print(phdr) - # Section Headers Info Test - print("") - print("Section Headers:") - print(" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al") - for i,s in enumerate(elf_file.sections,0): - print(f" [{i:2}] {s}") - # Symbols Info Test - if elf_file.symtab is not None: - print("") - print(f"Symbol table '{elf_file.symtab.name}' contains {len(elf_file.symtab.symbol_entries)} entries") - print(" Num: Value Size Type Bind Vis Ndx Name") - for i,sym in enumerate(elf_file.symtab.symbol_entries,0): - print(f"{i:6}: {sym}") - # Relocations Info Test - print("") - for s in elf_file.sections: - if s.is_rel(): - print(f"\nRelocation section '{s.name}' at offset 0x{s.sh_offset:06X} contains {len(s.relocations)} entries:") - print(" Offset Info Type Sym.Value Sym.Name + Addend") - for reloc in s.relocations: - print(f"{reloc}") - # mdebug Info Test - print("") - mdebug_section = elf_file.find_section_by_type(SHT_MIPS_DEBUG) - if mdebug_section is not None: - """ - for fdr in mdebug_section.fdrs: - print(fdr) - for symr in fdr.symrs: - print(symr) - for pdr in fdr.pdrs: - print(pdr) - for symr in pdr.symrs: - print(symr) - for liner in pdr.lines: - print(liner) - """ - for fdr in mdebug_section.fdrs: - print(fdr.c_str()) diff --git a/tools/m2ctx.py b/tools/m2ctx.py deleted file mode 100755 index 39c5dc50..00000000 --- a/tools/m2ctx.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import sys -import subprocess -import tempfile - -script_dir = os.path.dirname(os.path.realpath(__file__)) -root_dir = os.path.abspath(os.path.join(script_dir, "..")) -src_dir = root_dir + "src/" - -# Project-specific -CPP_FLAGS = [ - "-Iinclude", - "-Iinclude/PR", - "-Iinclude/gcc", - "-Isrc", - "-Isrc/libc", - "-Iver/current/build/include", - "-D_LANGUAGE_C", - "-DF3DEX_GBI_2", - "-D_MIPS_SZLONG=32", - "-DSCRIPT(...)={}" - "-D__attribute__(...)=", - "-D__asm__(...)=", - "-ffreestanding", - "-DM2CTX", -] - -def import_c_file(in_file) -> str: - in_file = os.path.relpath(in_file, root_dir) - cpp_command = ["gcc", "-E", "-P", "-dM", *CPP_FLAGS, in_file] - cpp_command2 = ["gcc", "-E", "-P", *CPP_FLAGS, in_file] - - with tempfile.NamedTemporaryFile(suffix=".c") as tmp: - stock_macros = subprocess.check_output(["gcc", "-E", "-P", "-dM", tmp.name], cwd=root_dir, encoding="utf-8") - - out_text = "" - try: - out_text += subprocess.check_output(cpp_command, cwd=root_dir, encoding="utf-8") - out_text += subprocess.check_output(cpp_command2, cwd=root_dir, encoding="utf-8") - except subprocess.CalledProcessError: - print( - "Failed to preprocess input file, when running command:\n" - + cpp_command, - file=sys.stderr, - ) - sys.exit(1) - - if not out_text: - print("Output is empty - aborting") - sys.exit(1) - - for line in stock_macros.strip().splitlines(): - out_text = out_text.replace(line + "\n", "") - return out_text - -def main(): - parser = argparse.ArgumentParser( - description="""Create a context file which can be used for mips_to_c""" - ) - parser.add_argument( - "c_file", - help="""File from which to create context""", - ) - args = parser.parse_args() - - output = import_c_file(args.c_file) - - with open(os.path.join(root_dir, "ctx.c"), "w", encoding="UTF-8") as f: - f.write(output) - - -if __name__ == "__main__": - main() diff --git a/tools/mdebug.py b/tools/mdebug.py deleted file mode 100644 index 6f061ecc..00000000 --- a/tools/mdebug.py +++ /dev/null @@ -1,938 +0,0 @@ -#!/usr/bin/env python3 -# -# .mdebug section -# - -""" -References: -https://www.cs.unibo.it/~solmi/teaching/arch_2002-2003/AssemblyLanguageProgDoc.pdf -https://web.archive.org/web/20010628021622/http://reality.sgi.com/davea/objectinfo.html -https://github.com/astrelsky/ghidra_mdebug -https://github.com/Rozelette/print-mdebug/blob/master/print_mdebug.py -https://opensource.apple.com/source/gcc_legacy/gcc_legacy-938/gcc/mips-tdump.c.auto.html -https://github.com/uhhpctools/openuh/blob/master/osprey-gcc-4.2.0/gcc/mips-tdump.c -https://github.com/bminor/binutils-gdb/blob/master/gdb/mdebugread.c - -(stabs docs): -https://sourceware.org/gdb/current/onlinedocs/stabs.html - -(ecoff docs): -https://web.archive.org/web/20160305114748/http://h41361.www4.hp.com/docs/base_doc/DOCUMENTATION/V50A_ACRO_SUP/OBJSPEC.PDF -https://chromium.googlesource.com/native_client/nacl-toolchain/+/refs/tags/gcc-4.4.3/binutils/gas/ecoff.c -https://kernel.googlesource.com/pub/scm/linux/kernel/git/hjl/binutils/+/hjl/secondary/include/coff/sym.h -https://kernel.googlesource.com/pub/scm/linux/kernel/git/hjl/binutils/+/hjl/secondary/include/coff/symconst.h -""" - -from enum import IntEnum -import struct - -class EcoffBt(IntEnum): # Basic Type - NIL = 0 # - ADR = 1 # pointer-sized integer type - CHAR = 2 # char - UCHAR = 3 # unsigned char - SHORT = 4 # short - USHORT = 5 # unsigned short - INT = 6 # int - UINT = 7 # unsigned int - LONG = 8 # long - ULONG = 9 # unsigned long - FLOAT = 10 # float - DOUBLE = 11 # double - STRUCT = 12 # struct - UNION = 13 # union - ENUM = 14 # enum - TYPEDEF = 15 # type definition - RANGE = 16 # subrange of int - SET = 17 # pascal set - COMPLEX = 18 # FORTRAN complex - DCOMPLEX = 19 # FORTRAN double com[plex - INDIRECT = 20 # forward or unnamed typedef - FIXEDDEC = 21 # Fixed point decimal - FLOATDEC = 22 # Floating point decimal - STRING = 23 # Varying length character string - BIT = 24 # Aligned bit tring - PICTURE = 25 # picture - VOID = 26 # void - LONGLONG = 27 # long long int - ULONGLONG = 28 # unsigned long long int - LONG64 = 30 # - ULONG64 = 31 # - LONGLONG64 = 32 # - ULONGLONG64 = 33 # - ADR64 = 34 # - INT64 = 35 # - UINT64 = 36 # - - AGGREGATE = 63 # not a basic type - MAX = 64 # - -class EcoffSc(IntEnum): - NIL = 0 - TEXT = 1 # .text symbol - DATA = 2 # .data symbol - BSS = 3 # .bss symbol - REGISTER = 4 # value of symbol is register number - ABS = 5 # value of symbol is absolute - UNDEFINED = 6 # value of symbol is undefined - CDBLOCAL = 7 # variable value is in se->va.?? - BITS = 8 # variable is a bit field - CDBSYSTEM = 9 # variable value is in cdb address space - REGIMAGE = 10 # register value is saved on stack - INFO = 11 # symbol contains debugger information - USERSTRUCT = 12 # address in struct user for current process - SDATA = 13 # load time only small data - SBSS = 14 # load time only small common - RDATA = 15 # load time only read-only data - VAR = 16 # var parameter (FORTRAN, Pascal) - COMMON = 17 # common variable - SCOMMON = 18 # small common - VARREGISTER = 19 # var parameter in a register - VARIANT = 20 # variant record - SUNDEFINED = 21 # small undefined (external) data - INIT = 22 # .init section symbol - BASEDVAR = 23 # FORTRAN or PL/1 ptr based var - XDATA = 24 # exception handling data - PDATA = 25 # procedure section - FINI = 26 # .fini section - RCONST = 27 # .rconst section - MAX = 32 # - -class EcoffSt(IntEnum): - NIL = 0 # - GLOBAL = 1 # external symbol - STATIC = 2 # static symbol - PARAM = 3 # procedure argument - LOCAL = 4 # local variable - LABEL = 5 # label - PROC = 6 # procedure - BLOCK = 7 # beginning of block - END = 8 # end of something - MEMBER = 9 # member of struct/union/enum/.. - TYPEDEF = 10 # type definition - FILE = 11 # filename - REGRELOC = 12 # register relocation - FORWARD = 13 # forwarding address - STATICPROC = 14 # load time only static procedures - # (CONSTANT and STAPARAM are in different orders between different sources...) - CONSTANT = 15 # constant - STAPARAM = 16 # FORTRAN static parameters - STRUCT = 26 # structure - UNION = 27 # union - ENUM = 28 # enum - INDIRECT = 34 # - -class EcoffTq(IntEnum): # Type qualifier - NIL = 0 # - PTR = 1 # pointer - PROC = 2 # procedure - ARRAY = 3 # array - FAR = 4 # longer addressing - VOL = 5 # volatile - CONST = 6 # constant - MAX = 8 # - - UNK7 = 7 # invalid - UNK9 = 9 # invalid - UNK10 = 10 # invalid - UNK11 = 11 # invalid - UNK12 = 12 # invalid - UNK13 = 13 # invalid - UNK14 = 14 # invalid - UNK = 15 # invalid - -class EcoffLanguageCode(IntEnum): - C = 0 - PASCAL = 1 - FORTRAN = 2 - ASM = 3 - MACHINE = 4 - NIL = 5 - ADA = 6 - PL1 = 7 - COBOL = 8 - STDC = 9 - CPLUSPLUSV2 = 10 - MAX = 11 - -def get_bitrange(value, start, length): - return (value >> start) & ((1 << length) - 1) - -def sign_extend_16(value): - return (value & 0x7FFF) - (value & 0x8000) - -def sign_extend_4(value): - return (value & 0x7) - (value & 0x8) - -class EcoffLiner: - """ - ECOFF Line Numbers Mapping - - typedef struct sLINER { - s32 count : 4; - s32 delta : 4; - } tLINER, *pLINER; - """ - - def __init__(self, data) -> None: - self.count = get_bitrange(data[0], 0, 4) + 1 - self.delta = get_bitrange(data[0], 4, 4) - - if self.delta == 8: - self.is_extended = True - self.delta = sign_extend_16((data[1] << 8) | data[2]) - self.data = data[:3] - else: - self.is_extended = False - self.delta = sign_extend_4(self.delta) - self.data = data[:1] - - def __str__(self) -> str: - return f"""= EcoffLiner ============= -delta = {self.delta} -count = {self.count} -extended = {self.is_extended}""" - -class EcoffTir: - """ - ECOFF Type Information Record - - typedef struct { -#ifdef LITTLE_ENDIAN - u32 tq3 : 4; - u32 tq2 : 4; - u32 tq1 : 4; /* 6 type qualifiers - tqPtr, etc. */ - u32 tq0 : 4; - /* ---- 16 bit boundary ---- */ - u32 tq5 : 4; - u32 tq4 : 4; - u32 bt : 6; /* basic type */ - u32 continued : 1; /* indicates additional TQ info in next AUX */ - u32 fBitfield : 1; /* set if bit width is specified */ -#else - u32 fBitfield : 1; /* set if bit width is specified */ - u32 continued : 1; /* indicates additional TQ info in next AUX */ - u32 bt : 6; /* basic type */ - u32 tq4 : 4; - u32 tq5 : 4; - /* ---- 16 bit boundary ---- */ - u32 tq0 : 4; - u32 tq1 : 4; /* 6 type qualifiers - tqPtr, etc. */ - u32 tq2 : 4; - u32 tq3 : 4; -#endif - } TIR, *pTIR; // size = 4 - """ - SIZE = 4 - - def __init__(self, data, endian) -> None: - if endian == 1: - self.tq3 = EcoffTq(get_bitrange(data, 0, 4)) - self.tq2 = EcoffTq(get_bitrange(data, 4, 4)) - self.tq1 = EcoffTq(get_bitrange(data, 8, 4)) - self.tq0 = EcoffTq(get_bitrange(data, 12, 4)) - self.tq5 = EcoffTq(get_bitrange(data, 16, 4)) - self.tq4 = EcoffTq(get_bitrange(data, 20, 4)) - self.bt = EcoffBt(get_bitrange(data, 24, 6)) - self.continued = get_bitrange(data, 30, 1) - self.fBitfield = get_bitrange(data, 31, 1) - else: - self.fBitfield = get_bitrange(data, 0, 1) - self.continued = get_bitrange(data, 1, 1) - self.bt = EcoffBt(get_bitrange(data, 2, 6)) - self.tq4 = EcoffTq(get_bitrange(data, 8, 4)) - self.tq5 = EcoffTq(get_bitrange(data, 12, 4)) - self.tq0 = EcoffTq(get_bitrange(data, 16, 4)) - self.tq1 = EcoffTq(get_bitrange(data, 20, 4)) - self.tq2 = EcoffTq(get_bitrange(data, 24, 4)) - self.tq3 = EcoffTq(get_bitrange(data, 28, 4)) - self.tqs = (self.tq0, self.tq1, self.tq2, self.tq3, self.tq4, self.tq5) - - def __str__(self) -> str: - return f"""= EcoffTIR ============== -fBitfield = {self.fBitfield} -continued = {self.continued} -bt = {self.bt.name} -tq4 = {self.tq4} -tq5 = {self.tq5} -tq0 = {self.tq0} -tq1 = {self.tq1} -tq2 = {self.tq2} -tq3 = {self.tq3}""" - -class EcoffRNDXR: - """ - typedef struct { -#ifdef LITTLE_ENDIAN - u32 index : 20; /* index int sym/aux/iss tables */ - u32 rfd : 12; /* index into the file indirect table */ -#else - u32 rfd : 12; /* index into the file indirect table */ - u32 index : 20; /* index int sym/aux/iss tables */ -#endif - } RNDXR, *pRNDXR; // size = 4 - """ - SIZE = 4 - - def __init__(self, data, endian) -> None: - if endian == 1: - self.index = get_bitrange(data, 0, 20) - self.rfd = get_bitrange(data, 20, 12) - else: - self.rfd = get_bitrange(data, 0, 12) - self.index = get_bitrange(data, 12, 20) - - def __str__(self) -> str: - return f"""= EcoffRNDXR ============== -index = {self.index} -rfd = {self.rfd}""" - -class EcoffAux: - """ - typedef union __sgi_auxu_u { - TIR ti; /* type information record */ - RNDXR rndx; /* relative index into symbol table */ - long_i dnLow; /* low dimension of array */ - long_i dnHigh; /* high dimension of array */ - long_i isym; /* symbol table index (end of proc) */ - long_i iss; /* index into string space (not used) */ - long_i width; /* width for non-default sized struct fields */ - long_i count; /* count of ranges for variant arm */ - } AUXU, *pAUXU; // size = 4 - """ - SIZE = 4 - - def __init__(self, fdr, data, endian) -> None: - data = struct.unpack((">" if endian == 1 else "<") + "I", data)[0] - self.ti = EcoffTir(data, endian) - self.rndx = EcoffRNDXR(data, endian) - self.dnLow = self.dnHigh = self.isym = self.iss = self.width = self.count = data - self.fdr = fdr - - def __str__(self) -> str: - return f"""= EcoffAux ============== -ti = {{ -{self.ti} -}} -rndx = {{ -{self.rndx} -}} -dnLow = {self.dnLow:04X} -dnHigh = {self.dnHigh:04X} -isym = {self.isym:04X} -iss = {self.iss:04X} -width = {self.width:04X} -count = {self.count:04X}""" - -class EcoffSymr: - """ - ECOFF Local Symbol - - typedef struct sSymr { - s32 iss; /* index into String Space of name */ - s32 value; /* symbol value */ - /* value can be an address, size or frame offset depending on symbol type */ - EcoffSt st : 6; /* symbol type */ - EcoffSc sc : 5; /* storage class - text, data, etc */ - s32 _reserved : 1; /* reserved bit */ - s32 index : 20; /* index into sym/aux table */ - } tSymr, *pSymr; // size = 0xC - """ - SIZE = 0xC - - def __init__(self, parent, idx, data): - self.idx = idx - self.parent = parent # can be either Fdr or Pdr - self.pdr = parent if type(parent) == EcoffPdr else None - self.fdr = self.pdr.parent if self.pdr is not None else parent - - self.data = data[:EcoffSymr.SIZE] - - self.iss, self.value, bits = struct.unpack(">III", self.data) - self.st = EcoffSt(get_bitrange(bits, 26, 6)) - self.sc = EcoffSc(get_bitrange(bits, 21, 5)) - self._reserved = get_bitrange(bits, 20, 1) - self.index = get_bitrange(bits, 0, 20) - - self.name = self.fdr.read_string(self.iss) - self.type_name = None - - self.c_repr = None - - assert self._reserved == 0 # Sanity check - - def link_syms(self): - if self.st == EcoffSt.END: - self.start_sym = self.fdr.symrs[self.index] - elif self.st in [EcoffSt.BLOCK, EcoffSt.FILE, EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.ENUM]: - self.end_sym = self.fdr.symrs[self.index - 1] - elif self.st in [EcoffSt.PROC, EcoffSt.STATICPROC]: - aux = self.fdr.auxs[self.index] - self.end_sym = self.fdr.symrs[aux.isym - 1] - elif self.st in [EcoffSt.GLOBAL, EcoffSt.STATIC, EcoffSt.PARAM, EcoffSt.LOCAL, EcoffSt.MEMBER, EcoffSt.TYPEDEF, EcoffSt.FORWARD]: - pass - - def late_init(self): - if self.st == EcoffSt.END: - """ - END symbols index the associated begin symbol - """ - self.start_sym = self.fdr.symrs[self.index] - if self.start_sym.st == EcoffSt.BLOCK: - self.c_repr = "}" - elif self.start_sym.st == EcoffSt.FILE: - self.c_repr = f"// end of file: \"{self.start_sym.name}\"" - elif self.start_sym.st in [EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.ENUM]: - self.c_repr = "}" - if len(self.start_sym.type_name) != 0: - self.c_repr += " " + self.start_sym.type_name - self.c_repr += f";" - elif self.st in [EcoffSt.BLOCK, EcoffSt.FILE, EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.ENUM]: - """ - These symbols index the first symbol after their associated END symbol - """ - self.end_sym = self.fdr.symrs[self.index - 1] - if self.st == EcoffSt.BLOCK: - self.c_repr = "{" - elif self.st == EcoffSt.FILE: - self.c_repr = f"#line 1 \"{self.name}\"" - elif self.st in [EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.ENUM]: - keyword = "" - self.type_name = "" - next_sym = self.fdr.symrs[self.index] - if next_sym.st == EcoffSt.TYPEDEF: - # possible typedef struct/union/enum - # TODO check this by ensuring the typedef symbol references this symbol - keyword += "typedef " - self.type_name = next_sym.name - - if self.st == EcoffSt.UNION: - keyword += "union" - elif self.st == EcoffSt.ENUM: - keyword += "enum" - else: - keyword += "struct" - - name = self.name - if len(name) != 0: - name = ' ' + name - - self.c_repr = f"{keyword}{name} {{" - elif self.st in [EcoffSt.PROC, EcoffSt.STATICPROC]: - aux1 = self.fdr.auxs[self.index] - self.end_sym = self.fdr.symrs[aux1.isym - 1] - - self.c_repr = "" - if self.st == EcoffSt.STATICPROC: - self.c_repr += "static " - - self.return_type, _ = self.process_type_information(1) - self.c_repr += self.return_type - if len(self.return_type) != 0: - self.c_repr += " " - self.c_repr += self.name - self.c_repr += "()" - elif self.st in [EcoffSt.GLOBAL, EcoffSt.STATIC, EcoffSt.PARAM, EcoffSt.LOCAL, EcoffSt.MEMBER, EcoffSt.FORWARD]: - self.c_repr = "" - if self.st == EcoffSt.MEMBER: - # value of a stMember is the offset in bits - self.c_repr += f"/* 0x{self.value//8:X} */ " - - type_str, bitwidth = self.process_type_information(0) - self.c_repr += type_str - if len(self.c_repr) != 0: - self.c_repr += " " - self.c_repr += f"{self.name}{f' : {bitwidth}' if bitwidth is not None else ''};" - elif self.st == EcoffSt.TYPEDEF: - # TODO the typedef may already be absorbed into a struct or similar, check before emitting - type_str, _ = self.process_type_information(0) - self.c_repr = f"typedef {type_str} {self.name};" - elif self.st == EcoffSt.LABEL: - self.c_repr = f"{self.name}:" - - def process_type_information(self, ind): - c_bt_names = { - EcoffBt.NIL : None, - EcoffBt.ADR : None, - EcoffBt.CHAR : "signed char", - EcoffBt.UCHAR : "char", - EcoffBt.SHORT : "short", - EcoffBt.USHORT : "unsigned short", - EcoffBt.INT : "int", - EcoffBt.UINT : "unsigned int", - EcoffBt.LONG : "long", - EcoffBt.ULONG : "unsigned long", - EcoffBt.FLOAT : "float", - EcoffBt.DOUBLE : "double", - EcoffBt.STRUCT : "struct", - EcoffBt.UNION : "union", - EcoffBt.ENUM : "enum", - EcoffBt.TYPEDEF : "typedef", - EcoffBt.RANGE : None, - EcoffBt.SET : None, - EcoffBt.COMPLEX : "complex", - EcoffBt.DCOMPLEX : "double complex", - EcoffBt.INDIRECT : None, - EcoffBt.FIXEDDEC : None, - EcoffBt.FLOATDEC : None, - EcoffBt.STRING : "const char*", - EcoffBt.BIT : None, - EcoffBt.PICTURE : None, - EcoffBt.VOID : "void", - EcoffBt.LONGLONG : "long long", - EcoffBt.ULONGLONG : "unsigned long long", - EcoffBt.LONG64 : "long", - EcoffBt.ULONG64 : "unsigned long", - EcoffBt.LONGLONG64 : "long long", - EcoffBt.ULONGLONG64 : "unsigned long long", - EcoffBt.ADR64 : None, - EcoffBt.INT64 : None, - EcoffBt.UINT64 : None, - } - c_tq_str = { - EcoffTq.NIL : "", - EcoffTq.PTR : "*", - EcoffTq.PROC : "()", - EcoffTq.ARRAY : "[]", - EcoffTq.FAR : "/* FAR */", - EcoffTq.VOL : "volatile", - EcoffTq.CONST : "const", - } - - if self.index == 0xFFFFF: - # no type info - return "", None - - aux = self.fdr.auxs[self.index + ind] - ind += 1 - type_str = "" - - bit_width = None - if aux.ti.fBitfield == 1: - bit_width = self.fdr.auxs[self.index + ind].isym - ind += 1 - - if aux.ti.bt in [EcoffBt.STRUCT, EcoffBt.UNION, EcoffBt.ENUM, EcoffBt.TYPEDEF]: - type_ref_aux = self.fdr.auxs[self.index + ind] - ind += 1 - - fd_ref_idx = type_ref_aux.rndx.rfd - if fd_ref_idx == 4095: - fd_ref_idx = self.fdr.auxs[self.index + ind].isym - ind += 1 - - fdr_ref = self.fdr.parent.fdrs[fd_ref_idx] - sym_ref = fdr_ref.symrs[type_ref_aux.rndx.index] - # now we have the reference to the stStruct, stUnion, stEnum, or stTypeDef - type_str += f"{sym_ref.type_name if sym_ref.type_name is not None else sym_ref.name}" - else: - type_str += f"{c_bt_names[aux.ti.bt]}" - - # TODO improve emitting qualified types - tqs = "" - for tq in aux.ti.tqs: - if tq == EcoffTq.NIL: - continue - if tq == EcoffTq.ARRAY: - ind += 2 # skips over some info such as the type of index (always int for C) - array_low_aux = self.fdr.auxs[self.index + ind] - array_high_aux = self.fdr.auxs[self.index + ind + 1] - stride_aux = self.fdr.auxs[self.index + ind + 2] - ind += 3 - tqs += "[" - if array_high_aux.dnHigh != 0xFFFFFFFF: - tqs += '%d' % (array_high_aux.dnHigh + 1) - tqs += "]" - else: - tqs += c_tq_str[tq] - tqs += " " - tqs = tqs.strip() - if len(tqs) != 0: - type_str += " " + tqs - - return type_str, bit_width - - def __str__(self) -> str: - return f"""= EcoffSymr ============== {self.idx} -iss = 0x{self.iss:08X} -value = 0x{self.value:08X} -st = st{self.st.name} -sc = sc{self.sc.name} -_reserved = {self._reserved} -index = 0x{self.index:05X} -name = {self.name}""" - -class EcoffPdr: - """ - ECOFF Procedure Descriptor - - typedef struct sPDR { - s32 addr; /* memory address of start of procedure */ - s32 isym; /* start of local symbol entries */ - s32 iline; /* start of line number entries */ - s32 regmask; /* save register mask */ - s32 regoffset; /* save register offset */ - s32 iopt; /* start of optimization symbol entries */ - s32 fregmask; /* save floating point register mask */ - s32 fregoffset; /* save floating point register offset */ - s32 frameoffset; /* frame size */ - u16 framereg; /* frame pointer register */ - u16 pcreg; /* offset or reg of return pc */ - s32 lnLow; /* lowest line in the procedure */ - s32 lnHigh; /* highest line in the procedure */ - s32 cbLineOffset; /* byte offset for this procedure from the fd base */ -#ifdef 64_BIT - // TODO there's a bitfield in here - s32 gpPrologue; /* byte size of GP prologue */ - s32 gpUsed; /* true if the procedure uses GP */ - s32 regFrame; /* true if register frame procedure */ - s32 prof; /* true if compiled with -pg */ - s32 localOffset; /* offset of local variables from vfp */ -#endif - } tPDR, *pPDR; // size = 0x34 - """ - SIZE = 0x34 - - def __init__(self, fdr, data) -> None: - self.parent = fdr - self.data = data[:EcoffPdr.SIZE] - - self.addr, self.isym, self.iline, self.regmask, \ - self.regoffset, self.iopt, self.fregmask, self.fregoffset, \ - self.frameoffset, self.framereg, self.pcreg, self.lnLow, \ - self.lnHigh, self.cbLineOffset = struct.unpack(">IIIIIIIIIHHIII", self.data) - - self.symrs = [] - - i = self.isym - symr = self.parent.symrs[i] - assert symr.st == EcoffSt.PROC or symr.st == EcoffSt.STATICPROC - # Inherit procedure name from procedure symbol - self.name = symr.name - - self.symrs.append(symr) - while not (symr.st == EcoffSt.END and symr.name == self.symrs[0].name): - i += 1 - symr = self.parent.symrs[i] - self.symrs.append(symr) - - assert symr.st == EcoffSt.END and symr.sc == EcoffSc.TEXT - self.size = symr.value # value field of an stEND and scTEXT symbol is the procedure size - assert self.size % 4 == 0 - - # indexed by asm word offset from proc start - self.lines = [] - - # ilineMax = self.parent.parent.hdrr.ilineMax - # cbLine = self.parent.parent.hdrr.cbLine - # cbLineOffset = self.parent.parent.hdrr.cbLineOffset - # ilineBase = self.parent.ilineBase - # cline = self.parent.cline - # cbLineOffset = self.parent.cbLineOffset - # cbLine = self.parent.cbLine - # lnLow = self.lnLow - # lnHigh = self.lnHigh - # iline = self.iline - - elf_data = self.parent.parent.parent.data - - line_no = self.lnLow # first line in the procedure - line_data = self.parent.parent.hdrr.cbLineOffset + self.parent.cbLineOffset + self.cbLineOffset - # line_end = self.parent.parent.hdrr.cbLineOffset + self.parent.cbLineOffset + self.parent.cbLine - - # print(self) - # print(f"{self.name} [{self.lnLow}:{self.lnHigh}]") - # print(self.size//4) - while len(self.lines) < self.size//4: - # assert line_data < line_end , "Overflow in line numbers table" - - liner = EcoffLiner(elf_data[line_data:]) - line_no += liner.delta - # if line_no < self.lnLow or line_no > self.lnHigh: - # break - - # print(liner) - for i in range(liner.count): - # print(f"[{len(self.lines)}] {line_no}") - self.lines.append(line_no) - - line_data += len(liner.data) - - def lookup_sym(self, value, type=-1): - for sym in self.symrs: - if sym.value == value and (type == -1 or type == sym.st): - return sym - return None - - def __str__(self) -> str: - return f"""= EcoffPdr =============== -addr = 0x{self.addr:08X} -isym = 0x{self.isym:08X} -iline = 0x{self.iline:08X} -regmask = 0b{self.regmask:032b} -regoffset = 0x{self.regoffset:08X} -iopt = 0x{self.iopt:08X} -fregmask = 0b{self.fregmask:032b} -fregoffset = 0x{self.fregoffset:08X} -frameoffset = 0x{self.frameoffset:08X} -framereg = ${self.framereg} -pcreg = ${self.pcreg} -lnLow = {self.lnLow} -lnHigh = {self.lnHigh} -cbLineOffset = 0x{self.cbLineOffset:08X} -name = {self.name}""" - -class EcoffFdr: - """ - ECOFF File Descriptor - - typedef struct sFDR { - u32 adr; /* memory address of beginning of file */ - s32 rss; /* file name (of source, if known) */ - s32 issBase; /* file's string space */ - s32 cbSs; /* number of bytes in the ss */ - s32 isymBase; /* beginning of symbols */ - s32 csym; /* count file's of symbols */ - s32 ilineBase; /* file's line symbols */ - s32 cline; /* count of file's line symbols */ - s32 ioptBase; /* file's optimization entries */ - s32 copt; /* count of file's optimization entries */ - u16 ipdFirst; /* start of procedures for this file */ - u16 cpd; /* count of procedures for this file */ - s32 iauxBase; /* file's auxiliary entries */ - s32 caux; /* count of file's auxiliary entries */ - s32 rfdBase; /* index into the file indirect table */ - s32 crfd; /* count file indirect entries */ - EcoffLanguageCode lang : 5; /* language for this file */ - u32 fMerge : 1; /* whether this file can be merged */ - u32 fReadin : 1; /* true if it was read in (not just created) */ - u32 fBigEndian : 1; /* true if AUXU's are big endian */ - u32 glevel : 2; /* level this file was compiled with */ - u32 _reserved : 20; /* reserved bits */ - s32 cbLineOffset; /* byte offset from header for this file ln's */ - s32 cbLine; /* size of lines for this file */ - } tFDR, *pFDR; // size = 0x48 - """ - SIZE = 0x48 - - @staticmethod - def from_binary(mdebug, i): - # Init - if 'init' not in EcoffFdr.from_binary.__dict__: - EcoffFdr.from_binary.cache = {} - EcoffFdr.from_binary.init = True - # Parent Init - if mdebug not in EcoffFdr.from_binary.cache: - EcoffFdr.from_binary.cache[mdebug] = {} - # Cache hit - if i in EcoffFdr.from_binary.cache[mdebug]: - return EcoffFdr.from_binary.cache[mdebug][i] - # Cache miss - cbFdOffset = mdebug.hdrr.cbFdOffset - elf_data = mdebug.parent.data - EcoffFdr.from_binary.cache[mdebug][i] = EcoffFdr(mdebug, elf_data[cbFdOffset+i*EcoffFdr.SIZE:cbFdOffset+(i+1)*EcoffFdr.SIZE]) - return EcoffFdr.from_binary.cache[mdebug][i] - - def __init__(self, mdebug, data) -> None: - self.parent = mdebug - self.data = data[:EcoffFdr.SIZE] - - self.adr, self.rss, self.issBase, self.cbSs, \ - self.isymBase, self.csym, self.ilineBase, self.cline, \ - self.ioptBase, self.copt, self.ipdFirst, self.cpd, \ - self.iauxBase, self.caux, self.rfdBase, self.crfd, \ - bits, self.cbLineOffset, self.cbLine = struct.unpack(">IIIIIIIIIIHHIIIIIII", self.data) - - self.lang = EcoffLanguageCode(get_bitrange(bits, 27, 5)) - self.fMerge = get_bitrange(bits, 26, 1) - self.fReadin = get_bitrange(bits, 25, 1) - self.fBigEndian = get_bitrange(bits, 24, 1) - self.glevel = get_bitrange(bits, 22, 2) - self._reserved = get_bitrange(bits, 2, 20) - - self.name = self.parent.read_string(self.issBase + self.rss) - - # print(self) - - hdrr = self.parent.hdrr - elf_data = self.parent.parent.data - - # Aux Symbols - self.auxs = [] - for i in range(self.caux): - i += self.iauxBase - assert i < hdrr.iauxMax , "Out of bounds in Auxiliary Symbol Table" - aux = EcoffAux(self, elf_data[hdrr.cbAuxOffset+i*EcoffAux.SIZE:][:EcoffAux.SIZE], self.fBigEndian) - self.auxs.append(aux) - - # Symbols - self.symrs = [] - for i in range(self.csym): - j = i + self.isymBase - assert j < hdrr.isymMax , "Out of bounds in Local Symbol Table" - symr = EcoffSymr(self, i, elf_data[hdrr.cbSymOffset+j*EcoffSymr.SIZE:hdrr.cbSymOffset+(j+1)*EcoffSymr.SIZE]) - self.symrs.append(symr) - for symr in self.symrs: - symr.link_syms() - - # PDRs - self.pdrs = [] - for i in range(self.cpd): - i += self.ipdFirst - assert i < hdrr.ipdMax , "Out of bounds in Procedure Descriptor Table" - pdr = EcoffPdr(self, elf_data[hdrr.cbPdOffset+i*EcoffPdr.SIZE:hdrr.cbPdOffset+(i+1)*EcoffPdr.SIZE]) - self.pdrs.append(pdr) - - self.size = sum([pdr.size for pdr in self.pdrs]) - - def late_init(self): - for symr in self.symrs: - symr.late_init() - - def pdr_forname(self, procedure_name): - for pdr in self.pdrs: - if pdr.name == procedure_name: - return pdr - return None - - def pdr_foranyaddr(self, addr): - for pdr in self.pdrs: - if pdr.addr <= addr and pdr.addr + pdr.size > addr: - return pdr - return None - - def pdr_foraddr(self, addr): - for pdr in self.pdrs: - if pdr.addr == addr: - return pdr - return None - - def read_string(self, index): - return self.parent.read_string(self.issBase + index) - - def c_str(self) -> str: - """ - C prettyprint file - """ - def print_symbol(symr): - return f"{symr.st.name} :: {symr.c_repr}" - - indent = 0 - out = f"File: {self.name}\n" - - for symr in self.symrs: - if symr.st in [EcoffSt.END]: - indent -= 2 - - out += " " * indent - out += print_symbol(symr) - out += "\n" - - if symr.st in [EcoffSt.FILE, EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.PROC, EcoffSt.STATICPROC, EcoffSt.BLOCK]: - indent += 2 - - return out - - def __str__(self) -> str: - return f"""= EcoffFdr =============== -adr = 0x{self.adr:08X} -rss = 0x{self.rss:08X} -issBase = 0x{self.issBase:08X} -cbSs = 0x{self.cbSs:08X} -isymBase = 0x{self.isymBase:08X} -csym = 0x{self.csym:08X} -ilineBase = 0x{self.ilineBase:08X} -cline = 0x{self.cline:08X} -ioptBase = 0x{self.ioptBase:08X} -copt = 0x{self.copt:08X} -ipdFirst = 0x{self.ipdFirst:08X} -cpd = 0x{self.cpd:08X} -iauxBase = 0x{self.iauxBase:08X} -caux = 0x{self.caux:08X} -rfdBase = 0x{self.rfdBase:08X} -crfd = 0x{self.crfd:08X} -lang = {self.lang.name} -fMerge = {bool(self.fMerge)} -fReadin = {bool(self.fReadin)} -fBigEndian = {bool(self.fBigEndian)} -glevel = {self.glevel} -_reserved = {self._reserved} -cbLineOffset = 0x{self.cbLineOffset:08X} -cbLine = 0x{self.cbLine:08X} -name = {self.name}""" - -class EcoffHDRR: - """ - Symbolic Header - - typedef struct sHDRR { - u16 magic; /* 0x7009 */ - u16 vstamp; /* version stamp */ - s32 ilineMax; /* number of line number entries */ - s32 cbLine; /* number of bytes for line number entries */ - s32 cbLineOffset; /* offset to start of line number entries */ - s32 idnMax; /* max index into dense number table */ - s32 cbDnOffset; /* offset to start dense number table */ - s32 ipdMax; /* number of procedures */ - s32 cbPdOffset; /* offset to procedure descriptor table */ - s32 isymMax; /* number of local symbols */ - s32 cbSymOffset; /* offset to start of local symbols */ - s32 ioptMax; /* max index into optimization symbol entries */ - s32 cbOptOffset; /* offset to optimization symbol entries */ - s32 iauxMax; /* number of auxillary symbol entries */ - s32 cbAuxOffset; /* offset to start of auxillary symbol entries */ - s32 issMax; /* max index into local strings */ - s32 cbSsOffset; /* offset to start of local strings */ - s32 issExtMax; /* max index into external strings */ - s32 cbSsExtOffset; /* offset to start of external strings */ - s32 ifdMax; /* number of file descriptor entries */ - s32 cbFdOffset; /* offset to file descriptor table */ - s32 crfd; /* number of relative file descriptor entries */ - s32 cbRfdOffset; /* offset to relative file descriptor table */ - s32 iextMax; /* max index into external symbols */ - s32 cbExtOffset; /* offset to start of external symbol entries */ - } tHDRR, *pHDRR; // size = 0x60 - """ - HDRR_MAGIC = 0x7009 - SIZE = 0x60 - - def __init__(self, data) -> None: - self.data = data[:EcoffHDRR.SIZE] - - self.magic, self.vstamp, self.ilineMax, self.cbLine, \ - self.cbLineOffset, self.idnMax, self.cbDnOffset, self.ipdMax, \ - self.cbPdOffset, self.isymMax, self.cbSymOffset, self.ioptMax, \ - self.cbOptOffset, self.iauxMax, self.cbAuxOffset, self.issMax, \ - self.cbSsOffset, self.issExtMax, self.cbSsExtOffset, self.ifdMax, \ - self.cbFdOffset, self.crfd, self.cbRfdOffset, self.iextMax, \ - self.cbExtOffset = struct.unpack(">HHIIIIIIIIIIIIIIIIIIIIIII", self.data) - - assert self.magic == EcoffHDRR.HDRR_MAGIC , f"Symbolic Header magic value is incorrect. Got 0x{self.magic:04X}, expected 0x{EcoffHDRR.HDRR_MAGIC:04X}" - - def __str__(self) -> str: - return f"""= EcoffHDRR ============== -magic = 0x{self.magic:04X} -vstamp = 0x{self.vstamp:04X} -ilineMax = 0x{self.ilineMax:08X} -cbLine = 0x{self.cbLine:08X} -cbLineOffset = 0x{self.cbLineOffset:08X} -idnMax = 0x{self.idnMax:08X} -cbDnOffset = 0x{self.cbDnOffset:08X} -ipdMax = 0x{self.ipdMax:08X} -cbPdOffset = 0x{self.cbPdOffset:08X} -isymMax = 0x{self.isymMax:08X} -cbSymOffset = 0x{self.cbSymOffset:08X} -ioptMax = 0x{self.ioptMax:08X} -cbOptOffset = 0x{self.cbOptOffset:08X} -iauxMax = 0x{self.iauxMax:08X} -cbAuxOffset = 0x{self.cbAuxOffset:08X} -issMax = 0x{self.issMax:08X} -cbSsOffset = 0x{self.cbSsOffset:08X} -issExtMax = 0x{self.issExtMax:08X} -cbSsExtOffset = 0x{self.cbSsExtOffset:08X} -ifdMax = 0x{self.ifdMax:08X} -cbFdOffset = 0x{self.cbFdOffset:08X} -crfd = 0x{self.crfd:08X} -cbRfdOffset = 0x{self.cbRfdOffset:08X} -iextMax = 0x{self.iextMax:08X} -cbExtOffset = 0x{self.cbExtOffset:08X}""" diff --git a/tools/mips_isa.py b/tools/mips_isa.py deleted file mode 100644 index d99dcdaa..00000000 --- a/tools/mips_isa.py +++ /dev/null @@ -1,1309 +0,0 @@ -# TODO enum these constants -from enum import IntEnum, auto - -# Register IDs -class MipsGPReg(IntEnum): - R0 = 0 # 0 - AT = auto() # 1 - V0 = auto() # 2 - V1 = auto() # 3 - A0 = auto() # 4 - A1 = auto() # 5 - A2 = auto() # 6 - A3 = auto() # 7 - T0 = auto() # 8 - T1 = auto() # 9 - T2 = auto() # 10 - T3 = auto() # 11 - T4 = auto() # 12 - T5 = auto() # 13 - T6 = auto() # 14 - T7 = auto() # 15 - S0 = auto() # 16 - S1 = auto() # 17 - S2 = auto() # 18 - S3 = auto() # 19 - S4 = auto() # 20 - S5 = auto() # 21 - S6 = auto() # 22 - S7 = auto() # 23 - T8 = auto() # 24 - T9 = auto() # 25 - K0 = auto() # 26 - K1 = auto() # 27 - GP = auto() # 28 - SP = auto() # 29 - FP = auto() # 30 - RA = auto() # 31 - -class MipsFPReg(IntEnum): - F0 = 0 # 0 - F1 = auto() # 1 - F2 = auto() # 2 - F3 = auto() # 3 - F4 = auto() # 4 - F5 = auto() # 5 - F6 = auto() # 6 - F7 = auto() # 7 - F8 = auto() # 8 - F9 = auto() # 9 - F10 = auto() # 10 - F11 = auto() # 11 - F12 = auto() # 12 - F13 = auto() # 13 - F14 = auto() # 14 - F15 = auto() # 15 - F16 = auto() # 16 - F17 = auto() # 17 - F18 = auto() # 18 - F19 = auto() # 19 - F20 = auto() # 20 - F21 = auto() # 21 - F22 = auto() # 22 - F23 = auto() # 23 - F24 = auto() # 24 - F25 = auto() # 25 - F26 = auto() # 26 - F27 = auto() # 27 - F28 = auto() # 28 - F29 = auto() # 29 - F30 = auto() # 30 - F31 = auto() # 31 - -# Instruction Unique IDs -MIPS_INS_SLL = 0 -MIPS_INS_SRL = 1 -MIPS_INS_SRA = 2 -MIPS_INS_SLLV = 3 -MIPS_INS_SRLV = 4 -MIPS_INS_SRAV = 5 -MIPS_INS_JR = 6 -MIPS_INS_JALR = 7 -MIPS_INS_SYSCALL = 8 -MIPS_INS_BREAK = 9 -MIPS_INS_SYNC = 10 -MIPS_INS_MFHI = 11 -MIPS_INS_MTHI = 12 -MIPS_INS_MFLO = 13 -MIPS_INS_MTLO = 14 -MIPS_INS_DSLLV = 15 -MIPS_INS_DSRLV = 16 -MIPS_INS_DSRAV = 17 -MIPS_INS_MULT = 18 -MIPS_INS_MULTU = 19 -MIPS_INS_DIV = 20 -MIPS_INS_DIVU = 21 -MIPS_INS_DMULT = 22 -MIPS_INS_DMULTU = 23 -MIPS_INS_DDIV = 24 -MIPS_INS_DDIVU = 25 -MIPS_INS_ADD = 26 -MIPS_INS_ADDU = 27 -MIPS_INS_SUB = 28 -MIPS_INS_SUBU = 29 -MIPS_INS_AND = 30 -MIPS_INS_OR = 31 -MIPS_INS_XOR = 32 -MIPS_INS_NOR = 33 -MIPS_INS_SLT = 34 -MIPS_INS_SLTU = 35 -MIPS_INS_DADD = 36 -MIPS_INS_DADDU = 37 -MIPS_INS_DSUB = 38 -MIPS_INS_DSUBU = 39 -MIPS_INS_TGE = 40 -MIPS_INS_TGEU = 41 -MIPS_INS_TLT = 42 -MIPS_INS_TLTU = 43 -MIPS_INS_TEQ = 44 -MIPS_INS_TNE = 45 -MIPS_INS_DSLL = 46 -MIPS_INS_DSRL = 47 -MIPS_INS_DSRA = 48 -MIPS_INS_DSLL32 = 49 -MIPS_INS_DSRL32 = 50 -MIPS_INS_DSRA32 = 51 -MIPS_INS_BLTZ = 52 -MIPS_INS_BGEZ = 53 -MIPS_INS_BLTZL = 54 -MIPS_INS_BGEZL = 55 -MIPS_INS_TGEI = 56 -MIPS_INS_TGEIU = 57 -MIPS_INS_TLTI = 58 -MIPS_INS_TLTIU = 59 -MIPS_INS_TEQI = 60 -MIPS_INS_TNEI = 61 -MIPS_INS_BLTZAL = 62 -MIPS_INS_BGEZAL = 63 -MIPS_INS_BLTZALL = 64 -MIPS_INS_BGEZALL = 65 -MIPS_INS_J = 66 -MIPS_INS_JAL = 67 -MIPS_INS_BEQ = 68 -MIPS_INS_BNE = 69 -MIPS_INS_BLEZ = 70 -MIPS_INS_BGTZ = 71 -MIPS_INS_ADDI = 72 -MIPS_INS_ADDIU = 73 -MIPS_INS_SLTI = 74 -MIPS_INS_SLTIU = 75 -MIPS_INS_ANDI = 76 -MIPS_INS_ORI = 77 -MIPS_INS_XORI = 78 -MIPS_INS_LUI = 79 -MIPS_INS_MFC0 = 80 -MIPS_INS_MTC0 = 81 -MIPS_INS_TLBR = 82 -MIPS_INS_TLBWI = 83 -MIPS_INS_TLBWR = 84 -MIPS_INS_TLBP = 85 -MIPS_INS_ERET = 86 -MIPS_INS_MFC1 = 87 -MIPS_INS_DMFC1 = 88 -MIPS_INS_CFC1 = 89 -MIPS_INS_MTC1 = 90 -MIPS_INS_DMTC1 = 91 -MIPS_INS_CTC1 = 92 -MIPS_INS_BC1F = 93 -MIPS_INS_BC1T = 94 -MIPS_INS_BC1FL = 95 -MIPS_INS_BC1TL = 96 -MIPS_INS_ADD_S = 97 -MIPS_INS_SUB_S = 98 -MIPS_INS_MUL_S = 99 -MIPS_INS_DIV_S = 100 -MIPS_INS_SQRT_S = 101 -MIPS_INS_ABS_S = 102 -MIPS_INS_MOV_S = 103 -MIPS_INS_NEG_S = 104 -MIPS_INS_ROUND_L_S = 105 -MIPS_INS_TRUNC_L_S = 106 -MIPS_INS_CEIL_L_S = 107 -MIPS_INS_FLOOR_L_S = 108 -MIPS_INS_ROUND_W_S = 109 -MIPS_INS_TRUNC_W_S = 110 -MIPS_INS_CEIL_W_S = 111 -MIPS_INS_FLOOR_W_S = 112 -MIPS_INS_CVT_D_S = 113 -MIPS_INS_CVT_W_S = 114 -MIPS_INS_CVT_L_S = 115 -MIPS_INS_C_F_S = 116 -MIPS_INS_C_UN_S = 117 -MIPS_INS_C_EQ_S = 118 -MIPS_INS_C_UEQ_S = 119 -MIPS_INS_C_OLT_S = 120 -MIPS_INS_C_ULT_S = 121 -MIPS_INS_C_OLE_S = 122 -MIPS_INS_C_ULE_S = 123 -MIPS_INS_C_SF_S = 124 -MIPS_INS_C_NGLE_S = 125 -MIPS_INS_C_SEQ_S = 126 -MIPS_INS_C_NGL_S = 127 -MIPS_INS_C_LT_S = 128 -MIPS_INS_C_NGE_S = 129 -MIPS_INS_C_LE_S = 130 -MIPS_INS_C_NGT_S = 131 -MIPS_INS_ADD_D = 132 -MIPS_INS_SUB_D = 133 -MIPS_INS_MUL_D = 134 -MIPS_INS_DIV_D = 135 -MIPS_INS_SQRT_D = 136 -MIPS_INS_ABS_D = 137 -MIPS_INS_MOV_D = 138 -MIPS_INS_NEG_D = 139 -MIPS_INS_ROUND_L_D = 140 -MIPS_INS_TRUNC_L_D = 141 -MIPS_INS_CEIL_L_D = 142 -MIPS_INS_FLOOR_L_D = 143 -MIPS_INS_ROUND_W_D = 144 -MIPS_INS_TRUNC_W_D = 145 -MIPS_INS_CEIL_W_D = 146 -MIPS_INS_FLOOR_W_D = 147 -MIPS_INS_CVT_S_D = 148 -MIPS_INS_CVT_W_D = 149 -MIPS_INS_CVT_L_D = 150 -MIPS_INS_C_F_D = 151 -MIPS_INS_C_UN_D = 152 -MIPS_INS_C_EQ_D = 153 -MIPS_INS_C_UEQ_D = 154 -MIPS_INS_C_OLT_D = 155 -MIPS_INS_C_ULT_D = 156 -MIPS_INS_C_OLE_D = 157 -MIPS_INS_C_ULE_D = 158 -MIPS_INS_C_SF_D = 159 -MIPS_INS_C_NGLE_D = 160 -MIPS_INS_C_SEQ_D = 161 -MIPS_INS_C_NGL_D = 162 -MIPS_INS_C_LT_D = 163 -MIPS_INS_C_NGE_D = 164 -MIPS_INS_C_LE_D = 165 -MIPS_INS_C_NGT_D = 166 -MIPS_INS_CVT_S_W = 167 -MIPS_INS_CVT_D_W = 168 -MIPS_INS_CVT_S_L = 169 -MIPS_INS_CVT_D_L = 170 -MIPS_INS_BEQL = 171 -MIPS_INS_BNEL = 172 -MIPS_INS_BLEZL = 173 -MIPS_INS_BGTZL = 174 -MIPS_INS_DADDI = 175 -MIPS_INS_DADDIU = 176 -MIPS_INS_LDL = 177 -MIPS_INS_LDR = 178 -MIPS_INS_LB = 179 -MIPS_INS_LH = 180 -MIPS_INS_LWL = 181 -MIPS_INS_LW = 182 -MIPS_INS_LBU = 183 -MIPS_INS_LHU = 184 -MIPS_INS_LWR = 185 -MIPS_INS_LWU = 186 -MIPS_INS_SB = 187 -MIPS_INS_SH = 188 -MIPS_INS_SWL = 189 -MIPS_INS_SW = 190 -MIPS_INS_SDL = 191 -MIPS_INS_SDR = 192 -MIPS_INS_SWR = 193 -MIPS_INS_CACHE = 194 -MIPS_INS_LL = 195 -MIPS_INS_LWC1 = 196 -MIPS_INS_LWC2 = 197 -MIPS_INS_LLD = 198 -MIPS_INS_LDC1 = 199 -MIPS_INS_LDC2 = 200 -MIPS_INS_LD = 201 -MIPS_INS_SC = 202 -MIPS_INS_SWC1 = 203 -MIPS_INS_SWC2 = 204 -MIPS_INS_SCD = 205 -MIPS_INS_SDC1 = 206 -MIPS_INS_SDC2 = 207 -MIPS_INS_SD = 208 - -# RSP COP2 -MIPS_INS_VMULF = 209 -MIPS_INS_VMULU = 210 -MIPS_INS_VRNDP = 211 -MIPS_INS_VMULQ = 212 -MIPS_INS_VMUDL = 213 -MIPS_INS_VMUDM = 214 -MIPS_INS_VMUDN = 215 -MIPS_INS_VMUDH = 216 -MIPS_INS_VMACF = 217 -MIPS_INS_VMACU = 218 -MIPS_INS_VRNDN = 219 -MIPS_INS_VMACQ = 220 -MIPS_INS_VMADL = 221 -MIPS_INS_VMADM = 222 -MIPS_INS_VMADN = 223 -MIPS_INS_VMADH = 224 -MIPS_INS_VADD = 225 -MIPS_INS_VSUB = 226 -MIPS_INS_VABS = 227 -MIPS_INS_VADDC = 228 -MIPS_INS_VSUBC = 229 -MIPS_INS_VSAR = 230 -MIPS_INS_VLT = 231 -MIPS_INS_VEQ = 232 -MIPS_INS_VNE = 233 -MIPS_INS_VGE = 234 -MIPS_INS_VCL = 235 -MIPS_INS_VCH = 236 -MIPS_INS_VCR = 237 -MIPS_INS_VMRG = 238 -MIPS_INS_VAND = 239 -MIPS_INS_VNAND = 240 -MIPS_INS_VOR = 241 -MIPS_INS_VNOR = 242 -MIPS_INS_VXOR = 243 -MIPS_INS_VNXOR = 244 -MIPS_INS_VRCP = 245 -MIPS_INS_VRCPL = 246 -MIPS_INS_VRCPH = 247 -MIPS_INS_VMOV = 248 -MIPS_INS_VRSQ = 249 -MIPS_INS_VRSQL = 250 -MIPS_INS_VRSQH = 251 -MIPS_INS_VNOP = 252 -MIPS_INS_LBV = 253 -MIPS_INS_LSV = 254 -MIPS_INS_LLV = 255 -MIPS_INS_LDV = 256 -MIPS_INS_LQV = 257 -MIPS_INS_LRV = 258 -MIPS_INS_LPV = 259 -MIPS_INS_LUV = 260 -MIPS_INS_LHV = 261 -MIPS_INS_LFV = 262 -MIPS_INS_LTV = 263 -MIPS_INS_SBV = 264 -MIPS_INS_SSV = 265 -MIPS_INS_SLV = 266 -MIPS_INS_SDV = 267 -MIPS_INS_SQV = 268 -MIPS_INS_SRV = 269 -MIPS_INS_SPV = 270 -MIPS_INS_SUV = 271 -MIPS_INS_SHV = 272 -MIPS_INS_SFV = 273 -MIPS_INS_SWV = 274 -MIPS_INS_STV = 275 -MIPS_INS_MFC2 = 276 -MIPS_INS_MTC2 = 277 -MIPS_INS_CFC2 = 278 -MIPS_INS_CTC2 = 279 - -# Pseudo-Instruction Unique IDs -MIPS_INS_BEQZ = 280 -MIPS_INS_BNEZ = 281 -MIPS_INS_B = 282 -MIPS_INS_NOP = 283 -MIPS_INS_MOVE = 284 -MIPS_INS_NEGU = 285 -MIPS_INS_NOT = 286 - -# Invalid Instruction Unique ID -MIPS_INS_INVALID = -1 - -# Op IDs -# MIPS_OP_RS = 0 -# MIPS_OP_RT = 0 -# MIPS_OP_RD = 0 -# MIPS_OP_IMM = 0 - -# Instruction Groups - -MIPS_BRANCH_LIKELY_INSNS = [ - MIPS_INS_BEQL, MIPS_INS_BGEZALL, - MIPS_INS_BGEZL, MIPS_INS_BGTZL, - MIPS_INS_BLEZL, MIPS_INS_BLTZALL, - MIPS_INS_BLTZL, MIPS_INS_BNEL, - MIPS_INS_BC1TL, MIPS_INS_BC1FL, -] - -MIPS_BRANCH_INSNS = [ - *MIPS_BRANCH_LIKELY_INSNS, - - MIPS_INS_BEQ, - MIPS_INS_BGEZ, MIPS_INS_BGEZAL, - MIPS_INS_BGTZ, - MIPS_INS_BNE, - MIPS_INS_BLTZ, MIPS_INS_BLTZAL, - MIPS_INS_BLEZ, - MIPS_INS_BC1T, MIPS_INS_BC1F, - - MIPS_INS_BEQZ, - MIPS_INS_BNEZ, - MIPS_INS_B, -] - -MIPS_JUMP_INSNS = [ - MIPS_INS_JAL, MIPS_INS_JALR, MIPS_INS_J, MIPS_INS_JR -] - -MIPS_DELAY_SLOT_INSNS = [ - *MIPS_BRANCH_INSNS, *MIPS_JUMP_INSNS -] - -MIPS_FP_LOAD_INSNS = [ - MIPS_INS_LWC1, MIPS_INS_LDC1 -] - -MIPS_LOAD_INSNS = [ - MIPS_INS_LB, MIPS_INS_LBU, - MIPS_INS_LH, MIPS_INS_LHU, - MIPS_INS_LW, MIPS_INS_LWL, MIPS_INS_LWR, MIPS_INS_LWU, - MIPS_INS_LD, MIPS_INS_LDL, MIPS_INS_LDR, - MIPS_INS_LL, MIPS_INS_LLD, - *MIPS_FP_LOAD_INSNS -] - -MIPS_FP_STORE_INSNS = [ - MIPS_INS_SWC1, MIPS_INS_SDC1 -] - -MIPS_STORE_INSNS = [ - MIPS_INS_SB, - MIPS_INS_SH, - MIPS_INS_SW, MIPS_INS_SWL, MIPS_INS_SWR, - MIPS_INS_SD, MIPS_INS_SDL, MIPS_INS_SDR, - MIPS_INS_SC, MIPS_INS_SCD, - *MIPS_FP_STORE_INSNS -] - -MIPS_LOAD_STORE_INSNS = [ - *MIPS_LOAD_INSNS, - *MIPS_STORE_INSNS, -] - -MIPS_FP_LOAD_STORE_INSNS = [ - *MIPS_FP_LOAD_INSNS, *MIPS_FP_STORE_INSNS -] - -RSP_VECTOR_LOAD_STORES = [ - MIPS_INS_LBV, MIPS_INS_LSV, MIPS_INS_LLV, MIPS_INS_LDV, - MIPS_INS_LQV, MIPS_INS_LRV, MIPS_INS_LPV, MIPS_INS_LUV, - MIPS_INS_LHV, MIPS_INS_LFV, MIPS_INS_LTV, - - MIPS_INS_SBV, MIPS_INS_SSV, MIPS_INS_SLV, MIPS_INS_SDV, - MIPS_INS_SQV, MIPS_INS_SRV, MIPS_INS_SPV, MIPS_INS_SUV, - MIPS_INS_SHV, MIPS_INS_SFV, MIPS_INS_SWV, MIPS_INS_STV, -] - -# Labels - -# These labels can be referenced from pointers/loads/stores/etc. -LABEL_TYPE_FUNC = 0 -LABEL_TYPE_JTBL = 1 -LABEL_TYPE_DATA = 2 -LABEL_TYPE_BRANCH = 3 - -# Unknown -LABEL_TYPE_UNK = 20 - -class MipsLabel: - """ - Label - """ - - def __init__(self, vaddr, name=None, lbl_type=LABEL_TYPE_UNK, is_global=None) -> None: - self.name = name - self.vaddr = vaddr - self.lbl_type = lbl_type - self.is_global = is_global or (False if (lbl_type == LABEL_TYPE_BRANCH) else True) - - def __str__(self): - if self.name is not None: - return self.name - - if self.lbl_type == LABEL_TYPE_FUNC: - return f"func_{self.vaddr:08X}" - elif self.lbl_type == LABEL_TYPE_JTBL: - return f"jtbl_{self.vaddr:08X}" - elif self.lbl_type == LABEL_TYPE_BRANCH: - return f".L{self.vaddr:08X}" - elif (self.lbl_type == LABEL_TYPE_DATA or self.lbl_type == LABEL_TYPE_UNK): - return f"D_{self.vaddr:08X}" - assert False , f"Unimplemented default name for label type {self.lbl_type}" - -# Register Names - -mips_gpr_names = ( - "$zero", - "$at", - "$v0", "$v1", - "$a0", "$a1", "$a2", "$a3", - "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", - "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", - "$t8", "$t9", - "$k0", "$k1", - "$gp", - "$sp", - "$fp", - "$ra", -) - -rsp_gpr_names = ( - "$zero", - "$1", "$2", "$3", "$4", "$5", "$6", - "$7", "$8", "$9", "$10", "$11", "$12", - "$13", "$14", "$15", "$16", "$17", "$18", - "$19", "$20", "$21", "$22", "$23", "$24", - "$25", "$26", "$27", "$28", "$29", "$30", - "$ra", -) - -mips_cop0_names = ( - "Index" , "Random" , "EntryLo0" , "EntryLo1" , - "Context" , "PageMask" , "Wired" , "Reserved07", - "BadVaddr" , "Count" , "EntryHi" , "Compare" , - "Status" , "Cause" , "EPC" , "PRevID" , - "Config" , "LLAddr" , "WatchLo" , "WatchHi" , - "XContext" , "Reserved21", "Reserved22", "Reserved23", - "Reserved24", "Reserved25", "PErr" , "CacheErr" , - "TagLo" , "TagHi" , "ErrorEPC" , "Reserved31", -) - -rsp_cop0_names = ( - "SP_MEM_ADDR", "SP_DRAM_ADDR", "SP_RD_LEN" , "SP_WR_LEN" , - "SP_STATUS" , "SP_DMA_FULL" , "SP_DMA_BUSY" , "SP_SEMAPHORE", - "DPC_START" , "DPC_END" , "DPC_CURRENT" , "DPC_STATUS" , - "DPC_CLOCK" , "DPC_BUFBUSY" , "DPC_PIPEBUSY", "DPC_TMEM" , -) - -mips_cop1_names = ( - "$f0", "$f1", "$f2", "$f3", - "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", - "$f12", "$f13", "$f14", "$f15", - "$f16", "$f17", "$f18", "$f19", - "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", - # Status register - "FpCsr", -) - -rsp_cop2_names = ( - "$v0", "$v1", "$v2", "$v3", "$v4", "$v5", "$v6", "$v7", - "$v8", "$v9", "$v10", "$v11", "$v12", "$v13", "$v14", "$v15", - "$v16", "$v17", "$v18", "$v19", "$v20", "$v21", "$v22", "$v23", - "$v24", "$v25", "$v26", "$v27", "$v28", "$v29", "$v30", "$v31", -) - -rsp_cop2_ctrl_names = ( - '$vco', '$vcc', '$vce' -) - -# Instruction sets - -class MipsAbi: - def __init__(self, name, gpr_names, cop0_names, cop1_names, cop2_names): - self.name = name - self.gpr_names, self.cop0_names, self.cop1_names, self.cop2_names = \ - gpr_names, cop0_names, cop1_names, cop2_names - -ABI_VR4300 = MipsAbi("VR4300", mips_gpr_names, mips_cop0_names, mips_cop1_names, None ) -ABI_RSP = MipsAbi("RSP", rsp_gpr_names, rsp_cop0_names, None, rsp_cop2_names) - -# Instruction field fetching - -def sign_extend_6(value): - if value & (1 << (6 - 1)): - return value - (1 << 6) - return value - -def sign_extend_16(value): - return (value & 0x7FFF) - (value & 0x8000) - -def mask_shift(v, s, w): - return (v >> s) & ((1 << w) - 1) - -mips_get_field = lambda raw,vaddr : mask_shift(raw, 26, 6) -mips_get_special = lambda raw,vaddr : mask_shift(raw, 0, 6) -mips_get_cop0 = lambda raw,vaddr : mask_shift(raw, 21, 5) -mips_get_cop1 = lambda raw,vaddr : mask_shift(raw, 21, 5) -mips_get_cop2 = lambda raw,vaddr : mask_shift(raw, 21, 4) -mips_get_regimm = lambda raw,vaddr : mask_shift(raw, 16, 5) -mips_get_tlb = lambda raw,vaddr : mask_shift(raw, 0, 5) -mips_get_function = lambda raw,vaddr : mask_shift(raw, 0, 6) - -mips_get_cond = lambda raw,vaddr : mask_shift(raw, 0, 4) -mips_get_fd = lambda raw,vaddr : MipsFPReg(mask_shift(raw, 6, 5)) -mips_get_fs = lambda raw,vaddr : MipsFPReg(mask_shift(raw, 11, 5)) -mips_get_ft = lambda raw,vaddr : MipsFPReg(mask_shift(raw, 16, 5)) -mips_get_fmt = lambda raw,vaddr : mask_shift(raw, 21, 5) -mips_get_ndtf = lambda raw,vaddr : mask_shift(raw, 16, 2) - -mips_get_target = lambda raw,vaddr : ((vaddr & 0xFC000000) | (mask_shift(raw, 0, 26) << 2)) -mips_get_offset = lambda raw,vaddr : vaddr + 4 + sign_extend_16(mask_shift(raw, 0, 16)) * 4 -mips_get_imm = lambda raw,vaddr : mask_shift(raw, 0, 16) - -mips_get_base = lambda raw,vaddr : MipsGPReg(mask_shift(raw, 21, 5)) - -mips_get_cd = lambda raw,vaddr : mask_shift(raw, 11, 5) - -mips_get_code = lambda raw,vaddr : (mask_shift(raw, 6, 20) << 6) >> 16 -mips_get_op = lambda raw,vaddr : mask_shift(raw, 16, 5) - -mips_get_sa = lambda raw,vaddr : mask_shift(raw, 6, 5) - -mips_get_rd = lambda raw,vaddr : MipsGPReg(mask_shift(raw, 11, 5)) -mips_get_rs = lambda raw,vaddr : MipsGPReg(mask_shift(raw, 21, 5)) -mips_get_rt = lambda raw,vaddr : MipsGPReg(mask_shift(raw, 16, 5)) - -rsp_load_store_multiplier = { - 0b00000 : 0x01, # lbv, sbv - 0b00001 : 0x02, # lsv, ssv - 0b00010 : 0x04, # llv, slv - 0b00011 : 0x08, # ldv, sdv - 0b00100 : 0x10, # lqv, sqv - 0b00101 : 0x10, # lrv, srv - 0b00110 : 0x08, # lpv, spv - 0b00111 : 0x08, # luv, suv - - 0b01000 : 0x02, # lhv, shv - 0b01001 : 0x04, # lfv, sfv - 0b01010 : 0x10, # swv - 0b01011 : 0x10, # ltv, stv -} - -mips_get_vc = lambda raw,vaddr : mask_shift(raw, 11, 5) -mips_get_vd = lambda raw,vaddr : mask_shift(raw, 6, 5) -mips_get_vs = lambda raw,vaddr : mask_shift(raw, 11, 5) -mips_get_vt = lambda raw,vaddr : mask_shift(raw, 16, 5) -mips_get_elem = lambda raw,vaddr : mask_shift(raw, 21, 4) -mips_get_elemd = lambda raw,vaddr : mask_shift(raw, 7, 4) -mips_get_cop2_func = lambda raw,vaddr : mask_shift(raw, 25, 1) -mips_get_lwc2 = lambda raw,vaddr : mask_shift(raw, 11, 5) -mips_get_swc2 = lambda raw,vaddr : mask_shift(raw, 11, 5) -mips_get_voffset = lambda raw,vaddr : sign_extend_6(mask_shift(raw, 0, 6)) * rsp_load_store_multiplier[mips_get_lwc2(raw,vaddr)] - -# Formatting - -def resolve_pseudo_insn(insn): - # move varies between assemblers - # IDO - move_insn = MIPS_INS_OR - # GCC - # move_insn = MIPS_INS_ADDU - - if insn.id == MIPS_INS_SLL and insn.rd == MipsGPReg.R0 and insn.rt == MipsGPReg.R0 and insn.sa == 0: - return MIPS_INS_NOP, "nop", (), () - elif insn.id == MIPS_INS_BEQ and insn.rs == MipsGPReg.R0 and insn.rt == MipsGPReg.R0: - return MIPS_INS_B, "b", ("offset",), (False,) - elif insn.id == move_insn and insn.rt == MipsGPReg.R0: - return MIPS_INS_MOVE, "move", ("rd","rs"), (True,False) - elif insn.id == MIPS_INS_BEQ and insn.rt == MipsGPReg.R0: - return MIPS_INS_BEQZ, "beqz", ("rs","offset"), (False,False) - elif insn.id == MIPS_INS_BNE and insn.rt == MipsGPReg.R0: - return MIPS_INS_BNEZ, "bnez", ("rs","offset"), (False,False) - elif insn.id == MIPS_INS_SUBU and insn.rs == MipsGPReg.R0: - return MIPS_INS_NEGU, "negu", ("rd","rt"), (True,False) - elif insn.id == MIPS_INS_NOR and insn.rt == MipsGPReg.R0: - return MIPS_INS_NOT, "not", ("rd","rs"), (True,False) - else: - return insn.id, insn.mnemonic, insn.fields, insn.writes - -def format_hex(v, signed, zeros, no_zero): - if abs(v) < 10: - if v == 0 and no_zero: - return "" - return f"{v}" - elif not signed: - return f"0x{v:{f'0{zeros}' if zeros > 0 else ''}x}" - else: - return f"{v:#x}" - -def format_vector_elem(insn, elem): - if insn.id in RSP_VECTOR_LOAD_STORES: - return f"[{elem}]" - elif (elem & 8) == 8: - return f"[{elem & 7}]" - elif (elem & 0xC) == 4: - return f"[{elem & 3}h]" - elif (elem & 0xE) == 2: - return f"[{elem & 1}q]" - else: - return "" - -mips_field_formatters = { - 'code' : (lambda insn : f'{insn.code}' if insn.code != 0 else ''), - 'cd' : (lambda insn : insn.abi.cop0_names[insn.cd]), - 'rd' : (lambda insn : insn.abi.gpr_names[insn.rd]), - 'rs' : (lambda insn : insn.abi.gpr_names[insn.rs]), - 'rt' : (lambda insn : insn.abi.gpr_names[insn.rt]), - 'fd' : (lambda insn : insn.abi.cop1_names[insn.fd]), - 'fs' : (lambda insn : insn.abi.cop1_names[insn.fs]), - 'ft' : (lambda insn : insn.abi.cop1_names[insn.ft]), - 'sa' : (lambda insn : format_hex(insn.sa, True, 0, False)), - 'op' : (lambda insn : format_hex(insn.op, False, 0, False)), - 'imm' : (lambda insn : format_hex(insn.imm, True, 0, False)), - 'offset(base)' : (lambda insn : f'{format_hex(insn.imm, True, 0, True)}({insn.abi.gpr_names[insn.base]})'), - 'offset' : (lambda insn : f'{format_hex(insn.offset, True, 0, False)}'), - 'target' : (lambda insn : f'{format_hex(insn.target, False, 0, False)}'), - 'vd' : (lambda insn : insn.abi.cop2_names[insn.vd]), - 'vs' : (lambda insn : insn.abi.cop2_names[insn.vs]), - 'vt' : (lambda insn : insn.abi.cop2_names[insn.vt]), - 'vt[e]' : (lambda insn : f"{insn.abi.cop2_names[insn.vt]}{format_vector_elem(insn, insn.elem)}"), - 'vt[ed]' : (lambda insn : f"{insn.abi.cop2_names[insn.vt]}{format_vector_elem(insn, insn.elemd)}"), - 'vd[e]' : (lambda insn : f"{insn.abi.cop2_names[insn.vd]}{format_vector_elem(insn, insn.elem)}"), - 'vd[ed]' : (lambda insn : f"{insn.abi.cop2_names[insn.vd]}{format_vector_elem(insn, insn.elemd)}"), - 'voffset' : (lambda insn : f'{format_hex(insn.voffset, True, 0, False)}'), - 'voffset(base)' : (lambda insn : f'{format_hex(insn.voffset, True, 0, True)}({insn.abi.gpr_names[insn.base]})'), - 'vc' : (lambda insn : rsp_cop2_ctrl_names[insn.vc]) -} - -class MipsInsn: - - def __init__(self, abi, raw, vaddr, values): - self.abi = abi - self.raw = raw - self.vaddr = vaddr - - if values is None: - values = MIPS_INS_INVALID, f"/* Invalid Instruction */ .4byte 0x{raw:08X}", (), () - - self.id, self.mnemonic, self.fields, self.writes = values - - # self.code = self.sa = self.op = self.cd = self.rd = self.rs = self.rt = self.fd = self.fs = self.ft = self.imm = self.offset = self.base = self.target = None - - for field in self.fields: - self.set_value(field) - - self.id, self.mnemonic, self.fields, self.writes = resolve_pseudo_insn(self) - - self.op_str = self.format_insn() - - def read_fields(self): - return [field for i,field in enumerate(self.fields) if self.writes[i] == False] - - def write_fields(self): - return [field for i,field in enumerate(self.fields) if self.writes[i] == True] - - def has_field(self, field): - return field in self.fields - - def format_field(self, field): - return mips_field_formatters[field](self) - - def format_insn(self): - return ", ".join([self.format_field(field) for field in self.fields]) - - def __str__(self): - return f"{self.mnemonic:12}{self.op_str}" - - # Field setters - - def set_code(self): - self.code = mips_get_code(self.raw, self.vaddr) - - def set_sa(self): - self.sa = mips_get_sa(self.raw, self.vaddr) - - def set_op(self): - self.op = mips_get_op(self.raw, self.vaddr) - - def set_cd(self): - self.cd = mips_get_cd(self.raw, self.vaddr) - - def set_rd(self): - self.rd = mips_get_rd(self.raw, self.vaddr) - - def set_rs(self): - self.rs = mips_get_rs(self.raw, self.vaddr) - - def set_rt(self): - self.rt = mips_get_rt(self.raw, self.vaddr) - - def set_fd(self): - self.fd = mips_get_fd(self.raw, self.vaddr) - - def set_fs(self): - self.fs = mips_get_fs(self.raw, self.vaddr) - - def set_ft(self): - self.ft = mips_get_ft(self.raw, self.vaddr) - - do_sign_extend = [ - MIPS_INS_ADDIU, MIPS_INS_SLTI, MIPS_INS_ADDI, MIPS_INS_DADDIU, - MIPS_INS_LB, MIPS_INS_LBU, - MIPS_INS_LH, MIPS_INS_LHU, - MIPS_INS_LW, MIPS_INS_LWL, MIPS_INS_LWR, MIPS_INS_LWU, - MIPS_INS_LWC1, - MIPS_INS_LD, MIPS_INS_LDL, MIPS_INS_LDR, - MIPS_INS_LDC1, - MIPS_INS_LL, MIPS_INS_LLD, - MIPS_INS_SB, - MIPS_INS_SH, - MIPS_INS_SW, MIPS_INS_SWL, MIPS_INS_SWR, - MIPS_INS_SWC1, - MIPS_INS_SD, MIPS_INS_SDL, MIPS_INS_SDR, - MIPS_INS_SDC1, - MIPS_INS_SC, MIPS_INS_SCD, - ] - - def set_imm(self): - self.imm = mips_get_imm(self.raw, self.vaddr) - if self.id in MipsInsn.do_sign_extend: # sign extended immediates - self.imm = sign_extend_16(self.imm) - - def set_offset(self): - self.offset = mips_get_offset(self.raw, self.vaddr) - - def set_base(self): - self.base = mips_get_base(self.raw, self.vaddr) - - def set_offset_base(self): - self.set_imm() - self.set_base() - - def set_target(self): - self.target = mips_get_target(self.raw, self.vaddr) - - def set_vd(self): - self.vd = mips_get_vd(self.raw, self.vaddr) - - def set_vs(self): - self.vs = mips_get_vs(self.raw, self.vaddr) - - def set_vt(self): - self.vt = mips_get_vt(self.raw, self.vaddr) - - def set_e(self): - self.elem = mips_get_elem(self.raw, self.vaddr) - - def set_ed(self): - self.elemd = mips_get_elemd(self.raw, self.vaddr) - - def set_vd_e(self): - self.set_vd() - self.set_e() - - def set_vt_ed(self): - self.set_vt() - self.set_ed() - - def set_vt_e(self): - self.set_vt() - self.set_e() - - def set_vd_ed(self): - self.set_vd() - self.set_ed() - - def set_voffset(self): - self.voffset = mips_get_voffset(self.raw, self.vaddr) - - def set_voffset_base(self): - self.set_voffset() - self.set_base() - - def set_vc(self): - self.vc = mips_get_vc(self.raw, self.vaddr) - - field_setters = { - 'code' : set_code, - 'sa' : set_sa, - 'op' : set_op, - 'cd' : set_cd, - 'rd' : set_rd, - 'rs' : set_rs, - 'rt' : set_rt, - 'fd' : set_fd, - 'fs' : set_fs, - 'ft' : set_ft, - 'imm' : set_imm, - 'offset' : set_offset, - 'base' : set_base, - 'offset(base)' : set_offset_base, - 'target' : set_target, - 'vd' : set_vd, - 'vs' : set_vs, - 'vt' : set_vt, - 'vt[e]' : set_vt_e, - 'vt[ed]' : set_vt_ed, - 'vd[e]' : set_vd_e, - 'vd[ed]' : set_vd_ed, - 'voffset' : set_voffset, - 'voffset(base)' : set_voffset_base, - 'vc' : set_vc - } - - def set_value(self, name): - MipsInsn.field_setters[name](self) - - # Field getters - - field_getters = { - 'code' : (lambda insn: insn.code), - 'sa' : (lambda insn: insn.sa), - 'op' : (lambda insn: insn.op), - 'cd' : (lambda insn: insn.cd), - 'rd' : (lambda insn: insn.rd), - 'rs' : (lambda insn: insn.rs), - 'rt' : (lambda insn: insn.rt), - 'fd' : (lambda insn: insn.fd), - 'fs' : (lambda insn: insn.fs), - 'ft' : (lambda insn: insn.ft), - 'imm' : (lambda insn: insn.imm), - 'offset' : (lambda insn: insn.offset), - 'base' : (lambda insn: insn.base), - 'offset(base)' : (lambda insn: (insn.offset, insn.base)), - 'target' : (lambda insn: insn.target), - 'vd' : (lambda insn: insn.vd), - 'vs' : (lambda insn: insn.vs), - 'vt' : (lambda insn: insn.vt), - 'vt[e]' : (lambda insn: (insn.vt, insn.elem)), - 'vt[ed]' : (lambda insn: (insn.vt, insn.elemd)), - 'vd[e]' : (lambda insn: (insn.vd, insn.elem)), - 'vd[ed]' : (lambda insn: (insn.vd, insn.elemd)), - 'voffset' : (lambda insn: insn.voffset), - 'voffset(base)' : (lambda insn: (insn.voffset, insn.base)), - 'vc' : (lambda insn: insn.vc), - } - - def value_forname(self, name): - return MipsInsn.field_getters[name](self) - -def fetch_insn(raw, vaddr, insn_set, func): - insn = insn_set.get(func(raw, vaddr), None) # default none for invalid instruction encoding - - if insn is not None and type(insn[1]) == dict: # extra decoding required - insn = fetch_insn(raw, vaddr, insn[1], insn[0]) - return insn - -def decode_insn(raw, vaddr): - return MipsInsn(ABI_VR4300, raw, vaddr, fetch_insn(raw, vaddr, mips_insns, mips_get_field)) - -def decode_rsp_insn(raw, vaddr): - return MipsInsn(ABI_RSP, raw, vaddr, fetch_insn(raw, vaddr, rsp_insns, mips_get_field)) - -mips_insns = { - 0b000000: (mips_get_special, { - # opcode id mnemonic fields field is written to - 0b000000: (MIPS_INS_SLL, "sll", ("rd","rt","sa"), (True , False, False)), - 0b000010: (MIPS_INS_SRL, "srl", ("rd","rt","sa"), (True , False, False)), - 0b000011: (MIPS_INS_SRA, "sra", ("rd","rt","sa"), (True , False, False)), - 0b000100: (MIPS_INS_SLLV, "sllv", ("rd","rt","rs"), (True , False, False)), - 0b000110: (MIPS_INS_SRLV, "srlv", ("rd","rt","rs"), (True , False, False)), - 0b000111: (MIPS_INS_SRAV, "srav", ("rd","rt","rs"), (True , False, False)), - 0b001000: (MIPS_INS_JR, "jr", ("rs", ), (False, )), - 0b001001: (MIPS_INS_JALR, "jalr", ("rs", ), (False, )), # technically also rd but it's always $ra - 0b001100: (MIPS_INS_SYSCALL, "syscall", ( ), ( )), - 0b001101: (MIPS_INS_BREAK, "break", ("code", ), (False, )), - 0b001111: (MIPS_INS_SYNC, "sync", ( ), ( )), - 0b010000: (MIPS_INS_MFHI, "mfhi", ("rd", ), (True , )), - 0b010001: (MIPS_INS_MTHI, "mthi", ("rs", ), (False, )), - 0b010010: (MIPS_INS_MFLO, "mflo", ("rd", ), (True , )), - 0b010011: (MIPS_INS_MTLO, "mtlo", ("rs", ), (False, )), - 0b010100: (MIPS_INS_DSLLV, "dsllv", ("rd","rt","rs"), (True , False, False)), - 0b010110: (MIPS_INS_DSRLV, "dsrlv", ("rd","rt","rs"), (True , False, False)), - 0b010111: (MIPS_INS_DSRAV, "dsrav", ("rd","rt","rs"), (True , False, False)), - 0b011000: (MIPS_INS_MULT, "mult", ("rs","rt" ), (False, False )), - 0b011001: (MIPS_INS_MULTU, "multu", ("rs","rt" ), (False, False )), - 0b011010: (MIPS_INS_DIV, "div", ("rd","rs","rt"), (False, False, False)), # for some reason gas hates div instructions - 0b011011: (MIPS_INS_DIVU, "divu", ("rd","rs","rt"), (False, False, False)), # with the correct number of operands - 0b011100: (MIPS_INS_DMULT, "dmult", ("rs","rt" ), (False, False )), - 0b011101: (MIPS_INS_DMULTU, "dmultu", ("rs","rt" ), (False, False )), - 0b011110: (MIPS_INS_DDIV, "ddiv", ("rd","rs","rt"), (False, False, False)), - 0b011111: (MIPS_INS_DDIVU, "ddivu", ("rd","rs","rt"), (False, False, False)), - 0b100000: (MIPS_INS_ADD, "add", ("rd","rs","rt"), (True , False, False)), - 0b100001: (MIPS_INS_ADDU, "addu", ("rd","rs","rt"), (True , False, False)), - 0b100010: (MIPS_INS_SUB, "sub", ("rd","rs","rt"), (True , False, False)), - 0b100011: (MIPS_INS_SUBU, "subu", ("rd","rs","rt"), (True , False, False)), - 0b100100: (MIPS_INS_AND, "and", ("rd","rs","rt"), (True , False, False)), - 0b100101: (MIPS_INS_OR, "or", ("rd","rs","rt"), (True , False, False)), - 0b100110: (MIPS_INS_XOR, "xor", ("rd","rs","rt"), (True , False, False)), - 0b100111: (MIPS_INS_NOR, "nor", ("rd","rs","rt"), (True , False, False)), - 0b101010: (MIPS_INS_SLT, "slt", ("rd","rs","rt"), (True , False, False)), - 0b101011: (MIPS_INS_SLTU, "sltu", ("rd","rs","rt"), (True , False, False)), - 0b101100: (MIPS_INS_DADD, "dadd", ("rd","rs","rt"), (True , False, False)), - 0b101101: (MIPS_INS_DADDU, "daddu", ("rd","rs","rt"), (True , False, False)), - 0b101110: (MIPS_INS_DSUB, "dsub", ("rd","rs","rt"), (True , False, False)), - 0b101111: (MIPS_INS_DSUBU, "dsubu", ("rd","rs","rt"), (True , False, False)), - 0b110000: (MIPS_INS_TGE, "tge", ("rs","rt" ), (False, False )), - 0b110001: (MIPS_INS_TGEU, "tgeu", ("rs","rt" ), (False, False )), - 0b110010: (MIPS_INS_TLT, "tlt", ("rs","rt" ), (False, False )), - 0b110011: (MIPS_INS_TLTU, "tltu", ("rs","rt" ), (False, False )), - 0b110100: (MIPS_INS_TEQ, "teq", ("rs","rt" ), (False, False )), - 0b110110: (MIPS_INS_TNE, "tne", ("rs","rt" ), (False, False )), - 0b111000: (MIPS_INS_DSLL, "dsll", ("rd","rt","sa"), (True , False, False)), - 0b111010: (MIPS_INS_DSRL, "dsrl", ("rd","rt","sa"), (True , False, False)), - 0b111011: (MIPS_INS_DSRA, "dsra", ("rd","rt","sa"), (True , False, False)), - 0b111100: (MIPS_INS_DSLL32, "dsll32", ("rd","rt","sa"), (True , False, False)), - 0b111110: (MIPS_INS_DSRL32, "dsrl32", ("rd","rt","sa"), (True , False, False)), - 0b111111: (MIPS_INS_DSRA32, "dsra32", ("rd","rt","sa"), (True , False, False)), - }), - 0b000001: (mips_get_regimm, { - 0b00000: (MIPS_INS_BLTZ, "bltz", ("rs","offset"), (False, False)), - 0b00001: (MIPS_INS_BGEZ, "bgez", ("rs","offset"), (False, False)), - 0b00010: (MIPS_INS_BLTZL, "bltzl", ("rs","offset"), (False, False)), - 0b00011: (MIPS_INS_BGEZL, "bgezl", ("rs","offset"), (False, False)), - 0b01000: (MIPS_INS_TGEI, "tgei", ("rs","imm" ), (False, False)), - 0b01001: (MIPS_INS_TGEIU, "tgeiu", ("rs","imm" ), (False, False)), - 0b01010: (MIPS_INS_TLTI, "tlti", ("rs","imm" ), (False, False)), - 0b01011: (MIPS_INS_TLTIU, "tltiu", ("rs","imm" ), (False, False)), - 0b01100: (MIPS_INS_TEQI, "teqi", ("rs","imm" ), (False, False)), - 0b01110: (MIPS_INS_TNEI, "tnei", ("rs","imm" ), (False, False)), - 0b10000: (MIPS_INS_BLTZAL, "bltzal", ("rs","offset"), (False, False)), - 0b10001: (MIPS_INS_BGEZAL, "bgezal", ("rs","offset"), (False, False)), - 0b10010: (MIPS_INS_BLTZALL, "bltzall", ("rs","offset"), (False, False)), - 0b10011: (MIPS_INS_BGEZALL, "bgezall", ("rs","offset"), (False, False)), - }), - 0b000010: (MIPS_INS_J, "j", ("target", ), (False, )), - 0b000011: (MIPS_INS_JAL, "jal", ("target", ), (False, )), - 0b000100: (MIPS_INS_BEQ, "beq", ("rs","rt","offset"), (False, False, False)), - 0b000101: (MIPS_INS_BNE, "bne", ("rs","rt","offset"), (False, False, False)), - 0b000110: (MIPS_INS_BLEZ, "blez", ("rs","offset" ), (False, False, )), - 0b000111: (MIPS_INS_BGTZ, "bgtz", ("rs","offset" ), (False, False, )), - 0b001000: (MIPS_INS_ADDI, "addi", ("rt","rs","imm" ), (True , False, False)), - 0b001001: (MIPS_INS_ADDIU, "addiu", ("rt","rs","imm" ), (True , False, False)), - 0b001010: (MIPS_INS_SLTI, "slti", ("rt","rs","imm" ), (True , False, False)), - 0b001011: (MIPS_INS_SLTIU, "sltiu", ("rt","rs","imm" ), (True , False, False)), - 0b001100: (MIPS_INS_ANDI, "andi", ("rt","rs","imm" ), (True , False, False)), - 0b001101: (MIPS_INS_ORI, "ori", ("rt","rs","imm" ), (True , False, False)), - 0b001110: (MIPS_INS_XORI, "xori", ("rt","rs","imm" ), (True , False, False)), - 0b001111: (MIPS_INS_LUI, "lui", ("rt","imm" ), (True , False )), - 0b010000: (mips_get_cop0, { - 0b00000: (MIPS_INS_MFC0, "mfc0", ("rt","cd"), (True , False)), - 0b00100: (MIPS_INS_MTC0, "mtc0", ("rt","cd"), (False, True )), - 0b10000: (mips_get_tlb, { - 0b000001: (MIPS_INS_TLBR, "tlbr", (), ()), - 0b000010: (MIPS_INS_TLBWI, "tlbwi", (), ()), - 0b000110: (MIPS_INS_TLBWR, "tlbwr", (), ()), - 0b001000: (MIPS_INS_TLBP, "tlbp", (), ()), - 0b011000: (MIPS_INS_ERET, "eret", (), ()), - }), - }), - 0b010001: (mips_get_cop1, { - 0b00000: (MIPS_INS_MFC1, "mfc1", ("rt","fs"), (True , False)), - 0b00001: (MIPS_INS_DMFC1, "dmfc1", ("rt","fs"), (True , False)), - 0b00010: (MIPS_INS_CFC1, "cfc1", ("rt","fs"), (True , False)), - 0b00100: (MIPS_INS_MTC1, "mtc1", ("rt","fs"), (False, True )), - 0b00101: (MIPS_INS_DMTC1, "dmtc1", ("rt","fs"), (False, True )), - 0b00110: (MIPS_INS_CTC1, "ctc1", ("rt","fs"), (False, True )), - 0b01000: (mips_get_ndtf, { - 0b00: (MIPS_INS_BC1F, "bc1f", ("offset",), (False,)), - 0b01: (MIPS_INS_BC1T, "bc1t", ("offset",), (False,)), - 0b10: (MIPS_INS_BC1FL, "bc1fl", ("offset",), (False,)), - 0b11: (MIPS_INS_BC1TL, "bc1tl", ("offset",), (False,)), - }), - 0b010000: (mips_get_function, { - 0b000000: (MIPS_INS_ADD_S, "add.s", ("fd","fs","ft"), (True , False, False)), - 0b000001: (MIPS_INS_SUB_S, "sub.s", ("fd","fs","ft"), (True , False, False)), - 0b000010: (MIPS_INS_MUL_S, "mul.s", ("fd","fs","ft"), (True , False, False)), - 0b000011: (MIPS_INS_DIV_S, "div.s", ("fd","fs","ft"), (True , False, False)), - 0b000100: (MIPS_INS_SQRT_S, "sqrt.s", ("fd","fs" ), (True , False )), - 0b000101: (MIPS_INS_ABS_S, "abs.s", ("fd","fs" ), (True , False )), - 0b000110: (MIPS_INS_MOV_S, "mov.s", ("fd","fs" ), (True , False )), - 0b000111: (MIPS_INS_NEG_S, "neg.s", ("fd","fs" ), (True , False )), - 0b001000: (MIPS_INS_ROUND_L_S, "round.l.s", ("fd","fs" ), (True , False )), - 0b001001: (MIPS_INS_TRUNC_L_S, "trunc.l.s", ("fd","fs" ), (True , False )), - 0b001010: (MIPS_INS_CEIL_L_S, "ceil.l.s", ("fd","fs" ), (True , False )), - 0b001011: (MIPS_INS_FLOOR_L_S, "floor.l.s", ("fd","fs" ), (True , False )), - 0b001100: (MIPS_INS_ROUND_W_S, "round.w.s", ("fd","fs" ), (True , False )), - 0b001101: (MIPS_INS_TRUNC_W_S, "trunc.w.s", ("fd","fs" ), (True , False )), - 0b001110: (MIPS_INS_CEIL_W_S, "ceil.w.s", ("fd","fs" ), (True , False )), - 0b001111: (MIPS_INS_FLOOR_W_S, "floor.w.s", ("fd","fs" ), (True , False )), - 0b100001: (MIPS_INS_CVT_D_S, "cvt.d.s", ("fd","fs" ), (True , False )), - 0b100100: (MIPS_INS_CVT_W_S, "cvt.w.s", ("fd","fs" ), (True , False )), - 0b100101: (MIPS_INS_CVT_L_S, "cvt.l.s", ("fd","fs" ), (True , False )), - 0b110000: (MIPS_INS_C_F_S, "c.f.s", ("fs","ft" ), (False, False )), - 0b110001: (MIPS_INS_C_UN_S, "c.un.s", ("fs","ft" ), (False, False )), - 0b110010: (MIPS_INS_C_EQ_S, "c.eq.s", ("fs","ft" ), (False, False )), - 0b110011: (MIPS_INS_C_UEQ_S, "c.ueq.s", ("fs","ft" ), (False, False )), - 0b110100: (MIPS_INS_C_OLT_S, "c.olt.s", ("fs","ft" ), (False, False )), - 0b110101: (MIPS_INS_C_ULT_S, "c.ult.s", ("fs","ft" ), (False, False )), - 0b110110: (MIPS_INS_C_OLE_S, "c.ole.s", ("fs","ft" ), (False, False )), - 0b110111: (MIPS_INS_C_ULE_S, "c.ule.s", ("fs","ft" ), (False, False )), - 0b111000: (MIPS_INS_C_SF_S, "c.sf.s", ("fs","ft" ), (False, False )), - 0b111001: (MIPS_INS_C_NGLE_S, "c.ngle.s", ("fs","ft" ), (False, False )), - 0b111010: (MIPS_INS_C_SEQ_S, "c.seq.s", ("fs","ft" ), (False, False )), - 0b111011: (MIPS_INS_C_NGL_S, "c.ngl.s", ("fs","ft" ), (False, False )), - 0b111100: (MIPS_INS_C_LT_S, "c.lt.s", ("fs","ft" ), (False, False )), - 0b111101: (MIPS_INS_C_NGE_S, "c.nge.s", ("fs","ft" ), (False, False )), - 0b111110: (MIPS_INS_C_LE_S, "c.le.s", ("fs","ft" ), (False, False )), - 0b111111: (MIPS_INS_C_NGT_S, "c.ngt.s", ("fs","ft" ), (False, False )), - }), - 0b010001: (mips_get_function, { - 0b000000: (MIPS_INS_ADD_D, "add.d", ("fd","fs","ft"), (True , False, False)), - 0b000001: (MIPS_INS_SUB_D, "sub.d", ("fd","fs","ft"), (True , False, False)), - 0b000010: (MIPS_INS_MUL_D, "mul.d", ("fd","fs","ft"), (True , False, False)), - 0b000011: (MIPS_INS_DIV_D, "div.d", ("fd","fs","ft"), (True , False, False)), - 0b000100: (MIPS_INS_SQRT_D, "sqrt.d", ("fd","fs" ), (True , False )), - 0b000101: (MIPS_INS_ABS_D, "abs.d", ("fd","fs" ), (True , False )), - 0b000110: (MIPS_INS_MOV_D, "mov.d", ("fd","fs" ), (True , False )), - 0b000111: (MIPS_INS_NEG_D, "neg.d", ("fd","fs" ), (True , False )), - 0b001000: (MIPS_INS_ROUND_L_D, "round.l.d", ("fd","fs" ), (True , False )), - 0b001001: (MIPS_INS_TRUNC_L_D, "trunc.l.d", ("fd","fs" ), (True , False )), - 0b001010: (MIPS_INS_CEIL_L_D, "ceil.l.d", ("fd","fs" ), (True , False )), - 0b001011: (MIPS_INS_FLOOR_L_D, "floor.l.d", ("fd","fs" ), (True , False )), - 0b001100: (MIPS_INS_ROUND_W_D, "round.w.d", ("fd","fs" ), (True , False )), - 0b001101: (MIPS_INS_TRUNC_W_D, "trunc.w.d", ("fd","fs" ), (True , False )), - 0b001110: (MIPS_INS_CEIL_W_D, "ceil.w.d", ("fd","fs" ), (True , False )), - 0b001111: (MIPS_INS_FLOOR_W_D, "floor.w.d", ("fd","fs" ), (True , False )), - 0b100000: (MIPS_INS_CVT_S_D, "cvt.s.d", ("fd","fs" ), (True , False )), - 0b100100: (MIPS_INS_CVT_W_D, "cvt.w.d", ("fd","fs" ), (True , False )), - 0b100101: (MIPS_INS_CVT_L_D, "cvt.l.d", ("fd","fs" ), (True , False )), - 0b110000: (MIPS_INS_C_F_D, "c.f.d", ("fs","ft" ), (False, False )), - 0b110001: (MIPS_INS_C_UN_D, "c.un.d", ("fs","ft" ), (False, False )), - 0b110010: (MIPS_INS_C_EQ_D, "c.eq.d", ("fs","ft" ), (False, False )), - 0b110011: (MIPS_INS_C_UEQ_D, "c.ueq.d", ("fs","ft" ), (False, False )), - 0b110100: (MIPS_INS_C_OLT_D, "c.olt.d", ("fs","ft" ), (False, False )), - 0b110101: (MIPS_INS_C_ULT_D, "c.ult.d", ("fs","ft" ), (False, False )), - 0b110110: (MIPS_INS_C_OLE_D, "c.ole.d", ("fs","ft" ), (False, False )), - 0b110111: (MIPS_INS_C_ULE_D, "c.ule.d", ("fs","ft" ), (False, False )), - 0b111000: (MIPS_INS_C_SF_D, "c.sf.d", ("fs","ft" ), (False, False )), - 0b111001: (MIPS_INS_C_NGLE_D, "c.ngle.d", ("fs","ft" ), (False, False )), - 0b111010: (MIPS_INS_C_SEQ_D, "c.seq.d", ("fs","ft" ), (False, False )), - 0b111011: (MIPS_INS_C_NGL_D, "c.ngl.d", ("fs","ft" ), (False, False )), - 0b111100: (MIPS_INS_C_LT_D, "c.lt.d", ("fs","ft" ), (False, False )), - 0b111101: (MIPS_INS_C_NGE_D, "c.nge.d", ("fs","ft" ), (False, False )), - 0b111110: (MIPS_INS_C_LE_D, "c.le.d", ("fs","ft" ), (False, False )), - 0b111111: (MIPS_INS_C_NGT_D, "c.ngt.d", ("fs","ft" ), (False, False )), - }), - 0b010100: (mips_get_function, { - 0b100000: (MIPS_INS_CVT_S_W, "cvt.s.w", ("fd","fs"), (True , False)), - 0b100001: (MIPS_INS_CVT_D_W, "cvt.d.w", ("fd","fs"), (True , False)), - }), - 0b010101: (mips_get_function, { - 0b100000: (MIPS_INS_CVT_S_L, "cvt.s.l", ("fd","fs"), (True , False)), - 0b100001: (MIPS_INS_CVT_D_L, "cvt.d.l", ("fd","fs"), (True , False)), - }), - }), - 0b010100: (MIPS_INS_BEQL, "beql", ("rs","rt","offset" ), (False, False, False)), - 0b010101: (MIPS_INS_BNEL, "bnel", ("rs","rt","offset" ), (False, False, False)), - 0b010110: (MIPS_INS_BLEZL, "blezl", ("rs","offset" ), (False, False )), - 0b010111: (MIPS_INS_BGTZL, "bgtzl", ("rs","offset" ), (False, False )), - 0b011000: (MIPS_INS_DADDI, "daddi", ("rt","rs","imm" ), (True , False, False)), - 0b011001: (MIPS_INS_DADDIU, "daddiu", ("rt","rs","imm" ), (True , False, False)), - 0b011010: (MIPS_INS_LDL, "ldl", ("rt","offset(base)"), (True , False )), - 0b011011: (MIPS_INS_LDR, "ldr", ("rt","offset(base)"), (True , False )), - 0b100000: (MIPS_INS_LB, "lb", ("rt","offset(base)"), (True , False )), - 0b100001: (MIPS_INS_LH, "lh", ("rt","offset(base)"), (True , False )), - 0b100010: (MIPS_INS_LWL, "lwl", ("rt","offset(base)"), (True , False )), - 0b100011: (MIPS_INS_LW, "lw", ("rt","offset(base)"), (True , False )), - 0b100100: (MIPS_INS_LBU, "lbu", ("rt","offset(base)"), (True , False )), - 0b100101: (MIPS_INS_LHU, "lhu", ("rt","offset(base)"), (True , False )), - 0b100110: (MIPS_INS_LWR, "lwr", ("rt","offset(base)"), (True , False )), - 0b100111: (MIPS_INS_LWU, "lwu", ("rt","offset(base)"), (True , False )), - 0b101000: (MIPS_INS_SB, "sb", ("rt","offset(base)"), (False, False )), - 0b101001: (MIPS_INS_SH, "sh", ("rt","offset(base)"), (False, False )), - 0b101010: (MIPS_INS_SWL, "swl", ("rt","offset(base)"), (False, False )), - 0b101011: (MIPS_INS_SW, "sw", ("rt","offset(base)"), (False, False )), - 0b101100: (MIPS_INS_SDL, "sdl", ("rt","offset(base)"), (False, False )), - 0b101101: (MIPS_INS_SDR, "sdr", ("rt","offset(base)"), (False, False )), - 0b101110: (MIPS_INS_SWR, "swr", ("rt","offset(base)"), (False, False )), - 0b101111: (MIPS_INS_CACHE, "cache", ("op","offset(base)"), (False, False, False)), - 0b110000: (MIPS_INS_LL, "ll", ("rt","offset(base)"), (True , False )), - 0b110001: (MIPS_INS_LWC1, "lwc1", ("ft","offset(base)"), (True , False )), - # lwc2 - 0b110100: (MIPS_INS_LLD, "lld", ("rt","offset(base)"), (True , False )), - 0b110101: (MIPS_INS_LDC1, "ldc1", ("ft","offset(base)"), (True , False )), - # ldc2 - 0b110111: (MIPS_INS_LD, "ld", ("rt","offset(base)"), (True , False )), - 0b111000: (MIPS_INS_SC, "sc", ("rt","offset(base)"), (False, False )), - 0b111001: (MIPS_INS_SWC1, "swc1", ("ft","offset(base)"), (False, False )), - # lwc2 - 0b111100: (MIPS_INS_SCD, "scd", ("rt","offset(base)"), (False, False )), - 0b111101: (MIPS_INS_SDC1, "sdc1", ("ft","offset(base)"), (False, False )), - # sdc2 - 0b111111: (MIPS_INS_SD, "sd", ("rt","offset(base)"), (False, False )), -} - -rsp_insns = { - 0b000000: (mips_get_special, { - # opcode id mnemonic fields field is written to - 0b000000: (MIPS_INS_SLL, "sll", ("rd","rt","sa"), (True , False, False)), - 0b000010: (MIPS_INS_SRL, "srl", ("rd","rt","sa"), (True , False, False)), - 0b000011: (MIPS_INS_SRA, "sra", ("rd","rt","sa"), (True , False, False)), - 0b000100: (MIPS_INS_SLLV, "sllv", ("rd","rt","rs"), (True , False, False)), - 0b000110: (MIPS_INS_SRLV, "srlv", ("rd","rt","rs"), (True , False, False)), - 0b000111: (MIPS_INS_SRAV, "srav", ("rd","rt","rs"), (True , False, False)), - 0b001000: (MIPS_INS_JR, "jr", ("rs", ), (False, )), - 0b001001: (MIPS_INS_JALR, "jalr", ("rs", ), (False, )), # technically also rd but it's always $ra - 0b001101: (MIPS_INS_BREAK, "break", ("code", ), (False, )), - 0b100000: (MIPS_INS_ADD, "add", ("rd","rs","rt"), (True , False, False)), - 0b100001: (MIPS_INS_ADDU, "addu", ("rd","rs","rt"), (True , False, False)), - 0b100010: (MIPS_INS_SUB, "sub", ("rd","rs","rt"), (True , False, False)), - 0b100011: (MIPS_INS_SUBU, "subu", ("rd","rs","rt"), (True , False, False)), - 0b100100: (MIPS_INS_AND, "and", ("rd","rs","rt"), (True , False, False)), - 0b100101: (MIPS_INS_OR, "or", ("rd","rs","rt"), (True , False, False)), - 0b100110: (MIPS_INS_XOR, "xor", ("rd","rs","rt"), (True , False, False)), - 0b100111: (MIPS_INS_NOR, "nor", ("rd","rs","rt"), (True , False, False)), - 0b101010: (MIPS_INS_SLT, "slt", ("rd","rs","rt"), (True , False, False)), - 0b101011: (MIPS_INS_SLTU, "sltu", ("rd","rs","rt"), (True , False, False)), - }), - 0b000001: (mips_get_regimm, { - 0b00000: (MIPS_INS_BLTZ, "bltz", ("rs","offset"), (False, False)), - 0b00001: (MIPS_INS_BGEZ, "bgez", ("rs","offset"), (False, False)), - 0b10000: (MIPS_INS_BLTZAL, "bltzal", ("rs","offset"), (False, False)), - 0b10001: (MIPS_INS_BGEZAL, "bgezal", ("rs","offset"), (False, False)), - }), - 0b000010: (MIPS_INS_J, "j", ("target", ), (False, )), - 0b000011: (MIPS_INS_JAL, "jal", ("target", ), (False, )), - 0b000100: (MIPS_INS_BEQ, "beq", ("rs","rt","offset"), (False, False, False)), - 0b000101: (MIPS_INS_BNE, "bne", ("rs","rt","offset"), (False, False, False)), - 0b000110: (MIPS_INS_BLEZ, "blez", ("rs","offset" ), (False, False )), - 0b000111: (MIPS_INS_BGTZ, "bgtz", ("rs","offset" ), (False, False )), - 0b001000: (MIPS_INS_ADDI, "addi", ("rt","rs","imm" ), (True , False, False)), - 0b001001: (MIPS_INS_ADDIU, "addiu", ("rt","rs","imm" ), (True , False, False)), - 0b001010: (MIPS_INS_SLTI, "slti", ("rt","rs","imm" ), (True , False, False)), - 0b001011: (MIPS_INS_SLTIU, "sltiu", ("rt","rs","imm" ), (True , False, False)), - 0b001100: (MIPS_INS_ANDI, "andi", ("rt","rs","imm" ), (True , False, False)), - 0b001101: (MIPS_INS_ORI, "ori", ("rt","rs","imm" ), (True , False, False)), - 0b001110: (MIPS_INS_XORI, "xori", ("rt","rs","imm" ), (True , False, False)), - 0b001111: (MIPS_INS_LUI, "lui", ("rt","imm" ), (True , False )), - 0b010000: (mips_get_cop0, { - 0b00000: (MIPS_INS_MFC0, "mfc0", ("rt","cd"), (True , False)), - 0b00100: (MIPS_INS_MTC0, "mtc0", ("rt","cd"), (False, True )), - }), - 0b010010: (mips_get_cop2_func, { # TODO this encoding got ugly, move to a mask matcher like gnu objdump - 0b0: (mips_get_cop2, { - 0b0000: (MIPS_INS_MFC2, "mfc2", ("rt", "vd[e]"), (True , False)), - 0b0100: (MIPS_INS_MTC2, "mtc2", ("rt", "vd[e]"), (False, True )), - 0b0010: (MIPS_INS_CFC2, "cfc2", ("rt", "vc" ), (True , False)), - 0b0110: (MIPS_INS_CTC2, "ctc2", ("rt", "vc" ), (False, True )), - }), - 0b1: (mips_get_function, { - 0b000000: (MIPS_INS_VMULF, "vmulf", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b000001: (MIPS_INS_VMULU, "vmulu", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b000010: (MIPS_INS_VRNDP, "vrndp", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b000011: (MIPS_INS_VMULQ, "vmulq", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b000100: (MIPS_INS_VMUDL, "vmudl", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b000101: (MIPS_INS_VMUDM, "vmudm", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b000110: (MIPS_INS_VMUDN, "vmudn", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b000111: (MIPS_INS_VMUDH, "vmudh", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b001000: (MIPS_INS_VMACF, "vmacf", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b001001: (MIPS_INS_VMACU, "vmacu", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b001010: (MIPS_INS_VRNDN, "vrndn", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b001011: (MIPS_INS_VMACQ, "vmacq", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b001100: (MIPS_INS_VMADL, "vmadl", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b001101: (MIPS_INS_VMADM, "vmadm", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b001110: (MIPS_INS_VMADN, "vmadn", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b001111: (MIPS_INS_VMADH, "vmadh", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b010000: (MIPS_INS_VADD, "vadd", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b010001: (MIPS_INS_VSUB, "vsub", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b010011: (MIPS_INS_VABS, "vabs", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b010100: (MIPS_INS_VADDC, "vaddc", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b010101: (MIPS_INS_VSUBC, "vsubc", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b011101: (MIPS_INS_VSAR, "vsar", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b100000: (MIPS_INS_VLT, "vlt", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b100001: (MIPS_INS_VEQ, "veq", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b100010: (MIPS_INS_VNE, "vne", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b100011: (MIPS_INS_VGE, "vge", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b100100: (MIPS_INS_VCL, "vcl", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b100101: (MIPS_INS_VCH, "vch", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b100110: (MIPS_INS_VCR, "vcr", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b100111: (MIPS_INS_VMRG, "vmrg", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b101000: (MIPS_INS_VAND, "vand", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b101001: (MIPS_INS_VNAND, "vnand", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b101010: (MIPS_INS_VOR, "vor", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b101011: (MIPS_INS_VNOR, "vnor", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b101100: (MIPS_INS_VXOR, "vxor", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b101101: (MIPS_INS_VNXOR, "vnxor", ("vd", "vs", "vt[e]"), (True , False, False)), - 0b110000: (MIPS_INS_VRCP, "vrcp", ("vd[ed]", "vt[e]" ), (True , False)), - 0b110001: (MIPS_INS_VRCPL, "vrcpl", ("vd[ed]", "vt[e]" ), (True , False)), - 0b110010: (MIPS_INS_VRCPH, "vrcph", ("vd[ed]", "vt[e]" ), (True , False)), - 0b110011: (MIPS_INS_VMOV, "vmov", ("vd[ed]", "vt[e]" ), (True , False)), - 0b110100: (MIPS_INS_VRSQ, "vrsq", ("vd[ed]", "vt[e]" ), (True , False)), - 0b110101: (MIPS_INS_VRSQL, "vrsql", ("vd[ed]", "vt[e]" ), (True , False)), - 0b110110: (MIPS_INS_VRSQH, "vrsqh", ("vd[ed]", "vt[e]" ), (True , False)), - 0b110111: (MIPS_INS_VNOP, "vnop", ( ), ()), - }), - }), - 0b100000: (MIPS_INS_LB, "lb", ("rt","offset(base)"), (True , False)), - 0b100001: (MIPS_INS_LH, "lh", ("rt","offset(base)"), (True , False)), - 0b100011: (MIPS_INS_LW, "lw", ("rt","offset(base)"), (True , False)), - 0b100100: (MIPS_INS_LBU, "lbu", ("rt","offset(base)"), (True , False)), - 0b100101: (MIPS_INS_LHU, "lhu", ("rt","offset(base)"), (True , False)), - 0b101000: (MIPS_INS_SB, "sb", ("rt","offset(base)"), (False, False)), - 0b101001: (MIPS_INS_SH, "sh", ("rt","offset(base)"), (False, False)), - 0b101011: (MIPS_INS_SW, "sw", ("rt","offset(base)"), (False, False)), - 0b110010: (mips_get_lwc2, { - 0b00000: (MIPS_INS_LBV, "lbv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b00001: (MIPS_INS_LSV, "lsv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b00010: (MIPS_INS_LLV, "llv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b00011: (MIPS_INS_LDV, "ldv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b00100: (MIPS_INS_LQV, "lqv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b00101: (MIPS_INS_LRV, "lrv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b00110: (MIPS_INS_LPV, "lpv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b00111: (MIPS_INS_LUV, "luv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b01000: (MIPS_INS_LHV, "lhv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b01001: (MIPS_INS_LFV, "lfv", ("vt[ed]", "voffset(base)"), (True , False)), - 0b01011: (MIPS_INS_LTV, "ltv", ("vt[ed]", "voffset(base)"), (True , False)), - }), - 0b111010: (mips_get_swc2, { - 0b00000: (MIPS_INS_SBV, "sbv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b00001: (MIPS_INS_SSV, "ssv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b00010: (MIPS_INS_SLV, "slv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b00011: (MIPS_INS_SDV, "sdv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b00100: (MIPS_INS_SQV, "sqv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b00101: (MIPS_INS_SRV, "srv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b00110: (MIPS_INS_SPV, "spv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b00111: (MIPS_INS_SUV, "suv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b01000: (MIPS_INS_SHV, "shv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b01001: (MIPS_INS_SFV, "sfv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b01010: (MIPS_INS_SWV, "swv", ("vt[ed]", "voffset(base)"), (False, False)), - 0b01011: (MIPS_INS_STV, "stv", ("vt[ed]", "voffset(base)"), (False, False)), - }), -} diff --git a/tools/patch_ar_meta.py b/tools/patch_ar_meta.py deleted file mode 100755 index d831785b..00000000 --- a/tools/patch_ar_meta.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python3 -# -# Patch metadata in .ar files -# - -import argparse - -def ar_patch(ar, original, new_uid, new_gid, new_filemode): - """ - AR file headers - - Offset Length Name Format - 0 16 File identifier ASCII - 16 12 File modification timestamp (in seconds) Decimal - 28 6 Owner ID Decimal - 34 6 Group ID Decimal - 40 8 File mode (type and permission) Octal - 48 10 File size in bytes Decimal - 58 2 Ending characters 0x60 0x0A - """ - assert len(ar) > 0x24 , "Got empty archive?" - - armap_time1 = None - armap_time2 = None - - i = 8 - while i < len(original): - file_name = original[i:][0:][:16].decode("ASCII") - file_size = int(original[i:][48:][:10].decode("ASCII").strip()) - end = original[i:][58:][:2].decode("ASCII") - - assert end == "`\n" - - if file_name.strip() == "/": - armap_time1 = original[i+16:i+16+12] - elif file_name.strip() == "//": - armap_time2 = original[i+16:i+16+12] - - if file_size % 2 != 0: - file_size += 1 - i += file_size + 60 - - assert armap_time1 is not None - assert armap_time2 is not None - - i = 8 - while i < len(ar): - file_name = ar[i:][0:][:16].decode("ASCII") - file_size = int(ar[i:][48:][:10].decode("ASCII").strip()) - end = ar[i:][58:][:2].decode("ASCII") - - assert end == "`\n" - - if file_name.strip() == "/": - ar[i+16:i+16+12] = armap_time1 - elif file_name.strip() == "//": - ar[i+16:i+16+12] = armap_time2 - else: - ar[i+28:i+28+6] = f"{new_uid:<6}".encode("ASCII")[:6] - ar[i+34:i+34+6] = f"{new_gid:<6}".encode("ASCII")[:6] - ar[i+40:i+40+8] = f"{new_filemode:<8}".encode("ASCII")[:8] - - if file_size % 2 != 0: - file_size += 1 - i += file_size + 60 - -def main(): - parser = argparse.ArgumentParser(description="Patch metadata in .a files.") - parser.add_argument("target", help="path to the ar file to patch") - parser.add_argument("original", help="path to the original ar file") - parser.add_argument("uid", help="new uid") - parser.add_argument("gid", help="new gid") - parser.add_argument("filemode", help="new filemode") - args = parser.parse_args() - - ar = None - with open(args.target, "rb") as ar_file: - ar = bytearray(ar_file.read()) - - original = None - with open(args.original, "rb") as original_ar_file: - original = bytearray(original_ar_file.read()) - - ar_patch(ar, original, args.uid, args.gid, args.filemode) - - with open(args.target, "wb") as ar_file: - ar_file.write(ar) - -if __name__ == '__main__': - main() diff --git a/tools/print_mdebug.py b/tools/print_mdebug.py deleted file mode 100755 index e0b295a3..00000000 --- a/tools/print_mdebug.py +++ /dev/null @@ -1,589 +0,0 @@ -#!/usr/bin/env python3 -''' -Resources: -http://www.cs.unibo.it/~solmi/teaching/arch_2002-2003/AssemblyLanguageProgDoc.pdf -https://github.com/pathscale/binutils/blob/5c2c133020e41fc4aadd80a99156d2cea4754b96/include/coff/sym.h -https://github.com/pathscale/binutils/blob/5c2c133020e41fc4aadd80a99156d2cea4754b96/include/coff/symconst.h -https://github.com/pathscale/binutils/blob/5c2c133020e41fc4aadd80a99156d2cea4754b96/gas/ecoff.c -https://github.com/pathscale/binutils/blob/5c2c133020e41fc4aadd80a99156d2cea4754b96/bfd/ecoff.c -https://github.com/pathscale/absoft/blob/master/svn/trunk/ekopath-gcc/ekopath-gcc-4.2.0/gcc/mips-tdump.c -https://chromium.googlesource.com/native_client/nacl-toolchain/+/refs/tags/gcc-4.4.3/binutils/gas/ecoff.c -https://android.googlesource.com/toolchain/binutils/+/refs/heads/donut/binutils-2.17/bfd/ecofflink.c -https://kernel.googlesource.com/pub/scm/linux/kernel/git/hjl/binutils/+/hjl/secondary/include/coff/symconst.h -https://c0de.pw/bg/binutils-gdb/blob/cdbf20f73486c66e24f322400eba877eb534ae51/gdb/mdebugread.c -''' - -import os -import struct -import collections -import sys - -OFFSET = 0 # TODO why are the offsets in the symbolic header off by some amount? - -indent_level = 0 -is_comment = False - -symbol_type_list = [ - 'stNil', 'stGlobal', 'stStatic', 'stParam', 'stLocal', 'stLabel', 'stProc', 'stBlock', - 'stEnd', 'stMember', 'stTypedef', 'stFile', 'INVALID', 'INVALID', 'stStaticProc', 'stConstant', - 'stStaParam', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', - 'INVALID', 'INVALID', 'stStruct', 'stUnion', 'stEnum', 'INVALID', 'INVALID', 'INVALID', - 'INVALID', 'INVALID', 'stIndirect'] -storage_class_list = ['scNil', 'scText', 'scData', 'scBss', 'scRegister', 'scAbs', 'scUndefined', 'reserved', - 'scBits', 'scDbx', 'scRegImage', 'scInfo', 'scUserStruct', 'scSData', 'scSBss', 'scRData', - 'scVar', 'scCommon', 'scSCommon', 'scVarRegister', 'scVariant', 'scUndefined', 'scInit'] -basic_type_c_list = ['nil', 'addr', 'signed char', 'unsigned char', 'short', 'unsigned short', 'int', 'unsigned int', - 'long', 'unsigned long', 'float', 'double', 'struct', 'union', 'enum', 'typedef', - 'range', 'set', 'complex', 'double complex', 'indirect', 'fixed decimal', 'float decimal', 'string', - 'bit', 'picture', 'void', 'long long', 'unsigned long long', 'INVALID', 'long', 'unsigned long', - 'long long', 'unsigned long long', 'addr', 'int64', 'unsigned int64'] - -def increase_indent(): - global indent_level - indent_level += 1 - -def decrease_indent(): - global indent_level - indent_level -= 1 - -def set_is_comment(set_to): - global is_comment - old = is_comment - is_comment = set_to - return old - -def get_indent(): - global indent_level - global is_comment - ret = '//' if is_comment else '' - for i in range(indent_level): - ret += ' ' - return ret - -def check_indent(): - global indent_level - assert indent_level == 0 - -def read_uint32_be(file_data, offset): - return struct.unpack('>I', file_data[offset:offset+4])[0] - -def read_uint16_be(file_data, offset): - return struct.unpack('>H', file_data[offset:offset+2])[0] - -def read_uint8_be(file_data, offset): - return struct.unpack('>B', file_data[offset:offset+1])[0] - -def read_elf_header(file_data, offset): - Header = collections.namedtuple('ElfHeader', - '''e_magic e_class e_data e_version e_osabi e_abiversion e_pad - e_type e_machine e_version2 e_entry e_phoff e_shoff e_flags - e_ehsize e_phentsize e_phnum e_shentsize e_shnum e_shstrndx''') - return Header._make(struct.unpack('>I5B7s2H5I6H', file_data[offset:offset+52])) - -def read_elf_section_header(file_data, offset): - Header = collections.namedtuple('SectionHeader', - '''sh_name sh_type sh_flags sh_addr sh_offset sh_size sh_link - sh_info sh_addralign sh_entsize''') - return Header._make(struct.unpack('>10I', file_data[offset:offset+40])) - -def read_symbolic_header(file_data, offset): - Header = collections.namedtuple('SymbolicHeader', - '''magic vstamp ilineMax cbLine cbLineOffset idnMax cbDnOffset - ipdMax cbPdOffset isymMax cbSymOffset ioptMax cbOptOffset - iauxMax cbAuxOffset issMax cbSsOffset issExtMax cbSsExtOffset - ifdMax cbFdOffset crfd cbRfdOffset iextMax cbExtOffset''') - return Header._make(struct.unpack('>2H23I', file_data[offset:offset+96])) - -# TODO find a better solution for the bitfield -def read_file_descriptor(file_data, offset): - if 'init' not in read_file_descriptor.__dict__: - read_file_descriptor.cache = {} - read_file_descriptor.header = collections.namedtuple('FileDescriptor', - '''adr rss issBase cbSs isymBase csym ilineBase cline ioptBase - copt ipdFirst cpd iauxBase caux rfdBase crfd XXX_bitfield - cbLineOffset cbLine''') - read_file_descriptor.init = True - if offset in read_file_descriptor.cache: - return read_file_descriptor.cache[offset] - read_file_descriptor.cache[offset] = read_file_descriptor.header._make( - struct.unpack('>I2iI6iHh4iI2I', file_data[offset:offset+72])) - return read_file_descriptor.cache[offset] - -def read_procedure_descriptor(file_data, offset): - Header = collections.namedtuple('ProcedureDescriptor', - '''adr isym iline regmask regoffset iopt fregmask fregoffset - frameoffset framereg pcreg lnLow lnHigh cbLineOffset''') - return Header._make(struct.unpack('>I8i2h2iI', file_data[offset:offset+52])) - -def read_symbol(file_data, offset): - if 'init' not in read_symbol.__dict__: - read_symbol.cache = {} - read_symbol.header = collections.namedtuple('Symbol', '''iss value st sc index''') - read_symbol.init = True - if offset in read_symbol.cache: - return read_symbol.cache[offset] - (word0, word1, word2) = struct.unpack('>iiI', file_data[offset:offset+12]) - read_symbol.cache[offset] = read_symbol.header._make(( - word0, word1, (word2 >> 26) & 0x3F, (word2 >> 21) & 0x1F, word2 & 0xFFFFF)) - return read_symbol.cache[offset] - -def read_auxiliary_symbol(file_data, offset): - if 'init' not in read_auxiliary_symbol.__dict__: - read_auxiliary_symbol.cache = {} - read_auxiliary_symbol.header = collections.namedtuple('AuxSymbol', - '''ti rndx dnLow dnHigh isym iss width count''') - read_auxiliary_symbol.type_info = collections.namedtuple('TypeInfo', - '''fBitfield continued bt tq4 tq5 tq0 tq1 tq2 tq3''') - read_auxiliary_symbol.rel_sym = collections.namedtuple('RelativeSymbol', '''rfd index''') - read_auxiliary_symbol.init = True - if offset in read_auxiliary_symbol.cache: - return read_auxiliary_symbol.cache[offset] - word0 = struct.unpack('>I', file_data[offset:offset+4])[0] - read_auxiliary_symbol.cache[offset] = read_auxiliary_symbol.header._make(( - read_auxiliary_symbol.type_info._make(((word0 >> 31) & 1, (word0 >> 30) & 1, (word0 >> 24) & 0x3F, (word0 >> 20) & 0xF, (word0 >> 16) & 0xF, (word0 >> 12) & 0xF, (word0 >> 8) & 0xF, (word0 >> 4) & 0xF, word0 & 0xF)), - read_auxiliary_symbol.rel_sym._make(((word0 >> 20) & 0xFFF, word0 & 0xFFFFF)), - word0, word0, word0, word0, word0, word0)) - return read_auxiliary_symbol.cache[offset] - -def read_string(file_data, offset): - current_offset = 0 - current_string = b'' - while True: - char = struct.unpack('c', file_data[offset+current_offset:offset+current_offset+1])[0] - if char == b'\0': - return current_string.decode('ascii') - else: - current_string += char - current_offset += 1 - -def map_relative_file_descriptor(file_data, fd, symbolic_header, rfd_num): - if fd.crfd == 0: - return rfd_num - - offset = symbolic_header.cbRfdOffset - OFFSET + (fd.rfdBase + rfd_num)*4 - return struct.unpack('>I', file_data[offset:offset+4])[0] - -def get_symbol_name_from_aux(file_data, fd, symbolic_header, aux_num, search_for_typedef): - aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + aux_num)*4) - fd_num = aux.rndx.rfd - next_aux = aux_num+1 - if fd_num == 4095: - aux2 = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + next_aux)*4) - fd_num = aux2.isym - next_aux = next_aux+1; - fd_num = map_relative_file_descriptor(file_data, fd, symbolic_header, fd_num) - fd2 = read_file_descriptor(file_data, symbolic_header.cbFdOffset - OFFSET + fd_num*72) - sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + (fd2.isymBase + aux.rndx.index)*12) - ret = '' - #print('%r' % (aux,)); - #print('%r' % (aux2,)); - #print('%r' % (sym,)); - if sym.st == 26 or sym.st == 27: #stStruct, stunion - ret = get_struct_or_union_string(file_data, fd2, symbolic_header, fd2.isymBase + aux.rndx.index, search_for_typedef) - elif sym.st == 28: #stEnum: - ret = get_enum_string(file_data, fd2, symbolic_header, fd2.isymBase + aux.rndx.index) - else: - ret = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd2.issBase + sym.iss) - return (ret, next_aux) - -def get_type_string(file_data, fd, symbolic_header, aux_num, name, search_for_typedef): - ret = '' - aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + aux_num)*4) - #print(''); - #print('%r' % (aux,)); - next_aux = aux_num+1 - has_bitfield = aux.ti.fBitfield == 1 - bitwidth = 0 - if has_bitfield: - bit_aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + next_aux)*4) - bitwidth = bit_aux.isym - next_aux = next_aux+1 - if aux.ti.bt == 12: # btStruct - (ret, next_aux) = get_symbol_name_from_aux(file_data, fd, symbolic_header, next_aux, search_for_typedef) - elif aux.ti.bt == 13: # btUnion - (ret, next_aux) = get_symbol_name_from_aux(file_data, fd, symbolic_header, next_aux, search_for_typedef) - elif aux.ti.bt == 15: # btTypedef - (ret, next_aux) = get_symbol_name_from_aux(file_data, fd, symbolic_header, next_aux, search_for_typedef) - elif aux.ti.bt == 14: # btEnum - (ret, next_aux) = get_symbol_name_from_aux(file_data, fd, symbolic_header, next_aux, search_for_typedef) - else: - if aux.ti.bt >= 36: - print('Error unknow bt: %d' % (aux.ti.bt)) - ret = basic_type_c_list[aux.ti.bt] - - tq_list = (aux.ti.tq0, aux.ti.tq1, aux.ti.tq2, aux.ti.tq3, aux.ti.tq4, aux.ti.tq5) - - # TODO this is very naive and probably does not work in a large amount of cases - last_was_proc = False # if we see a tqProc, assume the next will be a tqPtr - for tq in tq_list: - if tq == 0: # tqNil - break; - elif tq == 1: # tqPtr - if last_was_proc: - last_was_proc = False - continue - ret += '*' - elif tq == 2: # tqProc - last_was_proc = True - name = '(*%s)(/* ECOFF does not store param types */)' % name - elif tq == 3: # tqArray - next_aux += 2 # todo what does this skip over? (Apparantly the type of the index, so always int for C) - array_low_aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + next_aux)*4) - array_high_aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + next_aux+1)*4) - stride_aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + next_aux+2)*4) - next_aux += 3 - if array_high_aux.dnHigh == 0xFFFFFFFF: - name += '[]' - else: - name += '[%d]' % (array_high_aux.dnHigh + 1) - elif tq == 4: # tqFar - print('ERROR tqFar in get_type_name') - elif tq == 5: # tqVol - ret = 'volatile ' + ret - elif tq == 6: # tqConst - ret = 'const ' + ret - if has_bitfield: - name += ' : %d' % bitwidth - return ret + ' ' + name - -def get_enum_string(file_data, fd, symbolic_header, enum_sym_num): - ret = '' - start_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + enum_sym_num*12) - if start_sym.st != 28: - print('ERROR unknown type in get_enum_string start:%d' % start_sym.st) - return ret - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + start_sym.iss) - if name != '': - name += ' ' - ret += 'enum %s{\n' % name - increase_indent() - sym_num = enum_sym_num + 1 - while sym_num < fd.isymBase + start_sym.index: - sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - if sym.st == 8: # stEnd - decrease_indent() - ret += get_indent() - ret += '}' - elif sym.st == 9: # stMember - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + sym.iss) - ret += get_indent() - ret += '%s = %d,\n' % (name, sym.value) - else: - print('ERROR unknown type in get_enum_string:%d' % sym.st) - break - sym_num += 1 - return ret - -def get_struct_or_union_string(file_data, fd, symbolic_header, union_sym_num, search_for_typedef): - ret = '' - start_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + union_sym_num*12) - if search_for_typedef: - typedef_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + (fd.isymBase + start_sym.index)*12) - if typedef_sym.st == 10: # stTypedef - return read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + typedef_sym.iss) - else: - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + start_sym.iss) - if name != '': - return name - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + start_sym.iss) - if name != '': - name += ' ' - if start_sym.st == 26: # stStruct - ret += 'struct %s{\n' % name - increase_indent() - elif start_sym.st == 27: # stUnion - ret += 'union %s{\n' % name - increase_indent() - else: - print('ERROR unknown type in get_struct_or_union_string start:%d' % start_sym.st) - return ret - sym_num = union_sym_num + 1 - while sym_num < fd.isymBase + start_sym.index: - sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - if sym.st == 8: # stEnd - decrease_indent() - ret += get_indent() - ret += '}' - elif sym.st == 9: # stMember - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + sym.iss) - ret += get_indent() - ret += '/* 0x%X */ %s;\n' % (sym.value // 8, get_type_string(file_data, fd, symbolic_header, sym.index, name, True)) - elif sym.st == 26 or sym.st == 27: #stStruct, stUnion - sym_num = fd.isymBase + sym.index - continue - elif sym.st == 34: # stIndirect - # TODO what even is a stIndirect? - sym_num += 1 - else: - print('ERROR unknown type in get_struct_or_union_string:%d' % sym.st) - break - sym_num += 1 - return ret - -def print_typedef_symbols(file_data, fd, symbolic_header, typedef_sym_num): - typedef_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + typedef_sym_num*12) - if typedef_sym.st != 10: # stTypedef - print('ERROR expected stTypedef symbol in print_typedef_symbols, found:%d' % typedef_sym.st) - return - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + typedef_sym.iss) - print('typedef %s;' % get_type_string(file_data, fd, symbolic_header, typedef_sym.index, name, False)) - -def print_procedure(file_data, fd, symbolic_header, proc_sym_num): - proc_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + proc_sym_num*12) - proc_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + proc_sym.iss) - print('%s(' % get_type_string(file_data, fd, symbolic_header, proc_sym.index+1, proc_name, True), end='') - sym_num = proc_sym_num+1 - param_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - first = True - while param_sym.st == 3: # stParam - param_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + param_sym.iss) - print('%s%s' % ('' if first else ', ', - get_type_string(file_data, fd, symbolic_header, param_sym.index, param_name, True)), - end='') - sym_num += 1 - param_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - first = False - - print(');') - - check_indent(); - - comment_old = set_is_comment(True) - while sym_num < fd.isymBase + fd.csym: - sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - sym_num += 1 - if sym.st == 7: # stBlock - print('%s{' % get_indent()) - increase_indent() - elif sym.st == 8: # stEnd - end_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + sym.iss) - if end_name == proc_name: - break - if end_name != '': - # this is a stEnd for something other than the function. Let's back out and return - sym_num -= 1 - break - decrease_indent() - print('%s}' % get_indent()) - elif sym.st == 4: # stLocal - local_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + sym.iss) - is_reg = sym.sc == 4 # scRegister - print('%s%s%s;' % (get_indent(), - 'register ' if is_reg else '', - get_type_string(file_data, fd, symbolic_header, sym.index, local_name, True))) - elif sym.st == 2: # stStatic - static_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + sym.iss) - if sym.index == 0xFFFFF: - print('%sstatic %s; // no type symbol' % (get_indent(),static_name)) - else: - print('%sstatic %s;' % (get_indent(),get_type_string(file_data, fd, symbolic_header, sym.index, static_name, True))) - elif sym.st == 5: # stLabel - label_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + sym.iss) - print('%sLabel: %s @ %d;' % (get_indent(), label_name, sym.value)) - elif sym.st == 6: # stProc - # multiple name for function? - sym_num = print_procedure(file_data, fd, symbolic_header, sym_num-1) - elif sym.st == 26 or sym.st == 27: # stStruct, stUnion - print('%s%s;' % (get_indent(), get_struct_or_union_string(file_data, fd, symbolic_header, sym_num-1, False))) - sym_num = fd.isymBase + sym.index - elif sym.st == 28: # stEnum - print('%s%s;' % (get_indent(), get_enum_string(file_data, fd, symbolic_header, sym_num-1))) - sym_num = fd.isymBase + sym.index - elif sym.st == 34: # stIndirect - # TODO what even is a stIndirect? - indirect_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + sym.iss) - print('%sTODO Indirect: %s;' % (get_indent(), indirect_name)) - else: - print('ERROR unknown st in print_procedure: %d' % sym.st) - set_is_comment(comment_old) - - check_indent(); - - return sym_num - -def print_symbols(file_data, fd, symbolic_header): - sym_num = fd.isymBase - indirects = [] - typedefs = [] - while sym_num < fd.isymBase + fd.csym: - root_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - if root_sym.st == 10: # stTypedef - aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + root_sym.index)*4) - offset = 0 - if aux.ti.fBitfield == 1: - offset = 1 - if aux.ti.bt == 12 or aux.ti.bt == 13 or aux.ti.bt == 14 or aux.ti.bt == 15: # btStruct, btUnion, btEnum, btTypedef - aux2 = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + root_sym.index + 1 + offset)*4) - fd_num = aux2.rndx.rfd - if fd_num == 4095: - fd_num = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + root_sym.index + 2 + offset)*4).isym - fd_num = map_relative_file_descriptor(file_data, fd, symbolic_header, fd_num) - fd2 = read_file_descriptor(file_data, symbolic_header.cbFdOffset - OFFSET + fd_num*72) - sym2 = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + (fd2.isymBase + aux2.rndx.index)*12) - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd2.issBase + sym2.iss) - if name != '': - typedefs.append(name) - elif root_sym.st == 34: # stIndirect - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + root_sym.iss) - indirects.append(name); - sym_num += 1 - sym_num = fd.isymBase - while sym_num < fd.isymBase + fd.csym: - root_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - if root_sym.st == 11: # stFile - file_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + root_sym.iss) - print('// begin file %s\n' % file_name) - sym_num += 1 - leaf_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - while leaf_sym.st != 8: # stEnd - if leaf_sym.st == 26 or leaf_sym.st == 27: # stStruct, stUnion - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + leaf_sym.iss) - if (name != '') and ((name in indirects) or (name not in typedefs)): # TODO - print('%s;\n' % get_struct_or_union_string(file_data, fd, symbolic_header, sym_num, False)) - sym_num = fd.isymBase + leaf_sym.index - elif leaf_sym.st == 28: # stEnum - name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + leaf_sym.iss) - if (name != '') and (name not in typedefs): # TODO - print('%s;\n' % get_enum_string(file_data, fd, symbolic_header, sym_num)) - sym_num = fd.isymBase + leaf_sym.index - elif leaf_sym.st == 10: # stTypedef - # TODO typedef for stIndirect shoulf print the keyword i.e. typdef >struct< THING thing - print_typedef_symbols(file_data, fd, symbolic_header, sym_num) - sym_num += 1 - print('') - elif leaf_sym.st == 6 or leaf_sym.st == 14: # stProc, stStaticProc - # TODO how do stProc and stStaticProc differ? stStaticProc isn't exported? - sym_num = print_procedure(file_data, fd, symbolic_header, sym_num) - print('') - elif leaf_sym.st == 2: # stStatic - static_name = read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + leaf_sym.iss) - if leaf_sym.sc == 2 or leaf_sym.sc == 3 or leaf_sym.sc == 5 or leaf_sym.sc == 15: # scData, scBss, scAbsolute, scRData - if leaf_sym.index != 0xFFFFF: # looks like it's an invalid value for .s files - print('static %s;\n' % get_type_string(file_data, fd, symbolic_header, leaf_sym.index, static_name, True)) - else: - print('static %s;\n' % static_name) - else: - print('ERROR unknown sc for stStatic in print_symbols: %d' % leaf_sym.sc) - sym_num += 1 - elif leaf_sym.st == 34: # stIndirect - # stIndirect is put out when the compiler sees a struct when it is not yet defined - # TODO more info - sym_num += 1 - elif leaf_sym.st == 5: # stLabel - print('// label: %s' % read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + leaf_sym.iss)) - sym_num += 1 - elif leaf_sym.st == 0: # stNil - print('// nil: %s' % read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + leaf_sym.iss)) - sym_num += 1 - else: - print('ERROR unknown st in leaf_sym in print_symbols: %d' % leaf_sym.st) - sym_num += 1 - leaf_sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - sym_num = fd.isymBase + root_sym.index - print('// end file %s' % file_name) - else: - print('ERROR expected st of stFile as only root type in print_symbols:%d' % root_sym.st) - return - -def main(): - global OFFSET - if len(sys.argv) < 2: - return # TODO print usage - - filename = sys.argv[1] - - try: - with open(filename, 'rb') as f: - file_data = f.read() - except IOError: - print('failed to read file ' + filename) - return - - elf_header = read_elf_header(file_data, 0) - section_headers = [] - debug_index = 0xFFFFFFFF - #print('%r' % (elf_header,)) - for i in range(elf_header.e_shnum): - section_headers.append(read_elf_section_header(file_data, elf_header.e_shoff + i*40)) - #print('%r' % (section_headers[i],)) - if section_headers[i].sh_type == 0x70000005: - debug_index = i - - if debug_index != 0xFFFFFFFF: - symbolic_header = read_symbolic_header(file_data, section_headers[debug_index].sh_offset) - file_descriptors = [] - print('%r' % (symbolic_header,)) - # Set offset by assuming that there are no optimization symbols so cbOptOffset points to the start of the symbolic header - #OFFSET = symbolic_header.cbOptOffset - section_headers[debug_index].sh_offset - #print('Using OFFSET of %d' % OFFSET) - #for sym_num in range(symbolic_header.isymMax): - #sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - #print('%d:%r' % (sym_num, (sym,))); - #for aux_num in range(symbolic_header.iauxMax): - #aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + aux_num*4) - #print('%d:%r' % (aux_num, (aux,))); - for file_num in range(symbolic_header.ifdMax): - fd = read_file_descriptor(file_data, symbolic_header.cbFdOffset - OFFSET + file_num*72) - file_descriptors.append(fd) - for file_num in range(symbolic_header.ifdMax): - fd = read_file_descriptor(file_data, symbolic_header.cbFdOffset - OFFSET + file_num*72) - print('%r' % (fd,)) - print(' name:%s' % read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + fd.rss)) - - ''' - print(' Relative File Descriptors:') - for rfd_num in range(fd.rfdBase, fd.rfdBase + fd.crfd): - offset = symbolic_header.cbRfdOffset - OFFSET + rfd_num*4 - rfd_index = struct.unpack('>I', file_data[offset:offset+4])[0] - rfd = read_file_descriptor(file_data, symbolic_header.cbFdOffset - OFFSET + rfd_index*72) - print(' %d:%r' % (rfd_index, (rfd,))) - ''' - - ''' - print(' procedures:') - for proc_num in range(fd.ipdFirst, fd.ipdFirst + fd.cpd): - pd = read_procedure_descriptor(file_data, symbolic_header.cbPdOffset - OFFSET + proc_num*52) - print(' %r' % ((pd,))) - ''' - - ''' - print(' symbols:') - for sym_num in range(fd.isymBase, fd.isymBase + fd.csym): - sym = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + sym_num*12) - print(' %r' % ((sym,))) - print(' name:%s' % read_string(file_data, symbolic_header.cbSsOffset - OFFSET + fd.issBase + sym.iss)) - print(' type:%s(%d)' % (symbol_type_list[sym.st], sym.st)) - print(' storage class:%s(%d)' % (storage_class_list[sym.sc], sym.sc)) - if sym.st == 3 or sym.st == 4 or sym.st == 9 or sym.st == 10 or sym.st == 28: # stParam, stLocal, stMember, stTypedef, stEnum - aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + sym.index)*4) - print(' %r' % ((aux,))) - offset = 0 - if aux.ti.fBitfield == 1: - bitfield_aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + sym.index + 1)*4) - print(' %r' % ((bitfield_aux,))) - offset = 1 - if aux.ti.bt == 12 or aux.ti.bt == 13 or aux.ti.bt == 14 or aux.ti.bt == 15: # btStruct, btUnion, btEnum, btTypedef - aux2 = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + sym.index + 1 + offset)*4) - print(' %r' % ((aux2,))) - fd_num = aux2.rndx.rfd - if fd_num == 4095: - aux3 = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + sym.index + 2 + offset)*4) - print(' %r' % ((aux3,))) - fd_num = aux3.isym - fd_num = map_relative_file_descriptor(file_data, fd, symbolic_header, fd_num) - sym2 = read_symbol(file_data, symbolic_header.cbSymOffset - OFFSET + (file_descriptors[fd_num].isymBase + aux2.rndx.index)*12) - print(' %r' % (sym2,)) - print(' name:%s' % read_string(file_data, symbolic_header.cbSsOffset - OFFSET + file_descriptors[aux3.isym].issBase + sym2.iss)) - if sym.st == 6 or sym.st == 14: # stProc, stStaticProc - # TODO what is the first aux symbol for? - aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + sym.index)*4) - type_aux = read_auxiliary_symbol(file_data, symbolic_header.cbAuxOffset - OFFSET + (fd.iauxBase + sym.index+1)*4) - print(' %r' % ((aux,))) - print(' %r' % ((type_aux,))) - ''' - - print(' pretty print:') - print_symbols(file_data, fd, symbolic_header) - - -main() \ No newline at end of file diff --git a/tools/set_o32abi_bit.py b/tools/set_o32abi_bit.py deleted file mode 100755 index fc22d2c4..00000000 --- a/tools/set_o32abi_bit.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 -import argparse, struct, sys - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - - parser.add_argument('file', help='input file') - args = parser.parse_args() - - with open(args.file, 'r+b') as f: - magic = struct.unpack('>I', f.read(4))[0] - if magic != 0x7F454C46: - print('Error: Not an ELF file') - sys.exit(1) - - f.seek(36) - flags = struct.unpack('>I', f.read(4))[0] - # if flags & 0xF0000000 != 0x20000000: # test for mips3 - # print('Error: Architecture not mips3') - # sys.exit(1) - - flags |= 0x00001000 # set EF_MIPS_ABI_O32 - f.seek(36) - f.write(struct.pack('>I', flags)) - diff --git a/tools/strip_debug.sh b/tools/strip_debug.sh deleted file mode 100755 index 4a8be782..00000000 --- a/tools/strip_debug.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/bash - -cd $1 - -mkdir -p .cmp - -for f in *.o ; -do - ${CROSS}objcopy -p --strip-debug $f .cmp/${f/.o/.cmp.o} -done diff --git a/tools/util.py b/tools/util.py deleted file mode 100644 index b6c61559..00000000 --- a/tools/util.py +++ /dev/null @@ -1,44 +0,0 @@ - -import struct - -def enumerate_stepped(l, start=0, step=1): - p = start - for e in l: - yield p, e - p += step - -def back_align(x, al): - return x - (x % al) - -def forward_align(x, al): - return (x + (al - 1)) & ~(al - 1) - -def as_double(b): - return struct.unpack(">d", b)[0] - -def as_dword(b): - return struct.unpack(">Q", b)[0] - -def as_float(b): - return struct.unpack(">f", b)[0] - -def as_word(b): - return struct.unpack(">I", b)[0] - -def as_hword(b): - return struct.unpack(">H", b)[0] - -def as_double_list(b): - return [i[0] for i in struct.iter_unpack(">d", b)] - -def as_dword_list(b): - return [i[0] for i in struct.iter_unpack(">Q", b)] - -def as_float_list(b): - return [i[0] for i in struct.iter_unpack(">f", b)] - -def as_word_list(b): - return [i[0] for i in struct.iter_unpack(">I", b)] - -def as_hword_list(b): - return [h[0] for h in struct.iter_unpack(">H", b)] From dc57a593befa6020b7434b002b10b12f9509364d Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 16:59:11 -0400 Subject: [PATCH 09/17] Accidentally removed format checker --- tools/check_format.py | 83 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 tools/check_format.py diff --git a/tools/check_format.py b/tools/check_format.py new file mode 100644 index 00000000..bc3e8ff0 --- /dev/null +++ b/tools/check_format.py @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: 2024 zeldaret +# SPDX-License-Identifier: CC0-1.0 + +import subprocess +import argparse +import difflib +import multiprocessing +import glob +import os.path +import sys + +sys.path.insert(0, os.curdir) +import format + +sys.path.pop(0) + + +def get_git_status(): + return subprocess.check_output("git status --porcelain".split(), text=True) + + +def get_modified_files_to_format(compare_to): + modified_files_str = subprocess.check_output( + ["git", "diff", "--name-only", compare_to], text=True + ) + modified_files = set(modified_files_str.splitlines()) + + all_src_files, all_extra_files = format.list_files_to_format() + # Split modified_files between source files and extra files (see format.py) + # This also filters out deleted files that no longer exist + modified_src_files_existing = list(modified_files.intersection(all_src_files)) + modified_extra_files_existing = list(modified_files.intersection(all_extra_files)) + + return modified_src_files_existing, modified_extra_files_existing + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--verbose", action="store_true") + parser.add_argument("--compare-to", dest="compare_to") + args = parser.parse_args() + + if args.compare_to: + src_files, extra_files = get_modified_files_to_format(args.compare_to) + if args.verbose: + print("Formatting specific files:") + print(len(src_files), src_files) + print(len(extra_files), extra_files) + if not src_files and not extra_files: + if args.verbose: + print("Nothing to format") + exit(0) + else: + src_files, extra_files = format.list_files_to_format() + + nb_jobs = multiprocessing.cpu_count() + + git_status_pre = get_git_status() + + format.format_files(src_files, extra_files, nb_jobs) + + git_status_post = get_git_status() + + if git_status_pre != git_status_post: + print( + "Misformatted files found." + " Run ./format.py and verify codegen is not impacted." + ) + for l in difflib.unified_diff( + git_status_pre.splitlines(), + git_status_post.splitlines(), + "Old git status", + "New git status", + ): + print(l) + + with open("changes.patch", "w+") as patchf: + patchf.write(subprocess.check_output("git diff".split(), text=True)) + exit(1) + + +if __name__ == "__main__": + main() From bff0e4538b35687e57c9cd2fbc79a819379e52d8 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 17:00:55 -0400 Subject: [PATCH 10/17] Change VERSION to TARGET --- Makefile | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 7f4e6da8..f5d587c4 100644 --- a/Makefile +++ b/Makefile @@ -19,20 +19,20 @@ endif # Whether to colorize build messages COLOR ?= 1 -# VERSION - selects the version of the library to build +# TARGET - selects the version of the library to build # libultra - standard library # libultra_d - debug library # libultra_rom - final ROM library -VERSION ?= libultra_rom -$(eval $(call validate-option,VERSION,libultra libultra_d libultra_rom)) +TARGET ?= libultra_rom +$(eval $(call validate-option,TARGET,libultra libultra_d libultra_rom)) -ifeq ($(VERSION),libultra) +ifeq ($(TARGET),libultra) OPT_FLAGS := -Os -ggdb3 -ffast-math -fno-unsafe-math-optimizations DEFINES += NDEBUG=1 -else ifeq ($(VERSION),libultra_d) +else ifeq ($(TARGET),libultra_d) OPT_FLAGS := -Og -ggdb3 -ffast-math -fno-unsafe-math-optimizations DEFINES += _DEBUG=1 -else ifeq ($(VERSION),libultra_rom) +else ifeq ($(TARGET),libultra_rom) OPT_FLAGS := -Os -ggdb3 -ffast-math -fno-unsafe-math-optimizations DEFINES += NDEBUG=1 DEFINES += _FINALROM=1 @@ -59,11 +59,9 @@ else $(error Unable to detect a suitable MIPS toolchain installed) endif -TARGET := $(VERSION) - ifeq ($(filter clean,$(MAKECMDGOALS)),) $(info ==== Build Options ====) - $(info Version: $(VERSION)) + $(info Version: $(TARGET)) $(info =======================) endif @@ -72,7 +70,7 @@ endif #==============================================================================# BUILD_DIR_BASE := build # BUILD_DIR is the location where all build artifacts are placed -BUILD_DIR := $(BUILD_DIR_BASE)/$(VERSION) +BUILD_DIR := $(BUILD_DIR_BASE)/$(TARGET) LIB := $(BUILD_DIR)/$(TARGET).a # Directories containing source files @@ -180,21 +178,21 @@ $(BUILD_DIR)/%.o: %.s # Creating final library file $(LIB): $(O_FILES) - @$(PRINT) "$(GREEN)Creating $(VERSION): $(BLUE)$@ $(NO_COL)\n" + @$(PRINT) "$(GREEN)Creating $(TARGET): $(BLUE)$@ $(NO_COL)\n" $(V)$(AR) rcs -o $@ $(O_FILES) all: $(BUILD_DIR_BASE)/libultra.a $(BUILD_DIR_BASE)/libultra_d.a $(BUILD_DIR_BASE)/libultra_rom.a $(BUILD_DIR_BASE)/libultra.a: - $(V)$(MAKE) VERSION=libultra + $(V)$(MAKE) TARGET=libultra $(V)cp $(BUILD_DIR_BASE)/libultra/libultra.a $(BUILD_DIR_BASE) $(BUILD_DIR_BASE)/libultra_d.a: - $(V)$(MAKE) VERSION=libultra_d + $(V)$(MAKE) TARGET=libultra_d $(V)cp $(BUILD_DIR_BASE)/libultra_d/libultra_d.a $(BUILD_DIR_BASE) $(BUILD_DIR_BASE)/libultra_rom.a: - $(V)$(MAKE) VERSION=libultra_rom + $(V)$(MAKE) TARGET=libultra_rom $(V)cp $(BUILD_DIR_BASE)/libultra_rom/libultra_rom.a $(BUILD_DIR_BASE) include install.mk From 72ab4df81912bed9c41ef57f53b94e735332fe52 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 17:17:40 -0400 Subject: [PATCH 11/17] Fix install target by removing unused files and bring back ZSort header --- include/PR/gtoff.h | 12 + include/PR/gzsort.h | 581 +++++++++++++++++++++++++++++++++++++++++ include/PR/sptaskoff.h | 17 ++ install.mk | 8 +- 4 files changed, 614 insertions(+), 4 deletions(-) create mode 100644 include/PR/gtoff.h create mode 100644 include/PR/gzsort.h create mode 100644 include/PR/sptaskoff.h diff --git a/include/PR/gtoff.h b/include/PR/gtoff.h new file mode 100644 index 00000000..f8838ee3 --- /dev/null +++ b/include/PR/gtoff.h @@ -0,0 +1,12 @@ +/* GENERATED FILE, DO NOT EDIT! */ + +/* gtState_t structure offsets for assembly language: */ +#define GT_STATE_SIZE 88 +#define GT_STATE_OFF_RENDSTATE 0x00 +#define GT_STATE_OFF_TEXSTATE 0x04 +#define GT_STATE_OFF_VTXCOUNT 0x08 +#define GT_STATE_OFF_VTXV0 0x09 +#define GT_STATE_OFF_TRICOUNT 0x0a +#define GT_STATE_OFF_RDPCMDS 0x0c +#define GT_STATE_OFF_OTHERMODE 0x10 +#define GT_STATE_OFF_TRANSFORM 0x18 diff --git a/include/PR/gzsort.h b/include/PR/gzsort.h new file mode 100644 index 00000000..e4d267de --- /dev/null +++ b/include/PR/gzsort.h @@ -0,0 +1,581 @@ +/*---------------------------------------------------------------------* + Copyright (C) 1997-1998, Nintendo. + + File gzsort.h + Coded by Yoshitaka Yasumoto. Jun 16, 1997. + + $Id: gzsort.h,v 1.11 1998/01/06 01:28:21 yasu Exp $ + *---------------------------------------------------------------------*/ +#ifndef _GZSORT_H_ +#define _GZSORT_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +/*===========================================================================* + * GBI Commands for ZSort microcode + *===========================================================================*/ +/* GBI Header */ +#define G_ZS_ZOBJ 0x80 +#define G_ZS_RDPCMD 0x81 +#define G_ZS_MOVEMEM 0xdc +#define G_ZS_SENDSIGNAL 0xda +#define G_ZS_WAITSIGNAL 0xd9 +#define G_ZS_SETSUBDL 0xd8 +#define G_ZS_LINKSUBDL 0xd7 +#define G_ZS_MULT_MPMTX 0xd6 +#define G_ZS_MTXCAT 0xd5 +#define G_ZS_MTXTRNSP 0xd4 +#define G_ZS_LIGHTING_L 0xd3 /* Not use */ +#define G_ZS_LIGHTING 0xd2 +#define G_ZS_XFMLIGHT 0xd1 +#define G_ZS_INTERPOLATE 0xd0 + +/*------------------------------------------------------------------------* + * case_G_ZOBJ + * +--------+--------+--------+--------+ + * | Pointr for zobj0:32 | + * +--------+--------+--------+--------+ + * | Pointr for zobj1:32 | + * +--------+--------+--------+--------+ + * Draw zobject links + */ +#define gSPZObject(pkt, objA, objB) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = (unsigned int)(objA); \ + _g->words.w1 = (unsigned int)(objB); \ + } + +#define gsSPZObject(objA, objB) { (unsigned int)(objA), (unsigned int)(objB) } + +/*------------------------------------------------------------------------* + * case_G_RDPCMD + * +--------+--------+--------+--------+ + * | RDPCMD | 0 | + * +--------+--------+--------+--------+ + * | Pointr for rdpcmd:32 | + * +--------+--------+--------+--------+ + * Submit rdp cmd + */ +#define gSPZRdpCmd(pkt, cmd) gImmp1((pkt), G_ZS_RDPCMD, (cmd)) +#define gsSPZRdpCmd(cmd) gsImmp1(G_ZS_RDPCMD, (cmd)) + +/*------------------------------------------------------------------------* + * case_G_MOVEMEM + * +--------+--------++-------+-+----+-+ + * |MOVEMEM | Len:9 | Ofs:9 |ID:5|F| + * +--------+--------++-------+-+----+-+ + * | Pointr for RDRAM:32 | + * +--------+--------+--------+--------+ + * Read/Write DMEM + */ +#define GZM_USER0 0 +#define GZM_USER1 2 +#define GZM_MMTX 4 +#define GZM_PMTX 6 +#define GZM_MPMTX 8 +#define GZM_OTHERMODE 10 +#define GZM_VIEWPORT 12 +#define GZF_LOAD 0 +#define GZF_SAVE 1 + +#define gSPZMoveMem(pkt, id, ofs, len, buffer, flag) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_ZS_MOVEMEM, 24, 8) | _SHIFTL(((len) - 1) >> 3, 15, 9) | _SHIFTL((ofs) >> 3, 6, 9) \ + | (id) | (flag); \ + _g->words.w1 = (unsigned int)(buffer); \ + } + +#define gsSPZMoveMem(id, ofs, len, buffer, flag) \ + { _SHIFTL(G_ZS_MOVEMEM, 24, 8) | _SHIFTL(((len) - 1) >> 3, 15, 9) | _SHIFTL((ofs) >> 3, 6, 9) | (id) | (flag), \ + (unsigned int)(buffer) } + +/*------------------------------------------------------------------------*/ +#define gSPZSetUMem(pkt, umem, size, adrs) gSPZMoveMem((pkt), GZM_USER0, (umem), (size), (adrs), GZF_LOAD) +#define gsSPZSetUMem(umem, size, adrs) gsSPZMoveMem(GZM_USER0, (umem), (size), (adrs), GZF_LOAD) + +#define gSPZGetUMem(pkt, umem, size, adrs) gSPZMoveMem((pkt), GZM_USER0, (umem), (size), (adrs), GZF_SAVE) +#define gsSPZGetUMem(umem, size, adrs) gsSPZMoveMem(GZM_USER0, (umem), (size), (adrs), GZF_SAVE) + +/*------------------------------------------------------------------------*/ +#define gSPZSetMtx(pkt, mid, mp) gSPZMoveMem((pkt), (mid), 0, sizeof(Mtx), (mp), GZF_LOAD) +#define gsSPZSetMtx(mid, mp) gsSPZMoveMem((mid), 0, sizeof(Mtx), (mp), GZF_LOAD) + +/*------------------------------------------------------------------------*/ +#define gSPZGetMtx(pkt, mid, mp) gSPZMoveMem((pkt), (mid), 0, sizeof(Mtx), (mp), GZF_SAVE) +#define gsSPZGetMtx(mid, mp) gsSPZMoveMem((mid), 0, sizeof(Mtx), (mp), GZF_SAVE) + +/*------------------------------------------------------------------------*/ +#define gSPZSetAmbient(pkt, umem, lights) gSPZSetUMem((pkt), (umem), ZSIZE_AMBIENT, (lights)) +#define gsSPZSetAmbient(umem, lights) gsSPZSetUMem((umem), ZSIZE_AMBIENT, (lights)) + +/*------------------------------------------------------------------------*/ +#define gSPZSetDefuse(pkt, umem, lid, lights) \ + gSPZSetUMem((pkt), (umem) + ZSIZE_AMBIENT + (lid) * ZSIZE_LIGHT, ZSIZE_DEFUSE, (lights)) +#define gsSPZSetDefuse(umem, lid, lights) \ + gsSPZSetUMem((umem) + ZSIZE_AMBIENT + (lid) * ZSIZE_LIGHT, ZSIZE_DEFUSE, (lights)) + +/*------------------------------------------------------------------------*/ +#define gSPZSetLookAt(pkt, umem, lid, lookat) \ + { \ + gSPZSetUMem((pkt), (umem) + ZSIZE_AMBIENT + ((lid) + 0) * ZSIZE_LIGHT, ZSIZE_DEFUSE, \ + ((u32)(lookat)) + sizeof(Light) * 0); \ + gSPZSetUMem((pkt), (umem) + ZSIZE_AMBIENT + ((lid) + 1) * ZSIZE_LIGHT, ZSIZE_DEFUSE, \ + ((u32)(lookat)) + sizeof(Light) * 1); \ + } +#define gsSPZSetLookAt(umem, lid, lookat) \ + gsSPZSetUMem((umem) + ZSIZE_AMBIENT + ((lid) + 0) * ZSIZE_LIGHT, ZSIZE_DEFUSE, \ + ((u32)(lookat)) + sizeof(Light) * 0), \ + gsSPZSetUMem((umem) + ZSIZE_AMBIENT + ((lid) + 1) * ZSIZE_LIGHT, ZSIZE_DEFUSE, \ + ((u32)(lookat)) + sizeof(Light) * 1) + +/*------------------------------------------------------------------------*/ +#define gSPZViewport(pkt, v) gSPZMoveMem((pkt), GZM_VIEWPORT, 0, 16, (v), GZF_LOAD) +#define gsSPZViewport(v) gsSPZMoveMem(GZM_VIEWPORT, 0, 16, (v), GZF_LOAD) + +#define GZ_VIEWPORT_FOG_S(in, out) (0x400000 / ((out) - (in))) +#define GZ_VIEWPORT_FOG_T(in, out) ((0x200 * (out)) / ((in) - (out))) + +/*------------------------------------------------------------------------* + * case_G_SENDSIGNAL + * +--------+--------+--------+--------+ + * |Send SIG| Parameter1:24 | + * +--------+--------+--------+--------+ + * | Parameter2:32 | + * +--------+--------+--------+--------+ + * Send signal to CPU + */ +#define GZ_INTR_SPBRK1 SP_STATUS_RSPSIGNAL +#define GZ_INTR_SPBRK2 (SP_SET_RSPSIGNAL | SP_CLR_TASKDONE | SP_CLR_YIELDED | SP_SET_INTR) + +#define gSPZSendMessage(pkt) gDma0p((pkt), G_ZS_SENDSIGNAL, GZ_INTR_SPBRK2, GZ_INTR_SPBRK1) +#define gsSPZSendMessage() gsDma0p(G_ZS_SENDSIGNAL, GZ_INTR_SPBRK2, GZ_INTR_SPBRK1) + +/*------------------------------------------------------------------------* + * case_G_WAITSIGNAL + * +--------+--------+--------+--------+ + * |Wait SIG| Phy Adrs of signal buf:24| + * +--------+--------+--------+--------+ + * | Parameter:32 | + * +--------+--------+--------+--------+ + * Wait signal from CPU + */ +#define gSPZWaitSignal(pkt, adrs, param) gDma0p((pkt), G_ZS_WAITSIGNAL, (param), (adrs)) + +#define gsSPZWaitSignal(adrs, param) \ + { (unsigned int)((char*)(adrs) + ((G_ZS_WAITSIGNAL - 0x80) << 24)), (unsigned int)(param) } + +#define GZ_SENDSIGNAL(adrs, val) \ + { \ + IO_WRITE(&(((zSignal*)(adrs))->t.signal), (val)); \ + IO_WRITE(SP_STATUS_REG, SP_SET_CPUSIGNAL); \ + } + +/*------------------------------------------------------------------------* + * case_G_SETSUBDL + * +--------+--------+--------+--------+ + * |SETSUBDL| 0 | + * +--------+--------+--------+--------+ + * | DL:32 | + * +--------+--------+--------+--------+ + */ +#define gSPZSetSubDL(pkt, dl) gImmp1((pkt), G_ZS_SETSUBDL, (u32)(dl)) +#define gsSPZSetSubDL(dl) gsImmp1(G_ZS_SETSUBDL, (u32)(dl)) + +/*------------------------------------------------------------------------* + * case_G_LINKSUBDL + * +--------+--------+--------+--------+ + * |LINKSUBD| 0 | + * +--------+--------+--------+--------+ + * | 0 | + * +--------+--------+--------+--------+ + */ +#define gSPZLinkSubDL(pkt) gImmp0((pkt), G_ZS_LINKSUBDL) +#define gsSPZLinkSubDL() gsImmp0(G_ZS_LINKSUBDL) + +/*------------------------------------------------------------------------* + * case_G_MULT_MPMTX + * +--------+--------+--------+--------+ + * |MULT_MPM| 0 | Mid:8 | + * +--------+--------+----+---+--------+ + * | Num:8 | Src:12 | Dest:12 | + * +--------+-------------+------------+ + */ +#define gSPZMultMPMtx(pkt, mid, src, num, dest) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_ZS_MULT_MPMTX, 24, 8) | _SHIFTL((mid), 0, 8); \ + _g->words.w1 = _SHIFTL(((num) - 1), 24, 8) | _SHIFTL((src) + 1024, 12, 12) | _SHIFTL((dest) + 1024, 0, 12); \ + } + +#define gsSPZMultMPMtx(mid, src, num, dest) \ + { _SHIFTL(G_ZS_MULT_MPMTX, 24, 8) | _SHIFTL((mid), 0, 8), \ + _SHIFTL(((num) - 1), 24, 8) | _SHIFTL((src) + 1024, 12, 12) | _SHIFTL((dest) + 1024, 0, 12) } + +/*------------------------------------------------------------------------* + * case_G_MTXCAT + * +--------+--------+--------+--------+ + * | MTXCAT | 0 | 0 | MidS:8 | + * +--------+--------+--------+--------+ + * | 0 | MidT:8 | 0 | MidD:8 | + * +--------+--------+--------+--------+ + */ +#define gSPZMtxCat(pkt, midS, midT, midD) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_ZS_MTXCAT, 24, 8) | _SHIFTL((midS), 0, 8); \ + _g->words.w1 = _SHIFTL((midT), 16, 8) | _SHIFTL((midD), 0, 8); \ + } + +#define gsSPZMtxCat(midS, midT, midD) \ + { _SHIFTL(G_ZS_MTXCAT, 24, 8) | _SHIFTL((midS), 0, 8), _SHIFTL((midT), 16, 8) | _SHIFTL((midD), 0, 8) } + +/*------------------------------------------------------------------------* + * case_G_MTXTRNSP + * +--------+--------+--------+--------+ + * |MTXTRNSP| 0 | 0 | 0 | + * +--------+--------+--------+--------+ + * | 0 | 0 | 0 | Mid:8 | + * +--------+--------+--------+--------+ + */ +#define gSPZMtxTrnsp3x3(pkt, mid) gImmp1((pkt), G_ZS_MTXTRNSP, (mid)) +#define gsSPZMtxTrnsp3x3(mid) gsImmp1(G_ZS_MTXTRNSP, (mid)) + +/*------------------------------------------------------------------------* + * case_G_XFMLIGHT + * +--------+--------+--------+--------+ + * |XFMLIGHT| 0 | Mid:8 | + * +--------+----+---+----+---+--------+ + * | 0 | Lnum:8 | Lptr:12 | + * +--------+----+---+----+---+--------+ + */ +#define gSPZXfmLights(pkt, mid, umem, lnum) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_ZS_XFMLIGHT, 24, 8) | (mid); \ + _g->words.w1 = _SHIFTL((lnum) - 1, 12, 8) | _SHIFTL((umem) + 1024, 0, 12); \ + } + +#define gsSPZXfmLights(mid, umem, lnum) \ + { _SHIFTL(G_ZS_XFMLIGHT, 24, 8) | (mid), _SHIFTL((lnum) - 1, 12, 8) | _SHIFTL((umem) + 1024, 0, 12) } + +/*------------------------------------------------------------------------* + * case_G_LIGHTING + * +--------+-------------+------------+ + * |LIGHTING| Color:12 | Norm:12 | + * +--------+-------------+------------+ + * | Num:8 | DestColor:12| DestTxtr:12| + * +--------+-------------+------------+ + * gSPZLight/gSPZLightMaterial + */ +#define gSPZLightMaterial(pkt, csrc, nsrc, num, cdest, tdest) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_ZS_LIGHTING, 24, 8) | _SHIFTL((csrc) + 1024, 12, 12) | _SHIFTL((nsrc) + 1024, 0, 12); \ + _g->words.w1 = _SHIFTL((num) - 1, 24, 8) | _SHIFTL((cdest) + 1024, 12, 12) | _SHIFTL((tdest) + 1024, 0, 12); \ + } + +#define gsSPZLightMaterial(csrc, nsrc, num, cdest, tdest) \ + { _SHIFTL(G_ZS_LIGHTING, 24, 8) | _SHIFTL((csrc) + 1024, 12, 12) | _SHIFTL((nsrc) + 1024, 0, 12), \ + _SHIFTL((num) - 1, 24, 8) | _SHIFTL((cdest) + 1024, 12, 12) | _SHIFTL((tdest) + 1024, 0, 12) } + +#define gSPZLight(pkt, nsrc, num, cdest, tdest) gSPZLightMaterial((pkt), -16, (nsrc), (num), (cdest), (tdest)) +#define gsSPZLight(nsrc, num, cdest, tdest) gsSPZLightMaterial(-16, (nsrc), (num), (cdest), (tdest)) + +/*------------------------------------------------------------------------* + * case_G_INTERPOLATE + * +--------+--------+-----------------+ + * |INTERPLT| Type:8 | Factor:16 | + * +--------+--------+----+------------+ + * | Num:8 | Src1/Dest:12| Src2:12 | + * +--------+-------------+------------+ + */ +#define GZ_INTRP_S16 0 +#define GZ_INTRP_S8 2 +#define GZ_INTRP_U8 4 + +#define gSPZMix(pkt, src1, src2, num, factor, type) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_ZS_INTERPOLATE, 24, 8) | _SHIFTL((type), 16, 8) | _SHIFTL((factor), 0, 16); \ + _g->words.w1 = _SHIFTL((num) / 8 - 1, 24, 8) | _SHIFTL((src1), 12, 12) | _SHIFTL((src2), 0, 12); \ + } + +#define gsSPZMix(src1, src2, num, factor, type) \ + { _SHIFTL(G_ZS_INTERPOLATE, 24, 8) | _SHIFTL((type), 16, 8) | _SHIFTL((factor), 0, 16), \ + _SHIFTL(((num) - 1) / 8, 24, 8) | _SHIFTL((src1), 12, 12) | _SHIFTL((src2), 0, 12) } + +#define gSPZMixS16(pkt, src1, src2, num, factor) gSPZMix((pkt), (src1), (src2), (num), (factor), GZ_INTRP_S16) +#define gSPZMixS8(pkt, src1, src2, num, factor) gSPZMix((pkt), (src1), (src2), (num), (factor), GZ_INTRP_S8) +#define gSPZMixU8(pkt, src1, src2, num, factor) gSPZMix((pkt), (src1), (src2), (num), (factor), GZ_INTRP_U8) + +#define gsSPZMixS16(src1, src2, num, factor) gsSPZMix((src1), (src2), (num), (factor), GZ_INTRP_S16) +#define gsSPZMixS8(src1, src2, num, factor) gsSPZMix((src1), (src2), (num), (factor), GZ_INTRP_S8) +#define gsSPZMixU8(src1, src2, num, factor) gsSPZMix((src1), (src2), (num), (factor), GZ_INTRP_U8) + +/*------------------------------------------------------------------------* + * Compatible GBIs + *------------------------------------------------------------------------*/ +#define gSPZSegment(pkt, segment, base) gSPSegment((pkt), (segment), (base)) +#define gsSPZSegment(segment, base) gsSPSegment((segment), (base)) +#define gSPZPerspNormalize(pkt, s) gSPPerspNormalize((pkt), (s)) +#define gsSPZPerspNormalize(s) gsSPPerspNormalize(s) +#define gSPZDisplayList(pkt, dl) gSPDisplayList((pkt), (dl)) +#define gsSPZDisplayList(dl) gsSPDisplayList(dl) +#define gSPZEndDisplayList(pkt) gSPEndDisplayList(pkt) +#define gsSPZEndDisplayList() gsSPEndDisplayList() + +/*===========================================================================* + * Data structures for ZSort microcode + *===========================================================================*/ + +#define G_ZOBJ_NONE 0x80000000 + +/* ZObject IDs */ +#define ZH_NULL 0 +#define ZH_SHTRI 1 +#define ZH_TXTRI 2 +#define ZH_SHQUAD 3 +#define ZH_TXQUAD 4 +#define ZHDR(pointer, type) ((type) + ((u32)(pointer))) + +/*===========================================================================* + * Useful MACRO + *===========================================================================*/ +#define guZFixLookAt(lp) \ + { \ + (lp)->l[1].l.col[1] = (lp)->l[1].l.colc[1] = 0x00; \ + } + +#if (defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS)) +/*------------------------------------------------------------------------*/ +/* ZMath: Vertex structure format */ + +/*-----------------------------------------------------*/ +#define ZSIZE_VSRC 6 + +typedef struct { + s16 x, y, z; +} zVtxSrc; + +/*-----------------------------------------------------*/ +#define ZSIZE_VDEST 16 + +typedef struct { + s16 sx, sy; + s32 invw; + s16 xi, yi; + u8 cc; + u8 fog; + s16 wi; +} zVtxDest; + +#define GZ_CC_RIGHT 0x01 +#define GZ_CC_TOP 0x02 +#define GZ_CC_NEAR 0x04 +#define GZ_CC_LEFT 0x10 +#define GZ_CC_BOTTOM 0x20 +#define GZ_CC_FAR 0x40 + +/*-----------------------------------------------------*/ +#define ZSIZE_NSRC 3 + +typedef struct { + s8 nx, ny, nz; +} zNorm; + +/*-----------------------------------------------------*/ +#define ZSIZE_CSRC 4 +#define ZSIZE_CDEST 4 + +typedef struct { + u8 r, g, b, a; +} zColor_t; +typedef union { + zColor_t n; + u32 w; +} zColor; + +/*-----------------------------------------------------*/ +#define ZSIZE_TDEST 4 + +typedef struct { + s16 s, t; +} zTxtr_t; +typedef union { + zTxtr_t n; + u32 w; +} zTxtr; + +/*-----------------------------------------------------*/ +#define ZSIZE_LIGHT 24 +#define ZSIZE_DEFUSE 16 +#define ZSIZE_AMBIENT 8 + +/*------------------------------------------------------------------------*/ +/* ZObject: Common header */ +typedef struct { + u32 header; + Gfx* rdpcmd1; +} zHeader_t; + +typedef union { + zHeader_t t; + u64 force_structure_alignment; +} zHeader; + +/*------------------------------------------------------------------------*/ +/* ZObject: Null Node */ +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + Gfx* rdpcmd2; + Gfx* rdpcmd3; +} zNull_t; + +typedef union { + zNull_t t; + u64 force_structure_alignment; +} zNull; + +/*------------------------------------------------------------------------*/ +/* ZObject: Smooth Shaded Triangle */ +typedef struct { + s16 x, y; + u8 r, g, b, a; +} zShVtx; + +/*------------------------------------------------------------------------*/ +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + zShVtx v[3]; +} zShTri_t; + +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + u32 xy0, clr0; + u32 xy1, clr1; + u32 xy2, clr2; +} zShTri_w; + +typedef union { + zShTri_t t; + zShTri_w w; + u64 force_structure_alignment; +} zShTri; + +/*------------------------------------------------------------------------*/ +/* ZObject: Smooth Shaded Quadrangle */ +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + zShVtx v[4]; +} zShQuad_t; + +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + u32 xy0, clr0; + u32 xy1, clr1; + u32 xy2, clr2; + u32 xy3, clr3; +} zShQuad_w; + +typedef union { + zShQuad_t t; + zShQuad_w w; + u64 force_structure_alignment; +} zShQuad; + +/*------------------------------------------------------------------------*/ +/* ZObject: Smooth Shaded and Texture Mapped Triangle */ +typedef struct { + s16 x, y; + u8 r, g, b, a; + s16 s, t; + s32 invw; +} zTxVtx; + +/*------------------------------------------------------------------------*/ +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + Gfx* rdpcmd2; + Gfx* rdpcmd3; + zTxVtx v[3]; +} zTxTri_t; + +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + Gfx* rdpcmd2; + Gfx* rdpcmd3; + u32 xy0, clr0, st0, invw0; + u32 xy1, clr1, st1, invw1; + u32 xy2, clr2, st2, invw2; +} zTxTri_w; + +typedef union { + zTxTri_t t; + zTxTri_w w; + u64 force_structure_alignment; +} zTxTri; + +/*------------------------------------------------------------------------*/ +/* ZObject: Smooth Shaded and Texture Mapped Quadrangle */ +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + Gfx* rdpcmd2; + Gfx* rdpcmd3; + zTxVtx v[4]; +} zTxQuad_t; + +typedef struct { + zHeader* header; + Gfx* rdpcmd1; + Gfx* rdpcmd2; + Gfx* rdpcmd3; + u32 xy0, clr0, st0, invw0; + u32 xy1, clr1, st1, invw1; + u32 xy2, clr2, st2, invw2; + u32 xy3, clr3, st3, invw3; +} zTxQuad_w; + +typedef union { + zTxQuad_t t; + zTxQuad_w w; + u64 force_structure_alignment; +} zTxQuad; + +/*------------------------------------------------------------------------*/ +typedef struct { + u32 signal; + u32 padding; +} zSignal_t; + +typedef union { + zSignal_t t; + u64 force_structure_alignment; +} zSignal; + +/*===========================================================================* + * External functions + *===========================================================================*/ +extern u64 gspZSort_fifoTextStart[], gspZSort_fifoTextEnd[]; +extern u64 gspZSort_fifoDataStart[], gspZSort_fifoDataEnd[]; +extern u64 gspZSort_pl_fifoTextStart[], gspZSort_pl_fifoTextEnd[]; +extern u64 gspZSort_pl_fifoDataStart[], gspZSort_pl_fifoDataEnd[]; +#endif + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif +#endif /* _GZSORT_H_ */ + +/*======== End of gzsort.h ========*/ diff --git a/include/PR/sptaskoff.h b/include/PR/sptaskoff.h new file mode 100644 index 00000000..4ceace68 --- /dev/null +++ b/include/PR/sptaskoff.h @@ -0,0 +1,17 @@ +#define OS_TASK_SIZE 64 +#define OS_TASK_OFF_TYPE 0 +#define OS_TASK_OFF_FLAGS 4 +#define OS_TASK_OFF_UBOOT 8 +#define OS_TASK_OFF_UBOOT_SZ 12 +#define OS_TASK_OFF_UCODE 16 +#define OS_TASK_OFF_UCODE_SZ 20 +#define OS_TASK_OFF_UDATA 24 +#define OS_TASK_OFF_UDATA_SZ 28 +#define OS_TASK_OFF_STACK 32 +#define OS_TASK_OFF_STACK_SZ 36 +#define OS_TASK_OFF_OUTBUFF 40 +#define OS_TASK_OFF_OUTBUFF_SZ 44 +#define OS_TASK_OFF_DATA 48 +#define OS_TASK_OFF_DATA_SZ 52 +#define OS_TASK_OFF_YIELD 56 +#define OS_TASK_OFF_YIELD_SZ 60 diff --git a/install.mk b/install.mk index 90a38849..1516c19e 100644 --- a/install.mk +++ b/install.mk @@ -1,9 +1,9 @@ -PRHFILES = include/PR/os_internal_error.h include/PR/os_internal_thread.h include/PR/rsp.h \ - include/PR/os_time.h include/PR/uportals.h include/PR/os_gio.h include/PR/ramrom.h include/PR/ucode_debug.h \ +PRHFILES = include/PR/os_internal_error.h include/PR/os_internal_thread.h \ + include/PR/os_time.h include/PR/os_gio.h include/PR/ramrom.h \ include/PR/os_cache.h include/PR/os_cont.h include/PR/os_system.h include/PR/os_libc.h include/PR/os_pfs.h \ - include/PR/os_eeprom.h include/PR/sptask.h include/PR/R4300.h include/PR/PRimage.h \ + include/PR/os_eeprom.h include/PR/sptask.h include/PR/R4300.h \ include/PR/gu.h include/PR/gt.h include/PR/os_ai.h include/PR/abi.h include/PR/os_rdp.h include/PR/os_internal_rsp.h \ - include/PR/os_error.h include/PR/os_si.h include/PR/rsp_ipc.h include/PR/os_internal_reg.h include/PR/trace.h \ + include/PR/os_error.h include/PR/os_si.h include/PR/os_internal_reg.h \ include/PR/os_internal.h include/PR/os_message.h include/PR/os_internal_gio.h include/PR/os_motor.h \ include/PR/os_internal_host.h include/PR/os_rsp.h include/PR/os_internal_si.h include/PR/gs2dex.h \ include/PR/region.h include/PR/rcp.h include/PR/sptaskoff.h include/PR/gtoff.h include/PR/ultralog.h \ From 29a686c579c603912430c257671871c5ce583ab2 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 20:53:57 -0400 Subject: [PATCH 12/17] Fix CI typo --- .github/workflows/ci_gcc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_gcc.yml b/.github/workflows/ci_gcc.yml index 5df45f6c..c6bbd480 100644 --- a/.github/workflows/ci_gcc.yml +++ b/.github/workflows/ci_gcc.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - version: [[libultra, libultra_d, libultra_rom]] + version: [libultra, libultra_d, libultra_rom] steps: - name: Checkout repository From 4d92769bf47a8208127e7f42353c0e3737d32400 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 20:57:55 -0400 Subject: [PATCH 13/17] gzsort header removed, I will figure out how to add it in modsdk later --- include/PR/gzsort.h | 581 -------------------------------------------- 1 file changed, 581 deletions(-) delete mode 100644 include/PR/gzsort.h diff --git a/include/PR/gzsort.h b/include/PR/gzsort.h deleted file mode 100644 index e4d267de..00000000 --- a/include/PR/gzsort.h +++ /dev/null @@ -1,581 +0,0 @@ -/*---------------------------------------------------------------------* - Copyright (C) 1997-1998, Nintendo. - - File gzsort.h - Coded by Yoshitaka Yasumoto. Jun 16, 1997. - - $Id: gzsort.h,v 1.11 1998/01/06 01:28:21 yasu Exp $ - *---------------------------------------------------------------------*/ -#ifndef _GZSORT_H_ -#define _GZSORT_H_ - -#ifdef _LANGUAGE_C_PLUS_PLUS -extern "C" { -#endif - -/*===========================================================================* - * GBI Commands for ZSort microcode - *===========================================================================*/ -/* GBI Header */ -#define G_ZS_ZOBJ 0x80 -#define G_ZS_RDPCMD 0x81 -#define G_ZS_MOVEMEM 0xdc -#define G_ZS_SENDSIGNAL 0xda -#define G_ZS_WAITSIGNAL 0xd9 -#define G_ZS_SETSUBDL 0xd8 -#define G_ZS_LINKSUBDL 0xd7 -#define G_ZS_MULT_MPMTX 0xd6 -#define G_ZS_MTXCAT 0xd5 -#define G_ZS_MTXTRNSP 0xd4 -#define G_ZS_LIGHTING_L 0xd3 /* Not use */ -#define G_ZS_LIGHTING 0xd2 -#define G_ZS_XFMLIGHT 0xd1 -#define G_ZS_INTERPOLATE 0xd0 - -/*------------------------------------------------------------------------* - * case_G_ZOBJ - * +--------+--------+--------+--------+ - * | Pointr for zobj0:32 | - * +--------+--------+--------+--------+ - * | Pointr for zobj1:32 | - * +--------+--------+--------+--------+ - * Draw zobject links - */ -#define gSPZObject(pkt, objA, objB) \ - { \ - Gfx* _g = (Gfx*)(pkt); \ - _g->words.w0 = (unsigned int)(objA); \ - _g->words.w1 = (unsigned int)(objB); \ - } - -#define gsSPZObject(objA, objB) { (unsigned int)(objA), (unsigned int)(objB) } - -/*------------------------------------------------------------------------* - * case_G_RDPCMD - * +--------+--------+--------+--------+ - * | RDPCMD | 0 | - * +--------+--------+--------+--------+ - * | Pointr for rdpcmd:32 | - * +--------+--------+--------+--------+ - * Submit rdp cmd - */ -#define gSPZRdpCmd(pkt, cmd) gImmp1((pkt), G_ZS_RDPCMD, (cmd)) -#define gsSPZRdpCmd(cmd) gsImmp1(G_ZS_RDPCMD, (cmd)) - -/*------------------------------------------------------------------------* - * case_G_MOVEMEM - * +--------+--------++-------+-+----+-+ - * |MOVEMEM | Len:9 | Ofs:9 |ID:5|F| - * +--------+--------++-------+-+----+-+ - * | Pointr for RDRAM:32 | - * +--------+--------+--------+--------+ - * Read/Write DMEM - */ -#define GZM_USER0 0 -#define GZM_USER1 2 -#define GZM_MMTX 4 -#define GZM_PMTX 6 -#define GZM_MPMTX 8 -#define GZM_OTHERMODE 10 -#define GZM_VIEWPORT 12 -#define GZF_LOAD 0 -#define GZF_SAVE 1 - -#define gSPZMoveMem(pkt, id, ofs, len, buffer, flag) \ - { \ - Gfx* _g = (Gfx*)(pkt); \ - _g->words.w0 = _SHIFTL(G_ZS_MOVEMEM, 24, 8) | _SHIFTL(((len) - 1) >> 3, 15, 9) | _SHIFTL((ofs) >> 3, 6, 9) \ - | (id) | (flag); \ - _g->words.w1 = (unsigned int)(buffer); \ - } - -#define gsSPZMoveMem(id, ofs, len, buffer, flag) \ - { _SHIFTL(G_ZS_MOVEMEM, 24, 8) | _SHIFTL(((len) - 1) >> 3, 15, 9) | _SHIFTL((ofs) >> 3, 6, 9) | (id) | (flag), \ - (unsigned int)(buffer) } - -/*------------------------------------------------------------------------*/ -#define gSPZSetUMem(pkt, umem, size, adrs) gSPZMoveMem((pkt), GZM_USER0, (umem), (size), (adrs), GZF_LOAD) -#define gsSPZSetUMem(umem, size, adrs) gsSPZMoveMem(GZM_USER0, (umem), (size), (adrs), GZF_LOAD) - -#define gSPZGetUMem(pkt, umem, size, adrs) gSPZMoveMem((pkt), GZM_USER0, (umem), (size), (adrs), GZF_SAVE) -#define gsSPZGetUMem(umem, size, adrs) gsSPZMoveMem(GZM_USER0, (umem), (size), (adrs), GZF_SAVE) - -/*------------------------------------------------------------------------*/ -#define gSPZSetMtx(pkt, mid, mp) gSPZMoveMem((pkt), (mid), 0, sizeof(Mtx), (mp), GZF_LOAD) -#define gsSPZSetMtx(mid, mp) gsSPZMoveMem((mid), 0, sizeof(Mtx), (mp), GZF_LOAD) - -/*------------------------------------------------------------------------*/ -#define gSPZGetMtx(pkt, mid, mp) gSPZMoveMem((pkt), (mid), 0, sizeof(Mtx), (mp), GZF_SAVE) -#define gsSPZGetMtx(mid, mp) gsSPZMoveMem((mid), 0, sizeof(Mtx), (mp), GZF_SAVE) - -/*------------------------------------------------------------------------*/ -#define gSPZSetAmbient(pkt, umem, lights) gSPZSetUMem((pkt), (umem), ZSIZE_AMBIENT, (lights)) -#define gsSPZSetAmbient(umem, lights) gsSPZSetUMem((umem), ZSIZE_AMBIENT, (lights)) - -/*------------------------------------------------------------------------*/ -#define gSPZSetDefuse(pkt, umem, lid, lights) \ - gSPZSetUMem((pkt), (umem) + ZSIZE_AMBIENT + (lid) * ZSIZE_LIGHT, ZSIZE_DEFUSE, (lights)) -#define gsSPZSetDefuse(umem, lid, lights) \ - gsSPZSetUMem((umem) + ZSIZE_AMBIENT + (lid) * ZSIZE_LIGHT, ZSIZE_DEFUSE, (lights)) - -/*------------------------------------------------------------------------*/ -#define gSPZSetLookAt(pkt, umem, lid, lookat) \ - { \ - gSPZSetUMem((pkt), (umem) + ZSIZE_AMBIENT + ((lid) + 0) * ZSIZE_LIGHT, ZSIZE_DEFUSE, \ - ((u32)(lookat)) + sizeof(Light) * 0); \ - gSPZSetUMem((pkt), (umem) + ZSIZE_AMBIENT + ((lid) + 1) * ZSIZE_LIGHT, ZSIZE_DEFUSE, \ - ((u32)(lookat)) + sizeof(Light) * 1); \ - } -#define gsSPZSetLookAt(umem, lid, lookat) \ - gsSPZSetUMem((umem) + ZSIZE_AMBIENT + ((lid) + 0) * ZSIZE_LIGHT, ZSIZE_DEFUSE, \ - ((u32)(lookat)) + sizeof(Light) * 0), \ - gsSPZSetUMem((umem) + ZSIZE_AMBIENT + ((lid) + 1) * ZSIZE_LIGHT, ZSIZE_DEFUSE, \ - ((u32)(lookat)) + sizeof(Light) * 1) - -/*------------------------------------------------------------------------*/ -#define gSPZViewport(pkt, v) gSPZMoveMem((pkt), GZM_VIEWPORT, 0, 16, (v), GZF_LOAD) -#define gsSPZViewport(v) gsSPZMoveMem(GZM_VIEWPORT, 0, 16, (v), GZF_LOAD) - -#define GZ_VIEWPORT_FOG_S(in, out) (0x400000 / ((out) - (in))) -#define GZ_VIEWPORT_FOG_T(in, out) ((0x200 * (out)) / ((in) - (out))) - -/*------------------------------------------------------------------------* - * case_G_SENDSIGNAL - * +--------+--------+--------+--------+ - * |Send SIG| Parameter1:24 | - * +--------+--------+--------+--------+ - * | Parameter2:32 | - * +--------+--------+--------+--------+ - * Send signal to CPU - */ -#define GZ_INTR_SPBRK1 SP_STATUS_RSPSIGNAL -#define GZ_INTR_SPBRK2 (SP_SET_RSPSIGNAL | SP_CLR_TASKDONE | SP_CLR_YIELDED | SP_SET_INTR) - -#define gSPZSendMessage(pkt) gDma0p((pkt), G_ZS_SENDSIGNAL, GZ_INTR_SPBRK2, GZ_INTR_SPBRK1) -#define gsSPZSendMessage() gsDma0p(G_ZS_SENDSIGNAL, GZ_INTR_SPBRK2, GZ_INTR_SPBRK1) - -/*------------------------------------------------------------------------* - * case_G_WAITSIGNAL - * +--------+--------+--------+--------+ - * |Wait SIG| Phy Adrs of signal buf:24| - * +--------+--------+--------+--------+ - * | Parameter:32 | - * +--------+--------+--------+--------+ - * Wait signal from CPU - */ -#define gSPZWaitSignal(pkt, adrs, param) gDma0p((pkt), G_ZS_WAITSIGNAL, (param), (adrs)) - -#define gsSPZWaitSignal(adrs, param) \ - { (unsigned int)((char*)(adrs) + ((G_ZS_WAITSIGNAL - 0x80) << 24)), (unsigned int)(param) } - -#define GZ_SENDSIGNAL(adrs, val) \ - { \ - IO_WRITE(&(((zSignal*)(adrs))->t.signal), (val)); \ - IO_WRITE(SP_STATUS_REG, SP_SET_CPUSIGNAL); \ - } - -/*------------------------------------------------------------------------* - * case_G_SETSUBDL - * +--------+--------+--------+--------+ - * |SETSUBDL| 0 | - * +--------+--------+--------+--------+ - * | DL:32 | - * +--------+--------+--------+--------+ - */ -#define gSPZSetSubDL(pkt, dl) gImmp1((pkt), G_ZS_SETSUBDL, (u32)(dl)) -#define gsSPZSetSubDL(dl) gsImmp1(G_ZS_SETSUBDL, (u32)(dl)) - -/*------------------------------------------------------------------------* - * case_G_LINKSUBDL - * +--------+--------+--------+--------+ - * |LINKSUBD| 0 | - * +--------+--------+--------+--------+ - * | 0 | - * +--------+--------+--------+--------+ - */ -#define gSPZLinkSubDL(pkt) gImmp0((pkt), G_ZS_LINKSUBDL) -#define gsSPZLinkSubDL() gsImmp0(G_ZS_LINKSUBDL) - -/*------------------------------------------------------------------------* - * case_G_MULT_MPMTX - * +--------+--------+--------+--------+ - * |MULT_MPM| 0 | Mid:8 | - * +--------+--------+----+---+--------+ - * | Num:8 | Src:12 | Dest:12 | - * +--------+-------------+------------+ - */ -#define gSPZMultMPMtx(pkt, mid, src, num, dest) \ - { \ - Gfx* _g = (Gfx*)(pkt); \ - _g->words.w0 = _SHIFTL(G_ZS_MULT_MPMTX, 24, 8) | _SHIFTL((mid), 0, 8); \ - _g->words.w1 = _SHIFTL(((num) - 1), 24, 8) | _SHIFTL((src) + 1024, 12, 12) | _SHIFTL((dest) + 1024, 0, 12); \ - } - -#define gsSPZMultMPMtx(mid, src, num, dest) \ - { _SHIFTL(G_ZS_MULT_MPMTX, 24, 8) | _SHIFTL((mid), 0, 8), \ - _SHIFTL(((num) - 1), 24, 8) | _SHIFTL((src) + 1024, 12, 12) | _SHIFTL((dest) + 1024, 0, 12) } - -/*------------------------------------------------------------------------* - * case_G_MTXCAT - * +--------+--------+--------+--------+ - * | MTXCAT | 0 | 0 | MidS:8 | - * +--------+--------+--------+--------+ - * | 0 | MidT:8 | 0 | MidD:8 | - * +--------+--------+--------+--------+ - */ -#define gSPZMtxCat(pkt, midS, midT, midD) \ - { \ - Gfx* _g = (Gfx*)(pkt); \ - _g->words.w0 = _SHIFTL(G_ZS_MTXCAT, 24, 8) | _SHIFTL((midS), 0, 8); \ - _g->words.w1 = _SHIFTL((midT), 16, 8) | _SHIFTL((midD), 0, 8); \ - } - -#define gsSPZMtxCat(midS, midT, midD) \ - { _SHIFTL(G_ZS_MTXCAT, 24, 8) | _SHIFTL((midS), 0, 8), _SHIFTL((midT), 16, 8) | _SHIFTL((midD), 0, 8) } - -/*------------------------------------------------------------------------* - * case_G_MTXTRNSP - * +--------+--------+--------+--------+ - * |MTXTRNSP| 0 | 0 | 0 | - * +--------+--------+--------+--------+ - * | 0 | 0 | 0 | Mid:8 | - * +--------+--------+--------+--------+ - */ -#define gSPZMtxTrnsp3x3(pkt, mid) gImmp1((pkt), G_ZS_MTXTRNSP, (mid)) -#define gsSPZMtxTrnsp3x3(mid) gsImmp1(G_ZS_MTXTRNSP, (mid)) - -/*------------------------------------------------------------------------* - * case_G_XFMLIGHT - * +--------+--------+--------+--------+ - * |XFMLIGHT| 0 | Mid:8 | - * +--------+----+---+----+---+--------+ - * | 0 | Lnum:8 | Lptr:12 | - * +--------+----+---+----+---+--------+ - */ -#define gSPZXfmLights(pkt, mid, umem, lnum) \ - { \ - Gfx* _g = (Gfx*)(pkt); \ - _g->words.w0 = _SHIFTL(G_ZS_XFMLIGHT, 24, 8) | (mid); \ - _g->words.w1 = _SHIFTL((lnum) - 1, 12, 8) | _SHIFTL((umem) + 1024, 0, 12); \ - } - -#define gsSPZXfmLights(mid, umem, lnum) \ - { _SHIFTL(G_ZS_XFMLIGHT, 24, 8) | (mid), _SHIFTL((lnum) - 1, 12, 8) | _SHIFTL((umem) + 1024, 0, 12) } - -/*------------------------------------------------------------------------* - * case_G_LIGHTING - * +--------+-------------+------------+ - * |LIGHTING| Color:12 | Norm:12 | - * +--------+-------------+------------+ - * | Num:8 | DestColor:12| DestTxtr:12| - * +--------+-------------+------------+ - * gSPZLight/gSPZLightMaterial - */ -#define gSPZLightMaterial(pkt, csrc, nsrc, num, cdest, tdest) \ - { \ - Gfx* _g = (Gfx*)(pkt); \ - _g->words.w0 = _SHIFTL(G_ZS_LIGHTING, 24, 8) | _SHIFTL((csrc) + 1024, 12, 12) | _SHIFTL((nsrc) + 1024, 0, 12); \ - _g->words.w1 = _SHIFTL((num) - 1, 24, 8) | _SHIFTL((cdest) + 1024, 12, 12) | _SHIFTL((tdest) + 1024, 0, 12); \ - } - -#define gsSPZLightMaterial(csrc, nsrc, num, cdest, tdest) \ - { _SHIFTL(G_ZS_LIGHTING, 24, 8) | _SHIFTL((csrc) + 1024, 12, 12) | _SHIFTL((nsrc) + 1024, 0, 12), \ - _SHIFTL((num) - 1, 24, 8) | _SHIFTL((cdest) + 1024, 12, 12) | _SHIFTL((tdest) + 1024, 0, 12) } - -#define gSPZLight(pkt, nsrc, num, cdest, tdest) gSPZLightMaterial((pkt), -16, (nsrc), (num), (cdest), (tdest)) -#define gsSPZLight(nsrc, num, cdest, tdest) gsSPZLightMaterial(-16, (nsrc), (num), (cdest), (tdest)) - -/*------------------------------------------------------------------------* - * case_G_INTERPOLATE - * +--------+--------+-----------------+ - * |INTERPLT| Type:8 | Factor:16 | - * +--------+--------+----+------------+ - * | Num:8 | Src1/Dest:12| Src2:12 | - * +--------+-------------+------------+ - */ -#define GZ_INTRP_S16 0 -#define GZ_INTRP_S8 2 -#define GZ_INTRP_U8 4 - -#define gSPZMix(pkt, src1, src2, num, factor, type) \ - { \ - Gfx* _g = (Gfx*)(pkt); \ - _g->words.w0 = _SHIFTL(G_ZS_INTERPOLATE, 24, 8) | _SHIFTL((type), 16, 8) | _SHIFTL((factor), 0, 16); \ - _g->words.w1 = _SHIFTL((num) / 8 - 1, 24, 8) | _SHIFTL((src1), 12, 12) | _SHIFTL((src2), 0, 12); \ - } - -#define gsSPZMix(src1, src2, num, factor, type) \ - { _SHIFTL(G_ZS_INTERPOLATE, 24, 8) | _SHIFTL((type), 16, 8) | _SHIFTL((factor), 0, 16), \ - _SHIFTL(((num) - 1) / 8, 24, 8) | _SHIFTL((src1), 12, 12) | _SHIFTL((src2), 0, 12) } - -#define gSPZMixS16(pkt, src1, src2, num, factor) gSPZMix((pkt), (src1), (src2), (num), (factor), GZ_INTRP_S16) -#define gSPZMixS8(pkt, src1, src2, num, factor) gSPZMix((pkt), (src1), (src2), (num), (factor), GZ_INTRP_S8) -#define gSPZMixU8(pkt, src1, src2, num, factor) gSPZMix((pkt), (src1), (src2), (num), (factor), GZ_INTRP_U8) - -#define gsSPZMixS16(src1, src2, num, factor) gsSPZMix((src1), (src2), (num), (factor), GZ_INTRP_S16) -#define gsSPZMixS8(src1, src2, num, factor) gsSPZMix((src1), (src2), (num), (factor), GZ_INTRP_S8) -#define gsSPZMixU8(src1, src2, num, factor) gsSPZMix((src1), (src2), (num), (factor), GZ_INTRP_U8) - -/*------------------------------------------------------------------------* - * Compatible GBIs - *------------------------------------------------------------------------*/ -#define gSPZSegment(pkt, segment, base) gSPSegment((pkt), (segment), (base)) -#define gsSPZSegment(segment, base) gsSPSegment((segment), (base)) -#define gSPZPerspNormalize(pkt, s) gSPPerspNormalize((pkt), (s)) -#define gsSPZPerspNormalize(s) gsSPPerspNormalize(s) -#define gSPZDisplayList(pkt, dl) gSPDisplayList((pkt), (dl)) -#define gsSPZDisplayList(dl) gsSPDisplayList(dl) -#define gSPZEndDisplayList(pkt) gSPEndDisplayList(pkt) -#define gsSPZEndDisplayList() gsSPEndDisplayList() - -/*===========================================================================* - * Data structures for ZSort microcode - *===========================================================================*/ - -#define G_ZOBJ_NONE 0x80000000 - -/* ZObject IDs */ -#define ZH_NULL 0 -#define ZH_SHTRI 1 -#define ZH_TXTRI 2 -#define ZH_SHQUAD 3 -#define ZH_TXQUAD 4 -#define ZHDR(pointer, type) ((type) + ((u32)(pointer))) - -/*===========================================================================* - * Useful MACRO - *===========================================================================*/ -#define guZFixLookAt(lp) \ - { \ - (lp)->l[1].l.col[1] = (lp)->l[1].l.colc[1] = 0x00; \ - } - -#if (defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS)) -/*------------------------------------------------------------------------*/ -/* ZMath: Vertex structure format */ - -/*-----------------------------------------------------*/ -#define ZSIZE_VSRC 6 - -typedef struct { - s16 x, y, z; -} zVtxSrc; - -/*-----------------------------------------------------*/ -#define ZSIZE_VDEST 16 - -typedef struct { - s16 sx, sy; - s32 invw; - s16 xi, yi; - u8 cc; - u8 fog; - s16 wi; -} zVtxDest; - -#define GZ_CC_RIGHT 0x01 -#define GZ_CC_TOP 0x02 -#define GZ_CC_NEAR 0x04 -#define GZ_CC_LEFT 0x10 -#define GZ_CC_BOTTOM 0x20 -#define GZ_CC_FAR 0x40 - -/*-----------------------------------------------------*/ -#define ZSIZE_NSRC 3 - -typedef struct { - s8 nx, ny, nz; -} zNorm; - -/*-----------------------------------------------------*/ -#define ZSIZE_CSRC 4 -#define ZSIZE_CDEST 4 - -typedef struct { - u8 r, g, b, a; -} zColor_t; -typedef union { - zColor_t n; - u32 w; -} zColor; - -/*-----------------------------------------------------*/ -#define ZSIZE_TDEST 4 - -typedef struct { - s16 s, t; -} zTxtr_t; -typedef union { - zTxtr_t n; - u32 w; -} zTxtr; - -/*-----------------------------------------------------*/ -#define ZSIZE_LIGHT 24 -#define ZSIZE_DEFUSE 16 -#define ZSIZE_AMBIENT 8 - -/*------------------------------------------------------------------------*/ -/* ZObject: Common header */ -typedef struct { - u32 header; - Gfx* rdpcmd1; -} zHeader_t; - -typedef union { - zHeader_t t; - u64 force_structure_alignment; -} zHeader; - -/*------------------------------------------------------------------------*/ -/* ZObject: Null Node */ -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - Gfx* rdpcmd2; - Gfx* rdpcmd3; -} zNull_t; - -typedef union { - zNull_t t; - u64 force_structure_alignment; -} zNull; - -/*------------------------------------------------------------------------*/ -/* ZObject: Smooth Shaded Triangle */ -typedef struct { - s16 x, y; - u8 r, g, b, a; -} zShVtx; - -/*------------------------------------------------------------------------*/ -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - zShVtx v[3]; -} zShTri_t; - -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - u32 xy0, clr0; - u32 xy1, clr1; - u32 xy2, clr2; -} zShTri_w; - -typedef union { - zShTri_t t; - zShTri_w w; - u64 force_structure_alignment; -} zShTri; - -/*------------------------------------------------------------------------*/ -/* ZObject: Smooth Shaded Quadrangle */ -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - zShVtx v[4]; -} zShQuad_t; - -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - u32 xy0, clr0; - u32 xy1, clr1; - u32 xy2, clr2; - u32 xy3, clr3; -} zShQuad_w; - -typedef union { - zShQuad_t t; - zShQuad_w w; - u64 force_structure_alignment; -} zShQuad; - -/*------------------------------------------------------------------------*/ -/* ZObject: Smooth Shaded and Texture Mapped Triangle */ -typedef struct { - s16 x, y; - u8 r, g, b, a; - s16 s, t; - s32 invw; -} zTxVtx; - -/*------------------------------------------------------------------------*/ -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - Gfx* rdpcmd2; - Gfx* rdpcmd3; - zTxVtx v[3]; -} zTxTri_t; - -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - Gfx* rdpcmd2; - Gfx* rdpcmd3; - u32 xy0, clr0, st0, invw0; - u32 xy1, clr1, st1, invw1; - u32 xy2, clr2, st2, invw2; -} zTxTri_w; - -typedef union { - zTxTri_t t; - zTxTri_w w; - u64 force_structure_alignment; -} zTxTri; - -/*------------------------------------------------------------------------*/ -/* ZObject: Smooth Shaded and Texture Mapped Quadrangle */ -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - Gfx* rdpcmd2; - Gfx* rdpcmd3; - zTxVtx v[4]; -} zTxQuad_t; - -typedef struct { - zHeader* header; - Gfx* rdpcmd1; - Gfx* rdpcmd2; - Gfx* rdpcmd3; - u32 xy0, clr0, st0, invw0; - u32 xy1, clr1, st1, invw1; - u32 xy2, clr2, st2, invw2; - u32 xy3, clr3, st3, invw3; -} zTxQuad_w; - -typedef union { - zTxQuad_t t; - zTxQuad_w w; - u64 force_structure_alignment; -} zTxQuad; - -/*------------------------------------------------------------------------*/ -typedef struct { - u32 signal; - u32 padding; -} zSignal_t; - -typedef union { - zSignal_t t; - u64 force_structure_alignment; -} zSignal; - -/*===========================================================================* - * External functions - *===========================================================================*/ -extern u64 gspZSort_fifoTextStart[], gspZSort_fifoTextEnd[]; -extern u64 gspZSort_fifoDataStart[], gspZSort_fifoDataEnd[]; -extern u64 gspZSort_pl_fifoTextStart[], gspZSort_pl_fifoTextEnd[]; -extern u64 gspZSort_pl_fifoDataStart[], gspZSort_pl_fifoDataEnd[]; -#endif - -#ifdef _LANGUAGE_C_PLUS_PLUS -} -#endif -#endif /* _GZSORT_H_ */ - -/*======== End of gzsort.h ========*/ From ab8d22012ad429ced5191ae14bdc4ae35f99b284 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 21:01:45 -0400 Subject: [PATCH 14/17] just copy the whole PR folder instead and remove install.mk, unnecessary --- Makefile | 17 ++++++++++++++++- install.mk | 32 -------------------------------- 2 files changed, 16 insertions(+), 33 deletions(-) delete mode 100644 install.mk diff --git a/Makefile b/Makefile index f5d587c4..a19c89f7 100644 --- a/Makefile +++ b/Makefile @@ -195,7 +195,22 @@ $(BUILD_DIR_BASE)/libultra_rom.a: $(V)$(MAKE) TARGET=libultra_rom $(V)cp $(BUILD_DIR_BASE)/libultra_rom/libultra_rom.a $(BUILD_DIR_BASE) -include install.mk +install: all + $(V)mkdir -p /opt/crashsdk/mips64-elf/lib/ /opt/crashsdk/mips64-elf/include/ /opt/crashsdk/mips64-elf/include/PR + @$(PRINT) "$(GREEN)Copying libultra.a$(NO_COL)\n" + $(V)cp $(BUILD_DIR_BASE)/libultra.a /opt/crashsdk/mips64-elf/lib/libultra.a + @$(PRINT) "$(GREEN)Copying libultra_d.a$(NO_COL)\n" + $(V)cp $(BUILD_DIR_BASE)/libultra_d.a /opt/crashsdk/mips64-elf/lib/libultra_d.a + @$(PRINT) "$(GREEN)Copying libultra_rom.a$(NO_COL)\n" + $(V)cp $(BUILD_DIR_BASE)/libultra_rom.a /opt/crashsdk/mips64-elf/lib/libultra_rom.a + @$(PRINT) "$(GREEN)Copying assert.h$(NO_COL)\n" + $(V)cp include/assert.h /opt/crashsdk/mips64-elf/include/assert.h + @$(PRINT) "$(GREEN)Copying ultra64.h$(NO_COL)\n" + $(V)cp include/ultra64.h /opt/crashsdk/mips64-elf/include/ultra64.h + @$(PRINT) "$(GREEN)Copying ultrahost.h$(NO_COL)\n" + $(V)cp include/ultrahost.h /opt/crashsdk/mips64-elf/include/ultrahost.h + @$(PRINT) "$(GREEN)Copying PR headers$(NO_COL)\n" + $(V)cp -r include/PR /opt/crashsdk/mips64-elf/include/PR .PHONY: clean default all install pkginstall # with no prerequisites, .SECONDARY causes no intermediate target to be removed diff --git a/install.mk b/install.mk deleted file mode 100644 index 1516c19e..00000000 --- a/install.mk +++ /dev/null @@ -1,32 +0,0 @@ -PRHFILES = include/PR/os_internal_error.h include/PR/os_internal_thread.h \ - include/PR/os_time.h include/PR/os_gio.h include/PR/ramrom.h \ - include/PR/os_cache.h include/PR/os_cont.h include/PR/os_system.h include/PR/os_libc.h include/PR/os_pfs.h \ - include/PR/os_eeprom.h include/PR/sptask.h include/PR/R4300.h \ - include/PR/gu.h include/PR/gt.h include/PR/os_ai.h include/PR/abi.h include/PR/os_rdp.h include/PR/os_internal_rsp.h \ - include/PR/os_error.h include/PR/os_si.h include/PR/os_internal_reg.h \ - include/PR/os_internal.h include/PR/os_message.h include/PR/os_internal_gio.h include/PR/os_motor.h \ - include/PR/os_internal_host.h include/PR/os_rsp.h include/PR/os_internal_si.h include/PR/gs2dex.h \ - include/PR/region.h include/PR/rcp.h include/PR/sptaskoff.h include/PR/gtoff.h include/PR/ultralog.h \ - include/PR/os_tlb.h include/PR/os_thread.h include/PR/os_internal_flash.h \ - include/PR/rmon.h include/PR/os_internal_tlb.h include/PR/sp.h include/PR/os_exception.h include/PR/ucode.h \ - include/PR/os_debug.h include/PR/os_vi.h include/PR/gbi.h include/PR/libaudio.h include/PR/os_internal_debug.h \ - include/PR/os_host.h include/PR/os_gbpak.h include/PR/os_version.h include/PR/os_convert.h include/PR/os_internal_exception.h \ - include/PR/ultratypes.h include/PR/rdb.h include/PR/os_pi.h include/PR/os.h include/PR/os_voice.h include/PR/ultraerror.h include/PR/os_flash.h \ - include/PR/mbi.h include/PR/gzsort.h include/PR/sched.h include/PR/os_reg.h - -install: all - $(V)mkdir -p /opt/crashsdk/mips64-elf/lib/ /opt/crashsdk/mips64-elf/include/ /opt/crashsdk/mips64-elf/include/PR - @$(PRINT) "$(GREEN)Copying libultra.a$(NO_COL)\n" - $(V)cp $(BUILD_DIR_BASE)/libultra.a /opt/crashsdk/mips64-elf/lib/libultra.a - @$(PRINT) "$(GREEN)Copying libultra_d.a$(NO_COL)\n" - $(V)cp $(BUILD_DIR_BASE)/libultra_d.a /opt/crashsdk/mips64-elf/lib/libultra_d.a - @$(PRINT) "$(GREEN)Copying libultra_rom.a$(NO_COL)\n" - $(V)cp $(BUILD_DIR_BASE)/libultra_rom.a /opt/crashsdk/mips64-elf/lib/libultra_rom.a - @$(PRINT) "$(GREEN)Copying assert.h$(NO_COL)\n" - $(V)cp include/assert.h /opt/crashsdk/mips64-elf/include/assert.h - @$(PRINT) "$(GREEN)Copying ultra64.h$(NO_COL)\n" - $(V)cp include/ultra64.h /opt/crashsdk/mips64-elf/include/ultra64.h - @$(PRINT) "$(GREEN)Copying ultrahost.h$(NO_COL)\n" - $(V)cp include/ultrahost.h /opt/crashsdk/mips64-elf/include/ultrahost.h - @$(PRINT) "$(GREEN)Copying PR headers$(NO_COL)\n" - $(V)$(foreach var,$(PRHFILES),cp $(var) /opt/crashsdk/mips64-elf/include/PR/;) From d876e888e25956b5dd1b12b01c920ef83eaacb86 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 21:36:18 -0400 Subject: [PATCH 15/17] SDK installation shall be handled seperately --- Makefile | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Makefile b/Makefile index a19c89f7..401de010 100644 --- a/Makefile +++ b/Makefile @@ -195,23 +195,6 @@ $(BUILD_DIR_BASE)/libultra_rom.a: $(V)$(MAKE) TARGET=libultra_rom $(V)cp $(BUILD_DIR_BASE)/libultra_rom/libultra_rom.a $(BUILD_DIR_BASE) -install: all - $(V)mkdir -p /opt/crashsdk/mips64-elf/lib/ /opt/crashsdk/mips64-elf/include/ /opt/crashsdk/mips64-elf/include/PR - @$(PRINT) "$(GREEN)Copying libultra.a$(NO_COL)\n" - $(V)cp $(BUILD_DIR_BASE)/libultra.a /opt/crashsdk/mips64-elf/lib/libultra.a - @$(PRINT) "$(GREEN)Copying libultra_d.a$(NO_COL)\n" - $(V)cp $(BUILD_DIR_BASE)/libultra_d.a /opt/crashsdk/mips64-elf/lib/libultra_d.a - @$(PRINT) "$(GREEN)Copying libultra_rom.a$(NO_COL)\n" - $(V)cp $(BUILD_DIR_BASE)/libultra_rom.a /opt/crashsdk/mips64-elf/lib/libultra_rom.a - @$(PRINT) "$(GREEN)Copying assert.h$(NO_COL)\n" - $(V)cp include/assert.h /opt/crashsdk/mips64-elf/include/assert.h - @$(PRINT) "$(GREEN)Copying ultra64.h$(NO_COL)\n" - $(V)cp include/ultra64.h /opt/crashsdk/mips64-elf/include/ultra64.h - @$(PRINT) "$(GREEN)Copying ultrahost.h$(NO_COL)\n" - $(V)cp include/ultrahost.h /opt/crashsdk/mips64-elf/include/ultrahost.h - @$(PRINT) "$(GREEN)Copying PR headers$(NO_COL)\n" - $(V)cp -r include/PR /opt/crashsdk/mips64-elf/include/PR - .PHONY: clean default all install pkginstall # with no prerequisites, .SECONDARY causes no intermediate target to be removed .SECONDARY: From 8abcb4ca1b2ec0b86046a8c3aa8a078c2ed39685 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 21:47:43 -0400 Subject: [PATCH 16/17] Remove RMON and a few leftover KMC things --- include/PR/rmon.h | 1 - include/PRinternal/rmonint.h | 168 -------------- src/error/kmcprintf.c | 245 -------------------- src/os/initialize.c | 1 - src/rmon/rmonbrk.c | 424 ----------------------------------- src/rmon/rmoncmds.c | 46 ---- src/rmon/rmonmain.c | 127 ----------- src/rmon/rmonmem.c | 275 ----------------------- src/rmon/rmonmisc.c | 67 ------ src/rmon/rmonprint.c | 1 - src/rmon/rmonrcp.s | 60 ----- src/rmon/rmonregs.c | 403 --------------------------------- src/rmon/rmonsio.c | 79 ------- src/rmon/rmontask.c | 337 ---------------------------- 14 files changed, 2234 deletions(-) delete mode 100644 include/PRinternal/rmonint.h delete mode 100644 src/error/kmcprintf.c delete mode 100644 src/rmon/rmonbrk.c delete mode 100644 src/rmon/rmoncmds.c delete mode 100644 src/rmon/rmonmain.c delete mode 100644 src/rmon/rmonmem.c delete mode 100644 src/rmon/rmonmisc.c delete mode 100644 src/rmon/rmonprint.c delete mode 100644 src/rmon/rmonrcp.s delete mode 100644 src/rmon/rmonregs.c delete mode 100644 src/rmon/rmonsio.c delete mode 100644 src/rmon/rmontask.c diff --git a/include/PR/rmon.h b/include/PR/rmon.h index 64f16401..def309f5 100644 --- a/include/PR/rmon.h +++ b/include/PR/rmon.h @@ -29,7 +29,6 @@ extern "C" { #define RMON_DBG_BUF_SIZE 2048 #define RMON_STACKSIZE 0x1000 -extern void rmonMain(void*); extern void rmonPrintf(const char*, ...); #ifdef _LANGUAGE_C_PLUS_PLUS diff --git a/include/PRinternal/rmonint.h b/include/PRinternal/rmonint.h deleted file mode 100644 index 66d8e2d0..00000000 --- a/include/PRinternal/rmonint.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef _RMONINT_H -#define _RMONINT_H - -#include "PRinternal/dbgproto.h" -#include "PR/os_internal.h" - -/* mips */ - -#define MIPS_LWC2_OPCODE 50 -#define MIPS_SWC2_OPCODE 58 - -#define MIPS_LW_OPCODE 35 -#define MIPS_SW_OPCODE 43 - -#define MIPS_BREAK_OPCODE 0xD -#define MIPS_BREAK_MASK 0xFC00003F - -#define MIPS_BREAK(code) ((((code) & 0xFFFFF) << 6) | MIPS_BREAK_OPCODE) - -/* R4300 General Purpose Register Indices */ -#define GREG_IDX_ZERO 0 -#define GREG_IDX_AT 1 -#define GREG_IDX_T9 25 -#define GREG_IDX_K0 26 -#define GREG_IDX_GP 28 -#define GREG_IDX_RA 31 -#define GREG_IDX_LO 32 -#define GREG_IDX_HI 33 -#define GREG_IDX_CAUSE 34 -#define GREG_IDX_PC 35 -#define GREG_IDX_SR 36 - -/* RSP Scalar Register Indices */ -#define SREG_IDX_ZERO 0 -#define SREG_IDX_RA 31 -#define SREG_IDX_DRAM_ADDR (32 + 0) -#define SREG_IDX_MEM_ADDR (32 + 1) -#define SREG_IDX_RD_LEN (32 + 2) -#define SREG_IDX_PC (32 + 3) -#define SREG_IDX_WR_LEN (32 + 4) -#define SREG_IDX_STATUS (32 + 5) -#define SREG_IDX_DMA_FULL (32 + 6) -#define SREG_IDX_DMA_BUSY (32 + 7) - -/* RSP Vector Register Properties */ -#define VREG_NUM 32 -#define VREG_SIZE 0x10 - -/* rmon */ - -#define RMON_MESG_CPU_BREAK 2 -#define RMON_MESG_SP_BREAK 4 -#define RMON_MESG_FAULT 8 - -#define RMON_CPU 0 -#define RMON_RSP 1 - -/* "thread id" for rsp */ -#define RMON_TID_RSP 1000 -/* "thread priority" for rsp */ -#define RMON_PRI_RSP 42 - -/* "thread id" for no thread running */ -#define RMON_TID_NOTHREAD 1003 - -#define RMON_PID_CPU 1002 -#define RMON_PID_RSP 1001 - -/* Largest serviceable read/write memory request */ -#define RMON_MAX_XFER_SIZE 1024 - -/* rmonmain */ - -void __rmonSendHeader(KKHeader* const block, u32 blockSize, u32 type); -void __rmonSendReply(KKHeader* const block, u32 blockSize, u32 replyType); -void __rmonSendData(char* const block, unsigned int blockSize); - -extern int __rmonActive; - -/* rmonmisc */ - -void __rmonInit(void); -void __rmonPanic(void); - -extern OSMesgQueue __rmonMQ; - -/* rmonmem */ - -void __rmonWriteWordTo(u32* addr, u32 val); -u32 __rmonReadWordAt(u32* addr); -void __rmonMemcpy(u8* dest, u8* srce, u32 count); -void __rmonCopyWords(u32* dest, u32* srce, u32 count); - -extern u8 __rmonUtilityBuffer[]; - -/* rmonsio */ - -void __rmonSendFault(OSThread* thread); -void __rmonIOflush(void); -void __rmonIOputw(u32 word); -void __rmonIOhandler(void); - -extern void* __osRdb_DbgRead_Buf; -extern u8 rmonRdbReadBuf[]; - -/* rmonrcp */ - -int __rmonRCPrunning(void); -void __rmonIdleRCP(void); -void __rmonStepRCP(void); -void __rmonRunRCP(void); - -/* rmonbrk */ - -u32 __rmonGetBranchTarget(int method, int thread, char* addr); -int __rmonSetSingleStep(int thread, u32* instptr); -void __rmonGetExceptionStatus(KKStatusEvent* reply); -void __rmonHitBreak(void); -void __rmonHitSpBreak(void); -void __rmonHitCpuFault(void); - -extern u8 __rmonRcpAtBreak; - -/* rmonregs */ - -u32 __rmonGetRegisterContents(int method, int threadNumber, int regNumber); - -/* rmontask */ - -void __rmonMaskIdleThreadInts(void); -OSThread* __rmonGetTCB(int threadNumber); -int __rmonStopUserThreads(int whichThread); -int __rmonGetThreadStatus(int method, int id, KKStatusEvent* reply); - -/* rmoncmds */ - -int __rmonExecute(KKHeader* request); - -/* commands */ - -typedef int (*FUNPTR)(); - -int __rmonLoadProgram(KKHeader* req); -int __rmonListProcesses(KKHeader* req); -int __rmonGetExeName(KKHeader* req); -int __rmonListThreads(KKHeader* req); -int __rmonThreadStatus(KKHeader* req); -int __rmonStopThread(KKHeader* req); -int __rmonRunThread(KKHeader* req); -int __rmonSetFault(KKHeader* req); -int __rmonGetRegionCount(KKHeader* req); -int __rmonGetRegions(KKHeader* req); -int __rmonGetGRegisters(KKHeader* req); -int __rmonSetGRegisters(KKHeader* req); -int __rmonGetFRegisters(KKHeader* req); -int __rmonSetFRegisters(KKHeader* req); -int __rmonReadMem(KKHeader* req); -int __rmonWriteMem(KKHeader* req); -int __rmonSetBreak(KKHeader* req); -int __rmonClearBreak(KKHeader* req); -int __rmonListBreak(KKHeader* req); -int __rmonSetComm(KKHeader* req); -int __rmonGetSRegs(KKHeader* req); -int __rmonSetSRegs(KKHeader* req); -int __rmonGetVRegs(KKHeader* req); -int __rmonSetVRegs(KKHeader* req); - -#endif diff --git a/src/error/kmcprintf.c b/src/error/kmcprintf.c deleted file mode 100644 index efe165f4..00000000 --- a/src/error/kmcprintf.c +++ /dev/null @@ -1,245 +0,0 @@ -// This file was added in 2.0J and removed in 2.0K -#include "stdarg.h" -#include "PR/os.h" -#include "PR/rcp.h" -#include "PR/rdb.h" -#include "ultraerror.h" -#include "../libc/xstdio.h" - -extern u32 __kmc_pt_mode; - -static void* proutSyncPrintf(void* str, const char* buf, size_t n) { - size_t sent = 0; - - while (sent < n) { - sent += __osRdbSend(buf + sent, n - sent, RDB_TYPE_GtoH_PRINT); - } - return 1; -} - -static volatile unsigned int* stat = (unsigned*)0xbff08004; -static volatile unsigned int* wport = (unsigned*)0xbff08000; -static volatile unsigned int* piok = (unsigned*)PHYS_TO_K1(PI_STATUS_REG); - -static void rmonPutchar(char c) { - while (*piok & (PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY)) { - } - - while (!(*stat & 4)) { - } - - *wport = c; -} - -static void* kmc_proutSyncPrintf(void* str, const char* buf, int n) { - int i; - char c; - char* p; - char* q; - char xbuf[128]; - static int column = 0; - - p = &xbuf; - - for (i = 0; i < n; i++) { - c = *buf++; - - switch (c) { - case '\n': - *p++ = '\n'; - column = 0; - break; - case '\t': - do { - *p++ = ' '; - } while (++column % 8); - break; - default: - column++; - *p++ = c; - break; - } - - if (c == '\n' || (p - xbuf) > 100) { - rmonPutchar((p - xbuf) - 1); - - q = xbuf; - while (q != p) { - rmonPutchar(*q++); - } - p = xbuf; - } - } - if (p != xbuf) { - rmonPutchar((p - xbuf) - 1); - - q = xbuf; - while (q != p) { - rmonPutchar(*q++); - } - } - return (void*)1; -} - -char NULSTR[] = ""; - -const char* __os_error_message[] = { - NULSTR, - "osCreateThread: stack pointer not aligned to 8 bytes (0x%x)", - "osCreateThread: priority not in range [0-255] (%d)", - "osStartThread: thread has bad state (running/runnable/other)", - "osSetThreadPri: priority not in range [0-255] (%d)", - "osCreateMesgQueue: message count not > 0 (%d)", - "osSendMesg: flag not OS_MESG_NOBLOCK or OS_MESG_BLOCK (%d)", - "osJamMesg: flag not OS_MESG_NOBLOCK or OS_MESG_BLOCK (%d)", - "osRecvMesg: flag not OS_MESG_NOBLOCK or OS_MESG_BLOCK (%d)", - "osSetEventMesg: unknown event type (%d)", - "osMapTLB: index not in range [0-30] (%d)", - "osMapTLB: asid argument not -1 or in range [0-255] (%d)", - "osUnmapTLB: index not in range [0-30] (%d)", - "osSetTLBASID: asid not in range [0-255] (%d)", - "osAiSetFrequency: freq not in range [%d-%d] (%d)", - "osAiSetNextBuffer: address not aligned to 8 bytes (0x%x)", - "osAiSetNextBuffer: size not aligned to 8 bytes (0x%x)", - "osDpSetNextBuffer: address not aligned to 8 bytes (0x%x)", - "osDpSetNextBuffer: size not aligned to 8 bytes (0x%x)", - "osPiRawReadIo: address not aligned to 4 bytes (0x%x)", - "osPiRawWriteIo: address not aligned to 4 bytes (0x%x)", - "osPiRawStartDma: direction not OS_READ or OS_WRITE (%d)", - "osPiRawStartDma: device address not aligned to 2 bytes (0x%x)", - "osPiRawStartDma: DRAM address not aligned to 8 bytes (0x%x)", - "osPiRawStartDma: size not aligned to 2 bytes (%d)", - "osPiRawStartDma: size not in range [0,16777216] (%d)", - "osPiReadIo: address not aligned to 4 bytes (0x%x)", - "osPiWriteIo: address not aligned to 4 bytes (0x%x)", - "osPiStartDma: PI Manager not yet begun by osCreatePiManager", - "osPiStartDma: priority not OS_MESG_PRI_[NORMAL|HIGH] (%d)", - "osPiStartDma: direction not OS_READ or OS_WRITE (%d)", - "osPiStartDma: device address not aligned to 2 bytes (0x%x)", - "osPiStartDma: DRAM address not aligned to 8 bytes (0x%x)", - "osPiStartDma: size not aligned to 2 bytes (%d)", - "osPiStartDma: size not in range [0,16777216] (%d)", - "osCreatePiManager: priority not in range [0-255] (%d)", - "osViGetCurrentMode: VI Manager not yet begun", - "osViGetCurrentFramebuffer: VI Manager not yet begun", - "osViGetNextFramebuffer: VI Manager not yet begun", - "osViSetXScale: value not in range [0.25,1.0] (%f)", - "osViSetXScale: VI Manager not yet begun by osCreateViManager", - "osViSetYScale: value not in range [0.05,1.0] (%f)", - "osViSetYScale: VI Manager not yet begun by osCreateViManager", - "osViSetSpecialFeatures: not a known feature value (%d)", - "osViSetSpecialFeatures: VI Manager not yet begun", - "osViSetMode: VI Manager not yet begun by osCreateViManager", - "osViSetEvent: VI Manager not yet begun by osCreateViManager", - "osViSwapBuffer: frame buffer not aligned to 64 bytes (0x%x)", - "osViSwapBuffer: VI Manager not yet begun", - "osCreateViManager: priority not in range [0-255] (%d)", - "osCreateRegion: not a known alignment (%d)", - "osCreateRegion: length (%d) too small for buffer size (%d)", - "osMalloc: invalid or corrupt region (0x%x)", - "osFree: invalid or corrupt region (0x%x)", - "osFree: invalid address (0x%x) or\n corrupt region (0x%x)", - "osGetRegionBufCount: invalid or corrupt region (0x%x)", - "osGetRegionBufSize: invalid or corrupt region (0x%x)", - "osSpTaskLoad: dram_stack not aligned to 16 bytes (0x%x)", - "osSpTaskLoad: output_buff not aligned to 16 bytes (0x%x)", - "osSpTaskLoad: output_buff_size not aligned to 16 bytes (0x%x)", - "osSpTaskLoad: yield_data_ptr not aligned to 16 bytes (0x%x)", - "osProfileInit: profile counter is running, call osProfileStop before init", - "osProfileInit: profcnt is %d", - "osProfileInit: histo_base pointer must be 32-bit aligned (%x)", - "osProfileInit: text_start (%x) >= text_end (%x)", - "osProfileInit: histo_size is an illegal size (%d)", - "osProfileStart: microseconds is < PROF_MIN_INTERVAL (%d)", - "osProfileStart: profiling has already been started", - "osProfileStop: profiling has already been stopped", - "osProfileStop: no profile timer to stop", - "osReadHost: address not aligned to 8 bytes (0x%x)", - "osReadHost: size either 0 or not aligned to 4 bytes (0x%x)", - "osWriteHost: address not aligned to 8 bytes (0x%x)", - "osWriteHost: size either 0 or not aligned to 4 bytes (0x%x)", - "osGetTime: VI manager not yet begun by osCreateViManager", - "osSetTime: VI manager not yet begun by osCreateViManager", - "osSetTimer: VI manager not yet begun by osCreateViManager", - "osStopTimer: VI manager not yet begun by osCreateViManager", - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - NULSTR, - "_handleMIDIMsg: no sound mapped", - "_handleMIDIMsg: no free voices", - "_handleMIDIMsg: couldn't map voice", - "_handleMIDIMsg: note off - couldn't find voice", - "_handleMIDIMsg: poly pressure - couldn't find voice", - "_handleEvent: no free voices", - "Synthesizer: no free updates", - "alSndPDeallocate: attempt to deallocate a sound which is playing", - "alSndpDelete: attempt to delete player with playing sounds", - "alSndpPlay: attempt to play a sound which is playing", - "alSndpSetSound: sound id (%d) out of range (0 - %d)", - "alSndpSetPriority: sound id (%d) out of range (0 - %d)", - "alSndpSet Parameter: target (%d) out of range (0 - %d)", - "alBnkfNew: bank file out of date", - "alSeqNew: 0x%x is not a midi file", - "alSeqNew: 0x%x is not a type 0 midi file", - "alSeqNew: 0x%x has more than 1 track", - "alSeqNew: SMPTE delta times not supported", - "alSeqNew: Error parsing file 0x%x (no track header)", - "alSeqNextEvent: Unsupported system exclusive", - "alSeqNextEvent: Unsupported midi meta event 0x%x", - "_handleMIDIMsg: Invalid program change to %d, max instruments %d", - "_handleMIDIMsg: Unknown midi message 0x%x", - "_unmapVoice: Couldn't unmap voice 0x%x", - "alEvtqPostEvent: Out of free events", - "alHeapAlloc: Can't allocate %d bytes", - "alHeapCheck: Heap corrupt", - "alHeapCheck: Heap corrupt - first block is bad", - "alCSeqGetTrackEvent: Running status of zero on track %d", - "alCSeqGetTrackEvent: Note on velocity of zero on track %d", - "alCSPVoiceHandler: Stopping sequence but voice not free chan %d, key %d", - "alSeqNextEvent: Read past end of sequence", - "osAiSetNextBuffer: DMA buffer location may cause audio clicks (0x%x)", - "_loadOutputBuffer: Modulated delay greater than total delay by %d samples", - "osViExtendVStart: VI Manager not yet begun by osCreateViManager", - "osViExtendVStart: value not in range [0-48] %d", - NULSTR, -}; - -static void kmcErrorHandler(s16 code, s16 numArgs, ...); -OSErrorHandler __kmcErrorHandler = kmcErrorHandler; - -static void kmcErrorHandler(s16 code, s16 numArgs, ...) { - va_list ap; - const char* fmt; - - fmt = __os_error_message[code]; - va_start(ap, numArgs); - - if (__kmc_pt_mode) { - _Printf(kmc_proutSyncPrintf, NULL, fmt, ap); - } else { - _Printf(proutSyncPrintf, NULL, fmt, ap); - } - - osSyncPrintf("\n"); - - va_end(ap); -} diff --git a/src/os/initialize.c b/src/os/initialize.c index fedb2fdc..ef23337a 100644 --- a/src/os/initialize.c +++ b/src/os/initialize.c @@ -21,7 +21,6 @@ u32 __OSGlobalIntMask = OS_IM_ALL; #ifdef _FINALROM u32 __osFinalrom; #else -u32 __kmc_pt_mode; void* __printfunc = NULL; #endif diff --git a/src/rmon/rmonbrk.c b/src/rmon/rmonbrk.c deleted file mode 100644 index 59959d6d..00000000 --- a/src/rmon/rmonbrk.c +++ /dev/null @@ -1,424 +0,0 @@ -#ifndef _FINALROM - -#include "PR/os_internal.h" -#include "PRinternal/dbgproto.h" -#include "PR/rcp.h" -#include "PR/sptask.h" -#include "PRinternal/rmonint.h" - -#include "PRinternal/macros.h" - -// TODO: this comes from a header -#ident "$Revision: 1.4 $" - -#define TMP_BP 0 -#define NUM_BREAKPOINTS 16 - -typedef struct { - TVushort type; - TVushort response; - TVid threadID; - void* pc; -} TVExceptionReplyMsg; - -typedef struct { - u32* breakAddress; - u32 oldInstruction; -} BREAKINFO; - -/* first breakpoint is reserved for implementing single-stepping */ -static BREAKINFO breakpoints[NUM_BREAKPOINTS] ALIGNED(0x8); -/* breakpoint for alternate branch target */ -static BREAKINFO altBreak; - -static BREAKINFO RCPbreakpoints[NUM_BREAKPOINTS] ALIGNED(0x8); - -u8 __rmonRcpAtBreak; - -static void rmonFindFaultedThreads(void); - -static void SetTempBreakpoint(u32* addr1, u32* addr2) { - STUBBED_PRINTF(("Set temp BP at %08x", addr1)); - if (addr2 != NULL) { - STUBBED_PRINTF((" and %08x", addr2)); - } - STUBBED_PRINTF(("\n")); - - /* Save the word at the target address to be restored later */ - breakpoints[TMP_BP].oldInstruction = *addr1; - /* Install a break instruction at the target address */ - *addr1 = MIPS_BREAK(16); - osWritebackDCache(addr1, sizeof(*addr1)); - osInvalICache(addr1, sizeof(*addr1)); - breakpoints[TMP_BP].breakAddress = addr1; - - /* Also do so for an alt address if required */ - if (addr2 != NULL) { - altBreak.oldInstruction = *addr2; - *addr2 = MIPS_BREAK(16); - osWritebackDCache(addr2, sizeof(*addr2)); - osInvalICache(addr2, sizeof(*addr2)); - altBreak.breakAddress = addr2; - } -} - -static void ClearTempBreakpoint(void) { - u32 inst; - - if (breakpoints[TMP_BP].breakAddress != NULL) { - inst = *breakpoints[TMP_BP].breakAddress; - - if ((inst & MIPS_BREAK_MASK) == MIPS_BREAK_OPCODE) { - STUBBED_PRINTF(("ClearTempBreak @ %08x\n", breakpoints[TMP_BP].breakAddress)); - - /* After confirming that there is a break instruction with code at the target - address, restore the original contents of the word at the target address */ - *breakpoints[TMP_BP].breakAddress = breakpoints[TMP_BP].oldInstruction; - osWritebackDCache(breakpoints[TMP_BP].breakAddress, sizeof(*breakpoints[TMP_BP].breakAddress)); - osInvalICache(breakpoints[TMP_BP].breakAddress, sizeof(*breakpoints[TMP_BP].breakAddress)); - } - breakpoints[TMP_BP].breakAddress = NULL; - } - - /* Same as above for the alt breakpoint */ - if (altBreak.breakAddress != NULL) { - inst = *altBreak.breakAddress; - - if ((inst & MIPS_BREAK_MASK) == MIPS_BREAK_OPCODE) { - STUBBED_PRINTF(("ClearTempBreak @ %08x\n", altBreak.breakAddress)); - - *altBreak.breakAddress = altBreak.oldInstruction; - osWritebackDCache(altBreak.breakAddress, sizeof(*altBreak.breakAddress)); - osInvalICache(altBreak.breakAddress, sizeof(*altBreak.breakAddress)); - } - altBreak.breakAddress = NULL; - } -} - -int __rmonSetBreak(KKHeader* req) { - register KKSetBkptRequest* request = (KKSetBkptRequest*)req; - register BREAKINFO* breakBase; - register BREAKINFO* whichBreak; - register BREAKINFO* lastBreak; - KKBkptEvent reply; - - STUBBED_PRINTF(("SetBreak at %08x, method %d\n", request->addr, req->method)); - - /* Select breakpoint list */ - if (req->method == RMON_RSP) { - breakBase = RCPbreakpoints; - whichBreak = &RCPbreakpoints[1]; - lastBreak = &RCPbreakpoints[NUM_BREAKPOINTS]; - } else { - breakBase = breakpoints; - whichBreak = &breakpoints[1]; - lastBreak = &breakpoints[NUM_BREAKPOINTS]; - } - - /* Find breakpoint slot */ - for (; whichBreak < lastBreak; whichBreak++) { - if (whichBreak->breakAddress != NULL) { - if (whichBreak->breakAddress == (u32*)request->addr) { - /* Breakpoint already set here */ - break; - } - continue; - } else { - /* Empty slot */ - break; - } - } - - /* No breakpoints available */ - if (whichBreak == lastBreak) { - return TV_ERROR_NO_MORE_IDS; - } - - /* Set breakpoint if not already set */ - if (whichBreak->breakAddress == NULL) { - if (req->method == RMON_RSP) { - whichBreak->oldInstruction = __rmonReadWordAt((u32*)request->addr); - __rmonWriteWordTo((u32*)request->addr, MIPS_BREAK((whichBreak - breakBase) + NUM_BREAKPOINTS)); - } else { - whichBreak->oldInstruction = *(u32*)request->addr; - *(u32*)request->addr = MIPS_BREAK((whichBreak - breakBase) + NUM_BREAKPOINTS); - osWritebackDCache((void*)request->addr, sizeof(whichBreak->oldInstruction)); - osInvalICache((void*)request->addr, sizeof(whichBreak->oldInstruction)); - } - whichBreak->breakAddress = (u32*)request->addr; - STUBBED_PRINTF(("* (%08x) = %08x (was %08x)\n", whichBreak->breakAddress, *whichBreak->breakAddress, - whichBreak->oldInstruction)); - } - - /* Send reply */ - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.object = request->object; - reply.bp = whichBreak - breakBase; - reply.instruction = whichBreak->oldInstruction; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonListBreak(KKHeader* request UNUSED) { - STUBBED_PRINTF(("ListBreak\n")); - - return TV_ERROR_ILLEGAL_CALL; -} - -int __rmonClearBreak(KKHeader* req) { - register KKClrBkptRequest* request = (KKClrBkptRequest*)req; - register BREAKINFO* whichBreak; - KKBkptEvent reply; - u32 inst; - - STUBBED_PRINTF(("ClearBreak\n")); - - /* Check valid breakpoint index */ - if (request->bp >= NUM_BREAKPOINTS) { - return TV_ERROR_INVALID_ID; - } - - /* Clear the breakpoint, restore whatever was there before */ - if (req->method == RMON_RSP) { - whichBreak = &RCPbreakpoints[request->bp]; - - if (whichBreak->breakAddress == NULL) { - return TV_ERROR_INVALID_ID; - } - - inst = __rmonReadWordAt(whichBreak->breakAddress); - if ((inst & MIPS_BREAK_MASK) == MIPS_BREAK_OPCODE) { - __rmonWriteWordTo(whichBreak->breakAddress, whichBreak->oldInstruction); - } - } else { - whichBreak = &breakpoints[request->bp]; - - if (whichBreak->breakAddress == NULL) { - return TV_ERROR_INVALID_ID; - } - - inst = *whichBreak->breakAddress; - if ((inst & MIPS_BREAK_MASK) == MIPS_BREAK_OPCODE) { - *whichBreak->breakAddress = whichBreak->oldInstruction; - osWritebackDCache(whichBreak->breakAddress, sizeof(*whichBreak->breakAddress)); - osInvalICache(whichBreak->breakAddress, sizeof(*whichBreak->breakAddress)); - } - } - whichBreak->breakAddress = NULL; - - /* Send reply */ - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.object = request->object; - reply.bp = request->bp; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -u32 __rmonGetBranchTarget(int method, int thread, char* addr) { - int inst; - - if (method == RMON_RSP) { - inst = __rmonReadWordAt((u32*)addr); - } else { - inst = *(u32*)addr; - } - - switch ((inst >> 26) & 0x3F) { - case 0: /* SPECIAL */ - if (((inst >> 5) & 0x7FFF) == 0 && (inst & 0x3F) == 8) { - /* JR */ - return __rmonGetRegisterContents(method, thread, (inst >> 21) & 0x1F); - } - if (((inst >> 16) & 0x1F) == 0 && (inst & 0x7FF) == 9) { - /* JALR */ - return __rmonGetRegisterContents(method, thread, (inst >> 21) & 0x1F); - } - break; - case 1: /* REGIMM */ - switch ((inst >> 16) & 0x1F) { - case 0: /* BLTZ */ - case 1: /* BGEZ */ - case 2: /* BLTZL */ - case 3: /* BGEZL */ - case 16: /* BLTZAL */ - case 17: /* BGEZAL */ - case 18: /* BLTZALL */ - case 19: /* BGEZALL */ - return (((inst << 0x10) >> 0xE) + addr + 4); - } - break; - case 2: /* J */ - case 3: /* JAL */ - return (((u32)inst << 6) >> 4) + (((s32)((u32)addr + 4) >> 0x1C) << 0x1C); - case 4: /* BEQ */ - case 5: /* BNE */ - case 20: /* BEQL */ - case 21: /* BNEL */ - return (((inst << 0x10) >> 0xE) + addr + 4); - case 6: /* BLEZ */ - case 7: /* BGTZ */ - case 22: /* BLEZL */ - case 23: /* BGTZL */ - if (((inst >> 16) & 0x1F) == 0) { - return (((inst << 0x10) >> 0xE) + addr + 4); - } - break; - case 16: /* COP0 */ - case 17: /* COP1 */ - case 18: /* COP2 */ - case 19: /* COP3 */ - if (((inst >> 21) & 0x1F) == 8) { - switch ((inst >> 16) & 0x1F) { - case 0: /* BCzF */ - case 1: /* BCzT */ - case 2: /* BCzFL */ - case 3: /* BCzTL */ - return (((inst << 0x10) >> 0xE) + addr + 4); - } - } - break; - } - return -1; -} - -static int IsJump(u32 inst) { - switch ((inst >> 26) & 0x3F) { - case 0: /* SPECIAL */ - if (((inst >> 5) & 0x7FFF) == 0 && (inst & 0x3F) == 8) { - /* JR */ - return TRUE; - } - if (((inst >> 16) & 0x1F) == 0 && (inst & 0x7FF) == 9) { - /* JALR */ - return TRUE; - } - break; - case 2: /* J */ - case 3: /* JAL */ - return TRUE; - } - return FALSE; -} - -int __rmonSetSingleStep(int thread, u32* instptr) { - u32 branchTarget = __rmonGetBranchTarget(RMON_CPU, thread, (void*)instptr); - - STUBBED_PRINTF(("SingleStep\n")); - - if ((branchTarget & 3) != 0) { - /* no branch target, set breakpoint at next pc */ - SetTempBreakpoint(instptr + 1, NULL); - } else if (branchTarget == (u32)instptr) { - /* branch target is this instruction, can't single step here */ - return FALSE; - } else if (IsJump(*instptr) || branchTarget == (u32)(instptr + 2)) { - /* unconditional branch, set at branch target */ - SetTempBreakpoint((u32*)branchTarget, NULL); - } else { - /* set two breakpoints for handling conditional branches */ - SetTempBreakpoint((u32*)branchTarget, instptr + 2); - } - return TRUE; -} - -void __rmonGetExceptionStatus(KKStatusEvent* reply) { - reply->status.flags = OS_STATE_STOPPED; - reply->status.why = 2; - reply->status.what = 0; - reply->status.rv = 0; - reply->status.info.major = 2; - reply->status.info.minor = 4; - reply->header.code = KK_CODE_THREAD_STATUS; - reply->header.error = TV_ERROR_NO_ERROR; - reply->header.length = sizeof(*reply); -} - -#define FAULT_BREAKNUM (NUM_BREAKPOINTS - 1) - -static void rmonSendBreakMessage(s32 whichThread, int breakNumber) { - KKStatusEvent reply; - - STUBBED_PRINTF(("Break %d in thread %d\n", breakNumber, whichThread)); - - /* Build thread exception status */ - __rmonGetThreadStatus(RMON_CPU, (whichThread != 0) ? whichThread : RMON_TID_NOTHREAD, &reply); - __rmonGetExceptionStatus(&reply); - - if (breakNumber == FAULT_BREAKNUM) { - /* Hit fault */ - reply.status.info.major = 1; - reply.status.info.minor = 2; - } - if (breakNumber < NUM_BREAKPOINTS) { - breakNumber = 0; - } else { - breakNumber -= NUM_BREAKPOINTS; - } - if (breakNumber != 0) { - /* Break not set by debugger, or set during single-step */ - reply.status.instr = MIPS_BREAK_OPCODE; - } - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_EXCEPTION); -} - -void __rmonHitBreak(void) { - STUBBED_PRINTF(("HitBreak\n")); - - /* Stop all user threads and report faulted threads */ - ClearTempBreakpoint(); - __rmonStopUserThreads(0); - rmonFindFaultedThreads(); -} - -void __rmonHitSpBreak(void) { - KKStatusEvent exceptionReply; - - STUBBED_PRINTF(("Hit SP Break\n")); - - /* Rewind RSP PC by one instruction to return to the location of the break instruction */ - __rmonWriteWordTo((u32*)SP_PC_REG, __rmonReadWordAt((u32*)SP_PC_REG) - 4); - - /* Report RSP break event */ - __rmonGetThreadStatus(RMON_RSP, RMON_TID_RSP, &exceptionReply); - __rmonGetExceptionStatus(&exceptionReply); - __rmonSendReply(&exceptionReply.header, sizeof(exceptionReply), KK_TYPE_EXCEPTION); - __rmonRcpAtBreak = TRUE; -} - -void __rmonHitCpuFault(void) { - STUBBED_PRINTF(("HitCpuFault\n")); - - /* Stop all user threads and report faulted threads */ - __rmonMaskIdleThreadInts(); - __rmonStopUserThreads(0); - rmonFindFaultedThreads(); -} - -static void rmonFindFaultedThreads(void) { - register OSThread* tptr = __osGetActiveQueue(); - - while (tptr->priority != -1) { - if (tptr->priority > OS_PRIORITY_IDLE && tptr->priority <= OS_PRIORITY_APPMAX) { - if (tptr->flags & OS_FLAG_CPU_BREAK) { - int inst = *(u32*)tptr->context.pc; - - STUBBED_PRINTF(("Brk in thread %d @ %08x, inst %08x\r\n", tptr->id, tptr->context.pc, inst)); - - if ((inst & MIPS_BREAK_MASK) == MIPS_BREAK_OPCODE) { - rmonSendBreakMessage(tptr->id, inst >> 6); - } else { - rmonSendBreakMessage(tptr->id, 0); - } - } - if (tptr->flags & OS_FLAG_FAULT) { - __rmonSendFault(tptr); - rmonSendBreakMessage(tptr->id, FAULT_BREAKNUM); - } - } - tptr = tptr->tlnext; - } -} - -#endif diff --git a/src/rmon/rmoncmds.c b/src/rmon/rmoncmds.c deleted file mode 100644 index 325e4e51..00000000 --- a/src/rmon/rmoncmds.c +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _FINALROM - -#include "PRinternal/dbgproto.h" -#include "PRinternal/rmonint.h" - -#include "PRinternal/macros.h" - -// TODO: this comes from a header -#ident "$Revision: 1.4 $" - -static int NotImplemented(KKHeader* dummy UNUSED) { - return TV_ERROR_ILLEGAL_CALL; -} - -static FUNPTR dispatchTable[] = { - __rmonLoadProgram, __rmonListProcesses, __rmonGetExeName, __rmonListThreads, __rmonThreadStatus, - NotImplemented, __rmonStopThread, __rmonRunThread, NotImplemented, NotImplemented, - __rmonSetFault, NotImplemented, __rmonGetRegionCount, __rmonGetRegions, __rmonGetGRegisters, - __rmonSetGRegisters, __rmonGetFRegisters, __rmonSetFRegisters, __rmonReadMem, __rmonWriteMem, - __rmonSetBreak, __rmonClearBreak, __rmonListBreak, NotImplemented, NotImplemented, - NotImplemented, NotImplemented, NotImplemented, NotImplemented, NotImplemented, - __rmonSetComm, NotImplemented, NotImplemented, NotImplemented, NotImplemented, - NotImplemented, NotImplemented, NotImplemented, NotImplemented, NotImplemented, - NotImplemented, NotImplemented, NotImplemented, NotImplemented, NotImplemented, - NotImplemented, NotImplemented, NotImplemented, NotImplemented, __rmonGetSRegs, - __rmonSetSRegs, __rmonGetVRegs, __rmonSetVRegs, NotImplemented, -}; -/* -int __rmonExecute(KKHeader* request) { - int retval; - KKHeader reply; - - if (request->code >= ARRLEN(dispatchTable) - 1) { - return TV_ERROR_ILLEGAL_CALL; - } - - retval = dispatchTable[(int)request->code](request); - if (retval < TV_ERROR_NO_ERROR) { - reply.code = request->code; - reply.error = retval; - __rmonSendReply(&reply, sizeof(reply), KK_TYPE_REPLY); - } - return retval; -} -*/ -#endif diff --git a/src/rmon/rmonmain.c b/src/rmon/rmonmain.c deleted file mode 100644 index b286260b..00000000 --- a/src/rmon/rmonmain.c +++ /dev/null @@ -1,127 +0,0 @@ -#include "PR/os_version.h" - -#ifndef _FINALROM - -#include "PRinternal/dbgproto.h" -#include "PR/os_internal.h" -#include "PRinternal/rmonint.h" -#include "PR/rcp.h" -#include "PR/sptask.h" -#include "PR/rdb.h" - -#include "PRinternal/macros.h" - -// TODO: this comes from a header -#ident "$Revision: 1.4 $" - -int __rmonActive = FALSE; - -static vu32 somethingToDo; -static u32 inbuffer[280] ALIGNED(0x10); -static u8 cmdinptr; -static u8 cmdoutptr; -static int state; -static char* inPointer; - -void __rmonSendHeader(KKHeader* const block, u32 blockSize, u32 type) { - int sent; - char* cPtr = (char*)block; - - block->rev = KK_REV; - block->type = type; - - sent = 0; - while (sent < blockSize) { - sent += __osRdbSend(cPtr + sent, blockSize - sent, RDB_TYPE_GtoH_DEBUG); - } -} - -void __rmonSendReply(KKHeader* const block, u32 blockSize, u32 replyType) { - char* cPtr; - int sent = 0; - - block->length = blockSize; - cPtr = (char*)&blockSize; - - /* send size */ - while (sent < (signed)sizeof(blockSize)) { - sent += __osRdbSend(cPtr + sent, sizeof(blockSize) - sent, RDB_TYPE_GtoH_DEBUG); - } - - /* send data */ - __rmonSendHeader(block, blockSize, replyType); - __rmonIOflush(); -} - -void __rmonSendData(char* const block, unsigned int blockSize) { - int* blockPointer = (int*)block; - unsigned int wordCount = (u32)(blockSize + 3) / 4; - u32 data; - union { - char bufBytes[4]; - u32 bufWord; - } buffer; - - if (((u32)block & 3) == 0) { - while (wordCount--) { - if ((u32)blockPointer >= SP_DMEM_START && (u32)blockPointer < 0x05000000) { - __osSpRawReadIo((u32)blockPointer++, &data); - __rmonIOputw(data); - } else { - __rmonIOputw(*(blockPointer++)); - } - } - } else - while (wordCount--) { - __rmonMemcpy((u8*)buffer.bufBytes, (u8*)blockPointer, sizeof(buffer)); - __rmonIOputw(buffer.bufWord); - blockPointer++; - } - __rmonIOflush(); -} - -void rmonMain(void) { - register int newChars UNUSED; - - STUBBED_PRINTF(("rmon: Thread %d created\n")); - STUBBED_PRINTF(("rmon: Thread %d destroyed\n")); - - somethingToDo = 0; - cmdoutptr = 0; - cmdinptr = 0; - - __rmonInit(); - __rmonActive = TRUE; - - state = 0, newChars = 0, inPointer = (void*)&inbuffer; - for (;;) { - OSMesg work; - - osRecvMesg(&__rmonMQ, &work, OS_MESG_BLOCK); - - somethingToDo |= (u32)work; - - if (somethingToDo & RMON_MESG_CPU_BREAK) { - somethingToDo &= ~RMON_MESG_CPU_BREAK; - __rmonHitBreak(); - } - if (somethingToDo & RMON_MESG_SP_BREAK) { - somethingToDo &= ~RMON_MESG_SP_BREAK; - __rmonHitSpBreak(); - } - if (somethingToDo & RMON_MESG_FAULT) { - somethingToDo &= ~RMON_MESG_FAULT; - __rmonHitCpuFault(); - } - if (somethingToDo & 0x10) { - somethingToDo; - somethingToDo &= (u8)~0x10; - } - if (somethingToDo & 0x20) { - somethingToDo; - somethingToDo &= (u8)~0x20; - } - } -} - -#endif diff --git a/src/rmon/rmonmem.c b/src/rmon/rmonmem.c deleted file mode 100644 index 6992a0c2..00000000 --- a/src/rmon/rmonmem.c +++ /dev/null @@ -1,275 +0,0 @@ -#include "PR/os_version.h" - -#ifndef _FINALROM - -#include "PRinternal/dbgproto.h" -#include "PR/os_internal.h" -#include "PR/rcp.h" -#include "PR/sptask.h" -#include "PRinternal/rmonint.h" -#include "PR/rdb.h" - -#include "PRinternal/macros.h" - -// TODO: this comes from a header -#ident "$Revision: 1.4 $" - -u8 __rmonUtilityBuffer[256] ALIGNED(0x8); - -void __rmonWriteWordTo(u32* addr, u32 val) { - while (__osSpRawWriteIo((u32)addr, val) != 0) { - ; - } -} - -u32 __rmonReadWordAt(u32* addr) { - u32 data; - - if ((u32)addr >= SP_DMEM_START && (u32)addr < 0x05000000) { - __osSpRawReadIo((u32)addr, &data); - return data; - } - return 0; -} - -void __rmonMemcpy(u8* dest, u8* srce, u32 count) { - while (count--) { - *dest++ = *srce++; - } -} - -void __rmonCopyWords(u32* dest, u32* srce, u32 count) { - while (count--) { - *dest++ = *srce++; - } -} - -static void strcpy(char* dest, char* srce) { - while ((*dest++ = *srce++)) { - ; - } -} - -int __rmonReadMem(KKHeader* req) { - char* cPtr; - int sent; - int dataSize; - KKReadRequest* request = (KKReadRequest*)req; - KKBufferEvent* reply = (KKBufferEvent*)__rmonUtilityBuffer; - u8* blockStart; - - STUBBED_PRINTF(("ReadMem @ %08x for %d\n", request->addr, request->nbytes)); - - reply->header.code = request->header.code; - reply->object = request->object; - reply->header.error = TV_ERROR_NO_ERROR; - - if (request->addr == (u32)-1) { - return TV_ERROR_INVALID_ADDRESS; - } - if (request->nbytes > RMON_MAX_XFER_SIZE) { - return TV_ERROR_INVALID_CAPABILITY; - } - - if (req->method == RMON_RSP) { - if (!((request->addr < SP_IMEM_START || (request->addr + request->nbytes) > SP_IMEM_END) ? FALSE : TRUE) - && !((request->addr < SP_DMEM_START || (request->addr + request->nbytes) > SP_DMEM_END) ? FALSE : TRUE)) { - return TV_ERROR_INVALID_ADDRESS; - } - } else if (osVirtualToPhysical((void*)request->addr) == (u32)-1) { - return TV_ERROR_INVALID_ADDRESS; - } - - blockStart = (u8*)request->addr; - reply->header.length = request->nbytes + sizeof(reply->header) + sizeof(reply->object); - dataSize = request->nbytes + sizeof(reply->header) + sizeof(reply->object); - - cPtr = (char*)&dataSize; - sent = 0; - while (sent < (signed)sizeof(dataSize)) { - sent += __osRdbSend(cPtr + sent, sizeof(dataSize) - sent, RDB_TYPE_GtoH_DEBUG); - } - - __rmonSendHeader(&reply->header, sizeof(reply->header) + sizeof(reply->object), KK_TYPE_REPLY); - __rmonSendData(blockStart, request->nbytes); - return TV_ERROR_NO_ERROR; -} - -int __rmonWriteMem(KKHeader* req) { - register KKWriteRequest* request = (KKWriteRequest*)req; - KKObjectEvent reply; - - STUBBED_PRINTF(("WriteMem\n")); - - /* Bad virtual address, abort */ - if (req->method == RMON_CPU && osVirtualToPhysical((u32*)request->writeHeader.addr) == (u32)-1) { - return TV_ERROR_INVALID_ADDRESS; - } - - /* Transfer size too large, abort */ - if (request->writeHeader.nbytes > RMON_MAX_XFER_SIZE) { - return TV_ERROR_INVALID_CAPABILITY; - } - - if (((request->writeHeader.addr < SP_DMEM_START - || (request->writeHeader.addr + request->writeHeader.nbytes) > 0x04FFFFFF) - ? FALSE - : TRUE)) { - int align; - u32 word; - - if ((align = request->writeHeader.addr & 3) != 0) { - STUBBED_PRINTF(("Long unaligned write...\n")); - - if (request->writeHeader.nbytes != 1) { - return TV_ERROR_INVALID_ADDRESS; - } - - /* Unaligned write; read the word, substitute in the written byte, write it back */ - word = __rmonReadWordAt((u32*)(request->writeHeader.addr & ~3)); - if (align == 1) { - word = (word & ~0xFF0000) | (request->buffer[0] << 0x10); - } else if (align == 2) { - word = (word & ~0xFF00) | (request->buffer[0] << 8); - } else { - word = (word & ~0xFF) | (request->buffer[0] << 0); - } - __rmonWriteWordTo((u32*)(request->writeHeader.addr & ~3), word); - } else { - int wordCount = request->writeHeader.nbytes / sizeof(u32); - u32* wordPointer = (u32*)request->buffer; - - if (request->writeHeader.nbytes % sizeof(u32) != 0) { - STUBBED_PRINTF(("RCP write not an integral number of words\n")); - return TV_ERROR_INVALID_ADDRESS; - } - - while (wordCount--) { - __rmonWriteWordTo((u32*)request->writeHeader.addr, *(wordPointer++)); - request->writeHeader.addr += sizeof(*wordPointer); - } - } - } else { - __rmonMemcpy((u8*)request->writeHeader.addr, (u8*)request->buffer, request->writeHeader.nbytes); - } - - reply.header.code = request->writeHeader.header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.object = request->writeHeader.object; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - - return TV_ERROR_NO_ERROR; -} - -int __rmonListProcesses(KKHeader* req) { - KKObjectRequest* request = (KKObjectRequest*)req; - KKObjsEvent reply; - - STUBBED_PRINTF(("ListProcesses\n")); - - reply.object = 0; - reply.objs.number = 1; - reply.objs.objects[0] = (req->method == RMON_RSP) ? RMON_PID_RSP : RMON_PID_CPU; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonLoadProgram(KKHeader* request UNUSED) { - STUBBED_PRINTF(("LoadProgram\n")); - - return TV_ERROR_ILLEGAL_CALL; -} - -int __rmonGetExeName(KKHeader* req) { - KKObjectRequest* request = (KKObjectRequest*)req; - KKBufferEvent* reply = (KKBufferEvent*)__rmonUtilityBuffer; - - STUBBED_PRINTF(("GetExeName\n")); - - reply->header.code = request->header.code; - reply->header.error = TV_ERROR_NO_ERROR; - reply->object = request->object; - - if (req->method == RMON_RSP) { - strcpy(reply->buffer, "imem"); - } else { - strcpy(reply->buffer, "rmon"); - } - __rmonSendReply(&reply->header, sizeof(reply->header) + sizeof(reply->object) + 8, KK_TYPE_REPLY); - - return TV_ERROR_NO_ERROR; -} - -int __rmonGetRegionCount(KKHeader* req) { - KKObjectRequest* request = (KKObjectRequest*)req; - KKNumberEvent reply; - - STUBBED_PRINTF(("GetRegionCount\n")); - - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.object = request->object; - - reply.number = (req->method == RMON_RSP) ? 2 : 5; - - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - - return TV_ERROR_NO_ERROR; -} - -int __rmonGetRegions(KKHeader* req) { - KKObjectRequest* request = (KKObjectRequest*)req; - KKRegionEvent* reply = (KKRegionEvent*)__rmonUtilityBuffer; - int numRegions; - - STUBBED_PRINTF(("GetRegions\n")); - - numRegions = (req->method == RMON_RSP) ? 2 : 6; - - reply->header.length = numRegions * sizeof(reply->regions[0]) + sizeof(*reply); - reply->header.code = request->header.code; - reply->header.error = TV_ERROR_NO_ERROR; - reply->object = request->object; - reply->number = numRegions; - - reply->regions[1].vaddr = SP_IMEM_START; - reply->regions[1].size = SP_IMEM_END + 1 - SP_IMEM_START; - reply->regions[1].flags = 1 | 2 | 4; - reply->regions[1].paddr = SP_IMEM_START; - - reply->regions[0].vaddr = SP_DMEM_START; - reply->regions[0].size = SP_DMEM_END + 1 - SP_DMEM_START; - reply->regions[0].flags = 1 | 2; - reply->regions[0].paddr = SP_DMEM_START; - - if (numRegions > 2) { - reply->regions[2].vaddr = 0x88200000; - reply->regions[2].size = 0x6130; - reply->regions[2].flags = 1 | 4; - reply->regions[2].paddr = 0; - - reply->regions[3].vaddr = 4; - reply->regions[3].size = 0x200000; - reply->regions[3].flags = 1 | 2; - reply->regions[3].paddr = 0; - - reply->regions[4].vaddr = 0x4002000; - reply->regions[4].size = 0x800000; - reply->regions[4].flags = 1 | 2; - reply->regions[4].paddr = 0; - - reply->regions[5].vaddr = 0x88206130; - reply->regions[5].size = 0x9000; - reply->regions[5].flags = 1 | 2; - reply->regions[5].paddr = 0; - } - - __rmonSendReply(&reply->header, reply->header.length, KK_TYPE_REPLY); - - return TV_ERROR_NO_ERROR; -} - -#endif diff --git a/src/rmon/rmonmisc.c b/src/rmon/rmonmisc.c deleted file mode 100644 index 17c9a03b..00000000 --- a/src/rmon/rmonmisc.c +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _FINALROM - -#include "PRinternal/dbgproto.h" -#include "PR/os_internal.h" -#include "PR/sptask.h" -#include "PRinternal/rmonint.h" - -#include "PRinternal/macros.h" - -// TODO: this comes from a header -#ident "$Revision: 1.4 $" - -int __rmonSetFault(KKHeader* req) { - KKFaultRequest* request = (KKFaultRequest*)req; - KKObjectEvent reply; - - STUBBED_PRINTF(("SetFault\n")); - - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.object = request->tid; - - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -OSMesgQueue __rmonMQ ALIGNED(0x8); -static OSThread rmonIOThread ALIGNED(0x8); -static OSMesg rmonMsgs[8] ALIGNED(0x8); -static STACK(rmonIOStack, 0x4000) ALIGNED(0x10); -static OSMesg rmonPiMsgs[8] ALIGNED(0x8); -static OSMesgQueue rmonPiMQ ALIGNED(0x8); - -void __rmonInit(void) { - osCreateMesgQueue(&__rmonMQ, rmonMsgs, ARRLEN(rmonMsgs)); - osSetEventMesg(OS_EVENT_CPU_BREAK, &__rmonMQ, (OSMesg)RMON_MESG_CPU_BREAK); - osSetEventMesg(OS_EVENT_SP_BREAK, &__rmonMQ, (OSMesg)RMON_MESG_SP_BREAK); - osSetEventMesg(OS_EVENT_FAULT, &__rmonMQ, (OSMesg)RMON_MESG_FAULT); - osSetEventMesg(OS_EVENT_THREADSTATUS, &__rmonMQ, NULL); - osCreateThread(&rmonIOThread, 0, (void (*)(void*))__rmonIOhandler, NULL, STACK_START(rmonIOStack), OS_PRIORITY_MAX); - osCreatePiManager(OS_PRIORITY_PIMGR, &rmonPiMQ, rmonPiMsgs, ARRLEN(rmonPiMsgs)); - osStartThread(&rmonIOThread); -} - -void __rmonPanic(void) { - STUBBED_PRINTF(("PANIC!!\n")); - - for (;;) { - ; - } -} - -int __rmonSetComm(KKHeader* req) { - KKObjectEvent reply; - - STUBBED_PRINTF(("SetComm\n")); - - reply.header.code = req->code; - reply.object = 0; - reply.header.error = TV_ERROR_NO_ERROR; - - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - - return TV_ERROR_NO_ERROR; -} - -#endif diff --git a/src/rmon/rmonprint.c b/src/rmon/rmonprint.c deleted file mode 100644 index 8ac91a5f..00000000 --- a/src/rmon/rmonprint.c +++ /dev/null @@ -1 +0,0 @@ -/* Empty file */ diff --git a/src/rmon/rmonrcp.s b/src/rmon/rmonrcp.s deleted file mode 100644 index bddc1725..00000000 --- a/src/rmon/rmonrcp.s +++ /dev/null @@ -1,60 +0,0 @@ -#include "PR/os_version.h" -#if !defined(_FINALROM) || BUILD_VERSION < VERSION_J - -#include "sys/asm.h" -#include "sys/regdef.h" -#include "PR/rcp.h" -#include "PR/R4300.h" - -.text - -/* check if the rsp is currently running by polling HALT or BROKE bits in SP_STATUS */ -LEAF(__rmonRCPrunning) - move v0, zero - lw t0, PHYS_TO_K1(SP_STATUS_REG) - and t0, (SP_STATUS_HALT | SP_STATUS_BROKE) - bnez t0, isHalted - ori v0, 1 -isHalted: - jr ra -END(__rmonRCPrunning) - -/* stop the rsp, first wait for any ongoing dma to complete before setting HALT in SP_STATUS */ -LEAF(__rmonIdleRCP) - li a0, PHYS_TO_K1(SP_DMA_BUSY_REG) -wait4dma: - lw v0, (a0) - bnez v0, wait4dma - li a1, SP_CLR_INTR_BREAK | SP_SET_HALT - li a0, PHYS_TO_K1(SP_STATUS_REG) - sw a1, (a0) - -/* wait for the rsp to stop */ -awaitIdle: - li a0, PHYS_TO_K1(SP_STATUS_REG) - lw v0, (a0) - and v0, (SP_STATUS_HALT | SP_STATUS_BROKE) - beqz v0, awaitIdle - jr ra -END(__rmonIdleRCP) - -/* run the rsp in single-step mode to step one instruction */ -LEAF(__rmonStepRCP) - li a0, PHYS_TO_K1(SP_STATUS_REG) - li a1, (SP_CLR_INTR_BREAK | SP_SET_SSTEP | SP_CLR_BROKE | SP_CLR_HALT) - sw a1, (a0) - b awaitIdle -END(__rmonStepRCP) - -/* run the rsp normally */ -LEAF(__rmonRunRCP) - li a0, PHYS_TO_K1(MI_INTR_MASK_REG) - li a1, MI_INTR_MASK_SET_SP - sw a1, (a0) - li a0, PHYS_TO_K1(SP_STATUS_REG) - li a1, (SP_SET_INTR_BREAK | SP_CLR_SSTEP | SP_CLR_BROKE | SP_CLR_HALT) - sw a1, (a0) - jr ra -END(__rmonRunRCP) - -#endif diff --git a/src/rmon/rmonregs.c b/src/rmon/rmonregs.c deleted file mode 100644 index fcb39b32..00000000 --- a/src/rmon/rmonregs.c +++ /dev/null @@ -1,403 +0,0 @@ -#ifndef _FINALROM - -#include "PRinternal/dbgproto.h" -#include "PR/os_internal.h" -#include "PR/rcp.h" -#include "PR/sptask.h" -#include "PRinternal/rmonint.h" -#include "PR/rdb.h" -#include "PR/os_version.h" - -#include "PRinternal/macros.h" - -// TODO: these come from headers -#ident "$Revision: 1.4 $" -// This revision was bumped down at K for some reason -#ident "$Revision: 3.70 $" -#ident "$Revision: 1.5 $" -#ident "$Revision: 1.2 $" -#ident "$Revision: 1.4 $" - -static u32 RCPpc; -static u32 oldIMEMvalue; -static u32 DMEMbuffer[4] ALIGNED(0x8); - -typedef union { - u32 everything; - struct { - int opcode : 6; - int base : 5; - int rt : 5; - int offset : 16; - } scalarop; - struct { - int opcode : 6; - int base : 5; - int rt : 5; - int size : 5; - int element : 4; - int offset : 7; - } vectorop; -} INSTRUCTION; - -static void LoadStoreSU(int opcode, int regno) { - INSTRUCTION inst; - - /* Prepare a scalar load or store instruction at DMEM address 0 */ - inst.everything = 0; - inst.scalarop.opcode = opcode; - inst.scalarop.rt = regno; - __rmonWriteWordTo((u32*)SP_IMEM_START, inst.everything); - __rmonWriteWordTo((u32*)SP_PC_REG, 0); -} - -static void LoadStoreVU(int opcode, int regno) { - INSTRUCTION inst; - - /* Prepare a vector 128-bit load or store instruction at DMEM address 0 */ - inst.everything = 0; - inst.vectorop.opcode = opcode; - inst.vectorop.rt = regno; - inst.vectorop.size = 4; /* LQV / SQV */ - __rmonWriteWordTo((u32*)SP_IMEM_START, inst.everything); - __rmonWriteWordTo((u32*)SP_PC_REG, 0); -} - -static void SetUpForRCPop(int isVector) { - /* Save RSP data that would be overwritten when reading or writing registers */ - RCPpc = __rmonReadWordAt((u32*)SP_PC_REG); - oldIMEMvalue = __rmonReadWordAt((u32*)SP_IMEM_START); - DMEMbuffer[0] = __rmonReadWordAt((u32*)SP_DMEM_START); - if (isVector) { - DMEMbuffer[1] = __rmonReadWordAt((u32*)(SP_DMEM_START + 0x4)); - DMEMbuffer[2] = __rmonReadWordAt((u32*)(SP_DMEM_START + 0x8)); - DMEMbuffer[3] = __rmonReadWordAt((u32*)(SP_DMEM_START + 0xC)); - } -} - -static void CleanupFromRCPop(int isVector) { - /* Restore RSP data that was saved to read or write registers */ - __rmonWriteWordTo((u32*)SP_DMEM_START, DMEMbuffer[0]); - if (isVector) { - __rmonWriteWordTo((u32*)(SP_DMEM_START + 0x4), DMEMbuffer[1]); - __rmonWriteWordTo((u32*)(SP_DMEM_START + 0x8), DMEMbuffer[2]); - /* BUG: the last word is not restored properly */ - __rmonWriteWordTo((u32*)(SP_DMEM_START + 0xC), DMEMbuffer[2]); - } - __rmonWriteWordTo((u32*)SP_IMEM_START, oldIMEMvalue); - __rmonWriteWordTo((u32*)SP_PC_REG, RCPpc); -} - -int __rmonGetGRegisters(KKHeader* req) { - register KKObjectRequest* request = (KKObjectRequest*)req; - KKGregEvent reply; - - STUBBED_PRINTF(("GetGRegisters\n")); - - reply.tid = request->object; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - - if (request->header.method == RMON_CPU) { - OSThread* tptr = __rmonGetTCB(request->object); - u64* tcbregptr; - register s32 i; - - if (tptr == NULL) { - return TV_ERROR_INVALID_ID; - } - - for (i = GREG_IDX_AT, tcbregptr = &tptr->context.at; i < GREG_IDX_K0; i++, tcbregptr++) { - reply.registers.gregs[i] = *tcbregptr; - } - for (i = GREG_IDX_GP, tcbregptr = &tptr->context.gp; i < GREG_IDX_CAUSE; i++, tcbregptr++) { - reply.registers.gregs[i] = *tcbregptr; - } - - reply.registers.gregs[GREG_IDX_CAUSE] = tptr->context.cause; - reply.registers.gregs[GREG_IDX_PC] = tptr->context.pc; - reply.registers.gregs[GREG_IDX_SR] = tptr->context.sr; - reply.registers.gregs[GREG_IDX_ZERO] = 0; - } else { - return TV_ERROR_INVALID_ID; - } - - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonSetGRegisters(KKHeader* req) { - register KKGRegsetRequest* request = (KKGRegsetRequest*)req; - KKObjectEvent reply; - - STUBBED_PRINTF(("SetGRegisters\n")); - - if (request->header.method == RMON_CPU) { - OSThread* tptr = __rmonGetTCB(request->tid); - u64* tcbregptr; - register int i; - - if (tptr == NULL) { - return TV_ERROR_INVALID_ID; - } - - for (i = GREG_IDX_AT, tcbregptr = &tptr->context.at; i < GREG_IDX_K0; i++, tcbregptr++) { - *tcbregptr = (s32)request->registers.gregs[i]; - } - - for (i = GREG_IDX_GP, tcbregptr = &tptr->context.gp; i < GREG_IDX_CAUSE; i++, tcbregptr++) { - *tcbregptr = (s32)request->registers.gregs[i]; - } - - tptr->context.cause = request->registers.gregs[GREG_IDX_CAUSE]; - tptr->context.pc = request->registers.gregs[GREG_IDX_PC]; - tptr->context.sr = request->registers.gregs[GREG_IDX_SR]; - } else { - return TV_ERROR_INVALID_ID; - } - - reply.object = request->tid; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonGetFRegisters(KKHeader* req) { - register KKObjectRequest* request = (KKObjectRequest*)req; - KKFPregEvent reply; - OSThread* tptr; - volatile float f UNUSED; - - STUBBED_PRINTF(("GetFRegisters\n")); - - if (req->method != RMON_CPU) { - return TV_ERROR_INVALID_ID; - } - - /* touch fpu to ensure registers are saved to the context structure */ - f = 0.0f; - - tptr = __rmonGetTCB(request->object); - if (tptr == NULL) { - return TV_ERROR_INVALID_ID; - } - - __rmonCopyWords((u32*)reply.registers.fpregs.regs, (u32*)&tptr->context.fp0, ARRLEN(reply.registers.fpregs.regs)); - - reply.registers.fpcsr = tptr->context.fpcsr; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.tid = request->object; - - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonSetFRegisters(KKHeader* req) { - register KKFPRegsetRequest* request = (KKFPRegsetRequest*)req; - KKObjectEvent reply; - OSThread* tptr; - volatile float f UNUSED; - - STUBBED_PRINTF(("SetFRegisters\n")); - - if (req->method != RMON_CPU) { - return TV_ERROR_INVALID_ID; - } - - /* touch fpu to ensure registers are saved to the context structure */ - f = 0.0f; - - tptr = __rmonGetTCB(request->tid); - if (tptr == NULL) { - return TV_ERROR_INVALID_ID; - } - - __rmonCopyWords((u32*)&tptr->context.fp0, (u32*)request->registers.fpregs.regs, - ARRLEN(request->registers.fpregs.regs)); - tptr->context.fpcsr = request->registers.fpcsr; - - reply.object = request->tid; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -static u32 rmonGetRcpRegister(int regNumber) { - u32 contents; - - if (__rmonRCPrunning()) { - return 0; - } - - SetUpForRCPop(FALSE); - LoadStoreSU(MIPS_SW_OPCODE, regNumber); - __rmonStepRCP(); - contents = __rmonReadWordAt((u32*)SP_DMEM_START); - CleanupFromRCPop(FALSE); - - return contents; -} - -int __rmonGetSRegs(KKHeader* req) { - register KKObjectRequest* request = (KKObjectRequest*)req; - KKCpSregEvent reply; - register int i; - - STUBBED_PRINTF(("GetSRegisters\n")); - - if (__rmonRCPrunning()) { - return TV_ERROR_OPERATIONS_PROTECTED; - } - - reply.tid = request->object; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - - SetUpForRCPop(FALSE); - for (i = SREG_IDX_ZERO; i <= SREG_IDX_RA; i++) { - LoadStoreSU(MIPS_SW_OPCODE, i); - __rmonStepRCP(); - reply.registers.sregs[i] = __rmonReadWordAt((u32*)SP_DMEM_START); - } - CleanupFromRCPop(FALSE); - - reply.registers.sregs[SREG_IDX_DRAM_ADDR] = __rmonReadWordAt((u32*)SP_DRAM_ADDR_REG); - reply.registers.sregs[SREG_IDX_MEM_ADDR] = __rmonReadWordAt((u32*)SP_MEM_ADDR_REG); - reply.registers.sregs[SREG_IDX_RD_LEN] = __rmonReadWordAt((u32*)SP_RD_LEN_REG); - reply.registers.sregs[SREG_IDX_PC] = __rmonReadWordAt((u32*)SP_PC_REG) + SP_IMEM_START; - reply.registers.sregs[SREG_IDX_WR_LEN] = __rmonReadWordAt((u32*)SP_WR_LEN_REG); - reply.registers.sregs[SREG_IDX_STATUS] = __rmonReadWordAt((u32*)SP_STATUS_REG); - reply.registers.sregs[SREG_IDX_DMA_FULL] = __rmonReadWordAt((u32*)SP_DMA_FULL_REG); - reply.registers.sregs[SREG_IDX_DMA_BUSY] = __rmonReadWordAt((u32*)SP_DMA_BUSY_REG); - - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonSetSRegs(KKHeader* req) { - register KKCpScalarRegsetRequest* request = (KKCpScalarRegsetRequest*)req; - KKObjectEvent reply; - register int i; - - STUBBED_PRINTF(("SetSRegisters\n")); - - if (__rmonRCPrunning()) { - return TV_ERROR_OPERATIONS_PROTECTED; - } - - SetUpForRCPop(FALSE); - for (i = SREG_IDX_ZERO; i <= SREG_IDX_RA; i++) { - __rmonWriteWordTo((u32*)SP_DMEM_START, request->registers.sregs[i]); - LoadStoreSU(MIPS_LW_OPCODE, i); - __rmonStepRCP(); - } - CleanupFromRCPop(FALSE); - - __rmonWriteWordTo((u32*)SP_DRAM_ADDR_REG, request->registers.sregs[SREG_IDX_DRAM_ADDR]); - __rmonWriteWordTo((u32*)SP_MEM_ADDR_REG, request->registers.sregs[SREG_IDX_MEM_ADDR]); - __rmonWriteWordTo((u32*)SP_PC_REG, request->registers.sregs[SREG_IDX_PC] & 0xFFF); - __rmonWriteWordTo((u32*)SP_WR_LEN_REG, request->registers.sregs[SREG_IDX_WR_LEN]); - __rmonWriteWordTo((u32*)SP_STATUS_REG, request->registers.sregs[SREG_IDX_STATUS]); - - reply.object = request->tid; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonGetVRegs(KKHeader* req) { - char* cPtr; - int sent; - int dataSize; - register KKObjectRequest* request = (KKObjectRequest*)req; - KKCpVregEvent reply; - register int i; - - STUBBED_PRINTF(("GetVRegisters\n")); - - if (__rmonRCPrunning()) { - return TV_ERROR_OPERATIONS_PROTECTED; - } - - reply.tid = request->object; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.header.length = sizeof(reply); - - dataSize = sizeof(reply); - cPtr = (char*)&dataSize; - sent = 0; - while (sent < (signed)sizeof(dataSize)) { - sent += __osRdbSend(cPtr + sent, sizeof(dataSize) - sent, RDB_TYPE_GtoH_DEBUG); - } - - __rmonSendHeader(&reply.header, VREG_SIZE, KK_TYPE_REPLY); - - SetUpForRCPop(TRUE); - for (i = 0; i < VREG_NUM; i++) { - LoadStoreVU(MIPS_SWC2_OPCODE, i); - __rmonStepRCP(); - __rmonSendData((void*)SP_DMEM_START, VREG_SIZE); - } - CleanupFromRCPop(TRUE); - - return TV_ERROR_NO_ERROR; -} - -int __rmonSetVRegs(KKHeader* req) { - register KKCpVectorRegsetRequest* request = (KKCpVectorRegsetRequest*)req; - KKObjectEvent reply; - register int i; - - STUBBED_PRINTF(("SetVRegs\n")); - - if (__rmonRCPrunning()) { - return TV_ERROR_OPERATIONS_PROTECTED; - } - - SetUpForRCPop(TRUE); - for (i = 0; i < VREG_NUM; i++) { - __rmonCopyWords((u32*)SP_DMEM_START, (u32*)&request->registers.vregs[i], VREG_SIZE / sizeof(u32)); - LoadStoreVU(MIPS_LWC2_OPCODE, i); - __rmonStepRCP(); - } - CleanupFromRCPop(TRUE); - - reply.object = request->tid; - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -u32 __rmonGetRegisterContents(int method, int threadNumber, int regNumber) { - if (method == RMON_CPU) { - /* CPU register */ - u32* regPointer; - OSThread* tptr; - - if (regNumber >= GREG_IDX_AT && regNumber < GREG_IDX_K0) { - regNumber -= GREG_IDX_AT - GREG_IDX_ZERO; - } else if (regNumber >= GREG_IDX_GP && regNumber < GREG_IDX_LO) { - regNumber -= GREG_IDX_GP - GREG_IDX_T9; - } else { - return 0; - } - tptr = __rmonGetTCB(threadNumber); - if (tptr == NULL) { - return 0; - } - regPointer = (u32*)&tptr->context; - regPointer += regNumber; - return *regPointer; - } else { - /* RSP register */ - return rmonGetRcpRegister(regNumber); - } -} - -#endif diff --git a/src/rmon/rmonsio.c b/src/rmon/rmonsio.c deleted file mode 100644 index b539a535..00000000 --- a/src/rmon/rmonsio.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "PR/os_version.h" - -#ifndef _FINALROM - -#include "PR/os_internal.h" -#include "PR/ultraerror.h" -#include "PR/ultralog.h" -#include "PR/sptask.h" -#include "PRinternal/dbgproto.h" -#include "PRinternal/rmonint.h" -#include "PR/ramrom.h" -#include "PR/rdb.h" -#include "PR/rmon.h" - -#include "PRinternal/macros.h" - -// TODO: this comes from a header -#ident "$Revision: 1.4 $" - -static OSMesgQueue IOmq ALIGNED(0x8); -static OSMesg IOmsgs; - -void* __osRdb_DbgRead_Buf; -u8 rmonRdbReadBuf[RMON_DBG_BUF_SIZE] ALIGNED(0x10); - -void __rmonSendFault(OSThread* thread) { - volatile float f UNUSED; - u8* tPtr; - u32 sent = 0; - - /* touch fpu to ensure registers are saved to the context structure */ - f = 0.0f; - - tPtr = (u8*)thread; - while (sent < sizeof(OSThread)) { - sent += __osRdbSend(tPtr + sent, sizeof(OSThread) - sent, RDB_TYPE_GtoH_FAULT); - } -} - -void __rmonIOflush(void) { - int sent = 0; - char tstr[4]; - - while (sent <= 0) { - sent += __osRdbSend(tstr, 1, RDB_TYPE_GtoH_DEBUG_DONE); - } -} - -void __rmonIOputw(u32 word) { - int sent = 0; - char* cPtr = (char*)&word; - - while (sent < 4) { - sent += __osRdbSend(cPtr + sent, sizeof(word) - sent, RDB_TYPE_GtoH_DEBUG); - } -} - -void __rmonIOhandler(void) { - int sent; - char tstr[4]; - - osCreateMesgQueue(&IOmq, &IOmsgs, 1); - osSetEventMesg(OS_EVENT_RDB_DBG_DONE, &IOmq, NULL); - __osRdb_DbgRead_Buf = rmonRdbReadBuf; - - while (TRUE) { - osRecvMesg(&IOmq, NULL, OS_MESG_BLOCK); - - __rmonExecute((KKHeader*)&rmonRdbReadBuf); - __osRdb_DbgRead_Buf = rmonRdbReadBuf; - - sent = 0; - while (sent <= 0) { - sent += __osRdbSend(tstr, 1, RDB_TYPE_GtoH_DEBUG_READY); - } - } -} - -#endif diff --git a/src/rmon/rmontask.c b/src/rmon/rmontask.c deleted file mode 100644 index 1ff026e7..00000000 --- a/src/rmon/rmontask.c +++ /dev/null @@ -1,337 +0,0 @@ -#ifndef _FINALROM - -#include "PRinternal/dbgproto.h" -#include "PR/os_internal.h" -#include "PRinternal/rmonint.h" -#include "PR/rcp.h" -#include "PR/sptask.h" - -#include "PRinternal/macros.h" - -// TODO: this comes from a header -#ident "$Revision: 1.4 $" - -void __rmonMaskIdleThreadInts(void) { - register OSThread* tptr = __osGetActiveQueue(); - - while (tptr->priority != -1) { - if (tptr->priority == OS_PRIORITY_IDLE) { - tptr->context.sr &= ~OS_IM_CPU; - tptr->context.sr |= (OS_IM_RDBREAD | OS_IM_RDBWRITE | OS_IM_CART); - break; - } - tptr = tptr->tlnext; - } -} - -OSThread* __rmonGetTCB(int threadNumber) { - register OSThread* tptr = __osGetActiveQueue(); - - if (threadNumber < 1) { - return NULL; - } - - while (tptr->priority != -1) { - if (tptr->id == threadNumber) { - return tptr; - } - tptr = tptr->tlnext; - } - - return NULL; -} - -int __rmonStopUserThreads(int whichThread) { - register int whichOne = 0; - register OSThread* tptr = __osGetActiveQueue(); - - STUBBED_PRINTF(("StopThreads %d\n", whichThread)); - - if (whichThread != 0) { - /* Stop specified thread */ - - while (tptr->priority != -1) { - if (tptr->id == whichThread) { - break; - } - tptr = tptr->tlnext; - } - - if (tptr->priority == -1) { - return 0; - } - - if (tptr->priority > OS_PRIORITY_IDLE && tptr->priority <= OS_PRIORITY_APPMAX) { - osStopThread(tptr); - if (tptr->state != OS_STATE_STOPPED) { - STUBBED_PRINTF(("Couldn't stop thread %d\n", tptr->id)); - } - whichOne = whichThread; - } - } else { - /* Stop all threads */ - - while (tptr->priority != -1) { - if (tptr->priority > OS_PRIORITY_IDLE && tptr->priority <= OS_PRIORITY_APPMAX) { - osStopThread(tptr); - if (tptr->state != OS_STATE_STOPPED) { - STUBBED_PRINTF(("Couldn\'t stop thread %d\n", tptr->id)); - } - whichOne = -1; - } - tptr = tptr->tlnext; - } - } - return whichOne; -} - -int __rmonListThreads(KKHeader* req) { - register KKObjectRequest* request = (KKObjectRequest*)req; - KKObjsEvent* reply = (KKObjsEvent*)__rmonUtilityBuffer; - - STUBBED_PRINTF(("ListThreads\n")); - - reply->object = (request->object == -1) ? RMON_PID_CPU : request->object; - - if (req->method == RMON_RSP) { - reply->objs.number = 1; - reply->objs.objects[0] = RMON_TID_RSP; - } else { - register OSThread* tptr = __osGetActiveQueue(); - - reply->objs.number = 0; - - while (tptr->priority != -1) { - if (tptr->id != 0) { - reply->objs.objects[reply->objs.number] = tptr->id; - reply->objs.number++; - } - tptr = tptr->tlnext; - } - } - reply->header.code = request->header.code; - reply->header.error = TV_ERROR_NO_ERROR; - __rmonSendReply(&reply->header, sizeof(*reply) + sizeof(reply->objs.objects[0]) * (reply->objs.number - 1), - KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonGetThreadStatus(int method, int id, KKStatusEvent* reply) { - u32 inst; - - STUBBED_PRINTF(("ThreadStatus %d method %d\n", id, method)); - - reply->status.tid = id; - reply->status.pid = (method == RMON_RSP) ? RMON_PID_RSP : RMON_PID_CPU; - reply->status.why = 1; - reply->status.what = 0; - reply->status.info.major = 0; - reply->status.info.minor = 0; - reply->status.rv = 0; - - if (method == RMON_RSP) { - reply->status.start = SP_IMEM_START; - reply->status.priority = RMON_PRI_RSP; - - if (__rmonRCPrunning()) { - reply->status.flags = OS_STATE_RUNNING; - /* Cannot read RSP PC or current instruction while the RSP is running */ - reply->status.info.addr = 0; - reply->status.instr = 0; - } else { - reply->status.flags = OS_STATE_STOPPED; - reply->status.info.addr = __rmonReadWordAt((u32*)SP_PC_REG) + SP_IMEM_START; - inst = __rmonReadWordAt((u32*)reply->status.info.addr); - if ((inst & MIPS_BREAK_MASK) == MIPS_BREAK_OPCODE) { - inst = MIPS_BREAK_OPCODE; - } - if (__rmonRcpAtBreak) { - /* Report RSP break */ - reply->status.why = 2; - reply->status.info.major = 2; - reply->status.info.minor = 4; - } - reply->status.instr = inst; - } - } else { - OSThread* tptr = __osGetActiveQueue(); - - while (tptr->priority != -1) { - if (tptr->id == id) { - break; - } - tptr = tptr->tlnext; - } - if (tptr->priority == -1) { - return TV_ERROR_INVALID_ID; - } - - reply->status.priority = tptr->priority; - reply->status.flags = (tptr->state != 0) ? tptr->state : OS_STATE_STOPPED; - reply->status.info.addr = tptr->context.pc; - - inst = *(u32*)(tptr->context.pc); - if ((inst & MIPS_BREAK_MASK) == MIPS_BREAK_OPCODE) { - inst = MIPS_BREAK_OPCODE; - } - - reply->status.instr = inst; - reply->status.start = (int)tptr; - - if (tptr->flags & OS_FLAG_CPU_BREAK) { - /* Report break */ - reply->status.why = 2; - reply->status.info.major = 2; - reply->status.info.minor = 4; - } else if (tptr->flags & OS_FLAG_FAULT) { - /* Report fault */ - reply->status.why = 2; - reply->status.info.major = 1; - reply->status.info.minor = 2; - } - } - - return TV_ERROR_NO_ERROR; -} - -int __rmonThreadStatus(KKHeader* req) { - KKObjectRequest* request = (KKObjectRequest*)req; - KKStatusEvent reply; - - if (__rmonGetThreadStatus(req->method, request->object, &reply) != TV_ERROR_NO_ERROR) { - return TV_ERROR_INVALID_ID; - } - - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - return TV_ERROR_NO_ERROR; -} - -int __rmonStopThread(KKHeader* req) { - KKObjectRequest* request = (KKObjectRequest*)req; - KKStatusEvent reply; - u32* pc; - - STUBBED_PRINTF(("StopThread %d\n", request->object)); - - switch (req->method) { - case RMON_CPU: - __rmonStopUserThreads(request->object); - break; - case RMON_RSP: - if (__rmonRCPrunning()) { - /* Stop the rsp */ - __rmonIdleRCP(); - pc = (u32*)__rmonReadWordAt((u32*)SP_PC_REG); - if (pc == NULL) { - break; - } - pc--; - /* Check if the RSP is stopped in a branch delay slot, if it is step out of it. The RSP - would otherwise lose information about whether the branch should or should not be - taken when reading registers. */ - if (__rmonGetBranchTarget(RMON_RSP, RMON_TID_RSP, (void*)((u32)pc + SP_IMEM_START)) % 4 == 0) { - __rmonStepRCP(); - } - } - break; - default: - return TV_ERROR_OPERATIONS_PROTECTED; - } - - if (__rmonGetThreadStatus(req->method, request->object, &reply) != TV_ERROR_NO_ERROR) { - return TV_ERROR_INVALID_ID; - } - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - if (reply.status.flags == OS_STATE_STOPPED) { - reply.header.code = KK_CODE_THREAD_STATUS; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_EXCEPTION); - } - return TV_ERROR_NO_ERROR; -} - -int __rmonRunThread(KKHeader* req) { - KKRunThreadRequest* request = (KKRunThreadRequest*)req; - KKObjectEvent reply; - KKStatusEvent exceptionReply; - register OSThread* tptr; - register int runNeeded = FALSE; - - STUBBED_PRINTF(("RunThread %d\n", request->tid)); - - switch (req->method) { - case RMON_CPU: - tptr = __osGetActiveQueue(); - while (tptr->priority != -1) { - if (tptr->id == request->tid) { - break; - } - tptr = tptr->tlnext; - } - - if (tptr->priority == -1) { - return TV_ERROR_INVALID_ID; - } - if (tptr->state != OS_STATE_STOPPED) { - return TV_ERROR_OPERATIONS_PROTECTED; - } - tptr->flags &= ~(OS_FLAG_CPU_BREAK | OS_FLAG_FAULT); - if (request->actions.flags & KK_RUN_SETPC) { - tptr->context.pc = request->actions.vaddr; - } - if ((request->actions.flags & KK_RUN_SSTEP) && !__rmonSetSingleStep(request->tid, (u32*)tptr->context.pc)) { - return TV_ERROR_OPERATIONS_PROTECTED; - } - runNeeded = TRUE; - break; - case RMON_RSP: - if (__rmonRCPrunning()) { - return TV_ERROR_OPERATIONS_PROTECTED; - } - if (request->actions.flags & KK_RUN_SETPC) { - __rmonWriteWordTo((u32*)SP_PC_REG, request->actions.vaddr - SP_IMEM_START); - } - if (request->actions.flags & KK_RUN_SSTEP) { - /* If the RSP is stopped at a branch step twice so as to not stop in a branch delay - * slot. */ - if (__rmonGetBranchTarget(RMON_RSP, RMON_TID_RSP, - (void*)(__rmonReadWordAt((u32*)SP_PC_REG) + SP_IMEM_START)) - % 4 - == 0) { - __rmonStepRCP(); - } - __rmonStepRCP(); - __rmonRcpAtBreak = TRUE; - } else { - __rmonRcpAtBreak = FALSE; - __rmonRunRCP(); - } - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.object = request->tid; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - if (request->actions.flags & KK_RUN_SSTEP) { - __rmonGetThreadStatus(RMON_RSP, RMON_TID_RSP, &exceptionReply); - __rmonGetExceptionStatus(&exceptionReply); - __rmonSendReply(&exceptionReply.header, sizeof(exceptionReply), KK_TYPE_EXCEPTION); - } - return TV_ERROR_NO_ERROR; - default: - return TV_ERROR_OPERATIONS_PROTECTED; - } - - reply.header.code = request->header.code; - reply.header.error = TV_ERROR_NO_ERROR; - reply.object = request->tid; - __rmonSendReply(&reply.header, sizeof(reply), KK_TYPE_REPLY); - - if (runNeeded) { - osStartThread(tptr); - } - return 1; -} - -#endif From 164c66b3c51603769d8ad3f9541fe59afdb08301 Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Sat, 23 Aug 2025 21:57:11 -0400 Subject: [PATCH 17/17] Remove MODERN_CC, as everyone is gonna be building this on a modern compiler anyway --- Makefile | 12 ++++++------ include/PR/os_libc.h | 6 ------ src/os/exceptasm.s | 2 -- src/os/getfpccsr.s | 4 ---- src/os/setfpccsr.s | 6 +----- 5 files changed, 7 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 401de010..32aea906 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ include util.mk WORKING_DIR := $(shell pwd) -DEFINES = MODERN_CC=1 +DEFINES := SRC_DIRS := @@ -144,7 +144,7 @@ clean: ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS)) # Make sure build directory exists before compiling anything -DUMMY != mkdir -p $(ALL_DIRS) +$(shell mkdir -p $(ALL_DIRS)) $(BUILD_DIR)/src/voice/%.o: CFLAGS += -I$(WORKING_DIR)/src/voice $(BUILD_DIR)/src/voice/%.o: DEFINES += LANG_JAPANESE=1 @@ -162,19 +162,19 @@ $(BUILD_DIR)/src/sp/spriteex2.o: GBIDEFINE := # Compile C code $(BUILD_DIR)/src/voice/%.o: src/voice/%.c $(call print,Compiling:,$<,$@) - $(V)tools/compile_sjis.py -D__CC=$(CC) -D__BUILD_DIR=$(BUILD_DIR) -c $(CFLAGS) -MMD -MF $(BUILD_DIR)/src/voice/$*.d -o $@ $< + $(V)tools/compile_sjis.py -D__CC=$(CC) -D__BUILD_DIR=$(BUILD_DIR) -c $(CFLAGS) -MMD -MP -o $@ $< $(BUILD_DIR)/%.o: %.c $(call print,Compiling:,$<,$@) - $(V)$(CC) -c $(CFLAGS) $(GBIDEFINE) -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< + $(V)$(CC) -c $(CFLAGS) $(GBIDEFINE) -MMD -MP -o $@ $< $(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c $(call print,Compiling:,$<,$@) - $(V)$(CC) -c $(CFLAGS) $(GBIDEFINE) -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< + $(V)$(CC) -c $(CFLAGS) $(GBIDEFINE) -MMD -MP -o $@ $< # Assemble assembly code $(BUILD_DIR)/%.o: %.s $(call print,Assembling:,$<,$@) - $(V)$(CC) -c $(CFLAGS) $(foreach i,$(INCLUDE_DIRS),-Wa,-I$(i)) -x assembler-with-cpp -DMIPSEB -D_LANGUAGE_ASSEMBLY -D_ULTRA64 -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< + $(V)$(CC) -c $(CFLAGS) $(foreach i,$(INCLUDE_DIRS),-Wa,-I$(i)) -x assembler-with-cpp -DMIPSEB -D_LANGUAGE_ASSEMBLY -D_ULTRA64 -MMD -MP -o $@ $< # Creating final library file $(LIB): $(O_FILES) diff --git a/include/PR/os_libc.h b/include/PR/os_libc.h index ec0e89b0..cb53cbf4 100644 --- a/include/PR/os_libc.h +++ b/include/PR/os_libc.h @@ -76,15 +76,9 @@ extern "C" { /* byte string operations */ -#ifndef MODERN_CC -extern void bcopy(const void*, void*, int); -extern int bcmp(const void*, const void*, int); -extern void bzero(void*, int); -#else extern void bcopy(const void*, void*, size_t); extern int bcmp(const void*, const void*, size_t); extern void bzero(void*, size_t); -#endif /* Printf */ diff --git a/src/os/exceptasm.s b/src/os/exceptasm.s index 919482e7..9a7dbea4 100644 --- a/src/os/exceptasm.s +++ b/src/os/exceptasm.s @@ -1,6 +1,4 @@ -#ifdef MODERN_CC .set gp=64 -#endif #include "PR/R4300.h" #include "sys/asm.h" diff --git a/src/os/getfpccsr.s b/src/os/getfpccsr.s index f51b1d0f..75cf2ef0 100644 --- a/src/os/getfpccsr.s +++ b/src/os/getfpccsr.s @@ -6,8 +6,4 @@ LEAF(__osGetFpcCsr) CFC1( v0, fcr31) jr ra -#ifndef MODERN_CC -END(__osGetSR) # @bug: Should be __osGetFpcCsr -#else END(__osGetFpcCsr) -#endif diff --git a/src/os/setfpccsr.s b/src/os/setfpccsr.s index f36e5b8c..de51d00b 100644 --- a/src/os/setfpccsr.s +++ b/src/os/setfpccsr.s @@ -7,8 +7,4 @@ LEAF(__osSetFpcCsr) CFC1( v0, fcr31) CTC1( a0, fcr31) jr ra -#ifndef MODERN_CC -END(__osSetSR) # @bug: Should be __osSetFpcCsr -#else -END(__osSetFpcCsr) -#endif +END(__osSetFpcCsr) \ No newline at end of file