all repos — mgba @ dabd72ac13c0024b20cf18b050b25a25b385e4a0

mGBA Game Boy Advance Emulator

src/platform/wii/main.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#define asm __asm__
  7
  8#include <fat.h>
  9#include <gccore.h>
 10#include <malloc.h>
 11
 12#include "util/common.h"
 13
 14#include "gba/gba.h"
 15#include "gba/renderers/video-software.h"
 16#include "gba/serialize.h"
 17#include "gba/supervisor/overrides.h"
 18#include "gba/video.h"
 19#include "util/vfs.h"
 20
 21#define SAMPLES 1024
 22
 23static void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
 24static void GBAWiiFrame(void);
 25static bool GBAWiiLoadGame(const char* path);
 26
 27static void _postVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
 28static void _audioDMA(void);
 29
 30static struct GBA gba;
 31static struct ARMCore cpu;
 32static struct GBAVideoSoftwareRenderer renderer;
 33static struct VFile* rom;
 34static struct VFile* save;
 35static struct GBAAVStream stream;
 36static FILE* logfile;
 37static GXRModeObj* mode;
 38static Mtx model, view, modelview;
 39static uint16_t* texmem;
 40static GXTexObj tex;
 41
 42static void* framebuffer[2];
 43static int whichFb = 0;
 44
 45static struct GBAStereoSample audioBuffer[2][SAMPLES] __attribute__ ((__aligned__(32)));
 46static size_t audioBufferSize = 0;
 47static int currentAudioBuffer = 0;
 48
 49int main() {
 50	VIDEO_Init();
 51	PAD_Init();
 52	AUDIO_Init(0);
 53	AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
 54	AUDIO_RegisterDMACallback(_audioDMA);
 55
 56	memset(audioBuffer, 0, sizeof(audioBuffer));
 57
 58#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
 59#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
 60#endif
 61
 62	mode = VIDEO_GetPreferredMode(0);
 63	framebuffer[0] = SYS_AllocateFramebuffer(mode);
 64	framebuffer[1] = SYS_AllocateFramebuffer(mode);
 65
 66	VIDEO_Configure(mode);
 67	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
 68	VIDEO_SetBlack(FALSE);
 69	VIDEO_Flush();
 70	VIDEO_WaitVSync();
 71	if (mode->viTVMode & VI_NON_INTERLACE) {
 72		VIDEO_WaitVSync();
 73	}
 74
 75	GXColor bg = { 0, 0, 0, 0xFF };
 76	void* fifo = memalign(32, 0x40000);
 77	memset(fifo, 0, 0x40000);
 78	GX_Init(fifo, 0x40000);
 79	GX_SetCopyClear(bg, 0x00FFFFFF);
 80	GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
 81
 82	f32 yscale = GX_GetYScaleFactor(mode->efbHeight, mode->xfbHeight);
 83	u32 xfbHeight = GX_SetDispCopyYScale(yscale);
 84	GX_SetScissor(0, 0, mode->viWidth, mode->viWidth);
 85	GX_SetDispCopySrc(0, 0, mode->fbWidth, mode->efbHeight);
 86	GX_SetDispCopyDst(mode->fbWidth, xfbHeight);
 87	GX_SetCopyFilter(mode->aa, mode->sample_pattern, GX_TRUE, mode->vfilter);
 88	GX_SetFieldMode(mode->field_rendering, ((mode->viHeight == 2 * mode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
 89
 90	GX_SetCullMode(GX_CULL_NONE);
 91	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
 92	GX_SetDispCopyGamma(GX_GM_1_0);
 93
 94	GX_ClearVtxDesc();
 95	GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
 96	GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
 97
 98	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
 99	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
100
101	GX_SetNumChans(1);
102	GX_SetNumTexGens(1);
103	GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
104	GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE);
105
106	GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
107	GX_InvVtxCache();
108	GX_InvalidateTexAll();
109
110	Mtx44 proj;
111	guOrtho(proj, 0, VIDEO_VERTICAL_PIXELS, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300);
112	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
113
114	guVector cam = { 0.0f, 0.0f, 0.0f };
115	guVector up = { 0.0f, 1.0f, 0.0f };
116	guVector look = { 0.0f, 0.0f, -1.0f };
117	guLookAt(view, &cam, &up, &look);
118
119	guMtxIdentity(model);
120	guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
121	guMtxConcat(view, model, modelview);
122	GX_LoadPosMtxImm(modelview, GX_PNMTX0);
123
124	texmem = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
125	memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL);
126	GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
127
128	fatInitDefault();
129
130	logfile = fopen("/mgba.log", "w");
131
132	stream.postAudioFrame = 0;
133	stream.postAudioBuffer = 0;
134	stream.postVideoFrame = _postVideoFrame;
135
136	GBACreate(&gba);
137	ARMSetComponents(&cpu, &gba.d, 0, 0);
138	ARMInit(&cpu);
139	gba.logLevel = 0; // TODO: Settings
140	gba.logHandler = GBAWiiLog;
141	gba.stream = &stream;
142	gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings
143	rom = 0;
144
145	GBAVideoSoftwareRendererCreate(&renderer);
146	renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
147	renderer.outputBufferStride = 256;
148	GBAVideoAssociateRenderer(&gba.video, &renderer.d);
149
150	GBAAudioResizeBuffer(&gba.audio, SAMPLES);
151
152#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
153	blip_set_rates(gba.audio.left,  GBA_ARM7TDMI_FREQUENCY, 48000);
154	blip_set_rates(gba.audio.right, GBA_ARM7TDMI_FREQUENCY, 48000);
155#endif
156
157	if (!GBAWiiLoadGame("/rom.gba")) {
158		return 1;
159	}
160
161	while (true) {
162#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
163		int available = blip_samples_avail(gba.audio.left);
164		if (available + audioBufferSize > SAMPLES) {
165			available = SAMPLES - audioBufferSize;
166		}
167		if (available > 0) {
168			blip_read_samples(gba.audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
169			blip_read_samples(gba.audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
170			audioBufferSize += available;
171		}
172		if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
173			_audioDMA();
174			AUDIO_StartDMA();
175		}
176#endif
177		PAD_ScanPads();
178		u16 padkeys = PAD_ButtonsHeld(0);
179		int keys = 0;
180		gba.keySource = &keys;
181		if (padkeys & PAD_BUTTON_A) {
182			keys |= 1 << GBA_KEY_A;
183		}
184		if (padkeys & PAD_BUTTON_B) {
185			keys |= 1 << GBA_KEY_B;
186		}
187		if (padkeys & PAD_TRIGGER_L) {
188			keys |= 1 << GBA_KEY_L;
189		}
190		if (padkeys & PAD_TRIGGER_R) {
191			keys |= 1 << GBA_KEY_R;
192		}
193		if (padkeys & PAD_BUTTON_START) {
194			keys |= 1 << GBA_KEY_START;
195		}
196		if (padkeys & (PAD_BUTTON_X | PAD_BUTTON_Y)) {
197			keys |= 1 << GBA_KEY_SELECT;
198		}
199		if (padkeys & PAD_BUTTON_LEFT) {
200			keys |= 1 << GBA_KEY_LEFT;
201		}
202		if (padkeys & PAD_BUTTON_RIGHT) {
203			keys |= 1 << GBA_KEY_RIGHT;
204		}
205		if (padkeys & PAD_BUTTON_UP) {
206			keys |= 1 << GBA_KEY_UP;
207		}
208		if (padkeys & PAD_BUTTON_DOWN) {
209			keys |= 1 << GBA_KEY_DOWN;
210		}
211		if (padkeys & PAD_TRIGGER_Z) {
212			break;
213		}
214		int frameCount = gba.video.frameCounter;
215		while (gba.video.frameCounter == frameCount) {
216			ARMRunLoop(&cpu);
217		}
218	}
219
220	fclose(logfile);
221	free(fifo);
222
223	rom->close(rom);
224	save->close(save);
225
226	return 0;
227}
228
229static void GBAWiiFrame(void) {
230	size_t x, y;
231	uint64_t* texdest = (uint64_t*) texmem;
232	uint64_t* texsrc = (uint64_t*) renderer.outputBuffer;
233	for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
234		for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 2; ++x) {
235			texdest[0 + x * 4 + y * 64] = texsrc[0   + x + y * 64];
236			texdest[1 + x * 4 + y * 64] = texsrc[64  + x + y * 64];
237			texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
238			texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
239		}
240	}
241	DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
242
243	GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
244	GX_SetColorUpdate(GX_TRUE);
245
246	GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
247	GX_InvalidateTexAll();
248	GX_LoadTexObj(&tex, GX_TEXMAP0);
249
250	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
251	GX_Position2s16(0, 256);
252	GX_TexCoord2s16(0, 1);
253
254	GX_Position2s16(256, 256);
255	GX_TexCoord2s16(1, 1);
256
257	GX_Position2s16(256, 0);
258	GX_TexCoord2s16(1, 0);
259
260	GX_Position2s16(0, 0);
261	GX_TexCoord2s16(0, 0);
262	GX_End();
263
264	GX_DrawDone();
265
266	whichFb = !whichFb;
267
268	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
269	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
270	VIDEO_Flush();
271	VIDEO_WaitVSync();
272}
273
274bool GBAWiiLoadGame(const char* path) {
275	rom = VFileOpen(path, O_RDONLY);
276
277	if (!rom) {
278		return false;
279	}
280	if (!GBAIsROM(rom)) {
281		return false;
282	}
283
284	save = VFileOpen("test.sav", O_RDWR | O_CREAT);
285
286	GBALoadROM(&gba, rom, save, path);
287
288	struct GBACartridgeOverride override;
289	const struct GBACartridge* cart = (const struct GBACartridge*) gba.memory.rom;
290	memcpy(override.id, &cart->id, sizeof(override.id));
291	if (GBAOverrideFind(0, &override)) {
292		GBAOverrideApply(&gba, &override);
293	}
294
295	ARMReset(&cpu);
296	return true;
297}
298
299void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
300	UNUSED(thread);
301	UNUSED(level);
302	vfprintf(logfile, format, args);
303	fprintf(logfile, "\n");
304	fflush(logfile);
305}
306
307static void _postVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
308	UNUSED(stream);
309	UNUSED(renderer);
310	GBAWiiFrame();
311}
312
313static void _audioDMA(void) {
314	if (!audioBufferSize) {
315		return;
316	}
317	currentAudioBuffer = !currentAudioBuffer;
318	DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
319	AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
320	audioBufferSize = 0;
321}