1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-28 22:30:30 +00:00

Merge pull request #137 from TomHarte/NMIWaitTest

Introduces an NMI/wait interrupt timing test
This commit is contained in:
Thomas Harte 2017-06-22 21:11:54 -04:00 committed by GitHub
commit 3dfe45d225
6 changed files with 65 additions and 8 deletions

View File

@ -64,5 +64,6 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) {
@property(nonatomic) BOOL nmiLine; @property(nonatomic) BOOL nmiLine;
@property(nonatomic) BOOL irqLine; @property(nonatomic) BOOL irqLine;
@property(nonatomic) BOOL waitLine;
@end @end

View File

@ -167,6 +167,11 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
_processor->set_interrupt_line(irqLine ? true : false); _processor->set_interrupt_line(irqLine ? true : false);
} }
- (void)setWaitLine:(BOOL)waitLine {
_waitLine = waitLine;
_processor->set_wait_line(waitLine ? true : false);
}
- (CPU::AllRAMProcessor *)processor { - (CPU::AllRAMProcessor *)processor {
return _processor; return _processor;
} }

View File

@ -10,6 +10,15 @@ import XCTest
class Z80InterruptTests: XCTestCase { class Z80InterruptTests: XCTestCase {
private func assertNMI(machine: CSTestMachineZ80) {
// confirm that the PC is now at 0x66, that the old is on the stack and
// that IFF1 has migrated to IFF2
XCTAssertEqual(machine.value(for: .programCounter), 0x66)
XCTAssertEqual(machine.value(atAddress: 0xffff), 0x01)
XCTAssertEqual(machine.value(atAddress: 0xfffe), 0x02)
XCTAssertEqual(machine.value(for: .IFF2), 0)
}
func testNMI() { func testNMI() {
let machine = CSTestMachineZ80() let machine = CSTestMachineZ80()
@ -34,12 +43,45 @@ class Z80InterruptTests: XCTestCase {
// run for eleven more cycles to allow the NMI to begin // run for eleven more cycles to allow the NMI to begin
machine.runForNumber(ofCycles: 11) machine.runForNumber(ofCycles: 11)
// confirm that the PC is now at 0x66, that the old is on the stack and assertNMI(machine: machine)
// that IFF1 has migrated to IFF2 }
XCTAssertEqual(machine.value(for: .programCounter), 0x66)
XCTAssertEqual(machine.value(atAddress: 0xffff), 0x01) func testHaltNMIWait() {
XCTAssertEqual(machine.value(atAddress: 0xfffe), 0x02) let machine = CSTestMachineZ80()
XCTAssertEqual(machine.value(for: .IFF2), 0)
// start the PC at 0x0100 and install a NOP and a HALT for it
machine.setValue(0x0100, for: .programCounter)
machine.setValue(0, for: .IFF1)
machine.setValue(1, for: .IFF2)
machine.setValue(0x00, atAddress: 0x0100)
machine.setValue(0x76, atAddress: 0x0101)
// put the stack at the top of memory
machine.setValue(0, for: .stackPointer)
// run for ten cycles, check that the processor is halted and assert an NMI
machine.runForNumber(ofCycles: 10)
XCTAssert(machine.isHalted, "Machine should be halted")
machine.nmiLine = true
// check that the machine ceases believing itsef to be halted after two cycles
machine.runForNumber(ofCycles: 1)
XCTAssert(machine.isHalted, "Machine should still be halted")
machine.runForNumber(ofCycles: 1)
XCTAssert(!machine.isHalted, "Machine should no longer be halted")
// assert wait
machine.waitLine = true
// run for twenty cycles, an arbitrary big number
machine.runForNumber(ofCycles: 20)
// release wait
machine.waitLine = false
// NMI should have run for two cycles, then waited, so now there should be nine cycles left
machine.runForNumber(ofCycles: 9)
assertNMI(machine: machine)
} }
func testIRQDisabled() { func testIRQDisabled() {

View File

@ -803,12 +803,15 @@ template <class T> class Processor {
assemble_fetch_decode_execute(ddcb_page_, 3); assemble_fetch_decode_execute(ddcb_page_, 3);
MicroOp reset_program[] = Sequence(InternalOperation(3), {MicroOp::Reset}); MicroOp reset_program[] = Sequence(InternalOperation(3), {MicroOp::Reset});
// Justification for NMI timing: per Wilf Rigter on the ZX81 (http://www.user.dccnet.com/wrigter/index_files/ZX81WAIT.htm),
// wait cycles occur between T2 and T3 during NMI; extending the refresh cycle is also consistent with my guess
// for the action of other non-four-cycle opcode fetches
MicroOp nmi_program[] = { MicroOp nmi_program[] = {
{ MicroOp::BeginNMI }, { MicroOp::BeginNMI },
BusOp(ReadOpcodeStart()), BusOp(ReadOpcodeStart()),
BusOp(ReadOpcodeWait(1, false)),
BusOp(ReadOpcodeWait(1, true)), BusOp(ReadOpcodeWait(1, true)),
BusOp(Refresh(2)), BusOp(Refresh(3)),
Push(pc_), Push(pc_),
{ MicroOp::JumpTo66, nullptr, nullptr}, { MicroOp::JumpTo66, nullptr, nullptr},
{ MicroOp::MoveToNextProgram } { MicroOp::MoveToNextProgram }

View File

@ -90,6 +90,10 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
void set_non_maskable_interrupt_line(bool value) { void set_non_maskable_interrupt_line(bool value) {
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_non_maskable_interrupt_line(value); CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_non_maskable_interrupt_line(value);
} }
void set_wait_line(bool value) {
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_wait_line(value);
}
}; };
} }

View File

@ -33,8 +33,10 @@ class AllRAMProcessor:
virtual void set_value_of_register(Register r, uint16_t value) = 0; virtual void set_value_of_register(Register r, uint16_t value) = 0;
virtual bool get_halt_line() = 0; virtual bool get_halt_line() = 0;
virtual void reset_power_on() = 0; virtual void reset_power_on() = 0;
virtual void set_interrupt_line(bool value) = 0; virtual void set_interrupt_line(bool value) = 0;
virtual void set_non_maskable_interrupt_line(bool value) = 0; virtual void set_non_maskable_interrupt_line(bool value) = 0;
virtual void set_wait_line(bool value) = 0;
protected: protected:
MemoryAccessDelegate *delegate_; MemoryAccessDelegate *delegate_;