all repos — mgba @ 29556f45a4a1753c5cab239c26b5b8c1cd4e1d78

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