openMSX
TC8566AF.cc
Go to the documentation of this file.
1 /*
2  * Based on code from NLMSX written by Frits Hilderink
3  * and blueMSX written by Daniel Vik
4  */
5 
6 #include "TC8566AF.hh"
7 #include "DiskDrive.hh"
8 #include "Clock.hh"
9 #include "CliComm.hh"
10 #include "MSXException.hh"
11 #include "serialize.hh"
12 
13 namespace openmsx {
14 
15 static const byte STM_DB0 = 0x01; // FDD 0 Busy
16 static const byte STM_DB1 = 0x02; // FDD 1 Busy
17 static const byte STM_DB2 = 0x04; // FDD 2 Busy
18 static const byte STM_DB3 = 0x08; // FDD 3 Busy
19 static const byte STM_CB = 0x10; // FDC Busy
20 static const byte STM_NDM = 0x20; // Non-DMA mode
21 static const byte STM_DIO = 0x40; // Data Input/Output
22 static const byte STM_RQM = 0x80; // Request for Master
23 
24 static const byte ST0_DS0 = 0x01; // Drive Select 0,1
25 static const byte ST0_DS1 = 0x02; //
26 static const byte ST0_HD = 0x04; // Head Address
27 static const byte ST0_NR = 0x08; // Not Ready
28 static const byte ST0_EC = 0x10; // Equipment Check
29 static const byte ST0_SE = 0x20; // Seek End
30 static const byte ST0_IC0 = 0x40; // Interrupt Code
31 static const byte ST0_IC1 = 0x80; //
32 
33 static const byte ST1_MA = 0x01; // Missing Address Mark
34 static const byte ST1_NW = 0x02; // Not Writable
35 static const byte ST1_ND = 0x04; // No Data
36 // = 0x08; // -
37 static const byte ST1_OR = 0x10; // Over Run
38 static const byte ST1_DE = 0x20; // Data Error
39 // = 0x40; // -
40 static const byte ST1_EN = 0x80; // End of Cylinder
41 
42 static const byte ST2_MD = 0x01; // Missing Address Mark in Data Field
43 static const byte ST2_BC = 0x02; // Bad Cylinder
44 static const byte ST2_SN = 0x04; // Scan Not Satisfied
45 static const byte ST2_SH = 0x08; // Scan Equal Satisfied
46 static const byte ST2_NC = 0x10; // No cylinder
47 static const byte ST2_DD = 0x20; // Data Error in Data Field
48 static const byte ST2_CM = 0x40; // Control Mark
49 // = 0x80; // -
50 
51 static const byte ST3_DS0 = 0x01; // Drive Select 0
52 static const byte ST3_DS1 = 0x02; // Drive Select 1
53 static const byte ST3_HD = 0x04; // Head Address
54 static const byte ST3_2S = 0x08; // Two Side
55 static const byte ST3_TK0 = 0x10; // Track 0
56 static const byte ST3_RDY = 0x20; // Ready
57 static const byte ST3_WP = 0x40; // Write Protect
58 static const byte ST3_FLT = 0x80; // Fault
59 
60 
61 TC8566AF::TC8566AF(Scheduler& scheduler, DiskDrive* drv[4], CliComm& cliComm_,
62  EmuTime::param time)
63  : Schedulable(scheduler)
64  , cliComm(cliComm_)
65  , delayTime(EmuTime::zero)
66  , headUnloadTime(EmuTime::zero) // head not loaded
67 {
68  // avoid UMR (on savestate)
69  dataAvailable = 0;
70  dataCurrent = 0;
71  setDrqRate();
72 
73  drive[0] = drv[0];
74  drive[1] = drv[1];
75  drive[2] = drv[2];
76  drive[3] = drv[3];
77  reset(time);
78 }
79 
81 {
82  drive[0]->setMotor(false, time);
83  drive[1]->setMotor(false, time);
84  drive[2]->setMotor(false, time);
85  drive[3]->setMotor(false, time);
86  //enableIntDma = 0;
87  //notReset = 1;
88  driveSelect = 0;
89 
90  status0 = 0;
91  status1 = 0;
92  status2 = 0;
93  status3 = 0;
94  commandCode = 0;
95  command = CMD_UNKNOWN;
96  phase = PHASE_IDLE;
97  phaseStep = 0;
98  cylinderNumber = 0;
99  headNumber = 0;
100  sectorNumber = 0;
101  number = 0;
102  currentTrack = 0;
103  sectorsPerCylinder = 0;
104  fillerByte = 0;
105  gapLength = 0;
106  specifyData[0] = 0; // TODO check
107  specifyData[1] = 0; // TODO check
108  seekValue = 0;
109  headUnloadTime = EmuTime::zero; // head not loaded
110 
111  mainStatus = STM_RQM;
112  //interrupt = false;
113 }
114 
116 {
117  switch (reg) {
118  case 4: // Main Status Register
119  return peekStatus();
120  case 5: // data port
121  return peekDataPort(time);
122  }
123  return 0xff;
124 }
125 
127 {
128  switch (reg) {
129  case 4: // Main Status Register
130  return readStatus(time);
131  case 5: // data port
132  return readDataPort(time);
133  }
134  return 0xff;
135 }
136 
137 byte TC8566AF::peekStatus() const
138 {
139  bool nonDMAMode = specifyData[1] & 1;
140  bool dma = nonDMAMode && (phase == PHASE_DATATRANSFER);
141  return mainStatus | (dma ? STM_NDM : 0);
142 }
143 
144 byte TC8566AF::readStatus(EmuTime::param time)
145 {
146  if (delayTime.before(time)) {
147  mainStatus |= STM_RQM;
148  }
149  return peekStatus();
150 }
151 
152 void TC8566AF::setDrqRate()
153 {
154  delayTime.setFreq(trackData.getLength() * DiskDrive::ROTATIONS_PER_SECOND);
155 }
156 
157 byte TC8566AF::peekDataPort(EmuTime::param time) const
158 {
159  switch (phase) {
160  case PHASE_DATATRANSFER:
161  return executionPhasePeek(time);
162  case PHASE_RESULT:
163  return resultsPhasePeek();
164  default:
165  return 0xff;
166  }
167 }
168 
169 byte TC8566AF::readDataPort(EmuTime::param time)
170 {
171  //interrupt = false;
172  switch (phase) {
173  case PHASE_DATATRANSFER:
174  if (delayTime.before(time)) {
175  return executionPhaseRead(time);
176  } else {
177  return 0xff; // TODO check this
178  }
179  case PHASE_RESULT:
180  return resultsPhaseRead(time);
181  default:
182  return 0xff;
183  }
184 }
185 
186 byte TC8566AF::executionPhasePeek(EmuTime::param time) const
187 {
188  switch (command) {
189  case CMD_READ_DATA:
190  if (delayTime.before(time)) {
191  assert(dataAvailable);
192  return trackData.read(dataCurrent);
193  } else {
194  return 0xff; // TODO check this
195  }
196  default:
197  return 0xff;
198  }
199 }
200 
201 byte TC8566AF::executionPhaseRead(EmuTime::param time)
202 {
203  switch (command) {
204  case CMD_READ_DATA: {
205  assert(dataAvailable);
206  byte result = trackData.read(dataCurrent++);
207  crc.update(result);
208  --dataAvailable;
209  delayTime += 1; // time when next byte will be available
210  mainStatus &= ~STM_RQM;
211  if (delayTime.before(time)) {
212  // lost data
213  status0 |= ST0_IC0;
214  status1 |= ST1_OR;
215  resultPhase();
216  } else if (!dataAvailable) {
217  // check crc error
218  word diskCrc = 256 * trackData.read(dataCurrent++);
219  diskCrc += trackData.read(dataCurrent++);
220  if (diskCrc != crc.getValue()) {
221  status0 |= ST0_IC0;
222  status1 |= ST1_DE;
223  status2 |= ST2_DD;
224  }
225  resultPhase();
226  }
227  return result;
228  }
229  default:
230  return 0xff;
231  }
232 }
233 
234 byte TC8566AF::resultsPhasePeek() const
235 {
236  switch (command) {
237  case CMD_READ_DATA:
238  case CMD_WRITE_DATA:
239  case CMD_FORMAT:
240  switch (phaseStep) {
241  case 0:
242  return status0;
243  case 1:
244  return status1;
245  case 2:
246  return status2;
247  case 3:
248  return cylinderNumber;
249  case 4:
250  return headNumber;
251  case 5:
252  return sectorNumber;
253  case 6:
254  return number;
255  }
256  break;
257 
259  switch (phaseStep) {
260  case 0:
261  return status0;
262  case 1:
263  return currentTrack;
264  }
265  break;
266 
268  switch (phaseStep) {
269  case 0:
270  return status3;
271  }
272  break;
273  default:
274  // nothing
275  break;
276  }
277  return 0xff;
278 }
279 
280 byte TC8566AF::resultsPhaseRead(EmuTime::param time)
281 {
282  byte result = resultsPhasePeek();
283  switch (command) {
284  case CMD_READ_DATA:
285  case CMD_WRITE_DATA:
286  case CMD_FORMAT:
287  switch (phaseStep++) {
288  case 6:
289  endCommand(time);
290  break;
291  }
292  break;
293 
295  switch (phaseStep++) {
296  case 1:
297  endCommand(time);
298  break;
299  }
300  break;
301 
303  switch (phaseStep++) {
304  case 0:
305  endCommand(time);
306  break;
307  }
308  break;
309  default:
310  // nothing
311  break;
312  }
313  return result;
314 }
315 
317 {
318  switch (reg) {
319  case 2: // control register 0
320  drive[3]->setMotor((data & 0x80) != 0, time);
321  drive[2]->setMotor((data & 0x40) != 0, time);
322  drive[1]->setMotor((data & 0x20) != 0, time);
323  drive[0]->setMotor((data & 0x10) != 0, time);
324  //enableIntDma = data & 0x08;
325  //notReset = data & 0x04;
326  driveSelect = data & 0x03;
327  break;
328 
329  //case 3: // control register 1
330  // controlReg1 = data;
331  // break;
332 
333  case 5: // data port
334  writeDataPort(data, time);
335  break;
336  }
337 }
338 
339 void TC8566AF::writeDataPort(byte value, EmuTime::param time)
340 {
341  switch (phase) {
342  case PHASE_IDLE:
343  idlePhaseWrite(value, time);
344  break;
345 
346  case PHASE_COMMAND:
347  commandPhaseWrite(value, time);
348  break;
349 
350  case PHASE_DATATRANSFER:
351  executionPhaseWrite(value, time);
352  break;
353  default:
354  // nothing
355  break;
356  }
357 }
358 
359 void TC8566AF::idlePhaseWrite(byte value, EmuTime::param time)
360 {
361  command = CMD_UNKNOWN;
362  commandCode = value;
363  if ((commandCode & 0x1f) == 0x06) command = CMD_READ_DATA;
364  if ((commandCode & 0x3f) == 0x05) command = CMD_WRITE_DATA;
365  if ((commandCode & 0x3f) == 0x09) command = CMD_WRITE_DELETED_DATA;
366  if ((commandCode & 0x1f) == 0x0c) command = CMD_READ_DELETED_DATA;
367  if ((commandCode & 0xbf) == 0x02) command = CMD_READ_DIAGNOSTIC;
368  if ((commandCode & 0xbf) == 0x0a) command = CMD_READ_ID;
369  if ((commandCode & 0xbf) == 0x0d) command = CMD_FORMAT;
370  if ((commandCode & 0x1f) == 0x11) command = CMD_SCAN_EQUAL;
371  if ((commandCode & 0x1f) == 0x19) command = CMD_SCAN_LOW_OR_EQUAL;
372  if ((commandCode & 0x1f) == 0x1d) command = CMD_SCAN_HIGH_OR_EQUAL;
373  if ((commandCode & 0xff) == 0x0f) command = CMD_SEEK;
374  if ((commandCode & 0xff) == 0x07) command = CMD_RECALIBRATE;
375  if ((commandCode & 0xff) == 0x08) command = CMD_SENSE_INTERRUPT_STATUS;
376  if ((commandCode & 0xff) == 0x03) command = CMD_SPECIFY;
377  if ((commandCode & 0xff) == 0x04) command = CMD_SENSE_DEVICE_STATUS;
378 
379  phase = PHASE_COMMAND;
380  phaseStep = 0;
381  mainStatus |= STM_CB;
382 
383  switch (command) {
384  case CMD_READ_DATA:
385  case CMD_WRITE_DATA:
386  case CMD_FORMAT:
387  status0 &= ~(ST0_IC0 | ST0_IC1);
388  status1 = 0;
389  status2 = 0;
390  //MT = value & 0x80;
391  //MFM = value & 0x40;
392  //SK = value & 0x20;
393  break;
394 
395  case CMD_RECALIBRATE:
396  status0 &= ~ST0_SE;
397  break;
398 
400  resultPhase();
401  break;
402 
403  case CMD_SEEK:
404  case CMD_SPECIFY:
406  break;
407 
408  default:
409  endCommand(time);
410  }
411 }
412 
413 void TC8566AF::commandPhase1(byte value)
414 {
415  drive[driveSelect]->setSide((value & 0x04) != 0);
416  status0 &= ~(ST0_DS0 | ST0_DS1 | ST0_IC0 | ST0_IC1);
417  status0 |= //(drive[driveSelect]->isDiskInserted() ? 0 : ST0_DS0) |
418  (value & (ST0_DS0 | ST0_DS1)) |
419  (drive[driveSelect]->isDummyDrive() ? ST0_IC1 : 0);
420  status3 = (value & (ST3_DS0 | ST3_DS1)) |
421  (drive[driveSelect]->isTrack00() ? ST3_TK0 : 0) |
422  (drive[driveSelect]->isDoubleSided() ? ST3_HD : 0) |
423  (drive[driveSelect]->isWriteProtected() ? ST3_WP : 0) |
424  (drive[driveSelect]->isDiskInserted() ? ST3_RDY : 0);
425 }
426 
427 EmuTime TC8566AF::locateSector(EmuTime::param time)
428 {
429  RawTrack::Sector sectorInfo;
430  int lastIdx = -1;
431  EmuTime next = time;
432  while (true) {
433  try {
434  next = drive[driveSelect]->getNextSector(
435  next, trackData, sectorInfo);
436  setDrqRate();
437  } catch (MSXException& /*e*/) {
438  return EmuTime::infinity;
439  }
440  if ((next == EmuTime::infinity) ||
441  (sectorInfo.addrIdx == lastIdx)) {
442  // no sectors on track or sector already seen
443  return EmuTime::infinity;
444  }
445  if (lastIdx == -1) lastIdx = sectorInfo.addrIdx;
446  if (sectorInfo.addrCrcErr) continue;
447  if (sectorInfo.track != cylinderNumber) continue;
448  if (sectorInfo.head != headNumber) continue;
449  if (sectorInfo.sector != sectorNumber) continue;
450  break;
451  }
452  // TODO does TC8566AF look at lower 3 bits?
453  dataAvailable = 128 << (sectorInfo.sizeCode & 7);
454  dataCurrent = sectorInfo.dataIdx;
455  return next;
456 }
457 
458 void TC8566AF::commandPhaseWrite(byte value, EmuTime::param time)
459 {
460  switch (command) {
461  case CMD_READ_DATA:
462  case CMD_WRITE_DATA:
463  switch (phaseStep++) {
464  case 0:
465  commandPhase1(value);
466  break;
467  case 1:
468  cylinderNumber = value;
469  break;
470  case 2:
471  headNumber = value;
472  break;
473  case 3:
474  sectorNumber = value;
475  break;
476  case 4:
477  number = value;
478  break;
479  case 5: // End Of Track
480  break;
481  case 6: // Gap Length
482  break;
483  case 7: // Data length
484  phase = PHASE_DATATRANSFER;
485  phaseStep = 0;
486  //interrupt = true;
487 
488  // load drive head, if not already loaded
489  EmuTime ready = time;
490  if (!isHeadLoaded(time)) {
491  ready += getHeadLoadDelay();
492  // set 'head is loaded'
493  headUnloadTime = EmuTime::infinity;
494  }
495 
496  // actually read sector: fills in
497  // trackData, dataAvailable and dataCurrent
498  ready = locateSector(ready);
499  if (ready == EmuTime::infinity) {
500  status0 |= ST0_IC0;
501  status1 |= ST1_ND;
502  resultPhase();
503  return;
504  }
505  if (command == CMD_READ_DATA) {
506  mainStatus |= STM_DIO;
507  } else {
508  mainStatus &= ~STM_DIO;
509  }
510  // Initialize crc
511  // TODO 0xFB vs 0xF8 depends on deleted vs normal data
512  crc.init<0xA1, 0xA1, 0xA1, 0xFB>();
513 
514  // first byte is available when it's rotated below the
515  // drive-head
516  delayTime.reset(ready);
517  mainStatus &= ~STM_RQM;
518  break;
519  }
520  break;
521 
522  case CMD_FORMAT:
523  switch (phaseStep++) {
524  case 0:
525  commandPhase1(value);
526  break;
527  case 1:
528  number = value;
529  break;
530  case 2:
531  sectorsPerCylinder = value;
532  sectorNumber = value;
533  break;
534  case 3:
535  gapLength = value;
536  break;
537  case 4:
538  fillerByte = value;
539  mainStatus &= ~STM_DIO;
540  phase = PHASE_DATATRANSFER;
541  phaseStep = 0;
542  //interrupt = true;
543  initTrackHeader(time);
544  break;
545  }
546  break;
547 
548  case CMD_SEEK:
549  switch (phaseStep++) {
550  case 0:
551  commandPhase1(value);
552  break;
553  case 1:
554  seekValue = value; // target track
555  doSeek(time);
556  break;
557  }
558  break;
559 
560  case CMD_RECALIBRATE:
561  switch (phaseStep++) {
562  case 0:
563  commandPhase1(value);
564  seekValue = 255; // max try 255 steps
565  doSeek(time);
566  break;
567  }
568  break;
569 
570  case CMD_SPECIFY:
571  specifyData[phaseStep] = value;
572  switch (phaseStep++) {
573  case 1:
574  endCommand(time);
575  break;
576  }
577  break;
578 
580  switch (phaseStep++) {
581  case 0:
582  commandPhase1(value);
583  resultPhase();
584  break;
585  }
586  break;
587  default:
588  // nothing
589  break;
590  }
591 }
592 
593 void TC8566AF::initTrackHeader(EmuTime::param time)
594 {
595  try {
596  // get track length, see comment in WD2793 for details.
597  drive[driveSelect]->readTrack(trackData);
598  } catch (MSXException& /*e*/) {
599  endCommand(time);
600  }
601  trackData.clear(trackData.getLength());
602  setDrqRate();
603  dataCurrent = 0;
604  dataAvailable = trackData.getLength();
605 
606  for (int i = 0; i < 80; ++i) trackData.write(dataCurrent++, 0x4E); // gap4a
607  for (int i = 0; i < 12; ++i) trackData.write(dataCurrent++, 0x00); // sync
608  for (int i = 0; i < 3; ++i) trackData.write(dataCurrent++, 0xC2); // index mark
609  for (int i = 0; i < 1; ++i) trackData.write(dataCurrent++, 0xFC); // " "
610  for (int i = 0; i < 50; ++i) trackData.write(dataCurrent++, 0x4E); // gap1
611 }
612 
613 void TC8566AF::formatSector()
614 {
615  for (int i = 0; i < 12; ++i) trackData.write(dataCurrent++, 0x00); // sync
616 
617  for (int i = 0; i < 3; ++i) trackData.write(dataCurrent++, 0xA1); // addr mark
618  trackData.addIdam(dataCurrent);
619  for (int i = 0; i < 1; ++i) trackData.write(dataCurrent++, 0xFE); // " "
620  trackData.write(dataCurrent++, currentTrack); // C: Cylinder number
621  trackData.write(dataCurrent++, headNumber); // H: Head Address
622  trackData.write(dataCurrent++, sectorNumber); // R: Record
623  trackData.write(dataCurrent++, number); // N: Length of sector
624  word addrCrc = trackData.calcCrc(dataCurrent - 8, 8);
625  trackData.write(dataCurrent++, addrCrc >> 8); // CRC (high byte)
626  trackData.write(dataCurrent++, addrCrc & 0xff); // (low byte)
627 
628  for (int i = 0; i < 22; ++i) trackData.write(dataCurrent++, 0x4E); // gap2
629  for (int i = 0; i < 12; ++i) trackData.write(dataCurrent++, 0x00); // sync
630 
631  for (int i = 0; i < 3; ++i) trackData.write(dataCurrent++, 0xA1); // data mark
632  for (int i = 0; i < 1; ++i) trackData.write(dataCurrent++, 0xFB); // " "
633 
634  int sectorSize = 128 << (number & 7); // 2 -> 512bytes
635  for (int i = 0; i < sectorSize; ++i) trackData.write(dataCurrent++, fillerByte);
636 
637  word dataCrc = trackData.calcCrc(dataCurrent - (sectorSize + 4), sectorSize + 4);
638  trackData.write(dataCurrent++, dataCrc >> 8); // CRC (high byte)
639  trackData.write(dataCurrent++, dataCrc & 0xff); // (low byte)
640 
641  for (int i = 0; i < gapLength; ++i) trackData.write(dataCurrent++, 0x4E); // gap3
642 }
643 
644 void TC8566AF::doSeek(EmuTime::param time)
645 {
646  DiskDrive& currentDrive = *drive[driveSelect];
647 
648  bool direction = false; // initialize to avoid warning
649  switch (command) {
650  case CMD_SEEK:
651  if (seekValue > currentTrack) {
652  ++currentTrack;
653  direction = true;
654  } else if (seekValue < currentTrack) {
655  --currentTrack;
656  direction = false;
657  } else {
658  assert(seekValue == currentTrack);
659  status0 |= ST0_SE;
660  endCommand(time);
661  return;
662  }
663  break;
664  case CMD_RECALIBRATE:
665  if (currentDrive.isTrack00() || (seekValue == 0)) {
666  if (seekValue == 0) {
667  status0 |= ST0_EC;
668  }
669  currentTrack = 0;
670  status0 |= ST0_SE;
671  endCommand(time);
672  return;
673  }
674  direction = false;
675  --seekValue;
676  break;
677  default:
678  UNREACHABLE;
679  }
680 
681  currentDrive.step(direction, time);
682 
683  setSyncPoint(time + getSeekDelay());
684 }
685 
686 void TC8566AF::executeUntil(EmuTime::param time, int /*userData*/)
687 {
688  if ((command == CMD_SEEK) ||
689  (command == CMD_RECALIBRATE)) {
690  doSeek(time);
691  }
692 }
693 
694 void TC8566AF::writeSector()
695 {
696  // write 2 CRC bytes (big endian)
697  trackData.write(dataCurrent++, crc.getValue() >> 8);
698  trackData.write(dataCurrent++, crc.getValue() & 0xFF);
699  drive[driveSelect]->writeTrack(trackData);
700 }
701 
702 void TC8566AF::executionPhaseWrite(byte value, EmuTime::param time)
703 {
704  switch (command) {
705  case CMD_WRITE_DATA:
706  assert(dataAvailable);
707  trackData.write(dataCurrent++, value);
708  crc.update(value);
709  --dataAvailable;
710  delayTime += 1; // time when next byte can be written
711  mainStatus &= ~STM_RQM;
712  if (delayTime.before(time)) {
713  // lost data
714  status0 |= ST0_IC0;
715  status1 |= ST1_OR;
716  resultPhase();
717  } else if (!dataAvailable) {
718  try {
719  writeSector();
720  } catch (MSXException&) {
721  status0 |= ST0_IC0;
722  status1 |= ST1_NW;
723  }
724  resultPhase();
725  }
726  break;
727 
728  case CMD_FORMAT:
729  delayTime += 1; // correct?
730  mainStatus &= ~STM_RQM;
731  switch (phaseStep & 3) {
732  case 0:
733  currentTrack = value;
734  break;
735  case 1:
736  headNumber = value;
737  break;
738  case 2:
739  sectorNumber = value;
740  break;
741  case 3:
742  number = value;
743  formatSector();
744  break;
745  }
746  ++phaseStep;
747 
748  if (phaseStep == 4 * sectorsPerCylinder) {
749  // data for all sectors was written, now write track
750  try {
751  drive[driveSelect]->writeTrack(trackData);
752  } catch (MSXException&) {
753  status0 |= ST0_IC0;
754  status1 |= ST1_NW;
755  }
756  resultPhase();
757  }
758  break;
759  default:
760  // nothing
761  break;
762  }
763 }
764 
765 void TC8566AF::resultPhase()
766 {
767  mainStatus |= STM_DIO | STM_RQM;
768  phase = PHASE_RESULT;
769  phaseStep = 0;
770  //interrupt = true;
771 }
772 
773 void TC8566AF::endCommand(EmuTime::param time)
774 {
775  phase = PHASE_IDLE;
776  mainStatus &= ~(STM_CB | STM_DIO);
777  delayTime.reset(time); // set STM_RQM
778  if (headUnloadTime == EmuTime::infinity) {
779  headUnloadTime = time + getHeadUnloadDelay();
780  }
781 }
782 
783 bool TC8566AF::diskChanged(unsigned driveNum)
784 {
785  assert(driveNum < 4);
786  return drive[driveNum]->diskChanged();
787 }
788 
789 bool TC8566AF::peekDiskChanged(unsigned driveNum) const
790 {
791  assert(driveNum < 4);
792  return drive[driveNum]->peekDiskChanged();
793 }
794 
795 
796 bool TC8566AF::isHeadLoaded(EmuTime::param time) const
797 {
798  return time < headUnloadTime;
799 }
800 EmuDuration TC8566AF::getHeadLoadDelay() const
801 {
802  return EmuDuration::msec(2 * (specifyData[1] >> 1)); // 2ms per unit
803 }
804 EmuDuration TC8566AF::getHeadUnloadDelay() const
805 {
806  return EmuDuration::msec(16 * (specifyData[0] & 0x0F)); // 16ms per unit
807 }
808 
809 EmuDuration TC8566AF::getSeekDelay() const
810 {
811  return EmuDuration::msec(16 - (specifyData[0] >> 4)); // 1ms per unit
812 }
813 
814 
815 static enum_string<TC8566AF::Command> commandInfo[] = {
816  { "UNKNOWN", TC8566AF::CMD_UNKNOWN },
817  { "READ_DATA", TC8566AF::CMD_READ_DATA },
818  { "WRITE_DATA", TC8566AF::CMD_WRITE_DATA },
819  { "WRITE_DELETED_DATA", TC8566AF::CMD_WRITE_DELETED_DATA },
820  { "READ_DELETED_DATA", TC8566AF::CMD_READ_DELETED_DATA },
821  { "READ_DIAGNOSTIC", TC8566AF::CMD_READ_DIAGNOSTIC },
822  { "READ_ID", TC8566AF::CMD_READ_ID },
823  { "FORMAT", TC8566AF::CMD_FORMAT },
824  { "SCAN_EQUAL", TC8566AF::CMD_SCAN_EQUAL },
825  { "SCAN_LOW_OR_EQUAL", TC8566AF::CMD_SCAN_LOW_OR_EQUAL },
826  { "SCAN_HIGH_OR_EQUAL", TC8566AF::CMD_SCAN_HIGH_OR_EQUAL },
827  { "SEEK", TC8566AF::CMD_SEEK },
828  { "RECALIBRATE", TC8566AF::CMD_RECALIBRATE },
829  { "SENSE_INTERRUPT_STATUS", TC8566AF::CMD_SENSE_INTERRUPT_STATUS },
830  { "SPECIFY", TC8566AF::CMD_SPECIFY },
831  { "SENSE_DEVICE_STATUS", TC8566AF::CMD_SENSE_DEVICE_STATUS }
832 };
833 SERIALIZE_ENUM(TC8566AF::Command, commandInfo);
834 
835 static enum_string<TC8566AF::Phase> phaseInfo[] = {
836  { "IDLE", TC8566AF::PHASE_IDLE },
837  { "COMMAND", TC8566AF::PHASE_COMMAND },
838  { "DATATRANSFER", TC8566AF::PHASE_DATATRANSFER },
839  { "RESULT", TC8566AF::PHASE_RESULT }
840 };
841 SERIALIZE_ENUM(TC8566AF::Phase, phaseInfo);
842 
843 // version 1: initial version
844 // version 2: added specifyData, headUnloadTime, seekValue
845 // inherit from Schedulable
846 // version 3: Replaced 'sectorSize', 'sectorOffset', 'sectorBuf'
847 // with 'dataAvailable', 'dataCurrent', .trackData'.
848 // Not 100% backwardscompatible, see also comments in WD2793.
849 // Added 'crc' and 'gapLength'.
850 // version 4: changed type of delayTime from Clock to DynamicClock
851 template<typename Archive>
852 void TC8566AF::serialize(Archive& ar, unsigned version)
853 {
854  if (ar.versionAtLeast(version, 4)) {
855  ar.serialize("delayTime", delayTime);
856  } else {
857  assert(ar.isLoader());
859  ar.serialize("delayTime", c);
860  delayTime.reset(c.getTime());
861  delayTime.setFreq(6250 * 5);
862  }
863  ar.serialize("command", command);
864  ar.serialize("phase", phase);
865  ar.serialize("phaseStep", phaseStep);
866  ar.serialize("driveSelect", driveSelect);
867  ar.serialize("mainStatus", mainStatus);
868  ar.serialize("status0", status0);
869  ar.serialize("status1", status1);
870  ar.serialize("status2", status2);
871  ar.serialize("status3", status3);
872  ar.serialize("commandCode", commandCode);
873  ar.serialize("cylinderNumber", cylinderNumber);
874  ar.serialize("headNumber", headNumber);
875  ar.serialize("sectorNumber", sectorNumber);
876  ar.serialize("number", number);
877  ar.serialize("currentTrack", currentTrack);
878  ar.serialize("sectorsPerCylinder", sectorsPerCylinder);
879  ar.serialize("fillerByte", fillerByte);
880  if (ar.versionAtLeast(version, 2)) {
881  ar.template serializeBase<Schedulable>(*this);
882  ar.serialize("specifyData", specifyData);
883  ar.serialize("headUnloadTime", headUnloadTime);
884  ar.serialize("seekValue", seekValue);
885  } else {
886  assert(ar.isLoader());
887  specifyData[0] = 0xDF; // values normally set by TurboR disk rom
888  specifyData[1] = 0x03;
889  headUnloadTime = EmuTime::zero;
890  seekValue = 0;
891  }
892  if (ar.versionAtLeast(version, 3)) {
893  ar.serialize("dataAvailable", dataAvailable);
894  ar.serialize("dataCurrent", dataCurrent);
895  ar.serialize("trackData", trackData);
896  ar.serialize("gapLength", gapLength);
897  word crcVal = crc.getValue();
898  ar.serialize("crc", crcVal);
899  crc.init(crcVal);
900  } else {
901  // Compared to previous versions the buffer managment worked
902  // differently (was sector oriented instead of track oriented).
903  // Converting the old state to the new state is not that easy,
904  // we only give a warning when an old savestate that was in
905  // the middle of a read/write operation is loaded.
906  //ar.serialize("sectorSize", sectorSize);
907  //ar.serialize("sectorOffset", sectorOffset);
908  //ar.serialize_blob("sectorBuf", sectorBuf, sizeof(sectorBuf));
909  //TODO wrning
910  if ((phase == PHASE_DATATRANSFER) &&
911  ((command == CMD_READ_DATA) ||
912  (command == CMD_WRITE_DATA) ||
913  (command == CMD_FORMAT))) {
914  cliComm.printWarning(
915  "Loading an old savestate that had an "
916  "in-progress TC8566AF data-transfer command. "
917  "This is not fully backwards-compatible and "
918  "could cause wrong emulation behavior.");
919  }
920  }
921 };
923 
924 } // namespace openmsx
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
word calcCrc(int idx, int length) const
Convenience method to calculate CRC for part of this track.
Definition: RawTrack.cc:172
static const unsigned ROTATIONS_PER_SECOND
Definition: DiskDrive.hh:16
void addIdam(unsigned idx)
Definition: RawTrack.cc:34
void init(uint16_t initialCRC)
(Re)initialize the current value
Definition: CRC16.hh:25
static EmuDuration msec(unsigned x)
Definition: EmuDuration.hh:38
uint16_t getValue() const
Get current CRC value.
Definition: CRC16.hh:110
static param dummy()
Definition: EmuTime.hh:21
uint32_t next(octet_iterator &it, octet_iterator end)
bool before(EmuTime::param e) const
Checks whether this clock's last tick is or is not before the given time stamp.
Definition: DynamicClock.hh:44
unsigned getLength() const
Get track length.
Definition: RawTrack.hh:96
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:33
void printWarning(string_ref message)
Definition: CliComm.cc:28
virtual void setSide(bool side)=0
Side select.
Represents a clock with a fixed frequency.
Definition: Clock.hh:18
void setSyncPoint(EmuTime::param timestamp, int userData=0)
Definition: Schedulable.cc:25
virtual EmuTime getNextSector(EmuTime::param time, RawTrack &track, RawTrack::Sector &sector)=0
byte readReg(int reg, EmuTime::param time)
Definition: TC8566AF.cc:126
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:16
virtual void writeTrack(const RawTrack &track)=0
void write(int idx, byte val)
Definition: RawTrack.hh:104
virtual void readTrack(RawTrack &track)=0
virtual bool isTrack00() const =0
Head above track 0.
static const EmuTime infinity
Definition: EmuTime.hh:58
void serialize(Archive &ar, unsigned version)
Definition: TC8566AF.cc:852
void setFreq(unsigned freq)
Change the frequency at which this clock ticks.
Definition: DynamicClock.hh:86
const EmuTime & param
Definition: EmuTime.hh:20
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:38
void reset(EmuTime::param time)
Definition: TC8566AF.cc:80
void clear(unsigned size)
Clear track data.
Definition: RawTrack.cc:28
virtual void setMotor(bool status, EmuTime::param time)=0
Set motor on/off.
TC8566AF(Scheduler &scheduler, DiskDrive *drive[4], CliComm &cliComm, EmuTime::param time)
Definition: TC8566AF.cc:61
virtual bool isDummyDrive() const =0
Is there a dummy (unconncted) drive?
static const EmuTime zero
Definition: EmuTime.hh:57
void update(uint8_t value)
Update CRC with one byte.
Definition: CRC16.hh:66
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:802
virtual bool diskChanged()=0
Is disk changed?
bool diskChanged(unsigned driveNum)
Definition: TC8566AF.cc:783
uint8_t * data()
virtual bool peekDiskChanged() const =0
This (abstract) class defines the DiskDrive interface.
Definition: DiskDrive.hh:13
bool peekDiskChanged(unsigned driveNum) const
Definition: TC8566AF.cc:789
void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
void writeReg(int reg, byte data, EmuTime::param time)
Definition: TC8566AF.cc:316
byte peekReg(int reg, EmuTime::param time) const
Definition: TC8566AF.cc:115
byte read(int idx) const
Definition: RawTrack.hh:103
#define UNREACHABLE
Definition: unreachable.hh:56