From faf5d22660e51e9d4a112b32e62fabc5c699fea2 Mon Sep 17 00:00:00 2001 From: Matt Harlum Date: Tue, 6 Jun 2017 12:15:23 +1000 Subject: [PATCH 1/2] * Add ACIA Interrupt tests. * Fix ACIA6850 Interrupt behavior, Interrupt should be cleared on status register read. * Remove unneeded cpuAccess if statement from Acia6850 write that was preventing build completion * Fix ACIA6850 Tests so they run. --- .../com/loomcom/symon/devices/Acia6850.java | 14 ++- .../{AciaTest6850.java => Acia6850Test.java} | 98 ++++++++++++++++++- src/test/java/com/loomcom/symon/AciaTest.java | 74 +++++++++++++- 3 files changed, 173 insertions(+), 13 deletions(-) rename src/test/java/com/loomcom/symon/{AciaTest6850.java => Acia6850Test.java} (63%) diff --git a/src/main/java/com/loomcom/symon/devices/Acia6850.java b/src/main/java/com/loomcom/symon/devices/Acia6850.java index bd70eed..e957bb4 100644 --- a/src/main/java/com/loomcom/symon/devices/Acia6850.java +++ b/src/main/java/com/loomcom/symon/devices/Acia6850.java @@ -30,8 +30,8 @@ import com.loomcom.symon.exceptions.MemoryRangeException; /** * This is a simulation of the Motorola 6850 ACIA, with limited - * functionality. Interrupts are not supported. - *

+ * functionality. + * * Unlike a 16550 UART, the 6850 ACIA has only one-byte transmit and * receive buffers. It is the programmer's responsibility to check the * status (full or empty) for transmit and receive buffers before @@ -56,9 +56,6 @@ public class Acia6850 extends Acia { public int read(int address, boolean cpuAccess) throws MemoryAccessException { switch (address) { case RX_REG: - if (cpuAccess) { - interrupt = false; - } return rxRead(cpuAccess); case STAT_REG: return statusReg(cpuAccess); @@ -72,9 +69,6 @@ public class Acia6850 extends Acia { public void write(int address, int data) throws MemoryAccessException { switch (address) { case TX_REG: - if (cpuAccess) { - interrupt = false; - } txWrite(data); break; case CTRL_REG: @@ -119,6 +113,10 @@ public class Acia6850 extends Acia { stat |= 0x80; } + if (cpuAccess) { + interrupt = false; + } + return stat; } diff --git a/src/test/java/com/loomcom/symon/AciaTest6850.java b/src/test/java/com/loomcom/symon/Acia6850Test.java similarity index 63% rename from src/test/java/com/loomcom/symon/AciaTest6850.java rename to src/test/java/com/loomcom/symon/Acia6850Test.java index 0f7425c..e37539d 100644 --- a/src/test/java/com/loomcom/symon/AciaTest6850.java +++ b/src/test/java/com/loomcom/symon/Acia6850Test.java @@ -7,7 +7,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; -public class AciaTest6850 { +public class Acia6850Test { private final static int CMD_STAT_REG = 0; private final static int DATA_REG = 1; @@ -91,6 +91,78 @@ public class AciaTest6850 { verify(mockBus, never()).assertIrq(); } + @Test + public void shouldTriggerInterruptFlagOnRxFullIfRxIrqEnabled() throws Exception { + Bus mockBus = mock(Bus.class); + + Acia acia = newAcia(); + acia.setBus(mockBus); + + // Disable TX IRQ, Enable RX IRQ + acia.write(CMD_STAT_REG, 0x80); + + acia.rxWrite('a'); + + // Receive should cause IRQ flag to be set + assertEquals(0x80, acia.read(0x0000, true) & 0x80); + } + + @Test + public void shouldNotTriggerInterruptFlagOnRxFullIfRxIrqNotEnabled() throws Exception { + Bus mockBus = mock(Bus.class); + + Acia acia = newAcia(); + acia.setBus(mockBus); + + // Disable TX IRQ, Disable RX IRQ + acia.write(CMD_STAT_REG, 0x00); + + acia.rxWrite('a'); + + // Receive should not cause IRQ flag to be set + assertEquals(0x00, acia.read(0x0000, true) & 0x80); + } + + @Test + public void shouldTriggerInterruptFlagOnTxEmptyIfTxIrqEnabled() throws Exception { + Bus mockBus = mock(Bus.class); + + Acia acia = newAcia(); + acia.setBus(mockBus); + + // Enable TX IRQ, Disable RX IRQ + acia.write(CMD_STAT_REG, 0x20); + + // Write data + acia.write(1, 'a'); + + verify(mockBus, never()).assertIrq(); + + // Transmission should cause IRQ flag to be set + acia.txRead(true); + + assertEquals(0x80, acia.read(0x0000, true) & 0x80); + } + + @Test + public void shouldNotTriggerInterruptFlagOnTxEmptyIfTxIrqNotEnabled() throws Exception { + Bus mockBus = mock(Bus.class); + + Acia acia = newAcia(); + acia.setBus(mockBus); + + // Disable TX IRQ, Disable RX IRQ + acia.write(CMD_STAT_REG, 0x02); + + // Write data + acia.write(DATA_REG, 'a'); + + // Transmission should not cause IRQ flag to be set + acia.txRead(true); + + assertEquals(0x00, acia.read(0x0000, true) & 0x80); + } + @Test public void newAciaShouldHaveTxEmptyStatus() throws Exception { Acia acia = newAcia(); @@ -129,6 +201,24 @@ public class AciaTest6850 { @Test public void aciaShouldOverrunAndReadShouldReset() throws Exception { + + Acia acia = newAcia(); + + // overrun ACIA + acia.rxWrite('a'); + acia.rxWrite('b'); + + assertEquals(0x20, acia.read(CMD_STAT_REG, true) & 0x20); + + // read should reset + acia.rxRead(true); + assertEquals(0x00, acia.read(CMD_STAT_REG, true) & 0x20); + + } + + @Test + public void aciaShouldOverrunAndMemoryWindowReadShouldNotReset() + throws Exception { Acia acia = newAcia(); @@ -138,9 +228,9 @@ public class AciaTest6850 { assertEquals(0x20, acia.read(CMD_STAT_REG, true) & 0x20); - // read should reset - acia.rxRead(true); - assertEquals(0x00, acia.read(CMD_STAT_REG, true) & 0x20); + // memory window read should not reset + acia.rxRead(false); + assertEquals(0x20, acia.read(CMD_STAT_REG, true) & 0x20); } diff --git a/src/test/java/com/loomcom/symon/AciaTest.java b/src/test/java/com/loomcom/symon/AciaTest.java index 4e5bb1f..ba29b27 100644 --- a/src/test/java/com/loomcom/symon/AciaTest.java +++ b/src/test/java/com/loomcom/symon/AciaTest.java @@ -73,12 +73,84 @@ public class AciaTest { // Write data acia.write(0, 'a'); - // Transmission should cause IRQ + // Transmission should not cause IRQ acia.txRead(true); verify(mockBus, never()).assertIrq(); } + @Test + public void shouldTriggerInterruptFlagOnRxFullIfRxIrqEnabled() throws Exception { + Bus mockBus = mock(Bus.class); + + Acia acia = new Acia6551(0x000); + acia.setBus(mockBus); + + // Disable TX IRQ, Enable RX IRQ + acia.write(2, 0x00); + + acia.rxWrite('a'); + + // Receive should cause IRQ flag to be set + assertEquals(0x80, acia.read(0x0001, true) & 0x80); + } + + @Test + public void shouldNotTriggerInterruptFlagOnRxFullIfRxIrqNotEnabled() throws Exception { + Bus mockBus = mock(Bus.class); + + Acia acia = new Acia6551(0x000); + acia.setBus(mockBus); + + // Disable TX IRQ, Disable RX IRQ + acia.write(2, 0x02); + + acia.rxWrite('a'); + + // Receive should not cause IRQ flag to be set + assertEquals(0x00, acia.read(0x0001, true) & 0x80); + } + + @Test + public void shouldTriggerInterruptFlagOnTxEmptyIfTxIrqEnabled() throws Exception { + Bus mockBus = mock(Bus.class); + + Acia acia = new Acia6551(0x000); + acia.setBus(mockBus); + + // Enable TX IRQ, Disable RX IRQ + acia.write(2, 0x06); + + // Write data + acia.write(0, 'a'); + + verify(mockBus, never()).assertIrq(); + + // Transmission should cause IRQ flag to be set + acia.txRead(true); + + assertEquals(0x80, acia.read(0x0001, true) & 0x80); + } + + @Test + public void shouldNotTriggerInterruptFlagOnTxEmptyIfTxIrqNotEnabled() throws Exception { + Bus mockBus = mock(Bus.class); + + Acia acia = new Acia6551(0x000); + acia.setBus(mockBus); + + // Disable TX IRQ, Disable RX IRQ + acia.write(2, 0x02); + + // Write data + acia.write(0, 'a'); + + // Transmission should not cause IRQ flag to be set + acia.txRead(true); + + assertEquals(0x00, acia.read(0x0001, true) & 0x80); + } + @Test public void newAciaShouldHaveTxEmptyStatus() throws Exception { Acia acia = new Acia6551(0x000); From a9c6d5964f33d560b27f849fa397e13331c58831 Mon Sep 17 00:00:00 2001 From: Matt Harlum Date: Tue, 6 Jun 2017 13:24:29 +1000 Subject: [PATCH 2/2] * Add Support for All 65C02 Opcodes and all Rockwell/WDC opcodes except WAI/STP * Add 65C02 Opcode tests * All tests pass, Klaus' 6502_functional_tests pass & Klaus' 65C02_extended_opcodes_test also all pass --- src/main/java/com/loomcom/symon/Cpu.java | 573 ++++++++++++++++- .../com/loomcom/symon/InstructionTable.java | 302 +++++---- .../java/com/loomcom/symon/Simulator.java | 34 + .../symon/Cpu65C02AbsoluteModeTest.java | 125 ++++ .../symon/Cpu65C02AbsoluteXModeTest.java | 87 +++ .../symon/Cpu65C02ImmediateModeTest.java | 72 +++ .../symon/Cpu65C02ImpliedModeTest.java | 252 ++++++++ .../symon/Cpu65C02ZeroPageModeTest.java | 372 +++++++++++ .../symon/Cpu65C02ZeroPageRelativeTest.java | 585 ++++++++++++++++++ .../symon/Cpu65C02ZeroPageXModeTest.java | 69 +++ .../loomcom/symon/CpuIndirectModeTest.java | 6 +- .../loomcom/symon/CpuIndirectXModeTest.java | 2 +- .../symon/CpuZeroPageIndirectTest.java | 329 ++++++++++ 13 files changed, 2655 insertions(+), 153 deletions(-) create mode 100644 src/test/java/com/loomcom/symon/Cpu65C02AbsoluteModeTest.java create mode 100644 src/test/java/com/loomcom/symon/Cpu65C02AbsoluteXModeTest.java create mode 100644 src/test/java/com/loomcom/symon/Cpu65C02ImmediateModeTest.java create mode 100644 src/test/java/com/loomcom/symon/Cpu65C02ImpliedModeTest.java create mode 100644 src/test/java/com/loomcom/symon/Cpu65C02ZeroPageModeTest.java create mode 100644 src/test/java/com/loomcom/symon/Cpu65C02ZeroPageRelativeTest.java create mode 100644 src/test/java/com/loomcom/symon/Cpu65C02ZeroPageXModeTest.java 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 4e19015..ce38e27 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) { @@ -104,6 +104,10 @@ public class Cpu implements InstructionTable { this.behavior = behavior; } + public CpuBehavior getBehavior() { + return behavior; + } + /** * Reset the CPU to known initial values. */ @@ -204,15 +208,28 @@ 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], true), + bus.read((state.args[0] + 1) & 0xff, true)); + } + break; case 5: // Zero Page,X / Zero Page,Y - if (state.ir == 0x96 || state.ir == 0xb6) { + if (state.ir == 0x14) { // 65C02 TRB Zero Page + effectiveAddress = state.args[0]; + } + else if (state.ir == 0x96 || state.ir == 0xb6) { effectiveAddress = zpyAddress(state.args[0]); } else { effectiveAddress = zpxAddress(state.args[0]); } break; - case 7: // Absolute,X / Absolute,Y - if (state.ir == 0xbe) { + case 7: + if (state.ir == 0x9c || state.ir == 0x1c) { // 65C02 STZ & TRB Absolute + effectiveAddress = Utils.address(state.args[0], state.args[1]); + } + else if (state.ir == 0xbe) { // Absolute,X / Absolute,Y effectiveAddress = yAddress(state.args[0], state.args[1]); } else { effectiveAddress = xAddress(state.args[0], state.args[1]); @@ -220,6 +237,16 @@ public class Cpu implements InstructionTable { break; } break; + case 3: // Rockwell/WDC 65C02 + switch (irAddressMode) { + case 1: // Zero Page + case 3: + case 5: + case 7: // Zero Page, Relative + effectiveAddress = state.args[0]; + break; + } + break; case 1: switch (irAddressMode) { case 0: // (Zero Page,X) @@ -262,7 +289,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()) { @@ -305,6 +332,13 @@ public class Cpu implements InstructionTable { case 0x58: // CLI - Clear Interrupt Disable - Implied clearIrqDisableFlag(); break; + case 0x5a: // 65C02 PHY - Push Y to stack + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + stackPush(state.y); + break; case 0x60: // RTS - Return from Subroutine - Implied lo = stackPop(); hi = stackPop(); @@ -322,6 +356,20 @@ public class Cpu implements InstructionTable { case 0x78: // SEI - Set Interrupt Disable - Implied setIrqDisableFlag(); break; + case 0x7a: // 65C02 PLY - Pull Y from Stack + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + state.y = stackPop(); + setArithmeticFlags(state.y); + break; + case 0x80: // 65C02 BRA - Branch Always + if (behavior == CpuBehavior.CMOS_6502 || + behavior == CpuBehavior.CMOS_65816) { + state.pc = relAddress(state.args[0]); + } + break; case 0x88: // DEY - Decrement Y Register - Implied state.y = --state.y & 0xff; setArithmeticFlags(state.y); @@ -378,6 +426,13 @@ public class Cpu implements InstructionTable { case 0xd8: // CLD - Clear Decimal Mode - Implied clearDecimalModeFlag(); break; + case 0xda: // 65C02 PHX - Push X to stack + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + stackPush(state.x); + break; case 0xe8: // INX - Increment X Register - Implied state.x = ++state.x & 0xff; setArithmeticFlags(state.x); @@ -393,6 +448,14 @@ public class Cpu implements InstructionTable { case 0xf8: // SED - Set Decimal Flag - Implied setDecimalModeFlag(); break; + case 0xfa: // 65C02 PLX - Pull X from Stack + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + state.x = stackPop(); + setArithmeticFlags(state.x); + break; /** JMP *****************************************************************/ case 0x4c: // JMP - Absolute @@ -402,7 +465,7 @@ public class Cpu implements InstructionTable { 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 +485,26 @@ public class Cpu implements InstructionTable { * (http://www.obelisk.demon.co.uk/6502/reference.html#JMP) */ break; - + case 0x7c: // 65C02 JMP - (Absolute Indexed Indirect,X) + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + lo = (((state.args[1] << 8) | state.args[0]) + state.x) & 0xffff; + hi = lo + 1; + state.pc = Utils.address(bus.read(lo, true), bus.read(hi, true)); + 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 @@ -457,8 +533,17 @@ public class Cpu implements InstructionTable { /** BIT - Bit Test ******************************************************/ + case 0x89: // 65C02 #Immediate + setZeroFlag((state.a & state.args[0]) == 0); + break; + case 0x34: // 65C02 Zero Page,X + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } case 0x24: // Zero Page case 0x2c: // Absolute + case 0x3c: // Absolute,X tmp = bus.read(effectiveAddress, true); setZeroFlag((state.a & tmp) == 0); setNegativeFlag((tmp & 0x80) != 0); @@ -471,6 +556,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 +593,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 +633,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 +669,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 @@ -595,6 +700,17 @@ public class Cpu implements InstructionTable { bus.write(effectiveAddress, state.x); break; + /** STZ - 65C02 Store Zero ****************************************************/ + case 0x64: // Zero Page + case 0x74: // Zero Page,X + case 0x9c: // Absolute + case 0x9e: // Absolute,X + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + bus.write(effectiveAddress, 0); + break; /** LDY - Load Y Register ***********************************************/ case 0xa0: // #Immediate @@ -629,6 +745,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 +776,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 @@ -667,6 +793,14 @@ public class Cpu implements InstructionTable { /** DEC - Decrement Memory **********************************************/ + case 0x3a: // 65C02 Immediate + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + state.a = --state.a & 0xFF; + setArithmeticFlags(state.a); + break; case 0xc6: // Zero Page case 0xce: // Absolute case 0xd6: // Zero Page,X @@ -695,6 +829,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 @@ -711,6 +850,14 @@ public class Cpu implements InstructionTable { /** INC - Increment Memory **********************************************/ + case 0x1a: // 65C02 Increment Immediate + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + state.a = ++state.a & 0xff; + setArithmeticFlags(state.a); + break; case 0xe6: // Zero Page case 0xee: // Absolute case 0xf6: // Zero Page,X @@ -720,6 +867,364 @@ public class Cpu implements InstructionTable { setArithmeticFlags(tmp); break; + + /** 65C02 RMB - Reset Memory Bit **************************************/ + case 0x07: // 65C02 RMB0 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp &= ~(1 << 0); + bus.write(effectiveAddress, tmp); + break; + case 0x17: // 65C02 RMB1 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp &= ~(1 << 1); + bus.write(effectiveAddress, tmp); + break; + case 0x27: // 65C02 RMB2 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp &= ~(1 << 2); + bus.write(effectiveAddress, tmp); + break; + case 0x37: // 65C02 RMB3 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp &= ~(1 << 3); + bus.write(effectiveAddress, tmp); + break; + case 0x47: // 65C02 RMB4 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp &= ~(1 << 4); + bus.write(effectiveAddress, tmp); + break; + case 0x57: // 65C02 RMB5 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp &= ~(1 << 5); + bus.write(effectiveAddress, tmp); + break; + case 0x67: // 65C02 RMB6 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp &= ~(1 << 6); + bus.write(effectiveAddress, tmp); + break; + case 0x77: // 65C02 RMB7 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp &= ~(1 << 7); + bus.write(effectiveAddress, tmp); + break; + + + /** 65C02 SMB - Set Memory Bit **************************************/ + case 0x87: // 65C02 SMB0 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp |= (1); + bus.write(effectiveAddress, tmp); + break; + case 0x97: // 65C02 SMB1 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp |= (1 << 1); + bus.write(effectiveAddress, tmp); + break; + case 0xa7: // 65C02 SMB2 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp |= (1 << 2); + bus.write(effectiveAddress, tmp); + break; + case 0xb7: // 65C02 SMB3 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp |= (1 << 3); + bus.write(effectiveAddress, tmp); + break; + case 0xc7: // 65C02 SMB4 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp |= (1 << 4); + bus.write(effectiveAddress, tmp); + break; + case 0xd7: // 65C02 SMB5 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp |= (1 << 5); + bus.write(effectiveAddress, tmp); + break; + case 0xe7: // 65C02 SMB6 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp |= (1 << 6); + bus.write(effectiveAddress, tmp); + break; + case 0xf7: // 65C02 SMB7 - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true) & 0xff; + tmp |= (1 << 7); + bus.write(effectiveAddress, tmp); + break; + + /** 65C02 TRB/TSB - Test and Reset Bit/Test and Set Bit ***************/ + case 0x14: // 65C02 TRB - Test and Reset bit - Zero Page + case 0x1c: // 65C02 TRB - Test and Reset bit - Absolute + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + setZeroFlag((state.a & tmp) == 0); + tmp = (tmp &= ~(state.a)) & 0xff; + bus.write(effectiveAddress,tmp); + break; + + case 0x04: // 65C02 TSB - Test and Set bit - Zero Page + case 0x0c: // 65C02 TSB - Test and Set bit - Absolute + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + setZeroFlag((state.a & tmp) == 0); + tmp = (tmp |= (state.a)) & 0xff; + bus.write(effectiveAddress,tmp); + break; + + /** 65C02 BBR - Branch if Bit Reset *************************************/ + case 0x0f: // 65C02 BBR - Branch if bit 0 reset - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 0) == 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0x1f: // 65C02 BBR - Branch if bit 1 reset - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 1) == 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0x2f: // 65C02 BBR - Branch if bit 2 reset - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 2) == 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0x3f: // 65C02 BBR - Branch if bit 3 reset - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 3) == 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0x4f: // 65C02 BBR - Branch if bit 4 reset - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 4) == 0) { + state.pc = relAddress(state.args[1]); + } + break; + + + case 0x5f: // 65C02 BBR - Branch if bit 5 reset - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 5) == 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0x6f: // 65C02 BBR - Branch if bit 6 reset - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 6) == 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0x7f: // 65C02 BBR - Branch if bit 5 reset - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 7) == 0) { + state.pc = relAddress(state.args[1]); + } + break; + + + /** 65C02 BBS - Branch if Bit Set ************************************/ + case 0x8f: // 65C02 BBS - Branch if bit 0 set - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 0) > 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0x9f: // 65C02 BBS - Branch if bit 1 set - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 1) > 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0xaf: // 65C02 BBS - Branch if bit 2 set - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 2) > 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0xbf: // 65C02 BBS - Branch if bit 3 set - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 3) > 0) { + state.pc = relAddress(state.args[1]); + } + break; + + + case 0xcf: // 65C02 BBS - Branch if bit 4 set - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 4) > 0) { + state.pc = relAddress(state.args[1]); + } + break; + + + case 0xdf: // 65C02 BBS - Branch if bit 5 set - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 5) > 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0xef: // 65C02 BBS - Branch if bit 6 set - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 6) > 0) { + state.pc = relAddress(state.args[1]); + } + break; + + case 0xff: // 65C02 BBS - Branch if bit 5 set - Zero Page + if (behavior == CpuBehavior.NMOS_6502 || + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + break; + } + tmp = bus.read(effectiveAddress, true); + if ((tmp & 1 << 7) > 0) { + state.pc = relAddress(state.args[1]); + } + break; + + /** Unimplemented Instructions ****************************************/ // TODO: Create a flag to enable highly-accurate emulation of unimplemented instructions. default: @@ -782,6 +1287,12 @@ public class Cpu implements InstructionTable { // Set the Interrupt Disabled flag. RTI will clear it. setIrqDisableFlag(); + // 65C02 & 65816 clear Decimal flag after pushing Processor status to the stack + if (behavior == CpuBehavior.CMOS_6502|| + behavior == CpuBehavior.CMOS_65816) { + clearDecimalModeFlag(); + } + // Load interrupt vector address into PC state.pc = Utils.address(bus.read(vectorLow, true), bus.read(vectorHigh, true)); } @@ -819,8 +1330,15 @@ public class Cpu implements InstructionTable { result &= 0xff; setCarryFlag(h > 15); setZeroFlag(result == 0); - setNegativeFlag(false); // BCD is never negative setOverflowFlag(false); // BCD never sets overflow flag + + if (behavior == CpuBehavior.NMOS_6502|| + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + setNegativeFlag(false); // BCD is never negative on NMOS 6502 + } + else { + state.negativeFlag = (result & 0x80) != 0; // N Flag is valid on CMOS 6502/65816 + } return result; } @@ -845,11 +1363,18 @@ public class Cpu implements InstructionTable { if ((l & 0x10) != 0) l -= 6; h = (acc >> 4) - (operand >> 4) - ((l & 0x10) != 0 ? 1 : 0); if ((h & 0x10) != 0) h -= 6; - result = (l & 0x0f) | (h << 4); + result = (l & 0x0f) | (h << 4) & 0xff; setCarryFlag((h & 0xff) < 15); setZeroFlag(result == 0); - setNegativeFlag(false); // BCD is never negative setOverflowFlag(false); // BCD never sets overflow flag + + if (behavior == CpuBehavior.NMOS_6502|| + behavior == CpuBehavior.NMOS_WITH_ROR_BUG) { + setNegativeFlag(false); // BCD is never negative on NMOS 6502 + } + else { + state.negativeFlag = (result & 0x80) != 0; // N Flag is valid on CMOS 6502/65816 + } return (result & 0xff); } @@ -1334,6 +1859,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 +1874,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; } @@ -1387,6 +1919,8 @@ public class Cpu implements InstructionTable { case ABS: sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))); break; + case AIX: + sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X)"); case ABX: sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X"); break; @@ -1406,6 +1940,7 @@ public class Cpu implements InstructionTable { sb.append(" ($").append(Utils.byteToHex(args[0])).append("),Y"); break; case REL: + case ZPR: case ZPG: sb.append(" $").append(Utils.byteToHex(args[0])); break; diff --git a/src/main/java/com/loomcom/symon/InstructionTable.java b/src/main/java/com/loomcom/symon/InstructionTable.java index 4f46c4a..706c2bf 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 } /** @@ -71,7 +68,11 @@ public interface InstructionTable { return "Accumulator"; } }, - + AIX { + public String toString() { + return "Absolute, X-Indexed Indirect"; + } + }, ABS { public String toString() { return "Absolute"; @@ -128,19 +129,31 @@ public interface InstructionTable { ZPG { public String toString() { - return "Zeropage"; + return "Zero Page"; + } + }, + + ZPR { + public String toString() { + return "Zero Page, Relative"; } }, 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,156 +167,185 @@ 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", "NOP", "NOP", "TSB", "ORA", "ASL", "RMB0", // 0x00-0x07 + "PHP", "ORA", "ASL", "NOP", "TSB", "ORA", "ASL", "BBR0", // 0x08-0x0f + "BPL", "ORA", "ORA", "NOP", "TRB", "ORA", "ASL", "RMB1", // 0x10-0x17 + "CLC", "ORA", "INC", "NOP", "TRB", "ORA", "ASL", "BBR1", // 0x18-0x1f + "JSR", "AND", "NOP", "NOP", "BIT", "AND", "ROL", "RMB2", // 0x20-0x27 + "PLP", "AND", "ROL", "NOP", "BIT", "AND", "ROL", "BBR2", // 0x28-0x2f + "BMI", "AND", "AND", "NOP", "BIT", "AND", "ROL", "RMB3", // 0x30-0x37 + "SEC", "AND", "DEC", "NOP", "BIT", "AND", "ROL", "BBR3", // 0x38-0x3f + "RTI", "EOR", "NOP", "NOP", "NOP", "EOR", "LSR", "RMB4", // 0x40-0x47 + "PHA", "EOR", "LSR", "NOP", "JMP", "EOR", "LSR", "BBR4", // 0x48-0x4f + "BVC", "EOR", "EOR", "NOP", "NOP", "EOR", "LSR", "RMB5", // 0x50-0x57 + "CLI", "EOR", "PHY", "NOP", "NOP", "EOR", "LSR", "BBR5", // 0x58-0x5f + "RTS", "ADC", "NOP", "NOP", "STZ", "ADC", "ROR", "RMB6", // 0x60-0x67 + "PLA", "ADC", "ROR", "NOP", "JMP", "ADC", "ROR", "BBR6", // 0x68-0x6f + "BVS", "ADC", "ADC", "NOP", "STZ", "ADC", "ROR", "RMB7", // 0x70-0x77 + "SEI", "ADC", "PLY", "NOP", "JMP", "ADC", "ROR", "BBR7", // 0x78-0x7f + "BRA", "STA", "NOP", "NOP", "STY", "STA", "STX", "SMB0", // 0x80-0x87 + "DEY", "BIT", "TXA", "NOP", "STY", "STA", "STX", "BBS0", // 0x88-0x8f + "BCC", "STA", "STA", "NOP", "STY", "STA", "STX", "SMB1", // 0x90-0x97 + "TYA", "STA", "TXS", "NOP", "STZ", "STA", "STZ", "BBS1", // 0x98-0x9f + "LDY", "LDA", "LDX", "NOP", "LDY", "LDA", "LDX", "SMB2", // 0xa0-0xa7 + "TAY", "LDA", "TAX", "NOP", "LDY", "LDA", "LDX", "BBS2", // 0xa8-0xaf + "BCS", "LDA", "LDA", "NOP", "LDY", "LDA", "LDX", "SMB3", // 0xb0-0xb7 + "CLV", "LDA", "TSX", "NOP", "LDY", "LDA", "LDX", "BBS3", // 0xb8-0xbf + "CPY", "CMP", "NOP", "NOP", "CPY", "CMP", "DEC", "SMB4", // 0xc0-0xc7 + "INY", "CMP", "DEX", "NOP", "CPY", "CMP", "DEC", "BBS4", // 0xc8-0xcf + "BNE", "CMP", "CMP", "NOP", "NOP", "CMP", "DEC", "SMB5", // 0xd0-0xd7 + "CLD", "CMP", "PHX", "NOP", "NOP", "CMP", "DEC", "BBS5", // 0xd8-0xdf + "CPX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "SMB6", // 0xe0-0xe7 + "INX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "BBS6", // 0xe8-0xef + "BEQ", "SBC", "SBC", "NOP", "NOP", "SBC", "INC", "SMB7", // 0xf0-0xf7 + "SED", "SBC", "PLX", "NOP", "NOP", "SBC", "INC", "BBS7" // 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.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 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.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 + Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x0c-0x0f + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x10-0x13 + Mode.ZPG, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0x14-0x17 + Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x18-0x1b + Mode.ABS, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x1c-0x1f Mode.ABS, Mode.XIN, Mode.NUL, Mode.NUL, // 0x20-0x23 - Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27 + Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 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.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 + Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x2c-0x2f + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x30-0x33 + Mode.ZPX, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0x34-0x37 + Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x38-0x3b + Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x3c-0x3f Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x40-0x43 - Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47 + Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 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.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 + Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x4c-0x4f + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x50-0x53 + Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0x54-0x57 + Mode.IMP, Mode.ABY, Mode.IMM, Mode.NUL, // 0x58-0x5b + Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x5c-0x5f Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x60-0x63 - Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67 + Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 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.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.IND, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x6c-0x6f + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x70-0x73 + Mode.ZPX, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0x74-0x77 + Mode.IMP, Mode.ABY, Mode.IMM, Mode.NUL, // 0x78-0x7b + Mode.AIX, Mode.ABX, Mode.ABX, Mode.ZPR, // 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.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97 + Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0x84-0x87 + Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0x88-0x8b + Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x8c-0x8f + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x90-0x93 + Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.ZPG, // 0x94-0x97 Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b - Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f + Mode.ABS, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x9c-0x9f Mode.IMM, Mode.XIN, Mode.IMM, Mode.NUL, // 0xa0-0xa3 - Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7 + Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 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.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7 + Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0xac-0xaf + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xb0-0xb3 + Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.ZPG, // 0xb4-0xb7 Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xb8-0xbb - Mode.ABX, Mode.ABX, Mode.ABY, Mode.NUL, // 0xbc-0xbf + Mode.ABX, Mode.ABX, Mode.ABY, Mode.ZPR, // 0xbc-0xbf Mode.IMM, Mode.XIN, Mode.NUL, Mode.NUL, // 0xc0-0xc3 - Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7 + Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 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.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 + Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0xcc-0xcf + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xd0-0xd3 + Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0xd4-0xd7 + Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xd8-0xdb + Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR, // 0xdc-0xdf Mode.IMM, Mode.XIN, Mode.NUL, Mode.NUL, // 0xe0-0xe3 - Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7 + Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 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.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 + Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0xec-0xef + Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xf0-0xf3 + Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0xf4-0xf7 + Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xf8-0xfb + Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR // 0xfc-0xff }; /** - * 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, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x00-0x0f + 2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x10-0x1f + 3, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x20-0x2f + 2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x30-0x3f + 1, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x40-0x4f + 2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x50-0x5f + 1, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x60-0x6f + 2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x70-0x7f + 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x80-0x8f + 2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x90-0x9f + 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0xa0-0xaf + 2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0xb0-0xbf + 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0xc0-0xcf + 2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0xd0-0xdf + 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0xe0-0xef + 2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3 // 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, 2, 1, 5, 3, 5, 5, 3, 2, 2, 1, 6, 4, 6, 5, // 0x00-0x0f + 2, 5, 5, 1, 5, 4, 6, 5, 2, 4, 2, 1, 6, 4, 6, 5, // 0x10-0x1f + 6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 4, 4, 6, 5, // 0x20-0x2f + 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 2, 1, 4, 4, 6, 5, // 0x30-0x3f + 6, 6, 2, 1, 2, 3, 5, 3, 3, 2, 2, 1, 3, 4, 6, 5, // 0x40-0x4f + 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 1, 8, 4, 6, 5, // 0x50-0x5f + 6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 6, 4, 6, 5, // 0x60-0x6f + 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 3, 6, 4, 6, 5, // 0x70-0x7f + 3, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 5, // 0x80-0x8f + 2, 6, 5, 1, 4, 4, 4, 5, 2, 5, 2, 1, 4, 5, 5, 5, // 0x90-0x9f + 2, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 5, // 0xa0-0xaf + 2, 5, 5, 1, 4, 4, 4, 5, 2, 4, 2, 1, 4, 4, 4, 5, // 0xb0-0xbf + 2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 3, 4, 4, 6, 5, // 0xc0-0xcf + 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 3, 4, 4, 7, 5, // 0xd0-0xdf + 2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 1, 4, 4, 6, 5, // 0xe0-0xef + 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 1, 4, 4, 7, 5 // 0xf0-0xff }; } diff --git a/src/main/java/com/loomcom/symon/Simulator.java b/src/main/java/com/loomcom/symon/Simulator.java index e3de41f..ab14bf3 100644 --- a/src/main/java/com/loomcom/symon/Simulator.java +++ b/src/main/java/com/loomcom/symon/Simulator.java @@ -678,6 +678,21 @@ public class Simulator { } } + class SetCpuAction extends AbstractAction { + private Cpu.CpuBehavior behavior; + + public SetCpuAction(String cpu, Cpu.CpuBehavior behavior) { + super(cpu, null); + this.behavior = behavior; + putValue(SHORT_DESCRIPTION, "Set CPU to " + cpu); + } + + @Override + public void actionPerformed(ActionEvent actionEvent) { + machine.getCpu().setBehavior(behavior); + } + } + class ToggleTraceWindowAction extends AbstractAction { public ToggleTraceWindowAction() { super("Trace Log", null); @@ -868,6 +883,13 @@ public class Simulator { JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction()); simulatorMenu.add(selectMachineItem); + // "CPU" sub-menu + JMenu cpuTypeMenu = new JMenu("CPU"); + ButtonGroup cpuGroup = new ButtonGroup(); + + makeCpuMenuItem("NMOS 6502", Cpu.CpuBehavior.NMOS_6502, cpuTypeMenu, cpuGroup); + makeCpuMenuItem("CMOS 65C02", Cpu.CpuBehavior.CMOS_6502, cpuTypeMenu, cpuGroup); + // "Clock Speed" sub-menu JMenu speedSubMenu = new JMenu("Clock Speed"); ButtonGroup speedGroup = new ButtonGroup(); @@ -878,6 +900,7 @@ public class Simulator { makeSpeedMenuItem(8, speedSubMenu, speedGroup); simulatorMenu.add(speedSubMenu); + simulatorMenu.add(cpuTypeMenu); // "Breakpoints" final JCheckBoxMenuItem showBreakpoints = new JCheckBoxMenuItem(new ToggleBreakpointWindowAction()); @@ -914,6 +937,17 @@ public class Simulator { subMenu.add(item); group.add(item); } + + private void makeCpuMenuItem(String cpu, Cpu.CpuBehavior behavior, JMenu subMenu, ButtonGroup group) { + + Action action = new SetCpuAction(cpu, behavior); + + JCheckBoxMenuItem item = new JCheckBoxMenuItem(action); + item.setSelected(machine.getCpu().getBehavior() == behavior); + subMenu.add(item); + group.add(item); + } + } private void updateVisibleState() { diff --git a/src/test/java/com/loomcom/symon/Cpu65C02AbsoluteModeTest.java b/src/test/java/com/loomcom/symon/Cpu65C02AbsoluteModeTest.java new file mode 100644 index 0000000..3cc1471 --- /dev/null +++ b/src/test/java/com/loomcom/symon/Cpu65C02AbsoluteModeTest.java @@ -0,0 +1,125 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.exceptions.MemoryAccessException; +import junit.framework.*; + +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 Cpu65C02AbsoluteModeTest extends TestCase { + 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(); + } + + public void test_STZ() throws Exception { + makeCmosCpu(); + bus.write(0x0010,0xff); + + bus.loadProgram(0x9c, 0x10, 0x00); // STZ Absolute + + // Test STZ Absolute ($0010) + assertEquals(0xff, bus.read(0x0010, true)); + cpu.step(); + assertEquals(0x00, bus.read(0x0010, true)); + + } + + public void test_STZRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.write(0x0010,0xff); + + bus.loadProgram(0x9c, 0x10, 0x00); // STZ Absolute + + // Test STZ Absolute ($0010) + assertEquals(0xff, bus.read(0x0010, true)); + cpu.step(); + assertEquals(0xff, bus.read(0x0010, true)); + + } + + public void test_TSB() throws Exception { + makeCmosCpu(); + bus.loadProgram(0x0c, 0x10, 0x00); // 65C02 TSB Absolute $0010 + + bus.write(0x10, 0x01); + cpu.setAccumulator(0x01); + cpu.step(); + assertEquals(0x01,bus.read(0x10,true)); // 0x01 & 0x01 = 0x01 + assertFalse(cpu.getZeroFlag()); + + cpu.reset(); + cpu.setAccumulator(0x02); + cpu.step(); + assertEquals(0x03,bus.read(0x0010,true)); + assertTrue(cpu.getZeroFlag()); + + } + + public void test_TSBRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.loadProgram(0x0c, 0x10, 0x00); // 65C02 TSB Absolute $0010 + + bus.write(0x10, 0x00); + cpu.setAccumulator(0x01); + cpu.step(); + assertEquals(0x00,bus.read(0x0010,true)); + + } + + public void test_TRB() throws Exception { + makeCmosCpu(); + bus.loadProgram(0x1c, 0x00, 0x01); // 65C02 TRB Absolute $0010 + + bus.write(0x0100, 0x03); + cpu.setAccumulator(0x01); + cpu.step(); + assertEquals(0x02,bus.read(0x0100,true)); // $03 &= ~($01) = $02 + assertFalse(cpu.getZeroFlag()); // Z = !(A & M) + + cpu.reset(); + cpu.setAccumulator(0x01); + cpu.step(); + assertEquals(0x02,bus.read(0x0100,true)); // $02 &= ~($01) = $02 + assertTrue(cpu.getZeroFlag()); // Z = !(A & M) + + } + + public void test_TRBRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.loadProgram(0x1c, 0x00, 0x01); // 65C02 TRB Absolute $0010 + + bus.write(0x0100, 0xff); + cpu.setAccumulator(0x01); + cpu.step(); + assertEquals(0xff,bus.read(0x0100,true)); + + } +} diff --git a/src/test/java/com/loomcom/symon/Cpu65C02AbsoluteXModeTest.java b/src/test/java/com/loomcom/symon/Cpu65C02AbsoluteXModeTest.java new file mode 100644 index 0000000..d41979b --- /dev/null +++ b/src/test/java/com/loomcom/symon/Cpu65C02AbsoluteXModeTest.java @@ -0,0 +1,87 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.exceptions.MemoryAccessException; +import junit.framework.*; + +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 Cpu65C02AbsoluteXModeTest extends TestCase { + 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(); + } + + public void test_STZ() throws Exception { + makeCmosCpu(); + bus.write(0x0011,0xff); + + bus.loadProgram(0x9e, 0x10, 0x00); // STZ Absolute,X + + // Test STZ Absolute,X ($0011) + cpu.setXRegister(0x01); + assertEquals(0xff, bus.read(0x0011, true)); + cpu.step(); + assertEquals(0x00, bus.read(0x0011, true)); + } + + public void test_STZRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.write(0x0011,0xff); + + bus.loadProgram(0x9e, 0x10, 0x00); // STZ Absolute,X + + // Test STZ Absolute,X ($0011) + cpu.setXRegister(0x01); + assertEquals(0xff, bus.read(0x0011, true)); + cpu.step(); + assertEquals(0xff, bus.read(0x0011, true)); + } + + public void test_JMP_Indirect_Absolute_X () throws Exception { + makeCmosCpu(); + bus.write(0x304,00); + bus.write(0x0305,04); + bus.loadProgram(0x7c, 0x00, 0x03); + cpu.setXRegister(0x04); + cpu.step(); + assertEquals(0x0400,cpu.getProgramCounter()); + } + + public void test_JMP_Indirect_Absolute_XRequiresCmosCpu () throws Exception { + makeNmosCpu(); + bus.write(0x304,00); + bus.write(0x0305,04); + bus.loadProgram(0x7c, 0x00, 0x03); + cpu.setXRegister(0x04); + cpu.step(); + assertEquals(0x0203,cpu.getProgramCounter()); + } +} diff --git a/src/test/java/com/loomcom/symon/Cpu65C02ImmediateModeTest.java b/src/test/java/com/loomcom/symon/Cpu65C02ImmediateModeTest.java new file mode 100644 index 0000000..444afd8 --- /dev/null +++ b/src/test/java/com/loomcom/symon/Cpu65C02ImmediateModeTest.java @@ -0,0 +1,72 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.exceptions.MemoryAccessException; +import junit.framework.*; + +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 Cpu65C02ImmediateModeTest extends TestCase { + 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(); + } + + public void test_BIT_Immediate() throws Exception { + makeCmosCpu(); + bus.loadProgram(0x89, 0xF1); // 65C02 BIT #$01 + cpu.setAccumulator(0x02); + + cpu.step(); + assertTrue(cpu.getZeroFlag()); // #$02 & #$F1 = 0 + assertEquals(0x02,cpu.getAccumulator()); // Accumulator should not be modified + assertFalse(cpu.getNegativeFlag()); // BIT #Immediate should not set N or V Flags + assertFalse(cpu.getOverflowFlag()); + + cpu.reset(); + cpu.setAccumulator(0x01); + cpu.step(); + assertFalse(cpu.getZeroFlag()); // #$F1 & #$01 = 1 + assertEquals(0x01,cpu.getAccumulator()); + + } + + public void test_BIT_ImmediateRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.loadProgram(0x89, 0xF1); // 65C02 BIT #$01 + + cpu.step(); + cpu.setAccumulator(0x01); + assertTrue(cpu.getZeroFlag()); + assertEquals(0x01,cpu.getAccumulator()); + + } + +} diff --git a/src/test/java/com/loomcom/symon/Cpu65C02ImpliedModeTest.java b/src/test/java/com/loomcom/symon/Cpu65C02ImpliedModeTest.java new file mode 100644 index 0000000..83bfec5 --- /dev/null +++ b/src/test/java/com/loomcom/symon/Cpu65C02ImpliedModeTest.java @@ -0,0 +1,252 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.exceptions.MemoryAccessException; +import junit.framework.*; + +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 Cpu65C02ImpliedModeTest extends TestCase { + 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(); + } + + public void test_PHX() throws Exception { + makeCmosCpu(); + cpu.stackPush(0x00); + cpu.setXRegister(0xff); + bus.loadProgram(0xda); + + assertEquals(cpu.stackPeek(), 0x00); + cpu.step(); + assertEquals(cpu.stackPeek(), 0xff); + + } + + public void test_PHXRequiresCmosCpu() throws Exception { + makeNmosCpu(); + cpu.stackPush(0x00); + cpu.setXRegister(0xff); + bus.loadProgram(0xda); + + assertEquals(cpu.stackPeek(), 0x00); + cpu.step(); + assertEquals(cpu.stackPeek(), 0x00); + + } + + public void test_PLX() throws Exception { + makeCmosCpu(); + cpu.stackPush(0xff); + cpu.setXRegister(0x00); + bus.loadProgram(0xfa); + + assertEquals(0x00, cpu.getXRegister()); + cpu.step(); + assertEquals(0xff, cpu.getXRegister()); + + } + + public void test_PLXRequiresCmosCpu() throws Exception { + makeNmosCpu(); + cpu.stackPush(0xff); + cpu.setXRegister(0x00); + bus.loadProgram(0xfa); + + assertEquals(0x00, cpu.getXRegister()); + cpu.step(); + assertEquals(0x00, cpu.getXRegister()); + + } + + public void test_PHY() throws Exception { + makeCmosCpu(); + cpu.stackPush(0x00); + cpu.setYRegister(0xff); + bus.loadProgram(0x5a); + + assertEquals(0x00, cpu.stackPeek()); + cpu.step(); + assertEquals(0xff, cpu.stackPeek()); + + } + + public void test_PHYRequiresCmosCpu() throws Exception { + makeNmosCpu(); + cpu.stackPush(0x00); + cpu.setYRegister(0xff); + bus.loadProgram(0x5a); + + assertEquals(0x00, cpu.stackPeek()); + cpu.step(); + assertEquals(0x00, cpu.stackPeek()); + + } + + public void test_PLY() throws Exception { + makeCmosCpu(); + cpu.stackPush(0xff); + cpu.setYRegister(0x00); + bus.loadProgram(0x7a); + + assertEquals(0x00, cpu.getYRegister()); + cpu.step(); + assertEquals(0xff, cpu.getYRegister()); + + } + + public void test_PLYRequiresCmosCpu() throws Exception { + makeNmosCpu(); + cpu.stackPush(0xff); + cpu.setYRegister(0x00); + bus.loadProgram(0x7a); + + assertEquals(0x00, cpu.getYRegister()); + cpu.step(); + assertEquals(0x00, cpu.getYRegister()); + + } + + public void test_INC_A() throws Exception { + makeCmosCpu(); + cpu.setAccumulator(0x10); + bus.loadProgram(0x1a); + + cpu.step(); + assertEquals(0x11, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // Incrementing to 0 should set Zero Flag + cpu.reset(); + cpu.setAccumulator(0xff); + cpu.step(); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // Should set Negative Flag + cpu.reset(); + cpu.setAccumulator(0x7F); + cpu.step(); + assertTrue(cpu.getNegativeFlag()); + assertFalse(cpu.getZeroFlag()); + + } + + public void test_INC_ARequiresCmosCpu() throws Exception { + makeNmosCpu(); + cpu.setAccumulator(0x10); + bus.loadProgram(0x1a); + + cpu.step(); + assertEquals(0x10, cpu.getAccumulator()); + + } + + public void test_DEC_A() throws Exception { + makeCmosCpu(); + cpu.setAccumulator(0x10); + bus.loadProgram(0x3a); + + cpu.step(); + assertEquals(0x0F, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // Decrementing to 0 should set Zero Flag + cpu.reset(); + cpu.setAccumulator(0x01); + cpu.step(); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + // Should set Negative Flag + cpu.reset(); + cpu.setAccumulator(0x00); + cpu.step(); + assertTrue(cpu.getNegativeFlag()); + assertFalse(cpu.getZeroFlag()); + + } + + public void test_DEC_ARequiresCmosCpu() throws Exception { + makeNmosCpu(); + cpu.setAccumulator(0x10); + bus.loadProgram(0x3a); + + cpu.step(); + assertEquals(0x10, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + + } + + public void test_BRK_clearsDecimalModeFlag() throws Exception { + makeCmosCpu(); + cpu.setDecimalModeFlag(); + assertEquals(0x00, cpu.stackPeek()); + assertFalse(cpu.getBreakFlag()); + assertTrue(cpu.getDecimalModeFlag()); + assertEquals(0x0200, cpu.getProgramCounter()); + assertEquals(0xff, cpu.getStackPointer()); + + // Set the IRQ vector + bus.write(0xffff, 0x12); + bus.write(0xfffe, 0x34); + + bus.loadProgram(0xea, // NOP + 0xea, // NOP + 0xea, // NOP + 0x00, // BRK + 0xea, // NOP + 0xea); // NOP + + cpu.step(3); // Three NOP instructions + + assertEquals(0x203, cpu.getProgramCounter()); + assertTrue(cpu.getDecimalModeFlag()); + cpu.step(); // Triggers the BRK + + // Was at PC = 0x204. PC+1 should now be on the stack + assertEquals(0x02, bus.read(0x1ff, true)); // PC high byte + assertEquals(0x05, bus.read(0x1fe, true)); // PC low byte + + + // Interrupt vector held 0x1234, so we should be there. + assertEquals(0x1234, cpu.getProgramCounter()); + assertEquals(0xfc, cpu.getStackPointer()); + + // B and I flags should have been set on P + assertTrue(cpu.getBreakFlag()); + assertFalse(cpu.getDecimalModeFlag()); + } + +} diff --git a/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageModeTest.java b/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageModeTest.java new file mode 100644 index 0000000..4e96987 --- /dev/null +++ b/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageModeTest.java @@ -0,0 +1,372 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.exceptions.MemoryAccessException; +import junit.framework.*; + +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 Cpu65C02ZeroPageModeTest extends TestCase { + 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(); + } + + public void test_STZ() throws Exception { + makeCmosCpu(); + bus.write(0x0000,0xff); + + bus.loadProgram(0x64,0x00); // STZ Zero Page $00 + + // Test STZ Zero Page + assertEquals(0xff,bus.read(0x00, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x00, true)); + } + + public void test_STZRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.write(0x0000,0xff); + + bus.loadProgram(0x64,0x00); // STZ Zero Page $00 + + // Test STZ Zero Page + assertEquals(0xff,bus.read(0x00, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x00, true)); + + } + + public void test_SMB() throws Exception { + makeCmosCpu(); + bus.loadProgram(0x87,0x01, // SMB0 $01 + 0x97,0x01, // SMB1 $01 + 0xa7,0x01, // SMB2 $01 + 0xb7,0x01, // SMB3 $01 + 0xc7,0x01, // SMB4 $01 + 0xd7,0x01, // SMB5 $01 + 0xe7,0x01, // SMB6 $01 + 0xf7,0x01); // SMB7 $01 + + // SMB0 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(1 << 0,bus.read(0x0001, true)); + + // SMB1 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(1 << 1,bus.read(0x0001, true)); + + // SMB2 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(1 << 2,bus.read(0x0001, true)); + + // SMB3 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(1 << 3,bus.read(0x0001, true)); + + // SMB4 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(1 << 4,bus.read(0x0001, true)); + + // SMB5 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(1 << 5,bus.read(0x0001, true)); + + // SMB6 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(1 << 6,bus.read(0x0001, true)); + + // SMB7 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(1 << 7,bus.read(0x0001, true)); + + } + + public void test_SMBRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.loadProgram(0x87,0x01, // SMB0 $01 + 0x97,0x01, // SMB1 $01 + 0xa7,0x01, // SMB2 $01 + 0xb7,0x01, // SMB3 $01 + 0xc7,0x01, // SMB4 $01 + 0xd7,0x01, // SMB5 $01 + 0xe7,0x01, // SMB6 $01 + 0xf7,0x01); // SMB7 $01 + + // SMB0 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x0001, true)); + + // SMB1 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x0001, true)); + + // SMB2 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x0001, true)); + + // SMB3 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x0001, true)); + + // SMB4 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x0001, true)); + + // SMB5 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x0001, true)); + + // SMB6 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x0001, true)); + + // SMB7 + bus.write(0x01,0x00); + assertEquals(0x00,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x00,bus.read(0x0001, true)); + + } + + public void test_RMB() throws Exception { + makeCmosCpu(); + bus.loadProgram(0x07,0x01, // SMB0 $01 + 0x17,0x01, // SMB1 $01 + 0x27,0x01, // SMB2 $01 + 0x37,0x01, // SMB3 $01 + 0x47,0x01, // SMB4 $01 + 0x57,0x01, // SMB5 $01 + 0x67,0x01, // SMB6 $01 + 0x77,0x01); // SMB7 $01 + + // RMB0 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xfe,bus.read(0x0001, true)); + + // RMB1 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xfd,bus.read(0x0001, true)); + + // RMB2 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xfb,bus.read(0x0001, true)); + + // RMB3 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xf7,bus.read(0x0001, true)); + + // RMB4 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xef,bus.read(0x0001, true)); + + // RMB5 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xdf,bus.read(0x0001, true)); + + // RMB6 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xbf,bus.read(0x0001, true)); + + // RMB7 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0x7f,bus.read(0x0001, true)); + + } + + public void test_RMBRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.loadProgram(0x07,0x01, // SMB0 $01 + 0x17,0x01, // SMB1 $01 + 0x27,0x01, // SMB2 $01 + 0x37,0x01, // SMB3 $01 + 0x47,0x01, // SMB4 $01 + 0x57,0x01, // SMB5 $01 + 0x67,0x01, // SMB6 $01 + 0x77,0x01); // SMB7 $01 + + // RMB0 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x0001, true)); + + // RMB1 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x0001, true)); + + // RMB2 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x0001, true)); + + // RMB3 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x0001, true)); + + // RMB4 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x0001, true)); + + // RMB5 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x0001, true)); + + // RMB6 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x0001, true)); + + // RMB7 + bus.write(0x01,0xff); + assertEquals(0xff,bus.read(0x0001, true)); + cpu.step(); + assertEquals(0xff,bus.read(0x0001, true)); + + } + + public void test_TSB() throws Exception { + makeCmosCpu(); + bus.loadProgram(0x04, 0x10); // 65C02 TSB Zero Page $10 + + bus.write(0x10, 0x01); + cpu.setAccumulator(0x03); + cpu.step(); + assertEquals(0x03,bus.read(0x10,true)); + assertFalse(cpu.getZeroFlag()); + + cpu.reset(); + bus.write(0x10, 0x01); + cpu.setAccumulator(0x02); + cpu.step(); + assertEquals(0x03,bus.read(0x10,true)); + assertTrue(cpu.getZeroFlag()); + + } + + public void test_TSBRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.loadProgram(0x04, 0x10); // 65C02 TSB Zero Page $10 + + bus.write(0x10, 0x01); + cpu.setAccumulator(0x03); + cpu.step(); + assertEquals(0x01,bus.read(0x10,true)); + + } + + public void test_TRB() throws Exception { + makeCmosCpu(); + cpu.reset(); + bus.loadProgram(0x14, 0x40); // 65C02 TRB Zero Page $40 + + bus.write(0x0040, 0xff); + cpu.setAccumulator(0x01); + cpu.step(); + assertEquals(0xfe,bus.read(0x0040,true)); // $03 &= ~($01) = $02 + assertFalse(cpu.getZeroFlag()); // Z = !(A & M) + + cpu.reset(); + cpu.setAccumulator(0x01); + cpu.step(); + assertEquals(0xfe,bus.read(0x0040,true)); // $02 &= ~($01) = $02 + assertTrue(cpu.getZeroFlag()); // Z = !(A & M) + + } + + public void test_TRBRequiresCmosCpu() throws Exception { + makeNmosCpu(); + cpu.reset(); + bus.loadProgram(0x14, 0x40); // 65C02 TRB Zero Page $40 + + bus.write(0x0040, 0xff); + cpu.setAccumulator(0x01); + cpu.step(); + assertEquals(0xff,bus.read(0x0040,true)); // $03 &= ~($01) = $02 + + } + +} diff --git a/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageRelativeTest.java b/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageRelativeTest.java new file mode 100644 index 0000000..35c3db8 --- /dev/null +++ b/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageRelativeTest.java @@ -0,0 +1,585 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.exceptions.MemoryAccessException; +import junit.framework.*; + +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 Cpu65C02ZeroPageRelativeTest extends TestCase { + 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(); + } + public void test_BRA() throws Exception { + makeCmosCpu(); + // Positive Offset + bus.loadProgram(0x80, 0x05); // 65C02 BRA $05 ; *=$0202+$05 ($0207) + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0x80, 0xfb); // 65C02 BRA $fb ; *=$0202-$05 ($01fd) + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + } + + public void test_BRArequiresCmosCpu() throws Exception { + makeNmosCpu(); + // Should be a NOOP on NMOS and end up at $0202 + // Positive Offset + bus.loadProgram(0x80, 0x05); // BRA $05 ; *=$0202+$05 ($0207) + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0x80, 0xfb); // BRA $fb ; *=$0202-$05 ($01fd) + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + } + + public void test_BBR() throws Exception { + makeCmosCpu(); + + /* BBR0 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0xff); + bus.loadProgram(0x0f, 0x0a, 0x05); // 65C02 BBR0 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0xfe); + bus.loadProgram(0x0f, 0x0a, 0x05); // 65C02 BBR0 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0xfe); + bus.loadProgram(0x0f, 0x0a, 0xfb); // 65C02 BBR0 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBR1 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0xff); + bus.loadProgram(0x1f, 0x0a, 0x05); // 65C02 BBR1 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0xfd); + bus.loadProgram(0x1f, 0x0a, 0x05); // 65C02 BBR1 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0xfd); + bus.loadProgram(0x1f, 0x0a, 0xfb); // 65C02 BBR1 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBR2 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0xff); + bus.loadProgram(0x2f, 0x0a, 0x05); // 65C02 BBR2 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0xfb); + bus.loadProgram(0x2f, 0x0a, 0x05); // 65C02 BBR2 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0xfb); + bus.loadProgram(0x2f, 0x0a, 0xfb); // 65C02 BBR2 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBR3 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0xff); + bus.loadProgram(0x3f, 0x0a, 0x05); // 65C02 BBR3 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0xf7); + bus.loadProgram(0x3f, 0x0a, 0x05); // 65C02 BBR3 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0xf7); + bus.loadProgram(0x3f, 0x0a, 0xfb); // 65C02 BBR3 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBR4 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0xff); + bus.loadProgram(0x4f, 0x0a, 0x05); // 65C02 BBR4 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0xef); + bus.loadProgram(0x4f, 0x0a, 0x05); // 65C02 BBR4 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0xef); + bus.loadProgram(0x4f, 0x0a, 0xfb); // 65C02 BBR4 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBR5 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0xff); + bus.loadProgram(0x5f, 0x0a, 0x05); // 65C02 BBR5 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0xdf); + bus.loadProgram(0x5f, 0x0a, 0x05); // 65C02 BBR5 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0xdf); + bus.loadProgram(0x5f, 0x0a, 0xfb); // 65C02 BBR5 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBR6 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0xff); + bus.loadProgram(0x6f, 0x0a, 0x05); // 65C02 BBR6 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0xbf); + bus.loadProgram(0x6f, 0x0a, 0x05); // 65C02 BBR6 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0xbf); + bus.loadProgram(0x6f, 0x0a, 0xfb); // 65C02 BBR6 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBR7 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0xff); + bus.loadProgram(0x7f, 0x0a, 0x05); // 65C02 BBR7 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x7f); + bus.loadProgram(0x7f, 0x0a, 0x05); // 65C02 BBR7 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x7f); + bus.loadProgram(0x7f, 0x0a, 0xfb); // 65C02 BBR7 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + } + + public void test_BBRNeedsCmosCpu() throws Exception { + makeNmosCpu(); + + /* BBR0 */ + bus.write(0x0a,0xfe); + bus.loadProgram(0x0f, 0x0a, 0x05); // 65C02 BBR0 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBR1 */ + cpu.reset(); + bus.write(0x0a,0xfd); + bus.loadProgram(0x1f, 0x0a, 0x05); // 65C02 BBR1 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBR2 */ + cpu.reset(); + bus.write(0x0a,0xfb); + bus.loadProgram(0x2f, 0x0a, 0x05); // 65C02 BBR2 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBR3 */ + cpu.reset(); + bus.write(0x0a,0xf7); + bus.loadProgram(0x3f, 0x0a, 0x05); // 65C02 BBR3 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBR4 */ + cpu.reset(); + bus.write(0x0a,0xef); + bus.loadProgram(0x4f, 0x0a, 0x05); // 65C02 BBR4 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBR5 */ + cpu.reset(); + bus.write(0x0a,0xdf); + bus.loadProgram(0x5f, 0x0a, 0x05); // 65C02 BBR5 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBR6 */ + cpu.reset(); + bus.write(0x0a,0xbf); + bus.loadProgram(0x6f, 0x0a, 0x05); // 65C02 BBR6 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBR7 */ + cpu.reset(); + bus.write(0x0a,0x7f); + bus.loadProgram(0x7f, 0x0a, 0x05); // 65C02 BBR7 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + } + + public void test_BBS() throws Exception { + makeCmosCpu(); + + /* BBS0 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0x00); + bus.loadProgram(0x8f, 0x0a, 0x05); // 65C02 BBS0 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x01); + bus.loadProgram(0x8f, 0x0a, 0x05); // 65C02 BBS0 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x01); + bus.loadProgram(0x8f, 0x0a, 0xfb); // 65C02 BBS0 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBS1 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0x00); + bus.loadProgram(0x9f, 0x00, 0x05); // 65C02 BBS1 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x02); + bus.loadProgram(0x9f, 0x0a, 0x05); // 65C02 BBS1 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x02); + bus.loadProgram(0x9f, 0x0a, 0xfb); // 65C02 BBS1 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBS2 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0x00); + bus.loadProgram(0xaf, 0x0a, 0x05); // 65C02 BBS2 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x04); + bus.loadProgram(0xaf, 0x0a, 0x05); // 65C02 BBS2 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x04); + bus.loadProgram(0xaf, 0x0a, 0xfb); // 65C02 BBS2 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBS3 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0x00); + bus.loadProgram(0xbf, 0x0a, 0x05); // 65C02 BBS3 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x08); + bus.loadProgram(0xbf, 0x0a, 0x05); // 65C02 BBS3 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x08); + bus.loadProgram(0xbf, 0x0a, 0xfb); // 65C02 BBS3 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBS4 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0x00); + bus.loadProgram(0xcf, 0x0a, 0x05); // 65C02 BBS4 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x10); + bus.loadProgram(0xcf, 0x0a, 0x05); // 65C02 BBS4 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x10); + bus.loadProgram(0xcf, 0x0a, 0xfb); // 65C02 BBS4 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBS5 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0x00); + bus.loadProgram(0xdf, 0x0a, 0x05); // 65C02 BBS5 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x20); + bus.loadProgram(0xdf, 0x0a, 0x05); // 65C02 BBS5 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x20); + bus.loadProgram(0xdf, 0x0a, 0xfb); // 65C02 BBS5 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBS6 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0x00); + bus.loadProgram(0xef, 0x0a, 0x05); // 65C02 BBS6 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x40); + bus.loadProgram(0xef, 0x0a, 0x05); // 65C02 BBS6 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x40); + bus.loadProgram(0xef, 0x0a, 0xfb); // 65C02 BBS6 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + /* BBS7 */ + // Positive Offset + cpu.reset(); + bus.write(0x0a,0x00); + bus.loadProgram(0xff, 0x0a, 0x05); // 65C02 BBS7 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched + + cpu.reset(); + bus.write(0x0a,0x80); + bus.loadProgram(0xff, 0x0a, 0x05); // 65C02 BBS7 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x208, cpu.getProgramCounter()); // Should have branched + + // Negative Offset + cpu.reset(); + bus.write(0x0a,0x80); + bus.loadProgram(0xff, 0x0a, 0xfb); // 65C02 BBS7 $00 $fb ; *=$0203-$05 ($01fe) + cpu.step(); + assertEquals(0x1fe, cpu.getProgramCounter()); + + } + + public void test_BBSNeedsCmosCpu() throws Exception { + makeNmosCpu(); + + /* BBS0 */ + cpu.reset(); + bus.write(0x0a,0x01); + bus.loadProgram(0x8f, 0x0a, 0x05); // 65C02 BBS0 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBS1 */ + cpu.reset(); + bus.write(0x0a,0x02); + bus.loadProgram(0x9f, 0x0a, 0x05); // 65C02 BBS1 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBS2 */ + cpu.reset(); + bus.write(0x0a,0x04); + bus.loadProgram(0xaf, 0x0a, 0x05); // 65C02 BBS2 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBS3 */ + cpu.reset(); + bus.write(0x0a,0x08); + bus.loadProgram(0xbf, 0x0a, 0x05); // 65C02 BBS3 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBS4 */ + cpu.reset(); + bus.write(0x0a,0x10); + bus.loadProgram(0xcf, 0x0a, 0x05); // 65C02 BBS4 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBS5 */ + cpu.reset(); + bus.write(0x0a,0x20); + bus.loadProgram(0xdf, 0x0a, 0x05); // 65C02 BBS5 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBS6 */ + cpu.reset(); + bus.write(0x0a,0x40); + bus.loadProgram(0xef, 0x0a, 0x05); // 65C02 BBS6 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + /* BBS7 */ + cpu.reset(); + bus.write(0x0a,0x80); + bus.loadProgram(0xff, 0x0a, 0x05); // 65C02 BBS7 $00 $05 ; *=$0202+$05 ($0208) + + cpu.step(); + assertEquals(0x203, cpu.getProgramCounter()); + + } + +} diff --git a/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageXModeTest.java b/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageXModeTest.java new file mode 100644 index 0000000..d33f4e7 --- /dev/null +++ b/src/test/java/com/loomcom/symon/Cpu65C02ZeroPageXModeTest.java @@ -0,0 +1,69 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.exceptions.MemoryAccessException; +import junit.framework.*; + +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 Cpu65C02ZeroPageXModeTest extends TestCase { + 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(); + } + + public void test_STZ() throws Exception { + makeCmosCpu(); + bus.write(0x0002,0xff); + + bus.loadProgram(0x74,0x01); // STZ Zero Page,X $01 + + // Test STZ Zero Page,X ($01+1) + assertEquals(0xff,bus.read(0x02, true)); + cpu.setXRegister(0x01); + cpu.step(); + assertEquals(0x00,bus.read(0x02, true)); + } + + public void test_STZRequiresCmosCpu() throws Exception { + makeNmosCpu(); + bus.write(0x0002,0xff); + + bus.loadProgram(0x74,0x01); // STZ Zero Page,X $01 + + // Test STZ Zero Page,X ($01+1) + assertEquals(0xff,bus.read(0x02, true)); + cpu.setXRegister(0x01); + cpu.step(); + assertEquals(0xff,bus.read(0x02, true)); + + } + +} diff --git a/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java b/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java index 87f7442..95b8664 100644 --- a/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java +++ b/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java @@ -64,7 +64,7 @@ public class CpuIndirectModeTest extends TestCase { } 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); @@ -76,7 +76,7 @@ public class CpuIndirectModeTest extends TestCase { } 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); @@ -88,7 +88,7 @@ public class CpuIndirectModeTest extends TestCase { } 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 0eb2c21..8896980 100644 --- a/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java +++ b/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java @@ -162,7 +162,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 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()); + } +}