forked from lualiliu/esp32-gameboy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
interrupt.cpp
158 lines (137 loc) · 2.86 KB
/
interrupt.cpp
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include "interrupt.h"
#include "cpu.h"
static int enabled;
static int pending;
/* Pending interrupt flags */
static unsigned int vblank;
static unsigned int lcdstat;
static unsigned int timer;
static unsigned int serial;
static unsigned int joypad;
/* Interrupt masks */
static unsigned int vblank_masked = 1;
static unsigned int lcdstat_masked = 1;
static unsigned int timer_masked = 1;
static unsigned int serial_masked = 1;
static unsigned int joypad_masked = 1;
/* Returns true if the cpu should be unhalted */
int inline interrupt_flush(void)
{
/* Flush the highest priority interrupt and/or resume the cpu */
if(pending == 2)
{
pending--;
return 0;
}
pending = 0;
/* There's a pending interrupt but interrupts are disabled, just resume the cpu */
if(!enabled && ((vblank && !vblank_masked)
|| (lcdstat && !lcdstat_masked)
|| (timer && !timer_masked)
|| (serial && !serial_masked)
|| (joypad && !joypad_masked))
)
return 1;
/* Interrupts are enabled - Check if any need to fire */
if(vblank && !vblank_masked)
{
vblank = 0;
cpu_interrupt(0x40);
}
else if(lcdstat && !lcdstat_masked)
{
lcdstat = 0;
cpu_interrupt(0x48);
}
else if(timer && !timer_masked)
{
timer = 0;
cpu_interrupt(0x50);
}
else if(serial && !serial_masked)
{
serial = 0;
cpu_interrupt(0x58);
}
else if(joypad && !joypad_masked)
{
joypad = 0;
cpu_interrupt(0x60);
}
return 0;
}
void interrupt_enable(void)
{
enabled = 1;
pending = 2;
}
void interrupt_disable(void)
{
enabled = 0;
}
void interrupt(unsigned int n)
{
/* Add this interrupt to pending queue */
switch(n)
{
case INTR_VBLANK:
vblank = 1;
break;
case INTR_LCDSTAT:
lcdstat = 1;
break;
case INTR_TIMER:
timer = 1;
break;
case INTR_SERIAL:
serial = 1;
break;
case INTR_JOYPAD:
joypad = 1;
break;
}
/* If interrupts are already enabled, flush one now, otherwise wait for
* interrupts to be re-enabled.
*/
if(enabled)
interrupt_flush();
}
unsigned char interrupt_get_IF(void)
{
unsigned char mask = 0xE0;
mask |= (vblank << 0);
mask |= (lcdstat << 1);
mask |= (timer << 2);
mask |= (serial << 3);
mask |= (joypad << 4);
return mask;
}
void interrupt_set_IF(unsigned char mask)
{
// printf("IF set to %02x\n", mask);
vblank = !!(mask & 0x01);
lcdstat = !!(mask & 0x02);
timer = !!(mask & 0x04);
serial = !!(mask & 0x08);
joypad = !!(mask & 0x10);
if(enabled && mask)
pending = 1;
}
unsigned char interrupt_get_mask(void)
{
unsigned char mask = 0;
mask |= (!vblank_masked << 0);
mask |= (!lcdstat_masked << 1);
mask |= (!timer_masked << 2);
mask |= (!serial_masked << 3);
mask |= (!joypad_masked << 4);
return mask;
}
void interrupt_set_mask(unsigned char mask)
{
vblank_masked = !(mask & 0x01);
lcdstat_masked = !(mask & 0x02);
timer_masked = !(mask & 0x04);
serial_masked = !(mask & 0x08);
joypad_masked = !(mask & 0x10);
}