28 constexpr
unsigned SECTOR_SIZE =
sizeof(SectorBuffer);
30 static void DBERR(
const char* message, ...)
35 va_start(args, message);
37 vprintf(message, args);
48 , allowOtherDiskroms(false)
49 , enablePhantomDrives(true)
68 return !hostToMsxFifo.
empty();
75 unsigned duration = time - lastTime;
77 if (duration >= 500) {
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(0,
':') ||
127 (++recvCount == 40)) {
128 char* eData =
reinterpret_cast<char*
>(extraData);
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",
reinterpret_cast<char*
>(extraData));
147 void NowindHost::msxReset()
149 for (
auto& dev : devices) {
152 DBERR(
"MSX reset\n");
155 SectorAccessibleDisk* NowindHost::getDisk()
const
157 byte num = cmdData[7];
158 if (num >= drives.size()) {
161 return drives[num]->getSectorAccessibleDisk();
164 void NowindHost::executeCommand()
166 assert(recvCount == 9);
167 byte cmd = cmdData[8];
195 auto* disk = getDisk();
202 byte reg_f = cmdData[6];
204 diskWriteInit(*disk);
217 case 0x87: setDateMSX(); state =
STATE_SYNC1;
break;
219 case 0x89: deviceClose(); state =
STATE_SYNC1;
break;
221 case 0x8B: deviceWrite(); state =
STATE_SYNC1;
break;
222 case 0x8C: deviceRead(); state =
STATE_SYNC1;
break;
227 case 0xA0: state =
STATE_IMAGE; recvCount = 0;
break;
237 void NowindHost::send(
byte value)
241 void NowindHost::send16(
word value)
247 void NowindHost::purge()
249 hostToMsxFifo.
clear();
252 void NowindHost::sendHeader()
259 void 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(§orBuffer, 1, 1);
276 }
catch (MSXException&) {
278 sectorBuffer.raw[0] = 0;
280 send(sectorBuffer.raw[0]);
288 void 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()) {
308 void NowindHost::INIENV()
314 void NowindHost::setDateMSX()
316 auto td = time(
nullptr);
317 auto* tm = localtime(&td);
321 send(tm->tm_mon + 1);
322 send16(tm->tm_year + 1900);
326 unsigned NowindHost::getSectorAmount()
const
328 byte reg_b = cmdData[1];
331 unsigned 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;
343 unsigned NowindHost::getStartAddress()
const
345 byte reg_l = cmdData[4];
346 byte reg_h = cmdData[5];
347 return reg_h * 256 + reg_l;
349 unsigned NowindHost::getCurrentAddress()
const
351 unsigned startAddress = getStartAddress();
352 return startAddress + transferred;
356 void NowindHost::diskReadInit(SectorAccessibleDisk& disk)
358 unsigned sectorAmount = getSectorAmount();
359 buffer.resize(sectorAmount);
360 unsigned startSector = getStartSector();
362 disk.readSectors(buffer.data(), startSector, sectorAmount);
363 }
catch (MSXException&) {
374 void NowindHost::doDiskRead1()
376 unsigned bytesLeft = unsigned(buffer.size() *
SECTOR_SIZE) - transferred;
377 if (bytesLeft == 0) {
385 constexpr
unsigned NUMBEROFBLOCKS = 32;
386 transferSize =
std::min(bytesLeft, NUMBEROFBLOCKS * 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);
412 void 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) {
448 void NowindHost::transferSectors(
unsigned transferAddress,
unsigned amount)
452 send16(transferAddress);
455 auto* bufferPointer = buffer[0].raw + transferred;
456 for (
auto i :
xrange(amount)) {
457 send(bufferPointer[i]);
464 void NowindHost::transferSectorsBackwards(
unsigned transferAddress,
unsigned amount)
468 send16(transferAddress + amount);
471 auto* bufferPointer = buffer[0].raw + transferred;
472 for (
int i = amount - 1; i >= 0; --i) {
473 send(bufferPointer[i]);
480 void NowindHost::diskWriteInit(SectorAccessibleDisk& disk)
482 if (disk.isWriteProtected()) {
490 unsigned sectorAmount =
std::min(128u, getSectorAmount());
491 buffer.resize(sectorAmount);
496 void 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(&buffer[0], startSector, sectorAmount);
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;
529 send16(transferSize);
537 void NowindHost::doDiskWrite2()
539 assert(recvCount == (transferSize + 2));
540 auto* buf = buffer[0].raw + transferred;
541 for (
auto i :
xrange(transferSize)) {
542 buf[i] = extraData[i + 1];
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)) {
568 unsigned NowindHost::getFCB()
const
571 byte reg_l = cmdData[4];
572 byte reg_h = cmdData[5];
573 return reg_h * 256 + reg_l;
576 string NowindHost::extractName(
int begin,
int end)
const
580 char c = extraData[i];
582 result += char(toupper(c));
587 int NowindHost::getDeviceNum()
const
589 unsigned fcb = getFCB();
590 for (
auto [i, dev] :
enumerate(devices)) {
591 if (dev.fs && dev.fcb ==
fcb) {
598 int NowindHost::getFreeDeviceNum()
600 if (
int dev = getDeviceNum(); dev != -1) {
606 for (
auto [i, dev] :
enumerate(devices)) {
607 if (!dev.fs)
return int(i);
616 void NowindHost::deviceOpen()
620 assert(recvCount == 11);
621 string filename = extractName(0, 8);
622 string ext = extractName(8, 11);
627 unsigned fcb = getFCB();
628 unsigned dev = getFreeDeviceNum();
629 devices[dev].fs = std::make_unique<fstream>();
630 devices[dev].fcb =
fcb;
634 byte openMode = cmdData[2];
637 devices[dev].fs->open(
filename.c_str(), ios::in | ios::binary);
641 devices[dev].fs->open(
filename.c_str(), ios::out | ios::binary);
645 devices[dev].fs->open(
filename.c_str(), ios::out | ios::binary | ios::app);
655 assert(errorCode != 0);
656 if (devices[dev].
fs->fail()) {
657 devices[dev].fs.reset();
662 unsigned readLen = 0;
667 readLen = readHelper1(dev, buf);
668 assert(readLen <= 256);
674 send16(9 + readLen + (eof ? 1 : 0));
687 readHelper2(readLen, buf);
691 void NowindHost::deviceClose()
693 int dev = getDeviceNum();
694 if (dev == -1)
return;
695 devices[dev].fs.reset();
698 void NowindHost::deviceWrite()
700 int dev = getDeviceNum();
701 if (dev == -1)
return;
702 char data = cmdData[0];
703 devices[dev].fs->write(&data, 1);
706 void NowindHost::deviceRead()
708 int dev = getDeviceNum();
709 if (dev == -1)
return;
712 unsigned readLen = readHelper1(dev, buf);
713 bool eof = readLen < 256;
717 send16(getFCB() + 9);
718 send16(readLen + (eof ? 1 : 0));
719 readHelper2(readLen, buf);
722 unsigned NowindHost::readHelper1(
unsigned dev,
char* 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;
733 void NowindHost::readHelper2(
unsigned len,
const char* buf)
735 for (
auto i :
xrange(len)) {
746 static constexpr std::string_view stripquotes(std::string_view str)
748 auto first = str.find_first_of(
'\"');
749 if (first == string::npos) {
753 auto last = str.find_last_of (
'\"');
759 return str.substr(first + 1, last - first - 1);
762 void NowindHost::callImage(
const string&
filename)
764 byte num = cmdData[7];
765 if (num >= drives.size()) {
775 static constexpr std::initializer_list<enum_string<NowindHost::State>> stateInfo = {
786 template<
typename Archive>
791 ar.serialize(
"hostToMsxFifo", hostToMsxFifo,
792 "lastTime", lastTime,
794 "recvCount", recvCount,
796 "extraData", extraData);
800 byte* bufRaw = buffer.data()->raw;
801 vector<byte> tmp(bufRaw, bufRaw + bufSize);
802 ar.serialize(
"buffer", tmp);
803 memcpy(bufRaw, tmp.data(), bufSize);
805 ar.serialize(
"transfered", transferred,
806 "retryCount", retryCount,
807 "transferSize", transferSize,
809 "allowOtherDiskroms", allowOtherDiskroms,
810 "enablePhantomDrives", enablePhantomDrives);
std::unique_ptr< std::fstream > fs
NowindHost(const Drives &drives)
void serialize(Archive &ar, unsigned version)
void write(byte data, unsigned time)
std::vector< std::unique_ptr< DiskContainer > > Drives
bool getAllowOtherDiskroms() const
bool isDataAvailable() 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)
constexpr unsigned SECTOR_SIZE
constexpr const char *const filename
uint16_t word
16 bit unsigned integer
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
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)