initial import

git-svn-id: file:///home/notaz/opt/svn/PicoDrive@2 be3aeb3a-fb24-0410-a615-afba39da0efa
This commit is contained in:
notaz 2006-12-19 20:53:21 +00:00
parent 2cadbd5e56
commit cc68a136aa
341 changed files with 180839 additions and 0 deletions

189
Pico/Area.c Normal file
View file

@ -0,0 +1,189 @@
// 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 "PicoInt.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
// 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:
static int PackCpu(unsigned char *cpu)
{
unsigned int pc=0;
#ifdef EMU_A68K
memcpy(cpu,M68000_regs.d,0x40);
pc=M68000_regs.pc;
*(unsigned char *)(cpu+0x44)=(unsigned char)M68000_regs.ccr;
*(unsigned char *)(cpu+0x45)=(unsigned char)M68000_regs.srh;
*(unsigned int *)(cpu+0x48)=M68000_regs.isp;
#endif
#ifdef EMU_C68K
memcpy(cpu,PicoCpu.d,0x40);
pc=PicoCpu.pc-PicoCpu.membase;
*(unsigned int *)(cpu+0x44)=CycloneGetSr(&PicoCpu);
*(unsigned int *)(cpu+0x48)=PicoCpu.osp;
#endif
#ifdef EMU_M68K
memcpy(cpu,m68ki_cpu.dar,0x40);
pc=m68ki_cpu.pc;
*(unsigned int *)(cpu+0x44)=m68k_get_reg(NULL, M68K_REG_SR);
*(unsigned int *)(cpu+0x48)=m68ki_cpu.sp[0];
#endif
*(unsigned int *)(cpu+0x40)=pc;
return 0;
}
static int UnpackCpu(unsigned char *cpu)
{
unsigned int pc=0;
pc=*(unsigned int *)(cpu+0x40);
#ifdef EMU_A68K
memcpy(M68000_regs.d,cpu,0x40);
M68000_regs.pc=pc;
M68000_regs.ccr=*(unsigned char *)(cpu+0x44);
M68000_regs.srh=*(unsigned char *)(cpu+0x45);
M68000_regs.isp=*(unsigned int *)(cpu+0x48);
#endif
#ifdef EMU_C68K
CycloneSetSr(&PicoCpu, *(unsigned int *)(cpu+0x44));
PicoCpu.osp=*(unsigned int *)(cpu+0x48);
memcpy(PicoCpu.d,cpu,0x40);
PicoCpu.membase=0;
PicoCpu.pc =PicoCpu.checkpc(pc); // Base pc
#endif
#ifdef EMU_M68K
memcpy(m68ki_cpu.dar,cpu,0x40);
m68ki_cpu.pc=pc;
m68k_set_reg(M68K_REG_SR, *(unsigned int *)(cpu+0x44));
m68ki_cpu.sp[0]=*(unsigned int *)(cpu+0x48);
#endif
return 0;
}
// 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) PackCpu(cpu);
//SekInit(); // notaz: do we really have to do this here?
//PicoMemInit();
SCAN_VAR(cpu,"cpu")
if((PmovAction&3)==2) UnpackCpu(cpu);
SCAN_VAR(Pico.m ,"misc")
SCAN_VAR(Pico.video,"video")
if(ver == 0x0030) { // zram was being saved incorrectly in 0x0030 (byteswaped?)
Byteswap(Pico.zram, 0x2000);
return 0; // do not try to load sound stuff
}
//SCAN_VAR(Pico.s ,"sound")
// notaz: save/load z80, YM2612, sn76496 states instead of Pico.s (which is unused anyway)
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) {
ScanVar(ym2612_regs, 0x200+4, "YM2612state", PmovFile, PmovAction); // regs + addr line
if((PmovAction&3)==2) YM2612PicoStateLoad(); // 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];
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);
return 0;
}

197
Pico/Cart.c Normal file
View file

