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 "narrow.hh"
8#include "one_of.hh"
9#include "serialize.hh"
10#include "serialize_stl.hh"
11#include "unreachable.hh"
12#include "view.hh"
13#include "xrange.hh"
14#include <algorithm>
15#include <array>
16#include <cassert>
17#include <cctype>
18#include <cstdio>
19#include <cstdarg>
20#include <ctime>
21#include <fstream>
22#include <memory>
23
24using std::string;
25
26namespace openmsx {
27
28static constexpr unsigned SECTOR_SIZE = sizeof(SectorBuffer);
29
30static 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{
46}
47
48NowindHost::~NowindHost() = default;
49
50byte NowindHost::peek() const
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
68void 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 == one_of(byte(0), byte(':')) ||
122 (++recvCount == 40)) {
123 char* eData = reinterpret_cast<char*>(extraData.data());
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.data()));
134 state = STATE_SYNC1;
135 }
136 break;
137 default:
139 }
140}
141
142void NowindHost::msxReset()
143{
144 for (auto& dev : devices) {
145 dev.fs.reset();
146 }
147 DBERR("MSX reset\n");
148}
149
150SectorAccessibleDisk* 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
159void 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
232void NowindHost::send(byte value)
233{
234 hostToMsxFifo.push_back(value);
235}
236void NowindHost::send16(word value)
237{
238 hostToMsxFifo.push_back(narrow_cast<byte>(value & 255));
239 hostToMsxFifo.push_back(narrow_cast<byte>(value >> 8));
240}
241
242void NowindHost::purge()
243{
244 hostToMsxFifo.clear();
245}
246
247void NowindHost::sendHeader()
248{
249 send(0xFF); // needed because first read might fail!
250 send(0xAF);
251 send(0x05);
252}
253
254void 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 try {
270 disk->readSectors(std::span{&sectorBuffer, 1}, 1);
271 } catch (MSXException&) {
272 // TODO read error
273 sectorBuffer.raw[0] = 0;
274 }
275 send(sectorBuffer.raw[0]); // new media descriptor
276 } else {
277 send(0); // not changed
278 // TODO shouldn't we send some (dummy) byte here?
279 // nowind-disk-rom seems to read it (but doesn't use it)
280 }
281}
282
283void NowindHost::DRIVES()
284{
285 // at least one drive (MSX-DOS1 cannot handle 0 drives)
286 byte numberOfDrives = std::max<byte>(1, byte(drives.size()));
287
288 byte reg_a = cmdData[7];
289 sendHeader();
290 send(getEnablePhantomDrives() ? 0x02 : 0);
291 send(reg_a | (getAllowOtherDiskRoms() ? 0 : 0x80));
292 send(numberOfDrives);
293
294 romDisk = 255; // no rom disk
295 for (auto [i, drv] : enumerate(drives)) {
296 if (drv->isRomDisk()) {
297 romDisk = byte(i);
298 break;
299 }
300 }
301}
302
303void NowindHost::INIENV()
304{
305 sendHeader();
306 send(romDisk); // calculated in DRIVES()
307}
308
309void NowindHost::setDateMSX()
310{
311 auto td = time(nullptr);
312 auto* tm = localtime(&td);
313
314 sendHeader();
315 send(narrow_cast<uint8_t>(tm->tm_mday)); // day
316 send(narrow_cast<uint8_t>(tm->tm_mon + 1)); // month
317 send16(narrow_cast<uint16_t>(tm->tm_year + 1900)); // year
318}
319
320
321unsigned NowindHost::getSectorAmount() const
322{
323 byte reg_b = cmdData[1];
324 return reg_b;
325}
326unsigned NowindHost::getStartSector() const
327{
328 byte reg_c = cmdData[0];
329 byte reg_e = cmdData[2];
330 byte reg_d = cmdData[3];
331 unsigned startSector = reg_e + (reg_d * 256);
332 if (reg_c < 0x80) {
333 // FAT16 read/write sector
334 startSector += reg_c << 16;
335 }
336 return startSector;
337}
338unsigned NowindHost::getStartAddress() const
339{
340 byte reg_l = cmdData[4];
341 byte reg_h = cmdData[5];
342 return reg_h * 256 + reg_l;
343}
344unsigned NowindHost::getCurrentAddress() const
345{
346 unsigned startAddress = getStartAddress();
347 return startAddress + transferred;
348}
349
350
351void NowindHost::diskReadInit(SectorAccessibleDisk& disk)
352{
353 unsigned sectorAmount = getSectorAmount();
354 buffer.resize(sectorAmount);
355 unsigned startSector = getStartSector();
356 try {
357 disk.readSectors(std::span{buffer.data(), sectorAmount}, startSector);
358 } catch (MSXException&) {
359 // read error
360 state = STATE_SYNC1;
361 return;
362 }
363
364 transferred = 0;
365 retryCount = 0;
366 doDiskRead1();
367}
368
369void NowindHost::doDiskRead1()
370{
371 unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
372 if (bytesLeft == 0) {
373 sendHeader();
374 send(0x01); // end of receive-loop
375 send(0x00); // no more data
376 state = STATE_SYNC1;
377 return;
378 }
379
380 constexpr unsigned NUMBER_OF_BLOCKS = 32; // 32 * 64 bytes = 2048 bytes
381 transferSize = std::min(bytesLeft, NUMBER_OF_BLOCKS * 64); // hardcoded in firmware
382
383 unsigned address = getCurrentAddress();
384 if (address >= 0x8000) {
385 if (transferSize & 0x003F) {
386 transferSectors(address, transferSize);
387 } else {
388 transferSectorsBackwards(address, transferSize);
389 }
390 } else {
391 // transfer below 0x8000
392 // TODO shouldn't we also test for (transferSize & 0x3F)?
393 unsigned endAddress = address + transferSize;
394 if (endAddress <= 0x8000) {
395 transferSectorsBackwards(address, transferSize);
396 } else {
397 transferSize = 0x8000 - address;
398 transferSectors(address, transferSize);
399 }
400 }
401
402 // wait for 2 bytes
403 state = STATE_DISKREAD;
404 recvCount = 0;
405}
406
407void NowindHost::doDiskRead2()
408{
409 // disk rom sends back the last two bytes read
410 assert(recvCount == 2);
411 byte tail1 = extraData[0];
412 byte tail2 = extraData[1];
413 if ((tail1 == 0xAF) && (tail2 == 0x07)) {
414 transferred += transferSize;
415 retryCount = 0;
416
417 unsigned address = getCurrentAddress();
418 size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
419 if ((address == 0x8000) && (bytesLeft > 0)) {
420 sendHeader();
421 send(0x01); // end of receive-loop
422 send(0xff); // more data for page 2/3
423 }
424
425 // continue the rest of the disk read
426 doDiskRead1();
427 } else {
428 purge();
429 if (++retryCount == 10) {
430 // do nothing, timeout on MSX
431 // too many retries, aborting readDisk()
432 state = STATE_SYNC1;
433 return;
434 }
435
436 // try again, wait for two bytes
437 state = STATE_DISKREAD;
438 recvCount = 0;
439 }
440}
441
442// sends "02" + "transfer_addr" + "amount" + "data" + "0F 07"
443void NowindHost::transferSectors(unsigned transferAddress, unsigned amount)
444{
445 sendHeader();
446 send(0x00); // don't exit command, (more) data is coming
447 send16(narrow_cast<uint16_t>(transferAddress));
448 send16(narrow_cast<uint16_t>(amount));
449
450 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
451 auto buf = fullBuf.subspan(transferred, amount);
452 for (auto b : buf) {
453 send(b);
454 }
455 send(0xAF);
456 send(0x07); // used for validation
457}
458
459// sends "02" + "transfer_addr" + "amount" + "data" + "0F 07"
460void NowindHost::transferSectorsBackwards(unsigned transferAddress, unsigned amount)
461{
462 sendHeader();
463 send(0x02); // don't exit command, (more) data is coming
464 send16(narrow_cast<uint16_t>(transferAddress + amount));
465 send(narrow_cast<uint8_t>(amount / 64));
466
467 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
468 auto buf = fullBuf.subspan(transferred, amount);
469 for (auto b : view::reverse(buf)) {
470 send(b);
471 }
472 send(0xAF);
473 send(0x07); // used for validation
474}
475
476
477void NowindHost::diskWriteInit(SectorAccessibleDisk& disk)
478{
479 if (disk.isWriteProtected()) {
480 sendHeader();
481 send(1);
482 send(0); // WRITE PROTECTED
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
493void 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(std::span{buffer.data(), sectorAmount}, startSector);
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(narrow_cast<uint16_t>(address));
526 send16(narrow_cast<uint16_t>(transferSize));
527 send(0xaa);
528
529 // wait for data
530 state = STATE_DISKWRITE;
531 recvCount = 0;
532}
533
534void NowindHost::doDiskWrite2()
535{
536 assert(recvCount == (transferSize + 2));
537 //auto* buf = &buffer[0].raw[transferred];
538 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
539 auto dst = fullBuf.subspan(transferred, transferSize);
540 auto src = subspan(extraData, 1, transferSize);
541 ranges::copy(src, dst);
542
543 byte seq1 = extraData[0];
544 byte seq2 = extraData[transferSize + 1];
545 if ((seq1 == 0xaa) && (seq2 == 0xaa)) {
546 // good block received
547 transferred += transferSize;
548
549 unsigned address = getCurrentAddress();
550 size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
551 if ((address == 0x8000) && (bytesLeft > 0)) {
552 sendHeader();
553 send(254); // more data for page 2/3
554 }
555 } else {
556 // ERROR!!!
557 // This situation is still not handled correctly!
558 purge();
559 }
560
561 // continue the rest of the disk write
562 doDiskWrite1();
563}
564
565
566word NowindHost::getFCB() const
567{
568 // note: same code as getStartAddress(), merge???
569 byte reg_l = cmdData[4];
570 byte reg_h = cmdData[5];
571 return word(reg_h * 256 + reg_l);
572}
573
574string NowindHost::extractName(int begin, int end) const
575{
576 string result;
577 for (auto i : xrange(begin, end)) {
578 auto c = narrow_cast<char>(extraData[i]);
579 if (c == ' ') break;
580 result += char(toupper(c));
581 }
582 return result;
583}
584
585int NowindHost::getDeviceNum() const
586{
587 auto fcb = getFCB();
588 for (auto [i, dev] : enumerate(devices)) {
589 if (dev.fs && dev.fcb == fcb) {
590 return int(i);
591 }
592 }
593 return -1;
594}
595
596int NowindHost::getFreeDeviceNum()
597{
598 if (int dev = getDeviceNum(); dev != -1) {
599 // There already was a device open with this fcb address,
600 // reuse that device.
601 return dev;
602 }
603 // Search for free device.
604 for (auto [i, dev] : enumerate(devices)) {
605 if (!dev.fs) return int(i);
606 }
607 // All devices are in use. This can't happen when the MSX software
608 // functions correctly. We'll simply reuse the first device. It would
609 // be nicer if we reuse the oldest device, but that's harder to
610 // implement, and actually it doesn't really matter.
611 return 0;
612}
613
614void NowindHost::deviceOpen()
615{
616 state = STATE_SYNC1;
617
618 assert(recvCount == 11);
619 string filename = extractName(0, 8);
620 string ext = extractName(8, 11);
621 if (!ext.empty()) {
622 strAppend(filename, '.', ext);
623 }
624
625 auto fcb = getFCB();
626 unsigned dev = getFreeDeviceNum();
627 devices[dev].fs.emplace(); // takes care of deleting old fs
628 devices[dev].fcb = fcb;
629
630 sendHeader();
631 byte errorCode = 0;
632 byte openMode = cmdData[2]; // reg_e
633 switch (openMode) {
634 case 1: // read-only mode
635 devices[dev].fs->open(filename.c_str(), std::ios::in | std::ios::binary);
636 errorCode = 53; // file not found
637 break;
638 case 2: // create new file, write-only
639 devices[dev].fs->open(filename.c_str(), std::ios::out | std::ios::binary);
640 errorCode = 56; // bad file name
641 break;
642 case 8: // append to existing file, write-only
643 devices[dev].fs->open(filename.c_str(), std::ios::out | std::ios::binary | std::ios::app);
644 errorCode = 53; // file not found
645 break;
646 case 4:
647 send(58); // sequential I/O only
648 return;
649 default:
650 send(0xFF); // TODO figure out a good error number
651 return;
652 }
653 assert(errorCode != 0);
654 if (devices[dev].fs->fail()) {
655 devices[dev].fs.reset();
656 send(errorCode);
657 return;
658 }
659
660 unsigned readLen = 0;
661 bool eof = false;
662 std::array<char, 256> buf;
663 if (openMode == 1) {
664 // read-only mode, already buffer first 256 bytes
665 readLen = readHelper1(dev, buf);
666 assert(readLen <= 256);
667 eof = readLen < 256;
668 }
669
670 send(0x00); // no error
671 send16(fcb);
672 send16(narrow_cast<uint16_t>(9 + readLen + (eof ? 1 : 0))); // number of bytes to transfer
673
674 send(openMode);
675 send(0);
676 send(0);
677 send(0);
678 send(cmdData[3]); // reg_d
679 send(0);
680 send(0);
681 send(0);
682 send(0);
683
684 if (openMode == 1) {
685 readHelper2(subspan(buf, 0, readLen));
686 }
687}
688
689void NowindHost::deviceClose()
690{
691 int dev = getDeviceNum();
692 if (dev == -1) return;
693 devices[dev].fs.reset();
694}
695
696void NowindHost::deviceWrite()
697{
698 int dev = getDeviceNum();
699 if (dev == -1) return;
700 auto data = narrow_cast<char>(cmdData[0]); // reg_c
701 devices[dev].fs->write(&data, 1);
702}
703
704void NowindHost::deviceRead()
705{
706 int dev = getDeviceNum();
707 if (dev == -1) return;
708
709 std::array<char, 256> buf;
710 unsigned readLen = readHelper1(dev, buf);
711 bool eof = readLen < 256;
712 send(0xAF);
713 send(0x05);
714 send(0x00); // dummy
715 send16(narrow_cast<uint16_t>(getFCB() + 9));
716 send16(narrow_cast<uint16_t>(readLen + (eof ? 1 : 0)));
717 readHelper2(subspan(buf, 0, readLen));
718}
719
720unsigned NowindHost::readHelper1(unsigned dev, std::span<char, 256> buf)
721{
722 assert(dev < MAX_DEVICES);
723 unsigned len = 0;
724 for (; len < 256; ++len) {
725 devices[dev].fs->read(&buf[len], 1);
726 if (devices[dev].fs->eof()) break;
727 }
728 return len;
729}
730
731void NowindHost::readHelper2(std::span<const char> buf)
732{
733 for (auto c : buf) {
734 send(c);
735 }
736 if (buf.size() < 256) {
737 send(0x1A); // end-of-file
738 }
739}
740
741
742// strips a string from outer double-quotes and anything outside them
743// ie: 'pre("foo")bar' will result in 'foo'
744static constexpr std::string_view stripQuotes(std::string_view str)
745{
746 auto first = str.find_first_of('\"');
747 if (first == string::npos) {
748 // There are no quotes, return the whole string.
749 return str;
750 }
751 auto last = str.find_last_of ('\"');
752 if (first == last) {
753 // Error, there's only a single double-quote char.
754 return {};
755 }
756 // Return the part between the quotes.
757 return str.substr(first + 1, last - first - 1);
758}
759
760void NowindHost::callImage(const string& filename)
761{
762 byte num = cmdData[7]; // reg_a
763 if (num >= drives.size()) {
764 // invalid drive number
765 return;
766 }
767 if (drives[num]->insertDisk(FileOperations::expandTilde(string(stripQuotes(filename))))) {
768 // TODO error handling
769 }
770}
771
772
773static constexpr std::initializer_list<enum_string<NowindHost::State>> stateInfo = {
774 { "SYNC1", NowindHost::STATE_SYNC1 },
775 { "SYNC2", NowindHost::STATE_SYNC2 },
776 { "COMMAND", NowindHost::STATE_COMMAND },
777 { "DISKREAD", NowindHost::STATE_DISKREAD },
778 { "DISKWRITE", NowindHost::STATE_DISKWRITE },
779 { "DEVOPEN", NowindHost::STATE_DEVOPEN },
780 { "IMAGE", NowindHost::STATE_IMAGE },
781};
783
784template<typename Archive>
785void NowindHost::serialize(Archive& ar, unsigned /*version*/)
786{
787 // drives is serialized elsewhere
788
789 ar.serialize("hostToMsxFifo", hostToMsxFifo,
790 "lastTime", lastTime,
791 "state", state,
792 "recvCount", recvCount,
793 "cmdData", cmdData,
794 "extraData", extraData);
795
796 // for backwards compatibility, serialize buffer as a vector<byte>
797 size_t bufSize = buffer.size() * sizeof(SectorBuffer);
798 std::span<uint8_t> buf{buffer.data()->raw.data(), bufSize};
799 auto tmp = to_vector(buf);
800 ar.serialize("buffer", tmp);
801 ranges::copy(tmp, buf);
802
803 ar.serialize("transfered", transferred, // for bw compat, keep typo in serialize name
804 "retryCount", retryCount,
805 "transferSize", transferSize,
806 "romdisk", romDisk,
807 "allowOtherDiskroms", allowOtherDiskRoms,
808 "enablePhantomDrives", enablePhantomDrives);
809
810 // Note: We don't serialize 'devices'. So after a loadstate it will be
811 // as-if the devices are closed again. The reason for not serializing
812 // this is that it's very hard to serialize a fstream (and we anyway
813 // can't restore the state of the host filesystem).
814}
816
817} // namespace openmsx
bool empty() const
void push_back(U &&u)
const T & front() const
byte peek() const
Definition NowindHost.cc:50
NowindHost(const Drives &drives)
Definition NowindHost.cc:43
void serialize(Archive &ar, unsigned version)
void write(byte data, unsigned time)
Definition NowindHost.cc:68
std::vector< std::unique_ptr< DiskContainer > > Drives
Definition NowindHost.hh:23
bool isDataAvailable() const
Definition NowindHost.cc:61
bool getAllowOtherDiskRoms() const
Definition NowindHost.hh:43
bool getEnablePhantomDrives() const
Definition NowindHost.hh:46
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
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
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
auto copy(InputRange &&range, OutputIter out)
Definition ranges.hh:250
Definition view.hh:15
constexpr auto reverse(Range &&range)
Definition view.hh:514
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition ranges.hh:471
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
Definition stl.hh:275
void strAppend(std::string &result, Ts &&...ts)
Definition strCat.hh:752
#define UNREACHABLE
constexpr auto xrange(T e)
Definition xrange.hh:132
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)