initial import
authornotaz <notasas@gmail.com>
Tue, 19 Dec 2006 20:53:21 +0000 (20:53 +0000)
committernotaz <notasas@gmail.com>
Tue, 19 Dec 2006 20:53:21 +0000 (20:53 +0000)
git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@2 be3aeb3a-fb24-0410-a615-afba39da0efa

36 files changed:
gp2x/940.c [new file with mode: 0644]
gp2x/940ctl_ym2612.c [new file with mode: 0644]
gp2x/940ctl_ym2612.h [new file with mode: 0644]
gp2x/940init.s [new file with mode: 0644]
gp2x/940shared.h [new file with mode: 0644]
gp2x/Makefile [new file with mode: 0644]
gp2x/asmutils.h [new file with mode: 0644]
gp2x/asmutils.s [new file with mode: 0644]
gp2x/config.txt [new file with mode: 0644]
gp2x/cpuctrl.c [new file with mode: 0644]
gp2x/cpuctrl.gpl.txt [new file with mode: 0644]
gp2x/cpuctrl.h [new file with mode: 0644]
gp2x/emu.c [new file with mode: 0644]
gp2x/emu.h [new file with mode: 0644]
gp2x/gp2x.c [new file with mode: 0644]
gp2x/gp2x.h [new file with mode: 0644]
gp2x/main.c [new file with mode: 0644]
gp2x/menu.c [new file with mode: 0644]
gp2x/menu.h [new file with mode: 0644]
gp2x/mmuhack.c [new file with mode: 0644]
gp2x/mmuhack.txt [new file with mode: 0644]
gp2x/port_config.h [new file with mode: 0644]
gp2x/port_config.s [new file with mode: 0644]
gp2x/squidgehack.c [new file with mode: 0644]
gp2x/squidgehack.h [new file with mode: 0644]
gp2x/test.c [new file with mode: 0644]
gp2x/usbjoy.c [new file with mode: 0644]
gp2x/usbjoy.h [new file with mode: 0644]
gp2x/version.h [new file with mode: 0644]
linux/940ctl_ym2612.c [new file with mode: 0644]
linux/Makefile [new file with mode: 0644]
linux/README [new file with mode: 0644]
linux/blit.c [new file with mode: 0644]
linux/fakedasm.c [new file with mode: 0644]
linux/gp2x.c [new file with mode: 0644]
linux/port_config.h [new file with mode: 0644]