@ -0,0 +1,197 @@
// 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 "PicoInt.h"
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 *PicoCartAlloc(int filesize)
{
int alloc_size;
unsigned char *rom;
if (PicoMCD & 1) {
dprintf("sizeof(mcd_state): %i", sizeof(mcd_state));
if (filesize > 0x20000) return NULL; // invalid BIOS
rom=(unsigned char *)malloc(sizeof(mcd_state));
if (rom) memset(rom, 0, sizeof(mcd_state));
return rom;
}
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
//dprintf("alloc_size: %x\n", alloc_size);
// 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(FILE *f,unsigned char **prom,unsigned int *psize)
{
unsigned char *rom=NULL; int size;
if (f==NULL) return 1;
fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET);
if (size <= 0) return 1;
if (PicoMCD & 1) {
if (size > 0x20000) return 1; // invalid BIOS
size = 0xe0000;
} else {
size=(size+3)&~3; // Round up to a multiple of 4
}
// Allocate space for the rom plus padding
rom=PicoCartAlloc(size);
if (rom==NULL) return 1; // { fclose(f); return 1; }
fread(rom,1,size,f); // Load up the rom
// fclose(f); // this is confusing. From now on, caller should close it, because it opened this.
// Check for SMD:
if ((size&0x3fff)==0x200) { 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/remove 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
SRam.resize=1;
Pico.rom=rom;
Pico.romsize=romsize;
return PicoReset(1);
}
int PicoUnloadCart(unsigned char* romdata)
{
free(romdata);
return 0;
}
#ifdef _UNZIP_SUPPORT
// notaz
#include "../unzip/unzip.h"
// nearly same as PicoCartLoad, but works with zipfiles
int CartLoadZip(const char *fname, unsigned char **prom, unsigned int *psize)
{
unsigned char *rom=0;
struct zipent* zipentry;
int size;
ZIP *zipfile = openzip(fname);
if(!zipfile) return 1;
// find first bin or smd
while((zipentry = readzip(zipfile)) != 0)
{
char *ext;
if(strlen(zipentry->name) < 5) continue;
ext = zipentry->name+strlen(zipentry->name)-4;
if(!strcasecmp(ext, ".bin") || !strcasecmp(ext, ".smd") || !strcasecmp(ext, ".gen")) break;
}
if(!zipentry) {
closezip(zipfile);
return 4; // no roms
}
size = zipentry->uncompressed_size;
size=(size+3)&~3; // Round up to a multiple of 4
// Allocate space for the rom plus padding
rom=PicoCartAlloc(size);
if (rom==NULL) { closezip(zipfile); return 2; }
if(readuncompresszip(zipfile, zipentry, (char *)rom) != 0) {
free(rom);
rom = 0;
closezip(zipfile);
return 5; // unzip failed
}
closezip(zipfile);
// Check for SMD:
if ((size&0x3fff)==0x200) { 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;
}
#endif

1291
Pico/Draw.c Normal file

File diff suppressed because it is too large Load diff

1429
Pico/Draw.s Normal file

File diff suppressed because it is too large Load diff

633
Pico/Draw2.c Normal file
View file

@ -0,0 +1,633 @@
// 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.
// this is a frame-based renderer, alternative to Dave's line based which is in Draw.c
#include "PicoInt.h"
#include <assert.h>
#ifndef __GNUC__
#pragma warning (disable:4706) // Disable assignment within conditional
#endif
// 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
extern unsigned char *framebuff; // in format (8+320)x(8+224+8) (eights for borders)
int currpri = 0;
static int HighCacheA[41*(TILE_ROWS+1)+1+1]; // caches for high layers
static int HighCacheB[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 += 320+8) {
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 += 320+8) {
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 += 320+8) {
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 += 320+8) {
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 = framebuff;
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*328+8;
scrpos+=8*328*(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 += 328*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 = framebuff;
scrpos+=8*328*(planestart-START_ROW);
// Get vertical scroll value:
vscroll=Pico.vsram[plane]&0x1ff;
scrpos+=(8-(vscroll&7))*328;
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;
#ifdef USE_CACHE
if (code>>15) { // high priority tile
*hcache++ = code|(dx<<16)|(trow<<27); // cache it
#else
if ((code>>15) != currpri) {
#endif
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 += 328*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 = framebuff, *pd = 0;
// *hcache++ = code|(dx<<16)|(trow<<27); // cache it
scrpos+=(*hc++)*328 - START_ROW*328*8;
while((code=*hc++)) {
if((short)code == blank) continue;
// y pos
if(((unsigned)code>>27) != prevy) {
prevy = (unsigned)code>>27;
pd = scrpos + prevy*328*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 = framebuff;
scrpos+=(sy-START_ROW*8)*328;
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*328;
}
}
#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, i;
unsigned int *p=(unsigned int *)framebuff;
// Start with a background color:
// back=PicoCramHigh[reg7&0x3f];
back=reg7&0x3f;
back|=back<<8;
back|=back<<16;
for(i = (8+320)*(8+(END_ROW-START_ROW)*8)/16; i; i--) {
*p++ = back; // do 16 pixels per iteration
*p++ = back;
*p++ = back;
*p++ = back;
}
}
#endif
static void DrawDisplayFull()
{
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; }
currpri = 0;
DrawLayerFull(1, HighCacheB, START_ROW, (maxcolc<<16)|END_ROW);
switch(hvwin) {
case 4:
// fullscreen window
DrawWindowFull(START_ROW, (maxcolc<<16)|END_ROW, 0);
HighCacheA[1] = 0;
break;
case 3:
// we have plane A and both v and h windows
DrawLayerFull(0, HighCacheA, 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, HighCacheA, planestart, planeend);
DrawWindowFull(winstart, winend, 0);
break;
default:
// fullscreen plane A
DrawLayerFull(0, HighCacheA, START_ROW, (maxcolc<<16)|END_ROW);
break;
}
DrawAllSpritesFull(0, maxw);
#ifdef USE_CACHE
if(HighCacheB[1]) DrawTilesFromCacheF(HighCacheB);
if(HighCacheA[1]) DrawTilesFromCacheF(HighCacheA);
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;
}
#else
currpri = 1;
// TODO
#endif
DrawAllSpritesFull(1, maxw);
}
void PicoFrameFull()
{
// prepare cram?
if(PicoPrepareCram) PicoPrepareCram();
// Draw screen:
BackFillFull(Pico.video.reg[7]);
if (Pico.video.reg[1]&0x40) DrawDisplayFull();
}

928
Pico/Draw2.s Normal file
View file

@ -0,0 +1,928 @@
@ assembly optimized versions of most funtions from draw2.c
@ this is highly specialized, be careful if changing related C code!
@ (c) Copyright 2006, notaz
@ All Rights Reserved
.extern Pico
.extern framebuff
@ 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, =framebuff @ lr=framebuff
ldr lr, [lr]
add lr, lr, #328*8
mov r0, r0, lsl #26
mov r0, r0, lsr #26
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
sub lr, r3, r2
and lr, lr, #0x00ff0000 @ lr=cells
ldr r10, =(Pico+0x10000) @ r10=Pico.vram
ldr r11, =(Pico+0x22228) @ Pico.video
ldrb r5, [r11, #13] @ pvid->reg[13]
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
ldrb r7, [r11, #11]
tst r7, #3 @ full screen scroll? (if ==0)
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
ldrb r7, [r11, #16] @ ??hh??ww
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, =framebuff @ r11=framebuff
ldr r11, [r11]
sub r4, r9, #(START_ROW<<24)
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:
rsb r4, r7, #0 @ r4=tilex=(-ts->hscroll)>>3
mov r4, r4, asr #3
and r4, r4, #0xff
and r8, r8, #0xff000000
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, =framebuff @ r4=framebuff
ldr r4, [r4]
ldr r1, [r0], #4 @ read y offset
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
@ trow 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, =framebuff @ r11=scrpos
ldr r11, [r11]
add r11, r11, #328*8
add r11, r11, #8
and r4, r0, #0xff
sub r4, r4, #START_ROW
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
sub r12, r12, #0x78 @ r12=sy
ldr lr, [r0, #4] @ lr=code
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 r10, =(Pico+0x10000) @ r10=Pico.vram
ldr r11, =framebuff @ r11=scrpos
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

1423
Pico/Draw_.s Normal file

File diff suppressed because it is too large Load diff

1301
Pico/Draw_sh.c Normal file

File diff suppressed because it is too large Load diff

1527
Pico/Draw_sh.s Normal file

File diff suppressed because it is too large Load diff

829
Pico/Memory.c Normal file
View file

@ -0,0 +1,829 @@
// 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.
//#define __debug_io
#include "PicoInt.h"
#include "sound/sound.h"
#include "sound/ym2612.h"
#include "sound/sn76496.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
extern unsigned int lastSSRamWrite; // used by serial SRAM code
#ifdef _ASM_MEMORY_C
u8 PicoRead8(u32 a);
u16 PicoRead16(u32 a);
void PicoWriteRomHW_SSF2(u32 a,u32 d);
void PicoWriteRomHW_in1 (u32 a,u32 d);
#endif
#if defined(EMU_C68K) && defined(EMU_M68K)
// cyclone debug mode
u32 lastread_a, lastread_d[16]={0,}, lastwrite_cyc_d[16]={0,}, lastwrite_mus_d[16]={0,};
int lrp_cyc=0, lrp_mus=0, lwp_cyc=0, lwp_mus=0;
extern unsigned int ppop;
#endif
#if defined(EMU_C68K) || defined(EMU_A68K)
static __inline int PicoMemBase(u32 pc)
{
int membase=0;
if (pc<Pico.romsize+4)
{
membase=(int)Pico.rom; // Program Counter in Rom
}
else if ((pc&0xe00000)==0xe00000)
{
membase=(int)Pico.ram-(pc&0xff0000); // Program Counter in Ram
}
else
{
// Error - Program Counter is invalid
membase=(int)Pico.rom;
}
return membase;
}
#endif
#ifdef EMU_A68K
extern u8 *OP_ROM=NULL,*OP_RAM=NULL;
#endif
static u32 CPU_CALL PicoCheckPc(u32 pc)
{
u32 ret=0;
#if defined(EMU_C68K)
pc-=PicoCpu.membase; // Get real pc
pc&=0xfffffe;
PicoCpu.membase=PicoMemBase(pc);
ret = PicoCpu.membase+pc;
#elif defined(EMU_A68K)
OP_ROM=(u8 *)PicoMemBase(pc);
// don't bother calling us back unless it's outside the 64k segment
M68000_regs.AsmBank=(pc>>16);
#endif
return ret;
}
int PicoInitPc(u32 pc)
{
PicoCheckPc(pc);
return 0;
}
#ifndef _ASM_MEMORY_C
void PicoMemReset()
{
}
#endif
// -----------------------------------------------------------------
#ifndef _ASM_MEMORY_C
// address must already be checked
static int SRAMRead(u32 a)
{
u8 *d = SRam.data-SRam.start+a;
return (d[0]<<8)|d[1];
}
#endif
static int PadRead(int i)
{
int pad=0,value=0,TH;
pad=~PicoPad[i]; // Get inverse of pad MXYZ SACB RLDU
TH=Pico.ioports[i+1]&0x40;
if(PicoOpt & 0x20) { // 6 button gamepad enabled
int phase = Pico.m.padTHPhase[i];
if(phase == 2 && !TH) {
value=(pad&0xc0)>>2; // ?0SA 0000
goto end;
} else if(phase == 3 && TH) {
value=(pad&0x30)|((pad>>8)&0xf); // ?1CB MXYZ
goto end;
} else if(phase == 3 && !TH) {
value=((pad&0xc0)>>2)|0x0f; // ?0SA 1111
goto end;
}
}
if(TH) value=(pad&0x3f); // ?1CB RLDU
else value=((pad&0xc0)>>2)|(pad&3); // ?0SA 00DU
end:
// orr the bits, which are set as output
value |= Pico.ioports[i+1]&Pico.ioports[i+4];
return value; // will mirror later
}
u8 z80Read8(u32 a)
{
if(Pico.m.z80Run&1) return 0;
a&=0x1fff;
if(!(PicoOpt&4)) {
// 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];
}
// for nonstandard reads
#ifndef _ASM_MEMORY_C
static
#endif
u32 UnusualRead16(u32 a, int realsize)
{
u32 d=0;
dprintf("strange r%i: %06x @%06x", realsize, a&0xffffff, SekPc);
// for games with simple protection devices, discovered by Haze
// some dumb detection is used, but that should be enough to make things work
if ((a>>22) == 1 && Pico.romsize >= 512*1024) {
if (*(int *)(Pico.rom+0x123e4) == 0x00550c39 && *(int *)(Pico.rom+0x123e8) == 0x00000040) { // Super Bubble Bobble (Unl) [!]
if (a == 0x400000) { d=0x55<<8; goto end; }
else if (a == 0x400002) { d=0x0f<<8; goto end; }
}
else if (*(int *)(Pico.rom+0x008c4) == 0x66240055 && *(int *)(Pico.rom+0x008c8) == 0x00404df9) { // Smart Mouse (Unl)
if (a == 0x400000) { d=0x55<<8; goto end; }
else if (a == 0x400002) { d=0x0f<<8; goto end; }
else if (a == 0x400004) { d=0xaa<<8; goto end; }
else if (a == 0x400006) { d=0xf0<<8; goto end; }
}
else if (*(int *)(Pico.rom+0x00404) == 0x00a90600 && *(int *)(Pico.rom+0x00408) == 0x6708b013) { // King of Fighters '98, The (Unl) [!]
if (a == 0x480000 || a == 0x4800e0 || a == 0x4824a0 || a == 0x488880) { d=0xaa<<8; goto end; }
else if (a == 0x4a8820) { d=0x0a<<8; goto end; }
// there is also a read @ 0x4F8820 which needs 0, but that is returned in default case
}
else if (*(int *)(Pico.rom+0x01b24) == 0x004013f9 && *(int *)(Pico.rom+0x01b28) == 0x00ff0000) { // Mahjong Lover (Unl) [!]
if (a == 0x400000) { d=0x90<<8; goto end; }
else if (a == 0x401000) { d=0xd3<<8; goto end; } // this one doesn't seem to be needed, the code does 2 comparisons and only then
// checks the result, which is of the above one. Left it just in case.
}
else if (*(int *)(Pico.rom+0x05254) == 0x0c3962d0 && *(int *)(Pico.rom+0x05258) == 0x00400055) { // Elf Wor (Unl)
if (a == 0x400000) { d=0x55<<8; goto end; }
else if (a == 0x400004) { d=0xc9<<8; goto end; } // this check is done if the above one fails
else if (a == 0x400002) { d=0x0f<<8; goto end; }
else if (a == 0x400006) { d=0x18<<8; goto end; } // similar to above
}
// our default behaviour is to return whatever was last written a 0x400000-0x7fffff range (used by Squirrel King (R) [!])
// Lion King II, The (Unl) [!] writes @ 400000 and wants to get that val @ 400002 and wites another val
// @ 400004 which is expected @ 400006, so we really remember 2 values here
d = Pico.m.prot_bytes[(a>>2)&1]<<8;
}
else if (a == 0xa13000 && Pico.romsize >= 1024*1024) {
if (*(int *)(Pico.rom+0xc8af0) == 0x30133013 && *(int *)(Pico.rom+0xc8af4) == 0x000f0240) { // Rockman X3 (Unl) [!]
d=0x0c; goto end;
}
else if (*(int *)(Pico.rom+0x28888) == 0x07fc0000 && *(int *)(Pico.rom+0x2888c) == 0x4eb94e75) { // Bug's Life, A (Unl) [!]
d=0x28; goto end; // does the check from RAM
}
else if (*(int *)(Pico.rom+0xc8778) == 0x30133013 && *(int *)(Pico.rom+0xc877c) == 0x000f0240) { // Super Mario Bros. (Unl) [!]
d=0x0c; goto end; // seems to be the same code as in Rockman X3 (Unl) [!]
}
else if (*(int *)(Pico.rom+0xf20ec) == 0x30143013 && *(int *)(Pico.rom+0xf20f0) == 0x000f0200) { // Super Mario 2 1998 (Unl) [!]
d=0x0a; goto end;
}
}
else if (a == 0xa13002) { // Pocket Monsters (Unl)
d=0x01; goto end;
}
else if (a == 0xa1303E) { // Pocket Monsters (Unl)
d=0x1f; goto end;
}
else if (a == 0x30fe02) {
// Virtua Racing - just for fun
// this seems to be some flag that SVP is ready or something similar
d=1; goto end;
}
end:
dprintf("ret = %04x", d);
return d;
}
#ifndef _ASM_MEMORY_C
static
#endif
u32 OtherRead16(u32 a, int realsize)
{
u32 d=0;
if ((a&0xff0000)==0xa00000) {
if ((a&0x4000)==0x0000) { d=z80Read8(a); d|=d<<8; goto end; } // Z80 ram (not byteswaped)
if ((a&0x6000)==0x4000) { if(PicoOpt&1) d=YM2612Read(); else d=Pico.m.rotate++&3; goto end; } // 0x4000-0x5fff, Fudge if disabled
d=0xffff; goto end;
}
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); d|=Pico.ioports[1]&0x80; break;
case 2: d=PadRead(1); d|=Pico.ioports[2]&0x80; break;
default: d=Pico.ioports[a]; break; // IO ports can be used as RAM
}
d|=d<<8;
goto end;
}
// |=0x80 for Shadow of the Beast & Super Offroad; rotate fakes next fetched instruction for Time Killers
if (a==0xa11100) { d=((Pico.m.z80Run&1)<<8)|0x8000|Pico.m.rotate++; goto end; }
#ifndef _ASM_MEMORY_C
if ((a&0xe700e0)==0xc00000) { d=PicoVideoRead(a); goto end; }
#endif
d = UnusualRead16(a, realsize);
end:
return d;
}
//extern UINT32 mz80GetRegisterValue(void *, UINT32);
static void OtherWrite8(u32 a,u32 d,int realsize)
{
if ((a&0xe700f9)==0xc00011||(a&0xff7ff9)==0xa07f11) { if(PicoOpt&2) SN76496Write(d); return; } // PSG Sound
if ((a&0xff4000)==0xa00000) { if(!(Pico.m.z80Run&1)) Pico.zram[a&0x1fff]=(u8)d; return; } // Z80 ram
if ((a&0xff6000)==0xa04000) { if(PicoOpt&1) emustatus|=YM2612Write(a&3, d); return; } // FM Sound
if ((a&0xffffe0)==0xa10000) { // I/O ports
a=(a>>1)&0xf;
// 6 button gamepad: if TH went from 0 to 1, gamepad changes state
if(PicoOpt&0x20) {
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
return;
}
if (a==0xa11100) {
extern int z80startCycle, z80stopCycle;
//int lineCycles=(488-SekCyclesLeft)&0x1ff;
d&=1; d^=1;
if(!d) {
// hack: detect a nasty situation where Z80 was enabled and disabled in the same 68k timeslice (Golden Axe III)
if((PicoOpt&4) && Pico.m.z80Run==1) z80_run(20);
z80stopCycle = SekCyclesDone();
//z80ExtraCycles += (lineCycles>>1)-(lineCycles>>5); // only meaningful in PicoFrameHints()
} else {
z80startCycle = SekCyclesDone();
//if(Pico.m.scanline != -1)
//z80ExtraCycles -= (lineCycles>>1)-(lineCycles>>5)+16;
}
//dprintf("set_zrun: %i [%i|%i] zPC=%04x @%06x", d, Pico.m.scanline, SekCyclesDone(), mz80GetRegisterValue(NULL, 0), SekPc);
Pico.m.z80Run=(u8)d; return;
}
if (a==0xa11200) { if(!(d&1)) z80_reset(); 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
return;
}
if ((a&0xe700e0)==0xc00000) { PicoVideoWrite(a,(u16)(d|(d<<8))); return; } // Byte access gets mirrored
// sram
//if(a==0x200000) dprintf("cc : %02x @ %06x [%i|%i]", d, SekPc, SekCyclesDoneT(), SekCyclesDone());
//if(a==0x200001) dprintf("w8 : %02x @ %06x [%i]", d, SekPc, SekCyclesDoneT());
if(a >= SRam.start && a <= SRam.end) {
unsigned int sreg = Pico.m.sram_reg;
if(!(sreg & 0x10)) {
// not detected SRAM
if((a&~1)==0x200000) {
Pico.m.sram_reg|=4; // this should be a game with EEPROM (like NBA Jam)
SRam.start=0x200000; SRam.end=SRam.start+1;
}
Pico.m.sram_reg|=0x10;
}
if(sreg & 4) { // EEPROM write
if(SekCyclesDoneT()-lastSSRamWrite < 46) {
// just update pending state
SRAMUpdPending(a, d);
} else {
SRAMWriteEEPROM(sreg>>6); // execute pending
SRAMUpdPending(a, d);
lastSSRamWrite = SekCyclesDoneT();
}
} else if(!(sreg & 2)) {
u8 *pm=(u8 *)(SRam.data-SRam.start+a);
if(*pm != (u8)d) {
SRam.changed = 1;
*pm=(u8)d;
}
}
return;
}
#ifdef _ASM_MEMORY_C
// special ROM hardware (currently only banking and sram reg supported)
if((a&0xfffff1) == 0xA130F1) {
PicoWriteRomHW_SSF2(a, d); // SSF2 or SRAM
return;
}
#else
// sram access register
if(a == 0xA130F1) {
Pico.m.sram_reg = (u8)(d&3);
return;
}
#endif
dprintf("strange w%i: %06x, %08x @%06x", realsize, a&0xffffff, d, SekPc);
if(a >= 0xA13004 && a < 0xA13040) {
// dumb 12-in-1 or 4-in-1 banking support
int len;
a &= 0x3f; a <<= 16;
len = Pico.romsize - a;
if (len <= 0) return; // invalid/missing bank
if (len > 0x200000) len = 0x200000; // 2 megs
memcpy(Pico.rom, Pico.rom+a, len); // code which does this is in RAM so this is safe.
return;
}
// for games with simple protection devices, discovered by Haze
else if ((a>>22) == 1)
Pico.m.prot_bytes[(a>>2)&1] = (u8)d;
}
static void OtherWrite16(u32 a,u32 d)
{
if ((a&0xe700e0)==0xc00000) { PicoVideoWrite(a,(u16)d); return; }
if ((a&0xff4000)==0xa00000) { if(!(Pico.m.z80Run&1)) Pico.zram[a&0x1fff]=(u8)(d>>8); return; } // Z80 ram (MSB only)
if ((a&0xffffe0)==0xa10000) { // I/O ports
a=(a>>1)&0xf;
// 6 button gamepad: if TH went from 0 to 1, gamepad changes state
if(PicoOpt&0x20) {
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
return;
}
if (a==0xa11100) { OtherWrite8(a, d>>8, 16); return; }
if (a==0xa11200) { if(!(d&0x100)) z80_reset(); return; }
OtherWrite8(a, d>>8, 16);
OtherWrite8(a+1,d&0xff, 16);
}
// -----------------------------------------------------------------
// Read Rom and read Ram
#ifndef _ASM_MEMORY_C
u8 CPU_CALL PicoRead8(u32 a)
{
u32 d=0;
if ((a&0xe00000)==0xe00000) { d = *(u8 *)(Pico.ram+((a^1)&0xffff)); goto end; } // Ram
a&=0xffffff;
#if !(defined(EMU_C68K) && defined(EMU_M68K))
// sram
if(a >= SRam.start && a <= SRam.end) {
unsigned int sreg = Pico.m.sram_reg;
if(!(sreg & 0x10) && (sreg & 1) && a > 0x200001) { // not yet detected SRAM
Pico.m.sram_reg|=0x10; // should be normal SRAM
}
if(sreg & 4) { // EEPROM read
d = SRAMReadEEPROM();
goto end;
} else if(sreg & 1) {
d = *(u8 *)(SRam.data-SRam.start+a);
goto end;
}
}
#endif
if (a<Pico.romsize) { d = *(u8 *)(Pico.rom+(a^1)); goto end; } // Rom
if ((a&0xff4000)==0xa00000) { d=z80Read8(a); goto end; } // Z80 Ram
d=OtherRead16(a&~1, 8); if ((a&1)==0) d>>=8;
end:
//if ((a&0xe0ffff)==0xe0AE57+0x69c)
// dprintf("r8 : %06x, %02x @%06x", a&0xffffff, (u8)d, SekPc);
//if ((a&0xe0ffff)==0xe0a9ba+0x69c)
// dprintf("r8 : %06x, %02x @%06x", a&0xffffff, d, SekPc);
//if(a==0x200001) dprintf("r8 : %02x @ %06x [%i]", d, SekPc, SekCyclesDoneT());
//dprintf("r8 : %06x, %02x @%06x [%03i]", a&0xffffff, (u8)d, SekPc, Pico.m.scanline);
#ifdef __debug_io
dprintf("r8 : %06x, %02x @%06x", a&0xffffff, (u8)d, SekPc);
#endif
#if defined(EMU_C68K) && defined(EMU_M68K)
if(a>=Pico.romsize&&(ppop&0x3f)!=0x3a&&(ppop&0x3f)!=0x3b) {
lastread_a = a;
lastread_d[lrp_cyc++&15] = (u8)d;
}
#endif
return (u8)d;
}
u16 CPU_CALL PicoRead16(u32 a)
{
u16 d=0;
if ((a&0xe00000)==0xe00000) { d=*(u16 *)(Pico.ram+(a&0xfffe)); goto end; } // Ram
a&=0xfffffe;
#if !(defined(EMU_C68K) && defined(EMU_M68K))
// sram
if(a >= SRam.start && a <= SRam.end && (Pico.m.sram_reg & 1)) {
d = (u16) SRAMRead(a);
goto end;
}
#endif
if (a<Pico.romsize) { d = *(u16 *)(Pico.rom+a); goto end; } // Rom
d = (u16)OtherRead16(a, 16);
end:
//if ((a&0xe0ffff)==0xe0AF0E+0x69c||(a&0xe0ffff)==0xe0A9A8+0x69c||(a&0xe0ffff)==0xe0A9AA+0x69c||(a&0xe0ffff)==0xe0A9AC+0x69c)
// dprintf("r16: %06x, %04x @%06x", a&0xffffff, d, SekPc);
#ifdef __debug_io
dprintf("r16: %06x, %04x @%06x", a&0xffffff, d, SekPc);
#endif
#if defined(EMU_C68K) && defined(EMU_M68K)
if(a>=Pico.romsize&&(ppop&0x3f)!=0x3a&&(ppop&0x3f)!=0x3b) {
lastread_a = a;
lastread_d[lrp_cyc++&15] = d;
}
#endif
return d;
}
u32 CPU_CALL PicoRead32(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;
// sram
if(a >= SRam.start && a <= SRam.end && (Pico.m.sram_reg & 1)) {
d = (SRAMRead(a)<<16)|SRAMRead(a+2);
goto end;
}
if (a<Pico.romsize) { u16 *pm=(u16 *)(Pico.rom+a); d = (pm[0]<<16)|pm[1]; goto end; } // Rom
d = (OtherRead16(a, 32)<<16)|OtherRead16(a+2, 32);
end:
#ifdef __debug_io
dprintf("r32: %06x, %08x @%06x", a&0xffffff, d, SekPc);
#endif
#if defined(EMU_C68K) && defined(EMU_M68K)
if(a>=Pico.romsize&&(ppop&0x3f)!=0x3a&&(ppop&0x3f)!=0x3b) {
lastread_a = a;
lastread_d[lrp_cyc++&15] = d;
}
#endif
return d;
}
#endif
// -----------------------------------------------------------------
// Write Ram
static void CPU_CALL PicoWrite8(u32 a,u8 d)
{
#ifdef __debug_io
dprintf("w8 : %06x, %02x @%06x", a&0xffffff, d, SekPc);
#endif
#if defined(EMU_C68K) && defined(EMU_M68K)
lastwrite_cyc_d[lwp_cyc++&15] = d;
#endif
//if ((a&0xe0ffff)==0xe0a9ba+0x69c)
// dprintf("w8 : %06x, %02x @%06x", a&0xffffff, d, SekPc);
if ((a&0xe00000)==0xe00000) { u8 *pm=(u8 *)(Pico.ram+((a^1)&0xffff)); pm[0]=d; return; } // Ram
a&=0xffffff;
OtherWrite8(a,d,8);
}
static void CPU_CALL PicoWrite16(u32 a,u16 d)
{
#ifdef __debug_io
dprintf("w16: %06x, %04x", a&0xffffff, d);
#endif
#if defined(EMU_C68K) && defined(EMU_M68K)
lastwrite_cyc_d[lwp_cyc++&15] = d;
#endif
//if ((a&0xe0ffff)==0xe0AF0E+0x69c||(a&0xe0ffff)==0xe0A9A8+0x69c||(a&0xe0ffff)==0xe0A9AA+0x69c||(a&0xe0ffff)==0xe0A9AC+0x69c)
// dprintf("w16: %06x, %04x @%06x", a&0xffffff, d, SekPc);
if ((a&0xe00000)==0xe00000) { *(u16 *)(Pico.ram+(a&0xfffe))=d; return; } // Ram
a&=0xfffffe;
OtherWrite16(a,d);
}
static void CPU_CALL PicoWrite32(u32 a,u32 d)
{
#ifdef __debug_io
dprintf("w32: %06x, %08x", a&0xffffff, d);
#endif
#if defined(EMU_C68K) && defined(EMU_M68K)
lastwrite_cyc_d[lwp_cyc++&15] = d;
#endif
if ((a&0xe00000)==0xe00000)
{
// Ram:
u16 *pm=(u16 *)(Pico.ram+(a&0xfffe));
pm[0]=(u16)(d>>16); pm[1]=(u16)d;
return;
}
a&=0xfffffe;
OtherWrite16(a, (u16)(d>>16));
OtherWrite16(a+2,(u16)d);
}
// -----------------------------------------------------------------
int PicoMemInit()
{
#ifdef EMU_C68K
// Setup memory callbacks:
PicoCpu.checkpc=PicoCheckPc;
PicoCpu.fetch8 =PicoCpu.read8 =PicoRead8;
PicoCpu.fetch16=PicoCpu.read16=PicoRead16;
PicoCpu.fetch32=PicoCpu.read32=PicoRead32;
PicoCpu.write8 =PicoWrite8;
PicoCpu.write16=PicoWrite16;
PicoCpu.write32=PicoWrite32;
#endif
return 0;
}
#ifdef EMU_A68K
struct A68KInter
{
u32 unknown;
u8 (__fastcall *Read8) (u32 a);
u16 (__fastcall *Read16)(u32 a);
u32 (__fastcall *Read32)(u32 a);
void (__fastcall *Write8) (u32 a,u8 d);
void (__fastcall *Write16) (u32 a,u16 d);
void (__fastcall *Write32) (u32 a,u32 d);
void (__fastcall *ChangePc)(u32 a);
u8 (__fastcall *PcRel8) (u32 a);
u16 (__fastcall *PcRel16)(u32 a);
u32 (__fastcall *PcRel32)(u32 a);
u16 (__fastcall *Dir16)(u32 a);
u32 (__fastcall *Dir32)(u32 a);
};
struct A68KInter a68k_memory_intf=
{
0,
PicoRead8,
PicoRead16,
PicoRead32,
PicoWrite8,
PicoWrite16,
PicoWrite32,
PicoCheckPc,
PicoRead8,
PicoRead16,
PicoRead32,
PicoRead16, // unused
PicoRead32, // unused
};
#endif
#ifdef EMU_M68K
unsigned int m68k_read_pcrelative_CD8 (unsigned int a);
unsigned int m68k_read_pcrelative_CD16(unsigned int a);
unsigned int m68k_read_pcrelative_CD32(unsigned int a);
// these are allowed to access RAM
unsigned int m68k_read_pcrelative_8 (unsigned int a) {
a&=0xffffff;
if(PicoMCD&1) return m68k_read_pcrelative_CD8(a);
if(a<Pico.romsize) return *(u8 *)(Pico.rom+(a^1)); // Rom
if((a&0xe00000)==0xe00000) return *(u8 *)(Pico.ram+((a^1)&0xffff)); // Ram
return 0;//(u8) lastread_d;
}
unsigned int m68k_read_pcrelative_16(unsigned int a) {
a&=0xffffff;
if(PicoMCD&1) return m68k_read_pcrelative_CD16(a);
if(a<Pico.romsize) return *(u16 *)(Pico.rom+(a&~1)); // Rom
if((a&0xe00000)==0xe00000) return *(u16 *)(Pico.ram+(a&0xfffe)); // Ram
return 0;//(u16) lastread_d;
}
unsigned int m68k_read_pcrelative_32(unsigned int a) {
a&=0xffffff;
if(PicoMCD&1) return m68k_read_pcrelative_CD32(a);
if(a<Pico.romsize) { u16 *pm=(u16 *)(Pico.rom+(a&~1)); return (pm[0]<<16)|pm[1]; }
if((a&0xe00000)==0xe00000) { u16 *pm=(u16 *)(Pico.ram+(a&0xfffe)); return (pm[0]<<16)|pm[1]; } // Ram
return 0;//lastread_d;
}
unsigned int m68k_read_immediate_16(unsigned int a) { return m68k_read_pcrelative_16(a); }
unsigned int m68k_read_immediate_32(unsigned int a) { return m68k_read_pcrelative_32(a); }
unsigned int m68k_read_disassembler_8 (unsigned int a) { return m68k_read_pcrelative_8 (a); }
unsigned int m68k_read_disassembler_16(unsigned int a) { return m68k_read_pcrelative_16(a); }
unsigned int m68k_read_disassembler_32(unsigned int a) { return m68k_read_pcrelative_32(a); }
#ifdef EMU_C68K
// ROM only
unsigned int m68k_read_memory_8(unsigned int a) { if(a<Pico.romsize) return *(u8 *) (Pico.rom+(a^1)); return (u8) lastread_d[lrp_mus++&15]; }
unsigned int m68k_read_memory_16(unsigned int a) { if(a<Pico.romsize) return *(u16 *)(Pico.rom+(a&~1));return (u16) lastread_d[lrp_mus++&15]; }
unsigned int m68k_read_memory_32(unsigned int a) { if(a<Pico.romsize) {u16 *pm=(u16 *)(Pico.rom+(a&~1));return (pm[0]<<16)|pm[1];} return lastread_d[lrp_mus++&15]; }
// ignore writes, Cyclone already done that
void m68k_write_memory_8(unsigned int address, unsigned int value) { lastwrite_mus_d[lwp_mus++&15] = value; }
void m68k_write_memory_16(unsigned int address, unsigned int value) { lastwrite_mus_d[lwp_mus++&15] = value; }
void m68k_write_memory_32(unsigned int address, unsigned int value) { lastwrite_mus_d[lwp_mus++&15] = value; }
#else
unsigned char PicoReadCD8w (unsigned int a);
unsigned short PicoReadCD16w(unsigned int a);
unsigned int PicoReadCD32w(unsigned int a);
void PicoWriteCD8w (unsigned int a, unsigned char d);
void PicoWriteCD16w(unsigned int a, unsigned short d);
void PicoWriteCD32w(unsigned int a, unsigned int d);
unsigned int m68k_read_memory_8(unsigned int address)
{
return (PicoMCD&1) ? PicoReadCD8w(address) : PicoRead8(address);
}
unsigned int m68k_read_memory_16(unsigned int address)
{
return (PicoMCD&1) ? PicoReadCD16w(address) : PicoRead16(address);
}
unsigned int m68k_read_memory_32(unsigned int address)
{
return (PicoMCD&1) ? PicoReadCD32w(address) : PicoRead32(address);
}
void m68k_write_memory_8(unsigned int address, unsigned int value)
{
if (PicoMCD&1) PicoWriteCD8w(address, (u8)value); else PicoWrite8(address, (u8)value);
}
void m68k_write_memory_16(unsigned int address, unsigned int value)
{
if (PicoMCD&1) PicoWriteCD16w(address,(u16)value); else PicoWrite16(address,(u16)value);
}
void m68k_write_memory_32(unsigned int address, unsigned int value)
{
if (PicoMCD&1) PicoWriteCD32w(address, value); else PicoWrite32(address, value);
}
#endif
#endif // EMU_M68K
// -----------------------------------------------------------------
// z80 memhandlers
unsigned char z80_read(unsigned short a)
{
u8 ret = 0;
if ((a>>13)==2) // 0x4000-0x5fff (Charles MacDonald)
{
if(PicoOpt&1) ret = (u8) YM2612Read();
goto end;
}
if (a>=0x8000)
{
u32 addr68k;
addr68k=Pico.m.z80_bank68k<<15;
addr68k+=a&0x7fff;
ret = (u8) PicoRead8(addr68k);
//dprintf("z80->68k w8 : %06x, %02x", addr68k, ret);
goto end;
}
// should not be needed || dprintf("z80_read RAM");
if (a<0x4000) { ret = (u8) Pico.zram[a&0x1fff]; goto end; }
end:
return ret;
}
unsigned short z80_read16(unsigned short a)
{
//dprintf("z80_read16");
return (u16) ( (u16)z80_read(a) | ((u16)z80_read((u16)(a+1))<<8) );
}
void z80_write(unsigned char data, unsigned short a)
{
//if (a<0x4000)
// dprintf("z80 w8 : %06x, %02x @%04x", a, data, mz80GetRegisterValue(NULL, 0));
if ((a>>13)==2) // 0x4000-0x5fff (Charles MacDonald)
{
if(PicoOpt&1) emustatus|=YM2612Write(a, data);
return;
}
if ((a&0xfff9)==0x7f11) // 7f11 7f13 7f15 7f17
{
if(PicoOpt&2) SN76496Write(data);
return;
}
if ((a>>8)==0x60)
{
Pico.m.z80_bank68k>>=1;
Pico.m.z80_bank68k|=(data&1)<<8;
Pico.m.z80_bank68k&=0x1ff; // 9 bits and filled in the new top one
return;
}
if (a>=0x8000)
{
u32 addr68k;
addr68k=Pico.m.z80_bank68k<<15;
addr68k+=a&0x7fff;
PicoWrite8(addr68k, data);
//dprintf("z80->68k w8 : %06x, %02x", addr68k, data);
return;
}
// should not be needed, drZ80 knows how to access RAM itself || dprintf("z80_write RAM @ %08x", lr);
if (a<0x4000) { Pico.zram[a&0x1fff]=data; return; }
}
void z80_write16(unsigned short data, unsigned short a)
{
//dprintf("z80_write16");
z80_write((unsigned char) data,a);
z80_write((unsigned char)(data>>8),(u16)(a+1));
}

704
Pico/Memory.s Normal file
View file

@ -0,0 +1,704 @@
@ memory handlers with banking support for SSF II - The New Challengers
@ mostly based on Gens code
@ (c) Copyright 2006, notaz
@ All Rights Reserved
.text
@ 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
.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_read_null @ 0x800000 - 0x87FFFF
.long m_read_null @ 0x880000 - 0x8FFFFF
.long m_read_null @ 0x900000 - 0x97FFFF
.long m_read_null @ 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_read_null @ 0xD00000 - 0xD7FFFF
.long m_read_null @ 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_read_null @ 0x800000 - 0x87FFFF
.long m_read_null @ 0x880000 - 0x8FFFFF
.long m_read_null @ 0x900000 - 0x97FFFF
.long m_read_null @ 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_read_null @ 0xC80000 - 0xCFFFFF
.long m_read_null @ 0xD00000 - 0xD7FFFF
.long m_read_null @ 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_read_null @ 0x800000 - 0x87FFFF
.long m_read_null @ 0x880000 - 0x8FFFFF
.long m_read_null @ 0x900000 - 0x97FFFF
.long m_read_null @ 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_read_null @ 0xC80000 - 0xCFFFFF
.long m_read_null @ 0xD00000 - 0xD7FFFF
.long m_read_null @ 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
@.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
.global PicoMemReset
.global PicoRead8
.global PicoRead16
.global PicoRead32
.global PicoWriteRomHW_SSF2
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, #16
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, #16
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, #16
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 (1ci)
cmp r0, r1
blt m_read8_nosram
ldrb r1, [r3, #0x11] @ Pico.m.sram_reg (1ci)
sub r12,r0, #0x200000
tst r1, #0x10
bne m_read8_detected
cmp r12,#1
ble m_read8_detected
tst r1, #1
orrne r1, r1, #0x10
strneb r1, [r3, #0x11]
m_read8_detected:
tst r1, #4 @ EEPROM read?
ldrne r0, =SRAMReadEEPROM @ (1ci if ne)
bxne r0
m_read8_noteeprom:
tst r1, #1
beq m_read8_nosram
ldr r3, [r2] @ SRam.data
ldr r2, [r2, #4] @ SRam.start (1ci)
sub r3, r3, r2
ldrb r0, [r3, r0]
bx lr
m_read8_nosram:
ldr r1, [r3, #4] @ 1ci
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
@ is any ROM using that much?
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_misc:
bic r2, r0, #0x00ff
bic r2, r2, #0xbf00
cmp r2, #0xa00000 @ Z80 RAM?
ldreq r2, =z80Read8
bxeq r2
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
stmfd sp!,{r0,lr}
bic r0, r0, #1
bl PicoVideoRead
ldmfd sp!,{r1,lr}
tst r1, #1
moveq r0, r0, lsr #8
bx lr
m_read8_ram:
ldr r1, =Pico
bic r0, r0, #0xff0000
eor r0, r0, #1
ldrb r0, [r1, r0]
bx lr
m_read8_above_rom:
stmfd sp!,{r0,lr}
bic r0, r0, #1
mov r1, #8
bl UnusualRead16
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
ldrb r1, [r3, #0x11] @ Pico.m.sram_reg (2ci)
tst r1, #1
beq m_read16_nosram
ldr r1, [r2, #4] @ SRam.start (1ci)
cmp r0, r1
blt m_read16_nosram
ldr r2, [r2] @ SRam.data (1ci)
sub r2, r2, r1
ldrh r0, [r2, r0] @ 2ci
and r1, r0, #0xff
mov r0, r0, lsr #8
orr r0, r0, r1, lsl #8
bx lr
m_read16_nosram:
ldr r1, [r3, #4] @ 1ci
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
@ is any ROM using that much?
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_misc:
mov r1, #16
ldr r2, =OtherRead16
bic r0, r0, #1
bx r2
m_read16_vdp:
tst r0, #0x70000
tsteq r0, #0x000e0
bxne lr @ invalid read
ldr r1, =PicoVideoRead
bic r0, r0, #1
bx r1
m_read16_ram:
ldr r1, =Pico
bic r0, r0, #0xff0000
bic r0, r0, #1
ldrh r0, [r1, r0]
bx lr
m_read16_above_rom:
mov r1, #16
ldr r2, =UnusualRead16
bic r0, r0, #1
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
ldrb r1, [r3, #0x11] @ Pico.m.sram_reg (2ci)
tst r1, #1
beq m_read32_nosram
ldr r1, [r2, #4] @ SRam.start (1ci)
cmp r0, r1
blt m_read32_nosram
ldr r2, [r2] @ SRam.data (1ci)
sub r2, r2, r1
ldrh r0, [r2, r0]! @ (1ci)
ldrh r1, [r2, #2]
orr r0, r0, r0, lsl #16
mov r0, r0, ror #8
mov r0, r0, lsl #16
orr r0, r0, r1, lsr #8
and r1, r1, #0xff
orr r0, r0, r1, lsl #8
bx lr
m_read32_nosram:
ldr r1, [r3, #4] @ (1ci)
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
@ is any ROM using that much?
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_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
stmfd sp!,{r0,lr}
bl PicoVideoRead
mov r1, r0
ldmfd sp!,{r0}
stmfd sp!,{r1}
add r0, r0, #2
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:
bic r0, r0, #1
stmfd sp!,{r0,lr}
mov r1, #32
bl UnusualRead16
mov r1, r0
ldmfd sp!,{r0}
stmfd sp!,{r1}
add r0, r0, #2
mov r1, #32
bl UnusualRead16
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
and r1, r1, #3
strb r1, [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

305
Pico/Misc.c Normal file
View file

@ -0,0 +1,305 @@
// 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 "PicoInt.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,
};
// vcounter values for PicoFrameSimple
const unsigned short vcounts[] = {
0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
8, 8, 9, 9, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16,
16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 24,
25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 31, 31, 32, 32, 33,
33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41,
42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 49,
50, 50, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58,
58, 59, 59, 60, 60, 61, 62, 62, 63, 63, 64, 64, 65, 65, 66, 66,
67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 73, 73, 74, 74, 75,
75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 83, 83,
84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 89, 90, 90, 91, 91,
92, 93, 93, 94, 94, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99,100,
100,101,101,102,102,103,104,104,105,105,106,106,107,107,108,108,
109,109,110,110,111,111,112,112,113,114,114,115,115,116,116,117,
117,118,118,119,119,120,120,121,121,122,122,123,124,124,125,125,
126,126,127,127,128,128,129,129,130,130,131,131,132,132,133,133,
134,135,135,136,136,137,137,138,138,139,139,140,140,141,141,142,
142,143,143,144,145,145,146,146,147,147,148,148,149,149,150,150,
151,151,152,152,153,153,154,155,155,156,156,157,157,158,158,159,
159,160,160,161,161,162,162,163,163,164,164,165,166,166,167,167,
168,168,169,169,170,170,171,171,172,172,173,173,174,174,175,176,
176,177,177,178,178,179,179,180,180,181,181,182,182,183,183,184,
184,185,186,186,187,187,188,188,189,189,190,190,191,191,192,192,
193,193,194,194,195,195,196,197,197,198,198,199,199,200,200,201,
201,202,202,203,203,204,204,205,205,206,207,207,208,208,209,209,
210,210,211,211,212,212,213,213,214,214,215,215,216,217,217,218,
218,219,219,220,220,221,221,222,222,223,223,224,224,225,225,226,
226,227,228,228,229,229,230,230,231,231,232,232,233,233,234,234,
235,235,236,236,237,238,238,239,239,240,240,241,241,242,242,243,
243,244,244,245,245,246,246,247,248,248,249,249,250,250,251,251,
252,252,253,253,254,254,255,255,256,256,257,257,258,259,259,260,
260,261,261,262,262,263,263,264,264,265,265,266,266,267,267,268,
269,269,270,270,271,271,272,272,273,273,274,274,275,275,276,276,
277,277,278,279,279,280,280,281,281,282,282,283,283,284,284,285,
285,286,286,287,287,288,288,289,290,290,291,291,292,292,293,293,
294,294,295,295,296,296,297,297,298,298,299,300,300,301,301,302,
302,303,303,304,304,305,305,306,306,307,307,308,308,309,310,310,
311,311,311,311,
};
// rarely used EEPROM SRAM code
// known games which use this:
// Wonder Boy in Monster World, Megaman - The Wily Wars (X24C01, 128 bytes)
// NFL Quarterback Club*, Frank Thomas Big Hurt Baseball (X24C04?)
// College Slam, Blockbuster World Video Game Championship II, NBA Jam (X24C04?)
// HardBall '95
// the above sports games use addr 0x200000 for SCL line (handled in Memory.c)
unsigned int lastSSRamWrite = 0xffff0000;
// sram_reg: LAtd sela (L=pending SCL, A=pending SDA, t=type(1==uses 0x200000 for SCL and 2K bytes),
// d=SRAM was detected (header or by access), s=started, e=save is EEPROM, l=old SCL, a=old SDA)
void SRAMWriteEEPROM(unsigned int d) // ???? ??la (l=SCL, a=SDA)
{
unsigned int sreg = Pico.m.sram_reg, saddr = Pico.m.sram_addr, scyc = Pico.m.sram_cycle, ssa = Pico.m.sram_slave;
//dprintf("[%02x]", d);
sreg |= saddr&0xc000; // we store word count in add reg: dw?a aaaa ... (d=word count detected, w=words(0==use 2 words, else 1))
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
//dprintf("-start-");
if(!(sreg&0x8000) && scyc >= 9) {
if(scyc != 28) sreg |= 0x4000; // 1 word
//dprintf("detected word count: %i", scyc==28 ? 2 : 1);
sreg |= 0x8000;
}
//saddr = 0;
scyc = 0;
sreg |= 8;
} else if(!(sreg & 1) && (d&1)) {
// SDA went high == stop command
//dprintf("-stop-");
sreg &= ~8;
}
}
else if((sreg & 8) && !(sreg & 2) && (d&2)) {
// we are started and SCL went high - next cycle
scyc++; // pre-increment
if(sreg & 0x20) {
// X24C02+
if((ssa&1) && scyc == 18) {
scyc = 9;
saddr++; // next address in read mode
if(sreg&0x4000) saddr&=0xff; else saddr&=0x1fff; // mask
}
else if((sreg&0x4000) && 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
}
}
//dprintf("scyc: %i", scyc);
}
else if((sreg & 8) && (sreg & 2) && !(d&2)) {
// we are started and SCL went low (falling edge)
if(sreg & 0x20) {
// X24C02+
if(scyc == 9 || scyc == 18 || scyc == 27); // ACK cycles
else if( (!(sreg&0x4000) && scyc > 27) || ((sreg&0x4000) && 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
//dprintf("w done: %02x; addr inc: %x", *pm, saddr);
}
SRam.changed = 1;
}
} else if(scyc > 9) {
if(!(ssa&1)) {
// we latch another addr bit
saddr<<=1;
if(sreg&0x4000) saddr&=0xff; else saddr&=0x1fff; // mask
saddr|=d&1;
//if(scyc==17||scyc==26) dprintf("addr reg done: %x", saddr);
}
} else {
// slave address
ssa<<=1; ssa|=d&1;
//if(scyc==8) dprintf("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
//dprintf("addr inc: %x", saddr>>1);
}
SRam.changed = 1;
}
} else {
// we latch another addr bit
saddr<<=1; saddr|=d&1; saddr&=0xff;
//if(scyc==8) dprintf("addr done: %x", saddr>>1);
}
}
}
sreg &= ~3; sreg |= d&3; // remember SCL and SDA
Pico.m.sram_reg = (unsigned char) sreg;
Pico.m.sram_addr = (unsigned short)(saddr|(sreg&0xc000));
Pico.m.sram_cycle= (unsigned char) scyc;
Pico.m.sram_slave= (unsigned char) ssa;
}
unsigned int SRAMReadEEPROM()
{
unsigned int shift, d=0;
unsigned int sreg, saddr, scyc, ssa;
// flush last pending write
SRAMWriteEEPROM(Pico.m.sram_reg>>6);
sreg = Pico.m.sram_reg; saddr = Pico.m.sram_addr&0x1fff; scyc = Pico.m.sram_cycle; ssa = Pico.m.sram_slave;
// if(!(sreg & 2) && (sreg&0x80)) scyc++; // take care of raising edge now to compensate lag
if(SekCyclesDoneT()-lastSSRamWrite < 46) {
// data was just written, there was no time to respond (used by sports games)
d = (sreg>>6)&1;
} else if((sreg & 8) && scyc > 9 && scyc != 18 && scyc != 27) {
// started and first command word received
shift = 17-scyc;
if(sreg & 0x20) {
// X24C02+
if(ssa&1) {
//dprintf("read: addr %02x, cycle %i, reg %02x", saddr, scyc, sreg);
d = (SRam.data[saddr]>>shift)&1;
}
} else {
// X24C01
if(saddr&1) {
d = (SRam.data[saddr>>1]>>shift)&1;
}
}
}
//else dprintf("r ack");
return d;
}
void SRAMUpdPending(unsigned int a, unsigned int d)
{
unsigned int sreg = Pico.m.sram_reg;
if(!(a&1)) sreg|=0x20;
if(sreg&0x20) { // address through 0x200000
if(!(a&1)) {
sreg&=~0x80;
sreg|=d<<7;
} else {
sreg&=~0x40;
sreg|=(d<<6)&0x40;
}
} else {
sreg&=~0xc0;
sreg|=d<<6;
}
Pico.m.sram_reg = (unsigned char) sreg;
}

661
Pico/Pico.c Normal file
View file

@ -0,0 +1,661 @@
// 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 "PicoInt.h"
#include "sound/sound.h"
#include "sound/ym2612.h"
int PicoVer=0x0080;
struct Pico Pico;
int PicoOpt=0; // disable everything by default
int PicoSkipFrame=0; // skip rendering frame?
int PicoRegionOverride = 0; // override the region detection 0: Auto, 1: Japan NTSC, 2: Japan PAL, 4: US, 8: Europe
int emustatus = 0;
void (*PicoWriteSound)(void) = 0; // called once per frame at the best time to send sound buffer (PsndOut) to hardware
struct PicoSRAM SRam;
int z80startCycle = 0, z80stopCycle = 0; // in 68k cycles
//int z80ExtraCycles = 0;
int PicoPad[2]; // Joypads, format is SACB RLDU
int PicoMCD = 0; // mega CD status
// to be called once on emu init
int PicoInit(void)
{
// Blank space for state:
memset(&Pico,0,sizeof(Pico));
memset(&PicoPad,0,sizeof(PicoPad));
// Init CPUs:
SekInit();
z80_init(); // init even if we aren't going to use it
// Setup memory callbacks:
PicoMemInit();
PicoInitMCD();
// notaz: sram
SRam.data=0;
SRam.resize=1;
return 0;
}
// to be called once on emu exit
void PicoExit(void)
{
PicoExitMCD();
z80_exit();
// notaz: sram
if(SRam.data) free(SRam.data); SRam.data=0;
}
int PicoReset(int hard)
{
unsigned int region=0;
int support=0,hw=0,i=0;
unsigned char pal=0;
if (Pico.romsize<=0) return 1;
PicoMemReset();
SekReset();
SekCycleCntT=0;
z80_reset();
// reset VDP state, VRAM and PicoMisc
//memset(&Pico.video,0,sizeof(Pico.video));
//memset(&Pico.vram,0,sizeof(Pico.vram));
memset(&Pico.m,0,sizeof(Pico.m));
Pico.video.pending_ints=0;
emustatus = 0;
if(hard) {
// clear all memory of the emulated machine
memset(&Pico.ram,0,(unsigned int)&Pico.rom-(unsigned int)&Pico.ram);
}
// 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;
Pico.m.dirtyPal = 1;
if(PicoRegionOverride)
{
support = PicoRegionOverride;
}
else
{
// Read cartridge region data:
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
{
// New style code:
char s[2]={0,0};
s[0]=(char)c;
support|=strtol(s,NULL,16);
}
}
}
// 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;
Pico.video.status = 0x3408 | pal; // always set bits | vblank | pal
sound_reset(); // pal must be known here
if (PicoMCD & 1) {
PicoResetMCD(hard);
SRam.data = 0;
return 0;
}
// notaz: sram
if(SRam.resize) {
int sram_size = 0;
if(SRam.data) free(SRam.data); SRam.data=0;
Pico.m.sram_reg = 0;
if(*(Pico.rom+0x1B1) == 'R' && *(Pico.rom+0x1B0) == 'A') {
if(*(Pico.rom+0x1B2) & 0x40) {
// EEPROM SRAM
// what kind of EEPROMs are actually used? X24C02? X24C04? (X24C01 has only 128), but we will support up to 8K
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) & 0xFFFF00;
SRam.end = PicoRead32(0x1B8) | 1;
sram_size = SRam.end - SRam.start + 1;
}
Pico.m.sram_reg |= 0x10; // SRAM was detected
}
if(sram_size <= 0) {
// some games may have bad headers, like S&K and Sonic3
SRam.start = 0x200000;
SRam.end = 0x203FFF;
sram_size = 0x004000;
}
// enable sram access by default if it doesn't overlap with ROM
if(Pico.romsize <= SRam.start) Pico.m.sram_reg |= 1;
SRam.reg_back = Pico.m.sram_reg;
if(sram_size) {
SRam.data = (unsigned char *) calloc(sram_size, 1);
if(!SRam.data) return 1;
}
SRam.resize=0;
// Dino Dini's Soccer malfunctions if SRAM is not filled with 0xff
if (strncmp((char *)Pico.rom+0x150, "IDOND NI'I", 10) == 0)
memset(SRam.data, 0xff, sram_size);
}
Pico.m.sram_reg = SRam.reg_back; // restore sram_reg
SRam.changed = 0;
return 0;
}
static __inline void SekRun(int cyc)
{
int cyc_do;
SekCycleAim+=cyc;
if((cyc_do=SekCycleAim-SekCycleCnt) < 0) return;
#if defined(EMU_C68K) && defined(EMU_M68K)
// this means we do run-compare Cyclone vs Musashi
SekCycleCnt+=CM_compareRun(cyc_do);
#elif defined(EMU_C68K)
PicoCpu.cycles=cyc_do;
CycloneRun(&PicoCpu);
SekCycleCnt+=cyc_do-PicoCpu.cycles;
#elif defined(EMU_A68K)
m68k_ICount=cyc_do;
M68000_RUN();
SekCycleCnt+=cyc_do-m68k_ICount;
#elif defined(EMU_M68K)
SekCycleCnt+=m68k_execute(cyc_do);
#endif
}
static __inline void SekStep(void)
{
// this is required for timing sensitive stuff to work
int realaim=SekCycleAim; SekCycleAim=SekCycleCnt+1;
#if defined(EMU_C68K) && defined(EMU_M68K)
// this means we do run-compare Cyclone vs Musashi
SekCycleCnt+=CM_compareRun(1);
#elif defined(EMU_C68K)
PicoCpu.cycles=1;
CycloneRun(&PicoCpu);
SekCycleCnt+=1-PicoCpu.cycles;
#elif defined(EMU_A68K)
m68k_ICount=1;
M68000_RUN();
SekCycleCnt+=1-m68k_ICount;
#elif defined(EMU_M68K)
SekCycleCnt+=m68k_execute(1);
#endif
SekCycleAim=realaim;
}
static int CheckIdle(void)
{
#if 1
unsigned char state[0x88];
memset(state,0,sizeof(state));
// See if the state is the same after 2 steps:
SekState(state); SekStep(); SekStep(); SekState(state+0x44);
if (memcmp(state,state+0x44,0x44)==0) return 1;
#else
unsigned char state[0x44];
static unsigned char oldstate[0x44];
SekState(state);
if(memcmp(state,oldstate,0x40)==0) return 1;
memcpy(oldstate, state, 0x40);
#endif
return 0;
}
// to be called on 224 or line_sample scanlines only
static __inline void getSamples(int y)
{
if(y == 224) {
//dprintf("sta%i: %i [%i]", (emustatus & 2), emustatus, y);
if(emustatus & 2)
sound_render(PsndLen/2, PsndLen-PsndLen/2);
else sound_render(0, PsndLen);
if (emustatus&1) emustatus|=2; else emustatus&=~2;
if (PicoWriteSound) PicoWriteSound();
// clear sound buffer
memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1));
}
else if(emustatus & 3) {
emustatus|= 2;
emustatus&=~1;
sound_render(0, PsndLen/2);
}
}
//extern UINT32 mz80GetRegisterValue(void *, UINT32);
// Accurate but slower frame which does hints
static int PicoFrameHints(void)
{
struct PicoVideo *pv=&Pico.video;
int total_z80=0,lines,y,lines_vis = 224,z80CycleAim = 0,line_sample;
const int cycles_68k=488,cycles_z80=228; // both PAL and NTSC compile to same values
int skip=PicoSkipFrame || (PicoOpt&0x10);
int hint; // Hint counter
if(Pico.m.pal) { //
//cycles_68k = (int) ((double) OSC_PAL / 7 / 50 / 312 + 0.4); // should compile to a constant (488)
//cycles_z80 = (int) ((double) OSC_PAL / 15 / 50 / 312 + 0.4); // 228
lines = 312; // Steve Snake says there are 313 lines, but this seems to also work well
line_sample = 68;
if(pv->reg[1]&8) lines_vis = 240;
} else {
//cycles_68k = (int) ((double) OSC_NTSC / 7 / 60 / 262 + 0.4); // 488
//cycles_z80 = (int) ((double) OSC_NTSC / 15 / 60 / 262 + 0.4); // 228
lines = 262;
line_sample = 93;
}
SekCyclesReset();
//z80ExtraCycles = 0;
if(PicoOpt&4)
z80CycleAim = 0;
// z80_resetCycles();
pv->status&=~0x88; // clear V-Int, come out of vblank
hint=pv->reg[10]; // Load H-Int counter
//dprintf("-hint: %i", hint);
for (y=0;y<lines;y++)
{
Pico.m.scanline=(short)y;
// pad delay (for 6 button pads)
if(PicoOpt&0x20) {
if(Pico.m.padDelay[0]++ > 25) Pico.m.padTHPhase[0]=0;
if(Pico.m.padDelay[1]++ > 25) Pico.m.padTHPhase[1]=0;
}
// H-Interrupts:
if(y <= lines_vis && --hint < 0) // y <= lines_vis: Comix Zone, Golden Axe
{
//dprintf("rhint:old @ %06x", SekPc);
hint=pv->reg[10]; // Reload H-Int counter
pv->pending_ints|=0x10;
if (pv->reg[0]&0x10) SekInterrupt(4);
//dprintf("rhint: %i @ %06x [%i|%i]", hint, SekPc, y, SekCycleCnt);
//dprintf("hint_routine: %x", (*(unsigned short*)(Pico.ram+0x0B84)<<16)|*(unsigned short*)(Pico.ram+0x0B86));
}
// V-Interrupt:
if (y == lines_vis)
{
//dprintf("vint: @ %06x [%i|%i]", SekPc, y, SekCycleCnt);
pv->status|=0x88; // V-Int happened, go into vblank
SekRun(128); SekCycleAim-=128; // there must be a gap between H and V ints, also after vblank bit set (Mazin Saga, Bram Stoker's Dracula)
/*if(Pico.m.z80Run && (PicoOpt&4)) {
z80CycleAim+=cycles_z80/2;
total_z80+=z80_run(z80CycleAim-total_z80);
z80CycleAim-=cycles_z80/2;
}*/
pv->pending_ints|=0x20;
if(pv->reg[1]&0x20) SekInterrupt(6);
if(Pico.m.z80Run && (PicoOpt&4)) // ?
z80_int();
//dprintf("zint: [%i|%i] zPC=%04x", Pico.m.scanline, SekCyclesDone(), mz80GetRegisterValue(NULL, 0));
}
// decide if we draw this line
#if CAN_HANDLE_240_LINES
if(!skip && ((!(pv->reg[1]&8) && y<224) || ((pv->reg[1]&8) && y<240)) )
#else
if(!skip && y<224)
#endif
PicoLine(y);
if(PicoOpt&1)
sound_timers_and_dac(y);
// get samples from sound chips
if(y == 32 && PsndOut)
emustatus &= ~1;
else if((y == 224 || y == line_sample) && PsndOut)
getSamples(y);
// Run scanline:
SekRun(cycles_68k);
if((PicoOpt&4) && Pico.m.z80Run) {
Pico.m.z80Run|=2;
z80CycleAim+=cycles_z80;
total_z80+=z80_run(z80CycleAim-total_z80);
}
}
// draw a frame just after vblank in alternative render mode
if(!PicoSkipFrame && (PicoOpt&0x10))
PicoFrameFull();
return 0;
}
// helper z80 runner
static void PicoRunZ80Simple(int line_from, int line_to)
{
int line_from_r=line_from, line_to_r=line_to, line = line_from;
int line_sample = Pico.m.pal ? 68 : 93;
extern const unsigned short vcounts[];
if(!(PicoOpt&4) || Pico.m.z80Run == 0) { line_from_r = line_to_r; line_to_r = 0; }
if(z80startCycle != 0) {
line_from_r = vcounts[z80startCycle>>8]+1;
z80startCycle = 0;
}
if(z80stopCycle != 0) {
line_to_r = vcounts[z80stopCycle>>8]+1;
z80stopCycle = 0;
}
if(PicoOpt&1) {
// we have ym2612 enabled, so we have to run Z80 in lines, so we could update DAC and timers
for(; line < line_to; line++) {
sound_timers_and_dac(line);
if((line == 224 || line == line_sample) && PsndOut) getSamples(line);
if(line == 32 && PsndOut) emustatus &= ~1;
if(line >= line_from_r && line < line_to_r)
z80_run(228);
}
} else if(line_to_r-line_from_r > 0) {
z80_run(228*(line_to_r-line_from_r));
// samples will be taken by caller
}
}
// Simple frame without H-Ints
static int PicoFrameSimple(void)
{
struct PicoVideo *pv=&Pico.video;
int y=0,line=0,lines=0,lines_step=0,sects;
int cycles_68k_vblock,cycles_68k_block;
if(Pico.m.pal) {
// M68k cycles/frame: 152009.78
if(pv->reg[1]&8) { // 240 lines
cycles_68k_block = (int) ((double) OSC_PAL / 7 / 50 / 312 * 15 + 0.4); // 16 sects, 16*15=240, 7308
cycles_68k_vblock = (int) ((double) OSC_PAL / 7 / 50 / 312 * 24 + 0.4); // 3 sects, 3*24=72, 35163?
lines_step = 15;
} else {
cycles_68k_block = (int) ((double) OSC_PAL / 7 / 50 / 312 * 14 + 0.4); // 16*14=224
cycles_68k_vblock = (int) ((double) OSC_PAL / 7 / 50 / 312 * 22 + 0.4); // 4 sects, 4*22=88
lines_step = 14;
}
} else {
// M68k cycles/frame: 127840.71
cycles_68k_block = (int) ((double) OSC_NTSC / 7 / 60 / 262 * 14 + 0.4); // 16*14=224, 6831
cycles_68k_vblock = (int) ((double) OSC_NTSC / 7 / 60 / 262 * 19 + 0.4); // 2 sects, 2*19=38, 18544
lines_step = 14;
}
Pico.m.scanline=-1;
SekCyclesReset();
if(PicoOpt&4)
z80_resetCycles();
// 6 button pad: let's just say it timed out now
Pico.m.padTHPhase[0]=Pico.m.padTHPhase[1]=0;
// ---- Active Scan ----
pv->status&=~88; // clear V-Int, come out of vblank
// Run in sections:
for(sects=16; sects; sects--)
{
if (CheckIdle()) break;
lines += lines_step;
SekRun(cycles_68k_block);
PicoRunZ80Simple(line, lines);
line=lines;
}
// run Z80 for remaining sections
if(sects) {
int c = sects*cycles_68k_block;
lines += sects*lines_step;
PicoRunZ80Simple(line, lines);
// this is for approriate line counter, etc
SekCycleCnt += c;
SekCycleAim += c;
}
// here we render sound if ym2612 is disabled
if(!(PicoOpt&1) && PsndOut) {
sound_render(0, PsndLen);
if(PicoWriteSound) PicoWriteSound();
// clear sound buffer
memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1));
}
// render screen
if(!PicoSkipFrame) {
if(!(PicoOpt&0x10))
// Draw the screen
#if CAN_HANDLE_240_LINES
if(pv->reg[1]&8) {
for (y=0;y<240;y++) PicoLine(y);
} else {
for (y=0;y<224;y++) PicoLine(y);
}
#else
for (y=0;y<224;y++) PicoLine(y);
#endif
else PicoFrameFull();
}
// ---- V-Blanking period ----
// fix line counts
if(Pico.m.pal) {
if(pv->reg[1]&8) { // 240 lines
lines = line = 240;
sects = 3;
lines_step = 24;
} else {
lines = line = 224;
sects = 4;
lines_step = 22;
}
} else {
lines = line = 224;
sects = 2;
lines_step = 19;
}
//dprintf("vint: @ %06x [%i]", SekPc, SekCycleCnt);
pv->pending_ints|=0x20;
if (pv->reg[1]&0x20) SekInterrupt(6); // Set IRQ
pv->status|=0x88; // V-Int happened / go into vblank
if(Pico.m.z80Run && (PicoOpt&4)) // ?
z80_int();
while(sects) {
lines += lines_step;
SekRun(cycles_68k_vblock);
PicoRunZ80Simple(line, lines);
line=lines;
sects--;
if(sects && CheckIdle()) break;
}
// run Z80 for remaining sections
if(sects) {
lines += sects*lines_step;
PicoRunZ80Simple(line, lines);
}
return 0;
}
int PicoFrame(void)
{
int acc;
if (PicoMCD & 1) {
PicoFrameMCD();
return 0;
}
// be accurate if we are asked for this
if(PicoOpt&0x40) acc=1;
// don't be accurate in alternative render mode, as hint effects will not be rendered anyway
else if(PicoOpt&0x10) acc = 0;
else acc=Pico.video.reg[0]&0x10; // be accurate if hints are used
//if(Pico.video.reg[12]&0x2) Pico.video.status ^= 0x10; // change odd bit in interlace mode
if(!(PicoOpt&0x10))
PicoFrameStart();
if(acc)
PicoFrameHints();
else PicoFrameSimple();
return 0;
}
static int DefaultCram(int cram)
{
int high=0x0841;
// Convert 0000bbbb ggggrrrr
// to rrrr1ggg g10bbbb1
high|=(cram&0x00f)<<12; // Red
high|=(cram&0x0f0)<< 3; // Green
high|=(cram&0xf00)>> 7; // Blue
return high;
}
// Function to convert Megadrive Cram into a native colour:
int (*PicoCram)(int cram)=DefaultCram;
#if defined(__DEBUG_PRINT) || defined(WIN32)
// tmp debug: dump some stuff
#define bit(r, x) ((r>>x)&1)
void z80_debug(char *dstr);
char *debugString()
{
#if 1
static char dstr[1024];
unsigned char *reg=Pico.video.reg, r;
// dump some info
sprintf(dstr, "mode set 1: %02x\n", (r=reg[0]));
sprintf(dstr, "%sdisplay_disable: %i, M3: %i, palette: %i, ?, hints: %i\n\n", dstr, bit(r,0), bit(r,1), bit(r,2), bit(r,4));
sprintf(dstr, "%smode set 2: %02x\n", dstr, (r=reg[1]));
sprintf(dstr, "%sSMS/genesis: %i, pal: %i, dma: %i, vints: %i, disp: %i, TMS9918: %i\n\n",dstr, bit(r,2), bit(r,3), bit(r,4), bit(r,5), bit(r,6), bit(r,7));
sprintf(dstr, "%smode set 3: %02x\n", dstr, (r=reg[0xB]));
sprintf(dstr, "%sLSCR: %i, HSCR: %i, 2cell vscroll: %i, IE2: %i\n\n", dstr, bit(r,0), bit(r,1), bit(r,2), bit(r,3));
sprintf(dstr, "%smode set 4: %02x\n", dstr, (r=reg[0xC]));
sprintf(dstr, "%sinterlace: %i%i; cells: %i; shadow: %i\n\n", dstr, bit(r,2), bit(r,1), (r&0x80) ? 40 : 32, bit(r,3));
sprintf(dstr, "%sscroll size: w: %i; h: %i\n\n", dstr, reg[0x10]&3, (reg[0x10]&0x30)>>4);
sprintf(dstr, "%sSRAM: det: %i; eeprom: %i\n", dstr, bit(Pico.m.sram_reg, 4), bit(Pico.m.sram_reg, 2));
sprintf(dstr, "%sCPU state: PC: %06x cycles: %i\n", dstr, SekPc, SekCyclesDoneT());
#ifdef EMU_C68K
for(r=0; r < 8; r++)
sprintf(dstr, "%sd%i=%08x, a%i=%08x\n", dstr, r, PicoCpu.d[r], r, PicoCpu.a[r]);
#endif
z80_debug(dstr);
#else
struct PicoVideo *pvid=&Pico.video;
int table=0;
int i,u,n,link=0;
static char dstr[1024*8];
dstr[0] = 0;
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=n=0; u < 80 && n < 20; 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;
dprintf("#%02i x: %03i y: %03i %ix%i", u, sx, sy, ((code>>26)&3)+1, height);
link=(code>>16)&0x7f;
if(!link) break; // End of sprites
}
#endif
#if 0
{
FILE *f = fopen("zram", "wb");
fwrite(Pico.zram, 1, 0x2000, f);
fclose(f);
}
#endif
return dstr;
}
#endif

96
Pico/Pico.h Normal file
View file

@ -0,0 +1,96 @@
// -------------------- Pico Library --------------------
// Pico Library - Header File
// (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.
#ifndef PICO_H
#define PICO_H
// port-specific compile-time settings
#include <port_config.h>
#ifdef __cplusplus
extern "C" {
#endif
// Pico.c
// PicoOpt bits LSb->MSb:
// enable_ym2612&dac, enable_sn76496, enable_z80, stereo_sound,
// alt_renderer, 6button_gamepad, accurate_timing, accurate_sprites,
// draw_no_32col_border, external_ym2612
extern int PicoOpt;
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
int PicoInit(void);
void PicoExit(void);
int PicoReset(int hard);
int PicoFrame(void);
extern int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU
extern int (*PicoCram)(int cram); // Callback to convert colour ram 0000bbb0 ggg0rrr0
extern void (*PicoWriteSound)(void); // called once per frame at the best time to send sound buffer (PsndOut) to hardware
int PicoFrameMCD(void);
// Area.c
typedef size_t (arearw)(void *p, size_t _size, size_t _n, 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; // read and write function pointers for
extern arearw *areaWrite; // gzip save state ability
// Cart.c
int PicoCartLoad(FILE *f,unsigned char **prom,unsigned int *psize);
int PicoCartInsert(unsigned char *rom,unsigned int romsize);
// notaz
int CartLoadZip(const char *fname, unsigned char **prom, unsigned int *psize);
void Byteswap(unsigned char *data,int len);
// anotherguest
int PicoUnloadCart(unsigned char* romdata);
// Draw.c
void PicoDrawSetColorFormat(int which); // 0=BGR444, 1=RGB555, 2=8bit(HighPal pal)
extern void *DrawLineDest;
extern int (*PicoScan)(unsigned int num, void *data);
// internals
extern unsigned short HighPal[0x100];
extern int rendstatus;
// utility
#ifdef _ASM_DRAW_C
void *blockcpy(void *dst, const void *src, size_t n);
#else
#define blockcpy memcpy
#endif
// Draw2.c
// stuff below is optional
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;
void sound_reset();
void sound_rerate();
void z80_pack(unsigned char *data);
void z80_unpack(unsigned char *data);
void z80_reset();
// 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

240
Pico/PicoInt.h Normal file
View file

@ -0,0 +1,240 @@
// Pico Library - Header File
// (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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Pico.h"
// to select core, define EMU_C68K, EMU_M68K or EMU_A68K in your makefile
#ifdef __cplusplus
extern "C" {
#endif
// ----------------------- 68000 CPU -----------------------
#ifdef EMU_C68K
#include "../cpu/Cyclone/Cyclone.h"
extern struct Cyclone PicoCpu;
#define SekCyclesLeft PicoCpu.cycles // cycles left for this run
#define SekSetCyclesLeft(c) PicoCpu.cycles=c
#define SekPc (PicoCpu.pc-PicoCpu.membase)
#endif
#ifdef EMU_A68K
void __cdecl M68000_RUN();
// The format of the data in a68k.asm (at the _M68000_regs location)
struct A68KContext
{
unsigned int d[8],a[8];
unsigned int isp,srh,ccr,xc,pc,irq,sr;
int (*IrqCallback) (int nIrq);
unsigned int ppc;
void *pResetCallback;
unsigned int sfc,dfc,usp,vbr;
unsigned int AsmBank,CpuVersion;
};
struct A68KContext M68000_regs;
extern int m68k_ICount;
#define SekCyclesLeft m68k_ICount
#define SekSetCyclesLeft(c) m68k_ICount=c
#define SekPc M68000_regs.pc
#endif
#ifdef EMU_M68K
#include "../cpu/musashi/m68kcpu.h"
extern m68ki_cpu_core PicoM68kCPU; // MD's CPU
extern m68ki_cpu_core PicoS68kCPU; // Mega CD's CPU
#ifndef SekCyclesLeft
#define SekCyclesLeft m68k_cycles_remaining()
#define SekSetCyclesLeft(c) SET_CYCLES(c)
#define SekPc m68k_get_reg(&PicoM68kCPU, M68K_REG_PC)
#define SekPcS68k m68k_get_reg(&PicoS68kCPU, M68K_REG_PC)
#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+=SekCycleCnt;SekCycleCnt=SekCycleAim=0;}
#define SekCyclesBurn(c) SekCycleCnt+=c
#define SekCyclesDone() (SekCycleAim-SekCyclesLeft) // nuber 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=0;}
// does not work as expected
//extern int z80ExtraCycles; // extra z80 cycles, used when z80 is [en|dis]abled
extern int PicoMCD;
// ---------------------------------------------------------
// main oscillator clock which controls timing
#define OSC_NTSC 53693100
#define OSC_PAL 53203424 // not accurate
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????
unsigned char pad[0x13];
};
struct PicoMisc
{
unsigned char rotate;
unsigned char z80Run;
unsigned char padTHPhase[2]; // phase of gamepad TH switches
short scanline; // 0 to 261||311; -1 in fast mode
char dirtyPal; // Is the palette dirty (1 - change @ this frame, 2 - some time before)
unsigned char hardware; // Hardware value for country
unsigned char pal; // 1=PAL 0=NTSC
unsigned char sram_reg; // SRAM mode register. bit0: allow read? bit1: deny write? bit2: EEPROM?
unsigned short z80_bank68k;
unsigned short z80_lastaddr; // this is for Z80 faking
unsigned char z80_fakeval;
unsigned char pad0;
unsigned char padDelay[2]; // gamepad phase time outs, so we count a delay
unsigned short sram_addr; // EEPROM address register
unsigned char sram_cycle; // EEPROM SRAM cycle number
unsigned char sram_slave; // EEPROM slave word for X24C02 and better SRAMs
unsigned char prot_bytes[2]; // simple protection fakeing
unsigned char pad1[8];
};
// 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 resize; // 1=SRAM size changed and needs to be reallocated on PicoReset
unsigned char reg_back; // copy of Pico.m.sram_reg to set after reset
unsigned char changed;
unsigned char pad;
};
// MCD
#include "cd/cd_sys.h"
#include "cd/LC89510.h"
typedef struct
{
unsigned char bios[0x20000];
union {
unsigned char prg_ram[0x80000];
unsigned char prg_ram_b[4][0x20000];
};
unsigned char word_ram[0x40000];
unsigned char s68k_regs[0x200];
unsigned char m68k_regs[0x10];
CDD cdd;
CDC cdc;
_scd scd;
} mcd_state;
#define Pico_mcd ((mcd_state *)Pico.rom)
// Draw.c
int PicoLine(int scan);
void PicoFrameStart();
// Draw2.c
void PicoFrameFull();
// Memory.c
int PicoInitPc(unsigned int pc);
unsigned int CPU_CALL PicoRead32(unsigned int a);
int PicoMemInit();
void PicoMemReset();
void PicoDasm(int start,int len);
unsigned char z80_read(unsigned short a);
unsigned short z80_read16(unsigned short a);
void z80_write(unsigned char data, unsigned short a);
void z80_write16(unsigned short data, unsigned short a);
// cd/Memory.c
unsigned char PicoReadCD8 (unsigned int a);
unsigned short PicoReadCD16(unsigned int a);
unsigned int PicoReadCD32(unsigned int a);
void PicoWriteCD8 (unsigned int a, unsigned char d);
void PicoWriteCD16(unsigned int a, unsigned short d);
void PicoWriteCD32(unsigned int a, unsigned int d);
// Pico.c
extern struct Pico Pico;
extern struct PicoSRAM SRam;
extern int emustatus;
// cd/Pico.c
int PicoInitMCD(void);
void PicoExitMCD(void);
int PicoResetMCD(int hard);
// Sek.c
int SekInit(void);
int SekReset(void);
int SekInterrupt(int irq);
void SekState(unsigned char *data);
// cd/Sek.c
int SekInitS68k(void);
int SekResetS68k(void);
int SekInterruptS68k(int irq);
// VideoPort.c
void PicoVideoWrite(unsigned int a,unsigned short d);
unsigned int PicoVideoRead(unsigned int a);
// Misc.c
void SRAMWriteEEPROM(unsigned int d);
unsigned int SRAMReadEEPROM();
void SRAMUpdPending(unsigned int a, unsigned int d);
#ifdef __cplusplus
} // End of extern "C"
#endif

192
Pico/Sek.c Normal file
View file

@ -0,0 +1,192 @@
// 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 "PicoInt.h"
int SekCycleCnt=0; // cycles done in this frame
int SekCycleAim=0; // cycle aim
unsigned int SekCycleCntT=0;
#ifdef EMU_C68K
// ---------------------- Cyclone 68000 ----------------------
struct Cyclone PicoCpu;
#endif
#ifdef EMU_M68K
// ---------------------- MUSASHI 68000 ----------------------
m68ki_cpu_core PicoM68kCPU; // MD's CPU
#endif
#ifdef EMU_A68K
// ---------------------- A68K ----------------------
void __cdecl M68000_RESET();
int m68k_ICount=0;
unsigned int mem_amask=0xffffff; // 24-bit bus
unsigned int mame_debug=0,cur_mrhard=0,m68k_illegal_opcode=0,illegal_op=0,illegal_pc=0,opcode_entry=0; // filler
static int IrqCallback(int i) { i; return -1; }
static int DoReset() { return 0; }
static int (*ResetCallback)()=DoReset;
#pragma warning (disable:4152)
#endif
// interrupt acknowledgment
#ifdef EMU_C68K
static void SekIntAck(int level)
{
// try to emulate VDP's reaction to 68000 int ack
if (level == 4) Pico.video.pending_ints = 0;
else if(level == 6) Pico.video.pending_ints &= ~0x20;
PicoCpu.irq = 0;
}
static void SekResetAck()
{
#if defined(__DEBUG_PRINT) || defined(WIN32)
dprintf("Reset encountered @ %06x", SekPc);
#endif
}
static int SekUnrecognizedOpcode()
{
unsigned int pc, op;
pc = SekPc;
op = PicoCpu.read16(pc);
#if defined(__DEBUG_PRINT) || defined(WIN32)
dprintf("Unrecognized Opcode %04x @ %06x", op, pc);
#endif
// see if we are not executing trash
if (pc < 0x200 || (pc > Pico.romsize+4 && (pc&0xe00000)!=0xe00000)) {
PicoCpu.cycles = 0;
PicoCpu.stopped = 1;
return 1;
}
//exit(1);
return 0;
}
#endif
#ifdef EMU_M68K
static int SekIntAckM68K(int level)
{
if (level == 4) { Pico.video.pending_ints = 0; } // dprintf("hack: [%i|%i]", Pico.m.scanline, SekCyclesDone()); }
else if(level == 6) { Pico.video.pending_ints &= ~0x20; } // dprintf("vack: [%i|%i]", Pico.m.scanline, SekCyclesDone()); }
CPU_INT_LEVEL = 0;
return M68K_INT_ACK_AUTOVECTOR;
}
#endif
int SekInit()
{
#ifdef EMU_C68K
CycloneInit();
memset(&PicoCpu,0,sizeof(PicoCpu));
PicoCpu.IrqCallback=SekIntAck;
PicoCpu.ResetCallback=SekResetAck;
PicoCpu.UnrecognizedCallback=SekUnrecognizedOpcode;
#endif
#ifdef EMU_A68K
memset(&M68000_regs,0,sizeof(M68000_regs));
M68000_regs.IrqCallback=IrqCallback;
M68000_regs.pResetCallback=ResetCallback;
M68000_RESET(); // Init cpu emulator
#endif
#ifdef EMU_M68K
{
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoM68kCPU);
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
m68k_init();
m68k_set_int_ack_callback(SekIntAckM68K);
m68k_pulse_reset(); // Init cpu emulator
m68k_set_context(oldcontext);
}
#endif
return 0;
}
// Reset the 68000:
int SekReset()
{
if (Pico.rom==NULL) return 1;
#ifdef EMU_C68K
PicoCpu.stopped=0;
PicoCpu.osp=0;
PicoCpu.srh =0x27; // Supervisor mode
PicoCpu.flags=4; // Z set
PicoCpu.irq=0;
PicoCpu.a[7]=PicoCpu.read32(0); // Stack Pointer
PicoCpu.membase=0;
PicoCpu.pc=PicoCpu.checkpc(PicoCpu.read32(4)); // Program Counter
#endif
#ifdef EMU_A68K
// Reset CPU: fetch SP and PC
M68000_regs.srh=0x27; // Supervisor mode
M68000_regs.a[7]=PicoRead32(0);
M68000_regs.pc =PicoRead32(4);
PicoInitPc(M68000_regs.pc);
#endif
#ifdef EMU_M68K
{
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoM68kCPU);
m68k_pulse_reset();
m68k_set_context(oldcontext);
}
#endif
return 0;
}
int SekInterrupt(int irq)
{
#ifdef EMU_C68K
PicoCpu.irq=irq;
#endif
#ifdef EMU_A68K
M68000_regs.irq=irq; // raise irq (gets lowered after taken)
#endif
#ifdef EMU_M68K
{
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoM68kCPU);
m68k_set_irq(irq); // raise irq (gets lowered after taken or must be done in ack)
m68k_set_context(oldcontext);
}
#endif
return 0;
}
//int SekPc() { return PicoCpu.pc-PicoCpu.membase; }
//int SekPc() { return M68000_regs.pc; }
//int SekPc() { return m68k_get_reg(NULL, M68K_REG_PC); }
void SekState(unsigned char *data)
{
#ifdef EMU_C68K
memcpy(data,PicoCpu.d,0x44);
#elif defined(EMU_A68K)
memcpy(data, M68000_regs.d, 0x40);
memcpy(data+0x40,&M68000_regs.pc,0x04);
#elif defined(EMU_M68K)
memcpy(data, PicoM68kCPU.dar,0x40);
memcpy(data+0x40,&PicoM68kCPU.pc, 0x04);
#endif
}

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 "PicoInt.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;
}

432
Pico/VideoPort.c Normal file
View file

@ -0,0 +1,432 @@
// 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 "PicoInt.h"
extern const unsigned char hcounts_32[];
extern const unsigned char hcounts_40[];
extern const unsigned short vcounts[];
extern int rendstatus;
typedef unsigned short u16;
static __inline void AutoIncrement()
{
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;
rendstatus|=0x10; break;
case 3: Pico.m.dirtyPal = 1;
//dprintf("w[%i] @ %04x, inc=%i [%i|%i]", Pico.video.type, a, Pico.video.reg[0xf], Pico.m.scanline, SekCyclesDone());
Pico.cram [(a>>1)&0x003f]=d; break; // wraps (Desert Strike)
case 5: Pico.vsram[(a>>1)&0x003f]=d; break;
}
//dprintf("w[%i] @ %04x, inc=%i [%i|%i]", Pico.video.type, a, Pico.video.reg[0xf], Pico.m.scanline, SekCyclesDone());
AutoIncrement();
}
static unsigned int VideoRead()
{
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;
}
AutoIncrement();
return d;
}
// calculate the number of cycles 68k->VDP dma operation would take
static int DmaSlowBurn(int len)
{
// test: Legend of Galahad, Time Killers
int burn,maxlen,line=Pico.m.scanline;
if(line == -1) line=vcounts[SekCyclesDone()>>8];
maxlen=(224-line)*18;
if(len <= maxlen)
burn = len*(((488<<8)/18))>>8;
else {
burn = maxlen*(((488<<8)/18))>>8;
burn += (len-maxlen)*(((488<<8)/180))>>8;
}
return burn;
}
static int GetDmaLength()
{
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, burn;
source =Pico.video.reg[0x15]<<1;
source|=Pico.video.reg[0x16]<<9;
source|=Pico.video.reg[0x17]<<17;
//dprintf("DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%i|%i]", Pico.video.type, source, a, len, inc,
// (Pico.video.status&8)||!(Pico.video.reg[1]&0x40), Pico.m.scanline, SekCyclesDone());
if ((source&0xe00000)==0xe00000) { pd=(u16 *)(Pico.ram+(source&0xfffe)); pdend=(u16 *)(Pico.ram+0x10000); } // Ram
else if(source<Pico.romsize) { pd=(u16 *)(Pico.rom+(source&~1)); pdend=(u16 *)(Pico.rom+Pico.romsize); } // Rom
else return; // Invalid source address
// CPU is stopped during DMA, so we burn some cycles to compensate that
if((Pico.video.status&8)||!(Pico.video.reg[1]&0x40)) { // vblank?
burn = (len*(((488<<8)/167))>>8); // very approximate
if(!(Pico.video.status&8)) burn+=burn>>1; // a hack for Legend of Galahad
} else burn = DmaSlowBurn(len);
SekCyclesBurn(burn);
if(!(Pico.video.status&8))
SekEndRun(0);
//dprintf("DmaSlow burn: %i @ %06x", burn, SekPc);
switch (Pico.video.type)
{
case 1: // vram
r = Pico.vram;
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|=0x10;
break;
case 3: // cram
//dprintf("DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%i|%i]", Pico.video.type, source, a, len, inc,
// (Pico.video.status&8)||!(Pico.video.reg[1]&0x40), Pico.m.scanline, SekCyclesDone());
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;
}
// 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;
//dprintf("DmaCopy len %i [%i|%i]", len, Pico.m.scanline, SekCyclesDone());
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|=0x10;
}
// 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();
//dprintf("DmaFill len %i inc %i [%i|%i]", len, inc, Pico.m.scanline, SekCyclesDone());
// 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|=0x10;
}
static void CommandDma()
{
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()
{
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;
//dprintf("addr set: %04x", addr);
// Check for dma:
if (cmd&0x80) CommandDma();
}
void PicoVideoWrite(unsigned int a,unsigned short d)
{
struct PicoVideo *pvid=&Pico.video;
a&=0x1c;
if (a==0x00) // Data port 0 or 2
{
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
{
VideoWrite(d);
}
return;
}
if (a==0x04) // Control (command) port 4 or 6
{
//dprintf("vdp cw(%04x), p=%i @ %06x [%i]", d, pvid->pending, SekPc, SekCyclesDone());
if(pvid->pending)
{
// 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;
//if(num==00) dprintf("hint_onoff: %i->%i [%i|%i] pend=%i @ %06x", (pvid->reg[0]&0x10)>>4, (d&0x10)>>4, Pico.m.scanline, SekCyclesDone(), (pvid->pending_ints&0x10)>>4, SekPc);
//if(num==01) dprintf("vint_onoff: %i->%i [%i|%i] pend=%i @ %06x", (pvid->reg[1]&0x20)>>5, (d&0x20)>>5, Pico.m.scanline, SekCyclesDone(), (pvid->pending_ints&0x20)>>5, SekPc);
//if(num==01) dprintf("set_blank: %i @ %06x [%i|%i]", !((d&0x40)>>6), SekPc, Pico.m.scanline, SekCyclesDone());
//if(num==05) dprintf("spr_set: %i @ %06x [%i|%i]", (unsigned char)d, SekPc, Pico.m.scanline, SekCyclesDone());
//if(num==10) dprintf("hint_set: %i @ %06x [%i|%i]", (unsigned char)d, SekPc, Pico.m.scanline, SekCyclesDone());
pvid->reg[num]=(unsigned char)d;
#if !(defined(EMU_C68K) && defined(EMU_M68K)) // not debugging Cyclone
// update IRQ level (Lemmings, Wiz 'n' Liz intro, ... )
// may break if done improperly:
// International Superstar Soccer Deluxe (crash), Street Racer (logos), Burning Force (gfx), Fatal Rewind (hang), Sesame Street Counting Cafe
if(num < 2) {
#ifdef EMU_C68K
// hack: make sure we do not touch the irq line if Cyclone is just about to take the IRQ
if (PicoCpu.irq <= (PicoCpu.srh&7)) {
#endif
int lines, pints;
lines = (pvid->reg[1] & 0x20) | (pvid->reg[0] & 0x10);
pints = (pvid->pending_ints&lines);
if(pints & 0x20) SekInterrupt(6);
else if(pints & 0x10) SekInterrupt(4);
else SekInterrupt(0);
#ifdef EMU_C68K
// adjust cycles for Cyclone so it would take the int "in time"
if(PicoCpu.irq) {
SekEndRun(24);
}
}
#endif
}
else
#endif
if(num == 5) rendstatus|=1;
else if(num == 0xc) Pico.m.dirtyPal = 2; // renderers should update their palettes if sh/hi mode is changed
pvid->type=0; // register writes clear command (else no Sega logo in Golden Axe II)
} else {
// High word of command:
pvid->command&=0x0000ffff;
pvid->command|=d<<16;
pvid->pending=1;
}
}
}
}
unsigned int PicoVideoRead(unsigned int a)
{
unsigned int d=0;
a&=0x1c;
if (a==0x00) // data port
{
d=VideoRead();
goto end;
}
if (a==0x04) // control port
{
//dprintf("sr_read @ %06x [%i|%i]", SekPc, Pico.m.scanline, SekCyclesDone());
d=Pico.video.status;
if(PicoOpt&0x10) d|=0x0020; // sprite collision (Shadow of the Beast)
if(Pico.m.rotate++&8) d|=0x0100; else d|=0x0200; // Toggle fifo full empty (who uses that stuff?)
if(!(Pico.video.reg[1]&0x40)) d|=0x0008; // set V-Blank if display is disabled
if(SekCyclesLeft < 84+4) d|=0x0004; // H-Blank (Sonic3 vs)
Pico.video.pending=0; // ctrl port reads clear write-pending flag (Charles MacDonald)
goto end;
}
// 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 hc;
if(Pico.m.scanline != -1) {
int lineCycles=(488-SekCyclesLeft)&0x1ff;
d=Pico.m.scanline; // V-Counter
if(Pico.video.reg[12]&1)
hc=hcounts_40[lineCycles];
else hc=hcounts_32[lineCycles];
if(lineCycles > 488-12) d++; // Wheel of Fortune
} else {
// get approximate V-Counter
d=vcounts[SekCyclesDone()>>8];
hc = Pico.m.rotate&0xff;
}
if(Pico.m.pal) {
if(d >= 0x103) d-=56; // based on Gens
} else {
if(d >= 0xEB) d-=6;
}
if((Pico.video.reg[12]&6) == 6) {
// interlace mode 2 (Combat Cars (UE) [!])
d <<= 1;
if (d&0xf00) d|= 1;
}
//dprintf("hv: %02x %02x (%i) @ %06x", hc, d, SekCyclesDone(), SekPc);
d&=0xff; d<<=8;
d|=hc;
goto end;
}
end:
return d;
}

