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