diff --git a/Components/6532/6532.hpp b/Components/6532/6532.hpp index d2f68da2d..0b42515bf 100644 --- a/Components/6532/6532.hpp +++ b/Components/6532/6532.hpp @@ -38,10 +38,12 @@ template class MOS6532 { case 0x00: case 0x02: _port[decodedAddress / 2].output = value; static_cast(this)->set_port_output(decodedAddress / 2, _port[decodedAddress/2].output, _port[decodedAddress / 2].output_mask); + set_port_did_change(decodedAddress / 2); break; case 0x01: case 0x03: _port[decodedAddress / 2].output_mask = value; static_cast(this)->set_port_output(decodedAddress / 2, _port[decodedAddress/2].output, _port[decodedAddress / 2].output_mask); + set_port_did_change(decodedAddress / 2); break; // The timer and edge detect control @@ -125,7 +127,10 @@ template class MOS6532 { } MOS6532() : - _interrupt_status(0), _port{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}}, _a7_interrupt({.last_port_value = 0, .enabled = false}) + _interrupt_status(0), + _port{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}}, + _a7_interrupt({.last_port_value = 0, .enabled = false}), + _interrupt_line(false) {} inline void set_port_did_change(int port) @@ -149,6 +154,11 @@ template class MOS6532 { } } + inline bool get_inerrupt_line() + { + return _interrupt_line; + } + private: uint8_t _ram[128]; @@ -173,6 +183,7 @@ template class MOS6532 { Timer = 0x80, PA7 = 0x40 }; + bool _interrupt_line; // expected to be overridden uint8_t get_port_input(int port) { return 0xff; } @@ -181,10 +192,10 @@ template class MOS6532 { inline void evaluate_interrupts() { - set_irq_line( + _interrupt_line = ((_interrupt_status&InterruptFlag::Timer) && _timer.interrupt_enabled) || - ((_interrupt_status&InterruptFlag::PA7) && _a7_interrupt.enabled) - ); + ((_interrupt_status&InterruptFlag::PA7) && _a7_interrupt.enabled); + set_irq_line(_interrupt_line); } }; diff --git a/OSBindings/Mac/Clock SignalTests/6532Tests.swift b/OSBindings/Mac/Clock SignalTests/6532Tests.swift index 77bf396c9..8432f572a 100644 --- a/OSBindings/Mac/Clock SignalTests/6532Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/6532Tests.swift @@ -20,7 +20,7 @@ class MOS6532Tests: XCTestCase { func testOneTickTimer() { with6532 { // set a count of 128 at single-clock intervals - $0.setValue(128, forRegister:4) + $0.setValue(128, forRegister:0x14) // run for one clock and the count should now be 127 $0.runForCycles(1) @@ -32,11 +32,11 @@ class MOS6532Tests: XCTestCase { } } - // TODO: the test below makes the assumption that divider phase is flexible; verify + // TODO: the tests below makes the assumption that divider phase is flexible; verify func testEightTickTimer() { with6532 { // set a count of 28 at eight-clock intervals - $0.setValue(28, forRegister:5) + $0.setValue(28, forRegister:0x15) // run for seven clock and the count should still be 28 $0.runForCycles(7) @@ -59,4 +59,96 @@ class MOS6532Tests: XCTestCase { XCTAssert($0.valueForRegister(4) == 0xfa, "Timer should decrement after eighth cycle") } } + + func testTimerInterrupt() { + with6532 { + // set a count of 1 at single-clock intervals + $0.setValue(1, forRegister:0x1c) + + // run for one clock and the count should now be zero + $0.runForCycles(1) + + // interrupt shouldn't be signalled yet, bit should not be set + XCTAssert(!$0.irqLine, "IRQ line should not be set") + XCTAssert($0.valueForRegister(5) == 0x00, "Counter interrupt should not be set") + + // run for one more clock + $0.runForCycles(1) + + // the interrupt line and bit should now be set + XCTAssert($0.irqLine, "IRQ line should be set") + XCTAssert($0.valueForRegister(5) == 0x80, "Counter interrupt should be set") + + // writing again to the timer should clear both + $0.setValue(1, forRegister:0x1c) + XCTAssert(!$0.irqLine, "IRQ line should be clear") + XCTAssert($0.valueForRegister(5) == 0x00, "Counter interrupt should not be set") + } + } + + + // MARK: PA7 interrupt tests + func testPA7InterruptDisabled() { + with6532 { + // disable edge detection + $0.setValue(0, forRegister:4) + + // set output mode for port a + $0.setValue(0xff, forRegister:1) + + // toggle bit 7 of port a in both directions + $0.setValue(0x80, forRegister:0) + $0.setValue(0x00, forRegister:0) + $0.setValue(0x80, forRegister:0) + + // confirm that the interrupt flag is set but the line is not + XCTAssert(!$0.irqLine, "IRQ line should not be set") + XCTAssert($0.valueForRegister(5) == 0x40, "Timer interrupt bit should be set") + + // reading the status register should have reset the interrupt flag + XCTAssert($0.valueForRegister(5) == 0x00, "Timer interrupt bit should be reset") + } + } + + func testPA7LeadingEdge() { + with6532 { + // seed port a is high; ensure interrupt bit is clear + $0.setValue(0x00, forRegister:0) + $0.valueForRegister(5) + + // enable leading edge detection + $0.setValue(0, forRegister:7) + + // set output mode for port a + $0.setValue(0xff, forRegister:1) + + // toggle bit 7 of port a in a leading direction + $0.setValue(0x80, forRegister:0) + + // confirm that both the interrupt flag are the line are set + XCTAssert($0.irqLine, "IRQ line should be set") + XCTAssert($0.valueForRegister(5) == 0x40, "Timer interrupt bit should be set") + } + } + + func testPA7TrailingEdge() { + with6532 { + // seed port a is high; ensure interrupt bit is clear + $0.setValue(0x80, forRegister:0) + $0.valueForRegister(5) + + // enable trailing edge detection + $0.setValue(0, forRegister:6) + + // set output mode for port a + $0.setValue(0xff, forRegister:1) + + // toggle bit 7 of port a in a rising direction + $0.setValue(0x00, forRegister:0) + + // confirm that both the interrupt flag are the line are set + XCTAssert($0.irqLine, "IRQ line should be set") + XCTAssert($0.valueForRegister(5) == 0x40, "Timer interrupt bit should be set") + } + } } diff --git a/OSBindings/Mac/Clock SignalTests/MOS6532Bridge.mm b/OSBindings/Mac/Clock SignalTests/MOS6532Bridge.mm index c7db4c24f..884f199f2 100644 --- a/OSBindings/Mac/Clock SignalTests/MOS6532Bridge.mm +++ b/OSBindings/Mac/Clock SignalTests/MOS6532Bridge.mm @@ -32,4 +32,9 @@ class VanillaRIOT: public MOS::MOS6532 { _riot.run_for_cycles((int)numberOfCycles); } +- (BOOL)irqLine +{ + return _riot.get_inerrupt_line(); +} + @end