📄 65816emu.cpp
字号:
/* 65816EMU.CPP - Emulates one mode of the 65816. There are four different
versions depending on the mode of the processor.
*/
#undef XYTYPE
#undef ATYPE
#undef SIGNED_XYTYPE
#undef SIGNED_ATYPE
#undef XY_IS16BIT
#undef A_IS16BIT
#undef INDEX_X
#undef INDEX_Y
#undef ACCUM
#undef NV_SHIFT
#undef SIGN_BIT
#undef SIGN_BIT_XY
#undef SIGN_BIT_NUMBER
#undef BCDADDADJUST
#undef BCDSUBADJUST
#ifdef XY_16BIT
#define XYTYPE unsigned short
#define SIGNED_XYTYPE signed short
#define XY_IS16BIT 1
#define INDEX_X reg.X
#define INDEX_Y reg.Y
#else
#define XYTYPE unsigned char
#define SIGNED_XYTYPE signed char
#define XY_IS16BIT 0
#define INDEX_X reg.X_lo
#define INDEX_Y reg.Y_lo
#endif
#ifdef A_16BIT
#define ATYPE unsigned short
#define SIGNED_ATYPE signed short
#define A_IS16BIT 1
#define NV_SHIFT 8
#define BCDADDADJUST bcdaddadjust_word
#define BCDSUBADJUST bcdsubadjust_word
#define ACCUM reg.A
#else
#define ATYPE unsigned char
#define SIGNED_ATYPE signed char
#define A_IS16BIT 0
#define NV_SHIFT 0
#define BCDADDADJUST bcdaddadjust_byte
#define BCDSUBADJUST bcdsubadjust_byte
#define ACCUM reg.A_lo
#endif
#ifndef ADDRESSING_MODES
#define ADDRESSING_MODES
#define OPBYTE ((byte) opdata)
#define OPWORD ((word) opdata)
#define OPLONG opdata
// Using these returns a pointer to the appropriate memory address.
// The PC relative modes aren't listed since what is contained at the new location
// is irrelevant to the current instruction.
#define IMM_BYTE OPBYTE
// OPC %#b
#define IMM_ACCUM ((ATYPE) opdata)
// OPA %#
#define IMM_INDEX ((XYTYPE) opdata)
// OPX %#i
#define ABS (SNESMEM ((DBR << 16) + OPWORD))
// OPC %w
#define ABS_LONG (SNESMEM (OPLONG))
// OPC $%L
#define DIR (SNESMEM ((D + OPBYTE) & 0xFFFF))
// OPC <%b
#define DIR_INDIR_INDEX_Y ( _tmp = *(word*)SNESMEM(D + OPBYTE) + (DBR << 16) + INDEX_Y, SNESMEM(_tmp) )
// OPC (<%b),Y
#define DIR_INDIR_LONG ( _tmp = (*(dword*)SNESMEM( (D + OPBYTE)&0xFFFF )) & 0xFFFFFF, SNESMEM (_tmp) )
// OPC [<%b]
#define DIR_INDIR_LONG_INDEX_Y ( _tmp = ((*(dword*)SNESMEM( (D+OPBYTE)&0xFFFF )) & 0xFFFFFF) + INDEX_Y, SNESMEM (_tmp) )
// OPC [<%b],Y
#define DIR_INDEX_INDIR_X ( _tmp = *(word *)SNESMEM( (((D+OPBYTE)&0xFFFF) + INDEX_X) & 0xFFFF ) + (DBR << 16), SNESMEM (_tmp) )
// OPC (<%b,X)
#define DIR_INDEX_X ( SNESMEM(D + OPBYTE + INDEX_X) )
// OPC <%b,X
#define DIR_INDEX_Y ( SNESMEM(D + OPBYTE + INDEX_Y) )
// OPC <%b,Y
#define ABS_INDEX_X ( _tmp = (DBR << 16) + OPWORD + INDEX_X, SNESMEM(_tmp) )
// OPC %w,X
#define ABS_INDEX_Y ( _tmp = (DBR << 16) + OPWORD + INDEX_Y, SNESMEM(_tmp) )
// OPC %w, Y
#define ABS_LONG_INDEX_X ( SNESMEM(OPLONG + INDEX_X) )
// OPC %L,X
#define ABS_INDIR ( /*_tmp = *(word*)SNESMEM(OPWORD),*/ SNESMEM (OPWORD) )
// JML (%w) or JMP (%w)
#define DIR_INDIR ( _tmp = *(word*)SNESMEM((D + OPBYTE) & 0xFFFF) + (DBR << 16), SNESMEM (_tmp) )
// OPC (<%b)
#define ABS_INDEX_INDIR_X ( _tmp = OPWORD + INDEX_X + (DBR << 16), SNESMEM(_tmp) )
// OPC (%w,X)
#define ABS_INDEX_INDIR_X_JMP ( _tmp = OPWORD + INDEX_X + (PC & 0xFF0000), *(word*)SNESMEM(_tmp) | (PC & 0xFF0000) )
// JMP (%w,X) - returns the new value for the PC. As evidenced by Tetris Attack, the PBR (not DBR)
// should be used for both bank values.
#define S_REL (SNESMEM (S + OPBYTE))
// OPC <%b,S
#define S_REL_INDIR_INDEX_Y ( _tmp = *(word*)SNESMEM (S + OPBYTE) + (DBR << 16) + INDEX_Y, SNESMEM (_tmp) )
// OPC (<%b,S),Y
#define PUSHBYTE(b) ( *(byte*)SNESMEM(S) = (byte)(b), (*(word*)&S)-- )
#define PUSHWORD(w) ( *(word*)SNESMEM(S-1) = (word)(w), (*(word*)&S) -= 2 )
#define PULLBYTE(db) ( (*(word*)&S)++, (db) = *(byte*)SNESMEM(S) )
#define PULLWORD(dw) ( (*(word*)&S) += 2, (dw) = *(word*)SNESMEM(S-1) )
#endif
#ifdef A_16BIT
#define SIGN_BIT 0x8000
#define SIGN_BIT_NUMBER 15
#else
#define SIGN_BIT 0x80
#define SIGN_BIT_NUMBER 7
#endif
#ifdef XY_16BIT
#define SIGN_BIT_XY 0x8000
#else
#define SIGN_BIT_XY 0x80
#endif
#define SAVE(A) (oldA = (A))
#define ADCSETCARRY(_tmpc) ((_tmpc) & (SIGN_BIT << 1) ? P |= CARRY : P &= ~CARRY)
#define SBCSETCARRY(_tmpc) ((_tmpc) & (SIGN_BIT << 1) ? P &= ~CARRY : P |= CARRY)
#define SETZERO(A) ((A) == 0 ? P |= ZERO : P &= ~ZERO)
#define SETNEGATIVE(A) ((A) & SIGN_BIT ? P |= NEGATIVE : P &= ~NEGATIVE)
#define SETNEGATIVEXY(XY) (((XY) & SIGN_BIT_XY) ? P |= NEGATIVE : P &= ~NEGATIVE)
#define SETOVERFLOW(A,oldA) ( ((A ^ oldA) & SIGN_BIT) && ((A>oldA?A-oldA:oldA-A) <= SIGN_BIT) ? P |= OVERFLOW : P &= ~OVERFLOW)
//#define SETOVERFLOW(A,oldA) ( (((SIGNED_ATYPE)A>(SIGNED_ATYPE)oldA?(int)((SIGNED_ATYPE)A)-(int)((SIGNED_ATYPE)oldA):(int)((SIGNED_ATYPE)oldA)-(int)((SIGNED_ATYPE)A))\
> SIGN_BIT) ? P |= OVERFLOW : P &= ~OVERFLOW)
// For an overflow condition:
// - The signs have to change, and
// - The value of (greater - smaller) is <= 32768
#define TRAPREAD(ptr) (ptr >= registers && ptr <= (registers+0x4000) ? trapregread(ptr,ISWORDOP) : ptr)
#define TRAPWRITE(ptr) if (ptr >= registers && ptr <= (registers+0x4000)) trapregwrite(ptr,ISWORDOP)
// TRAPWRITE should be called after the write and before any flags get set based on the new value.
#define ISWORDOP A_IS16BIT
#define P reg.P
#define S reg.S
#define D reg.D
#define E reg.E
#define DBR reg.DBR
#define PC reg.PC
#define ACCUM_16BIT reg.A
#define INDEX_X_16BIT reg.X
#define INDEX_Y_16BIT reg.Y
#define cycles scan_cycles
#define DOBREAK break
void EMUROUTINE ()
{
//register dword PC;
//static dword S;
//static XYTYPE XYTYPE_X;
//static XYTYPE XYTYPE_Y;
//static byte DBR;
//static word D;
//static ATYPE ATYPE_A;
//static byte P;
//static boolean E; // Emulation mode flag (mostly ignored)
static byte opcode;
static dword opdata;
dword _tmp, _tmp2, _tmpc;
ATYPE oldA; // used by macros
XYTYPE oldXY;
ATYPE *amem;
byte *mem;
char unasm [20], lastop;
(void)unasm;
//PC = reg.PC;
//S = reg.S;
//XYTYPE_X = (XYTYPE) reg.X;
//XYTYPE_Y = (XYTYPE) reg.Y;
//DBR = reg.DBR;
//D = reg.D;
//ATYPE_A = (ATYPE) reg.A;
//P = reg.P;
//E = reg.E;
//#define MAKE2(y) #y
//#define MAKESTR(x) MAKE2(x)
//if (debug_slice < 0) debug0 ("Entered: %s PC=%X P=%X S=%X", MAKESTR(EMUROUTINE), PC, P, S);
assert ((S & 0xFFFF) == S);
assert ((PC & 0xFFFFFF) == PC);
while ((cycles & 0x80000000) == 0) {
//if (!(PC & 0x8000)) {
//addmessage ("warning! PC set to 0x%X", PC);
//returntogui = true;
//frame_cycles -= 100000;
//cycles -= 200;
//return;
//}
debug_instr++;
opdata = *(dword*) SNESMEM(PC);
//if (debug_instr >= 20250 && debug_instr < 20270) {
// unassemble (opdata,unasm,P);
// debug0 ("<-instr PC$%X (cycles=%d) asm:%s",PC,cycles,unasm);
//}
// debug thing
/* debug_thing -= 2;
*(word*)(expram + (debug_thing*6)+2) = (word)PC;
*(word*)(expram + (debug_thing*6)+4) = (word)opdata;
*(word*)(expram + (debug_thing*6)+6) = (word)S;
*(word*)(expram + (debug_thing*6)+8) = (word)0;
*(word*)(expram + (debug_thing*6)+10)= (word)E;
*(word*)(expram + (debug_thing*6)+12)= (word)P;
*(word*)expram = (0x6002 + debug_thing*6);
*/
opcode = (byte)opdata;
opdata >>= 8;
// Another debug thing
//if (opcode == 0x50 || opcode == 0x70)
instr_tally[lastop][(P >> 3) & 1]++;
lastop = opcode;
switch (opcode) {
case 0x00: // "BRK", 2, 8, Has a signature word: %w two bytes ignored
PC += 2; // Add 2 because RTI adds one more
//PUSHBYTE (PC >> 16);
//PUSHWORD ((word)PC);
//PUSHBYTE (P);
//PC = *(word *)SNESMEM (E? 0x00FFFE : 0x00FFE6);
addmessage ("Warning: BRK encountered at $%LX", PC-2);
cycles -= 200; frame_cycles -= 100000; returntogui = true;
cycles -= 8;
return;
//DOBREAK;
case 0x01: // "ORA (<%b,X)", 2, 6,
mem = DIR_INDEX_INDIR_X;
ACCUM |= *(ATYPE*)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 6+A_IS16BIT; DOBREAK;
case 0x02: // "CSP", 2, 8, Has one-byte signature %b
PC += 2; // Add one because RTI adds one more -- wait, add 2??
PUSHBYTE (PC >> 16);
PUSHWORD ((word)PC);
PUSHBYTE (P);
PC = *(word *)SNESMEM (E? 0x00FFF4 : 0x00FFE4);
cycles -= 8; DOBREAK;
case 0x03: // "ORA <%b,S", 2, 4,
mem = S_REL;
ACCUM |= *(ATYPE*)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 4+A_IS16BIT; DOBREAK;
case 0x04: // "TSB <%b", 2, 5,
amem = (ATYPE*)DIR;
SETZERO (*amem & ACCUM);
*amem |= ACCUM;
TRAPWRITE((byte*)amem);
PC += 2; cycles -= 5+A_IS16BIT; DOBREAK;
case 0x05: // "ORA <%b", 2, 3,
mem = DIR;
ACCUM |= *(ATYPE*)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 3+A_IS16BIT; DOBREAK;
case 0x06: // "SHL <%b", 2, 5,
if (*(amem = (ATYPE*)DIR) & SIGN_BIT) P |= CARRY;
else P &= ~CARRY;
*amem <<= 1;
TRAPWRITE ((byte*)amem);
SETZERO (*amem);
SETNEGATIVE (*amem);
PC += 2; cycles -= 5+A_IS16BIT; DOBREAK;
case 0x07: // "ORA [<%b]", 2, 6,
mem = DIR_INDIR_LONG;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 6+A_IS16BIT; DOBREAK;
case 0x08: // "PSH P", 1, 3, // PHP
PUSHBYTE (P);
PC += 1; cycles -= 3; DOBREAK;
case 0x09: // "ORA %#", 2, 2,
ACCUM |= IMM_ACCUM;
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2+A_IS16BIT; cycles -= 2+A_IS16BIT; DOBREAK;
case 0x0A: // "SHL A", 1, 2,
if (ACCUM & SIGN_BIT) P |= CARRY;
else P &= ~CARRY;
ACCUM <<= 1;
SETZERO (ACCUM);
SETNEGATIVE (ACCUM);
PC += 1; cycles -= 2; DOBREAK;
case 0x0B: // "PSH D", 1, 4, // PHD Push Direct
PUSHWORD (D);
PC += 1; cycles -= 4; DOBREAK;
case 0x0C: // "TSB %w", 3, 6,
amem = (ATYPE*)ABS;
SETZERO (*amem & ACCUM);
*amem |= ACCUM;
TRAPWRITE ((byte*)amem);
PC += 3; cycles -= 6+A_IS16BIT; DOBREAK;
case 0x0D: // "ORA %w", 3, 4,
mem = ABS;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 3; cycles -= 4+A_IS16BIT; DOBREAK;
case 0x0E: // "SHL %w", 3, 6,
if (*(amem = (ATYPE*)ABS) & SIGN_BIT) P |= CARRY;
else P &= ~CARRY;
*amem <<= 1;
TRAPWRITE ((byte*)amem);
SETZERO (*amem);
SETNEGATIVE (*amem);
PC += 3; cycles -= 6+A_IS16BIT; DOBREAK;
case 0x0F: // "ORA %L", 4, 5,
mem = ABS_LONG;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 4; cycles -= 5+A_IS16BIT; DOBREAK;
case 0x10: // "BPL @%b", 2, 2,
if (P & NEGATIVE) { // False
PC += 2; cycles -= 2; DOBREAK;
}
PC += 2 + ((signed char) opdata);
cycles -= 3; DOBREAK;
case 0x11: // "ORA (<%b),Y", 2, 5,
mem = DIR_INDIR_INDEX_Y;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 5+A_IS16BIT; DOBREAK;
case 0x12: // "ORA (<%b)", 2, 5,
mem = DIR_INDIR;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 5+A_IS16BIT; DOBREAK;
case 0x13: // "ORA (<%b,S),Y", 2, 7,
mem = S_REL_INDIR_INDEX_Y;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 7+A_IS16BIT; DOBREAK;
case 0x14: // "TRB <%b", 2, 5,
amem = (ATYPE*)DIR;
SETZERO (*amem & ACCUM);
*amem &= ~ACCUM;
TRAPWRITE ((byte*)amem);
PC += 2; cycles -= 5+A_IS16BIT; DOBREAK;
case 0x15: // "ORA <%b,X", 2, 4,
mem = DIR_INDEX_X;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 4+A_IS16BIT; DOBREAK;
case 0x16: // "SHL <%b,X",2, 6,
if (*(amem = (ATYPE*)DIR_INDEX_X) & SIGN_BIT) P |= CARRY;
else P &= ~CARRY;
*amem <<= 1;
TRAPWRITE ((byte*)amem);
SETZERO (*amem);
SETNEGATIVE (*amem);
PC += 2; cycles -= 6+A_IS16BIT; DOBREAK;
case 0x17: // "ORA [<%b],Y", 2, 6,
mem = DIR_INDIR_LONG_INDEX_Y;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 2; cycles -= 6+A_IS16BIT; DOBREAK;
case 0x18: // "CLC", 1, 2,
P &= ~CARRY;
PC += 1; cycles -= 2; DOBREAK;
case 0x19: // "ORA %w,Y", 3, 4,
mem = ABS_INDEX_Y;
ACCUM |= *(ATYPE *)TRAPREAD(mem);
SETZERO (ACCUM); SETNEGATIVE (ACCUM);
PC += 3; cycles -= 4+A_IS16BIT; DOBREAK;
case 0x1A: // "INC A", 1, 2,
ACCUM++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -