/*
------------------------------------------------------------------------------------
interrupt.S
andrew p. bilyk
dungeon monkey studios
September 21, 2004
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
Unused registers
------------------------------------------------------------------------------------
r1
r2
r3
r8
r9
r10
r11 = FP (frame pointer)
r12 = IP
r13 = SP (stack pointer)
r15 = PC (program counter)
------------------------------------------------------------------------------------
Used by MainInterruptServiceRoutine
------------------------------------------------------------------------------------
r0 = scratch data
r4 = interrupts_to_be_serviced
r5 = enabled_interrupts
r6 = saved_spsr_irq
r7 = REG_BASE_ADDRESS
r14 = LR (link register)
*/
.section .iwram
.align 2
.global MainInterruptServiceRoutine
.type MainInterruptServiceRoutine,function
@ void MainInterruptServiceRoutine(void)
@----------------------------------------
MainInterruptServiceRoutine:
@ save contents of r4-r7 and link register
@ we'll be using r4-r7, and a function call to an IRQ handler will overwrite lr
stmfd sp!, {r4-r7, lr} @ push r4-r7 & link register
@ store base address used to refer to REG_IE, REG_IF, REG_IME, & ISR_RAM_IF
mov r7, #0x4000000 @ r7 = 0x4000000
add r7, r7, #0x200 @ r7 = REG_BASE_ADDRESS = 0x4000200
@ determine which interrupts to service (enabled_interrupts & requested_interrupts)
ldr r5, [r7] @ r5_lo = enabled_interrupts = REG_IE; r5_hi = requested_interrupts = REG_IF
and r4, r5, r5,lsr#16 @ r4 = interrupts_to_be_serviced = REG_IE & REG_IF
@ acknowledge interrupts_to_be_serviced in REG_IF
strh r4, [r7, #0x2] @ REG_IF = r4 = interrupts_to_be_serviced
@ acknowledge interrupts_to_be_serviced in ISR_RAM_IF
ldr r0, [r7, #-0x208] @ r0 = ISR_RAM_IF
orr r0, r0, r4 @ r0 = ISR_RAM_IF | interrupts_to_be_serviced
str r0, [r7, #-0x208] @ ISR_RAM_IF = r0 = ISR_RAM_IF | interrupts_to_be_serviced
@ save contents of SPSR_irq; if a nested interrupt occurs, it will overwrite the register
mrs r6, spsr @ r6 = saved_spsr_irq = SPSR_irq
@ disable interrupts via REG_IME
mov r0, #0 @ r0 = 0
strh r0, [r7, #0x8] @ REG_IME = r0 = 0
@ enable interrupts via CPSR (but they are still disabled due to REG_IME)
mrs r0, cpsr @ r0 = CPSR
bic r0, r0, #0x80 @ set CPSR's IRQ flag to enable by clearing bit 7
msr cpsr, r0 @ CPSR = r0
.L_HBLANK:
@ if((interrupts_to_be_serviced & HBlank flag) != 0), handle interrupt
@ otherwise, branch to next test
tst r4, #0x0002 @ test interrupts_to_be_serviced & HBlank flag
beq .L_TIMER_0 @ if result of test == 0, branch to next test
@ call specific ISR
ldr r0, .L_HBLANK_ISR @ load address of function pointer into r0
ldr r0, [r0] @ load function pointer into r0
mov lr, pc @ save program counter in link register
bx r0 @ call ISR pointed to by the function pointer
.L_TIMER_0:
@ if((interrupts_to_be_serviced & Timer_0 flag) != 0), handle interrupt
@ otherwise, branch to next test
tst r4, #0x0008 @ test interrupts_to_be_serviced & Timer_0 flag
beq .L_KEYPAD @ if result of test == 0, branch to next test
@ temporarily disable lower-priority interrupts via REG_IE
bic r0, r5, #0x1000 @ r0 = enabled_interrupts & ~(key input flag)
bic r0, r0, #0x0001 @ r0 = r0 & ~(vblank flag)
strh r0, [r7] @ REG_IE = r0 = enabled_interrupts & ~lower_priority_flags
@ re-enable interrupts via REG_IME
mov r0, #1 @ r0 = 1
strh r0, [r7, #0x8] @ REG_IME = r0 = 1
@ call Timer 0 ISR
ldr r0, .L_TIMER_0_ISR @ load address of function pointer into r0
ldr r0, [r0] @ load function pointer into r0
mov lr, pc @ save program counter in link register
bx r0 @ call ISR pointed to by the function pointer
@ disable interrupts via REG_IME
mov r0, #0 @ r0 = 0
strh r0, [r7, #0x8] @ REG_IME = r0 = 0
@ re-enable lower-priority interrupts via REG_IE
strh r5, [r7] @ REG_IE = r5 = enabled_interrupts
.L_KEYPAD:
@ if((interrupts_to_be_serviced & Keypad flag) != 0), handle interrupt
@ otherwise, branch to next test
tst r4, #0x1000 @ test interrupts_to_be_serviced & Keypad flag
beq .L_VBLANK @ if result of test == 0, branch to next test
@ re-enable interrupts via REG_IME
mov r0, #1 @ r0 = 1
strh r0, [r7, #0x8] @ REG_IME = r0 = 1
@ call Keypad ISR
ldr r0, .L_KEYPAD_ISR @ load address of function pointer into r0
ldr r0, [r0] @ load function pointer into r0
mov lr, pc @ save program counter in link register
bx r0 @ call ISR pointed to by the function pointer
@ disable interrupts via REG_IME
mov r0, #0 @ r0 = 0
strh r0, [r7, #0x8] @ REG_IME = r0 = 0
.L_VBLANK:
@ if((interrupts_to_be_serviced & VBlank flag) != 0), handle interrupt
@ otherwise, branch to next test
tst r4, #0x0001 @ test interrupts_to_be_serviced & VBlank flag
beq .L_END @ if result of test == 0, branch to next test
@ re-enable interrupts via REG_IME
mov r0, #1 @ r0 = 1
strh r0, [r7, #0x8] @ REG_IME = r0 = 1
@ call Vblank ISR
ldr r0, .L_VBLANK_ISR @ load address of function pointer into r0
ldr r0, [r0] @ load function pointer into r0
mov lr, pc @ save program counter in link register
bx r0 @ call ISR pointed to by the function pointer
@ disable interrupts via REG_IME
mov r0, #0 @ r0 = 0
strh r0, [r7, #0x8] @ REG_IME = r0 = 0
.L_END:
@ restore contents of SPSR_irq
msr spsr, r6 @ SPSR_irq = r6
@ re-enable interrupts via REG_IME
mov r0, #1 @ r0 = 1
strh r0, [r7, #0x8] @ REG_IME = r0 = 1
@ restore contents of r4-r7 and link register
ldmfd sp!, {r4-r7, lr} @ pop r4-r7 & link register
@ return to caller
bx lr @ return to caller
.L_HBLANK_ISR:
.word hblank_isr
.L_TIMER_0_ISR:
.word timer_0_isr
.L_KEYPAD_ISR:
.word keypad_isr
.L_VBLANK_ISR:
.word vblank_isr
.Lfe1:
.size MainInterruptServiceRoutine,.Lfe1-MainInterruptServiceRoutine
|