1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-26 10:29:31 +00:00
CLK/Components/6526/Implementation/6526Storage.hpp
Thomas Harte ee6039bfa5 Writes to a timer _during reload_ now have effect.
Net: one CIA test passed.
2021-08-03 16:57:05 -04:00

159 lines
3.6 KiB
C++

//
// 6526Storage.hpp
// Clock Signal
//
// Created by Thomas Harte on 18/07/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#ifndef _526Storage_h
#define _526Storage_h
namespace MOS {
namespace MOS6526 {
struct MOS6526Storage {
HalfCycles half_divider_;
uint8_t output_[2] = {0, 0};
uint8_t data_direction_[2] = {0, 0};
uint8_t interrupt_control_ = 0;
uint8_t interrupt_state_ = 0;
uint32_t tod_increment_mask_ = uint32_t(~0);
uint32_t tod_latch_ = 0;
uint32_t tod_ = 0;
uint32_t tod_alarm_ = 0;
struct Counter {
uint16_t reload = 0;
uint16_t value = 0;
uint8_t control = 0;
template <int shift> void set_reload(uint8_t v) {
reload = (reload & (0xff00 >> shift)) | uint16_t(v << shift);
if constexpr (shift == 8) {
if(!(control&1)) {
pending |= ReloadInOne;
}
}
// If this write has hit during a reload cycle, reload.
if(pending & ReloadNow) {
value = reload;
}
}
template <bool is_counter_2> void set_control(uint8_t v) {
control = v;
}
template <bool is_counter_2> bool advance(bool chained_input) {
// TODO: remove most of the conditionals here in favour of bit shuffling.
pending = (pending & PendingClearMask) << 1;
//
// Apply feeder states inputs: anything that
// will take effect in the future.
//
// Schedule a force reload if requested.
if(control & 0x10) {
pending |= ReloadInOne;
control &= ~0x10;
}
// Determine whether an input clock is applicable.
if constexpr(is_counter_2) {
switch(control&0x60) {
case 0x00:
pending |= TestInputInOne;
break;
default:
// TODO: all other forms of input.
assert(false);
break;
}
} else {
if(!(control&0x20)) {
pending |= TestInputNow;
} else if (chained_input) { // TODO: check CNT directly, probably?
pending |= TestInputInOne;
}
}
if(pending&TestInputNow && control&1) {
pending |= ApplyClockInTwo;
}
// Keep a history of the one-shot bit.
if(control & 0x08) {
pending |= OneShotInOne | OneShotNow;
}
//
// Perform a timer tick and decide whether a reload is prompted.
//
if(pending & ApplyClockNow) {
--value;
}
const bool should_reload = !value && (pending & ApplyClockInOne);
// Schedule a reload if so ordered.
if(should_reload) {
pending |= ReloadNow; // Combine this decision with a deferred
// input from the force-reoad test above.
// If this was one-shot, stop.
if(pending&(OneShotInOne | OneShotNow)) {
control &= ~1;
pending &= ~(ApplyClockInOne|ApplyClockInTwo); // Cancel scheculed ticks.
}
}
// Reload if scheduled.
if(pending & ReloadNow) {
value = reload;
pending &= ~ApplyClockInOne; // Skip next decrement.
}
return should_reload;
}
private:
int pending = 0;
static constexpr int ReloadInOne = 1 << 0;
static constexpr int ReloadNow = 1 << 1;
static constexpr int OneShotInOne = 1 << 2;
static constexpr int OneShotNow = 1 << 3;
static constexpr int ApplyClockInTwo = 1 << 4;
static constexpr int ApplyClockInOne = 1 << 5;
static constexpr int ApplyClockNow = 1 << 6;
static constexpr int TestInputInOne = 1 << 7;
static constexpr int TestInputNow = 1 << 8;
static constexpr int PendingClearMask = ~(ReloadNow | OneShotNow | ApplyClockNow);
bool active_ = false;
} counter_[2];
static constexpr int InterruptInOne = 1 << 0;
static constexpr int InterruptNow = 1 << 1;
static constexpr int PendingClearMask = ~(InterruptNow);
int pending_ = 0;
};
}
}
#endif /* _526Storage_h */