Turns out DMA triggers an interrupt and my trainer executes in the exception handler, so it was causing infinite recursion. This code worked:
Code:
.set noreorder
;-------------------------------;
; BEGIN CHEATS ;
;-------------------------------;
; Save load code
.long 0xC0DE0000
.long 0x00000000
LUI S1, 0x800B ;Only load save if it hasn't been loaded
;This read's purpose is explained by
;a write to this address which occurs later in the code
LW S1, 0x6AC4 (S1) ;[0x800B6AC4] == 0?
BEQZ S1, Exit
NOP
LUI S1, 0x8010 ;
LH S2, 0x5304 (S1) ;
XORI S2, S2, 0x0020 ;Check L button; load if only L pressed
BNEZ S2, Exit
NOP
LUI S0, 0xA460 ;0xA4600000
LUI S1, 0x000B ;
ORI S1, S1, 0x6AC8 ;0x000B6AC8; 0x800B6AC8 is where the save will be loaded to
LUI S2, 0x0800 ;0x08000000; Flash base address
;(other saves at save# * 0x4000; 6 in all starting with #0)
ORI S3, R0, 0x0537 ;0x00000537; Saves are 0x537 + 1 bytes in size
;ORI S4, R0, 0x0002 ;0x00000002; this does not need to be written
;to 0xA4600010 like usual because the exception
;handler the game initalizes will do this anyway
;(incidentally this is why this code causes
;a recursion crash without the semaphore
;implemented above)
Wait_Loop:
LW S5, 0x0010 (S0) ;
ANDI S5, S5, 0x0003 ;
BNEZ S5, Wait_Loop
NOP
LUI S4, 0x800B ;This will prevent the exception handler
;from calling this code again recursively
;(this code is executed in the exception handler
;which is triggered when DMA is done,
;such as by writing to 0xA460000C like in this case)
;based on a check performed earlier in this code
SW R0, 0x6AC4 (S4) ;[0x800B6AC4] = 0
SW S1, 0x0000 (S0) ;RAM
SW S2, 0x0004 (S0) ;Hardware
SW S3, 0x000C (S0) ;Size
Exit:
JR RA ;
NOP
.align 3
.long 0xE0000000
.long 0x00000000
; End of save load test
;-------------------------------;
; END OF CHEAT LIST ;
;-------------------------------;
.long 0xFFFFFFFF
.long 0xFFFFFFFF