all repos — mgba @ 52048db173c8d41403a7c203bc402d7ffbe5627f

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/context/context.h"
 17#include "gba/gui/gui-runner.h"
 18#include "util/gui.h"
 19#include "util/gui/file-select.h"
 20#include "util/gui/font.h"
 21#include "util/vfs.h"
 22
 23#define SAMPLES 1024
 24
 25static void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
 26
 27static void _audioDMA(void);
 28static void _setRumble(struct GBARumble* rumble, int enable);
 29static void _sampleRotation(struct GBARotationSource* source);
 30static int32_t _readTiltX(struct GBARotationSource* source);
 31static int32_t _readTiltY(struct GBARotationSource* source);
 32static int32_t _readGyroZ(struct GBARotationSource* source);
 33
 34static void _drawStart(void);
 35static void _drawEnd(void);
 36static int _pollInput(void);
 37static void _guiPrepare(void);
 38static void _guiFinish(void);
 39
 40static void _setup(struct GBAGUIRunner* runner);
 41static void _gameLoaded(struct GBAGUIRunner* runner);
 42static void _gameUnloaded(struct GBAGUIRunner* runner);
 43static void _prepareForFrame(struct GBAGUIRunner* runner);
 44static void _drawFrame(struct GBAGUIRunner* runner, bool faded);
 45static uint16_t _pollGameInput(struct GBAGUIRunner* runner);
 46
 47static struct GBAVideoSoftwareRenderer renderer;
 48static struct GBARumble rumble;
 49static struct GBARotationSource rotation;
 50static FILE* logfile;
 51static GXRModeObj* mode;
 52static Mtx model, view, modelview;
 53static uint16_t* texmem;
 54static GXTexObj tex;
 55static int32_t tiltX;
 56static int32_t tiltY;
 57static int32_t gyroZ;
 58
 59static void* framebuffer[2];
 60static int whichFb = 0;
 61
 62static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
 63static volatile size_t audioBufferSize = 0;
 64static volatile int currentAudioBuffer = 0;
 65
 66static struct GUIFont* font;
 67
 68int main() {
 69	VIDEO_Init();
 70	PAD_Init();
 71	WPAD_Init();
 72	AUDIO_Init(0);
 73	AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
 74	AUDIO_RegisterDMACallback(_audioDMA);
 75
 76	memset(audioBuffer, 0, sizeof(audioBuffer));
 77
 78#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
 79#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
 80#endif
 81
 82	mode = VIDEO_GetPreferredMode(0);
 83	framebuffer[0] = SYS_AllocateFramebuffer(mode);
 84	framebuffer[1] = SYS_AllocateFramebuffer(mode);
 85
 86	VIDEO_Configure(mode);
 87	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
 88	VIDEO_SetBlack(FALSE);
 89	VIDEO_Flush();
 90	VIDEO_WaitVSync();
 91	if (mode->viTVMode & VI_NON_INTERLACE) {
 92		VIDEO_WaitVSync();
 93	}
 94
 95	GXColor bg = { 0, 0, 0, 0xFF };
 96	void* fifo = memalign(32, 0x40000);
 97	memset(fifo, 0, 0x40000);
 98	GX_Init(fifo, 0x40000);
 99	GX_SetCopyClear(bg, 0x00FFFFFF);
100	GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
101
102	f32 yscale = GX_GetYScaleFactor(mode->efbHeight, mode->xfbHeight);
103	u32 xfbHeight = GX_SetDispCopyYScale(yscale);
104	GX_SetScissor(0, 0, mode->viWidth, mode->viWidth);
105	GX_SetDispCopySrc(0, 0, mode->fbWidth, mode->efbHeight);
106	GX_SetDispCopyDst(mode->fbWidth, xfbHeight);
107	GX_SetCopyFilter(mode->aa, mode->sample_pattern, GX_TRUE, mode->vfilter);
108	GX_SetFieldMode(mode->field_rendering, ((mode->viHeight == 2 * mode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
109
110	GX_SetCullMode(GX_CULL_NONE);
111	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
112	GX_SetDispCopyGamma(GX_GM_1_0);
113
114	GX_ClearVtxDesc();
115	GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
116	GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
117	GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
118
119	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
120	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
121	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
122
123	GX_SetNumChans(1);
124	GX_SetNumTexGens(1);
125	GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
126	GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
127
128	GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
129	GX_InvVtxCache();
130	GX_InvalidateTexAll();
131
132	guVector cam = { 0.0f, 0.0f, 0.0f };
133	guVector up = { 0.0f, 1.0f, 0.0f };
134	guVector look = { 0.0f, 0.0f, -1.0f };
135	guLookAt(view, &cam, &up, &look);
136
137	guMtxIdentity(model);
138	guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
139	guMtxConcat(view, model, modelview);
140	GX_LoadPosMtxImm(modelview, GX_PNMTX0);
141
142	texmem = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
143	memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL);
144	GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
145
146	font = GUIFontCreate();
147
148	fatInitDefault();
149
150	logfile = fopen("/mgba.log", "w");
151
152	rumble.setRumble = _setRumble;
153
154	rotation.sample = _sampleRotation;
155	rotation.readTiltX = _readTiltX;
156	rotation.readTiltY = _readTiltY;
157	rotation.readGyroZ = _readGyroZ;
158
159	struct GBAGUIRunner runner = {
160		.params = {
161			352, 230,
162			font, "/",
163			_drawStart, _drawEnd, _pollInput,
164			_guiPrepare, _guiFinish,
165
166			GUI_PARAMS_TRAIL
167		},
168		.setup = _setup,
169		.teardown = 0,
170		.gameLoaded = _gameLoaded,
171		.gameUnloaded = _gameUnloaded,
172		.prepareForFrame = _prepareForFrame,
173		.drawFrame = _drawFrame,
174		.pollGameInput = _pollGameInput
175	};
176	GBAGUIInit(&runner, 0);
177	GBAGUIRunloop(&runner);
178	GBAGUIDeinit(&runner);
179
180	fclose(logfile);
181	free(fifo);
182
183	free(renderer.outputBuffer);
184	GUIFontDestroy(font);
185
186	return 0;
187}
188
189void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
190	UNUSED(thread);
191	UNUSED(level);
192	if (!logfile) {
193		return;
194	}
195	vfprintf(logfile, format, args);
196	fprintf(logfile, "\n");
197	fflush(logfile);
198}
199
200static void _audioDMA(void) {
201	if (!audioBufferSize) {
202		return;
203	}
204	DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
205	AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
206	currentAudioBuffer = (currentAudioBuffer + 1) % 3;
207	audioBufferSize = 0;
208}
209
210static void _drawStart(void) {
211	VIDEO_WaitVSync();
212	GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
213	GX_SetColorUpdate(GX_TRUE);
214
215	GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
216}
217
218static void _drawEnd(void) {
219	GX_DrawDone();
220
221	whichFb = !whichFb;
222
223	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
224	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
225	VIDEO_Flush();
226}
227
228static int _pollInput(void) {
229	PAD_ScanPads();
230	u16 padkeys = PAD_ButtonsHeld(0);
231
232	WPAD_ScanPads();
233	u32 wiiPad = WPAD_ButtonsHeld(0);
234	u32 ext = 0;
235	WPAD_Probe(0, &ext);
236
237	int keys = 0;
238	int x = PAD_StickX(0);
239	int y = PAD_StickY(0);
240	if (x < -0x40) {
241		keys |= 1 << GUI_INPUT_LEFT;
242	}
243	if (x > 0x40) {
244		keys |= 1 << GUI_INPUT_RIGHT;
245	}
246	if (y < -0x40) {
247		keys |= 1 << GUI_INPUT_DOWN;
248	}
249	if (y > 0x40) {
250		keys |= 1 << GUI_INPUT_UP;
251	}
252	if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) || 
253	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
254		keys |= 1 << GUI_INPUT_SELECT;
255	}
256	if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) ||
257	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
258		keys |= 1 << GUI_INPUT_BACK;
259	}
260	if ((padkeys & PAD_TRIGGER_Z) || (wiiPad & WPAD_BUTTON_HOME) ||
261	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_HOME)))) {
262		keys |= 1 << GUI_INPUT_CANCEL;
263	}
264	if ((padkeys & PAD_BUTTON_LEFT)|| (wiiPad & WPAD_BUTTON_UP) ||
265	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
266		keys |= 1 << GUI_INPUT_LEFT;
267	}
268	if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
269	   ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
270		keys |= 1 << GUI_INPUT_RIGHT;
271	}
272	if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
273	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
274		keys |= 1 << GUI_INPUT_UP;
275	}
276	if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
277	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
278		keys |= 1 << GUI_INPUT_DOWN;
279	}
280	return keys;
281}
282
283void _guiPrepare(void) {
284	Mtx44 proj;
285	guOrtho(proj, -20, 240, 0, 352, 0, 300);
286	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
287}
288
289void _guiFinish(void) {
290	Mtx44 proj;
291	guOrtho(proj, -10, VIDEO_VERTICAL_PIXELS + 10, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300);
292	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
293}
294
295void _setup(struct GBAGUIRunner* runner) {
296	struct GBAOptions opts = {
297		.useBios = true,
298		.logLevel = 0,
299		.idleOptimization = IDLE_LOOP_DETECT
300	};
301	GBAConfigLoadDefaults(&runner->context.config, &opts);
302	runner->context.gba->logHandler = GBAWiiLog;
303	runner->context.gba->rumble = &rumble;
304	runner->context.gba->rotationSource = &rotation;
305
306	GBAVideoSoftwareRendererCreate(&renderer);
307	renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
308	renderer.outputBufferStride = 256;
309	runner->context.renderer = &renderer.d;
310
311	GBAAudioResizeBuffer(&runner->context.gba->audio, SAMPLES);
312
313#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
314	blip_set_rates(runner->context.gba->audio.left,  GBA_ARM7TDMI_FREQUENCY, 48000);
315	blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000);
316#endif
317}
318
319void _gameUnloaded(struct GBAGUIRunner* runner) {
320	UNUSED(runner);
321	AUDIO_StopDMA();
322}
323
324void _gameLoaded(struct GBAGUIRunner* runner) {
325	WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC);
326	if (runner->context.gba->memory.hw.devices & HW_GYRO) {
327		int i;
328		for (i = 0; i < 6; ++i) {
329			u32 result = WPAD_SetMotionPlus(0, 1);
330			if (result == WPAD_ERR_NONE) {
331				break;
332			}
333			sleep(1);
334		}
335	}
336}
337
338void _prepareForFrame(struct GBAGUIRunner* runner) {
339#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
340	int available = blip_samples_avail(runner->context.gba->audio.left);
341	if (available + audioBufferSize > SAMPLES) {
342		available = SAMPLES - audioBufferSize;
343	}
344	available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
345	if (available > 0) {
346		blip_read_samples(runner->context.gba->audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
347		blip_read_samples(runner->context.gba->audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
348		audioBufferSize += available;
349	}
350	if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
351		_audioDMA();
352		AUDIO_StartDMA();
353	}
354#endif
355}
356
357void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
358	UNUSED(runner);
359	uint32_t color = 0xFFFFFF3F;
360	if (!faded) {
361		color |= 0xC0;
362	}
363	size_t x, y;
364	uint64_t* texdest = (uint64_t*) texmem;
365	uint64_t* texsrc = (uint64_t*) renderer.outputBuffer;
366	for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
367		for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 2; ++x) {
368			texdest[0 + x * 4 + y * 64] = texsrc[0   + x + y * 64];
369			texdest[1 + x * 4 + y * 64] = texsrc[64  + x + y * 64];
370			texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
371			texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
372		}
373	}
374	DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
375
376	_drawStart();
377
378	if (faded) {
379		GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
380	} else {
381		GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
382	}
383	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
384	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
385	GX_InvalidateTexAll();
386	GX_LoadTexObj(&tex, GX_TEXMAP0);
387
388	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
389	GX_Position2s16(0, 256);
390	GX_Color1u32(color);
391	GX_TexCoord2s16(0, 1);
392
393	GX_Position2s16(256, 256);
394	GX_Color1u32(color);
395	GX_TexCoord2s16(1, 1);
396
397	GX_Position2s16(256, 0);
398	GX_Color1u32(color);
399	GX_TexCoord2s16(1, 0);
400
401	GX_Position2s16(0, 0);
402	GX_Color1u32(color);
403	GX_TexCoord2s16(0, 0);
404	GX_End();
405
406	_drawEnd();
407}
408
409uint16_t _pollGameInput(struct GBAGUIRunner* runner) {
410	UNUSED(runner);
411	PAD_ScanPads();
412	u16 padkeys = PAD_ButtonsHeld(0);
413	WPAD_ScanPads();
414	u32 wiiPad = WPAD_ButtonsHeld(0);
415	u32 ext = 0;
416	uint16_t keys = 0;
417	WPAD_Probe(0, &ext);
418
419	if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) || 
420	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_A | WPAD_CLASSIC_BUTTON_Y)))) {
421		keys |= 1 << GBA_KEY_A;
422	}
423	if ((padkeys & PAD_BUTTON_B) || (wiiPad & WPAD_BUTTON_1) ||
424	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & (WPAD_CLASSIC_BUTTON_B | WPAD_CLASSIC_BUTTON_X)))) {
425		keys |= 1 << GBA_KEY_B;
426	}
427	if ((padkeys & PAD_TRIGGER_L) || (wiiPad & WPAD_BUTTON_B) ||
428	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_L))) {
429		keys |= 1 << GBA_KEY_L;
430	}
431	if ((padkeys & PAD_TRIGGER_R) || (wiiPad & WPAD_BUTTON_A) ||
432	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_FULL_R))) {
433		keys |= 1 << GBA_KEY_R;
434	}
435	if ((padkeys & PAD_BUTTON_START) || (wiiPad & WPAD_BUTTON_PLUS) ||
436	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_PLUS))) {
437		keys |= 1 << GBA_KEY_START;
438	}
439	if ((padkeys & (PAD_BUTTON_X | PAD_BUTTON_Y)) || (wiiPad & WPAD_BUTTON_MINUS) ||
440	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_MINUS))) {
441		keys |= 1 << GBA_KEY_SELECT;
442	}
443	if ((padkeys & PAD_BUTTON_LEFT) || (wiiPad & WPAD_BUTTON_UP) ||
444	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_LEFT))) {
445		keys |= 1 << GBA_KEY_LEFT;
446	}
447	if ((padkeys & PAD_BUTTON_RIGHT) || (wiiPad & WPAD_BUTTON_DOWN) ||
448	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_RIGHT))) {
449		keys |= 1 << GBA_KEY_RIGHT;
450	}
451	if ((padkeys & PAD_BUTTON_UP) || (wiiPad & WPAD_BUTTON_RIGHT) ||
452	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_UP))) {
453		keys |= 1 << GBA_KEY_UP;
454	}
455	if ((padkeys & PAD_BUTTON_DOWN) || (wiiPad & WPAD_BUTTON_LEFT) ||
456	    ((ext == WPAD_EXP_CLASSIC) && (wiiPad & WPAD_CLASSIC_BUTTON_DOWN))) {
457		keys |= 1 << GBA_KEY_DOWN;
458	}
459	int x = PAD_StickX(0);
460	int y = PAD_StickY(0);
461	if (x < -0x40) {
462		keys |= 1 << GBA_KEY_LEFT;
463	}
464	if (x > 0x40) {
465		keys |= 1 << GBA_KEY_RIGHT;
466	}
467	if (y < -0x40) {
468		keys |= 1 << GBA_KEY_DOWN;
469	}
470	if (y > 0x40) {
471		keys |= 1 << GBA_KEY_UP;
472	}
473	return keys;
474}
475
476void _setRumble(struct GBARumble* rumble, int enable) {
477	UNUSED(rumble);
478	WPAD_Rumble(0, enable);
479	if (enable) {
480		PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
481	} else {
482		PAD_ControlMotor(0, PAD_MOTOR_STOP);
483	}
484}
485
486void _sampleRotation(struct GBARotationSource* source) {
487	UNUSED(source);
488	vec3w_t accel;
489	WPAD_Accel(0, &accel);
490	// These are swapped
491	tiltX = (accel.y - 0x1EA) << 22;
492	tiltY = (accel.x - 0x1EA) << 22;
493
494	// This doesn't seem to work at all with -TR remotes
495	struct expansion_t exp;
496	WPAD_Expansion(0, &exp);
497	if (exp.type != EXP_MOTION_PLUS) {
498		return;
499	}
500	gyroZ = exp.mp.rz - 0x1FA0;
501	gyroZ <<= 18;
502}
503
504int32_t _readTiltX(struct GBARotationSource* source) {
505	UNUSED(source);
506	return tiltX;
507}
508
509int32_t _readTiltY(struct GBARotationSource* source) {
510	UNUSED(source);
511	return tiltY;
512}
513
514int32_t _readGyroZ(struct GBARotationSource* source) {
515	UNUSED(source);
516	return gyroZ;
517}