drc: various fixes / refactoring
[picodrive.git] / platform / psp / psp.c
1 /*
2  * PicoDrive
3  * (C) notaz, 2007,2008
4  *
5  * This work is licensed under the terms of MAME license.
6  * See COPYING file in the top-level directory.
7  */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include <pspkernel.h>
15 #include <pspiofilemgr.h>
16 #include <pspdisplay.h>
17 #include <psppower.h>
18 #include <psprtc.h>
19 #include <pspgu.h>
20 #include <pspsdk.h>
21
22 #include "psp.h"
23 #include "emu.h"
24 #include "../common/lprintf.h"
25 #include "version.h"
26
27 extern int pico_main(void);
28
29 #ifndef FW15
30
31 PSP_MODULE_INFO("PicoDrive", 0, 1, 51);
32 PSP_HEAP_SIZE_MAX();
33
34 int main() { return pico_main(); }      /* just a wrapper */
35
36 #else
37
38 PSP_MODULE_INFO("PicoDrive", 0x1000, 1, 51);
39 PSP_MAIN_THREAD_ATTR(0);
40
41 int main()
42 {
43         SceUID thid;
44
45         /* this is the thing we need the kernel mode for */
46         pspSdkInstallNoDeviceCheckPatch();
47
48         thid = sceKernelCreateThread("pico_main", (SceKernelThreadEntry) pico_main, 32, 0x2000, PSP_THREAD_ATTR_USER, NULL);
49         if (thid >= 0)
50                 sceKernelStartThread(thid, 0, 0);
51 #ifndef GCOV
52         sceKernelExitDeleteThread(0);
53 #else
54         while (engineState != PGS_Quit)
55                 sceKernelDelayThread(1024 * 1024);
56 #endif
57
58         return 0;
59 }
60
61 #endif
62
63 int psp_unhandled_suspend = 0;
64
65 unsigned int __attribute__((aligned(16))) guCmdList[GU_CMDLIST_SIZE];
66
67 void *psp_screen = VRAM_FB0;
68
69 static int current_screen = 0; /* front bufer */
70
71 static SceUID main_thread_id = -1;
72
73 #define ANALOG_DEADZONE 80
74
75 /* Exit callback */
76 static int exit_callback(int arg1, int arg2, void *common)
77 {
78         sceKernelExitGame();
79         return 0;
80 }
81
82 /* Power Callback */
83 static int power_callback(int unknown, int pwrflags, void *common)
84 {
85         lprintf("power_callback: flags: 0x%08X\n", pwrflags);
86
87         /* check for power switch and suspending as one is manual and the other automatic */
88         if (pwrflags & PSP_POWER_CB_POWER_SWITCH || pwrflags & PSP_POWER_CB_SUSPENDING || pwrflags & PSP_POWER_CB_STANDBY)
89         {
90                 psp_unhandled_suspend = 1;
91                 if (engineState != PGS_Suspending)
92                         engineStateSuspend = engineState;
93                 sceKernelDelayThread(100000); // ??
94         }
95         else if (pwrflags & PSP_POWER_CB_RESUME_COMPLETE)
96         {
97                 engineState = PGS_SuspendWake;
98         }
99
100         //sceDisplayWaitVblankStart();
101         return 0;
102 }
103
104 /* Callback thread */
105 static int callback_thread(SceSize args, void *argp)
106 {
107         int cbid;
108
109         lprintf("callback_thread started with id %08x, priority %i\n",
110                 sceKernelGetThreadId(), sceKernelGetThreadCurrentPriority());
111
112         cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
113         sceKernelRegisterExitCallback(cbid);
114         cbid = sceKernelCreateCallback("Power Callback", power_callback, NULL);
115         scePowerRegisterCallback(0, cbid);
116
117         sceKernelSleepThreadCB();
118
119         return 0;
120 }
121
122 void psp_init(void)
123 {
124         SceUID thid;
125         char buff[128], *r;
126
127         /* fw 1.5 sometimes returns 8002032c, although getcwd works */
128         r = getcwd(buff, sizeof(buff));
129         if (r) sceIoChdir(buff);
130
131         main_thread_id = sceKernelGetThreadId();
132
133         lprintf("\n%s\n", "PicoDrive v" VERSION " " __DATE__ " " __TIME__);
134         lprintf("running on %08x kernel\n", sceKernelDevkitVersion()),
135         lprintf("entered psp_init, threadId %08x, priority %i\n", main_thread_id,
136                 sceKernelGetThreadCurrentPriority());
137
138         thid = sceKernelCreateThread("update_thread", callback_thread, 0x11, 0xFA0, 0, NULL);
139         if (thid >= 0)
140         {
141                 sceKernelStartThread(thid, 0, 0);
142         }
143
144         /* video */
145         sceDisplaySetMode(0, 480, 272);
146         sceDisplaySetFrameBuf(VRAM_FB1, 512, PSP_DISPLAY_PIXEL_FORMAT_565, PSP_DISPLAY_SETBUF_NEXTFRAME);
147         current_screen = 1;
148         psp_screen = VRAM_FB0;
149
150         /* gu */
151         sceGuInit();
152
153         sceGuStart(GU_DIRECT, guCmdList);
154         sceGuDrawBuffer(GU_PSM_5650, (void *)VRAMOFFS_FB0, 512);
155         sceGuDispBuffer(480, 272, (void *)VRAMOFFS_FB1, 512); // don't care
156         sceGuClear(GU_COLOR_BUFFER_BIT | GU_DEPTH_BUFFER_BIT);
157         sceGuDepthBuffer((void *)VRAMOFFS_DEPTH, 512);
158         sceGuOffset(2048 - (480 / 2), 2048 - (272 / 2));
159         sceGuViewport(2048, 2048, 480, 272);
160         sceGuDepthRange(0xc350, 0x2710);
161         sceGuScissor(0, 0, 480, 272);
162         sceGuEnable(GU_SCISSOR_TEST);
163
164         sceGuDepthMask(0xffff);
165         sceGuDisable(GU_DEPTH_TEST);
166
167         sceGuFrontFace(GU_CW);
168         sceGuEnable(GU_TEXTURE_2D);
169         sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB);
170         sceGuAmbientColor(0xffffffff);
171         sceGuColor(0xffffffff);
172         sceGuFinish();
173         sceGuSync(0, 0);
174
175         sceDisplayWaitVblankStart();
176         sceGuDisplay(GU_TRUE);
177
178
179         /* input */
180         sceCtrlSetSamplingCycle(0);
181         sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
182 }
183
184 void psp_finish(void)
185 {
186         lprintf("psp_finish..\n");
187         sceGuTerm();
188
189         //sceKernelSleepThread();
190         sceKernelExitGame();
191 }
192
193 void psp_video_flip(int wait_vsync)
194 {
195         if (wait_vsync) sceDisplayWaitVblankStart();
196         sceDisplaySetFrameBuf(psp_screen, 512, PSP_DISPLAY_PIXEL_FORMAT_565,
197                 wait_vsync ? PSP_DISPLAY_SETBUF_IMMEDIATE : PSP_DISPLAY_SETBUF_NEXTFRAME);
198         current_screen ^= 1;
199         psp_screen = current_screen ? VRAM_FB0 : VRAM_FB1;
200 }
201
202 void *psp_video_get_active_fb(void)
203 {
204         return current_screen ? VRAM_FB1 : VRAM_FB0;
205 }
206
207 void psp_video_switch_to_single(void)
208 {
209         psp_screen = VRAM_FB0;
210         sceDisplaySetFrameBuf(psp_screen, 512, PSP_DISPLAY_PIXEL_FORMAT_565, PSP_DISPLAY_SETBUF_NEXTFRAME);
211         current_screen = 0;
212 }
213
214 void psp_msleep(int ms)
215 {
216         sceKernelDelayThread(ms * 1000);
217 }
218
219 unsigned int psp_pad_read(int blocking)
220 {
221         unsigned int buttons;
222         SceCtrlData pad;
223         if (blocking)
224              sceCtrlReadBufferPositive(&pad, 1);
225         else sceCtrlPeekBufferPositive(&pad, 1);
226         buttons = pad.Buttons;
227
228         // analog..
229         buttons &= ~(PBTN_NUB_UP|PBTN_NUB_DOWN|PBTN_NUB_LEFT|PBTN_NUB_RIGHT);
230         if (pad.Lx < 128 - ANALOG_DEADZONE) buttons |= PBTN_NUB_LEFT;
231         if (pad.Lx > 128 + ANALOG_DEADZONE) buttons |= PBTN_NUB_RIGHT;
232         if (pad.Ly < 128 - ANALOG_DEADZONE) buttons |= PBTN_NUB_UP;
233         if (pad.Ly > 128 + ANALOG_DEADZONE) buttons |= PBTN_NUB_DOWN;
234
235         return buttons;
236 }
237
238 int psp_get_cpu_clock(void)
239 {
240         return scePowerGetCpuClockFrequencyInt();
241 }
242
243 int psp_set_cpu_clock(int clock)
244 {
245         int ret = scePowerSetClockFrequency(clock, clock, clock/2);
246         if (ret != 0) lprintf("failed to set clock: %i\n", ret);
247
248         return ret;
249 }
250
251 char *psp_get_status_line(void)
252 {
253         static char buff[64];
254         int ret, bat_percent, bat_time;
255         pspTime time;
256
257         ret = sceRtcGetCurrentClockLocalTime(&time);
258         bat_percent = scePowerGetBatteryLifePercent();
259         bat_time = scePowerGetBatteryLifeTime();
260         if (ret < 0 || bat_percent < 0 || bat_time < 0) return NULL;
261
262         snprintf(buff, sizeof(buff), "%02i:%02i  bat: %3i%%", time.hour, time.minutes, bat_percent);
263         if (!scePowerIsPowerOnline())
264                 snprintf(buff+strlen(buff), sizeof(buff)-strlen(buff), " (%i:%02i)", bat_time/60, bat_time%60);
265         return buff;
266 }
267
268 void psp_wait_suspend(void)
269 {
270         // probably should do something smarter here?
271         sceDisplayWaitVblankStart();
272 }
273
274 void psp_resume_suspend(void)
275 {
276         // for some reason file IO doesn't seem to work
277         // after resume for some period of time, at least on 1.5
278         SceUID fd;
279         int i;
280         for (i = 0; i < 30; i++) {
281                 fd = sceIoOpen("EBOOT.PBP", PSP_O_RDONLY, 0777);
282                 if (fd >= 0) break;
283                 sceKernelDelayThread(100 * 1024);
284         }
285         if (fd >= 0) sceIoClose(fd);
286         sceDisplayWaitVblankStart();
287         if (i < 30)
288                 lprintf("io resumed after %i tries\n", i);
289         else {
290                 lprintf("io resume failed with %08x\n", fd);
291                 sceKernelDelayThread(500 * 1024);
292         }
293 }
294
295 /* alt logging */
296 #define LOG_FILE "log.txt"
297
298 #ifndef LPRINTF_STDIO
299 typedef struct _log_entry
300 {
301         char buff[256];
302         struct _log_entry *next;
303 } log_entry;
304
305 static log_entry *le_root = NULL;
306 #endif
307
308 /* strange: if this function leaks memory (before psp_init() call?),
309  * resume after suspend breaks on 3.90 */
310 void lprintf(const char *fmt, ...)
311 {
312         va_list vl;
313
314 #ifdef LPRINTF_STDIO
315         va_start(vl, fmt);
316         vprintf(fmt, vl);
317         va_end(vl);
318 #else
319         static SceUID logfd = -1;
320         static int msg_count = 0;
321         char buff[256];
322         log_entry *le, *le1;
323
324         if (logfd == -2) return; // disabled
325
326         va_start(vl, fmt);
327         vsnprintf(buff, sizeof(buff), fmt, vl);
328         va_end(vl);
329
330         // note: this is still unsafe code
331         if (main_thread_id != sceKernelGetThreadId())
332         {
333                 le = malloc(sizeof(*le));
334                 if (le == NULL) return;
335                 le->next = NULL;
336                 strcpy(le->buff, buff);
337                 if (le_root == NULL) le_root = le;
338                 else {
339                         for (le1 = le_root; le1->next != NULL; le1 = le1->next);
340                         le1->next = le;
341                 }
342                 return;
343         }
344
345         logfd = sceIoOpen(LOG_FILE, PSP_O_WRONLY|PSP_O_APPEND, 0777);
346         if (logfd < 0) {
347                 if (msg_count == 0) logfd = -2;
348                 return;
349         }
350
351         if (le_root != NULL)
352         {
353                 le1 = le_root;
354                 le_root = NULL;
355                 sceKernelDelayThread(1000);
356                 while (le1 != NULL) {
357                         le = le1;
358                         le1 = le->next;
359                         sceIoWrite(logfd, le->buff, strlen(le->buff));
360                         free(le);
361                         msg_count++;
362                 }
363         }
364
365         sceIoWrite(logfd, buff, strlen(buff));
366         msg_count++;
367
368         // make sure it gets flushed
369         sceIoClose(logfd);
370         logfd = -1;
371 #endif
372 }
373
374