98733207 |
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 | |
117 | unsigned char *gp2x_screen8 ,*gp2x_upperRAM; |
118 | unsigned long gp2x_dev[8]={0,0,0,0,0,0,0,0}, gp2x_physvram[8], *gp2x_memregl, gp2x_volume, gp2x_ticks_per_second; |
119 | volatile unsigned long gp2x_sound_pausei=1, gp2x_ticks=0, gp2x_sound=0, *gp2x_dualcore_ram; |
120 | 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 |
121 | volatile unsigned short gp2x_palette[512]; |
122 | pthread_t gp2x_sound_thread=0; |
123 | |
124 | extern void gp2x_sound_frame(void *blah, void *buff, int samples); |
125 | |
126 | |
127 | void gp2x_video_flip(void) |
128 | { |
129 | unsigned long address=gp2x_physvram[gp2x_physvram[7]]; |
130 | |
131 | if(++gp2x_physvram[7]==4) gp2x_physvram[7]=0; |
132 | gp2x_screen15=gp2x_logvram15[gp2x_physvram[7]]; |
133 | gp2x_screen8=(unsigned char *)gp2x_screen15; |
134 | |
135 | gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xFFFF); |
136 | gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16); |
137 | gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xFFFF); |
138 | gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16); |
139 | } |
140 | |
141 | void gp2x_video_flip_single(void) |
142 | { |
143 | unsigned long address=gp2x_physvram[0]; |
144 | |
145 | gp2x_screen15=gp2x_logvram15[0]; |
146 | gp2x_screen8=(unsigned char *)gp2x_screen15; |
147 | |
148 | gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xFFFF); |
149 | gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16); |
150 | gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xFFFF); |
151 | gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16); |
152 | } |
153 | |
154 | |
155 | void gp2x_video_setgamma(unsigned short gamma) /*0..255*/ |
156 | { |
157 | int i=256*3; |
158 | gp2x_memregs[0x295C>>1]=0; |
159 | while(i--) gp2x_memregs[0x295E>>1]=gamma; |
160 | } |
161 | |
162 | void gp2x_video_setpalette(void) |
163 | { |
164 | unsigned short *g=(unsigned short *)gp2x_palette; int i=512; |
165 | gp2x_memregs[0x2958>>1]=0; |
166 | while(i--) gp2x_memregs[0x295A>>1]=*g++; |
167 | } |
168 | |
169 | void gp2x_blitter_rect15(gp2x_rect *r) |
170 | { |
171 | int x, y; unsigned short *data=r->data15, *offset=&gp2x_screen15[r->x+r->y*320]; |
172 | |
173 | y=r->h; if(r->solid) |
174 | while(y--) { x=r->w; while(x--) *offset++=*data++; offset+=320-x; } |
175 | else |
176 | while(y--) { x=r->w; while(x--) { if(*data) *offset=*data; offset++, data++; } |
177 | offset+=320-x; } |
178 | } |
179 | |
180 | void gp2x_blitter_rect8(gp2x_rect *r) |
181 | { |
182 | int x, y; unsigned char *data=r->data8, *offset=&gp2x_screen8[r->x+r->y*320]; |
183 | |
184 | y=r->h; if(r->solid) |
185 | while(y--) { x=r->w; while(x--) *offset++=*data++; offset+=320-x; } |
186 | else |
187 | while(y--) { x=r->w; while(x--) { if(*data) *offset=*data; offset++, data++; } |
188 | offset+=320-x; } |
189 | } |
190 | |
191 | unsigned long gp2x_joystick_read(void) |
192 | { |
193 | unsigned long value=(gp2x_memregs[0x1198>>1] & 0x00FF); |
194 | |
195 | if(value==0xFD) value=0xFA; |
196 | if(value==0xF7) value=0xEB; |
197 | if(value==0xDF) value=0xAF; |
198 | if(value==0x7F) value=0xBE; |
199 | |
200 | return ~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16)); |
201 | } |
202 | |
203 | void gp2x_sound_volume(int l, int r) |
204 | { |
205 | l=(((l*0x50)/100)<<8)|((r*0x50)/100); //0x5A, 0x60 |
206 | ioctl(gp2x_dev[4], SOUND_MIXER_WRITE_PCM, &l); //SOUND_MIXER_WRITE_VOLUME |
207 | } |
208 | |
209 | void gp2x_timer_delay(unsigned long ticks) |
210 | { |
211 | unsigned long target=gp2x_memregl[0x0A00>>2]+ticks*gp2x_ticks_per_second; |
212 | while(gp2x_memregl[0x0A00>>2]<target); |
213 | } |
214 | |
215 | unsigned long gp2x_timer_read(void) |
216 | { |
217 | return gp2x_memregl[0x0A00>>2]/gp2x_ticks_per_second; |
218 | } |
219 | |
220 | unsigned long gp2x_timer_read_ms(void) |
221 | { |
222 | return gp2x_memregl[0x0A00>>2]; |
223 | } |
224 | |
225 | |
226 | void gp2x_sound_pause(int yes) { gp2x_sound_pausei=yes; } |
227 | |
228 | |
229 | static void *gp2x_sound_play(void *blah) |
230 | { |
231 | int flip=0, flyp=gp2x_sound_buffer[1]; |
232 | struct timespec ts; ts.tv_sec=0, ts.tv_nsec=gp2x_sound_buffer[2]; |
233 | |
234 | while(!gp2x_sound) |
235 | { |
236 | nanosleep(&ts, NULL); |
237 | |
238 | |
239 | if(!gp2x_sound_pausei) |
240 | { |
241 | // [1] is sound buffer size at 22050, 16, stereo, it is 1468 (367*4) |
242 | // [0] number of bytes?, at 22050,16,stereo, it is 367 |
243 | // first half of buffer |
244 | |
245 | // first one is 368 |
246 | |
247 | |
248 | gp2x_sound_frame(blah, (void *)(&gp2x_sound_buffer[4+flip]), gp2x_sound_buffer[0]); |
249 | // write out to second half of buffer |
250 | write(gp2x_dev[3], (void *)(&gp2x_sound_buffer[4+flyp]), gp2x_sound_buffer[1]); |
251 | |
252 | flip+=gp2x_sound_buffer[1]; |
253 | if(flip==gp2x_sound_buffer[1]*8) flip=0; |
254 | flyp+=gp2x_sound_buffer[1]; |
255 | if(flyp==gp2x_sound_buffer[1]*8) flyp=0; |
256 | } |
257 | } |
258 | return NULL; |
259 | } |
260 | |
261 | static void gp2x_initqueue(gp2x_queue *q, unsigned long queue_items, unsigned long *position920t, unsigned long *position940t) |
262 | { |
263 | q->head = q->tail = q->items = 0; |
264 | q->max_items = queue_items; |
265 | if(position920t) q->place920t=position920t; else q->place920t=(unsigned long *)malloc(sizeof(unsigned long) * queue_items); |
266 | if(position940t) q->place940t=position940t; |
267 | memset(q->place920t, 0, sizeof(unsigned long) * queue_items); |
268 | } |
269 | |
270 | static void gp2x_enqueue(gp2x_queue *q, unsigned long data) |
271 | { |
272 | while(q->items==q->max_items); /*waiting for tail to decrease...*/ |
273 | q->place920t[q->head = (q->head < q->max_items ? q->head+1 : 0)] = data; |
274 | q->items++; |
275 | } |
276 | |
277 | /* UNUSED |
278 | static unsigned long gp2x_dequeue(gp2x_queue *q) |
279 | { |
280 | while(!q->items); //waiting for head to increase... |
281 | q->items--; |
282 | return q->place920t[q->tail = (q->tail < q->max_items ? q->tail+1 : 0)]; |
283 | } */ |
284 | |
285 | |
286 | |
287 | void gp2x_dualcore_pause(int yes) { if(yes) gp2x_memregs[0x0904>>1] &= 0xFFFE; else gp2x_memregs[0x0904>>1] |= 1; } |
288 | static void gp2x_940t_reset(int yes) { gp2x_memregs[0x3B48>>1] = ((yes&1) << 7) | (0x03); } |
289 | static void gp2x_940t_pause(int yes) { gp2x_dualcore_pause(yes); } |
290 | |
291 | static void gp2x_dualcore_registers(int save) |
292 | { |
293 | static unsigned short regs[8]; |
294 | |
295 | if(save) |
296 | { |
297 | regs[0]=gp2x_memregs[0x0904>>1]; regs[1]=gp2x_memregs[0x0912>>1]; |
298 | regs[2]=gp2x_memregs[0x091c>>1]; regs[3]=gp2x_memregs[0x3b40>>1]; |
299 | regs[4]=gp2x_memregs[0x3b42>>1]; regs[5]=gp2x_memregs[0x3b48>>1]; |
300 | regs[6]=gp2x_memregs[0x3b44>>1]; regs[7]=gp2x_memregs[0x3b46>>1]; |
301 | |
302 | gp2x_940t_reset(1); |
303 | gp2x_940t_pause(1); |
304 | } |
305 | else |
306 | { |
307 | gp2x_memregs[0x0904>>1]=regs[0]; gp2x_memregs[0x0912>>1]=regs[1]; |
308 | gp2x_memregs[0x091c>>1]=regs[2]; gp2x_memregs[0x3b40>>1]=regs[3]; |
309 | gp2x_memregs[0x3b42>>1]=regs[4]; gp2x_memregs[0x3b48>>1]=regs[5]; |
310 | gp2x_memregs[0x3b44>>1]=regs[6]; gp2x_memregs[0x3b46>>1]=regs[7]; |
311 | } |
312 | } |
313 | |
314 | void gp2x_dualcore_sync(void) |
315 | { |
316 | gp2x_queue *q=(gp2x_queue *)gp2x_1stcore_data_ptr(GP2X_QUEUE_ARRAY_PTR); |
317 | while(q->items); |
318 | } |
319 | |
320 | void gp2x_dualcore_exec(unsigned long command) { gp2x_enqueue((gp2x_queue *)gp2x_1stcore_data_ptr(GP2X_QUEUE_ARRAY_PTR),command); } |
321 | |
322 | void gp2x_dualcore_launch_program(unsigned long *area, unsigned long size) |
323 | { |
324 | unsigned long i=0, *arm940t_ram=(unsigned long *)gp2x_dualcore_ram; |
325 | |
326 | gp2x_940t_reset(1); |
327 | |
328 | gp2x_memregs[0x3B40>>1] = 0; //disable interrupts |
329 | gp2x_memregs[0x3B42>>1] = 0; |
330 | gp2x_memregs[0x3B44>>1] = 0xffff; |
331 | gp2x_memregs[0x3B46>>1] = 0xffff; |
332 | |
333 | gp2x_940t_pause(0); |
334 | |
335 | while(i < size) *arm940t_ram++=area[i++]; |
336 | |
337 | 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)); |
338 | |
339 | gp2x_940t_reset(0); |
340 | } |
341 | |
342 | void gp2x_dualcore_launch_program_from_disk(const char *file, unsigned long offset, unsigned long size) |
343 | { |
344 | FILE *in; void *data; |
345 | |
346 | if((in=fopen(file, "rb"))==NULL) return; |
347 | if((data=malloc(size))==NULL) { fclose(in); return; } |
348 | fseek(in, 0L, offset); |
349 | fread(data, 1, size, in); |
350 | gp2x_dualcore_launch_program((unsigned long *)data, size); |
351 | free(data); |
352 | fclose(in); |
353 | } |
354 | |
355 | void gp2x_init(int ticks_per_second, int bpp, int rate, int bits, int stereo, int Hz) |
356 | { |
357 | struct fb_fix_screeninfo fixed_info; |
358 | static int first=1; |
359 | |
360 | // GP2X_DO_SOUND |
361 | int gp2x_do_sound=1; |
362 | int frag=0; |
363 | int channels=1; |
364 | int stereoLocal=0; |
365 | |
366 | gp2x_ticks_per_second=7372800/ticks_per_second; |
367 | |
368 | if(!gp2x_dev[0]) gp2x_dev[0] = open("/dev/fb0", O_RDWR); |
369 | if(!gp2x_dev[1]) gp2x_dev[1] = open("/dev/fb1", O_RDWR); |
370 | if(!gp2x_dev[2]) gp2x_dev[2] = open("/dev/mem", O_RDWR); |
371 | |
372 | // don't run sound right now |
373 | if ( gp2x_do_sound) |
374 | { |
375 | if(!gp2x_dev[3]) gp2x_dev[3] = open("/dev/dsp", O_WRONLY); |
376 | if(!gp2x_dev[4]) gp2x_dev[4] = open("/dev/mixer", O_RDWR); |
377 | } |
378 | |
379 | gp2x_dualcore_ram=(unsigned long *)mmap(0, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0x03000000); |
380 | /*gp2x_dualcore_ram=(unsigned long *)mmap(0, 0x2000000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0x02000000);*/ |
381 | gp2x_memregl=(unsigned long *)mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0xc0000000); |
382 | gp2x_memregs=(unsigned short *)gp2x_memregl; |
383 | |
384 | if(first) { printf(MINILIB_VERSION "\n"); |
385 | gp2x_dualcore_registers(1); |
386 | gp2x_sound_volume(100,100); |
387 | gp2x_memregs[0x0F16>>1] = 0x830a; usleep(100000); |
388 | gp2x_memregs[0x0F58>>1] = 0x100c; usleep(100000); } |
389 | |
390 | ioctl(gp2x_dev[gp2x_physvram[7]=0], FBIOGET_FSCREENINFO, &fixed_info); |
391 | gp2x_screen15=gp2x_logvram15[2]=gp2x_logvram15[0]=(unsigned short *)mmap(0, 320*240*2, PROT_WRITE, MAP_SHARED, gp2x_dev[0], 0); |
392 | gp2x_screen8=(unsigned char *)gp2x_screen15; |
393 | gp2x_physvram[2]=gp2x_physvram[0]=fixed_info.smem_start; |
394 | |
395 | ioctl(gp2x_dev[1], FBIOGET_FSCREENINFO, &fixed_info); |
396 | gp2x_logvram15[3]=gp2x_logvram15[1]=(unsigned short *)mmap(0, 320*240*2, PROT_WRITE, MAP_SHARED, gp2x_dev[1], 0); |
397 | gp2x_physvram[3]=gp2x_physvram[1]=fixed_info.smem_start; |
398 | |
399 | gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/ |
400 | gp2x_memregs[0x290C>>1]=320*((bpp+1)/8); /*line width in bytes*/ |
401 | |
402 | memset(gp2x_screen15, 0, 320*240*2); gp2x_video_flip(); |
403 | memset(gp2x_screen15, 0, 320*240*2); gp2x_video_flip(); |
404 | |
405 | if(bpp==8) gp2x_physvram[2]+=320*240, gp2x_physvram[3]+=320*240, |
406 | gp2x_logvram15[2]+=320*240/2, gp2x_logvram15[3]+=320*240/2; |
407 | |
408 | |
409 | if ( gp2x_do_sound) |
410 | { |
411 | |
412 | ioctl(gp2x_dev[3], SNDCTL_DSP_SPEED, &rate); |
413 | ioctl(gp2x_dev[3], SNDCTL_DSP_SETFMT, &bits); |
414 | |
415 | ioctl(gp2x_dev[3], SNDCTL_DSP_CHANNELS, &channels); |
416 | ioctl(gp2x_dev[3], SNDCTL_DSP_STEREO, &stereoLocal); |
417 | |
418 | frag = 0x40000|13; |
419 | ioctl(gp2x_dev[3], SNDCTL_DSP_SETFRAGMENT, &frag); |
420 | |
421 | |
422 | printf("minimal() do sound, rate %d, bits %d, stereo %d, frag %d\n", rate, bits, stereo, frag); |
423 | |
424 | if(first) |
425 | { |
426 | first=0; |
427 | } |
428 | } |
429 | |
430 | } |
431 | |
432 | |
433 | extern int fcloseall(void); |
434 | void gp2x_deinit(void) |
435 | { |
436 | while((gp2x_sound++)<1000000); //wait arm920t threads to finish |
437 | |
438 | gp2x_dualcore_registers(0); |
439 | |
440 | gp2x_memregs[0x28DA>>1]=0x4AB; //set video mode |
441 | gp2x_memregs[0x290C>>1]=640; |
442 | |
443 | { int i; for(i=0;i<8;i++) if(gp2x_dev[i]) close(gp2x_dev[i]); } //close all devices |
444 | |
445 | fcloseall(); //close all files |
446 | |
447 | } |
448 | |
449 | |
450 | void SetVideoScaling(int pixels,int width,int height) |
451 | { |
452 | float x, y; |
453 | float xscale,yscale; |
454 | |
455 | int bpp=(gp2x_memregs[0x28DA>>1]>>9)&0x3; /* bytes per pixel */ |
456 | |
457 | if(gp2x_memregs[0x2800>>1]&0x100) /* TV-Out ON? */ |
458 | { |
459 | xscale=489.0; /* X-Scale with TV-Out (PAL or NTSC) */ |
460 | if (gp2x_memregs[0x2818>>1] == 287) /* PAL? */ |
461 | yscale=(pixels*274.0)/320.0; /* Y-Scale with PAL */ |
462 | else if (gp2x_memregs[0x2818>>1] == 239) /* NTSC? */ |
463 | yscale=(pixels*331.0)/320.0; /* Y-Scale with NTSC */ |
464 | } |
465 | else /* TV-Out OFF? */ |
466 | { |
467 | xscale=1024.0; /* X-Scale for LCD */ |
468 | yscale=pixels; /* Y-Scale for LCD */ |
469 | } |
470 | |
471 | x=(xscale*width)/320.0; |
472 | y=(yscale*bpp*height)/240.0; |
473 | gp2x_memregs[0x2906>>1]=(unsigned short)x; /* scale horizontal */ |
474 | gp2x_memregl[0x2908>>2]=(unsigned long)y; /* scale vertical */ |
475 | gp2x_memregs[0x290C>>1]=pixels*bpp; /* Set Video Width */ |
476 | } |