28static constexpr unsigned SECTOR_SIZE =
sizeof(SectorBuffer);
30static void DBERR(
const char* message, ...)
35 va_start(args, message);
37 vprintf(message, args);
63 return !hostToMsxFifo.
empty();
70 unsigned duration = time - lastTime;
72 if (duration >= 500) {
91 assert(recvCount < 9);
92 cmdData[recvCount] = data;
93 if (++recvCount == 9) {
98 assert(recvCount < 2);
99 extraData[recvCount] = data;
100 if (++recvCount == 2) {
105 assert(recvCount < (transferSize + 2));
106 extraData[recvCount] = data;
107 if (++recvCount == (transferSize + 2)) {
112 assert(recvCount < 11);
113 extraData[recvCount] = data;
114 if (++recvCount == 11) {
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));
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()));
142void NowindHost::msxReset()
144 for (
auto& dev : devices) {
147 DBERR(
"MSX reset\n");
150SectorAccessibleDisk* NowindHost::getDisk()
const
152 byte num = cmdData[7];
153 if (num >= drives.size()) {
156 return drives[num]->getSectorAccessibleDisk();
159void NowindHost::executeCommand()
161 assert(recvCount == 9);
162 byte cmd = cmdData[8];
190 auto* disk = getDisk();
197 byte reg_f = cmdData[6];
199 diskWriteInit(*disk);
212 case 0x87: setDateMSX(); state =
STATE_SYNC1;
break;
214 case 0x89: deviceClose(); state =
STATE_SYNC1;
break;
216 case 0x8B: deviceWrite(); state =
STATE_SYNC1;
break;
217 case 0x8C: deviceRead(); state =
STATE_SYNC1;
break;
222 case 0xA0: state =
STATE_IMAGE; recvCount = 0;
break;
232void NowindHost::send(
byte value)
236void NowindHost::send16(
word value)
238 hostToMsxFifo.
push_back(narrow_cast<byte>(value & 255));
239 hostToMsxFifo.
push_back(narrow_cast<byte>(value >> 8));
242void NowindHost::purge()
244 hostToMsxFifo.
clear();
247void NowindHost::sendHeader()
254void NowindHost::DSKCHG()
256 auto* disk = getDisk();
263 byte num = cmdData[7];
264 assert(num < drives.size());
265 if (drives[num]->diskChanged()) {
268 SectorBuffer sectorBuffer;
270 disk->readSectors(std::span{§orBuffer, 1}, 1);
271 }
catch (MSXException&) {
273 sectorBuffer.raw[0] = 0;
275 send(sectorBuffer.raw[0]);
283void NowindHost::DRIVES()
286 byte numberOfDrives = std::max<byte>(1,
byte(drives.size()));
288 byte reg_a = cmdData[7];
292 send(numberOfDrives);
295 for (
auto [i, drv] :
enumerate(drives)) {
296 if (drv->isRomDisk()) {
303void NowindHost::INIENV()
309void NowindHost::setDateMSX()
311 auto td = time(
nullptr);
312 auto* tm = localtime(&td);
315 send(narrow_cast<uint8_t>(tm->tm_mday));
316 send(narrow_cast<uint8_t>(tm->tm_mon + 1));
317 send16(narrow_cast<uint16_t>(tm->tm_year + 1900));
321unsigned NowindHost::getSectorAmount()
const
323 byte reg_b = cmdData[1];
326unsigned NowindHost::getStartSector()
const
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);
334 startSector += reg_c << 16;
338unsigned NowindHost::getStartAddress()
const
340 byte reg_l = cmdData[4];
341 byte reg_h = cmdData[5];
342 return reg_h * 256 + reg_l;
344unsigned NowindHost::getCurrentAddress()
const
346 unsigned startAddress = getStartAddress();
347 return startAddress + transferred;
351void NowindHost::diskReadInit(SectorAccessibleDisk& disk)
353 unsigned sectorAmount = getSectorAmount();
354 buffer.resize(sectorAmount);
355 unsigned startSector = getStartSector();
357 disk.readSectors(std::span{buffer.data(), sectorAmount}, startSector);
358 }
catch (MSXException&) {
369void NowindHost::doDiskRead1()
371 unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
372 if (bytesLeft == 0) {
380 constexpr unsigned NUMBER_OF_BLOCKS = 32;
381 transferSize =
std::min(bytesLeft, NUMBER_OF_BLOCKS * 64);
383 unsigned address = getCurrentAddress();
384 if (address >= 0x8000) {
385 if (transferSize & 0x003F) {
386 transferSectors(address, transferSize);
388 transferSectorsBackwards(address, transferSize);
393 unsigned endAddress = address + transferSize;
394 if (endAddress <= 0x8000) {
395 transferSectorsBackwards(address, transferSize);
397 transferSize = 0x8000 - address;
398 transferSectors(address, transferSize);
407void NowindHost::doDiskRead2()
410 assert(recvCount == 2);
411 byte tail1 = extraData[0];
412 byte tail2 = extraData[1];
413 if ((tail1 == 0xAF) && (tail2 == 0x07)) {
414 transferred += transferSize;
417 unsigned address = getCurrentAddress();
418 size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
419 if ((address == 0x8000) && (bytesLeft > 0)) {
429 if (++retryCount == 10) {
443void NowindHost::transferSectors(
unsigned transferAddress,
unsigned amount)
447 send16(narrow_cast<uint16_t>(transferAddress));
448 send16(narrow_cast<uint16_t>(amount));
450 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
451 auto buf = fullBuf.subspan(transferred, amount);
460void NowindHost::transferSectorsBackwards(
unsigned transferAddress,
unsigned amount)
464 send16(narrow_cast<uint16_t>(transferAddress + amount));
465 send(narrow_cast<uint8_t>(amount / 64));
467 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
468 auto buf = fullBuf.subspan(transferred, amount);
477void NowindHost::diskWriteInit(SectorAccessibleDisk& disk)
479 if (disk.isWriteProtected()) {
487 unsigned sectorAmount =
std::min(128u, getSectorAmount());
488 buffer.resize(sectorAmount);
493void NowindHost::doDiskWrite1()
495 unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
496 if (bytesLeft == 0) {
498 auto sectorAmount = unsigned(buffer.size());
499 unsigned startSector = getStartSector();
500 if (
auto* disk = getDisk()) {
502 disk->writeSectors(std::span{buffer.data(), sectorAmount}, startSector);
503 }
catch (MSXException&) {
513 constexpr unsigned BLOCKSIZE = 240;
514 transferSize =
std::min(bytesLeft, BLOCKSIZE);
516 unsigned address = getCurrentAddress();
517 unsigned endAddress = address + transferSize;
518 if ((address ^ endAddress) & 0x8000) {
520 transferSize = 0x8000 - address;
525 send16(narrow_cast<uint16_t>(address));
526 send16(narrow_cast<uint16_t>(transferSize));
534void NowindHost::doDiskWrite2()
536 assert(recvCount == (transferSize + 2));
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);
543 byte seq1 = extraData[0];
544 byte seq2 = extraData[transferSize + 1];
545 if ((seq1 == 0xaa) && (seq2 == 0xaa)) {
547 transferred += transferSize;
549 unsigned address = getCurrentAddress();
550 size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
551 if ((address == 0x8000) && (bytesLeft > 0)) {
566word NowindHost::getFCB()
const
569 byte reg_l = cmdData[4];
570 byte reg_h = cmdData[5];
571 return word(reg_h * 256 + reg_l);
574string NowindHost::extractName(
int begin,
int end)
const
578 auto c = narrow_cast<char>(extraData[i]);
580 result += char(toupper(c));
585int NowindHost::getDeviceNum()
const
588 for (
auto [i, dev] :
enumerate(devices)) {
589 if (dev.fs && dev.fcb == fcb) {
596int NowindHost::getFreeDeviceNum()
598 if (
int dev = getDeviceNum(); dev != -1) {
604 for (
auto [i, dev] :
enumerate(devices)) {
605 if (!dev.fs)
return int(i);
614void NowindHost::deviceOpen()
618 assert(recvCount == 11);
619 string filename = extractName(0, 8);
620 string ext = extractName(8, 11);
626 unsigned dev = getFreeDeviceNum();
627 devices[dev].fs.emplace();
628 devices[dev].fcb = fcb;
632 byte openMode = cmdData[2];
635 devices[dev].fs->open(filename.c_str(), std::ios::in | std::ios::binary);
639 devices[dev].fs->open(filename.c_str(), std::ios::out | std::ios::binary);
643 devices[dev].fs->open(filename.c_str(), std::ios::out | std::ios::binary | std::ios::app);
653 assert(errorCode != 0);
654 if (devices[dev].fs->fail()) {
655 devices[dev].fs.reset();
660 unsigned readLen = 0;
662 std::array<char, 256> buf;
665 readLen = readHelper1(dev, buf);
666 assert(readLen <= 256);
672 send16(narrow_cast<uint16_t>(9 + readLen + (eof ? 1 : 0)));
685 readHelper2(
subspan(buf, 0, readLen));
689void NowindHost::deviceClose()
691 int dev = getDeviceNum();
692 if (dev == -1)
return;
693 devices[dev].fs.reset();
696void NowindHost::deviceWrite()
698 int dev = getDeviceNum();
699 if (dev == -1)
return;
700 auto data = narrow_cast<char>(cmdData[0]);
701 devices[dev].fs->write(&data, 1);
704void NowindHost::deviceRead()
706 int dev = getDeviceNum();
707 if (dev == -1)
return;
709 std::array<char, 256> buf;
710 unsigned readLen = readHelper1(dev, buf);
711 bool eof = readLen < 256;
715 send16(narrow_cast<uint16_t>(getFCB() + 9));
716 send16(narrow_cast<uint16_t>(readLen + (eof ? 1 : 0)));
717 readHelper2(
subspan(buf, 0, readLen));
720unsigned NowindHost::readHelper1(
unsigned dev, std::span<char, 256> buf)
722 assert(dev < MAX_DEVICES);
724 for (; len < 256; ++len) {
725 devices[dev].fs->read(&buf[len], 1);
726 if (devices[dev].fs->eof())
break;
731void NowindHost::readHelper2(std::span<const char> buf)
736 if (buf.size() < 256) {
744static constexpr std::string_view stripQuotes(std::string_view str)
746 auto first = str.find_first_of(
'\"');
747 if (first == string::npos) {
751 auto last = str.find_last_of (
'\"');
757 return str.substr(first + 1, last - first - 1);
760void NowindHost::callImage(
const string& filename)
762 byte num = cmdData[7];
763 if (num >= drives.size()) {
773static constexpr std::initializer_list<enum_string<NowindHost::State>> stateInfo = {
784template<
typename Archive>
789 ar.serialize(
"hostToMsxFifo", hostToMsxFifo,
790 "lastTime", lastTime,
792 "recvCount", recvCount,
794 "extraData", extraData);
798 std::span<uint8_t> buf{buffer.data()->raw.data(), bufSize};
800 ar.serialize(
"buffer", tmp);
803 ar.serialize(
"transfered", transferred,
804 "retryCount", retryCount,
805 "transferSize", transferSize,
807 "allowOtherDiskroms", allowOtherDiskRoms,
808 "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....
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
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
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
uint16_t word
16 bit unsigned integer
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)
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)