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}