all repos — mgba @ fc0109282b81c5fac6a108b4636192d8efe2b345

mGBA Game Boy Advance Emulator

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