Browse Source

targets/gba: implement interrupt handler

Thanks to Kyle Lemons for the inspiration and original design. The
implementation in this commit is very different however, building on top
of the software vectoring needed in RISC-V. The result is a flexible
interrupt handler that does not take up any RAM for configuration.
pull/774/head
Ayke van Laethem 5 years ago
committed by Ron Evans
parent
commit
8687f3f8f4
  1. 19
      src/machine/machine_gameboyadvance.go
  2. 36
      src/runtime/interrupt/interrupt_gameboyadvance.go
  3. 1
      src/runtime/runtime_arm7tdmi.go
  4. 5
      targets/gameboy-advance.ld
  5. 18
      targets/gameboy-advance.s

19
src/machine/machine_gameboyadvance.go

@ -8,6 +8,25 @@ import (
"unsafe"
)
// Interrupt numbers as used on the GameBoy Advance. Register them with
// runtime/interrupt.New.
const (
IRQ_VBLANK = 0
IRQ_HBLANK = 1
IRQ_VCOUNT = 2
IRQ_TIMER0 = 3
IRQ_TIMER1 = 4
IRQ_TIMER2 = 5
IRQ_TIMER3 = 6
IRQ_COM = 7
IRQ_DMA0 = 8
IRQ_DMA1 = 9
IRQ_DMA2 = 10
IRQ_DMA3 = 11
IRQ_KEYPAD = 12
IRQ_GAMEPAK = 13
)
// Make it easier to directly write to I/O RAM.
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))

36
src/runtime/interrupt/interrupt_gameboyadvance.go

@ -0,0 +1,36 @@
// +build gameboyadvance
package interrupt
import (
"runtime/volatile"
"unsafe"
)
var (
regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200)))
regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202)))
regInterruptMasterEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000208)))
)
// Enable enables this interrupt. Right after calling this function, the
// interrupt may be invoked if it was already pending.
func (irq Interrupt) Enable() {
regInterruptEnable.SetBits(1 << uint(irq.num))
}
//export handleInterrupt
func handleInterrupt() {
flags := regInterruptRequestFlags.Get()
for i := 0; i < 14; i++ {
if flags&(1<<uint(i)) != 0 {
regInterruptRequestFlags.Set(1 << uint(i)) // acknowledge interrupt
callInterruptHandler(i)
}
}
}
// callInterruptHandler is a compiler-generated function that calls the
// appropriate interrupt handler for the given interrupt ID.
//go:linkname callInterruptHandler runtime.callInterruptHandler
func callInterruptHandler(id int)

1
src/runtime/runtime_arm7tdmi.go

@ -3,6 +3,7 @@
package runtime
import (
_ "runtime/interrupt" // make sure the interrupt handler is defined
"unsafe"
)

5
targets/gameboy-advance.ld

@ -1,9 +1,12 @@
OUTPUT_ARCH(arm)
ENTRY(_start)
/* Note: iwram is reduced by 96 bytes because the last part of that RAM
* (starting at 0x03007FA0) is used for interrupt handling.
*/
MEMORY {
ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */
iwram : ORIGIN = 0x03000000, LENGTH = 32K /* in-chip work RAM (faster) */
iwram : ORIGIN = 0x03000000, LENGTH = 32K-96 /* in-chip work RAM (faster) */
rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */
}

18
targets/gameboy-advance.s

@ -17,9 +17,7 @@ _start:
.byte 0x00,0x00 // Checksum (80000BEh)
start_vector:
mov r0, #0x4000000 // REG_BASE
str r0, [r0, #0x208]
// Configure stacks
mov r0, #0x12 // Switch to IRQ Mode
msr cpsr, r0
ldr sp, =_stack_top_irq // Set IRQ stack
@ -27,7 +25,21 @@ start_vector:
msr cpsr, r0
ldr sp, =_stack_top // Set user stack
// Configure interrupt handler
mov r0, #0x4000000 // REG_BASE
ldr r1, =handleInterruptARM
str r1, [r0, #-4] // actually storing to 0x03007FFC due to mirroring
// Enable interrupts
mov r1, #1
str r1, [r0, #0x208] // 0x04000208 Interrupt Master Enable
// Jump to user code (switching to Thumb mode)
ldr r3, =main
bx r3
// Small interrupt handler that immediately jumps to a function defined in the
// program (in Thumb) for further processing.
handleInterruptARM:
ldr r0, =handleInterrupt
bx r0

Loading…
Cancel
Save