130
Pico/_cyclone_debug.c Normal file
View file

@ -0,0 +1,130 @@
#include "PicoInt.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;
//static
void dumpPCandExit()
{
char buff[128];
int i;
m68k_disassemble(buff, pppc, M68K_CPU_TYPE_68000);
dprintf("PC: %06x: %04x: %s", pppc, ppop, buff);
for(i=0; i < 8; i++)
dprintf("d%i=%08x, a%i=%08x | d%i=%08x, a%i=%08x", i, PicoCpu.d[i], i, PicoCpu.a[i], i, old_regs[i], i, old_regs[i+8]);
dprintf("SR: %04x | %04x (??s? 0iii 000x nzvc)", CycloneGetSr(&PicoCpu), old_sr);
dprintf("last_read: %08x @ %06x", lastread_d[--lrp_cyc&15], lastread_a);
dprintf("ops done: %i", ops);
exit(1);
}
int CM_compareRun(int cyc)
{
char *str;
int cyc_done=0, cyc_cyclone, cyc_musashi, err=0;
unsigned int i, mu_sr;
lrp_cyc = lrp_mus = 0;
while(cyc > cyc_done) {
pppc = SekPc;
ppop = m68k_read_disassembler_16(pppc);
memcpy(old_regs, PicoCpu.d, 4*16);
old_sr = CycloneGetSr(&PicoCpu);
PicoCpu.cycles=1;
CycloneRun(&PicoCpu);
cyc_cyclone=1-PicoCpu.cycles;
cyc_musashi=m68k_execute(1);
if(cyc_cyclone != cyc_musashi) {
dprintf("cycles: %i vs %i", cyc_cyclone, 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
if( SekPc != (m68ki_cpu.pc&0xffffff) ) {
dprintf("PC: %06x vs %06x", SekPc, m68ki_cpu.pc&0xffffff);
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(PicoCpu.d[i] != m68ki_cpu.dar[i]) {
str = (i < 8) ? "d" : "a";
dprintf("reg: %s%i: %08x vs %08x", str, i&7, PicoCpu.d[i], m68ki_cpu.dar[i]);
err=1;
break;
}
}
// SR
if((CycloneGetSr(&PicoCpu)) != (mu_sr = m68k_get_reg(NULL, M68K_REG_SR))) {
dprintf("SR: %04x vs %04x (??s? 0iii 000x nzvc)", CycloneGetSr(&PicoCpu), mu_sr);
err=1;
}
// IRQl
if(PicoCpu.irq != (m68ki_cpu.int_level>>8)) {
dprintf("IRQ: %i vs %i", PicoCpu.irq, (m68ki_cpu.int_level>>8));
err=1;
}
// OSP/USP
if(PicoCpu.osp != m68ki_cpu.sp[((mu_sr>>11)&4)^4]) {
dprintf("OSP: %06x vs %06x", PicoCpu.osp, m68ki_cpu.sp[0]);
err=1;
}
// stopped
if((PicoCpu.stopped && !m68ki_cpu.stopped) || (!PicoCpu.stopped && m68ki_cpu.stopped)) {
dprintf("stopped: %i vs %i", PicoCpu.stopped, m68ki_cpu.stopped);
err=1;
}
if(err) dumpPCandExit();
#if 0
m68k_set_reg(M68K_REG_SR, ((mu_sr-1)&~0x2000)|(mu_sr&0x2000)); // broken
CycloneSetSr(&PicoCpu, ((mu_sr-1)&~0x2000)|(mu_sr&0x2000));
PicoCpu.stopped = m68ki_cpu.stopped = 0;
if(SekPc > 0x400 && (PicoCpu.a[7] < 0xff0000 || PicoCpu.a[7] > 0xffffff))
PicoCpu.a[7] = m68ki_cpu.dar[15] = 0xff8000;
#endif
cyc_done += cyc_cyclone;
ops++;
}
return cyc_done;
}

658
Pico/cd/LC89510.c Normal file
View file

@ -0,0 +1,658 @@
#if 0
#include <stdio.h>
#include <windows.h>
#include "misc.h"
#include "lc89510.h"
#include "cd_aspi.h"
#include "Star_68k.h"
#include "mem_S68k.h"
#include "pcm.h"
#endif
#include "../PicoInt.h"
#define cdprintf printf
//#define cdprintf(x...)
#define CDC_DMA_SPEED 256
int CDC_Decode_Reg_Read;
static int Status_CDC; // internal status
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, (16 * 1024 * 2) + 2352);
CDC_Update_Header();
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;
CDC_Decode_Reg_Read = 0;
Status_CDC = 0;
}
void LC89510_Reset(void)
{
CDD_Reset();
CDC_Reset();
Pico_mcd->cdc.Host_Data = 0;
Pico_mcd->cdc.DMA_Adr = 0;
Pico_mcd->cdc.Stop_Watch = 0;
}
#if 0 // TODO
void Update_CDC_TRansfer(void)
{
unsigned int i, dep, lenght, add_dest;
unsigned char *dest;
if ((Status_CDC & 0x08) == 0) return;
switch(Pico_mcd->s68k_regs[4] & 7)
{
case 0x0200: // MAIN CPU
case 0x0300: // SUB CPU
Pico_mcd->s68k_regs[4] |= 0x40; // Data ready in host port
return;
case 0x0400: // PCM RAM
dest = (unsigned char *) Ram_PCM;
dep = ((Pico_mcd->cdc.DMA_Adr & 0x03FF) << 2) + PCM_Chip.Bank;
add_dest = 2;
break;
case 0x0500: // PRG RAM
dest = (unsigned char *) Ram_Prg;
dep = (Pico_mcd->cdc.DMA_Adr & 0xFFFF) << 3;
add_dest = 2;
// cdprintf("DMA transfer PRG RAM : adr = %.8X ", dep);
break;
case 0x0700: // WORD RAM
if (Ram_Word_State >= 2)
{
dest = (unsigned char *) Ram_Word_1M;
add_dest = 2;
if (Ram_Word_State & 1) dep = ((Pico_mcd->cdc.DMA_Adr & 0x3FFF) << 3);
else dep = ((Pico_mcd->cdc.DMA_Adr & 0x3FFF) << 3) + 0x20000;
}
else
{
dest = (unsigned char *) Ram_Word_2M;
dep = ((Pico_mcd->cdc.DMA_Adr & 0x7FFF) << 3);
add_dest = 2;
}
break;
default:
return;
}
if (Pico_mcd->cdc.DBC.N <= (CDC_DMA_SPEED * 2))
{
lenght = (Pico_mcd->cdc.DBC.N + 1) >> 1;
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 (Int_Mask_S68K & 0x20) sub68k_interrupt(5, -1);
cdprintf("CDC - DTE interrupt\n");
}
}
else lenght = CDC_DMA_SPEED;
// cdprintf("DMA lenght = %.4X\n", lenght);
if ((Pico_mcd->s68k_regs[4] & 7) == 4) // PCM DMA
{
__asm
{
mov ecx, lenght
mov edi, dest
lea esi, Pico_mcd->cdc.Buffer
add edi, dep
add esi, Pico_mcd->cdc.DAC.N
mov ebx, add_dest
Loop_DMA_PCM:
mov ax, [esi]
add esi, 2
mov [edi], ax
add edi, ebx
dec ecx
jnz Loop_DMA_PCM
}
lenght <<= 1;
Pico_mcd->cdc.DMA_Adr += lenght >> 2;
}
else // OTHER DMA
{
__asm
{
mov ecx, lenght
mov edi, dest
lea esi, Pico_mcd->cdc.Buffer
add edi, dep
add esi, Pico_mcd->cdc.DAC.N
mov ebx, add_dest
Loop_DMA:
mov ax, [esi]
add esi, 2
rol ax, 8
mov [edi], ax
add edi, ebx
dec ecx
jnz Loop_DMA
}
lenght <<= 1;
Pico_mcd->cdc.DMA_Adr += lenght >> 3;
}
Pico_mcd->cdc.DAC.N = (Pico_mcd->cdc.DAC.N + lenght) & 0xFFFF;
if (Status_CDC & 0x08) Pico_mcd->cdc.DBC.N -= lenght;
else Pico_mcd->cdc.DBC.N = 0;
}
#endif
unsigned short Read_CDC_Host(int is_sub)
{
int addr;
if (!(Status_CDC & 0x08))
{
// 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
return 0;
}
Pico_mcd->cdc.DBC.N -= 2;
if (Pico_mcd->cdc.DBC.N <= 0)
{
Pico_mcd->cdc.DBC.N = 0;
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)) {
dprintf("m68k: s68k irq 5");
SekInterruptS68k(5);
}
cdprintf("CDC - DTE interrupt\n");
}
}
addr = Pico_mcd->cdc.DAC.N;
Pico_mcd->cdc.DAC.N += 2;
return (Pico_mcd->cdc.Buffer[addr]<<8) | Pico_mcd->cdc.Buffer[addr+1];
#if 0
__asm
{
mov esi, Pico_mcd->cdc.DAC.N
lea ebx, Pico_mcd->cdc.Buffer
// and esi, 0x3FFF
mov ax, [ebx + esi]
add esi, 2
rol ax, 8
mov Pico_mcd->cdc.DAC.N, esi
mov val, ax
}
#endif
}
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;
}
}
unsigned char CDC_Read_Reg(void)
{
unsigned char ret;
cdprintf("CDC read reg %.2d = ", Pico_mcd->s68k_regs[5] & 0xF);
switch(Pico_mcd->s68k_regs[5] & 0xF)
{
case 0x0: // COMIN
cdprintf("%.2X\n", Pico_mcd->cdc.COMIN);
Pico_mcd->s68k_regs[5] = 0x1;
return Pico_mcd->cdc.COMIN;
case 0x1: // IFSTAT
cdprintf("%.2X\n", Pico_mcd->cdc.IFSTAT);
CDC_Decode_Reg_Read |= (1 << 1); // Reg 1 (decoding)
Pico_mcd->s68k_regs[5] = 0x2;
return Pico_mcd->cdc.IFSTAT;
case 0x2: // DBCL
cdprintf("%.2X\n", Pico_mcd->cdc.DBC.B.L);
Pico_mcd->s68k_regs[5] = 0x3;
return Pico_mcd->cdc.DBC.B.L;
case 0x3: // DBCH
cdprintf("%.2X\n", Pico_mcd->cdc.DBC.B.H);
Pico_mcd->s68k_regs[5] = 0x4;
return Pico_mcd->cdc.DBC.B.H;
case 0x4: // HEAD0
cdprintf("%.2X\n", Pico_mcd->cdc.HEAD.B.B0);
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("%.2X\n", Pico_mcd->cdc.HEAD.B.B1);
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("%.2X\n", Pico_mcd->cdc.HEAD.B.B2);
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("%.2X\n", Pico_mcd->cdc.HEAD.B.B3);
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("%.2X\n", Pico_mcd->cdc.PT.B.L);
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("%.2X\n", Pico_mcd->cdc.PT.B.H);
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("%.2X\n", Pico_mcd->cdc.WA.B.L);
Pico_mcd->s68k_regs[5] = 0xB;
return Pico_mcd->cdc.WA.B.L;
case 0xB: // WAH
cdprintf("%.2X\n", Pico_mcd->cdc.WA.B.H);
Pico_mcd->s68k_regs[5] = 0xC;
return Pico_mcd->cdc.WA.B.H;
case 0xC: // STAT0
cdprintf("%.2X\n", Pico_mcd->cdc.STAT.B.B0);
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("%.2X\n", Pico_mcd->cdc.STAT.B.B1);
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("%.2X\n", Pico_mcd->cdc.STAT.B.B2);
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("%.2X\n", 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 ((CDC_Decode_Reg_Read & 0x73F2) == 0x73F2)
Pico_mcd->cdc.STAT.B.B3 = 0x80;
}
return ret;
}
return 0;
}
void CDC_Write_Reg(unsigned char Data)
{
cdprintf("CDC write reg%d = %.2X\n", 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;
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
Status_CDC |= 0x08; // Data transfer in progress
Pico_mcd->s68k_regs[4] &= 0x7F; // A data transfer start
cdprintf("\n************** Starting Data Transfer ***********\n");
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->cdc.DMA_Adr);
}
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);
}
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[0x36] &= 3; // CDD.Control
if (Pico_mcd->s68k_regs[0x33] & (1<<4))
{
dprintf("cdd export irq 4");
SekInterruptS68k(4);
}
cdprintf("CDD exported status\n");
cdprintf("Status =%.4X, Minute=%.4X, Second=%.4X, Frame=%.4X Checksum=%.4X\n",
(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]);
}
void CDD_Import_Command(void)
{
cdprintf("CDD importing command\n");
cdprintf("Command=%.4X, Minute=%.4X, Second=%.4X, Frame=%.4X Checksum=%.4X\n",
(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 lenght (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;
}
}

130
Pico/cd/LC89510.h Normal file
View file

@ -0,0 +1,130 @@
#ifndef _LC89510_H
#define _LC89510_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
unsigned int Host_Data;
unsigned int DMA_Adr;
unsigned int Stop_Watch;
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 char Buffer[(32 * 1024 * 2) + 2352];
} 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;
} CDD;
extern int CDC_Decode_Reg_Read;
void LC89510_Reset(void);
unsigned short Read_CDC_Host(int is_sub);
void Update_CDC_TRansfer(void);
void CDC_Update_Header(void);
unsigned char CDC_Read_Reg(void);
void CDC_Write_Reg(unsigned char Data);
void CDD_Export_Status(void);
void CDD_Import_Command(void);
unsigned char SCD_Read_Byte(unsigned int Adr);
unsigned short SCD_Read_Word(unsigned int Adr);
#ifdef __cplusplus
};
#endif
#endif

