all repos — mgba @ 97447ffa40d6f90ae5f1daf84987e6ff7688d0b8

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#include <wiiuse/wpad.h>
 12
 13#include "util/common.h"
 14
 15#include "gba/renderers/video-software.h"
 16#include "gba/supervisor/context.h"
 17#include "util/gui.h"
 18#include "util/gui/file-select.h"
 19#include "util/gui/font.h"
 20#include "util/vfs.h"
 21
 22#define SAMPLES 1024
 23
 24static void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
 25static void GBAWiiFrame(void);
 26static bool GBAWiiLoadGame(const char* path);
 27
 28static void _postVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
 29static void _audioDMA(void);
 30static void _setRumble(struct GBARumble* rumble, int enable);
 31
 32static void _drawStart(void);
 33static void _drawEnd(void);
 34static int _pollInput(void);
 35
 36static struct GBAContext context;
 37static struct GBAVideoSoftwareRenderer renderer;
 38static struct GBAAVStream stream;
 39static struct GBARumble rumble;
 40static FILE* logfile;
 41static GXRModeObj* mode;
 42static Mtx model, view, modelview;
 43static uint16_t* texmem;
 44static GXTexObj tex;
 45
 46static void* framebuffer[2];
 47static int whichFb = 0;
 48
 49static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
 50static volatile size_t audioBufferSize = 0;
 51static volatile int currentAudioBuffer = 0;
 52
 53static struct GUIFont* font;
 54
 55int main() {
 56	VIDEO_Init();
 57	PAD_Init();
 58	WPAD_Init();
 59	AUDIO_Init(0);
 60	AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
 61	AUDIO_RegisterDMACallback(_audioDMA);
 62
 63	memset(audioBuffer, 0, sizeof(audioBuffer));
 64
 65#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
 66#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
 67#endif
 68
 69	mode = VIDEO_GetPreferredMode(0);
 70	framebuffer[0] = SYS_AllocateFramebuffer(mode);
 71	framebuffer[1] = SYS_AllocateFramebuffer(mode);
 72
 73	VIDEO_Configure(mode);
 74	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
 75	VIDEO_SetBlack(FALSE);
 76	VIDEO_Flush();
 77	VIDEO_WaitVSync();
 78	if (mode->viTVMode & VI_NON_INTERLACE) {
 79		VIDEO_WaitVSync();
 80	}
 81
 82	GXColor bg = { 0, 0, 0, 0xFF };
 83	void* fifo = memalign(32, 0x40000);
 84	memset(fifo, 0, 0x40000);
 85	GX_Init(fifo, 0x40000);
 86	GX_SetCopyClear(bg, 0x00FFFFFF);
 87	GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
 88
 89	f32 yscale = GX_GetYScaleFactor(mode->efbHeight, mode->xfbHeight);
 90	u32 xfbHeight = GX_SetDispCopyYScale(yscale);
 91	GX_SetScissor(0, 0, mode->viWidth, mode->viWidth);
 92	GX_SetDispCopySrc(0, 0, mode->fbWidth, mode->efbHeight);
 93	GX_SetDispCopyDst(mode->fbWidth, xfbHeight);
 94	GX_SetCopyFilter(mode->aa, mode->sample_pattern, GX_TRUE, mode->vfilter);
 95	GX_SetFieldMode(mode->field_rendering, ((mode->viHeight == 2 * mode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
 96
 97	GX_SetCullMode(GX_CULL_NONE);
 98	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
 99	GX_SetDispCopyGamma(GX_GM_1_0);
100
101	GX_ClearVtxDesc();
102	GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
103	GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
104
105	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
106	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
107
108	GX_SetNumChans(1);
109	GX_SetNumTexGens(1);
110	GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
111	GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
112
113	GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
114	GX_InvVtxCache();
115	GX_InvalidateTexAll();
116
117	Mtx44 proj;
118
119	guVector cam = { 0.0f, 0.0f, 0.0f };
120	guVector up = { 0.0f, 1.0f, 0.0f };
121	guVector look = { 0.0f, 0.0f, -1.0f };
122	guLookAt(view, &cam, &up, &look);
123
124	guMtxIdentity(model);
125	guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
126	guMtxConcat(view, model, modelview);
127	GX_LoadPosMtxImm(modelview, GX_PNMTX0);
128
129	texmem = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
130	memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL);
131	GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
132
133	font = GUIFontCreate();
134
135	fatInitDefault();
136
137	logfile = fopen("/mgba.log", "w");
138
139	stream.postAudioFrame = 0;
140	stream.postAudioBuffer = 0;
141	stream.postVideoFrame = _postVideoFrame;
142
143	rumble.setRumble = _setRumble;
144
145	GBAContextInit(&context, 0);
146	struct GBAOptions opts = {
147		.useBios = true,
148		.logLevel = 0,
149		.idleOptimization = IDLE_LOOP_REMOVE
150	};
151	GBAConfigLoadDefaults(&context.config, &opts);
152	context.gba->logHandler = GBAWiiLog;
153	context.gba->stream = &stream;
154	context.gba->rumble = &rumble;
155
156	GBAVideoSoftwareRendererCreate(&renderer);
157	renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
158	renderer.outputBufferStride = 256;
159	GBAVideoAssociateRenderer(&context.gba->video, &renderer.d);
160
161	GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
162
163#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
164	blip_set_rates(context.gba->audio.left,  GBA_ARM7TDMI_FREQUENCY, 48000);
165	blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000);
166#endif
167
168	char currentPath[256] = "";
169	while (true) {
170		char path[256];
171		guOrtho(proj, -20, 240, 0, 352, 0, 300);
172		GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
173		GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
174
175		struct GUIParams params = {
176			352, 230,
177			font, _drawStart, _drawEnd, _pollInput
178		};
179		if (!selectFile(&params, "/", path, currentPath, sizeof(path), GBAIsROM) || !GBAWiiLoadGame(path)) {
180			break;
181		}
182		GBAContextStart(&context);
183
184		guOrtho(proj, -10, VIDEO_VERTICAL_PIXELS + 10, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300);
185		GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
186		GX_SetVtxDesc(GX_VA_CLR0, GX_NONE);
187
188		while (true) {
189	#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
190			int available = blip_samples_avail(context.gba->audio.left);
191			if (available + audioBufferSize > SAMPLES) {
192				available = SAMPLES - audioBufferSize;
193			}
194			available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
195			if (available > 0) {
196				blip_read_samples(context.gba->audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
197				blip_read_samples(context.gba->audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
198				audioBufferSize += available;
199			}
200			if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
201				_audioDMA();
202				AUDIO_StartDMA();
203			}
204	#endif
205			PAD_ScanPads();
206			u16 padkeys = PAD_ButtonsHeld(0);
207			WPAD_ScanPads();
208			u32 wiiPad = WPAD_ButtonsHeld(0);
209			u32 ext = 0;
210			uint16_t keys = 0;
211			WPAD_Probe(0, &ext);
212
213			if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) || 
214			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
215				keys |= 1 << GBA_KEY_A;
216			}
217			if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) ||
218			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
219				keys |= 1 << GBA_KEY_B;
220			}
221			if ((padkeys & PAD_TRIGGER_L) || (wiiPad & WPAD_BUTTON_B) ||
222			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_L))) {
223				keys |= 1 << GBA_KEY_L;
224			}
225			if ((padkeys & PAD_TRIGGER_R) || (wiiPad & WPAD_BUTTON_A) ||
226			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_R))) {
227				keys |= 1 << GBA_KEY_R;
228			}
229			if ((padkeys & PAD_BUTTON_START) || (wiiPad & WPAD_BUTTON_PLUS) ||
230			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_PLUS))) {
231				keys |= 1 << GBA_KEY_START;
232			}
233			if ((padkeys & (PAD_BUTTON_X | PAD_BUTTON_Y)) || (wiiPad & WPAD_BUTTON_MINUS) ||
234			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_MINUS))) {
235				keys |= 1 << GBA_KEY_SELECT;
236			}
237			if ((padkeys & PAD_BUTTON_LEFT) || (wiiPad & WPAD_BUTTON_UP) ||
238			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
239				keys |= 1 << GBA_KEY_LEFT;
240			}
241			if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
242			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
243				keys |= 1 << GBA_KEY_RIGHT;
244			}
245			if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
246			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
247				keys |= 1 << GBA_KEY_UP;
248			}
249			if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
250			    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
251				keys |= 1 << GBA_KEY_DOWN;
252			}
253			int x = PAD_StickX(0);
254			int y = PAD_StickY(0);
255			if (x < -0x40) {
256				keys |= 1 << GBA_KEY_LEFT;
257			}
258			if (x > 0x40) {
259				keys |= 1 << GBA_KEY_RIGHT;
260			}
261			if (y < -0x40) {
262				keys |= 1 << GBA_KEY_DOWN;
263			}
264			if (y > 0x40) {
265				keys |= 1 << GBA_KEY_UP;
266			}
267			if ((padkeys & PAD_TRIGGER_Z) || (wiiPad & WPAD_BUTTON_HOME) || (wiiPad & WPAD_CLASSIC_BUTTON_HOME)) {
268				break;
269			}
270			GBAContextFrame(&context, keys);
271		}
272		AUDIO_StopDMA();
273		GBAContextStop(&context);
274	}
275
276	fclose(logfile);
277	free(fifo);
278
279	GBAContextDeinit(&context);
280
281	free(renderer.outputBuffer);
282	GUIFontDestroy(font);
283
284	return 0;
285}
286
287static void GBAWiiFrame(void) {
288	size_t x, y;
289	uint64_t* texdest = (uint64_t*) texmem;
290	uint64_t* texsrc = (uint64_t*) renderer.outputBuffer;
291	for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
292		for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 2; ++x) {
293			texdest[0 + x * 4 + y * 64] = texsrc[0   + x + y * 64];
294			texdest[1 + x * 4 + y * 64] = texsrc[64  + x + y * 64];
295			texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
296			texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
297		}
298	}
299	DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
300
301	_drawStart();
302
303	GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_SET);
304	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
305	GX_InvalidateTexAll();
306	GX_LoadTexObj(&tex, GX_TEXMAP0);
307
308	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
309	GX_Position2s16(0, 256);
310	GX_TexCoord2s16(0, 1);
311
312	GX_Position2s16(256, 256);
313	GX_TexCoord2s16(1, 1);
314
315	GX_Position2s16(256, 0);
316	GX_TexCoord2s16(1, 0);
317
318	GX_Position2s16(0, 0);
319	GX_TexCoord2s16(0, 0);
320	GX_End();
321
322	_drawEnd();
323}
324
325bool GBAWiiLoadGame(const char* path) {
326	_drawStart();
327	GUIFontPrintf(font, 176, 120, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading...");
328	_drawEnd();
329
330	return GBAContextLoadROM(&context, path, true);
331}
332
333void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
334	UNUSED(thread);
335	UNUSED(level);
336	vfprintf(logfile, format, args);
337	fprintf(logfile, "\n");
338	fflush(logfile);
339}
340
341static void _postVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
342	UNUSED(stream);
343	UNUSED(renderer);
344	GBAWiiFrame();
345}
346
347static void _audioDMA(void) {
348	if (!audioBufferSize) {
349		return;
350	}
351	DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
352	AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
353	currentAudioBuffer = (currentAudioBuffer + 1) % 3;
354	audioBufferSize = 0;
355}
356
357static void _drawStart(void) {
358	VIDEO_WaitVSync();
359	GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
360	GX_SetColorUpdate(GX_TRUE);
361
362	GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
363}
364
365static void _drawEnd(void) {
366	GX_DrawDone();
367
368	whichFb = !whichFb;
369
370	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
371	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
372	VIDEO_Flush();
373}
374
375static int _pollInput(void) {
376	PAD_ScanPads();
377	u16 padkeys = PAD_ButtonsHeld(0);
378
379	WPAD_ScanPads();
380	u32 wiiPad = WPAD_ButtonsHeld(0);
381	u32 ext = 0;
382	WPAD_Probe(0, &ext);
383
384	int keys = 0;
385	int x = PAD_StickX(0);
386	int y = PAD_StickY(0);
387	if (x < -0x40) {
388		keys |= 1 << GUI_INPUT_LEFT;
389	}
390	if (x > 0x40) {
391		keys |= 1 << GUI_INPUT_RIGHT;
392	}
393	if (y < -0x40) {
394		keys |= 1 << GUI_INPUT_DOWN;
395	}
396	if (y > 0x40) {
397		keys |= 1 << GUI_INPUT_UP;
398	}
399	if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) || 
400	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
401		keys |= 1 << GUI_INPUT_SELECT;
402	}
403	if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) ||
404	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
405		keys |= 1 << GUI_INPUT_BACK;
406	}
407	if ((padkeys & PAD_TRIGGER_Z) || (wiiPad & WPAD_BUTTON_HOME) ||
408	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_HOME)))) {
409		keys |= 1 << GUI_INPUT_CANCEL;
410	}
411	if ((padkeys & PAD_BUTTON_LEFT)|| (wiiPad & WPAD_BUTTON_UP) ||
412	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
413		keys |= 1 << GUI_INPUT_LEFT;
414	}
415	if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
416	   ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
417		keys |= 1 << GUI_INPUT_RIGHT;
418	}
419	if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
420	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
421		keys |= 1 << GUI_INPUT_UP;
422	}
423	if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
424	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
425		keys |= 1 << GUI_INPUT_DOWN;
426	}
427	return keys;
428}
429
430void _setRumble(struct GBARumble* rumble, int enable) {
431	UNUSED(rumble);
432	WPAD_Rumble(0, enable);
433	if (enable) {
434		PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
435	} else {
436		PAD_ControlMotor(0, PAD_MOTOR_STOP);
437	}
438}