1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-29 12:50:28 +00:00

Adds full 8-bit clock addressing; stubs clock into the IIgs.

This commit is contained in:
Thomas Harte 2020-10-29 21:38:36 -04:00
parent 1249fb598b
commit 034056d0cd
2 changed files with 162 additions and 42 deletions

View File

@ -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_;
};
}
}

View File

@ -15,6 +15,7 @@
#include "MemoryMap.hpp"
#include "../../../Components/8530/z8530.hpp"
#include "../../../Components/AppleClock/AppleClock.hpp"
#include "../../../Components/DiskII/IWM.hpp"
#include <cassert>
@ -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 &region = 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<ConcreteMachine, false> m65816_;
MemoryMap memory_;
Apple::Clock::ParallelClock clock_;
int fast_access_phase_ = 0;
int slow_access_phase_ = 0;