mirror of
https://github.com/zeldaret/oot
synced 2026-08-04 01:03:36 -04:00
[rsp] Introduce microcode build process, disassemble and build both rspboot versions (#2764)
* [rsp] Introduce microcode build process, disassemble and build both rspboot versions * Suggested changes * Check-in tool for generating armips.cpp from upstream sources, regen armips.cpp against a newer commit * License armips_gen.py, add notice that armips.cpp is automatically generated * Qualify comment with X105 * Clarify the assumed purpose of rspboot releasing SP_SEMAPHORE
This commit is contained in:
@@ -49,6 +49,8 @@ DEBUG_OBJECTS ?= 0
|
||||
MIPS_BINUTILS_PREFIX ?=
|
||||
# Emulator w/ flags for 'make run'.
|
||||
N64_EMULATOR ?=
|
||||
# Set to also write .tmp.s and .sym files when assembling microcodes, for build debugging or development purposes
|
||||
UCODE_ASM_DEBUG ?= 0
|
||||
# Set to override game region in the ROM header (options: JP, US, EU). This can be used to build a fake US version
|
||||
# of the debug ROM for better emulator compatibility, or to build US versions of NTSC N64 ROMs.
|
||||
# REGION ?= US
|
||||
@@ -369,6 +371,7 @@ MKSPECRULES := tools/mkspecrules
|
||||
MKDMADATA := tools/mkdmadata
|
||||
BIN2C := tools/bin2c
|
||||
FADO := tools/fado/fado.elf
|
||||
ARMIPS := tools/armips
|
||||
PYTHON ?= $(VENV)/bin/python3
|
||||
BUILD_FROM_PNG := tools/assets/build_from_png/build_from_png
|
||||
BUILD_JFIF := tools/assets/build_jfif/build_jfif
|
||||
@@ -463,6 +466,7 @@ SPEC := spec/spec
|
||||
SPEC_INCLUDES := $(wildcard spec/*.inc)
|
||||
|
||||
SRC_DIRS := $(shell find src -type d)
|
||||
RSP_DIRS := $(shell find rsp -type d)
|
||||
UNDECOMPILED_DATA_DIRS := $(shell find data -type d)
|
||||
|
||||
ifneq ($(wildcard $(EXTRACTED_DIR)/assets/audio),)
|
||||
@@ -559,6 +563,7 @@ $(shell mkdir -p $(BUILD_DIR)/baserom \
|
||||
$(SEGMENTS_DIR))
|
||||
$(shell mkdir -p $(foreach dir, \
|
||||
$(SRC_DIRS) \
|
||||
$(RSP_DIRS) \
|
||||
$(UNDECOMPILED_DATA_DIRS) \
|
||||
$(SAMPLE_DIRS) \
|
||||
$(SAMPLEBANK_DIRS) \
|
||||
@@ -1039,6 +1044,44 @@ $(BUILD_DIR)/dmadata_table_spec.h $(BUILD_DIR)/compress_ranges.txt: $(BUILD_DIR)
|
||||
$(BUILD_DIR)/src/boot/z_std_dma.o: $(BUILD_DIR)/dmadata_table_spec.h
|
||||
$(BUILD_DIR)/src/dmadata/dmadata.o: $(BUILD_DIR)/dmadata_table_spec.h
|
||||
|
||||
# Note this is required for f3dex2 on GC/iQue as the rsp text is in .rodata
|
||||
RSP_TEXT_SECTION := .text
|
||||
RSP_DATA_SECTION := .rodata
|
||||
|
||||
.PRECIOUS: $(BUILD_DIR)/rsp/%.s
|
||||
$(BUILD_DIR)/rsp/%.s: rsp/%.s
|
||||
$(CPP) $(CPPFLAGS) -D_LANGUAGE_ASSEMBLY $(GBI_DEFINES) -MMD -MP -MT $@ -I include -I include/ultra64 -I rsp $< -o $@
|
||||
|
||||
ifneq ($(UCODE_ASM_DEBUG),0)
|
||||
# Instruct armips to output a symbol map and a processed view of the asm
|
||||
ARMIPS_FLAGS = -sym2 $(<:.s=.sym) -temp $(<:.s=.tmp.s)
|
||||
else
|
||||
ARMIPS_FLAGS =
|
||||
endif
|
||||
|
||||
.PRECIOUS: $(BUILD_DIR)/rsp/%.text.bin $(BUILD_DIR)/rsp/%.data.bin
|
||||
$(BUILD_DIR)/rsp/%.text.bin $(BUILD_DIR)/rsp/%.data.bin: $(BUILD_DIR)/rsp/%.s
|
||||
# assemble to code and data binaries
|
||||
$(ARMIPS) -strequ CODE_FILE $(<:.s=.text.bin) -strequ DATA_FILE $(<:.s=.data.bin) $< $(ARMIPS_FLAGS)
|
||||
# create an empty file if armips did not error but one of the files was not created
|
||||
touch $(<:.s=.text.bin) $(<:.s=.data.bin)
|
||||
|
||||
# The default microcode name equals the source file name with dots substituted for underscores.
|
||||
UC_NAME = $(subst .,_,$(@F:.o=))
|
||||
|
||||
# Override the name for the antipiracy rspboot variant to just be "rspboot"
|
||||
$(BUILD_DIR)/rsp/rspboot_ap.o: UC_NAME = rspboot
|
||||
|
||||
RSP2ELF_DEFS = \
|
||||
-D UC_NAME=$(UC_NAME) \
|
||||
-D UC_TEXT_SECTION=$(RSP_TEXT_SECTION) \
|
||||
-D UC_DATA_SECTION=$(RSP_DATA_SECTION) \
|
||||
-D UC_TEXT_BIN_PATH="$(@:.o=.text.bin)" \
|
||||
-D UC_DATA_BIN_PATH="$(@:.o=.data.bin)"
|
||||
|
||||
$(BUILD_DIR)/rsp/%.o: $(BUILD_DIR)/rsp/%.text.bin $(BUILD_DIR)/rsp/%.data.bin rsp/rsp2elf.s
|
||||
$(CPP) $(CPPFLAGS) $(RSP2ELF_DEFS) rsp/rsp2elf.s | $(AS) $(ASFLAGS) -o $@
|
||||
|
||||
$(BUILD_DIR)/src/%.o: src/%.c
|
||||
$(CC_CHECK) $< -o $@
|
||||
$(PREPROCESS) $(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
.include "macro.inc"
|
||||
|
||||
/* assembler directives */
|
||||
.set noat /* allow manual use of $at */
|
||||
.set noreorder /* don't insert nops after branches */
|
||||
.set gp=64 /* allow use of 64-bit general purpose registers */
|
||||
|
||||
.section .text
|
||||
|
||||
.balign 16
|
||||
|
||||
glabel rspbootTextStart
|
||||
.incbin "incbin/rspbootText"
|
||||
glabel rspbootTextEnd
|
||||
@@ -28,6 +28,14 @@
|
||||
|
||||
#define OS_YIELD_DATA_SIZE 0xC00
|
||||
|
||||
#ifdef _LANGUAGE_ASSEMBLY
|
||||
#include "sptaskoff.h"
|
||||
#endif
|
||||
|
||||
#ifdef _LANGUAGE_C
|
||||
|
||||
#include "ultratypes.h"
|
||||
|
||||
typedef struct OSTask_t {
|
||||
/* 0x00 */ u32 type;
|
||||
/* 0x04 */ u32 flags;
|
||||
@@ -66,3 +74,5 @@ typedef u32 OSYieldResult;
|
||||
osSpTaskStartGo(p);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef ULTRA64_SPTASKOFF_H
|
||||
#define ULTRA64_SPTASKOFF_H
|
||||
|
||||
#define OS_TASK_SIZE 0x40 /* sizeof(OSTask) */
|
||||
|
||||
#define OS_TASK_OFF_TYPE 0x00
|
||||
#define OS_TASK_OFF_FLAGS 0x04
|
||||
#define OS_TASK_OFF_UBOOT 0x08
|
||||
#define OS_TASK_OFF_UBOOT_SZ 0x0C
|
||||
#define OS_TASK_OFF_UCODE 0x10
|
||||
#define OS_TASK_OFF_UCODE_SZ 0x14
|
||||
#define OS_TASK_OFF_UDATA 0x18
|
||||
#define OS_TASK_OFF_UDATA_SZ 0x1C
|
||||
#define OS_TASK_OFF_STACK 0x20
|
||||
#define OS_TASK_OFF_STACK_SZ 0x24
|
||||
#define OS_TASK_OFF_OUTBUFF 0x28
|
||||
#define OS_TASK_OFF_OUTBUFF_SZ 0x2C
|
||||
#define OS_TASK_OFF_DATA 0x30
|
||||
#define OS_TASK_OFF_DATA_SZ 0x34
|
||||
#define OS_TASK_OFF_YIELD 0x38
|
||||
#define OS_TASK_OFF_YIELD_SZ 0x3C
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file rsp2elf.s
|
||||
*
|
||||
* This file converts binaries output from armips into a suitable elf file to be linked into the project.
|
||||
* It requires the following preprocessor definitions to be provided when assembled:
|
||||
* UC_NAME : The microcode name, used to generate symbol names for text and data
|
||||
* UC_TEXT_SECTION : The program section to place the microcode text into, usually .text
|
||||
* UC_DATA_SECTION : The program section to place the microcode data into, usually .rodata
|
||||
* UC_TEXT_BIN_PATH : The path to the microcode text binary
|
||||
* UC_DATA_BIN_PATH : The path to the microcode data binary
|
||||
*/
|
||||
|
||||
/* Preprocessor macros */
|
||||
|
||||
#define GLUE(a,b) a##b
|
||||
#define SYM_NAME(a,b) GLUE(a,b)
|
||||
|
||||
#define STR(x) #x
|
||||
#define BIN_PATH(x) STR(x)
|
||||
|
||||
/* Microcode text */
|
||||
|
||||
.section UC_TEXT_SECTION
|
||||
|
||||
.global SYM_NAME(UC_NAME,TextStart)
|
||||
SYM_NAME(UC_NAME,TextStart):
|
||||
|
||||
.incbin BIN_PATH(UC_TEXT_BIN_PATH)
|
||||
|
||||
.global SYM_NAME(UC_NAME,TextEnd)
|
||||
SYM_NAME(UC_NAME,TextEnd):
|
||||
|
||||
.size SYM_NAME(UC_NAME,TextStart), SYM_NAME(UC_NAME,TextEnd) - SYM_NAME(UC_NAME,TextStart)
|
||||
|
||||
/* Microcode data */
|
||||
|
||||
.section UC_DATA_SECTION
|
||||
|
||||
.global SYM_NAME(UC_NAME,DataStart)
|
||||
SYM_NAME(UC_NAME,DataStart):
|
||||
|
||||
.incbin BIN_PATH(UC_DATA_BIN_PATH)
|
||||
|
||||
.global SYM_NAME(UC_NAME,DataEnd)
|
||||
SYM_NAME(UC_NAME,DataEnd):
|
||||
|
||||
.size SYM_NAME(UC_NAME,DataStart), SYM_NAME(UC_NAME,DataEnd) - SYM_NAME(UC_NAME,DataStart)
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef RSPBOOT_H
|
||||
#define RSPBOOT_H
|
||||
|
||||
/* Memory addressing */
|
||||
#define DMEM_START (0x0000)
|
||||
#define DMEM_SIZE (0x1000)
|
||||
#define DMEM_END (DMEM_START + DMEM_SIZE)
|
||||
#define IMEM_START (DMEM_END)
|
||||
#define IMEM_SIZE (0x1000)
|
||||
#define IMEM_END (IMEM_START + IMEM_SIZE)
|
||||
|
||||
#define DMEM_START_VA (0x04000000 | DMEM_START)
|
||||
#define IMEM_START_VA (0x04000000 | IMEM_START)
|
||||
#define DMEM_END_VA (0x04000000 | DMEM_END)
|
||||
#define IMEM_END_VA (0x04000000 | IMEM_END)
|
||||
|
||||
/* The first bytes of IMEM are reserved so that rspboot has space for
|
||||
* code that will load the new IMEM contents for the target ucode.
|
||||
*/
|
||||
#define RSPBOOT_RESERVED_IMEM 0x80
|
||||
|
||||
/* Entrypoint for ucodes loaded with rspboot */
|
||||
#define RSPBOOT_ENTRYPOINT (IMEM_START + RSPBOOT_RESERVED_IMEM)
|
||||
#define RSPBOOT_ENTRYPOINT_VA (0x04000000 | RSPBOOT_ENTRYPOINT)
|
||||
|
||||
/* OSTask structure */
|
||||
#define OSTASK_ADDR (DMEM_END - OS_TASK_SIZE)
|
||||
|
||||
#endif
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @file rspboot.s
|
||||
*
|
||||
* This RSP microcode program acts as a loader for other microcodes. This ucode:
|
||||
* - (Optionally) Waits for the RDP to go idle
|
||||
* - Loads a ucode's .data section to the start of DMEM
|
||||
* - Loads a ucode's .text section to RSPBOOT_ENTRYPOINT
|
||||
* - Jumps to the loaded ucode
|
||||
*
|
||||
* Along the way it also checks whether the task scheduler running on the CPU has
|
||||
* requested a yield and, if so, halts the RSP.
|
||||
*/
|
||||
.rsp
|
||||
#include "rcp.h"
|
||||
#include "sptask.h"
|
||||
#include "rspboot.h"
|
||||
|
||||
// $1 is expected to contain the OSTask pointer by many ucodes
|
||||
OSTask_reg equ $1
|
||||
|
||||
.create CODE_FILE, IMEM_START_VA
|
||||
|
||||
entry:
|
||||
j start
|
||||
addi OSTask_reg, $zero, OSTASK_ADDR
|
||||
|
||||
load_ucode_text_and_enter:
|
||||
// Read the ucode .text DRAM address from the OSTask
|
||||
lw $2, OS_TASK_OFF_UCODE(OSTask_reg)
|
||||
// Always DMA as much as will fit in IMEM to RSPBOOT_ENTRYPOINT
|
||||
addi $3, $zero, (IMEM_SIZE - (RSPBOOT_ENTRYPOINT - IMEM_START)) - 1
|
||||
addi $7, $zero, RSPBOOT_ENTRYPOINT
|
||||
// Start the DMA, assuming it's not busy having waited for it while
|
||||
// loading the ucode .data
|
||||
mtc0 $7, SP_MEM_ADDR
|
||||
mtc0 $2, SP_DRAM_ADDR
|
||||
mtc0 $3, SP_RD_LEN
|
||||
// Wait for .text to load
|
||||
@@while_dma_busy:
|
||||
mfc0 $4, SP_DMA_BUSY
|
||||
bnez $4, @@while_dma_busy
|
||||
nop
|
||||
// Check yield one more time
|
||||
jal check_yield
|
||||
nop
|
||||
// Release the semaphore just in case a prior task or the CPU failed to release it
|
||||
// and jump to the ucode that was loaded
|
||||
jr $7
|
||||
mtc0 $zero, SP_SEMAPHORE
|
||||
|
||||
check_yield:
|
||||
// Check the YIELD bit in SP_STATUS: if it is set enter yield_break,
|
||||
// otherwise return
|
||||
mfc0 $8, SP_STATUS
|
||||
andi $8, $8, SP_STATUS_YIELD
|
||||
bnez $8, yield_break
|
||||
nop
|
||||
// No need to yield, return to caller
|
||||
jr $ra
|
||||
yield_break:
|
||||
// Release the semaphore (note this happens even if there is no yield as it
|
||||
// is in the delay slot of the above instruction)
|
||||
mtc0 $zero, SP_SEMAPHORE
|
||||
// Update the RSP status to signal task complete and yielded, clear the
|
||||
// yield request
|
||||
li $8, (SP_SET_TASKDONE | SP_SET_YIELDED | SP_CLR_YIELD)
|
||||
mtc0 $8, SP_STATUS
|
||||
// Halt the RSP
|
||||
break
|
||||
nop
|
||||
|
||||
// Everything up to here needs to end before RSPBOOT_ENTRYPOINT so it's not overwritten by the .text loader
|
||||
.if . > RSPBOOT_ENTRYPOINT_VA
|
||||
.error "Not enough reserved IMEM for .text bootstrapper"
|
||||
.endif
|
||||
|
||||
start:
|
||||
// Check the DP_WAIT flag, if not set branch straight to loading ucode .data
|
||||
lw $2, OS_TASK_OFF_FLAGS(OSTask_reg)
|
||||
andi $2, $2, OS_TASK_DP_WAIT
|
||||
beqz $2, load_ucode_data
|
||||
nop
|
||||
// If the flag is set, we need to wait for the RDP to become idle just in case
|
||||
// it's still reading commands from DMEM in XBUS mode. First check if we need
|
||||
// to yield to another task.
|
||||
jal check_yield
|
||||
nop
|
||||
// Now poll RDP DMA busy
|
||||
mfc0 $2, DPC_STATUS
|
||||
andi $2, $2, DPC_STATUS_DMA_BUSY
|
||||
// If the RDP is still busy, check yield again. This will return to the same
|
||||
// return address as the above jal since $ra has not been clobbered since then.
|
||||
bgtz $2, check_yield
|
||||
nop
|
||||
load_ucode_data:
|
||||
// Load the DRAM address and size of the ucode .data from the OSTask
|
||||
lw $2, OS_TASK_OFF_UDATA(OSTask_reg)
|
||||
lw $3, OS_TASK_OFF_UDATA_SZ(OSTask_reg)
|
||||
addi $3, $3, -1
|
||||
// Wait for a DMA slot to free
|
||||
@@while_dma_full:
|
||||
mfc0 $30, SP_DMA_FULL
|
||||
bnez $30, @@while_dma_full
|
||||
nop
|
||||
// Submit the DMA transfer for the ucode .data, targeting the start of DMEM
|
||||
mtc0 $zero, SP_MEM_ADDR
|
||||
mtc0 $2, SP_DRAM_ADDR
|
||||
mtc0 $3, SP_RD_LEN
|
||||
// Wait for the transfer to complete
|
||||
@@while_dma_busy:
|
||||
mfc0 $4, SP_DMA_BUSY
|
||||
bnez $4, @@while_dma_busy
|
||||
nop
|
||||
// Check whether we need to yield, does not return if we do
|
||||
jal check_yield
|
||||
nop
|
||||
// Jump to ucode .text loader
|
||||
j load_ucode_text_and_enter
|
||||
nop
|
||||
|
||||
.align 0x10
|
||||
|
||||
.if . > IMEM_END_VA
|
||||
.error "Not enough room in IMEM"
|
||||
.endif
|
||||
|
||||
.close
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* @file rspboot_ap.s
|
||||
*
|
||||
* This RSP microcode program acts as a loader for other microcodes. This ucode:
|
||||
* - (Optionally) Waits for the RDP to go idle
|
||||
* - Loads a ucode's .data section to the start of DMEM
|
||||
* - Loads a ucode's .text section to RSPBOOT_ENTRYPOINT
|
||||
* - Jumps to the loaded ucode
|
||||
*
|
||||
* Along the way it also checks whether the task scheduler running on the CPU has
|
||||
* requested a yield and, if so, halts the RSP.
|
||||
*
|
||||
* This is a special variant of rspboot that is designed to work alongside IPL3 X105
|
||||
* and the cic6105 microcode to attempt to detect whether the running game is a pirated
|
||||
* copy. If it is so determined (in the antipiracy_test routine) cic6105 will not set
|
||||
* signal 7 in SP_STATUS, causing subsequent runs of rspboot to corrupt random regions
|
||||
* of memory (see the dpclock_corrupt routine)
|
||||
*/
|
||||
.rsp
|
||||
#include "rcp.h"
|
||||
#include "sptask.h"
|
||||
#include "rspboot.h"
|
||||
|
||||
#define OSTASK_FIELD(field) (-0x40 + OS_TASK_OFF_##field)($zero)
|
||||
|
||||
.create CODE_FILE, IMEM_START
|
||||
|
||||
entry:
|
||||
// This jump is overwritten once rspboot runs for the first time (see set_status_and_patch below)
|
||||
j antipiracy_test
|
||||
lw $17, OSTASK_FIELD(FLAGS)
|
||||
|
||||
load_ucode_text_and_enter:
|
||||
li $ra, RSPBOOT_ENTRYPOINT
|
||||
li $1, (IMEM_SIZE - (RSPBOOT_ENTRYPOINT - IMEM_START))-1
|
||||
mtc0 $16, SP_DRAM_ADDR
|
||||
mtc0 $ra, SP_MEM_ADDR
|
||||
mtc0 $1, SP_RD_LEN
|
||||
@@while_dma_busy:
|
||||
mfc0 $1, SP_DMA_BUSY
|
||||
bnez $1, @@while_dma_busy
|
||||
check_yield:
|
||||
mfc0 $1, SP_STATUS
|
||||
andi $1, $1, SP_STATUS_YIELD
|
||||
bnez $1, yield_break
|
||||
li $1, OSTASK_ADDR // $1 is expected to contain the OSTask pointer by many ucodes
|
||||
jr $ra
|
||||
|
||||
yield_break:
|
||||
li $17, (SP_SET_SIG2 | SP_SET_SIG1 | SP_CLR_SIG0)
|
||||
mtc0 $17, SP_STATUS
|
||||
break
|
||||
|
||||
antipiracy_test:
|
||||
// Checks that $4, $5, $6 and $11 all have expected register values set by IPL3 X105
|
||||
lui $1, (SP_CLR_SIG7 >> 16)
|
||||
li $3, 0x3D8
|
||||
bne $11, $3, set_status_and_patch
|
||||
addi $16, $zero, entry_failure
|
||||
lui $3, 0x3A0
|
||||
ori $3, $3, 0x4820
|
||||
bne $4, $3, set_status_and_patch
|
||||
lui $3, 0x2529
|
||||
ori $3, $3, 0x4
|
||||
bne $6, $3, set_status_and_patch
|
||||
srl $3, $5, 12
|
||||
addi $2, $3, -76
|
||||
bltz $2, set_status_and_patch
|
||||
addi $2, $3, -79
|
||||
bgtz $2, set_status_and_patch
|
||||
vxor $v13, $v13, $v13[1q]
|
||||
vaddc $v13, $v13, $v13[2h]
|
||||
vnxor $v13, $v13, $v13[4]
|
||||
mfc2 $3, $v13[0]
|
||||
xori $3, $3, 0x1234 // $3 is calculated and then never used before it is repurposed
|
||||
nop
|
||||
lui $1, (SP_SET_SIG7 >> 16)
|
||||
addi $16, $zero, entry_success
|
||||
set_status_and_patch:
|
||||
mtc0 $1, SP_STATUS
|
||||
lw $1, OSTASK_FIELD(UBOOT)
|
||||
// fallthrough to dma write 8 bytes, patches the first 8 bytes of the ucode in dram
|
||||
// with a new jump target that avoids the DPC_CLOCK code if SP_STATUS_SIG7 is set
|
||||
|
||||
dma_write_8:
|
||||
@@while_dma_full:
|
||||
mfc0 $3, SP_DMA_FULL
|
||||
bne $3, $3, @@while_dma_full
|
||||
nop
|
||||
mtc0 $16, SP_MEM_ADDR
|
||||
mtc0 $1, SP_DRAM_ADDR
|
||||
j dp_wait
|
||||
mtc0 $zero, SP_WR_LEN // 0 = 8-byte write to RDRAM
|
||||
|
||||
.align 8
|
||||
entry_success:
|
||||
// antipiracy check passed, rspboot is patched to check signal 7 instead of performing the check again
|
||||
j check_sig7
|
||||
lw $17, OSTASK_FIELD(FLAGS)
|
||||
|
||||
.align 8
|
||||
entry_failure:
|
||||
// antipiracy check failed, rspboot is patched to run the failure codepath until reset
|
||||
j dpclock_corrupt
|
||||
lw $17, OSTASK_FIELD(FLAGS)
|
||||
|
||||
check_sig7: // if signal 7 is set, skip DPC_CLOCK sampling + corruption
|
||||
mfc0 $16, SP_STATUS
|
||||
andi $16, $16, SP_STATUS_SIG7
|
||||
bnez $16, dp_wait
|
||||
dpclock_corrupt:
|
||||
mfc0 $16, DPC_CLOCK
|
||||
andi $1, $16, 0x1FFF
|
||||
bnez $1, @@stepover // if the 13 low-order bits of the sampled clock are 0, set DPC_END to DPC_CLOCK
|
||||
andi $1, $16, 0x7F
|
||||
mtc0 $16, DPC_END
|
||||
@@stepover:
|
||||
beqz $1, dma_write_8 // if the 7 low-order bits of the sampled clock are 0, corrupt 8 random bytes of DRAM
|
||||
srl $1, $16, 2
|
||||
dp_wait:
|
||||
sll $17, $17, (32 - OS_TASK_DP_WAIT)
|
||||
bgezal $17, load_ucode_data
|
||||
lw $16, OSTASK_FIELD(UDATA_SZ)
|
||||
mfc0 $1, DPC_STATUS
|
||||
andi $1, $1, DPC_STATUS_DMA_BUSY
|
||||
bnez $1, check_yield // yield check while waiting for RDP Command FIFO to become idle
|
||||
|
||||
load_ucode_data:
|
||||
@@while_dma_full:
|
||||
mfc0 $1, SP_DMA_FULL
|
||||
bnez $1, @@while_dma_full
|
||||
lw $1, OSTASK_FIELD(UDATA)
|
||||
addi $16, $16, -1
|
||||
mtc0 $zero, SP_MEM_ADDR
|
||||
mtc0 $1, SP_DRAM_ADDR
|
||||
mtc0 $16, SP_RD_LEN
|
||||
@@while_dma_busy:
|
||||
mfc0 $1, SP_DMA_BUSY
|
||||
bnez $1, @@while_dma_busy
|
||||
li $ra, load_ucode_text_and_enter
|
||||
mtc0 $zero, SP_SEMAPHORE // Release the semaphore just in case a prior task or the CPU failed to release it
|
||||
vxor $v16, $v16, $v16 // set $v16 to 0, some F3DZEX versions rely on this being set to function properly
|
||||
sw $4, OSTASK_FIELD(UBOOT)
|
||||
j check_yield
|
||||
lw $16, OSTASK_FIELD(UCODE)
|
||||
|
||||
.align 0x10
|
||||
|
||||
.if . > IMEM_END
|
||||
.error "Not enough room in IMEM"
|
||||
.endif
|
||||
|
||||
.close
|
||||
@@ -85,7 +85,11 @@ beginseg
|
||||
include "$(BUILD_DIR)/src/boot/build.o"
|
||||
|
||||
// RSP microcode
|
||||
include "$(BUILD_DIR)/data/rspboot.o"
|
||||
#if PLATFORM_N64
|
||||
include "$(BUILD_DIR)/rsp/rspboot_ap.o"
|
||||
#else
|
||||
include "$(BUILD_DIR)/rsp/rspboot.o"
|
||||
#endif
|
||||
include "$(BUILD_DIR)/data/cic6105.o"
|
||||
|
||||
// Extra files for non-matching debug builds
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Output files
|
||||
*.exe
|
||||
armips
|
||||
bin2c
|
||||
mkdmadata
|
||||
mkldscript
|
||||
|
||||
+15
-7
@@ -1,4 +1,3 @@
|
||||
CFLAGS := -Wall -Wextra -pedantic -std=gnu99 -g -O2
|
||||
PROGRAMS := bin2c mkdmadata mkldscript mkspecrules preprocess_pragmas vtxdis
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
@@ -18,10 +17,16 @@ EGCS_BINUTILS_VERSION := 0.6
|
||||
EGCS_GCC_VERSION := 0.7
|
||||
EGCS_DIR := egcs/$(DETECTED_OS)
|
||||
|
||||
WARNFLAGS := -Wall -Wextra -pedantic
|
||||
OPTFLAGS := -O2 -g
|
||||
LDFLAGS :=
|
||||
|
||||
ifeq ($(shell command -v clang >/dev/null 2>&1; echo $$?),0)
|
||||
CC := clang
|
||||
CXX := clang++
|
||||
else
|
||||
CC := gcc
|
||||
CXX := g++
|
||||
endif
|
||||
|
||||
LLD ?= 0
|
||||
@@ -31,10 +36,12 @@ ifeq ($(shell command -v ld.lld >/dev/null 2>&1; echo $$?),0)
|
||||
endif
|
||||
|
||||
ifneq ($(LLD),0)
|
||||
CFLAGS += -fuse-ld=lld
|
||||
LDFLAGS += -fuse-ld=lld
|
||||
endif
|
||||
|
||||
all: $(PROGRAMS) $(IDO_RECOMP_5_3_DIR) $(IDO_RECOMP_7_1_DIR) $(EGCS_DIR)
|
||||
.PHONY: all clean distclean
|
||||
|
||||
all: $(PROGRAMS) armips $(IDO_RECOMP_5_3_DIR) $(IDO_RECOMP_7_1_DIR) $(EGCS_DIR)
|
||||
$(MAKE) -C fado
|
||||
$(MAKE) -C audio
|
||||
$(MAKE) -C com-plugin
|
||||
@@ -52,8 +59,10 @@ clean:
|
||||
distclean: clean
|
||||
$(MAKE) -C audio distclean
|
||||
$(MAKE) -C assets distclean
|
||||
$(RM) armips
|
||||
|
||||
.PHONY: all clean distclean
|
||||
armips: armips.cpp
|
||||
$(CXX) $(WARNFLAGS) -std=c++17 $(OPTFLAGS) -s -fno-rtti -pipe -Wno-unused-parameter -Wno-sign-compare $< -o $@
|
||||
|
||||
bin2c_SOURCES := bin2c.c
|
||||
mkdmadata_SOURCES := mkdmadata.c spec.c util.c
|
||||
@@ -62,10 +71,9 @@ mkspecrules_SOURCES := mkspecrules.c spec.c util.c
|
||||
preprocess_pragmas_SOURCES := preprocess_pragmas.c
|
||||
vtxdis_SOURCES := vtxdis.c
|
||||
|
||||
|
||||
define COMPILE =
|
||||
define COMPILE
|
||||
$(1): $($1_SOURCES)
|
||||
$(CC) $(CFLAGS) $$^ -o $$@
|
||||
$(CC) $(WARNFLAGS) -std=gnu99 $(OPTFLAGS) $$^ $(LDFLAGS) -o $$@
|
||||
endef
|
||||
|
||||
$(foreach p,$(PROGRAMS),$(eval $(call COMPILE,$(p))))
|
||||
|
||||
+20859
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
diff --git a/Commands/CAssemblerLabel.cpp b/Commands/CAssemblerLabel.cpp
|
||||
index 3ca67dd..50ea7ae 100644
|
||||
--- a/Commands/CAssemblerLabel.cpp
|
||||
+++ b/Commands/CAssemblerLabel.cpp
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "Commands/CAssemblerLabel.h"
|
||||
|
||||
-#include "Archs/ARM/Arm.h"
|
||||
#include "Core/Common.h"
|
||||
#include "Core/FileManager.h"
|
||||
#include "Core/Misc.h"
|
||||
@@ -26,12 +25,7 @@ CAssemblerLabel::CAssemblerLabel(const Identifier& name, const Identifier& origi
|
||||
|
||||
// does this need to be in validate?
|
||||
if (label->getUpdateInfo())
|
||||
- {
|
||||
- if (&Architecture::current() != &Arm)
|
||||
- label->setInfo(0);
|
||||
- else
|
||||
- label->setInfo(thumbMode.value_or(Arm.GetThumbMode()));
|
||||
- }
|
||||
+ label->setInfo(0);
|
||||
}
|
||||
|
||||
CAssemblerLabel::CAssemblerLabel(const Identifier& name, const Identifier& originalName, Expression& value, std::optional<bool> thumbMode)
|
||||
diff --git a/Commands/CDirectiveConditional.cpp b/Commands/CDirectiveConditional.cpp
|
||||
index e2177bd..1170247 100644
|
||||
--- a/Commands/CDirectiveConditional.cpp
|
||||
+++ b/Commands/CDirectiveConditional.cpp
|
||||
@@ -1,12 +1,9 @@
|
||||
#include "Commands/CDirectiveConditional.h"
|
||||
|
||||
-#include "Archs/ARM/Arm.h"
|
||||
#include "Core/Common.h"
|
||||
#include "Core/Misc.h"
|
||||
#include "Util/Util.h"
|
||||
|
||||
-extern CArmArchitecture Arm;
|
||||
-
|
||||
CDirectiveConditional::CDirectiveConditional(ConditionType type)
|
||||
{
|
||||
this->type = type;
|
||||
diff --git a/Core/Assembler.cpp b/Core/Assembler.cpp
|
||||
index b1d006c..51d9c49 100644
|
||||
--- a/Core/Assembler.cpp
|
||||
+++ b/Core/Assembler.cpp
|
||||
@@ -1,11 +1,7 @@
|
||||
#include "Core/Assembler.h"
|
||||
|
||||
-#include "Archs/ARM/Arm.h"
|
||||
-#include "Archs/ARM/ArmExpressionFunctions.h"
|
||||
#include "Archs/MIPS/Mips.h"
|
||||
#include "Archs/MIPS/MipsExpressionFunctions.h"
|
||||
-#include "Archs/SuperH/SuperH.h"
|
||||
-#include "Archs/SuperH/ShExpressionFunctions.h"
|
||||
#include "Commands/CAssemblerCommand.h"
|
||||
#include "Core/Allocations.h"
|
||||
#include "Core/Common.h"
|
||||
@@ -21,9 +17,7 @@ bool encodeAssembly(std::unique_ptr<CAssemblerCommand> content, SymbolData& symD
|
||||
{
|
||||
bool Revalidate;
|
||||
|
||||
- Arm.Pass2();
|
||||
Mips.Pass2();
|
||||
- SuperH.Pass2();
|
||||
|
||||
ValidateState validation;
|
||||
do // loop until everything is constant
|
||||
@@ -50,9 +44,7 @@ bool encodeAssembly(std::unique_ptr<CAssemblerCommand> content, SymbolData& symD
|
||||
|
||||
Revalidate = content->Validate(validation);
|
||||
|
||||
- Arm.Revalidate();
|
||||
Mips.Revalidate();
|
||||
- SuperH.Revalidate();
|
||||
|
||||
if (Global.memoryMode)
|
||||
g_fileManager->closeFile();
|
||||
@@ -162,8 +154,6 @@ bool runArmips(ArmipsArguments& settings)
|
||||
Global.FileInfo.LineNumber = 0;
|
||||
Global.FileInfo.FileNum = 0;
|
||||
|
||||
- Arm.clear();
|
||||
-
|
||||
ExpressionFunctionHandler::instance().reset();
|
||||
registerExpressionFunctions(ExpressionFunctionHandler::instance());
|
||||
|
||||
diff --git a/Parser/DirectivesParser.cpp b/Parser/DirectivesParser.cpp
|
||||
index d20d401..483d18e 100644
|
||||
--- a/Parser/DirectivesParser.cpp
|
||||
+++ b/Parser/DirectivesParser.cpp
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "Parser/DirectivesParser.h"
|
||||
|
||||
-#include "Archs/ARM/Arm.h"
|
||||
#include "Archs/MIPS/Mips.h"
|
||||
-#include "Archs/SuperH/SuperH.h"
|
||||
#include "Commands/CAssemblerLabel.h"
|
||||
#include "Commands/CDirectiveArea.h"
|
||||
#include "Commands/CDirectiveConditional.h"
|
||||
@@ -432,51 +430,6 @@ std::unique_ptr<CAssemblerCommand> parseDirectiveMipsArch(Parser& parser, int fl
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
-std::unique_ptr<CAssemblerCommand> parseDirectiveArmArch(Parser& parser, int flags)
|
||||
-{
|
||||
- Architecture::setCurrent(Arm);
|
||||
-
|
||||
- switch (flags)
|
||||
- {
|
||||
- case DIRECTIVE_ARM_GBA:
|
||||
- Arm.SetThumbMode(true);
|
||||
- Arm.setVersion(AARCH_GBA);
|
||||
- return std::make_unique<ArchitectureCommand>(".gba\n.thumb", ".thumb");
|
||||
- case DIRECTIVE_ARM_NDS:
|
||||
- Arm.SetThumbMode(false);
|
||||
- Arm.setVersion(AARCH_NDS);
|
||||
- return std::make_unique<ArchitectureCommand>(".nds\n.arm", ".arm");
|
||||
- case DIRECTIVE_ARM_3DS:
|
||||
- Arm.SetThumbMode(false);
|
||||
- Arm.setVersion(AARCH_3DS);
|
||||
- return std::make_unique<ArchitectureCommand>(".3ds\n.arm", ".arm");
|
||||
- case DIRECTIVE_ARM_BIG:
|
||||
- Arm.SetThumbMode(false);
|
||||
- Arm.setVersion(AARCH_BIG);
|
||||
- return std::make_unique<ArchitectureCommand>(".arm.big\n.arm", ".arm");
|
||||
- case DIRECTIVE_ARM_LITTLE:
|
||||
- Arm.SetThumbMode(false);
|
||||
- Arm.setVersion(AARCH_LITTLE);
|
||||
- return std::make_unique<ArchitectureCommand>(".arm.little\n.arm", ".arm");
|
||||
- }
|
||||
-
|
||||
- return nullptr;
|
||||
-}
|
||||
-
|
||||
-std::unique_ptr<CAssemblerCommand> parseDirectiveShArch(Parser& parser, int flags)
|
||||
-{
|
||||
- Architecture::setCurrent(SuperH);
|
||||
-
|
||||
- switch (flags)
|
||||
- {
|
||||
- case DIRECTIVE_SH_SATURN:
|
||||
- SuperH.setVersion(SHARCH_SATURN);
|
||||
- return std::make_unique<ArchitectureCommand>(".saturn", "");
|
||||
- }
|
||||
-
|
||||
- return nullptr;
|
||||
-}
|
||||
-
|
||||
std::unique_ptr<CAssemblerCommand> parseDirectiveArea(Parser& parser, int flags)
|
||||
{
|
||||
std::vector<Expression> parameters;
|
||||
@@ -820,15 +773,6 @@ const DirectiveMap directives = {
|
||||
{ ".n64", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_N64 } },
|
||||
{ ".rsp", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_RSP } },
|
||||
|
||||
- { ".gba", { &parseDirectiveArmArch, DIRECTIVE_ARM_GBA } },
|
||||
- { ".nds", { &parseDirectiveArmArch, DIRECTIVE_ARM_NDS } },
|
||||
- { ".3ds", { &parseDirectiveArmArch, DIRECTIVE_ARM_3DS } },
|
||||
- { ".arm.big", { &parseDirectiveArmArch, DIRECTIVE_ARM_BIG } },
|
||||
- { ".arm.little", { &parseDirectiveArmArch, DIRECTIVE_ARM_LITTLE } },
|
||||
-
|
||||
- { ".saturn", { &parseDirectiveShArch, DIRECTIVE_SH_SATURN } },
|
||||
- { ".32x", { &parseDirectiveShArch, DIRECTIVE_SH_SATURN } },
|
||||
-
|
||||
{ ".area", { &parseDirectiveArea, 0 } },
|
||||
{ ".autoregion", { &parseDirectiveAutoRegion, 0 } },
|
||||
{ ".region", { &parseDirectiveArea, DIRECTIVE_AREA_SHARED } },
|
||||
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-FileCopyrightText: © 2026 ZeldaRET
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Create a single-file armips assembler from an upstream commit, for MIPS only.
|
||||
# Changing the target commit may require updating
|
||||
# - armips.diff
|
||||
# - the included file list
|
||||
# - the line filters in filter_lines
|
||||
#
|
||||
|
||||
import contextlib, io, os
|
||||
|
||||
|
||||
ARMIPS_URL = "https://github.com/kingcom/armips"
|
||||
ARMIPS_COMMIT = "9029577f6448c78dadcffbc876d2b8cbcfc6da9b"
|
||||
ARMIPS_MAJOR_VER = "v0.11"
|
||||
ARMIPS_PATCH = "armips.diff"
|
||||
ARMIPS_OUTPUT = "armips.cpp"
|
||||
|
||||
|
||||
file_list = (
|
||||
"Util/FileSystem.h",
|
||||
"ext/tinyformat/tinyformat.h",
|
||||
"Core/Types.h",
|
||||
"Core/Types.cpp",
|
||||
"Commands/CAssemblerCommand.h",
|
||||
"Core/Expression.h",
|
||||
"Parser/Tokenizer.h",
|
||||
"Core/ExpressionFunctionHandler.h",
|
||||
"Core/ExpressionFunctions.h",
|
||||
"Core/SymbolData.h",
|
||||
"Util/Util.h",
|
||||
"Util/FileClasses.h",
|
||||
"Util/ByteArray.h",
|
||||
"Core/FileManager.h",
|
||||
"Core/ELF/ElfTypes.h",
|
||||
"Core/ELF/ElfFile.h",
|
||||
"Core/ELF/ElfRelocator.h",
|
||||
"Archs/Architecture.h",
|
||||
"Archs/MIPS/Mips.h",
|
||||
"Archs/MIPS/MipsOpcodes.h",
|
||||
"Archs/MIPS/CMipsInstruction.h",
|
||||
"Util/EncodingTable.h",
|
||||
"Core/Misc.h",
|
||||
"Core/Assembler.h",
|
||||
"Core/SymbolTable.h",
|
||||
"Core/Common.h",
|
||||
"Core/Allocations.h",
|
||||
"Core/Allocations.cpp",
|
||||
"Parser/DirectivesParser.h",
|
||||
"Archs/MIPS/MipsMacros.h",
|
||||
"Archs/MIPS/MipsParser.h",
|
||||
"Archs/MIPS/CMipsInstruction.cpp",
|
||||
"Archs/MIPS/MipsExpressionFunctions.h",
|
||||
"Archs/MIPS/MipsElfRelocator.h",
|
||||
"Archs/MIPS/Mips.cpp",
|
||||
"Archs/MIPS/MipsElfFile.h",
|
||||
"Util/CRC.h",
|
||||
"Archs/MIPS/MipsElfFile.cpp",
|
||||
"Commands/CommandSequence.h",
|
||||
"Parser/Parser.h",
|
||||
"Archs/MIPS/MipsElfRelocator.cpp",
|
||||
"Archs/MIPS/MipsExpressionFunctions.cpp",
|
||||
"Archs/MIPS/MipsMacros.cpp",
|
||||
"Archs/MIPS/MipsOpcodes.cpp",
|
||||
"Parser/ExpressionParser.h",
|
||||
"Core/ExpressionFunctionHandler.cpp",
|
||||
"Archs/MIPS/PsxRelocator.h",
|
||||
"Commands/CDirectiveFile.h",
|
||||
"Archs/MIPS/MipsParser.cpp",
|
||||
"Archs/MIPS/PsxRelocator.cpp",
|
||||
"Archs/Architecture.cpp",
|
||||
"Commands/CAssemblerCommand.cpp",
|
||||
"Commands/CAssemblerLabel.h",
|
||||
"Commands/CAssemblerLabel.cpp",
|
||||
"Commands/CDirectiveArea.h",
|
||||
"Commands/CDirectiveArea.cpp",
|
||||
"Commands/CDirectiveConditional.h",
|
||||
"Commands/CDirectiveConditional.cpp",
|
||||
"Commands/CDirectiveData.h",
|
||||
"Commands/CDirectiveData.cpp",
|
||||
"Commands/CDirectiveFile.cpp",
|
||||
"Commands/CDirectiveMessage.h",
|
||||
"Commands/CDirectiveMessage.cpp",
|
||||
"Commands/CommandSequence.cpp",
|
||||
"Parser/DirectivesParser.cpp",
|
||||
"Parser/ExpressionParser.cpp",
|
||||
"Parser/Parser.cpp",
|
||||
"Parser/Tokenizer.cpp",
|
||||
"Util/ByteArray.cpp",
|
||||
"Util/CRC.cpp",
|
||||
"Util/EncodingTable.cpp",
|
||||
"Util/FileClasses.cpp",
|
||||
"Util/Util.cpp",
|
||||
"Main/CommandLineInterface.h",
|
||||
"Main/CommandLineInterface.cpp",
|
||||
"Core/ELF/ElfFile.cpp",
|
||||
"Core/ELF/ElfRelocator.cpp",
|
||||
"Core/Assembler.cpp",
|
||||
"Core/Common.cpp",
|
||||
"Core/Expression.cpp",
|
||||
"Core/ExpressionFunctions.cpp",
|
||||
"Core/FileManager.cpp",
|
||||
"Core/Misc.cpp",
|
||||
"Core/SymbolData.cpp",
|
||||
"Core/SymbolTable.cpp",
|
||||
"Main/main.cpp",
|
||||
)
|
||||
|
||||
|
||||
file_header = \
|
||||
f"""// == AUTOMATICALLY GENERATED by armips_gen.py, do not edit directly ==
|
||||
// armips assembler {ARMIPS_MAJOR_VER} (commit {ARMIPS_COMMIT})
|
||||
// {ARMIPS_URL}
|
||||
// To simplify compilation, all files have been concatenated into one.
|
||||
// MIPS only, other architectures not included.\n\n"""
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def working_dir(x: str):
|
||||
old = os.getcwd()
|
||||
os.chdir(x)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(old)
|
||||
|
||||
|
||||
def filter_lines(line: str):
|
||||
blacklist = (
|
||||
"#pragma once",
|
||||
"#include \"",
|
||||
"#include <tinyformat.h>"
|
||||
)
|
||||
return not any(s in line for s in blacklist)
|
||||
|
||||
|
||||
def cat_file(fout: io.TextIOBase, fin_name: str):
|
||||
with open(fin_name) as fin:
|
||||
lines = fin.readlines()
|
||||
lines = map(str.rstrip, filter(filter_lines, lines))
|
||||
fout.write("\n".join(lines) + "\n")
|
||||
|
||||
|
||||
def combine_armips(fout_name: str, armips_path: str):
|
||||
with open(fout_name, "w") as fout:
|
||||
fout.write(file_header)
|
||||
fout.write("/*\n")
|
||||
cat_file(fout, os.path.join(armips_path, "LICENSE.txt"))
|
||||
fout.write("*/\n\n")
|
||||
fout.write("#define ARMIPS_USE_STD_FILESYSTEM\n")
|
||||
for f in file_list:
|
||||
fout.write(f"\n#pragma region \"File: {f}\"\n")
|
||||
cat_file(fout, os.path.join(armips_path, f))
|
||||
|
||||
|
||||
def main():
|
||||
with working_dir(os.path.dirname(os.path.realpath(__file__))):
|
||||
temp_dir = "armips_clone"
|
||||
# Acquire the base armips commit
|
||||
os.system(f"rm -rf {temp_dir}")
|
||||
os.system(f"git init {temp_dir} 2>/dev/null")
|
||||
with working_dir(temp_dir):
|
||||
os.system(f"git remote add origin {ARMIPS_URL}.git")
|
||||
os.system(f"git fetch origin --depth 1 {ARMIPS_COMMIT}")
|
||||
os.system(f"git checkout FETCH_HEAD 2>/dev/null")
|
||||
# Apply patch
|
||||
os.system(f"git apply ../{ARMIPS_PATCH}")
|
||||
# Concat all included files
|
||||
print(f"Creating {ARMIPS_OUTPUT}")
|
||||
combine_armips(ARMIPS_OUTPUT, os.path.expanduser(temp_dir))
|
||||
os.system(f"rm -rf {temp_dir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user