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);
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);
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);
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_M3, CNTR_M3_))) {
252 counter = counterLoad;
253 int half = (counter + 1) / 2;
255 EmuDuration total = tick * counter;
256 EmuDuration high = tick * half;
262 if (!active && (mode ==
one_of(CNTR_M2, CNTR_M2_))) {
264 counter = counterLoad;
266 EmuDuration total = high * counter;
273 if (mode == CNTR_M0) {
282 if ((value & WRT_FRMT) == 0) {
293 switch (control & CNTR_MODE) {
298 case CNTR_M2:
case CNTR_M2_:
299 case CNTR_M3:
case CNTR_M3_:
315 uint8_t out = output.
getState(time) ? 0x80 : 0;
316 latchedControl = out | control;
326 latchedCounter = narrow_cast<uint16_t>(counter);
333 if (gate != newStatus) {
335 switch (control & CNTR_MODE) {
342 if (gate && active) {
344 counter = counterLoad;
349 case CNTR_M2:
case CNTR_M2_:
350 case CNTR_M3:
case CNTR_M3_:
353 counter = counterLoad;
365 if (gate && active) {
367 counter = counterLoad;
377void Counter::advance(EmuTime::param time)
383 switch (control & CNTR_MODE) {
385 if (gate && counting) {
398 if (triggered && (counter < 0)) {
410 if (counterLoad != 0) {
411 counter %= counterLoad;
421 counter -= 2 * ticks;
424 if (counterLoad != 0) {
425 counter %= counterLoad;
438 }
else if (counter < 0) {
464static constexpr std::initializer_list<enum_string<Counter::ByteOrder>> byteOrderInfo = {
470template<
typename Archive>
473 ar.serialize(
"clock", clock,
475 "currentTime", currentTime,
477 "latchedCounter", latchedCounter,
478 "counterLoad", counterLoad,
480 "latchedControl", latchedControl,
481 "ltchCtrl", ltchCtrl,
482 "ltchCntr", ltchCntr,
483 "readOrder", readOrder,
484 "writeOrder", writeOrder,
485 "writeLatch", writeLatch,
488 "triggered", triggered,
489 "counting", counting);
492template<
typename Archive>
495 std::array<char, 9> tag = {
'c',
'o',
'u',
'n',
't',
'e',
'r',
'X', 0};
496 for (
auto [i, cntr] :
enumerate(counter)) {
497 tag[7] = char(
'0' + i);
498 ar.serialize(tag.data(), cntr);
unsigned getTicksBetween(EmuTime::param begin, EmuTime::param end) const
void setState(bool status, EmuTime::param time)
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)
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:
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)