all repos — mgba @ cddae84a437fd5753f8bf3c7c549641ba162f657

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 "gba/gba.h"
  9#include "gba/input.h"
 10#include "gba/audio.h"
 11#include "gba/supervisor/overrides.h"
 12#include "gba/video.h"
 13
 14#include "gba/renderers/video-software.h"
 15#include "util/circle-buffer.h"
 16#include "util/memory.h"
 17#include "util/threading.h"
 18#include "util/vfs.h"
 19#include "platform/psp2/sce-vfs.h"
 20#include "third-party/blip_buf/blip_buf.h"
 21
 22#include <psp2/audioout.h>
 23#include <psp2/ctrl.h>
 24#include <psp2/display.h>
 25#include <psp2/gxm.h>
 26#include <psp2/kernel/sysmem.h>
 27
 28#include <vita2d.h>
 29
 30static char gameName[13];
 31static struct GBA* gba;
 32static struct ARMCore* cpu;
 33static struct VFile* rom;
 34static struct VFile* save;
 35static struct GBAVideoSoftwareRenderer renderer;
 36static struct GBAInputMap inputMap;
 37static vita2d_texture* tex;
 38static Thread audioThread;
 39
 40static bool fullscreen = false;
 41
 42#define PSP2_HORIZONTAL_PIXELS 960
 43#define PSP2_VERTICAL_PIXELS 544
 44#define PSP2_INPUT 0x50535032
 45#define PSP2_SAMPLES 64
 46#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 19)
 47
 48static struct GBAPSP2AudioContext {
 49	struct CircleBuffer buffer;
 50	Mutex mutex;
 51	Condition cond;
 52	bool running;
 53} audioContext;
 54
 55static void _mapVitaKey(struct GBAInputMap* map, int pspKey, enum GBAKey key) {
 56	GBAInputBindKey(map, PSP2_INPUT, __builtin_ctz(pspKey), key);
 57}
 58
 59static THREAD_ENTRY _audioThread(void* context) {
 60	struct GBAPSP2AudioContext* audio = (struct GBAPSP2AudioContext*) context;
 61	struct GBAStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE];
 62	int audioPort = sceAudioOutOpenPort(PSP2_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_AUDIO_BUFFER_SIZE, 48000, PSP2_AUDIO_OUT_MODE_STEREO);
 63	while (audio->running) {
 64		MutexLock(&audio->mutex);
 65		int len = CircleBufferSize(&audio->buffer);
 66		len /= sizeof(buffer[0]);
 67		if (len > PSP2_AUDIO_BUFFER_SIZE) {
 68			len = PSP2_AUDIO_BUFFER_SIZE;
 69		}
 70		if (len > 0) {
 71			len &= ~(PSP2_AUDIO_MIN_LEN - 1);
 72			CircleBufferRead(&audio->buffer, buffer, len * sizeof(buffer[0]));
 73			MutexUnlock(&audio->mutex);
 74			sceAudioOutSetConfig(audioPort, len, -1, -1);
 75			sceAudioOutOutput(audioPort, buffer);
 76			MutexLock(&audio->mutex);
 77		}
 78		ConditionWait(&audio->cond, &audio->mutex);
 79		MutexUnlock(&audio->mutex);
 80	}
 81	sceAudioOutReleasePort(audioPort);
 82	return 0;
 83}
 84
 85void GBAPSP2Setup() {
 86	GBAInputMapInit(&inputMap);
 87	_mapVitaKey(&inputMap, PSP2_CTRL_CROSS, GBA_KEY_A);
 88	_mapVitaKey(&inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B);
 89	_mapVitaKey(&inputMap, PSP2_CTRL_START, GBA_KEY_START);
 90	_mapVitaKey(&inputMap, PSP2_CTRL_SELECT, GBA_KEY_SELECT);
 91	_mapVitaKey(&inputMap, PSP2_CTRL_UP, GBA_KEY_UP);
 92	_mapVitaKey(&inputMap, PSP2_CTRL_DOWN, GBA_KEY_DOWN);
 93	_mapVitaKey(&inputMap, PSP2_CTRL_LEFT, GBA_KEY_LEFT);
 94	_mapVitaKey(&inputMap, PSP2_CTRL_RIGHT, GBA_KEY_RIGHT);
 95	_mapVitaKey(&inputMap, PSP2_CTRL_LTRIGGER, GBA_KEY_L);
 96	_mapVitaKey(&inputMap, PSP2_CTRL_RTRIGGER, GBA_KEY_R);
 97
 98	struct GBAAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 };
 99	GBAInputBindAxis(&inputMap, PSP2_INPUT, 0, &desc);
