lowercasing filenames, part3

git-svn-id: file:///home/notaz/opt/svn/PicoDrive@576 be3aeb3a-fb24-0410-a615-afba39da0efa
This commit is contained in:
notaz 2008-08-28 12:36:57 +00:00
parent d158df697d
commit 1cfc5cc4ce
71 changed files with 0 additions and 0 deletions

207
pico/area.c Normal file
View file

@ -0,0 +1,207 @@
// 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_int.h"
// ym2612
#include "sound/ym2612.h"
// sn76496
extern int *sn76496_regs;
struct PicoArea { void *data; int len; char *name; };
// strange observation on Symbian OS 9.1, m600 organizer fw r3a06:
// taking an address of fread or fwrite causes "application could't be started" error
// on startup randomly depending on binary layout of executable file.
arearw *areaRead = (arearw *) 0; // fread; // read and write function pointers for
arearw *areaWrite = (arearw *) 0; // fwrite; // gzip save state ability
areaeof *areaEof = (areaeof *) 0;
areaseek *areaSeek = (areaseek *) 0;
areaclose *areaClose = (areaclose *) 0;
void (*PicoLoadStateHook)(void) = NULL;
// Scan one variable and callback
static int ScanVar(void *data,int len,char *name,void *PmovFile,int PmovAction)
{
int ret = 0;
if ((PmovAction&3)==1) ret = areaWrite(data,1,len,PmovFile);
if ((PmovAction&3)==2) ret = areaRead (data,1,len,PmovFile);
return (ret != len);
}
#define SCAN_VAR(x,y) ScanVar(&x,sizeof(x),y,PmovFile,PmovAction);
#define SCANP(x) ScanVar(&Pico.x,sizeof(Pico.x),#x,PmovFile,PmovAction);
// Pack the cpu into a common format:
PICO_INTERNAL void PicoAreaPackCpu(unsigned char *cpu, int is_sub)
{
unsigned int pc=0;
#if defined(EMU_C68K)
struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
memcpy(cpu,context->d,0x40);
pc=context->pc-context->membase;
*(unsigned int *)(cpu+0x44)=CycloneGetSr(context);
*(unsigned int *)(cpu+0x48)=context->osp;
cpu[0x4c] = context->irq;
cpu[0x4d] = context->state_flags & 1;
#elif defined(EMU_M68K)
void *oldcontext = m68ki_cpu_p;
m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
memcpy(cpu,m68ki_cpu_p->dar,0x40);
pc=m68ki_cpu_p->pc;
*(unsigned int *)(cpu+0x44)=m68k_get_reg(NULL, M68K_REG_SR);
*(unsigned int *)(cpu+0x48)=m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET];
cpu[0x4c] = CPU_INT_LEVEL>>8;
cpu[0x4d] = CPU_STOPPED;
m68k_set_context(oldcontext);
#elif defined(EMU_F68K)
M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
memcpy(cpu,context->dreg,0x40);
pc=context->pc;
*(unsigned int *)(cpu+0x44)=context->sr;
*(unsigned int *)(cpu+0x48)=context->asp;
cpu[0x4c] = context->interrupts[0];
cpu[0x4d] = (context->execinfo & FM68K_HALTED) ? 1 : 0;
#endif
*(unsigned int *)(cpu+0x40)=pc;
}
PICO_INTERNAL void PicoAreaUnpackCpu(unsigned char *cpu, int is_sub)
{
#if defined(EMU_C68K)
struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
CycloneSetSr(context, *(unsigned int *)(cpu+0x44));
context->osp=*(unsigned int *)(cpu+0x48);
memcpy(context->d,cpu,0x40);
context->membase=0;
context->pc = context->checkpc(*(unsigned int *)(cpu+0x40)); // Base pc
context->irq = cpu[0x4c];
context->state_flags = 0;
if (cpu[0x4d])
context->state_flags |= 1;
#elif defined(EMU_M68K)
void *oldcontext = m68ki_cpu_p;
m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
m68k_set_reg(M68K_REG_SR, *(unsigned int *)(cpu+0x44));
memcpy(m68ki_cpu_p->dar,cpu,0x40);
m68ki_cpu_p->pc=*(unsigned int *)(cpu+0x40);
m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET]=*(unsigned int *)(cpu+0x48);
CPU_INT_LEVEL = cpu[0x4c] << 8;
CPU_STOPPED = cpu[0x4d];
m68k_set_context(oldcontext);
#elif defined(EMU_F68K)
M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
memcpy(context->dreg,cpu,0x40);
context->pc =*(unsigned int *)(cpu+0x40);
context->sr =*(unsigned int *)(cpu+0x44);
context->asp=*(unsigned int *)(cpu+0x48);
context->interrupts[0] = cpu[0x4c];
context->execinfo &= ~FM68K_HALTED;
if (cpu[0x4d]&1) context->execinfo |= FM68K_HALTED;
#endif
}
// Scan the contents of the virtual machine's memory for saving or loading
static int PicoAreaScan(int PmovAction,unsigned int ver, void *PmovFile)
{
void *ym2612_regs;
unsigned char cpu[0x60];
unsigned char cpu_z80[0x60];
int ret;
memset(&cpu,0,sizeof(cpu));
memset(&cpu_z80,0,sizeof(cpu_z80));
ym2612_regs = YM2612GetRegs();
if (PmovAction&4)
{
Pico.m.scanline=0;
// Scan all the memory areas:
SCANP(ram) SCANP(vram) SCANP(zram) SCANP(cram) SCANP(vsram)
// Pack, scan and unpack the cpu data:
if((PmovAction&3)==1) PicoAreaPackCpu(cpu, 0);
//PicoMemInit();
SCAN_VAR(cpu,"cpu")
if((PmovAction&3)==2) PicoAreaUnpackCpu(cpu, 0);
SCAN_VAR(Pico.m ,"misc")
SCAN_VAR(Pico.video,"video")
if (PicoOpt&7) {
if((PmovAction&3)==1) z80_pack(cpu_z80);
ret = SCAN_VAR(cpu_z80,"cpu_z80")
// do not unpack if we fail to load z80 state
if((PmovAction&3)==2) {
if(ret) z80_reset();
else z80_unpack(cpu_z80);
}
}
if (PicoOpt&3)
ScanVar(sn76496_regs,28*4,"SN76496state", PmovFile, PmovAction); // regs and other stuff
if (PicoOpt&1) {
if((PmovAction&3)==1) ym2612_pack_state();
ScanVar(ym2612_regs, 0x200+4, "YM2612state", PmovFile, PmovAction); // regs + addr line
if((PmovAction&3)==2) ym2612_unpack_state(); // reload YM2612 state from it's regs
}
}
return 0;
}
// ---------------------------------------------------------------------------
// Helper code to save/load to a file handle
// Save or load the state from PmovFile:
int PmovState(int PmovAction, void *PmovFile)
{
int minimum=0;
unsigned char head[32];
if ((PicoAHW & PAHW_MCD) || carthw_chunks != NULL)
{
if (PmovAction&1) return PicoCdSaveState(PmovFile);
if (PmovAction&2) {
int ret = PicoCdLoadState(PmovFile);
if (PicoLoadStateHook) PicoLoadStateHook();
return ret;
}
}
memset(head,0,sizeof(head));
// Find out minimal compatible version:
//PicoAreaScan(PmovAction&0xc,&minimum);
minimum = 0x0021;
memcpy(head,"Pico",4);
*(unsigned int *)(head+0x8)=PicoVer;
*(unsigned int *)(head+0xc)=minimum;
// Scan header:
if (PmovAction&1) areaWrite(head,1,sizeof(head),PmovFile);
if (PmovAction&2) areaRead (head,1,sizeof(head),PmovFile);
// Scan memory areas:
PicoAreaScan(PmovAction, *(unsigned int *)(head+0x8), PmovFile);
if ((PmovAction&2) && PicoLoadStateHook) PicoLoadStateHook();
return 0;
}

732
pico/cart.c Normal file
View file

@ -0,0 +1,732 @@
// This is part of Pico Library
// (c) Copyright 2004 Dave, All rights reserved.
// (c) Copyright 2006-2007, Grazvydas "notaz" Ignotas
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include "pico_int.h"
#include "../zlib/zlib.h"
#include "../unzip/unzip.h"
#include "../unzip/unzip_stream.h"
static char *rom_exts[] = { "bin", "gen", "smd", "iso" };
void (*PicoCartUnloadHook)(void) = NULL;
void (*PicoCartLoadProgressCB)(int percent) = NULL;
void (*PicoCDLoadProgressCB)(int percent) = NULL; // handled in Pico/cd/cd_file.c
static void PicoCartDetect(void);
/* cso struct */
typedef struct _cso_struct
{
unsigned char in_buff[2*2048];
unsigned char out_buff[2048];
struct {
char magic[4];
unsigned int unused;
unsigned int total_bytes;
unsigned int total_bytes_high; // ignored here
unsigned int block_size; // 10h
unsigned char ver;
unsigned char align;
unsigned char reserved[2];
} header;
unsigned int fpos_in; // input file read pointer
unsigned int fpos_out; // pos in virtual decompressed file
int block_in_buff; // block which we have read in in_buff
int pad;
int index[0];
}
cso_struct;
static int uncompress2(void *dest, int destLen, void *source, int sourceLen)
{
z_stream stream;
int err;
stream.next_in = (Bytef*)source;
stream.avail_in = (uInt)sourceLen;
stream.next_out = dest;
stream.avail_out = (uInt)destLen;
stream.zalloc = NULL;
stream.zfree = NULL;
err = inflateInit2(&stream, -15);
if (err != Z_OK) return err;
err = inflate(&stream, Z_FINISH);
if (err != Z_STREAM_END) {
inflateEnd(&stream);
return err;
}
//*destLen = stream.total_out;
return inflateEnd(&stream);
}
pm_file *pm_open(const char *path)
{
pm_file *file = NULL;
const char *ext;
FILE *f;
if (path == NULL) return NULL;
if (strlen(path) < 5) ext = NULL; // no ext
else ext = path + strlen(path) - 3;
if (ext && strcasecmp(ext, "zip") == 0)
{
struct zipent *zipentry;
gzFile gzf = NULL;
ZIP *zipfile;
int i;
zipfile = openzip(path);
if (zipfile != NULL)
{
/* search for suitable file (right extension or large enough file) */
while ((zipentry = readzip(zipfile)) != NULL)
{
if (zipentry->uncompressed_size >= 128*1024) goto found_rom_zip;
if (strlen(zipentry->name) < 5) continue;
ext = zipentry->name+strlen(zipentry->name)-3;
for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++)
if (strcasecmp(ext, rom_exts[i]) == 0) goto found_rom_zip;
}
/* zipfile given, but nothing found suitable for us inside */
goto zip_failed;
found_rom_zip:
/* try to convert to gzip stream, so we could use standard gzio functions from zlib */
gzf = zip2gz(zipfile, zipentry);
if (gzf == NULL) goto zip_failed;
file = malloc(sizeof(*file));
if (file == NULL) goto zip_failed;
file->file = zipfile;
file->param = gzf;
file->size = zipentry->uncompressed_size;
file->type = PMT_ZIP;
return file;
zip_failed:
if (gzf) {
gzclose(gzf);
zipfile->fp = NULL; // gzclose() closed it
}
closezip(zipfile);
return NULL;
}
}
else if (ext && strcasecmp(ext, "cso") == 0)
{
cso_struct *cso = NULL, *tmp = NULL;
int size;
f = fopen(path, "rb");
if (f == NULL)
goto cso_failed;
#ifndef __EPOC32__
/* we use our own buffering */
setvbuf(f, NULL, _IONBF, 0);
#endif
cso = malloc(sizeof(*cso));
if (cso == NULL)
goto cso_failed;
if (fread(&cso->header, 1, sizeof(cso->header), f) != sizeof(cso->header))
goto cso_failed;
if (strncmp(cso->header.magic, "CISO", 4) != 0) {
elprintf(EL_STATUS, "cso: bad header");
goto cso_failed;
}
if (cso->header.block_size != 2048) {
elprintf(EL_STATUS, "cso: bad block size (%u)", cso->header.block_size);
goto cso_failed;
}
size = ((cso->header.total_bytes >> 11) + 1)*4 + sizeof(*cso);
tmp = realloc(cso, size);
if (tmp == NULL)
goto cso_failed;
cso = tmp;
elprintf(EL_STATUS, "allocated %i bytes for CSO struct", size);
size -= sizeof(*cso); // index size
if (fread(cso->index, 1, size, f) != size) {
elprintf(EL_STATUS, "cso: premature EOF");
goto cso_failed;
}
// all ok
cso->fpos_in = ftell(f);
cso->fpos_out = 0;
cso->block_in_buff = -1;
file = malloc(sizeof(*file));
if (file == NULL) goto cso_failed;
file->file = f;
file->param = cso;
file->size = cso->header.total_bytes;
file->type = PMT_CSO;
return file;
cso_failed:
if (cso != NULL) free(cso);
if (f != NULL) fclose(f);
return NULL;
}
/* not a zip, treat as uncompressed file */
f = fopen(path, "rb");
if (f == NULL) return NULL;
file = malloc(sizeof(*file));
if (file == NULL) {
fclose(f);
return NULL;
}
fseek(f, 0, SEEK_END);
file->file = f;
file->param = NULL;
file->size = ftell(f);
file->type = PMT_UNCOMPRESSED;
fseek(f, 0, SEEK_SET);
#ifndef __EPOC32__ // makes things worse on Symbian
if (file->size > 0x400000)
/* we use our own buffering */
setvbuf(f, NULL, _IONBF, 0);
#endif
return file;
}
size_t pm_read(void *ptr, size_t bytes, pm_file *stream)
{
int ret;
if (stream->type == PMT_UNCOMPRESSED)
{
ret = fread(ptr, 1, bytes, stream->file);
}
else if (stream->type == PMT_ZIP)
{
gzFile gf = stream->param;
int err;
ret = gzread(gf, ptr, bytes);
err = gzerror2(gf);
if (ret > 0 && (err == Z_DATA_ERROR || err == Z_STREAM_END))
/* we must reset stream pointer or else next seek/read fails */
gzrewind(gf);
}
else if (stream->type == PMT_CSO)
{
cso_struct *cso = stream->param;
int read_pos, read_len, out_offs, rret;
int block = cso->fpos_out >> 11;
int index = cso->index[block];
int index_end = cso->index[block+1];
unsigned char *out = ptr, *tmp_dst;
ret = 0;
while (bytes != 0)
{
out_offs = cso->fpos_out&0x7ff;
if (out_offs == 0 && bytes >= 2048)
tmp_dst = out;
else tmp_dst = cso->out_buff;
read_pos = (index&0x7fffffff) << cso->header.align;
if (index < 0) {
if (read_pos != cso->fpos_in)
fseek(stream->file, read_pos, SEEK_SET);
rret = fread(tmp_dst, 1, 2048, stream->file);
cso->fpos_in = read_pos + rret;
if (rret != 2048) break;
} else {
read_len = (((index_end&0x7fffffff) << cso->header.align) - read_pos) & 0xfff;
if (block != cso->block_in_buff)
{
if (read_pos != cso->fpos_in)
fseek(stream->file, read_pos, SEEK_SET);
rret = fread(cso->in_buff, 1, read_len, stream->file);
cso->fpos_in = read_pos + rret;
if (rret != read_len) {
elprintf(EL_STATUS, "cso: read failed @ %08x", read_pos);
break;
}
cso->block_in_buff = block;
}
rret = uncompress2(tmp_dst, 2048, cso->in_buff, read_len);
if (rret != 0) {
elprintf(EL_STATUS, "cso: uncompress failed @ %08x with %i", read_pos, rret);
break;
}
}
rret = 2048;
if (out_offs != 0 || bytes < 2048) {
//elprintf(EL_STATUS, "cso: unaligned/nonfull @ %08x, offs=%i, len=%u", cso->fpos_out, out_offs, bytes);
if (bytes < rret) rret = bytes;
if (2048 - out_offs < rret) rret = 2048 - out_offs;
memcpy(out, tmp_dst + out_offs, rret);
}
ret += rret;
out += rret;
cso->fpos_out += rret;
bytes -= rret;
block++;
index = index_end;
index_end = cso->index[block+1];
}
}
else
ret = 0;
return ret;
}
int pm_seek(pm_file *stream, long offset, int whence)
{
if (stream->type == PMT_UNCOMPRESSED)
{
fseek(stream->file, offset, whence);
return ftell(stream->file);
}
else if (stream->type == PMT_ZIP)
{
if (PicoMessage != NULL && offset > 6*1024*1024) {
long pos = gztell((gzFile) stream->param);
if (offset < pos || offset - pos > 6*1024*1024)
PicoMessage("Decompressing data...");
}
return gzseek((gzFile) stream->param, offset, whence);
}
else if (stream->type == PMT_CSO)
{
cso_struct *cso = stream->param;
switch (whence)
{
case SEEK_CUR: cso->fpos_out += offset; break;
case SEEK_SET: cso->fpos_out = offset; break;
case SEEK_END: cso->fpos_out = cso->header.total_bytes - offset; break;
}
return cso->fpos_out;
}
else
return -1;
}
int pm_close(pm_file *fp)
{
int ret = 0;
if (fp == NULL) return EOF;
if (fp->type == PMT_UNCOMPRESSED)
{
fclose(fp->file);
}
else if (fp->type == PMT_ZIP)
{
ZIP *zipfile = fp->file;
gzclose((gzFile) fp->param);
zipfile->fp = NULL; // gzclose() closed it
closezip(zipfile);
}
else if (fp->type == PMT_CSO)
{
free(fp->param);
fclose(fp->file);
}
else
ret = EOF;
free(fp);
return ret;
}
void Byteswap(unsigned char *data,int len)
{
int i=0;
if (len<2) return; // Too short
do
{
unsigned short *pd=(unsigned short *)(data+i);
int value=*pd; // Get 2 bytes
value=(value<<8)|(value>>8); // Byteswap it
*pd=(unsigned short)value; // Put 2b ytes
i+=2;
}
while (i+2<=len);
}
// Interleve a 16k block and byteswap
static int InterleveBlock(unsigned char *dest,unsigned char *src)
{
int i=0;
for (i=0;i<0x2000;i++) dest[(i<<1) ]=src[ i]; // Odd
for (i=0;i<0x2000;i++) dest[(i<<1)+1]=src[0x2000+i]; // Even
return 0;
}
// Decode a SMD file
static int DecodeSmd(unsigned char *data,int len)
{
unsigned char *temp=NULL;
int i=0;
temp=(unsigned char *)malloc(0x4000);
if (temp==NULL) return 1;
memset(temp,0,0x4000);
// Interleve each 16k block and shift down by 0x200:
for (i=0; i+0x4200<=len; i+=0x4000)
{
InterleveBlock(temp,data+0x200+i); // Interleve 16k to temporary buffer
memcpy(data+i,temp,0x4000); // Copy back in
}
free(temp);
return 0;
}
static unsigned char *cd_realloc(void *old, int filesize)
{
unsigned char *rom;
rom=realloc(old, sizeof(mcd_state));
if (rom) memset(rom+0x20000, 0, sizeof(mcd_state)-0x20000);
return rom;
}
static unsigned char *PicoCartAlloc(int filesize)
{
int alloc_size;
unsigned char *rom;
if (PicoAHW & PAHW_MCD) return cd_realloc(NULL, filesize);
alloc_size=filesize+0x7ffff;
if((filesize&0x3fff)==0x200) alloc_size-=0x200;
alloc_size&=~0x7ffff; // use alloc size of multiples of 512K, so that memhandlers could be set up more efficiently
if((filesize&0x3fff)==0x200) alloc_size+=0x200;
else if(alloc_size-filesize < 4) alloc_size+=4; // padding for out-of-bound exec protection
// Allocate space for the rom plus padding
rom=(unsigned char *)malloc(alloc_size);
if(rom) memset(rom+alloc_size-0x80000,0,0x80000);
return rom;
}
int PicoCartLoad(pm_file *f,unsigned char **prom,unsigned int *psize)
{
unsigned char *rom=NULL; int size, bytes_read;
if (f==NULL) return 1;
size=f->size;
if (size <= 0) return 1;
size=(size+3)&~3; // Round up to a multiple of 4
// Allocate space for the rom plus padding
rom=PicoCartAlloc(size);
if (rom==NULL) {
elprintf(EL_STATUS, "out of memory (wanted %i)", size);
return 2;
}
if (PicoCartLoadProgressCB != NULL)
{
// read ROM in blocks, just for fun
int ret;
unsigned char *p = rom;
bytes_read=0;
do
{
int todo = size - bytes_read;
if (todo > 256*1024) todo = 256*1024;
ret = pm_read(p,todo,f);
bytes_read += ret;
p += ret;
PicoCartLoadProgressCB(bytes_read * 100 / size);
}
while (ret > 0);
}
else
bytes_read = pm_read(rom,size,f); // Load up the rom
if (bytes_read <= 0) {
elprintf(EL_STATUS, "read failed");
free(rom);
return 3;
}
// maybe we are loading MegaCD BIOS?
if (!(PicoAHW & PAHW_MCD) && size == 0x20000 && (!strncmp((char *)rom+0x124, "BOOT", 4) ||
!strncmp((char *)rom+0x128, "BOOT", 4))) {
PicoAHW |= PAHW_MCD;
rom = cd_realloc(rom, size);
}
// Check for SMD:
if (size >= 0x4200 && (size&0x3fff)==0x200 &&
((rom[0x2280] == 'S' && rom[0x280] == 'E') || (rom[0x280] == 'S' && rom[0x2281] == 'E'))) {
DecodeSmd(rom,size); size-=0x200; // Decode and byteswap SMD
}
else Byteswap(rom,size); // Just byteswap
if (prom) *prom=rom;
if (psize) *psize=size;
return 0;
}
// Insert a cartridge:
int PicoCartInsert(unsigned char *rom,unsigned int romsize)
{
// notaz: add a 68k "jump one op back" opcode to the end of ROM.
// This will hang the emu, but will prevent nasty crashes.
// note: 4 bytes are padded to every ROM
if (rom != NULL)
*(unsigned long *)(rom+romsize) = 0xFFFE4EFA; // 4EFA FFFE byteswapped
Pico.rom=rom;
Pico.romsize=romsize;
if (SRam.data) {
free(SRam.data);
SRam.data = NULL;
}
if (PicoCartUnloadHook != NULL) {
PicoCartUnloadHook();
PicoCartUnloadHook = NULL;
}
PicoAHW &= PAHW_MCD;
PicoMemResetHooks();
PicoDmaHook = NULL;
PicoResetHook = NULL;
PicoLineHook = NULL;
PicoLoadStateHook = NULL;
carthw_chunks = NULL;
PicoMemReset();
if (!(PicoAHW & PAHW_MCD))
PicoCartDetect();
// setup correct memory map for loaded ROM
// call PicoMemReset again due to possible memmap change
switch (PicoAHW) {
default:
elprintf(EL_STATUS|EL_ANOMALY, "starting in unknown hw configuration: %x", PicoAHW);
case 0:
case PAHW_SVP: PicoMemSetup(); break;
case PAHW_MCD: PicoMemSetupCD(); break;
case PAHW_PICO: PicoMemSetupPico(); break;
}
PicoMemReset();
PicoPower();
return 0;
}
void PicoCartUnload(void)
{
if (Pico.rom != NULL) {
SekFinishIdleDet();
free(Pico.rom);
Pico.rom=NULL;
}
}
static int rom_strcmp(int rom_offset, const char *s1)
{
int i, len = strlen(s1);
const char *s_rom = (const char *)Pico.rom + rom_offset;
for (i = 0; i < len; i++)
if (s1[i] != s_rom[i^1])
return 1;
return 0;
}
static int name_cmp(const char *name)
{
return rom_strcmp(0x150, name);
}
/*
* various cart-specific things, which can't be handled by generic code
* (maybe I should start using CRC for this stuff?)
*/
static void PicoCartDetect(void)
{
int sram_size = 0, csum;
Pico.m.sram_reg = 0;
csum = PicoRead32(0x18c) & 0xffff;
if (Pico.rom[0x1B1] == 'R' && Pico.rom[0x1B0] == 'A')
{
if (Pico.rom[0x1B2] & 0x40)
{
// EEPROM
SRam.start = PicoRead32(0x1B4) & ~1; // zero address is used for clock by some games
SRam.end = PicoRead32(0x1B8);
sram_size = 0x2000;
Pico.m.sram_reg |= 4;
} else {
// normal SRAM
SRam.start = PicoRead32(0x1B4) & ~0xff;
SRam.end = PicoRead32(0x1B8) | 1;
sram_size = SRam.end - SRam.start + 1;
}
SRam.start &= ~0xff000000;
SRam.end &= ~0xff000000;
Pico.m.sram_reg |= 0x10; // SRAM was detected
}
if (sram_size <= 0)
{
// some games may have bad headers, like S&K and Sonic3
// note: majority games use 0x200000 as starting address, but there are some which
// use something else (0x300000 by HardBall '95). Luckily they have good headers.
SRam.start = 0x200000;
SRam.end = 0x203FFF;
sram_size = 0x004000;
}
// this game actually doesn't have SRAM, but some weird protection
if (rom_strcmp(0x120, "PUGGSY") == 0)
{
SRam.start = SRam.end = sram_size = 0;
}
if (sram_size)
{
SRam.data = (unsigned char *) calloc(sram_size, 1);
if (SRam.data == NULL) return;
}
SRam.changed = 0;
// set EEPROM defaults, in case it gets detected
SRam.eeprom_type = 0; // 7bit (24C01)
SRam.eeprom_abits = 3; // eeprom access must be odd addr for: bit0 ~ cl, bit1 ~ in
SRam.eeprom_bit_cl = 1;
SRam.eeprom_bit_in = 0;
SRam.eeprom_bit_out= 0;
// some known EEPROM data (thanks to EkeEke)
if (name_cmp("COLLEGE SLAM") == 0 ||
name_cmp("FRANK THOMAS BIGHURT BASEBAL") == 0)
{
SRam.eeprom_type = 3;
SRam.eeprom_abits = 2;
SRam.eeprom_bit_cl = 0;
}
else if (name_cmp("NBA JAM TOURNAMENT EDITION") == 0 ||
name_cmp("NFL QUARTERBACK CLUB") == 0)
{
SRam.eeprom_type = 2;
SRam.eeprom_abits = 2;
SRam.eeprom_bit_cl = 0;
}
else if (name_cmp("NBA JAM") == 0)
{
SRam.eeprom_type = 2;
SRam.eeprom_bit_out = 1;
SRam.eeprom_abits = 0;
}
else if (name_cmp("NHLPA HOCKEY '93") == 0 ||
name_cmp("NHLPA Hockey '93") == 0 ||
name_cmp("RINGS OF POWER") == 0)
{
SRam.start = SRam.end = 0x200000;
Pico.m.sram_reg = 0x14;
SRam.eeprom_abits = 0;
SRam.eeprom_bit_cl = 6;
SRam.eeprom_bit_in = 7;
SRam.eeprom_bit_out= 7;
}
else if ( name_cmp("MICRO MACHINES II") == 0 ||
(name_cmp(" ") == 0 && // Micro Machines {Turbo Tournament '96, Military - It's a Blast!}
(csum == 0x165e || csum == 0x168b || csum == 0xCEE0 || csum == 0x2C41)))
{
SRam.start = 0x300000;
SRam.end = 0x380001;
Pico.m.sram_reg = 0x14;
SRam.eeprom_type = 2;
SRam.eeprom_abits = 0;
SRam.eeprom_bit_cl = 1;
SRam.eeprom_bit_in = 0;
SRam.eeprom_bit_out= 7;
}
// SVP detection
else if (name_cmp("Virtua Racing") == 0 ||
name_cmp("VIRTUA RACING") == 0)
{
PicoSVPStartup();
}
// Pico
else if (rom_strcmp(0x100, "SEGA PICO") == 0 ||
rom_strcmp(0x100, "IMA IKUNOUJYUKU") == 0) // what is that supposed to mean?
{
PicoInitPico();
}
// Detect 12-in-1 mapper
else if ((name_cmp("ROBOCOP 3") == 0 && Pico.romsize == 0x200000) ||
(rom_strcmp(0x160, "FLICKY") == 0 && Pico.romsize >= 0x200000) ||
(name_cmp(" SHOVE IT!") == 0 && Pico.romsize >= 0x200000) ||
(name_cmp("MS PACMAN") == 0 && Pico.romsize >= 0x200000)) // bad dump?
{
carthw_12in1_startup();
}
// Realtec mapper
else if (Pico.romsize == 512*1024 && (
rom_strcmp(0x94, "THE EARTH DEFEND") == 0 ||
rom_strcmp(0xfe, "WISEGAME 11-03-1993") == 0 || // Funny World
rom_strcmp(0x95, "MALLET LEGEND ") == 0)) // Whac-A-Critter
{
carthw_realtec_startup();
}
// Radica mapper
else if (name_cmp("KID CHAMELEON") == 0 && Pico.romsize > 0x100000)
{
carthw_radica_startup();
}
// Some games malfunction if SRAM is not filled with 0xff
if (name_cmp("DINO DINI'S SOCCER") == 0 ||
name_cmp("MICRO MACHINES II") == 0)
{
memset(SRam.data, 0xff, sram_size);
}
// Unusual region 'code'
if (rom_strcmp(0x1f0, "EUROPE") == 0 || rom_strcmp(0x1f0, "Europe") == 0)
*(int *) (Pico.rom+0x1f0) = 0x20204520;
}

225
pico/carthw/carthw.c Normal file
View file

@ -0,0 +1,225 @@
/*
* Support for a few cart mappers.
*
* (c) Copyright 2008, Grazvydas "notaz" Ignotas
* Free for non-commercial use.
*
*
* I should better do some pointer stuff here. But as none of these bankswitch
* while the game runs, memcpy will suffice.
*/
#include "../pico_int.h"
/* 12-in-1 and 4-in-1. Assuming >= 2MB ROMs here. */
static unsigned int carthw_12in1_baddr = 0;
static carthw_state_chunk carthw_12in1_state[] =
{
{ CHUNK_CARTHW, sizeof(carthw_12in1_baddr), &carthw_12in1_baddr },
{ 0, 0, NULL }
};
static unsigned int carthw_12in1_read16(unsigned int a, int realsize)
{
// ??
elprintf(EL_UIO, "12-in-1: read [%06x] @ %06x", a, SekPc);
return 0;
}
static void carthw_12in1_write8(unsigned int a, unsigned int d, int realsize)
{
int len;
if (a < 0xA13000 || a >= 0xA13040) {
/* 4-in-1 has Real Deal Boxing, which uses serial eeprom,
* but I really doubt that pirate cart had it */
if (a != 0x200001)
elprintf(EL_ANOMALY, "12-in-1: unexpected write [%06x] %02x @ %06x", a, d, SekPc);
return;
}
carthw_12in1_baddr = a;
a &= 0x3f; a <<= 16;
len = Pico.romsize - a;
if (len <= 0) {
elprintf(EL_ANOMALY|EL_STATUS, "12-in-1: missing bank @ %06x", a);
return;
}
memcpy(Pico.rom, Pico.rom + Pico.romsize + a, len);
}
static void carthw_12in1_reset(void)
{
carthw_12in1_write8(0xA13000, 0, 0);
}
static void carthw_12in1_statef(void)
{
carthw_12in1_write8(carthw_12in1_baddr, 0, 0);
}
void carthw_12in1_startup(void)
{
void *tmp;
elprintf(EL_STATUS, "12-in-1 mapper detected");
tmp = realloc(Pico.rom, Pico.romsize * 2);
if (tmp == NULL)
{
elprintf(EL_STATUS, "OOM");
return;
}
Pico.rom = tmp;
memcpy(Pico.rom + Pico.romsize, Pico.rom, Pico.romsize);
PicoRead16Hook = carthw_12in1_read16;
PicoWrite8Hook = carthw_12in1_write8;
PicoResetHook = carthw_12in1_reset;
PicoLoadStateHook = carthw_12in1_statef;
carthw_chunks = carthw_12in1_state;
}
/* Realtec, based on TascoDLX doc
* http://www.sharemation.com/TascoDLX/REALTEC%20Cart%20Mapper%20-%20description%20v1.txt
*/
static int realtec_bank = 0x80000000, realtec_size = 0x80000000;
static int realtec_romsize = 0;
static void carthw_realtec_write8(unsigned int a, unsigned int d, int realsize)
{
int i, bank_old = realtec_bank, size_old = realtec_size;
if (a == 0x400000)
{
realtec_bank &= 0x0e0000;
realtec_bank |= 0x300000 & (d << 19);
if (realtec_bank != bank_old)
elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
}
else if (a == 0x402000)
{
realtec_size = (d << 17) & 0x3e0000;
if (realtec_size != size_old)
elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
}
else if (a == 0x404000)
{
realtec_bank &= 0x300000;
realtec_bank |= 0x0e0000 & (d << 17);
if (realtec_bank != bank_old)
elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
}
else
elprintf(EL_ANOMALY, "realtec: unexpected write [%06x] %02x @ %06x", a, d, SekPc);
if (realtec_bank >= 0 && realtec_size >= 0 &&
(realtec_bank != bank_old || realtec_size != size_old))
{
elprintf(EL_ANOMALY, "realtec: new bank %06x, size %06x", realtec_bank, realtec_size, SekPc);
if (realtec_size > realtec_romsize - realtec_bank || realtec_bank >= realtec_romsize)
{
elprintf(EL_ANOMALY, "realtec: bank too large / out of range?");
return;
}
for (i = 0; i < 0x400000; i += realtec_size)
memcpy(Pico.rom + i, Pico.rom + 0x400000 + realtec_bank, realtec_size);
}
}
static void carthw_realtec_reset(void)
{
int i;
/* map boot code */
for (i = 0; i < 0x400000; i += 0x2000)
memcpy(Pico.rom + i, Pico.rom + 0x400000 + realtec_romsize - 0x2000, 0x2000);
realtec_bank = realtec_size = 0x80000000;
}
void carthw_realtec_startup(void)
{
void *tmp;
elprintf(EL_STATUS, "Realtec mapper detected");
realtec_romsize = Pico.romsize;
Pico.romsize = 0x400000;
tmp = realloc(Pico.rom, 0x400000 + realtec_romsize);
if (tmp == NULL)
{
elprintf(EL_STATUS, "OOM");
return;
}
Pico.rom = tmp;
memcpy(Pico.rom + 0x400000, Pico.rom, realtec_romsize);
PicoWrite8Hook = carthw_realtec_write8;
PicoResetHook = carthw_realtec_reset;
}
/* Radica mapper, based on DevSter's info
* http://devster.monkeeh.com/sega/radica/
*/
static unsigned int carthw_radica_baddr = 0;
static carthw_state_chunk carthw_radica_state[] =
{
{ CHUNK_CARTHW, sizeof(carthw_radica_baddr), &carthw_radica_baddr },
{ 0, 0, NULL }
};
static unsigned int carthw_radica_read16(unsigned int a, int realsize)
{
if ((a & 0xffff80) != 0xa13000) {
elprintf(EL_UIO, "radica: r16 %06x", a);
return 0;
}
carthw_radica_baddr = a;
a = (a & 0x7e) << 15;
if (a >= Pico.romsize) {
elprintf(EL_ANOMALY|EL_STATUS, "radica: missing bank @ %06x", a);
return 0;
}
memcpy(Pico.rom, Pico.rom + Pico.romsize + a, Pico.romsize - a);
return 0;
}
static void carthw_radica_statef(void)
{
carthw_radica_read16(carthw_radica_baddr, 0);
}
static void carthw_radica_reset(void)
{
memcpy(Pico.rom, Pico.rom + Pico.romsize, Pico.romsize);
}
void carthw_radica_startup(void)
{
void *tmp;
elprintf(EL_STATUS, "Radica mapper detected");
tmp = realloc(Pico.rom, Pico.romsize * 2);
if (tmp == NULL)
{
elprintf(EL_STATUS, "OOM");
return;
}
Pico.rom = tmp;
memcpy(Pico.rom + Pico.romsize, Pico.rom, Pico.romsize);
PicoRead16Hook = carthw_radica_read16;
PicoResetHook = carthw_radica_reset;
PicoLoadStateHook = carthw_radica_statef;
carthw_chunks = carthw_radica_state;
}

23
pico/carthw/carthw.h Normal file
View file

@ -0,0 +1,23 @@
/* svp */
#include "svp/ssp16.h"
typedef struct {
unsigned char iram_rom[0x20000]; // IRAM (0-0x7ff) and program ROM (0x800-0x1ffff)
unsigned char dram[0x20000];
ssp1601_t ssp1601;
} svp_t;
extern svp_t *svp;
void PicoSVPInit(void);
void PicoSVPStartup(void);
unsigned int PicoSVPRead16(unsigned int a, int realsize);
void PicoSVPWrite8 (unsigned int a, unsigned int d, int realsize);
void PicoSVPWrite16(unsigned int a, unsigned int d, int realsize);
/* misc */
void carthw_12in1_startup(void);
void carthw_realtec_startup(void);
void carthw_radica_startup(void);

1850
pico/carthw/svp/compiler.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
#define SSP_TCACHE_SIZE (512*1024)
#define SSP_BLOCKTAB_SIZE (0x5090/2*4)
#define SSP_BLOCKTAB_IRAM_SIZE (15*0x800/2*4)
#define SSP_BLOCKTAB_ALIGN_SIZE 3808
#define SSP_DRC_SIZE (SSP_TCACHE_SIZE + SSP_BLOCKTAB_SIZE + SSP_BLOCKTAB_IRAM_SIZE + SSP_BLOCKTAB_ALIGN_SIZE)
extern unsigned int tcache[SSP_TCACHE_SIZE/4];
extern unsigned int *ssp_block_table[0x5090/2];
extern unsigned int *ssp_block_table_iram[15][0x800/2];
int ssp_drc_entry(int cycles);
void ssp_drc_next(void);
void ssp_drc_next_patch(void);
void ssp_drc_end(void);
void ssp_hle_800(void);
void ssp_hle_902(void);
void ssp_hle_07_6d6(void);
void ssp_hle_07_030(void);
void ssp_hle_07_036(void);
void ssp_hle_11_12c(void);
void ssp_hle_11_384(void);
void ssp_hle_11_38a(void);
int ssp1601_dyn_startup(void);
void ssp1601_dyn_reset(ssp1601_t *ssp);
void ssp1601_dyn_run(int cycles);

234
pico/carthw/svp/gen_arm.c Normal file
View file

@ -0,0 +1,234 @@
// Basic macros to emit ARM instructions and some utils
// (c) Copyright 2008, Grazvydas "notaz" Ignotas
// Free for non-commercial use.
#define EMIT(x) *tcache_ptr++ = x
#define A_R4M (1 << 4)
#define A_R5M (1 << 5)
#define A_R6M (1 << 6)
#define A_R7M (1 << 7)
#define A_R8M (1 << 8)
#define A_R9M (1 << 9)
#define A_R10M (1 << 10)
#define A_R11M (1 << 11)
#define A_R14M (1 << 14)
#define A_COND_AL 0xe
#define A_COND_EQ 0x0
#define A_COND_NE 0x1
#define A_COND_MI 0x4
#define A_COND_PL 0x5
#define A_COND_LE 0xd
/* addressing mode 1 */
#define A_AM1_LSL 0
#define A_AM1_LSR 1
#define A_AM1_ASR 2
#define A_AM1_ROR 3
#define A_AM1_IMM(ror2,imm8) (((ror2)<<8) | (imm8) | 0x02000000)
#define A_AM1_REG_XIMM(shift_imm,shift_op,rm) (((shift_imm)<<7) | ((shift_op)<<5) | (rm))
#define A_AM1_REG_XREG(rs,shift_op,rm) (((rs)<<8) | ((shift_op)<<5) | 0x10 | (rm))
/* data processing op */
#define A_OP_AND 0x0
#define A_OP_EOR 0x1
#define A_OP_SUB 0x2
#define A_OP_RSB 0x3
#define A_OP_ADD 0x4
#define A_OP_TST 0x8
#define A_OP_CMP 0xa
#define A_OP_ORR 0xc
#define A_OP_MOV 0xd
#define A_OP_BIC 0xe
#define EOP_C_DOP_X(cond,op,s,rn,rd,shifter_op) \
EMIT(((cond)<<28) | ((op)<< 21) | ((s)<<20) | ((rn)<<16) | ((rd)<<12) | (shifter_op))
#define EOP_C_DOP_IMM( cond,op,s,rn,rd,ror2,imm8) EOP_C_DOP_X(cond,op,s,rn,rd,A_AM1_IMM(ror2,imm8))
#define EOP_C_DOP_REG_XIMM(cond,op,s,rn,rd,shift_imm,shift_op,rm) EOP_C_DOP_X(cond,op,s,rn,rd,A_AM1_REG_XIMM(shift_imm,shift_op,rm))
#define EOP_C_DOP_REG_XREG(cond,op,s,rn,rd,rs, shift_op,rm) EOP_C_DOP_X(cond,op,s,rn,rd,A_AM1_REG_XREG(rs, shift_op,rm))
#define EOP_MOV_IMM(rd, ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_MOV,0, 0,rd,ror2,imm8)
#define EOP_ORR_IMM(rd,rn,ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_ORR,0,rn,rd,ror2,imm8)
#define EOP_ADD_IMM(rd,rn,ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_ADD,0,rn,rd,ror2,imm8)
#define EOP_BIC_IMM(rd,rn,ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_BIC,0,rn,rd,ror2,imm8)
#define EOP_AND_IMM(rd,rn,ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_AND,0,rn,rd,ror2,imm8)
#define EOP_SUB_IMM(rd,rn,ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_SUB,0,rn,rd,ror2,imm8)
#define EOP_TST_IMM( rn,ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_TST,1,rn, 0,ror2,imm8)
#define EOP_CMP_IMM( rn,ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_CMP,1,rn, 0,ror2,imm8)
#define EOP_RSB_IMM(rd,rn,ror2,imm8) EOP_C_DOP_IMM(A_COND_AL,A_OP_RSB,0,rn,rd,ror2,imm8)
#define EOP_MOV_REG(s, rd,shift_imm,shift_op,rm) EOP_C_DOP_REG_XIMM(A_COND_AL,A_OP_MOV,s, 0,rd,shift_imm,shift_op,rm)
#define EOP_ORR_REG(s,rn,rd,shift_imm,shift_op,rm) EOP_C_DOP_REG_XIMM(A_COND_AL,A_OP_ORR,s,rn,rd,shift_imm,shift_op,rm)
#define EOP_ADD_REG(s,rn,rd,shift_imm,shift_op,rm) EOP_C_DOP_REG_XIMM(A_COND_AL,A_OP_ADD,s,rn,rd,shift_imm,shift_op,rm)
#define EOP_TST_REG( rn, shift_imm,shift_op,rm) EOP_C_DOP_REG_XIMM(A_COND_AL,A_OP_TST,1,rn, 0,shift_imm,shift_op,rm)
#define EOP_MOV_REG2(s, rd,rs,shift_op,rm) EOP_C_DOP_REG_XREG(A_COND_AL,A_OP_MOV,s, 0,rd,rs,shift_op,rm)
#define EOP_ADD_REG2(s,rn,rd,rs,shift_op,rm) EOP_C_DOP_REG_XREG(A_COND_AL,A_OP_ADD,s,rn,rd,rs,shift_op,rm)
#define EOP_SUB_REG2(s,rn,rd,rs,shift_op,rm) EOP_C_DOP_REG_XREG(A_COND_AL,A_OP_SUB,s,rn,rd,rs,shift_op,rm)
#define EOP_MOV_REG_SIMPLE(rd,rm) EOP_MOV_REG(0,rd,0,A_AM1_LSL,rm)
#define EOP_MOV_REG_LSL(rd, rm,shift_imm) EOP_MOV_REG(0,rd,shift_imm,A_AM1_LSL,rm)
#define EOP_MOV_REG_LSR(rd, rm,shift_imm) EOP_MOV_REG(0,rd,shift_imm,A_AM1_LSR,rm)
#define EOP_MOV_REG_ASR(rd, rm,shift_imm) EOP_MOV_REG(0,rd,shift_imm,A_AM1_ASR,rm)
#define EOP_MOV_REG_ROR(rd, rm,shift_imm) EOP_MOV_REG(0,rd,shift_imm,A_AM1_ROR,rm)
#define EOP_ORR_REG_SIMPLE(rd,rm) EOP_ORR_REG(0,rd,rd,0,A_AM1_LSL,rm)
#define EOP_ORR_REG_LSL(rd,rn,rm,shift_imm) EOP_ORR_REG(0,rn,rd,shift_imm,A_AM1_LSL,rm)
#define EOP_ORR_REG_LSR(rd,rn,rm,shift_imm) EOP_ORR_REG(0,rn,rd,shift_imm,A_AM1_LSR,rm)
#define EOP_ORR_REG_ASR(rd,rn,rm,shift_imm) EOP_ORR_REG(0,rn,rd,shift_imm,A_AM1_ASR,rm)
#define EOP_ORR_REG_ROR(rd,rn,rm,shift_imm) EOP_ORR_REG(0,rn,rd,shift_imm,A_AM1_ROR,rm)
#define EOP_ADD_REG_SIMPLE(rd,rm) EOP_ADD_REG(0,rd,rd,0,A_AM1_LSL,rm)
#define EOP_ADD_REG_LSL(rd,rn,rm,shift_imm) EOP_ADD_REG(0,rn,rd,shift_imm,A_AM1_LSL,rm)
#define EOP_ADD_REG_LSR(rd,rn,rm,shift_imm) EOP_ADD_REG(0,rn,rd,shift_imm,A_AM1_LSR,rm)
#define EOP_TST_REG_SIMPLE(rn,rm) EOP_TST_REG( rn, 0,A_AM1_LSL,rm)
#define EOP_MOV_REG2_LSL(rd, rm,rs) EOP_MOV_REG2(0, rd,rs,A_AM1_LSL,rm)
#define EOP_MOV_REG2_ROR(rd, rm,rs) EOP_MOV_REG2(0, rd,rs,A_AM1_ROR,rm)
#define EOP_ADD_REG2_LSL(rd,rn,rm,rs) EOP_ADD_REG2(0,rn,rd,rs,A_AM1_LSL,rm)
#define EOP_SUB_REG2_LSL(rd,rn,rm,rs) EOP_SUB_REG2(0,rn,rd,rs,A_AM1_LSL,rm)
/* addressing mode 2 */
#define EOP_C_AM2_IMM(cond,u,b,l,rn,rd,offset_12) \
EMIT(((cond)<<28) | 0x05000000 | ((u)<<23) | ((b)<<22) | ((l)<<20) | ((rn)<<16) | ((rd)<<12) | (offset_12))
/* addressing mode 3 */
#define EOP_C_AM3(cond,u,r,l,rn,rd,s,h,immed_reg) \
EMIT(((cond)<<28) | 0x01000090 | ((u)<<23) | ((r)<<22) | ((l)<<20) | ((rn)<<16) | ((rd)<<12) | \
((s)<<6) | ((h)<<5) | (immed_reg))
#define EOP_C_AM3_IMM(cond,u,l,rn,rd,s,h,offset_8) EOP_C_AM3(cond,u,1,l,rn,rd,s,h,(((offset_8)&0xf0)<<4)|((offset_8)&0xf))
#define EOP_C_AM3_REG(cond,u,l,rn,rd,s,h,rm) EOP_C_AM3(cond,u,0,l,rn,rd,s,h,rm)
/* ldr and str */
#define EOP_LDR_IMM( rd,rn,offset_12) EOP_C_AM2_IMM(A_COND_AL,1,0,1,rn,rd,offset_12)
#define EOP_LDR_NEGIMM(rd,rn,offset_12) EOP_C_AM2_IMM(A_COND_AL,0,0,1,rn,rd,offset_12)
#define EOP_LDR_SIMPLE(rd,rn) EOP_C_AM2_IMM(A_COND_AL,1,0,1,rn,rd,0)
#define EOP_STR_IMM( rd,rn,offset_12) EOP_C_AM2_IMM(A_COND_AL,1,0,0,rn,rd,offset_12)
#define EOP_STR_SIMPLE(rd,rn) EOP_C_AM2_IMM(A_COND_AL,1,0,0,rn,rd,0)
#define EOP_LDRH_IMM( rd,rn,offset_8) EOP_C_AM3_IMM(A_COND_AL,1,1,rn,rd,0,1,offset_8)
#define EOP_LDRH_SIMPLE(rd,rn) EOP_C_AM3_IMM(A_COND_AL,1,1,rn,rd,0,1,0)
#define EOP_LDRH_REG( rd,rn,rm) EOP_C_AM3_REG(A_COND_AL,1,1,rn,rd,0,1,rm)
#define EOP_STRH_IMM( rd,rn,offset_8) EOP_C_AM3_IMM(A_COND_AL,1,0,rn,rd,0,1,offset_8)
#define EOP_STRH_SIMPLE(rd,rn) EOP_C_AM3_IMM(A_COND_AL,1,0,rn,rd,0,1,0)
#define EOP_STRH_REG( rd,rn,rm) EOP_C_AM3_REG(A_COND_AL,1,0,rn,rd,0,1,rm)
/* ldm and stm */
#define EOP_XXM(cond,p,u,s,w,l,rn,list) \
EMIT(((cond)<<28) | (1<<27) | ((p)<<24) | ((u)<<23) | ((s)<<22) | ((w)<<21) | ((l)<<20) | ((rn)<<16) | (list))
#define EOP_STMFD_ST(list) EOP_XXM(A_COND_AL,1,0,0,1,0,13,list)
#define EOP_LDMFD_ST(list) EOP_XXM(A_COND_AL,0,1,0,1,1,13,list)
/* branches */
#define EOP_C_BX(cond,rm) \
EMIT(((cond)<<28) | 0x012fff10 | (rm))
#define EOP_BX(rm) EOP_C_BX(A_COND_AL,rm)
#define EOP_C_B(cond,l,signed_immed_24) \
EMIT(((cond)<<28) | 0x0a000000 | ((l)<<24) | (signed_immed_24))
#define EOP_B( signed_immed_24) EOP_C_B(A_COND_AL,0,signed_immed_24)
#define EOP_BL(signed_immed_24) EOP_C_B(A_COND_AL,1,signed_immed_24)
/* misc */
#define EOP_C_MUL(cond,s,rd,rs,rm) \
EMIT(((cond)<<28) | ((s)<<20) | ((rd)<<16) | ((rs)<<8) | 0x90 | (rm))
#define EOP_MUL(rd,rm,rs) EOP_C_MUL(A_COND_AL,0,rd,rs,rm) // note: rd != rm
#define EOP_C_MRS(cond,rd) \
EMIT(((cond)<<28) | 0x010f0000 | ((rd)<<12))
#define EOP_C_MSR_IMM(cond,ror2,imm) \
EMIT(((cond)<<28) | 0x0328f000 | ((ror2)<<8) | (imm)) // cpsr_f
#define EOP_C_MSR_REG(cond,rm) \
EMIT(((cond)<<28) | 0x0128f000 | (rm)) // cpsr_f
#define EOP_MRS(rd) EOP_C_MRS(A_COND_AL,rd)
#define EOP_MSR_IMM(ror2,imm) EOP_C_MSR_IMM(A_COND_AL,ror2,imm)
#define EOP_MSR_REG(rm) EOP_C_MSR_REG(A_COND_AL,rm)
static void emit_mov_const(int cond, int d, unsigned int val)
{
int need_or = 0;
if (val & 0xff000000) {
EOP_C_DOP_IMM(cond, A_OP_MOV, 0, 0, d, 8/2, (val>>24)&0xff);
need_or = 1;
}
if (val & 0x00ff0000) {
EOP_C_DOP_IMM(cond, need_or ? A_OP_ORR : A_OP_MOV, 0, need_or ? d : 0, d, 16/2, (val>>16)&0xff);
need_or = 1;
}
if (val & 0x0000ff00) {
EOP_C_DOP_IMM(cond, need_or ? A_OP_ORR : A_OP_MOV, 0, need_or ? d : 0, d, 24/2, (val>>8)&0xff);
need_or = 1;
}
if ((val &0x000000ff) || !need_or)
EOP_C_DOP_IMM(cond, need_or ? A_OP_ORR : A_OP_MOV, 0, need_or ? d : 0, d, 0, val&0xff);
}
static int is_offset_24(int val)
{
if (val >= (int)0xff000000 && val <= 0x00ffffff) return 1;
return 0;
}
static int emit_xbranch(int cond, void *target, int is_call)
{
int val = (unsigned int *)target - tcache_ptr - 2;
int direct = is_offset_24(val);
u32 *start_ptr = tcache_ptr;
if (direct)
{
EOP_C_B(cond,is_call,val & 0xffffff); // b, bl target
}
else
{
#ifdef __EPOC32__
// elprintf(EL_SVP, "emitting indirect jmp %08x->%08x", tcache_ptr, target);
if (is_call)
EOP_ADD_IMM(14,15,0,8); // add lr,pc,#8
EOP_C_AM2_IMM(cond,1,0,1,15,15,0); // ldrcc pc,[pc]
EOP_MOV_REG_SIMPLE(15,15); // mov pc, pc
EMIT((u32)target);
#else
// should never happen
elprintf(EL_STATUS|EL_SVP|EL_ANOMALY, "indirect jmp %08x->%08x", target, tcache_ptr);
exit(1);
#endif
}
return tcache_ptr - start_ptr;
}
static int emit_call(int cond, void *target)
{
return emit_xbranch(cond, target, 1);
}
static int emit_jump(int cond, void *target)
{
return emit_xbranch(cond, target, 0);
}
static void handle_caches(void)
{
#ifdef ARM
extern void cache_flush_d_inval_i(const void *start_addr, const void *end_addr);
cache_flush_d_inval_i(tcache, tcache_ptr);
#endif
}

View file

@ -0,0 +1,67 @@
vscroll: 1 (0); 209 (26) - alternates every 4 frames
vram range for patterns: 0000-999f (low scr 0000-395f,72e0-999f; high 3980-999f)
name table address: c000
seen DMAs (in order): [300002-3026c3]->[0020-26e1] len 4961
[3026c2-303943]->[26e0-3961] len 2369
[303942-306003]->[72e0-99a1] len 4961
---
[306002-3086c3]->[3980-6041] len 4961
[3086c2-309943]->[6040-72c1] len 2369
[309942-30c003]->[72e0-99a2] len 4961
tile arrangement:
000: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
001: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
002: 001 003 005 007 009 00b 00d 00f 011 013 015 017 019 01b 01d 01f 021 023 025 027 029 02b 02d 02f 031 033 035 037 039 03b 03d 03f
003: 002 004 006 008 00a 00c 00e 010 012 014 016 018 01a 01c 01e 020 022 024 026 028 02a 02c 02e 030 032 034 036 038 03a 03c 03e 040
004: 041 043 045 047 049 04b 04d 04f 051 053 055 057 059 05b 05d 05f 061 063 065 067 069 06b 06d 06f 071 073 075 077 079 07b 07d 07f
005: 042 044 046 048 04a 04c 04e 050 052 054 056 058 05a 05c 05e 060 062 064 066 068 06a 06c 06e 070 072 074 076 078 07a 07c 07e 080
006: 081 083 085 087 089 08b 08d 08f 091 093 095 097 099 09b 09d 09f 0a1 0a3 0a5 0a7 0a9 0ab 0ad 0af 0b1 0b3 0b5 0b7 0b9 0bb 0bd 0bf
007: 082 084 086 088 08a 08c 08e 090 092 094 096 098 09a 09c 09e 0a0 0a2 0a4 0a6 0a8 0aa 0ac 0ae 0b0 0b2 0b4 0b6 0b8 0ba 0bc 0be 0c0
008: 0c1 0c3 0c5 0c7 0c9 0cb 0cd 0cf 0d1 0d3 0d5 0d7 0d9 0db 0dd 0df 0e1 0e3 0e5 0e7 0e9 0eb 0ed 0ef 0f1 0f3 0f5 0f7 0f9 0fb 0fd 0ff
009: 0c2 0c4 0c6 0c8 0ca 0cc 0ce 0d0 0d2 0d4 0d6 0d8 0da 0dc 0de 0e0 0e2 0e4 0e6 0e8 0ea 0ec 0ee 0f0 0f2 0f4 0f6 0f8 0fa 0fc 0fe 100
010: 101 103 105 107 109 10b 10d 10f 111 113 115 117 119 11b 11d 11f 121 123 125 127 129 12b 12d 12f 131 133 135 137 139 13b 13d 13f
011: 102 104 106 108 10a 10c 10e 110 112 114 116 118 11a 11c 11e 120 122 124 126 128 12a 12c 12e 130 132 134 136 138 13a 13c 13e 140
012: 141 143 145 147 149 14b 14d 14f 151 153 155 157 159 15b 15d 15f 161 163 165 167 169 16b 16d 16f 171 173 175 177 179 17b 17d 17f
013: 142 144 146 148 14a 14c 14e 150 152 154 156 158 15a 15c 15e 160 162 164 166 168 16a 16c 16e 170 172 174 176 178 17a 17c 17e 180
014: 181 183 185 187 189 18b 18d 18f 191 193 195 197 199 19b 19d 19f 1a1 1a3 1a5 1a7 1a9 1ab 1ad 1af 1b1 1b3 1b5 1b7 1b9 1bb 1bd 1bf
015: 182 184 186 188 18a 18c 18e 190 192 194 196 198 19a 19c 19e 1a0 1a2 1a4 1a6 1a8 1aa 1ac 1ae 1b0 1b2 1b4 1b6 1b8 1ba 1bc 1be 1c0
016: 1c1 1c3 1c5 1c7 1c9 397 399 39b 39d 39f 3a1 3a3 3a5 3a7 3a9 3ab 3ad 3af 3b1 3b3 3b5 3b7 3b9 3bb 3bd 3bf 3c1 3c3 3c5 3c7 3c9 3cb
017: 1c2 1c4 1c6 1c8 1ca 398 39a 39c 39e 3a0 3a2 3a4 3a6 3a8 3aa 3ac 3ae 3b0 3b2 3b4 3b6 3b8 3ba 3bc 3be 3c0 3c2 3c4 3c6 3c8 3ca 3cc
018: 3cd 3cf 3d1 3d3 3d5 3d7 3d9 3db 3dd 3df 3e1 3e3 3e5 3e7 3e9 3eb 3ed 3ef 3f1 3f3 3f5 3f7 3f9 3fb 3fd 3ff 401 403 405 407 409 40b
019: 3ce 3d0 3d2 3d4 3d6 3d8 3da 3dc 3de 3e0 3e2 3e4 3e6 3e8 3ea 3ec 3ee 3f0 3f2 3f4 3f6 3f8 3fa 3fc 3fe 400 402 404 406 408 40a 40c
020: 40d 40f 411 413 415 417 419 41b 41d 41f 421 423 425 427 429 42b 42d 42f 431 433 435 437 439 43b 43d 43f 441 443 445 447 449 44b
021: 40e 410 412 414 416 418 41a 41c 41e 420 422 424 426 428 42a 42c 42e 430 432 434 436 438 43a 43c 43e 440 442 444 446 448 44a 44c
022: 44d 44f 451 453 455 457 459 45b 45d 45f 461 463 465 467 469 46b 46d 46f 471 473 475 477 479 47b 47d 47f 481 483 485 487 489 48b
023: 44e 450 452 454 456 458 45a 45c 45e 460 462 464 466 468 46a 46c 46e 470 472 474 476 478 47a 47c 47e 480 482 484 486 488 48a 48c
024: 48d 48f 491 493 495 497 499 49b 49d 49f 4a1 4a3 4a5 4a7 4a9 4ab 4ad 4af 4b1 4b3 4b5 4b7 4b9 4bb 4bd 4bf 4c1 4c3 4c5 4c7 4c9 4cb
025: 48e 490 492 494 496 498 49a 49c 49e 4a0 4a2 4a4 4a6 4a8 4aa 4ac 4ae 4b0 4b2 4b4 4b6 4b8 4ba 4bc 4be 4c0 4c2 4c4 4c6 4c8 4ca 4cc
026: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
027: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
028: 1cc 1ce 1d0 1d2 1d4 1d6 1d8 1da 1dc 1de 1e0 1e2 1e4 1e6 1e8 1ea 1ec 1ee 1f0 1f2 1f4 1f6 1f8 1fa 1fc 1fe 200 202 204 206 208 20a
029: 1cd 1cf 1d1 1d3 1d5 1d7 1d9 1db 1dd 1df 1e1 1e3 1e5 1e7 1e9 1eb 1ed 1ef 1f1 1f3 1f5 1f7 1f9 1fb 1fd 1ff 201 203 205 207 209 20b
030: 20c 20e 210 212 214 216 218 21a 21c 21e 220 222 224 226 228 22a 22c 22e 230 232 234 236 238 23a 23c 23e 240 242 244 246 248 24a
031: 20d 20f 211 213 215 217 219 21b 21d 21f 221 223 225 227 229 22b 22d 22f 231 233 235 237 239 23b 23d 23f 241 243 245 247 249 24b
032: 24c 24e 250 252 254 256 258 25a 25c 25e 260 262 264 266 268 26a 26c 26e 270 272 274 276 278 27a 27c 27e 280 282 284 286 288 28a
033: 24d 24f 251 253 255 257 259 25b 25d 25f 261 263 265 267 269 26b 26d 26f 271 273 275 277 279 27b 27d 27f 281 283 285 287 289 28b
034: 28c 28e 290 292 294 296 298 29a 29c 29e 2a0 2a2 2a4 2a6 2a8 2aa 2ac 2ae 2b0 2b2 2b4 2b6 2b8 2ba 2bc 2be 2c0 2c2 2c4 2c6 2c8 2ca
035: 28d 28f 291 293 295 297 299 29b 29d 29f 2a1 2a3 2a5 2a7 2a9 2ab 2ad 2af 2b1 2b3 2b5 2b7 2b9 2bb 2bd 2bf 2c1 2c3 2c5 2c7 2c9 2cb
036: 2cc 2ce 2d0 2d2 2d4 2d6 2d8 2da 2dc 2de 2e0 2e2 2e4 2e6 2e8 2ea 2ec 2ee 2f0 2f2 2f4 2f6 2f8 2fa 2fc 2fe 300 302 304 306 308 30a
037: 2cd 2cf 2d1 2d3 2d5 2d7 2d9 2db 2dd 2df 2e1 2e3 2e5 2e7 2e9 2eb 2ed 2ef 2f1 2f3 2f5 2f7 2f9 2fb 2fd 2ff 301 303 305 307 309 30b
038: 30c 30e 310 312 314 316 318 31a 31c 31e 320 322 324 326 328 32a 32c 32e 330 332 334 336 338 33a 33c 33e 340 342 344 346 348 34a
039: 30d 30f 311 313 315 317 319 31b 31d 31f 321 323 325 327 329 32b 32d 32f 331 333 335 337 339 33b 33d 33f 341 343 345 347 349 34b
040: 34c 34e 350 352 354 356 358 35a 35c 35e 360 362 364 366 368 36a 36c 36e 370 372 374 376 378 37a 37c 37e 380 382 384 386 388 38a
041: 34d 34f 351 353 355 357 359 35b 35d 35f 361 363 365 367 369 36b 36d 36f 371 373 375 377 379 37b 37d 37f 381 383 385 387 389 38b
042: 38c 38e 390 392 394 397 399 39b 39d 39f 3a1 3a3 3a5 3a7 3a9 3ab 3ad 3af 3b1 3b3 3b5 3b7 3b9 3bb 3bd 3bf 3c1 3c3 3c5 3c7 3c9 3cb
043: 38d 38f 391 393 395 398 39a 39c 39e 3a0 3a2 3a4 3a6 3a8 3aa 3ac 3ae 3b0 3b2 3b4 3b6 3b8 3ba 3bc 3be 3c0 3c2 3c4 3c6 3c8 3ca 3cc
044: 3cd 3cf 3d1 3d3 3d5 3d7 3d9 3db 3dd 3df 3e1 3e3 3e5 3e7 3e9 3eb 3ed 3ef 3f1 3f3 3f5 3f7 3f9 3fb 3fd 3ff 401 403 405 407 409 40b
045: 3ce 3d0 3d2 3d4 3d6 3d8 3da 3dc 3de 3e0 3e2 3e4 3e6 3e8 3ea 3ec 3ee 3f0 3f2 3f4 3f6 3f8 3fa 3fc 3fe 400 402 404 406 408 40a 40c
046: 40d 40f 411 413 415 417 419 41b 41d 41f 421 423 425 427 429 42b 42d 42f 431 433 435 437 439 43b 43d 43f 441 443 445 447 449 44b
047: 40e 410 412 414 416 418 41a 41c 41e 420 422 424 426 428 42a 42c 42e 430 432 434 436 438 43a 43c 43e 440 442 444 446 448 44a 44c
048: 44d 44f 451 453 455 457 459 45b 45d 45f 461 463 465 467 469 46b 46d 46f 471 473 475 477 479 47b 47d 47f 481 483 485 487 489 48b
049: 44e 450 452 454 456 458 45a 45c 45e 460 462 464 466 468 46a 46c 46e 470 472 474 476 478 47a 47c 47e 480 482 484 486 488 48a 48c
050: 48d 48f 491 493 495 497 499 49b 49d 49f 4a1 4a3 4a5 4a7 4a9 4ab 4ad 4af 4b1 4b3 4b5 4b7 4b9 4bb 4bd 4bf 4c1 4c3 4c5 4c7 4c9 4cb
051: 48e 490 492 494 496 498 49a 49c 49e 4a0 4a2 4a4 4a6 4a8 4aa 4ac 4ae 4b0 4b2 4b4 4b6 4b8 4ba 4bc 4be 4c0 4c2 4c4 4c6 4c8 4ca 4cc
052: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
053: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000

129
pico/carthw/svp/memory.c Normal file
View file

@ -0,0 +1,129 @@
// The SVP chip emulator, mem I/O stuff
// (c) Copyright 2008, Grazvydas "notaz" Ignotas
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include "../../pico_int.h"
#ifndef UTYPES_DEFINED
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define UTYPES_DEFINED
#endif
#define CLEAR_DETECT(pc_start,pc_end,text) \
if (d == 0 && SekPc >= pc_start && SekPc < pc_end) \
{ \
if (!clearing_ram) \
elprintf(EL_SVP, text); \
clearing_ram = 1; \
return; \
}
unsigned int PicoSVPRead16(unsigned int a, int realsize)
{
unsigned int d = 0;
static int a15004_looping = 0;
// dram: 300000-31ffff
if ((a & 0xfe0000) == 0x300000)
d = *(u16 *)(svp->dram + (a&0x1fffe));
// "cell arrange" 1: 390000-39ffff
else if ((a & 0xff0000) == 0x390000) {
// this is rewritten 68k code
unsigned int a1 = a >> 1;
a1 = (a1 & 0x7001) | ((a1 & 0x3e) << 6) | ((a1 & 0xfc0) >> 5);
d = ((u16 *)svp->dram)[a1];
}
// "cell arrange" 2: 3a0000-3affff
else if ((a & 0xff0000) == 0x3a0000) {
// this is rewritten 68k code
unsigned int a1 = a >> 1;
a1 = (a1 & 0x7801) | ((a1 & 0x1e) << 6) | ((a1 & 0x7e0) >> 4);
d = ((u16 *)svp->dram)[a1];
}
// regs
else if ((a & 0xfffff0) == 0xa15000) {
switch (a & 0xf) {
case 0:
case 2:
d = svp->ssp1601.gr[SSP_XST].h;
break;
case 4:
d = svp->ssp1601.gr[SSP_PM0].h;
svp->ssp1601.gr[SSP_PM0].h &= ~1;
if (d&1) a15004_looping = 0;
break;
}
}
else
elprintf(EL_UIO|EL_SVP|EL_ANOMALY, "SVP FIXME: unhandled r%i: [%06x] %04x @%06x", realsize, a&0xffffff, d, SekPc);
if (!a15004_looping)
elprintf(EL_SVP, "SVP r%i: [%06x] %04x @%06x", realsize, a&0xffffff, d, SekPc);
if (a == 0xa15004 && !(d&1)) {
if (!a15004_looping)
elprintf(EL_SVP, "SVP det TIGHT loop: a15004");
a15004_looping = 1;
}
else a15004_looping = 0;
//if (a == 0x30fe02 && d == 0)
// elprintf(EL_ANOMALY, "SVP lag?");
return d;
}
void PicoSVPWrite8(unsigned int a, unsigned int d, int realsize)
{
elprintf(EL_UIO|EL_SVP|EL_ANOMALY, "!!! SVP w%i: [%06x], %08x @%06x", realsize, a&0xffffff, d, SekPc);
}
void PicoSVPWrite16(unsigned int a, unsigned int d, int realsize)
{
static int clearing_ram = 0;
// DRAM
if ((a & 0xfe0000) == 0x300000)
*(u16 *)(svp->dram + (a&0x1fffe)) = d;
// regs
else if ((a & 0xfffff0) == 0xa15000) {
if (a == 0xa15000 || a == 0xa15002) {
// just guessing here
svp->ssp1601.gr[SSP_XST].h = d;
svp->ssp1601.gr[SSP_PM0].h |= 2;
svp->ssp1601.emu_status &= ~SSP_WAIT_PM0;
}
//else if (a == 0xa15006) svp->ssp1601.gr[SSP_PM0].h = d | (d << 1);
// 0xa15006 probably has 'halt'
}
else
elprintf(EL_UIO|EL_SVP|EL_ANOMALY, "SVP FIXME: unhandled w%i: [%06x] %04x @%06x", realsize, a&0xffffff, d, SekPc);
if (a == 0x30fe06 && d != 0)
svp->ssp1601.emu_status &= ~SSP_WAIT_30FE06;
if (a == 0x30fe08 && d != 0)
svp->ssp1601.emu_status &= ~SSP_WAIT_30FE08;
// debug: detect RAM clears..
CLEAR_DETECT(0x0221dc, 0x0221f0, "SVP RAM CLEAR (full) @ 0221C2");
CLEAR_DETECT(0x02204c, 0x022068, "SVP RAM CLEAR 300000-31ffbf (1) @ 022032");
CLEAR_DETECT(0x021900, 0x021ff0, "SVP RAM CLEAR 300000-305fff");
CLEAR_DETECT(0x0220b0, 0x0220cc, "SVP RAM CLEAR 300000-31ffbf (2) @ 022096");
clearing_ram = 0;
elprintf(EL_SVP, "SVP w%i: [%06x] %04x @%06x", realsize, a&0xffffff, d, SekPc);
}

1224
pico/carthw/svp/ssp16.c Normal file

File diff suppressed because it is too large Load diff

72
pico/carthw/svp/ssp16.h Normal file
View file

@ -0,0 +1,72 @@
// basic, incomplete SSP160x (SSP1601?) interpreter
// (c) Copyright 2008, Grazvydas "notaz" Ignotas
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
// register names
enum {
SSP_GR0, SSP_X, SSP_Y, SSP_A,
SSP_ST, SSP_STACK, SSP_PC, SSP_P,
SSP_PM0, SSP_PM1, SSP_PM2, SSP_XST,
SSP_PM4, SSP_gr13, SSP_PMC, SSP_AL
};
typedef union
{
unsigned int v;
struct {
unsigned short l;
unsigned short h;
};
} ssp_reg_t;
typedef struct
{
union {
unsigned short RAM[256*2]; // 000 2 internal RAM banks
struct {
unsigned short RAM0[256];
unsigned short RAM1[256];
};
};
ssp_reg_t gr[16]; // 400 general registers
union {
unsigned char r[8]; // 440 BANK pointers
struct {
unsigned char r0[4];
unsigned char r1[4];
};
};
unsigned short stack[6]; // 448
unsigned int pmac_read[6]; // 454 read modes/addrs for PM0-PM5
unsigned int pmac_write[6]; // 46c write ...
//
#define SSP_PMC_HAVE_ADDR 0x0001 // address written to PMAC, waiting for mode
#define SSP_PMC_SET 0x0002 // PMAC is set
#define SSP_WAIT_PM0 0x2000 // bit1 in PM0
#define SSP_WAIT_30FE06 0x4000 // ssp tight loops on 30FE06 to become non-zero
#define SSP_WAIT_30FE08 0x8000 // same for 30FE06
#define SSP_WAIT_MASK 0xe000
unsigned int emu_status; // 484
/* used by recompiler only: */
struct {
unsigned int ptr_rom; // 488
unsigned int ptr_iram_rom; // 48c
unsigned int ptr_dram; // 490
unsigned int iram_dirty; // 494
unsigned int iram_context; // 498
unsigned int ptr_btable; // 49c
unsigned int ptr_btable_iram; // 4a0
unsigned int tmp0; // 4a4
unsigned int tmp1; // 4a8
unsigned int tmp2; // 4ac
} drc;
} ssp1601_t;
void ssp1601_reset(ssp1601_t *ssp);
void ssp1601_run(int cycles);

640
pico/carthw/svp/stub_arm.S Normal file
View file

@ -0,0 +1,640 @@
@ vim:filetype=armasm
@ Compiler helper functions and some SVP HLE code
@ (c) Copyright 2008, Grazvydas "notaz" Ignotas
@ Free for non-commercial use.
.if 0
#include "compiler.h"
.endif
.global tcache
.global ssp_block_table
.global ssp_block_table_iram
.global ssp_drc_entry
.global ssp_drc_next
.global ssp_drc_next_patch
.global ssp_drc_end
.global ssp_hle_800
.global ssp_hle_902
.global ssp_hle_07_030
.global ssp_hle_07_036
.global ssp_hle_07_6d6
.global ssp_hle_11_12c
.global ssp_hle_11_384
.global ssp_hle_11_38a
@ translation cache buffer + pointer table
.data
.align 12 @ 4096
@.size tcache, SSP_TCACHE_SIZE
@.size ssp_block_table, SSP_BLOCKTAB_SIZE
@.size ssp_block_table_iram, SSP_BLOCKTAB_IRAM_SIZE
tcache:
.space SSP_TCACHE_SIZE
ssp_block_table:
.space SSP_BLOCKTAB_SIZE
ssp_block_table_iram:
.space SSP_BLOCKTAB_IRAM_SIZE
.space SSP_BLOCKTAB_ALIGN_SIZE
.text
.align 2
@ SSP_GR0, SSP_X, SSP_Y, SSP_A,
@ SSP_ST, SSP_STACK, SSP_PC, SSP_P,
@ SSP_PM0, SSP_PM1, SSP_PM2, SSP_XST,
@ SSP_PM4, SSP_gr13, SSP_PMC, SSP_AL
@ register map:
@ r4: XXYY
@ r5: A
@ r6: STACK and emu flags: sss0 * .uu. .lll NZCV (NZCV is PSR bits from ARM)
@ r7: SSP context
@ r8: r0-r2 (.210)
@ r9: r4-r6 (.654)
@ r10: P
@ r11: cycles
@ r12: tmp
#define SSP_OFFS_GR 0x400
#define SSP_PC 6
#define SSP_P 7
#define SSP_PM0 8
#define SSP_PMC 14
#define SSP_OFFS_PM_WRITE 0x46c // pmac_write[]
#define SSP_OFFS_EMUSTAT 0x484 // emu_status
#define SSP_OFFS_IRAM_ROM 0x48c // ptr_iram_rom
#define SSP_OFFS_DRAM 0x490 // ptr_dram
#define SSP_OFFS_IRAM_DIRTY 0x494
#define SSP_OFFS_IRAM_CTX 0x498 // iram_context
#define SSP_OFFS_BLTAB 0x49c // block_table
#define SSP_OFFS_BLTAB_IRAM 0x4a0
#define SSP_OFFS_TMP0 0x4a4 // for entry PC
#define SSP_OFFS_TMP1 0x4a8
#define SSP_OFFS_TMP2 0x4ac
#define SSP_WAIT_PM0 0x2000
.macro ssp_drc_do_next patch_jump=0
.if \patch_jump
str lr, [r7, #SSP_OFFS_TMP2] @ jump instr. (actually call) address + 4
.endif
mov r0, r0, lsl #16
mov r0, r0, lsr #16
str r0, [r7, #SSP_OFFS_TMP0]
cmp r0, #0x400
blt 0f @ ssp_de_iram
ldr r2, [r7, #SSP_OFFS_BLTAB]
ldr r2, [r2, r0, lsl #2]
tst r2, r2
.if \patch_jump
bne ssp_drc_do_patch
.else
bxne r2
.endif
bl ssp_translate_block
mov r2, r0
ldr r0, [r7, #SSP_OFFS_TMP0] @ entry PC
ldr r1, [r7, #SSP_OFFS_BLTAB]
str r2, [r1, r0, lsl #2]
.if \patch_jump
b ssp_drc_do_patch
.else
bx r2
.endif
0: @ ssp_de_iram:
ldr r1, [r7, #SSP_OFFS_IRAM_DIRTY]
tst r1, r1
ldreq r1, [r7, #SSP_OFFS_IRAM_CTX]
beq 1f @ ssp_de_iram_ctx
bl ssp_get_iram_context
mov r1, #0
str r1, [r7, #SSP_OFFS_IRAM_DIRTY]
mov r1, r0
str r1, [r7, #SSP_OFFS_IRAM_CTX]
ldr r0, [r7, #SSP_OFFS_TMP0] @ entry PC
1: @ ssp_de_iram_ctx:
ldr r2, [r7, #SSP_OFFS_BLTAB_IRAM]
add r2, r2, r1, lsl #12 @ block_tab_iram + iram_context * 0x800/2*4
add r1, r2, r0, lsl #2
ldr r2, [r1]
tst r2, r2
.if \patch_jump
bne ssp_drc_do_patch
.else
bxne r2
.endif
str r1, [r7, #SSP_OFFS_TMP1]
bl ssp_translate_block
mov r2, r0
ldr r0, [r7, #SSP_OFFS_TMP0] @ entry PC
ldr r1, [r7, #SSP_OFFS_TMP1] @ &block_table_iram[iram_context][rPC]
str r2, [r1]
.if \patch_jump
b ssp_drc_do_patch
.else
bx r2
.endif
.endm @ ssp_drc_do_next
ssp_drc_entry:
stmfd sp!, {r4-r11, lr}
mov r11, r0
ssp_regfile_load:
ldr r7, =ssp
ldr r7, [r7]
add r2, r7, #0x400
add r2, r2, #4
ldmia r2, {r3,r4,r5,r6,r8}
mov r3, r3, lsr #16
mov r3, r3, lsl #16
orr r4, r3, r4, lsr #16 @ XXYY
and r8, r8, #0x0f0000
mov r8, r8, lsl #13 @ sss0 *
and r9, r6, #0x670000
tst r6, #0x80000000
orrne r8, r8, #0x8
tst r6, #0x20000000
orrne r8, r8, #0x4 @ sss0 * NZ..
orr r6, r8, r9, lsr #12 @ sss0 * .uu. .lll NZ..
ldr r8, [r7, #0x440] @ r0-r2
ldr r9, [r7, #0x444] @ r4-r6
ldr r10,[r7, #(0x400+SSP_P*4)] @ P
ldr r0, [r7, #(SSP_OFFS_GR+SSP_PC*4)]
mov r0, r0, lsr #16
ssp_drc_next:
ssp_drc_do_next 0
ssp_drc_next_patch:
ssp_drc_do_next 1
ssp_drc_do_patch:
ldr r1, [r7, #SSP_OFFS_TMP2] @ jump instr. (actually call) address + 4
subs r12,r2, r1
moveq r3, #0xe1000000
orreq r3, r3, #0x00a00000 @ nop
streq r3, [r1, #-4]
beq ssp_drc_dp_end
cmp r12,#4
ldreq r3, [r1]
addeq r3, r3, #1
streq r3, [r1, #-4] @ move the other cond up
moveq r3, #0xe1000000
orreq r3, r3, #0x00a00000
streq r3, [r1] @ fill it's place with nop
beq ssp_drc_dp_end
ldr r3, [r1, #-4]
sub r12,r12,#4
mov r3, r3, lsr #24
bic r3, r3, #1 @ L bit
orr r3, r3, r12,lsl #6
mov r3, r3, ror #8 @ patched branch instruction
str r3, [r1, #-4] @ patch the bl/b to jump directly to another handler
ssp_drc_dp_end:
str r2, [r7, #SSP_OFFS_TMP1]
sub r0, r1, #4
add r1, r1, #4
bl cache_flush_d_inval_i
ldr r2, [r7, #SSP_OFFS_TMP1]
ldr r0, [r7, #SSP_OFFS_TMP0]
bx r2
ssp_drc_end:
mov r0, r0, lsl #16
str r0, [r7, #(SSP_OFFS_GR+SSP_PC*4)]
ssp_regfile_store:
str r10,[r7, #(0x400+SSP_P*4)] @ P
str r8, [r7, #0x440] @ r0-r2
str r9, [r7, #0x444] @ r4-r6
mov r9, r6, lsr #13
and r9, r9, #(7<<16) @ STACK
mov r3, r6, lsl #28
msr cpsr_flg, r3 @ to to ARM PSR
and r6, r6, #0x670
mov r6, r6, lsl #12
orrmi r6, r6, #0x80000000 @ N
orreq r6, r6, #0x20000000 @ Z
mov r3, r4, lsl #16 @ Y
mov r2, r4, lsr #16
mov r2, r2, lsl #16 @ X
add r8, r7, #0x400
add r8, r8, #4
stmia r8, {r2,r3,r5,r6,r9}
mov r0, r11
ldmfd sp!, {r4-r11, lr}
bx lr
@ ld A, PM0
@ andi 2
@ bra z=1, gloc_0800
ssp_hle_800:
ldr r0, [r7, #(SSP_OFFS_GR+SSP_PM0*4)]
ldr r1, [r7, #SSP_OFFS_EMUSTAT]
tst r0, #0x20000
orreq r1, r1, #SSP_WAIT_PM0
subeq r11,r11, #1024
streq r1, [r7, #SSP_OFFS_EMUSTAT]
mov r0, #0x400
beq ssp_drc_end
orrne r0, r0, #0x004
b ssp_drc_next
.macro hle_flushflags
bic r6, r6, #0xf
mrs r1, cpsr
orr r6, r6, r1, lsr #28
.endm
.macro hle_popstack
sub r6, r6, #0x20000000
add r1, r7, #0x400
add r1, r1, #0x048 @ stack
add r1, r1, r6, lsr #28
ldrh r0, [r1]
.endm
ssp_hle_902:
cmp r11, #0
ble ssp_drc_end
add r1, r7, #0x200
ldrh r0, [r1]
ldr r3, [r7, #SSP_OFFS_IRAM_ROM]
add r2, r3, r0, lsl #1 @ (r7|00)
ldrh r0, [r2], #2
mov r5, r5, lsl #16
mov r5, r5, lsr #16
bic r0, r0, #0xfc00
add r3, r3, r0, lsl #1 @ IRAM dest
ldrh r12,[r2], #2 @ length
bic r3, r3, #3 @ always seen aligned
@ orr r5, r5, #0x08000000
@ orr r5, r5, #0x00880000
@ sub r5, r5, r12, lsl #16
bic r6, r6, #0xf
add r12,r12,#1
mov r0, #1
str r0, [r7, #SSP_OFFS_IRAM_DIRTY]
sub r11,r11,r12,lsl #1
sub r11,r11,r12 @ -= length*3
ssp_hle_902_loop:
ldrh r0, [r2], #2
ldrh r1, [r2], #2
subs r12,r12,#2
orr r0, r0, r1, lsl #16
str r0, [r3], #4
bgt ssp_hle_902_loop
tst r12, #1
ldrneh r0, [r2], #2
strneh r0, [r3], #2
ldr r0, [r7, #SSP_OFFS_IRAM_ROM]
add r1, r7, #0x200
sub r2, r2, r0
mov r2, r2, lsr #1
strh r2, [r1] @ (r7|00)
sub r0, r3, r0
mov r0, r0, lsr #1
orr r0, r0, #0x08000000
orr r0, r0, #0x001c8000
str r0, [r7, #(SSP_OFFS_GR+SSP_PMC*4)]
str r0, [r7, #(SSP_OFFS_PM_WRITE+4*4)]
hle_popstack
subs r11,r11,#16 @ timeslice is likely to end
ble ssp_drc_end
b ssp_drc_next
@ this one is car rendering related
.macro hle_11_12c_mla offs_in
ldrsh r5, [r7, #(\offs_in+0)]
ldrsh r0, [r7, #(\offs_in+2)]
ldrsh r1, [r7, #(\offs_in+4)]
mul r5, r2, r5
ldrsh r12,[r7, #(\offs_in+6)]
mla r5, r3, r0, r5
mla r5, r4, r1, r5
add r5, r5, r12,lsl #11
movs r5, r5, lsr #13
add r1, r7, r8, lsr #23
strh r5, [r1]
add r8, r8, #(1<<24)
.endm
ssp_hle_11_12c:
cmp r11, #0
ble ssp_drc_end
mov r0, #0
bl ssp_pm_read
mov r4, r0
mov r0, #0
bl ssp_pm_read
mov r5, r0
mov r0, #0
bl ssp_pm_read
mov r2, r4, lsl #16
mov r2, r2, asr #15 @ (r7|00) << 1
mov r3, r5, lsl #16
mov r3, r3, asr #15 @ (r7|01) << 1
mov r4, r0, lsl #16
mov r4, r4, asr #15 @ (r7|10) << 1
bic r8, r8, #0xff
mov r8, r8, ror #16
hle_11_12c_mla 0x20
hle_11_12c_mla 0x28
hle_11_12c_mla 0x30
mov r8, r8, ror #16
orr r8, r8, #0x1c
@ hle_flushflags
hle_popstack
sub r11,r11,#33
b ssp_drc_next
ssp_hle_11_384:
mov r3, #2
b ssp_hle_11_38x
ssp_hle_11_38a:
mov r3, #3 @ r5
ssp_hle_11_38x:
cmp r11, #0
ble ssp_drc_end
mov r2, #0 @ EFh, EEh
mov r1, #1 @ r4
add r0, r7, #0x1c0 @ r0 (based)
ssp_hle_11_38x_loop:
ldrh r5, [r0], #2
ldr r12,[r7, #0x224]
mov r5, r5, lsl #16
eor r5, r5, r5, asr #31
add r5, r5, r5, lsr #31 @ abs(r5)
cmp r5, r12,lsl #16
orrpl r2, r2, r1,lsl #16 @ EFh |= r4
ldrh r5, [r0, #2]!
ldr r12,[r7, #0x220]
cmp r5, r12,lsr #16
orrpl r2, r2, r1,lsl #16 @ EFh |= r4
ldr r12,[r7, #0x1e8]
add r0, r0, #2
mov r12,r12,lsl #16
cmp r5, r12,lsr #16
orrmi r2, r2, r1
mov r1, r1, lsl #1
subs r3, r3, #1
bpl ssp_hle_11_38x_loop
str r2, [r7, #0x1dc]
sub r0, r0, r7
bic r8, r8, #0xff
orr r8, r8, r0, lsr #1
bic r9, r9, #0xff
orr r9, r9, r1
@ hle_flushflags
hle_popstack
sub r11,r11,#(9+30*4)
b ssp_drc_next
ssp_hle_07_6d6:
cmp r11, #0
ble ssp_drc_end
ldr r1, [r7, #0x20c]
and r0, r8, #0xff @ assuming alignment
add r0, r7, r0, lsl #1
mov r2, r1, lsr #16
mov r1, r1, lsl #16 @ 106h << 16
mov r2, r2, lsl #16 @ 107h << 16
ssp_hle_07_6d6_loop:
ldr r5, [r0], #4
tst r5, r5
bmi ssp_hle_07_6d6_end
mov r5, r5, lsl #16
cmp r5, r1
movmi r1, r5
cmp r5, r2
sub r11,r11,#16
bmi ssp_hle_07_6d6_loop
mov r2, r5
b ssp_hle_07_6d6_loop
ssp_hle_07_6d6_end:
sub r0, r0, r7
mov r0, r0, lsr #1
bic r8, r8, #0xff
orr r8, r8, r0
orr r1, r2, r1, lsr #16
str r1, [r7, #0x20c]
hle_popstack
sub r11,r11,#6
b ssp_drc_next
ssp_hle_07_030:
ldrh r0, [r7]
mov r0, r0, lsl #4
orr r0, r0, r0, lsr #16
strh r0, [r7]
sub r11,r11,#3
ssp_hle_07_036:
ldr r1, [r7, #0x1e0] @ F1h F0h
rsb r5, r1, r1, lsr #16
mov r5, r5, lsl #16 @ AL not needed
cmp r5, #(4<<16)
sub r11,r11,#5
bmi hle_07_036_ending2
ldr r1, [r7, #0x1dc] @ EEh
cmp r5, r1, lsl #16
sub r11,r11,#5
bpl hle_07_036_ret
mov r0, r5, lsr #16
add r1, r7, #0x100
strh r0, [r1, #0xea] @ F5h
ldr r0, [r7, #0x1e0] @ F0h
and r0, r0, #3
strh r0, [r1, #0xf0] @ F8h
add r2, r0, #0xc0 @ r2
add r2, r7, r2, lsl #1
ldrh r2, [r2]
ldr r0, [r7]
mov r1, #4
and r0, r0, r2
bl ssp_pm_write
@ will handle PMC later
ldr r0, [r7, #0x1e8] @ F5h << 16
ldr r1, [r7, #0x1f0] @ F8h
ldr r2, [r7, #0x1d4] @ EAh
sub r0, r0, #(3<<16)
add r0, r0, r1, lsl #16
sub r0, r2, r0, asr #18
and r0, r0, #0x7f
rsbs r0, r0, #0x78 @ length
ble hle_07_036_ending1
sub r11,r11,r0
@ copy part
ldr r1, [r7, #(SSP_OFFS_GR+SSP_PMC*4)]
ldr r2, [r7, #SSP_OFFS_DRAM]
mov r1, r1, lsl #16
add r1, r2, r1, lsr #15 @ addr (based)
ldrh r2, [r7, #0] @ pattern
ldrh r3, [r7, #6] @ mode
mov r12, #0x4000
orr r12,r12,#0x0018
subs r12,r3, r12
subnes r12,r12,#0x0400
blne tr_unhandled
orr r2, r2, r2, lsl #16
tst r3, #0x400
bne hle_07_036_ovrwr
hle_07_036_no_ovrwr:
tst r1, #2
strneh r2, [r1], #0x3e @ align
subne r0, r0, #1
subs r0, r0, #4
blt hle_07_036_l2
hle_07_036_l1:
subs r0, r0, #4
str r2, [r1], #0x40
str r2, [r1], #0x40
bge hle_07_036_l1
hle_07_036_l2:
tst r0, #2
strne r2, [r1], #0x40
tst r0, #1
strneh r2, [r1], #2
b hle_07_036_end_copy
hle_07_036_ovrwr:
tst r2, #0x000f
orreq r12,r12,#0x000f
tst r2, #0x00f0
orreq r12,r12,#0x00f0
tst r2, #0x0f00
orreq r12,r12,#0x0f00
tst r2, #0xf000
orreq r12,r12,#0xf000
orrs r12,r12,r12,lsl #16
beq hle_07_036_no_ovrwr
tst r1, #2
beq hle_07_036_ol0
ldrh r3, [r1]
and r3, r3, r12
orr r3, r3, r2
strh r3, [r1], #0x3e @ align
sub r0, r0, #1
hle_07_036_ol0:
subs r0, r0, #2
blt hle_07_036_ol2
hle_07_036_ol1:
subs r0, r0, #2
ldr r3, [r1]
and r3, r3, r12
orr r3, r3, r2
str r3, [r1], #0x40
bge hle_07_036_ol1
hle_07_036_ol2:
tst r0, #1
ldrneh r3, [r1]
andne r3, r3, r12
orrne r3, r3, r2
strneh r3, [r1], #2
hle_07_036_end_copy:
ldr r2, [r7, #SSP_OFFS_DRAM]
add r3, r7, #0x400
sub r0, r1, r2 @ new addr
mov r0, r0, lsr #1
strh r0, [r3, #(0x6c+4*4)] @ SSP_OFFS_PM_WRITE+4*4 (low)
hle_07_036_ending1:
ldr r0, [r7, #0x1e0] @ F1h << 16
add r0, r0, #(1<<16)
and r0, r0, #(3<<16)
add r0, r0, #(0xc4<<16)
bic r8, r8, #0xff0000
orr r8, r8, r0 @ r2
add r0, r7, r0, lsr #15
ldrh r0, [r0]
ldr r2, [r7]
and r0, r0, r2
movs r5, r0, lsl #16
ldr r1, [r7, #4] @ new mode
add r2, r7, #0x400
strh r1, [r2, #(0x6c+4*4+2)] @ SSP_OFFS_PM_WRITE+4*4 (high)
mov r1, #4
bl ssp_pm_write
sub r11,r11,#35
hle_07_036_ret:
hle_popstack
b ssp_drc_next
hle_07_036_ending2:
sub r11,r11,#3
movs r5, r5, lsl #1
bmi hle_07_036_ret
mov r0, #0x87
b ssp_drc_next @ let the dispatcher finish this

169
pico/carthw/svp/svp.c Normal file
View file

@ -0,0 +1,169 @@
// The SVP chip emulator
// (c) Copyright 2008, Grazvydas "notaz" Ignotas
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include "../../pico_int.h"
#include "compiler.h"
#ifdef __GP2X__
#include <sys/mman.h>
#endif
svp_t *svp = NULL;
int PicoSVPCycles = 850; // cycles/line, just a guess
static int svp_dyn_ready = 0;
/* save state stuff */
typedef enum {
CHUNK_IRAM = CHUNK_CARTHW,
CHUNK_DRAM,
CHUNK_SSP
} chunk_name_e;
static carthw_state_chunk svp_states[] =
{
{ CHUNK_IRAM, 0x800, NULL },
{ CHUNK_DRAM, sizeof(svp->dram), NULL },
{ CHUNK_SSP, sizeof(svp->ssp1601) - sizeof(svp->ssp1601.drc), NULL },
{ 0, 0, NULL }
};
static void PicoSVPReset(void)
{
elprintf(EL_SVP, "SVP reset");
memcpy(svp->iram_rom + 0x800, Pico.rom + 0x800, 0x20000 - 0x800);
ssp1601_reset(&svp->ssp1601);
#ifndef PSP
if ((PicoOpt&POPT_EN_SVP_DRC) && svp_dyn_ready)
ssp1601_dyn_reset(&svp->ssp1601);
#endif
}
static void PicoSVPLine(void)
{
int count = 1;
#if defined(ARM) || defined(PSP)
// performance hack
static int delay_lines = 0;
delay_lines++;
if ((Pico.m.scanline&0xf) != 0xf && Pico.m.scanline != 261 && Pico.m.scanline != 311)
return;
count = delay_lines;
delay_lines = 0;
#endif
#ifndef PSP
if ((PicoOpt&POPT_EN_SVP_DRC) && svp_dyn_ready)
ssp1601_dyn_run(PicoSVPCycles * count);
else
#endif
{
ssp1601_run(PicoSVPCycles * count);
svp_dyn_ready = 0; // just in case
}
// test mode
//if (Pico.m.frame_count == 13) PicoPad[0] |= 0xff;
}
static int PicoSVPDma(unsigned int source, int len, unsigned short **srcp, unsigned short **limitp)
{
if (source < Pico.romsize) // Rom
{
source -= 2;
*srcp = (unsigned short *)(Pico.rom + (source&~1));
*limitp = (unsigned short *)(Pico.rom + Pico.romsize);
return 1;
}
else if ((source & 0xfe0000) == 0x300000)
{
elprintf(EL_VDPDMA|EL_SVP, "SVP DmaSlow from %06x, len=%i", source, len);
source &= 0x1fffe;
source -= 2;
*srcp = (unsigned short *)(svp->dram + source);
*limitp = (unsigned short *)(svp->dram + sizeof(svp->dram));
return 1;
}
else
elprintf(EL_VDPDMA|EL_SVP|EL_ANOMALY, "SVP FIXME unhandled DmaSlow from %06x, len=%i", source, len);
return 0;
}
void PicoSVPInit(void)
{
#ifdef __GP2X__
int ret;
ret = munmap(tcache, SSP_DRC_SIZE);
printf("munmap tcache: %i\n", ret);
#endif
}
static void PicoSVPShutdown(void)
{
#ifdef __GP2X__
// also unmap tcache
PicoSVPInit();
#endif
}
void PicoSVPStartup(void)
{
void *tmp;
elprintf(EL_SVP, "SVP init");
tmp = realloc(Pico.rom, 0x200000 + sizeof(*svp));
if (tmp == NULL)
{
elprintf(EL_STATUS|EL_SVP, "OOM for SVP data");
return;
}
//PicoOpt &= ~0x20000;
Pico.rom = tmp;
svp = (void *) ((char *)tmp + 0x200000);
memset(svp, 0, sizeof(*svp));
#ifdef __GP2X__
tmp = mmap(tcache, SSP_DRC_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
printf("mmap tcache: %p, asked %p\n", tmp, tcache);
#endif
// init SVP compiler
svp_dyn_ready = 0;
#ifndef PSP
if (PicoOpt&POPT_EN_SVP_DRC) {
if (ssp1601_dyn_startup()) return;
svp_dyn_ready = 1;
}
#endif
// init ok, setup hooks..
PicoRead16Hook = PicoSVPRead16;
PicoWrite8Hook = PicoSVPWrite8;
PicoWrite16Hook = PicoSVPWrite16;
PicoDmaHook = PicoSVPDma;
PicoResetHook = PicoSVPReset;
PicoLineHook = PicoSVPLine;
PicoCartUnloadHook = PicoSVPShutdown;
// save state stuff
svp_states[0].ptr = svp->iram_rom;
svp_states[1].ptr = svp->dram;
svp_states[2].ptr = &svp->ssp1601;
carthw_chunks = svp_states;
PicoAHW |= PAHW_SVP;
}

624
pico/cd/LC89510.c Normal file
View file

@ -0,0 +1,624 @@
/***********************************************************
* *
* This source file was taken from the Gens project *
* Written by Stéphane Dallongeville *
* Copyright (c) 2002 by Stéphane Dallongeville *
* Modified/adapted for PicoDrive by notaz, 2007 *
* *
***********************************************************/
#include "../pico_int.h"
#define CDC_DMA_SPEED 256
static void CDD_Reset(void)
{
// Reseting CDD
memset(Pico_mcd->s68k_regs+0x34, 0, 2*2); // CDD.Fader, CDD.Control
Pico_mcd->cdd.Status = 0;
Pico_mcd->cdd.Minute = 0;
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
// clear receive status and transfer command
memset(Pico_mcd->s68k_regs+0x38, 0, 20);
Pico_mcd->s68k_regs[0x38+9] = 0xF; // Default checksum
}
static void CDC_Reset(void)
{
// Reseting CDC
memset(Pico_mcd->cdc.Buffer, 0, sizeof(Pico_mcd->cdc.Buffer));
Pico_mcd->cdc.COMIN = 0;
Pico_mcd->cdc.IFSTAT = 0xFF;
Pico_mcd->cdc.DAC.N = 0;
Pico_mcd->cdc.DBC.N = 0;
Pico_mcd->cdc.HEAD.N = 0x01000000;
Pico_mcd->cdc.PT.N = 0;
Pico_mcd->cdc.WA.N = 2352 * 2;
Pico_mcd->cdc.STAT.N = 0x00000080;
Pico_mcd->cdc.SBOUT = 0;
Pico_mcd->cdc.IFCTRL = 0;
Pico_mcd->cdc.CTRL.N = 0;
Pico_mcd->cdc.Decode_Reg_Read = 0;
Pico_mcd->scd.Status_CDC &= ~0x08;
}
PICO_INTERNAL void LC89510_Reset(void)
{
CDD_Reset();
CDC_Reset();
// clear DMA_Adr & Stop_Watch
memset(Pico_mcd->s68k_regs + 0xA, 0, 4);
}
PICO_INTERNAL void Update_CDC_TRansfer(int which)
{
unsigned int DMA_Adr, dep, length;
unsigned short *dest;
unsigned char *src;
if (Pico_mcd->cdc.DBC.N <= (CDC_DMA_SPEED * 2))
{
length = (Pico_mcd->cdc.DBC.N + 1) >> 1;
Pico_mcd->scd.Status_CDC &= ~0x08; // Last transfer
Pico_mcd->s68k_regs[4] |= 0x80; // End data transfer
Pico_mcd->s68k_regs[4] &= ~0x40; // no more data ready
Pico_mcd->cdc.IFSTAT |= 0x08; // No more data transfer in progress
if (Pico_mcd->cdc.IFCTRL & 0x40) // DTEIEN = Data Trasnfer End Interrupt Enable ?
{
Pico_mcd->cdc.IFSTAT &= ~0x40;
if (Pico_mcd->s68k_regs[0x33] & (1<<5))
{
elprintf(EL_INTS, "cdc DTE irq 5");
SekInterruptS68k(5);
}
}
}
else length = CDC_DMA_SPEED;
// TODO: dst bounds checking?
src = Pico_mcd->cdc.Buffer + Pico_mcd->cdc.DAC.N;
DMA_Adr = (Pico_mcd->s68k_regs[0xA]<<8) | Pico_mcd->s68k_regs[0xB];
if (which == 7) // WORD RAM
{
if (Pico_mcd->s68k_regs[3] & 4)
{
// test: Final Fight
int bank = !(Pico_mcd->s68k_regs[3]&1);
dep = ((DMA_Adr & 0x3FFF) << 3);
cdprintf("CD DMA # %04x -> word_ram1M # %06x, len=%i",
Pico_mcd->cdc.DAC.N, dep, length);
dest = (unsigned short *) (Pico_mcd->word_ram1M[bank] + dep);
memcpy16bswap(dest, src, length);
/*{ // debug
unsigned char *b1 = Pico_mcd->word_ram1M[bank] + dep;
unsigned char *b2 = (unsigned char *)(dest+length) - 8;
dprintf("%02x %02x %02x %02x .. %02x %02x %02x %02x",
b1[0], b1[1], b1[4], b1[5], b2[0], b2[1], b2[4], b2[5]);
}*/
}
else
{
dep = ((DMA_Adr & 0x7FFF) << 3);
cdprintf("CD DMA # %04x -> word_ram2M # %06x, len=%i",
Pico_mcd->cdc.DAC.N, dep, length);
dest = (unsigned short *) (Pico_mcd->word_ram2M + dep);
memcpy16bswap(dest, src, length);
/*{ // debug
unsigned char *b1 = Pico_mcd->word_ram2M + dep;
unsigned char *b2 = (unsigned char *)(dest+length) - 4;
dprintf("%02x %02x %02x %02x .. %02x %02x %02x %02x",
b1[0], b1[1], b1[2], b1[3], b2[0], b2[1], b2[2], b2[3]);
}*/
}
}
else if (which == 4) // PCM RAM (check: popful Mail)
{
dep = (DMA_Adr & 0x03FF) << 2;
cdprintf("CD DMA # %04x -> PCM[%i] # %04x, len=%i",
Pico_mcd->cdc.DAC.N, Pico_mcd->pcm.bank, dep, length);
dest = (unsigned short *) (Pico_mcd->pcm_ram_b[Pico_mcd->pcm.bank] + dep);
if (Pico_mcd->cdc.DAC.N & 1) /* unaligned src? */
memcpy(dest, src, length*2);
else memcpy16(dest, (unsigned short *) src, length);
}
else if (which == 5) // PRG RAM
{
dep = DMA_Adr << 3;
dest = (unsigned short *) (Pico_mcd->prg_ram + dep);
cdprintf("CD DMA # %04x -> prg_ram # %06x, len=%i",
Pico_mcd->cdc.DAC.N, dep, length);
memcpy16bswap(dest, src, length);
/*{ // debug
unsigned char *b1 = Pico_mcd->prg_ram + dep;
unsigned char *b2 = (unsigned char *)(dest+length) - 4;
dprintf("%02x %02x %02x %02x .. %02x %02x %02x %02x",
b1[0], b1[1], b1[2], b1[3], b2[0], b2[1], b2[2], b2[3]);
}*/
}
length <<= 1;
Pico_mcd->cdc.DAC.N = (Pico_mcd->cdc.DAC.N + length) & 0xFFFF;
if (Pico_mcd->scd.Status_CDC & 0x08) Pico_mcd->cdc.DBC.N -= length;
else Pico_mcd->cdc.DBC.N = 0;
// update DMA_Adr
length >>= 2;
if (which != 4) length >>= 1;
DMA_Adr += length;
Pico_mcd->s68k_regs[0xA] = DMA_Adr >> 8;
Pico_mcd->s68k_regs[0xB] = DMA_Adr;
}
PICO_INTERNAL_ASM unsigned short Read_CDC_Host(int is_sub)
{
int addr;
if (!(Pico_mcd->scd.Status_CDC & 0x08))
{
// Transfer data disabled
cdprintf("Read_CDC_Host FIXME: Transfer data disabled");
return 0;
}
if ((is_sub && (Pico_mcd->s68k_regs[4] & 7) != 3) ||
(!is_sub && (Pico_mcd->s68k_regs[4] & 7) != 2))
{
// Wrong setting
cdprintf("Read_CDC_Host FIXME: Wrong setting");
return 0;
}
Pico_mcd->cdc.DBC.N -= 2;
if (Pico_mcd->cdc.DBC.N <= 0)
{
Pico_mcd->cdc.DBC.N = 0;
Pico_mcd->scd.Status_CDC &= ~0x08; // Last transfer
Pico_mcd->s68k_regs[4] |= 0x80; // End data transfer
Pico_mcd->s68k_regs[4] &= ~0x40; // no more data ready
Pico_mcd->cdc.IFSTAT |= 0x08; // No more data transfer in progress
if (Pico_mcd->cdc.IFCTRL & 0x40) // DTEIEN = Data Transfer End Interrupt Enable ?
{
Pico_mcd->cdc.IFSTAT &= ~0x40;
if (Pico_mcd->s68k_regs[0x33]&(1<<5)) {
elprintf(EL_INTS, "m68k: s68k irq 5");
SekInterruptS68k(5);
}
cdprintf("CDC - DTE interrupt");
}
}
addr = Pico_mcd->cdc.DAC.N;
Pico_mcd->cdc.DAC.N += 2;
cdprintf("Read_CDC_Host sub=%i d=%04x dac=%04x dbc=%04x", is_sub,
(Pico_mcd->cdc.Buffer[addr]<<8) | Pico_mcd->cdc.Buffer[addr+1], Pico_mcd->cdc.DAC.N, Pico_mcd->cdc.DBC.N);
return (Pico_mcd->cdc.Buffer[addr]<<8) | Pico_mcd->cdc.Buffer[addr+1];
}
PICO_INTERNAL void CDC_Update_Header(void)
{
if (Pico_mcd->cdc.CTRL.B.B1 & 0x01) // Sub-Header wanted ?
{
Pico_mcd->cdc.HEAD.B.B0 = 0;
Pico_mcd->cdc.HEAD.B.B1 = 0;
Pico_mcd->cdc.HEAD.B.B2 = 0;
Pico_mcd->cdc.HEAD.B.B3 = 0;
}
else
{
_msf MSF;
LBA_to_MSF(Pico_mcd->scd.Cur_LBA, &MSF);
Pico_mcd->cdc.HEAD.B.B0 = INT_TO_BCDB(MSF.M);
Pico_mcd->cdc.HEAD.B.B1 = INT_TO_BCDB(MSF.S);
Pico_mcd->cdc.HEAD.B.B2 = INT_TO_BCDB(MSF.F);
Pico_mcd->cdc.HEAD.B.B3 = 0x01;
}
}
PICO_INTERNAL unsigned char CDC_Read_Reg(void)
{
unsigned char ret;
switch(Pico_mcd->s68k_regs[5] & 0xF)
{
case 0x0: // COMIN
cdprintf("CDC read reg 00 = %.2X", Pico_mcd->cdc.COMIN);
Pico_mcd->s68k_regs[5] = 0x1;
return Pico_mcd->cdc.COMIN;
case 0x1: // IFSTAT
cdprintf("CDC read reg 01 = %.2X", Pico_mcd->cdc.IFSTAT);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 1); // Reg 1 (decoding)
Pico_mcd->s68k_regs[5] = 0x2;
return Pico_mcd->cdc.IFSTAT;
case 0x2: // DBCL
cdprintf("CDC read reg 02 = %.2X", Pico_mcd->cdc.DBC.B.L);
Pico_mcd->s68k_regs[5] = 0x3;
return Pico_mcd->cdc.DBC.B.L;
case 0x3: // DBCH
cdprintf("CDC read reg 03 = %.2X", Pico_mcd->cdc.DBC.B.H);
Pico_mcd->s68k_regs[5] = 0x4;
return Pico_mcd->cdc.DBC.B.H;
case 0x4: // HEAD0
cdprintf("CDC read reg 04 = %.2X", Pico_mcd->cdc.HEAD.B.B0);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 4); // Reg 4 (decoding)
Pico_mcd->s68k_regs[5] = 0x5;
return Pico_mcd->cdc.HEAD.B.B0;
case 0x5: // HEAD1
cdprintf("CDC read reg 05 = %.2X", Pico_mcd->cdc.HEAD.B.B1);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 5); // Reg 5 (decoding)
Pico_mcd->s68k_regs[5] = 0x6;
return Pico_mcd->cdc.HEAD.B.B1;
case 0x6: // HEAD2
cdprintf("CDC read reg 06 = %.2X", Pico_mcd->cdc.HEAD.B.B2);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 6); // Reg 6 (decoding)
Pico_mcd->s68k_regs[5] = 0x7;
return Pico_mcd->cdc.HEAD.B.B2;
case 0x7: // HEAD3
cdprintf("CDC read reg 07 = %.2X", Pico_mcd->cdc.HEAD.B.B3);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 7); // Reg 7 (decoding)
Pico_mcd->s68k_regs[5] = 0x8;
return Pico_mcd->cdc.HEAD.B.B3;
case 0x8: // PTL
cdprintf("CDC read reg 08 = %.2X", Pico_mcd->cdc.PT.B.L);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 8); // Reg 8 (decoding)
Pico_mcd->s68k_regs[5] = 0x9;
return Pico_mcd->cdc.PT.B.L;
case 0x9: // PTH
cdprintf("CDC read reg 09 = %.2X", Pico_mcd->cdc.PT.B.H);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 9); // Reg 9 (decoding)
Pico_mcd->s68k_regs[5] = 0xA;
return Pico_mcd->cdc.PT.B.H;
case 0xA: // WAL
cdprintf("CDC read reg 10 = %.2X", Pico_mcd->cdc.WA.B.L);
Pico_mcd->s68k_regs[5] = 0xB;
return Pico_mcd->cdc.WA.B.L;
case 0xB: // WAH
cdprintf("CDC read reg 11 = %.2X", Pico_mcd->cdc.WA.B.H);
Pico_mcd->s68k_regs[5] = 0xC;
return Pico_mcd->cdc.WA.B.H;
case 0xC: // STAT0
cdprintf("CDC read reg 12 = %.2X", Pico_mcd->cdc.STAT.B.B0);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 12); // Reg 12 (decoding)
Pico_mcd->s68k_regs[5] = 0xD;
return Pico_mcd->cdc.STAT.B.B0;
case 0xD: // STAT1
cdprintf("CDC read reg 13 = %.2X", Pico_mcd->cdc.STAT.B.B1);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 13); // Reg 13 (decoding)
Pico_mcd->s68k_regs[5] = 0xE;
return Pico_mcd->cdc.STAT.B.B1;
case 0xE: // STAT2
cdprintf("CDC read reg 14 = %.2X", Pico_mcd->cdc.STAT.B.B2);
Pico_mcd->cdc.Decode_Reg_Read |= (1 << 14); // Reg 14 (decoding)
Pico_mcd->s68k_regs[5] = 0xF;
return Pico_mcd->cdc.STAT.B.B2;
case 0xF: // STAT3
cdprintf("CDC read reg 15 = %.2X", Pico_mcd->cdc.STAT.B.B3);
ret = Pico_mcd->cdc.STAT.B.B3;
Pico_mcd->cdc.IFSTAT |= 0x20; // decoding interrupt flag cleared
if ((Pico_mcd->cdc.CTRL.B.B0 & 0x80) && (Pico_mcd->cdc.IFCTRL & 0x20))
{
if ((Pico_mcd->cdc.Decode_Reg_Read & 0x73F2) == 0x73F2)
Pico_mcd->cdc.STAT.B.B3 = 0x80;
}
return ret;
}
return 0;
}
PICO_INTERNAL void CDC_Write_Reg(unsigned char Data)
{
cdprintf("CDC write reg%02d = %.2X", Pico_mcd->s68k_regs[5] & 0xF, Data);
switch (Pico_mcd->s68k_regs[5] & 0xF)
{
case 0x0: // SBOUT
Pico_mcd->s68k_regs[5] = 0x1;
Pico_mcd->cdc.SBOUT = Data;
break;
case 0x1: // IFCTRL
Pico_mcd->s68k_regs[5] = 0x2;
Pico_mcd->cdc.IFCTRL = Data;
if ((Pico_mcd->cdc.IFCTRL & 0x02) == 0) // Stop data transfer
{
Pico_mcd->cdc.DBC.N = 0;
Pico_mcd->scd.Status_CDC &= ~0x08;
Pico_mcd->cdc.IFSTAT |= 0x08; // No more data transfer in progress
}
break;
case 0x2: // DBCL
Pico_mcd->s68k_regs[5] = 0x3;
Pico_mcd->cdc.DBC.B.L = Data;
break;
case 0x3: // DBCH
Pico_mcd->s68k_regs[5] = 0x4;
Pico_mcd->cdc.DBC.B.H = Data;
break;
case 0x4: // DACL
Pico_mcd->s68k_regs[5] = 0x5;
Pico_mcd->cdc.DAC.B.L = Data;
break;
case 0x5: // DACH
Pico_mcd->s68k_regs[5] = 0x6;
Pico_mcd->cdc.DAC.B.H = Data;
break;
case 0x6: // DTTRG
if (Pico_mcd->cdc.IFCTRL & 0x02) // Data transfer enable ?
{
Pico_mcd->cdc.IFSTAT &= ~0x08; // Data transfer in progress
Pico_mcd->scd.Status_CDC |= 0x08; // Data transfer in progress
Pico_mcd->s68k_regs[4] &= 0x7F; // A data transfer start
cdprintf("************** Starting Data Transfer ***********");
cdprintf("RS0 = %.4X DAC = %.4X DBC = %.4X DMA adr = %.4X\n\n", Pico_mcd->s68k_regs[4]<<8,
Pico_mcd->cdc.DAC.N, Pico_mcd->cdc.DBC.N, (Pico_mcd->s68k_regs[0xA]<<8) | Pico_mcd->s68k_regs[0xB]);
}
break;
case 0x7: // DTACK
Pico_mcd->cdc.IFSTAT |= 0x40; // end data transfer interrupt flag cleared
break;
case 0x8: // WAL
Pico_mcd->s68k_regs[5] = 0x9;
Pico_mcd->cdc.WA.B.L = Data;
break;
case 0x9: // WAH
Pico_mcd->s68k_regs[5] = 0xA;
Pico_mcd->cdc.WA.B.H = Data;
break;
case 0xA: // CTRL0
Pico_mcd->s68k_regs[5] = 0xB;
Pico_mcd->cdc.CTRL.B.B0 = Data;
break;
case 0xB: // CTRL1
Pico_mcd->s68k_regs[5] = 0xC;
Pico_mcd->cdc.CTRL.B.B1 = Data;
break;
case 0xC: // PTL
Pico_mcd->s68k_regs[5] = 0xD;
Pico_mcd->cdc.PT.B.L = Data;
break;
case 0xD: // PTH
Pico_mcd->s68k_regs[5] = 0xE;
Pico_mcd->cdc.PT.B.H = Data;
break;
case 0xE: // CTRL2
Pico_mcd->cdc.CTRL.B.B2 = Data;
break;
case 0xF: // RESET
CDC_Reset();
break;
}
}
static int bswapwrite(int a, unsigned short d)
{
*(unsigned short *)(Pico_mcd->s68k_regs + a) = (d>>8)|(d<<8);
return d + (d >> 8);
}
PICO_INTERNAL void CDD_Export_Status(void)
{
unsigned int csum;
csum = bswapwrite( 0x38+0, Pico_mcd->cdd.Status);
csum += bswapwrite( 0x38+2, Pico_mcd->cdd.Minute);
csum += bswapwrite( 0x38+4, Pico_mcd->cdd.Seconde);
csum += bswapwrite( 0x38+6, Pico_mcd->cdd.Frame);
Pico_mcd->s68k_regs[0x38+8] = Pico_mcd->cdd.Ext;
csum += Pico_mcd->cdd.Ext;
Pico_mcd->s68k_regs[0x38+9] = ~csum & 0xf;
Pico_mcd->s68k_regs[0x37] &= 3; // CDD.Control
if (Pico_mcd->s68k_regs[0x33] & (1<<4))
{
elprintf(EL_INTS, "cdd export irq 4");
SekInterruptS68k(4);
}
// cdprintf("CDD exported status\n");
cdprintf("out: Status=%.4X, Minute=%.4X, Second=%.4X, Frame=%.4X Checksum=%.4X",
(Pico_mcd->s68k_regs[0x38+0] << 8) | Pico_mcd->s68k_regs[0x38+1],
(Pico_mcd->s68k_regs[0x38+2] << 8) | Pico_mcd->s68k_regs[0x38+3],
(Pico_mcd->s68k_regs[0x38+4] << 8) | Pico_mcd->s68k_regs[0x38+5],
(Pico_mcd->s68k_regs[0x38+6] << 8) | Pico_mcd->s68k_regs[0x38+7],
(Pico_mcd->s68k_regs[0x38+8] << 8) | Pico_mcd->s68k_regs[0x38+9]);
}
PICO_INTERNAL void CDD_Import_Command(void)
{
// cdprintf("CDD importing command\n");
cdprintf("in: Command=%.4X, Minute=%.4X, Second=%.4X, Frame=%.4X Checksum=%.4X",
(Pico_mcd->s68k_regs[0x38+10+0] << 8) | Pico_mcd->s68k_regs[0x38+10+1],
(Pico_mcd->s68k_regs[0x38+10+2] << 8) | Pico_mcd->s68k_regs[0x38+10+3],
(Pico_mcd->s68k_regs[0x38+10+4] << 8) | Pico_mcd->s68k_regs[0x38+10+5],
(Pico_mcd->s68k_regs[0x38+10+6] << 8) | Pico_mcd->s68k_regs[0x38+10+7],
(Pico_mcd->s68k_regs[0x38+10+8] << 8) | Pico_mcd->s68k_regs[0x38+10+9]);
switch (Pico_mcd->s68k_regs[0x38+10+0])
{
case 0x0: // STATUS (?)
Get_Status_CDD_c0();
break;
case 0x1: // STOP ALL (?)
Stop_CDD_c1();
break;
case 0x2: // GET TOC INFORMATIONS
switch(Pico_mcd->s68k_regs[0x38+10+3])
{
case 0x0: // get current position (MSF format)
Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00);
Get_Pos_CDD_c20();
break;
case 0x1: // get elapsed time of current track played/scanned (relative MSF format)
Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 1;
Get_Track_Pos_CDD_c21();
break;
case 0x2: // get current track in RS2-RS3
Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 2;
Get_Current_Track_CDD_c22();
break;
case 0x3: // get total length (MSF format)
Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 3;
Get_Total_Lenght_CDD_c23();
break;
case 0x4: // first & last track number
Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 4;
Get_First_Last_Track_CDD_c24();
break;
case 0x5: // get track addresse (MSF format)
Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 5;
Get_Track_Adr_CDD_c25();
break;
default : // invalid, then we return status
Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 0xF;
Get_Status_CDD_c0();
break;
}
break;
case 0x3: // READ
Play_CDD_c3();
break;
case 0x4: // SEEK
Seek_CDD_c4();
break;
case 0x6: // PAUSE/STOP
Pause_CDD_c6();
break;
case 0x7: // RESUME
Resume_CDD_c7();
break;
case 0x8: // FAST FOWARD
Fast_Foward_CDD_c8();
break;
case 0x9: // FAST REWIND
Fast_Rewind_CDD_c9();
break;
case 0xA: // RECOVER INITIAL STATE (?)
CDD_cA();
break;
case 0xC: // CLOSE TRAY
Close_Tray_CDD_cC();
break;
case 0xD: // OPEN TRAY
Open_Tray_CDD_cD();
break;
default: // UNKNOWN
CDD_Def();
break;
}
}

135
pico/cd/LC89510.h Normal file
View file

@ -0,0 +1,135 @@
/***********************************************************
* *
* This source was taken from the Gens project *
* Written by Stéphane Dallongeville *
* Copyright (c) 2002 by Stéphane Dallongeville *
* Modified/adapted for PicoDrive by notaz, 2007 *
* *
***********************************************************/
#ifndef _LC89510_H
#define _LC89510_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
unsigned char Buffer[(32 * 1024 * 2) + 2352];
// unsigned int Host_Data; // unused
// unsigned int DMA_Adr; // 0A
// unsigned int Stop_Watch; // 0C
unsigned int COMIN;
unsigned int IFSTAT;
union
{
struct
{
unsigned char L;
unsigned char H;
unsigned short unused;
} B;
int N;
} DBC;
union
{
struct
{
unsigned char L;
unsigned char H;
unsigned short unused;
} B;
int N;
} DAC;
union
{
struct
{
unsigned char B0;
unsigned char B1;
unsigned char B2;
unsigned char B3;
} B;
unsigned int N;
} HEAD;
union
{
struct
{
unsigned char L;
unsigned char H;
unsigned short unused;
} B;
int N;
} PT;
union
{
struct
{
unsigned char L;
unsigned char H;
unsigned short unused;
} B;
int N;
} WA;
union
{
struct
{
unsigned char B0;
unsigned char B1;
unsigned char B2;
unsigned char B3;
} B;
unsigned int N;
} STAT;
unsigned int SBOUT;
unsigned int IFCTRL;
union
{
struct
{
unsigned char B0;
unsigned char B1;
unsigned char B2;
unsigned char B3;
} B;
unsigned int N;
} CTRL;
unsigned int Decode_Reg_Read;
} CDC;
typedef struct
{
// unsigned short Fader; // 34
// unsigned short Control; // 36
// unsigned short Cur_Comm;// unused
// "Receive status"
unsigned short Status;
unsigned short Minute;
unsigned short Seconde;
unsigned short Frame;
unsigned char Ext;
unsigned char pad[3];
} CDD;
PICO_INTERNAL_ASM unsigned short Read_CDC_Host(int is_sub);
PICO_INTERNAL void LC89510_Reset(void);
PICO_INTERNAL void Update_CDC_TRansfer(int which);
PICO_INTERNAL void CDC_Update_Header(void);
PICO_INTERNAL unsigned char CDC_Read_Reg(void);
PICO_INTERNAL void CDC_Write_Reg(unsigned char Data);
PICO_INTERNAL void CDD_Export_Status(void);
PICO_INTERNAL void CDD_Import_Command(void);
#ifdef __cplusplus
};
#endif
#endif

332
pico/cd/area.c Normal file
View file

@ -0,0 +1,332 @@
// Savestate handling for emulated Sega/Mega CD machine.
// (c) Copyright 2007, Grazvydas "notaz" Ignotas
#include "../pico_int.h"
// ym2612
#include "../sound/ym2612.h"
// sn76496
extern int *sn76496_regs;
carthw_state_chunk *carthw_chunks;
void (*PicoStateProgressCB)(const char *str) = 0;
typedef enum {
CHUNK_M68K = 1,
CHUNK_RAM,
CHUNK_VRAM,
CHUNK_ZRAM,
CHUNK_CRAM, // 5
CHUNK_VSRAM,
CHUNK_MISC,
CHUNK_VIDEO,
CHUNK_Z80,
CHUNK_PSG, // 10
CHUNK_FM,
// CD stuff
CHUNK_S68K,
CHUNK_PRG_RAM,
CHUNK_WORD_RAM,
CHUNK_PCM_RAM, // 15
CHUNK_BRAM,
CHUNK_GA_REGS,
CHUNK_PCM,
CHUNK_CDC,
CHUNK_CDD, // 20
CHUNK_SCD,
CHUNK_RC,
CHUNK_MISC_CD,
CHUNK_DEFAULT_COUNT
// CHUNK_CARTHW = 64, // defined in PicoInt
} chunk_name_e;
static char *chunk_names[] = {
"INVALID!",
"Saving.. M68K state",
"Saving.. RAM",
"Saving.. VRAM",
"Saving.. ZRAM",
"Saving.. CRAM", // 5
"Saving.. VSRAM",
"Saving.. emu state",
"Saving.. VIDEO",
"Saving.. Z80 state",
"Saving.. PSG", // 10
"Saving.. FM",
// CD stuff
"Saving.. S68K state",
"Saving.. PRG_RAM",
"Saving.. WORD_RAM",
"Saving.. PCM_RAM", // 15
"Saving.. BRAM",
"Saving.. GATE ARRAY regs",
"Saving.. PCM state",
"Saving.. CDC",
"Saving.. CDD", // 20
"Saving.. SCD",
"Saving.. GFX chip",
"Saving.. MCD state",
};
static int write_chunk(chunk_name_e name, int len, void *data, void *file)
{
size_t bwritten = 0;
bwritten += areaWrite(&name, 1, 1, file);
bwritten += areaWrite(&len, 1, 4, file);
bwritten += areaWrite(data, 1, len, file);
return (bwritten == len + 4 + 1);
}
#define CHECKED_WRITE(name,len,data) { \
if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) \
PicoStateProgressCB(chunk_names[name]); \
if (!write_chunk(name, len, data, file)) return 1; \
}
#define CHECKED_WRITE_BUFF(name,buff) { \
if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) \
PicoStateProgressCB(chunk_names[name]); \
if (!write_chunk(name, sizeof(buff), &buff, file)) return 1; \
}
PICO_INTERNAL int PicoCdSaveState(void *file)
{
unsigned char buff[0x60];
void *ym2612_regs = YM2612GetRegs();
areaWrite("PicoSEXT", 1, 8, file);
areaWrite(&PicoVer, 1, 4, file);
memset(buff, 0, sizeof(buff));
PicoAreaPackCpu(buff, 0);
CHECKED_WRITE_BUFF(CHUNK_M68K, buff);
CHECKED_WRITE_BUFF(CHUNK_RAM, Pico.ram);
CHECKED_WRITE_BUFF(CHUNK_VRAM, Pico.vram);
CHECKED_WRITE_BUFF(CHUNK_ZRAM, Pico.zram);
CHECKED_WRITE_BUFF(CHUNK_CRAM, Pico.cram);
CHECKED_WRITE_BUFF(CHUNK_VSRAM, Pico.vsram);
CHECKED_WRITE_BUFF(CHUNK_MISC, Pico.m);
CHECKED_WRITE_BUFF(CHUNK_VIDEO, Pico.video);
if (PicoOpt&7) {
memset(buff, 0, sizeof(buff));
z80_pack(buff);
CHECKED_WRITE_BUFF(CHUNK_Z80, buff);
}
if (PicoOpt&3)
CHECKED_WRITE(CHUNK_PSG, 28*4, sn76496_regs);
if (PicoOpt&1) {
ym2612_pack_state();
CHECKED_WRITE(CHUNK_FM, 0x200+4, ym2612_regs);
}
if (PicoAHW & PAHW_MCD)
{
memset(buff, 0, sizeof(buff));
PicoAreaPackCpu(buff, 1);
if (Pico_mcd->s68k_regs[3]&4) // 1M mode?
wram_1M_to_2M(Pico_mcd->word_ram2M);
Pico_mcd->m.hint_vector = *(unsigned short *)(Pico_mcd->bios + 0x72);
CHECKED_WRITE_BUFF(CHUNK_S68K, buff);
CHECKED_WRITE_BUFF(CHUNK_PRG_RAM, Pico_mcd->prg_ram);
CHECKED_WRITE_BUFF(CHUNK_WORD_RAM, Pico_mcd->word_ram2M); // in 2M format
CHECKED_WRITE_BUFF(CHUNK_PCM_RAM, Pico_mcd->pcm_ram);
CHECKED_WRITE_BUFF(CHUNK_BRAM, Pico_mcd->bram);
CHECKED_WRITE_BUFF(CHUNK_GA_REGS, Pico_mcd->s68k_regs); // GA regs, not CPU regs
CHECKED_WRITE_BUFF(CHUNK_PCM, Pico_mcd->pcm);
CHECKED_WRITE_BUFF(CHUNK_CDD, Pico_mcd->cdd);
CHECKED_WRITE_BUFF(CHUNK_CDC, Pico_mcd->cdc);
CHECKED_WRITE_BUFF(CHUNK_SCD, Pico_mcd->scd);
CHECKED_WRITE_BUFF(CHUNK_RC, Pico_mcd->rot_comp);
CHECKED_WRITE_BUFF(CHUNK_MISC_CD, Pico_mcd->m);
if (Pico_mcd->s68k_regs[3]&4) // convert back
wram_2M_to_1M(Pico_mcd->word_ram2M);
}
if (carthw_chunks != NULL)
{
carthw_state_chunk *chwc;
if (PicoStateProgressCB)
PicoStateProgressCB("Saving.. cart hw state");
for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++)
CHECKED_WRITE(chwc->chunk, chwc->size, chwc->ptr);
}
return 0;
}
static int g_read_offs = 0;
#define R_ERROR_RETURN(error) \
{ \
elprintf(EL_STATUS, "PicoCdLoadState @ %x: " error, g_read_offs); \
return 1; \
}
// when is eof really set?
#define CHECKED_READ(len,data) \
if (areaRead(data, 1, len, file) != len) { \
if (len == 1 && areaEof(file)) goto readend; \
R_ERROR_RETURN("areaRead: premature EOF\n"); \
return 1; \
} \
g_read_offs += len;
#define CHECKED_READ2(len2,data) \
if (len2 != len) { \
elprintf(EL_STATUS, "unexpected len %i, wanted %i (%s)", len, len2, #len2); \
if (len > len2) R_ERROR_RETURN("failed."); \
/* else read anyway and hope for the best.. */ \
} \
CHECKED_READ(len, data)
#define CHECKED_READ_BUFF(buff) CHECKED_READ2(sizeof(buff), &buff);
PICO_INTERNAL int PicoCdLoadState(void *file)
{
unsigned char buff[0x60], buff_m68k[0x60], buff_s68k[0x60];
int ver, len;
void *ym2612_regs = YM2612GetRegs();
g_read_offs = 0;
CHECKED_READ(8, buff);
if (strncmp((char *)buff, "PicoSMCD", 8) && strncmp((char *)buff, "PicoSEXT", 8))
R_ERROR_RETURN("bad header");
CHECKED_READ(4, &ver);
while (!areaEof(file))
{
CHECKED_READ(1, buff);
CHECKED_READ(4, &len);
if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");
if (buff[0] > CHUNK_FM && buff[0] <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))
R_ERROR_RETURN("cd chunk in non CD state?");
switch (buff[0])
{
case CHUNK_M68K:
CHECKED_READ_BUFF(buff_m68k);
break;
case CHUNK_Z80:
CHECKED_READ_BUFF(buff);
z80_unpack(buff);
break;
case CHUNK_RAM: CHECKED_READ_BUFF(Pico.ram); break;
case CHUNK_VRAM: CHECKED_READ_BUFF(Pico.vram); break;
case CHUNK_ZRAM: CHECKED_READ_BUFF(Pico.zram); break;
case CHUNK_CRAM: CHECKED_READ_BUFF(Pico.cram); break;
case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); break;
case CHUNK_MISC: CHECKED_READ_BUFF(Pico.m); break;
case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); break;
case CHUNK_PSG: CHECKED_READ2(28*4, sn76496_regs); break;
case CHUNK_FM:
CHECKED_READ2(0x200+4, ym2612_regs);
ym2612_unpack_state();
break;
// cd stuff
case CHUNK_S68K:
CHECKED_READ_BUFF(buff_s68k);
break;
case CHUNK_PRG_RAM: CHECKED_READ_BUFF(Pico_mcd->prg_ram); break;
case CHUNK_WORD_RAM: CHECKED_READ_BUFF(Pico_mcd->word_ram2M); break;
case CHUNK_PCM_RAM: CHECKED_READ_BUFF(Pico_mcd->pcm_ram); break;
case CHUNK_BRAM: CHECKED_READ_BUFF(Pico_mcd->bram); break;
case CHUNK_GA_REGS: CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break;
case CHUNK_PCM: CHECKED_READ_BUFF(Pico_mcd->pcm); break;
case CHUNK_CDD: CHECKED_READ_BUFF(Pico_mcd->cdd); break;
case CHUNK_CDC: CHECKED_READ_BUFF(Pico_mcd->cdc); break;
case CHUNK_SCD: CHECKED_READ_BUFF(Pico_mcd->scd); break;
case CHUNK_RC: CHECKED_READ_BUFF(Pico_mcd->rot_comp); break;
case CHUNK_MISC_CD: CHECKED_READ_BUFF(Pico_mcd->m); break;
default:
if (carthw_chunks != NULL)
{
carthw_state_chunk *chwc;
for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++) {
if (chwc->chunk == buff[0]) {
CHECKED_READ2(chwc->size, chwc->ptr);
goto breakswitch;
}
}
}
elprintf(EL_STATUS, "PicoCdLoadState: skipping unknown chunk %i of size %i", buff[0], len);
areaSeek(file, len, SEEK_CUR);
break;
}
breakswitch:;
}
readend:
if (PicoAHW & PAHW_MCD)
{
/* after load events */
if (Pico_mcd->s68k_regs[3]&4) // 1M mode?
wram_2M_to_1M(Pico_mcd->word_ram2M);
PicoMemResetCD(Pico_mcd->s68k_regs[3]);
#ifdef _ASM_CD_MEMORY_C
if (Pico_mcd->s68k_regs[3]&4)
PicoMemResetCDdecode(Pico_mcd->s68k_regs[3]);
#endif
if (!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))
cdda_start_play();
// restore hint vector
*(unsigned short *)(Pico_mcd->bios + 0x72) = Pico_mcd->m.hint_vector;
// must unpack after other CD stuff is loaded
PicoAreaUnpackCpu(buff_s68k, 1);
}
PicoAreaUnpackCpu(buff_m68k, 0);
return 0;
}
int PicoCdLoadStateGfx(void *file)
{
int ver, len, found = 0;
char buff[8];
g_read_offs = 0;
CHECKED_READ(8, buff);
if (strncmp((char *)buff, "PicoSMCD", 8) && strncmp((char *)buff, "PicoSEXT", 8))
R_ERROR_RETURN("bad header");
CHECKED_READ(4, &ver);
while (!areaEof(file) && found < 4)
{
CHECKED_READ(1, buff);
CHECKED_READ(4, &len);
if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");
if (buff[0] > CHUNK_FM && buff[0] <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))
R_ERROR_RETURN("cd chunk in non CD state?");
switch (buff[0])
{
case CHUNK_VRAM: CHECKED_READ_BUFF(Pico.vram); found++; break;
case CHUNK_CRAM: CHECKED_READ_BUFF(Pico.cram); found++; break;
case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); found++; break;
case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); found++; break;
default:
areaSeek(file, len, SEEK_CUR);
break;
}
}
readend:
return 0;
}

144
pico/cd/buffering.c Normal file
View file

@ -0,0 +1,144 @@
// Buffering handling
// (c) Copyright 2007, Grazvydas "notaz" Ignotas
#include "../pico_int.h"
int PicoCDBuffers = 0;
static unsigned char *cd_buffer = NULL;
static int prev_lba = 0x80000000;
static int hits, reads;
void PicoCDBufferInit(void)
{
void *tmp = NULL;
prev_lba = 0x80000000;
hits = reads = 0;
if (PicoCDBuffers <= 1) {
PicoCDBuffers = 0;
return; /* buffering off */
}
/* try alloc'ing until we succeed */
while (PicoCDBuffers > 0)
{
tmp = realloc(cd_buffer, PicoCDBuffers * 2048 + 304);
if (tmp != NULL) break;
PicoCDBuffers >>= 1;
}
if (PicoCDBuffers <= 0) return; /* buffering became off */
cd_buffer = tmp;
}
void PicoCDBufferFree(void)
{
if (cd_buffer) {
free(cd_buffer);
cd_buffer = NULL;
}
if (reads)
elprintf(EL_STATUS, "CD buffer hits: %i/%i (%i%%)\n", hits, reads, hits * 100 / reads);
}
void PicoCDBufferFlush(void)
{
prev_lba = 0x80000000;
}
/* this is was a try to fight slow SD access of GP2X */
PICO_INTERNAL void PicoCDBufferRead(void *dest, int lba)
{
int is_bin, offs, read_len, moved = 0;
reads++;
is_bin = Pico_mcd->TOC.Tracks[0].ftype == TYPE_BIN;
if (PicoCDBuffers <= 0)
{
/* no buffering */
int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
pm_read(dest, 2048, Pico_mcd->TOC.Tracks[0].F);
return;
}
/* hit? */
offs = lba - prev_lba;
if (offs >= 0 && offs < PicoCDBuffers)
{
hits++;
if (offs == 0) dprintf("CD buffer seek to old %i -> %i\n", prev_lba, lba);
memcpy32(dest, (int *)(cd_buffer + offs*2048), 2048/4);
return;
}
if (prev_lba + PicoCDBuffers != lba)
{
int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
dprintf("CD buffer seek %i -> %i\n", prev_lba, lba);
pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
}
dprintf("CD buffer miss %i -> %i\n", prev_lba, lba);
if (lba < prev_lba && prev_lba - lba < PicoCDBuffers)
{
read_len = prev_lba - lba;
dprintf("CD buffer move=%i, read_len=%i", PicoCDBuffers - read_len, read_len);
memmove(cd_buffer + read_len*2048, cd_buffer, (PicoCDBuffers - read_len)*2048);
moved = 1;
}
else
{
read_len = PicoCDBuffers;
}
if (PicoMessage != NULL && read_len >= 512)
{
PicoMessage("Buffering data...");
}
if (is_bin)
{
int i = 0;
#if REDUCE_IO_CALLS
int bufs = (read_len*2048) / (2048+304);
pm_read(cd_buffer, bufs*(2048+304), Pico_mcd->TOC.Tracks[0].F);
for (i = 1; i < bufs; i++)
// should really use memmove here, but my memcpy32 implementation is also suitable here
memcpy32((int *)(cd_buffer + i*2048), (int *)(cd_buffer + i*(2048+304)), 2048/4);
#endif
for (; i < read_len - 1; i++)
{
pm_read(cd_buffer + i*2048, 2048 + 304, Pico_mcd->TOC.Tracks[0].F);
// pm_seek(Pico_mcd->TOC.Tracks[0].F, 304, SEEK_CUR); // seeking is slower, in PSP case even more
}
// further data might be moved, do not overwrite
pm_read(cd_buffer + i*2048, 2048, Pico_mcd->TOC.Tracks[0].F);
pm_seek(Pico_mcd->TOC.Tracks[0].F, 304, SEEK_CUR);
}
else
{
pm_read(cd_buffer, read_len*2048, Pico_mcd->TOC.Tracks[0].F);
}
memcpy32(dest, (int *) cd_buffer, 2048/4);
prev_lba = lba;
if (moved)
{
/* file pointer must point to the same data in file, as would-be data after our buffer */
int where_seek;
lba += PicoCDBuffers;
where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
}
}

374
pico/cd/cd_file.c Normal file
View file

@ -0,0 +1,374 @@
/***********************************************************
* *
* This source was taken from the Gens project *
* Written by Stéphane Dallongeville *
* Copyright (c) 2002 by Stéphane Dallongeville *
* Modified/adapted for PicoDrive by notaz, 2007 *
* *
***********************************************************/
#include "../pico_int.h"
#include "cd_file.h"
#include "cue.h"
//#define cdprintf(f,...) printf(f "\n",##__VA_ARGS__) // tmp
static int audio_track_mp3(const char *fname, int index)
{
_scd_track *Tracks = Pico_mcd->TOC.Tracks;
FILE *tmp_file;
int fs, ret;
tmp_file = fopen(fname, "rb");
if (tmp_file == NULL)
return -1;
ret = fseek(tmp_file, 0, SEEK_END);
fs = ftell(tmp_file); // used to calculate length
fseek(tmp_file, 0, SEEK_SET);
#if DONT_OPEN_MANY_FILES
// some systems (like PSP) can't have many open files at a time,
// so we work with their names instead.
fclose(tmp_file);
tmp_file = (void *) strdup(fname);
#endif
Tracks[index].KBtps = (short) mp3_get_bitrate(tmp_file, fs);
Tracks[index].KBtps >>= 3;
if (ret != 0 || Tracks[index].KBtps <= 0)
{
elprintf(EL_STATUS, "track %2i: mp3 bitrate %i", index+1, Tracks[index].KBtps);
#if !DONT_OPEN_MANY_FILES
fclose(tmp_file);
#else
free(tmp_file);
#endif
return -1;
}
Tracks[index].F = tmp_file;
// MP3 File
Tracks[index].ftype = TYPE_MP3;
fs *= 75;
fs /= Tracks[index].KBtps * 1000;
Tracks[index].Length = fs;
Tracks[index].Offset = 0;
return 0;
}
PICO_INTERNAL int Load_CD_Image(const char *cd_img_name, cd_img_type type)
{
int i, j, num_track, Cur_LBA, index, ret, iso_name_len, missed, cd_img_sectors;
_scd_track *Tracks = Pico_mcd->TOC.Tracks;
char tmp_name[1024], tmp_ext[10];
cue_data_t *cue_data = NULL;
pm_file *pmf;
static char *exts[] = {
"%02d.mp3", " %02d.mp3", "-%02d.mp3", "_%02d.mp3", " - %02d.mp3",
"%d.mp3", " %d.mp3", "-%d.mp3", "_%d.mp3", " - %d.mp3",
#if CASE_SENSITIVE_FS
"%02d.MP3", " %02d.MP3", "-%02d.MP3", "_%02d.MP3", " - %02d.MP3",
#endif
};
if (PicoCDLoadProgressCB != NULL) PicoCDLoadProgressCB(1);
Unload_ISO();
/* is this .cue? */
ret = strlen(cd_img_name);
if (ret >= 3 && strcasecmp(cd_img_name + ret - 3, "cue") == 0)
cue_data = cue_parse(cd_img_name);
if (cue_data != NULL)
cd_img_name = cue_data->tracks[1].fname;
Tracks[0].ftype = type == CIT_BIN ? TYPE_BIN : TYPE_ISO;
Tracks[0].F = pmf = pm_open(cd_img_name);
if (Tracks[0].F == NULL)
{
Tracks[0].ftype = 0;
Tracks[0].Length = 0;
if (cue_data != NULL)
cue_destroy(cue_data);
return -1;
}
if (Tracks[0].ftype == TYPE_ISO)
cd_img_sectors = pmf->size >>= 11; // size in sectors
else cd_img_sectors = pmf->size /= 2352;
Tracks[0].Offset = 0;
Tracks[0].MSF.M = 0; // minutes
Tracks[0].MSF.S = 2; // seconds
Tracks[0].MSF.F = 0; // frames
elprintf(EL_STATUS, "Track 1: %02d:%02d:%02d %9i DATA",
Tracks[0].MSF.M, Tracks[0].MSF.S, Tracks[0].MSF.F, Tracks[0].Length);
Cur_LBA = Tracks[0].Length = cd_img_sectors;
if (cue_data != NULL)
{
if (cue_data->tracks[2].fname == NULL) { // NULL means track2 is in same file as track1
Cur_LBA = Tracks[0].Length = cue_data->tracks[2].sector_offset;
}
i = 100 / cue_data->track_count+1;
for (num_track = 2; num_track <= cue_data->track_count; num_track++)
{
if (PicoCDLoadProgressCB != NULL) PicoCDLoadProgressCB(i * num_track);
index = num_track - 1;
Cur_LBA += cue_data->tracks[num_track].pregap;
if (cue_data->tracks[num_track].type == CT_MP3) {
ret = audio_track_mp3(cue_data->tracks[num_track].fname, index);
if (ret != 0) break;
}
else
{
Tracks[index].ftype = cue_data->tracks[num_track].type;
if (cue_data->tracks[num_track].fname != NULL)
{
pm_file *pmfn = pm_open(cue_data->tracks[num_track].fname);
if (pmfn != NULL)
{
// addume raw, ignore header for wav..
Tracks[index].F = pmfn;
Tracks[index].Length = pmfn->size / 2352;
Tracks[index].Offset = cue_data->tracks[num_track].sector_offset;
}
else
{
elprintf(EL_STATUS, "track %2i (%s): can't determine length",
num_track, cue_data->tracks[num_track].fname);
Tracks[index].Length = 2*75;
Tracks[index].Offset = 0;
}
}
else
{
if (num_track < cue_data->track_count)
Tracks[index].Length = cue_data->tracks[num_track+1].sector_offset -
cue_data->tracks[num_track].sector_offset;
else
Tracks[index].Length = cd_img_sectors - cue_data->tracks[num_track].sector_offset;
Tracks[index].Offset = cue_data->tracks[num_track].sector_offset;
}
}
if (cue_data->tracks[num_track].sector_xlength != 0)
// overriden by custom cue command
Tracks[index].Length = cue_data->tracks[num_track].sector_xlength;
LBA_to_MSF(Cur_LBA, &Tracks[index].MSF);
Cur_LBA += Tracks[index].Length;
elprintf(EL_STATUS, "Track %2i: %02d:%02d:%02d %9i AUDIO - %s", num_track, Tracks[index].MSF.M,
Tracks[index].MSF.S, Tracks[index].MSF.F, Tracks[index].Length,
cue_data->tracks[num_track].fname);
}
cue_destroy(cue_data);
goto finish;
}
/* mp3 track autosearch, Gens-like */
iso_name_len = strlen(cd_img_name);
if (iso_name_len >= sizeof(tmp_name))
iso_name_len = sizeof(tmp_name) - 1;
for (num_track = 2, i = 0, missed = 0; i < 100 && missed < 4; i++)
{
if (PicoCDLoadProgressCB != NULL && i > 1) PicoCDLoadProgressCB(i + (100-i)*missed/4);
for (j = 0; j < sizeof(exts)/sizeof(char *); j++)
{
int ext_len;
sprintf(tmp_ext, exts[j], i);
ext_len = strlen(tmp_ext);
memcpy(tmp_name, cd_img_name, iso_name_len + 1);
tmp_name[iso_name_len - 4] = 0;
strcat(tmp_name, tmp_ext);
index = num_track - 1;
ret = audio_track_mp3(tmp_name, index);
if (ret != 0 && i > 1 && iso_name_len > ext_len) {
tmp_name[iso_name_len - ext_len] = 0;
strcat(tmp_name, tmp_ext);
ret = audio_track_mp3(tmp_name, index);
}
if (ret == 0)
{
LBA_to_MSF(Cur_LBA, &Tracks[index].MSF);
Cur_LBA += Tracks[index].Length;
elprintf(EL_STATUS, "Track %2i: %02d:%02d:%02d %9i AUDIO - %s", num_track, Tracks[index].MSF.M,
Tracks[index].MSF.S, Tracks[index].MSF.F, Tracks[index].Length, tmp_name);
num_track++;
missed = 0;
break;
}
}
if (ret != 0 && i > 1) missed++;
}
finish:
Pico_mcd->TOC.Last_Track = num_track - 1;
index = num_track - 1;
LBA_to_MSF(Cur_LBA, &Tracks[index].MSF);
elprintf(EL_STATUS, "End CD - %02d:%02d:%02d\n", Tracks[index].MSF.M,
Tracks[index].MSF.S, Tracks[index].MSF.F);
if (PicoCDLoadProgressCB != NULL) PicoCDLoadProgressCB(100);
return 0;
}
PICO_INTERNAL void Unload_ISO(void)
{
int i;
if (Pico_mcd == NULL) return;
if (Pico_mcd->TOC.Tracks[0].F) pm_close(Pico_mcd->TOC.Tracks[0].F);
for(i = 1; i < 100; i++)
{
if (Pico_mcd->TOC.Tracks[i].F != NULL)
{
if (Pico_mcd->TOC.Tracks[i].ftype == TYPE_MP3)
#if DONT_OPEN_MANY_FILES
free(Pico_mcd->TOC.Tracks[i].F);
#else
fclose(Pico_mcd->TOC.Tracks[i].F);
#endif
else
pm_close(Pico_mcd->TOC.Tracks[i].F);
}
}
memset(Pico_mcd->TOC.Tracks, 0, sizeof(Pico_mcd->TOC.Tracks));
}
PICO_INTERNAL int FILE_Read_One_LBA_CDC(void)
{
if (Pico_mcd->s68k_regs[0x36] & 1) // DATA
{
if (Pico_mcd->TOC.Tracks[0].F == NULL) return -1;
// moved below..
//fseek(Pico_mcd->TOC.Tracks[0].F, where_read, SEEK_SET);
//fread(cp_buf, 1, 2048, Pico_mcd->TOC.Tracks[0].F);
cdprintf("Read file CDC 1 data sector :\n");
}
else // AUDIO
{
cdprintf("Read file CDC 1 audio sector :\n");
}
// Update CDC stuff
CDC_Update_Header();
if (Pico_mcd->s68k_regs[0x36] & 1) // DATA track
{
if (Pico_mcd->cdc.CTRL.B.B0 & 0x80) // DECEN = decoding enable
{
if (Pico_mcd->cdc.CTRL.B.B0 & 0x04) // WRRQ : this bit enable write to buffer
{
int where_read = 0;
// CAUTION : lookahead bit not implemented
if (Pico_mcd->scd.Cur_LBA < 0)
where_read = 0;
else if (Pico_mcd->scd.Cur_LBA >= Pico_mcd->TOC.Tracks[0].Length)
where_read = Pico_mcd->TOC.Tracks[0].Length - 1;
else where_read = Pico_mcd->scd.Cur_LBA;
Pico_mcd->scd.Cur_LBA++;
Pico_mcd->cdc.WA.N = (Pico_mcd->cdc.WA.N + 2352) & 0x7FFF; // add one sector to WA
Pico_mcd->cdc.PT.N = (Pico_mcd->cdc.PT.N + 2352) & 0x7FFF;
*(unsigned int *)(Pico_mcd->cdc.Buffer + Pico_mcd->cdc.PT.N) = Pico_mcd->cdc.HEAD.N;
//memcpy(&Pico_mcd->cdc.Buffer[Pico_mcd->cdc.PT.N + 4], cp_buf, 2048);
//pm_seek(Pico_mcd->TOC.Tracks[0].F, where_read, SEEK_SET);
//pm_read(Pico_mcd->cdc.Buffer + Pico_mcd->cdc.PT.N + 4, 2048, Pico_mcd->TOC.Tracks[0].F);
PicoCDBufferRead(Pico_mcd->cdc.Buffer + Pico_mcd->cdc.PT.N + 4, where_read);
cdprintf("Read -> WA = %d Buffer[%d] =", Pico_mcd->cdc.WA.N, Pico_mcd->cdc.PT.N & 0x3FFF);
cdprintf("Header 1 = %.2X %.2X %.2X %.2X", Pico_mcd->cdc.HEAD.B.B0,
Pico_mcd->cdc.HEAD.B.B1, Pico_mcd->cdc.HEAD.B.B2, Pico_mcd->cdc.HEAD.B.B3);
cdprintf("Header 2 = %.2X %.2X %.2X %.2X --- %.2X %.2X\n\n",
Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 0) & 0x3FFF],
Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 1) & 0x3FFF],
Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 2) & 0x3FFF],
Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 3) & 0x3FFF],
Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 4) & 0x3FFF],
Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 5) & 0x3FFF]);
}
}
}
else // music track
{
Pico_mcd->scd.Cur_LBA++;
Pico_mcd->cdc.WA.N = (Pico_mcd->cdc.WA.N + 2352) & 0x7FFF; // add one sector to WA
Pico_mcd->cdc.PT.N = (Pico_mcd->cdc.PT.N + 2352) & 0x7FFF;
if (Pico_mcd->cdc.CTRL.B.B0 & 0x80) // DECEN = decoding enable
{
if (Pico_mcd->cdc.CTRL.B.B0 & 0x04) // WRRQ : this bit enable write to buffer
{
// CAUTION : lookahead bit not implemented
// this is pretty rough, but oh well - not much depends on this anyway
memcpy(&Pico_mcd->cdc.Buffer[Pico_mcd->cdc.PT.N], cdda_out_buffer, 2352);
}
}
}
if (Pico_mcd->cdc.CTRL.B.B0 & 0x80) // DECEN = decoding enable
{
Pico_mcd->cdc.STAT.B.B0 = 0x80;
if (Pico_mcd->cdc.CTRL.B.B0 & 0x10) // determine form bit form sub header ?
{
Pico_mcd->cdc.STAT.B.B2 = Pico_mcd->cdc.CTRL.B.B1 & 0x08;
}
else
{
Pico_mcd->cdc.STAT.B.B2 = Pico_mcd->cdc.CTRL.B.B1 & 0x0C;
}
if (Pico_mcd->cdc.CTRL.B.B0 & 0x02) Pico_mcd->cdc.STAT.B.B3 = 0x20; // ECC done
else Pico_mcd->cdc.STAT.B.B3 = 0x00; // ECC not done
if (Pico_mcd->cdc.IFCTRL & 0x20)
{
if (Pico_mcd->s68k_regs[0x33] & (1<<5))
{
elprintf(EL_INTS, "cdc dec irq 5");
SekInterruptS68k(5);
}
Pico_mcd->cdc.IFSTAT &= ~0x20; // DEC interrupt happen
Pico_mcd->cdc.Decode_Reg_Read = 0; // Reset read after DEC int
}
}
return 0;
}

32
pico/cd/cd_file.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef _CD_FILE_H
#define _CD_FILE_H
#ifdef __cplusplus
extern "C" {
#endif
#define TYPE_ISO 1
#define TYPE_BIN 2
#define TYPE_MP3 3
#define TYPE_WAV 4
typedef enum
{
CIT_NOT_CD = 0,
CIT_ISO,
CIT_BIN,
CIT_CUE
}
cd_img_type;
PICO_INTERNAL int Load_CD_Image(const char *iso_name, cd_img_type type);
PICO_INTERNAL void Unload_ISO(void);
PICO_INTERNAL int FILE_Read_One_LBA_CDC(void);
#ifdef __cplusplus
};
#endif
#endif

739
pico/cd/cd_sys.c Normal file
View file

@ -0,0 +1,739 @@
/***********************************************************
* *
* This source file was taken from the Gens project *
* Written by Stéphane Dallongeville *
* Copyright (c) 2002 by Stéphane Dallongeville *
* Modified/adapted for PicoDrive by notaz, 2007 *
* *
***********************************************************/
#include <stdio.h>
#include "../pico_int.h"
#include "cd_sys.h"
#include "cd_file.h"
#define DEBUG_CD
#define TRAY_OPEN 0x0500 // TRAY OPEN CDD status
#define NOCD 0x0000 // CD removed CDD status
#define STOPPED 0x0900 // STOPPED CDD status (happen after stop or close tray command)
#define READY 0x0400 // READY CDD status (also used for seeking)
#define FAST_FOW 0x0300 // FAST FORWARD track CDD status
#define FAST_REV 0x10300 // FAST REVERSE track CDD status
#define PLAYING 0x0100 // PLAYING audio track CDD status
static int CD_Present = 0;
#define CHECK_TRAY_OPEN \
if (Pico_mcd->scd.Status_CDD == TRAY_OPEN) \
{ \
Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD; \
\
Pico_mcd->cdd.Minute = 0; \
Pico_mcd->cdd.Seconde = 0; \
Pico_mcd->cdd.Frame = 0; \
Pico_mcd->cdd.Ext = 0; \
\
Pico_mcd->scd.CDD_Complete = 1; \
\
return 2; \
}
#define CHECK_CD_PRESENT \
if (!CD_Present) \
{ \
Pico_mcd->scd.Status_CDD = NOCD; \
Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD; \
\
Pico_mcd->cdd.Minute = 0; \
Pico_mcd->cdd.Seconde = 0; \
Pico_mcd->cdd.Frame = 0; \
Pico_mcd->cdd.Ext = 0; \
\
Pico_mcd->scd.CDD_Complete = 1; \
\
return 3; \
}
static int MSF_to_LBA(_msf *MSF)
{
return (MSF->M * 60 * 75) + (MSF->S * 75) + MSF->F - 150;
}
PICO_INTERNAL void LBA_to_MSF(int lba, _msf *MSF)
{
if (lba < -150) lba = 0;
else lba += 150;
MSF->M = lba / (60 * 75);
MSF->S = (lba / 75) % 60;
MSF->F = lba % 75;
}
static unsigned int MSF_to_Track(_msf *MSF)
{
int i, Start, Cur;
Start = (MSF->M << 16) + (MSF->S << 8) + MSF->F;
for(i = 1; i <= (Pico_mcd->TOC.Last_Track + 1); i++)
{
Cur = Pico_mcd->TOC.Tracks[i - 1].MSF.M << 16;
Cur += Pico_mcd->TOC.Tracks[i - 1].MSF.S << 8;
Cur += Pico_mcd->TOC.Tracks[i - 1].MSF.F;
if (Cur > Start) break;
}
--i;
if (i > Pico_mcd->TOC.Last_Track) return 100;
else if (i < 1) i = 1;
return (unsigned) i;
}
static unsigned int LBA_to_Track(int lba)
{
_msf MSF;
LBA_to_MSF(lba, &MSF);
return MSF_to_Track(&MSF);
}
static void Track_to_MSF(int track, _msf *MSF)
{
if (track < 1) track = 1;
else if (track > Pico_mcd->TOC.Last_Track) track = Pico_mcd->TOC.Last_Track;
MSF->M = Pico_mcd->TOC.Tracks[track - 1].MSF.M;
MSF->S = Pico_mcd->TOC.Tracks[track - 1].MSF.S;
MSF->F = Pico_mcd->TOC.Tracks[track - 1].MSF.F;
}
PICO_INTERNAL int Track_to_LBA(int track)
{
_msf MSF;
Track_to_MSF(track, &MSF);
return MSF_to_LBA(&MSF);
}
PICO_INTERNAL void Check_CD_Command(void)
{
cdprintf("CHECK CD COMMAND");
// Check CDC
if (Pico_mcd->scd.Status_CDC & 1) // CDC is reading data ...
{
cdprintf("Got a read command");
// DATA ?
if (Pico_mcd->scd.Cur_Track == 1)
Pico_mcd->s68k_regs[0x36] |= 0x01;
else Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO
if (Pico_mcd->scd.File_Add_Delay == 0)
{
FILE_Read_One_LBA_CDC();
}
else Pico_mcd->scd.File_Add_Delay--;
}
// Check CDD
if (Pico_mcd->scd.CDD_Complete)
{
Pico_mcd->scd.CDD_Complete = 0;
CDD_Export_Status();
}
if (Pico_mcd->scd.Status_CDD == FAST_FOW)
{
Pico_mcd->scd.Cur_LBA += 10;
CDC_Update_Header();
}
else if (Pico_mcd->scd.Status_CDD == FAST_REV)
{
Pico_mcd->scd.Cur_LBA -= 10;
if (Pico_mcd->scd.Cur_LBA < -150) Pico_mcd->scd.Cur_LBA = -150;
CDC_Update_Header();
}
}
PICO_INTERNAL int Init_CD_Driver(void)
{
return 0;
}
PICO_INTERNAL void End_CD_Driver(void)
{
Unload_ISO();
}
PICO_INTERNAL void Reset_CD(void)
{
Pico_mcd->scd.Cur_Track = 0;
Pico_mcd->scd.Cur_LBA = -150;
Pico_mcd->scd.Status_CDC &= ~1;
Pico_mcd->scd.Status_CDD = CD_Present ? READY : NOCD;
Pico_mcd->scd.CDD_Complete = 0;
Pico_mcd->scd.File_Add_Delay = 0;
}
int Insert_CD(char *cdimg_name, int type)
{
int ret = 1;
CD_Present = 0;
Pico_mcd->scd.Status_CDD = NOCD;
if (cdimg_name != NULL && type != CIT_NOT_CD)
{
ret = Load_CD_Image(cdimg_name, type);
if (ret == 0) {
CD_Present = 1;
Pico_mcd->scd.Status_CDD = READY;
}
}
return ret;
}
void Stop_CD(void)
{
Unload_ISO();
CD_Present = 0;
}
/*
PICO_INTERNAL void Change_CD(void)
{
if (Pico_mcd->scd.Status_CDD == TRAY_OPEN) Close_Tray_CDD_cC();
else Open_Tray_CDD_cD();
}
*/
PICO_INTERNAL int Get_Status_CDD_c0(void)
{
cdprintf("Status command : Cur LBA = %d", Pico_mcd->scd.Cur_LBA);
// Clear immediat status
if ((Pico_mcd->cdd.Status & 0x0F00) == 0x0200)
Pico_mcd->cdd.Status = (Pico_mcd->scd.Status_CDD & 0xFF00) | (Pico_mcd->cdd.Status & 0x00FF);
else if ((Pico_mcd->cdd.Status & 0x0F00) == 0x0700)
Pico_mcd->cdd.Status = (Pico_mcd->scd.Status_CDD & 0xFF00) | (Pico_mcd->cdd.Status & 0x00FF);
else if ((Pico_mcd->cdd.Status & 0x0F00) == 0x0E00)
Pico_mcd->cdd.Status = (Pico_mcd->scd.Status_CDD & 0xFF00) | (Pico_mcd->cdd.Status & 0x00FF);
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Stop_CDD_c1(void)
{
CHECK_TRAY_OPEN
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read
if (CD_Present) Pico_mcd->scd.Status_CDD = STOPPED;
else Pico_mcd->scd.Status_CDD = NOCD;
Pico_mcd->cdd.Status = 0x0000;
Pico_mcd->s68k_regs[0x36] |= 0x01; // Data bit set because stopped
Pico_mcd->cdd.Minute = 0;
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Get_Pos_CDD_c20(void)
{
_msf MSF;
cdprintf("command 200 : Cur LBA = %d", Pico_mcd->scd.Cur_LBA);
CHECK_TRAY_OPEN
Pico_mcd->cdd.Status &= 0xFF;
if (!CD_Present)
{
Pico_mcd->scd.Status_CDD = NOCD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
}
// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
cdprintf("Status CDD = %.4X Status = %.4X", Pico_mcd->scd.Status_CDD, Pico_mcd->cdd.Status);
LBA_to_MSF(Pico_mcd->scd.Cur_LBA, &MSF);
Pico_mcd->cdd.Minute = INT_TO_BCDW(MSF.M);
Pico_mcd->cdd.Seconde = INT_TO_BCDW(MSF.S);
Pico_mcd->cdd.Frame = INT_TO_BCDW(MSF.F);
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Get_Track_Pos_CDD_c21(void)
{
int elapsed_time;
_msf MSF;
cdprintf("command 201 : Cur LBA = %d", Pico_mcd->scd.Cur_LBA);
CHECK_TRAY_OPEN
Pico_mcd->cdd.Status &= 0xFF;
if (!CD_Present)
{
Pico_mcd->scd.Status_CDD = NOCD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
}
// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
elapsed_time = Pico_mcd->scd.Cur_LBA - Track_to_LBA(LBA_to_Track(Pico_mcd->scd.Cur_LBA));
LBA_to_MSF(elapsed_time - 150, &MSF);
cdprintf(" elapsed = %d", elapsed_time);
Pico_mcd->cdd.Minute = INT_TO_BCDW(MSF.M);
Pico_mcd->cdd.Seconde = INT_TO_BCDW(MSF.S);
Pico_mcd->cdd.Frame = INT_TO_BCDW(MSF.F);
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Get_Current_Track_CDD_c22(void)
{
cdprintf("Status CDD = %.4X Status = %.4X", Pico_mcd->scd.Status_CDD, Pico_mcd->cdd.Status);
CHECK_TRAY_OPEN
Pico_mcd->cdd.Status &= 0xFF;
if (!CD_Present)
{
Pico_mcd->scd.Status_CDD = NOCD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
}
// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->scd.Cur_Track = LBA_to_Track(Pico_mcd->scd.Cur_LBA);
if (Pico_mcd->scd.Cur_Track == 100) Pico_mcd->cdd.Minute = 0x0A02;
else Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track);
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Get_Total_Lenght_CDD_c23(void)
{
CHECK_TRAY_OPEN
Pico_mcd->cdd.Status &= 0xFF;
if (!CD_Present)
{
Pico_mcd->scd.Status_CDD = NOCD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
}
// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->TOC.Tracks[Pico_mcd->TOC.Last_Track].MSF.M);
Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->TOC.Tracks[Pico_mcd->TOC.Last_Track].MSF.S);
Pico_mcd->cdd.Frame = INT_TO_BCDW(Pico_mcd->TOC.Tracks[Pico_mcd->TOC.Last_Track].MSF.F);
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Get_First_Last_Track_CDD_c24(void)
{
CHECK_TRAY_OPEN
Pico_mcd->cdd.Status &= 0xFF;
if (!CD_Present)
{
Pico_mcd->scd.Status_CDD = NOCD;
}
// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Minute = INT_TO_BCDW(1);
Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->TOC.Last_Track);
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Get_Track_Adr_CDD_c25(void)
{
int track_number;
CHECK_TRAY_OPEN
// track number in TC4 & TC5
track_number = (Pico_mcd->s68k_regs[0x38+10+4] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+5] & 0xF);
Pico_mcd->cdd.Status &= 0xFF;
if (!CD_Present)
{
Pico_mcd->scd.Status_CDD = NOCD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
}
// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD;
if (track_number > Pico_mcd->TOC.Last_Track) track_number = Pico_mcd->TOC.Last_Track;
else if (track_number < 1) track_number = 1;
Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->TOC.Tracks[track_number - 1].MSF.M);
Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->TOC.Tracks[track_number - 1].MSF.S);
Pico_mcd->cdd.Frame = INT_TO_BCDW(Pico_mcd->TOC.Tracks[track_number - 1].MSF.F);
Pico_mcd->cdd.Ext = track_number % 10;
if (track_number == 1) Pico_mcd->cdd.Frame |= 0x0800; // data track
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Play_CDD_c3(void)
{
_msf MSF;
int delay, new_lba;
CHECK_TRAY_OPEN
CHECK_CD_PRESENT
// MSF of the track to play in TC buffer
MSF.M = (Pico_mcd->s68k_regs[0x38+10+2] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+3] & 0xF);
MSF.S = (Pico_mcd->s68k_regs[0x38+10+4] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+5] & 0xF);
MSF.F = (Pico_mcd->s68k_regs[0x38+10+6] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+7] & 0xF);
Pico_mcd->scd.Cur_Track = MSF_to_Track(&MSF);
new_lba = MSF_to_LBA(&MSF);
delay = new_lba - Pico_mcd->scd.Cur_LBA;
if (delay < 0) delay = -delay;
delay >>= 12;
Pico_mcd->scd.Cur_LBA = new_lba;
CDC_Update_Header();
cdprintf("Read : Cur LBA = %d, M=%d, S=%d, F=%d", Pico_mcd->scd.Cur_LBA, MSF.M, MSF.S, MSF.F);
if (Pico_mcd->scd.Status_CDD != PLAYING) delay += 20;
Pico_mcd->scd.Status_CDD = PLAYING;
Pico_mcd->cdd.Status = 0x0102;
// Pico_mcd->cdd.Status = COMM_OK;
if (Pico_mcd->scd.File_Add_Delay == 0) Pico_mcd->scd.File_Add_Delay = delay;
if (Pico_mcd->scd.Cur_Track == 1)
{
Pico_mcd->s68k_regs[0x36] |= 0x01; // DATA
}
else
{
Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO
cdda_start_play();
}
if (Pico_mcd->scd.Cur_Track == 100) Pico_mcd->cdd.Minute = 0x0A02;
else Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track);
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.Status_CDC |= 1; // Read data with CDC
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Seek_CDD_c4(void)
{
_msf MSF;
CHECK_TRAY_OPEN
CHECK_CD_PRESENT
// MSF to seek in TC buffer
MSF.M = (Pico_mcd->s68k_regs[0x38+10+2] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+3] & 0xF);
MSF.S = (Pico_mcd->s68k_regs[0x38+10+4] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+5] & 0xF);
MSF.F = (Pico_mcd->s68k_regs[0x38+10+6] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+7] & 0xF);
Pico_mcd->scd.Cur_Track = MSF_to_Track(&MSF);
Pico_mcd->scd.Cur_LBA = MSF_to_LBA(&MSF);
CDC_Update_Header();
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read
Pico_mcd->scd.Status_CDD = READY;
Pico_mcd->cdd.Status = 0x0200;
// DATA ?
if (Pico_mcd->scd.Cur_Track == 1)
Pico_mcd->s68k_regs[0x36] |= 0x01;
else Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO
Pico_mcd->cdd.Minute = 0;
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Pause_CDD_c6(void)
{
CHECK_TRAY_OPEN
CHECK_CD_PRESENT
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read to start a new one if raw data
Pico_mcd->scd.Status_CDD = READY;
Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD;
Pico_mcd->s68k_regs[0x36] |= 0x01; // Data bit set because stopped
Pico_mcd->cdd.Minute = 0;
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Resume_CDD_c7(void)
{
CHECK_TRAY_OPEN
CHECK_CD_PRESENT
Pico_mcd->scd.Cur_Track = LBA_to_Track(Pico_mcd->scd.Cur_LBA);
#ifdef DEBUG_CD
{
_msf MSF;
LBA_to_MSF(Pico_mcd->scd.Cur_LBA, &MSF);
cdprintf("Resume read : Cur LBA = %d, M=%d, S=%d, F=%d", Pico_mcd->scd.Cur_LBA, MSF.M, MSF.S, MSF.F);
}
#endif
Pico_mcd->scd.Status_CDD = PLAYING;
Pico_mcd->cdd.Status = 0x0102;
if (Pico_mcd->scd.Cur_Track == 1)
{
Pico_mcd->s68k_regs[0x36] |= 0x01; // DATA
}
else
{
Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO
cdda_start_play();
}
if (Pico_mcd->scd.Cur_Track == 100) Pico_mcd->cdd.Minute = 0x0A02;
else Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track);
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.Status_CDC |= 1; // Read data with CDC
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Fast_Foward_CDD_c8(void)
{
CHECK_TRAY_OPEN
CHECK_CD_PRESENT
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read
Pico_mcd->scd.Status_CDD = FAST_FOW;
Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD | 2;
Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track);
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Fast_Rewind_CDD_c9(void)
{
CHECK_TRAY_OPEN
CHECK_CD_PRESENT
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read
Pico_mcd->scd.Status_CDD = FAST_REV;
Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD | 2;
Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track);
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Close_Tray_CDD_cC(void)
{
CD_Present = 0;
//Clear_Sound_Buffer();
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read
elprintf(EL_STATUS, "tray close\n");
if (PicoMCDcloseTray != NULL)
CD_Present = PicoMCDcloseTray();
Pico_mcd->scd.Status_CDD = CD_Present ? STOPPED : NOCD;
Pico_mcd->cdd.Status = 0x0000;
Pico_mcd->cdd.Minute = 0;
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int Open_Tray_CDD_cD(void)
{
CHECK_TRAY_OPEN
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read
elprintf(EL_STATUS, "tray open\n");
Unload_ISO();
CD_Present = 0;
if (PicoMCDopenTray != NULL)
PicoMCDopenTray();
Pico_mcd->scd.Status_CDD = TRAY_OPEN;
Pico_mcd->cdd.Status = 0x0E00;
Pico_mcd->cdd.Minute = 0;
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int CDD_cA(void)
{
CHECK_TRAY_OPEN
CHECK_CD_PRESENT
Pico_mcd->scd.Status_CDC &= ~1;
Pico_mcd->scd.Status_CDD = READY;
Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Minute = 0;
Pico_mcd->cdd.Seconde = INT_TO_BCDW(1);
Pico_mcd->cdd.Frame = INT_TO_BCDW(1);
Pico_mcd->cdd.Ext = 0;
Pico_mcd->scd.CDD_Complete = 1;
return 0;
}
PICO_INTERNAL int CDD_Def(void)
{
Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD;
Pico_mcd->cdd.Minute = 0;
Pico_mcd->cdd.Seconde = 0;
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
return 0;
}

111
pico/cd/cd_sys.h Normal file
View file

@ -0,0 +1,111 @@
/***********************************************************
* *
* This source was taken from the Gens project *
* Written by Stéphane Dallongeville *
* Copyright (c) 2002 by Stéphane Dallongeville *
* Modified/adapted for PicoDrive by notaz, 2007 *
* *
***********************************************************/
#ifndef _CD_SYS_H
#define _CD_SYS_H
#include "cd_file.h"
#ifdef __cplusplus
extern "C" {
#endif
#define INT_TO_BCDB(c) \
((c) > 99)?(0x99):((((c) / 10) << 4) + ((c) % 10));
#define INT_TO_BCDW(c) \
((c) > 99)?(0x0909):((((c) / 10) << 8) + ((c) % 10));
#define BCDB_TO_INT(c) \
(((c) >> 4) * 10) + ((c) & 0xF);
#define BCDW_TO_INT(c) \
(((c) >> 8) * 10) + ((c) & 0xF);
typedef struct
{
unsigned char M;
unsigned char S;
unsigned char F;
} _msf;
typedef struct
{
// unsigned char Type; // always 1 (data) for 1st track, 0 (audio) for others
// unsigned char Num; // unused
_msf MSF;
//
char ftype; // TYPE_ISO, TYPE_BIN, TYPE_MP3
void *F;
int Length;
int Offset; // sector offset, when single file is used for multiple virtual tracks
short KBtps; // kbytes per sec for mp3s (bitrate / 1000 / 8)
short pad;
} _scd_track;
typedef struct
{
// unsigned char First_Track; // always 1
_scd_track Tracks[100];
unsigned int Last_Track;
} _scd_toc;
typedef struct {
unsigned int Status_CDD;
unsigned int Status_CDC;
int Cur_LBA;
unsigned int Cur_Track;
int File_Add_Delay;
char CDD_Complete;
int pad[6];
} _scd;
PICO_INTERNAL void LBA_to_MSF(int lba, _msf *MSF);
PICO_INTERNAL int Track_to_LBA(int track);
// moved to pico.h
// int Insert_CD(char *iso_name, int is_bin);
// void Stop_CD(void);
PICO_INTERNAL void Check_CD_Command(void);
PICO_INTERNAL int Init_CD_Driver(void);
PICO_INTERNAL void End_CD_Driver(void);
PICO_INTERNAL void Reset_CD(void);
PICO_INTERNAL int Get_Status_CDD_c0(void);
PICO_INTERNAL int Stop_CDD_c1(void);
PICO_INTERNAL int Get_Pos_CDD_c20(void);
PICO_INTERNAL int Get_Track_Pos_CDD_c21(void);
PICO_INTERNAL int Get_Current_Track_CDD_c22(void);
PICO_INTERNAL int Get_Total_Lenght_CDD_c23(void);
PICO_INTERNAL int Get_First_Last_Track_CDD_c24(void);
PICO_INTERNAL int Get_Track_Adr_CDD_c25(void);
PICO_INTERNAL int Play_CDD_c3(void);
PICO_INTERNAL int Seek_CDD_c4(void);
PICO_INTERNAL int Pause_CDD_c6(void);
PICO_INTERNAL int Resume_CDD_c7(void);
PICO_INTERNAL int Fast_Foward_CDD_c8(void);
PICO_INTERNAL int Fast_Rewind_CDD_c9(void);
PICO_INTERNAL int CDD_cA(void);
PICO_INTERNAL int Close_Tray_CDD_cC(void);
PICO_INTERNAL int Open_Tray_CDD_cD(void);
PICO_INTERNAL int CDD_Def(void);
#ifdef __cplusplus
};
#endif
#endif

40
pico/cd/cell_map.c Normal file
View file

@ -0,0 +1,40 @@
// Convert "cell arrange" address to normal address.
// (c) Copyright 2007, Grazvydas "notaz" Ignotas
// 64 x32 x16 x8 x4 x4
static unsigned int cell_map(int celln)
{
int col, row;
switch ((celln >> 12) & 7) { // 0-0x8000
case 0: // x32 cells
case 1:
case 2:
case 3:
col = celln >> 8;
row = celln & 0xff;
break;
case 4: // x16
case 5:
col = celln >> 7;
row = celln & 0x7f;
row |= 0x10000 >> 8;
break;
case 6: // x8
col = celln >> 6;
row = celln & 0x3f;
row |= 0x18000 >> 8;
break;
case 7: // x4
col = celln >> 5;
row = celln & 0x1f;
row |= (celln & 0x7800) >> 6;
break;
default: // never happens, only here to make compiler happy
col = row = 0;
break;
}
return (col & 0x3f) + row*64;
}

268
pico/cd/cue.c Normal file
View file

@ -0,0 +1,268 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cue.h"
#include "../pico_int.h"
// #define elprintf(w,f,...) printf(f "\n",##__VA_ARGS__);
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
#ifdef __EPOC32__
#define snprintf(b,s,...) sprintf(b,##__VA_ARGS__)
#endif
static char *mystrip(char *str)
{
int i, len;
len = strlen(str);
for (i = 0; i < len; i++)
if (str[i] != ' ') break;
if (i > 0) memmove(str, str + i, len - i + 1);
len = strlen(str);
for (i = len - 1; i >= 0; i--)
if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
str[i+1] = 0;
return str;
}
static int get_token(const char *buff, char *dest, int len)
{
const char *p = buff;
char sep = ' ';
int d = 0, skip = 0;
while (*p && *p == ' ') {
skip++;
p++;
}
if (*p == '\"') {
sep = '\"';
p++;
}
while (*p && *p != sep && d < len-1)
dest[d++] = *p++;
dest[d] = 0;
if (sep == '\"' && *p != sep)
elprintf(EL_STATUS, "cue: bad token: \"%s\"", buff);
return d + skip;
}
static char *get_ext(char *fname)
{
int len = strlen(fname);
return (len >= 3) ? (fname + len - 3) : fname;
}
#define BEGINS(buff,str) (strncmp(buff,str,sizeof(str)-1) == 0)
/* note: tracks[0] is not used */
cue_data_t *cue_parse(const char *fname)
{
char buff[256], current_file[256], buff2[32], *current_filep;
FILE *f, *tmpf;
int ret, count = 0, count_alloc = 2, pending_pregap = 0;
cue_data_t *data;
void *tmp;
f = fopen(fname, "r");
if (f == NULL) return NULL;
snprintf(current_file, sizeof(current_file), "%s", fname);
for (current_filep = current_file + strlen(current_file); current_filep > current_file; current_filep--)
if (current_filep[-1] == '/' || current_filep[-1] == '\\') break;
data = calloc(1, sizeof(*data) + count_alloc * sizeof(cue_track));
if (data == NULL) {
fclose(f);
return NULL;
}
while (!feof(f))
{
tmp = fgets(buff, sizeof(buff), f);
if (tmp == NULL) break;
mystrip(buff);
if (buff[0] == 0) continue;
if (BEGINS(buff, "TITLE ") || BEGINS(buff, "PERFORMER ") || BEGINS(buff, "SONGWRITER "))
continue; /* who would put those here? Ignore! */
else if (BEGINS(buff, "FILE "))
{
get_token(buff+5, current_filep, sizeof(current_file) - (current_filep - current_file));
}
else if (BEGINS(buff, "TRACK "))
{
count++;
if (count >= count_alloc) {
count_alloc *= 2;
tmp = realloc(data, sizeof(*data) + count_alloc * sizeof(cue_track));
if (tmp == NULL) { count--; break; }
data = tmp;
}
memset(&data->tracks[count], 0, sizeof(data->tracks[0]));
if (count == 1 || strcmp(data->tracks[1].fname, current_file) != 0)
{
data->tracks[count].fname = strdup(current_file);
if (data->tracks[count].fname == NULL) break;
tmpf = fopen(current_file, "rb");
if (tmpf == NULL) {
elprintf(EL_STATUS, "cue: bad/missing file: \"%s\"", current_file);
count--; break;
}
fclose(tmpf);
}
data->tracks[count].pregap = pending_pregap;
pending_pregap = 0;
// track number
ret = get_token(buff+6, buff2, sizeof(buff2));
if (count != atoi(buff2))
elprintf(EL_STATUS, "cue: track index mismatch: track %i is track %i in cue",
count, atoi(buff2));
// check type
get_token(buff+6+ret, buff2, sizeof(buff2));
if (strcmp(buff2, "MODE1/2352") == 0)
data->tracks[count].type = CT_BIN;
else if (strcmp(buff2, "MODE1/2048") == 0)
data->tracks[count].type = CT_ISO;
else if (strcmp(buff2, "AUDIO") == 0)
{
if (data->tracks[count].fname != NULL)
{
// rely on extension, not type in cue..
char *ext = get_ext(data->tracks[count].fname);
if (strcasecmp(ext, "mp3") == 0)
data->tracks[count].type = CT_MP3;
else if (strcasecmp(ext, "wav") == 0)
data->tracks[count].type = CT_WAV;
else {
elprintf(EL_STATUS, "unhandled audio format: \"%s\"",
data->tracks[count].fname);
}
}
else
{
// propagate previous
data->tracks[count].type = data->tracks[count-1].type;
}
}
else {
elprintf(EL_STATUS, "unhandled track type: \"%s\"", buff2);
}
}
else if (BEGINS(buff, "INDEX "))
{
int m, s, f;
// type
ret = get_token(buff+6, buff2, sizeof(buff2));
if (atoi(buff2) == 0) continue;
if (atoi(buff2) != 1) {
elprintf(EL_STATUS, "cue: don't know how to handle: \"%s\"", buff);
count--; break;
}
// offset in file
get_token(buff+6+ret, buff2, sizeof(buff2));
ret = sscanf(buff2, "%d:%d:%d", &m, &s, &f);
if (ret != 3) {
elprintf(EL_STATUS, "cue: failed to parse: \"%s\"", buff);
count--; break;
}
data->tracks[count].sector_offset = m*60*75 + s*75 + f;
// some strange .cues may need this
if (data->tracks[count].fname != NULL && strcmp(data->tracks[count].fname, current_file) != 0)
{
free(data->tracks[count].fname);
data->tracks[count].fname = strdup(current_file);
}
if (data->tracks[count].fname == NULL && strcmp(data->tracks[1].fname, current_file) != 0)
{
data->tracks[count].fname = strdup(current_file);
}
}
else if (BEGINS(buff, "PREGAP ") || BEGINS(buff, "POSTGAP "))
{
int m, s, f;
get_token(buff+7, buff2, sizeof(buff2));
ret = sscanf(buff2, "%d:%d:%d", &m, &s, &f);
if (ret != 3) {
elprintf(EL_STATUS, "cue: failed to parse: \"%s\"", buff);
continue;
}
// pregap overrides previous postgap?
// by looking at some .cues produced by some programs I've decided that..
if (BEGINS(buff, "PREGAP "))
data->tracks[count].pregap = m*60*75 + s*75 + f;
else
pending_pregap = m*60*75 + s*75 + f;
}
else if (BEGINS(buff, "REM LENGTH ")) // custom "extension"
{
int m, s, f;
get_token(buff+11, buff2, sizeof(buff2));
ret = sscanf(buff2, "%d:%d:%d", &m, &s, &f);
if (ret != 3) continue;
data->tracks[count].sector_xlength = m*60*75 + s*75 + f;
}
else if (BEGINS(buff, "REM"))
continue;
else
{
elprintf(EL_STATUS, "cue: unhandled line: \"%s\"", buff);
}
}
if (count < 1 || data->tracks[1].fname == NULL) {
// failed..
for (; count > 0; count--)
if (data->tracks[count].fname != NULL)
free(data->tracks[count].fname);
free(data);
return NULL;
}
data->track_count = count;
return data;
}
void cue_destroy(cue_data_t *data)
{
int c;
if (data == NULL) return;
for (c = data->track_count; c > 0; c--)
if (data->tracks[c].fname != NULL)
free(data->tracks[c].fname);
free(data);
}
#if 0
int main(int argc, char *argv[])
{
cue_data_t *data = cue_parse(argv[1]);
int c;
if (data == NULL) return 1;
for (c = 1; c <= data->track_count; c++)
printf("%2i: %i %9i %02i:%02i:%02i %9i %s\n", c, data->tracks[c].type, data->tracks[c].sector_offset,
data->tracks[c].sector_offset / (75*60), data->tracks[c].sector_offset / 75 % 60,
data->tracks[c].sector_offset % 75, data->tracks[c].pregap, data->tracks[c].fname);
cue_destroy(data);
return 0;
}
#endif

29
pico/cd/cue.h Normal file
View file

@ -0,0 +1,29 @@
typedef enum
{
CT_UNKNOWN = 0,
CT_ISO = 1, /* 2048 B/sector */
CT_BIN = 2, /* 2352 B/sector */
CT_MP3 = 3,
CT_WAV = 4
} cue_track_type;
typedef struct
{
char *fname;
int pregap; /* pregap for current track */
int sector_offset; /* in current file */
int sector_xlength;
cue_track_type type;
} cue_track;
typedef struct
{
int track_count;
cue_track tracks[0];
} cue_data_t;
cue_data_t *cue_parse(const char *fname);
void cue_destroy(cue_data_t *data);

489
pico/cd/gfx_cd.c Normal file
View file

@ -0,0 +1,489 @@
// This is a direct rewrite of gfx_cd.asm (x86 asm to C).
// You can even find some x86 register names :)
// Original code (c) 2002 by Stéphane Dallongeville
// (c) Copyright 2007, Grazvydas "notaz" Ignotas
#include "../pico_int.h"
#define _rot_comp Pico_mcd->rot_comp
static const int Table_Rot_Time[] =
{
0x00054000, 0x00048000, 0x00040000, 0x00036000, //; 008-032 ; briefing - sprite
0x0002E000, 0x00028000, 0x00024000, 0x00022000, //; 036-064 ; arbre souvent
0x00021000, 0x00020000, 0x0001E000, 0x0001B800, //; 068-096 ; map thunderstrike
0x00019800, 0x00017A00, 0x00015C00, 0x00013E00, //; 100-128 ; logo défoncé
0x00012000, 0x00011800, 0x00011000, 0x00010800, //; 132-160 ; briefing - map
0x00010000, 0x0000F800, 0x0000F000, 0x0000E800, //; 164-192
0x0000E000, 0x0000D800, 0x0000D000, 0x0000C800, //; 196-224
0x0000C000, 0x0000B800, 0x0000B000, 0x0000A800, //; 228-256 ; batman visage
0x0000A000, 0x00009F00, 0x00009E00, 0x00009D00, //; 260-288
0x00009C00, 0x00009B00, 0x00009A00, 0x00009900, //; 292-320
0x00009800, 0x00009700, 0x00009600, 0x00009500, //; 324-352
0x00009400, 0x00009300, 0x00009200, 0x00009100, //; 356-384
0x00009000, 0x00008F00, 0x00008E00, 0x00008D00, //; 388-416
0x00008C00, 0x00008B00, 0x00008A00, 0x00008900, //; 420-448
0x00008800, 0x00008700, 0x00008600, 0x00008500, //; 452-476
0x00008400, 0x00008300, 0x00008200, 0x00008100, //; 480-512
};
static void gfx_cd_start(void)
{
int upd_len;
// _rot_comp.XD_Mul = ((_rot_comp.Reg_5C & 0x1f) + 1) * 4; // unused
_rot_comp.Function = (_rot_comp.Reg_58 & 7) | (Pico_mcd->s68k_regs[3] & 0x18); // Jmp_Adr
// _rot_comp.Buffer_Adr = (_rot_comp.Reg_5E & 0xfff8) << 2; // unused?
_rot_comp.YD = (_rot_comp.Reg_60 >> 3) & 7;
_rot_comp.Vector_Adr = (_rot_comp.Reg_66 & 0xfffe) << 2;
upd_len = (_rot_comp.Reg_62 >> 3) & 0x3f;
upd_len = Table_Rot_Time[upd_len];
_rot_comp.Draw_Speed = _rot_comp.Float_Part = upd_len;
_rot_comp.Reg_58 |= 0x8000; // Stamp_Size, we start a new GFX operation
switch (_rot_comp.Reg_58 & 6) // Scr_16?
{
case 0: // ?
_rot_comp.Stamp_Map_Adr = (_rot_comp.Reg_5A & 0xff80) << 2;
break;
case 2: // .Dot_32
_rot_comp.Stamp_Map_Adr = (_rot_comp.Reg_5A & 0xffe0) << 2;
break;
case 4: // .Scr_16
_rot_comp.Stamp_Map_Adr = 0x20000;
break;
case 6: // .Scr_16_Dot_32
_rot_comp.Stamp_Map_Adr = (_rot_comp.Reg_5A & 0xe000) << 2;
break;
}
dprintf("gfx_cd_start, stamp_map_addr=%06x", _rot_comp.Stamp_Map_Adr);
gfx_cd_update();
}
static void gfx_completed(void)
{
_rot_comp.Reg_58 &= 0x7fff; // Stamp_Size
_rot_comp.Reg_64 = 0;
if (Pico_mcd->s68k_regs[0x33] & (1<<1))
{
elprintf(EL_INTS, "gfx_cd irq 1");
SekInterruptS68k(1);
}
}
static void gfx_do(unsigned int func, unsigned short *stamp_base, unsigned int H_Dot)
{
unsigned int eax, ebx, ecx, edx, esi, edi, pixel;
unsigned int XD, Buffer_Adr;
int DYXS;
XD = _rot_comp.Reg_60 & 7;
Buffer_Adr = ((_rot_comp.Reg_5E & 0xfff8) + _rot_comp.YD) << 2;
ecx = *(unsigned int *)(Pico_mcd->word_ram2M + _rot_comp.Vector_Adr);
edx = ecx >> 16;
ecx = (ecx & 0xffff) << 8;
edx <<= 8;
DYXS = *(int *)(Pico_mcd->word_ram2M + _rot_comp.Vector_Adr + 4);
_rot_comp.Vector_Adr += 8;
// MAKE_IMAGE_LINE
while (H_Dot)
{
// MAKE_IMAGE_PIXEL
if (!(func & 1)) // NOT TILED
{
int mask = (func & 4) ? 0x00800000 : 0x00f80000;
if ((ecx | edx) & mask)
{
if (func & 0x18) goto Next_Pixel;
pixel = 0;
goto Pixel_Out;
}
}
if (func & 2) // mode 32x32 dot
{
if (func & 4) // 16x16 screen
{
ebx = ((ecx >> (11+5)) & 0x007f) |
((edx >> (11-2)) & 0x3f80);
}
else // 1x1 screen
{
ebx = ((ecx >> (11+5)) & 0x07) |
((edx >> (11+2)) & 0x38);
}
}
else // mode 16x16 dot
{
if (func & 4) // 16x16 screen
{
ebx = ((ecx >> (11+4)) & 0x00ff) |
((edx >> (11-4)) & 0xff00);
}
else // 1x1 screen
{
ebx = ((ecx >> (11+4)) & 0x0f) |
((edx >> (11+0)) & 0xf0);
}
}
edi = stamp_base[ebx];
esi = (edi & 0x7ff) << 7;
if (!esi) { pixel = 0; goto Pixel_Out; }
edi >>= (11+1);
edi &= (0x1c>>1);
eax = ecx;
ebx = edx;
if (func & 2) edi |= 1; // 32 dots?
switch (edi)
{
case 0x00: // No_Flip_0, 16x16 dots
ebx = (ebx >> 9) & 0x3c;
ebx += esi;
edi = (eax & 0x3800) ^ 0x1000; // bswap
eax = ((eax >> 8) & 0x40) + ebx;
break;
case 0x01: // No_Flip_0, 32x32 dots
ebx = (ebx >> 9) & 0x7c;
ebx += esi;
edi = (eax & 0x3800) ^ 0x1000; // bswap
eax = ((eax >> 7) & 0x180) + ebx;
break;
case 0x02: // No_Flip_90, 16x16 dots
eax = (eax >> 9) & 0x3c;
eax += esi;
edi = (ebx & 0x3800) ^ 0x2800; // bswap
eax += ((ebx >> 8) & 0x40) ^ 0x40;
break;
case 0x03: // No_Flip_90, 32x32 dots
eax = (eax >> 9) & 0x7c;
eax += esi;
edi = (ebx & 0x3800) ^ 0x2800; // bswap
eax += ((ebx >> 7) & 0x180) ^ 0x180;
break;
case 0x04: // No_Flip_180, 16x16 dots
ebx = ((ebx >> 9) & 0x3c) ^ 0x3c;
ebx += esi;
edi = (eax & 0x3800) ^ 0x2800; // bswap and flip
eax = (((eax >> 8) & 0x40) ^ 0x40) + ebx;
break;
case 0x05: // No_Flip_180, 32x32 dots
ebx = ((ebx >> 9) & 0x7c) ^ 0x7c;
ebx += esi;
edi = (eax & 0x3800) ^ 0x2800; // bswap and flip
eax = (((eax >> 7) & 0x180) ^ 0x180) + ebx;
break;
case 0x06: // No_Flip_270, 16x16 dots
eax = ((eax >> 9) & 0x3c) ^ 0x3c;
eax += esi;
edi = (ebx & 0x3800) ^ 0x1000; // bswap
eax += (ebx >> 8) & 0x40;
break;
case 0x07: // No_Flip_270, 32x32 dots
eax = ((eax >> 9) & 0x7c) ^ 0x7c;
eax += esi;
edi = (ebx & 0x3800) ^ 0x1000; // bswap
eax += (ebx >> 7) & 0x180;
break;
case 0x08: // Flip_0, 16x16 dots
ebx = (ebx >> 9) & 0x3c;
ebx += esi;
edi = (eax & 0x3800) ^ 0x2800; // bswap, flip
eax = (((eax >> 8) & 0x40) ^ 0x40) + ebx;
break;
case 0x09: // Flip_0, 32x32 dots
ebx = (ebx >> 9) & 0x7c;
ebx += esi;
edi = (eax & 0x3800) ^ 0x2800; // bswap, flip
eax = (((eax >> 7) & 0x180) ^ 0x180) + ebx;
break;
case 0x0a: // Flip_90, 16x16 dots
eax = ((eax >> 9) & 0x3c) ^ 0x3c;
eax += esi;
edi = (ebx & 0x3800) ^ 0x2800; // bswap, flip
eax += ((ebx >> 8) & 0x40) ^ 0x40;
break;
case 0x0b: // Flip_90, 32x32 dots
eax = ((eax >> 9) & 0x7c) ^ 0x7c;
eax += esi;
edi = (ebx & 0x3800) ^ 0x2800; // bswap, flip
eax += ((ebx >> 7) & 0x180) ^ 0x180;
break;
case 0x0c: // Flip_180, 16x16 dots
ebx = ((ebx >> 9) & 0x3c) ^ 0x3c;
ebx += esi;
edi = (eax & 0x3800) ^ 0x1000; // bswap
eax = ((eax >> 8) & 0x40) + ebx;
break;
case 0x0d: // Flip_180, 32x32 dots
ebx = ((ebx >> 9) & 0x7c) ^ 0x7c;
ebx += esi;
edi = (eax & 0x3800) ^ 0x1000; // bswap
eax = ((eax >> 7) & 0x180) + ebx;
break;
case 0x0e: // Flip_270, 16x16 dots
eax = (eax >> 9) & 0x3c;
eax += esi;
edi = (ebx & 0x3800) ^ 0x1000; // bswap, flip
eax += (ebx >> 8) & 0x40;
break;
case 0x0f: // Flip_270, 32x32 dots
eax = (eax >> 9) & 0x7c;
eax += esi;
edi = (ebx & 0x3800) ^ 0x1000; // bswap, flip
eax += (ebx >> 7) & 0x180;
break;
}
pixel = *(Pico_mcd->word_ram2M + (edi >> 12) + eax);
if (!(edi & 0x800)) pixel >>= 4;
else pixel &= 0x0f;
Pixel_Out:
if (!pixel && (func & 0x18)) goto Next_Pixel;
esi = Buffer_Adr + ((XD>>1)^1); // pixel addr
eax = *(Pico_mcd->word_ram2M + esi); // old pixel
if (XD & 1)
{
if ((eax & 0x0f) && (func & 0x18) == 0x08) goto Next_Pixel; // underwrite
*(Pico_mcd->word_ram2M + esi) = pixel | (eax & 0xf0);
}
else
{
if ((eax & 0xf0) && (func & 0x18) == 0x08) goto Next_Pixel; // underwrite
*(Pico_mcd->word_ram2M + esi) = (pixel << 4) | (eax & 0xf);
}
Next_Pixel:
ecx += (DYXS << 16) >> 16; // _rot_comp.DXS;
edx += DYXS >> 16; // _rot_comp.DYS;
XD++;
if (XD >= 8)
{
Buffer_Adr += ((_rot_comp.Reg_5C & 0x1f) + 1) << 5;
XD = 0;
}
H_Dot--;
}
// end while
// nothing_to_draw:
_rot_comp.YD++;
// _rot_comp.V_Dot--; // will be done by caller
}
PICO_INTERNAL void gfx_cd_update(void)
{
int V_Dot = _rot_comp.Reg_64 & 0xff;
int jobs;
dprintf("gfx_cd_update, Reg_64 = %04x", _rot_comp.Reg_64);
if (!V_Dot)
{
gfx_completed();
return;
}
jobs = _rot_comp.Float_Part >> 16;
if (!jobs)
{
_rot_comp.Float_Part += _rot_comp.Draw_Speed;
return;
}
_rot_comp.Float_Part &= 0xffff;
_rot_comp.Float_Part += _rot_comp.Draw_Speed;
if (PicoOpt & POPT_EN_MCD_GFX)
{
unsigned int func = _rot_comp.Function;
unsigned int H_Dot = _rot_comp.Reg_62 & 0x1ff;
unsigned short *stamp_base = (unsigned short *) (Pico_mcd->word_ram2M + _rot_comp.Stamp_Map_Adr);
while (jobs--)
{
gfx_do(func, stamp_base, H_Dot); // jmp [Jmp_Adr]:
V_Dot--; // dec byte [V_Dot]
if (V_Dot == 0)
{
// GFX_Completed:
gfx_completed();
return;
}
}
}
else
{
if (jobs >= V_Dot)
{
gfx_completed();
return;
}
V_Dot -= jobs;
}
_rot_comp.Reg_64 = V_Dot;
}
PICO_INTERNAL_ASM unsigned int gfx_cd_read(unsigned int a)
{
unsigned int d = 0;
switch (a) {
case 0x58: d = _rot_comp.Reg_58; break;
case 0x5A: d = _rot_comp.Reg_5A; break;
case 0x5C: d = _rot_comp.Reg_5C; break;
case 0x5E: d = _rot_comp.Reg_5E; break;
case 0x60: d = _rot_comp.Reg_60; break;
case 0x62: d = _rot_comp.Reg_62; break;
case 0x64: d = _rot_comp.Reg_64; break;
case 0x66: break;
default: dprintf("gfx_cd_read FIXME: unexpected address: %02x", a); break;
}
dprintf("gfx_cd_read(%02x) = %04x", a, d);
return d;
}
PICO_INTERNAL_ASM void gfx_cd_write16(unsigned int a, unsigned int d)
{
dprintf("gfx_cd_write16(%x, %04x)", a, d);
switch (a) {
case 0x58: // .Reg_Stamp_Size
_rot_comp.Reg_58 = d & 7;
return;
case 0x5A: // .Reg_Stamp_Adr
_rot_comp.Reg_5A = d & 0xffe0;
return;
case 0x5C: // .Reg_IM_VCell_Size
_rot_comp.Reg_5C = d & 0x1f;
return;
case 0x5E: // .Reg_IM_Adr
_rot_comp.Reg_5E = d & 0xFFF8;
return;
case 0x60: // .Reg_IM_Offset
_rot_comp.Reg_60 = d & 0x3f;
return;
case 0x62: // .Reg_IM_HDot_Size
_rot_comp.Reg_62 = d & 0x1ff;
return;
case 0x64: // .Reg_IM_VDot_Size
_rot_comp.Reg_64 = d & 0xff; // V_Dot, must be 32bit?
return;
case 0x66: // .Reg_Vector_Adr
_rot_comp.Reg_66 = d & 0xfffe;
if (Pico_mcd->s68k_regs[3]&4) return; // can't do tanformations in 1M mode
gfx_cd_start();
return;
default: dprintf("gfx_cd_write16 FIXME: unexpected address: %02x", a); return;
}
}
PICO_INTERNAL void gfx_cd_reset(void)
{
memset(&_rot_comp.Reg_58, 0, sizeof(_rot_comp));
}
// --------------------------------
#include "cell_map.c"
#ifndef UTYPES_DEFINED
typedef unsigned short u16;
#endif
// check: Heart of the alien, jaguar xj 220
PICO_INTERNAL void DmaSlowCell(unsigned int source, unsigned int a, int len, unsigned char inc)
{
unsigned char *base;
unsigned int asrc, a2;
u16 *r;
base = Pico_mcd->word_ram1M[Pico_mcd->s68k_regs[3]&1];
switch (Pico.video.type)
{
case 1: // vram
r = Pico.vram;
for(; len; len--)
{
asrc = cell_map(source >> 2) << 2;
asrc |= source & 2;
// if(a&1) d=(d<<8)|(d>>8); // ??
r[a>>1] = *(u16 *)(base + asrc);
source += 2;
// AutoIncrement
a=(u16)(a+inc);
}
rendstatus |= PDRAW_SPRITES_MOVED;
break;
case 3: // cram
Pico.m.dirtyPal = 1;
r = Pico.cram;
for(a2=a&0x7f; len; len--)
{
asrc = cell_map(source >> 2) << 2;
asrc |= source & 2;
r[a2>>1] = *(u16 *)(base + asrc);
source += 2;
// AutoIncrement
a2+=inc;
// good dest?
if(a2 >= 0x80) break;
}
a=(a&0xff00)|a2;
break;
case 5: // vsram[a&0x003f]=d;
r = Pico.vsram;
for(a2=a&0x7f; len; len--)
{
asrc = cell_map(source >> 2) << 2;
asrc |= source & 2;
r[a2>>1] = *(u16 *)(base + asrc);
source += 2;
// AutoIncrement
a2+=inc;
// good dest?
if(a2 >= 0x80) break;
}
a=(a&0xff00)|a2;
break;
}
// remember addr
Pico.video.addr=(u16)a;
}

37
pico/cd/gfx_cd.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef _GFX_CD_H
#define _GFX_CD_H
typedef struct
{
unsigned int Reg_58; // Stamp_Size
unsigned int Reg_5A;
unsigned int Reg_5C;
unsigned int Reg_5E;
unsigned int Reg_60;
unsigned int Reg_62;
unsigned int Reg_64; // V_Dot
unsigned int Reg_66;
unsigned int Stamp_Map_Adr;
unsigned int Vector_Adr;
unsigned int Function; // Jmp_Adr;
unsigned int Float_Part;
unsigned int Draw_Speed;
unsigned int YD;
int pad[10];
} Rot_Comp;
PICO_INTERNAL void gfx_cd_update(void);
PICO_INTERNAL_ASM unsigned int gfx_cd_read(unsigned int a);
PICO_INTERNAL_ASM void gfx_cd_write16(unsigned int a, unsigned int d);
PICO_INTERNAL void gfx_cd_reset(void);
PICO_INTERNAL void DmaSlowCell(unsigned int source, unsigned int a, int len, unsigned char inc);
#endif // _GFX_CD_H

1853
pico/cd/memory.c Normal file

File diff suppressed because it is too large Load diff

2364
pico/cd/memory_arm.s Normal file

File diff suppressed because it is too large Load diff

61
pico/cd/misc.c Normal file
View file

@ -0,0 +1,61 @@
// Some misc stuff
// (c) Copyright 2007, Grazvydas "notaz" Ignotas
#include "../pico_int.h"
unsigned char formatted_bram[4*0x10] =
{
#if 0
0x00, 0xd4, 0x63, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x53, 0xd2, 0xf5, 0x3a, 0x48, 0x50, 0x35, 0x0f,
0x47, 0x14, 0xf5, 0x7e, 0x5c, 0xd4, 0xf3, 0x03, 0x00, 0x03, 0x12, 0x00, 0x0a, 0xff, 0xca, 0xa6,
0xf5, 0x27, 0xed, 0x22, 0x47, 0xfa, 0x22, 0x96, 0x6c, 0xa5, 0x88, 0x14, 0x48, 0x48, 0x0a, 0xbb,
#endif
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x40,
0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x53, 0x45, 0x47, 0x41, 0x5f, 0x43, 0x44, 0x5f, 0x52, 0x4f, 0x4d, 0x00, 0x01, 0x00, 0x00, 0x00,
0x52, 0x41, 0x4d, 0x5f, 0x43, 0x41, 0x52, 0x54, 0x52, 0x49, 0x44, 0x47, 0x45, 0x5f, 0x5f, 0x5f,
// SEGA_CD_ROM.....RAM_CARTRIDGE___
};
// offs | 2Mbit | 1Mbit |
// 0 | [ 2M | unused |
// 128K | bit ] | bank0 |
// 256K | unused | bank1 |
#ifndef _ASM_MISC_C
PICO_INTERNAL_ASM void wram_2M_to_1M(unsigned char *m)
{
unsigned short *m1M_b0, *m1M_b1;
unsigned int i, tmp, *m2M;
m2M = (unsigned int *) (m + 0x40000);
m1M_b0 = (unsigned short *) m2M;
m1M_b1 = (unsigned short *) (m + 0x60000);
for (i = 0x40000/4; i; i--)
{
tmp = *(--m2M);
*(--m1M_b0) = tmp;
*(--m1M_b1) = tmp >> 16;
}
}
PICO_INTERNAL_ASM void wram_1M_to_2M(unsigned char *m)
{
unsigned short *m1M_b0, *m1M_b1;
unsigned int i, tmp, *m2M;
m2M = (unsigned int *) m;
m1M_b0 = (unsigned short *) (m + 0x20000);
m1M_b1 = (unsigned short *) (m + 0x40000);
for (i = 0x40000/4; i; i--)
{
tmp = *m1M_b0++ | (*m1M_b1++ << 16);
*m2M++ = tmp;
}
}
#endif

76
pico/cd/misc_arm.s Normal file
View file

@ -0,0 +1,76 @@
@ vim:filetype=armasm
@ Memory converters for different modes
@ (c) Copyright 2007, Grazvydas "notaz" Ignotas
@ r10 is tmp, io1 is lsb halfword, io2 is msb
@ | 0 1 | 2 3 | -> | 0 2 | 1 3 | (little endian)
.macro _conv_reg io1 io2
mov r10, \io2, lsl #16
and \io2, \io2, r11, lsl #16
orr \io2, \io2, \io1, lsr #16
and \io1, \io1, r11
orr \io1, \io1, r10
/*
mov \io2, \io2, ror #16
mov r10, \io1, lsl #16
orr r10, r10, \io2, lsr #16
mov \io1, \io1, lsr #16
orr \io1, \io1, \io2, lsl #16
mov \io2, r10, ror #16
*/
.endm
.global wram_2M_to_1M
wram_2M_to_1M:
stmfd sp!,{r4-r11,lr}
add r1, r0, #0x60000 @ m1M_b1
add r0, r0, #0x40000 @ m1M_b0
mov r2, r0 @ m2M
mov r11, #0xff
orr r11, r11, r11, lsl #8
mov r12, #(0x40000/8/4)
_2Mto1M_loop:
ldmdb r2!,{r3-r9,lr}
_conv_reg r3,r4
_conv_reg r5,r6
_conv_reg r7,r8
_conv_reg r9,lr
subs r12, r12, #1
stmdb r0!,{r3,r5,r7,r9}
stmdb r1!,{r4,r6,r8,lr}
bne _2Mto1M_loop
ldmfd sp!,{r4-r11,pc}
.global wram_1M_to_2M
wram_1M_to_2M:
stmfd sp!,{r4-r11,lr}
mov r2, r0 @ m2M
add r1, r0, #0x40000 @ m1M_b1
add r0, r0, #0x20000 @ m1M_b0
mov r11, #0xff
orr r11, r11, r11, lsl #8
mov r12, #(0x40000/8/4)
_1Mto2M_loop:
ldmia r0!,{r3,r5,r7,r9}
ldmia r1!,{r4,r6,r8,lr}
_conv_reg r3,r4
_conv_reg r5,r6
_conv_reg r7,r8
_conv_reg r9,lr
subs r12, r12, #1
stmia r2!,{r3-r9,lr}
bne _1Mto2M_loop
ldmfd sp!,{r4-r11,pc}

133
pico/cd/pcm.c Normal file
View file

@ -0,0 +1,133 @@
// Emulation routines for the RF5C164 PCM chip.
// Based on Gens code by Stéphane Dallongeville
// (c) Copyright 2007, Grazvydas "notaz" Ignotas
#include "../pico_int.h"
#include "pcm.h"
static unsigned int g_rate = 0; // 18.14 fixed point
PICO_INTERNAL_ASM void pcm_write(unsigned int a, unsigned int d)
{
//printf("pcm_write(%i, %02x)\n", a, d);
if (a < 7)
{
Pico_mcd->pcm.ch[Pico_mcd->pcm.cur_ch].regs[a] = d;
}
else if (a == 7) // control register
{
if (d & 0x40) Pico_mcd->pcm.cur_ch = d & 7;
else Pico_mcd->pcm.bank = d & 0xf;
Pico_mcd->pcm.control = d;
// dprintf("pcm control=%02x", Pico_mcd->pcm.control);
}
else if (a == 8) // sound on/off
{
if (!(Pico_mcd->pcm.enabled & 0x01)) Pico_mcd->pcm.ch[0].addr =
Pico_mcd->pcm.ch[0].regs[6] << (PCM_STEP_SHIFT + 8);
if (!(Pico_mcd->pcm.enabled & 0x02)) Pico_mcd->pcm.ch[1].addr =
Pico_mcd->pcm.ch[1].regs[6] << (PCM_STEP_SHIFT + 8);
if (!(Pico_mcd->pcm.enabled & 0x04)) Pico_mcd->pcm.ch[2].addr =
Pico_mcd->pcm.ch[2].regs[6] << (PCM_STEP_SHIFT + 8);
if (!(Pico_mcd->pcm.enabled & 0x08)) Pico_mcd->pcm.ch[3].addr =
Pico_mcd->pcm.ch[3].regs[6] << (PCM_STEP_SHIFT + 8);
if (!(Pico_mcd->pcm.enabled & 0x10)) Pico_mcd->pcm.ch[4].addr =
Pico_mcd->pcm.ch[4].regs[6] << (PCM_STEP_SHIFT + 8);
if (!(Pico_mcd->pcm.enabled & 0x20)) Pico_mcd->pcm.ch[5].addr =
Pico_mcd->pcm.ch[5].regs[6] << (PCM_STEP_SHIFT + 8);
if (!(Pico_mcd->pcm.enabled & 0x40)) Pico_mcd->pcm.ch[6].addr =
Pico_mcd->pcm.ch[6].regs[6] << (PCM_STEP_SHIFT + 8);
if (!(Pico_mcd->pcm.enabled & 0x80)) Pico_mcd->pcm.ch[7].addr =
Pico_mcd->pcm.ch[7].regs[6] << (PCM_STEP_SHIFT + 8);
// printf("addr %x %x %x %x %x %x %x %x\n", Pico_mcd->pcm.ch[0].addr, Pico_mcd->pcm.ch[1].addr
// , Pico_mcd->pcm.ch[2].addr, Pico_mcd->pcm.ch[3].addr, Pico_mcd->pcm.ch[4].addr, Pico_mcd->pcm.ch[5].addr
// , Pico_mcd->pcm.ch[6].addr, Pico_mcd->pcm.ch[7].addr);
Pico_mcd->pcm.enabled = ~d;
//printf("enabled=%02x\n", Pico_mcd->pcm.enabled);
}
}
PICO_INTERNAL void pcm_set_rate(int rate)
{
float step = 31.8 * 1024.0 / (float) rate; // max <4 @ 8000Hz
step *= 256*256/4;
g_rate = (unsigned int) step;
if (step - (float) g_rate >= 0.5) g_rate++;
elprintf(EL_STATUS, "g_rate: %f %08x\n", (double)step, g_rate);
}
PICO_INTERNAL void pcm_update(int *buffer, int length, int stereo)
{
struct pcm_chan *ch;
unsigned int step, addr;
int mul_l, mul_r, smp;
int i, j, k;
int *out;
// PCM disabled or all channels off (to be checked by caller)
//if (!(Pico_mcd->pcm.control & 0x80) || !Pico_mcd->pcm.enabled) return;
//printf("-- upd %i\n", length);
for (i = 0; i < 8; i++)
{
if (!(Pico_mcd->pcm.enabled & (1 << i))) continue; // channel disabled
out = buffer;
ch = &Pico_mcd->pcm.ch[i];
addr = ch->addr; // >> PCM_STEP_SHIFT;
mul_l = ((int)ch->regs[0] * (ch->regs[1] & 0xf)) >> (5+1); // (env * pan) >> 5
mul_r = ((int)ch->regs[0] * (ch->regs[1] >> 4)) >> (5+1);
step = ((unsigned int)(*(unsigned short *)&ch->regs[2]) * g_rate) >> 14; // freq step
// fprintf(stderr, "step=%i, cstep=%i, mul_l=%i, mul_r=%i, ch=%i, addr=%x, en=%02x\n",
// *(unsigned short *)&ch->regs[2], step, mul_l, mul_r, i, addr, Pico_mcd->pcm.enabled);
if (!stereo && mul_l < mul_r) mul_l = mul_r;
for (j = 0; j < length; j++)
{
// printf("addr=%08x\n", addr);
smp = Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT];
// test for loop signal
if (smp == 0xff)
{
addr = *(unsigned short *)&ch->regs[4]; // loop_addr
smp = Pico_mcd->pcm_ram[addr];
addr <<= PCM_STEP_SHIFT;
if (smp == 0xff) break;
}
if (smp & 0x80) smp = -(smp & 0x7f);
*out++ += smp * mul_l; // max 128 * 119 = 15232
if(stereo)
*out++ += smp * mul_r;
// update address register
k = (addr >> PCM_STEP_SHIFT) + 1;
addr = (addr + step) & 0x7FFFFFF;
for(; k < (addr >> PCM_STEP_SHIFT); k++)
{
if (Pico_mcd->pcm_ram[k] == 0xff)
{
addr = (unsigned int)(*(unsigned short *)&ch->regs[4]) << PCM_STEP_SHIFT; // loop_addr
break;
}
}
}
if (Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT] == 0xff)
addr = (unsigned int)(*(unsigned short *)&ch->regs[4]) << PCM_STEP_SHIFT; // loop_addr
ch->addr = addr;
}
}

7
pico/cd/pcm.h Normal file
View file

@ -0,0 +1,7 @@
#define PCM_STEP_SHIFT 11
PICO_INTERNAL_ASM void pcm_write(unsigned int a, unsigned int d);
PICO_INTERNAL void pcm_set_rate(int rate);
PICO_INTERNAL void pcm_update(int *buffer, int length, int stereo);

249
pico/cd/pico.c Normal file
View file

@ -0,0 +1,249 @@
// (c) Copyright 2007 notaz, All rights reserved.
#include "../pico_int.h"
#include "../sound/ym2612.h"
extern unsigned char formatted_bram[4*0x10];
extern unsigned int s68k_poll_adclk;
void (*PicoMCDopenTray)(void) = NULL;
int (*PicoMCDcloseTray)(void) = NULL;
PICO_INTERNAL void PicoInitMCD(void)
{
SekInitS68k();
Init_CD_Driver();
}
PICO_INTERNAL void PicoExitMCD(void)
{
End_CD_Driver();
}
PICO_INTERNAL void PicoPowerMCD(void)
{
int fmt_size = sizeof(formatted_bram);
memset(Pico_mcd->prg_ram, 0, sizeof(Pico_mcd->prg_ram));
memset(Pico_mcd->word_ram2M, 0, sizeof(Pico_mcd->word_ram2M));
memset(Pico_mcd->pcm_ram, 0, sizeof(Pico_mcd->pcm_ram));
memset(Pico_mcd->bram, 0, sizeof(Pico_mcd->bram));
memcpy(Pico_mcd->bram + sizeof(Pico_mcd->bram) - fmt_size, formatted_bram, fmt_size);
}
PICO_INTERNAL int PicoResetMCD(void)
{
memset(Pico_mcd->s68k_regs, 0, sizeof(Pico_mcd->s68k_regs));
memset(&Pico_mcd->pcm, 0, sizeof(Pico_mcd->pcm));
memset(&Pico_mcd->m, 0, sizeof(Pico_mcd->m));
*(unsigned int *)(Pico_mcd->bios + 0x70) = 0xffffffff; // reset hint vector (simplest way to implement reg6)
Pico_mcd->m.state_flags |= 1; // s68k reset pending
Pico_mcd->s68k_regs[3] = 1; // 2M word RAM mode with m68k access after reset
Reset_CD();
LC89510_Reset();
gfx_cd_reset();
PicoMemResetCD(1);
#ifdef _ASM_CD_MEMORY_C
//PicoMemResetCDdecode(1); // don't have to call this in 2M mode
#endif
// use SRam.data for RAM cart
if (PicoOpt&POPT_EN_MCD_RAMCART) {
if (SRam.data == NULL)
SRam.data = calloc(1, 0x12000);
}
else if (SRam.data != NULL) {
free(SRam.data);
SRam.data = NULL;
}
SRam.start = SRam.end = 0; // unused
return 0;
}
static __inline void SekRunM68k(int cyc)
{
int cyc_do;
SekCycleAim+=cyc;
if ((cyc_do=SekCycleAim-SekCycleCnt) <= 0) return;
#if defined(EMU_CORE_DEBUG)
SekCycleCnt+=CM_compareRun(cyc_do, 0);
#elif defined(EMU_C68K)
PicoCpuCM68k.cycles=cyc_do;
CycloneRun(&PicoCpuCM68k);
SekCycleCnt+=cyc_do-PicoCpuCM68k.cycles;
#elif defined(EMU_M68K)
m68k_set_context(&PicoCpuMM68k);
SekCycleCnt+=m68k_execute(cyc_do);
#elif defined(EMU_F68K)
g_m68kcontext=&PicoCpuFM68k;
SekCycleCnt+=fm68k_emulate(cyc_do, 0, 0);
#endif
}
static __inline void SekRunS68k(int cyc)
{
int cyc_do;
SekCycleAimS68k+=cyc;
if ((cyc_do=SekCycleAimS68k-SekCycleCntS68k) <= 0) return;
#if defined(EMU_CORE_DEBUG)
SekCycleCntS68k+=CM_compareRun(cyc_do, 1);
#elif defined(EMU_C68K)
PicoCpuCS68k.cycles=cyc_do;
CycloneRun(&PicoCpuCS68k);
SekCycleCntS68k+=cyc_do-PicoCpuCS68k.cycles;
#elif defined(EMU_M68K)
m68k_set_context(&PicoCpuMS68k);
SekCycleCntS68k+=m68k_execute(cyc_do);
#elif defined(EMU_F68K)
g_m68kcontext=&PicoCpuFS68k;
SekCycleCntS68k+=fm68k_emulate(cyc_do, 0, 0);
#endif
}
#define PS_STEP_M68K ((488<<16)/20) // ~24
//#define PS_STEP_S68K 13
#if defined(_ASM_CD_PICO_C)
extern void SekRunPS(int cyc_m68k, int cyc_s68k);
#elif defined(EMU_F68K)
static __inline void SekRunPS(int cyc_m68k, int cyc_s68k)
{
SekCycleAim+=cyc_m68k;
SekCycleAimS68k+=cyc_s68k;
fm68k_emulate(0, 1, 0);
}
#else
static __inline void SekRunPS(int cyc_m68k, int cyc_s68k)
{
int cycn, cycn_s68k, cyc_do;
SekCycleAim+=cyc_m68k;
SekCycleAimS68k+=cyc_s68k;
// fprintf(stderr, "=== start %3i/%3i [%3i/%3i] {%05i.%i} ===\n", cyc_m68k, cyc_s68k,
// SekCycleAim-SekCycleCnt, SekCycleAimS68k-SekCycleCntS68k, Pico.m.frame_count, Pico.m.scanline);
/* loop 488 downto 0 in steps of PS_STEP */
for (cycn = (488<<16)-PS_STEP_M68K; cycn >= 0; cycn -= PS_STEP_M68K)
{
cycn_s68k = (cycn + cycn/2 + cycn/8) >> 16;
if ((cyc_do = SekCycleAim-SekCycleCnt-(cycn>>16)) > 0) {
#if defined(EMU_C68K)
PicoCpuCM68k.cycles = cyc_do;
CycloneRun(&PicoCpuCM68k);
SekCycleCnt += cyc_do - PicoCpuCM68k.cycles;
#elif defined(EMU_M68K)
m68k_set_context(&PicoCpuMM68k);
SekCycleCnt += m68k_execute(cyc_do);
#elif defined(EMU_F68K)
g_m68kcontext = &PicoCpuFM68k;
SekCycleCnt += fm68k_emulate(cyc_do, 0, 0);
#endif
}
if ((cyc_do = SekCycleAimS68k-SekCycleCntS68k-cycn_s68k) > 0) {
#if defined(EMU_C68K)
PicoCpuCS68k.cycles = cyc_do;
CycloneRun(&PicoCpuCS68k);
SekCycleCntS68k += cyc_do - PicoCpuCS68k.cycles;
#elif defined(EMU_M68K)
m68k_set_context(&PicoCpuMS68k);
SekCycleCntS68k += m68k_execute(cyc_do);
#elif defined(EMU_F68K)
g_m68kcontext = &PicoCpuFS68k;
SekCycleCntS68k += fm68k_emulate(cyc_do, 0, 0);
#endif
}
}
}
#endif
static __inline void check_cd_dma(void)
{
int ddx;
if (!(Pico_mcd->scd.Status_CDC & 0x08)) return;
ddx = Pico_mcd->s68k_regs[4] & 7;
if (ddx < 2) return; // invalid
if (ddx < 4) {
Pico_mcd->s68k_regs[4] |= 0x40; // Data set ready in host port
return;
}
if (ddx == 6) return; // invalid
Update_CDC_TRansfer(ddx); // now go and do the actual transfer
}
static __inline void update_chips(void)
{
int counter_timer, int3_set;
int counter75hz_lim = Pico.m.pal ? 2080 : 2096;
// 75Hz CDC update
if ((Pico_mcd->m.counter75hz+=10) >= counter75hz_lim) {
Pico_mcd->m.counter75hz -= counter75hz_lim;
Check_CD_Command();
}
// update timers
counter_timer = Pico.m.pal ? 0x21630 : 0x2121c; // 136752 : 135708;
Pico_mcd->m.timer_stopwatch += counter_timer;
if ((int3_set = Pico_mcd->s68k_regs[0x31])) {
Pico_mcd->m.timer_int3 -= counter_timer;
if (Pico_mcd->m.timer_int3 < 0) {
if (Pico_mcd->s68k_regs[0x33] & (1<<3)) {
elprintf(EL_INTS, "s68k: timer irq 3");
SekInterruptS68k(3);
Pico_mcd->m.timer_int3 += int3_set << 16;
}
// is this really what happens if irq3 is masked out?
Pico_mcd->m.timer_int3 &= 0xffffff;
}
}
// update gfx chip
if (Pico_mcd->rot_comp.Reg_58 & 0x8000)
gfx_cd_update();
// delayed setting of DMNA bit (needed for Silpheed)
if (Pico_mcd->m.state_flags & 2) {
Pico_mcd->m.state_flags &= ~2;
if (!(Pico_mcd->s68k_regs[3] & 4)) {
Pico_mcd->s68k_regs[3] |= 2;
Pico_mcd->s68k_regs[3] &= ~1;
#ifdef USE_POLL_DETECT
if ((s68k_poll_adclk&0xfe) == 2) {
SekSetStopS68k(0); s68k_poll_adclk = 0;
}
#endif
}
}
}
static __inline void getSamples(int y)
{
int len = PsndRender(0, PsndLen);
if (PicoWriteSound) PicoWriteSound(len);
// clear sound buffer
PsndClear();
}
#define PICO_CD
#include "../pico_cmn.c"
PICO_INTERNAL void PicoFrameMCD(void)
{
if (!(PicoOpt&POPT_ALT_RENDERER))
PicoFrameStart();
PicoFrameHints();
}

181
pico/cd/pico_arm.s Normal file
View file

@ -0,0 +1,181 @@
@ vim:filetype=armasm
@ SekRunPS runs PicoCpuCM68k and PicoCpuCS68k interleaved in steps of PS_STEP_M68K
@ cycles. This is done without calling CycloneRun and jumping directly to
@ Cyclone code to avoid pushing/popping all the registers every time.
@ (c) Copyright 2007, Grazvydas "notaz" Ignotas
@ All Rights Reserved
.equiv PS_STEP_M68K, ((488<<16)/20) @ ~24
@ .extern is ignored by gas, we add these here just to see what we depend on.
.extern CycloneJumpTab
.extern CycloneDoInterrupt
.extern PicoCpuCM68k
.extern PicoCpuCS68k
.extern SekCycleAim
.extern SekCycleCnt
.extern SekCycleAimS68k
.extern SekCycleCntS68k
.text
.align 4
.global SekRunPS @ cyc_m68k, cyc_s68k
SekRunPS:
stmfd sp!, {r4-r8,r10,r11,lr}
sub sp, sp, #2*4 @ sp[0] = main_cycle_cnt, sp[4] = run_cycle_cnt
@ override CycloneEnd for both contexts
ldr r7, =PicoCpuCM68k
ldr lr, =PicoCpuCS68k
ldr r2, =CycloneEnd_M68k
ldr r3, =CycloneEnd_S68k
str r2, [r7,#0x98]
str r3, [lr,#0x98]
@ update aims
ldr r8, =SekCycleAim
ldr r10,=SekCycleAimS68k
ldr r2, [r8]
ldr r3, [r10]
add r2, r2, r0
add r3, r3, r1
str r2, [r8]
str r3, [r10]
ldr r6, =CycloneJumpTab
ldr r1, =SekCycleCnt
ldr r0, =((488<<16)-PS_STEP_M68K)
str r6, [r7,#0x54]
str r6, [lr,#0x54] @ make copies to avoid literal pools
@ schedule m68k for the first time..
ldr r1, [r1]
str r0, [sp] @ main target 'left cycle' counter
sub r1, r2, r1
subs r5, r1, r0, asr #16
ble schedule_s68k @ m68k has not enough cycles
str r5, [sp,#4] @ run_cycle_cnt
b CycloneRunLocal
CycloneEnd_M68k:
ldr r3, =SekCycleCnt
ldr r0, [sp,#4] @ run_cycle_cnt
ldr r1, [r3]
str r4, [r7,#0x40] ;@ Save Current PC + Memory Base
strb r10,[r7,#0x46] ;@ Save Flags (NZCV)
sub r0, r0, r5 @ subtract leftover cycles (which should be negative)
add r0, r0, r1
str r0, [r3]
schedule_s68k:
ldr r8, =SekCycleCntS68k
ldr r10,=SekCycleAimS68k
ldr r3, [sp]
ldr r8, [r8]
ldr r10,[r10]
sub r0, r10, r8
mov r2, r3
add r3, r3, r2, asr #1
add r3, r3, r2, asr #3 @ cycn_s68k = (cycn + cycn/2 + cycn/8)
subs r5, r0, r3, asr #16
ble schedule_m68k @ s68k has not enough cycles
ldr r7, =PicoCpuCS68k
str r5, [sp,#4] @ run_cycle_cnt
b CycloneRunLocal
CycloneEnd_S68k:
ldr r3, =SekCycleCntS68k
ldr r0, [sp,#4] @ run_cycle_cnt
ldr r1, [r3]
str r4, [r7,#0x40] ;@ Save Current PC + Memory Base
strb r10,[r7,#0x46] ;@ Save Flags (NZCV)
sub r0, r0, r5 @ subtract leftover cycles (should be negative)
add r0, r0, r1
str r0, [r3]
schedule_m68k:
ldr r1, =PS_STEP_M68K
ldr r3, [sp] @ main_cycle_cnt
ldr r8, =SekCycleCnt
ldr r10,=SekCycleAim
subs r3, r3, r1
bmi SekRunPS_end
ldr r8, [r8]
ldr r10,[r10]
str r3, [sp] @ update main_cycle_cnt
sub r0, r10, r8
subs r5, r0, r3, asr #16
ble schedule_s68k @ m68k has not enough cycles
ldr r7, =PicoCpuCM68k
str r5, [sp,#4] @ run_cycle_cnt
b CycloneRunLocal
SekRunPS_end:
ldr r7, =PicoCpuCM68k
ldr lr, =PicoCpuCS68k
mov r0, #0
str r0, [r7,#0x98] @ remove CycloneEnd handler
str r0, [lr,#0x98]
@ return
add sp, sp, #2*4
ldmfd sp!, {r4-r8,r10,r11,pc}
CycloneRunLocal:
;@ r0-3 = Temporary registers
ldr r4,[r7,#0x40] ;@ r4 = Current PC + Memory Base
;@ r5 = Cycles
;@ r6 = Opcode Jump table
;@ r7 = Pointer to Cpu Context
;@ r8 = Current Opcode
ldrb r10,[r7,#0x46];@ r10 = Flags (NZCV)
ldr r1,[r7,#0x44] ;@ get SR high and IRQ level
orr r10,r10,r10,lsl #28 ;@ r10 = Flags 0xf0000000, cpsr format
;@ CheckInterrupt:
movs r0,r1,lsr #24 ;@ Get IRQ level
beq NoIntsLocal
cmp r0,#6 ;@ irq>6 ?
andle r1,r1,#7 ;@ Get interrupt mask
cmple r0,r1 ;@ irq<=6: Is irq<=mask ?
bgt CycloneDoInterrupt
NoIntsLocal:
;@ Check if our processor is in special state
;@ and jump to opcode handler if not
ldr r0,[r7,#0x58] ;@ state_flags
ldrh r8,[r4],#2 ;@ Fetch first opcode
tst r0,#0x03 ;@ special state?
andeq r10,r10,#0xf0000000
ldreq pc,[r6,r8,asl #2] ;@ Jump to opcode handler
CycloneSpecial2:
tst r0,#2 ;@ tracing?
bne CycloneDoTrace
;@ stopped or halted
sub r4,r4,#2
ldr r1,[r7,#0x98]
mov r5,#0
bx r1

196
pico/cd/sek.c Normal file
View file

@ -0,0 +1,196 @@
// (c) Copyright 2007 notaz, All rights reserved.
#include "../pico_int.h"
int SekCycleCntS68k=0; // cycles done in this frame
int SekCycleAimS68k=0; // cycle aim
/* context */
// Cyclone 68000
#ifdef EMU_C68K
struct Cyclone PicoCpuCS68k;
#endif
// MUSASHI 68000
#ifdef EMU_M68K
m68ki_cpu_core PicoCpuMS68k;
#endif
// FAME 68000
#ifdef EMU_F68K
M68K_CONTEXT PicoCpuFS68k;
#endif
static int new_irq_level(int level)
{
int level_new = 0, irqs;
Pico_mcd->m.s68k_pend_ints &= ~(1 << level);
irqs = Pico_mcd->m.s68k_pend_ints;
irqs &= Pico_mcd->s68k_regs[0x33];
while ((irqs >>= 1)) level_new++;
return level_new;
}
#ifdef EMU_C68K
// interrupt acknowledgement
static int SekIntAckS68k(int level)
{
int level_new = new_irq_level(level);
elprintf(EL_INTS, "s68kACK %i -> %i", level, level_new);
PicoCpuCS68k.irq = level_new;
return CYCLONE_INT_ACK_AUTOVECTOR;
}
static void SekResetAckS68k(void)
{
elprintf(EL_ANOMALY, "s68k: Reset encountered @ %06x", SekPcS68k);
}
static int SekUnrecognizedOpcodeS68k(void)
{
unsigned int pc, op;
pc = SekPcS68k;
op = PicoCpuCS68k.read16(pc);
elprintf(EL_ANOMALY, "Unrecognized Opcode %04x @ %06x", op, pc);
//exit(1);
return 0;
}
#endif
#ifdef EMU_M68K
static int SekIntAckMS68k(int level)
{
#ifndef EMU_CORE_DEBUG
int level_new = new_irq_level(level);
elprintf(EL_INTS, "s68kACK %i -> %i", level, level_new);
CPU_INT_LEVEL = level_new << 8;
#else
CPU_INT_LEVEL = 0;
#endif
return M68K_INT_ACK_AUTOVECTOR;
}
#endif
#ifdef EMU_F68K
static void SekIntAckFS68k(unsigned level)
{
int level_new = new_irq_level(level);
elprintf(EL_INTS, "s68kACK %i -> %i", level, level_new);
#ifndef EMU_CORE_DEBUG
PicoCpuFS68k.interrupts[0] = level_new;
#else
{
extern int dbg_irq_level_sub;
dbg_irq_level_sub = level_new;
PicoCpuFS68k.interrupts[0] = 0;
}
#endif
}
#endif
PICO_INTERNAL void SekInitS68k(void)
{
#ifdef EMU_C68K
// CycloneInit();
memset(&PicoCpuCS68k,0,sizeof(PicoCpuCS68k));
PicoCpuCS68k.IrqCallback=SekIntAckS68k;
PicoCpuCS68k.ResetCallback=SekResetAckS68k;
PicoCpuCS68k.UnrecognizedCallback=SekUnrecognizedOpcodeS68k;
#endif
#ifdef EMU_M68K
{
// Musashi is not very context friendly..
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoCpuMS68k);
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
m68k_init();
m68k_set_int_ack_callback(SekIntAckMS68k);
// m68k_pulse_reset(); // not yet, memmap is not set up
m68k_set_context(oldcontext);
}
#endif
#ifdef EMU_F68K
{
void *oldcontext = g_m68kcontext;
g_m68kcontext = &PicoCpuFS68k;
memset(&PicoCpuFS68k, 0, sizeof(PicoCpuFS68k));
fm68k_init();
PicoCpuFS68k.iack_handler = SekIntAckFS68k;
PicoCpuFS68k.sr = 0x2704; // Z flag
g_m68kcontext = oldcontext;
}
#endif
}
// Reset the 68000:
PICO_INTERNAL int SekResetS68k(void)
{
if (Pico.rom==NULL) return 1;
#ifdef EMU_C68K
PicoCpuCS68k.state_flags=0;
PicoCpuCS68k.osp=0;
PicoCpuCS68k.srh =0x27; // Supervisor mode
PicoCpuCS68k.flags=4; // Z set
PicoCpuCS68k.irq=0;
PicoCpuCS68k.a[7]=PicoCpuCS68k.read32(0); // Stack Pointer
PicoCpuCS68k.membase=0;
PicoCpuCS68k.pc=PicoCpuCS68k.checkpc(PicoCpuCS68k.read32(4)); // Program Counter
#endif
#ifdef EMU_M68K
{
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoCpuMS68k);
m68ki_cpu.sp[0]=0;
m68k_set_irq(0);
m68k_pulse_reset();
m68k_set_context(oldcontext);
}
#endif
#ifdef EMU_F68K
{
void *oldcontext = g_m68kcontext;
g_m68kcontext = &PicoCpuFS68k;
fm68k_reset();
g_m68kcontext = oldcontext;
}
#endif
return 0;
}
PICO_INTERNAL int SekInterruptS68k(int irq)
{
int irqs, real_irq = 1;
Pico_mcd->m.s68k_pend_ints |= 1 << irq;
irqs = Pico_mcd->m.s68k_pend_ints >> 1;
while ((irqs >>= 1)) real_irq++;
#ifdef EMU_CORE_DEBUG
{
extern int dbg_irq_level_sub;
dbg_irq_level_sub=real_irq;
return 0;
}
#endif
#ifdef EMU_C68K
PicoCpuCS68k.irq=real_irq;
#endif
#ifdef EMU_M68K
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoCpuMS68k);
m68k_set_irq(real_irq);
m68k_set_context(oldcontext);
#endif
#ifdef EMU_F68K
PicoCpuFS68k.interrupts[0]=real_irq;
#endif
return 0;
}

303
pico/debug.c Normal file
View file

@ -0,0 +1,303 @@
// some debug code, just for fun of it
// (c) Copyright 2008 notaz, All rights reserved.
#include "pico_int.h"
#include "debug.h"
#define bit(r, x) ((r>>x)&1)
#define MVP dstrp+=strlen(dstrp)
void z80_debug(char *dstr);
static char dstr[1024*8];
char *PDebugMain(void)
{
struct PicoVideo *pv=&Pico.video;
unsigned char *reg=pv->reg, r;
extern int HighPreSpr[];
int i, sprites_lo, sprites_hi;
char *dstrp;
sprites_lo = sprites_hi = 0;
for (i = 0; HighPreSpr[i] != 0; i+=2)
if (HighPreSpr[i+1] & 0x8000)
sprites_hi++;
else sprites_lo++;
dstrp = dstr;
sprintf(dstrp, "mode set 1: %02x spr lo: %2i, spr hi: %2i\n", (r=reg[0]), sprites_lo, sprites_hi); MVP;
sprintf(dstrp, "display_disable: %i, M3: %i, palette: %i, ?, hints: %i\n", bit(r,0), bit(r,1), bit(r,2), bit(r,4)); MVP;
sprintf(dstrp, "mode set 2: %02x hcnt: %i\n", (r=reg[1]), pv->reg[10]); MVP;
sprintf(dstrp, "SMS/gen: %i, pal: %i, dma: %i, vints: %i, disp: %i, TMS: %i\n",bit(r,2),bit(r,3),bit(r,4),bit(r,5),bit(r,6),bit(r,7)); MVP;
sprintf(dstrp, "mode set 3: %02x\n", (r=reg[0xB])); MVP;
sprintf(dstrp, "LSCR: %i, HSCR: %i, 2cell vscroll: %i, IE2: %i\n", bit(r,0), bit(r,1), bit(r,2), bit(r,3)); MVP;
sprintf(dstrp, "mode set 4: %02x\n", (r=reg[0xC])); MVP;
sprintf(dstrp, "interlace: %i%i, cells: %i, shadow: %i\n", bit(r,2), bit(r,1), (r&0x80) ? 40 : 32, bit(r,3)); MVP;
sprintf(dstrp, "scroll size: w: %i, h: %i SRAM: %i; eeprom: %i (%i)\n", reg[0x10]&3, (reg[0x10]&0x30)>>4,
bit(Pico.m.sram_reg, 4), bit(Pico.m.sram_reg, 2), SRam.eeprom_type); MVP;
sprintf(dstrp, "sram range: %06x-%06x, reg: %02x\n", SRam.start, SRam.end, Pico.m.sram_reg); MVP;
sprintf(dstrp, "pend int: v:%i, h:%i, vdp status: %04x\n", bit(pv->pending_ints,5), bit(pv->pending_ints,4), pv->status); MVP;
sprintf(dstrp, "pal: %i, hw: %02x, frame#: %i\n", Pico.m.pal, Pico.m.hardware, Pico.m.frame_count); MVP;
#if defined(EMU_C68K)
sprintf(dstrp, "M68k: PC: %06x, st_flg: %x, cycles: %u\n", SekPc, PicoCpuCM68k.state_flags, SekCyclesDoneT()); MVP;
sprintf(dstrp, "d0=%08x, a0=%08x, osp=%08x, irql=%i\n", PicoCpuCM68k.d[0], PicoCpuCM68k.a[0], PicoCpuCM68k.osp, PicoCpuCM68k.irq); MVP;
sprintf(dstrp, "d1=%08x, a1=%08x, sr=%04x\n", PicoCpuCM68k.d[1], PicoCpuCM68k.a[1], CycloneGetSr(&PicoCpuCM68k)); dstrp+=strlen(dstrp); MVP;
for(r=2; r < 8; r++) {
sprintf(dstrp, "d%i=%08x, a%i=%08x\n", r, PicoCpuCM68k.d[r], r, PicoCpuCM68k.a[r]); MVP;
}
#elif defined(EMU_M68K)
sprintf(dstrp, "M68k: PC: %06x, cycles: %u, irql: %i\n", SekPc, SekCyclesDoneT(), PicoCpuMM68k.int_level>>8); MVP;
#elif defined(EMU_F68K)
sprintf(dstrp, "M68k: PC: %06x, cycles: %u, irql: %i\n", SekPc, SekCyclesDoneT(), PicoCpuFM68k.interrupts[0]); MVP;
#endif
sprintf(dstrp, "z80Run: %i, z80_reset: %i, z80_bnk: %06x\n", Pico.m.z80Run, Pico.m.z80_reset, Pico.m.z80_bank68k<<15); MVP;
z80_debug(dstrp); MVP;
if (strlen(dstr) > sizeof(dstr))
elprintf(EL_STATUS, "warning: debug buffer overflow (%i/%i)\n", strlen(dstr), sizeof(dstr));
return dstr;
}
char *PDebugSpriteList(void)
{
struct PicoVideo *pvid=&Pico.video;
int table=0,u,link=0;
int max_sprites = 80;
char *dstrp;
if (!(pvid->reg[12]&1))
max_sprites = 64;
dstr[0] = 0;
dstrp = dstr;
table=pvid->reg[5]&0x7f;
if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode
table<<=8; // Get sprite table address/2
for (u=0; u < max_sprites; u++)
{
unsigned int *sprite;
int code, code2, sx, sy, height;
sprite=(unsigned int *)(Pico.vram+((table+(link<<2))&0x7ffc)); // Find sprite
// get sprite info
code = sprite[0];
// check if it is on this line
sy = (code&0x1ff)-0x80;
height = ((code>>24)&3)+1;
// masking sprite?
code2 = sprite[1];
sx = ((code2>>16)&0x1ff)-0x80;
sprintf(dstrp, "#%02i x:%4i y:%4i %ix%i %s\n", u, sx, sy, ((code>>26)&3)+1, height,
(code2&0x8000)?"hi":"lo");
MVP;
link=(code>>16)&0x7f;
if(!link) break; // End of sprites
}
return dstr;
}
#define GREEN1 0x0700
#ifdef USE_BGR555
#define YELLOW1 0x071c
#define BLUE1 0xf000
#define RED1 0x001e
#else
#define YELLOW1 0xe700
#define BLUE1 0x001e
#define RED1 0xf000
#endif
static void set16(unsigned short *p, unsigned short d, int cnt)
{
while (cnt-- > 0)
*p++ = d;
}
void PDebugShowSpriteStats(unsigned short *screen, int stride)
{
int lines, i, u, step;
unsigned short *dest;
unsigned char *p;
step = (320-4*4-1) / MAX_LINE_SPRITES;
lines = 240;
if (!Pico.m.pal || !(Pico.video.reg[1]&8))
lines = 224, screen += stride*8;
for (i = 0; i < lines; i++)
{
dest = screen + stride*i;
p = &HighLnSpr[i][0];
// sprite graphs
for (u = 0; u < (p[0] & 0x7f); u++) {
set16(dest, (p[3+u] & 0x80) ? YELLOW1 : GREEN1, step);
dest += step;
}
// flags
dest = screen + stride*i + 320-4*4;
if (p[1] & 0x40) set16(dest+4*0, GREEN1, 4);
if (p[1] & 0x80) set16(dest+4*1, YELLOW1, 4);
if (p[1] & 0x20) set16(dest+4*2, BLUE1, 4);
if (p[1] & 0x10) set16(dest+4*3, RED1, 4);
}
// draw grid
for (i = step*5; i <= 320-4*4-1; i += step*5) {
for (u = 0; u < lines; u++)
screen[i + u*stride] = 0x182;
}
}
void PDebugShowPalette(unsigned short *screen, int stride)
{
unsigned int *spal=(void *)Pico.cram;
unsigned int *dpal=(void *)HighPal;
int x, y, i;
for (i = 0x3f/2; i >= 0; i--)
#ifdef USE_BGR555
dpal[i] = ((spal[i]&0x000f000f)<< 1)|((spal[i]&0x00f000f0)<<3)|((spal[i]&0x0f000f00)<<4);
#else
dpal[i] = ((spal[i]&0x000f000f)<<12)|((spal[i]&0x00f000f0)<<3)|((spal[i]&0x0f000f00)>>7);
#endif
for (i = 0x3f; i >= 0; i--)
HighPal[0x40|i] = (unsigned short)((HighPal[i]>>1)&0x738e);
for (i = 0x3f; i >= 0; i--) {
int t=HighPal[i]&0xe71c;t+=0x4208;if(t&0x20)t|=0x1c;if(t&0x800)t|=0x700;if(t&0x10000)t|=0xe000;t&=0xe71c;
HighPal[0x80|i] = (unsigned short)t;
}
screen += 16*stride+8;
for (y = 0; y < 8*4; y++)
for (x = 0; x < 8*16; x++)
screen[x + y*stride] = HighPal[x/8 + (y/8)*16];
screen += 160;
for (y = 0; y < 8*4; y++)
for (x = 0; x < 8*16; x++)
screen[x + y*stride] = HighPal[(x/8 + (y/8)*16) | 0x40];
screen += stride*48;
for (y = 0; y < 8*4; y++)
for (x = 0; x < 8*16; x++)
screen[x + y*stride] = HighPal[(x/8 + (y/8)*16) | 0x80];
Pico.m.dirtyPal = 1;
}
#if defined(DRAW2_OVERRIDE_LINE_WIDTH)
#define DRAW2_LINE_WIDTH DRAW2_OVERRIDE_LINE_WIDTH
#else
#define DRAW2_LINE_WIDTH 328
#endif
void PDebugShowSprite(unsigned short *screen, int stride, int which)
{
struct PicoVideo *pvid=&Pico.video;
int table=0,u,link=0,*sprite=0,*fsprite,oldsprite[2];
int x,y,max_sprites = 80, oldcol, oldreg;
if (!(pvid->reg[12]&1))
max_sprites = 64;
table=pvid->reg[5]&0x7f;
if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode
table<<=8; // Get sprite table address/2
for (u=0; u < max_sprites && u <= which; u++)
{
sprite=(int *)(Pico.vram+((table+(link<<2))&0x7ffc)); // Find sprite
link=(sprite[0]>>16)&0x7f;
if (!link) break; // End of sprites
}
if (u >= max_sprites) return;
fsprite = (int *)(Pico.vram+(table&0x7ffc));
oldsprite[0] = fsprite[0];
oldsprite[1] = fsprite[1];
fsprite[0] = (sprite[0] & ~0x007f01ff) | 0x000080;
fsprite[1] = (sprite[1] & ~0x01ff8000) | 0x800000;
oldreg = pvid->reg[7];
oldcol = Pico.cram[0];
pvid->reg[7] = 0;
Pico.cram[0] = 0;
PicoDrawMask = PDRAW_SPRITES_LOW_ON;
PicoFrameFull();
for (y = 0; y < 8*4; y++)
{
unsigned char *ps = PicoDraw2FB + DRAW2_LINE_WIDTH*y + 8;
for (x = 0; x < 8*4; x++)
if (ps[x]) screen[x] = HighPal[ps[x]], ps[x] = 0;
screen += stride;
}
fsprite[0] = oldsprite[0];
fsprite[1] = oldsprite[1];
pvid->reg[7] = oldreg;
Pico.cram[0] = oldcol;
PicoDrawMask = -1;
}
#define dump_ram(ram,fname) \
{ \
unsigned short *sram = (unsigned short *) ram; \
FILE *f; \
int i; \
\
for (i = 0; i < sizeof(ram)/2; i++) \
sram[i] = (sram[i]<<8) | (sram[i]>>8); \
f = fopen(fname, "wb"); \
if (f) { \
fwrite(ram, 1, sizeof(ram), f); \
fclose(f); \
} \
for (i = 0; i < sizeof(ram)/2; i++) \
sram[i] = (sram[i]<<8) | (sram[i]>>8); \
}
#define dump_ram_noswab(ram,fname) \
{ \
FILE *f; \
f = fopen(fname, "wb"); \
if (f) { \
fwrite(ram, 1, sizeof(ram), f); \
fclose(f); \
} \
}
void PDebugDumpMem(void)
{
dump_ram(Pico.ram, "dumps/ram.bin");
dump_ram_noswab(Pico.zram, "dumps/zram.bin");
dump_ram(Pico.vram, "dumps/vram.bin");
dump_ram(Pico.cram, "dumps/cram.bin");
dump_ram(Pico.vsram,"dumps/vsram.bin");
if (PicoAHW & PAHW_MCD)
{
dump_ram(Pico_mcd->prg_ram, "dumps/prg_ram.bin");
if (Pico_mcd->s68k_regs[3]&4) // 1M mode?
wram_1M_to_2M(Pico_mcd->word_ram2M);
dump_ram(Pico_mcd->word_ram2M, "dumps/word_ram_2M.bin");
wram_2M_to_1M(Pico_mcd->word_ram2M);
dump_ram(Pico_mcd->word_ram1M[0], "dumps/word_ram_1M_0.bin");
dump_ram(Pico_mcd->word_ram1M[1], "dumps/word_ram_1M_1.bin");
if (!(Pico_mcd->s68k_regs[3]&4)) // 2M mode?
wram_2M_to_1M(Pico_mcd->word_ram2M);
dump_ram_noswab(Pico_mcd->pcm_ram,"dumps/pcm_ram.bin");
dump_ram_noswab(Pico_mcd->bram, "dumps/bram.bin");
}
}

8
pico/debug.h Normal file
View file

@ -0,0 +1,8 @@
char *PDebugMain(void);
char *PDebugSpriteList(void);
void PDebugShowSpriteStats(unsigned short *screen, int stride);
void PDebugShowPalette(unsigned short *screen, int stride);
void PDebugShowSprite(unsigned short *screen, int stride, int which);
void PDebugDumpMem(void);

221
pico/debugCPU.c Normal file
View file

@ -0,0 +1,221 @@
#include "pico_int.h"
typedef unsigned char u8;
static unsigned int pppc, ops=0;
extern unsigned int lastread_a, lastread_d[16], lastwrite_cyc_d[16], lastwrite_mus_d[16];
extern int lrp_cyc, lrp_mus, lwp_cyc, lwp_mus;
unsigned int old_regs[16], old_sr, ppop, have_illegal = 0;
int dbg_irq_level = 0, dbg_irq_level_sub = 0;
#undef dprintf
#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__)
#if defined(EMU_C68K)
static struct Cyclone *currentC68k = NULL;
#define other_set_sub(s) currentC68k=(s)?&PicoCpuCS68k:&PicoCpuCM68k;
#define other_get_sr() CycloneGetSr(currentC68k)
#define other_dar(i) currentC68k->d[i]
#define other_osp currentC68k->osp
#define other_get_irq() currentC68k->irq
#define other_set_irq(i) currentC68k->irq=i
#define other_is_stopped() (currentC68k->state_flags&1)
#define other_is_tracing() ((currentC68k->state_flags&2)?1:0)
#elif defined(EMU_F68K)
#define other_set_sub(s) g_m68kcontext=(s)?&PicoCpuFS68k:&PicoCpuFM68k;
#define other_get_sr() g_m68kcontext->sr
#define other_dar(i) ((unsigned int*)g_m68kcontext->dreg)[i]
#define other_osp g_m68kcontext->asp
#define other_get_irq() g_m68kcontext->interrupts[0]
#define other_set_irq(irq) g_m68kcontext->interrupts[0]=irq
#define other_is_stopped() ((g_m68kcontext->execinfo&FM68K_HALTED)?1:0)
#define other_is_tracing() ((g_m68kcontext->execinfo&FM68K_EMULATE_TRACE)?1:0)
#else
#error other core missing, don't compile this file
#endif
static int otherRun(void)
{
#if defined(EMU_C68K)
currentC68k->cycles=1;
CycloneRun(currentC68k);
return 1-currentC68k->cycles;
#elif defined(EMU_F68K)
return fm68k_emulate(1, 0);
#endif
}
//static
void dumpPCandExit(int is_sub)
{
char buff[128];
int i;
m68k_disassemble(buff, pppc, M68K_CPU_TYPE_68000);
dprintf("PC: %06x: %04x: %s", pppc, ppop, buff);
dprintf(" this | prev");
for(i=0; i < 8; i++)
dprintf("d%i=%08x, a%i=%08x | d%i=%08x, a%i=%08x", i, other_dar(i), i, other_dar(i+8), i, old_regs[i], i, old_regs[i+8]);
dprintf("SR: %04x | %04x (??s? 0iii 000x nzvc)", other_get_sr(), old_sr);
dprintf("last_read: %08x @ %06x", lastread_d[--lrp_cyc&15], lastread_a);
dprintf("ops done: %i, is_sub: %i", ops, is_sub);
exit(1);
}
int CM_compareRun(int cyc, int is_sub)
{
char *str;
int cyc_done=0, cyc_other, cyc_musashi, *irq_level, err=0;
unsigned int i, pc, mu_sr;
m68ki_cpu_p=is_sub?&PicoCpuMS68k:&PicoCpuMM68k;
other_set_sub(is_sub);
lrp_cyc = lrp_mus = 0;
while (cyc_done < cyc)
{
if (have_illegal && m68k_read_disassembler_16(m68ki_cpu.pc) != 0x4e73) // not rte
{
have_illegal = 0;
m68ki_cpu.pc += 2;
#ifdef EMU_C68K
currentC68k->pc=currentC68k->checkpc(currentC68k->pc + 2);
#endif
}
// hacks for test_misc2
if (m68ki_cpu.pc == 0x0002e0 && m68k_read_disassembler_16(m68ki_cpu.pc) == 0x4e73)
{
// get out of "priviledge violation" loop
have_illegal = 1;
//m68ki_cpu.s_flag = SFLAG_SET;
//currentC68k->srh|=0x20;
}
pppc = is_sub ? SekPcS68k : SekPc;
ppop = m68k_read_disassembler_16(pppc);
memcpy(old_regs, &other_dar(0), 4*16);
old_sr = other_get_sr();
#if 0
{
char buff[128];
dprintf("---");
m68k_disassemble(buff, pppc, M68K_CPU_TYPE_68000);
dprintf("PC: %06x: %04x: %s", pppc, ppop, buff);
//dprintf("A7: %08x", currentC68k->a[7]);
}
#endif
irq_level = is_sub ? &dbg_irq_level_sub : &dbg_irq_level;
if (*irq_level)
{
other_set_irq(*irq_level);
m68k_set_irq(*irq_level);
*irq_level=0;
}
cyc_other=otherRun();
// Musashi takes irq even if it hasn't got cycles left, let othercpu do it too
if (other_get_irq() && other_get_irq() > ((other_get_sr()>>8)&7))
cyc_other+=otherRun();
cyc_musashi=m68k_execute(1);
if (cyc_other != cyc_musashi) {
dprintf("cycles: %i vs %i", cyc_other, cyc_musashi);
err=1;
}
if (lrp_cyc != lrp_mus) {
dprintf("lrp: %i vs %i", lrp_cyc&15, lrp_mus&15);
err=1;
}
if (lwp_cyc != lwp_mus) {
dprintf("lwp: %i vs %i", lwp_cyc&15, lwp_mus&15);
err=1;
}
for (i=0; i < 16; i++) {
if (lastwrite_cyc_d[i] != lastwrite_mus_d[i]) {
dprintf("lastwrite: [%i]= %08x vs %08x", i, lastwrite_cyc_d[i], lastwrite_mus_d[i]);
err=1;
break;
}
}
// compare PC
pc = is_sub ? SekPcS68k : SekPc;
m68ki_cpu.pc&=~1;
if (pc != m68ki_cpu.pc) {
dprintf("PC: %06x vs %06x", pc, m68ki_cpu.pc);
err=1;
}
#if 0
if( SekPc > Pico.romsize || SekPc < 0x200 ) {
dprintf("PC out of bounds: %06x", SekPc);
err=1;
}
#endif
// compare regs
for (i=0; i < 16; i++) {
if (other_dar(i) != m68ki_cpu.dar[i]) {
str = (i < 8) ? "d" : "a";
dprintf("reg: %s%i: %08x vs %08x", str, i&7, other_dar(i), m68ki_cpu.dar[i]);
err=1;
}
}
// SR
if (other_get_sr() != (mu_sr = m68k_get_reg(NULL, M68K_REG_SR))) {
dprintf("SR: %04x vs %04x (??s? 0iii 000x nzvc)", other_get_sr(), mu_sr);
err=1;
}
// IRQl
if (other_get_irq() != (m68ki_cpu.int_level>>8)) {
dprintf("IRQ: %i vs %i", other_get_irq(), (m68ki_cpu.int_level>>8));
err=1;
}
// OSP/USP
if (other_osp != m68ki_cpu.sp[((mu_sr>>11)&4)^4]) {
dprintf("OSP: %06x vs %06x", other_osp, m68ki_cpu.sp[((mu_sr>>11)&4)^4]);
err=1;
}
// stopped
if ((other_is_stopped() && !m68ki_cpu.stopped) || (!other_is_stopped() && m68ki_cpu.stopped)) {
dprintf("stopped: %i vs %i", other_is_stopped(), m68ki_cpu.stopped);
err=1;
}
// tracing
if((other_is_tracing() && !m68ki_tracing) || (!other_is_tracing() && m68ki_tracing)) {
dprintf("tracing: %i vs %i", other_is_tracing(), m68ki_tracing);
err=1;
}
if(err) dumpPCandExit(is_sub);
#if 0
if (m68ki_cpu.dar[15] < 0x00ff0000 || m68ki_cpu.dar[15] >= 0x01000000)
{
other_dar(15) = m68ki_cpu.dar[15] = 0xff8000;
}
#endif
#if 0
m68k_set_reg(M68K_REG_SR, ((mu_sr-1)&~0x2000)|(mu_sr&0x2000)); // broken
CycloneSetSr(currentC68k, ((mu_sr-1)&~0x2000)|(mu_sr&0x2000));
currentC68k->stopped = m68ki_cpu.stopped = 0;
if(SekPc > 0x400 && (currentC68k->a[7] < 0xff0000 || currentC68k->a[7] > 0xffffff))
currentC68k->a[7] = m68ki_cpu.dar[15] = 0xff8000;
#endif
cyc_done += cyc_other;
ops++;
}
return cyc_done;
}

1473
pico/draw.c Normal file

File diff suppressed because it is too large Load diff

623
pico/draw2.c Normal file
View file

@ -0,0 +1,623 @@
// This is part of Pico Library
// (c) Copyright 2007, Grazvydas "notaz" Ignotas
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
// this is a frame-based renderer, alternative to Dave's line based which is in Draw.c
#include "pico_int.h"
// port_config.h include must define these 2 defines:
// #define START_ROW 1 // which row of tiles to start rendering at?
// #define END_ROW 27 // ..end
// one row means 8 pixels. If above example was used, (27-1)*8=208 lines would be rendered.
#define TILE_ROWS END_ROW-START_ROW
#define USE_CACHE
// note: this is not implemented in ARM asm
#if defined(DRAW2_OVERRIDE_LINE_WIDTH)
#define LINE_WIDTH DRAW2_OVERRIDE_LINE_WIDTH
#else
#define LINE_WIDTH 328
#endif
static int HighCache2A[41*(TILE_ROWS+1)+1+1]; // caches for high layers
static int HighCache2B[41*(TILE_ROWS+1)+1+1];
unsigned short *PicoCramHigh=Pico.cram; // pointer to CRAM buff (0x40 shorts), converted to native device color (works only with 16bit for now)
void (*PicoPrepareCram)()=0; // prepares PicoCramHigh for renderer to use
// stuff available in asm:
#ifdef _ASM_DRAW_C
void BackFillFull(int reg7);
void DrawLayerFull(int plane, int *hcache, int planestart, int planeend);
void DrawTilesFromCacheF(int *hc);
void DrawWindowFull(int start, int end, int prio);
void DrawSpriteFull(unsigned int *sprite);
#else
static int TileXnormYnorm(unsigned char *pd,int addr,unsigned char pal)
{
unsigned int pack=0; unsigned int t=0, blank = 1;
int i;
for(i=8; i; i--, addr+=2, pd += LINE_WIDTH) {
pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
if(!pack) continue;
t=pack&0x0000f000; if (t) pd[0]=(unsigned char)((t>>12)|pal);
t=pack&0x00000f00; if (t) pd[1]=(unsigned char)((t>> 8)|pal);
t=pack&0x000000f0; if (t) pd[2]=(unsigned char)((t>> 4)|pal);
t=pack&0x0000000f; if (t) pd[3]=(unsigned char)((t )|pal);
t=pack&0xf0000000; if (t) pd[4]=(unsigned char)((t>>28)|pal);
t=pack&0x0f000000; if (t) pd[5]=(unsigned char)((t>>24)|pal);
t=pack&0x00f00000; if (t) pd[6]=(unsigned char)((t>>20)|pal);
t=pack&0x000f0000; if (t) pd[7]=(unsigned char)((t>>16)|pal);
blank = 0;
}
return blank; // Tile blank?
}
static int TileXflipYnorm(unsigned char *pd,int addr,unsigned char pal)
{
unsigned int pack=0; unsigned int t=0, blank = 1;
int i;
for(i=8; i; i--, addr+=2, pd += LINE_WIDTH) {
pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
if(!pack) continue;
t=pack&0x000f0000; if (t) pd[0]=(unsigned char)((t>>16)|pal);
t=pack&0x00f00000; if (t) pd[1]=(unsigned char)((t>>20)|pal);
t=pack&0x0f000000; if (t) pd[2]=(unsigned char)((t>>24)|pal);
t=pack&0xf0000000; if (t) pd[3]=(unsigned char)((t>>28)|pal);
t=pack&0x0000000f; if (t) pd[4]=(unsigned char)((t )|pal);
t=pack&0x000000f0; if (t) pd[5]=(unsigned char)((t>> 4)|pal);
t=pack&0x00000f00; if (t) pd[6]=(unsigned char)((t>> 8)|pal);
t=pack&0x0000f000; if (t) pd[7]=(unsigned char)((t>>12)|pal);
blank = 0;
}
return blank; // Tile blank?
}
static int TileXnormYflip(unsigned char *pd,int addr,unsigned char pal)
{
unsigned int pack=0; unsigned int t=0, blank = 1;
int i;
addr+=14;
for(i=8; i; i--, addr-=2, pd += LINE_WIDTH) {
pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
if(!pack) continue;
t=pack&0x0000f000; if (t) pd[0]=(unsigned char)((t>>12)|pal);
t=pack&0x00000f00; if (t) pd[1]=(unsigned char)((t>> 8)|pal);
t=pack&0x000000f0; if (t) pd[2]=(unsigned char)((t>> 4)|pal);
t=pack&0x0000000f; if (t) pd[3]=(unsigned char)((t )|pal);
t=pack&0xf0000000; if (t) pd[4]=(unsigned char)((t>>28)|pal);
t=pack&0x0f000000; if (t) pd[5]=(unsigned char)((t>>24)|pal);
t=pack&0x00f00000; if (t) pd[6]=(unsigned char)((t>>20)|pal);
t=pack&0x000f0000; if (t) pd[7]=(unsigned char)((t>>16)|pal);
blank = 0;
}
return blank; // Tile blank?
}
static int TileXflipYflip(unsigned char *pd,int addr,unsigned char pal)
{
unsigned int pack=0; unsigned int t=0, blank = 1;
int i;
addr+=14;
for(i=8; i; i--, addr-=2, pd += LINE_WIDTH) {
pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
if(!pack) continue;
t=pack&0x000f0000; if (t) pd[0]=(unsigned char)((t>>16)|pal);
t=pack&0x00f00000; if (t) pd[1]=(unsigned char)((t>>20)|pal);
t=pack&0x0f000000; if (t) pd[2]=(unsigned char)((t>>24)|pal);
t=pack&0xf0000000; if (t) pd[3]=(unsigned char)((t>>28)|pal);
t=pack&0x0000000f; if (t) pd[4]=(unsigned char)((t )|pal);
t=pack&0x000000f0; if (t) pd[5]=(unsigned char)((t>> 4)|pal);
t=pack&0x00000f00; if (t) pd[6]=(unsigned char)((t>> 8)|pal);
t=pack&0x0000f000; if (t) pd[7]=(unsigned char)((t>>12)|pal);
blank = 0;
}
return blank; // Tile blank?
}
// start: (tile_start<<16)|row_start, end: [same]
static void DrawWindowFull(int start, int end, int prio)
{
struct PicoVideo *pvid=&Pico.video;
int nametab, nametab_step, trow, tilex, blank=-1, code;
unsigned char *scrpos = PicoDraw2FB;
int tile_start, tile_end; // in cells
// parse ranges
tile_start = start>>16;
tile_end = end>>16;
start = start<<16>>16;
end = end<<16>>16;
// Find name table line:
if (pvid->reg[12]&1)
{
nametab=(pvid->reg[3]&0x3c)<<9; // 40-cell mode
nametab_step = 1<<6;
}
else
{
nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode
nametab_step = 1<<5;
}
nametab += nametab_step*start;
// check priority
code=Pico.vram[nametab+tile_start];
if ((code>>15) != prio) return; // hack: just assume that whole window uses same priority
scrpos+=8*LINE_WIDTH+8;
scrpos+=8*LINE_WIDTH*(start-START_ROW);
// do a window until we reach planestart row
for(trow = start; trow < end; trow++, nametab+=nametab_step) { // current tile row
for (tilex=tile_start; tilex<tile_end; tilex++)
{
int code,addr,zero=0;
// unsigned short *pal=NULL;
unsigned char pal;
code=Pico.vram[nametab+tilex];
if (code==blank) continue;
// Get tile address/2:
addr=(code&0x7ff)<<4;
// pal=PicoCramHigh+((code>>9)&0x30);
pal=(unsigned char)((code>>9)&0x30);
switch((code>>11)&3) {
case 0: zero=TileXnormYnorm(scrpos+(tilex<<3),addr,pal); break;
case 1: zero=TileXflipYnorm(scrpos+(tilex<<3),addr,pal); break;
case 2: zero=TileXnormYflip(scrpos+(tilex<<3),addr,pal); break;
case 3: zero=TileXflipYflip(scrpos+(tilex<<3),addr,pal); break;
}
if(zero) blank=code; // We know this tile is blank now
}
scrpos += LINE_WIDTH*8;
}
}
static void DrawLayerFull(int plane, int *hcache, int planestart, int planeend)
{
struct PicoVideo *pvid=&Pico.video;
static char shift[4]={5,6,6,7}; // 32,64 or 128 sized tilemaps
int width, height, ymask, htab;
int nametab, hscroll=0, vscroll, cells;
unsigned char *scrpos;
int blank=-1, xmask, nametab_row, trow;
// parse ranges
cells = (planeend>>16)-(planestart>>16);
planestart = planestart<<16>>16;
planeend = planeend<<16>>16;
// Work out the Tiles to draw
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
if(!(pvid->reg[11]&3)) { // full screen scroll
// Get horizontal scroll value
hscroll=Pico.vram[htab&0x7fff];
htab = 0; // this marks that we don't have to update scroll value
}
// Work out the name table size: 32 64 or 128 tiles (0-3)
width=pvid->reg[16];
height=(width>>4)&3; width&=3;
xmask=(1<<shift[width ])-1; // X Mask in tiles
ymask=(height<<5)|0x1f; // Y Mask in tiles
if(width == 1) ymask&=0x3f;
else if(width>1) ymask =0x1f;
// Find name table:
if (plane==0) nametab=(pvid->reg[2]&0x38)<< 9; // A
else nametab=(pvid->reg[4]&0x07)<<12; // B
scrpos = PicoDraw2FB;
scrpos+=8*LINE_WIDTH*(planestart-START_ROW);
// Get vertical scroll value:
vscroll=Pico.vsram[plane]&0x1ff;
scrpos+=(8-(vscroll&7))*LINE_WIDTH;
if(vscroll&7) planeend++; // we have vertically clipped tiles due to vscroll, so we need 1 more row
*hcache++ = 8-(vscroll&7); // push y-offset to tilecache
for(trow = planestart; trow < planeend; trow++) { // current tile row
int cellc=cells,tilex,dx;
// Find the tile row in the name table
//ts.line=(vscroll+Scanline)&ymask;
//ts.nametab+=(ts.line>>3)<<shift[width];
nametab_row = nametab + (((trow+(vscroll>>3))&ymask)<<shift[width]); // pointer to nametable entries for this row
// update hscroll if needed
if(htab) {
int htaddr=htab+(trow<<4);
if(trow) htaddr-=(vscroll&7)<<1;
hscroll=Pico.vram[htaddr&0x7fff];
}
// Draw tiles across screen:
tilex=(-hscroll)>>3;
dx=((hscroll-1)&7)+1;
if(dx != 8) cellc++; // have hscroll, do more cells
for (; cellc; dx+=8,tilex++,cellc--)
{
int code=0,addr=0,zero=0;
// unsigned short *pal=NULL;
unsigned char pal;
code=Pico.vram[nametab_row+(tilex&xmask)];
if (code==blank) continue;
if (code>>15) { // high priority tile
*hcache++ = code|(dx<<16)|(trow<<27); // cache it
continue;
}
// Get tile address/2:
addr=(code&0x7ff)<<4;
// pal=PicoCramHigh+((code>>9)&0x30);
pal=(unsigned char)((code>>9)&0x30);
switch((code>>11)&3) {
case 0: zero=TileXnormYnorm(scrpos+dx,addr,pal); break;
case 1: zero=TileXflipYnorm(scrpos+dx,addr,pal); break;
case 2: zero=TileXnormYflip(scrpos+dx,addr,pal); break;
case 3: zero=TileXflipYflip(scrpos+dx,addr,pal); break;
}
if(zero) blank=code; // We know this tile is blank now
}
scrpos += LINE_WIDTH*8;
}
*hcache = 0; // terminate cache
}
static void DrawTilesFromCacheF(int *hc)
{
int code, addr, zero = 0;
unsigned int prevy=0xFFFFFFFF;
// unsigned short *pal;
unsigned char pal;
short blank=-1; // The tile we know is blank
unsigned char *scrpos = PicoDraw2FB, *pd = 0;
// *hcache++ = code|(dx<<16)|(trow<<27); // cache it
scrpos+=(*hc++)*LINE_WIDTH - START_ROW*LINE_WIDTH*8;
while((code=*hc++)) {
if((short)code == blank) continue;
// y pos
if(((unsigned)code>>27) != prevy) {
prevy = (unsigned)code>>27;
pd = scrpos + prevy*LINE_WIDTH*8;
}
// Get tile address/2:
addr=(code&0x7ff)<<4;
// pal=PicoCramHigh+((code>>9)&0x30);
pal=(unsigned char)((code>>9)&0x30);
switch((code>>11)&3) {
case 0: zero=TileXnormYnorm(pd+((code>>16)&0x1ff),addr,pal); break;
case 1: zero=TileXflipYnorm(pd+((code>>16)&0x1ff),addr,pal); break;
case 2: zero=TileXnormYflip(pd+((code>>16)&0x1ff),addr,pal); break;
case 3: zero=TileXflipYflip(pd+((code>>16)&0x1ff),addr,pal); break;
}
if(zero) blank=(short)code;
}
}
// sx and sy are coords of virtual screen with 8pix borders on top and on left
static void DrawSpriteFull(unsigned int *sprite)
{
int width=0,height=0;
// unsigned short *pal=NULL;
unsigned char pal;
int tile,code,tdeltax,tdeltay;
unsigned char *scrpos;
int sx, sy;
sy=sprite[0];
height=sy>>24;
sy=(sy&0x1ff)-0x78; // Y
width=(height>>2)&3; height&=3;
width++; height++; // Width and height in tiles
code=sprite[1];
sx=((code>>16)&0x1ff)-0x78; // X
tile=code&0x7ff; // Tile number
tdeltax=height; // Delta to increase tile by going right
tdeltay=1; // Delta to increase tile by going down
if (code&0x0800) { tdeltax=-tdeltax; tile+=height*(width-1); } // Flip X
if (code&0x1000) { tdeltay=-tdeltay; tile+=height-1; } // Flip Y
//delta<<=4; // Delta of address
// pal=PicoCramHigh+((code>>9)&0x30); // Get palette pointer
pal=(unsigned char)((code>>9)&0x30);
// goto first vertically visible tile
while(sy <= START_ROW*8) { sy+=8; tile+=tdeltay; height--; }
scrpos = PicoDraw2FB;
scrpos+=(sy-START_ROW*8)*LINE_WIDTH;
for (; height > 0; height--, sy+=8, tile+=tdeltay)
{
int w = width, x=sx, t=tile;
if(sy >= END_ROW*8+8) return; // offscreen
for (; w; w--,x+=8,t+=tdeltax)
{
if(x<=0) continue;
if(x>=328) break; // Offscreen
t&=0x7fff; // Clip tile address
switch((code>>11)&3) {
case 0: TileXnormYnorm(scrpos+x,t<<4,pal); break;
case 1: TileXflipYnorm(scrpos+x,t<<4,pal); break;
case 2: TileXnormYflip(scrpos+x,t<<4,pal); break;
case 3: TileXflipYflip(scrpos+x,t<<4,pal); break;
}
}
scrpos+=8*LINE_WIDTH;
}
}
#endif
static void DrawAllSpritesFull(int prio, int maxwidth)
{
struct PicoVideo *pvid=&Pico.video;
int table=0,maskrange=0;
int i,u,link=0;
unsigned int *sprites[80]; // Sprites
int y_min=START_ROW*8, y_max=END_ROW*8; // for a simple sprite masking
table=pvid->reg[5]&0x7f;
if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode
table<<=8; // Get sprite table address/2
for (i=u=0; u < 80; u++)
{
unsigned int *sprite=NULL;
int code, code2, sx, sy, height;
sprite=(unsigned int *)(Pico.vram+((table+(link<<2))&0x7ffc)); // Find sprite
// get sprite info
code = sprite[0];
// check if it is not hidden vertically
sy = (code&0x1ff)-0x80;
height = (((code>>24)&3)+1)<<3;
if(sy+height <= y_min || sy > y_max) goto nextsprite;
// masking sprite?
code2=sprite[1];
sx = (code2>>16)&0x1ff;
if(!sx) {
int to = sy+height; // sy ~ from
if(maskrange) {
// try to merge with previous range
if((maskrange>>16)+1 >= sy && (maskrange>>16) <= to && (maskrange&0xffff) < sy) sy = (maskrange&0xffff);
else if((maskrange&0xffff)-1 <= to && (maskrange&0xffff) >= sy && (maskrange>>16) > to) to = (maskrange>>16);
}
// support only very simple masking (top and bottom of screen)
if(sy <= y_min && to+1 > y_min) y_min = to+1;
else if(to >= y_max && sy-1 < y_max) y_max = sy-1;
else maskrange=sy|(to<<16);
goto nextsprite;
}
// priority
if(((code2>>15)&1) != prio) goto nextsprite; // wrong priority
// check if sprite is not hidden horizontally
sx -= 0x78; // Get X coordinate + 8
if(sx <= -8*3 || sx >= maxwidth) goto nextsprite;
// sprite is good, save it's index
sprites[i++]=sprite;
nextsprite:
// Find next sprite
link=(code>>16)&0x7f;
if(!link) break; // End of sprites
}
// Go through sprites backwards:
for (i-- ;i>=0; i--)
{
DrawSpriteFull(sprites[i]);
}
}
#ifndef _ASM_DRAW_C
static void BackFillFull(int reg7)
{
unsigned int back;
// Start with a background color:
// back=PicoCramHigh[reg7&0x3f];
back=reg7&0x3f;
back|=back<<8;
back|=back<<16;
memset32((int *)PicoDraw2FB, back, LINE_WIDTH*(8+(END_ROW-START_ROW)*8)/4);
}
#endif
static void DrawDisplayFull(void)
{
struct PicoVideo *pvid=&Pico.video;
int win, edge=0, hvwin=0; // LSb->MSb: hwin&plane, vwin&plane, full
int planestart=START_ROW, planeend=END_ROW; // plane A start/end when window shares display with plane A (in tile rows or columns)
int winstart=START_ROW, winend=END_ROW; // same for window
int maxw, maxcolc; // max width and col cells
if(pvid->reg[12]&1) {
maxw = 328; maxcolc = 40;
} else {
maxw = 264; maxcolc = 32;
}
// horizontal window?
if ((win=pvid->reg[0x12]))
{
hvwin=1; // hwindow shares display with plane A
edge=win&0x1f;
if(win == 0x80) {
// fullscreen window
hvwin=4;
} else if(win < 0x80) {
// window on the top
if(edge <= START_ROW) hvwin=0; // window not visible in our drawing region
else if(edge >= END_ROW) hvwin=4;
else planestart = winend = edge;
} else if(win > 0x80) {
// window at the bottom
if(edge >= END_ROW) hvwin=0;
else planeend = winstart = edge;
}
}
// check for vertical window, but only if win is not fullscreen
if (hvwin != 4)
{
win=pvid->reg[0x11];
edge=win&0x1f;
if (win&0x80) {
if(!edge) hvwin=4;
else if(edge < (maxcolc>>1)) {
// window is on the right
hvwin|=2;
planeend|=edge<<17;
winstart|=edge<<17;
winend|=maxcolc<<16;
}
} else {
if(edge >= (maxcolc>>1)) hvwin=4;
else if(edge) {
// window is on the left
hvwin|=2;
winend|=edge<<17;
planestart|=edge<<17;
planeend|=maxcolc<<16;
}
}
}
if (hvwin==1) { winend|=maxcolc<<16; planeend|=maxcolc<<16; }
HighCache2A[1] = HighCache2B[1] = 0;
if (PicoDrawMask & PDRAW_LAYERB_ON)
DrawLayerFull(1, HighCache2B, START_ROW, (maxcolc<<16)|END_ROW);
if (PicoDrawMask & PDRAW_LAYERA_ON) switch (hvwin)
{
case 4:
// fullscreen window
DrawWindowFull(START_ROW, (maxcolc<<16)|END_ROW, 0);
break;
case 3:
// we have plane A and both v and h windows
DrawLayerFull(0, HighCache2A, planestart, planeend);
DrawWindowFull( winstart&~0xff0000, (winend&~0xff0000)|(maxcolc<<16), 0); // h
DrawWindowFull((winstart&~0xff)|START_ROW, (winend&~0xff)|END_ROW, 0); // v
break;
case 2:
case 1:
// both window and plane A visible, window is vertical XOR horizontal
DrawLayerFull(0, HighCache2A, planestart, planeend);
DrawWindowFull(winstart, winend, 0);
break;
default:
// fullscreen plane A
DrawLayerFull(0, HighCache2A, START_ROW, (maxcolc<<16)|END_ROW);
break;
}
if (PicoDrawMask & PDRAW_SPRITES_LOW_ON)
DrawAllSpritesFull(0, maxw);
if (HighCache2B[1]) DrawTilesFromCacheF(HighCache2B);
if (HighCache2A[1]) DrawTilesFromCacheF(HighCache2A);
if (PicoDrawMask & PDRAW_LAYERA_ON) switch (hvwin)
{
case 4:
// fullscreen window
DrawWindowFull(START_ROW, (maxcolc<<16)|END_ROW, 1);
break;
case 3:
// we have plane A and both v and h windows
DrawWindowFull( winstart&~0xff0000, (winend&~0xff0000)|(maxcolc<<16), 1); // h
DrawWindowFull((winstart&~0xff)|START_ROW, (winend&~0xff)|END_ROW, 1); // v
break;
case 2:
case 1:
// both window and plane A visible, window is vertical XOR horizontal
DrawWindowFull(winstart, winend, 1);
break;
}
if (PicoDrawMask & PDRAW_SPRITES_HI_ON)
DrawAllSpritesFull(1, maxw);
}
PICO_INTERNAL void PicoFrameFull()
{
// prepare cram?
if (PicoPrepareCram) PicoPrepareCram();
// Draw screen:
BackFillFull(Pico.video.reg[7]);
if (Pico.video.reg[1]&0x40) DrawDisplayFull();
}

929
pico/draw2_arm.s Normal file
View file

@ -0,0 +1,929 @@
@ vim:filetype=armasm
@ assembly optimized versions of most funtions from draw2.c
@ this is highly specialized, be careful if changing related C code!
@ (c) Copyright 2007, Grazvydas "notaz" Ignotas
@ All Rights Reserved
.extern Pico
.extern PicoDraw2FB
@ define these constants in your include file:
@ .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.
.include "port_config.s"
.global BackFillFull @ int reg7
BackFillFull:
stmfd sp!, {r4-r9,lr}
ldr lr, =PicoDraw2FB @ lr=PicoDraw2FB
mov r0, r0, lsl #26
ldr lr, [lr]
mov r0, r0, lsr #26
add lr, lr, #328*8
orr r0, r0, r0, lsl #8
orr r0, r0, r0, lsl #16
mov r1, r0 @ 25 opcodes wasted?
mov r2, r0
mov r3, r0
mov r4, r0
mov r5, r0
mov r6, r0
mov r7, r0
mov r8, r0
mov r9, r0
mov r12, #(END_ROW-START_ROW)*8
@ go go go!
.bff_loop:
add lr, lr, #8
subs r12, r12, #1
stmia lr!, {r0-r9} @ 10*4*8
stmia lr!, {r0-r9}
stmia lr!, {r0-r9}
stmia lr!, {r0-r9}
stmia lr!, {r0-r9}
stmia lr!, {r0-r9}
stmia lr!, {r0-r9}
stmia lr!, {r0-r9}
bne .bff_loop
ldmfd sp!, {r4-r9,r12}
bx r12
.pool
@ -------- some macros --------
@ helper
@ TileLineSinglecol (r1=pdest, r2=pixels8, r3=pal) r4: scratch, r0: pixels8_old
.macro TileLineSinglecol notsinglecol=0
and r2, r2, #0xf @ #0x0000000f
.if !\notsinglecol
cmp r2, r0, lsr #28 @ if these don't match,
bicne r9, r9, #2 @ it is a sign that whole tile is not singlecolor (only it's lines may be)
.endif
orr r4, r3, r2
orr r4, r4, r4, lsl #8
tst r1, #1 @ not aligned?
strneb r4, [r1], #1
streqh r4, [r1], #2
strh r4, [r1], #2
strh r4, [r1], #2
strh r4, [r1], #2
strneb r4, [r1], #1 @ have a remaining unaligned pixel?
sub r1, r1, #8
.if !\notsinglecol
mov r0, #0xf
orr r0, r0, r2, lsl #28 @ we will need the old palindex later
.endif
.endm
@ TileNorm (r1=pdest, r2=pixels8, r3=pal) r0,r4: scratch
.macro TileLineNorm
ands r4, r0, r2, lsr #12 @ #0x0000f000
orrne r4, r3, r4
strneb r4, [r1]
ands r4, r0, r2, lsr #8 @ #0x00000f00
orrne r4, r3, r4
strneb r4, [r1,#1]
ands r4, r0, r2, lsr #4 @ #0x000000f0
orrne r4, r3, r4
strneb r4, [r1,#2]
ands r4, r0, r2 @ #0x0000000f
orrne r4, r3, r4
strneb r4, [r1,#3]
ands r4, r0, r2, lsr #28 @ #0xf0000000
orrne r4, r3, r4
strneb r4, [r1,#4]
ands r4, r0, r2, lsr #24 @ #0x0f000000
orrne r4, r3, r4
strneb r4, [r1,#5]
ands r4, r0, r2, lsr #20 @ #0x00f00000
orrne r4, r3, r4
strneb r4, [r1,#6]
ands r4, r0, r2, lsr #16 @ #0x000f0000
orrne r4, r3, r4
strneb r4, [r1,#7]
.endm
@ TileFlip (r1=pdest, r2=pixels8, r3=pal) r0,r4: scratch
.macro TileLineFlip
ands r4, r0, r2, lsr #16 @ #0x000f0000
orrne r4, r3, r4
strneb r4, [r1]
ands r4, r0, r2, lsr #20 @ #0x00f00000
orrne r4, r3, r4
strneb r4, [r1,#1]
ands r4, r0, r2, lsr #24 @ #0x0f000000
orrne r4, r3, r4
strneb r4, [r1,#2]
ands r4, r0, r2, lsr #28 @ #0xf0000000
orrne r4, r3, r4
strneb r4, [r1,#3]
ands r4, r0, r2 @ #0x0000000f
orrne r4, r3, r4
strneb r4, [r1,#4]
ands r4, r0, r2, lsr #4 @ #0x000000f0
orrne r4, r3, r4
strneb r4, [r1,#5]
ands r4, r0, r2, lsr #8 @ #0x00000f00
orrne r4, r3, r4
strneb r4, [r1,#6]
ands r4, r0, r2, lsr #12 @ #0x0000f000
orrne r4, r3, r4
strneb r4, [r1,#7]
.endm
@ Tile (r1=pdest, r3=pal, r9=prevcode, r10=Pico.vram) r2,r4,r7: scratch, r0=0xf
.macro Tile hflip vflip
mov r7, r9, lsl #13 @ r9=code<<8; addr=(code&0x7ff)<<4;
add r7, r10, r7, lsr #16
orr r9, r9, #3 @ emptytile=singlecolor=1, r9 must be <code_16> 00000xxx
.if \vflip
@ we read tilecodes in reverse order if we have vflip
add r7, r7, #8*4
.endif
@ loop through 8 lines
orr r9, r9, #(7<<24)
b 1f @ loop_enter
0: @ singlecol_loop
subs r9, r9, #(1<<24)
add r1, r1, #328 @ set pointer to next line
bmi 8f @ loop_exit with r0 restore
1:
.if \vflip
ldr r2, [r7, #-4]! @ pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels
.else
ldr r2, [r7], #4
.endif
tst r2, r2
beq 2f @ empty line
bic r9, r9, #1
cmp r2, r2, ror #4
bne 3f @ not singlecolor
TileLineSinglecol
b 0b
2:
bic r9, r9, #2
2: @ empty_loop
subs r9, r9, #(1<<24)
add r1, r1, #328 @ set pointer to next line
bmi 8f @ loop_exit with r0 restore
.if \vflip
ldr r2, [r7, #-4]! @ next pack
.else
ldr r2, [r7], #4
.endif
mov r0, #0xf @ singlecol_loop might have messed r0
tst r2, r2
beq 2b
bic r9, r9, #3 @ if we are here, it means we have empty and not empty line
b 5f
3: @ not empty, not singlecol
mov r0, #0xf
bic r9, r9, #3
b 6f
4: @ not empty, not singlecol loop
subs r9, r9, #(1<<24)
add r1, r1, #328 @ set pointer to next line
bmi 9f @ loop_exit
.if \vflip
ldr r2, [r7, #-4]! @ next pack
.else
ldr r2, [r7], #4
.endif
tst r2, r2
beq 4b @ empty line
5:
cmp r2, r2, ror #4
beq 7f @ singlecolor line
6:
.if \hflip
TileLineFlip
.else
TileLineNorm
.endif
b 4b
7:
TileLineSinglecol 1
b 4b
8:
mov r0, #0xf
9: @ loop_exit
add r9, r9, #(1<<24) @ fix r9
sub r1, r1, #328*8 @ restore pdest pointer
.endm
@ TileLineSinglecolAl (r1=pdest, r4,r7=color)
.macro TileLineSinglecolAl0
stmia r1!, {r4,r7}
add r1, r1, #320
.endm
.macro TileLineSinglecolAl1
strb r4, [r1], #1
strh r4, [r1], #2
str r4, [r1], #4
strb r4, [r1], #1+320
@ add r1, r1, #320
.endm
.macro TileLineSinglecolAl2
strh r4, [r1], #2
str r4, [r1], #4
strh r4, [r1], #2
add r1, r1, #320
.endm
.macro TileLineSinglecolAl3
strb r4, [r1], #1
str r4, [r1], #4
strh r4, [r1], #2
strb r4, [r1], #1+320
@ add r1, r1, #320
.endm
@ TileSinglecol (r1=pdest, r2=pixels8, r3=pal) r4,r7: scratch, r0=0xf
@ kaligned==1, if dest is always aligned
.macro TileSinglecol kaligned=0
and r4, r2, #0xf @ we assume we have good r2 from previous time
orr r4, r4, r3
orr r4, r4, r4, lsl #8
orr r4, r4, r4, lsl #16
mov r7, r4
.if !\kaligned
tst r1, #2 @ not aligned?
bne 2f
tst r1, #1
bne 1f
.endif
TileLineSinglecolAl0
TileLineSinglecolAl0
TileLineSinglecolAl0
TileLineSinglecolAl0
TileLineSinglecolAl0
TileLineSinglecolAl0
TileLineSinglecolAl0
TileLineSinglecolAl0
.if !\kaligned
b 4f
1:
TileLineSinglecolAl1
TileLineSinglecolAl1
TileLineSinglecolAl1
TileLineSinglecolAl1
TileLineSinglecolAl1
TileLineSinglecolAl1
TileLineSinglecolAl1
TileLineSinglecolAl1
b 4f
2:
tst r1, #1
bne 3f
TileLineSinglecolAl2
TileLineSinglecolAl2
TileLineSinglecolAl2
TileLineSinglecolAl2
TileLineSinglecolAl2
TileLineSinglecolAl2
TileLineSinglecolAl2
TileLineSinglecolAl2
b 4f
3:
TileLineSinglecolAl3
TileLineSinglecolAl3
TileLineSinglecolAl3
TileLineSinglecolAl3
TileLineSinglecolAl3
TileLineSinglecolAl3
TileLineSinglecolAl3
TileLineSinglecolAl3
4:
.endif
sub r1, r1, #328*8 @ restore pdest pointer
.endm
@ DrawLayerTiles(*hcache, *scrpos, (cells<<24)|(nametab<<9)|(vscroll&0x3ff)<<11|(shift[width]<<8)|planeend, (ymask<<24)|(planestart<<16)|[htab||hscroll]
@static void DrawLayerFull(int plane, int *hcache, int planestart, int planeend)
.global DrawLayerFull
DrawLayerFull:
stmfd sp!, {r4-r11,lr}
mov r6, r1 @ hcache
ldr r11, =(Pico+0x22228) @ Pico.video
ldr r10, =(Pico+0x10000) @ r10=Pico.vram
ldrb r5, [r11, #13] @ pvid->reg[13]
ldrb r7, [r11, #11]
sub lr, r3, r2
and lr, lr, #0x00ff0000 @ lr=cells
mov r5, r5, lsl #10 @ htab=pvid->reg[13]<<9; (halfwords)
add r5, r5, r0, lsl #1 @ htab+=plane
bic r5, r5, #0x00ff0000 @ just in case
tst r7, #3 @ full screen scroll? (if ==0)
ldrb r7, [r11, #16] @ ??hh??ww
ldreqh r5, [r10, r5]
biceq r5, r5, #0x0000fc00 @ r5=hscroll (0-0x3ff)
movne r5, r5, lsr #1
orrne r5, r5, #0x8000 @ this marks that we have htab pointer, not hscroll here
and r8, r7, #3
orr r5, r5, r7, lsl #1+24
orr r5, r5, #0x1f000000
cmp r8, #1
biclt r5, r5, #0x80000000
biceq r5, r5, #0xc0000000
bicgt r5, r5, #0xe0000000
mov r9, r2, lsl #24
orr r5, r5, r9, lsr #8 @ r5=(ymask<<24)|(trow<<16)|[htab||hscroll]
add r4, r8, #5
cmp r4, #7
subge r4, r4, #1 @ r4=shift[width] (5,6,6,7)
orr lr, lr, r4
orr lr, lr, r3, lsl #24 @ lr=(planeend<<24)|(cells<<16)|shift[width]
@ calculate xmask:
mov r8, r8, lsl #24+5
orr r8, r8, #0x1f000000
@ Find name table:
tst r0, r0
ldreqb r4, [r11, #2]
moveq r4, r4, lsr #3
ldrneb r4, [r11, #4]
and r4, r4, #7
orr lr, lr, r4, lsl #13 @ lr|=nametab_bits{3}<<13
ldr r11, =PicoDraw2FB @ r11=PicoDraw2FB
sub r4, r9, #(START_ROW<<24)
ldr r11, [r11]
mov r4, r4, asr #24
mov r7, #328*8
mla r11, r4, r7, r11 @ scrpos+=8*328*(planestart-START_ROW);
@ Get vertical scroll value:
add r7, r10, #0x012000
add r7, r7, #0x000180 @ r7=Pico.vsram (Pico+0x22180)
ldr r7, [r7]
tst r0, r0
moveq r7, r7, lsl #22
movne r7, r7, lsl #6
mov r7, r7, lsr #22 @ r7=vscroll (10 bits)
orr lr, lr, r7, lsl #3
mov lr, lr, ror #24 @ packed: cccccccc nnnvvvvv vvvvvsss pppppppp: cells, nametab, vscroll, shift[width], planeend
ands r7, r7, #7
addne lr, lr, #1 @ we have vertically clipped tiles due to vscroll, so we need 1 more row
rsb r7, r7, #8
str r7, [r6], #4 @ push y-offset to tilecache
mov r4, #328
mla r11, r4, r7, r11 @ scrpos+=(8-(vscroll&7))*328;
mov r9, #0xff000000 @ r9=(prevcode<<8)|flags: 1~tile empty, 2~tile singlecolor
.rtrloop_outer:
mov r4, lr, lsl #11
mov r4, r4, lsr #25 @ r4=vscroll>>3 (7 bits)
add r4, r4, r5, lsr #16 @ +trow
and r4, r4, r5, lsr #24 @ &=ymask
mov r7, lr, lsr #8
and r7, r7, #7 @ shift[width]
mov r0, lr, lsr #9
and r0, r0, #0x7000 @ nametab
add r12,r0, r4, lsl r7 @ nametab_row = nametab + (((trow+(vscroll>>3))&ymask)<<shift[width]);
mov r4, lr, lsr #24
orr r12,r12,r4, lsl #23
mov r12,r12,lsl #1 @ (nametab_row|(cells<<24)) (halfword compliant)
@ htab?
tst r5, #0x8000
moveq r7, r5, lsl #22 @ hscroll (0-3FFh)
moveq r7, r7, lsr #22
beq .rtr_hscroll_done
@ get hscroll from htab
mov r7, r5, lsl #17
ands r4, r5, #0x00ff0000
add r7, r7, r4, lsl #5 @ +=trow<<4
andne r4, lr, #0x3800
subne r7, r7, r4, lsl #7 @ if(trow) htaddr-=(vscroll&7)<<1;
mov r7, r7, lsr #16 @ halfwords
ldrh r7, [r10, r7]
.rtr_hscroll_done:
and r8, r8, #0xff000000
rsb r4, r7, #0 @ r4=tilex=(-ts->hscroll)>>3
mov r4, r4, asr #3
and r4, r4, #0xff
orr r8, r8, r4 @ r8=(xmask<<24)|tilex
sub r7, r7, #1
and r7, r7, #7
add r7, r7, #1 @ r7=dx=((ts->hscroll-1)&7)+1
cmp r7, #8
subeq r12,r12, #0x01000000 @ we will loop cells+1 times, so loop less when there is no hscroll
add r1, r11, r7 @ r1=pdest
mov r0, #0xf
b .rtrloop_enter
@ r4 & r7 are scratch in this loop
.rtrloop: @ 40-41 times
add r1, r1, #8
subs r12,r12, #0x01000000
add r8, r8, #1
bmi .rtrloop_exit
.rtrloop_enter:
and r7, r8, r8, lsr #24
add r7, r10, r7, lsl #1
bic r4, r12, #0xff000000 @ Pico.vram[nametab_row+(tilex&xmask)];
ldrh r7, [r7, r4] @ r7=code (int, but from unsigned, no sign extend)
tst r7, #0x8000
bne .rtr_hiprio
cmp r7, r9, lsr #8
bne .rtr_notsamecode
@ we know stuff about this tile already
tst r9, #1
bne .rtrloop @ empty tile
tst r9, #2
bne .rtr_singlecolor @ singlecolor tile
b .rtr_samecode
.rtr_notsamecode:
and r4, r9, #0x600000
mov r9, r7, lsl #8 @ remember new code
@ update cram
and r7, r7, #0x6000
mov r3, r7, asr #9 @ r3=pal=((code&0x6000)>>9);
.rtr_samecode:
tst r9, #0x100000 @ vflip?
bne .rtr_vflip
tst r9, #0x080000 @ hflip?
bne .rtr_hflip
@ Tile (r1=pdest, r3=pal, r9=prevcode, r10=Pico.vram) r2,r4,r7: scratch, r0=0xf
Tile 0, 0
b .rtrloop
.rtr_hflip:
Tile 1, 0
b .rtrloop
.rtr_vflip:
tst r9, #0x080000 @ hflip?
bne .rtr_vflip_hflip
Tile 0, 1
b .rtrloop
.rtr_vflip_hflip:
Tile 1, 1
b .rtrloop
.rtr_singlecolor:
TileSinglecol
b .rtrloop
.rtr_hiprio:
@ *(*hcache)++ = code|(dx<<16)|(trow<<27);
sub r4, r1, r11
orr r7, r7, r4, lsl #16
and r4, r5, #0x00ff0000
orr r7, r7, r4, lsl #11 @ (trow<<27)
str r7, [r6], #4 @ cache hi priority tile
b .rtrloop
.rtrloop_exit:
add r5, r5, #0x00010000
mov r4, r5, lsl #8
cmp r4, lr, lsl #24
bge .rtrloop_outer_exit
add r11, r11, #328*8
b .rtrloop_outer
.rtrloop_outer_exit:
@ terminate cache list
mov r0, #0
str r0, [r6] @ save cache pointer
ldmfd sp!, {r4-r11,lr}
bx lr
.pool
.global DrawTilesFromCacheF @ int *hc
DrawTilesFromCacheF:
stmfd sp!, {r4-r10,lr}
mov r9, #0xff000000 @ r9=prevcode=-1
mvn r6, #0 @ r6=prevy=-1
ldr r4, =PicoDraw2FB @ r4=PicoDraw2FB
ldr r1, [r0], #4 @ read y offset
ldr r4, [r4]
mov r7, #328
mla r1, r7, r1, r4
sub r12, r1, #(328*8*START_ROW) @ r12=scrpos
ldr r10, =(Pico+0x10000) @ r10=Pico.vram
mov r8, r0 @ hc
mov r0, #0xf
@ scratch: r4, r7
@ *hcache++ = code|(dx<<16)|(trow<<27); // cache it
.dtfcf_loop:
ldr r7, [r8], #4 @ read code
movs r1, r7, lsr #16 @ r1=dx;
ldmeqfd sp!, {r4-r10,pc} @ dx is never zero, this must be a terminator, return
@ row changed?
cmp r6, r7, lsr #27
movne r6, r7, lsr #27
movne r4, #328*8
mlane r5, r4, r6, r12 @ r5=pd = scrpos + prevy*328*8
bic r1, r1, #0xf800
add r1, r5, r1 @ r1=pdest (halfwords)
mov r7, r7, lsl #16
mov r7, r7, lsr #16
cmp r7, r9, lsr #8
bne .dtfcf_notsamecode
@ we know stuff about this tile already
tst r9, #1
bne .dtfcf_loop @ empty tile
tst r9, #2
bne .dtfcf_singlecolor @ singlecolor tile
b .dtfcf_samecode
.dtfcf_notsamecode:
and r4, r9, #0x600000
mov r9, r7, lsl #8 @ remember new code
@ update cram val
and r7, r7, #0x6000
mov r3, r7, asr #9 @ r3=pal=((code&0x6000)>>9);
.dtfcf_samecode:
tst r9, #0x100000 @ vflip?
bne .dtfcf_vflip
tst r9, #0x080000 @ hflip?
bne .dtfcf_hflip
@ Tile (r1=pdest, r3=pal, r9=prevcode, r10=Pico.vram) r2,r4,r7: scratch, r0=0xf
Tile 0, 0
b .dtfcf_loop
.dtfcf_hflip:
Tile 1, 0
b .dtfcf_loop
.dtfcf_vflip:
tst r9, #0x080000 @ hflip?
bne .dtfcf_vflip_hflip
Tile 0, 1
b .dtfcf_loop
.dtfcf_vflip_hflip:
Tile 1, 1
b .dtfcf_loop
.dtfcf_singlecolor:
TileSinglecol
b .dtfcf_loop
.pool
@ @@@@@@@@@@@@@@@
@ (tile_start<<16)|row_start
.global DrawWindowFull @ int tstart, int tend, int prio
DrawWindowFull:
stmfd sp!, {r4-r11,lr}
ldr r11, =(Pico+0x22228) @ Pico.video
ldrb r12, [r11, #3] @ pvid->reg[3]
mov r12, r12, lsl #10
ldr r4, [r11, #12]
mov r5, #1 @ nametab_step
tst r4, #1 @ 40 cell mode?
andne r12, r12, #0xf000 @ 0x3c<<10
andeq r12, r12, #0xf800
movne r5, r5, lsl #7
moveq r5, r5, lsl #6 @ nametab_step
and r4, r0, #0xff
mla r12, r5, r4, r12 @ nametab += nametab_step*start;
mov r4, r0, lsr #16 @ r4=start_cell_h
add r7, r12, r4, lsl #1
@ fetch the first code now
ldr r10, =(Pico+0x10000) @ lr=Pico.vram
ldrh r7, [r10, r7]
cmp r2, r7, lsr #15
ldmnefd sp!, {r4-r11,pc} @ hack: simply assume that whole window uses same priority
rsb r8, r4, r1, lsr #16 @ cells (h)
orr r8, r8, r4, lsl #8
mov r4, r1, lsl #24
sub r4, r4, r0, lsl #24
orr r8, r8, r4, lsr #8 @ r8=cells_h|(start_cell_h<<8)|(cells_v<<16)
sub r8, r8, #0x010000 @ adjust for algo
mov r9, #0xff000000 @ r9=prevcode=-1
ldr r11, =PicoDraw2FB @ r11=scrpos
and r4, r0, #0xff
ldr r11, [r11]
sub r4, r4, #START_ROW
add r11, r11, #328*8
add r11, r11, #8
mov r7, #328*8
mla r11, r7, r4, r11 @ scrpos+=8*328*(start-START_ROW);
mov r0, #0xf
.dwfloop_outer:
and r6, r8, #0xff00 @ r6=tilex
add r1, r11, r6, lsr #5 @ r1=pdest
add r6, r12, r6, lsr #7
add r6, r10, r6 @ r6=Pico.vram+nametab+tilex
orr r8, r8, r8, lsl #24
sub r8, r8, #0x01000000 @ cell loop counter
b .dwfloop_enter
@ r4 & r7 are scratch in this loop
.dwfloop:
add r1, r1, #8
subs r8, r8, #0x01000000
bmi .dwfloop_exit
.dwfloop_enter:
ldrh r7, [r6], #2 @ r7=code
cmp r7, r9, lsr #8
bne .dwf_notsamecode
@ we know stuff about this tile already
tst r9, #1
bne .dwfloop @ empty tile
tst r9, #2
bne .dwf_singlecolor @ singlecolor tile
b .dwf_samecode
.dwf_notsamecode:
and r4, r9, #0x600000
mov r9, r7, lsl #8 @ remember new code
@ update cram val
and r7, r7, #0x6000
mov r3, r7, asr #9 @ r3=pal=((code&0x6000)>>9);
.dwf_samecode:
tst r9, #0x100000 @ vflip?
bne .dwf_vflip
tst r9, #0x080000 @ hflip?
bne .dwf_hflip
@ Tile (r1=pdest, r3=pal, r9=prevcode, r10=Pico.vram) r2,r4,r7: scratch, r0=0xf
Tile 0, 0
b .dwfloop
.dwf_hflip:
Tile 1, 0
b .dwfloop
.dwf_vflip:
tst r9, #0x080000 @ hflip?
bne .dwf_vflip_hflip
Tile 0, 1
b .dwfloop
.dwf_vflip_hflip:
Tile 1, 1
b .dwfloop
.dwf_singlecolor:
TileSinglecol 1
b .dwfloop
.dwfloop_exit:
bic r8, r8, #0xff000000 @ fix r8
subs r8, r8, #0x010000
ldmmifd sp!, {r4-r11,pc}
add r11, r11, #328*8
add r12, r12, r5 @ nametab+=nametab_step
b .dwfloop_outer
.pool
@ ---------------- sprites ---------------
.macro SpriteLoop hflip vflip
.if \vflip
mov r1, r5, lsr #24 @ height
mov r0, #328*8
mla r11, r1, r0, r11 @ scrpos+=height*328*8;
add r12, r12, r1, lsl #3 @ sy+=height*8
.endif
mov r0, #0xf
.if \hflip
and r1, r5, #0xff
add r8, r8, r1, lsl #3 @ sx+=width*8
58:
cmp r8, #336
blt 51f
add r9, r9, r5, lsr #16
sub r5, r5, #1 @ sub width
sub r8, r8, #8
b 58b
.else
cmp r8, #0 @ skip tiles hidden on the left of screen
bgt 51f
58:
add r9, r9, r5, lsr #16
sub r5, r5, #1
adds r8, r8, #8
ble 58b
b 51f
.endif
50: @ outer
.if !\hflip
add r8, r8, #8 @ sx+=8
.endif
bic r5, r5, #0xff000000 @ fix height
orr r5, r5, r5, lsl #16
51: @ outer_enter
sub r5, r5, #1 @ width--
movs r1, r5, lsl #24
ldmmifd sp!, {r4-r11,pc} @ end of tile
.if \hflip
subs r8, r8, #8 @ sx-=8
ldmlefd sp!, {r4-r11,pc} @ tile offscreen
.else
cmp r8, #328
ldmgefd sp!, {r4-r11,pc} @ tile offscreen
.endif
mov r6, r12 @ r6=sy
add r1, r11, r8 @ pdest=scrpos+sx
b 53f
52: @ inner
add r9, r9, #1<<8 @ tile++
.if !\vflip
add r6, r6, #8 @ sy+=8
add r1, r1, #328*8
.endif
53: @ inner_enter
@ end of sprite?
subs r5, r5, #0x01000000
bmi 50b @ ->outer
.if \vflip
sub r6, r6, #8 @ sy-=8
sub r1, r1, #328*8
.endif
@ offscreen?
cmp r6, #(START_ROW*8)
ble 52b
cmp r6, #(END_ROW*8+8)
bge 52b
@ Tile (r1=pdest, r3=pal, r9=prevcode, r10=Pico.vram) r2,r4,r7: scratch, r0=0xf
Tile \hflip, \vflip
b 52b
.endm
.global DrawSpriteFull @ unsigned int *sprite
DrawSpriteFull:
stmfd sp!, {r4-r11,lr}
ldr r3, [r0] @ sprite[0]
mov r5, r3, lsl #4
mov r6, r5, lsr #30
add r6, r6, #1 @ r6=width
mov r5, r5, lsl #2
mov r5, r5, lsr #30
add r5, r5, #1 @ r5=height
mov r12, r3, lsl #23
mov r12, r12, lsr #23
ldr lr, [r0, #4] @ lr=code
sub r12, r12, #0x78 @ r12=sy
mov r8, lr, lsl #7
mov r8, r8, lsr #23
sub r8, r8, #0x78 @ r8=sx
mov r9, lr, lsl #21
mov r9, r9, lsr #13 @ r9=tile<<8
and r3, lr, #0x6000
mov r3, r3, lsr #9 @ r3=pal=((code>>9)&0x30);
ldr r11, =PicoDraw2FB @ r11=scrpos
ldr r10, =(Pico+0x10000) @ r10=Pico.vram
ldr r11, [r11]
sub r1, r12, #(START_ROW*8)
mov r0, #328
mla r11, r1, r0, r11 @ scrpos+=(sy-START_ROW*8)*328;
orr r5, r5, r5, lsl #16 @
orr r5, r6, r5, lsl #8 @ r5=width|(height<<8)|(height<<24)
tst lr, #0x1000 @ vflip?
bne .dsf_vflip
tst lr, #0x0800 @ hflip?
bne .dsf_hflip
SpriteLoop 0, 0
.dsf_hflip:
SpriteLoop 1, 0
.dsf_vflip:
tst lr, #0x0800 @ hflip?
bne .dsf_vflip_hflip
SpriteLoop 0, 1
.dsf_vflip_hflip:
SpriteLoop 1, 1
.pool

1752
pico/draw_amips.s Normal file

File diff suppressed because it is too large Load diff

2039
pico/draw_arm.s Normal file

File diff suppressed because it is too large Load diff

1071
pico/memory.c Normal file

File diff suppressed because it is too large Load diff

788
pico/memory_amips.s Normal file
View file

@ -0,0 +1,788 @@
# vim:filetype=mips
# memory handlers with banking support for SSF II - The New Challengers
# mostly based on Gens code
# (c) Copyright 2007, Grazvydas "notaz" Ignotas
# All Rights Reserved
.set noreorder
.set noat
.text
.align 4
# default jump tables
m_read8_def_table:
.long m_read8_rom0 # 0x000000 - 0x07FFFF
.long m_read8_rom1 # 0x080000 - 0x0FFFFF
.long m_read8_rom2 # 0x100000 - 0x17FFFF
.long m_read8_rom3 # 0x180000 - 0x1FFFFF
.long m_read8_rom4 # 0x200000 - 0x27FFFF
.long m_read8_rom5 # 0x280000 - 0x2FFFFF
.long m_read8_rom6 # 0x300000 - 0x37FFFF
.long m_read8_rom7 # 0x380000 - 0x3FFFFF
.long m_read8_rom8 # 0x400000 - 0x47FFFF - for all those large ROM hacks
.long m_read8_rom9 # 0x480000 - 0x4FFFFF
.long m_read8_romA # 0x500000 - 0x57FFFF
.long m_read8_romB # 0x580000 - 0x5FFFFF
.long m_read8_romC # 0x600000 - 0x67FFFF
.long m_read8_romD # 0x680000 - 0x6FFFFF
.long m_read8_romE # 0x700000 - 0x77FFFF
.long m_read8_romF # 0x780000 - 0x7FFFFF
.long m_read8_rom10 # 0x800000 - 0x87FFFF
.long m_read8_rom11 # 0x880000 - 0x8FFFFF
.long m_read8_rom12 # 0x900000 - 0x97FFFF
.long m_read8_rom13 # 0x980000 - 0x9FFFFF
.long m_read8_misc # 0xA00000 - 0xA7FFFF
.long m_read_null # 0xA80000 - 0xAFFFFF
.long m_read_null # 0xB00000 - 0xB7FFFF
.long m_read_null # 0xB80000 - 0xBFFFFF
.long m_read8_vdp # 0xC00000 - 0xC7FFFF
.long m_read8_vdp # 0xC80000 - 0xCFFFFF
.long m_read8_vdp # 0xD00000 - 0xD7FFFF
.long m_read8_vdp # 0xD80000 - 0xDFFFFF
.long m_read8_ram # 0xE00000 - 0xE7FFFF
.long m_read8_ram # 0xE80000 - 0xEFFFFF
.long m_read8_ram # 0xF00000 - 0xF7FFFF
.long m_read8_ram # 0xF80000 - 0xFFFFFF
m_read16_def_table:
.long m_read16_rom0 # 0x000000 - 0x07FFFF
.long m_read16_rom1 # 0x080000 - 0x0FFFFF
.long m_read16_rom2 # 0x100000 - 0x17FFFF
.long m_read16_rom3 # 0x180000 - 0x1FFFFF
.long m_read16_rom4 # 0x200000 - 0x27FFFF
.long m_read16_rom5 # 0x280000 - 0x2FFFFF
.long m_read16_rom6 # 0x300000 - 0x37FFFF
.long m_read16_rom7 # 0x380000 - 0x3FFFFF
.long m_read16_rom8 # 0x400000 - 0x47FFFF
.long m_read16_rom9 # 0x480000 - 0x4FFFFF
.long m_read16_romA # 0x500000 - 0x57FFFF
.long m_read16_romB # 0x580000 - 0x5FFFFF
.long m_read16_romC # 0x600000 - 0x67FFFF
.long m_read16_romD # 0x680000 - 0x6FFFFF
.long m_read16_romE # 0x700000 - 0x77FFFF
.long m_read16_romF # 0x780000 - 0x7FFFFF
.long m_read16_rom10 # 0x800000 - 0x87FFFF
.long m_read16_rom11 # 0x880000 - 0x8FFFFF
.long m_read16_rom12 # 0x900000 - 0x97FFFF
.long m_read16_rom13 # 0x980000 - 0x9FFFFF
.long m_read16_misc # 0xA00000 - 0xA7FFFF
.long m_read_null # 0xA80000 - 0xAFFFFF
.long m_read_null # 0xB00000 - 0xB7FFFF
.long m_read_null # 0xB80000 - 0xBFFFFF
.long m_read16_vdp # 0xC00000 - 0xC7FFFF
.long m_read16_vdp # 0xC80000 - 0xCFFFFF
.long m_read16_vdp # 0xD00000 - 0xD7FFFF
.long m_read16_vdp # 0xD80000 - 0xDFFFFF
.long m_read16_ram # 0xE00000 - 0xE7FFFF
.long m_read16_ram # 0xE80000 - 0xEFFFFF
.long m_read16_ram # 0xF00000 - 0xF7FFFF
.long m_read16_ram # 0xF80000 - 0xFFFFFF
m_read32_def_table:
.long m_read32_rom0 # 0x000000 - 0x07FFFF
.long m_read32_rom1 # 0x080000 - 0x0FFFFF
.long m_read32_rom2 # 0x100000 - 0x17FFFF
.long m_read32_rom3 # 0x180000 - 0x1FFFFF
.long m_read32_rom4 # 0x200000 - 0x27FFFF
.long m_read32_rom5 # 0x280000 - 0x2FFFFF
.long m_read32_rom6 # 0x300000 - 0x37FFFF
.long m_read32_rom7 # 0x380000 - 0x3FFFFF
.long m_read32_rom8 # 0x400000 - 0x47FFFF
.long m_read32_rom9 # 0x480000 - 0x4FFFFF
.long m_read32_romA # 0x500000 - 0x57FFFF
.long m_read32_romB # 0x580000 - 0x5FFFFF
.long m_read32_romC # 0x600000 - 0x67FFFF
.long m_read32_romD # 0x680000 - 0x6FFFFF
.long m_read32_romE # 0x700000 - 0x77FFFF
.long m_read32_romF # 0x780000 - 0x7FFFFF
.long m_read32_rom10 # 0x800000 - 0x87FFFF
.long m_read32_rom11 # 0x880000 - 0x8FFFFF
.long m_read32_rom12 # 0x900000 - 0x97FFFF
.long m_read32_rom13 # 0x980000 - 0x9FFFFF
.long m_read32_misc # 0xA00000 - 0xA7FFFF
.long m_read_null # 0xA80000 - 0xAFFFFF
.long m_read_null # 0xB00000 - 0xB7FFFF
.long m_read_null # 0xB80000 - 0xBFFFFF
.long m_read32_vdp # 0xC00000 - 0xC7FFFF
.long m_read32_vdp # 0xC80000 - 0xCFFFFF
.long m_read32_vdp # 0xD00000 - 0xD7FFFF
.long m_read32_vdp # 0xD80000 - 0xDFFFFF
.long m_read32_ram # 0xE00000 - 0xE7FFFF
.long m_read32_ram # 0xE80000 - 0xEFFFFF
.long m_read32_ram # 0xF00000 - 0xF7FFFF
.long m_read32_ram # 0xF80000 - 0xFFFFFF
# #############################################################################
.bss
.align 4
# used tables
m_read8_table:
.skip 32*4
m_read16_table:
.skip 32*4
m_read32_table:
.skip 32*4
# #############################################################################
.text
.align 4
.global PicoMemReset
.global PicoRead8
.global PicoRead16
.global PicoRead32
.global PicoWriteRomHW_SSF2
.global m_read8_def_table
.global m_read8_table
.macro PicoMemResetCopyDef dst_table src_table
lui $t0, %hi(\dst_table)
addiu $t0, %lo(\dst_table)
lui $t1, %hi(\src_table)
addiu $t1, %lo(\src_table)
li $t2, 32
1:
lw $t3, 0($t1)
sw $t3, 0($t0)
addiu $t2, -1
addiu $t1, 4
bnez $t2, 1b
addiu $t0, 4
.endm
# $t4 = 4
.macro PicoMemResetRomArea dst_table ar_label
lui $t0, %hi(\dst_table)
addiu $t0, %lo(\dst_table)
lui $t1, %hi(\ar_label)
addiu $t1, %lo(\ar_label)
li $t2, 20
1:
beq $t2, $v1, 2f
addiu $t2, -1
sll $t3, $t2, 2
beq $t2, $t4, 1b # do not touch the SRAM area
addu $t3, $t0
j 1b
sw $t1, 0($t3)
2:
.endm
PicoMemReset:
lui $v1, %hi(Pico+0x22204)
lw $v1, %lo(Pico+0x22204)($v1) # romsize
lui $t0, 8
addu $v1, $t0
addiu $v1, -1
srl $v1, 19
PicoMemResetCopyDef m_read8_table m_read8_def_table
PicoMemResetCopyDef m_read16_table m_read16_def_table
PicoMemResetCopyDef m_read32_table m_read32_def_table
# update memhandlers according to ROM size
li $t4, 4
PicoMemResetRomArea m_read8_table m_read8_above_rom
PicoMemResetRomArea m_read16_table m_read16_above_rom
PicoMemResetRomArea m_read32_table m_read32_above_rom
jr $ra
nop
# #############################################################################
.macro PicoReadJump table
lui $t0, %hi(\table)
srl $t1, $a0, 19
ins $t0, $t1, 2, 5
lw $t0, %lo(\table)($t0)
ins $a0, $0, 24, 8
jr $t0
nop
.endm
PicoRead8: # u32 a
PicoReadJump m_read8_table
PicoRead16: # u32 a
PicoReadJump m_read16_table
PicoRead32: # u32 a
PicoReadJump m_read32_table
# #############################################################################
m_read_null:
jr $ra
li $v0, 0
m_read_neg1:
jr $ra
addiu $v0, $0, 0xffff
# loads &Pico.rom to $t3
.macro m_read_rom_try_sram is200000 size
lui $t2, %hi(SRam)
addiu $t2, %lo(SRam)
lui $t3, %hi(Pico+0x22200)
lw $t1, 8($t2) # SRam.end
.if \is200000
ins $a0, $0, 19, 13
lui $t4, 0x20
or $a0, $t4
.endif
subu $t4, $a0, $t1
bgtz $t4, 1f
addiu $t3, %lo(Pico+0x22200)
lw $t1, 4($t2) # SRam.start
subu $t4, $t1, $a0
bgtz $t4, 1f
nop
lb $t1, 0x11($t3) # Pico.m.sram_reg
andi $t4, $t1, 5
beqz $t4, 1f
nop
.if \size == 8
j SRAMRead
nop
.elseif \size == 16
sw $ra, -4($sp)
jal SRAMRead16
addiu $sp, -4
lw $ra, 0($sp)
jr $ra
addiu $sp, 4
.else
addiu $sp, -8
sw $ra, 0($sp)
sw $a0, 4($sp)
jal SRAMRead16
nop
lw $a0, 4($sp)
sw $v0, 4($sp)
jal SRAMRead16
addiu $a0, 2
lw $v1, 4($sp)
lw $ra, 0($sp)
addiu $sp, 8
jr $ra
ins $v0, $v1, 16, 16
.endif
# m_read_nosram:
1:
.endm
.macro m_read8_rom sect
lui $t0, %hi(Pico+0x22200)
lw $t0, %lo(Pico+0x22200)($t0) # rom
xori $a0, 1
ins $a0, $0, 19, 13
.if \sect
lui $t1, 8*\sect
addu $a0, $t1
.endif
addu $t0, $a0
jr $ra
lb $v0, 0($t0)
.endm
m_read8_rom0: # 0x000000 - 0x07ffff
m_read8_rom 0
m_read8_rom1: # 0x080000 - 0x0fffff
m_read8_rom 1
m_read8_rom2: # 0x100000 - 0x17ffff
m_read8_rom 2
m_read8_rom3: # 0x180000 - 0x1fffff
m_read8_rom 3
m_read8_rom4: # 0x200000 - 0x27ffff, SRAM area
m_read_rom_try_sram 1 8
lw $t1, 4($t3) # romsize
subu $t4, $t1, $a0
blez $t4, m_read_null
lw $t1, 0($t3) # rom
xori $a0, 1
addu $t1, $a0
jr $ra
lb $v0, 0($t1)
m_read8_rom5: # 0x280000 - 0x2fffff
m_read8_rom 5
m_read8_rom6: # 0x300000 - 0x37ffff
m_read8_rom 6
m_read8_rom7: # 0x380000 - 0x3fffff
m_read8_rom 7
m_read8_rom8: # 0x400000 - 0x47ffff
m_read8_rom 8
m_read8_rom9: # 0x480000 - 0x4fffff
m_read8_rom 9
m_read8_romA: # 0x500000 - 0x57ffff
m_read8_rom 0xA
m_read8_romB: # 0x580000 - 0x5fffff
m_read8_rom 0xB
m_read8_romC: # 0x600000 - 0x67ffff
m_read8_rom 0xC
m_read8_romD: # 0x680000 - 0x6fffff
m_read8_rom 0xD
m_read8_romE: # 0x700000 - 0x77ffff
m_read8_rom 0xE
m_read8_romF: # 0x780000 - 0x7fffff
m_read8_rom 0xF
m_read8_rom10: # 0x800000 - 0x87ffff
m_read8_rom 0x10
m_read8_rom11: # 0x880000 - 0x8fffff
m_read8_rom 0x11
m_read8_rom12: # 0x900000 - 0x97ffff
m_read8_rom 0x12
m_read8_rom13: # 0x980000 - 0x9fffff
m_read8_rom 0x13
m_read8_misc:
srl $t0, $a0, 5
sll $t0, $t0, 5
lui $t1, 0xa1
bne $t0, $t1, m_read8_misc2
andi $t0, $a0, 0x1e
m_read8_misc_io:
beqz $t0, m_read8_misc_hwreg
sub $t1, $t0, 4
bgtz $t1, m_read8_misc_ioports
nop
slti $a0, $t0, 4
xori $a0, 1
j PadRead
nop
m_read8_misc_hwreg:
lui $v0, %hi(Pico+0x2220f)
jr $ra
lb $v0, %lo(Pico+0x2220f)($v0)
m_read8_misc_ioports:
lui $v0, %hi(Pico+0x22000)
ins $v0, $t0, 0, 5
jr $ra
lb $v0, %lo(Pico+0x22000)($v0)
m_read8_misc2:
lui $t0, 0xa1
ori $t0, 0x1100
bne $a0, $t0, m_read8_misc3
srl $t0, $a0, 16
j z80ReadBusReq
m_read8_misc3:
addiu $t0, 0xff60 # expecting 0xa0 to get 0
bnez $t0, m_read8_misc4
# z80 area
andi $t0, $a0, 0x4000
bnez $t0, m_read8_z80_misc
andi $t0, $a0, 0x6000
j z80Read8 # z80 RAM
m_read8_z80_misc:
addiu $t0, 0xc000 # expecting 0x4000 to get 0
bnez $t0, m_read_neg1 # invalid
nop
j ym2612_read_local_68k
nop
m_read8_fake_ym2612:
lb $v0, %lo(Pico+0x22208)($t0) # Pico.m.rotate
addiu $t1, $v0, 1
jr $ra
sb $t1, %lo(Pico+0x22208)($t0)
# delay slot friendly
.macro m_read8_call16 funcname is_func_ptr=0
.if \is_func_ptr
lui $t1, %hi(\funcname)
lw $t1, %lo(\funcname)($t1)
.endif
andi $t0, $a0, 1
beqz $t0, 1f
li $a1, 8 # not always needed, but shouln't cause problems
.if \is_func_ptr
jr $t1
.else
j \funcname # odd address
.endif
nop
1:
addiu $sp, -4
sw $ra, 0($sp)
.if \is_func_ptr
jalr $t1
.else
jal \funcname
.endif
xori $a0, 1
lw $ra, 0($sp)
addiu $sp, 4
jr $ra
srl $v0, 8
.endm
m_read8_misc4:
# if everything else fails, use generic handler
m_read8_call16 OtherRead16
m_read8_vdp:
ext $t0, $a0, 16, 3
andi $t1, $a0, 0xe0
or $t0, $t1
bnez $t0, m_read_null # invalid address
nop
j PicoVideoRead8
nop
m_read8_ram:
lui $t0, %hi(Pico)
ins $t0, $a0, 0, 16
xori $t0, 1
jr $ra
lb $v0, %lo(Pico)($t0)
m_read8_above_rom:
# might still be SRam (Micro Machines, HardBall '95)
m_read_rom_try_sram 0 8
m_read8_call16 PicoRead16Hook 1
# #############################################################################
.macro m_read16_rom sect
lui $t0, %hi(Pico+0x22200)
lw $t0, %lo(Pico+0x22200)($t0) # rom
ins $a0, $0, 0, 1
ins $a0, $0, 19, 13
.if \sect
lui $t1, 8*\sect
addu $a0, $t1
.endif
addu $t0, $a0
jr $ra
lh $v0, 0($t0)
.endm
m_read16_rom0: # 0x000000 - 0x07ffff
m_read16_rom 0
m_read16_rom1: # 0x080000 - 0x0fffff
m_read16_rom 1
m_read16_rom2: # 0x100000 - 0x17ffff
m_read16_rom 2
m_read16_rom3: # 0x180000 - 0x1fffff
m_read16_rom 3
m_read16_rom4: # 0x200000 - 0x27ffff, SRAM area
m_read_rom_try_sram 1 16
lw $t1, 4($t3) # romsize
subu $t4, $t1, $a0
blez $t4, m_read_null
lw $t1, 0($t3) # rom
ins $a0, $0, 0, 1
addu $t1, $a0
jr $ra
lh $v0, 0($t1)
m_read16_rom5: # 0x280000 - 0x2fffff
m_read16_rom 5
m_read16_rom6: # 0x300000 - 0x37ffff
m_read16_rom 6
m_read16_rom7: # 0x380000 - 0x3fffff
m_read16_rom 7
m_read16_rom8: # 0x400000 - 0x47ffff
m_read16_rom 8
m_read16_rom9: # 0x480000 - 0x4fffff
m_read16_rom 9
m_read16_romA: # 0x500000 - 0x57ffff
m_read16_rom 0xA
m_read16_romB: # 0x580000 - 0x5fffff
m_read16_rom 0xB
m_read16_romC: # 0x600000 - 0x67ffff
m_read16_rom 0xC
m_read16_romD: # 0x680000 - 0x6fffff
m_read16_rom 0xD
m_read16_romE: # 0x700000 - 0x77ffff
m_read16_rom 0xE
m_read16_romF: # 0x780000 - 0x7fffff
m_read16_rom 0xF
m_read16_rom10: # 0x800000 - 0x87ffff
m_read16_rom 0x10
m_read16_rom11: # 0x880000 - 0x8fffff
m_read16_rom 0x11
m_read16_rom12: # 0x900000 - 0x97ffff
m_read16_rom 0x12
m_read16_rom13: # 0x980000 - 0x9fffff
m_read16_rom 0x13
m_read16_misc:
ins $a0, $0, 0, 1
j OtherRead16
li $a1, 16
m_read16_vdp:
ext $t0, $a0, 16, 3
andi $t1, $a0, 0xe0
or $t0, $t1
bnez $t0, m_read_null # invalid address
ins $a0, $0, 0, 1
j PicoVideoRead
nop
m_read16_ram:
lui $t0, %hi(Pico)
ins $a0, $0, 0, 1
ins $t0, $a0, 0, 16
jr $ra
lh $v0, %lo(Pico)($t0)
m_read16_above_rom:
# might still be SRam
m_read_rom_try_sram 0 16
lui $t1, %hi(PicoRead16Hook)
lw $t1, %lo(PicoRead16Hook)($t1)
jr $t1
ins $a0, $0, 0, 1
# #############################################################################
.macro m_read32_rom sect
lui $t0, %hi(Pico+0x22200)
lw $t0, %lo(Pico+0x22200)($t0) # rom
ins $a0, $0, 0, 1
ins $a0, $0, 19, 13
.if \sect
lui $t1, 8*\sect
addu $a0, $t1
.endif
addu $t0, $a0
lh $v1, 0($t0)
lh $v0, 2($t0)
jr $ra
ins $v0, $v1, 16, 16
.endm
m_read32_rom0: # 0x000000 - 0x07ffff
m_read32_rom 0
m_read32_rom1: # 0x080000 - 0x0fffff
m_read32_rom 1
m_read32_rom2: # 0x100000 - 0x17ffff
m_read32_rom 2
m_read32_rom3: # 0x180000 - 0x1fffff
m_read32_rom 3
m_read32_rom4: # 0x200000 - 0x27ffff, SRAM area
m_read_rom_try_sram 1 32
lw $t1, 4($t3) # romsize
subu $t4, $t1, $a0
blez $t4, m_read_null
lw $t1, 0($t3) # rom
ins $a0, $0, 0, 1
addu $t1, $a0
lh $v1, 0($t1)
lh $v0, 2($t1)
jr $ra
ins $v0, $v1, 16, 16
m_read32_rom5: # 0x280000 - 0x2fffff
m_read32_rom 5
m_read32_rom6: # 0x300000 - 0x37ffff
m_read32_rom 6
m_read32_rom7: # 0x380000 - 0x3fffff
m_read32_rom 7
m_read32_rom8: # 0x400000 - 0x47ffff
m_read32_rom 8
m_read32_rom9: # 0x480000 - 0x4fffff
m_read32_rom 9
m_read32_romA: # 0x500000 - 0x57ffff
m_read32_rom 0xA
m_read32_romB: # 0x580000 - 0x5fffff
m_read32_rom 0xB
m_read32_romC: # 0x600000 - 0x67ffff
m_read32_rom 0xC
m_read32_romD: # 0x680000 - 0x6fffff
m_read32_rom 0xD
m_read32_romE: # 0x700000 - 0x77ffff
m_read32_rom 0xE
m_read32_romF: # 0x780000 - 0x7fffff
m_read32_rom 0xF
m_read32_rom10: # 0x800000 - 0x87ffff
m_read32_rom 0x10
m_read32_rom11: # 0x880000 - 0x8fffff
m_read32_rom 0x11
m_read32_rom12: # 0x900000 - 0x97ffff
m_read32_rom 0x12
m_read32_rom13: # 0x980000 - 0x9fffff
m_read32_rom 0x13
.macro m_read32_call16 func need_a1=0
addiu $sp, -8
sw $ra, 0($sp)
sw $s0, 4($sp)
.if \need_a1
li $a1, 16
.endif
jal \func
move $s0, $a0
addu $a0, $s0, 2
.if \need_a1
li $a1, 16
.endif
jal \func
move $s0, $v0
ins $v0, $s0, 16, 16
lw $ra, 0($sp)
lw $s0, 4($sp)
jr $ra
addiu $sp, 8
.endm
m_read32_misc:
ins $a0, $0, 0, 1
m_read32_call16 OtherRead16, 1
m_read32_vdp:
ext $t0, $a0, 16, 3
andi $t1, $a0, 0xe0
or $t0, $t1
bnez $t0, m_read_null # invalid address
ins $a0, $0, 0, 1
m_read32_call16 PicoVideoRead
m_read32_ram:
lui $t0, %hi(Pico)
ins $a0, $0, 0, 1
ins $t0, $a0, 0, 16
lh $v1, %lo(Pico)($t0)
lh $v0, %lo(Pico+2)($t0)
jr $ra
ins $v0, $v1, 16, 16
m_read32_above_rom:
# might still be SRam
m_read_rom_try_sram 0 32
ins $a0, $0, 0, 1
lui $t1, %hi(PicoRead16Hook)
lw $t1, %lo(PicoRead16Hook)($t1)
addiu $sp, -4*3
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $t1, 8($sp)
jalr $t1
move $s0, $a0
lw $t1, 8($sp)
addu $a0, $s0, 2
jalr $t1
move $s0, $v0
ins $v0, $s0, 16, 16
lw $ra, 0($sp)
lw $s0, 4($sp)
jr $ra
addiu $sp, 4*3
# #############################################################################
.macro PicoWriteRomHW_SSF2_ls def_table table
lui $t3, %hi(\def_table)
ins $t3, $a1, 2, 5
lw $t0, %lo(\def_table)($t3)
lui $t2, %hi(\table)
ins $t2, $a0, 2, 3
sw $t0, %lo(\table)($t2)
.endm
PicoWriteRomHW_SSF2: # u32 a, u32 d
ext $a0, $a0, 1, 3
bnez $a0, pwr_banking
# sram register
lui $t0, %hi(Pico+0x22211)
lb $t1, %lo(Pico+0x22211)($t0) # Pico.m.sram_reg
ins $t1, $a1, 0, 2
jr $ra
sb $t1, %lo(Pico+0x22211)($t0)
pwr_banking:
andi $a1, 0x1f
PicoWriteRomHW_SSF2_ls m_read8_def_table m_read8_table
PicoWriteRomHW_SSF2_ls m_read16_def_table m_read16_table
PicoWriteRomHW_SSF2_ls m_read32_def_table m_read32_table
jr $ra
nop

925
pico/memory_arm.s Normal file
View file

@ -0,0 +1,925 @@
@ vim:filetype=armasm
@ memory handlers with banking support for SSF II - The New Challengers
@ mostly based on Gens code
@ (c) Copyright 2006-2007, Grazvydas "notaz" Ignotas
@ All Rights Reserved
.include "port_config.s"
.text
.align 4
@ default jump tables
m_read8_def_table:
.long m_read8_rom0 @ 0x000000 - 0x07FFFF
.long m_read8_rom1 @ 0x080000 - 0x0FFFFF
.long m_read8_rom2 @ 0x100000 - 0x17FFFF
.long m_read8_rom3 @ 0x180000 - 0x1FFFFF
.long m_read8_rom4 @ 0x200000 - 0x27FFFF
.long m_read8_rom5 @ 0x280000 - 0x2FFFFF
.long m_read8_rom6 @ 0x300000 - 0x37FFFF
.long m_read8_rom7 @ 0x380000 - 0x3FFFFF
.long m_read8_rom8 @ 0x400000 - 0x47FFFF - for all those large ROM hacks
.long m_read8_rom9 @ 0x480000 - 0x4FFFFF
.long m_read8_romA @ 0x500000 - 0x57FFFF
.long m_read8_romB @ 0x580000 - 0x5FFFFF
.long m_read8_romC @ 0x600000 - 0x67FFFF
.long m_read8_romD @ 0x680000 - 0x6FFFFF
.long m_read8_romE @ 0x700000 - 0x77FFFF
.long m_read8_romF @ 0x780000 - 0x7FFFFF
.long m_read8_rom10 @ 0x800000 - 0x87FFFF
.long m_read8_rom11 @ 0x880000 - 0x8FFFFF
.long m_read8_rom12 @ 0x900000 - 0x97FFFF
.long m_read8_rom13 @ 0x980000 - 0x9FFFFF
.long m_read8_misc @ 0xA00000 - 0xA7FFFF
.long m_read_null @ 0xA80000 - 0xAFFFFF
.long m_read_null @ 0xB00000 - 0xB7FFFF
.long m_read_null @ 0xB80000 - 0xBFFFFF
.long m_read8_vdp @ 0xC00000 - 0xC7FFFF
.long m_read8_vdp @ 0xC80000 - 0xCFFFFF
.long m_read8_vdp @ 0xD00000 - 0xD7FFFF
.long m_read8_vdp @ 0xD80000 - 0xDFFFFF
.long m_read8_ram @ 0xE00000 - 0xE7FFFF
.long m_read8_ram @ 0xE80000 - 0xEFFFFF
.long m_read8_ram @ 0xF00000 - 0xF7FFFF
.long m_read8_ram @ 0xF80000 - 0xFFFFFF
m_read16_def_table:
.long m_read16_rom0 @ 0x000000 - 0x07FFFF
.long m_read16_rom1 @ 0x080000 - 0x0FFFFF
.long m_read16_rom2 @ 0x100000 - 0x17FFFF
.long m_read16_rom3 @ 0x180000 - 0x1FFFFF
.long m_read16_rom4 @ 0x200000 - 0x27FFFF
.long m_read16_rom5 @ 0x280000 - 0x2FFFFF
.long m_read16_rom6 @ 0x300000 - 0x37FFFF
.long m_read16_rom7 @ 0x380000 - 0x3FFFFF
.long m_read16_rom8 @ 0x400000 - 0x47FFFF
.long m_read16_rom9 @ 0x480000 - 0x4FFFFF
.long m_read16_romA @ 0x500000 - 0x57FFFF
.long m_read16_romB @ 0x580000 - 0x5FFFFF
.long m_read16_romC @ 0x600000 - 0x67FFFF
.long m_read16_romD @ 0x680000 - 0x6FFFFF
.long m_read16_romE @ 0x700000 - 0x77FFFF
.long m_read16_romF @ 0x780000 - 0x7FFFFF
.long m_read16_rom10 @ 0x800000 - 0x87FFFF
.long m_read16_rom11 @ 0x880000 - 0x8FFFFF
.long m_read16_rom12 @ 0x900000 - 0x97FFFF
.long m_read16_rom13 @ 0x980000 - 0x9FFFFF
.long m_read16_misc @ 0xA00000 - 0xA7FFFF
.long m_read_null @ 0xA80000 - 0xAFFFFF
.long m_read_null @ 0xB00000 - 0xB7FFFF
.long m_read_null @ 0xB80000 - 0xBFFFFF
.long m_read16_vdp @ 0xC00000 - 0xC7FFFF
.long m_read16_vdp @ 0xC80000 - 0xCFFFFF
.long m_read16_vdp @ 0xD00000 - 0xD7FFFF
.long m_read16_vdp @ 0xD80000 - 0xDFFFFF
.long m_read16_ram @ 0xE00000 - 0xE7FFFF
.long m_read16_ram @ 0xE80000 - 0xEFFFFF
.long m_read16_ram @ 0xF00000 - 0xF7FFFF
.long m_read16_ram @ 0xF80000 - 0xFFFFFF
m_read32_def_table:
.long m_read32_rom0 @ 0x000000 - 0x07FFFF
.long m_read32_rom1 @ 0x080000 - 0x0FFFFF
.long m_read32_rom2 @ 0x100000 - 0x17FFFF
.long m_read32_rom3 @ 0x180000 - 0x1FFFFF
.long m_read32_rom4 @ 0x200000 - 0x27FFFF
.long m_read32_rom5 @ 0x280000 - 0x2FFFFF
.long m_read32_rom6 @ 0x300000 - 0x37FFFF
.long m_read32_rom7 @ 0x380000 - 0x3FFFFF
.long m_read32_rom8 @ 0x400000 - 0x47FFFF
.long m_read32_rom9 @ 0x480000 - 0x4FFFFF
.long m_read32_romA @ 0x500000 - 0x57FFFF
.long m_read32_romB @ 0x580000 - 0x5FFFFF
.long m_read32_romC @ 0x600000 - 0x67FFFF
.long m_read32_romD @ 0x680000 - 0x6FFFFF
.long m_read32_romE @ 0x700000 - 0x77FFFF
.long m_read32_romF @ 0x780000 - 0x7FFFFF
.long m_read32_rom10 @ 0x800000 - 0x87FFFF
.long m_read32_rom11 @ 0x880000 - 0x8FFFFF
.long m_read32_rom12 @ 0x900000 - 0x97FFFF
.long m_read32_rom13 @ 0x980000 - 0x9FFFFF
.long m_read32_misc @ 0xA00000 - 0xA7FFFF
.long m_read_null @ 0xA80000 - 0xAFFFFF
.long m_read_null @ 0xB00000 - 0xB7FFFF
.long m_read_null @ 0xB80000 - 0xBFFFFF
.long m_read32_vdp @ 0xC00000 - 0xC7FFFF
.long m_read32_vdp @ 0xC80000 - 0xCFFFFF
.long m_read32_vdp @ 0xD00000 - 0xD7FFFF
.long m_read32_vdp @ 0xD80000 - 0xDFFFFF
.long m_read32_ram @ 0xE00000 - 0xE7FFFF
.long m_read32_ram @ 0xE80000 - 0xEFFFFF
.long m_read32_ram @ 0xF00000 - 0xF7FFFF
.long m_read32_ram @ 0xF80000 - 0xFFFFFF
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.bss
.align 4
@.section .bss, "brw"
@.data
@ used tables
m_read8_table:
.skip 32*4
m_read16_table:
.skip 32*4
m_read32_table:
.skip 32*4
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.text
.align 4
.global PicoMemReset
.global PicoRead8
.global PicoRead16
.global PicoRead32
.global PicoWrite8
.global PicoWriteRomHW_SSF2
.global m_m68k_read8_misc
.global m_m68k_write8_misc
PicoMemReset:
ldr r12,=(Pico+0x22204)
ldr r12,[r12] @ romsize
add r12,r12,#0x80000
sub r12,r12,#1
mov r12,r12,lsr #19
ldr r0, =m_read8_table
ldr r1, =m_read8_def_table
mov r2, #32
1:
ldr r3, [r1], #4
str r3, [r0], #4
subs r2, r2, #1
bne 1b
ldr r0, =m_read16_table
ldr r1, =m_read16_def_table
mov r2, #32
1:
subs r2, r2, #1
ldr r3, [r1], #4
str r3, [r0], #4
bne 1b
ldr r0, =m_read32_table
ldr r1, =m_read32_def_table
mov r2, #32
1:
subs r2, r2, #1
ldr r3, [r1], #4
str r3, [r0], #4
bne 1b
@ update memhandlers according to ROM size
ldr r1, =m_read8_above_rom
ldr r0, =m_read8_table
mov r2, #20
1:
sub r2, r2, #1
cmp r2, r12
blt 2f
cmp r2, #4
beq 1b @ do not touch the SRAM area
str r1, [r0, r2, lsl #2]
b 1b
2:
ldr r1, =m_read16_above_rom
ldr r0, =m_read16_table
mov r2, #20
1:
sub r2, r2, #1
cmp r2, r12
blt 2f
cmp r2, #4
beq 1b
str r1, [r0, r2, lsl #2]
b 1b
2:
ldr r1, =m_read32_above_rom
ldr r0, =m_read32_table
mov r2, #20
1:
sub r2, r2, #1
cmp r2, r12
blt 2f
cmp r2, #4
beq 1b
str r1, [r0, r2, lsl #2]
b 1b
2:
bx lr
.pool
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
PicoRead8: @ u32 a
ldr r2, =m_read8_table
bic r0, r0, #0xff000000
and r1, r0, #0x00f80000
ldr pc, [r2, r1, lsr #17]
PicoRead16: @ u32 a
ldr r2, =m_read16_table
bic r0, r0, #0xff000000
and r1, r0, #0x00f80000
ldr pc, [r2, r1, lsr #17]
PicoRead32: @ u32 a
ldr r2, =m_read32_table
bic r0, r0, #0xff000000
and r1, r0, #0x00f80000
ldr pc, [r2, r1, lsr #17]
.pool
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
m_read_null:
mov r0, #0
bx lr
.macro m_read8_rom sect
ldr r1, =(Pico+0x22200)
bic r0, r0, #0xf80000
ldr r1, [r1]
.if \sect
orr r0, r0, #0x080000*\sect
.endif
eor r0, r0, #1
ldrb r0, [r1, r0]
bx lr
.endm
m_read8_rom0: @ 0x000000 - 0x07ffff
m_read8_rom 0
m_read8_rom1: @ 0x080000 - 0x0fffff
m_read8_rom 1
m_read8_rom2: @ 0x100000 - 0x17ffff
m_read8_rom 2
m_read8_rom3: @ 0x180000 - 0x1fffff
m_read8_rom 3
m_read8_rom4: @ 0x200000 - 0x27ffff, SRAM area
ldr r2, =(SRam)
ldr r3, =(Pico+0x22200)
ldr r1, [r2, #8] @ SRam.end
bic r0, r0, #0xf80000
orr r0, r0, #0x200000
cmp r0, r1
bgt m_read8_nosram
ldr r1, [r2, #4] @ SRam.start
cmp r0, r1
blt m_read8_nosram
ldrb r1, [r3, #0x11] @ Pico.m.sram_reg
tst r1, #5
bne SRAMRead
m_read8_nosram:
ldr r1, [r3, #4] @ romsize
cmp r0, r1
movgt r0, #0
bxgt lr @ bad location
ldr r1, [r3]
eor r0, r0, #1
ldrb r0, [r1, r0]
bx lr
m_read8_rom5: @ 0x280000 - 0x2fffff
m_read8_rom 5
m_read8_rom6: @ 0x300000 - 0x37ffff
m_read8_rom 6
m_read8_rom7: @ 0x380000 - 0x3fffff
m_read8_rom 7
m_read8_rom8: @ 0x400000 - 0x47ffff
m_read8_rom 8
m_read8_rom9: @ 0x480000 - 0x4fffff
m_read8_rom 9
m_read8_romA: @ 0x500000 - 0x57ffff
m_read8_rom 0xA
m_read8_romB: @ 0x580000 - 0x5fffff
m_read8_rom 0xB
m_read8_romC: @ 0x600000 - 0x67ffff
m_read8_rom 0xC
m_read8_romD: @ 0x680000 - 0x6fffff
m_read8_rom 0xD
m_read8_romE: @ 0x700000 - 0x77ffff
m_read8_rom 0xE
m_read8_romF: @ 0x780000 - 0x7fffff
m_read8_rom 0xF
m_read8_rom10: @ 0x800000 - 0x87ffff
m_read8_rom 0x10
m_read8_rom11: @ 0x880000 - 0x8fffff
m_read8_rom 0x11
m_read8_rom12: @ 0x900000 - 0x97ffff
m_read8_rom 0x12
m_read8_rom13: @ 0x980000 - 0x9fffff
m_read8_rom 0x13
m_m68k_read8_misc:
m_read8_misc:
bic r2, r0, #0x001f @ most commonly we get i/o port read,
cmp r2, #0xa10000 @ so check for it first
bne m_read8_misc2
m_read8_misc_io:
ands r0, r0, #0x1e
beq m_read8_misc_hwreg
cmp r0, #4
movlt r0, #0
moveq r0, #1
ble PadRead
ldr r3, =(Pico+0x22000)
mov r0, r0, lsr #1 @ other IO ports (Pico.ioports[a])
ldrb r0, [r3, r0]
bx lr
m_read8_misc_hwreg:
ldr r3, =(Pico+0x22200)
ldrb r0, [r3, #0x0f] @ Pico.m.hardware
bx lr
m_read8_misc2:
mov r2, #0xa10000 @ games also like to poll busreq,
orr r2, r2, #0x001100 @ so we'll try it now
cmp r0, r2
beq z80ReadBusReq
and r2, r0, #0xff0000 @ finally it might be
cmp r2, #0xa00000 @ z80 area
bne m_read8_misc3
tst r0, #0x4000
beq z80Read8 @ z80 RAM
and r2, r0, #0x6000
cmp r2, #0x4000
mvnne r0, #0
bxne lr @ invalid
b ym2612_read_local_68k
m_read8_fake_ym2612:
ldr r3, =(Pico+0x22200)
ldrb r0, [r3, #8] @ Pico.m.rotate
add r1, r0, #1
strb r1, [r3, #8]
and r0, r0, #3
bx lr
m_read8_misc3:
@ if everything else fails, use generic handler
stmfd sp!,{r0,lr}
bic r0, r0, #1
mov r1, #8
bl OtherRead16
ldmfd sp!,{r1,lr}
tst r1, #1
moveq r0, r0, lsr #8
bx lr
m_read8_vdp:
tst r0, #0x70000
tsteq r0, #0x000e0
bxne lr @ invalid read
b PicoVideoRead8
m_read8_ram:
ldr r1, =Pico
bic r0, r0, #0xff0000
eor r0, r0, #1
ldrb r0, [r1, r0]
bx lr
m_read8_above_rom:
@ might still be SRam (Micro Machines, HardBall '95)
ldr r2, =(SRam)
ldr r3, =(Pico+0x22200)
ldr r1, [r2, #8] @ SRam.end
cmp r0, r1
bgt m_read8_ar_nosram
ldr r1, [r2, #4] @ SRam.start
cmp r0, r1
blt m_read8_ar_nosram
ldrb r1, [r3, #0x11] @ Pico.m.sram_reg
tst r1, #5
bne SRAMRead
m_read8_ar_nosram:
ldr r2, =PicoRead16Hook
stmfd sp!,{r0,lr}
ldr r2, [r2]
bic r0, r0, #1
mov r1, #8
mov lr, pc
bx r2
ldmfd sp!,{r1,lr}
tst r1, #1
moveq r0, r0, lsr #8
bx lr
.pool
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.macro m_read16_rom sect
ldr r1, =(Pico+0x22200)
bic r0, r0, #0xf80000
ldr r1, [r1]
bic r0, r0, #1
.if \sect
orr r0, r0, #0x080000*\sect
.endif
ldrh r0, [r1, r0]
bx lr
.endm
m_read16_rom0: @ 0x000000 - 0x07ffff
m_read16_rom 0
m_read16_rom1: @ 0x080000 - 0x0fffff
m_read16_rom 1
m_read16_rom2: @ 0x100000 - 0x17ffff
m_read16_rom 2
m_read16_rom3: @ 0x180000 - 0x1fffff
m_read16_rom 3
m_read16_rom4: @ 0x200000 - 0x27ffff, SRAM area (NBA Live 95)
ldr r2, =(SRam)
ldr r3, =(Pico+0x22200)
ldr r1, [r2, #8] @ SRam.end
bic r0, r0, #0xf80000
bic r0, r0, #1
orr r0, r0, #0x200000
cmp r0, r1
bgt m_read16_nosram
ldr r1, [r2, #4] @ SRam.start
cmp r0, r1
blt m_read16_nosram
ldrb r1, [r3, #0x11] @ Pico.m.sram_reg
tst r1, #5
beq m_read16_nosram
stmfd sp!,{lr}
bl SRAMRead16
ldmfd sp!,{pc}
m_read16_nosram:
ldr r1, [r3, #4] @ romsize
cmp r0, r1
movgt r0, #0
bxgt lr @ bad location
ldr r1, [r3] @ 1ci
ldrh r0, [r1, r0]
bx lr
m_read16_rom5: @ 0x280000 - 0x2fffff
m_read16_rom 5
m_read16_rom6: @ 0x300000 - 0x37ffff
m_read16_rom 6
m_read16_rom7: @ 0x380000 - 0x3fffff
m_read16_rom 7
m_read16_rom8: @ 0x400000 - 0x47ffff
m_read16_rom 8
m_read16_rom9: @ 0x480000 - 0x4fffff
m_read16_rom 9
m_read16_romA: @ 0x500000 - 0x57ffff
m_read16_rom 0xA
m_read16_romB: @ 0x580000 - 0x5fffff
m_read16_rom 0xB
m_read16_romC: @ 0x600000 - 0x67ffff
m_read16_rom 0xC
m_read16_romD: @ 0x680000 - 0x6fffff
m_read16_rom 0xD
m_read16_romE: @ 0x700000 - 0x77ffff
m_read16_rom 0xE
m_read16_romF: @ 0x780000 - 0x7fffff
m_read16_rom 0xF
m_read16_rom10: @ 0x800000 - 0x87ffff
m_read16_rom 0x10
m_read16_rom11: @ 0x880000 - 0x8fffff
m_read16_rom 0x11
m_read16_rom12: @ 0x900000 - 0x97ffff
m_read16_rom 0x12
m_read16_rom13: @ 0x980000 - 0x9fffff
m_read16_rom 0x13
m_read16_misc:
bic r0, r0, #1
mov r1, #16
b OtherRead16
m_read16_vdp:
tst r0, #0x70000 @ if ((a&0xe700e0)==0xc00000)
tsteq r0, #0x000e0
bxne lr @ invalid read
bic r0, r0, #1
b PicoVideoRead
m_read16_ram:
ldr r1, =Pico
bic r0, r0, #0xff0000
bic r0, r0, #1
ldrh r0, [r1, r0]
bx lr
m_read16_above_rom:
@ might still be SRam
ldr r2, =(SRam)
ldr r3, =(Pico+0x22200)
ldr r1, [r2, #8] @ SRam.end
bic r0, r0, #1
cmp r0, r1
bgt m_read16_ar_nosram
ldr r1, [r2, #4] @ SRam.start
cmp r0, r1
blt m_read16_ar_nosram
ldrb r1, [r3, #0x11] @ Pico.m.sram_reg
tst r1, #5
beq m_read16_ar_nosram
stmfd sp!,{lr}
bl SRAMRead16
ldmfd sp!,{pc}
m_read16_ar_nosram:
ldr r2, =PicoRead16Hook
ldr r2, [r2]
mov r1, #16
bx r2
.pool
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.macro m_read32_rom sect
ldr r1, =(Pico+0x22200)
bic r0, r0, #0xf80000
ldr r1, [r1]
bic r0, r0, #1
.if \sect
orr r0, r0, #0x080000*\sect
.endif
ldrh r0, [r1, r0]!
ldrh r1, [r1, #2] @ 1ci
orr r0, r1, r0, lsl #16
bx lr
.endm
m_read32_rom0: @ 0x000000 - 0x07ffff
m_read32_rom 0
m_read32_rom1: @ 0x080000 - 0x0fffff
m_read32_rom 1
m_read32_rom2: @ 0x100000 - 0x17ffff
m_read32_rom 2
m_read32_rom3: @ 0x180000 - 0x1fffff
m_read32_rom 3
m_read32_rom4: @ 0x200000 - 0x27ffff, SRAM area (does any game do long reads?)
ldr r2, =(SRam)
ldr r3, =(Pico+0x22200)
ldr r1, [r2, #8] @ SRam.end
bic r0, r0, #0xf80000
bic r0, r0, #1
orr r0, r0, #0x200000
cmp r0, r1
bgt m_read32_nosram
ldr r1, [r2, #4] @ SRam.start
cmp r0, r1
blt m_read32_nosram
ldrb r1, [r3, #0x11] @ Pico.m.sram_reg
tst r1, #5
beq m_read32_nosram
stmfd sp!,{r0,lr}
bl SRAMRead16
ldmfd sp!,{r1,lr}
stmfd sp!,{r0,lr}
add r0, r1, #2
bl SRAMRead16
ldmfd sp!,{r1,lr}
orr r0, r0, r1, lsl #16
bx lr
m_read32_nosram:
ldr r1, [r3, #4] @ romsize
cmp r0, r1
movgt r0, #0
bxgt lr @ bad location
ldr r1, [r3] @ (1ci)
ldrh r0, [r1, r0]!
ldrh r1, [r1, #2] @ (2ci)
orr r0, r1, r0, lsl #16
bx lr
m_read32_rom5: @ 0x280000 - 0x2fffff
m_read32_rom 5
m_read32_rom6: @ 0x300000 - 0x37ffff
m_read32_rom 6
m_read32_rom7: @ 0x380000 - 0x3fffff
m_read32_rom 7
m_read32_rom8: @ 0x400000 - 0x47ffff
m_read32_rom 8
m_read32_rom9: @ 0x480000 - 0x4fffff
m_read32_rom 9
m_read32_romA: @ 0x500000 - 0x57ffff
m_read32_rom 0xA
m_read32_romB: @ 0x580000 - 0x5fffff
m_read32_rom 0xB
m_read32_romC: @ 0x600000 - 0x67ffff
m_read32_rom 0xC
m_read32_romD: @ 0x680000 - 0x6fffff
m_read32_rom 0xD
m_read32_romE: @ 0x700000 - 0x77ffff
m_read32_rom 0xE
m_read32_romF: @ 0x780000 - 0x7fffff
m_read32_rom 0xF
m_read32_rom10: @ 0x800000 - 0x87ffff
m_read32_rom 0x10
m_read32_rom11: @ 0x880000 - 0x8fffff
m_read32_rom 0x11
m_read32_rom12: @ 0x900000 - 0x97ffff
m_read32_rom 0x12
m_read32_rom13: @ 0x980000 - 0x9fffff
m_read32_rom 0x13
m_read32_misc:
bic r0, r0, #1
stmfd sp!,{r0,lr}
mov r1, #32
bl OtherRead16
mov r1, r0
ldmfd sp!,{r0}
stmfd sp!,{r1}
add r0, r0, #2
mov r1, #32
bl OtherRead16
ldmfd sp!,{r1,lr}
orr r0, r0, r1, lsl #16
bx lr
m_read32_vdp:
tst r0, #0x70000
tsteq r0, #0x000e0
bxne lr @ invalid read
bic r0, r0, #1
add r1, r0, #2
stmfd sp!,{r1,lr}
bl PicoVideoRead
swp r0, r0, [sp]
bl PicoVideoRead
ldmfd sp!,{r1,lr}
orr r0, r0, r1, lsl #16
bx lr
m_read32_ram:
ldr r1, =Pico
bic r0, r0, #0xff0000
bic r0, r0, #1
ldrh r0, [r1, r0]!
ldrh r1, [r1, #2] @ 2ci
orr r0, r1, r0, lsl #16
bx lr
m_read32_above_rom:
ldr r2, =PicoRead16Hook
bic r0, r0, #1
ldr r2, [r2]
mov r1, #32
stmfd sp!,{r0,r2,lr}
mov lr, pc
bx r2
mov r1, r0
ldmfd sp!,{r0,r2}
stmfd sp!,{r1}
add r0, r0, #2
mov r1, #32
mov lr, pc
bx r2
ldmfd sp!,{r1,lr}
orr r0, r0, r1, lsl #16
bx lr
.pool
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
PicoWriteRomHW_SSF2: @ u32 a, u32 d
and r0, r0, #0xe
movs r0, r0, lsr #1
bne pwr_banking
@ sram register
ldr r2, =(Pico+0x22211) @ Pico.m.sram_reg
ldrb r0, [r2]
and r1, r1, #3
bic r0, r0, #3
orr r0, r0, r1
strb r0, [r2]
bx lr
pwr_banking:
and r1, r1, #0x1f
ldr r3, =m_read8_def_table
ldr r2, =m_read8_table
ldr r12, [r3, r1, lsl #2]
str r12, [r2, r0, lsl #2]
ldr r3, =m_read16_def_table
ldr r2, =m_read16_table
ldr r12, [r3, r1, lsl #2]
str r12, [r2, r0, lsl #2]
ldr r3, =m_read32_def_table
ldr r2, =m_read32_table
ldr r12, [r3, r1, lsl #2]
str r12, [r2, r0, lsl #2]
bx lr
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ Here we only handle most often used locations,
@ everything else is passed to generic handlers
PicoWrite8: @ u32 a, u8 d
bic r0, r0, #0xff000000
and r2, r0, #0x00e00000
cmp r2, #0x00e00000 @ RAM?
ldr r3, =Pico
biceq r0, r0, #0x00ff0000
eoreq r0, r0, #1
streqb r1, [r3, r0]
bxeq lr
m_m68k_write8_misc:
bic r2, r0, #0x1f @ most commonly we get i/o port write,
cmp r2, #0xa10000 @ so check for it first
bne m_write8_misc2
m_write8_io:
ldr r2, =PicoOpt
and r0, r0, #0x1e
ldr r2, [r2]
ldr r3, =(Pico+0x22000) @ Pico.ioports
tst r2, #0x20 @ 6 button pad?
streqb r1, [r3, r0, lsr #1]
bxeq lr
cmp r0, #2
cmpne r0, #4
bne m_write8_io_done @ not likely to happen
add r2, r3, #0x200 @ Pico+0x22200
mov r12,#0
cmp r0, #2
streqb r12,[r2,#0x18]
strneb r12,[r2,#0x19] @ Pico.m.padDelay[i] = 0
tst r1, #0x40 @ TH
beq m_write8_io_done
ldrb r12,[r3, r0, lsr #1]
tst r12,#0x40
bne m_write8_io_done
cmp r0, #2
ldreqb r12,[r2,#0x0a]
ldrneb r12,[r2,#0x0b] @ Pico.m.padTHPhase
add r12,r12,#1
streqb r12,[r2,#0x0a]
strneb r12,[r2,#0x0b] @ Pico.m.padTHPhase
m_write8_io_done:
strb r1, [r3, r0, lsr #1]
bx lr
m_write8_misc2:
and r2, r0, #0xff0000
cmp r2, #0xa00000 @ z80 area?
bne m_write8_not_z80
tst r0, #0x4000
bne m_write8_z80_not_ram
ldr r3, =(Pico+0x20000) @ Pico.zram
add r2, r3, #0x02200 @ Pico+0x22200
ldrb r2, [r2, #9] @ Pico.m.z80Run
bic r0, r0, #0xff0000
bic r0, r0, #0x00e000
tst r2, #1
ldr r2, =SekCycleCnt
streqb r1, [r3, r0] @ zram
ldr r0, [r2]
add r0, r0, #2 @ hack?
str r0, [r2]
bx lr
m_write8_z80_not_ram:
and r2, r0, #0x6000
cmp r2, #0x4000
bne m_write8_z80_not_ym2612
ldr r3, =PicoOpt
and r0, r0, #3
ldr r3, [r3]
mov r2, #0 @ is_from_z80 = 0
tst r3, #1
bxeq lr
stmfd sp!,{lr}
and r1, r1, #0xff
bl ym2612_write_local
ldr r2, =emustatus
ldmfd sp!,{lr}
ldr r1, [r2]
and r0, r0, #1
orr r1, r0, r1
str r1, [r2] @ emustatus|=ym2612_write_local(a&3, d);
bx lr
m_write8_z80_not_ym2612: @ not too likely
mov r2, r0, lsl #17
bic r2, r2, #6<<17
mov r3, #0x7f00
orr r3, r3, #0x0011
cmp r3, r2, lsr #17 @ psg @ z80 area?
beq m_write8_psg
and r2, r0, #0x7f00
cmp r2, #0x6000 @ bank register?
bxne lr @ invalid write
m_write8_z80_bank_reg:
ldr r3, =(Pico+0x22208) @ Pico.m
ldrh r2, [r3, #0x0a]
mov r1, r1, lsl #8
orr r2, r1, r2, lsr #1
bic r2, r2, #0xfe00
strh r2, [r3, #0x0a]
bx lr
m_write8_not_z80:
and r2, r0, #0xe70000
cmp r2, #0xc00000 @ VDP area?
bne OtherWrite8 @ passthrough
and r2, r0, #0xf9
cmp r2, #0x11
bne OtherWrite8
m_write8_psg:
ldr r2, =PicoOpt
and r0, r1, #0xff
ldr r2, [r2]
tst r2, #2
bxeq lr
b SN76496Write

247
pico/memory_cmn.c Normal file
View file

@ -0,0 +1,247 @@
// common code for Memory.c and cd/Memory.c
// (c) Copyright 2006-2007, Grazvydas "notaz" Ignotas
#ifndef UTYPES_DEFINED
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define UTYPES_DEFINED
#endif
#ifndef _ASM_MEMORY_C
static
#endif
u8 z80Read8(u32 a)
{
if(Pico.m.z80Run&1) return 0;
a&=0x1fff;
if (!(PicoOpt&POPT_EN_Z80))
{
// Z80 disabled, do some faking
static u8 zerosent = 0;
if(a == Pico.m.z80_lastaddr) { // probably polling something
u8 d = Pico.m.z80_fakeval;
if((d & 0xf) == 0xf && !zerosent) {
d = 0; zerosent = 1;
} else {
Pico.m.z80_fakeval++;
zerosent = 0;
}
return d;
} else {
Pico.m.z80_fakeval = 0;
}
}
Pico.m.z80_lastaddr = (u16) a;
return Pico.zram[a];
}
#ifndef _ASM_MEMORY_C
static
#endif
u32 z80ReadBusReq(void)
{
u32 d=Pico.m.z80Run&1;
if (!d) {
// needed by buggy Terminator (Sega CD)
int stop_before = SekCyclesDone() - z80stopCycle;
//elprintf(EL_BUSREQ, "get_zrun: stop before: %i", stop_before);
// note: if we use 20 or more here, Barkley Shut Up and Jam! will purposedly crash itself.
// but CD Terminator needs at least 32, so it only works because next frame cycle wrap.
if (stop_before > 0 && stop_before < 20) // Gens uses 16 here
d = 1; // bus not yet available
}
elprintf(EL_BUSREQ, "get_zrun: %02x [%i] @%06x", d|0x80, SekCyclesDone(), SekPc);
return d|0x80;
}
static void z80WriteBusReq(u32 d)
{
d&=1; d^=1;
elprintf(EL_BUSREQ, "set_zrun: %i->%i [%i] @%06x", Pico.m.z80Run, d, SekCyclesDone(), SekPc);
if (d ^ Pico.m.z80Run)
{
if (d)
{
z80_cycle_cnt = cycles_68k_to_z80(SekCyclesDone());
}
else
{
z80stopCycle = SekCyclesDone();
if ((PicoOpt&POPT_EN_Z80) && !Pico.m.z80_reset)
PicoSyncZ80(z80stopCycle);
}
Pico.m.z80Run=d;
}
}
static void z80WriteReset(u32 d)
{
d&=1; d^=1;
elprintf(EL_BUSREQ, "set_zreset: %i->%i [%i] @%06x", Pico.m.z80_reset, d, SekCyclesDone(), SekPc);
if (d ^ Pico.m.z80_reset)
{
if (d)
{
if ((PicoOpt&POPT_EN_Z80) && Pico.m.z80Run)
PicoSyncZ80(SekCyclesDone());
}
else
{
z80_cycle_cnt = cycles_68k_to_z80(SekCyclesDone());
z80_reset();
}
YM2612ResetChip();
timers_reset();
Pico.m.z80_reset=d;
}
}
#ifndef _ASM_MEMORY_C
static
#endif
u32 OtherRead16(u32 a, int realsize)
{
u32 d=0;
if ((a&0xffffe0)==0xa10000) { // I/O ports
a=(a>>1)&0xf;
switch(a) {
case 0: d=Pico.m.hardware; break; // Hardware value (Version register)
case 1: d=PadRead(0); break;
case 2: d=PadRead(1); break;
default: d=Pico.ioports[a]; break; // IO ports can be used as RAM
}
d|=d<<8;
goto end;
}
// rotate fakes next fetched instruction for Time Killers
if (a==0xa11100) { // z80 busreq
d=(z80ReadBusReq()<<8)|Pico.m.rotate++;
goto end;
}
if ((a&0xff0000)==0xa00000)
{
if (Pico.m.z80Run&1)
elprintf(EL_ANOMALY, "68k z80 read with no bus! [%06x] @ %06x", a, SekPc);
if ((a&0x4000)==0x0000) { d=z80Read8(a); d|=d<<8; goto end; } // Z80 ram (not byteswaped)
if ((a&0x6000)==0x4000) { d=ym2612_read_local_68k(); goto end; } // 0x4000-0x5fff
elprintf(EL_ANOMALY, "68k bad read [%06x]", a);
d=0xffff;
goto end;
}
d = PicoRead16Hook(a, realsize);
end:
return d;
}
static void IoWrite8(u32 a, u32 d)
{
a=(a>>1)&0xf;
// 6 button gamepad: if TH went from 0 to 1, gamepad changes state
if (PicoOpt&POPT_6BTN_PAD)
{
if (a==1) {
Pico.m.padDelay[0] = 0;
if(!(Pico.ioports[1]&0x40) && (d&0x40)) Pico.m.padTHPhase[0]++;
}
else if (a==2) {
Pico.m.padDelay[1] = 0;
if(!(Pico.ioports[2]&0x40) && (d&0x40)) Pico.m.padTHPhase[1]++;
}
}
Pico.ioports[a]=(u8)d; // IO ports can be used as RAM
}
#ifndef _ASM_CD_MEMORY_C
static
#endif
void OtherWrite8(u32 a,u32 d)
{
#if !defined(_ASM_MEMORY_C) || defined(_ASM_MEMORY_C_AMIPS)
if ((a&0xe700f9)==0xc00011||(a&0xff7ff9)==0xa07f11) { if(PicoOpt&2) SN76496Write(d); return; } // PSG Sound
if ((a&0xff4000)==0xa00000) { // z80 RAM
SekCyclesBurn(2); // hack
if (!(Pico.m.z80Run&1) && !Pico.m.z80_reset) Pico.zram[a&0x1fff]=(u8)d;
else elprintf(EL_ANOMALY, "68k z80 write with no bus or reset! [%06x] %02x @ %06x", a, d&0xff, SekPc);
return;
}
if ((a&0xff6000)==0xa04000) { if(PicoOpt&1) emustatus|=ym2612_write_local(a&3, d&0xff, 0)&1; return; } // FM Sound
if ((a&0xffffe0)==0xa10000) { IoWrite8(a, d); return; } // I/O ports
#endif
if (a==0xa11100) { z80WriteBusReq(d); return; }
if (a==0xa11200) { z80WriteReset(d); return; }
#if !defined(_ASM_MEMORY_C) || defined(_ASM_MEMORY_C_AMIPS)
if ((a&0xff7f00)==0xa06000) // Z80 BANK register
{
Pico.m.z80_bank68k>>=1;
Pico.m.z80_bank68k|=(d&1)<<8;
Pico.m.z80_bank68k&=0x1ff; // 9 bits and filled in the new top one
elprintf(EL_Z80BNK, "z80 bank=%06x", Pico.m.z80_bank68k<<15);
return;
}
#endif
if ((a&0xe700e0)==0xc00000) {
d&=0xff;
PicoVideoWrite(a,(u16)(d|(d<<8))); // Byte access gets mirrored
return;
}
PicoWrite8Hook(a, d&0xff, 8);
}
#ifndef _ASM_CD_MEMORY_C
static
#endif
void OtherWrite16(u32 a,u32 d)
{
if (a==0xa11100) { z80WriteBusReq(d>>8); return; }
if ((a&0xffffe0)==0xa10000) { IoWrite8(a, d); return; } // I/O ports
if ((a&0xe700f8)==0xc00010||(a&0xff7ff8)==0xa07f10) { if(PicoOpt&2) SN76496Write(d); return; } // PSG Sound
if ((a&0xff6000)==0xa04000) { if(PicoOpt&1) emustatus|=ym2612_write_local(a&3, d&0xff, 0)&1; return; } // FM Sound
if ((a&0xff4000)==0xa00000) { // Z80 ram (MSB only)
if (!(Pico.m.z80Run&1) && !Pico.m.z80_reset) Pico.zram[a&0x1fff]=(u8)(d>>8);
else elprintf(EL_ANOMALY, "68k z80 write with no bus or reset! [%06x] %04x @ %06x", a, d&0xffff, SekPc);
return;
}
if (a==0xa11200) { z80WriteReset(d>>8); return; }
if ((a&0xff7f00)==0xa06000) // Z80 BANK register
{
Pico.m.z80_bank68k>>=1;
Pico.m.z80_bank68k|=(d&1)<<8;
Pico.m.z80_bank68k&=0x1ff; // 9 bits and filled in the new top one
elprintf(EL_Z80BNK, "z80 bank=%06x", Pico.m.z80_bank68k<<15);
return;
}
#ifndef _CD_MEMORY_C
if (a >= SRam.start && a <= SRam.end) {
elprintf(EL_SRAMIO, "sram w16 [%06x] %04x @ %06x", a, d, SekPc);
if ((Pico.m.sram_reg&0x16)==0x10) { // detected, not EEPROM, write not disabled
u8 *pm=(u8 *)(SRam.data-SRam.start+a);
*pm++=d>>8;
*pm++=d;
SRam.changed = 1;
}
else
SRAMWrite(a, d);
return;
}
#endif
PicoWrite16Hook(a, d&0xffff, 16);
}

340
pico/misc.c Normal file
View file

@ -0,0 +1,340 @@
// This is part of Pico Library
// (c) Copyright 2006 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include "pico_int.h"
// H-counter table for hvcounter reads in 40col mode
// based on Gens code
const unsigned char hcounts_40[] = {
0x07,0x07,0x08,0x08,0x08,0x09,0x09,0x0a,0x0a,0x0b,0x0b,0x0b,0x0c,0x0c,0x0d,0x0d,
0x0e,0x0e,0x0e,0x0f,0x0f,0x10,0x10,0x10,0x11,0x11,0x12,0x12,0x13,0x13,0x13,0x14,
0x14,0x15,0x15,0x15,0x16,0x16,0x17,0x17,0x18,0x18,0x18,0x19,0x19,0x1a,0x1a,0x1b,
0x1b,0x1b,0x1c,0x1c,0x1d,0x1d,0x1d,0x1e,0x1e,0x1f,0x1f,0x20,0x20,0x20,0x21,0x21,
0x22,0x22,0x23,0x23,0x23,0x24,0x24,0x25,0x25,0x25,0x26,0x26,0x27,0x27,0x28,0x28,
0x28,0x29,0x29,0x2a,0x2a,0x2a,0x2b,0x2b,0x2c,0x2c,0x2d,0x2d,0x2d,0x2e,0x2e,0x2f,
0x2f,0x30,0x30,0x30,0x31,0x31,0x32,0x32,0x32,0x33,0x33,0x34,0x34,0x35,0x35,0x35,
0x36,0x36,0x37,0x37,0x38,0x38,0x38,0x39,0x39,0x3a,0x3a,0x3a,0x3b,0x3b,0x3c,0x3c,
0x3d,0x3d,0x3d,0x3e,0x3e,0x3f,0x3f,0x3f,0x40,0x40,0x41,0x41,0x42,0x42,0x42,0x43,
0x43,0x44,0x44,0x45,0x45,0x45,0x46,0x46,0x47,0x47,0x47,0x48,0x48,0x49,0x49,0x4a,
0x4a,0x4a,0x4b,0x4b,0x4c,0x4c,0x4d,0x4d,0x4d,0x4e,0x4e,0x4f,0x4f,0x4f,0x50,0x50,
0x51,0x51,0x52,0x52,0x52,0x53,0x53,0x54,0x54,0x55,0x55,0x55,0x56,0x56,0x57,0x57,
0x57,0x58,0x58,0x59,0x59,0x5a,0x5a,0x5a,0x5b,0x5b,0x5c,0x5c,0x5c,0x5d,0x5d,0x5e,
0x5e,0x5f,0x5f,0x5f,0x60,0x60,0x61,0x61,0x62,0x62,0x62,0x63,0x63,0x64,0x64,0x64,
0x65,0x65,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x69,0x69,0x6a,0x6a,0x6a,0x6b,0x6b,
0x6c,0x6c,0x6c,0x6d,0x6d,0x6e,0x6e,0x6f,0x6f,0x6f,0x70,0x70,0x71,0x71,0x71,0x72,
0x72,0x73,0x73,0x74,0x74,0x74,0x75,0x75,0x76,0x76,0x77,0x77,0x77,0x78,0x78,0x79,
0x79,0x79,0x7a,0x7a,0x7b,0x7b,0x7c,0x7c,0x7c,0x7d,0x7d,0x7e,0x7e,0x7f,0x7f,0x7f,
0x80,0x80,0x81,0x81,0x81,0x82,0x82,0x83,0x83,0x84,0x84,0x84,0x85,0x85,0x86,0x86,
0x86,0x87,0x87,0x88,0x88,0x89,0x89,0x89,0x8a,0x8a,0x8b,0x8b,0x8c,0x8c,0x8c,0x8d,
0x8d,0x8e,0x8e,0x8e,0x8f,0x8f,0x90,0x90,0x91,0x91,0x91,0x92,0x92,0x93,0x93,0x94,
0x94,0x94,0x95,0x95,0x96,0x96,0x96,0x97,0x97,0x98,0x98,0x99,0x99,0x99,0x9a,0x9a,
0x9b,0x9b,0x9b,0x9c,0x9c,0x9d,0x9d,0x9e,0x9e,0x9e,0x9f,0x9f,0xa0,0xa0,0xa1,0xa1,
0xa1,0xa2,0xa2,0xa3,0xa3,0xa3,0xa4,0xa4,0xa5,0xa5,0xa6,0xa6,0xa6,0xa7,0xa7,0xa8,
0xa8,0xa9,0xa9,0xa9,0xaa,0xaa,0xab,0xab,0xab,0xac,0xac,0xad,0xad,0xae,0xae,0xae,
0xaf,0xaf,0xb0,0xb0,
0xe4,0xe4,0xe4,0xe5,0xe5,0xe6,0xe6,0xe6,0xe7,0xe7,0xe8,0xe8,0xe9,0xe9,0xe9,0xea,
0xea,0xeb,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,
0xf1,0xf1,0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,
0xf8,0xf8,0xf9,0xf9,0xf9,0xfa,0xfa,0xfb,0xfb,0xfb,0xfc,0xfc,0xfd,0xfd,0xfe,0xfe,
0xfe,0xff,0xff,0x00,0x00,0x00,0x01,0x01,0x02,0x02,0x03,0x03,0x03,0x04,0x04,0x05,
0x05,0x06,0x06,0x06,
0x07,0x07,0x08,0x08,0x08,0x09,0x09,0x0a,0x0a,0x0b,0x0b,0x0b,0x0c,0x0c,0x0d,0x0d,
0x0e,0x0e,0x0e,0x0f,0x0f,0x10,0x10,0x10,
};
// H-counter table for hvcounter reads in 32col mode
const unsigned char hcounts_32[] = {
0x05,0x05,0x05,0x06,0x06,0x07,0x07,0x07,0x08,0x08,0x08,0x09,0x09,0x09,0x0a,0x0a,
0x0a,0x0b,0x0b,0x0b,0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x0e,0x0e,0x0f,0x0f,0x0f,0x10,
0x10,0x10,0x11,0x11,0x11,0x12,0x12,0x12,0x13,0x13,0x13,0x14,0x14,0x14,0x15,0x15,
0x15,0x16,0x16,0x17,0x17,0x17,0x18,0x18,0x18,0x19,0x19,0x19,0x1a,0x1a,0x1a,0x1b,
0x1b,0x1b,0x1c,0x1c,0x1c,0x1d,0x1d,0x1d,0x1e,0x1e,0x1f,0x1f,0x1f,0x20,0x20,0x20,
0x21,0x21,0x21,0x22,0x22,0x22,0x23,0x23,0x23,0x24,0x24,0x24,0x25,0x25,0x26,0x26,
0x26,0x27,0x27,0x27,0x28,0x28,0x28,0x29,0x29,0x29,0x2a,0x2a,0x2a,0x2b,0x2b,0x2b,
0x2c,0x2c,0x2c,0x2d,0x2d,0x2e,0x2e,0x2e,0x2f,0x2f,0x2f,0x30,0x30,0x30,0x31,0x31,
0x31,0x32,0x32,0x32,0x33,0x33,0x33,0x34,0x34,0x34,0x35,0x35,0x36,0x36,0x36,0x37,
0x37,0x37,0x38,0x38,0x38,0x39,0x39,0x39,0x3a,0x3a,0x3a,0x3b,0x3b,0x3b,0x3c,0x3c,
0x3d,0x3d,0x3d,0x3e,0x3e,0x3e,0x3f,0x3f,0x3f,0x40,0x40,0x40,0x41,0x41,0x41,0x42,
0x42,0x42,0x43,0x43,0x43,0x44,0x44,0x45,0x45,0x45,0x46,0x46,0x46,0x47,0x47,0x47,
0x48,0x48,0x48,0x49,0x49,0x49,0x4a,0x4a,0x4a,0x4b,0x4b,0x4b,0x4c,0x4c,0x4d,0x4d,
0x4d,0x4e,0x4e,0x4e,0x4f,0x4f,0x4f,0x50,0x50,0x50,0x51,0x51,0x51,0x52,0x52,0x52,
0x53,0x53,0x53,0x54,0x54,0x55,0x55,0x55,0x56,0x56,0x56,0x57,0x57,0x57,0x58,0x58,
0x58,0x59,0x59,0x59,0x5a,0x5a,0x5a,0x5b,0x5b,0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x5e,
0x5e,0x5e,0x5f,0x5f,0x5f,0x60,0x60,0x60,0x61,0x61,0x61,0x62,0x62,0x62,0x63,0x63,
0x64,0x64,0x64,0x65,0x65,0x65,0x66,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x68,0x69,
0x69,0x69,0x6a,0x6a,0x6a,0x6b,0x6b,0x6c,0x6c,0x6c,0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,
0x6f,0x6f,0x6f,0x70,0x70,0x70,0x71,0x71,0x71,0x72,0x72,0x72,0x73,0x73,0x74,0x74,
0x74,0x75,0x75,0x75,0x76,0x76,0x76,0x77,0x77,0x77,0x78,0x78,0x78,0x79,0x79,0x79,
0x7a,0x7a,0x7b,0x7b,0x7b,0x7c,0x7c,0x7c,0x7d,0x7d,0x7d,0x7e,0x7e,0x7e,0x7f,0x7f,
0x7f,0x80,0x80,0x80,0x81,0x81,0x81,0x82,0x82,0x83,0x83,0x83,0x84,0x84,0x84,0x85,
0x85,0x85,0x86,0x86,0x86,0x87,0x87,0x87,0x88,0x88,0x88,0x89,0x89,0x89,0x8a,0x8a,
0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x8d,0x8d,0x8d,0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x90,
0x90,0x90,0x91,0x91,
0xe8,0xe8,0xe8,0xe9,0xe9,0xe9,0xea,0xea,0xea,0xeb,0xeb,0xeb,0xec,0xec,0xec,0xed,
0xed,0xed,0xee,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf0,0xf1,0xf1,0xf1,0xf2,0xf2,0xf2,
0xf3,0xf3,0xf3,0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf8,0xf8,
0xf8,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,
0xfe,0xfe,0xfe,0xff,0xff,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x02,0x03,0x03,
0x03,0x04,0x04,0x04,
0x05,0x05,0x05,0x06,0x06,0x07,0x07,0x07,0x08,0x08,0x08,0x09,0x09,0x09,0x0a,0x0a,
0x0a,0x0b,0x0b,0x0b,0x0c,0x0c,0x0c,0x0d,
};
// rarely used EEPROM SRAM code
// known games which use this:
// Wonder Boy in Monster World, Megaman - The Wily Wars (X24C01, 128 bytes)
// (see Genesis Plus for Wii/GC code and docs for info,
// full game list and better code).
unsigned int lastSSRamWrite = 0xffff0000;
// sram_reg: LAtd sela (L=pending SCL, A=pending SDA, t=(unused),
// d=SRAM was detected (header or by access), s=started, e=save is EEPROM, l=old SCL, a=old SDA)
PICO_INTERNAL void SRAMWriteEEPROM(unsigned int d) // ???? ??la (l=SCL, a=SDA)
{
unsigned int sreg = Pico.m.sram_reg, saddr = Pico.m.eeprom_addr, scyc = Pico.m.eeprom_cycle, ssa = Pico.m.eeprom_slave;
elprintf(EL_EEPROM, "eeprom: scl/sda: %i/%i -> %i/%i, newtime=%i", (sreg&2)>>1, sreg&1,
(d&2)>>1, d&1, SekCyclesDoneT()-lastSSRamWrite);
saddr&=0x1fff;
if(sreg & d & 2) {
// SCL was and is still high..
if((sreg & 1) && !(d&1)) {
// ..and SDA went low, means it's a start command, so clear internal addr reg and clock counter
elprintf(EL_EEPROM, "eeprom: -start-");
//saddr = 0;
scyc = 0;
sreg |= 8;
} else if(!(sreg & 1) && (d&1)) {
// SDA went high == stop command
elprintf(EL_EEPROM, "eeprom: -stop-");
sreg &= ~8;
}
}
else if((sreg & 8) && !(sreg & 2) && (d&2))
{
// we are started and SCL went high - next cycle
scyc++; // pre-increment
if(SRam.eeprom_type) {
// X24C02+
if((ssa&1) && scyc == 18) {
scyc = 9;
saddr++; // next address in read mode
/*if(SRam.eeprom_type==2) saddr&=0xff; else*/ saddr&=0x1fff; // mask
}
else if(SRam.eeprom_type == 2 && scyc == 27) scyc = 18;
else if(scyc == 36) scyc = 27;
} else {
// X24C01
if(scyc == 18) {
scyc = 9; // wrap
if(saddr&1) { saddr+=2; saddr&=0xff; } // next addr in read mode
}
}
elprintf(EL_EEPROM, "eeprom: scyc: %i", scyc);
}
else if((sreg & 8) && (sreg & 2) && !(d&2))
{
// we are started and SCL went low (falling edge)
if(SRam.eeprom_type) {
// X24C02+
if(scyc == 9 || scyc == 18 || scyc == 27); // ACK cycles
else if( (SRam.eeprom_type == 3 && scyc > 27) || (SRam.eeprom_type == 2 && scyc > 18) ) {
if(!(ssa&1)) {
// data write
unsigned char *pm=SRam.data+saddr;
*pm <<= 1; *pm |= d&1;
if(scyc == 26 || scyc == 35) {
saddr=(saddr&~0xf)|((saddr+1)&0xf); // only 4 (?) lowest bits are incremented
elprintf(EL_EEPROM, "eeprom: write done, addr inc to: %x, last byte=%02x", saddr, *pm);
}
SRam.changed = 1;
}
} else if(scyc > 9) {
if(!(ssa&1)) {
// we latch another addr bit
saddr<<=1;
if(SRam.eeprom_type == 2) saddr&=0xff; else saddr&=0x1fff; // mask
saddr|=d&1;
if(scyc==17||scyc==26) {
elprintf(EL_EEPROM, "eeprom: addr reg done: %x", saddr);
if(scyc==17&&SRam.eeprom_type==2) { saddr&=0xff; saddr|=(ssa<<7)&0x700; } // add device bits too
}
}
} else {
// slave address
ssa<<=1; ssa|=d&1;
if(scyc==8) elprintf(EL_EEPROM, "eeprom: slave done: %x", ssa);
}
} else {
// X24C01
if(scyc == 9); // ACK cycle, do nothing
else if(scyc > 9) {
if(!(saddr&1)) {
// data write
unsigned char *pm=SRam.data+(saddr>>1);
*pm <<= 1; *pm |= d&1;
if(scyc == 17) {
saddr=(saddr&0xf9)|((saddr+2)&6); // only 2 lowest bits are incremented
elprintf(EL_EEPROM, "eeprom: write done, addr inc to: %x, last byte=%02x", saddr>>1, *pm);
}
SRam.changed = 1;
}
} else {
// we latch another addr bit
saddr<<=1; saddr|=d&1; saddr&=0xff;
if(scyc==8) elprintf(EL_EEPROM, "eeprom: addr done: %x", saddr>>1);
}
}
}
sreg &= ~3; sreg |= d&3; // remember SCL and SDA
Pico.m.sram_reg = (unsigned char) sreg;
Pico.m.eeprom_cycle= (unsigned char) scyc;
Pico.m.eeprom_slave= (unsigned char) ssa;
Pico.m.eeprom_addr = (unsigned short)saddr;
}
PICO_INTERNAL_ASM unsigned int SRAMReadEEPROM(void)
{
unsigned int shift, d;
unsigned int sreg, saddr, scyc, ssa, interval;
// flush last pending write
SRAMWriteEEPROM(Pico.m.sram_reg>>6);
sreg = Pico.m.sram_reg; saddr = Pico.m.eeprom_addr&0x1fff; scyc = Pico.m.eeprom_cycle; ssa = Pico.m.eeprom_slave;
interval = SekCyclesDoneT()-lastSSRamWrite;
d = (sreg>>6)&1; // use SDA as "open bus"
// NBA Jam is nasty enough to read <before> raising the SCL and starting the new cycle.
// this is probably valid because data changes occur while SCL is low and data can be read
// before it's actual cycle begins.
if (!(sreg&0x80) && interval >= 24) {
elprintf(EL_EEPROM, "eeprom: early read, cycles=%i", interval);
scyc++;
}
if (!(sreg & 8)); // not started, use open bus
else if (scyc == 9 || scyc == 18 || scyc == 27) {
elprintf(EL_EEPROM, "eeprom: r ack");
d = 0;
} else if (scyc > 9 && scyc < 18) {
// started and first command word received
shift = 17-scyc;
if (SRam.eeprom_type) {
// X24C02+
if (ssa&1) {
elprintf(EL_EEPROM, "eeprom: read: addr %02x, cycle %i, reg %02x", saddr, scyc, sreg);
if (shift==0) elprintf(EL_EEPROM, "eeprom: read done, byte %02x", SRam.data[saddr]);
d = (SRam.data[saddr]>>shift)&1;
}
} else {
// X24C01
if (saddr&1) {
elprintf(EL_EEPROM, "eeprom: read: addr %02x, cycle %i, reg %02x", saddr>>1, scyc, sreg);
if (shift==0) elprintf(EL_EEPROM, "eeprom: read done, byte %02x", SRam.data[saddr>>1]);
d = (SRam.data[saddr>>1]>>shift)&1;
}
}
}
return (d << SRam.eeprom_bit_out);
}
PICO_INTERNAL void SRAMUpdPending(unsigned int a, unsigned int d)
{
unsigned int d1, sreg = Pico.m.sram_reg;
if (!((SRam.eeprom_abits^a)&1))
{
// SCL
sreg &= ~0x80;
d1 = (d >> SRam.eeprom_bit_cl) & 1;
sreg |= d1<<7;
}
if (!(((SRam.eeprom_abits>>1)^a)&1))
{
// SDA in
sreg &= ~0x40;
d1 = (d >> SRam.eeprom_bit_in) & 1;
sreg |= d1<<6;
}
Pico.m.sram_reg = (unsigned char) sreg;
}
#ifndef _ASM_MISC_C
typedef struct
{
int b0;
int b1;
int b2;
int b3;
int b4;
int b5;
int b6;
int b7;
} intblock;
PICO_INTERNAL_ASM void memcpy16(unsigned short *dest, unsigned short *src, int count)
{
if ((((int)dest | (int)src) & 3) == 0)
{
if (count >= 32) {
memcpy32((int *)dest, (int *)src, count/2);
count&=1;
} else {
for (; count >= 2; count -= 2, dest+=2, src+=2)
*(int *)dest = *(int *)src;
}
}
while (count--)
*dest++ = *src++;
}
PICO_INTERNAL_ASM void memcpy16bswap(unsigned short *dest, void *src, int count)
{
unsigned char *src_ = src;
for (; count; count--, src_ += 2)
*dest++ = (src_[0] << 8) | src_[1];
}
#ifndef _ASM_MISC_C_AMIPS
PICO_INTERNAL_ASM void memcpy32(int *dest, int *src, int count)
{
intblock *bd = (intblock *) dest, *bs = (intblock *) src;
for (; count >= sizeof(*bd)/4; count -= sizeof(*bd)/4)
*bd++ = *bs++;
dest = (int *)bd; src = (int *)bs;
while (count--)
*dest++ = *src++;
}
PICO_INTERNAL_ASM void memset32(int *dest, int c, int count)
{
for (; count >= 8; count -= 8, dest += 8)
dest[0] = dest[1] = dest[2] = dest[3] =
dest[4] = dest[5] = dest[6] = dest[7] = c;
while (count--)
*dest++ = c;
}
void memset32_uncached(int *dest, int c, int count) { memset32(dest, c, count); }
#endif
#endif

175
pico/misc_amips.s Normal file
View file

@ -0,0 +1,175 @@
# vim:filetype=mips
# Some misc routines for Allegrex MIPS
# (c) Copyright 2007, Grazvydas "notaz" Ignotas
# All Rights Reserved
.set noreorder
.set noat
.text
.align 4
.globl memset32 # int *dest, int c, int count
memset32:
ms32_aloop:
andi $t0, $a0, 0x3f
beqz $t0, ms32_bloop_prep
nop
sw $a1, 0($a0)
addiu $a2, -1
beqz $a2, ms32_return
addiu $a0, 4
j ms32_aloop
nop
ms32_bloop_prep:
srl $t0, $a2, 4 # we will do 64 bytes per iteration (cache line)
beqz $t0, ms32_bloop_end
ms32_bloop:
addiu $t0, -1
cache 0x18, ($a0) # create dirty exclusive
sw $a1, 0x00($a0)
sw $a1, 0x04($a0)
sw $a1, 0x08($a0)
sw $a1, 0x0c($a0)
sw $a1, 0x10($a0)
sw $a1, 0x14($a0)
sw $a1, 0x18($a0)
sw $a1, 0x1c($a0)
sw $a1, 0x20($a0)
sw $a1, 0x24($a0)
sw $a1, 0x28($a0)
sw $a1, 0x2c($a0)
sw $a1, 0x30($a0)
sw $a1, 0x34($a0)
sw $a1, 0x38($a0)
sw $a1, 0x3c($a0)
bnez $t0, ms32_bloop
addiu $a0, 0x40
ms32_bloop_end:
andi $a2, $a2, 0x0f
beqz $a2, ms32_return
ms32_cloop:
addiu $a2, -1
sw $a1, 0($a0)
bnez $a2, ms32_cloop
addiu $a0, 4
ms32_return:
jr $ra
nop
.globl memset32_uncached # int *dest, int c, int count
memset32_uncached:
srl $t0, $a2, 3 # we will do 32 bytes per iteration
beqz $t0, ms32u_bloop_end
ms32u_bloop:
addiu $t0, -1
sw $a1, 0x00($a0)
sw $a1, 0x04($a0)
sw $a1, 0x08($a0)
sw $a1, 0x0c($a0)
sw $a1, 0x10($a0)
sw $a1, 0x14($a0)
sw $a1, 0x18($a0)
sw $a1, 0x1c($a0)
bnez $t0, ms32u_bloop
addiu $a0, 0x20
ms32u_bloop_end:
andi $a2, $a2, 0x0f
beqz $a2, ms32u_return
ms32u_cloop:
addiu $a2, -1
sw $a1, 0($a0)
bnez $a2, ms32u_cloop
addiu $a0, 4
ms32u_return:
jr $ra
nop
.globl memcpy32 # int *dest, int *src, int count
memcpy32:
mc32_aloop:
andi $t0, $a0, 0x3f
beqz $t0, mc32_bloop_prep
nop
lw $t1, 0($a1)
addiu $a2, -1
sw $t1, 0($a0)
beqz $a2, mc32_return
addiu $a0, 4
j mc32_aloop
addiu $a1, 4
mc32_bloop_prep:
srl $t0, $a2, 4 # we will do 64 bytes per iteration (cache line)
beqz $t0, mc32_bloop_end
mc32_bloop:
addiu $t0, -1
cache 0x18, ($a0) # create dirty exclusive
lw $t2, 0x00($a1)
lw $t3, 0x04($a1)
lw $t4, 0x08($a1)
lw $t5, 0x0c($a1)
lw $t6, 0x10($a1)
lw $t7, 0x14($a1)
lw $t8, 0x18($a1)
lw $t9, 0x1c($a1)
sw $t2, 0x00($a0)
sw $t3, 0x04($a0)
sw $t4, 0x08($a0)
sw $t5, 0x0c($a0)
sw $t6, 0x10($a0)
sw $t7, 0x14($a0)
sw $t8, 0x18($a0)
sw $t9, 0x1c($a0)
lw $t2, 0x20($a1)
lw $t3, 0x24($a1)
lw $t4, 0x28($a1)
lw $t5, 0x2c($a1)
lw $t6, 0x30($a1)
lw $t7, 0x34($a1)
lw $t8, 0x38($a1)
lw $t9, 0x3c($a1)
sw $t2, 0x20($a0)
sw $t3, 0x24($a0)
sw $t4, 0x28($a0)
sw $t5, 0x2c($a0)
sw $t6, 0x30($a0)
sw $t7, 0x34($a0)
sw $t8, 0x38($a0)
sw $t9, 0x3c($a0)
addiu $a0, 0x40
bnez $t0, mc32_bloop
addiu $a1, 0x40
mc32_bloop_end:
andi $a2, $a2, 0x0f
beqz $a2, mc32_return
mc32_cloop:
lw $t1, 0($a1)
addiu $a2, -1
addiu $a1, 4
sw $t1, 0($a0)
bnez $a2, mc32_cloop
addiu $a0, 4
mc32_return:
jr $ra
nop

181
pico/misc_arm.s Normal file
View file

@ -0,0 +1,181 @@
@ vim:filetype=armasm
@ Generic memory routines.
@ (c) Copyright 2007, Grazvydas "notaz" Ignotas
.global memcpy16 @ unsigned short *dest, unsigned short *src, int count
memcpy16:
eor r3, r0, r1
tst r3, #2
bne mcp16_cant_align
tst r0, #2
ldrneh r3, [r1], #2
subne r2, r2, #1
strneh r3, [r0], #2
subs r2, r2, #4
bmi mcp16_fin
mcp16_loop:
ldmia r1!, {r3,r12}
subs r2, r2, #4
stmia r0!, {r3,r12}
bpl mcp16_loop
mcp16_fin:
tst r2, #2
ldrne r3, [r1], #4
strne r3, [r0], #4
ands r2, r2, #1
bxeq lr
mcp16_cant_align:
ldrh r3, [r1], #2
subs r2, r2, #1
strh r3, [r0], #2
bne mcp16_cant_align
bx lr
@ 0x12345678 -> 0x34127856
@ r4=temp, lr=0x00ff00ff
.macro bswap reg
and r4, \reg, lr
and \reg, lr, \reg, lsr #8
orr \reg, \reg, r4, lsl #8
.endm
@ dest must be halfword aligned, src can be unaligned
.global memcpy16bswap @ unsigned short *dest, void *src, int count
memcpy16bswap:
tst r1, #1
bne mcp16bs_cant_align2
eor r3, r0, r1
tst r3, #2
bne mcp16bs_cant_align
tst r0, #2
beq mcp16bs_aligned
ldrh r3, [r1], #2
sub r2, r2, #1
orr r3, r3, r3, lsl #16
mov r3, r3, lsr #8
strh r3, [r0], #2
mcp16bs_aligned:
stmfd sp!, {r4,lr}
mov lr, #0xff
orr lr, lr, lr, lsl #16
subs r2, r2, #4
bmi mcp16bs_fin4
mcp16bs_loop:
ldmia r1!, {r3,r12}
subs r2, r2, #4
bswap r3
bswap r12
stmia r0!, {r3,r12}
bpl mcp16bs_loop
mcp16bs_fin4:
tst r2, #2
beq mcp16bs_fin2
ldr r3, [r1], #4
bswap r3
str r3, [r0], #4
mcp16bs_fin2:
ldmfd sp!, {r4,lr}
ands r2, r2, #1
bxeq lr
mcp16bs_cant_align:
ldrh r3, [r1], #2
subs r2, r2, #1
orr r3, r3, r3, lsl #16
mov r3, r3, lsr #8
strh r3, [r0], #2
bne mcp16bs_cant_align
bx lr
@ worst case
mcp16bs_cant_align2:
ldrb r3, [r1], #1
ldrb r12,[r1], #1
subs r2, r2, #1
mov r3, r3, lsl #8
orr r3, r3, r12
strh r3, [r0], #2
bne mcp16bs_cant_align2
bx lr
.global memcpy32 @ int *dest, int *src, int count
memcpy32:
stmfd sp!, {r4,lr}
subs r2, r2, #4
bmi mcp32_fin
mcp32_loop:
ldmia r1!, {r3,r4,r12,lr}
subs r2, r2, #4
stmia r0!, {r3,r4,r12,lr}
bpl mcp32_loop
mcp32_fin:
tst r2, #3
ldmeqfd sp!, {r4,pc}
tst r2, #1
ldrne r3, [r1], #4
strne r3, [r0], #4
mcp32_no_unal1:
tst r2, #2
ldmneia r1!, {r3,r12}
ldmfd sp!, {r4,lr}
stmneia r0!, {r3,r12}
bx lr
.global memset32 @ int *dest, int c, int count
memset32:
stmfd sp!, {lr}
mov r3, r1
subs r2, r2, #4
bmi mst32_fin
mov r12,r1
mov lr, r1
mst32_loop:
subs r2, r2, #4
stmia r0!, {r1,r3,r12,lr}
bpl mst32_loop
mst32_fin:
tst r2, #1
strne r1, [r0], #4
tst r2, #2
stmneia r0!, {r1,r3}
ldmfd sp!, {lr}
bx lr

337
pico/patch.c Normal file
View file

@ -0,0 +1,337 @@
/* Decode a Game Genie code into an M68000 address/data pair.
* The Game Genie code is made of the characters
* ABCDEFGHJKLMNPRSTVWXYZ0123456789 (notice the missing I, O, Q and U).
* Where A = 00000, B = 00001, C = 00010, ... , on to 9 = 11111.
*
* These come out to a very scrambled bit pattern like this:
* (SCRA-MBLE is just an example)
*
* S C R A - M B L E
* 01111 00010 01110 00000 01011 00001 01010 00100
* ijklm nopIJ KLMNO PABCD EFGHd efgha bcQRS TUVWX
*
* Our goal is to rearrange that to this:
*
* 0000 0101 1001 1100 0100 0100 : 1011 0000 0111 1000
* ABCD EFGH IJKL MNOP QRST UVWX : abcd efgh ijkl mnop
*
* which in Hexadecimal is 059C44:B078. Simple, huh? ;)
*
* So, then, we dutifully change memory location 059C44 to B078!
* (of course, that's handled by a different source file :)
*/
//#include <stdio.h>
//#include <string.h>
#include <ctype.h>
#include "pico_int.h"
#include "patch.h"
struct patch
{
unsigned int addr;
unsigned short data;
};
struct patch_inst *PicoPatches = NULL;
int PicoPatchCount = 0;
static char genie_chars[] = "AaBbCcDdEeFfGgHhJjKkLlMmNnPpRrSsTtVvWwXxYyZz0O1I2233445566778899";
/* genie_decode
* This function converts a Game Genie code to an address:data pair.
* The code is given as an 8-character string, like "BJX0SA1C". It need not
* be null terminated, since only the first 8 characters are taken. It is
* assumed that the code is already made of valid characters, i.e. there are no
* Q's, U's, or symbols. If such a character is
* encountered, the function will return with a warning on stderr.
*
* The resulting address:data pair is returned in the struct patch pointed to
* by result. If an error results, both the address and data will be set to -1.
*/
static void genie_decode(const char* code, struct patch* result)
{
int i = 0, n;
char* x;
for(; i < 8; ++i)
{
/* If strchr returns NULL, we were given a bad character */
if(!(x = strchr(genie_chars, code[i])))
{
result->addr = -1; result->data = -1;
return;
}
n = (x - genie_chars) >> 1;
/* Now, based on which character this is, fit it into the result */
switch(i)
{
case 0:
/* ____ ____ ____ ____ ____ ____ : ____ ____ ABCD E___ */
result->data |= n << 3;
break;
case 1:
/* ____ ____ DE__ ____ ____ ____ : ____ ____ ____ _ABC */
result->data |= n >> 2;
result->addr |= (n & 3) << 14;
break;
case 2:
/* ____ ____ __AB CDE_ ____ ____ : ____ ____ ____ ____ */
result->addr |= n << 9;
break;
case 3:
/* BCDE ____ ____ ___A ____ ____ : ____ ____ ____ ____ */
result->addr |= (n & 0xF) << 20 | (n >> 4) << 8;
break;
case 4:
/* ____ ABCD ____ ____ ____ ____ : ___E ____ ____ ____ */
result->data |= (n & 1) << 12;
result->addr |= (n >> 1) << 16;
break;
case 5:
/* ____ ____ ____ ____ ____ ____ : E___ ABCD ____ ____ */
result->data |= (n & 1) << 15 | (n >> 1) << 8;
break;
case 6:
/* ____ ____ ____ ____ CDE_ ____ : _AB_ ____ ____ ____ */
result->data |= (n >> 3) << 13;
result->addr |= (n & 7) << 5;
break;
case 7:
/* ____ ____ ____ ____ ___A BCDE : ____ ____ ____ ____ */
result->addr |= n;
break;
}
/* Go around again */
}
return;
}
/* "Decode" an address/data pair into a structure. This is for "012345:ABCD"
* type codes. You're more likely to find Genie codes circulating around, but
* there's a chance you could come on to one of these. Which is nice, since
* they're MUCH easier to implement ;) Once again, the input should be depunc-
* tuated already. */
static char hex_chars[] = "00112233445566778899AaBbCcDdEeFf";
static void hex_decode(const char *code, struct patch *result)
{
char *x;
int i;
/* 6 digits for address */
for(i = 0; i < 6; ++i)
{
if(!(x = strchr(hex_chars, code[i])))
{
result->addr = result->data = -1;
return;
}
result->addr = (result->addr << 4) | ((x - hex_chars) >> 1);
}
/* 4 digits for data */
for(i = 6; i < 10; ++i)
{
if(!(x = strchr(hex_chars, code[i])))
{
result->addr = result->data = -1;
return;
}
result->data = (result->data << 4) | ((x - hex_chars) >> 1);
}
}
/* THIS is the function you call from the MegaDrive or whatever. This figures
* out whether it's a genie or hex code, depunctuates it, and calls the proper
* decoder. */
static void decode(const char* code, struct patch* result)
{
int len = strlen(code), i, j;
char code_to_pass[16], *x;
const char *ad, *da;
int adl, dal;
/* Initialize the result */
result->addr = result->data = 0;
/* Just assume 8 char long string to be Game Genie code */
if (len == 8)
{
genie_decode(code, result);
return;
}
/* If it's 9 chars long and the 5th is a hyphen, we have a Game Genie
* code. */
if(len == 9 && code[4] == '-')
{
/* Remove the hyphen and pass to genie_decode */
code_to_pass[0] = code[0];
code_to_pass[1] = code[1];
code_to_pass[2] = code[2];
code_to_pass[3] = code[3];
code_to_pass[4] = code[5];
code_to_pass[5] = code[6];
code_to_pass[6] = code[7];
code_to_pass[7] = code[8];
code_to_pass[8] = '\0';
genie_decode(code_to_pass, result);
return;
}
/* Otherwise, we assume it's a hex code.
* Find the colon so we know where address ends and data starts. If there's
* no colon, then we haven't a code at all! */
if(!(x = strchr(code, ':'))) goto bad_code;
ad = code; da = x + 1; adl = x - code; dal = len - adl - 1;
/* If a section is empty or too long, toss it */
if(adl == 0 || adl > 6 || dal == 0 || dal > 4) goto bad_code;
/* Pad the address with zeros, then fill it with the value */
for(i = 0; i < (6 - adl); ++i) code_to_pass[i] = '0';
for(j = 0; i < 6; ++i, ++j) code_to_pass[i] = ad[j];
/* Do the same for data */
for(i = 6; i < (10 - dal); ++i) code_to_pass[i] = '0';
for(j = 0; i < 10; ++i, ++j) code_to_pass[i] = da[j];
code_to_pass[10] = '\0';
/* Decode and goodbye */
hex_decode(code_to_pass, result);
return;
bad_code:
/* AGH! Invalid code! */
result->data = result->addr = -1;
return;
}
unsigned int PicoRead16(unsigned int a);
void PicoWrite16(unsigned int a, unsigned short d);
void PicoPatchUnload(void)
{
if (PicoPatches != NULL)
{
free(PicoPatches);
PicoPatches = NULL;
}
PicoPatchCount = 0;
}
int PicoPatchLoad(const char *fname)
{
FILE *f;
char buff[256];
struct patch pt;
int array_len = 0;
PicoPatchUnload();
f = fopen(fname, "r");
if (f == NULL)
{
return -1;
}
while (fgets(buff, sizeof(buff), f))
{
int llen, clen;
llen = strlen(buff);
for (clen = 0; clen < llen; clen++)
if (isspace(buff[clen])) break;
buff[clen] = 0;
if (clen > 11 || clen < 8)
continue;
decode(buff, &pt);
if (pt.addr == (unsigned int)-1 || pt.data == (unsigned short)-1)
continue;
/* code was good, add it */
if (array_len < PicoPatchCount + 1)
{
void *ptr;
array_len *= 2;
array_len++;
ptr = realloc(PicoPatches, array_len * sizeof(PicoPatches[0]));
if (ptr == NULL) break;
PicoPatches = ptr;
}
strcpy(PicoPatches[PicoPatchCount].code, buff);
/* strip */
for (clen++; clen < llen; clen++)
if (!isspace(buff[clen])) break;
for (llen--; llen > 0; llen--)
if (!isspace(buff[llen])) break;
buff[llen+1] = 0;
strncpy(PicoPatches[PicoPatchCount].name, buff + clen, 51);
PicoPatches[PicoPatchCount].name[51] = 0;
PicoPatches[PicoPatchCount].active = 0;
PicoPatches[PicoPatchCount].addr = pt.addr;
PicoPatches[PicoPatchCount].data = pt.data;
PicoPatches[PicoPatchCount].data_old = 0;
PicoPatchCount++;
// fprintf(stderr, "loaded patch #%i: %06x:%04x \"%s\"\n", PicoPatchCount-1, pt.addr, pt.data,
// PicoPatches[PicoPatchCount-1].name);
}
fclose(f);
return 0;
}
/* to be called when the Rom is loaded and byteswapped */
void PicoPatchPrepare(void)
{
int i;
for (i = 0; i < PicoPatchCount; i++)
{
PicoPatches[i].addr &= ~1;
PicoPatches[i].data_old = PicoRead16(PicoPatches[i].addr);
if (strstr(PicoPatches[i].name, "AUTO"))
PicoPatches[i].active = 1;
}
}
void PicoPatchApply(void)
{
int i, u;
unsigned int addr;
for (i = 0; i < PicoPatchCount; i++)
{
addr = PicoPatches[i].addr;
if (addr < Pico.romsize)
{
if (PicoPatches[i].active)
*(unsigned short *)(Pico.rom + addr) = PicoPatches[i].data;
else {
// if current addr is not patched by older patch, write back original val
for (u = 0; u < i; u++)
if (PicoPatches[u].addr == addr) break;
if (u == i)
*(unsigned short *)(Pico.rom + addr) = PicoPatches[i].data_old;
}
// fprintf(stderr, "patched %i: %06x:%04x\n", PicoPatches[i].active, addr,
// *(unsigned short *)(Pico.rom + addr));
}
else
{
/* RAM or some other weird patch */
if (PicoPatches[i].active)
PicoWrite16(addr, PicoPatches[i].data);
}
}
}

31
pico/patch.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef _GENIE_DECODE_H__
#define _GENIE_DECODE_H__
#ifdef __cplusplus
extern "C" {
#endif
struct patch_inst
{
char code[12];
char name[52];
unsigned int active;
unsigned int addr;
unsigned short data;
unsigned short data_old;
};
extern struct patch_inst *PicoPatches;
extern int PicoPatchCount;
int PicoPatchLoad(const char *fname);
void PicoPatchUnload(void);
void PicoPatchPrepare(void);
void PicoPatchApply(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _GENIE_DECODE_H__

349
pico/pico.c Normal file
View file

@ -0,0 +1,349 @@
// PicoDrive
// (c) Copyright 2004 Dave, All rights reserved.
// (c) Copyright 2006-2008 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include "pico_int.h"
#include "sound/ym2612.h"
int PicoVer=0x0133;
struct Pico Pico;
int PicoOpt = 0;
int PicoSkipFrame = 0; // skip rendering frame?
int emustatus = 0; // rapid_ym2612, multi_ym_updates
int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU
int PicoPadInt[2]; // internal copy
int PicoAHW = 0; // active addon hardware: scd_active, 32x_active, svp_active, pico_active
int PicoRegionOverride = 0; // override the region detection 0: Auto, 1: Japan NTSC, 2: Japan PAL, 4: US, 8: Europe
int PicoAutoRgnOrder = 0;
struct PicoSRAM SRam = {0,};
void (*PicoWriteSound)(int len) = NULL; // called at the best time to send sound buffer (PsndOut) to hardware
void (*PicoResetHook)(void) = NULL;
void (*PicoLineHook)(void) = NULL;
// to be called once on emu init
void PicoInit(void)
{
// Blank space for state:
memset(&Pico,0,sizeof(Pico));
memset(&PicoPad,0,sizeof(PicoPad));
memset(&PicoPadInt,0,sizeof(PicoPadInt));
// Init CPUs:
SekInit();
z80_init(); // init even if we aren't going to use it
PicoInitMCD();
PicoSVPInit();
SRam.data=0;
}
// to be called once on emu exit
void PicoExit(void)
{
if (PicoAHW & PAHW_MCD)
PicoExitMCD();
PicoCartUnload();
z80_exit();
if (SRam.data) free(SRam.data); SRam.data=0;
}
void PicoPower(void)
{
unsigned char sram_reg=Pico.m.sram_reg; // must be preserved
Pico.m.frame_count = 0;
// clear all memory of the emulated machine
memset(&Pico.ram,0,(unsigned int)&Pico.rom-(unsigned int)&Pico.ram);
memset(&Pico.video,0,sizeof(Pico.video));
memset(&Pico.m,0,sizeof(Pico.m));
Pico.video.pending_ints=0;
z80_reset();
// default VDP register values (based on Fusion)
Pico.video.reg[0] = Pico.video.reg[1] = 0x04;
Pico.video.reg[0xc] = 0x81;
Pico.video.reg[0xf] = 0x02;
if (PicoAHW & PAHW_MCD)
PicoPowerMCD();
Pico.m.sram_reg=sram_reg;
PicoReset();
}
PICO_INTERNAL void PicoDetectRegion(void)
{
int support=0, hw=0, i;
unsigned char pal=0;
if (PicoRegionOverride)
{
support = PicoRegionOverride;
}
else
{
// Read cartridge region data:
int region=PicoRead32(0x1f0);
for (i=0;i<4;i++)
{
int c=0;
c=region>>(i<<3); c&=0xff;
if (c<=' ') continue;
if (c=='J') support|=1;
else if (c=='U') support|=4;
else if (c=='E') support|=8;
else if (c=='j') {support|=1; break; }
else if (c=='u') {support|=4; break; }
else if (c=='e') {support|=8; break; }
else
{
// New style code:
char s[2]={0,0};
s[0]=(char)c;
support|=strtol(s,NULL,16);
}
}
}
// auto detection order override
if (PicoAutoRgnOrder) {
if (((PicoAutoRgnOrder>>0)&0xf) & support) support = (PicoAutoRgnOrder>>0)&0xf;
else if (((PicoAutoRgnOrder>>4)&0xf) & support) support = (PicoAutoRgnOrder>>4)&0xf;
else if (((PicoAutoRgnOrder>>8)&0xf) & support) support = (PicoAutoRgnOrder>>8)&0xf;
}
// Try to pick the best hardware value for English/50hz:
if (support&8) { hw=0xc0; pal=1; } // Europe
else if (support&4) hw=0x80; // USA
else if (support&2) { hw=0x40; pal=1; } // Japan PAL
else if (support&1) hw=0x00; // Japan NTSC
else hw=0x80; // USA
Pico.m.hardware=(unsigned char)(hw|0x20); // No disk attached
Pico.m.pal=pal;
}
int PicoReset(void)
{
unsigned char sram_reg=Pico.m.sram_reg; // must be preserved
if (Pico.romsize<=0) return 1;
/* must call now, so that banking is reset, and correct vectors get fetched */
if (PicoResetHook) PicoResetHook();
PicoMemReset();
SekReset();
memset(&PicoPadInt,0,sizeof(PicoPadInt));
// s68k doesn't have the TAS quirk, so we just globally set normal TAS handler in MCD mode (used by Batman games).
SekSetRealTAS(PicoAHW & PAHW_MCD);
SekCycleCntT=0;
if (PicoAHW & PAHW_MCD)
// needed for MCD to reset properly, probably some bug hides behind this..
memset(Pico.ioports,0,sizeof(Pico.ioports));
emustatus = 0;
Pico.m.dirtyPal = 1;
Pico.m.z80_bank68k = 0;
memset(Pico.zram, 0, sizeof(Pico.zram)); // ??
PicoDetectRegion();
Pico.video.status = 0x3428 | Pico.m.pal; // 'always set' bits | vblank | collision | pal
PsndReset(); // pal must be known here
// create an empty "dma" to cause 68k exec start at random frame location
if (Pico.m.dma_xfers == 0 && !(PicoOpt&POPT_DIS_VDP_FIFO))
Pico.m.dma_xfers = rand() & 0x1fff;
SekFinishIdleDet();
if (PicoAHW & PAHW_MCD) {
PicoResetMCD();
return 0;
}
// reinit, so that checksum checks pass
if (!(PicoOpt & POPT_DIS_IDLE_DET))
SekInitIdleDet();
// reset sram state; enable sram access by default if it doesn't overlap with ROM
Pico.m.sram_reg=sram_reg&0x14;
if (!(Pico.m.sram_reg&4) && Pico.romsize <= SRam.start) Pico.m.sram_reg |= 1;
elprintf(EL_STATUS, "sram: det: %i; eeprom: %i; start: %06x; end: %06x",
(Pico.m.sram_reg>>4)&1, (Pico.m.sram_reg>>2)&1, SRam.start, SRam.end);
return 0;
}
// dma2vram settings are just hacks to unglitch Legend of Galahad (needs <= 104 to work)
// same for Outrunners (92-121, when active is set to 24)
// 96 is VR hack
static const int dma_timings[] = {
96, 167, 166, 83, // vblank: 32cell: dma2vram dma2[vs|c]ram vram_fill vram_copy
102, 205, 204, 102, // vblank: 40cell:
16, 16, 15, 8, // active: 32cell:
24, 18, 17, 9 // ...
};
static const int dma_bsycles[] = {
(488<<8)/96, (488<<8)/167, (488<<8)/166, (488<<8)/83,
(488<<8)/102, (488<<8)/205, (488<<8)/204, (488<<8)/102,
(488<<8)/16, (488<<8)/16, (488<<8)/15, (488<<8)/8,
(488<<8)/24, (488<<8)/18, (488<<8)/17, (488<<8)/9
};
PICO_INTERNAL int CheckDMA(void)
{
int burn = 0, xfers_can, dma_op = Pico.video.reg[0x17]>>6; // see gens for 00 and 01 modes
int xfers = Pico.m.dma_xfers;
int dma_op1;
if(!(dma_op&2)) dma_op = (Pico.video.type==1) ? 0 : 1; // setting dma_timings offset here according to Gens
dma_op1 = dma_op;
if(Pico.video.reg[12] & 1) dma_op |= 4; // 40 cell mode?
if(!(Pico.video.status&8)&&(Pico.video.reg[1]&0x40)) dma_op|=8; // active display?
xfers_can = dma_timings[dma_op];
if(xfers <= xfers_can)
{
if(dma_op&2) Pico.video.status&=~2; // dma no longer busy
else {
burn = xfers * dma_bsycles[dma_op] >> 8; // have to be approximate because can't afford division..
}
Pico.m.dma_xfers = 0;
} else {
if(!(dma_op&2)) burn = 488;
Pico.m.dma_xfers -= xfers_can;
}
elprintf(EL_VDPDMA, "~Dma %i op=%i can=%i burn=%i [%i]", Pico.m.dma_xfers, dma_op1, xfers_can, burn, SekCyclesDone());
//dprintf("~aim: %i, cnt: %i", SekCycleAim, SekCycleCnt);
return burn;
}
static __inline void SekRunM68k(int cyc)
{
int cyc_do;
SekCycleAim+=cyc;
if ((cyc_do=SekCycleAim-SekCycleCnt) <= 0) return;
#if defined(EMU_CORE_DEBUG)
// this means we do run-compare
SekCycleCnt+=CM_compareRun(cyc_do, 0);
#elif defined(EMU_C68K)
PicoCpuCM68k.cycles=cyc_do;
CycloneRun(&PicoCpuCM68k);
SekCycleCnt+=cyc_do-PicoCpuCM68k.cycles;
#elif defined(EMU_M68K)
SekCycleCnt+=m68k_execute(cyc_do);
#elif defined(EMU_F68K)
SekCycleCnt+=fm68k_emulate(cyc_do+1, 0, 0);
#endif
}
// to be called on 224 or line_sample scanlines only
static __inline void getSamples(int y)
{
#if SIMPLE_WRITE_SOUND
if (y != 224) return;
PsndRender(0, PsndLen);
if (PicoWriteSound) PicoWriteSound(PsndLen);
PsndClear();
#else
static int curr_pos = 0;
if(y == 224) {
if(emustatus & 2)
curr_pos += PsndRender(curr_pos, PsndLen-PsndLen/2);
else curr_pos = PsndRender(0, PsndLen);
if (emustatus&1) emustatus|=2; else emustatus&=~2;
if (PicoWriteSound) PicoWriteSound(curr_pos);
// clear sound buffer
PsndClear();
}
else if(emustatus & 3) {
emustatus|= 2;
emustatus&=~1;
curr_pos = PsndRender(0, PsndLen/2);
}
#endif
}
#include "pico_cmn.c"
int z80stopCycle;
int z80_cycle_cnt; /* 'done' z80 cycles before z80_run() */
int z80_cycle_aim;
int z80_scanline;
int z80_scanline_cycles; /* cycles done until z80_scanline */
/* sync z80 to 68k */
PICO_INTERNAL void PicoSyncZ80(int m68k_cycles_done)
{
int cnt;
z80_cycle_aim = cycles_68k_to_z80(m68k_cycles_done);
cnt = z80_cycle_aim - z80_cycle_cnt;
elprintf(EL_BUSREQ, "z80 sync %i (%i|%i -> %i|%i)", cnt, z80_cycle_cnt, z80_cycle_cnt / 228,
z80_cycle_aim, z80_cycle_aim / 228);
if (cnt > 0)
z80_cycle_cnt += z80_run(cnt);
}
void PicoFrame(void)
{
Pico.m.frame_count++;
if (PicoAHW & PAHW_MCD) {
PicoFrameMCD();
return;
}
//if(Pico.video.reg[12]&0x2) Pico.video.status ^= 0x10; // change odd bit in interlace mode
if (!(PicoOpt&POPT_ALT_RENDERER))
PicoFrameStart();
PicoFrameHints();
}
void PicoFrameDrawOnly(void)
{
PicoFrameStart();
PicoDrawSync(223, 0);
}
void PicoGetInternal(pint_t which, pint_ret_t *r)
{
switch (which)
{
case PI_ROM: r->vptr = Pico.rom; break;
case PI_ISPAL: r->vint = Pico.m.pal; break;
case PI_IS40_CELL: r->vint = Pico.video.reg[12]&1; break;
case PI_IS240_LINES: r->vint = Pico.m.pal && (Pico.video.reg[1]&8); break;
}
}
// callback to output message from emu
void (*PicoMessage)(const char *msg)=NULL;

207
pico/pico.h Normal file
View file

@ -0,0 +1,207 @@
// -------------------- Pico Library --------------------
// Pico Library - Header File
// (c) Copyright 2004 Dave, All rights reserved.
// (c) Copyright 2006-2008 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#ifndef PICO_H
#define PICO_H
#include <stdio.h>
// port-specific compile-time settings
#include <port_config.h>
#ifdef __cplusplus
extern "C" {
#endif
// external funcs for Sega/Mega CD
extern int mp3_get_bitrate(FILE *f, int size);
extern void mp3_start_play(FILE *f, int pos);
extern void mp3_update(int *buffer, int length, int stereo);
// this function should write-back d-cache and invalidate i-cache
// on a mem region [start_addr, end_addr)
// used by SVP dynarec
extern void cache_flush_d_inval_i(const void *start_addr, const void *end_addr);
// Pico.c
#define POPT_EN_FM (1<< 0) // 00 000x
#define POPT_EN_PSG (1<< 1)
#define POPT_EN_Z80 (1<< 2)
#define POPT_EN_STEREO (1<< 3)
#define POPT_ALT_RENDERER (1<< 4) // 00 00x0
#define POPT_6BTN_PAD (1<< 5)
// unused (1<< 6)
#define POPT_ACC_SPRITES (1<< 7)
#define POPT_DIS_32C_BORDER (1<< 8) // 00 0x00
#define POPT_EXT_FM (1<< 9)
#define POPT_EN_MCD_PCM (1<<10)
#define POPT_EN_MCD_CDDA (1<<11)
#define POPT_EN_MCD_GFX (1<<12) // 00 x000
#define POPT_EN_MCD_PSYNC (1<<13)
#define POPT_EN_SOFTSCALE (1<<14)
#define POPT_EN_MCD_RAMCART (1<<15)
#define POPT_DIS_VDP_FIFO (1<<16) // 0x 0000
#define POPT_EN_SVP_DRC (1<<17)
#define POPT_DIS_SPRITE_LIM (1<<18)
#define POPT_DIS_IDLE_DET (1<<19)
extern int PicoOpt; // bitfield
#define PAHW_MCD (1<<0)
#define PAHW_32X (1<<1)
#define PAHW_SVP (1<<2)
#define PAHW_PICO (1<<3)
extern int PicoAHW; // Pico active hw
extern int PicoVer;
extern int PicoSkipFrame; // skip rendering frame, but still do sound (if enabled) and emulation stuff
extern int PicoRegionOverride; // override the region detection 0: auto, 1: Japan NTSC, 2: Japan PAL, 4: US, 8: Europe
extern int PicoAutoRgnOrder; // packed priority list of regions, for example 0x148 means this detection order: EUR, USA, JAP
extern int PicoSVPCycles;
void PicoInit(void);
void PicoExit(void);
void PicoPower(void);
int PicoReset(void);
void PicoFrame(void);
void PicoFrameDrawOnly(void);
extern int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU
extern void (*PicoWriteSound)(int len); // called once per frame at the best time to send sound buffer (PsndOut) to hardware
extern void (*PicoMessage)(const char *msg); // callback to output text message from emu
typedef enum { PI_ROM, PI_ISPAL, PI_IS40_CELL, PI_IS240_LINES } pint_t;
typedef union { int vint; void *vptr; } pint_ret_t;
void PicoGetInternal(pint_t which, pint_ret_t *ret);
// cd/Pico.c
extern void (*PicoMCDopenTray)(void);
extern int (*PicoMCDcloseTray)(void);
extern int PicoCDBuffers;
// Pico/Pico.c
#define XPCM_BUFFER_SIZE (320+160)
typedef struct
{
int pen_pos[2];
int page;
// internal
int fifo_bytes; // bytes in FIFO
int fifo_bytes_prev;
int fifo_line_bytes; // float part, << 16
int line_counter;
unsigned short r1, r12;
unsigned char xpcm_buffer[XPCM_BUFFER_SIZE+4];
unsigned char *xpcm_ptr;
} picohw_state;
extern picohw_state PicoPicohw;
// Area.c
typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file);
typedef size_t (areaeof)(void *file);
typedef int (areaseek)(void *file, long offset, int whence);
typedef int (areaclose)(void *file);
// Save or load the state from PmovFile:
int PmovState(int PmovAction, void *PmovFile); // &1=for reading &2=for writing &4=volatile &8=non-volatile
extern arearw *areaRead; // external read and write function pointers for
extern arearw *areaWrite; // gzip save state ability
extern areaeof *areaEof;
extern areaseek *areaSeek;
extern areaclose *areaClose;
extern void (*PicoStateProgressCB)(const char *str);
// cd/Area.c
int PicoCdLoadStateGfx(void *file);
// cd/buffering.c
void PicoCDBufferInit(void);
void PicoCDBufferFree(void);
void PicoCDBufferFlush(void);
// cd/cd_sys.c
int Insert_CD(char *cdimg_name, int type);
void Stop_CD(void); // releases all resources taken when CD game was started.
// Cart.c
typedef enum
{
PMT_UNCOMPRESSED = 0,
PMT_ZIP,
PMT_CSO
} pm_type;
typedef struct
{
void *file; /* file handle */
void *param; /* additional file related field */
unsigned int size; /* size */
pm_type type;
} pm_file;
pm_file *pm_open(const char *path);
size_t pm_read(void *ptr, size_t bytes, pm_file *stream);
int pm_seek(pm_file *stream, long offset, int whence);
int pm_close(pm_file *fp);
int PicoCartLoad(pm_file *f,unsigned char **prom,unsigned int *psize);
int PicoCartInsert(unsigned char *rom,unsigned int romsize);
void Byteswap(unsigned char *data,int len);
void PicoCartUnload(void);
extern void (*PicoCartLoadProgressCB)(int percent);
extern void (*PicoCDLoadProgressCB)(int percent);
// Draw.c
void PicoDrawSetColorFormat(int which); // 0=BGR444, 1=RGB555, 2=8bit(HighPal pal)
extern void *DrawLineDest;
#if OVERRIDE_HIGHCOL
extern unsigned char *HighCol;
#else
extern unsigned char HighCol[8+320+8];
#endif
extern int (*PicoScanBegin)(unsigned int num);
extern int (*PicoScanEnd)(unsigned int num);
// utility
#ifdef _ASM_DRAW_C
void vidConvCpyRGB565(void *to, void *from, int pixels);
#endif
void PicoDoHighPal555(int sh);
extern int PicoDrawMask;
#define PDRAW_LAYERB_ON (1<<2)
#define PDRAW_LAYERA_ON (1<<3)
#define PDRAW_SPRITES_LOW_ON (1<<4)
#define PDRAW_SPRITES_HI_ON (1<<7)
// internals
#define PDRAW_SPRITES_MOVED (1<<0) // (asm)
#define PDRAW_WND_DIFF_PRIO (1<<1) // not all window tiles use same priority
#define PDRAW_SPR_LO_ON_HI (1<<2) // seen sprites without layer pri bit ontop spr. with that bit
#define PDRAW_INTERLACE (1<<3)
#define PDRAW_DIRTY_SPRITES (1<<4) // (asm)
#define PDRAW_SONIC_MODE (1<<5) // mid-frame palette changes for 8bit renderer
#define PDRAW_PLANE_HI_PRIO (1<<6) // have layer with all hi prio tiles (mk3)
#define PDRAW_SHHI_DONE (1<<7) // layer sh/hi already processed
extern int rendstatus;
extern unsigned short HighPal[0x100];
// Draw2.c
// stuff below is optional
extern unsigned char *PicoDraw2FB; // buffer for fast renderer in format (8+320)x(8+224+8) (eights for borders)
extern unsigned short *PicoCramHigh; // pointer to CRAM buff (0x40 shorts), converted to native device color (works only with 16bit for now)
extern void (*PicoPrepareCram)(); // prepares PicoCramHigh for renderer to use
// sound.c
extern int PsndRate,PsndLen;
extern short *PsndOut;
extern void (*PsndMix_32_to_16l)(short *dest, int *src, int count);
void PsndRerate(int preserve_state);
// Utils.c
extern int PicuAnd;
int PicuQuick(unsigned short *dest,unsigned short *src);
int PicuShrink(unsigned short *dest,int destLen,unsigned short *src,int srcLen);
int PicuShrinkReverse(unsigned short *dest,int destLen,unsigned short *src,int srcLen);
int PicuMerge(unsigned short *dest,int destLen,unsigned short *src,int srcLen);
#ifdef __cplusplus
} // End of extern "C"
#endif
#endif // PICO_H

275
pico/pico/memory.c Normal file
View file

@ -0,0 +1,275 @@
#include "../pico_int.h"
#include "../sound/sn76496.h"
#ifndef UTYPES_DEFINED
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define UTYPES_DEFINED
#endif
// -----------------------------------------------------------------
// Read Rom and read Ram
static u32 PicoReadPico8(u32 a)
{
u32 d=0;
if ((a&0xe00000)==0xe00000) { d = *(u8 *)(Pico.ram+((a^1)&0xffff)); goto end; } // Ram
if (a<Pico.romsize) { d = *(u8 *)(Pico.rom+(a^1)); goto end; } // Rom
a&=0xffffff;
if ((a&0xfffff0)==0xc00000) { // VDP
d=PicoVideoRead8(a);
goto end;
}
if ((a&0xffffe0)==0x800000) // Pico I/O
{
switch (a & 0x1f)
{
case 0x01: d = PicoPicohw.r1; break;
case 0x03:
d = PicoPad[0]&0x1f; // d-pad
d |= (PicoPad[0]&0x20) << 2; // pen push -> C
d = ~d;
break;
case 0x05: d = (PicoPicohw.pen_pos[0] >> 8); break; // what is MS bit for? Games read it..
case 0x07: d = PicoPicohw.pen_pos[0] & 0xff; break;
case 0x09: d = (PicoPicohw.pen_pos[1] >> 8); break;
case 0x0b: d = PicoPicohw.pen_pos[1] & 0xff; break;
case 0x0d: d = (1 << (PicoPicohw.page & 7)) - 1; break;
case 0x12: d = PicoPicohw.fifo_bytes == 0 ? 0x80 : 0; break; // guess
default:
elprintf(EL_UIO, "r8 : %06x, %02x @%06x", a&0xffffff, (u8)d, SekPc);
break;
}
}
//elprintf(EL_UIO, "r8 : %06x, %02x @%06x", a&0xffffff, (u8)d, SekPc);
end:
elprintf(EL_IO, "r8 : %06x, %02x @%06x", a&0xffffff, (u8)d, SekPc);
return d;
}
static u32 PicoReadPico16(u32 a)
{
u32 d=0;
if ((a&0xe00000)==0xe00000) { d=*(u16 *)(Pico.ram+(a&0xfffe)); goto end; } // Ram
a&=0xfffffe;
if (a<Pico.romsize) { d = *(u16 *)(Pico.rom+a); goto end; } // Rom
if ((a&0xfffff0)==0xc00000) {
d = PicoVideoRead(a);
goto end;
}
if (a == 0x800010)
d = (PicoPicohw.fifo_bytes > 0x3f) ? 0 : (0x3f - PicoPicohw.fifo_bytes);
else if (a == 0x800012)
d = PicoPicohw.fifo_bytes == 0 ? 0x8000 : 0; // guess
else
elprintf(EL_UIO, "r16: %06x, %04x @%06x", a&0xffffff, d, SekPc);
//elprintf(EL_UIO, "r16: %06x, %04x @%06x", a&0xffffff, d, SekPc);
end:
elprintf(EL_IO, "r16: %06x, %04x @%06x", a&0xffffff, d, SekPc);
return d;
}
static u32 PicoReadPico32(u32 a)
{
u32 d=0;
if ((a&0xe00000)==0xe00000) { u16 *pm=(u16 *)(Pico.ram+(a&0xfffe)); d = (pm[0]<<16)|pm[1]; goto end; } // Ram
a&=0xfffffe;
if (a<Pico.romsize) { u16 *pm=(u16 *)(Pico.rom+a); d = (pm[0]<<16)|pm[1]; goto end; } // Rom
if ((a&0xfffff0)==0xc00000) {
d = (PicoVideoRead(a)<<16)|PicoVideoRead(a+2);
goto end;
}
elprintf(EL_UIO, "r32: %06x, %08x @%06x", a&0xffffff, d, SekPc);
end:
elprintf(EL_IO, "r32: %06x, %08x @%06x", a&0xffffff, d, SekPc);
return d;
}
// -----------------------------------------------------------------
// Write Ram
/*
void dump(u16 w)
{
static FILE *f[0x10] = { NULL, };
char fname[32];
int num = PicoPicohw.r12 & 0xf;
w = (w << 8) | (w >> 8);
sprintf(fname, "ldump%i.bin", num);
if (f[num] == NULL)
f[num] = fopen(fname, "wb");
fwrite(&w, 1, 2, f[num]);
//fclose(f);
}
*/
static void PicoWritePico8(u32 a,u8 d)
{
elprintf(EL_IO, "w8 : %06x, %02x @%06x", a&0xffffff, d, SekPc);
if ((a&0xe00000)==0xe00000) { *(u8 *)(Pico.ram+((a^1)&0xffff))=d; return; } // Ram
a&=0xffffff;
if ((a&0xfffff9)==0xc00011) { if (PicoOpt&2) SN76496Write(d); return; } // PSG Sound
if ((a&0xfffff0)==0xc00000) { // VDP
d&=0xff;
PicoVideoWrite(a,(u16)(d|(d<<8))); // Byte access gets mirrored
return;
}
switch (a & 0x1f) {
case 0x19: case 0x1b: case 0x1d: case 0x1f: break; // 'S' 'E' 'G' 'A'
default:
elprintf(EL_UIO, "w8 : %06x, %02x @%06x", a&0xffffff, d, SekPc);
break;
}
//elprintf(EL_UIO, "w8 : %06x, %02x @%06x", a&0xffffff, d, SekPc);
}
static void PicoWritePico16(u32 a,u16 d)
{
elprintf(EL_IO, "w16: %06x, %04x", a&0xffffff, d);
if ((a&0xe00000)==0xe00000) { *(u16 *)(Pico.ram+(a&0xfffe))=d; return; } // Ram
a&=0xfffffe;
if ((a&0xfffff0)==0xc00000) { PicoVideoWrite(a,(u16)d); return; } // VDP
//if (a == 0x800010) dump(d);
if (a == 0x800010)
{
PicoPicohw.fifo_bytes += 2;
if (PicoPicohw.xpcm_ptr < PicoPicohw.xpcm_buffer + XPCM_BUFFER_SIZE) {
*PicoPicohw.xpcm_ptr++ = d >> 8;
*PicoPicohw.xpcm_ptr++ = d;
}
else if (PicoPicohw.xpcm_ptr == PicoPicohw.xpcm_buffer + XPCM_BUFFER_SIZE) {
elprintf(EL_ANOMALY|EL_PICOHW, "xpcm_buffer overflow!");
PicoPicohw.xpcm_ptr++;
}
}
else if (a == 0x800012) {
int r12_old = PicoPicohw.r12;
PicoPicohw.r12 = d;
if (r12_old != d)
PicoReratePico();
}
else
elprintf(EL_UIO, "w16: %06x, %04x", a&0xffffff, d);
//elprintf(EL_UIO, "w16: %06x, %04x", a&0xffffff, d);
}
static void PicoWritePico32(u32 a,u32 d)
{
elprintf(EL_IO, "w32: %06x, %08x", a&0xffffff, d);
if ((a&0xe00000)==0xe00000)
{
// Ram:
u16 *pm=(u16 *)(Pico.ram+(a&0xfffe));
pm[0]=(u16)(d>>16); pm[1]=(u16)d;
return;
}
a&=0xfffffe;
if ((a&0xfffff0)==0xc00000)
{
// VDP:
PicoVideoWrite(a, (u16)(d>>16));
PicoVideoWrite(a+2,(u16)d);
return;
}
elprintf(EL_UIO, "w32: %06x, %08x", a&0xffffff, d);
}
#ifdef EMU_M68K
extern unsigned int (*pm68k_read_memory_8) (unsigned int address);
extern unsigned int (*pm68k_read_memory_16)(unsigned int address);
extern unsigned int (*pm68k_read_memory_32)(unsigned int address);
extern void (*pm68k_write_memory_8) (unsigned int address, unsigned char value);
extern void (*pm68k_write_memory_16)(unsigned int address, unsigned short value);
extern void (*pm68k_write_memory_32)(unsigned int address, unsigned int value);
extern unsigned int (*pm68k_read_memory_pcr_8) (unsigned int address);
extern unsigned int (*pm68k_read_memory_pcr_16)(unsigned int address);
extern unsigned int (*pm68k_read_memory_pcr_32)(unsigned int address);
static unsigned int m68k_read_memory_pcrp_8(unsigned int a)
{
if((a&0xe00000)==0xe00000) return *(u8 *)(Pico.ram+((a^1)&0xffff)); // Ram
return 0;
}
static unsigned int m68k_read_memory_pcrp_16(unsigned int a)
{
if((a&0xe00000)==0xe00000) return *(u16 *)(Pico.ram+(a&0xfffe)); // Ram
return 0;
}
static unsigned int m68k_read_memory_pcrp_32(unsigned int a)
{
if((a&0xe00000)==0xe00000) { u16 *pm=(u16 *)(Pico.ram+(a&0xfffe)); return (pm[0]<<16)|pm[1]; } // Ram
return 0;
}
#endif // EMU_M68K
PICO_INTERNAL void PicoMemSetupPico(void)
{
#ifdef EMU_C68K
PicoCpuCM68k.checkpc=PicoCheckPc;
PicoCpuCM68k.fetch8 =PicoCpuCM68k.read8 =PicoReadPico8;
PicoCpuCM68k.fetch16=PicoCpuCM68k.read16=PicoReadPico16;
PicoCpuCM68k.fetch32=PicoCpuCM68k.read32=PicoReadPico32;
PicoCpuCM68k.write8 =PicoWritePico8;
PicoCpuCM68k.write16=PicoWritePico16;
PicoCpuCM68k.write32=PicoWritePico32;
#endif
#ifdef EMU_M68K
pm68k_read_memory_8 = PicoReadPico8;
pm68k_read_memory_16 = PicoReadPico16;
pm68k_read_memory_32 = PicoReadPico32;
pm68k_write_memory_8 = PicoWritePico8;
pm68k_write_memory_16 = PicoWritePico16;
pm68k_write_memory_32 = PicoWritePico32;
pm68k_read_memory_pcr_8 = m68k_read_memory_pcrp_8;
pm68k_read_memory_pcr_16 = m68k_read_memory_pcrp_16;
pm68k_read_memory_pcr_32 = m68k_read_memory_pcrp_32;
#endif
#ifdef EMU_F68K
// use standard setup, only override handlers
PicoMemSetup();
PicoCpuFM68k.read_byte =PicoReadPico8;
PicoCpuFM68k.read_word =PicoReadPico16;
PicoCpuFM68k.read_long =PicoReadPico32;
PicoCpuFM68k.write_byte=PicoWritePico8;
PicoCpuFM68k.write_word=PicoWritePico16;
PicoCpuFM68k.write_long=PicoWritePico32;
#endif
}

97
pico/pico/pico.c Normal file
View file

@ -0,0 +1,97 @@
#include "../pico_int.h"
// x: 0x03c - 0x19d
// y: 0x1fc - 0x2f7
// 0x2f8 - 0x3f3
picohw_state PicoPicohw;
static int prev_line_cnt_irq3 = 0, prev_line_cnt_irq5 = 0;
static int fifo_bytes_line = (16000<<16)/60/262/2;
static const int guessed_rates[] = { 8000, 14000, 12000, 14000, 16000, 18000, 16000, 16000 }; // ?
#define PICOHW_FIFO_IRQ_THRESHOLD 12
PICO_INTERNAL void PicoReratePico(void)
{
int rate = guessed_rates[PicoPicohw.r12 & 7];
if (Pico.m.pal)
fifo_bytes_line = (rate<<16)/50/312/2;
else fifo_bytes_line = (rate<<16)/60/262/2;
PicoPicoPCMRerate(rate);
}
static void PicoLinePico(void)
{
PicoPicohw.line_counter++;
#if 1
if ((PicoPicohw.r12 & 0x4003) && PicoPicohw.line_counter - prev_line_cnt_irq3 > 200) {
prev_line_cnt_irq3 = PicoPicohw.line_counter;
// just a guess/hack, allows 101 Dalmantians to boot
elprintf(EL_PICOHW, "irq3");
SekInterrupt(3);
return;
}
#endif
if (PicoPicohw.fifo_bytes > 0)
{
PicoPicohw.fifo_line_bytes += fifo_bytes_line;
if (PicoPicohw.fifo_line_bytes >= (1<<16)) {
PicoPicohw.fifo_bytes -= PicoPicohw.fifo_line_bytes >> 16;
PicoPicohw.fifo_line_bytes &= 0xffff;
if (PicoPicohw.fifo_bytes < 0)
PicoPicohw.fifo_bytes = 0;
}
}
else
PicoPicohw.fifo_line_bytes = 0;
#if 1
if (PicoPicohw.fifo_bytes_prev >= PICOHW_FIFO_IRQ_THRESHOLD &&
PicoPicohw.fifo_bytes < PICOHW_FIFO_IRQ_THRESHOLD) {
prev_line_cnt_irq3 = PicoPicohw.line_counter; // ?
elprintf(EL_PICOHW, "irq3, fb=%i", PicoPicohw.fifo_bytes);
SekInterrupt(3);
}
PicoPicohw.fifo_bytes_prev = PicoPicohw.fifo_bytes;
#endif
#if 0
if (PicoPicohw.line_counter - prev_line_cnt_irq5 > 512) {
prev_line_cnt_irq5 = PicoPicohw.line_counter;
elprintf(EL_PICOHW, "irq5");
SekInterrupt(5);
}
#endif
}
static void PicoResetPico(void)
{
PicoPicoPCMReset();
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
}
PICO_INTERNAL void PicoInitPico(void)
{
elprintf(EL_STATUS, "Pico detected");
PicoLineHook = PicoLinePico;
PicoResetHook = PicoResetPico;
PicoAHW = PAHW_PICO;
memset(&PicoPicohw, 0, sizeof(PicoPicohw));
PicoPicohw.pen_pos[0] = 0x03c + 320/2;
PicoPicohw.pen_pos[1] = 0x200 + 240/2;
prev_line_cnt_irq3 = prev_line_cnt_irq5 = 0;
// map version register
PicoDetectRegion();
switch (Pico.m.hardware >> 6) {
case 0: PicoPicohw.r1 = 0x00; break;
case 1: PicoPicohw.r1 = 0x00; break;
case 2: PicoPicohw.r1 = 0x40; break;
case 3: PicoPicohw.r1 = 0x20; break;
}
}

114
pico/pico/xpcm.c Normal file
View file

@ -0,0 +1,114 @@
/*
* The following ADPCM algorithm was stolen from MAME aica driver.
* I'm quite sure it's not the right one, but it's the
* best sounding of the ones that I tried.
*/
#include "../pico_int.h"
#define ADPCMSHIFT 8
#define ADFIX(f) (int) ((double)f * (double)(1<<ADPCMSHIFT))
/* limitter */
#define Limit(val, max, min) { \
if ( val > max ) val = max; \
else if ( val < min ) val = min; \
}
static const int TableQuant[8] =
{
ADFIX(0.8984375),
ADFIX(0.8984375),
ADFIX(0.8984375),
ADFIX(0.8984375),
ADFIX(1.19921875),
ADFIX(1.59765625),
ADFIX(2.0),
ADFIX(2.3984375)
};
// changed using trial and error..
//static const int quant_mul[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15 };
static const int quant_mul[16] = { 1, 3, 5, 7, 9, 11, 13, -1, -1, -3, -5, -7, -9, -11, -13, -15 };
static int sample = 0, quant = 0, sgn = 0;
static int stepsamples = (44100<<10)/16000;
PICO_INTERNAL void PicoPicoPCMReset(void)
{
sample = sgn = 0;
quant = 0x7f;
memset(PicoPicohw.xpcm_buffer, 0, sizeof(PicoPicohw.xpcm_buffer));
}
PICO_INTERNAL void PicoPicoPCMRerate(int xpcm_rate)
{
stepsamples = (PsndRate<<10)/xpcm_rate;
}
#define XSHIFT 6
#define do_sample() \
{ \
int delta = quant * quant_mul[srcval] >> XSHIFT; \
sample += delta - (delta >> 2); /* 3/4 */ \
quant = (quant * TableQuant[srcval&7]) >> ADPCMSHIFT; \
Limit(quant, 0x6000, 0x7f); \
Limit(sample, 32767*3/4, -32768*3/4); \
}
PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo)
{
unsigned char *src = PicoPicohw.xpcm_buffer;
unsigned char *lim = PicoPicohw.xpcm_ptr;
int srcval, needsamples = 0;
if (src == lim) goto end;
for (; length > 0 && src < lim; src++)
{
srcval = *src >> 4;
do_sample();
for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
*buffer++ += sample;
if (stereo) { buffer[0] = buffer[-1]; buffer++; }
}
srcval = *src & 0xf;
do_sample();
for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
*buffer++ += sample;
if (stereo) { buffer[0] = buffer[-1]; buffer++; }
}
// lame normalization stuff, needed due to wrong adpcm algo
sgn += (sample < 0) ? -1 : 1;
if (sgn < -16 || sgn > 16) sample -= sample >> 5;
}
if (src < lim) {
int di = lim - src;
memmove(PicoPicohw.xpcm_buffer, src, di);
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer + di;
elprintf(EL_PICOHW, "xpcm update: over %i", di);
// adjust fifo
PicoPicohw.fifo_bytes = di;
return;
}
elprintf(EL_PICOHW, "xpcm update: under %i", length);
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
end:
if (stereo)
// still must expand SN76496 to stereo
for (; length > 0; buffer+=2, length--)
buffer[1] = buffer[0];
sample = sgn = 0;
quant = 0x7f;
}

262
pico/pico_cmn.c Normal file
View file

@ -0,0 +1,262 @@
// common code for Pico.c and cd/Pico.c
// (c) Copyright 2007,2008 Grazvydas "notaz" Ignotas
#define CYCLES_M68K_LINE 488 // suitable for both PAL/NTSC
#define CYCLES_M68K_VINT_LAG 68
#define CYCLES_M68K_ASD 148
#define CYCLES_S68K_LINE 795
#define CYCLES_S68K_ASD 241
// pad delay (for 6 button pads)
#define PAD_DELAY \
if (PicoOpt&POPT_6BTN_PAD) { \
if(Pico.m.padDelay[0]++ > 25) Pico.m.padTHPhase[0]=0; \
if(Pico.m.padDelay[1]++ > 25) Pico.m.padTHPhase[1]=0; \
}
// CPUS_RUN
#ifndef PICO_CD
#define CPUS_RUN(m68k_cycles,s68k_cycles) \
SekRunM68k(m68k_cycles);
#else
#define CPUS_RUN(m68k_cycles,s68k_cycles) \
{ \
if ((PicoOpt&POPT_EN_MCD_PSYNC) && (Pico_mcd->m.busreq&3) == 1) { \
SekRunPS(m68k_cycles, s68k_cycles); /* "better/perfect sync" */ \
} else { \
SekRunM68k(m68k_cycles); \
if ((Pico_mcd->m.busreq&3) == 1) /* no busreq/no reset */ \
SekRunS68k(s68k_cycles); \
} \
}
#endif
// Accurate but slower frame which does hints
static int PicoFrameHints(void)
{
struct PicoVideo *pv=&Pico.video;
int lines, y, lines_vis = 224, line_sample, skip, vcnt_wrap;
int hint; // Hint counter
pv->v_counter = Pico.m.scanline = 0;
if ((PicoOpt&POPT_ALT_RENDERER) && !PicoSkipFrame && (pv->reg[1]&0x40)) { // fast rend., display enabled
// draw a frame just after vblank in alternative render mode
// yes, this will cause 1 frame lag, but this is inaccurate mode anyway.
PicoFrameFull();
#ifdef DRAW_FINISH_FUNC
DRAW_FINISH_FUNC();
#endif
skip = 1;
}
else skip=PicoSkipFrame;
if (Pico.m.pal) {
line_sample = 68;
if (pv->reg[1]&8) lines_vis = 240;
} else {
line_sample = 93;
}
SekCyclesReset();
z80_resetCycles();
#ifdef PICO_CD
SekCyclesResetS68k();
#endif
PsndDacLine = 0;
pv->status&=~0x88; // clear V-Int, come out of vblank
hint=pv->reg[10]; // Load H-Int counter
//dprintf("-hint: %i", hint);
// This is to make active scan longer (needed for Double Dragon 2, mainly)
CPUS_RUN(CYCLES_M68K_ASD, CYCLES_S68K_ASD);
for (y = 0; y < lines_vis; y++)
{
pv->v_counter = Pico.m.scanline = y;
if ((pv->reg[12]&6) == 6) { // interlace mode 2
pv->v_counter <<= 1;
pv->v_counter |= pv->v_counter >> 8;
pv->v_counter &= 0xff;
}
// VDP FIFO
pv->lwrite_cnt -= 12;
if (pv->lwrite_cnt <= 0) {
pv->lwrite_cnt=0;
Pico.video.status|=0x200;
}
PAD_DELAY
#ifdef PICO_CD
check_cd_dma();
#endif
// H-Interrupts:
if (--hint < 0) // y <= lines_vis: Comix Zone, Golden Axe
{
hint=pv->reg[10]; // Reload H-Int counter
pv->pending_ints|=0x10;
if (pv->reg[0]&0x10) {
elprintf(EL_INTS, "hint: @ %06x [%i]", SekPc, SekCycleCnt);
SekInterrupt(4);
}
}
// decide if we draw this line
if (!skip && (PicoOpt & POPT_ALT_RENDERER))
{
// find the right moment for frame renderer, when display is no longer blanked
if ((pv->reg[1]&0x40) || y > 100) {
PicoFrameFull();
#ifdef DRAW_FINISH_FUNC
DRAW_FINISH_FUNC();
#endif
skip = 1;
}
}
#ifndef PICO_CD
// get samples from sound chips
if (y == 32 && PsndOut)
emustatus &= ~1;
else if ((y == 224 || y == line_sample) && PsndOut)
{
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoOpt&POPT_EN_Z80))
PicoSyncZ80(SekCycleCnt);
if (ym2612.dacen && PsndDacLine <= y)
PsndDoDAC(y);
getSamples(y);
}
#endif
// Run scanline:
if (Pico.m.dma_xfers) SekCyclesBurn(CheckDMA());
CPUS_RUN(CYCLES_M68K_LINE, CYCLES_S68K_LINE);
#ifdef PICO_CD
update_chips();
#else
if (PicoLineHook) PicoLineHook();
#endif
}
if (!skip)
{
if (DrawScanline < y)
PicoDrawSync(y - 1, 0);
#ifdef DRAW_FINISH_FUNC
DRAW_FINISH_FUNC();
#endif
}
// V-int line (224 or 240)
Pico.m.scanline = y;
pv->v_counter = 0xe0; // bad for 240 mode
if ((pv->reg[12]&6) == 6) pv->v_counter = 0xc1;
// VDP FIFO
pv->lwrite_cnt=0;
Pico.video.status|=0x200;
memcpy(PicoPadInt, PicoPad, sizeof(PicoPadInt));
PAD_DELAY
#ifdef PICO_CD
check_cd_dma();
#endif
// Last H-Int:
if (--hint < 0)
{
hint=pv->reg[10]; // Reload H-Int counter
pv->pending_ints|=0x10;
//printf("rhint: %i @ %06x [%i|%i]\n", hint, SekPc, y, SekCycleCnt);
if (pv->reg[0]&0x10) SekInterrupt(4);
}
pv->status|=0x08; // go into vblank
pv->pending_ints|=0x20;
// the following SekRun is there for several reasons:
// there must be a delay after vblank bit is set and irq is asserted (Mazin Saga)
// also delay between F bit (bit 7) is set in SR and IRQ happens (Ex-Mutants)
// also delay between last H-int and V-int (Golden Axe 3)
SekRunM68k(CYCLES_M68K_VINT_LAG);
if (pv->reg[1]&0x20) {
elprintf(EL_INTS, "vint: @ %06x [%i]", SekPc, SekCycleCnt);
SekInterrupt(6);
}
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoOpt&POPT_EN_Z80)) {
PicoSyncZ80(SekCycleCnt);
elprintf(EL_INTS, "zint");
z80_int();
}
// get samples from sound chips
#ifndef PICO_CD
if (y == 224)
#endif
if (PsndOut)
{
if (ym2612.dacen && PsndDacLine <= y)
PsndDoDAC(y);
getSamples(y);
}
// Run scanline:
if (Pico.m.dma_xfers) SekCyclesBurn(CheckDMA());
CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG - CYCLES_M68K_ASD,
CYCLES_S68K_LINE - CYCLES_S68K_ASD);
#ifdef PICO_CD
update_chips();
#else
if (PicoLineHook) PicoLineHook();
#endif
// PAL line count might actually be 313 according to Steve Snake, but that would complicate things.
lines = Pico.m.pal ? 312 : 262;
vcnt_wrap = Pico.m.pal ? 0x103 : 0xEB; // based on Gens
for (y++; y < lines; y++)
{
pv->v_counter = Pico.m.scanline = y;
if (y >= vcnt_wrap)
pv->v_counter -= Pico.m.pal ? 56 : 6;
if ((pv->reg[12]&6) == 6)
pv->v_counter = (pv->v_counter << 1) | 1;
pv->v_counter &= 0xff;
PAD_DELAY
#ifdef PICO_CD
check_cd_dma();
#endif
// Run scanline:
if (Pico.m.dma_xfers) SekCyclesBurn(CheckDMA());
CPUS_RUN(CYCLES_M68K_LINE, CYCLES_S68K_LINE);
#ifdef PICO_CD
update_chips();
#else
if (PicoLineHook) PicoLineHook();
#endif
}
// sync z80
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoOpt&POPT_EN_Z80))
PicoSyncZ80(Pico.m.pal ? 151809 : 127671); // cycles adjusted for converter
if (PsndOut && ym2612.dacen && PsndDacLine <= lines-1)
PsndDoDAC(lines-1);
timers_cycle();
return 0;
}
#undef PAD_DELAY
#undef CPUS_RUN

594
pico/pico_int.h Normal file
View file

@ -0,0 +1,594 @@
// Pico Library - Internal Header File
// (c) Copyright 2004 Dave, All rights reserved.
// (c) Copyright 2006-2008 Grazvydas "notaz" Ignotas, all rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#ifndef PICO_INTERNAL_INCLUDED
#define PICO_INTERNAL_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico.h"
#include "carthw/carthw.h"
//
#define USE_POLL_DETECT
#ifndef PICO_INTERNAL
#define PICO_INTERNAL
#endif
#ifndef PICO_INTERNAL_ASM
#define PICO_INTERNAL_ASM
#endif
// to select core, define EMU_C68K, EMU_M68K or EMU_F68K in your makefile or project
#ifdef __cplusplus
extern "C" {
#endif
// ----------------------- 68000 CPU -----------------------
#ifdef EMU_C68K
#include "../cpu/Cyclone/Cyclone.h"
extern struct Cyclone PicoCpuCM68k, PicoCpuCS68k;
#define SekCyclesLeftNoMCD PicoCpuCM68k.cycles // cycles left for this run
#define SekCyclesLeft \
(((PicoAHW&1) && (PicoOpt & POPT_EN_MCD_PSYNC)) ? (SekCycleAim-SekCycleCnt) : SekCyclesLeftNoMCD)
#define SekCyclesLeftS68k \
((PicoOpt & POPT_EN_MCD_PSYNC) ? (SekCycleAimS68k-SekCycleCntS68k) : PicoCpuCS68k.cycles)
#define SekSetCyclesLeftNoMCD(c) PicoCpuCM68k.cycles=c
#define SekSetCyclesLeft(c) { \
if ((PicoAHW&1) && (PicoOpt & POPT_EN_MCD_PSYNC)) SekCycleCnt=SekCycleAim-(c); else SekSetCyclesLeftNoMCD(c); \
}
#define SekPc (PicoCpuCM68k.pc-PicoCpuCM68k.membase)
#define SekPcS68k (PicoCpuCS68k.pc-PicoCpuCS68k.membase)
#define SekSetStop(x) { PicoCpuCM68k.state_flags&=~1; if (x) { PicoCpuCM68k.state_flags|=1; PicoCpuCM68k.cycles=0; } }
#define SekSetStopS68k(x) { PicoCpuCS68k.state_flags&=~1; if (x) { PicoCpuCS68k.state_flags|=1; PicoCpuCS68k.cycles=0; } }
#define SekIsStoppedS68k() (PicoCpuCS68k.state_flags&1)
#define SekShouldInterrupt (PicoCpuCM68k.irq > (PicoCpuCM68k.srh&7))
#define SekInterrupt(i) PicoCpuCM68k.irq=i
#ifdef EMU_M68K
#define EMU_CORE_DEBUG
#endif
#endif
#ifdef EMU_F68K
#include "../cpu/fame/fame.h"
extern M68K_CONTEXT PicoCpuFM68k, PicoCpuFS68k;
#define SekCyclesLeftNoMCD PicoCpuFM68k.io_cycle_counter
#define SekCyclesLeft \
(((PicoAHW&1) && (PicoOpt & POPT_EN_MCD_PSYNC)) ? (SekCycleAim-SekCycleCnt) : SekCyclesLeftNoMCD)
#define SekCyclesLeftS68k \
((PicoOpt & POPT_EN_MCD_PSYNC) ? (SekCycleAimS68k-SekCycleCntS68k) : PicoCpuFS68k.io_cycle_counter)
#define SekSetCyclesLeftNoMCD(c) PicoCpuFM68k.io_cycle_counter=c
#define SekSetCyclesLeft(c) { \
if ((PicoAHW&1) && (PicoOpt & POPT_EN_MCD_PSYNC)) SekCycleCnt=SekCycleAim-(c); else SekSetCyclesLeftNoMCD(c); \
}
#define SekPc fm68k_get_pc(&PicoCpuFM68k)
#define SekPcS68k fm68k_get_pc(&PicoCpuFS68k)
#define SekSetStop(x) { \
PicoCpuFM68k.execinfo &= ~FM68K_HALTED; \
if (x) { PicoCpuFM68k.execinfo |= FM68K_HALTED; PicoCpuFM68k.io_cycle_counter = 0; } \
}
#define SekSetStopS68k(x) { \
PicoCpuFS68k.execinfo &= ~FM68K_HALTED; \
if (x) { PicoCpuFS68k.execinfo |= FM68K_HALTED; PicoCpuFS68k.io_cycle_counter = 0; } \
}
#define SekIsStoppedS68k() (PicoCpuFS68k.execinfo&FM68K_HALTED)
#define SekShouldInterrupt fm68k_would_interrupt()
#define SekInterrupt(irq) PicoCpuFM68k.interrupts[0]=irq
#ifdef EMU_M68K
#define EMU_CORE_DEBUG
#endif
#endif
#ifdef EMU_M68K
#include "../cpu/musashi/m68kcpu.h"
extern m68ki_cpu_core PicoCpuMM68k, PicoCpuMS68k;
#ifndef SekCyclesLeft
#define SekCyclesLeftNoMCD PicoCpuMM68k.cyc_remaining_cycles
#define SekCyclesLeft \
(((PicoAHW&1) && (PicoOpt & POPT_EN_MCD_PSYNC)) ? (SekCycleAim-SekCycleCnt) : SekCyclesLeftNoMCD)
#define SekCyclesLeftS68k \
((PicoOpt & POPT_EN_MCD_PSYNC) ? (SekCycleAimS68k-SekCycleCntS68k) : PicoCpuMS68k.cyc_remaining_cycles)
#define SekSetCyclesLeftNoMCD(c) SET_CYCLES(c)
#define SekSetCyclesLeft(c) { \
if ((PicoAHW&1) && (PicoOpt & POPT_EN_MCD_PSYNC)) SekCycleCnt=SekCycleAim-(c); else SET_CYCLES(c); \
}
#define SekPc m68k_get_reg(&PicoCpuMM68k, M68K_REG_PC)
#define SekPcS68k m68k_get_reg(&PicoCpuMS68k, M68K_REG_PC)
#define SekSetStop(x) { \
if(x) { SET_CYCLES(0); PicoCpuMM68k.stopped=STOP_LEVEL_STOP; } \
else PicoCpuMM68k.stopped=0; \
}
#define SekSetStopS68k(x) { \
if(x) { SET_CYCLES(0); PicoCpuMS68k.stopped=STOP_LEVEL_STOP; } \
else PicoCpuMS68k.stopped=0; \
}
#define SekIsStoppedS68k() (PicoCpuMS68k.stopped==STOP_LEVEL_STOP)
#define SekShouldInterrupt (CPU_INT_LEVEL > FLAG_INT_MASK)
#define SekInterrupt(irq) { \
void *oldcontext = m68ki_cpu_p; \
m68k_set_context(&PicoCpuMM68k); \
m68k_set_irq(irq); \
m68k_set_context(oldcontext); \
}
#endif
#endif
extern int SekCycleCnt; // cycles done in this frame
extern int SekCycleAim; // cycle aim
extern unsigned int SekCycleCntT; // total cycle counter, updated once per frame
#define SekCyclesReset() { \
SekCycleCntT+=SekCycleAim; \
SekCycleCnt-=SekCycleAim; \
SekCycleAim=0; \
}
#define SekCyclesBurn(c) SekCycleCnt+=c
#define SekCyclesDone() (SekCycleAim-SekCyclesLeft) // number of cycles done in this frame (can be checked anywhere)
#define SekCyclesDoneT() (SekCycleCntT+SekCyclesDone()) // total nuber of cycles done for this rom
#define SekEndRun(after) { \
SekCycleCnt -= SekCyclesLeft - after; \
if(SekCycleCnt < 0) SekCycleCnt = 0; \
SekSetCyclesLeft(after); \
}
extern int SekCycleCntS68k;
extern int SekCycleAimS68k;
#define SekCyclesResetS68k() { \
SekCycleCntS68k-=SekCycleAimS68k; \
SekCycleAimS68k=0; \
}
#define SekCyclesDoneS68k() (SekCycleAimS68k-SekCyclesLeftS68k)
#ifdef EMU_CORE_DEBUG
extern int dbg_irq_level;
#undef SekSetCyclesLeftNoMCD
#undef SekSetCyclesLeft
#undef SekCyclesBurn
#undef SekEndRun
#undef SekInterrupt
#define SekSetCyclesLeftNoMCD(c)
#define SekSetCyclesLeft(c)
#define SekCyclesBurn(c) c
#define SekEndRun(c)
#define SekInterrupt(irq) dbg_irq_level=irq
#endif
// ----------------------- Z80 CPU -----------------------
#if defined(_USE_MZ80)
#include "../cpu/mz80/mz80.h"
#define z80_run(cycles) { mz80GetElapsedTicks(1); mz80_run(cycles) }
#define z80_run_nr(cycles) mz80_run(cycles)
#define z80_int() mz80int(0)
#elif defined(_USE_DRZ80)
#include "../cpu/DrZ80/drz80.h"
extern struct DrZ80 drZ80;
#define z80_run(cycles) ((cycles) - DrZ80Run(&drZ80, cycles))
#define z80_run_nr(cycles) DrZ80Run(&drZ80, cycles)
#define z80_int() drZ80.Z80_IRQ = 1
#define z80_cyclesLeft drZ80.cycles
#elif defined(_USE_CZ80)
#include "../cpu/cz80/cz80.h"
#define z80_run(cycles) Cz80_Exec(&CZ80, cycles)
#define z80_run_nr(cycles) Cz80_Exec(&CZ80, cycles)
#define z80_int() Cz80_Set_IRQ(&CZ80, 0, HOLD_LINE)
#define z80_cyclesLeft (CZ80.ICount - CZ80.ExtraCycles)
#else
#define z80_run(cycles) (cycles)
#define z80_run_nr(cycles)
#define z80_int()
#endif
extern int z80stopCycle; /* in 68k cycles */
extern int z80_cycle_cnt; /* 'done' z80 cycles before z80_run() */
extern int z80_cycle_aim;
extern int z80_scanline;
extern int z80_scanline_cycles; /* cycles done until z80_scanline */
#define z80_resetCycles() \
z80_cycle_cnt = z80_cycle_aim = z80_scanline = z80_scanline_cycles = 0;
#define z80_cyclesDone() \
(z80_cycle_aim - z80_cyclesLeft)
#define cycles_68k_to_z80(x) ((x)*957 >> 11)
// ---------------------------------------------------------
// main oscillator clock which controls timing
#define OSC_NTSC 53693100
// seems to be accurate, see scans from http://www.hot.ee/tmeeco/
#define OSC_PAL 53203424
struct PicoVideo
{
unsigned char reg[0x20];
unsigned int command; // 32-bit Command
unsigned char pending; // 1 if waiting for second half of 32-bit command
unsigned char type; // Command type (v/c/vsram read/write)
unsigned short addr; // Read/Write address
int status; // Status bits
unsigned char pending_ints; // pending interrupts: ??VH????
signed char lwrite_cnt; // VDP write count during active display line
unsigned short v_counter; // V-counter
unsigned char pad[0x10];
};
struct PicoMisc
{
unsigned char rotate;
unsigned char z80Run;
unsigned char padTHPhase[2]; // 02 phase of gamepad TH switches
unsigned short scanline; // 04 0 to 261||311
char dirtyPal; // 06 Is the palette dirty (1 - change @ this frame, 2 - some time before)
unsigned char hardware; // 07 Hardware value for country
unsigned char pal; // 08 1=PAL 0=NTSC
unsigned char sram_reg; // SRAM mode register. bit0: allow read? bit1: deny write? bit2: EEPROM? bit4: detected? (header or by access)
unsigned short z80_bank68k; // 0a
unsigned short z80_lastaddr; // this is for Z80 faking
unsigned char z80_fakeval;
unsigned char z80_reset; // z80 reset held
unsigned char padDelay[2]; // 10 gamepad phase time outs, so we count a delay
unsigned short eeprom_addr; // EEPROM address register
unsigned char eeprom_cycle; // EEPROM SRAM cycle number
unsigned char eeprom_slave; // EEPROM slave word for X24C02 and better SRAMs
unsigned char prot_bytes[2]; // simple protection faking
unsigned short dma_xfers; // 18
unsigned char pad[2];
unsigned int frame_count; // 1c for movies and idle det
};
// some assembly stuff depend on these, do not touch!
struct Pico
{
unsigned char ram[0x10000]; // 0x00000 scratch ram
unsigned short vram[0x8000]; // 0x10000
unsigned char zram[0x2000]; // 0x20000 Z80 ram
unsigned char ioports[0x10];
unsigned int pad[0x3c]; // unused
unsigned short cram[0x40]; // 0x22100
unsigned short vsram[0x40]; // 0x22180
unsigned char *rom; // 0x22200
unsigned int romsize; // 0x22204
struct PicoMisc m;
struct PicoVideo video;
};
// sram
struct PicoSRAM
{
unsigned char *data; // actual data
unsigned int start; // start address in 68k address space
unsigned int end;
unsigned char unused1; // 0c: unused
unsigned char unused2;
unsigned char changed;
unsigned char eeprom_type; // eeprom type: 0: 7bit (24C01), 2: device with 2 addr words (X24C02+), 3: dev with 3 addr words
unsigned char eeprom_abits; // eeprom access must be odd addr for: bit0 ~ cl, bit1 ~ out
unsigned char eeprom_bit_cl; // bit number for cl
unsigned char eeprom_bit_in; // bit number for in
unsigned char eeprom_bit_out; // bit number for out
};
// MCD
#include "cd/cd_sys.h"
#include "cd/LC89510.h"
#include "cd/gfx_cd.h"
struct mcd_pcm
{
unsigned char control; // reg7
unsigned char enabled; // reg8
unsigned char cur_ch;
unsigned char bank;
int pad1;
struct pcm_chan // 08, size 0x10
{
unsigned char regs[8];
unsigned int addr; // .08: played sample address
int pad;
} ch[8];
};
struct mcd_misc
{
unsigned short hint_vector;
unsigned char busreq;
unsigned char s68k_pend_ints;
unsigned int state_flags; // 04: emu state: reset_pending, dmna_pending
unsigned int counter75hz;
unsigned int pad0;
int timer_int3; // 10
unsigned int timer_stopwatch;
unsigned char bcram_reg; // 18: battery-backed RAM cart register
unsigned char pad2;
unsigned short pad3;
int pad[9];
};
typedef struct
{
unsigned char bios[0x20000]; // 000000: 128K
union { // 020000: 512K
unsigned char prg_ram[0x80000];
unsigned char prg_ram_b[4][0x20000];
};
union { // 0a0000: 256K
struct {
unsigned char word_ram2M[0x40000];
unsigned char unused0[0x20000];
};
struct {
unsigned char unused1[0x20000];
unsigned char word_ram1M[2][0x20000];
};
};
union { // 100000: 64K
unsigned char pcm_ram[0x10000];
unsigned char pcm_ram_b[0x10][0x1000];
};
unsigned char s68k_regs[0x200]; // 110000: GA, not CPU regs
unsigned char bram[0x2000]; // 110200: 8K
struct mcd_misc m; // 112200: misc
struct mcd_pcm pcm; // 112240:
_scd_toc TOC; // not to be saved
CDD cdd;
CDC cdc;
_scd scd;
Rot_Comp rot_comp;
} mcd_state;
#define Pico_mcd ((mcd_state *)Pico.rom)
// Area.c
PICO_INTERNAL void PicoAreaPackCpu(unsigned char *cpu, int is_sub);
PICO_INTERNAL void PicoAreaUnpackCpu(unsigned char *cpu, int is_sub);
extern void (*PicoLoadStateHook)(void);
// cd/Area.c
PICO_INTERNAL int PicoCdSaveState(void *file);
PICO_INTERNAL int PicoCdLoadState(void *file);
typedef struct {
int chunk;
int size;
void *ptr;
} carthw_state_chunk;
extern carthw_state_chunk *carthw_chunks;
#define CHUNK_CARTHW 64
// Cart.c
extern void (*PicoCartUnloadHook)(void);
// Debug.c
int CM_compareRun(int cyc, int is_sub);
// Draw.c
PICO_INTERNAL void PicoFrameStart(void);
void PicoDrawSync(int to, int blank_last_line);
extern int DrawScanline;
#define MAX_LINE_SPRITES 29
extern unsigned char HighLnSpr[240][3 + MAX_LINE_SPRITES];
// Draw2.c
PICO_INTERNAL void PicoFrameFull();
// Memory.c
PICO_INTERNAL void PicoInitPc(unsigned int pc);
PICO_INTERNAL unsigned int PicoCheckPc(unsigned int pc);
PICO_INTERNAL_ASM unsigned int PicoRead32(unsigned int a);
PICO_INTERNAL void PicoMemSetup(void);
PICO_INTERNAL_ASM void PicoMemReset(void);
PICO_INTERNAL void PicoMemResetHooks(void);
PICO_INTERNAL int PadRead(int i);
PICO_INTERNAL unsigned char z80_read(unsigned short a);
#ifndef _USE_CZ80
PICO_INTERNAL_ASM void z80_write(unsigned char data, unsigned short a);
PICO_INTERNAL void z80_write16(unsigned short data, unsigned short a);
PICO_INTERNAL unsigned short z80_read16(unsigned short a);
#else
PICO_INTERNAL_ASM void z80_write(unsigned int a, unsigned char data);
#endif
PICO_INTERNAL int ym2612_write_local(unsigned int a, unsigned int d, int is_from_z80);
extern unsigned int (*PicoRead16Hook)(unsigned int a, int realsize);
extern void (*PicoWrite8Hook) (unsigned int a,unsigned int d,int realsize);
extern void (*PicoWrite16Hook)(unsigned int a,unsigned int d,int realsize);
// cd/Memory.c
PICO_INTERNAL void PicoMemSetupCD(void);
PICO_INTERNAL_ASM void PicoMemResetCD(int r3);
PICO_INTERNAL_ASM void PicoMemResetCDdecode(int r3);
// Pico/Memory.c
PICO_INTERNAL void PicoMemSetupPico(void);
PICO_INTERNAL unsigned int ym2612_read_local_68k(void);
// Pico.c
extern struct Pico Pico;
extern struct PicoSRAM SRam;
extern int PicoPadInt[2];
extern int emustatus;
extern void (*PicoResetHook)(void);
extern void (*PicoLineHook)(void);
PICO_INTERNAL int CheckDMA(void);
PICO_INTERNAL void PicoDetectRegion(void);
PICO_INTERNAL void PicoSyncZ80(int m68k_cycles_done);
// cd/Pico.c
PICO_INTERNAL void PicoInitMCD(void);
PICO_INTERNAL void PicoExitMCD(void);
PICO_INTERNAL void PicoPowerMCD(void);
PICO_INTERNAL int PicoResetMCD(void);
PICO_INTERNAL void PicoFrameMCD(void);
// Pico/Pico.c
PICO_INTERNAL void PicoInitPico(void);
PICO_INTERNAL void PicoReratePico(void);
// Pico/xpcm.c
PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo);
PICO_INTERNAL void PicoPicoPCMReset(void);
PICO_INTERNAL void PicoPicoPCMRerate(int xpcm_rate);
// Sek.c
PICO_INTERNAL void SekInit(void);
PICO_INTERNAL int SekReset(void);
PICO_INTERNAL void SekState(int *data);
PICO_INTERNAL void SekSetRealTAS(int use_real);
void SekStepM68k(void);
void SekInitIdleDet(void);
void SekFinishIdleDet(void);
// cd/Sek.c
PICO_INTERNAL void SekInitS68k(void);
PICO_INTERNAL int SekResetS68k(void);
PICO_INTERNAL int SekInterruptS68k(int irq);
// sound/sound.c
PICO_INTERNAL void cdda_start_play();
extern short cdda_out_buffer[2*1152];
extern int PsndLen_exc_cnt;
extern int PsndLen_exc_add;
extern int timer_a_next_oflow, timer_a_step; // in z80 cycles
extern int timer_b_next_oflow, timer_b_step;
void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new);
void ym2612_pack_state(void);
void ym2612_unpack_state(void);
#define TIMER_NO_OFLOW 0x70000000
// tA = 72 * (1024 - NA) / M
#define TIMER_A_TICK_ZCYCLES 17203
// tB = 1152 * (256 - NA) / M
#define TIMER_B_TICK_ZCYCLES 262800 // 275251 broken, see Dai Makaimura
#define timers_cycle() \
if (timer_a_next_oflow > 0 && timer_a_next_oflow < TIMER_NO_OFLOW) \
timer_a_next_oflow -= Pico.m.pal ? 70938*256 : 59659*256; \
if (timer_b_next_oflow > 0 && timer_b_next_oflow < TIMER_NO_OFLOW) \
timer_b_next_oflow -= Pico.m.pal ? 70938*256 : 59659*256; \
ym2612_sync_timers(0, ym2612.OPN.ST.mode, ym2612.OPN.ST.mode);
#define timers_reset() \
timer_a_next_oflow = timer_b_next_oflow = TIMER_NO_OFLOW; \
timer_a_step = TIMER_A_TICK_ZCYCLES * 1024; \
timer_b_step = TIMER_B_TICK_ZCYCLES * 256;
// VideoPort.c
PICO_INTERNAL_ASM void PicoVideoWrite(unsigned int a,unsigned short d);
PICO_INTERNAL_ASM unsigned int PicoVideoRead(unsigned int a);
PICO_INTERNAL_ASM unsigned int PicoVideoRead8(unsigned int a);
extern int (*PicoDmaHook)(unsigned int source, int len, unsigned short **srcp, unsigned short **limitp);
// Misc.c
PICO_INTERNAL void SRAMWriteEEPROM(unsigned int d);
PICO_INTERNAL void SRAMUpdPending(unsigned int a, unsigned int d);
PICO_INTERNAL_ASM unsigned int SRAMReadEEPROM(void);
PICO_INTERNAL_ASM void memcpy16(unsigned short *dest, unsigned short *src, int count);
PICO_INTERNAL_ASM void memcpy16bswap(unsigned short *dest, void *src, int count);
PICO_INTERNAL_ASM void memcpy32(int *dest, int *src, int count); // 32bit word count
PICO_INTERNAL_ASM void memset32(int *dest, int c, int count);
// cd/Misc.c
PICO_INTERNAL_ASM void wram_2M_to_1M(unsigned char *m);
PICO_INTERNAL_ASM void wram_1M_to_2M(unsigned char *m);
// cd/buffering.c
PICO_INTERNAL void PicoCDBufferRead(void *dest, int lba);
// sound/sound.c
PICO_INTERNAL void PsndReset(void);
PICO_INTERNAL void PsndDoDAC(int line_to);
PICO_INTERNAL int PsndRender(int offset, int length);
PICO_INTERNAL void PsndClear(void);
// z80 functionality wrappers
PICO_INTERNAL void z80_init(void);
PICO_INTERNAL void z80_pack(unsigned char *data);
PICO_INTERNAL void z80_unpack(unsigned char *data);
PICO_INTERNAL void z80_reset(void);
PICO_INTERNAL void z80_exit(void);
extern int PsndDacLine;
// emulation event logging
#ifndef EL_LOGMASK
#define EL_LOGMASK 0
#endif
#define EL_HVCNT 0x00000001 /* hv counter reads */
#define EL_SR 0x00000002 /* SR reads */
#define EL_INTS 0x00000004 /* ints and acks */
#define EL_YMTIMER 0x00000008 /* ym2612 timer stuff */
#define EL_INTSW 0x00000010 /* log irq switching on/off */
#define EL_ASVDP 0x00000020 /* VDP accesses during active scan */
#define EL_VDPDMA 0x00000040 /* VDP DMA transfers and their timing */
#define EL_BUSREQ 0x00000080 /* z80 busreq r/w or reset w */
#define EL_Z80BNK 0x00000100 /* z80 i/o through bank area */
#define EL_SRAMIO 0x00000200 /* sram i/o */
#define EL_EEPROM 0x00000400 /* eeprom debug */
#define EL_UIO 0x00000800 /* unmapped i/o */
#define EL_IO 0x00001000 /* all i/o */
#define EL_CDPOLL 0x00002000 /* MCD: log poll detection */
#define EL_SVP 0x00004000 /* SVP stuff */
#define EL_PICOHW 0x00008000 /* Pico stuff */
#define EL_IDLE 0x00010000 /* idle loop det. */
#define EL_STATUS 0x40000000 /* status messages */
#define EL_ANOMALY 0x80000000 /* some unexpected conditions (during emulation) */
#if EL_LOGMASK
extern void lprintf(const char *fmt, ...);
#define elprintf(w,f,...) \
{ \
if ((w) & EL_LOGMASK) \
lprintf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__); \
}
#elif defined(_MSC_VER)
#define elprintf
#else
#define elprintf(w,f,...)
#endif
#ifdef _MSC_VER
#define cdprintf
#else
#define cdprintf(x...)
#endif
#ifdef __cplusplus
} // End of extern "C"
#endif
#endif // PICO_INTERNAL_INCLUDED

370
pico/sek.c Normal file
View file

@ -0,0 +1,370 @@
// 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_int.h"
int SekCycleCnt=0; // cycles done in this frame
int SekCycleAim=0; // cycle aim
unsigned int SekCycleCntT=0;
/* context */
// Cyclone 68000
#ifdef EMU_C68K
struct Cyclone PicoCpuCM68k;
#endif
// MUSASHI 68000
#ifdef EMU_M68K
m68ki_cpu_core PicoCpuMM68k;
#endif
// FAME 68000
#ifdef EMU_F68K
M68K_CONTEXT PicoCpuFM68k;
#endif
/* callbacks */
#ifdef EMU_C68K
// interrupt acknowledgment
static int SekIntAck(int level)
{
// try to emulate VDP's reaction to 68000 int ack
if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }
else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }
PicoCpuCM68k.irq = 0;
return CYCLONE_INT_ACK_AUTOVECTOR;
}
static void SekResetAck(void)
{
elprintf(EL_ANOMALY, "Reset encountered @ %06x", SekPc);
}
static int SekUnrecognizedOpcode()
{
unsigned int pc, op;
pc = SekPc;
op = PicoCpuCM68k.read16(pc);
elprintf(EL_ANOMALY, "Unrecognized Opcode %04x @ %06x", op, pc);
// see if we are not executing trash
if (pc < 0x200 || (pc > Pico.romsize+4 && (pc&0xe00000)!=0xe00000)) {
PicoCpuCM68k.cycles = 0;
PicoCpuCM68k.state_flags |= 1;
return 1;
}
#ifdef EMU_M68K // debugging cyclone
{
extern int have_illegal;
have_illegal = 1;
}
#endif
return 0;
}
#endif
#ifdef EMU_M68K
static int SekIntAckM68K(int level)
{
if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }
else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }
CPU_INT_LEVEL = 0;
return M68K_INT_ACK_AUTOVECTOR;
}
static int SekTasCallback(void)
{
return 0; // no writeback
}
#endif
#ifdef EMU_F68K
static void SekIntAckF68K(unsigned level)
{
if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }
else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }
PicoCpuFM68k.interrupts[0] = 0;
}
#endif
PICO_INTERNAL void SekInit(void)
{
#ifdef EMU_C68K
CycloneInit();
memset(&PicoCpuCM68k,0,sizeof(PicoCpuCM68k));
PicoCpuCM68k.IrqCallback=SekIntAck;
PicoCpuCM68k.ResetCallback=SekResetAck;
PicoCpuCM68k.UnrecognizedCallback=SekUnrecognizedOpcode;
PicoCpuCM68k.flags=4; // Z set
#endif
#ifdef EMU_M68K
{
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoCpuMM68k);
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
m68k_init();
m68k_set_int_ack_callback(SekIntAckM68K);
m68k_set_tas_instr_callback(SekTasCallback);
//m68k_pulse_reset();
m68k_set_context(oldcontext);
}
#endif
#ifdef EMU_F68K
{
void *oldcontext = g_m68kcontext;
g_m68kcontext = &PicoCpuFM68k;
memset(&PicoCpuFM68k, 0, sizeof(PicoCpuFM68k));
fm68k_init();
PicoCpuFM68k.iack_handler = SekIntAckF68K;
PicoCpuFM68k.sr = 0x2704; // Z flag
g_m68kcontext = oldcontext;
}
#endif
}
// Reset the 68000:
PICO_INTERNAL int SekReset(void)
{
if (Pico.rom==NULL) return 1;
#ifdef EMU_C68K
PicoCpuCM68k.state_flags=0;
PicoCpuCM68k.osp=0;
PicoCpuCM68k.srh =0x27; // Supervisor mode
PicoCpuCM68k.irq=0;
PicoCpuCM68k.a[7]=PicoCpuCM68k.read32(0); // Stack Pointer
PicoCpuCM68k.membase=0;
PicoCpuCM68k.pc=PicoCpuCM68k.checkpc(PicoCpuCM68k.read32(4)); // Program Counter
#endif
#ifdef EMU_M68K
m68k_set_context(&PicoCpuMM68k); // if we ever reset m68k, we always need it's context to be set
m68ki_cpu.sp[0]=0;
m68k_set_irq(0);
m68k_pulse_reset();
REG_USP = 0; // ?
#endif
#ifdef EMU_F68K
{
g_m68kcontext = &PicoCpuFM68k;
fm68k_reset();
}
#endif
return 0;
}
void SekStepM68k(void)
{
SekCycleAim=SekCycleCnt+1;
#if defined(EMU_CORE_DEBUG)
SekCycleCnt+=CM_compareRun(1, 0);
#elif defined(EMU_C68K)
PicoCpuCM68k.cycles=1;
CycloneRun(&PicoCpuCM68k);
SekCycleCnt+=1-PicoCpuCM68k.cycles;
#elif defined(EMU_M68K)
SekCycleCnt+=m68k_execute(1);
#elif defined(EMU_F68K)
SekCycleCnt+=fm68k_emulate(1, 0, 0);
#endif
}
PICO_INTERNAL void SekSetRealTAS(int use_real)
{
#ifdef EMU_C68K
CycloneSetRealTAS(use_real);
#endif
#ifdef EMU_F68K
// TODO
#endif
}
/* idle loop detection, not to be used in CD mode */
#ifdef EMU_C68K
#include "cpu/Cyclone/tools/idle.h"
#endif
static int *idledet_addrs = NULL;
static int idledet_count = 0, idledet_bads = 0;
int idledet_start_frame = 0;
#if 0
#define IDLE_STATS 1
unsigned int idlehit_addrs[128], idlehit_counts[128];
void SekRegisterIdleHit(unsigned int pc)
{
int i;
for (i = 0; i < 127 && idlehit_addrs[i]; i++) {
if (idlehit_addrs[i] == pc) {
idlehit_counts[i]++;
return;
}
}
idlehit_addrs[i] = pc;
idlehit_counts[i] = 1;
idlehit_addrs[i+1] = 0;
}
#endif
void SekInitIdleDet(void)
{
void *tmp = realloc(idledet_addrs, 0x200*4);
if (tmp == NULL) {
free(idledet_addrs);
idledet_addrs = NULL;
}
else
idledet_addrs = tmp;
idledet_count = idledet_bads = 0;
idledet_start_frame = Pico.m.frame_count + 360;
#ifdef IDLE_STATS
idlehit_addrs[0] = 0;
#endif
#ifdef EMU_C68K
CycloneInitIdle();
#endif
#ifdef EMU_F68K
fm68k_emulate(0, 0, 1);
#endif
}
int SekIsIdleCode(unsigned short *dst, int bytes)
{
// printf("SekIsIdleCode %04x %i\n", *dst, bytes);
switch (bytes)
{
case 2:
if ((*dst & 0xf000) != 0x6000) // not another branch
return 1;
break;
case 4:
if ( (*dst & 0xfff8) == 0x4a10 || // tst.b ($aX) // there should be no need to wait
(*dst & 0xfff8) == 0x4a28 || // tst.b ($xxxx,a0) // for byte change anywhere
(*dst & 0xff3f) == 0x4a38 || // tst.x ($xxxx.w); tas ($xxxx.w)
(*dst & 0xc1ff) == 0x0038 || // move.x ($xxxx.w), dX
(*dst & 0xf13f) == 0xb038) // cmp.x ($xxxx.w), dX
return 1;
break;
case 6:
if ( ((dst[1] & 0xe0) == 0xe0 && ( // RAM and
*dst == 0x4a39 || // tst.b ($xxxxxxxx)
*dst == 0x4a79 || // tst.w ($xxxxxxxx)
*dst == 0x4ab9 || // tst.l ($xxxxxxxx)
(*dst & 0xc1ff) == 0x0039 || // move.x ($xxxxxxxx), dX
(*dst & 0xf13f) == 0xb039))||// cmp.x ($xxxxxxxx), dX
*dst == 0x0838 || // btst $X, ($xxxx.w) [6 byte op]
(*dst & 0xffbf) == 0x0c38) // cmpi.{b,w} $X, ($xxxx.w)
return 1;
break;
case 8:
if ( ((dst[2] & 0xe0) == 0xe0 && ( // RAM and
*dst == 0x0839 || // btst $X, ($xxxxxxxx.w) [8 byte op]
(*dst & 0xffbf) == 0x0c39))||// cmpi.{b,w} $X, ($xxxxxxxx)
*dst == 0x0cb8) // cmpi.l $X, ($xxxx.w)
return 1;
break;
case 12:
if ((*dst & 0xf1f8) == 0x3010 && // move.w (aX), dX
(dst[1]&0xf100) == 0x0000 && // arithmetic
(dst[3]&0xf100) == 0x0000) // arithmetic
return 1;
break;
}
return 0;
}
int SekRegisterIdlePatch(unsigned int pc, int oldop, int newop, void *ctx)
{
int is_main68k = 1;
#if defined(EMU_C68K)
struct Cyclone *cyc = ctx;
is_main68k = cyc == &PicoCpuCM68k;
pc -= cyc->membase;
#elif defined(EMU_F68K)
is_main68k = ctx == &PicoCpuFM68k;
#endif
pc &= ~0xff000000;
elprintf(EL_IDLE, "idle: patch %06x %04x %04x %c %c #%i", pc, oldop, newop,
(newop&0x200)?'n':'y', is_main68k?'m':'s', idledet_count);
if (pc > Pico.romsize && !(PicoAHW & PAHW_SVP)) {
if (++idledet_bads > 128) return 2; // remove detector
return 1; // don't patch
}
if (idledet_count >= 0x200 && (idledet_count & 0x1ff) == 0) {
void *tmp = realloc(idledet_addrs, (idledet_count+0x200)*4);
if (tmp == NULL) return 1;
idledet_addrs = tmp;
}
if (pc < Pico.romsize)
idledet_addrs[idledet_count++] = pc;
return 0;
}
void SekFinishIdleDet(void)
{
#ifdef EMU_C68K
CycloneFinishIdle();
#endif
#ifdef EMU_F68K
fm68k_emulate(0, 0, 2);
#endif
while (idledet_count > 0)
{
unsigned short *op = (unsigned short *)&Pico.rom[idledet_addrs[--idledet_count]];
if ((*op & 0xfd00) == 0x7100)
*op &= 0xff, *op |= 0x6600;
else if ((*op & 0xfd00) == 0x7500)
*op &= 0xff, *op |= 0x6700;
else if ((*op & 0xfd00) == 0x7d00)
*op &= 0xff, *op |= 0x6000;
else
elprintf(EL_STATUS|EL_IDLE, "idle: don't know how to restore %04x", *op);
}
}
#if defined(EMU_M68K) && M68K_INSTRUCTION_HOOK == OPT_SPECIFY_HANDLER
static unsigned char op_flags[0x400000/2] = { 0, };
static int atexit_set = 0;
static void make_idc(void)
{
FILE *f = fopen("idc.idc", "w");
int i;
if (!f) return;
fprintf(f, "#include <idc.idc>\nstatic main() {\n");
for (i = 0; i < 0x400000/2; i++)
if (op_flags[i] != 0)
fprintf(f, " MakeCode(0x%06x);\n", i*2);
fprintf(f, "}\n");
fclose(f);
}
void instruction_hook(void)
{
if (!atexit_set) {
atexit(make_idc);
atexit_set = 1;
}
if (REG_PC < 0x400000)
op_flags[REG_PC/2] = 1;
}
#endif

74
pico/sound/mix.c Normal file
View file

@ -0,0 +1,74 @@
// some code for sample mixing
// (c) Copyright 2006-2007, Grazvydas "notaz" Ignotas
#define MAXOUT (+32767)
#define MINOUT (-32768)
/* limitter */
#define Limit(val, max,min) { \
if ( val > max ) val = max; \
else if ( val < min ) val = min; \
}
void mix_32_to_16l_stereo(short *dest, int *src, int count)
{
int l, r;
for (; count > 0; count--)
{
l = r = *dest;
l += *src++;
r += *src++;
Limit( l, MAXOUT, MINOUT );
Limit( r, MAXOUT, MINOUT );
*dest++ = l;
*dest++ = r;
}
}
void mix_32_to_16_mono(short *dest, int *src, int count)
{
int l;
for (; count > 0; count--)
{
l = *dest;
l += *src++;
Limit( l, MAXOUT, MINOUT );
*dest++ = l;
}
}
void mix_16h_to_32(int *dest_buf, short *mp3_buf, int count)
{
while (count--)
{
*dest_buf++ += *mp3_buf++ >> 1;
}
}
void mix_16h_to_32_s1(int *dest_buf, short *mp3_buf, int count)
{
count >>= 1;
while (count--)
{
*dest_buf++ += *mp3_buf++ >> 1;
*dest_buf++ += *mp3_buf++ >> 1;
mp3_buf += 1*2;
}
}
void mix_16h_to_32_s2(int *dest_buf, short *mp3_buf, int count)
{
count >>= 1;
while (count--)
{
*dest_buf++ += *mp3_buf++ >> 1;
*dest_buf++ += *mp3_buf++ >> 1;
mp3_buf += 3*2;
}
}

10
pico/sound/mix.h Normal file
View file

@ -0,0 +1,10 @@
//void mix_32_to_32(int *dest, int *src, int count);
void mix_16h_to_32(int *dest, short *src, int count);
void mix_16h_to_32_s1(int *dest, short *src, int count);
void mix_16h_to_32_s2(int *dest, short *src, int count);
void mix_32_to_16l_stereo(short *dest, int *src, int count);
void mix_32_to_16_mono(short *dest, int *src, int count);
extern int mix_32_to_16l_level;
void mix_32_to_16l_stereo_lvl(short *dest, int *src, int count);

367
pico/sound/mix_arm.s Normal file
View file

@ -0,0 +1,367 @@
@ vim:filetype=armasm
@ Generic routines for mixing audio samples
@ (c) Copyright 2007, Grazvydas "notaz" Ignotas
.text
.align 4
@ this assumes src is word aligned
.global mix_16h_to_32 @ int *dest, short *src, int count
mix_16h_to_32:
stmfd sp!, {r4-r6,lr}
/*
tst r1, #2
beq m16_32_mo_unalw
ldrsh r4, [r1], #2
ldr r3, [r0]
sub r2, r2, #1
add r3, r3, r4, asr #1
str r3, [r0], #4
*/
m16_32_mo_unalw:
subs r2, r2, #4
bmi m16_32_end
m16_32_loop:
ldmia r0, {r3-r6}
ldmia r1!,{r12,lr}
subs r2, r2, #4
add r4, r4, r12,asr #17 @ we use half volume
mov r12,r12,lsl #16
add r3, r3, r12,asr #17
add r6, r6, lr, asr #17
mov lr, lr, lsl #16
add r5, r5, lr, asr #17
stmia r0!,{r3-r6}
bpl m16_32_loop
m16_32_end:
tst r2, #2
beq m16_32_no_unal2
ldr r5, [r1], #4
ldmia r0, {r3,r4}
mov r12,r5, lsl #16
add r3, r3, r12,asr #17
add r4, r4, r5, asr #17
stmia r0!,{r3,r4}
m16_32_no_unal2:
tst r2, #1
ldmeqfd sp!, {r4-r6,pc}
ldrsh r4, [r1], #2
ldr r3, [r0]
add r3, r3, r4, asr #1
str r3, [r0], #4
ldmfd sp!, {r4-r6,lr}
bx lr
.global mix_16h_to_32_s1 @ int *dest, short *src, int count
mix_16h_to_32_s1:
stmfd sp!, {r4-r6,lr}
subs r2, r2, #4
bmi m16_32_s1_end
m16_32_s1_loop:
ldmia r0, {r3-r6}
ldr r12,[r1], #8
ldr lr, [r1], #8
subs r2, r2, #4
add r4, r4, r12,asr #17
mov r12,r12,lsl #16
add r3, r3, r12,asr #17 @ we use half volume
add r6, r6, lr, asr #17
mov lr, lr, lsl #16
add r5, r5, lr, asr #17
stmia r0!,{r3-r6}
bpl m16_32_s1_loop
m16_32_s1_end:
tst r2, #2
beq m16_32_s1_no_unal2
ldr r5, [r1], #8
ldmia r0, {r3,r4}
mov r12,r5, lsl #16
add r3, r3, r12,asr #17
add r4, r4, r5, asr #17
stmia r0!,{r3,r4}
m16_32_s1_no_unal2:
tst r2, #1
ldmeqfd sp!, {r4-r6,pc}
ldrsh r4, [r1], #2
ldr r3, [r0]
add r3, r3, r4, asr #1
str r3, [r0], #4
ldmfd sp!, {r4-r6,lr}
bx lr
.global mix_16h_to_32_s2 @ int *dest, short *src, int count
mix_16h_to_32_s2:
stmfd sp!, {r4-r6,lr}
subs r2, r2, #4
bmi m16_32_s2_end
m16_32_s2_loop:
ldmia r0, {r3-r6}
ldr r12,[r1], #16
ldr lr, [r1], #16
subs r2, r2, #4
add r4, r4, r12,asr #17
mov r12,r12,lsl #16
add r3, r3, r12,asr #17 @ we use half volume
add r6, r6, lr, asr #17
mov lr, lr, lsl #16
add r5, r5, lr, asr #17
stmia r0!,{r3-r6}
bpl m16_32_s2_loop
m16_32_s2_end:
tst r2, #2
beq m16_32_s2_no_unal2
ldr r5, [r1], #16
ldmia r0, {r3,r4}
mov r12,r5, lsl #16
add r3, r3, r12,asr #17
add r4, r4, r5, asr #17
stmia r0!,{r3,r4}
m16_32_s2_no_unal2:
tst r2, #1
ldmeqfd sp!, {r4-r6,pc}
ldrsh r4, [r1], #2
ldr r3, [r0]
add r3, r3, r4, asr #1
str r3, [r0], #4
ldmfd sp!, {r4-r6,lr}
bx lr
@ limit
@ reg=int_sample, lr=1, r3=tmp, kills flags
.macro Limit reg
add r3, lr, \reg, asr #15
bics r3, r3, #1 @ in non-overflow conditions r3 is 0 or 1
movne \reg, #0x8000
subpl \reg, \reg, #1
.endm
@ limit and shift up by 16
@ reg=int_sample, lr=1, r3=tmp, kills flags
.macro Limitsh reg
@ movs r4, r3, asr #16
@ cmnne r4, #1
@ beq c32_16_no_overflow
@ tst r4, r4
@ mov r3, #0x8000
@ subpl r3, r3, #1
add r3, lr, \reg, asr #15
bics r3, r3, #1 @ in non-overflow conditions r3 is 0 or 1
moveq \reg, \reg, lsl #16
movne \reg, #0x80000000
subpl \reg, \reg, #0x00010000
.endm
@ mix 32bit audio (with 16bits really used, upper bits indicate overflow) with normal 16 bit audio with left channel only
@ warning: this function assumes dest is word aligned
.global mix_32_to_16l_stereo @ short *dest, int *src, int count
mix_32_to_16l_stereo:
stmfd sp!, {r4-r8,lr}
mov lr, #1
mov r2, r2, lsl #1
subs r2, r2, #4
bmi m32_16l_st_end
m32_16l_st_loop:
ldmia r0, {r8,r12}
ldmia r1!, {r4-r7}
mov r8, r8, lsl #16
mov r12,r12,lsl #16
add r4, r4, r8, asr #16
add r5, r5, r8, asr #16
add r6, r6, r12,asr #16
add r7, r7, r12,asr #16
Limitsh r4
Limitsh r5
Limitsh r6
Limitsh r7
subs r2, r2, #4
orr r4, r5, r4, lsr #16
orr r5, r7, r6, lsr #16
stmia r0!, {r4,r5}
bpl m32_16l_st_loop
m32_16l_st_end:
@ check for remaining bytes to convert
tst r2, #2
beq m32_16l_st_no_unal2
ldrsh r6, [r0]
ldmia r1!,{r4,r5}
add r4, r4, r6
add r5, r5, r6
Limitsh r4
Limitsh r5
orr r4, r5, r4, lsr #16
str r4, [r0], #4
m32_16l_st_no_unal2:
ldmfd sp!, {r4-r8,lr}
bx lr
@ mix 32bit audio (with 16bits really used, upper bits indicate overflow) with normal 16 bit audio (for mono sound)
.global mix_32_to_16_mono @ short *dest, int *src, int count
mix_32_to_16_mono:
stmfd sp!, {r4-r8,lr}
mov lr, #1
@ check if dest is word aligned
tst r0, #2
beq m32_16_mo_no_unalw
ldrsh r5, [r0]
ldr r4, [r1], #4
sub r2, r2, #1
add r4, r4, r5
Limit r4
strh r4, [r0], #2
m32_16_mo_no_unalw:
subs r2, r2, #4
bmi m32_16_mo_end
m32_16_mo_loop:
ldmia r0, {r8,r12}
ldmia r1!, {r4-r7}
add r5, r5, r8, asr #16
mov r8, r8, lsl #16
add r4, r4, r8, asr #16
add r7, r7, r12,asr #16
mov r12,r12,lsl #16
add r6, r6, r12,asr #16
Limitsh r4
Limitsh r5
Limitsh r6
Limitsh r7
subs r2, r2, #4
orr r4, r5, r4, lsr #16
orr r5, r7, r6, lsr #16
stmia r0!, {r4,r5}
bpl m32_16_mo_loop
m32_16_mo_end:
@ check for remaining bytes to convert
tst r2, #2
beq m32_16_mo_no_unal2
ldr r6, [r0]
ldmia r1!,{r4,r5}
add r5, r5, r6, asr #16
mov r6, r6, lsl #16
add r4, r4, r6, asr #16
Limitsh r4
Limitsh r5
orr r4, r5, r4, lsr #16
str r4, [r0], #4
m32_16_mo_no_unal2:
tst r2, #1
ldmeqfd sp!, {r4-r8,pc}
ldrsh r5, [r0]
ldr r4, [r1], #4
add r4, r4, r5
Limit r4
strh r4, [r0], #2
ldmfd sp!, {r4-r8,lr}
bx lr
.data
.align 4
.global mix_32_to_16l_level
mix_32_to_16l_level:
.word 0
.text
.align 4
@ same as mix_32_to_16l_stereo, but with additional shift
.global mix_32_to_16l_stereo_lvl @ short *dest, int *src, int count
mix_32_to_16l_stereo_lvl:
stmfd sp!, {r4-r9,lr}
ldr r9, =mix_32_to_16l_level
mov lr, #1
ldr r9, [r9]
mov r2, r2, lsl #1
subs r2, r2, #4
bmi m32_16l_st_l_end
m32_16l_st_l_loop:
ldmia r0, {r8,r12}
ldmia r1!, {r4-r7}
mov r8, r8, lsl #16
mov r12,r12,lsl #16
add r4, r4, r8, asr #16
add r5, r5, r8, asr #16
add r6, r6, r12,asr #16
add r7, r7, r12,asr #16
mov r4, r4, asr r9
mov r5, r5, asr r9
mov r6, r6, asr r9
mov r7, r7, asr r9
Limitsh r4
Limitsh r5
Limitsh r6
Limitsh r7
subs r2, r2, #4
orr r4, r5, r4, lsr #16
orr r5, r7, r6, lsr #16
stmia r0!, {r4,r5}
bpl m32_16l_st_l_loop
m32_16l_st_l_end:
@ check for remaining bytes to convert
tst r2, #2
beq m32_16l_st_l_no_unal2
ldrsh r6, [r0]
ldmia r1!,{r4,r5}
add r4, r4, r6
add r5, r5, r6
mov r4, r4, asr r9
mov r5, r5, asr r9
Limitsh r4
Limitsh r5
orr r4, r5, r4, lsr #16
str r4, [r0], #4
m32_16l_st_l_no_unal2:
ldmfd sp!, {r4-r9,lr}
bx lr

344
pico/sound/sn76496.c Normal file
View file

@ -0,0 +1,344 @@
/***************************************************************************
sn76496.c
Routines to emulate the Texas Instruments SN76489 / SN76496 programmable
tone /noise generator. Also known as (or at least compatible with) TMS9919.
Noise emulation is not accurate due to lack of documentation. The noise
generator uses a shift register with a XOR-feedback network, but the exact
layout is unknown. It can be set for either period or white noise; again,
the details are unknown.
28/03/2005 : Sebastien Chevalier
Update th SN76496Write func, according to SN76489 doc found on SMSPower.
- On write with 0x80 set to 0, when LastRegister is other then TONE,
the function is similar than update with 0x80 set to 1
***************************************************************************/
#ifndef __GNUC__
#pragma warning (disable:4244)
#endif
#include "sn76496.h"
#define MAX_OUTPUT 0x47ff // was 0x7fff
#define STEP 0x10000
/* Formulas for noise generator */
/* bit0 = output */
/* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */
#define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */
/* noise feedback for periodic noise mode */
//#define FB_PNOISE 0x10000 /* 16bit rorate */
#define FB_PNOISE 0x08000 /* JH 981127 - fixes Do Run Run */
/*
0x08000 is definitely wrong. The Master System conversion of Marble Madness
uses periodic noise as a baseline. With a 15-bit rotate, the bassline is
out of tune.
The 16-bit rotate has been confirmed against a real PAL Sega Master System 2.
Hope that helps the System E stuff, more news on the PSG as and when!
*/
/* noise generator start preset (for periodic noise) */
#define NG_PRESET 0x0f35
struct SN76496
{
//sound_stream * Channel;
int SampleRate;
unsigned int UpdateStep;
int VolTable[16]; /* volume table */
int Register[8]; /* registers */
int LastRegister; /* last register written */
int Volume[4]; /* volume of voice 0-2 and noise */
unsigned int RNG; /* noise generator */
int NoiseFB; /* noise feedback mask */
int Period[4];
int Count[4];
int Output[4];
int pad[1];
};
static struct SN76496 ono_sn; // one and only SN76496
int *sn76496_regs;
//static
void SN76496Write(int data)
{
struct SN76496 *R = &ono_sn;
int n;
/* update the output buffer before changing the registers */
//stream_update(R->Channel,0);
if (data & 0x80)
{
int r = (data & 0x70) >> 4;
int c = r/2;
R->LastRegister = r;
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
switch (r)
{
case 0: /* tone 0 : frequency */
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
R->Period[c] = R->UpdateStep * R->Register[r];
if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
if (r == 4)
{
/* update noise shift frequency */
if ((R->Register[6] & 0x03) == 0x03)
R->Period[3] = 2 * R->Period[2];
}
break;
case 1: /* tone 0 : volume */
case 3: /* tone 1 : volume */
case 5: /* tone 2 : volume */
case 7: /* noise : volume */
R->Volume[c] = R->VolTable[data & 0x0f];
break;
case 6: /* noise : frequency, mode */
{
int n = R->Register[6];
R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
n &= 3;
/* N/512,N/1024,N/2048,Tone #3 output */
R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3)));
/* reset noise shifter */
R->RNG = NG_PRESET;
R->Output[3] = R->RNG & 1;
}
break;
}
}
else
{
int r = R->LastRegister;
int c = r/2;
switch (r)
{
case 0: /* tone 0 : frequency */
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);
R->Period[c] = R->UpdateStep * R->Register[r];
if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
if (r == 4)
{
/* update noise shift frequency */
if ((R->Register[6] & 0x03) == 0x03)
R->Period[3] = 2 * R->Period[2];
}
break;
case 1: /* tone 0 : volume */
case 3: /* tone 1 : volume */
case 5: /* tone 2 : volume */
case 7: /* noise : volume */
R->Volume[c] = R->VolTable[data & 0x0f];
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
break;
case 6: /* noise : frequency, mode */
{
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
n = R->Register[6];
R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
n &= 3;
/* N/512,N/1024,N/2048,Tone #3 output */
R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3)));
/* reset noise shifter */
R->RNG = NG_PRESET;
R->Output[3] = R->RNG & 1;
}
break;
}
}
}
/*
WRITE8_HANDLER( SN76496_0_w ) { SN76496Write(0,data); }
WRITE8_HANDLER( SN76496_1_w ) { SN76496Write(1,data); }
WRITE8_HANDLER( SN76496_2_w ) { SN76496Write(2,data); }
WRITE8_HANDLER( SN76496_3_w ) { SN76496Write(3,data); }
WRITE8_HANDLER( SN76496_4_w ) { SN76496Write(4,data); }
*/
//static
void SN76496Update(short *buffer, int length, int stereo)
{
int i;
struct SN76496 *R = &ono_sn;
/* If the volume is 0, increase the counter */
for (i = 0;i < 4;i++)
{
if (R->Volume[i] == 0)
{
/* note that I do count += length, NOT count = length + 1. You might think */
/* it's the same since the volume is 0, but doing the latter could cause */
/* interferencies when the program is rapidly modulating the volume. */
if (R->Count[i] <= length*STEP) R->Count[i] += length*STEP;
}
}
while (length > 0)
{
int vol[4];
unsigned int out;
int left;
/* vol[] keeps track of how long each square wave stays */
/* in the 1 position during the sample period. */
vol[0] = vol[1] = vol[2] = vol[3] = 0;
for (i = 0;i < 3;i++)
{
if (R->Output[i]) vol[i] += R->Count[i];
R->Count[i] -= STEP;
/* Period[i] is the half period of the square wave. Here, in each */
/* loop I add Period[i] twice, so that at the end of the loop the */
/* square wave is in the same status (0 or 1) it was at the start. */
/* vol[i] is also incremented by Period[i], since the wave has been 1 */
/* exactly half of the time, regardless of the initial position. */
/* If we exit the loop in the middle, Output[i] has to be inverted */
/* and vol[i] incremented only if the exit status of the square */
/* wave is 1. */
while (R->Count[i] <= 0)
{
R->Count[i] += R->Period[i];
if (R->Count[i] > 0)
{
R->Output[i] ^= 1;
if (R->Output[i]) vol[i] += R->Period[i];
break;
}
R->Count[i] += R->Period[i];
vol[i] += R->Period[i];
}
if (R->Output[i]) vol[i] -= R->Count[i];
}
left = STEP;
do
{
int nextevent;
if (R->Count[3] < left) nextevent = R->Count[3];
else nextevent = left;
if (R->Output[3]) vol[3] += R->Count[3];
R->Count[3] -= nextevent;
if (R->Count[3] <= 0)
{
if (R->RNG & 1) R->RNG ^= R->NoiseFB;
R->RNG >>= 1;
R->Output[3] = R->RNG & 1;
R->Count[3] += R->Period[3];
if (R->Output[3]) vol[3] += R->Period[3];
}
if (R->Output[3]) vol[3] -= R->Count[3];
left -= nextevent;
} while (left > 0);
out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] +
vol[2] * R->Volume[2] + vol[3] * R->Volume[3];
if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP;
if ((out /= STEP)) // will be optimized to shift; max 0x47ff = 18431
*buffer += out;
if(stereo) buffer+=2; // only left for stereo, to be mixed to right later
else buffer++;
length--;
}
}
static void SN76496_set_clock(struct SN76496 *R,int clock)
{
/* the base clock for the tone generators is the chip clock divided by 16; */
/* for the noise generator, it is clock / 256. */
/* Here we calculate the number of steps which happen during one sample */
/* at the given sample rate. No. of events = sample rate / (clock/16). */
/* STEP is a multiplier used to turn the fraction into a fixed point */
/* number. */
R->UpdateStep = ((double)STEP * R->SampleRate * 16) / clock;
}
static void SN76496_set_gain(struct SN76496 *R,int gain)
{
int i;
double out;
gain &= 0xff;
/* increase max output basing on gain (0.2 dB per step) */
out = MAX_OUTPUT / 3;
while (gain-- > 0)
out *= 1.023292992; /* = (10 ^ (0.2/20)) */
/* build volume table (2dB per step) */
for (i = 0;i < 15;i++)
{
/* limit volume to avoid clipping */
if (out > MAX_OUTPUT / 3) R->VolTable[i] = MAX_OUTPUT / 3;
else R->VolTable[i] = out;
out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
}
R->VolTable[15] = 0;
}
//static
int SN76496_init(int clock,int sample_rate)
{
struct SN76496 *R = &ono_sn;
int i;
//R->Channel = stream_create(0,1, sample_rate,R,SN76496Update);
sn76496_regs = R->Register;
R->SampleRate = sample_rate;
SN76496_set_clock(R,clock);
for (i = 0;i < 4;i++) R->Volume[i] = 0;
R->LastRegister = 0;
for (i = 0;i < 8;i+=2)
{
R->Register[i] = 0;
R->Register[i + 1] = 0x0f; /* volume = 0 */
}
for (i = 0;i < 4;i++)
{
R->Output[i] = 0;
R->Period[i] = R->Count[i] = R->UpdateStep;
}
R->RNG = NG_PRESET;
R->Output[3] = R->RNG & 1;
// added
SN76496_set_gain(R, 0);
return 0;
}

8
pico/sound/sn76496.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef SN76496_H
#define SN76496_H
void SN76496Write(int data);
void SN76496Update(short *buffer,int length,int stereo);
int SN76496_init(int clock,int sample_rate);
#endif

631
pico/sound/sound.c Normal file
View file

@ -0,0 +1,631 @@
// This is part of Pico Library
// (c) Copyright 2004 Dave, All rights reserved.
// (c) Copyright 2006,2007 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include <string.h>
#include "ym2612.h"
#include "sn76496.h"
#include "../pico_int.h"
#include "../cd/pcm.h"
#include "mix.h"
void (*PsndMix_32_to_16l)(short *dest, int *src, int count) = mix_32_to_16l_stereo;
// master int buffer to mix to
static int PsndBuffer[2*44100/50];
// dac
static unsigned short dac_info[312+4]; // pppppppp ppppllll, p - pos in buff, l - length to write for this sample
// cdda output buffer
short cdda_out_buffer[2*1152];
// for Pico
int PsndRate=0;
int PsndLen=0; // number of mono samples, multiply by 2 for stereo
int PsndLen_exc_add=0; // this is for non-integer sample counts per line, eg. 22050/60
int PsndLen_exc_cnt=0;
int PsndDacLine=0;
short *PsndOut=NULL; // PCM data buffer
// timers
int timer_a_next_oflow, timer_a_step; // in z80 cycles
int timer_b_next_oflow, timer_b_step;
// sn76496
extern int *sn76496_regs;
static void dac_recalculate(void)
{
int i, dac_cnt, pos, len, lines = Pico.m.pal ? 312 : 262, mid = Pico.m.pal ? 68 : 93;
if (PsndLen <= lines)
{
// shrinking algo
dac_cnt = -PsndLen;
len=1; pos=0;
dac_info[225] = 1;
for(i=226; i != 225; i++)
{
if (i >= lines) i = 0;
len = 0;
if(dac_cnt < 0) {
len=1;
pos++;
dac_cnt += lines;
}
dac_cnt -= PsndLen;
dac_info[i] = (pos<<4)|len;
}
}
else
{
// stretching
dac_cnt = PsndLen;
pos=0;
for(i = 225; i != 224; i++)
{
if (i >= lines) i = 0;
len=0;
while(dac_cnt >= 0) {
dac_cnt -= lines;
len++;
}
if (i == mid) // midpoint
while(pos+len < PsndLen/2) {
dac_cnt -= lines;
len++;
}
dac_cnt += PsndLen;
dac_info[i] = (pos<<4)|len;
pos+=len;
}
// last sample
for(len = 0, i = pos; i < PsndLen; i++) len++;
if (PsndLen_exc_add) len++;
dac_info[224] = (pos<<4)|len;
}
mid = (dac_info[lines-1] & 0xfff0) + ((dac_info[lines-1] & 0xf) << 4);
for (i = lines; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)
dac_info[i] = mid;
//for(i=len=0; i < lines; i++) {
// printf("%03i : %03i : %i\n", i, dac_info[i]>>4, dac_info[i]&0xf);
// len+=dac_info[i]&0xf;
//}
//printf("rate is %i, len %f\n", PsndRate, (double)PsndRate/(Pico.m.pal ? 50.0 : 60.0));
//printf("len total: %i, last pos: %i\n", len, pos);
//exit(8);
}
PICO_INTERNAL void PsndReset(void)
{
void *ym2612_regs;
// also clear the internal registers+addr line
ym2612_regs = YM2612GetRegs();
memset(ym2612_regs, 0, 0x200+4);
timers_reset();
PsndRerate(0);
}
// to be called after changing sound rate or chips
void PsndRerate(int preserve_state)
{
void *state = NULL;
int target_fps = Pico.m.pal ? 50 : 60;
// not all rates are supported in MCD mode due to mp3 decoder limitations
if (PicoAHW & PAHW_MCD) {
if (PsndRate != 11025 && PsndRate != 22050 && PsndRate != 44100) PsndRate = 22050;
PicoOpt |= POPT_EN_STEREO; // force stereo
}
if (preserve_state) {
state = malloc(0x200);
if (state == NULL) return;
memcpy(state, YM2612GetRegs(), 0x200);
}
YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);
if (preserve_state) {
// feed it back it's own registers, just like after loading state
memcpy(YM2612GetRegs(), state, 0x200);
ym2612_unpack_state();
if ((PicoAHW & PAHW_MCD) && !(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))
cdda_start_play();
}
if (preserve_state) memcpy(state, sn76496_regs, 28*4); // remember old state
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);
if (preserve_state) memcpy(sn76496_regs, state, 28*4); // restore old state
if (state)
free(state);
// calculate PsndLen
PsndLen=PsndRate / target_fps;
PsndLen_exc_add=((PsndRate - PsndLen*target_fps)<<16) / target_fps;
PsndLen_exc_cnt=0;
// recalculate dac info
dac_recalculate();
if (PicoAHW & PAHW_MCD)
pcm_set_rate(PsndRate);
// clear all buffers
memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);
memset(cdda_out_buffer, 0, sizeof(cdda_out_buffer));
if (PsndOut)
PsndClear();
// set mixer
PsndMix_32_to_16l = (PicoOpt & POPT_EN_STEREO) ? mix_32_to_16l_stereo : mix_32_to_16_mono;
if (PicoAHW & PAHW_PICO)
PicoReratePico();
}
PICO_INTERNAL void PsndDoDAC(int line_to)
{
int pos, pos1, len;
int dout = ym2612.dacout;
int line_from = PsndDacLine;
PsndDacLine = line_to + 1;
pos =dac_info[line_from]>>4;
pos1=dac_info[line_to];
len = ((pos1>>4)-pos) + (pos1&0xf);
if (!len) return;
if (PicoOpt & POPT_EN_STEREO) {
short *d = PsndOut + pos*2;
for (; len > 0; len--, d+=2) *d = dout;
} else {
short *d = PsndOut + pos;
for (; len > 0; len--, d++) *d = dout;
}
#if 0
if (do_pcm) {
int *d = PsndBuffer;
d += (PicoOpt&8) ? pos*2 : pos;
pcm_update(d, len, 1);
}
#endif
}
// cdda
static pm_file *cdda_stream = NULL;
static void cdda_raw_update(int *buffer, int length)
{
int ret, cdda_bytes;
if (cdda_stream == NULL) return;
cdda_bytes = length*4;
if (PsndRate <= 22050) cdda_bytes *= 2;
if (PsndRate < 22050) cdda_bytes *= 2;
ret = pm_read(cdda_out_buffer, cdda_bytes, cdda_stream);
if (ret < cdda_bytes) {
memset((char *)cdda_out_buffer + ret, 0, cdda_bytes - ret);
cdda_stream = NULL;
return;
}
// now mix
switch (PsndRate) {
case 44100: mix_16h_to_32(buffer, cdda_out_buffer, length*2); break;
case 22050: mix_16h_to_32_s1(buffer, cdda_out_buffer, length*2); break;
case 11025: mix_16h_to_32_s2(buffer, cdda_out_buffer, length*2); break;
}
}
PICO_INTERNAL void cdda_start_play(void)
{
int lba_offset, index, lba_length, i;
elprintf(EL_STATUS, "cdda play track #%i", Pico_mcd->scd.Cur_Track);
index = Pico_mcd->scd.Cur_Track - 1;
lba_offset = Pico_mcd->scd.Cur_LBA - Track_to_LBA(index + 1);
if (lba_offset < 0) lba_offset = 0;
lba_offset += Pico_mcd->TOC.Tracks[index].Offset;
// find the actual file for this track
for (i = index; i >= 0; i--)
if (Pico_mcd->TOC.Tracks[i].F != NULL) break;
if (Pico_mcd->TOC.Tracks[i].F == NULL) {
elprintf(EL_STATUS|EL_ANOMALY, "no track?!");
return;
}
if (Pico_mcd->TOC.Tracks[i].ftype == TYPE_MP3)
{
int pos1024 = 0;
lba_length = Pico_mcd->TOC.Tracks[i].Length;
for (i++; i < Pico_mcd->TOC.Last_Track; i++) {
if (Pico_mcd->TOC.Tracks[i].F != NULL) break;
lba_length += Pico_mcd->TOC.Tracks[i].Length;
}
if (lba_offset)
pos1024 = lba_offset * 1024 / lba_length;
mp3_start_play(Pico_mcd->TOC.Tracks[index].F, pos1024);
return;
}
cdda_stream = Pico_mcd->TOC.Tracks[i].F;
PicoCDBufferFlush(); // buffering relies on fp not being touched
pm_seek(cdda_stream, lba_offset * 2352, SEEK_SET);
if (Pico_mcd->TOC.Tracks[i].ftype == TYPE_WAV)
{
// skip headers, assume it's 44kHz stereo uncompressed
pm_seek(cdda_stream, 44, SEEK_CUR);
}
}
PICO_INTERNAL void PsndClear(void)
{
int len = PsndLen;
if (PsndLen_exc_add) len++;
if (PicoOpt & POPT_EN_STEREO)
memset32((int *) PsndOut, 0, len); // assume PsndOut to be aligned
else {
short *out = PsndOut;
if ((int)out & 2) { *out++ = 0; len--; }
memset32((int *) out, 0, len/2);
if (len & 1) out[len-1] = 0;
}
}
PICO_INTERNAL int PsndRender(int offset, int length)
{
int buf32_updated = 0;
int *buf32 = PsndBuffer+offset;
int stereo = (PicoOpt & 8) >> 3;
// emulating CD && PCM option enabled && PCM chip on && have enabled channels
int do_pcm = (PicoAHW & PAHW_MCD) && (PicoOpt&POPT_EN_MCD_PCM) &&
(Pico_mcd->pcm.control & 0x80) && Pico_mcd->pcm.enabled;
offset <<= stereo;
#if !SIMPLE_WRITE_SOUND
if (offset == 0) { // should happen once per frame
// compensate for float part of PsndLen
PsndLen_exc_cnt += PsndLen_exc_add;
if (PsndLen_exc_cnt >= 0x10000) {
PsndLen_exc_cnt -= 0x10000;
length++;
}
}
#endif
// PSG
if (PicoOpt & POPT_EN_PSG)
SN76496Update(PsndOut+offset, length, stereo);
if (PicoAHW & PAHW_PICO) {
PicoPicoPCMUpdate(PsndOut+offset, length, stereo);
return length;
}
// Add in the stereo FM buffer
if (PicoOpt & POPT_EN_FM) {
buf32_updated = YM2612UpdateOne(buf32, length, stereo, 1);
} else
memset32(buf32, 0, length<<stereo);
//printf("active_chs: %02x\n", buf32_updated);
// CD: PCM sound
if (do_pcm) {
pcm_update(buf32, length, stereo);
//buf32_updated = 1;
}
// CD: CDDA audio
// CD mode, cdda enabled, not data track, CDC is reading
if ((PicoAHW & PAHW_MCD) && (PicoOpt & POPT_EN_MCD_CDDA) &&
!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))
{
// note: only 44, 22 and 11 kHz supported, with forced stereo
int index = Pico_mcd->scd.Cur_Track - 1;
if (Pico_mcd->TOC.Tracks[index].ftype == TYPE_MP3)
mp3_update(buf32, length, stereo);
else
cdda_raw_update(buf32, length);
}
// convert + limit to normal 16bit output
PsndMix_32_to_16l(PsndOut+offset, buf32, length);
return length;
}
// -----------------------------------------------------------------
// z80 stuff
#ifdef _USE_MZ80
// memhandlers for mz80 core
unsigned char mz80_read(UINT32 a, struct MemoryReadByte *w) { return z80_read(a); }
void mz80_write(UINT32 a, UINT8 d, struct MemoryWriteByte *w) { z80_write(d, a); }
// structures for mz80 core
static struct MemoryReadByte mz80_mem_read[]=
{
{0x0000,0xffff,mz80_read},
{(UINT32) -1,(UINT32) -1,NULL}
};
static struct MemoryWriteByte mz80_mem_write[]=
{
{0x0000,0xffff,mz80_write},
{(UINT32) -1,(UINT32) -1,NULL}
};
static struct z80PortRead mz80_io_read[] ={
{(UINT16) -1,(UINT16) -1,NULL}
};
static struct z80PortWrite mz80_io_write[]={
{(UINT16) -1,(UINT16) -1,NULL}
};
int mz80_run(int cycles)
{
int ticks_pre = mz80GetElapsedTicks(0);
mz80exec(cycles);
return mz80GetElapsedTicks(0) - ticks_pre;
}
#endif
#ifdef _USE_DRZ80
struct DrZ80 drZ80;
static unsigned int DrZ80_rebasePC(unsigned short a)
{
drZ80.Z80PC_BASE = (unsigned int) Pico.zram;
return drZ80.Z80PC_BASE + a;
}
static unsigned int DrZ80_rebaseSP(unsigned short a)
{
drZ80.Z80SP_BASE = (unsigned int) Pico.zram;
return drZ80.Z80SP_BASE + a;
}
#endif
#if defined(_USE_DRZ80) || defined(_USE_CZ80)
static unsigned char z80_in(unsigned short p)
{
elprintf(EL_ANOMALY, "Z80 port %04x read", p);
return 0xff;
}
static void z80_out(unsigned short p,unsigned char d)
{
elprintf(EL_ANOMALY, "Z80 port %04x write %02x", p, d);
}
#endif
// z80 functionality wrappers
PICO_INTERNAL void z80_init(void)
{
#ifdef _USE_MZ80
struct mz80context z80;
// z80
mz80init();
// Modify the default context
mz80GetContext(&z80);
// point mz80 stuff
z80.z80Base=Pico.zram;
z80.z80MemRead=mz80_mem_read;
z80.z80MemWrite=mz80_mem_write;
z80.z80IoRead=mz80_io_read;
z80.z80IoWrite=mz80_io_write;
mz80SetContext(&z80);
#endif
#ifdef _USE_DRZ80
memset(&drZ80, 0, sizeof(struct DrZ80));
drZ80.z80_rebasePC=DrZ80_rebasePC;
drZ80.z80_rebaseSP=DrZ80_rebaseSP;
drZ80.z80_read8 =z80_read;
drZ80.z80_read16 =NULL;
drZ80.z80_write8 =z80_write;
drZ80.z80_write16 =NULL;
drZ80.z80_in =z80_in;
drZ80.z80_out =z80_out;
drZ80.z80_irq_callback=NULL;
#endif
#ifdef _USE_CZ80
memset(&CZ80, 0, sizeof(CZ80));
Cz80_Init(&CZ80);
Cz80_Set_Fetch(&CZ80, 0x0000, 0x1fff, (UINT32)Pico.zram); // main RAM
Cz80_Set_Fetch(&CZ80, 0x2000, 0x3fff, (UINT32)Pico.zram); // mirror
Cz80_Set_ReadB(&CZ80, (UINT8 (*)(UINT32 address))z80_read); // unused (hacked in)
Cz80_Set_WriteB(&CZ80, z80_write);
Cz80_Set_INPort(&CZ80, z80_in);
Cz80_Set_OUTPort(&CZ80, z80_out);
#endif
}
PICO_INTERNAL void z80_reset(void)
{
#ifdef _USE_MZ80
mz80reset();
#endif
#ifdef _USE_DRZ80
memset(&drZ80, 0, 0x54);
drZ80.Z80F = (1<<2); // set ZFlag
drZ80.Z80F2 = (1<<2); // set ZFlag
drZ80.Z80IX = 0xFFFF << 16;
drZ80.Z80IY = 0xFFFF << 16;
drZ80.Z80IM = 0; // 1?
drZ80.z80irqvector = 0xff0000; // RST 38h
drZ80.Z80PC = drZ80.z80_rebasePC(0);
drZ80.Z80SP = drZ80.z80_rebaseSP(0x2000); // 0xf000 ?
#endif
#ifdef _USE_CZ80
Cz80_Reset(&CZ80);
Cz80_Set_Reg(&CZ80, CZ80_IX, 0xffff);
Cz80_Set_Reg(&CZ80, CZ80_IY, 0xffff);
Cz80_Set_Reg(&CZ80, CZ80_SP, 0x2000);
#endif
Pico.m.z80_fakeval = 0; // for faking when Z80 is disabled
}
#if 0
static int had_irq = 0, z80_ppc, z80_ops_done = 0;
int z80_cycles_left = 0;
void z80_int(void)
{
had_irq = 1;
}
static void xfail(void)
{
printf("PC: %04x, %04x\n", Cz80_Get_Reg(&CZ80, CZ80_PC), z80_ppc);
printf("z80_ops_done done: %i\n", z80_ops_done);
exit(1);
}
int z80_run(int cycles)
{
int fail = 0;
int cdrz, ccz;
z80_cycles_left = cycles;
z80_ppc = Cz80_Get_Reg(&CZ80, CZ80_PC);
if (had_irq) {
printf("irq @ %04x\n", Cz80_Get_Reg(&CZ80, CZ80_PC));
Cz80_Set_IRQ(&CZ80, 0, HOLD_LINE);
drZ80.Z80_IRQ = 1;
had_irq = 0;
}
while (z80_cycles_left > 0)
{
ccz = Cz80_Exec(&CZ80, 1);
cdrz = 1 - DrZ80Run(&drZ80, 1);
if (drZ80.Z80_IRQ && (drZ80.Z80IF&1))
cdrz += 1 - DrZ80Run(&drZ80, 1); // cz80 processes IRQ after EI, DrZ80 does not
if (cdrz != ccz) {
printf("cycles: %i vs %i\n", cdrz, ccz);
fail = 1;
}
if (drZ80.Z80PC - drZ80.Z80PC_BASE != Cz80_Get_Reg(&CZ80, CZ80_PC)) {
printf("PC: %04x vs %04x\n", drZ80.Z80PC - drZ80.Z80PC_BASE, Cz80_Get_Reg(&CZ80, CZ80_PC));
fail = 1;
}
if (fail) xfail();
z80_ops_done++;
z80_cycles_left -= ccz;
z80_ppc = Cz80_Get_Reg(&CZ80, CZ80_PC);
}
return co - z80_cycles_left;
}
#endif
PICO_INTERNAL void z80_pack(unsigned char *data)
{
#if defined(_USE_MZ80)
struct mz80context mz80;
*(int *)data = 0x00005A6D; // "mZ"
mz80GetContext(&mz80);
memcpy(data+4, &mz80.z80clockticks, sizeof(mz80)-5*4); // don't save base&memhandlers
#elif defined(_USE_DRZ80)
*(int *)data = 0x015A7244; // "DrZ" v1
drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE);
drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE);
memcpy(data+4, &drZ80, 0x54);
#elif defined(_USE_CZ80)
*(int *)data = 0x00007a43; // "Cz"
*(int *)(data+4) = Cz80_Get_Reg(&CZ80, CZ80_PC);
memcpy(data+8, &CZ80, (INT32)&CZ80.BasePC - (INT32)&CZ80);
#endif
}
PICO_INTERNAL void z80_unpack(unsigned char *data)
{
#if defined(_USE_MZ80)
if (*(int *)data == 0x00005A6D) { // "mZ" save?
struct mz80context mz80;
mz80GetContext(&mz80);
memcpy(&mz80.z80clockticks, data+4, sizeof(mz80)-5*4);
mz80SetContext(&mz80);
} else {
z80_reset();
z80_int();
}
#elif defined(_USE_DRZ80)
if (*(int *)data == 0x015A7244) { // "DrZ" v1 save?
memcpy(&drZ80, data+4, 0x54);
// update bases
drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE);
drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE);
} else {
z80_reset();
drZ80.Z80IM = 1;
z80_int(); // try to goto int handler, maybe we won't execute trash there?
}
#elif defined(_USE_CZ80)
if (*(int *)data == 0x00007a43) { // "Cz" save?
memcpy(&CZ80, data+8, (INT32)&CZ80.BasePC - (INT32)&CZ80);
Cz80_Set_Reg(&CZ80, CZ80_PC, *(int *)(data+4));
} else {
z80_reset();
z80_int();
}
#endif
}
PICO_INTERNAL void z80_exit(void)
{
#if defined(_USE_MZ80)
mz80shutdown();
#endif
}
#if 1 // defined(__DEBUG_PRINT) || defined(__GP2X__) || defined(__GIZ__)
PICO_INTERNAL void z80_debug(char *dstr)
{
#if defined(_USE_DRZ80)
sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", drZ80.Z80PC-drZ80.Z80PC_BASE, drZ80.Z80SP-drZ80.Z80SP_BASE);
#elif defined(_USE_CZ80)
sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", CZ80.PC - CZ80.BasePC, CZ80.SP.W);
#endif
}
#endif

2057
pico/sound/ym2612.c Normal file

File diff suppressed because it is too large Load diff

192
pico/sound/ym2612.h Normal file
View file

@ -0,0 +1,192 @@
/*
header file for software emulation for FM sound generator
*/
#ifndef _H_FM_FM_
#define _H_FM_FM_
/* compiler dependence */
#ifndef UINT8
typedef unsigned char UINT8; /* unsigned 8bit */
typedef unsigned short UINT16; /* unsigned 16bit */
typedef unsigned int UINT32; /* unsigned 32bit */
#endif
#ifndef INT8
typedef signed char INT8; /* signed 8bit */
typedef signed short INT16; /* signed 16bit */
typedef signed int INT32; /* signed 32bit */
#endif
#if 1
/* struct describing a single operator (SLOT) */
typedef struct
{
INT32 *DT; /* #0x00 detune :dt_tab[DT] */
UINT8 ar; /* #0x04 attack rate */
UINT8 d1r; /* #0x05 decay rate */
UINT8 d2r; /* #0x06 sustain rate */
UINT8 rr; /* #0x07 release rate */
UINT32 mul; /* #0x08 multiple :ML_TABLE[ML] */
/* Phase Generator */
UINT32 phase; /* #0x0c phase counter | need_save */
UINT32 Incr; /* #0x10 phase step */
UINT8 KSR; /* #0x14 key scale rate :3-KSR */
UINT8 ksr; /* #0x15 key scale rate :kcode>>(3-KSR) */
UINT8 key; /* #0x16 0=last key was KEY OFF, 1=KEY ON */
/* Envelope Generator */
UINT8 state; /* #0x17 phase type: EG_OFF=0, EG_REL, EG_SUS, EG_DEC, EG_ATT | need_save */
UINT16 tl; /* #0x18 total level: TL << 3 */
INT16 volume; /* #0x1a envelope counter | need_save */
UINT32 sl; /* #0x1c sustain level:sl_table[SL] */
UINT32 eg_pack_ar; /* #0x20 (attack state) */
UINT32 eg_pack_d1r; /* #0x24 (decay state) */
UINT32 eg_pack_d2r; /* #0x28 (sustain state) */
UINT32 eg_pack_rr; /* #0x2c (release state) */
} FM_SLOT;
typedef struct
{
FM_SLOT SLOT[4]; /* four SLOTs (operators) */
UINT8 ALGO; /* +00 algorithm */
UINT8 FB; /* feedback shift */
UINT8 pad[2];
INT32 op1_out; /* op1 output for feedback */
INT32 mem_value; /* +08 delayed sample (MEM) value */
INT32 pms; /* channel PMS */
UINT8 ams; /* channel AMS */
UINT8 kcode; /* +11 key code: */
UINT8 fn_h; /* freq latch */
UINT8 pad2;
UINT32 fc; /* fnum,blk:adjusted to sample rate */
UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
/* LFO */
UINT8 AMmasks; /* AM enable flag */
UINT8 pad3[3];
} FM_CH;
typedef struct
{
int clock; /* master clock (Hz) */
int rate; /* sampling rate (Hz) */
double freqbase; /* 08 frequency base */
UINT8 address; /* 10 address register | need_save */
UINT8 status; /* 11 status flag | need_save */
UINT8 mode; /* mode CSM / 3SLOT */
UINT8 pad;
int TA; /* timer a */
int TAC; /* timer a maxval */
int TAT; /* timer a ticker | need_save */
UINT8 TB; /* timer b */
UINT8 pad2[3];
int TBC; /* timer b maxval */
int TBT; /* timer b ticker | need_save */
/* local time tables */
INT32 dt_tab[8][32];/* DeTune table */
} FM_ST;
/***********************************************************/
/* OPN unit */
/***********************************************************/
/* OPN 3slot struct */
typedef struct
{
UINT32 fc[3]; /* fnum3,blk3: calculated */
UINT8 fn_h; /* freq3 latch */
UINT8 kcode[3]; /* key code */
UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
} FM_3SLOT;
/* OPN/A/B common state */
typedef struct
{
FM_ST ST; /* general state */
FM_3SLOT SL3; /* 3 slot mode state */
UINT32 pan; /* fm channels output mask (bit 1 = enable) */
UINT32 eg_cnt; /* global envelope generator counter | need_save */
UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 | need_save */
UINT32 eg_timer_add; /* step of eg_timer */
/* LFO */
UINT32 lfo_cnt; /* need_save */
UINT32 lfo_inc;
UINT32 lfo_freq[8]; /* LFO FREQ table */
} FM_OPN;
/* here's the virtual YM2612 */
typedef struct
{
UINT8 REGS[0x200]; /* registers (for save states) */
INT32 addr_A1; /* address line A1 | need_save */
FM_CH CH[6]; /* channel state */
/* dac output (YM2612) */
int dacen;
INT32 dacout;
FM_OPN OPN; /* OPN state */
UINT32 slot_mask; /* active slot mask (performance hack) */
} YM2612;
#endif
#ifndef EXTERNAL_YM2612
extern YM2612 ym2612;
#endif
void YM2612Init_(int baseclock, int rate);
void YM2612ResetChip_(void);
int YM2612UpdateOne_(int *buffer, int length, int stereo, int is_buf_empty);
int YM2612Write_(unsigned int a, unsigned int v);
//unsigned char YM2612Read_(void);
int YM2612PicoTick_(int n);
void YM2612PicoStateLoad_(void);
void *YM2612GetRegs(void);
void YM2612PicoStateSave2(int tat, int tbt);
int YM2612PicoStateLoad2(int *tat, int *tbt);
#ifndef __GP2X__
#define YM2612Init YM2612Init_
#define YM2612ResetChip YM2612ResetChip_
#define YM2612UpdateOne YM2612UpdateOne_
#define YM2612PicoStateLoad YM2612PicoStateLoad_
#else
/* GP2X specific */
#include "../../platform/gp2x/940ctl.h"
extern int PicoOpt;
#define YM2612Init(baseclock,rate) { \
if (PicoOpt&0x200) YM2612Init_940(baseclock, rate); \
else YM2612Init_(baseclock, rate); \
}
#define YM2612ResetChip() { \
if (PicoOpt&0x200) YM2612ResetChip_940(); \
else YM2612ResetChip_(); \
}
#define YM2612UpdateOne(buffer,length,stereo,is_buf_empty) \
(PicoOpt&0x200) ? YM2612UpdateOne_940(buffer, length, stereo, is_buf_empty) : \
YM2612UpdateOne_(buffer, length, stereo, is_buf_empty);
#define YM2612PicoStateLoad() { \
if (PicoOpt&0x200) YM2612PicoStateLoad_940(); \
else YM2612PicoStateLoad_(); \
}
#endif /* __GP2X__ */
#endif /* _H_FM_FM_ */

919
pico/sound/ym2612_arm.s Normal file
View file

@ -0,0 +1,919 @@
@ this is a rewrite of MAME's ym2612 code, in particular this is only the main sample-generatin loop.
@ it does not seem to give much performance increase (if any at all), so don't use it if it causes trouble.
@ - notaz, 2006
@ vim:filetype=armasm
.equiv SLOT1, 0
.equiv SLOT2, 2
.equiv SLOT3, 1
.equiv SLOT4, 3
.equiv SLOT_STRUCT_SIZE, 0x30
.equiv TL_TAB_LEN, 0x1A00
.equiv EG_ATT, 4
.equiv EG_DEC, 3
.equiv EG_SUS, 2
.equiv EG_REL, 1
.equiv EG_OFF, 0
.equiv EG_SH, 16 @ 16.16 fixed point (envelope generator timing)
.equiv EG_TIMER_OVERFLOW, (3*(1<<EG_SH)) @ envelope generator timer overflows every 3 samples (on real chip)
.equiv LFO_SH, 25 /* 7.25 fixed point (LFO calculations) */
.equiv ENV_QUIET, (2*13*256/8)/2
@ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
@ writes output to routp, but only if vol_out changes
.macro update_eg_phase_slot slot
ldrb r2, [r5,#0x17] @ state
mov r3, #1 @ 1ci
cmp r2, #1
blt 5f @ EG_OFF
beq 3f @ EG_REL
cmp r2, #3
blt 2f @ EG_SUS
beq 1f @ EG_DEC
0: @ EG_ATT
ldr r2, [r5,#0x20] @ eg_pack_ar (1ci)
mov r0, r2, lsr #24
mov r3, r3, lsl r0
sub r3, r3, #1
tst r1, r3
bne 5f @ do smth for tl problem (set on init?)
mov r3, r1, lsr r0
ldrh r0, [r5,#0x1a] @ volume, unsigned (0-1023)
and r3, r3, #7
add r3, r3, r3, lsl #1
mov r3, r2, lsr r3
and r3, r3, #7 @ shift for eg_inc calculation
mvn r2, r0
mov r2, r2, lsl r3
add r0, r0, r2, asr #5
cmp r0, #0 @ if (volume <= MIN_ATT_INDEX)
movle r3, #EG_DEC
strleb r3, [r5,#0x17] @ state
movle r0, #0
b 4f
1: @ EG_DEC
ldr r2, [r5,#0x24] @ eg_pack_d1r (1ci)
mov r0, r2, lsr #24
mov r3, r3, lsl r0
sub r3, r3, #1
tst r1, r3
bne 5f @ do smth for tl problem (set on init?)
mov r3, r1, lsr r0
ldrh r0, [r5,#0x1a] @ volume
and r3, r3, #7
add r3, r3, r3, lsl #1
mov r3, r2, lsr r3
and r3, r3, #7 @ shift for eg_inc calculation
mov r2, #1
mov r3, r2, lsl r3
ldr r2, [r5,#0x1c] @ sl (can be 16bit?)
add r0, r0, r3, asr #1
cmp r0, r2 @ if ( volume >= (INT32) SLOT->sl )
movge r3, #EG_SUS
strgeb r3, [r5,#0x17] @ state
b 4f
2: @ EG_SUS
ldr r2, [r5,#0x28] @ eg_pack_d2r (1ci)
mov r0, r2, lsr #24
mov r3, r3, lsl r0
sub r3, r3, #1
tst r1, r3
bne 5f @ do smth for tl problem (set on init?)
mov r3, r1, lsr r0
ldrh r0, [r5,#0x1a] @ volume
and r3, r3, #7
add r3, r3, r3, lsl #1
mov r3, r2, lsr r3
and r3, r3, #7 @ shift for eg_inc calculation
mov r2, #1
mov r3, r2, lsl r3
add r0, r0, r3, asr #1
mov r2, #1024
sub r2, r2, #1 @ r2 = MAX_ATT_INDEX
cmp r0, r2 @ if ( volume >= MAX_ATT_INDEX )
movge r0, r2
b 4f
3: @ EG_REL
ldr r2, [r5,#0x2c] @ eg_pack_rr (1ci)
mov r0, r2, lsr #24
mov r3, r3, lsl r0
sub r3, r3, #1
tst r1, r3
bne 5f @ do smth for tl problem (set on init?)
mov r3, r1, lsr r0
ldrh r0, [r5,#0x1a] @ volume
and r3, r3, #7
add r3, r3, r3, lsl #1
mov r3, r2, lsr r3
and r3, r3, #7 @ shift for eg_inc calculation
mov r2, #1
mov r3, r2, lsl r3
add r0, r0, r3, asr #1
mov r2, #1024
sub r2, r2, #1 @ r2 = MAX_ATT_INDEX
cmp r0, r2 @ if ( volume >= MAX_ATT_INDEX )
movge r0, r2
movge r3, #EG_OFF
strgeb r3, [r5,#0x17] @ state
4:
ldrh r3, [r5,#0x18] @ tl
strh r0, [r5,#0x1a] @ volume
.if \slot == SLOT1
mov r6, r6, lsr #16
add r0, r0, r3
orr r6, r0, r6, lsl #16
.elseif \slot == SLOT2
mov r6, r6, lsl #16
add r0, r0, r3
mov r0, r0, lsl #16
orr r6, r0, r6, lsr #16
.elseif \slot == SLOT3
mov r7, r7, lsr #16
add r0, r0, r3
orr r7, r0, r7, lsl #16
.elseif \slot == SLOT4
mov r7, r7, lsl #16
add r0, r0, r3
mov r0, r0, lsl #16
orr r7, r0, r7, lsr #16
.endif
5:
.endm
@ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt, r3=scratch
.macro advance_lfo_m
mov r2, r2, lsr #LFO_SH
cmp r2, r1, lsr #LFO_SH
beq 0f
and r3, r2, #0x3f
cmp r2, #0x40
rsbge r3, r3, #0x3f
bic r12,r12, #0xff000000 @ lfo_ampm &= 0xff
orr r12,r12, r3, lsl #1+24
mov r2, r2, lsr #2
cmp r2, r1, lsr #LFO_SH+2
bicne r12,r12, #0xff0000
orrne r12,r12, r2, lsl #16
0:
.endm
@ result goes to r1, trashes r2
.macro make_eg_out slot
tst r12, #8
tstne r12, #(1<<(\slot+8))
.if \slot == SLOT1
mov r1, r6, lsl #16
mov r1, r1, lsr #17
.elseif \slot == SLOT2
mov r1, r6, lsr #17
.elseif \slot == SLOT3
mov r1, r7, lsl #16
mov r1, r1, lsr #17
.elseif \slot == SLOT4
mov r1, r7, lsr #17
.endif
andne r2, r12, #0xc0
movne r2, r2, lsr #6
addne r2, r2, #24
addne r1, r1, r12, lsr r2
.endm
.macro lookup_tl r
tst \r, #0x100
eorne \r, \r, #0xff @ if (sin & 0x100) sin = 0xff - (sin&0xff);
tst \r, #0x200
and \r, \r, #0xff
orr \r, \r, r1, lsl #8
mov \r, \r, lsl #1
ldrh \r, [r3, \r] @ 2ci if ne
rsbne \r, \r, #0
.endm
@ lr=context, r12=pack (stereo, lastchan, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16])
@ r0-r2=scratch, r3=sin_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out
.macro upd_algo0_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r2, [lr, #0x18]
ldr r0, [lr, #0x38] @ mem (signed)
mov r2, r2, lsr #16
add r0, r2, r0, lsr #1
lookup_tl r0 @ r0=c2
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
movcs r2, #0
bcs 2f
ldr r2, [lr, #0x14] @ 1ci
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2 @ r2=mem
2:
str r2, [lr, #0x38] @ mem
.endm
.macro upd_algo1_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r2, [lr, #0x18]
ldr r0, [lr, #0x38] @ mem (signed)
mov r2, r2, lsr #16
add r0, r2, r0, lsr #1
lookup_tl r0 @ r0=c2
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
movcs r2, #0
bcs 2f
ldr r2, [lr, #0x14] @ 1ci
mov r2, r2, lsr #16
lookup_tl r2 @ r2=mem
2:
add r2, r2, r10, asr #16
str r2, [lr, #0x38]
.endm
.macro upd_algo2_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r2, [lr, #0x18]
ldr r0, [lr, #0x38] @ mem (signed)
mov r2, r2, lsr #16
add r0, r2, r0, lsr #1
lookup_tl r0 @ r0=c2
0:
add r0, r0, r10, asr #16
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
movcs r2, #0
bcs 2f
ldr r2, [lr, #0x14]
mov r2, r2, lsr #16 @ 1ci
lookup_tl r2 @ r2=mem
2:
str r2, [lr, #0x38] @ mem
.endm
.macro upd_algo3_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
ldr r2, [lr, #0x38] @ mem (for future)
movcs r0, r2
bcs 0f
ldr r0, [lr, #0x18] @ 1ci
mov r0, r0, lsr #16
lookup_tl r0 @ r0=c2
0:
add r0, r0, r2
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
movcs r2, #0
bcs 2f
ldr r2, [lr, #0x14]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2 @ r2=mem
2:
str r2, [lr, #0x38] @ mem
.endm
.macro upd_algo4_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r0, [lr, #0x18]
mov r0, r0, lsr #16 @ 1ci
lookup_tl r0 @ r0=c2
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
bcs 2f
ldr r2, [lr, #0x14]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2
add r0, r0, r2 @ add to smp
2:
.endm
.macro upd_algo5_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r2, [lr, #0x18]
ldr r0, [lr, #0x38] @ mem (signed)
mov r2, r2, lsr #16
add r0, r2, r0, lsr #1
lookup_tl r0 @ r0=output smp
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
bcs 1f
ldr r2, [lr, #0x1c]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2
add r0, r0, r2 @ add to smp
1: @ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
bcs 2f
ldr r2, [lr, #0x14]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2
add r0, r0, r2 @ add to smp
2:
mov r1, r10, asr #16
str r1, [lr, #0x38] @ mem
.endm
.macro upd_algo6_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r0, [lr, #0x18]
mov r0, r0, lsr #16 @ 1ci
lookup_tl r0 @ r0=output smp
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
bcs 1f
ldr r2, [lr, #0x1c]
mov r2, r2, lsr #16 @ 1ci
lookup_tl r2
add r0, r0, r2 @ add to smp
1: @ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
bcs 2f
ldr r2, [lr, #0x14]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2
add r0, r0, r2 @ add to smp
2:
.endm
.macro upd_algo7_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r0, [lr, #0x18]
mov r0, r0, lsr #16 @ 1ci
lookup_tl r0 @ r0=output smp
0:
add r0, r0, r10, asr #16
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
bcs 1f
ldr r2, [lr, #0x1c]
mov r2, r2, lsr #16 @ 1ci
lookup_tl r2
add r0, r0, r2 @ add to smp
1: @ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
bcs 2f
ldr r2, [lr, #0x14]
mov r2, r2, lsr #16 @ 1ci
lookup_tl r2
add r0, r0, r2 @ add to smp
2:
.endm
.macro upd_slot1_m
make_eg_out SLOT1
cmp r1, #ENV_QUIET
movcs r10, r10, lsl #16 @ ct->op1_out <<= 16; // op1_out0 = op1_out1; op1_out1 = 0;
bcs 0f
ands r2, r12, #0xf000
moveq r0, #0
movne r2, r2, lsr #12
addne r0, r10, r10, lsl #16
movne r0, r0, asr #16
movne r0, r0, lsl r2
ldr r2, [lr, #0x10]
mov r0, r0, lsr #16
add r0, r0, r2, lsr #16
lookup_tl r0
mov r10,r10,lsl #16 @ ct->op1_out <<= 16;
mov r0, r0, lsl #16
orr r10,r10, r0, lsr #16
0:
.endm
/*
.global update_eg_phase @ FM_SLOT *SLOT, UINT32 eg_cnt
update_eg_phase:
stmfd sp!, {r5,r6}
mov r5, r0 @ slot
ldrh r3, [r5,#0x18] @ tl
ldrh r6, [r5,#0x1a] @ volume
add r6, r6, r3
update_eg_phase_slot SLOT1
mov r0, r6
ldmfd sp!, {r5,r6}
bx lr
.pool
.global advance_lfo @ int lfo_ampm, UINT32 lfo_cnt_old, UINT32 lfo_cnt
advance_lfo:
mov r12, r0, lsl #16
advance_lfo_m
mov r0, r12, lsr #16
bx lr
.pool
.global upd_algo0 @ chan_rend_context *c
upd_algo0:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_algo0_m
ldmfd sp!, {r4-r10,pc}
.pool
.global upd_algo1 @ chan_rend_context *c
upd_algo1:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_algo1_m
ldmfd sp!, {r4-r10,pc}
.pool
.global upd_algo2 @ chan_rend_context *c
upd_algo2:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_algo2_m
ldmfd sp!, {r4-r10,pc}
.pool
.global upd_algo3 @ chan_rend_context *c
upd_algo3:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_algo3_m
ldmfd sp!, {r4-r10,pc}
.pool
.global upd_algo4 @ chan_rend_context *c
upd_algo4:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_algo4_m
ldmfd sp!, {r4-r10,pc}
.pool
.global upd_algo5 @ chan_rend_context *c
upd_algo5:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_algo5_m
ldmfd sp!, {r4-r10,pc}
.pool
.global upd_algo6 @ chan_rend_context *c
upd_algo6:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_algo6_m
ldmfd sp!, {r4-r10,pc}
.pool
.global upd_algo7 @ chan_rend_context *c
upd_algo7:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_algo7_m
ldmfd sp!, {r4-r10,pc}
.pool
.global upd_slot1 @ chan_rend_context *c
upd_slot1:
stmfd sp!, {r4-r10,lr}
mov lr, r0
ldr r3, =ym_sin_tab
ldr r5, =ym_tl_tab
ldmia lr, {r6-r7}
ldr r10, [lr, #0x54]
ldr r12, [lr, #0x4c]
upd_slot1_m
str r10, [lr, #0x38]
ldmfd sp!, {r4-r10,pc}
.pool
*/
@ lr=context, r12=pack (stereo, lastchan, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16])
@ r0-r2=scratch, r3=sin_tab/scratch, r4=(length<<8)|unused[4],was_update,algo[3], r5=tl_tab/slot,
@ r6-r7=vol_out[4], r8=eg_timer, r9=eg_timer_add[31:16], r10=op1_out, r11=buffer
.global chan_render_loop @ chan_rend_context *ct, int *buffer, int length
chan_render_loop:
stmfd sp!, {r4-r11,lr}
mov lr, r0
mov r4, r2, lsl #8 @ no more 24 bits here
ldr r12, [lr, #0x4c]
ldr r0, [lr, #0x50]
mov r11, r1
and r0, r0, #7
orr r4, r4, r0 @ (length<<8)|algo
add r0, lr, #0x44
ldmia r0, {r8,r9} @ eg_timer, eg_timer_add
ldr r10, [lr, #0x54] @ op1_out
ldmia lr, {r6,r7} @ load volumes
tst r12, #8 @ lfo?
beq crl_loop
crl_loop_lfo:
add r0, lr, #0x30
ldmia r0, {r1,r2}
add r2, r2, r1
str r2, [lr, #0x30]
@ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt
advance_lfo_m
crl_loop:
subs r4, r4, #0x100
bmi crl_loop_end
@ -- EG --
add r8, r8, r9
cmp r8, #EG_TIMER_OVERFLOW
bcc eg_done
add r0, lr, #0x3c
ldmia r0, {r1,r5} @ eg_cnt, CH
eg_loop:
sub r8, r8, #EG_TIMER_OVERFLOW
add r1, r1, #1
@ SLOT1 (0)
@ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
update_eg_phase_slot SLOT1
add r5, r5, #SLOT_STRUCT_SIZE*2 @ SLOT2 (2)
update_eg_phase_slot SLOT2
sub r5, r5, #SLOT_STRUCT_SIZE @ SLOT3 (1)
update_eg_phase_slot SLOT3
add r5, r5, #SLOT_STRUCT_SIZE*2 @ SLOT4 (3)
update_eg_phase_slot SLOT4
cmp r8, #EG_TIMER_OVERFLOW
subcs r5, r5, #SLOT_STRUCT_SIZE*3
bcs eg_loop
str r1, [lr, #0x3c]
eg_done:
@ -- disabled? --
and r0, r12, #0xC
cmp r0, #0xC
beq crl_loop_lfo
cmp r0, #0x4
beq crl_loop
@ -- SLOT1 --
ldr r3, =ym_tl_tab
@ lr=context, r12=pack (stereo, lastchan, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16])
@ r0-r2=scratch, r3=tl_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out
upd_slot1_m
@ -- SLOT2+ --
and r0, r4, #7
ldr pc, [pc, r0, lsl #2]
nop
.word crl_algo0
.word crl_algo1
.word crl_algo2
.word crl_algo3
.word crl_algo4
.word crl_algo5
.word crl_algo6
.word crl_algo7
.pool
crl_algo0:
upd_algo0_m
b crl_algo_done
.pool
crl_algo1:
upd_algo1_m
b crl_algo_done
.pool
crl_algo2:
upd_algo2_m
b crl_algo_done
.pool
crl_algo3:
upd_algo3_m
b crl_algo_done
.pool
crl_algo4:
upd_algo4_m
b crl_algo_done
.pool
crl_algo5:
upd_algo5_m
b crl_algo_done
.pool
crl_algo6:
upd_algo6_m
b crl_algo_done
.pool
crl_algo7:
upd_algo7_m
.pool
crl_algo_done:
@ -- WRITE SAMPLE --
tst r0, r0
beq ctl_sample_skip
orr r4, r4, #8 @ have_output
tst r12, #1
beq ctl_sample_mono
tst r12, #0x20 @ L
ldrne r1, [r11]
addeq r11, r11, #4
addne r1, r0, r1
strne r1, [r11], #4
tst r12, #0x10 @ R
ldrne r1, [r11]
addeq r11, r11, #4
addne r1, r0, r1
strne r1, [r11], #4
b crl_do_phase
ctl_sample_skip:
and r1, r12, #1
add r1, r1, #1
add r11,r11, r1, lsl #2
b crl_do_phase
ctl_sample_mono:
ldr r1, [r11]
add r1, r0, r1
str r1, [r11], #4
crl_do_phase:
@ -- PHASE UPDATE --
add r5, lr, #0x10
ldmia r5, {r0-r1}
add r5, lr, #0x20
ldmia r5, {r2-r3}
add r5, lr, #0x10
add r0, r0, r2
add r1, r1, r3
stmia r5!,{r0-r1}
ldmia r5, {r0-r1}
add r5, lr, #0x28
ldmia r5, {r2-r3}
add r5, lr, #0x18
add r0, r0, r2
add r1, r1, r3
stmia r5, {r0-r1}
tst r12, #8
bne crl_loop_lfo
b crl_loop
crl_loop_end:
str r8, [lr, #0x44] @ eg_timer
str r12, [lr, #0x4c] @ pack (for lfo_ampm)
str r4, [lr, #0x50] @ was_update
str r10, [lr, #0x54] @ op1_out
ldmfd sp!, {r4-r11,pc}
.pool

108
pico/utils.c Normal file
View file

@ -0,0 +1,108 @@
// 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_int.h"
int PicuAnd=0xf7de;
// Quick low-quality conversion of 320 to 176:
int PicuQuick(unsigned short *dest,unsigned short *src)
{
unsigned short *end=NULL;
src+=13; end=src+290;
dest++;
do
{
*dest++=*src++;
*dest++=*src; src+=2;
*dest++=*src; src+=2;
*dest++=*src++;
*dest++=*src; src+=2;
*dest++=*src; src+=2;
}
while (src<end);
return 0;
}
// Shrink the pixels in src/srcLen, to the screen line pointed to by dest/destLen
int PicuShrink(unsigned short *dest,int destLen,unsigned short *src,int srcLen)
{
unsigned short *end=NULL;
int bias=0,pa=0,sub=0;
end=dest+destLen;
sub=srcLen-destLen;
do
{
pa=*src++; bias-=sub;
if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; }
*dest++=(unsigned short)pa;
pa=*src++; bias-=sub;
if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; }
*dest++=(unsigned short)pa;
}
while (dest<end);
return 0;
}
// same thing, only reversed (dest is in pre-decremental mode)
int PicuShrinkReverse(unsigned short *dest,int destLen,unsigned short *src,int srcLen)
{
unsigned short *end=NULL;
int bias=0,pa=0,sub=0;
end=dest-destLen;
sub=srcLen-destLen;
do
{
pa=*src++; bias-=sub;
if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; }
*(--dest)=(unsigned short)pa;
pa=*src++; bias-=sub;
if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; }
*(--dest)=(unsigned short)pa;
}
while (dest>end);
return 0;
}
int PicuMerge(unsigned short *dest,int destLen,unsigned short *src,int srcLen)
{
unsigned short *end=NULL;
int bias=0,pa=0,mask=PicuAnd,sub=0;
end=dest+destLen;
sub=srcLen-destLen;
do
{
pa=*src++; bias-=sub;
if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; }
pa&=mask; pa+=(*dest)&mask; pa>>=1;
*dest++=(unsigned short)pa;
pa=*src++; bias-=sub;
if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; }
pa&=mask; pa+=(*dest)&mask; pa>>=1;
*dest++=(unsigned short)pa;
}
while (dest<end);
return 0;
}

547
pico/videoport.c Normal file
View file

@ -0,0 +1,547 @@
// PicoDrive
// (c) Copyright 2004 Dave, All rights reserved.
// (c) Copyright 2006-2008, Grazvydas "notaz" Ignotas
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include "pico_int.h"
#include "cd/gfx_cd.h"
extern const unsigned char hcounts_32[];
extern const unsigned char hcounts_40[];
#ifndef UTYPES_DEFINED
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define UTYPES_DEFINED
#endif
int (*PicoDmaHook)(unsigned int source, int len, unsigned short **srcp, unsigned short **limitp) = NULL;
static __inline void AutoIncrement(void)
{
Pico.video.addr=(unsigned short)(Pico.video.addr+Pico.video.reg[0xf]);
}
static void VideoWrite(u16 d)
{
unsigned int a=Pico.video.addr;
switch (Pico.video.type)
{
case 1: if(a&1) d=(u16)((d<<8)|(d>>8)); // If address is odd, bytes are swapped (which game needs this?)
Pico.vram [(a>>1)&0x7fff]=d;
if (a - ((unsigned)(Pico.video.reg[5]&0x7f) << 9) < 0x400)
rendstatus |= PDRAW_DIRTY_SPRITES;
break;
case 3: Pico.m.dirtyPal = 1;
Pico.cram [(a>>1)&0x003f]=d; break; // wraps (Desert Strike)
case 5: Pico.vsram[(a>>1)&0x003f]=d; break;
//default:elprintf(EL_ANOMALY, "VDP write %04x with bad type %i", d, Pico.video.type); break;
}
AutoIncrement();
}
static unsigned int VideoRead(void)
{
unsigned int a=0,d=0;
a=Pico.video.addr; a>>=1;
switch (Pico.video.type)
{
case 0: d=Pico.vram [a&0x7fff]; break;
case 8: d=Pico.cram [a&0x003f]; break;
case 4: d=Pico.vsram[a&0x003f]; break;
default:elprintf(EL_ANOMALY, "VDP read with bad type %i", Pico.video.type); break;
}
AutoIncrement();
return d;
}
static int GetDmaLength(void)
{
struct PicoVideo *pvid=&Pico.video;
int len=0;
// 16-bit words to transfer:
len =pvid->reg[0x13];
len|=pvid->reg[0x14]<<8;
// Charles MacDonald:
if(!len) len = 0xffff;
return len;
}
static void DmaSlow(int len)
{
u16 *pd=0, *pdend, *r;
unsigned int a=Pico.video.addr, a2, d;
unsigned char inc=Pico.video.reg[0xf];
unsigned int source;
source =Pico.video.reg[0x15]<<1;
source|=Pico.video.reg[0x16]<<9;
source|=Pico.video.reg[0x17]<<17;
elprintf(EL_VDPDMA, "DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%i] @ %06x",
Pico.video.type, source, a, len, inc, (Pico.video.status&8)||!(Pico.video.reg[1]&0x40),
SekCyclesDone(), SekPc);
Pico.m.dma_xfers += len;
if ((PicoAHW & PAHW_MCD) && (PicoOpt & POPT_EN_MCD_PSYNC)) SekCyclesBurn(CheckDMA());
else SekSetCyclesLeftNoMCD(SekCyclesLeftNoMCD - CheckDMA());
if ((source&0xe00000)==0xe00000) { // Ram
pd=(u16 *)(Pico.ram+(source&0xfffe));
pdend=(u16 *)(Pico.ram+0x10000);
}
else if (PicoAHW & PAHW_MCD)
{
elprintf(EL_VDPDMA, "DmaSlow CD, r3=%02x", Pico_mcd->s68k_regs[3]);
if(source<0x20000) { // Bios area
pd=(u16 *)(Pico_mcd->bios+(source&~1));
pdend=(u16 *)(Pico_mcd->bios+0x20000);
} else if ((source&0xfc0000)==0x200000) { // Word Ram
source -= 2;
if (!(Pico_mcd->s68k_regs[3]&4)) { // 2M mode
pd=(u16 *)(Pico_mcd->word_ram2M+(source&0x3fffe));
pdend=(u16 *)(Pico_mcd->word_ram2M+0x40000);
} else {
if (source < 0x220000) { // 1M mode
int bank = Pico_mcd->s68k_regs[3]&1;
pd=(u16 *)(Pico_mcd->word_ram1M[bank]+(source&0x1fffe));
pdend=(u16 *)(Pico_mcd->word_ram1M[bank]+0x20000);
} else {
DmaSlowCell(source, a, len, inc);
return;
}
}
} else if ((source&0xfe0000)==0x020000) { // Prg Ram
u8 *prg_ram = Pico_mcd->prg_ram_b[Pico_mcd->s68k_regs[3]>>6];
pd=(u16 *)(prg_ram+(source&0x1fffe));
pdend=(u16 *)(prg_ram+0x20000);
} else {
elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: FIXME: unsupported src", Pico.video.type, source, a);
return;
}
}
else
{
// if we have DmaHook, let it handle ROM because of possible DMA delay
if (PicoDmaHook && PicoDmaHook(source, len, &pd, &pdend));
else if (source<Pico.romsize) { // Rom
pd=(u16 *)(Pico.rom+(source&~1));
pdend=(u16 *)(Pico.rom+Pico.romsize);
}
else {
elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: invalid src", Pico.video.type, source, a);
return;
}
}
// overflow protection, might break something..
if (len > pdend - pd) {
len = pdend - pd;
elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow overflow");
}
switch (Pico.video.type)
{
case 1: // vram
r = Pico.vram;
if (inc == 2 && !(a&1) && a+len*2 < 0x10000)
{
// most used DMA mode
memcpy16(r + (a>>1), pd, len);
a += len*2;
}
else
{
for(; len; len--)
{
d=*pd++;
if(a&1) d=(d<<8)|(d>>8);
r[a>>1] = (u16)d; // will drop the upper bits
// AutoIncrement
a=(u16)(a+inc);
// didn't src overlap?
//if(pd >= pdend) pd-=0x8000; // should be good for RAM, bad for ROM
}
}
rendstatus |= PDRAW_DIRTY_SPRITES;
break;
case 3: // cram
Pico.m.dirtyPal = 1;
r = Pico.cram;
for(a2=a&0x7f; len; len--)
{
r[a2>>1] = (u16)*pd++; // bit 0 is ignored
// AutoIncrement
a2+=inc;
// didn't src overlap?
//if(pd >= pdend) pd-=0x8000;
// good dest?
if(a2 >= 0x80) break; // Todds Adventures in Slime World / Andre Agassi tennis
}
a=(a&0xff00)|a2;
break;
case 5: // vsram[a&0x003f]=d;
r = Pico.vsram;
for(a2=a&0x7f; len; len--)
{
r[a2>>1] = (u16)*pd++;
// AutoIncrement
a2+=inc;
// didn't src overlap?
//if(pd >= pdend) pd-=0x8000;
// good dest?
if(a2 >= 0x80) break;
}
a=(a&0xff00)|a2;
break;
default:
elprintf(EL_VDPDMA|EL_ANOMALY, "DMA with bad type %i", Pico.video.type);
break;
}
// remember addr
Pico.video.addr=(u16)a;
}
static void DmaCopy(int len)
{
u16 a=Pico.video.addr;
unsigned char *vr = (unsigned char *) Pico.vram;
unsigned char *vrs;
unsigned char inc=Pico.video.reg[0xf];
int source;
elprintf(EL_VDPDMA, "DmaCopy len %i [%i]", len, SekCyclesDone());
Pico.m.dma_xfers += len;
Pico.video.status |= 2; // dma busy
source =Pico.video.reg[0x15];
source|=Pico.video.reg[0x16]<<8;
vrs=vr+source;
if (source+len > 0x10000) len=0x10000-source; // clip??
for (; len; len--)
{
vr[a] = *vrs++;
// AutoIncrement
a=(u16)(a+inc);
}
// remember addr
Pico.video.addr=a;
rendstatus |= PDRAW_DIRTY_SPRITES;
}
// check: Contra, Megaman
// note: this is still inaccurate
static void DmaFill(int data)
{
int len;
unsigned short a=Pico.video.addr;
unsigned char *vr=(unsigned char *) Pico.vram;
unsigned char high = (unsigned char) (data >> 8);
unsigned char inc=Pico.video.reg[0xf];
len=GetDmaLength();
elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%i]", len, inc, SekCyclesDone());
Pico.m.dma_xfers += len;
Pico.video.status |= 2; // dma busy
// from Charles MacDonald's genvdp.txt:
// Write lower byte to address specified
vr[a] = (unsigned char) data;
a=(u16)(a+inc);
if (!inc) len=1;
for (; len; len--) {
// Write upper byte to adjacent address
// (here we are byteswapped, so address is already 'adjacent')
vr[a] = high;
// Increment address register
a=(u16)(a+inc);
}
// remember addr
Pico.video.addr=a;
// update length
Pico.video.reg[0x13] = Pico.video.reg[0x14] = 0; // Dino Dini's Soccer (E) (by Haze)
rendstatus |= PDRAW_DIRTY_SPRITES;
}
static void CommandDma(void)
{
struct PicoVideo *pvid=&Pico.video;
int len=0,method=0;
if ((pvid->reg[1]&0x10)==0) return; // DMA not enabled
len=GetDmaLength();
method=pvid->reg[0x17]>>6;
if (method< 2) DmaSlow(len); // 68000 to VDP
if (method==3) DmaCopy(len); // VRAM Copy
}
static void CommandChange(void)
{
struct PicoVideo *pvid=&Pico.video;
unsigned int cmd=0,addr=0;
cmd=pvid->command;
// Get type of transfer 0xc0000030 (v/c/vsram read/write)
pvid->type=(unsigned char)(((cmd>>2)&0xc)|(cmd>>30));
// Get address 0x3fff0003
addr =(cmd>>16)&0x3fff;
addr|=(cmd<<14)&0xc000;
pvid->addr=(unsigned short)addr;
// Check for dma:
if (cmd&0x80) CommandDma();
}
static __inline void DrawSync(int blank_on)
{
if (Pico.m.scanline < 224 && !(PicoOpt & POPT_ALT_RENDERER) &&
!PicoSkipFrame && DrawScanline <= Pico.m.scanline) {
//elprintf(EL_ANOMALY, "sync");
PicoDrawSync(Pico.m.scanline, blank_on);
}
}
PICO_INTERNAL_ASM void PicoVideoWrite(unsigned int a,unsigned short d)
{
struct PicoVideo *pvid=&Pico.video;
//if (Pico.m.scanline < 224)
// elprintf(EL_STATUS, "PicoVideoWrite [%06x] %04x", a, d);
a&=0x1c;
if (a==0x00) // Data port 0 or 2
{
// try avoiding the sync..
if (Pico.m.scanline < 224 && (pvid->reg[1]&0x40) &&
!(!pvid->pending &&
((pvid->command & 0xc00000f0) == 0x40000010 && Pico.vsram[pvid->addr>>1] == d))
)
DrawSync(0);
if (pvid->pending) {
CommandChange();
pvid->pending=0;
}
// If a DMA fill has been set up, do it
if ((pvid->command&0x80) && (pvid->reg[1]&0x10) && (pvid->reg[0x17]>>6)==2)
{
DmaFill(d);
}
else
{
// preliminary FIFO emulation for Chaos Engine, The (E)
if (!(pvid->status&8) && (pvid->reg[1]&0x40) && !(PicoOpt&POPT_DIS_VDP_FIFO)) // active display?
{
pvid->status&=~0x200; // FIFO no longer empty
pvid->lwrite_cnt++;
if (pvid->lwrite_cnt >= 4) pvid->status|=0x100; // FIFO full
if (pvid->lwrite_cnt > 4) {
SekCyclesBurn(32); // penalty // 488/12-8
if (SekCycleCnt>=SekCycleAim) SekEndRun(0);
}
elprintf(EL_ASVDP, "VDP data write: %04x [%06x] {%i} #%i @ %06x", d, Pico.video.addr,
Pico.video.type, pvid->lwrite_cnt, SekPc);
}
VideoWrite(d);
}
return;
}
if (a==0x04) // Control (command) port 4 or 6
{
if (pvid->pending)
{
if (d & 0x80) DrawSync(0); // only need sync for DMA
// Low word of command:
pvid->command&=0xffff0000;
pvid->command|=d;
pvid->pending=0;
CommandChange();
}
else
{
if ((d&0xc000)==0x8000)
{
// Register write:
int num=(d>>8)&0x1f;
int dold=pvid->reg[num];
int blank_on = 0;
pvid->type=0; // register writes clear command (else no Sega logo in Golden Axe II)
if (num > 0x0a && !(pvid->reg[1]&4)) {
elprintf(EL_ANOMALY, "%02x written to reg %02x in SMS mode @ %06x", d, num, SekPc);
return;
}
if (num == 1 && !(d&0x40) && SekCyclesLeft > 390) blank_on = 1;
DrawSync(blank_on);
pvid->reg[num]=(unsigned char)d;
switch (num)
{
case 0x00:
elprintf(EL_INTSW, "hint_onoff: %i->%i [%i] pend=%i @ %06x", (dold&0x10)>>4,
(d&0x10)>>4, SekCyclesDone(), (pvid->pending_ints&0x10)>>4, SekPc);
goto update_irq;
case 0x01:
elprintf(EL_INTSW, "vint_onoff: %i->%i [%i] pend=%i @ %06x", (dold&0x20)>>5,
(d&0x20)>>5, SekCyclesDone(), (pvid->pending_ints&0x20)>>5, SekPc);
goto update_irq;
case 0x05:
//elprintf(EL_STATUS, "spritep moved to %04x", (unsigned)(Pico.video.reg[5]&0x7f) << 9);
if (d^dold) rendstatus |= PDRAW_SPRITES_MOVED;
break;
case 0x0c:
// renderers should update their palettes if sh/hi mode is changed
if ((d^dold)&8) Pico.m.dirtyPal = 2;
break;
}
return;
update_irq:
#ifndef EMU_CORE_DEBUG
// update IRQ level
if (!SekShouldInterrupt) // hack
{
int lines, pints, irq=0;
lines = (pvid->reg[1] & 0x20) | (pvid->reg[0] & 0x10);
pints = (pvid->pending_ints&lines);
if (pints & 0x20) irq = 6;
else if (pints & 0x10) irq = 4;
SekInterrupt(irq); // update line
if (irq) SekEndRun(24); // make it delayed
}
#endif
}
else
{
// High word of command:
pvid->command&=0x0000ffff;
pvid->command|=d<<16;
pvid->pending=1;
}
}
}
}
PICO_INTERNAL_ASM unsigned int PicoVideoRead(unsigned int a)
{
a&=0x1c;
if (a==0x04) // control port
{
struct PicoVideo *pv=&Pico.video;
unsigned int d;
d=pv->status;
//if (PicoOpt&POPT_ALT_RENDERER) d|=0x0020; // sprite collision (Shadow of the Beast)
if (SekCyclesLeft < 84+4) d|=0x0004; // H-Blank (Sonic3 vs)
d |= ((pv->reg[1]&0x40)^0x40) >> 3; // set V-Blank if display is disabled
d |= (pv->pending_ints&0x20)<<2; // V-int pending?
if (d&0x100) pv->status&=~0x100; // FIFO no longer full
pv->pending = 0; // ctrl port reads clear write-pending flag (Charles MacDonald)
elprintf(EL_SR, "SR read: %04x @ %06x", d, SekPc);
return d;
}
// H-counter info (based on Generator):
// frame:
// | <- hblank? -> |
// start <416> hint <36> hdisplay <38> end // CPU cycles
// |---------...---------|------------|-------------|
// 0 B6 E4 FF // 40 cells
// 0 93 E8 FF // 32 cells
// Gens (?) v-render
// start <hblank=84> hint hdisplay <404> |
// |---------------------|--------------------------|
// E4 (hc[0x43]==0) 07 B1 // 40
// E8 (hc[0x45]==0) 05 91 // 32
// check: Sonic 3D Blast bonus, Cannon Fodder, Chase HQ II, 3 Ninjas kick back, Road Rash 3, Skitchin', Wheel of Fortune
if ((a&0x1c)==0x08)
{
unsigned int d;
int lineCycles;
lineCycles = (488-SekCyclesLeft)&0x1ff;
if (Pico.video.reg[12]&1)
d = hcounts_40[lineCycles];
else d = hcounts_32[lineCycles];
elprintf(EL_HVCNT, "hv: %02x %02x (%i) @ %06x", d, Pico.video.v_counter, SekCyclesDone(), SekPc);
return d | (Pico.video.v_counter << 8);
}
if (a==0x00) // data port
{
return VideoRead();
}
return 0;
}
unsigned int PicoVideoRead8(unsigned int a)
{
unsigned int d;
a&=0x1d;
switch (a)
{
case 0: return VideoRead() >> 8;
case 1: return VideoRead() & 0xff;
case 4: // control port/status reg
d = Pico.video.status >> 8;
if (d&1) Pico.video.status&=~0x100; // FIFO no longer full
Pico.video.pending = 0;
elprintf(EL_SR, "SR read (h): %02x @ %06x", d, SekPc);
return d;
case 5:
d = Pico.video.status & 0xff;
//if (PicoOpt&POPT_ALT_RENDERER) d|=0x0020; // sprite collision (Shadow of the Beast)
d |= ((Pico.video.reg[1]&0x40)^0x40) >> 3; // set V-Blank if display is disabled
d |= (Pico.video.pending_ints&0x20)<<2; // V-int pending?
if (SekCyclesLeft < 84+4) d |= 4; // H-Blank
Pico.video.pending = 0;
elprintf(EL_SR, "SR read (l): %02x @ %06x", d, SekPc);
return d;
case 8: // hv counter
elprintf(EL_HVCNT, "vcounter: %02x (%i) @ %06x", Pico.video.v_counter, SekCyclesDone(), SekPc);
return Pico.video.v_counter;
case 9:
d = (488-SekCyclesLeft)&0x1ff;
if (Pico.video.reg[12]&1)
d = hcounts_40[d];
else d = hcounts_32[d];
elprintf(EL_HVCNT, "hcounter: %02x (%i) @ %06x", d, SekCyclesDone(), SekPc);
return d;
}
return 0;
}