30static constexpr unsigned SECTOR_SIZE =
sizeof(SectorBuffer);
32static void DBERR(
const char* message, ...)
37 va_start(args, message);
39 vprintf(message, args);
65 return !hostToMsxFifo.
empty();
73 unsigned duration = time - lastTime;
75 if (duration >= 500) {
83 if (data == 0xAF) state =
SYNC2;
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;
94 assert(recvCount < 9);
95 cmdData[recvCount] = data;
96 if (++recvCount == 9) {
101 assert(recvCount < 2);
102 extraData[recvCount] = data;
103 if (++recvCount == 2) {
108 assert(recvCount < (transferSize + 2));
109 extraData[recvCount] = data;
110 if (++recvCount == (transferSize + 2)) {
115 assert(recvCount < 11);
116 extraData[recvCount] = data;
117 if (++recvCount == 11) {
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));
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()));
145void NowindHost::msxReset()
147 for (
auto& dev : devices) {
150 DBERR(
"MSX reset\n");
153SectorAccessibleDisk* NowindHost::getDisk()
const
155 byte num = cmdData[7];
156 if (num >= drives.size()) {
159 return drives[num]->getSectorAccessibleDisk();
162void NowindHost::executeCommand()
165 assert(recvCount == 9);
166 byte cmd = cmdData[8];
194 auto* disk = getDisk();
201 if (
byte reg_f = cmdData[6]; reg_f & 1) {
202 diskWriteInit(*disk);
209 case 0x81: DSKCHG(); state =
SYNC1;
break;
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;
219 case 0x8B: deviceWrite(); state =
SYNC1;
break;
220 case 0x8C: deviceRead(); state =
SYNC1;
break;
224 case 0x90: state =
MESSAGE; recvCount = 0;
break;
225 case 0xA0: state =
IMAGE; recvCount = 0;
break;
235void NowindHost::send(
byte value)
239void NowindHost::send16(
word value)
241 hostToMsxFifo.
push_back(narrow_cast<byte>(value & 255));
242 hostToMsxFifo.
push_back(narrow_cast<byte>(value >> 8));
245void NowindHost::purge()
247 hostToMsxFifo.
clear();
250void NowindHost::sendHeader()
257void NowindHost::DSKCHG()
259 auto* disk = getDisk();
266 byte num = cmdData[7];
267 assert(num < drives.size());
268 if (drives[num]->diskChanged()) {
271 SectorBuffer sectorBuffer;
273 disk->readSectors(std::span{§orBuffer, 1}, 1);
274 }
catch (MSXException&) {
276 sectorBuffer.raw[0] = 0;
278 send(sectorBuffer.raw[0]);
286void NowindHost::DRIVES()
289 byte numberOfDrives = std::max<byte>(1,
byte(drives.size()));
291 byte reg_a = cmdData[7];
295 send(numberOfDrives);
298 for (
auto [i, drv] :
enumerate(drives)) {
299 if (drv->isRomDisk()) {
306void NowindHost::INIENV()
312void NowindHost::setDateMSX()
314 auto td = time(
nullptr);
315 auto* tm = localtime(&td);
318 send(narrow_cast<uint8_t>(tm->tm_mday));
319 send(narrow_cast<uint8_t>(tm->tm_mon + 1));
320 send16(narrow_cast<uint16_t>(tm->tm_year + 1900));
324unsigned NowindHost::getSectorAmount()
const
326 byte reg_b = cmdData[1];
329unsigned NowindHost::getStartSector()
const
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);
337 startSector += reg_c << 16;
341unsigned NowindHost::getStartAddress()
const
343 byte reg_l = cmdData[4];
344 byte reg_h = cmdData[5];
345 return reg_h * 256 + reg_l;
347unsigned NowindHost::getCurrentAddress()
const
349 unsigned startAddress = getStartAddress();
350 return startAddress + transferred;
354void NowindHost::diskReadInit(
const SectorAccessibleDisk& disk)
356 unsigned sectorAmount = getSectorAmount();
357 buffer.resize(sectorAmount);
358 unsigned startSector = getStartSector();
360 disk.readSectors(std::span{buffer.data(), sectorAmount}, startSector);
361 }
catch (MSXException&) {
372void NowindHost::doDiskRead1()
374 unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
375 if (bytesLeft == 0) {
383 constexpr unsigned NUMBER_OF_BLOCKS = 32;
384 transferSize = std::min(bytesLeft, NUMBER_OF_BLOCKS * 64);
386 unsigned address = getCurrentAddress();
387 if (address >= 0x8000) {
388 if (transferSize & 0x003F) {
389 transferSectors(address, transferSize);
391 transferSectorsBackwards(address, transferSize);
396 unsigned endAddress = address + transferSize;
397 if (endAddress <= 0x8000) {
398 transferSectorsBackwards(address, transferSize);
400 transferSize = 0x8000 - address;
401 transferSectors(address, transferSize);
410void NowindHost::doDiskRead2()
413 assert(recvCount == 2);
414 byte tail1 = extraData[0];
415 byte tail2 = extraData[1];
416 if ((tail1 == 0xAF) && (tail2 == 0x07)) {
417 transferred += transferSize;
420 unsigned address = getCurrentAddress();
421 size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
422 if ((address == 0x8000) && (bytesLeft > 0)) {
432 if (++retryCount == 10) {
446void NowindHost::transferSectors(
unsigned transferAddress,
unsigned amount)
450 send16(narrow_cast<uint16_t>(transferAddress));
451 send16(narrow_cast<uint16_t>(amount));
453 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
454 for (
auto buf = fullBuf.subspan(transferred, amount);
463void NowindHost::transferSectorsBackwards(
unsigned transferAddress,
unsigned amount)
467 send16(narrow_cast<uint16_t>(transferAddress + amount));
468 send(narrow_cast<uint8_t>(amount / 64));
470 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
471 for (
auto buf = fullBuf.subspan(transferred, amount);
480void NowindHost::diskWriteInit(
const SectorAccessibleDisk& disk)
482 if (disk.isWriteProtected()) {
490 unsigned sectorAmount = std::min(128u, getSectorAmount());
491 buffer.resize(sectorAmount);
496void NowindHost::doDiskWrite1()
498 unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
499 if (bytesLeft == 0) {
501 auto sectorAmount = unsigned(buffer.size());
502 unsigned startSector = getStartSector();
503 if (
auto* disk = getDisk()) {
505 disk->writeSectors(std::span{buffer.data(), sectorAmount}, startSector);
506 }
catch (MSXException&) {
516 constexpr unsigned BLOCKSIZE = 240;
517 transferSize = std::min(bytesLeft, BLOCKSIZE);
519 unsigned address = getCurrentAddress();
520 unsigned endAddress = address + transferSize;
521 if ((address ^ endAddress) & 0x8000) {
523 transferSize = 0x8000 - address;
528 send16(narrow_cast<uint16_t>(address));
529 send16(narrow_cast<uint16_t>(transferSize));
537void NowindHost::doDiskWrite2()
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);
545 byte seq1 = extraData[0];
546 byte seq2 = extraData[transferSize + 1];
547 if ((seq1 == 0xaa) && (seq2 == 0xaa)) {
549 transferred += transferSize;
551 unsigned address = getCurrentAddress();
552 size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
553 if ((address == 0x8000) && (bytesLeft > 0)) {
568word NowindHost::getFCB()
const
571 byte reg_l = cmdData[4];
572 byte reg_h = cmdData[5];
573 return word(reg_h * 256 + reg_l);
576std::string NowindHost::extractName(
int begin,
int end)
const
580 auto c = narrow_cast<char>(extraData[i]);
582 result += char(toupper(c));
587int NowindHost::getDeviceNum()
const
590 for (
auto [i, dev] :
enumerate(devices)) {
591 if (dev.fs && dev.fcb == fcb) {
598int NowindHost::getFreeDeviceNum()
600 if (
int dev = getDeviceNum(); dev != -1) {
606 for (
auto [i, dev] :
enumerate(devices)) {
607 if (!dev.fs)
return int(i);
616void NowindHost::deviceOpen()
620 assert(recvCount == 11);
621 std::string filename = extractName(0, 8);
622 std::string ext = extractName(8, 11);
628 unsigned dev = getFreeDeviceNum();
629 devices[dev].fs.emplace();
630 devices[dev].fcb = fcb;
634 byte openMode = cmdData[2];
637 devices[dev].fs->open(filename.c_str(), std::ios::in | std::ios::binary);
641 devices[dev].fs->open(filename.c_str(), std::ios::out | std::ios::binary);
645 devices[dev].fs->open(filename.c_str(), std::ios::out | std::ios::binary | std::ios::app);
655 assert(errorCode != 0);
656 if (devices[dev].fs->fail()) {
657 devices[dev].fs.reset();
662 unsigned readLen = 0;
664 std::array<char, 256> buf;
667 readLen = readHelper1(dev, buf);
668 assert(readLen <= 256);
674 send16(narrow_cast<uint16_t>(9 + readLen + (eof ? 1 : 0)));
687 readHelper2(
subspan(buf, 0, readLen));
691void NowindHost::deviceClose()
693 int dev = getDeviceNum();
694 if (dev == -1)
return;
695 devices[dev].fs.reset();
698void NowindHost::deviceWrite()
700 int dev = getDeviceNum();
701 if (dev == -1)
return;
702 auto data = narrow_cast<char>(cmdData[0]);
703 devices[dev].fs->write(&data, 1);
706void NowindHost::deviceRead()
708 int dev = getDeviceNum();
709 if (dev == -1)
return;
711 std::array<char, 256> buf;
712 unsigned readLen = readHelper1(dev, buf);
713 bool eof = readLen < 256;
717 send16(narrow_cast<uint16_t>(getFCB() + 9));
718 send16(narrow_cast<uint16_t>(readLen + (eof ? 1 : 0)));
719 readHelper2(
subspan(buf, 0, readLen));
722unsigned NowindHost::readHelper1(
unsigned dev, std::span<char, 256> buf)
724 assert(dev < MAX_DEVICES);
726 for (; len < 256; ++len) {
727 devices[dev].fs->read(&buf[len], 1);
728 if (devices[dev].fs->eof())
break;
733void NowindHost::readHelper2(std::span<const char> buf)
738 if (buf.size() < 256) {
746static constexpr std::string_view stripQuotes(std::string_view str)
748 auto first = str.find_first_of(
'\"');
749 if (first == std::string::npos) {
753 auto last = str.find_last_of (
'\"');
759 return str.substr(first + 1, last - first - 1);
762void NowindHost::callImage(
const std::string& filename)
764 byte num = cmdData[7];
765 if (num >= drives.size()) {
775static constexpr std::initializer_list<enum_string<NowindHost::State>> stateInfo = {
786template<
typename Archive>
791 ar.serialize(
"hostToMsxFifo", hostToMsxFifo,
792 "lastTime", lastTime,
794 "recvCount", recvCount,
796 "extraData", extraData);
800 std::span<uint8_t> buf{buffer.data()->raw.data(), bufSize};
802 ar.serialize(
"buffer", tmp);
805 ar.serialize(
"transfered", transferred,
806 "retryCount", retryCount,
807 "transferSize", transferSize,
809 "allowOtherDiskroms", allowOtherDiskRoms,
810 "enablePhantomDrives", enablePhantomDrives);
NowindHost(const Drives &drives)
void serialize(Archive &ar, unsigned version)
void write(byte data, unsigned time)
std::vector< std::unique_ptr< DiskContainer > > Drives
bool isDataAvailable() const
bool getAllowOtherDiskRoms() const
bool getEnablePhantomDrives() const
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
string expandTilde(string path)
Expand the '~' character to the users home directory.
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
uint16_t word
16 bit unsigned integer
constexpr auto copy(InputRange &&range, OutputIter out)
constexpr auto reverse(Range &&range)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
void strAppend(std::string &result, Ts &&...ts)
constexpr auto xrange(T e)
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)