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