X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=fceu.git;a=blobdiff_plain;f=drivers%2Fgp2x%2Fminimal.c;fp=drivers%2Fgp2x%2Fminimal.c;h=193fa9e2f32625511b0b6cbbcde2430697c07bfa;hp=0000000000000000000000000000000000000000;hb=35868d35201adb0e4066584d1145c920f5be9b03;hpb=9e1b19d04e5020bfbfe2edfbc701543c8fa53e40 diff --git a/drivers/gp2x/minimal.c b/drivers/gp2x/minimal.c new file mode 100644 index 0000000..193fa9e --- /dev/null +++ b/drivers/gp2x/minimal.c @@ -0,0 +1,476 @@ +/* + GP2X minimal library v0.A by rlyeh, 2005. emulnation.info@rlyeh (swap it!) + + + GP2X 920t/940t CPUs library with a FIFO message system. + + GP2X video library with double buffering. + + GP2X sound library with double buffering. + + GP2X blitter library. + + GP2X timer library. + + GP2X joystick library. + + Thanks to Squidge, Robster, snaff, Reesy and NK, for the help & previous work! :-) + + + License + ======= + + Free for non-commercial projects (it would be nice receiving a mail from you). + Other cases, ask me first. + + GamePark Holdings is not allowed to use this library and/or use parts from it. + + + Known projects using the library or parts from it + ================================================= + + REminiscence-0.1.8 (rlyeh) + Atari800 GP2X pre-release 3 (foft) + XUMP (kedo) + MAME2X (Franxis) + DrMD for GP2X (Reesy) + + + What's new + ========== + + 0.A: 940t disabled all time unless required + sound is paused by default now, use gp2x_sound_pause() to unlock it + + fixed functions: + - gp2x_sound_play() ; increased the number of sampling buffers + - gp2x_timer_read() ; it should work again. broken at some point before + - gp2x_dualcore_launch_program_from_disk() ; it missed the code uploading call + + added functions: + - gp2x_sound_pause() + - gp2x_timer_delay() + - gp2x_dualcore_pause() + + 0.9: initial FIFO message system for dual cpu cores. + initial 48 Mb support. + initial quadruple buffering in 8bbp mode. + + added functions: + - gp2x_dualcore_exec() ; initial FIFO message system for dual cpu cores. + - gp2x_dualcore_sync() ; initial FIFO message system for dual cpu cores. + + improved functions: + - gp2x_video_flip() ; initial quadruple buffering in 8bbp mode. + + 0.8: initial dual cores cpu support. + very basic blit functions by popular demand ;-) + vsync & hsync waiting code (thanks Reesy) + + added functions: + - gp2x_launch_program() ; initial dual cores cpu support. + - gp2x_launch_program_from_disk() ; initial dual cores cpu support. + - gp2x_launch_subprogram() ; initial dual cores cpu support. + - gp2x_blitter_rect15() ; very basic blit function by popular demand ;-) + - gp2x_blitter_rect8() ; very basic blit function by popular demand ;-) + - gp2x_video_hsync() ; hsync waiting code (thanks Reesy) + - gp2x_video_vsync() ; vsync waiting code (thanks Reesy) + + fixed functions: + - gp2x_video_color8() ; bugfixed a stupid typo (thanks Franxis for the bug report) + + 0.7: added functions: + - gp2x_sound_volume() + + fixed functions: + - gp2x_deinit() ; fixed menu launch code when exiting. + + improved functions: + - gp2x_timer_read() ; rewritten timer. it should be more accurate now. + - gp2x_init() ; faster init code. + + 0.6: added functions: + - gp2x_timer_read() + - gp2x_sound_pause() + + fixed functions: + - gp2x_video_setpalette() ; palette handling was incorrect. fixed. + + 0.5: improved functions: + - gp2x_init() ; better and cleaner initalizing code. + - gp2x_init() ; sound patched for real stereo output (by using NK's solution) + + 0.4: lots of cleanups. + sound is threaded and double buffered now. + 8 bpp video support. + + fixed functions: + - gp2x_deinit() ; better and cleaner exiting code. + + 0.3: shorter library. + + fixed functions: + - gp2x_joystick_read() ; improved joystick diagonal detection. + + 0.2: better code layout. + public release. + + 0.1: beta release. +*/ + +#include "minimal.h" + + unsigned char *gp2x_screen8 ,*gp2x_upperRAM; + unsigned long gp2x_dev[8]={0,0,0,0,0,0,0,0}, gp2x_physvram[8], *gp2x_memregl, gp2x_volume, gp2x_ticks_per_second; +volatile unsigned long gp2x_sound_pausei=1, gp2x_ticks=0, gp2x_sound=0, *gp2x_dualcore_ram; + 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 +volatile unsigned short gp2x_palette[512]; + pthread_t gp2x_sound_thread=0; + +extern void gp2x_sound_frame(void *blah, void *buff, int samples); + + +void gp2x_video_flip(void) +{ + unsigned long address=gp2x_physvram[gp2x_physvram[7]]; + + if(++gp2x_physvram[7]==4) gp2x_physvram[7]=0; + gp2x_screen15=gp2x_logvram15[gp2x_physvram[7]]; + gp2x_screen8=(unsigned char *)gp2x_screen15; + + gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xFFFF); + gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16); + gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xFFFF); + gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16); +} + +void gp2x_video_flip_single(void) +{ + unsigned long address=gp2x_physvram[0]; + + gp2x_screen15=gp2x_logvram15[0]; + gp2x_screen8=(unsigned char *)gp2x_screen15; + + gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xFFFF); + gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16); + gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xFFFF); + gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16); +} + + +void gp2x_video_setgamma(unsigned short gamma) /*0..255*/ +{ + int i=256*3; + gp2x_memregs[0x295C>>1]=0; + while(i--) gp2x_memregs[0x295E>>1]=gamma; +} + +void gp2x_video_setpalette(void) +{ + unsigned short *g=(unsigned short *)gp2x_palette; int i=512; + gp2x_memregs[0x2958>>1]=0; + while(i--) gp2x_memregs[0x295A>>1]=*g++; +} + +void gp2x_blitter_rect15(gp2x_rect *r) +{ + int x, y; unsigned short *data=r->data15, *offset=&gp2x_screen15[r->x+r->y*320]; + + y=r->h; if(r->solid) + while(y--) { x=r->w; while(x--) *offset++=*data++; offset+=320-x; } + else + while(y--) { x=r->w; while(x--) { if(*data) *offset=*data; offset++, data++; } + offset+=320-x; } +} + +void gp2x_blitter_rect8(gp2x_rect *r) +{ + int x, y; unsigned char *data=r->data8, *offset=&gp2x_screen8[r->x+r->y*320]; + + y=r->h; if(r->solid) + while(y--) { x=r->w; while(x--) *offset++=*data++; offset+=320-x; } + else + while(y--) { x=r->w; while(x--) { if(*data) *offset=*data; offset++, data++; } + offset+=320-x; } +} + +unsigned long gp2x_joystick_read(void) +{ + unsigned long value=(gp2x_memregs[0x1198>>1] & 0x00FF); + + if(value==0xFD) value=0xFA; + if(value==0xF7) value=0xEB; + if(value==0xDF) value=0xAF; + if(value==0x7F) value=0xBE; + + return ~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16)); +} + +void gp2x_sound_volume(int l, int r) +{ + l=(((l*0x50)/100)<<8)|((r*0x50)/100); //0x5A, 0x60 + ioctl(gp2x_dev[4], SOUND_MIXER_WRITE_PCM, &l); //SOUND_MIXER_WRITE_VOLUME +} + +void gp2x_timer_delay(unsigned long ticks) +{ + unsigned long target=gp2x_memregl[0x0A00>>2]+ticks*gp2x_ticks_per_second; + while(gp2x_memregl[0x0A00>>2]>2]/gp2x_ticks_per_second; +} + +unsigned long gp2x_timer_read_ms(void) +{ + return gp2x_memregl[0x0A00>>2]; +} + + +void gp2x_sound_pause(int yes) { gp2x_sound_pausei=yes; } + + +static void *gp2x_sound_play(void *blah) +{ + int flip=0, flyp=gp2x_sound_buffer[1]; + struct timespec ts; ts.tv_sec=0, ts.tv_nsec=gp2x_sound_buffer[2]; + + while(!gp2x_sound) + { + nanosleep(&ts, NULL); + + + if(!gp2x_sound_pausei) + { + // [1] is sound buffer size at 22050, 16, stereo, it is 1468 (367*4) + // [0] number of bytes?, at 22050,16,stereo, it is 367 + // first half of buffer + + // first one is 368 + + + gp2x_sound_frame(blah, (void *)(&gp2x_sound_buffer[4+flip]), gp2x_sound_buffer[0]); + // write out to second half of buffer + write(gp2x_dev[3], (void *)(&gp2x_sound_buffer[4+flyp]), gp2x_sound_buffer[1]); + + flip+=gp2x_sound_buffer[1]; + if(flip==gp2x_sound_buffer[1]*8) flip=0; + flyp+=gp2x_sound_buffer[1]; + if(flyp==gp2x_sound_buffer[1]*8) flyp=0; + } + } + return NULL; +} + +static void gp2x_initqueue(gp2x_queue *q, unsigned long queue_items, unsigned long *position920t, unsigned long *position940t) +{ + q->head = q->tail = q->items = 0; + q->max_items = queue_items; + if(position920t) q->place920t=position920t; else q->place920t=(unsigned long *)malloc(sizeof(unsigned long) * queue_items); + if(position940t) q->place940t=position940t; + memset(q->place920t, 0, sizeof(unsigned long) * queue_items); +} + +static void gp2x_enqueue(gp2x_queue *q, unsigned long data) +{ + while(q->items==q->max_items); /*waiting for tail to decrease...*/ + q->place920t[q->head = (q->head < q->max_items ? q->head+1 : 0)] = data; + q->items++; +} + +/* UNUSED + static unsigned long gp2x_dequeue(gp2x_queue *q) +{ + while(!q->items); //waiting for head to increase... + q->items--; + return q->place920t[q->tail = (q->tail < q->max_items ? q->tail+1 : 0)]; +} */ + + + + void gp2x_dualcore_pause(int yes) { if(yes) gp2x_memregs[0x0904>>1] &= 0xFFFE; else gp2x_memregs[0x0904>>1] |= 1; } +static void gp2x_940t_reset(int yes) { gp2x_memregs[0x3B48>>1] = ((yes&1) << 7) | (0x03); } +static void gp2x_940t_pause(int yes) { gp2x_dualcore_pause(yes); } + +static void gp2x_dualcore_registers(int save) +{ + static unsigned short regs[8]; + + if(save) + { + regs[0]=gp2x_memregs[0x0904>>1]; regs[1]=gp2x_memregs[0x0912>>1]; + regs[2]=gp2x_memregs[0x091c>>1]; regs[3]=gp2x_memregs[0x3b40>>1]; + regs[4]=gp2x_memregs[0x3b42>>1]; regs[5]=gp2x_memregs[0x3b48>>1]; + regs[6]=gp2x_memregs[0x3b44>>1]; regs[7]=gp2x_memregs[0x3b46>>1]; + + gp2x_940t_reset(1); + gp2x_940t_pause(1); + } + else + { + gp2x_memregs[0x0904>>1]=regs[0]; gp2x_memregs[0x0912>>1]=regs[1]; + gp2x_memregs[0x091c>>1]=regs[2]; gp2x_memregs[0x3b40>>1]=regs[3]; + gp2x_memregs[0x3b42>>1]=regs[4]; gp2x_memregs[0x3b48>>1]=regs[5]; + gp2x_memregs[0x3b44>>1]=regs[6]; gp2x_memregs[0x3b46>>1]=regs[7]; + } +} + +void gp2x_dualcore_sync(void) +{ + gp2x_queue *q=(gp2x_queue *)gp2x_1stcore_data_ptr(GP2X_QUEUE_ARRAY_PTR); + while(q->items); +} + +void gp2x_dualcore_exec(unsigned long command) { gp2x_enqueue((gp2x_queue *)gp2x_1stcore_data_ptr(GP2X_QUEUE_ARRAY_PTR),command); } + +void gp2x_dualcore_launch_program(unsigned long *area, unsigned long size) +{ + unsigned long i=0, *arm940t_ram=(unsigned long *)gp2x_dualcore_ram; + + gp2x_940t_reset(1); + + gp2x_memregs[0x3B40>>1] = 0; //disable interrupts + gp2x_memregs[0x3B42>>1] = 0; + gp2x_memregs[0x3B44>>1] = 0xffff; + gp2x_memregs[0x3B46>>1] = 0xffff; + + gp2x_940t_pause(0); + + while(i < size) *arm940t_ram++=area[i++]; + + 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)); + + gp2x_940t_reset(0); +} + +void gp2x_dualcore_launch_program_from_disk(const char *file, unsigned long offset, unsigned long size) +{ + FILE *in; void *data; + + if((in=fopen(file, "rb"))==NULL) return; + if((data=malloc(size))==NULL) { fclose(in); return; } + fseek(in, 0L, offset); + fread(data, 1, size, in); + gp2x_dualcore_launch_program((unsigned long *)data, size); + free(data); + fclose(in); +} + +void gp2x_init(int ticks_per_second, int bpp, int rate, int bits, int stereo, int Hz) +{ + struct fb_fix_screeninfo fixed_info; + static int first=1; + + // GP2X_DO_SOUND + int gp2x_do_sound=1; + int frag=0; + int channels=1; + int stereoLocal=0; + + gp2x_ticks_per_second=7372800/ticks_per_second; + + if(!gp2x_dev[0]) gp2x_dev[0] = open("/dev/fb0", O_RDWR); + if(!gp2x_dev[1]) gp2x_dev[1] = open("/dev/fb1", O_RDWR); + if(!gp2x_dev[2]) gp2x_dev[2] = open("/dev/mem", O_RDWR); + + // don't run sound right now + if ( gp2x_do_sound) + { + if(!gp2x_dev[3]) gp2x_dev[3] = open("/dev/dsp", O_WRONLY); + if(!gp2x_dev[4]) gp2x_dev[4] = open("/dev/mixer", O_RDWR); + } + + gp2x_dualcore_ram=(unsigned long *)mmap(0, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0x03000000); +/*gp2x_dualcore_ram=(unsigned long *)mmap(0, 0x2000000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0x02000000);*/ + gp2x_memregl=(unsigned long *)mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0xc0000000); + gp2x_memregs=(unsigned short *)gp2x_memregl; + + if(first) { printf(MINILIB_VERSION "\n"); + gp2x_dualcore_registers(1); + gp2x_sound_volume(100,100); + gp2x_memregs[0x0F16>>1] = 0x830a; usleep(100000); + gp2x_memregs[0x0F58>>1] = 0x100c; usleep(100000); } + + ioctl(gp2x_dev[gp2x_physvram[7]=0], FBIOGET_FSCREENINFO, &fixed_info); + gp2x_screen15=gp2x_logvram15[2]=gp2x_logvram15[0]=(unsigned short *)mmap(0, 320*240*2, PROT_WRITE, MAP_SHARED, gp2x_dev[0], 0); + gp2x_screen8=(unsigned char *)gp2x_screen15; + gp2x_physvram[2]=gp2x_physvram[0]=fixed_info.smem_start; + + ioctl(gp2x_dev[1], FBIOGET_FSCREENINFO, &fixed_info); + gp2x_logvram15[3]=gp2x_logvram15[1]=(unsigned short *)mmap(0, 320*240*2, PROT_WRITE, MAP_SHARED, gp2x_dev[1], 0); + gp2x_physvram[3]=gp2x_physvram[1]=fixed_info.smem_start; + + gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/ + gp2x_memregs[0x290C>>1]=320*((bpp+1)/8); /*line width in bytes*/ + + memset(gp2x_screen15, 0, 320*240*2); gp2x_video_flip(); + memset(gp2x_screen15, 0, 320*240*2); gp2x_video_flip(); + + if(bpp==8) gp2x_physvram[2]+=320*240, gp2x_physvram[3]+=320*240, + gp2x_logvram15[2]+=320*240/2, gp2x_logvram15[3]+=320*240/2; + + + if ( gp2x_do_sound) + { + + ioctl(gp2x_dev[3], SNDCTL_DSP_SPEED, &rate); + ioctl(gp2x_dev[3], SNDCTL_DSP_SETFMT, &bits); + + ioctl(gp2x_dev[3], SNDCTL_DSP_CHANNELS, &channels); + ioctl(gp2x_dev[3], SNDCTL_DSP_STEREO, &stereoLocal); + + frag = 0x40000|13; + ioctl(gp2x_dev[3], SNDCTL_DSP_SETFRAGMENT, &frag); + + + printf("minimal() do sound, rate %d, bits %d, stereo %d, frag %d\n", rate, bits, stereo, frag); + + if(first) + { + first=0; + } + } + +} + + +extern int fcloseall(void); +void gp2x_deinit(void) +{ + while((gp2x_sound++)<1000000); //wait arm920t threads to finish + + gp2x_dualcore_registers(0); + + gp2x_memregs[0x28DA>>1]=0x4AB; //set video mode + gp2x_memregs[0x290C>>1]=640; + + { int i; for(i=0;i<8;i++) if(gp2x_dev[i]) close(gp2x_dev[i]); } //close all devices + + fcloseall(); //close all files + +} + + +void SetVideoScaling(int pixels,int width,int height) +{ + float x, y; + float xscale,yscale; + + int bpp=(gp2x_memregs[0x28DA>>1]>>9)&0x3; /* bytes per pixel */ + + if(gp2x_memregs[0x2800>>1]&0x100) /* TV-Out ON? */ + { + xscale=489.0; /* X-Scale with TV-Out (PAL or NTSC) */ + if (gp2x_memregs[0x2818>>1] == 287) /* PAL? */ + yscale=(pixels*274.0)/320.0; /* Y-Scale with PAL */ + else if (gp2x_memregs[0x2818>>1] == 239) /* NTSC? */ + yscale=(pixels*331.0)/320.0; /* Y-Scale with NTSC */ + } + else /* TV-Out OFF? */ + { + xscale=1024.0; /* X-Scale for LCD */ + yscale=pixels; /* Y-Scale for LCD */ + } + + x=(xscale*width)/320.0; + y=(yscale*bpp*height)/240.0; + gp2x_memregs[0x2906>>1]=(unsigned short)x; /* scale horizontal */ + gp2x_memregl[0x2908>>2]=(unsigned long)y; /* scale vertical */ + gp2x_memregs[0x290C>>1]=pixels*bpp; /* Set Video Width */ +}