12static constexpr uint8_t READ_BACK = 0xC0;
13static constexpr uint8_t RB_CNTR0 = 0x02;
14static constexpr uint8_t RB_CNTR1 = 0x04;
15static constexpr uint8_t RB_CNTR2 = 0x08;
16static constexpr uint8_t RB_STATUS = 0x10;
17static constexpr uint8_t RB_COUNT = 0x20;
25 : counter(generate_array<3>([&](auto i) {
26 auto* output = (i == 0) ? output0
29 return Counter(scheduler, output, time);
36 for (
auto& c : counter) {
45 case 0:
case 1:
case 2:
46 return counter[port].readIO(time);
58 case 0:
case 1:
case 2:
59 return counter[port].peekIO(time);
71 case 0:
case 1:
case 2:
72 counter[port].writeIO(value, time);
76 if ((value & READ_BACK) != READ_BACK) {
78 counter[value >> 6].writeControlWord(
82 if (value & RB_CNTR0) {
83 readBackHelper(value, 0, time);
85 if (value & RB_CNTR1) {
86 readBackHelper(value, 1, time);
88 if (value & RB_CNTR2) {
89 readBackHelper(value, 2, time);
98void I8254::readBackHelper(uint8_t value,
unsigned cntr, EmuTime::param time)
101 if (!(value & RB_STATUS)) {
102 counter[cntr].latchStatus(time);
104 if (!(value & RB_COUNT)) {
105 counter[cntr].latchCounter(time);
112 counter[cntr].setGateStatus(status, time);
118 return counter[cntr].clock;
124 return counter[cntr].output;
132 : clock(scheduler), output(scheduler, listener)
160 return latchedControl;
163 uint16_t readData = ltchCntr ? latchedCounter : narrow_cast<uint16_t>(counter);
164 switch (control & WRT_FRMT) {
169 return narrow_cast<uint8_t>(readData & 0x00FF);
172 return narrow_cast<uint8_t>(readData >> 8);
174 if (readOrder ==
LOW) {
176 return narrow_cast<uint8_t>(readData & 0x00FF);
180 return narrow_cast<uint8_t>(readData >> 8);
190 return latchedControl;
193 const_cast<Counter*
>(
this)->advance(time);
195 uint16_t readData = ltchCntr ? latchedCounter : narrow_cast<uint16_t>(counter);
196 switch (control & WRT_FRMT) {
200 return narrow_cast<uint8_t>(readData & 0x00FF);
202 return narrow_cast<uint8_t>(readData >> 8);
204 if (readOrder ==
LOW) {
205 return narrow_cast<uint8_t>(readData & 0x00FF);
207 return narrow_cast<uint8_t>(readData >> 8);
217 switch (control & WRT_FRMT) {
221 writeLoad((counterLoad & 0xFF00) | uint16_t(value << 0), time);
224 writeLoad((counterLoad & 0x00FF) | uint16_t(value << 8), time);
227 if (writeOrder ==
LOW) {
230 if ((control & CNTR_MODE) == CNTR_M0)
236 writeLoad(uint16_t((value << 8) | writeLatch), time);
243void Counter::writeLoad(uint16_t value, EmuTime::param time)
246 uint8_t mode = control & CNTR_MODE;
247 if (mode ==
one_of(CNTR_M0, CNTR_M4)) {
248 counter = counterLoad;
250 if (!active && (mode ==
one_of(CNTR_M2, CNTR_M2_, CNTR_M3, CNTR_M3_))) {
252 counter = counterLoad;
254 EmuDuration total = high * counter;
260 if (mode == CNTR_M0) {
269 if ((value & WRT_FRMT) == 0) {
280 switch (control & CNTR_MODE) {
285 case CNTR_M2:
case CNTR_M2_:
286 case CNTR_M3:
case CNTR_M3_:
302 uint8_t out = output.
getState(time) ? 0x80 : 0;
303 latchedControl = out | control;
313 latchedCounter = narrow_cast<uint16_t>(counter);
320 if (gate != newStatus) {
322 switch (control & CNTR_MODE) {
329 if (gate && active) {
331 counter = counterLoad;
336 case CNTR_M2:
case CNTR_M2_:
337 case CNTR_M3:
case CNTR_M3_:
340 counter = counterLoad;
352 if (gate && active) {
354 counter = counterLoad;
364void Counter::advance(EmuTime::param time)
370 switch (control & CNTR_MODE) {
372 if (gate && counting) {
399 if (counterLoad != 0) {
400 counter %= counterLoad;
410 counter -= 2 * ticks;
413 if (counterLoad != 0) {
414 counter %= counterLoad;
427 }
else if (counter < 0) {
453static constexpr std::initializer_list<enum_string<Counter::ByteOrder>> byteOrderInfo = {
459template<
typename Archive>
462 ar.serialize(
"clock", clock,
464 "currentTime", currentTime,
466 "latchedCounter", latchedCounter,
467 "counterLoad", counterLoad,
469 "latchedControl", latchedControl,
470 "ltchCtrl", ltchCtrl,
471 "ltchCntr", ltchCntr,
472 "readOrder", readOrder,
473 "writeOrder", writeOrder,
474 "writeLatch", writeLatch,
477 "triggered", triggered,
478 "counting", counting);
481template<
typename Archive>
484 std::array<char, 9> tag = {
'c',
'o',
'u',
'n',
't',
'e',
'r',
'X', 0};
485 for (
auto [i, cntr] :
enumerate(counter)) {
486 tag[7] = char(
'0' + i);
487 ar.serialize(tag.data(), cntr);
void setState(bool status, EmuTime::param time)
int getTicksBetween(EmuTime::param begin, EmuTime::param end) const
bool getState(EmuTime::param time) const
void setPeriodicState(EmuDuration::param total, EmuDuration::param hi, EmuTime::param time)
EmuDuration::param getTotalDuration() const
void latchStatus(EmuTime::param time)
uint8_t peekIO(EmuTime::param time) const
void serialize(Archive &ar, unsigned version)
void reset(EmuTime::param time)
void writeIO(uint8_t value, EmuTime::param time)
uint8_t readIO(EmuTime::param time)
void latchCounter(EmuTime::param time)
void writeControlWord(uint8_t value, EmuTime::param time)
void setGateStatus(bool status, EmuTime::param time)
Counter(Scheduler &scheduler, ClockPinListener *listener, EmuTime::param time)
const EmuDuration & param
void reset(EmuTime::param time)
uint8_t peekIO(uint16_t port, EmuTime::param time) const
void writeIO(uint16_t port, uint8_t value, EmuTime::param time)
uint8_t readIO(uint16_t port, EmuTime::param time)
ClockPin & getClockPin(unsigned cntr)
I8254(Scheduler &scheduler, ClockPinListener *output0, ClockPinListener *output1, ClockPinListener *output2, EmuTime::param time)
void serialize(Archive &ar, unsigned version)
void setGate(unsigned cntr, bool status, EmuTime::param time)
ClockPin & getOutputPin(unsigned cntr)
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
This file implemented 3 utility functions:
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)