src/gba/gba.c (view raw)
1#include "gba.h"
2
3#include "gba-bios.h"
4#include "gba-io.h"
5
6#include "debugger.h"
7
8#include <limits.h>
9#include <stdarg.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/mman.h>
14#include <sys/stat.h>
15
16enum {
17 SP_BASE_SYSTEM = 0x03FFFF00,
18 SP_BASE_IRQ = 0x03FFFFA0,
19 SP_BASE_SUPERVISOR = 0x03FFFFE0
20};
21
22static void GBAProcessEvents(struct ARMBoard* board);
23static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles);
24static void GBAHitStub(struct ARMBoard* board, uint32_t opcode);
25
26void GBAInit(struct GBA* gba) {
27 gba->errno = GBA_NO_ERROR;
28 gba->errstr = 0;
29
30 ARMInit(&gba->cpu);
31
32 gba->memory.p = gba;
33 GBAMemoryInit(&gba->memory);
34 ARMAssociateMemory(&gba->cpu, &gba->memory.d);
35
36 gba->board.p = gba;
37 GBABoardInit(&gba->board);
38 ARMAssociateBoard(&gba->cpu, &gba->board.d);
39
40 gba->video.p = gba;
41 GBAVideoInit(&gba->video);
42
43 GBAIOInit(gba);
44
45 memset(gba->timers, 0, sizeof(gba->timers));
46
47 gba->springIRQ = 0;
48 gba->keySource = 0;
49
50 ARMReset(&gba->cpu);
51}
52
53void GBADeinit(struct GBA* gba) {
54 GBAMemoryDeinit(&gba->memory);
55 GBAVideoDeinit(&gba->video);
56}
57
58void GBABoardInit(struct GBABoard* board) {
59 board->d.reset = GBABoardReset;
60 board->d.processEvents = GBAProcessEvents;
61 board->d.swi16 = GBASwi16;
62 board->d.swi32 = GBASwi32;
63 board->d.hitStub = GBAHitStub;
64}
65
66void GBABoardReset(struct ARMBoard* board) {
67 struct ARMCore* cpu = board->cpu;
68 ARMSetPrivilegeMode(cpu, MODE_IRQ);
69 cpu->gprs[ARM_SP] = SP_BASE_IRQ;
70 ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
71 cpu->gprs[ARM_SP] = SP_BASE_SUPERVISOR;
72 ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
73 cpu->gprs[ARM_SP] = SP_BASE_SYSTEM;
74}
75
76static void GBAProcessEvents(struct ARMBoard* board) {
77 struct GBABoard* gbaBoard = (struct GBABoard*) board;
78 int32_t cycles = board->cpu->cycles;
79 int32_t nextEvent = INT_MAX;
80 int32_t testEvent;
81
82 if (gbaBoard->p->springIRQ) {
83 ARMRaiseIRQ(&gbaBoard->p->cpu);
84 gbaBoard->p->springIRQ = 0;
85 }
86
87 testEvent = GBAVideoProcessEvents(&gbaBoard->p->video, cycles);
88 if (testEvent < nextEvent) {
89 nextEvent = testEvent;
90 }
91
92 testEvent = GBAMemoryProcessEvents(&gbaBoard->p->memory, cycles);
93 if (testEvent < nextEvent) {
94 nextEvent = testEvent;
95 }
96
97 testEvent = GBATimersProcessEvents(gbaBoard->p, cycles);
98 if (testEvent < nextEvent) {
99 nextEvent = testEvent;
100 }
101
102 board->cpu->cycles = 0;
103 board->cpu->nextEvent = nextEvent;
104}
105
106static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
107 int32_t nextEvent = INT_MAX;
108 if (gba->timersEnabled) {
109 struct GBATimer* timer;
110 struct GBATimer* nextTimer;
111
112 timer = &gba->timers[0];
113 if (timer->enable) {
114 timer->nextEvent -= cycles;
115 timer->lastEvent -= cycles;
116 if (timer->nextEvent <= 0) {
117 timer->lastEvent = timer->nextEvent;
118 timer->nextEvent += timer->overflowInterval;
119 gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;
120 timer->oldReload = timer->reload;
121
122 if (timer->doIrq) {
123 GBARaiseIRQ(gba, IRQ_TIMER0);
124 }
125
126 nextTimer = &gba->timers[1];
127 if (nextTimer->countUp) {
128 ++gba->memory.io[REG_TM1CNT_LO >> 1];
129 if (!gba->memory.io[REG_TM1CNT_LO >> 1]) {
130 nextTimer->nextEvent = 0;
131 }
132 }
133 }
134 nextEvent = timer->nextEvent;
135 }
136
137 timer = &gba->timers[1];
138 if (timer->enable) {
139 timer->nextEvent -= cycles;
140 timer->lastEvent -= cycles;
141 if (timer->nextEvent <= 0) {
142 timer->lastEvent = timer->nextEvent;
143 timer->nextEvent += timer->overflowInterval;
144 gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload;
145 timer->oldReload = timer->reload;
146
147 if (timer->doIrq) {
148 GBARaiseIRQ(gba, IRQ_TIMER1);
149 }
150
151 if (timer->countUp) {
152 timer->nextEvent = INT_MAX;
153 }
154
155 nextTimer = &gba->timers[2];
156 if (nextTimer->countUp) {
157 ++gba->memory.io[REG_TM2CNT_LO >> 1];
158 if (!gba->memory.io[REG_TM2CNT_LO >> 1]) {
159 nextTimer->nextEvent = 0;
160 }
161 }
162 }
163 if (timer->nextEvent < nextEvent) {
164 nextEvent = timer->nextEvent;
165 }
166 }
167
168 timer = &gba->timers[2];
169 if (timer->enable) {
170 timer->nextEvent -= cycles;
171 timer->lastEvent -= cycles;
172 nextEvent = timer->nextEvent;
173 if (timer->nextEvent <= 0) {
174 timer->lastEvent = timer->nextEvent;
175 timer->nextEvent += timer->overflowInterval;
176 gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload;
177 timer->oldReload = timer->reload;
178
179 if (timer->doIrq) {
180 GBARaiseIRQ(gba, IRQ_TIMER2);
181 }
182
183 if (timer->countUp) {
184 timer->nextEvent = INT_MAX;
185 }
186
187 nextTimer = &gba->timers[3];
188 if (nextTimer->countUp) {
189 ++gba->memory.io[REG_TM3CNT_LO >> 1];
190 if (!gba->memory.io[REG_TM3CNT_LO >> 1]) {
191 nextTimer->nextEvent = 0;
192 }
193 }
194 }
195 if (timer->nextEvent < nextEvent) {
196 nextEvent = timer->nextEvent;
197 }
198 }
199
200 timer = &gba->timers[3];
201 if (timer->enable) {
202 timer->nextEvent -= cycles;
203 timer->lastEvent -= cycles;
204 nextEvent = timer->nextEvent;
205 if (timer->nextEvent <= 0) {
206 timer->lastEvent = timer->nextEvent;
207 timer->nextEvent += timer->overflowInterval;
208 gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload;
209 timer->oldReload = timer->reload;
210
211 if (timer->doIrq) {
212 GBARaiseIRQ(gba, IRQ_TIMER3);
213 }
214
215 if (timer->countUp) {
216 timer->nextEvent = INT_MAX;
217 }
218 }
219 if (timer->nextEvent < nextEvent) {
220 nextEvent = timer->nextEvent;
221 }
222 }
223 }
224 return nextEvent;
225}
226
227void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger) {
228 ARMDebuggerInit(debugger, &gba->cpu);
229 gba->debugger = debugger;
230}
231
232void GBALoadROM(struct GBA* gba, int fd) {
233 struct stat info;
234 gba->memory.rom = mmap(0, SIZE_CART0, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
235 fstat(fd, &info);
236 gba->memory.romSize = info.st_size;
237 // TODO: error check
238}
239
240void GBATimerUpdateRegister(struct GBA* gba, int timer) {
241 struct GBATimer* currentTimer = &gba->timers[timer];
242 if (currentTimer->enable && !currentTimer->countUp) {
243 gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu.cycles - currentTimer->lastEvent) >> currentTimer->prescaleBits);
244 }
245}
246
247void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) {
248 gba->timers[timer].reload = reload;
249}
250
251void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
252 struct GBATimer* currentTimer = &gba->timers[timer];
253 GBATimerUpdateRegister(gba, timer);
254
255 int oldPrescale = currentTimer->prescaleBits;
256 switch (control & 0x0003) {
257 case 0x0000:
258 currentTimer->prescaleBits = 0;
259 break;
260 case 0x0001:
261 currentTimer->prescaleBits = 6;
262 break;
263 case 0x0002:
264 currentTimer->prescaleBits = 8;
265 break;
266 case 0x0003:
267 currentTimer->prescaleBits = 10;
268 break;
269 }
270 currentTimer->countUp = !!(control & 0x0004);
271 currentTimer->doIrq = !!(control & 0x0040);
272 currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << currentTimer->prescaleBits;
273 int wasEnabled = currentTimer->enable;
274 currentTimer->enable = !!(control & 0x0080);
275 if (!wasEnabled && currentTimer->enable) {
276 if (!currentTimer->countUp) {
277 currentTimer->nextEvent = gba->cpu.cycles + currentTimer->overflowInterval;
278 } else {
279 currentTimer->nextEvent = INT_MAX;
280 }
281 gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload;
282 currentTimer->oldReload = currentTimer->reload;
283 gba->timersEnabled |= 1 << timer;
284 } else if (wasEnabled && !currentTimer->enable) {
285 if (!currentTimer->countUp) {
286 gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu.cycles - currentTimer->lastEvent) >> oldPrescale);
287 }
288 gba->timersEnabled &= ~(1 << timer);
289 } else if (currentTimer->prescaleBits != oldPrescale && !currentTimer->countUp) {
290 // FIXME: this might be before present
291 currentTimer->nextEvent = currentTimer->lastEvent + currentTimer->overflowInterval;
292 }
293
294 if (currentTimer->nextEvent < gba->cpu.nextEvent) {
295 gba->cpu.nextEvent = currentTimer->nextEvent;
296 }
297};
298
299void GBAWriteIE(struct GBA* gba, uint16_t value) {
300 if (value & (1 << IRQ_SIO)) {
301 GBALog(GBA_LOG_STUB, "SIO interrupts not implemented");
302 }
303
304 if (value & (1 << IRQ_KEYPAD)) {
305 GBALog(GBA_LOG_STUB, "Keypad interrupts not implemented");
306 }
307
308 if (value & (1 << IRQ_GAMEPAK)) {
309 GBALog(GBA_LOG_STUB, "Gamepak interrupts not implemented");
310 }
311
312 if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) {
313 ARMRaiseIRQ(&gba->cpu);
314 }
315}
316
317void GBAWriteIME(struct GBA* gba, uint16_t value) {
318 if (value && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
319 ARMRaiseIRQ(&gba->cpu);
320 }
321}
322
323void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) {
324 gba->memory.io[REG_IF >> 1] |= 1 << irq;
325
326 if (gba->memory.io[REG_IME >> 1] && (gba->memory.io[REG_IE >> 1] & 1 << irq)) {
327 ARMRaiseIRQ(&gba->cpu);
328 }
329}
330
331int GBATestIRQ(struct GBA* gba) {
332 if (gba->memory.io[REG_IME >> 1] && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
333 gba->springIRQ = 1;
334 gba->cpu.nextEvent = gba->cpu.cycles;
335 return 1;
336 }
337 return 0;
338}
339
340int GBAWaitForIRQ(struct GBA* gba) {
341 int irqs = gba->memory.io[REG_IF >> 1];
342 int newIRQs = 0;
343 gba->memory.io[REG_IF >> 1] = 0;
344 while (1) {
345 if (gba->cpu.nextEvent == INT_MAX) {
346 break;
347 } else {
348 gba->cpu.cycles = gba->cpu.nextEvent;
349 GBAProcessEvents(&gba->board.d);
350 if (gba->memory.io[REG_IF >> 1]) {
351 newIRQs = gba->memory.io[REG_IF >> 1];
352 break;
353 }
354 }
355 }
356 gba->memory.io[REG_IF >> 1] = newIRQs | irqs;
357 return newIRQs;
358}
359
360int GBAHalt(struct GBA* gba) {
361 return GBAWaitForIRQ(gba);
362}
363
364void GBALog(int level, const char* format, ...) {
365 (void)(level);
366 va_list args;
367 va_start(args, format);
368 vprintf(format, args);
369 va_end(args);
370 printf("\n");
371}
372
373void GBAHitStub(struct ARMBoard* board, uint32_t opcode) {
374 GBALog(GBA_LOG_STUB, "Stub opcode: %08x", opcode);
375 struct GBABoard* gbaBoard = (struct GBABoard*) board;
376 if (!gbaBoard->p->debugger) {
377 abort();
378 } else {
379 ARMDebuggerEnter(gbaBoard->p->debugger);
380 }
381}