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(FSMState state, EmuTime::param time)
334void WD2793::executeUntil(EmuTime::param time)
340 if ((commandReg & 0x80) == 0x00) {
346 if ((commandReg & 0xC0) == 0x80) {
352 if ((commandReg & 0xC0) == 0x80) {
358 if ((commandReg & 0xC0) == 0x80) {
364 if ((commandReg & 0xE0) == 0xA0) {
366 checkStartWrite(time);
370 if ((commandReg & 0xE0) == 0xA0) {
372 preWriteSector(time);
376 if ((commandReg & 0xE0) == 0xA0) {
378 writeSectorData(time);
382 if ((commandReg & 0xE0) == 0xA0) {
384 postWriteSector(time);
388 if (((commandReg & 0xC0) == 0xC0) &&
389 ((commandReg & 0xF0) != 0xD0)) {
395 if (((commandReg & 0xC0) == 0xC0) &&
396 ((commandReg & 0xF0) != 0xD0)) {
402 if ((commandReg & 0xF0) == 0xF0) {
404 writeTrackData(time);
408 if ((commandReg & 0xF0) == 0xE0) {
419void WD2793::startType1Cmd(EmuTime::param time)
421 statusReg &= ~(SEEK_ERROR | CRC_ERROR);
424 if (commandReg & H_FLAG) {
431 hldTime = EmuTime::infinity();
434 switch (commandReg & 0xF0) {
464void WD2793::seek(EmuTime::param time)
466 if (trackReg == dataReg) {
469 directionIn = (dataReg > trackReg);
474void WD2793::step(EmuTime::param time)
476 static constexpr std::array<EmuDuration, 4> timePerStep = {
484 if ((commandReg & T_FLAG) || ((commandReg & 0xE0) == 0x00)) {
496 drive.
step(directionIn, time);
497 schedule(
FSM_SEEK, time + timePerStep[commandReg & STEP_SPEED]);
501void WD2793::seekNext(EmuTime::param time)
503 if ((commandReg & 0xE0) == 0x00) {
511void WD2793::endType1Cmd(EmuTime::param time)
513 if (commandReg & V_FLAG) {
521void WD2793::startType2Cmd(EmuTime::param time)
523 statusReg &= ~(LOST_DATA | RECORD_NOT_FOUND |
524 RECORD_TYPE | WRITE_PROTECTED);
526 dataRegWritten =
false;
534 if (commandReg & E_FLAG) {
543void WD2793::type2Loaded(EmuTime::param time)
547 statusReg |= WRITE_PROTECTED;
556void WD2793::type2Search(EmuTime::param time)
558 assert(time < pulse5);
568 }
catch (MSXException& ) {
573 if (pulse5 < EmuTime::infinity()) {
581void WD2793::type2Rotated(EmuTime::param time)
585 statusReg |= CRC_ERROR;
587 statusReg &= ~CRC_ERROR;
590 (sectorInfo.
track != trackReg) ||
591 (sectorInfo.
sector != sectorReg)) {
597 if (sectorInfo.
dataIdx == -1) {
606 switch (commandReg & 0xE0) {
608 startReadSector(time);
612 startWriteSector(time);
617void WD2793::type2NotFound(EmuTime::param time)
619 statusReg |= RECORD_NOT_FOUND;
623void WD2793::startReadSector(EmuTime::param time)
626 crc.
init({0xA1, 0xA1, 0xA1, 0xF8});
628 crc.
init({0xA1, 0xA1, 0xA1, 0xFB});
632 unsigned gapLength = (tmp >= 0) ? tmp : (tmp + trackLength);
633 assert(gapLength < trackLength);
635 drqTime += gapLength + 1 + 1;
636 dataCurrent = sectorInfo.
dataIdx;
642 dataAvailable = 128 << (sectorInfo.
sizeCode & 3);
645void WD2793::startWriteSector(EmuTime::param time)
673void WD2793::checkStartWrite(EmuTime::param time)
677 if (!dataRegWritten) {
678 statusReg |= LOST_DATA;
691 dataCurrent = sectorInfo.
addrIdx
698 drqTime.
reset(EmuTime::infinity());
702void WD2793::preWriteSector(EmuTime::param time)
706 if (dataAvailable > 0) {
707 if (dataAvailable >= 4) {
716 drqTime.
reset(EmuTime::infinity());
719 crc.
init({0xA1, 0xA1, 0xA1});
720 uint8_t mark = (commandReg & A0_FLAG) ? 0xF8 : 0xFB;
725 dataOutReg = dataReg;
726 dataRegWritten =
false;
727 dataAvailable = 128 << (sectorInfo.
sizeCode & 3);
735 }
catch (MSXException&) {
736 statusReg |= NOT_READY;
741void WD2793::writeSectorData(EmuTime::param time)
749 if (dataAvailable > 0) {
750 if (dataRegWritten) {
751 dataOutReg = dataReg;
752 dataRegWritten =
false;
755 statusReg |= LOST_DATA;
767 drqTime.
reset(EmuTime::infinity());
769 }
catch (MSXException&) {
770 statusReg |= NOT_READY;
775void WD2793::postWriteSector(EmuTime::param time)
779 if (dataAvailable > 0) {
781 uint8_t val = (dataAvailable == 2) ? narrow_cast<uint8_t>((crc.
getValue() >> 8))
782 : narrow_cast<uint8_t>((crc.
getValue() & 0xFF));
786 drqTime.
reset(EmuTime::infinity());
794 if (!(commandReg & M_FLAG)) {
798 drqTime.
reset(EmuTime::infinity());
803 }
catch (MSXException&) {
805 statusReg |= NOT_READY;
811void WD2793::startType3Cmd(EmuTime::param time)
813 statusReg &= ~(LOST_DATA | RECORD_NOT_FOUND | RECORD_TYPE);
819 if ((commandReg & 0xF0) == 0xF0) {
827 if (commandReg & E_FLAG) {
836void WD2793::type3Loaded(EmuTime::param time)
841 statusReg |= WRITE_PROTECTED;
846 EmuTime
next(EmuTime::dummy());
847 if ((commandReg & 0xF0) == 0xC0) {
853 if (next == EmuTime::infinity()) {
855 statusReg |= RECORD_NOT_FOUND;
859 dataCurrent = sectorInfo.
addrIdx;
862 }
catch (MSXException&) {
864 statusReg |= RECORD_NOT_FOUND;
872 if (next == EmuTime::infinity()) {
882void WD2793::type3Rotated(EmuTime::param time)
884 switch (commandReg & 0xF0) {
886 readAddressCmd(time);
892 startWriteTrack(time);
897void WD2793::readAddressCmd(EmuTime::param time)
903void WD2793::readTrackCmd(EmuTime::param time)
908 setDrqRate(trackLength);
910 dataAvailable = narrow<int>(trackLength);
917 }
catch (MSXException&) {
924void WD2793::startWriteTrack(EmuTime::param time)
928 if (!dataRegWritten) {
929 statusReg |= LOST_DATA;
935 setDrqRate(trackLength);
937 dataAvailable = narrow<int>(trackLength);
940 dataOutReg = dataReg;
941 dataRegWritten =
false;
946 }
catch (MSXException& ) {
951void WD2793::writeTrackData(EmuTime::param time)
954 bool prevA1 = lastWasA1;
958 uint8_t CRCvalue2 = 0;
963 }
else if (dataOutReg == 0xF5) {
974 crc.
init({0xA1, 0xA1});
975 }
else if (dataOutReg == 0xF6) {
978 }
else if (dataOutReg == 0xF7) {
980 dataOutReg = narrow_cast<uint8_t>(crc.
getValue() >> 8);
981 CRCvalue2 = narrow_cast<uint8_t>(crc.
getValue() >> 0);
983 }
else if (dataOutReg == 0xFE) {
988 if (prevA1) idam =
true;
995 if (dataAvailable > 0) {
1003 if (dataRegWritten) {
1004 dataOutReg = dataReg;
1005 dataRegWritten =
false;
1008 statusReg |= LOST_DATA;
1011 dataOutReg = CRCvalue2;
1013 drqTime.
reset(EmuTime::infinity());
1020 }
catch (MSXException&) {
1021 statusReg |= NOT_READY;
1026void WD2793::startType4Cmd(EmuTime::param time)
1029 uint8_t flags = commandReg & 0x0F;
1030 if (flags & (N2R_IRQ | R2N_IRQ)) {
1033 std::cerr <<
"WD2793 type 4 cmd, unimplemented bits " << int(flags) <<
'\n';
1037 if (flags == 0x00) {
1038 immediateIRQ =
false;
1040 if ((flags & IDX_IRQ) && isReady()) {
1043 assert(irqTime == EmuTime::infinity());
1045 if (flags & IMM_IRQ) {
1046 immediateIRQ =
true;
1049 drqTime.
reset(EmuTime::infinity());
1053void WD2793::endCmd(EmuTime::param time)
1055 if ((hldTime <= time) && (time < (hldTime + IDLE))) {
1061 drqTime.
reset(EmuTime::infinity());
1062 irqTime = EmuTime::zero();
1067static constexpr std::initializer_list<enum_string<WD2793::FSMState>> fsmStateInfo = {
1110template<
typename Archive>
1113 EmuTime bw_irqTime = EmuTime::zero();
1114 if (ar.versionAtLeast(version, 8)) {
1115 ar.template serializeBase<Schedulable>(*
this);
1117 constexpr int SCHED_FSM = 0;
1118 constexpr int SCHED_IDX_IRQ = 1;
1119 assert(Archive::IS_LOADER);
1122 if (old.userData == SCHED_FSM) {
1124 }
else if (old.userData == SCHED_IDX_IRQ) {
1125 bw_irqTime = old.time;
1130 ar.serialize(
"fsmState", fsmState,
1131 "statusReg", statusReg,
1132 "commandReg", commandReg,
1133 "sectorReg", sectorReg,
1134 "trackReg", trackReg,
1137 "directionIn", directionIn,
1138 "immediateIRQ", immediateIRQ,
1140 "dataCurrent", dataCurrent,
1141 "dataAvailable", dataAvailable);
1143 if (ar.versionAtLeast(version, 2)) {
1144 if (ar.versionAtLeast(version, 4)) {
1145 ar.serialize(
"drqTime", drqTime);
1147 assert(Archive::IS_LOADER);
1149 ar.serialize(
"drqTime", c);
1154 assert(Archive::IS_LOADER);
1160 drqTime.
reset(EmuTime::infinity());
1163 if (ar.versionAtLeast(version, 3)) {
1164 ar.serialize(
"lastWasA1", lastWasA1);
1166 ar.serialize(
"crc", crcVal);
1170 if (ar.versionAtLeast(version, 5)) {
1171 ar.serialize(
"pulse5", pulse5,
1172 "sectorInfo", sectorInfo);
1178 if (ar.versionAtLeast(version, 7)) {
1179 ar.serialize(
"irqTime", irqTime);
1181 assert(Archive::IS_LOADER);
1183 ar.serialize(
"INTRQ", INTRQ);
1184 irqTime = INTRQ ? EmuTime::zero() : EmuTime::infinity();
1185 if (bw_irqTime != EmuTime::zero()) {
1186 irqTime = bw_irqTime;
1190 if (ar.versionAtLeast(version, 11)) {
1191 ar.serialize(
"dataOutReg", dataOutReg,
1192 "dataRegWritten", dataRegWritten,
1193 "lastWasCRC", lastWasCRC);
1195 assert(Archive::IS_LOADER);
1196 dataOutReg = dataReg;
1197 dataRegWritten =
false;
1201 if (ar.versionBelow(version, 11)) {
1202 assert(Archive::IS_LOADER);
1205 if (statusReg & BUSY) {
1207 "Loading an old savestate that has an "
1208 "in-progress WD2793 command. This is not "
1209 "fully backwards-compatible and can cause "
1210 "wrong emulation behavior.");
1214 if (ar.versionAtLeast(version, 12)) {
1215 ar.serialize(
"hldTime", hldTime);
1217 if (statusReg & BUSY) {
1220 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:
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
uint32_t next(octet_iterator &it, octet_iterator end)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)