all repos — mgba @ 476b34ce078910bae2fd51262ae861632b3d5438

mGBA Game Boy Advance Emulator

src/platform/psp2/psp2-context.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#include "psp2-context.h"
  7
  8#include "core/core.h"
  9
 10#ifdef M_CORE_GBA
 11#include "gba/gba.h"
 12#endif
 13#ifdef M_CORE_GB
 14#include "gb/gb.h"
 15#endif
 16
 17#include "feature/gui/gui-runner.h"
 18#include "gba/input.h"
 19
 20#include "util/memory.h"
 21#include "util/circle-buffer.h"
 22#include "util/ring-fifo.h"
 23#include "util/threading.h"
 24#include "util/vfs.h"
 25#include "platform/psp2/sce-vfs.h"
 26#include "third-party/blip_buf/blip_buf.h"
 27
 28#include <psp2/audioout.h>
 29#include <psp2/ctrl.h>
 30#include <psp2/display.h>
 31#include <psp2/gxm.h>
 32#include <psp2/kernel/sysmem.h>
 33#include <psp2/motion.h>
 34#include <psp2/power.h>
 35
 36#include <vita2d.h>
 37
 38#define RUMBLE_PWM 8
 39
 40static enum ScreenMode {
 41	SM_BACKDROP,
 42	SM_PLAIN,
 43	SM_FULL,
 44	SM_ASPECT,
 45	SM_MAX
 46} screenMode;
 47
 48static void* outputBuffer;
 49static vita2d_texture* tex;
 50static vita2d_texture* screenshot;
 51static Thread audioThread;
 52static struct mSceRotationSource {
 53	struct mRotationSource d;
 54	struct SceMotionSensorState state;
 55} rotation;
 56static struct mSceRumble {
 57	struct mRumble d;
 58	struct CircleBuffer history;
 59	int current;
 60} rumble;
 61
 62extern const uint8_t _binary_backdrop_png_start[];
 63static vita2d_texture* backdrop = 0;
 64
 65#define PSP2_SAMPLES 64
 66#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 40)
 67
 68static struct mPSP2AudioContext {
 69	struct RingFIFO buffer;
 70	size_t samples;
 71	Mutex mutex;
 72	Condition cond;
 73	bool running;
 74} audioContext;
 75
 76static void _mapVitaKey(struct mInputMap* map, int pspKey, enum GBAKey key) {
 77	mInputBindKey(map, PSP2_INPUT, __builtin_ctz(pspKey), key);
 78}
 79
 80static THREAD_ENTRY _audioThread(void* context) {
 81	struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context;
 82	int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO);
 83	while (audio->running) {
 84		MutexLock(&audio->mutex);
 85		int len = audio->samples;
 86		if (len > PSP2_SAMPLES) {
 87			len = PSP2_SAMPLES;
 88		}
 89		struct GBAStereoSample* buffer = audio->buffer.readPtr;
 90		RingFIFORead(&audio->buffer, NULL, len * 4);
 91		audio->samples -= len;
 92
 93		MutexUnlock(&audio->mutex);
 94		sceAudioOutOutput(audioPort, buffer);
 95		MutexLock(&audio->mutex);
 96
 97		if (audio->samples < PSP2_SAMPLES) {
 98			ConditionWait(&audio->cond, &audio->mutex);
 99		}
100		MutexUnlock(&audio->mutex);
101	}
102	sceAudioOutReleasePort(audioPort);
103	return 0;
104}
105
106static void _sampleRotation(struct mRotationSource* source) {
107	struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
108	sceMotionGetSensorState(&rotation->state, 1);
109}
110
111static int32_t _readTiltX(struct mRotationSource* source) {
112	struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
113	return rotation->state.accelerometer.x * 0x30000000;
114}
115
116static int32_t _readTiltY(struct mRotationSource* source) {
117	struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
118	return rotation->state.accelerometer.y * -0x30000000;
119}
120
121static int32_t _readGyroZ(struct mRotationSource* source) {
122	struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
123	return rotation->state.gyro.z * -0x10000000;
124}
125
126static void _setRumble(struct mRumble* source, int enable) {
127	struct mSceRumble* rumble = (struct mSceRumble*) source;
128	rumble->current += enable;
129	if (CircleBufferSize(&rumble->history) == RUMBLE_PWM) {
130		int8_t oldLevel;
131		CircleBufferRead8(&rumble->history, &oldLevel);
132		rumble->current -= oldLevel;
133	}
134	CircleBufferWrite8(&rumble->history, enable);
135	struct SceCtrlActuator state = {
136		rumble->current * 31,
137		0
138	};
139	sceCtrlSetActuator(1, &state);
140}
141
142uint16_t mPSP2PollInput(struct mGUIRunner* runner) {
143	SceCtrlData pad;
144	sceCtrlPeekBufferPositive(0, &pad, 1);
145
146	int activeKeys = mInputMapKeyBits(&runner->core->inputMap, PSP2_INPUT, pad.buttons, 0);
147	enum GBAKey angles = mInputMapAxis(&runner->core->inputMap, PSP2_INPUT, 0, pad.ly);
148	if (angles != GBA_KEY_NONE) {
149		activeKeys |= 1 << angles;
150	}
151	angles = mInputMapAxis(&runner->core->inputMap, PSP2_INPUT, 1, pad.lx);
152	if (angles != GBA_KEY_NONE) {
153		activeKeys |= 1 << angles;
154	}
155	angles = mInputMapAxis(&runner->core->inputMap, PSP2_INPUT, 2, pad.ry);
156	if (angles != GBA_KEY_NONE) {
157		activeKeys |= 1 << angles;
158	}
159	angles = mInputMapAxis(&runner->core->inputMap, PSP2_INPUT, 3, pad.rx);
160	if (angles != GBA_KEY_NONE) {
161		activeKeys |= 1 << angles;
162	}
163	return activeKeys;
164}
165
166void mPSP2Setup(struct mGUIRunner* runner) {
167	mCoreConfigSetDefaultIntValue(&runner->core->config, "threadedVideo", 1);
168	mCoreLoadConfig(runner->core);
169
170	scePowerSetArmClockFrequency(80);
171	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_CROSS, GBA_KEY_A);
172	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B);
173	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_START, GBA_KEY_START);
174	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_SELECT, GBA_KEY_SELECT);
175	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_UP, GBA_KEY_UP);
176	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_DOWN, GBA_KEY_DOWN);
177	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_LEFT, GBA_KEY_LEFT);
178	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_RIGHT, GBA_KEY_RIGHT);
179	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_LTRIGGER, GBA_KEY_L);
180	_mapVitaKey(&runner->core->inputMap, SCE_CTRL_RTRIGGER, GBA_KEY_R);
181
182	struct mInputAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 };
183	mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 0, &desc);
184	desc = (struct mInputAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 };
185	mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc);
186
187	tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
188	screenshot = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
189
190	outputBuffer = vita2d_texture_get_datap(tex);
191	runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
192
193	rotation.d.sample = _sampleRotation;
194	rotation.d.readTiltX = _readTiltX;
195	rotation.d.readTiltY = _readTiltY;
196	rotation.d.readGyroZ = _readGyroZ;
197	runner->core->setRotation(runner->core, &rotation.d);
198
199	rumble.d.setRumble = _setRumble;
200	CircleBufferInit(&rumble.history, RUMBLE_PWM);
201	runner->core->setRumble(runner->core, &rumble.d);
202
203	backdrop = vita2d_load_PNG_buffer(_binary_backdrop_png_start);
204
205	unsigned mode;
206	if (mCoreConfigGetUIntValue(&runner->core->config, "screenMode", &mode) && mode < SM_MAX) {
207		screenMode = mode;
208	}
209}
210
211void mPSP2LoadROM(struct mGUIRunner* runner) {
212	scePowerSetArmClockFrequency(444);
213	double ratio = GBAAudioCalculateRatio(1, 60, 1);
214	blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
215	blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
216
217	switch (runner->core->platform(runner->core)) {
218#ifdef M_CORE_GBA
219	case PLATFORM_GBA:
220		if (((struct GBA*) runner->core->board)->memory.hw.devices & (HW_TILT | HW_GYRO)) {
221			sceMotionStartSampling();
222		}
223		break;
224#endif
225#ifdef M_CORE_GB
226	case PLATFORM_GB:
227		if (((struct GB*) runner->core->board)->memory.mbcType == GB_MBC7) {
228			sceMotionStartSampling();
229		}
230		break;
231#endif
232	default:
233		break;
234	}
235
236	RingFIFOInit(&audioContext.buffer, PSP2_AUDIO_BUFFER_SIZE * sizeof(struct GBAStereoSample), PSP2_SAMPLES * 4);
237	MutexInit(&audioContext.mutex);
238	ConditionInit(&audioContext.cond);
239	audioContext.running = true;
240	ThreadCreate(&audioThread, _audioThread, &audioContext);
241}
242
243void mPSP2PrepareForFrame(struct mGUIRunner* runner) {
244	MutexLock(&audioContext.mutex);
245	while (blip_samples_avail(runner->core->getAudioChannel(runner->core, 0)) >= PSP2_SAMPLES) {
246		struct GBAStereoSample* samples = audioContext.buffer.writePtr;
247		blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &samples[0].left, PSP2_SAMPLES, true);
248		blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &samples[0].right, PSP2_SAMPLES, true);
249		RingFIFOWrite(&audioContext.buffer, NULL, PSP2_SAMPLES * 4);
250		audioContext.samples += PSP2_SAMPLES;
251	}
252	ConditionWake(&audioContext.cond);
253	MutexUnlock(&audioContext.mutex);
254}
255
256void mPSP2UnloadROM(struct mGUIRunner* runner) {
257	switch (runner->core->platform(runner->core)) {
258#ifdef M_CORE_GBA
259	case PLATFORM_GBA:
260		if (((struct GBA*) runner->core->board)->memory.hw.devices & (HW_TILT | HW_GYRO)) {
261			sceMotionStopSampling();
262		}
263		break;
264#endif
265#ifdef M_CORE_GB
266	case PLATFORM_GB:
267		if (((struct GB*) runner->core->board)->memory.mbcType == GB_MBC7) {
268			sceMotionStopSampling();
269		}
270		break;
271#endif
272	default:
273		break;
274	}
275	scePowerSetArmClockFrequency(80);
276}
277
278void mPSP2Paused(struct mGUIRunner* runner) {
279	UNUSED(runner);
280	struct SceCtrlActuator state = {
281		0,
282		0
283	};
284	sceCtrlSetActuator(1, &state);
285}
286
287void mPSP2Unpaused(struct mGUIRunner* runner) {
288	unsigned mode;
289	if (mCoreConfigGetUIntValue(&runner->core->config, "screenMode", &mode) && mode != screenMode) {
290		screenMode = mode;
291	}
292}
293
294void mPSP2Teardown(struct mGUIRunner* runner) {
295	CircleBufferDeinit(&rumble.history);
296	vita2d_free_texture(tex);
297	vita2d_free_texture(screenshot);
298}
299
300
301void _drawTex(vita2d_texture* t, unsigned width, unsigned height, bool faded) {
302	unsigned w = width;
303	unsigned h = height;
304	// Get greatest common divisor
305	while (w != 0) {
306		int temp = h % w;
307		h = w;
308		w = temp;
309	}
310	int gcd = h;
311	int aspectw = width / gcd;
312	int aspecth = height / gcd;
313	float scalex;
314	float scaley;
315
316	switch (screenMode) {
317	case SM_BACKDROP:
318	default:
319		vita2d_draw_texture_tint(backdrop, 0, 0, (faded ? 0 : 0xC0000000) | 0x3FFFFFFF);
320		// Fall through
321	case SM_PLAIN:
322		w = 960 / width;
323		h = 544 / height;
324		if (w * height > 544) {
325			scalex = h;
326			w = width * h;
327			h = height * h;
328		} else {
329			scalex = w;
330			w = width * w;
331			h = height * w;
332		}
333		scaley = scalex;
334		break;
335	case SM_ASPECT:
336		w = 960 / aspectw;
337		h = 544 / aspecth;
338		if (w * aspecth > 544) {
339			w = aspectw * h;
340			h = aspecth * h;
341		} else {
342			w = aspectw * w;
343			h = aspecth * w;
344		}
345		scalex = w / (float) width;
346		scaley = scalex;
347		break;
348	case SM_FULL:
349		w = 960;
350		h = 544;
351		scalex = 960.0f / width;
352		scaley = 544.0f / height;
353		break;
354	}
355	vita2d_draw_texture_tint_part_scale(t,
356	                                    (960.0f - w) / 2.0f, (544.0f - h) / 2.0f,
357	                                    0, 0, width, height,
358	                                    scalex, scaley,
359	                                    (faded ? 0 : 0xC0000000) | 0x3FFFFFFF);
360}
361
362void mPSP2Draw(struct mGUIRunner* runner, bool faded) {
363	unsigned width, height;
364	runner->core->desiredVideoDimensions(runner->core, &width, &height);
365	_drawTex(tex, width, height, faded);
366}
367
368void mPSP2DrawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, unsigned width, unsigned height, bool faded) {
369	UNUSED(runner);
370	uint32_t* texpixels = vita2d_texture_get_datap(screenshot);
371	int y;
372	for (y = 0; y < height; ++y) {
373		memcpy(&texpixels[256 * y], &pixels[width * y], width * 4);
374	}
375	_drawTex(screenshot, width, height, faded);
376}
377
378void mPSP2IncrementScreenMode(struct mGUIRunner* runner) {
379	screenMode = (screenMode + 1) % SM_MAX;
380	mCoreConfigSetUIntValue(&runner->core->config, "screenMode", screenMode);
381}
382
383__attribute__((noreturn, weak)) void __assert_func(const char* file, int line, const char* func, const char* expr) {
384	printf("ASSERT FAILED: %s in %s at %s:%i\n", expr, func, file, line);
385	exit(1);
386}