From 0e17e13c909910a18b2bbd76d2f43dc8af581158 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Thu, 6 Apr 2023 10:10:16 +0200 Subject: [PATCH] [arm] Fix I/O write epilogue This fixes many games and brings it closer to what the other dynarecs do. Without this patch there's register corruption if the memory write triggers an IRQ (since raise_interrupt mangles the registers that have not been saved). At this point there's still an issue with CPSR saving but that affects aso the other dynarecs. --- arm/arm_stub.S | 63 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/arm/arm_stub.S b/arm/arm_stub.S index a5beeda..3d536b4 100644 --- a/arm/arm_stub.S +++ b/arm/arm_stub.S @@ -504,8 +504,17 @@ ext_io_store_u##store_type: ;\ save_flags() ;\ ldr r2, [lr] /* load PC */;\ str r2, [reg_base, #REG_PC] /* write out PC */;\ - store_align_##store_type() ;\ - call_c_function(write_memory##store_type) ;\ + ;\ + ldr r2, [reg_base, #REG_CPSR] /* load CPSR */;\ + tst r2, #0x20 /* check Thumb bit is set */;\ + bne 1f /* Store arm/thumb regs */;\ + store_registers_arm() ;\ + b 2f ;\ +1: ;\ + store_registers_thumb() ;\ +2: ;\ + mask_addr_##store_type(10) /* Mask to IO memory (+align) */;\ + call_c_function(write_io_register##store_type) ;\ b write_epilogue /* handle additional write stuff */;\ ;\ ext_store_iwram_u##store_type: ;\ @@ -669,23 +678,17 @@ ext_store_oam_ram_u32_safe: write_epilogue: - cmp r0, #0 @ check if the write rose an alert - beq 4f @ if not we can exit + cmp r0, #0 @ if nothing happened, we can resume + beq no_alert - collapse_flags(r1) @ interrupt needs current flags + cmp r0, #3 @ check if the write rose an alert + beq irq_alert @ triggered an IRQ, go execute it - cmp r0, #2 @ see if the alert is due to SMC + cmp r0, #2 @ see if the alert is due to SMC (via DMA) beq smc_write @ if so, goto SMC handler - ldr r1, [reg_base, #REG_CPSR] @ r1 = cpsr - tst r1, #0x20 @ see if Thumb bit is set - bne 1f @ if so do Thumb update - - store_registers_arm() @ save ARM registers - b alert_loop - -1: - store_registers_thumb() @ save Thumb registers + collapse_flags(r1) @ interrupt needs current flags + mvn r0, reg_cycles @ setup for update_gba alert_loop: call_c_function(update_gba) @ update GBA until CPU isn't halted @@ -715,30 +718,46 @@ alert_loop: restore_flags() bx r0 @ jump to new Thumb block -4: + +no_alert: restore_flags() - add pc, lr, #4 @ return + add pc, lr, #4 @ return, skip inlined PC smc_write: call_c_function(flush_translation_cache_ram) -lookup_pc: + @ Resume regular execution (except we might need to recompile due to flush) ldr r0, [reg_base, #REG_PC] @ r0 = new pc ldr r1, [reg_base, #REG_CPSR] @ r1 = flags tst r1, #0x20 @ see if Thumb bit is set - beq lookup_pc_arm @ if not lookup ARM + beq 3f @ if not lookup ARM -lookup_pc_thumb: call_c_function(block_lookup_address_thumb) restore_flags() bx r0 @ jump to new Thumb block - -lookup_pc_arm: +3: call_c_function(block_lookup_address_arm) restore_flags() bx r0 @ jump to new ARM block +irq_alert: + @ Resume regular execution, usually ARM mode, need to reload registers + ldr r0, [reg_base, #REG_PC] @ r0 = new pc + ldr r1, [reg_base, #REG_CPSR] @ r1 = flags + tst r1, #0x20 @ see if Thumb bit is set + beq 4f @ if not lookup ARM + + call_c_function(block_lookup_address_thumb) + restore_flags() + load_registers_thumb() + bx r0 @ jump to new Thumb block +4: + call_c_function(block_lookup_address_arm) + restore_flags() + load_registers_arm() + bx r0 @ jump to new ARM block + #define exec_ld_op_s8(mirrorbits) ;\ mov r0, r0, lsl #(32 - mirrorbits) ;\