From 034056d0cdbe5dfec5a11dda485b0c93d014077a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 29 Oct 2020 21:38:36 -0400 Subject: [PATCH] Adds full 8-bit clock addressing; stubs clock into the IIgs. --- Components/AppleClock/AppleClock.hpp | 170 +++++++++++++++++++------ Machines/Apple/AppleIIgs/AppleIIgs.cpp | 34 ++++- 2 files changed, 162 insertions(+), 42 deletions(-) diff --git a/Components/AppleClock/AppleClock.hpp b/Components/AppleClock/AppleClock.hpp index 6dbf1dc6d..441b6ff5c 100644 --- a/Components/AppleClock/AppleClock.hpp +++ b/Components/AppleClock/AppleClock.hpp @@ -49,7 +49,7 @@ class ClockStorage { protected: static constexpr uint16_t NoResult = 0x100; - static constexpr uint16_t DidWrite = 0x101; + static constexpr uint16_t DidComplete = 0x101; uint16_t perform(uint8_t command) { /* Documented commands: @@ -74,43 +74,87 @@ class ClockStorage { All the documentation says about the test register is to set the top two bits to 0 for normal operation. Abnormal operation is undefined. */ + switch(phase_) { + case Phase::Command: + // Decode an address. + switch(command & 0x70) { + default: + if(command & 0x40) { + // RAM addresses 0x00 – 0x0f. + address_ = (command >> 2) & 0xf; + } else return DidComplete; // Unrecognised. + break; - if(!is_writing_) { - // Decode an address; use values >= 0x100 to represent clock time. - address_ = (command >> 2) & 0x1f; - if(address_ < 4) { - address_ |= 0x100; - } else if(address_ >= 0x10) { - address_ &= 0xf; - } else if(address_ >= 0x8 && address_ <= 0xb) { - address_ = 0x10 + (address_ & 0x3); - } - - // If this is a read, return a result; otherwise prepare to write. - if(command & 0x80) { - return (address_ & 0x100) ? seconds_[address_ & 0xff] : data_[address_]; - } - - is_writing_ = true; - return NoResult; - } else { - // First test: is this to the write-protect register? - if(address_ == 0xd) { - write_protect_ = command; - } - - // No other writing is permitted if the write protect - // register won't allow it. - if(!(write_protect_ & 0x80)) { - if(address_ & 0x100) { - seconds_[address_ & 0xff] = command; - } else { - data_[address_] = command; + case 0x00: + // A time access. + address_ = SecondsBuffer + ((command >> 2)&3); + break; + case 0x30: + // Either a register access or an extended instruction. + if(command & 0x08) { + address_ = (command & 0x7) << 5; + phase_ = (command & 0x80) ? Phase::SecondAddressByteWrite : Phase::SecondAddressByteRead; + return NoResult; + } else { + address_ = (command & 4) ? RegisterWriteProtect : RegisterTest; + } + break; + case 0x20: + // RAM addresses 0x10 – 0x13. + address_ = 0x10 + ((command >> 2) & 0x3); + break; } - } - is_writing_ = false; - return DidWrite; + // If this is a read, return a result; otherwise prepare to write. + if(command & 0x80) { + // The two registers are write-only. + if(address_ == RegisterTest || address_ == RegisterWriteProtect) { + return DidComplete; + } + return (address_ >= SecondsBuffer) ? seconds_[address_ & 0xff] : data_[address_]; + } + phase_ = Phase::WriteData; + return NoResult; + + case Phase::SecondAddressByteRead: + case Phase::SecondAddressByteWrite: + if(command & 0x83) { + return DidComplete; + } + address_ |= command >> 2; + + if(phase_ == Phase::SecondAddressByteRead) { + phase_ = Phase::Command; + return data_[address_]; // Only RAM accesses can get this far. + } else { + phase_ = Phase::WriteData; + } + return NoResult; + + case Phase::WriteData: + // First test: is this to the write-protect register? + if(address_ == RegisterWriteProtect) { + write_protect_ = command; + return DidComplete; + } + + if(address_ == RegisterTest) { + // No documentation here. + return DidComplete; + } + + // No other writing is permitted if the write protect + // register won't allow it. + if(!(write_protect_ & 0x80)) { + if(address_ >= SecondsBuffer) { + seconds_[address_ & 0xff] = command; + } else { + data_[address_] = command; + } + } + + phase_ = Phase::Command; + return DidComplete; } } @@ -119,8 +163,20 @@ class ClockStorage { uint8_t data_[256]; uint8_t seconds_[4]; uint8_t write_protect_; - bool is_writing_ = false; int address_; + + static constexpr int SecondsBuffer = 0x100; + static constexpr int RegisterTest = 0x200; + static constexpr int RegisterWriteProtect = 0x201; + + enum class Phase { + Command, + SecondAddressByteRead, + SecondAddressByteWrite, + WriteData + }; + Phase phase_ = Phase::Command; + }; /*! @@ -152,7 +208,7 @@ class SerialClock: public ClockStorage { default: result_ = uint8_t(effect); break; - case ClockStorage::DidWrite: + case ClockStorage::DidComplete: abort(); break; } @@ -186,6 +242,46 @@ class SerialClock: public ClockStorage { bool previous_clock_ = false; }; +/*! + Provides the parallel interface implemented by the IIgs. +*/ +class ParallelClock: public ClockStorage { + public: + void set_control(uint8_t control) { + if(!(control&0x80)) return; + + if(control & 0x40) { + // Read from the RTC. + // A no-op for now. + } else { + // Write to the RTC. Which in this implementation also sets up a future read. + data_ = uint8_t(perform(data_)); + } + + // MAGIC! The transaction took 0 seconds. + // TODO: no magic. + control_ = control & 0x7f; + + // Bit 5 is also meant to be 1 or 0 to indicate the final byte. + } + + uint8_t get_control() { + return control_; + } + + void set_data(uint8_t data) { + data_ = data; + } + + uint8_t get_data() { + return data_; + } + + private: + uint8_t data_; + uint8_t control_; +}; + } } diff --git a/Machines/Apple/AppleIIgs/AppleIIgs.cpp b/Machines/Apple/AppleIIgs/AppleIIgs.cpp index 1d7bda774..248502179 100644 --- a/Machines/Apple/AppleIIgs/AppleIIgs.cpp +++ b/Machines/Apple/AppleIIgs/AppleIIgs.cpp @@ -15,6 +15,7 @@ #include "MemoryMap.hpp" #include "../../../Components/8530/z8530.hpp" +#include "../../../Components/AppleClock/AppleClock.hpp" #include "../../../Components/DiskII/IWM.hpp" #include @@ -91,15 +92,18 @@ class ConcreteMachine: forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) { const auto ®ion = MemoryMapRegion(memory_, address); + // TODO: potentially push time to clock_. + if(region.flags & MemoryMap::Region::IsIO) { // Ensure classic auxiliary and language card accesses have effect. - memory_.access(uint16_t(address), isReadOperation(operation)); + const bool is_read = isReadOperation(operation); + memory_.access(uint16_t(address), is_read); switch(address & 0xffff) { // New video register. case 0xc029: - if(isReadOperation(operation)) { + if(is_read) { *value = 0; } else { printf("New video: %02x\n", *value); @@ -110,16 +114,35 @@ class ConcreteMachine: // Shadow register. case 0xc035: - if(isReadOperation(operation)) { + if(is_read) { *value = memory_.get_shadow_register(); } else { memory_.set_shadow_register(*value); } break; + // Clock data. + case 0xc033: + if(is_read) { + *value = clock_.get_data(); + } else { + clock_.set_data(*value); + } + break; + + // Clock and border control. + case 0xc034: + if(is_read) { + *value = clock_.get_control(); + } else { + clock_.set_control(*value); + // TODO: also set border colour. + } + break; + // Speed register. case 0xc036: - if(isReadOperation(operation)) { + if(is_read) { *value = speed_register_; } else { memory_.set_speed_register(*value); @@ -130,7 +153,7 @@ class ConcreteMachine: // [Memory] State register. case 0xc068: - if(isReadOperation(operation)) { + if(is_read) { *value = memory_.get_state_register(); } else { memory_.set_state_register(*value); @@ -208,6 +231,7 @@ class ConcreteMachine: private: CPU::WDC65816::Processor m65816_; MemoryMap memory_; + Apple::Clock::ParallelClock clock_; int fast_access_phase_ = 0; int slow_access_phase_ = 0;