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