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