mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-11 23:19:30 +00:00
cd364a9315
* gba: add bios interrupt flags Adds a new GBA register for interrupt flags, the register is equivalent to the IF register, but is intended for BIOS functions. Acknowledging the interrupts with this register allows us to use BIOS halt functions such as VBlankIntrWait and IntrWait, which we can use to get rid of busy loops in the GBA code. * fix formatting --------- Co-authored-by: zoey <git@zoey.si>
106 lines
2.9 KiB
Go
106 lines
2.9 KiB
Go
//go:build gameboyadvance
|
|
|
|
package interrupt
|
|
|
|
// This is good documentation of the GBA: https://www.akkit.org/info/gbatek.htm
|
|
|
|
import (
|
|
"device/gba"
|
|
)
|
|
|
|
// Enable enables this interrupt. Right after calling this function, the
|
|
// interrupt may be invoked if it was already pending.
|
|
func (irq Interrupt) Enable() {
|
|
gba.INTERRUPT.IE.SetBits(1 << uint(irq.num))
|
|
}
|
|
|
|
var inInterrupt bool
|
|
|
|
//export handleInterrupt
|
|
func handleInterrupt() {
|
|
inInterrupt = true
|
|
flags := gba.INTERRUPT.IF.Get()
|
|
for i := 0; i < 14; i++ {
|
|
if flags&(1<<uint(i)) != 0 {
|
|
gba.INTERRUPT.IF.Set(1 << uint(i)) // acknowledge interrupt
|
|
gba.BIOS_IF.Set(1 << uint(i)) // acknowledge interrupt for BIOS
|
|
callInterruptHandler(i)
|
|
}
|
|
}
|
|
inInterrupt = false
|
|
}
|
|
|
|
// Pseudo function call that is replaced by the compiler with the actual
|
|
// functions registered through interrupt.New. If there are none, calls will be
|
|
// replaced with 'unreachablecalls will be replaced with 'unreachable'.
|
|
//
|
|
//go:linkname callHandlers runtime/interrupt.callHandlers
|
|
func callHandlers(num int)
|
|
|
|
func callInterruptHandler(id int) {
|
|
switch id {
|
|
case gba.IRQ_VBLANK:
|
|
callHandlers(gba.IRQ_VBLANK)
|
|
case gba.IRQ_HBLANK:
|
|
callHandlers(gba.IRQ_HBLANK)
|
|
case gba.IRQ_VCOUNT:
|
|
callHandlers(gba.IRQ_VCOUNT)
|
|
case gba.IRQ_TIMER0:
|
|
callHandlers(gba.IRQ_TIMER0)
|
|
case gba.IRQ_TIMER1:
|
|
callHandlers(gba.IRQ_TIMER1)
|
|
case gba.IRQ_TIMER2:
|
|
callHandlers(gba.IRQ_TIMER2)
|
|
case gba.IRQ_TIMER3:
|
|
callHandlers(gba.IRQ_TIMER3)
|
|
case gba.IRQ_COM:
|
|
callHandlers(gba.IRQ_COM)
|
|
case gba.IRQ_DMA0:
|
|
callHandlers(gba.IRQ_DMA0)
|
|
case gba.IRQ_DMA1:
|
|
callHandlers(gba.IRQ_DMA1)
|
|
case gba.IRQ_DMA2:
|
|
callHandlers(gba.IRQ_DMA2)
|
|
case gba.IRQ_DMA3:
|
|
callHandlers(gba.IRQ_DMA3)
|
|
case gba.IRQ_KEYPAD:
|
|
callHandlers(gba.IRQ_KEYPAD)
|
|
case gba.IRQ_GAMEPAK:
|
|
callHandlers(gba.IRQ_GAMEPAK)
|
|
}
|
|
}
|
|
|
|
// State represents the previous global interrupt state.
|
|
type State uint8
|
|
|
|
// Disable disables all interrupts and returns the previous interrupt state. It
|
|
// can be used in a critical section like this:
|
|
//
|
|
// state := interrupt.Disable()
|
|
// // critical section
|
|
// interrupt.Restore(state)
|
|
//
|
|
// Critical sections can be nested. Make sure to call Restore in the same order
|
|
// as you called Disable (this happens naturally with the pattern above).
|
|
func Disable() (state State) {
|
|
// Save the previous interrupt state.
|
|
state = State(gba.INTERRUPT.PAUSE.Get())
|
|
// Disable all interrupts.
|
|
gba.INTERRUPT.PAUSE.Set(0)
|
|
return
|
|
}
|
|
|
|
// Restore restores interrupts to what they were before. Give the previous state
|
|
// returned by Disable as a parameter. If interrupts were disabled before
|
|
// calling Disable, this will not re-enable interrupts, allowing for nested
|
|
// critical sections.
|
|
func Restore(state State) {
|
|
// Restore interrupts to the previous state.
|
|
gba.INTERRUPT.PAUSE.Set(uint16(state))
|
|
}
|
|
|
|
// In returns whether the system is currently in an interrupt.
|
|
func In() bool {
|
|
return inInterrupt
|
|
}
|