diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index d9c250db3..21c2971ca 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -14,8 +14,29 @@ namespace MOS { -template class MOS6522 { +class MOS6522Delegate { public: + virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0; +}; + +template class MOS6522 { + private: + enum InterruptFlag: uint8_t { + CA2ActiveEdge = 1 << 0, + CA1ActiveEdge = 1 << 1, + ShiftRegister = 1 << 2, + CB2ActiveEdge = 1 << 3, + CB1ActiveEdge = 1 << 4, + Timer2 = 1 << 5, + Timer1 = 1 << 6, + }; + + public: + void set_delegate(MOS6522Delegate *delegate) + { + _delegate = delegate; + } + MOS6522() : _data_direction{0, 0}, _timer_is_running{false, false} @@ -31,23 +52,31 @@ template class MOS6522 { case 0x06: case 0x04: _interval_timer_latch[0] = (_interval_timer_latch[0]&0xff00) | value; break; case 0x05: case 0x07: _interval_timer_latch[0] = (_interval_timer_latch[0]&0x00ff) | (uint16_t)(value << 8); - // TODO: clear interrupt flag + _interrupt_flags &= ~InterruptFlag::Timer1; if(address == 0x05) { _interval_timer[0] = _interval_timer_latch[0]; _timer_is_running[0] = true; } + reevaluate_interrupts(); break; // Timer 2 case 0x08: _interval_timer_latch[1] = value; break; case 0x09: - // TODO: clear interrupt flag + _interrupt_flags &= ~InterruptFlag::Timer2; _interval_timer[1] = _interval_timer_latch[1] | (uint16_t)(value << 8); _timer_is_running[1] = true; + reevaluate_interrupts(); break; - case 0x11: _auxiliary_control_register = value; break; + case 11: _auxiliary_control_register = value; break; + case 13: + if(!(value&0x80)) value = ~value; + _interrupt_mask = value; + break; + case 14: + break; } } @@ -59,11 +88,16 @@ template class MOS6522 { { // Timer 1 case 0x04: - // TODO: clear interrupt flag + _interrupt_flags &= ~InterruptFlag::Timer1; + reevaluate_interrupts(); return _interval_timer[0] & 0x00ff; case 0x05: return _interval_timer[0] >> 8; case 0x06: return _interval_timer_latch[0] & 0x00ff; case 0x07: return _interval_timer_latch[0] >> 8; + + case 11: return _auxiliary_control_register; + case 13: return _interrupt_flags; + case 14: return _interrupt_mask; } return 0xff; @@ -73,14 +107,48 @@ template class MOS6522 { { _interval_timer[0] -= number_of_cycles; _interval_timer[1] -= number_of_cycles; - // TODO: interrupts, potentially reload of the first timer, other status effects + + if(!_interval_timer[1] && _timer_is_running[1]) + { + _timer_is_running[1] = false; + _interrupt_flags |= InterruptFlag::Timer2; + reevaluate_interrupts(); + } + + if(!_interval_timer[0] && _timer_is_running[0]) + { + _interrupt_flags |= InterruptFlag::Timer1; + reevaluate_interrupts(); + + // TODO: reload shouldn't occur for a further 1.5 cycles + if(_auxiliary_control_register&0x40) + { + _interval_timer[0] = _interval_timer_latch[0]; + } + else + _timer_is_running[0] = false; + } + // TODO: lots of other status effects + } + + bool get_interrupt_line() + { + uint8_t interrupt_status = _interrupt_flags & (~_interrupt_mask) & 0x7f; + return !!interrupt_status; } private: + inline void reevaluate_interrupts() + { + if(_delegate) _delegate->mos6522_did_change_interrupt_status(this); + } + + MOS6522Delegate *_delegate; uint16_t _interval_timer[2], _interval_timer_latch[2]; uint8_t _shift_register; uint8_t _input_latch[2]; uint8_t _data_direction[2]; + uint8_t _interrupt_flags, _interrupt_mask; bool _timer_is_running[2]; diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index 1fe5e3205..c20391932 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -120,8 +120,8 @@ uint8_t MOS6560::get_register(int address) switch(address) { default: return _registers[address]; - case 0x03: return ((_vertical_counter >> 1) & 0x80) | (_registers[3] & 0x7f); - case 0x04: return _vertical_counter & 0xff; + case 0x03: return (uint8_t)(_vertical_counter << 7) | (_registers[3] & 0x7f); + case 0x04: return (_vertical_counter >> 1) & 0xff; } } diff --git a/Machines/Vic-20/Vic20.cpp b/Machines/Vic-20/Vic20.cpp index e35b0352c..3bf55f007 100644 --- a/Machines/Vic-20/Vic20.cpp +++ b/Machines/Vic-20/Vic20.cpp @@ -16,6 +16,8 @@ Machine::Machine() : _userPortVIA(new UserPortVIA()), _keyboardVIA(new KeyboardVIA()) { + _userPortVIA->set_delegate(this); + _keyboardVIA->set_delegate(this); set_reset_line(true); } @@ -68,6 +70,15 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin return 1; } +#pragma mark - 6522 delegate + +void Machine::mos6522_did_change_interrupt_status(void *mos6522) +{ + bool irq = _userPortVIA->get_interrupt_line() || _keyboardVIA->get_interrupt_line(); + printf("IRQ: %s\n", irq ? "e" : "-"); + set_irq_line(irq); +} + #pragma mark - Setup void Machine::setup_output(float aspect_ratio) diff --git a/Machines/Vic-20/Vic20.hpp b/Machines/Vic-20/Vic20.hpp index c82e6b60f..05092f96c 100644 --- a/Machines/Vic-20/Vic20.hpp +++ b/Machines/Vic-20/Vic20.hpp @@ -28,7 +28,7 @@ class UserPortVIA: public MOS::MOS6522 { class KeyboardVIA: public MOS::MOS6522 { }; -class Machine: public CPU6502::Processor, public CRTMachine::Machine { +class Machine: public CPU6502::Processor, public CRTMachine::Machine, public MOS::MOS6522Delegate { public: Machine(); @@ -46,6 +46,9 @@ class Machine: public CPU6502::Processor, public CRTMachine::Machine { virtual Outputs::Speaker *get_speaker() { return nullptr; } // TODO virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor::run_for_cycles(number_of_cycles); } + // to satisfy MOS::MOS6522::Delegate + virtual void mos6522_did_change_interrupt_status(void *mos6522); + private: uint8_t _characterROM[0x1000]; uint8_t _basicROM[0x2000];