all repos — mgba @ 8e5df9473d5f26057c9f37c9bf76362036f80795

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