mapper fixes for ncpu, debug is broken atm
[fceu.git] / drivers / gp2x / minimal.c
1 /*
2   GP2X minimal library v0.A by rlyeh, 2005. emulnation.info@rlyeh (swap it!)
3
4   + GP2X 920t/940t CPUs library with a FIFO message system.
5   + GP2X video library with double buffering.
6   + GP2X sound library with double buffering.
7   + GP2X blitter library.
8   + GP2X timer library.
9   + GP2X joystick library.
10
11   Thanks to Squidge, Robster, snaff, Reesy and NK, for the help & previous work! :-)
12
13
14   License
15   =======
16
17   Free for non-commercial projects (it would be nice receiving a mail from you).
18   Other cases, ask me first.
19
20   GamePark Holdings is not allowed to use this library and/or use parts from it.
21
22
23   Known projects using the library or parts from it
24   =================================================
25
26   REminiscence-0.1.8 (rlyeh)
27   Atari800 GP2X pre-release 3 (foft)
28   XUMP (kedo)
29   MAME2X (Franxis)
30   DrMD for GP2X (Reesy)
31
32
33   What's new
34   ==========
35
36   0.A: 940t disabled all time unless required
37        sound is paused by default now, use gp2x_sound_pause() to unlock it
38
39        fixed functions:
40         - gp2x_sound_play()                        ; increased the number of sampling buffers
41         - gp2x_timer_read()                        ; it should work again. broken at some point before
42         - gp2x_dualcore_launch_program_from_disk() ; it missed the code uploading call
43
44        added functions:
45         - gp2x_sound_pause()
46         - gp2x_timer_delay()
47         - gp2x_dualcore_pause()
48
49   0.9: initial FIFO message system for dual cpu cores.
50        initial 48 Mb support.
51        initial quadruple buffering in 8bbp mode.
52
53        added functions:
54         - gp2x_dualcore_exec() ; initial FIFO message system for dual cpu cores.
55         - gp2x_dualcore_sync() ; initial FIFO message system for dual cpu cores.
56
57        improved functions:
58         - gp2x_video_flip()    ; initial quadruple buffering in 8bbp mode.
59
60   0.8: initial dual cores cpu support.
61        very basic blit functions by popular demand ;-)
62        vsync & hsync waiting code (thanks Reesy)
63
64        added functions:
65         - gp2x_launch_program()            ; initial dual cores cpu support.
66         - gp2x_launch_program_from_disk()  ; initial dual cores cpu support.
67         - gp2x_launch_subprogram()         ; initial dual cores cpu support.
68         - gp2x_blitter_rect15()            ; very basic blit function by popular demand ;-)
69         - gp2x_blitter_rect8()             ; very basic blit function by popular demand ;-)
70         - gp2x_video_hsync()               ; hsync waiting code (thanks Reesy)
71         - gp2x_video_vsync()               ; vsync waiting code (thanks Reesy)
72
73        fixed functions:
74         - gp2x_video_color8()              ; bugfixed a stupid typo (thanks Franxis for the bug report)
75
76   0.7: added functions:
77         - gp2x_sound_volume()
78
79        fixed functions:
80         - gp2x_deinit()           ; fixed menu launch code when exiting.
81
82        improved functions:
83         - gp2x_timer_read()       ; rewritten timer. it should be more accurate now.
84         - gp2x_init()             ; faster init code.
85
86   0.6: added functions:
87         - gp2x_timer_read()
88         - gp2x_sound_pause()
89
90        fixed functions:
91         - gp2x_video_setpalette() ; palette handling was incorrect. fixed.
92
93   0.5: improved functions:
94         - gp2x_init()             ; better and cleaner initalizing code.
95         - gp2x_init()             ; sound patched for real stereo output (by using NK's solution)
96
97   0.4: lots of cleanups.
98        sound is threaded and double buffered now.
99        8 bpp video support.
100
101        fixed functions:
102         - gp2x_deinit()           ; better and cleaner exiting code.
103
104   0.3: shorter library.
105
106        fixed functions:
107         - gp2x_joystick_read()    ; improved joystick diagonal detection.
108
109   0.2: better code layout.
110        public release.
111
112   0.1: beta release.
113 */
114
115 #include "minimal.h"
116 #include "squidgehack.h"
117 #include "asmutils.h"
118
119          unsigned char  *gp2x_screen8                 ,*gp2x_upperRAM;
120          unsigned long   gp2x_dev[8]={0,0,0,0,0,0,0,0}, gp2x_physvram[8], *gp2x_memregl,       gp2x_volume,                      gp2x_ticks_per_second;
121 volatile unsigned long   gp2x_sound_pausei=1,           gp2x_ticks=0,      gp2x_sound=0,      *gp2x_dualcore_ram=0;
122          unsigned short *gp2x_memregs,                 *gp2x_screen15,    *gp2x_logvram15[4],  gp2x_sound_buffer[4+((44100/25)*2)*8]; //25 Hz gives our biggest supported sampling buffer
123 volatile unsigned short  gp2x_palette[512];
124
125 extern void gp2x_sound_frame(void *blah, void *buff, int samples);
126
127 // hack
128 static int scaling_enabled = 0;
129
130 void gp2x_video_flip(void)
131 {
132   unsigned long address=gp2x_physvram[gp2x_physvram[7]];
133   if (scaling_enabled) address += 32;
134
135   // since we are using the mmu hack, we must flush the cache first
136   // (the params are most likely wrong, but they seem to work somehow)
137   flushcache(address, address + 320*240, 0);
138
139   if(++gp2x_physvram[7]==4) gp2x_physvram[7]=0;
140   gp2x_screen15=gp2x_logvram15[gp2x_physvram[7]];
141   gp2x_screen8=(unsigned char *)gp2x_screen15;
142
143   gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xFFFF);
144   gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16);
145   gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xFFFF);
146   gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16);
147 }
148
149 void gp2x_video_flip_single(void)
150 {
151   unsigned long address=gp2x_physvram[0];
152
153   gp2x_screen15=gp2x_logvram15[0];
154   gp2x_screen8=(unsigned char *)gp2x_screen15;
155
156   gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xFFFF);
157   gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16);
158   gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xFFFF);
159   gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16);
160 }
161
162
163 void gp2x_video_setgamma(unsigned short gamma) /*0..255*/
164 {
165   int i=256*3;
166   gp2x_memregs[0x295C>>1]=0;
167   while(i--) gp2x_memregs[0x295E>>1]=gamma;
168 }
169
170 void gp2x_video_setpalette(void)
171 {
172   unsigned short *g=(unsigned short *)gp2x_palette; int i=512;
173   gp2x_memregs[0x2958>>1]=0;
174   while(i--) gp2x_memregs[0x295A>>1]=*g++;
175 }
176
177 void gp2x_blitter_rect15(gp2x_rect *r)
178 {
179  int x, y; unsigned short *data=r->data15, *offset=&gp2x_screen15[r->x+r->y*320];
180
181  y=r->h; if(r->solid)
182          while(y--) { x=r->w; while(x--) *offset++=*data++; offset+=320-x; }
183          else
184          while(y--) { x=r->w; while(x--) { if(*data) *offset=*data; offset++, data++; }
185                       offset+=320-x; }
186 }
187
188 void gp2x_blitter_rect8(gp2x_rect *r)
189 {
190  int x, y; unsigned char *data=r->data8,   *offset=&gp2x_screen8[r->x+r->y*320];
191
192  y=r->h; if(r->solid)
193          while(y--) { x=r->w; while(x--) *offset++=*data++; offset+=320-x; }
194          else
195          while(y--) { x=r->w; while(x--) { if(*data) *offset=*data; offset++, data++; }
196                       offset+=320-x; }
197 }
198
199 unsigned long gp2x_joystick_read(void)
200 {
201   unsigned long value=(gp2x_memregs[0x1198>>1] & 0x00FF);
202
203   if(value==0xFD) value=0xFA;
204   if(value==0xF7) value=0xEB;
205   if(value==0xDF) value=0xAF;
206   if(value==0x7F) value=0xBE;
207
208   return ~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16));
209 }
210
211 void gp2x_sound_volume(int l, int r)
212 {
213  l=(((l*0x50)/100)<<8)|((r*0x50)/100);          //0x5A, 0x60
214  ioctl(gp2x_dev[4], SOUND_MIXER_WRITE_PCM, &l); //SOUND_MIXER_WRITE_VOLUME
215 }
216
217 void gp2x_timer_delay(unsigned long ticks)
218 {
219  unsigned long target=gp2x_memregl[0x0A00>>2]+ticks*gp2x_ticks_per_second;
220  while(gp2x_memregl[0x0A00>>2]<target);
221 }
222
223 unsigned long gp2x_timer_read(void)
224 {
225  return gp2x_memregl[0x0A00>>2]/gp2x_ticks_per_second;
226 }
227
228 unsigned long gp2x_timer_read_ms(void)
229 {
230  return gp2x_memregl[0x0A00>>2];
231 }
232
233
234 void gp2x_sound_pause(int yes) { gp2x_sound_pausei=yes; }
235
236 #if 0
237 static void *gp2x_sound_play(void *blah)
238 {
239   int flip=0, flyp=gp2x_sound_buffer[1];
240   struct timespec ts; ts.tv_sec=0, ts.tv_nsec=gp2x_sound_buffer[2];
241
242   while(!gp2x_sound)
243   {
244     nanosleep(&ts, NULL);
245
246
247    if(!gp2x_sound_pausei)
248    {
249      // [1] is sound buffer size     at 22050, 16, stereo, it is 1468 (367*4)
250      // [0] number of bytes?,  at 22050,16,stereo, it is 367
251      // first half of buffer
252
253      // first one is 368
254
255
256     gp2x_sound_frame(blah, (void *)(&gp2x_sound_buffer[4+flip]), gp2x_sound_buffer[0]);
257     // write out to second half of buffer
258     write(gp2x_dev[3],     (void *)(&gp2x_sound_buffer[4+flyp]), gp2x_sound_buffer[1]);
259
260     flip+=gp2x_sound_buffer[1];
261          if(flip==gp2x_sound_buffer[1]*8) flip=0;
262    flyp+=gp2x_sound_buffer[1];
263    if(flyp==gp2x_sound_buffer[1]*8) flyp=0;
264    }
265   }
266   return NULL;
267 }
268 #endif
269
270 #if 0
271 static void gp2x_initqueue(gp2x_queue *q, unsigned long queue_items, unsigned long *position920t, unsigned long *position940t)
272 {
273   q->head  = q->tail  = q->items = 0;
274   q->max_items = queue_items;
275   if(position920t) q->place920t=position920t; else q->place920t=(unsigned long *)malloc(sizeof(unsigned long) * queue_items);
276   if(position940t) q->place940t=position940t;
277   memset(q->place920t, 0, sizeof(unsigned long) * queue_items);
278 }
279
280 static void gp2x_enqueue(gp2x_queue *q, unsigned long data)
281 {
282   while(q->items==q->max_items); /*waiting for tail to decrease...*/
283   q->place920t[q->head = (q->head < q->max_items ? q->head+1 : 0)] = data;
284   q->items++;
285 }
286
287 /* UNUSED
288  static unsigned long gp2x_dequeue(gp2x_queue *q)
289 {
290   while(!q->items); //waiting for head to increase...
291   q->items--;
292   return q->place920t[q->tail = (q->tail < q->max_items ? q->tail+1 : 0)];
293 } */
294 #endif
295
296        void gp2x_dualcore_pause(int yes) { if(yes) gp2x_memregs[0x0904>>1] &= 0xFFFE; else gp2x_memregs[0x0904>>1] |= 1; }
297 static void gp2x_940t_reset(int yes)     { gp2x_memregs[0x3B48>>1] = ((yes&1) << 7) | (0x03); }
298 static void gp2x_940t_pause(int yes)     { gp2x_dualcore_pause(yes); }
299
300 static void gp2x_dualcore_registers(int save)
301 {
302  static unsigned short regs[8];
303
304  if(save)
305  {
306   regs[0]=gp2x_memregs[0x0904>>1];  regs[1]=gp2x_memregs[0x0912>>1];
307   regs[2]=gp2x_memregs[0x091c>>1];  regs[3]=gp2x_memregs[0x3b40>>1];
308   regs[4]=gp2x_memregs[0x3b42>>1];  regs[5]=gp2x_memregs[0x3b48>>1];
309   regs[6]=gp2x_memregs[0x3b44>>1];  regs[7]=gp2x_memregs[0x3b46>>1];
310
311   gp2x_940t_reset(1);
312   gp2x_940t_pause(1);
313  }
314  else
315  {
316   gp2x_memregs[0x0904>>1]=regs[0];  gp2x_memregs[0x0912>>1]=regs[1];
317   gp2x_memregs[0x091c>>1]=regs[2];  gp2x_memregs[0x3b40>>1]=regs[3];
318   gp2x_memregs[0x3b42>>1]=regs[4];  gp2x_memregs[0x3b48>>1]=regs[5];
319   gp2x_memregs[0x3b44>>1]=regs[6];  gp2x_memregs[0x3b46>>1]=regs[7];
320  }
321 }
322
323 #if 0
324 void gp2x_dualcore_sync(void)
325 {
326   gp2x_queue *q=(gp2x_queue *)gp2x_1stcore_data_ptr(GP2X_QUEUE_ARRAY_PTR);
327   while(q->items);
328 }
329
330 void gp2x_dualcore_exec(unsigned long command) { gp2x_enqueue((gp2x_queue *)gp2x_1stcore_data_ptr(GP2X_QUEUE_ARRAY_PTR),command); }
331
332 void gp2x_dualcore_launch_program(unsigned long *area, unsigned long size)
333 {
334   unsigned long i=0, *arm940t_ram=(unsigned long *)gp2x_dualcore_ram;
335
336   gp2x_940t_reset(1);
337
338   gp2x_memregs[0x3B40>>1] = 0;                               //disable interrupts
339   gp2x_memregs[0x3B42>>1] = 0;
340   gp2x_memregs[0x3B44>>1] = 0xffff;
341   gp2x_memregs[0x3B46>>1] = 0xffff;
342
343   gp2x_940t_pause(0);
344
345   while(i < size) *arm940t_ram++=area[i++];
346
347   gp2x_initqueue((gp2x_queue *)gp2x_1stcore_data_ptr(GP2X_QUEUE_ARRAY_PTR), GP2X_QUEUE_MAX_ITEMS, (unsigned long *)gp2x_1stcore_data_ptr(GP2X_QUEUE_DATA_PTR), (unsigned long *)gp2x_2ndcore_data_ptr(GP2X_QUEUE_DATA_PTR));
348
349   gp2x_940t_reset(0);
350 }
351
352 void gp2x_dualcore_launch_program_from_disk(const char *file, unsigned long offset, unsigned long size)
353 {
354  FILE *in; void *data;
355
356  if((in=fopen(file, "rb"))==NULL) return;
357  if((data=malloc(size))==NULL) { fclose(in); return; }
358  fseek(in, 0L, offset);
359  fread(data, 1, size, in);
360  gp2x_dualcore_launch_program((unsigned long *)data, size);
361  free(data);
362  fclose(in);
363 }
364 #endif
365
366 void gp2x_init(int ticks_per_second, int bpp, int rate, int bits, int stereo, int Hz)
367 {
368   struct fb_fix_screeninfo fixed_info;
369   static int first=1;
370
371   // GP2X_DO_SOUND
372   int gp2x_do_sound=1;
373   int frag=0;
374   int channels=1;
375   int stereoLocal=0;
376   int ret;
377
378   gp2x_ticks_per_second=7372800/ticks_per_second;
379
380   if(!gp2x_dev[0])   gp2x_dev[0] = open("/dev/fb0",   O_RDWR);
381   if(!gp2x_dev[1])   gp2x_dev[1] = open("/dev/fb1",   O_RDWR);
382   if(!gp2x_dev[2])   gp2x_dev[2] = open("/dev/mem",   O_RDWR);
383
384   ioctl(gp2x_dev[gp2x_physvram[7]=0], FBIOGET_FSCREENINFO, &fixed_info);
385    gp2x_physvram[2]=gp2x_physvram[0]=fixed_info.smem_start;
386    gp2x_screen15=gp2x_logvram15[2]=gp2x_logvram15[0]=
387     (unsigned short *)mmap(0, 0x20000*2, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], gp2x_physvram[0]);
388                                         gp2x_screen8=(unsigned char *)gp2x_screen15;
389   printf("/dev/fb0 is @ %08lx / %p\n", gp2x_physvram[0], gp2x_screen15);
390
391   ioctl(gp2x_dev[1], FBIOGET_FSCREENINFO, &fixed_info);
392    gp2x_physvram[3]=gp2x_physvram[1]=fixed_info.smem_start;
393    gp2x_logvram15[3]=gp2x_logvram15[1]=
394     (unsigned short *)mmap(0, 0x20000*2, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], gp2x_physvram[1]);
395   printf("/dev/fb1 is @ %08lx / %p\n", gp2x_physvram[1], gp2x_logvram15[1]);
396
397   // apply the MMU hack
398   ret = mmuhack();
399   printf("squidge hack code finished and returned %s\n", ret ? "ok" : "fail");
400
401   // don't run sound right now
402   if ( gp2x_do_sound)
403   {
404     if(!gp2x_dev[3])   gp2x_dev[3] = open("/dev/dsp",   O_WRONLY|O_ASYNC);
405     if(!gp2x_dev[4])   gp2x_dev[4] = open("/dev/mixer", O_RDWR);
406   }
407
408 //  gp2x_dualcore_ram=(unsigned long  *)mmap(0, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0x03000000);
409 /*gp2x_dualcore_ram=(unsigned long  *)mmap(0, 0x2000000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0x02000000);*/
410        gp2x_memregl=(unsigned long  *)mmap(0, 0x10000,   PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0xc0000000);
411         gp2x_memregs=(unsigned short *)gp2x_memregl;
412
413   if(first) { printf(MINILIB_VERSION "\n");
414               gp2x_dualcore_registers(1);
415               gp2x_sound_volume(100,100);
416               gp2x_memregs[0x0F16>>1] = 0x830a; usleep(100000);
417               gp2x_memregs[0x0F58>>1] = 0x100c; usleep(100000); }
418
419   gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/
420   gp2x_memregs[0x290C>>1]=320*((bpp+1)/8);       /*line width in bytes*/
421
422   memset(gp2x_screen15, 0, 320*240*2); gp2x_video_flip();
423   memset(gp2x_screen15, 0, 320*240*2); gp2x_video_flip();
424
425   //if(bpp==8)  gp2x_physvram[2]+=320*240,    gp2x_physvram[3]+=320*240,
426   //           gp2x_logvram15[2]+=320*240/2, gp2x_logvram15[3]+=320*240/2;
427   if(bpp==8)  gp2x_physvram[2]+=0x20000,   gp2x_physvram[3]+=0x20000,
428              gp2x_logvram15[2]+=0x20000/2, gp2x_logvram15[3]+=0x20000/2;
429
430
431  if ( gp2x_do_sound)
432  {
433     ioctl(gp2x_dev[3], SNDCTL_DSP_SPEED,  &rate);
434     ioctl(gp2x_dev[3], SNDCTL_DSP_SETFMT, &bits);
435
436     ioctl(gp2x_dev[3], SNDCTL_DSP_CHANNELS, &channels);
437     ioctl(gp2x_dev[3], SNDCTL_DSP_STEREO, &stereoLocal);
438
439     frag = 0x40000|13;
440     ioctl(gp2x_dev[3], SNDCTL_DSP_SETFRAGMENT, &frag);
441
442     {
443         // calculate buffer size
444         int frag = 0, bsize, buffers = 16;
445         bsize = rate / 32;
446         if (rate > 22050) { bsize*=4; buffers*=2; } // 44k mode seems to be very demanding
447         while ((bsize>>=1)) frag++;
448         frag |= buffers<<16; // 16 buffers
449         ioctl(gp2x_dev[3], SNDCTL_DSP_SETFRAGMENT, &frag);
450         printf("gp2x_set_sound: %i/%ibit/%s, %i buffers of %i bytes\n",
451                 rate, bits, stereo?"stereo":"mono", frag>>16, 1<<(frag&0xffff));
452     }
453
454     if(first)
455     {
456       first=0;
457     }
458   }
459 }
460
461
462 extern int fcloseall(void);
463 void gp2x_deinit(void)
464 {
465   while((gp2x_sound++)<1000000);                               //wait arm920t threads to finish
466
467   gp2x_dualcore_registers(0);
468   mmuunhack();
469
470   gp2x_memregs[0x28DA>>1]=0x4AB;                               //set video mode
471   gp2x_memregs[0x290C>>1]=640;
472
473   { int i; for(i=0;i<8;i++) if(gp2x_dev[i]) close(gp2x_dev[i]); }    //close all devices
474
475   fcloseall();                                                   //close all files
476
477 }
478
479
480 void SetVideoScaling(int pixels,int width,int height)
481 {
482         float x, y;
483         float xscale,yscale=0;
484
485         int bpp=(gp2x_memregs[0x28DA>>1]>>9)&0x3;  /* bytes per pixel */
486
487         scaling_enabled = (width == 320) ? 0 : 1;
488
489         if(gp2x_memregs[0x2800>>1]&0x100) /* TV-Out ON? */
490         {
491                 xscale=489.0; /* X-Scale with TV-Out (PAL or NTSC) */
492                 if (gp2x_memregs[0x2818>>1]  == 287) /* PAL? */
493                         yscale=(pixels*274.0)/320.0; /* Y-Scale with PAL */
494                 else if (gp2x_memregs[0x2818>>1]  == 239) /* NTSC? */
495                         yscale=(pixels*331.0)/320.0; /* Y-Scale with NTSC */
496         }
497         else /* TV-Out OFF? */
498         {
499                 xscale=1024.0; /* X-Scale for LCD */
500                 yscale=pixels; /* Y-Scale for LCD */
501         }
502
503         x=(xscale*width)/320.0;
504         y=(yscale*bpp*height)/240.0;
505         gp2x_memregs[0x2906>>1]=(unsigned short)x; /* scale horizontal */
506         gp2x_memregl[0x2908>>2]=(unsigned  long)y; /* scale vertical */
507         gp2x_memregs[0x290C>>1]=pixels*bpp; /* Set Video Width */
508 }