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

339
_docs/license_gpl.txt Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

32
_docs/license_mame.txt Normal file
View file

@ -0,0 +1,32 @@
Copyright (c) 1997-2005, Nicola Salmoria and the MAME team
All rights reserved.
Redistribution and use of this code or any derivative works are permitted
provided that the following conditions are met:
* Redistributions may not be sold, nor may they be used in a commercial
product or activity.
* Redistributions that are modified from the original source must include the
complete source code, including the source code for all components used by a
binary built from the modified sources. However, as a special exception, the
source code distributed need not include anything that is normally distributed
(in either source or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable runs, unless that
component itself accompanies the executable.
* Redistributions must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

70
_docs/readme.txt Normal file
View file

@ -0,0 +1,70 @@
Compiling Symbian ports
-----------------------
First note: there is no WINS support, because I use assembly code in some
places and I don't think emulator is useful for this project because of it's
too different behaviour compared to real device. If you need wins support,
you will have to add it yourself. In that case, you will have to use a68k
instead of Cyclone and mz80 instead of DrZ80. Emulator does have support
for these, also there is equivalent c code for the asm stuff, but you will
have to dig the sources and figure everything out yourself.
Exe:
Before building the exe you will need to compile Cyclone, use the included
vc6 project or see Cyclone.txt for more info.
I don't use standard Symbian build methods for exe, because I use ARM
assembly files (*.s) and different compiler options for different files,
and this is difficult to achieve with .mmp files. Also I use Cylone
patcher, check \cyclone\epoc\ for more info. So I use custom makefile
instead, but to use it, UIQROOT2 environmental variable must be set to
the root of your UIQ SDK. So to build, you need to type something like
this in console window:
> SET UIQROOT2=C:\UIQ_21\
> make -f picosmall.armi noecompxl=1
(To use ECompXL, you must rename ECompXL's petran.exe to petran_.exe or
edit the makefile).
Launcher:
There should be far less problems building that. Simply typing
> makmake PicodriveN.mmp armi
> make -f PicodriveN.armi
in console window with launcher directory set should build it without
problems.
Compiling GP2X port
-------------------
If you use devkitGP2X with gcc 4.0.2 and your paths are set correctly, running
'make' should be enough. If you are under Linux, you may need do some
adjustments, like changing case of filenames or setting up paths. I am sure
you will figure out yourself :)
License mess
------------
The launcher for Symbian OS is based on Peter van Sebille's projects,
which are released under GPL (license_gpl.txt).
YM2612 and sn76496 sound cores were taken from the MAME project, which is
under it's own license (license_mame.txt).
Dave's Cyclone 68000 core, Pico library are under simple
"Free for non-commercial use, For commercial use, separate licencing
terms must be obtained" license.
As far as I checked, both "Free for non-commercial use" and MAME licenses
might be incompatible with GPL, because GPL DOES allow commercial distribution.
But I don't think the original copyright holders (Peter, Dave, Reesy or the
MAME devs) would get very upset about this "violation", as this is both free
and open-source project. However, the whole project most likely falls under
GPL now (I don't know for sure as I'm just another coder, not a lawyer).
Alternatively, the launcher and exe can be viewed as separate programs
(technically this is true, they both use separate binaries, only protocol
is shared), so I hope nobody sees a problem here.
Contact
-------
My e-mail: notasas(atsymbol)gmail.com

58
cpu/Cyclone/Cyclone.h Normal file
View file

@ -0,0 +1,58 @@
// Cyclone 68000 Emulator - Header File
// Most code (c) Copyright 2004 Dave, All rights reserved.
// Some coding/bugfixing was done by notaz
// Cyclone 68000 is free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#ifdef __cplusplus
extern "C" {
#endif
extern int CycloneVer; // Version number of library
struct Cyclone
{
unsigned int d[8]; // [r7,#0x00]
unsigned int a[8]; // [r7,#0x20]
unsigned int pc; // [r7,#0x40] Memory Base+PC
unsigned char srh; // [r7,#0x44] Status Register high (T_S__III)
unsigned char xc; // [r7,#0x45] Extend flag (____??X?)
unsigned char flags; // [r7,#0x46] Flags (ARM order: ____NZCV) [68k order is XNZVC]
unsigned char irq; // [r7,#0x47] IRQ level
unsigned int osp; // [r7,#0x48] Other Stack Pointer (USP/SSP)
unsigned int vector; // [r7,#0x4c] IRQ vector (temporary)
unsigned int pad1[2];
int stopped; // [r7,#0x58] 1 == processor is in stopped state
int cycles; // [r7,#0x5c]
int membase; // [r7,#0x60] Memory Base (ARM address minus 68000 address)
unsigned int (*checkpc)(unsigned int pc); // [r7,#0x64] - Called to recalc Memory Base+pc
unsigned char (*read8 )(unsigned int a); // [r7,#0x68]
unsigned short (*read16 )(unsigned int a); // [r7,#0x6c]
unsigned int (*read32 )(unsigned int a); // [r7,#0x70]
void (*write8 )(unsigned int a,unsigned char d); // [r7,#0x74]
void (*write16)(unsigned int a,unsigned short d); // [r7,#0x78]
void (*write32)(unsigned int a,unsigned int d); // [r7,#0x7c]
unsigned char (*fetch8 )(unsigned int a); // [r7,#0x80]
unsigned short (*fetch16)(unsigned int a); // [r7,#0x84]
unsigned int (*fetch32)(unsigned int a); // [r7,#0x88]
void (*IrqCallback)(int int_level); // [r7,#0x8c] - optional irq callback function, see config.h
void (*ResetCallback)(); // [r7,#0x90] - if enabled in config.h, calls this whenever RESET opcode is encountered.
int (*UnrecognizedCallback)(); // [r7,#0x94] - if enabled in config.h, calls this whenever unrecognized opcode is encountered.
};
// used only if Cyclone was compiled with compressed jumptable, see config.h
void CycloneInit();
// run cyclone. Cycles should be specified in context (pcy->cycles)
void CycloneRun(struct Cyclone *pcy);
// utility functions to get and set SR
void CycloneSetSr(struct Cyclone *pcy, unsigned int sr); // auto-swaps a7<->osp if detects supervisor change
unsigned int CycloneGetSr(struct Cyclone *pcy);
#ifdef __cplusplus
} // End of extern "C"
#endif

473
cpu/Cyclone/Cyclone.txt Normal file
View file

@ -0,0 +1,473 @@
_____ __
/ ___/__ __ ____ / /___ ___ ___ ___________________
/ /__ / // // __// // _ \ / _ \/ -_) ___________________
\___/ \_, / \__//_/ \___//_//_/\__/ ___________________
/___/
___________________ ____ ___ ___ ___ ___
___________________ / __// _ \ / _ \ / _ \ / _ \
___________________ / _ \/ _ // // // // // // /
\___/\___/ \___/ \___/ \___/
___________________________________________________________________________
Cyclone 68000 (c) Copyright 2004 Dave. Free for non-commercial use
Homepage: http://www.finalburn.com/
Dave's e-mail: dev(atsymbol)finalburn.com
Replace (atsymbol) with @
Additional coding and bugfixes done by notaz, 2005, 2006
Homepage: http://mif.vu.lt/~grig2790/Cyclone/
e-mail: notasas(atsymbol)gmail.com
___________________________________________________________________________
What is it?
-----------
Cyclone 68000 is an emulator for the 68000 microprocessor, written in ARM 32-bit assembly.
It is aimed at chips such as ARM7 and ARM9 cores, StrongARM and XScale, to interpret 68000
code as fast as possible.
Flags are mapped onto ARM flags whenever possible, which speeds up the processing of opcode.
What's New
----------
v0.0086 notaz
+ Cyclone now can be customized to better suit your project, see config.h .
+ Added an option to compress the jumptable at compile-time. Must call CycloneInit()
at runtime to decompress it if enabled (see config.h).
+ Added missing CHK opcode handler (used by SeaQuest DSV).
+ Added missing TAS opcode handler (Gargoyles,Bubba N Stix,...). As in real genesis,
memory write-back phase is ignored (but can be enabled in config.h if needed).
+ Added missing NBCD and TRAPV opcode handlers.
+ Added missing addressing mode for CMP/EOR.
+ Added some minor optimizations.
- Removed 216 handlers for 2927 opcodes which were generated for invalid addressing modes.
+ Fixed flags for ASL, NEG, NEGX, DIVU, ADDX, SUBX, ROXR.
+ Bugs fixed in MOVEP, LINK, ADDQ, DIVS handlers.
* Undocumented flags for CHK, ABCD, SBCD and NBCD are now emulated the same way as in Musashi.
+ Added Uninitialized Interrupt emulation.
+ Altered timing for about half of opcodes to match Musashi's.
v0.0082 Reesy
+ Change cyclone to clear cycles before returning when halted
+ Added Irq call back function. This allows emulators to be notified
when cyclone has taken an interrupt allowing them to set internal flags
which can help fix timing problems.
v0.0081 notaz
+ .asm version was broken and did not compile with armasm. Fixed.
+ Finished implementing Stop opcode. Now it really stops the processor.
v0.0080 notaz
+ Added real cmpm opcode, it was using eor handler before this.
Fixes Dune and Sensible Soccer.
v0.0078 notaz
note: these bugs were actually found Reesy, I reimplemented these by
using his changelog as a guide.
+ Fixed a problem with divu which was using long divisor instead of word.
Fixes gear switching in Top Gear 2.
+ Fixed btst opcode, The bit to test should shifted a max of 31 or 7
depending on if a register or memory location is being tested.
+ Fixed abcd,sbcd. They did bad decimal correction on invalid BCD numbers
Score counters in Streets of Rage level end work now.
+ Changed flag handling of abcd,sbcd,addx,subx,asl,lsl,...
Some ops did not have flag handling at all.
Some ops must not change Z flag when result is zero, but they did.
Shift ops must not change X if shift count is zero, but they did.
There are probably still some flag problems left.
+ Patially implemented Stop and Reset opcodes - Fixes Thunderforce IV
v0.0075 notaz
+ Added missing displacement addressing mode for movem (Fantastic Dizzy)
+ Added OSP <-> A7 swapping code in opcodes, which change privilege mode
+ Implemented privilege violation, line emulator and divide by zero exceptions
+ Added negx opcode (Shining Force works!)
+ Added overflow detection for divs/divu
v0.0072 notaz
note: I could only get v0.0069 cyclone, so I had to implement these myself using Dave's
changelog as a guide.
+ Fixed a problem with divs - remainder should be negative when divident is negative
+ Added movep opcode (Sonic 3 works)
+ Fixed a problem with DBcc incorrectly decrementing if the condition is true (Shadow of the Beast)
v0.0069
+ Added SBCD and the flags for ABCD/SBCD. Score and time now works in games such as
Rolling Thunder 2, Ghouls 'N Ghosts
+ Fixed a problem with addx and subx with 8-bit and 16-bit values.
Ghouls 'N' Ghosts now works!
v0.0068
+ Added ABCD opcode (Streets of Rage works now!)
v0.0067
+ Added dbCC (After Burner)
+ Added asr EA (Sonic 1 Boss/Labyrinth Zone)
+ Added andi/ori/eori ccr (Altered Beast)
+ Added trap (After Burner)
+ Added special case for move.b (a7)+ and -(a7), stepping by 2
After Burner is playable! Eternal Champions shows more
+ Fixed lsr.b/w zero flag (Ghostbusters)
Rolling Thunder 2 now works!
+ Fixed N flag for .b and .w arithmetic. Golden Axe works!
v0.0066
+ Fixed a stupid typo for exg (orr r10,r10, not orr r10,r8), which caused alignment
crashes on Strider
v0.0065
+ Fixed a problem with immediate values - they weren't being shifted up correctly for some
opcodes. Spiderman works, After Burner shows a bit of graphics.
+ Fixed a problem with EA:"110nnn" extension word. 32-bit offsets were being decoded as 8-bit
offsets by mistake. Castlevania Bloodlines seems fine now.
+ Added exg opcode
+ Fixed asr opcode (Sonic jumping left is fixed)
+ Fixed a problem with the carry bit in rol.b (Marble Madness)
v0.0064
+ Added rtr
+ Fixed addq/subq.l (all An opcodes are 32-bit) (Road Rash)
+ Fixed various little timings
v0.0063
+ Added link/unlk opcodes
+ Fixed various little timings
+ Fixed a problem with dbCC opcode being emitted at set opcodes
+ Improved long register access, the EA fetch now does ldr r0,[r7,r0,lsl #2] whenever
possible, saving 1 or 2 cycles on many opcodes, which should give a nice speed up.
+ May have fixed N flag on ext opcode?
+ Added dasm for link opcode.
v0.0062
* I was a bit too keen with the Arithmetic opcodes! Some of them should have been abcd,
exg and addx. Removed the incorrect opcodes, pending re-adding them as abcd, exg and addx.
+ Changed unknown opcodes to act as nops.
Not very technical, but fun - a few more games show more graphics ;)
v0.0060
+ Fixed divu (EA intro)
+ Added sf (set false) opcode - SOR2
* Todo: pea/link/unlk opcodes
v0.0059: Added remainder to divide opcodes.
The new stuff
-------------
Before using Cyclone, be sure to customize config.h to better suit your project. All options
are documented inside that file.
IrqCallback has been changed a bit, unlike in previous version, it should not return anything.
If you need to change IRQ level, you can safely do that in your handler.
Cyclone has changed quite a bit from the time when Dave stopped updating it, but the rest of
documentation still applies, so read it if you haven't done that yet. If you have, check the
"Accessing ..." parts.
ARM Register Usage
------------------
See source code for up to date of register usage, however a summary is here:
r0-3: Temporary registers
r4 : Current PC + Memory Base (i.e. pointer to next opcode)
r5 : Cycles remaining
r6 : Pointer to Opcode Jump table
r7 : Pointer to Cpu Context
r8 : Current Opcode
r9 : Flags (NZCV) in highest four bits
(r10 : Temporary source value or Memory Base)
(r11 : Temporary register)
How to Compile
--------------
Like Starscream and A68K, Cyclone uses a 'Core Creator' program which calculates and outputs
all possible 68000 Opcodes and a jump table into files called Cyclone.s and .asm
It then assembles these files into Cyclone.o and .obj
Cyclone.o is the GCC assembled version and Cyclone.obj is the Microsoft assembled version.
First unzip "Cyclone.zip" into a "Cyclone" directory.
If you are compiling for Windows CE, find ARMASM.EXE (the Microsoft ARM assembler) and
put it in the directory as well or put it on your path.
Open up Cyclone.dsw in Visual Studio 6.0, compile and run the project.
Cyclone.obj and Cyclone.o will be created.
Compiling without Visual C++
----------------------------
If you aren't using Visual C++, it still shouldn't be too hard to compile, just get a C compiler,
compile all the CPPs and C file, link them into an EXE, and run the exe.
e.g. gcc Main.cpp OpAny.cpp OpArith.cpp OpBranch.cpp OpLogic.cpp OpMove.cpp Disa.c
Main.exe
Adding to your project
----------------------
To add Cyclone to you project, add Cyclone.o or obj, and include Cyclone.h
There is one structure: 'struct Cyclone', and one function: CycloneRun
Don't worry if this seem very minimal - its all you need to run as many 68000s as you want.
It works with both C and C++.
Byteswapped Memory
------------------
If you have used Starscream, A68K or Turbo68K or similar emulators you'll be familiar with this!
Any memory which the 68000 can access directly must be have every two bytes swapped around.
This is to speed up 16-bit memory accesses, because the 68000 has Big-Endian memory
and ARM has Little-Endian memory.
Now you may think you only technically have to byteswap ROM, not RAM, because
16-bit RAM reads go through a memory handler and you could just return (mem[a]<<8) | mem[a+1].
This would work, but remember some systems can execute code from RAM as well as ROM, and
that would fail.
So it's best to use byteswapped ROM and RAM if the 68000 can access it directly.
It's also faster for the memory handlers, because you can do this:
return *(unsigned short *)(mem+a)
Declaring Memory handlers
-------------------------
Before you can reset or execute 68000 opcodes you must first set up a set of memory handlers.
There are 7 functions you have to set up per CPU, like this:
static unsigned int MyCheckPc(unsigned int pc)
static unsigned char MyRead8 (unsigned int a)
static unsigned short MyRead16 (unsigned int a)
static unsigned int MyRead32 (unsigned int a)
static void MyWrite8 (unsigned int a,unsigned char d)
static void MyWrite16(unsigned int a,unsigned short d)
static void MyWrite32(unsigned int a,unsigned int d)
You can think of these functions representing the 68000's memory bus.
The Read and Write functions are called whenever the 68000 reads or writes memory.
For example you might set MyRead8 like this:
unsigned char MyRead8(unsigned int a)
{
a&=0xffffff; // Clip address to 24-bits
if (a<RomLength) return RomData[a^1]; // ^1 because the memory is byteswapped
if (a>=0xe00000) return RamData[(a^1)&0xffff];
return 0xff; // Out of range memory access
}
The other 5 read/write functions are similar. I'll describe the CheckPc function later on.
Declaring a CPU Context
-----------------------
To declare a CPU simple declare a struct Cyclone in your code. For example to declare
two 68000s:
struct Cyclone MyCpu;
struct Cyclone MyCpu2;
It's probably a good idea to initialise the memory to zero:
memset(&MyCpu, 0,sizeof(MyCpu));
memset(&MyCpu2,0,sizeof(MyCpu2));
Next point to your memory handlers:
MyCpu.checkpc=MyCheckPc;
MyCpu.read8 =MyRead8;
MyCpu.read16 =MyRead16;
MyCpu.read32 =MyRead32;
MyCpu.write8 =MyWrite8;
MyCpu.write16=MyWrite16;
MyCpu.write32=MyWrite32;
You also need to point the fetch handlers - for most systems out there you can just
point them at the read handlers:
MyCpu.fetch8 =MyRead8;
MyCpu.fetch16 =MyRead16;
MyCpu.fetch32 =MyRead32;
( Why a different set of function pointers for fetch?
Well there are some systems, the main one being CPS2, which return different data
depending on whether the 'fetch' line on the 68000 bus is high or low.
If this is the case, you can set up different functions for fetch reads.
Generally though you don't need to. )
Now you are nearly ready to reset the 68000, except you need one more function: checkpc().
The checkpc() function
----------------------
When Cyclone reads opcodes, it doesn't use a memory handler every time, this would be
far too slow, instead it uses a direct pointer to ARM memory.
For example if your Rom image was at 0x3000000 and the program counter was $206,
Cyclone's program counter would be 0x3000206.
The difference between an ARM address and a 68000 address is also stored in a variable called
'membase'. In the above example it's 0x3000000. To retrieve the real PC, Cyclone just
subtracts 'membase'.
When a long jump happens, Cyclone calls checkpc(). If the PC is in a different bank,
for example Ram instead of Rom, change 'membase', recalculate the new PC and return it:
static int MyCheckPc(unsigned int pc)
{
pc-=MyCpu.membase; // Get the real program counter
if (pc<RomLength) MyCpu.membase=(int)RomMem; // Jump to Rom
if (pc>=0xff0000) MyCpu.membase=(int)RamMem-0xff0000; // Jump to Ram
return MyCpu.membase+pc; // New program counter
}
Notice that the membase is always ARM address minus 68000 address.
The above example doesn't consider mirrored ram, but for an example of what to do see
PicoDrive (in Memory.cpp).
Almost there - Reset the 68000!
-------------------------------
Next we need to Reset the 68000 to get the initial Program Counter and Stack Pointer. This
is obtained from addresses 000000 and 000004.
Here is code which resets the 68000 (using your memory handlers):
MyCpu.srh=0x27; // Set supervisor mode
MyCpu.a[7]=MyCpu.read32(0); // Get Stack Pointer
MyCpu.membase=0;
MyCpu.pc=MyCpu.checkpc(MyCpu.read32(4)); // Get Program Counter
And that's ready to go.
Executing the 68000
-------------------
To execute the 68000, set the 'cycles' variable to the number of cycles you wish to execute,
and then call CycloneRun with a pointer to the Cyclone structure.
e.g.:
// Execute 1000 cycles on the 68000:
MyCpu.cycles=1000; CycloneRun(&MyCpu);
For each opcode, the number of cycles it took is subtracted and the function returns when
it reaches 0.
e.g.
// Execute one instruction on the 68000:
MyCpu.cycles=0; CycloneRun(&MyCpu);
printf(" The opcode took %d cycles\n", -MyCpu.cycles);
You should try to execute as many cycles as you can for maximum speed.
The number actually executed may be slightly more than requested, i.e. cycles may come
out with a small negative value:
e.g.
int todo=12000000/60; // 12Mhz, for one 60hz frame
MyCpu.cycles=todo; CycloneRun(&MyCpu);
printf(" Actually executed %d cycles\n", todo-MyCpu.cycles);
To calculate the number of cycles executed, use this formula:
Number of cycles requested - Cycle counter at the end
Interrupts
----------
Causing an interrupt is very simple, simply set the irq variable in the Cyclone structure
to the IRQ number.
To lower the IRQ line, set it to zero.
e.g:
MyCpu.irq=6; // Interrupt level 6
MyCpu.cycles=20000; CycloneRun(&MyCpu);
Note that the interrupt is not actually processed until the next call to CycloneRun,
and the interrupt may not be taken until the 68000 interrupt mask is changed to allow it.
( The IRQ isn't checked on exiting from a memory handler: I don't think this will cause
me any trouble because I've never needed to trigger an interrupt from a memory handler,
but if someone needs to, let me know...)
Accessing Cycle Counter
-----------------------
The cycle counter in the Cyclone structure is not, by default, updated before
calling a memory handler, only at the end of an execution.
*update*
Now this is configurable in config.h, there is no 'debug' variable.
Accessing Program Counter and registers
---------------------------------------
You can read Cyclone's registers directly from the structure at any time (as far as I know).
The Program Counter, should you need to read or write it, is stored with membase
added on. So use this formula to calculate the real 68000 program counter:
pc = MyCpu.pc - MyCpu.membase;
The program counter is stored in r4 during execution, and isn't written back to the
structure until the end of execution, which means you can't read normally real it from
a memory handler.
*update*
Now this is configurable in config.h, there is no 'debug' variable. You can even enable
access to SR if you need. However changing PC in memhandlers is still not safe, you should
better clear cycles, wait untill CycloneRun() returns and then do whatever you need.
Emulating more than one CPU
---------------------------
Since everything is based on the structures, emulating more than one cpu at the same time
is just a matter of declaring more than one structures and timeslicing. You can emulate
as many 68000s as you want.
Just set up the memory handlers for each cpu and run each cpu for a certain number of cycles.
e.g.
// Execute 1000 cycles on 68000 #1:
MyCpu.cycles=1000; CycloneRun(&MyCpu);
// Execute 1000 cycles on 68000 #2:
MyCpu2.cycles=1000; CycloneRun(&MyCpu2);
Thanks to...
------------
* All the previous code-generating assembler cpu core guys!
Who are iirc... Neill Corlett, Neil Bradley, Mike Coates, Darren Olafson
and Bart Trzynadlowski
* Charles Macdonald, for researching just about every console ever
* MameDev+FBA, for keeping on going and going and going
-------------
Dave - 17th April 2004
notaz - 17th July 2006
Homepage: http://www.finalburn.com/
Dave's e-mail: dev(atsymbol)finalburn.com
Replace (atsymbol) with @

821
cpu/Cyclone/Disa/Disa.c Normal file
View file

@ -0,0 +1,821 @@
// Dave's Disa 68000 Disassembler
#ifndef __GNUC__
#pragma warning(disable:4115)
#endif
#include <stdio.h>
#include <string.h>
#include "Disa.h"
unsigned int DisaPc=0;
char *DisaText=NULL; // Text buffer to write in
static char Tasm[]="bwl?";
static char Comment[64]="";
unsigned short (CPU_CALL *DisaWord)(unsigned int a)=NULL;
static unsigned int DisaLong(unsigned int a)
{
unsigned int d=0;
if (DisaWord==NULL) return d;
d= DisaWord(a)<<16;
d|=DisaWord(a+2)&0xffff;
return d;
}
// Get text version of the effective address
int DisaGetEa(char *t,int ea,int size)
{
ea&=0x3f; t[0]=0;
if ((ea&0x38)==0x00) { sprintf(t,"d%d",ea ); return 0; } // 000rrr
if ((ea&0x38)==0x08) { sprintf(t,"a%d",ea&7); return 0; } // 001rrr
if ((ea&0x38)==0x10) { sprintf(t,"(a%d)",ea&7); return 0; } // 010rrr
if ((ea&0x38)==0x18) { sprintf(t,"(a%d)+",ea&7); return 0; } // 011rrr
if ((ea&0x38)==0x20) { sprintf(t,"-(a%d)",ea&7); return 0; } // 100rrr
if ((ea&0x38)==0x28) { sprintf(t,"($%x,a%d)",DisaWord(DisaPc)&0xffff,ea&7); DisaPc+=2; return 0; } // 101rrr
if ((ea&0x38)==0x30)
{
// 110nnn - An + Disp + D/An
int areg=0,ext=0,off=0,da=0,reg=0,wol=0,scale=0;
ext=DisaWord(DisaPc)&0xffff;
areg=ea&7;
off=ext&0xff; da =ext&0x8000?'a':'d';
reg=(ext>>12)&7; wol=ext&0x0800?'l':'w';
scale=1<<((ext>>9)&3);
if (scale<2) sprintf(t,"($%x,a%d,%c%d.%c)", off,areg,da,reg,wol);
else sprintf(t,"($%x,a%d,%c%d.%c*%d)",off,areg,da,reg,wol,scale); // 68020
DisaPc+=2;
return 0;
}
if (ea==0x38) { sprintf(t,"$%x.w",DisaWord(DisaPc)&0xffff); DisaPc+=2; return 0; } // 111000 - Absolute short
if (ea==0x39) { sprintf(t,"$%x.l",DisaLong(DisaPc)); DisaPc+=4; return 0; } // 111001 - Absolute long
if (ea==0x3a)
{
// 111010 - PC Relative
int ext=DisaWord(DisaPc)&0xffff;
sprintf(t,"($%x,pc)",ext);
sprintf(Comment,"; =%x",DisaPc+(short)ext); // Comment where pc+ext is
DisaPc+=2;
return 0;
}
if (ea==0x3b)
{
// 111011 - PC Relative + D/An
int ext=0,off=0,da=0,reg=0,wol=0,scale=0;
ext=DisaWord(DisaPc)&0xffff;
off=ext&0xff; da =ext&0x8000?'a':'d';
reg=(ext>>12)&7; wol=ext&0x0800?'l':'w';
scale=1<<((ext>>9)&3);
if (scale<2) sprintf(t,"($%x,pc,%c%d.%c)", off,da,reg,wol);
else sprintf(t,"($%x,pc,%c%d.%c*%d)",off,da,reg,wol,scale); // 68020
sprintf(Comment,"; =%x",DisaPc+(char)off); // Comment where pc+ext is
DisaPc+=2;
return 0;
}
if (ea==0x3c)
{
// 111100 - Immediate
switch (size)
{
case 0: sprintf(t,"#$%x",DisaWord(DisaPc)&0x00ff); DisaPc+=2; return 0;
case 1: sprintf(t,"#$%x",DisaWord(DisaPc)&0xffff); DisaPc+=2; return 0;
case 2: sprintf(t,"#$%x",DisaLong(DisaPc) ); DisaPc+=4; return 0;
}
return 1;
}
// Unknown effective address
sprintf(t,"ea=(%d%d%d %d%d%d)",
(ea>>5)&1,(ea>>4)&1,(ea>>3)&1,
(ea>>2)&1,(ea>>1)&1, ea &1);
return 1;
}
static void GetOffset(char *text)
{
int off=(short)DisaWord(DisaPc); DisaPc+=2;
if (off<0) sprintf(text,"-$%x",-off);
else sprintf(text,"$%x", off);
}
// ================ Opcodes 0x0000+ ================
static int DisaArithImm(int op)
{
// Or/And/Sub/Add/Eor/Cmp Immediate 0000ttt0 xxDDDddd (tt=type, xx=size extension, DDDddd=Dest ea)
int dea=0;
char seat[64]="",deat[64]="";
int type=0,size=0;
char *arith[8]={"or","and","sub","add","?","eor","cmp","?"};
type=(op>>9)&7; if (type==4 || type>=7) return 1;
size=(op>>6)&3; if (size>=3) return 1;
dea=op&0x3f; if (dea==0x3c) return 1;
DisaGetEa(seat,0x3c,size);
DisaGetEa(deat,dea, size);
sprintf(DisaText,"%si.%c %s, %s",arith[type],Tasm[size],seat,deat);
return 0;
}
// ================ Opcodes 0x0108+ ================
static int DisaMovep(int op)
{
// movep.x (Aa),Dn - 0000nnn1 dx001aaa nn
int dn=0,dir=0,size=0,an=0;
char offset[32]="";
dn =(op>>9)&7;
dir =(op>>7)&1;
size=(op>>6)&1; size++;
an = op &7;
GetOffset(offset);
if (dir) sprintf(DisaText,"movep.%c d%d, (%s,a%d)",Tasm[size],dn,offset,an);
else sprintf(DisaText,"movep.%c (%s,a%d), d%d",Tasm[size],offset,an,dn);
return 0;
}
// ================ Opcodes 0x007c+ ================
static int DisaArithSr(int op)
{
// Ori/Andi/Eori $nnnn,sr 0000t0tx 0s111100
char *opcode[6]={"ori","andi","","","","eori"};
char seat[64]="";
int type=0,size=0;
type=(op>>9)&5;
size=(op>>6)&1;
DisaGetEa(seat,0x3c,size);
sprintf(DisaText,"%s.%c %s, %s", opcode[type], Tasm[size], seat, size?"sr":"ccr");
return 0;
}
// ================ Opcodes 0x0100+ ================
static int DisaBtstReg(int op)
{
// Btst/Bchg/Bclr/Bset 0000nnn1 tteeeeee (nn=reg number, eeeeee=Dest ea)
int type=0;
int sea=0,dea=0;
char seat[64]="",deat[64]="";
char *opcode[4]={"btst","bchg","bclr","bset"};
sea =(op>>9)&7;
type=(op>>6)&3;
dea= op&0x3f;
if ((dea&0x38)==0x08) return 1; // movep
DisaGetEa(seat,sea,0);
DisaGetEa(deat,dea,0);
sprintf(DisaText,"%s %s, %s",opcode[type],seat,deat);
return 0;
}
// ================ Opcodes 0x0800+ ================
static int DisaBtstImm(int op)
{
// Btst/Bchg/Bclr/Bset 00001000 tteeeeee 00 nn (eeeeee=ea, nn=bit number)
int type=0;
char seat[64]="",deat[64]="";
char *opcode[4]={"btst","bchg","bclr","bset"};
type=(op>>6)&3;
DisaGetEa(seat, 0x3c,0);
DisaGetEa(deat,op&0x3f,0);
sprintf(DisaText,"%s %s, %s",opcode[type],seat,deat);
return 0;
}
// ================ Opcodes 0x1000+ ================
static int DisaMove(int op)
{
// Move 00xxdddD DDssssss (xx=size extension, ssssss=Source EA, DDDddd=Dest ea)
int sea=0,dea=0;
char inst[64]="",seat[64]="",deat[64]="";
char *movea="";
int size=0;
if ((op&0x01c0)==0x0040) movea="a"; // See if it's a movea opcode
// Find size extension
switch (op&0x3000)
{
case 0x1000: size=0; break;
case 0x3000: size=1; break;
case 0x2000: size=2; break;
default: return 1;
}
sea = op&0x003f;
DisaGetEa(seat,sea,size);
dea =(op&0x01c0)>>3;
dea|=(op&0x0e00)>>9;
DisaGetEa(deat,dea,size);
sprintf(inst,"move%s.%c",movea,Tasm[size]);
sprintf(DisaText,"%s %s, %s",inst,seat,deat);
return 0;
}
// ================ Opcodes 0x4000+ ================
static int DisaNeg(int op)
{
// 01000tt0 xxeeeeee (tt=negx/clr/neg/not, xx=size, eeeeee=EA)
char eat[64]="";
int type=0,size=0;
char *opcode[4]={"negx","clr","neg","not"};
type=(op>>9)&3;
size=(op>>6)&3; if (size>=3) return 1;
DisaGetEa(eat,op&0x3f,size);
sprintf(DisaText,"%s.%c %s",opcode[type],Tasm[size],eat);
return 0;
}
// ================ Opcodes 0x40c0+ ================
static int DisaMoveSr(int op)
{
// 01000tt0 11eeeeee (tt=type, xx=size, eeeeee=EA)
int type=0,ea=0;
char eat[64]="";
type=(op>>9)&3;
ea=op&0x3f;
DisaGetEa(eat,ea,1);
switch (type)
{
default: sprintf(DisaText,"move sr, %s", eat); break;
case 1: sprintf(DisaText,"move ccr, %s",eat); break;
case 2: sprintf(DisaText,"move %s, ccr",eat); break;
case 3: sprintf(DisaText,"move %s, sr", eat); break;
}
return 0;
}
// ================ Opcodes 0x41c0+ ================
static int DisaLea(int op)
{
// Lea 0100nnn1 11eeeeee (eeeeee=ea)
int sea=0,dea=0;
char seat[64]="",deat[64]="";
sea=op&0x003f;
DisaGetEa(seat,sea,0);
dea=(op>>9)&7; dea|=8;
DisaGetEa(deat,dea,2);
sprintf(DisaText,"lea %s, %s",seat,deat);
return 0;
}
static int MakeRegList(char *list,int mask,int ea)
{
int reverse=0,i=0,low=0,len=0;
if ((ea&0x38)==0x20) reverse=1; // -(An), bitfield is reversed
mask&=0xffff; list[0]=0;
for (i=0;i<17;i++)
{
int bit=0;
// Mask off bit i:
if (reverse) bit=0x8000>>i; else bit=1<<i;
bit&=mask;
if (bit==0 || i==8)
{
// low to i-1 are a continuous section, add it:
char add[16]="";
int ad=low&8?'a':'d';
if (low==i-1) sprintf(add,"%c%d/", ad,low&7);
if (low< i-1) sprintf(add,"%c%d-%c%d/",ad,low&7, ad,(i-1)&7);
strcat(list,add);
low=i; // Next section
}
if (bit==0) low=i+1;
}
// Knock off trailing '/'
len=strlen(list);
if (len>0) if (list[len-1]=='/') list[len-1]=0;
return 0;
}
// ================ Opcodes 0x4840+ ================
static int DisaSwap(int op)
{
// Swap, 01001000 01000nnn swap Dn
sprintf(DisaText,"swap d%d",op&7);
return 0;
}
// ================ Opcodes 0x4850+ ================
static int DisaPea(int op)
{
// Pea 01001000 01eeeeee (eeeeee=ea) pea
int ea=0;
char eat[64]="";
ea=op&0x003f; if (ea<0x10) return 1; // swap opcode
DisaGetEa(eat,ea,2);
sprintf(DisaText,"pea %s",eat);
return 0;
}
// ================ Opcodes 0x4880+ ================
static int DisaExt(int op)
{
// Ext 01001000 1x000nnn (x=size, eeeeee=EA)
char eat[64]="";
int size=0;
size=(op>>6)&1; size++;
DisaGetEa(eat,op&0x3f,size);
sprintf(DisaText,"ext.%c %s",Tasm[size],eat);
return 0;
}
// ================ Opcodes 0x4890+ ================
static int DisaMovem(int op)
{
// Movem 01001d00 1xeeeeee regmask d=direction, x=size, eeeeee=EA
int dir=0,size=0;
int ea=0,mask=0;
char list[64]="",eat[64]="";
dir=(op>>10)&1;
size=((op>>6)&1)+1;
ea=op&0x3f; if (ea<0x10) return 1; // ext opcode
mask=DisaWord(DisaPc)&0xffff; DisaPc+=2;
MakeRegList(list,mask,ea); // Turn register mask into text
DisaGetEa(eat,ea,size);
if (dir) sprintf(DisaText,"movem.%c %s, %s",Tasm[size],eat,list);
else sprintf(DisaText,"movem.%c %s, %s",Tasm[size],list,eat);
return 0;
}
// ================ Opcodes 0x4e40+ ================
static int DisaTrap(int op)
{
sprintf(DisaText,"trap #%d",op&0xf);
return 0;
}
// ================ Opcodes 0x4e50+ ================
static int DisaLink(int op)
{
// Link opcode, 01001110 01010nnn dd link An,#offset
char eat[64]="";
char offset[32]="";
DisaGetEa(eat,(op&7)|8,0);
GetOffset(offset);
sprintf(DisaText,"link %s,#%s",eat,offset);
return 0;
}
// ================ Opcodes 0x4e58+ ================
static int DisaUnlk(int op)
{
// Link opcode, 01001110 01011nnn dd unlk An
char eat[64]="";
DisaGetEa(eat,(op&7)|8,0);
sprintf(DisaText,"unlk %s",eat);
return 0;
}
// ================ Opcodes 0x4e60+ ================
static int DisaMoveUsp(int op)
{
// Move USP opcode, 01001110 0110dnnn move An to/from USP (d=direction)
int ea=0,dir=0;
char eat[64]="";
dir=(op>>3)&1;
ea=(op&7)|8;
DisaGetEa(eat,ea,0);
if (dir) sprintf(DisaText,"move usp, %s",eat);
else sprintf(DisaText,"move %s, usp",eat);
return 0;
}
// ================ Opcodes 0x4e70+ ================
static int Disa4E70(int op)
{
char *inst[8]={"reset","nop","stop","rte","rtd","rts","trapv","rtr"};
int n=0;
n=op&7;
sprintf(DisaText,"%s",inst[n]);
//todo - 'stop' with 16 bit data
return 0;
}
// ================ Opcodes 0x4a00+ ================
static int DisaTst(int op)
{
// Tst 01001010 xxeeeeee (eeeeee=ea)
int ea=0;
char eat[64]="";
int size=0;
ea=op&0x003f;
DisaGetEa(eat,ea,0);
size=(op>>6)&3; if (size>=3) return 1;
sprintf(DisaText,"tst.%c %s",Tasm[size],eat);
return 0;
}
// ================ Opcodes 0x4e80+ ================
static int DisaJsr(int op)
{
// Jsr/Jmp 0100 1110 1mEE Eeee (eeeeee=ea m=1=jmp)
int sea=0;
char seat[64]="";
sea=op&0x003f;
DisaGetEa(seat,sea,0);
sprintf(DisaText,"j%s %s", op&0x40?"mp":"sr", seat);
return 0;
}
// ================ Opcodes 0x5000+ ================
static int DisaAddq(int op)
{
// 0101nnnt xxeeeeee (nnn=#8,1-7 t=addq/subq xx=size, eeeeee=EA)
int num=0,type=0,size=0,ea=0;
char eat[64]="";
num =(op>>9)&7; if (num==0) num=8;
type=(op>>8)&1;
size=(op>>6)&3; if (size>=3) return 1;
ea = op&0x3f;
DisaGetEa(eat,ea,size);
sprintf(DisaText,"%s.%c #%d, %s",type?"subq":"addq",Tasm[size],num,eat);
return 0;
}
// ================ Opcodes 0x50c0+ ================
static int DisaSet(int op)
{
// 0101cccc 11eeeeee (sxx ea)
static char *cond[16]=
{"t" ,"f", "hi","ls","cc","cs","ne","eq",
"vc","vs","pl","mi","ge","lt","gt","le"};
char *cc="";
int ea=0;
char eat[64]="";
cc=cond[(op>>8)&0xf]; // Get condition code
ea=op&0x3f;
if ((ea&0x38)==0x08) return 1; // dbra, not scc
DisaGetEa(eat,ea,0);
sprintf(DisaText,"s%s %s",cc,eat);
return 0;
}
// ================ Opcodes 0x50c8+ ================
static int DisaDbra(int op)
{
// 0101cccc 11001nnn offset (dbra/dbxx Rn,offset)
int dea=0; char deat[64]="";
int pc=0,Offset=0;
static char *BraCode[16]=
{"bt" ,"bra","bhi","bls","bcc","bcs","bne","beq",
"bvc","bvs","bpl","bmi","bge","blt","bgt","ble"};
char *Bra="";
dea=op&7;
DisaGetEa(deat,dea,2);
// Get condition code
Bra=BraCode[(op>>8)&0xf];
// Get offset
pc=DisaPc;
Offset=(short)DisaWord(DisaPc); DisaPc+=2;
sprintf(DisaText,"d%s %s, %x",Bra,deat,pc+Offset);
return 0;
}
// ================ Opcodes 0x6000+ ================
static int DisaBranch(int op)
{
// Branch 0110cccc nn (cccc=condition)
int pc=0,Offset=0;
static char *BraCode[16]=
{"bra","bsr","bhi","bls","bcc","bcs","bne","beq",
"bvc","bvs","bpl","bmi","bge","blt","bgt","ble"};
char *Bra="";
// Get condition code
Bra=BraCode[(op>>8)&0x0f];
// Get offset
pc=DisaPc;
Offset=(char)(op&0xff);
if (Offset== 0) { Offset=(short)DisaWord(DisaPc); DisaPc+=2; }
else if (Offset==-1) { Offset= DisaLong(DisaPc); DisaPc+=4; }
sprintf(DisaText,"%s %x",Bra,pc+Offset);
return 0;
}
// ================ Opcodes 0x7000+ ================
static int DisaMoveq(int op)
{
// Moveq 0111rrr0 nn (rrr=Dest register, nn=data)
int dea=0; char deat[64]="";
char *inst="moveq";
int val=0;
dea=(op>>9)&7;
DisaGetEa(deat,dea,2);
val=(char)(op&0xff);
sprintf(DisaText,"%s #$%x, %s",inst,val,deat);
return 0;
}
// ================ Opcodes 0x8000+ ================
static int DisaArithReg(int op)
{
// 1t0tnnnd xxeeeeee (tt=type:or/sub/and/add xx=size, eeeeee=EA)
int type=0,size=0,dir=0,rea=0,ea=0;
char reat[64]="",eat[64]="";
char *opcode[]={"or","sub","","","and","add"};
type=(op>>12)&5;
rea =(op>> 9)&7;
dir =(op>> 8)&1;
size=(op>> 6)&3; if (size>=3) return 1;
ea = op&0x3f;
if (dir && ea<0x10) return 1; // addx opcode
DisaGetEa(reat,rea,size);
DisaGetEa( eat, ea,size);
if (dir) sprintf(DisaText,"%s.%c %s, %s",opcode[type],Tasm[size],reat,eat);
else sprintf(DisaText,"%s.%c %s, %s",opcode[type],Tasm[size],eat,reat);
return 0;
}
// ================ Opcodes 0x8100+ ================
static int DisaAbcd(int op)
{
// 1t00ddd1 0000asss - sbcd/abcd Ds,Dd or -(As),-(Ad)
int type=0;
int dn=0,addr=0,sn=0;
char *opcode[]={"sbcd","abcd"};
type=(op>>14)&1;
dn =(op>> 9)&7;
addr=(op>> 3)&1;
sn = op &7;
if (addr) sprintf(DisaText,"%s -(a%d), -(a%d)",opcode[type],sn,dn);
else sprintf(DisaText,"%s d%d, d%d", opcode[type],sn,dn);
return 0;
}
// ================ Opcodes 0x80c0+ ================
static int DisaMul(int op)
{
// Div/Mul: 1m00nnns 11eeeeee (m=Mul, nnn=Register Dn, s=signed, eeeeee=EA)
int type=0,rea=0,sign=0,ea=0,size=1;
char reat[64]="",eat[64]="";
char *opcode[2]={"div","mul"};
type=(op>>14)&1; // div/mul
rea =(op>> 9)&7;
sign=(op>> 8)&1;
ea = op&0x3f;
DisaGetEa(reat,rea,size);
DisaGetEa( eat, ea,size);
sprintf(DisaText,"%s%c.%c %s, %s",opcode[type],sign?'s':'u',Tasm[size],eat,reat);
return 0;
}
// ================ Opcodes 0x90c0+ ================
static int DisaAritha(int op)
{
// Suba/Cmpa/Adda 1tt1nnnx 11eeeeee (tt=type, x=size, eeeeee=Source EA)
int type=0,size=0,sea=0,dea=0;
char seat[64]="",deat[64]="";
char *aritha[4]={"suba","cmpa","adda",""};
type=(op>>13)&3; if (type>=3) return 1;
size=(op>>8)&1; size++;
dea =(op>>9)&7; dea|=8; // Dest=An
sea = op&0x003f; // Source
DisaGetEa(seat,sea,size);
DisaGetEa(deat,dea,size);
sprintf(DisaText,"%s.%c %s, %s",aritha[type],Tasm[size],seat,deat);
return 0;
}
// ================ Opcodes 0xb000+ ================
static int DisaCmpEor(int op)
{
// Cmp/Eor 1011rrrt xxeeeeee (rrr=Dn, t=cmp/eor, xx=size extension, eeeeee=ea)
char reat[64]="",eat[64]="";
int type=0,size=0;
type=(op>>8)&1;
size=(op>>6)&3; if (size>=3) return 1;
DisaGetEa(reat,(op>>9)&7,size);
DisaGetEa(eat, op&0x3f, size);
if (type) sprintf(DisaText,"eor.%c %s, %s",Tasm[size],reat,eat);
else sprintf(DisaText,"cmp.%c %s, %s",Tasm[size],eat,reat);
return 0;
}
// ================ Opcodes 0xc140+ ================
// 1100ttt1 01000sss exg ds,dt
// 1100ttt1 01001sss exg as,at
// 1100ttt1 10001sss exg as,dt
static int DisaExg(int op)
{
int tr=0,type=0,sr=0;
tr =(op>>9)&7;
type= op&0xf8;
sr = op&7;
if (type==0x40) sprintf(DisaText,"exg d%d, d%d",sr,tr);
else if (type==0x48) sprintf(DisaText,"exg a%d, a%d",sr,tr);
else if (type==0x88) sprintf(DisaText,"exg a%d, d%d",sr,tr);
else return 1;
return 0;
}
// ================ Opcodes 0xd100+ ================
static int DisaAddx(int op)
{
// 1t01ddd1 xx000sss addx
int type=0,size=0,dea=0,sea=0;
char deat[64]="",seat[64]="";
char *opcode[6]={"","subx","","","","addx"};
type=(op>>12)&5;
dea =(op>> 9)&7;
size=(op>> 6)&3; if (size>=3) return 1;
sea = op&0x3f;
DisaGetEa(deat,dea,size);
DisaGetEa(seat,sea,size);
sprintf(DisaText,"%s.%c %s, %s",opcode[type],Tasm[size],seat,deat);
return 0;
}
// ================ Opcodes 0xe000+ ================
static char *AsrName[4]={"as","ls","rox","ro"};
static int DisaAsr(int op)
{
// Asr/l/Ror/l etc - 1110cccd xxuttnnn
// (ccc=count, d=direction xx=size extension, u=use reg for count, tt=type, nnn=register Dn)
int count=0,dir=0,size=0,usereg=0,type=0,num=0;
count =(op>>9)&7;
dir =(op>>8)&1;
size =(op>>6)&3; if (size>=3) return 1; // todo Asr EA
usereg=(op>>5)&1;
type =(op>>3)&3;
num = op &7; // Register number
if (usereg==0) count=((count-1)&7)+1; // because ccc=000 means 8
sprintf(DisaText,"%s%c.%c %c%d, d%d",
AsrName[type], dir?'l':'r', Tasm[size],
usereg?'d':'#', count, num);
return 0;
}
static int DisaAsrEa(int op)
{
// Asr/l/Ror/l etc EA - 11100ttd 11eeeeee
int type=0,dir=0,size=1;
char eat[64]="";
type=(op>>9)&3;
dir =(op>>8)&1;
DisaGetEa(eat,op&0x3f,size);
sprintf(DisaText,"%s%c.w %s", AsrName[type], dir?'l':'r', eat);
return 0;
}
// =================================================================
static int TryOp(int op)
{
if ((op&0xf100)==0x0000) DisaArithImm(op); // Ori/And/Sub/Add/Eor/Cmp Immediate
if ((op&0xf5bf)==0x003c) DisaArithSr(op); // Ori/Andi/Eori $nnnn,sr
if ((op&0xf100)==0x0100) DisaBtstReg(op);
if ((op&0xf138)==0x0108) DisaMovep(op);
if ((op&0xff00)==0x0800) DisaBtstImm(op); // Btst/Bchg/Bclr/Bset
if ((op&0xc000)==0x0000) DisaMove(op);
if ((op&0xf900)==0x4000) DisaNeg(op); // Negx/Clr/Neg/Not
if ((op&0xf1c0)==0x41c0) DisaLea(op);
if ((op&0xf9c0)==0x40c0) DisaMoveSr(op);
if ((op&0xfff8)==0x4840) DisaSwap(op);
if ((op&0xffc0)==0x4840) DisaPea(op);
if ((op&0xffb8)==0x4880) DisaExt(op);
if ((op&0xfb80)==0x4880) DisaMovem(op);
if ((op&0xff00)==0x4a00) DisaTst(op);
if ((op&0xfff0)==0x4e40) DisaTrap(op);
if ((op&0xfff8)==0x4e50) DisaLink(op);
if ((op&0xfff8)==0x4e58) DisaUnlk(op);
if ((op&0xfff0)==0x4e60) DisaMoveUsp(op);
if ((op&0xfff8)==0x4e70) Disa4E70(op);
if ((op&0xff80)==0x4e80) DisaJsr(op);
if ((op&0xf000)==0x5000) DisaAddq(op);
if ((op&0xf0c0)==0x50c0) DisaSet(op);
if ((op&0xf0f8)==0x50c8) DisaDbra(op);
if ((op&0xf000)==0x6000) DisaBranch(op);
if ((op&0xa000)==0x8000) DisaArithReg(op); // Or/Sub/And/Add
if ((op&0xb1f0)==0x8100) DisaAbcd(op);
if ((op&0xb130)==0x9100) DisaAddx(op);
if ((op&0xb0c0)==0x80c0) DisaMul(op);
if ((op&0xf100)==0x7000) DisaMoveq(op);
if ((op&0x90c0)==0x90c0) DisaAritha(op);
if ((op&0xf000)==0xb000) DisaCmpEor(op);
if ((op&0xf130)==0xc100) DisaExg(op);
if ((op&0xf000)==0xe000) DisaAsr(op);
if ((op&0xf8c0)==0xe0c0) DisaAsrEa(op);
// Unknown opcoode
return 0;
}
int DisaGet()
{
int op=0;
if (DisaWord==NULL) return 1;
Comment[0]=0;
DisaText[0]=0; // Assume opcode unknown
op=DisaWord(DisaPc)&0xffff; DisaPc+=2;
TryOp(op);
strcat(DisaText,Comment);
// Unknown opcoode
return 0;
}

24
cpu/Cyclone/Disa/Disa.h Normal file
View file

@ -0,0 +1,24 @@
// Dave's Disa 68000 Disassembler
#ifdef __cplusplus
extern "C" {
#endif
#if defined(ARM) || defined(GP32) || !defined (__WINS__)
#define CPU_CALL
#else
#define CPU_CALL __fastcall
#endif
extern unsigned int DisaPc;
extern char *DisaText; // Text buffer to write in
extern unsigned short (CPU_CALL *DisaWord)(unsigned int a);
int DisaGetEa(char *t,int ea,int size);
int DisaGet();
#ifdef __cplusplus
} // End of extern "C"
#endif

414
cpu/Cyclone/Ea.cpp Normal file
View file

@ -0,0 +1,414 @@
#include "app.h"
// some ops use non-standard cycle counts for EAs, so are listed here.
// all constants borrowed from the MUSASHI core by Karl Stenerud.
/* Extra cycles for JMP instruction (000, 010) */
int g_jmp_cycle_table[8] =
{
4, /* EA_MODE_AI */
6, /* EA_MODE_DI */
10, /* EA_MODE_IX */
6, /* EA_MODE_AW */
8, /* EA_MODE_AL */
6, /* EA_MODE_PCDI */
10, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
/* Extra cycles for JSR instruction (000, 010) */
int g_jsr_cycle_table[8] =
{
4, /* EA_MODE_AI */
6, /* EA_MODE_DI */
10, /* EA_MODE_IX */
6, /* EA_MODE_AW */
8, /* EA_MODE_AL */
6, /* EA_MODE_PCDI */
10, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
/* Extra cycles for LEA instruction (000, 010) */
int g_lea_cycle_table[8] =
{
4, /* EA_MODE_AI */
8, /* EA_MODE_DI */
12, /* EA_MODE_IX */
8, /* EA_MODE_AW */
12, /* EA_MODE_AL */
8, /* EA_MODE_PCDI */
12, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
/* Extra cycles for PEA instruction (000, 010) */
int g_pea_cycle_table[8] =
{
6, /* EA_MODE_AI */
10, /* EA_MODE_DI */
14, /* EA_MODE_IX */
10, /* EA_MODE_AW */
14, /* EA_MODE_AL */
10, /* EA_MODE_PCDI */
14, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
/* Extra cycles for MOVEM instruction (000, 010) */
int g_movem_cycle_table[8] =
{
0, /* EA_MODE_AI */
4, /* EA_MODE_DI */
6, /* EA_MODE_IX */
4, /* EA_MODE_AW */
8, /* EA_MODE_AL */
0, /* EA_MODE_PCDI */
0, /* EA_MODE_PCIX */
0, /* EA_MODE_I */
};
// add nonstandard EA
int Ea_add_ns(int *tab, int ea)
{
if(ea<0x10) return 0;
if((ea&0x38)==0x10) return tab[0]; // (An) (ai)
if(ea<0x28) return 0;
if(ea<0x30) return tab[1]; // ($nn,An) (di)
if(ea<0x38) return tab[2]; // ($nn,An,Rn) (ix)
if(ea==0x38) return tab[3]; // (aw)
if(ea==0x39) return tab[4]; // (al)
if(ea==0x3a) return tab[5]; // ($nn,PC) (pcdi)
if(ea==0x3b) return tab[6]; // ($nn,pc,Rn) (pcix)
if(ea==0x3c) return tab[7]; // #$nnnn (i)
return 0;
}
// ---------------------------------------------------------------------------
// Gets the offset of a register for an ea, and puts it in 'r'
// Shifted left by 'shift'
// Doesn't trash anything
static int EaCalcReg(int r,int ea,int mask,int forceor,int shift,int noshift=0)
{
int i=0,low=0,needor=0;
int lsl=0;
for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is
mask&=0xf<<low; // This is the max we can do
if (ea>=8) needor=1; // Need to OR to access A0-7
if ((mask>>low)&8) if (ea&8) needor=0; // Ah - no we don't actually need to or, since the bit is high in r8
if (forceor) needor=1; // Special case for 0x30-0x38 EAs ;)
ot(" and r%d,r8,#0x%.4x\n",r,mask);
if (needor) ot(" orr r%d,r%d,#0x%x ;@ A0-7\n",r,r,8<<low);
// Find out amount to shift left:
lsl=shift-low;
if (lsl&&!noshift)
{
ot(" mov r%d,r%d,",r,r);
if (lsl>0) ot("lsl #%d\n", lsl);
else ot("lsr #%d\n",-lsl);
}
return 0;
}
// EaCalc - ARM Register 'a' = Effective Address
// Trashes r0,r2 and r3
// size values 0, 1, 2 ~ byte, word, long
int EaCalc(int a,int mask,int ea,int size,int top)
{
char text[32]="";
int func=0;
DisaPc=2; DisaGetEa(text,ea,size); // Get text version of the effective address
func=0x68+(size<<2); // Get correct read handler
if (ea<0x10)
{
int noshift=0;
if (size>=2||(size==0&&top)) noshift=1; // Saves one opcode
ot(";@ EaCalc : Get register index into r%d:\n",a);
EaCalcReg(a,ea,mask,0,2,noshift);
return 0;
}
ot(";@ EaCalc : Get '%s' into r%d:\n",text,a);
// (An), (An)+, -(An)
if (ea<0x28)
{
int step=1<<size, strr=a;
int low=0,lsl,i;
if ((ea&7)==7 && step<2) step=2; // move.b (a7)+ or -(a7) steps by 2 not 1
EaCalcReg(2,ea,mask,0,0,1);
if(mask)
for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is
lsl=2-low; // Having a lsl #x here saves one opcode
if (lsl>=0) ot(" ldr r%d,[r7,r2,lsl #%i]\n",a,lsl);
else if (lsl<0) ot(" ldr r%d,[r7,r2,lsr #%i]\n",a,-lsl);
if ((ea&0x38)==0x18) // (An)+
{
ot(" add r3,r%d,#%d ;@ Post-increment An\n",a,step);
strr=3;
}
if ((ea&0x38)==0x20) // -(An)
ot(" sub r%d,r%d,#%d ;@ Pre-decrement An\n",a,a,step);
if ((ea&0x38)==0x18||(ea&0x38)==0x20)
{
if (lsl>=0) ot(" str r%d,[r7,r2,lsl #%i]\n",strr,lsl);
else if (lsl<0) ot(" str r%d,[r7,r2,lsr #%i]\n",strr,-lsl);
}
if ((ea&0x38)==0x20) Cycles+=size<2 ? 6:10; // -(An) Extra cycles
else Cycles+=size<2 ? 4:8; // (An),(An)+ Extra cycles
return 0;
}
if (ea<0x30) // ($nn,An) (di)
{
EaCalcReg(2,8,mask,0,0);
ot(" ldrsh r0,[r4],#2 ;@ Fetch offset\n");
ot(" ldr r2,[r7,r2,lsl #2]\n");
ot(" add r%d,r0,r2 ;@ Add on offset\n",a);
Cycles+=size<2 ? 8:12; // Extra cycles
return 0;
}
if (ea<0x38) // ($nn,An,Rn) (ix)
{
ot(";@ Get extension word into r3:\n");
ot(" ldrh r3,[r4],#2 ;@ ($Disp,PC,Rn)\n");
ot(" mov r2,r3,lsr #10\n");
ot(" tst r3,#0x0800 ;@ Is Rn Word or Long\n");
ot(" and r2,r2,#0x3c ;@ r2=Index of Rn\n");
ot(" ldreqsh r2,[r7,r2] ;@ r2=Rn.w\n");
ot(" ldrne r2,[r7,r2] ;@ r2=Rn.l\n");
ot(" mov r0,r3,asl #24 ;@ r0=Get 8-bit signed Disp\n");
ot(" add r3,r2,r0,asr #24 ;@ r3=Disp+Rn\n");
EaCalcReg(2,8,mask,1,0);
ot(" ldr r2,[r7,r2,lsl #2]\n");
ot(" add r%d,r2,r3 ;@ r%d=Disp+An+Rn\n",a,a);
Cycles+=size<2 ? 10:14; // Extra cycles
return 0;
}
if (ea==0x38) // (aw)
{
ot(" ldrsh r%d,[r4],#2 ;@ Fetch Absolute Short address\n",a);
Cycles+=size<2 ? 8:12; // Extra cycles
return 0;
}
if (ea==0x39) // (al)
{
ot(" ldrh r2,[r4],#2 ;@ Fetch Absolute Long address\n");
ot(" ldrh r0,[r4],#2\n");
ot(" orr r%d,r0,r2,lsl #16\n",a);
Cycles+=size<2 ? 12:16; // Extra cycles
return 0;
}
if (ea==0x3a) // ($nn,PC) (pcdi)
{
ot(" ldr r0,[r7,#0x60] ;@ Get Memory base\n");
ot(" sub r0,r4,r0 ;@ Real PC\n");
ot(" ldrsh r2,[r4],#2 ;@ Fetch extension\n");
ot(" mov r0,r0,lsl #8\n");
ot(" add r%d,r2,r0,asr #8 ;@ ($nn,PC)\n",a);
Cycles+=size<2 ? 8:12; // Extra cycles
return 0;
}
if (ea==0x3b) // ($nn,pc,Rn) (pcix)
{
ot(" ldr r0,[r7,#0x60] ;@ Get Memory base\n");
ot(" ldrh r3,[r4] ;@ Get extension word\n");
ot(" sub r0,r4,r0 ;@ r0=PC\n");
ot(" add r4,r4,#2\n");
ot(" mov r0,r0,asl #8 ;@ use only 24bits of PC\n");
ot(" mov r2,r3,lsr #10\n");
ot(" tst r3,#0x0800 ;@ Is Rn Word or Long\n");
ot(" and r2,r2,#0x3c ;@ r2=Index of Rn\n");
ot(" ldreqsh r2,[r7,r2] ;@ r2=Rn.w\n");
ot(" ldrne r2,[r7,r2] ;@ r2=Rn.l\n");
ot(" mov r3,r3,asl #24 ;@ r3=Get 8-bit signed Disp\n");
ot(" add r2,r2,r3,asr #24 ;@ r2=Disp+Rn\n");
ot(" add r%d,r2,r0,asr #8 ;@ r%d=Disp+PC+Rn\n",a,a);
Cycles+=size<2 ? 10:14; // Extra cycles
return 0;
}
if (ea==0x3c) // #$nnnn (i)
{
if (size<2)
{
ot(" ldr%s r%d,[r4],#2 ;@ Fetch immediate value\n",Sarm[size&3],a);
Cycles+=4; // Extra cycles
return 0;
}
ot(" ldrh r2,[r4],#2 ;@ Fetch immediate value\n");
ot(" ldrh r0,[r4],#2\n");
ot(" orr r%d,r0,r2,lsl #16\n",a);
Cycles+=8; // Extra cycles
return 0;
}
return 1;
}
// ---------------------------------------------------------------------------
// Read effective address in (ARM Register 'a') to ARM register 'v'
// 'a' and 'v' can be anything but 0 is generally best (for both)
// If (ea<0x10) nothing is trashed, else r0-r3 is trashed
// If 'top' is given, the ARM register v shifted to the top, e.g. 0xc000 -> 0xc0000000
// Otherwise the ARM register v is sign extended, e.g. 0xc000 -> 0xffffc000
int EaRead(int a,int v,int ea,int size,int mask,int top)
{
char text[32]="";
int shift=0;
shift=32-(8<<size);
DisaPc=2; DisaGetEa(text,ea,size); // Get text version of the effective address
if (ea<0x10)
{
int lsl=0,low=0,i;
if (size>=2||(size==0&&top)) {
if(mask)
for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is
lsl=2-low; // Having a lsl #2 here saves one opcode
}
ot(";@ EaRead : Read register[r%d] into r%d:\n",a,v);
if (lsl>0) ot(" ldr%s r%d,[r7,r%d,lsl #%i]\n",Narm[size&3],v,a,lsl);
else if (lsl<0) ot(" ldr%s r%d,[r7,r%d,lsr #%i]\n",Narm[size&3],v,a,-lsl);
else ot(" ldr%s r%d,[r7,r%d]\n",Sarm[size&3],v,a);
if (top && shift) ot(" mov r%d,r%d,asl #%d\n",v,v,shift);
ot("\n"); return 0;
}
ot(";@ EaRead : Read '%s' (address in r%d) into r%d:\n",text,a,v);
if (ea==0x3c)
{
int asl=0;
if (top) asl=shift;
if (v!=a || asl) ot(" mov r%d,r%d,asl #%d\n",v,a,asl);
ot("\n"); return 0;
}
if (a!=0) ot(" mov r0,r%d\n",a);
if (ea>=0x3a && ea<=0x3b) MemHandler(2,size); // Fetch
else MemHandler(0,size); // Read
if (v!=0 || shift) {
if (shift) ot(" mov r%d,r0,asl #%d\n",v,shift);
else ot(" mov r%d,r0\n",v);
}
if (top==0 && shift) ot(" mov r%d,r%d,asr #%d\n",v,v,shift);
ot("\n"); return 0;
}
// Return 1 if we can read this ea
int EaCanRead(int ea,int size)
{
if (size<0)
{
// LEA:
// These don't make sense?:
if (ea< 0x10) return 0; // Register
if (ea==0x3c) return 0; // Immediate
if (ea>=0x18 && ea<0x28) return 0; // Pre/Post inc/dec An
}
if (ea<=0x3c) return 1;
return 0;
}
// ---------------------------------------------------------------------------
// Write effective address (ARM Register 'a') with ARM register 'v'
// Trashes r0-r3,r12,lr; 'a' can be 0 or 2+, 'v' can be 1 or higher
// If a==0 and v==1 it's faster though.
int EaWrite(int a,int v,int ea,int size,int mask,int top)
{
char text[32]="";
int shift=0;
if(a == 1) { printf("Error! EaWrite a==1 !\n"); return 1; }
if (top) shift=32-(8<<size);
DisaPc=2; DisaGetEa(text,ea,size); // Get text version of the effective address
if (ea<0x10)
{
int lsl=0,low=0,i;
if (size>=2||(size==0&&top)) {
if(mask)
for (i=mask|0x8000; (i&1)==0; i>>=1) low++; // Find out how high up the EA mask is
lsl=2-low; // Having a lsl #x here saves one opcode
}
ot(";@ EaWrite: r%d into register[r%d]:\n",v,a);
if (shift) ot(" mov r%d,r%d,asr #%d\n",v,v,shift);
if (lsl>0) ot(" str%s r%d,[r7,r%d,lsl #%i]\n",Narm[size&3],v,a,lsl);
else if (lsl<0) ot(" str%s r%d,[r7,r%d,lsr #%i]\n",Narm[size&3],v,a,-lsl);
else ot(" str%s r%d,[r7,r%d]\n",Narm[size&3],v,a);
ot("\n"); return 0;
}
ot(";@ EaWrite: Write r%d into '%s' (address in r%d):\n",v,text,a);
if (ea==0x3c) { ot("Error! Write EA=0x%x\n\n",ea); return 1; }
if (a!=0 && v!=0) ot(" mov r0,r%d\n",a);
if (v!=1 || shift) ot(" mov r1,r%d,asr #%d\n",v,shift);
if (a!=0 && v==0) ot(" mov r0,r%d\n",a);
MemHandler(1,size); // Call write handler
ot("\n"); return 0;
}
// Return 1 if we can write this ea
int EaCanWrite(int ea)
{
if (ea<=0x39) return 1; // 3b?
return 0;
}
// ---------------------------------------------------------------------------
// Return 1 if EA is An reg
int EaAn(int ea)
{
if((ea&0x38)==8) return 1;
return 0;
}

648
cpu/Cyclone/Main.cpp Normal file
View file

@ -0,0 +1,648 @@
#include "app.h"
static FILE *AsmFile=NULL;
static int CycloneVer=0x0086; // Version number of library
int *CyJump=NULL; // Jump table
int ms=USE_MS_SYNTAX; // If non-zero, output in Microsoft ARMASM format
char *Narm[4]={ "b", "h","",""}; // Normal ARM Extensions for operand sizes 0,1,2
char *Sarm[4]={"sb","sh","",""}; // Sign-extend ARM Extensions for operand sizes 0,1,2
int Cycles; // Current cycles for opcode
void ot(const char *format, ...)
{
va_list valist=NULL;
int i, len;
// notaz: stop me from leaving newlines in the middle of format string
// and generating bad code
for(i=0, len=strlen(format); i < len && format[i] != '\n'; i++);
if(i < len-1 && format[len-1] != '\n') printf("\nWARNING: possible improper newline placement:\n%s\n", format);
va_start(valist,format);
if (AsmFile) vfprintf(AsmFile,format,valist);
va_end(valist);
}
void ltorg()
{
if (ms) ot(" LTORG\n");
else ot(" .ltorg\n");
}
// trashes all temp regs
static void PrintException(int ints)
{
if(!ints) {
ot(" ;@ Cause an Exception - Vector address in r0\n");
ot(" mov r11,r0\n");
}
ot(";@ swap OSP <-> A7?\n");
ot(" ldr r0,[r7,#0x44] ;@ Get SR high\n");
ot(" tst r0,#0x20\n");
ot(" bne no_sp_swap%i\n",ints);
ot(";@ swap OSP and A7:\n");
ot(" ldr r0,[r7,#0x3C] ;@ Get A7\n");
ot(" ldr r1,[r7,#0x48] ;@ Get OSP\n");
ot(" str r0,[r7,#0x48]\n");
ot(" str r1,[r7,#0x3C]\n");
ot("no_sp_swap%i%s\n",ints,ms?"":":");
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
ot(" mov r1,r4,lsl #8\n");
ot(" sub r1,r1,r10,lsl #8 ;@ r1 = Old PC\n");
ot(" mov r1,r1,asr #8 ;@ push sign extended\n");
OpPush32();
OpPushSr(1);
ot(" mov r0,r11\n");
ot(";@ Read IRQ Vector:\n");
MemHandler(0,2);
if(ints) {
ot(" tst r0,r0 ;@ uninitialized int vector?\n");
ot(" moveq r0,#0x3c\n");
ot(" moveq lr,pc\n");
ot(" ldreq pc,[r7,#0x70] ;@ Call read32(r0) handler\n");
}
#if USE_CHECKPC_CALLBACK
ot(" add r0,r0,r10 ;@ r0 = Memory Base + New PC\n");
ot(" mov lr,pc\n");
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n");
ot(" mov r4,r0\n");
#endif
ot("\n");
if(!ints) {
ot(" ldr r0,[r7,#0x44] ;@ Get SR high\n");
ot(" bic r0,r0,#0xd8 ;@ clear trace and unused flags\n");
ot(" orr r0,r0,#0x20 ;@ set supervisor mode\n");
ot(" strb r0,[r7,#0x44]\n");
}
}
// Trashes r0,r1
void CheckInterrupt(int op)
{
ot(";@ CheckInterrupt:\n");
ot(" ldr r0,[r7,#0x44]\n"); // same as ldrb r0,[r7,#0x47]
ot(" movs r0,r0,lsr #24 ;@ Get IRQ level (loading word is faster)\n");
ot(" beq NoInts%x\n",op);
ot(" cmp r0,#6 ;@ irq>6 ?\n");
ot(" ldrleb r1,[r7,#0x44] ;@ Get SR high: T_S__III\n");
ot(" andle r1,r1,#7 ;@ Get interrupt mask\n");
ot(" cmple r0,r1 ;@ irq<=6: Is irq<=mask ?\n");
ot(" blgt DoInterrupt\n");
ot("NoInts%x%s\n", op,ms?"":":");
ot("\n");
}
static void PrintFramework()
{
ot(";@ --------------------------- Framework --------------------------\n");
if (ms) ot("CycloneRun\n");
else ot("CycloneRun:\n");
ot(" stmdb sp!,{r4-r11,lr}\n");
ot(" mov r7,r0 ;@ r7 = Pointer to Cpu Context\n");
ot(" ;@ r0-3 = Temporary registers\n");
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Flags (NZCV)\n");
ot(" ldr r6,=JumpTab ;@ r6 = Opcode Jump table\n");
ot(" ldr r5,[r7,#0x5c] ;@ r5 = Cycles\n");
ot(" ldr r4,[r7,#0x40] ;@ r4 = Current PC + Memory Base\n");
ot(" ;@ r8 = Current Opcode\n");
ot(" ldr r0,[r7,#0x44]\n");
ot(" mov r9,r9,lsl #28 ;@ r9 = Flags 0xf0000000, cpsr format\n");
ot(" ;@ r10 = Source value / Memory Base\n");
ot("\n");
ot(";@ CheckInterrupt:\n");
ot(" movs r0,r0,lsr #24 ;@ Get IRQ level\n"); // same as ldrb r0,[r7,#0x47]
ot(" beq NoInts0\n");
ot(" cmp r0,#6 ;@ irq>6 ?\n");
ot(" ldrleb r1,[r7,#0x44] ;@ Get SR high: T_S__III\n");
ot(" andle r1,r1,#7 ;@ Get interrupt mask\n");
ot(" cmple r0,r1 ;@ irq<=6: Is irq<=mask ?\n");
ot(" blgt DoInterrupt\n");
ot(";@ Check if interrupt used up all the cycles:\n");
ot(" subs r5,r5,#0\n");
ot(" blt CycloneEndNoBack\n");
ot("NoInts0%s\n", ms?"":":");
ot("\n");
ot(";@ Check if our processor is in stopped state and jump to opcode handler if not\n");
ot(" ldr r0,[r7,#0x58]\n");
ot(" ldrh r8,[r4],#2 ;@ Fetch first opcode\n");
ot(" tst r0,r0 ;@ stopped?\n");
ot(" bne CycloneStopped\n");
ot(" ldr pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
ot("\n");
ot("\n");
ot(";@ We come back here after execution\n");
ot("CycloneEnd%s\n", ms?"":":");
ot(" sub r4,r4,#2\n");
ot("CycloneEndNoBack%s\n", ms?"":":");
ot(" mov r9,r9,lsr #28\n");
ot(" str r4,[r7,#0x40] ;@ Save Current PC + Memory Base\n");
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
ot(" strb r9,[r7,#0x46] ;@ Save Flags (NZCV)\n");
ot(" ldmia sp!,{r4-r11,pc}\n");
ot("\n");
ot("CycloneStopped%s\n", ms?"":":");
ot(" mov r5,#0\n");
ot(" str r5,[r7,#0x5C] ;@ eat all cycles\n");
ot(" ldmia sp!,{r4-r11,pc} ;@ we are stopped, do nothing!\n");
ot("\n");
ltorg();
#if COMPRESS_JUMPTABLE
ot(";@ uncompress jump table\n");
if (ms) ot("CycloneInit\n");
else ot("CycloneInit:\n");
ot(" ldr r12,=JumpTab\n");
ot(" add r0,r12,#0xe000*4 ;@ ctrl code pointer\n");
ot(" ldr r1,[r0,#-4]\n");
ot(" tst r1,r1\n");
ot(" movne pc,lr ;@ already uncompressed\n");
ot(" add r3,r12,#0xa000*4 ;@ handler table pointer, r12=dest\n");
ot("unc_loop%s\n", ms?"":":");
ot(" ldrh r1,[r0],#2\n");
ot(" and r2,r1,#0xf\n");
ot(" bic r1,r1,#0xf\n");
ot(" ldr r1,[r3,r1,lsr #2] ;@ r1=handler\n");
ot(" cmp r2,#0xf\n");
ot(" addeq r2,r2,#1 ;@ 0xf is really 0x10\n");
ot(" tst r2,r2\n");
ot(" ldreqh r2,[r0],#2 ;@ counter is in next word\n");
ot(" tst r2,r2\n");
ot(" beq unc_finish ;@ done decompressing\n");
ot(" tst r1,r1\n");
ot(" addeq r12,r12,r2,lsl #2 ;@ 0 handler means we should skip those bytes\n");
ot(" beq unc_loop\n");
ot("unc_loop_in%s\n", ms?"":":");
ot(" subs r2,r2,#1\n");
ot(" str r1,[r12],#4\n");
ot(" bgt unc_loop_in\n");
ot(" b unc_loop\n");
ot("unc_finish%s\n", ms?"":":");
ot(" ldr r12,=JumpTab\n");
ot(" ;@ set a-line and f-line handlers\n");
ot(" add r0,r12,#0xa000*4\n");
ot(" ldr r1,[r0,#4] ;@ a-line handler\n");
ot(" ldr r3,[r0,#8] ;@ f-line handler\n");
ot(" mov r2,#0x1000\n");
ot("unc_fill3%s\n", ms?"":":");
ot(" subs r2,r2,#1\n");
ot(" str r1,[r0],#4\n");
ot(" bgt unc_fill3\n");
ot(" add r0,r12,#0xf000*4\n");
ot(" mov r2,#0x1000\n");
ot("unc_fill4%s\n", ms?"":":");
ot(" subs r2,r2,#1\n");
ot(" str r3,[r0],#4\n");
ot(" bgt unc_fill4\n");
ot(" bx lr\n");
ltorg();
ot("\n");
#else
ot(";@ do nothing\n");
if (ms) ot("CycloneInit\n");
else ot("CycloneInit:\n");
ot(" bx lr\n");
ot("\n");
#endif
if (ms) ot("CycloneSetSr\n");
else ot("CycloneSetSr:\n");
ot(" mov r2,r1,lsr #8\n");
ot(" ldrb r3,[r0,#0x44] ;@ get SR high\n");
ot(" eor r3,r3,r2\n");
ot(" tst r3,#0x20\n");
ot(" and r2,r2,#0xa7 ;@ only nonzero bits\n");
ot(" strb r2,[r0,#0x44] ;@ set SR high\n");
ot(" bne setsr_noswap\n");
ot(" ldr r2,[r0,#0x3C] ;@ Get A7\n");
ot(" ldr r3,[r0,#0x48] ;@ Get OSP\n");
ot(" str r3,[r0,#0x3C]\n");
ot(" str r2,[r0,#0x48]\n");
ot("setsr_noswap%s\n",ms?"":":");
ot(" mov r2,r1,lsr #3\n");
ot(" strb r2,[r0,#0x45] ;@ the X flag\n");
ot(" bic r2,r1,#0xf3\n");
ot(" tst r1,#1\n");
ot(" orrne r2,r2,#2\n");
ot(" tst r1,#2\n");
ot(" orrne r2,r2,#1\n");
ot(" strb r2,[r0,#0x46] ;@ flags\n");
ot(" bx lr\n");
ot("\n");
if (ms) ot("CycloneGetSr\n");
else ot("CycloneGetSr:\n");
ot(" ldrb r1,[r0,#0x46] ;@ flags\n");
ot(" bic r2,r1,#0xf3\n");
ot(" tst r1,#1\n");
ot(" orrne r2,r2,#2\n");
ot(" tst r1,#2\n");
ot(" orrne r2,r2,#1\n");
ot(" ldrb r1,[r0,#0x45] ;@ the X flag\n");
ot(" tst r1,#2\n");
ot(" orrne r2,r2,#0x10\n");
ot(" ldrb r1,[r0,#0x44] ;@ the SR high\n");
ot(" orr r0,r2,r1,lsl #8\n");
ot(" bx lr\n");
ot("\n");
ot(";@ DoInterrupt - r0=IRQ number\n");
ot("DoInterrupt%s\n", ms?"":":");
ot(" stmdb sp!,{lr} ;@ Push ARM return address\n");
ot(";@ Get IRQ Vector address:\n");
ot(" mov r0,r0,asl #2\n");
ot(" add r11,r0,#0x60\n");
PrintException(1);
ot(" ldrb r0,[r7,#0x47] ;@ IRQ\n");
ot(" mov r2,#0\n");
ot(" orr r1,r0,#0x20 ;@ Supervisor mode + IRQ number\n");
ot(" strb r1,[r7,#0x44] ;@ Put SR high\n");
ot(";@ Clear stopped states:\n");
ot(" str r2,[r7,#0x58]\n");
ot(" sub r5,r5,#%d ;@ Subtract cycles\n",44);
ot("\n");
#if USE_INT_ACK_CALLBACK
#if INT_ACK_NEEDS_STUFF
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
ot(" mov r1,r9,lsr #28\n");
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
#endif
ot(" ldr r11,[r7,#0x8c] ;@ IrqCallback\n");
ot(" tst r11,r11\n");
ot(" movne lr,pc\n");
ot(" movne pc,r11 ;@ call IrqCallback if it is defined\n");
#if INT_ACK_CHANGES_STUFF
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Load Flags (NZCV)\n");
ot(" mov r9,r9,lsl #28\n");
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
#endif
#else // not USE_INT_ACK_CALLBACK
ot(";@ Clear irq:\n");
ot(" strb r1,[r7,#0x47]\n");
#endif
ot(" ldmia sp!,{pc} ;@ Return\n");
ot("\n");
ot("Exception%s\n", ms?"":":");
ot("\n");
ot(" stmdb sp!,{lr} ;@ Preserve ARM return address\n");
PrintException(0);
ot(" ldmia sp!,{pc} ;@ Return\n");
ot("\n");
}
// ---------------------------------------------------------------------------
// Call Read(r0), Write(r0,r1) or Fetch(r0)
// Trashes r0-r3,r12,lr
int MemHandler(int type,int size)
{
int func=0;
func=0x68+type*0xc+(size<<2); // Find correct offset
#if MEMHANDLERS_NEED_PC
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
#endif
#if MEMHANDLERS_NEED_FLAGS
ot(" mov r3,r9,lsr #28\n");
ot(" strb r3,[r7,#0x46] ;@ Save Flags (NZCV)\n");
#endif
#if MEMHANDLERS_NEED_CYCLES
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
#endif
ot(" mov lr,pc\n");
ot(" ldr pc,[r7,#0x%x] ;@ Call ",func);
// Document what we are calling:
if (type==0) ot("read");
if (type==1) ot("write");
if (type==2) ot("fetch");
if (type==1) ot("%d(r0,r1)",8<<size);
else ot("%d(r0)", 8<<size);
ot(" handler\n");
#if MEMHANDLERS_CHANGE_CYCLES
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
#endif
#if MEMHANDLERS_CHANGE_FLAGS
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Load Flags (NZCV)\n");
ot(" mov r9,r9,lsl #28\n");
#endif
#if MEMHANDLERS_CHANGE_PC
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
#endif
return 0;
}
static void PrintOpcodes()
{
int op=0;
printf("Creating Opcodes: [");
ot(";@ ---------------------------- Opcodes ---------------------------\n");
// Emit null opcode:
ot("Op____%s ;@ Called if an opcode is not recognised\n", ms?"":":");
ot(" sub r4,r4,#2\n");
#if USE_UNRECOGNIZED_CALLBACK
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
ot(" mov r1,r9,lsr #28\n");
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
ot(" ldr r11,[r7,#0x94] ;@ UnrecognizedCallback\n");
ot(" tst r11,r11\n");
ot(" movne lr,pc\n");
ot(" movne pc,r11 ;@ call UnrecognizedCallback if it is defined\n");
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Load Flags (NZCV)\n");
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
ot(" mov r9,r9,lsl #28\n");
ot(" tst r0,r0\n");
ot(" moveq r0,#0x10\n");
ot(" bleq Exception\n");
#else
ot(" mov r0,#0x10\n");
ot(" bl Exception\n");
#endif
Cycles=34;
OpEnd();
// Unrecognised a-line and f-line opcodes throw an exception:
ot("Op__al%s ;@ Unrecognised a-line opcode\n", ms?"":":");
ot(" sub r4,r4,#2\n");
#if USE_AFLINE_CALLBACK
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
ot(" mov r1,r9,lsr #28\n");
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
ot(" ldr r11,[r7,#0x94] ;@ UnrecognizedCallback\n");
ot(" tst r11,r11\n");
ot(" movne lr,pc\n");
ot(" movne pc,r11 ;@ call UnrecognizedCallback if it is defined\n");
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Load Flags (NZCV)\n");
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
ot(" mov r9,r9,lsl #28\n");
ot(" tst r0,r0\n");
ot(" moveq r0,#0x28\n");
ot(" bleq Exception\n");
#else
ot(" mov r0,#0x28\n");
ot(" bl Exception\n");
#endif
Cycles=4;
OpEnd();
ot("Op__fl%s ;@ Unrecognised f-line opcode\n", ms?"":":");
ot(" sub r4,r4,#2\n");
#if USE_AFLINE_CALLBACK
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
ot(" mov r1,r9,lsr #28\n");
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
ot(" ldr r11,[r7,#0x94] ;@ UnrecognizedCallback\n");
ot(" tst r11,r11\n");
ot(" movne lr,pc\n");
ot(" movne pc,r11 ;@ call UnrecognizedCallback if it is defined\n");
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Load Flags (NZCV)\n");
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
ot(" mov r9,r9,lsl #28\n");
ot(" tst r0,r0\n");
ot(" moveq r0,#0x2c\n");
ot(" bleq Exception\n");
#else
ot(" mov r0,#0x2c\n");
ot(" bl Exception\n");
#endif
Cycles=4;
OpEnd();
for (op=0;op<0x10000;op++)
{
if ((op&0xfff)==0) { printf("%x",op>>12); fflush(stdout); } // Update progress
OpAny(op);
}
ot("\n");
printf("]\n");
}
// helper
static void ott(const char *str, int par, const char *nl, int nlp, int counter, int size)
{
switch(size) {
case 0: if((counter&7)==0) ot(ms?" dcb ":" .byte "); break;
case 1: if((counter&7)==0) ot(ms?" dcw ":" .hword "); break;
case 2: if((counter&7)==0) ot(ms?" dcd ":" .long "); break;
}
ot(str, par);
if((counter&7)==7) ot(nl,nlp); else ot(",");
}
static void PrintJumpTable()
{
int i=0,op=0,len=0;
ot(";@ -------------------------- Jump Table --------------------------\n");
#if COMPRESS_JUMPTABLE
int handlers=0,reps=0,*indexes,ip,u,out;
// use some weird compression on the jump table
indexes=(int *)malloc(0x10000*4);
if(!indexes) { printf("ERROR: out of memory\n"); exit(1); }
len=0x10000;
// space for decompressed table
ot(ms?" area |.data|, data\n":" .data\n .align 4\n\n");
ot("JumpTab%s\n", ms?"":":");
if(ms) {
for(i = 0; i < 0xa000/8; i++)
ot(" dcd 0,0,0,0,0,0,0,0\n");
} else
ot(" .rept 0x%x\n .long 0,0,0,0,0,0,0,0\n .endr\n", 0xa000/8);
// hanlers live in "a-line" part of the table
// first output nop,a-line,f-line handlers
ot(ms?" dcd Op____,Op__al,Op__fl,":" .long Op____,Op__al,Op__fl,");
handlers=3;
for(i=0;i<len;i++)
{
op=CyJump[i];
for(u=i-1; u>=0; u--) if(op == CyJump[u]) break; // already done with this op?
if(u==-1 && op >= 0) {
ott("Op%.4x",op," ;@ %.4x\n",i,handlers,2);
indexes[op] = handlers;
handlers++;
}
}
if(handlers&7) {
fseek(AsmFile, -1, SEEK_CUR); // remove last comma
for(i = 8-(handlers&7); i > 0; i--)
ot(",000000");
ot("\n");
}
if(ms) {
for(i = (0x4000-handlers)/8; i > 0; i--)
ot(" dcd 0,0,0,0,0,0,0,0\n");
} else {
ot(ms?"":" .rept 0x%x\n .long 0,0,0,0,0,0,0,0\n .endr\n", (0x4000-handlers)/8);
}
printf("total distinct hanlers: %i\n",handlers);
// output data
for(i=0,ip=0; i < 0xf000; i++, ip++) {
op=CyJump[i];
if(op == -2) {
// it must skip a-line area, because we keep our data there
ott("0x%.4x", handlers<<4, "\n",0,ip++,1);
ott("0x%.4x", 0x1000, "\n",0,ip,1);
i+=0xfff;
continue;
}
for(reps=1; i < 0xf000; i++, reps++) if(op != CyJump[i+1]) break;
if(op>=0) out=indexes[op]<<4; else out=0; // unrecognised
if(reps <= 0xe || reps==0x10) {
if(reps!=0x10) out|=reps; else out|=0xf; // 0xf means 0x10 (0xf appeared to be unused anyway)
ott("0x%.4x", out, "\n",0,ip,1);
} else {
ott("0x%.4x", out, "\n",0,ip++,1);
ott("0x%.4x", reps,"\n",0,ip,1);
}
}
if(ip&1) ott("0x%.4x", 0, "\n",0,ip++,1);
if(ip&7) fseek(AsmFile, -1, SEEK_CUR); // remove last comma
ot("\n");
if(ip&7) {
for(i = 8-(ip&7); i > 0; i--)
ot(",0x0000");
ot("\n");
}
if(ms) {
for(i = (0x2000-ip/2)/8+1; i > 0; i--)
ot(" dcd 0,0,0,0,0,0,0,0\n");
} else {
ot(" .rept 0x%x\n .long 0,0,0,0,0,0,0,0\n .endr\n", (0x2000-ip/2)/8+1);
}
ot("\n");
free(indexes);
#else
ot("JumpTab%s\n", ms?"":":");
len=0xfffe; // Hmmm, armasm 2.50.8684 messes up with a 0x10000 long jump table
// notaz: same thing with GNU as 2.9-psion-98r2 (reloc overflow)
// this is due to COFF objects using only 2 bytes for reloc count
for (i=0;i<len;i++)
{
op=CyJump[i];
if(op>=0) ott("Op%.4x",op," ;@ %.4x\n",i-7,i,2);
else if(op==-2) ott("Op__al",0, " ;@ %.4x\n",i-7,i,2);
else if(op==-3) ott("Op__fl",0, " ;@ %.4x\n",i-7,i,2);
else ott("Op____",0, " ;@ %.4x\n",i-7,i,2);
}
if(i&7) fseek(AsmFile, -1, SEEK_CUR); // remove last comma
ot("\n");
ot(";@ notaz: we don't want to crash if we run into those 2 missing opcodes\n");
ot(";@ so we leave this pattern to patch it later\n");
ot("%s 0x78563412\n", ms?" dcd":" .long");
ot("%s 0x56341290\n", ms?" dcd":" .long");
#endif
}
static int CycloneMake()
{
int i;
char *name="Cyclone.s";
// Open the assembly file
if (ms) name="Cyclone.asm";
AsmFile=fopen(name,"wt"); if (AsmFile==NULL) return 1;
printf("Making %s...\n",name);
ot("\n;@ Dave's Cyclone 68000 Emulator v%x.%.3x - Assembler Output\n\n",CycloneVer>>12,CycloneVer&0xfff);
ot(";@ (c) Copyright 2003 Dave, All rights reserved.\n");
ot(";@ some code (c) Copyright 2005-2006 notaz, All rights reserved.\n");
ot(";@ Cyclone 68000 is free for non-commercial use.\n\n");
ot(";@ For commercial use, separate licencing terms must be obtained.\n\n");
CyJump=(int *)malloc(0x40000); if (CyJump==NULL) return 1;
memset(CyJump,0xff,0x40000); // Init to -1
for(i=0xa000; i<0xb000; i++) CyJump[i] = -2; // a-line emulation
for(i=0xf000; i<0x10000; i++) CyJump[i] = -3; // f-line emulation
if (ms)
{
ot(" area |.text|, code\n");
ot(" export CycloneInit\n");
ot(" export CycloneRun\n");
ot(" export CycloneSetSr\n");
ot(" export CycloneGetSr\n");
ot(" export CycloneVer\n");
ot("\n");
ot("CycloneVer dcd 0x%.4x\n",CycloneVer);
}
else
{
ot(" .global CycloneInit\n");
ot(" .global CycloneRun\n");
ot(" .global CycloneSetSr\n");
ot(" .global CycloneGetSr\n");
ot(" .global CycloneVer\n");
ot("CycloneVer: .long 0x%.4x\n",CycloneVer);
}
ot("\n");
PrintFramework();
PrintOpcodes();
PrintJumpTable();
if (ms) ot(" END\n");
fclose(AsmFile); AsmFile=NULL;
#if 0
printf("Assembling...\n");
// Assemble the file
if (ms) system("armasm Cyclone.asm");
else system("as -o Cyclone.o Cyclone.s");
printf("Done!\n\n");
#endif
free(CyJump);
return 0;
}
int main()
{
printf("\n Dave's Cyclone 68000 Emulator v%x.%.3x - Core Creator\n\n",CycloneVer>>12,CycloneVer&0xfff);
// Make GAS or ARMASM version
CycloneMake();
return 0;
}

119
cpu/Cyclone/OpAny.cpp Normal file
View file

@ -0,0 +1,119 @@
#include "app.h"
static unsigned char OpData[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static unsigned short CPU_CALL OpRead16(unsigned int a)
{
return (unsigned short)( (OpData[a&15]<<8) | OpData[(a+1)&15] );
}
// For opcode 'op' use handler 'use'
void OpUse(int op,int use)
{
char text[64]="";
CyJump[op]=use;
if (op!=use) return;
// Disassemble opcode
DisaPc=0;
DisaText=text;
DisaWord=OpRead16;
DisaGet();
ot(";@ ---------- [%.4x] %s uses Op%.4x ----------\n",op,text,use);
}
void OpStart(int op)
{
Cycles=0;
OpUse(op,op); // This opcode obviously uses this handler
ot("Op%.4x%s\n", op, ms?"":":");
}
void OpEnd()
{
ot(" ldrh r8,[r4],#2 ;@ Fetch next opcode\n");
ot(" subs r5,r5,#%d ;@ Subtract cycles\n",Cycles);
ot(" ldrge pc,[r6,r8,asl #2] ;@ Jump to opcode handler\n");
ot(" b CycloneEnd\n");
ot("\n");
}
int OpBase(int op,int sepa)
{
int ea=op&0x3f; // Get Effective Address
if (ea<0x10) return sepa?(op&~0x7):(op&~0xf); // Use 1 handler for d0-d7 and a0-a7
if (ea>=0x18 && ea<0x28 && (ea&7)==7) return op; // Specific handler for (a7)+ and -(a7)
if (ea<0x38) return op&~7; // Use 1 handler for (a0)-(a7), etc...
return op;
}
// Get flags, trashes r2
int OpGetFlags(int subtract,int xbit,int specialz)
{
if (specialz) ot(" orr r2,r9,#0xb0000000 ;@ for old Z\n");
ot(" mrs r9,cpsr ;@ r9=flags\n");
if (specialz) ot(" andeq r9,r9,r2 ;@ fix Z\n");
if (subtract) ot(" eor r9,r9,#0x20000000 ;@ Invert carry\n");
if (xbit)
{
ot(" mov r2,r9,lsr #28\n");
ot(" strb r2,[r7,#0x45] ;@ Save X bit\n");
}
return 0;
}
// -----------------------------------------------------------------
void OpAny(int op)
{
memset(OpData,0x33,sizeof(OpData));
OpData[0]=(unsigned char)(op>>8);
OpData[1]=(unsigned char)op;
if ((op&0xf100)==0x0000) OpArith(op);
if ((op&0xc000)==0x0000) OpMove(op);
if ((op&0xf5bf)==0x003c) OpArithSr(op); // Ori/Andi/Eori $nnnn,sr
if ((op&0xf100)==0x0100) OpBtstReg(op);
if ((op&0xf138)==0x0108) OpMovep(op);
if ((op&0xff00)==0x0800) OpBtstImm(op);
if ((op&0xf900)==0x4000) OpNeg(op);
if ((op&0xf140)==0x4100) OpChk(op);
if ((op&0xf1c0)==0x41c0) OpLea(op);
if ((op&0xf9c0)==0x40c0) OpMoveSr(op);
if ((op&0xffc0)==0x4800) OpNbcd(op);
if ((op&0xfff8)==0x4840) OpSwap(op);
if ((op&0xffc0)==0x4840) OpPea(op);
if ((op&0xffb8)==0x4880) OpExt(op);
if ((op&0xfb80)==0x4880) OpMovem(op);
if ((op&0xff00)==0x4a00) OpTst(op);
if ((op&0xffc0)==0x4ac0) OpTas(op);
if ((op&0xfff0)==0x4e40) OpTrap(op);
if ((op&0xfff8)==0x4e50) OpLink(op);
if ((op&0xfff8)==0x4e58) OpUnlk(op);
if ((op&0xfff0)==0x4e60) OpMoveUsp(op);
if ((op&0xfff8)==0x4e70) Op4E70(op); // Reset/Rts etc
if ((op&0xfffd)==0x4e70) OpStopReset(op);
if ((op&0xff80)==0x4e80) OpJsr(op);
if ((op&0xf000)==0x5000) OpAddq(op);
if ((op&0xf0c0)==0x50c0) OpSet(op);
if ((op&0xf0f8)==0x50c8) OpDbra(op);
if ((op&0xf000)==0x6000) OpBranch(op);
if ((op&0xf100)==0x7000) OpMoveq(op);
if ((op&0xa000)==0x8000) OpArithReg(op); // Or/Sub/And/Add
if ((op&0xb1f0)==0x8100) OpAbcd(op);
if ((op&0xb0c0)==0x80c0) OpMul(op);
if ((op&0x90c0)==0x90c0) OpAritha(op);
if ((op&0xb130)==0x9100) OpAddx(op);
if ((op&0xf000)==0xb000) OpCmpEor(op);
if ((op&0xf138)==0xb108) OpCmpm(op);
if ((op&0xf130)==0xc100) OpExg(op);
if ((op&0xf000)==0xe000) OpAsr(op); // Asr/l/Ror/l etc
if ((op&0xf8c0)==0xe0c0) OpAsrEa(op);
}

758
cpu/Cyclone/OpArith.cpp Normal file
View file

@ -0,0 +1,758 @@
#include "app.h"
// --------------------- Opcodes 0x0000+ ---------------------
// Emit an Ori/And/Sub/Add/Eor/Cmp Immediate opcode, 0000ttt0 ssaaaaaa
int OpArith(int op)
{
int type=0,size=0;
int sea=0,tea=0;
int use=0;
// Get source and target EA
type=(op>>9)&7; if (type==4 || type>=7) return 1;
size=(op>>6)&3; if (size>=3) return 1;
sea= 0x003c;
tea=op&0x003f;
// See if we can do this opcode:
if (EaCanRead(tea,size)==0) return 1;
if (EaCanWrite(tea)==0 || EaAn(tea)) return 1;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
EaCalc(10,0x0000, sea,size,1);
EaRead(10, 10, sea,size,0,1);
EaCalc(11,0x003f, tea,size,1);
EaRead(11, 0, tea,size,0x003f,1);
ot(";@ Do arithmetic:\n");
if (type==0) ot(" orr r1,r0,r10\n");
if (type==1) ot(" and r1,r0,r10\n");
if (type==2) ot(" subs r1,r0,r10 ;@ Defines NZCV\n");
if (type==3) ot(" adds r1,r0,r10 ;@ Defines NZCV\n");
if (type==5) ot(" eor r1,r0,r10\n");
if (type==6) ot(" cmp r0,r10 ;@ Defines NZCV\n");
if (type<2 || type==5) ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n"); // 0,1,5
if (type< 2) OpGetFlags(0,0); // Ori/And
if (type==2) OpGetFlags(1,1); // Sub: Subtract/X-bit
if (type==3) OpGetFlags(0,1); // Add: X-bit
if (type==5) OpGetFlags(0,0); // Eor
if (type==6) OpGetFlags(1,0); // Cmp: Subtract
ot("\n");
if (type!=6)
{
EaWrite(11, 1, tea,size,0x003f,1);
}
// Correct cycles:
if (type==6)
{
if (size>=2 && tea<0x10) Cycles+=2;
}
else
{
if (size>=2) Cycles+=4;
if (tea>=8) Cycles+=4;
if (type==1 && size>=2 && tea<8) Cycles-=2;
}
OpEnd();
return 0;
}
// --------------------- Opcodes 0x5000+ ---------------------
int OpAddq(int op)
{
// 0101nnnt xxeeeeee (nnn=#8,1-7 t=addq/subq xx=size, eeeeee=EA)
int num=0,type=0,size=0,ea=0;
int use=0;
char count[16]="";
int shift=0;
num =(op>>9)&7; if (num==0) num=8;
type=(op>>8)&1;
size=(op>>6)&3; if (size>=3) return 1;
ea = op&0x3f;
// See if we can do this opcode:
if (EaCanRead (ea,size)==0) return 1;
if (EaCanWrite(ea) ==0) return 1;
if (size == 0 && EaAn(ea) ) return 1;
use=OpBase(op,1);
if (num!=8) use|=0x0e00; // If num is not 8, use same handler
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
Cycles=ea<8?4:8;
if(type==0&&size==1) Cycles=ea<0x10?4:8;
if(size>=2) Cycles=ea<0x10?8:12;
if (size>0 && (ea&0x38)==0x08) size=2; // addq.w #n,An is also 32-bit
EaCalc(10,0x003f, ea,size,1);
EaRead(10, 0, ea,size,0x003f,1);
shift=32-(8<<size);
if (num!=8)
{
int lsr=9-shift;
if (lsr>=0) ot(" mov r2,r8,lsr #%d ;@ Get quick value\n", lsr);
else ot(" mov r2,r8,lsl #%d ;@ Get quick value\n",-lsr);
ot(" and r2,r2,#0x%.4x\n",7<<shift);
ot("\n");
strcpy(count,"r2");
}
if (num==8) sprintf(count,"#0x%.4x",8<<shift);
if (type==0) ot(" adds r1,r0,%s\n",count);
if (type==1) ot(" subs r1,r0,%s\n",count);
if ((ea&0x38)!=0x08) OpGetFlags(type,1);
ot("\n");
EaWrite(10, 1, ea,size,0x003f,1);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x8000+ ---------------------
// 1t0tnnnd xxeeeeee (tt=type:or/sub/and/add xx=size, eeeeee=EA)
int OpArithReg(int op)
{
int use=0;
int type=0,size=0,dir=0,rea=0,ea=0;
type=(op>>12)&5;
rea =(op>> 9)&7;
dir =(op>> 8)&1; // er,re
size=(op>> 6)&3; if (size>=3) return 1;
ea = op&0x3f;
if (dir && ea<0x10) return 1; // addx/subx opcode
// See if we can do this opcode:
if (dir==0 && EaCanRead (ea,size)==0) return 1;
if (dir && EaCanWrite(ea)==0) return 1;
if ((size==0||!(type&1))&&EaAn(ea)) return 1;
use=OpBase(op);
use&=~0x0e00; // Use same opcode for Dn
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
ot(";@ Get r10=EA r11=EA value\n");
EaCalc(10,0x003f, ea,size,1);
EaRead(10, 11, ea,size,0x003f,1);
ot(";@ Get r0=Register r1=Register value\n");
EaCalc( 0,0x0e00,rea,size,1);
EaRead( 0, 1,rea,size,0x0e00,1);
ot(";@ Do arithmetic:\n");
if (type==0) ot(" orr ");
if (type==1) ot(" subs ");
if (type==4) ot(" and ");
if (type==5) ot(" adds ");
if (dir) ot("r1,r11,r1\n");
else ot("r1,r1,r11\n");
if ((type&1)==0) ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(type==1,type&1); // 1==subtract
ot("\n");
ot(";@ Save result:\n");
if (dir) EaWrite(10, 1, ea,size,0x003f,1);
else EaWrite( 0, 1,rea,size,0x0e00,1);
if(rea==ea) {
if(ea<8) Cycles=(size>=2)?8:4; else Cycles+=(size>=2)?26:14;
} else if(dir) {
Cycles+=4;
if(size>=2) Cycles+=4;
} else {
if(size>=2) {
Cycles+=2;
if(ea<0x10||ea==0x3c) Cycles+=2;
}
}
OpEnd();
return 0;
}
// --------------------- Opcodes 0x80c0+ ---------------------
int OpMul(int op)
{
// Div/Mul: 1m00nnns 11eeeeee (m=Mul, nnn=Register Dn, s=signed, eeeeee=EA)
int type=0,rea=0,sign=0,ea=0;
int use=0;
type=(op>>14)&1; // div/mul
rea =(op>> 9)&7;
sign=(op>> 8)&1;
ea = op&0x3f;
// See if we can do this opcode:
if (EaCanRead(ea,1)==0||EaAn(ea)) return 1;
use=OpBase(op);
use&=~0x0e00; // Use same for all registers
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
if(type) Cycles=54;
else Cycles=sign?158:140;
EaCalc(10,0x003f, ea, 1);
EaRead(10, 10, ea, 1,0x003f);
EaCalc (0,0x0e00,rea, 2,1);
EaRead (0, 2,rea, 2,0x0e00,1);
if (type==0) // div
{
// the manual says C is always cleared, but neither Musashi nor FAME do that
//ot(" bic r9,r9,#0x20000000 ;@ always clear C\n");
ot(" tst r10,r10\n");
ot(" beq divzero%.4x ;@ division by zero\n",op);
ot("\n");
if (sign)
{
ot(" mov r11,#0 ;@ r11 = 1 or 2 if the result is negative\n");
ot(" orrmi r11,r11,#1\n");
ot(" rsbmi r10,r10,#0 ;@ Make r10 positive\n");
ot("\n");
ot(" tst r2,r2\n");
ot(" orrmi r11,r11,#2\n");
ot(" rsbmi r2,r2,#0 ;@ Make r2 positive\n");
ot("\n");
}
else
{
ot(" mov r10,r10,lsl #16 ;@ use only 16 bits of divisor\n");
ot(" mov r10,r10,lsr #16\n");
}
ot(";@ Divide r2 by r10\n");
ot(" mov r3,#0\n");
ot(" mov r1,r10\n");
ot("\n");
ot(";@ Shift up divisor till it's just less than numerator\n");
ot("Shift%.4x%s\n",op,ms?"":":");
ot(" cmp r1,r2,lsr #1\n");
ot(" movls r1,r1,lsl #1\n");
ot(" bcc Shift%.4x\n",op);
ot("\n");
ot("Divide%.4x%s\n",op,ms?"":":");
ot(" cmp r2,r1\n");
ot(" adc r3,r3,r3 ;@ Double r3 and add 1 if carry set\n");
ot(" subcs r2,r2,r1\n");
ot(" teq r1,r10\n");
ot(" movne r1,r1,lsr #1\n");
ot(" bne Divide%.4x\n",op);
ot("\n");
ot(";@r3==quotient,r2==remainder\n");
if (sign)
{
// sign correction
ot(" and r1,r11,#1\n");
ot(" teq r1,r11,lsr #1\n");
ot(" rsbne r3,r3,#0 ;@ negate if quotient is negative\n");
ot(" tst r11,#2\n");
ot(" rsbne r2,r2,#0 ;@ negate the remainder if divident was negative\n");
ot("\n");
// signed overflow check
ot(" mov r1,r3,asl #16\n");
ot(" cmp r3,r1,asr #16 ;@ signed overflow?\n");
ot(" orrne r9,r9,#0x10000000 ;@ set overflow flag\n");
ot(" bne endofop%.4x ;@ overflow!\n",op);
}
else
{
// overflow check
ot(" movs r1,r3,lsr #16 ;@ check for overflow condition\n");
ot(" orrne r9,r9,#0x10000000 ;@ set overflow flag\n");
ot(" bne endofop%.4x ;@ overflow!\n",op);
}
ot(" mov r1,r3,lsl #16 ;@ Clip to 16-bits\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot(" mov r1,r1,lsr #16\n");
ot(" orr r1,r1,r2,lsl #16 ;@ Insert remainder\n");
}
if (type==1)
{
char *shift="asr";
ot(";@ Get 16-bit signs right:\n");
if (sign==0) { ot(" mov r10,r10,lsl #16\n"); shift="lsr"; }
ot(" mov r2,r2,lsl #16\n");
if (sign==0) ot(" mov r10,r10,lsr #16\n");
ot(" mov r2,r2,%s #16\n",shift);
ot("\n");
ot(" mul r1,r2,r10\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
}
ot("\n");
EaWrite(0, 1,rea, 2,0x0e00,1);
ot("endofop%.4x%s\n",op,ms?"":":");
OpEnd();
ot("divzero%.4x%s\n",op,ms?"":":");
ot(" mov r0,#0x14 ;@ Divide by zero\n");
ot(" bl Exception\n");
Cycles+=38;
OpEnd();
ot("\n");
return 0;
}
// Get X Bit into carry - trashes r2
int GetXBit(int subtract)
{
ot(";@ Get X bit:\n");
ot(" ldrb r2,[r7,#0x45]\n");
if (subtract) ot(" mvn r2,r2,lsl #28 ;@ Invert it\n");
else ot(" mov r2,r2,lsl #28\n");
ot(" msr cpsr_flg,r2 ;@ Get into Carry\n");
ot("\n");
return 0;
}
// --------------------- Opcodes 0x8100+ ---------------------
// 1t00ddd1 0000asss - sbcd/abcd Ds,Dd or -(As),-(Ad)
int OpAbcd(int op)
{
int use=0;
int type=0,sea=0,addr=0,dea=0;
type=(op>>14)&1; // sbcd/abcd
dea =(op>> 9)&7;
addr=(op>> 3)&1;
sea = op &7;
if (addr) { sea|=0x20; dea|=0x20; }
use=op&~0x0e07; // Use same opcode for all registers..
if (sea==0x27||dea==0x27) use=op; // ..except -(a7)
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=6;
EaCalc( 0,0x0007, sea,0,1);
EaRead( 0, 10, sea,0,0x0007,1);
EaCalc(11,0x0e00, dea,0,1);
EaRead(11, 1, dea,0,0x0e00,1);
ot(" bic r9,r9,#0xb1000000 ;@ clear all flags except old Z\n");
if (type)
{
ot(" ldrb r0,[r7,#0x45] ;@ Get X bit\n");
ot(" mov r3,#0x00f00000\n");
ot(" and r2,r3,r1,lsr #4\n");
ot(" tst r0,#2\n");
ot(" and r0,r3,r10,lsr #4\n");
ot(" add r0,r0,r2\n");
ot(" addne r0,r0,#0x00100000\n");
// ot(" tst r0,#0x00800000\n");
// ot(" orreq r9,r9,#0x01000000 ;@ Undefined V behavior\n");
ot(" cmp r0,#0x00900000\n");
ot(" addhi r0,r0,#0x00600000 ;@ Decimal adjust units\n");
ot(" mov r2,r1,lsr #28\n");
ot(" add r0,r0,r2,lsl #24\n");
ot(" mov r2,r10,lsr #28\n");
ot(" add r0,r0,r2,lsl #24\n");
ot(" cmp r0,#0x09900000\n");
ot(" orrhi r9,r9,#0x20000000 ;@ C\n");
ot(" subhi r0,r0,#0x0a000000\n");
// ot(" and r3,r9,r0,lsr #3 ;@ Undefined V behavior part II\n");
// ot(" orr r9,r9,r3,lsl #4 ;@ V\n");
ot(" movs r0,r0,lsl #4\n");
ot(" orrmi r9,r9,#0x90000000 ;@ Undefined N+V behavior\n"); // this is what Musashi really does
ot(" bicne r9,r9,#0x40000000 ;@ Z flag\n");
}
else
{
ot(" ldrb r0,[r7,#0x45] ;@ Get X bit\n");
ot(" mov r3,#0x00f00000\n");
ot(" and r2,r3,r10,lsr #4\n");
ot(" tst r0,#2\n");
ot(" and r0,r3,r1,lsr #4\n");
ot(" sub r0,r0,r2\n");
ot(" subne r0,r0,#0x00100000\n");
// ot(" tst r0,#0x00800000\n");
// ot(" orreq r9,r9,#0x01000000 ;@ Undefined V behavior\n");
ot(" cmp r0,#0x00900000\n");
ot(" subhi r0,r0,#0x00600000 ;@ Decimal adjust units\n");
ot(" mov r2,r1,lsr #28\n");
ot(" add r0,r0,r2,lsl #24\n");
ot(" mov r2,r10,lsr #28\n");
ot(" sub r0,r0,r2,lsl #24\n");
ot(" cmp r0,#0x09900000\n");
ot(" orrhi r9,r9,#0xa0000000 ;@ N and C\n");
ot(" addhi r0,r0,#0x0a000000\n");
// ot(" and r3,r9,r0,lsr #3 ;@ Undefined V behavior part II\n");
// ot(" orr r9,r9,r3,lsl #4 ;@ V\n");
ot(" movs r0,r0,lsl #4\n");
// ot(" orrmi r9,r9,#0x80000000 ;@ Undefined N behavior\n");
ot(" bicne r9,r9,#0x40000000 ;@ Z flag\n");
}
ot(" mov r2,r9,lsr #28\n");
ot(" strb r2,[r7,#0x45] ;@ Save X bit\n");
EaWrite(11, 0, dea,0,0x0e00,1);
OpEnd();
return 0;
}
// 01008000 00eeeeee - nbcd <ea>
int OpNbcd(int op)
{
int use=0;
int ea=0;
ea=op&0x3f;
if(EaCanWrite(ea)==0||EaAn(ea)) return 1;
use=OpBase(op);
if(op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=6;
if(ea >= 8) Cycles+=2;
EaCalc(10,0x3f, ea,0,1);
EaRead(10, 0, ea,0,0x3f,1);
// this is rewrite of Musashi's code
ot(" ldrb r2,[r7,#0x45]\n");
ot(" tst r2,#2\n");
ot(" mov r2,r0\n");
ot(" addne r2,r0,#0x01000000 ;@ add X\n");
ot(" rsbs r1,r2,#0x9a000000 ;@ do arithmetic\n");
ot(" bic r9,r9,#0xb0000000 ;@ clear all flags, except Z\n");
ot(" orrmi r9,r9,#0x80000000 ;@ N\n");
ot(" cmp r1,#0x9a000000\n");
ot(" beq finish%.4x\n",op);
ot("\n");
ot(" mvn r3,r9,lsr #3 ;@ Undefined V behavior\n",op);
ot(" and r2,r1,#0x0f000000\n");
ot(" cmp r2,#0x0a000000\n");
ot(" andeq r1,r1,#0xf0000000\n");
ot(" addeq r1,r1,#0x10000000\n");
ot(" and r3,r3,r1,lsr #3 ;@ Undefined V behavior part II\n",op);
ot(" tst r1,r1\n");
ot(" orr r9,r9,r3 ;@ save V\n",op);
ot(" bicne r9,r9,#0x40000000 ;@ Z\n");
ot(" orr r9,r9,#0x20000000 ;@ C\n");
ot("\n");
EaWrite(10, 1, ea,0,0x3f,1);
ot("finish%.4x%s\n",op,ms?"":":");
ot(" mov r2,r9,lsr #28\n");
ot(" strb r2, [r7,#0x45]\n");
OpEnd();
return 0;
}
// --------------------- Opcodes 0x90c0+ ---------------------
// Suba/Cmpa/Adda 1tt1nnnx 11eeeeee (tt=type, x=size, eeeeee=Source EA)
int OpAritha(int op)
{
int use=0;
int type=0,size=0,sea=0,dea=0;
// Suba/Cmpa/Adda/(invalid):
type=(op>>13)&3; if (type>=3) return 1;
size=(op>>8)&1; size++;
dea=(op>>9)&7; dea|=8; // Dest=An
sea=op&0x003f; // Source
// See if we can do this opcode:
if (EaCanRead(sea,size)==0) return 1;
use=OpBase(op);
use&=~0x0e00; // Use same opcode for An
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=(size==2)?6:8;
if(size==2&&(sea<0x10||sea==0x3c)) Cycles+=2;
if(type==1) Cycles=6;
EaCalc ( 0,0x003f, sea,size);
EaRead ( 0, 10, sea,size,0x003f);
EaCalc ( 0,0x0e00, dea,2,1);
EaRead ( 0, 1, dea,2,0x0e00);
if (type==0) ot(" sub r1,r1,r10\n");
if (type==1) ot(" cmp r1,r10 ;@ Defines NZCV\n");
if (type==1) OpGetFlags(1,0); // Get Cmp flags
if (type==2) ot(" add r1,r1,r10\n");
ot("\n");
if (type!=1) EaWrite( 0, 1, dea,2,0x0e00,1);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x9100+ ---------------------
// Emit a Subx/Addx opcode, 1t01ddd1 zz00rsss addx.z Ds,Dd
int OpAddx(int op)
{
int use=0;
int type=0,size=0,dea=0,sea=0,mem=0;
type=(op>>12)&5;
dea =(op>> 9)&7;
size=(op>> 6)&3; if (size>=3) return 1;
sea = op&7;
mem =(op>> 3)&1;
// See if we can do this opcode:
if (EaCanRead(sea,size)==0) return 1;
if (EaCanWrite(dea)==0) return 1;
if(mem) { sea+=0x20; dea+=0x20; }
use=op&~0x0e07; // Use same opcode for Dn
if (size==0&&(sea==0x27||dea==0x27)) use=op; // ___x.b -(a7)
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
if(size>=2) Cycles+=4;
if(sea>=0x10) Cycles+=2;
ot(";@ Get r10=EA r11=EA value\n");
EaCalc( 0,0x0007,sea,size,1);
EaRead( 0, 11,sea,size,0x0007,1);
ot(";@ Get r0=Register r1=Register value\n");
EaCalc( 0,0x0e00,dea,size,1);
EaRead( 0, 1,dea,size,0x0e00,1);
ot(";@ Do arithmetic:\n");
GetXBit(type==1);
if (type==5 && size<2)
{
ot(";@ Make sure the carry bit will tip the balance:\n");
ot(" mvn r2,#0\n");
ot(" orr r11,r11,r2,lsr #%i\n",(size==0)?8:16);
ot("\n");
}
if (type==1) ot(" sbcs r1,r1,r11\n");
if (type==5) ot(" adcs r1,r1,r11\n");
ot(" orr r3,r9,#0xb0000000 ;@ for old Z\n");
OpGetFlags(type==1,1,0); // subtract
if (size<2) {
ot(" movs r2,r1,lsr #%i\n", size?16:24);
ot(" orreq r9,r9,#0x40000000 ;@ add potentially missed Z\n");
}
ot(" andeq r9,r9,r3 ;@ fix Z\n");
ot("\n");
ot(";@ Save result:\n");
EaWrite( 0, 1, dea,size,0x0e00,1);
OpEnd();
return 0;
}
// --------------------- Opcodes 0xb000+ ---------------------
// Emit a Cmp/Eor opcode, 1011rrrt xxeeeeee (rrr=Dn, t=cmp/eor, xx=size extension, eeeeee=ea)
int OpCmpEor(int op)
{
int rea=0,eor=0;
int size=0,ea=0,use=0;
// Get EA and register EA
rea=(op>>9)&7;
eor=(op>>8)&1;
size=(op>>6)&3; if (size>=3) return 1;
ea=op&0x3f;
if (eor && (ea>>3) == 1) return 1; // not a valid mode for eor
// See if we can do this opcode:
if (EaCanRead(ea,size)==0) return 1;
if (eor && EaCanWrite(ea)==0) return 1;
if (EaAn(ea)&&(eor||size==0)) return 1;
use=OpBase(op);
use&=~0x0e00; // Use 1 handler for register d0-7
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
if(eor) {
if(ea>8) Cycles+=4;
if(size>=2) Cycles+=4;
} else {
if(size>=2) Cycles+=2;
}
ot(";@ Get EA into r10 and value into r0:\n");
EaCalc (10,0x003f, ea,size,1);
EaRead (10, 0, ea,size,0x003f,1);
ot(";@ Get register operand into r1:\n");
EaCalc (1, 0x0e00, rea,size,1);
EaRead (1, 1, rea,size,0x0e00,1);
ot(";@ Do arithmetic:\n");
if (eor==0) ot(" cmp r1,r0\n");
if (eor)
{
ot(" eor r1,r0,r1\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
}
OpGetFlags(eor==0,0); // Cmp like subtract
ot("\n");
if (eor) EaWrite(10, 1,ea,size,0x003f,1);
OpEnd();
return 0;
}
// Emit a Cmpm opcode, 1011ddd1 xx001sss (rrr=Adst, xx=size extension, sss=Asrc)
int OpCmpm(int op)
{
int size=0,sea=0,dea=0,use=0;
// get size, get EAs
size=(op>>6)&3; if (size>=3) return 1;
sea=(op&7)|0x18;
dea=(op>>9)&0x3f;
use=op&~0x0e07; // Use 1 handler for all registers..
if (size==0&&(sea==0x1f||dea==0x1f)) use=op; // ..except (a7)+
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
ot(";@ Get src operand into r10:\n");
EaCalc (0,0x000f, sea,size,1);
EaRead (0, 10, sea,size,0x000f,1);
ot(";@ Get dst operand into r0:\n");
EaCalc (0,0x1e00, dea,size,1);
EaRead (0, 0, dea,size,0x1e00,1);
ot(" cmp r0,r10\n");
OpGetFlags(1,0); // Cmp like subtract
OpEnd();
return 0;
}
// Emit a Chk opcode, 0100ddd1 x0eeeeee (rrr=Dn, x=size extension, eeeeee=ea)
int OpChk(int op)
{
int rea=0;
int size=0,ea=0,use=0;
// Get EA and register EA
rea=(op>>9)&7;
if((op>>7)&1)
size=1; // word operation
else size=2; // long
ea=op&0x3f;
if (EaAn(ea)) return 1; // not a valid mode
if (size!=1) return 1; // 000 variant only supports word
// See if we can do this opcode:
if (EaCanRead(ea,size)==0) return 1;
use=OpBase(op);
use&=~0x0e00; // Use 1 handler for register d0-7
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=10;
ot(";@ Get EA into r10 and value into r0:\n");
EaCalc (10,0x003f, ea,size,1);
EaRead (10, 0, ea,size,0x003f,1);
ot(";@ Get register operand into r1:\n");
EaCalc (1, 0x0e00, rea,size,1);
EaRead (1, 1, rea,size,0x0e00,1);
ot(";@ get flags, including undocumented ones\n");
ot(" and r3,r9,#0x80000000\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot(";@ is reg negative?\n");
ot(" bmi chktrap%.4x\n",op);
ot(";@ Do arithmetic:\n");
ot(" cmp r1,r0\n");
ot(" bicgt r9,r9,#0x80000000 ;@ N\n");
ot(" bgt chktrap%.4x\n",op);
ot(";@ old N remains\n");
ot(" bic r9,r9,#0x80000000 ;@ N\n");
ot(" orr r9,r9,r3\n");
OpEnd();
ot("chktrap%.4x%s ;@ CHK exception:\n",op,ms?"":":");
ot(" mov r0,#0x18\n");
ot(" bl Exception\n");
Cycles+=40;
OpEnd();
return 0;
}

434
cpu/Cyclone/OpBranch.cpp Normal file
View file

@ -0,0 +1,434 @@
#include "app.h"
#if USE_CHECKPC_CALLBACK
static void CheckPc()
{
ot(";@ Check Memory Base+pc (r4)\n");
ot(" add lr,pc,#4\n");
ot(" mov r0,r4\n");
ot(" ldr pc,[r7,#0x64] ;@ Call checkpc()\n");
ot(" mov r4,r0\n");
ot("\n");
}
#endif
// Push 32-bit value in r1 - trashes r0-r3,r12,lr
void OpPush32()
{
ot(";@ Push r1 onto stack\n");
ot(" ldr r0,[r7,#0x3c]\n");
ot(" sub r0,r0,#4 ;@ Predecrement A7\n");
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
MemHandler(1,2);
ot("\n");
}
// Push SR - trashes r0-r3,r12,lr
void OpPushSr(int high)
{
ot(";@ Push SR:\n");
OpFlagsToReg(high);
ot(" ldr r0,[r7,#0x3c]\n");
ot(" sub r0,r0,#2 ;@ Predecrement A7\n");
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
MemHandler(1,1);
ot("\n");
}
// Pop SR - trashes r0-r3
static void PopSr(int high)
{
ot(";@ Pop SR:\n");
ot(" ldr r0,[r7,#0x3c]\n");
ot(" add r1,r0,#2 ;@ Postincrement A7\n");
ot(" str r1,[r7,#0x3c] ;@ Save A7\n");
MemHandler(0,1);
ot("\n");
OpRegToFlags(high);
}
// Pop PC - assumes r10=Memory Base - trashes r0-r3
static void PopPc()
{
ot(";@ Pop PC:\n");
ot(" ldr r0,[r7,#0x3c]\n");
ot(" add r1,r0,#4 ;@ Postincrement A7\n");
ot(" str r1,[r7,#0x3c] ;@ Save A7\n");
MemHandler(0,2);
ot(" add r4,r0,r10 ;@ r4=Memory Base+PC\n");
ot("\n");
CheckPc();
}
int OpTrap(int op)
{
int use=0;
use=op&~0xf;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
ot(" and r0,r8,#0xf ;@ Get trap number\n");
ot(" orr r0,r0,#0x20\n");
ot(" mov r0,r0,asl #2\n");
ot(" bl Exception\n");
ot("\n");
Cycles=38; OpEnd();
return 0;
}
// --------------------- Opcodes 0x4e50+ ---------------------
int OpLink(int op)
{
int use=0,reg;
use=op&~7;
reg=op&7;
if (reg==7) use=op;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
if(reg!=7) {
ot(";@ Get An\n");
EaCalc(10, 7, 8, 2, 1);
EaRead(10, 1, 8, 2, 7, 1);
}
ot(" ldr r0,[r7,#0x3c] ;@ Get A7\n");
ot(" sub r0,r0,#4 ;@ A7-=4\n");
ot(" mov r11,r0\n");
if(reg==7) ot(" mov r1,r0\n");
ot("\n");
ot(";@ Write An to Stack\n");
MemHandler(1,2);
ot(";@ Save to An\n");
if(reg!=7)
EaWrite(10,11, 8, 2, 7, 1);
ot(";@ Get offset:\n");
EaCalc(0,0,0x3c,1);
EaRead(0,0,0x3c,1,0);
ot(" add r11,r11,r0 ;@ Add offset to A7\n");
ot(" str r11,[r7,#0x3c]\n");
ot("\n");
Cycles=16;
OpEnd();
return 0;
}
// --------------------- Opcodes 0x4e58+ ---------------------
int OpUnlk(int op)
{
int use=0;
use=op&~7;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
ot(";@ Get An\n");
EaCalc(10, 7, 8, 2, 1);
EaRead(10, 0, 8, 2, 7, 1);
ot(" add r11,r0,#4 ;@ A7+=4\n");
ot("\n");
ot(";@ Pop An from stack:\n");
MemHandler(0,2);
ot("\n");
ot(" str r11,[r7,#0x3c] ;@ Save A7\n");
ot("\n");
ot(";@ An = value from stack:\n");
EaWrite(10, 0, 8, 2, 7, 1);
Cycles=12;
OpEnd();
return 0;
}
// --------------------- Opcodes 0x4e70+ ---------------------
int Op4E70(int op)
{
int type=0;
type=op&7; // 01001110 01110ttt, reset/nop/stop/rte/rtd/rts/trapv/rtr
switch (type)
{
case 1: // nop
OpStart(op);
Cycles=4;
OpEnd();
return 0;
case 3: // rte
OpStart(op); Cycles=20;
SuperCheck(op);
PopSr(1);
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
PopPc();
SuperChange(op);
CheckInterrupt(op);
OpEnd();
SuperEnd(op);
return 0;
case 5: // rts
OpStart(op); Cycles=16;
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
PopPc();
OpEnd();
return 0;
case 6: // trapv
OpStart(op); Cycles=4;
ot(" tst r9,#0x10000000\n");
ot(" subne r5,r5,#%i\n",30);
ot(" movne r0,#0x1c ;@ TRAPV exception\n");
ot(" blne Exception\n");
OpEnd();
return 0;
case 7: // rtr
OpStart(op); Cycles=20;
PopSr(0);
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
PopPc();
OpEnd();
return 0;
default:
return 1;
}
}
// --------------------- Opcodes 0x4e80+ ---------------------
// Emit a Jsr/Jmp opcode, 01001110 1meeeeee
int OpJsr(int op)
{
int use=0;
int sea=0;
sea=op&0x003f;
// See if we can do this opcode:
if (EaCanRead(sea,-1)==0) return 1;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
ot("\n");
EaCalc(0,0x003f,sea,0);
ot(";@ Jump - Get new PC from r0\n");
if (op&0x40)
{
// Jmp - Get new PC from r0
ot(" add r4,r0,r10 ;@ r4 = Memory Base + New PC\n");
ot("\n");
}
else
{
ot(";@ Jsr - Push old PC first\n");
ot(" sub r1,r4,r10 ;@ r1 = Old PC\n");
ot(" add r4,r0,r10 ;@ r4 = Memory Base + New PC\n");
ot(" mov r1,r1,lsl #8\n");
ot(" ldr r0,[r7,#0x3c]\n");
ot(" mov r1,r1,asr #8\n");
ot(";@ Push r1 onto stack\n");
ot(" sub r0,r0,#4 ;@ Predecrement A7\n");
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
MemHandler(1,2);
ot("\n");
}
#if USE_CHECKPC_CALLBACK
CheckPc();
#endif
Cycles=(op&0x40) ? 4 : 12;
Cycles+=Ea_add_ns((op&0x40) ? g_jmp_cycle_table : g_jsr_cycle_table, sea);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x50c8+ ---------------------
// ARM version of 68000 condition codes:
static char *Cond[16]=
{
"", "", "hi","ls","cc","cs","ne","eq",
"vc","vs","pl","mi","ge","lt","gt","le"
};
// Emit a Dbra opcode, 0101cccc 11001nnn vv
int OpDbra(int op)
{
int use=0;
int cc=0;
use=op&~7; // Use same handler
cc=(op>>8)&15;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
if (cc>=2)
{
ot(";@ Is the condition true?\n");
if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000 ;@ Invert carry for hi/ls\n");
ot(" msr cpsr_flg,r9 ;@ ARM flags = 68000 flags\n");
if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000\n");
ot(";@ If so, don't dbra\n");
ot(" b%s DbraTrue%.4x\n",Cond[cc],op);
ot("\n");
}
ot(";@ Decrement Dn.w\n");
ot(" and r1,r8,#0x0007\n");
ot(" mov r1,r1,lsl #2\n");
ot(" ldrsh r0,[r7,r1]\n");
ot(" sub r0,r0,#1\n");
ot(" strh r0,[r7,r1]\n");
ot("\n");
ot(";@ Check if Dn.w is -1\n");
ot(" cmps r0,#-1\n");
ot(" beq DbraMin1%.4x\n",op);
ot("\n");
ot(";@ Get Branch offset:\n");
ot(" ldrsh r0,[r4]\n");
ot(" add r4,r4,r0 ;@ r4 = New PC\n");
ot("\n");
Cycles=12-2;
OpEnd();
ot(";@ Dn.w is -1:\n");
ot("DbraMin1%.4x%s\n", op, ms?"":":");
ot(" add r4,r4,#2 ;@ Skip branch offset\n");
ot("\n");
Cycles=12+2;
OpEnd();
ot(";@ condition true:\n");
ot("DbraTrue%.4x%s\n", op, ms?"":":");
ot(" add r4,r4,#2 ;@ Skip branch offset\n");
ot("\n");
Cycles=12;
OpEnd();
return 0;
}
// --------------------- Opcodes 0x6000+ ---------------------
// Emit a Branch opcode 0110cccc nn (cccc=condition)
int OpBranch(int op)
{
int size=0,use=0;
int offset=0;
int cc=0;
offset=(char)(op&0xff);
cc=(op>>8)&15;
// Special offsets:
if (offset==0) size=1;
if (offset==-1) size=2;
if (size) use=op; // 16-bit or 32-bit
else use=(op&0xff00)+1; // Use same opcode for all 8-bit branches
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
ot(";@ Get Branch offset:\n");
if (size)
{
EaCalc(0,0,0x3c,size);
EaRead(0,0,0x3c,size,0);
}
// above code messes cycles
Cycles=10; // Assume branch taken
if (size==0) ot(" mov r0,r8,asl #24 ;@ Shift 8-bit signed offset up...\n\n");
if (cc==1) ot(" ldr r10,[r7,#0x60] ;@ Get Memory base\n");
if (cc>=2)
{
ot(";@ Is the condition true?\n");
if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000 ;@ Invert carry for hi/ls\n");
ot(" msr cpsr_flg,r9 ;@ ARM flags = 68000 flags\n");
if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000\n");
if (size==0) ot(" mov r0,r0,asr #24 ;@ ...shift down\n\n");
ot(" b%s DontBranch%.4x\n",Cond[cc^1],op);
ot("\n");
}
else
{
if (size==0) ot(" mov r0,r0,asr #24 ;@ ...shift down\n\n");
}
ot(";@ Branch taken - Add on r0 to PC\n");
if (cc==1)
{
ot(";@ Bsr - remember old PC\n");
ot(" sub r1,r4,r10 ;@ r1 = Old PC\n");
ot(" mov r1,r1, lsl #8\n");
ot(" mov r1,r1, asr #8\n");
ot("\n");
if (size) ot(" sub r4,r4,#%d ;@ (Branch is relative to Opcode+2)\n",1<<size);
ot(" ldr r2,[r7,#0x3c]\n");
ot(" add r4,r4,r0 ;@ r4 = New PC\n");
ot(";@ Push r1 onto stack\n");
ot(" sub r0,r2,#4 ;@ Predecrement A7\n");
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
MemHandler(1,2);
ot("\n");
Cycles=18; // always 18
}
else
{
if (size) ot(" sub r4,r4,#%d ;@ (Branch is relative to Opcode+2)\n",1<<size);
ot(" add r4,r4,r0 ;@ r4 = New PC\n");
ot("\n");
}
#if USE_CHECKPC_CALLBACK
if (offset==0 || offset==-1)
{
ot(";@ Branch is quite far, so may be a good idea to check Memory Base+pc\n");
CheckPc();
}
#endif
OpEnd();
if (cc>=2)
{
ot("DontBranch%.4x%s\n", op, ms?"":":");
Cycles+=(size==1)? 2 : -2; // Branch not taken
OpEnd();
}
return 0;
}

672
cpu/Cyclone/OpLogic.cpp Normal file
View file

@ -0,0 +1,672 @@
#include "app.h"
// --------------------- Opcodes 0x0100+ ---------------------
// Emit a Btst (Register) opcode 0000nnn1 ttaaaaaa
int OpBtstReg(int op)
{
int use=0;
int type=0,sea=0,tea=0;
int size=0;
type=(op>>6)&3; // Btst/Bchg/Bclr/Bset
// Get source and target EA
sea=(op>>9)&7;
tea=op&0x003f;
if (tea<0x10) size=2; // For registers, 32-bits
if ((tea&0x38)==0x08) return 1; // movep
// See if we can do this opcode:
if (EaCanRead(tea,0)==0) return 1;
if (type>0)
{
if (EaCanWrite(tea)==0) return 1;
}
use=OpBase(op);
use&=~0x0e00; // Use same handler for all registers
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
if(type==1||type==3) {
Cycles=8;
} else {
Cycles=type?8:4;
if(size>=2) Cycles+=2;
}
EaCalc (0,0x0e00,sea,0);
EaRead (0, 0,sea,0,0x0e00);
if (tea>=0x10)
ot(" and r10,r0,#7 ;@ mem - do mod 8\n");
else ot(" and r10,r0,#31 ;@ reg - do mod 32\n");
ot("\n");
EaCalc(11,0x003f,tea,size);
EaRead(11, 0,tea,size,0x003f);
ot(" mov r1,#1\n");
ot(" tst r0,r1,lsl r10 ;@ Do arithmetic\n");
ot(" bicne r9,r9,#0x40000000\n");
ot(" orreq r9,r9,#0x40000000 ;@ Get Z flag\n");
ot("\n");
if (type>0)
{
if (type==1) ot(" eor r1,r0,r1,lsl r10 ;@ Toggle bit\n");
if (type==2) ot(" bic r1,r0,r1,lsl r10 ;@ Clear bit\n");
if (type==3) ot(" orr r1,r0,r1,lsl r10 ;@ Set bit\n");
ot("\n");
EaWrite(11, 1,tea,size,0x003f);
}
OpEnd();
return 0;
}
// --------------------- Opcodes 0x0800+ ---------------------
// Emit a Btst/Bchg/Bclr/Bset (Immediate) opcode 00001000 ttaaaaaa nn
int OpBtstImm(int op)
{
int type=0,sea=0,tea=0;
int use=0;
int size=0;
type=(op>>6)&3;
// Get source and target EA
sea= 0x003c;
tea=op&0x003f;
if (tea<0x10) size=2; // For registers, 32-bits
// See if we can do this opcode:
if (EaCanRead(tea,0)==0||EaAn(tea)||tea==0x3c) return 1;
if (type>0)
{
if (EaCanWrite(tea)==0) return 1;
}
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
ot(" mov r10,#1\n");
ot("\n");
EaCalc ( 0,0x0000,sea,0);
EaRead ( 0, 0,sea,0,0);
ot(" bic r9,r9,#0x40000000 ;@ Blank Z flag\n");
if (tea>=0x10)
ot(" and r0,r0,#7 ;@ mem - do mod 8\n");
else ot(" and r0,r0,#0x1F ;@ reg - do mod 32\n");
ot(" mov r10,r10,lsl r0 ;@ Make bit mask\n");
ot("\n");
if(type==1||type==3) {
Cycles=12;
} else {
Cycles=type?12:8;
if(size>=2) Cycles+=2;
}
EaCalc (11,0x003f,tea,size);
EaRead (11, 0,tea,size,0x003f);
ot(" tst r0,r10 ;@ Do arithmetic\n");
ot(" orreq r9,r9,#0x40000000 ;@ Get Z flag\n");
ot("\n");
if (type>0)
{
if (type==1) ot(" eor r1,r0,r10 ;@ Toggle bit\n");
if (type==2) ot(" bic r1,r0,r10 ;@ Clear bit\n");
if (type==3) ot(" orr r1,r0,r10 ;@ Set bit\n");
ot("\n");
EaWrite(11, 1,tea,size,0x003f);
}
OpEnd();
return 0;
}
// --------------------- Opcodes 0x4000+ ---------------------
int OpNeg(int op)
{
// 01000tt0 xxeeeeee (tt=negx/clr/neg/not, xx=size, eeeeee=EA)
int type=0,size=0,ea=0,use=0;
type=(op>>9)&3;
ea =op&0x003f;
size=(op>>6)&3; if (size>=3) return 1;
// See if we can do this opcode:
if (EaCanRead (ea,size)==0||EaAn(ea)) return 1;
if (EaCanWrite(ea )==0) return 1;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=size<2?4:6;
if(ea >= 0x10) {
Cycles*=2;
#ifdef CYCLONE_FOR_GENESIS
// This is same as in Starscream core, CLR uses only 6 cycles for memory EAs.
// May be this is similar case as with TAS opcode, but this time the dummy
// read is ignored somehow? Without this hack Fatal Rewind hangs even in Gens.
if(type==1&&size<2) Cycles-=2;
#endif
}
EaCalc (10,0x003f,ea,size);
if (type!=1) EaRead (10,0,ea,size,0x003f); // Don't need to read for 'clr'
if (type==1) ot("\n");
if (type==0)
{
ot(";@ Negx:\n");
GetXBit(1);
if(size!=2) ot(" mov r0,r0,lsl #%i\n",size?16:24);
ot(" rscs r1,r0,#0 ;@ do arithmetic\n");
ot(" orr r3,r9,#0xb0000000 ;@ for old Z\n");
OpGetFlags(1,1,0);
if(size!=2) {
ot(" movs r1,r1,asr #%i\n",size?16:24);
ot(" orreq r9,r9,#0x40000000 ;@ possily missed Z\n");
}
ot(" andeq r9,r9,r3 ;@ fix Z\n");
ot("\n");
}
if (type==1)
{
ot(";@ Clear:\n");
ot(" mov r1,#0\n");
ot(" mov r9,#0x40000000 ;@ NZCV=0100\n");
ot("\n");
}
if (type==2)
{
ot(";@ Neg:\n");
if(size!=2) ot(" mov r0,r0,lsl #%i\n",size?16:24);
ot(" rsbs r1,r0,#0\n");
OpGetFlags(1,1);
if(size!=2) ot(" mov r1,r1,asr #%i\n",size?16:24);
ot("\n");
}
if (type==3)
{
ot(";@ Not:\n");
ot(" mvn r1,r0\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot("\n");
}
EaWrite(10, 1,ea,size,0x003f);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x4840+ ---------------------
// Swap, 01001000 01000nnn swap Dn
int OpSwap(int op)
{
int ea=0,use=0;
ea=op&7;
use=op&~0x0007; // Use same opcode for all An
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
EaCalc (10,0x0007,ea,2,1);
EaRead (10, 0,ea,2,0x0007,1);
ot(" mov r1,r0,ror #16\n");
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
EaWrite(10, 1,8,2,0x0007,1);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x4a00+ ---------------------
// Emit a Tst opcode, 01001010 xxeeeeee
int OpTst(int op)
{
int sea=0;
int size=0,use=0;
sea=op&0x003f;
size=(op>>6)&3; if (size>=3) return 1;
// See if we can do this opcode:
if (EaCanWrite(sea)==0||EaAn(sea)) return 1;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
EaCalc ( 0,0x003f,sea,size,1);
EaRead ( 0, 0,sea,size,0x003f,1);
ot(" adds r0,r0,#0 ;@ Defines NZ, clears CV\n");
ot(" mrs r9,cpsr ;@ r9=flags\n");
ot("\n");
OpEnd();
return 0;
}
// --------------------- Opcodes 0x4880+ ---------------------
// Emit an Ext opcode, 01001000 1x000nnn
int OpExt(int op)
{
int ea=0;
int size=0,use=0;
int shift=0;
ea=op&0x0007;
size=(op>>6)&1;
shift=32-(8<<size);
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
EaCalc (10,0x0007,ea,size+1);
EaRead (10, 0,ea,size+1,0x0007);
ot(" mov r0,r0,asl #%d\n",shift);
ot(" adds r0,r0,#0 ;@ Defines NZ, clears CV\n");
ot(" mrs r9,cpsr ;@ r9=flags\n");
ot(" mov r1,r0,asr #%d\n",shift);
ot("\n");
EaWrite(10, 1,ea,size+1,0x0007);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x50c0+ ---------------------
// Emit a Set cc opcode, 0101cccc 11eeeeee
int OpSet(int op)
{
int cc=0,ea=0;
int size=0,use=0;
char *cond[16]=
{
"al","", "hi","ls","cc","cs","ne","eq",
"vc","vs","pl","mi","ge","lt","gt","le"
};
cc=(op>>8)&15;
ea=op&0x003f;
if ((ea&0x38)==0x08) return 1; // dbra, not scc
// See if we can do this opcode:
if (EaCanWrite(ea)==0) return 1;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=8;
if (ea<8) Cycles=4;
ot(" mov r1,#0\n");
if (cc!=1)
{
ot(";@ Is the condition true?\n");
if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000 ;@ Invert carry for hi/ls\n");
ot(" msr cpsr_flg,r9 ;@ ARM flags = 68000 flags\n");
if ((cc&~1)==2) ot(" eor r9,r9,#0x20000000 ;@ Invert carry for hi/ls\n");
ot(" mvn%s r1,r1\n",cond[cc]);
}
if (cc!=1 && ea<8) ot(" sub%s r5,r5,#2 ;@ Extra cycles\n",cond[cc]);
ot("\n");
EaCalc (0,0x003f, ea,size);
EaWrite(0, 1, ea,size,0x003f);
OpEnd();
return 0;
}
// Emit a Asr/Lsr/Roxr/Ror opcode
static int EmitAsr(int op,int type,int dir,int count,int size,int usereg)
{
char pct[8]=""; // count
int shift=32-(8<<size);
if (count>=1) sprintf(pct,"#%d",count); // Fixed count
if (usereg)
{
ot(";@ Use Dn for count:\n");
ot(" and r2,r8,#7<<9\n");
ot(" ldr r2,[r7,r2,lsr #7]\n");
ot(" and r2,r2,#63\n");
ot("\n");
strcpy(pct,"r2");
}
else if (count<0)
{
ot(" mov r2,r8,lsr #9 ;@ Get 'n'\n");
ot(" and r2,r2,#7\n\n"); strcpy(pct,"r2");
}
// Take 2*n cycles:
if (count<0) ot(" sub r5,r5,r2,asl #1 ;@ Take 2*n cycles\n\n");
else Cycles+=count<<1;
if (type<2)
{
// Asr/Lsr
if (dir==0 && size<2)
{
ot(";@ For shift right, use loworder bits for the operation:\n");
ot(" mov r0,r0,%s #%d\n",type?"lsr":"asr",32-(8<<size));
ot("\n");
}
if (type==0 && dir) ot(" mov r3,r0 ;@ save old value for V flag calculation\n");
ot(";@ Shift register:\n");
if (type==0) ot(" movs r0,r0,%s %s\n",dir?"asl":"asr",pct);
if (type==1) ot(" movs r0,r0,%s %s\n",dir?"lsl":"lsr",pct);
if (dir==0 && size<2)
{
ot(";@ restore after right shift:\n");
ot(" mov r0,r0,lsl #%d\n",32-(8<<size));
ot("\n");
}
OpGetFlags(0,0);
if (usereg) { // store X only if count is not 0
ot(" cmp %s,#0 ;@ shifting by 0?\n",pct);
ot(" biceq r9,r9,#0x20000000 ;@ if so, clear carry\n");
ot(" movne r1,r9,lsr #28\n");
ot(" strneb r1,[r7,#0x45] ;@ else Save X bit\n");
} else {
// count will never be 0 if we use immediate
ot(" mov r1,r9,lsr #28\n");
ot(" strb r1,[r7,#0x45] ;@ Save X bit\n");
}
if (type==0 && dir) {
ot(";@ calculate V flag (set if sign bit changes at anytime):\n");
ot(" mov r1,#0x80000000\n");
ot(" ands r3,r3,r1,asr %s\n", pct);
ot(" cmpne r3,r1,asr %s\n", pct);
ot(" biceq r9,r9,#0x10000000\n");
ot(" orrne r9,r9,#0x10000000\n");
}
ot("\n");
}
// --------------------------------------
if (type==2)
{
int wide=8<<size;
// Roxr
if(count == 1) {
if(dir==0) {
if(size!=2) {
ot(" orr r0,r0,r0,lsr #%i\n", size?16:24);
ot(" bic r0,r0,#0x%x\n", 1<<(32-wide));
}
GetXBit(0);
ot(" movs r0,r0,rrx\n");
OpGetFlags(0,1);
} else {
ot(" ldrb r3,[r7,#0x45]\n");
ot(" movs r0,r0,lsl #1\n");
OpGetFlags(0,1);
ot(" tst r3,#2\n");
ot(" orrne r0,r0,#0x%x\n", 1<<(32-wide));
ot(" bicne r9,r9,#0x40000000 ;@ clear Z in case it got there\n");
}
ot(" bic r9,r9,#0x10000000 ;@ make suve V is clear\n");
return 0;
}
if (usereg)
{
ot(";@ Reduce r2 until <0:\n");
ot("Reduce_%.4x%s\n",op,ms?"":":");
ot(" subs r2,r2,#%d\n",wide+1);
ot(" bpl Reduce_%.4x\n",op);
ot(" adds r2,r2,#%d ;@ Now r2=0-%d\n",wide+1,wide);
ot(" beq norotx%.4x\n",op);
ot("\n");
}
if (usereg||count < 0)
{
if (dir) ot(" rsb r2,r2,#%d ;@ Reverse direction\n",wide+1);
}
else
{
if (dir) ot(" mov r2,#%d ;@ Reversed\n",wide+1-count);
else ot(" mov r2,#%d\n",count);
}
if (shift) ot(" mov r0,r0,lsr #%d ;@ Shift down\n",shift);
ot(";@ Rotate bits:\n");
ot(" mov r3,r0,lsr r2 ;@ Get right part\n");
ot(" rsbs r2,r2,#%d ;@ should also clear ARM V\n",wide+1);
ot(" movs r0,r0,lsl r2 ;@ Get left part\n");
ot(" orr r0,r3,r0 ;@ r0=Rotated value\n");
ot(";@ Insert X bit into r2-1:\n");
ot(" ldrb r3,[r7,#0x45]\n");
ot(" sub r2,r2,#1\n");
ot(" and r3,r3,#2\n");
ot(" mov r3,r3,lsr #1\n");
ot(" orr r0,r0,r3,lsl r2\n");
ot("\n");
if (shift) ot(" movs r0,r0,lsl #%d ;@ Shift up and get correct NC flags\n",shift);
OpGetFlags(0,!usereg);
if (!shift) {
ot(" tst r0,r0\n");
ot(" bicne r9,r9,#0x40000000 ;@ make sure we didn't mess Z\n");
}
if (usereg) { // store X only if count is not 0
ot(" mov r2,r9,lsr #28\n");
ot(" strb r2,[r7,#0x45] ;@ if not 0, Save X bit\n");
ot(" b nozerox%.4x\n",op);
ot("norotx%.4x%s\n",op,ms?"":":");
ot(" ldrb r2,[r7,#0x45]\n");
ot(" adds r0,r0,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot(" and r2,r2,#2\n");
ot(" orr r9,r9,r2,lsl #28 ;@ C = old_X\n");
ot("nozerox%.4x%s\n",op,ms?"":":");
}
ot("\n");
}
// --------------------------------------
if (type==3)
{
// Ror
if (size<2)
{
ot(";@ Mirror value in whole 32 bits:\n");
if (size<=0) ot(" orr r0,r0,r0,lsr #8\n");
if (size<=1) ot(" orr r0,r0,r0,lsr #16\n");
ot("\n");
}
ot(";@ Rotate register:\n");
if (count<0)
{
if (dir) ot(" rsbs %s,%s,#32\n",pct,pct);
ot(" movs r0,r0,ror %s\n",pct);
}
else
{
int ror=count;
if (dir) ror=32-ror;
if (ror&31) ot(" movs r0,r0,ror #%d\n",ror);
}
OpGetFlags(0,0);
if (!dir) ot(" bic r9,r9,#0x10000000 ;@ make suve V is clear\n");
if (dir)
{
ot(";@ Get carry bit from bit 0:\n");
if (usereg)
{
ot(" cmp %s,#32 ;@ rotating by 0?\n",pct);
ot(" tstne r0,#1 ;@ no, check bit 0\n");
}
else
ot(" tst r0,#1\n");
ot(" orrne r9,r9,#0x20000000\n");
ot(" biceq r9,r9,#0x20000000\n");
}
else if (usereg)
{
// if we rotate something by 0, ARM doesn't clear C
// so we need to detect that
ot(" cmp %s,#0\n",pct);
ot(" biceq r9,r9,#0x20000000\n");
}
ot("\n");
}
// --------------------------------------
return 0;
}
// Emit a Asr/Lsr/Roxr/Ror opcode - 1110cccd xxuttnnn
// (ccc=count, d=direction(r,l) xx=size extension, u=use reg for count, tt=type, nnn=register Dn)
int OpAsr(int op)
{
int ea=0,use=0;
int count=0,dir=0;
int size=0,usereg=0,type=0;
ea=0;
count =(op>>9)&7;
dir =(op>>8)&1;
size =(op>>6)&3;
if (size>=3) return 1; // use OpAsrEa()
usereg=(op>>5)&1;
type =(op>>3)&3;
if (usereg==0) count=((count-1)&7)+1; // because ccc=000 means 8
// Use the same opcode for target registers:
use=op&~0x0007;
// As long as count is not 8, use the same opcode for all shift counts::
if (usereg==0 && count!=8 && !(count==1&&type==2)) { use|=0x0e00; count=-1; }
if (usereg) { use&=~0x0e00; count=-1; } // Use same opcode for all Dn
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=size<2?6:8;
EaCalc(10,0x0007, ea,size,1);
EaRead(10, 0, ea,size,0x0007,1);
EmitAsr(op,type,dir,count, size,usereg);
EaWrite(10, 0, ea,size,0x0007,1);
OpEnd();
return 0;
}
// Asr/Lsr/Roxr/Ror etc EA - 11100ttd 11eeeeee
int OpAsrEa(int op)
{
int use=0,type=0,dir=0,ea=0,size=1;
type=(op>>9)&3;
dir =(op>>8)&1;
ea = op&0x3f;
if (ea<0x10) return 1;
// See if we can do this opcode:
if (EaCanRead(ea,0)==0) return 1;
if (EaCanWrite(ea)==0) return 1;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=6; // EmitAsr() will add 2
EaCalc (10,0x003f,ea,size,1);
EaRead (10, 0,ea,size,0x003f,1);
EmitAsr(op,type,dir,1,size,0);
EaWrite(10, 0,ea,size,0x003f,1);
OpEnd();
return 0;
}
int OpTas(int op)
{
int ea=0;
int use=0;
ea=op&0x003f;
// See if we can do this opcode:
if (EaCanWrite(ea)==0 || EaAn(ea)) return 1;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
if(ea>=8) Cycles+=10;
EaCalc (10,0x003f,ea,0,1);
EaRead (10, 1,ea,0,0x003f,1);
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
OpGetFlags(0,0);
ot("\n");
#if CYCLONE_FOR_GENESIS
// the original Sega hardware ignores write-back phase (to memory only)
if (ea < 0x10) {
#endif
ot(" orr r1,r1,#0x80000000 ;@ set bit7\n");
EaWrite(10, 1,ea,0,0x003f,1);
#if CYCLONE_FOR_GENESIS
}
#endif
OpEnd();
return 0;
}

616
cpu/Cyclone/OpMove.cpp Normal file
View file

@ -0,0 +1,616 @@
#include "app.h"
// Pack our flags into r1, in SR/CCR register format
// trashes r0,r2
void OpFlagsToReg(int high)
{
ot(" ldrb r0,[r7,#0x45] ;@ X bit\n");
ot(" mov r1,r9,lsr #28 ;@ ____NZCV\n");
ot(" eor r2,r1,r1,ror #1 ;@ Bit 0=C^V\n");
ot(" tst r2,#1 ;@ 1 if C!=V\n");
ot(" eorne r1,r1,#3 ;@ ____NZVC\n");
ot("\n");
if (high) ot(" ldrb r2,[r7,#0x44] ;@ Include SR high\n");
ot(" and r0,r0,#0x02\n");
ot(" orr r1,r1,r0,lsl #3 ;@ ___XNZVC\n");
if (high) ot(" orr r1,r1,r2,lsl #8\n");
ot("\n");
}
// Convert SR/CRR register in r0 to our flags
// trashes r0,r1
void OpRegToFlags(int high)
{
ot(" eor r1,r0,r0,ror #1 ;@ Bit 0=C^V\n");
ot(" mov r2,r0,lsr #3 ;@ r2=___XN\n");
ot(" tst r1,#1 ;@ 1 if C!=V\n");
ot(" eorne r0,r0,#3 ;@ ___XNZCV\n");
ot(" strb r2,[r7,#0x45] ;@ Store X bit\n");
ot(" mov r9,r0,lsl #28 ;@ r9=NZCV...\n");
if (high)
{
ot(" mov r0,r0,ror #8\n");
ot(" and r0,r0,#0xa7 ;@ only take defined bits\n");
ot(" strb r0,[r7,#0x44] ;@ Store SR high\n");
}
ot("\n");
}
// checks for supervisor bit, if not set, jumps to SuperEnd()
// also sets r11 to SR high value, SuperChange() uses this
void SuperCheck(int op)
{
ot(" ldr r11,[r7,#0x44] ;@ Get SR high\n");
ot(" tst r11,#0x20 ;@ Check we are in supervisor mode\n");
ot(" beq WrongMode%.4x ;@ No\n",op);
ot("\n");
}
void SuperEnd(int op)
{
ot("WrongMode%.4x%s\n",op,ms?"":":");
ot(" sub r4,r4,#2 ;@ this opcode wasn't executed - go back\n");
ot(" mov r0,#0x20 ;@ privilege violation\n");
ot(" bl Exception\n");
Cycles=34;
OpEnd();
}
// does OSP and A7 swapping if needed
// new or old SR (not the one already in [r7,#0x44]) should be passed in r11
// trashes r1,r11
void SuperChange(int op)
{
ot(";@ A7 <-> OSP?\n");
ot(" ldr r1,[r7,#0x44] ;@ Get other SR high\n");
ot(" and r11,r11,#0x20\n");
ot(" and r1,r1,#0x20\n");
ot(" teq r11,r1 ;@ r11 xor r1\n");
ot(" beq no_sp_swap%.4x\n",op);
ot(" ;@ swap OSP and A7:\n");
ot(" ldr r11,[r7,#0x3C] ;@ Get A7\n");
ot(" ldr r1, [r7,#0x48] ;@ Get OSP\n");
ot(" str r11,[r7,#0x48]\n");
ot(" str r1, [r7,#0x3C]\n");
ot("no_sp_swap%.4x%s\n", op, ms?"":":");
}
// --------------------- Opcodes 0x1000+ ---------------------
// Emit a Move opcode, 00xxdddd ddssssss
int OpMove(int op)
{
int sea=0,tea=0;
int size=0,use=0;
int movea=0;
// Get source and target EA
sea = op&0x003f;
tea =(op&0x01c0)>>3;
tea|=(op&0x0e00)>>9;
if (tea>=8 && tea<0x10) movea=1;
// Find size extension
switch (op&0x3000)
{
default: return 1;
case 0x1000: size=0; break;
case 0x3000: size=1; break;
case 0x2000: size=2; break;
}
if (size<1 && (movea || EaAn(sea))) return 1; // move.b An,* and movea.b * are invalid
// See if we can do this opcode:
if (EaCanRead (sea,size)==0) return 1;
if (EaCanWrite(tea )==0) return 1;
use=OpBase(op);
if (tea<0x38) use&=~0x0e00; // Use same handler for register ?0-7
if (tea>=0x18 && tea<0x28 && (tea&7)==7) use|=0x0e00; // Specific handler for (a7)+ and -(a7)
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
EaCalc(0,0x003f,sea,size);
EaRead(0, 1,sea,size,0x003f);
if (movea==0) {
ot(" adds r1,r1,#0 ;@ Defines NZ, clears CV\n");
ot(" mrs r9,cpsr ;@ r9=NZCV flags\n");
ot("\n");
}
if (movea) size=2; // movea always expands to 32-bits
EaCalc (0,0x0e00,tea,size);
#if SPLIT_MOVEL_PD
if ((tea&0x38)==0x20 && size==2) { // -(An)
ot(" mov r10,r0\n");
ot(" mov r11,r1\n");
ot(" add r0,r0,#2\n");
EaWrite(0, 1,tea,1,0x0e00);
EaWrite(10, 11,tea,1,0x0e00,1);
} else {
EaWrite(0, 1,tea,size,0x0e00);
}
#else
EaWrite(0, 1,tea,size,0x0e00);
#endif
#if CYCLONE_FOR_GENESIS && !MEMHANDLERS_CHANGE_CYCLES
// this is a bit hacky
if ((tea==0x39||(tea&0x38)==0x10)&&size>=1)
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
#endif
if((tea&0x38)==0x20) Cycles-=2; // less cycles when dest is -(An)
OpEnd();
return 0;
}
// --------------------- Opcodes 0x41c0+ ---------------------
// Emit an Lea opcode, 0100nnn1 11aaaaaa
int OpLea(int op)
{
int use=0;
int sea=0,tea=0;
sea= op&0x003f;
tea=(op&0x0e00)>>9; tea|=8;
if (EaCanRead(sea,-1)==0) return 1; // See if we can do this opcode
use=OpBase(op);
use&=~0x0e00; // Also use 1 handler for target ?0-7
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
EaCalc (1,0x003f,sea,0); // Lea
EaCalc (0,0x0e00,tea,2,1);
EaWrite(0, 1,tea,2,0x0e00,1);
Cycles=Ea_add_ns(g_lea_cycle_table,sea);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x40c0+ ---------------------
// Move SR opcode, 01000tt0 11aaaaaa move SR
int OpMoveSr(int op)
{
int type=0,ea=0;
int use=0,size=1;
type=(op>>9)&3; // from SR, from CCR, to CCR, to SR
ea=op&0x3f;
if(EaAn(ea)) return 1; // can't use An regs
switch(type)
{
case 0:
if (EaCanWrite(ea)==0) return 1; // See if we can do this opcode:
break;
case 1:
return 1; // no such op in 68000
case 2: case 3:
if (EaCanRead(ea,size)==0) return 1; // See if we can do this opcode:
break;
}
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
Cycles=12;
if (type==0) Cycles=(ea>=8)?8:6;
if (type==3) SuperCheck(op); // 68000 model allows reading whole SR in user mode (but newer models don't)
if (type==0 || type==1)
{
OpFlagsToReg(type==0);
EaCalc (0,0x003f,ea,size);
EaWrite(0, 1,ea,size,0x003f);
}
if (type==2 || type==3)
{
EaCalc(0,0x003f,ea,size);
EaRead(0, 0,ea,size,0x003f);
OpRegToFlags(type==3);
if (type==3) {
SuperChange(op);
CheckInterrupt(op);
}
}
OpEnd();
if (type==3) SuperEnd(op);
return 0;
}
// Ori/Andi/Eori $nnnn,sr 0000t0t0 01111100
int OpArithSr(int op)
{
int type=0,ea=0;
int use=0,size=0;
type=(op>>9)&5; if (type==4) return 1;
size=(op>>6)&1; // ccr or sr?
ea=0x3c;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=16;
if (size) SuperCheck(op);
EaCalc(0,0x003f,ea,size);
EaRead(0, 10,ea,size,0x003f);
OpFlagsToReg(size);
if (type==0) ot(" orr r0,r1,r10\n");
if (type==1) ot(" and r0,r1,r10\n");
if (type==5) ot(" eor r0,r1,r10\n");
OpRegToFlags(size);
if (size) {
SuperChange(op);
CheckInterrupt(op);
}
OpEnd();
if (size) SuperEnd(op);
return 0;
}
// --------------------- Opcodes 0x4850+ ---------------------
// Emit an Pea opcode, 01001000 01aaaaaa
int OpPea(int op)
{
int use=0;
int ea=0;
ea=op&0x003f; if (ea<0x10) return 1; // Swap opcode
if (EaCanRead(ea,-1)==0) return 1; // See if we can do this opcode:
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
ot(" ldr r10,[r7,#0x3c]\n");
EaCalc (1,0x003f, ea,0);
ot("\n");
ot(" sub r0,r10,#4 ;@ Predecrement A7\n");
ot(" str r0,[r7,#0x3c] ;@ Save A7\n");
ot("\n");
MemHandler(1,2); // Write 32-bit
ot("\n");
Cycles=6+Ea_add_ns(g_pea_cycle_table,ea);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x4880+ ---------------------
// Emit a Movem opcode, 01001d00 1xeeeeee regmask
int OpMovem(int op)
{
int size=0,ea=0,cea=0,dir=0;
int use=0,decr=0,change=0;
size=((op>>6)&1)+1; // word, long
ea=op&0x003f;
dir=(op>>10)&1; // Direction (1==ea2reg)
if (dir) {
if (ea<0x10 || ea>0x3b || (ea&0x38)==0x20) return 1; // Invalid EA
} else {
if (ea<0x10 || ea>0x39 || (ea&0x38)==0x18) return 1;
}
if ((ea&0x38)==0x18 || (ea&0x38)==0x20) change=1;
if ((ea&0x38)==0x20) decr=1; // -(An), bitfield is decr
cea=ea; if (change) cea=0x10;
use=OpBase(op);
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op);
ot(" stmdb sp!,{r9} ;@ Push r9\n"); // can't just use r12 or lr here, because memhandlers touch them
ot(" ldrh r11,[r4],#2 ;@ r11=register mask\n");
ot("\n");
ot(";@ Get the address into r9:\n");
EaCalc(9,0x003f,cea,size);
ot(";@ r10=Register Index*4:\n");
if (decr) ot(" mov r10,#0x3c ;@ order reversed for -(An)\n");
else ot(" mov r10,#0\n");
ot("\n");
ot("MoreReg%.4x%s\n",op, ms?"":":");
ot(" tst r11,#1\n");
ot(" beq SkipReg%.4x\n",op);
ot("\n");
if (decr) ot(" sub r9,r9,#%d ;@ Pre-decrement address\n",1<<size);
if (dir)
{
ot(" ;@ Copy memory to register:\n",1<<size);
EaRead (9,0,ea,size,0x003f);
ot(" str r0,[r7,r10] ;@ Save value into Dn/An\n");
}
else
{
ot(" ;@ Copy register to memory:\n",1<<size);
ot(" ldr r1,[r7,r10] ;@ Load value from Dn/An\n");
EaWrite(9,1,ea,size,0x003f);
}
if (decr==0) ot(" add r9,r9,#%d ;@ Post-increment address\n",1<<size);
ot(" sub r5,r5,#%d ;@ Take some cycles\n",2<<size);
ot("\n");
ot("SkipReg%.4x%s\n",op, ms?"":":");
ot(" movs r11,r11,lsr #1;@ Shift mask:\n");
ot(" add r10,r10,#%d ;@ r10=Next Register\n",decr?-4:4);
ot(" bne MoreReg%.4x\n",op);
ot("\n");
if (change)
{
ot(";@ Write back address:\n");
EaCalc (0,0x0007,8|(ea&7),2);
EaWrite(0, 9,8|(ea&7),2,0x0007);
}
ot(" ldmia sp!,{r9} ;@ Pop r9\n");
ot("\n");
if(dir) { // er
if (ea==0x3a) Cycles=16; // ($nn,PC)
else if (ea==0x3b) Cycles=18; // ($nn,pc,Rn)
else Cycles=12;
} else {
Cycles=8;
}
Cycles+=Ea_add_ns(g_movem_cycle_table,ea);
OpEnd();
return 0;
}
// --------------------- Opcodes 0x4e60+ ---------------------
// Emit a Move USP opcode, 01001110 0110dnnn move An to/from USP
int OpMoveUsp(int op)
{
int use=0,dir=0;
dir=(op>>3)&1; // Direction
use=op&~0x0007; // Use same opcode for all An
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
SuperCheck(op);
if (dir)
{
ot(" ldr r1,[r7,#0x48] ;@ Get from USP\n\n");
EaCalc (0,0x0007,8,2,1);
EaWrite(0, 1,8,2,0x0007,1);
}
else
{
EaCalc (0,0x0007,8,2,1);
EaRead (0, 0,8,2,0x0007,1);
ot(" str r0,[r7,#0x48] ;@ Put in USP\n\n");
}
OpEnd();
SuperEnd(op);
return 0;
}
// --------------------- Opcodes 0x7000+ ---------------------
// Emit a Move Quick opcode, 0111nnn0 dddddddd moveq #dd,Dn
int OpMoveq(int op)
{
int use=0;
use=op&0xf100; // Use same opcode for all values
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=4;
ot(" movs r0,r8,asl #24\n");
ot(" and r1,r8,#0x0e00\n");
ot(" mov r0,r0,asr #24 ;@ Sign extended Quick value\n");
ot(" mrs r9,cpsr ;@ r9=NZ flags\n");
ot(" str r0,[r7,r1,lsr #7] ;@ Store into Dn\n");
ot("\n");
OpEnd();
return 0;
}
// --------------------- Opcodes 0xc140+ ---------------------
// Emit a Exchange opcode:
// 1100ttt1 01000sss exg ds,dt
// 1100ttt1 01001sss exg as,at
// 1100ttt1 10001sss exg as,dt
int OpExg(int op)
{
int use=0,type=0;
type=op&0xf8;
if (type!=0x40 && type!=0x48 && type!=0x88) return 1; // Not an exg opcode
use=op&0xf1f8; // Use same opcode for all values
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler
OpStart(op); Cycles=6;
ot(" and r10,r8,#0x0e00 ;@ Find T register\n");
ot(" and r11,r8,#0x000f ;@ Find S register\n");
if (type==0x48) ot(" orr r10,r10,#0x1000 ;@ T is an address register\n");
ot("\n");
ot(" ldr r0,[r7,r10,lsr #7] ;@ Get T\n");
ot(" ldr r1,[r7,r11,lsl #2] ;@ Get S\n");
ot("\n");
ot(" str r0,[r7,r11,lsl #2] ;@ T->S\n");
ot(" str r1,[r7,r10,lsr #7] ;@ S->T\n");
ot("\n");
OpEnd();
return 0;
}
// ------------------------- movep -------------------------------
// 0000ddd1 0z001sss
// 0000sss1 1z001ddd (to mem)
int OpMovep(int op)
{
int ea=0;
int size=1,use=0,dir;
use=op&0xf1f8;
if (op!=use) { OpUse(op,use); return 0; } // Use existing handler (for all dests, srcs)
// Get EA
ea = (op&0x0007)|0x28;
dir = (op>>7)&1;
// Find size extension
if(op&0x0040) size=2;
OpStart(op);
if(dir) { // reg to mem
EaCalc(11,0x0e00,0,size); // reg number -> r11
EaRead(11,11,0,size,0x0e00); // regval -> r11
EaCalc(10,0x0007,ea,size);
if(size==2) { // if operand is long
ot(" mov r1,r11,lsr #24 ;@ first byte\n");
EaWrite(10,1,ea,0,0x0007); // store first byte
ot(" add r10,r10,#2\n");
ot(" mov r1,r11,lsr #16 ;@ second byte\n");
EaWrite(10,1,ea,0,0x0007); // store second byte
ot(" add r10,r10,#2\n");
}
ot(" mov r1,r11,lsr #8 ;@ first or third byte\n");
EaWrite(10,1,ea,0,0x0007);
ot(" add r10,r10,#2\n");
ot(" and r1,r11,#0xff\n");
EaWrite(10,1,ea,0,0x0007);
} else { // mem to reg
EaCalc(10,0x0007,ea,size,1);
EaRead(10,11,ea,0,0x0007,1); // read first byte
ot(" add r10,r10,#2\n");
EaRead(10,1,ea,0,0x0007,1); // read second byte
if(size==2) { // if operand is long
ot(" orr r11,r11,r1,lsr #8 ;@ second byte\n");
ot(" add r10,r10,#2\n");
EaRead(10,1,ea,0,0x0007,1);
ot(" orr r11,r11,r1,lsr #16 ;@ third byte\n");
ot(" add r10,r10,#2\n");
EaRead(10,1,ea,0,0x0007,1);
ot(" orr r0,r11,r1,lsr #24 ;@ fourth byte\n");
} else {
ot(" orr r0,r11,r1,lsr #8 ;@ second byte\n");
}
// store the result
EaCalc(11,0x0e00,0,size,1); // reg number -> r11
EaWrite(11,0,0,size,0x0e00,1);
}
Cycles=(size==2)?24:16;
OpEnd();
return 0;
}
// Emit a Stop/Reset opcodes, 01001110 011100t0 imm
int OpStopReset(int op)
{
int type=(op>>1)&1; // reset/stop
OpStart(op);
SuperCheck(op);
if(type) {
// copy immediate to SR, stop the CPU and eat all remaining cycles.
ot(" ldrh r0,[r4],#2 ;@ Fetch the immediate\n");
SuperChange(op);
OpRegToFlags(1);
ot("\n");
ot(" mov r0,#1\n");
ot(" str r0,[r7,#0x58] ;@ stopped\n");
ot("\n");
ot(" mov r5,#0 ;@ eat cycles\n");
Cycles = 4;
ot("\n");
}
else
{
Cycles = 132;
#if USE_RESET_CALLBACK
ot(" str r4,[r7,#0x40] ;@ Save PC\n");
ot(" mov r1,r9,lsr #28\n");
ot(" strb r1,[r7,#0x46] ;@ Save Flags (NZCV)\n");
ot(" str r5,[r7,#0x5c] ;@ Save Cycles\n");
ot(" ldr r11,[r7,#0x90] ;@ ResetCallback\n");
ot(" tst r11,r11\n");
ot(" movne lr,pc\n");
ot(" movne pc,r11 ;@ call ResetCallback if it is defined\n");
ot(" ldrb r9,[r7,#0x46] ;@ r9 = Load Flags (NZCV)\n");
ot(" ldr r5,[r7,#0x5c] ;@ Load Cycles\n");
ot(" ldr r4,[r7,#0x40] ;@ Load PC\n");
ot(" mov r9,r9,lsl #28\n");
#endif
}
OpEnd();
SuperEnd(op);
return 0;
}

100
cpu/Cyclone/app.h Normal file
View file

@ -0,0 +1,100 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
// Disa.c
#include "Disa/Disa.h"
// Ea.cpp
extern int g_jmp_cycle_table[];
extern int g_jsr_cycle_table[];
extern int g_lea_cycle_table[];
extern int g_pea_cycle_table[];
extern int g_movem_cycle_table[];
int Ea_add_ns(int *tab, int ea); // add nonstandard EA cycles
int EaCalc(int a,int mask,int ea,int size,int top=0);
int EaRead(int a,int v,int ea,int size,int mask,int top=0);
int EaCanRead(int ea,int size);
int EaWrite(int a,int v,int ea,int size,int mask,int top=0);
int EaCanWrite(int ea);
int EaAn(int ea);
// Main.cpp
extern int *CyJump; // Jump table
extern int ms; // If non-zero, output in Microsoft ARMASM format
extern char *Narm[4]; // Normal ARM Extensions for operand sizes 0,1,2
extern char *Sarm[4]; // Sign-extend ARM Extensions for operand sizes 0,1,2
extern int Cycles; // Current cycles for opcode
void ot(const char *format, ...);
void ltorg();
void CheckInterrupt(int op);
int MemHandler(int type,int size);
// OpAny.cpp
int OpGetFlags(int subtract,int xbit,int sprecialz=0);
void OpUse(int op,int use);
void OpStart(int op);
void OpEnd();
int OpBase(int op,int sepa=0);
void OpAny(int op);
//----------------------
// OpArith.cpp
int OpArith(int op);
int OpLea(int op);
int OpAddq(int op);
int OpArithReg(int op);
int OpMul(int op);
int OpAbcd(int op);
int OpNbcd(int op);
int OpAritha(int op);
int OpAddx(int op);
int OpCmpEor(int op);
int OpCmpm(int op);
int OpChk(int op);
int GetXBit(int subtract);
// OpBranch.cpp
void OpPush32();
void OpPushSr(int high);
int OpTrap(int op);
int OpLink(int op);
int OpUnlk(int op);
int Op4E70(int op);
int OpJsr(int op);
int OpBranch(int op);
int OpDbra(int op);
// OpLogic.cpp
int OpBtstReg(int op);
int OpBtstImm(int op);
int OpNeg(int op);
int OpSwap(int op);
int OpTst(int op);
int OpExt(int op);
int OpSet(int op);
int OpAsr(int op);
int OpAsrEa(int op);
int OpTas(int op);
// OpMove.cpp
int OpMove(int op);
int OpLea(int op);
void OpFlagsToReg(int high);
void OpRegToFlags(int high);
int OpMoveSr(int op);
int OpArithSr(int op);
int OpPea(int op);
int OpMovem(int op);
int OpMoveq(int op);
int OpMoveUsp(int op);
int OpExg(int op);
int OpMovep(int op); // notaz
int OpStopReset(int op);
void SuperCheck(int op);
void SuperEnd(int op);
void SuperChange(int op);

101
cpu/Cyclone/config.h Normal file
View file

@ -0,0 +1,101 @@
/**
* Cyclone 68000 configuration file
**/
/*
* If this option is enabled, Microsoft ARMASM compatible output is generated.
* Otherwise GNU as syntax is used.
*/
#define USE_MS_SYNTAX 0
/*
* Enable this option if you are going to use Cyclone to emulate Genesis /
* Mega Drive system. As VDP chip in these systems had control of the bus,
* several instructions were acting differently, for example TAS did'n have
* the write-back phase. That will be emulated, if this option is enebled.
* This option also alters timing slightly.
*/
#define CYCLONE_FOR_GENESIS 1
/*
* This option compresses Cyclone's jumptable. Because of this the executable
* will be smaller and load slightly faster and less relocations will be needed.
* This also fixes the crash problem with 0xfffe and 0xffff opcodes.
* Warning: if you enable this, you MUST call CycloneInit() before calling
* CycloneRun(), or else it will crash.
*/
#define COMPRESS_JUMPTABLE 1
/*
* Cyclone keeps the 4 least significant bits of SR, PC+membase and it's cycle
* count in ARM registers instead of the context for performance reasons. If you for
* any reason need to access them in your memory handlers, enable the options below,
* otherwise disable them to improve performance.
* Warning: the PC value will not point to start of instruction (it will be middle or
* end), also updating PC is dangerous, as Cyclone may internally increment the PC
* before fetching the next instruction and continue executing at wrong location.
*/
#define MEMHANDLERS_NEED_PC 0
#define MEMHANDLERS_NEED_FLAGS 0
#define MEMHANDLERS_NEED_CYCLES 1
#define MEMHANDLERS_CHANGE_PC 0
#define MEMHANDLERS_CHANGE_FLAGS 0
#define MEMHANDLERS_CHANGE_CYCLES 0
/*
* If enabled, Cyclone will call IrqCallback routine from it's context whenever it
* acknowledges an IRQ. IRQ level is not cleared automatically, do this in your
* hadler if needed. PC, flags and cycles are valid in the context and can be read.
* If disabled, it simply clears the IRQ level and continues execution.
*/
#define USE_INT_ACK_CALLBACK 1
/*
* Enable this if you need/change PC, flags or cycles in your IrqCallback function.
*/
#define INT_ACK_NEEDS_STUFF 0
#define INT_ACK_CHANGES_STUFF 0
/*
* If enabled, ResetCallback is called from the context, whenever RESET opcode is
* encountered. All context members are valid and can be changed.
* If disabled, RESET opcode acts as an NOP.
*/
#define USE_RESET_CALLBACK 1
/*
* If enabled, UnrecognizedCallback is called if an invalid opcode is
* encountered. All context members are valid and can be changed. The handler
* should return zero if you want Cyclone to gererate "Illegal Instruction"
* exception after this, or nonzero if not. In the later case you shuold change
* the PC by yourself, or else Cyclone will keep executing that opcode all over
* again.
* If disabled, "Illegal Instruction" exception is generated and execution is
* continued.
*/
#define USE_UNRECOGNIZED_CALLBACK 1
/*
* This option will also call UnrecognizedCallback for a-line and f-line
* (0xa*** and 0xf***) opcodes the same way as described above, only appropriate
* exceptions will be generated.
*/
#define USE_AFLINE_CALLBACK 1
/*
* This makes Cyclone to call checkpc from it's context whenever it changes the PC
* by a large value. It takes and should return the PC value in PC+membase form.
* The flags and cycle counter are not valid in this function.
*/
#define USE_CHECKPC_CALLBACK 1
/*
* When this option is enabled Cyclone will do two word writes instead of one
* long write when handling MOVE.L with pre-decrementing destination, as described in
* Bart Trzynadlowski's doc (http://www.trzy.org/files/68knotes.txt).
* Enable this if you are emulating a 16 bit system.
*/
#define SPLIT_MOVEL_PD 1

Binary file not shown.

View file

@ -0,0 +1,150 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned long _dontcare1[4];
char signature[4]; // 'EPOC'
unsigned long iCpu; // 0x1000 = X86, 0x2000 = ARM, 0x4000 = M*Core
unsigned long iCheckSumCode; // sum of all 32 bit words in .text
unsigned long _dontcare3[5];
unsigned long iCodeSize; // size of code, import address table, constant data and export dir |+30
unsigned long _dontcare4[12];
unsigned long iCodeOffset; // file offset to code section |+64
unsigned long _dontcare5[2];
unsigned long iCodeRelocOffset; // relocations for code and const |+70
unsigned long iDataRelocOffset; // relocations for data
unsigned long iPriority; // priority of this process (EPriorityHigh=450)
} E32ImageHeader;
typedef struct {
unsigned long iSize; // size of this relocation section
unsigned long iNumberOfRelocs; // number of relocations in this section
} E32RelocSection;
typedef struct {
unsigned long base;
unsigned long size;
} reloc_page_header;
// E32Image relocation section consists of a number of pages
// every page has 8 byte header and a number or 16bit relocation entries
// entry format:
// 0x3000 | <12bit_reloc_offset>
//
// if we have page_header.base == 0x1000 and a reloc entry 0x3110,
// it means that 32bit value at offset 0x1110 of .text section
// is relocatable
int main(int argc, char *argv[])
{
FILE *f = 0;
unsigned char pattern[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56 };
unsigned char *buff, *p;
unsigned long patt_offset; // pattern offset in .text section
unsigned long size = 0, i, symbols, insert_pos, *handler;
unsigned short reloc_entry;
E32ImageHeader *header;
E32RelocSection *reloc_section;
reloc_page_header *reloc_page;
if(argc != 3) {
printf("usage: %s <e32_exe> <nsymbols>\n\n", argv[0]);
printf("note: this was written to fix a problem caused by as v.2.9-psion-98r2 and shouldn't be used for anything else.\n", argv[0]);
return 1;
}
f = fopen(argv[1], "rb+");
if(!f) {
printf("%s: couldn't open %s\n", argv[0], argv[1]);
return 2;
}
symbols = atoi(argv[2]);
// read the file
fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET);
buff = (unsigned char *) malloc(size);
fread(buff,1,size,f);
header = (E32ImageHeader *) buff;
if(strncmp(header->signature, "EPOC", 4) || header->iCpu != 0x2000) {
printf("%s: not a E32 executable image for ARM target.\n", argv[0]);
fclose(f);
free(buff);
return 2;
}
// find the pattern
for(i = 0; i < size-8; i++)
if(memcmp(buff+i, pattern, 8) == 0) break;
if(i == size-8 || i < 4) {
printf("%s: failed to find the pattern.\n", argv[0]);
fclose(f);
free(buff);
return 3;
}
patt_offset = i - header->iCodeOffset;
// find suitable reloc section
reloc_section = (E32RelocSection *) (buff + header->iCodeRelocOffset);
for(i = 0, p = buff+header->iCodeRelocOffset+8; i < reloc_section->iSize; ) {
reloc_page = (reloc_page_header *) p;
if(patt_offset - reloc_page->base >= 0 && patt_offset - reloc_page->base < 0x1000 - symbols*4) break;
i += reloc_page->size;
p += reloc_page->size;
}
if(i >= reloc_section->iSize) {
printf("%s: suitable reloc section not found.\n", argv[0]);
fclose(f);
free(buff);
return 4;
}
// now find the insert pos and update everything
insert_pos = p + reloc_page->size - buff;
reloc_page->size += symbols*2;
reloc_section->iSize += symbols*2;
reloc_section->iNumberOfRelocs += symbols;
header->iDataRelocOffset += symbols*2; // data reloc section is now also pushed a little
header->iPriority = 450; // let's boost our priority :)
// replace the placeholders themselves
handler = (unsigned long *) (buff + patt_offset + header->iCodeOffset - 4);
for(i = 1; i <= symbols; i++)
*(handler+i) = *handler;
// recalculate checksum
header->iCheckSumCode = 0;
for(i = 0, p = buff+header->iCodeOffset; i < header->iCodeSize; i+=4, p+=4)
header->iCheckSumCode += *(unsigned long *) p;
// check for possible padding
if(!*(buff+insert_pos-1)) insert_pos -= 2;
// write all this joy
fseek(f,0,SEEK_SET);
fwrite(buff, 1, insert_pos, f);
// write new reloc entries
for(i = 0; i < symbols; i++) {
handler++;
reloc_entry = ((unsigned char *) handler - buff - reloc_page->base - header->iCodeOffset) | 0x3000;
fwrite(&reloc_entry, 1, 2, f);
}
// write the remaining data
fwrite(buff+insert_pos, 1, size-insert_pos, f);
// done at last!
fclose(f);
free(buff);
return 0;
}

View file

@ -0,0 +1,133 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#define symbols 2
int main(int argc, char *argv[])
{
FILE *f = 0;
unsigned char pattern[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56 };
unsigned char *buff, *p;
unsigned long patt_offset; // pattern offset in .text section
unsigned long size = 0, i, insert_pos, *handler;//, symbols;
unsigned short reloc_entry;
IMAGE_BASE_RELOCATION *reloc_page;
IMAGE_DOS_HEADER *dos_header;
IMAGE_FILE_HEADER *file_header;
IMAGE_SECTION_HEADER *sect_header, *relocsect_header = 0, *codesect_header = 0;
if(argc != 2) {
printf("usage: %s <pe_exe_or_app_before_petran>\n\n", argv[0]);
printf("note: this was written to fix a problem related to Cyclone and as v.2.9-psion-98r2 and shouldn't be used for anything else. See Readme.\n", argv[0]);
return 1;
}
f = fopen(argv[1], "rb+");
if(!f) {
printf("%s: couldn't open %s\n", argv[0], argv[1]);
return 2;
}
//symbols = atoi(argv[2]);
// read the file
fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET);
buff = (unsigned char *) malloc(size);
fread(buff,1,size,f);
dos_header = (IMAGE_DOS_HEADER *) buff;
file_header= (IMAGE_FILE_HEADER *) (buff+dos_header->e_lfanew+4);
sect_header= (IMAGE_SECTION_HEADER *) (buff+dos_header->e_lfanew+4+sizeof(IMAGE_FILE_HEADER)+sizeof(IMAGE_OPTIONAL_HEADER32));
if(size < 0x500 || dos_header->e_magic != IMAGE_DOS_SIGNATURE ||
*(DWORD *)(buff+dos_header->e_lfanew) != IMAGE_NT_SIGNATURE || file_header->Machine != 0x0A00) {
printf("%s: not a PE executable image for ARM target.\n", argv[0]);
fclose(f);
free(buff);
return 2;
}
// scan all sections for data and reloc sections
for(i = 0; i < file_header->NumberOfSections; i++, sect_header++) {
if(strncmp(sect_header->Name, ".text", 5) == 0) codesect_header = sect_header;
else if(strncmp(sect_header->Name, ".reloc", 6) == 0) relocsect_header = sect_header;
}
if(!codesect_header || !relocsect_header) {
printf("%s: failed to find reloc and/or data section.\n", argv[0]);
fclose(f);
free(buff);
return 3;
}
if(relocsect_header != sect_header-1) {
printf("%s: bug: reloc section is not last, this is unexpected and not supported.\n", argv[0]);
fclose(f);
free(buff);
return 4;
}
// find the pattern
for(i = codesect_header->PointerToRawData; i < size-8; i+=2)
if(memcmp(buff+i, pattern, 8) == 0) break;
if(i == size-8 || i < 4) {
printf("%s: failed to find the pattern.\n", argv[0]);
fclose(f);
free(buff);
return 5;
}
// calculate pattern offset in RVA (relative virtual address)
patt_offset = i - codesect_header->PointerToRawData + codesect_header->VirtualAddress;
// replace the placeholders themselves
handler = (unsigned long *) (buff + i - 4);
for(i = 1; i <= symbols; i++)
*(handler+i) = *handler;
// find suitable reloc section
for(i = 0, p = buff+relocsect_header->PointerToRawData; i < relocsect_header->SizeOfRawData; ) {
reloc_page = (IMAGE_BASE_RELOCATION *) p;
if(patt_offset - reloc_page->VirtualAddress >= 0 && patt_offset - reloc_page->VirtualAddress < 0x1000 - symbols*2) break;
i += reloc_page->SizeOfBlock;
p += reloc_page->SizeOfBlock;
}
if(i >= relocsect_header->SizeOfRawData) {
printf("%s: suitable reloc section not found.\n", argv[0]);
fclose(f);
free(buff);
return 6;
}
// now find the insert pos and update everything
insert_pos = p + reloc_page->SizeOfBlock - buff;
reloc_page->SizeOfBlock += symbols*2;
relocsect_header->SizeOfRawData += symbols*2;
// check for possible padding
if(!*(buff+insert_pos-1)) insert_pos -= 2;
// write all this joy
fseek(f,0,SEEK_SET);
fwrite(buff, 1, insert_pos, f);
// write new reloc entries
for(i = 0; i < symbols; i++) {
handler++;
reloc_entry = (unsigned short)(((unsigned char *) handler - buff) - reloc_page->VirtualAddress - codesect_header->PointerToRawData + codesect_header->VirtualAddress) | 0x3000; // quite nasty
fwrite(&reloc_entry, 1, 2, f);
}
// write the remaining data
fwrite(buff+insert_pos, 1, size-insert_pos, f);
// done at last!
fclose(f);
free(buff);
return 0;
}

View file

@ -0,0 +1,42 @@
*update*
Use the "compress jumtable" Cyclone config.h option to fix this issue
(no patcher will be needed then).
There is a problem with Cyclone on symbian platform:
GNU as generates COFF object files, which allow max of 0xFFFF (65535) relocation
entries. Cyclone uses a jumptable of 0x10000 (65536, 1 for every opcode-word)
antries. When the executable is loaded, all jumptable entries must be relocated
to point to right code location. Because of this limitation, Cyclone's jumptable is
incomplete (misses 2 entries), and if M68k opcodes 0xFFFE or 0xFFFF are ever
encoundered when emulating, your emulator will crash.
I have written a little patcher to fix that. It writes those two missing entries and
marks them as relocatable. Placeholders must not be deleted just after the jumttable
in the Cyclone source code.
This version works with intermediate PE executable, which is used both for APPs and EXEs,
and is produced by gcc toolkit just before running petran. So it's best to insert
this in your makefile, in the rule which builds your APP/EXE, just after last 'ld'
statement, for example:
$(EPOCTRGUREL)\PICODRIVEN.APP : $(EPOCBLDUREL)\PICODRIVEN.in $(EPOCSTATLINKUREL)\EDLL.LIB $(LIBSUREL)
...
ld -s -e _E32Dll -u _E32Dll --dll \
"$(EPOCBLDUREL)\PICODRIVEN.exp" \
-Map "$(EPOCTRGUREL)\PICODRIVEN.APP.map" -o "$(EPOCBLDUREL)\PICODRIVEN.APP" \
"$(EPOCSTATLINKUREL)\EDLL.LIB" --whole-archive "$(EPOCBLDUREL)\PICODRIVEN.in" \
--no-whole-archive $(LIBSUREL) $(USERLDFLAGS)
-$(ERASE) "$(EPOCBLDUREL)\PICODRIVEN.exp"
patchtable_symb2 "$(EPOCBLDUREL)\PICODRIVEN.APP"
petran "$(EPOCBLDUREL)\PICODRIVEN.APP" "$@" \
-nocall -uid1 0x10000079 -uid2 0x100039ce -uid3 0x1000c193
-$(ERASE) "$(EPOCBLDUREL)\PICODRIVEN.APP"
perl -S ecopyfile.pl "$@" "PICODRIVEN.APP"
This is also compatible with ECompXL.
To test if this thing worked, you can load crash_cyclone.bin in your emulator.

View file

@ -0,0 +1,39 @@
CFLAGS = -Wall
ALL : cyclone.s
cyclone.s : Cyclone.exe
./Cyclone.exe
Cyclone.exe : Main.o Ea.o OpAny.o OpArith.o OpBranch.o OpLogic.o Disa.o OpMove.o
$(CC) $^ -o $@ -lstdc++
Main.o : ../Main.cpp ../app.h
$(CC) $(CFLAGS) ../Main.cpp -c -o $@
Ea.o : ../Ea.cpp ../app.h
$(CC) $(CFLAGS) ../Ea.cpp -c -o $@
OpAny.o : ../OpAny.cpp ../app.h
$(CC) $(CFLAGS) ../OpAny.cpp -c -o $@
OpArith.o : ../OpArith.cpp ../app.h
$(CC) $(CFLAGS) ../OpArith.cpp -c -o $@
OpBranch.o : ../OpBranch.cpp ../app.h
$(CC) $(CFLAGS) ../OpBranch.cpp -c -o $@
OpLogic.o : ../OpLogic.cpp ../app.h
$(CC) $(CFLAGS) ../OpLogic.cpp -c -o $@
OpMove.o : ../OpMove.cpp ../app.h
$(CC) $(CFLAGS) ../OpMove.cpp -c -o $@
Disa.o : ../Disa/Disa.c ../Disa/Disa.h
$(CC) $(CFLAGS) ../Disa/Disa.c -c -o $@
../app.h : ../config.h
clean :
$(RM) *.o Cyclone.exe Cyclone.s

View file

@ -0,0 +1,60 @@
# Makefile for MS Visual C
CPP=cl.exe
CPP_PROJ=/nologo /ML /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" $(RC_FLAGS) /YX /FD /c
LINK32=link.exe
LINK32_FLAGS=user32.lib /nologo /subsystem:console /machine:I386 /out:Cyclone.exe
ALL : cyclone.s
cyclone.s : Cyclone.exe
Cyclone.exe
Cyclone.exe : Main.obj Ea.obj OpAny.obj OpArith.obj OpBranch.obj OpLogic.obj Disa.obj OpMove.obj
$(LINK32) Main.obj Ea.obj OpAny.obj OpArith.obj OpBranch.obj OpLogic.obj Disa.obj OpMove.obj $(LINK32_FLAGS)
Main.obj : ..\Main.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\Main.cpp
Ea.obj : ..\Ea.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\Ea.cpp
OpAny.obj : ..\OpAny.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpAny.cpp
OpArith.obj : ..\OpArith.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpArith.cpp
OpBranch.obj : ..\OpBranch.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpBranch.cpp
OpLogic.obj : ..\OpLogic.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpLogic.cpp
OpMove.obj : ..\OpMove.cpp ..\app.h
$(CPP) $(CPP_PROJ) ..\OpMove.cpp
Disa.obj : ..\disa\Disa.c ..\disa\Disa.h
$(CPP) /nologo /ML /W4 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /c ..\disa\Disa.c
..\app.h : ..\config.h
CLEAN :
-@erase "Disa.obj"
-@erase "Ea.obj"
-@erase "Main.obj"
-@erase "OpAny.obj"
-@erase "OpArith.obj"
-@erase "OpBranch.obj"
-@erase "OpLogic.obj"
-@erase "OpMove.obj"
-@erase "vc60.idb"
-@erase "vc60.pch"
-@erase "Cyclone.exe"
-@erase "Cyclone.asm"
-@erase "Cyclone.s"
-@erase "Cyclone.o"

View file

@ -0,0 +1,146 @@
# Microsoft Developer Studio Project File - Name="cyclone" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=cyclone - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "cyclone.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "cyclone.mak" CFG="cyclone - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "cyclone - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "cyclone - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "cyclone - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x427 /d "NDEBUG"
# ADD RSC /l 0x427 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"cyclone.exe"
!ELSEIF "$(CFG)" == "cyclone - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0x427 /d "_DEBUG"
# ADD RSC /l 0x427 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"cyclone.exe" /pdbtype:sept
!ENDIF
# Begin Target
# Name "cyclone - Win32 Release"
# Name "cyclone - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=..\disa\Disa.c
# End Source File
# Begin Source File
SOURCE=..\Ea.cpp
# End Source File
# Begin Source File
SOURCE=..\Main.cpp
# End Source File
# Begin Source File
SOURCE=..\OpAny.cpp
# End Source File
# Begin Source File
SOURCE=..\OpArith.cpp
# End Source File
# Begin Source File
SOURCE=..\OpBranch.cpp
# End Source File
# Begin Source File
SOURCE=..\OpLogic.cpp
# End Source File
# Begin Source File
SOURCE=..\OpMove.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=..\app.h
# End Source File
# Begin Source File
SOURCE=..\config.h
# End Source File
# Begin Source File
SOURCE=..\Cyclone.h
# End Source File
# Begin Source File
SOURCE=..\disa\Disa.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

144
cpu/DrZ80/DrZ80.txt Normal file
View file

@ -0,0 +1,144 @@
___________________________________________________________________________
DrZ80 (c) Copyright 2004 Reesy. Free for non-commercial use
Reesy's e-mail: drsms_reesy(atsymbol)yahoo.co.uk
Replace (atsymbol) with @
___________________________________________________________________________
What is it?
-----------
DrZ80 is an emulator for the Z80 microprocessor, written in ARM 32-bit assembly.
It is aimed at chips such as ARM7 and ARM9 cores, StrongARM and XScale, to interpret Z80
code as fast as possible.
Flags are mapped onto ARM flags whenever possible, which speeds up the processing of an opcode.
ARM Register Usage
------------------
See source code for up to date of register usage, however a summary is here:
r0-3: Temporary registers
r3 : Pointer to Opcode Jump table
r4 : T-States remaining
r5 : Pointer to Cpu Context
r6 : Current PC + Memory Base (i.e. pointer to next opcode)
r7 : Z80 A Register in top 8 bits (i.e. 0xAA000000)
r8 : Z80 F Register (Flags) (NZCV) in lowest four bits
r9 : Z80 BC Register pair in top 16 bits (i.e. 0xBBCC0000)
r10 : Z80 DE Register pair in top 16 bits (i.e. 0xDDEE0000)
r11 : Z80 HL Register pair in top 16 bits (i.e. 0xHHLL0000)
r12 : Z80 Stack + Memory Base (i.e. pointer to current stack in host system memory)
( note: r3,r12 are always preserved when calling external memory functions )
How to Compile
--------------
The core is targeted for the GNU compiler, so to compile just add the "DrZ80.o" object
to your makefile and that should be it.
If you want to compile it seperately, use: as -o DrZ80.o DrZ80.s
( note: If you want to use DrZ80 with a different compiler you will need to run
some sort of parser through the source to make the syntax of the source
compatible with your target compiler )
Adding to your project
----------------------
To add DrZ80 to your project, add DrZ80.o, and include DrZ80.h
There is one structure: 'struct DrZ80', and three functions: DrZ80Run,DrZ80RaiseInt
and DrZ80_irq_callback.
Don't worry if this seem very minimal - its all you need to run as many Z80s as you want.
It works with both C and C++.
( Note: DrZ80_irq_callback is just a pointer to an irq call back function that needs
to be written by you )
Declaring a Memory handlers
---------------------------
Before you can reset or execute Z80 opcodes you must first set up a set of memory handlers.
There are 8 functions you have to set up per CPU, like this:
unsigned int z80_rebaseSP(unsigned short new_sp);
unsigned int z80_rebasePC(unsigned short new_pc);
unsigned char z80_read8(unsigned short a);
unsigned short z80_read16(unsigned short a);
void z80_write8(unsigned char d,unsigned short a);
void z80_write16(unsigned short d,unsigned short a);
unsigned char z80_in(unsigned char p);
void z80_out(unsigned char p,unsigned char d);
You can think of these functions representing the Z80's memory bus.
The Read and Write functions are called whenever the Z80 reads or writes memory.
The In and Out functions are called whenever the Z80 uses the I/O ports.
The z80_rebasePC and z80_rebaseSP functions are to do with creating direct memory
pointers in the host machines memory, I will explain more about this later.
Declaring a CPU Context
-----------------------
To declare a CPU simple declare a struct Cyclone in your code. For example to declare
two Z80s:
struct DrZ80 MyCpu;
struct DrZ80 MyCpu2;
It's probably a good idea to initialise the memory to zero:
memset(&MyCpu, 0,sizeof(MyCpu));
memset(&MyCpu2,0,sizeof(MyCpu2));
Next point to your memory handlers:
MyCpu.z80_rebasePC=z80_rebasePC;
MyCpu.z80_rebaseSP=z80_rebaseSP;
MyCpu.z80_read8 =z80_read8;
MyCpu.z80_read16 =z80_read16;
MyCpu.z80_write8 =z80_write8;
MyCpu.z80_write16 =z80_write16;
MyCpu.z80_in =z80_in;
MyCpu.z80_out =z80_out;
Now you are nearly ready to reset the Z80, except you need one more function: checkpc().
The z80_rebasePC() function
---------------------------
When DrZ80 reads opcodes, it doesn't use a memory handler every time, this would be
far too slow, instead it uses a direct pointer to ARM memory.
For example if your Rom image was at 0x3000000 and the program counter was $206,
Cyclone's program counter would be 0x3000206.
The difference between an ARM address and a Z80 address is also stored in a variable called
'pc_membase'. In the above example it's 0x3000000. To retrieve the real PC, DrZ80 just
subtracts 'pc_membase'.
Everytime the Z80 PC is modified, i.e. by a jump,branch intructions or by an interupt, DrZ80
calls the z80_rebasePC function. If the PC is in a different bank, for example Ram instead
of Rom, change 'pc_membase', recalculate the new PC and return it.
The z80_rebaseSP() function
---------------------------
When DrZ80 pushs/pops to the Z80 stack pointer, it doesn't use a memory handler every time. In
order to gain more speed a direct pointer to ARM memory is used. For example if your Ram was at
0x3000000 and the z80 stack pointer counter was 0xD000, DrZ80's stack pointer would be 0x300D000.
The difference between an ARM address and a Z80 address is also stored in a variable called
'sp_membase'. In the above example it's 0x3000000. To retrieve the real SP, DrZ80 just
subtracts 'sp_membase'.
Everytime the Z80 SP is modified ( i.e. set with a new value LD SP,NN etc ) DrZ80
calls the z80_rebaseSP function. If the SP is in a different bank, for example Rom instead
of Ram, change 'sp_membase', recalculate the new SP and return it.

77
cpu/DrZ80/drz80.h Normal file
View file

@ -0,0 +1,77 @@
/*
* DrZ80 Version 1.0
* Z80 Emulator by Reesy
* Copyright 2005 Reesy
*
* This file is part of DrZ80.
*
* DrZ80 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* DrZ80 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DrZ80; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef DRZ80_H
#define DRZ80_H
extern int DrZ80Ver; /* Version number of library */
struct DrZ80
{
unsigned int Z80PC; /*0x00 - PC Program Counter (Memory Base + PC) */
unsigned int Z80A; /*0x04 - A Register: 0xAA------ */
unsigned int Z80F; /*0x08 - F Register: 0xFF------ */
unsigned int Z80BC; /*0x0C - BC Registers: 0xBBCC---- */
unsigned int Z80DE; /*0x10 - DE Registers: 0xDDEE---- */
unsigned int Z80HL; /*0x14 - HL Registers: 0xHHLL---- */
unsigned int Z80SP; /*0x18 - SP Stack Pointer (Memory Base + PC) */
unsigned int Z80PC_BASE; /*0x1C - PC Program Counter (Memory Base) */
unsigned int Z80SP_BASE; /*0x20 - SP Stack Pointer (Memory Base) */
unsigned int Z80IX; /*0x24 - IX Index Register */
unsigned int Z80IY; /*0x28 - IY Index Register */
unsigned int Z80I; /*0x2C - I Interrupt Register */
unsigned int Z80A2; /*0x30 - A' Register: 0xAA------ */
unsigned int Z80F2; /*0x34 - F' Register: 0xFF------ */
unsigned int Z80BC2; /*0x38 - B'C' Registers: 0xBBCC---- */
unsigned int Z80DE2; /*0x3C - D'E' Registers: 0xDDEE---- */
unsigned int Z80HL2; /*0x40 - H'L' Registers: 0xHHLL---- */
int cycles; /*0x44 - Cycles pending to be executed yet */
int previouspc; /*0x48 - Previous PC */
unsigned char Z80_IRQ; /*0x4C - Set IRQ Number (must be halfword aligned) */
unsigned char Z80IF; /*0x4D - Interrupt Flags: bit1=_IFF1, bit2=_IFF2, bit3=_HALT */
unsigned char Z80IM; /*0x4E - Set IRQ Mode */
unsigned char spare; /*0x4F - N/A */
unsigned int z80irqvector; /*0x50 - Set IRQ Vector i.e. 0xFF=RST */
void (*z80_irq_callback )(void);
void (*z80_write8 )(unsigned char d,unsigned short a);
void (*z80_write16 )(unsigned short d,unsigned short a);
unsigned char (*z80_in)(unsigned short p);
void (*z80_out )(unsigned short p,unsigned char d);
unsigned char (*z80_read8)(unsigned short a);
unsigned short (*z80_read16)(unsigned short a);
unsigned int (*z80_rebaseSP)(unsigned short new_sp);
unsigned int (*z80_rebasePC)(unsigned short new_pc);
unsigned int bla;
};
extern int DrZ80Run(struct DrZ80 *pcy,unsigned int cyc);
#endif
#ifdef __cplusplus
} /* End of extern "C" */
#endif

8076
cpu/DrZ80/drz80.s Normal file

File diff suppressed because it is too large Load diff

16
cpu/a68k/Makefile.win Normal file
View file

@ -0,0 +1,16 @@
all : a68k.obj
a68k.obj :
cl /DWIN32 /DFASTCALL make68kd.c
make68kd a68k.asm a68k_jmp.asm 00
nasm -f win32 a68k.asm
clean : tidy
del a68k.obj
tidy :
del a68k.asm
del a68k_jmp.asm
del make68kd.exe
del make68kd.obj

1
cpu/a68k/cpuintrf.h Normal file
View file

@ -0,0 +1 @@
// dave filler file

8192
cpu/a68k/make68kd.c Normal file

File diff suppressed because it is too large Load diff

371
cpu/musashi/m68k.h Normal file
View file

@ -0,0 +1,371 @@
#ifndef M68K__HEADER
#define M68K__HEADER
/* ======================================================================== */
/* ========================= LICENSING & COPYRIGHT ======================== */
/* ======================================================================== */
/*
* MUSASHI
* Version 3.3
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
*
* This code may be freely used for non-commercial purposes as long as this
* copyright notice remains unaltered in the source code and any binary files
* containing this code in compiled form.
*
* All other lisencing terms must be negotiated with the author
* (Karl Stenerud).
*
* The latest version of this code can be obtained at:
* http://kstenerud.cjb.net
*/
/* ======================================================================== */
/* ============================= CONFIGURATION ============================ */
/* ======================================================================== */
/* Import the configuration for this build */
#include "m68kconf.h"
/* ======================================================================== */
/* ============================ GENERAL DEFINES =========================== */
/* ======================================================================== */
/* There are 7 levels of interrupt to the 68K.
* A transition from < 7 to 7 will cause a non-maskable interrupt (NMI).
*/
#define M68K_IRQ_NONE 0
#define M68K_IRQ_1 1
#define M68K_IRQ_2 2
#define M68K_IRQ_3 3
#define M68K_IRQ_4 4
#define M68K_IRQ_5 5
#define M68K_IRQ_6 6
#define M68K_IRQ_7 7
/* Special interrupt acknowledge values.
* Use these as special returns from the interrupt acknowledge callback
* (specified later in this header).
*/
/* Causes an interrupt autovector (0x18 + interrupt level) to be taken.
* This happens in a real 68K if VPA or AVEC is asserted during an interrupt
* acknowledge cycle instead of DTACK.
*/
#define M68K_INT_ACK_AUTOVECTOR 0xffffffff
/* Causes the spurious interrupt vector (0x18) to be taken
* This happens in a real 68K if BERR is asserted during the interrupt
* acknowledge cycle (i.e. no devices responded to the acknowledge).
*/
#define M68K_INT_ACK_SPURIOUS 0xfffffffe
/* CPU types for use in m68k_set_cpu_type() */
enum
{
M68K_CPU_TYPE_INVALID,
M68K_CPU_TYPE_68000,
M68K_CPU_TYPE_68008,
M68K_CPU_TYPE_68010,
M68K_CPU_TYPE_68EC020,
M68K_CPU_TYPE_68020,
M68K_CPU_TYPE_68030, /* Supported by disassembler ONLY */
M68K_CPU_TYPE_68040 /* Supported by disassembler ONLY */
};
/* Registers used by m68k_get_reg() and m68k_set_reg() */
typedef enum
{
/* Real registers */
M68K_REG_D0, /* Data registers */
M68K_REG_D1,
M68K_REG_D2,
M68K_REG_D3,
M68K_REG_D4,
M68K_REG_D5,
M68K_REG_D6,
M68K_REG_D7,
M68K_REG_A0, /* Address registers */
M68K_REG_A1,
M68K_REG_A2,
M68K_REG_A3,
M68K_REG_A4,
M68K_REG_A5,
M68K_REG_A6,
M68K_REG_A7,
M68K_REG_PC, /* Program Counter */
M68K_REG_SR, /* Status Register */
M68K_REG_SP, /* The current Stack Pointer (located in A7) */
M68K_REG_USP, /* User Stack Pointer */
M68K_REG_ISP, /* Interrupt Stack Pointer */
M68K_REG_MSP, /* Master Stack Pointer */
M68K_REG_SFC, /* Source Function Code */
M68K_REG_DFC, /* Destination Function Code */
M68K_REG_VBR, /* Vector Base Register */
M68K_REG_CACR, /* Cache Control Register */
M68K_REG_CAAR, /* Cache Address Register */
/* Assumed registers */
/* These are cheat registers which emulate the 1-longword prefetch
* present in the 68000 and 68010.
*/
M68K_REG_PREF_ADDR, /* Last prefetch address */
M68K_REG_PREF_DATA, /* Last prefetch data */
/* Convenience registers */
M68K_REG_PPC, /* Previous value in the program counter */
M68K_REG_IR, /* Instruction register */
M68K_REG_CPU_TYPE /* Type of CPU being run */
} m68k_register_t;
/* ======================================================================== */
/* ====================== FUNCTIONS CALLED BY THE CPU ===================== */
/* ======================================================================== */
/* You will have to implement these functions */
/* read/write functions called by the CPU to access memory.
* while values used are 32 bits, only the appropriate number
* of bits are relevant (i.e. in write_memory_8, only the lower 8 bits
* of value should be written to memory).
*
* NOTE: I have separated the immediate and PC-relative memory fetches
* from the other memory fetches because some systems require
* differentiation between PROGRAM and DATA fetches (usually
* for security setups such as encryption).
* This separation can either be achieved by setting
* M68K_SEPARATE_READS in m68kconf.h and defining
* the read functions, or by setting M68K_EMULATE_FC and
* making a function code callback function.
* Using the callback offers better emulation coverage
* because you can also monitor whether the CPU is in SYSTEM or
* USER mode, but it is also slower.
*/
/* Read from anywhere */
unsigned int m68k_read_memory_8(unsigned int address);
unsigned int m68k_read_memory_16(unsigned int address);
unsigned int m68k_read_memory_32(unsigned int address);
/* Read data immediately following the PC */
unsigned int m68k_read_immediate_16(unsigned int address);
unsigned int m68k_read_immediate_32(unsigned int address);
/* Read data relative to the PC */
unsigned int m68k_read_pcrelative_8(unsigned int address);
unsigned int m68k_read_pcrelative_16(unsigned int address);
unsigned int m68k_read_pcrelative_32(unsigned int address);
/* Memory access for the disassembler */
unsigned int m68k_read_disassembler_8 (unsigned int address);
unsigned int m68k_read_disassembler_16 (unsigned int address);
unsigned int m68k_read_disassembler_32 (unsigned int address);
/* Write to anywhere */
void m68k_write_memory_8(unsigned int address, unsigned int value);
void m68k_write_memory_16(unsigned int address, unsigned int value);
void m68k_write_memory_32(unsigned int address, unsigned int value);
/* Special call to simulate undocumented 68k behavior when move.l with a
* predecrement destination mode is executed.
* To simulate real 68k behavior, first write the high word to
* [address+2], and then write the low word to [address].
*
* Enable this functionality with M68K_SIMULATE_PD_WRITES in m68kconf.h.
*/
void m68k_write_memory_32_pd(unsigned int address, unsigned int value);
/* ======================================================================== */
/* ============================== CALLBACKS =============================== */
/* ======================================================================== */
/* These functions allow you to set callbacks to the host when specific events
* occur. Note that you must enable the corresponding value in m68kconf.h
* in order for these to do anything useful.
* Note: I have defined default callbacks which are used if you have enabled
* the corresponding #define in m68kconf.h but either haven't assigned a
* callback or have assigned a callback of NULL.
*/
/* Set the callback for an interrupt acknowledge.
* You must enable M68K_EMULATE_INT_ACK in m68kconf.h.
* The CPU will call the callback with the interrupt level being acknowledged.
* The host program must return either a vector from 0x02-0xff, or one of the
* special interrupt acknowledge values specified earlier in this header.
* If this is not implemented, the CPU will always assume an autovectored
* interrupt, and will automatically clear the interrupt request when it
* services the interrupt.
* Default behavior: return M68K_INT_ACK_AUTOVECTOR.
*/
void m68k_set_int_ack_callback(int (*callback)(int int_level));
/* Set the callback for a breakpoint acknowledge (68010+).
* You must enable M68K_EMULATE_BKPT_ACK in m68kconf.h.
* The CPU will call the callback with whatever was in the data field of the
* BKPT instruction for 68020+, or 0 for 68010.
* Default behavior: do nothing.
*/
void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data));
/* Set the callback for the RESET instruction.
* You must enable M68K_EMULATE_RESET in m68kconf.h.
* The CPU calls this callback every time it encounters a RESET instruction.
* Default behavior: do nothing.
*/
void m68k_set_reset_instr_callback(void (*callback)(void));
/* Set the callback for the CMPI.L #v, Dn instruction.
* You must enable M68K_CMPILD_HAS_CALLBACK in m68kconf.h.
* The CPU calls this callback every time it encounters a CMPI.L #v, Dn instruction.
* Default behavior: do nothing.
*/
void m68k_set_cmpild_instr_callback(void (*callback)(unsigned int val, int reg));
/* Set the callback for the RTE instruction.
* You must enable M68K_RTE_HAS_CALLBACK in m68kconf.h.
* The CPU calls this callback every time it encounters a RTE instruction.
* Default behavior: do nothing.
*/
void m68k_set_rte_instr_callback(void (*callback)(void));
/* Set the callback for informing of a large PC change.
* You must enable M68K_MONITOR_PC in m68kconf.h.
* The CPU calls this callback with the new PC value every time the PC changes
* by a large value (currently set for changes by longwords).
* Default behavior: do nothing.
*/
void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc));
/* Set the callback for CPU function code changes.
* You must enable M68K_EMULATE_FC in m68kconf.h.
* The CPU calls this callback with the function code before every memory
* access to set the CPU's function code according to what kind of memory
* access it is (supervisor/user, program/data and such).
* Default behavior: do nothing.
*/
void m68k_set_fc_callback(void (*callback)(unsigned int new_fc));
/* Set a callback for the instruction cycle of the CPU.
* You must enable M68K_INSTRUCTION_HOOK in m68kconf.h.
* The CPU calls this callback just before fetching the opcode in the
* instruction cycle.
* Default behavior: do nothing.
*/
void m68k_set_instr_hook_callback(void (*callback)(void));
/* ======================================================================== */
/* ====================== FUNCTIONS TO ACCESS THE CPU ===================== */
/* ======================================================================== */
/* Use this function to set the CPU type you want to emulate.
* Currently supported types are: M68K_CPU_TYPE_68000, M68K_CPU_TYPE_68008,
* M68K_CPU_TYPE_68010, M68K_CPU_TYPE_EC020, and M68K_CPU_TYPE_68020.
*/
void m68k_set_cpu_type(unsigned int cpu_type);
/* Do whatever initialisations the core requires. Should be called
* at least once at init time.
*/
void m68k_init(void);
/* Pulse the RESET pin on the CPU.
* You *MUST* reset the CPU at least once to initialize the emulation
* Note: If you didn't call m68k_set_cpu_type() before resetting
* the CPU for the first time, the CPU will be set to
* M68K_CPU_TYPE_68000.
*/
void m68k_pulse_reset(void);
/* execute num_cycles worth of instructions. returns number of cycles used */
int m68k_execute(int num_cycles);
/* These functions let you read/write/modify the number of cycles left to run
* while m68k_execute() is running.
* These are useful if the 68k accesses a memory-mapped port on another device
* that requires immediate processing by another CPU.
*/
int m68k_cycles_run(void); /* Number of cycles run so far */
int m68k_cycles_remaining(void); /* Number of cycles left */
void m68k_modify_timeslice(int cycles); /* Modify cycles left */
void m68k_end_timeslice(void); /* End timeslice now */
/* Set the IPL0-IPL2 pins on the CPU (IRQ).
* A transition from < 7 to 7 will cause a non-maskable interrupt (NMI).
* Setting IRQ to 0 will clear an interrupt request.
*/
void m68k_set_irq(unsigned int int_level);
/* Halt the CPU as if you pulsed the HALT pin. */
void m68k_pulse_halt(void);
/* Context switching to allow multiple CPUs */
/* Get the size of the cpu context in bytes */
unsigned int m68k_context_size(void);
/* Get a cpu context */
unsigned int m68k_get_context(void* dst);
/* set the current cpu context */
void m68k_set_context(void* dst);
/* Register the CPU state information */
void m68k_state_register(const char *type, int index);
/* Peek at the internals of a CPU context. This can either be a context
* retrieved using m68k_get_context() or the currently running context.
* If context is NULL, the currently running CPU context will be used.
*/
unsigned int m68k_get_reg(void* context, m68k_register_t reg);
/* Poke values into the internals of the currently running CPU context */
void m68k_set_reg(m68k_register_t reg, unsigned int value);
/* Check if an instruction is valid for the specified CPU type */
unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type);
/* Disassemble 1 instruction using the epecified CPU type at pc. Stores
* disassembly in str_buff and returns the size of the instruction in bytes.
*/
unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_type);
/* Same as above but accepts raw opcode data directly rather than fetching
* via the read/write interfaces.
*/
unsigned int m68k_disassemble_raw(char* str_buff, unsigned int pc, unsigned char* opdata, unsigned char* argdata, int length, unsigned int cpu_type);
/* ======================================================================== */
/* ============================== MAME STUFF ============================== */
/* ======================================================================== */
#if M68K_COMPILE_FOR_MAME == OPT_ON
#include "m68kmame.h"
#endif /* M68K_COMPILE_FOR_MAME */
/* ======================================================================== */
/* ============================== END OF FILE ============================= */
/* ======================================================================== */
#endif /* M68K__HEADER */

10636
cpu/musashi/m68k_in.c Normal file

File diff suppressed because it is too large Load diff

203
cpu/musashi/m68kconf.h Normal file
View file

@ -0,0 +1,203 @@
/* ======================================================================== */
/* ========================= LICENSING & COPYRIGHT ======================== */
/* ======================================================================== */
/*
* MUSASHI
* Version 3.3
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
*
* This code may be freely used for non-commercial purposes as long as this
* copyright notice remains unaltered in the source code and any binary files
* containing this code in compiled form.
*
* All other lisencing terms must be negotiated with the author
* (Karl Stenerud).
*
* The latest version of this code can be obtained at:
* http://kstenerud.cjb.net
*/
// notaz: kill some stupid VC warnings
#ifndef __GNUC__
#pragma warning (disable:4100) // unreferenced formal parameter
#pragma warning (disable:4127) // conditional expression is constant
#pragma warning (disable:4245) // type conversion
#pragma warning (disable:4514) // unreferenced inline function has been removed
#endif
#ifndef M68KCONF__HEADER
#define M68KCONF__HEADER
/* Configuration switches.
* Use OPT_SPECIFY_HANDLER for configuration options that allow callbacks.
* OPT_SPECIFY_HANDLER causes the core to link directly to the function
* or macro you specify, rather than using callback functions whose pointer
* must be passed in using m68k_set_xxx_callback().
*/
#define OPT_OFF 0
#define OPT_ON 1
#define OPT_SPECIFY_HANDLER 2
/* ======================================================================== */
/* ============================== MAME STUFF ============================== */
/* ======================================================================== */
/* If you're compiling this for MAME, only change M68K_COMPILE_FOR_MAME
* to OPT_ON and use m68kmame.h to configure the 68k core.
*/
#ifndef M68K_COMPILE_FOR_MAME
#define M68K_COMPILE_FOR_MAME OPT_OFF
#endif /* M68K_COMPILE_FOR_MAME */
#if M68K_COMPILE_FOR_MAME == OPT_OFF
/* ======================================================================== */
/* ============================= CONFIGURATION ============================ */
/* ======================================================================== */
/* Turn ON if you want to use the following M68K variants */
#define M68K_EMULATE_008 OPT_OFF
#define M68K_EMULATE_010 OPT_OFF
#define M68K_EMULATE_EC020 OPT_OFF
#define M68K_EMULATE_020 OPT_OFF
#define M68K_EMULATE_040 OPT_OFF
/* If ON, the CPU will call m68k_read_immediate_xx() for immediate addressing
* and m68k_read_pcrelative_xx() for PC-relative addressing.
* If off, all read requests from the CPU will be redirected to m68k_read_xx()
*/
#define M68K_SEPARATE_READS OPT_ON
/* If ON, the CPU will call m68k_write_32_pd() when it executes move.l with a
* predecrement destination EA mode instead of m68k_write_32().
* To simulate real 68k behavior, m68k_write_32_pd() must first write the high
* word to [address+2], and then write the low word to [address].
*/
#define M68K_SIMULATE_PD_WRITES OPT_OFF
/* If ON, CPU will call the interrupt acknowledge callback when it services an
* interrupt.
* If off, all interrupts will be autovectored and all interrupt requests will
* auto-clear when the interrupt is serviced.
*/
#define M68K_EMULATE_INT_ACK OPT_ON
#define M68K_INT_ACK_CALLBACK(A) your_int_ack_handler_function(A)
/* If ON, CPU will call the breakpoint acknowledge callback when it encounters
* a breakpoint instruction and it is running a 68010+.
*/
#define M68K_EMULATE_BKPT_ACK OPT_OFF
#define M68K_BKPT_ACK_CALLBACK() your_bkpt_ack_handler_function()
/* If ON, the CPU will monitor the trace flags and take trace exceptions
*/
#define M68K_EMULATE_TRACE OPT_OFF
/* If ON, CPU will call the output reset callback when it encounters a reset
* instruction.
*/
#define M68K_EMULATE_RESET OPT_OFF
#define M68K_RESET_CALLBACK() your_reset_handler_function()
/* If ON, CPU will call the callback when it encounters a cmpi.l #v, dn
* instruction.
*/
#define M68K_CMPILD_HAS_CALLBACK OPT_OFF
#define M68K_CMPILD_CALLBACK(v,r) your_cmpild_handler_function(v,r)
/* If ON, CPU will call the callback when it encounters a rte
* instruction.
*/
#define M68K_RTE_HAS_CALLBACK OPT_OFF
#define M68K_RTE_CALLBACK() your_rte_handler_function()
/* If ON, CPU will call the set fc callback on every memory access to
* differentiate between user/supervisor, program/data access like a real
* 68000 would. This should be enabled and the callback should be set if you
* want to properly emulate the m68010 or higher. (moves uses function codes
* to read/write data from different address spaces)
*/
#define M68K_EMULATE_FC OPT_OFF
#define M68K_SET_FC_CALLBACK(A) your_set_fc_handler_function(A)
/* If ON, CPU will call the pc changed callback when it changes the PC by a
* large value. This allows host programs to be nicer when it comes to
* fetching immediate data and instructions on a banked memory system.
*/
#define M68K_MONITOR_PC OPT_OFF
#define M68K_SET_PC_CALLBACK(A) your_pc_changed_handler_function(A)
/* If ON, CPU will call the instruction hook callback before every
* instruction.
*/
#define M68K_INSTRUCTION_HOOK OPT_OFF
#define M68K_INSTRUCTION_CALLBACK() your_instruction_hook_function()
/* If ON, the CPU will emulate the 4-byte prefetch queue of a real 68000 */
#define M68K_EMULATE_PREFETCH OPT_OFF
/* If ON, the CPU will generate address error exceptions if it tries to
* access a word or longword at an odd address.
* NOTE: This is only emulated properly for 68000 mode.
*/
#define M68K_EMULATE_ADDRESS_ERROR OPT_OFF
/* Turn ON to enable logging of illegal instruction calls.
* M68K_LOG_FILEHANDLE must be #defined to a stdio file stream.
* Turn on M68K_LOG_1010_1111 to log all 1010 and 1111 calls.
*/
#define M68K_LOG_ENABLE OPT_OFF
#define M68K_LOG_1010_1111 OPT_OFF
#define M68K_LOG_FILEHANDLE some_file_handle
/* ----------------------------- COMPATIBILITY ---------------------------- */
/* The following options set optimizations that violate the current ANSI
* standard, but will be compliant under the forthcoming C9X standard.
*/
/* If ON, the enulation core will use 64-bit integers to speed up some
* operations.
*/
#define M68K_USE_64_BIT OPT_OFF
/* Set to your compiler's static inline keyword to enable it, or
* set it to blank to disable it.
* If you define INLINE in the makefile, it will override this value.
* NOTE: not enabling inline functions will SEVERELY slow down emulation.
*/
#ifndef INLINE
#define INLINE static __inline
#endif /* INLINE */
#endif /* M68K_COMPILE_FOR_MAME */
/* ======================================================================== */
/* ============================== END OF FILE ============================= */
/* ======================================================================== */
#endif /* M68KCONF__HEADER */

1015
cpu/musashi/m68kcpu.c Normal file

File diff suppressed because it is too large Load diff

2022
cpu/musashi/m68kcpu.h Normal file

File diff suppressed because it is too large Load diff

3604
cpu/musashi/m68kdasm.c Normal file

File diff suppressed because it is too large Load diff

1484
cpu/musashi/m68kmake.c Normal file

File diff suppressed because it is too large Load diff

12199
cpu/musashi/m68kopac.c Normal file

File diff suppressed because it is too large Load diff

13385
cpu/musashi/m68kopdm.c Normal file

File diff suppressed because it is too large Load diff

8878
cpu/musashi/m68kopnz.c Normal file

File diff suppressed because it is too large Load diff

2093
cpu/musashi/m68kops.c Normal file

File diff suppressed because it is too large Load diff

1986
cpu/musashi/m68kops.h Normal file

File diff suppressed because it is too large Load diff

315
cpu/musashi/readme.txt Normal file
View file

@ -0,0 +1,315 @@
MUSASHI
=======
Version 3.3
A portable Motorola M680x0 processor emulation engine.
Copyright 1998-2001 Karl Stenerud. All rights reserved.
INTRODUCTION:
------------
Musashi is a Motorola 68000, 68010, 68EC020, and 68020 emulator written in C.
This emulator was written with two goals in mind: portability and speed.
The emulator is written to ANSI C specifications with the exception that I use
inline functions. This is not compliant to the ANSI spec, but will be
compliant to the ANSI C9X spec.
It has been successfully running in the MAME project (www.mame.net) for over 2
years and so has had time to mature.
LICENSE AND COPYRIGHT:
---------------------
The Musashi M680x0 emulator is copyright 1998-2001 Karl Stenerud.
The source code included in this archive is provided AS-IS, free for any
non-commercial purpose.
If you build a program using this core, please give credit to the author.
If you wish to use this core in a commercial environment, please contact
the author to discuss commercial licensing.
AVAILABILITY:
------------
The latest version of this code can be obtained at:
http://kstenerud.cjb.net
CONTACTING THE AUTHOR:
---------------------
I can be reached at kstenerud@mame.net
BASIC CONFIGURATION:
-------------------
The basic configuration will give you a standard 68000 that has sufficient
functionality to work in a primitive environment.
This setup assumes that you only have 1 device interrupting it, that the
device will always request an autovectored interrupt, and it will always clear
the interrupt before the interrupt service routine finishes (but could
possibly re-assert the interrupt).
You will have only one address space, no tracing, and no instruction prefetch.
To implement the basic configuration:
- Open m68kconf.h and verify that the settings for INLINE and DECL_SPEC will
work with your compiler. (They are set for gcc)
- In your host program, implement the following functions:
unsigned int m68k_read_memory_8(unsigned int address);
unsigned int m68k_read_memory_16(unsigned int address);
unsigned int m68k_read_memory_32(unsigned int address);
void m68k_write_memory_8(unsigned int address, unsigned int value);
void m68k_write_memory_16(unsigned int address, unsigned int value);
void m68k_write_memory_32(unsigned int address, unsigned int value);
- In your host program, be sure to call m68k_pulse_reset() once before calling
any of the other functions as this initializes the core.
- Use m68k_execute() to execute instructions and m68k_set_irq() to cause an
interrupt.
ADDING PROPER INTERRUPT HANDLING:
--------------------------------
The interrupt handling in the basic configuration doesn't emulate the
interrupt acknowledge phase of the CPU and automatically clears an interrupt
request during interrupt processing.
While this works for most systems, you may need more accurate interrupt
handling.
To add proper interrupt handling:
- In m68kconf.h, set M68K_EMULATE_INT_ACK to OPT_SPECIFY_HANDLER
- In m68kconf.h, set M68K_INT_ACK_CALLBACK(A) to your interrupt acknowledge
routine
- Your interrupt acknowledge routine must return an interrupt vector,
M68K_INT_ACK_AUTOVECTOR, or M68K_INT_ACK_SPURIOUS. most m68k
implementations just use autovectored interrupts.
- When the interrupting device is satisfied, you must call m68k_set_irq(0) to
remove the interrupt request.
MULTIPLE INTERRUPTS:
-------------------
The above system will work if you have only one device interrupting the CPU,
but if you have more than one device, you must do a bit more.
To add multiple interrupts:
- You must make an interrupt arbitration device that will take the highest
priority interrupt and encode it onto the IRQ pins on the CPU.
- The interrupt arbitration device should use m68k_set_irq() to set the
highest pending interrupt, or 0 for no interrupts pending.
SEPARATE IMMEDIATE AND PC-RELATIVE READS:
----------------------------------------
You can write faster memory access functions if you know whether you are
fetching from ROM or RAM. Immediate reads are always from the program space
(Always in ROM unless it is running self-modifying code).
This will also separate the pc-relative reads, since some systems treat
PROGRAM mode reads and DATA mode reads differently (for program encryption,
for instance). See the section below (ADDRESS SPACE) for an explanation of
PROGRAM and DATA mode.
To enable separate reads:
- In m68kconf.h, turn on M68K_SEPARATE_READS.
- In your host program, implement the following functions:
unsigned int m68k_read_immediate_16(unsigned int address);
unsigned int m68k_read_immediate_32(unsigned int address);
unsigned int m68k_read_pcrelative_8(unsigned int address);
unsigned int m68k_read_pcrelative_16(unsigned int address);
unsigned int m68k_read_pcrelative_32(unsigned int address);
- If you need to know the current PC (for banking and such), set
M68K_MONITOR_PC to OPT_SPECIFY_HANDLER, and set M68K_SET_PC_CALLBACK(A) to
your routine.
ADDRESS SPACES:
--------------
Most systems will only implement one address space, placing ROM at the lower
addresses and RAM at the higher. However, there is the possibility that a
system will implement ROM and RAM in the same address range, but in different
address spaces, or will have different mamory types that require different
handling for the program and the data.
The 68k accomodates this by allowing different program spaces, the most
important to us being PROGRAM and DATA space. Here is a breakdown of
how information is fetched:
- All immediate reads are fetched from PROGRAM space.
- All PC-relative reads are fetched from PROGRAM space.
- The initial stack pointer and program counter are fetched from PROGRAM space.
- All other reads (except for those from the moves instruction for 68020)
are fetched from DATA space.
The m68k deals with this by encoding the requested address space on the
function code pins:
FC
Address Space 210
------------------ ---
USER DATA 001
USER PROGRAM 010
SUPERVISOR DATA 101
SUPERVISOR PROGRAM 110
CPU SPACE 111 <-- not emulated in this core since we emulate
interrupt acknowledge in another way.
Problems arise here if you need to emulate this distinction (if, for example,
your ROM and RAM are at the same address range, with RAM and ROM enable
wired to the function code pins).
There are 2 ways to deal with this situation using Musashi:
1. If you only need the distinction between PROGRAM and DATA (the most common),
you can just separate the reads (see the preceeding section). This is the
faster solution.
2. You can emulate the function code pins entirely.
To emulate the function code pins:
- In m68kconf.h, set M68K_EMULATE_FC to OPT_SPECIFY_HANDLER and set
M68K_SET_FC_CALLBACK(A) to your function code handler function.
- Your function code handler should select the proper address space for
subsequent calls to m68k_read_xx (and m68k_write_xx for 68010+).
Note: immediate reads are always done from program space, so technically you
don't need to implement the separate immediate reads, although you could
gain more speed improvements leaving them in and doing some clever
programming.
USING DIFFERENT CPU TYPES:
-------------------------
The default is to enable only the 68000 cpu type. To change this, change the
settings for M68K_EMULATE_010 etc in m68kconf.h.
To set the CPU type you want to use:
- Make sure it is enabled in m68kconf.h. Current switches are:
M68K_EMULATE_010
M68K_EMULATE_EC020
M68K_EMULATE_020
- In your host program, call m68k_set_cpu_type() and then call
m68k_pulse_reset(). Valid CPU types are:
M68K_CPU_TYPE_68000,
M68K_CPU_TYPE_68010,
M68K_CPU_TYPE_68EC020,
M68K_CPU_TYPE_68020
CLOCK FREQUENCY:
---------------
In order to emulate the correct clock frequency, you will have to calculate
how long it takes the emulation to execute a certain number of "cycles" and
vary your calls to m68k_execute() accordingly.
As well, it is a good idea to take away the CPU's timeslice when it writes to
a memory-mapped port in order to give the device it wrote to a chance to
react.
You can use the functions m68k_cycles_run(), m68k_cycles_remaining(),
m68k_modify_timeslice(), and m68k_end_timeslice() to do this.
Try to use large cycle values in your calls to m68k_execute() since it will
increase throughput. You can always take away the timeslice later.
MORE CORRECT EMULATION:
----------------------
You may need to enable these in order to properly emulate some of the more
obscure functions of the m68k:
- M68K_EMULATE_BKPT_ACK causes the CPU to call a breakpoint handler on a BKPT
instruction
- M68K_EMULATE_TRACE causes the CPU to generate trace exceptions when the
trace bits are set
- M68K_EMULATE_RESET causes the CPU to call a reset handler on a RESET
instruction.
- M68K_EMULATE_PREFETCH emulates the 4-word instruction prefetch that is part
of the 68000/68010 (needed for Amiga emulation).
- call m68k_pulse_halt() to emulate the HALT pin.
CONVENIENCE FUNCTIONS:
---------------------
These are in here for programmer convenience:
- M68K_INSTRUCTION_HOOK lets you call a handler before each instruction.
- M68K_LOG_ENABLE and M68K_LOG_1010_1111 lets you log illegal and A/F-line
instructions.
MULTIPLE CPU EMULATION:
----------------------
The default is to use only one CPU. To use more than one CPU in this core,
there are some things to keep in mind:
- To have different cpus call different functions, use OPT_ON instead of
OPT_SPECIFY_HANDLER, and use the m68k_set_xxx_callback() functions to set
your callback handlers on a per-cpu basis.
- Be sure to call set_cpu_type() for each CPU you use.
- Use m68k_set_context() and m68k_get_context() to switch to another CPU.
LOAD AND SAVE CPU CONTEXTS FROM DISK:
------------------------------------
You can use them68k_load_context() and m68k_save_context() functions to load
and save the CPU state to disk.
GET/SET INFORMATION FROM THE CPU:
--------------------------------
You can use m68k_get_reg() and m68k_set_reg() to gain access to the internals
of the CPU.
EXAMPLE:
-------
I have included a file example.zip that contains a full example.

13
cpu/mz80/Makefile Normal file
View file

@ -0,0 +1,13 @@
CFLAGS = -Wno-conversion -Wno-sign-compare # -Wno-pointer-sign
all : mz80.asm
mz80.asm : makez80
./makez80 -s -l -x86 $@
makez80 : makez80.o
clean :
$(RM) makez80 makez80.o mz80.asm

18
cpu/mz80/Makefile.win Normal file
View file

@ -0,0 +1,18 @@
all : mz80.obj
mz80.obj : mz80.asm
nasm -f win32 mz80.asm -o $@
mz80.asm : makez80.exe
makez80.exe -s -x86 $@
makez80.exe : makez80.c
cl /DWIN32 /W3 makez80.c
clean : tidy
del mz80.obj
tidy :
del mz80.asm makez80.exe makez80.obj

9512
cpu/mz80/makez80.c Normal file

File diff suppressed because it is too large Load diff

17053
cpu/mz80/mz80.c Normal file

File diff suppressed because it is too large Load diff

394
cpu/mz80/mz80.h Normal file
View file

@ -0,0 +1,394 @@
/* Multi-Z80 32 Bit emulator */
/* Copyright 1996, Neil Bradley, All rights reserved
*
* License agreement:
*
* The mZ80 emulator may be distributed in unmodified form to any medium.
*
* mZ80 May not be sold, or sold as a part of a commercial package without
* the express written permission of Neil Bradley (neil@synthcom.com). This
* includes shareware.
*
* Modified versions of mZ80 may not be publicly redistributed without author
* approval (neil@synthcom.com). This includes distributing via a publicly
* accessible LAN. You may make your own source modifications and distribute
* mZ80 in object only form.
*
* mZ80 Licensing for commercial applications is available. Please email
* neil@synthcom.com for details.
*
* Synthcom Systems, Inc, and Neil Bradley will not be held responsible for
* any damage done by the use of mZ80. It is purely "as-is".
*
* If you use mZ80 in a freeware application, credit in the following text:
*
* "Multi-Z80 CPU emulator by Neil Bradley (neil@synthcom.com)"
*
* must accompany the freeware application within the application itself or
* in the documentation.
*
* Legal stuff aside:
*
* If you find problems with mZ80, please email the author so they can get
* resolved. If you find a bug and fix it, please also email the author so
* that those bug fixes can be propogated to the installed base of mZ80
* users. If you find performance improvements or problems with mZ80, please
* email the author with your changes/suggestions and they will be rolled in
* with subsequent releases of mZ80.
*
* The whole idea of this emulator is to have the fastest available 32 bit
* Multi-z80 emulator for the PC, giving maximum performance.
*/
/* General z80 based defines */
#ifndef _MZ80_H_
#define _MZ80_H_
#ifndef UINT32
#define UINT32 unsigned long int
#endif
#ifndef UINT16
#define UINT16 unsigned short int
#endif
#ifndef UINT8
#define UINT8 unsigned char
#endif
#ifndef INT32
#define INT32 signed long int
#endif
#ifndef INT16
#define INT16 signed short int
#endif
#ifndef INT8
#define INT8 signed char
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _MEMORYREADWRITEBYTE_
#define _MEMORYREADWRITEBYTE_
struct MemoryWriteByte
{
UINT32 lowAddr;
UINT32 highAddr;
void (*memoryCall)(UINT32, UINT8, struct MemoryWriteByte *);
void *pUserArea;
};
struct MemoryReadByte
{
UINT32 lowAddr;
UINT32 highAddr;
UINT8 (*memoryCall)(UINT32, struct MemoryReadByte *);
void *pUserArea;
};
#endif // _MEMORYREADWRITEBYTE_
struct z80PortWrite
{
UINT16 lowIoAddr;
UINT16 highIoAddr;
void (*IOCall)(UINT16, UINT8, struct z80PortWrite *);
void *pUserArea;
};
struct z80PortRead
{
UINT16 lowIoAddr;
UINT16 highIoAddr;
UINT16 (*IOCall)(UINT16, struct z80PortRead *);
void *pUserArea;
};
struct z80TrapRec
{
UINT16 trapAddr;
UINT8 skipCnt;
UINT8 origIns;
};
typedef union
{
UINT32 af;
struct
{
#ifdef WORDS_BIGENDIAN
UINT16 wFiller;
UINT8 a;
UINT8 f;
#else
UINT8 f;
UINT8 a;
UINT16 wFiller;
#endif
} half;
} reg_af;
#define z80AF z80af.af
#define z80A z80af.half.a
#define z80F z80af.half.f
typedef union
{
UINT32 bc;
struct
{
#ifdef WORDS_BIGENDIAN
UINT16 wFiller;
UINT8 b;
UINT8 c;
#else
UINT8 c;
UINT8 b;
UINT16 wFiller;
#endif
} half;
} reg_bc;
#define z80BC z80bc.bc
#define z80B z80bc.half.b
#define z80C z80bc.half.c
typedef union
{
UINT32 de;
struct
{
#ifdef WORDS_BIGENDIAN
UINT16 wFiller;
UINT8 d;
UINT8 e;
#else
UINT8 e;
UINT8 d;
UINT16 wFiller;
#endif
} half;
} reg_de;
#define z80DE z80de.de
#define z80D z80de.half.d
#define z80E z80de.half.e
typedef union
{
UINT32 hl;
struct
{
#ifdef WORDS_BIGENDIAN
UINT16 wFiller;
UINT8 h;
UINT8 l;
#else
UINT8 l;
UINT8 h;
UINT16 wFiller;
#endif
} half;
} reg_hl;
#define z80HL z80hl.hl
#define z80H z80hl.half.h
#define z80L z80hl.half.l
#define z80SP z80sp.sp
typedef union
{
UINT32 ix;
struct
{
#ifdef WORDS_BIGENDIAN
UINT16 wFiller;
UINT8 xh;
UINT8 xl;
#else
UINT8 xl;
UINT8 xh;
UINT16 wFiller;
#endif
} half;
} reg_ix;
#define z80IX z80ix.ix
#define z80XH z80ix.half.xh
#define z80XL z80ix.half.xl
typedef union
{
UINT32 iy;
struct
{
#ifdef WORDS_BIGENDIAN
UINT16 wFiller;
UINT8 yh;
UINT8 yl;
#else
UINT8 yl;
UINT8 yh;
UINT16 wFiller;
#endif
} half;
} reg_iy;
#define z80IY z80iy.iy
#define z80YH z80iy.half.yh
#define z80YL z80iy.half.yl
struct mz80context
{
UINT8 *z80Base;
struct MemoryReadByte *z80MemRead;
struct MemoryWriteByte *z80MemWrite;
struct z80PortRead *z80IoRead;
struct z80PortWrite *z80IoWrite;
UINT32 z80clockticks;
UINT32 z80iff;
UINT32 z80interruptMode;
UINT32 z80halted;
reg_af z80af;
reg_bc z80bc;
reg_de z80de;
reg_hl z80hl;
UINT32 z80afprime;
UINT32 z80bcprime;
UINT32 z80deprime;
UINT32 z80hlprime;
reg_ix z80ix;
reg_iy z80iy;
UINT32 z80sp;
UINT32 z80pc;
UINT32 z80nmiAddr;
UINT32 z80intAddr;
UINT32 z80rCounter;
UINT8 z80i;
UINT8 z80r;
UINT8 z80intPending;
};
// These are the enumerations used for register access. DO NOT ALTER THEIR
// ORDER! It must match the same order as in the mz80.c/mz80.asm files!
enum
{
#ifndef CPUREG_PC
CPUREG_PC = 0,
#endif
CPUREG_Z80_AF = 1,
CPUREG_Z80_BC,
CPUREG_Z80_DE,
CPUREG_Z80_HL,
CPUREG_Z80_AFPRIME,
CPUREG_Z80_BCPRIME,
CPUREG_Z80_DEPRIME,
CPUREG_Z80_HLPRIME,
CPUREG_Z80_IX,
CPUREG_Z80_IY,
CPUREG_Z80_SP,
CPUREG_Z80_I,
CPUREG_Z80_R,
CPUREG_Z80_A,
CPUREG_Z80_B,
CPUREG_Z80_C,
CPUREG_Z80_D,
CPUREG_Z80_E,
CPUREG_Z80_H,
CPUREG_Z80_L,
CPUREG_Z80_F,
CPUREG_Z80_CARRY,
CPUREG_Z80_NEGATIVE,
CPUREG_Z80_PARITY,
CPUREG_Z80_OVERFLOW,
CPUREG_Z80_HALFCARRY,
CPUREG_Z80_ZERO,
CPUREG_Z80_SIGN,
CPUREG_Z80_IFF1,
CPUREG_Z80_IFF2,
// Leave this here!
CPUREG_Z80_MAX_INDEX
};
extern UINT32 mz80exec(UINT32);
extern UINT32 mz80GetContextSize(void);
extern UINT32 mz80GetElapsedTicks(UINT32);
extern void mz80ReleaseTimeslice(void);
extern void mz80GetContext(void *);
extern void mz80SetContext(void *);
extern void mz80reset(void);
extern void mz80ClearPendingInterrupt(void);
extern UINT32 mz80int(UINT32);
extern UINT32 mz80nmi(void);
extern void mz80init(void);
extern void mz80shutdown(void);
extern UINT32 z80intAddr;
extern UINT32 z80nmiAddr;
// Debugger useful routines
extern UINT8 mz80SetRegisterValue(void *, UINT32, UINT32);
extern UINT32 mz80GetRegisterValue(void *, UINT32);
extern UINT32 mz80GetRegisterTextValue(void *, UINT32, UINT8 *);
extern UINT8 *mz80GetRegisterName(UINT32);
// Memory/IO read/write commands
#ifndef VALUE_BYTE
#define VALUE_BYTE 0
#endif
#ifndef VALUE_WORD
#define VALUE_WORD 1
#endif
#ifndef VALUE_DWORD
#define VALUE_DWORD 2
#endif
#ifndef VALUE_IO
#define VALUE_IO 3
#endif
extern void mz80WriteValue(UINT8 bWhat, UINT32 dwAddr, UINT32 dwData);
extern UINT32 mz80ReadValue(UINT8 bWhat, UINT32 dwAddr);
// Flag definitions
#define Z80_FLAG_CARRY 0x01
#define Z80_FLAG_NEGATIVE 0x02
#define Z80_FLAG_OVERFLOW_PARITY 0x04
#define Z80_FLAG_UNDEFINED1 0x08
#define Z80_FLAG_HALF_CARRY 0x10
#define Z80_FLAG_UNDEFINED2 0x20
#define Z80_FLAG_ZERO 0x40
#define Z80_FLAG_SIGN 0x80
#define IFF1 0x01
#define IFF2 0x02
typedef struct mz80context CONTEXTMZ80;
#ifdef __cplusplus
};
#endif
#endif // _MZ80_H_

809
cpu/mz80/mz80.txt Normal file
View file

@ -0,0 +1,809 @@
Multi-Z80 32 Bit emulator
Copyright 1996, 1997, 1998, 1999, 2000 - Neil Bradley, All rights reserved
MZ80 License agreement
-----------------------
(MZ80 Refers to both the assembly code emitted by makez80.c and makez80.c
itself)
MZ80 May be distributed in unmodified form to any medium.
MZ80 May not be sold, or sold as a part of a commercial package without
the express written permission of Neil Bradley (neil@synthcom.com). This
includes shareware.
Modified versions of MZ80 may not be publicly redistributed without author
approval (neil@synthcom.com). This includes distributing via a publicly
accessible LAN. You may make your own source modifications and distribute
MZ80 in source or object form, but if you make modifications to MZ80
then it should be noted in the top as a comment in makez80.c.
MZ80 Licensing for commercial applications is available. Please email
neil@synthcom.com for details.
Synthcom Systems, Inc, and Neil Bradley will not be held responsible for
any damage done by the use of MZ80. It is purely "as-is".
If you use MZ80 in a freeware application, credit in the following text:
"Multi-Z80 CPU emulator by Neil Bradley (neil@synthcom.com)"
must accompany the freeware application within the application itself or
in the documentation.
Legal stuff aside:
If you find problems with MZ80, please email the author so they can get
resolved. If you find a bug and fix it, please also email the author so
that those bug fixes can be propogated to the installed base of MZ80
users. If you find performance improvements or problems with MZ80, please
email the author with your changes/suggestions and they will be rolled in
with subsequent releases of MZ80.
The whole idea of this emulator is to have the fastest available 32 bit
Multi-Z80 emulator for the x86, giving maximum performance.
MZ80 Contact information
-------------------------
Author : Neil Bradley (neil@synthcom.com)
Distribution: ftp://ftp.synthcom.com/pub/emulators/cpu/makez80.zip (latest)
You can join the cpuemu mailing list on Synthcom for discussion of Neil
Bradley's Z80 (and other) CPU emulators. Send a message to
"cpuemu-request@synthcom.com" with "subscribe" in the message body. The
traffic is fairly low, and is used as a general discussion and announcement
for aforementioned emulators.
MZ80 Documentation
-------------------
MZ80 Is a full featured Z80 emulator coded in 32 bit assembly. It runs well
over a hundred games, in addition to it supporting many undocumented Z80
instructions required to run some of the Midway MCR games, Galaga, and
countless other wonderful Z80 based arcade games.
MZ80 Contains a makez80.c program that must be compiled. It is the program
that emits the assembly code that NASM will compile. This minimizes the
possibility of bugs creeping in to MZ80 for the different addressing modes
for each instruction. It requires NASM 0.97 or greater.
The goal of MZ80 is to have a high performance Z80 emulator that is capable
of running multiple emulations concurrently at full speed, even on lower-end
machines (486/33). MZ80 Harnesses the striking similarities of both the Z80
and the x86 instruction sets to take advantage of flag handling which greatly
reduces the time required to emulate a processor, so no extra time is spent
computing things that are already available in the native x86 processor,
allowing it to perform leaps and bounds over comparable C based Z80 emulators
on the same platform.
MZ80 Is designed exclusively for use with NASM, the Netwide Assembler. This
gives the ultimate in flexibility, as NASM can emit object files that work
with Watcom, Microsoft Visual C++ (4.0-current), DJGPP, Borland C++, and
gcc under FreeBSD or Linux. MZ80 Has been tested with each one of these
compilers and is known to work properly on each.
What's in the package
---------------------
MZ80.TXT - This text file
MAKEZ80.C - Multi Z80 32 Bit emulator emitter program
MZ80.H - C Header file for MZ80 functions
What's new in this release
--------------------------
Revision 3.4:
* Fixed the overflow flag not getting cleared in the SetOverflow()
routine. It caused strange problems with a handful of Genesis games
* Removed invalid instruction in the C version so that more
instructions will execute
Revision 3.3:
* Undocumented opcodes added to the C emitter
* Bug fix to the C emission that properly handles shared RAM regions
(I.E. with handlers that are NULL)
* Now using 32 bit registers to do register/memory access. Slight
speed increase (assembly version only)
Revision 3.2:
* R Register emulation now accurate with a real Z80
* mz80int() Called when interrupts are disabled causes the
z80intPending flag to be set, and an interrupt will be caused after
the execution of EI and the next instruction. See "IMPORTANT NOTE
ABOUT INTERRUPTS" below
* The instruction after EI executes fully before interrupt status is
checked. (as does a real Z80)
Revision 3.1:
* Fixed bug in memory dereference when handler was set to NULL (keeps
system from crashing or faulting)
* Removed the only stricmp() from the entire file and replaced it
with strcmp() so that stdlibs without it will compile
* Changed cyclesRemaining > 0 to cyclesRemaining >= 0 to be compatible
with the ASM core
* Removed additional sub [dwCyclesRemaining], 5 at the beginning of
mz80exec() (ASM Core only). Increases timing accuracy.
* NMIs And INTs add additional time to dwElapsedTicks as it should
* mz80ReleaseTimeslice() Sets remaining clocks to 0 instead of 1
Revision 3.0:
* All instructions validated against a real Z80. Used an ISA card
with a Z80 on it to validate flag handling, instruction handling,
timing, and other goodies. The only thing not implemented/emulated
is flag bit 3 & 5 emulation. Believed to be 100% bug free!
* 80% Speed improvement over version 2.7 of mz80
* z80stb.c Removed. Use -c to emit a C version of mz80! API compatible!
Note that this is mostly, but not fully, debugged, so consider the
C version a beta! It's at least healthier than z80stb.c was. The C
version does not include the undocumented Z80 instructions.
* mz80nmi() No longer trashes registers it uses when using -cs
* IN/OUT Instructions work properly when using -16
* IN A, (xxh) uses A as high 8 bits of I/O fetch address when using -16
* IM 0/IM 1 Description in documentation fixed
* Sizes of all context registers increased to 32 bits - for speed!
* IFF1/IFF2 Now properly emulated
* JR Instruction offset can fetch from $ffff and properly wrap
* LDIR/LDDR Instruction now won't go to completion - instead it will
run until BC=0 or the # of cycles to execute have expired. These
instructions used to run to completion - even beyond the # of cycles
left to execute
* INI/IND/INIR/INDR countdown bug fixed - it was decrementing B twice
for each IN! Whoops!
* If you specify NULL as a handler address to a memory region, mz80 will
use vpData as a pointer to where that block of data resides. Quite
useful for multiprocessor emulations that share the same memory.
* EDI Now keeps track of cycle counting for faster execution
* Modified memory region scanning code to use 32 bit registers instead
of their 16 bit counterparts
* Get/SetContext() uses rep movsd/movsb. Insignificant overall, but
why waste the time?
* Debugging routines added. See the "DEBUGGING" section below for more
information. NOTE: The debugging routines are not yet available in
the C emission.
* Timing done slightly differently now. Mz80 now executes one
instruction past the timing given on input. For example, mz80exec(0)
will cause a single instruction to be executed (thusly -ss was
removed).
Revision 2.7:
* Fixed OTIR/OTDR/INIR/INDR instructions so their 16 bit counterparts
work properly
* Emulation core 30-70% faster overall than 2.6 due to optimization to
the timing routines
* Replaced word reads/writes with a special word write routine rather
than the standard calling to read/write byte functions
* z80stb.c (the C equivalent of mz80) compiles properly now
* Fixed OS/2 text/segment issue
* Fixed bug in set/getCPU context that ensures that ES=DS and avoids
crashes. Caused crashes under OS/2 and other OS's
Revision 2.6:
* Emulator core 5-30% faster overall. Some 16 and 8 bit instructions
sped up when using their 32 bit equivalents.
* Fix to -l so that proper labels without leading and trailing
underscores so Linux/FreeBSD compiles will work properly
* Single step now executes the # of instructions passed in to z80exec()
instead of just 1 as it had in prior releases. This is only active
when the -ss option is used.
* The -nt option was added. This will cause the timing information to
not be added in, speeding up execution. Warning: Only do this if your
emulated target does not require instruction timing!
* Updated documentation errors
* C Version of mz80 (mz80.c) that is API compliant is distributed with
the archive (With kind permission of Edward Massey).
Revision 2.5:
* Fixed an unconditional flag being cleared in the ddcbxx instructions.
It caused Donkey Kong's barrels to not roll.
Revision 2.4:
* Fixed improper HALT handling (didn't advance the PTR when it should)
* Fixed SRL (IX+$xx) instruction so that carry wasn't trashed
* Fixed single stepping problems with it giving too much time to
any given instruction
* Fixed half carry flag handling with 16 bit SBC and ADD instructions
* Fixed DAA emulation so that parity flags weren't getting trashed
Revision 2.3:
* Fixed many stack handling bugs
* Timing problems fixed. The prior version was causing massive
overruns on maximum timeslices with some insutructions.
Revision 2.2:
* Fixed a bug in CPI/CPD/CPIR/CPDR that mishandled flags
* All known bugs are out of mz80 now
* Added the -cs option to route all stack operations through the
handlers (required for games like Galaga)
Revision 2.1:
* Fixed a bug in CPI/CPD/CPIR/CPDR that caused intermittent lockups.
Also fixed a bug that caused erratic behavior in several video games.
* Added INI/IND/INIR/INDR instruction group
* Added OUTI/OUTD/OTIR/OTDR instruction group
Revision 1.0:
* First release! The whole thing is new!
ASSEMBLING FOR USE WITH WATCOM C/C++
------------------------------------
Watcom, by default, uses register calling conventions, as does MZ80. To
create a proper emulator for Watcom:
makez80 MZ80.asm -x86
From here:
nasm -f win32 MZ80.asm
Link the MZ80.obj with your Watcom linker.
ASSEMBLING FOR USE WITH MICROSOFT VISUAL C++ AND BORLAND C++
--------------------------------------------------------------------
Visual C++ and Borland C++ use stack calling conventions by default. To
create a proper emulator for these compilers:
makez80 MZ80.asm -s -x86
For Visual C++ or Borland C++:
nasm -f win32 MZ80.asm
Link with your standard Visual C++ or Borland C++.
ASSEMBLING FOR USE WITH DJGPP, GCC/FREEBSD, OR GCC/LINUX
--------------------------------------------------------------------
DJGPP Uses stack calling conventions:
makez80 MZ80.asm -s -x86
To assemble:
nasm -f coff MZ80.asm
Link with your standard DJGPP linker. The same holds true for GCC under
FreeBSD or Linux. If you're using GCC, use the -l option to generate "plain"
labels so that gcc's linker will properly link things.
MAKEZ80 COMMAND LINE OPTIONS
----------------------------
-s - Use stack calling conventions (DJGPP, MSVC, Borland, etc...)
-cs - Force all stack operations to go through the Read/Write memory handlers.
This slows things down, but is useful when needed.
-16 - Treat all I/O input and output as 16 bit (BC)
-l - Create 'plain' labels - ones without leading and trailing underscores
-nt - Do not generate timing code - this speeds the emulator up, but the
downside is that no timing info is available.
-c - Emit a C mz80 emulator (API Compatible with the assembly version -
handy for porters!)
-x86 - Emit an assembly (x86) mz80 emulator
-os2 - Generate OS/2 compatible segmentation
IMPORTANT NOTE ABOUT INTERRUPTS
-------------------------------
A minor change was made between the 3.1 and 3.2 versions of makez80 in the
way that interrupts were handled.
On a real Z80, the !INT line is a level triggered interrupt, meaning that if
the interrupt line is held low, the Z80 will continue to take interrupts
immediately after the instruction after the EI instruction is executed until
the interrupt line is high again.
In 3.1, if an interrupt came in and interrupts were disabled, the interrupt
would never be "latched" for later execution. The Z80 does not have any
internal latching capabilities, however external hardware often does hold
the interrupt line low until the interrupt is executed, in effect, a latch.
I've only found one video game so far that requires the "raising/lowering"
of the interrupt line (Ataxx). In the games that I've tried, it has improved
performance, in some cases drastically, and in others not at all. This can
be accounted for by interrupts being taken now, where they were being dropped
in prior mz80 releases.
mz80 Emulates the most commonly used scenario. Now when mz80int() is executed
and a nonzero value is returned (indicating interrupts were disabled), it
will set z80intPending, and the interrupt will be taken after execution of
one instruction beyond the EI instruction.
So now, if mz80int() returns a nonzero value, that means an interrupt is
latched. If clearing this latch is desired or the old behavior of 3.1 is
desired, make a call to the mz80ClearPendingInterrupt() call. It's a 2
instruction call that has extremely small overhead and will not affect
performance in any measurable way.
In any case, MZ80 will now execute one instruction after EI regardless of
how much time is available to avoid the possibility of an interrupt request
coming in directly after the EI instruction.
STEPS TO EMULATION
------------------
NOTE: -16 Is a command line option that will treat all I/O as 16 bit. That
is, in an instruction like "IN AL, (C)", the addressed passed to the I/O
handler will be BC instead of just C. Bear this in mind when considering your
emulated platform.
There are a few steps you want to go through to get proper emulation, and a
few guidelines must be followed.
1) Create a MZ80CONTEXT
2) Create your virtual 64K memory space using whatever means of obtaining
memory you need to do.
3) Set mz80Base in your context to be the base of your 64K memory space
4) Load up your image to be emulated within that 64K address space.
5) Set z80IoRead and z80IoWrite to their appropriate structure arrays. Here's
an example:
struct z80PortRead ReadPorts[] =
{
{0x10, 0x1f, SoundChip1Read},
{0x20, 0x2f, SoundChip2Read}
{(UINT32) -1, (UINT32) -1, NULL}
};
When an IN instruction occurs, mz80 will probe this table looking for a
handler to the address of the "IN" instruction. If it is found in the list,
it's up to the handler to return the proper value. Otherwise, a value of
0ffh is returned internally if no handler for that I/O address is found. In
the case above, SoundChip1Read is called when the I/O address is between 0x10-
0x1f. A similar structure is used for I/O writes as well (OUT):
struct z80PortWrite WritePorts[] =
{
{0x20, 0x2f, SoundChip2Write},
{0x30, 0x36, VideoCtrlWrite},
{(UINT32) -1, (UINT32) -1, NULL}
}
Of course, this does the opposite that the z80PortRead struct, and instead
looks for a handler to hand some data to. If it doesn't find an appropriate
handler, nothing happens.
6) Set mz80MemoryRead & mz80MemoryWrite to their appropriate structure
arrays. Here is an example:
struct MemoryWriteByte GameWrite[] =
{
{0x3000, 0x3fff, VideoWrite},
{0x4000, 0x4fff, SpriteWrite},
{(UINT32) -1, (UINT32) -1, NULL}
};
The above example says that any time a write occurs in the 0x3000-0x3fff
range, call the VideoWrite routine. The same holds true for the SpriteWrite
region as well.
NOTE: When your write handler is called, it is passed the address of the
write and the data that is to be written to it. If your handler doesn't
write the data to the virtual image, the mz80 internal code will not.
NOTE: These routines will *NOT* be called when execution asks for these
addresses. It will only call them when a particular instruction uses the
memory at these locations.
If you wish for a region to be RAM, just leave it out of your memory region
exception list. The WriteMemoryByte routine will treat it as read/write
RAM and will write to mz80Base + addr directly.
If you wish to protect ROM regions (not often necessary), create a range that
encompasses the ROM image, and have it call a routine that does nothing. This
will prevent data from being written back onto the ROM image.
Leave your last entry in the table as shown above, with a null handler and
0xffffffff-0xffffffff as your read address. Even though the Z80 only
addresses 64K of space, the read/write handlers are defined as 32 bit so
the compiler won't pass junk in the upper 16 bits of the address lines. Not
only that, it allows orthoganality for future CPU emulators that may use
these upper bits.
You can do a mz80GetContext() if you'd like to read the current context of
the registers. Note that by the time your handler gets called, the program
counter will be pointing to the *NEXT* instruction.
struct MemoryReadByte GameRead[] =
{
{0x2000, 0x200f, ReadHandler},
{(UINT32) -1, (UINT32) -1, NULL}
};
Same story here. If you have a special handler for an attempted read at a
particular address, place its range in this table and create a handler
routine for it.
If you don't define a handler for a particular region, then the ReadMemoryByte
in mz80.ASM will actually read the value out of mz80Base + the offset
required to complete the instruction.
7) Set the intAddr and nmiAddr to the addresses where you want mz80 to start
executing when an interrupt or NMI happens. Take a look at the section
entitled "INTERRUPTS" below for more information on this.
8) Call mz80SetContext() on your Z80 context
9) Call mz80Reset(). This will prime the program counter and cause a virtual
CPU-wide reset.
10) Once you have those defined, you're ready to begin emulation. There's some
sort of main loop that you'll want. Maybe something like:
while (hit == 0)
{
if (lastSec != (UINT32) time(0))
{
diff = (mz80clockticks - prior) / 3000000;
printf("%ld Clockticks, %ld frames, %ld Times original speed\n", MZ80clockticks - prior, frames, diff);
frames = 0;
prior = mz80clockticks;
lastSec = time(0);
if (kbhit())
{
getch();
hit = 1;
}
}
/* 9000 Cycles per NMI (~3 milliseconds @ 3MHZ) */
dwResult = mz80exec(9000);
mz80clockticks += mz80GetElapsedTicks(TRUE);
mz80nmi();
/* If the result is not 0x80000000, it's an address where
an invalid instruction was hit. */
if (0x80000000 != dwResult)
{
mz80GetContext(&sCpu1);
printf("Invalid instruction at %.2x\n", sCpu1.MZ80pc);
exit(1);
}
}
Call mz80exec() With the # of virtual CPU cycles you'd like mz80 to
execute. Be sure to use the mz80GetElapsedTicks() call *AFTER* execution to
see how many virtual CPU cycles it actually executed. For example, if you tell
mz80 to execute 500 virtual CPU cycles, it will execute slightly more. Anything
from 500 to 524 (24 cycles being the longest any 1 instruction takes in the
Z80).
Use the mz80GetElapsedTicks() call for more accurate cycle counting. Of course,
this is only if you have *NOT* included the -nt option.
If you pass FALSE to the mz80GetElapsedTicks() function, the internal CPU
elapsed tick clock will not be reset. The elapsed tick counter is something
that continues to increase every emulated instruction, and like an odometer,
will keep counting unless you pass TRUE to mz80GetElapsedTicks(), of which
case it will return you the current value of the elapsed ticks and set it to
0 when complete.
NOTE: The bigger value you pass to mz80exec, the greater benefit you get out
of the virtual registers persisting within the emulator, and it will run
faster. Pass in a value that is large enough to take advantage of it, but
not so often that you can't handle nmi or int's properly.
If you wish to create a virtual NMI, call mz80nmi(), and it will be taken
the next time you call mz80exec, or alternately if you have a handler call
mz80nmi/mz80int(), the interrupt will be taken upon return. Note that
mz80nmi() doesn't actually execute any code - it only primes the emulator to
begin executing NMI/INT code.
NOTE: mz80int() is defined with a UINT32 as a formal parameter. Depending
upon what interrupt mode you're executing in (described later), it may or may
not take a value.
NMI's can interrupt interrupts, but not the other way around - just like a
real Z80. If your program is already in an interrupt, another one will not be
taken. The same holds true for an NMI - Just like a real Z80!
MUTLI-PROCESSOR NOTES
---------------------
Doing multi processor support is a bit trickier, but is still fairly straight-
forward.
For each processor to be emulated, go through steps 1-7 above - giving each
CPU its own memory space, register storage, and read/write handlers.
EXECUTION OF MULTI-CPUS:
-------------------------
When you're ready to execute a given CPU, do the following:
mz80SetContext(contextPointer);
This will load up all information saved before into the emulator and ready it
for execution. Then execute step 7 above to do your virtual NMI's, interrupts,
etc... All CPU state information is saved within a context.
When the execution cycle is complete, do the following to save the updated
context away for later:
mz80GetContext(contextPointer);
Give each virtual processor a slice of time to execute. Don't make the values
too small or it will spend its time swapping contexts. While this in itself
isn't particularly CPU expensive, the more time you spend executing the better.
mz80 Keeps all of the Z80 register in native x86 register (including most
of the flags, HL, BC, and A). If no context swap is needed, then you get the
added advantage of the register storage. For example, let's say you were
running two Z80s - one at 2.0MHZ and one at 3.0MHZ. An example like this
might be desirable:
mz80SetContext(cpu1Context); // Set CPU #1's information
mz80exec(2000); // 2000 Instructions for 2.0MHZ CPU
mz80GetContext(cpu1Context); // Get CPU #1's state info
mz80SetContext(cpu2Context); // Set CPU #2's state information
mz80exec(3000); // 3000 Instructions for 3.0MHZ CPU
mz80GetContext(cpu2Context); // Get CPU #2's state information
This isn't entirely realistic, but if you keep the instruction or timing
ratios between the emulated CPUs even, then timing is a bit more accurate.
NOTE: If you need to make a particular CPU give up its own time cycle because
of a memory read/write, simply trap a particular address (say, a write to a
slave processor) and call mz80ReleaseTimeslice(). It will not execute any
further instructions, and will give up its timeslice. Put this in your
read/write memory trap.
NOTE: You are responsible for "holding back" the processor emulator from
running too fast.
INTERRUPTS
----------
The Z80 has three interrupt modes: IM 0 - IM 2. Each act differently. Here's
a description of each:
IM 0
This mode will cause the Z80 to be able to pull a "single byte instruction"
off the bus when an interrupt occurs. Since we're not doing bus cycle
emulation, it acts identically to mode 1 (described below). The formal
parameter to mz80int() is ignored. There is really no point in actually
emulating the instruction execution since any instruction that would be
executed would be a branch instruction!
IM 1
This mode is the "default" mode that the Z80 (and mz80 for that matter) comes
up in. When you call mz80reset(), the interrupt address is set to 38h and
the NMI address is set to 66h. So when you're in IM 1 and mz80int() is
called, the formal parameter is ignored and the z80intAddr/z80nmiAddr values
are appropriately loaded into the program counter.
IM 2
This mode causes the Z80 to read the upper 8 bits from the current value
of the "I" register, and the lower 8 bits from the value passed into mz80int().
So, if I contained 35h, and you did an mz80int(0x64), then an interrupt at
address 3564h would be taken. Simple!
OTHER GOODIES
-------------
MZ80 Has a nice feature for allowing the same handler to handle different
data regions on a single handler. Here's an example:
struct PokeyDataStruct Pokey1;
struct PokeyDataStruct Pokey2;
struct MemoryWriteByte GameWrite[] =
{
{0x1000, 0x100f, PokeyHandler, Pokey1},
{0x1010, 0x101f, PokeyHandler, Pokey2},
{(UINT32) -1, (UINT32) -1, NULL}
};
void PokeyHandler(UINT32 dwAddr, UINT8 bData, struct sMemoryWriteByte *psMem)
{
struct PokeyDataStruct *psPokey = psMem->pUserArea;
// Do stuff with psPokey here....
}
This passes in the pointer to the sMemoryWriteByte structure that caused
the handler to be called. The pUserArea is a user defined address that can
be anything. It is not necessary to fill it in with anything or even
initialize it if the handler doesn't actually use it.
This allows a single handler to handle multiple data references. This is
particularly useful when handling sound chip emulation, where there might
be more than one of a given device. Sure beats having multiple unique
handlers that are identical with the exception of the data area where it
writes! This allows a good deal of flexibility.
The same construct holds for MemoryReadByte, z80PortRead, and z80PortWrite,
so all can take advantage of this feature.
SHARED MEMORY FEATURES
----------------------
MZ80 Also has another useful feature for dealing with shared memory regions:
UINT8 bSharedRAM[0x100];
struct MemoryWriteByte Processor1[] =
{
{0x1000, 0x10ff, NULL, bSharedRAM},
{(UINT32) -1, (UINT32) -1, NULL}
};
struct MemoryWriteByte Processor2[] =
{
{0x1000, 0x10ff, NULL, bSharedRAM},
{(UINT32) -1, (UINT32) -1, NULL}
};
If the handler address is NULL, mz80 will look at the pUserArea field as a
pointer to RAM to read from/write to. This comes in extremely handy when you
have an emulation that requires two or more processors writing to the same
memory block. And it's lots faster than creating a handler that writes to
a common area as well.
DEBUGGING
---------
Several new functions have been added to mz80 that assist the emulator
author by providing a standard set of functions for register access:
UINT8 mz80SetRegisterValue(void *pContext, UINT32 dwRegister, UINT32 dwValue)
This allows setting of any register within the Z80. The register field can be
one of the following values (defined in mz80.h):
CPUREG_PC
CPUREG_Z80_AF
CPUREG_Z80_BC
CPUREG_Z80_DE
CPUREG_Z80_HL
CPUREG_Z80_AFPRIME
CPUREG_Z80_BCPRIME
CPUREG_Z80_DEPRIME
CPUREG_Z80_HLPRIME
CPUREG_Z80_IX
CPUREG_Z80_IY
CPUREG_Z80_SP
CPUREG_Z80_I
CPUREG_Z80_R
CPUREG_Z80_A
CPUREG_Z80_B
CPUREG_Z80_C
CPUREG_Z80_D
CPUREG_Z80_E
CPUREG_Z80_H
CPUREG_Z80_L
CPUREG_Z80_F
CPUREG_Z80_CARRY
CPUREG_Z80_NEGATIVE
CPUREG_Z80_PARITY
CPUREG_Z80_OVERFLOW
CPUREG_Z80_HALFCARRY
CPUREG_Z80_ZERO
CPUREG_Z80_SIGN
CPUREG_Z80_IFF1
CPUREG_Z80_IFF2
Each individual register's value can be set, including the flags at the end.
The only valid values for the flags are 1 and 0. Setting these will
automatically adjust the "F" register.
If pContext is NULL, then the registers in the currently active context are
changed. If pContext points to a non-NULL area, that area is assumed to be
a CONTEXTMZ80 structure where the new register value will be written.
If mz80SetRegisterValue() returns a nonzero value, either the register value
or register is out of range or invalid.
UINT32 mz80GetRegisterValue(void *pContext, UINT32 dwRegister)
This returns the value of the register given on input (listed above as
CPUREG_Z80_xxxxx). Flag values will be 1 or 0.
If pContext is NULL, then the registers in the currently active context are
read. If pContext points to a non-NULL area, that area is assumed to be
a CONTEXTMZ80 structure from which register values are pulled.
UINT32 mz80GetRegisterTextValue(void *pContext, UINT32 dwRegister,
UINT8 *pbTextArea)
This returns the textual representation of the value of a given register.
It is a text printable string that can be used in sprintf() statements and
the like. This function is useful because different representations for
registers (like flags) can be a group of 8 flag bytes instead of a single
value.
On entry, pContext being set to NULL indicates that mz80 should get the
register value from the currently active context. Otherwise, it is assumed
to be pointing to a CONTEXTMZ80 structure, which contains the value of the
registers to be read.
pbTextArea points to a buffer where the value text can be written. This points
to a user supplied buffer.
On exit, if any nonzero value is encountered, either the register # is out
of range or pbTextArea is NULL.
UINT8 *mz80GetRegisterName(UINT32 dwRegister)
This returns a pointer to the textual name of the register passed in. NULL
Is returned if the register index (CPUREG_Z80_xxxx table described above) is
out of range. DO NOT MODIFY THE TEXT! It is static data.
FINAL NOTES
-----------
I have debugged MZ80.ASM to the best of my abilities. There might still be
a few bugs floating around in it, but I'm not aware of any. I've validated
all instructions (That I could) against a custom built Z80 on an ISA card
(that fits in a PC) so I'm quite confident that it works just like a real
Z80.
If you see any problems, please point them out to me, as I am eager to make
mz80 the best emulator that I can.
If you have questions, comments, etc... about mz80, please don't hesitate
to send me an email. And if you use mz80 in your emulator, I'd love to take
a look at your work. If you have special needs, or need implementation
specific hints, feel free to email me, Neil Bradley (neil@synthcom.com). I
will do my best to help you.
Enjoy!
Neil Bradley
neil@synthcom.com

93
platform/gp2x/940.c Normal file
View file

@ -0,0 +1,93 @@
#include "940shared.h"
/* this code assumes that we live @ 0x3000000 bank */
//static volatile unsigned short *gp2x_memregs = (void *) 0x0xbd000000;
//static volatile unsigned long *gp2x_memregl = (void *) 0x0xbd000000;
static _940_data_t *shared_data = (_940_data_t *) 0x100000;
static _940_ctl_t *shared_ctl = (_940_ctl_t *) 0x200000;
YM2612 *ym2612_940;
int *mix_buffer;
// from init.s
void wait_irq(void);
void spend_cycles(int c);
void cache_clean(void);
void cache_clean_flush(void);
// asm volatile ("mov r0, #0" ::: "r0");
// asm volatile ("mcr p15, 0, r0, c7, c6, 0" ::: "r0"); /* flush dcache */
// asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer */
void Main940(int startvector)
{
ym2612_940 = &shared_data->ym2612;
mix_buffer = shared_data->mix_buffer;
// debug
shared_ctl->vstarts[startvector]++;
asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0");
/* unmask IRQs */
for (;; shared_ctl->loopc++)
{
/*
while (!shared_ctl->busy)
{
//shared_ctl->waitc++;
spend_cycles(256);
}
*/
if (!shared_ctl->busy)
{
wait_irq();
}
switch (shared_ctl->job)
{
case JOB940_YM2612INIT:
shared_ctl->writebuff0[0] = shared_ctl->writebuff1[0] = 0xffff;
YM2612Init_(shared_ctl->baseclock, shared_ctl->rate);
break;
case JOB940_YM2612RESETCHIP:
YM2612ResetChip_();
break;
case JOB940_PICOSTATELOAD:
YM2612PicoStateLoad_();
break;
case JOB940_YM2612UPDATEONE: {
int i, dw, *wbuff;
if (shared_ctl->writebuffsel == 1) {
wbuff = (int *) shared_ctl->writebuff1;
} else {
wbuff = (int *) shared_ctl->writebuff0;
}
/* playback all writes */
for (i = 2048/2; i > 0; i--) {
UINT16 d;
dw = *wbuff++;
d = dw;
if (d == 0xffff) break;
YM2612Write_(d >> 8, d);
d = (dw>>16);
if (d == 0xffff) break;
YM2612Write_(d >> 8, d);
}
YM2612UpdateOne_(0, shared_ctl->length, shared_ctl->stereo);
// cache_clean_flush();
cache_clean();
// asm volatile ("mov r0, #0" ::: "r0");
// asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer, should be done on nonbuffered write */
break;
}
}
shared_ctl->busy = 0;
}
}

View file

@ -0,0 +1,451 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include "940shared.h"
#include "gp2x.h"
#include "emu.h"
#include "menu.h"
#include "asmutils.h"
/* we will need some gp2x internals here */
extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */
extern volatile unsigned long *gp2x_memregl;
static unsigned char *shared_mem = 0;
static _940_data_t *shared_data = 0;
static _940_ctl_t *shared_ctl = 0;
int crashed_940 = 0;
/***********************************************************/
#define MAXOUT (+32767)
#define MINOUT (-32768)
/* limitter */
#define Limit(val, max,min) { \
if ( val > max ) val = max; \
else if ( val < min ) val = min; \
}
/* these will be managed locally on our side */
extern int *ym2612_dacen;
extern INT32 *ym2612_dacout;
extern void *ym2612_regs;
static UINT8 *REGS = 0; /* we will also keep local copy of regs for savestates and such */
static INT32 addr_A1; /* address line A1 */
static int dacen;
static INT32 dacout;
static UINT8 ST_address; /* address register */
static UINT8 ST_status; /* status flag */
static UINT8 ST_mode; /* mode CSM / 3SLOT */
static int ST_TA; /* timer a */
static int ST_TAC; /* timer a maxval */
static int ST_TAT; /* timer a ticker */
static UINT8 ST_TB; /* timer b */
static int ST_TBC; /* timer b maxval */
static int ST_TBT; /* timer b ticker */
static int writebuff_ptr = 0;
/* OPN Mode Register Write */
static void set_timers( int v )
{
/* b7 = CSM MODE */
/* b6 = 3 slot mode */
/* b5 = reset b */
/* b4 = reset a */
/* b3 = timer enable b */
/* b2 = timer enable a */
/* b1 = load b */
/* b0 = load a */
ST_mode = v;
/* reset Timer b flag */
if( v & 0x20 )
ST_status &= ~2;
/* reset Timer a flag */
if( v & 0x10 )
ST_status &= ~1;
}
/* YM2612 write */
/* a = address */
/* v = value */
/* returns 1 if sample affecting state changed */
int YM2612Write_940(unsigned int a, unsigned int v)
{
int addr; //, ret=1;
v &= 0xff; /* adjust to 8 bit bus */
a &= 3;
switch( a ) {
case 0: /* address port 0 */
ST_address = v;
addr_A1 = 0;
//ret=0;
break;
case 1: /* data port 0 */
if (addr_A1 != 0) {
return 0; /* verified on real YM2608 */
}
addr = ST_address;
REGS[addr] = v;
switch( addr & 0xf0 )
{
case 0x20: /* 0x20-0x2f Mode */
switch( addr )
{
case 0x24: { // timer A High 8
int TAnew = (ST_TA & 0x03)|(((int)v)<<2);
if(ST_TA != TAnew) {
// we should reset ticker only if new value is written. Outrun requires this.
ST_TA = TAnew;
ST_TAC = (1024-TAnew)*18;
ST_TAT = 0;
}
return 0;
}
case 0x25: { // timer A Low 2
int TAnew = (ST_TA & 0x3fc)|(v&3);
if(ST_TA != TAnew) {
ST_TA = TAnew;
ST_TAC = (1024-TAnew)*18;
ST_TAT = 0;
}
return 0;
}
case 0x26: // timer B
if(ST_TB != v) {
ST_TB = v;
ST_TBC = (256-v)<<4;
ST_TBC *= 18;
ST_TBT = 0;
}
return 0;
case 0x27: /* mode, timer control */
set_timers( v );
break; // other side needs ST.mode for 3slot mode
case 0x2a: /* DAC data (YM2612) */
dacout = ((int)v - 0x80) << 6; /* level unknown (notaz: 8 seems to be too much) */
return 0;
case 0x2b: /* DAC Sel (YM2612) */
/* b7 = dac enable */
dacen = v & 0x80;
break; // other side has to know this
default:
break;
}
break;
}
break;
case 2: /* address port 1 */
ST_address = v;
addr_A1 = 1;
//ret=0;
break;
case 3: /* data port 1 */
if (addr_A1 != 1) {
return 0; /* verified on real YM2608 */
}
addr = ST_address | 0x100;
REGS[addr] = v;
break;
}
if(currentConfig.EmuOpt & 4) {
/* queue this write for 940 */
if (writebuff_ptr < 2047) {
if (shared_ctl->writebuffsel == 1) {
shared_ctl->writebuff0[writebuff_ptr++] = (a<<8)|v;
} else {
shared_ctl->writebuff1[writebuff_ptr++] = (a<<8)|v;
}
} else {
printf("warning: writebuff_ptr > 2047\n");
}
}
return 0; // cause the engine to do updates once per frame only
}
UINT8 YM2612Read_940(void)
{
return ST_status;
}
int YM2612PicoTick_940(int n)
{
//int ret = 0;
// timer A
if(ST_mode & 0x01 && (ST_TAT+=64*n) >= ST_TAC) {
ST_TAT -= ST_TAC;
if(ST_mode & 0x04) ST_status |= 1;
// CSM mode total level latch and auto key on
/* FIXME
if(ST_mode & 0x80) {
CSMKeyControll( &(ym2612_940->CH[2]) ); // Vectorman2, etc.
ret = 1;
}
*/
}
// timer B
if(ST_mode & 0x02 && (ST_TBT+=64*n) >= ST_TBC) {
ST_TBT -= ST_TBC;
if(ST_mode & 0x08) ST_status |= 2;
}
return 0;
}
static void wait_busy_940(void)
{
int i;
#if 0
printf("940 busy, entering wait loop.. (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);
for (i = 0; i < 8; i++)
printf("%i ", shared_ctl->vstarts[i]);
printf(")\n");
for (i = 0; shared_ctl->busy; i++)
{
spend_cycles(1024); /* needs tuning */
}
printf("wait iterations: %i\n", i);
#else
for (i = 0; shared_ctl->busy && i < 0x10000; i++)
spend_cycles(4*1024);
if (i < 0x10000) return;
/* 940 crashed */
printf("940 crashed (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);
for (i = 0; i < 8; i++)
printf("%i ", shared_ctl->vstarts[i]);
printf(")\n");
strcpy(menuErrorMsg, "940 crashed.");
engineState = PGS_Menu;
crashed_940 = 1;
#endif
}
static void add_job_940(int job)
{
shared_ctl->job = job;
shared_ctl->busy = 1;
gp2x_memregs[0x3B3E>>1] = 0xffff; // cause an IRQ for 940
}
void YM2612PicoStateLoad_940(void)
{
int i, old_A1 = addr_A1;
if (shared_ctl->busy) wait_busy_940();
// feed all the registers and update internal state
for(i = 0; i < 0x100; i++) {
YM2612Write_940(0, i);
YM2612Write_940(1, REGS[i]);
}
for(i = 0; i < 0x100; i++) {
YM2612Write_940(2, i);
YM2612Write_940(3, REGS[i|0x100]);
}
addr_A1 = old_A1;
add_job_940(JOB940_PICOSTATELOAD);
}
static void internal_reset(void)
{
writebuff_ptr = 0;
ST_mode = 0;
ST_status = 0; /* normal mode */
ST_TA = 0;
ST_TAC = 0;
ST_TB = 0;
ST_TBC = 0;
dacen = 0;
}
extern char **g_argv;
/* none of the functions in this file should be called before this one */
void YM2612Init_940(int baseclock, int rate)
{
printf("YM2612Init_940()\n");
//printf("sizeof(*shared_data): %i (%x)\n", sizeof(*shared_data), sizeof(*shared_data));
//printf("sizeof(*shared_ctl): %i (%x)\n", sizeof(*shared_ctl), sizeof(*shared_ctl));
Reset940(1);
Pause940(1);
gp2x_memregs[0x3B46>>1] = 0xffff; // clear pending DUALCPU interrupts for 940
gp2x_memregs[0x3B42>>1] = 0xffff; // enable DUALCPU interrupts for 940
gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller
if (shared_mem == NULL)
{
shared_mem = (unsigned char *) mmap(0, 0x210000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x3000000);
if(shared_mem == MAP_FAILED)
{
printf("mmap(shared_data) failed with %i\n", errno);
exit(1);
}
shared_data = (_940_data_t *) (shared_mem+0x100000);
/* this area must not get buffered on either side */
shared_ctl = (_940_ctl_t *) (shared_mem+0x200000);
crashed_940 = 1;
}
if (crashed_940)
{
unsigned char ucData[1024];
int nRead, i, nLen = 0;
char binpath[1024];
FILE *fp;
strncpy(binpath, g_argv[0], 1023);
binpath[1023] = 0;
for (i = strlen(binpath); i > 0; i--)
if (binpath[i] == '/') { binpath[i] = 0; break; }
strcat(binpath, "/code940.bin");
fp = fopen(binpath, "rb");
if(!fp)
{
memset(gp2x_screen, 0, 320*240);
gp2x_text_out8(10, 100, "failed to open required file:");
gp2x_text_out8(10, 110, "code940.bin");
gp2x_video_flip();
printf("failed to open %s\n", binpath);
exit(1);
}
while(1)
{
nRead = fread(ucData, 1, 1024, fp);
if(nRead <= 0)
break;
memcpy(shared_mem + nLen, ucData, nRead);
nLen += nRead;
}
fclose(fp);
crashed_940 = 0;
}
memset(shared_data, 0, sizeof(*shared_data));
memset(shared_ctl, 0, sizeof(*shared_ctl));
REGS = YM2612GetRegs();
ym2612_dacen = &dacen;
ym2612_dacout = &dacout;
internal_reset();
/* now cause 940 to init it's ym2612 stuff */
shared_ctl->baseclock = baseclock;
shared_ctl->rate = rate;
shared_ctl->job = JOB940_YM2612INIT;
shared_ctl->busy = 1;
/* start the 940 */
Reset940(0);
Pause940(0);
// YM2612ResetChip_940(); // will be done on JOB940_YM2612INIT
}
void YM2612ResetChip_940(void)
{
printf("YM2612ResetChip_940()\n");
if (shared_data == NULL) {
printf("YM2612ResetChip_940: reset before init?\n");
return;
}
if (shared_ctl->busy) wait_busy_940();
internal_reset();
add_job_940(JOB940_YM2612RESETCHIP);
}
void YM2612UpdateOne_940(short *buffer, int length, int stereo)
{
int i, *mix_buffer = shared_data->mix_buffer;
//printf("YM2612UpdateOne_940()\n");
if (shared_ctl->busy) wait_busy_940();
//printf("940 (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc);
//for (i = 0; i < 8; i++)
// printf("%i ", shared_ctl->vstarts[i]);
//printf(")\n");
/* mix data from previous go */
if (stereo) {
int *mb = mix_buffer;
for (i = length; i > 0; i--) {
int l, r;
l = r = *buffer;
l += *mb++, r += *mb++;
Limit( l, MAXOUT, MINOUT );
Limit( r, MAXOUT, MINOUT );
*buffer++ = l; *buffer++ = r;
}
} else {
for (i = 0; i < length; i++) {
int l = mix_buffer[i];
l += buffer[i];
Limit( l, MAXOUT, MINOUT );
buffer[i] = l;
}
}
//printf("new writes: %i\n", writebuff_ptr);
if (shared_ctl->writebuffsel == 1) {
shared_ctl->writebuff0[writebuff_ptr] = 0xffff;
} else {
shared_ctl->writebuff1[writebuff_ptr] = 0xffff;
}
writebuff_ptr = 0;
/* give 940 another job */
shared_ctl->writebuffsel ^= 1;
shared_ctl->length = length;
shared_ctl->stereo = stereo;
add_job_940(JOB940_YM2612UPDATEONE);
//spend_cycles(512);
//printf("SRCPND: %08lx, INTMODE: %08lx, INTMASK: %08lx, INTPEND: %08lx\n",
// gp2x_memregl[0x4500>>2], gp2x_memregl[0x4504>>2], gp2x_memregl[0x4508>>2], gp2x_memregl[0x4510>>2]);
}

View file

@ -0,0 +1,9 @@
void YM2612Init_940(int baseclock, int rate);
void YM2612ResetChip_940(void);
void YM2612UpdateOne_940(short *buffer, int length, int stereo);
int YM2612Write_940(unsigned int a, unsigned int v);
unsigned char YM2612Read_940(void);
int YM2612PicoTick_940(int n);
void YM2612PicoStateLoad_940(void);

174
platform/gp2x/940init.s Normal file
View file

@ -0,0 +1,174 @@
.global code940
code940: @ interrupt table:
b .b_reset @ reset
b .b_undef @ undefined instructions
b .b_swi @ software interrupt
b .b_pabort @ prefetch abort
b .b_dabort @ data abort
b .b_reserved @ reserved
b .b_irq @ IRQ
b .b_fiq @ FIQ
@ test
.b_reset:
mov r12, #0
b .Begin
.b_undef:
mov r12, #1
b .Begin
.b_swi:
mov r12, #2
b .Begin
.b_pabort:
mov r12, #3
b .Begin
.b_dabort:
mov r12, #4
b .Begin
.b_reserved:
mov r12, #5
b .Begin
.b_irq:
mov r12, #6
mov sp, #0x100000 @ reset stack
sub sp, sp, #4
mov r1, #0xbd000000 @ assume we live @ 0x3000000 bank
orr r2, r1, #0x3B00
orr r2, r2, #0x0046
mvn r3, #0
strh r3, [r2] @ clear any pending interrupts from the DUALCPU unit
orr r2, r1, #0x4500
str r3, [r2] @ clear all pending interrupts in irq controller's SRCPND register
orr r2, r2, #0x0010
str r3, [r2] @ clear all pending interrupts in irq controller's INTPND register
b .Enter
.b_fiq:
mov r12, #7
b .Begin
.Begin:
mov sp, #0x100000 @ set the stack top (1M)
sub sp, sp, #4 @ minus 4
@ set up memory region 0 -- the whole 4GB address space
mov r0, #(0x1f<<1)|1 @ region data
mcr p15, 0, r0, c6, c0, 0 @ opcode2 ~ data/instr
mcr p15, 0, r0, c6, c0, 1
@ set up region 1 which is the first 2 megabytes.
mov r0, #(0x14<<1)|1 @ region data
mcr p15, 0, r0, c6, c1, 0
mcr p15, 0, r0, c6, c1, 1
@ set up region 2: 64k 0x200000-0x210000
mov r0, #(0x0f<<1)|1
orr r0, r0, #0x200000
mcr p15, 0, r0, c6, c2, 0
mcr p15, 0, r0, c6, c2, 1
@ set up region 3: 64k 0xbd000000-0xbd010000 (hw control registers)
mov r0, #(0x0f<<1)|1
orr r0, r0, #0xbd000000
mcr p15, 0, r0, c6, c3, 0
mcr p15, 0, r0, c6, c3, 1
@ set region 1 to be cacheable (so the first 2M will be cacheable)
mov r0, #2
mcr p15, 0, r0, c2, c0, 0
mcr p15, 0, r0, c2, c0, 1
@ set region 1 to be bufferable too (only data)
mcr p15, 0, r0, c3, c0, 0
@ set protection, allow accsess only to regions 1 and 2
mov r0, #(3<<6)|(3<<4)|(3<<2)|(0) @ data: [full, full, full, no access] for regions [3 2 1 0]
mcr p15, 0, r0, c5, c0, 0
mov r0, #(0<<6)|(0<<4)|(3<<2)|(0) @ instructions: [no access, no, full, no]
mcr p15, 0, r0, c5, c0, 1
mrc p15, 0, r0, c1, c0, 0 @ fetch current control reg
orr r0, r0, #1 @ 0x00000001: enable protection unit
orr r0, r0, #4 @ 0x00000004: enable D cache
orr r0, r0, #0x1000 @ 0x00001000: enable I cache
orr r0, r0, #0xC0000000 @ 0xC0000000: async+fastbus
mcr p15, 0, r0, c1, c0, 0 @ set control reg
@ flush (invalidate) the cache (just in case)
mov r0, #0
mcr p15, 0, r0, c7, c6, 0
.Enter:
mov r0, r12
bl Main940
@ we should never get here
.b_deadloop:
b .b_deadloop
@ so asm utils are also defined here:
.global spend_cycles @ c
spend_cycles:
mov r0, r0, lsr #2 @ 4 cycles/iteration
sub r0, r0, #2 @ entry/exit/init
.sc_loop:
subs r0, r0, #1
bpl .sc_loop
bx lr
@ clean-flush function from ARM940T technical reference manual
.global cache_clean_flush
cache_clean_flush:
mov r1, #0 @ init line counter
ccf_outer_loop:
mov r0, #0 @ segment counter
ccf_inner_loop:
orr r2, r1, r0 @ make segment and line address
mcr p15, 0, r2, c7, c14, 2 @ clean and flush that line
add r0, r0, #0x10 @ incremet secment counter
cmp r0, #0x40 @ complete all 4 segments?
bne ccf_inner_loop
add r1, r1, #0x04000000 @ increment line counter
cmp r1, #0 @ complete all lines?
bne ccf_outer_loop
bx lr
@ clean-only version
.global cache_clean
cache_clean:
mov r1, #0 @ init line counter
cf_outer_loop:
mov r0, #0 @ segment counter
cf_inner_loop:
orr r2, r1, r0 @ make segment and line address
mcr p15, 0, r2, c7, c10, 2 @ clean that line
add r0, r0, #0x10 @ incremet secment counter
cmp r0, #0x40 @ complete all 4 segments?
bne cf_inner_loop
add r1, r1, #0x04000000 @ increment line counter
cmp r1, #0 @ complete all lines?
bne cf_outer_loop
bx lr
.global wait_irq
wait_irq:
mrs r0, cpsr
bic r0, r0, #0x80
msr cpsr_c, r0 @ enable interrupts
mov r0, #0
mcr p15, 0, r0, c7, c0, 4 @ wait for IRQ
@ mcr p15, 0, r0, c15, c8, 2
b .b_reserved
.pool

33
platform/gp2x/940shared.h Normal file
View file

@ -0,0 +1,33 @@
#include "../../Pico/sound/ym2612.h"
enum _940_job_t {
JOB940_YM2612INIT = 1,
JOB940_YM2612RESETCHIP,
JOB940_YM2612UPDATEONE,
JOB940_PICOSTATELOAD,
JOB940_NUMJOBS
};
typedef struct
{
YM2612 ym2612; /* current state of the emulated YM2612 */
int mix_buffer[44100/50*2]; /* this is where the YM2612 samples will be mixed to */
} _940_data_t;
typedef struct
{
int job; /* a job for second core */
int busy; /* busy status of the 940 core */
int length; /* number of samples to mix (882 max) */
int stereo; /* mix samples as stereo, doubles sample count automatically */
int baseclock; /* ym2612 settings */
int rate;
int writebuffsel; /* which write buffer to use (from 940 side) */
UINT16 writebuff0[2048]; /* 1024 for savestates, 1024 extra */
UINT16 writebuff1[2048];
int vstarts[8]; /* debug: number of starts from each of 8 vectors */
int loopc; /* debug: main loop counter */
int waitc; /* debug: wait loop counter */
} _940_ctl_t;

191
platform/gp2x/Makefile Normal file
View file

@ -0,0 +1,191 @@
# you may or may not need to change this
#devkit_path = x:/stuff/dev/devkitgp2x/
devkit_path = /usr/local/devkitPro/devkitGP2X/
lgcc_path = $(devkit_path)lib/gcc/arm-linux/4.0.3/
CROSS = arm-linux-
#CROSS = $(devkit_path)bin/arm-linux-
# settings
dprint = 1
#mz80 = 1
#debug_cyclone = 1
asm_memory = 1
asm_render = 1
asm_ym2612 = 1
#profile = 1
#use_musashi = 1
#up = 1
DEFINC = -I../.. -I. -D__GP2X__ -D_UNZIP_SUPPORT # -DBENCHMARK
COPT_COMMON = -static -s -O3 -ftracer -fstrength-reduce -Wall -funroll-loops -fomit-frame-pointer -fstrict-aliasing -ffast-math
ifeq "$(profile)" "1"
COPT_COMMON += -fprofile-generate
endif
ifeq "$(profile)" "2"
COPT_COMMON += -fprofile-use
endif
COPT = $(COPT_COMMON) -mtune=arm920t
ASOPT = -mcpu=arm920t -mfloat-abi=soft
GCC = $(CROSS)gcc
STRIP = $(CROSS)strip
AS = $(CROSS)as
LD = $(CROSS)ld
OBJCOPY = $(CROSS)objcopy
# frontend
OBJS += main.o menu.o gp2x.o usbjoy.o emu.o squidgehack.o asmutils.o cpuctrl.o
# 940 core control
OBJS += 940ctl_ym2612.o
# Pico
OBJS += ../../Pico/Area.o ../../Pico/Cart.o ../../Pico/Utils.o ../../Pico/Memory.o ../../Pico/Misc.o \
../../Pico/Pico.o ../../Pico/Sek.o ../../Pico/VideoPort.o ../../Pico/Draw2.o ../../Pico/Draw.o
# asm stuff
ifeq "$(asm_render)" "1"
DEFINC += -D_ASM_DRAW_C
OBJS += ../../Pico/draw_asm.o ../../Pico/draw2_asm.o
endif
ifeq "$(asm_memory)" "1"
DEFINC += -D_ASM_MEMORY_C
OBJS += ../../Pico/memory_asm.o
endif
ifeq "$(asm_ym2612)" "1"
DEFINC += -D_ASM_YM2612_C
OBJS += ../../Pico/sound/ym2612_asm.o
endif
# Pico - sound
OBJS += ../../Pico/sound/sound.o ../../Pico/sound/sn76496.o ../../Pico/sound/ym2612.o
# zlib
OBJS += ../../zlib/gzio.o ../../zlib/inffast.o ../../zlib/inflate.o ../../zlib/inftrees.o ../../zlib/trees.o \
../../zlib/deflate.o ../../zlib/crc32.o ../../zlib/adler32.o ../../zlib/zutil.o ../../zlib/compress.o
# unzip
OBJS += ../../unzip/unzip.o
# CPU cores
ifeq "$(use_musashi)" "1"
DEFINC += -DEMU_M68K
OBJS += _build\m68kcpu.o _build\m68kopac.o _build\m68kopdm.o _build\m68kopnz.o _build\m68kops.o
else
DEFINC += -DEMU_C68K
OBJS += ../../cpu/Cyclone/proj/Cyclone.o
endif
# drz80/mz80
ifeq "$(mz80)" "1"
DEFINC += -D_USE_MZ80
OBJS += ../../cpu/mz80/mz80.o
else
DEFINC += -D_USE_DRZ80
OBJS += ../../cpu/DrZ80/drz80.o
endif
all: PicoDrive.gpe code940.bin
PicoDrive.gpe : $(OBJS)
@echo $@
@$(GCC) $(COPT) $(OBJS) $(PRELIBS) -lm -o $@
@$(STRIP) $@
# @$(GCC) $(COPT) $(OBJS) $(PRELIBS) -lm -o PicoDrive_.gpe
# @gpecomp PicoDrive_.gpe $@
ifeq "$(up)" "1"
@cmd //C copy $@ \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\
endif
up: up940
@cmd //C copy PicoDrive.gpe \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\
up940:
@cmd //C copy code940.bin \\\\10.0.1.2\\gp2x\\mnt\\sd\\games\\PicoDrive\\
testrefr.gpe : test.o gp2x.o asmutils.o
@echo $@
@$(GCC) $(COPT) $^ $(PRELIBS) -o $@
@$(STRIP) $@
.c.o:
@echo $<
@$(GCC) $(COPT) $(DEFINC) -c $< -o $@
.s.o:
@echo $<
@$(GCC) $(COPT) $(DEFINC) -c $< -o $@
../../Pico/draw_asm.o : ../../Pico/Draw.s
@echo $<
@$(AS) $(ASOPT) $< -o $@
../../Pico/draw2_asm.o : ../../Pico/Draw2.s
@echo $<
@$(AS) $(ASOPT) $< -o $@
../../Pico/memory_asm.o : ../../Pico/Memory.s
@echo $<
@$(AS) $(ASOPT) $< -o $@
../../Pico/sound/ym2612_asm.o : ../../Pico/sound/ym2612.s
@echo $<
@$(AS) $(ASOPT) $< -o $@
# build Cyclone
../../cpu/Cyclone/proj/Cyclone.s :
@echo building Cyclone...
@make -C ../../cpu/Cyclone/proj -f Makefile.linux
# stuff for 940 core
# init, emu_control, emu
OBJS940 += 940init.o 940.o 940ym2612.o
# the asm code seems to be faster when run on 920, but not on 940 for some reason
# OBJS940 += ../../Pico/sound/ym2612_asm.o
# uClibc library code
OBJS940 += uClibc/memset.o uClibc/s_floor.o uClibc/e_pow.o uClibc/e_sqrt.o uClibc/s_fabs.o
OBJS940 += uClibc/s_scalbn.o uClibc/s_copysign.o uClibc/k_sin.o uClibc/k_cos.o uClibc/s_sin.o
OBJS940 += uClibc/e_rem_pio2.o uClibc/k_rem_pio2.o uClibc/e_log.o uClibc/wrappers.o
code940.bin : code940.gpe
@echo $@
@$(OBJCOPY) -O binary $< $@
code940.gpe : $(OBJS940)
@echo $@
@$(LD) -static -e code940 -Ttext 0x0 $^ -L$(lgcc_path) -lgcc -o $@
940ym2612.o : ../../Pico/sound/ym2612.c
@echo $@
@$(GCC) $(COPT_COMMON) -mtune=arm940t $(DEFINC) -DEXTERNAL_YM2612 -c $< -o $@
# cleanup
clean: clean_pd clean_940
tidy: tidy_pd tidy_940
clean_pd: tidy_pd
@$(RM) PicoDrive.gpe
tidy_pd:
@$(RM) $(OBJS)
# @make -C ../../cpu/Cyclone/proj -f Makefile.linux clean
clean_940: tidy_940
@$(RM) code940.bin
tidy_940:
@$(RM) code940.gpe $(OBJS940)
clean_prof:
find ../.. -name '*.gcno' -delete
find ../.. -name '*.gcda' -delete
# test
usbjoy.o : usbjoy.c
@echo $<
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
../../Pico/Cart.o : ../../Pico/Cart.c
@echo $<
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
../../zlib/trees.o : ../../zlib/trees.c
@echo $<
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
uClibc/e_pow.o : uClibc/e_pow.c
@echo $<
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@
uClibc/e_sqrt.o : uClibc/e_sqrt.c
@echo $<
@$(GCC) $(COPT) $(DEFINC) -fno-profile-generate -c $< -o $@

12
platform/gp2x/asmutils.h Normal file
View file

@ -0,0 +1,12 @@
// (c) Copyright 2006 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
void vidConvCpyRGB32 (void *to, void *from, int pixels);
void vidConvCpyRGB32sh(void *to, void *from, int pixels);
void vidConvCpyRGB32hi(void *to, void *from, int pixels);
void vidCpyM2_40col(void *dest, void *src);
void vidCpyM2_32col(void *dest, void *src);
void vidCpyM2_32col_nobord(void *dest, void *src);
void spend_cycles(int c); // utility

210
platform/gp2x/asmutils.s Normal file
View file

@ -0,0 +1,210 @@
@ some color conversion and blitting routines
@ (c) Copyright 2006, notaz
@ All Rights Reserved
@ Convert 0000bbb0 ggg0rrr0 0000bbb0 ggg0rrr0
@ to 00000000 rrr00000 ggg00000 bbb00000 ...
@ lr = 0x00e000e0, out: r3=lower_pix, r2=higher_pix; trashes rin
@ if sh==2, r8=0x00404040 (sh!=0 destroys flags!)
.macro convRGB32_2 rin sh=0
and r2, lr, \rin, lsr #4 @ blue
and r3, \rin, lr
orr r2, r2, r3, lsl #8 @ g0b0g0b0
mov r3, r2, lsl #16 @ g0b00000
and \rin,lr, \rin, ror #12 @ 00r000r0 (reversed)
orr r3, r3, \rin, lsr #16 @ g0b000r0
.if \sh == 1
mov r3, r3, ror #17 @ shadow mode
.elseif \sh == 2
adds r3, r3, #0x40000000 @ green
orrcs r3, r3, #0xe0000000
mov r3, r3, ror #8
adds r3, r3, #0x40000000
orrcs r3, r3, #0xe0000000
mov r3, r3, ror #16
adds r3, r3, #0x40000000
orrcs r3, r3, #0xe0000000
mov r3, r3, ror #24
.else
mov r3, r3, ror #16 @ r3=low
.endif
orr r3, r3, r3, lsr #3
str r3, [r0], #4
mov r2, r2, lsr #16
orr r2, r2, \rin, lsl #16
.if \sh == 1
mov r2, r2, lsr #1
.elseif \sh == 2
mov r2, r2, ror #8
adds r2, r2, #0x40000000 @ blue
orrcs r2, r2, #0xe0000000
mov r2, r2, ror #8
adds r2, r2, #0x40000000
orrcs r2, r2, #0xe0000000
mov r2, r2, ror #8
adds r2, r2, #0x40000000
orrcs r2, r2, #0xe0000000
mov r2, r2, ror #8
.endif
orr r2, r2, r2, lsr #3
str r2, [r0], #4
.endm
.global vidConvCpyRGB32 @ void *to, void *from, int pixels
vidConvCpyRGB32:
stmfd sp!, {r4-r7,lr}
mov r12, r2, lsr #3 @ repeats
mov lr, #0x00e00000
orr lr, lr, #0x00e0
.loopRGB32:
subs r12, r12, #1
ldmia r1!, {r4-r7}
convRGB32_2 r4
convRGB32_2 r5
convRGB32_2 r6
convRGB32_2 r7
bgt .loopRGB32
ldmfd sp!, {r4-r7,lr}
bx lr
.global vidConvCpyRGB32sh @ void *to, void *from, int pixels
vidConvCpyRGB32sh:
stmfd sp!, {r4-r7,lr}
mov r12, r2, lsr #3 @ repeats
mov lr, #0x00e00000
orr lr, lr, #0x00e0
.loopRGB32sh:
subs r12, r12, #1
ldmia r1!, {r4-r7}
convRGB32_2 r4, 1
convRGB32_2 r5, 1
convRGB32_2 r6, 1
convRGB32_2 r7, 1
bgt .loopRGB32sh
ldmfd sp!, {r4-r7,lr}
bx lr
.global vidConvCpyRGB32hi @ void *to, void *from, int pixels
vidConvCpyRGB32hi:
stmfd sp!, {r4-r7,lr}
mov r12, r2, lsr #3 @ repeats
mov lr, #0x00e00000
orr lr, lr, #0x00e0
.loopRGB32hi:
ldmia r1!, {r4-r7}
convRGB32_2 r4, 2
convRGB32_2 r5, 2
convRGB32_2 r6, 2
convRGB32_2 r7, 2
subs r12, r12, #1
bgt .loopRGB32hi
ldmfd sp!, {r4-r7,lr}
bx lr
@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ mode2 blitter for 40 cols
.global vidCpyM2_40col @ void *dest, void *src
vidCpyM2_40col:
stmfd sp!, {r4-r6,lr}
mov r12, #224 @ lines
add r1, r1, #8
vidCpyM2_40_loop_out:
mov r6, #10
vidCpyM2_40_loop:
subs r6, r6, #1
ldmia r1!, {r2-r5}
stmia r0!, {r2-r5}
ldmia r1!, {r2-r5}
stmia r0!, {r2-r5}
bne vidCpyM2_40_loop
subs r12,r12,#1
add r1, r1, #8
bne vidCpyM2_40_loop_out
ldmfd sp!, {r4-r6,lr}
bx lr
@ mode2 blitter for 32 cols
.global vidCpyM2_32col @ void *dest, void *src
vidCpyM2_32col:
stmfd sp!, {r4-r6,lr}
mov r12, #224 @ lines
add r1, r1, #8
add r0, r0, #32
vidCpyM2_32_loop_out:
mov r6, #8
vidCpyM2_32_loop:
subs r6, r6, #1
ldmia r1!, {r2-r5}
stmia r0!, {r2-r5}
ldmia r1!, {r2-r5}
stmia r0!, {r2-r5}
bne vidCpyM2_32_loop
subs r12,r12,#1
add r0, r0, #64
add r1, r1, #8+64
bne vidCpyM2_32_loop_out
ldmfd sp!, {r4-r6,lr}
bx lr
@ mode2 blitter for 32 cols with no borders
.global vidCpyM2_32col_nobord @ void *dest, void *src
vidCpyM2_32col_nobord:
stmfd sp!, {r4-r6,lr}
mov r12, #224 @ lines
add r1, r1, #8
b vidCpyM2_32_loop_out
@ test
.global spend_cycles @ c
spend_cycles:
mov r0, r0, lsr #2 @ 4 cycles/iteration
sub r0, r0, #2 @ entry/exit/init
.sc_loop:
subs r0, r0, #1
bpl .sc_loop
bx lr

138
platform/gp2x/config.txt Normal file
View file

@ -0,0 +1,138 @@
As PicoDrive is multiplatform emulator, this is GP2X specific part of readme
about configuration.
Configuration
-------------
1. "Renderer"
8bit fast:
This enables alternative heavily optimized tile-based renderer, which renders
pixels not line-by-line (this is what accurate renderers do), but in 8x8 tiles,
which is much faster. But because of the way it works it can't render any
mid-frame image changes (raster effects), so it is useful only with some games.
Other two are accurate line-based renderers. The 8bit is faster but does not
run well with some games like Street Racer.
2. "Accurate timing"
This adds some more emulation precision, but slows the emulation down. Whithout
this option some games do not boot (Red Zone for example), others have sound
problems.
3. "Accurate sprites"
This option improves emulation of sprite priorities, it also enables emulation
of sprite collision bit. If you see one sprite being drawn incorrectly above
the other (often seen in Sonic 3D Blast), you can enable this to fix the problem.
This only works with the default renderer (see first option).
4. "Show FPS"
Self-explanatory. Format is XX/YY, where XX is the number of rendered frames and
YY is the number of emulated frames per second.
5. "Frameskip"
How many frames to skip rendering before displaying another.
"Auto" is recommended.
6. "Enable sound"
Does what it says. You must enable at least YM2612 or SN76496 (in advanced options,
see below) for this to make sense.
7. "Sound Quality"
Sound rate and stereo mode. If you want 44100Hz sound, it is recommended to enable
the second core (next option).
8. "Use ARM940 core for sound"
This option causes PicoDrive to use ARM940T core (GP2X's second CPU) for sound
(i.e. to generate YM2612 samples) to improve performance noticeably.
9. "6 button pad"
If you enable this, games will think that 6 button gamepad is connected. If you
go and reconfigure your keys, you will be able to bind X,Y,Z and mode actions.
10. "Genesis Region"
This option lets you force the game to think it is running on machine from the
specified region.
11. "Use SRAM savestates"
This will automatically read/write SRAM savestates for games which are using them.
SRAM is saved whenever you pause your game or exit the emulator.
12. "GP2X CPU clocks"
Here you can change clocks of both GP2X's CPUs. Larger values increase performance.
There is no separate option for the second CPU because both CPUs use the same clock
source. Setting this option to 200 will cause PicoDrive NOT to change GP2X's clocks
at all.
13. "[advanced options]"
Enters advanced options menu (see below).
14. "Save cfg as default"
If you save your config here it will be loaded on next ROM load, but only if there
is no game specific config saved (which will be loaded in that case).
15. "Save cfg for current game only"
Whenever you load current ROM again these settings will be loaded (squidgehack and
RAM settings will not take effect until emulator is restarted).
Advanced configuration
----------------------
Enter [advanced options] in config menu to see these options.
1. "Scale 32 column mode"
This enables hardware scaling for lower-res genesis mode (where width is
32 8-pixel tiles, instead of 40 in other mode).
2. "Gamma correction"
Alters image gamma through GP2X hardware. Larger values make image to look brighter,
lower - darker (default is 1.0).
3. "Emulate Z80"
Enables emulation of Z80 chip, which was mostly used to drive the other sound chips.
Some games do complex sync with it, so you must enable it even if you don't use
sound to be able to play them.
4. "Emulate YM2612 (FM)"
This enables emulation of six-channel FM sound synthesizer chip, which was used to
produce sound effects and music.
5. "Emulate SN76496 (PSG)"
This enables emulation of additional sound chip for additional effects.
Note: if you change sound settings AFTER loading a ROM, you may need to reset
game to get sound. This is because most games initialize sound chips on
startup, and this data is lost when sound chips are being enabled/disabled.
6. "gzip savestates"
This will always apply gzip compression on your savestates, allowing you to
save some space and load/save time.
7. "USB joy controls player X"
If you are able to use USB joysticks with your GP2X, this options selects which
player the joystick controls.
8. "Don't save config on exit"
This will disable config autowrite on exit (which might cause SD card corruption
according to DaveC).
9. "craigix's RAM timings"
This overclocks the GP2X RAM chips, but may cause instability. Recommended if you
use the second core for sound. Needs emulator restart to take effect.
See this thread:
http://www.gp32x.com/board/index.php?showtopic=32319
10. "squidgehack"
Well known way to improve the GP2X performance. You must restart the emulator
for the change of this option to take effect.
Key configuration
-----------------
When you select "Configure controls" from the menu, you enter a key configuration
mode, where you use SELECT to change an action, and then press a key you like to
bind to that action. You can press the same key again to unbind. Select "DONE"
action and press any key to finish.

156
platform/gp2x/cpuctrl.c Normal file
View file

@ -0,0 +1,156 @@
/* cpuctrl for GP2X
Copyright (C) 2005 Hermes/PS2Reality
the gamma-routine was provided by theoddbot
parts (c) Rlyehs Work & (C) 2006 god_at_hell
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <sys/mman.h>
#include <math.h>
#include "cpuctrl.h"
/* system registers */
static struct
{
unsigned short SYSCLKENREG,SYSCSETREG,FPLLVSETREG,DUALINT920,DUALINT940,DUALCTRL940,MEMTIMEX0,MEMTIMEX1;
}
system_reg;
static unsigned short dispclockdiv;
static volatile unsigned short *MEM_REG;
#define SYS_CLK_FREQ 7372800
void cpuctrl_init(void)
{
extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */
MEM_REG=&gp2x_memregs[0];
system_reg.SYSCSETREG=MEM_REG[0x91c>>1];
system_reg.FPLLVSETREG=MEM_REG[0x912>>1];
system_reg.SYSCLKENREG=MEM_REG[0x904>>1];
system_reg.DUALINT920=MEM_REG[0x3B40>>1];
system_reg.DUALINT940=MEM_REG[0x3B42>>1];
system_reg.DUALCTRL940=MEM_REG[0x3B48>>1];
system_reg.MEMTIMEX0=MEM_REG[0x3802>>1];
system_reg.MEMTIMEX1=MEM_REG[0x3804>>1];
dispclockdiv=MEM_REG[0x924>>1];
}
void cpuctrl_deinit(void)
{
MEM_REG[0x91c>>1]=system_reg.SYSCSETREG;
MEM_REG[0x910>>1]=system_reg.FPLLVSETREG;
MEM_REG[0x3B40>>1]=system_reg.DUALINT920;
MEM_REG[0x3B42>>1]=system_reg.DUALINT940;
MEM_REG[0x3B48>>1]=system_reg.DUALCTRL940;
MEM_REG[0x904>>1]=system_reg.SYSCLKENREG;
MEM_REG[0x924>>1]=dispclockdiv;
MEM_REG[0x3802>>1]=system_reg.MEMTIMEX0;
MEM_REG[0x3804>>1]=system_reg.MEMTIMEX1 /*| 0x9000*/;
}
void set_display_clock_div(unsigned div)
{
div=((div & 63) | 64)<<8;
MEM_REG[0x924>>1]=(MEM_REG[0x924>>1] & ~(255<<8)) | div;
}
void set_FCLK(unsigned MHZ)
{
unsigned v;
unsigned mdiv,pdiv=3,scale=0;
MHZ*=1000000;
mdiv=(MHZ*pdiv)/SYS_CLK_FREQ;
mdiv=((mdiv-8)<<8) & 0xff00;
pdiv=((pdiv-2)<<2) & 0xfc;
scale&=3;
v=mdiv | pdiv | scale;
MEM_REG[0x910>>1]=v;
}
void set_920_Div(unsigned short div)
{
unsigned short v;
v = MEM_REG[0x91c>>1] & (~0x3);
MEM_REG[0x91c>>1] = (div & 0x7) | v;
}
void set_DCLK_Div( unsigned short div )
{
unsigned short v;
v = (unsigned short)( MEM_REG[0x91c>>1] & (~(0x7 << 6)) );
MEM_REG[0x91c>>1] = ((div & 0x7) << 6) | v;
}
/*
void Disable_940(void)
{
MEM_REG[0x3B42>>1];
MEM_REG[0x3B42>>1]=0;
MEM_REG[0x3B46>>1]=0xffff;
MEM_REG[0x3B48>>1]|= (1 << 7);
MEM_REG[0x904>>1]&=0xfffe;
}
*/
void set_RAM_Timings(int tRC, int tRAS, int tWR, int tMRD, int tRFC, int tRP, int tRCD)
{
tRC -= 1; tRAS -= 1; tWR -= 1; tMRD -= 1; tRFC -= 1; tRP -= 1; tRCD -= 1; // ???
MEM_REG[0x3802>>1] = ((tMRD & 0xF) << 12) | ((tRFC & 0xF) << 8) | ((tRP & 0xF) << 4) | (tRCD & 0xF);
MEM_REG[0x3804>>1] = /*0x9000 |*/ ((tRC & 0xF) << 8) | ((tRAS & 0xF) << 4) | (tWR & 0xF);
}
/*
void gp2x_video_wait_vsync(void)
{
MEM_REG[0x2846>>1]=(MEM_REG[0x2846>>1] | 0x20) & ~2;
while(!(MEM_REG[0x2846>>1] & 2));
}
*/
void set_gamma(int g100)
{
float gamma = (float) g100 / 100;
int i;
//printf ("set gamma = %f\r\n",gamma);
gamma = 1/gamma;
//enable gamma
MEM_REG[0x2880>>1]&=~(1<<12);
MEM_REG[0x295C>>1]=0;
for(i=0; i<256; i++)
{
unsigned char g;
unsigned short s;
g =(unsigned char)(255.0*pow(i/255.0,gamma));
s = (g<<8) | g;
MEM_REG[0x295E>>1]= s;
MEM_REG[0x295E>>1]= g;
}
}

View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

16
platform/gp2x/cpuctrl.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef __CPUCTRL_H__
#define __CPUCTRL_H__
extern void cpuctrl_init(void); /* call this at first */
extern void save_system_regs(void); /* save some registers */
extern void cpuctrl_deinit(void);
extern void set_display_clock_div(unsigned div);
extern void set_FCLK(unsigned MHZ); /* adjust the clock frequency (in Mhz units) */
extern void set_920_Div(unsigned short div); /* 0 to 7 divider (freq=FCLK/(1+div)) */
extern void set_DCLK_Div(unsigned short div); /* 0 to 7 divider (freq=FCLK/(1+div)) */
//extern void Disable_940(void); /* 940t down */
//extern void gp2x_video_wait_vsync(void);
extern void set_RAM_Timings(int tRC, int tRAS, int tWR, int tMRD, int tRFC, int tRP, int tRCD);
extern void set_gamma(int g100);
#endif

1121
platform/gp2x/emu.c Normal file

File diff suppressed because it is too large Load diff

46
platform/gp2x/emu.h Normal file
View file

@ -0,0 +1,46 @@
// (c) Copyright 2006 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
// engine states
enum TPicoGameState {
PGS_Paused = 1,
PGS_Running,
PGS_Quit,
PGS_KeyConfig,
PGS_ReloadRom,
PGS_Menu,
};
typedef struct {
char lastRomFile[512];
int EmuOpt; // LSb->MSb: use_sram, show_fps, enable_sound, gzip_saves,
// squidgehack, save_cfg_on_exit, <unused>, 16_bit_mode
// craigix_ram, confirm_save
int PicoOpt; // used for config saving only, see Pico.h
int PsndRate; // ditto
int PicoRegion; // ditto
int Frameskip;
int CPUclock;
int KeyBinds[32];
int volume;
int gamma;
int JoyBinds[4][32];
} currentConfig_t;
extern char romFileName[];
extern int engineState;
extern currentConfig_t currentConfig;
int emu_ReloadRom(void);
void emu_Init(void);
void emu_Deinit(void);
int emu_SaveLoadGame(int load, int sram);
void emu_Loop(void);
void emu_ResetGame(void);
int emu_ReadConfig(int game);
int emu_WriteConfig(int game);

311
platform/gp2x/gp2x.c Normal file
View file

@ -0,0 +1,311 @@
/**
* All this is mostly based on rlyeh's minimal library.
* Copied here to review all his code and understand what's going on.
**/
/*
GP2X minimal library v0.A by rlyeh, (c) 2005. emulnation.info@rlyeh (swap it!)
Thanks to Squidge, Robster, snaff, Reesy and NK, for the help & previous work! :-)
License
=======
Free for non-commercial projects (it would be nice receiving a mail from you).
Other cases, ask me first.
GamePark Holdings is not allowed to use this library and/or use parts from it.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <fcntl.h>
#include <errno.h>
#include "gp2x.h"
#include "usbjoy.h"
volatile unsigned short *gp2x_memregs;
//static
volatile unsigned long *gp2x_memregl;
static void *gp2x_screens[4];
static int screensel = 0;
//static
int memdev = 0;
static int sounddev = 0, mixerdev = 0;
void *gp2x_screen;
#define FRAMEBUFF_ADDR0 0x4000000-640*480
#define FRAMEBUFF_ADDR1 0x4000000-640*480*2
#define FRAMEBUFF_ADDR2 0x4000000-640*480*3
#define FRAMEBUFF_ADDR3 0x4000000-640*480*4
static const int gp2x_screenaddrs[] = { FRAMEBUFF_ADDR0, FRAMEBUFF_ADDR1, FRAMEBUFF_ADDR2, FRAMEBUFF_ADDR3 };
/* video stuff */
void gp2x_video_flip(void)
{
unsigned int address = gp2x_screenaddrs[screensel&3];
/* test */
/* {
int i; char *p=gp2x_screen;
for (i=0; i < 240; i++) { memset(p+i*320, 0, 32); }
}*/
gp2x_memregs[0x290E>>1]=(unsigned short)(address);
gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16);
gp2x_memregs[0x2912>>1]=(unsigned short)(address);
gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16);
// jump to other buffer:
gp2x_screen = gp2x_screens[++screensel&3];
}
void gp2x_video_changemode(int bpp)
{
gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/
gp2x_memregs[0x290C>>1]=320*((bpp+1)/8); /*line width in bytes*/
gp2x_memset_all_buffers(0, 0, 640*480);
gp2x_video_flip();
}
void gp2x_video_setpalette(int *pal, int len)
{
unsigned short *g=(unsigned short *)pal;
volatile unsigned short *memreg = &gp2x_memregs[0x295A>>1];
gp2x_memregs[0x2958>>1] = 0;
len *= 2;
while(len--) *memreg=*g++;
}
// TV Compatible function //
void gp2x_video_RGB_setscaling(int W, int H)
{
float escalaw, escalah;
int bpp = (gp2x_memregs[0x28DA>>1]>>9)&0x3;
escalaw = 1024.0; // RGB Horiz LCD
escalah = 320.0; // RGB Vert LCD
if(gp2x_memregs[0x2800>>1]&0x100) //TV-Out
{
escalaw=489.0; // RGB Horiz TV (PAL, NTSC)
if (gp2x_memregs[0x2818>>1] == 287) //PAL
escalah=274.0; // RGB Vert TV PAL
else if (gp2x_memregs[0x2818>>1] == 239) //NTSC
escalah=331.0; // RGB Vert TV NTSC
}
// scale horizontal
gp2x_memregs[0x2906>>1]=(unsigned short)((float)escalaw *(W/320.0));
// scale vertical
gp2x_memregl[0x2908>>2]=(unsigned long)((float)escalah *bpp *(H/240.0));
}
/* LCD updates @ 80Hz? */
void gp2x_video_wait_vsync(void)
{
gp2x_memregs[0x2846>>1] = 0x20|2; //(gp2x_memregs[0x2846>>1] | 0x20) & ~2;
while(!(gp2x_memregs[0x2846>>1] & 2));// usleep(1);
}
void gp2x_memcpy_all_buffers(void *data, int offset, int len)
{
memcpy((char *)gp2x_screens[0] + offset, data, len);
memcpy((char *)gp2x_screens[1] + offset, data, len);
memcpy((char *)gp2x_screens[2] + offset, data, len);
memcpy((char *)gp2x_screens[3] + offset, data, len);
}
void gp2x_memset_all_buffers(int offset, int byte, int len)
{
memset((char *)gp2x_screens[0] + offset, byte, len);
memset((char *)gp2x_screens[1] + offset, byte, len);
memset((char *)gp2x_screens[2] + offset, byte, len);
memset((char *)gp2x_screens[3] + offset, byte, len);
}
unsigned long gp2x_joystick_read(int allow_usb_joy)
{
int i;
unsigned long value=(gp2x_memregs[0x1198>>1] & 0x00FF);
if(value==0xFD) value=0xFA;
if(value==0xF7) value=0xEB;
if(value==0xDF) value=0xAF;
if(value==0x7F) value=0xBE;
value = ~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16));
if (allow_usb_joy && num_of_joys > 0) {
// check the usb joy as well..
gp2x_usbjoy_update();
for (i = 0; i < num_of_joys; i++)
value |= gp2x_usbjoy_check(i);
}
return value;
}
static int s_oldrate = 0, s_oldbits = 0, s_oldstereo = 0;
void gp2x_start_sound(int rate, int bits, int stereo)
{
int frag = 0, bsize, buffers;
// if no settings change, we don't need to do anything
if (rate == s_oldrate && s_oldbits == bits && s_oldstereo == stereo) return;
if (sounddev > 0) close(sounddev);
sounddev = open("/dev/dsp", O_WRONLY|O_ASYNC);
if (sounddev == -1)
printf("open(\"/dev/dsp\") failed with %i\n", errno);
ioctl(sounddev, SNDCTL_DSP_SPEED, &rate);
ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits);
ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo);
// calculate buffer size
buffers = 16;
bsize = rate / 32;
if (rate > 22050) { bsize*=4; buffers*=2; } // 44k mode seems to be very demanding
while ((bsize>>=1)) frag++;
frag |= buffers<<16; // 16 buffers
ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag);
printf("gp2x_set_sound: %i/%ibit/%s, %i buffers of %i bytes\n",
rate, bits, stereo?"stereo":"mono", frag>>16, 1<<(frag&0xffff));
s_oldrate = rate; s_oldbits = bits; s_oldstereo = stereo;
usleep(100000);
}
void gp2x_sound_write(void *buff, int len)
{
write(sounddev, buff, len);
}
void gp2x_sound_volume(int l, int r)
{
l=l<0?0:l; l=l>255?255:l; r=r<0?0:r; r=r>255?255:r;
l<<=8; l|=r;
ioctl(mixerdev, SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/
}
/* 940 */
void Pause940(int yes)
{
if(yes)
gp2x_memregs[0x0904>>1] &= 0xFFFE;
else
gp2x_memregs[0x0904>>1] |= 1;
}
void Reset940(int yes)
{
gp2x_memregs[0x3B48>>1] = ((yes&1) << 7) | (0x03); /* bank=3 */
}
/* common */
void gp2x_init(void)
{
printf("entering init()\n"); fflush(stdout);
memdev = open("/dev/mem", O_RDWR);
if (memdev == -1)
{
printf("open(\"/dev/mem\") failed with %i\n", errno);
exit(1);
}
gp2x_memregs = mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000);
printf("memregs are @ %p\n", gp2x_memregs);
if(gp2x_memregs == MAP_FAILED)
{
printf("mmap(memregs) failed with %i\n", errno);
exit(1);
}
gp2x_memregl = (unsigned long *) gp2x_memregs;
gp2x_screens[3] = mmap(0, 640*480*4, PROT_WRITE, MAP_SHARED, memdev, FRAMEBUFF_ADDR3);
if(gp2x_screens[3] == MAP_FAILED)
{
printf("mmap(gp2x_screen) failed with %i\n", errno);
exit(1);
}
printf("framebuffers point to %p\n", gp2x_screens[3]);
gp2x_screens[2] = (char *) gp2x_screens[3]+640*480;
gp2x_screens[1] = (char *) gp2x_screens[2]+640*480;
gp2x_screens[0] = (char *) gp2x_screens[1]+640*480;
gp2x_screen = gp2x_screens[0];
screensel = 0;
// snd
mixerdev = open("/dev/mixer", O_RDWR);
if (mixerdev == -1)
printf("open(\"/dev/mixer\") failed with %i\n", errno);
/* init usb joys -GnoStiC */
gp2x_usbjoy_init();
printf("exitting init()\n"); fflush(stdout);
}
char *ext_menu = 0, *ext_state = 0;
void gp2x_deinit(void)
{
Reset940(1);
Pause940(1);
gp2x_video_changemode(15);
munmap(gp2x_screens[0], 640*480*4);
munmap((void *)gp2x_memregs, 0x10000);
close(memdev);
close(mixerdev);
if (sounddev > 0) close(sounddev);
gp2x_usbjoy_deinit();
printf("all done, running ");
// Zaq121's alternative frontend support from MAME
if(ext_menu && ext_state) {
printf("%s -state %s\n", ext_menu, ext_state);
execl(ext_menu, ext_menu, "-state", ext_state, NULL);
} else if(ext_menu) {
printf("%s\n", ext_menu);
execl(ext_menu, ext_menu, NULL);
} else {
printf("gp2xmenu\n");
chdir("/usr/gp2x");
execl("gp2xmenu", "gp2xmenu", NULL);
}
}

40
platform/gp2x/gp2x.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef __GP2X_H__
#define __GP2X_H__
void gp2x_init(void);
void gp2x_deinit(void);
/* video */
void gp2x_video_flip(void);
void gp2x_video_changemode(int bpp);
void gp2x_video_setpalette(int *pal, int len);
void gp2x_video_RGB_setscaling(int W, int H);
void gp2x_video_wait_vsync(void);
void gp2x_memcpy_all_buffers(void *data, int offset, int len);
void gp2x_memset_all_buffers(int offset, int byte, int len);
/* sound */
void gp2x_start_sound(int rate, int bits, int stereo);
void gp2x_sound_write(void *buff, int len);
void gp2x_sound_volume(int l, int r);
/* joy */
unsigned long gp2x_joystick_read(int allow_usb_joy);
/* 940 core */
void Pause940(int yes);
void Reset940(int yes);
extern void *gp2x_screen;
extern int memdev;
enum { GP2X_UP=0x1, GP2X_LEFT=0x4, GP2X_DOWN=0x10, GP2X_RIGHT=0x40,
GP2X_START=1<<8, GP2X_SELECT=1<<9, GP2X_L=1<<10, GP2X_R=1<<11,
GP2X_A=1<<12, GP2X_B=1<<13, GP2X_X=1<<14, GP2X_Y=1<<15,
GP2X_VOL_UP=1<<23, GP2X_VOL_DOWN=1<<22, GP2X_PUSH=1<<27 };
#endif

143
platform/gp2x/main.c Normal file
View file

@ -0,0 +1,143 @@
// (c) Copyright 2006 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <strings.h>
#include <linux/limits.h>
#include "gp2x.h"
#include "menu.h"
#include "emu.h"
#include "version.h"
#include "squidgehack.h"
#include "cpuctrl.h"
extern char *ext_menu, *ext_state;
extern int select_exits;
extern char *PicoConfigFile;
int mmuhack_status = 0;
char **g_argv;
void parse_cmd_line(int argc, char *argv[])
{
int x, unrecognized = 0;
for(x = 1; x < argc; x++)
{
if(argv[x][0] == '-')
{
if(strcasecmp(argv[x], "-menu") == 0) {
if(x+1 < argc) { ++x; ext_menu = argv[x]; } /* External Frontend: Program Name */
}
else if(strcasecmp(argv[x], "-state") == 0) {
if(x+1 < argc) { ++x; ext_state = argv[x]; } /* External Frontend: Arguments */
}
else if(strcasecmp(argv[x], "-config") == 0) {
if(x+1 < argc) { ++x; PicoConfigFile = argv[x]; }
}
else if(strcasecmp(argv[x], "-selectexit") == 0) {
select_exits = 1;
}
else {
unrecognized = 1;
break;
}
} else {
/* External Frontend: ROM Name */
FILE *f;
strncpy(romFileName, argv[x], PATH_MAX);
romFileName[PATH_MAX-1] = 0;
f = fopen(romFileName, "rb");
if (f) fclose(f);
else unrecognized = 1;
engineState = PGS_ReloadRom;
break;
}
}
if (unrecognized) {
printf("\n\n\nPicoDrive v" VERSION " (c) notaz, 2006\n");
printf("usage: %s [options] [romfile]\n", argv[0]);
printf( "options:\n"
"-menu <menu_path> launch a custom program on exit instead of default gp2xmenu\n"
"-state <param> pass '-state param' to the menu program\n"
"-config <file> use specified config file instead of default 'picoconfig.bin'\n"
" see currentConfig_t structure in emu.h for the file format\n"
"-selectexit pressing SELECT will exit the emu and start 'menu_path'\n");
}
}
int main(int argc, char *argv[])
{
g_argv = argv;
emu_ReadConfig(0);
gp2x_init();
if (currentConfig.EmuOpt&0x10) {
int ret = mmuhack();
printf("squidge hack code finished and returned %i\n", ret); fflush(stdout);
mmuhack_status = ret;
}
cpuctrl_init();
Reset940(1);
Pause940(1);
if (currentConfig.EmuOpt&0x100) {
printf("setting RAM timings.. "); fflush(stdout);
// craigix: --trc 6 --tras 4 --twr 1 --tmrd 1 --trfc 1 --trp 2 --trcd 2
set_RAM_Timings(6, 4, 1, 1, 1, 2, 2);
printf("done.\n"); fflush(stdout);
}
emu_Init();
engineState = PGS_Menu;
if (argc > 1)
parse_cmd_line(argc, argv);
for (;;)
{
switch (engineState)
{
case PGS_Menu:
menu_loop();
break;
case PGS_ReloadRom:
if (emu_ReloadRom())
engineState = PGS_Running;
else {
printf("PGS_ReloadRom == 0\n");
engineState = PGS_Menu;
}
break;
case PGS_Running:
emu_Loop();
break;
case PGS_Quit:
goto endloop;
default:
printf("engine got into unknown state (%i), exitting\n", engineState);
goto endloop;
}
}
endloop:
emu_Deinit();
cpuctrl_deinit();
gp2x_deinit();
if(mmuhack_status)
mmuunhack();
return 0;
}

992
platform/gp2x/menu.c Normal file
View file

@ -0,0 +1,992 @@
// (c) Copyright 2006 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <dirent.h>
#include "gp2x.h"
#include "emu.h"
#include "menu.h"
#include "usbjoy.h"
#include "version.h"
#include "Pico/PicoInt.h"
#ifndef _DIRENT_HAVE_D_TYPE
#error "need d_type for file browser
#endif
extern char *actionNames[];
extern char romFileName[PATH_MAX];
extern char *rom_data;
extern int mmuhack_status;
extern int state_slot;
static char *gp2xKeyNames[] = {
"UP", "???", "LEFT", "???", "DOWN", "???", "RIGHT", "???",
"START", "SELECT", "L", "R", "A", "B", "X", "Y",
"???", "???", "???", "???", "???", "???", "VOL DOWN", "VOL UP",
"???", "???", "???", "PUSH", "???", "???", "???", "???"
};
char menuErrorMsg[40] = {0, };
static unsigned char fontdata8x8[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C,
0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,
0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00,
0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00,
0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,
0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40,
0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00,
0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00,
0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00,
0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00,
0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00,
0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00,
0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00,
0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00,
0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,
0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00,
0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00,
0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00,
0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00,
0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00,
0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00,
0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30,
0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,
0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00,
0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00,
0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00,
0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00,
0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00,
0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,
0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00,
0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00,
0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00,
0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00,
0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00,
0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00,
0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,
0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,
0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00,
0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00,
0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00,
0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C,
0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00,
0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00,
0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00,
0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00,
0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06,
0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00,
0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00,
0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00,
0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C,
0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00,
0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00,
0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00,
};
static void gp2x_text(unsigned char *screen, int x, int y, char *text, int color)
{
int i,l;
screen = screen + x + y*320;
for (i = 0; i < strlen(text); i++)
{
for (l=0;l<8;l++)
{
if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=color;
if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=color;
if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=color;
if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=color;
if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=color;
if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=color;
if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=color;
if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=color;
}
screen += 8;
}
}
// draws white text to current bbp15 screen
void gp2x_text_out15(int x, int y, char *text)
{
int i,l;
unsigned short *screen = gp2x_screen;
screen = screen + x + y*320;
for (i = 0; i < strlen(text); i++)
{
for (l=0;l<8;l++)
{
if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=0xffff;
if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=0xffff;
if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=0xffff;
if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=0xffff;
if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=0xffff;
if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=0xffff;
if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=0xffff;
if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=0xffff;
}
screen += 8;
}
}
void gp2x_text_out8(int x, int y, char *texto, ...)
{
va_list args;
char buffer[512];
va_start(args,texto);
vsprintf(buffer,texto,args);
va_end(args);
gp2x_text(gp2x_screen,x,y,buffer,1);
}
void gp2x_text_out8_2(int x, int y, char *texto, int color)
{
gp2x_text(gp2x_screen, x, y, texto, color);
}
void gp2x_text_out8_lim(int x, int y, char *texto, int max)
{
char buffer[320/8+1];
strncpy(buffer, texto, 320/8);
if (max > 320/8) max = 320/8;
if (max < 0) max = 0;
buffer[max] = 0;
gp2x_text(gp2x_screen,x,y,buffer,1);
}
static unsigned long inp_prev = 0;
static int inp_prevjoy = 0;
static unsigned long wait_for_input(unsigned long interesting)
{
unsigned long ret;
static int repeats = 0, wait = 300*1000;
int release = 0, i;
if (repeats == 5 || repeats == 15 || repeats == 30) wait /= 2;
for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {
if(i == 0) repeats++;
usleep(wait/6);
}
while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {
usleep(50000);
release = 1;
}
if (release || ret != inp_prev) {
repeats = 0;
wait = 300*1000;
}
inp_prev = ret;
inp_prevjoy = 0;
// we don't need diagonals in menus
if ((ret&GP2X_UP) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;
if ((ret&GP2X_UP) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;
if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;
if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;
return ret;
}
static unsigned long input2_read(unsigned long interesting, int *joy)
{
unsigned long ret;
int i;
do
{
*joy = 0;
if ((ret = gp2x_joystick_read(0) & interesting)) break;
gp2x_usbjoy_update();
for (i = 0; i < num_of_joys; i++) {
ret = gp2x_usbjoy_check2(i);
if (ret) { *joy = i + 1; break; }
}
if (ret) break;
}
while(0);
return ret;
}
// similar to wait_for_input(), but returns joy num
static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy)
{
unsigned long ret;
const int wait = 300*1000;
int i;
if (inp_prevjoy == 0) inp_prev &= interesting;
for (i = 0; i < 6; i++) {
ret = input2_read(interesting, joy);
if (*joy != inp_prevjoy || ret != inp_prev) break;
usleep(wait/6);
}
while ( !(ret = input2_read(interesting, joy)) ) {
usleep(50000);
}
inp_prev = ret;
inp_prevjoy = *joy;
return ret;
}
// -------------- ROM selector --------------
static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)
{
int start, i, pos;
start = 12 - sel;
n--; // exclude current dir (".")
memset(gp2x_screen, 0, 320*240);
if(start - 2 >= 0)
gp2x_text_out8_lim(14, (start - 2)*10, curdir, 38);
for (i = 0; i < n; i++) {
pos = start + i;
if (pos < 0) continue;
if (pos > 23) break;
if (namelist[i+1]->d_type == DT_DIR) {
gp2x_text_out8_lim(14, pos*10, "/", 1);
gp2x_text_out8_lim(14+8, pos*10, namelist[i+1]->d_name, 37);
} else {
gp2x_text_out8_lim(14, pos*10, namelist[i+1]->d_name, 38);
}
}
gp2x_text_out8(5, 120, ">");
gp2x_video_flip();
}
static int scandir_cmp(const void *p1, const void *p2)
{
struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;
if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);
if ((*d1)->d_type == DT_DIR) return -1; // put before
if ((*d2)->d_type == DT_DIR) return 1;
return alphasort(d1, d2);
}
static char *romsel_loop(char *curr_path)
{
struct dirent **namelist;
DIR *dir;
int n, sel = 0;
unsigned long inp = 0;
char *ret = NULL, *fname = NULL;
// is this a dir or a full path?
if ((dir = opendir(curr_path))) {
closedir(dir);
} else {
char *p;
for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);
*p = 0;
fname = p+1;
}
n = scandir(curr_path, &namelist, 0, scandir_cmp);
if (n < 0) {
// try root
n = scandir(curr_path, &namelist, 0, scandir_cmp);
if (n < 0) {
// oops, we failed
printf("dir: "); printf(curr_path); printf("\n");
perror("scandir");
return NULL;
}
}
// try to find sel
if (fname != NULL) {
int i;
for (i = 1; i < n; i++) {
if (strcmp(namelist[i]->d_name, fname) == 0) {
sel = i - 1;
break;
}
}
}
for (;;)
{
draw_dirlist(curr_path, namelist, n, sel);
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X);
if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = n-2; }
if(inp & GP2X_DOWN) { sel++; if (sel > n-2) sel = 0; }
if(inp & GP2X_LEFT) { sel-=10; if (sel < 0) sel = 0; }
if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }
if(inp & GP2X_B) { // enter dir/select
again:
if (namelist[sel+1]->d_type == DT_REG) {
strcpy(romFileName, curr_path);
strcat(romFileName, "/");
strcat(romFileName, namelist[sel+1]->d_name);
ret = romFileName;
break;
} else if (namelist[sel+1]->d_type == DT_DIR) {
int newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;
char *p, *newdir = malloc(newlen);
if (strcmp(namelist[sel+1]->d_name, "..") == 0) {
char *start = curr_path;
p = start + strlen(start) - 1;
while (*p == '/' && p > start) p--;
while (*p != '/' && p > start) p--;
if (p <= start) strcpy(newdir, "/");
else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }
} else {
strcpy(newdir, curr_path);
p = newdir + strlen(newdir) - 1;
while (*p == '/' && p >= newdir) *p-- = 0;
strcat(newdir, "/");
strcat(newdir, namelist[sel+1]->d_name);
}
ret = romsel_loop(newdir);
free(newdir);
break;
} else {
// unknown file type, happens on NTFS mounts. Try to guess.
FILE *tstf; int tmp;
strcpy(romFileName, curr_path);
strcat(romFileName, "/");
strcat(romFileName, namelist[sel+1]->d_name);
tstf = fopen(romFileName, "rb");
if (tstf != NULL)
{
if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)
namelist[sel+1]->d_type = DT_REG;
else namelist[sel+1]->d_type = DT_DIR;
fclose(tstf);
goto again;
}
}
}
if(inp & GP2X_X) break; // cancel
}
if (n > 0) {
while(n--) free(namelist[n]);
free(namelist);
}
return ret;
}
// -------------- key config --------------
static char *usb_joy_key_name(int joy, int num)
{
static char name[16];
switch (num)
{
case 0: sprintf(name, "Joy%i UP", joy); break;
case 1: sprintf(name, "Joy%i DOWN", joy); break;
case 2: sprintf(name, "Joy%i LEFT", joy); break;
case 3: sprintf(name, "Joy%i RIGHT", joy); break;
default:sprintf(name, "Joy%i b%i", joy, num-3); break;
}
return name;
}
static void draw_key_config(int curr_act, int is_p2)
{
char strkeys[32*5];
int joy, i;
strkeys[0] = 0;
for (i = 0; i < 32; i++)
{
if (currentConfig.KeyBinds[i] & (1 << curr_act))
{
if (curr_act < 16 && (currentConfig.KeyBinds[i] & (1 << 16)) != (is_p2 << 16)) continue;
if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gp2xKeyNames[i]); break; }
else strcpy(strkeys, gp2xKeyNames[i]);
}
}
for (joy = 0; joy < num_of_joys; joy++)
{
for (i = 0; i < 32; i++)
{
if (currentConfig.JoyBinds[joy][i] & (1 << curr_act))
{
if (curr_act < 16 && (currentConfig.JoyBinds[joy][i] & (1 << 16)) != (is_p2 << 16)) continue;
if (strkeys[0]) {
strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));
break;
}
else strcpy(strkeys, usb_joy_key_name(joy + 1, i));
}
}
}
memset(gp2x_screen, 0, 320*240);
gp2x_text_out8(60, 40, "Action: %s", actionNames[curr_act]);
gp2x_text_out8(60, 60, "Keys: %s", strkeys);
gp2x_text_out8(30, 180, "Use SELECT to change action");
gp2x_text_out8(30, 190, "Press a key to bind/unbind");
gp2x_text_out8(30, 200, "Select \"Done\" action and");
gp2x_text_out8(30, 210, " press any key to finish");
gp2x_video_flip();
}
static void key_config_loop(int is_p2)
{
int curr_act = 0, joy = 0, i;
unsigned long inp = 0;
for (;;)
{
draw_key_config(curr_act, is_p2);
inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS, &joy);
// printf("got %08lX from joy %i\n", inp, joy);
if (joy == 0) {
if (inp & GP2X_SELECT) {
curr_act++;
while (!actionNames[curr_act] && curr_act < 32) curr_act++;
if (curr_act > 31) curr_act = 0;
}
inp &= CONFIGURABLE_KEYS;
inp &= ~GP2X_SELECT;
}
if (curr_act == 31 && inp) break;
if (joy == 0) {
for (i = 0; i < 32; i++)
if (inp & (1 << i)) {
currentConfig.KeyBinds[i] ^= (1 << curr_act);
if (is_p2) currentConfig.KeyBinds[i] |= (1 << 16); // player 2 flag
else currentConfig.KeyBinds[i] &= ~(1 << 16);
}
} else {
for (i = 0; i < 32; i++)
if (inp & (1 << i)) {
currentConfig.JoyBinds[joy-1][i] ^= (1 << curr_act);
if (is_p2) currentConfig.JoyBinds[joy-1][i] |= (1 << 16);
else currentConfig.JoyBinds[joy-1][i] &= ~(1 << 16);
}
}
}
}
static void draw_kc_sel(int menu_sel)
{
int tl_x = 25+40, tl_y = 60, y, i;
char joyname[36];
y = tl_y;
memset(gp2x_screen, 0, 320*240);
gp2x_text_out8(tl_x, y, "Player 1");
gp2x_text_out8(tl_x, (y+=10), "Player 2");
gp2x_text_out8(tl_x, (y+=10), "Done");
// draw cursor
gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");
tl_x = 25;
gp2x_text_out8(tl_x, (y=110), "USB joys detected:");
if (num_of_joys > 0) {
for (i = 0; i < num_of_joys; i++) {
strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;
gp2x_text_out8(tl_x, (y+=10), "%i: %s", i+1, joyname);
}
} else {
gp2x_text_out8(tl_x, (y+=10), "none");
}
gp2x_video_flip();
}
static void kc_sel_loop(void)
{
int menu_sel = 2, menu_sel_max = 2;
unsigned long inp = 0;
for(;;)
{
draw_kc_sel(menu_sel);
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);
if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
if(inp & GP2X_B) {
switch (menu_sel) {
case 0: key_config_loop(0); return;
case 1: key_config_loop(1); return;
default: return;
}
}
if(inp & GP2X_X) return;
}
}
// --------- advanced options ----------
// order must match that of currentConfig_t
struct {
int EmuOpt;
int PicoOpt;
int PsndRate;
int PicoRegion;
int Frameskip;
int CPUclock;
} tmp_opts;
int tmp_gamma;
static void draw_amenu_options(int menu_sel)
{
int tl_x = 25, tl_y = 60, y;
char *mms = mmuhack_status ? "active) " : "inactive)";
y = tl_y;
memset(gp2x_screen, 0, 320*240);
gp2x_text_out8(tl_x, y, "Scale 32 column mode %s", (tmp_opts.PicoOpt&0x100)?"ON":"OFF"); // 0
gp2x_text_out8(tl_x, (y+=10), "Gamma correction %i.%02i", tmp_gamma / 100, tmp_gamma%100); // 1
gp2x_text_out8(tl_x, (y+=10), "Emulate Z80 %s", (tmp_opts.PicoOpt&0x004)?"ON":"OFF"); // 2
gp2x_text_out8(tl_x, (y+=10), "Emulate YM2612 (FM) %s", (tmp_opts.PicoOpt&0x001)?"ON":"OFF"); // 3
gp2x_text_out8(tl_x, (y+=10), "Emulate SN76496 (PSG) %s", (tmp_opts.PicoOpt&0x002)?"ON":"OFF"); // 4
gp2x_text_out8(tl_x, (y+=10), "gzip savestates %s", (tmp_opts.EmuOpt &0x008)?"ON":"OFF"); // 5
gp2x_text_out8(tl_x, (y+=10), "Don't save config on exit %s", (tmp_opts.EmuOpt &0x020)?"ON":"OFF"); // 6
gp2x_text_out8(tl_x, (y+=10), "needs restart:");
gp2x_text_out8(tl_x, (y+=10), "craigix's RAM timings %s", (tmp_opts.EmuOpt &0x100)?"ON":"OFF"); // 8
gp2x_text_out8(tl_x, (y+=10), "squidgehack (now %s %s", mms, (tmp_opts.EmuOpt &0x010)?"ON":"OFF"); // 9
gp2x_text_out8(tl_x, (y+=10), "Done");
// draw cursor
gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");
gp2x_video_flip();
}
static void amenu_loop_options(void)
{
int menu_sel = 0, menu_sel_max = 11;
unsigned long inp = 0;
for(;;)
{
draw_amenu_options(menu_sel);
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);
if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options
switch (menu_sel) {
case 0: tmp_opts.PicoOpt^=0x100; break;
case 2: tmp_opts.PicoOpt^=0x004; break;
case 3: tmp_opts.PicoOpt^=0x001; break;
case 4: tmp_opts.PicoOpt^=0x002; break;
case 5: tmp_opts.EmuOpt ^=0x008; break;
case 6: tmp_opts.EmuOpt ^=0x020; break;
case 8: tmp_opts.EmuOpt ^=0x100; break;
case 9: tmp_opts.EmuOpt ^=0x010; break;
case 10: return;
}
}
if(inp & (GP2X_X|GP2X_A)) return;
if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise
switch (menu_sel) {
case 1:
while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {
tmp_gamma += (inp & GP2X_LEFT) ? -1 : 1;
if (tmp_gamma < 1) tmp_gamma = 1;
if (tmp_gamma > 300) tmp_gamma = 300;
draw_amenu_options(menu_sel);
usleep(18*1000);
}
break;
}
}
}
}
// -------------- options --------------
static char *region_name(unsigned int code)
{
char *names[] = { "Auto", "Japan NTSC", "Japan PAL", "USA", "Europe" };
int i = 0;
code <<= 1;
while((code >>=1)) i++;
if (i > 4) return "unknown";
return names[i];
}
static void draw_menu_options(int menu_sel)
{
int tl_x = 25, tl_y = 40, y;
char monostereo[8], strframeskip[8], *strrend;
strcpy(monostereo, (tmp_opts.PicoOpt&0x08)?"stereo":"mono");
if (tmp_opts.Frameskip < 0)
strcpy(strframeskip, "Auto");
else sprintf(strframeskip, "%i", tmp_opts.Frameskip);
if (tmp_opts.PicoOpt&0x10) {
strrend = " 8bit fast";
} else if (tmp_opts.EmuOpt&0x80) {
strrend = "16bit accurate";
} else {
strrend = " 8bit accurate";
}
y = tl_y;
memset(gp2x_screen, 0, 320*240);
gp2x_text_out8(tl_x, y, "Renderer: %s", strrend); // 0
gp2x_text_out8(tl_x, (y+=10), "Accurate timing (slower) %s", (tmp_opts.PicoOpt&0x040)?"ON":"OFF"); // 1
gp2x_text_out8(tl_x, (y+=10), "Accurate sprites (slower) %s", (tmp_opts.PicoOpt&0x080)?"ON":"OFF"); // 2
gp2x_text_out8(tl_x, (y+=10), "Show FPS %s", (tmp_opts.EmuOpt &0x002)?"ON":"OFF"); // 3
gp2x_text_out8(tl_x, (y+=10), "Frameskip %s", strframeskip);
gp2x_text_out8(tl_x, (y+=10), "Enable sound %s", (tmp_opts.EmuOpt &0x004)?"ON":"OFF"); // 5
gp2x_text_out8(tl_x, (y+=10), "Sound Quality: %5iHz %s", tmp_opts.PsndRate, monostereo);
gp2x_text_out8(tl_x, (y+=10), "Use ARM940 core for sound %s", (tmp_opts.PicoOpt&0x200)?"ON":"OFF"); // 7
gp2x_text_out8(tl_x, (y+=10), "6 button pad %s", (tmp_opts.PicoOpt&0x020)?"ON":"OFF"); // 8
gp2x_text_out8(tl_x, (y+=10), "Genesis Region: %s", region_name(tmp_opts.PicoRegion));
gp2x_text_out8(tl_x, (y+=10), "Use SRAM savestates %s", (tmp_opts.EmuOpt &0x001)?"ON":"OFF"); // 10
gp2x_text_out8(tl_x, (y+=10), "Confirm save overwrites %s", (tmp_opts.EmuOpt &0x200)?"ON":"OFF"); // 11
gp2x_text_out8(tl_x, (y+=10), "Save slot %i", state_slot); // 12
gp2x_text_out8(tl_x, (y+=10), "GP2X CPU clocks %iMhz", tmp_opts.CPUclock);
gp2x_text_out8(tl_x, (y+=10), "[advanced options]");
gp2x_text_out8(tl_x, (y+=10), "Save cfg as default");
if (rom_data)
gp2x_text_out8(tl_x, (y+=10), "Save cfg for current game only");
// draw cursor
gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");
gp2x_video_flip();
}
static int sndrate_prevnext(int rate, int dir)
{
int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };
for (i = 0; i < 5; i++)
if (rates[i] == rate) break;
i += dir ? 1 : -1;
if (i > 4) return dir ? 44100 : 22050;
if (i < 0) return dir ? 11025 : 8000;
return rates[i];
}
static void menu_options_save(void)
{
memcpy(&currentConfig.EmuOpt, &tmp_opts.EmuOpt, sizeof(tmp_opts));
currentConfig.gamma = tmp_gamma;
PicoOpt = currentConfig.PicoOpt;
PsndRate = currentConfig.PsndRate;
PicoRegionOverride = currentConfig.PicoRegion;
if (PicoOpt & 0x20) {
actionNames[ 8] = "Z"; actionNames[ 9] = "Y";
actionNames[10] = "X"; actionNames[11] = "MODE";
} else {
actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;
}
}
static void menu_loop_options(void)
{
int menu_sel = 0, menu_sel_max = 15;
unsigned long inp = 0;
if (rom_data) menu_sel_max++;
memcpy(&tmp_opts.EmuOpt, &currentConfig.EmuOpt, sizeof(tmp_opts));
tmp_gamma = currentConfig.gamma;
tmp_opts.PicoOpt = PicoOpt;
tmp_opts.PsndRate = PsndRate;
tmp_opts.PicoRegion = PicoRegionOverride;
for(;;)
{
draw_menu_options(menu_sel);
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);
if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }
if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }
if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options
switch (menu_sel) {
case 1: tmp_opts.PicoOpt^=0x040; break;
case 2: tmp_opts.PicoOpt^=0x080; break;
case 3: tmp_opts.EmuOpt ^=0x002; break;
case 5: tmp_opts.EmuOpt ^=0x004; break;
case 7: tmp_opts.PicoOpt^=0x200; break;
case 8: tmp_opts.PicoOpt^=0x020; break;
case 10: tmp_opts.EmuOpt ^=0x001; break;
case 11: tmp_opts.EmuOpt ^=0x200; break;
case 14: amenu_loop_options(); break;
case 15: // done (save)
menu_options_save();
emu_WriteConfig(0);
return;
case 16: // done (save for current game)
menu_options_save();
emu_WriteConfig(1);
return;
}
}
if(inp & GP2X_X) return; // done (no save)
if(inp & GP2X_A) {
menu_options_save();
return; // done (save)
}
if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise
switch (menu_sel) {
case 0:
if (inp & GP2X_LEFT) {
if ( tmp_opts.PicoOpt&0x10) tmp_opts.PicoOpt&= ~0x10;
else if (!(tmp_opts.EmuOpt &0x80))tmp_opts.EmuOpt |= 0x80;
else if ( tmp_opts.EmuOpt &0x80) break;
} else {
if ( tmp_opts.PicoOpt&0x10) break;
else if (!(tmp_opts.EmuOpt &0x80))tmp_opts.PicoOpt|= 0x10;
else if ( tmp_opts.EmuOpt &0x80) tmp_opts.EmuOpt &= ~0x80;
}
break;
case 4:
tmp_opts.Frameskip += (inp & GP2X_LEFT) ? -1 : 1;
if (tmp_opts.Frameskip < 0) tmp_opts.Frameskip = -1;
if (tmp_opts.Frameskip > 32) tmp_opts.Frameskip = 32;
break;
case 6:
if ((inp & GP2X_RIGHT) && tmp_opts.PsndRate == 44100 && !(tmp_opts.PicoOpt&0x08)) {
tmp_opts.PsndRate = 8000; tmp_opts.PicoOpt|= 0x08;
} else if ((inp & GP2X_LEFT) && tmp_opts.PsndRate == 8000 && (tmp_opts.PicoOpt&0x08)) {
tmp_opts.PsndRate = 44100; tmp_opts.PicoOpt&=~0x08;
} else tmp_opts.PsndRate = sndrate_prevnext(tmp_opts.PsndRate, inp & GP2X_RIGHT);
break;
case 9:
if (inp & GP2X_RIGHT) {
if (tmp_opts.PicoRegion) tmp_opts.PicoRegion<<=1; else tmp_opts.PicoRegion=1;
if (tmp_opts.PicoRegion > 8) tmp_opts.PicoRegion = 8;
} else tmp_opts.PicoRegion>>=1;
break;
case 12:
if (inp & GP2X_RIGHT) {
state_slot++; if (state_slot > 9) state_slot = 0;
} else {state_slot--; if (state_slot < 0) state_slot = 9;
}
break;
case 13:
while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {
tmp_opts.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;
if (tmp_opts.CPUclock < 1) tmp_opts.CPUclock = 1;
draw_menu_options(menu_sel);
usleep(50*1000);
}
break;
}
}
}
}
// -------------- credits --------------
static void draw_menu_credits(void)
{
int tl_x = 15, tl_y = 70, y;
memset(gp2x_screen, 0, 320*240);
gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006");
y = tl_y;
gp2x_text_out8(tl_x, y, "Credits:");
gp2x_text_out8(tl_x, (y+=10), "Dave: Cyclone 68000 core,");
gp2x_text_out8(tl_x, (y+=10), " base code of PicoDrive");
gp2x_text_out8(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");
gp2x_text_out8(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");
gp2x_text_out8(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");
gp2x_text_out8(tl_x, (y+=10), "Stephane Dallongeville:");
gp2x_text_out8(tl_x, (y+=10), " opensource Gens");
gp2x_text_out8(tl_x, (y+=10), "Haze: Genesis hw info");
gp2x_text_out8(tl_x, (y+=10), "rlyeh and others: minimal SDK");
gp2x_text_out8(tl_x, (y+=10), "Squidge: squidgehack");
gp2x_text_out8(tl_x, (y+=10), "Dzz: ARM940 sample");
gp2x_text_out8(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");
gp2x_text_out8(tl_x, (y+=10), "craigix: GP2X hardware");
gp2x_video_flip();
}
// -------------- root menu --------------
static void draw_menu_root(int menu_sel)
{
int tl_x = 70, tl_y = 70, y;
memset(gp2x_screen, 0, 320*240);
gp2x_text_out8(tl_x, 20, "PicoDrive v" VERSION);
y = tl_y;
if (rom_data) {
gp2x_text_out8(tl_x, y, "Resume game");
gp2x_text_out8(tl_x, (y+=10), "Save State");
gp2x_text_out8(tl_x, (y+=10), "Load State");
gp2x_text_out8(tl_x, (y+=10), "Reset game");
} else {
y += 30;
}
gp2x_text_out8(tl_x, (y+=10), "Load new ROM");
gp2x_text_out8(tl_x, (y+=10), "Change options");
gp2x_text_out8(tl_x, (y+=10), "Configure controls");
gp2x_text_out8(tl_x, (y+=10), "Credits");
gp2x_text_out8(tl_x, (y+=10), "Exit");
// draw cursor
gp2x_text_out8(tl_x - 16, tl_y + menu_sel*10, ">");
// error
if (menuErrorMsg[0]) gp2x_text_out8(5, 226, menuErrorMsg);
gp2x_video_flip();
}
static void menu_loop_root(void)
{
int menu_sel = 4, menu_sel_max = 8, menu_sel_min = 4;
unsigned long inp = 0;
char curr_path[PATH_MAX], *selfname;
FILE *tstf;
if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )
{
fclose(tstf);
strcpy(curr_path, currentConfig.lastRomFile);
}
else
{
getcwd(curr_path, PATH_MAX);
}
if (rom_data) menu_sel = menu_sel_min = 0;
for(;;)
{
draw_menu_root(menu_sel);
inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT);
if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < menu_sel_min) menu_sel = menu_sel_max; }
if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = menu_sel_min; }
if(inp &(GP2X_SELECT|GP2X_X)){
if (rom_data) {
while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released
engineState = PGS_Running;
break;
}
}
if(inp & GP2X_B ) {
switch (menu_sel) {
case 0: // resume game
if (rom_data) { engineState = PGS_Running; return; }
break;
case 1: // save state
if (rom_data) {
if(emu_SaveLoadGame(0, 0)) {
strcpy(menuErrorMsg, "save failed");
continue;
}
engineState = PGS_Running;
return;
}
break;
case 2: // load state
if (rom_data) {
if(emu_SaveLoadGame(1, 0)) {
strcpy(menuErrorMsg, "load failed");
continue;
}
engineState = PGS_Running;
return;
}
break;
case 3: // reset game
if (rom_data) {
emu_ResetGame();
engineState = PGS_Running;
return;
}
break;
case 4: // select rom
selfname = romsel_loop(curr_path);
if (selfname) {
printf("selected file: %s\n", selfname);
strncpy(currentConfig.lastRomFile, selfname, sizeof(currentConfig.lastRomFile)-1);
currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0;
engineState = PGS_ReloadRom;
}
return;
case 5: // options
menu_loop_options();
break;
case 6: // controls
kc_sel_loop();
break;
case 7: // credits
draw_menu_credits();
usleep(500*1000);
inp = wait_for_input(GP2X_B|GP2X_X);
break;
case 8: // exit
engineState = PGS_Quit;
return;
}
}
menuErrorMsg[0] = 0; // clear error msg
}
}
void menu_loop(void)
{
int pal[2];
// switch to 8bpp
gp2x_video_changemode(8);
gp2x_video_RGB_setscaling(320, 240);
// set pal
pal[0] = 0;
pal[1] = 0x00ffffff;
gp2x_video_setpalette(pal, 2);
menu_loop_root();
menuErrorMsg[0] = 0;
}

16
platform/gp2x/menu.h Normal file
View file

@ -0,0 +1,16 @@
// (c) Copyright 2006 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
extern char menuErrorMsg[40];
void gp2x_text_out8 (int x, int y, char *texto, ...);
void gp2x_text_out15 (int x, int y, char *text);
void gp2x_text_out8_2(int x, int y, char *texto, int color);
void menu_loop(void);
#define CONFIGURABLE_KEYS \
(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_A|GP2X_B|GP2X_X|GP2X_Y| \
GP2X_START|GP2X_SELECT|GP2X_L|GP2X_R|GP2X_PUSH|GP2X_VOL_UP|GP2X_VOL_DOWN)

Some files were not shown because too many files have changed in this diff Show more