diff --git a/Components/6526/6526.hpp b/Components/6526/6526.hpp index ba907fda4..43a735f11 100644 --- a/Components/6526/6526.hpp +++ b/Components/6526/6526.hpp @@ -51,9 +51,12 @@ template class MOS6526: /// Fetches the value of the register @c address. Only the low two bits of the address are decoded. uint8_t read(int address); - /// Runs for a specified number of half cycles. + /// Pulses Phi2 to advance by the specified number of half cycles. void run_for(const HalfCycles half_cycles); + /// Pulses the TOD input the specified number of times. + void advance_tod(int count); + private: PortHandlerT &port_handler_; diff --git a/Components/6526/Implementation/6526Implementation.hpp b/Components/6526/Implementation/6526Implementation.hpp index 5ba277930..6cb226fd8 100644 --- a/Components/6526/Implementation/6526Implementation.hpp +++ b/Components/6526/Implementation/6526Implementation.hpp @@ -17,14 +17,14 @@ namespace MOS6526 { template template void MOS6526::set_port_output() { - const uint8_t output = registers_.output[port] | (~registers_.data_direction[port]); + const uint8_t output = output_[port] | (~data_direction_[port]); port_handler_.set_port_output(Port(port), output); } template template uint8_t MOS6526::get_port_input() { const uint8_t input = port_handler_.get_port_input(Port(port)); - return (input & ~registers_.data_direction[port]) | (registers_.output[port] & registers_.data_direction[port]); + return (input & ~data_direction_[port]) | (output_[port] & data_direction_[port]); } template @@ -37,40 +37,85 @@ void MOS6526::write(int address, uint8_t value) { switch(address) { // Port output. case 0: - registers_.output[0] = value; + output_[0] = value; set_port_output<0>(); break; case 1: - registers_.output[1] = value; + output_[1] = value; set_port_output<1>(); break; // Port direction. case 2: - registers_.data_direction[0] = value; + data_direction_[0] = value; set_port_output<0>(); break; case 3: - registers_.data_direction[1] = value; + data_direction_[1] = value; set_port_output<1>(); break; + // Counters; writes set the reload values. + case 4: counters_[0].reload = (counters_[0].reload & 0xff00) | uint16_t(value << 0); break; + case 5: counters_[0].reload = (counters_[0].reload & 0x00ff) | uint16_t(value << 8); break; + case 6: counters_[1].reload = (counters_[1].reload & 0xff00) | uint16_t(value << 0); break; + case 7: counters_[1].reload = (counters_[1].reload & 0x00ff) | uint16_t(value << 8); break; + + // Time-of-day clock. + // + // 8520: a binary counter; stopped on any write, restarted + // upon a write to the LSB. + case 8: + if constexpr (personality == Personality::P8250) { + tod_ = (tod_ & 0xffff00) | uint32_t(value); + tod_increment_mask_ = uint32_t(~0); + } else { + printf("6526 TOD clock not implemented\n"); + assert(false); + } + break; + case 9: + if constexpr (personality == Personality::P8250) { + tod_ = (tod_ & 0xff00ff) | uint32_t(value << 8); + tod_increment_mask_ = 0; + } else { + printf("6526 TOD clock not implemented\n"); + assert(false); + } + break; + case 10: + if constexpr (personality == Personality::P8250) { + tod_ = (tod_ & 0x00ffff) | uint32_t(value << 16); + tod_increment_mask_ = 0; + } else { + printf("6526 TOD clock not implemented\n"); + assert(false); + } + break; + + case 11: + if constexpr (personality != Personality::P8250) { + printf("6526 TOD clock not implemented\n"); + assert(false); + } + break; + // Interrupt control. case 13: - registers_.interrupt_control_ = value; + interrupt_control_ = value; update_interrupts(); break; // Control. case 14: case 15: - registers_.control[address - 14] = value; + control_[address - 14] = value; printf("Ignoring control write: %02x to %d\n", value, address); break; default: printf("Unhandled 6526 write: %02x to %d\n", value, address); -// assert(false); + assert(false); break; } } @@ -81,17 +126,72 @@ uint8_t MOS6526::read(int address) { switch(address) { case 0: return get_port_input<0>(); case 1: return get_port_input<1>(); - case 13: return registers_.interrupt_control_; + case 13: return interrupt_control_; case 2: case 3: - return registers_.data_direction[address - 2]; + return data_direction_[address - 2]; + + // Counters; reads obtain the current values. + case 4: return uint8_t(counters_[0].value >> 0); + case 5: return uint8_t(counters_[0].value >> 8); + case 6: return uint8_t(counters_[1].value >> 0); + case 7: return uint8_t(counters_[1].value >> 0); case 14: case 15: - return registers_.control[address - 14]; + return control_[address - 14]; + + // Time-of-day clock. + // + // 8250: Latch on MSB. Unlatch on LSB. Read raw if not latched. + case 8: + if constexpr (personality == Personality::P8250) { + if(tod_latch_) { + const uint8_t result = tod_latch_ & 0xff; + tod_latch_ = 0; + return result; + } else { + return tod_ & 0xff; + } + } else { + printf("6526 TOD clock not implemented\n"); + assert(false); + } + break; + case 9: + if constexpr (personality == Personality::P8250) { + if(tod_latch_) { + return (tod_latch_ >> 8) & 0xff; + } else { + return (tod_ >> 8) & 0xff; + } + } else { + printf("6526 TOD clock not implemented\n"); + assert(false); + } + break; + case 10: + if constexpr (personality == Personality::P8250) { + tod_latch_ = tod_ | 0xff00'0000; + return (tod_ >> 16) & 0xff; + } else { + printf("6526 TOD clock not implemented\n"); + assert(false); + } + break; + + case 11: + if constexpr (personality == Personality::P8250) { + return 0x00; // Assumed. Just a guss. + } else { + printf("6526 TOD clock not implemented\n"); + assert(false); + + } + break; default: printf("Unhandled 6526 read from %d\n", address); -// assert(false); + assert(false); break; } return 0xff; @@ -99,9 +199,26 @@ uint8_t MOS6526::read(int address) { template void MOS6526::run_for(const HalfCycles half_cycles) { - (void)half_cycles; + half_divider_ += half_cycles; + const int sub = half_divider_.divide_cycles().template as(); + + // TODO: apply to timers, depending on mode. + (void)sub; } +template +void MOS6526::advance_tod(int count) { + if constexpr(personality == Personality::P8250) { + // The 8250 uses a simple binary counter to replace the + // 6526's time-of-day clock. So this is easy. + tod_ += uint32_t(count) & tod_increment_mask_; + } else { + // The 6526 uses a time-of-day clock. This may or may not + // be accurate. + } +} + + } } diff --git a/Components/6526/Implementation/6526Storage.hpp b/Components/6526/Implementation/6526Storage.hpp index 2a96e0406..630e6e9af 100644 --- a/Components/6526/Implementation/6526Storage.hpp +++ b/Components/6526/Implementation/6526Storage.hpp @@ -13,14 +13,24 @@ namespace MOS { namespace MOS6526 { struct MOS6526Storage { + HalfCycles half_divider_; - struct Registers { - uint8_t output[2] = {0, 0}; - uint8_t data_direction[2] = {0, 0}; - uint8_t interrupt_control_ = 0; - uint8_t control[2] = {0, 0}; - } registers_; + uint8_t output_[2] = {0, 0}; + uint8_t data_direction_[2] = {0, 0}; + uint8_t interrupt_control_ = 0; + uint8_t control_[2] = {0, 0}; + uint32_t tod_increment_mask_ = uint32_t(~0); + uint32_t tod_latch_ = 0; + uint32_t tod_ = 0; + + bool write_tod_alarm_ = false; + uint32_t tod_alarm_ = 0; + + struct Counter { + uint16_t reload = 0; + uint16_t value = 0; + } counters_[2]; }; } diff --git a/Machines/Amiga/Amiga.cpp b/Machines/Amiga/Amiga.cpp index 844d61dc6..1680f1de9 100644 --- a/Machines/Amiga/Amiga.cpp +++ b/Machines/Amiga/Amiga.cpp @@ -79,7 +79,9 @@ class ConcreteMachine: cia_b_.run_for(e_clocks); } - chipset_.run_for(cycle.length); + const auto changes = chipset_.run_for(cycle.length); + cia_a_.advance_tod(changes.vsyncs); + cia_b_.advance_tod(changes.hsyncs); // Check for assertion of reset. if(cycle.operation & Microcycle::Reset) { diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp index 01be6c3a7..6b6c6a455 100644 --- a/Machines/Amiga/Chipset.cpp +++ b/Machines/Amiga/Chipset.cpp @@ -20,13 +20,20 @@ Chipset::Chipset(uint16_t *ram, size_t size) : blitter_(ram, size) { } -void Chipset::run_for(HalfCycles length) { +Chipset::Changes Chipset::run_for(HalfCycles length) { + Changes changes; // Update raster position. // TODO: actual graphics, why not? x_ += length.as(); - y_ = (y_ + x_ / (227 + (y_&1))) % 262; + changes.hsyncs = x_ / (227 + (y_&1)); x_ %= 227; + + y_ += changes.hsyncs; + changes.vsyncs = y_ / 262; + y_ %= 262; + + return changes; } void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp index 31aec88f7..520e809e7 100644 --- a/Machines/Amiga/Chipset.hpp +++ b/Machines/Amiga/Chipset.hpp @@ -26,8 +26,14 @@ class Chipset { /// available CPU slot for accessing chip memory. HalfCycles time_until_cpu_slot(); + struct Changes { + int hsyncs = 0; + int vsyncs = 0; + // TODO: interrupt change? + }; + /// Advances the stated amount of time. - void run_for(HalfCycles); + Changes run_for(HalfCycles); /// Performs the provided microcycle, which the caller guarantees to be a memory access. void perform(const CPU::MC68000::Microcycle &);