all repos — mgba @ c14da05d8dca225010677643c32fea5c0ac8517a

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