all repos — mgba @ 04d542e241fe2d2e3e5c5ba07038e868e7483457

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	struct VDir* dir = VDirOpenArchive(path);
 78	if (dir) {
 79		struct VDirEntry* de;
 80		while ((de = dir->listNext(dir))) {
 81			struct VFile* vf = dir->openFile(dir, de->name(de), O_RDONLY);
 82			if (!vf) {
 83				continue;
 84			}
 85			if (GBAIsROM(vf)) {
 86				context->rom = vf;
 87				break;
 88			}
 89			vf->close(vf);
 90		}
 91		if (!context->rom) {
 92			dir->close(dir);
 93		} else {
 94			context->romDir = dir;
 95		}
 96	} else {
 97		context->rom = VFileOpen(path, O_RDONLY);
 98	}
 99
100	if (!context->rom) {
101		return false;
102	}
103
104	if (!GBAIsROM(context->rom)) {
105		context->rom->close(context->rom);
106		context->rom = 0;
107		return false;
108	}
109
110	context->fname = path;
111	if (autoloadSave) {
112		context->save = VDirOptionalOpenFile(0, path, 0, ".sav", O_RDWR | O_CREAT);
113	}
114	return true;
115}
116
117void GBAContextUnloadROM(struct GBAContext* context) {
118	GBAUnloadROM(context->gba);
119	if (context->bios) {
120		context->bios->close(context->bios);
121		context->bios = 0;
122	}
123	if (context->rom) {
124		context->rom->close(context->rom);
125		context->rom = 0;
126	}
127	if (context->romDir) {
128		context->romDir->close(context->romDir);
129		context->romDir = 0;
130	}
131	if (context->save) {
132		context->save->close(context->save);
133		context->save = 0;
134	}
135}
136
137bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save) {
138	context->rom = rom;
139	if (!GBAIsROM(context->rom)) {
140		context->rom = 0;
141		return false;
142	}
143	context->save = save;
144	return true;
145}
146
147bool GBAContextLoadBIOS(struct GBAContext* context, const char* path) {
148	context->bios = VFileOpen(path, O_RDONLY);
149	if (!context->bios) {
150		return false;
151	}
152
153	if (!GBAIsBIOS(context->bios)) {
154		context->bios->close(context->bios);
155		context->bios = 0;
156		return false;
157	}
158	return true;
159}
160
161bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios) {
162	context->bios = bios;
163	if (!GBAIsBIOS(context->bios)) {
164		context->bios = 0;
165		return false;
166	}
167	return true;
168}
169
170bool GBAContextStart(struct GBAContext* context) {
171	struct GBAOptions opts = { .bios = 0 };
172
173	if (context->renderer) {
174		GBAVideoAssociateRenderer(&context->gba->video, context->renderer);
175	}
176
177	if (!GBALoadROM(context->gba, context->rom, context->save, context->fname)) {
178		return false;
179	}
180
181	GBAConfigMap(&context->config, &opts);
182
183	if (!context->bios && opts.bios) {
184		GBAContextLoadBIOS(context, opts.bios);
185	}
186	if (opts.useBios && context->bios) {
187		GBALoadBIOS(context->gba, context->bios);
188	}
189	context->gba->logLevel = opts.logLevel;
190	context->gba->idleOptimization = opts.idleOptimization;
191
192	GBAContextReset(context);
193
194	// TODO: Move this into GBAContextReset
195	if (opts.skipBios) {
196		GBASkipBIOS(context->gba);
197	}
198
199	struct GBACartridgeOverride override;
200	const struct GBACartridge* cart = (const struct GBACartridge*) context->gba->memory.rom;
201	memcpy(override.id, &cart->id, sizeof(override.id));
202	if (GBAOverrideFind(GBAConfigGetOverrides(&context->config), &override)) {
203		GBAOverrideApply(context->gba, &override);
204	}
205	GBAConfigFreeOpts(&opts);
206	return true;
207}
208
209void GBAContextReset(struct GBAContext* context) {
210	ARMReset(context->cpu);
211}
212
213void GBAContextStop(struct GBAContext* context) {
214	UNUSED(context);
215	// TODO?
216}
217
218void GBAContextFrame(struct GBAContext* context, uint16_t keys) {
219	int activeKeys = keys;
220	context->gba->keySource = &activeKeys;
221
222	int frameCounter = context->gba->video.frameCounter;
223	while (frameCounter == context->gba->video.frameCounter) {
224		ARMRunLoop(context->cpu);
225	}
226}
227
228static void _GBAContextLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
229	UNUSED(thread);
230	UNUSED(level);
231	// TODO: Make this local
232	if (!_logFile) {
233		return;
234	}
235	char out[256];
236	size_t len = vsnprintf(out, sizeof(out), format, args);
237	if (len >= sizeof(out)) {
238		len = sizeof(out) - 1;
239	}
240	out[len] = '\n';
241	_logFile->write(_logFile, out, len + 1);
242}