Update to v078r02 release.

byuu says:

New S-SMP core is feature-complete, but still buggy.  It's good enough
for perfect audio in Zelda 3 and Super Mario World, but there are plenty
of issues.  No audio in Bahamut Lagoon, deadlock in Earthworm Jim 2,
etc.

With this core, bsnes/Performance runs about 3-5% faster than with the
old one. That won't seem like much, because the S-SMP is the least
demanding portion of the SNES.  blargg's SMP core netted me a 5-8%
speedup the last time I tried it, so I'm sure there's still room to
speed things up.

The core is opcode-based, but has dummy op_io() calls (they compile to
nothing), so it is trivial to make it cycle-based if desired.  I'm not
convinced that is necessary, but we shall see once we get the opcode
bugs ironed out.
This commit is contained in:
Tim Allen 2011-05-03 19:58:12 +10:00
parent 9a3650c6ab
commit 67e6a6e742
9 changed files with 832 additions and 157 deletions

View File

@ -1,11 +1,11 @@
include nall/Makefile
snes := snes
gameboy := gameboy
profile := accuracy
profile := performance
ui := ui
# debugger
options := debugger
options :=
# compiler
c := $(compiler) -std=gnu99

View File

@ -1,94 +1,136 @@
void SMP::op_io() {
timer0.tick();
timer1.tick();
timer2.tick();
}
uint8 SMP::op_read(uint16 addr) {
timer0.tick();
timer1.tick();
timer2.tick();
if((addr & 0xfff0) == 0x00f0) return mmio_read(addr);
if((addr & 0xffc0) == 0xffc0 && status.iplrom_enable) return iplrom[addr & 0x3f];
return apuram[addr];
}
void SMP::op_write(uint16 addr, uint8 data) {
timer0.tick();
timer1.tick();
timer2.tick();
if((addr & 0xfff0) == 0x00f0) mmio_write(addr, data);
apuram[addr] = data; //all writes go to RAM, even MMIO writes
}
#define op_readpc() op_read(regs.pc++)
#define op_readdp(addr) op_read((regs.p.p << 8) + addr)
#define op_writedp(addr, data) op_write((regs.p.p << 8) + addr, data)
#define OP_ADJUST_DP(func) \
dp = op_readpc(); \
rd = op_readdp(dp); \
rd = func(rd); \
op_writedp(dp, rd);
#define OP_ADJUST_REG(func, target) \
op_io(); \
target = func(target);
#define OP_BRANCH(condition) \
rd = op_readpc(); \
if(condition) { \
op_io(); \
op_io(); \
regs.pc += (int8)rd; \
}
#define OP_MOV_DP_REG(source) \
dp = op_readpc(); \
op_readdp(dp); \
op_writedp(dp, source);
#define OP_MOV_REG_CONST(target) \
target = op_readpc(); \
regs.p.n = (target & 0x80); \
regs.p.z = (target == 0);
#define OP_MOV_REG_DP(target) \
sp = op_readpc(); \
target = op_readdp(sp); \
regs.p.n = (target & 0x80); \
regs.p.z = (target == 0);
#define OP_MOV_REG_REG(target, source) \
op_io(); \
target = source; \
regs.p.n = (target & 0x80); \
regs.p.z = (target == 0);
#define OP_READ_REG_DP(func, target) \
dp = op_readpc(); \
rd = op_readdp(dp); \
target = func(target, rd);
//TODO:
//* untaken conditional branches should subtract from opcode's cycle overhead
void SMP::op_step() {
unsigned opcode = op_readpc();
op_io();
#define op_readpc() op_read(regs.pc++)
#define op_readdp(addr) op_read((regs.p.p << 8) + addr)
#define op_writedp(addr, data) op_write((regs.p.p << 8) + addr, data)
//#define op_readsp() op_read(0x0100 | ++regs.sp)
//#define op_writesp(data) op_write(0x0100 | regs.sp--, data)
#define op_readsp() stackram[++regs.sp]
#define op_writesp(data) stackram[regs.sp--] = data
static unsigned rd, wr, dp, sp, ya, bit;
static unsigned rd, wr, dp, sp;
unsigned opcode = op_readpc();
switch(opcode) {
case 0x00: //nop
op_io();
break;
case 0x10: //bpl $rr
OP_BRANCH(regs.p.n == 0)
case 0xef: //sleep
case 0xff: //stop
regs.pc--;
break;
case 0x1d: //dec x
OP_ADJUST_REG(fn_dec, regs.x)
//
// batch
//
case 0x01: case 0x11: case 0x21: case 0x31: case 0x41: case 0x51: case 0x61: case 0x71:
case 0x81: case 0x91: case 0xa1: case 0xb1: case 0xc1: case 0xd1: case 0xe1: case 0xf1: //tcall vector
dp = 0xffde - ((opcode >> 4) << 1);
rd = op_read(dp + 0) << 0;
rd |= op_read(dp + 1) << 8;
op_io();
op_io();
op_io();
op_writesp(regs.pc >> 8);
op_writesp(regs.pc >> 0);
regs.pc = rd;
break;
case 0x02: case 0x22: case 0x42: case 0x62: case 0x82: case 0xa2: case 0xc2: case 0xe2: //setB $dp
dp = op_readpc();
rd = op_readdp(dp) | (1 << (opcode >> 5));
op_writedp(dp, rd);
break;
case 0x12: case 0x32: case 0x52: case 0x72: case 0x92: case 0xb2: case 0xd2: case 0xf2: //clrB $dp
dp = op_readpc();
rd = op_readdp(dp) &~ (1 << (opcode >> 5));
op_writedp(dp, rd);
break;
case 0x03: case 0x23: case 0x43: case 0x63: case 0x83: case 0xa3: case 0xc3: case 0xe3: //bbsB $dp,$rr
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & (1 << (opcode >> 5))) == 0) break;
op_io();
op_io();
regs.pc += (int8)rd;
break;
case 0x13: case 0x33: case 0x53: case 0x73: case 0x93: case 0xb3: case 0xd3: case 0xf3: //bbcB $dp,$rr
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & (1 << (opcode >> 5))) != 0) break;
op_io();
op_io();
regs.pc += (int8)rd;
break;
case 0x20: op_io(); regs.p.p = 0; break; //clrp
case 0x40: op_io(); regs.p.p = 1; break; //setp
case 0x60: op_io(); regs.p.c = 0; break; //clrc
case 0x80: op_io(); regs.p.c = 1; break; //setc
case 0xe0: op_io(); regs.p.v = 0; break; //clrv
//
case 0xa0: op_io(); op_io(); regs.p.i = 1; break; //ei
case 0xc0: op_io(); op_io(); regs.p.i = 0; break; //di
//
case 0xed: op_io(); op_io(); regs.p.c = !regs.p.c; break; //notc
//
// jump
//
#define OP_BRANCH_IF(condition) \
rd = op_readpc(); \
if((condition) == false) break; \
op_io(); \
op_io(); \
regs.pc += (int8)rd; \
break;
case 0x10: OP_BRANCH_IF(regs.p.n == 0); //bpl $rr
case 0x30: OP_BRANCH_IF(regs.p.n == 1); //bmi $rr
case 0x50: OP_BRANCH_IF(regs.p.v == 0); //bvc $rr
case 0x70: OP_BRANCH_IF(regs.p.v == 1); //bvs $rr
case 0x90: OP_BRANCH_IF(regs.p.c == 0); //bcc $rr
case 0xb0: OP_BRANCH_IF(regs.p.c == 1); //bcs $rr
case 0xd0: OP_BRANCH_IF(regs.p.z == 0); //bne $rr
case 0xf0: OP_BRANCH_IF(regs.p.z == 1); //beq $rr
case 0x0f: //brk
rd = op_read(0xffde) << 0;
rd |= op_read(0xffdf) << 8;
op_io();
op_io();
op_writesp(regs.pc >> 8);
op_writesp(regs.pc >> 0);
op_writesp(regs.p);
regs.pc = rd;
regs.p.b = 1;
regs.p.i = 0;
break;
case 0x1f: //jmp ($aaaa,x)
@ -108,26 +150,437 @@ void SMP::op_step() {
regs.pc += (int8)rd;
break;
case 0x5d: //mov x,a
OP_MOV_REG_REG(regs.x, regs.a)
case 0x3f: //call $addr
rd = op_readpc() << 0;
rd |= op_readpc() << 8;
op_io();
op_io();
op_io();
op_writesp(regs.pc >> 8);
op_writesp(regs.pc >> 0);
regs.pc = rd;
break;
case 0x78: //cmp $dp,#$ss
case 0x4f: //pcall $ff00+$dp
rd = op_readpc();
op_io();
op_io();
op_writesp(regs.pc >> 8);
op_writesp(regs.pc >> 0);
regs.pc = 0xff00 | rd;
break;
case 0x5f: //jmp $addr
rd = op_readpc() << 0;
rd |= op_readpc() << 8;
regs.pc = rd;
break;
case 0x6f: //ret
rd = op_readsp() << 0;
rd |= op_readsp() << 8;
op_io();
op_io();
regs.pc = rd;
break;
case 0x7f: //reti
regs.p = op_readsp();
rd = op_readsp() << 0;
rd |= op_readsp() << 8;
op_io();
op_io();
regs.pc = rd;
break;
case 0x2e: //cbne $dp,$rr
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if(regs.a == sp) break;
op_io();
op_io();
regs.pc += (int8)rd;
break;
case 0x6e: //dbnz $dp,$rr
dp = op_readpc();
wr = op_readdp(dp);
fn_cmp(wr, rd);
op_writedp(dp, --wr);
rd = op_readpc();
if(wr == 0) break;
op_io();
op_io();
regs.pc += (int8)rd;
break;
case 0x7d: //mov a,x
OP_MOV_REG_REG(regs.a, regs.x)
case 0xde: //cbne $dp+x,$rr
dp = op_readpc();
op_io();
sp = op_readdp(dp + regs.x);
rd = op_readpc();
op_io();
if(regs.a == sp) break;
op_io();
op_io();
regs.pc += (int8)rd;
break;
case 0x7e: //cmp y,$dp
OP_READ_REG_DP(fn_cmp, regs.y)
case 0xfe: //dbz y,$rr
rd = op_readpc();
op_io();
regs.y--;
op_io();
if(regs.y == 0) break;
op_io();
op_io();
regs.pc += (int8)rd;
break;
//
// stack
//
#define OP_PUSH_REG(source) \
op_io(); \
op_io(); \
op_writesp(source); \
break;
case 0x0d: OP_PUSH_REG(regs.p); //push p
case 0x2d: OP_PUSH_REG(regs.a); //push a
case 0x4d: OP_PUSH_REG(regs.x); //push x
case 0x6d: OP_PUSH_REG(regs.y); //push y
#define OP_POP_REG(target) \
op_io(); \
op_io(); \
target = op_readsp(); \
break;
case 0x8e: OP_POP_REG(regs.p); //pop p
case 0xae: OP_POP_REG(regs.a); //pop a
case 0xce: OP_POP_REG(regs.x); //pop x
case 0xee: OP_POP_REG(regs.y); //pop y
//
// ALU
//
case 0x9e: //div ya,x
op_io(); op_io(); op_io(); op_io();
op_io(); op_io(); op_io(); op_io();
op_io(); op_io(); op_io();
ya = regs.ya;
regs.p.v = !!(regs.y >= regs.x);
regs.p.h = !!((regs.y & 15) >= (regs.x & 15));
if(regs.y < (regs.x << 1)) {
regs.a = ya / regs.x;
regs.y = ya % regs.x;
} else {
regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x);
regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x);
}
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
break;
case 0xcf: //mul ya
op_io(); op_io(); op_io(); op_io();
op_io(); op_io(); op_io(); op_io();
ya = regs.y * regs.a;
regs.a = ya;
regs.y = ya >> 8;
regs.p.n = (regs.y & 0x80);
regs.p.z = (regs.y == 0);
break;
//
// read
//
#define OP_READ_REG_DP(func, target) \
dp = op_readpc(); \
rd = op_readdp(dp); \
target = func(target, rd); \
break;
case 0x04: OP_READ_REG_DP(fn_or, regs.a); //or a,$dp
case 0x24: OP_READ_REG_DP(fn_and, regs.a); //and a,$dp
case 0x44: OP_READ_REG_DP(fn_eor, regs.a); //eor a,$dp
case 0x64: OP_READ_REG_DP(fn_cmp, regs.a); //cmp a,$dp
case 0x84: OP_READ_REG_DP(fn_adc, regs.a); //adc a,$dp
case 0xa4: OP_READ_REG_DP(fn_sbc, regs.a); //sbc a,$dp
//
case 0x3e: OP_READ_REG_DP(fn_cmp, regs.x); //cmp x,$dp
case 0x7e: OP_READ_REG_DP(fn_cmp, regs.y); //cmp y,$dp
#define OP_READ_REG_ADDR(func, target) \
dp = op_readpc() << 0; \
dp |= op_readpc() << 8; \
rd = op_read(dp); \
target = func(target, rd); \
break;
case 0x05: OP_READ_REG_ADDR(fn_or, regs.a); //or a,$addr
case 0x25: OP_READ_REG_ADDR(fn_and, regs.a); //and a,$addr
case 0x45: OP_READ_REG_ADDR(fn_eor, regs.a); //eor a,$addr
case 0x65: OP_READ_REG_ADDR(fn_cmp, regs.a); //cmp a,$addr
case 0x85: OP_READ_REG_ADDR(fn_adc, regs.a); //adc a,$addr
case 0xa5: OP_READ_REG_ADDR(fn_sbc, regs.a); //sbc a,$addr
//
case 0x1e: OP_READ_REG_ADDR(fn_cmp, regs.x); //cmp x,$addr
case 0x5e: OP_READ_REG_ADDR(fn_cmp, regs.y); //cmp y,$addr
#define OP_READ_A_IX(func) \
op_io(); \
rd = op_readdp(regs.x); \
regs.a = func(regs.a, rd); \
break;
case 0x06: OP_READ_A_IX(fn_or ); //or a,(x)
case 0x26: OP_READ_A_IX(fn_and); //and a,(x)
case 0x46: OP_READ_A_IX(fn_eor); //eor a,(x)
case 0x66: OP_READ_A_IX(fn_cmp); //cmp a,(x)
case 0x86: OP_READ_A_IX(fn_adc); //adc a,(x)
case 0xa6: OP_READ_A_IX(fn_sbc); //sbc a,(x)
#define OP_READ_A_IDPX(func) \
dp = op_readpc() + regs.x; \
op_io(); \
sp = op_readdp(dp + 0) << 0; \
sp |= op_readdp(dp + 1) << 8; \
rd = op_read(sp); \
regs.a = func(regs.a, rd); \
break;
case 0x07: OP_READ_A_IDPX(fn_or ); //or a,($dp+x)
case 0x27: OP_READ_A_IDPX(fn_and); //and a,($dp+x)
case 0x47: OP_READ_A_IDPX(fn_eor); //eor a,($dp+x)
case 0x67: OP_READ_A_IDPX(fn_cmp); //cmp a,($dp+x)
case 0x87: OP_READ_A_IDPX(fn_adc); //adc a,($dp+x)
case 0xa7: OP_READ_A_IDPX(fn_sbc); //sbc a,($dp+x)
#define OP_READ_REG_CONST(func, target) \
rd = op_readpc(); \
target = func(target, rd); \
break;
case 0x08: OP_READ_REG_CONST(fn_or, regs.a); //or a,#$ss
case 0x28: OP_READ_REG_CONST(fn_and, regs.a); //and a,#$ss
case 0x48: OP_READ_REG_CONST(fn_eor, regs.a); //eor a,#$ss
case 0x68: OP_READ_REG_CONST(fn_cmp, regs.a); //cmp a,#$ss
case 0x88: OP_READ_REG_CONST(fn_adc, regs.a); //adc a,#$ss
case 0xa8: OP_READ_REG_CONST(fn_sbc, regs.a); //sbc a,#$ss
//
case 0xad: OP_READ_REG_CONST(fn_cmp, regs.y); //cmp y,#$ss
case 0xc8: OP_READ_REG_CONST(fn_cmp, regs.x); //cmp x,#$ss
#define OP_READ_DP_DP(func) \
sp = op_readpc(); \
rd = op_readdp(sp); \
dp = op_readpc(); \
wr = op_readdp(dp); \
wr = func(wr, rd);
case 0x09: OP_READ_DP_DP(fn_or ); op_writedp(dp, wr); break; //or $dp,$dp
case 0x29: OP_READ_DP_DP(fn_and); op_writedp(dp, wr); break; //and $dp,$dp
case 0x49: OP_READ_DP_DP(fn_eor); op_writedp(dp, wr); break; //eor $dp,$dp
case 0x69: OP_READ_DP_DP(fn_cmp); op_io(); break; //cmp $dp,$dp
case 0x89: OP_READ_DP_DP(fn_adc); op_writedp(dp, wr); break; //adc $dp,$dp
case 0xa9: OP_READ_DP_DP(fn_sbc); op_writedp(dp, wr); break; //sbc $dp,$dp
#define OP_READ_A_DPX(func) \
dp = op_readpc(); \
op_io(); \
rd = op_readdp(dp + regs.x); \
regs.a = func(regs.a, rd); \
break;
case 0x14: OP_READ_A_DPX(fn_or ); //or a,$dp+x
case 0x34: OP_READ_A_DPX(fn_and); //and a,$dp+x
case 0x54: OP_READ_A_DPX(fn_eor); //eor a,$dp+x
case 0x74: OP_READ_A_DPX(fn_cmp); //cmp a,$dp+x
case 0x94: OP_READ_A_DPX(fn_adc); //adc a,$dp+x
case 0xb4: OP_READ_A_DPX(fn_sbc); //sbc a,$dp+x
#define OP_READ_A_ADDRR(func, source) \
dp = op_readpc() << 0; \
dp |= op_readpc() << 8; \
op_io(); \
rd = op_read(dp + source); \
regs.a = func(regs.a, rd); \
break;
case 0x15: OP_READ_A_ADDRR(fn_or , regs.x); //or a,$dp+x
case 0x16: OP_READ_A_ADDRR(fn_or , regs.y); //or a,$dp+y
case 0x35: OP_READ_A_ADDRR(fn_and, regs.x); //and a,$dp+x
case 0x36: OP_READ_A_ADDRR(fn_and, regs.y); //and a,$dp+y
case 0x55: OP_READ_A_ADDRR(fn_eor, regs.x); //eor a,$dp+x
case 0x56: OP_READ_A_ADDRR(fn_eor, regs.y); //eor a,$dp+y
case 0x75: OP_READ_A_ADDRR(fn_cmp, regs.x); //cmp a,$dp+x
case 0x76: OP_READ_A_ADDRR(fn_cmp, regs.y); //cmp a,$dp+y
case 0x95: OP_READ_A_ADDRR(fn_adc, regs.x); //adc a,$dp+x
case 0x96: OP_READ_A_ADDRR(fn_adc, regs.y); //adc a,$dp+y
case 0xb5: OP_READ_A_ADDRR(fn_sbc, regs.x); //sbc a,$dp+x
case 0xb6: OP_READ_A_ADDRR(fn_sbc, regs.y); //sbc a,$dp+y
#define OP_READ_A_IDPY(func) \
dp = op_readpc(); \
op_io(); \
sp = op_readdp(dp + 0) << 0; \
sp |= op_readdp(dp + 1) << 8; \
rd = op_read(sp + regs.y); \
regs.a = func(regs.a, rd); \
break;
case 0x17: OP_READ_A_IDPY(fn_or ); //or a,($dp)+y
case 0x37: OP_READ_A_IDPY(fn_and); //and a,($dp)+y
case 0x57: OP_READ_A_IDPY(fn_eor); //eor a,($dp)+y
case 0x77: OP_READ_A_IDPY(fn_cmp); //cmp a,($dp)+y
case 0x97: OP_READ_A_IDPY(fn_adc); //adc a,($dp)+y
case 0xb7: OP_READ_A_IDPY(fn_sbc); //sbc a,($dp)+y
#define OP_READ_DP_CONST(func) \
rd = op_readpc(); \
dp = op_readpc(); \
wr = op_readdp(dp); \
wr = func(wr, rd);
case 0x18: OP_READ_DP_CONST(fn_or ); op_writedp(dp, wr); break; //or $dp,#$ss
case 0x38: OP_READ_DP_CONST(fn_and); op_writedp(dp, wr); break; //and $dp,#$ss
case 0x58: OP_READ_DP_CONST(fn_eor); op_writedp(dp, wr); break; //eor $dp,#$ss
case 0x78: OP_READ_DP_CONST(fn_cmp); op_io(); break; //cmp $dp,#$ss
case 0x98: OP_READ_DP_CONST(fn_adc); op_writedp(dp, wr); break; //adc $dp,#$ss
case 0xb8: OP_READ_DP_CONST(fn_sbc); op_writedp(dp, wr); break; //sbc $dp,#$ss
#define OP_READ_IX_IY(func) \
op_io(); \
rd = op_readdp(regs.y); \
wr = op_readdp(regs.x); \
wr = func(wr, rd);
case 0x19: OP_READ_IX_IY(fn_or ); op_writedp(regs.x, wr); break; //or (x),(y)
case 0x39: OP_READ_IX_IY(fn_and); op_writedp(regs.x, wr); break; //and (x),(y)
case 0x59: OP_READ_IX_IY(fn_eor); op_writedp(regs.x, wr); break; //eor (x),(y)
case 0x79: OP_READ_IX_IY(fn_cmp); op_io(); break; //cmp (x),(y)
case 0x99: OP_READ_IX_IY(fn_adc); op_writedp(regs.x, wr); break; //adc (x),(y)
case 0xb9: OP_READ_IX_IY(fn_sbc); op_writedp(regs.x, wr); break; //sbc (x),(y)
#define OP_READ_YA_DP(func) \
dp = op_readpc(); \
rd = op_readdp(dp + 0) << 0; \
op_io(); \
rd |= op_readdp(dp + 1) << 8; \
regs.ya = func(regs.ya, rd); \
break;
case 0x7a: OP_READ_YA_DP(fn_addw); //addw $dp
case 0x9a: OP_READ_YA_DP(fn_subw); //subw $dp
//
// move
//
#define OP_MOV_REG_REG(target, source) \
op_io(); \
target = source; \
regs.p.n = (target & 0x80); \
regs.p.z = (target == 0); \
break;
case 0x5d: OP_MOV_REG_REG(regs.x, regs.a ); //mov x,a
case 0x7d: OP_MOV_REG_REG(regs.a, regs.x ); //mov a,x
case 0x9d: OP_MOV_REG_REG(regs.x, regs.sp); //mov x,sp
case 0xdd: OP_MOV_REG_REG(regs.a, regs.y ); //mov a,y
case 0xfd: OP_MOV_REG_REG(regs.y, regs.a ); //mov y,a
#define OP_MOV_REG_CONST(target) \
target = op_readpc(); \
regs.p.n = (target & 0x80); \
regs.p.z = (target == 0); \
break;
case 0x8d: OP_MOV_REG_CONST(regs.y); //mov y,#$ss
case 0xcd: OP_MOV_REG_CONST(regs.x); //mov x,#$ss
case 0xe8: OP_MOV_REG_CONST(regs.a); //mov a,#$ss
#define OP_MOV_DP_REG(source) \
dp = op_readpc(); \
op_readdp(dp); \
op_writedp(dp, source); \
break;
case 0xc4: OP_MOV_DP_REG(regs.a); //mov $dp,a
case 0xcb: OP_MOV_DP_REG(regs.y); //mov $dp,y
case 0xd8: OP_MOV_DP_REG(regs.x); //mov $dp,x
#define OP_MOV_ADDR_REG(source) \
dp = op_readpc() << 0; \
dp |= op_readpc() << 8; \
op_read(dp); \
op_write(dp, source); \
break;
case 0xc5: OP_MOV_ADDR_REG(regs.a); //mov $addr,a
case 0xc9: OP_MOV_ADDR_REG(regs.x); //mov $addr,x
case 0xcc: OP_MOV_ADDR_REG(regs.y); //mov $addr,y
#define OP_MOV_DPR_REG(target, source) \
dp = op_readpc(); \
op_io(); \
dp += source; \
op_readdp(dp); \
op_writedp(dp, target); \
break;
case 0xd4: OP_MOV_DPR_REG(regs.a, regs.x); //mov $dp+a,x
case 0xd9: OP_MOV_DPR_REG(regs.x, regs.y); //mov $dp+x,y
case 0xdb: OP_MOV_DPR_REG(regs.y, regs.x); //mov $dp+y,x
#define OP_MOV_ADDRR_A(source) \
dp = op_readpc() << 0; \
dp |= op_readpc() << 8; \
op_io(); \
dp += source; \
op_read(dp); \
op_write(dp, regs.a); \
break;
case 0xd5: OP_MOV_ADDRR_A(regs.x); //mov $addr+x,a
case 0xd6: OP_MOV_ADDRR_A(regs.y); //mov $addr+y,a
#define OP_MOV_REG_DP(target) \
sp = op_readpc(); \
target = op_readdp(sp); \
regs.p.n = (target & 0x80); \
regs.p.z = (target == 0); \
break;
case 0xe4: OP_MOV_REG_DP(regs.a); //mov a,$dp
case 0xeb: OP_MOV_REG_DP(regs.y); //mov y,$dp
case 0xf8: OP_MOV_REG_DP(regs.x); //mov x,$dp
#define OP_MOV_REG_ADDR(target) \
sp = op_readpc() << 0; \
sp |= op_readpc() << 8; \
target = op_read(sp); \
regs.p.n = (target & 0x80); \
regs.p.z = (target == 0); \
break;
case 0xe5: OP_MOV_REG_ADDR(regs.a); //mov a,$addr
case 0xe9: OP_MOV_REG_ADDR(regs.x); //mov x,$addr
case 0xec: OP_MOV_REG_ADDR(regs.y); //mov y,$addr
case 0xe6: //mov a,(x)
op_io();
regs.a = op_readdp(regs.x);
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
break;
#define OP_MOV_REG_DPR(target, source) \
sp = op_readpc(); \
op_io(); \
target = op_readdp(sp + source); \
regs.p.n = (target & 0x80); \
regs.p.z = (target == 0); \
break;
case 0xf4: OP_MOV_REG_DPR(regs.a, regs.x); //mov a,$dp+x
case 0xf9: OP_MOV_REG_DPR(regs.x, regs.y); //mov x,$dp+y
case 0xfb: OP_MOV_REG_DPR(regs.y, regs.x); //mov y,$dp+x
#define OP_MOV_A_ADDRR(source) \
sp = op_readpc() << 0; \
sp |= op_readpc() << 8; \
op_io(); \
regs.a = op_read(sp + source); \
regs.p.n = (regs.a & 0x80); \
regs.p.z = (regs.a == 0); \
break;
case 0xf5: OP_MOV_A_ADDRR(regs.x); //mov a,$addr+x
case 0xf6: OP_MOV_A_ADDRR(regs.y); //mov a,$addr+y
case 0x8f: //mov $dp,#$ss
rd = op_readpc();
dp = op_readpc();
@ -135,8 +588,10 @@ void SMP::op_step() {
op_writedp(dp, rd);
break;
case 0xab: //inc $dp
OP_ADJUST_DP(fn_inc)
case 0xaf: //mov (x)+,a
op_io();
op_io();
op_writedp(regs.x++, regs.a);
break;
case 0xba: //movw ya,$dp
@ -153,8 +608,12 @@ void SMP::op_step() {
regs.sp = regs.x;
break;
case 0xc4: //mov $dd,a
OP_MOV_DP_REG(regs.a)
case 0xbf: //mov a,(x)+
op_io();
regs.a = op_readdp(regs.x++);
op_io();
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
break;
case 0xc6: //mov (x),a
@ -163,16 +622,14 @@ void SMP::op_step() {
op_writedp(regs.x, regs.a);
break;
case 0xcb: //mov $dp,y
OP_MOV_DP_REG(regs.y)
break;
case 0xcd: //mov x,#$ss
OP_MOV_REG_CONST(regs.x)
break;
case 0xd0: //bne $rr
OP_BRANCH(regs.p.z == 0)
case 0xc7: //mov ($dp+x),a
sp = op_readpc();
op_io();
sp += regs.x;
dp = op_readdp(sp + 0) << 0;
dp |= op_readdp(sp + 1) << 8;
op_read(dp);
op_write(dp, regs.a);
break;
case 0xd7: //mov ($dp)+y,a
@ -192,43 +649,245 @@ void SMP::op_step() {
op_writedp(dp + 1, regs.y);
break;
case 0xdd: //mov a,y
OP_MOV_REG_REG(regs.a, regs.y)
case 0xe7: //mov a,($dp+x)
dp = op_readpc() + regs.x;
op_io();
sp = op_readdp(dp + 0) << 0;
sp |= op_readdp(dp + 1) << 8;
regs.a = op_read(sp);
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
break;
case 0xe4: //mov y,$dp
OP_MOV_REG_DP(regs.a)
case 0xf7: //mov a,($dp)+y
dp = op_readpc();
op_io();
sp = op_readdp(dp + 0) << 0;
sp |= op_readdp(dp + 1) << 8;
regs.a = op_read(sp + regs.y);
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
break;
case 0xe8: //mov a,#$ss
OP_MOV_REG_CONST(regs.a)
case 0xfa: //mov $dp,$dp
sp = op_readpc();
rd = op_readdp(sp);
dp = op_readpc();
op_writedp(dp, rd);
break;
case 0xeb: //mov y,$dp
OP_MOV_REG_DP(regs.y)
//
// read/modify/write
//
#define OP_ADJUST_DP(func) \
dp = op_readpc(); \
rd = op_readdp(dp); \
rd = func(rd); \
op_writedp(dp, rd); \
break;
case 0x0b: OP_ADJUST_DP(fn_asl); //asl $dp
case 0x2b: OP_ADJUST_DP(fn_rol); //rol $dp
case 0x4b: OP_ADJUST_DP(fn_lsr); //lsr $dp
case 0x6b: OP_ADJUST_DP(fn_ror); //ror $dp
case 0x8b: OP_ADJUST_DP(fn_dec); //dec $dp
case 0xab: OP_ADJUST_DP(fn_inc); //inc $dp
#define OP_ADJUST_ADDR(func) \
dp = op_readpc() << 0; \
dp |= op_readpc() << 8; \
rd = op_read(dp); \
rd = func(rd); \
op_write(dp, rd); \
break;
case 0x0c: OP_ADJUST_ADDR(fn_asl); //asl $addr
case 0x2c: OP_ADJUST_ADDR(fn_rol); //rol $addr
case 0x4c: OP_ADJUST_ADDR(fn_lsr); //lsr $addr
case 0x6c: OP_ADJUST_ADDR(fn_ror); //ror $addr
case 0x8c: OP_ADJUST_ADDR(fn_dec); //dec $addr
case 0xac: OP_ADJUST_ADDR(fn_inc); //inc $addr
#define OP_ADJUSTW_DP(adjust) \
dp = op_readpc(); \
rd = op_readdp(dp) << 0; \
rd += adjust; \
op_writedp(dp++, rd); \
rd += op_readdp(dp) << 8; \
op_writedp(dp, rd >> 8); \
regs.p.n = (rd & 0x8000); \
regs.p.z = (rd == 0); \
break;
case 0x1a: OP_ADJUSTW_DP(-1); //decw $dp
case 0x3a: OP_ADJUSTW_DP(+1); //incw $dp
#define OP_ADJUST_DPX(func) \
dp = op_readpc(); \
op_io(); \
rd = op_readdp(dp + regs.x); \
rd = func(rd); \
op_writedp(dp + regs.x, rd); \
break;
case 0x1b: OP_ADJUST_DPX(fn_asl); //asl $dp+x
case 0x3b: OP_ADJUST_DPX(fn_rol); //rol $dp+x
case 0x5b: OP_ADJUST_DPX(fn_lsr); //lsr $dp+x
case 0x7b: OP_ADJUST_DPX(fn_ror); //ror $dp+x
case 0x9b: OP_ADJUST_DPX(fn_dec); //dec $dp+x
case 0xbb: OP_ADJUST_DPX(fn_inc); //inc $dp+x
#define OP_ADJUST_REG(func, target) \
op_io(); \
target = func(target); \
break;
case 0x1c: OP_ADJUST_REG(fn_asl, regs.a); //asl a
case 0x3c: OP_ADJUST_REG(fn_rol, regs.a); //rol a
case 0x5c: OP_ADJUST_REG(fn_lsr, regs.a); //lsr a
case 0x7c: OP_ADJUST_REG(fn_ror, regs.a); //ror a
case 0x9c: OP_ADJUST_REG(fn_dec, regs.a); //dec a
case 0xbc: OP_ADJUST_REG(fn_inc, regs.a); //inc a
case 0x1d: OP_ADJUST_REG(fn_dec, regs.x); //dec x
case 0x3d: OP_ADJUST_REG(fn_inc, regs.x); //inc x
case 0xdc: OP_ADJUST_REG(fn_dec, regs.y); //dec y
case 0xfc: OP_ADJUST_REG(fn_inc, regs.y); //inc y
//
// bit manipulation
//
#define OP_OR1_BIT(op) \
dp = op_readpc() << 0; \
dp |= op_readpc() << 8; \
bit = dp >> 13; \
dp &= 0x1fff; \
rd = op_read(dp); \
op_io(); \
regs.p.c = regs.p.c = ((bool)(rd & (1 << bit)) ^ op); \
break;
case 0x0a: OP_OR1_BIT(0); //or1 c,$addr:bit
case 0x2a: OP_OR1_BIT(1); //or1 c,!$addr:bit
#define OP_AND1_BIT(op) \
dp = op_readpc() << 0; \
dp |= op_readpc() << 8; \
bit = dp >> 13; \
dp &= 0x1fff; \
rd = op_read(dp); \
regs.p.c = regs.p.c & ((bool)(rd & (1 << bit)) ^ op); \
break;
case 0x4a: OP_AND1_BIT(0); //and1 c,$addr:bit
case 0x6a: OP_AND1_BIT(1); //and1 c,!$addr:bit
case 0x8a: //eor1 c,$addr:bit
dp = op_readpc() << 0;
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_read(dp);
op_io();
regs.p.c = regs.p.c ^ (bool)(rd & (1 << bit));
break;
case 0xfc: //inc y
OP_ADJUST_REG(fn_inc, regs.y)
case 0xaa: //mov1 c,$addr:bit
sp = op_readpc() << 0;
sp |= op_readpc() << 8;
bit = sp >> 13;
sp &= 0x1fff;
rd = op_read(sp);
regs.p.c = (rd & (1 << bit));
break;
default:
static bool stop = false;
regs.pc--;
if(stop == false) {
stop = true;
print("Unknown opcode @ ", hex<4>(regs.pc), ":", hex<2>(opcode), "\n");
char buffer[4096];
disassemble_opcode(buffer, regs.pc);
print(buffer, "\n");
case 0xca: //mov1 $addr:bit,c
dp = op_readpc() << 0;
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_read(dp);
regs.p.c ? rd |= (1 << bit) : rd &=~ (1 << bit);
op_io();
op_write(dp, rd);
break;
case 0xea: //not1 c,$addr:bit
dp = op_readpc() << 0;
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_read(dp);
rd ^= 1 << bit;
op_write(dp, rd);
break;
//
// miscellaneous
//
#define OP_TADJUST_ADDR(mathop) \
dp = op_readpc() << 0; \
dp |= op_readpc() << 8; \
rd = op_read(dp); \
regs.p.n = ((regs.a - rd) & 0x80); \
regs.p.z = ((regs.a - rd) == 0); \
op_read(dp); \
op_write(dp, rd mathop regs.a); \
break;
case 0x0e: OP_TADJUST_ADDR(| ); //tset $addr,a
case 0x4e: OP_TADJUST_ADDR(&~); //tclr $addr,a
case 0x5a: //cmpw ya,$dp
dp = op_readpc();
rd = op_readdp(dp + 0) << 0;
rd |= op_readdp(dp + 1) << 8;
fn_cmpw(regs.ya, rd);
break;
case 0x9f: //xcn a
op_io(); op_io(); op_io(); op_io();
regs.a = (regs.a >> 4) | (regs.a << 4);
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
break;
case 0xbe: //das
op_io();
op_io();
if(!regs.p.c || (regs.a) > 0x99) {
regs.a -= 0x60;
regs.p.c = 0;
}
if(!regs.p.h || (regs.a & 15) > 0x09) {
regs.a -= 0x06;
}
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
break;
case 0xdf: //daa
op_io();
op_io();
if(regs.p.c || (regs.a) > 0x99) {
regs.a += 0x60;
regs.p.c = 1;
}
if(regs.p.h || (regs.a & 15) > 0x09) {
regs.a += 0x06;
}
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
break;
}
step(cycle_table[opcode]);
timer0.tick(cycle_count_table[opcode]);
timer1.tick(cycle_count_table[opcode]);
timer2.tick(cycle_count_table[opcode]);
clock += cycle_table_cpu[opcode];
dsp.clock -= cycle_table_dsp[opcode];
synchronize_dsp();
}
const unsigned SMP::cycle_table[256] = {
const unsigned SMP::cycle_count_table[256] = {
#define c 12
//0 1 2 3 4 5 6 7 8 9 A B C D E F
2,8,4,7, 3,4,3,6, 2,6,5,4, 5,4,6,8, //0

View File

@ -2,7 +2,9 @@ alwaysinline void op_io();
debugvirtual alwaysinline uint8 op_read(uint16 addr);
debugvirtual alwaysinline void op_write(uint16 addr, uint8 data);
debugvirtual alwaysinline void op_step();
static const unsigned cycle_table[256];
static const unsigned cycle_count_table[256];
uint64 cycle_table_cpu[256];
unsigned cycle_table_dsp[256];
uint8 fn_adc (uint8 x, uint8 y);
uint16 fn_addw(uint16 x, uint16 y);

View File

@ -19,4 +19,9 @@ public:
SMPDebugger();
~SMPDebugger();
//disassembler
void disassemble_opcode(char *output, uint16 addr);
inline uint8 disassemble_read(uint16 addr);
inline uint16 relb(int8 offset, int op_len);
};

View File

@ -5,6 +5,7 @@ namespace SNES {
#if defined(DEBUGGER)
#include "debugger/debugger.cpp"
#include "debugger/disassembler.cpp"
SMPDebugger smp;
#else
SMP smp;
@ -12,22 +13,15 @@ namespace SNES {
#include "algorithms.cpp"
#include "core.cpp"
#include "disassembler.cpp"
#include "iplrom.cpp"
#include "memory.cpp"
#include "timing.cpp"
void SMP::step(unsigned clocks) {
clock += clocks * (uint64)cpu.frequency;
dsp.clock -= clocks;
synchronize_dsp();
}
void SMP::synchronize_cpu() {
if(CPU::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
} else {
//while(clock >= 0) cpu.enter();
while(clock >= 0) cpu.enter();
}
}
@ -40,17 +34,22 @@ void SMP::synchronize_dsp() {
}
void SMP::enter() {
op_step();
while(clock < 0) op_step();
}
void SMP::power() {
Processor::frequency = system.apu_frequency() / 24;
Processor::frequency = system.apu_frequency();
Processor::clock = 0;
timer0.target = 0;
timer1.target = 0;
timer2.target = 0;
for(unsigned n = 0; n < 256; n++) {
cycle_table_dsp[n] = (cycle_count_table[n] * 24);
cycle_table_cpu[n] = (cycle_count_table[n] * 24) * cpu.frequency;
}
reset();
}
@ -58,7 +57,7 @@ void SMP::reset() {
for(unsigned n = 0x0000; n <= 0xffff; n++) apuram[n] = 0x00;
regs.pc = 0xffc0;
regs.sp = 0x00ef;
regs.sp = 0xef;
regs.a = 0x00;
regs.x = 0x00;
regs.y = 0x00;
@ -69,14 +68,6 @@ void SMP::reset() {
status.dsp_counter = 0;
status.timer_step = 3;
//$00f0
status.clock_speed = 0;
status.timer_speed = 0;
status.timers_enable = true;
status.ram_disable = false;
status.ram_writable = true;
status.timers_disable = false;
//$00f1
status.iplrom_enable = true;
@ -89,9 +80,40 @@ void SMP::reset() {
}
void SMP::serialize(serializer &s) {
Processor::serialize(s);
s.array(apuram, 64 * 1024);
s.integer(regs.pc);
s.integer(regs.sp);
s.integer(regs.a);
s.integer(regs.x);
s.integer(regs.y);
s.integer(regs.p.n);
s.integer(regs.p.v);
s.integer(regs.p.p);
s.integer(regs.p.b);
s.integer(regs.p.h);
s.integer(regs.p.i);
s.integer(regs.p.z);
s.integer(regs.p.c);
s.integer(status.clock_counter);
s.integer(status.dsp_counter);
s.integer(status.timer_step);
s.integer(status.iplrom_enable);
s.integer(status.dsp_addr);
s.integer(status.ram00f8);
s.integer(status.ram00f9);
}
SMP::SMP() {
apuram = new uint8[64 * 1024];
stackram = apuram + 0x0100;
}
SMP::~SMP() {

View File

@ -1,16 +1,19 @@
class SMP : public Processor {
public:
static const uint8 iplrom[64];
uint8 apuram[64 * 1024];
uint8 *apuram;
uint8 *stackram;
enum : bool { Threaded = false };
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_cpu();
alwaysinline void synchronize_dsp();
unsigned port_read(unsigned port);
void port_write(unsigned port, unsigned data);
unsigned mmio_read(unsigned addr);
void mmio_write(unsigned addr, unsigned data);
void enter();
void power();
void reset();
@ -41,7 +44,7 @@ public:
struct Regs {
uint16 pc;
uint16 sp;
uint8 sp;
union {
uint16 ya;
struct { uint8 order_lsb2(a, y); };
@ -56,14 +59,6 @@ public:
unsigned dsp_counter;
unsigned timer_step;
//$00f0
unsigned clock_speed;
unsigned timer_speed;
bool timers_enable;
bool ram_disable;
bool ram_writable;
bool timers_disable;
//$00f1
bool iplrom_enable;
@ -85,21 +80,13 @@ public:
bool enable;
unsigned target;
void tick();
void tick(unsigned clocks);
void synchronize();
};
Timer<192> timer0;
Timer<192> timer1;
Timer< 24> timer2;
unsigned mmio_read(unsigned addr);
void mmio_write(unsigned addr, unsigned data);
//disassembler
void disassemble_opcode(char *output, uint16 addr);
inline uint8 disassemble_read(uint16 addr);
inline uint16 relb(int8 offset, int op_len);
Timer<64> timer0;
Timer<64> timer1;
Timer< 8> timer2;
};
#if defined(DEBUGGER)

View File

@ -1,7 +1,7 @@
template<unsigned timer_frequency>
void SMP::Timer<timer_frequency>::tick() {
void SMP::Timer<timer_frequency>::tick(unsigned clocks) {
//stage 0 increment
stage0_ticks += smp.status.timer_step;
stage0_ticks += clocks;
if(stage0_ticks < timer_frequency) return;
stage0_ticks -= timer_frequency;

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "078.01";
static const char Version[] = "078.02";
static const unsigned SerializerVersion = 19;
}
}