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