15static constexpr int BUSY = 0x01;
16static constexpr int INDEX = 0x02;
17static constexpr int S_DRQ = 0x02;
18static constexpr int TRACK00 = 0x04;
19static constexpr int LOST_DATA = 0x04;
20static constexpr int CRC_ERROR = 0x08;
21static constexpr int SEEK_ERROR = 0x10;
22static constexpr int RECORD_NOT_FOUND = 0x10;
23static constexpr int HEAD_LOADED = 0x20;
24static constexpr int RECORD_TYPE = 0x20;
25static constexpr int WRITE_PROTECTED = 0x40;
26static constexpr int NOT_READY = 0x80;
29static constexpr int STEP_SPEED = 0x03;
30static constexpr int V_FLAG = 0x04;
31static constexpr int E_FLAG = 0x04;
32static constexpr int H_FLAG = 0x08;
33static constexpr int T_FLAG = 0x10;
34static constexpr int M_FLAG = 0x10;
35static constexpr int A0_FLAG = 0x01;
36static constexpr int N2R_IRQ = 0x01;
37static constexpr int R2N_IRQ = 0x02;
38static constexpr int IDX_IRQ = 0x04;
39static constexpr int IMM_IRQ = 0x08;
50 EmuTime::param time,
bool isWD1770_)
70 drqTime.
reset(EmuTime::infinity());
71 irqTime = EmuTime::infinity();
86 return time >= drqTime.
getTime();
89void WD2793::setDrqRate(
unsigned trackLength)
101 return immediateIRQ || (irqTime <= time);
104bool WD2793::isReady()
const
113 if (((commandReg & 0xE0) == 0xA0) ||
114 ((commandReg & 0xF0) == 0xF0)) {
127 irqTime = EmuTime::infinity();
128 switch (commandReg & 0xF0) {
161 if (((commandReg & 0x80) == 0) || ((commandReg & 0xF0) == 0xD0)) {
163 statusReg &= ~(INDEX | TRACK00 | HEAD_LOADED | WRITE_PROTECTED);
168 statusReg |= TRACK00;
170 if ((hldTime <= time) && (time < (hldTime + IDLE))) {
171 statusReg |= HEAD_LOADED;
174 statusReg |= WRITE_PROTECTED;
186 statusReg &= ~NOT_READY;
188 statusReg |= NOT_READY;
192 if (irqTime <= time) {
193 irqTime = EmuTime::infinity();
240 assert(statusReg & BUSY);
242 if (((commandReg & 0xE0) == 0xA0) ||
243 ((commandReg & 0xF0) == 0xF0)) {
244 dataRegWritten =
true;
245 drqTime.
reset(EmuTime::infinity());
251 if ((((commandReg & 0xE0) == 0x80) ||
252 ((commandReg & 0xF0) == 0xC0) ||
253 ((commandReg & 0xF0) == 0xE0)) &&
255 assert(statusReg & BUSY);
261 while (dataAvailable && (
getDTRQ(time))) {
262 statusReg |= LOST_DATA;
268 assert(!dataAvailable || !
getDTRQ(time));
269 if (dataAvailable == 0) {
270 if ((commandReg & 0xE0) == 0x80) {
276 statusReg &= ~CRC_ERROR;
278 statusReg |= CRC_ERROR;
285 statusReg |= RECORD_TYPE;
287 if (!(commandReg & M_FLAG)) {
291 drqTime.
reset(EmuTime::infinity());
297 if ((commandReg & 0xF0) == 0xE0) {
300 if ((commandReg & 0xF0) == 0xC0) {
302 statusReg |= CRC_ERROR;
304 statusReg &= ~CRC_ERROR;
316 if ((((commandReg & 0xE0) == 0x80) ||
317 ((commandReg & 0xF0) == 0xC0) ||
318 ((commandReg & 0xF0) == 0xE0)) &&
327void WD2793::schedule(FSM state, EmuTime::param time)
334void WD2793::executeUntil(EmuTime::param time)
337 FSM state = fsmState;
341 if ((commandReg & 0x80) == 0x00) {
347 if ((commandReg & 0xC0) == 0x80) {
353 if ((commandReg & 0xC0) == 0x80) {
359 if ((commandReg & 0xC0) == 0x80) {
365 if ((commandReg & 0xE0) == 0xA0) {
367 checkStartWrite(time);
371 if ((commandReg & 0xE0) == 0xA0) {
373 preWriteSector(time);
377 if ((commandReg & 0xE0) == 0xA0) {
379 writeSectorData(time);
383 if ((commandReg & 0xE0) == 0xA0) {
385 postWriteSector(time);
389 if (((commandReg & 0xC0) == 0xC0) &&
390 ((commandReg & 0xF0) != 0xD0)) {
396 if (((commandReg & 0xC0) == 0xC0) &&
397 ((commandReg & 0xF0) != 0xD0)) {
403 if ((commandReg & 0xF0) == 0xF0) {
405 writeTrackData(time);
409 if ((commandReg & 0xF0) == 0xE0) {
420void WD2793::startType1Cmd(EmuTime::param time)
422 statusReg &= ~(SEEK_ERROR | CRC_ERROR);
425 if (commandReg & H_FLAG) {
432 hldTime = EmuTime::infinity();
435 switch (commandReg & 0xF0) {
465void WD2793::seek(EmuTime::param time)
467 if (trackReg == dataReg) {
470 directionIn = (dataReg > trackReg);
475void WD2793::step(EmuTime::param time)
477 static constexpr std::array<EmuDuration, 4> timePerStep = {
485 if ((commandReg & T_FLAG) || ((commandReg & 0xE0) == 0x00)) {
497 drive.
step(directionIn, time);
498 schedule(
FSM::SEEK, time + timePerStep[commandReg & STEP_SPEED]);
502void WD2793::seekNext(EmuTime::param time)
504 if ((commandReg & 0xE0) == 0x00) {
512void WD2793::endType1Cmd(EmuTime::param time)
514 if (commandReg & V_FLAG) {
522void WD2793::startType2Cmd(EmuTime::param time)
524 statusReg &= ~(LOST_DATA | RECORD_NOT_FOUND |
525 RECORD_TYPE | WRITE_PROTECTED);
527 dataRegWritten =
false;
535 if (commandReg & E_FLAG) {
544void WD2793::type2Loaded(EmuTime::param time)
548 statusReg |= WRITE_PROTECTED;
557void WD2793::type2Search(EmuTime::param time)
559 assert(time < pulse5);
569 }
catch (MSXException& ) {
574 if (pulse5 < EmuTime::infinity()) {
582void WD2793::type2Rotated(EmuTime::param time)
586 statusReg |= CRC_ERROR;
588 statusReg &= ~CRC_ERROR;
591 (sectorInfo.
track != trackReg) ||
592 (sectorInfo.
sector != sectorReg)) {
598 if (sectorInfo.
dataIdx == -1) {
607 switch (commandReg & 0xE0) {
609 startReadSector(time);
613 startWriteSector(time);
618void WD2793::type2NotFound(EmuTime::param time)
620 statusReg |= RECORD_NOT_FOUND;
624void WD2793::startReadSector(EmuTime::param time)
627 crc.
init({0xA1, 0xA1, 0xA1, 0xF8});
629 crc.
init({0xA1, 0xA1, 0xA1, 0xFB});
633 unsigned gapLength = (tmp >= 0) ? tmp : (tmp + trackLength);
634 assert(gapLength < trackLength);
636 drqTime += gapLength + 1 + 1;
637 dataCurrent = sectorInfo.
dataIdx;
643 dataAvailable = 128 << (sectorInfo.
sizeCode & 3);
646void WD2793::startWriteSector(EmuTime::param time)
674void WD2793::checkStartWrite(EmuTime::param time)
678 if (!dataRegWritten) {
679 statusReg |= LOST_DATA;
692 dataCurrent = sectorInfo.
addrIdx
699 drqTime.
reset(EmuTime::infinity());
703void WD2793::preWriteSector(EmuTime::param time)
707 if (dataAvailable > 0) {
708 if (dataAvailable >= 4) {
717 drqTime.
reset(EmuTime::infinity());
720 crc.
init({0xA1, 0xA1, 0xA1});
721 uint8_t mark = (commandReg & A0_FLAG) ? 0xF8 : 0xFB;
726 dataOutReg = dataReg;
727 dataRegWritten =
false;
728 dataAvailable = 128 << (sectorInfo.
sizeCode & 3);
736 }
catch (MSXException&) {
737 statusReg |= NOT_READY;
742void WD2793::writeSectorData(EmuTime::param time)
750 if (dataAvailable > 0) {
751 if (dataRegWritten) {
752 dataOutReg = dataReg;
753 dataRegWritten =
false;
756 statusReg |= LOST_DATA;
764 if (dataAvailable == 1) {
769 drqTime.
reset(EmuTime::infinity());
776 drqTime.
reset(EmuTime::infinity());
778 }
catch (MSXException&) {
779 statusReg |= NOT_READY;
784void WD2793::postWriteSector(EmuTime::param time)
788 if (dataAvailable > 0) {
790 uint8_t val = (dataAvailable == 2) ? narrow_cast<uint8_t>((crc.
getValue() >> 8))
795 drqTime.
reset(EmuTime::infinity());
803 if (!(commandReg & M_FLAG)) {
807 drqTime.
reset(EmuTime::infinity());
812 }
catch (MSXException&) {
814 statusReg |= NOT_READY;
820void WD2793::startType3Cmd(EmuTime::param time)
822 statusReg &= ~(LOST_DATA | RECORD_NOT_FOUND | RECORD_TYPE);
828 if ((commandReg & 0xF0) == 0xF0) {
836 if (commandReg & E_FLAG) {
845void WD2793::type3Loaded(EmuTime::param time)
850 statusReg |= WRITE_PROTECTED;
855 EmuTime
next(EmuTime::dummy());
856 if ((commandReg & 0xF0) == 0xC0) {
862 if (next == EmuTime::infinity()) {
864 statusReg |= RECORD_NOT_FOUND;
868 dataCurrent = sectorInfo.
addrIdx;
871 }
catch (MSXException&) {
873 statusReg |= RECORD_NOT_FOUND;
881 if (next == EmuTime::infinity()) {
891void WD2793::type3Rotated(EmuTime::param time)
893 switch (commandReg & 0xF0) {
895 readAddressCmd(time);
901 startWriteTrack(time);
906void WD2793::readAddressCmd(EmuTime::param time)
912void WD2793::readTrackCmd(EmuTime::param time)
917 setDrqRate(trackLength);
919 dataAvailable = narrow<int>(trackLength);
926 }
catch (MSXException&) {
933void WD2793::startWriteTrack(EmuTime::param time)
937 if (!dataRegWritten) {
938 statusReg |= LOST_DATA;
944 setDrqRate(trackLength);
946 dataAvailable = narrow<int>(trackLength);
949 dataOutReg = dataReg;
950 dataRegWritten =
false;
955 }
catch (MSXException& ) {
960void WD2793::writeTrackData(EmuTime::param time)
963 bool prevA1 = lastWasA1;
967 uint8_t CRCvalue2 = 0;
972 }
else if (dataOutReg == 0xF5) {
983 crc.
init({0xA1, 0xA1});
984 }
else if (dataOutReg == 0xF6) {
987 }
else if (dataOutReg == 0xF7) {
989 dataOutReg = narrow_cast<uint8_t>(crc.
getValue() >> 8);
990 CRCvalue2 = narrow_cast<uint8_t>(crc.
getValue() >> 0);
992 }
else if (dataOutReg == 0xFE) {
997 if (prevA1) idam =
true;
1004 if (dataAvailable > 0) {
1005 drqTime.
reset(time);
1012 if (dataRegWritten) {
1013 dataOutReg = dataReg;
1014 dataRegWritten =
false;
1017 statusReg |= LOST_DATA;
1020 dataOutReg = CRCvalue2;
1022 drqTime.
reset(EmuTime::infinity());
1029 }
catch (MSXException&) {
1030 statusReg |= NOT_READY;
1035void WD2793::startType4Cmd(EmuTime::param time)
1038 uint8_t flags = commandReg & 0x0F;
1039 if (flags & (N2R_IRQ | R2N_IRQ)) {
1042 std::cerr <<
"WD2793 type 4 cmd, unimplemented bits " << int(flags) <<
'\n';
1046 if (flags == 0x00) {
1047 immediateIRQ =
false;
1049 if ((flags &
IDX_IRQ) && isReady()) {
1052 assert(irqTime == EmuTime::infinity());
1054 if (flags & IMM_IRQ) {
1055 immediateIRQ =
true;
1058 drqTime.
reset(EmuTime::infinity());
1062void WD2793::endCmd(EmuTime::param time)
1064 if ((hldTime <= time) && (time < (hldTime + IDLE))) {
1070 drqTime.
reset(EmuTime::infinity());
1071 irqTime = EmuTime::zero();
1076static constexpr std::initializer_list<enum_string<WD2793::FSM>> fsmStateInfo = {
1119template<
typename Archive>
1122 EmuTime bw_irqTime = EmuTime::zero();
1123 if (ar.versionAtLeast(version, 8)) {
1124 ar.template serializeBase<Schedulable>(*
this);
1126 constexpr int SCHED_FSM = 0;
1127 constexpr int SCHED_IDX_IRQ = 1;
1128 assert(Archive::IS_LOADER);
1131 if (old.userData == SCHED_FSM) {
1133 }
else if (old.userData == SCHED_IDX_IRQ) {
1134 bw_irqTime = old.time;
1139 ar.serialize(
"fsmState", fsmState,
1140 "statusReg", statusReg,
1141 "commandReg", commandReg,
1142 "sectorReg", sectorReg,
1143 "trackReg", trackReg,
1146 "directionIn", directionIn,
1147 "immediateIRQ", immediateIRQ,
1149 "dataCurrent", dataCurrent,
1150 "dataAvailable", dataAvailable);
1152 if (ar.versionAtLeast(version, 2)) {
1153 if (ar.versionAtLeast(version, 4)) {
1154 ar.serialize(
"drqTime", drqTime);
1156 assert(Archive::IS_LOADER);
1158 ar.serialize(
"drqTime", c);
1163 assert(Archive::IS_LOADER);
1169 drqTime.
reset(EmuTime::infinity());
1172 if (ar.versionAtLeast(version, 3)) {
1173 ar.serialize(
"lastWasA1", lastWasA1);
1175 ar.serialize(
"crc", crcVal);
1179 if (ar.versionAtLeast(version, 5)) {
1180 ar.serialize(
"pulse5", pulse5,
1181 "sectorInfo", sectorInfo);
1187 if (ar.versionAtLeast(version, 7)) {
1188 ar.serialize(
"irqTime", irqTime);
1190 assert(Archive::IS_LOADER);
1192 ar.serialize(
"INTRQ", INTRQ);
1193 irqTime = INTRQ ? EmuTime::zero() : EmuTime::infinity();
1194 if (bw_irqTime != EmuTime::zero()) {
1195 irqTime = bw_irqTime;
1199 if (ar.versionAtLeast(version, 11)) {
1200 ar.serialize(
"dataOutReg", dataOutReg,
1201 "dataRegWritten", dataRegWritten,
1202 "lastWasCRC", lastWasCRC);
1204 assert(Archive::IS_LOADER);
1205 dataOutReg = dataReg;
1206 dataRegWritten =
false;
1210 if (ar.versionBelow(version, 11)) {
1211 assert(Archive::IS_LOADER);
1214 if (statusReg & BUSY) {
1216 "Loading an old savestate that has an "
1217 "in-progress WD2793 command. This is not "
1218 "fully backwards-compatible and can cause "
1219 "wrong emulation behavior.");
1223 if (ar.versionAtLeast(version, 12)) {
1224 ar.serialize(
"hldTime", hldTime);
1226 if (statusReg & BUSY) {
1229 hldTime = EmuTime::infinity();
constexpr void update(uint8_t value)
Update CRC with one byte.
constexpr uint16_t getValue() const
Get current CRC value.
constexpr void init(uint16_t initialCRC)
(Re)initialize the current value
void printWarning(std::string_view message)
Represents a clock with a fixed frequency.
constexpr EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
This (abstract) class defines the DiskDrive interface.
virtual void applyWd2793ReadTrackQuirk()=0
See RawTrack::applyWd2793ReadTrackQuirk()
virtual bool isDiskInserted() const =0
Is drive ready?
virtual bool isWriteProtected() const =0
Is disk write protected?
virtual EmuTime getNextSector(EmuTime::param time, RawTrack::Sector §or)=0
virtual void step(bool direction, EmuTime::param time)=0
Step head.
virtual bool isTrack00() const =0
Head above track 0.
virtual bool indexPulse(EmuTime::param time)=0
Gets the state of the index pulse.
virtual EmuTime getTimeTillIndexPulse(EmuTime::param time, int count=1)=0
Return the time till the start of the next index pulse When there is no disk in the drive or when the...
virtual void flushTrack()=0
virtual void writeTrackByte(int idx, uint8_t val, bool addIdam=false)=0
virtual unsigned getTrackLength()=0
virtual uint8_t readTrackByte(int idx)=0
static constexpr unsigned ROTATIONS_PER_SECOND
virtual void invalidateWd2793ReadTrackQuirk()=0
void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
void setFreq(unsigned freq)
Change the frequency at which this clock ticks.
EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
static constexpr EmuDuration sec(unsigned x)
static constexpr EmuDuration msec(unsigned x)
static constexpr unsigned STANDARD_SIZE
Every class that wants to get scheduled at some point must inherit from this class.
void setSyncPoint(EmuTime::param timestamp)
bool pendingSyncPoint() const
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
static std::vector< SyncPointBW > serializeBW(Archive &ar)
uint8_t peekDataReg(EmuTime::param time) const
bool peekIRQ(EmuTime::param time) const
uint8_t getStatusReg(EmuTime::param time)
void setTrackReg(uint8_t value, EmuTime::param time)
void setSectorReg(uint8_t value, EmuTime::param time)
WD2793(Scheduler &scheduler, DiskDrive &drive, MSXCliComm &cliComm, EmuTime::param time, bool isWD1770)
This class has emulation for WD1770, WD1793, WD2793.
uint8_t getTrackReg(EmuTime::param time) const
uint8_t peekSectorReg(EmuTime::param time) const
bool peekDTRQ(EmuTime::param time) const
void setCommandReg(uint8_t value, EmuTime::param time)
uint8_t peekTrackReg(EmuTime::param time) const
bool getDTRQ(EmuTime::param time) const
uint8_t peekStatusReg(EmuTime::param time) const
void serialize(Archive &ar, unsigned version)
uint8_t getDataReg(EmuTime::param time)
void setDataReg(uint8_t value, EmuTime::param time)
bool getIRQ(EmuTime::param time) const
uint8_t getSectorReg(EmuTime::param time) const
void reset(EmuTime::param time)
This file implemented 3 utility functions:
uint32_t next(octet_iterator &it, octet_iterator end)
constexpr To narrow_cast(From &&from) noexcept
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)