all repos — mgba @ 811d8281c3f285e9e7421cf290ecefaefb46069d

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