all repos — mgba @ ea2159d159f2ddb5514b9137a116eb74a2ed872f

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