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