diff --git a/src/main/java/ca/craigthomas/yacoco3e/components/BranchInstruction.java b/src/main/java/ca/craigthomas/yacoco3e/components/BranchInstruction.java index 6910629..848b7f1 100644 --- a/src/main/java/ca/craigthomas/yacoco3e/components/BranchInstruction.java +++ b/src/main/java/ca/craigthomas/yacoco3e/components/BranchInstruction.java @@ -39,7 +39,6 @@ public int call(IOController io) { if (operation.apply(io).equals(true)) { io.regs.pc.add(byteRead.isNegative() ? byteRead.getSignedShort() : byteRead.getShort()); } - return ticks; } diff --git a/src/main/java/ca/craigthomas/yacoco3e/components/ByteRegisterInstruction.java b/src/main/java/ca/craigthomas/yacoco3e/components/ByteRegisterInstruction.java index 0c30acb..5db5e70 100644 --- a/src/main/java/ca/craigthomas/yacoco3e/components/ByteRegisterInstruction.java +++ b/src/main/java/ca/craigthomas/yacoco3e/components/ByteRegisterInstruction.java @@ -239,9 +239,7 @@ public static void decimalAdditionAdjust(IOController io, UnsignedByte registerB } io.regs.a.set(new UnsignedByte(io.regs.a.getShort() + result.getShort())); io.regs.cc.or(io.regs.a.isZero() ? CC_Z : 0); - io.regs.cc.or(carryPreviouslySet ? CC_C : 0); - io.regs.cc.or(result.isNegative() ? CC_N : 0); - io.regs.a.set(result); + io.regs.cc.or(io.regs.a.isNegative() ? CC_N : 0); } diff --git a/src/main/java/ca/craigthomas/yacoco3e/components/CPU.java b/src/main/java/ca/craigthomas/yacoco3e/components/CPU.java index 52c3d41..8a1be80 100644 --- a/src/main/java/ca/craigthomas/yacoco3e/components/CPU.java +++ b/src/main/java/ca/craigthomas/yacoco3e/components/CPU.java @@ -14,15 +14,13 @@ public class CPU { /* CPU Internal Variables */ - private UnsignedWord lastPC; - private UnsignedByte lastOperand; private final IOController io; public Instruction instruction; /* Interrupt request flags */ - private boolean fireIRQ; - private boolean fireFIRQ; - private boolean fireNMI; + protected boolean fireIRQ; + protected boolean fireFIRQ; + protected boolean fireNMI; public CPU(IOController ioController) { io = ioController; @@ -35,8 +33,8 @@ public CPU(IOController ioController) { * @return the number of ticks taken up by the instruction */ public int executeInstruction() throws MalformedInstructionException { - UnsignedWord opWord = io.readWord(io.regs.pc); - instruction = InstructionTable.get(opWord); + UnsignedWord op = io.readWord(io.regs.pc); + instruction = InstructionTable.get(op); return instruction.execute(io); } @@ -56,7 +54,7 @@ public void interruptRequest() { io.regs.cc.or(CC_E); io.pushStack(Register.S, io.regs.cc); io.regs.cc.or(CC_I); - io.regs.pc.set(io.readWord(new UnsignedWord(0xFFF8))); + io.regs.pc.set(io.readWord(0xFFF8)); } /** @@ -70,7 +68,7 @@ public void fastInterruptRequest() { io.pushStack(Register.S, io.regs.cc); io.regs.cc.or(CC_F); io.regs.cc.or(CC_I); - io.regs.pc.set(io.readWord(new UnsignedWord(0xFFF6))); + io.regs.pc.set(io.readWord(0xFFF6)); } /** @@ -90,22 +88,7 @@ public void nonMaskableInterruptRequest() { io.pushStack(Register.S, io.regs.cc); io.regs.cc.or(CC_I); io.regs.cc.or(CC_F); - io.regs.pc.set(io.readWord(new UnsignedWord(0xFFFC))); - } - - /** - * Schedules an IRQ interrupt to occur. - */ - public void scheduleIRQ() { - fireIRQ = true; - } - - public boolean irqWaiting() { - return fireIRQ; - } - - public void clearIRQ() { - fireIRQ = false; + io.regs.pc.set(io.readWord(0xFFFC)); } /** @@ -113,22 +96,33 @@ public void clearIRQ() { * with it. */ public void serviceInterrupts() { + if (fireIRQ | fireFIRQ | fireNMI) { + io.waitForIRQ = false; + } + if (fireIRQ) { interruptRequest(); - clearIRQ(); + fireIRQ = false; } if (fireFIRQ) { fastInterruptRequest(); - clearFIRQ(); + fireFIRQ = false; } if (fireNMI) { nonMaskableInterruptRequest(); - clearNMI(); + fireNMI = false; } } + /** + * Schedules an IRQ interrupt to occur. + */ + public void scheduleIRQ() { + fireIRQ = true; + } + /** * Schedules a fast interrupt to occur. */ @@ -136,14 +130,6 @@ public void scheduleFIRQ() { fireFIRQ = true; } - public boolean firqWaiting() { - return fireFIRQ; - } - - public void clearFIRQ() { - fireFIRQ = false; - } - /** * Schedules a non-maskable interrupt to occur. */ @@ -151,14 +137,6 @@ public void scheduleNMI() { fireNMI = true; } - public boolean nmiWaiting() { - return fireNMI; - } - - public void clearNMI() { - fireNMI = false; - } - public void reset() { } diff --git a/src/main/java/ca/craigthomas/yacoco3e/components/Emulator.java b/src/main/java/ca/craigthomas/yacoco3e/components/Emulator.java index 6cc5d10..cb91c20 100644 --- a/src/main/java/ca/craigthomas/yacoco3e/components/Emulator.java +++ b/src/main/java/ca/craigthomas/yacoco3e/components/Emulator.java @@ -25,7 +25,7 @@ public class Emulator extends Thread private Screen screen; private Keyboard keyboard; private CPU cpu; - private IOController ioController; + private IOController io; private Cassette cassette; private Memory memory; @@ -105,9 +105,9 @@ private Emulator(Builder builder) { keyboard = new EmulatedKeyboard(); screen = new Screen(builder.scale); cassette = new Cassette(); - ioController = new IOController(memory, new RegisterSet(), keyboard, screen, cassette); - cpu = new CPU(ioController); - ioController.setCPU(cpu); + io = new IOController(memory, new RegisterSet(), keyboard, screen, cassette); + cpu = new CPU(io); + io.setCPU(cpu); trace = builder.trace; verbose = builder.verbose; status = EmulatorStatus.STOPPED; @@ -192,7 +192,7 @@ public void loadFromConfigFile(ConfigFile config) { if (drive0 != null) { JV1Disk disk = new JV1Disk(); if (disk.loadFile(drive0)) { - ioController.disk[0].loadFromVirtualDisk(disk); + io.disk[0].loadFromVirtualDisk(disk); } } } @@ -202,7 +202,7 @@ public void loadFromConfigFile(ConfigFile config) { */ public void reset() { memory.resetMemory(); - ioController.reset(); + io.reset(); cpu.reset(); } @@ -379,7 +379,7 @@ public void switchKeyListener(Keyboard newKeyboard) { canvas.removeKeyListener(keyboard); canvas.addKeyListener(newKeyboard); keyboard = newKeyboard; - ioController.setKeyboard(keyboard); + io.setKeyboard(keyboard); } /** @@ -404,7 +404,7 @@ private void refreshScreen() * 60th of a second time slice. */ public void refreshTicks() { - remainingTicks = ioController.tickRefreshAmount; + remainingTicks = io.tickRefreshAmount; } /** @@ -435,13 +435,13 @@ public void run() { while (status != EmulatorStatus.KILLED) { while (status == EmulatorStatus.RUNNING) { if (this.trace) { - System.out.print(ioController.regs.toString() + " | "); + System.out.print(io.regs.toString() + " | "); } try { - if (remainingTicks > 0) { + if (remainingTicks > 0 && !io.waitForIRQ) { operationTicks = cpu.executeInstruction(); - remainingTicks -= operationTicks; + remainingTicks = remainingTicks - operationTicks; } } catch (MalformedInstructionException e) { System.out.println(e.getMessage()); @@ -449,7 +449,9 @@ public void run() { } /* Increment timers if necessary */ - ioController.timerTick(operationTicks); + io.timerTick(operationTicks); + +// System.out.print(" new pc: " + ioController.regs.pc); /* Fire interrupts if set */ cpu.serviceInterrupts(); @@ -460,6 +462,7 @@ public void run() { // + cpu.getLastOperand() + " | " + cpu.instruction.getShortDescription()); if (cpu.instruction != null) { System.out.print(cpu.instruction.getShortDescription()); + System.out.print(" new pc: " + io.regs.pc); System.out.println(); } } @@ -492,7 +495,7 @@ public Memory getMemory() { } public IOController getIOController() { - return ioController; + return io; } public Cassette getCassette() { diff --git a/src/main/java/ca/craigthomas/yacoco3e/components/IOController.java b/src/main/java/ca/craigthomas/yacoco3e/components/IOController.java index 88598cb..2ed21d7 100644 --- a/src/main/java/ca/craigthomas/yacoco3e/components/IOController.java +++ b/src/main/java/ca/craigthomas/yacoco3e/components/IOController.java @@ -72,6 +72,9 @@ public class IOController /* GIME FIRQ status */ public UnsignedByte firqStatus; + /* Whether the CPU should wait for an interrupt request */ + public boolean waitForIRQ; + /* The number of ticks to pass in 63.5 microseconds */ public static final int TIMER_63_5_MICROS = 56; @@ -166,6 +169,7 @@ public IOController(Memory memory, RegisterSet registerSet, Keyboard keyboard, S samClockSpeed = new UnsignedByte(); tickRefreshAmount = LOW_SPEED_CLOCK_FREQUENCY; + waitForIRQ = false; } /** @@ -1189,7 +1193,7 @@ public void reset() { memory.disableAllRAMMode(); /* Load PC with Reset Interrupt Vector */ - regs.pc = readWord(new UnsignedWord(0xFFFE)); + regs.pc = readWord(0xFFFE); } /** diff --git a/src/main/java/ca/craigthomas/yacoco3e/components/Instruction.java b/src/main/java/ca/craigthomas/yacoco3e/components/Instruction.java index 75f0a76..7eab232 100644 --- a/src/main/java/ca/craigthomas/yacoco3e/components/Instruction.java +++ b/src/main/java/ca/craigthomas/yacoco3e/components/Instruction.java @@ -153,7 +153,7 @@ public void getIndexed(IOController io) throws MalformedInstructionException { UnsignedWord r; UnsignedWord d; UnsignedByte n; - UnsignedWord nWord; + UnsignedWord w; numBytesRead = 1; /* 5-bit offset - check for signed values */ @@ -248,10 +248,10 @@ public void getIndexed(IOController io) throws MalformedInstructionException { /* n,R -> 16-bit offset from R */ case 0x09: r = io.getWordRegister(io.getIndexedRegister(postByte)); - nWord = io.readWord(io.regs.pc); + w = io.readWord(io.regs.pc); io.regs.incrementPC(); io.regs.incrementPC(); - addressRead = new UnsignedWord(r.getInt() + nWord.getSignedInt()); + addressRead = new UnsignedWord(r.getInt() + w.getSignedInt()); wordRead = io.readWord(addressRead); byteRead = wordRead.getHigh(); numBytesRead = 3; @@ -280,10 +280,10 @@ public void getIndexed(IOController io) throws MalformedInstructionException { /* n,PC -> 16-bit offset from PC */ case 0x0D: r = io.getWordRegister(Register.PC); - nWord = io.readWord(io.regs.pc); + w = io.readWord(io.regs.pc); io.regs.incrementPC(); io.regs.incrementPC(); - addressRead = new UnsignedWord(r.getInt() + nWord.getSignedInt()); + addressRead = new UnsignedWord(r.getInt() + w.getSignedInt()); wordRead = io.readWord(addressRead); byteRead = wordRead.getHigh(); numBytesRead = 3; @@ -345,10 +345,10 @@ public void getIndexed(IOController io) throws MalformedInstructionException { /* [n,R] -> 16-bit offset from R - indirect */ case 0x19: r = io.getWordRegister(io.getIndexedRegister(postByte)); - nWord = io.readWord(io.regs.pc); + w = io.readWord(io.regs.pc); io.regs.incrementPC(); io.regs.incrementPC(); - addressRead = io.readWord(new UnsignedWord(r.getInt() + nWord.getSignedInt())); + addressRead = io.readWord(new UnsignedWord(r.getInt() + w.getSignedInt())); wordRead = io.readWord(addressRead); byteRead = wordRead.getHigh(); numBytesRead = 3; @@ -376,10 +376,10 @@ public void getIndexed(IOController io) throws MalformedInstructionException { /* [n,PC] -> 16-bit offset from PC - indirect */ case 0x1D: r = io.getWordRegister(Register.PC); - nWord = io.readWord(io.regs.pc); + w = io.readWord(io.regs.pc); io.regs.incrementPC(); io.regs.incrementPC(); - addressRead = io.readWord(new UnsignedWord(r.getInt() + nWord.getSignedInt())); + addressRead = io.readWord(new UnsignedWord(r.getInt() + w.getSignedInt())); wordRead = io.readWord(addressRead); byteRead = wordRead.getHigh(); numBytesRead = 3; @@ -387,10 +387,10 @@ public void getIndexed(IOController io) throws MalformedInstructionException { /* [n] -> extended indirect */ case 0x1F: - nWord = io.readWord(io.regs.pc); + w = io.readWord(io.regs.pc); io.regs.incrementPC(); io.regs.incrementPC(); - addressRead = io.readWord(nWord); + addressRead = io.readWord(w); wordRead = io.readWord(addressRead); byteRead = wordRead.getHigh(); numBytesRead = 3; diff --git a/src/main/java/ca/craigthomas/yacoco3e/components/Memory.java b/src/main/java/ca/craigthomas/yacoco3e/components/Memory.java index 9972e90..09cf67e 100644 --- a/src/main/java/ca/craigthomas/yacoco3e/components/Memory.java +++ b/src/main/java/ca/craigthomas/yacoco3e/components/Memory.java @@ -207,6 +207,10 @@ public UnsignedByte readROMByte(int address) { return new UnsignedByte(rom[0x3FF0 + (address & 0x000F)]); } + public void writeByte(int address, int value) { + writeByte(new UnsignedWord(address), new UnsignedByte(value)); + } + /** * Writes an UnsignedByte to the specified memory address. If the system * is in a ROM mode, will not write bytes to a ROM location. @@ -332,6 +336,10 @@ public void setTaskPAR(int par, UnsignedByte value) { taskPAR[par] = value.getShort(); } + public UnsignedByte readByte(int address) { + return readByte(new UnsignedWord(address)); + } + /** * Reads an UnsignedByte from the specified address. * diff --git a/src/main/java/ca/craigthomas/yacoco3e/components/VoidInstruction.java b/src/main/java/ca/craigthomas/yacoco3e/components/VoidInstruction.java index 81651b5..5944e4b 100644 --- a/src/main/java/ca/craigthomas/yacoco3e/components/VoidInstruction.java +++ b/src/main/java/ca/craigthomas/yacoco3e/components/VoidInstruction.java @@ -6,7 +6,6 @@ import ca.craigthomas.yacoco3e.datatypes.*; -import static ca.craigthomas.yacoco3e.datatypes.AddressingMode.IMMEDIATE; import static ca.craigthomas.yacoco3e.datatypes.AddressingMode.INDEXED; import static ca.craigthomas.yacoco3e.datatypes.RegisterSet.CC_E; @@ -54,6 +53,7 @@ public int call(IOController io) { * Waits for an interrupt to occur. */ public static void sync(IOController io, UnsignedByte memoryByte, UnsignedWord address) { + io.waitForIRQ = true; } /** @@ -77,6 +77,7 @@ public static void callAndWaitForInterrupt(IOController io, UnsignedByte memoryB io.pushStack(Register.S, io.regs.b); io.pushStack(Register.S, io.regs.a); io.pushStack(Register.S, io.regs.cc); + io.waitForIRQ = true; } /** diff --git a/src/test/java/ca/craigthomas/yacoco3e/components/ByteRegisterInstructionTest.java b/src/test/java/ca/craigthomas/yacoco3e/components/ByteRegisterInstructionTest.java index e4e9167..2344e30 100644 --- a/src/test/java/ca/craigthomas/yacoco3e/components/ByteRegisterInstructionTest.java +++ b/src/test/java/ca/craigthomas/yacoco3e/components/ByteRegisterInstructionTest.java @@ -2353,4 +2353,53 @@ public void testSubtractByteWithCarryCarrySetRegression1() { assertFalse(io.regs.cc.isMasked(CC_C)); assertFalse(io.regs.cc.isMasked(CC_N)); } + + @Test + public void testDecimalAdditionAdjustWorksCorrectly() { + regs.a.set(0x64); + ByteRegisterInstruction.addByte(io, regs.a, new UnsignedByte(0x27), null); + ByteRegisterInstruction.decimalAdditionAdjust(io, regs.a, null, null); + assertEquals(0x91, regs.a.getShort()); + assertFalse(regs.cc.isMasked(CC_C)); + } + + @Test + public void testDecimalAdditionAdjustWorksCorrectlyTest2() { + regs.a.set(0x80); + ByteRegisterInstruction.addByte(io, regs.a, new UnsignedByte(0x0A), null); + ByteRegisterInstruction.decimalAdditionAdjust(io, regs.a, null, null); + assertEquals(0x90, regs.a.getShort()); + assertFalse(regs.cc.isMasked(CC_C)); + assertTrue(regs.cc.isMasked(CC_N)); + } + + @Test + public void testDecimalAdditionAdjustWorksCorrectlyTest3SetsCarry() { + regs.a.set(0xA0); + ByteRegisterInstruction.addByte(io, regs.a, new UnsignedByte(0x00), null); + ByteRegisterInstruction.decimalAdditionAdjust(io, regs.a, null, null); + assertEquals(0x00, regs.a.getShort()); + assertTrue(regs.cc.isMasked(CC_C)); + } + + @Test + public void testDecimalAdditionAdjustWorksCorrectlyTest4() { + regs.a.set(0x05); + ByteRegisterInstruction.addByte(io, regs.a, new UnsignedByte(0x06), null); + ByteRegisterInstruction.decimalAdditionAdjust(io, regs.a, null, null); + assertEquals(0x11, regs.a.getShort()); + assertFalse(regs.cc.isMasked(CC_C)); + assertFalse(regs.cc.isMasked(CC_N)); + } + + @Test + public void testDecimalAdditionAdjustWorksCorrectlyTest5() { + regs.a.set(0x0F); + ByteRegisterInstruction.addByte(io, regs.a, new UnsignedByte(0x01), null); + assertTrue(regs.cc.isMasked(CC_H)); + ByteRegisterInstruction.decimalAdditionAdjust(io, regs.a, null, null); + assertEquals(0x16, regs.a.getShort()); + assertFalse(regs.cc.isMasked(CC_C)); + assertFalse(regs.cc.isMasked(CC_N)); + } } diff --git a/src/test/java/ca/craigthomas/yacoco3e/components/CPUIntegrationTest.java b/src/test/java/ca/craigthomas/yacoco3e/components/CPUIntegrationTest.java index 591246d..a6f592c 100644 --- a/src/test/java/ca/craigthomas/yacoco3e/components/CPUIntegrationTest.java +++ b/src/test/java/ca/craigthomas/yacoco3e/components/CPUIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Craig Thomas + * Copyright (C) 2017-2025 Craig Thomas * This project uses an MIT style license - see LICENSE for details. */ package ca.craigthomas.yacoco3e.components; diff --git a/src/test/java/ca/craigthomas/yacoco3e/components/CPUTest.java b/src/test/java/ca/craigthomas/yacoco3e/components/CPUTest.java new file mode 100644 index 0000000..6ee768b --- /dev/null +++ b/src/test/java/ca/craigthomas/yacoco3e/components/CPUTest.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2022-2025 Craig Thomas + * This project uses an MIT style license - see LICENSE for details. + */ +package ca.craigthomas.yacoco3e.components; + +import ca.craigthomas.yacoco3e.datatypes.RegisterSet; +import ca.craigthomas.yacoco3e.datatypes.UnsignedByte; +import ca.craigthomas.yacoco3e.datatypes.UnsignedWord; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CPUTest +{ + private Memory memory; + private RegisterSet regs; + private CPU cpu; + + @Before + public void setup() throws MalformedInstructionException { + memory = new Memory(); + regs = new RegisterSet(); + IOController io = new IOController(memory, regs, new EmulatedKeyboard(), new Screen(1), new Cassette()); + cpu = new CPU(io); + io.setCPU(cpu); + } + + @Test + public void testExecuteThrowsMalformedInstructionException() { + regs.pc.set(0); + memory.writeByte(0x0000, 0x6F); // CLRM + memory.writeByte(0x0001, 0x97); // Bad postbyte + assertThrows(MalformedInstructionException.class, () -> cpu.executeInstruction()); + } + + @Test + public void testInterrruptRequestWorksCorrectly() { + regs.s.set(0x5000); + regs.pc.set(0x1234); + regs.u.set(0x5678); + regs.y.set(0x9ABC); + regs.x.set(0xDEF1); + regs.dp.set(0x32); + regs.b.set(0x54); + regs.a.set(0x76); + regs.cc.set(0x01); + cpu.interruptRequest(); + assertEquals(new UnsignedByte(0x34), memory.readByte(0x4FFF)); + assertEquals(new UnsignedByte(0x12), memory.readByte(0x4FFE)); + assertEquals(new UnsignedByte(0x78), memory.readByte(0x4FFD)); + assertEquals(new UnsignedByte(0x56), memory.readByte(0x4FFC)); + assertEquals(new UnsignedByte(0xBC), memory.readByte(0x4FFB)); + assertEquals(new UnsignedByte(0x9A), memory.readByte(0x4FFA)); + assertEquals(new UnsignedByte(0xF1), memory.readByte(0x4FF9)); + assertEquals(new UnsignedByte(0xDE), memory.readByte(0x4FF8)); + assertEquals(new UnsignedByte(0x32), memory.readByte(0x4FF7)); + assertEquals(new UnsignedByte(0x54), memory.readByte(0x4FF6)); + assertEquals(new UnsignedByte(0x76), memory.readByte(0x4FF5)); + assertEquals(new UnsignedByte(0x81), memory.readByte(0x4FF4)); + assertEquals(new UnsignedWord(0x0000), regs.pc); + assertEquals(new UnsignedByte(0x91), regs.cc); + } + + @Test + public void testServiceInterruptsFiresIRQ() { + cpu.fireIRQ = true; + regs.s.set(0x5000); + regs.pc.set(0x1234); + regs.u.set(0x5678); + regs.y.set(0x9ABC); + regs.x.set(0xDEF1); + regs.dp.set(0x32); + regs.b.set(0x54); + regs.a.set(0x76); + regs.cc.set(0x01); + cpu.serviceInterrupts(); + assertEquals(new UnsignedByte(0x34), memory.readByte(0x4FFF)); + assertEquals(new UnsignedByte(0x12), memory.readByte(0x4FFE)); + assertEquals(new UnsignedByte(0x78), memory.readByte(0x4FFD)); + assertEquals(new UnsignedByte(0x56), memory.readByte(0x4FFC)); + assertEquals(new UnsignedByte(0xBC), memory.readByte(0x4FFB)); + assertEquals(new UnsignedByte(0x9A), memory.readByte(0x4FFA)); + assertEquals(new UnsignedByte(0xF1), memory.readByte(0x4FF9)); + assertEquals(new UnsignedByte(0xDE), memory.readByte(0x4FF8)); + assertEquals(new UnsignedByte(0x32), memory.readByte(0x4FF7)); + assertEquals(new UnsignedByte(0x54), memory.readByte(0x4FF6)); + assertEquals(new UnsignedByte(0x76), memory.readByte(0x4FF5)); + assertEquals(new UnsignedByte(0x81), memory.readByte(0x4FF4)); + assertEquals(new UnsignedWord(0x0000), regs.pc); + assertEquals(new UnsignedByte(0x91), regs.cc); + } + + @Test + public void testFastInterrruptRequestWorksCorrectly() { + regs.s.set(0x5000); + regs.pc.set(0x1234); + regs.cc.set(0x01); + cpu.fastInterruptRequest(); + assertEquals(new UnsignedByte(0x34), memory.readByte(0x4FFF)); + assertEquals(new UnsignedByte(0x12), memory.readByte(0x4FFE)); + assertEquals(new UnsignedByte(0x01), memory.readByte(0x4FFD)); + assertEquals(new UnsignedWord(0x0000), regs.pc); + assertEquals(new UnsignedByte(0x51), regs.cc); + } + + @Test + public void testServiceInterruptsFiresFIRQ() { + cpu.fireFIRQ = true; + regs.s.set(0x5000); + regs.pc.set(0x1234); + regs.cc.set(0x01); + cpu.serviceInterrupts(); + assertEquals(new UnsignedByte(0x34), memory.readByte(0x4FFF)); + assertEquals(new UnsignedByte(0x12), memory.readByte(0x4FFE)); + assertEquals(new UnsignedByte(0x01), memory.readByte(0x4FFD)); + assertEquals(new UnsignedWord(0x0000), regs.pc); + assertEquals(new UnsignedByte(0x51), regs.cc); + } + + @Test + public void testNMIRequestWorksCorrectly() { + regs.s.set(0x5000); + regs.pc.set(0x1234); + regs.u.set(0x5678); + regs.y.set(0x9ABC); + regs.x.set(0xDEF1); + regs.dp.set(0x32); + regs.b.set(0x54); + regs.a.set(0x76); + regs.cc.set(0x01); + cpu.nonMaskableInterruptRequest(); + assertEquals(new UnsignedByte(0x34), memory.readByte(0x4FFF)); + assertEquals(new UnsignedByte(0x12), memory.readByte(0x4FFE)); + assertEquals(new UnsignedByte(0x78), memory.readByte(0x4FFD)); + assertEquals(new UnsignedByte(0x56), memory.readByte(0x4FFC)); + assertEquals(new UnsignedByte(0xBC), memory.readByte(0x4FFB)); + assertEquals(new UnsignedByte(0x9A), memory.readByte(0x4FFA)); + assertEquals(new UnsignedByte(0xF1), memory.readByte(0x4FF9)); + assertEquals(new UnsignedByte(0xDE), memory.readByte(0x4FF8)); + assertEquals(new UnsignedByte(0x32), memory.readByte(0x4FF7)); + assertEquals(new UnsignedByte(0x54), memory.readByte(0x4FF6)); + assertEquals(new UnsignedByte(0x76), memory.readByte(0x4FF5)); + assertEquals(new UnsignedByte(0x81), memory.readByte(0x4FF4)); + assertEquals(new UnsignedWord(0x0000), regs.pc); + assertEquals(new UnsignedByte(0xD1), regs.cc); + } + + @Test + public void testServiceInterruptsFiresNMI() { + cpu.fireNMI = true; + regs.s.set(0x5000); + regs.pc.set(0x1234); + regs.u.set(0x5678); + regs.y.set(0x9ABC); + regs.x.set(0xDEF1); + regs.dp.set(0x32); + regs.b.set(0x54); + regs.a.set(0x76); + regs.cc.set(0x01); + cpu.serviceInterrupts(); + assertEquals(new UnsignedByte(0x34), memory.readByte(0x4FFF)); + assertEquals(new UnsignedByte(0x12), memory.readByte(0x4FFE)); + assertEquals(new UnsignedByte(0x78), memory.readByte(0x4FFD)); + assertEquals(new UnsignedByte(0x56), memory.readByte(0x4FFC)); + assertEquals(new UnsignedByte(0xBC), memory.readByte(0x4FFB)); + assertEquals(new UnsignedByte(0x9A), memory.readByte(0x4FFA)); + assertEquals(new UnsignedByte(0xF1), memory.readByte(0x4FF9)); + assertEquals(new UnsignedByte(0xDE), memory.readByte(0x4FF8)); + assertEquals(new UnsignedByte(0x32), memory.readByte(0x4FF7)); + assertEquals(new UnsignedByte(0x54), memory.readByte(0x4FF6)); + assertEquals(new UnsignedByte(0x76), memory.readByte(0x4FF5)); + assertEquals(new UnsignedByte(0x81), memory.readByte(0x4FF4)); + assertEquals(new UnsignedWord(0x0000), regs.pc); + assertEquals(new UnsignedByte(0xD1), regs.cc); + } + + @Test + public void testScheduleIRQ() { + assertFalse(cpu.fireIRQ); + cpu.scheduleIRQ(); + assertTrue(cpu.fireIRQ); + } + + @Test + public void testScheduleFIRQ() { + assertFalse(cpu.fireFIRQ); + cpu.scheduleFIRQ(); + assertTrue(cpu.fireFIRQ); + } + + @Test + public void testScheduleNMI() { + assertFalse(cpu.fireNMI); + cpu.scheduleNMI(); + assertTrue(cpu.fireNMI); + } +} diff --git a/src/test/java/ca/craigthomas/yacoco3e/components/IOControllerTest.java b/src/test/java/ca/craigthomas/yacoco3e/components/IOControllerTest.java index dac0e91..94793b3 100644 --- a/src/test/java/ca/craigthomas/yacoco3e/components/IOControllerTest.java +++ b/src/test/java/ca/craigthomas/yacoco3e/components/IOControllerTest.java @@ -327,7 +327,7 @@ public void testGIMEHorizontalBorderInterruptFiresCorrectly() throws MalformedIn memory.rom[0x3FF8] = (short) 0xDE; memory.rom[0x3FF9] = (short) 0xAD; - regs.s.set(new UnsignedWord(0x0300)); + regs.s.set(0x0300); io.horizontalBorderTickValue = 999999; io.irqEnabled = true; io.irqStatus = new UnsignedByte(0x10); @@ -335,7 +335,7 @@ public void testGIMEHorizontalBorderInterruptFiresCorrectly() throws MalformedIn cpu.executeInstruction(); cpu.serviceInterrupts(); - assertEquals(new UnsignedWord(0xDEAD), io.getWordRegister(Register.PC)); + assertEquals(0xDEAD, io.getWordRegister(Register.PC).getInt()); } @Test diff --git a/src/test/java/ca/craigthomas/yacoco3e/components/VoidInstructionTest.java b/src/test/java/ca/craigthomas/yacoco3e/components/VoidInstructionTest.java index cf43571..23433af 100644 --- a/src/test/java/ca/craigthomas/yacoco3e/components/VoidInstructionTest.java +++ b/src/test/java/ca/craigthomas/yacoco3e/components/VoidInstructionTest.java @@ -28,6 +28,12 @@ public void setUp() { io.regs.pc.set(0); } + @Test + public void testSync() { + VoidInstruction.sync(io, null, null); + assertTrue(io.waitForIRQ); + } + @Test public void testUnconditionalJumpCorrect() { VoidInstruction.unconditionalJump(io, null, new UnsignedWord(0xBEEF)); diff --git a/src/test/java/ca/craigthomas/yacoco3e/datatypes/UnsignedWordTest.java b/src/test/java/ca/craigthomas/yacoco3e/datatypes/UnsignedWordTest.java index a431017..046aefd 100644 --- a/src/test/java/ca/craigthomas/yacoco3e/datatypes/UnsignedWordTest.java +++ b/src/test/java/ca/craigthomas/yacoco3e/datatypes/UnsignedWordTest.java @@ -34,6 +34,12 @@ public void testDefaultConstructorSetsZero() { assertEquals(0, result.getInt()); } + @Test + public void testAlternateConstructor() { + UnsignedWord result = new UnsignedWord(0xC0, new UnsignedByte(0x11)); + assertEquals(0xC011, result.getInt()); + } + @Test public void testHighSetsHighByteOnly() { UnsignedWord result = new UnsignedWord();