all repos — mgba @ d18177888339cfc7892b186741e4de603d05b7f4

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 <ogc/machine/processor.h>
  11#include <malloc.h>
  12#include <unistd.h>
  13#include <wiiuse/wpad.h>
  14
  15#include <mgba-util/common.h>
  16
  17#include <mgba/core/blip_buf.h>
  18#include <mgba/core/core.h>
  19#include "feature/gui/gui-runner.h"
  20#include <mgba/internal/gba/audio.h>
  21#include <mgba/internal/gba/gba.h>
  22#include <mgba/internal/gba/input.h>
  23#include <mgba-util/gui.h>
  24#include <mgba-util/gui/file-select.h>
  25#include <mgba-util/gui/font.h>
  26#include <mgba-util/gui/menu.h>
  27#include <mgba-util/memory.h>
  28#include <mgba-util/vfs.h>
  29
  30#define GCN1_INPUT 0x47434E31
  31#define GCN2_INPUT 0x47434E32
  32#define WIIMOTE_INPUT 0x5749494D
  33#define CLASSIC_INPUT 0x57494943
  34
  35#define TEX_W 256
  36#define TEX_H 224
  37
  38static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
  39	mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
  40}
  41
  42static enum ScreenMode {
  43	SM_PA,
  44	SM_SF,
  45	SM_MAX
  46} screenMode = SM_PA;
  47
  48static enum FilterMode {
  49	FM_NEAREST,
  50	FM_LINEAR_1x,
  51	FM_LINEAR_2x,
  52	FM_MAX
  53} filterMode = FM_NEAREST;
  54
  55static enum VideoMode {
  56	VM_AUTODETECT,
  57	VM_480i,
  58	VM_480p,
  59	VM_240p,
  60	// TODO: PAL support
  61	VM_MAX
  62} videoMode = VM_AUTODETECT;
  63
  64#define SAMPLES 1024
  65#define GUI_SCALE 1.35f
  66#define GUI_SCALE_240p 2.0f
  67
  68static void _retraceCallback(u32 count);
  69
  70static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right);
  71static void _audioDMA(void);
  72static void _setRumble(struct mRumble* rumble, int enable);
  73static void _sampleRotation(struct mRotationSource* source);
  74static int32_t _readTiltX(struct mRotationSource* source);
  75static int32_t _readTiltY(struct mRotationSource* source);
  76static int32_t _readGyroZ(struct mRotationSource* source);
  77
  78static void _drawStart(void);
  79static void _drawEnd(void);
  80static uint32_t _pollInput(const struct mInputMap*);
  81static enum GUICursorState _pollCursor(unsigned* x, unsigned* y);
  82static void _guiPrepare(void);
  83
  84static void _setup(struct mGUIRunner* runner);
  85static void _gameLoaded(struct mGUIRunner* runner);
  86static void _gameUnloaded(struct mGUIRunner* runner);
  87static void _unpaused(struct mGUIRunner* runner);
  88static void _drawFrame(struct mGUIRunner* runner, bool faded);
  89static uint16_t _pollGameInput(struct mGUIRunner* runner);
  90static void _setFrameLimiter(struct mGUIRunner* runner, bool limit);
  91static void _incrementScreenMode(struct mGUIRunner* runner);
  92
  93static s8 WPAD_StickX(u8 chan, u8 right);
  94static s8 WPAD_StickY(u8 chan, u8 right);
  95
  96static void* outputBuffer;
  97static struct mAVStream stream;
  98static struct mRumble rumble;
  99static struct mRotationSource rotation;
 100static GXRModeObj* vmode;
 101static float wAdjust;
 102static float hAdjust;
 103static float wStretch = 1.0f;
 104static float hStretch = 0.9f;
 105static float guiScale = GUI_SCALE;
 106static Mtx model, view, modelview;
 107static uint16_t* texmem;
 108static GXTexObj tex;
 109static uint16_t* rescaleTexmem;
 110static GXTexObj rescaleTex;
 111static int32_t tiltX;
 112static int32_t tiltY;
 113static int32_t gyroZ;
 114static uint32_t retraceCount;
 115static uint32_t referenceRetraceCount;
 116static bool frameLimiter = true;
 117static int scaleFactor;
 118static unsigned corew, coreh;
 119
 120uint32_t* romBuffer;
 121size_t romBufferSize;
 122
 123static void* framebuffer[2] = { 0, 0 };
 124static int whichFb = 0;
 125
 126static struct GBAStereoSample audioBuffer[3][SAMPLES] __attribute__((__aligned__(32)));
 127static volatile size_t audioBufferSize = 0;
 128static volatile int currentAudioBuffer = 0;
 129static double audioSampleRate = 60.0 / 1.001;
 130
 131static struct GUIFont* font;
 132
 133static void reconfigureScreen(struct mGUIRunner* runner) {
 134	if (runner) {
 135		unsigned mode;
 136		if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
 137			videoMode = mode;
 138		}
 139	}
 140	wAdjust = 1.f;
 141	hAdjust = 1.f;
 142	guiScale = GUI_SCALE;
 143	audioSampleRate = 60.0 / 1.001;
 144
 145	s32 signalMode = CONF_GetVideo();
 146
 147	switch (videoMode) {
 148	case VM_AUTODETECT:
 149	default:
 150		vmode = VIDEO_GetPreferredMode(0);
 151		break;
 152	case VM_480i:
 153		switch (signalMode) {
 154		case CONF_VIDEO_NTSC:
 155			vmode = &TVNtsc480IntDf;
 156			break;
 157		case CONF_VIDEO_MPAL:
 158			vmode = &TVMpal480IntDf;
 159			break;
 160		case CONF_VIDEO_PAL:
 161			vmode = &TVEurgb60Hz480IntDf;
 162			break;
 163		}
 164		break;
 165	case VM_480p:
 166		switch (signalMode) {
 167		case CONF_VIDEO_NTSC:
 168			vmode = &TVNtsc480Prog;
 169			break;
 170		case CONF_VIDEO_MPAL:
 171			vmode = &TVMpal480Prog;
 172			break;
 173		case CONF_VIDEO_PAL:
 174			vmode = &TVEurgb60Hz480Prog;
 175			break;
 176		}
 177		break;
 178	case VM_240p:
 179		switch (signalMode) {
 180		case CONF_VIDEO_NTSC:
 181			vmode = &TVNtsc240Ds;
 182			break;
 183		case CONF_VIDEO_MPAL:
 184			vmode = &TVMpal240Ds;
 185			break;
 186		case CONF_VIDEO_PAL:
 187			vmode = &TVEurgb60Hz240Ds;
 188			break;
 189		}
 190		wAdjust = 0.5f;
 191		audioSampleRate = 90.0 / 1.50436;
 192		guiScale = GUI_SCALE_240p;
 193		break;
 194	}
 195
 196	free(framebuffer[0]);
 197	free(framebuffer[1]);
 198
 199	VIDEO_SetBlack(true);
 200	VIDEO_Configure(vmode);
 201
 202	framebuffer[0] = SYS_AllocateFramebuffer(vmode);
 203	framebuffer[1] = SYS_AllocateFramebuffer(vmode);
 204	VIDEO_ClearFrameBuffer(vmode, framebuffer[0], COLOR_BLACK);
 205	VIDEO_ClearFrameBuffer(vmode, framebuffer[1], COLOR_BLACK);
 206
 207	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
 208	VIDEO_Flush();
 209	VIDEO_WaitVSync();
 210	if (vmode->viTVMode & VI_NON_INTERLACE) {
 211		VIDEO_WaitVSync();
 212	}
 213	GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
 214
 215	f32 yscale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight);
 216	u32 xfbHeight = GX_SetDispCopyYScale(yscale);
 217	GX_SetScissor(0, 0, vmode->viWidth, vmode->viWidth);
 218	GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight);
 219	GX_SetDispCopyDst(vmode->fbWidth, xfbHeight);
 220	GX_SetCopyFilter(vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter);
 221	GX_SetFieldMode(vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
 222
 223	if (runner) {
 224		runner->params.width = vmode->fbWidth * guiScale * wAdjust;
 225		runner->params.height = vmode->efbHeight * guiScale * hAdjust;
 226		if (runner->core) {
 227			double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
 228			blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
 229			blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
 230
 231			runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
 232			int hfactor = vmode->fbWidth / (corew * wAdjust);
 233			int vfactor = vmode->efbHeight / (coreh * hAdjust);
 234			if (hfactor > vfactor) {
 235				scaleFactor = vfactor;
 236			} else {
 237				scaleFactor = hfactor;
 238			}
 239		}
 240	}
 241}
 242
 243int main(int argc, char* argv[]) {
 244	VIDEO_Init();
 245	VIDEO_SetBlack(true);
 246	VIDEO_Flush();
 247	VIDEO_WaitVSync();
 248	PAD_Init();
 249	WPAD_Init();
 250	WPAD_SetDataFormat(0, WPAD_FMT_BTNS_ACC_IR);
 251	AUDIO_Init(0);
 252	AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
 253	AUDIO_RegisterDMACallback(_audioDMA);
 254
 255	memset(audioBuffer, 0, sizeof(audioBuffer));
 256#ifdef FIXED_ROM_BUFFER
 257	romBufferSize = SIZE_CART0;
 258	romBuffer = SYS_GetArena2Lo();
 259	SYS_SetArena2Lo((void*)((intptr_t) romBuffer + SIZE_CART0));
 260#endif
 261
 262#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
 263#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
 264#endif
 265
 266	GXColor bg = { 0, 0, 0, 0xFF };
 267	void* fifo = memalign(32, 0x40000);
 268	memset(fifo, 0, 0x40000);
 269	GX_Init(fifo, 0x40000);
 270	GX_SetCopyClear(bg, 0x00FFFFFF);
 271
 272	GX_SetCullMode(GX_CULL_NONE);
 273	GX_SetDispCopyGamma(GX_GM_1_0);
 274
 275	GX_ClearVtxDesc();
 276	GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
 277	GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
 278	GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
 279
 280	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
 281	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
 282	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
 283
 284	GX_SetNumChans(1);
 285	GX_SetNumTexGens(1);
 286	GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
 287	GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
 288
 289	GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
 290	GX_InvVtxCache();
 291	GX_InvalidateTexAll();
 292
 293	guVector cam = { 0.0f, 0.0f, 0.0f };
 294	guVector up = { 0.0f, 1.0f, 0.0f };
 295	guVector look = { 0.0f, 0.0f, -1.0f };
 296	guLookAt(view, &cam, &up, &look);
 297
 298	guMtxIdentity(model);
 299	guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
 300	guMtxConcat(view, model, modelview);
 301	GX_LoadPosMtxImm(modelview, GX_PNMTX0);
 302
 303	texmem = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
 304	GX_InitTexObj(&tex, texmem, TEX_W, TEX_H, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
 305	rescaleTexmem = memalign(32, TEX_W * TEX_H * 4 * BYTES_PER_PIXEL);
 306	GX_InitTexObj(&rescaleTex, rescaleTexmem, TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
 307	GX_InitTexObjFilterMode(&rescaleTex, GX_LINEAR, GX_LINEAR);
 308
 309	VIDEO_SetPostRetraceCallback(_retraceCallback);
 310
 311	font = GUIFontCreate();
 312
 313	fatInitDefault();
 314
 315	rumble.setRumble = _setRumble;
 316
 317	rotation.sample = _sampleRotation;
 318	rotation.readTiltX = _readTiltX;
 319	rotation.readTiltY = _readTiltY;
 320	rotation.readGyroZ = _readGyroZ;
 321
 322	stream.videoDimensionsChanged = NULL;
 323	stream.postVideoFrame = NULL;
 324	stream.postAudioFrame = NULL;
 325	stream.postAudioBuffer = _postAudioBuffer;
 326
 327	struct mGUIRunner runner = {
 328		.params = {
 329			720, 480,
 330			font, "",
 331			_drawStart, _drawEnd,
 332			_pollInput, _pollCursor,
 333			0,
 334			_guiPrepare, 0,
 335		},
 336		.keySources = (struct GUIInputKeys[]) {
 337			{
 338				.name = "GameCube Input (1)",
 339				.id = GCN1_INPUT,
 340				.keyNames = (const char*[]) {
 341					"D-Pad Left",
 342					"D-Pad Right",
 343					"D-Pad Down",
 344					"D-Pad Up",
 345					"Z",
 346					"R",
 347					"L",
 348					0,
 349					"A",
 350					"B",
 351					"X",
 352					"Y",
 353					"Start"
 354				},
 355				.nKeys = 13
 356			},
 357			{
 358				.name = "GameCube Input (2)",
 359				.id = GCN2_INPUT,
 360				.keyNames = (const char*[]) {
 361					"D-Pad Left",
 362					"D-Pad Right",
 363					"D-Pad Down",
 364					"D-Pad Up",
 365					"Z",
 366					"R",
 367					"L",
 368					0,
 369					"A",
 370					"B",
 371					"X",
 372					"Y",
 373					"Start"
 374				},
 375				.nKeys = 13
 376			},
 377			{
 378				.name = "Wii Remote Input",
 379				.id = WIIMOTE_INPUT,
 380				.keyNames = (const char*[]) {
 381					"2",
 382					"1",
 383					"B",
 384					"A",
 385					"-",
 386					0,
 387					0,
 388					"\1\xE",
 389					"Left",
 390					"Right",
 391					"Down",
 392					"Up",
 393					"+",
 394					0,
 395					0,
 396					0,
 397					"Z",
 398					"C",
 399				},
 400				.nKeys = 18
 401			},
 402			{
 403				.name = "Classic Controller Input",
 404				.id = CLASSIC_INPUT,
 405				.keyNames = (const char*[]) {
 406					0,
 407					0,
 408					0,
 409					0,
 410					0,
 411					0,
 412					0,
 413					0,
 414					0,
 415					0,
 416					0,
 417					0,
 418					0,
 419					0,
 420					0,
 421					0,
 422					"Up",
 423					"Left",
 424					"ZR",
 425					"X",
 426					"A",
 427					"Y",
 428					"B",
 429					"ZL",
 430					0,
 431					"R",
 432					"+",
 433					"\1\xE",
 434					"-",
 435					"L",
 436					"Down",
 437					"Right",
 438				},
 439				.nKeys = 32
 440			},
 441			{ .id = 0 }
 442		},
 443		.configExtra = (struct GUIMenuItem[]) {
 444			{
 445				.title = "Video mode",
 446				.data = "videoMode",
 447				.submenu = 0,
 448				.state = 0,
 449				.validStates = (const char*[]) {
 450					"Autodetect (recommended)",
 451					"480i",
 452					"480p",
 453					"240p",
 454				},
 455				.nStates = 4
 456			},
 457			{
 458				.title = "Screen mode",
 459				.data = "screenMode",
 460				.submenu = 0,
 461				.state = 0,
 462				.validStates = (const char*[]) {
 463					"Pixel-Accurate",
 464					"Stretched",
 465				},
 466				.nStates = 2
 467			},
 468			{
 469				.title = "Filtering",
 470				.data = "filter",
 471				.submenu = 0,
 472				.state = 0,
 473				.validStates = (const char*[]) {
 474					"Pixelated",
 475					"Bilinear (smoother)",
 476					"Bilinear (pixelated)",
 477				},
 478				.nStates = 3
 479			}
 480		},
 481		.nConfigExtra = 3,
 482		.setup = _setup,
 483		.teardown = 0,
 484		.gameLoaded = _gameLoaded,
 485		.gameUnloaded = _gameUnloaded,
 486		.prepareForFrame = 0,
 487		.drawFrame = _drawFrame,
 488		.paused = _gameUnloaded,
 489		.unpaused = _unpaused,
 490		.incrementScreenMode = _incrementScreenMode,
 491		.setFrameLimiter = _setFrameLimiter,
 492		.pollGameInput = _pollGameInput
 493	};
 494	mGUIInit(&runner, "wii");
 495	reconfigureScreen(&runner);
 496
 497	// Make sure screen is properly initialized by drawing a blank frame
 498	_drawStart();
 499	_drawEnd();
 500
 501	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_A, GUI_INPUT_SELECT);
 502	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_B, GUI_INPUT_BACK);
 503	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_TRIGGER_Z, GUI_INPUT_CANCEL);
 504	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_UP, GUI_INPUT_UP);
 505	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_DOWN, GUI_INPUT_DOWN);
 506	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_LEFT, GUI_INPUT_LEFT);
 507	_mapKey(&runner.params.keyMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GUI_INPUT_RIGHT);
 508
 509	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GUI_INPUT_SELECT);
 510	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GUI_INPUT_BACK);
 511	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_HOME, GUI_INPUT_CANCEL);
 512	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GUI_INPUT_UP);
 513	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GUI_INPUT_DOWN);
 514	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GUI_INPUT_LEFT);
 515	_mapKey(&runner.params.keyMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GUI_INPUT_RIGHT);
 516
 517	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GUI_INPUT_SELECT);
 518	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_Y, GUI_INPUT_SELECT);
 519	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GUI_INPUT_BACK);
 520	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_X, GUI_INPUT_BACK);
 521	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_HOME, GUI_INPUT_CANCEL);
 522	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GUI_INPUT_UP);
 523	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GUI_INPUT_DOWN);
 524	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GUI_INPUT_LEFT);
 525	_mapKey(&runner.params.keyMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GUI_INPUT_RIGHT);
 526
 527	if (argc > 1) {
 528		size_t i;
 529		for (i = 0; runner.keySources[i].id; ++i) {
 530			mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
 531		}
 532		mGUIRun(&runner, argv[1]);
 533	} else {
 534		mGUIRunloop(&runner);
 535	}
 536	VIDEO_SetBlack(true);
 537	VIDEO_Flush();
 538	VIDEO_WaitVSync();
 539	mGUIDeinit(&runner);
 540
 541	free(fifo);
 542	free(texmem);
 543	free(rescaleTexmem);
 544
 545	free(outputBuffer);
 546	GUIFontDestroy(font);
 547
 548	free(framebuffer[0]);
 549	free(framebuffer[1]);
 550
 551	return 0;
 552}
 553
 554static void _audioDMA(void) {
 555	if (!audioBufferSize) {
 556		return;
 557	}
 558	DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
 559	AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
 560	currentAudioBuffer = (currentAudioBuffer + 1) % 3;
 561	audioBufferSize = 0;
 562}
 563
 564static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
 565	UNUSED(stream);
 566	int available = blip_samples_avail(left);
 567	if (available + audioBufferSize > SAMPLES) {
 568		available = SAMPLES - audioBufferSize;
 569	}
 570	available &= ~((32 / sizeof(struct GBAStereoSample)) - 1); // Force align to 32 bytes
 571	if (available > 0) {
 572		// These appear to be reversed for AUDIO_InitDMA
 573		blip_read_samples(left, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
 574		blip_read_samples(right, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
 575		audioBufferSize += available;
 576	}
 577	if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
 578		_audioDMA();
 579		AUDIO_StartDMA();
 580	}
 581}
 582
 583static void _drawStart(void) {
 584	VIDEO_SetBlack(false);
 585
 586	u32 level = 0;
 587	_CPU_ISR_Disable(level);
 588	if (referenceRetraceCount > retraceCount) {
 589		if (frameLimiter) {
 590			VIDEO_WaitVSync();
 591		}
 592		referenceRetraceCount = retraceCount;
 593	}
 594	_CPU_ISR_Restore(level);
 595
 596	GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
 597	GX_SetColorUpdate(GX_TRUE);
 598
 599	GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1);
 600}
 601
 602static void _drawEnd(void) {
 603	GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
 604	GX_DrawDone();
 605	VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
 606	VIDEO_Flush();
 607	whichFb = !whichFb;
 608
 609	u32 level = 0;
 610	_CPU_ISR_Disable(level);
 611	++referenceRetraceCount;
 612	_CPU_ISR_Restore(level);
 613}
 614
 615static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
 616	UNUSED(runner);
 617	frameLimiter = limit;
 618}
 619
 620static uint32_t _pollInput(const struct mInputMap* map) {
 621	PAD_ScanPads();
 622	u16 padkeys = PAD_ButtonsHeld(0);
 623
 624	WPAD_ScanPads();
 625	u32 wiiPad = WPAD_ButtonsHeld(0);
 626	u32 ext = 0;
 627	WPAD_Probe(0, &ext);
 628
 629	int keys = 0;
 630	keys |= mInputMapKeyBits(map, GCN1_INPUT, padkeys, 0);
 631	keys |= mInputMapKeyBits(map, GCN2_INPUT, padkeys, 0);
 632	keys |= mInputMapKeyBits(map, WIIMOTE_INPUT, wiiPad, 0);
 633	if (ext == WPAD_EXP_CLASSIC) {
 634		keys |= mInputMapKeyBits(map, CLASSIC_INPUT, wiiPad, 0);
 635	}
 636	int x = PAD_StickX(0);
 637	int y = PAD_StickY(0);
 638	int w_x = WPAD_StickX(0, 0);
 639	int w_y = WPAD_StickY(0, 0);
 640	if (x < -0x20 || w_x < -0x20) {
 641		keys |= 1 << GUI_INPUT_LEFT;
 642	}
 643	if (x > 0x20 || w_x > 0x20) {
 644		keys |= 1 << GUI_INPUT_RIGHT;
 645	}
 646	if (y < -0x20 || w_y <- 0x20) {
 647		keys |= 1 << GUI_INPUT_DOWN;
 648	}
 649	if (y > 0x20 || w_y > 0x20) {
 650		keys |= 1 << GUI_INPUT_UP;
 651	}
 652	return keys;
 653}
 654
 655static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
 656	ir_t ir;
 657	WPAD_IR(0, &ir);
 658	if (!ir.smooth_valid) {
 659		return GUI_CURSOR_NOT_PRESENT;
 660	}
 661	*x = ir.sx;
 662	*y = ir.sy;
 663	WPAD_ScanPads();
 664	u32 wiiPad = WPAD_ButtonsHeld(0);
 665	if (wiiPad & WPAD_BUTTON_A) {
 666		return GUI_CURSOR_DOWN;
 667	}
 668	return GUI_CURSOR_UP;
 669}
 670
 671void _reproj(int w, int h) {
 672	Mtx44 proj;
 673	int top = (vmode->efbHeight * hAdjust - h) / 2;
 674	int left = (vmode->fbWidth * wAdjust - w) / 2;
 675	guOrtho(proj, -top, top + h, -left, left + w, 0, 300);
 676	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
 677}
 678
 679void _reproj2(int w, int h) {
 680	Mtx44 proj;
 681	int top = h * (1.0 - hStretch) / 2;
 682	int left = w * (1.0 - wStretch) / 2;
 683	guOrtho(proj, -top, h + top, -left, w + left, 0, 300);
 684	GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
 685}
 686
 687void _guiPrepare(void) {
 688	_reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust);
 689}
 690
 691void _setup(struct mGUIRunner* runner) {
 692	runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
 693	runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble);
 694	runner->core->setAVStream(runner->core, &stream);
 695
 696	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A);
 697	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B);
 698	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_START, GBA_KEY_START);
 699	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_X, GBA_KEY_SELECT);
 700	_mapKey(&runner->core->inputMap, GCN2_INPUT, PAD_BUTTON_Y, GBA_KEY_SELECT);
 701	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_UP, GBA_KEY_UP);
 702	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_DOWN, GBA_KEY_DOWN);
 703	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_LEFT, GBA_KEY_LEFT);
 704	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_RIGHT, GBA_KEY_RIGHT);
 705	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_L, GBA_KEY_L);
 706	_mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_TRIGGER_R, GBA_KEY_R);
 707
 708	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_2, GBA_KEY_A);
 709	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_1, GBA_KEY_B);
 710	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_PLUS, GBA_KEY_START);
 711	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_MINUS, GBA_KEY_SELECT);
 712	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_RIGHT, GBA_KEY_UP);
 713	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_LEFT, GBA_KEY_DOWN);
 714	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_UP, GBA_KEY_LEFT);
 715	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_DOWN, GBA_KEY_RIGHT);
 716	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_B, GBA_KEY_L);
 717	_mapKey(&runner->core->inputMap, WIIMOTE_INPUT, WPAD_BUTTON_A, GBA_KEY_R);
 718
 719	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_A, GBA_KEY_A);
 720	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_B, GBA_KEY_B);
 721	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_PLUS, GBA_KEY_START);
 722	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_MINUS, GBA_KEY_SELECT);
 723	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_UP, GBA_KEY_UP);
 724	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_DOWN, GBA_KEY_DOWN);
 725	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_LEFT, GBA_KEY_LEFT);
 726	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_RIGHT, GBA_KEY_RIGHT);
 727	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
 728	_mapKey(&runner->core->inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
 729
 730	struct mInputAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
 731	mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 0, &desc);
 732	mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, &desc);
 733	desc = (struct mInputAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
 734	mInputBindAxis(&runner->core->inputMap, GCN1_INPUT, 1, &desc);
 735	mInputBindAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, &desc);
 736
 737	outputBuffer = memalign(32, TEX_W * TEX_H * BYTES_PER_PIXEL);
 738	runner->core->setVideoBuffer(runner->core, outputBuffer, TEX_W);
 739
 740	runner->core->setAudioBufferSize(runner->core, SAMPLES);
 741
 742	double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
 743	blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
 744	blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
 745
 746	frameLimiter = true;
 747}
 748
 749void _gameUnloaded(struct mGUIRunner* runner) {
 750	UNUSED(runner);
 751	AUDIO_StopDMA();
 752	frameLimiter = true;
 753	VIDEO_SetBlack(true);
 754	VIDEO_Flush();
 755	VIDEO_WaitVSync();
 756}
 757
 758void _gameLoaded(struct mGUIRunner* runner) {
 759	reconfigureScreen(runner);
 760	if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
 761		int i;
 762		for (i = 0; i < 6; ++i) {
 763			u32 result = WPAD_SetMotionPlus(0, 1);
 764			if (result == WPAD_ERR_NONE) {
 765				break;
 766			}
 767			sleep(1);
 768		}
 769	}
 770	memset(texmem, 0, TEX_W * TEX_H * BYTES_PER_PIXEL);
 771	_unpaused(runner);
 772}
 773
 774void _unpaused(struct mGUIRunner* runner) {
 775	u32 level = 0;
 776	VIDEO_WaitVSync();
 777	_CPU_ISR_Disable(level);
 778	referenceRetraceCount = retraceCount;
 779	_CPU_ISR_Restore(level);
 780
 781	unsigned mode;
 782	if (mCoreConfigGetUIntValue(&runner->config, "videoMode", &mode) && mode < VM_MAX) {
 783		if (mode != videoMode) {
 784			reconfigureScreen(runner);
 785		}
 786	}
 787	if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
 788		screenMode = mode;
 789	}
 790	if (mCoreConfigGetUIntValue(&runner->config, "filter", &mode) && mode < FM_MAX) {
 791		filterMode = mode;
 792		switch (mode) {
 793		case FM_NEAREST:
 794		case FM_LINEAR_2x:
 795		default:
 796			GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
 797			break;
 798		case FM_LINEAR_1x:
 799			GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
 800			break;
 801		}
 802	}
 803	float stretch;
 804	if (mCoreConfigGetFloatValue(&runner->config, "stretchWidth", &stretch)) {
 805		wStretch = fminf(1.0f, fmaxf(0.5f, stretch));
 806	}
 807	if (mCoreConfigGetFloatValue(&runner->config, "stretchHeight", &stretch)) {
 808		hStretch = fminf(1.0f, fmaxf(0.5f, stretch));
 809	}
 810}
 811
 812void _drawFrame(struct mGUIRunner* runner, bool faded) {
 813	UNUSED(runner);
 814	uint32_t color = 0xFFFFFF3F;
 815	if (!faded) {
 816		color |= 0xC0;
 817	}
 818	size_t x, y;
 819	uint64_t* texdest = (uint64_t*) texmem;
 820	uint64_t* texsrc = (uint64_t*) outputBuffer;
 821	for (y = 0; y < coreh; y += 4) {
 822		for (x = 0; x < corew >> 2; ++x) {
 823			texdest[0 + x * 4 + y * 64] = texsrc[0   + x + y * 64];
 824			texdest[1 + x * 4 + y * 64] = texsrc[64  + x + y * 64];
 825			texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
 826			texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
 827		}
 828	}
 829	DCFlushRange(texdest, TEX_W * TEX_H * BYTES_PER_PIXEL);
 830
 831	if (faded) {
 832		GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
 833	} else {
 834		GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_NOOP);
 835	}
 836	GX_InvalidateTexAll();
 837	GX_LoadTexObj(&tex, GX_TEXMAP0);
 838
 839	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
 840	s16 vertWidth = TEX_W;
 841	s16 vertHeight = TEX_H;
 842
 843	if (filterMode == FM_LINEAR_2x) {
 844		Mtx44 proj;
 845		guOrtho(proj, 0, vmode->efbHeight, 0, vmode->fbWidth, 0, 300);
 846		GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
 847
 848		GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
 849		GX_Position2s16(0, TEX_H * 2);
 850		GX_Color1u32(0xFFFFFFFF);
 851		GX_TexCoord2s16(0, 1);
 852
 853		GX_Position2s16(TEX_W * 2, TEX_H * 2);
 854		GX_Color1u32(0xFFFFFFFF);
 855		GX_TexCoord2s16(1, 1);
 856
 857		GX_Position2s16(TEX_W * 2, 0);
 858		GX_Color1u32(0xFFFFFFFF);
 859		GX_TexCoord2s16(1, 0);
 860
 861		GX_Position2s16(0, 0);
 862		GX_Color1u32(0xFFFFFFFF);
 863		GX_TexCoord2s16(0, 0);
 864		GX_End();
 865
 866		GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
 867		GX_SetTexCopyDst(TEX_W * 2, TEX_H * 2, GX_TF_RGB565, GX_FALSE);
 868		GX_CopyTex(rescaleTexmem, GX_TRUE);
 869		GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
 870	}
 871
 872	if (screenMode == SM_PA) {
 873		vertWidth *= scaleFactor;
 874		vertHeight *= scaleFactor;
 875	}
 876
 877	if (screenMode == SM_PA) {
 878		_reproj(corew * scaleFactor, coreh * scaleFactor);
 879	} else {
 880		_reproj2(corew, coreh);
 881	}
 882
 883	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
 884	GX_Position2s16(0, vertHeight);
 885	GX_Color1u32(color);
 886	GX_TexCoord2s16(0, 1);
 887
 888	GX_Position2s16(vertWidth, vertHeight);
 889	GX_Color1u32(color);
 890	GX_TexCoord2s16(1, 1);
 891
 892	GX_Position2s16(vertWidth, 0);
 893	GX_Color1u32(color);
 894	GX_TexCoord2s16(1, 0);
 895
 896	GX_Position2s16(0, 0);
 897	GX_Color1u32(color);
 898	GX_TexCoord2s16(0, 0);
 899	GX_End();
 900}
 901
 902uint16_t _pollGameInput(struct mGUIRunner* runner) {
 903	UNUSED(runner);
 904	PAD_ScanPads();
 905	u16 padkeys = PAD_ButtonsHeld(0);
 906	WPAD_ScanPads();
 907	u32 wiiPad = WPAD_ButtonsHeld(0);
 908	u32 ext = 0;
 909	WPAD_Probe(0, &ext);
 910	uint16_t keys = mInputMapKeyBits(&runner->core->inputMap, GCN1_INPUT, padkeys, 0);
 911	keys |= mInputMapKeyBits(&runner->core->inputMap, GCN2_INPUT, padkeys, 0);
 912	keys |= mInputMapKeyBits(&runner->core->inputMap, WIIMOTE_INPUT, wiiPad, 0);
 913
 914	enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 0, PAD_StickX(0));
 915	if (angles != GBA_KEY_NONE) {
 916		keys |= 1 << angles;
 917	}
 918	angles = mInputMapAxis(&runner->core->inputMap, GCN1_INPUT, 1, PAD_StickY(0));
 919	if (angles != GBA_KEY_NONE) {
 920		keys |= 1 << angles;
 921	}
 922	if (ext == WPAD_EXP_CLASSIC) {
 923		keys |= mInputMapKeyBits(&runner->core->inputMap, CLASSIC_INPUT, wiiPad, 0);
 924		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 0, WPAD_StickX(0, 0));
 925		if (angles != GBA_KEY_NONE) {
 926			keys |= 1 << angles;
 927		}
 928		angles = mInputMapAxis(&runner->core->inputMap, CLASSIC_INPUT, 1, WPAD_StickY(0, 0));
 929		if (angles != GBA_KEY_NONE) {
 930			keys |= 1 << angles;
 931		}
 932	}
 933
 934	return keys;
 935}
 936
 937void _incrementScreenMode(struct mGUIRunner* runner) {
 938	UNUSED(runner);
 939	int mode = screenMode | (filterMode << 1);
 940	++mode;
 941	screenMode = mode % SM_MAX;
 942	filterMode = (mode >> 1) % FM_MAX;
 943	mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
 944	mCoreConfigSetUIntValue(&runner->config, "filter", filterMode);
 945	switch (filterMode) {
 946	case FM_NEAREST:
 947	case FM_LINEAR_2x:
 948	default:
 949		GX_InitTexObjFilterMode(&tex, GX_NEAR, GX_NEAR);
 950		break;
 951	case FM_LINEAR_1x:
 952		GX_InitTexObjFilterMode(&tex, GX_LINEAR, GX_LINEAR);
 953		break;
 954	}
 955}
 956
 957void _setRumble(struct mRumble* rumble, int enable) {
 958	UNUSED(rumble);
 959	WPAD_Rumble(0, enable);
 960	if (enable) {
 961		PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);
 962	} else {
 963		PAD_ControlMotor(0, PAD_MOTOR_STOP);
 964	}
 965}
 966
 967void _sampleRotation(struct mRotationSource* source) {
 968	UNUSED(source);
 969	vec3w_t accel;
 970	WPAD_Accel(0, &accel);
 971	// These are swapped
 972	tiltX = (0x1EA - accel.y) << 22;
 973	tiltY = (0x1EA - accel.x) << 22;
 974
 975	// This doesn't seem to work at all with -TR remotes
 976	struct expansion_t exp;
 977	WPAD_Expansion(0, &exp);
 978	if (exp.type != EXP_MOTION_PLUS) {
 979		return;
 980	}
 981	gyroZ = exp.mp.rz - 0x1FA0;
 982	gyroZ <<= 18;
 983}
 984
 985int32_t _readTiltX(struct mRotationSource* source) {
 986	UNUSED(source);
 987	return tiltX;
 988}
 989
 990int32_t _readTiltY(struct mRotationSource* source) {
 991	UNUSED(source);
 992	return tiltY;
 993}
 994
 995int32_t _readGyroZ(struct mRotationSource* source) {
 996	UNUSED(source);
 997	return gyroZ;
 998}
 999