788
Pico/cd/Memory.c Normal file
View file

@ -0,0 +1,788 @@
// 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.
// A68K no longer supported here
//#define __debug_io
#include "../PicoInt.h"
#include "../sound/sound.h"
#include "../sound/ym2612.h"
#include "../sound/sn76496.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
//#define __debug_io
//#define __debug_io2
// -----------------------------------------------------------------
extern m68ki_cpu_core m68ki_cpu;
extern int counter75hz;
static u32 m68k_reg_read16(u32 a, int realsize)
{
u32 d=0;
a &= 0x3e;
dprintf("m68k_regs r%2i: [%02x] @%06x", realsize&~1, a+(realsize&1), SekPc);
switch (a) {
case 2:
d = (Pico_mcd->m68k_regs[a]<<8) | Pico_mcd->m68k_regs[a+1] | 1; // for now 2M to m68k
goto end;
case 8:
dprintf("m68k host data read");
d = Read_CDC_Host(0);
goto end;
case 0xC:
dprintf("m68k stopwatch read");
break;
}
if (a < 0xE) {
d = (Pico_mcd->m68k_regs[a]<<8) | Pico_mcd->m68k_regs[a+1];
goto end;
}
if (a < 0x30) {
// comm flag/cmd/status (0xE-0x2F)
d = (Pico_mcd->s68k_regs[a]<<8) | Pico_mcd->s68k_regs[a+1];
goto end;
}
dprintf("m68k_regs invalid read @ %02x", a);
end:
dprintf("ret = %04x", d);
return d;
}
static void m68k_reg_write8(u32 a, u32 d, int realsize)
{
a &= 0x3f;
dprintf("m68k_regs w%2i: [%02x] %02x @%06x", realsize, a, d, SekPc);
switch (a) {
case 0:
if ((d&1) && (Pico_mcd->s68k_regs[0x33]&(1<<2))) { dprintf("m68k: s68k irq 2"); SekInterruptS68k(2); }
break;
case 1:
if (!(d&1)) PicoMCD |= 2; // reset pending, needed to be sure we fetch the right vectors on reset
if ( (Pico_mcd->m68k_regs[1]&1) != (d&1)) dprintf("m68k: s68k reset %i", !(d&1));
if ( (Pico_mcd->m68k_regs[1]&2) != (d&2)) dprintf("m68k: s68k brq %i", (d&2)>>1);
if (/*!(Pico_mcd->m68k_regs[1]&1) &&*/ (PicoMCD&2) && (d&3)==1) {
SekResetS68k(); // S68k comes out of RESET or BRQ state
PicoMCD&=~2;
dprintf("m68k: resetting s68k");
}
break;
case 3:
if ((Pico_mcd->m68k_regs[3]>>6) != ((d>>6)&3))
dprintf("m68k: prg bank: %i -> %i", (Pico_mcd->m68k_regs[a]>>6), ((d>>6)&3));
if ((Pico_mcd->m68k_regs[3]&4) != (d&4)) dprintf("m68k: ram mode %i mbit", (d&4) ? 1 : 2);
if ((Pico_mcd->m68k_regs[3]&2) != (d&2)) dprintf("m68k: %s", (d&4) ? ((d&2) ? "word swap req" : "noop?") :
((d&2) ? "word ram to s68k" : "word ram to m68k"));
break;
case 0xe:
dprintf("m68k: comm flag: %02x", d);
dprintf("s68k @ %06x", SekPcS68k);
Pico_mcd->s68k_regs[0xe] = d;
break;
}
if ((a&0xff) == 0x10) {
Pico_mcd->s68k_regs[a] = d;
}
if (a >= 0x20 || (a >= 0xa && a <= 0xd) || a == 0x0f)
dprintf("m68k: invalid write?");
if (a < 0x10)
Pico_mcd->m68k_regs[a] = (u8) d;
}
static u32 s68k_reg_read16(u32 a, int realsize)
{
u32 d=0;
a &= 0x1fe;
dprintf("s68k_regs r%2i: [%02x] @ %06x", realsize&~1, a+(realsize&1), SekPcS68k);
switch (a) {
case 0:
d = 1; goto end; // ver = 0, not in reset state
case 6:
d = CDC_Read_Reg();
goto end;
case 8:
dprintf("s68k host data read");
d = Read_CDC_Host(1);
goto end;
case 0xC:
dprintf("s68k stopwatch read");
break;
case 0x34: // fader
d = 0; // no busy bit
goto end;
}
d = (Pico_mcd->s68k_regs[a]<<8) | Pico_mcd->s68k_regs[a+1];
end:
dprintf("ret = %04x", d);
return d;
}
static void s68k_reg_write8(u32 a, u32 d, int realsize)
{
a &= 0x1ff;
dprintf("s68k_regs w%2i: [%02x] %02x @ %06x", realsize, a, d, SekPcS68k);
// TODO: review against Gens
switch (a) {
case 4:
dprintf("s68k CDC dest: %x", d&7);
Pico_mcd->s68k_regs[4] = (Pico_mcd->s68k_regs[4]&0xC0) | (d&7); // CDC mode
return;
case 5:
dprintf("s68k CDC reg addr: %x", d&0xf);
break;
case 7:
CDC_Write_Reg(d);
return;
case 0xa:
dprintf("s68k set CDC dma addr");
break;
case 0x33: // IRQ mask
dprintf("s68k irq mask: %02x", d);
if ((d&(1<<4)) && (Pico_mcd->s68k_regs[0x37]&4) && !(Pico_mcd->s68k_regs[0x33]&(1<<4))) {
CDD_Export_Status();
// counter75hz = 0; // ???
}
break;
case 0x34: // fader
Pico_mcd->s68k_regs[a] = (u8) d & 0x7f;
return;
case 0x37:
if ((d&4) && !(Pico_mcd->s68k_regs[0x37]&4)) {
CDD_Export_Status();
// counter75hz = 0; // ???
}
break;
case 0x4b:
Pico_mcd->s68k_regs[a] = (u8) d;
CDD_Import_Command();
return;
}
if ((a&0x1f0) == 0x10 || a == 0x0e || (a >= 0x38 && a < 0x42))
{
dprintf("m68k: invalid write @ %02x?", a);
return;
}
Pico_mcd->s68k_regs[a] = (u8) d;
}
static int PadRead(int i)
{
int pad=0,value=0,TH;
pad=~PicoPad[i]; // Get inverse of pad MXYZ SACB RLDU
TH=Pico.ioports[i+1]&0x40;
if(PicoOpt & 0x20) { // 6 button gamepad enabled
int phase = Pico.m.padTHPhase[i];
if(phase == 2 && !TH) {
value=(pad&0xc0)>>2; // ?0SA 0000
goto end;
} else if(phase == 3 && TH) {
value=(pad&0x30)|((pad>>8)&0xf); // ?1CB MXYZ
goto end;
} else if(phase == 3 && !TH) {
value=((pad&0xc0)>>2)|0x0f; // ?0SA 1111
goto end;
}
}
if(TH) value=(pad&0x3f); // ?1CB RLDU
else value=((pad&0xc0)>>2)|(pad&3); // ?0SA 00DU
end:
// orr the bits, which are set as output
value |= Pico.ioports[i+1]&Pico.ioports[i+4];
return value; // will mirror later
}
static u8 z80Read8(u32 a)
{
if(Pico.m.z80Run&1) return 0;
a&=0x1fff;
if(!(PicoOpt&4)) {
// 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];
}
// for nonstandard reads
static u32 UnusualRead16(u32 a, int realsize)
{
u32 d=0;
dprintf("unusual r%i: %06x @%06x", realsize&~1, (a&0xfffffe)+(realsize&1), SekPc);
dprintf("ret = %04x", d);
return d;
}
static u32 OtherRead16(u32 a, int realsize)
{
u32 d=0;
if ((a&0xff0000)==0xa00000) {
if ((a&0x4000)==0x0000) { d=z80Read8(a); d|=d<<8; goto end; } // Z80 ram (not byteswaped)
if ((a&0x6000)==0x4000) { if(PicoOpt&1) d=YM2612Read(); else d=Pico.m.rotate++&3; goto end; } // 0x4000-0x5fff, Fudge if disabled
d=0xffff; goto end;
}
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); d|=Pico.ioports[1]&0x80; break;
case 2: d=PadRead(1); d|=Pico.ioports[2]&0x80; break;
default: d=Pico.ioports[a]; break; // IO ports can be used as RAM
}
d|=d<<8;
goto end;
}
// |=0x80 for Shadow of the Beast & Super Offroad; rotate fakes next fetched instruction for Time Killers
if (a==0xa11100) { d=((Pico.m.z80Run&1)<<8)|0x8000|Pico.m.rotate++; goto end; }
if ((a&0xe700e0)==0xc00000) { d=PicoVideoRead(a); goto end; }
if ((a&0xffffc0)==0xa12000) { d=m68k_reg_read16(a, realsize); goto end; }
d = UnusualRead16(a, realsize);
end:
return d;
}
//extern UINT32 mz80GetRegisterValue(void *, UINT32);
static void OtherWrite8(u32 a,u32 d,int realsize)
{
if ((a&0xe700f9)==0xc00011||(a&0xff7ff9)==0xa07f11) { if(PicoOpt&2) SN76496Write(d); return; } // PSG Sound
if ((a&0xff4000)==0xa00000) { if(!(Pico.m.z80Run&1)) Pico.zram[a&0x1fff]=(u8)d; return; } // Z80 ram
if ((a&0xff6000)==0xa04000) { if(PicoOpt&1) emustatus|=YM2612Write(a&3, d); return; } // FM Sound
if ((a&0xffffe0)==0xa10000) { // I/O ports
a=(a>>1)&0xf;
// 6 button gamepad: if TH went from 0 to 1, gamepad changes state
if(PicoOpt&0x20) {
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
return;
}
if (a==0xa11100) {
extern int z80startCycle, z80stopCycle;
//int lineCycles=(488-SekCyclesLeft)&0x1ff;
d&=1; d^=1;
if(!d) {
// hack: detect a nasty situation where Z80 was enabled and disabled in the same 68k timeslice (Golden Axe III)
if((PicoOpt&4) && Pico.m.z80Run==1) z80_run(20);
z80stopCycle = SekCyclesDone();
//z80ExtraCycles += (lineCycles>>1)-(lineCycles>>5); // only meaningful in PicoFrameHints()
} else {
z80startCycle = SekCyclesDone();
//if(Pico.m.scanline != -1)
//z80ExtraCycles -= (lineCycles>>1)-(lineCycles>>5)+16;
}
//dprintf("set_zrun: %i [%i|%i] zPC=%04x @%06x", d, Pico.m.scanline, SekCyclesDone(), mz80GetRegisterValue(NULL, 0), SekPc);
Pico.m.z80Run=(u8)d; return;
}
if (a==0xa11200) { if(!(d&1)) z80_reset(); 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
return;
}
if ((a&0xe700e0)==0xc00000) { PicoVideoWrite(a,(u16)(d|(d<<8))); return; } // Byte access gets mirrored
if ((a&0xffffc0)==0xa12000) { m68k_reg_write8(a, d, realsize); return; }
dprintf("strange w%i: %06x, %08x @%06x", realsize, a&0xffffff, d, SekPc);
}
static void OtherWrite16(u32 a,u32 d)
{
if ((a&0xe700e0)==0xc00000) { PicoVideoWrite(a,(u16)d); return; }
if ((a&0xff4000)==0xa00000) { if(!(Pico.m.z80Run&1)) Pico.zram[a&0x1fff]=(u8)(d>>8); return; } // Z80 ram (MSB only)
if ((a&0xffffe0)==0xa10000) { // I/O ports
a=(a>>1)&0xf;
// 6 button gamepad: if TH went from 0 to 1, gamepad changes state
if(PicoOpt&0x20) {
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
return;
}
if (a==0xa11100) { OtherWrite8(a, d>>8, 16); return; }
if (a==0xa11200) { if(!(d&0x100)) z80_reset(); return; }
OtherWrite8(a, d>>8, 16);
OtherWrite8(a+1,d&0xff, 16);
}
// -----------------------------------------------------------------
// Read Rom and read Ram
u8 PicoReadM68k8(u32 a)
{
u32 d=0;
if ((a&0xe00000)==0xe00000) { d = *(u8 *)(Pico.ram+((a^1)&0xffff)); goto end; } // Ram
a&=0xffffff;
if (a < 0x20000) { d = *(u8 *)(Pico_mcd->bios+(a^1)); goto end; } // bios
// prg RAM
if ((a&0xfe0000)==0x020000) {
u8 *prg_bank = Pico_mcd->prg_ram_b[Pico_mcd->m68k_regs[3]>>6];
d = *(prg_bank+((a^1)&0x1ffff));
goto end;
}
if ((a&0xff4000)==0xa00000) { d=z80Read8(a); goto end; } // Z80 Ram
d=OtherRead16(a&~1, 8|(a&1)); if ((a&1)==0) d>>=8;
end:
#ifdef __debug_io
dprintf("r8 : %06x, %02x @%06x", a&0xffffff, (u8)d, SekPc);
#endif
return (u8)d;
}
u16 PicoReadM68k16(u32 a)
{
u16 d=0;
if ((a&0xe00000)==0xe00000) { d=*(u16 *)(Pico.ram+(a&0xfffe)); goto end; } // Ram
a&=0xfffffe;
if (a < 0x20000) { d = *(u16 *)(Pico_mcd->bios+a); goto end; } // bios
// prg RAM
if ((a&0xfe0000)==0x020000) {
u8 *prg_bank = Pico_mcd->prg_ram_b[Pico_mcd->m68k_regs[3]>>6];
d = *(u16 *)(prg_bank+(a&0x1fffe));
goto end;
}
d = (u16)OtherRead16(a, 16);
end:
#ifdef __debug_io
dprintf("r16: %06x, %04x @%06x", a&0xffffff, d, SekPc);
#endif
return d;
}
u32 PicoReadM68k32(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 < 0x20000) { u16 *pm=(u16 *)(Pico_mcd->bios+a); d = (pm[0]<<16)|pm[1]; goto end; } // bios
// prg RAM
if ((a&0xfe0000)==0x020000) {
u8 *prg_bank = Pico_mcd->prg_ram_b[Pico_mcd->m68k_regs[3]>>6];
u16 *pm=(u16 *)(prg_bank+(a&0x1fffe));
d = (pm[0]<<16)|pm[1];
goto end;
}
d = (OtherRead16(a, 32)<<16)|OtherRead16(a+2, 32);
end:
#ifdef __debug_io
dprintf("r32: %06x, %08x @%06x", a&0xffffff, d, SekPc);
#endif
return d;
}
// -----------------------------------------------------------------
// Write Ram
void PicoWriteM68k8(u32 a,u8 d)
{
#ifdef __debug_io
dprintf("w8 : %06x, %02x @%06x", a&0xffffff, d, SekPc);
#endif
//if ((a&0xe0ffff)==0xe0a9ba+0x69c)
// dprintf("w8 : %06x, %02x @%06x", a&0xffffff, d, SekPc);
if ((a&0xe00000)==0xe00000) { u8 *pm=(u8 *)(Pico.ram+((a^1)&0xffff)); pm[0]=d; return; } // Ram
a&=0xffffff;
// prg RAM
if ((a&0xfe0000)==0x020000) {
u8 *prg_bank = Pico_mcd->prg_ram_b[Pico_mcd->m68k_regs[3]>>6];
u8 *pm=(u8 *)(prg_bank+((a^1)&0x1ffff));
*pm=d;
return;
}
OtherWrite8(a,d,8);
}
void PicoWriteM68k16(u32 a,u16 d)
{
#ifdef __debug_io
dprintf("w16: %06x, %04x", a&0xffffff, d);
#endif
//if ((a&0xe0ffff)==0xe0AF0E+0x69c||(a&0xe0ffff)==0xe0A9A8+0x69c||(a&0xe0ffff)==0xe0A9AA+0x69c||(a&0xe0ffff)==0xe0A9AC+0x69c)
// dprintf("w16: %06x, %04x @%06x", a&0xffffff, d, SekPc);
if ((a&0xe00000)==0xe00000) { *(u16 *)(Pico.ram+(a&0xfffe))=d; return; } // Ram
a&=0xfffffe;
// prg RAM
if ((a&0xfe0000)==0x020000) {
u8 *prg_bank = Pico_mcd->prg_ram_b[Pico_mcd->m68k_regs[3]>>6];
*(u16 *)(prg_bank+(a&0x1fffe))=d;
return;
}
OtherWrite16(a,d);
}
void PicoWriteM68k32(u32 a,u32 d)
{
#ifdef __debug_io
dprintf("w32: %06x, %08x", a&0xffffff, d);
#endif
if ((a&0xe00000)==0xe00000)
{
// Ram:
u16 *pm=(u16 *)(Pico.ram+(a&0xfffe));
pm[0]=(u16)(d>>16); pm[1]=(u16)d;
return;
}
a&=0xfffffe;
// prg RAM
if ((a&0xfe0000)==0x020000) {
u8 *prg_bank = Pico_mcd->prg_ram_b[Pico_mcd->m68k_regs[3]>>6];
u16 *pm=(u16 *)(prg_bank+(a&0x1fffe));
pm[0]=(u16)(d>>16); pm[1]=(u16)d;
return;
}
OtherWrite16(a, (u16)(d>>16));
OtherWrite16(a+2,(u16)d);
}
// -----------------------------------------------------------------
u8 PicoReadS68k8(u32 a)
{
u32 d=0;
a&=0xffffff;
// prg RAM
if (a < 0x80000) {
d = *(Pico_mcd->prg_ram+(a^1));
goto end;
}
// regs
if ((a&0xfffe00) == 0xff8000) {
d = s68k_reg_read16(a&~1, 8|(a&1)); if ((a&1)==0) d>>=8;
goto end;
}
dprintf("s68k r8 : %06x, %02x @%06x", a&0xffffff, (u8)d, SekPcS68k);
end:
#ifdef __debug_io2
dprintf("s68k r8 : %06x, %02x @%06x", a&0xffffff, (u8)d, SekPcS68k);
#endif
return (u8)d;
}
u16 PicoReadS68k16(u32 a)
{
u16 d=0;
a&=0xfffffe;
// prg RAM
if (a < 0x80000) {
d = *(u16 *)(Pico_mcd->prg_ram+a);
goto end;
}
// regs
if ((a&0xfffe00) == 0xff8000) {
d = s68k_reg_read16(a, 16);
goto end;
}
dprintf("s68k r16: %06x, %04x @%06x", a&0xffffff, d, SekPcS68k);
end:
#ifdef __debug_io2
dprintf("s68k r16: %06x, %04x @%06x", a&0xffffff, d, SekPcS68k);
#endif
return d;
}
u32 PicoReadS68k32(u32 a)
{
u32 d=0;
a&=0xfffffe;
// prg RAM
if (a < 0x80000) {
u16 *pm=(u16 *)(Pico_mcd->prg_ram+a);
d = (pm[0]<<16)|pm[1];
goto end;
}
// regs
if ((a&0xfffe00) == 0xff8000) {
d = (s68k_reg_read16(a, 32)<<16)|s68k_reg_read16(a+2, 32);
goto end;
}
dprintf("s68k r32: %06x, %08x @%06x", a&0xffffff, d, SekPcS68k);
end:
#ifdef __debug_io2
dprintf("s68k r32: %06x, %08x @%06x", a&0xffffff, d, SekPcS68k);
#endif
return d;
}
// -----------------------------------------------------------------
void PicoWriteS68k8(u32 a,u8 d)
{
#ifdef __debug_io2
dprintf("s68k w8 : %06x, %02x @%06x", a&0xffffff, d, SekPcS68k);
#endif
a&=0xffffff;
// prg RAM
if (a < 0x80000) {
u8 *pm=(u8 *)(Pico_mcd->prg_ram+(a^1));
*pm=d;
return;
}
// regs
if ((a&0xfffe00) == 0xff8000) {
s68k_reg_write8(a,d,8);
return;
}
dprintf("s68k w8 : %06x, %02x @%06x", a&0xffffff, d, SekPcS68k);
}
void PicoWriteS68k16(u32 a,u16 d)
{
#ifdef __debug_io2
dprintf("s68k w16: %06x, %04x @%06x", a&0xffffff, d, SekPcS68k);
#endif
a&=0xfffffe;
// prg RAM
if (a < 0x80000) {
*(u16 *)(Pico_mcd->prg_ram+a)=d;
return;
}
// regs
if ((a&0xfffe00) == 0xff8000) {
s68k_reg_write8(a, d>>8, 16);
s68k_reg_write8(a+1,d&0xff, 16);
return;
}
dprintf("s68k w16: %06x, %04x @%06x", a&0xffffff, d, SekPcS68k);
}
void PicoWriteS68k32(u32 a,u32 d)
{
#ifdef __debug_io2
dprintf("s68k w32: %06x, %08x @%06x", a&0xffffff, d, SekPcS68k);
#endif
a&=0xfffffe;
// prg RAM
if (a < 0x80000) {
u16 *pm=(u16 *)(Pico_mcd->prg_ram+a);
pm[0]=(u16)(d>>16); pm[1]=(u16)d;
return;
}
// regs
if ((a&0xfffe00) == 0xff8000) {
s68k_reg_write8(a, d>>24, 32);
s68k_reg_write8(a+1,(d>>16)&0xff, 32);
s68k_reg_write8(a+2,(d>>8) &0xff, 32);
s68k_reg_write8(a+3, d &0xff, 32);
return;
}
dprintf("s68k w32: %06x, %08x @%06x", a&0xffffff, d, SekPcS68k);
}
// -----------------------------------------------------------------
#ifdef EMU_M68K
unsigned char PicoReadCD8w (unsigned int a) {
return m68ki_cpu_p == &PicoS68kCPU ? PicoReadS68k8(a) : PicoReadM68k8(a);
}
unsigned short PicoReadCD16w(unsigned int a) {
return m68ki_cpu_p == &PicoS68kCPU ? PicoReadS68k16(a) : PicoReadM68k16(a);
}
unsigned int PicoReadCD32w(unsigned int a) {
return m68ki_cpu_p == &PicoS68kCPU ? PicoReadS68k32(a) : PicoReadM68k32(a);
}
void PicoWriteCD8w (unsigned int a, unsigned char d) {
if (m68ki_cpu_p == &PicoS68kCPU) PicoWriteS68k8(a, d); else PicoWriteM68k8(a, d);
}
void PicoWriteCD16w(unsigned int a, unsigned short d) {
if (m68ki_cpu_p == &PicoS68kCPU) PicoWriteS68k16(a, d); else PicoWriteM68k16(a, d);
}
void PicoWriteCD32w(unsigned int a, unsigned int d) {
if (m68ki_cpu_p == &PicoS68kCPU) PicoWriteS68k32(a, d); else PicoWriteM68k32(a, d);
}
// these are allowed to access RAM
unsigned int m68k_read_pcrelative_CD8 (unsigned int a) {
a&=0xffffff;
if(m68ki_cpu_p == &PicoS68kCPU) {
if (a < 0x80000) return *(u8 *)(Pico_mcd->prg_ram+(a^1)); // PRG Ram
else dprintf("s68k read_pcrel8 @ %06x", a);
} else {
if(a<Pico.romsize) return *(u8 *)(Pico.rom+(a^1)); // Rom
if((a&0xe00000)==0xe00000) return *(u8 *)(Pico.ram+((a^1)&0xffff)); // Ram
}
return 0;//(u8) lastread_d;
}
unsigned int m68k_read_pcrelative_CD16(unsigned int a) {
a&=0xffffff;
if(m68ki_cpu_p == &PicoS68kCPU) {
if (a < 0x80000) return *(u16 *)(Pico_mcd->prg_ram+(a&~1)); // PRG Ram
else dprintf("s68k read_pcrel16 @ %06x", a);
} else {
if(a<Pico.romsize) return *(u16 *)(Pico.rom+(a&~1)); // Rom
if((a&0xe00000)==0xe00000) return *(u16 *)(Pico.ram+(a&0xfffe)); // Ram
}
return 0;//(u16) lastread_d;
}
unsigned int m68k_read_pcrelative_CD32(unsigned int a) {
a&=0xffffff;
if(m68ki_cpu_p == &PicoS68kCPU) {
if (a < 0x80000) { u16 *pm=(u16 *)(Pico_mcd->prg_ram+(a&~1)); return (pm[0]<<16)|pm[1]; } // PRG Ram
else dprintf("s68k read_pcrel32 @ %06x", a);
} else {
if(a<Pico.romsize) { u16 *pm=(u16 *)(Pico.rom+(a&~1)); return (pm[0]<<16)|pm[1]; }
if((a&0xe00000)==0xe00000) { u16 *pm=(u16 *)(Pico.ram+(a&0xfffe)); return (pm[0]<<16)|pm[1]; } // Ram
}
return 0; //lastread_d;
}
#endif // EMU_M68K

213
Pico/cd/Pico.c Normal file
View file

@ -0,0 +1,213 @@
// 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 "../PicoInt.h"
#include "../sound/sound.h"
int counter75hz = 0;
int PicoInitMCD(void)
{
SekInitS68k();
Init_CD_Driver();
return 0;
}
void PicoExitMCD(void)
{
End_CD_Driver();
}
int PicoResetMCD(int hard)
{
// clear everything except BIOS
memset(Pico_mcd->prg_ram, 0, sizeof(mcd_state) - sizeof(Pico_mcd->bios));
PicoMCD |= 2; // s68k reset pending
counter75hz = 0;
LC89510_Reset();
Reset_CD();
return 0;
}
static __inline void SekRun(int cyc)
{
int cyc_do;
SekCycleAim+=cyc;
if((cyc_do=SekCycleAim-SekCycleCnt) < 0) return;
#if defined(EMU_M68K)
m68k_set_context(&PicoM68kCPU);
SekCycleCnt+=m68k_execute(cyc_do);
#endif
}
static __inline void SekRunS68k(int cyc)
{
int cyc_do;
SekCycleAimS68k+=cyc;
if((cyc_do=SekCycleAimS68k-SekCycleCntS68k) < 0) return;
#if defined(EMU_M68K)
m68k_set_context(&PicoS68kCPU);
SekCycleCntS68k+=m68k_execute(cyc_do);
#endif
}
// TODO: tidy
extern unsigned char m68k_regs[0x40];
extern unsigned char s68k_regs[0x200];
// Accurate but slower frame which does hints
static int PicoFrameHintsMCD(void)
{
struct PicoVideo *pv=&Pico.video;
int total_z80=0,lines,y,lines_vis = 224,z80CycleAim = 0,line_sample;
const int cycles_68k=488,cycles_z80=228,cycles_s68k=795; // both PAL and NTSC compile to same values
int skip=PicoSkipFrame || (PicoOpt&0x10);
int hint; // Hint counter
if(Pico.m.pal) { //
//cycles_68k = (int) ((double) OSC_PAL / 7 / 50 / 312 + 0.4); // should compile to a constant (488)
//cycles_z80 = (int) ((double) OSC_PAL / 15 / 50 / 312 + 0.4); // 228
lines = 312; // Steve Snake says there are 313 lines, but this seems to also work well
line_sample = 68;
if(pv->reg[1]&8) lines_vis = 240;
} else {
//cycles_68k = (int) ((double) OSC_NTSC / 7 / 60 / 262 + 0.4); // 488
//cycles_z80 = (int) ((double) OSC_NTSC / 15 / 60 / 262 + 0.4); // 228
lines = 262;
line_sample = 93;
}
SekCyclesReset();
SekCyclesResetS68k();
//z80ExtraCycles = 0;
if(PicoOpt&4)
z80CycleAim = 0;
// z80_resetCycles();
pv->status&=~0x88; // clear V-Int, come out of vblank
hint=pv->reg[10]; // Load H-Int counter
//dprintf("-hint: %i", hint);
for (y=0;y<lines;y++)
{
Pico.m.scanline=(short)y;
// pad delay (for 6 button pads)
if(PicoOpt&0x20) {
if(Pico.m.padDelay[0]++ > 25) Pico.m.padTHPhase[0]=0;
if(Pico.m.padDelay[1]++ > 25) Pico.m.padTHPhase[1]=0;
}
// H-Interrupts:
if(y <= lines_vis && --hint < 0) // y <= lines_vis: Comix Zone, Golden Axe
{
//dprintf("rhint:old @ %06x", SekPc);
hint=pv->reg[10]; // Reload H-Int counter
pv->pending_ints|=0x10;
if (pv->reg[0]&0x10) SekInterrupt(4);
//dprintf("rhint: %i @ %06x [%i|%i]", hint, SekPc, y, SekCycleCnt);
//dprintf("hint_routine: %x", (*(unsigned short*)(Pico.ram+0x0B84)<<16)|*(unsigned short*)(Pico.ram+0x0B86));
}
// V-Interrupt:
if (y == lines_vis)
{
//dprintf("vint: @ %06x [%i|%i]", SekPc, y, SekCycleCnt);
pv->status|=0x88; // V-Int happened, go into vblank
SekRun(128); SekCycleAim-=128; // there must be a gap between H and V ints, also after vblank bit set (Mazin Saga, Bram Stoker's Dracula)
/*if(Pico.m.z80Run && (PicoOpt&4)) {
z80CycleAim+=cycles_z80/2;
total_z80+=z80_run(z80CycleAim-total_z80);
z80CycleAim-=cycles_z80/2;
}*/
pv->pending_ints|=0x20;
if(pv->reg[1]&0x20) SekInterrupt(6);
if(Pico.m.z80Run && (PicoOpt&4)) // ?
z80_int();
//dprintf("zint: [%i|%i] zPC=%04x", Pico.m.scanline, SekCyclesDone(), mz80GetRegisterValue(NULL, 0));
}
// decide if we draw this line
#if CAN_HANDLE_240_LINES
if(!skip && ((!(pv->reg[1]&8) && y<224) || ((pv->reg[1]&8) && y<240)) )
#else
if(!skip && y<224)
#endif
PicoLine(y);
if(PicoOpt&1)
sound_timers_and_dac(y);
// get samples from sound chips
if(y == 32 && PsndOut)
emustatus &= ~1;
else if((y == 224 || y == line_sample) && PsndOut)
;//getSamples(y);
// Run scanline:
//dprintf("m68k starting exec @ %06x", SekPc);
SekRun(cycles_68k);
if ((Pico_mcd->m68k_regs[1]&3) == 1) { // no busreq/no reset
#if 0
int i;
FILE *f = fopen("prg_ram.bin", "wb");
for (i = 0; i < 0x80000; i+=2)
{
int tmp = Pico_mcd->prg_ram[i];
Pico_mcd->prg_ram[i] = Pico_mcd->prg_ram[i+1];
Pico_mcd->prg_ram[i+1] = tmp;
}
fwrite(Pico_mcd->prg_ram, 1, 0x80000, f);
fclose(f);
exit(1);
#endif
//dprintf("s68k starting exec @ %06x", SekPcS68k);
SekRunS68k(cycles_s68k);
}
if((PicoOpt&4) && Pico.m.z80Run) {
Pico.m.z80Run|=2;
z80CycleAim+=cycles_z80;
total_z80+=z80_run(z80CycleAim-total_z80);
}
// if cdd is on, counter elapsed and irq4 is not masked, do irq4
if ((Pico_mcd->s68k_regs[0x37]&4) && ++counter75hz > 209 && (Pico_mcd->s68k_regs[0x33]&(1<<4))) {
counter75hz = 0;
Check_CD_Command();
}
}
// draw a frame just after vblank in alternative render mode
if(!PicoSkipFrame && (PicoOpt&0x10))
PicoFrameFull();
return 0;
}
int PicoFrameMCD(void)
{
if(!(PicoOpt&0x10))
PicoFrameStart();
PicoFrameHintsMCD();
return 0;
}

78
Pico/cd/Sek.c Normal file
View file

@ -0,0 +1,78 @@
// 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 "../PicoInt.h"
int SekCycleCntS68k=0; // cycles done in this frame
int SekCycleAimS68k=0; // cycle aim
#ifdef EMU_M68K
// ---------------------- MUSASHI 68000 ----------------------
m68ki_cpu_core PicoS68kCPU; // Mega CD's CPU
#endif
#ifdef EMU_M68K
int SekIntAckS68k(int level)
{
dprintf("s68k: int %i ack [%i|%i]", level, Pico.m.scanline, SekCyclesDone());
CPU_INT_LEVEL = 0;
return M68K_INT_ACK_AUTOVECTOR;
}
#endif
int SekInitS68k()
{
#ifdef EMU_M68K
{
// Musashi is not very context friendly..
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoS68kCPU);
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
m68k_init();
m68k_set_int_ack_callback(SekIntAckS68k);
// m68k_pulse_reset(); // not yet, memmap is not set up
m68k_set_context(oldcontext);
}
#endif
return 0;
}
// Reset the 68000:
int SekResetS68k()
{
if (Pico.rom==NULL) return 1;
#ifdef EMU_M68K
{
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoS68kCPU);
m68k_pulse_reset();
m68k_set_context(oldcontext);
}
#endif
return 0;
}
int SekInterruptS68k(int irq)
{
#ifdef EMU_M68K
void *oldcontext = m68ki_cpu_p;
m68k_set_context(&PicoS68kCPU);
m68k_set_irq(irq); // raise irq (gets lowered after taken or must be done in ack)
m68k_set_context(oldcontext);
#endif
return 0;
}

