From 3e0bb6c9d1f788b51d26ed55acd32291df721129 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Wed, 28 Apr 2021 19:30:20 +0200 Subject: Add support for IRQ handling --- src/common.h | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) (limited to 'src/common.h') diff --git a/src/common.h b/src/common.h index e3405be..dac48ad 100644 --- a/src/common.h +++ b/src/common.h @@ -46,6 +46,15 @@ #define DISP_OBJ (1 << 12) #define DISP_ENABLE_SPRITES DISP_OBJ | DISP_OBJ_1D +// These bits are used to control the DISP_STATUS register. +#define DISP_VBLANK_STATUS (1 << 0x0) +#define DISP_HBLANK_STATUS (1 << 0x1) +#define DISP_VCOUNT_STATUS (1 << 0x2) +#define DISP_VBLANK_IRQ (1 << 0x3) +#define DISP_HBLANK_IRQ (1 << 0x4) +#define DISP_VCOUNT_IRQ (1 << 0x5) +#define DISP_VCOUNT_TRIGGER(N) ((N) << 0x8) + // Registers to control of BG layers. #define BG_CTRL(N) *((vu16*)(0x04000008 + 0x0002 * (N))) @@ -245,8 +254,14 @@ u32 profile_stop(void) { // Input handling. // -// Memory address for key input register +// Memory address for key input and control register #define KEY_INPUTS *((vu16*) 0x04000130) +#define KEY_CTRL *((vu16*) 0x04000132) + +// Key control register bits. +#define KEY_IRQ_KEY(N) (N) +#define KEY_IRQ (1 << 0xE) +#define KEY_IRQ_IF_SET (1 << 0xF) // Alias for key pressing bits. #define KEY_A (1 << 0) @@ -385,4 +400,120 @@ dma_fill(void *dst, const void *src, u32 size, int channel) { dma_transfer_fill(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); } +// +// Interrupts. +// + +#define IRQ_ENABLE *((vu16*) 0x04000200) +#define IRQ_ACK *((vu16*) 0x04000202) +#define IRQ_CTRL *((vu16*) 0x04000208) +#define IRQ_ACK_BIOS *((vu16*) 0x03007FF8) + +typedef enum { + IRQ_VBLANK, + IRQ_HBLANK, + IRQ_VCOUNT, + IRQ_TIMER_0, + IRQ_TIMER_1, + IRQ_TIMER_2, + IRQ_TIMER_3, + IRQ_SERIAL, + IRQ_DMA_0, + IRQ_DMA_1, + IRQ_DMA_2, + IRQ_DMA_3, + IRQ_KEYPAD, + IRQ_GAMEPAK, +} IrqIndex; + +typedef void (*IrsFunc)(void); + +IrsFunc irs_table[] = { + [IRQ_VBLANK ] = NULL, + [IRQ_HBLANK ] = NULL, + [IRQ_VCOUNT ] = NULL, + [IRQ_TIMER_0] = NULL, + [IRQ_TIMER_1] = NULL, + [IRQ_TIMER_2] = NULL, + [IRQ_TIMER_3] = NULL, + [IRQ_SERIAL ] = NULL, + [IRQ_DMA_0 ] = NULL, + [IRQ_DMA_1 ] = NULL, + [IRQ_DMA_2 ] = NULL, + [IRQ_DMA_3 ] = NULL, + [IRQ_KEYPAD ] = NULL, + [IRQ_GAMEPAK] = NULL, +}; + +void +irq_enable(IrqIndex idx) { + switch (idx) { + case IRQ_VBLANK: { DISP_STATUS |= DISP_VBLANK_IRQ; } break; + case IRQ_HBLANK: { DISP_STATUS |= DISP_HBLANK_IRQ; } break; + case IRQ_VCOUNT: { DISP_STATUS |= DISP_VCOUNT_IRQ; } break; + case IRQ_TIMER_0: { TIMER_CTRL_0 |= TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_1: { TIMER_CTRL_1 |= TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_2: { TIMER_CTRL_2 |= TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_3: { TIMER_CTRL_3 |= TIMER_CTRL_IRQ; } break; + case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; + case IRQ_DMA_0: { DMA_CTRL(0) |= DMA_IRQ; } break; + case IRQ_DMA_1: { DMA_CTRL(1) |= DMA_IRQ; } break; + case IRQ_DMA_2: { DMA_CTRL(2) |= DMA_IRQ; } break; + case IRQ_DMA_3: { DMA_CTRL(3) |= DMA_IRQ; } break; + case IRQ_KEYPAD: { KEY_CTRL |= KEY_IRQ; } break; + case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; + } + IRQ_ENABLE |= (1 << idx); +} + +void +irq_disable(IrqIndex idx) { + switch (idx) { + case IRQ_VBLANK: { DISP_STATUS &= ~DISP_VBLANK_IRQ; } break; + case IRQ_HBLANK: { DISP_STATUS &= ~DISP_HBLANK_IRQ; } break; + case IRQ_VCOUNT: { DISP_STATUS &= ~DISP_VCOUNT_IRQ; } break; + case IRQ_TIMER_0: { TIMER_CTRL_0 &= ~TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_1: { TIMER_CTRL_1 &= ~TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_2: { TIMER_CTRL_2 &= ~TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_3: { TIMER_CTRL_3 &= ~TIMER_CTRL_IRQ; } break; + case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; + case IRQ_DMA_0: { DMA_CTRL(0) &= ~DMA_IRQ; } break; + case IRQ_DMA_1: { DMA_CTRL(1) &= ~DMA_IRQ; } break; + case IRQ_DMA_2: { DMA_CTRL(2) &= ~DMA_IRQ; } break; + case IRQ_DMA_3: { DMA_CTRL(3) &= ~DMA_IRQ; } break; + case IRQ_KEYPAD: { KEY_CTRL &= ~KEY_IRQ; } break; + case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; + } + IRQ_ENABLE &= ~(1 << idx); +} + +void +irs_set(IrqIndex idx, IrsFunc func) { + // Store IRQ_CTRL status and disable interrupts for now. + u16 irq_ctrl = IRQ_CTRL; + IRQ_CTRL = 0; + + // Update the IRS table and enable/disable the given IRQ. + irs_table[idx] = func; + if (func == NULL) { + irq_disable(idx); + } else { + irq_enable(idx); + } + + // Restore previous irq_ctrl. + IRQ_CTRL = irq_ctrl; +} + +#define IRS_MAIN *(IrsFunc*)(0x03007FFC) + +// External irs_main function, has to be written in ARM assembly. +void irs_main(void); + +void +irq_init() { + IRS_MAIN = irs_main; + IRQ_CTRL = 1; +} + #endif // GBAEXP_COMMON_H -- cgit v1.2.1