[WIP] GCC Building + libhackerultra (#17)

Co-authored-by: a <a>
This commit is contained in:
bicyclesoda
2025-06-19 21:25:47 -04:00
committed by GitHub
parent 3ac9688312
commit 13313f3e31
1046 changed files with 71764 additions and 40860 deletions

200
Makefile
View File

@@ -18,13 +18,8 @@ DEFINES :=
# Build for the N64 (turn this off for ports)
TARGET_N64 ?= 1
# COMPILER - selects the C compiler to use
# ido - uses the SGI IRIS Development Option compiler, which is used to build
# an original matching N64 ROM
# gcc - uses the GNU C Compiler
COMPILER ?= ido
$(eval $(call validate-option,COMPILER,ido gcc))
COMPILER ?= gcc
$(eval $(call validate-option,COMPILER, gcc))
# VERSION - selects the version of the game to build
@@ -92,27 +87,9 @@ else ifeq ($(GRUCODE),f3dzex) # Fast3DZEX (2.0J / Animal Forest - Dōbutsu no Mo
DEFINES += F3DZEX_GBI_2=1 F3DEX_GBI_2=1 F3DEX_GBI_SHARED=1
endif
# USE_QEMU_IRIX - when ido is selected, select which way to emulate IRIX programs
# 1 - use qemu-irix
# 0 - statically recompile the IRIX programs
USE_QEMU_IRIX ?= 0
$(eval $(call validate-option,USE_QEMU_IRIX,0 1))
ifeq ($(COMPILER),ido)
ifeq ($(USE_QEMU_IRIX),1)
# Verify that qemu-irix exists
QEMU_IRIX ?= $(call find-command,qemu-irix)
ifeq (, $(QEMU_IRIX))
$(error Using the IDO compiler requires qemu-irix. Please install qemu-irix package or set the QEMU_IRIX environment variable to the full qemu-irix binary path)
endif
endif
MIPSISET := -mips2
else ifeq ($(COMPILER),gcc)
NON_MATCHING := 1
MIPSISET := -mips3
OPT_FLAGS := -O2
endif
NON_MATCHING := 1
MIPSISET := -mips3
OPT_FLAGS := -O2
# NON_MATCHING - whether to build a matching, identical copy of the ROM
@@ -171,7 +148,7 @@ ifeq ($(filter clean distclean,$(MAKECMDGOALS)),)
$(info =======================)
endif
DEFINES += _FINALROM=1
DEFINES += _FINALROM=1 LIBULTRA_VERSION=9
ifeq ($(TARGET_N64),1)
DEFINES += TARGET_N64=1
@@ -182,6 +159,8 @@ endif
#==============================================================================#
TOOLS_DIR := tools
LIBULTRA_DIR := lib/hackerlibultra
LIBULTRA_BUILD_DIR := lib/hackerlibultra/build/L/libgultra_rom
# (This is a bit hacky, but a lot of rules implicitly depend
# on tools and assets, and we use directory globs further down
@@ -211,6 +190,13 @@ ifeq ($(filter clean distclean print-%,$(MAKECMDGOALS)),)
ifeq ($(DUMMY),FAIL)
$(error Failed to build tools)
endif
$(info Building hackerlibultra...)
DUMMY != $(MAKE) -s -C $(LIBULTRA_DIR) >&2 || echo FAIL
ifeq ($(DUMMY),FAIL)
$(error Failed to build hackerlibultra)
endif
$(info Building ROM...)
endif
@@ -237,10 +223,6 @@ LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h)))
SRC_DIRS := src src/engine src/game src/menu src/buffers src/audio $(AUDIO_SRC_DIR) actors levels bin data assets asm lib sound
BIN_DIRS := bin bin/$(VERSION)
ifeq ($(VERSION),cn)
LIBGCC_SRC_DIRS += lib/src/libgcc
endif
GODDARD_SRC_DIRS := src/goddard src/goddard/dynlists
# File dependencies and variables for specific files
@@ -251,7 +233,6 @@ LEVEL_C_FILES := $(wildcard levels/*/leveldata.c) $(wildcard levels/*/script
C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) $(LEVEL_C_FILES)
S_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.s))
GODDARD_C_FILES := $(foreach dir,$(GODDARD_SRC_DIRS),$(wildcard $(dir)/*.c))
LIBGCC_C_FILES := $(foreach dir,$(LIBGCC_SRC_DIRS),$(wildcard $(dir)/*.c))
GENERATED_C_FILES := $(BUILD_DIR)/assets/mario_anim_data.c $(BUILD_DIR)/assets/demo_data.c
# Sound files
@@ -279,58 +260,23 @@ O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.o)) \
GODDARD_O_FILES := $(foreach file,$(GODDARD_C_FILES),$(BUILD_DIR)/$(file:.c=.o))
LIBGCC_O_FILES := $(foreach file,$(LIBGCC_C_FILES),$(BUILD_DIR)/$(file:.c=.o))
# Automatic dependency files
DEP_FILES := $(O_FILES:.o=.d) $(GODDARD_O_FILES:.o=.d) $(LIBGCC_O_FILES:.o=.d) $(BUILD_DIR)/$(LD_SCRIPT).d
# Files with GLOBAL_ASM blocks
ifeq ($(NON_MATCHING),0)
GLOBAL_ASM_C_FILES != grep -rl 'GLOBAL_ASM(' $(wildcard src/**/*.c)
GLOBAL_ASM_O_FILES = $(foreach file,$(GLOBAL_ASM_C_FILES),$(BUILD_DIR)/$(file:.c=.o))
GLOBAL_ASM_DEP = $(BUILD_DIR)/src/audio/non_matching_dep
endif
DEP_FILES := $(O_FILES:.o=.d) $(GODDARD_O_FILES:.o=.d) $(BUILD_DIR)/$(LD_SCRIPT).d
#==============================================================================#
# Compiler Options #
#==============================================================================#
EGCS_PATH := $(TOOLS_DIR)/egcs
LD_PATH := $(TOOLS_DIR)/ld
# detect prefix for MIPS toolchain
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-elf-ld),)
CROSS := mips64-elf-
# detect hackerchain
ifneq ($(call find-command, $(HACKERCHAIN)/mips64-elf-ld),)
CROSS := $(HACKERCHAIN)/mips64-elf-
else
$(error Unable to detect a suitable MIPS toolchain installed)
$(error Unable to detect a hackerchain toolchain installation.)
endif
AS := $(CROSS)as
ifeq ($(COMPILER),gcc)
CC := $(CROSS)gcc
else
ifeq ($(USE_QEMU_IRIX),1)
IRIX_ROOT := $(TOOLS_DIR)/ido5.3_compiler
CC := $(QEMU_IRIX) -silent -L $(IRIX_ROOT) $(IRIX_ROOT)/usr/bin/cc
ACPP := $(QEMU_IRIX) -silent -L $(IRIX_ROOT) $(IRIX_ROOT)/usr/lib/acpp
COPT := $(QEMU_IRIX) -silent -L $(IRIX_ROOT) $(IRIX_ROOT)/usr/lib/copt
else
IDO_ROOT := $(TOOLS_DIR)/ido-static-recomp/build/out
CC := $(IDO_ROOT)/cc
ACPP := $(IDO_ROOT)/acpp
COPT := $(IDO_ROOT)/copt
endif
endif
ifeq ($(COMPILER),gcc)
LD := $(CROSS)ld
else
LD := LD_LIBRARY_PATH=$(LD_PATH) $(LD_PATH)/mips64-elf-ld
endif
CC := $(CROSS)gcc
LD := $(CROSS)ld
AR := $(CROSS)ar
OBJDUMP := $(CROSS)objdump
OBJCOPY := $(CROSS)objcopy
@@ -348,32 +294,6 @@ endif
C_DEFINES := $(foreach d,$(DEFINES),-D$(d))
DEF_INC_CFLAGS := $(foreach i,$(INCLUDE_DIRS),-I$(i)) $(C_DEFINES)
EGCS_AS := $(EGCS_PATH)/as
EGCS_ASFLAGS = -mcpu=r4300 -mabi=32 $(foreach i,$(INCLUDE_DIRS),-I$(i))
include libultra.mk
ifeq ($(VERSION),cn)
EGCS_REASSEMBLED_ASM_FILES := $(wildcard asm/*.s)
EGCS_REASSEMBLED_ASM_FILES := $(filter-out asm/ipl3_font.s, $(EGCS_REASSEMBLED_ASM_FILES))
EGCS_REASSEMBLED := $(foreach file,$(EGCS_REASSEMBLED_ASM_FILES),$(BUILD_DIR)/$(file:.s=.o))
$(EGCS_REASSEMBLED): AS := $(EGCS_AS)
$(EGCS_REASSEMBLED): ASFLAGS = $(EGCS_ASFLAGS)
endif
EGCS_CC := COMPILER_PATH=$(EGCS_PATH) $(EGCS_PATH)/gcc
EGCS_CFLAGS = -G 0 $(TARGET_CFLAGS) -mcpu=r4300 -fno-pic -Wa,--strip-local-absolute $(DEF_INC_CFLAGS)
# iQue recompiled some files with a different compiler
ifeq ($(VERSION),cn)
IQUE_RECOMPILED_SRC_GAME := $(addprefix $(BUILD_DIR)/src/game/,rumble_init.o level_update.o memory.o area.o print.o ingame_menu.o hud.o cn_common_syms_1.o cn_common_syms_2.o) $(addprefix $(BUILD_DIR)/src/menu/,title_screen.o intro_geo.o file_select.o star_select.o)
IQUE_RECOMPILED_LIBGCC_SRC := $(LIBGCC_O_FILES)
IQUE_RECOMPILED = $(IQUE_RECOMPILED_SRC_GAME) $(IQUE_RECOMPILED_LIBGCC_SRC)
$(IQUE_RECOMPILED): CC := $(EGCS_CC)
$(IQUE_RECOMPILED): CFLAGS = $(EGCS_CFLAGS)
$(IQUE_RECOMPILED): MIPSISET :=
endif
# Prefer clang as C preprocessor if installed on the system
ifneq (,$(call find-command,clang))
CPP := clang
@@ -383,29 +303,15 @@ else
CPPFLAGS := -P -Wno-trigraphs $(DEF_INC_CFLAGS)
endif
# Check code syntax with host compiler
CC_CHECK := gcc
CC_CHECK_CFLAGS := -fsyntax-only -fsigned-char $(CC_CFLAGS) $(TARGET_CFLAGS) -std=gnu90 -Wall -Wextra -Wno-format-security -Wno-main -DNON_MATCHING -DAVOID_UB $(DEF_INC_CFLAGS)
# C compiler options
CFLAGS = -G 0 $(TARGET_CFLAGS) $(DEF_INC_CFLAGS)
ifeq ($(COMPILER),gcc)
CFLAGS += -mno-shared -march=vr4300 -mfix4300 -mabi=32 -mhard-float -mdivide-breaks -fno-stack-protector -fno-common -fno-zero-initialized-in-bss -fno-PIC -mno-abicalls -fno-strict-aliasing -fno-inline-functions -ffreestanding -fwrapv -Wall -Wextra
else
CFLAGS += -non_shared -Wab,-r4300_mul -Xcpluscomm -Xfullwarn -signed -32
endif
CFLAGS += -mno-shared -march=vr4300 -mfix4300 -mabi=32 -mhard-float -mdivide-breaks -fno-stack-protector -fno-common -fno-zero-initialized-in-bss -fno-PIC -mno-abicalls -fno-strict-aliasing -fno-inline-functions -ffreestanding -fwrapv -Wall -Wextra -Wno-trigraphs
CFLAGS += -Wno-missing-braces
ASFLAGS := -march=vr4300 -mabi=32 $(foreach i,$(INCLUDE_DIRS),-I$(i)) $(foreach d,$(DEFINES),--defsym $(d))
ASMFLAGS := -G 0 $(OPT_FLAGS) $(TARGET_CFLAGS) -mips3 $(DEF_INC_CFLAGS) -mno-shared -march=vr4300 -mfix4300 -mabi=32 -mhard-float -mdivide-breaks -fno-stack-protector -fno-common -fno-zero-initialized-in-bss -fno-PIC -mno-abicalls -fno-strict-aliasing -fno-inline-functions -ffreestanding -fwrapv -Wall -Wextra
RSPASMFLAGS := $(foreach d,$(DEFINES),-definelabel $(subst =, ,$(d)))
ifeq ($(shell getconf LONG_BIT), 32)
# Work around memory allocation bug in QEMU
export QEMU_GUEST_BASE := 1
else
# Ensure that gcc treats the code as 32-bit
CC_CHECK_CFLAGS += -m32
endif
# Prevent a crash with -sopt
export LANG := C
@@ -447,12 +353,7 @@ BLINK := \033[33;5m
endif
# Use objcopy instead of extract_data_for_mio to get 16-byte aligned padding
ifeq ($(COMPILER),gcc)
EXTRACT_DATA_FOR_MIO := $(OBJCOPY) -O binary --only-section=.data
endif
ifeq ($(VERSION),cn)
EXTRACT_DATA_FOR_MIO := $(OBJCOPY) -O binary --only-section=.data
endif
EXTRACT_DATA_FOR_MIO := $(OBJCOPY) -O binary --only-section=.data
# Common build print status function
define print
@@ -476,6 +377,7 @@ distclean: clean
$(PYTHON) extract_assets.py --clean
$(MAKE) -C $(TOOLS_DIR) clean
$(MAKE) -C $(TOOLS_DIR)/sm64tools clean
$(MAKE) -C $(LIBULTRA_DIR) clean
test: $(ROM)
$(EMULATOR) $(EMU_FLAGS) $<
@@ -494,10 +396,6 @@ $(BUILD_DIR)/src/audio/sh/load.o: $(SOUND_BIN_DIR)/bank_sets.inc.c $(SOUND_BIN_D
$(CRASH_TEXTURE_C_FILES): TEXTURE_ENCODING := u32
ifeq ($(COMPILER),gcc)
$(BUILD_DIR)/lib/src/math/%.o: CFLAGS += -fno-builtin
endif
ifeq ($(VERSION),eu)
TEXT_DIRS := text/de text/us text/fr
@@ -522,7 +420,7 @@ else
endif
$(BUILD_DIR)/bin/segment2.o: $(BUILD_DIR)/text/debug_text.raw.inc.c
ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(GODDARD_SRC_DIRS) $(ULTRA_SRC_DIRS) $(LIBGCC_SRC_DIRS) $(BIN_DIRS) $(TEXTURE_DIRS) $(TEXT_DIRS) $(SOUND_SAMPLE_DIRS) $(addprefix levels/,$(LEVEL_DIRS)) rsp include) $(MIO0_DIR) $(addprefix $(MIO0_DIR)/,$(VERSION)) $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/sequences/$(VERSION)
ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(GODDARD_SRC_DIRS) $(ULTRA_SRC_DIRS) $(BIN_DIRS) $(TEXTURE_DIRS) $(TEXT_DIRS) $(SOUND_SAMPLE_DIRS) $(addprefix levels/,$(LEVEL_DIRS)) rsp include) $(MIO0_DIR) $(addprefix $(MIO0_DIR)/,$(VERSION)) $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/sequences/$(VERSION)
# Make sure build directory exists before compiling anything
DUMMY != mkdir -p $(ALL_DIRS)
@@ -690,18 +588,6 @@ $(BUILD_DIR)/include/level_headers.h: levels/level_headers.h.in
$(call print,Preprocessing level headers:,$<,$@)
$(V)$(CPP) $(CPPFLAGS) -I . $< | sed -E 's|(.+)|#include "\1"|' > $@
# Run asm_processor on files that have NON_MATCHING code
ifeq ($(NON_MATCHING),0)
$(GLOBAL_ASM_O_FILES): CC := $(V)$(PYTHON) $(TOOLS_DIR)/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) --
endif
# Rebuild files with 'GLOBAL_ASM' if the NON_MATCHING flag changes.
$(GLOBAL_ASM_O_FILES): $(GLOBAL_ASM_DEP).$(NON_MATCHING)
$(GLOBAL_ASM_DEP).$(NON_MATCHING):
@$(RM) $(GLOBAL_ASM_DEP).*
$(V)touch $@
#==============================================================================#
# Compilation Recipes #
#==============================================================================#
@@ -713,9 +599,6 @@ ifeq ($(NON_MATCHING),0)
$(BUILD_DIR)/bin/%.o: OPT_FLAGS := -g
$(BUILD_DIR)/src/goddard/%.o: OPT_FLAGS := -g
$(BUILD_DIR)/src/goddard/%.o: MIPSISET := -mips1
ifeq ($(VERSION),cn)
$(BUILD_DIR)/lib/src/libgcc/%.o: OPT_FLAGS := -O2 -g -mips2
endif
# Audio specific flags:
@@ -749,18 +632,10 @@ endif
# Compile C code
$(BUILD_DIR)/%.o: %.c
$(call print,Compiling:,$<,$@)
$(V)$(CC_CHECK) $(CC_CHECK_CFLAGS) -MMD -MP -MT $@ -MF $(BUILD_DIR)/$*.d $<
$(V)$(CC) -c $(CFLAGS) $(OPT_FLAGS) $(MIPSISET) -o $@ $<
ifeq ($(VERSION),cn)
$(V)$(TOOLS_DIR)/patch_elf_32bit $@
endif
$(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c
$(call print,Compiling:,$<,$@)
$(V)$(CC_CHECK) $(CC_CHECK_CFLAGS) -MMD -MP -MT $@ -MF $(BUILD_DIR)/$*.d $<
$(V)$(CC) -c $(CFLAGS) $(OPT_FLAGS) $(MIPSISET) -o $@ $<
ifeq ($(VERSION),cn)
$(V)$(TOOLS_DIR)/patch_elf_32bit $@
endif
# Assemble assembly code
$(BUILD_DIR)/%.o: %.s
@@ -775,38 +650,25 @@ $(BUILD_DIR)/rsp/%.bin $(BUILD_DIR)/rsp/%_data.bin: rsp/%.s
# Run linker script through the C preprocessor
$(BUILD_DIR)/$(LD_SCRIPT): $(LD_SCRIPT)
$(call print,Preprocessing linker script:,$<,$@)
$(V)$(CPP) $(CPPFLAGS) -DBUILD_DIR=$(BUILD_DIR) -MMD -MP -MT $@ -MF $@.d -o $@ $<
$(V)$(CPP) $(CPPFLAGS) -DBUILD_DIR=$(BUILD_DIR) -DULTRA_BUILD_DIR=$(LIBULTRA_BUILD_DIR) -MMD -MP -MT $@ -MF $@.d -o $@ $<
# Link libgoddard
$(BUILD_DIR)/libgoddard.a: $(GODDARD_O_FILES)
@$(PRINT) "$(GREEN)Linking libgoddard: $(BLUE)$@ $(NO_COL)\n"
$(V)$(AR) rcs -o $@ $(GODDARD_O_FILES)
# Link libgcc
$(BUILD_DIR)/libgcc.a: $(LIBGCC_O_FILES)
@$(PRINT) "$(GREEN)Linking libgcc: $(BLUE)$@ $(NO_COL)\n"
$(V)$(AR) rcs -o $@ $(LIBGCC_O_FILES)
# Link SM64 ELF file
$(ELF): $(O_FILES) $(MIO0_OBJ_FILES) $(SEG_FILES) $(BUILD_DIR)/$(LD_SCRIPT) $(LIBULTRA_AR) $(BUILD_DIR)/libgoddard.a $(BUILD_DIR)/libgcc.a
$(ELF): $(LIBULTRA_BUILD_DIR)/libgultra_rom.a $(O_FILES) $(MIO0_OBJ_FILES) $(SEG_FILES) $(BUILD_DIR)/$(LD_SCRIPT) $(BUILD_DIR)/libgoddard.a
@$(PRINT) "$(GREEN)Linking ELF file: $(BLUE)$@ $(NO_COL)\n"
$(V)$(LD) -L $(BUILD_DIR) -T $(BUILD_DIR)/$(LD_SCRIPT) -Map $(BUILD_DIR)/sm64.$(VERSION).map --no-check-sections $(addprefix -R ,$(SEG_FILES)) -o $@ $(O_FILES) -lultra -lgoddard -lgcc
$(V)$(LD) -L $(BUILD_DIR) -L $(LIBULTRA_BUILD_DIR) -T $(BUILD_DIR)/$(LD_SCRIPT) -Map $(BUILD_DIR)/sm64.$(VERSION).map --no-check-sections $(addprefix -R ,$(SEG_FILES)) -o $@ $(O_FILES) -lgultra_rom -lgoddard
# Build ROM
ifeq ($(VERSION),cn)
PAD_TO_GAP_FILL := --pad-to=0x7B0000 --gap-fill=0x00
else
PAD_TO_GAP_FILL := --pad-to=0x800000 --gap-fill=0xFF
endif
$(ROM): $(ELF)
$(call print,Building ROM:,$<,$@)
ifeq ($(VERSION),cn) # cn has no checksums
$(V)$(OBJCOPY) $(PAD_TO_GAP_FILL) $< $(@) -O binary
else
$(V)$(OBJCOPY) $(PAD_TO_GAP_FILL) $< $(@:.z64=.bin) -O binary
$(V)$(N64CKSUM) $(@:.z64=.bin) $@
endif
$(BUILD_DIR)/$(TARGET).objdump: $(ELF)
$(OBJDUMP) -D $< > $@