src/gb/gb.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "gb.h"
7
8#include "gb/io.h"
9
10#include "core/core.h"
11#include "util/crc32.h"
12#include "util/memory.h"
13#include "util/math.h"
14#include "util/patch.h"
15#include "util/vfs.h"
16
17const uint32_t CGB_LR35902_FREQUENCY = 0x800000;
18const uint32_t SGB_LR35902_FREQUENCY = 0x418B1E;
19
20const uint32_t GB_COMPONENT_MAGIC = 0x400000;
21
22mLOG_DEFINE_CATEGORY(GB, "GB");
23
24static void GBInit(void* cpu, struct mCPUComponent* component);
25static void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh);
26static void GBProcessEvents(struct LR35902Core* cpu);
27static void GBSetInterrupts(struct LR35902Core* cpu, bool enable);
28static void GBIllegal(struct LR35902Core* cpu);
29static void GBStop(struct LR35902Core* cpu);
30
31#ifdef _3DS
32extern uint32_t* romBuffer;
33extern size_t romBufferSize;
34#endif
35
36void GBCreate(struct GB* gb) {
37 gb->d.id = GB_COMPONENT_MAGIC;
38 gb->d.init = GBInit;
39 gb->d.deinit = 0;
40}
41
42static void GBInit(void* cpu, struct mCPUComponent* component) {
43 struct GB* gb = (struct GB*) component;
44 gb->cpu = cpu;
45 gb->sync = NULL;
46
47 GBInterruptHandlerInit(&gb->cpu->irqh);
48 GBMemoryInit(gb);
49
50 gb->video.p = gb;
51 GBVideoInit(&gb->video);
52
53 gb->audio.p = gb;
54 GBAudioInit(&gb->audio, 2048, &gb->memory.io[REG_NR52], GB_AUDIO_DMG); // TODO: Remove magic constant
55
56 gb->timer.p = gb;
57
58 gb->romVf = 0;
59 gb->sramVf = 0;
60
61 gb->pristineRom = 0;
62 gb->pristineRomSize = 0;
63 gb->yankedRomSize = 0;
64
65 gb->stream = NULL;
66
67 gb->eiPending = false;
68 gb->doubleSpeed = 0;
69}
70
71bool GBLoadROM(struct GB* gb, struct VFile* vf) {
72 GBUnloadROM(gb);
73 gb->romVf = vf;
74 gb->pristineRomSize = vf->size(vf);
75 vf->seek(vf, 0, SEEK_SET);
76#ifdef _3DS
77 gb->pristineRom = 0;
78 if (gb->pristineRomSize <= romBufferSize) {
79 gb->pristineRom = romBuffer;
80 vf->read(vf, romBuffer, gb->pristineRomSize);
81 }
82#else
83 gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ);
84#endif
85 if (!gb->pristineRom) {
86 return false;
87 }
88 gb->yankedRomSize = 0;
89 gb->memory.rom = gb->pristineRom;
90 gb->memory.romSize = gb->pristineRomSize;
91 gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
92
93 // TODO: error check
94 return true;
95}
96
97bool GBLoadSave(struct GB* gb, struct VFile* vf) {
98 gb->sramVf = vf;
99 if (vf) {
100 // TODO: Do this in bank-switching code
101 if (vf->size(vf) < 0x20000) {
102 vf->truncate(vf, 0x20000);
103 }
104 gb->memory.sram = vf->map(vf, 0x20000, MAP_WRITE);
105 }
106 return gb->memory.sram;
107}
108
109void GBUnloadROM(struct GB* gb) {
110 // TODO: Share with GBAUnloadROM
111 if (gb->memory.rom && gb->pristineRom != gb->memory.rom) {
112 if (gb->yankedRomSize) {
113 gb->yankedRomSize = 0;
114 }
115 mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX);
116 }
117 gb->memory.rom = 0;
118
119 if (gb->romVf) {
120#ifndef _3DS
121 gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize);
122#endif
123 gb->pristineRom = 0;
124 gb->romVf = 0;
125 }
126
127 if (gb->sramVf) {
128 gb->sramVf->unmap(gb->sramVf, gb->memory.sram, 0x8000);
129 gb->sramVf = 0;
130 } else if (gb->memory.sram) {
131 mappedMemoryFree(gb->memory.sram, 0x8000);
132 }
133 gb->memory.sram = 0;
134}
135
136void GBApplyPatch(struct GB* gb, struct Patch* patch) {
137 size_t patchedSize = patch->outputSize(patch, gb->memory.romSize);
138 if (!patchedSize) {
139 return;
140 }
141 if (patchedSize > GB_SIZE_CART_MAX) {
142 patchedSize = GB_SIZE_CART_MAX;
143 }
144 gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX);
145 if (!patch->applyPatch(patch, gb->pristineRom, gb->pristineRomSize, gb->memory.rom, patchedSize)) {
146 mappedMemoryFree(gb->memory.rom, patchedSize);
147 gb->memory.rom = gb->pristineRom;
148 return;
149 }
150 gb->memory.romSize = patchedSize;
151 gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
152}
153
154void GBDestroy(struct GB* gb) {
155 GBUnloadROM(gb);
156
157 GBMemoryDeinit(gb);
158}
159
160void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
161 irqh->reset = GBReset;
162 irqh->processEvents = GBProcessEvents;
163 irqh->setInterrupts = GBSetInterrupts;
164 irqh->hitIllegal = GBIllegal;
165 irqh->stop = GBStop;
166 irqh->halt = GBHalt;
167}
168
169void GBReset(struct LR35902Core* cpu) {
170 struct GB* gb = (struct GB*) cpu->master;
171
172 const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
173 if (cart->cgb & 0x80) {
174 gb->model = GB_MODEL_CGB;
175 gb->audio.style = GB_AUDIO_CGB;
176 cpu->a = 0x11;
177 } else {
178 // TODO: SGB
179 gb->model = GB_MODEL_DMG;
180 gb->audio.style = GB_AUDIO_DMG;
181 cpu->a = 1;
182 }
183
184 cpu->f.packed = 0xB0;
185 cpu->b = 0;
186 cpu->c = 0x13;
187 cpu->d = 0;
188 cpu->e = 0xD8;
189 cpu->h = 1;
190 cpu->l = 0x4D;
191 cpu->sp = 0xFFFE;
192 cpu->pc = 0x100;
193
194 if (gb->yankedRomSize) {
195 gb->memory.romSize = gb->yankedRomSize;
196 gb->yankedRomSize = 0;
197 }
198 GBMemoryReset(gb);
199 GBVideoReset(&gb->video);
200 GBTimerReset(&gb->timer);
201 GBIOReset(gb);
202 GBAudioReset(&gb->audio);
203}
204
205void GBUpdateIRQs(struct GB* gb) {
206 int irqs = gb->memory.ie & gb->memory.io[REG_IF];
207 if (!irqs) {
208 return;
209 }
210 gb->cpu->halted = false;
211
212 if (!gb->memory.ime) {
213 return;
214 }
215
216 if (irqs & (1 << GB_IRQ_VBLANK)) {
217 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_VBLANK);
218 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_VBLANK);
219 return;
220 }
221 if (irqs & (1 << GB_IRQ_LCDSTAT)) {
222 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_LCDSTAT);
223 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_LCDSTAT);
224 return;
225 }
226 if (irqs & (1 << GB_IRQ_TIMER)) {
227 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_TIMER);
228 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_TIMER);
229 return;
230 }
231 if (irqs & (1 << GB_IRQ_SIO)) {
232 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_SIO);
233 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_SIO);
234 return;
235 }
236 if (irqs & (1 << GB_IRQ_KEYPAD)) {
237 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_KEYPAD);
238 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_KEYPAD);
239 }
240}
241
242void GBProcessEvents(struct LR35902Core* cpu) {
243 struct GB* gb = (struct GB*) cpu->master;
244 do {
245 int32_t cycles = cpu->nextEvent;
246 int32_t nextEvent = INT_MAX;
247 int32_t testEvent;
248
249 if (gb->eiPending) {
250 gb->eiPending -= cycles;
251 if (gb->eiPending <= 0) {
252 gb->memory.ime = true;
253 GBUpdateIRQs(gb);
254 gb->eiPending = 0;
255 }
256 }
257
258 testEvent = GBVideoProcessEvents(&gb->video, cycles >> gb->doubleSpeed);
259 if (testEvent != INT_MAX) {
260 testEvent <<= gb->doubleSpeed;
261 if (testEvent < nextEvent) {
262 nextEvent = testEvent;
263 }
264 }
265
266 testEvent = GBAudioProcessEvents(&gb->audio, cycles >> gb->doubleSpeed);
267 if (testEvent != INT_MAX) {
268 testEvent <<= gb->doubleSpeed;
269 if (testEvent < nextEvent) {
270 nextEvent = testEvent;
271 }
272 }
273
274 testEvent = GBTimerProcessEvents(&gb->timer, cycles);
275 if (testEvent < nextEvent) {
276 nextEvent = testEvent;
277 }
278
279 testEvent = GBMemoryProcessEvents(gb, cycles);
280 if (testEvent < nextEvent) {
281 nextEvent = testEvent;
282 }
283
284 cpu->cycles -= cycles;
285 cpu->nextEvent = nextEvent;
286
287 if (cpu->halted) {
288 cpu->cycles = cpu->nextEvent;
289 }
290 } while (cpu->cycles >= cpu->nextEvent);
291}
292
293void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
294 struct GB* gb = (struct GB*) cpu->master;
295 if (!enable) {
296 gb->memory.ime = enable;
297 gb->eiPending = 0;
298 GBUpdateIRQs(gb);
299 } else {
300 if (cpu->nextEvent > cpu->cycles + 4) {
301 cpu->nextEvent = cpu->cycles + 4;
302 }
303 gb->eiPending = cpu->cycles + 4;
304 }
305}
306
307void GBHalt(struct LR35902Core* cpu) {
308 cpu->cycles = cpu->nextEvent;
309 cpu->halted = true;
310}
311
312void GBStop(struct LR35902Core* cpu) {
313 struct GB* gb = (struct GB*) cpu->master;
314 if (gb->memory.io[REG_KEY1] & 1) {
315 gb->doubleSpeed ^= 1;
316 gb->memory.io[REG_KEY1] &= 1;
317 gb->memory.io[REG_KEY1] |= gb->doubleSpeed << 7;
318 }
319 // TODO: Actually stop
320}
321
322void GBIllegal(struct LR35902Core* cpu) {
323 // TODO
324 mLOG(GB, GAME_ERROR, "Hit illegal opcode at address %04X:%02X\n", cpu->pc, cpu->bus);
325}
326
327bool GBIsROM(struct VFile* vf) {
328 vf->seek(vf, 0x104, SEEK_SET);
329 uint8_t header[4];
330 static const uint8_t knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
331
332 if (vf->read(vf, &header, sizeof(header)) < (ssize_t) sizeof(header)) {
333 return false;
334 }
335 if (memcmp(header, knownHeader, sizeof(header))) {
336 return false;
337 }
338 return true;
339}
340
341void GBGetGameTitle(struct GB* gb, char* out) {
342 const struct GBCartridge* cart = NULL;
343 if (gb->memory.rom) {
344 cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
345 }
346 if (gb->pristineRom) {
347 cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
348 }
349 if (!cart) {
350 return;
351 }
352 if (cart->oldLicensee != 0x33) {
353 memcpy(out, cart->titleLong, 16);
354 } else {
355 memcpy(out, cart->titleShort, 11);
356 }
357}
358
359void GBGetGameCode(struct GB* gb, char* out) {
360 memset(out, 0, 8);
361 const struct GBCartridge* cart = NULL;
362 if (gb->memory.rom) {
363 cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
364 }
365 if (gb->pristineRom) {
366 cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
367 }
368 if (!cart) {
369 return;
370 }
371 if (cart->cgb == 0xC0) {
372 memcpy(out, "CGB-????", 8);
373 } else {
374 memcpy(out, "DMG-????", 8);
375 }
376 if (cart->oldLicensee == 0x33) {
377 memcpy(&out[4], cart->maker, 4);
378 }
379}