From 720ee7f6244cb51cc123a7fd16832161db9a3b4d Mon Sep 17 00:00:00 2001 From: notaz Date: Tue, 19 Dec 2006 20:53:21 +0000 Subject: [PATCH 1/1] initial import git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@2 be3aeb3a-fb24-0410-a615-afba39da0efa --- gp2x/940.c | 93 ++++ gp2x/940ctl_ym2612.c | 451 +++++++++++++++++ gp2x/940ctl_ym2612.h | 9 + gp2x/940init.s | 174 +++++++ gp2x/940shared.h | 33 ++ gp2x/Makefile | 191 +++++++ gp2x/asmutils.h | 12 + gp2x/asmutils.s | 210 ++++++++ gp2x/config.txt | 138 +++++ gp2x/cpuctrl.c | 156 ++++++ gp2x/cpuctrl.gpl.txt | 340 +++++++++++++ gp2x/cpuctrl.h | 16 + gp2x/emu.c | 1121 +++++++++++++++++++++++++++++++++++++++++ gp2x/emu.h | 46 ++ gp2x/gp2x.c | 311 ++++++++++++ gp2x/gp2x.h | 40 ++ gp2x/main.c | 143 ++++++ gp2x/menu.c | 992 ++++++++++++++++++++++++++++++++++++ gp2x/menu.h | 16 + gp2x/mmuhack.c | 104 ++++ gp2x/mmuhack.txt | 4 + gp2x/port_config.h | 18 + gp2x/port_config.s | 8 + gp2x/squidgehack.c | 45 ++ gp2x/squidgehack.h | 7 + gp2x/test.c | 37 ++ gp2x/usbjoy.c | 424 ++++++++++++++++ gp2x/usbjoy.h | 241 +++++++++ gp2x/version.h | 2 + linux/940ctl_ym2612.c | 112 ++++ linux/Makefile | 91 ++++ linux/README | 4 + linux/blit.c | 81 +++ linux/fakedasm.c | 667 ++++++++++++++++++++++++ linux/gp2x.c | 384 ++++++++++++++ linux/port_config.h | 19 + 36 files changed, 6740 insertions(+) create mode 100644 gp2x/940.c create mode 100644 gp2x/940ctl_ym2612.c create mode 100644 gp2x/940ctl_ym2612.h create mode 100644 gp2x/940init.s create mode 100644 gp2x/940shared.h create mode 100644 gp2x/Makefile create mode 100644 gp2x/asmutils.h create mode 100644 gp2x/asmutils.s create mode 100644 gp2x/config.txt create mode 100644 gp2x/cpuctrl.c create mode 100644 gp2x/cpuctrl.gpl.txt create mode 100644 gp2x/cpuctrl.h create mode 100644 gp2x/emu.c create mode 100644 gp2x/emu.h create mode 100644 gp2x/gp2x.c create mode 100644 gp2x/gp2x.h create mode 100644 gp2x/main.c create mode 100644 gp2x/menu.c create mode 100644 gp2x/menu.h create mode 100644 gp2x/mmuhack.c create mode 100644 gp2x/mmuhack.txt create mode 100644 gp2x/port_config.h create mode 100644 gp2x/port_config.s create mode 100644 gp2x/squidgehack.c create mode 100644 gp2x/squidgehack.h create mode 100644 gp2x/test.c create mode 100644 gp2x/usbjoy.c create mode 100644 gp2x/usbjoy.h create mode 100644 gp2x/version.h create mode 100644 linux/940ctl_ym2612.c create mode 100644 linux/Makefile create mode 100644 linux/README create mode 100644 linux/blit.c create mode 100644 linux/fakedasm.c create mode 100644 linux/gp2x.c create mode 100644 linux/port_config.h diff --git a/gp2x/940.c b/gp2x/940.c new file mode 100644 index 0000000..c3f0b11 --- /dev/null +++ b/gp2x/940.c @@ -0,0 +1,93 @@ +#include "940shared.h" + +/* this code assumes that we live @ 0x3000000 bank */ +//static volatile unsigned short *gp2x_memregs = (void *) 0x0xbd000000; +//static volatile unsigned long *gp2x_memregl = (void *) 0x0xbd000000; + +static _940_data_t *shared_data = (_940_data_t *) 0x100000; +static _940_ctl_t *shared_ctl = (_940_ctl_t *) 0x200000; +YM2612 *ym2612_940; +int *mix_buffer; + +// from init.s +void wait_irq(void); +void spend_cycles(int c); +void cache_clean(void); +void cache_clean_flush(void); + +// asm volatile ("mov r0, #0" ::: "r0"); +// asm volatile ("mcr p15, 0, r0, c7, c6, 0" ::: "r0"); /* flush dcache */ +// asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer */ + +void Main940(int startvector) +{ + ym2612_940 = &shared_data->ym2612; + mix_buffer = shared_data->mix_buffer; + + // debug + shared_ctl->vstarts[startvector]++; + asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); + + /* unmask IRQs */ + + for (;; shared_ctl->loopc++) + { +/* + while (!shared_ctl->busy) + { + //shared_ctl->waitc++; + spend_cycles(256); + } +*/ + if (!shared_ctl->busy) + { + wait_irq(); + } + + switch (shared_ctl->job) + { + case JOB940_YM2612INIT: + shared_ctl->writebuff0[0] = shared_ctl->writebuff1[0] = 0xffff; + YM2612Init_(shared_ctl->baseclock, shared_ctl->rate); + break; + + case JOB940_YM2612RESETCHIP: + YM2612ResetChip_(); + break; + + case JOB940_PICOSTATELOAD: + YM2612PicoStateLoad_(); + break; + + case JOB940_YM2612UPDATEONE: { + int i, dw, *wbuff; + if (shared_ctl->writebuffsel == 1) { + wbuff = (int *) shared_ctl->writebuff1; + } else { + wbuff = (int *) shared_ctl->writebuff0; + } + + /* playback all writes */ + for (i = 2048/2; i > 0; i--) { + UINT16 d; + dw = *wbuff++; + d = dw; + if (d == 0xffff) break; + YM2612Write_(d >> 8, d); + d = (dw>>16); + if (d == 0xffff) break; + YM2612Write_(d >> 8, d); + } + + YM2612UpdateOne_(0, shared_ctl->length, shared_ctl->stereo); +// cache_clean_flush(); + cache_clean(); +// asm volatile ("mov r0, #0" ::: "r0"); +// asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer, should be done on nonbuffered write */ + break; + } + } + + shared_ctl->busy = 0; + } +} diff --git a/gp2x/940ctl_ym2612.c b/gp2x/940ctl_ym2612.c new file mode 100644 index 0000000..d8a10b2 --- /dev/null +++ b/gp2x/940ctl_ym2612.c @@ -0,0 +1,451 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "940shared.h" +#include "gp2x.h" +#include "emu.h" +#include "menu.h" +#include "asmutils.h" + +/* we will need some gp2x internals here */ +extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */ +extern volatile unsigned long *gp2x_memregl; + +static unsigned char *shared_mem = 0; +static _940_data_t *shared_data = 0; +static _940_ctl_t *shared_ctl = 0; + +int crashed_940 = 0; + + +/***********************************************************/ + +#define MAXOUT (+32767) +#define MINOUT (-32768) + +/* limitter */ +#define Limit(val, max,min) { \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +/* these will be managed locally on our side */ +extern int *ym2612_dacen; +extern INT32 *ym2612_dacout; +extern void *ym2612_regs; + +static UINT8 *REGS = 0; /* we will also keep local copy of regs for savestates and such */ +static INT32 addr_A1; /* address line A1 */ +static int dacen; +static INT32 dacout; +static UINT8 ST_address; /* address register */ +static UINT8 ST_status; /* status flag */ +static UINT8 ST_mode; /* mode CSM / 3SLOT */ +static int ST_TA; /* timer a */ +static int ST_TAC; /* timer a maxval */ +static int ST_TAT; /* timer a ticker */ +static UINT8 ST_TB; /* timer b */ +static int ST_TBC; /* timer b maxval */ +static int ST_TBT; /* timer b ticker */ + +static int writebuff_ptr = 0; + + +/* OPN Mode Register Write */ +static void set_timers( int v ) +{ + /* b7 = CSM MODE */ + /* b6 = 3 slot mode */ + /* b5 = reset b */ + /* b4 = reset a */ + /* b3 = timer enable b */ + /* b2 = timer enable a */ + /* b1 = load b */ + /* b0 = load a */ + ST_mode = v; + + /* reset Timer b flag */ + if( v & 0x20 ) + ST_status &= ~2; + + /* reset Timer a flag */ + if( v & 0x10 ) + ST_status &= ~1; +} + +/* YM2612 write */ +/* a = address */ +/* v = value */ +/* returns 1 if sample affecting state changed */ +int YM2612Write_940(unsigned int a, unsigned int v) +{ + int addr; //, ret=1; + + v &= 0xff; /* adjust to 8 bit bus */ + a &= 3; + + switch( a ) { + case 0: /* address port 0 */ + ST_address = v; + addr_A1 = 0; + //ret=0; + break; + + case 1: /* data port 0 */ + if (addr_A1 != 0) { + return 0; /* verified on real YM2608 */ + } + + addr = ST_address; + REGS[addr] = v; + + switch( addr & 0xf0 ) + { + case 0x20: /* 0x20-0x2f Mode */ + switch( addr ) + { + case 0x24: { // timer A High 8 + int TAnew = (ST_TA & 0x03)|(((int)v)<<2); + if(ST_TA != TAnew) { + // we should reset ticker only if new value is written. Outrun requires this. + ST_TA = TAnew; + ST_TAC = (1024-TAnew)*18; + ST_TAT = 0; + } + return 0; + } + case 0x25: { // timer A Low 2 + int TAnew = (ST_TA & 0x3fc)|(v&3); + if(ST_TA != TAnew) { + ST_TA = TAnew; + ST_TAC = (1024-TAnew)*18; + ST_TAT = 0; + } + return 0; + } + case 0x26: // timer B + if(ST_TB != v) { + ST_TB = v; + ST_TBC = (256-v)<<4; + ST_TBC *= 18; + ST_TBT = 0; + } + return 0; + case 0x27: /* mode, timer control */ + set_timers( v ); + break; // other side needs ST.mode for 3slot mode + case 0x2a: /* DAC data (YM2612) */ + dacout = ((int)v - 0x80) << 6; /* level unknown (notaz: 8 seems to be too much) */ + return 0; + case 0x2b: /* DAC Sel (YM2612) */ + /* b7 = dac enable */ + dacen = v & 0x80; + break; // other side has to know this + default: + break; + } + break; + } + break; + + case 2: /* address port 1 */ + ST_address = v; + addr_A1 = 1; + //ret=0; + break; + + case 3: /* data port 1 */ + if (addr_A1 != 1) { + return 0; /* verified on real YM2608 */ + } + + addr = ST_address | 0x100; + REGS[addr] = v; + break; + } + + if(currentConfig.EmuOpt & 4) { + /* queue this write for 940 */ + if (writebuff_ptr < 2047) { + if (shared_ctl->writebuffsel == 1) { + shared_ctl->writebuff0[writebuff_ptr++] = (a<<8)|v; + } else { + shared_ctl->writebuff1[writebuff_ptr++] = (a<<8)|v; + } + } else { + printf("warning: writebuff_ptr > 2047\n"); + } + } + + return 0; // cause the engine to do updates once per frame only +} + +UINT8 YM2612Read_940(void) +{ + return ST_status; +} + + +int YM2612PicoTick_940(int n) +{ + //int ret = 0; + + // timer A + if(ST_mode & 0x01 && (ST_TAT+=64*n) >= ST_TAC) { + ST_TAT -= ST_TAC; + if(ST_mode & 0x04) ST_status |= 1; + // CSM mode total level latch and auto key on +/* FIXME + if(ST_mode & 0x80) { + CSMKeyControll( &(ym2612_940->CH[2]) ); // Vectorman2, etc. + ret = 1; + } +*/ + } + + // timer B + if(ST_mode & 0x02 && (ST_TBT+=64*n) >= ST_TBC) { + ST_TBT -= ST_TBC; + if(ST_mode & 0x08) ST_status |= 2; + } + + return 0; +} + + +static void wait_busy_940(void) +{ + int i; +#if 0 + printf("940 busy, entering wait loop.. (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc); + for (i = 0; i < 8; i++) + printf("%i ", shared_ctl->vstarts[i]); + printf(")\n"); + + for (i = 0; shared_ctl->busy; i++) + { + spend_cycles(1024); /* needs tuning */ + } + printf("wait iterations: %i\n", i); +#else + for (i = 0; shared_ctl->busy && i < 0x10000; i++) + spend_cycles(4*1024); + if (i < 0x10000) return; + + /* 940 crashed */ + printf("940 crashed (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc); + for (i = 0; i < 8; i++) + printf("%i ", shared_ctl->vstarts[i]); + printf(")\n"); + strcpy(menuErrorMsg, "940 crashed."); + engineState = PGS_Menu; + crashed_940 = 1; +#endif +} + + +static void add_job_940(int job) +{ + shared_ctl->job = job; + shared_ctl->busy = 1; + gp2x_memregs[0x3B3E>>1] = 0xffff; // cause an IRQ for 940 +} + + +void YM2612PicoStateLoad_940(void) +{ + int i, old_A1 = addr_A1; + + if (shared_ctl->busy) wait_busy_940(); + + // feed all the registers and update internal state + for(i = 0; i < 0x100; i++) { + YM2612Write_940(0, i); + YM2612Write_940(1, REGS[i]); + } + for(i = 0; i < 0x100; i++) { + YM2612Write_940(2, i); + YM2612Write_940(3, REGS[i|0x100]); + } + + addr_A1 = old_A1; + + add_job_940(JOB940_PICOSTATELOAD); +} + + +static void internal_reset(void) +{ + writebuff_ptr = 0; + ST_mode = 0; + ST_status = 0; /* normal mode */ + ST_TA = 0; + ST_TAC = 0; + ST_TB = 0; + ST_TBC = 0; + dacen = 0; +} + + +extern char **g_argv; + +/* none of the functions in this file should be called before this one */ +void YM2612Init_940(int baseclock, int rate) +{ + printf("YM2612Init_940()\n"); + //printf("sizeof(*shared_data): %i (%x)\n", sizeof(*shared_data), sizeof(*shared_data)); + //printf("sizeof(*shared_ctl): %i (%x)\n", sizeof(*shared_ctl), sizeof(*shared_ctl)); + + Reset940(1); + Pause940(1); + + gp2x_memregs[0x3B46>>1] = 0xffff; // clear pending DUALCPU interrupts for 940 + gp2x_memregs[0x3B42>>1] = 0xffff; // enable DUALCPU interrupts for 940 + + gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller + + if (shared_mem == NULL) + { + shared_mem = (unsigned char *) mmap(0, 0x210000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x3000000); + if(shared_mem == MAP_FAILED) + { + printf("mmap(shared_data) failed with %i\n", errno); + exit(1); + } + shared_data = (_940_data_t *) (shared_mem+0x100000); + /* this area must not get buffered on either side */ + shared_ctl = (_940_ctl_t *) (shared_mem+0x200000); + crashed_940 = 1; + } + + if (crashed_940) + { + unsigned char ucData[1024]; + int nRead, i, nLen = 0; + char binpath[1024]; + FILE *fp; + + strncpy(binpath, g_argv[0], 1023); + binpath[1023] = 0; + for (i = strlen(binpath); i > 0; i--) + if (binpath[i] == '/') { binpath[i] = 0; break; } + strcat(binpath, "/code940.bin"); + + fp = fopen(binpath, "rb"); + if(!fp) + { + memset(gp2x_screen, 0, 320*240); + gp2x_text_out8(10, 100, "failed to open required file:"); + gp2x_text_out8(10, 110, "code940.bin"); + gp2x_video_flip(); + printf("failed to open %s\n", binpath); + exit(1); + } + + while(1) + { + nRead = fread(ucData, 1, 1024, fp); + if(nRead <= 0) + break; + memcpy(shared_mem + nLen, ucData, nRead); + nLen += nRead; + } + fclose(fp); + crashed_940 = 0; + } + + memset(shared_data, 0, sizeof(*shared_data)); + memset(shared_ctl, 0, sizeof(*shared_ctl)); + + REGS = YM2612GetRegs(); + + ym2612_dacen = &dacen; + ym2612_dacout = &dacout; + + internal_reset(); + + /* now cause 940 to init it's ym2612 stuff */ + shared_ctl->baseclock = baseclock; + shared_ctl->rate = rate; + shared_ctl->job = JOB940_YM2612INIT; + shared_ctl->busy = 1; + + /* start the 940 */ + Reset940(0); + Pause940(0); + + // YM2612ResetChip_940(); // will be done on JOB940_YM2612INIT +} + + +void YM2612ResetChip_940(void) +{ + printf("YM2612ResetChip_940()\n"); + if (shared_data == NULL) { + printf("YM2612ResetChip_940: reset before init?\n"); + return; + } + + if (shared_ctl->busy) wait_busy_940(); + + internal_reset(); + + add_job_940(JOB940_YM2612RESETCHIP); +} + + +void YM2612UpdateOne_940(short *buffer, int length, int stereo) +{ + int i, *mix_buffer = shared_data->mix_buffer; + + //printf("YM2612UpdateOne_940()\n"); + if (shared_ctl->busy) wait_busy_940(); + + //printf("940 (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc); + //for (i = 0; i < 8; i++) + // printf("%i ", shared_ctl->vstarts[i]); + //printf(")\n"); + + /* mix data from previous go */ + if (stereo) { + int *mb = mix_buffer; + for (i = length; i > 0; i--) { + int l, r; + l = r = *buffer; + l += *mb++, r += *mb++; + Limit( l, MAXOUT, MINOUT ); + Limit( r, MAXOUT, MINOUT ); + *buffer++ = l; *buffer++ = r; + } + } else { + for (i = 0; i < length; i++) { + int l = mix_buffer[i]; + l += buffer[i]; + Limit( l, MAXOUT, MINOUT ); + buffer[i] = l; + } + } + + //printf("new writes: %i\n", writebuff_ptr); + if (shared_ctl->writebuffsel == 1) { + shared_ctl->writebuff0[writebuff_ptr] = 0xffff; + } else { + shared_ctl->writebuff1[writebuff_ptr] = 0xffff; + } + writebuff_ptr = 0; + + /* give 940 another job */ + shared_ctl->writebuffsel ^= 1; + shared_ctl->length = length; + shared_ctl->stereo = stereo; + add_job_940(JOB940_YM2612UPDATEONE); + //spend_cycles(512); + //printf("SRCPND: %08lx, INTMODE: %08lx, INTMASK: %08lx, INTPEND: %08lx\n", + // gp2x_memregl[0x4500>>2], gp2x_memregl[0x4504>>2], gp2x_memregl[0x4508>>2], gp2x_memregl[0x4510>>2]); +} diff --git a/gp2x/940ctl_ym2612.h b/gp2x/940ctl_ym2612.h new file mode 100644 index 0000000..7d94c13 --- /dev/null +++ b/gp2x/940ctl_ym2612.h @@ -0,0 +1,9 @@ +void YM2612Init_940(int baseclock, int rate); +void YM2612ResetChip_940(void); +void YM2612UpdateOne_940(short *buffer, int length, int stereo); + +int YM2612Write_940(unsigned int a, unsigned int v); +unsigned char YM2612Read_940(void); + +int YM2612PicoTick_940(int n); +void YM2612PicoStateLoad_940(void); diff --git a/gp2x/940init.s b/gp2x/940init.s new file mode 100644 index 0000000..e28b453 --- /dev/null +++ b/gp2x/940init.s @@ -0,0 +1,174 @@ +.global code940 + +code940: @ interrupt table: + b .b_reset @ reset + b .b_undef @ undefined instructions + b .b_swi @ software interrupt + b .b_pabort @ prefetch abort + b .b_dabort @ data abort + b .b_reserved @ reserved + b .b_irq @ IRQ + b .b_fiq @ FIQ + +@ test +.b_reset: + mov r12, #0 + b .Begin +.b_undef: + mov r12, #1 + b .Begin +.b_swi: + mov r12, #2 + b .Begin +.b_pabort: + mov r12, #3 + b .Begin +.b_dabort: + mov r12, #4 + b .Begin +.b_reserved: + mov r12, #5 + b .Begin +.b_irq: + mov r12, #6 + mov sp, #0x100000 @ reset stack + sub sp, sp, #4 + mov r1, #0xbd000000 @ assume we live @ 0x3000000 bank + orr r2, r1, #0x3B00 + orr r2, r2, #0x0046 + mvn r3, #0 + strh r3, [r2] @ clear any pending interrupts from the DUALCPU unit + orr r2, r1, #0x4500 + str r3, [r2] @ clear all pending interrupts in irq controller's SRCPND register + orr r2, r2, #0x0010 + str r3, [r2] @ clear all pending interrupts in irq controller's INTPND register + b .Enter +.b_fiq: + mov r12, #7 + b .Begin + +.Begin: + mov sp, #0x100000 @ set the stack top (1M) + sub sp, sp, #4 @ minus 4 + + @ set up memory region 0 -- the whole 4GB address space + mov r0, #(0x1f<<1)|1 @ region data + mcr p15, 0, r0, c6, c0, 0 @ opcode2 ~ data/instr + mcr p15, 0, r0, c6, c0, 1 + + @ set up region 1 which is the first 2 megabytes. + mov r0, #(0x14<<1)|1 @ region data + mcr p15, 0, r0, c6, c1, 0 + mcr p15, 0, r0, c6, c1, 1 + + @ set up region 2: 64k 0x200000-0x210000 + mov r0, #(0x0f<<1)|1 + orr r0, r0, #0x200000 + mcr p15, 0, r0, c6, c2, 0 + mcr p15, 0, r0, c6, c2, 1 + + @ set up region 3: 64k 0xbd000000-0xbd010000 (hw control registers) + mov r0, #(0x0f<<1)|1 + orr r0, r0, #0xbd000000 + mcr p15, 0, r0, c6, c3, 0 + mcr p15, 0, r0, c6, c3, 1 + + @ set region 1 to be cacheable (so the first 2M will be cacheable) + mov r0, #2 + mcr p15, 0, r0, c2, c0, 0 + mcr p15, 0, r0, c2, c0, 1 + + @ set region 1 to be bufferable too (only data) + mcr p15, 0, r0, c3, c0, 0 + + @ set protection, allow accsess only to regions 1 and 2 + mov r0, #(3<<6)|(3<<4)|(3<<2)|(0) @ data: [full, full, full, no access] for regions [3 2 1 0] + mcr p15, 0, r0, c5, c0, 0 + mov r0, #(0<<6)|(0<<4)|(3<<2)|(0) @ instructions: [no access, no, full, no] + mcr p15, 0, r0, c5, c0, 1 + + mrc p15, 0, r0, c1, c0, 0 @ fetch current control reg + orr r0, r0, #1 @ 0x00000001: enable protection unit + orr r0, r0, #4 @ 0x00000004: enable D cache + orr r0, r0, #0x1000 @ 0x00001000: enable I cache + orr r0, r0, #0xC0000000 @ 0xC0000000: async+fastbus + mcr p15, 0, r0, c1, c0, 0 @ set control reg + + @ flush (invalidate) the cache (just in case) + mov r0, #0 + mcr p15, 0, r0, c7, c6, 0 + +.Enter: + mov r0, r12 + bl Main940 + + @ we should never get here +.b_deadloop: + b .b_deadloop + + + +@ so asm utils are also defined here: +.global spend_cycles @ c + +spend_cycles: + mov r0, r0, lsr #2 @ 4 cycles/iteration + sub r0, r0, #2 @ entry/exit/init +.sc_loop: + subs r0, r0, #1 + bpl .sc_loop + + bx lr + + +@ clean-flush function from ARM940T technical reference manual +.global cache_clean_flush + +cache_clean_flush: + mov r1, #0 @ init line counter +ccf_outer_loop: + mov r0, #0 @ segment counter +ccf_inner_loop: + orr r2, r1, r0 @ make segment and line address + mcr p15, 0, r2, c7, c14, 2 @ clean and flush that line + add r0, r0, #0x10 @ incremet secment counter + cmp r0, #0x40 @ complete all 4 segments? + bne ccf_inner_loop + add r1, r1, #0x04000000 @ increment line counter + cmp r1, #0 @ complete all lines? + bne ccf_outer_loop + bx lr + + +@ clean-only version +.global cache_clean + +cache_clean: + mov r1, #0 @ init line counter +cf_outer_loop: + mov r0, #0 @ segment counter +cf_inner_loop: + orr r2, r1, r0 @ make segment and line address + mcr p15, 0, r2, c7, c10, 2 @ clean that line + add r0, r0, #0x10 @ incremet secment counter + cmp r0, #0x40 @ complete all 4 segments? + bne cf_inner_loop + add r1, r1, #0x04000000 @ increment line counter + cmp r1, #0 @ complete all lines? + bne cf_outer_loop + bx lr + + +.global wait_irq + +wait_irq: + mrs r0, cpsr + bic r0, r0, #0x80 + msr cpsr_c, r0 @ enable interrupts + + mov r0, #0 + mcr p15, 0, r0, c7, c0, 4 @ wait for IRQ +@ mcr p15, 0, r0, c15, c8, 2 + b .b_reserved + +.pool diff --git a/gp2x/940shared.h b/gp2x/940shared.h new file mode 100644 index 0000000..71114da --- /dev/null +++ b/gp2x/940shared.h @@ -0,0 +1,33 @@ +#include "../../Pico/sound/ym2612.h" + +enum _940_job_t { + JOB940_YM2612INIT = 1, + JOB940_YM2612RESETCHIP, + JOB940_YM2612UPDATEONE, + JOB940_PICOSTATELOAD, + JOB940_NUMJOBS +}; + + +typedef struct +{ + YM2612 ym2612; /* current state of the emulated YM2612 */ + int mix_buffer[44100/50*2]; /* this is where the YM2612 samples will be mixed to */ +} _940_data_t; + + +typedef struct +{ + int job; /* a job for second core */ + int busy; /* busy status of the 940 core */ + int length; /* number of samples to mix (882 max) */ + int stereo; /* mix samples as stereo, doubles sample count automatically */ + int baseclock; /* ym2612 settings */ + int rate; + int writebuffsel; /* which write buffer to use (from 940 side) */ + UINT16 writebuff0[2048]; /* 1024 for savestates, 1024 extra */ + UINT16 writebuff1[2048]; + int vstarts[8]; /* debug: number of starts from each of 8 vectors */ + int loopc; /* debug: main loop counter */ + int waitc; /* debug: wait loop counter */ +} _940_ctl_t; diff --git a/gp2x/Makefile b/gp2x/Makefile new file mode 100644 index 0000000..c634c3d --- /dev/null +++ b/gp2x/Makefile @@ -0,0 +1,191 @@ + +# you may or may not need to change this +#devkit_path = x:/stuff/dev/devkitgp2x/ +devkit_path = /usr/local/devkitPro/devkitGP2X/ +lgcc_path = $(devkit_path)lib/gcc/arm-linux/4.0.3/ +CROSS = arm-linux- +#CROSS = $(devkit_path)bin/arm-linux- + +# settings +dprint = 1 +#mz80 = 1 +#debug_cyclone = 1 +asm_memory = 1 +asm_render = 1 +asm_ym2612 = 1 +#profile = 1 +#use_musashi = 1 +#up = 1 + +DEFINC = -I../.. -I. -D__GP2X__ -D_UNZIP_SUPPORT # -DBENCHMARK +COPT_COMMON = -static -s -O3 -ftracer -fstrength-reduce -Wall -funroll-loops -fomit-frame-pointer -fstrict-aliasing -ffast-math +ifeq "$(profile)" "1" +COPT_COMMON += -fprofile-generate +endif +ifeq "$(profile)" "2" +COPT_COMMON += -fprofile-use +endif +COPT = $(COPT_COMMON) -mtune=arm920t +ASOPT = -mcpu=arm920t -mfloat-abi=soft +GCC = $(CROSS)gcc +STRIP = $(CROSS)strip +AS = $(CROSS)as +LD = $(CROSS)ld +OBJCOPY = $(CROSS)objcopy + +# frontend +OBJS += main.o menu.o gp2x.o usbjoy.o emu.o squidgehack.o asmutils.o cpuctrl.o +# 940 core control +OBJS += 940ctl_ym2612.o +# Pico +OBJS += ../../Pico/Area.o ../../Pico/Cart.o ../../Pico/Utils.o ../../Pico/Memory.o ../../Pico/Misc.o \ + ../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o +# asm stuff +ifeq "$(asm_render)" "1" +DEFINC += -D_ASM_DRAW_C +OBJS += ../../Pico/draw_asm.o ../../Pico/draw2_asm.o +endif +ifeq "$(asm_memory)" "1" +DEFINC += -D_ASM_MEMORY_C +OBJS += ../../Pico/memory_asm.o +endif +ifeq "$(asm_ym2612)" "1" +DEFINC += -D_ASM_YM2612_C +OBJS += ../../Pico/sound/ym2612_asm.o +endif +# Pico - sound +OBJS += ../../Pico/sound/sound.o ../../Pico/sound/sn76496.o ../../Pico/sound/ym2612.o +# zlib +OBJS += ../../zlib/gzio.o ../../zlib/inffast.o ../../zlib/inflate.o ../../zlib/inftrees.o ../../zlib/trees.o \ + ../../zlib/deflate.o ../../zlib/crc32.o ../../zlib/adler32.o ../../zlib/zutil.o ../../zlib/compress.o +# unzip +OBJS += ../../unzip/unzip.o +# CPU cores +ifeq "$(use_musashi)" "1" +DEFINC += -DEMU_M68K +OBJS += _build\m68kcpu.o _build\m68kopac.o _build\m68kopdm.o _build\m68kopnz.o _build\m68kops.o +else +DEFINC += -DEMU_C68K +OBJS += ../../cpu/Cyclone/proj/Cyclone.o +endif +# drz80/mz80 +ifeq "$(mz80)" "1" +DEFINC += -D_USE_MZ80 +OBJS += ../../cpu/mz80/mz80.o +else +DEFINC += -D_USE_DRZ80 +OBJS += ../../cpu/DrZ80/drz80.o +endif + +all: PicoDrive.gpe code940.bin + +PicoDrive.gpe : $(OBJS) + @echo $@ + @$(GCC) $(COPT) $(OBJS) $(PRELIBS) -lm -o $@ + @$(STRIP) $@ +# @$(GCC) $(COPT) $(OBJS) $(PRELIBS) -lm -o PicoDrive_.gpe +# @gpecomp PicoDrive_.gpe $@ +ifeq "$(up)" "1" + @cmd //C copy $@ \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\ +endif + +up: up940 + @cmd //C copy PicoDrive.gpe \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\ +up940: + @cmd //C copy code940.bin \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\ + +testrefr.gpe : test.o gp2x.o asmutils.o + @echo $@ + @$(GCC) $(COPT) $^ $(PRELIBS) -o $@ + @$(STRIP) $@ + +.c.o: + @echo $< + @$(GCC) $(COPT) $(DEFINC) -c $< -o $@ +.s.o: + @echo $< + @$(GCC) $(COPT) $(DEFINC) -c $< -o $@ + +../../Pico/draw_asm.o : ../../Pico/Draw.s + @echo $< + @$(AS) $(ASOPT) $< -o $@ +../../Pico/draw2_asm.o : ../../Pico/Draw2.s + @echo $< + @$(AS) $(ASOPT) $< -o $@ +../../Pico/memory_asm.o : ../../Pico/Memory.s + @echo $< + @$(AS) $(ASOPT) $< -o $@ +../../Pico/sound/ym2612_asm.o : ../../Pico/sound/ym2612.s + @echo $< + @$(AS) $(ASOPT) $< -o $@ + +# build Cyclone +../../cpu/Cyclone/proj/Cyclone.s : + @echo building Cyclone... + @make -C ../../cpu/Cyclone/proj -f Makefile.linux + + +# stuff for 940 core + +# init, emu_control, emu +OBJS940 += 940init.o 940.o 940ym2612.o +# the asm code seems to be faster when run on 920, but not on 940 for some reason +# OBJS940 += ../../Pico/sound/ym2612_asm.o + +# uClibc library code +OBJS940 += uClibc/memset.o uClibc/s_floor.o uClibc/e_pow.o uClibc/e_sqrt.o uClibc/s_fabs.o +OBJS940 += uClibc/s_scalbn.o uClibc/s_copysign.o uClibc/k_sin.o uClibc/k_cos.o uClibc/s_sin.o +OBJS940 += uClibc/e_rem_pio2.o uClibc/k_rem_pio2.o uClibc/e_log.o uClibc/wrappers.o + +code940.bin : code940.gpe + @echo $@ + @$(OBJCOPY) -O binary $< $@ + +code940.gpe : $(OBJS940) + @echo $@ + @$(LD) -static -e code940 -Ttext 0x0 $^ -L$(lgcc_path) -lgcc -o $@ + +940ym2612.o : ../../Pico/sound/ym2612.c + @echo $@ + @$(GCC) $(COPT_COMMON) -mtune=arm940t $(DEFINC) -DEXTERNAL_YM2612 -c $< -o $@ + + +# cleanup +clean: clean_pd clean_940 +tidy: tidy_pd tidy_940 + +clean_pd: tidy_pd + @$(RM) PicoDrive.gpe +tidy_pd: + @$(RM) $(OBJS) +# @make -C ../../cpu/Cyclone/proj -f Makefile.linux clean + +clean_940: tidy_940 + @$(RM) code940.bin +tidy_940: + @$(RM) code940.gpe $(OBJS940) + +clean_prof: + find ../.. -name '*.gcno' -delete + find ../.. -name '*.gcda' -delete + +# test +usbjoy.o : usbjoy.c + @echo $< + @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@ + +../../Pico/Cart.o : ../../Pico/Cart.c + @echo $< + @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@ + +../../zlib/trees.o : ../../zlib/trees.c + @echo $< + @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@ + +uClibc/e_pow.o : uClibc/e_pow.c + @echo $< + @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@ + +uClibc/e_sqrt.o : uClibc/e_sqrt.c + @echo $< + @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@ diff --git a/gp2x/asmutils.h b/gp2x/asmutils.h new file mode 100644 index 0000000..d922690 --- /dev/null +++ b/gp2x/asmutils.h @@ -0,0 +1,12 @@ +// (c) Copyright 2006 notaz, All rights reserved. +// Free for non-commercial use. + +// For commercial use, separate licencing terms must be obtained. + +void vidConvCpyRGB32 (void *to, void *from, int pixels); +void vidConvCpyRGB32sh(void *to, void *from, int pixels); +void vidConvCpyRGB32hi(void *to, void *from, int pixels); +void vidCpyM2_40col(void *dest, void *src); +void vidCpyM2_32col(void *dest, void *src); +void vidCpyM2_32col_nobord(void *dest, void *src); +void spend_cycles(int c); // utility diff --git a/gp2x/asmutils.s b/gp2x/asmutils.s new file mode 100644 index 0000000..e1e0945 --- /dev/null +++ b/gp2x/asmutils.s @@ -0,0 +1,210 @@ +@ some color conversion and blitting routines + +@ (c) Copyright 2006, notaz +@ All Rights Reserved + + +@ Convert 0000bbb0 ggg0rrr0 0000bbb0 ggg0rrr0 +@ to 00000000 rrr00000 ggg00000 bbb00000 ... + +@ lr = 0x00e000e0, out: r3=lower_pix, r2=higher_pix; trashes rin +@ if sh==2, r8=0x00404040 (sh!=0 destroys flags!) +.macro convRGB32_2 rin sh=0 + and r2, lr, \rin, lsr #4 @ blue + and r3, \rin, lr + orr r2, r2, r3, lsl #8 @ g0b0g0b0 + + mov r3, r2, lsl #16 @ g0b00000 + and \rin,lr, \rin, ror #12 @ 00r000r0 (reversed) + orr r3, r3, \rin, lsr #16 @ g0b000r0 +.if \sh == 1 + mov r3, r3, ror #17 @ shadow mode +.elseif \sh == 2 + adds r3, r3, #0x40000000 @ green + orrcs r3, r3, #0xe0000000 + mov r3, r3, ror #8 + adds r3, r3, #0x40000000 + orrcs r3, r3, #0xe0000000 + mov r3, r3, ror #16 + adds r3, r3, #0x40000000 + orrcs r3, r3, #0xe0000000 + mov r3, r3, ror #24 +.else + mov r3, r3, ror #16 @ r3=low +.endif + + orr r3, r3, r3, lsr #3 + str r3, [r0], #4 + + mov r2, r2, lsr #16 + orr r2, r2, \rin, lsl #16 +.if \sh == 1 + mov r2, r2, lsr #1 +.elseif \sh == 2 + mov r2, r2, ror #8 + adds r2, r2, #0x40000000 @ blue + orrcs r2, r2, #0xe0000000 + mov r2, r2, ror #8 + adds r2, r2, #0x40000000 + orrcs r2, r2, #0xe0000000 + mov r2, r2, ror #8 + adds r2, r2, #0x40000000 + orrcs r2, r2, #0xe0000000 + mov r2, r2, ror #8 +.endif + + orr r2, r2, r2, lsr #3 + str r2, [r0], #4 +.endm + + +.global vidConvCpyRGB32 @ void *to, void *from, int pixels + +vidConvCpyRGB32: + stmfd sp!, {r4-r7,lr} + + mov r12, r2, lsr #3 @ repeats + mov lr, #0x00e00000 + orr lr, lr, #0x00e0 + +.loopRGB32: + subs r12, r12, #1 + + ldmia r1!, {r4-r7} + convRGB32_2 r4 + convRGB32_2 r5 + convRGB32_2 r6 + convRGB32_2 r7 + + bgt .loopRGB32 + + ldmfd sp!, {r4-r7,lr} + bx lr + + +.global vidConvCpyRGB32sh @ void *to, void *from, int pixels + +vidConvCpyRGB32sh: + stmfd sp!, {r4-r7,lr} + + mov r12, r2, lsr #3 @ repeats + mov lr, #0x00e00000 + orr lr, lr, #0x00e0 + +.loopRGB32sh: + subs r12, r12, #1 + + ldmia r1!, {r4-r7} + convRGB32_2 r4, 1 + convRGB32_2 r5, 1 + convRGB32_2 r6, 1 + convRGB32_2 r7, 1 + + bgt .loopRGB32sh + + ldmfd sp!, {r4-r7,lr} + bx lr + + +.global vidConvCpyRGB32hi @ void *to, void *from, int pixels + +vidConvCpyRGB32hi: + stmfd sp!, {r4-r7,lr} + + mov r12, r2, lsr #3 @ repeats + mov lr, #0x00e00000 + orr lr, lr, #0x00e0 + +.loopRGB32hi: + ldmia r1!, {r4-r7} + convRGB32_2 r4, 2 + convRGB32_2 r5, 2 + convRGB32_2 r6, 2 + convRGB32_2 r7, 2 + + subs r12, r12, #1 + bgt .loopRGB32hi + + ldmfd sp!, {r4-r7,lr} + bx lr + + +@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + +@ mode2 blitter for 40 cols +.global vidCpyM2_40col @ void *dest, void *src + +vidCpyM2_40col: + stmfd sp!, {r4-r6,lr} + + mov r12, #224 @ lines + add r1, r1, #8 + +vidCpyM2_40_loop_out: + mov r6, #10 +vidCpyM2_40_loop: + subs r6, r6, #1 + ldmia r1!, {r2-r5} + stmia r0!, {r2-r5} + ldmia r1!, {r2-r5} + stmia r0!, {r2-r5} + bne vidCpyM2_40_loop + subs r12,r12,#1 + add r1, r1, #8 + bne vidCpyM2_40_loop_out + + ldmfd sp!, {r4-r6,lr} + bx lr + + +@ mode2 blitter for 32 cols +.global vidCpyM2_32col @ void *dest, void *src + +vidCpyM2_32col: + stmfd sp!, {r4-r6,lr} + + mov r12, #224 @ lines + add r1, r1, #8 + add r0, r0, #32 + +vidCpyM2_32_loop_out: + mov r6, #8 +vidCpyM2_32_loop: + subs r6, r6, #1 + ldmia r1!, {r2-r5} + stmia r0!, {r2-r5} + ldmia r1!, {r2-r5} + stmia r0!, {r2-r5} + bne vidCpyM2_32_loop + subs r12,r12,#1 + add r0, r0, #64 + add r1, r1, #8+64 + bne vidCpyM2_32_loop_out + + ldmfd sp!, {r4-r6,lr} + bx lr + + +@ mode2 blitter for 32 cols with no borders +.global vidCpyM2_32col_nobord @ void *dest, void *src + +vidCpyM2_32col_nobord: + stmfd sp!, {r4-r6,lr} + + mov r12, #224 @ lines + add r1, r1, #8 + b vidCpyM2_32_loop_out + + +@ test +.global spend_cycles @ c + +spend_cycles: + mov r0, r0, lsr #2 @ 4 cycles/iteration + sub r0, r0, #2 @ entry/exit/init +.sc_loop: + subs r0, r0, #1 + bpl .sc_loop + + bx lr diff --git a/gp2x/config.txt b/gp2x/config.txt new file mode 100644 index 0000000..0874946 --- /dev/null +++ b/gp2x/config.txt @@ -0,0 +1,138 @@ +As PicoDrive is multiplatform emulator, this is GP2X specific part of readme +about configuration. + + +Configuration +------------- + +1. "Renderer" +8bit fast: +This enables alternative heavily optimized tile-based renderer, which renders +pixels not line-by-line (this is what accurate renderers do), but in 8x8 tiles, +which is much faster. But because of the way it works it can't render any +mid-frame image changes (raster effects), so it is useful only with some games. + +Other two are accurate line-based renderers. The 8bit is faster but does not +run well with some games like Street Racer. + +2. "Accurate timing" +This adds some more emulation precision, but slows the emulation down. Whithout +this option some games do not boot (Red Zone for example), others have sound +problems. + +3. "Accurate sprites" +This option improves emulation of sprite priorities, it also enables emulation +of sprite collision bit. If you see one sprite being drawn incorrectly above +the other (often seen in Sonic 3D Blast), you can enable this to fix the problem. +This only works with the default renderer (see first option). + +4. "Show FPS" +Self-explanatory. Format is XX/YY, where XX is the number of rendered frames and +YY is the number of emulated frames per second. + +5. "Frameskip" +How many frames to skip rendering before displaying another. +"Auto" is recommended. + +6. "Enable sound" +Does what it says. You must enable at least YM2612 or SN76496 (in advanced options, +see below) for this to make sense. + +7. "Sound Quality" +Sound rate and stereo mode. If you want 44100Hz sound, it is recommended to enable +the second core (next option). + +8. "Use ARM940 core for sound" +This option causes PicoDrive to use ARM940T core (GP2X's second CPU) for sound +(i.e. to generate YM2612 samples) to improve performance noticeably. + +9. "6 button pad" +If you enable this, games will think that 6 button gamepad is connected. If you +go and reconfigure your keys, you will be able to bind X,Y,Z and mode actions. + +10. "Genesis Region" +This option lets you force the game to think it is running on machine from the +specified region. + +11. "Use SRAM savestates" +This will automatically read/write SRAM savestates for games which are using them. +SRAM is saved whenever you pause your game or exit the emulator. + +12. "GP2X CPU clocks" +Here you can change clocks of both GP2X's CPUs. Larger values increase performance. +There is no separate option for the second CPU because both CPUs use the same clock +source. Setting this option to 200 will cause PicoDrive NOT to change GP2X's clocks +at all. + +13. "[advanced options]" +Enters advanced options menu (see below). + +14. "Save cfg as default" +If you save your config here it will be loaded on next ROM load, but only if there +is no game specific config saved (which will be loaded in that case). + +15. "Save cfg for current game only" +Whenever you load current ROM again these settings will be loaded (squidgehack and +RAM settings will not take effect until emulator is restarted). + +Advanced configuration +---------------------- + +Enter [advanced options] in config menu to see these options. + +1. "Scale 32 column mode" +This enables hardware scaling for lower-res genesis mode (where width is +32 8-pixel tiles, instead of 40 in other mode). + +2. "Gamma correction" +Alters image gamma through GP2X hardware. Larger values make image to look brighter, +lower - darker (default is 1.0). + +3. "Emulate Z80" +Enables emulation of Z80 chip, which was mostly used to drive the other sound chips. +Some games do complex sync with it, so you must enable it even if you don't use +sound to be able to play them. + +4. "Emulate YM2612 (FM)" +This enables emulation of six-channel FM sound synthesizer chip, which was used to +produce sound effects and music. + +5. "Emulate SN76496 (PSG)" +This enables emulation of additional sound chip for additional effects. + +Note: if you change sound settings AFTER loading a ROM, you may need to reset +game to get sound. This is because most games initialize sound chips on +startup, and this data is lost when sound chips are being enabled/disabled. + +6. "gzip savestates" +This will always apply gzip compression on your savestates, allowing you to +save some space and load/save time. + +7. "USB joy controls player X" +If you are able to use USB joysticks with your GP2X, this options selects which +player the joystick controls. + +8. "Don't save config on exit" +This will disable config autowrite on exit (which might cause SD card corruption +according to DaveC). + +9. "craigix's RAM timings" +This overclocks the GP2X RAM chips, but may cause instability. Recommended if you +use the second core for sound. Needs emulator restart to take effect. +See this thread: +http://www.gp32x.com/board/index.php?showtopic=32319 + +10. "squidgehack" +Well known way to improve the GP2X performance. You must restart the emulator +for the change of this option to take effect. + + +Key configuration +----------------- + +When you select "Configure controls" from the menu, you enter a key configuration +mode, where you use SELECT to change an action, and then press a key you like to +bind to that action. You can press the same key again to unbind. Select "DONE" +action and press any key to finish. + + diff --git a/gp2x/cpuctrl.c b/gp2x/cpuctrl.c new file mode 100644 index 0000000..7bbdb67 --- /dev/null +++ b/gp2x/cpuctrl.c @@ -0,0 +1,156 @@ +/* cpuctrl for GP2X + Copyright (C) 2005 Hermes/PS2Reality + the gamma-routine was provided by theoddbot + parts (c) Rlyehs Work & (C) 2006 god_at_hell + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include +#include +#include "cpuctrl.h" + + +/* system registers */ +static struct +{ + unsigned short SYSCLKENREG,SYSCSETREG,FPLLVSETREG,DUALINT920,DUALINT940,DUALCTRL940,MEMTIMEX0,MEMTIMEX1; +} +system_reg; + +static unsigned short dispclockdiv; + +static volatile unsigned short *MEM_REG; + +#define SYS_CLK_FREQ 7372800 + + +void cpuctrl_init(void) +{ + extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */ + MEM_REG=&gp2x_memregs[0]; + system_reg.SYSCSETREG=MEM_REG[0x91c>>1]; + system_reg.FPLLVSETREG=MEM_REG[0x912>>1]; + system_reg.SYSCLKENREG=MEM_REG[0x904>>1]; + system_reg.DUALINT920=MEM_REG[0x3B40>>1]; + system_reg.DUALINT940=MEM_REG[0x3B42>>1]; + system_reg.DUALCTRL940=MEM_REG[0x3B48>>1]; + system_reg.MEMTIMEX0=MEM_REG[0x3802>>1]; + system_reg.MEMTIMEX1=MEM_REG[0x3804>>1]; + dispclockdiv=MEM_REG[0x924>>1]; +} + + +void cpuctrl_deinit(void) +{ + MEM_REG[0x91c>>1]=system_reg.SYSCSETREG; + MEM_REG[0x910>>1]=system_reg.FPLLVSETREG; + MEM_REG[0x3B40>>1]=system_reg.DUALINT920; + MEM_REG[0x3B42>>1]=system_reg.DUALINT940; + MEM_REG[0x3B48>>1]=system_reg.DUALCTRL940; + MEM_REG[0x904>>1]=system_reg.SYSCLKENREG; + MEM_REG[0x924>>1]=dispclockdiv; + MEM_REG[0x3802>>1]=system_reg.MEMTIMEX0; + MEM_REG[0x3804>>1]=system_reg.MEMTIMEX1 /*| 0x9000*/; +} + + +void set_display_clock_div(unsigned div) +{ + div=((div & 63) | 64)<<8; + MEM_REG[0x924>>1]=(MEM_REG[0x924>>1] & ~(255<<8)) | div; +} + + +void set_FCLK(unsigned MHZ) +{ + unsigned v; + unsigned mdiv,pdiv=3,scale=0; + MHZ*=1000000; + mdiv=(MHZ*pdiv)/SYS_CLK_FREQ; + mdiv=((mdiv-8)<<8) & 0xff00; + pdiv=((pdiv-2)<<2) & 0xfc; + scale&=3; + v=mdiv | pdiv | scale; + MEM_REG[0x910>>1]=v; +} + + +void set_920_Div(unsigned short div) +{ + unsigned short v; + v = MEM_REG[0x91c>>1] & (~0x3); + MEM_REG[0x91c>>1] = (div & 0x7) | v; +} + + +void set_DCLK_Div( unsigned short div ) +{ + unsigned short v; + v = (unsigned short)( MEM_REG[0x91c>>1] & (~(0x7 << 6)) ); + MEM_REG[0x91c>>1] = ((div & 0x7) << 6) | v; +} + +/* +void Disable_940(void) +{ + MEM_REG[0x3B42>>1]; + MEM_REG[0x3B42>>1]=0; + MEM_REG[0x3B46>>1]=0xffff; + MEM_REG[0x3B48>>1]|= (1 << 7); + MEM_REG[0x904>>1]&=0xfffe; +} +*/ + +void set_RAM_Timings(int tRC, int tRAS, int tWR, int tMRD, int tRFC, int tRP, int tRCD) +{ + tRC -= 1; tRAS -= 1; tWR -= 1; tMRD -= 1; tRFC -= 1; tRP -= 1; tRCD -= 1; // ??? + MEM_REG[0x3802>>1] = ((tMRD & 0xF) << 12) | ((tRFC & 0xF) << 8) | ((tRP & 0xF) << 4) | (tRCD & 0xF); + MEM_REG[0x3804>>1] = /*0x9000 |*/ ((tRC & 0xF) << 8) | ((tRAS & 0xF) << 4) | (tWR & 0xF); +} + + +/* +void gp2x_video_wait_vsync(void) +{ + MEM_REG[0x2846>>1]=(MEM_REG[0x2846>>1] | 0x20) & ~2; + while(!(MEM_REG[0x2846>>1] & 2)); +} +*/ + +void set_gamma(int g100) +{ + float gamma = (float) g100 / 100; + int i; + //printf ("set gamma = %f\r\n",gamma); + gamma = 1/gamma; + + //enable gamma + MEM_REG[0x2880>>1]&=~(1<<12); + + MEM_REG[0x295C>>1]=0; + for(i=0; i<256; i++) + { + unsigned char g; + unsigned short s; + g =(unsigned char)(255.0*pow(i/255.0,gamma)); + s = (g<<8) | g; + MEM_REG[0x295E>>1]= s; + MEM_REG[0x295E>>1]= g; + } +} + diff --git a/gp2x/cpuctrl.gpl.txt b/gp2x/cpuctrl.gpl.txt new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/gp2x/cpuctrl.gpl.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gp2x/cpuctrl.h b/gp2x/cpuctrl.h new file mode 100644 index 0000000..5b482a5 --- /dev/null +++ b/gp2x/cpuctrl.h @@ -0,0 +1,16 @@ +#ifndef __CPUCTRL_H__ +#define __CPUCTRL_H__ + +extern void cpuctrl_init(void); /* call this at first */ +extern void save_system_regs(void); /* save some registers */ +extern void cpuctrl_deinit(void); +extern void set_display_clock_div(unsigned div); +extern void set_FCLK(unsigned MHZ); /* adjust the clock frequency (in Mhz units) */ +extern void set_920_Div(unsigned short div); /* 0 to 7 divider (freq=FCLK/(1+div)) */ +extern void set_DCLK_Div(unsigned short div); /* 0 to 7 divider (freq=FCLK/(1+div)) */ +//extern void Disable_940(void); /* 940t down */ +//extern void gp2x_video_wait_vsync(void); +extern void set_RAM_Timings(int tRC, int tRAS, int tWR, int tMRD, int tRFC, int tRP, int tRCD); +extern void set_gamma(int g100); + +#endif diff --git a/gp2x/emu.c b/gp2x/emu.c new file mode 100644 index 0000000..0e4fdd7 --- /dev/null +++ b/gp2x/emu.c @@ -0,0 +1,1121 @@ +// (c) Copyright 2006 notaz, All rights reserved. +// Free for non-commercial use. + +// For commercial use, separate licencing terms must be obtained. + +#include +#include +#include +#include +#include +#include + +#include + +#include "emu.h" +#include "gp2x.h" +#include "usbjoy.h" +#include "menu.h" +#include "asmutils.h" +#include "cpuctrl.h" + +#include "Pico/PicoInt.h" +#include "zlib/zlib.h" + + +#ifdef BENCHMARK +#define OSD_FPS_X 220 +#else +#define OSD_FPS_X 260 +#endif + +// PicoPad[] format: SACB RLDU +char *actionNames[] = { + "UP", "DOWN", "LEFT", "RIGHT", "B", "C", "A", "START", + 0, 0, 0, 0, 0, 0, 0, 0, // Z, Y, X, MODE (enabled only when needed), ?, ?, ?, ? + 0, 0, 0, 0, 0, 0, 0, "ENTER MENU", // player2_flag, ?, ?, ?, ?, ?, ?, menu + "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE", + "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE" +}; + +int engineState; +int select_exits = 0; +char *PicoConfigFile = "picoconfig.bin"; +currentConfig_t currentConfig; + +char romFileName[PATH_MAX]; +unsigned char *rom_data = NULL; + +extern int crashed_940; + +static char noticeMsg[64]; // notice msg to draw +static struct timeval noticeMsgTime = { 0, 0 }; // when started showing +static int reset_timing, osd_fps_x; +static int combo_keys = 0, combo_acts = 0; // keys and actions which need button combos +static int gp2x_old_gamma = 100; +static unsigned char *movie_data = NULL; +static int movie_size = 0; +static int frame_count = 0; +unsigned char *framebuff = 0; // temporary buffer for alt renderer +int state_slot = 0; + +/* +// tmp +static FILE *logf = NULL; + +void pprintf(char *texto, ...) +{ + va_list args; + + va_start(args,texto); + vfprintf(logf,texto,args); + va_end(args); + fflush(logf); + sync(); +} +*/ +// utilities +static void strlwr(char* string) +{ + while ( (*string++ = (char)tolower(*string)) ); +} + +static int try_rfn_ext(char *ext) +{ + FILE *tmp; + char *p; + + p = romFileName + strlen(romFileName) - 4; + if (p < romFileName) p = romFileName; + strcpy(p, ext); + + if((tmp = fopen(romFileName, "rb"))) { + fclose(tmp); + return 1; + } + return 0; +} + +int emu_ReloadRom(void) +{ + unsigned int rom_size = 0; + char ext[5], *p; + FILE *rom; + int ret; + + printf("emu_ReloadRom(%s)\n", romFileName); + + // detect wrong extensions (.srm and .mds) + p = romFileName + strlen(romFileName) - 4; + if (p < romFileName) p = romFileName; + strncpy(ext, p, 4); + ext[4] = 0; + strlwr(ext); + + if(!strcmp(ext, ".srm") || !strcmp(ext, "s.gz") || !strcmp(ext, ".mds")) { // s.gz ~ .mds.gz + sprintf(menuErrorMsg, "Not a ROM selected."); + return 0; + } + + // check for movie file + if(movie_data) { + free(movie_data); + movie_data = 0; + } + if(!strcmp(ext, ".gmv")) { + // check for both gmv and rom + int dummy; + FILE *movie_file = fopen(romFileName, "rb"); + if(!movie_file) { + sprintf(menuErrorMsg, "Failed to open movie."); + return 0; + } + fseek(movie_file, 0, SEEK_END); + movie_size = ftell(movie_file); + fseek(movie_file, 0, SEEK_SET); + if(movie_size < 64+3) { + sprintf(menuErrorMsg, "Invalid GMV file."); + fclose(movie_file); + return 0; + } + movie_data = malloc(movie_size); + if(movie_data == NULL) { + sprintf(menuErrorMsg, "low memory."); + fclose(movie_file); + return 0; + } + fread(movie_data, 1, movie_size, movie_file); + fclose(movie_file); + if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) { + sprintf(menuErrorMsg, "Invalid GMV file."); + return 0; + } + dummy = try_rfn_ext(".zip") || try_rfn_ext(".bin") || + try_rfn_ext(".smd") || try_rfn_ext(".gen"); + if (!dummy) { + sprintf(menuErrorMsg, "Could't find a ROM for movie."); + return 0; + } + } + + rom = fopen(romFileName, "rb"); + if(!rom) { + sprintf(menuErrorMsg, "Failed to open rom."); + return 0; + } + + if(rom_data) { + free(rom_data); + rom_data = 0; + rom_size = 0; + } + + // zipfile support + if(!strcasecmp(ext, ".zip")) { + fclose(rom); + ret = CartLoadZip(romFileName, &rom_data, &rom_size); + if(ret) { + if (ret == 4) strcpy(menuErrorMsg, "No ROMs in zip found."); + else sprintf(menuErrorMsg, "Unzip failed with code %i", ret); + printf("%s\n", menuErrorMsg); + return 0; + } + } else { + if( (ret = PicoCartLoad(rom, &rom_data, &rom_size)) ) { + sprintf(menuErrorMsg, "PicoCartLoad() failed."); + printf("%s\n", menuErrorMsg); + fclose(rom); + return 0; + } + fclose(rom); + } + + // detect wrong files (Pico crashes on very small files), also see if ROM EP is good + if(rom_size <= 0x200 || strncmp((char *)rom_data, "Pico", 4) == 0 || + ((*(unsigned short *)(rom_data+4)<<16)|(*(unsigned short *)(rom_data+6))) >= (int)rom_size) { + if (rom_data) free(rom_data); + rom_data = 0; + sprintf(menuErrorMsg, "Not a ROM selected."); + return 0; + } + + printf("PicoCartInsert(%p, %d);\n", rom_data, rom_size); + if(PicoCartInsert(rom_data, rom_size)) { + sprintf(menuErrorMsg, "Failed to load ROM."); + return 0; + } + + // load config for this ROM + ret = emu_ReadConfig(1); + if (!ret) + emu_ReadConfig(0); + + // emu_ReadConfig() might have messed currentConfig.lastRomFile + strncpy(currentConfig.lastRomFile, romFileName, sizeof(currentConfig.lastRomFile)-1); + currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0; + + // additional movie stuff + if(movie_data) { + if(movie_data[0x14] == '6') + PicoOpt |= 0x20; // 6 button pad + else PicoOpt &= ~0x20; + if(movie_data[0xF] >= 'A') { + //Pico.m.pal = movie_data[0x16] >> 7; + // TODO: bits 6 & 5 + } + strcpy(noticeMsg, "MOVIE LOADED"); + } + else + { + if(Pico.m.pal) { + strcpy(noticeMsg, "PAL SYSTEM / 50 FPS"); + } else { + strcpy(noticeMsg, "NTSC SYSTEM / 60 FPS"); + } + } + gettimeofday(¬iceMsgTime, 0); + + // load SRAM for this ROM + if(currentConfig.EmuOpt & 1) + emu_SaveLoadGame(1, 1); + + frame_count = 0; + + return 1; +} + + +void emu_Init(void) +{ + // make temp buffer for alt renderer + framebuff = malloc((8+320)*(8+240+8)); + if (!framebuff) + { + printf("framebuff == 0\n"); + } + + PicoInit(); + +// logf = fopen("log.txt", "w"); +} + + +static void romfname_ext(char *dst, char *ext) +{ + char *p; + + // make save filename + for (p = romFileName+strlen(romFileName)-1; p >= romFileName && *p != '/'; p--); p++; + strncpy(dst, p, 511); + dst[511-8] = 0; + if(dst[strlen(dst)-4] == '.') dst[strlen(dst)-4] = 0; + strcat(dst, ext); +} + + +static void find_combos(void) +{ + int act, u; + + // find out which keys and actions are combos + combo_keys = combo_acts = 0; + for (act = 0; act < 32; act++) + { + int keyc = 0; + if (act == 16) continue; // player2 flag + for (u = 0; u < 32; u++) + { + if (currentConfig.KeyBinds[u] & (1 << act)) keyc++; + } + if (keyc > 1) + { + // loop again and mark those keys and actions as combo + for (u = 0; u < 32; u++) + { + if (currentConfig.KeyBinds[u] & (1 << act)) { + combo_keys |= 1 << u; + combo_acts |= 1 << act; + } + } + } + } + // printf("combo keys/acts: %08x %08x\n", combo_keys, combo_acts); +} + + +int emu_ReadConfig(int game) +{ + FILE *f; + char cfg[512]; + int bread = 0; + + if (!game) + { + // set default config + memset(¤tConfig, 0, sizeof(currentConfig)); + currentConfig.lastRomFile[0] = 0; + currentConfig.EmuOpt = 0x1f; + currentConfig.PicoOpt = 0x0f; + currentConfig.PsndRate = 22050; + currentConfig.PicoRegion = 0; // auto + currentConfig.Frameskip = -1; // auto + currentConfig.CPUclock = 200; + currentConfig.volume = 50; + currentConfig.KeyBinds[ 0] = 1<<0; // SACB RLDU + currentConfig.KeyBinds[ 4] = 1<<1; + currentConfig.KeyBinds[ 2] = 1<<2; + currentConfig.KeyBinds[ 6] = 1<<3; + currentConfig.KeyBinds[14] = 1<<4; + currentConfig.KeyBinds[13] = 1<<5; + currentConfig.KeyBinds[12] = 1<<6; + currentConfig.KeyBinds[ 8] = 1<<7; + currentConfig.KeyBinds[15] = 1<<26; // switch rend + currentConfig.KeyBinds[10] = 1<<27; // save state + currentConfig.KeyBinds[11] = 1<<28; // load state + currentConfig.KeyBinds[23] = 1<<29; // vol up + currentConfig.KeyBinds[22] = 1<<30; // vol down + currentConfig.gamma = 100; + strncpy(cfg, PicoConfigFile, 511); + cfg[511] = 0; + } else { + romfname_ext(cfg, ".pbcfg"); + } + + printf("emu_ReadConfig: %s ", cfg); + f = fopen(cfg, "rb"); + if (f) { + bread = fread(¤tConfig, 1, sizeof(currentConfig), f); + fclose(f); + } + printf((bread == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n"); + + PicoOpt = currentConfig.PicoOpt; + PsndRate = currentConfig.PsndRate; + PicoRegionOverride = currentConfig.PicoRegion; + if (PicoOpt & 0x20) { + actionNames[ 8] = "Z"; actionNames[ 9] = "Y"; + actionNames[10] = "X"; actionNames[11] = "MODE"; + } + // some sanity checks + if (currentConfig.CPUclock < 1 || currentConfig.CPUclock > 4096) currentConfig.CPUclock = 200; + if (currentConfig.gamma < 10 || currentConfig.gamma > 300) currentConfig.gamma = 100; + // if volume keys are unbound, bind them to volume control + if (!currentConfig.KeyBinds[23] && !currentConfig.KeyBinds[22]) { + currentConfig.KeyBinds[23] = 1<<29; // vol up + currentConfig.KeyBinds[22] = 1<<30; // vol down + } + + return (bread == sizeof(currentConfig)); +} + + +int emu_WriteConfig(int game) +{ + FILE *f; + char cfg[512]; + int bwrite = 0; + + if (!game) + { + strncpy(cfg, PicoConfigFile, 511); + cfg[511] = 0; + } else { + romfname_ext(cfg, ".pbcfg"); + } + + printf("emu_WriteConfig: %s ", cfg); + f = fopen(cfg, "wb"); + if (f) { + currentConfig.PicoOpt = PicoOpt; + currentConfig.PsndRate = PsndRate; + currentConfig.PicoRegion = PicoRegionOverride; + bwrite = fwrite(¤tConfig, 1, sizeof(currentConfig), f); + fflush(f); + fclose(f); + sync(); + } + printf((bwrite == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n"); + + return (bwrite == sizeof(currentConfig)); +} + + +void emu_Deinit(void) +{ + // save SRAM + if((currentConfig.EmuOpt & 1) && SRam.changed) { + emu_SaveLoadGame(0, 1); + SRam.changed = 0; + } + + if (!(currentConfig.EmuOpt & 0x20)) + emu_WriteConfig(0); + free(framebuff); + + PicoExit(); +// fclose(logf); + + // restore gamma + if (gp2x_old_gamma != 100) + set_gamma(100); +} + + +void osd_text(int x, int y, char *text) +{ + int len = strlen(text)*8; + + if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) { + int *p, i, h, black, white; + if (PicoOpt&0x10) { + black = 0x40404040; white = 0x41; + } else { + black = 0xe0e0e0e0; white = 0xf0; + } + x &= ~3; // align x + len = (len+3) >> 2; + for (h = 0; h < 8; h++) { + p = (int *) ((unsigned char *) gp2x_screen+x+320*(y+h)); + for (i = len; i; i--, p++) *p = black; + } + gp2x_text_out8_2(x, y, text, white); + } else { + int *p, i, h; + x &= ~1; // align x + len = (len+1) >> 1; + for (h = 0; h < 8; h++) { + p = (int *) ((unsigned short *) gp2x_screen+x+320*(y+h)); + for (i = len; i; i--, p++) *p = (*p>>2)&0x39e7; + } + gp2x_text_out15(x, y, text); + } +} + +static int EmuScan16(unsigned int num, void *sdata) +{ + if (!(Pico.video.reg[1]&8)) num += 8; + DrawLineDest = (unsigned short *) gp2x_screen + 320*(num+1); + + return 0; +} + +static int EmuScan8(unsigned int num, void *sdata) +{ + if (!(Pico.video.reg[1]&8)) num += 8; + DrawLineDest = (unsigned char *) gp2x_screen + 320*(num+1); + + return 0; +} + +static int localPal[0x100]; +static void (*vidCpyM2)(void *dest, void *src) = NULL; + +static void blit(char *fps, char *notice) +{ + if (PicoOpt&0x10) { + // 8bit fast renderer + if (Pico.m.dirtyPal) { + Pico.m.dirtyPal = 0; + vidConvCpyRGB32(localPal, Pico.cram, 0x40); + // feed new palette to our device + gp2x_video_setpalette(localPal, 0x40); + } + vidCpyM2((unsigned char *)gp2x_screen+320*8, framebuff+328*8); + } else if (!(currentConfig.EmuOpt&0x80)) { + // 8bit accurate renderer + if (Pico.m.dirtyPal) { + Pico.m.dirtyPal = 0; + if(Pico.video.reg[0xC]&8) { // shadow/hilight mode + vidConvCpyRGB32(localPal, Pico.cram, 0x40); + vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40); + vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40); + blockcpy(localPal+0xc0, localPal+0x40, 0x40*4); + localPal[0xe0] = 0x00000000; // reserved pixels for OSD + localPal[0xf0] = 0x00ffffff; + gp2x_video_setpalette(localPal, 0x100); + } else if (rendstatus & 0x20) { // mid-frame palette changes + vidConvCpyRGB32(localPal, Pico.cram, 0x40); + vidConvCpyRGB32(localPal+0x40, HighPal, 0x40); + vidConvCpyRGB32(localPal+0x80, HighPal+0x40, 0x40); + gp2x_video_setpalette(localPal, 0xc0); + } else { + vidConvCpyRGB32(localPal, Pico.cram, 0x40); + gp2x_video_setpalette(localPal, 0x40); + } + } + } + + if (notice) osd_text(4, 232, notice); + if (currentConfig.EmuOpt & 2) + osd_text(osd_fps_x, 232, fps); + + //gp2x_video_wait_vsync(); + gp2x_video_flip(); + + if (!(PicoOpt&0x10)) { + if (!(Pico.video.reg[1]&8)) { + if (currentConfig.EmuOpt&0x80) { + DrawLineDest = (unsigned short *) gp2x_screen + 320*8; + } else { + DrawLineDest = (unsigned char *) gp2x_screen + 320*8; + } + } else { + DrawLineDest = gp2x_screen; + } + } +} + + +// clears whole screen or just the notice area (in all buffers) +static void clearArea(int full) +{ + if (PicoOpt&0x10) { + // 8bit fast renderer + if (full) gp2x_memset_all_buffers(0, 0x40, 320*240); + else gp2x_memset_all_buffers(320*232, 0x40, 320*8); + } else if (currentConfig.EmuOpt&0x80) { + // 16bit accurate renderer + if (full) gp2x_memset_all_buffers(0, 0, 320*240*2); + else gp2x_memset_all_buffers(320*232*2, 0, 320*8*2); + } else { + // 8bit accurate renderer + if (full) gp2x_memset_all_buffers(0, 0xe0, 320*240); + else gp2x_memset_all_buffers(320*232, 0xe0, 320*8); + } +} + + +static void vidResetMode(void) +{ + if (PicoOpt&0x10) { + localPal[0x40] = 0; + localPal[0x41] = 0x00ffffff; + gp2x_video_changemode(8); + gp2x_video_setpalette(localPal, 0x42); + gp2x_memset_all_buffers(0, 0x40, 320*240); + gp2x_video_flip(); + } else if (currentConfig.EmuOpt&0x80) { + gp2x_video_changemode(15); + PicoDrawSetColorFormat(1); + PicoScan = EmuScan16; + PicoScan(0, 0); + } else { + localPal[0xe0] = 0x00000000; // reserved pixels for OSD + localPal[0xf0] = 0x00ffffff; + gp2x_video_changemode(8); + gp2x_video_setpalette(localPal, 0x100); + gp2x_memset_all_buffers(0, 0xe0, 320*240); + gp2x_video_flip(); + PicoDrawSetColorFormat(2); + PicoScan = EmuScan8; + PicoScan(0, 0); + } + Pico.m.dirtyPal = 1; + // reset scaling + gp2x_video_RGB_setscaling((PicoOpt&0x100)&&!(Pico.video.reg[12]&1) ? 256 : 320, 240); +} + + +static int check_save_file(void) +{ + char saveFname[512]; + char ext[16]; + FILE *f; + + ext[0] = 0; + if(state_slot > 0 && state_slot < 10) sprintf(ext, ".%i", state_slot); + strcat(ext, ".mds"); + if(currentConfig.EmuOpt & 8) strcat(ext, ".gz"); + + romfname_ext(saveFname, ext); + if ((f = fopen(saveFname, "rb"))) { + fclose(f); + return 1; + } + return 0; +} + +static void RunEvents(unsigned int which) +{ + if(which & 0x1800) { // save or load (but not both) + int do_it = 1; + if (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200) && check_save_file()) { + unsigned long keys; + blit("", "OVERWRITE SAVE? (Y=yes, X=no)"); + while( !((keys = gp2x_joystick_read(1)) & (GP2X_X|GP2X_Y)) ) + usleep(50*1024); + if (keys & GP2X_X) do_it = 0; + clearArea(0); + } + if (do_it) { + blit("", (which & 0x1000) ? "LOADING GAME" : "SAVING GAME"); + emu_SaveLoadGame(which & 0x1000, 0); + } + + reset_timing = 1; + } + if(which & 0x0400) { // switch renderer + if ( PicoOpt&0x10) { PicoOpt&=~0x10; currentConfig.EmuOpt |= 0x80; } + else if (!(currentConfig.EmuOpt&0x80)) PicoOpt|= 0x10; + else currentConfig.EmuOpt &= ~0x80; + + vidResetMode(); + + if (PicoOpt&0x10) { + strcpy(noticeMsg, " 8bit fast renderer"); + } else if (currentConfig.EmuOpt&0x80) { + strcpy(noticeMsg, "16bit accurate renderer"); + } else { + strcpy(noticeMsg, " 8bit accurate renderer"); + } + + gettimeofday(¬iceMsgTime, 0); + } + if(which & 0x0300) { + if(which&0x0200) { + state_slot -= 1; + if(state_slot < 0) state_slot = 9; + } else { + state_slot += 1; + if(state_slot > 9) state_slot = 0; + } + sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, check_save_file() ? "USED" : "FREE"); + gettimeofday(¬iceMsgTime, 0); + } + if(which & 0x0080) { + engineState = PGS_Menu; + } +} + + +static void updateKeys(void) +{ + unsigned long keys, allActions[2] = { 0, 0 }, events; + static unsigned long prevEvents = 0; + int joy, i; + + keys = gp2x_joystick_read(0); + if (keys & GP2X_SELECT) { + engineState = select_exits ? PGS_Quit : PGS_Menu; + // wait until select is released, so menu would not resume game + while (gp2x_joystick_read(1) & GP2X_SELECT) usleep(50*1000); + } + + keys &= CONFIGURABLE_KEYS; + + for (i = 0; i < 32; i++) + { + if (keys & (1 << i)) { + int pl, acts = currentConfig.KeyBinds[i]; + if (!acts) continue; + pl = (acts >> 16) & 1; + if (combo_keys & (1 << i)) { + int u = i+1, acts_c = acts & combo_acts; + // let's try to find the other one + if (acts_c) + for (; u < 32; u++) + if ( (currentConfig.KeyBinds[u] & acts_c) && (keys & (1 << u)) ) { + allActions[pl] |= acts_c; + keys &= ~((1 << i) | (1 << u)); + break; + } + // add non-combo actions if combo ones were not found + if (!acts_c || u == 32) + allActions[pl] |= acts & ~combo_acts; + } else { + allActions[pl] |= acts; + } + } + } + + // add joy inputs + if (num_of_joys > 0) + { + gp2x_usbjoy_update(); + for (joy = 0; joy < num_of_joys; joy++) { + int keys = gp2x_usbjoy_check2(joy); + for (i = 0; i < 32; i++) { + if (keys & (1 << i)) { + int acts = currentConfig.JoyBinds[joy][i]; + int pl = (acts >> 16) & 1; + allActions[pl] |= acts; + } + } + } + } + + if(movie_data) + { + int offs = frame_count*3 + 0x40; + if (offs+3 > movie_size) { + free(movie_data); + movie_data = 0; + strcpy(noticeMsg, "END OF MOVIE."); + printf("END OF MOVIE.\n"); + gettimeofday(¬iceMsgTime, 0); + } else { + // MXYZ SACB RLDU + PicoPad[0] = ~movie_data[offs] & 0x8f; // ! SCBA RLDU + if(!(movie_data[offs] & 0x10)) PicoPad[0] |= 0x40; // A + if(!(movie_data[offs] & 0x20)) PicoPad[0] |= 0x10; // B + if(!(movie_data[offs] & 0x40)) PicoPad[0] |= 0x20; // A + PicoPad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU + if(!(movie_data[offs+1] & 0x10)) PicoPad[1] |= 0x40; // A + if(!(movie_data[offs+1] & 0x20)) PicoPad[1] |= 0x10; // B + if(!(movie_data[offs+1] & 0x40)) PicoPad[1] |= 0x20; // A + PicoPad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX + if(!(movie_data[offs+2] & 0x01)) PicoPad[0] |= 0x0400; // X + if(!(movie_data[offs+2] & 0x04)) PicoPad[0] |= 0x0100; // Z + PicoPad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX + if(!(movie_data[offs+2] & 0x10)) PicoPad[1] |= 0x0400; // X + if(!(movie_data[offs+2] & 0x40)) PicoPad[1] |= 0x0100; // Z + if ((PicoPad[0] & 0x80) || (PicoPad[1] & 0x80)) + printf("%d: start\n", frame_count); + } + } + else + { + PicoPad[0] = (unsigned short) allActions[0]; + PicoPad[1] = (unsigned short) allActions[1]; + } + frame_count++; + + events = (allActions[0] | allActions[1]) >> 16; + + // volume is treated in special way and triggered every frame + if(events & 0x6000) { + int vol = currentConfig.volume; + if (events & 0x2000) { + if (vol < 90) vol++; + } else { + if (vol > 0) vol--; + } + gp2x_sound_volume(vol, vol); + sprintf(noticeMsg, "VOL: %02i", vol); + gettimeofday(¬iceMsgTime, 0); + currentConfig.volume = vol; + } + + events &= ~prevEvents; + if (events) RunEvents(events); + + prevEvents = (allActions[0] | allActions[1]) >> 16; +} + +static int snd_excess_add = 0, snd_excess_cnt = 0; // hack + +static void updateSound(void) +{ + int len = (PicoOpt&8)?PsndLen*2:PsndLen; + + snd_excess_cnt += snd_excess_add; + if (snd_excess_cnt >= 0x10000) { + snd_excess_cnt -= 0x10000; + if (PicoOpt&8) { + PsndOut[len] = PsndOut[len-2]; + PsndOut[len+1] = PsndOut[len-1]; + len+=2; + } else { + PsndOut[len] = PsndOut[len-1]; + len++; + } + } + + gp2x_sound_write(PsndOut, len<<1); +} + + +static void SkipFrame(int do_sound) +{ + void *sndbuff_tmp = 0; + if (PsndOut && !do_sound) { + sndbuff_tmp = PsndOut; + PsndOut = 0; + } + + PicoSkipFrame=1; + PicoFrame(); + PicoSkipFrame=0; + + if (sndbuff_tmp && !do_sound) { + PsndOut = sndbuff_tmp; + } +} + + +static void simpleWait(int thissec, int lim_time) +{ + struct timeval tval; + + spend_cycles(1024); + gettimeofday(&tval, 0); + if(thissec != tval.tv_sec) tval.tv_usec+=1000000; + + while(tval.tv_usec < lim_time) + { + spend_cycles(1024); + gettimeofday(&tval, 0); + if(thissec != tval.tv_sec) tval.tv_usec+=1000000; + } +} + + +void emu_Loop(void) +{ + static int gp2x_old_clock = 200; + static int PsndRate_old = 0, PicoOpt_old = 0, PsndLen_real = 0, pal_old = 0; + char fpsbuff[24]; // fps count c string + struct timeval tval; // timing + int thissec = 0, frames_done = 0, frames_shown = 0, oldmodes = 0; + int target_fps, target_frametime, lim_time, i; + char *notice = 0; + + printf("entered emu_Loop()\n"); + + if (gp2x_old_clock != currentConfig.CPUclock) { + printf("changing clock to %i...", currentConfig.CPUclock); fflush(stdout); + set_FCLK(currentConfig.CPUclock); + gp2x_old_clock = currentConfig.CPUclock; + printf(" done\n"); + } + + if (gp2x_old_gamma != currentConfig.gamma) { + set_gamma(currentConfig.gamma); + gp2x_old_gamma = currentConfig.gamma; + printf("updated gamma to %i\n", currentConfig.gamma); + } + + fpsbuff[0] = 0; + + // make sure we are in correct mode + vidResetMode(); + oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc; + find_combos(); + + // pal/ntsc might have changed, reset related stuff + target_fps = Pico.m.pal ? 50 : 60; + target_frametime = 1000000/target_fps; + reset_timing = 1; + + // prepare sound stuff + if(currentConfig.EmuOpt & 4) { + if(PsndRate != PsndRate_old || (PicoOpt&0x20b) != (PicoOpt_old&0x20b) || Pico.m.pal != pal_old || crashed_940) { + /* if 940 is turned off, we need it to be put back to sleep */ + if (!(PicoOpt&0x200) && ((PicoOpt^PicoOpt_old)&0x200)) { + Reset940(1); + Pause940(1); + } + sound_rerate(); + } + //excess_samples = PsndRate - PsndLen*target_fps; + snd_excess_cnt = 0; + snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps; + printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n", PsndRate, PsndLen, snd_excess_add, (PicoOpt&8)>>3, Pico.m.pal); + gp2x_start_sound(PsndRate, 16, (PicoOpt&8)>>3); + gp2x_sound_volume(currentConfig.volume, currentConfig.volume); + PicoWriteSound = updateSound; + PsndOut = calloc((PicoOpt&8) ? (PsndLen*4+4) : (PsndLen*2+2), 1); + PsndRate_old = PsndRate; + PsndLen_real = PsndLen; + PicoOpt_old = PicoOpt; + pal_old = Pico.m.pal; + } else { + PsndOut = 0; + } + + // loop? + while (engineState == PGS_Running) + { + int modes; + + gettimeofday(&tval, 0); + if(reset_timing) { + reset_timing = 0; + thissec = tval.tv_sec; + frames_shown = frames_done = tval.tv_usec/target_frametime; + } + + // show notice message? + if(noticeMsgTime.tv_sec) { + static int noticeMsgSum; + if((tval.tv_sec*1000000+tval.tv_usec) - (noticeMsgTime.tv_sec*1000000+noticeMsgTime.tv_usec) > 2000000) { // > 2.0 sec + noticeMsgTime.tv_sec = noticeMsgTime.tv_usec = 0; + clearArea(0); + notice = 0; + } else { + int sum = noticeMsg[0]+noticeMsg[1]+noticeMsg[2]; + if (sum != noticeMsgSum) { clearArea(0); noticeMsgSum = sum; } + notice = noticeMsg; + } + } + + // check for mode changes + modes = ((Pico.video.reg[12]&1)<<2)|(Pico.video.reg[1]&8); + if (modes != oldmodes) { + int scalex = 320; + osd_fps_x = OSD_FPS_X; + if (modes & 4) { + vidCpyM2 = vidCpyM2_40col; + } else { + if (PicoOpt & 0x100) { + vidCpyM2 = vidCpyM2_32col_nobord; + scalex = 256; + osd_fps_x = OSD_FPS_X - 64; + } else { + vidCpyM2 = vidCpyM2_32col; + } + } + gp2x_video_RGB_setscaling(scalex, 240); + oldmodes = modes; + clearArea(1); + } + + // second changed? + if(thissec != tval.tv_sec) { +#ifdef BENCHMARK + static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4]; + if(++bench == 10) { + bench = 0; + bench_fps_s = bench_fps; + bf[bfp++ & 3] = bench_fps; + bench_fps = 0; + } + bench_fps += frames_shown; + sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2); +#else + if(currentConfig.EmuOpt & 2) + sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done); +#endif + thissec = tval.tv_sec; + + if(PsndOut == 0 && currentConfig.Frameskip >= 0) { + frames_done = frames_shown = 0; + } else { + // it is quite common for this implementation to leave 1 fame unfinished + // when second changes, but we don't want buffer to starve. + if(PsndOut && frames_done < target_fps && frames_done > target_fps-5) { + updateKeys(); + SkipFrame(1); frames_done++; + } + + frames_done -= target_fps; if (frames_done < 0) frames_done = 0; + frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0; + if (frames_shown > frames_done) frames_shown = frames_done; + } + } + + lim_time = (frames_done+1) * target_frametime; + if(currentConfig.Frameskip >= 0) { // frameskip enabled + for(i = 0; i < currentConfig.Frameskip; i++) { + updateKeys(); + SkipFrame(1); frames_done++; + if (PsndOut) { // do framelimitting if sound is enabled + gettimeofday(&tval, 0); + if(thissec != tval.tv_sec) tval.tv_usec+=1000000; + if(tval.tv_usec < lim_time) { // we are too fast + simpleWait(thissec, lim_time); + } + } + lim_time += target_frametime; + } + } else if(tval.tv_usec > lim_time) { // auto frameskip + // no time left for this frame - skip + updateKeys(); + SkipFrame(tval.tv_usec < lim_time+target_frametime); frames_done++; + continue; + } + + updateKeys(); + PicoFrame(); + + // check time + gettimeofday(&tval, 0); + if(thissec != tval.tv_sec) tval.tv_usec+=1000000; + + // sleep if we are still too fast + if(PsndOut != 0 || currentConfig.Frameskip < 0) + { + // usleep sleeps for ~20ms minimum, so it is not a solution here + gettimeofday(&tval, 0); + if(thissec != tval.tv_sec) tval.tv_usec+=1000000; + if(tval.tv_usec < lim_time) + { + // we are too fast + simpleWait(thissec, lim_time); + } + } + + blit(fpsbuff, notice); + + frames_done++; frames_shown++; + } + + // save SRAM + if((currentConfig.EmuOpt & 1) && SRam.changed) { + emu_SaveLoadGame(0, 1); + SRam.changed = 0; + } + + if (PsndOut != 0) { + free(PsndOut); + PsndOut = 0; + } +} + + +void emu_ResetGame(void) +{ + PicoReset(0); + reset_timing = 1; +} + + +size_t gzRead2(void *p, size_t _size, size_t _n, void *file) +{ + return gzread(file, p, _n); +} + + +size_t gzWrite2(void *p, size_t _size, size_t _n, void *file) +{ + return gzwrite(file, p, _n); +} + +typedef unsigned int (*STATE_SL_FUNC)(void *, unsigned int, unsigned int, void *); + +int emu_SaveLoadGame(int load, int sram) +{ + int ret = 0; + char saveFname[512]; + + // make save filename + romfname_ext(saveFname, ""); + if(sram) strcat(saveFname, ".srm"); + else { + if(state_slot > 0 && state_slot < 10) sprintf(saveFname, "%s.%i", saveFname, state_slot); + strcat(saveFname, ".mds"); + } + + printf("saveLoad (%i, %i): %s\n", load, sram, saveFname); + + if(sram) { + FILE *sramFile; + int sram_size = SRam.end-SRam.start+1; + if(SRam.reg_back & 4) sram_size=0x2000; + if(!SRam.data) return 0; // SRam forcefully disabled for this game + if(load) { + sramFile = fopen(saveFname, "rb"); + if(!sramFile) return -1; + fread(SRam.data, 1, sram_size, sramFile); + fclose(sramFile); + } else { + // sram save needs some special processing + // see if we have anything to save + for(; sram_size > 0; sram_size--) + if(SRam.data[sram_size-1]) break; + + if(sram_size) { + sramFile = fopen(saveFname, "wb"); + ret = fwrite(SRam.data, 1, sram_size, sramFile); + ret = (ret != sram_size) ? -1 : 0; + fclose(sramFile); + sync(); + } + } + return ret; + } else { + void *PmovFile = NULL; + // try gzip first + if(currentConfig.EmuOpt & 8) { + strcat(saveFname, ".gz"); + if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) { + areaRead = gzRead2; + areaWrite = gzWrite2; + if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY); + } else + saveFname[strlen(saveFname)-3] = 0; + } + if(!PmovFile) { // gzip failed or was disabled + if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) { + areaRead = (STATE_SL_FUNC) fread; + areaWrite = (STATE_SL_FUNC) fwrite; + } + } + if(PmovFile) { + PmovState(load ? 6 : 5, PmovFile); + strcpy(noticeMsg, load ? "GAME LOADED " : "GAME SAVED "); + if(areaRead == gzRead2) + gzclose(PmovFile); + else fclose ((FILE *) PmovFile); + PmovFile = 0; + if (!load) sync(); + else Pico.m.dirtyPal=1; + } else { + strcpy(noticeMsg, load ? "LOAD FAILED " : "SAVE FAILED "); + ret = -1; + } + + gettimeofday(¬iceMsgTime, 0); + return ret; + } +} diff --git a/gp2x/emu.h b/gp2x/emu.h new file mode 100644 index 0000000..33b1ae7 --- /dev/null +++ b/gp2x/emu.h @@ -0,0 +1,46 @@ +// (c) Copyright 2006 notaz, All rights reserved. +// Free for non-commercial use. + +// For commercial use, separate licencing terms must be obtained. + + + +// engine states +enum TPicoGameState { + PGS_Paused = 1, + PGS_Running, + PGS_Quit, + PGS_KeyConfig, + PGS_ReloadRom, + PGS_Menu, +}; + +typedef struct { + char lastRomFile[512]; + int EmuOpt; // LSb->MSb: use_sram, show_fps, enable_sound, gzip_saves, + // squidgehack, save_cfg_on_exit, , 16_bit_mode + // craigix_ram, confirm_save + int PicoOpt; // used for config saving only, see Pico.h + int PsndRate; // ditto + int PicoRegion; // ditto + int Frameskip; + int CPUclock; + int KeyBinds[32]; + int volume; + int gamma; + int JoyBinds[4][32]; +} currentConfig_t; + +extern char romFileName[]; +extern int engineState; +extern currentConfig_t currentConfig; + + +int emu_ReloadRom(void); +void emu_Init(void); +void emu_Deinit(void); +int emu_SaveLoadGame(int load, int sram); +void emu_Loop(void); +void emu_ResetGame(void); +int emu_ReadConfig(int game); +int emu_WriteConfig(int game); diff --git a/gp2x/gp2x.c b/gp2x/gp2x.c new file mode 100644 index 0000000..84e5f7e --- /dev/null +++ b/gp2x/gp2x.c @@ -0,0 +1,311 @@ +/** + * All this is mostly based on rlyeh's minimal library. + * Copied here to review all his code and understand what's going on. +**/ + +/* + + GP2X minimal library v0.A by rlyeh, (c) 2005. emulnation.info@rlyeh (swap it!) + + 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. + +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gp2x.h" +#include "usbjoy.h" + +volatile unsigned short *gp2x_memregs; +//static +volatile unsigned long *gp2x_memregl; +static void *gp2x_screens[4]; +static int screensel = 0; +//static +int memdev = 0; +static int sounddev = 0, mixerdev = 0; + +void *gp2x_screen; + +#define FRAMEBUFF_ADDR0 0x4000000-640*480 +#define FRAMEBUFF_ADDR1 0x4000000-640*480*2 +#define FRAMEBUFF_ADDR2 0x4000000-640*480*3 +#define FRAMEBUFF_ADDR3 0x4000000-640*480*4 + +static const int gp2x_screenaddrs[] = { FRAMEBUFF_ADDR0, FRAMEBUFF_ADDR1, FRAMEBUFF_ADDR2, FRAMEBUFF_ADDR3 }; + + +/* video stuff */ +void gp2x_video_flip(void) +{ + unsigned int address = gp2x_screenaddrs[screensel&3]; + + /* test */ +/* { + int i; char *p=gp2x_screen; + for (i=0; i < 240; i++) { memset(p+i*320, 0, 32); } + }*/ + + gp2x_memregs[0x290E>>1]=(unsigned short)(address); + gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16); + gp2x_memregs[0x2912>>1]=(unsigned short)(address); + gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16); + + // jump to other buffer: + gp2x_screen = gp2x_screens[++screensel&3]; +} + + +void gp2x_video_changemode(int bpp) +{ + 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*/ + + gp2x_memset_all_buffers(0, 0, 640*480); + gp2x_video_flip(); +} + + +void gp2x_video_setpalette(int *pal, int len) +{ + unsigned short *g=(unsigned short *)pal; + volatile unsigned short *memreg = &gp2x_memregs[0x295A>>1]; + gp2x_memregs[0x2958>>1] = 0; + + len *= 2; + while(len--) *memreg=*g++; +} + + +// TV Compatible function // +void gp2x_video_RGB_setscaling(int W, int H) +{ + float escalaw, escalah; + int bpp = (gp2x_memregs[0x28DA>>1]>>9)&0x3; + + escalaw = 1024.0; // RGB Horiz LCD + escalah = 320.0; // RGB Vert LCD + + if(gp2x_memregs[0x2800>>1]&0x100) //TV-Out + { + escalaw=489.0; // RGB Horiz TV (PAL, NTSC) + if (gp2x_memregs[0x2818>>1] == 287) //PAL + escalah=274.0; // RGB Vert TV PAL + else if (gp2x_memregs[0x2818>>1] == 239) //NTSC + escalah=331.0; // RGB Vert TV NTSC + } + + // scale horizontal + gp2x_memregs[0x2906>>1]=(unsigned short)((float)escalaw *(W/320.0)); + // scale vertical + gp2x_memregl[0x2908>>2]=(unsigned long)((float)escalah *bpp *(H/240.0)); +} + + +/* LCD updates @ 80Hz? */ +void gp2x_video_wait_vsync(void) +{ + gp2x_memregs[0x2846>>1] = 0x20|2; //(gp2x_memregs[0x2846>>1] | 0x20) & ~2; + while(!(gp2x_memregs[0x2846>>1] & 2));// usleep(1); +} + + +void gp2x_memcpy_all_buffers(void *data, int offset, int len) +{ + memcpy((char *)gp2x_screens[0] + offset, data, len); + memcpy((char *)gp2x_screens[1] + offset, data, len); + memcpy((char *)gp2x_screens[2] + offset, data, len); + memcpy((char *)gp2x_screens[3] + offset, data, len); +} + + +void gp2x_memset_all_buffers(int offset, int byte, int len) +{ + memset((char *)gp2x_screens[0] + offset, byte, len); + memset((char *)gp2x_screens[1] + offset, byte, len); + memset((char *)gp2x_screens[2] + offset, byte, len); + memset((char *)gp2x_screens[3] + offset, byte, len); +} + + +unsigned long gp2x_joystick_read(int allow_usb_joy) +{ + int i; + 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; + value = ~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16)); + + if (allow_usb_joy && num_of_joys > 0) { + // check the usb joy as well.. + gp2x_usbjoy_update(); + for (i = 0; i < num_of_joys; i++) + value |= gp2x_usbjoy_check(i); + } + + return value; +} + +static int s_oldrate = 0, s_oldbits = 0, s_oldstereo = 0; + +void gp2x_start_sound(int rate, int bits, int stereo) +{ + int frag = 0, bsize, buffers; + + // if no settings change, we don't need to do anything + if (rate == s_oldrate && s_oldbits == bits && s_oldstereo == stereo) return; + + if (sounddev > 0) close(sounddev); + sounddev = open("/dev/dsp", O_WRONLY|O_ASYNC); + if (sounddev == -1) + printf("open(\"/dev/dsp\") failed with %i\n", errno); + + ioctl(sounddev, SNDCTL_DSP_SPEED, &rate); + ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits); + ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo); + // calculate buffer size + buffers = 16; + bsize = rate / 32; + if (rate > 22050) { bsize*=4; buffers*=2; } // 44k mode seems to be very demanding + while ((bsize>>=1)) frag++; + frag |= buffers<<16; // 16 buffers + ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag); + printf("gp2x_set_sound: %i/%ibit/%s, %i buffers of %i bytes\n", + rate, bits, stereo?"stereo":"mono", frag>>16, 1<<(frag&0xffff)); + + s_oldrate = rate; s_oldbits = bits; s_oldstereo = stereo; + usleep(100000); +} + + +void gp2x_sound_write(void *buff, int len) +{ + write(sounddev, buff, len); +} + + +void gp2x_sound_volume(int l, int r) +{ + l=l<0?0:l; l=l>255?255:l; r=r<0?0:r; r=r>255?255:r; + l<<=8; l|=r; + ioctl(mixerdev, SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/ +} + + +/* 940 */ +void Pause940(int yes) +{ + if(yes) + gp2x_memregs[0x0904>>1] &= 0xFFFE; + else + gp2x_memregs[0x0904>>1] |= 1; +} + + +void Reset940(int yes) +{ + gp2x_memregs[0x3B48>>1] = ((yes&1) << 7) | (0x03); /* bank=3 */ +} + + + +/* common */ +void gp2x_init(void) +{ + printf("entering init()\n"); fflush(stdout); + + memdev = open("/dev/mem", O_RDWR); + if (memdev == -1) + { + printf("open(\"/dev/mem\") failed with %i\n", errno); + exit(1); + } + + gp2x_memregs = mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000); + printf("memregs are @ %p\n", gp2x_memregs); + if(gp2x_memregs == MAP_FAILED) + { + printf("mmap(memregs) failed with %i\n", errno); + exit(1); + } + gp2x_memregl = (unsigned long *) gp2x_memregs; + + gp2x_screens[3] = mmap(0, 640*480*4, PROT_WRITE, MAP_SHARED, memdev, FRAMEBUFF_ADDR3); + if(gp2x_screens[3] == MAP_FAILED) + { + printf("mmap(gp2x_screen) failed with %i\n", errno); + exit(1); + } + printf("framebuffers point to %p\n", gp2x_screens[3]); + gp2x_screens[2] = (char *) gp2x_screens[3]+640*480; + gp2x_screens[1] = (char *) gp2x_screens[2]+640*480; + gp2x_screens[0] = (char *) gp2x_screens[1]+640*480; + + gp2x_screen = gp2x_screens[0]; + screensel = 0; + + // snd + mixerdev = open("/dev/mixer", O_RDWR); + if (mixerdev == -1) + printf("open(\"/dev/mixer\") failed with %i\n", errno); + + /* init usb joys -GnoStiC */ + gp2x_usbjoy_init(); + + printf("exitting init()\n"); fflush(stdout); +} + +char *ext_menu = 0, *ext_state = 0; + +void gp2x_deinit(void) +{ + Reset940(1); + Pause940(1); + + gp2x_video_changemode(15); + munmap(gp2x_screens[0], 640*480*4); + munmap((void *)gp2x_memregs, 0x10000); + close(memdev); + close(mixerdev); + if (sounddev > 0) close(sounddev); + + gp2x_usbjoy_deinit(); + + printf("all done, running "); + + // Zaq121's alternative frontend support from MAME + if(ext_menu && ext_state) { + printf("%s -state %s\n", ext_menu, ext_state); + execl(ext_menu, ext_menu, "-state", ext_state, NULL); + } else if(ext_menu) { + printf("%s\n", ext_menu); + execl(ext_menu, ext_menu, NULL); + } else { + printf("gp2xmenu\n"); + chdir("/usr/gp2x"); + execl("gp2xmenu", "gp2xmenu", NULL); + } +} + + diff --git a/gp2x/gp2x.h b/gp2x/gp2x.h new file mode 100644 index 0000000..113f674 --- /dev/null +++ b/gp2x/gp2x.h @@ -0,0 +1,40 @@ + +#ifndef __GP2X_H__ +#define __GP2X_H__ + + +void gp2x_init(void); +void gp2x_deinit(void); + +/* video */ +void gp2x_video_flip(void); +void gp2x_video_changemode(int bpp); +void gp2x_video_setpalette(int *pal, int len); +void gp2x_video_RGB_setscaling(int W, int H); +void gp2x_video_wait_vsync(void); +void gp2x_memcpy_all_buffers(void *data, int offset, int len); +void gp2x_memset_all_buffers(int offset, int byte, int len); + +/* sound */ +void gp2x_start_sound(int rate, int bits, int stereo); +void gp2x_sound_write(void *buff, int len); +void gp2x_sound_volume(int l, int r); + +/* joy */ +unsigned long gp2x_joystick_read(int allow_usb_joy); + +/* 940 core */ +void Pause940(int yes); +void Reset940(int yes); + + +extern void *gp2x_screen; +extern int memdev; + + +enum { GP2X_UP=0x1, GP2X_LEFT=0x4, GP2X_DOWN=0x10, GP2X_RIGHT=0x40, + GP2X_START=1<<8, GP2X_SELECT=1<<9, GP2X_L=1<<10, GP2X_R=1<<11, + GP2X_A=1<<12, GP2X_B=1<<13, GP2X_X=1<<14, GP2X_Y=1<<15, + GP2X_VOL_UP=1<<23, GP2X_VOL_DOWN=1<<22, GP2X_PUSH=1<<27 }; + +#endif diff --git a/gp2x/main.c b/gp2x/main.c new file mode 100644 index 0000000..f21ebd7 --- /dev/null +++ b/gp2x/main.c @@ -0,0 +1,143 @@ +// (c) Copyright 2006 notaz, All rights reserved. +// Free for non-commercial use. + +// For commercial use, separate licencing terms must be obtained. + +#include +#include +#include +#include +#include + +#include "gp2x.h" +#include "menu.h" +#include "emu.h" +#include "version.h" + +#include "squidgehack.h" +#include "cpuctrl.h" + + +extern char *ext_menu, *ext_state; +extern int select_exits; +extern char *PicoConfigFile; +int mmuhack_status = 0; +char **g_argv; + +void parse_cmd_line(int argc, char *argv[]) +{ + int x, unrecognized = 0; + + for(x = 1; x < argc; x++) + { + if(argv[x][0] == '-') + { + if(strcasecmp(argv[x], "-menu") == 0) { + if(x+1 < argc) { ++x; ext_menu = argv[x]; } /* External Frontend: Program Name */ + } + else if(strcasecmp(argv[x], "-state") == 0) { + if(x+1 < argc) { ++x; ext_state = argv[x]; } /* External Frontend: Arguments */ + } + else if(strcasecmp(argv[x], "-config") == 0) { + if(x+1 < argc) { ++x; PicoConfigFile = argv[x]; } + } + else if(strcasecmp(argv[x], "-selectexit") == 0) { + select_exits = 1; + } + else { + unrecognized = 1; + break; + } + } else { + /* External Frontend: ROM Name */ + FILE *f; + strncpy(romFileName, argv[x], PATH_MAX); + romFileName[PATH_MAX-1] = 0; + f = fopen(romFileName, "rb"); + if (f) fclose(f); + else unrecognized = 1; + engineState = PGS_ReloadRom; + break; + } + } + + if (unrecognized) { + printf("\n\n\nPicoDrive v" VERSION " (c) notaz, 2006\n"); + printf("usage: %s [options] [romfile]\n", argv[0]); + printf( "options:\n" + "-menu launch a custom program on exit instead of default gp2xmenu\n" + "-state pass '-state param' to the menu program\n" + "-config use specified config file instead of default 'picoconfig.bin'\n" + " see currentConfig_t structure in emu.h for the file format\n" + "-selectexit pressing SELECT will exit the emu and start 'menu_path'\n"); + } +} + + +int main(int argc, char *argv[]) +{ + g_argv = argv; + + emu_ReadConfig(0); + gp2x_init(); + if (currentConfig.EmuOpt&0x10) { + int ret = mmuhack(); + printf("squidge hack code finished and returned %i\n", ret); fflush(stdout); + mmuhack_status = ret; + } + cpuctrl_init(); + Reset940(1); + Pause940(1); + if (currentConfig.EmuOpt&0x100) { + printf("setting RAM timings.. "); fflush(stdout); + // craigix: --trc 6 --tras 4 --twr 1 --tmrd 1 --trfc 1 --trp 2 --trcd 2 + set_RAM_Timings(6, 4, 1, 1, 1, 2, 2); + printf("done.\n"); fflush(stdout); + } + emu_Init(); + + engineState = PGS_Menu; + + if (argc > 1) + parse_cmd_line(argc, argv); + + for (;;) + { + switch (engineState) + { + case PGS_Menu: + menu_loop(); + break; + + case PGS_ReloadRom: + if (emu_ReloadRom()) + engineState = PGS_Running; + else { + printf("PGS_ReloadRom == 0\n"); + engineState = PGS_Menu; + } + break; + + case PGS_Running: + emu_Loop(); + break; + + case PGS_Quit: + goto endloop; + + default: + printf("engine got into unknown state (%i), exitting\n", engineState); + goto endloop; + } + } + + endloop: + + emu_Deinit(); + cpuctrl_deinit(); + gp2x_deinit(); + if(mmuhack_status) + mmuunhack(); + + return 0; +} diff --git a/gp2x/menu.c b/gp2x/menu.c new file mode 100644 index 0000000..6336126 --- /dev/null +++ b/gp2x/menu.c @@ -0,0 +1,992 @@ +// (c) Copyright 2006 notaz, All rights reserved. +// Free for non-commercial use. + +// For commercial use, separate licencing terms must be obtained. + +#include +#include +#include +#include +#include +#include + +#include "gp2x.h" +#include "emu.h" +#include "menu.h" +#include "usbjoy.h" +#include "version.h" + +#include "Pico/PicoInt.h" + +#ifndef _DIRENT_HAVE_D_TYPE +#error "need d_type for file browser +#endif + +extern char *actionNames[]; +extern char romFileName[PATH_MAX]; +extern char *rom_data; +extern int mmuhack_status; +extern int state_slot; + +static char *gp2xKeyNames[] = { + "UP", "???", "LEFT", "???", "DOWN", "???", "RIGHT", "???", + "START", "SELECT", "L", "R", "A", "B", "X", "Y", + "???", "???", "???", "???", "???", "???", "VOL DOWN", "VOL UP", + "???", "???", "???", "PUSH", "???", "???", "???", "???" +}; + +char menuErrorMsg[40] = {0, }; + + +static unsigned char fontdata8x8[] = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C, + 0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00, + 0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00, + 0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00, + 0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00, + 0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40, + 0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00, + 0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00, + 0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00, + 0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00, + 0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00, + 0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00, + 0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00, + 0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00, + 0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00, + 0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00, + 0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00, + 0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00, + 0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00, + 0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00, + 0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30, + 0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00, + 0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00, + 0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00, + 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00, + 0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00, + 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00, + 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00, + 0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00, + 0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00, + 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, + 0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00, + 0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00, + 0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, + 0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00, + 0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00, + 0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00, + 0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00, + 0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF, + 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00, + 0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00, + 0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00, + 0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C, + 0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00, + 0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00, + 0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00, + 0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00, + 0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06, + 0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00, + 0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00, + 0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00, + 0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C, + 0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00, + 0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00, + 0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00, +}; + +static void gp2x_text(unsigned char *screen, int x, int y, char *text, int color) +{ + int i,l; + + screen = screen + x + y*320; + + for (i = 0; i < strlen(text); i++) + { + for (l=0;l<8;l++) + { + if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=color; + if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=color; + if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=color; + if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=color; + if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=color; + if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=color; + if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=color; + if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=color; + } + screen += 8; + } +} + +// draws white text to current bbp15 screen +void gp2x_text_out15(int x, int y, char *text) +{ + int i,l; + unsigned short *screen = gp2x_screen; + + screen = screen + x + y*320; + + for (i = 0; i < strlen(text); i++) + { + for (l=0;l<8;l++) + { + if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=0xffff; + if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=0xffff; + if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=0xffff; + if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=0xffff; + if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=0xffff; + if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=0xffff; + if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=0xffff; + if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=0xffff; + } + screen += 8; + } +} + + +void gp2x_text_out8(int x, int y, char *texto, ...) +{ + va_list args; + char buffer[512]; + + va_start(args,texto); + vsprintf(buffer,texto,args); + va_end(args); + + gp2x_text(gp2x_screen,x,y,buffer,1); +} + + +void gp2x_text_out8_2(int x, int y, char *texto, int color) +{ + gp2x_text(gp2x_screen, x, y, texto, color); +} + +void gp2x_text_out8_lim(int x, int y, char *texto, int max) +{ + char buffer[320/8+1]; + + strncpy(buffer, texto, 320/8); + if (max > 320/8) max = 320/8; + if (max < 0) max = 0; + buffer[max] = 0; + + gp2x_text(gp2x_screen,x,y,buffer,1); +} + + +static unsigned long inp_prev = 0; +static int inp_prevjoy = 0; + +static unsigned long wait_for_input(unsigned long interesting) +{ + unsigned long ret; + static int repeats = 0, wait = 300*1000; + int release = 0, i; + + if (repeats == 5 || repeats == 15 || repeats == 30) wait /= 2; + + for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) { + if(i == 0) repeats++; + usleep(wait/6); + } + + while ( !((ret = gp2x_joystick_read(1)) & interesting) ) { + usleep(50000); + release = 1; + } + + if (release || ret != inp_prev) { + repeats = 0; + wait = 300*1000; + } + inp_prev = ret; + inp_prevjoy = 0; + + // we don't need diagonals in menus + if ((ret&GP2X_UP) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT; + if ((ret&GP2X_UP) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT; + if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT; + if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT; + + return ret; +} + +static unsigned long input2_read(unsigned long interesting, int *joy) +{ + unsigned long ret; + int i; + + do + { + *joy = 0; + if ((ret = gp2x_joystick_read(0) & interesting)) break; + gp2x_usbjoy_update(); + for (i = 0; i < num_of_joys; i++) { + ret = gp2x_usbjoy_check2(i); + if (ret) { *joy = i + 1; break; } + } + if (ret) break; + } + while(0); + + return ret; +} + +// similar to wait_for_input(), but returns joy num +static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy) +{ + unsigned long ret; + const int wait = 300*1000; + int i; + + if (inp_prevjoy == 0) inp_prev &= interesting; + for (i = 0; i < 6; i++) { + ret = input2_read(interesting, joy); + if (*joy != inp_prevjoy || ret != inp_prev) break; + usleep(wait/6); + } + + while ( !(ret = input2_read(interesting, joy)) ) { + usleep(50000); + } + + inp_prev = ret; + inp_prevjoy = *joy; + + return ret; +} + + + +// -------------- ROM selector -------------- + +static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel) +{ + int start, i, pos; + + start = 12 - sel; + n--; // exclude current dir (".") + + memset(gp2x_screen, 0, 320*240); + + if(start - 2 >= 0) + gp2x_text_out8_lim(14, (start - 2)*10, curdir, 38); + for (i = 0; i < n; i++) { + pos = start + i; + if (pos < 0) continue; + if (pos > 23) break; + if (namelist[i+1]->d_type == DT_DIR) { + gp2x_text_out8_lim(14, pos*10, "/", 1); + gp2x_text_out8_lim(14+8, pos*10, namelist[i+1]->d_name, 37); + } else { + gp2x_text_out8_lim(14, pos*10, namelist[i+1]->d_name, 38); + } + } + gp2x_text_out8(5, 120, ">"); + gp2x_video_flip(); +} + +static int scandir_cmp(const void *p1, const void *p2) +{ + struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2; + if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2); + if ((*d1)->d_type == DT_DIR) return -1; // put before + if ((*d2)->d_type == DT_DIR) return 1; + return alphasort(d1, d2); +} + + +static char *romsel_loop(char *curr_path) +{ + struct dirent **namelist; + DIR *dir; + int n, sel = 0; + unsigned long inp = 0; + char *ret = NULL, *fname = NULL; + + // is this a dir or a full path? + if ((dir = opendir(curr_path))) { + closedir(dir); + } else { + char *p; + for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--); + *p = 0; + fname = p+1; + } + + n = scandir(curr_path, &namelist, 0, scandir_cmp); + if (n < 0) { + // try root + n = scandir(curr_path, &namelist, 0, scandir_cmp); + if (n < 0) { + // oops, we failed + printf("dir: "); printf(curr_path); printf("\n"); + perror("scandir"); + return NULL; + } + } + + // try to find sel + if (fname != NULL) { + int i; + for (i = 1; i < n; i++) { + if (strcmp(namelist[i]->d_name, fname) == 0) { + sel = i - 1; + break; + } + } + } + + for (;;) + { + draw_dirlist(curr_path, namelist, n, sel); + inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X); + if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = n-2; } + if(inp & GP2X_DOWN) { sel++; if (sel > n-2) sel = 0; } + if(inp & GP2X_LEFT) { sel-=10; if (sel < 0) sel = 0; } + if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; } + if(inp & GP2X_B) { // enter dir/select + again: + if (namelist[sel+1]->d_type == DT_REG) { + strcpy(romFileName, curr_path); + strcat(romFileName, "/"); + strcat(romFileName, namelist[sel+1]->d_name); + ret = romFileName; + break; + } else if (namelist[sel+1]->d_type == DT_DIR) { + int newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2; + char *p, *newdir = malloc(newlen); + if (strcmp(namelist[sel+1]->d_name, "..") == 0) { + char *start = curr_path; + p = start + strlen(start) - 1; + while (*p == '/' && p > start) p--; + while (*p != '/' && p > start) p--; + if (p <= start) strcpy(newdir, "/"); + else { strncpy(newdir, start, p-start); newdir[p-start] = 0; } + } else { + strcpy(newdir, curr_path); + p = newdir + strlen(newdir) - 1; + while (*p == '/' && p >= newdir) *p-- = 0; + strcat(newdir, "/"); + strcat(newdir, namelist[sel+1]->d_name); + } + ret = romsel_loop(newdir); + free(newdir); + break; + } else { + // unknown file type, happens on NTFS mounts. Try to guess. + FILE *tstf; int tmp; + strcpy(romFileName, curr_path); + strcat(romFileName, "/"); + strcat(romFileName, namelist[sel+1]->d_name); + tstf = fopen(romFileName, "rb"); + if (tstf != NULL) + { + if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0) + namelist[sel+1]->d_type = DT_REG; + else namelist[sel+1]->d_type = DT_DIR; + fclose(tstf); + goto again; + } + } + } + if(inp & GP2X_X) break; // cancel + } + + if (n > 0) { + while(n--) free(namelist[n]); + free(namelist); + } + + return ret; +} + +// -------------- key config -------------- + +static char *usb_joy_key_name(int joy, int num) +{ + static char name[16]; + switch (num) + { + case 0: sprintf(name, "Joy%i UP", joy); break; + case 1: sprintf(name, "Joy%i DOWN", joy); break; + case 2: sprintf(name, "Joy%i LEFT", joy); break; + case 3: sprintf(name, "Joy%i RIGHT", joy); break; + default:sprintf(name, "Joy%i b%i", joy, num-3); break; + } + return name; +} + +static void draw_key_config(int curr_act, int is_p2) +{ + char strkeys[32*5]; + int joy, i; + + strkeys[0] = 0; + for (i = 0; i < 32; i++) + { + if (currentConfig.KeyBinds[i] & (1 << curr_act)) + { + if (curr_act < 16 && (currentConfig.KeyBinds[i] & (1 << 16)) != (is_p2 << 16)) continue; + if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gp2xKeyNames[i]); break; } + else strcpy(strkeys, gp2xKeyNames[i]); + } + } + for (joy = 0; joy < num_of_joys; joy++) + { + for (i = 0; i < 32; i++) + { + if (currentConfig.JoyBinds[joy][i] & (1 << curr_act)) + { + if (curr_act < 16 && (currentConfig.JoyBinds[joy][i] & (1 << 16)) != (is_p2 << 16)) continue; + if (strkeys[0]) { + strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i)); + break; + } + else strcpy(strkeys, usb_joy_key_name(joy + 1, i)); + } + } + } + + memset(gp2x_screen, 0, 320*240); + gp2x_text_out8(60, 40, "Action: %s", actionNames[curr_act]); + gp2x_text_out8(60, 60, "Keys: %s", strkeys); + + gp2x_text_out8(30, 180, "Use SELECT to change action"); + gp2x_text_out8(30, 190, "Press a key to bind/unbind"); + gp2x_text_out8(30, 200, "Select \"Done\" action and"); + gp2x_text_out8(30, 210, " press any key to finish"); + gp2x_video_flip(); +} + +static void key_config_loop(int is_p2) +{ + int curr_act = 0, joy = 0, i; + unsigned long inp = 0; + + for (;;) + { + draw_key_config(curr_act, is_p2); + inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS, &joy); + // printf("got %08lX from joy %i\n", inp, joy); + if (joy == 0) { + if (inp & GP2X_SELECT) { + curr_act++; + while (!actionNames[curr_act] && curr_act < 32) curr_act++; + if (curr_act > 31) curr_act = 0; + } + inp &= CONFIGURABLE_KEYS; + inp &= ~GP2X_SELECT; + } + if (curr_act == 31 && inp) break; + if (joy == 0) { + for (i = 0; i < 32; i++) + if (inp & (1 << i)) { + currentConfig.KeyBinds[i] ^= (1 << curr_act); + if (is_p2) currentConfig.KeyBinds[i] |= (1 << 16); // player 2 flag + else currentConfig.KeyBinds[i] &= ~(1 << 16); + } + } else { + for (i = 0; i < 32; i++) + if (inp & (1 << i)) { + currentConfig.JoyBinds[joy-1][i] ^= (1 << curr_act); + if (is_p2) currentConfig.JoyBinds[joy-1][i] |= (1 << 16); + else currentConfig.JoyBinds[joy-1][i] &= ~(1 << 16); + } + } + } +} + +static void draw_kc_sel(int menu_sel) +{ + int tl_x = 25+40, tl_y = 60, y, i; + char joyname[36]; + + y = tl_y; + memset(gp2x_screen, 0, 320*240); + gp2x_text_out8(tl_x, y, "Player 1"); + gp2x_text_out8(tl_x, (y+=10), "Player 2"); + gp2x_text_out8(tl_x, (y+=10), "Done"); + + // draw cursor + gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + + tl_x = 25; + gp2x_text_out8(tl_x, (y=110), "USB joys detected:"); + if (num_of_joys > 0) { + for (i = 0; i < num_of_joys; i++) { + strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0; + gp2x_text_out8(tl_x, (y+=10), "%i: %s", i+1, joyname); + } + } else { + gp2x_text_out8(tl_x, (y+=10), "none"); + } + + + gp2x_video_flip(); +} + +static void kc_sel_loop(void) +{ + int menu_sel = 2, menu_sel_max = 2; + unsigned long inp = 0; + + for(;;) + { + draw_kc_sel(menu_sel); + inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X); + if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; } + if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; } + if(inp & GP2X_B) { + switch (menu_sel) { + case 0: key_config_loop(0); return; + case 1: key_config_loop(1); return; + default: return; + } + } + if(inp & GP2X_X) return; + } +} + + + + +// --------- advanced options ---------- + +// order must match that of currentConfig_t +struct { + int EmuOpt; + int PicoOpt; + int PsndRate; + int PicoRegion; + int Frameskip; + int CPUclock; +} tmp_opts; +int tmp_gamma; + +static void draw_amenu_options(int menu_sel) +{ + int tl_x = 25, tl_y = 60, y; + char *mms = mmuhack_status ? "active) " : "inactive)"; + + y = tl_y; + memset(gp2x_screen, 0, 320*240); + gp2x_text_out8(tl_x, y, "Scale 32 column mode %s", (tmp_opts.PicoOpt&0x100)?"ON":"OFF"); // 0 + gp2x_text_out8(tl_x, (y+=10), "Gamma correction %i.%02i", tmp_gamma / 100, tmp_gamma%100); // 1 + gp2x_text_out8(tl_x, (y+=10), "Emulate Z80 %s", (tmp_opts.PicoOpt&0x004)?"ON":"OFF"); // 2 + gp2x_text_out8(tl_x, (y+=10), "Emulate YM2612 (FM) %s", (tmp_opts.PicoOpt&0x001)?"ON":"OFF"); // 3 + gp2x_text_out8(tl_x, (y+=10), "Emulate SN76496 (PSG) %s", (tmp_opts.PicoOpt&0x002)?"ON":"OFF"); // 4 + gp2x_text_out8(tl_x, (y+=10), "gzip savestates %s", (tmp_opts.EmuOpt &0x008)?"ON":"OFF"); // 5 + gp2x_text_out8(tl_x, (y+=10), "Don't save config on exit %s", (tmp_opts.EmuOpt &0x020)?"ON":"OFF"); // 6 + gp2x_text_out8(tl_x, (y+=10), "needs restart:"); + gp2x_text_out8(tl_x, (y+=10), "craigix's RAM timings %s", (tmp_opts.EmuOpt &0x100)?"ON":"OFF"); // 8 + gp2x_text_out8(tl_x, (y+=10), "squidgehack (now %s %s", mms, (tmp_opts.EmuOpt &0x010)?"ON":"OFF"); // 9 + gp2x_text_out8(tl_x, (y+=10), "Done"); + + // draw cursor + gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + + gp2x_video_flip(); +} + +static void amenu_loop_options(void) +{ + int menu_sel = 0, menu_sel_max = 11; + unsigned long inp = 0; + + for(;;) + { + draw_amenu_options(menu_sel); + inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A); + if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; } + if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; } + if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options + switch (menu_sel) { + case 0: tmp_opts.PicoOpt^=0x100; break; + case 2: tmp_opts.PicoOpt^=0x004; break; + case 3: tmp_opts.PicoOpt^=0x001; break; + case 4: tmp_opts.PicoOpt^=0x002; break; + case 5: tmp_opts.EmuOpt ^=0x008; break; + case 6: tmp_opts.EmuOpt ^=0x020; break; + case 8: tmp_opts.EmuOpt ^=0x100; break; + case 9: tmp_opts.EmuOpt ^=0x010; break; + case 10: return; + } + } + if(inp & (GP2X_X|GP2X_A)) return; + if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise + switch (menu_sel) { + case 1: + while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) { + tmp_gamma += (inp & GP2X_LEFT) ? -1 : 1; + if (tmp_gamma < 1) tmp_gamma = 1; + if (tmp_gamma > 300) tmp_gamma = 300; + draw_amenu_options(menu_sel); + usleep(18*1000); + } + break; + } + } + } +} + +// -------------- options -------------- + +static char *region_name(unsigned int code) +{ + char *names[] = { "Auto", "Japan NTSC", "Japan PAL", "USA", "Europe" }; + int i = 0; + code <<= 1; + while((code >>=1)) i++; + if (i > 4) return "unknown"; + return names[i]; +} + +static void draw_menu_options(int menu_sel) +{ + int tl_x = 25, tl_y = 40, y; + char monostereo[8], strframeskip[8], *strrend; + + strcpy(monostereo, (tmp_opts.PicoOpt&0x08)?"stereo":"mono"); + if (tmp_opts.Frameskip < 0) + strcpy(strframeskip, "Auto"); + else sprintf(strframeskip, "%i", tmp_opts.Frameskip); + if (tmp_opts.PicoOpt&0x10) { + strrend = " 8bit fast"; + } else if (tmp_opts.EmuOpt&0x80) { + strrend = "16bit accurate"; + } else { + strrend = " 8bit accurate"; + } + + y = tl_y; + memset(gp2x_screen, 0, 320*240); + gp2x_text_out8(tl_x, y, "Renderer: %s", strrend); // 0 + gp2x_text_out8(tl_x, (y+=10), "Accurate timing (slower) %s", (tmp_opts.PicoOpt&0x040)?"ON":"OFF"); // 1 + gp2x_text_out8(tl_x, (y+=10), "Accurate sprites (slower) %s", (tmp_opts.PicoOpt&0x080)?"ON":"OFF"); // 2 + gp2x_text_out8(tl_x, (y+=10), "Show FPS %s", (tmp_opts.EmuOpt &0x002)?"ON":"OFF"); // 3 + gp2x_text_out8(tl_x, (y+=10), "Frameskip %s", strframeskip); + gp2x_text_out8(tl_x, (y+=10), "Enable sound %s", (tmp_opts.EmuOpt &0x004)?"ON":"OFF"); // 5 + gp2x_text_out8(tl_x, (y+=10), "Sound Quality: %5iHz %s", tmp_opts.PsndRate, monostereo); + gp2x_text_out8(tl_x, (y+=10), "Use ARM940 core for sound %s", (tmp_opts.PicoOpt&0x200)?"ON":"OFF"); // 7 + gp2x_text_out8(tl_x, (y+=10), "6 button pad %s", (tmp_opts.PicoOpt&0x020)?"ON":"OFF"); // 8 + gp2x_text_out8(tl_x, (y+=10), "Genesis Region: %s", region_name(tmp_opts.PicoRegion)); + gp2x_text_out8(tl_x, (y+=10), "Use SRAM savestates %s", (tmp_opts.EmuOpt &0x001)?"ON":"OFF"); // 10 + gp2x_text_out8(tl_x, (y+=10), "Confirm save overwrites %s", (tmp_opts.EmuOpt &0x200)?"ON":"OFF"); // 11 + gp2x_text_out8(tl_x, (y+=10), "Save slot %i", state_slot); // 12 + gp2x_text_out8(tl_x, (y+=10), "GP2X CPU clocks %iMhz", tmp_opts.CPUclock); + gp2x_text_out8(tl_x, (y+=10), "[advanced options]"); + gp2x_text_out8(tl_x, (y+=10), "Save cfg as default"); + if (rom_data) + gp2x_text_out8(tl_x, (y+=10), "Save cfg for current game only"); + + // draw cursor + gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + + gp2x_video_flip(); +} + +static int sndrate_prevnext(int rate, int dir) +{ + int i, rates[] = { 8000, 11025, 16000, 22050, 44100 }; + + for (i = 0; i < 5; i++) + if (rates[i] == rate) break; + + i += dir ? 1 : -1; + if (i > 4) return dir ? 44100 : 22050; + if (i < 0) return dir ? 11025 : 8000; + return rates[i]; +} + +static void menu_options_save(void) +{ + memcpy(¤tConfig.EmuOpt, &tmp_opts.EmuOpt, sizeof(tmp_opts)); + currentConfig.gamma = tmp_gamma; + PicoOpt = currentConfig.PicoOpt; + PsndRate = currentConfig.PsndRate; + PicoRegionOverride = currentConfig.PicoRegion; + if (PicoOpt & 0x20) { + actionNames[ 8] = "Z"; actionNames[ 9] = "Y"; + actionNames[10] = "X"; actionNames[11] = "MODE"; + } else { + actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0; + } +} + +static void menu_loop_options(void) +{ + int menu_sel = 0, menu_sel_max = 15; + unsigned long inp = 0; + + if (rom_data) menu_sel_max++; + memcpy(&tmp_opts.EmuOpt, ¤tConfig.EmuOpt, sizeof(tmp_opts)); + tmp_gamma = currentConfig.gamma; + tmp_opts.PicoOpt = PicoOpt; + tmp_opts.PsndRate = PsndRate; + tmp_opts.PicoRegion = PicoRegionOverride; + + for(;;) + { + draw_menu_options(menu_sel); + inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A); + if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; } + if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; } + if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options + switch (menu_sel) { + case 1: tmp_opts.PicoOpt^=0x040; break; + case 2: tmp_opts.PicoOpt^=0x080; break; + case 3: tmp_opts.EmuOpt ^=0x002; break; + case 5: tmp_opts.EmuOpt ^=0x004; break; + case 7: tmp_opts.PicoOpt^=0x200; break; + case 8: tmp_opts.PicoOpt^=0x020; break; + case 10: tmp_opts.EmuOpt ^=0x001; break; + case 11: tmp_opts.EmuOpt ^=0x200; break; + case 14: amenu_loop_options(); break; + case 15: // done (save) + menu_options_save(); + emu_WriteConfig(0); + return; + case 16: // done (save for current game) + menu_options_save(); + emu_WriteConfig(1); + return; + } + } + if(inp & GP2X_X) return; // done (no save) + if(inp & GP2X_A) { + menu_options_save(); + return; // done (save) + } + if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise + switch (menu_sel) { + case 0: + if (inp & GP2X_LEFT) { + if ( tmp_opts.PicoOpt&0x10) tmp_opts.PicoOpt&= ~0x10; + else if (!(tmp_opts.EmuOpt &0x80))tmp_opts.EmuOpt |= 0x80; + else if ( tmp_opts.EmuOpt &0x80) break; + } else { + if ( tmp_opts.PicoOpt&0x10) break; + else if (!(tmp_opts.EmuOpt &0x80))tmp_opts.PicoOpt|= 0x10; + else if ( tmp_opts.EmuOpt &0x80) tmp_opts.EmuOpt &= ~0x80; + } + break; + case 4: + tmp_opts.Frameskip += (inp & GP2X_LEFT) ? -1 : 1; + if (tmp_opts.Frameskip < 0) tmp_opts.Frameskip = -1; + if (tmp_opts.Frameskip > 32) tmp_opts.Frameskip = 32; + break; + case 6: + if ((inp & GP2X_RIGHT) && tmp_opts.PsndRate == 44100 && !(tmp_opts.PicoOpt&0x08)) { + tmp_opts.PsndRate = 8000; tmp_opts.PicoOpt|= 0x08; + } else if ((inp & GP2X_LEFT) && tmp_opts.PsndRate == 8000 && (tmp_opts.PicoOpt&0x08)) { + tmp_opts.PsndRate = 44100; tmp_opts.PicoOpt&=~0x08; + } else tmp_opts.PsndRate = sndrate_prevnext(tmp_opts.PsndRate, inp & GP2X_RIGHT); + break; + case 9: + if (inp & GP2X_RIGHT) { + if (tmp_opts.PicoRegion) tmp_opts.PicoRegion<<=1; else tmp_opts.PicoRegion=1; + if (tmp_opts.PicoRegion > 8) tmp_opts.PicoRegion = 8; + } else tmp_opts.PicoRegion>>=1; + break; + case 12: + if (inp & GP2X_RIGHT) { + state_slot++; if (state_slot > 9) state_slot = 0; + } else {state_slot--; if (state_slot < 0) state_slot = 9; + } + break; + case 13: + while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) { + tmp_opts.CPUclock += (inp & GP2X_LEFT) ? -1 : 1; + if (tmp_opts.CPUclock < 1) tmp_opts.CPUclock = 1; + draw_menu_options(menu_sel); + usleep(50*1000); + } + break; + } + } + } +} + +// -------------- credits -------------- + +static void draw_menu_credits(void) +{ + int tl_x = 15, tl_y = 70, y; + memset(gp2x_screen, 0, 320*240); + + gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006"); + y = tl_y; + gp2x_text_out8(tl_x, y, "Credits:"); + gp2x_text_out8(tl_x, (y+=10), "Dave: Cyclone 68000 core,"); + gp2x_text_out8(tl_x, (y+=10), " base code of PicoDrive"); + gp2x_text_out8(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core"); + gp2x_text_out8(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores"); + gp2x_text_out8(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs"); + gp2x_text_out8(tl_x, (y+=10), "Stephane Dallongeville:"); + gp2x_text_out8(tl_x, (y+=10), " opensource Gens"); + gp2x_text_out8(tl_x, (y+=10), "Haze: Genesis hw info"); + gp2x_text_out8(tl_x, (y+=10), "rlyeh and others: minimal SDK"); + gp2x_text_out8(tl_x, (y+=10), "Squidge: squidgehack"); + gp2x_text_out8(tl_x, (y+=10), "Dzz: ARM940 sample"); + gp2x_text_out8(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick"); + gp2x_text_out8(tl_x, (y+=10), "craigix: GP2X hardware"); + + gp2x_video_flip(); +} + + +// -------------- root menu -------------- + +static void draw_menu_root(int menu_sel) +{ + int tl_x = 70, tl_y = 70, y; + memset(gp2x_screen, 0, 320*240); + + gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION); + + y = tl_y; + if (rom_data) { + gp2x_text_out8(tl_x, y, "Resume game"); + gp2x_text_out8(tl_x, (y+=10), "Save State"); + gp2x_text_out8(tl_x, (y+=10), "Load State"); + gp2x_text_out8(tl_x, (y+=10), "Reset game"); + } else { + y += 30; + } + gp2x_text_out8(tl_x, (y+=10), "Load new ROM"); + gp2x_text_out8(tl_x, (y+=10), "Change options"); + gp2x_text_out8(tl_x, (y+=10), "Configure controls"); + gp2x_text_out8(tl_x, (y+=10), "Credits"); + gp2x_text_out8(tl_x, (y+=10), "Exit"); + + // draw cursor + gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">"); + // error + if (menuErrorMsg[0]) gp2x_text_out8(5, 226, menuErrorMsg); + gp2x_video_flip(); +} + + +static void menu_loop_root(void) +{ + int menu_sel = 4, menu_sel_max = 8, menu_sel_min = 4; + unsigned long inp = 0; + char curr_path[PATH_MAX], *selfname; + FILE *tstf; + + if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) ) + { + fclose(tstf); + strcpy(curr_path, currentConfig.lastRomFile); + } + else + { + getcwd(curr_path, PATH_MAX); + } + + if (rom_data) menu_sel = menu_sel_min = 0; + + for(;;) + { + draw_menu_root(menu_sel); + inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT); + if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < menu_sel_min) menu_sel = menu_sel_max; } + if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = menu_sel_min; } + if(inp &(GP2X_SELECT|GP2X_X)){ + if (rom_data) { + while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released + engineState = PGS_Running; + break; + } + } + if(inp & GP2X_B ) { + switch (menu_sel) { + case 0: // resume game + if (rom_data) { engineState = PGS_Running; return; } + break; + case 1: // save state + if (rom_data) { + if(emu_SaveLoadGame(0, 0)) { + strcpy(menuErrorMsg, "save failed"); + continue; + } + engineState = PGS_Running; + return; + } + break; + case 2: // load state + if (rom_data) { + if(emu_SaveLoadGame(1, 0)) { + strcpy(menuErrorMsg, "load failed"); + continue; + } + engineState = PGS_Running; + return; + } + break; + case 3: // reset game + if (rom_data) { + emu_ResetGame(); + engineState = PGS_Running; + return; + } + break; + case 4: // select rom + selfname = romsel_loop(curr_path); + if (selfname) { + printf("selected file: %s\n", selfname); + strncpy(currentConfig.lastRomFile, selfname, sizeof(currentConfig.lastRomFile)-1); + currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0; + engineState = PGS_ReloadRom; + } + return; + case 5: // options + menu_loop_options(); + break; + case 6: // controls + kc_sel_loop(); + break; + case 7: // credits + draw_menu_credits(); + usleep(500*1000); + inp = wait_for_input(GP2X_B|GP2X_X); + break; + case 8: // exit + engineState = PGS_Quit; + return; + } + } + menuErrorMsg[0] = 0; // clear error msg + } +} + + +void menu_loop(void) +{ + int pal[2]; + + // switch to 8bpp + gp2x_video_changemode(8); + gp2x_video_RGB_setscaling(320, 240); + // set pal + pal[0] = 0; + pal[1] = 0x00ffffff; + gp2x_video_setpalette(pal, 2); + + menu_loop_root(); + + menuErrorMsg[0] = 0; +} diff --git a/gp2x/menu.h b/gp2x/menu.h new file mode 100644 index 0000000..197500e --- /dev/null +++ b/gp2x/menu.h @@ -0,0 +1,16 @@ +// (c) Copyright 2006 notaz, All rights reserved. +// Free for non-commercial use. + +// For commercial use, separate licencing terms must be obtained. + +extern char menuErrorMsg[40]; + +void gp2x_text_out8 (int x, int y, char *texto, ...); +void gp2x_text_out15 (int x, int y, char *text); +void gp2x_text_out8_2(int x, int y, char *texto, int color); +void menu_loop(void); + +#define CONFIGURABLE_KEYS \ + (GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_A|GP2X_B|GP2X_X|GP2X_Y| \ + GP2X_START|GP2X_SELECT|GP2X_L|GP2X_R|GP2X_PUSH|GP2X_VOL_UP|GP2X_VOL_DOWN) + diff --git a/gp2x/mmuhack.c b/gp2x/mmuhack.c new file mode 100644 index 0000000..c0c2189 --- /dev/null +++ b/gp2x/mmuhack.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include + +#define MMUHACK_MINOR 225 +#define DEVICE_NAME "mmuhack" + +#if __GNUC__ == 3 +#include +static const char __module_kernel_version_gcc3[] __attribute__((__used__)) __attribute__((section(".modinfo"))) = +"kernel_version=" UTS_RELEASE; +#endif + +static ssize_t mmuhack_open(struct inode *inode, struct file *filp) +{ + unsigned int *pgtable; + unsigned int *cpt; + int i, j; + int ttb; + int ret = -EFAULT; + + // get the pointer to the translation table base... + asm volatile( + "stmdb sp!, {r0}\n\t" + "mrc p15, 0, r0, c2, c0, 0\n\t" + "mov %0, r0\n\t" + "ldmia sp!, {r0}\n\t": "=r"(ttb) + ); + + pgtable = __va(ttb); + + for (i = 0; i < 4096; i ++) if ( (pgtable[i] & 3) == 1 ) { + cpt = __va(pgtable[i] & 0xfffffc00); + + for (j = 0; j < 256; j ++) {/* + if ( (cpt[j] & 0xfe00000f) == 0x02000002 ) { + // set C and B bits in upper 32MB memory area... + printk("Set C&B bits %08x\n",cpt[j]); + cpt[j] |= 0xFFC; + ret = 0; + } + */ + if (((cpt[j] & 0xff000000) == 0x02000000) && ((cpt[j] & 12)==0) ) + { + //printk("Set C&B bits %08x\n",cpt[j]); + cpt[j] |= 0xFFC; + } + //if ((a>=0x31 && a<=0x36) && ((cpt[i] & 12)==0)) + if (((cpt[j] & 0xff000000) == 0x03000000) && ((cpt[j] & 12)==0)) + { + //printk("Set C&B bits %08x\n",cpt[j]); + //printf("SDL c and b bits not set, overwriting\n"); + cpt[j] |= 0xFFC; + } + } + } + + // drain the write buffer and flush the tlb caches... + asm volatile( + "stmdb sp!, {r0}\n\t" + "mov r0, #0\n\t" + "mcr 15, 0, r0, cr7, cr10, 4\n\t" + "mcr 15, 0, r0, cr8, cr7, 0\n\t" + "ldmia sp!, {r0}\n\t" + ); + + if (ret == 0) + printk("MMU hack applied.\n"); + + return 0; +} + +static struct file_operations mmuhack_fops = { + owner: THIS_MODULE, + open: mmuhack_open, +}; + + +static struct miscdevice mmuhack = { + MMUHACK_MINOR, DEVICE_NAME, &mmuhack_fops +}; + +static int __init mmuhack_init(void) +{ + misc_register(&mmuhack); +/* + printk("MMSP2 MMU Hack module.\n"); +*/ + return 0; +} + +static void __exit mmuhack_exit(void) +{ + misc_deregister(&mmuhack); +/* + printk(KERN_ALERT "MMU Hack module removed.\n"); +*/ +} + +module_init(mmuhack_init); +module_exit(mmuhack_exit); diff --git a/gp2x/mmuhack.txt b/gp2x/mmuhack.txt new file mode 100644 index 0000000..207e09c --- /dev/null +++ b/gp2x/mmuhack.txt @@ -0,0 +1,4 @@ +Squidge's MMU Hack modularized. +Original code by Squidge. +Module by Annonymous? +Slightly modified by me to suit my need. diff --git a/gp2x/port_config.h b/gp2x/port_config.h new file mode 100644 index 0000000..239df01 --- /dev/null +++ b/gp2x/port_config.h @@ -0,0 +1,18 @@ +// port specific settings + +#ifndef PORT_CONFIG_H +#define PORT_CONFIG_H + +#define CPU_CALL + +// draw2.c +#define START_ROW 0 // which row of tiles to start rendering at? +#define END_ROW 28 // ..end + +// pico.c +#define CAN_HANDLE_240_LINES 1 + +//#define dprintf(f,...) printf(f"\n",##__VA_ARGS__) +#define dprintf(x...) + +#endif //PORT_CONFIG_H diff --git a/gp2x/port_config.s b/gp2x/port_config.s new file mode 100644 index 0000000..094ea3c --- /dev/null +++ b/gp2x/port_config.s @@ -0,0 +1,8 @@ +@ .equiv START_ROW, 1 +@ .equiv END_ROW, 27 +@ one row means 8 pixels. If above example was used, (27-1)*8=208 lines would be rendered. +.equiv START_ROW, 0 +.equiv END_ROW, 28 + +@ this should be set to one only for GP2X port +.equiv EXTERNAL_YM2612, 1 diff --git a/gp2x/squidgehack.c b/gp2x/squidgehack.c new file mode 100644 index 0000000..f831bd4 --- /dev/null +++ b/gp2x/squidgehack.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include + +extern char **g_argv; + +/* Call this MMU Hack kernel module after doing mmap, and before doing memset*/ +int mmuhack(void) +{ + char kocmd[1024]; + int i, mmufd = open("/dev/mmuhack", O_RDWR); + + if(mmufd < 0) { + strcpy(kocmd, "/sbin/insmod "); + strncpy(kocmd+13, g_argv[0], 1023-13); + kocmd[1023] = 0; + for (i = strlen(kocmd); i > 0; i--) + if (kocmd[i] == '/') { kocmd[i] = 0; break; } + strcat(kocmd, "/mmuhack.o"); + + printf("Installing NK's kernel module for Squidge MMU Hack (%s)...\n", kocmd); + system(kocmd); + mmufd = open("/dev/mmuhack", O_RDWR); + } + if(mmufd < 0) return 0; + + close(mmufd); + return 1; +} + + +/* Unload MMU Hack kernel module after closing all memory devices*/ +int mmuunhack(void) +{ + int ret; + printf("Removing NK's kernel module for Squidge MMU Hack... "); fflush(stdout); + ret = system("/sbin/rmmod mmuhack"); + printf("done (%i)\n", ret); + + return ret; +} diff --git a/gp2x/squidgehack.h b/gp2x/squidgehack.h new file mode 100644 index 0000000..a83c737 --- /dev/null +++ b/gp2x/squidgehack.h @@ -0,0 +1,7 @@ +#ifndef __MMUHACK__ +#define __MMUHACK__ + +extern int mmuhack(void); +extern int mmuunhack(void); + +#endif /* __MMUHACK__ */ diff --git a/gp2x/test.c b/gp2x/test.c new file mode 100644 index 0000000..c0d28cb --- /dev/null +++ b/gp2x/test.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include "gp2x.h" + +void spend_cycles(int c); + +int main(void) +{ + struct timeval tval; // timing + int thissec = 0, frames_done = 0; + + gp2x_init(); + + for (;;) + { + gettimeofday(&tval, 0); + + if(thissec != tval.tv_sec) + { + thissec = tval.tv_sec; + + printf("frames_done: %i\n", frames_done); + frames_done = 0; + } + + + //gp2x_video_wait_vsync(); + //usleep(1); // sleeps a minimum of ~20ms + //gp2x_video_flip(); // can be called ~430000 times/sec + spend_cycles(1000); + frames_done++; + } + +} + diff --git a/gp2x/usbjoy.c b/gp2x/usbjoy.c new file mode 100644 index 0000000..569f615 --- /dev/null +++ b/gp2x/usbjoy.c @@ -0,0 +1,424 @@ +/* Title: USB Joystick library + Version 0.2 + Written by Puck2099 (puck2099@gmail.com), (c) 2006. + + + If you use this library or a part of it, please, let it know. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include /* For the definition of NULL */ +#include // For Device open +#include +#include +#include +#include // For Device read + +#include +#include /* For the definition of PATH_MAX */ +#include + +#include "usbjoy.h" + + +/* + Function: joy_open + + Opens a USB joystick and fills its information. + + Parameters: + + joynumber - Joystick's identifier (0 reserved for GP2X's builtin Joystick). + + Returns: + + Filled usbjoy structure. + +*/ +struct usbjoy *joy_open(int joynumber) +{ + int fd; + char path [128]; + struct usbjoy * joy = NULL; + struct js_event event; + static char insmod_done = 0; + + // notaz: on my system I get unresolved input_* symbols, so have to 'insmod input' too + // also we should insmod only once, not on every joy_open() call. + if (!insmod_done) { + system ("insmod input"); + system ("insmod joydev"); // Loads joydev module + insmod_done = 1; + } + + if (joynumber == 0) { + } + else if (joynumber > 0) { + sprintf (path, "/dev/input/js%d", joynumber-1); + fd = open(path, O_RDONLY, 0); + if (fd > 0) { + joy = (struct usbjoy *) malloc(sizeof(*joy)); + if (joy == NULL) { close(fd); return NULL; } + memset(joy, 0, sizeof(*joy)); + + // Set the joystick to non-blocking read mode + fcntl(fd, F_SETFL, O_NONBLOCK); + + // notaz: maybe we should flush init events now. + // My pad returns axis as active when I plug it in, which is kind of annoying. + while (read(fd, &event, sizeof(event)) > 0); + + // Joystick's file descriptor + joy->fd = fd; + + // Joystick's name + ioctl(joy->fd, JSIOCGNAME(128*sizeof(char)), joy->name); + + // Joystick's device + strcpy(joy->device, path); + + // Joystick's buttons + ioctl(joy->fd, JSIOCGBUTTONS, &joy->numbuttons); + + // Joystick's axes + ioctl(joy->fd, JSIOCGAXES, &joy->numaxes); + + // Joystick's type (derived from name) + if (strncasecmp(joy->name, "logitech", strlen("logitech")) == 0) + joy->type = JOY_TYPE_LOGITECH; + else joy->type = JOY_TYPE_GENERIC; + } else { + // printf ("ERROR: No Joystick found\n"); + } + } + return joy; +} + +/* + Function: joy_name + + Returns Joystick's name. + + Parameters: + + joy - Selected joystick. + + Returns: + + Joystick's name or NULL if struct is empty. +*/ +char * joy_name (struct usbjoy * joy) { + if (joy != NULL) return joy->name; + else return NULL; +} + + +/* + Function: joy_device + + Returns Joystick's device. + + Parameters: + + joy - Selected joystick. + + Returns: + + Joystick's device or NULL if struct is empty. +*/ +char * joy_device (struct usbjoy * joy) { + if (joy != NULL) return joy->device; + else return NULL; +} + + +/* + Function: joy_buttons + + Returns Joystick's buttons number. + + Parameters: + + joy - Selected joystick. + + Returns: + + Joystick's buttons or 0 if struct is empty. +*/ +int joy_buttons (struct usbjoy * joy) { + if (joy != NULL) return joy->numbuttons; + else return 0; +} + + +/* + Function: joy_axes + + Returns Joystick's axes number. + + Parameters: + + joy - Selected joystick. + + Returns: + + Joystick's axes or 0 if struct is empty. +*/ +int joy_axes (struct usbjoy * joy) { + if (joy != NULL) return joy->numaxes; + else return 0; +} + + +/* + Function: joy_update + + Updates Joystick's internal information ( and fields). + + Parameters: + + joy - Selected joystick. + + Returns: + + 0 - No events registered (no need to update). + 1 - Events registered (a button or axe has been pushed). + -1 - Error: struct is empty. +*/ +int joy_update (struct usbjoy * joy) { + struct js_event events[0xff]; + int i, len; + int event = 0; + if (joy != NULL) { + if ((len=read(joy->fd, events, (sizeof events))) >0) { + len /= sizeof(events[0]); + for ( i=0; istateaxes[JOYLEFT] = joy->stateaxes[JOYRIGHT] = 0; + else if (events[i].value < 0) joy->stateaxes[JOYLEFT] = 1; + else joy->stateaxes[JOYRIGHT] = 1; + } + else if (events[i].number == 1) { + if (events[i].value == 0) joy->stateaxes[JOYUP] = joy->stateaxes[JOYDOWN] = 0; + else if (events[i].value < 0) joy->stateaxes[JOYUP] = 1; + else joy->stateaxes[JOYDOWN] = 1; + } + event = 1; + break; + case JS_EVENT_BUTTON: + joy->statebuttons[events[i].number] = events[i].value; + event = 1; + break; + default: + break; + } + } + } + } + else { + event = -1; + } + return event; +} + + +/* + Function: joy_getbutton + + Returns Joystick's button information. + + Parameters: + + button - Button which value you want to know (from 0 to 31). + joy - Selected joystick. + + Returns: + + 0 - Button NOT pushed. + 1 - Button pushed. + -1 - Error: struct is empty. +*/ +int joy_getbutton (int button, struct usbjoy * joy) { + if (joy != NULL) { + if (button < joy_buttons(joy)) return joy->statebuttons[button]; + else return 0; + } + else return -1; +} + + +/* + Function: joy_getaxe + + Returns Joystick's axes information. + + Parameters: + + axe - Axe which value you want to know (see ). + joy - Selected joystick. + + Returns: + + 0 - Direction NOT pushed. + 1 - Direction pushed. + -1 - Error: struct is empty. +*/ +int joy_getaxe (int axe, struct usbjoy * joy) { + if (joy != NULL) { + if (axe < 4) return joy->stateaxes[axe]; + else return 0; + } + else return -1; +} + + +/* + Function: joy_close + + Closes selected joystick's file descriptor and detroys it's fields. + + Parameters: + + joy - Selected joystick. + + Returns: + + 0 - Joystick successfully closed. + -1 - Error: struct is empty. +*/ +int joy_close (struct usbjoy * joy) { + if (joy != NULL) { + close (joy->fd); + free (joy); + return 0; + } + else return -1; +} + + +/*********************************************************************/ +/* GP2X USB Joystick Handling -GnoStiC */ +/*********************************************************************/ + +#include "gp2x.h" + +int num_of_joys = 0; +struct usbjoy *joys[4]; + +void gp2x_usbjoy_init (void) { + /* Open available joysticks -GnoStiC */ + int i, n = 0; + + printf("\n"); + for (i = 0; i < 4; i++) { + joys[n] = joy_open(i+1); + if (joys[n] && joy_buttons(joys[n]) > 0) { + printf ("+-Joystick %d: \"%s\", buttons = %i\n", i+1, joy_name(joys[n]), joy_buttons(joys[n])); + n++; + } + } + num_of_joys = n; + + printf("Found %d Joystick(s)\n",num_of_joys); +} + +void gp2x_usbjoy_update (void) { + /* Update Joystick Event Cache */ + int q, foo; + for (q=0; q < num_of_joys; q++) { + foo = joy_update (joys[q]); + } +} + +int gp2x_usbjoy_check (int joyno) { + /* Check Joystick */ + int q, joyExKey = 0; + struct usbjoy *joy = joys[joyno]; + + if (joy != NULL) { + if (joy_getaxe(JOYUP, joy)) { joyExKey |= GP2X_UP; } + if (joy_getaxe(JOYDOWN, joy)) { joyExKey |= GP2X_DOWN; } + if (joy_getaxe(JOYLEFT, joy)) { joyExKey |= GP2X_LEFT; } + if (joy_getaxe(JOYRIGHT, joy)) { joyExKey |= GP2X_RIGHT; } + + /* loop through joy buttons to check if they are pushed */ + for (q=0; qtype == JOY_TYPE_LOGITECH) { + switch (q) { + case 0: joyExKey |= GP2X_A; break; + case 1: joyExKey |= GP2X_X; break; + case 2: joyExKey |= GP2X_B; break; + case 3: joyExKey |= GP2X_Y; break; + } + } else { + switch (q) { + case 0: joyExKey |= GP2X_Y; break; + case 1: joyExKey |= GP2X_B; break; + case 2: joyExKey |= GP2X_X; break; + case 3: joyExKey |= GP2X_A; break; + } + } + + switch (q) { + case 4: joyExKey |= GP2X_L; break; + case 5: joyExKey |= GP2X_R; break; + case 6: joyExKey |= GP2X_L; break; /* left shoulder button 2 */ + case 7: joyExKey |= GP2X_R; break; /* right shoulder button 2 */ + case 8: joyExKey |= GP2X_SELECT;break; + case 9: joyExKey |= GP2X_START; break; + case 10: joyExKey |= GP2X_PUSH; break; + case 11: joyExKey |= GP2X_PUSH; break; + } + } + } + } + return joyExKey; +} + +int gp2x_usbjoy_check2 (int joyno) { + /* Check Joystick, don't map to gp2x joy */ + int q, to, joyExKey = 0; + struct usbjoy *joy = joys[joyno]; + + if (joy != NULL) { + if (joy_getaxe(JOYUP, joy)) { joyExKey |= 1 << 0; } + if (joy_getaxe(JOYDOWN, joy)) { joyExKey |= 1 << 1; } + if (joy_getaxe(JOYLEFT, joy)) { joyExKey |= 1 << 2; } + if (joy_getaxe(JOYRIGHT, joy)) { joyExKey |= 1 << 3; } + + /* loop through joy buttons to check if they are pushed */ + to = joy->numbuttons; + if (to > 32-4) to = 32-4; + for (q=0; q < to; q++) + if (joy->statebuttons[q]) joyExKey |= 1 << (q+4); + } + return joyExKey; +} + + + +void gp2x_usbjoy_deinit (void) { + int i; + for (i=0; i + + If you use this library or a part of it, please, let it know. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef USBJOY_H +#define USBJOY_H + +/* notaz: my Logitech has different button layout, and I want it to match gptx's */ +typedef enum { + JOY_TYPE_GENERIC, + JOY_TYPE_LOGITECH +} joy_type; + +/* + Enumeration: Axes values + This enumeration contains shortcuts to the values used on axes. + + Constants: + JOYUP - Joystick Up + JOYDOWN - Joystick Down + JOYLEFT - Joystick Left + JOYRIGHT - Joystick Right + + See also: + +*/ +#define JOYUP (0) +#define JOYDOWN (1) +#define JOYLEFT (2) +#define JOYRIGHT (3) + + +/* + Struct: usbjoy + + Contains all Joystick needed information. + + Fields: + fd - File descriptor used. + name - Joystick's name. + device - /dev/input/jsX device. + numbuttons - Joystick's buttons. + numaxes - Joystick's axes. + numhats - Joystick's hats. + statebuttons - Current state of each button. + stateaxes - Current state of each direction. +*/ +struct usbjoy { + int fd; + char name [128]; + char device [128]; + int numbuttons; + int numaxes; + int numhats; + int statebuttons[32]; + int stateaxes[4]; + joy_type type; +}; + + +/* + Function: joy_open + + Opens a USB joystick and fills its information. + + Parameters: + + joynumber - Joystick's identifier (0 reserved for GP2X's builtin Joystick). + + Returns: + + Filled usbjoy structure. +*/ +struct usbjoy * joy_open (int joynumber); + + +/* + Function: joy_name + + Returns Joystick's name. + + Parameters: + + joy - Selected joystick. + + Returns: + + Joystick's name or NULL if struct is empty. +*/ +char * joy_name (struct usbjoy * joy); + + +/* + Function: joy_device + + Returns Joystick's device. + + Parameters: + + joy - Selected joystick. + + Returns: + + Joystick's device or NULL if struct is empty. +*/ +char * joy_device (struct usbjoy * joy); + +/* + Function: joy_buttons + + Returns Joystick's buttons number. + + Parameters: + + joy - Selected joystick. + + Returns: + + Joystick's buttons or 0 if struct is empty. +*/ +int joy_buttons (struct usbjoy * joy); + +/* + Function: joy_axes + + Returns Joystick's axes number. + + Parameters: + + joy - Selected joystick. + + Returns: + + Joystick's axes or 0 if struct is empty. +*/ +int joy_axes (struct usbjoy * joy); + + +/* + Function: joy_update + + Updates Joystick's internal information ( and fields). + + Parameters: + + joy - Selected joystick. + + Returns: + + 0 - No events registered (no need to update). + 1 - Events registered (a button or axe has been pushed). + -1 - Error: struct is empty. +*/ +int joy_update (struct usbjoy * joy); + + +/* + Function: joy_getbutton + + Returns Joystick's button information. + + Parameters: + + button - Button which value you want to know (from 0 to 31). + joy - Selected joystick. + + Returns: + + 0 - Button NOT pushed. + 1 - Button pushed. + -1 - Error: struct is empty. +*/ +int joy_getbutton (int button, struct usbjoy * joy); + + +/* + Function: joy_getaxe + + Returns Joystick's axes information. + + Parameters: + + axe - Axe which value you want to know (see ). + joy - Selected joystick. + + Returns: + + 0 - Direction NOT pushed. + 1 - Direction pushed. + -1 - Error: struct is empty. +*/ +int joy_getaxe (int axe, struct usbjoy * joy); + +/* + Function: joy_close + + Closes selected joystick's file descriptor and detroys it's fields. + + Parameters: + + joy - Selected joystick. + + Returns: + + 0 - Joystick successfully closed. + -1 - Error: struct is empty. +*/ +int joy_close (struct usbjoy * joy); + + + +/* gp2x stuff */ +extern int num_of_joys; +extern struct usbjoy *joys[4]; + +void gp2x_usbjoy_update(void); +void gp2x_usbjoy_init(void); +int gp2x_usbjoy_check(int joyno); +int gp2x_usbjoy_check2(int joyno); +void gp2x_usbjoy_deinit(void); + + +#endif // USBJOY_H diff --git a/gp2x/version.h b/gp2x/version.h new file mode 100644 index 0000000..1331a8a --- /dev/null +++ b/gp2x/version.h @@ -0,0 +1,2 @@ +#define VERSION "0.964" + diff --git a/linux/940ctl_ym2612.c b/linux/940ctl_ym2612.c new file mode 100644 index 0000000..9702914 --- /dev/null +++ b/linux/940ctl_ym2612.c @@ -0,0 +1,112 @@ +/* faked 940 code just uses local copy of ym2612 */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../Pico/sound/ym2612.h" +#include "../gp2x/gp2x.h" +#include "../gp2x/emu.h" +#include "../gp2x/menu.h" + + +static YM2612 ym2612; + +YM2612 *ym2612_940 = &ym2612; +int mix_buffer_[44100/50*2]; /* this is where the YM2612 samples will be mixed to */ +int *mix_buffer = mix_buffer_; + + +/***********************************************************/ + +#define MAXOUT (+32767) +#define MINOUT (-32768) + +/* limitter */ +#define Limit(val, max,min) { \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + + +int YM2612Write_940(unsigned int a, unsigned int v) +{ + YM2612Write_(a, v); + + return 0; // cause the engine to do updates once per frame only +} + +UINT8 YM2612Read_940(void) +{ + return YM2612Read_(); +} + + +int YM2612PicoTick_940(int n) +{ + YM2612PicoTick_(n); + + return 0; +} + + +void YM2612PicoStateLoad_940(void) +{ + int i; + + YM2612PicoStateLoad_(); + + for(i = 0; i < 0x100; i++) { + YM2612Write_(0, i); + YM2612Write_(1, ym2612.REGS[i]); + } + for(i = 0; i < 0x100; i++) { + YM2612Write_(2, i); + YM2612Write_(3, ym2612.REGS[i|0x100]); + } +} + + +void YM2612Init_940(int baseclock, int rate) +{ + YM2612Init_(baseclock, rate); +} + + +void YM2612ResetChip_940(void) +{ + YM2612ResetChip_(); +} + + +void YM2612UpdateOne_940(short *buffer, int length, int stereo) +{ + int i; + + YM2612UpdateOne_(buffer, length, stereo); + + /* mix data */ + if (stereo) { + int *mb = mix_buffer; + for (i = length; i > 0; i--) { + int l, r; + l = r = *buffer; + l += *mb++, r += *mb++; + Limit( l, MAXOUT, MINOUT ); + Limit( r, MAXOUT, MINOUT ); + *buffer++ = l; *buffer++ = r; + } + } else { + for (i = 0; i < length; i++) { + int l = mix_buffer[i]; + l += buffer[i]; + Limit( l, MAXOUT, MINOUT ); + buffer[i] = l; + } + } +} + diff --git a/linux/Makefile b/linux/Makefile new file mode 100644 index 0000000..fc6b200 --- /dev/null +++ b/linux/Makefile @@ -0,0 +1,91 @@ + +# settings +dprint = 1 +# profile = 1 + + +DEFINC = -I../.. -I. -D__GP2X__ -D_UNZIP_SUPPORT # -DBENCHMARK +GCC = gcc +STRIP = strip +AS = gcc + +ifeq "$(profile)" "1" +COPT_COMMON = -s -O3 -ftracer -fstrength-reduce -Wall -funroll-loops -fomit-frame-pointer -fstrict-aliasing -ffast-math -fprofile-generate # -static +COPT = $(COPT_COMMON) # -mtune=arm920t +else +COPT = -ggdb -Wall -fno-strict-aliasing # -pg -O3 -ftracer -fstrength-reduce -funroll-loops -fomit-frame-pointer -ffast-math +COPT_COMMON = $(COPT) +endif + +# gtk +COPT += `pkg-config --cflags gtk+-2.0` +LDFLAGS += `pkg-config --libs gtk+-2.0` +COPT += `pkg-config --cflags gthread-2.0` +LDFLAGS += `pkg-config --libs gthread-2.0` + +# frontend +OBJS += ../gp2x/main.o ../gp2x/menu.o ../gp2x/emu.o ../gp2x/usbjoy.o blit.o gp2x.o 940ctl_ym2612.o +# Pico +OBJS += ../../Pico/Area.o ../../Pico/Cart.o ../../Pico/Utils.o ../../Pico/Memory.o ../../Pico/Misc.o \ + ../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o +# Pico - CD +OBJS += ../../Pico/cd/Pico.o ../../Pico/cd/Memory.o ../../Pico/cd/Sek.o ../../Pico/cd/LC89510.o \ + ../../Pico/cd/cd_sys.o +# Pico - sound +OBJS += ../../Pico/sound/sound.o ../../Pico/sound/sn76496.o ../../Pico/sound/ym2612.o +# zlib +OBJS += ../../zlib/gzio.o ../../zlib/inffast.o ../../zlib/inflate.o ../../zlib/inftrees.o ../../zlib/trees.o \ + ../../zlib/deflate.o ../../zlib/crc32.o ../../zlib/adler32.o ../../zlib/zutil.o ../../zlib/compress.o +# unzip +OBJS += ../../unzip/unzip.o +# CPU cores +DEFINC += -DEMU_M68K +OBJS += ../../cpu/musashi/m68kcpu.o ../../cpu/musashi/m68kopac.o ../../cpu/musashi/m68kopdm.o \ + ../../cpu/musashi/m68kopnz.o ../../cpu/musashi/m68kops.o +# mz80 +DEFINC += -D_USE_MZ80 +OBJS += ../../cpu/mz80/mz80.o + +# faked asm +#DEFINC += -D_ASM_DRAW_C +#DEFINC += -D_ASM_MEMORY_C +#DEFINC += -D_ASM_YM2612_C +OBJS += fakedasm.o + + +all: PicoDrive +clean: tidy + @$(RM) PicoDrive +tidy: + @$(RM) $(OBJS) + @make -C ../../cpu/mz80/ clean + +PicoDrive : $(OBJS) + @echo $@ + @$(GCC) $(COPT) $(OBJS) $(LDFLAGS) -lm -o $@ + + +../../cpu/mz80/mz80.o : ../../cpu/mz80/mz80.asm + @echo $@ + @nasm -f elf $< -o $@ + +../../cpu/mz80/mz80.asm : + @make -C ../../cpu/mz80/ + +.c.o: + @echo $< + @$(GCC) $(COPT) $(DEFINC) -c $< -o $@ +.s.o: + @echo $< + @$(GCC) $(COPT) $(DEFINC) -c $< -o $@ + + +../../Pico/sound/ym2612.o : ../../Pico/sound/ym2612.c + @echo $@ + @$(GCC) $(COPT_COMMON) $(DEFINC) -DEXTERNAL_YM2612 -c $< -o $@ # -mtune=arm940t + +# faked asm +../../Pico/Draw.o : ../../Pico/Draw.c + @echo $< + @$(GCC) $(COPT) $(DEFINC) -D_ASM_DRAW_C -c $< -o $@ + diff --git a/linux/README b/linux/README new file mode 100644 index 0000000..fa36b28 --- /dev/null +++ b/linux/README @@ -0,0 +1,4 @@ +This port tries to emulate gp2x environment on a standard linux box for testing +(i.e. to be able to use things like valgrind and efence, gcc runtime +optimizations, etc.). + diff --git a/linux/blit.c b/linux/blit.c new file mode 100644 index 0000000..a4981d9 --- /dev/null +++ b/linux/blit.c @@ -0,0 +1,81 @@ + +// Convert 0000bbb0 ggg0rrr0 0000bbb0 ggg0rrr0 +// to 00000000 rrr00000 ggg00000 bbb00000 ... + +void vidConvCpyRGB32 (void *to, void *from, int pixels) +{ + unsigned short *ps = from; + unsigned int *pd = to; + + for (; pixels; pixels--, ps++, pd++) + { + *pd = ((*ps<<20)&0xe00000) | ((*ps<<8)&0xe000) | ((*ps>>4)&0xe0); + *pd |= *pd >> 3; + } +} + +void vidConvCpyRGB32sh(void *to, void *from, int pixels) +{ + unsigned short *ps = from; + unsigned int *pd = to; + + for (; pixels; pixels--, ps++, pd++) + { + *pd = ((*ps<<20)&0xe00000) | ((*ps<<8)&0xe000) | ((*ps>>4)&0xe0); + *pd >>= 1; + *pd |= *pd >> 3; + } +} + +void vidConvCpyRGB32hi(void *to, void *from, int pixels) +{ + unsigned short *ps = from; + unsigned int *pd = to; + + for (; pixels; pixels--, ps++, pd++) + { + *pd = ((*ps<<20)&0xe00000) | ((*ps<<8)&0xe000) | ((*ps>>4)&0xe0); + continue; + *pd += 0x00404040; + if (*pd & 0x01000000) *pd |= 0x00e00000; + if (*pd & 0x00010000) *pd |= 0x0000e000; + if (*pd & 0x00000100) *pd |= 0x000000e0; + *pd &= 0x00e0e0e0; + *pd |= *pd >> 3; + } +} + +void vidCpyM2_40col(void *dest, void *src) +{ + unsigned char *pd = dest, *ps = src; + int i, u; + + for (i = 0; i < 224; i++) + { + ps += 8; + for (u = 0; u < 320; u++) + *pd++ = *ps++; + } +} + +void vidCpyM2_32col(void *dest, void *src) +{ + unsigned char *pd = dest, *ps = src; + int i, u; + + for (i = 0; i < 224; i++) + { + ps += 8; + pd += 32; + for (u = 0; u < 256; u++) + *pd++ = *ps++; + ps += 64; + pd += 32; + } +} + +void vidCpyM2_32col_nobord(void *dest, void *src) +{ + vidCpyM2_32col(dest, src); +} + diff --git a/linux/fakedasm.c b/linux/fakedasm.c new file mode 100644 index 0000000..d0e357c --- /dev/null +++ b/linux/fakedasm.c @@ -0,0 +1,667 @@ +// This is part of Pico Library + +// (c) Copyright 2004 Dave, All rights reserved. +// (c) Copyright 2006 notaz, All rights reserved. +// Free for non-commercial use. + +// For commercial use, separate licencing terms must be obtained. + + +#include "../../Pico/PicoInt.h" +#undef blockcpy + +extern unsigned short DefOutBuff[320*2]; +extern unsigned char HighCol[8+320+8]; +extern char HighSprZ[320+8+8]; // Z-buffer for accurate sprites and shadow/hilight mode + // (if bit 7 == 0, sh caused by tile; if bit 6 == 0 pixel must be shadowed, else hilighted, if bit5 == 1) +// lsb->msb: moved sprites, all window tiles don't use same priority, accurate sprites (copied from PicoOpt), interlace +// dirty sprites, sonic mode +extern int rendstatus; +extern int Scanline; // Scanline + + +struct TileStrip +{ + int nametab; // Position in VRAM of name table (for this tile line) + int line; // Line number in pixels 0x000-0x3ff within the virtual tilemap + int hscroll; // Horizontal scroll value in pixels for the line + int xmask; // X-Mask (0x1f - 0x7f) for horizontal wraparound in the tilemap + int *hc; // cache for high tile codes and their positions + int cells; // cells (tiles) to draw (32 col mode doesn't need to update whole 320) +}; + +// utility +void *blockcpy(void *dst, const void *src, size_t n) +{ + return memcpy(dst, src, n); +} + +void blockcpy_or(void *dst, void *src, size_t n, int pat) +{ + unsigned char *pd = dst, *ps = src; + for (; n; n--) + *pd++ = (unsigned char) (*ps++ | pat); +} + + +static int TileNorm(int sx,int addr,int pal) +{ + unsigned char *pd = HighCol+sx; + unsigned int pack=0; unsigned int t=0; + + pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels + if (pack) + { + t=pack&0x0000f000; if (t) pd[0]=(unsigned char)(pal|(t>>12)); + t=pack&0x00000f00; if (t) pd[1]=(unsigned char)(pal|(t>> 8)); + t=pack&0x000000f0; if (t) pd[2]=(unsigned char)(pal|(t>> 4)); + t=pack&0x0000000f; if (t) pd[3]=(unsigned char)(pal|(t )); + t=pack&0xf0000000; if (t) pd[4]=(unsigned char)(pal|(t>>28)); + t=pack&0x0f000000; if (t) pd[5]=(unsigned char)(pal|(t>>24)); + t=pack&0x00f00000; if (t) pd[6]=(unsigned char)(pal|(t>>20)); + t=pack&0x000f0000; if (t) pd[7]=(unsigned char)(pal|(t>>16)); + return 0; + } + + return 1; // Tile blank +} + +static int TileFlip(int sx,int addr,int pal) +{ + unsigned char *pd = HighCol+sx; + unsigned int pack=0; unsigned int t=0; + + pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels + if (pack) + { + t=pack&0x000f0000; if (t) pd[0]=(unsigned char)(pal|(t>>16)); + t=pack&0x00f00000; if (t) pd[1]=(unsigned char)(pal|(t>>20)); + t=pack&0x0f000000; if (t) pd[2]=(unsigned char)(pal|(t>>24)); + t=pack&0xf0000000; if (t) pd[3]=(unsigned char)(pal|(t>>28)); + t=pack&0x0000000f; if (t) pd[4]=(unsigned char)(pal|(t )); + t=pack&0x000000f0; if (t) pd[5]=(unsigned char)(pal|(t>> 4)); + t=pack&0x00000f00; if (t) pd[6]=(unsigned char)(pal|(t>> 8)); + t=pack&0x0000f000; if (t) pd[7]=(unsigned char)(pal|(t>>12)); + return 0; + } + return 1; // Tile blank +} + + +// tile renderers for hacky operator sprite support +#define sh_pix(x) \ + if(!t); \ + else if(t==0xe) pd[x]=(unsigned char)((pd[x]&0x3f)|0x80); /* hilight */ \ + else if(t==0xf) pd[x]=(unsigned char)((pd[x]&0x3f)|0xc0); /* shadow */ \ + else pd[x]=(unsigned char)(pal|t); + +static int TileNormSH(int sx,int addr,int pal) +{ + unsigned int pack=0; unsigned int t=0; + unsigned char *pd = HighCol+sx; + + pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels + if (pack) + { + t=(pack&0x0000f000)>>12; sh_pix(0); + t=(pack&0x00000f00)>> 8; sh_pix(1); + t=(pack&0x000000f0)>> 4; sh_pix(2); + t=(pack&0x0000000f) ; sh_pix(3); + t=(pack&0xf0000000)>>28; sh_pix(4); + t=(pack&0x0f000000)>>24; sh_pix(5); + t=(pack&0x00f00000)>>20; sh_pix(6); + t=(pack&0x000f0000)>>16; sh_pix(7); + return 0; + } + + return 1; // Tile blank +} + +static int TileFlipSH(int sx,int addr,int pal) +{ + unsigned int pack=0; unsigned int t=0; + unsigned char *pd = HighCol+sx; + + pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels + if (pack) + { + t=(pack&0x000f0000)>>16; sh_pix(0); + t=(pack&0x00f00000)>>20; sh_pix(1); + t=(pack&0x0f000000)>>24; sh_pix(2); + t=(pack&0xf0000000)>>28; sh_pix(3); + t=(pack&0x0000000f) ; sh_pix(4); + t=(pack&0x000000f0)>> 4; sh_pix(5); + t=(pack&0x00000f00)>> 8; sh_pix(6); + t=(pack&0x0000f000)>>12; sh_pix(7); + return 0; + } + return 1; // Tile blank +} + + +// -------------------------------------------- + +static void DrawStrip(struct TileStrip *ts, int sh) +{ + int tilex=0,dx=0,ty=0,code=0,addr=0,cells; + int oldcode=-1,blank=-1; // The tile we know is blank + int pal=0; + + // Draw tiles across screen: + tilex=(-ts->hscroll)>>3; + ty=(ts->line&7)<<1; // Y-Offset into tile + dx=((ts->hscroll-1)&7)+1; + cells = ts->cells; + if(dx != 8) cells++; // have hscroll, need to draw 1 cell more + + for (; cells; dx+=8,tilex++,cells--) + { + int zero=0; + + code=Pico.vram[ts->nametab+(tilex&ts->xmask)]; + if (code==blank) continue; + if (code>>15) { // high priority tile + int cval = code | (dx<<16) | (ty<<25); + if(code&0x1000) cval^=7<<26; + *ts->hc++ = cval; // cache it + continue; + } + + if (code!=oldcode) { + oldcode = code; + // Get tile address/2: + addr=(code&0x7ff)<<4; + addr+=ty; + if (code&0x1000) addr^=0xe; // Y-flip + +// pal=Pico.cram+((code>>9)&0x30); + pal=((code>>9)&0x30)|(sh<<6); + } + + if (code&0x0800) zero=TileFlip(dx,addr,pal); + else zero=TileNorm(dx,addr,pal); + + if (zero) blank=code; // We know this tile is blank now + } + + // terminate the cache list + *ts->hc = 0; +} + +static void DrawStripVSRam(struct TileStrip *ts, int plane) +{ + int tilex=0,dx=0,ty=0,code=0,addr=0,cell=0,nametabadd=0; + int oldcode=-1,blank=-1; // The tile we know is blank + int pal=0,scan=Scanline; + + // Draw tiles across screen: + tilex=(-ts->hscroll)>>3; + dx=((ts->hscroll-1)&7)+1; + if(dx != 8) { + int vscroll, line; + cell--; // have hscroll, start with negative cell + // also calculate intial VS stuff + vscroll=Pico.vsram[plane]; + + // Find the line in the name table + line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask .. + nametabadd=(line>>3)<<(ts->line>>24); // .. and shift[width] + ty=(line&7)<<1; // Y-Offset into tile + } + + for (; cell < ts->cells; dx+=8,tilex++,cell++) + { + int zero=0; + + if((cell&1)==0) { + int line,vscroll; + vscroll=Pico.vsram[plane+(cell&~1)]; + + // Find the line in the name table + line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask .. + nametabadd=(line>>3)<<(ts->line>>24); // .. and shift[width] + ty=(line&7)<<1; // Y-Offset into tile + } + + code=Pico.vram[ts->nametab+nametabadd+(tilex&ts->xmask)]; + if (code==blank) continue; + if (code>>15) { // high priority tile + int cval = code | (dx<<16) | (ty<<25); + if(code&0x1000) cval^=7<<26; + *ts->hc++ = cval; // cache it + continue; + } + + if (code!=oldcode) { + oldcode = code; + // Get tile address/2: + addr=(code&0x7ff)<<4; + if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip + +// pal=Pico.cram+((code>>9)&0x30); + pal=((code>>9)&0x30); + } + + if (code&0x0800) zero=TileFlip(dx,addr,pal); + else zero=TileNorm(dx,addr,pal); + + if (zero) blank=code; // We know this tile is blank now + } + + // terminate the cache list + *ts->hc = 0; +} + +static void DrawStripInterlace(struct TileStrip *ts) +{ + int tilex=0,dx=0,ty=0,code=0,addr=0,cells; + int oldcode=-1,blank=-1; // The tile we know is blank + int pal=0; + + // Draw tiles across screen: + tilex=(-ts->hscroll)>>3; + ty=(ts->line&15)<<1; // Y-Offset into tile + dx=((ts->hscroll-1)&7)+1; + cells = ts->cells; + if(dx != 8) cells++; // have hscroll, need to draw 1 cell more + + for (; cells; dx+=8,tilex++,cells--) + { + int zero=0; + + code=Pico.vram[ts->nametab+(tilex&ts->xmask)]; + if (code==blank) continue; + if (code>>15) { // high priority tile + int cval = (code&0xfc00) | (dx<<16) | (ty<<25); + cval|=(code&0x3ff)<<1; + if(code&0x1000) cval^=0xf<<26; + *ts->hc++ = cval; // cache it + continue; + } + + if (code!=oldcode) { + oldcode = code; + // Get tile address/2: + addr=(code&0x7ff)<<5; + if (code&0x1000) addr+=30-ty; else addr+=ty; // Y-flip + +// pal=Pico.cram+((code>>9)&0x30); + pal=((code>>9)&0x30); + } + + if (code&0x0800) zero=TileFlip(dx,addr,pal); + else zero=TileNorm(dx,addr,pal); + + if (zero) blank=code; // We know this tile is blank now + } + + // terminate the cache list + *ts->hc = 0; +} + +// -------------------------------------------- + +void DrawLayer(int plane, int *hcache, int maxcells, int sh) +{ + struct PicoVideo *pvid=&Pico.video; + const char shift[4]={5,6,5,7}; // 32,64 or 128 sized tilemaps (2 is invalid) + struct TileStrip ts; + int width, height, ymask; + int vscroll, htab; + + ts.hc=hcache; + ts.cells=maxcells; + + // Work out the TileStrip to draw + + // Work out the name table size: 32 64 or 128 tiles (0-3) + width=pvid->reg[16]; + height=(width>>4)&3; width&=3; + + ts.xmask=(1<1) ymask =0x0ff; + + // Find name table: + if (plane==0) ts.nametab=(pvid->reg[2]&0x38)<< 9; // A + else ts.nametab=(pvid->reg[4]&0x07)<<12; // B + + htab=pvid->reg[13]<<9; // Horizontal scroll table address + if ( pvid->reg[11]&2) htab+=Scanline<<1; // Offset by line + if ((pvid->reg[11]&1)==0) htab&=~0xf; // Offset by tile + htab+=plane; // A or B + + // Get horizontal scroll value, will be masked later + ts.hscroll=Pico.vram[htab&0x7fff]; + + if((pvid->reg[12]&6) == 6) { + // interlace mode 2 + vscroll=Pico.vsram[plane]; // Get vertical scroll value + + // Find the line in the name table + ts.line=(vscroll+(Scanline<<1))&((ymask<<1)|1); + ts.nametab+=(ts.line>>4)<reg[11]&4) { + // shit, we have 2-cell column based vscroll + // luckily this doesn't happen too often + ts.line=ymask|(shift[width]<<24); // save some stuff instead of line + DrawStripVSRam(&ts, plane); + } else { + vscroll=Pico.vsram[plane]; // Get vertical scroll value + + // Find the line in the name table + ts.line=(vscroll+Scanline)&ymask; + ts.nametab+=(ts.line>>3)<reg[12]&1) + { + nametab=(pvid->reg[3]&0x3c)<<9; // 40-cell mode + nametab+=(Scanline>>3)<<6; + } + else + { + nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode + nametab+=(Scanline>>3)<<5; + } + + tilex=tstart<<1; + tend<<=1; + + ty=(Scanline&7)<<1; // Y-Offset into tile + + if(!(rendstatus&2)) { + // check the first tile code + code=Pico.vram[nametab+tilex]; + // if the whole window uses same priority (what is often the case), we may be able to skip this field + if((code>>15) != prio) return; + } + + // Draw tiles across screen: + for (; tilex < tend; tilex++) + { + int addr=0,zero=0; + int pal; + + code=Pico.vram[nametab+tilex]; + if(code==blank) continue; + if((code>>15) != prio) { + rendstatus|=2; + continue; + } + + pal=((code>>9)&0x30); + + if(sh) { + int tmp, *zb = (int *)(HighCol+8+(tilex<<3)); + if(prio) { + tmp = *zb; + if(!(tmp&0x00000080)) tmp&=~0x000000c0; if(!(tmp&0x00008000)) tmp&=~0x0000c000; + if(!(tmp&0x00800000)) tmp&=~0x00c00000; if(!(tmp&0x80000000)) tmp&=~0xc0000000; + *zb++=tmp; tmp = *zb; + if(!(tmp&0x00000080)) tmp&=~0x000000c0; if(!(tmp&0x00008000)) tmp&=~0x0000c000; + if(!(tmp&0x00800000)) tmp&=~0x00c00000; if(!(tmp&0x80000000)) tmp&=~0xc0000000; + *zb++=tmp; + } else { + pal |= 0x40; + } + } + + // Get tile address/2: + addr=(code&0x7ff)<<4; + if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip + + if (code&0x0800) zero=TileFlip(8+(tilex<<3),addr,pal); + else zero=TileNorm(8+(tilex<<3),addr,pal); + + if (zero) blank=code; // We know this tile is blank now + } + + // terminate the cache list + //*hcache = 0; +} + +// -------------------------------------------- + +void DrawTilesFromCache(int *hc, int sh) +{ + int code, addr, zero, dx; + int pal; + short blank=-1; // The tile we know is blank + + // *ts->hc++ = code | (dx<<16) | (ty<<25); // cache it + + while((code=*hc++)) { + if(!sh && (short)code == blank) continue; + + // Get tile address/2: + addr=(code&0x7ff)<<4; + addr+=(unsigned int)code>>25; // y offset into tile + dx=(code>>16)&0x1ff; + if(sh) { + unsigned char *zb = HighCol+dx; + if(!(*zb&0x80)) *zb&=0x3f; zb++; if(!(*zb&0x80)) *zb&=0x3f; zb++; + if(!(*zb&0x80)) *zb&=0x3f; zb++; if(!(*zb&0x80)) *zb&=0x3f; zb++; + if(!(*zb&0x80)) *zb&=0x3f; zb++; if(!(*zb&0x80)) *zb&=0x3f; zb++; + if(!(*zb&0x80)) *zb&=0x3f; zb++; if(!(*zb&0x80)) *zb&=0x3f; zb++; + } + + pal=((code>>9)&0x30); + + if (code&0x0800) zero=TileFlip(dx,addr,pal); + else zero=TileNorm(dx,addr,pal); + + if(zero) blank=(short)code; + } +} + +// -------------------------------------------- + +// Index + 0 : hhhhvvvv ab--hhvv yyyyyyyy yyyyyyyy // a: offscreen h, b: offs. v, h: horiz. size +// Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8 + +void DrawSprite(int *sprite, int **hc, int sh) +{ + int width=0,height=0; + int row=0,code=0; + int pal; + int tile=0,delta=0; + int sx, sy; + int (*fTileFunc)(int sx,int addr,int pal); + + // parse the sprite data + sy=sprite[0]; + code=sprite[1]; + sx=code>>16; // X + width=sy>>28; + height=(sy>>24)&7; // Width and height in tiles + sy=(sy<<16)>>16; // Y + + row=Scanline-sy; // Row of the sprite we are on + + if (code&0x1000) row=(height<<3)-1-row; // Flip Y + + tile=code&0x7ff; // Tile number + tile+=row>>3; // Tile number increases going down + delta=height; // Delta to increase tile by going right + if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X + + tile<<=4; tile+=(row&7)<<1; // Tile address + + if(code&0x8000) { // high priority - cache it + *(*hc)++ = (tile<<16)|((code&0x0800)<<5)|((sx<<6)&0x0000ffc0)|((code>>9)&0x30)|((sprite[0]>>16)&0xf); + } else { + delta<<=4; // Delta of address + pal=((code>>9)&0x30)|(sh<<6); + + if(sh && (code&0x6000) == 0x6000) { + if(code&0x0800) fTileFunc=TileFlipSH; + else fTileFunc=TileNormSH; + } else { + if(code&0x0800) fTileFunc=TileFlip; + else fTileFunc=TileNorm; + } + + for (; width; width--,sx+=8,tile+=delta) + { + if(sx<=0) continue; + if(sx>=328) break; // Offscreen + + tile&=0x7fff; // Clip tile address + fTileFunc(sx,tile,pal); + } + } +} + + +void DrawSpritesFromCache(int *hc, int sh) +{ + int code, tile, sx, delta, width; + int pal; + int (*fTileFunc)(int sx,int addr,int pal); + + // *(*hc)++ = (tile<<16)|((code&0x0800)<<5)|((sx<<6)&0x0000ffc0)|((code>>9)&0x30)|((sprite[0]>>24)&0xf); + + while((code=*hc++)) { + pal=(code&0x30); + delta=code&0xf; + width=delta>>2; delta&=3; + width++; delta++; // Width and height in tiles + if (code&0x10000) delta=-delta; // Flip X + delta<<=4; + tile=((unsigned int)code>>17)<<1; + sx=(code<<16)>>22; // sx can be negative (start offscreen), so sign extend + + if(sh && pal == 0x30) { // + if(code&0x10000) fTileFunc=TileFlipSH; + else fTileFunc=TileNormSH; + } else { + if(code&0x10000) fTileFunc=TileFlip; + else fTileFunc=TileNorm; + } + + for (; width; width--,sx+=8,tile+=delta) + { + if(sx<=0) continue; + if(sx>=328) break; // Offscreen + + tile&=0x7fff; // Clip tile address + fTileFunc(sx,tile,pal); + } + } +} + + +void BackFill(int reg7, int sh) +{ + unsigned int back=0; + unsigned int *pd=NULL,*end=NULL; + + // Start with a blank scanline (background colour): + back=reg7&0x3f; + back|=sh<<6; + back|=back<<8; + back|=back<<16; + + pd= (unsigned int *)(HighCol+8); + end=(unsigned int *)(HighCol+8+320); + + do { pd[0]=pd[1]=pd[2]=pd[3]=back; pd+=4; } while (pd= 0; i--) + pal[0x40|i] = pal[0xc0|i] = (unsigned short)((pal[i]>>1)&0x0777); + // hilighted pixels + for(i = 0x3f; i >= 0; i--) { + t=pal[i]&0xeee;t+=0x444;if(t&0x10)t|=0xe;if(t&0x100)t|=0xe0;if(t&0x1000)t|=0xe00;t&=0xeee; + pal[0x80|i]=(unsigned short)t; + } + Pico.m.dirtyPal = 0; + } + } + + for(i = 0; i < len; i++) + pd[i] = pal[ps[i]]; +} + + +void FinalizeLineRGB555(int sh) +{ + unsigned short *pd=DrawLineDest; + unsigned char *ps=HighCol+8; + unsigned short *pal=HighPal; + int len, i, t, dirtyPal = Pico.m.dirtyPal; + + if(dirtyPal) { + unsigned short *ppal=Pico.cram; + for(i = 0x3f; i >= 0; i--) + pal[i] = (unsigned short) (((ppal[i]&0x00f)<<12)|((ppal[i]&0x0f0)<<3)|((ppal[i]&0xf00)>>7)); + Pico.m.dirtyPal = 0; + } + + if (Pico.video.reg[12]&1) { + len = 320; + } else { + if(!(PicoOpt&0x100)) pd+=32; + len = 256; + } + + if(sh) { + if(dirtyPal) { + // shadowed pixels + for(i = 0x3f; i >= 0; i--) + pal[0x40|i] = pal[0xc0|i] = (unsigned short)((pal[i]>>1)&0x738e); + // hilighted pixels + for(i = 0x3f; i >= 0; i--) { + t=pal[i]&0xe71c;t+=0x4208;if(t&0x20)t|=0x1c;if(t&0x800)t|=0x700;if(t&0x10000)t|=0xe000;t&=0xe71c; + pal[0x80|i]=(unsigned short)t; + } + } + } + + for(i = 0; i < len; i++) + pd[i] = pal[ps[i]]; +} + + + diff --git a/linux/gp2x.c b/linux/gp2x.c new file mode 100644 index 0000000..2651a50 --- /dev/null +++ b/linux/gp2x.c @@ -0,0 +1,384 @@ +/* faking/emulating gp2x.c by using gtk */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../gp2x/emu.h" +#include "../gp2x/gp2x.h" +#include "../gp2x/usbjoy.h" +#include "../gp2x/version.h" + +void *gp2x_screen; +static int current_bpp = 8; +static int current_pal[256]; +static unsigned long current_keys = 0; +static int sounddev = 0, mixerdev = 0; +static const char *verstring = "PicoDrive " VERSION; + +// dummies +char *ext_menu = 0, *ext_state = 0; + +/* gtk */ +struct gtk_global_struct +{ + GtkWidget *window; + GtkWidget *pixmap1; +} gtk_items; + + +static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) +{ + return FALSE; +} + +static void destroy (GtkWidget *widget, gpointer data) +{ + gtk_main_quit (); +} + +static gint key_press_event (GtkWidget *widget, GdkEventKey *event) +{ + switch (event->hardware_keycode) + { + case 0x62: current_keys |= GP2X_UP; break; + case 0x68: current_keys |= GP2X_DOWN; break; + case 0x64: current_keys |= GP2X_LEFT; break; + case 0x66: current_keys |= GP2X_RIGHT; break; + case 0x24: current_keys |= GP2X_START; break; // enter + case 0x23: current_keys |= GP2X_SELECT;break; // ] + case 0x34: current_keys |= GP2X_A; break; // z + case 0x35: current_keys |= GP2X_X; break; // x + case 0x36: current_keys |= GP2X_B; break; // c + case 0x37: current_keys |= GP2X_Y; break; // v + case 0x27: current_keys |= GP2X_L; break; // s + case 0x28: current_keys |= GP2X_R; break; // d + case 0x29: current_keys |= GP2X_PUSH; break; // f + case 0x18: current_keys |= GP2X_VOL_DOWN;break; // q + case 0x19: current_keys |= GP2X_VOL_UP;break; // w + } + + return 0; +} + +static gint key_release_event (GtkWidget *widget, GdkEventKey *event) +{ + switch (event->hardware_keycode) + { + case 0x62: current_keys &= ~GP2X_UP; break; + case 0x68: current_keys &= ~GP2X_DOWN; break; + case 0x64: current_keys &= ~GP2X_LEFT; break; + case 0x66: current_keys &= ~GP2X_RIGHT; break; + case 0x24: current_keys &= ~GP2X_START; break; // enter + case 0x23: current_keys &= ~GP2X_SELECT;break; // ] + case 0x34: current_keys &= ~GP2X_A; break; // z + case 0x35: current_keys &= ~GP2X_X; break; // x + case 0x36: current_keys &= ~GP2X_B; break; // c + case 0x37: current_keys &= ~GP2X_Y; break; // v + case 0x27: current_keys &= ~GP2X_L; break; // s + case 0x28: current_keys &= ~GP2X_R; break; // d + case 0x29: current_keys &= ~GP2X_PUSH; break; // f + case 0x18: current_keys &= ~GP2X_VOL_DOWN;break; // q + case 0x19: current_keys &= ~GP2X_VOL_UP;break; // w + } + + return 0; +} + +static void *gtk_threadf(void *none) +{ + gtk_main(); + + printf("linux: gtk thread finishing\n"); + engineState = PGS_Quit; + + return NULL; +} + +static void gtk_initf(void) +{ + int argc = 0; + char *argv[] = { "" }; + GtkWidget *box; + pthread_t gtk_thread; + + g_thread_init (NULL); + gdk_threads_init (); + gdk_set_locale (); + gtk_init (&argc, (char ***) &argv); + + /* create new window */ + gtk_items.window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (G_OBJECT (gtk_items.window), "delete_event", + G_CALLBACK (delete_event), NULL); + + g_signal_connect (G_OBJECT (gtk_items.window), "destroy", + G_CALLBACK (destroy), NULL); + + g_signal_connect (G_OBJECT (gtk_items.window), "key_press_event", + G_CALLBACK (key_press_event), NULL); + + g_signal_connect (G_OBJECT (gtk_items.window), "key_release_event", + G_CALLBACK (key_release_event), NULL); + + gtk_container_set_border_width (GTK_CONTAINER (gtk_items.window), 2); + gtk_window_set_title ((GtkWindow *) gtk_items.window, verstring); + + box = gtk_hbox_new(FALSE, 0); + gtk_widget_show(box); + gtk_container_add (GTK_CONTAINER (gtk_items.window), box); + + /* live pixmap */ + gtk_items.pixmap1 = gtk_image_new (); + gtk_container_add (GTK_CONTAINER (box), gtk_items.pixmap1); + gtk_widget_show (gtk_items.pixmap1); + gtk_widget_set_size_request (gtk_items.pixmap1, 320, 240); + + gtk_widget_show (gtk_items.window); + + // pthread_mutex_init (&thr_mutex, NULL); + // pthread_mutex_lock (&thr_mutex); + // pthread_mutex_init (&scanner_muttex, NULL); + + pthread_create(>k_thread, NULL, gtk_threadf, NULL); +} + +void finalize_image(guchar *pixels, gpointer data) +{ + free(pixels); +} + +/* --- */ + +void gp2x_init(void) +{ + printf("entering init()\n"); fflush(stdout); + + gp2x_screen = malloc(320*240*2 + 320*2); + + // snd + mixerdev = open("/dev/mixer", O_RDWR); + if (mixerdev == -1) + printf("open(\"/dev/mixer\") failed with %i\n", errno); + + gtk_initf(); + + gp2x_usbjoy_init(); + + printf("exitting init()\n"); fflush(stdout); +} + +void gp2x_deinit(void) +{ + free(gp2x_screen); + if (sounddev > 0) close(sounddev); + close(mixerdev); + gp2x_usbjoy_deinit(); +} + +/* video */ +void gp2x_video_flip(void) +{ + GdkPixbuf *pixbuf; + unsigned char *image; + int i; + + gdk_threads_enter(); + + image = malloc (320*240*3); + if (image == NULL) + { + gdk_threads_leave(); + return; + } + + if (current_bpp == 8) + { + unsigned char *pixels = gp2x_screen; + int pix; + + for (i = 0; i < 320*240; i++) + { + pix = current_pal[pixels[i]]; + image[3 * i + 0] = pix >> 16; + image[3 * i + 1] = pix >> 8; + image[3 * i + 2] = pix; + } + } + else + { + unsigned short *pixels = gp2x_screen; + + for (i = 0; i < 320*240; i++) + { + /* in: rrrr rggg gggb bbbb */ + /* out: rrrr r000 gggg gg00 bbbb b000 */ + image[3 * i + 0] = (pixels[i] >> 8) & 0xf8; + image[3 * i + 1] = (pixels[i] >> 3) & 0xfc; + image[3 * i + 2] = (pixels[i] << 3); + } + } + + pixbuf = gdk_pixbuf_new_from_data (image, GDK_COLORSPACE_RGB, + FALSE, 8, 320, 240, 320*3, finalize_image, NULL); + gtk_image_set_from_pixbuf (GTK_IMAGE (gtk_items.pixmap1), pixbuf); + g_object_unref (pixbuf); + + gdk_threads_leave(); +} + +void gp2x_video_changemode(int bpp) +{ + current_bpp = bpp; +} + +void gp2x_video_setpalette(int *pal, int len) +{ + memcpy(current_pal, pal, len*4); +} + +void gp2x_video_RGB_setscaling(int W, int H) +{ +} + +void gp2x_memcpy_all_buffers(void *data, int offset, int len) +{ + memcpy((char *)gp2x_screen + offset, data, len); +} + + +void gp2x_memset_all_buffers(int offset, int byte, int len) +{ + memset((char *)gp2x_screen + offset, byte, len); +} + + +/* sound */ +static int s_oldrate = 0, s_oldbits = 0, s_oldstereo = 0; + +void gp2x_start_sound(int rate, int bits, int stereo) +{ + int frag = 0, bsize, buffers; + + // if no settings change, we don't need to do anything + if (rate == s_oldrate && s_oldbits == bits && s_oldstereo == stereo) return; + + if (sounddev > 0) close(sounddev); + sounddev = open("/dev/dsp", O_WRONLY|O_ASYNC); + if (sounddev == -1) + printf("open(\"/dev/dsp\") failed with %i\n", errno); + + ioctl(sounddev, SNDCTL_DSP_SPEED, &rate); + ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits); + ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo); + // calculate buffer size + buffers = 16; + bsize = rate / 32; + if (rate > 22050) { bsize*=4; buffers*=2; } // 44k mode seems to be very demanding + while ((bsize>>=1)) frag++; + frag |= buffers<<16; // 16 buffers + ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag); + printf("gp2x_set_sound: %i/%ibit/%s, %i buffers of %i bytes\n", + rate, bits, stereo?"stereo":"mono", frag>>16, 1<<(frag&0xffff)); + + s_oldrate = rate; s_oldbits = bits; s_oldstereo = stereo; +} + +void gp2x_sound_write(void *buff, int len) +{ + write(sounddev, buff, len); +} + +void gp2x_sound_volume(int l, int r) +{ + l=l<0?0:l; l=l>255?255:l; r=r<0?0:r; r=r>255?255:r; + l<<=8; l|=r; + ioctl(mixerdev, SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/ +} + +/* joy */ +unsigned long gp2x_joystick_read(int allow_usb_joy) +{ + unsigned long value = current_keys; + int i; + + if (allow_usb_joy && num_of_joys > 0) { + // check the usb joy as well.. + gp2x_usbjoy_update(); + for (i = 0; i < num_of_joys; i++) + value |= gp2x_usbjoy_check(i); + } + + return value; +} + +/* 940 */ +int crashed_940 = 0; +void Pause940(int yes) +{ +} + +void Reset940(int yes) +{ +} + +/* faking gp2x cpuctrl.c */ +void cpuctrl_init(void) +{ +} + +void cpuctrl_deinit(void) +{ +} + +void set_FCLK(unsigned MHZ) +{ +} + +void Disable_940(void) +{ +} + +void gp2x_video_wait_vsync(void) +{ +} + +void set_RAM_Timings(int tRC, int tRAS, int tWR, int tMRD, int tRFC, int tRP, int tRCD) +{ +} + +void set_gamma(int g100) +{ +} + + +/* squidgehack.c */ +int mmuhack(void) +{ + return 0; +} + + +int mmuunhack(void) +{ + return 0; +} + + +/* misc */ +void spend_cycles(int c) +{ + usleep(c/*/200*/); +} + + + diff --git a/linux/port_config.h b/linux/port_config.h new file mode 100644 index 0000000..4f183c7 --- /dev/null +++ b/linux/port_config.h @@ -0,0 +1,19 @@ +// port specific settings + +#ifndef PORT_CONFIG_H +#define PORT_CONFIG_H + +#define CPU_CALL + +// draw2.c +#define START_ROW 0 // which row of tiles to start rendering at? +#define END_ROW 28 // ..end + +// pico.c +#define CAN_HANDLE_240_LINES 1 + +#define dprintf(f,...) printf(f"\n",##__VA_ARGS__) +//#define dprintf(x...) + +#endif //PORT_CONFIG_H + -- 2.39.5