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