diff --git a/Components/6532/6532.hpp b/Components/6532/6532.hpp index d47970cf1..d2f68da2d 100644 --- a/Components/6532/6532.hpp +++ b/Components/6532/6532.hpp @@ -10,6 +10,7 @@ #define _532_hpp #include +#include namespace MOS { @@ -33,24 +34,31 @@ template class MOS6532 { { const uint8_t decodedAddress = address & 0x07; switch(decodedAddress) { - case 0x00: - case 0x02: - static_cast(this)->set_port_output(decodedAddress / 2, value, _port[decodedAddress / 2].direction); - _port[decodedAddress/2].output = value; + // Port output + 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); + 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); break; - case 0x01: - case 0x03: - _port[decodedAddress / 2].direction = value; - break; - - case 0x04: - case 0x05: - case 0x06: - case 0x07: - _timer.writtenShift = _timer.activeShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10 - _timer.value = ((unsigned int)(value) << _timer.activeShift) | ((1 << _timer.activeShift)-1); - _timer.status &= ~0x80; + // The timer and edge detect control + case 0x04: case 0x05: case 0x06: case 0x07: + if(address & 0x10) + { + _timer.writtenShift = _timer.activeShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10 + _timer.value = ((unsigned int)(value) << _timer.activeShift) | ((1 << _timer.activeShift)-1); + _timer.interrupt_enabled = !!(address&0x08); + _interrupt_status &= ~InterruptFlag::Timer; + evaluate_interrupts(); + } + else + { + _a7_interrupt.enabled = !!(address&0x2); + _a7_interrupt.active_on_positive = !!(address & 0x01); + } break; } } @@ -59,22 +67,25 @@ template class MOS6532 { { const uint8_t decodedAddress = address & 0x7; switch(decodedAddress) { - case 0x00: - case 0x02: + // Port input + case 0x00: case 0x02: { const int port = decodedAddress / 2; uint8_t input = static_cast(this)->get_port_input(port); - return (input & ~_port[port].direction) | (_port[port].output & _port[port].direction); + return (input & ~_port[port].output_mask) | (_port[port].output & _port[port].output_mask); } break; - case 0x01: - case 0x03: - return _port[decodedAddress / 2].direction; + case 0x01: case 0x03: + return _port[decodedAddress / 2].output_mask; break; - case 0x04: - case 0x06: + + // Timer and interrupt control + case 0x04: case 0x06: { uint8_t value = (uint8_t)(_timer.value >> _timer.activeShift); + _timer.interrupt_enabled = !!(address&0x08); + _interrupt_status &= ~InterruptFlag::Timer; + evaluate_interrupts(); if(_timer.activeShift != _timer.writtenShift) { unsigned int shift = _timer.writtenShift - _timer.activeShift; @@ -85,11 +96,12 @@ template class MOS6532 { return value; } break; - case 0x05: - case 0x07: + + case 0x05: case 0x07: { - uint8_t value = _timer.status; - _timer.status &= ~0x40; + uint8_t value = _interrupt_status; + _interrupt_status &= ~InterruptFlag::PA7; + evaluate_interrupts(); return value; } break; @@ -100,36 +112,80 @@ template class MOS6532 { inline void run_for_cycles(unsigned int number_of_cycles) { + // permit counting _to_ zero; counting _through_ zero initiates the other behaviour if(_timer.value >= number_of_cycles) { _timer.value -= number_of_cycles; } else { number_of_cycles -= _timer.value; _timer.value = 0x100 - number_of_cycles; _timer.activeShift = 0; - _timer.status |= 0xc0; + _interrupt_status |= InterruptFlag::Timer; + evaluate_interrupts(); } } MOS6532() : - _timer({.status = 0}) + _interrupt_status(0), _port{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}}, _a7_interrupt({.last_port_value = 0, .enabled = false}) {} + inline void set_port_did_change(int port) + { + if(!port) + { + uint8_t new_port_a_value = (get_port_input(0) & ~_port[0].output_mask) | (_port[0].output & _port[0].output_mask); + uint8_t difference = new_port_a_value ^ _a7_interrupt.last_port_value; + _a7_interrupt.last_port_value = new_port_a_value; + if(difference&0x80) + { + if( + ((new_port_a_value&0x80) && _a7_interrupt.active_on_positive) || + (!(new_port_a_value&0x80) && !_a7_interrupt.active_on_positive) + ) + { + _interrupt_status |= InterruptFlag::PA7; + evaluate_interrupts(); + } + } + } + } + private: uint8_t _ram[128]; struct { unsigned int value; unsigned int activeShift, writtenShift; - uint8_t status; + bool interrupt_enabled; } _timer; struct { - uint8_t direction, output; + bool enabled; + bool active_on_positive; + uint8_t last_port_value; + } _a7_interrupt; + + struct { + uint8_t output_mask, output; } _port[2]; + uint8_t _interrupt_status; + enum InterruptFlag: uint8_t { + Timer = 0x80, + PA7 = 0x40 + }; + // expected to be overridden uint8_t get_port_input(int port) { return 0xff; } - void set_port_output(int port, uint8_t value, uint8_t direction_mask) {} + void set_port_output(int port, uint8_t value, uint8_t output_mask) {} + void set_irq_line(bool new_value) {} + + inline void evaluate_interrupts() + { + set_irq_line( + ((_interrupt_status&InterruptFlag::Timer) && _timer.interrupt_enabled) || + ((_interrupt_status&InterruptFlag::PA7) && _a7_interrupt.enabled) + ); + } }; } diff --git a/Machines/Atari2600/Atari2600.hpp b/Machines/Atari2600/Atari2600.hpp index baa8dd0b0..08b3a3fef 100644 --- a/Machines/Atari2600/Atari2600.hpp +++ b/Machines/Atari2600/Atari2600.hpp @@ -60,6 +60,7 @@ class PIA: public MOS::MOS6532 { inline void update_port_input(int port, uint8_t mask, bool set) { if(set) _portValues[port] &= ~mask; else _portValues[port] |= mask; + set_port_did_change(port); } PIA() : diff --git a/Processors/6502/CPU6502.hpp b/Processors/6502/CPU6502.hpp index 5bd2c5653..7005dfe29 100644 --- a/Processors/6502/CPU6502.hpp +++ b/Processors/6502/CPU6502.hpp @@ -31,7 +31,7 @@ enum Register { /* Flags as defined on the 6502; can be used to decode the result of @c get_flags or to form a value for @c set_flags. */ -enum Flag { +enum Flag: uint8_t { Sign = 0x80, Overflow = 0x40, Always = 0x20,