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
48 gb->pristineRom = 0;
49 gb->pristineRomSize = 0;
50 gb->yankedRomSize = 0;
51}
52
53bool GBLoadROM(struct GB* gb, struct VFile* vf, struct VFile* sav, const char* fname) {
54 GBUnloadROM(gb);
55 gb->romVf = vf;
56 gb->pristineRomSize = vf->size(vf);
57 vf->seek(vf, 0, SEEK_SET);
58#ifdef _3DS
59 gb->pristineRom = 0;
60 if (gb->pristineRomSize <= romBufferSize) {
61 gb->pristineRom = romBuffer;
62 vf->read(vf, romBuffer, gb->pristineRomSize);
63 }
64#else
65 gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ);
66#endif
67 if (!gb->pristineRom) {
68 return false;
69 }
70 gb->yankedRomSize = 0;
71 gb->memory.rom = gb->pristineRom;
72 gb->activeFile = fname;
73 gb->memory.romSize = gb->pristineRomSize;
74 gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
75 return true;
76 // TODO: error check
77}
78
79void GBUnloadROM(struct GB* gb) {
80 // TODO: Share with GBAUnloadROM
81 if (gb->memory.rom && gb->pristineRom != gb->memory.rom) {
82 if (gb->yankedRomSize) {
83 gb->yankedRomSize = 0;
84 }
85 mappedMemoryFree(gb->memory.rom, 0x400000);
86 }
87 gb->memory.rom = 0;
88
89 if (gb->romVf) {
90#ifndef _3DS
91 gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize);
92#endif
93 gb->pristineRom = 0;
94 gb->romVf = 0;
95 }
96}
97
98void GBDestroy(struct GB* gb) {
99 GBUnloadROM(gb);
100
101 GBMemoryDeinit(gb);
102}
103
104void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
105 irqh->reset = GBReset;
106 irqh->processEvents = GBProcessEvents;
107 irqh->setInterrupts = GBSetInterrupts;
108 irqh->hitStub = GBHitStub;
109}
110
111void GBReset(struct LR35902Core* cpu) {
112 cpu->a = 1;
113 cpu->f.packed = 0xB0;
114 cpu->b = 0;
115 cpu->c = 0x13;
116 cpu->d = 0;
117 cpu->e = 0xD8;
118 cpu->h = 1;
119 cpu->l = 0x4D;
120 cpu->sp = 0xFFFE;
121 cpu->pc = 0x100;
122
123 struct GB* gb = (struct GB*) cpu->master;
124
125 if (gb->yankedRomSize) {
126 gb->memory.romSize = gb->yankedRomSize;
127 gb->yankedRomSize = 0;
128 }
129 GBMemoryReset(gb);
130 GBVideoReset(&gb->video);
131 GBTimerReset(&gb->timer);
132 GBIOReset(gb);
133}
134
135void GBUpdateIRQs(struct GB* gb) {
136 if (!gb->memory.ime) {
137 return;
138 }
139 int irqs = gb->memory.ie & gb->memory.io[REG_IF];
140 if (!irqs) {
141 return;
142 }
143
144 gb->cpu->irqh.setInterrupts(gb->cpu, false);
145 if (irqs & (1 << GB_IRQ_VBLANK)) {
146 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_VBLANK);
147 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_VBLANK);
148 return;
149 }
150 if (irqs & (1 << GB_IRQ_LCDSTAT)) {
151 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_LCDSTAT);
152 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_LCDSTAT);
153 return;
154 }
155 if (irqs & (1 << GB_IRQ_TIMER)) {
156 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_TIMER);
157 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_TIMER);
158 return;
159 }
160 if (irqs & (1 << GB_IRQ_SIO)) {
161 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_SIO);
162 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_SIO);
163 return;
164 }
165 if (irqs & (1 << GB_IRQ_KEYPAD)) {
166 LR35902RaiseIRQ(gb->cpu, GB_VECTOR_KEYPAD);
167 gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_KEYPAD);
168 }
169}
170
171void GBProcessEvents(struct LR35902Core* cpu) {
172 struct GB* gb = (struct GB*) cpu->master;
173 int32_t cycles = cpu->nextEvent;
174 int32_t nextEvent = INT_MAX;
175 int32_t testEvent;
176
177 testEvent = GBVideoProcessEvents(&gb->video, cycles);
178 if (testEvent < nextEvent) {
179 nextEvent = testEvent;
180 }
181
182 testEvent = GBTimerProcessEvents(&gb->timer, cycles);
183 if (testEvent < nextEvent) {
184 nextEvent = testEvent;
185 }
186
187 testEvent = GBMemoryProcessEvents(gb, cycles);
188 if (testEvent < nextEvent) {
189 nextEvent = testEvent;
190 }
191
192 cpu->cycles -= cycles;
193 cpu->nextEvent = nextEvent;
194}
195
196void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
197 struct GB* gb = (struct GB*) cpu->master;
198 gb->memory.ime = enable;
199 GBUpdateIRQs(gb);
200}
201
202void GBHitStub(struct LR35902Core* cpu) {
203 // TODO
204 //printf("Hit stub at address %04X\n", cpu->pc);
205}
206
207bool GBIsROM(struct VFile* vf) {
208 vf->seek(vf, 0x104, SEEK_SET);
209 uint8_t header[4];
210 static const uint8_t knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
211
212 if (vf->read(vf, &header, sizeof(header)) < (ssize_t) sizeof(header)) {
213 return false;
214 }
215 if (memcmp(header, knownHeader, sizeof(header))) {
216 return false;
217 }
218 return true;
219}