openMSX
NowindHost.cc
Go to the documentation of this file.
1 #include "NowindHost.hh"
2 #include "DiskContainer.hh"
3 #include "FileOperations.hh"
4 #include "MSXException.hh"
6 #include "enumerate.hh"
7 #include "one_of.hh"
8 #include "serialize.hh"
9 #include "serialize_stl.hh"
10 #include "unreachable.hh"
11 #include "xrange.hh"
12 #include <algorithm>
13 #include <cassert>
14 #include <cctype>
15 #include <cstdio>
16 #include <cstdarg>
17 #include <ctime>
18 #include <fstream>
19 #include <memory>
20 
21 using std::string;
22 using std::vector;
23 using std::fstream;
24 using std::ios;
25 
26 namespace openmsx {
27 
28 constexpr unsigned SECTOR_SIZE = sizeof(SectorBuffer);
29 
30 static void DBERR(const char* message, ...)
31 {
32  (void)message;
33 #if 0
34  va_list args;
35  va_start(args, message);
36  printf("nowind: ");
37  vprintf(message, args);
38  va_end(args);
39 #endif
40 }
41 
42 
44  : drives(drives_)
45  , lastTime(0)
46  , state(STATE_SYNC1)
47  , romdisk(255)
48  , allowOtherDiskroms(false)
49  , enablePhantomDrives(true)
50 {
51 }
52 
53 NowindHost::~NowindHost() = default;
54 
55 byte NowindHost::peek() const
56 {
57  return isDataAvailable() ? hostToMsxFifo.front() : 0xFF;
58 }
59 
60 // receive: msx <- pc
62 {
63  return isDataAvailable() ? hostToMsxFifo.pop_front() : 0xFF;
64 }
65 
67 {
68  return !hostToMsxFifo.empty();
69 }
70 
71 
72 // send: msx -> pc
73 void NowindHost::write(byte data, unsigned time)
74 {
75  unsigned duration = time - lastTime;
76  lastTime = time;
77  if (duration >= 500) {
78  // timeout (500ms), start looking for AF05
79  purge();
80  state = STATE_SYNC1;
81  }
82 
83  switch (state) {
84  case STATE_SYNC1:
85  if (data == 0xAF) state = STATE_SYNC2;
86  break;
87  case STATE_SYNC2:
88  switch (data) {
89  case 0x05: state = STATE_COMMAND; recvCount = 0; break;
90  case 0xAF: state = STATE_SYNC2; break;
91  case 0xFF: state = STATE_SYNC1; msxReset(); break;
92  default: state = STATE_SYNC1; break;
93  }
94  break;
95  case STATE_COMMAND:
96  assert(recvCount < 9);
97  cmdData[recvCount] = data;
98  if (++recvCount == 9) {
99  executeCommand();
100  }
101  break;
102  case STATE_DISKREAD:
103  assert(recvCount < 2);
104  extraData[recvCount] = data;
105  if (++recvCount == 2) {
106  doDiskRead2();
107  }
108  break;
109  case STATE_DISKWRITE:
110  assert(recvCount < (transferSize + 2));
111  extraData[recvCount] = data;
112  if (++recvCount == (transferSize + 2)) {
113  doDiskWrite2();
114  }
115  break;
116  case STATE_DEVOPEN:
117  assert(recvCount < 11);
118  extraData[recvCount] = data;
119  if (++recvCount == 11) {
120  deviceOpen();
121  }
122  break;
123  case STATE_IMAGE:
124  assert(recvCount < 40);
125  extraData[recvCount] = data;
126  if (data == one_of(0, ':') ||
127  (++recvCount == 40)) {
128  char* eData = reinterpret_cast<char*>(extraData);
129  callImage(string(eData, recvCount));
130  state = STATE_SYNC1;
131  }
132  break;
133  case STATE_MESSAGE:
134  assert(recvCount < (240 - 1));
135  extraData[recvCount] = data;
136  if ((data == 0) || (++recvCount == (240 - 1))) {
137  extraData[recvCount] = 0;
138  DBERR("%s\n", reinterpret_cast<char*>(extraData));
139  state = STATE_SYNC1;
140  }
141  break;
142  default:
143  UNREACHABLE;
144  }
145 }
146 
147 void NowindHost::msxReset()
148 {
149  for (auto& dev : devices) {
150  dev.fs.reset();
151  }
152  DBERR("MSX reset\n");
153 }
154 
155 SectorAccessibleDisk* NowindHost::getDisk() const
156 {
157  byte num = cmdData[7]; // reg_a
158  if (num >= drives.size()) {
159  return nullptr;
160  }
161  return drives[num]->getSectorAccessibleDisk();
162 }
163 
164 void NowindHost::executeCommand()
165 {
166  assert(recvCount == 9);
167  byte cmd = cmdData[8];
168  switch (cmd) {
169  //case 0x0D: BDOS_0DH_DiskReset();
170  //case 0x0F: BDOS_0FH_OpenFile();
171  //case 0x10: BDOS_10H_CloseFile();
172  //case 0x11: BDOS_11H_FindFirst();
173  //case 0x12: BDOS_12H_FindNext();
174  //case 0x13: BDOS_13H_DeleteFile();
175  //case 0x14: BDOS_14H_ReadSeq();
176  //case 0x15: BDOS_15H_WriteSeq();
177  //case 0x16: BDOS_16H_CreateFile();
178  //case 0x17: BDOS_17H_RenameFile();
179  //case 0x21: BDOS_21H_ReadRandomFile();
180  //case 0x22: BDOS_22H_WriteRandomFile();
181  //case 0x23: BDOS_23H_GetFileSize();
182  //case 0x24: BDOS_24H_SetRandomRecordField();
183  //case 0x26: BDOS_26H_WriteRandomBlock();
184  //case 0x27: BDOS_27H_ReadRandomBlock();
185  //case 0x28: BDOS_28H_WriteRandomFileWithZeros();
186  //case 0x2A: BDOS_2AH_GetDate();
187  //case 0x2B: BDOS_2BH_SetDate();
188  //case 0x2C: BDOS_2CH_GetTime();
189  //case 0x2D: BDOS_2DH_SetTime();
190  //case 0x2E: BDOS_2EH_Verify();
191  //case 0x2F: BDOS_2FH_ReadLogicalSector();
192  //case 0x30: BDOS_30H_WriteLogicalSector();
193 
194  case 0x80: { // DSKIO
195  auto* disk = getDisk();
196  if (!disk) {
197  // no such drive or no disk inserted
198  // (causes a timeout on the MSX side)
199  state = STATE_SYNC1;
200  return;
201  }
202  byte reg_f = cmdData[6];
203  if (reg_f & 1) { // carry flag
204  diskWriteInit(*disk);
205  } else {
206  diskReadInit(*disk);
207  }
208  break;
209  }
210 
211  case 0x81: DSKCHG(); state = STATE_SYNC1; break;
212  //case 0x82: GETDPB();
213  //case 0x83: CHOICE();
214  //case 0x84: DSKFMT();
215  case 0x85: DRIVES(); state = STATE_SYNC1; break;
216  case 0x86: INIENV(); state = STATE_SYNC1; break;
217  case 0x87: setDateMSX(); state = STATE_SYNC1; break;
218  case 0x88: state = STATE_DEVOPEN; recvCount = 0; break;
219  case 0x89: deviceClose(); state = STATE_SYNC1; break;
220  //case 0x8A: deviceRandomIO(fcb);
221  case 0x8B: deviceWrite(); state = STATE_SYNC1; break;
222  case 0x8C: deviceRead(); state = STATE_SYNC1; break;
223  //case 0x8D: deviceEof(fcb);
224  //case 0x8E: auxIn();
225  //case 0x8F: auxOut();
226  case 0x90: state = STATE_MESSAGE; recvCount = 0; break;
227  case 0xA0: state = STATE_IMAGE; recvCount = 0; break;
228  //case 0xFF: vramDump();
229  default:
230  // Unknown USB command!
231  state = STATE_SYNC1;
232  break;
233  }
234 }
235 
236 // send: pc -> msx
237 void NowindHost::send(byte value)
238 {
239  hostToMsxFifo.push_back(value);
240 }
241 void NowindHost::send16(word value)
242 {
243  hostToMsxFifo.push_back(value & 255);
244  hostToMsxFifo.push_back(value >> 8);
245 }
246 
247 void NowindHost::purge()
248 {
249  hostToMsxFifo.clear();
250 }
251 
252 void NowindHost::sendHeader()
253 {
254  send(0xFF); // needed because first read might fail!
255  send(0xAF);
256  send(0x05);
257 }
258 
259 void NowindHost::DSKCHG()
260 {
261  auto* disk = getDisk();
262  if (!disk) {
263  // no such drive or no disk inserted
264  return;
265  }
266 
267  sendHeader();
268  byte num = cmdData[7]; // reg_a
269  assert(num < drives.size());
270  if (drives[num]->diskChanged()) {
271  send(255); // changed
272  // read first FAT sector (contains media descriptor)
273  SectorBuffer sectorBuffer;
274  try {
275  disk->readSectors(&sectorBuffer, 1, 1);
276  } catch (MSXException&) {
277  // TODO read error
278  sectorBuffer.raw[0] = 0;
279  }
280  send(sectorBuffer.raw[0]); // new mediadescriptor
281  } else {
282  send(0); // not changed
283  // TODO shouldn't we send some (dummy) byte here?
284  // nowind-diskrom seems to read it (but doesn't use it)
285  }
286 }
287 
288 void NowindHost::DRIVES()
289 {
290  // at least one drive (MSX-DOS1 cannot handle 0 drives)
291  byte numberOfDrives = std::max<byte>(1, byte(drives.size()));
292 
293  byte reg_a = cmdData[7];
294  sendHeader();
295  send(getEnablePhantomDrives() ? 0x02 : 0);
296  send(reg_a | (getAllowOtherDiskroms() ? 0 : 0x80));
297  send(numberOfDrives);
298 
299  romdisk = 255; // no romdisk
300  for (auto [i, drv] : enumerate(drives)) {
301  if (drv->isRomdisk()) {
302  romdisk = byte(i);
303  break;
304  }
305  }
306 }
307 
308 void NowindHost::INIENV()
309 {
310  sendHeader();
311  send(romdisk); // calculated in DRIVES()
312 }
313 
314 void NowindHost::setDateMSX()
315 {
316  auto td = time(nullptr);
317  auto* tm = localtime(&td);
318 
319  sendHeader();
320  send(tm->tm_mday); // day
321  send(tm->tm_mon + 1); // month
322  send16(tm->tm_year + 1900); // year
323 }
324 
325 
326 unsigned NowindHost::getSectorAmount() const
327 {
328  byte reg_b = cmdData[1];
329  return reg_b;
330 }
331 unsigned NowindHost::getStartSector() const
332 {
333  byte reg_c = cmdData[0];
334  byte reg_e = cmdData[2];
335  byte reg_d = cmdData[3];
336  unsigned startSector = reg_e + (reg_d * 256);
337  if (reg_c < 0x80) {
338  // FAT16 read/write sector
339  startSector += reg_c << 16;
340  }
341  return startSector;
342 }
343 unsigned NowindHost::getStartAddress() const
344 {
345  byte reg_l = cmdData[4];
346  byte reg_h = cmdData[5];
347  return reg_h * 256 + reg_l;
348 }
349 unsigned NowindHost::getCurrentAddress() const
350 {
351  unsigned startAddress = getStartAddress();
352  return startAddress + transferred;
353 }
354 
355 
356 void NowindHost::diskReadInit(SectorAccessibleDisk& disk)
357 {
358  unsigned sectorAmount = getSectorAmount();
359  buffer.resize(sectorAmount);
360  unsigned startSector = getStartSector();
361  try {
362  disk.readSectors(buffer.data(), startSector, sectorAmount);
363  } catch (MSXException&) {
364  // read error
365  state = STATE_SYNC1;
366  return;
367  }
368 
369  transferred = 0;
370  retryCount = 0;
371  doDiskRead1();
372 }
373 
374 void NowindHost::doDiskRead1()
375 {
376  unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
377  if (bytesLeft == 0) {
378  sendHeader();
379  send(0x01); // end of receive-loop
380  send(0x00); // no more data
381  state = STATE_SYNC1;
382  return;
383  }
384 
385  constexpr unsigned NUMBEROFBLOCKS = 32; // 32 * 64 bytes = 2048 bytes
386  transferSize = std::min(bytesLeft, NUMBEROFBLOCKS * 64); // hardcoded in firmware
387 
388  unsigned address = getCurrentAddress();
389  if (address >= 0x8000) {
390  if (transferSize & 0x003F) {
391  transferSectors(address, transferSize);
392  } else {
393  transferSectorsBackwards(address, transferSize);
394  }
395  } else {
396  // transfer below 0x8000
397  // TODO shouldn't we also test for (transferSize & 0x3F)?
398  unsigned endAddress = address + transferSize;
399  if (endAddress <= 0x8000) {
400  transferSectorsBackwards(address, transferSize);
401  } else {
402  transferSize = 0x8000 - address;
403  transferSectors(address, transferSize);
404  }
405  }
406 
407  // wait for 2 bytes
408  state = STATE_DISKREAD;
409  recvCount = 0;
410 }
411 
412 void NowindHost::doDiskRead2()
413 {
414  // diskrom sends back the last two bytes read
415  assert(recvCount == 2);
416  byte tail1 = extraData[0];
417  byte tail2 = extraData[1];
418  if ((tail1 == 0xAF) && (tail2 == 0x07)) {
419  transferred += transferSize;
420  retryCount = 0;
421 
422  unsigned address = getCurrentAddress();
423  size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
424  if ((address == 0x8000) && (bytesLeft > 0)) {
425  sendHeader();
426  send(0x01); // end of receive-loop
427  send(0xff); // more data for page 2/3
428  }
429 
430  // continue the rest of the disk read
431  doDiskRead1();
432  } else {
433  purge();
434  if (++retryCount == 10) {
435  // do nothing, timeout on MSX
436  // too many retries, aborting readDisk()
437  state = STATE_SYNC1;
438  return;
439  }
440 
441  // try again, wait for two bytes
442  state = STATE_DISKREAD;
443  recvCount = 0;
444  }
445 }
446 
447 // sends "02" + "transfer_addr" + "amount" + "data" + "0F 07"
448 void NowindHost::transferSectors(unsigned transferAddress, unsigned amount)
449 {
450  sendHeader();
451  send(0x00); // don't exit command, (more) data is coming
452  send16(transferAddress);
453  send16(amount);
454 
455  auto* bufferPointer = buffer[0].raw + transferred;
456  for (auto i : xrange(amount)) {
457  send(bufferPointer[i]);
458  }
459  send(0xAF);
460  send(0x07); // used for validation
461 }
462 
463 // sends "02" + "transfer_addr" + "amount" + "data" + "0F 07"
464 void NowindHost::transferSectorsBackwards(unsigned transferAddress, unsigned amount)
465 {
466  sendHeader();
467  send(0x02); // don't exit command, (more) data is coming
468  send16(transferAddress + amount);
469  send(amount / 64);
470 
471  auto* bufferPointer = buffer[0].raw + transferred;
472  for (int i = amount - 1; i >= 0; --i) {
473  send(bufferPointer[i]);
474  }
475  send(0xAF);
476  send(0x07); // used for validation
477 }
478 
479 
480 void NowindHost::diskWriteInit(SectorAccessibleDisk& disk)
481 {
482  if (disk.isWriteProtected()) {
483  sendHeader();
484  send(1);
485  send(0); // WRITEPROTECTED
486  state = STATE_SYNC1;
487  return;
488  }
489 
490  unsigned sectorAmount = std::min(128u, getSectorAmount());
491  buffer.resize(sectorAmount);
492  transferred = 0;
493  doDiskWrite1();
494 }
495 
496 void NowindHost::doDiskWrite1()
497 {
498  unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
499  if (bytesLeft == 0) {
500  // All data transferred!
501  auto sectorAmount = unsigned(buffer.size());
502  unsigned startSector = getStartSector();
503  if (auto* disk = getDisk()) {
504  try {
505  disk->writeSectors(&buffer[0], startSector, sectorAmount);
506  } catch (MSXException&) {
507  // TODO write error
508  }
509  }
510  sendHeader();
511  send(255);
512  state = STATE_SYNC1;
513  return;
514  }
515 
516  constexpr unsigned BLOCKSIZE = 240;
517  transferSize = std::min(bytesLeft, BLOCKSIZE);
518 
519  unsigned address = getCurrentAddress();
520  unsigned endAddress = address + transferSize;
521  if ((address ^ endAddress) & 0x8000) {
522  // would cross page 1-2 boundary -> limit to page 1
523  transferSize = 0x8000 - address;
524  }
525 
526  sendHeader();
527  send(0); // data ahead!
528  send16(address);
529  send16(transferSize);
530  send(0xaa);
531 
532  // wait for data
533  state = STATE_DISKWRITE;
534  recvCount = 0;
535 }
536 
537 void NowindHost::doDiskWrite2()
538 {
539  assert(recvCount == (transferSize + 2));
540  auto* buf = buffer[0].raw + transferred;
541  for (auto i : xrange(transferSize)) {
542  buf[i] = extraData[i + 1];
543  }
544 
545  byte seq1 = extraData[0];
546  byte seq2 = extraData[transferSize + 1];
547  if ((seq1 == 0xaa) && (seq2 == 0xaa)) {
548  // good block received
549  transferred += transferSize;
550 
551  unsigned address = getCurrentAddress();
552  size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
553  if ((address == 0x8000) && (bytesLeft > 0)) {
554  sendHeader();
555  send(254); // more data for page 2/3
556  }
557  } else {
558  // ERROR!!!
559  // This situation is still not handled correctly!
560  purge();
561  }
562 
563  // continue the rest of the disk write
564  doDiskWrite1();
565 }
566 
567 
568 unsigned NowindHost::getFCB() const
569 {
570  // note: same code as getStartAddress(), merge???
571  byte reg_l = cmdData[4];
572  byte reg_h = cmdData[5];
573  return reg_h * 256 + reg_l;
574 }
575 
576 string NowindHost::extractName(int begin, int end) const
577 {
578  string result;
579  for (auto i : xrange(begin, end)) {
580  char c = extraData[i];
581  if (c == ' ') break;
582  result += char(toupper(c));
583  }
584  return result;
585 }
586 
587 int NowindHost::getDeviceNum() const
588 {
589  unsigned fcb = getFCB();
590  for (auto [i, dev] : enumerate(devices)) {
591  if (dev.fs && dev.fcb == fcb) {
592  return int(i);
593  }
594  }
595  return -1;
596 }
597 
598 int NowindHost::getFreeDeviceNum()
599 {
600  if (int dev = getDeviceNum(); dev != -1) {
601  // There already was a device open with this fcb address,
602  // reuse that device.
603  return dev;
604  }
605  // Search for free device.
606  for (auto [i, dev] : enumerate(devices)) {
607  if (!dev.fs) return int(i);
608  }
609  // All devices are in use. This can't happen when the MSX software
610  // functions correctly. We'll simply reuse the first device. It would
611  // be nicer if we reuse the oldest device, but that's harder to
612  // implement, and actually it doesn't really matter.
613  return 0;
614 }
615 
616 void NowindHost::deviceOpen()
617 {
618  state = STATE_SYNC1;
619 
620  assert(recvCount == 11);
621  string filename = extractName(0, 8);
622  string ext = extractName(8, 11);
623  if (!ext.empty()) {
624  strAppend(filename, '.', ext);
625  }
626 
627  unsigned fcb = getFCB();
628  unsigned dev = getFreeDeviceNum();
629  devices[dev].fs = std::make_unique<fstream>(); // takes care of deleting old fs
630  devices[dev].fcb = fcb;
631 
632  sendHeader();
633  byte errorCode = 0;
634  byte openMode = cmdData[2]; // reg_e
635  switch (openMode) {
636  case 1: // read-only mode
637  devices[dev].fs->open(filename.c_str(), ios::in | ios::binary);
638  errorCode = 53; // file not found
639  break;
640  case 2: // create new file, write-only
641  devices[dev].fs->open(filename.c_str(), ios::out | ios::binary);
642  errorCode = 56; // bad file name
643  break;
644  case 8: // append to existing file, write-only
645  devices[dev].fs->open(filename.c_str(), ios::out | ios::binary | ios::app);
646  errorCode = 53; // file not found
647  break;
648  case 4:
649  send(58); // sequential I/O only
650  return;
651  default:
652  send(0xFF); // TODO figure out a good error number
653  return;
654  }
655  assert(errorCode != 0);
656  if (devices[dev].fs->fail()) {
657  devices[dev].fs.reset();
658  send(errorCode);
659  return;
660  }
661 
662  unsigned readLen = 0;
663  bool eof = false;
664  char buf[256];
665  if (openMode == 1) {
666  // read-only mode, already buffer first 256 bytes
667  readLen = readHelper1(dev, buf);
668  assert(readLen <= 256);
669  eof = readLen < 256;
670  }
671 
672  send(0x00); // no error
673  send16(fcb);
674  send16(9 + readLen + (eof ? 1 : 0)); // number of bytes to transfer
675 
676  send(openMode);
677  send(0);
678  send(0);
679  send(0);
680  send(cmdData[3]); // reg_d
681  send(0);
682  send(0);
683  send(0);
684  send(0);
685 
686  if (openMode == 1) {
687  readHelper2(readLen, buf);
688  }
689 }
690 
691 void NowindHost::deviceClose()
692 {
693  int dev = getDeviceNum();
694  if (dev == -1) return;
695  devices[dev].fs.reset();
696 }
697 
698 void NowindHost::deviceWrite()
699 {
700  int dev = getDeviceNum();
701  if (dev == -1) return;
702  char data = cmdData[0]; // reg_c
703  devices[dev].fs->write(&data, 1);
704 }
705 
706 void NowindHost::deviceRead()
707 {
708  int dev = getDeviceNum();
709  if (dev == -1) return;
710 
711  char buf[256];
712  unsigned readLen = readHelper1(dev, buf);
713  bool eof = readLen < 256;
714  send(0xAF);
715  send(0x05);
716  send(0x00); // dummy
717  send16(getFCB() + 9);
718  send16(readLen + (eof ? 1 : 0));
719  readHelper2(readLen, buf);
720 }
721 
722 unsigned NowindHost::readHelper1(unsigned dev, char* buf)
723 {
724  assert(dev < MAX_DEVICES);
725  unsigned len = 0;
726  for (; len < 256; ++len) {
727  devices[dev].fs->read(&buf[len], 1);
728  if (devices[dev].fs->eof()) break;
729  }
730  return len;
731 }
732 
733 void NowindHost::readHelper2(unsigned len, const char* buf)
734 {
735  for (auto i : xrange(len)) {
736  send(buf[i]);
737  }
738  if (len < 256) {
739  send(0x1A); // end-of-file
740  }
741 }
742 
743 
744 // strips a string from outer double-quotes and anything outside them
745 // ie: 'pre("foo")bar' will result in 'foo'
746 static constexpr std::string_view stripquotes(std::string_view str)
747 {
748  auto first = str.find_first_of('\"');
749  if (first == string::npos) {
750  // There are no quotes, return the whole string.
751  return str;
752  }
753  auto last = str.find_last_of ('\"');
754  if (first == last) {
755  // Error, there's only a single double-quote char.
756  return {};
757  }
758  // Return the part between the quotes.
759  return str.substr(first + 1, last - first - 1);
760 }
761 
762 void NowindHost::callImage(const string& filename)
763 {
764  byte num = cmdData[7]; // reg_a
765  if (num >= drives.size()) {
766  // invalid drive number
767  return;
768  }
769  if (drives[num]->insertDisk(FileOperations::expandTilde(string(stripquotes(filename))))) {
770  // TODO error handling
771  }
772 }
773 
774 
775 static constexpr std::initializer_list<enum_string<NowindHost::State>> stateInfo = {
776  { "SYNC1", NowindHost::STATE_SYNC1 },
777  { "SYNC2", NowindHost::STATE_SYNC2 },
778  { "COMMAND", NowindHost::STATE_COMMAND },
779  { "DISKREAD", NowindHost::STATE_DISKREAD },
780  { "DISKWRITE", NowindHost::STATE_DISKWRITE },
781  { "DEVOPEN", NowindHost::STATE_DEVOPEN },
782  { "IMAGE", NowindHost::STATE_IMAGE },
783 };
785 
786 template<typename Archive>
787 void NowindHost::serialize(Archive& ar, unsigned /*version*/)
788 {
789  // drives is serialized elsewhere
790 
791  ar.serialize("hostToMsxFifo", hostToMsxFifo,
792  "lastTime", lastTime,
793  "state", state,
794  "recvCount", recvCount,
795  "cmdData", cmdData,
796  "extraData", extraData);
797 
798  // for backwards compatibility, serialize buffer as a vector<byte>
799  size_t bufSize = buffer.size() * sizeof(SectorBuffer);
800  byte* bufRaw = buffer.data()->raw;
801  vector<byte> tmp(bufRaw, bufRaw + bufSize);
802  ar.serialize("buffer", tmp);
803  memcpy(bufRaw, tmp.data(), bufSize);
804 
805  ar.serialize("transfered", transferred, // for bw compat, keep typo in serialize name
806  "retryCount", retryCount,
807  "transferSize", transferSize,
808  "romdisk", romdisk,
809  "allowOtherDiskroms", allowOtherDiskroms,
810  "enablePhantomDrives", enablePhantomDrives);
811 
812  // Note: We don't serialize 'devices'. So after a loadstate it will be
813  // as-if the devices are closed again. The reason for not serializing
814  // this is that it's very hard to serialize a fstream (and we anyway
815  // can't restore the state of the host filesystem).
816 }
818 
819 } // namspace openmsx
bool empty() const
void push_back(U &&u)
const T & front() const
Definition: one_of.hh:7
std::unique_ptr< std::fstream > fs
Definition: NowindHost.hh:112
byte peek() const
Definition: NowindHost.cc:55
NowindHost(const Drives &drives)
Definition: NowindHost.cc:43
void serialize(Archive &ar, unsigned version)
Definition: NowindHost.cc:787
void write(byte data, unsigned time)
Definition: NowindHost.cc:73
std::vector< std::unique_ptr< DiskContainer > > Drives
Definition: NowindHost.hh:20
bool getAllowOtherDiskroms() const
Definition: NowindHost.hh:40
bool isDataAvailable() const
Definition: NowindHost.cc:66
bool getEnablePhantomDrives() const
Definition: NowindHost.hh:43
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition: enumerate.hh:28
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
string expandTilde(string path)
Expand the '~' character to the users home directory.
This file implemented 3 utility functions:
Definition: Autofire.cc:5
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
constexpr unsigned SECTOR_SIZE
Definition: DirAsDSK.cc:23
constexpr const char *const filename
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:669
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:155
constexpr auto begin(const zstring_view &x)
Definition: zstring_view.hh:82
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:83