diff --git a/OSBindings/Mac/Clock SignalTests/Z80InterruptTests.swift b/OSBindings/Mac/Clock SignalTests/Z80InterruptTests.swift index 0fca9435f..423743a27 100644 --- a/OSBindings/Mac/Clock SignalTests/Z80InterruptTests.swift +++ b/OSBindings/Mac/Clock SignalTests/Z80InterruptTests.swift @@ -73,7 +73,46 @@ class Z80InterruptTests: XCTestCase { XCTAssertEqual(machine.value(for: .programCounter), 0x0105) } + func testIRQMode0() { + // In interrupt mode 0, receipt of an IRQ causes an instruction to be read from the bus and + // executed, with a two-cycle penalty. The test machine posts 0x21 during an interrupt acknowledge + // cycle so the instruction that is executed will be LD HL, nnnn + + let machine = CSTestMachineZ80() + + // start the PC at 0x0100 and install two NOPs for it and then one copy of 0x83, then + // something that isn't 0x83, and ensuring interrupts are enabled and in mode 1 + machine.setValue(0x0100, for: .programCounter) + machine.setValue(1, for: .IFF1) + machine.setValue(0, for: .IM) + machine.setValue(0x1010, for: .HL) + machine.setValue(0x00, atAddress: 0x0100) + machine.setValue(0x00, atAddress: 0x0101) + machine.setValue(0x83, atAddress: 0x0102) + machine.setValue(0x9b, atAddress: 0x0103) + + // run for four cycles, and signal an IRQ + machine.runForNumber(ofCycles: 4) + machine.irqLine = true + + // run for four more cycles to get to where the IRQ should be recognised + machine.runForNumber(ofCycles: 4) + XCTAssertEqual(machine.value(for: .programCounter), 0x0102) + + // run for twelve more cycles, to complete a LD HL, nnnn with the additional two cycles + // of cost for it being an IRQ program + machine.runForNumber(ofCycles: 12) + + // confirm that the PC is still where it was, but HL is now 0x8383, and interrupts are disabled + XCTAssertEqual(machine.value(for: .programCounter), 0x0102) + XCTAssertEqual(machine.value(for: .HL), 0x8383) + XCTAssertEqual(machine.value(for: .IFF1), 0) + } + func testIRQMode1() { + // In interrupt mode 1, receipt of an IRQ means that the interrupt flag is disabled, + // the PC is pushed to the stack and execution resumes at 0x38. + let machine = CSTestMachineZ80() // start the PC at 0x0100 and install three NOPs for it, ensuring interrupts are enabled @@ -106,4 +145,47 @@ class Z80InterruptTests: XCTestCase { XCTAssertEqual(machine.value(atAddress: 0xfffe), 0x02) XCTAssertEqual(machine.value(for: .IFF1), 0) } + + func testIRQMode2() { + // In interrupt mode 2, the current bus value is combined with the I register to look + // up a vector from memory; the PC calls that vector. Total cost: 19 cycles. + + let machine = CSTestMachineZ80() + + // start the PC at 0x0100 and install two NOPs for it and then one copy of 0x83, then + // something that isn't 0x83, and ensuring interrupts are enabled and in mode 1 + machine.setValue(0x0100, for: .programCounter) + machine.setValue(1, for: .IFF1) + machine.setValue(2, for: .IM) + machine.setValue(0x00, atAddress: 0x0100) + machine.setValue(0x00, atAddress: 0x0101) + + // set I to 0x0200 to establish the location of our vector table, and because the test + // machine will post a 0x21 in response to the interrupt cycle, at 0x0221 put the + // arbitrarily-chosen address 0x8049 + machine.setValue(0x02, for: .I) + machine.setValue(0x49, atAddress: 0x0221) + machine.setValue(0x80, atAddress: 0x0222) + + // put the stack at the top of memory + machine.setValue(0, for: .stackPointer) + + // run for four cycles, and signal an IRQ + machine.runForNumber(ofCycles: 4) + machine.irqLine = true + + // run for four more cycles to get to where the IRQ should be recognised + machine.runForNumber(ofCycles: 4) + XCTAssertEqual(machine.value(for: .programCounter), 0x0102) + + // run for nineteen more cycles, to complete the interrupt beginning + machine.runForNumber(ofCycles: 19) + + // confirm that the PC is now at 0x8049, the old is on the stack, and interrupts + // are disabled + XCTAssertEqual(machine.value(for: .programCounter), 0x8049) + XCTAssertEqual(machine.value(atAddress: 0xffff), 0x01) + XCTAssertEqual(machine.value(atAddress: 0xfffe), 0x02) + XCTAssertEqual(machine.value(for: .IFF1), 0) + } } diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index c04111a99..e6aadd979 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -707,7 +707,7 @@ template class Processor: public MicroOpScheduler { }; MicroOp irq_mode0_program[] = { { MicroOp::BeginIRQMode0 }, - { MicroOp::BusOperation, nullptr, nullptr, {BusOperation::Interrupt, 7, nullptr, &operation_}}, + { MicroOp::BusOperation, nullptr, nullptr, {BusOperation::Interrupt, 6, nullptr, &operation_}}, { MicroOp::DecodeOperationNoRChange } }; MicroOp irq_mode1_program[] = {