996
Pico/cd/cd_sys.c Normal file
View file

@ -0,0 +1,996 @@
#include <stdio.h>
#include "cd_sys.h"
//#include "cd_file.h"
#include "../PicoInt.h"
#define cdprintf printf
//#define cdprintf(x...)
#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
/*
#include "gens.h"
#include "G_dsound.h"
#include "cdda_mp3.h"
#include "lc89510.h"
#include "Star_68k.h"
#include "Mem_M68K.h"
#include "Mem_S68K.h"
#include "save.h"
#include "misc.h"
*/
/*
int CDDA_Enable;
int CD_Audio_Buffer_L[8192];
int CD_Audio_Buffer_R[8192];
int CD_Audio_Buffer_Read_Pos = 0;
int CD_Audio_Buffer_Write_Pos = 2000;
int CD_Audio_Starting;
*/
static int CD_Present = 0;
int CD_Timer_Counter = 0; // TODO: check refs
static int CDD_Complete;
static int File_Add_Delay = 0;
//_scd SCD;
#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; \
\
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; \
\
CDD_Complete = 1; \
\
return 3; \
}
#if 0
static void MSB2DWORD(unsigned int *d, unsigned char *b)
{
unsigned int retVal;
retVal = (unsigned int )b[0];
retVal = (retVal<<8) + (unsigned int )b[1];
retVal = (retVal<<8) + (unsigned int )b[2];
retVal = (retVal<<8) + (unsigned int )b[3];
*d = retVal;
}
#endif
static int MSF_to_LBA(_msf *MSF)
{
return (MSF->M * 60 * 75) + (MSF->S * 75) + MSF->F - 150;
}
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 = Pico_mcd->scd.TOC.First_Track; i <= (Pico_mcd->scd.TOC.Last_Track + 1); i++)
{
Cur = Pico_mcd->scd.TOC.Tracks[i - Pico_mcd->scd.TOC.First_Track].MSF.M << 16;
Cur += Pico_mcd->scd.TOC.Tracks[i - Pico_mcd->scd.TOC.First_Track].MSF.S << 8;
Cur += Pico_mcd->scd.TOC.Tracks[i - Pico_mcd->scd.TOC.First_Track].MSF.F;
if (Cur > Start) break;
}
--i;
if (i > Pico_mcd->scd.TOC.Last_Track) return 100;
if (i < Pico_mcd->scd.TOC.First_Track) i = Pico_mcd->scd.TOC.First_Track;
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 < Pico_mcd->scd.TOC.First_Track) track = Pico_mcd->scd.TOC.First_Track;
else if (track > Pico_mcd->scd.TOC.Last_Track) track = Pico_mcd->scd.TOC.Last_Track;
MSF->M = Pico_mcd->scd.TOC.Tracks[track - Pico_mcd->scd.TOC.First_Track].MSF.M;
MSF->S = Pico_mcd->scd.TOC.Tracks[track - Pico_mcd->scd.TOC.First_Track].MSF.S;
MSF->F = Pico_mcd->scd.TOC.Tracks[track - Pico_mcd->scd.TOC.First_Track].MSF.F;
}
int Track_to_LBA(int track)
{
_msf MSF;
Track_to_MSF(track, &MSF);
return MSF_to_LBA(&MSF);
}
void Check_CD_Command(void)
{
cdprintf("CHECK CD COMMAND\n");
// Check CDD
if (CDD_Complete)
{
CDD_Complete = 0;
CDD_Export_Status();
}
// Check CDC
if (Pico_mcd->scd.Status_CDC & 1) // CDC is reading data ...
{
cdprintf("Sending a read command\n");
// DATA ?
if (Pico_mcd->scd.TOC.Tracks[Pico_mcd->scd.Cur_Track - Pico_mcd->scd.TOC.First_Track].Type)
Pico_mcd->s68k_regs[0x36] |= 0x01;
else Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO
if (File_Add_Delay == 0)
{
#if 0 // TODO
FILE_Read_One_LBA_CDC();
#endif
}
else File_Add_Delay--;
}
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();
}
}
int Init_CD_Driver(void)
{
#if 0 // TODO
FILE_Init();
#endif
return 0;
}
void End_CD_Driver(void)
{
#if 0 // TODO
FILE_End();
#endif
}
void Reset_CD(void)
{
Pico_mcd->scd.Cur_Track = 0;
Pico_mcd->scd.Cur_LBA = -150;
Pico_mcd->scd.Status_CDD = READY;
CDD_Complete = 0;
}
int Insert_CD(char *buf, char *iso_name)
{
// memset(CD_Audio_Buffer_L, 0, 4096 * 4);
// memset(CD_Audio_Buffer_R, 0, 4096 * 4);
if (iso_name == NULL)
{
CD_Present = 0;
}
else
{
#if 0 // TODO
Load_ISO(buf, iso_name);
CD_Present = 1;
#endif
return 0;
}
return 0;
}
void Stop_CD(void)
{
#if 0 // TODO
Unload_ISO();
#endif
CD_Present = 0;
}
void Change_CD(void)
{
if (Pico_mcd->scd.Status_CDD == TRAY_OPEN) Close_Tray_CDD_cC();
else Open_Tray_CDD_cD();
}
int Get_Status_CDD_c0(void)
{
cdprintf("Status command : Cur LBA = %d\n", 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);
CDD_Complete = 1;
return 0;
}
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;
CDD_Complete = 1;
return 0;
}
int Get_Pos_CDD_c20(void)
{
_msf MSF;
cdprintf("command 200 : Cur LBA = %d\n", 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\n", 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;
CDD_Complete = 1;
return 0;
}
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\n", 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;
CDD_Complete = 1;
return 0;
}
int Get_Current_Track_CDD_c22(void)
{
cdprintf("Status CDD = %.4X Status = %.4X\n", 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;
CDD_Complete = 1;
return 0;
}
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->scd.TOC.Tracks[Pico_mcd->scd.TOC.Last_Track -
Pico_mcd->scd.TOC.First_Track + 1].MSF.M);
Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->scd.TOC.Tracks[Pico_mcd->scd.TOC.Last_Track -
Pico_mcd->scd.TOC.First_Track + 1].MSF.S);
Pico_mcd->cdd.Frame = INT_TO_BCDW(Pico_mcd->scd.TOC.Tracks[Pico_mcd->scd.TOC.Last_Track -
Pico_mcd->scd.TOC.First_Track + 1].MSF.F);
Pico_mcd->cdd.Ext = 0;
// FIXME: remove
Pico_mcd->cdd.Seconde = 2;
CDD_Complete = 1;
return 0;
}
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(Pico_mcd->scd.TOC.First_Track);
Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->scd.TOC.Last_Track);
Pico_mcd->cdd.Frame = 0;
Pico_mcd->cdd.Ext = 0;
// FIXME: remove
Pico_mcd->cdd.Minute = Pico_mcd->cdd.Seconde = 1;
CDD_Complete = 1;
return 0;
}
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->scd.TOC.Last_Track) track_number = Pico_mcd->scd.TOC.Last_Track;
else if (track_number < Pico_mcd->scd.TOC.First_Track) track_number = Pico_mcd->scd.TOC.First_Track;
Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.TOC.Tracks[track_number - Pico_mcd->scd.TOC.First_Track].MSF.M);
Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->scd.TOC.Tracks[track_number - Pico_mcd->scd.TOC.First_Track].MSF.S);
Pico_mcd->cdd.Frame = INT_TO_BCDW(Pico_mcd->scd.TOC.Tracks[track_number - Pico_mcd->scd.TOC.First_Track].MSF.F);
Pico_mcd->cdd.Ext = track_number % 10;
if (Pico_mcd->scd.TOC.Tracks[track_number - Pico_mcd->scd.TOC.First_Track].Type) Pico_mcd->cdd.Frame |= 0x0800;
CDD_Complete = 1;
return 0;
}
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\n", 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 (File_Add_Delay == 0) File_Add_Delay = delay;
if (Pico_mcd->scd.TOC.Tracks[Pico_mcd->scd.Cur_Track - Pico_mcd->scd.TOC.First_Track].Type)
{
Pico_mcd->s68k_regs[0x36] |= 0x01; // DATA
}
else
{
Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO
//CD_Audio_Starting = 1;
#if 0 // TODO
FILE_Play_CD_LBA();
#endif
}
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
CDD_Complete = 1;
return 0;
}
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.TOC.Tracks[Pico_mcd->scd.Cur_Track - Pico_mcd->scd.TOC.First_Track].Type)
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;
CDD_Complete = 1;
return 0;
}
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;
CDD_Complete = 1;
return 0;
}
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\n", 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.TOC.Tracks[Pico_mcd->scd.Cur_Track - Pico_mcd->scd.TOC.First_Track].Type)
{
Pico_mcd->s68k_regs[0x36] |= 0x01; // DATA
}
else
{
Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO
//CD_Audio_Starting = 1;
#if 0 // TODO
FILE_Play_CD_LBA();
#endif
}
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
CDD_Complete = 1;
return 0;
}
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;
CDD_Complete = 1;
return 0;
}
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;
CDD_Complete = 1;
return 0;
}
int Close_Tray_CDD_cC(void)
{
//Clear_Sound_Buffer();
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read
{
#if 0 // TODO
char new_iso[1024];
memset(new_iso, 0, 1024);
while (!Change_File_L(new_iso, Rom_Dir, "Load SegaCD image file", "SegaCD image file\0*.bin;*.iso;*.raw\0All files\0*.*\0\0", ""));
Reload_SegaCD(new_iso);
CD_Present = 1;
#else
CD_Present = 0;
#endif
Pico_mcd->scd.Status_CDD = STOPPED;
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;
}
CDD_Complete = 1;
return 0;
}
int Open_Tray_CDD_cD(void)
{
CHECK_TRAY_OPEN
Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read
#if 0 // TODO
Unload_ISO();
#endif
CD_Present = 0;
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;
CDD_Complete = 1;
return 0;
}
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;
CDD_Complete = 1;
return 0;
}
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;
}
/***************************
* Others CD functions *
**************************/
// do we need them?
#if 0
void Write_CD_Audio(short *Buf, int rate, int channel, int lenght)
{
unsigned int lenght_src, lenght_dst;
unsigned int pos_src, pas_src;
if (rate == 0) return;
if (Sound_Rate == 0) return;
if (CD_Audio_Starting)
{
CD_Audio_Starting = 0;
memset(CD_Audio_Buffer_L, 0, 4096 * 4);
memset(CD_Audio_Buffer_R, 0, 4096 * 4);
CD_Audio_Buffer_Write_Pos = (CD_Audio_Buffer_Read_Pos + 2000) & 0xFFF;
}
lenght_src = rate / 75; // 75th of a second
lenght_dst = Sound_Rate / 75; // 75th of a second
pas_src = (lenght_src << 16) / lenght_dst;
pos_src = 0;
#ifdef DEBUG_CD
fprintf(debug_SCD_file, "\n********* Write Pos = %d ", CD_Audio_Buffer_Write_Pos);
#endif
if (channel == 2)
{
__asm
{
mov edi, CD_Audio_Buffer_Write_Pos
mov ebx, Buf
xor esi, esi
mov ecx, lenght_dst
xor eax, eax
mov edx, pas_src
dec ecx
jmp short loop_stereo
align 16
loop_stereo:
movsx eax, word ptr [ebx + esi * 4]
mov CD_Audio_Buffer_L[edi * 4], eax
movsx eax, word ptr [ebx + esi * 4 + 2]
mov CD_Audio_Buffer_R[edi * 4], eax
mov esi, dword ptr pos_src
inc edi
add esi, edx
and edi, 0xFFF
mov dword ptr pos_src, esi
shr esi, 16
dec ecx
jns short loop_stereo
mov CD_Audio_Buffer_Write_Pos, edi
}
}
else
{
__asm
{
mov edi, CD_Audio_Buffer_Write_Pos
mov ebx, Buf
xor esi, esi
mov ecx, lenght_dst
xor eax, eax
mov edx, pas_src
dec ecx
jmp short loop_mono
align 16
loop_mono:
movsx eax, word ptr [ebx + esi * 2]
mov CD_Audio_Buffer_L[edi * 4], eax
mov CD_Audio_Buffer_R[edi * 4], eax
mov esi, dword ptr pos_src
inc edi
add esi, edx
and edi, 0xFFF
mov dword ptr pos_src, esi
shr esi, 16
dec ecx
jns short loop_mono
mov CD_Audio_Buffer_Write_Pos, edi
}
}
#ifdef DEBUG_CD
fprintf(debug_SCD_file, "Write Pos 2 = %d\n\n", CD_Audio_Buffer_Write_Pos);
#endif
}
void Update_CD_Audio(int **buf, int lenght)
{
int *Buf_L, *Buf_R;
int diff;
Buf_L = buf[0];
Buf_R = buf[1];
if (Pico_mcd->s68k_regs[0x36] & 0x01) return;
if (!(Pico_mcd->scd.Status_CDC & 1)) return;
if (CD_Audio_Starting) return;
#ifdef DEBUG_CD
fprintf(debug_SCD_file, "\n********* Read Pos Normal = %d ", CD_Audio_Buffer_Read_Pos);
#endif
if (CD_Audio_Buffer_Write_Pos < CD_Audio_Buffer_Read_Pos)
{
diff = CD_Audio_Buffer_Write_Pos + (4096) - CD_Audio_Buffer_Read_Pos;
}
else
{
diff = CD_Audio_Buffer_Write_Pos - CD_Audio_Buffer_Read_Pos;
}
if (diff < 500) CD_Audio_Buffer_Read_Pos -= 2000;
else if (diff > 3500) CD_Audio_Buffer_Read_Pos += 2000;
#ifdef DEBUG_CD
else fprintf(debug_SCD_file, " pas de modifs ");
#endif
CD_Audio_Buffer_Read_Pos &= 0xFFF;
#ifdef DEBUG_CD
fprintf(debug_SCD_file, "Read Pos = %d ", CD_Audio_Buffer_Read_Pos);
#endif
if (CDDA_Enable)
{
__asm
{
mov ecx, lenght
mov esi, CD_Audio_Buffer_Read_Pos
mov edi, Buf_L
dec ecx
loop_L:
mov eax, CD_Audio_Buffer_L[esi * 4]
add [edi], eax
inc esi
add edi, 4
and esi, 0xFFF
dec ecx
jns short loop_L
mov ecx, lenght
mov esi, CD_Audio_Buffer_Read_Pos
mov edi, Buf_R
dec ecx
loop_R:
mov eax, CD_Audio_Buffer_R[esi * 4]
add [edi], eax
inc esi
add edi, 4
and esi, 0xFFF
dec ecx
jns short loop_R
mov CD_Audio_Buffer_Read_Pos, esi
}
}
else
{
CD_Audio_Buffer_Read_Pos += lenght;
CD_Audio_Buffer_Read_Pos &= 0xFFF;
}
#ifdef DEBUG_CD
fprintf(debug_SCD_file, "Read Pos 2 = %d\n\n", CD_Audio_Buffer_Read_Pos);
#endif
}
#endif

