all repos — mgba @ 465dc2b40048e70380ed26a7451d208f4e643d6c

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