mirror of
https://github.com/sicklittlemonkey/AppleIIGo.git
synced 2024-12-28 00:29:37 +00:00
1618 lines
34 KiB
Java
1618 lines
34 KiB
Java
|
|
/**
|
|
* AppleIIGo
|
|
* Apple II Emulator for J2SE
|
|
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
|
* Released under the GPL
|
|
* Adapted from code by Doug Kwan
|
|
* Adapted from code by Randy Frank randy@tessa.iaf.uiowa.edu
|
|
* Adapted from code (C) 1989 Ben Koning [556498717 408/738-1763 ben@apple.com]
|
|
*/
|
|
|
|
public class Em6502 {
|
|
/**
|
|
* Base memory (including zero page and stack)
|
|
*/
|
|
public byte[] mem = null;
|
|
|
|
/**
|
|
* Generic memory read & write (0x0000-0xffff)
|
|
*/
|
|
protected int memoryRead(int addr) {
|
|
return (mem[addr] & 0xff);
|
|
}
|
|
protected void memoryWrite(int addr, int value) {
|
|
mem[addr] = (byte) value;
|
|
}
|
|
|
|
/*
|
|
* Zero page read & write
|
|
*/
|
|
private final int zeroPageRead(int addr) {
|
|
return (mem[addr] & 0xff);
|
|
}
|
|
private final void zeroPageWrite(int addr, int value) {
|
|
mem[addr] = (byte) value;
|
|
}
|
|
|
|
/**
|
|
* Userspace interrupts
|
|
*/
|
|
public final void assertReset() {
|
|
exceptionRegister |= SIG_6502_RESET;
|
|
}
|
|
public final void assertNMI() {
|
|
exceptionRegister |= SIG_6502_NMI;
|
|
}
|
|
public final void assertIRQ() {
|
|
exceptionRegister |= SIG_6502_IRQ;
|
|
}
|
|
|
|
/**
|
|
* Userspace interrupt handlers
|
|
*/
|
|
protected void onReset() {
|
|
};
|
|
|
|
protected void onNMI() {
|
|
};
|
|
|
|
protected void onIRQ() {
|
|
};
|
|
|
|
/**
|
|
* CPU Registers
|
|
*/
|
|
public int A, X, Y, P, S, PC;
|
|
|
|
/**
|
|
* CPU Clock
|
|
*/
|
|
protected int clock;
|
|
|
|
/**
|
|
* CPU Flags
|
|
*/
|
|
public static final int FLAG_C = (1 << 0);
|
|
public static final int FLAG_Z = (1 << 1);
|
|
public static final int FLAG_I = (1 << 2);
|
|
public static final int FLAG_D = (1 << 3);
|
|
public static final int FLAG_B = (1 << 4);
|
|
public static final int FLAG_V = (1 << 6);
|
|
public static final int FLAG_N = (1 << 7);
|
|
/*
|
|
* owing to a bug in 6502, the bit 5 must be always 1;
|
|
* otherwise, programs like DOS 3.3 will break down
|
|
* see instructions in $9FF4-$9FF5 of DOS 3.3
|
|
*/
|
|
|
|
/**
|
|
* CPU Signals
|
|
*/
|
|
private int exceptionRegister = 0;
|
|
|
|
public static final int SIG_6502_RESET = (1 << 0);
|
|
public static final int SIG_6502_NMI = (1 << 1);
|
|
public static final int SIG_6502_IRQ = (1 << 2);
|
|
|
|
/**
|
|
* CPU IRQ State
|
|
*/
|
|
private int pendingIRQ;
|
|
|
|
/**
|
|
* Emulator registers
|
|
*/
|
|
private int easp1, easp2;
|
|
private int operandAddress;
|
|
private int opcode;
|
|
private int operand;
|
|
private int result;
|
|
private int NZFlags;
|
|
|
|
/**
|
|
* ALU look up tables
|
|
*/
|
|
private int BCDTableAdd[]; // addition correction
|
|
private int BCDTableSub[]; // subtraction correction
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public void Em6502() {
|
|
// Init BCD tables
|
|
BCDTableAdd = new int[512];
|
|
BCDTableSub = new int[512];
|
|
|
|
for (int i = 0; i < 512; i++) {
|
|
BCDTableAdd[i] = ((i & 0x0f) <= 0x09) ? i : (i + 0x06);
|
|
BCDTableAdd[i] += ((BCDTableAdd[i] & 0xf0) <= 0x90) ? 0 : 0x60;
|
|
if (BCDTableAdd[i] > 0x1ff)
|
|
BCDTableAdd[i] -= 0x100;
|
|
|
|
BCDTableSub[i] = ((i & 0x0f) <= 0x09) ? i : (i - 0x06);
|
|
BCDTableSub[i] -= ((BCDTableSub[i] & 0xf0) <= 0x90) ? 0 : 0x60;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Stack macros
|
|
*/
|
|
private final int pop() {
|
|
S++;
|
|
S &= 0xff;
|
|
return (mem[S | 0x100] & 0xff);
|
|
}
|
|
private final void push(int value) {
|
|
mem[S | 0x100] = (byte) value;
|
|
S--;
|
|
S &= 0xff;
|
|
}
|
|
|
|
/*
|
|
* Macros for P flags
|
|
*/
|
|
private final void setN(boolean b) {if (b) P |= FLAG_N; else P &= ~FLAG_N;}
|
|
private final void setV(boolean b) {if (b) P |= FLAG_V; else P &= ~FLAG_V;}
|
|
private final void setB(boolean b) {if (b) P |= FLAG_B; else P &= ~FLAG_B;}
|
|
private final void setD(boolean b) {if (b) P |= FLAG_D; else P &= ~FLAG_D;}
|
|
private final void setI(boolean b) {if (b) P |= FLAG_I; else P &= ~FLAG_I;}
|
|
private final void setZ(boolean b) {if (b) P |= FLAG_Z; else P &= ~FLAG_Z;}
|
|
private final void setC(boolean b) {if (b) P |= FLAG_C; else P &= ~FLAG_C;}
|
|
private final boolean getN() {return ((P & FLAG_N) != 0);}
|
|
private final boolean getV() {return ((P & FLAG_V) != 0);}
|
|
private final boolean getB() {return ((P & FLAG_B) != 0);}
|
|
private final boolean getD() {return ((P & FLAG_D) != 0);}
|
|
private final boolean getI() {return ((P & FLAG_I) != 0);}
|
|
private final boolean getZ() {return ((P & FLAG_Z) != 0);}
|
|
private final boolean getC() {return ((P & FLAG_C) != 0);}
|
|
|
|
/**
|
|
* Fast condition codes. Instead of using bits to encode condition codes,
|
|
* recent ALU results are cached to that the condition codes can be
|
|
* handled more easily by the emulator's native hardware.
|
|
*/
|
|
private final boolean getFN() {return ((NZFlags & 0x280) != 0);}
|
|
private final boolean getFNotN() {return ((NZFlags & 0x280) == 0);}
|
|
private final boolean getFZ() {return ((NZFlags & 0xff) == 0);}
|
|
private final boolean getFNotZ() {return ((NZFlags & 0xff) != 0);}
|
|
private final void setFNZ(boolean n, boolean z) {NZFlags = ((n) ? 0x200 : 0x00) | ((z) ? 0x00 : 0x01);}
|
|
private final boolean getFC() {return (result >> 8) != 0;}
|
|
private final boolean getFNotC() {return (result >> 8) == 0;}
|
|
private final int getFC_() {return result >> 8;}
|
|
private final void setFC(boolean c) {result = (c ? 0x100 : 0x00);}
|
|
|
|
/*
|
|
* Macro for page crossing cycle regulation
|
|
*/
|
|
private final void checkCrossPage(int addr, int offset) {
|
|
if ((((addr + offset) ^ addr) & 0xff00) != 0) clock++;
|
|
}
|
|
|
|
/*
|
|
* Macros for effective address calculation
|
|
* (Macros whose names end with NC do not check for page crossing)
|
|
*/
|
|
private final int eaimm() {
|
|
easp1 = memoryRead(PC); PC++; return easp1;
|
|
}
|
|
private final int eazp() {
|
|
easp1 = memoryRead(PC); PC++; return easp1;
|
|
}
|
|
private final int eazpx() {
|
|
easp1 = (memoryRead(PC) + X) & 0xff; PC++; return easp1;
|
|
}
|
|
private final int eazpy() {
|
|
easp1 = (memoryRead(PC) + Y) & 0xff; PC++; return easp1;
|
|
}
|
|
private final int eaabs() {
|
|
easp1 = memoryRead(PC); PC++;
|
|
easp1 += (memoryRead(PC) << 8); PC++;
|
|
return easp1;
|
|
}
|
|
private final int earel() {
|
|
// easp1 = memoryRead(PC); PC++;
|
|
// return ((easp1 & 0x80) != 0) ? easp1 - 256 : easp1;
|
|
easp1 = (byte) memoryRead(PC); PC++; return easp1;
|
|
}
|
|
private final int eaabsx() {
|
|
// No cross page check...
|
|
// easp1 = eaabs();
|
|
// checkCrossPage(easp1, X);
|
|
// return easp1 + X;
|
|
return eaabs() + X;
|
|
}
|
|
private final int eaabsxNC() {
|
|
return eaabs() + X;
|
|
}
|
|
private final int eaabsy() {
|
|
// No cross page check...
|
|
// easp1 = eaabs();
|
|
// checkCrossPage(easp1, Y);
|
|
// return easp1 + Y;
|
|
return eaabs() + Y;
|
|
}
|
|
private final int eaabsyNC() {
|
|
return eaabs() + Y;
|
|
}
|
|
|
|
/*
|
|
* Indirect addressing
|
|
*/
|
|
private final int eaabsind() {
|
|
easp1 = eaabs();
|
|
easp2 = memoryRead(easp1);
|
|
return easp2 + (memoryRead(easp1 + 1) << 8);
|
|
}
|
|
private final int eazpxind() {
|
|
easp1 = eazpx();
|
|
easp2 = zeroPageRead(easp1);
|
|
return easp2 + (zeroPageRead((easp1 + 1) & 0xff) << 8);
|
|
}
|
|
private final int eazpindy() {
|
|
easp1 = eaimm();
|
|
easp2 = zeroPageRead(easp1);
|
|
// No cross page check...
|
|
// easp2 += (zeroPageRead((easp1 + 1) & 0xff) << 8);
|
|
// checkCrossPage(easp2,Y);
|
|
// return easp2 + Y;
|
|
return easp2 + (zeroPageRead((easp1 + 1) & 0xff) << 8) + Y;
|
|
}
|
|
private final int eazpindyNC() {
|
|
easp1 = eaimm();
|
|
easp2 = zeroPageRead(easp1);
|
|
return easp2 + (zeroPageRead((easp1 + 1) & 0xff) << 8) + Y;
|
|
}
|
|
|
|
/*
|
|
* New 65C02 addressing mode
|
|
*/
|
|
private final int eazpind() {
|
|
easp1 = eazp();
|
|
easp2 = zeroPageRead(easp1);
|
|
return easp2 + (zeroPageRead((easp1 + 1) & 0xff) << 8);
|
|
}
|
|
private final int eaabsxind() {
|
|
easp1 = eaabs();
|
|
easp2 = memoryRead(easp1);
|
|
return easp2 + (memoryRead(easp1 + 1) << 8) + X;
|
|
}
|
|
|
|
/*
|
|
* Misc. macros
|
|
*/
|
|
private final void adcBCDAdjust() {
|
|
if (getD()) result = BCDTableAdd[result];
|
|
}
|
|
private final void sbcBCDAdjust() {
|
|
if (getD()) result = BCDTableSub[result];
|
|
}
|
|
private final void branch(int operand) {
|
|
// No cross page check...
|
|
// checkCrossPage(PC, operand);
|
|
PC += operand;
|
|
clock++;
|
|
}
|
|
|
|
/** This executes a single instruction. */
|
|
private final void executeInstruction() {
|
|
opcode = memoryRead(PC);
|
|
PC++;
|
|
|
|
switch(opcode) {
|
|
case 0x69: // ADC #imm
|
|
operand = eaimm();
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x6D: // ADC abs
|
|
operand = memoryRead(eaabs());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x65: // ADC zp
|
|
operand = zeroPageRead(eazp());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x61: // ADC (zp,X)
|
|
operand = memoryRead(eazpxind());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x71: // ADC (zp),Y
|
|
operandAddress = eazpindy();
|
|
operand = memoryRead(operandAddress);
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x75: // ADC zp,X
|
|
operandAddress = eazpx();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x7D: // ADC abs,X
|
|
operand = memoryRead(eaabsx());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x79: // ADC abs,Y
|
|
operand = memoryRead(eaabsy());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x29: // AND #imm
|
|
A &= eaimm();
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x2D: // AND abs
|
|
A &= memoryRead(eaabs());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x25: // AND zp
|
|
A &= zeroPageRead(eazp());
|
|
NZFlags = A;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x21: // AND (zp,X)
|
|
A &= memoryRead(eazpxind());
|
|
NZFlags = A;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x31: // AND (zp),Y
|
|
A &= memoryRead(eazpindy());
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x35: // AND zp,X
|
|
A &= zeroPageRead(eazpx());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x3D: // AND abs,X
|
|
A &= memoryRead(eaabsx());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x39: // AND abs,Y
|
|
A &= memoryRead(eaabsy());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x0E: // ASL abs
|
|
operandAddress = eaabs();
|
|
operand = memoryRead(operandAddress);
|
|
result = operand << 1;
|
|
NZFlags = result;
|
|
memoryWrite(operandAddress, result);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x06: // ASL zp
|
|
operandAddress = eazp();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = operand << 1;
|
|
NZFlags = result;
|
|
zeroPageWrite(operandAddress, result);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x0A: // ASL acc
|
|
result = A << 1;
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x16: // ASL zp,X
|
|
operandAddress = eazpx();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = operand << 1;
|
|
NZFlags = result;
|
|
zeroPageWrite(operandAddress, result);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x1E: // ASL abs,X
|
|
operandAddress = eaabsx();
|
|
operand = memoryRead(operandAddress);
|
|
result = operand << 1;
|
|
NZFlags = result;
|
|
memoryWrite(operandAddress, result);
|
|
clock += 7;
|
|
break;
|
|
|
|
case 0x90: // BCC rr
|
|
operand = earel();
|
|
clock += 2;
|
|
if (getFNotC())
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0xB0: // BCS rr
|
|
operand = earel();
|
|
clock += 2;
|
|
if (getFC())
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0xF0: // BEQ rr
|
|
operand = earel();
|
|
clock += 2;
|
|
if (getFZ())
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0x2C: // BIT abs
|
|
operand = memoryRead(eaabs());
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x24: // BIT zp
|
|
operand = zeroPageRead(eazp());
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x30: // BMI rr
|
|
operand = earel();
|
|
clock += 2;
|
|
if (getFN())
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0xD0: // BNE rr
|
|
operand = earel();
|
|
clock += 2;
|
|
if (getFNotZ())
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0x10: // BPL rr
|
|
operand = earel();
|
|
clock += 2;
|
|
if (getFNotN())
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0x00: // BRK
|
|
push(PC >> 8); // save PCH, PCL & P
|
|
push(PC);
|
|
setN(getFN());
|
|
setZ(getFZ());
|
|
setC(getFC());
|
|
setB(true);
|
|
push(P);
|
|
setI(true);
|
|
PC = memoryRead(0xfffe);
|
|
PC |= memoryRead(0xffff) << 8;
|
|
clock += 7;
|
|
break;
|
|
|
|
case 0x50: // BVC rr
|
|
operand = earel();
|
|
clock += 2;
|
|
if (!getV())
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0x70: // BVS rr
|
|
operand = earel();
|
|
clock += 2;
|
|
if (getV())
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0x18: // CLC rr
|
|
setFC(false);
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xD8: // CLD
|
|
setD(false);
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x58: // CLI
|
|
setI(false);
|
|
clock += 2;
|
|
if (pendingIRQ > 0) {
|
|
pendingIRQ--;
|
|
assertIRQ();
|
|
}
|
|
break;
|
|
|
|
case 0xB8: // CLV
|
|
setV(false);
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xC9: // CMP #imm
|
|
result = 0x100 + A - eaimm();
|
|
NZFlags = result;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xCD: // CMP abs
|
|
result = 0x100 + A - memoryRead(eaabs());
|
|
NZFlags = result;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xC5: // CMP zp
|
|
result = 0x100 + A - zeroPageRead(eazp());
|
|
NZFlags = result;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0xC1: // CMP (zp,X)
|
|
result = 0x100 + A - memoryRead(eazpxind());
|
|
NZFlags = result;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xD1: // CMP (zp),Y
|
|
result = 0x100 + A - memoryRead(eazpindy());
|
|
NZFlags = result;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0xD5: // CMP zp,X
|
|
result = 0x100 + A - zeroPageRead(eazpx());
|
|
NZFlags = result;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xDD: // CMP abs,X
|
|
result = 0x100 + A - memoryRead(eaabsx());
|
|
NZFlags = result;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xD9: // CMP abs,Y
|
|
result = 0x100 + A - memoryRead(eaabsy());
|
|
NZFlags = result;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xE0: // CPX #imm
|
|
result = 0x100 + X - eaimm();
|
|
NZFlags = result;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xEC: // CPX abs
|
|
result = 0x100 + X - memoryRead(eaabs());
|
|
NZFlags = result;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xE4: // CPX zp
|
|
result = 0x100 + X - zeroPageRead(eazp());
|
|
NZFlags = result;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0xC0: // CPY #imm
|
|
result = 0x100 + Y - eaimm();
|
|
NZFlags = result;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xCC: // CPY abs
|
|
result = 0x100 + Y - memoryRead(eaabs());
|
|
NZFlags = result;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xC4: // CPY zp
|
|
result = 0x100+ Y - zeroPageRead(eazp());
|
|
NZFlags = result;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0xCE: // DEC abs
|
|
operandAddress = eaabs();
|
|
operand = memoryRead(operandAddress);
|
|
NZFlags = operand + 0xff;
|
|
memoryWrite(operandAddress, NZFlags);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xC6: // DEC zp
|
|
operandAddress = eazp();
|
|
operand = zeroPageRead(operandAddress);
|
|
NZFlags = operand + 0xff;
|
|
zeroPageWrite(operandAddress, NZFlags);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0xD6: // DEC zp,X
|
|
operandAddress = eazpx();
|
|
operand = zeroPageRead(operandAddress);
|
|
NZFlags = operand + 0xff;
|
|
zeroPageWrite(operandAddress, NZFlags);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xDE: // DEC abs,X
|
|
operandAddress = eaabsx();
|
|
operand = memoryRead(operandAddress);
|
|
NZFlags = operand + 0xff;
|
|
memoryWrite(operandAddress, NZFlags);
|
|
clock += 7;
|
|
break;
|
|
|
|
case 0xCA: // DEX
|
|
NZFlags = X + 0xff;
|
|
X = NZFlags & 0xff;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x88: // DEY
|
|
NZFlags = Y + 0xff;
|
|
Y = NZFlags & 0xff;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x49: // EOR #imm
|
|
A ^= eaimm();
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x4D: // EOR abs
|
|
A ^= memoryRead(eaabs());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x45: // EOR zp
|
|
A ^= zeroPageRead(eazp());
|
|
NZFlags = A;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x41: // EOR (zp,X)
|
|
A ^= memoryRead(eazpxind());
|
|
NZFlags = A;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x51: // EOR (zp),Y
|
|
A ^= memoryRead(eazpindy());
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x55: // EOR zp,X
|
|
A ^= zeroPageRead(eazpx());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x5D: // EOR abs,X
|
|
A ^= memoryRead(eaabsx());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x59: // EOR abs,Y
|
|
A ^= memoryRead(eaabsy());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xEE: // INC abs
|
|
operandAddress = eaabs();
|
|
operand = memoryRead(operandAddress);
|
|
NZFlags = operand + 1;
|
|
memoryWrite(operandAddress, NZFlags);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xE6: // INC zp
|
|
operandAddress = eazp();
|
|
operand = zeroPageRead(operandAddress);
|
|
NZFlags = operand + 1;
|
|
zeroPageWrite(operandAddress, NZFlags);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0xF6: // INC zp,X
|
|
operandAddress = eazpx();
|
|
operand = zeroPageRead(operandAddress);
|
|
NZFlags = operand + 1;
|
|
zeroPageWrite(operandAddress, NZFlags);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xFE: // INC abs,X
|
|
operandAddress = eaabsxNC();
|
|
operand = memoryRead(operandAddress);
|
|
NZFlags = operand + 1;
|
|
memoryWrite(operandAddress, NZFlags);
|
|
clock += 7;
|
|
break;
|
|
|
|
case 0xE8: // INX
|
|
NZFlags = X + 1;
|
|
X = NZFlags & 0xff;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xC8: // INY
|
|
NZFlags = Y + 1;
|
|
Y = NZFlags & 0xff;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x4C: // JMP abs
|
|
PC = eaabs();
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x6C: // JMP (abs)
|
|
PC = eaabsind();
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x20: // JSR abs
|
|
operandAddress = eaabs();
|
|
PC--;
|
|
push(PC >> 8);
|
|
push(PC);
|
|
PC = operandAddress;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xA9: // LDA #imm
|
|
A = eaimm();
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xAD: // LDA abs
|
|
A = memoryRead(eaabs());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xA5: // LDA zp
|
|
A = zeroPageRead(eazp());
|
|
NZFlags = A;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0xA1: // LDA (zp,X)
|
|
A = memoryRead(eazpxind());
|
|
NZFlags = A;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xB1: // LDA (zp),Y
|
|
A = memoryRead(eazpindy());
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0xB5: // LDA zp,X
|
|
A = zeroPageRead(eazpx());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xBD: // LDA abs,X
|
|
A = memoryRead(eaabsx());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xB9: // LDA abs,Y
|
|
A = memoryRead(eaabsy());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xA2: // LDX #imm
|
|
X = eaimm();
|
|
NZFlags = X;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xAE: // LDX abs
|
|
X = memoryRead(eaabs());
|
|
NZFlags = X;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xA6: // LDX zp
|
|
X = zeroPageRead(eazp());
|
|
NZFlags = X;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0xBE: // LDX abs,Y
|
|
X = memoryRead(eaabsy());
|
|
NZFlags = X;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xB6: // LDX zp,Y
|
|
X = zeroPageRead(eazpy());
|
|
NZFlags = X;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xA0: // LDY #imm
|
|
Y = eaimm();
|
|
NZFlags = Y;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xAC: // LDY abs
|
|
Y = memoryRead(eaabs());
|
|
NZFlags = Y;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xA4: // LDY zp
|
|
Y = zeroPageRead(eazp());
|
|
NZFlags = Y;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0xB4: // LDY zp,X
|
|
Y = zeroPageRead(eazpx());
|
|
NZFlags = Y;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xBC: // LDY abs,X
|
|
Y = memoryRead(eaabsx());
|
|
NZFlags = Y;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x4E: // LSR abs
|
|
operandAddress = eaabs();
|
|
operand = memoryRead(operandAddress);
|
|
result = (operand & 0x01) << 8; // just get the C bit
|
|
NZFlags = operand >> 1; // result in NZFlags
|
|
memoryWrite(operandAddress, NZFlags);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x46: // LSR zp
|
|
operandAddress = eazp();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = (operand & 0x01) << 8; // just get the C bit
|
|
NZFlags = operand >> 1; // result in NZFlags
|
|
zeroPageWrite(operandAddress, NZFlags);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x4A: // LSR acc
|
|
result = (A & 0x01) << 8; // just get the C bit
|
|
A >>= 1;
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x56: // LSR zp,X
|
|
operandAddress = eazpx();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = (operand & 0x01) << 8; // just get the C bit
|
|
NZFlags = operand >> 1; // result in NZFlags
|
|
zeroPageWrite(operandAddress, NZFlags);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x5E: // LSR abs,X
|
|
operandAddress = eaabsx();
|
|
operand = memoryRead(operandAddress);
|
|
result = (operand & 0x01) << 8; // just get the C bit
|
|
NZFlags = operand >> 1; // result in NZFlags
|
|
memoryWrite(operandAddress, NZFlags);
|
|
clock += 7;
|
|
break;
|
|
|
|
case 0xEA: // NOP
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x09: // ORA #imm
|
|
A |= eaimm();
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x0D: // ORA abs
|
|
A |= memoryRead(eaabs());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x05: // ORA zp
|
|
A |= zeroPageRead(eazp());
|
|
NZFlags = A;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x01: // ORA (zp,X)
|
|
A |= memoryRead(eazpxind());
|
|
NZFlags = A;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x11: // ORA (zp),Y
|
|
A |= memoryRead(eazpindy());
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x15: // ORA zp,X
|
|
A |= zeroPageRead(eazpx());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x1D: // ORA abs,X
|
|
A |= memoryRead(eaabsx());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x19: // ORA abs,Y
|
|
A |= memoryRead(eaabsy());
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x48: // PHA
|
|
push(A);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x08: // PHP
|
|
setN(getFN());
|
|
setZ(getFZ());
|
|
setC(getFC());
|
|
push(P);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x68: // PLA
|
|
A = pop();
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x28: // PLP
|
|
P = pop() | 0x20; // fix bug in bit5 of P
|
|
setFC(getC());
|
|
setFNZ(getN(), getZ());
|
|
clock += 4;
|
|
if ((pendingIRQ > 0) && !getI()) {
|
|
pendingIRQ--;
|
|
assertIRQ();
|
|
}
|
|
break;
|
|
|
|
case 0x2E: // ROL abs
|
|
operandAddress = eaabs();
|
|
operand = memoryRead(operandAddress);
|
|
result = (operand << 1) | getFC_();
|
|
NZFlags = result;
|
|
memoryWrite(operandAddress, result);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x26: // ROL zp
|
|
operandAddress = eazp();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = (operand << 1) | getFC_();
|
|
NZFlags = result;
|
|
zeroPageWrite(operandAddress, result);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x2A: // ROL acc
|
|
result = (A << 1) | getFC_();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x36: // ROL zp,X
|
|
operandAddress = eazpx();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = (operand << 1) | getFC_();
|
|
NZFlags = result;
|
|
zeroPageWrite(operandAddress, result);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x3E: // ROL abs,X
|
|
operandAddress = eaabsx();
|
|
operand = memoryRead(operandAddress);
|
|
result = (operand << 1) | getFC_();
|
|
NZFlags = result;
|
|
memoryWrite(operandAddress, result);
|
|
clock += 7;
|
|
break;
|
|
|
|
case 0x6E: // ROR abs
|
|
operandAddress = eaabs();
|
|
operand = memoryRead(operandAddress);
|
|
result = ((operand & 0x01) << 8) | (getFC_() << 7) |
|
|
(operand >> 1);
|
|
NZFlags = result;
|
|
memoryWrite(operandAddress, result);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x66: // ROR zp
|
|
operandAddress = eazp();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = ((operand & 0x01) << 8) | (getFC_() << 7) |
|
|
(operand >> 1);
|
|
NZFlags = result;
|
|
zeroPageWrite(operandAddress, result);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x6A: // ROR acc
|
|
result = ((A & 0x01) << 8) | (getFC_() << 7) | (A >> 1);
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x76: // ROR zp,X
|
|
operandAddress = eazpx();
|
|
operand = zeroPageRead(operandAddress);
|
|
result = ((operand & 0x01) << 8) | (getFC_() << 7) |
|
|
(operand >> 1);
|
|
NZFlags = result;
|
|
zeroPageWrite(operandAddress, result);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x7E: // ROR abs,X
|
|
operandAddress = eaabsx();
|
|
operand = memoryRead(operandAddress);
|
|
result = ((operand & 0x01) << 8) | (getFC_() << 7) |
|
|
(operand >> 1);
|
|
NZFlags = result;
|
|
memoryWrite(operandAddress, result);
|
|
clock += 7;
|
|
break;
|
|
|
|
case 0x40: // RTI
|
|
P = pop() | 0x20; // bit 5 bug of 6502
|
|
setFC(getC());
|
|
setFNZ(getN(), getZ());
|
|
PC = pop(); // splitting is necessary
|
|
PC += pop() << 8; // because of nested macros
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x60: // RTS
|
|
PC = pop(); // splitting is necessary
|
|
PC += pop() << 8; // because of nested macros
|
|
PC++;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xE9: // SBC #imm
|
|
operand = 255 - eaimm();
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xED: // SBC abs
|
|
operand = 255 - memoryRead(eaabs());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xE5: // SBC zp
|
|
operand = 255 - zeroPageRead(eazp());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0xE1: // SBC (zp,X)
|
|
operand = 255 - memoryRead(eazpxind());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xF1: // SBC (zp),Y
|
|
operand = 255 - memoryRead(eazpindy());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0xF5: // SBC zp,X
|
|
operand = 255 - zeroPageRead(eazpx());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xFD: // SBC abs,X
|
|
operand = 255 - memoryRead(eaabsx());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xF9: // SBC abs,Y
|
|
operand = 255 - memoryRead(eaabsy());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x38: // SEC
|
|
setFC(true);
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xF8: // SED
|
|
setD(true);
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x78: // SEI
|
|
setI(true);
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x8D: // STA abs
|
|
memoryWrite(eaabs(), A);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x85: // STA zp
|
|
zeroPageWrite(eazp(), A);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x81: // STA (zp,X)
|
|
memoryWrite(eazpxind(), A);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x91: // STA (zp),Y
|
|
memoryWrite(eazpindy(), A);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x95: // STA zp,X
|
|
zeroPageWrite(eazpx(), A);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x9D: // STA abs,X
|
|
memoryWrite(eaabsx(), A);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x99: // STA abs,Y
|
|
memoryWrite(eaabsy(), A);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x8E: // STX abs
|
|
memoryWrite(eaabs(), X);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x86: // STX zp
|
|
zeroPageWrite(eazp(), X);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x96: // STX zp,Y
|
|
zeroPageWrite(eazpy(), X);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x8C: // STY abs
|
|
memoryWrite(eaabs(), Y);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x84: // STY zp
|
|
zeroPageWrite(eazp(), Y);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x94: // STY zp,X
|
|
zeroPageWrite(eazpx(), Y);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xAA: // TAX
|
|
X = A;
|
|
NZFlags = X;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xA8: // TAY
|
|
Y = A;
|
|
NZFlags = Y;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0xBA: // TSX
|
|
X = S;
|
|
NZFlags = X;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x8A: // TXA
|
|
A = X;
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x9A: // TXS
|
|
S = X;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x98: // TYA
|
|
A = Y;
|
|
NZFlags = A;
|
|
clock += 2;
|
|
break;
|
|
|
|
/*
|
|
* 65C02 instructions
|
|
* note: timing is not correct
|
|
*/
|
|
|
|
case 0x72: // ADC (zp)
|
|
operand = memoryRead(eazpind());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
adcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x32: // AND (zp)
|
|
A &= memoryRead(eazpind());
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x34: // BIT zp,X
|
|
operand = zeroPageRead(eazpx());
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x89: // BIT #imm
|
|
operand = eaimm();
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x3C: // BIT abs,X
|
|
operand = eaabsx();
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x80: // BRA rr
|
|
operand = earel();
|
|
clock += 2;
|
|
branch(operand);
|
|
break;
|
|
|
|
case 0xD2: // CMP (zp)
|
|
result = 0x100 + A - memoryRead(eazpind());
|
|
NZFlags = result;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x3A: // DEA acc
|
|
NZFlags = A + 0xff;
|
|
A = NZFlags & 0xff;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x52: // EOR (zp)
|
|
A ^= memoryRead(eazpind());
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x1A: // INA acc
|
|
NZFlags = A + 1;
|
|
A = NZFlags & 0xff;
|
|
clock += 2;
|
|
break;
|
|
|
|
case 0x7C: // JMP (abs,X)
|
|
PC = eaabsxind();
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0xB2: // LDA (zp)
|
|
A = memoryRead(eazpind());
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x12: // ORA (zp)
|
|
A |= memoryRead(eazpind());
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0xDA: // PHX
|
|
push(X);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0xFA: // PLX
|
|
X = pop();
|
|
NZFlags = X;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x5A: // PHY
|
|
push(Y);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x7A: // PLY
|
|
Y = pop();
|
|
NZFlags = Y;
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0xF2: // SBC (zp)
|
|
operand = 255 - memoryRead(eazpind());
|
|
result = operand + A + getFC_();
|
|
setV(!(((operand ^ A) & 0x80) != 0) && (((A ^ result) & 0x80) != 0));
|
|
sbcBCDAdjust();
|
|
A = result & 0xff;
|
|
NZFlags = A;
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x92: // STA (zp)
|
|
memoryWrite(eazpind(), A);
|
|
clock += 6;
|
|
break;
|
|
|
|
case 0x9C: // STZ abs
|
|
memoryWrite(eaabs(), 0);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x64: // STZ zp
|
|
zeroPageWrite(eazp(), 0);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x74: // STZ zp,X
|
|
zeroPageWrite(eazpx(), 0);
|
|
clock += 3;
|
|
break;
|
|
|
|
case 0x9E: // STZ abs,X
|
|
memoryWrite(eaabsx(), 0);
|
|
clock += 4;
|
|
break;
|
|
|
|
case 0x1C: // TRB abs
|
|
operandAddress = eaabs();
|
|
operand = memoryRead(operandAddress);
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
memoryWrite(operandAddress, (operand & ~A) & 0xff);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x14: // TRB zp
|
|
operandAddress = eazp();
|
|
operand = zeroPageRead(operandAddress);
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
zeroPageWrite(operandAddress, (operand & ~A) & 0xff);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x0C: // TSB abs
|
|
operandAddress = eaabs();
|
|
operand = memoryRead(operandAddress);
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
memoryWrite(operandAddress, operand | A);
|
|
clock += 5;
|
|
break;
|
|
|
|
case 0x04: // TSB zp
|
|
operandAddress = eazp();
|
|
operand = zeroPageRead(operandAddress);
|
|
setV((operand & 0x40) != 0);
|
|
NZFlags = ((operand & 0x80) << 2) | (A & operand);
|
|
zeroPageWrite(operandAddress, operand | A);
|
|
clock += 5;
|
|
break;
|
|
|
|
default: // unknown instructions
|
|
clock += 2;
|
|
}
|
|
}
|
|
|
|
public final int executeInstructions(int num) {
|
|
// Initialize
|
|
int clockStart = clock;
|
|
|
|
for (; num >= 16; num -= 16) {
|
|
PC &= 0xffff; // Keep PC "sort of" bounded
|
|
executeInstruction(); executeInstruction();
|
|
executeInstruction(); executeInstruction();
|
|
executeInstruction(); executeInstruction();
|
|
executeInstruction(); executeInstruction();
|
|
executeInstruction(); executeInstruction();
|
|
executeInstruction(); executeInstruction();
|
|
executeInstruction(); executeInstruction();
|
|
}
|
|
PC &= 0xffff;
|
|
for (; num > 0; num--)
|
|
executeInstruction();
|
|
|
|
return (clock - clockStart) & 0x7fffffff;
|
|
}
|
|
|
|
public final void checkInterrupts() {
|
|
// Reset
|
|
if ((exceptionRegister & SIG_6502_RESET) != 0) {
|
|
onReset();
|
|
|
|
A = X = Y = 0;
|
|
P = 0x20;
|
|
setFC(getC());
|
|
setFNZ(getN(), getZ());
|
|
S = 0xff;
|
|
PC = memoryRead(0xfffc);
|
|
PC |= (memoryRead(0xfffd) << 8);
|
|
exceptionRegister &= ~SIG_6502_RESET;
|
|
}
|
|
|
|
// No NMI nor IRQ...
|
|
if ((exceptionRegister & SIG_6502_NMI) != 0) {
|
|
onNMI();
|
|
|
|
push(PC >> 8);
|
|
push(PC);
|
|
setN(getFN());
|
|
setZ(getFZ());
|
|
setC(getFC());
|
|
push(P);
|
|
PC = memoryRead(0xfffa);
|
|
PC |= memoryRead(0xfffb) << 8;
|
|
clock += 7;
|
|
exceptionRegister ^= SIG_6502_NMI;
|
|
}
|
|
|
|
if ((exceptionRegister & SIG_6502_IRQ) != 0) {
|
|
onIRQ();
|
|
|
|
if (getI())
|
|
pendingIRQ++;
|
|
else {
|
|
push(PC >> 8);
|
|
push(PC);
|
|
setN(getFN());
|
|
setZ(getFZ());
|
|
setC(getFC());
|
|
setB(false);
|
|
push(P);
|
|
setI(true);
|
|
PC = memoryRead(0xfffe);
|
|
PC |= memoryRead(0xffff) << 8;
|
|
clock += 7;
|
|
}
|
|
exceptionRegister ^= SIG_6502_IRQ;
|
|
}
|
|
}
|
|
}
|