97
Pico/cd/cd_sys.h Normal file
View file

@ -0,0 +1,97 @@
#ifndef _CD_SYS_H
#define _CD_SYS_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;
unsigned char Num;
_msf MSF;
} _scd_track;
typedef struct
{
unsigned char First_Track;
unsigned char Last_Track;
_scd_track Tracks[100];
} _scd_toc;
typedef struct {
unsigned int Status_CDD;
unsigned int Status_CDC;
_scd_toc TOC;
int Cur_LBA;
unsigned int Cur_Track;
} _scd;
extern int CD_Timer_Counter;
void LBA_to_MSF(int lba, _msf *MSF);
int Track_to_LBA(int track);
void Check_CD_Command(void);
int Init_CD_Driver(void);
void End_CD_Driver(void);
int Insert_CD(char *buf, char *iso_name);
void Stop_CD(void);
void Change_CD(void);
void Reset_CD(void);
int Get_Status_CDD_c0(void);
int Stop_CDD_c1(void);
int Get_Pos_CDD_c20(void);
int Get_Track_Pos_CDD_c21(void);
int Get_Current_Track_CDD_c22(void);
int Get_Total_Lenght_CDD_c23(void);
int Get_First_Last_Track_CDD_c24(void);
int Get_Track_Adr_CDD_c25(void);
int Play_CDD_c3(void);
int Seek_CDD_c4(void);
int Pause_CDD_c6(void);
int Resume_CDD_c7(void);
int Fast_Foward_CDD_c8(void);
int Fast_Rewind_CDD_c9(void);
int CDD_cA(void);
int Close_Tray_CDD_cC(void);
int Open_Tray_CDD_cD(void);
int CDD_Def(void);
//void Write_CD_Audio(short *Buf, int rate, int channel, int lenght);
//void Update_CD_Audio(int **Buf, int lenght);
#ifdef __cplusplus
};
#endif
#endif

