mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-17 03:03:27 +00:00
targets/gba: implement interrupt handler
This commit is contained in:
@@ -1309,7 +1309,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
|||||||
return c.emitVolatileLoad(frame, instr)
|
return c.emitVolatileLoad(frame, instr)
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||||
return c.emitVolatileStore(frame, instr)
|
return c.emitVolatileStore(frame, instr)
|
||||||
case name == "runtime/interrupt.New":
|
case name == "runtime/interrupt.New" && len(fn.Blocks) == 0:
|
||||||
return c.emitInterruptGlobal(frame, instr)
|
return c.emitInterruptGlobal(frame, instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,19 +12,3 @@ type Interrupt struct {
|
|||||||
// some encapsulation.
|
// some encapsulation.
|
||||||
num int
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
_ "runtime/interrupt" // make sure the interrupt handler is defined
|
||||||
)
|
)
|
||||||
|
|
||||||
type timeUnit int64
|
type timeUnit int64
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
OUTPUT_ARCH(arm)
|
OUTPUT_ARCH(arm)
|
||||||
ENTRY(_start)
|
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 {
|
MEMORY {
|
||||||
ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */
|
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 */
|
rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */
|
||||||
}
|
}
|
||||||
|
|
||||||
__stack_size_irq = 1K;
|
__stack_size_irq = 1K;
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ _start:
|
|||||||
.byte 0x00,0x00 // Checksum (80000BEh)
|
.byte 0x00,0x00 // Checksum (80000BEh)
|
||||||
|
|
||||||
start_vector:
|
start_vector:
|
||||||
mov r0, #0x4000000 // REG_BASE
|
// Configure stacks
|
||||||
str r0, [r0, #0x208]
|
|
||||||
|
|
||||||
mov r0, #0x12 // Switch to IRQ Mode
|
mov r0, #0x12 // Switch to IRQ Mode
|
||||||
msr cpsr, r0
|
msr cpsr, r0
|
||||||
ldr sp, =__sp_irq // Set IRQ stack
|
ldr sp, =__sp_irq // Set IRQ stack
|
||||||
@@ -27,7 +25,21 @@ start_vector:
|
|||||||
msr cpsr, r0
|
msr cpsr, r0
|
||||||
ldr sp, =__sp_usr // Set user stack
|
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)
|
// Jump to user code (switching to Thumb mode)
|
||||||
ldr r3, =main
|
ldr r3, =main
|
||||||
bx r3
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user