mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 16:31:42 +00:00
Adds full 8-bit clock addressing; stubs clock into the IIgs.
This commit is contained in:
parent
1249fb598b
commit
034056d0cd
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 ®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<ConcreteMachine, false> m65816_;
|
||||
MemoryMap memory_;
|
||||
Apple::Clock::ParallelClock clock_;
|
||||
|
||||
int fast_access_phase_ = 0;
|
||||
int slow_access_phase_ = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user