all repos — mgba @ 20f900520d7e3e6047ba5169befe3ed26e3f9a04

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