mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-20 14:29:11 +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:
|
protected:
|
||||||
static constexpr uint16_t NoResult = 0x100;
|
static constexpr uint16_t NoResult = 0x100;
|
||||||
static constexpr uint16_t DidWrite = 0x101;
|
static constexpr uint16_t DidComplete = 0x101;
|
||||||
uint16_t perform(uint8_t command) {
|
uint16_t perform(uint8_t command) {
|
||||||
/*
|
/*
|
||||||
Documented commands:
|
Documented commands:
|
||||||
@ -74,43 +74,87 @@ class ClockStorage {
|
|||||||
All the documentation says about the test register is to set the top
|
All the documentation says about the test register is to set the top
|
||||||
two bits to 0 for normal operation. Abnormal operation is undefined.
|
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_) {
|
case 0x00:
|
||||||
// Decode an address; use values >= 0x100 to represent clock time.
|
// A time access.
|
||||||
address_ = (command >> 2) & 0x1f;
|
address_ = SecondsBuffer + ((command >> 2)&3);
|
||||||
if(address_ < 4) {
|
break;
|
||||||
address_ |= 0x100;
|
case 0x30:
|
||||||
} else if(address_ >= 0x10) {
|
// Either a register access or an extended instruction.
|
||||||
address_ &= 0xf;
|
if(command & 0x08) {
|
||||||
} else if(address_ >= 0x8 && address_ <= 0xb) {
|
address_ = (command & 0x7) << 5;
|
||||||
address_ = 0x10 + (address_ & 0x3);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a read, return a result; otherwise prepare to write.
|
// If this is a read, return a result; otherwise prepare to write.
|
||||||
if(command & 0x80) {
|
if(command & 0x80) {
|
||||||
return (address_ & 0x100) ? seconds_[address_ & 0xff] : data_[address_];
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_writing_ = true;
|
if(address_ == RegisterTest) {
|
||||||
return NoResult;
|
// No documentation here.
|
||||||
} else {
|
return DidComplete;
|
||||||
// First test: is this to the write-protect register?
|
|
||||||
if(address_ == 0xd) {
|
|
||||||
write_protect_ = command;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No other writing is permitted if the write protect
|
// No other writing is permitted if the write protect
|
||||||
// register won't allow it.
|
// register won't allow it.
|
||||||
if(!(write_protect_ & 0x80)) {
|
if(!(write_protect_ & 0x80)) {
|
||||||
if(address_ & 0x100) {
|
if(address_ >= SecondsBuffer) {
|
||||||
seconds_[address_ & 0xff] = command;
|
seconds_[address_ & 0xff] = command;
|
||||||
} else {
|
} else {
|
||||||
data_[address_] = command;
|
data_[address_] = command;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is_writing_ = false;
|
phase_ = Phase::Command;
|
||||||
return DidWrite;
|
return DidComplete;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,8 +163,20 @@ class ClockStorage {
|
|||||||
uint8_t data_[256];
|
uint8_t data_[256];
|
||||||
uint8_t seconds_[4];
|
uint8_t seconds_[4];
|
||||||
uint8_t write_protect_;
|
uint8_t write_protect_;
|
||||||
bool is_writing_ = false;
|
|
||||||
int address_;
|
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:
|
default:
|
||||||
result_ = uint8_t(effect);
|
result_ = uint8_t(effect);
|
||||||
break;
|
break;
|
||||||
case ClockStorage::DidWrite:
|
case ClockStorage::DidComplete:
|
||||||
abort();
|
abort();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -186,6 +242,46 @@ class SerialClock: public ClockStorage {
|
|||||||
bool previous_clock_ = false;
|
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 "MemoryMap.hpp"
|
||||||
|
|
||||||
#include "../../../Components/8530/z8530.hpp"
|
#include "../../../Components/8530/z8530.hpp"
|
||||||
|
#include "../../../Components/AppleClock/AppleClock.hpp"
|
||||||
#include "../../../Components/DiskII/IWM.hpp"
|
#include "../../../Components/DiskII/IWM.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#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) {
|
forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) {
|
||||||
const auto ®ion = MemoryMapRegion(memory_, address);
|
const auto ®ion = MemoryMapRegion(memory_, address);
|
||||||
|
|
||||||
|
// TODO: potentially push time to clock_.
|
||||||
|
|
||||||
if(region.flags & MemoryMap::Region::IsIO) {
|
if(region.flags & MemoryMap::Region::IsIO) {
|
||||||
// Ensure classic auxiliary and language card accesses have effect.
|
// 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) {
|
switch(address & 0xffff) {
|
||||||
|
|
||||||
// New video register.
|
// New video register.
|
||||||
case 0xc029:
|
case 0xc029:
|
||||||
if(isReadOperation(operation)) {
|
if(is_read) {
|
||||||
*value = 0;
|
*value = 0;
|
||||||
} else {
|
} else {
|
||||||
printf("New video: %02x\n", *value);
|
printf("New video: %02x\n", *value);
|
||||||
@ -110,16 +114,35 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// Shadow register.
|
// Shadow register.
|
||||||
case 0xc035:
|
case 0xc035:
|
||||||
if(isReadOperation(operation)) {
|
if(is_read) {
|
||||||
*value = memory_.get_shadow_register();
|
*value = memory_.get_shadow_register();
|
||||||
} else {
|
} else {
|
||||||
memory_.set_shadow_register(*value);
|
memory_.set_shadow_register(*value);
|
||||||
}
|
}
|
||||||
break;
|
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.
|
// Speed register.
|
||||||
case 0xc036:
|
case 0xc036:
|
||||||
if(isReadOperation(operation)) {
|
if(is_read) {
|
||||||
*value = speed_register_;
|
*value = speed_register_;
|
||||||
} else {
|
} else {
|
||||||
memory_.set_speed_register(*value);
|
memory_.set_speed_register(*value);
|
||||||
@ -130,7 +153,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// [Memory] State register.
|
// [Memory] State register.
|
||||||
case 0xc068:
|
case 0xc068:
|
||||||
if(isReadOperation(operation)) {
|
if(is_read) {
|
||||||
*value = memory_.get_state_register();
|
*value = memory_.get_state_register();
|
||||||
} else {
|
} else {
|
||||||
memory_.set_state_register(*value);
|
memory_.set_state_register(*value);
|
||||||
@ -208,6 +231,7 @@ class ConcreteMachine:
|
|||||||
private:
|
private:
|
||||||
CPU::WDC65816::Processor<ConcreteMachine, false> m65816_;
|
CPU::WDC65816::Processor<ConcreteMachine, false> m65816_;
|
||||||
MemoryMap memory_;
|
MemoryMap memory_;
|
||||||
|
Apple::Clock::ParallelClock clock_;
|
||||||
|
|
||||||
int fast_access_phase_ = 0;
|
int fast_access_phase_ = 0;
|
||||||
int slow_access_phase_ = 0;
|
int slow_access_phase_ = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user