100	desc = (struct GBAAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 };
101	GBAInputBindAxis(&inputMap, PSP2_INPUT, 1, &desc);
102
103	vita2d_init();
104	tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
105
106	renderer.outputBuffer = vita2d_texture_get_datap(tex);
107	renderer.outputBufferStride = 256;
108}
109
110void GBAPSP2LoadROM(const char* path) {
111	GBAVideoSoftwareRendererCreate(&renderer);
112
113	gba = anonymousMemoryMap(sizeof(struct GBA));
114	cpu = anonymousMemoryMap(sizeof(struct ARMCore));
115
116	printf("GBA: %08X", gba);
117	printf("CPU: %08X", cpu);
118
119	rom = VFileOpenSce(path, PSP2_O_RDONLY, 0666);
120	save = VDirOptionalOpenFile(0, path, 0, ".sav", PSP2_O_RDWR | PSP2_O_CREAT);
121
122	printf("ROM: %08X", rom);
123	printf("Save: %08X", save);
124
125	GBACreate(gba);
126	ARMSetComponents(cpu, &gba->d, 0, 0);
127	ARMInit(cpu);
128	printf("%s initialized.", "CPU");
129
130	gba->sync = 0;
131
132	GBAVideoAssociateRenderer(&gba->video, &renderer.d);
133
134	GBALoadROM(gba, rom, save, 0);
135	GBAOverrideApplyDefaults(gba);
136	GBAGetGameTitle(gba, gameName);
137	printf("ROM loaded: %s", gameName);
138
139	ARMReset(cpu);
140	double ratio = GBAAudioCalculateRatio(1, 60, 1);
141	blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
142	blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio);
143
144	CircleBufferInit(&audioContext.buffer, PSP2_AUDIO_BUFFER_SIZE * sizeof(struct GBAStereoSample));
145	MutexInit(&audioContext.mutex);
146	ConditionInit(&audioContext.cond);
147	audioContext.running = true;
148	ThreadCreate(&audioThread, _audioThread, &audioContext);
149
150	printf("%s all set and ready to roll.", projectName);
151}
152
153void GBAPSP2Runloop(void) {
154	int activeKeys = 0;
155	gba->keySource = &activeKeys;
156
157	bool fsToggle = false;
158	int frameCounter = 0;
159	while (true) {
160		ARMRunLoop(cpu);
161
162		if (frameCounter != gba->video.frameCounter) {
163			SceCtrlData pad;
164			sceCtrlPeekBufferPositive(0, &pad, 1);
165			if (pad.buttons & PSP2_CTRL_TRIANGLE) {
166				break;
167			}
168			if (pad.buttons & PSP2_CTRL_SQUARE) {
169				if (!fsToggle) {
170					fullscreen = !fullscreen;
171				}
172				fsToggle = true;
173			} else {
174				fsToggle = false;
175			}
176
177			activeKeys = GBAInputMapKeyBits(&inputMap, PSP2_INPUT, pad.buttons, 0);
178			enum GBAKey angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 0, pad.ly);
179			if (angles != GBA_KEY_NONE) {
180				activeKeys |= 1 << angles;
181			}
182			angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 1, pad.lx);
183			if (angles != GBA_KEY_NONE) {
184				activeKeys |= 1 << angles;
185			}
186			angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 2, pad.ry);
187			if (angles != GBA_KEY_NONE) {
188				activeKeys |= 1 << angles;
189			}
190			angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 3, pad.rx);
191			if (angles != GBA_KEY_NONE) {
192				activeKeys |= 1 << angles;
193			}
194
195			MutexLock(&audioContext.mutex);
196			while (blip_samples_avail(gba->audio.left) >= PSP2_SAMPLES) {
197				if (CircleBufferSize(&audioContext.buffer) + PSP2_SAMPLES * sizeof(struct GBAStereoSample) > CircleBufferCapacity(&audioContext.buffer)) {
198					break;
199				}
200				struct GBAStereoSample samples[PSP2_SAMPLES];
201				blip_read_samples(gba->audio.left, &samples[0].left, PSP2_SAMPLES, true);
202				blip_read_samples(gba->audio.right, &samples[0].right, PSP2_SAMPLES, true);
203				int i;
204				for (i = 0; i < PSP2_SAMPLES; ++i) {
205					CircleBufferWrite16(&audioContext.buffer, samples[i].left);
206					CircleBufferWrite16(&audioContext.buffer, samples[i].right);
207				}
208			}
209			ConditionWake(&audioContext.cond);
210			MutexUnlock(&audioContext.mutex);
211
212			vita2d_start_drawing();
213			vita2d_clear_screen();
214			GBAPSP2Draw();
215			vita2d_end_drawing();
216			vita2d_swap_buffers();
217
218			frameCounter = gba->video.frameCounter;
219		}
220	}
221}
222
223void GBAPSP2UnloadROM(void) {
224	printf("%s shutting down...", projectName);
225
226	ARMDeinit(cpu);
227	GBADestroy(gba);
228
229	rom->close(rom);
230	save->close(save);
231
232	MutexLock(&audioContext.mutex);
233	audioContext.running = false;
234	ConditionWake(&audioContext.cond);
235	MutexUnlock(&audioContext.mutex);
236	ThreadJoin(audioThread);
237	CircleBufferDeinit(&audioContext.buffer);
238	MutexDeinit(&audioContext.mutex);
239	ConditionDeinit(&audioContext.cond);
240
241	mappedMemoryFree(gba, 0);
242	mappedMemoryFree(cpu, 0);
243
244	gba = 0;
245	cpu = 0;
246	rom = 0;
247	save = 0;
248}
249
250void GBAPSP2Teardown(void) {
251	GBAInputMapDeinit(&inputMap);
252
253	vita2d_free_texture(tex);
254	vita2d_fini();
255}
256
257void GBAPSP2Draw(void) {
258	if (fullscreen) {
259		vita2d_draw_texture_scale(tex, 0, 0, 960.0f / 240.0f, 544.0f / 160.0f);
260	} else {
261		vita2d_draw_texture_scale(tex, 120, 32, 3.0f, 3.0f);
262	}
263}