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