all repos — mgba @ f836a67863e66499c812c8d963d37ff56ad10638

mGBA Game Boy Advance Emulator

src/gba/context/context.c (view raw)

  1/* Copyright (c) 2013-2015 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 "gba/context/context.h"
  7
  8#include "gba/context/overrides.h"
  9
 10#include "util/memory.h"
 11#include "util/vfs.h"
 12
 13static struct VFile* _logFile = 0;
 14static void _GBAContextLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
 15
 16bool GBAContextInit(struct GBAContext* context, const char* port) {
 17	context->gba = anonymousMemoryMap(sizeof(struct GBA));
 18	context->cpu = anonymousMemoryMap(sizeof(struct ARMCore));
 19	context->rom = 0;
 20	context->bios = 0;
 21	context->fname = 0;
 22	context->save = 0;
 23	context->renderer = 0;
 24	memset(context->components, 0, sizeof(context->components));
 25
 26	if (!context->gba || !context->cpu) {
 27		if (context->gba) {
 28			mappedMemoryFree(context->gba, sizeof(struct GBA));
 29		}
 30		if (context->cpu) {
 31			mappedMemoryFree(context->cpu, sizeof(struct ARMCore));
 32		}
 33		return false;
 34	}
 35	GBACreate(context->gba);
 36	ARMSetComponents(context->cpu, &context->gba->d, 0, context->components);
 37	ARMInit(context->cpu);
 38
 39	GBAConfigInit(&context->config, port);
 40	if (port) {
 41		if (!_logFile) {
 42			char logPath[PATH_MAX];
 43			GBAConfigDirectory(logPath, PATH_MAX);
 44			strncat(logPath, PATH_SEP "log", PATH_MAX - strlen(logPath));
 45			_logFile = VFileOpen(logPath, O_WRONLY | O_CREAT | O_TRUNC);
 46		}
 47		context->gba->logHandler = _GBAContextLog;
 48
 49		char biosPath[PATH_MAX];
 50		GBAConfigDirectory(biosPath, PATH_MAX);
 51		strncat(biosPath, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(biosPath));
 52
 53		struct GBAOptions opts = {
 54			.bios = biosPath,
 55			.useBios = true,
 56			.idleOptimization = IDLE_LOOP_DETECT,
 57			.logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS
 58		};
 59		GBAConfigLoad(&context->config);
 60		GBAConfigLoadDefaults(&context->config, &opts);
 61	}
 62
 63	context->gba->sync = 0;
 64	return true;
 65}
 66
 67void GBAContextDeinit(struct GBAContext* context) {
 68	ARMDeinit(context->cpu);
 69	GBADestroy(context->gba);
 70	mappedMemoryFree(context->gba, 0);
 71	mappedMemoryFree(context->cpu, 0);
 72	GBAConfigDeinit(&context->config);
 73}
 74
 75bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave) {
 76	struct VDir* dir = VDirOpenArchive(path);
 77	if (dir) {
 78		struct VDirEntry* de;
 79		while ((de = dir->listNext(dir))) {
 80			struct VFile* vf = dir->openFile(dir, de->name(de), O_RDONLY);
 81			if (!vf) {
 82				continue;
 83			}
 84			if (GBAIsROM(vf)) {
 85				context->rom = vf;
 86				break;
 87			}
 88			vf->close(vf);
 89		}
 90		dir->close(dir);
 91	} else {
 92		context->rom = VFileOpen(path, O_RDONLY);
 93	}
 94
 95	if (!context->rom) {
 96		return false;
 97	}
 98
 99	if (!GBAIsROM(context->rom)) {
100		context->rom->close(context->rom);
101		context->rom = 0;
102		return false;
103	}
104
105	context->fname = path;
106	if (autoloadSave) {
107		context->save = VDirOptionalOpenFile(0, path, 0, ".sav", O_RDWR | O_CREAT);
108	}
109	return true;
110}
111
112void GBAContextUnloadROM(struct GBAContext* context) {
113	GBAUnloadROM(context->gba);
114	if (context->bios) {
115		context->bios->close(context->bios);
116		context->bios = 0;
117	}
118	if (context->rom) {
119		context->rom->close(context->rom);
120		context->rom = 0;
121	}
122	if (context->save) {
123		context->save->close(context->save);
124		context->save = 0;
125	}
126}
127
128bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save) {
129	context->rom = rom;
130	if (!GBAIsROM(context->rom)) {
131		context->rom = 0;
132		return false;
133	}
134	context->save = save;
135	return true;
136}
137
138bool GBAContextLoadBIOS(struct GBAContext* context, const char* path) {
139	context->bios = VFileOpen(path, O_RDONLY);
140	if (!context->bios) {
141		return false;
142	}
143
144	if (!GBAIsBIOS(context->bios)) {
145		context->bios->close(context->bios);
146		context->bios = 0;
147		return false;
148	}
149	return true;
150}
151
152bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios) {
153	context->bios = bios;
154	if (!GBAIsBIOS(context->bios)) {
155		context->bios = 0;
156		return false;
157	}
158	return true;
159}
160
161bool GBAContextStart(struct GBAContext* context) {
162	struct GBAOptions opts = { .bios = 0 };
163
164	if (context->renderer) {
165		GBAVideoAssociateRenderer(&context->gba->video, context->renderer);
166	}
167
168	if (!GBALoadROM(context->gba, context->rom, context->save, context->fname)) {
169		return false;
170	}
171
172	GBAConfigMap(&context->config, &opts);
173
174	if (!context->bios && opts.bios) {
175		GBAContextLoadBIOS(context, opts.bios);
176	}
177	if (opts.useBios && context->bios) {
178		GBALoadBIOS(context->gba, context->bios);
179	}
180	context->gba->logLevel = opts.logLevel;
181	context->gba->idleOptimization = opts.idleOptimization;
182
183	ARMReset(context->cpu);
184
185	if (opts.skipBios) {
186		GBASkipBIOS(context->cpu);
187	}
188
189	struct GBACartridgeOverride override;
190	const struct GBACartridge* cart = (const struct GBACartridge*) context->gba->memory.rom;
191	memcpy(override.id, &cart->id, sizeof(override.id));
192	if (GBAOverrideFind(GBAConfigGetOverrides(&context->config), &override)) {
193		GBAOverrideApply(context->gba, &override);
194	}
195	GBAConfigFreeOpts(&opts);
196	return true;
197}
198
199void GBAContextStop(struct GBAContext* context) {
200	UNUSED(context);
201	// TODO?
202}
203
204void GBAContextFrame(struct GBAContext* context, uint16_t keys) {
205	int activeKeys = keys;
206	context->gba->keySource = &activeKeys;
207
208	int frameCounter = context->gba->video.frameCounter;
209	while (frameCounter == context->gba->video.frameCounter) {
210		ARMRunLoop(context->cpu);
211	}
212}
213
214static void _GBAContextLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
215	UNUSED(thread);
216	UNUSED(level);
217	// TODO: Make this local
218	if (!_logFile) {
219		return;
220	}
221	char out[256];
222	size_t len = vsnprintf(out, sizeof(out), format, args);
223	if (len >= sizeof(out)) {
224		len = sizeof(out) - 1;
225	}
226	out[len] = '\n';
227	_logFile->write(_logFile, out, len + 1);
228}