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