diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 506d439..e384ae2 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -21,7 +21,6 @@ const DMA_32_BIT_MEMCPY: DmaControl = DmaControl::new().with_transfer_32bit(true).with_enabled(true); const DMA3_OFFSET: usize = DMA3_SRC.as_usize() - 0x0400_0000; -const IME_OFFSET: usize = IME.as_usize() - 0x0400_0000; const WAITCNT_OFFSET: usize = WAITCNT.as_usize() - 0x0400_0000; // Proc-macros can't see the target being built for, so we use this declarative @@ -125,56 +124,43 @@ core::arch::global_asm! { mgba_logging_enable_request = const MGBA_LOGGING_ENABLE_REQUEST, } +// This handler DOES NOT allow nested interrupts at this time. core::arch::global_asm! { bracer::put_fn_in_section!(".text.gba_rom_header"), ".global __runtime_irq_handler", // On Entry: r0 = 0x0400_0000 (mmio_base) + // We're allowed to use the usual C ABI registers. "__runtime_irq_handler:", force_a32!{ - /* swap IME off, user can turn it back on if they want */ - "add r12, r0, #{ime_offset}", - "mov r3, #0", - "swp r3, r3, [r12]", - - /* Read/Update IE and IF */ - "ldr r0, [r12, #-8]", - "and r0, r0, r0, LSR #16", - "strh r0, [r12, #-6]", - - /* Read/Update BIOS_IF */ - "sub r2, r12, #(0x208+8)", - "ldrh r1, [r2]", - "orr r1, r1, r0", - "strh r1, [r2]", - - /* Call the Rust fn pointer (if set), using System mode */ - "ldr r1, ={RUST_IRQ_HANDLER}", - "ldr r1, [r1]", - bracer::when!(("r1" != "#0")[9] { - // Note(Lokathor): We are *SKIPPING* the part where we ensure that the - // System stack pointer is aligned to 8 during the call to the rust - // function. This is *technically* against the AAPCS ABI, but the GBA's - // ARMv4T CPU does not even support any instructions that require an - // alignment of 8. By not bothering to align the stack, we save about 5 - // cycles total. Which is neat, but if this were on the DS (which has an - // ARMv5TE CPU) you'd want to ensure the aligned stack. - - bracer::a32_read_spsr_to!("r2"), - bracer::a32_set_cpu_control!(System, irq_masked= false, fiq_masked= false), - "push {{r2, r3, r12, lr}}", - bracer::a32_fake_blx!("r1"), - "pop {{r2, r3, r12, lr}}", - bracer::a32_set_cpu_control!(Supervisor, irq_masked= true, fiq_masked= false), - bracer::a32_write_spsr_from!("r2") + /* A fox wizard told me how to do this one */ + // handle MMIO interrupt system + "mov r12, 0x04000000", // load r12 with a 1 cycle value + "ldr r0, [r12, #0x200]!", // load IE_IF with r12 writeback + "and r0, r0, r0, LSR #16", // bits = IE & IF + "strh r0, [r12, #2]", // write16 to just IF + // handle BIOS IntrWait system + "ldr r1, [r12, #-0x208]!", // load BIOS_IF_?? with r12 writeback + "orr r1, r1, r0", // mark `bits` as `has_occurred` + "strh r1, [r12]", // write16 to just BIOS_IF + + // Get the rust code handler fn pointer, call it if non-null. + "ldr r12, ={RUST_IRQ_HANDLER}", + "ldr r12, [r12]", + bracer::when!(("r12" != "#0")[1] { + bracer::a32_read_spsr_to!("r3"), + "push {{r3, lr}}", + bracer::a32_set_cpu_control!(System, irq_masked = true, fiq_masked = true), + bracer::a32_fake_blx!("r12"), + bracer::a32_set_cpu_control!(IRQ, irq_masked = true, fiq_masked = true), + "pop {{r3, lr}}", + bracer::a32_write_spsr_from!("r3"), }), - /* Restore initial IME setting and return */ - "swp r3, r3, [r12]", + // return to the BIOS "bx lr", }, // Define Our Constants - ime_offset = const IME_OFFSET, RUST_IRQ_HANDLER = sym crate::RUST_IRQ_HANDLER, }