From 76dcbd712a7669e97cd7f66f5a1e685c1942465d Mon Sep 17 00:00:00 2001 From: Seth Morabito Date: Sat, 25 Jan 2014 20:45:39 -0800 Subject: [PATCH] IRQ and NMI handling at CPU level. --- src/main/java/com/loomcom/symon/Cpu.java | 50 +- .../loomcom/symon/CpuAccumulatorModeTest.java | 482 +++++++++--------- src/test/java/com/loomcom/symon/CpuTest.java | 147 ++++++ 3 files changed, 428 insertions(+), 251 deletions(-) diff --git a/src/main/java/com/loomcom/symon/Cpu.java b/src/main/java/com/loomcom/symon/Cpu.java index 85a6cba..a5d72d6 100644 --- a/src/main/java/com/loomcom/symon/Cpu.java +++ b/src/main/java/com/loomcom/symon/Cpu.java @@ -160,6 +160,14 @@ public class Cpu implements InstructionTable { // Store the address from which the IR was read, for debugging state.lastPc = state.pc; + // Check for Interrupts before doing anything else. + // This will set the PC and jump to the interrupt vector. + if (state.nmiAsserted) { + handleNmi(); + } else if (state.irqAsserted && !getIrqDisableFlag()) { + handleIrq(); + } + // Fetch memory location for this instruction. state.ir = bus.read(state.pc); irAddressMode = (state.ir >> 2) & 0x07; @@ -251,16 +259,7 @@ public class Cpu implements InstructionTable { /** Single Byte Instructions; Implied and Relative **/ case 0x00: // BRK - Force Interrupt - Implied if (!getIrqDisableFlag()) { - // Set the break flag before pushing. - setBreakFlag(); - // Push program counter + 1 onto the stack - stackPush((state.pc + 1 >> 8) & 0xff); // PC high byte - stackPush(state.pc + 1 & 0xff); // PC low byte - stackPush(state.getStatusFlag()); - // Set the Interrupt Disabled flag. RTI will clear it. - setIrqDisableFlag(); - // Load interrupt vector address into PC - state.pc = address(bus.read(IRQ_VECTOR_L), bus.read(IRQ_VECTOR_H)); + handleIrq(); } break; case 0x08: // PHP - Push Processor Status - Implied @@ -733,6 +732,37 @@ public class Cpu implements InstructionTable { delayLoop(state.ir); } + private void handleIrq() throws MemoryAccessException { + handleInterrupt(IRQ_VECTOR_L, IRQ_VECTOR_H); + + clearIrq(); + } + + private void handleNmi() throws MemoryAccessException { + handleInterrupt(NMI_VECTOR_L, NMI_VECTOR_H); + + clearNmi(); + } + + /** + * Handle the common behavior of BRK, /IRQ, and /NMI + * + * @throws MemoryAccessException + */ + private void handleInterrupt(int vectorLow, int vectorHigh) throws MemoryAccessException { + // Set the break flag before pushing. + setBreakFlag(); + // Push program counter + 1 onto the stack + stackPush((state.pc + 1 >> 8) & 0xff); // PC high byte + stackPush(state.pc + 1 & 0xff); // PC low byte + stackPush(state.getStatusFlag()); + // Set the Interrupt Disabled flag. RTI will clear it. + setIrqDisableFlag(); + + // Load interrupt vector address into PC + state.pc = address(bus.read(vectorLow), bus.read(vectorHigh)); + } + /** * Add with Carry, used by all addressing mode implementations of ADC. * As a side effect, this will set the overflow and carry flags as diff --git a/src/test/java/com/loomcom/symon/CpuAccumulatorModeTest.java b/src/test/java/com/loomcom/symon/CpuAccumulatorModeTest.java index 43e8cce..f036b9c 100644 --- a/src/test/java/com/loomcom/symon/CpuAccumulatorModeTest.java +++ b/src/test/java/com/loomcom/symon/CpuAccumulatorModeTest.java @@ -7,303 +7,303 @@ import junit.framework.*; public class CpuAccumulatorModeTest extends TestCase { - protected Cpu cpu; - protected Bus bus; - protected Memory mem; + protected Cpu cpu; + protected Bus bus; + protected Memory mem; - public void setUp() throws MemoryRangeException, MemoryAccessException { - this.cpu = new Cpu(); - this.bus = new Bus(0x0000, 0xffff); - this.mem = new Memory(0x0000, 0x10000); - bus.addCpu(cpu); - bus.addDevice(mem); + public void setUp() throws MemoryRangeException, MemoryAccessException { + this.cpu = new Cpu(); + this.bus = new Bus(0x0000, 0xffff); + this.mem = new Memory(0x0000, 0x10000); + 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); + // Load the reset vector. + bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff); + bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8); - cpu.reset(); + cpu.reset(); - // Assert initial state - assertEquals(0, cpu.getAccumulator()); - assertEquals(0, cpu.getXRegister()); - assertEquals(0, cpu.getYRegister()); - assertEquals(0x200, cpu.getProgramCounter()); - assertEquals(0xff, cpu.getStackPointer()); - assertEquals(0x20, cpu.getProcessorStatus()); - } + // Assert initial state + assertEquals(0, cpu.getAccumulator()); + assertEquals(0, cpu.getXRegister()); + assertEquals(0, cpu.getYRegister()); + assertEquals(0x200, cpu.getProgramCounter()); + assertEquals(0xff, cpu.getStackPointer()); + assertEquals(0x20, cpu.getProcessorStatus()); + } - /* - * The following opcodes are tested for correctness in this file: - * - * ASL - $0a - * ROL - $2a - * LSR - $4a - * ROR - $6a - */ + /* + * The following opcodes are tested for correctness in this file: + * + * ASL - $0a + * ROL - $2a + * LSR - $4a + * ROR - $6a + */ /* ASL - Arithmetic Shift Left - $0a */ - public void test_ASL() throws MemoryAccessException { - bus.loadProgram(0xa9, 0x00, // LDA #$00 - 0x0a, // ASL A + public void test_ASL() throws MemoryAccessException { + bus.loadProgram(0xa9, 0x00, // LDA #$00 + 0x0a, // ASL A - 0xa9, 0x01, // LDA #$01 - 0x0a, // ASL A + 0xa9, 0x01, // LDA #$01 + 0x0a, // ASL A - 0xa9, 0x02, // LDA #$02 - 0x0a, // ASL A + 0xa9, 0x02, // LDA #$02 + 0x0a, // ASL A - 0xa9, 0x44, // LDA #$44 - 0x0a, // ASL A + 0xa9, 0x44, // LDA #$44 + 0x0a, // ASL A - 0xa9, 0x80, // LDA #$80 - 0x0a); // ASL A + 0xa9, 0x80, // LDA #$80 + 0x0a); // ASL A - cpu.step(2); - assertEquals(0x00, cpu.getAccumulator()); - assertTrue(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x00, cpu.getAccumulator()); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x02, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x02, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x04, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x04, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x88, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertTrue(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x88, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x00, cpu.getAccumulator()); - assertTrue(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertTrue(cpu.getCarryFlag()); - } + cpu.step(2); + assertEquals(0x00, cpu.getAccumulator()); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertTrue(cpu.getCarryFlag()); + } /* ROL - Rotate Left - $2a */ - public void test_ROL() throws MemoryAccessException { - bus.loadProgram(0xa9, 0x00, // LDA #$00 - 0x2a, // ROL A (m=%00000000, c=0) - 0xa9, 0x01, // LDA #$01 - 0x2a, // ROL A (m=%00000010, c=0) - 0x38, // SEC (m=%00000010, c=1) - 0x2a, // ROL A (m=%00000101, c=0) - 0x2a, // ROL A (m=%00001010, c=0) - 0x2a, // ROL A (m=%00010100, c=0) - 0x2a, // ROL A (m=%00101000, c=0) - 0x2a, // ROL A (m=%01010000, c=0) - 0x2a, // ROL A (m=%10100000, c=0) - 0x2a, // ROL A (m=%01000000, c=1) - 0x2a); // ROL A (m=%10000001, c=0) + public void test_ROL() throws MemoryAccessException { + bus.loadProgram(0xa9, 0x00, // LDA #$00 + 0x2a, // ROL A (m=%00000000, c=0) + 0xa9, 0x01, // LDA #$01 + 0x2a, // ROL A (m=%00000010, c=0) + 0x38, // SEC (m=%00000010, c=1) + 0x2a, // ROL A (m=%00000101, c=0) + 0x2a, // ROL A (m=%00001010, c=0) + 0x2a, // ROL A (m=%00010100, c=0) + 0x2a, // ROL A (m=%00101000, c=0) + 0x2a, // ROL A (m=%01010000, c=0) + 0x2a, // ROL A (m=%10100000, c=0) + 0x2a, // ROL A (m=%01000000, c=1) + 0x2a); // ROL A (m=%10000001, c=0) - cpu.step(2); - assertEquals(0x00, cpu.getAccumulator()); - assertTrue(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x00, cpu.getAccumulator()); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x02, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x02, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x05, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x05, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x0a, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x0a, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x14, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x14, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x28, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x28, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x50, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x50, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0xa0, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertTrue(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0xa0, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x40, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertTrue(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x40, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertTrue(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x81, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertTrue(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); - } + cpu.step(); + assertEquals(0x81, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); + } /* LSR - Logical Shift Right - $4a */ - public void test_LSR() throws MemoryAccessException { - bus.loadProgram(0xa9, 0x00, // LDA #$00 - 0x4a, // LSR A + public void test_LSR() throws MemoryAccessException { + bus.loadProgram(0xa9, 0x00, // LDA #$00 + 0x4a, // LSR A - 0xa9, 0x01, // LDA #$01 - 0x4a, // LSR A + 0xa9, 0x01, // LDA #$01 + 0x4a, // LSR A - 0xa9, 0x02, // LDA #$02 - 0x4a, // LSR A + 0xa9, 0x02, // LDA #$02 + 0x4a, // LSR A - 0xa9, 0x44, // LDA #$44 - 0x4a, // LSR A + 0xa9, 0x44, // LDA #$44 + 0x4a, // LSR A - 0xa9, 0x80, // LDA #$80 - 0x4a, // LSR A + 0xa9, 0x80, // LDA #$80 + 0x4a, // LSR A - 0x38, // SEC - 0xa9, 0x02, // LDA #$02 - 0x4a); // LSR $05 + 0x38, // SEC + 0xa9, 0x02, // LDA #$02 + 0x4a); // LSR $05 - cpu.step(2); - assertEquals(0x00, cpu.getAccumulator()); - assertTrue(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x00, cpu.getAccumulator()); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x00, cpu.getAccumulator()); - assertTrue(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertTrue(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x00, cpu.getAccumulator()); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertTrue(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x01, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x01, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x22, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x22, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x40, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x40, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - // Setting Carry should not affect the result. - cpu.step(3); - assertEquals(0x01, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); - } + // Setting Carry should not affect the result. + cpu.step(3); + assertEquals(0x01, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); + } /* ROR - Rotate Right - $6a */ - public void test_ROR() throws MemoryAccessException { - bus.loadProgram(0xa9, 0x00, // LDA #$00 - 0x6a, // ROR A (m=%00000000, c=0) - 0xa9, 0x10, // LDA #$10 - 0x6a, // ROR A (m=%00001000, c=0) - 0x6a, // ROR A (m=%00000100, c=0) - 0x6a, // ROR A (m=%00000010, c=0) - 0x6a, // ROR A (m=%00000001, c=0) - 0x6a, // ROR A (m=%00000000, c=1) - 0x6a, // ROR A (m=%10000000, c=0) - 0x6a, // ROR A (m=%01000000, c=0) - 0x6a, // ROR A (m=%00100000, c=0) - 0x6a); // ROR A (m=%00010000, c=0) + public void test_ROR() throws MemoryAccessException { + bus.loadProgram(0xa9, 0x00, // LDA #$00 + 0x6a, // ROR A (m=%00000000, c=0) + 0xa9, 0x10, // LDA #$10 + 0x6a, // ROR A (m=%00001000, c=0) + 0x6a, // ROR A (m=%00000100, c=0) + 0x6a, // ROR A (m=%00000010, c=0) + 0x6a, // ROR A (m=%00000001, c=0) + 0x6a, // ROR A (m=%00000000, c=1) + 0x6a, // ROR A (m=%10000000, c=0) + 0x6a, // ROR A (m=%01000000, c=0) + 0x6a, // ROR A (m=%00100000, c=0) + 0x6a); // ROR A (m=%00010000, c=0) - cpu.step(2); - assertEquals(0x00, cpu.getAccumulator()); - assertTrue(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x00, cpu.getAccumulator()); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(2); - assertEquals(0x08, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(2); + assertEquals(0x08, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x04, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x04, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x02, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x02, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x01, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x01, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x00, cpu.getAccumulator()); - assertTrue(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertTrue(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x00, cpu.getAccumulator()); + assertTrue(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertTrue(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x80, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertTrue(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x80, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x40, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x40, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x20, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); + cpu.step(); + assertEquals(0x20, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); - cpu.step(); - assertEquals(0x10, cpu.getAccumulator()); - assertFalse(cpu.getZeroFlag()); - assertFalse(cpu.getNegativeFlag()); - assertFalse(cpu.getCarryFlag()); - } + cpu.step(); + assertEquals(0x10, cpu.getAccumulator()); + assertFalse(cpu.getZeroFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getCarryFlag()); + } } \ No newline at end of file diff --git a/src/test/java/com/loomcom/symon/CpuTest.java b/src/test/java/com/loomcom/symon/CpuTest.java index 84efaae..1332adf 100644 --- a/src/test/java/com/loomcom/symon/CpuTest.java +++ b/src/test/java/com/loomcom/symon/CpuTest.java @@ -345,6 +345,153 @@ public class CpuTest extends TestCase { assertFalse(cpu.getNegativeFlag()); } + public void testIrq() throws Exception { + // Ensure the IRQ disable flag is cleared + cpu.clearIrqDisableFlag(); + + // Set the IRQ vector + bus.write(0xffff, 0x12); + bus.write(0xfffe, 0x34); + + // Create an IRQ handler at 0x1234 + cpu.setProgramCounter(0x1234); + bus.loadProgram(0xa9, 0x33, // LDA #$33 + 0x69, 0x01); // ADC #$01 + + cpu.setProgramCounter(0x0200); + // Create a little program at 0x0200 + bus.loadProgram(0x18, // CLC + 0xa9, 0x01, // LDA #$00 + 0x69, 0x01); // ADC #$01 + + cpu.step(); + assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed. + assertEquals(0x00, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x0203, cpu.getProgramCounter()); + assertEquals(0x01, cpu.getAccumulator()); + + cpu.assertIrq(); + + cpu.step(); + assertTrue(cpu.getIrqDisableFlag()); // Should have been set by the IRQ + assertEquals(0x1236, cpu.getProgramCounter()); + assertEquals(0x33, cpu.getAccumulator()); + + // Be sure that the IRQ line is no longer held low + assertFalse(cpu.getCpuState().irqAsserted); + } + + public void testIrqHonorsIrqDisabledFlag() throws Exception { + // Ensure the IRQ disable flag is set + cpu.setIrqDisableFlag(); + + // Set the IRQ vector + bus.write(0xffff, 0x12); + bus.write(0xfffe, 0x34); + + // Create an IRQ handler at 0x1234 + cpu.setProgramCounter(0x1234); + bus.loadProgram(0xa9, 0x33, // LDA #$33 + 0x69, 0x01); // ADC #$01 + + cpu.setProgramCounter(0x0200); + // Create a little program at 0x0200 + bus.loadProgram(0x18, // CLC + 0xa9, 0x01, // LDA #$00 + 0x69, 0x01); // ADC #$01 + + cpu.step(); + assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed. + assertEquals(0x00, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x0203, cpu.getProgramCounter()); + assertEquals(0x01, cpu.getAccumulator()); + + cpu.assertIrq(); // Should be ignored, because the disable flag is set. + + cpu.step(); + assertTrue(cpu.getIrqDisableFlag()); // Should have been left alone. + assertEquals(0x0205, cpu.getProgramCounter()); + assertEquals(0x02, cpu.getAccumulator()); + } + + public void testNmi() throws Exception { + // Set the NMI vector to 0x1000 + bus.write(0xfffb, 0x10); + bus.write(0xfffa, 0x00); + + // Create an NMI handler at 0x1000 + cpu.setProgramCounter(0x1000); + bus.loadProgram(0xa9, 0x33, // LDA #$33 + 0x69, 0x01); // ADC #$01 + + // Create a little program at 0x0200 + cpu.setProgramCounter(0x0200); + bus.loadProgram(0x18, // CLC + 0xa9, 0x01, // LDA #$00 + 0x69, 0x01); // ADC #$01 + + cpu.step(); + assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed. + assertEquals(0x00, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x0203, cpu.getProgramCounter()); + assertEquals(0x01, cpu.getAccumulator()); + + cpu.assertNmi(); + + cpu.step(); + assertTrue(cpu.getIrqDisableFlag()); // Should have been set by the NMI + assertEquals(0x1002, cpu.getProgramCounter()); + assertEquals(0x33, cpu.getAccumulator()); + + // Be sure that the NMI line is no longer held low + assertFalse(cpu.getCpuState().nmiAsserted); + } + + public void testNmiIgnoresIrqDisableFlag() throws Exception { + // Set the IRQ disable flag, which should be ignored by the NMI + cpu.setIrqDisableFlag(); + + // Set the NMI vector to 0x1000 + bus.write(0xfffb, 0x10); + bus.write(0xfffa, 0x00); + + // Create an NMI handler at 0x1000 + cpu.setProgramCounter(0x1000); + bus.loadProgram(0xa9, 0x33, // LDA #$33 + 0x69, 0x01); // ADC #$01 + + // Create a little program at 0x0200 + cpu.setProgramCounter(0x0200); + bus.loadProgram(0x18, // CLC + 0xa9, 0x01, // LDA #$00 + 0x69, 0x01); // ADC #$01 + + cpu.step(); + assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed. + assertEquals(0x00, cpu.getAccumulator()); + + cpu.step(); + assertEquals(0x0203, cpu.getProgramCounter()); + assertEquals(0x01, cpu.getAccumulator()); + + cpu.assertNmi(); + + cpu.step(); + assertTrue(cpu.getIrqDisableFlag()); // Should have been set by the NMI + assertEquals(0x1002, cpu.getProgramCounter()); + assertEquals(0x33, cpu.getAccumulator()); + + // Be sure that the NMI line is no longer held low + assertFalse(cpu.getCpuState().nmiAsserted); + } + + public void testAddress() { assertEquals(0xf1ea, cpu.address(0xea, 0xf1)); assertEquals(0x00ea, cpu.address(0xea, 0x00));