1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Implements time-of-day counters, provisionally.

Interrupts to do.
This commit is contained in:
Thomas Harte 2021-07-23 21:24:07 -04:00
parent 6216d53b1a
commit d898a43dff
6 changed files with 170 additions and 25 deletions

View File

@ -51,9 +51,12 @@ template <typename PortHandlerT, Personality personality> 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_;

View File

@ -17,14 +17,14 @@ namespace MOS6526 {
template <typename BusHandlerT, Personality personality>
template <int port> void MOS6526<BusHandlerT, personality>::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 <typename BusHandlerT, Personality personality>
template <int port> uint8_t MOS6526<BusHandlerT, personality>::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 <typename BusHandlerT, Personality personality>
@ -37,40 +37,85 @@ void MOS6526<BusHandlerT, personality>::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<BusHandlerT, personality>::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<BusHandlerT, personality>::read(int address) {
template <typename BusHandlerT, Personality personality>
void MOS6526<BusHandlerT, personality>::run_for(const HalfCycles half_cycles) {
(void)half_cycles;
half_divider_ += half_cycles;
const int sub = half_divider_.divide_cycles().template as<int>();
// TODO: apply to timers, depending on mode.
(void)sub;
}
template <typename BusHandlerT, Personality personality>
void MOS6526<BusHandlerT, personality>::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.
}
}
}
}

View File

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

View File

@ -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) {

View File

@ -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<int>();
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) {

View File

@ -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 &);