frodo-wii/Src/CPU_emulcycle.h

1115 lines
19 KiB
C

/*
* CPU_emulcycle.h - SC 6510/6502 emulation core (body of
* EmulateCycle() function, the same for
* both 6510 and 6502)
*
* Frodo (C) 1994-1997,2002-2005 Christian Bauer
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Stack macros
*/
// Pop processor flags from the stack
#define pop_flags() \
read_to(sp | 0x100, data); \
n_flag = data; \
v_flag = data & 0x40; \
d_flag = data & 0x08; \
i_flag = data & 0x04; \
z_flag = !(data & 0x02); \
c_flag = data & 0x01;
// Push processor flags onto the stack
#define push_flags(b_flag) \
data = 0x20 | (n_flag & 0x80); \
if (v_flag) data |= 0x40; \
if (b_flag) data |= 0x10; \
if (d_flag) data |= 0x08; \
if (i_flag) data |= 0x04; \
if (!z_flag) data |= 0x02; \
if (c_flag) data |= 0x01; \
write_byte(sp-- | 0x100, data);
/*
* Other macros
*/
// Branch (cycle 1)
#define Branch(flag) \
read_to(pc++, data); \
if (flag) { \
ar = pc + (int8)data; \
if ((ar >> 8) != (pc >> 8)) { \
if (data & 0x80) \
state = O_BRANCH_BP; \
else \
state = O_BRANCH_FP; \
} else \
state = O_BRANCH_NP; \
} else \
state = 0; \
break;
// Set N and Z flags according to byte
#define set_nz(x) (z_flag = n_flag = (x))
// Address fetch of RMW instruction done, now read and write operand
#define DoRMW state = RMW_DO_IT; break;
// Operand fetch done, now execute opcode
#define Execute state = OpTab[op]; break;
// Last cycle of opcode
#define Last state = 0; break;
/*
* EmulCycle() function
*/
switch (state) {
// Opcode fetch (cycle 0)
case 0:
read_to(pc++, op);
state = ModeTab[op];
break;
// IRQ
case 0x0008:
read_idle(pc);
state = 0x0009;
break;
case 0x0009:
read_idle(pc);
state = 0x000a;
break;
case 0x000a:
write_byte(sp-- | 0x100, pc >> 8);
state = 0x000b;
break;
case 0x000b:
write_byte(sp-- | 0x100, pc);
state = 0x000c;
break;
case 0x000c:
push_flags(false);
i_flag = true;
state = 0x000d;
break;
case 0x000d:
read_to(0xfffe, pc);
state = 0x000e;
break;
case 0x000e:
read_to(0xffff, data);
pc |= data << 8;
Last;
// NMI
case 0x0010:
read_idle(pc);
state = 0x0011;
break;
case 0x0011:
read_idle(pc);
state = 0x0012;
break;
case 0x0012:
write_byte(sp-- | 0x100, pc >> 8);
state = 0x0013;
break;
case 0x0013:
write_byte(sp-- | 0x100, pc);
state = 0x0014;
break;
case 0x0014:
push_flags(false);
i_flag = true;
state = 0x0015;
break;
case 0x0015:
read_to(0xfffa, pc);
state = 0x0016;
break;
case 0x0016:
read_to(0xfffb, data);
pc |= data << 8;
Last;
// Addressing modes: Fetch effective address, no extra cycles (-> ar)
case A_ZERO:
read_to(pc++, ar);
Execute;
case A_ZEROX:
read_to(pc++, ar);
state = A_ZEROX1;
break;
case A_ZEROX1:
read_idle(ar);
ar = (ar + x) & 0xff;
Execute;
case A_ZEROY:
read_to(pc++, ar);
state = A_ZEROY1;
break;
case A_ZEROY1:
read_idle(ar);
ar = (ar + y) & 0xff;
Execute;
case A_ABS:
read_to(pc++, ar);
state = A_ABS1;
break;
case A_ABS1:
read_to(pc++, data);
ar = ar | (data << 8);
Execute;
case A_ABSX:
read_to(pc++, ar);
state = A_ABSX1;
break;
case A_ABSX1:
read_to(pc++, ar2); // Note: Some undocumented opcodes rely on the value of ar2
if (ar+x < 0x100)
state = A_ABSX2;
else
state = A_ABSX3;
ar = ((ar + x) & 0xff) | (ar2 << 8);
break;
case A_ABSX2: // No page crossed
read_idle(ar);
Execute;
case A_ABSX3: // Page crossed
read_idle(ar);
ar += 0x100;
Execute;
case A_ABSY:
read_to(pc++, ar);
state = A_ABSY1;
break;
case A_ABSY1:
read_to(pc++, ar2); // Note: Some undocumented opcodes rely on the value of ar2
if (ar+y < 0x100)
state = A_ABSY2;
else
state = A_ABSY3;
ar = ((ar + y) & 0xff) | (ar2 << 8);
break;
case A_ABSY2: // No page crossed
read_idle(ar);
Execute;
case A_ABSY3: // Page crossed
read_idle(ar);
ar += 0x100;
Execute;
case A_INDX:
read_to(pc++, ar2);
state = A_INDX1;
break;
case A_INDX1:
read_idle(ar2);
ar2 = (ar2 + x) & 0xff;
state = A_INDX2;
break;
case A_INDX2:
read_to(ar2, ar);
state = A_INDX3;
break;
case A_INDX3:
read_to((ar2 + 1) & 0xff, data);
ar = ar | (data << 8);
Execute;
case A_INDY:
read_to(pc++, ar2);
state = A_INDY1;
break;
case A_INDY1:
read_to(ar2, ar);
state = A_INDY2;
break;
case A_INDY2:
read_to((ar2 + 1) & 0xff, ar2); // Note: Some undocumented opcodes rely on the value of ar2
if (ar+y < 0x100)
state = A_INDY3;
else
state = A_INDY4;
ar = ((ar + y) & 0xff) | (ar2 << 8);
break;
case A_INDY3: // No page crossed
read_idle(ar);
Execute;
case A_INDY4: // Page crossed
read_idle(ar);
ar += 0x100;
Execute;
// Addressing modes: Fetch effective address, extra cycle on page crossing (-> ar)
case AE_ABSX:
read_to(pc++, ar);
state = AE_ABSX1;
break;
case AE_ABSX1:
read_to(pc++, data);
if (ar+x < 0x100) {
ar = ((ar + x) & 0xff) | (data << 8);
Execute;
} else {
ar = ((ar + x) & 0xff) | (data << 8);
state = AE_ABSX2;
}
break;
case AE_ABSX2: // Page crossed
read_idle(ar);
ar += 0x100;
Execute;
case AE_ABSY:
read_to(pc++, ar);
state = AE_ABSY1;
break;
case AE_ABSY1:
read_to(pc++, data);
if (ar+y < 0x100) {
ar = ((ar + y) & 0xff) | (data << 8);
Execute;
} else {
ar = ((ar + y) & 0xff) | (data << 8);
state = AE_ABSY2;
}
break;
case AE_ABSY2: // Page crossed
read_idle(ar);
ar += 0x100;
Execute;
case AE_INDY:
read_to(pc++, ar2);
state = AE_INDY1;
break;
case AE_INDY1:
read_to(ar2, ar);
state = AE_INDY2;
break;
case AE_INDY2:
read_to((ar2 + 1) & 0xff, data);
if (ar+y < 0x100) {
ar = ((ar + y) & 0xff) | (data << 8);
Execute;
} else {
ar = ((ar + y) & 0xff) | (data << 8);
state = AE_INDY3;
}
break;
case AE_INDY3: // Page crossed
read_idle(ar);
ar += 0x100;
Execute;
// Addressing modes: Read operand, write it back, no extra cycles (-> ar, rdbuf)
case M_ZERO:
read_to(pc++, ar);
DoRMW;
case M_ZEROX:
read_to(pc++, ar);
state = M_ZEROX1;
break;
case M_ZEROX1:
read_idle(ar);
ar = (ar + x) & 0xff;
DoRMW;
case M_ZEROY:
read_to(pc++, ar);
state = M_ZEROY1;
break;
case M_ZEROY1:
read_idle(ar);
ar = (ar + y) & 0xff;
DoRMW;
case M_ABS:
read_to(pc++, ar);
state = M_ABS1;
break;
case M_ABS1:
read_to(pc++, data);
ar = ar | (data << 8);
DoRMW;
case M_ABSX:
read_to(pc++, ar);
state = M_ABSX1;
break;
case M_ABSX1:
read_to(pc++, data);
if (ar+x < 0x100)
state = M_ABSX2;
else
state = M_ABSX3;
ar = ((ar + x) & 0xff) | (data << 8);
break;
case M_ABSX2: // No page crossed
read_idle(ar);
DoRMW;
case M_ABSX3: // Page crossed
read_idle(ar);
ar += 0x100;
DoRMW;
case M_ABSY:
read_to(pc++, ar);
state = M_ABSY1;
break;
case M_ABSY1:
read_to(pc++, data);
if (ar+y < 0x100)
state = M_ABSY2;
else
state = M_ABSY3;
ar = ((ar + y) & 0xff) | (data << 8);
break;
case M_ABSY2: // No page crossed
read_idle(ar);
DoRMW;
case M_ABSY3: // Page crossed
read_idle(ar);
ar += 0x100;
DoRMW;
case M_INDX:
read_to(pc++, ar2);
state = M_INDX1;
break;
case M_INDX1:
read_idle(ar2);
ar2 = (ar2 + x) & 0xff;
state = M_INDX2;
break;
case M_INDX2:
read_to(ar2, ar);
state = M_INDX3;
break;
case M_INDX3:
read_to((ar2 + 1) & 0xff, data);
ar = ar | (data << 8);
DoRMW;
case M_INDY:
read_to(pc++, ar2);
state = M_INDY1;
break;
case M_INDY1:
read_to(ar2, ar);
state = M_INDY2;
break;
case M_INDY2:
read_to((ar2 + 1) & 0xff, data);
if (ar+y < 0x100)
state = M_INDY3;
else
state = M_INDY4;
ar = ((ar + y) & 0xff) | (data << 8);
break;
case M_INDY3: // No page crossed
read_idle(ar);
DoRMW;
case M_INDY4: // Page crossed
read_idle(ar);
ar += 0x100;
DoRMW;
case RMW_DO_IT:
read_to(ar, rdbuf);
state = RMW_DO_IT1;
break;
case RMW_DO_IT1:
write_byte(ar, rdbuf);
Execute;
// Load group
case O_LDA:
read_to(ar, data);
set_nz(a = data);
Last;
case O_LDA_I:
read_to(pc++, data);
set_nz(a = data);
Last;
case O_LDX:
read_to(ar, data);
set_nz(x = data);
Last;
case O_LDX_I:
read_to(pc++, data);
set_nz(x = data);
Last;
case O_LDY:
read_to(ar, data);
set_nz(y = data);
Last;
case O_LDY_I:
read_to(pc++, data);
set_nz(y = data);
Last;
// Store group
case O_STA:
write_byte(ar, a);
Last;
case O_STX:
write_byte(ar, x);
Last;
case O_STY:
write_byte(ar, y);
Last;
// Transfer group
case O_TAX:
read_idle(pc);
set_nz(x = a);
Last;
case O_TXA:
read_idle(pc);
set_nz(a = x);
Last;
case O_TAY:
read_idle(pc);
set_nz(y = a);
Last;
case O_TYA:
read_idle(pc);
set_nz(a = y);
Last;
case O_TSX:
read_idle(pc);
set_nz(x = sp);
Last;
case O_TXS:
read_idle(pc);
sp = x;
Last;
// Arithmetic group
case O_ADC:
read_to(ar, data);
do_adc(data);
Last;
case O_ADC_I:
read_to(pc++, data);
do_adc(data);
Last;
case O_SBC:
read_to(ar, data);
do_sbc(data);
Last;
case O_SBC_I:
read_to(pc++, data);
do_sbc(data);
Last;
// Increment/decrement group
case O_INX:
read_idle(pc);
set_nz(++x);
Last;
case O_DEX:
read_idle(pc);
set_nz(--x);
Last;
case O_INY:
read_idle(pc);
set_nz(++y);
Last;
case O_DEY:
read_idle(pc);
set_nz(--y);
Last;
case O_INC:
write_byte(ar, set_nz(rdbuf + 1));
Last;
case O_DEC:
write_byte(ar, set_nz(rdbuf - 1));
Last;
// Logic group
case O_AND:
read_to(ar, data);
set_nz(a &= data);
Last;
case O_AND_I:
read_to(pc++, data);
set_nz(a &= data);
Last;
case O_ORA:
read_to(ar, data);
set_nz(a |= data);
Last;
case O_ORA_I:
read_to(pc++, data);
set_nz(a |= data);
Last;
case O_EOR:
read_to(ar, data);
set_nz(a ^= data);
Last;
case O_EOR_I:
read_to(pc++, data);
set_nz(a ^= data);
Last;
// Compare group
case O_CMP:
read_to(ar, data);
set_nz(ar = a - data);
c_flag = ar < 0x100;
Last;
case O_CMP_I:
read_to(pc++, data);
set_nz(ar = a - data);
c_flag = ar < 0x100;
Last;
case O_CPX:
read_to(ar, data);
set_nz(ar = x - data);
c_flag = ar < 0x100;
Last;
case O_CPX_I:
read_to(pc++, data);
set_nz(ar = x - data);
c_flag = ar < 0x100;
Last;
case O_CPY:
read_to(ar, data);
set_nz(ar = y - data);
c_flag = ar < 0x100;
Last;
case O_CPY_I:
read_to(pc++, data);
set_nz(ar = y - data);
c_flag = ar < 0x100;
Last;
// Bit-test group
case O_BIT:
read_to(ar, data);
z_flag = a & data;
n_flag = data;
v_flag = data & 0x40;
Last;
// Shift/rotate group
case O_ASL:
c_flag = rdbuf & 0x80;
write_byte(ar, set_nz(rdbuf << 1));
Last;
case O_ASL_A:
read_idle(pc);
c_flag = a & 0x80;
set_nz(a <<= 1);
Last;
case O_LSR:
c_flag = rdbuf & 0x01;
write_byte(ar, set_nz(rdbuf >> 1));
Last;
case O_LSR_A:
read_idle(pc);
c_flag = a & 0x01;
set_nz(a >>= 1);
Last;
case O_ROL:
write_byte(ar, set_nz(c_flag ? (rdbuf << 1) | 0x01 : rdbuf << 1));
c_flag = rdbuf & 0x80;
Last;
case O_ROL_A:
read_idle(pc);
data = a & 0x80;
set_nz(a = c_flag ? (a << 1) | 0x01 : a << 1);
c_flag = data;
Last;
case O_ROR:
write_byte(ar, set_nz(c_flag ? (rdbuf >> 1) | 0x80 : rdbuf >> 1));
c_flag = rdbuf & 0x01;
Last;
case O_ROR_A:
read_idle(pc);
data = a & 0x01;
set_nz(a = (c_flag ? (a >> 1) | 0x80 : a >> 1));
c_flag = data;
Last;
// Stack group
case O_PHA:
read_idle(pc);
state = O_PHA1;
break;
case O_PHA1:
write_byte(sp-- | 0x100, a);
Last;
case O_PLA:
read_idle(pc);
state = O_PLA1;
break;
case O_PLA1:
read_idle(sp++ | 0x100);
state = O_PLA2;
break;
case O_PLA2:
read_to(sp | 0x100, data);
set_nz(a = data);
Last;
case O_PHP:
read_idle(pc);
state = O_PHP1;
break;
case O_PHP1:
push_flags(true);
Last;
case O_PLP:
read_idle(pc);
state = O_PLP1;
break;
case O_PLP1:
read_idle(sp++ | 0x100);
state = O_PLP2;
break;
case O_PLP2:
pop_flags();
Last;
// Jump/branch group
case O_JMP:
read_to(pc++, ar);
state = O_JMP1;
break;
case O_JMP1:
read_to(pc, data);
pc = (data << 8) | ar;
Last;
case O_JMP_I:
read_to(ar, pc);
state = O_JMP_I1;
break;
case O_JMP_I1:
read_to(((ar + 1) & 0xff) | (ar & 0xff00), data);
pc |= data << 8;
Last;
case O_JSR:
read_to(pc++, ar);
state = O_JSR1;
break;
case O_JSR1:
read_idle(sp | 0x100);
state = O_JSR2;
break;
case O_JSR2:
write_byte(sp-- | 0x100, pc >> 8);
state = O_JSR3;
break;
case O_JSR3:
write_byte(sp-- | 0x100, pc);
state = O_JSR4;
break;
case O_JSR4:
read_to(pc++, data);
pc = ar | (data << 8);
Last;
case O_RTS:
read_idle(pc);
state = O_RTS1;
break;
case O_RTS1:
read_idle(sp++ | 0x100);
state = O_RTS2;
break;
case O_RTS2:
read_to(sp++ | 0x100, pc);
state = O_RTS3;
break;
case O_RTS3:
read_to(sp | 0x100, data);
pc |= data << 8;
state = O_RTS4;
break;
case O_RTS4:
read_idle(pc++);
Last;
case O_RTI:
read_idle(pc);
state = O_RTI1;
break;
case O_RTI1:
read_idle(sp++ | 0x100);
state = O_RTI2;
break;
case O_RTI2:
pop_flags();
sp++;
state = O_RTI3;
break;
case O_RTI3:
read_to(sp++ | 0x100, pc);
state = O_RTI4;
break;
case O_RTI4:
read_to(sp | 0x100, data);
pc |= data << 8;
Last;
case O_BRK:
read_idle(pc++);
state = O_BRK1;
break;
case O_BRK1:
write_byte(sp-- | 0x100, pc >> 8);
state = O_BRK2;
break;
case O_BRK2:
write_byte(sp-- | 0x100, pc);
state = O_BRK3;
break;
case O_BRK3:
push_flags(true);
i_flag = true;
#ifndef IS_CPU_1541
if (interrupt.intr[INT_NMI]) { // BRK interrupted by NMI?
interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input
state = 0x0015; // Jump to NMI sequence
break;
}
#endif
state = O_BRK4;
break;
case O_BRK4:
#ifndef IS_CPU_1541
first_nmi_cycle++; // Delay NMI
#endif
read_to(0xfffe, pc);
state = O_BRK5;
break;
case O_BRK5:
read_to(0xffff, data);
pc |= data << 8;
Last;
case O_BCS:
Branch(c_flag);
case O_BCC:
Branch(!c_flag);
case O_BEQ:
Branch(!z_flag);
case O_BNE:
Branch(z_flag);
case O_BVS:
#ifndef IS_CPU_1541
Branch(v_flag);
#else
Branch((via2_pcr & 0x0e) == 0x0e ? 1 : v_flag); // GCR byte ready flag
#endif
case O_BVC:
#ifndef IS_CPU_1541
Branch(!v_flag);
#else
Branch(!((via2_pcr & 0x0e) == 0x0e) ? 0 : v_flag); // GCR byte ready flag
#endif
case O_BMI:
Branch(n_flag & 0x80);
case O_BPL:
Branch(!(n_flag & 0x80));
case O_BRANCH_NP: // No page crossed
first_irq_cycle++; // Delay IRQ
#ifndef IS_CPU_1541
first_nmi_cycle++; // Delay NMI
#endif
read_idle(pc);
pc = ar;
Last;
case O_BRANCH_BP: // Page crossed, branch backwards
read_idle(pc);
pc = ar;
state = O_BRANCH_BP1;
break;
case O_BRANCH_BP1:
read_idle(pc + 0x100);
Last;
case O_BRANCH_FP: // Page crossed, branch forwards
read_idle(pc);
pc = ar;
state = O_BRANCH_FP1;
break;
case O_BRANCH_FP1:
read_idle(pc - 0x100);
Last;
// Flag group
case O_SEC:
read_idle(pc);
c_flag = true;
Last;
case O_CLC:
read_idle(pc);
c_flag = false;
Last;
case O_SED:
read_idle(pc);
d_flag = true;
Last;
case O_CLD:
read_idle(pc);
d_flag = false;
Last;
case O_SEI:
read_idle(pc);
i_flag = true;
Last;
case O_CLI:
read_idle(pc);
i_flag = false;
Last;
case O_CLV:
read_idle(pc);
v_flag = false;
Last;
// NOP group
case O_NOP:
read_idle(pc);
Last;
/*
* Undocumented opcodes start here
*/
// NOP group
case O_NOP_I:
read_idle(pc++);
Last;
case O_NOP_A:
read_idle(ar);
Last;
// Load A/X group
case O_LAX:
read_to(ar, data);
set_nz(a = x = data);
Last;
// Store A/X group
case O_SAX:
write_byte(ar, a & x);
Last;
// ASL/ORA group
case O_SLO:
c_flag = rdbuf & 0x80;
rdbuf <<= 1;
write_byte(ar, rdbuf);
set_nz(a |= rdbuf);
Last;
// ROL/AND group
case O_RLA:
tmp = rdbuf & 0x80;
rdbuf = c_flag ? (rdbuf << 1) | 0x01 : rdbuf << 1;
c_flag = tmp;
write_byte(ar, rdbuf);
set_nz(a &= rdbuf);
Last;
// LSR/EOR group
case O_SRE:
c_flag = rdbuf & 0x01;
rdbuf >>= 1;
write_byte(ar, rdbuf);
set_nz(a ^= rdbuf);
Last;
// ROR/ADC group
case O_RRA:
tmp = rdbuf & 0x01;
rdbuf = c_flag ? (rdbuf >> 1) | 0x80 : rdbuf >> 1;
c_flag = tmp;
write_byte(ar, rdbuf);
do_adc(rdbuf);
Last;
// DEC/CMP group
case O_DCP:
write_byte(ar, --rdbuf);
set_nz(ar = a - rdbuf);
c_flag = ar < 0x100;
Last;
// INC/SBC group
case O_ISB:
write_byte(ar, ++rdbuf);
do_sbc(rdbuf);
Last;
// Complex functions
case O_ANC_I:
read_to(pc++, data);
set_nz(a &= data);
c_flag = n_flag & 0x80;
Last;
case O_ASR_I:
read_to(pc++, data);
a &= data;
c_flag = a & 0x01;
set_nz(a >>= 1);
Last;
case O_ARR_I:
read_to(pc++, data);
data &= a;
a = (c_flag ? (data >> 1) | 0x80 : data >> 1);
if (!d_flag) {
set_nz(a);
c_flag = a & 0x40;
v_flag = (a & 0x40) ^ ((a & 0x20) << 1);
} else {
n_flag = c_flag ? 0x80 : 0;
z_flag = a;
v_flag = (data ^ a) & 0x40;
if ((data & 0x0f) + (data & 0x01) > 5)
a = (a & 0xf0) | ((a + 6) & 0x0f);
if ((c_flag = ((data + (data & 0x10)) & 0x1f0) > 0x50) != 0)
a += 0x60;
}
Last;
case O_ANE_I:
read_to(pc++, data);
set_nz(a = (a | 0xee) & x & data);
Last;
case O_LXA_I:
read_to(pc++, data);
set_nz(a = x = (a | 0xee) & data);
Last;
case O_SBX_I:
read_to(pc++, data);
set_nz(x = ar = (x & a) - data);
c_flag = ar < 0x100;
Last;
case O_LAS:
read_to(ar, data);
set_nz(a = x = sp = data & sp);
Last;
case O_SHS: // ar2 contains the high byte of the operand address
write_byte(ar, (ar2+1) & (sp = a & x));
Last;
case O_SHY: // ar2 contains the high byte of the operand address
write_byte(ar, y & (ar2+1));
Last;
case O_SHX: // ar2 contains the high byte of the operand address
write_byte(ar, x & (ar2+1));
Last;
case O_SHA: // ar2 contains the high byte of the operand address
write_byte(ar, a & x & (ar2+1));
Last;