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 "util/crc32.h"
11#include "util/memory.h"
12#include "util/math.h"
13#include "util/patch.h"
14#include "util/vfs.h"
15
16const uint32_t DMG_LR35902_FREQUENCY = 0x400000;
17const uint32_t CGB_LR35902_FREQUENCY = 0x800000;
18const uint32_t SGB_LR35902_FREQUENCY = 0x418B1E;
19
20const uint32_t GB_COMPONENT_MAGIC = 0x400000;
21
22static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component);
23static void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh);
24static void GBProcessEvents(struct LR35902Core* cpu);
25static void GBSetInterrupts(struct LR35902Core* cpu, bool enable);
26static void GBHitStub(struct LR35902Core* cpu);
27
28void GBCreate(struct GB* gb) {
29 gb->d.id = GB_COMPONENT_MAGIC;
30 gb->d.init = GBInit;
31 gb->d.deinit = 0;
32}
33
34static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component) {
35 struct GB* gb = (struct GB*) component;
36 gb->cpu = cpu;
37
38 GBInterruptHandlerInit(&cpu->irqh);
39 GBMemoryInit(gb);
40
41 gb->video.p = gb;
42 GBVideoInit(&gb->video);
43
44 gb->timer.p = gb;
45
46 gb->romVf = 0;
47 gb->sramVf = 0;
48
49 gb->pristineRom = 0;
50 gb->pristineRomSize = 0;
51 gb->yankedRomSize = 0;
52}
53
54bool GBLoadROM(struct GB* gb, struct VFile* vf, struct VFile* sav, const char* fname) {
55 GBUnloadROM(gb);
56 gb->romVf = vf;
57 gb->pristineRomSize = vf->size(vf);
58 vf->seek(vf, 0, SEEK_SET);
59#ifdef _3DS
60 gb->pristineRom = 0;
61 if (gb->pristineRomSize <= romBufferSize) {
62 gb->pristineRom = romBuffer;
63 vf->read(vf, romBuffer, gb->pristineRomSize);
64 }
65#else
66 gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ);
67#endif
68 if (!gb->pristineRom) {
69 return false;
70 }
71 gb->yankedRomSize = 0;
72 gb->memory.rom = gb->pristineRom;
73 gb->activeFile = fname;
74 gb->memory.romSize = gb->pristineRomSize;
75 gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
76 gb->sramVf = sav;
77 if (sav) {
78 gb->memory.sram = sav->map(sav, 0x8000, MAP_WRITE);
79 } else {
80 gb->memory.sram = anonymousMemoryMap(0x8000);
81 }
82 return true;
83 // TODO: error check
84}
85
86void GBUnloadROM(struct GB* gb) {
87 // TODO: Share with GBAUnloadROM
88 if (gb->memory.rom && gb->pristineRom != gb->memory.rom) {
89 if (gb->yankedRomSize) {
90 gb->yankedRomSize = 0;
91 }
92 mappedMemoryFree(gb->memory.rom, 0x400000);
93 }
94 gb->memory.rom = 0;
95
96 if (gb->romVf) {
97#ifndef _3DS
98 gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize);
99#endif
100 gb->pristineRom = 0;
101 gb->romVf = 0;
102 }
103
104 if (gb->sramVf) {
105 gb->sramVf->unmap(gb->sramVf, gb->memory.sram, 0x8000);
106 } else if (gb->memory.sram) {
107 mappedMemoryFree(gb->memory.sram, 0x8000);
108 }
109 gb->memory.sram = 0;
110}
111
112void GBDestroy(struct GB* gb) {
113 GBUnloadROM(gb);
114
115 GBMemoryDeinit(gb);
116}
117
118void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
119 irqh->reset = GBReset;
120 irqh->processEvents = GBProcessEvents;
121 irqh->setInterrupts = GBSetInterrupts;
122 irqh->hitStub = GBHitStub;
123 irqh->halt = GBHalt;
124}
125
126void GBReset(struct LR35902Core* cpu) {
127 cpu->a = 1;
128 cpu->f.packed = 0xB0;
129 cpu->b = 0;
130 cpu->c = 0x13;
131 cpu->d = 0;
132 cpu->e = 0xD8;
133 cpu->h = 1;
134 cpu->l = 0x4D;
135 cpu->sp = 0xFFFE;
136 cpu->pc = 0x100;
137
138 struct GB* gb = (struct GB*) cpu->master;
139
140 if (gb->yankedRomSize) {
141 gb->memory.romSize = gb->yankedRomSize;
142 gb->yankedRomSize = 0;
143 }
144 GBMemoryReset(gb);
145 GBVideoReset(&gb->video);
146 GBTimerReset(&gb->timer);
147 GBIOReset(gb);
148}
149
150void GBUpdateIRQs(struct GB* gb) {
151 int irqs = gb->memory.ie & gb->memory.io[REG_IF];
152 if (!irqs) {
153 return;
154 }
155 gb->cpu->halted = false;
156
157 if (!gb->memory.ime) {
158 return;
159 }
160
161 gb->cpu->irqh.setInterrupts(gb->cpu, false);
162 if (irqs & (1 << GB_IRQ_VBLANK)) {
163 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_VBLANK);
164 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_VBLANK);
165 return;
166 }
167 if (irqs & (1 << GB_IRQ_LCDSTAT)) {
168 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_LCDSTAT);
169 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_LCDSTAT);
170 return;
171 }
172 if (irqs & (1 << GB_IRQ_TIMER)) {
173 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_TIMER);
174 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_TIMER);
175 return;
176 }
177 if (irqs & (1 << GB_IRQ_SIO)) {
178 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_SIO);
179 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_SIO);
180 return;
181 }
182 if (irqs & (1 << GB_IRQ_KEYPAD)) {
183 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_KEYPAD);
184 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_KEYPAD);
185 }
186}
187
188void GBProcessEvents(struct LR35902Core* cpu) {
189 struct GB* gb = (struct GB*) cpu->master;
190 do {
191 int32_t cycles = cpu->nextEvent;
192 int32_t nextEvent = INT_MAX;
193 int32_t testEvent;
194
195 testEvent = GBVideoProcessEvents(&gb->video, cycles);
196 if (testEvent < nextEvent) {
197 nextEvent = testEvent;
198 }
199
200 testEvent = GBTimerProcessEvents(&gb->timer, cycles);
201 if (testEvent < nextEvent) {
202 nextEvent = testEvent;
203 }
204
205 testEvent = GBMemoryProcessEvents(gb, cycles);
206 if (testEvent < nextEvent) {
207 nextEvent = testEvent;
208 }
209
210 cpu->cycles -= cycles;
211 cpu->nextEvent = nextEvent;
212
213 if (cpu->halted) {
214 cpu->cycles = cpu->nextEvent;
215 }
216 } while (cpu->cycles >= cpu->nextEvent);
217}
218
219void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
220 struct GB* gb = (struct GB*) cpu->master;
221 gb->memory.ime = enable;
222 GBUpdateIRQs(gb);
223}
224
225void GBHalt(struct LR35902Core* cpu) {
226 cpu->cycles = cpu->nextEvent;
227 cpu->halted = true;
228}
229
230void GBHitStub(struct LR35902Core* cpu) {
231 // TODO
232 //printf("Hit stub at address %04X\n", cpu->pc);
233}
234
235bool GBIsROM(struct VFile* vf) {
236 vf->seek(vf, 0x104, SEEK_SET);
237 uint8_t header[4];
238 static const uint8_t knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
239
240 if (vf->read(vf, &header, sizeof(header)) < (ssize_t) sizeof(header)) {
241 return false;
242 }
243 if (memcmp(header, knownHeader, sizeof(header))) {
244 return false;
245 }
246 return true;
247}