32static constexpr unsigned SECTOR_SIZE =
sizeof(SectorBuffer);
34static void DBERR(
const char* message, ...)
39 va_start(args, message);
41 vprintf(message, args);
67 return !hostToMsxFifo.
empty();
75 unsigned duration = time - lastTime;
77 if (duration >= 500) {
85 if (data == 0xAF) state =
SYNC2;
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;
96 assert(recvCount < 9);
97 cmdData[recvCount] = data;
98 if (++recvCount == 9) {
103 assert(recvCount < 2);
104 extraData[recvCount] = data;
105 if (++recvCount == 2) {
110 assert(recvCount < (transferSize + 2));
111 extraData[recvCount] = data;
112 if (++recvCount == (transferSize + 2)) {
117 assert(recvCount < 11);
118 extraData[recvCount] = data;
119 if (++recvCount == 11) {
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));
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()));
147void NowindHost::msxReset()
149 for (
auto& dev : devices) {
152 DBERR(
"MSX reset\n");
155SectorAccessibleDisk* NowindHost::getDisk()
const
157 byte num = cmdData[7];
158 if (num >= drives.size()) {
161 return drives[num]->getSectorAccessibleDisk();
164void NowindHost::executeCommand()
167 assert(recvCount == 9);
168 byte cmd = cmdData[8];
196 auto* disk = getDisk();
203 if (
byte reg_f = cmdData[6]; reg_f & 1) {
204 diskWriteInit(*disk);
211 case 0x81: DSKCHG(); state =
SYNC1;
break;
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;
221 case 0x8B: deviceWrite(); state =
SYNC1;
break;
222 case 0x8C: deviceRead(); state =
SYNC1;
break;
226 case 0x90: state =
MESSAGE; recvCount = 0;
break;
227 case 0xA0: state =
IMAGE; recvCount = 0;
break;
237void NowindHost::send(
byte value)
241void NowindHost::send16(
word value)
243 hostToMsxFifo.
push_back(narrow_cast<byte>(value & 255));
244 hostToMsxFifo.
push_back(narrow_cast<byte>(value >> 8));
247void NowindHost::purge()
249 hostToMsxFifo.
clear();
252void NowindHost::sendHeader()
259void NowindHost::DSKCHG()
261 auto* disk = getDisk();
268 byte num = cmdData[7];
269 assert(num < drives.size());
270 if (drives[num]->diskChanged()) {
273 SectorBuffer sectorBuffer;
275 disk->readSectors(std::span{§orBuffer, 1}, 1);
276 }
catch (MSXException&) {
278 sectorBuffer.raw[0] = 0;
280 send(sectorBuffer.raw[0]);
288void NowindHost::DRIVES()
291 byte numberOfDrives = std::max<byte>(1,
byte(drives.size()));
293 byte reg_a = cmdData[7];
297 send(numberOfDrives);
300 for (
auto [i, drv] :
enumerate(drives)) {
301 if (drv->isRomDisk()) {
308void NowindHost::INIENV()
314void NowindHost::setDateMSX()
316 auto td = time(
nullptr);
317 auto* tm = localtime(&td);
320 send(narrow_cast<uint8_t>(tm->tm_mday));
321 send(narrow_cast<uint8_t>(tm->tm_mon + 1));
322 send16(narrow_cast<uint16_t>(tm->tm_year + 1900));
326unsigned NowindHost::getSectorAmount()
const
328 byte reg_b = cmdData[1];
331unsigned NowindHost::getStartSector()
const
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);
339 startSector += reg_c << 16;
343unsigned NowindHost::getStartAddress()
const
345 byte reg_l = cmdData[4];
346 byte reg_h = cmdData[5];
347 return reg_h * 256 + reg_l;
349unsigned NowindHost::getCurrentAddress()
const
351 unsigned startAddress = getStartAddress();
352 return startAddress + transferred;
356void NowindHost::diskReadInit(
const SectorAccessibleDisk& disk)
358 unsigned sectorAmount = getSectorAmount();
359 buffer.resize(sectorAmount);
360 unsigned startSector = getStartSector();
362 disk.readSectors(std::span{buffer.data(), sectorAmount}, startSector);
363 }
catch (MSXException&) {
374void NowindHost::doDiskRead1()
376 unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
377 if (bytesLeft == 0) {
385 constexpr unsigned NUMBER_OF_BLOCKS = 32;
386 transferSize = std::min(bytesLeft, NUMBER_OF_BLOCKS * 64);
388 unsigned address = getCurrentAddress();
389 if (address >= 0x8000) {
390 if (transferSize & 0x003F) {
391 transferSectors(address, transferSize);
393 transferSectorsBackwards(address, transferSize);
398 unsigned endAddress = address + transferSize;
399 if (endAddress <= 0x8000) {
400 transferSectorsBackwards(address, transferSize);
402 transferSize = 0x8000 - address;
403 transferSectors(address, transferSize);
412void NowindHost::doDiskRead2()
415 assert(recvCount == 2);
416 byte tail1 = extraData[0];
417 byte tail2 = extraData[1];
418 if ((tail1 == 0xAF) && (tail2 == 0x07)) {
419 transferred += transferSize;
422 unsigned address = getCurrentAddress();
423 size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
424 if ((address == 0x8000) && (bytesLeft > 0)) {
434 if (++retryCount == 10) {
448void NowindHost::transferSectors(
unsigned transferAddress,
unsigned amount)
452 send16(narrow_cast<uint16_t>(transferAddress));
453 send16(narrow_cast<uint16_t>(amount));
455 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
456 for (
auto buf = fullBuf.subspan(transferred, amount);
465void NowindHost::transferSectorsBackwards(
unsigned transferAddress,
unsigned amount)
469 send16(narrow_cast<uint16_t>(transferAddress + amount));
470 send(narrow_cast<uint8_t>(amount / 64));
472 std::span fullBuf{buffer[0].raw.data(), buffer.size() * SECTOR_SIZE};
473 for (
auto buf = fullBuf.subspan(transferred, amount);
482void NowindHost::diskWriteInit(
const SectorAccessibleDisk& disk)
484 if (disk.isWriteProtected()) {
492 unsigned sectorAmount = std::min(128u, getSectorAmount());
493 buffer.resize(sectorAmount);
498void NowindHost::doDiskWrite1()
500 unsigned bytesLeft = unsigned(buffer.size() * SECTOR_SIZE) - transferred;
501 if (bytesLeft == 0) {
503 auto sectorAmount = unsigned(buffer.size());
504 unsigned startSector = getStartSector();
505 if (
auto* disk = getDisk()) {
507 disk->writeSectors(std::span{buffer.data(), sectorAmount}, startSector);
508 }
catch (MSXException&) {
518 constexpr unsigned BLOCKSIZE = 240;
519 transferSize = std::min(bytesLeft, BLOCKSIZE);
521 unsigned address = getCurrentAddress();
522 unsigned endAddress = address + transferSize;
523 if ((address ^ endAddress) & 0x8000) {
525 transferSize = 0x8000 - address;
530 send16(narrow_cast<uint16_t>(address));
531 send16(narrow_cast<uint16_t>(transferSize));
539void NowindHost::doDiskWrite2()
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);
547 byte seq1 = extraData[0];
548 byte seq2 = extraData[transferSize + 1];
549 if ((seq1 == 0xaa) && (seq2 == 0xaa)) {
551 transferred += transferSize;
553 unsigned address = getCurrentAddress();
554 size_t bytesLeft = (buffer.size() * SECTOR_SIZE) - transferred;
555 if ((address == 0x8000) && (bytesLeft > 0)) {
570word NowindHost::getFCB()
const
573 byte reg_l = cmdData[4];
574 byte reg_h = cmdData[5];
575 return word(reg_h * 256 + reg_l);
578string NowindHost::extractName(
int begin,
int end)
const
582 auto c = narrow_cast<char>(extraData[i]);
584 result += char(toupper(c));
589int NowindHost::getDeviceNum()
const
592 for (
auto [i, dev] :
enumerate(devices)) {
593 if (dev.fs && dev.fcb == fcb) {
600int NowindHost::getFreeDeviceNum()
602 if (
int dev = getDeviceNum(); dev != -1) {
608 for (
auto [i, dev] :
enumerate(devices)) {
609 if (!dev.fs)
return int(i);
618void NowindHost::deviceOpen()
622 assert(recvCount == 11);
623 string filename = extractName(0, 8);
624 string ext = extractName(8, 11);
630 unsigned dev = getFreeDeviceNum();
631 devices[dev].fs.emplace();
632 devices[dev].fcb = fcb;
636 byte openMode = cmdData[2];
639 devices[dev].fs->open(filename.c_str(), std::ios::in | std::ios::binary);
643 devices[dev].fs->open(filename.c_str(), std::ios::out | std::ios::binary);
647 devices[dev].fs->open(filename.c_str(), std::ios::out | std::ios::binary | std::ios::app);
657 assert(errorCode != 0);
658 if (devices[dev].fs->fail()) {
659 devices[dev].fs.reset();
664 unsigned readLen = 0;
666 std::array<char, 256> buf;
669 readLen = readHelper1(dev, buf);
670 assert(readLen <= 256);
676 send16(narrow_cast<uint16_t>(9 + readLen + (eof ? 1 : 0)));
689 readHelper2(
subspan(buf, 0, readLen));
693void NowindHost::deviceClose()
695 int dev = getDeviceNum();
696 if (dev == -1)
return;
697 devices[dev].fs.reset();
700void NowindHost::deviceWrite()
702 int dev = getDeviceNum();
703 if (dev == -1)
return;
704 auto data = narrow_cast<char>(cmdData[0]);
705 devices[dev].fs->write(&data, 1);
708void NowindHost::deviceRead()
710 int dev = getDeviceNum();
711 if (dev == -1)
return;
713 std::array<char, 256> buf;
714 unsigned readLen = readHelper1(dev, buf);
715 bool eof = readLen < 256;
719 send16(narrow_cast<uint16_t>(getFCB() + 9));
720 send16(narrow_cast<uint16_t>(readLen + (eof ? 1 : 0)));
721 readHelper2(
subspan(buf, 0, readLen));
724unsigned NowindHost::readHelper1(
unsigned dev, std::span<char, 256> buf)
726 assert(dev < MAX_DEVICES);
728 for (; len < 256; ++len) {
729 devices[dev].fs->read(&buf[len], 1);
730 if (devices[dev].fs->eof())
break;
735void NowindHost::readHelper2(std::span<const char> buf)
740 if (buf.size() < 256) {
748static constexpr std::string_view stripQuotes(std::string_view str)
750 auto first = str.find_first_of(
'\"');
751 if (first == string::npos) {
755 auto last = str.find_last_of (
'\"');
761 return str.substr(first + 1, last - first - 1);
764void NowindHost::callImage(
const string& filename)
766 byte num = cmdData[7];
767 if (num >= drives.size()) {
777static constexpr std::initializer_list<enum_string<NowindHost::State>> stateInfo = {
788template<
typename Archive>
793 ar.serialize(
"hostToMsxFifo", hostToMsxFifo,
794 "lastTime", lastTime,
796 "recvCount", recvCount,
798 "extraData", extraData);
802 std::span<uint8_t> buf{buffer.data()->raw.data(), bufSize};
804 ar.serialize(
"buffer", tmp);
807 ar.serialize(
"transfered", transferred,
808 "retryCount", retryCount,
809 "transferSize", transferSize,
811 "allowOtherDiskroms", allowOtherDiskRoms,
812 "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
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)