From 4cca0b4663bdc5cd9dd0578cc7616cd8757d8e27 Mon Sep 17 00:00:00 2001 From: Seth Morabito Date: Sun, 12 Jun 2016 18:55:09 -0700 Subject: [PATCH] Initial 65C02 Support First pass at adding CMOS 65C02 support for the following instructions and addressing modes: * ORA (ZP Indirect) * AND (ZP Indirect) * EOR (ZP Indirect) * ADC (ZP Indirect) * STA (ZP Indirect) * LDA (ZP Indirect) * CMP (ZP Indirect) * SBC (ZP Indirect) --- src/main/java/com/loomcom/symon/Cpu.java | 83 ++++- .../com/loomcom/symon/InstructionTable.java | 214 +++++++----- .../symon/CpuIndirectIndexedModeTest.java | 8 - .../loomcom/symon/CpuIndirectModeTest.java | 21 +- .../loomcom/symon/CpuIndirectXModeTest.java | 33 +- src/test/java/com/loomcom/symon/CpuTest.java | 1 + .../symon/CpuZeroPageIndirectTest.java | 329 ++++++++++++++++++ 7 files changed, 560 insertions(+), 129 deletions(-) create mode 100644 src/test/java/com/loomcom/symon/CpuZeroPageIndirectTest.java diff --git a/src/main/java/com/loomcom/symon/Cpu.java b/src/main/java/com/loomcom/symon/Cpu.java index f72f1da..794c934 100644 --- a/src/main/java/com/loomcom/symon/Cpu.java +++ b/src/main/java/com/loomcom/symon/Cpu.java @@ -79,7 +79,7 @@ public class Cpu implements InstructionTable { * Construct a new CPU. */ public Cpu() { - this(CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG); + this(CpuBehavior.NMOS_6502); } public Cpu(CpuBehavior behavior) { @@ -204,6 +204,13 @@ public class Cpu implements InstructionTable { case 3: // Absolute effectiveAddress = Utils.address(state.args[0], state.args[1]); break; + case 4: // 65C02 (Zero Page) + if (behavior == CpuBehavior.CMOS_6502 || + behavior == CpuBehavior.CMOS_65816) { + effectiveAddress = Utils.address(bus.read(state.args[0]), + bus.read((state.args[0] + 1) & 0xff)); + } + break; case 5: // Zero Page,X / Zero Page,Y if (state.ir == 0x96 || state.ir == 0xb6) { effectiveAddress = zpyAddress(state.args[0]); @@ -262,7 +269,7 @@ public class Cpu implements InstructionTable { break; case 0x08: // PHP - Push Processor Status - Implied // Break flag is always set in the stack value. - stackPush(state.getStatusFlag() | 0x10); + stackPush(state.getStatusFlag() | 0x10); break; case 0x10: // BPL - Branch if Positive - Relative if (!getNegativeFlag()) { @@ -398,11 +405,12 @@ public class Cpu implements InstructionTable { case 0x4c: // JMP - Absolute state.pc = Utils.address(state.args[0], state.args[1]); break; + // TODO: 65C02 instruction 0x7C, JMP (Indirect,X) case 0x6c: // JMP - Indirect lo = Utils.address(state.args[0], state.args[1]); // Address of low byte if (state.args[0] == 0xff && - (behavior == CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG || + (behavior == CpuBehavior.NMOS_6502 || behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) { hi = Utils.address(0x00, state.args[1]); } else { @@ -422,13 +430,20 @@ public class Cpu implements InstructionTable { * (http://www.obelisk.demon.co.uk/6502/reference.html#JMP) */ break; - + case 0x7C: // 65C02 JMP - Absolute,X + state.pc = (Utils.address(state.args[0], state.args[1]) + state.x) & 0xffff; + break; /** ORA - Logical Inclusive Or ******************************************/ case 0x09: // #Immediate state.a |= state.args[0]; setArithmeticFlags(state.a); break; + case 0x12: // 65C02 ORA (ZP) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0x01: // (Zero Page,X) case 0x05: // Zero Page case 0x0d: // Absolute @@ -471,6 +486,11 @@ public class Cpu implements InstructionTable { state.a &= state.args[0]; setArithmeticFlags(state.a); break; + case 0x32: // 65C02 AND (ZP) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0x21: // (Zero Page,X) case 0x25: // Zero Page case 0x2d: // Absolute @@ -503,6 +523,11 @@ public class Cpu implements InstructionTable { state.a ^= state.args[0]; setArithmeticFlags(state.a); break; + case 0x52: // 65C02 EOR (ZP) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0x41: // (Zero Page,X) case 0x45: // Zero Page case 0x4d: // Absolute @@ -538,6 +563,11 @@ public class Cpu implements InstructionTable { state.a = adc(state.a, state.args[0]); } break; + case 0x72: // 65C02 ADC (ZP) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0x61: // (Zero Page,X) case 0x65: // Zero Page case 0x6d: // Absolute @@ -569,6 +599,11 @@ public class Cpu implements InstructionTable { /** STA - Store Accumulator *********************************************/ + case 0x92: // 65C02 STA (ZP) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0x81: // (Zero Page,X) case 0x85: // Zero Page case 0x8d: // Absolute @@ -629,6 +664,11 @@ public class Cpu implements InstructionTable { state.a = state.args[0]; setArithmeticFlags(state.a); break; + case 0xb2: // 65C02 LDA (ZP) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0xa1: // (Zero Page,X) case 0xa5: // Zero Page case 0xad: // Absolute @@ -655,6 +695,11 @@ public class Cpu implements InstructionTable { case 0xc9: // #Immediate cmp(state.a, state.args[0]); break; + case 0xd2: // 65C02 CMP (ZP) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0xc1: // (Zero Page,X) case 0xc5: // Zero Page case 0xcd: // Absolute @@ -695,6 +740,11 @@ public class Cpu implements InstructionTable { state.a = sbc(state.a, state.args[0]); } break; + case 0xf2: // 65C02 SBC (ZP) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0xe1: // (Zero Page,X) case 0xe5: // Zero Page case 0xed: // Absolute @@ -1334,6 +1384,13 @@ public class Cpu implements InstructionTable { return (zp + state.x) & 0xff; } + /** + * Given a single byte, compute the Zero Page,Y offset address. + */ + int zpyAddress(int zp) { + return (zp + state.y) & 0xff; + } + /** * Given a single byte, compute the offset address. */ @@ -1342,21 +1399,21 @@ public class Cpu implements InstructionTable { return (state.pc + (byte) offset) & 0xffff; } - /** - * Given a single byte, compute the Zero Page,Y offset address. - */ - int zpyAddress(int zp) { - return (zp + state.y) & 0xff; - } - /* * Perform a busy-loop until the instruction should complete on the wall clock */ private void delayLoop(int opcode) { - int clockSteps = Cpu.instructionClocks[0xff & opcode]; + final int clockSteps; + + if (behavior == CpuBehavior.NMOS_WITH_ROR_BUG || + behavior == CpuBehavior.NMOS_6502) { + clockSteps = Cpu.instructionClocksNmos[0xff & opcode]; + } else { + clockSteps = Cpu.instructionClocksCmos[0xff & opcode]; + } if (clockSteps == 0) { - logger.warn("Opcode {} has clock step of 0!", opcode); + logger.warn("Opcode {} has clock step of 0!", String.format("0x%02x", opcode)); return; } diff --git a/src/main/java/com/loomcom/symon/InstructionTable.java b/src/main/java/com/loomcom/symon/InstructionTable.java index 4f46c4a..d4a46bb 100644 --- a/src/main/java/com/loomcom/symon/InstructionTable.java +++ b/src/main/java/com/loomcom/symon/InstructionTable.java @@ -46,20 +46,17 @@ public interface InstructionTable { * * NB: Does NOT implement "unimplemented" NMOS instructions. */ - NMOS_WITH_INDIRECT_JMP_BUG, - - /** - * Emulate an NMOS 6502 without the indirect JMP bug. This type of 6502 - * does not actually exist in the wild. - * - * NB: Does NOT implement "unimplemented" NMOS instructions. - */ - NMOS_WITHOUT_INDIRECT_JMP_BUG, + NMOS_6502, /** * Emulate a CMOS 65C02, with all CMOS instructions and addressing modes. */ - CMOS + CMOS_6502, + + /** + * Emulate a CMOS 65C816. + */ + CMOS_65816 } /** @@ -128,19 +125,25 @@ public interface InstructionTable { ZPG { public String toString() { - return "Zeropage"; + return "Zero Page"; } }, ZPX { public String toString() { - return "Zeropage, X-indexed"; + return "Zero Page, X-indexed"; } }, ZPY { public String toString() { - return "Zeropage, Y-indexed"; + return "Zero Page, Y-indexed"; + } + }, + + ZPI { + public String toString() { + return "Zero Page Indirect"; } }, @@ -154,52 +157,55 @@ public interface InstructionTable { // 6502 opcodes. No 65C02 opcodes implemented. /** - * Instruction opcode names. + * Instruction opcode names. This lists all opcodes for + * NMOS 6502, CMOS 65C02, and CMOS 65C816 */ String[] opcodeNames = { - "BRK", "ORA", null, null, null, "ORA", "ASL", null, - "PHP", "ORA", "ASL", null, null, "ORA", "ASL", null, - "BPL", "ORA", null, null, null, "ORA", "ASL", null, - "CLC", "ORA", null, null, null, "ORA", "ASL", null, - "JSR", "AND", null, null, "BIT", "AND", "ROL", null, - "PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null, - "BMI", "AND", null, null, null, "AND", "ROL", null, - "SEC", "AND", null, null, null, "AND", "ROL", null, - "RTI", "EOR", null, null, null, "EOR", "LSR", null, - "PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null, - "BVC", "EOR", null, null, null, "EOR", "LSR", null, - "CLI", "EOR", null, null, null, "EOR", "LSR", null, - "RTS", "ADC", null, null, null, "ADC", "ROR", null, - "PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null, - "BVS", "ADC", null, null, null, "ADC", "ROR", null, - "SEI", "ADC", null, null, null, "ADC", "ROR", null, - "BCS", "STA", null, null, "STY", "STA", "STX", null, - "DEY", null, "TXA", null, "STY", "STA", "STX", null, - "BCC", "STA", null, null, "STY", "STA", "STX", null, - "TYA", "STA", "TXS", null, null, "STA", null, null, - "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, - "TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, - "BCS", "LDA", null, null, "LDY", "LDA", "LDX", null, - "CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, - "CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, - "INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, - "BNE", "CMP", null, null, null, "CMP", "DEC", null, - "CLD", "CMP", null, null, null, "CMP", "DEC", null, - "CPX", "SBC", null, null, "CPX", "SBC", "INC", null, - "INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null, - "BEQ", "SBC", null, null, null, "SBC", "INC", null, - "SED", "SBC", null, null, null, "SBC", "INC", null + "BRK", "ORA", null, null, null, "ORA", "ASL", null, // 0x00-0x07 + "PHP", "ORA", "ASL", null, null, "ORA", "ASL", null, // 0x08-0x0f + "BPL", "ORA", "ORA", null, null, "ORA", "ASL", null, // 0x10-0x17 + "CLC", "ORA", null, null, null, "ORA", "ASL", null, // 0x18-0x1f + "JSR", "AND", null, null, "BIT", "AND", "ROL", null, // 0x20-0x27 + "PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null, // 0x28-0x2f + "BMI", "AND", "AND", null, null, "AND", "ROL", null, // 0x30-0x37 + "SEC", "AND", null, null, null, "AND", "ROL", null, // 0x38-0x3f + "RTI", "EOR", null, null, null, "EOR", "LSR", null, // 0x40-0x47 + "PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null, // 0x48-0x4f + "BVC", "EOR", "EOR", null, null, "EOR", "LSR", null, // 0x50-0x57 + "CLI", "EOR", null, null, null, "EOR", "LSR", null, // 0x58-0x5f + "RTS", "ADC", null, null, null, "ADC", "ROR", null, // 0x60-0x67 + "PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null, // 0x68-0x6f + "BVS", "ADC", "ADC", null, null, "ADC", "ROR", null, // 0x70-0x77 + "SEI", "ADC", null, null, "JMP", "ADC", "ROR", null, // 0x78-0x7f + "BCS", "STA", null, null, "STY", "STA", "STX", null, // 0x80-0x87 + "DEY", null, "TXA", null, "STY", "STA", "STX", null, // 0x88-0x8f + "BCC", "STA", "STA", null, "STY", "STA", "STX", null, // 0x90-0x97 + "TYA", "STA", "TXS", null, null, "STA", null, null, // 0x98-0x9f + "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, // 0xa0-0xa7 + "TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, // 0xa8-0xaf + "BCS", "LDA", "LDA", null, "LDY", "LDA", "LDX", null, // 0xb0-0xb7 + "CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, // 0xb8-0xbf + "CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, // 0xc0-0xc7 + "INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, // 0xc8-0xcf + "BNE", "CMP", "CMP", null, null, "CMP", "DEC", null, // 0xd0-0xd7 + "CLD", "CMP", null, null, null, "CMP", "DEC", null, // 0xd8-0xdf + "CPX", "SBC", null, null, "CPX", "SBC", "INC", null, // 0xe0-0xe7 + "INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null, // 0xe8-0xef + "BEQ", "SBC", "SBC", null, null, "SBC", "INC", null, // 0xf0-0xf7 + "SED", "SBC", null, null, null, "SBC", "INC", null // 0xf8-0xff }; /** - * Instruction addressing modes. + * Instruction addressing modes. This table includes sizes + * for all instructions for NMOS 6502, CMOS 65C02, + * and CMOS 65C816 */ Mode[] instructionModes = { Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03 Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07 Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f - Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x10-0x13 + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x10-0x13 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17 Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f @@ -207,7 +213,7 @@ public interface InstructionTable { Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27 Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f - Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x30-0x33 + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x30-0x33 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37 Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f @@ -215,7 +221,7 @@ public interface InstructionTable { Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47 Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f - Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x50-0x53 + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x50-0x53 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57 Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f @@ -223,15 +229,15 @@ public interface InstructionTable { Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67 Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f - Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x70-0x73 + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x70-0x73 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77 Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b - Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f + Mode.ABX, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87 Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f - Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93 + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x90-0x93 Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97 Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f @@ -239,7 +245,7 @@ public interface InstructionTable { Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7 Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf - Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3 + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xb0-0xb3 Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7 Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xb8-0xbb Mode.ABX, Mode.ABX, Mode.ABY, Mode.NUL, // 0xbc-0xbf @@ -247,7 +253,7 @@ public interface InstructionTable { Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7 Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf - Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xd0-0xd3 + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xd0-0xd3 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7 Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf @@ -255,7 +261,7 @@ public interface InstructionTable { Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7 Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef - Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xf0-0xf3 + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xf0-0xf3 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7 Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff @@ -263,47 +269,73 @@ public interface InstructionTable { /** - * Size, in bytes, required for each instruction. + * Size, in bytes, required for each instruction. This table + * includes sizes for all instructions for NMOS 6502, CMOS 65C02, + * and CMOS 65C816 */ int[] instructionSizes = { - 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, - 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, - 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, - 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, - 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, - 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, - 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, - 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, - 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, - 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, - 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, - 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, - 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, - 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, - 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, - 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0 + 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, // 0x00-0x0f + 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x10-0x1f + 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x20-0x2f + 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x30-0x3f + 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x40-0x4f + 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x50-0x5f + 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x60-0x6f + 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 3, 3, 3, 0, // 0x70-0x7f + 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, // 0x80-0x8f + 2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, // 0x90-0x9f + 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xa0-0xaf + 2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 0xb0-0xbf + 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xc0-0xcf + 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0xd0-0xdf + 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xe0-0xef + 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0 // 0xf0-0xff }; /** - * Number of clock cycles required for each instruction + * Number of clock cycles required for each instruction when + * in NMOS mode. */ - int[] instructionClocks = { - 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, - 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, - 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, - 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, - 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, - 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, - 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, - 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, - 2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, - 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, - 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, - 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, - 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, - 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, - 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, - 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 + int[] instructionClocksNmos = { + 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f + 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f + 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f + 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f + 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f + 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f + 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f + 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f + 2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f + 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f + 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf + 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf + 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf + 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf + 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef + 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff + }; + + /** + * Number of clock cycles required for each instruction when + * in CMOS mode + */ + int[] instructionClocksCmos = { + 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f + 2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f + 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f + 2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f + 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f + 2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f + 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f + 2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f + 2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f + 2, 6, 5, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f + 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf + 2, 5, 5, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf + 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf + 2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf + 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef + 2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff }; } diff --git a/src/test/java/com/loomcom/symon/CpuIndirectIndexedModeTest.java b/src/test/java/com/loomcom/symon/CpuIndirectIndexedModeTest.java index 191e930..831e36b 100644 --- a/src/test/java/com/loomcom/symon/CpuIndirectIndexedModeTest.java +++ b/src/test/java/com/loomcom/symon/CpuIndirectIndexedModeTest.java @@ -25,18 +25,10 @@ public class CpuIndirectIndexedModeTest { bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8); cpu.reset(); - // Assert initial state - assertEquals(0, cpu.getAccumulator()); - assertEquals(0, cpu.getXRegister()); - assertEquals(0, cpu.getYRegister()); - assertEquals(0x200, cpu.getProgramCounter()); - assertEquals(0xff, cpu.getStackPointer()); - assertEquals(0x20, cpu.getProcessorStatus()); } @Test public void test_LDA() throws Exception { - assertEquals(cpu.toString(), 0x00, cpu.getAccumulator()); bus.write(0x0014, 0x00); bus.write(0x0015, 0xd8); bus.write(0xd828, 0x03); diff --git a/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java b/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java index 87f7442..2fc7f41 100644 --- a/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java +++ b/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java @@ -3,15 +3,19 @@ package com.loomcom.symon; import com.loomcom.symon.devices.Memory; import com.loomcom.symon.exceptions.MemoryAccessException; -import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; -public class CpuIndirectModeTest extends TestCase { +import static org.junit.Assert.assertEquals; + +public class CpuIndirectModeTest { protected Cpu cpu; protected Bus bus; protected Memory mem; - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { this.cpu = new Cpu(); this.bus = new Bus(0x0000, 0xffff); this.mem = new Memory(0x0000, 0xffff); @@ -41,6 +45,7 @@ public class CpuIndirectModeTest extends TestCase { /* JMP - Jump - $6c */ + @Test public void test_JMP_notOnPageBoundary() throws MemoryAccessException { bus.write(0x3400, 0x00); bus.write(0x3401, 0x54); @@ -51,6 +56,7 @@ public class CpuIndirectModeTest extends TestCase { assertEquals(0x20, cpu.getProcessorStatus()); } + @Test public void test_JMP_with_ROR_Bug() throws MemoryAccessException { cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG); bus.write(0x3400, 0x22); @@ -63,8 +69,9 @@ public class CpuIndirectModeTest extends TestCase { assertEquals(0x20, cpu.getProcessorStatus()); } + @Test public void test_JMP_withIndirectBug() throws MemoryAccessException { - cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG); + cpu.setBehavior(Cpu.CpuBehavior.NMOS_6502); bus.write(0x3400, 0x22); bus.write(0x34ff, 0x00); bus.write(0x3500, 0x54); @@ -75,8 +82,9 @@ public class CpuIndirectModeTest extends TestCase { assertEquals(0x20, cpu.getProcessorStatus()); } + @Test public void test_JMP_withOutIndirectBug() throws MemoryAccessException { - cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITHOUT_INDIRECT_JMP_BUG); + cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502); bus.write(0x3400, 0x22); bus.write(0x34ff, 0x00); bus.write(0x3500, 0x54); @@ -87,8 +95,9 @@ public class CpuIndirectModeTest extends TestCase { assertEquals(0x20, cpu.getProcessorStatus()); } + @Test public void test_JMP_cmos() throws MemoryAccessException { - cpu.setBehavior(Cpu.CpuBehavior.CMOS); + cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502); bus.write(0x3400, 0x22); bus.write(0x34ff, 0x00); bus.write(0x3500, 0x54); diff --git a/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java b/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java index 15ad0ce..c93367b 100644 --- a/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java +++ b/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java @@ -3,14 +3,21 @@ package com.loomcom.symon; import com.loomcom.symon.devices.Memory; import com.loomcom.symon.exceptions.MemoryAccessException; import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; -public class CpuIndirectXModeTest extends TestCase { +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CpuIndirectXModeTest { protected Cpu cpu; protected Bus bus; protected Memory mem; - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { this.cpu = new Cpu(); this.bus = new Bus(0x0000, 0xffff); this.mem = new Memory(0x0000, 0xffff); @@ -47,7 +54,7 @@ public class CpuIndirectXModeTest extends TestCase { */ /* ORA - Logical Inclusive OR - $1d */ - + @Test public void test_ORA() throws MemoryAccessException { // Set some initial values in memory bus.write(0x2c30, 0x00); @@ -93,7 +100,7 @@ public class CpuIndirectXModeTest extends TestCase { } /* AND - Logical AND - $3d */ - + @Test public void test_AND() throws MemoryAccessException { bus.write(0x1a30, 0x00); bus.write(0x1a31, 0x11); @@ -153,7 +160,7 @@ public class CpuIndirectXModeTest extends TestCase { } /* EOR - Exclusive OR - $5d */ - + @Test public void test_EOR() throws MemoryAccessException { bus.write(0xab40, 0x00); bus.write(0xab41, 0xff); @@ -162,7 +169,7 @@ public class CpuIndirectXModeTest extends TestCase { cpu.setXRegister(0x30); - bus.loadProgram(0xa9, 0x88, // LDA #$88 + bus.loadProgram(0xa9, 0x88, // LDA #$88 0x5d, 0x10, 0xab, // EOR $ab10,X 0x5d, 0x11, 0xab, // EOR $ab11,X 0x5d, 0x12, 0xab, // EOR $ab12,X @@ -189,7 +196,7 @@ public class CpuIndirectXModeTest extends TestCase { } /* ADC - Add with Carry - $7d */ - + @Test public void test_ADC() throws MemoryAccessException { bus.write(0xab40, 0x01); bus.write(0xab41, 0xff); @@ -290,6 +297,7 @@ public class CpuIndirectXModeTest extends TestCase { assertTrue(cpu.getCarryFlag()); } + @Test public void test_ADC_IncludesCarry() throws MemoryAccessException { bus.write(0xab40, 0x01); @@ -307,6 +315,7 @@ public class CpuIndirectXModeTest extends TestCase { assertFalse(cpu.getCarryFlag()); } + @Test public void test_ADC_DecimalMode() throws MemoryAccessException { bus.write(0xab40, 0x01); bus.write(0xab41, 0x99); @@ -404,7 +413,7 @@ public class CpuIndirectXModeTest extends TestCase { } /* STA - Store Accumulator - $9d */ - + @Test public void test_STA() throws MemoryAccessException { cpu.setXRegister(0x30); @@ -438,7 +447,7 @@ public class CpuIndirectXModeTest extends TestCase { } /* LDA - Load Accumulator - $bd */ - + @Test public void test_LDA() throws MemoryAccessException { bus.write(0xab42, 0x00); bus.write(0xab43, 0x0f); @@ -467,7 +476,7 @@ public class CpuIndirectXModeTest extends TestCase { } /* CMP - Compare Accumulator - $dd */ - + @Test public void test_CMP() throws MemoryAccessException { bus.write(0xab40, 0x00); bus.write(0xab41, 0x80); @@ -498,7 +507,7 @@ public class CpuIndirectXModeTest extends TestCase { } /* SBC - Subtract with Carry - $fd */ - + @Test public void test_SBC() throws MemoryAccessException { bus.write(0xab40, 0x01); @@ -557,6 +566,7 @@ public class CpuIndirectXModeTest extends TestCase { assertTrue(cpu.getCarryFlag()); } + @Test public void test_SBC_IncludesNotOfCarry() throws MemoryAccessException { bus.write(0xab40, 0x01); @@ -602,6 +612,7 @@ public class CpuIndirectXModeTest extends TestCase { } + @Test public void test_SBC_DecimalMode() throws MemoryAccessException { bus.write(0xab40, 0x01); bus.write(0xab50, 0x11); diff --git a/src/test/java/com/loomcom/symon/CpuTest.java b/src/test/java/com/loomcom/symon/CpuTest.java index ed90780..b8eaab0 100644 --- a/src/test/java/com/loomcom/symon/CpuTest.java +++ b/src/test/java/com/loomcom/symon/CpuTest.java @@ -241,6 +241,7 @@ public class CpuTest extends TestCase { public void testSetProcessorStatus() { // Default + assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getIrqDisableFlag()); diff --git a/src/test/java/com/loomcom/symon/CpuZeroPageIndirectTest.java b/src/test/java/com/loomcom/symon/CpuZeroPageIndirectTest.java new file mode 100644 index 0000000..7ef91cb --- /dev/null +++ b/src/test/java/com/loomcom/symon/CpuZeroPageIndirectTest.java @@ -0,0 +1,329 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import org.junit.Test; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Test for Zero Page Indirect addressing mode, found on some instructions + * in the 65C02 and 65816 + */ +public class CpuZeroPageIndirectTest { + protected Cpu cpu; + protected Bus bus; + protected Memory mem; + + private void makeCmosCpu() throws Exception { + makeCpu(InstructionTable.CpuBehavior.CMOS_6502); + } + + private void makeNmosCpu() throws Exception { + makeCpu(InstructionTable.CpuBehavior.NMOS_6502); + } + + private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception { + this.cpu = new Cpu(behavior); + this.bus = new Bus(0x0000, 0xffff); + this.mem = new Memory(0x0000, 0xffff); + bus.addCpu(cpu); + bus.addDevice(mem); + + // Load the reset vector. + bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff); + bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8); + + cpu.reset(); + } + + @Test + public void test_ora() throws Exception { + makeCmosCpu(); + + // Set some initial values in zero page. + bus.write(0x30, 0x00); + bus.write(0x31, 0x10); + + bus.write(0x40, 0x01); + bus.write(0x41, 0x10); + + bus.write(0x50, 0x02); + bus.write(0x51, 0x10); + + bus.write(0x60, 0x03); + bus.write(0x61, 0x10); + + bus.write(0x1000, 0x11); + bus.write(0x1001, 0x22); + bus.write(0x1002, 0x44); + bus.write(0x1003, 0x88); + + bus.loadProgram(0x12, 0x30, // ORA ($30) + 0x12, 0x40, // ORA ($40) + 0x12, 0x50, // ORA ($50) + 0x12, 0x60); // ORA ($60) + + + assertEquals(0x00, cpu.getAccumulator()); + + // 0x00 | 0x11 = 0x11 + cpu.step(); + assertEquals(0x11, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // 0x11 | 0x22 = 0x33 + cpu.step(); + assertEquals(0x33, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // 0x33 | 0x44 = 0x77 + cpu.step(); + assertEquals(0x77, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // 0x77 | 0x88 = 0xff + cpu.step(); + assertEquals(0xff, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getNegativeFlag()); + } + + @Test + public void test_ora_requiresCmosCpu() throws Exception { + makeNmosCpu(); + + // Set some initial values in zero page. + bus.write(0x30, 0x00); + bus.write(0x31, 0x10); + + bus.write(0x40, 0x01); + bus.write(0x41, 0x10); + + bus.write(0x50, 0x02); + bus.write(0x51, 0x10); + + bus.write(0x60, 0x03); + bus.write(0x61, 0x10); + + bus.write(0x1000, 0x11); + bus.write(0x1001, 0x22); + bus.write(0x1002, 0x44); + bus.write(0x1003, 0x88); + + bus.loadProgram(0x12, 0x30, // ORA ($30) + 0x12, 0x40, // ORA ($40) + 0x12, 0x50, // ORA ($50) + 0x12, 0x60); // ORA ($60) + + + assertEquals(0x00, cpu.getAccumulator()); + + boolean zState = cpu.getZeroFlag(); + boolean nState = cpu.getNegativeFlag(); + + // 0x00 | 0x11 = 0x11, but not implemented in NMOS cpu + cpu.step(); + assertEquals(0x00, cpu.getAccumulator()); + assertEquals(zState, cpu.getZeroFlag()); // unchanged + assertEquals(nState, cpu.getNegativeFlag()); // unchanged + + // 0x11 | 0x22 = 0x33, but not implemented in NMOS cpu + cpu.step(); + assertEquals(0x00, cpu.getAccumulator()); + assertEquals(zState, cpu.getZeroFlag()); // unchanged + assertEquals(nState, cpu.getNegativeFlag()); // unchanged + + // 0x33 | 0x44 = 0x77, but not implemented in NMOS cpu + cpu.step(); + assertEquals(0x00, cpu.getAccumulator()); + assertEquals(zState, cpu.getZeroFlag()); // unchanged + assertEquals(nState, cpu.getNegativeFlag()); // unchanged + + // 0x77 | 0x88 = 0xff, but not implemented in NMOS cpu + cpu.step(); + assertEquals(0x00, cpu.getAccumulator()); + assertEquals(zState, cpu.getZeroFlag()); // unchanged + assertEquals(nState, cpu.getNegativeFlag()); // unchanged + } + + @Test + public void test_and() throws Exception { + makeCmosCpu(); + + // Set some initial values in zero page. + bus.write(0x30, 0x00); + bus.write(0x31, 0x10); + + bus.write(0x40, 0x01); + bus.write(0x41, 0x10); + + bus.write(0x50, 0x02); + bus.write(0x51, 0x10); + + bus.write(0x1000, 0x33); + bus.write(0x1001, 0x11); + bus.write(0x1002, 0x88); + + bus.loadProgram(0x32, 0x30, // AND ($30) + 0x32, 0x40, // AND ($40) + 0x32, 0x50); // AND ($50) + + cpu.setAccumulator(0xff); + + // 0xFF & 0x33 == 0x33 + cpu.step(); + assertEquals(0x33, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // 0x33 & 0x11 == 0x11 + cpu.step(); + assertEquals(0x11, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // 0x11 & 0x80 == 0 + cpu.step(); + assertEquals(0, cpu.getAccumulator()); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + } + + @Test + public void test_and_requiresCmosCpu() throws Exception { + makeNmosCpu(); + + // Set some initial values in zero page. + bus.write(0x30, 0x00); + bus.write(0x31, 0x10); + + bus.write(0x40, 0x01); + bus.write(0x41, 0x10); + + bus.write(0x50, 0x02); + bus.write(0x51, 0x10); + + bus.write(0x1000, 0x33); + bus.write(0x1001, 0x11); + bus.write(0x1002, 0x88); + + bus.loadProgram(0x32, 0x30, // AND ($30) + 0x32, 0x40, // AND ($40) + 0x32, 0x50); // AND ($50) + + + cpu.setAccumulator(0xff); + + boolean zState = cpu.getZeroFlag(); + boolean nState = cpu.getNegativeFlag(); + + // 0xFF & 0x33 == 0x33, but not implemented in NMOS cpu + cpu.step(); + assertEquals(0xff, cpu.getAccumulator()); + assertEquals(zState, cpu.getZeroFlag()); + assertEquals(nState, cpu.getNegativeFlag()); + + // 0x33 & 0x11 == 0x11, but not implemented in NMOS cpu + cpu.step(); + assertEquals(0xff, cpu.getAccumulator()); + assertEquals(zState, cpu.getZeroFlag()); + assertEquals(nState, cpu.getNegativeFlag()); + + // 0x11 & 0x80 == 0, but not implemented in NMOS cpu + cpu.step(); + assertEquals(0xff, cpu.getAccumulator()); + assertEquals(zState, cpu.getZeroFlag()); + assertEquals(nState, cpu.getNegativeFlag()); + } + + @Test + public void test_eor() throws Exception { + makeCmosCpu(); + + // Set some initial values in zero page. + bus.write(0x30, 0x00); + bus.write(0x31, 0x10); + + bus.write(0x40, 0x01); + bus.write(0x41, 0x10); + + bus.write(0x50, 0x02); + bus.write(0x51, 0x10); + + bus.write(0x60, 0x03); + bus.write(0x61, 0x10); + + bus.write(0x1000, 0x00); + bus.write(0x1001, 0xff); + bus.write(0x1002, 0x33); + bus.write(0x1003, 0x44); + + bus.loadProgram(0x52, 0x30, // AND ($30) + 0x52, 0x40, // AND ($40) + 0x52, 0x50, // EOR ($50) + 0x52, 0x60); // AND ($60) + + + cpu.setAccumulator(0x88); + + cpu.step(); + assertEquals(0x88, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x77, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x44, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x00, cpu.getAccumulator()); + } + + @Test + public void test_eor_requiresCmosCpu() throws Exception { + makeNmosCpu(); + + // Set some initial values in zero page. + bus.write(0x30, 0x00); + bus.write(0x31, 0x10); + + bus.write(0x40, 0x01); + bus.write(0x41, 0x10); + + bus.write(0x50, 0x02); + bus.write(0x51, 0x10); + + bus.write(0x60, 0x03); + bus.write(0x61, 0x10); + + bus.write(0x1000, 0x00); + bus.write(0x1001, 0xff); + bus.write(0x1002, 0x33); + bus.write(0x1003, 0x44); + + bus.loadProgram(0x52, 0x30, // AND ($30) + 0x52, 0x40, // AND ($40) + 0x52, 0x50, // EOR ($50) + 0x52, 0x60); // AND ($60) + + + cpu.setAccumulator(0x88); + + cpu.step(); + assertEquals(0x88, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x88, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x88, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x88, cpu.getAccumulator()); + } +}