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}