aboutsummaryrefslogtreecommitdiffstats
path: root/src/gba/interrupts.c
blob: 3b113359be51186e546babe0183ffe03167440df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "gba.h"

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,
};

// External irs_main function, has to be written in ARM assembly.
void irs_main(void);
#define IRS_MAIN *(IrsFunc*)(0x03007FFC)

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;
}

void
irq_init(void) {
    IRS_MAIN = irs_main;
    IRQ_CTRL = 1;
}

void
irs_stub(void) {}