Skip to content

Commit 8687f3f

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent f14127b commit 8687f3f

File tree

5 files changed

+77
-6
lines changed

5 files changed

+77
-6
lines changed

src/machine/machine_gameboyadvance.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,25 @@ import (
88
"unsafe"
99
)
1010

11+
// Interrupt numbers as used on the GameBoy Advance. Register them with
12+
// runtime/interrupt.New.
13+
const (
14+
IRQ_VBLANK = 0
15+
IRQ_HBLANK = 1
16+
IRQ_VCOUNT = 2
17+
IRQ_TIMER0 = 3
18+
IRQ_TIMER1 = 4
19+
IRQ_TIMER2 = 5
20+
IRQ_TIMER3 = 6
21+
IRQ_COM = 7
22+
IRQ_DMA0 = 8
23+
IRQ_DMA1 = 9
24+
IRQ_DMA2 = 10
25+
IRQ_DMA3 = 11
26+
IRQ_KEYPAD = 12
27+
IRQ_GAMEPAK = 13
28+
)
29+
1130
// Make it easier to directly write to I/O RAM.
1231
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))
1332

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// +build gameboyadvance
2+
3+
package interrupt
4+
5+
import (
6+
"runtime/volatile"
7+
"unsafe"
8+
)
9+
10+
var (
11+
regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200)))
12+
regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202)))
13+
regInterruptMasterEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000208)))
14+
)
15+
16+
// Enable enables this interrupt. Right after calling this function, the
17+
// interrupt may be invoked if it was already pending.
18+
func (irq Interrupt) Enable() {
19+
regInterruptEnable.SetBits(1 << uint(irq.num))
20+
}
21+
22+
//export handleInterrupt
23+
func handleInterrupt() {
24+
flags := regInterruptRequestFlags.Get()
25+
for i := 0; i < 14; i++ {
26+
if flags&(1<<uint(i)) != 0 {
27+
regInterruptRequestFlags.Set(1 << uint(i)) // acknowledge interrupt
28+
callInterruptHandler(i)
29+
}
30+
}
31+
}
32+
33+
// callInterruptHandler is a compiler-generated function that calls the
34+
// appropriate interrupt handler for the given interrupt ID.
35+
//go:linkname callInterruptHandler runtime.callInterruptHandler
36+
func callInterruptHandler(id int)

src/runtime/runtime_arm7tdmi.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package runtime
44

55
import (
6+
_ "runtime/interrupt" // make sure the interrupt handler is defined
67
"unsafe"
78
)
89

targets/gameboy-advance.ld

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
OUTPUT_ARCH(arm)
22
ENTRY(_start)
33

4+
/* Note: iwram is reduced by 96 bytes because the last part of that RAM
5+
* (starting at 0x03007FA0) is used for interrupt handling.
6+
*/
47
MEMORY {
5-
ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */
6-
iwram : ORIGIN = 0x03000000, LENGTH = 32K /* in-chip work RAM (faster) */
7-
rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */
8+
ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */
9+
iwram : ORIGIN = 0x03000000, LENGTH = 32K-96 /* in-chip work RAM (faster) */
10+
rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */
811
}
912

1013
__stack_size_irq = 1K;

targets/gameboy-advance.s

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,29 @@ _start:
1717
.byte 0x00,0x00 // Checksum (80000BEh)
1818

1919
start_vector:
20-
mov r0, #0x4000000 // REG_BASE
21-
str r0, [r0, #0x208]
22-
20+
// Configure stacks
2321
mov r0, #0x12 // Switch to IRQ Mode
2422
msr cpsr, r0
2523
ldr sp, =_stack_top_irq // Set IRQ stack
2624
mov r0, #0x1f // Switch to System Mode
2725
msr cpsr, r0
2826
ldr sp, =_stack_top // Set user stack
2927

28+
// Configure interrupt handler
29+
mov r0, #0x4000000 // REG_BASE
30+
ldr r1, =handleInterruptARM
31+
str r1, [r0, #-4] // actually storing to 0x03007FFC due to mirroring
32+
33+
// Enable interrupts
34+
mov r1, #1
35+
str r1, [r0, #0x208] // 0x04000208 Interrupt Master Enable
36+
3037
// Jump to user code (switching to Thumb mode)
3138
ldr r3, =main
3239
bx r3
3340

41+
// Small interrupt handler that immediately jumps to a function defined in the
42+
// program (in Thumb) for further processing.
43+
handleInterruptARM:
44+
ldr r0, =handleInterrupt
45+
bx r0

0 commit comments

Comments
 (0)