4a628c7638d5d5283dd8227ac37897092da58b88
[libpicofe.git] / pandora / emu.c
1 // (c) Copyright 2006-2009 notaz, All rights reserved.\r
2 // Free for non-commercial use.\r
3 \r
4 // For commercial use, separate licencing terms must be obtained.\r
5 \r
6 #include <stdio.h>\r
7 \r
8 #include "../common/emu.h"\r
9 #include "../common/menu.h"\r
10 #include "../common/plat.h"\r
11 #include "../common/arm_utils.h"\r
12 #include "../linux/sndout_oss.h"\r
13 #include "asm_utils.h"\r
14 #include "version.h"\r
15 \r
16 #include <pico/pico_int.h>\r
17 \r
18 #define USE_320_SCREEN 1\r
19 \r
20 \r
21 static short __attribute__((aligned(4))) sndBuffer[2*44100/50];\r
22 static unsigned char temp_frame[g_screen_width * g_screen_height * 2];\r
23 unsigned char *PicoDraw2FB = temp_frame;\r
24 static int osd_fps_x;\r
25 char cpu_clk_name[] = "unused";\r
26 \r
27 \r
28 void pemu_prep_defconfig(void)\r
29 {\r
30         // XXX: move elsewhere\r
31         g_menubg_ptr = temp_frame;\r
32 }\r
33 \r
34 void pemu_validate_config(void)\r
35 {\r
36 }\r
37 \r
38 // FIXME: cleanup\r
39 static void osd_text(int x, int y, const char *text)\r
40 {\r
41         int len = strlen(text)*8;\r
42 \r
43         if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
44                 int *p, i, h;\r
45                 x &= ~3; // align x\r
46                 len = (len+3) >> 2;\r
47                 for (h = 0; h < 8; h++) {\r
48                         p = (int *) ((unsigned char *) g_screen_ptr+x+g_screen_width*(y+h));\r
49                         for (i = len; i; i--, p++) *p = 0xe0e0e0e0;\r
50                 }\r
51                 emu_text_out8(x, y, text);\r
52         } else {\r
53                 int *p, i, h;\r
54                 x &= ~1; // align x\r
55                 len++;\r
56                 for (h = 0; h < 16; h++) {\r
57                         p = (int *) ((unsigned short *) g_screen_ptr+x+g_screen_width*(y+h));\r
58                         for (i = len; i; i--, p++) *p = 0;//(*p>>2)&0x39e7;\r
59                 }\r
60                 text_out16(x, y, text);\r
61         }\r
62 }\r
63 \r
64 static void draw_cd_leds(void)\r
65 {\r
66 //      static\r
67         int old_reg;\r
68 //      if (!((Pico_mcd->s68k_regs[0] ^ old_reg) & 3)) return; // no change // mmu hack problems?\r
69         old_reg = Pico_mcd->s68k_regs[0];\r
70 \r
71         if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
72                 // 8-bit modes\r
73                 unsigned int col_g = (old_reg & 2) ? 0xc0c0c0c0 : 0xe0e0e0e0;\r
74                 unsigned int col_r = (old_reg & 1) ? 0xd0d0d0d0 : 0xe0e0e0e0;\r
75                 *(unsigned int *)((char *)g_screen_ptr + g_screen_width*2+ 4) =\r
76                 *(unsigned int *)((char *)g_screen_ptr + g_screen_width*3+ 4) =\r
77                 *(unsigned int *)((char *)g_screen_ptr + g_screen_width*4+ 4) = col_g;\r
78                 *(unsigned int *)((char *)g_screen_ptr + g_screen_width*2+12) =\r
79                 *(unsigned int *)((char *)g_screen_ptr + g_screen_width*3+12) =\r
80                 *(unsigned int *)((char *)g_screen_ptr + g_screen_width*4+12) = col_r;\r
81         } else {\r
82                 // 16-bit modes\r
83                 unsigned int *p = (unsigned int *)((short *)g_screen_ptr + g_screen_width*2+4);\r
84                 unsigned int col_g = (old_reg & 2) ? 0x06000600 : 0;\r
85                 unsigned int col_r = (old_reg & 1) ? 0xc000c000 : 0;\r
86                 *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += g_screen_width/2 - 12/2;\r
87                 *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += g_screen_width/2 - 12/2;\r
88                 *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r;\r
89         }\r
90 }\r
91 \r
92 #ifdef USE_320_SCREEN\r
93 \r
94 static int EmuScanBegin16(unsigned int num)\r
95 {\r
96         DrawLineDest = (unsigned short *)g_screen_ptr + num*800 + 800/2 - 320/2;\r
97         //int w = (Pico.video.reg[12]&1) ? 320 : 256;\r
98         //DrawLineDest = (unsigned short *)g_screen_ptr + num*w;\r
99 \r
100         return 0;\r
101 }\r
102 \r
103 #else // USE_320_SCREEN\r
104 \r
105 static int EmuScanEnd16(unsigned int num)\r
106 {\r
107         unsigned char  *ps=HighCol+8;\r
108         unsigned short *pd;\r
109         unsigned short *pal=HighPal;\r
110         int sh = Pico.video.reg[0xC]&8;\r
111         int len, mask = 0xff;\r
112 \r
113         pd=(unsigned short *)g_screen_ptr + num*800*2 + 800/2 - 320*2/2;\r
114 \r
115         if (Pico.m.dirtyPal)\r
116                 PicoDoHighPal555(sh);\r
117 \r
118         if (Pico.video.reg[12]&1) {\r
119                 len = 320;\r
120         } else {\r
121                 pd += 32*2;\r
122                 len = 256;\r
123         }\r
124 \r
125         if (!sh && (rendstatus & PDRAW_SPR_LO_ON_HI))\r
126                 mask=0x3f; // messed sprites, upper bits are priority stuff\r
127 \r
128 #if 1\r
129         clut_line(pd, ps, pal, (mask<<16) | len);\r
130 #else\r
131         for (; len > 0; len--)\r
132         {\r
133                 unsigned int p = pal[*ps++ & mask];\r
134                 p |= p << 16;\r
135                 *(unsigned int *)pd = p;\r
136                 *(unsigned int *)(&pd[800]) = p;\r
137                 pd += 2;\r
138         }\r
139 #endif\r
140 \r
141         return 0;\r
142 }\r
143 \r
144 #endif // USE_320_SCREEN\r
145 \r
146 void pemu_update_display(const char *fps, const char *notice)\r
147 {\r
148         if (notice || (currentConfig.EmuOpt & EOPT_SHOW_FPS)) {\r
149                 if (notice)\r
150                         osd_text(4, 460, notice);\r
151                 if (currentConfig.EmuOpt & EOPT_SHOW_FPS)\r
152                         osd_text(osd_fps_x, 460, fps);\r
153         }\r
154         if ((PicoAHW & PAHW_MCD) && (currentConfig.EmuOpt & EOPT_EN_CD_LEDS))\r
155                 draw_cd_leds();\r
156 }\r
157 \r
158 void plat_video_toggle_renderer(int is_next, int force_16bpp, int is_menu)\r
159 {\r
160         // this will auto-select SMS/32X renderers\r
161         PicoDrawSetColorFormat(1);\r
162 }\r
163 \r
164 void plat_video_menu_enter(int is_rom_loaded)\r
165 {\r
166 }\r
167 \r
168 void plat_video_menu_begin(void)\r
169 {\r
170         memcpy32(g_screen_ptr, g_menubg_ptr, g_screen_width * g_screen_height * 2 / 4);\r
171 }\r
172 \r
173 void plat_video_menu_end(void)\r
174 {\r
175 }\r
176 \r
177 void plat_status_msg_clear(void)\r
178 {\r
179         unsigned short *d = (unsigned short *)g_screen_ptr + g_screen_width * g_screen_height;\r
180         int l = g_screen_width * 8;\r
181         memset32((int *)(d - l), 0, l * 2 / 4);\r
182 }\r
183 \r
184 void plat_status_msg_busy_next(const char *msg)\r
185 {\r
186         plat_status_msg_clear();\r
187         pemu_update_display("", msg);\r
188         emu_status_msg("");\r
189         reset_timing = 1;\r
190 }\r
191 \r
192 void plat_status_msg_busy_first(const char *msg)\r
193 {\r
194 //      memset32(g_screen_ptr, 0, g_screen_width * g_screen_height * 2 / 4);\r
195         plat_status_msg_busy_next(msg);\r
196 }\r
197 \r
198 void plat_update_volume(int has_changed, int is_up)\r
199 {\r
200         static int prev_frame = 0, wait_frames = 0;\r
201         int vol = currentConfig.volume;\r
202 \r
203         if (has_changed)\r
204         {\r
205                 if (is_up) {\r
206                         if (vol < 99) vol++;\r
207                 } else {\r
208                         if (vol >  0) vol--;\r
209                 }\r
210                 wait_frames = 0;\r
211                 sndout_oss_setvol(vol, vol);\r
212                 currentConfig.volume = vol;\r
213                 emu_status_msg("VOL: %02i", vol);\r
214                 prev_frame = Pico.m.frame_count;\r
215         }\r
216 }\r
217 \r
218 void pemu_forced_frame(int opts)\r
219 {\r
220         int po_old = PicoOpt;\r
221         int eo_old = currentConfig.EmuOpt;\r
222 \r
223         PicoOpt &= ~0x10;\r
224         PicoOpt |= opts|POPT_ACC_SPRITES; // acc_sprites\r
225         currentConfig.EmuOpt |= 0x80;\r
226 \r
227 #ifdef USE_320_SCREEN\r
228         PicoDrawSetColorFormat(1);\r
229         PicoScanBegin = EmuScanBegin16;\r
230 #else\r
231         PicoDrawSetColorFormat(-1);\r
232         PicoScanEnd = EmuScanEnd16;\r
233 #endif\r
234         Pico.m.dirtyPal = 1;\r
235         PicoFrameDrawOnly();\r
236 \r
237         PicoOpt = po_old;\r
238         currentConfig.EmuOpt = eo_old;\r
239 }\r
240 \r
241 static void updateSound(int len)\r
242 {\r
243         len <<= 1;\r
244         if (PicoOpt & POPT_EN_STEREO)\r
245                 len <<= 1;\r
246 \r
247         if ((currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) && !sndout_oss_can_write(len))\r
248                 return;\r
249 \r
250         /* avoid writing audio when lagging behind to prevent audio lag */\r
251         if (PicoSkipFrame != 2)\r
252                 sndout_oss_write(PsndOut, len);\r
253 }\r
254 \r
255 void pemu_sound_start(void)\r
256 {\r
257         static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;\r
258         int target_fps = Pico.m.pal ? 50 : 60;\r
259 \r
260         PsndOut = NULL;\r
261 \r
262         if (currentConfig.EmuOpt & 4)\r
263         {\r
264                 int snd_excess_add;\r
265                 if (PsndRate != PsndRate_old || (PicoOpt&0x20b) != (PicoOpt_old&0x20b) || Pico.m.pal != pal_old)\r
266                         PsndRerate(Pico.m.frame_count ? 1 : 0);\r
267 \r
268                 snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
269                 printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n",\r
270                         PsndRate, PsndLen, snd_excess_add, (PicoOpt&8)>>3, Pico.m.pal);\r
271                 sndout_oss_start(PsndRate, 16, (PicoOpt&8)>>3);\r
272                 sndout_oss_setvol(currentConfig.volume, currentConfig.volume);\r
273                 PicoWriteSound = updateSound;\r
274                 plat_update_volume(0, 0);\r
275                 memset(sndBuffer, 0, sizeof(sndBuffer));\r
276                 PsndOut = sndBuffer;\r
277                 PsndRate_old = PsndRate;\r
278                 PicoOpt_old  = PicoOpt;\r
279                 pal_old = Pico.m.pal;\r
280         }\r
281 }\r
282 \r
283 void pemu_sound_stop(void)\r
284 {\r
285 }\r
286 \r
287 void pemu_sound_wait(void)\r
288 {\r
289         // don't need to do anything, writes will block by themselves\r
290 }\r
291 \r
292 void plat_debug_cat(char *str)\r
293 {\r
294 }\r
295 \r
296 void emu_video_mode_change(int start_line, int line_count, int is_32cols)\r
297 {\r
298         osd_fps_x = 260;\r
299 \r
300         // clear whole screen in all buffers\r
301         memset32(g_screen_ptr, 0, g_screen_width * g_screen_height * 2 / 4);\r
302 }\r
303 \r
304 void pemu_loop_prep(void)\r
305 {\r
306 #ifdef USE_320_SCREEN\r
307         PicoDrawSetColorFormat(1);\r
308         PicoScanBegin = EmuScanBegin16;\r
309 #else\r
310         PicoDrawSetColorFormat(-1);\r
311         PicoScanEnd = EmuScanEnd16;\r
312 #endif\r
313 \r
314         pemu_sound_start();\r
315 }\r
316 \r
317 void pemu_loop_end(void)\r
318 {\r
319         int po_old = PicoOpt;\r
320         int eo_old = currentConfig.EmuOpt;\r
321 \r
322         pemu_sound_stop();\r
323         memset32(g_screen_ptr, 0, g_screen_width * g_screen_height * 2 / 4);\r
324 \r
325         /* do one more frame for menu bg */\r
326         PicoOpt &= ~POPT_ALT_RENDERER;\r
327         PicoOpt |= POPT_EN_SOFTSCALE|POPT_ACC_SPRITES;\r
328         currentConfig.EmuOpt |= EOPT_16BPP;\r
329 \r
330         PicoDrawSetColorFormat(1);\r
331         Pico.m.dirtyPal = 1;\r
332         PicoFrame();\r
333 \r
334         PicoOpt = po_old;\r
335         currentConfig.EmuOpt = eo_old;\r
336 }\r
337 \r
338 /* XXX: avoid busy wait somehow? */\r
339 void plat_wait_till_us(unsigned int us_to)\r
340 {\r
341         unsigned int now;\r
342 \r
343         spend_cycles(1024);\r
344         now = plat_get_ticks_us();\r
345 \r
346         while ((signed int)(us_to - now) > 512)\r
347         {\r
348                 spend_cycles(1024);\r
349                 now = plat_get_ticks_us();\r
350         }\r
351 }\r
352 \r
353 void plat_video_wait_vsync(void)\r
354 {\r
355 }\r
356 \r
357 const char *plat_get_credits(void)\r
358 {\r
359         return "PicoDrive v" VERSION " (c) notaz, 2006-2009\n\n\n"\r
360                 "Credits:\n"\r
361                 "fDave: Cyclone 68000 core,\n"\r
362                 "      base code of PicoDrive\n"\r
363                 "Reesy & FluBBa: DrZ80 core\n"\r
364                 "MAME devs: YM2612 and SN76496 cores\n"\r
365                 "rlyeh and others: minimal SDK\n"\r
366                 "Squidge: mmuhack\n"\r
367                 "Dzz: ARM940 sample\n"\r
368                 "GnoStiC / Puck2099: USB joy code\n"\r
369                 "craigix: GP2X hardware\n"\r
370                 "ketchupgun: skin design\n"\r
371                 "\n"\r
372                 "special thanks (for docs, ideas):\n"\r
373                 " Charles MacDonald, Haze,\n"\r
374                 " Stephane Dallongeville,\n"\r
375                 " Lordus, Exophase, Rokas,\n"\r
376                 " Nemesis, Tasco Deluxe";\r
377 }\r