From ebde8b58751c120ad6358e753d86928034af8e07 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Mon, 6 Jan 2020 11:50:41 +0100 Subject: [PATCH] targets/gba: implement interrupt handler --- compiler/compiler.go | 2 +- src/runtime/interrupt/interrupt.go | 16 ----- .../interrupt/interrupt_gameboyadvance.go | 59 +++++++++++++++++++ src/runtime/interrupt/interrupt_vectored.go | 19 ++++++ src/runtime/runtime_arm7tdmi.go | 1 + targets/gameboy-advance.ld | 9 ++- targets/gameboy-advance.s | 18 +++++- 7 files changed, 101 insertions(+), 23 deletions(-) create mode 100644 src/runtime/interrupt/interrupt_gameboyadvance.go create mode 100644 src/runtime/interrupt/interrupt_vectored.go diff --git a/compiler/compiler.go b/compiler/compiler.go index ac8a0cf02..515913eb2 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1309,7 +1309,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e return c.emitVolatileLoad(frame, instr) case strings.HasPrefix(name, "runtime/volatile.Store"): return c.emitVolatileStore(frame, instr) - case name == "runtime/interrupt.New": + case name == "runtime/interrupt.New" && len(fn.Blocks) == 0: return c.emitInterruptGlobal(frame, instr) } diff --git a/src/runtime/interrupt/interrupt.go b/src/runtime/interrupt/interrupt.go index 6d853d3d3..20fbe7371 100644 --- a/src/runtime/interrupt/interrupt.go +++ b/src/runtime/interrupt/interrupt.go @@ -12,19 +12,3 @@ type Interrupt struct { // some encapsulation. num int } - -// New is a compiler intrinsic that creates a new Interrupt object. You may call -// it only once, and must pass constant parameters to it. That means that the -// interrupt ID must be a Go constant and that the handler must be a simple -// function: closures are not supported. -func New(id int, handler func(Interrupt)) Interrupt - -// Register is used to declare an interrupt. You should not normally call this -// function: it is only for telling the compiler about the mapping between an -// interrupt number and the interrupt handler name. -func Register(id int, handlerName string) int - -type handle struct { - handler func(Interrupt) - Interrupt -} diff --git a/src/runtime/interrupt/interrupt_gameboyadvance.go b/src/runtime/interrupt/interrupt_gameboyadvance.go new file mode 100644 index 000000000..b8fef6452 --- /dev/null +++ b/src/runtime/interrupt/interrupt_gameboyadvance.go @@ -0,0 +1,59 @@ +// +build gameboyadvance + +package interrupt + +import ( + "runtime/volatile" + "unsafe" +) + +var handlers = [14]func(Interrupt){} + +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 +) + +var ( + regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200))) + regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202))) + regInterruptMasterEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000208))) +) + +// New creates a new Interrupt object. Do not call it multiple times. If you do, +// make sure the interrupt is disabled while you do so. The last call will set +// the active interrupt handler. +func New(id int, handler func(Interrupt)) Interrupt { + handlers[id] = handler + return Interrupt{id} +} + +// 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 << irq.num) +} + +//export handleInterrupt +func handleInterrupt() { + flags := regInterruptRequestFlags.Get() + for i := range handlers { + if flags & (1 << i) != 0 { + irq := Interrupt{i} + regInterruptRequestFlags.Set(1 << i) // acknowledge interrupt + handlers[i](irq) + } + } +} diff --git a/src/runtime/interrupt/interrupt_vectored.go b/src/runtime/interrupt/interrupt_vectored.go new file mode 100644 index 000000000..93fc67dea --- /dev/null +++ b/src/runtime/interrupt/interrupt_vectored.go @@ -0,0 +1,19 @@ +// +build avr riscv,baremetal cortexm + +package interrupt + +// New is a compiler intrinsic that creates a new Interrupt object. You may call +// it only once, and must pass constant parameters to it. That means that the +// interrupt ID must be a Go constant and that the handler must be a simple +// function: closures are not supported. +func New(id int, handler func(Interrupt)) Interrupt + +// Register is used to declare an interrupt. You should not normally call this +// function: it is only for telling the compiler about the mapping between an +// interrupt number and the interrupt handler name. +func Register(id int, handlerName string) int + +type handle struct { + handler func(Interrupt) + Interrupt +} diff --git a/src/runtime/runtime_arm7tdmi.go b/src/runtime/runtime_arm7tdmi.go index 8d9631e27..d5c8c8185 100644 --- a/src/runtime/runtime_arm7tdmi.go +++ b/src/runtime/runtime_arm7tdmi.go @@ -4,6 +4,7 @@ package runtime import ( "unsafe" + _ "runtime/interrupt" // make sure the interrupt handler is defined ) type timeUnit int64 diff --git a/targets/gameboy-advance.ld b/targets/gameboy-advance.ld index 1b997eca2..61dd77f28 100644 --- a/targets/gameboy-advance.ld +++ b/targets/gameboy-advance.ld @@ -1,10 +1,13 @@ 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) */ - rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */ + ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */ + iwram : ORIGIN = 0x03000000, LENGTH = 32K-96 /* in-chip work RAM (faster) */ + rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */ } __stack_size_irq = 1K; diff --git a/targets/gameboy-advance.s b/targets/gameboy-advance.s index d00ec8798..70b184db2 100644 --- a/targets/gameboy-advance.s +++ b/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, =__sp_irq // Set IRQ stack @@ -27,7 +25,21 @@ start_vector: msr cpsr, r0 ldr sp, =__sp_usr // 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