mmuhack, 6502 tweaks, fskip
[fceu.git] / drivers / gp2x / minimal.c
CommitLineData
35868d35 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.
22f08d95 51 initial quadruple buffering in 8bbp mode.
35868d35 52
53 added functions:
22f08d95 54 - gp2x_dualcore_exec() ; initial FIFO message system for dual cpu cores.
35868d35 55 - gp2x_dualcore_sync() ; initial FIFO message system for dual cpu cores.
56
57 improved functions:
22f08d95 58 - gp2x_video_flip() ; initial quadruple buffering in 8bbp mode.
35868d35 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"
937bf65b 116#include "squidgehack.h"
117#include "asmutils.h"
35868d35 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;
937bf65b 121volatile unsigned long gp2x_sound_pausei=1, gp2x_ticks=0, gp2x_sound=0, *gp2x_dualcore_ram=0;
35868d35 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
123volatile unsigned short gp2x_palette[512];
35868d35 124
125extern void gp2x_sound_frame(void *blah, void *buff, int samples);
126
937bf65b 127// hack
128static int scaling_enabled = 0;
35868d35 129
130void gp2x_video_flip(void)
131{
132 unsigned long address=gp2x_physvram[gp2x_physvram[7]];
937bf65b 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);
35868d35 138
139 if(++gp2x_physvram[7]==4) gp2x_physvram[7]=0;
22f08d95 140 gp2x_screen15=gp2x_logvram15[gp2x_physvram[7]];
141 gp2x_screen8=(unsigned char *)gp2x_screen15;
142
35868d35 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
149void gp2x_video_flip_single(void)
150{
151 unsigned long address=gp2x_physvram[0];
152
22f08d95 153 gp2x_screen15=gp2x_logvram15[0];
154 gp2x_screen8=(unsigned char *)gp2x_screen15;
155
35868d35 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
163void gp2x_video_setgamma(unsigned short gamma) /*0..255*/
164{
22f08d95 165 int i=256*3;
166 gp2x_memregs[0x295C>>1]=0;
167 while(i--) gp2x_memregs[0x295E>>1]=gamma;
35868d35 168}
169
170void gp2x_video_setpalette(void)
171{
172 unsigned short *g=(unsigned short *)gp2x_palette; int i=512;
22f08d95 173 gp2x_memregs[0x2958>>1]=0;
174 while(i--) gp2x_memregs[0x295A>>1]=*g++;
35868d35 175}
176
177void 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
188void gp2x_blitter_rect8(gp2x_rect *r)
189{
22f08d95 190 int x, y; unsigned char *data=r->data8, *offset=&gp2x_screen8[r->x+r->y*320];
35868d35 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}
22f08d95 198
35868d35 199unsigned 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;
22f08d95 207
35868d35 208 return ~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16));
209}
210
211void 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
217void gp2x_timer_delay(unsigned long ticks)
218{
219 unsigned long target=gp2x_memregl[0x0A00>>2]+ticks*gp2x_ticks_per_second;
22f08d95 220 while(gp2x_memregl[0x0A00>>2]<target);
35868d35 221}
222
223unsigned long gp2x_timer_read(void)
224{
225 return gp2x_memregl[0x0A00>>2]/gp2x_ticks_per_second;
22f08d95 226}
35868d35 227
228unsigned long gp2x_timer_read_ms(void)
229{
230 return gp2x_memregl[0x0A00>>2];
22f08d95 231}
35868d35 232
233
234void gp2x_sound_pause(int yes) { gp2x_sound_pausei=yes; }
235
22f08d95 236#if 0
35868d35 237static 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
22f08d95 242 while(!gp2x_sound)
243 {
35868d35 244 nanosleep(&ts, NULL);
22f08d95 245
246
247 if(!gp2x_sound_pausei)
248 {
35868d35 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
22f08d95 255
35868d35 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;
22f08d95 262 flyp+=gp2x_sound_buffer[1];
35868d35 263 if(flyp==gp2x_sound_buffer[1]*8) flyp=0;
22f08d95 264 }
35868d35 265 }
266 return NULL;
267}
22f08d95 268#endif
35868d35 269
937bf65b 270#if 0
35868d35 271static 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
280static void gp2x_enqueue(gp2x_queue *q, unsigned long data)
22f08d95 281{
35868d35 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} */
937bf65b 294#endif
35868d35 295
296 void gp2x_dualcore_pause(int yes) { if(yes) gp2x_memregs[0x0904>>1] &= 0xFFFE; else gp2x_memregs[0x0904>>1] |= 1; }
297static void gp2x_940t_reset(int yes) { gp2x_memregs[0x3B48>>1] = ((yes&1) << 7) | (0x03); }
298static void gp2x_940t_pause(int yes) { gp2x_dualcore_pause(yes); }
299
300static 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
937bf65b 323#if 0
35868d35 324void 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
330void gp2x_dualcore_exec(unsigned long command) { gp2x_enqueue((gp2x_queue *)gp2x_1stcore_data_ptr(GP2X_QUEUE_ARRAY_PTR),command); }
331
332void gp2x_dualcore_launch_program(unsigned long *area, unsigned long size)
333{
334 unsigned long i=0, *arm940t_ram=(unsigned long *)gp2x_dualcore_ram;
22f08d95 335
35868d35 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
22f08d95 343 gp2x_940t_pause(0);
344
35868d35 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
352void 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}
937bf65b 364#endif
35868d35 365
366void 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;
937bf65b 376 int ret;
35868d35 377
22f08d95 378 gp2x_ticks_per_second=7372800/ticks_per_second;
35868d35 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);
22f08d95 382 if(!gp2x_dev[2]) gp2x_dev[2] = open("/dev/mem", O_RDWR);
383
937bf65b 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
35868d35 401 // don't run sound right now
402 if ( gp2x_do_sound)
403 {
22f08d95 404 if(!gp2x_dev[3]) gp2x_dev[3] = open("/dev/dsp", O_WRONLY|O_ASYNC);
35868d35 405 if(!gp2x_dev[4]) gp2x_dev[4] = open("/dev/mixer", O_RDWR);
406 }
407
937bf65b 408// gp2x_dualcore_ram=(unsigned long *)mmap(0, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0x03000000);
35868d35 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");
22f08d95 414 gp2x_dualcore_registers(1);
35868d35 415 gp2x_sound_volume(100,100);
416 gp2x_memregs[0x0F16>>1] = 0x830a; usleep(100000);
417 gp2x_memregs[0x0F58>>1] = 0x100c; usleep(100000); }
418
35868d35 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
937bf65b 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;
35868d35 429
430
431 if ( gp2x_do_sound)
432 {
35868d35 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);
22f08d95 441
35868d35 442
443 printf("minimal() do sound, rate %d, bits %d, stereo %d, frag %d\n", rate, bits, stereo, frag);
444
22f08d95 445 if(first)
446 {
447 first=0;
35868d35 448 }
22f08d95 449 }
35868d35 450}
451
452
453extern int fcloseall(void);
454void gp2x_deinit(void)
455{
456 while((gp2x_sound++)<1000000); //wait arm920t threads to finish
457
458 gp2x_dualcore_registers(0);
937bf65b 459 mmuunhack();
35868d35 460
461 gp2x_memregs[0x28DA>>1]=0x4AB; //set video mode
22f08d95 462 gp2x_memregs[0x290C>>1]=640;
463
35868d35 464 { int i; for(i=0;i<8;i++) if(gp2x_dev[i]) close(gp2x_dev[i]); } //close all devices
465
466 fcloseall(); //close all files
467
468}
469
22f08d95 470
35868d35 471void SetVideoScaling(int pixels,int width,int height)
472{
473 float x, y;
22f08d95 474 float xscale,yscale=0;
35868d35 475
476 int bpp=(gp2x_memregs[0x28DA>>1]>>9)&0x3; /* bytes per pixel */
477
937bf65b 478 scaling_enabled = (width == 320) ? 0 : 1;
479
35868d35 480 if(gp2x_memregs[0x2800>>1]&0x100) /* TV-Out ON? */
481 {
482 xscale=489.0; /* X-Scale with TV-Out (PAL or NTSC) */
483 if (gp2x_memregs[0x2818>>1] == 287) /* PAL? */
484 yscale=(pixels*274.0)/320.0; /* Y-Scale with PAL */
485 else if (gp2x_memregs[0x2818>>1] == 239) /* NTSC? */
486 yscale=(pixels*331.0)/320.0; /* Y-Scale with NTSC */
487 }
22f08d95 488 else /* TV-Out OFF? */
35868d35 489 {
490 xscale=1024.0; /* X-Scale for LCD */
491 yscale=pixels; /* Y-Scale for LCD */
492 }
493
494 x=(xscale*width)/320.0;
495 y=(yscale*bpp*height)/240.0;
496 gp2x_memregs[0x2906>>1]=(unsigned short)x; /* scale horizontal */
497 gp2x_memregl[0x2908>>2]=(unsigned long)y; /* scale vertical */
498 gp2x_memregs[0x290C>>1]=pixels*bpp; /* Set Video Width */
499}