diff --git a/gp2x/940.c b/gp2x/940.c
new file mode 100644 (file)
index 0000000..c3f0b11
--- /dev/null
@@ -0,0 +1,93 @@
+#include "940shared.h"\r
+\r
+/* this code assumes that we live @ 0x3000000 bank */\r
+//static volatile unsigned short *gp2x_memregs = (void *) 0x0xbd000000;\r
+//static volatile unsigned long  *gp2x_memregl = (void *) 0x0xbd000000;\r
+\r
+static _940_data_t *shared_data = (_940_data_t *) 0x100000;\r
+static _940_ctl_t  *shared_ctl  = (_940_ctl_t *)  0x200000;\r
+YM2612 *ym2612_940;\r
+int *mix_buffer;\r
+\r
+// from init.s\r
+void wait_irq(void);\r
+void spend_cycles(int c);\r
+void cache_clean(void);\r
+void cache_clean_flush(void);\r
+\r
+//     asm volatile ("mov r0, #0" ::: "r0");\r
+//     asm volatile ("mcr p15, 0, r0, c7, c6,  0" ::: "r0"); /* flush dcache */\r
+//     asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer */\r
+\r
+void Main940(int startvector)\r
+{\r
+       ym2612_940 = &shared_data->ym2612;\r
+       mix_buffer = shared_data->mix_buffer;\r
+\r
+       // debug\r
+       shared_ctl->vstarts[startvector]++;\r
+       asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0");\r
+\r
+       /* unmask IRQs */\r
+\r
+       for (;; shared_ctl->loopc++)\r
+       {\r
+/*\r
+               while (!shared_ctl->busy)\r
+               {\r
+                       //shared_ctl->waitc++;\r
+                       spend_cycles(256);\r
+               }\r
+*/\r
+               if (!shared_ctl->busy)\r
+               {\r
+                       wait_irq();\r
+               }\r
+\r
+               switch (shared_ctl->job)\r
+               {\r
+                       case JOB940_YM2612INIT:\r
+                               shared_ctl->writebuff0[0] = shared_ctl->writebuff1[0] = 0xffff;\r
+                               YM2612Init_(shared_ctl->baseclock, shared_ctl->rate);\r
+                               break;\r
+\r
+                       case JOB940_YM2612RESETCHIP:\r
+                               YM2612ResetChip_();\r
+                               break;\r
+\r
+                       case JOB940_PICOSTATELOAD:\r
+                               YM2612PicoStateLoad_();\r
+                               break;\r
+\r
+                       case JOB940_YM2612UPDATEONE: {\r
+                               int i, dw, *wbuff;\r
+                               if (shared_ctl->writebuffsel == 1) {\r
+                                       wbuff = (int *) shared_ctl->writebuff1;\r
+                               } else {\r
+                                       wbuff = (int *) shared_ctl->writebuff0;\r
+                               }\r
+\r
+                               /* playback all writes */\r
+                               for (i = 2048/2; i > 0; i--) {\r
+                                       UINT16 d;\r
+                                       dw = *wbuff++;\r
+                                       d = dw;\r
+                                       if (d == 0xffff) break;\r
+                                       YM2612Write_(d >> 8, d);\r
+                                       d = (dw>>16);\r
+                                       if (d == 0xffff) break;\r
+                                       YM2612Write_(d >> 8, d);\r
+                               }\r
+\r
+                               YM2612UpdateOne_(0, shared_ctl->length, shared_ctl->stereo);\r
+//                             cache_clean_flush();\r
+                               cache_clean();\r
+//                             asm volatile ("mov r0, #0" ::: "r0");\r
+//                             asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer, should be done on nonbuffered write */\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               shared_ctl->busy = 0;\r
+       }\r
+}\r
diff --git a/gp2x/940ctl_ym2612.c b/gp2x/940ctl_ym2612.c
new file mode 100644 (file)
index 0000000..d8a10b2
--- /dev/null
@@ -0,0 +1,451 @@
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <unistd.h>\r
+#include <sys/mman.h>\r
+#include <sys/ioctl.h>\r
+#include <fcntl.h>\r
+#include <errno.h>\r
+\r
+#include "940shared.h"\r
+#include "gp2x.h"\r
+#include "emu.h"\r
+#include "menu.h"\r
+#include "asmutils.h"\r
+\r
+/* we will need some gp2x internals here */\r
+extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */\r
+extern volatile unsigned long  *gp2x_memregl;\r
+\r
+static unsigned char *shared_mem = 0;\r
+static _940_data_t *shared_data = 0;\r
+static _940_ctl_t *shared_ctl = 0;\r
+\r
+int crashed_940 = 0;\r
+\r
+\r
+/***********************************************************/\r
+\r
+#define MAXOUT         (+32767)\r
+#define MINOUT         (-32768)\r
+\r
+/* limitter */\r
+#define Limit(val, max,min) { \\r
+       if ( val > max )      val = max; \\r
+       else if ( val < min ) val = min; \\r
+}\r
+\r
+/* these will be managed locally on our side */\r
+extern int   *ym2612_dacen;\r
+extern INT32 *ym2612_dacout;\r
+extern void  *ym2612_regs;\r
+\r
+static UINT8 *REGS = 0;                /* we will also keep local copy of regs for savestates and such */\r
+static INT32 addr_A1;          /* address line A1      */\r
+static int      dacen;\r
+static INT32 dacout;\r
+static UINT8 ST_address;       /* address register     */\r
+static UINT8 ST_status;                /* status flag          */\r
+static UINT8 ST_mode;          /* mode  CSM / 3SLOT    */\r
+static int   ST_TA;                    /* timer a              */\r
+static int   ST_TAC;           /* timer a maxval       */\r
+static int   ST_TAT;           /* timer a ticker       */\r
+static UINT8 ST_TB;                    /* timer b              */\r
+static int   ST_TBC;           /* timer b maxval       */\r
+static int   ST_TBT;           /* timer b ticker       */\r
+\r
+static int   writebuff_ptr = 0;\r
+\r
+\r
+/* OPN Mode Register Write */\r
+static void set_timers( int v )\r
+{\r
+       /* b7 = CSM MODE */\r
+       /* b6 = 3 slot mode */\r
+       /* b5 = reset b */\r
+       /* b4 = reset a */\r
+       /* b3 = timer enable b */\r
+       /* b2 = timer enable a */\r
+       /* b1 = load b */\r
+       /* b0 = load a */\r
+       ST_mode = v;\r
+\r
+       /* reset Timer b flag */\r
+       if( v & 0x20 )\r
+               ST_status &= ~2;\r
+\r
+       /* reset Timer a flag */\r
+       if( v & 0x10 )\r
+               ST_status &= ~1;\r
+}\r
+\r
+/* YM2612 write */\r
+/* a = address */\r
+/* v = value   */\r
+/* returns 1 if sample affecting state changed */\r
+int YM2612Write_940(unsigned int a, unsigned int v)\r
+{\r
+       int addr; //, ret=1;\r
+\r
+       v &= 0xff;      /* adjust to 8 bit bus */\r
+       a &= 3;\r
+\r
+       switch( a ) {\r
+       case 0: /* address port 0 */\r
+               ST_address = v;\r
+               addr_A1 = 0;\r
+               //ret=0;\r
+               break;\r
+\r
+       case 1: /* data port 0    */\r
+               if (addr_A1 != 0) {\r
+                       return 0;       /* verified on real YM2608 */\r
+               }\r
+\r
+               addr = ST_address;\r
+               REGS[addr] = v;\r
+\r
+               switch( addr & 0xf0 )\r
+               {\r
+               case 0x20:      /* 0x20-0x2f Mode */\r
+                       switch( addr )\r
+                       {\r
+                       case 0x24: { // timer A High 8\r
+                                       int TAnew = (ST_TA & 0x03)|(((int)v)<<2);\r
+                                       if(ST_TA != TAnew) {\r
+                                               // we should reset ticker only if new value is written. Outrun requires this.\r
+                                               ST_TA = TAnew;\r
+                                               ST_TAC = (1024-TAnew)*18;\r
+                                               ST_TAT = 0;\r
+                                       }\r
+                                       return 0;\r
+                               }\r
+                       case 0x25: { // timer A Low 2\r
+                                       int TAnew = (ST_TA & 0x3fc)|(v&3);\r
+                                       if(ST_TA != TAnew) {\r
+                                               ST_TA = TAnew;\r
+                                               ST_TAC = (1024-TAnew)*18;\r
+                                               ST_TAT = 0;\r
+                                       }\r
+                                       return 0;\r
+                               }\r
+                       case 0x26: // timer B\r
+                               if(ST_TB != v) {\r
+                                       ST_TB = v;\r
+                                       ST_TBC  = (256-v)<<4;\r
+                                       ST_TBC *= 18;\r
+                                       ST_TBT  = 0;\r
+                               }\r
+                               return 0;\r
+                       case 0x27:      /* mode, timer control */\r
+                               set_timers( v );\r
+                               break; // other side needs ST.mode for 3slot mode\r
+                       case 0x2a:      /* DAC data (YM2612) */\r
+                               dacout = ((int)v - 0x80) << 6;  /* level unknown (notaz: 8 seems to be too much) */\r
+                               return 0;\r
+                       case 0x2b:      /* DAC Sel  (YM2612) */\r
+                               /* b7 = dac enable */\r
+                               dacen = v & 0x80;\r
+                               break; // other side has to know this\r
+                       default:\r
+                               break;\r
+                       }\r
+                       break;\r
+               }\r
+               break;\r
+\r
+       case 2: /* address port 1 */\r
+               ST_address = v;\r
+               addr_A1 = 1;\r
+               //ret=0;\r
+               break;\r
+\r
+       case 3: /* data port 1    */\r
+               if (addr_A1 != 1) {\r
+                       return 0;       /* verified on real YM2608 */\r
+               }\r
+\r
+               addr = ST_address | 0x100;\r
+               REGS[addr] = v;\r
+               break;\r
+       }\r
+\r
+       if(currentConfig.EmuOpt & 4) {\r
+               /* queue this write for 940 */\r
+               if (writebuff_ptr < 2047) {\r
+                       if (shared_ctl->writebuffsel == 1) {\r
+                               shared_ctl->writebuff0[writebuff_ptr++] = (a<<8)|v;\r
+                       } else {\r
+                               shared_ctl->writebuff1[writebuff_ptr++] = (a<<8)|v;\r
+                       }\r
+               } else {\r
+                       printf("warning: writebuff_ptr > 2047\n");\r
+               }\r
+       }\r
+\r
+       return 0; // cause the engine to do updates once per frame only\r
+}\r
+\r
+UINT8 YM2612Read_940(void)\r
+{\r
+       return ST_status;\r
+}\r
+\r
+\r
+int YM2612PicoTick_940(int n)\r
+{\r
+       //int ret = 0;\r
+\r
+       // timer A\r
+       if(ST_mode & 0x01 && (ST_TAT+=64*n) >= ST_TAC) {\r
+               ST_TAT -= ST_TAC;\r
+               if(ST_mode & 0x04) ST_status |= 1;\r
+               // CSM mode total level latch and auto key on\r
+/*             FIXME\r
+               if(ST_mode & 0x80) {\r
+                       CSMKeyControll( &(ym2612_940->CH[2]) ); // Vectorman2, etc.\r
+                       ret = 1;\r
+               }\r
+*/\r
+       }\r
+\r
+       // timer B\r
+       if(ST_mode & 0x02 && (ST_TBT+=64*n) >= ST_TBC) {\r
+               ST_TBT -= ST_TBC;\r
+               if(ST_mode & 0x08) ST_status |= 2;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static void wait_busy_940(void)\r
+{\r
+       int i;\r
+#if 0\r
+       printf("940 busy, entering wait loop.. (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
+       for (i = 0; i < 8; i++)\r
+               printf("%i ", shared_ctl->vstarts[i]);\r
+       printf(")\n");\r
+\r
+       for (i = 0; shared_ctl->busy; i++)\r
+       {\r
+               spend_cycles(1024); /* needs tuning */\r
+       }\r
+       printf("wait iterations: %i\n", i);\r
+#else\r
+       for (i = 0; shared_ctl->busy && i < 0x10000; i++)\r
+               spend_cycles(4*1024);\r
+       if (i < 0x10000) return;\r
+\r
+       /* 940 crashed */\r
+       printf("940 crashed (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
+       for (i = 0; i < 8; i++)\r
+               printf("%i ", shared_ctl->vstarts[i]);\r
+       printf(")\n");\r
+       strcpy(menuErrorMsg, "940 crashed.");\r
+       engineState = PGS_Menu;\r
+       crashed_940 = 1;\r
+#endif\r
+}\r
+\r
+\r
+static void add_job_940(int job)\r
+{\r
+       shared_ctl->job = job;\r
+       shared_ctl->busy = 1;\r
+       gp2x_memregs[0x3B3E>>1] = 0xffff; // cause an IRQ for 940\r
+}\r
+\r
+\r
+void YM2612PicoStateLoad_940(void)\r
+{\r
+       int i, old_A1 = addr_A1;\r
+\r
+       if (shared_ctl->busy) wait_busy_940();\r
+\r
+       // feed all the registers and update internal state\r
+       for(i = 0; i < 0x100; i++) {\r
+               YM2612Write_940(0, i);\r
+               YM2612Write_940(1, REGS[i]);\r
+       }\r
+       for(i = 0; i < 0x100; i++) {\r
+               YM2612Write_940(2, i);\r
+               YM2612Write_940(3, REGS[i|0x100]);\r
+       }\r
+\r
+       addr_A1 = old_A1;\r
+\r
+       add_job_940(JOB940_PICOSTATELOAD);\r
+}\r
+\r
+\r
+static void internal_reset(void)\r
+{\r
+       writebuff_ptr = 0;\r
+       ST_mode   = 0;\r
+       ST_status = 0;  /* normal mode */\r
+       ST_TA     = 0;\r
+       ST_TAC    = 0;\r
+       ST_TB     = 0;\r
+       ST_TBC    = 0;\r
+       dacen = 0;\r
+}\r
+\r
+\r
+extern char **g_argv;\r
+\r
+/* none of the functions in this file should be called before this one */\r
+void YM2612Init_940(int baseclock, int rate)\r
+{\r
+       printf("YM2612Init_940()\n");\r
+       //printf("sizeof(*shared_data): %i (%x)\n", sizeof(*shared_data), sizeof(*shared_data));\r
+       //printf("sizeof(*shared_ctl): %i (%x)\n", sizeof(*shared_ctl), sizeof(*shared_ctl));\r
+\r
+       Reset940(1);\r
+       Pause940(1);\r
+\r
+       gp2x_memregs[0x3B46>>1] = 0xffff; // clear pending DUALCPU interrupts for 940\r
+       gp2x_memregs[0x3B42>>1] = 0xffff; // enable DUALCPU interrupts for 940\r
+\r
+       gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller\r
+\r
+       if (shared_mem == NULL)\r
+       {\r
+               shared_mem = (unsigned char *) mmap(0, 0x210000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x3000000);\r
+               if(shared_mem == MAP_FAILED)\r
+               {\r
+                       printf("mmap(shared_data) failed with %i\n", errno);\r
+                       exit(1);\r
+               }\r
+               shared_data = (_940_data_t *) (shared_mem+0x100000);\r
+               /* this area must not get buffered on either side */\r
+               shared_ctl =  (_940_ctl_t *)  (shared_mem+0x200000);\r
+               crashed_940 = 1;\r
+       }\r
+\r
+       if (crashed_940)\r
+       {\r
+               unsigned char ucData[1024];\r
+               int nRead, i, nLen = 0;\r
+               char binpath[1024];\r
+               FILE *fp;\r
+\r
+               strncpy(binpath, g_argv[0], 1023);\r
+               binpath[1023] = 0;\r
+               for (i = strlen(binpath); i > 0; i--)\r
+                       if (binpath[i] == '/') { binpath[i] = 0; break; }\r
+               strcat(binpath, "/code940.bin");\r
+\r
+               fp = fopen(binpath, "rb");\r
+               if(!fp)\r
+               {\r
+                       memset(gp2x_screen, 0, 320*240);\r
+                       gp2x_text_out8(10, 100, "failed to open required file:");\r
+                       gp2x_text_out8(10, 110, "code940.bin");\r
+                       gp2x_video_flip();\r
+                       printf("failed to open %s\n", binpath);\r
+                       exit(1);\r
+               }\r
+\r
+               while(1)\r
+               {\r
+                       nRead = fread(ucData, 1, 1024, fp);\r
+                       if(nRead <= 0)\r
+                               break;\r
+                       memcpy(shared_mem + nLen, ucData, nRead);\r
+                       nLen += nRead;\r
+               }\r
+               fclose(fp);\r
+               crashed_940 = 0;\r
+       }\r
+\r
+       memset(shared_data, 0, sizeof(*shared_data));\r
+       memset(shared_ctl,  0, sizeof(*shared_ctl));\r
+\r
+       REGS = YM2612GetRegs();\r
+\r
+       ym2612_dacen  = &dacen;\r
+       ym2612_dacout = &dacout;\r
+\r
+       internal_reset();\r
+\r
+       /* now cause 940 to init it's ym2612 stuff */\r
+       shared_ctl->baseclock = baseclock;\r
+       shared_ctl->rate = rate;\r
+       shared_ctl->job = JOB940_YM2612INIT;\r
+       shared_ctl->busy = 1;\r
+\r
+       /* start the 940 */\r
+       Reset940(0);\r
+       Pause940(0);\r
+\r
+       // YM2612ResetChip_940(); // will be done on JOB940_YM2612INIT\r
+}\r
+\r
+\r
+void YM2612ResetChip_940(void)\r
+{\r
+       printf("YM2612ResetChip_940()\n");\r
+       if (shared_data == NULL) {\r
+               printf("YM2612ResetChip_940: reset before init?\n");\r
+               return;\r
+       }\r
+\r
+       if (shared_ctl->busy) wait_busy_940();\r
+\r
+       internal_reset();\r
+\r
+       add_job_940(JOB940_YM2612RESETCHIP);\r
+}\r
+\r
+\r
+void YM2612UpdateOne_940(short *buffer, int length, int stereo)\r
+{\r
+       int i, *mix_buffer = shared_data->mix_buffer;\r
+\r
+       //printf("YM2612UpdateOne_940()\n");\r
+       if (shared_ctl->busy) wait_busy_940();\r
+\r
+       //printf("940 (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);\r
+       //for (i = 0; i < 8; i++)\r
+       //      printf("%i ", shared_ctl->vstarts[i]);\r
+       //printf(")\n");\r
+\r
+       /* mix data from previous go */\r
+       if (stereo) {\r
+               int *mb = mix_buffer;\r
+               for (i = length; i > 0; i--) {\r
+                       int l, r;\r
+                       l = r = *buffer;\r
+                       l += *mb++, r += *mb++;\r
+                       Limit( l, MAXOUT, MINOUT );\r
+                       Limit( r, MAXOUT, MINOUT );\r
+                       *buffer++ = l; *buffer++ = r;\r
+               }\r
+       } else {\r
+               for (i = 0; i < length; i++) {\r
+                       int l = mix_buffer[i];\r
+                       l += buffer[i];\r
+                       Limit( l, MAXOUT, MINOUT );\r
+                       buffer[i] = l;\r
+               }\r
+       }\r
+\r
+       //printf("new writes: %i\n", writebuff_ptr);\r
+       if (shared_ctl->writebuffsel == 1) {\r
+               shared_ctl->writebuff0[writebuff_ptr] = 0xffff;\r
+       } else {\r
+               shared_ctl->writebuff1[writebuff_ptr] = 0xffff;\r
+       }\r
+       writebuff_ptr = 0;\r
+\r
+       /* give 940 another job */\r
+       shared_ctl->writebuffsel ^= 1;\r
+       shared_ctl->length = length;\r
+       shared_ctl->stereo = stereo;\r
+       add_job_940(JOB940_YM2612UPDATEONE);\r
+       //spend_cycles(512);\r
+       //printf("SRCPND: %08lx, INTMODE: %08lx, INTMASK: %08lx, INTPEND: %08lx\n",\r
+       //      gp2x_memregl[0x4500>>2], gp2x_memregl[0x4504>>2], gp2x_memregl[0x4508>>2], gp2x_memregl[0x4510>>2]);\r
+}\r
diff --git a/gp2x/940ctl_ym2612.h b/gp2x/940ctl_ym2612.h
new file mode 100644 (file)
index 0000000..7d94c13
--- /dev/null
@@ -0,0 +1,9 @@
+void YM2612Init_940(int baseclock, int rate);\r
+void YM2612ResetChip_940(void);\r
+void YM2612UpdateOne_940(short *buffer, int length, int stereo);\r
+\r
+int  YM2612Write_940(unsigned int a, unsigned int v);\r
+unsigned char YM2612Read_940(void);\r
+\r
+int  YM2612PicoTick_940(int n);\r
+void YM2612PicoStateLoad_940(void);\r
diff --git a/gp2x/940init.s b/gp2x/940init.s
new file mode 100644 (file)
index 0000000..e28b453
--- /dev/null
@@ -0,0 +1,174 @@
+.global code940\r
+\r
+code940:                          @ interrupt table:\r
+    b .b_reset                    @ reset\r
+    b .b_undef                    @ undefined instructions\r
+    b .b_swi                      @ software interrupt\r
+    b .b_pabort                   @ prefetch abort\r
+    b .b_dabort                   @ data abort\r
+    b .b_reserved                 @ reserved\r
+    b .b_irq                      @ IRQ\r
+    b .b_fiq                      @ FIQ\r
+\r
+@ test\r
+.b_reset:\r
+    mov     r12, #0\r
+    b       .Begin\r
+.b_undef:\r
+    mov     r12, #1\r
+    b       .Begin\r
+.b_swi:\r
+    mov     r12, #2\r
+    b       .Begin\r
+.b_pabort:\r
+    mov     r12, #3\r
+    b       .Begin\r
+.b_dabort:\r
+    mov     r12, #4\r
+    b       .Begin\r
+.b_reserved:\r
+    mov     r12, #5\r
+    b       .Begin\r
+.b_irq:\r
+    mov     r12, #6\r
+       mov     sp, #0x100000       @ reset stack\r
+       sub     sp, sp, #4\r
+    mov     r1, #0xbd000000     @ assume we live @ 0x3000000 bank\r
+    orr     r2, r1, #0x3B00\r
+    orr     r2, r2, #0x0046\r
+    mvn     r3, #0\r
+    strh    r3, [r2]            @ clear any pending interrupts from the DUALCPU unit\r
+    orr     r2, r1, #0x4500\r
+    str     r3, [r2]            @ clear all pending interrupts in irq controller's SRCPND register\r
+    orr     r2, r2, #0x0010\r
+    str     r3, [r2]            @ clear all pending interrupts in irq controller's INTPND register\r
+    b       .Enter\r
+.b_fiq:\r
+    mov     r12, #7\r
+    b       .Begin\r
+\r
+.Begin:\r
+       mov sp, #0x100000           @ set the stack top (1M)\r
+       sub sp, sp, #4              @ minus 4\r
+\r
+       @ set up memory region 0 -- the whole 4GB address space\r
+       mov r0, #(0x1f<<1)|1        @ region data\r
+       mcr p15, 0, r0, c6, c0, 0   @ opcode2 ~ data/instr\r
+       mcr p15, 0, r0, c6, c0, 1\r
+\r
+    @ set up region 1 which is the first 2 megabytes.\r
+       mov r0, #(0x14<<1)|1        @ region data\r
+       mcr p15, 0, r0, c6, c1, 0\r
+       mcr p15, 0, r0, c6, c1, 1\r
+\r
+    @ set up region 2: 64k 0x200000-0x210000\r
+       mov r0, #(0x0f<<1)|1\r
+    orr r0, r0, #0x200000\r
+       mcr p15, 0, r0, c6, c2, 0\r
+       mcr p15, 0, r0, c6, c2, 1\r
+\r
+    @ set up region 3: 64k 0xbd000000-0xbd010000 (hw control registers)\r
+       mov r0, #(0x0f<<1)|1\r
+    orr r0, r0, #0xbd000000\r
+       mcr p15, 0, r0, c6, c3, 0\r
+       mcr p15, 0, r0, c6, c3, 1\r
+\r
+    @ set region 1 to be cacheable (so the first 2M will be cacheable)\r
+       mov r0, #2\r
+       mcr p15, 0, r0, c2, c0, 0\r
+       mcr p15, 0, r0, c2, c0, 1\r
+\r
+    @ set region 1 to be bufferable too (only data)\r
+       mcr p15, 0, r0, c3, c0, 0\r
+\r
+    @ set protection, allow accsess only to regions 1 and 2\r
+       mov r0, #(3<<6)|(3<<4)|(3<<2)|(0)  @ data: [full, full, full, no access] for regions [3 2 1 0]\r
+       mcr p15, 0, r0, c5, c0, 0\r
+       mov r0, #(0<<6)|(0<<4)|(3<<2)|(0)  @ instructions: [no access, no, full, no]\r
+       mcr p15, 0, r0, c5, c0, 1\r
+\r
+       mrc p15, 0, r0, c1, c0, 0   @ fetch current control reg\r
+       orr r0, r0, #1              @ 0x00000001: enable protection unit\r
+       orr r0, r0, #4              @ 0x00000004: enable D cache\r
+       orr r0, r0, #0x1000         @ 0x00001000: enable I cache\r
+       orr r0, r0, #0xC0000000     @ 0xC0000000: async+fastbus\r
+       mcr p15, 0, r0, c1, c0, 0   @ set control reg\r
+\r
+    @ flush (invalidate) the cache (just in case)\r
+    mov r0, #0\r
+    mcr p15, 0, r0, c7, c6, 0\r
+\r
+.Enter:\r
+    mov r0, r12\r
+    bl Main940\r
+\r
+    @ we should never get here\r
+.b_deadloop:\r
+    b .b_deadloop\r
+\r
+\r
+\r
+@ so asm utils are also defined here:\r
+.global spend_cycles @ c\r
+\r
+spend_cycles:\r
+    mov     r0, r0, lsr #2  @ 4 cycles/iteration\r
+    sub     r0, r0, #2      @ entry/exit/init\r
+.sc_loop:\r
+    subs    r0, r0, #1\r
+    bpl     .sc_loop\r
+\r
+    bx      lr\r
+\r
+\r
+@ clean-flush function from ARM940T technical reference manual\r
+.global cache_clean_flush\r
+\r
+cache_clean_flush:\r
+    mov     r1, #0                  @ init line counter\r
+ccf_outer_loop:\r
+    mov     r0, #0                  @ segment counter\r
+ccf_inner_loop:\r
+    orr     r2, r1, r0              @ make segment and line address\r
+    mcr     p15, 0, r2, c7, c14, 2  @ clean and flush that line\r
+    add     r0, r0, #0x10           @ incremet secment counter\r
+    cmp     r0, #0x40               @ complete all 4 segments?\r
+    bne     ccf_inner_loop\r
+    add     r1, r1, #0x04000000     @ increment line counter\r
+    cmp     r1, #0                  @ complete all lines?\r
+    bne     ccf_outer_loop\r
+    bx      lr\r
+\r
+\r
+@ clean-only version\r
+.global cache_clean\r
+\r
+cache_clean:\r
+    mov     r1, #0                  @ init line counter\r
+cf_outer_loop:\r
+    mov     r0, #0                  @ segment counter\r
+cf_inner_loop:\r
+    orr     r2, r1, r0              @ make segment and line address\r
+    mcr     p15, 0, r2, c7, c10, 2  @ clean that line\r
+    add     r0, r0, #0x10           @ incremet secment counter\r
+    cmp     r0, #0x40               @ complete all 4 segments?\r
+    bne     cf_inner_loop\r
+    add     r1, r1, #0x04000000     @ increment line counter\r
+    cmp     r1, #0                  @ complete all lines?\r
+    bne     cf_outer_loop\r
+    bx      lr\r
+\r
+\r
+.global wait_irq\r
+\r
+wait_irq:\r
+    mrs     r0, cpsr\r
+    bic     r0, r0, #0x80\r
+    msr     cpsr_c, r0               @ enable interrupts\r
+\r
+    mov     r0, #0\r
+    mcr     p15, 0, r0, c7, c0, 4   @ wait for IRQ\r
+@    mcr     p15, 0, r0, c15, c8, 2\r
+    b       .b_reserved\r
+\r
+.pool\r
diff --git a/gp2x/940shared.h b/gp2x/940shared.h
new file mode 100644 (file)
index 0000000..71114da
--- /dev/null
@@ -0,0 +1,33 @@
+#include "../../Pico/sound/ym2612.h"\r
+\r
+enum _940_job_t {\r
+       JOB940_YM2612INIT = 1,\r
+       JOB940_YM2612RESETCHIP,\r
+       JOB940_YM2612UPDATEONE,\r
+       JOB940_PICOSTATELOAD,\r
+       JOB940_NUMJOBS\r
+};\r
+\r
+\r
+typedef struct\r
+{\r
+       YM2612  ym2612;                                 /* current state of the emulated YM2612 */\r
+       int             mix_buffer[44100/50*2]; /* this is where the YM2612 samples will be mixed to */\r
+} _940_data_t;\r
+\r
+\r
+typedef struct\r
+{\r
+       int             job;                                    /* a job for second core */\r
+       int             busy;                                   /* busy status of the 940 core */\r
+       int             length;                                 /* number of samples to mix (882 max) */\r
+       int             stereo;                                 /* mix samples as stereo, doubles sample count automatically */\r
+       int             baseclock;                              /* ym2612 settings */\r
+       int             rate;\r
+       int             writebuffsel;                   /* which write buffer to use (from 940 side) */\r
+       UINT16  writebuff0[2048];               /* 1024 for savestates, 1024 extra */\r
+       UINT16  writebuff1[2048];\r
+       int             vstarts[8];                             /* debug: number of starts from each of 8 vectors */\r
+       int             loopc;                                  /* debug: main loop counter */\r
+       int             waitc;                                  /* debug: wait loop counter */\r
+} _940_ctl_t;\r
diff --git a/gp2x/Makefile b/gp2x/Makefile
new file mode 100644 (file)
index 0000000..c634c3d
--- /dev/null
@@ -0,0 +1,191 @@
+\r
+# you may or may not need to change this\r
+#devkit_path = x:/stuff/dev/devkitgp2x/\r
+devkit_path = /usr/local/devkitPro/devkitGP2X/\r
+lgcc_path = $(devkit_path)lib/gcc/arm-linux/4.0.3/\r
+CROSS = arm-linux-\r
+#CROSS = $(devkit_path)bin/arm-linux-\r
+\r
+# settings\r
+dprint = 1\r
+#mz80 = 1\r
+#debug_cyclone = 1\r
+asm_memory = 1\r
+asm_render = 1\r
+asm_ym2612 = 1\r
+#profile = 1\r
+#use_musashi = 1\r
+#up = 1\r
+\r
+DEFINC = -I../.. -I. -D__GP2X__ -D_UNZIP_SUPPORT # -DBENCHMARK\r
+COPT_COMMON = -static -s -O3 -ftracer -fstrength-reduce -Wall -funroll-loops -fomit-frame-pointer -fstrict-aliasing -ffast-math\r
+ifeq "$(profile)" "1"\r
+COPT_COMMON += -fprofile-generate\r
+endif\r
+ifeq "$(profile)" "2"\r
+COPT_COMMON += -fprofile-use\r
+endif\r
+COPT = $(COPT_COMMON) -mtune=arm920t\r
+ASOPT = -mcpu=arm920t -mfloat-abi=soft\r
+GCC = $(CROSS)gcc\r
+STRIP = $(CROSS)strip\r
+AS = $(CROSS)as\r
+LD = $(CROSS)ld\r
+OBJCOPY = $(CROSS)objcopy\r
+\r
+# frontend\r
+OBJS += main.o menu.o gp2x.o usbjoy.o emu.o squidgehack.o asmutils.o cpuctrl.o\r
+# 940 core control\r
+OBJS += 940ctl_ym2612.o\r
+# Pico\r
+OBJS += ../../Pico/Area.o ../../Pico/Cart.o ../../Pico/Utils.o ../../Pico/Memory.o ../../Pico/Misc.o \\r
+               ../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o\r
+# asm stuff\r
+ifeq "$(asm_render)" "1"\r
+DEFINC += -D_ASM_DRAW_C\r
+OBJS += ../../Pico/draw_asm.o ../../Pico/draw2_asm.o\r
+endif\r
+ifeq "$(asm_memory)" "1"\r
+DEFINC += -D_ASM_MEMORY_C\r
+OBJS += ../../Pico/memory_asm.o\r
+endif\r
+ifeq "$(asm_ym2612)" "1"\r
+DEFINC += -D_ASM_YM2612_C\r
+OBJS += ../../Pico/sound/ym2612_asm.o\r
+endif\r
+# Pico - sound\r
+OBJS += ../../Pico/sound/sound.o ../../Pico/sound/sn76496.o ../../Pico/sound/ym2612.o\r
+# zlib\r
+OBJS += ../../zlib/gzio.o ../../zlib/inffast.o ../../zlib/inflate.o ../../zlib/inftrees.o ../../zlib/trees.o \\r
+       ../../zlib/deflate.o ../../zlib/crc32.o ../../zlib/adler32.o ../../zlib/zutil.o ../../zlib/compress.o\r
+# unzip\r
+OBJS += ../../unzip/unzip.o\r
+# CPU cores\r
+ifeq "$(use_musashi)" "1"\r
+DEFINC += -DEMU_M68K\r
+OBJS += _build\m68kcpu.o _build\m68kopac.o _build\m68kopdm.o _build\m68kopnz.o _build\m68kops.o\r
+else\r
+DEFINC += -DEMU_C68K\r
+OBJS += ../../cpu/Cyclone/proj/Cyclone.o\r
+endif\r
+# drz80/mz80\r
+ifeq "$(mz80)" "1"\r
+DEFINC += -D_USE_MZ80\r
+OBJS += ../../cpu/mz80/mz80.o\r
+else\r
+DEFINC += -D_USE_DRZ80\r
+OBJS += ../../cpu/DrZ80/drz80.o\r
+endif\r
+\r
+all: PicoDrive.gpe code940.bin\r
+\r
+PicoDrive.gpe : $(OBJS)\r
+       @echo $@\r
+       @$(GCC) $(COPT) $(OBJS) $(PRELIBS) -lm -o $@\r
+       @$(STRIP) $@\r
+#      @$(GCC) $(COPT) $(OBJS) $(PRELIBS) -lm -o PicoDrive_.gpe\r
+#      @gpecomp PicoDrive_.gpe $@\r
+ifeq "$(up)" "1"\r
+       @cmd //C copy $@ \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\\r
+endif\r
+\r
+up: up940\r
+       @cmd //C copy PicoDrive.gpe \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\\r
+up940:\r
+       @cmd //C copy code940.bin \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\\r
+\r
+testrefr.gpe : test.o gp2x.o asmutils.o\r
+       @echo $@\r
+       @$(GCC) $(COPT) $^ $(PRELIBS) -o $@\r
+       @$(STRIP) $@\r
+\r
+.c.o:\r
+       @echo $<\r
+       @$(GCC) $(COPT) $(DEFINC) -c $< -o $@\r
+.s.o:\r
+       @echo $<\r
+       @$(GCC) $(COPT) $(DEFINC) -c $< -o $@\r
+\r
+../../Pico/draw_asm.o : ../../Pico/Draw.s\r
+       @echo $<\r
+       @$(AS) $(ASOPT) $< -o $@\r
+../../Pico/draw2_asm.o : ../../Pico/Draw2.s\r
+       @echo $<\r
+       @$(AS) $(ASOPT) $< -o $@\r
+../../Pico/memory_asm.o : ../../Pico/Memory.s\r
+       @echo $<\r
+       @$(AS) $(ASOPT) $< -o $@\r
+../../Pico/sound/ym2612_asm.o : ../../Pico/sound/ym2612.s\r
+       @echo $<\r
+       @$(AS) $(ASOPT) $< -o $@\r
+\r
+# build Cyclone\r
+../../cpu/Cyclone/proj/Cyclone.s :\r
+       @echo building Cyclone...\r
+       @make -C ../../cpu/Cyclone/proj -f Makefile.linux\r
+\r
+\r
+# stuff for 940 core\r
+\r
+# init, emu_control, emu\r
+OBJS940 += 940init.o 940.o 940ym2612.o\r
+# the asm code seems to be faster when run on 920, but not on 940 for some reason\r
+# OBJS940 += ../../Pico/sound/ym2612_asm.o\r
+\r
+# uClibc library code\r
+OBJS940 += uClibc/memset.o uClibc/s_floor.o uClibc/e_pow.o uClibc/e_sqrt.o uClibc/s_fabs.o\r
+OBJS940 += uClibc/s_scalbn.o uClibc/s_copysign.o uClibc/k_sin.o uClibc/k_cos.o uClibc/s_sin.o\r
+OBJS940 += uClibc/e_rem_pio2.o uClibc/k_rem_pio2.o uClibc/e_log.o uClibc/wrappers.o\r
+\r
+code940.bin : code940.gpe\r
+       @echo $@\r
+       @$(OBJCOPY) -O binary $< $@\r
+\r
+code940.gpe : $(OBJS940)\r
+       @echo $@\r
+       @$(LD) -static -e code940 -Ttext 0x0 $^ -L$(lgcc_path) -lgcc -o $@\r
+\r
+940ym2612.o : ../../Pico/sound/ym2612.c\r
+       @echo $@\r
+       @$(GCC) $(COPT_COMMON) -mtune=arm940t $(DEFINC) -DEXTERNAL_YM2612 -c $< -o $@\r
+\r
+\r
+# cleanup\r
+clean: clean_pd clean_940\r
+tidy: tidy_pd tidy_940\r
+\r
+clean_pd: tidy_pd\r
+       @$(RM) PicoDrive.gpe\r
+tidy_pd:\r
+       @$(RM) $(OBJS)\r
+#      @make -C ../../cpu/Cyclone/proj -f Makefile.linux clean\r
+\r
+clean_940: tidy_940\r
+       @$(RM) code940.bin\r
+tidy_940:\r
+       @$(RM) code940.gpe $(OBJS940)\r
+\r
+clean_prof:\r
+       find ../.. -name '*.gcno' -delete\r
+       find ../.. -name '*.gcda' -delete\r
+\r
+# test\r
+usbjoy.o : usbjoy.c\r
+       @echo $<\r
+       @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@\r
+\r
+../../Pico/Cart.o : ../../Pico/Cart.c\r
+       @echo $<\r
+       @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@\r
+\r
+../../zlib/trees.o : ../../zlib/trees.c\r
+       @echo $<\r
+       @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@\r
+\r
+uClibc/e_pow.o : uClibc/e_pow.c\r
+       @echo $<\r
+       @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@\r
+\r
+uClibc/e_sqrt.o : uClibc/e_sqrt.c\r
+       @echo $<\r
+       @$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@\r
diff --git a/gp2x/asmutils.h b/gp2x/asmutils.h
new file mode 100644 (file)
index 0000000..d922690
--- /dev/null
@@ -0,0 +1,12 @@
+// (c) Copyright 2006 notaz, All rights reserved.\r
+// Free for non-commercial use.\r
+\r
+// For commercial use, separate licencing terms must be obtained.\r
+\r
+void vidConvCpyRGB32  (void *to, void *from, int pixels);\r
+void vidConvCpyRGB32sh(void *to, void *from, int pixels);\r
+void vidConvCpyRGB32hi(void *to, void *from, int pixels);\r
+void vidCpyM2_40col(void *dest, void *src);\r
+void vidCpyM2_32col(void *dest, void *src);\r
+void vidCpyM2_32col_nobord(void *dest, void *src);\r
+void spend_cycles(int c); // utility\r
diff --git a/gp2x/asmutils.s b/gp2x/asmutils.s
new file mode 100644 (file)
index 0000000..e1e0945
--- /dev/null
@@ -0,0 +1,210 @@
+@ some color conversion and blitting routines\r
+\r
+@ (c) Copyright 2006, notaz\r
+@ All Rights Reserved\r
+\r
+\r
+@ Convert 0000bbb0 ggg0rrr0 0000bbb0 ggg0rrr0\r
+@ to      00000000 rrr00000 ggg00000 bbb00000 ...\r
+\r
+@ lr =  0x00e000e0, out: r3=lower_pix, r2=higher_pix; trashes rin\r
+@ if sh==2, r8=0x00404040 (sh!=0 destroys flags!)\r
+.macro convRGB32_2 rin sh=0\r
+    and     r2,  lr, \rin, lsr #4 @ blue\r
+    and     r3,  \rin, lr\r
+    orr     r2,  r2,   r3, lsl #8         @ g0b0g0b0\r
+\r
+    mov     r3,  r2,  lsl #16             @ g0b00000\r
+    and     \rin,lr,  \rin, ror #12       @ 00r000r0 (reversed)\r
+    orr     r3,  r3,  \rin, lsr #16       @ g0b000r0\r
+.if \sh == 1\r
+    mov     r3,  r3,  ror #17             @ shadow mode\r
+.elseif \sh == 2\r
+    adds    r3,  r3,  #0x40000000         @ green\r
+    orrcs   r3,  r3,  #0xe0000000\r
+    mov     r3,  r3,  ror #8\r
+    adds    r3,  r3,  #0x40000000\r
+    orrcs   r3,  r3,  #0xe0000000\r
+    mov     r3,  r3,  ror #16\r
+    adds    r3,  r3,  #0x40000000\r
+    orrcs   r3,  r3,  #0xe0000000\r
+    mov     r3,  r3,  ror #24\r
+.else\r
+    mov     r3,  r3,  ror #16             @ r3=low\r
+.endif\r
+\r
+    orr     r3,  r3,   r3, lsr #3\r
+    str     r3, [r0], #4\r
+\r
+    mov     r2,  r2,  lsr #16\r
+    orr     r2,  r2,  \rin, lsl #16\r
+.if \sh == 1\r
+    mov     r2,  r2,  lsr #1\r
+.elseif \sh == 2\r
+    mov     r2,  r2,  ror #8\r
+    adds    r2,  r2,  #0x40000000         @ blue\r
+    orrcs   r2,  r2,  #0xe0000000\r
+    mov     r2,  r2,  ror #8\r
+    adds    r2,  r2,  #0x40000000\r
+    orrcs   r2,  r2,  #0xe0000000\r
+    mov     r2,  r2,  ror #8\r
+    adds    r2,  r2,  #0x40000000\r
+    orrcs   r2,  r2,  #0xe0000000\r
+    mov     r2,  r2,  ror #8\r
+.endif\r
+\r
+    orr     r2,  r2,   r2,  lsr #3\r
+    str     r2, [r0], #4\r
+.endm\r
+\r
+\r
+.global vidConvCpyRGB32 @ void *to, void *from, int pixels\r
+\r
+vidConvCpyRGB32:\r
+    stmfd   sp!, {r4-r7,lr}\r
+\r
+    mov     r12, r2, lsr #3 @ repeats\r
+    mov     lr, #0x00e00000\r
+    orr     lr, lr, #0x00e0\r
+\r
+.loopRGB32:\r
+    subs    r12, r12, #1\r
+\r
+    ldmia    r1!, {r4-r7}\r
+    convRGB32_2 r4\r
+    convRGB32_2 r5\r
+    convRGB32_2 r6\r
+    convRGB32_2 r7\r
+\r
+    bgt     .loopRGB32\r
+\r
+    ldmfd   sp!, {r4-r7,lr}\r
+    bx      lr\r
+\r
+\r
+.global vidConvCpyRGB32sh @ void *to, void *from, int pixels\r
+\r
+vidConvCpyRGB32sh:\r
+    stmfd   sp!, {r4-r7,lr}\r
+\r
+    mov     r12, r2, lsr #3 @ repeats\r
+    mov     lr, #0x00e00000\r
+    orr     lr, lr, #0x00e0\r
+\r
+.loopRGB32sh:\r
+    subs    r12, r12, #1\r
+\r
+    ldmia    r1!, {r4-r7}\r
+    convRGB32_2 r4, 1\r
+    convRGB32_2 r5, 1\r
+    convRGB32_2 r6, 1\r
+    convRGB32_2 r7, 1\r
+\r
+    bgt     .loopRGB32sh\r
+\r
+    ldmfd   sp!, {r4-r7,lr}\r
+    bx      lr\r
+\r
+\r
+.global vidConvCpyRGB32hi @ void *to, void *from, int pixels\r
+\r
+vidConvCpyRGB32hi:\r
+    stmfd   sp!, {r4-r7,lr}\r
+\r
+    mov     r12, r2, lsr #3 @ repeats\r
+    mov     lr, #0x00e00000\r
+    orr     lr, lr, #0x00e0\r
+\r
+.loopRGB32hi:\r
+     ldmia    r1!, {r4-r7}\r
+    convRGB32_2 r4, 2\r
+    convRGB32_2 r5, 2\r
+    convRGB32_2 r6, 2\r
+    convRGB32_2 r7, 2\r
+\r
+    subs    r12, r12, #1\r
+    bgt     .loopRGB32hi\r
+\r
+    ldmfd   sp!, {r4-r7,lr}\r
+    bx      lr\r
+\r
+\r
+@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r
+\r
+\r
+@ mode2 blitter for 40 cols\r
+.global vidCpyM2_40col @ void *dest, void *src\r
+\r
+vidCpyM2_40col:\r
+    stmfd   sp!, {r4-r6,lr}\r
+\r
+    mov     r12, #224       @ lines\r
+    add     r1, r1, #8\r
+\r
+vidCpyM2_40_loop_out:\r
+    mov     r6, #10\r
+vidCpyM2_40_loop:\r
+    subs    r6, r6, #1\r
+     ldmia    r1!, {r2-r5}\r
+     stmia    r0!, {r2-r5}\r
+     ldmia    r1!, {r2-r5}\r
+     stmia    r0!, {r2-r5}\r
+    bne     vidCpyM2_40_loop\r
+    subs    r12,r12,#1\r
+    add     r1, r1, #8\r
+    bne     vidCpyM2_40_loop_out\r
+\r
+    ldmfd   sp!, {r4-r6,lr}\r
+    bx      lr\r
+\r
+\r
+@ mode2 blitter for 32 cols\r
+.global vidCpyM2_32col @ void *dest, void *src\r
+\r
+vidCpyM2_32col:\r
+    stmfd   sp!, {r4-r6,lr}\r
+\r
+    mov     r12, #224       @ lines\r
+    add     r1, r1, #8\r
+    add     r0, r0, #32\r
+\r
+vidCpyM2_32_loop_out:\r
+    mov     r6, #8\r
+vidCpyM2_32_loop:\r
+    subs    r6, r6, #1\r
+     ldmia    r1!, {r2-r5}\r
+     stmia    r0!, {r2-r5}\r
+     ldmia    r1!, {r2-r5}\r
+     stmia    r0!, {r2-r5}\r
+    bne     vidCpyM2_32_loop\r
+    subs    r12,r12,#1\r
+    add     r0, r0, #64\r
+    add     r1, r1, #8+64\r
+    bne     vidCpyM2_32_loop_out\r
+\r
+    ldmfd   sp!, {r4-r6,lr}\r
+    bx      lr\r
+\r
+\r
+@ mode2 blitter for 32 cols with no borders\r
+.global vidCpyM2_32col_nobord @ void *dest, void *src\r
+\r
+vidCpyM2_32col_nobord:\r
+    stmfd   sp!, {r4-r6,lr}\r
+\r
+    mov     r12, #224       @ lines\r
+    add     r1, r1, #8\r
+    b       vidCpyM2_32_loop_out\r
+\r
+\r
+@ test\r
+.global spend_cycles @ c\r
+\r
+spend_cycles:\r
+    mov     r0, r0, lsr #2  @ 4 cycles/iteration\r
+    sub     r0, r0, #2      @ entry/exit/init\r
+.sc_loop:\r
+    subs    r0, r0, #1\r
+    bpl     .sc_loop\r
+\r
+    bx      lr\r
diff --git a/gp2x/config.txt b/gp2x/config.txt
new file mode 100644 (file)
index 0000000..0874946
--- /dev/null
@@ -0,0 +1,138 @@
+As PicoDrive is multiplatform emulator, this is GP2X specific part of readme\r
+about configuration.\r
+\r
+\r
+Configuration\r
+-------------\r
+\r
+1. "Renderer"\r
+8bit fast:\r
+This enables alternative heavily optimized tile-based renderer, which renders\r
+pixels not line-by-line (this is what accurate renderers do), but in 8x8 tiles,\r
+which is much faster. But because of the way it works it can't render any\r
+mid-frame image changes (raster effects), so it is useful only with some games.\r
+\r
+Other two are accurate line-based renderers. The 8bit is faster but does not\r
+run well with some games like Street Racer.\r
+\r
+2. "Accurate timing"\r
+This adds some more emulation precision, but slows the emulation down. Whithout\r
+this option some games do not boot (Red Zone for example), others have sound\r
+problems.\r
+\r
+3. "Accurate sprites"\r
+This option improves emulation of sprite priorities, it also enables emulation\r
+of sprite collision bit. If you see one sprite being drawn incorrectly above\r
+the other (often seen in Sonic 3D Blast), you can enable this to fix the problem.\r
+This only works with the default renderer (see first option).\r
+\r
+4. "Show FPS"\r
+Self-explanatory. Format is XX/YY, where XX is the number of rendered frames and\r
+YY is the number of emulated frames per second.\r
+\r
+5. "Frameskip"\r
+How many frames to skip rendering before displaying another.\r
+"Auto" is recommended.\r
+\r
+6. "Enable sound"\r
+Does what it says. You must enable at least YM2612 or SN76496 (in advanced options,\r
+see below) for this to make sense.\r
+\r
+7. "Sound Quality"\r
+Sound rate and stereo mode. If you want 44100Hz sound, it is recommended to enable\r
+the second core (next option).\r
+\r
+8. "Use ARM940 core for sound"\r
+This option causes PicoDrive to use ARM940T core (GP2X's second CPU) for sound \r
+(i.e. to generate YM2612 samples) to improve performance noticeably.\r
+\r
+9. "6 button pad"\r
+If you enable this, games will think that 6 button gamepad is connected. If you\r
+go and reconfigure your keys, you will be able to bind X,Y,Z and mode actions.\r
+\r
+10. "Genesis Region"\r
+This option lets you force the game to think it is running on machine from the\r
+specified region.\r
+\r
+11. "Use SRAM savestates"\r
+This will automatically read/write SRAM savestates for games which are using them.\r
+SRAM is saved whenever you pause your game or exit the emulator.\r
+\r
+12. "GP2X CPU clocks"\r
+Here you can change clocks of both GP2X's CPUs. Larger values increase performance.\r
+There is no separate option for the second CPU because both CPUs use the same clock\r
+source. Setting this option to 200 will cause PicoDrive NOT to change GP2X's clocks\r
+at all.\r
+\r
+13. "[advanced options]"\r
+Enters advanced options menu (see below).\r
+\r
+14. "Save cfg as default"\r
+If you save your config here it will be loaded on next ROM load, but only if there\r
+is no game specific config saved (which will be loaded in that case).\r
+\r
+15. "Save cfg for current game only"\r
+Whenever you load current ROM again these settings will be loaded (squidgehack and\r
+RAM settings will not take effect until emulator is restarted).\r
+\r
+Advanced configuration\r
+----------------------\r
+\r
+Enter [advanced options] in config menu to see these options.\r
+\r
+1. "Scale 32 column mode"\r
+This enables hardware scaling for lower-res genesis mode (where width is\r
+32 8-pixel tiles, instead of 40 in other mode).\r
+\r
+2. "Gamma correction"\r
+Alters image gamma through GP2X hardware. Larger values make image to look brighter,\r
+lower - darker (default is 1.0).\r
+\r
+3. "Emulate Z80"\r
+Enables emulation of Z80 chip, which was mostly used to drive the other sound chips.\r
+Some games do complex sync with it, so you must enable it even if you don't use\r
+sound to be able to play them.\r
+\r
+4. "Emulate YM2612 (FM)"\r
+This enables emulation of six-channel FM sound synthesizer chip, which was used to\r
+produce sound effects and music.\r
+\r
+5. "Emulate SN76496 (PSG)"\r
+This enables emulation of additional sound chip for additional effects.\r
+\r
+Note: if you change sound settings AFTER loading a ROM, you may need to reset\r
+game to get sound. This is because most games initialize sound chips on\r
+startup, and this data is lost when sound chips are being enabled/disabled.\r
+\r
+6. "gzip savestates"\r
+This will always apply gzip compression on your savestates, allowing you to\r
+save some space and load/save time.\r
+\r
+7. "USB joy controls player X"\r
+If you are able to use USB joysticks with your GP2X, this options selects which\r
+player the joystick controls.\r
+\r
+8. "Don't save config on exit"\r
+This will disable config autowrite on exit (which might cause SD card corruption\r
+according to DaveC).\r
+\r
+9. "craigix's RAM timings"\r
+This overclocks the GP2X RAM chips, but may cause instability. Recommended if you\r
+use the second core for sound. Needs emulator restart to take effect.\r
+See this thread:\r
+http://www.gp32x.com/board/index.php?showtopic=32319\r
+\r
+10. "squidgehack"\r
+Well known way to improve the GP2X performance. You must restart the emulator\r
+for the change of this option to take effect.\r
+\r
+\r
+Key configuration\r
+-----------------\r
+\r
+When you select "Configure controls" from the menu, you enter a key configuration\r
+mode, where you use SELECT to change an action, and then press a key you like to\r
+bind to that action. You can press the same key again to unbind. Select "DONE"\r
+action and press any key to finish.\r
+\r
+\r
diff --git a/gp2x/cpuctrl.c b/gp2x/cpuctrl.c
new file mode 100644 (file)
index 0000000..7bbdb67
--- /dev/null
@@ -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 <sys/mman.h>
+#include <math.h>
+#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 (file)
index 0000000..3912109
--- /dev/null
@@ -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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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 (file)
index 0000000..5b482a5
--- /dev/null
@@ -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 (file)
index 0000000..0e4fdd7
--- /dev/null
@@ -0,0 +1,1121 @@
+// (c) Copyright 2006 notaz, All rights reserved.\r
+// Free for non-commercial use.\r
+\r
+// For commercial use, separate licencing terms must be obtained.\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <sys/time.h>\r
+#include <linux/limits.h>\r
+#include <ctype.h>\r
+#include <unistd.h>\r
+\r
+#include <stdarg.h>\r
+\r
+#include "emu.h"\r
+#include "gp2x.h"\r
+#include "usbjoy.h"\r
+#include "menu.h"\r
+#include "asmutils.h"\r
+#include "cpuctrl.h"\r
+\r
+#include "Pico/PicoInt.h"\r
+#include "zlib/zlib.h"\r
+\r
+\r
+#ifdef BENCHMARK\r
+#define OSD_FPS_X 220\r
+#else\r
+#define OSD_FPS_X 260\r
+#endif\r
+\r
+// PicoPad[] format: SACB RLDU\r
+char *actionNames[] = {\r
+       "UP", "DOWN", "LEFT", "RIGHT", "B", "C", "A", "START",\r
+       0, 0, 0, 0, 0, 0, 0, 0, // Z, Y, X, MODE (enabled only when needed), ?, ?, ?, ?\r
+       0, 0, 0, 0, 0, 0, 0, "ENTER MENU", // player2_flag, ?, ?, ?, ?, ?, ?, menu\r
+       "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
+       "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
+};\r
+\r
+int engineState;\r
+int select_exits = 0;\r
+char *PicoConfigFile = "picoconfig.bin";\r
+currentConfig_t currentConfig;\r
+\r
+char romFileName[PATH_MAX];\r
+unsigned char *rom_data = NULL;\r
+\r
+extern int crashed_940;\r
+\r
+static char noticeMsg[64];                                     // notice msg to draw\r
+static struct timeval noticeMsgTime = { 0, 0 };        // when started showing\r
+static int reset_timing, osd_fps_x;\r
+static int combo_keys = 0, combo_acts = 0;     // keys and actions which need button combos\r
+static int gp2x_old_gamma = 100;\r
+static unsigned char *movie_data = NULL;\r
+static int movie_size = 0;\r
+static int frame_count = 0;\r
+unsigned char *framebuff = 0;  // temporary buffer for alt renderer\r
+int state_slot = 0;\r
+\r
+/*\r
+// tmp\r
+static FILE *logf = NULL;\r
+\r
+void pprintf(char *texto, ...)\r
+{\r
+       va_list args;\r
+\r
+       va_start(args,texto);\r
+       vfprintf(logf,texto,args);\r
+       va_end(args);\r
+       fflush(logf);\r
+       sync();\r
+}\r
+*/\r
+// utilities\r
+static void strlwr(char* string)\r
+{\r
+       while ( (*string++ = (char)tolower(*string)) );\r
+}\r
+\r
+static int try_rfn_ext(char *ext)\r
+{\r
+       FILE *tmp;\r
+       char *p;\r
+\r
+       p = romFileName + strlen(romFileName) - 4;\r
+       if (p < romFileName) p = romFileName;\r
+       strcpy(p, ext);\r
+\r
+       if((tmp = fopen(romFileName, "rb"))) {\r
+               fclose(tmp);\r
+               return 1;\r
+       }\r
+       return 0;\r
+}\r
+\r
+int emu_ReloadRom(void)\r
+{\r
+       unsigned int rom_size = 0;\r
+       char ext[5], *p;\r
+       FILE *rom;\r
+       int ret;\r
+\r
+       printf("emu_ReloadRom(%s)\n", romFileName);\r
+\r
+       // detect wrong extensions (.srm and .mds)\r
+       p = romFileName + strlen(romFileName) - 4;\r
+       if (p < romFileName) p = romFileName;\r
+       strncpy(ext, p, 4);\r
+       ext[4] = 0;\r
+       strlwr(ext);\r
+\r
+       if(!strcmp(ext, ".srm") || !strcmp(ext, "s.gz") || !strcmp(ext, ".mds")) { // s.gz ~ .mds.gz\r
+               sprintf(menuErrorMsg, "Not a ROM selected.");\r
+               return 0;\r
+       }\r
+\r
+       // check for movie file\r
+       if(movie_data) {\r
+               free(movie_data);\r
+               movie_data = 0;\r
+       }\r
+       if(!strcmp(ext, ".gmv")) {\r
+               // check for both gmv and rom\r
+               int dummy;\r
+               FILE *movie_file = fopen(romFileName, "rb");\r
+               if(!movie_file) {\r
+                       sprintf(menuErrorMsg, "Failed to open movie.");\r
+                       return 0;\r
+               }\r
+               fseek(movie_file, 0, SEEK_END);\r
+               movie_size = ftell(movie_file);\r
+               fseek(movie_file, 0, SEEK_SET);\r
+               if(movie_size < 64+3) {\r
+                       sprintf(menuErrorMsg, "Invalid GMV file.");\r
+                       fclose(movie_file);\r
+                       return 0;\r
+               }\r
+               movie_data = malloc(movie_size);\r
+               if(movie_data == NULL) {\r
+                       sprintf(menuErrorMsg, "low memory.");\r
+                       fclose(movie_file);\r
+                       return 0;\r
+               }\r
+               fread(movie_data, 1, movie_size, movie_file);\r
+               fclose(movie_file);\r
+               if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {\r
+                       sprintf(menuErrorMsg, "Invalid GMV file.");\r
+                       return 0;\r
+               }\r
+               dummy = try_rfn_ext(".zip") || try_rfn_ext(".bin") ||\r
+                       try_rfn_ext(".smd") || try_rfn_ext(".gen");\r
+               if (!dummy) {\r
+                       sprintf(menuErrorMsg, "Could't find a ROM for movie.");\r
+                       return 0;\r
+               }\r
+       }\r
+\r
+       rom = fopen(romFileName, "rb");\r
+       if(!rom) {\r
+               sprintf(menuErrorMsg, "Failed to open rom.");\r
+               return 0;\r
+       }\r
+\r
+       if(rom_data) {\r
+               free(rom_data);\r
+               rom_data = 0;\r
+               rom_size = 0;\r
+       }\r
+\r
+       // zipfile support\r
+       if(!strcasecmp(ext, ".zip")) {\r
+               fclose(rom);\r
+               ret = CartLoadZip(romFileName, &rom_data, &rom_size);\r
+               if(ret) {\r
+                       if (ret == 4) strcpy(menuErrorMsg, "No ROMs in zip found.");\r
+                       else sprintf(menuErrorMsg, "Unzip failed with code %i", ret);\r
+                       printf("%s\n", menuErrorMsg);\r
+                       return 0;\r
+               }\r
+       } else {\r
+               if( (ret = PicoCartLoad(rom, &rom_data, &rom_size)) ) {\r
+                       sprintf(menuErrorMsg, "PicoCartLoad() failed.");\r
+                       printf("%s\n", menuErrorMsg);\r
+                       fclose(rom);\r
+                       return 0;\r
+               }\r
+               fclose(rom);\r
+       }\r
+\r
+       // detect wrong files (Pico crashes on very small files), also see if ROM EP is good\r
+       if(rom_size <= 0x200 || strncmp((char *)rom_data, "Pico", 4) == 0 ||\r
+         ((*(unsigned short *)(rom_data+4)<<16)|(*(unsigned short *)(rom_data+6))) >= (int)rom_size) {\r
+               if (rom_data) free(rom_data);\r
+               rom_data = 0;\r
+               sprintf(menuErrorMsg, "Not a ROM selected.");\r
+               return 0;\r
+       }\r
+\r
+       printf("PicoCartInsert(%p, %d);\n", rom_data, rom_size);\r
+       if(PicoCartInsert(rom_data, rom_size)) {\r
+               sprintf(menuErrorMsg, "Failed to load ROM.");\r
+               return 0;\r
+       }\r
+\r
+       // load config for this ROM\r
+       ret = emu_ReadConfig(1);\r
+       if (!ret)\r
+               emu_ReadConfig(0);\r
+\r
+       // emu_ReadConfig() might have messed currentConfig.lastRomFile\r
+       strncpy(currentConfig.lastRomFile, romFileName, sizeof(currentConfig.lastRomFile)-1);\r
+       currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0;\r
+\r
+       // additional movie stuff\r
+       if(movie_data) {\r
+               if(movie_data[0x14] == '6')\r
+                    PicoOpt |=  0x20; // 6 button pad\r
+               else PicoOpt &= ~0x20;\r
+               if(movie_data[0xF] >= 'A') {\r
+                       //Pico.m.pal = movie_data[0x16] >> 7;\r
+                       // TODO: bits 6 & 5\r
+               }\r
+               strcpy(noticeMsg, "MOVIE LOADED");\r
+       }\r
+       else\r
+       {\r
+               if(Pico.m.pal) {\r
+                       strcpy(noticeMsg, "PAL SYSTEM / 50 FPS");\r
+               } else {\r
+                       strcpy(noticeMsg, "NTSC SYSTEM / 60 FPS");\r
+               }\r
+       }\r
+       gettimeofday(&noticeMsgTime, 0);\r
+\r
+       // load SRAM for this ROM\r
+       if(currentConfig.EmuOpt & 1)\r
+               emu_SaveLoadGame(1, 1);\r
+\r
+       frame_count = 0;\r
+\r
+       return 1;\r
+}\r
+\r
+\r
+void emu_Init(void)\r
+{\r
+       // make temp buffer for alt renderer\r
+       framebuff = malloc((8+320)*(8+240+8));\r
+       if (!framebuff)\r
+       {\r
+               printf("framebuff == 0\n");\r
+       }\r
+\r
+       PicoInit();\r
+\r
+//     logf = fopen("log.txt", "w");\r
+}\r
+\r
+\r
+static void romfname_ext(char *dst, char *ext)\r
+{\r
+       char *p;\r
+\r
+       // make save filename\r
+       for (p = romFileName+strlen(romFileName)-1; p >= romFileName && *p != '/'; p--); p++;\r
+       strncpy(dst, p, 511);\r
+       dst[511-8] = 0;\r
+       if(dst[strlen(dst)-4] == '.') dst[strlen(dst)-4] = 0;\r
+       strcat(dst, ext);\r
+}\r
+\r
+\r
+static void find_combos(void)\r
+{\r
+       int act, u;\r
+\r
+       // find out which keys and actions are combos\r
+       combo_keys = combo_acts = 0;\r
+       for (act = 0; act < 32; act++)\r
+       {\r
+               int keyc = 0;\r
+               if (act == 16) continue; // player2 flag\r
+               for (u = 0; u < 32; u++)\r
+               {\r
+                       if (currentConfig.KeyBinds[u] & (1 << act)) keyc++;\r
+               }\r
+               if (keyc > 1)\r
+               {\r
+                       // loop again and mark those keys and actions as combo\r
+                       for (u = 0; u < 32; u++)\r
+                       {\r
+                               if (currentConfig.KeyBinds[u] & (1 << act)) {\r
+                                       combo_keys |= 1 << u;\r
+                                       combo_acts |= 1 << act;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       // printf("combo keys/acts: %08x %08x\n", combo_keys, combo_acts);\r
+}\r
+\r
+\r
+int emu_ReadConfig(int game)\r
+{\r
+       FILE *f;\r
+       char cfg[512];\r
+       int bread = 0;\r
+\r
+       if (!game)\r
+       {\r
+               // set default config\r
+               memset(&currentConfig, 0, sizeof(currentConfig));\r
+               currentConfig.lastRomFile[0] = 0;\r
+               currentConfig.EmuOpt  = 0x1f;\r
+               currentConfig.PicoOpt = 0x0f;\r
+               currentConfig.PsndRate = 22050;\r
+               currentConfig.PicoRegion = 0; // auto\r
+               currentConfig.Frameskip = -1; // auto\r
+               currentConfig.CPUclock = 200;\r
+               currentConfig.volume = 50;\r
+               currentConfig.KeyBinds[ 0] = 1<<0; // SACB RLDU\r
+               currentConfig.KeyBinds[ 4] = 1<<1;\r
+               currentConfig.KeyBinds[ 2] = 1<<2;\r
+               currentConfig.KeyBinds[ 6] = 1<<3;\r
+               currentConfig.KeyBinds[14] = 1<<4;\r
+               currentConfig.KeyBinds[13] = 1<<5;\r
+               currentConfig.KeyBinds[12] = 1<<6;\r
+               currentConfig.KeyBinds[ 8] = 1<<7;\r
+               currentConfig.KeyBinds[15] = 1<<26; // switch rend\r
+               currentConfig.KeyBinds[10] = 1<<27; // save state\r
+               currentConfig.KeyBinds[11] = 1<<28; // load state\r
+               currentConfig.KeyBinds[23] = 1<<29; // vol up\r
+               currentConfig.KeyBinds[22] = 1<<30; // vol down\r
+               currentConfig.gamma = 100;\r
+               strncpy(cfg, PicoConfigFile, 511);\r
+               cfg[511] = 0;\r
+       } else {\r
+               romfname_ext(cfg, ".pbcfg");\r
+       }\r
+\r
+       printf("emu_ReadConfig: %s ", cfg);\r
+       f = fopen(cfg, "rb");\r
+       if (f) {\r
+               bread = fread(&currentConfig, 1, sizeof(currentConfig), f);\r
+               fclose(f);\r
+       }\r
+       printf((bread == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n");\r
+\r
+       PicoOpt = currentConfig.PicoOpt;\r
+       PsndRate = currentConfig.PsndRate;\r
+       PicoRegionOverride = currentConfig.PicoRegion;\r
+       if (PicoOpt & 0x20) {\r
+               actionNames[ 8] = "Z"; actionNames[ 9] = "Y";\r
+               actionNames[10] = "X"; actionNames[11] = "MODE";\r
+       }\r
+       // some sanity checks\r
+       if (currentConfig.CPUclock < 1 || currentConfig.CPUclock > 4096) currentConfig.CPUclock = 200;\r
+       if (currentConfig.gamma < 10 || currentConfig.gamma > 300) currentConfig.gamma = 100;\r
+       // if volume keys are unbound, bind them to volume control\r
+       if (!currentConfig.KeyBinds[23] && !currentConfig.KeyBinds[22]) {\r
+               currentConfig.KeyBinds[23] = 1<<29; // vol up\r
+               currentConfig.KeyBinds[22] = 1<<30; // vol down\r
+       }\r
+\r
+       return (bread == sizeof(currentConfig));\r
+}\r
+\r
+\r
+int emu_WriteConfig(int game)\r
+{\r
+       FILE *f;\r
+       char cfg[512];\r
+       int bwrite = 0;\r
+\r
+       if (!game)\r
+       {\r
+               strncpy(cfg, PicoConfigFile, 511);\r
+               cfg[511] = 0;\r
+       } else {\r
+               romfname_ext(cfg, ".pbcfg");\r
+       }\r
+\r
+       printf("emu_WriteConfig: %s ", cfg);\r
+       f = fopen(cfg, "wb");\r
+       if (f) {\r
+               currentConfig.PicoOpt = PicoOpt;\r
+               currentConfig.PsndRate = PsndRate;\r
+               currentConfig.PicoRegion = PicoRegionOverride;\r
+               bwrite = fwrite(&currentConfig, 1, sizeof(currentConfig), f);\r
+               fflush(f);\r
+               fclose(f);\r
+               sync();\r
+       }\r
+       printf((bwrite == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n");\r
+\r
+       return (bwrite == sizeof(currentConfig));\r
+}\r
+\r
+\r
+void emu_Deinit(void)\r
+{\r
+       // save SRAM\r
+       if((currentConfig.EmuOpt & 1) && SRam.changed) {\r
+               emu_SaveLoadGame(0, 1);\r
+               SRam.changed = 0;\r
+       }\r
+\r
+       if (!(currentConfig.EmuOpt & 0x20))\r
+               emu_WriteConfig(0);\r
+       free(framebuff);\r
+\r
+       PicoExit();\r
+//     fclose(logf);\r
+\r
+       // restore gamma\r
+       if (gp2x_old_gamma != 100)\r
+               set_gamma(100);\r
+}\r
+\r
+\r
+void osd_text(int x, int y, char *text)\r
+{\r
+       int len = strlen(text)*8;\r
+\r
+       if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
+               int *p, i, h, black, white;\r
+               if (PicoOpt&0x10) {\r
+                       black = 0x40404040; white = 0x41;\r
+               } else {\r
+                       black = 0xe0e0e0e0; white = 0xf0;\r
+               }\r
+               x &= ~3; // align x\r
+               len = (len+3) >> 2;\r
+               for (h = 0; h < 8; h++) {\r
+                       p = (int *) ((unsigned char *) gp2x_screen+x+320*(y+h));\r
+                       for (i = len; i; i--, p++) *p = black;\r
+               }\r
+               gp2x_text_out8_2(x, y, text, white);\r
+       } else {\r
+               int *p, i, h;\r
+               x &= ~1; // align x\r
+               len = (len+1) >> 1;\r
+               for (h = 0; h < 8; h++) {\r
+                       p = (int *) ((unsigned short *) gp2x_screen+x+320*(y+h));\r
+                       for (i = len; i; i--, p++) *p = (*p>>2)&0x39e7;\r
+               }\r
+               gp2x_text_out15(x, y, text);\r
+       }\r
+}\r
+\r
+static int EmuScan16(unsigned int num, void *sdata)\r
+{\r
+       if (!(Pico.video.reg[1]&8)) num += 8;\r
+       DrawLineDest = (unsigned short *) gp2x_screen + 320*(num+1);\r
+\r
+       return 0;\r
+}\r
+\r
+static int EmuScan8(unsigned int num, void *sdata)\r
+{\r
+       if (!(Pico.video.reg[1]&8)) num += 8;\r
+       DrawLineDest = (unsigned char *)  gp2x_screen + 320*(num+1);\r
+\r
+       return 0;\r
+}\r
+\r
+static int localPal[0x100];\r
+static void (*vidCpyM2)(void *dest, void *src) = NULL;\r
+\r
+static void blit(char *fps, char *notice)\r
+{\r
+       if (PicoOpt&0x10) {\r
+               // 8bit fast renderer\r
+               if (Pico.m.dirtyPal) {\r
+                       Pico.m.dirtyPal = 0;\r
+                       vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
+                       // feed new palette to our device\r
+                       gp2x_video_setpalette(localPal, 0x40);\r
+               }\r
+               vidCpyM2((unsigned char *)gp2x_screen+320*8, framebuff+328*8);\r
+       } else if (!(currentConfig.EmuOpt&0x80)) {\r
+               // 8bit accurate renderer\r
+               if (Pico.m.dirtyPal) {\r
+                       Pico.m.dirtyPal = 0;\r
+                       if(Pico.video.reg[0xC]&8) { // shadow/hilight mode\r
+                               vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
+                               vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40);\r
+                               vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40);\r
+                               blockcpy(localPal+0xc0, localPal+0x40, 0x40*4);\r
+                               localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
+                               localPal[0xf0] = 0x00ffffff;\r
+                               gp2x_video_setpalette(localPal, 0x100);\r
+                       } else if (rendstatus & 0x20) { // mid-frame palette changes\r
+                               vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
+                               vidConvCpyRGB32(localPal+0x40, HighPal, 0x40);\r
+                               vidConvCpyRGB32(localPal+0x80, HighPal+0x40, 0x40);\r
+                               gp2x_video_setpalette(localPal, 0xc0);\r
+                       } else {\r
+                               vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
+                               gp2x_video_setpalette(localPal, 0x40);\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (notice) osd_text(4, 232, notice);\r
+       if (currentConfig.EmuOpt & 2)\r
+               osd_text(osd_fps_x, 232, fps);\r
+\r
+       //gp2x_video_wait_vsync();\r
+       gp2x_video_flip();\r
+\r
+       if (!(PicoOpt&0x10)) {\r
+               if (!(Pico.video.reg[1]&8)) {\r
+                       if (currentConfig.EmuOpt&0x80) {\r
+                               DrawLineDest = (unsigned short *) gp2x_screen + 320*8;\r
+                       } else {\r
+                               DrawLineDest = (unsigned char  *) gp2x_screen + 320*8;\r
+                       }\r
+               } else {\r
+                       DrawLineDest = gp2x_screen;\r
+               }\r
+       }\r
+}\r
+\r
+\r
+// clears whole screen or just the notice area (in all buffers)\r
+static void clearArea(int full)\r
+{\r
+       if (PicoOpt&0x10) {\r
+               // 8bit fast renderer\r
+               if (full) gp2x_memset_all_buffers(0, 0x40, 320*240);\r
+               else      gp2x_memset_all_buffers(320*232, 0x40, 320*8);\r
+       } else if (currentConfig.EmuOpt&0x80) {\r
+               // 16bit accurate renderer\r
+               if (full) gp2x_memset_all_buffers(0, 0, 320*240*2);\r
+               else      gp2x_memset_all_buffers(320*232*2, 0, 320*8*2);\r
+       } else {\r
+               // 8bit accurate renderer\r
+               if (full) gp2x_memset_all_buffers(0, 0xe0, 320*240);\r
+               else      gp2x_memset_all_buffers(320*232, 0xe0, 320*8);\r
+       }\r
+}\r
+\r
+\r
+static void vidResetMode(void)\r
+{\r
+       if (PicoOpt&0x10) {\r
+               localPal[0x40] = 0;\r
+               localPal[0x41] = 0x00ffffff;\r
+               gp2x_video_changemode(8);\r
+               gp2x_video_setpalette(localPal, 0x42);\r
+               gp2x_memset_all_buffers(0, 0x40, 320*240);\r
+               gp2x_video_flip();\r
+       } else if (currentConfig.EmuOpt&0x80) {\r
+               gp2x_video_changemode(15);\r
+               PicoDrawSetColorFormat(1);\r
+               PicoScan = EmuScan16;\r
+               PicoScan(0, 0);\r
+       } else {\r
+               localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
+               localPal[0xf0] = 0x00ffffff;\r
+               gp2x_video_changemode(8);\r
+               gp2x_video_setpalette(localPal, 0x100);\r
+               gp2x_memset_all_buffers(0, 0xe0, 320*240);\r
+               gp2x_video_flip();\r
+               PicoDrawSetColorFormat(2);\r
+               PicoScan = EmuScan8;\r
+               PicoScan(0, 0);\r
+       }\r
+       Pico.m.dirtyPal = 1;\r
+       // reset scaling\r
+       gp2x_video_RGB_setscaling((PicoOpt&0x100)&&!(Pico.video.reg[12]&1) ? 256 : 320, 240);\r
+}\r
+\r
+\r
+static int check_save_file(void)\r
+{\r
+       char saveFname[512];\r
+       char ext[16];\r
+       FILE *f;\r
+\r
+       ext[0] = 0;\r
+       if(state_slot > 0 && state_slot < 10) sprintf(ext, ".%i", state_slot);\r
+       strcat(ext, ".mds");\r
+       if(currentConfig.EmuOpt & 8) strcat(ext, ".gz");\r
+\r
+       romfname_ext(saveFname, ext);\r
+       if ((f = fopen(saveFname, "rb"))) {\r
+               fclose(f);\r
+               return 1;\r
+       }\r
+       return 0;\r
+}\r
+\r
+static void RunEvents(unsigned int which)\r
+{\r
+       if(which & 0x1800) { // save or load (but not both)\r
+               int do_it = 1;\r
+               if (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200) && check_save_file()) {\r
+                       unsigned long keys;\r
+                       blit("", "OVERWRITE SAVE? (Y=yes, X=no)");\r
+                       while( !((keys = gp2x_joystick_read(1)) & (GP2X_X|GP2X_Y)) )\r
+                               usleep(50*1024);\r
+                       if (keys & GP2X_X) do_it = 0;\r
+                       clearArea(0);\r
+               }\r
+               if (do_it) {\r
+                       blit("", (which & 0x1000) ? "LOADING GAME" : "SAVING GAME");\r
+                       emu_SaveLoadGame(which & 0x1000, 0);\r
+               }\r
+\r
+               reset_timing = 1;\r
+       }\r
+       if(which & 0x0400) { // switch renderer\r
+               if      (  PicoOpt&0x10)             { PicoOpt&=~0x10; currentConfig.EmuOpt |= 0x80; }\r
+               else if (!(currentConfig.EmuOpt&0x80)) PicoOpt|= 0x10;\r
+               else   currentConfig.EmuOpt &= ~0x80;\r
+\r
+               vidResetMode();\r
+\r
+               if (PicoOpt&0x10) {\r
+                       strcpy(noticeMsg, " 8bit fast renderer");\r
+               } else if (currentConfig.EmuOpt&0x80) {\r
+                       strcpy(noticeMsg, "16bit accurate renderer");\r
+               } else {\r
+                       strcpy(noticeMsg, " 8bit accurate renderer");\r
+               }\r
+\r
+               gettimeofday(&noticeMsgTime, 0);\r
+       }\r
+       if(which & 0x0300) {\r
+               if(which&0x0200) {\r
+                       state_slot -= 1;\r
+                       if(state_slot < 0) state_slot = 9;\r
+               } else {\r
+                       state_slot += 1;\r
+                       if(state_slot > 9) state_slot = 0;\r
+               }\r
+               sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, check_save_file() ? "USED" : "FREE");\r
+               gettimeofday(&noticeMsgTime, 0);\r
+       }\r
+       if(which & 0x0080) {\r
+               engineState = PGS_Menu;\r
+       }\r
+}\r
+\r
+\r
+static void updateKeys(void)\r
+{\r
+       unsigned long keys, allActions[2] = { 0, 0 }, events;\r
+       static unsigned long prevEvents = 0;\r
+       int joy, i;\r
+\r
+       keys = gp2x_joystick_read(0);\r
+       if (keys & GP2X_SELECT) {\r
+               engineState = select_exits ? PGS_Quit : PGS_Menu;\r
+               // wait until select is released, so menu would not resume game\r
+               while (gp2x_joystick_read(1) & GP2X_SELECT) usleep(50*1000);\r
+       }\r
+\r
+       keys &= CONFIGURABLE_KEYS;\r
+\r
+       for (i = 0; i < 32; i++)\r
+       {\r
+               if (keys & (1 << i)) {\r
+                       int pl, acts = currentConfig.KeyBinds[i];\r
+                       if (!acts) continue;\r
+                       pl = (acts >> 16) & 1;\r
+                       if (combo_keys & (1 << i)) {\r
+                               int u = i+1, acts_c = acts & combo_acts;\r
+                               // let's try to find the other one\r
+                               if (acts_c)\r
+                                       for (; u < 32; u++)\r
+                                               if ( (currentConfig.KeyBinds[u] & acts_c) && (keys & (1 << u)) ) {\r
+                                                       allActions[pl] |= acts_c;\r
+                                                       keys &= ~((1 << i) | (1 << u));\r
+                                                       break;\r
+                                               }\r
+                               // add non-combo actions if combo ones were not found\r
+                               if (!acts_c || u == 32)\r
+                                       allActions[pl] |= acts & ~combo_acts;\r
+                       } else {\r
+                               allActions[pl] |= acts;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // add joy inputs\r
+       if (num_of_joys > 0)\r
+       {\r
+               gp2x_usbjoy_update();\r
+               for (joy = 0; joy < num_of_joys; joy++) {\r
+                       int keys = gp2x_usbjoy_check2(joy);\r
+                       for (i = 0; i < 32; i++) {\r
+                               if (keys & (1 << i)) {\r
+                                       int acts = currentConfig.JoyBinds[joy][i];\r
+                                       int pl = (acts >> 16) & 1;\r
+                                       allActions[pl] |= acts;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       if(movie_data)\r
+       {\r
+               int offs = frame_count*3 + 0x40;\r
+               if (offs+3 > movie_size) {\r
+                       free(movie_data);\r
+                       movie_data = 0;\r
+                       strcpy(noticeMsg, "END OF MOVIE.");\r
+                       printf("END OF MOVIE.\n");\r
+                       gettimeofday(&noticeMsgTime, 0);\r
+               } else {\r
+                       // MXYZ SACB RLDU\r
+                       PicoPad[0] = ~movie_data[offs]   & 0x8f; // ! SCBA RLDU\r
+                       if(!(movie_data[offs]   & 0x10)) PicoPad[0] |= 0x40; // A\r
+                       if(!(movie_data[offs]   & 0x20)) PicoPad[0] |= 0x10; // B\r
+                       if(!(movie_data[offs]   & 0x40)) PicoPad[0] |= 0x20; // A\r
+                       PicoPad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU\r
+                       if(!(movie_data[offs+1] & 0x10)) PicoPad[1] |= 0x40; // A\r
+                       if(!(movie_data[offs+1] & 0x20)) PicoPad[1] |= 0x10; // B\r
+                       if(!(movie_data[offs+1] & 0x40)) PicoPad[1] |= 0x20; // A\r
+                       PicoPad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX\r
+                       if(!(movie_data[offs+2] & 0x01)) PicoPad[0] |= 0x0400; // X\r
+                       if(!(movie_data[offs+2] & 0x04)) PicoPad[0] |= 0x0100; // Z\r
+                       PicoPad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX\r
+                       if(!(movie_data[offs+2] & 0x10)) PicoPad[1] |= 0x0400; // X\r
+                       if(!(movie_data[offs+2] & 0x40)) PicoPad[1] |= 0x0100; // Z\r
+                       if ((PicoPad[0] & 0x80) || (PicoPad[1] & 0x80))\r
+                               printf("%d: start\n", frame_count);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               PicoPad[0] = (unsigned short) allActions[0];\r
+               PicoPad[1] = (unsigned short) allActions[1];\r
+       }\r
+       frame_count++;\r
+\r
+       events = (allActions[0] | allActions[1]) >> 16;\r
+\r
+       // volume is treated in special way and triggered every frame\r
+       if(events & 0x6000) {\r
+               int vol = currentConfig.volume;\r
+               if (events & 0x2000) {\r
+                       if (vol < 90) vol++;\r
+               } else {\r
+                       if (vol >  0) vol--;\r
+               }\r
+               gp2x_sound_volume(vol, vol);\r
+               sprintf(noticeMsg, "VOL: %02i", vol);\r
+               gettimeofday(&noticeMsgTime, 0);\r
+               currentConfig.volume = vol;\r
+       }\r
+\r
+       events &= ~prevEvents;\r
+       if (events) RunEvents(events);\r
+\r
+       prevEvents = (allActions[0] | allActions[1]) >> 16;\r
+}\r
+\r
+static int snd_excess_add = 0, snd_excess_cnt = 0; // hack\r
+\r
+static void updateSound(void)\r
+{\r
+       int len = (PicoOpt&8)?PsndLen*2:PsndLen;\r
+\r
+       snd_excess_cnt += snd_excess_add;\r
+       if (snd_excess_cnt >= 0x10000) {\r
+               snd_excess_cnt -= 0x10000;\r
+               if (PicoOpt&8) {\r
+                       PsndOut[len]   = PsndOut[len-2];\r
+                       PsndOut[len+1] = PsndOut[len-1];\r
+                       len+=2;\r
+               } else {\r
+                       PsndOut[len]   = PsndOut[len-1];\r
+                       len++;\r
+               }\r
+       }\r
+\r
+       gp2x_sound_write(PsndOut, len<<1);\r
+}\r
+\r
+\r
+static void SkipFrame(int do_sound)\r
+{\r
+       void *sndbuff_tmp = 0;\r
+       if (PsndOut && !do_sound) {\r
+               sndbuff_tmp = PsndOut;\r
+               PsndOut = 0;\r
+       }\r
+\r
+       PicoSkipFrame=1;\r
+       PicoFrame();\r
+       PicoSkipFrame=0;\r
+\r
+       if (sndbuff_tmp && !do_sound) {\r
+               PsndOut = sndbuff_tmp;\r
+       }\r
+}\r
+\r
+\r
+static void simpleWait(int thissec, int lim_time)\r
+{\r
+       struct timeval tval;\r
+\r
+       spend_cycles(1024);\r
+       gettimeofday(&tval, 0);\r
+       if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
+\r
+       while(tval.tv_usec < lim_time)\r
+       {\r
+               spend_cycles(1024);\r
+               gettimeofday(&tval, 0);\r
+               if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
+       }\r
+}\r
+\r
+\r
+void emu_Loop(void)\r
+{\r
+       static int gp2x_old_clock = 200;\r
+       static int PsndRate_old = 0, PicoOpt_old = 0, PsndLen_real = 0, pal_old = 0;\r
+       char fpsbuff[24]; // fps count c string\r
+       struct timeval tval; // timing\r
+       int thissec = 0, frames_done = 0, frames_shown = 0, oldmodes = 0;\r
+       int target_fps, target_frametime, lim_time, i;\r
+       char *notice = 0;\r
+\r
+       printf("entered emu_Loop()\n");\r
+\r
+       if (gp2x_old_clock != currentConfig.CPUclock) {\r
+               printf("changing clock to %i...", currentConfig.CPUclock); fflush(stdout);\r
+               set_FCLK(currentConfig.CPUclock);\r
+               gp2x_old_clock = currentConfig.CPUclock;\r
+               printf(" done\n");\r
+       }\r
+\r
+       if (gp2x_old_gamma != currentConfig.gamma) {\r
+               set_gamma(currentConfig.gamma);\r
+               gp2x_old_gamma = currentConfig.gamma;\r
+               printf("updated gamma to %i\n", currentConfig.gamma);\r
+       }\r
+\r
+       fpsbuff[0] = 0;\r
+\r
+       // make sure we are in correct mode\r
+       vidResetMode();\r
+       oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;\r
+       find_combos();\r
+\r
+       // pal/ntsc might have changed, reset related stuff\r
+       target_fps = Pico.m.pal ? 50 : 60;\r
+       target_frametime = 1000000/target_fps;\r
+       reset_timing = 1;\r
+\r
+       // prepare sound stuff\r
+       if(currentConfig.EmuOpt & 4) {\r
+               if(PsndRate != PsndRate_old || (PicoOpt&0x20b) != (PicoOpt_old&0x20b) || Pico.m.pal != pal_old || crashed_940) {\r
+                       /* if 940 is turned off, we need it to be put back to sleep */\r
+                       if (!(PicoOpt&0x200) && ((PicoOpt^PicoOpt_old)&0x200)) {\r
+                               Reset940(1);\r
+                               Pause940(1);\r
+                       }\r
+                       sound_rerate();\r
+               }\r
+               //excess_samples = PsndRate - PsndLen*target_fps;\r
+               snd_excess_cnt = 0;\r
+               snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
+               printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n", PsndRate, PsndLen, snd_excess_add, (PicoOpt&8)>>3, Pico.m.pal);\r
+               gp2x_start_sound(PsndRate, 16, (PicoOpt&8)>>3);\r
+               gp2x_sound_volume(currentConfig.volume, currentConfig.volume);\r
+               PicoWriteSound = updateSound;\r
+               PsndOut = calloc((PicoOpt&8) ? (PsndLen*4+4) : (PsndLen*2+2), 1);\r
+               PsndRate_old = PsndRate;\r
+               PsndLen_real = PsndLen;\r
+               PicoOpt_old  = PicoOpt;\r
+               pal_old = Pico.m.pal;\r
+       } else {\r
+               PsndOut = 0;\r
+       }\r
+\r
+       // loop?\r
+       while (engineState == PGS_Running)\r
+       {\r
+               int modes;\r
+\r
+               gettimeofday(&tval, 0);\r
+               if(reset_timing) {\r
+                       reset_timing = 0;\r
+                       thissec = tval.tv_sec;\r
+                       frames_shown = frames_done = tval.tv_usec/target_frametime;\r
+               }\r
+\r
+               // show notice message?\r
+               if(noticeMsgTime.tv_sec) {\r
+                       static int noticeMsgSum;\r
+                       if((tval.tv_sec*1000000+tval.tv_usec) - (noticeMsgTime.tv_sec*1000000+noticeMsgTime.tv_usec) > 2000000) { // > 2.0 sec\r
+                               noticeMsgTime.tv_sec = noticeMsgTime.tv_usec = 0;\r
+                               clearArea(0);\r
+                               notice = 0;\r
+                       } else {\r
+                               int sum = noticeMsg[0]+noticeMsg[1]+noticeMsg[2];\r
+                               if (sum != noticeMsgSum) { clearArea(0); noticeMsgSum = sum; }\r
+                               notice = noticeMsg;\r
+                       }\r
+               }\r
+\r
+               // check for mode changes\r
+               modes = ((Pico.video.reg[12]&1)<<2)|(Pico.video.reg[1]&8);\r
+               if (modes != oldmodes) {\r
+                       int scalex = 320;\r
+                       osd_fps_x = OSD_FPS_X;\r
+                       if (modes & 4) {\r
+                               vidCpyM2 = vidCpyM2_40col;\r
+                       } else {\r
+                               if (PicoOpt & 0x100) {\r
+                                       vidCpyM2 = vidCpyM2_32col_nobord;\r
+                                       scalex = 256;\r
+                                       osd_fps_x = OSD_FPS_X - 64;\r
+                               } else {\r
+                                       vidCpyM2 = vidCpyM2_32col;\r
+                               }\r
+                       }\r
+                       gp2x_video_RGB_setscaling(scalex, 240);\r
+                       oldmodes = modes;\r
+                       clearArea(1);\r
+               }\r
+\r
+               // second changed?\r
+               if(thissec != tval.tv_sec) {\r
+#ifdef BENCHMARK\r
+                       static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
+                       if(++bench == 10) {\r
+                               bench = 0;\r
+                               bench_fps_s = bench_fps;\r
+                               bf[bfp++ & 3] = bench_fps;\r
+                               bench_fps = 0;\r
+                       }\r
+                       bench_fps += frames_shown;\r
+                       sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
+#else\r
+                       if(currentConfig.EmuOpt & 2)\r
+                               sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);\r
+#endif\r
+                       thissec = tval.tv_sec;\r
+\r
+                       if(PsndOut == 0 && currentConfig.Frameskip >= 0) {\r
+                               frames_done = frames_shown = 0;\r
+                       } else {\r
+                               // it is quite common for this implementation to leave 1 fame unfinished\r
+                               // when second changes, but we don't want buffer to starve.\r
+                               if(PsndOut && frames_done < target_fps && frames_done > target_fps-5) {\r
+                                       updateKeys();\r
+                                       SkipFrame(1); frames_done++;\r
+                               }\r
+\r
+                               frames_done  -= target_fps; if (frames_done  < 0) frames_done  = 0;\r
+                               frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;\r
+                               if (frames_shown > frames_done) frames_shown = frames_done;\r
+                       }\r
+               }\r
+\r
+               lim_time = (frames_done+1) * target_frametime;\r
+               if(currentConfig.Frameskip >= 0) { // frameskip enabled\r
+                       for(i = 0; i < currentConfig.Frameskip; i++) {\r
+                               updateKeys();\r
+                               SkipFrame(1); frames_done++;\r
+                               if (PsndOut) { // do framelimitting if sound is enabled\r
+                                       gettimeofday(&tval, 0);\r
+                                       if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
+                                       if(tval.tv_usec < lim_time) { // we are too fast\r
+                                               simpleWait(thissec, lim_time);\r
+                                       }\r
+                               }\r
+                               lim_time += target_frametime;\r
+                       }\r
+               } else if(tval.tv_usec > lim_time) { // auto frameskip\r
+                       // no time left for this frame - skip\r
+                       updateKeys();\r
+                       SkipFrame(tval.tv_usec < lim_time+target_frametime); frames_done++;\r
+                       continue;\r
+               }\r
+\r
+               updateKeys();\r
+               PicoFrame();\r
+\r
+               // check time\r
+               gettimeofday(&tval, 0);\r
+               if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
+\r
+               // sleep if we are still too fast\r
+               if(PsndOut != 0 || currentConfig.Frameskip < 0)\r
+               {\r
+                       // usleep sleeps for ~20ms minimum, so it is not a solution here\r
+                       gettimeofday(&tval, 0);\r
+                       if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
+                       if(tval.tv_usec < lim_time)\r
+                       {\r
+                               // we are too fast\r
+                               simpleWait(thissec, lim_time);\r
+                       }\r
+               }\r
+\r
+               blit(fpsbuff, notice);\r
+\r
+               frames_done++; frames_shown++;\r
+       }\r
+\r
+       // save SRAM\r
+       if((currentConfig.EmuOpt & 1) && SRam.changed) {\r
+               emu_SaveLoadGame(0, 1);\r
+               SRam.changed = 0;\r
+       }\r
+\r
+       if (PsndOut != 0) {\r
+               free(PsndOut);\r
+               PsndOut = 0;\r
+       }\r
+}\r
+\r
+\r
+void emu_ResetGame(void)\r
+{\r
+       PicoReset(0);\r
+       reset_timing = 1;\r
+}\r
+\r
+\r
+size_t gzRead2(void *p, size_t _size, size_t _n, void *file)\r
+{\r
+       return gzread(file, p, _n);\r
+}\r
+\r
+\r
+size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)\r
+{\r
+       return gzwrite(file, p, _n);\r
+}\r
+\r
+typedef unsigned int (*STATE_SL_FUNC)(void *, unsigned int, unsigned int, void *);\r
+\r
+int emu_SaveLoadGame(int load, int sram)\r
+{\r
+       int ret = 0;\r
+       char saveFname[512];\r
+\r
+       // make save filename\r
+       romfname_ext(saveFname, "");\r
+       if(sram) strcat(saveFname, ".srm");\r
+       else {\r
+               if(state_slot > 0 && state_slot < 10) sprintf(saveFname, "%s.%i", saveFname, state_slot);\r
+               strcat(saveFname, ".mds");\r
+       }\r
+\r
+       printf("saveLoad (%i, %i): %s\n", load, sram, saveFname);\r
+\r
+       if(sram) {\r
+               FILE *sramFile;\r
+               int sram_size = SRam.end-SRam.start+1;\r
+               if(SRam.reg_back & 4) sram_size=0x2000;\r
+               if(!SRam.data) return 0; // SRam forcefully disabled for this game\r
+               if(load) {\r
+                       sramFile = fopen(saveFname, "rb");\r
+                       if(!sramFile) return -1;\r
+                       fread(SRam.data, 1, sram_size, sramFile);\r
+                       fclose(sramFile);\r
+               } else {\r
+                       // sram save needs some special processing\r
+                       // see if we have anything to save\r
+                       for(; sram_size > 0; sram_size--)\r
+                               if(SRam.data[sram_size-1]) break;\r
+\r
+                       if(sram_size) {\r
+                               sramFile = fopen(saveFname, "wb");\r
+                               ret = fwrite(SRam.data, 1, sram_size, sramFile);\r
+                               ret = (ret != sram_size) ? -1 : 0;\r
+                               fclose(sramFile);\r
+                               sync();\r
+                       }\r
+               }\r
+               return ret;\r
+       } else {\r
+               void *PmovFile = NULL;\r
+               // try gzip first\r
+               if(currentConfig.EmuOpt & 8) {\r
+                       strcat(saveFname, ".gz");\r
+                       if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) {\r
+                               areaRead  = gzRead2;\r
+                               areaWrite = gzWrite2;\r
+                               if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY);\r
+                       } else\r
+                               saveFname[strlen(saveFname)-3] = 0;\r
+               }\r
+               if(!PmovFile) { // gzip failed or was disabled\r
+                       if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) {\r
+                               areaRead  = (STATE_SL_FUNC) fread;\r
+                               areaWrite = (STATE_SL_FUNC) fwrite;\r
+                       }\r
+               }\r
+               if(PmovFile) {\r
+                       PmovState(load ? 6 : 5, PmovFile);\r
+                       strcpy(noticeMsg, load ? "GAME LOADED  " : "GAME SAVED   ");\r
+                       if(areaRead == gzRead2)\r
+                                gzclose(PmovFile);\r
+                       else fclose ((FILE *) PmovFile);\r
+                       PmovFile = 0;\r
+                       if (!load) sync();\r
+                       else Pico.m.dirtyPal=1;\r
+               } else {\r
+                       strcpy(noticeMsg, load ? "LOAD FAILED  " : "SAVE FAILED  ");\r
+                       ret = -1;\r
+               }\r
+\r
+               gettimeofday(&noticeMsgTime, 0);\r
+               return ret;\r
+       }\r
+}\r
diff --git a/gp2x/emu.h b/gp2x/emu.h
new file mode 100644 (file)
index 0000000..33b1ae7
--- /dev/null
@@ -0,0 +1,46 @@
+// (c) Copyright 2006 notaz, All rights reserved.\r
+// Free for non-commercial use.\r
+\r
+// For commercial use, separate licencing terms must be obtained.\r
+\r
+\r
+\r
+// engine states\r
+enum TPicoGameState {\r
+       PGS_Paused = 1,\r
+       PGS_Running,\r
+       PGS_Quit,\r
+       PGS_KeyConfig,\r
+       PGS_ReloadRom,\r
+       PGS_Menu,\r
+};\r
+\r
+typedef struct {\r
+       char lastRomFile[512];\r
+       int EmuOpt;             // LSb->MSb: use_sram, show_fps, enable_sound, gzip_saves,\r
+                                       // squidgehack, save_cfg_on_exit, <unused>, 16_bit_mode\r
+                                       // craigix_ram, confirm_save\r
+       int PicoOpt;  // used for config saving only, see Pico.h\r
+       int PsndRate; // ditto\r
+       int PicoRegion; // ditto\r
+       int Frameskip;\r
+       int CPUclock;\r
+       int KeyBinds[32];\r
+       int volume;\r
+       int gamma;\r
+       int JoyBinds[4][32];\r
+} currentConfig_t;\r
+\r
+extern char romFileName[];\r
+extern int engineState;\r
+extern currentConfig_t currentConfig;\r
+\r
+\r
+int  emu_ReloadRom(void);\r
+void emu_Init(void);\r
+void emu_Deinit(void);\r
+int  emu_SaveLoadGame(int load, int sram);\r
+void emu_Loop(void);\r
+void emu_ResetGame(void);\r
+int  emu_ReadConfig(int game);\r
+int  emu_WriteConfig(int game);\r
diff --git a/gp2x/gp2x.c b/gp2x/gp2x.c
new file mode 100644 (file)
index 0000000..84e5f7e
--- /dev/null
@@ -0,0 +1,311 @@
+/**\r
+ * All this is mostly based on rlyeh's minimal library.\r
+ * Copied here to review all his code and understand what's going on.\r
+**/\r
+\r
+/*\r
+\r
+  GP2X minimal library v0.A by rlyeh, (c) 2005. emulnation.info@rlyeh (swap it!)\r
+\r
+  Thanks to Squidge, Robster, snaff, Reesy and NK, for the help & previous work! :-)\r
+\r
+  License\r
+  =======\r
+\r
+  Free for non-commercial projects (it would be nice receiving a mail from you).\r
+  Other cases, ask me first.\r
+\r
+  GamePark Holdings is not allowed to use this library and/or use parts from it.\r
+\r
+*/\r
+\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <unistd.h>\r
+#include <sys/mman.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <sys/ioctl.h>\r
+#include <sys/soundcard.h>\r
+#include <fcntl.h>\r
+#include <errno.h>\r
+\r
+#include "gp2x.h"\r
+#include "usbjoy.h"\r
+\r
+volatile unsigned short *gp2x_memregs;\r
+//static\r
+volatile unsigned long  *gp2x_memregl;\r
+static void *gp2x_screens[4];\r
+static int screensel = 0;\r
+//static\r
+int memdev = 0;\r
+static int sounddev = 0, mixerdev = 0;\r
+\r
+void *gp2x_screen;\r
+\r
+#define FRAMEBUFF_ADDR0 0x4000000-640*480\r
+#define FRAMEBUFF_ADDR1 0x4000000-640*480*2\r
+#define FRAMEBUFF_ADDR2 0x4000000-640*480*3\r
+#define FRAMEBUFF_ADDR3 0x4000000-640*480*4\r
+\r
+static const int gp2x_screenaddrs[] = { FRAMEBUFF_ADDR0, FRAMEBUFF_ADDR1, FRAMEBUFF_ADDR2, FRAMEBUFF_ADDR3 };\r
+\r
+\r
+/* video stuff */\r
+void gp2x_video_flip(void)\r
+{\r
+       unsigned int address = gp2x_screenaddrs[screensel&3];\r
+\r
+       /* test */\r
+/*     {\r
+               int i; char *p=gp2x_screen;\r
+               for (i=0; i < 240; i++) { memset(p+i*320, 0, 32); }\r
+       }*/\r
+\r
+       gp2x_memregs[0x290E>>1]=(unsigned short)(address);\r
+       gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16);\r
+       gp2x_memregs[0x2912>>1]=(unsigned short)(address);\r
+       gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16);\r
+\r
+       // jump to other buffer:\r
+       gp2x_screen = gp2x_screens[++screensel&3];\r
+}\r
+\r
+\r
+void gp2x_video_changemode(int bpp)\r
+{\r
+       gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/\r
+       gp2x_memregs[0x290C>>1]=320*((bpp+1)/8); /*line width in bytes*/\r
+\r
+       gp2x_memset_all_buffers(0, 0, 640*480);\r
+       gp2x_video_flip();\r
+}\r
+\r
+\r
+void gp2x_video_setpalette(int *pal, int len)\r
+{\r
+       unsigned short *g=(unsigned short *)pal;\r
+       volatile unsigned short *memreg = &gp2x_memregs[0x295A>>1];\r
+       gp2x_memregs[0x2958>>1] = 0;\r
+\r
+       len *= 2;\r
+       while(len--) *memreg=*g++;\r
+}\r
+\r
+\r
+// TV Compatible function //\r
+void gp2x_video_RGB_setscaling(int W, int H)\r
+{\r
+       float escalaw, escalah;\r
+       int bpp = (gp2x_memregs[0x28DA>>1]>>9)&0x3;\r
+\r
+       escalaw = 1024.0; // RGB Horiz LCD\r
+       escalah = 320.0; // RGB Vert LCD\r
+\r
+       if(gp2x_memregs[0x2800>>1]&0x100) //TV-Out\r
+       {\r
+               escalaw=489.0; // RGB Horiz TV (PAL, NTSC)\r
+               if (gp2x_memregs[0x2818>>1]  == 287) //PAL\r
+                       escalah=274.0; // RGB Vert TV PAL\r
+               else if (gp2x_memregs[0x2818>>1]  == 239) //NTSC\r
+                       escalah=331.0; // RGB Vert TV NTSC\r
+       }\r
+\r
+       // scale horizontal\r
+       gp2x_memregs[0x2906>>1]=(unsigned short)((float)escalaw *(W/320.0));\r
+       // scale vertical\r
+       gp2x_memregl[0x2908>>2]=(unsigned long)((float)escalah *bpp *(H/240.0));\r
+}\r
+\r
+\r
+/* LCD updates @ 80Hz? */\r
+void gp2x_video_wait_vsync(void)\r
+{\r
+       gp2x_memregs[0x2846>>1] = 0x20|2; //(gp2x_memregs[0x2846>>1] | 0x20) & ~2;\r
+       while(!(gp2x_memregs[0x2846>>1] & 2));// usleep(1);\r
+}\r
+\r
+\r
+void gp2x_memcpy_all_buffers(void *data, int offset, int len)\r
+{\r
+       memcpy((char *)gp2x_screens[0] + offset, data, len);\r
+       memcpy((char *)gp2x_screens[1] + offset, data, len);\r
+       memcpy((char *)gp2x_screens[2] + offset, data, len);\r
+       memcpy((char *)gp2x_screens[3] + offset, data, len);\r
+}\r
+\r
+\r
+void gp2x_memset_all_buffers(int offset, int byte, int len)\r
+{\r
+       memset((char *)gp2x_screens[0] + offset, byte, len);\r
+       memset((char *)gp2x_screens[1] + offset, byte, len);\r
+       memset((char *)gp2x_screens[2] + offset, byte, len);\r
+       memset((char *)gp2x_screens[3] + offset, byte, len);\r
+}\r
+\r
+\r
+unsigned long gp2x_joystick_read(int allow_usb_joy)\r
+{\r
+       int i;\r
+       unsigned long value=(gp2x_memregs[0x1198>>1] & 0x00FF);\r
+       if(value==0xFD) value=0xFA;\r
+       if(value==0xF7) value=0xEB;\r
+       if(value==0xDF) value=0xAF;\r
+       if(value==0x7F) value=0xBE;\r
+       value = ~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16));\r
+\r
+       if (allow_usb_joy && num_of_joys > 0) {\r
+               // check the usb joy as well..\r
+               gp2x_usbjoy_update();\r
+               for (i = 0; i < num_of_joys; i++)\r
+                       value |= gp2x_usbjoy_check(i);\r
+       }\r
+\r
+       return value;\r
+}\r
+\r
+static int s_oldrate = 0, s_oldbits = 0, s_oldstereo = 0;\r
+\r
+void gp2x_start_sound(int rate, int bits, int stereo)\r
+{\r
+       int frag = 0, bsize, buffers;\r
+\r
+       // if no settings change, we don't need to do anything\r
+       if (rate == s_oldrate && s_oldbits == bits && s_oldstereo == stereo) return;\r
+\r
+       if (sounddev > 0) close(sounddev);\r
+       sounddev = open("/dev/dsp", O_WRONLY|O_ASYNC);\r
+       if (sounddev == -1)\r
+               printf("open(\"/dev/dsp\") failed with %i\n", errno);\r
+\r
+       ioctl(sounddev, SNDCTL_DSP_SPEED,  &rate);\r
+       ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits);\r
+       ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo);\r
+       // calculate buffer size\r
+       buffers = 16;\r
+       bsize = rate / 32;\r
+       if (rate > 22050) { bsize*=4; buffers*=2; } // 44k mode seems to be very demanding\r
+       while ((bsize>>=1)) frag++;\r
+       frag |= buffers<<16; // 16 buffers\r
+       ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag);\r
+       printf("gp2x_set_sound: %i/%ibit/%s, %i buffers of %i bytes\n",\r
+               rate, bits, stereo?"stereo":"mono", frag>>16, 1<<(frag&0xffff));\r
+\r
+       s_oldrate = rate; s_oldbits = bits; s_oldstereo = stereo;\r
+       usleep(100000);\r
+}\r
+\r
+\r
+void gp2x_sound_write(void *buff, int len)\r
+{\r
+       write(sounddev, buff, len);\r
+}\r
+\r
+\r
+void gp2x_sound_volume(int l, int r)\r
+{\r
+       l=l<0?0:l; l=l>255?255:l; r=r<0?0:r; r=r>255?255:r;\r
+       l<<=8; l|=r;\r
+       ioctl(mixerdev, SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/\r
+}\r
+\r
+\r
+/* 940 */\r
+void Pause940(int yes)\r
+{\r
+       if(yes)\r
+               gp2x_memregs[0x0904>>1] &= 0xFFFE;\r
+       else\r
+               gp2x_memregs[0x0904>>1] |= 1;\r
+}\r
+\r
+\r
+void Reset940(int yes)\r
+{\r
+       gp2x_memregs[0x3B48>>1] = ((yes&1) << 7) | (0x03); /* bank=3 */\r
+}\r
+\r
+\r
+\r
+/* common */\r
+void gp2x_init(void)\r
+{\r
+       printf("entering init()\n"); fflush(stdout);\r
+\r
+       memdev = open("/dev/mem", O_RDWR);\r
+       if (memdev == -1)\r
+       {\r
+               printf("open(\"/dev/mem\") failed with %i\n", errno);\r
+               exit(1);\r
+       }\r
+\r
+       gp2x_memregs = mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000);\r
+       printf("memregs are @ %p\n", gp2x_memregs);\r
+       if(gp2x_memregs == MAP_FAILED)\r
+       {\r
+               printf("mmap(memregs) failed with %i\n", errno);\r
+               exit(1);\r
+       }\r
+       gp2x_memregl = (unsigned long *) gp2x_memregs;\r
+\r
+       gp2x_screens[3] = mmap(0, 640*480*4, PROT_WRITE, MAP_SHARED, memdev, FRAMEBUFF_ADDR3);\r
+       if(gp2x_screens[3] == MAP_FAILED)\r
+       {\r
+               printf("mmap(gp2x_screen) failed with %i\n", errno);\r
+               exit(1);\r
+       }\r
+       printf("framebuffers point to %p\n", gp2x_screens[3]);\r
+       gp2x_screens[2] = (char *) gp2x_screens[3]+640*480;\r
+       gp2x_screens[1] = (char *) gp2x_screens[2]+640*480;\r
+       gp2x_screens[0] = (char *) gp2x_screens[1]+640*480;\r
+\r
+       gp2x_screen = gp2x_screens[0];\r
+       screensel = 0;\r
+\r
+       // snd\r
+       mixerdev = open("/dev/mixer", O_RDWR);\r
+       if (mixerdev == -1)\r
+               printf("open(\"/dev/mixer\") failed with %i\n", errno);\r
+\r
+       /* init usb joys -GnoStiC */\r
+       gp2x_usbjoy_init();\r
+\r
+       printf("exitting init()\n"); fflush(stdout);\r
+}\r
+\r
+char *ext_menu = 0, *ext_state = 0;\r
+\r
+void gp2x_deinit(void)\r
+{\r
+       Reset940(1);\r
+       Pause940(1);\r
+\r
+       gp2x_video_changemode(15);\r
+       munmap(gp2x_screens[0], 640*480*4);\r
+       munmap((void *)gp2x_memregs, 0x10000);\r
+       close(memdev);\r
+       close(mixerdev);\r
+       if (sounddev > 0) close(sounddev);\r
+\r
+       gp2x_usbjoy_deinit();\r
+\r
+       printf("all done, running ");\r
+\r
+       // Zaq121's alternative frontend support from MAME\r
+       if(ext_menu && ext_state) {\r
+               printf("%s -state %s\n", ext_menu, ext_state);\r
+               execl(ext_menu, ext_menu, "-state", ext_state, NULL);\r
+       } else if(ext_menu) {\r
+               printf("%s\n", ext_menu);\r
+               execl(ext_menu, ext_menu, NULL);\r
+       } else {\r
+               printf("gp2xmenu\n");\r
+               chdir("/usr/gp2x");\r
+               execl("gp2xmenu", "gp2xmenu", NULL);\r
+       }\r
+}\r
+\r
+\r
diff --git a/gp2x/gp2x.h b/gp2x/gp2x.h
new file mode 100644 (file)
index 0000000..113f674
--- /dev/null
@@ -0,0 +1,40 @@
+\r
+#ifndef __GP2X_H__\r
+#define __GP2X_H__\r
+\r
+\r
+void gp2x_init(void);\r
+void gp2x_deinit(void);\r
+\r
+/* video */\r
+void gp2x_video_flip(void);\r
+void gp2x_video_changemode(int bpp);\r
+void gp2x_video_setpalette(int *pal, int len);\r
+void gp2x_video_RGB_setscaling(int W, int H);\r
+void gp2x_video_wait_vsync(void);\r
+void gp2x_memcpy_all_buffers(void *data, int offset, int len);\r
+void gp2x_memset_all_buffers(int offset, int byte, int len);\r
+\r
+/* sound */\r
+void gp2x_start_sound(int rate, int bits, int stereo);\r
+void gp2x_sound_write(void *buff, int len);\r
+void gp2x_sound_volume(int l, int r);\r
+\r
+/* joy */\r
+unsigned long gp2x_joystick_read(int allow_usb_joy);\r
+\r
+/* 940 core */\r
+void Pause940(int yes);\r
+void Reset940(int yes);\r
+\r
+\r
+extern void *gp2x_screen;\r
+extern int memdev;\r
+\r
+\r
+enum  { GP2X_UP=0x1,       GP2X_LEFT=0x4,       GP2X_DOWN=0x10,  GP2X_RIGHT=0x40,\r
+        GP2X_START=1<<8,   GP2X_SELECT=1<<9,    GP2X_L=1<<10,    GP2X_R=1<<11,\r
+        GP2X_A=1<<12,      GP2X_B=1<<13,        GP2X_X=1<<14,    GP2X_Y=1<<15,\r
+        GP2X_VOL_UP=1<<23, GP2X_VOL_DOWN=1<<22, GP2X_PUSH=1<<27 };\r
+\r
+#endif\r
diff --git a/gp2x/main.c b/gp2x/main.c
new file mode 100644 (file)
index 0000000..f21ebd7
--- /dev/null
@@ -0,0 +1,143 @@
+// (c) Copyright 2006 notaz, All rights reserved.\r
+// Free for non-commercial use.\r
+\r
+// For commercial use, separate licencing terms must be obtained.\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <unistd.h>\r
+#include <strings.h>\r
+#include <linux/limits.h>\r
+\r
+#include "gp2x.h"\r
+#include "menu.h"\r
+#include "emu.h"\r
+#include "version.h"\r
+\r
+#include "squidgehack.h"\r
+#include "cpuctrl.h"\r
+\r
+\r
+extern char *ext_menu, *ext_state;\r
+extern int select_exits;\r
+extern char *PicoConfigFile;\r
+int mmuhack_status = 0;\r
+char **g_argv;\r
+\r
+void parse_cmd_line(int argc, char *argv[])\r
+{\r
+       int x, unrecognized = 0;\r
+\r
+       for(x = 1; x < argc; x++)\r
+       {\r
+               if(argv[x][0] == '-')\r
+               {\r
+                       if(strcasecmp(argv[x], "-menu") == 0) {\r
+                               if(x+1 < argc) { ++x; ext_menu = argv[x]; } /* External Frontend: Program Name */\r
+                       }\r
+                       else if(strcasecmp(argv[x], "-state") == 0) {\r
+                               if(x+1 < argc) { ++x; ext_state = argv[x]; } /* External Frontend: Arguments */\r
+                       }\r
+                       else if(strcasecmp(argv[x], "-config") == 0) {\r
+                               if(x+1 < argc) { ++x; PicoConfigFile = argv[x]; }\r
+                       }\r
+                       else if(strcasecmp(argv[x], "-selectexit") == 0) {\r
+                               select_exits = 1;\r
+                       }\r
+                       else {\r
+                               unrecognized = 1;\r
+                               break;\r
+                       }\r
+               } else {\r
+                       /* External Frontend: ROM Name */\r
+                       FILE *f;\r
+                       strncpy(romFileName, argv[x], PATH_MAX);\r
+                       romFileName[PATH_MAX-1] = 0;\r
+                       f = fopen(romFileName, "rb");\r
+                       if (f) fclose(f);\r
+                       else unrecognized = 1;\r
+                       engineState = PGS_ReloadRom;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       if (unrecognized) {\r
+               printf("\n\n\nPicoDrive v" VERSION " (c) notaz, 2006\n");\r
+               printf("usage: %s [options] [romfile]\n", argv[0]);\r
+               printf( "options:\n"\r
+                               "-menu <menu_path> launch a custom program on exit instead of default gp2xmenu\n"\r
+                               "-state <param>    pass '-state param' to the menu program\n"\r
+                               "-config <file>    use specified config file instead of default 'picoconfig.bin'\n"\r
+                               "                  see currentConfig_t structure in emu.h for the file format\n"\r
+                               "-selectexit       pressing SELECT will exit the emu and start 'menu_path'\n");\r
+       }\r
+}\r
+\r
+\r
+int main(int argc, char *argv[])\r
+{\r
+       g_argv = argv;\r
+\r
+       emu_ReadConfig(0);\r
+       gp2x_init();\r
+       if (currentConfig.EmuOpt&0x10) {\r
+               int ret = mmuhack();\r
+               printf("squidge hack code finished and returned %i\n", ret); fflush(stdout);\r
+               mmuhack_status = ret;\r
+       }\r
+       cpuctrl_init();\r
+       Reset940(1);\r
+       Pause940(1);\r
+       if (currentConfig.EmuOpt&0x100) {\r
+               printf("setting RAM timings.. "); fflush(stdout);\r
+               // craigix: --trc 6 --tras 4 --twr 1 --tmrd 1 --trfc 1 --trp 2 --trcd 2\r
+               set_RAM_Timings(6, 4, 1, 1, 1, 2, 2);\r
+               printf("done.\n"); fflush(stdout);\r
+       }\r
+       emu_Init();\r
+\r
+       engineState = PGS_Menu;\r
+\r
+       if (argc > 1)\r
+               parse_cmd_line(argc, argv);\r
+\r
+       for (;;)\r
+       {\r
+               switch (engineState)\r
+               {\r
+                       case PGS_Menu:\r
+                               menu_loop();\r
+                               break;\r
+\r
+                       case PGS_ReloadRom:\r
+                               if (emu_ReloadRom())\r
+                                       engineState = PGS_Running;\r
+                               else {\r
+                                       printf("PGS_ReloadRom == 0\n");\r
+                                       engineState = PGS_Menu;\r
+                               }\r
+                               break;\r
+\r
+                       case PGS_Running:\r
+                               emu_Loop();\r
+                               break;\r
+\r
+                       case PGS_Quit:\r
+                               goto endloop;\r
+                       \r
+                       default:\r
+                               printf("engine got into unknown state (%i), exitting\n", engineState);\r
+                               goto endloop;\r
+               }\r
+       }\r
+\r
+       endloop:\r
+\r
+       emu_Deinit();\r
+       cpuctrl_deinit();\r
+       gp2x_deinit();\r
+       if(mmuhack_status)\r
+               mmuunhack();\r
+\r
+       return 0;\r
+}\r
diff --git a/gp2x/menu.c b/gp2x/menu.c
new file mode 100644 (file)
index 0000000..6336126
--- /dev/null
@@ -0,0 +1,992 @@
+// (c) Copyright 2006 notaz, All rights reserved.\r
+// Free for non-commercial use.\r
+\r
+// For commercial use, separate licencing terms must be obtained.\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <unistd.h>\r
+#include <dirent.h>\r
+\r
+#include "gp2x.h"\r
+#include "emu.h"\r
+#include "menu.h"\r
+#include "usbjoy.h"\r
+#include "version.h"\r
+\r
+#include "Pico/PicoInt.h"\r
+\r
+#ifndef _DIRENT_HAVE_D_TYPE\r
+#error "need d_type for file browser\r
+#endif\r
+\r
+extern char *actionNames[];\r
+extern char romFileName[PATH_MAX];\r
+extern char *rom_data;\r
+extern int  mmuhack_status;\r
+extern int  state_slot;\r
+\r
+static char *gp2xKeyNames[] = {\r
+       "UP",    "???",    "LEFT", "???",  "DOWN", "???", "RIGHT",    "???",\r
+       "START", "SELECT", "L",    "R",    "A",    "B",   "X",        "Y",\r
+       "???",   "???",    "???",  "???",  "???",  "???", "VOL DOWN", "VOL UP",\r
+       "???",   "???",    "???",  "PUSH", "???",  "???", "???",      "???"\r
+};\r
+\r
+char menuErrorMsg[40] = {0, };\r
+\r
+\r
+static unsigned char fontdata8x8[] =\r
+{\r
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
+       0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C,\r
+       0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,\r
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,\r
+       0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00,\r
+       0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00,\r
+       0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,\r
+       0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40,\r
+       0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00,\r
+       0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00,\r
+       0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00,\r
+       0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00,\r
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00,\r
+       0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00,\r
+       0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00,\r
+       0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00,\r
+       0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00,\r
+       0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,\r
+       0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,\r
+       0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00,\r
+       0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00,\r
+       0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00,\r
+       0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00,\r
+       0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00,\r
+       0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00,\r
+       0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30,\r
+       0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,\r
+       0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00,\r
+       0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00,\r
+       0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00,\r
+       0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00,\r
+       0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00,\r
+       0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,\r
+       0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00,\r
+       0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00,\r
+       0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,\r
+       0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00,\r
+       0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00,\r
+       0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,\r
+       0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00,\r
+       0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00,\r
+       0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,\r
+       0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,\r
+       0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,\r
+       0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00,\r
+       0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00,\r
+       0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00,\r
+       0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C,\r
+       0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00,\r
+       0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00,\r
+       0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00,\r
+       0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00,\r
+       0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06,\r
+       0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00,\r
+       0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00,\r
+       0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00,\r
+       0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C,\r
+       0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00,\r
+       0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00,\r
+       0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00,\r
+};\r
+\r
+static void gp2x_text(unsigned char *screen, int x, int y, char *text, int color)\r
+{\r
+       int i,l;\r
+\r
+       screen = screen + x + y*320;\r
+\r
+       for (i = 0; i < strlen(text); i++)\r
+       {\r
+               for (l=0;l<8;l++)\r
+               {\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=color;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=color;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=color;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=color;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=color;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=color;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=color;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=color;\r
+               }\r
+               screen += 8;\r
+       }\r
+}\r
+\r
+// draws white text to current bbp15 screen\r
+void gp2x_text_out15(int x, int y, char *text)\r
+{\r
+       int i,l;\r
+       unsigned short *screen = gp2x_screen;\r
+\r
+       screen = screen + x + y*320;\r
+\r
+       for (i = 0; i < strlen(text); i++)\r
+       {\r
+               for (l=0;l<8;l++)\r
+               {\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=0xffff;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=0xffff;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=0xffff;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=0xffff;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=0xffff;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=0xffff;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=0xffff;\r
+                       if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=0xffff;\r
+               }\r
+               screen += 8;\r
+       }\r
+}\r
+\r
+\r
+void gp2x_text_out8(int x, int y, char *texto, ...)\r
+{\r
+       va_list args;\r
+       char    buffer[512];\r
+\r
+       va_start(args,texto);\r
+       vsprintf(buffer,texto,args);\r
+       va_end(args);\r
+\r
+       gp2x_text(gp2x_screen,x,y,buffer,1);\r
+}\r
+\r
+\r
+void gp2x_text_out8_2(int x, int y, char *texto, int color)\r
+{\r
+       gp2x_text(gp2x_screen, x, y, texto, color);\r
+}\r
+\r
+void gp2x_text_out8_lim(int x, int y, char *texto, int max)\r
+{\r
+       char    buffer[320/8+1];\r
+\r
+       strncpy(buffer, texto, 320/8);\r
+       if (max > 320/8) max = 320/8;\r
+       if (max < 0) max = 0;\r
+       buffer[max] = 0;\r
+\r
+       gp2x_text(gp2x_screen,x,y,buffer,1);\r
+}\r
+\r
+\r
+static unsigned long inp_prev = 0;\r
+static int inp_prevjoy = 0;\r
+\r
+static unsigned long wait_for_input(unsigned long interesting)\r
+{\r
+       unsigned long ret;\r
+       static int repeats = 0, wait = 300*1000;\r
+       int release = 0, i;\r
+\r
+       if (repeats == 5 || repeats == 15 || repeats == 30) wait /= 2;\r
+\r
+       for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
+               if(i == 0) repeats++;\r
+               usleep(wait/6);\r
+       }\r
+\r
+       while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {\r
+               usleep(50000);\r
+               release = 1;\r
+       }\r
+\r
+       if (release || ret != inp_prev) {\r
+               repeats = 0;\r
+               wait = 300*1000;\r
+       }\r
+       inp_prev = ret;\r
+       inp_prevjoy = 0;\r
+\r
+       // we don't need diagonals in menus\r
+       if ((ret&GP2X_UP)   && (ret&GP2X_LEFT))  ret &= ~GP2X_LEFT;\r
+       if ((ret&GP2X_UP)   && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
+       if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT))  ret &= ~GP2X_LEFT;\r
+       if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
+\r
+       return ret;\r
+}\r
+\r
+static unsigned long input2_read(unsigned long interesting, int *joy)\r
+{\r
+       unsigned long ret;\r
+       int i;\r
+\r
+       do\r
+       {\r
+               *joy = 0;\r
+               if ((ret = gp2x_joystick_read(0) & interesting)) break;\r
+               gp2x_usbjoy_update();\r
+               for (i = 0; i < num_of_joys; i++) {\r
+                       ret = gp2x_usbjoy_check2(i);\r
+                       if (ret) { *joy = i + 1; break; }\r
+               }\r
+               if (ret) break;\r
+       }\r
+       while(0);\r
+\r
+       return ret;\r
+}\r
+\r
+// similar to wait_for_input(), but returns joy num\r
+static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy)\r
+{\r
+       unsigned long ret;\r
+       const int wait = 300*1000;\r
+       int i;\r
+\r
+       if (inp_prevjoy == 0) inp_prev &= interesting;\r
+       for (i = 0; i < 6; i++) {\r
+               ret = input2_read(interesting, joy);\r
+               if (*joy != inp_prevjoy || ret != inp_prev) break;\r
+               usleep(wait/6);\r
+       }\r
+\r
+       while ( !(ret = input2_read(interesting, joy)) ) {\r
+               usleep(50000);\r
+       }\r
+\r
+       inp_prev = ret;\r
+       inp_prevjoy = *joy;\r
+\r
+       return ret;\r
+}\r
+\r
+\r
+\r
+// -------------- ROM selector --------------\r
+\r
+static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)\r
+{\r
+       int start, i, pos;\r
+\r
+       start = 12 - sel;\r
+       n--; // exclude current dir (".")\r
+\r
+       memset(gp2x_screen, 0, 320*240);\r
+\r
+       if(start - 2 >= 0)\r
+               gp2x_text_out8_lim(14, (start - 2)*10, curdir, 38);\r
+       for (i = 0; i < n; i++) {\r
+               pos = start + i;\r
+               if (pos < 0)  continue;\r
+               if (pos > 23) break;\r
+               if (namelist[i+1]->d_type == DT_DIR) {\r
+                       gp2x_text_out8_lim(14,   pos*10, "/", 1);\r
+                       gp2x_text_out8_lim(14+8, pos*10, namelist[i+1]->d_name, 37);\r
+               } else {\r
+                       gp2x_text_out8_lim(14,   pos*10, namelist[i+1]->d_name, 38);\r
+               }\r
+       }\r
+       gp2x_text_out8(5, 120, ">");\r
+       gp2x_video_flip();\r
+}\r
+\r
+static int scandir_cmp(const void *p1, const void *p2)\r
+{\r
+       struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;\r
+       if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);\r
+       if ((*d1)->d_type == DT_DIR) return -1; // put before\r
+       if ((*d2)->d_type == DT_DIR) return  1;\r
+       return alphasort(d1, d2);\r
+}\r
+\r
+\r
+static char *romsel_loop(char *curr_path)\r
+{\r
+       struct dirent **namelist;\r
+       DIR *dir;\r
+       int n, sel = 0;\r
+       unsigned long inp = 0;\r
+       char *ret = NULL, *fname = NULL;\r
+\r
+       // is this a dir or a full path?\r
+       if ((dir = opendir(curr_path))) {\r
+               closedir(dir);\r
+       } else {\r
+               char *p;\r
+               for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);\r
+               *p = 0;\r
+               fname = p+1;\r
+       }\r
+\r
+       n = scandir(curr_path, &namelist, 0, scandir_cmp);\r
+       if (n < 0) {\r
+               // try root\r
+               n = scandir(curr_path, &namelist, 0, scandir_cmp);\r
+               if (n < 0) {\r
+                       // oops, we failed\r
+                       printf("dir: "); printf(curr_path); printf("\n");\r
+                       perror("scandir");\r
+                       return NULL;\r
+               }\r
+       }\r
+\r
+       // try to find sel\r
+       if (fname != NULL) {\r
+               int i;\r
+               for (i = 1; i < n; i++) {\r
+                       if (strcmp(namelist[i]->d_name, fname) == 0) {\r
+                               sel = i - 1;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       for (;;)\r
+       {\r
+               draw_dirlist(curr_path, namelist, n, sel);\r
+               inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X);\r
+               if(inp & GP2X_UP  )  { sel--;   if (sel < 0)   sel = n-2; }\r
+               if(inp & GP2X_DOWN)  { sel++;   if (sel > n-2) sel = 0; }\r
+               if(inp & GP2X_LEFT)  { sel-=10; if (sel < 0)   sel = 0; }\r
+               if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }\r
+               if(inp & GP2X_B)     { // enter dir/select\r
+                       again:\r
+                       if (namelist[sel+1]->d_type == DT_REG) {\r
+                               strcpy(romFileName, curr_path);\r
+                               strcat(romFileName, "/");\r
+                               strcat(romFileName, namelist[sel+1]->d_name);\r
+                               ret = romFileName;\r
+                               break;\r
+                       } else if (namelist[sel+1]->d_type == DT_DIR) {\r
+                               int newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;\r
+                               char *p, *newdir = malloc(newlen);\r
+                               if (strcmp(namelist[sel+1]->d_name, "..") == 0) {\r
+                                       char *start = curr_path;\r
+                                       p = start + strlen(start) - 1;\r
+                                       while (*p == '/' && p > start) p--;\r
+                                       while (*p != '/' && p > start) p--;\r
+                                       if (p <= start) strcpy(newdir, "/");\r
+                                       else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }\r
+                               } else {\r
+                                       strcpy(newdir, curr_path);\r
+                                       p = newdir + strlen(newdir) - 1;\r
+                                       while (*p == '/' && p >= newdir) *p-- = 0;\r
+                                       strcat(newdir, "/");\r
+                                       strcat(newdir, namelist[sel+1]->d_name);\r
+                               }\r
+                               ret = romsel_loop(newdir);\r
+                               free(newdir);\r
+                               break;\r
+                       } else {\r
+                               // unknown file type, happens on NTFS mounts. Try to guess.\r
+                               FILE *tstf; int tmp;\r
+                               strcpy(romFileName, curr_path);\r
+                               strcat(romFileName, "/");\r
+                               strcat(romFileName, namelist[sel+1]->d_name);\r
+                               tstf = fopen(romFileName, "rb");\r
+                               if (tstf != NULL)\r
+                               {\r
+                                       if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)\r
+                                               namelist[sel+1]->d_type = DT_REG;\r
+                                       else    namelist[sel+1]->d_type = DT_DIR;\r
+                                       fclose(tstf);\r
+                                       goto again;\r
+                               }\r
+                       }\r
+               }\r
+               if(inp & GP2X_X) break; // cancel\r
+       }\r
+\r
+       if (n > 0) {\r
+               while(n--) free(namelist[n]);\r
+               free(namelist);\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+// -------------- key config --------------\r
+\r
+static char *usb_joy_key_name(int joy, int num)\r
+{\r
+       static char name[16];\r
+       switch (num)\r
+       {\r
+               case 0: sprintf(name, "Joy%i UP", joy); break;\r
+               case 1: sprintf(name, "Joy%i DOWN", joy); break;\r
+               case 2: sprintf(name, "Joy%i LEFT", joy); break;\r
+               case 3: sprintf(name, "Joy%i RIGHT", joy); break;\r
+               default:sprintf(name, "Joy%i b%i", joy, num-3); break;\r
+       }\r
+       return name;\r
+}\r
+\r
+static void draw_key_config(int curr_act, int is_p2)\r
+{\r
+       char strkeys[32*5];\r
+       int joy, i;\r
+\r
+       strkeys[0] = 0;\r
+       for (i = 0; i < 32; i++)\r
+       {\r
+               if (currentConfig.KeyBinds[i] & (1 << curr_act))\r
+               {\r
+                       if (curr_act < 16 && (currentConfig.KeyBinds[i] & (1 << 16)) != (is_p2 << 16)) continue;\r
+                       if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gp2xKeyNames[i]); break; }\r
+                       else strcpy(strkeys, gp2xKeyNames[i]);\r
+               }\r
+       }\r
+       for (joy = 0; joy < num_of_joys; joy++)\r
+       {\r
+               for (i = 0; i < 32; i++)\r
+               {\r
+                       if (currentConfig.JoyBinds[joy][i] & (1 << curr_act))\r
+                       {\r
+                               if (curr_act < 16 && (currentConfig.JoyBinds[joy][i] & (1 << 16)) != (is_p2 << 16)) continue;\r
+                               if (strkeys[0]) {\r
+                                       strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));\r
+                                       break;\r
+                               }\r
+                               else strcpy(strkeys, usb_joy_key_name(joy + 1, i));\r
+                       }\r
+               }\r
+       }\r
+\r
+       memset(gp2x_screen, 0, 320*240);\r
+       gp2x_text_out8(60, 40, "Action: %s", actionNames[curr_act]);\r
+       gp2x_text_out8(60, 60, "Keys: %s", strkeys);\r
+\r
+       gp2x_text_out8(30, 180, "Use SELECT to change action");\r
+       gp2x_text_out8(30, 190, "Press a key to bind/unbind");\r
+       gp2x_text_out8(30, 200, "Select \"Done\" action and");\r
+       gp2x_text_out8(30, 210, "  press any key to finish");\r
+       gp2x_video_flip();\r
+}\r
+\r
+static void key_config_loop(int is_p2)\r
+{\r
+       int curr_act = 0, joy = 0, i;\r
+       unsigned long inp = 0;\r
+\r
+       for (;;)\r
+       {\r
+               draw_key_config(curr_act, is_p2);\r
+               inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS, &joy);\r
+               // printf("got %08lX from joy %i\n", inp, joy);\r
+               if (joy == 0) {\r
+                       if (inp & GP2X_SELECT) {\r
+                               curr_act++;\r
+                               while (!actionNames[curr_act] && curr_act < 32) curr_act++;\r
+                               if (curr_act > 31) curr_act = 0;\r
+                       }\r
+                       inp &= CONFIGURABLE_KEYS;\r
+                       inp &= ~GP2X_SELECT;\r
+               }\r
+               if (curr_act == 31 && inp) break;\r
+               if (joy == 0) {\r
+                       for (i = 0; i < 32; i++)\r
+                               if (inp & (1 << i)) {\r
+                                       currentConfig.KeyBinds[i] ^= (1 << curr_act);\r
+                                       if (is_p2) currentConfig.KeyBinds[i] |=  (1 << 16); // player 2 flag\r
+                                       else       currentConfig.KeyBinds[i] &= ~(1 << 16);\r
+                               }\r
+               } else {\r
+                       for (i = 0; i < 32; i++)\r
+                               if (inp & (1 << i)) {\r
+                                       currentConfig.JoyBinds[joy-1][i] ^= (1 << curr_act);\r
+                                       if (is_p2) currentConfig.JoyBinds[joy-1][i] |=  (1 << 16);\r
+                                       else       currentConfig.JoyBinds[joy-1][i] &= ~(1 << 16);\r
+                               }\r
+               }\r
+       }\r
+}\r
+\r
+static void draw_kc_sel(int menu_sel)\r
+{\r
+       int tl_x = 25+40, tl_y = 60, y, i;\r
+       char joyname[36];\r
+\r
+       y = tl_y;\r
+       memset(gp2x_screen, 0, 320*240);\r
+       gp2x_text_out8(tl_x, y,       "Player 1");\r
+       gp2x_text_out8(tl_x, (y+=10), "Player 2");\r
+       gp2x_text_out8(tl_x, (y+=10), "Done");\r
+\r
+       // draw cursor\r
+       gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");\r
+\r
+       tl_x = 25;\r
+       gp2x_text_out8(tl_x, (y=110), "USB joys detected:");\r
+       if (num_of_joys > 0) {\r
+               for (i = 0; i < num_of_joys; i++) {\r
+                       strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
+                       gp2x_text_out8(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
+               }\r
+       } else {\r
+               gp2x_text_out8(tl_x, (y+=10), "none");\r
+       }\r
+\r
+\r
+       gp2x_video_flip();\r
+}\r
+\r
+static void kc_sel_loop(void)\r
+{\r
+       int menu_sel = 2, menu_sel_max = 2;\r
+       unsigned long inp = 0;\r
+\r
+       for(;;)\r
+       {\r
+               draw_kc_sel(menu_sel);\r
+               inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
+               if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
+               if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
+               if(inp & GP2X_B) {\r
+                       switch (menu_sel) {\r
+                               case 0: key_config_loop(0); return;\r
+                               case 1: key_config_loop(1); return;\r
+                               default: return;\r
+                       }\r
+               }\r
+               if(inp & GP2X_X) return;\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+// --------- advanced options ----------\r
+\r
+// order must match that of currentConfig_t\r
+struct {\r
+       int EmuOpt;\r
+       int PicoOpt;\r
+       int PsndRate;\r
+       int PicoRegion;\r
+       int Frameskip;\r
+       int CPUclock;\r
+} tmp_opts;\r
+int tmp_gamma;\r
+\r
+static void draw_amenu_options(int menu_sel)\r
+{\r
+       int tl_x = 25, tl_y = 60, y;\r
+       char *mms = mmuhack_status ? "active)  " : "inactive)";\r
+\r
+       y = tl_y;\r
+       memset(gp2x_screen, 0, 320*240);\r
+       gp2x_text_out8(tl_x, y,       "Scale 32 column mode       %s", (tmp_opts.PicoOpt&0x100)?"ON":"OFF"); // 0\r
+       gp2x_text_out8(tl_x, (y+=10), "Gamma correction           %i.%02i", tmp_gamma / 100, tmp_gamma%100); // 1\r
+       gp2x_text_out8(tl_x, (y+=10), "Emulate Z80                %s", (tmp_opts.PicoOpt&0x004)?"ON":"OFF"); // 2\r
+       gp2x_text_out8(tl_x, (y+=10), "Emulate YM2612 (FM)        %s", (tmp_opts.PicoOpt&0x001)?"ON":"OFF"); // 3\r
+       gp2x_text_out8(tl_x, (y+=10), "Emulate SN76496 (PSG)      %s", (tmp_opts.PicoOpt&0x002)?"ON":"OFF"); // 4\r
+       gp2x_text_out8(tl_x, (y+=10), "gzip savestates            %s", (tmp_opts.EmuOpt &0x008)?"ON":"OFF"); // 5\r
+       gp2x_text_out8(tl_x, (y+=10), "Don't save config on exit  %s", (tmp_opts.EmuOpt &0x020)?"ON":"OFF"); // 6\r
+       gp2x_text_out8(tl_x, (y+=10), "needs restart:");\r
+       gp2x_text_out8(tl_x, (y+=10), "craigix's RAM timings      %s", (tmp_opts.EmuOpt &0x100)?"ON":"OFF"); // 8\r
+       gp2x_text_out8(tl_x, (y+=10), "squidgehack (now %s %s",   mms, (tmp_opts.EmuOpt &0x010)?"ON":"OFF"); // 9\r
+       gp2x_text_out8(tl_x, (y+=10), "Done");\r
+\r
+       // draw cursor\r
+       gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");\r
+\r
+       gp2x_video_flip();\r
+}\r
+\r
+static void amenu_loop_options(void)\r
+{\r
+       int menu_sel = 0, menu_sel_max = 11;\r
+       unsigned long inp = 0;\r
+\r
+       for(;;)\r
+       {\r
+               draw_amenu_options(menu_sel);\r
+               inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
+               if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
+               if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
+               if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options\r
+                       switch (menu_sel) {\r
+                               case  0: tmp_opts.PicoOpt^=0x100; break;\r
+                               case  2: tmp_opts.PicoOpt^=0x004; break;\r
+                               case  3: tmp_opts.PicoOpt^=0x001; break;\r
+                               case  4: tmp_opts.PicoOpt^=0x002; break;\r
+                               case  5: tmp_opts.EmuOpt ^=0x008; break;\r
+                               case  6: tmp_opts.EmuOpt ^=0x020; break;\r
+                               case  8: tmp_opts.EmuOpt ^=0x100; break;\r
+                               case  9: tmp_opts.EmuOpt ^=0x010; break;\r
+                               case 10: return;\r
+                       }\r
+               }\r
+               if(inp & (GP2X_X|GP2X_A)) return;\r
+               if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
+                       switch (menu_sel) {\r
+                               case 1:\r
+                                       while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
+                                               tmp_gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
+                                               if (tmp_gamma <   1) tmp_gamma =   1;\r
+                                               if (tmp_gamma > 300) tmp_gamma = 300;\r
+                                               draw_amenu_options(menu_sel);\r
+                                               usleep(18*1000);\r
+                                       }\r
+                                       break;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+// -------------- options --------------\r
+\r
+static char *region_name(unsigned int code)\r
+{\r
+       char *names[] = { "Auto", "Japan NTSC", "Japan PAL", "USA", "Europe" };\r
+       int i = 0;\r
+       code <<= 1;\r
+       while((code >>=1)) i++;\r
+       if (i > 4) return "unknown";\r
+       return names[i];\r
+}\r
+\r
+static void draw_menu_options(int menu_sel)\r
+{\r
+       int tl_x = 25, tl_y = 40, y;\r
+       char monostereo[8], strframeskip[8], *strrend;\r
+\r
+       strcpy(monostereo, (tmp_opts.PicoOpt&0x08)?"stereo":"mono");\r
+       if (tmp_opts.Frameskip < 0)\r
+                strcpy(strframeskip, "Auto");\r
+       else sprintf(strframeskip, "%i", tmp_opts.Frameskip);\r
+       if (tmp_opts.PicoOpt&0x10) {\r
+               strrend = " 8bit fast";\r
+       } else if (tmp_opts.EmuOpt&0x80) {\r
+               strrend = "16bit accurate";\r
+       } else {\r
+               strrend = " 8bit accurate";\r
+       }\r
+\r
+       y = tl_y;\r
+       memset(gp2x_screen, 0, 320*240);\r
+       gp2x_text_out8(tl_x, y,       "Renderer:            %s", strrend); // 0\r
+       gp2x_text_out8(tl_x, (y+=10), "Accurate timing (slower)   %s", (tmp_opts.PicoOpt&0x040)?"ON":"OFF"); // 1\r
+       gp2x_text_out8(tl_x, (y+=10), "Accurate sprites (slower)  %s", (tmp_opts.PicoOpt&0x080)?"ON":"OFF"); // 2\r
+       gp2x_text_out8(tl_x, (y+=10), "Show FPS                   %s", (tmp_opts.EmuOpt &0x002)?"ON":"OFF"); // 3\r
+       gp2x_text_out8(tl_x, (y+=10), "Frameskip                  %s", strframeskip);\r
+       gp2x_text_out8(tl_x, (y+=10), "Enable sound               %s", (tmp_opts.EmuOpt &0x004)?"ON":"OFF"); // 5\r
+       gp2x_text_out8(tl_x, (y+=10), "Sound Quality:     %5iHz %s", tmp_opts.PsndRate, monostereo);\r
+       gp2x_text_out8(tl_x, (y+=10), "Use ARM940 core for sound  %s", (tmp_opts.PicoOpt&0x200)?"ON":"OFF"); // 7\r
+       gp2x_text_out8(tl_x, (y+=10), "6 button pad               %s", (tmp_opts.PicoOpt&0x020)?"ON":"OFF"); // 8\r
+       gp2x_text_out8(tl_x, (y+=10), "Genesis Region:            %s", region_name(tmp_opts.PicoRegion));\r
+       gp2x_text_out8(tl_x, (y+=10), "Use SRAM savestates        %s", (tmp_opts.EmuOpt &0x001)?"ON":"OFF"); // 10\r
+       gp2x_text_out8(tl_x, (y+=10), "Confirm save overwrites    %s", (tmp_opts.EmuOpt &0x200)?"ON":"OFF"); // 11\r
+       gp2x_text_out8(tl_x, (y+=10), "Save slot                  %i", state_slot); // 12\r
+       gp2x_text_out8(tl_x, (y+=10), "GP2X CPU clocks            %iMhz", tmp_opts.CPUclock);\r
+       gp2x_text_out8(tl_x, (y+=10), "[advanced options]");\r
+       gp2x_text_out8(tl_x, (y+=10), "Save cfg as default");\r
+       if (rom_data)\r
+               gp2x_text_out8(tl_x, (y+=10), "Save cfg for current game only");\r
+\r
+       // draw cursor\r
+       gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");\r
+\r
+       gp2x_video_flip();\r
+}\r
+\r
+static int sndrate_prevnext(int rate, int dir)\r
+{\r
+       int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
+\r
+       for (i = 0; i < 5; i++)\r
+               if (rates[i] == rate) break;\r
+\r
+       i += dir ? 1 : -1;\r
+       if (i > 4) return dir ? 44100 : 22050;\r
+       if (i < 0) return dir ? 11025 : 8000;\r
+       return rates[i];\r
+}\r
+\r
+static void menu_options_save(void)\r
+{\r
+       memcpy(&currentConfig.EmuOpt, &tmp_opts.EmuOpt, sizeof(tmp_opts));\r
+       currentConfig.gamma = tmp_gamma;\r
+       PicoOpt = currentConfig.PicoOpt;\r
+       PsndRate = currentConfig.PsndRate;\r
+       PicoRegionOverride = currentConfig.PicoRegion;\r
+       if (PicoOpt & 0x20) {\r
+               actionNames[ 8] = "Z"; actionNames[ 9] = "Y";\r
+               actionNames[10] = "X"; actionNames[11] = "MODE";\r
+       } else {\r
+               actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;\r
+       }\r
+}\r
+\r
+static void menu_loop_options(void)\r
+{\r
+       int menu_sel = 0, menu_sel_max = 15;\r
+       unsigned long inp = 0;\r
+\r
+       if (rom_data) menu_sel_max++;\r
+       memcpy(&tmp_opts.EmuOpt, &currentConfig.EmuOpt, sizeof(tmp_opts));\r
+       tmp_gamma = currentConfig.gamma;\r
+       tmp_opts.PicoOpt = PicoOpt;\r
+       tmp_opts.PsndRate = PsndRate;\r
+       tmp_opts.PicoRegion = PicoRegionOverride;\r
+\r
+       for(;;)\r
+       {\r
+               draw_menu_options(menu_sel);\r
+               inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
+               if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
+               if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
+               if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options\r
+                       switch (menu_sel) {\r
+                               case  1: tmp_opts.PicoOpt^=0x040; break;\r
+                               case  2: tmp_opts.PicoOpt^=0x080; break;\r
+                               case  3: tmp_opts.EmuOpt ^=0x002; break;\r
+                               case  5: tmp_opts.EmuOpt ^=0x004; break;\r
+                               case  7: tmp_opts.PicoOpt^=0x200; break;\r
+                               case  8: tmp_opts.PicoOpt^=0x020; break;\r
+                               case 10: tmp_opts.EmuOpt ^=0x001; break;\r
+                               case 11: tmp_opts.EmuOpt ^=0x200; break;\r
+                               case 14: amenu_loop_options();    break;\r
+                               case 15: // done (save)\r
+                                       menu_options_save();\r
+                                       emu_WriteConfig(0);\r
+                                       return;\r
+                               case 16: // done (save for current game)\r
+                                       menu_options_save();\r
+                                       emu_WriteConfig(1);\r
+                                       return;\r
+                       }\r
+               }\r
+               if(inp & GP2X_X) return;  // done (no save)\r
+               if(inp & GP2X_A) {\r
+                       menu_options_save();\r
+                       return;  // done (save)\r
+               }\r
+               if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
+                       switch (menu_sel) {\r
+                               case  0:\r
+                                       if (inp & GP2X_LEFT) {\r
+                                               if      (  tmp_opts.PicoOpt&0x10) tmp_opts.PicoOpt&= ~0x10;\r
+                                               else if (!(tmp_opts.EmuOpt &0x80))tmp_opts.EmuOpt |=  0x80;\r
+                                               else if (  tmp_opts.EmuOpt &0x80) break;\r
+                                       } else {\r
+                                               if      (  tmp_opts.PicoOpt&0x10) break;\r
+                                               else if (!(tmp_opts.EmuOpt &0x80))tmp_opts.PicoOpt|=  0x10;\r
+                                               else if (  tmp_opts.EmuOpt &0x80) tmp_opts.EmuOpt &= ~0x80;\r
+                                       }\r
+                                       break;\r
+                               case  4:\r
+                                       tmp_opts.Frameskip += (inp & GP2X_LEFT) ? -1 : 1;\r
+                                       if (tmp_opts.Frameskip < 0)  tmp_opts.Frameskip = -1;\r
+                                       if (tmp_opts.Frameskip > 32) tmp_opts.Frameskip = 32;\r
+                                       break;\r
+                               case  6:\r
+                                       if ((inp & GP2X_RIGHT) && tmp_opts.PsndRate == 44100 && !(tmp_opts.PicoOpt&0x08)) {\r
+                                               tmp_opts.PsndRate = 8000;  tmp_opts.PicoOpt|= 0x08;\r
+                                       } else if ((inp & GP2X_LEFT) && tmp_opts.PsndRate == 8000 && (tmp_opts.PicoOpt&0x08)) {\r
+                                               tmp_opts.PsndRate = 44100; tmp_opts.PicoOpt&=~0x08;\r
+                                       } else tmp_opts.PsndRate = sndrate_prevnext(tmp_opts.PsndRate, inp & GP2X_RIGHT);\r
+                                       break;\r
+                               case  9:\r
+                                       if (inp & GP2X_RIGHT) {\r
+                                               if (tmp_opts.PicoRegion) tmp_opts.PicoRegion<<=1; else tmp_opts.PicoRegion=1;\r
+                                               if (tmp_opts.PicoRegion > 8) tmp_opts.PicoRegion = 8;\r
+                                       } else  tmp_opts.PicoRegion>>=1;\r
+                                       break;\r
+                               case 12:\r
+                                       if (inp & GP2X_RIGHT) {\r
+                                               state_slot++; if (state_slot > 9) state_slot = 0;\r
+                                       } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
+                                       }\r
+                                       break;\r
+                               case 13:\r
+                                       while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
+                                               tmp_opts.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
+                                               if (tmp_opts.CPUclock < 1) tmp_opts.CPUclock = 1;\r
+                                               draw_menu_options(menu_sel);\r
+                                               usleep(50*1000);\r
+                                       }\r
+                                       break;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+// -------------- credits --------------\r
+\r
+static void draw_menu_credits(void)\r
+{\r
+       int tl_x = 15, tl_y = 70, y;\r
+       memset(gp2x_screen, 0, 320*240);\r
+\r
+       gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006");\r
+       y = tl_y;\r
+       gp2x_text_out8(tl_x, y, "Credits:");\r
+       gp2x_text_out8(tl_x, (y+=10), "Dave: Cyclone 68000 core,");\r
+       gp2x_text_out8(tl_x, (y+=10), "      base code of PicoDrive");\r
+       gp2x_text_out8(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
+       gp2x_text_out8(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
+       gp2x_text_out8(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");\r
+       gp2x_text_out8(tl_x, (y+=10), "Stephane Dallongeville:");\r
+       gp2x_text_out8(tl_x, (y+=10), "      opensource Gens");\r
+       gp2x_text_out8(tl_x, (y+=10), "Haze: Genesis hw info");\r
+       gp2x_text_out8(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
+       gp2x_text_out8(tl_x, (y+=10), "Squidge: squidgehack");\r
+       gp2x_text_out8(tl_x, (y+=10), "Dzz: ARM940 sample");\r
+       gp2x_text_out8(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");\r
+       gp2x_text_out8(tl_x, (y+=10), "craigix: GP2X hardware");\r
+\r
+       gp2x_video_flip();\r
+}\r
+\r
+\r
+// -------------- root menu --------------\r
+\r
+static void draw_menu_root(int menu_sel)\r
+{\r
+       int tl_x = 70, tl_y = 70, y;\r
+       memset(gp2x_screen, 0, 320*240);\r
+\r
+       gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION);\r
+\r
+       y = tl_y;\r
+       if (rom_data) {\r
+               gp2x_text_out8(tl_x, y,       "Resume game");\r
+               gp2x_text_out8(tl_x, (y+=10), "Save State");\r
+               gp2x_text_out8(tl_x, (y+=10), "Load State");\r
+               gp2x_text_out8(tl_x, (y+=10), "Reset game");\r
+       } else {\r
+               y += 30;\r
+       }\r
+       gp2x_text_out8(tl_x, (y+=10), "Load new ROM");\r
+       gp2x_text_out8(tl_x, (y+=10), "Change options");\r
+       gp2x_text_out8(tl_x, (y+=10), "Configure controls");\r
+       gp2x_text_out8(tl_x, (y+=10), "Credits");\r
+       gp2x_text_out8(tl_x, (y+=10), "Exit");\r
+\r
+       // draw cursor\r
+       gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");\r
+       // error\r
+       if (menuErrorMsg[0]) gp2x_text_out8(5, 226, menuErrorMsg);\r
+       gp2x_video_flip();\r
+}\r
+\r
+\r
+static void menu_loop_root(void)\r
+{\r
+       int menu_sel = 4, menu_sel_max = 8, menu_sel_min = 4;\r
+       unsigned long inp = 0;\r
+       char curr_path[PATH_MAX], *selfname;\r
+       FILE *tstf;\r
+\r
+       if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
+       {\r
+               fclose(tstf);\r
+               strcpy(curr_path, currentConfig.lastRomFile);\r
+       }\r
+       else\r
+       {\r
+               getcwd(curr_path, PATH_MAX);\r
+       }\r
+\r
+       if (rom_data) menu_sel = menu_sel_min = 0;\r
+\r
+       for(;;)\r
+       {\r
+               draw_menu_root(menu_sel);\r
+               inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT);\r
+               if(inp & GP2X_UP  )  { menu_sel--; if (menu_sel < menu_sel_min) menu_sel = menu_sel_max; }\r
+               if(inp & GP2X_DOWN)  { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = menu_sel_min; }\r
+               if(inp &(GP2X_SELECT|GP2X_X)){\r
+                       if (rom_data) {\r
+                               while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
+                               engineState = PGS_Running;\r
+                               break;\r
+                       }\r
+               }\r
+               if(inp & GP2X_B   )  {\r
+                       switch (menu_sel) {\r
+                               case 0: // resume game\r
+                                       if (rom_data) { engineState = PGS_Running; return; }\r
+                                       break;\r
+                               case 1: // save state\r
+                                       if (rom_data) {\r
+                                               if(emu_SaveLoadGame(0, 0)) {\r
+                                                       strcpy(menuErrorMsg, "save failed");\r
+                                                       continue;\r
+                                               }\r
+                                               engineState = PGS_Running;\r
+                                               return;\r
+                                       }\r
+                                       break;\r
+                               case 2: // load state\r
+                                       if (rom_data) {\r
+                                               if(emu_SaveLoadGame(1, 0)) {\r
+                                                       strcpy(menuErrorMsg, "load failed");\r
+                                                       continue;\r
+                                               }\r
+                                               engineState = PGS_Running;\r
+                                               return;\r
+                                       }\r
+                                       break;\r
+                               case 3: // reset game\r
+                                       if (rom_data) {\r
+                                               emu_ResetGame();\r
+                                               engineState = PGS_Running;\r
+                                               return;\r
+                                       }\r
+                                       break;\r
+                               case 4: // select rom\r
+                                       selfname = romsel_loop(curr_path);\r
+                                       if (selfname) {\r
+                                               printf("selected file: %s\n", selfname);\r
+                                               strncpy(currentConfig.lastRomFile, selfname, sizeof(currentConfig.lastRomFile)-1);\r
+                                               currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0;\r
+                                               engineState = PGS_ReloadRom;\r
+                                       }\r
+                                       return;\r
+                               case 5: // options\r
+                                       menu_loop_options();\r
+                                       break;\r
+                               case 6: // controls\r
+                                       kc_sel_loop();\r
+                                       break;\r
+                               case 7: // credits\r
+                                       draw_menu_credits();\r
+                                       usleep(500*1000);\r
+                                       inp = wait_for_input(GP2X_B|GP2X_X);\r
+                                       break;\r
+                               case 8: // exit\r
+                                       engineState = PGS_Quit;\r
+                                       return;\r
+                       }\r
+               }\r
+               menuErrorMsg[0] = 0; // clear error msg\r
+       }\r
+}\r
+\r
+\r
+void menu_loop(void)\r
+{\r
+       int pal[2];\r
+\r
+       // switch to 8bpp\r
+       gp2x_video_changemode(8);\r
+       gp2x_video_RGB_setscaling(320, 240);\r
+       // set pal\r
+       pal[0] = 0;\r
+       pal[1] = 0x00ffffff;\r
+       gp2x_video_setpalette(pal, 2);\r
+\r
+       menu_loop_root();\r
+\r
+       menuErrorMsg[0] = 0;\r
+}\r
diff --git a/gp2x/menu.h b/gp2x/menu.h
new file mode 100644 (file)
index 0000000..197500e
--- /dev/null
@@ -0,0 +1,16 @@
+// (c) Copyright 2006 notaz, All rights reserved.\r
+// Free for non-commercial use.\r
+\r
+// For commercial use, separate licencing terms must be obtained.\r
+\r
+extern char menuErrorMsg[40];\r
+\r
+void gp2x_text_out8  (int x, int y, char *texto, ...);\r
+void gp2x_text_out15 (int x, int y, char *text);\r
+void gp2x_text_out8_2(int x, int y, char *texto, int color);\r
+void menu_loop(void);\r
+\r
+#define CONFIGURABLE_KEYS \\r
+       (GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_A|GP2X_B|GP2X_X|GP2X_Y| \\r
+               GP2X_START|GP2X_SELECT|GP2X_L|GP2X_R|GP2X_PUSH|GP2X_VOL_UP|GP2X_VOL_DOWN)\r
+\r
diff --git a/gp2x/mmuhack.c b/gp2x/mmuhack.c
new file mode 100644 (file)
index 0000000..c0c2189
--- /dev/null
@@ -0,0 +1,104 @@
+#include <linux/config.h>\r
+#include <linux/module.h>\r
+#include <linux/kernel.h>\r
+#include <linux/init.h>\r
+#include <linux/miscdevice.h>\r
+#include <asm/memory.h>\r
+\r
+#define MMUHACK_MINOR 225\r
+#define DEVICE_NAME "mmuhack"\r
+\r
+#if __GNUC__ == 3\r
+#include <linux/version.h>\r
+static const char __module_kernel_version_gcc3[] __attribute__((__used__)) __attribute__((section(".modinfo"))) =\r
+"kernel_version=" UTS_RELEASE;\r
+#endif\r
+\r
+static ssize_t mmuhack_open(struct inode *inode, struct file *filp)\r
+{\r
+    unsigned int *pgtable;\r
+       unsigned int *cpt;\r
+    int i, j;\r
+       int ttb;\r
+       int ret = -EFAULT;\r
+\r
+       // get the pointer to the translation table base...\r
+       asm volatile(\r
+               "stmdb sp!, {r0}\n\t"\r
+               "mrc p15, 0, r0, c2, c0, 0\n\t"\r
+               "mov %0, r0\n\t"\r
+               "ldmia sp!, {r0}\n\t": "=r"(ttb)\r
+       );\r
+\r
+       pgtable = __va(ttb);\r
+\r
+    for (i = 0; i < 4096; i ++) if ( (pgtable[i] & 3) == 1 ) {\r
+               cpt = __va(pgtable[i] & 0xfffffc00);\r
+\r
+               for (j = 0; j < 256; j ++) {/*\r
+                       if ( (cpt[j] & 0xfe00000f) == 0x02000002 ) {\r
+                               // set C and B bits in upper 32MB memory area...\r
+                               printk("Set C&B bits %08x\n",cpt[j]);\r
+                               cpt[j] |= 0xFFC;\r
+                               ret = 0;\r
+                       }\r
+                                          */\r
+                       if (((cpt[j] & 0xff000000) == 0x02000000) && ((cpt[j] & 12)==0) )\r
+                       {\r
+                               //printk("Set C&B bits %08x\n",cpt[j]);\r
+                               cpt[j] |= 0xFFC;\r
+                       }\r
+                       //if ((a>=0x31 && a<=0x36) && ((cpt[i] & 12)==0))\r
+                       if (((cpt[j] & 0xff000000) == 0x03000000) && ((cpt[j] & 12)==0))\r
+                       {\r
+                               //printk("Set C&B bits %08x\n",cpt[j]);\r
+                               //printf("SDL   c and b bits not set, overwriting\n");\r
+                               cpt[j] |= 0xFFC;\r
+                       }\r
+               }\r
+    }\r
+\r
+       // drain the write buffer and flush the tlb caches...\r
+       asm volatile(\r
+               "stmdb sp!, {r0}\n\t"\r
+               "mov    r0, #0\n\t"\r
+               "mcr    15, 0, r0, cr7, cr10, 4\n\t"\r
+               "mcr    15, 0, r0, cr8, cr7, 0\n\t"\r
+               "ldmia sp!, {r0}\n\t"\r
+       );\r
+\r
+       if (ret == 0)\r
+               printk("MMU hack applied.\n");\r
+\r
+       return 0;\r
+}\r
+\r
+static struct file_operations mmuhack_fops = {\r
+    owner:      THIS_MODULE,\r
+    open:       mmuhack_open,\r
+};\r
+\r
+\r
+static struct miscdevice mmuhack = {\r
+    MMUHACK_MINOR, DEVICE_NAME, &mmuhack_fops\r
+};\r
+\r
+static int __init mmuhack_init(void)\r
+{\r
+       misc_register(&mmuhack);\r
+/*\r
+       printk("MMSP2 MMU Hack module.\n");\r
+*/\r
+    return 0;\r
+}\r
+\r
+static void __exit mmuhack_exit(void)\r
+{\r
+    misc_deregister(&mmuhack);\r
+/*\r
+       printk(KERN_ALERT "MMU Hack module removed.\n");\r
+*/\r
+}\r
+\r
+module_init(mmuhack_init);\r
+module_exit(mmuhack_exit);\r
diff --git a/gp2x/mmuhack.txt b/gp2x/mmuhack.txt
new file mode 100644 (file)
index 0000000..207e09c
--- /dev/null
@@ -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 (file)
index 0000000..239df01
--- /dev/null
@@ -0,0 +1,18 @@
+// port specific settings\r
+\r
+#ifndef PORT_CONFIG_H\r
+#define PORT_CONFIG_H\r
+\r
+#define CPU_CALL\r
+\r
+// draw2.c\r
+#define START_ROW  0 // which row of tiles to start rendering at?\r
+#define END_ROW   28 // ..end\r
+\r
+// pico.c\r
+#define CAN_HANDLE_240_LINES   1\r
+\r
+//#define dprintf(f,...) printf(f"\n",##__VA_ARGS__)\r
+#define dprintf(x...)\r
+\r
+#endif //PORT_CONFIG_H\r
diff --git a/gp2x/port_config.s b/gp2x/port_config.s
new file mode 100644 (file)
index 0000000..094ea3c
--- /dev/null
@@ -0,0 +1,8 @@
+@ .equiv START_ROW,    1\r
+@ .equiv END_ROW,              27\r
+@ one row means 8 pixels. If above example was used, (27-1)*8=208 lines would be rendered.\r
+.equiv START_ROW,              0\r
+.equiv END_ROW,                28\r
+\r
+@ this should be set to one only for GP2X port\r
+.equiv EXTERNAL_YM2612, 1\r
diff --git a/gp2x/squidgehack.c b/gp2x/squidgehack.c
new file mode 100644 (file)
index 0000000..f831bd4
--- /dev/null
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+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 (file)
index 0000000..a83c737
--- /dev/null
@@ -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 (file)
index 0000000..c0d28cb
--- /dev/null
@@ -0,0 +1,37 @@
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <unistd.h>\r
+#include <sys/time.h>\r
+#include "gp2x.h"\r
+\r
+void spend_cycles(int c);\r
+\r
+int main(void)\r
+{\r
+       struct timeval tval; // timing\r
+       int thissec = 0, frames_done = 0;\r
+\r
+       gp2x_init();\r
+\r
+       for (;;)\r
+       {\r
+               gettimeofday(&tval, 0);\r
+\r
+               if(thissec != tval.tv_sec)\r
+               {\r
+                       thissec = tval.tv_sec;\r
+\r
+                       printf("frames_done: %i\n", frames_done);\r
+                       frames_done = 0;\r
+               }\r
+\r
+\r
+               //gp2x_video_wait_vsync();\r
+               //usleep(1); // sleeps a minimum of ~20ms\r
+               //gp2x_video_flip(); // can be called ~430000 times/sec\r
+               spend_cycles(1000);\r
+               frames_done++;\r
+       }\r
+\r
+}\r
+\r
diff --git a/gp2x/usbjoy.c b/gp2x/usbjoy.c
new file mode 100644 (file)
index 0000000..569f615
--- /dev/null
@@ -0,0 +1,424 @@
+/* Title: USB Joystick library
+   Version 0.2
+   Written by Puck2099 (puck2099@gmail.com), (c) 2006.
+   <http://www.gp32wip.com>
+
+   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 <stdlib.h>
+#include <stdio.h>             /* For the definition of NULL */
+#include <sys/types.h>         // For Device open
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>            // For Device read
+
+#include <string.h>
+#include <limits.h>            /* For the definition of PATH_MAX */
+#include <linux/joystick.h>
+
+#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 <usbjoy> 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 <usbjoy> 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 <usbjoy> 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 <usbjoy> 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 (<statebuttons> and <stateaxes> 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: <usbjoy> 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; i<len; ++i ) {
+       switch (events[i].type & ~JS_EVENT_INIT) {
+       case JS_EVENT_AXIS:
+         if (events[i].number == 0) {
+           if (events[i].value == 0) joy->stateaxes[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: <usbjoy> 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 <Axes values>).
+  joy - Selected joystick.
+
+  Returns:
+
+  0 - Direction NOT pushed.
+  1 - Direction pushed.
+  -1 - Error: <usbjoy> 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: <usbjoy> 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; q<joy_buttons (joy); q++) {
+                       if (joy_getbutton (q, joy)) {
+                               if (joy->type == 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<num_of_joys; i++) {
+               joy_close (joys[i]);
+       }
+       num_of_joys = 0;
+}
+
diff --git a/gp2x/usbjoy.h b/gp2x/usbjoy.h
new file mode 100644 (file)
index 0000000..68fc33a
--- /dev/null
@@ -0,0 +1,241 @@
+/* Title: USB Joystick library
+   Version 0.2
+   Written by Puck2099 (puck2099@gmail.com), (c) 2006.
+   <http://www.gp32wip.com>
+
+   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:
+  <joy_getaxe>
+*/
+#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 <usbjoy> 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 <usbjoy> 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 <usbjoy> 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 <usbjoy> struct is empty.
+*/
+int joy_axes (struct usbjoy * joy);
+
+
+/*
+  Function: joy_update
+
+  Updates Joystick's internal information (<statebuttons> and <stateaxes> 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: <usbjoy> 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: <usbjoy> 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 <Axes values>).
+  joy - Selected joystick.
+
+  Returns:
+
+  0 - Direction NOT pushed.
+  1 - Direction pushed.
+  -1 - Error: <usbjoy> 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: <usbjoy> 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 (file)
index 0000000..1331a8a
--- /dev/null
@@ -0,0 +1,2 @@
+#define VERSION "0.964"\r
+\r
diff --git a/linux/940ctl_ym2612.c b/linux/940ctl_ym2612.c
new file mode 100644 (file)
index 0000000..9702914
--- /dev/null
@@ -0,0 +1,112 @@
+/* faked 940 code just uses local copy of ym2612 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#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 (file)
index 0000000..fc6b200
--- /dev/null
@@ -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 (file)
index 0000000..fa36b28
--- /dev/null
@@ -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 (file)
index 0000000..a4981d9
--- /dev/null
@@ -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 (file)
index 0000000..d0e357c
--- /dev/null
@@ -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<<shift[width])-1; // X Mask in tiles (0x1f-0x7f)
+  ymask=(height<<8)|0xff;       // Y Mask in pixels
+  if(width == 1)   ymask&=0x1ff;
+  else if(width>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)<<shift[width];
+
+    DrawStripInterlace(&ts);
+  } else if( pvid->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)<<shift[width];
+
+    DrawStrip(&ts, sh);
+  }
+}
+
+
+// --------------------------------------------
+
+// tstart & tend are tile pair numbers
+void DrawWindow(int tstart, int tend, int prio, int sh) // int *hcache
+{
+  struct PicoVideo *pvid=&Pico.video;
+  int tilex=0,ty=0,nametab,code=0;
+  int blank=-1; // The tile we know is blank
+
+  // Find name table line:
+  if (pvid->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<end);
+}
+
+// --------------------------------------------
+
+extern unsigned short HighPal[0x100];
+
+void FinalizeLineBGR444(int sh)
+{
+  unsigned short *pd=DrawLineDest;
+  unsigned char  *ps=HighCol+8;
+  unsigned short *pal=Pico.cram;
+  int len, i, t;
+
+  if (Pico.video.reg[12]&1) {
+    len = 320;
+  } else {
+    if(!(PicoOpt&0x100)) pd+=32;
+    len = 256;
+  }
+
+  if(sh) {
+    pal=HighPal;
+    if(Pico.m.dirtyPal) {
+      blockcpy(pal, Pico.cram, 0x40*2);
+      // shadowed pixels
+      for(i = 0x3f; i >= 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 (file)
index 0000000..2651a50
--- /dev/null
@@ -0,0 +1,384 @@
+/* faking/emulating gp2x.c by using gtk */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <gtk/gtk.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#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(&gtk_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 (file)
index 0000000..4f183c7
--- /dev/null
@@ -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
+