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