1000static s8 WPAD_StickX(u8 chan, u8 right) {
1001	struct expansion_t exp;
1002	WPAD_Expansion(chan, &exp);
1003	struct joystick_t* js = NULL;
1004
1005	switch (exp.type)	{
1006	case WPAD_EXP_NUNCHUK:
1007	case WPAD_EXP_GUITARHERO3:
1008		if (right == 0) {
1009			js = &exp.nunchuk.js;
1010		}
1011		break;
1012	case WPAD_EXP_CLASSIC:
1013		if (right == 0) {
1014			js = &exp.classic.ljs;
1015		} else {
1016			js = &exp.classic.rjs;
1017		}
1018		break;
1019	default:
1020		break;
1021	}
1022
1023	if (!js) {
1024		return 0;
1025	}
1026	int centered = (int) js->pos.x - (int) js->center.x;
1027	int range = js->max.x - js->min.x;
1028	return (centered * 0xFF) / range;
1029}
1030
1031static s8 WPAD_StickY(u8 chan, u8 right) {
1032	struct expansion_t exp;
1033	WPAD_Expansion(chan, &exp);
1034	struct joystick_t* js = NULL;
1035
1036	switch (exp.type)	{
1037	case WPAD_EXP_NUNCHUK:
1038	case WPAD_EXP_GUITARHERO3:
1039		if (right == 0) {
1040			js = &exp.nunchuk.js;
1041		}
1042		break;
1043	case WPAD_EXP_CLASSIC:
1044		if (right == 0) {
1045			js = &exp.classic.ljs;
1046		} else {
1047			js = &exp.classic.rjs;
1048		}
1049		break;
1050	default:
1051		break;
1052	}
1053
1054	if (!js) {
1055		return 0;
1056	}
1057	int centered = (int) js->pos.y - (int) js->center.y;
1058	int range = js->max.y - js->min.y;
1059	return (centered * 0xFF) / range;
1060}
1061
1062void _retraceCallback(u32 count) {
1063	u32 level = 0;
1064	_CPU_ISR_Disable(level);
1065	retraceCount = count;
1066	_CPU_ISR_Restore(level);
1067}