diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index c655ccf30..bc2bdbc58 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -111,6 +111,8 @@ template class MOS6522: public MOS6522Storage { private: void do_phase1(); void do_phase2(); + void shift_in(); + void shift_out(); T &bus_handler_; void access(int address); diff --git a/Components/6522/Implementation/6522Implementation.hpp b/Components/6522/Implementation/6522Implementation.hpp index d705d3885..9dbdba037 100644 --- a/Components/6522/Implementation/6522Implementation.hpp +++ b/Components/6522/Implementation/6522Implementation.hpp @@ -91,7 +91,7 @@ template void MOS6522::set_register(int address, uint8_t value) registers_.auxiliary_control = value; break; case 0xc: { - const auto old_peripheral_control = registers_.peripheral_control; +// const auto old_peripheral_control = registers_.peripheral_control; registers_.peripheral_control = value; int shift = 0; @@ -208,16 +208,26 @@ template void MOS6522::reevaluate_interrupts() { template void MOS6522::set_control_line_input(Port port, Line line, bool value) { switch(line) { case Line::One: - if( value != control_inputs_[port].lines[line] && - value == !!(registers_.peripheral_control & (port ? 0x10 : 0x01)) - ) { + if( value != control_inputs_[port].lines[line]) { // In handshake mode, any transition on C[A/B]1 sets output high on C[A/B]2. if(handshake_modes_[port] == HandshakeMode::Handshake) { set_control_line_output(port, Line::Two, true); } - registers_.interrupt_flags |= port ? InterruptFlag::CB1ActiveEdge : InterruptFlag::CA1ActiveEdge; - reevaluate_interrupts(); + // Set the proper transition interrupt bit if enabled. + if(value == !!(registers_.peripheral_control & (port ? 0x10 : 0x01))) { + registers_.interrupt_flags |= port ? InterruptFlag::CB1ActiveEdge : InterruptFlag::CA1ActiveEdge; + reevaluate_interrupts(); + } + + // If this is a low-to-high transition, consider updating the shift register. + if(value) { + switch((registers_.auxiliary_control >> 2)&7) { + default: break; + case 3: shift_in(); break; + case 7: shift_out(); break; + } + } } control_inputs_[port].lines[line] = value; break; @@ -269,6 +279,15 @@ template void MOS6522::do_phase1() { // IRQ is raised on the half cycle after overflow if((registers_.timer[1] == 0xffff) && !registers_.last_timer[1] && timer_is_running_[1]) { timer_is_running_[1] = false; + + // If the shift register is shifting according to this timer, do a shift. + switch((registers_.auxiliary_control >> 2)&7) { + default: break; + case 1: shift_in(); break; + case 4: shift_out(); break; + case 5: shift_out(); break; // TODO: present a clock on CB1. + } + registers_.interrupt_flags |= InterruptFlag::Timer2; reevaluate_interrupts(); } @@ -282,6 +301,13 @@ template void MOS6522::do_phase1() { else timer_is_running_[0] = false; } + + // If the shift register is shifting according to the input clock, do a shift. + switch((registers_.auxiliary_control >> 2)&7) { + default: break; + case 2: shift_in(); break; + case 6: shift_out(); break; + } } /*! Runs for a specified number of half cycles. */ @@ -335,5 +361,14 @@ template void MOS6522::set_control_line_output(Port port, Line l } } +template void MOS6522::shift_in() { + registers_.shift = uint8_t((registers_.shift << 1) | (control_inputs_[1].lines[1] ? 1 : 0)); +} + +template void MOS6522::shift_out() { + set_control_line_output(Port::B, Line::Two, registers_.shift & 0x80); + registers_.shift <<= 1; +} + } }