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 
main()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 
main()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 */
exit_callback(int arg1,int arg2,void * common)76 static int exit_callback(int arg1, int arg2, void *common)
77 {
78 	sceKernelExitGame();
79 	return 0;
80 }
81 
82 /* Power Callback */
power_callback(int unknown,int pwrflags,void * common)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 */
callback_thread(SceSize args,void * argp)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 
psp_init(void)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 
psp_finish(void)184 void psp_finish(void)
185 {
186 	lprintf("psp_finish..\n");
187 	sceGuTerm();
188 
189 	//sceKernelSleepThread();
190 	sceKernelExitGame();
191 }
192 
psp_video_flip(int wait_vsync)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 
psp_video_get_active_fb(void)202 void *psp_video_get_active_fb(void)
203 {
204 	return current_screen ? VRAM_FB1 : VRAM_FB0;
205 }
206 
psp_video_switch_to_single(void)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 
psp_msleep(int ms)214 void psp_msleep(int ms)
215 {
216 	sceKernelDelayThread(ms * 1000);
217 }
218 
psp_pad_read(int blocking)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 
psp_get_cpu_clock(void)238 int psp_get_cpu_clock(void)
239 {
240 	return scePowerGetCpuClockFrequencyInt();
241 }
242 
psp_set_cpu_clock(int clock)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 
psp_get_status_line(void)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 
psp_wait_suspend(void)268 void psp_wait_suspend(void)
269 {
270 	// probably should do something smarter here?
271 	sceDisplayWaitVblankStart();
272 }
273 
psp_resume_suspend(void)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 */
lprintf(const char * fmt,...)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 
375