all repos — mgba @ 1300b7570dafd8f204202d74f3d62c177b87436b

mGBA Game Boy Advance Emulator

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