AppleWin/source/CPU/cpu_instructions.inl

666 lines
20 KiB
C++

/*
AppleWin : An Apple //e emulator for Windows
Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006-2010, Tom Charlesworth, Michael Pohoreski
AppleWin 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.
AppleWin 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 AppleWin; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Description: 6502/65C02 emulation
*
* Author: Various
*/
// TO DO:
// . All these CPP macros need to be converted to inline funcs
/****************************************************************************
*
* INSTRUCTION MACROS
*
***/
#undef ADC_NMOS
#undef ADC_CMOS
#undef ALR
#undef AND
#undef ANC
#undef ARR
#undef ASL_NMOS
#undef ASL_CMOS
#undef ASLA
#undef ASO
#undef AXA
#undef AXS
#undef BCC
#undef BCS
#undef BEQ
#undef BIT
#undef BITI
#undef BMI
#undef BNE
#undef BPL
#undef BRA
#undef BRK_NMOS
#undef BRK_CMOS
#undef BVC
#undef BVS
#undef CLC
#undef CLD
#undef CLI
#undef CLV
#undef CMP
#undef CPX
#undef CPY
#undef DCM
#undef DEA
#undef DEC
#undef DEX
#undef DEY
#undef EOR
#undef HLT
#undef INA
#undef INC
#undef INS
#undef INX
#undef INY
#undef JMP
#undef JSR
#undef LAS
#undef LAX
#undef LDA
#undef LDX
#undef LDY
#undef LSE
#undef LSR_NMOS
#undef LSR_CMOS
#undef LSRA
#undef NOP
#undef OAL
#undef ORA
#undef PHA
#undef PHP
#undef PHX
#undef PHY
#undef PLA
#undef PLP
#undef PLX
#undef PLY
#undef RLA
#undef ROL_NMOS
#undef ROL_CMOS
#undef ROLA
#undef ROR_NMOS
#undef ROR_CMOS
#undef RORA
#undef RRA
#undef RTI
#undef RTS
#undef SAX
#undef SAY
#undef SBC_NMOS
#undef SBC_CMOS
#undef SEC
#undef SED
#undef SEI
#undef STA
#undef STX
#undef STY
#undef STZ
#undef TAS
#undef TAX
#undef TAY
#undef TRB
#undef TSB
#undef TSX
#undef TXA
#undef TXS
#undef TYA
#undef XAA
#undef XAS
// ==========
#undef ADCn
#undef ASLn
#undef BRKn
#undef LSRn
#undef ROLn
#undef RORn
#undef SBCn
#define ADCn ADC_NMOS
#define ASLn ASL_NMOS
#define BRKn BRK_NMOS
#define LSRn LSR_NMOS
#define ROLn ROL_NMOS
#define RORn ROR_NMOS
#define SBCn SBC_NMOS
// ==========
#undef ADCc
#undef ASLc
#undef BRKC
#undef LSRc
#undef ROLc
#undef RORc
#undef SBCc
#define ADCc ADC_CMOS
#define ASLc ASL_CMOS
#define BRKc BRK_CMOS
#define LSRc LSR_CMOS
#define ROLc ROL_CMOS
#define RORc ROR_CMOS
#define SBCc SBC_CMOS
// ==========
#define ADC_NMOS /*bSlowerOnPagecross = 1;*/ \
temp = READ; \
if (regs.ps & AF_DECIMAL) { \
val = (regs.a & 0x0F) + (temp & 0x0F) + flagc; \
if (val > 0x09) \
val += 0x06; \
if (val <= 0x0F) \
val = (val & 0x0F) + (regs.a & 0xF0) + (temp & 0xF0); \
else \
val = (val & 0x0F) + (regs.a & 0xF0) + (temp & 0xF0) + 0x10;\
flagz = !((regs.a + temp + flagc) & 0xFF); \
flagn = (val & 0x80); \
flagv = ((regs.a ^ val) & 0x80) && !((regs.a ^ temp) & 0x80);\
if ((val & 0x1F0) > 0x90) \
val += 0x60; \
flagc = ((val & 0xFF0) > 0xF0); \
regs.a = val & 0xFF; \
} \
else { \
val = regs.a + temp + flagc; \
flagc = (val > 0xFF); \
flagv = (((regs.a & 0x80) == (temp & 0x80)) && \
((regs.a & 0x80) != (val & 0x80))); \
regs.a = val & 0xFF; \
SETNZ(regs.a); \
}
#define ADC_CMOS /*bSlowerOnPagecross = 1*/; \
temp = READ; \
flagv = !((regs.a ^ temp) & 0x80); \
if (regs.ps & AF_DECIMAL) { \
uExtraCycles++; \
val = (regs.a & 0x0f) + (temp & 0x0f) + flagc; \
if (val >= 0x0A) \
val = 0x10 | ((val + 6) & 0x0f); \
val += (regs.a & 0xf0) + (temp & 0xf0); \
if (val >= 0xA0) { \
flagc = 1; \
if (val >= 0x180) \
flagv = 0; \
val += 0x60; \
} \
else { \
flagc = 0; \
if (val < 0x80) \
flagv = 0; \
} \
} \
else { \
val = regs.a + temp + flagc; \
if (val >= 0x100) { \
flagc = 1; \
if (val >= 0x180) flagv = 0; \
} \
else { \
flagc = 0; \
if (val < 0x80) flagv = 0; \
} \
} \
regs.a = val & 0xFF; \
SETNZ(regs.a)
#define ALR regs.a &= READ; \
flagc = (regs.a & 1); \
flagn = 0; \
regs.a >>= 1; \
SETZ(regs.a)
#define AND /*bSlowerOnPagecross = 1;*/ \
regs.a &= READ; \
SETNZ(regs.a)
#define ANC regs.a &= READ; \
SETNZ(regs.a) \
flagc = !!flagn;
#define ARR temp = regs.a & READ; /* Yes, this is sick */ \
if (regs.ps & AF_DECIMAL) { \
val = temp; \
val |= (flagc ? 0x100 : 0); \
val >>= 1; \
flagn = (flagc ? 0x80 : 0); \
SETZ(val) \
flagv = ((val ^ temp) & 0x40); \
if (((val & 0x0F) + (val & 0x01)) > 0x05) \
val = (val & 0xF0) | ((val + 0x06) & 0x0F); \
if (((val & 0xF0) + (val & 0x10)) > 0x50) { \
val = (val & 0x0F) | ((val + 0x60) & 0xF0); \
flagc = 1; \
} \
else \
flagc = 0; \
regs.a = (val & 0xFF); \
} \
else { \
val = temp | (flagc ? 0x100 : 0); \
val >>= 1; \
SETNZ(val) \
flagc = !!(val & 0x40); \
flagv = ((val & 0x40) ^ ((val & 0x20) << 1)); \
regs.a = (val & 0xFF); \
}
#define ASL_NMOS /*bSlowerOnPagecross = 0;*/ \
val = READ << 1; \
flagc = (val > 0xFF); \
SETNZ(val) \
WRITE(val)
#define ASL_CMOS /*bSlowerOnPagecross = 1*/; \
val = READ << 1; \
flagc = (val > 0xFF); \
SETNZ(val) \
WRITE(val)
#define ASLA val = regs.a << 1; \
flagc = (val > 0xFF); \
SETNZ(val) \
regs.a = (BYTE)val;
#define ASO /*bSlowerOnPagecross = 0;*/ \
val = READ << 1; \
flagc = (val > 0xFF); \
WRITE(val) \
regs.a |= val; \
SETNZ(regs.a)
#define AXA /*bSlowerOnPagecross = 0;*/ \
val = regs.a & regs.x & (((base >> 8) + 1) & 0xFF); \
ON_PAGECROSS_REPLACE_HI_ADDR \
WRITE(val)
#define AXS /*bSlowerOnPagecross = 0;*/ \
WRITE(regs.a & regs.x)
#define BCC if (!flagc) BRANCH_TAKEN;
#define BCS if ( flagc) BRANCH_TAKEN;
#define BEQ if ( flagz) BRANCH_TAKEN;
#define BIT /*bSlowerOnPagecross = 1;*/ \
val = READ; \
flagz = !(regs.a & val); \
flagn = val & 0x80; \
flagv = val & 0x40;
#define BITI flagz = !(regs.a & READ);
#define BMI if ( flagn) BRANCH_TAKEN;
#define BNE if (!flagz) BRANCH_TAKEN;
#define BPL if (!flagn) BRANCH_TAKEN;
#define BRA BRANCH_TAKEN;
#define BRK_NMOS regs.pc++; \
PUSH(regs.pc >> 8) \
PUSH(regs.pc & 0xFF) \
EF_TO_AF \
PUSH(regs.ps); \
regs.ps |= AF_INTERRUPT; \
regs.pc = *(LPWORD)(mem+0xFFFE);
#define BRK_CMOS regs.pc++; \
PUSH(regs.pc >> 8) \
PUSH(regs.pc & 0xFF) \
EF_TO_AF \
PUSH(regs.ps); \
regs.ps |= AF_INTERRUPT; \
regs.ps &= ~AF_DECIMAL; /*CMOS clears D flag*/ \
regs.pc = *(LPWORD)(mem+0xFFFE);
#define BVC if (!flagv) BRANCH_TAKEN;
#define BVS if ( flagv) BRANCH_TAKEN;
#define CLC flagc = 0;
#define CLD regs.ps &= ~AF_DECIMAL;
#define CLI regs.ps &= ~AF_INTERRUPT;
#define CLV flagv = 0;
#define CMP /*bSlowerOnPagecross = 1;*/ \
val = READ; \
flagc = (regs.a >= val); \
val = regs.a-val; \
SETNZ(val)
#define CPX val = READ; \
flagc = (regs.x >= val); \
val = regs.x-val; \
SETNZ(val)
#define CPY val = READ; \
flagc = (regs.y >= val); \
val = regs.y-val; \
SETNZ(val)
#define DCM /*bSlowerOnPagecross = 0;*/ \
val = READ-1; \
WRITE(val) \
flagc = (regs.a >= val); \
val = regs.a-val; \
SETNZ(val)
#define DEA --regs.a; \
SETNZ(regs.a)
#define DEC /*bSlowerOnPagecross = 0;*/ \
val = READ-1; \
SETNZ(val) \
WRITE(val)
#define DEX --regs.x; \
SETNZ(regs.x)
#define DEY --regs.y; \
SETNZ(regs.y)
#define EOR /*bSlowerOnPagecross = 1;*/ \
regs.a ^= READ; \
SETNZ(regs.a)
#define HLT regs.bJammed = 1; \
--regs.pc;
#define INA ++regs.a; \
SETNZ(regs.a)
#define INC /*bSlowerOnPagecross = 0;*/ \
val = READ+1; \
SETNZ(val) \
WRITE(val)
#define INS /*bSlowerOnPagecross = 0;*/ \
val = READ+1; \
WRITE(val) \
temp = val; \
temp2 = regs.a - temp - !flagc; \
if (regs.ps & AF_DECIMAL) { \
val = (regs.a & 0x0F) - (temp & 0x0F) - !flagc; \
if (val & 0x10) \
val = ((val - 0x06) & 0x0F) | ((regs.a & 0xF0) - (temp & 0xF0) - 0x10);\
else \
val = (val & 0x0F) | ((regs.a & 0xF0) - (temp & 0xF0));\
if (val & 0x100) \
val -= 0x60; \
flagc = (temp2 < 0x100); \
SETNZ(temp2 & 0xFF); \
flagv = ((regs.a ^ temp2) & 0x80) && ((regs.a ^ temp) & 0x80);\
regs.a = val & 0xFF; \
} \
else { \
val = temp2; \
flagc = (val < 0x100); \
flagv = (((regs.a & 0x80) != (temp & 0x80)) && \
((regs.a & 0x80) != (val & 0x80))); \
regs.a = val & 0xFF; \
SETNZ(regs.a); \
}
#define INX ++regs.x; \
SETNZ(regs.x)
#define INY ++regs.y; \
SETNZ(regs.y)
#define JMP regs.pc = addr;
#define JSR addr = *(LPBYTE)(mem+regs.pc); regs.pc++; \
PUSH(regs.pc >> 8) \
PUSH(regs.pc & 0xFF) \
regs.pc = addr | (*(LPBYTE)(mem+regs.pc)) << 8; /* GH#1257 */
#define LAS /*bSlowerOnPagecross = 1*/; \
val = (BYTE)(READ & regs.sp); \
regs.a = regs.x = (BYTE) val; \
regs.sp = val | 0x100; \
SETNZ(val)
#define LAX /*bSlowerOnPagecross = 1;*/ \
regs.a = regs.x = READ; \
SETNZ(regs.a)
#define LDA /*bSlowerOnPagecross = 1;*/ \
regs.a = READ; \
SETNZ(regs.a)
#define LDD /*Undocumented 65C02: LoaD and Discard*/ \
READ;
#define LDX /*bSlowerOnPagecross = 1;*/ \
regs.x = READ; \
SETNZ(regs.x)
#define LDY /*bSlowerOnPagecross = 1;*/ \
regs.y = READ; \
SETNZ(regs.y)
#define LSE /*bSlowerOnPagecross = 0;*/ \
val = READ; \
flagc = (val & 1); \
val >>= 1; \
WRITE(val) \
regs.a ^= val; \
SETNZ(regs.a)
#define LSR_NMOS /*bSlowerOnPagecross = 0;*/ \
val = READ; \
flagc = (val & 1); \
flagn = 0; \
val >>= 1; \
SETZ(val) \
WRITE(val)
#define LSR_CMOS /*bSlowerOnPagecross = 1;*/ \
val = READ; \
flagc = (val & 1); \
flagn = 0; \
val >>= 1; \
SETZ(val) \
WRITE(val)
#define LSRA flagc = (regs.a & 1); \
flagn = 0; \
regs.a >>= 1; \
SETZ(regs.a)
#define NOP /*bSlowerOnPagecross = 1;*/
#define OAL regs.a |= 0xEE; \
regs.a &= READ; \
regs.x = regs.a; \
SETNZ(regs.a)
#define ORA /*bSlowerOnPagecross = 1;*/ \
regs.a |= READ; \
SETNZ(regs.a)
#define PHA PUSH(regs.a)
#define PHP EF_TO_AF \
PUSH(regs.ps)
#define PHX PUSH(regs.x)
#define PHY PUSH(regs.y)
#define PLA regs.a = POP; \
SETNZ(regs.a)
#define PLP regs.ps = POP | AF_RESERVED | AF_BREAK; \
AF_TO_EF
#define PLX regs.x = POP; \
SETNZ(regs.x)
#define PLY regs.y = POP; \
SETNZ(regs.y)
#define RLA /*bSlowerOnPagecross = 0;*/ \
val = (READ << 1) | flagc; \
flagc = (val > 0xFF); \
WRITE(val) \
regs.a &= val; \
SETNZ(regs.a)
#define ROL_NMOS /*bSlowerOnPagecross = 0;*/ \
val = (READ << 1) | flagc; \
flagc = (val > 0xFF); \
SETNZ(val) \
WRITE(val)
#define ROL_CMOS /*bSlowerOnPagecross = 1;*/ \
val = (READ << 1) | flagc; \
flagc = (val > 0xFF); \
SETNZ(val) \
WRITE(val)
#define ROLA val = (((WORD)regs.a) << 1) | flagc; \
flagc = (val > 0xFF); \
regs.a = val & 0xFF; \
SETNZ(regs.a);
#define ROR_NMOS /*bSlowerOnPagecross = 0;*/ \
temp = READ; \
val = (temp >> 1) | (flagc ? 0x80 : 0); \
flagc = (temp & 1); \
SETNZ(val) \
WRITE(val)
#define ROR_CMOS /*bSlowerOnPagecross = 1;*/ \
temp = READ; \
val = (temp >> 1) | (flagc ? 0x80 : 0); \
flagc = (temp & 1); \
SETNZ(val) \
WRITE(val)
#define RORA val = (((WORD)regs.a) >> 1) | (flagc ? 0x80 : 0); \
flagc = (regs.a & 1); \
regs.a = val & 0xFF; \
SETNZ(regs.a)
#define RRA /*bSlowerOnPagecross = 0;*/ \
temp = READ; \
val = (temp >> 1) | (flagc ? 0x80 : 0); \
flagc = (temp & 1); \
WRITE(val) \
temp = val; \
if (regs.ps & AF_DECIMAL) { \
val = (regs.a & 0x0F) + (temp & 0x0F) + flagc; \
if (val > 0x09) \
val += 0x06; \
if (val <= 0x0F) \
val = (val & 0x0F) + (regs.a & 0xF0) + (temp & 0xF0); \
else \
val = (val & 0x0F) + (regs.a & 0xF0) + (temp & 0xF0) + 0x10;\
flagz = !((regs.a + temp + flagc) & 0xFF); \
flagn = (val & 0x80); \
flagv = ((regs.a ^ val) & 0x80) && !((regs.a ^ temp) & 0x80);\
if ((val & 0x1F0) > 0x90) \
val += 0x60; \
flagc = ((val & 0xFF0) > 0xF0); \
regs.a = val & 0xFF; \
} \
else { \
val = regs.a + temp + flagc; \
flagc = (val > 0xFF); \
flagv = (((regs.a & 0x80) == (temp & 0x80)) && \
((regs.a & 0x80) != (val & 0x80))); \
regs.a = val & 0xFF; \
SETNZ(regs.a); \
}
#define RTI regs.ps = POP | AF_RESERVED | AF_BREAK; \
AF_TO_EF \
regs.pc = POP; \
regs.pc |= (((WORD)POP) << 8);
#define RTS regs.pc = POP; \
regs.pc |= (((WORD)POP) << 8); \
++regs.pc;
#define SAX temp = regs.a & regs.x; \
val = READ; \
flagc = (temp >= val); \
regs.x = temp-val; \
SETNZ(regs.x)
#define SAY /*bSlowerOnPagecross = 0;*/ \
val = regs.y & (((base >> 8) + 1) & 0xFF); \
ON_PAGECROSS_REPLACE_HI_ADDR \
WRITE(val)
#define SBC_NMOS /*bSlowerOnPagecross = 1;*/ \
temp = READ; \
temp2 = regs.a - temp - !flagc; \
if (regs.ps & AF_DECIMAL) { \
val = (regs.a & 0x0F) - (temp & 0x0F) - !flagc; \
if (val & 0x10) \
val = ((val - 0x06) & 0x0F) | ((regs.a & 0xF0) - (temp & 0xF0) - 0x10);\
else \
val = (val & 0x0F) | ((regs.a & 0xF0) - (temp & 0xF0));\
if (val & 0x100) \
val -= 0x60; \
flagc = (temp2 < 0x100); \
SETNZ(temp2 & 0xFF); \
flagv = ((regs.a ^ temp2) & 0x80) && ((regs.a ^ temp) & 0x80);\
regs.a = val & 0xFF; \
} \
else { \
val = temp2; \
flagc = (val < 0x100); \
flagv = (((regs.a & 0x80) != (temp & 0x80)) && \
((regs.a & 0x80) != (val & 0x80))); \
regs.a = val & 0xFF; \
SETNZ(regs.a); \
}
#define SBC_CMOS /*bSlowerOnPagecross = 1;*/ \
temp = READ; \
flagv = ((regs.a ^ temp) & 0x80); \
if (regs.ps & AF_DECIMAL) { \
uExtraCycles++; \
temp2 = 0x0F + (regs.a & 0x0F) - (temp & 0x0F) + flagc; \
if (temp2 < 0x10) { \
val = 0; \
temp2 -= 0x06; \
} \
else { \
val = 0x10; \
temp2 -= 0x10; \
} \
val += 0xF0 + (regs.a & 0xF0) - (temp & 0xF0); \
if (val < 0x100) { \
flagc = 0; \
if (val < 0x80) \
flagv = 0; \
val -= 0x60; \
} \
else { \
flagc = 1; \
if (val >= 0x180) \
flagv = 0; \
} \
val += temp2; \
} \
else { \
val = 0xff + regs.a - temp + flagc; \
if (val < 0x100) { \
flagc = 0; \
if (val < 0x80) \
flagv = 0; \
} \
else { \
flagc = 1; \
if (val >= 0x180) \
flagv = 0; \
} \
} \
regs.a = val & 0xFF; \
SETNZ(regs.a)
#define SEC flagc = 1;
#define SED regs.ps |= AF_DECIMAL;
#define SEI regs.ps |= AF_INTERRUPT;
#define STA /*bSlowerOnPagecross = 0;*/ \
WRITE(regs.a)
#define STX /*bSlowerOnPagecross = 0;*/ \
WRITE(regs.x)
#define STY /*bSlowerOnPagecross = 0;*/ \
WRITE(regs.y)
#define STZ /*bSlowerOnPagecross = 0;*/ \
WRITE(0)
#define TAS /*bSlowerOnPagecross = 0;*/ \
val = regs.a & regs.x; \
regs.sp = 0x100 | val; \
val &= (((base >> 8) + 1) & 0xFF); \
ON_PAGECROSS_REPLACE_HI_ADDR \
WRITE(val)
#define TAX regs.x = regs.a; \
SETNZ(regs.x)
#define TAY regs.y = regs.a; \
SETNZ(regs.y)
#define TRB /*bSlowerOnPagecross = 0;*/ \
val = READ; \
flagz = !(regs.a & val); \
val &= ~regs.a; \
WRITE(val)
#define TSB /*bSlowerOnPagecross = 0;*/ \
val = READ; \
flagz = !(regs.a & val); \
val |= regs.a; \
WRITE(val)
#define TSX regs.x = regs.sp & 0xFF; \
SETNZ(regs.x)
#define TXA regs.a = regs.x; \
SETNZ(regs.a)
#define TXS regs.sp = 0x100 | regs.x;
#define TYA regs.a = regs.y; \
SETNZ(regs.a)
#define XAA regs.a = regs.x; \
regs.a &= READ; \
SETNZ(regs.a)
#define XAS /*bSlowerOnPagecross = 0;*/ \
val = regs.x & (((base >> 8) + 1) & 0xFF); \
ON_PAGECROSS_REPLACE_HI_ADDR \
WRITE(val)