mirror of
https://github.com/RaySollium99/libpicofe.git
synced 2025-09-05 06:47:45 -04:00
initial import
git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@2 be3aeb3a-fb24-0410-a615-afba39da0efa
This commit is contained in:
commit
720ee7f624
36 changed files with 6740 additions and 0 deletions
93
gp2x/940.c
Normal file
93
gp2x/940.c
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#include "940shared.h"
|
||||||
|
|
||||||
|
/* this code assumes that we live @ 0x3000000 bank */
|
||||||
|
//static volatile unsigned short *gp2x_memregs = (void *) 0x0xbd000000;
|
||||||
|
//static volatile unsigned long *gp2x_memregl = (void *) 0x0xbd000000;
|
||||||
|
|
||||||
|
static _940_data_t *shared_data = (_940_data_t *) 0x100000;
|
||||||
|
static _940_ctl_t *shared_ctl = (_940_ctl_t *) 0x200000;
|
||||||
|
YM2612 *ym2612_940;
|
||||||
|
int *mix_buffer;
|
||||||
|
|
||||||
|
// from init.s
|
||||||
|
void wait_irq(void);
|
||||||
|
void spend_cycles(int c);
|
||||||
|
void cache_clean(void);
|
||||||
|
void cache_clean_flush(void);
|
||||||
|
|
||||||
|
// asm volatile ("mov r0, #0" ::: "r0");
|
||||||
|
// asm volatile ("mcr p15, 0, r0, c7, c6, 0" ::: "r0"); /* flush dcache */
|
||||||
|
// asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer */
|
||||||
|
|
||||||
|
void Main940(int startvector)
|
||||||
|
{
|
||||||
|
ym2612_940 = &shared_data->ym2612;
|
||||||
|
mix_buffer = shared_data->mix_buffer;
|
||||||
|
|
||||||
|
// debug
|
||||||
|
shared_ctl->vstarts[startvector]++;
|
||||||
|
asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0");
|
||||||
|
|
||||||
|
/* unmask IRQs */
|
||||||
|
|
||||||
|
for (;; shared_ctl->loopc++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
while (!shared_ctl->busy)
|
||||||
|
{
|
||||||
|
//shared_ctl->waitc++;
|
||||||
|
spend_cycles(256);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (!shared_ctl->busy)
|
||||||
|
{
|
||||||
|
wait_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (shared_ctl->job)
|
||||||
|
{
|
||||||
|
case JOB940_YM2612INIT:
|
||||||
|
shared_ctl->writebuff0[0] = shared_ctl->writebuff1[0] = 0xffff;
|
||||||
|
YM2612Init_(shared_ctl->baseclock, shared_ctl->rate);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JOB940_YM2612RESETCHIP:
|
||||||
|
YM2612ResetChip_();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JOB940_PICOSTATELOAD:
|
||||||
|
YM2612PicoStateLoad_();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JOB940_YM2612UPDATEONE: {
|
||||||
|
int i, dw, *wbuff;
|
||||||
|
if (shared_ctl->writebuffsel == 1) {
|
||||||
|
wbuff = (int *) shared_ctl->writebuff1;
|
||||||
|
} else {
|
||||||
|
wbuff = (int *) shared_ctl->writebuff0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* playback all writes */
|
||||||
|
for (i = 2048/2; i > 0; i--) {
|
||||||
|
UINT16 d;
|
||||||
|
dw = *wbuff++;
|
||||||
|
d = dw;
|
||||||
|
if (d == 0xffff) break;
|
||||||
|
YM2612Write_(d >> 8, d);
|
||||||
|
d = (dw>>16);
|
||||||
|
if (d == 0xffff) break;
|
||||||
|
YM2612Write_(d >> 8, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
YM2612UpdateOne_(0, shared_ctl->length, shared_ctl->stereo);
|
||||||
|
// cache_clean_flush();
|
||||||
|
cache_clean();
|
||||||
|
// asm volatile ("mov r0, #0" ::: "r0");
|
||||||
|
// asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer, should be done on nonbuffered write */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ctl->busy = 0;
|
||||||
|
}
|
||||||
|
}
|
451
gp2x/940ctl_ym2612.c
Normal file
451
gp2x/940ctl_ym2612.c
Normal file
|
@ -0,0 +1,451 @@
|
||||||
|
#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 "940shared.h"
|
||||||
|
#include "gp2x.h"
|
||||||
|
#include "emu.h"
|
||||||
|
#include "menu.h"
|
||||||
|
#include "asmutils.h"
|
||||||
|
|
||||||
|
/* we will need some gp2x internals here */
|
||||||
|
extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */
|
||||||
|
extern volatile unsigned long *gp2x_memregl;
|
||||||
|
|
||||||
|
static unsigned char *shared_mem = 0;
|
||||||
|
static _940_data_t *shared_data = 0;
|
||||||
|
static _940_ctl_t *shared_ctl = 0;
|
||||||
|
|
||||||
|
int crashed_940 = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************/
|
||||||
|
|
||||||
|
#define MAXOUT (+32767)
|
||||||
|
#define MINOUT (-32768)
|
||||||
|
|
||||||
|
/* limitter */
|
||||||
|
#define Limit(val, max,min) { \
|
||||||
|
if ( val > max ) val = max; \
|
||||||
|
else if ( val < min ) val = min; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* these will be managed locally on our side */
|
||||||
|
extern int *ym2612_dacen;
|
||||||
|
extern INT32 *ym2612_dacout;
|
||||||
|
extern void *ym2612_regs;
|
||||||
|
|
||||||
|
static UINT8 *REGS = 0; /* we will also keep local copy of regs for savestates and such */
|
||||||
|
static INT32 addr_A1; /* address line A1 */
|
||||||
|
static int dacen;
|
||||||
|
static INT32 dacout;
|
||||||
|
static UINT8 ST_address; /* address register */
|
||||||
|
static UINT8 ST_status; /* status flag */
|
||||||
|
static UINT8 ST_mode; /* mode CSM / 3SLOT */
|
||||||
|
static int ST_TA; /* timer a */
|
||||||
|
static int ST_TAC; /* timer a maxval */
|
||||||
|
static int ST_TAT; /* timer a ticker */
|
||||||
|
static UINT8 ST_TB; /* timer b */
|
||||||
|
static int ST_TBC; /* timer b maxval */
|
||||||
|
static int ST_TBT; /* timer b ticker */
|
||||||
|
|
||||||
|
static int writebuff_ptr = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/* OPN Mode Register Write */
|
||||||
|
static void set_timers( int v )
|
||||||
|
{
|
||||||
|
/* b7 = CSM MODE */
|
||||||
|
/* b6 = 3 slot mode */
|
||||||
|
/* b5 = reset b */
|
||||||
|
/* b4 = reset a */
|
||||||
|
/* b3 = timer enable b */
|
||||||
|
/* b2 = timer enable a */
|
||||||
|
/* b1 = load b */
|
||||||
|
/* b0 = load a */
|
||||||
|
ST_mode = v;
|
||||||
|
|
||||||
|
/* reset Timer b flag */
|
||||||
|
if( v & 0x20 )
|
||||||
|
ST_status &= ~2;
|
||||||
|
|
||||||
|
/* reset Timer a flag */
|
||||||
|
if( v & 0x10 )
|
||||||
|
ST_status &= ~1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* YM2612 write */
|
||||||
|
/* a = address */
|
||||||
|
/* v = value */
|
||||||
|
/* returns 1 if sample affecting state changed */
|
||||||
|
int YM2612Write_940(unsigned int a, unsigned int v)
|
||||||
|
{
|
||||||
|
int addr; //, ret=1;
|
||||||
|
|
||||||
|
v &= 0xff; /* adjust to 8 bit bus */
|
||||||
|
a &= 3;
|
||||||
|
|
||||||
|
switch( a ) {
|
||||||
|
case 0: /* address port 0 */
|
||||||
|
ST_address = v;
|
||||||
|
addr_A1 = 0;
|
||||||
|
//ret=0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /* data port 0 */
|
||||||
|
if (addr_A1 != 0) {
|
||||||
|
return 0; /* verified on real YM2608 */
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = ST_address;
|
||||||
|
REGS[addr] = v;
|
||||||
|
|
||||||
|
switch( addr & 0xf0 )
|
||||||
|
{
|
||||||
|
case 0x20: /* 0x20-0x2f Mode */
|
||||||
|
switch( addr )
|
||||||
|
{
|
||||||
|
case 0x24: { // timer A High 8
|
||||||
|
int TAnew = (ST_TA & 0x03)|(((int)v)<<2);
|
||||||
|
if(ST_TA != TAnew) {
|
||||||
|
// we should reset ticker only if new value is written. Outrun requires this.
|
||||||
|
ST_TA = TAnew;
|
||||||
|
ST_TAC = (1024-TAnew)*18;
|
||||||
|
ST_TAT = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case 0x25: { // timer A Low 2
|
||||||
|
int TAnew = (ST_TA & 0x3fc)|(v&3);
|
||||||
|
if(ST_TA != TAnew) {
|
||||||
|
ST_TA = TAnew;
|
||||||
|
ST_TAC = (1024-TAnew)*18;
|
||||||
|
ST_TAT = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case 0x26: // timer B
|
||||||
|
if(ST_TB != v) {
|
||||||
|
ST_TB = v;
|
||||||
|
ST_TBC = (256-v)<<4;
|
||||||
|
ST_TBC *= 18;
|
||||||
|
ST_TBT = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
case 0x27: /* mode, timer control */
|
||||||
|
set_timers( v );
|
||||||
|
break; // other side needs ST.mode for 3slot mode
|
||||||
|
case 0x2a: /* DAC data (YM2612) */
|
||||||
|
dacout = ((int)v - 0x80) << 6; /* level unknown (notaz: 8 seems to be too much) */
|
||||||
|
return 0;
|
||||||
|
case 0x2b: /* DAC Sel (YM2612) */
|
||||||
|
/* b7 = dac enable */
|
||||||
|
dacen = v & 0x80;
|
||||||
|
break; // other side has to know this
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: /* address port 1 */
|
||||||
|
ST_address = v;
|
||||||
|
addr_A1 = 1;
|
||||||
|
//ret=0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: /* data port 1 */
|
||||||
|
if (addr_A1 != 1) {
|
||||||
|
return 0; /* verified on real YM2608 */
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = ST_address | 0x100;
|
||||||
|
REGS[addr] = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(currentConfig.EmuOpt & 4) {
|
||||||
|
/* queue this write for 940 */
|
||||||
|
if (writebuff_ptr < 2047) {
|
||||||
|
if (shared_ctl->writebuffsel == 1) {
|
||||||
|
shared_ctl->writebuff0[writebuff_ptr++] = (a<<8)|v;
|
||||||
|
} else {
|
||||||
|
shared_ctl->writebuff1[writebuff_ptr++] = (a<<8)|v;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("warning: writebuff_ptr > 2047\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // cause the engine to do updates once per frame only
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 YM2612Read_940(void)
|
||||||
|
{
|
||||||
|
return ST_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int YM2612PicoTick_940(int n)
|
||||||
|
{
|
||||||
|
//int ret = 0;
|
||||||
|
|
||||||
|
// timer A
|
||||||
|
if(ST_mode & 0x01 && (ST_TAT+=64*n) >= ST_TAC) {
|
||||||
|
ST_TAT -= ST_TAC;
|
||||||
|
if(ST_mode & 0x04) ST_status |= 1;
|
||||||
|
// CSM mode total level latch and auto key on
|
||||||
|
/* FIXME
|
||||||
|
if(ST_mode & 0x80) {
|
||||||
|
CSMKeyControll( &(ym2612_940->CH[2]) ); // Vectorman2, etc.
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// timer B
|
||||||
|
if(ST_mode & 0x02 && (ST_TBT+=64*n) >= ST_TBC) {
|
||||||
|
ST_TBT -= ST_TBC;
|
||||||
|
if(ST_mode & 0x08) ST_status |= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void wait_busy_940(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
#if 0
|
||||||
|
printf("940 busy, entering wait loop.. (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
printf("%i ", shared_ctl->vstarts[i]);
|
||||||
|
printf(")\n");
|
||||||
|
|
||||||
|
for (i = 0; shared_ctl->busy; i++)
|
||||||
|
{
|
||||||
|
spend_cycles(1024); /* needs tuning */
|
||||||
|
}
|
||||||
|
printf("wait iterations: %i\n", i);
|
||||||
|
#else
|
||||||
|
for (i = 0; shared_ctl->busy && i < 0x10000; i++)
|
||||||
|
spend_cycles(4*1024);
|
||||||
|
if (i < 0x10000) return;
|
||||||
|
|
||||||
|
/* 940 crashed */
|
||||||
|
printf("940 crashed (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
printf("%i ", shared_ctl->vstarts[i]);
|
||||||
|
printf(")\n");
|
||||||
|
strcpy(menuErrorMsg, "940 crashed.");
|
||||||
|
engineState = PGS_Menu;
|
||||||
|
crashed_940 = 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void add_job_940(int job)
|
||||||
|
{
|
||||||
|
shared_ctl->job = job;
|
||||||
|
shared_ctl->busy = 1;
|
||||||
|
gp2x_memregs[0x3B3E>>1] = 0xffff; // cause an IRQ for 940
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void YM2612PicoStateLoad_940(void)
|
||||||
|
{
|
||||||
|
int i, old_A1 = addr_A1;
|
||||||
|
|
||||||
|
if (shared_ctl->busy) wait_busy_940();
|
||||||
|
|
||||||
|
// feed all the registers and update internal state
|
||||||
|
for(i = 0; i < 0x100; i++) {
|
||||||
|
YM2612Write_940(0, i);
|
||||||
|
YM2612Write_940(1, REGS[i]);
|
||||||
|
}
|
||||||
|
for(i = 0; i < 0x100; i++) {
|
||||||
|
YM2612Write_940(2, i);
|
||||||
|
YM2612Write_940(3, REGS[i|0x100]);
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_A1 = old_A1;
|
||||||
|
|
||||||
|
add_job_940(JOB940_PICOSTATELOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void internal_reset(void)
|
||||||
|
{
|
||||||
|
writebuff_ptr = 0;
|
||||||
|
ST_mode = 0;
|
||||||
|
ST_status = 0; /* normal mode */
|
||||||
|
ST_TA = 0;
|
||||||
|
ST_TAC = 0;
|
||||||
|
ST_TB = 0;
|
||||||
|
ST_TBC = 0;
|
||||||
|
dacen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern char **g_argv;
|
||||||
|
|
||||||
|
/* none of the functions in this file should be called before this one */
|
||||||
|
void YM2612Init_940(int baseclock, int rate)
|
||||||
|
{
|
||||||
|
printf("YM2612Init_940()\n");
|
||||||
|
//printf("sizeof(*shared_data): %i (%x)\n", sizeof(*shared_data), sizeof(*shared_data));
|
||||||
|
//printf("sizeof(*shared_ctl): %i (%x)\n", sizeof(*shared_ctl), sizeof(*shared_ctl));
|
||||||
|
|
||||||
|
Reset940(1);
|
||||||
|
Pause940(1);
|
||||||
|
|
||||||
|
gp2x_memregs[0x3B46>>1] = 0xffff; // clear pending DUALCPU interrupts for 940
|
||||||
|
gp2x_memregs[0x3B42>>1] = 0xffff; // enable DUALCPU interrupts for 940
|
||||||
|
|
||||||
|
gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller
|
||||||
|
|
||||||
|
if (shared_mem == NULL)
|
||||||
|
{
|
||||||
|
shared_mem = (unsigned char *) mmap(0, 0x210000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x3000000);
|
||||||
|
if(shared_mem == MAP_FAILED)
|
||||||
|
{
|
||||||
|
printf("mmap(shared_data) failed with %i\n", errno);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
shared_data = (_940_data_t *) (shared_mem+0x100000);
|
||||||
|
/* this area must not get buffered on either side */
|
||||||
|
shared_ctl = (_940_ctl_t *) (shared_mem+0x200000);
|
||||||
|
crashed_940 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crashed_940)
|
||||||
|
{
|
||||||
|
unsigned char ucData[1024];
|
||||||
|
int nRead, i, nLen = 0;
|
||||||
|
char binpath[1024];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
strncpy(binpath, g_argv[0], 1023);
|
||||||
|
binpath[1023] = 0;
|
||||||
|
for (i = strlen(binpath); i > 0; i--)
|
||||||
|
if (binpath[i] == '/') { binpath[i] = 0; break; }
|
||||||
|
strcat(binpath, "/code940.bin");
|
||||||
|
|
||||||
|
fp = fopen(binpath, "rb");
|
||||||
|
if(!fp)
|
||||||
|
{
|
||||||
|
memset(gp2x_screen, 0, 320*240);
|
||||||
|
gp2x_text_out8(10, 100, "failed to open required file:");
|
||||||
|
gp2x_text_out8(10, 110, "code940.bin");
|
||||||
|
gp2x_video_flip();
|
||||||
|
printf("failed to open %s\n", binpath);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
nRead = fread(ucData, 1, 1024, fp);
|
||||||
|
if(nRead <= 0)
|
||||||
|
break;
|
||||||
|
memcpy(shared_mem + nLen, ucData, nRead);
|
||||||
|
nLen += nRead;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
crashed_940 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(shared_data, 0, sizeof(*shared_data));
|
||||||
|
memset(shared_ctl, 0, sizeof(*shared_ctl));
|
||||||
|
|
||||||
|
REGS = YM2612GetRegs();
|
||||||
|
|
||||||
|
ym2612_dacen = &dacen;
|
||||||
|
ym2612_dacout = &dacout;
|
||||||
|
|
||||||
|
internal_reset();
|
||||||
|
|
||||||
|
/* now cause 940 to init it's ym2612 stuff */
|
||||||
|
shared_ctl->baseclock = baseclock;
|
||||||
|
shared_ctl->rate = rate;
|
||||||
|
shared_ctl->job = JOB940_YM2612INIT;
|
||||||
|
shared_ctl->busy = 1;
|
||||||
|
|
||||||
|
/* start the 940 */
|
||||||
|
Reset940(0);
|
||||||
|
Pause940(0);
|
||||||
|
|
||||||
|
// YM2612ResetChip_940(); // will be done on JOB940_YM2612INIT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void YM2612ResetChip_940(void)
|
||||||
|
{
|
||||||
|
printf("YM2612ResetChip_940()\n");
|
||||||
|
if (shared_data == NULL) {
|
||||||
|
printf("YM2612ResetChip_940: reset before init?\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shared_ctl->busy) wait_busy_940();
|
||||||
|
|
||||||
|
internal_reset();
|
||||||
|
|
||||||
|
add_job_940(JOB940_YM2612RESETCHIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void YM2612UpdateOne_940(short *buffer, int length, int stereo)
|
||||||
|
{
|
||||||
|
int i, *mix_buffer = shared_data->mix_buffer;
|
||||||
|
|
||||||
|
//printf("YM2612UpdateOne_940()\n");
|
||||||
|
if (shared_ctl->busy) wait_busy_940();
|
||||||
|
|
||||||
|
//printf("940 (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);
|
||||||
|
//for (i = 0; i < 8; i++)
|
||||||
|
// printf("%i ", shared_ctl->vstarts[i]);
|
||||||
|
//printf(")\n");
|
||||||
|
|
||||||
|
/* mix data from previous go */
|
||||||
|
if (stereo) {
|
||||||
|
int *mb = mix_buffer;
|
||||||
|
for (i = length; i > 0; i--) {
|
||||||
|
int l, r;
|
||||||
|
l = r = *buffer;
|
||||||
|
l += *mb++, r += *mb++;
|
||||||
|
Limit( l, MAXOUT, MINOUT );
|
||||||
|
Limit( r, MAXOUT, MINOUT );
|
||||||
|
*buffer++ = l; *buffer++ = r;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
int l = mix_buffer[i];
|
||||||
|
l += buffer[i];
|
||||||
|
Limit( l, MAXOUT, MINOUT );
|
||||||
|
buffer[i] = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("new writes: %i\n", writebuff_ptr);
|
||||||
|
if (shared_ctl->writebuffsel == 1) {
|
||||||
|
shared_ctl->writebuff0[writebuff_ptr] = 0xffff;
|
||||||
|
} else {
|
||||||
|
shared_ctl->writebuff1[writebuff_ptr] = 0xffff;
|
||||||
|
}
|
||||||
|
writebuff_ptr = 0;
|
||||||
|
|
||||||
|
/* give 940 another job */
|
||||||
|
shared_ctl->writebuffsel ^= 1;
|
||||||
|
shared_ctl->length = length;
|
||||||
|
shared_ctl->stereo = stereo;
|
||||||
|
add_job_940(JOB940_YM2612UPDATEONE);
|
||||||
|
//spend_cycles(512);
|
||||||
|
//printf("SRCPND: %08lx, INTMODE: %08lx, INTMASK: %08lx, INTPEND: %08lx\n",
|
||||||
|
// gp2x_memregl[0x4500>>2], gp2x_memregl[0x4504>>2], gp2x_memregl[0x4508>>2], gp2x_memregl[0x4510>>2]);
|
||||||
|
}
|
9
gp2x/940ctl_ym2612.h
Normal file
9
gp2x/940ctl_ym2612.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
void YM2612Init_940(int baseclock, int rate);
|
||||||
|
void YM2612ResetChip_940(void);
|
||||||
|
void YM2612UpdateOne_940(short *buffer, int length, int stereo);
|
||||||
|
|
||||||
|
int YM2612Write_940(unsigned int a, unsigned int v);
|
||||||
|
unsigned char YM2612Read_940(void);
|
||||||
|
|
||||||
|
int YM2612PicoTick_940(int n);
|
||||||
|
void YM2612PicoStateLoad_940(void);
|
174
gp2x/940init.s
Normal file
174
gp2x/940init.s
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
.global code940
|
||||||
|
|
||||||
|
code940: @ interrupt table:
|
||||||
|
b .b_reset @ reset
|
||||||
|
b .b_undef @ undefined instructions
|
||||||
|
b .b_swi @ software interrupt
|
||||||
|
b .b_pabort @ prefetch abort
|
||||||
|
b .b_dabort @ data abort
|
||||||
|
b .b_reserved @ reserved
|
||||||
|
b .b_irq @ IRQ
|
||||||
|
b .b_fiq @ FIQ
|
||||||
|
|
||||||
|
@ test
|
||||||
|
.b_reset:
|
||||||
|
mov r12, #0
|
||||||
|
b .Begin
|
||||||
|
.b_undef:
|
||||||
|
mov r12, #1
|
||||||
|
b .Begin
|
||||||
|
.b_swi:
|
||||||
|
mov r12, #2
|
||||||
|
b .Begin
|
||||||
|
.b_pabort:
|
||||||
|
mov r12, #3
|
||||||
|
b .Begin
|
||||||
|
.b_dabort:
|
||||||
|
mov r12, #4
|
||||||
|
b .Begin
|
||||||
|
.b_reserved:
|
||||||
|
mov r12, #5
|
||||||
|
b .Begin
|
||||||
|
.b_irq:
|
||||||
|
mov r12, #6
|
||||||
|
mov sp, #0x100000 @ reset stack
|
||||||
|
sub sp, sp, #4
|
||||||
|
mov r1, #0xbd000000 @ assume we live @ 0x3000000 bank
|
||||||
|
orr r2, r1, #0x3B00
|
||||||
|
orr r2, r2, #0x0046
|
||||||
|
mvn r3, #0
|
||||||
|
strh r3, [r2] @ clear any pending interrupts from the DUALCPU unit
|
||||||
|
orr r2, r1, #0x4500
|
||||||
|
str r3, [r2] @ clear all pending interrupts in irq controller's SRCPND register
|
||||||
|
orr r2, r2, #0x0010
|
||||||
|
str r3, [r2] @ clear all pending interrupts in irq controller's INTPND register
|
||||||
|
b .Enter
|
||||||
|
.b_fiq:
|
||||||
|
mov r12, #7
|
||||||
|
b .Begin
|
||||||
|
|
||||||
|
.Begin:
|
||||||
|
mov sp, #0x100000 @ set the stack top (1M)
|
||||||
|
sub sp, sp, #4 @ minus 4
|
||||||
|
|
||||||
|
@ set up memory region 0 -- the whole 4GB address space
|
||||||
|
mov r0, #(0x1f<<1)|1 @ region data
|
||||||
|
mcr p15, 0, r0, c6, c0, 0 @ opcode2 ~ data/instr
|
||||||
|
mcr p15, 0, r0, c6, c0, 1
|
||||||
|
|
||||||
|
@ set up region 1 which is the first 2 megabytes.
|
||||||
|
mov r0, #(0x14<<1)|1 @ region data
|
||||||
|
mcr p15, 0, r0, c6, c1, 0
|
||||||
|
mcr p15, 0, r0, c6, c1, 1
|
||||||
|
|
||||||
|
@ set up region 2: 64k 0x200000-0x210000
|
||||||
|
mov r0, #(0x0f<<1)|1
|
||||||
|
orr r0, r0, #0x200000
|
||||||
|
mcr p15, 0, r0, c6, c2, 0
|
||||||
|
mcr p15, 0, r0, c6, c2, 1
|
||||||
|
|
||||||
|
@ set up region 3: 64k 0xbd000000-0xbd010000 (hw control registers)
|
||||||
|
mov r0, #(0x0f<<1)|1
|
||||||
|
orr r0, r0, #0xbd000000
|
||||||
|
mcr p15, 0, r0, c6, c3, 0
|
||||||
|
mcr p15, 0, r0, c6, c3, 1
|
||||||
|
|
||||||
|
@ set region 1 to be cacheable (so the first 2M will be cacheable)
|
||||||
|
mov r0, #2
|
||||||
|
mcr p15, 0, r0, c2, c0, 0
|
||||||
|
mcr p15, 0, r0, c2, c0, 1
|
||||||
|
|
||||||
|
@ set region 1 to be bufferable too (only data)
|
||||||
|
mcr p15, 0, r0, c3, c0, 0
|
||||||
|
|
||||||
|
@ set protection, allow accsess only to regions 1 and 2
|
||||||
|
mov r0, #(3<<6)|(3<<4)|(3<<2)|(0) @ data: [full, full, full, no access] for regions [3 2 1 0]
|
||||||
|
mcr p15, 0, r0, c5, c0, 0
|
||||||
|
mov r0, #(0<<6)|(0<<4)|(3<<2)|(0) @ instructions: [no access, no, full, no]
|
||||||
|
mcr p15, 0, r0, c5, c0, 1
|
||||||
|
|
||||||
|
mrc p15, 0, r0, c1, c0, 0 @ fetch current control reg
|
||||||
|
orr r0, r0, #1 @ 0x00000001: enable protection unit
|
||||||
|
orr r0, r0, #4 @ 0x00000004: enable D cache
|
||||||
|
orr r0, r0, #0x1000 @ 0x00001000: enable I cache
|
||||||
|
orr r0, r0, #0xC0000000 @ 0xC0000000: async+fastbus
|
||||||
|
mcr p15, 0, r0, c1, c0, 0 @ set control reg
|
||||||
|
|
||||||
|
@ flush (invalidate) the cache (just in case)
|
||||||
|
mov r0, #0
|
||||||
|
mcr p15, 0, r0, c7, c6, 0
|
||||||
|
|
||||||
|
.Enter:
|
||||||
|
mov r0, r12
|
||||||
|
bl Main940
|
||||||
|
|
||||||
|
@ we should never get here
|
||||||
|
.b_deadloop:
|
||||||
|
b .b_deadloop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ so asm utils are also defined here:
|
||||||
|
.global spend_cycles @ c
|
||||||
|
|
||||||
|
spend_cycles:
|
||||||
|
mov r0, r0, lsr #2 @ 4 cycles/iteration
|
||||||
|
sub r0, r0, #2 @ entry/exit/init
|
||||||
|
.sc_loop:
|
||||||
|
subs r0, r0, #1
|
||||||
|
bpl .sc_loop
|
||||||
|
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
@ clean-flush function from ARM940T technical reference manual
|
||||||
|
.global cache_clean_flush
|
||||||
|
|
||||||
|
cache_clean_flush:
|
||||||
|
mov r1, #0 @ init line counter
|
||||||
|
ccf_outer_loop:
|
||||||
|
mov r0, #0 @ segment counter
|
||||||
|
ccf_inner_loop:
|
||||||
|
orr r2, r1, r0 @ make segment and line address
|
||||||
|
mcr p15, 0, r2, c7, c14, 2 @ clean and flush that line
|
||||||
|
add r0, r0, #0x10 @ incremet secment counter
|
||||||
|
cmp r0, #0x40 @ complete all 4 segments?
|
||||||
|
bne ccf_inner_loop
|
||||||
|
add r1, r1, #0x04000000 @ increment line counter
|
||||||
|
cmp r1, #0 @ complete all lines?
|
||||||
|
bne ccf_outer_loop
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
@ clean-only version
|
||||||
|
.global cache_clean
|
||||||
|
|
||||||
|
cache_clean:
|
||||||
|
mov r1, #0 @ init line counter
|
||||||
|
cf_outer_loop:
|
||||||
|
mov r0, #0 @ segment counter
|
||||||
|
cf_inner_loop:
|
||||||
|
orr r2, r1, r0 @ make segment and line address
|
||||||
|
mcr p15, 0, r2, c7, c10, 2 @ clean that line
|
||||||
|
add r0, r0, #0x10 @ incremet secment counter
|
||||||
|
cmp r0, #0x40 @ complete all 4 segments?
|
||||||
|
bne cf_inner_loop
|
||||||
|
add r1, r1, #0x04000000 @ increment line counter
|
||||||
|
cmp r1, #0 @ complete all lines?
|
||||||
|
bne cf_outer_loop
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
.global wait_irq
|
||||||
|
|
||||||
|
wait_irq:
|
||||||
|
mrs r0, cpsr
|
||||||
|
bic r0, r0, #0x80
|
||||||
|
msr cpsr_c, r0 @ enable interrupts
|
||||||
|
|
||||||
|
mov r0, #0
|
||||||
|
mcr p15, 0, r0, c7, c0, 4 @ wait for IRQ
|
||||||
|
@ mcr p15, 0, r0, c15, c8, 2
|
||||||
|
b .b_reserved
|
||||||
|
|
||||||
|
.pool
|
33
gp2x/940shared.h
Normal file
33
gp2x/940shared.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#include "../../Pico/sound/ym2612.h"
|
||||||
|
|
||||||
|
enum _940_job_t {
|
||||||
|
JOB940_YM2612INIT = 1,
|
||||||
|
JOB940_YM2612RESETCHIP,
|
||||||
|
JOB940_YM2612UPDATEONE,
|
||||||
|
JOB940_PICOSTATELOAD,
|
||||||
|
JOB940_NUMJOBS
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
YM2612 ym2612; /* current state of the emulated YM2612 */
|
||||||
|
int mix_buffer[44100/50*2]; /* this is where the YM2612 samples will be mixed to */
|
||||||
|
} _940_data_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int job; /* a job for second core */
|
||||||
|
int busy; /* busy status of the 940 core */
|
||||||
|
int length; /* number of samples to mix (882 max) */
|
||||||
|
int stereo; /* mix samples as stereo, doubles sample count automatically */
|
||||||
|
int baseclock; /* ym2612 settings */
|
||||||
|
int rate;
|
||||||
|
int writebuffsel; /* which write buffer to use (from 940 side) */
|
||||||
|
UINT16 writebuff0[2048]; /* 1024 for savestates, 1024 extra */
|
||||||
|
UINT16 writebuff1[2048];
|
||||||
|
int vstarts[8]; /* debug: number of starts from each of 8 vectors */
|
||||||
|
int loopc; /* debug: main loop counter */
|
||||||
|
int waitc; /* debug: wait loop counter */
|
||||||
|
} _940_ctl_t;
|
191
gp2x/Makefile
Normal file
191
gp2x/Makefile
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
# you may or may not need to change this
|
||||||
|
#devkit_path = x:/stuff/dev/devkitgp2x/
|
||||||
|
devkit_path = /usr/local/devkitPro/devkitGP2X/
|
||||||
|
lgcc_path = $(devkit_path)lib/gcc/arm-linux/4.0.3/
|
||||||
|
CROSS = arm-linux-
|
||||||
|
#CROSS = $(devkit_path)bin/arm-linux-
|
||||||
|
|
||||||
|
# settings
|
||||||
|
dprint = 1
|
||||||
|
#mz80 = 1
|
||||||
|
#debug_cyclone = 1
|
||||||
|
asm_memory = 1
|
||||||
|
asm_render = 1
|
||||||
|
asm_ym2612 = 1
|
||||||
|
#profile = 1
|
||||||
|
#use_musashi = 1
|
||||||
|
#up = 1
|
||||||
|
|
||||||
|
DEFINC = -I../.. -I. -D__GP2X__ -D_UNZIP_SUPPORT # -DBENCHMARK
|
||||||
|
COPT_COMMON = -static -s -O3 -ftracer -fstrength-reduce -Wall -funroll-loops -fomit-frame-pointer -fstrict-aliasing -ffast-math
|
||||||
|
ifeq "$(profile)" "1"
|
||||||
|
COPT_COMMON += -fprofile-generate
|
||||||
|
endif
|
||||||
|
ifeq "$(profile)" "2"
|
||||||
|
COPT_COMMON += -fprofile-use
|
||||||
|
endif
|
||||||
|
COPT = $(COPT_COMMON) -mtune=arm920t
|
||||||
|
ASOPT = -mcpu=arm920t -mfloat-abi=soft
|
||||||
|
GCC = $(CROSS)gcc
|
||||||
|
STRIP = $(CROSS)strip
|
||||||
|
AS = $(CROSS)as
|
||||||
|
LD = $(CROSS)ld
|
||||||
|
OBJCOPY = $(CROSS)objcopy
|
||||||
|
|
||||||
|
# frontend
|
||||||
|
OBJS += main.o menu.o gp2x.o usbjoy.o emu.o squidgehack.o asmutils.o cpuctrl.o
|
||||||
|
# 940 core control
|
||||||
|
OBJS += 940ctl_ym2612.o
|
||||||
|
# Pico
|
||||||
|
OBJS += ../../Pico/Area.o ../../Pico/Cart.o ../../Pico/Utils.o ../../Pico/Memory.o ../../Pico/Misc.o \
|
||||||
|
../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o
|
||||||
|
# asm stuff
|
||||||
|
ifeq "$(asm_render)" "1"
|
||||||
|
DEFINC += -D_ASM_DRAW_C
|
||||||
|
OBJS += ../../Pico/draw_asm.o ../../Pico/draw2_asm.o
|
||||||
|
endif
|
||||||
|
ifeq "$(asm_memory)" "1"
|
||||||
|
DEFINC += -D_ASM_MEMORY_C
|
||||||
|
OBJS += ../../Pico/memory_asm.o
|
||||||
|
endif
|
||||||
|
ifeq "$(asm_ym2612)" "1"
|
||||||
|
DEFINC += -D_ASM_YM2612_C
|
||||||
|
OBJS += ../../Pico/sound/ym2612_asm.o
|
||||||
|
endif
|
||||||
|
# Pico - sound
|
||||||
|
OBJS += ../../Pico/sound/sound.o ../../Pico/sound/sn76496.o ../../Pico/sound/ym2612.o
|
||||||
|
# zlib
|
||||||
|
OBJS += ../../zlib/gzio.o ../../zlib/inffast.o ../../zlib/inflate.o ../../zlib/inftrees.o ../../zlib/trees.o \
|
||||||
|
../../zlib/deflate.o ../../zlib/crc32.o ../../zlib/adler32.o ../../zlib/zutil.o ../../zlib/compress.o
|
||||||
|
# unzip
|
||||||
|
OBJS += ../../unzip/unzip.o
|
||||||
|
# CPU cores
|
||||||
|
ifeq "$(use_musashi)" "1"
|
||||||
|
DEFINC += -DEMU_M68K
|
||||||
|
OBJS += _build\m68kcpu.o _build\m68kopac.o _build\m68kopdm.o _build\m68kopnz.o _build\m68kops.o
|
||||||
|
else
|
||||||
|
DEFINC += -DEMU_C68K
|
||||||
|
OBJS += ../../cpu/Cyclone/proj/Cyclone.o
|
||||||
|
endif
|
||||||
|
# drz80/mz80
|
||||||
|
ifeq "$(mz80)" "1"
|
||||||
|
DEFINC += -D_USE_MZ80
|
||||||
|
OBJS += ../../cpu/mz80/mz80.o
|
||||||
|
else
|
||||||
|
DEFINC += -D_USE_DRZ80
|
||||||
|
OBJS += ../../cpu/DrZ80/drz80.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: PicoDrive.gpe code940.bin
|
||||||
|
|
||||||
|
PicoDrive.gpe : $(OBJS)
|
||||||
|
@echo $@
|
||||||
|
@$(GCC) $(COPT) $(OBJS) $(PRELIBS) -lm -o $@
|
||||||
|
@$(STRIP) $@
|
||||||
|
# @$(GCC) $(COPT) $(OBJS) $(PRELIBS) -lm -o PicoDrive_.gpe
|
||||||
|
# @gpecomp PicoDrive_.gpe $@
|
||||||
|
ifeq "$(up)" "1"
|
||||||
|
@cmd //C copy $@ \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\
|
||||||
|
endif
|
||||||
|
|
||||||
|
up: up940
|
||||||
|
@cmd //C copy PicoDrive.gpe \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\
|
||||||
|
up940:
|
||||||
|
@cmd //C copy code940.bin \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\
|
||||||
|
|
||||||
|
testrefr.gpe : test.o gp2x.o asmutils.o
|
||||||
|
@echo $@
|
||||||
|
@$(GCC) $(COPT) $^ $(PRELIBS) -o $@
|
||||||
|
@$(STRIP) $@
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
@echo $<
|
||||||
|
@$(GCC) $(COPT) $(DEFINC) -c $< -o $@
|
||||||
|
.s.o:
|
||||||
|
@echo $<
|
||||||
|
@$(GCC) $(COPT) $(DEFINC) -c $< -o $@
|
||||||
|
|
||||||
|
../../Pico/draw_asm.o : ../../Pico/Draw.s
|
||||||
|
@echo $<
|
||||||
|
@$(AS) $(ASOPT) $< -o $@
|
||||||
|
../../Pico/draw2_asm.o : ../../Pico/Draw2.s
|
||||||
|
@echo $<
|
||||||
|
@$(AS) $(ASOPT) $< -o $@
|
||||||
|
../../Pico/memory_asm.o : ../../Pico/Memory.s
|
||||||
|
@echo $<
|
||||||
|
@$(AS) $(ASOPT) $< -o $@
|
||||||
|
../../Pico/sound/ym2612_asm.o : ../../Pico/sound/ym2612.s
|
||||||
|
@echo $<
|
||||||
|
@$(AS) $(ASOPT) $< -o $@
|
||||||
|
|
||||||
|
# build Cyclone
|
||||||
|
../../cpu/Cyclone/proj/Cyclone.s :
|
||||||
|
@echo building Cyclone...
|
||||||
|
@make -C ../../cpu/Cyclone/proj -f Makefile.linux
|
||||||
|
|
||||||
|
|
||||||
|
# stuff for 940 core
|
||||||
|
|
||||||
|
# init, emu_control, emu
|
||||||
|
OBJS940 += 940init.o 940.o 940ym2612.o
|
||||||
|
# the asm code seems to be faster when run on 920, but not on 940 for some reason
|
||||||
|
# OBJS940 += ../../Pico/sound/ym2612_asm.o
|
||||||
|
|
||||||
|
# uClibc library code
|
||||||
|
OBJS940 += uClibc/memset.o uClibc/s_floor.o uClibc/e_pow.o uClibc/e_sqrt.o uClibc/s_fabs.o
|
||||||
|
OBJS940 += uClibc/s_scalbn.o uClibc/s_copysign.o uClibc/k_sin.o uClibc/k_cos.o uClibc/s_sin.o
|
||||||
|
OBJS940 += uClibc/e_rem_pio2.o uClibc/k_rem_pio2.o uClibc/e_log.o uClibc/wrappers.o
|
||||||
|
|
||||||
|
code940.bin : code940.gpe
|
||||||
|
@echo $@
|
||||||
|
@$(OBJCOPY) -O binary $< $@
|
||||||
|
|
||||||
|
code940.gpe : $(OBJS940)
|
||||||
|
@echo $@
|
||||||
|
@$(LD) -static -e code940 -Ttext 0x0 $^ -L$(lgcc_path) -lgcc -o $@
|
||||||
|
|
||||||
|
940ym2612.o : ../../Pico/sound/ym2612.c
|
||||||
|
@echo $@
|
||||||
|
@$(GCC) $(COPT_COMMON) -mtune=arm940t $(DEFINC) -DEXTERNAL_YM2612 -c $< -o $@
|
||||||
|
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
clean: clean_pd clean_940
|
||||||
|
tidy: tidy_pd tidy_940
|
||||||
|
|
||||||
|
clean_pd: tidy_pd
|
||||||
|
@$(RM) PicoDrive.gpe
|
||||||
|
tidy_pd:
|
||||||
|
@$(RM) $(OBJS)
|
||||||
|
# @make -C ../../cpu/Cyclone/proj -f Makefile.linux clean
|
||||||
|
|
||||||
|
clean_940: tidy_940
|
||||||
|
@$(RM) code940.bin
|
||||||
|
tidy_940:
|
||||||
|
@$(RM) code940.gpe $(OBJS940)
|
||||||
|
|
||||||
|
clean_prof:
|
||||||
|
find ../.. -name '*.gcno' -delete
|
||||||
|
find ../.. -name '*.gcda' -delete
|
||||||
|
|
||||||
|
# test
|
||||||
|
usbjoy.o : usbjoy.c
|
||||||
|
@echo $<
|
||||||
|
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
|
||||||
|
|
||||||
|
../../Pico/Cart.o : ../../Pico/Cart.c
|
||||||
|
@echo $<
|
||||||
|
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
|
||||||
|
|
||||||
|
../../zlib/trees.o : ../../zlib/trees.c
|
||||||
|
@echo $<
|
||||||
|
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
|
||||||
|
|
||||||
|
uClibc/e_pow.o : uClibc/e_pow.c
|
||||||
|
@echo $<
|
||||||
|
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
|
||||||
|
|
||||||
|
uClibc/e_sqrt.o : uClibc/e_sqrt.c
|
||||||
|
@echo $<
|
||||||
|
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
|
12
gp2x/asmutils.h
Normal file
12
gp2x/asmutils.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// (c) Copyright 2006 notaz, All rights reserved.
|
||||||
|
// Free for non-commercial use.
|
||||||
|
|
||||||
|
// For commercial use, separate licencing terms must be obtained.
|
||||||
|
|
||||||
|
void vidConvCpyRGB32 (void *to, void *from, int pixels);
|
||||||
|
void vidConvCpyRGB32sh(void *to, void *from, int pixels);
|
||||||
|
void vidConvCpyRGB32hi(void *to, void *from, int pixels);
|
||||||
|
void vidCpyM2_40col(void *dest, void *src);
|
||||||
|
void vidCpyM2_32col(void *dest, void *src);
|
||||||
|
void vidCpyM2_32col_nobord(void *dest, void *src);
|
||||||
|
void spend_cycles(int c); // utility
|
210
gp2x/asmutils.s
Normal file
210
gp2x/asmutils.s
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
@ some color conversion and blitting routines
|
||||||
|
|
||||||
|
@ (c) Copyright 2006, notaz
|
||||||
|
@ All Rights Reserved
|
||||||
|
|
||||||
|
|
||||||
|
@ Convert 0000bbb0 ggg0rrr0 0000bbb0 ggg0rrr0
|
||||||
|
@ to 00000000 rrr00000 ggg00000 bbb00000 ...
|
||||||
|
|
||||||
|
@ lr = 0x00e000e0, out: r3=lower_pix, r2=higher_pix; trashes rin
|
||||||
|
@ if sh==2, r8=0x00404040 (sh!=0 destroys flags!)
|
||||||
|
.macro convRGB32_2 rin sh=0
|
||||||
|
and r2, lr, \rin, lsr #4 @ blue
|
||||||
|
and r3, \rin, lr
|
||||||
|
orr r2, r2, r3, lsl #8 @ g0b0g0b0
|
||||||
|
|
||||||
|
mov r3, r2, lsl #16 @ g0b00000
|
||||||
|
and \rin,lr, \rin, ror #12 @ 00r000r0 (reversed)
|
||||||
|
orr r3, r3, \rin, lsr #16 @ g0b000r0
|
||||||
|
.if \sh == 1
|
||||||
|
mov r3, r3, ror #17 @ shadow mode
|
||||||
|
.elseif \sh == 2
|
||||||
|
adds r3, r3, #0x40000000 @ green
|
||||||
|
orrcs r3, r3, #0xe0000000
|
||||||
|
mov r3, r3, ror #8
|
||||||
|
adds r3, r3, #0x40000000
|
||||||
|
orrcs r3, r3, #0xe0000000
|
||||||
|
mov r3, r3, ror #16
|
||||||
|
adds r3, r3, #0x40000000
|
||||||
|
orrcs r3, r3, #0xe0000000
|
||||||
|
mov r3, r3, ror #24
|
||||||
|
.else
|
||||||
|
mov r3, r3, ror #16 @ r3=low
|
||||||
|
.endif
|
||||||
|
|
||||||
|
orr r3, r3, r3, lsr #3
|
||||||
|
str r3, [r0], #4
|
||||||
|
|
||||||
|
mov r2, r2, lsr #16
|
||||||
|
orr r2, r2, \rin, lsl #16
|
||||||
|
.if \sh == 1
|
||||||
|
mov r2, r2, lsr #1
|
||||||
|
.elseif \sh == 2
|
||||||
|
mov r2, r2, ror #8
|
||||||
|
adds r2, r2, #0x40000000 @ blue
|
||||||
|
orrcs r2, r2, #0xe0000000
|
||||||
|
mov r2, r2, ror #8
|
||||||
|
adds r2, r2, #0x40000000
|
||||||
|
orrcs r2, r2, #0xe0000000
|
||||||
|
mov r2, r2, ror #8
|
||||||
|
adds r2, r2, #0x40000000
|
||||||
|
orrcs r2, r2, #0xe0000000
|
||||||
|
mov r2, r2, ror #8
|
||||||
|
.endif
|
||||||
|
|
||||||
|
orr r2, r2, r2, lsr #3
|
||||||
|
str r2, [r0], #4
|
||||||
|
.endm
|
||||||
|
|
||||||
|
|
||||||
|
.global vidConvCpyRGB32 @ void *to, void *from, int pixels
|
||||||
|
|
||||||
|
vidConvCpyRGB32:
|
||||||
|
stmfd sp!, {r4-r7,lr}
|
||||||
|
|
||||||
|
mov r12, r2, lsr #3 @ repeats
|
||||||
|
mov lr, #0x00e00000
|
||||||
|
orr lr, lr, #0x00e0
|
||||||
|
|
||||||
|
.loopRGB32:
|
||||||
|
subs r12, r12, #1
|
||||||
|
|
||||||
|
ldmia r1!, {r4-r7}
|
||||||
|
convRGB32_2 r4
|
||||||
|
convRGB32_2 r5
|
||||||
|
convRGB32_2 r6
|
||||||
|
convRGB32_2 r7
|
||||||
|
|
||||||
|
bgt .loopRGB32
|
||||||
|
|
||||||
|
ldmfd sp!, {r4-r7,lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
.global vidConvCpyRGB32sh @ void *to, void *from, int pixels
|
||||||
|
|
||||||
|
vidConvCpyRGB32sh:
|
||||||
|
stmfd sp!, {r4-r7,lr}
|
||||||
|
|
||||||
|
mov r12, r2, lsr #3 @ repeats
|
||||||
|
mov lr, #0x00e00000
|
||||||
|
orr lr, lr, #0x00e0
|
||||||
|
|
||||||
|
.loopRGB32sh:
|
||||||
|
subs r12, r12, #1
|
||||||
|
|
||||||
|
ldmia r1!, {r4-r7}
|
||||||
|
convRGB32_2 r4, 1
|
||||||
|
convRGB32_2 r5, 1
|
||||||
|
convRGB32_2 r6, 1
|
||||||
|
convRGB32_2 r7, 1
|
||||||
|
|
||||||
|
bgt .loopRGB32sh
|
||||||
|
|
||||||
|
ldmfd sp!, {r4-r7,lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
.global vidConvCpyRGB32hi @ void *to, void *from, int pixels
|
||||||
|
|
||||||
|
vidConvCpyRGB32hi:
|
||||||
|
stmfd sp!, {r4-r7,lr}
|
||||||
|
|
||||||
|
mov r12, r2, lsr #3 @ repeats
|
||||||
|
mov lr, #0x00e00000
|
||||||
|
orr lr, lr, #0x00e0
|
||||||
|
|
||||||
|
.loopRGB32hi:
|
||||||
|
ldmia r1!, {r4-r7}
|
||||||
|
convRGB32_2 r4, 2
|
||||||
|
convRGB32_2 r5, 2
|
||||||
|
convRGB32_2 r6, 2
|
||||||
|
convRGB32_2 r7, 2
|
||||||
|
|
||||||
|
subs r12, r12, #1
|
||||||
|
bgt .loopRGB32hi
|
||||||
|
|
||||||
|
ldmfd sp!, {r4-r7,lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||||
|
|
||||||
|
|
||||||
|
@ mode2 blitter for 40 cols
|
||||||
|
.global vidCpyM2_40col @ void *dest, void *src
|
||||||
|
|
||||||
|
vidCpyM2_40col:
|
||||||
|
stmfd sp!, {r4-r6,lr}
|
||||||
|
|
||||||
|
mov r12, #224 @ lines
|
||||||
|
add r1, r1, #8
|
||||||
|
|
||||||
|
vidCpyM2_40_loop_out:
|
||||||
|
mov r6, #10
|
||||||
|
vidCpyM2_40_loop:
|
||||||
|
subs r6, r6, #1
|
||||||
|
ldmia r1!, {r2-r5}
|
||||||
|
stmia r0!, {r2-r5}
|
||||||
|
ldmia r1!, {r2-r5}
|
||||||
|
stmia r0!, {r2-r5}
|
||||||
|
bne vidCpyM2_40_loop
|
||||||
|
subs r12,r12,#1
|
||||||
|
add r1, r1, #8
|
||||||
|
bne vidCpyM2_40_loop_out
|
||||||
|
|
||||||
|
ldmfd sp!, {r4-r6,lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
@ mode2 blitter for 32 cols
|
||||||
|
.global vidCpyM2_32col @ void *dest, void *src
|
||||||
|
|
||||||
|
vidCpyM2_32col:
|
||||||
|
stmfd sp!, {r4-r6,lr}
|
||||||
|
|
||||||
|
mov r12, #224 @ lines
|
||||||
|
add r1, r1, #8
|
||||||
|
add r0, r0, #32
|
||||||
|
|
||||||
|
vidCpyM2_32_loop_out:
|
||||||
|
mov r6, #8
|
||||||
|
vidCpyM2_32_loop:
|
||||||
|
subs r6, r6, #1
|
||||||
|
ldmia r1!, {r2-r5}
|
||||||
|
stmia r0!, {r2-r5}
|
||||||
|
ldmia r1!, {r2-r5}
|
||||||
|
stmia r0!, {r2-r5}
|
||||||
|
bne vidCpyM2_32_loop
|
||||||
|
subs r12,r12,#1
|
||||||
|
add r0, r0, #64
|
||||||
|
add r1, r1, #8+64
|
||||||
|
bne vidCpyM2_32_loop_out
|
||||||
|
|
||||||
|
ldmfd sp!, {r4-r6,lr}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
@ mode2 blitter for 32 cols with no borders
|
||||||
|
.global vidCpyM2_32col_nobord @ void *dest, void *src
|
||||||
|
|
||||||
|
vidCpyM2_32col_nobord:
|
||||||
|
stmfd sp!, {r4-r6,lr}
|
||||||
|
|
||||||
|
mov r12, #224 @ lines
|
||||||
|
add r1, r1, #8
|
||||||
|
b vidCpyM2_32_loop_out
|
||||||
|
|
||||||
|
|
||||||
|
@ test
|
||||||
|
.global spend_cycles @ c
|
||||||
|
|
||||||
|
spend_cycles:
|
||||||
|
mov r0, r0, lsr #2 @ 4 cycles/iteration
|
||||||
|
sub r0, r0, #2 @ entry/exit/init
|
||||||
|
.sc_loop:
|
||||||
|
subs r0, r0, #1
|
||||||
|
bpl .sc_loop
|
||||||
|
|
||||||
|
bx lr
|
138
gp2x/config.txt
Normal file
138
gp2x/config.txt
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
As PicoDrive is multiplatform emulator, this is GP2X specific part of readme
|
||||||
|
about configuration.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
1. "Renderer"
|
||||||
|
8bit fast:
|
||||||
|
This enables alternative heavily optimized tile-based renderer, which renders
|
||||||
|
pixels not line-by-line (this is what accurate renderers do), but in 8x8 tiles,
|
||||||
|
which is much faster. But because of the way it works it can't render any
|
||||||
|
mid-frame image changes (raster effects), so it is useful only with some games.
|
||||||
|
|
||||||
|
Other two are accurate line-based renderers. The 8bit is faster but does not
|
||||||
|
run well with some games like Street Racer.
|
||||||
|
|
||||||
|
2. "Accurate timing"
|
||||||
|
This adds some more emulation precision, but slows the emulation down. Whithout
|
||||||
|
this option some games do not boot (Red Zone for example), others have sound
|
||||||
|
problems.
|
||||||
|
|
||||||
|
3. "Accurate sprites"
|
||||||
|
This option improves emulation of sprite priorities, it also enables emulation
|
||||||
|
of sprite collision bit. If you see one sprite being drawn incorrectly above
|
||||||
|
the other (often seen in Sonic 3D Blast), you can enable this to fix the problem.
|
||||||
|
This only works with the default renderer (see first option).
|
||||||
|
|
||||||
|
4. "Show FPS"
|
||||||
|
Self-explanatory. Format is XX/YY, where XX is the number of rendered frames and
|
||||||
|
YY is the number of emulated frames per second.
|
||||||
|
|
||||||
|
5. "Frameskip"
|
||||||
|
How many frames to skip rendering before displaying another.
|
||||||
|
"Auto" is recommended.
|
||||||
|
|
||||||
|
6. "Enable sound"
|
||||||
|
Does what it says. You must enable at least YM2612 or SN76496 (in advanced options,
|
||||||
|
see below) for this to make sense.
|
||||||
|
|
||||||
|
7. "Sound Quality"
|
||||||
|
Sound rate and stereo mode. If you want 44100Hz sound, it is recommended to enable
|
||||||
|
the second core (next option).
|
||||||
|
|
||||||
|
8. "Use ARM940 core for sound"
|
||||||
|
This option causes PicoDrive to use ARM940T core (GP2X's second CPU) for sound
|
||||||
|
(i.e. to generate YM2612 samples) to improve performance noticeably.
|
||||||
|
|
||||||
|
9. "6 button pad"
|
||||||
|
If you enable this, games will think that 6 button gamepad is connected. If you
|
||||||
|
go and reconfigure your keys, you will be able to bind X,Y,Z and mode actions.
|
||||||
|
|
||||||
|
10. "Genesis Region"
|
||||||
|
This option lets you force the game to think it is running on machine from the
|
||||||
|
specified region.
|
||||||
|
|
||||||
|
11. "Use SRAM savestates"
|
||||||
|
This will automatically read/write SRAM savestates for games which are using them.
|
||||||
|
SRAM is saved whenever you pause your game or exit the emulator.
|
||||||
|
|
||||||
|
12. "GP2X CPU clocks"
|
||||||
|
Here you can change clocks of both GP2X's CPUs. Larger values increase performance.
|
||||||
|
There is no separate option for the second CPU because both CPUs use the same clock
|
||||||
|
source. Setting this option to 200 will cause PicoDrive NOT to change GP2X's clocks
|
||||||
|
at all.
|
||||||
|
|
||||||
|
13. "[advanced options]"
|
||||||
|
Enters advanced options menu (see below).
|
||||||
|
|
||||||
|
14. "Save cfg as default"
|
||||||
|
If you save your config here it will be loaded on next ROM load, but only if there
|
||||||
|
is no game specific config saved (which will be loaded in that case).
|
||||||
|
|
||||||
|
15. "Save cfg for current game only"
|
||||||
|
Whenever you load current ROM again these settings will be loaded (squidgehack and
|
||||||
|
RAM settings will not take effect until emulator is restarted).
|
||||||
|
|
||||||
|
Advanced configuration
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Enter [advanced options] in config menu to see these options.
|
||||||
|
|
||||||
|
1. "Scale 32 column mode"
|
||||||
|
This enables hardware scaling for lower-res genesis mode (where width is
|
||||||
|
32 8-pixel tiles, instead of 40 in other mode).
|
||||||
|
|
||||||
|
2. "Gamma correction"
|
||||||
|
Alters image gamma through GP2X hardware. Larger values make image to look brighter,
|
||||||
|
lower - darker (default is 1.0).
|
||||||
|
|
||||||
|
3. "Emulate Z80"
|
||||||
|
Enables emulation of Z80 chip, which was mostly used to drive the other sound chips.
|
||||||
|
Some games do complex sync with it, so you must enable it even if you don't use
|
||||||
|
sound to be able to play them.
|
||||||
|
|
||||||
|
4. "Emulate YM2612 (FM)"
|
||||||
|
This enables emulation of six-channel FM sound synthesizer chip, which was used to
|
||||||
|
produce sound effects and music.
|
||||||
|
|
||||||
|
5. "Emulate SN76496 (PSG)"
|
||||||
|
This enables emulation of additional sound chip for additional effects.
|
||||||
|
|
||||||
|
Note: if you change sound settings AFTER loading a ROM, you may need to reset
|
||||||
|
game to get sound. This is because most games initialize sound chips on
|
||||||
|
startup, and this data is lost when sound chips are being enabled/disabled.
|
||||||
|
|
||||||
|
6. "gzip savestates"
|
||||||
|
This will always apply gzip compression on your savestates, allowing you to
|
||||||
|
save some space and load/save time.
|
||||||
|
|
||||||
|
7. "USB joy controls player X"
|
||||||
|
If you are able to use USB joysticks with your GP2X, this options selects which
|
||||||
|
player the joystick controls.
|
||||||
|
|
||||||
|
8. "Don't save config on exit"
|
||||||
|
This will disable config autowrite on exit (which might cause SD card corruption
|
||||||
|
according to DaveC).
|
||||||
|
|
||||||
|
9. "craigix's RAM timings"
|
||||||
|
This overclocks the GP2X RAM chips, but may cause instability. Recommended if you
|
||||||
|
use the second core for sound. Needs emulator restart to take effect.
|
||||||
|
See this thread:
|
||||||
|
http://www.gp32x.com/board/index.php?showtopic=32319
|
||||||
|
|
||||||
|
10. "squidgehack"
|
||||||
|
Well known way to improve the GP2X performance. You must restart the emulator
|
||||||
|
for the change of this option to take effect.
|
||||||
|
|
||||||
|
|
||||||
|
Key configuration
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
When you select "Configure controls" from the menu, you enter a key configuration
|
||||||
|
mode, where you use SELECT to change an action, and then press a key you like to
|
||||||
|
bind to that action. You can press the same key again to unbind. Select "DONE"
|
||||||
|
action and press any key to finish.
|
||||||
|
|
||||||
|
|
156
gp2x/cpuctrl.c
Normal file
156
gp2x/cpuctrl.c
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
340
gp2x/cpuctrl.gpl.txt
Normal file
340
gp2x/cpuctrl.gpl.txt
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Library General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<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.
|
16
gp2x/cpuctrl.h
Normal file
16
gp2x/cpuctrl.h
Normal file
|
@ -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
|
1121
gp2x/emu.c
Normal file
1121
gp2x/emu.c
Normal file
File diff suppressed because it is too large
Load diff
46
gp2x/emu.h
Normal file
46
gp2x/emu.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// (c) Copyright 2006 notaz, All rights reserved.
|
||||||
|
// Free for non-commercial use.
|
||||||
|
|
||||||
|
// For commercial use, separate licencing terms must be obtained.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// engine states
|
||||||
|
enum TPicoGameState {
|
||||||
|
PGS_Paused = 1,
|
||||||
|
PGS_Running,
|
||||||
|
PGS_Quit,
|
||||||
|
PGS_KeyConfig,
|
||||||
|
PGS_ReloadRom,
|
||||||
|
PGS_Menu,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char lastRomFile[512];
|
||||||
|
int EmuOpt; // LSb->MSb: use_sram, show_fps, enable_sound, gzip_saves,
|
||||||
|
// squidgehack, save_cfg_on_exit, <unused>, 16_bit_mode
|
||||||
|
// craigix_ram, confirm_save
|
||||||
|
int PicoOpt; // used for config saving only, see Pico.h
|
||||||
|
int PsndRate; // ditto
|
||||||
|
int PicoRegion; // ditto
|
||||||
|
int Frameskip;
|
||||||
|
int CPUclock;
|
||||||
|
int KeyBinds[32];
|
||||||
|
int volume;
|
||||||
|
int gamma;
|
||||||
|
int JoyBinds[4][32];
|
||||||
|
} currentConfig_t;
|
||||||
|
|
||||||
|
extern char romFileName[];
|
||||||
|
extern int engineState;
|
||||||
|
extern currentConfig_t currentConfig;
|
||||||
|
|
||||||
|
|
||||||
|
int emu_ReloadRom(void);
|
||||||
|
void emu_Init(void);
|
||||||
|
void emu_Deinit(void);
|
||||||
|
int emu_SaveLoadGame(int load, int sram);
|
||||||
|
void emu_Loop(void);
|
||||||
|
void emu_ResetGame(void);
|
||||||
|
int emu_ReadConfig(int game);
|
||||||
|
int emu_WriteConfig(int game);
|
311
gp2x/gp2x.c
Normal file
311
gp2x/gp2x.c
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
/**
|
||||||
|
* All this is mostly based on rlyeh's minimal library.
|
||||||
|
* Copied here to review all his code and understand what's going on.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
GP2X minimal library v0.A by rlyeh, (c) 2005. emulnation.info@rlyeh (swap it!)
|
||||||
|
|
||||||
|
Thanks to Squidge, Robster, snaff, Reesy and NK, for the help & previous work! :-)
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
Free for non-commercial projects (it would be nice receiving a mail from you).
|
||||||
|
Other cases, ask me first.
|
||||||
|
|
||||||
|
GamePark Holdings is not allowed to use this library and/or use parts from it.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.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.h"
|
||||||
|
#include "usbjoy.h"
|
||||||
|
|
||||||
|
volatile unsigned short *gp2x_memregs;
|
||||||
|
//static
|
||||||
|
volatile unsigned long *gp2x_memregl;
|
||||||
|
static void *gp2x_screens[4];
|
||||||
|
static int screensel = 0;
|
||||||
|
//static
|
||||||
|
int memdev = 0;
|
||||||
|
static int sounddev = 0, mixerdev = 0;
|
||||||
|
|
||||||
|
void *gp2x_screen;
|
||||||
|
|
||||||
|
#define FRAMEBUFF_ADDR0 0x4000000-640*480
|
||||||
|
#define FRAMEBUFF_ADDR1 0x4000000-640*480*2
|
||||||
|
#define FRAMEBUFF_ADDR2 0x4000000-640*480*3
|
||||||
|
#define FRAMEBUFF_ADDR3 0x4000000-640*480*4
|
||||||
|
|
||||||
|
static const int gp2x_screenaddrs[] = { FRAMEBUFF_ADDR0, FRAMEBUFF_ADDR1, FRAMEBUFF_ADDR2, FRAMEBUFF_ADDR3 };
|
||||||
|
|
||||||
|
|
||||||
|
/* video stuff */
|
||||||
|
void gp2x_video_flip(void)
|
||||||
|
{
|
||||||
|
unsigned int address = gp2x_screenaddrs[screensel&3];
|
||||||
|
|
||||||
|
/* test */
|
||||||
|
/* {
|
||||||
|
int i; char *p=gp2x_screen;
|
||||||
|
for (i=0; i < 240; i++) { memset(p+i*320, 0, 32); }
|
||||||
|
}*/
|
||||||
|
|
||||||
|
gp2x_memregs[0x290E>>1]=(unsigned short)(address);
|
||||||
|
gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16);
|
||||||
|
gp2x_memregs[0x2912>>1]=(unsigned short)(address);
|
||||||
|
gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16);
|
||||||
|
|
||||||
|
// jump to other buffer:
|
||||||
|
gp2x_screen = gp2x_screens[++screensel&3];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_video_changemode(int bpp)
|
||||||
|
{
|
||||||
|
gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/
|
||||||
|
gp2x_memregs[0x290C>>1]=320*((bpp+1)/8); /*line width in bytes*/
|
||||||
|
|
||||||
|
gp2x_memset_all_buffers(0, 0, 640*480);
|
||||||
|
gp2x_video_flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_video_setpalette(int *pal, int len)
|
||||||
|
{
|
||||||
|
unsigned short *g=(unsigned short *)pal;
|
||||||
|
volatile unsigned short *memreg = &gp2x_memregs[0x295A>>1];
|
||||||
|
gp2x_memregs[0x2958>>1] = 0;
|
||||||
|
|
||||||
|
len *= 2;
|
||||||
|
while(len--) *memreg=*g++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TV Compatible function //
|
||||||
|
void gp2x_video_RGB_setscaling(int W, int H)
|
||||||
|
{
|
||||||
|
float escalaw, escalah;
|
||||||
|
int bpp = (gp2x_memregs[0x28DA>>1]>>9)&0x3;
|
||||||
|
|
||||||
|
escalaw = 1024.0; // RGB Horiz LCD
|
||||||
|
escalah = 320.0; // RGB Vert LCD
|
||||||
|
|
||||||
|
if(gp2x_memregs[0x2800>>1]&0x100) //TV-Out
|
||||||
|
{
|
||||||
|
escalaw=489.0; // RGB Horiz TV (PAL, NTSC)
|
||||||
|
if (gp2x_memregs[0x2818>>1] == 287) //PAL
|
||||||
|
escalah=274.0; // RGB Vert TV PAL
|
||||||
|
else if (gp2x_memregs[0x2818>>1] == 239) //NTSC
|
||||||
|
escalah=331.0; // RGB Vert TV NTSC
|
||||||
|
}
|
||||||
|
|
||||||
|
// scale horizontal
|
||||||
|
gp2x_memregs[0x2906>>1]=(unsigned short)((float)escalaw *(W/320.0));
|
||||||
|
// scale vertical
|
||||||
|
gp2x_memregl[0x2908>>2]=(unsigned long)((float)escalah *bpp *(H/240.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* LCD updates @ 80Hz? */
|
||||||
|
void gp2x_video_wait_vsync(void)
|
||||||
|
{
|
||||||
|
gp2x_memregs[0x2846>>1] = 0x20|2; //(gp2x_memregs[0x2846>>1] | 0x20) & ~2;
|
||||||
|
while(!(gp2x_memregs[0x2846>>1] & 2));// usleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_memcpy_all_buffers(void *data, int offset, int len)
|
||||||
|
{
|
||||||
|
memcpy((char *)gp2x_screens[0] + offset, data, len);
|
||||||
|
memcpy((char *)gp2x_screens[1] + offset, data, len);
|
||||||
|
memcpy((char *)gp2x_screens[2] + offset, data, len);
|
||||||
|
memcpy((char *)gp2x_screens[3] + offset, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_memset_all_buffers(int offset, int byte, int len)
|
||||||
|
{
|
||||||
|
memset((char *)gp2x_screens[0] + offset, byte, len);
|
||||||
|
memset((char *)gp2x_screens[1] + offset, byte, len);
|
||||||
|
memset((char *)gp2x_screens[2] + offset, byte, len);
|
||||||
|
memset((char *)gp2x_screens[3] + offset, byte, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned long gp2x_joystick_read(int allow_usb_joy)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned long value=(gp2x_memregs[0x1198>>1] & 0x00FF);
|
||||||
|
if(value==0xFD) value=0xFA;
|
||||||
|
if(value==0xF7) value=0xEB;
|
||||||
|
if(value==0xDF) value=0xAF;
|
||||||
|
if(value==0x7F) value=0xBE;
|
||||||
|
value = ~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16));
|
||||||
|
|
||||||
|
if (allow_usb_joy && num_of_joys > 0) {
|
||||||
|
// check the usb joy as well..
|
||||||
|
gp2x_usbjoy_update();
|
||||||
|
for (i = 0; i < num_of_joys; i++)
|
||||||
|
value |= gp2x_usbjoy_check(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s_oldrate = 0, s_oldbits = 0, s_oldstereo = 0;
|
||||||
|
|
||||||
|
void gp2x_start_sound(int rate, int bits, int stereo)
|
||||||
|
{
|
||||||
|
int frag = 0, bsize, buffers;
|
||||||
|
|
||||||
|
// if no settings change, we don't need to do anything
|
||||||
|
if (rate == s_oldrate && s_oldbits == bits && s_oldstereo == stereo) return;
|
||||||
|
|
||||||
|
if (sounddev > 0) close(sounddev);
|
||||||
|
sounddev = open("/dev/dsp", O_WRONLY|O_ASYNC);
|
||||||
|
if (sounddev == -1)
|
||||||
|
printf("open(\"/dev/dsp\") failed with %i\n", errno);
|
||||||
|
|
||||||
|
ioctl(sounddev, SNDCTL_DSP_SPEED, &rate);
|
||||||
|
ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits);
|
||||||
|
ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo);
|
||||||
|
// calculate buffer size
|
||||||
|
buffers = 16;
|
||||||
|
bsize = rate / 32;
|
||||||
|
if (rate > 22050) { bsize*=4; buffers*=2; } // 44k mode seems to be very demanding
|
||||||
|
while ((bsize>>=1)) frag++;
|
||||||
|
frag |= buffers<<16; // 16 buffers
|
||||||
|
ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag);
|
||||||
|
printf("gp2x_set_sound: %i/%ibit/%s, %i buffers of %i bytes\n",
|
||||||
|
rate, bits, stereo?"stereo":"mono", frag>>16, 1<<(frag&0xffff));
|
||||||
|
|
||||||
|
s_oldrate = rate; s_oldbits = bits; s_oldstereo = stereo;
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_sound_write(void *buff, int len)
|
||||||
|
{
|
||||||
|
write(sounddev, buff, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_sound_volume(int l, int r)
|
||||||
|
{
|
||||||
|
l=l<0?0:l; l=l>255?255:l; r=r<0?0:r; r=r>255?255:r;
|
||||||
|
l<<=8; l|=r;
|
||||||
|
ioctl(mixerdev, SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 940 */
|
||||||
|
void Pause940(int yes)
|
||||||
|
{
|
||||||
|
if(yes)
|
||||||
|
gp2x_memregs[0x0904>>1] &= 0xFFFE;
|
||||||
|
else
|
||||||
|
gp2x_memregs[0x0904>>1] |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Reset940(int yes)
|
||||||
|
{
|
||||||
|
gp2x_memregs[0x3B48>>1] = ((yes&1) << 7) | (0x03); /* bank=3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* common */
|
||||||
|
void gp2x_init(void)
|
||||||
|
{
|
||||||
|
printf("entering init()\n"); fflush(stdout);
|
||||||
|
|
||||||
|
memdev = open("/dev/mem", O_RDWR);
|
||||||
|
if (memdev == -1)
|
||||||
|
{
|
||||||
|
printf("open(\"/dev/mem\") failed with %i\n", errno);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
gp2x_memregs = mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000);
|
||||||
|
printf("memregs are @ %p\n", gp2x_memregs);
|
||||||
|
if(gp2x_memregs == MAP_FAILED)
|
||||||
|
{
|
||||||
|
printf("mmap(memregs) failed with %i\n", errno);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
gp2x_memregl = (unsigned long *) gp2x_memregs;
|
||||||
|
|
||||||
|
gp2x_screens[3] = mmap(0, 640*480*4, PROT_WRITE, MAP_SHARED, memdev, FRAMEBUFF_ADDR3);
|
||||||
|
if(gp2x_screens[3] == MAP_FAILED)
|
||||||
|
{
|
||||||
|
printf("mmap(gp2x_screen) failed with %i\n", errno);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
printf("framebuffers point to %p\n", gp2x_screens[3]);
|
||||||
|
gp2x_screens[2] = (char *) gp2x_screens[3]+640*480;
|
||||||
|
gp2x_screens[1] = (char *) gp2x_screens[2]+640*480;
|
||||||
|
gp2x_screens[0] = (char *) gp2x_screens[1]+640*480;
|
||||||
|
|
||||||
|
gp2x_screen = gp2x_screens[0];
|
||||||
|
screensel = 0;
|
||||||
|
|
||||||
|
// snd
|
||||||
|
mixerdev = open("/dev/mixer", O_RDWR);
|
||||||
|
if (mixerdev == -1)
|
||||||
|
printf("open(\"/dev/mixer\") failed with %i\n", errno);
|
||||||
|
|
||||||
|
/* init usb joys -GnoStiC */
|
||||||
|
gp2x_usbjoy_init();
|
||||||
|
|
||||||
|
printf("exitting init()\n"); fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ext_menu = 0, *ext_state = 0;
|
||||||
|
|
||||||
|
void gp2x_deinit(void)
|
||||||
|
{
|
||||||
|
Reset940(1);
|
||||||
|
Pause940(1);
|
||||||
|
|
||||||
|
gp2x_video_changemode(15);
|
||||||
|
munmap(gp2x_screens[0], 640*480*4);
|
||||||
|
munmap((void *)gp2x_memregs, 0x10000);
|
||||||
|
close(memdev);
|
||||||
|
close(mixerdev);
|
||||||
|
if (sounddev > 0) close(sounddev);
|
||||||
|
|
||||||
|
gp2x_usbjoy_deinit();
|
||||||
|
|
||||||
|
printf("all done, running ");
|
||||||
|
|
||||||
|
// Zaq121's alternative frontend support from MAME
|
||||||
|
if(ext_menu && ext_state) {
|
||||||
|
printf("%s -state %s\n", ext_menu, ext_state);
|
||||||
|
execl(ext_menu, ext_menu, "-state", ext_state, NULL);
|
||||||
|
} else if(ext_menu) {
|
||||||
|
printf("%s\n", ext_menu);
|
||||||
|
execl(ext_menu, ext_menu, NULL);
|
||||||
|
} else {
|
||||||
|
printf("gp2xmenu\n");
|
||||||
|
chdir("/usr/gp2x");
|
||||||
|
execl("gp2xmenu", "gp2xmenu", NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
40
gp2x/gp2x.h
Normal file
40
gp2x/gp2x.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
#ifndef __GP2X_H__
|
||||||
|
#define __GP2X_H__
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_init(void);
|
||||||
|
void gp2x_deinit(void);
|
||||||
|
|
||||||
|
/* video */
|
||||||
|
void gp2x_video_flip(void);
|
||||||
|
void gp2x_video_changemode(int bpp);
|
||||||
|
void gp2x_video_setpalette(int *pal, int len);
|
||||||
|
void gp2x_video_RGB_setscaling(int W, int H);
|
||||||
|
void gp2x_video_wait_vsync(void);
|
||||||
|
void gp2x_memcpy_all_buffers(void *data, int offset, int len);
|
||||||
|
void gp2x_memset_all_buffers(int offset, int byte, int len);
|
||||||
|
|
||||||
|
/* sound */
|
||||||
|
void gp2x_start_sound(int rate, int bits, int stereo);
|
||||||
|
void gp2x_sound_write(void *buff, int len);
|
||||||
|
void gp2x_sound_volume(int l, int r);
|
||||||
|
|
||||||
|
/* joy */
|
||||||
|
unsigned long gp2x_joystick_read(int allow_usb_joy);
|
||||||
|
|
||||||
|
/* 940 core */
|
||||||
|
void Pause940(int yes);
|
||||||
|
void Reset940(int yes);
|
||||||
|
|
||||||
|
|
||||||
|
extern void *gp2x_screen;
|
||||||
|
extern int memdev;
|
||||||
|
|
||||||
|
|
||||||
|
enum { GP2X_UP=0x1, GP2X_LEFT=0x4, GP2X_DOWN=0x10, GP2X_RIGHT=0x40,
|
||||||
|
GP2X_START=1<<8, GP2X_SELECT=1<<9, GP2X_L=1<<10, GP2X_R=1<<11,
|
||||||
|
GP2X_A=1<<12, GP2X_B=1<<13, GP2X_X=1<<14, GP2X_Y=1<<15,
|
||||||
|
GP2X_VOL_UP=1<<23, GP2X_VOL_DOWN=1<<22, GP2X_PUSH=1<<27 };
|
||||||
|
|
||||||
|
#endif
|
143
gp2x/main.c
Normal file
143
gp2x/main.c
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
// (c) Copyright 2006 notaz, All rights reserved.
|
||||||
|
// Free for non-commercial use.
|
||||||
|
|
||||||
|
// For commercial use, separate licencing terms must be obtained.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
#include "gp2x.h"
|
||||||
|
#include "menu.h"
|
||||||
|
#include "emu.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#include "squidgehack.h"
|
||||||
|
#include "cpuctrl.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern char *ext_menu, *ext_state;
|
||||||
|
extern int select_exits;
|
||||||
|
extern char *PicoConfigFile;
|
||||||
|
int mmuhack_status = 0;
|
||||||
|
char **g_argv;
|
||||||
|
|
||||||
|
void parse_cmd_line(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int x, unrecognized = 0;
|
||||||
|
|
||||||
|
for(x = 1; x < argc; x++)
|
||||||
|
{
|
||||||
|
if(argv[x][0] == '-')
|
||||||
|
{
|
||||||
|
if(strcasecmp(argv[x], "-menu") == 0) {
|
||||||
|
if(x+1 < argc) { ++x; ext_menu = argv[x]; } /* External Frontend: Program Name */
|
||||||
|
}
|
||||||
|
else if(strcasecmp(argv[x], "-state") == 0) {
|
||||||
|
if(x+1 < argc) { ++x; ext_state = argv[x]; } /* External Frontend: Arguments */
|
||||||
|
}
|
||||||
|
else if(strcasecmp(argv[x], "-config") == 0) {
|
||||||
|
if(x+1 < argc) { ++x; PicoConfigFile = argv[x]; }
|
||||||
|
}
|
||||||
|
else if(strcasecmp(argv[x], "-selectexit") == 0) {
|
||||||
|
select_exits = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unrecognized = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* External Frontend: ROM Name */
|
||||||
|
FILE *f;
|
||||||
|
strncpy(romFileName, argv[x], PATH_MAX);
|
||||||
|
romFileName[PATH_MAX-1] = 0;
|
||||||
|
f = fopen(romFileName, "rb");
|
||||||
|
if (f) fclose(f);
|
||||||
|
else unrecognized = 1;
|
||||||
|
engineState = PGS_ReloadRom;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unrecognized) {
|
||||||
|
printf("\n\n\nPicoDrive v" VERSION " (c) notaz, 2006\n");
|
||||||
|
printf("usage: %s [options] [romfile]\n", argv[0]);
|
||||||
|
printf( "options:\n"
|
||||||
|
"-menu <menu_path> launch a custom program on exit instead of default gp2xmenu\n"
|
||||||
|
"-state <param> pass '-state param' to the menu program\n"
|
||||||
|
"-config <file> use specified config file instead of default 'picoconfig.bin'\n"
|
||||||
|
" see currentConfig_t structure in emu.h for the file format\n"
|
||||||
|
"-selectexit pressing SELECT will exit the emu and start 'menu_path'\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
g_argv = argv;
|
||||||
|
|
||||||
|
emu_ReadConfig(0);
|
||||||
|
gp2x_init();
|
||||||
|
if (currentConfig.EmuOpt&0x10) {
|
||||||
|
int ret = mmuhack();
|
||||||
|
printf("squidge hack code finished and returned %i\n", ret); fflush(stdout);
|
||||||
|
mmuhack_status = ret;
|
||||||
|
}
|
||||||
|
cpuctrl_init();
|
||||||
|
Reset940(1);
|
||||||
|
Pause940(1);
|
||||||
|
if (currentConfig.EmuOpt&0x100) {
|
||||||
|
printf("setting RAM timings.. "); fflush(stdout);
|
||||||
|
// craigix: --trc 6 --tras 4 --twr 1 --tmrd 1 --trfc 1 --trp 2 --trcd 2
|
||||||
|
set_RAM_Timings(6, 4, 1, 1, 1, 2, 2);
|
||||||
|
printf("done.\n"); fflush(stdout);
|
||||||
|
}
|
||||||
|
emu_Init();
|
||||||
|
|
||||||
|
engineState = PGS_Menu;
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
parse_cmd_line(argc, argv);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
switch (engineState)
|
||||||
|
{
|
||||||
|
case PGS_Menu:
|
||||||
|
menu_loop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGS_ReloadRom:
|
||||||
|
if (emu_ReloadRom())
|
||||||
|
engineState = PGS_Running;
|
||||||
|
else {
|
||||||
|
printf("PGS_ReloadRom == 0\n");
|
||||||
|
engineState = PGS_Menu;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGS_Running:
|
||||||
|
emu_Loop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PGS_Quit:
|
||||||
|
goto endloop;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("engine got into unknown state (%i), exitting\n", engineState);
|
||||||
|
goto endloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endloop:
|
||||||
|
|
||||||
|
emu_Deinit();
|
||||||
|
cpuctrl_deinit();
|
||||||
|
gp2x_deinit();
|
||||||
|
if(mmuhack_status)
|
||||||
|
mmuunhack();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
992
gp2x/menu.c
Normal file
992
gp2x/menu.c
Normal file
|
@ -0,0 +1,992 @@
|
||||||
|
// (c) Copyright 2006 notaz, All rights reserved.
|
||||||
|
// Free for non-commercial use.
|
||||||
|
|
||||||
|
// For commercial use, separate licencing terms must be obtained.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "gp2x.h"
|
||||||
|
#include "emu.h"
|
||||||
|
#include "menu.h"
|
||||||
|
#include "usbjoy.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#include "Pico/PicoInt.h"
|
||||||
|
|
||||||
|
#ifndef _DIRENT_HAVE_D_TYPE
|
||||||
|
#error "need d_type for file browser
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern char *actionNames[];
|
||||||
|
extern char romFileName[PATH_MAX];
|
||||||
|
extern char *rom_data;
|
||||||
|
extern int mmuhack_status;
|
||||||
|
extern int state_slot;
|
||||||
|
|
||||||
|
static char *gp2xKeyNames[] = {
|
||||||
|
"UP", "???", "LEFT", "???", "DOWN", "???", "RIGHT", "???",
|
||||||
|
"START", "SELECT", "L", "R", "A", "B", "X", "Y",
|
||||||
|
"???", "???", "???", "???", "???", "???", "VOL DOWN", "VOL UP",
|
||||||
|
"???", "???", "???", "PUSH", "???", "???", "???", "???"
|
||||||
|
};
|
||||||
|
|
||||||
|
char menuErrorMsg[40] = {0, };
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned char fontdata8x8[] =
|
||||||
|
{
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C,
|
||||||
|
0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,
|
||||||
|
0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00,
|
||||||
|
0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00,
|
||||||
|
0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,
|
||||||
|
0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40,
|
||||||
|
0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00,
|
||||||
|
0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00,
|
||||||
|
0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00,
|
||||||
|
0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00,
|
||||||
|
0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00,
|
||||||
|
0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00,
|
||||||
|
0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00,
|
||||||
|
0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00,
|
||||||
|
0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00,
|
||||||
|
0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00,
|
||||||
|
0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00,
|
||||||
|
0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00,
|
||||||
|
0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00,
|
||||||
|
0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30,
|
||||||
|
0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,
|
||||||
|
0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00,
|
||||||
|
0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00,
|
||||||
|
0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00,
|
||||||
|
0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00,
|
||||||
|
0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00,
|
||||||
|
0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,
|
||||||
|
0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00,
|
||||||
|
0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00,
|
||||||
|
0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
|
||||||
|
0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00,
|
||||||
|
0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00,
|
||||||
|
0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
|
||||||
|
0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00,
|
||||||
|
0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00,
|
||||||
|
0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,
|
||||||
|
0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,
|
||||||
|
0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
|
||||||
|
0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00,
|
||||||
|
0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00,
|
||||||
|
0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00,
|
||||||
|
0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C,
|
||||||
|
0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00,
|
||||||
|
0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00,
|
||||||
|
0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00,
|
||||||
|
0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00,
|
||||||
|
0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06,
|
||||||
|
0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00,
|
||||||
|
0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00,
|
||||||
|
0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00,
|
||||||
|
0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C,
|
||||||
|
0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00,
|
||||||
|
0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00,
|
||||||
|
0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gp2x_text(unsigned char *screen, int x, int y, char *text, int color)
|
||||||
|
{
|
||||||
|
int i,l;
|
||||||
|
|
||||||
|
screen = screen + x + y*320;
|
||||||
|
|
||||||
|
for (i = 0; i < strlen(text); i++)
|
||||||
|
{
|
||||||
|
for (l=0;l<8;l++)
|
||||||
|
{
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=color;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=color;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=color;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=color;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=color;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=color;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=color;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=color;
|
||||||
|
}
|
||||||
|
screen += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draws white text to current bbp15 screen
|
||||||
|
void gp2x_text_out15(int x, int y, char *text)
|
||||||
|
{
|
||||||
|
int i,l;
|
||||||
|
unsigned short *screen = gp2x_screen;
|
||||||
|
|
||||||
|
screen = screen + x + y*320;
|
||||||
|
|
||||||
|
for (i = 0; i < strlen(text); i++)
|
||||||
|
{
|
||||||
|
for (l=0;l<8;l++)
|
||||||
|
{
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=0xffff;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=0xffff;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=0xffff;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=0xffff;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=0xffff;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=0xffff;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=0xffff;
|
||||||
|
if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=0xffff;
|
||||||
|
}
|
||||||
|
screen += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_text_out8(int x, int y, char *texto, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
char buffer[512];
|
||||||
|
|
||||||
|
va_start(args,texto);
|
||||||
|
vsprintf(buffer,texto,args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
gp2x_text(gp2x_screen,x,y,buffer,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_text_out8_2(int x, int y, char *texto, int color)
|
||||||
|
{
|
||||||
|
gp2x_text(gp2x_screen, x, y, texto, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_text_out8_lim(int x, int y, char *texto, int max)
|
||||||
|
{
|
||||||
|
char buffer[320/8+1];
|
||||||
|
|
||||||
|
strncpy(buffer, texto, 320/8);
|
||||||
|
if (max > 320/8) max = 320/8;
|
||||||
|
if (max < 0) max = 0;
|
||||||
|
buffer[max] = 0;
|
||||||
|
|
||||||
|
gp2x_text(gp2x_screen,x,y,buffer,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned long inp_prev = 0;
|
||||||
|
static int inp_prevjoy = 0;
|
||||||
|
|
||||||
|
static unsigned long wait_for_input(unsigned long interesting)
|
||||||
|
{
|
||||||
|
unsigned long ret;
|
||||||
|
static int repeats = 0, wait = 300*1000;
|
||||||
|
int release = 0, i;
|
||||||
|
|
||||||
|
if (repeats == 5 || repeats == 15 || repeats == 30) wait /= 2;
|
||||||
|
|
||||||
|
for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {
|
||||||
|
if(i == 0) repeats++;
|
||||||
|
usleep(wait/6);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {
|
||||||
|
usleep(50000);
|
||||||
|
release = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (release || ret != inp_prev) {
|
||||||
|
repeats = 0;
|
||||||
|
wait = 300*1000;
|
||||||
|
}
|
||||||
|
inp_prev = ret;
|
||||||
|
inp_prevjoy = 0;
|
||||||
|
|
||||||
|
// we don't need diagonals in menus
|
||||||
|
if ((ret&GP2X_UP) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;
|
||||||
|
if ((ret&GP2X_UP) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;
|
||||||
|
if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;
|
||||||
|
if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long input2_read(unsigned long interesting, int *joy)
|
||||||
|
{
|
||||||
|
unsigned long ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
*joy = 0;
|
||||||
|
if ((ret = gp2x_joystick_read(0) & interesting)) break;
|
||||||
|
gp2x_usbjoy_update();
|
||||||
|
for (i = 0; i < num_of_joys; i++) {
|
||||||
|
ret = gp2x_usbjoy_check2(i);
|
||||||
|
if (ret) { *joy = i + 1; break; }
|
||||||
|
}
|
||||||
|
if (ret) break;
|
||||||
|
}
|
||||||
|
while(0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// similar to wait_for_input(), but returns joy num
|
||||||
|
static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy)
|
||||||
|
{
|
||||||
|
unsigned long ret;
|
||||||
|
const int wait = 300*1000;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (inp_prevjoy == 0) inp_prev &= interesting;
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
ret = input2_read(interesting, joy);
|
||||||
|
if (*joy != inp_prevjoy || ret != inp_prev) break;
|
||||||
|
usleep(wait/6);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( !(ret = input2_read(interesting, joy)) ) {
|
||||||
|
usleep(50000);
|
||||||
|
}
|
||||||
|
|
||||||
|
inp_prev = ret;
|
||||||
|
inp_prevjoy = *joy;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -------------- ROM selector --------------
|
||||||
|
|
||||||
|
static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)
|
||||||
|
{
|
||||||
|
int start, i, pos;
|
||||||
|
|
||||||
|
start = 12 - sel;
|
||||||
|
n--; // exclude current dir (".")
|
||||||
|
|
||||||
|
memset(gp2x_screen, 0, 320*240);
|
||||||
|
|
||||||
|
if(start - 2 >= 0)
|
||||||
|
gp2x_text_out8_lim(14, (start - 2)*10, curdir, 38);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
pos = start + i;
|
||||||
|
if (pos < 0) continue;
|
||||||
|
if (pos > 23) break;
|
||||||
|
if (namelist[i+1]->d_type == DT_DIR) {
|
||||||
|
gp2x_text_out8_lim(14, pos*10, "/", 1);
|
||||||
|
gp2x_text_out8_lim(14+8, pos*10, namelist[i+1]->d_name, 37);
|
||||||
|
} else {
|
||||||
|
gp2x_text_out8_lim(14, pos*10, namelist[i+1]->d_name, 38);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gp2x_text_out8(5, 120, ">");
|
||||||
|
gp2x_video_flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int scandir_cmp(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;
|
||||||
|
if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);
|
||||||
|
if ((*d1)->d_type == DT_DIR) return -1; // put before
|
||||||
|
if ((*d2)->d_type == DT_DIR) return 1;
|
||||||
|
return alphasort(d1, d2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *romsel_loop(char *curr_path)
|
||||||
|
{
|
||||||
|
struct dirent **namelist;
|
||||||
|
DIR *dir;
|
||||||
|
int n, sel = 0;
|
||||||
|
unsigned long inp = 0;
|
||||||
|
char *ret = NULL, *fname = NULL;
|
||||||
|
|
||||||
|
// is this a dir or a full path?
|
||||||
|
if ((dir = opendir(curr_path))) {
|
||||||
|
closedir(dir);
|
||||||
|
} else {
|
||||||
|
char *p;
|
||||||
|
for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);
|
||||||
|
*p = 0;
|
||||||
|
fname = p+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = scandir(curr_path, &namelist, 0, scandir_cmp);
|
||||||
|
if (n < 0) {
|
||||||
|
// try root
|
||||||
|
n = scandir(curr_path, &namelist, 0, scandir_cmp);
|
||||||
|
if (n < 0) {
|
||||||
|
// oops, we failed
|
||||||
|
printf("dir: "); printf(curr_path); printf("\n");
|
||||||
|
perror("scandir");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to find sel
|
||||||
|
if (fname != NULL) {
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < n; i++) {
|
||||||
|
if (strcmp(namelist[i]->d_name, fname) == 0) {
|
||||||
|
sel = i - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
draw_dirlist(curr_path, namelist, n, sel);
|
||||||
|
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X);
|
||||||
|
if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = n-2; }
|
||||||
|
if(inp & GP2X_DOWN) { sel++; if (sel > n-2) sel = 0; }
|
||||||
|
if(inp & GP2X_LEFT) { sel-=10; if (sel < 0) sel = 0; }
|
||||||
|
if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }
|
||||||
|
if(inp & GP2X_B) { // enter dir/select
|
||||||
|
again:
|
||||||
|
if (namelist[sel+1]->d_type == DT_REG) {
|
||||||
|
strcpy(romFileName, curr_path);
|
||||||
|
strcat(romFileName, "/");
|
||||||
|
strcat(romFileName, namelist[sel+1]->d_name);
|
||||||
|
ret = romFileName;
|
||||||
|
break;
|
||||||
|
} else if (namelist[sel+1]->d_type == DT_DIR) {
|
||||||
|
int newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;
|
||||||
|
char *p, *newdir = malloc(newlen);
|
||||||
|
if (strcmp(namelist[sel+1]->d_name, "..") == 0) {
|
||||||
|
char *start = curr_path;
|
||||||
|
p = start + strlen(start) - 1;
|
||||||
|
while (*p == '/' && p > start) p--;
|
||||||
|
while (*p != '/' && p > start) p--;
|
||||||
|
if (p <= start) strcpy(newdir, "/");
|
||||||
|
else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }
|
||||||
|
} else {
|
||||||
|
strcpy(newdir, curr_path);
|
||||||
|
p = newdir + strlen(newdir) - 1;
|
||||||
|
while (*p == '/' && p >= newdir) *p-- = 0;
|
||||||
|
strcat(newdir, "/");
|
||||||
|
strcat(newdir, namelist[sel+1]->d_name);
|
||||||
|
}
|
||||||
|
ret = romsel_loop(newdir);
|
||||||
|
free(newdir);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// unknown file type, happens on NTFS mounts. Try to guess.
|
||||||
|
FILE *tstf; int tmp;
|
||||||
|
strcpy(romFileName, curr_path);
|
||||||
|
strcat(romFileName, "/");
|
||||||
|
strcat(romFileName, namelist[sel+1]->d_name);
|
||||||
|
tstf = fopen(romFileName, "rb");
|
||||||
|
if (tstf != NULL)
|
||||||
|
{
|
||||||
|
if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)
|
||||||
|
namelist[sel+1]->d_type = DT_REG;
|
||||||
|
else namelist[sel+1]->d_type = DT_DIR;
|
||||||
|
fclose(tstf);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(inp & GP2X_X) break; // cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
while(n--) free(namelist[n]);
|
||||||
|
free(namelist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------- key config --------------
|
||||||
|
|
||||||
|
static char *usb_joy_key_name(int joy, int num)
|
||||||
|
{
|
||||||
|
static char name[16];
|
||||||
|
switch (num)
|
||||||
|
{
|
||||||
|
case 0: sprintf(name, "Joy%i UP", joy); break;
|
||||||
|
case 1: sprintf(name, "Joy%i DOWN", joy); break;
|
||||||
|
case 2: sprintf(name, "Joy%i LEFT", joy); break;
|
||||||
|
case 3: sprintf(name, "Joy%i RIGHT", joy); break;
|
||||||
|
default:sprintf(name, "Joy%i b%i", joy, num-3); break;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_key_config(int curr_act, int is_p2)
|
||||||
|
{
|
||||||
|
char strkeys[32*5];
|
||||||
|
int joy, i;
|
||||||
|
|
||||||
|
strkeys[0] = 0;
|
||||||
|
for (i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
if (currentConfig.KeyBinds[i] & (1 << curr_act))
|
||||||
|
{
|
||||||
|
if (curr_act < 16 && (currentConfig.KeyBinds[i] & (1 << 16)) != (is_p2 << 16)) continue;
|
||||||
|
if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gp2xKeyNames[i]); break; }
|
||||||
|
else strcpy(strkeys, gp2xKeyNames[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (joy = 0; joy < num_of_joys; joy++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
if (currentConfig.JoyBinds[joy][i] & (1 << curr_act))
|
||||||
|
{
|
||||||
|
if (curr_act < 16 && (currentConfig.JoyBinds[joy][i] & (1 << 16)) != (is_p2 << 16)) continue;
|
||||||
|
if (strkeys[0]) {
|
||||||
|
strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else strcpy(strkeys, usb_joy_key_name(joy + 1, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(gp2x_screen, 0, 320*240);
|
||||||
|
gp2x_text_out8(60, 40, "Action: %s", actionNames[curr_act]);
|
||||||
|
gp2x_text_out8(60, 60, "Keys: %s", strkeys);
|
||||||
|
|
||||||
|
gp2x_text_out8(30, 180, "Use SELECT to change action");
|
||||||
|
gp2x_text_out8(30, 190, "Press a key to bind/unbind");
|
||||||
|
gp2x_text_out8(30, 200, "Select \"Done\" action and");
|
||||||
|
gp2x_text_out8(30, 210, " press any key to finish");
|
||||||
|
gp2x_video_flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void key_config_loop(int is_p2)
|
||||||
|
{
|
||||||
|
int curr_act = 0, joy = 0, i;
|
||||||
|
unsigned long inp = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
draw_key_config(curr_act, is_p2);
|
||||||
|
inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS, &joy);
|
||||||
|
// printf("got %08lX from joy %i\n", inp, joy);
|
||||||
|
if (joy == 0) {
|
||||||
|
if (inp & GP2X_SELECT) {
|
||||||
|
curr_act++;
|
||||||
|
while (!actionNames[curr_act] && curr_act < 32) curr_act++;
|
||||||
|
if (curr_act > 31) curr_act = 0;
|
||||||
|
}
|
||||||
|
inp &= CONFIGURABLE_KEYS;
|
||||||
|
inp &= ~GP2X_SELECT;
|
||||||
|
}
|
||||||
|
if (curr_act == 31 && inp) break;
|
||||||
|
if (joy == 0) {
|
||||||
|
for (i = 0; i < 32; i++)
|
||||||
|
if (inp & (1 << i)) {
|
||||||
|
currentConfig.KeyBinds[i] ^= (1 << curr_act);
|
||||||
|
if (is_p2) currentConfig.KeyBinds[i] |= (1 << 16); // player 2 flag
|
||||||
|
else currentConfig.KeyBinds[i] &= ~(1 << 16);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < 32; i++)
|
||||||
|
if (inp & (1 << i)) {
|
||||||
|
currentConfig.JoyBinds[joy-1][i] ^= (1 << curr_act);
|
||||||
|
if (is_p2) currentConfig.JoyBinds[joy-1][i] |= (1 << 16);
|
||||||
|
else currentConfig.JoyBinds[joy-1][i] &= ~(1 << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_kc_sel(int menu_sel)
|
||||||
|
{
|
||||||
|
int tl_x = 25+40, tl_y = 60, y, i;
|
||||||
|
char joyname[36];
|
||||||
|
|
||||||
|
y = tl_y;
|
||||||
|
memset(gp2x_screen, 0, 320*240);
|
||||||
|
gp2x_text_out8(tl_x, y, "Player 1");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Player 2");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Done");
|
||||||
|
|
||||||
|
// draw cursor
|
||||||
|
gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");
|
||||||
|
|
||||||
|
tl_x = 25;
|
||||||
|
gp2x_text_out8(tl_x, (y=110), "USB joys detected:");
|
||||||
|
if (num_of_joys > 0) {
|
||||||
|
for (i = 0; i < num_of_joys; i++) {
|
||||||
|
strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "%i: %s", i+1, joyname);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gp2x_video_flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kc_sel_loop(void)
|
||||||
|
{
|
||||||
|
int menu_sel = 2, menu_sel_max = 2;
|
||||||
|
unsigned long inp = 0;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
draw_kc_sel(menu_sel);
|
||||||
|
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);
|
||||||
|
if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
|
||||||
|
if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
|
||||||
|
if(inp & GP2X_B) {
|
||||||
|
switch (menu_sel) {
|
||||||
|
case 0: key_config_loop(0); return;
|
||||||
|
case 1: key_config_loop(1); return;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(inp & GP2X_X) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --------- advanced options ----------
|
||||||
|
|
||||||
|
// order must match that of currentConfig_t
|
||||||
|
struct {
|
||||||
|
int EmuOpt;
|
||||||
|
int PicoOpt;
|
||||||
|
int PsndRate;
|
||||||
|
int PicoRegion;
|
||||||
|
int Frameskip;
|
||||||
|
int CPUclock;
|
||||||
|
} tmp_opts;
|
||||||
|
int tmp_gamma;
|
||||||
|
|
||||||
|
static void draw_amenu_options(int menu_sel)
|
||||||
|
{
|
||||||
|
int tl_x = 25, tl_y = 60, y;
|
||||||
|
char *mms = mmuhack_status ? "active) " : "inactive)";
|
||||||
|
|
||||||
|
y = tl_y;
|
||||||
|
memset(gp2x_screen, 0, 320*240);
|
||||||
|
gp2x_text_out8(tl_x, y, "Scale 32 column mode %s", (tmp_opts.PicoOpt&0x100)?"ON":"OFF"); // 0
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Gamma correction %i.%02i", tmp_gamma / 100, tmp_gamma%100); // 1
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Emulate Z80 %s", (tmp_opts.PicoOpt&0x004)?"ON":"OFF"); // 2
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Emulate YM2612 (FM) %s", (tmp_opts.PicoOpt&0x001)?"ON":"OFF"); // 3
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Emulate SN76496 (PSG) %s", (tmp_opts.PicoOpt&0x002)?"ON":"OFF"); // 4
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "gzip savestates %s", (tmp_opts.EmuOpt &0x008)?"ON":"OFF"); // 5
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Don't save config on exit %s", (tmp_opts.EmuOpt &0x020)?"ON":"OFF"); // 6
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "needs restart:");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "craigix's RAM timings %s", (tmp_opts.EmuOpt &0x100)?"ON":"OFF"); // 8
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "squidgehack (now %s %s", mms, (tmp_opts.EmuOpt &0x010)?"ON":"OFF"); // 9
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Done");
|
||||||
|
|
||||||
|
// draw cursor
|
||||||
|
gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");
|
||||||
|
|
||||||
|
gp2x_video_flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amenu_loop_options(void)
|
||||||
|
{
|
||||||
|
int menu_sel = 0, menu_sel_max = 11;
|
||||||
|
unsigned long inp = 0;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
draw_amenu_options(menu_sel);
|
||||||
|
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);
|
||||||
|
if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
|
||||||
|
if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
|
||||||
|
if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options
|
||||||
|
switch (menu_sel) {
|
||||||
|
case 0: tmp_opts.PicoOpt^=0x100; break;
|
||||||
|
case 2: tmp_opts.PicoOpt^=0x004; break;
|
||||||
|
case 3: tmp_opts.PicoOpt^=0x001; break;
|
||||||
|
case 4: tmp_opts.PicoOpt^=0x002; break;
|
||||||
|
case 5: tmp_opts.EmuOpt ^=0x008; break;
|
||||||
|
case 6: tmp_opts.EmuOpt ^=0x020; break;
|
||||||
|
case 8: tmp_opts.EmuOpt ^=0x100; break;
|
||||||
|
case 9: tmp_opts.EmuOpt ^=0x010; break;
|
||||||
|
case 10: return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(inp & (GP2X_X|GP2X_A)) return;
|
||||||
|
if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise
|
||||||
|
switch (menu_sel) {
|
||||||
|
case 1:
|
||||||
|
while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {
|
||||||
|
tmp_gamma += (inp & GP2X_LEFT) ? -1 : 1;
|
||||||
|
if (tmp_gamma < 1) tmp_gamma = 1;
|
||||||
|
if (tmp_gamma > 300) tmp_gamma = 300;
|
||||||
|
draw_amenu_options(menu_sel);
|
||||||
|
usleep(18*1000);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------- options --------------
|
||||||
|
|
||||||
|
static char *region_name(unsigned int code)
|
||||||
|
{
|
||||||
|
char *names[] = { "Auto", "Japan NTSC", "Japan PAL", "USA", "Europe" };
|
||||||
|
int i = 0;
|
||||||
|
code <<= 1;
|
||||||
|
while((code >>=1)) i++;
|
||||||
|
if (i > 4) return "unknown";
|
||||||
|
return names[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_menu_options(int menu_sel)
|
||||||
|
{
|
||||||
|
int tl_x = 25, tl_y = 40, y;
|
||||||
|
char monostereo[8], strframeskip[8], *strrend;
|
||||||
|
|
||||||
|
strcpy(monostereo, (tmp_opts.PicoOpt&0x08)?"stereo":"mono");
|
||||||
|
if (tmp_opts.Frameskip < 0)
|
||||||
|
strcpy(strframeskip, "Auto");
|
||||||
|
else sprintf(strframeskip, "%i", tmp_opts.Frameskip);
|
||||||
|
if (tmp_opts.PicoOpt&0x10) {
|
||||||
|
strrend = " 8bit fast";
|
||||||
|
} else if (tmp_opts.EmuOpt&0x80) {
|
||||||
|
strrend = "16bit accurate";
|
||||||
|
} else {
|
||||||
|
strrend = " 8bit accurate";
|
||||||
|
}
|
||||||
|
|
||||||
|
y = tl_y;
|
||||||
|
memset(gp2x_screen, 0, 320*240);
|
||||||
|
gp2x_text_out8(tl_x, y, "Renderer: %s", strrend); // 0
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Accurate timing (slower) %s", (tmp_opts.PicoOpt&0x040)?"ON":"OFF"); // 1
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Accurate sprites (slower) %s", (tmp_opts.PicoOpt&0x080)?"ON":"OFF"); // 2
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Show FPS %s", (tmp_opts.EmuOpt &0x002)?"ON":"OFF"); // 3
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Frameskip %s", strframeskip);
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Enable sound %s", (tmp_opts.EmuOpt &0x004)?"ON":"OFF"); // 5
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Sound Quality: %5iHz %s", tmp_opts.PsndRate, monostereo);
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Use ARM940 core for sound %s", (tmp_opts.PicoOpt&0x200)?"ON":"OFF"); // 7
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "6 button pad %s", (tmp_opts.PicoOpt&0x020)?"ON":"OFF"); // 8
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Genesis Region: %s", region_name(tmp_opts.PicoRegion));
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Use SRAM savestates %s", (tmp_opts.EmuOpt &0x001)?"ON":"OFF"); // 10
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Confirm save overwrites %s", (tmp_opts.EmuOpt &0x200)?"ON":"OFF"); // 11
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Save slot %i", state_slot); // 12
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "GP2X CPU clocks %iMhz", tmp_opts.CPUclock);
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "[advanced options]");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Save cfg as default");
|
||||||
|
if (rom_data)
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Save cfg for current game only");
|
||||||
|
|
||||||
|
// draw cursor
|
||||||
|
gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");
|
||||||
|
|
||||||
|
gp2x_video_flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sndrate_prevnext(int rate, int dir)
|
||||||
|
{
|
||||||
|
int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };
|
||||||
|
|
||||||
|
for (i = 0; i < 5; i++)
|
||||||
|
if (rates[i] == rate) break;
|
||||||
|
|
||||||
|
i += dir ? 1 : -1;
|
||||||
|
if (i > 4) return dir ? 44100 : 22050;
|
||||||
|
if (i < 0) return dir ? 11025 : 8000;
|
||||||
|
return rates[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menu_options_save(void)
|
||||||
|
{
|
||||||
|
memcpy(¤tConfig.EmuOpt, &tmp_opts.EmuOpt, sizeof(tmp_opts));
|
||||||
|
currentConfig.gamma = tmp_gamma;
|
||||||
|
PicoOpt = currentConfig.PicoOpt;
|
||||||
|
PsndRate = currentConfig.PsndRate;
|
||||||
|
PicoRegionOverride = currentConfig.PicoRegion;
|
||||||
|
if (PicoOpt & 0x20) {
|
||||||
|
actionNames[ 8] = "Z"; actionNames[ 9] = "Y";
|
||||||
|
actionNames[10] = "X"; actionNames[11] = "MODE";
|
||||||
|
} else {
|
||||||
|
actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menu_loop_options(void)
|
||||||
|
{
|
||||||
|
int menu_sel = 0, menu_sel_max = 15;
|
||||||
|
unsigned long inp = 0;
|
||||||
|
|
||||||
|
if (rom_data) menu_sel_max++;
|
||||||
|
memcpy(&tmp_opts.EmuOpt, ¤tConfig.EmuOpt, sizeof(tmp_opts));
|
||||||
|
tmp_gamma = currentConfig.gamma;
|
||||||
|
tmp_opts.PicoOpt = PicoOpt;
|
||||||
|
tmp_opts.PsndRate = PsndRate;
|
||||||
|
tmp_opts.PicoRegion = PicoRegionOverride;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
draw_menu_options(menu_sel);
|
||||||
|
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);
|
||||||
|
if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
|
||||||
|
if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
|
||||||
|
if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options
|
||||||
|
switch (menu_sel) {
|
||||||
|
case 1: tmp_opts.PicoOpt^=0x040; break;
|
||||||
|
case 2: tmp_opts.PicoOpt^=0x080; break;
|
||||||
|
case 3: tmp_opts.EmuOpt ^=0x002; break;
|
||||||
|
case 5: tmp_opts.EmuOpt ^=0x004; break;
|
||||||
|
case 7: tmp_opts.PicoOpt^=0x200; break;
|
||||||
|
case 8: tmp_opts.PicoOpt^=0x020; break;
|
||||||
|
case 10: tmp_opts.EmuOpt ^=0x001; break;
|
||||||
|
case 11: tmp_opts.EmuOpt ^=0x200; break;
|
||||||
|
case 14: amenu_loop_options(); break;
|
||||||
|
case 15: // done (save)
|
||||||
|
menu_options_save();
|
||||||
|
emu_WriteConfig(0);
|
||||||
|
return;
|
||||||
|
case 16: // done (save for current game)
|
||||||
|
menu_options_save();
|
||||||
|
emu_WriteConfig(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(inp & GP2X_X) return; // done (no save)
|
||||||
|
if(inp & GP2X_A) {
|
||||||
|
menu_options_save();
|
||||||
|
return; // done (save)
|
||||||
|
}
|
||||||
|
if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise
|
||||||
|
switch (menu_sel) {
|
||||||
|
case 0:
|
||||||
|
if (inp & GP2X_LEFT) {
|
||||||
|
if ( tmp_opts.PicoOpt&0x10) tmp_opts.PicoOpt&= ~0x10;
|
||||||
|
else if (!(tmp_opts.EmuOpt &0x80))tmp_opts.EmuOpt |= 0x80;
|
||||||
|
else if ( tmp_opts.EmuOpt &0x80) break;
|
||||||
|
} else {
|
||||||
|
if ( tmp_opts.PicoOpt&0x10) break;
|
||||||
|
else if (!(tmp_opts.EmuOpt &0x80))tmp_opts.PicoOpt|= 0x10;
|
||||||
|
else if ( tmp_opts.EmuOpt &0x80) tmp_opts.EmuOpt &= ~0x80;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
tmp_opts.Frameskip += (inp & GP2X_LEFT) ? -1 : 1;
|
||||||
|
if (tmp_opts.Frameskip < 0) tmp_opts.Frameskip = -1;
|
||||||
|
if (tmp_opts.Frameskip > 32) tmp_opts.Frameskip = 32;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
if ((inp & GP2X_RIGHT) && tmp_opts.PsndRate == 44100 && !(tmp_opts.PicoOpt&0x08)) {
|
||||||
|
tmp_opts.PsndRate = 8000; tmp_opts.PicoOpt|= 0x08;
|
||||||
|
} else if ((inp & GP2X_LEFT) && tmp_opts.PsndRate == 8000 && (tmp_opts.PicoOpt&0x08)) {
|
||||||
|
tmp_opts.PsndRate = 44100; tmp_opts.PicoOpt&=~0x08;
|
||||||
|
} else tmp_opts.PsndRate = sndrate_prevnext(tmp_opts.PsndRate, inp & GP2X_RIGHT);
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
if (inp & GP2X_RIGHT) {
|
||||||
|
if (tmp_opts.PicoRegion) tmp_opts.PicoRegion<<=1; else tmp_opts.PicoRegion=1;
|
||||||
|
if (tmp_opts.PicoRegion > 8) tmp_opts.PicoRegion = 8;
|
||||||
|
} else tmp_opts.PicoRegion>>=1;
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
if (inp & GP2X_RIGHT) {
|
||||||
|
state_slot++; if (state_slot > 9) state_slot = 0;
|
||||||
|
} else {state_slot--; if (state_slot < 0) state_slot = 9;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {
|
||||||
|
tmp_opts.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;
|
||||||
|
if (tmp_opts.CPUclock < 1) tmp_opts.CPUclock = 1;
|
||||||
|
draw_menu_options(menu_sel);
|
||||||
|
usleep(50*1000);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------- credits --------------
|
||||||
|
|
||||||
|
static void draw_menu_credits(void)
|
||||||
|
{
|
||||||
|
int tl_x = 15, tl_y = 70, y;
|
||||||
|
memset(gp2x_screen, 0, 320*240);
|
||||||
|
|
||||||
|
gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006");
|
||||||
|
y = tl_y;
|
||||||
|
gp2x_text_out8(tl_x, y, "Credits:");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Dave: Cyclone 68000 core,");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), " base code of PicoDrive");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Stephane Dallongeville:");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), " opensource Gens");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Haze: Genesis hw info");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "rlyeh and others: minimal SDK");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Squidge: squidgehack");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Dzz: ARM940 sample");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "craigix: GP2X hardware");
|
||||||
|
|
||||||
|
gp2x_video_flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------- root menu --------------
|
||||||
|
|
||||||
|
static void draw_menu_root(int menu_sel)
|
||||||
|
{
|
||||||
|
int tl_x = 70, tl_y = 70, y;
|
||||||
|
memset(gp2x_screen, 0, 320*240);
|
||||||
|
|
||||||
|
gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION);
|
||||||
|
|
||||||
|
y = tl_y;
|
||||||
|
if (rom_data) {
|
||||||
|
gp2x_text_out8(tl_x, y, "Resume game");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Save State");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Load State");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Reset game");
|
||||||
|
} else {
|
||||||
|
y += 30;
|
||||||
|
}
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Load new ROM");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Change options");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Configure controls");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Credits");
|
||||||
|
gp2x_text_out8(tl_x, (y+=10), "Exit");
|
||||||
|
|
||||||
|
// draw cursor
|
||||||
|
gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");
|
||||||
|
// error
|
||||||
|
if (menuErrorMsg[0]) gp2x_text_out8(5, 226, menuErrorMsg);
|
||||||
|
gp2x_video_flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void menu_loop_root(void)
|
||||||
|
{
|
||||||
|
int menu_sel = 4, menu_sel_max = 8, menu_sel_min = 4;
|
||||||
|
unsigned long inp = 0;
|
||||||
|
char curr_path[PATH_MAX], *selfname;
|
||||||
|
FILE *tstf;
|
||||||
|
|
||||||
|
if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )
|
||||||
|
{
|
||||||
|
fclose(tstf);
|
||||||
|
strcpy(curr_path, currentConfig.lastRomFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
getcwd(curr_path, PATH_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rom_data) menu_sel = menu_sel_min = 0;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
draw_menu_root(menu_sel);
|
||||||
|
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT);
|
||||||
|
if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < menu_sel_min) menu_sel = menu_sel_max; }
|
||||||
|
if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = menu_sel_min; }
|
||||||
|
if(inp &(GP2X_SELECT|GP2X_X)){
|
||||||
|
if (rom_data) {
|
||||||
|
while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released
|
||||||
|
engineState = PGS_Running;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(inp & GP2X_B ) {
|
||||||
|
switch (menu_sel) {
|
||||||
|
case 0: // resume game
|
||||||
|
if (rom_data) { engineState = PGS_Running; return; }
|
||||||
|
break;
|
||||||
|
case 1: // save state
|
||||||
|
if (rom_data) {
|
||||||
|
if(emu_SaveLoadGame(0, 0)) {
|
||||||
|
strcpy(menuErrorMsg, "save failed");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
engineState = PGS_Running;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // load state
|
||||||
|
if (rom_data) {
|
||||||
|
if(emu_SaveLoadGame(1, 0)) {
|
||||||
|
strcpy(menuErrorMsg, "load failed");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
engineState = PGS_Running;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3: // reset game
|
||||||
|
if (rom_data) {
|
||||||
|
emu_ResetGame();
|
||||||
|
engineState = PGS_Running;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: // select rom
|
||||||
|
selfname = romsel_loop(curr_path);
|
||||||
|
if (selfname) {
|
||||||
|
printf("selected file: %s\n", selfname);
|
||||||
|
strncpy(currentConfig.lastRomFile, selfname, sizeof(currentConfig.lastRomFile)-1);
|
||||||
|
currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0;
|
||||||
|
engineState = PGS_ReloadRom;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 5: // options
|
||||||
|
menu_loop_options();
|
||||||
|
break;
|
||||||
|
case 6: // controls
|
||||||
|
kc_sel_loop();
|
||||||
|
break;
|
||||||
|
case 7: // credits
|
||||||
|
draw_menu_credits();
|
||||||
|
usleep(500*1000);
|
||||||
|
inp = wait_for_input(GP2X_B|GP2X_X);
|
||||||
|
break;
|
||||||
|
case 8: // exit
|
||||||
|
engineState = PGS_Quit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menuErrorMsg[0] = 0; // clear error msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void menu_loop(void)
|
||||||
|
{
|
||||||
|
int pal[2];
|
||||||
|
|
||||||
|
// switch to 8bpp
|
||||||
|
gp2x_video_changemode(8);
|
||||||
|
gp2x_video_RGB_setscaling(320, 240);
|
||||||
|
// set pal
|
||||||
|
pal[0] = 0;
|
||||||
|
pal[1] = 0x00ffffff;
|
||||||
|
gp2x_video_setpalette(pal, 2);
|
||||||
|
|
||||||
|
menu_loop_root();
|
||||||
|
|
||||||
|
menuErrorMsg[0] = 0;
|
||||||
|
}
|
16
gp2x/menu.h
Normal file
16
gp2x/menu.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// (c) Copyright 2006 notaz, All rights reserved.
|
||||||
|
// Free for non-commercial use.
|
||||||
|
|
||||||
|
// For commercial use, separate licencing terms must be obtained.
|
||||||
|
|
||||||
|
extern char menuErrorMsg[40];
|
||||||
|
|
||||||
|
void gp2x_text_out8 (int x, int y, char *texto, ...);
|
||||||
|
void gp2x_text_out15 (int x, int y, char *text);
|
||||||
|
void gp2x_text_out8_2(int x, int y, char *texto, int color);
|
||||||
|
void menu_loop(void);
|
||||||
|
|
||||||
|
#define CONFIGURABLE_KEYS \
|
||||||
|
(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_A|GP2X_B|GP2X_X|GP2X_Y| \
|
||||||
|
GP2X_START|GP2X_SELECT|GP2X_L|GP2X_R|GP2X_PUSH|GP2X_VOL_UP|GP2X_VOL_DOWN)
|
||||||
|
|
104
gp2x/mmuhack.c
Normal file
104
gp2x/mmuhack.c
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/miscdevice.h>
|
||||||
|
#include <asm/memory.h>
|
||||||
|
|
||||||
|
#define MMUHACK_MINOR 225
|
||||||
|
#define DEVICE_NAME "mmuhack"
|
||||||
|
|
||||||
|
#if __GNUC__ == 3
|
||||||
|
#include <linux/version.h>
|
||||||
|
static const char __module_kernel_version_gcc3[] __attribute__((__used__)) __attribute__((section(".modinfo"))) =
|
||||||
|
"kernel_version=" UTS_RELEASE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static ssize_t mmuhack_open(struct inode *inode, struct file *filp)
|
||||||
|
{
|
||||||
|
unsigned int *pgtable;
|
||||||
|
unsigned int *cpt;
|
||||||
|
int i, j;
|
||||||
|
int ttb;
|
||||||
|
int ret = -EFAULT;
|
||||||
|
|
||||||
|
// get the pointer to the translation table base...
|
||||||
|
asm volatile(
|
||||||
|
"stmdb sp!, {r0}\n\t"
|
||||||
|
"mrc p15, 0, r0, c2, c0, 0\n\t"
|
||||||
|
"mov %0, r0\n\t"
|
||||||
|
"ldmia sp!, {r0}\n\t": "=r"(ttb)
|
||||||
|
);
|
||||||
|
|
||||||
|
pgtable = __va(ttb);
|
||||||
|
|
||||||
|
for (i = 0; i < 4096; i ++) if ( (pgtable[i] & 3) == 1 ) {
|
||||||
|
cpt = __va(pgtable[i] & 0xfffffc00);
|
||||||
|
|
||||||
|
for (j = 0; j < 256; j ++) {/*
|
||||||
|
if ( (cpt[j] & 0xfe00000f) == 0x02000002 ) {
|
||||||
|
// set C and B bits in upper 32MB memory area...
|
||||||
|
printk("Set C&B bits %08x\n",cpt[j]);
|
||||||
|
cpt[j] |= 0xFFC;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (((cpt[j] & 0xff000000) == 0x02000000) && ((cpt[j] & 12)==0) )
|
||||||
|
{
|
||||||
|
//printk("Set C&B bits %08x\n",cpt[j]);
|
||||||
|
cpt[j] |= 0xFFC;
|
||||||
|
}
|
||||||
|
//if ((a>=0x31 && a<=0x36) && ((cpt[i] & 12)==0))
|
||||||
|
if (((cpt[j] & 0xff000000) == 0x03000000) && ((cpt[j] & 12)==0))
|
||||||
|
{
|
||||||
|
//printk("Set C&B bits %08x\n",cpt[j]);
|
||||||
|
//printf("SDL c and b bits not set, overwriting\n");
|
||||||
|
cpt[j] |= 0xFFC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// drain the write buffer and flush the tlb caches...
|
||||||
|
asm volatile(
|
||||||
|
"stmdb sp!, {r0}\n\t"
|
||||||
|
"mov r0, #0\n\t"
|
||||||
|
"mcr 15, 0, r0, cr7, cr10, 4\n\t"
|
||||||
|
"mcr 15, 0, r0, cr8, cr7, 0\n\t"
|
||||||
|
"ldmia sp!, {r0}\n\t"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
printk("MMU hack applied.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct file_operations mmuhack_fops = {
|
||||||
|
owner: THIS_MODULE,
|
||||||
|
open: mmuhack_open,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static struct miscdevice mmuhack = {
|
||||||
|
MMUHACK_MINOR, DEVICE_NAME, &mmuhack_fops
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init mmuhack_init(void)
|
||||||
|
{
|
||||||
|
misc_register(&mmuhack);
|
||||||
|
/*
|
||||||
|
printk("MMSP2 MMU Hack module.\n");
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit mmuhack_exit(void)
|
||||||
|
{
|
||||||
|
misc_deregister(&mmuhack);
|
||||||
|
/*
|
||||||
|
printk(KERN_ALERT "MMU Hack module removed.\n");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(mmuhack_init);
|
||||||
|
module_exit(mmuhack_exit);
|
4
gp2x/mmuhack.txt
Normal file
4
gp2x/mmuhack.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Squidge's MMU Hack modularized.
|
||||||
|
Original code by Squidge.
|
||||||
|
Module by Annonymous?
|
||||||
|
Slightly modified by me to suit my need.
|
18
gp2x/port_config.h
Normal file
18
gp2x/port_config.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// port specific settings
|
||||||
|
|
||||||
|
#ifndef PORT_CONFIG_H
|
||||||
|
#define PORT_CONFIG_H
|
||||||
|
|
||||||
|
#define CPU_CALL
|
||||||
|
|
||||||
|
// draw2.c
|
||||||
|
#define START_ROW 0 // which row of tiles to start rendering at?
|
||||||
|
#define END_ROW 28 // ..end
|
||||||
|
|
||||||
|
// pico.c
|
||||||
|
#define CAN_HANDLE_240_LINES 1
|
||||||
|
|
||||||
|
//#define dprintf(f,...) printf(f"\n",##__VA_ARGS__)
|
||||||
|
#define dprintf(x...)
|
||||||
|
|
||||||
|
#endif //PORT_CONFIG_H
|
8
gp2x/port_config.s
Normal file
8
gp2x/port_config.s
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
@ .equiv START_ROW, 1
|
||||||
|
@ .equiv END_ROW, 27
|
||||||
|
@ one row means 8 pixels. If above example was used, (27-1)*8=208 lines would be rendered.
|
||||||
|
.equiv START_ROW, 0
|
||||||
|
.equiv END_ROW, 28
|
||||||
|
|
||||||
|
@ this should be set to one only for GP2X port
|
||||||
|
.equiv EXTERNAL_YM2612, 1
|
45
gp2x/squidgehack.c
Normal file
45
gp2x/squidgehack.c
Normal file
|
@ -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;
|
||||||
|
}
|
7
gp2x/squidgehack.h
Normal file
7
gp2x/squidgehack.h
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef __MMUHACK__
|
||||||
|
#define __MMUHACK__
|
||||||
|
|
||||||
|
extern int mmuhack(void);
|
||||||
|
extern int mmuunhack(void);
|
||||||
|
|
||||||
|
#endif /* __MMUHACK__ */
|
37
gp2x/test.c
Normal file
37
gp2x/test.c
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "gp2x.h"
|
||||||
|
|
||||||
|
void spend_cycles(int c);
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct timeval tval; // timing
|
||||||
|
int thissec = 0, frames_done = 0;
|
||||||
|
|
||||||
|
gp2x_init();
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
gettimeofday(&tval, 0);
|
||||||
|
|
||||||
|
if(thissec != tval.tv_sec)
|
||||||
|
{
|
||||||
|
thissec = tval.tv_sec;
|
||||||
|
|
||||||
|
printf("frames_done: %i\n", frames_done);
|
||||||
|
frames_done = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//gp2x_video_wait_vsync();
|
||||||
|
//usleep(1); // sleeps a minimum of ~20ms
|
||||||
|
//gp2x_video_flip(); // can be called ~430000 times/sec
|
||||||
|
spend_cycles(1000);
|
||||||
|
frames_done++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
424
gp2x/usbjoy.c
Normal file
424
gp2x/usbjoy.c
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
241
gp2x/usbjoy.h
Normal file
241
gp2x/usbjoy.h
Normal file
|
@ -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
|
2
gp2x/version.h
Normal file
2
gp2x/version.h
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#define VERSION "0.964"
|
||||||
|
|
112
linux/940ctl_ym2612.c
Normal file
112
linux/940ctl_ym2612.c
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
91
linux/Makefile
Normal file
91
linux/Makefile
Normal file
|
@ -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 $@
|
||||||
|
|
4
linux/README
Normal file
4
linux/README
Normal file
|
@ -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.).
|
||||||
|
|
81
linux/blit.c
Normal file
81
linux/blit.c
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
|
667
linux/fakedasm.c
Normal file
667
linux/fakedasm.c
Normal file
|
@ -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]];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
384
linux/gp2x.c
Normal file
384
linux/gp2x.c
Normal file
|
@ -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(>k_thread, NULL, gtk_threadf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalize_image(guchar *pixels, gpointer data)
|
||||||
|
{
|
||||||
|
free(pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
void gp2x_init(void)
|
||||||
|
{
|
||||||
|
printf("entering init()\n"); fflush(stdout);
|
||||||
|
|
||||||
|
gp2x_screen = malloc(320*240*2 + 320*2);
|
||||||
|
|
||||||
|
// snd
|
||||||
|
mixerdev = open("/dev/mixer", O_RDWR);
|
||||||
|
if (mixerdev == -1)
|
||||||
|
printf("open(\"/dev/mixer\") failed with %i\n", errno);
|
||||||
|
|
||||||
|
gtk_initf();
|
||||||
|
|
||||||
|
gp2x_usbjoy_init();
|
||||||
|
|
||||||
|
printf("exitting init()\n"); fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_deinit(void)
|
||||||
|
{
|
||||||
|
free(gp2x_screen);
|
||||||
|
if (sounddev > 0) close(sounddev);
|
||||||
|
close(mixerdev);
|
||||||
|
gp2x_usbjoy_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* video */
|
||||||
|
void gp2x_video_flip(void)
|
||||||
|
{
|
||||||
|
GdkPixbuf *pixbuf;
|
||||||
|
unsigned char *image;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
gdk_threads_enter();
|
||||||
|
|
||||||
|
image = malloc (320*240*3);
|
||||||
|
if (image == NULL)
|
||||||
|
{
|
||||||
|
gdk_threads_leave();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_bpp == 8)
|
||||||
|
{
|
||||||
|
unsigned char *pixels = gp2x_screen;
|
||||||
|
int pix;
|
||||||
|
|
||||||
|
for (i = 0; i < 320*240; i++)
|
||||||
|
{
|
||||||
|
pix = current_pal[pixels[i]];
|
||||||
|
image[3 * i + 0] = pix >> 16;
|
||||||
|
image[3 * i + 1] = pix >> 8;
|
||||||
|
image[3 * i + 2] = pix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned short *pixels = gp2x_screen;
|
||||||
|
|
||||||
|
for (i = 0; i < 320*240; i++)
|
||||||
|
{
|
||||||
|
/* in: rrrr rggg gggb bbbb */
|
||||||
|
/* out: rrrr r000 gggg gg00 bbbb b000 */
|
||||||
|
image[3 * i + 0] = (pixels[i] >> 8) & 0xf8;
|
||||||
|
image[3 * i + 1] = (pixels[i] >> 3) & 0xfc;
|
||||||
|
image[3 * i + 2] = (pixels[i] << 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixbuf = gdk_pixbuf_new_from_data (image, GDK_COLORSPACE_RGB,
|
||||||
|
FALSE, 8, 320, 240, 320*3, finalize_image, NULL);
|
||||||
|
gtk_image_set_from_pixbuf (GTK_IMAGE (gtk_items.pixmap1), pixbuf);
|
||||||
|
g_object_unref (pixbuf);
|
||||||
|
|
||||||
|
gdk_threads_leave();
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_video_changemode(int bpp)
|
||||||
|
{
|
||||||
|
current_bpp = bpp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_video_setpalette(int *pal, int len)
|
||||||
|
{
|
||||||
|
memcpy(current_pal, pal, len*4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_video_RGB_setscaling(int W, int H)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_memcpy_all_buffers(void *data, int offset, int len)
|
||||||
|
{
|
||||||
|
memcpy((char *)gp2x_screen + offset, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gp2x_memset_all_buffers(int offset, int byte, int len)
|
||||||
|
{
|
||||||
|
memset((char *)gp2x_screen + offset, byte, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* sound */
|
||||||
|
static int s_oldrate = 0, s_oldbits = 0, s_oldstereo = 0;
|
||||||
|
|
||||||
|
void gp2x_start_sound(int rate, int bits, int stereo)
|
||||||
|
{
|
||||||
|
int frag = 0, bsize, buffers;
|
||||||
|
|
||||||
|
// if no settings change, we don't need to do anything
|
||||||
|
if (rate == s_oldrate && s_oldbits == bits && s_oldstereo == stereo) return;
|
||||||
|
|
||||||
|
if (sounddev > 0) close(sounddev);
|
||||||
|
sounddev = open("/dev/dsp", O_WRONLY|O_ASYNC);
|
||||||
|
if (sounddev == -1)
|
||||||
|
printf("open(\"/dev/dsp\") failed with %i\n", errno);
|
||||||
|
|
||||||
|
ioctl(sounddev, SNDCTL_DSP_SPEED, &rate);
|
||||||
|
ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits);
|
||||||
|
ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo);
|
||||||
|
// calculate buffer size
|
||||||
|
buffers = 16;
|
||||||
|
bsize = rate / 32;
|
||||||
|
if (rate > 22050) { bsize*=4; buffers*=2; } // 44k mode seems to be very demanding
|
||||||
|
while ((bsize>>=1)) frag++;
|
||||||
|
frag |= buffers<<16; // 16 buffers
|
||||||
|
ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag);
|
||||||
|
printf("gp2x_set_sound: %i/%ibit/%s, %i buffers of %i bytes\n",
|
||||||
|
rate, bits, stereo?"stereo":"mono", frag>>16, 1<<(frag&0xffff));
|
||||||
|
|
||||||
|
s_oldrate = rate; s_oldbits = bits; s_oldstereo = stereo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_sound_write(void *buff, int len)
|
||||||
|
{
|
||||||
|
write(sounddev, buff, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_sound_volume(int l, int r)
|
||||||
|
{
|
||||||
|
l=l<0?0:l; l=l>255?255:l; r=r<0?0:r; r=r>255?255:r;
|
||||||
|
l<<=8; l|=r;
|
||||||
|
ioctl(mixerdev, SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* joy */
|
||||||
|
unsigned long gp2x_joystick_read(int allow_usb_joy)
|
||||||
|
{
|
||||||
|
unsigned long value = current_keys;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (allow_usb_joy && num_of_joys > 0) {
|
||||||
|
// check the usb joy as well..
|
||||||
|
gp2x_usbjoy_update();
|
||||||
|
for (i = 0; i < num_of_joys; i++)
|
||||||
|
value |= gp2x_usbjoy_check(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 940 */
|
||||||
|
int crashed_940 = 0;
|
||||||
|
void Pause940(int yes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset940(int yes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* faking gp2x cpuctrl.c */
|
||||||
|
void cpuctrl_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpuctrl_deinit(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_FCLK(unsigned MHZ)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disable_940(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp2x_video_wait_vsync(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_RAM_Timings(int tRC, int tRAS, int tWR, int tMRD, int tRFC, int tRP, int tRCD)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_gamma(int g100)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* squidgehack.c */
|
||||||
|
int mmuhack(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int mmuunhack(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* misc */
|
||||||
|
void spend_cycles(int c)
|
||||||
|
{
|
||||||
|
usleep(c/*/200*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
19
linux/port_config.h
Normal file
19
linux/port_config.h
Normal file
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue