mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 16:31:31 +00:00
Merge pull request #137 from TomHarte/NMIWaitTest
Introduces an NMI/wait interrupt timing test
This commit is contained in:
commit
3dfe45d225
@ -64,5 +64,6 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) {
|
||||
|
||||
@property(nonatomic) BOOL nmiLine;
|
||||
@property(nonatomic) BOOL irqLine;
|
||||
@property(nonatomic) BOOL waitLine;
|
||||
|
||||
@end
|
||||
|
@ -167,6 +167,11 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
||||
_processor->set_interrupt_line(irqLine ? true : false);
|
||||
}
|
||||
|
||||
- (void)setWaitLine:(BOOL)waitLine {
|
||||
_waitLine = waitLine;
|
||||
_processor->set_wait_line(waitLine ? true : false);
|
||||
}
|
||||
|
||||
- (CPU::AllRAMProcessor *)processor {
|
||||
return _processor;
|
||||
}
|
||||
|
@ -10,6 +10,15 @@ import XCTest
|
||||
|
||||
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() {
|
||||
let machine = CSTestMachineZ80()
|
||||
|
||||
@ -34,12 +43,45 @@ class Z80InterruptTests: XCTestCase {
|
||||
// run for eleven more cycles to allow the NMI to begin
|
||||
machine.runForNumber(ofCycles: 11)
|
||||
|
||||
// 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)
|
||||
assertNMI(machine: machine)
|
||||
}
|
||||
|
||||
func testHaltNMIWait() {
|
||||
let machine = CSTestMachineZ80()
|
||||
|
||||
// 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() {
|
||||
|
@ -803,12 +803,15 @@ template <class T> class Processor {
|
||||
assemble_fetch_decode_execute(ddcb_page_, 3);
|
||||
|
||||
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::BeginNMI },
|
||||
BusOp(ReadOpcodeStart()),
|
||||
BusOp(ReadOpcodeWait(1, false)),
|
||||
BusOp(ReadOpcodeWait(1, true)),
|
||||
BusOp(Refresh(2)),
|
||||
BusOp(Refresh(3)),
|
||||
Push(pc_),
|
||||
{ MicroOp::JumpTo66, nullptr, nullptr},
|
||||
{ MicroOp::MoveToNextProgram }
|
||||
|
@ -90,6 +90,10 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
|
||||
void set_non_maskable_interrupt_line(bool 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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -33,8 +33,10 @@ class AllRAMProcessor:
|
||||
virtual void set_value_of_register(Register r, uint16_t value) = 0;
|
||||
virtual bool get_halt_line() = 0;
|
||||
virtual void reset_power_on() = 0;
|
||||
|
||||
virtual void set_interrupt_line(bool value) = 0;
|
||||
virtual void set_non_maskable_interrupt_line(bool value) = 0;
|
||||
virtual void set_wait_line(bool value) = 0;
|
||||
|
||||
protected:
|
||||
MemoryAccessDelegate *delegate_;
|
||||
|
Loading…
Reference in New Issue
Block a user