347
Pico/sound/sn76496.c Normal file
View file

@ -0,0 +1,347 @@
/***************************************************************************
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;
out /= STEP; // will be optimized to shift
if(stereo) {
// only left channel for stereo (will be copied to right by ym2612 mixing code)
*buffer += out;
buffer+=2;
} else
*buffer++ += out;
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

390
Pico/sound/sound.c Normal file
View file

@ -0,0 +1,390 @@
// 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 <string.h>
#include "sound.h"
#include "ym2612.h"
#include "sn76496.h"
#ifndef __GNUC__
#pragma warning (disable:4244)
#endif
#if defined(_USE_MZ80)
#include "../../cpu/mz80/mz80.h"
#elif defined(_USE_DRZ80)
#include "../../cpu/DrZ80/drz80.h"
#endif
#include "../PicoInt.h"
//int z80CycleAim = 0;
// dac
short *dac_out;
unsigned short dac_info[312]; // pppppppp ppppllll, p - pos in buff, l - length to write for this sample
// for Pico
int PsndRate=0;
int PsndLen=0; // number of mono samples, multiply by 2 for stereo
short *PsndOut=NULL; // PCM data buffer
// from ym2612.c
extern int *ym2612_dacen;
extern INT32 *ym2612_dacout;
void YM2612TimerHandler(int c,int cnt);
// sn76496
extern int *sn76496_regs;
static void dac_recalculate()
{
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 = 0;//lines - 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/2;
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++;
dac_info[224] = (pos<<4)|len;
}
// dprintf("rate is %i, len %i", PsndRate, PsndLen);
// for(i=0; i < lines; i++)
// dprintf("%03i : %03i : %i", i, dac_info[i]>>4, dac_info[i]&0xf);
//exit(8);
}
void sound_reset()
{
extern int z80stopCycle;
void *ym2612_regs;
// init even if we are not going to use them, just in case we ever enable it
YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);
// also clear the internal registers+addr line
ym2612_regs = YM2612GetRegs();
memset(ym2612_regs, 0, 0x200+4);
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);
// calculate PsndLen
PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);
// recalculate dac info
dac_recalculate();
z80stopCycle = 0;
}
// to be called after changing sound rate or chips
void sound_rerate()
{
unsigned int state[28];
YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);
// feed it back it's own registers, just like after loading state
YM2612PicoStateLoad();
memcpy(state, sn76496_regs, 28*4); // remember old state
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);
memcpy(sn76496_regs, state, 28*4); // restore old state
// calculate PsndLen
PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);
// recalculate dac info
dac_recalculate();
}
// This is called once per raster (aka line), but not necessarily for every line
int sound_timers_and_dac(int raster)
{
if(raster >= 0 && PsndOut && (PicoOpt&1) && *ym2612_dacen) {
short dout = (short) *ym2612_dacout;
int pos=dac_info[raster], len=pos&0xf;
short *d;
pos>>=4;
if(PicoOpt&8) { // only left channel for stereo (will be copied to right by ym2612 mixing code)
d=PsndOut+pos*2;
while(len--) { *d = dout; d += 2; }
} else {
d=PsndOut+pos;
while(len--) *d++ = dout;
}
}
//dprintf("s: %03i", raster);
// Our raster lasts 63.61323/64.102564 microseconds (NTSC/PAL)
YM2612PicoTick(1);
return 0;
}
int sound_render(int offset, int length)
{
int stereo = (PicoOpt & 8) >> 3;
offset <<= stereo;
// PSG
if(PicoOpt & 2)
SN76496Update(PsndOut+offset, length, stereo);
// Add in the stereo FM buffer
if(PicoOpt & 1) {
YM2612UpdateOne(PsndOut+offset, length, stereo);
} else {
// YM2612 upmixes to stereo, so we have to do this manually here
int i;
short *s = PsndOut+offset;
for (i = 0; i < length; i++) {
*(s+1) = *s; s+=2;
}
}
return 0;
}
#if defined(_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}
};
#elif defined(_USE_DRZ80)
static 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;
}
static unsigned char DrZ80_in(unsigned short p)
{
return 0xff;
}
static void DrZ80_out(unsigned short p,unsigned char d)
{
}
static void DrZ80_irq_callback()
{
drZ80.Z80_IRQ = 0; // lower irq when accepted
}
#endif
// z80 functionality wrappers
void z80_init()
{
#if defined(_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);
#elif defined(_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 =z80_read16;
drZ80.z80_write8 =z80_write;
drZ80.z80_write16 =z80_write16;
drZ80.z80_in =DrZ80_in;
drZ80.z80_out =DrZ80_out;
drZ80.z80_irq_callback=DrZ80_irq_callback;
#endif
}
void z80_reset()
{
#if defined(_USE_MZ80)
mz80reset();
#elif defined(_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.Z80PC = drZ80.z80_rebasePC(0);
drZ80.Z80SP = drZ80.z80_rebaseSP(0x2000); // 0xf000 ?
#endif
Pico.m.z80_fakeval = 0; // for faking when Z80 is disabled
}
void z80_resetCycles()
{
#if defined(_USE_MZ80)
mz80GetElapsedTicks(1);
#endif
}
void z80_int()
{
#if defined(_USE_MZ80)
mz80int(0);
#elif defined(_USE_DRZ80)
drZ80.z80irqvector = 0xFF; // default IRQ vector RST opcode
drZ80.Z80_IRQ = 1;
#endif
}
// returns number of cycles actually executed
int z80_run(int cycles)
{
#if defined(_USE_MZ80)
int ticks_pre = mz80GetElapsedTicks(0);
mz80exec(cycles);
return mz80GetElapsedTicks(0) - ticks_pre;
#elif defined(_USE_DRZ80)
return cycles - DrZ80Run(&drZ80, cycles);
#else
return cycles;
#endif
}
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);
#endif
}
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?
}
#endif
}
void z80_exit()
{
#if defined(_USE_MZ80)
mz80shutdown();
#endif
}
#if defined(__DEBUG_PRINT) || defined(WIN32)
void z80_debug(char *dstr)
{
#if defined(_USE_DRZ80)
sprintf(dstr, "%sZ80 state: PC: %04x SP: %04x\n", dstr, drZ80.Z80PC-drZ80.Z80PC_BASE, drZ80.Z80SP-drZ80.Z80SP_BASE);
#endif
}
#endif

28
Pico/sound/sound.h Normal file
View file

@ -0,0 +1,28 @@
// 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.
#ifdef __cplusplus
extern "C" {
#endif
int sound_timers_and_dac(int raster);
int sound_render(int offset, int length);
//int YM2612PicoTick(int n);
// z80 functionality wrappers
void z80_init();
void z80_resetCycles();
void z80_int();
int z80_run(int cycles);
void z80_exit();
#ifdef __cplusplus
} // End of extern "C"
#endif

1899
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 */
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 */
UINT16 tl; /* #0x18 total level: TL << 3 */
INT16 volume; /* #0x1a envelope counter */
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; /* algorithm */
UINT8 FB; /* feedback shift */
INT32 op1_out; /* op1 output for feedback */
INT32 mem_value; /* delayed sample (MEM) value */
INT32 pms; /* channel PMS */
UINT8 ams; /* channel AMS */
UINT8 kcode; /* key code: */
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 */
} FM_CH;
typedef struct
{
int clock; /* master clock (Hz) */
int rate; /* sampling rate (Hz) */
double freqbase; /* frequency base */
UINT8 address; /* address register */
UINT8 status; /* status flag */
UINT8 mode; /* mode CSM / 3SLOT */
UINT8 fn_h; /* freq latch */
int TA; /* timer a */
int TAC; /* timer a maxval */
int TAT; /* timer a ticker */
UINT8 TB; /* timer b */
int TBC; /* timer b maxval */
int TBT; /* timer b ticker */
/* 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; /* #0xb38 global envelope generator counter */
UINT32 eg_timer; /* #0xb3c global envelope generator counter works at frequency = chipclock/64/3 */
UINT32 eg_timer_add; /* #0xb40 step of eg_timer */
/* LFO */
UINT32 lfo_cnt;
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 */
FM_CH CH[6]; /* channel state (0x168 bytes each)? */
/* dac output (YM2612) */
int dacen;
INT32 dacout;
FM_OPN OPN; /* OPN state */
} YM2612;
#endif
void YM2612Init_(int baseclock, int rate);
void YM2612ResetChip_(void);
void YM2612UpdateOne_(short *buffer, int length, int stereo);
int YM2612Write_(unsigned int a, unsigned int v);
unsigned char YM2612Read_(void);
int YM2612PicoTick_(int n);
void YM2612PicoStateLoad_(void);
void *YM2612GetRegs(void);
#ifndef __GP2X__
#define YM2612Init YM2612Init_
#define YM2612ResetChip YM2612ResetChip_
#define YM2612UpdateOne YM2612UpdateOne_
#define YM2612Write YM2612Write_
#define YM2612Read YM2612Read_
#define YM2612PicoTick YM2612PicoTick_
#define YM2612PicoStateLoad YM2612PicoStateLoad_
#else
/* GP2X specific */
#include "../../platform/gp2x/940ctl_ym2612.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) { \
if (PicoOpt&0x200) YM2612UpdateOne_940(buffer, length, stereo); \
else YM2612UpdateOne_(buffer, length, stereo); \
}
#define YM2612Write(a,v) \
(PicoOpt&0x200) ? YM2612Write_940(a, v) : YM2612Write_(a, v)
#define YM2612Read() \
(PicoOpt&0x200) ? YM2612Read_940() : YM2612Read_()
#define YM2612PicoTick(n) \
(PicoOpt&0x200) ? YM2612PicoTick_940(n) : YM2612PicoTick_(n)
#define YM2612PicoStateLoad() { \
if (PicoOpt&0x200) YM2612PicoStateLoad_940(); \
else YM2612PicoStateLoad_(); \
}
#endif /* __GP2X__ */
#endif /* _H_FM_FM_ */

915
Pico/sound/ym2612.s Normal file
View file

@ -0,0 +1,915 @@
@ 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
.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)|algo, 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
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 r10, [lr, #0x54] @ op1_out
ldmfd sp!, {r4-r11,pc}
.pool