diff --git a/apple/diskii.cpp b/apple/diskii.cpp index 65aab2b..8e9e595 100644 --- a/apple/diskii.cpp +++ b/apple/diskii.cpp @@ -40,6 +40,10 @@ DiskII::DiskII(AppleMMU *mmu) disk[0] = disk[1] = NULL; diskIsSpinningUntil[0] = diskIsSpinningUntil[1] = 0; selectedDisk = 0; + + driveSpinupCycles = 0; + deliveredDiskBits = 0; + // debugDeliveredDiskBits = 0; } DiskII::~DiskII() @@ -163,6 +167,36 @@ void DiskII::Reset() ejectDisk(1); } +void DiskII::driveOff() +{ + diskIsSpinningUntil[selectedDisk] = g_cpu->cycles + SPINDOWNDELAY; // 1 second lag + if (diskIsSpinningUntil[selectedDisk] == -1 || + diskIsSpinningUntil[selectedDisk] == 0) + diskIsSpinningUntil[selectedDisk] = 2; // fudge magic numbers; 0 is "off" and -1 is "forever". + + // The drive-is-on-indicator is turned off later, when the disk + // actually spins down. +} + +void DiskII::driveOn() +{ + if (diskIsSpinningUntil[selectedDisk] != -1) { + // If the drive isn't already spinning, then start keeping track of how + // many bits we've delivered (so we can honor the disk bit-delivery time + // that might be in the Woz disk image). + driveSpinupCycles = g_cpu->cycles; + //printf("driveOn @ cycle %d\n", driveSpinupCycles); + deliveredDiskBits = 0; + // debugDeliveredDiskBits = 0; + diskIsSpinningUntil[selectedDisk] = -1; // magic "forever" + } + + g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: do we really want to update the UI from inside this thread? + + // Start the given disk drive spinning + lastDiskRead[selectedDisk] = g_cpu->cycles; +} + uint8_t DiskII::readSwitches(uint8_t s) { switch (s) { @@ -188,17 +222,10 @@ uint8_t DiskII::readSwitches(uint8_t s) break; case 0x08: // drive off - diskIsSpinningUntil[selectedDisk] = g_cpu->cycles + SPINDOWNDELAY; // 1 second lag - if (diskIsSpinningUntil[selectedDisk] == -1 || - diskIsSpinningUntil[selectedDisk] == 0) - diskIsSpinningUntil[selectedDisk] = 2; // fudge magic numbers; 0 is "off" and -1 is "forever". + driveOff(); break; case 0x09: // drive on - diskIsSpinningUntil[selectedDisk] = -1; // magic "forever" - g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: delay a bit? Queue for later drawing? *** - - // Start the given disk drive spinning - lastDiskRead[selectedDisk] = g_cpu->cycles; + driveOn(); break; case 0x0A: // select drive 1 @@ -210,8 +237,15 @@ uint8_t DiskII::readSwitches(uint8_t s) case 0x0C: // shift one read or write byte readWriteLatch = readOrWriteByte(); - if (readWriteLatch & 0x80) + if (readWriteLatch & 0x80) { + // static uint32_t lastC = 0; + // printf("%u: read data\n", g_cpu->cycles - lastC); + // lastC = g_cpu->cycles; + if (!(sequencer & 0x80)) { + printf("SEQ RESET EARLY [1]\n"); + } sequencer = 0; + } break; case 0x0D: // load data register (latch) @@ -223,6 +257,10 @@ uint8_t DiskII::readSwitches(uint8_t s) else readWriteLatch &= 0x7F; } + if (!(sequencer & 0x80)) { + printf("SEQ RESET EARLY [2]\n"); + } + sequencer = 0; break; case 0x0E: // set read mode @@ -263,17 +301,10 @@ void DiskII::writeSwitches(uint8_t s, uint8_t v) break; case 0x08: // drive off - diskIsSpinningUntil[selectedDisk] = g_cpu->cycles + SPINDOWNDELAY; // 1 second lag - if (diskIsSpinningUntil[selectedDisk] == -1 || - diskIsSpinningUntil[selectedDisk] == 0) - diskIsSpinningUntil[selectedDisk] = 2; // fudge magic numbers; 0 is "off" and -1 is "forever". + driveOff(); break; case 0x09: // drive on - diskIsSpinningUntil[selectedDisk] = -1; // magic "forever" - g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: delay a bit? Queue for later drawing? *** - - // Start the given disk drive spinning - lastDiskRead[selectedDisk] = g_cpu->cycles; + driveOn(); break; case 0x0A: // select drive 1 @@ -284,8 +315,12 @@ void DiskII::writeSwitches(uint8_t s, uint8_t v) break; case 0x0C: // shift one read or write byte - if (readOrWriteByte() & 0x80) + if (readOrWriteByte() & 0x80) { + if (!(sequencer & 0x80)) { + printf("SEQ RESET EARLY [3]\n"); + } sequencer = 0; + } break; case 0x0D: // drive write @@ -366,8 +401,9 @@ void DiskII::setPhase(uint8_t phase) if (curHalfTrack[selectedDisk] != prevHalfTrack) { // We're changing track - flush the old track back to disk - - curWozTrack[selectedDisk] = disk[selectedDisk]->trackNumberForQuarterTrack(curHalfTrack[selectedDisk]*2); + // FIXME flush + curWozTrack[selectedDisk] = disk[selectedDisk]->dataTrackNumberForQuarterTrack(curHalfTrack[selectedDisk]*2); + printf("track change => %d\n", curWozTrack[selectedDisk]); } } @@ -410,9 +446,9 @@ void DiskII::insertDisk(int8_t driveNum, const char *filename, bool drawIt) ejectDisk(driveNum); disk[driveNum] = new WozSerializer(); - disk[driveNum]->readFile(filename, false, T_AUTO); // FIXME error checking + disk[driveNum]->readFile(filename, true, T_AUTO); // FIXME error checking; also FIXME the true is 'preload all tracks' and that won't work on the teensy - curWozTrack[driveNum] = disk[driveNum]->trackNumberForQuarterTrack(curHalfTrack[driveNum]*2); + curWozTrack[driveNum] = disk[driveNum]->dataTrackNumberForQuarterTrack(curHalfTrack[driveNum]*2); if (drawIt) g_ui->drawOnOffUIElement(UIeDisk1_state + driveNum, false); @@ -444,8 +480,10 @@ void DiskII::select(int8_t which) } // Update the current woz track for the given disk drive - curWozTrack[selectedDisk] = - disk[selectedDisk]->trackNumberForQuarterTrack(curHalfTrack[selectedDisk]*2); + if (disk[selectedDisk]) { + curWozTrack[selectedDisk] = + disk[selectedDisk]->dataTrackNumberForQuarterTrack(curHalfTrack[selectedDisk]*2); + } } uint8_t DiskII::readOrWriteByte() @@ -455,18 +493,24 @@ uint8_t DiskII::readOrWriteByte() return 0xFF; } - // FIXME: not handling writes at all at the moment *** - if (writeMode && !writeProt) { - return 0; - } - uint32_t curCycles = g_cpu->cycles; bool updateCycles = false; + // FIXME: for writes, we need to check s/t like ... if (diskIsSpinningUntil[selectedDisk] >= curCycles) { return } ... + + if (writeMode && !writeProt) { + // It's a write request. Inject 'readWriteLatch'. + disk[selectedDisk]->writeNextWozByte(curWozTrack[selectedDisk], readWriteLatch); + + updateCycles = true; // need to update when we last read, b/c disk is still spinning + goto done; + } + if (diskIsSpinningUntil[selectedDisk] >= curCycles) { if (lastDiskRead[selectedDisk] == 0) { // assume it's a first-read-after-spinup; return the first valid data + printf("FIRST SPIN\n"); sequencer = disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]); updateCycles = true; goto done; @@ -474,16 +518,87 @@ uint8_t DiskII::readOrWriteByte() // Otherwise we figure out how many cycles we missed since the last // disk read, and pop the right number of bits off the woz track - uint32_t missedCycles; - missedCycles = curCycles - lastDiskRead[selectedDisk]; - - missedCycles >>= 2; - if (missedCycles) + // uint32_t missedCycles; + // missedCycles = curCycles - lastDiskRead[selectedDisk]; + + // The stock 4ms disk bit timing is just missedCycles >> 2. But we + // want to support others, too. We can't simply base it on cycle + // count any more at that point, because of fractional cycles + // being important. + // So instead of just "missedCycles >>= 2" here, we need to calculate + // how many *bits* should have been transited at time (x); and we need + // a floating counter of how long the drive has been spinning (b/c + // that's not a constant since startup!); and we need the counter of + // how many bits we actually did pull from the drive. Then we can + // calculate exactly how many bits we should pull this time, update the + // number that did transit, and be more or less where we're supposed + // to be for this clock cycle. + + // Handle rollover, which is a mess. + if (driveSpinupCycles > g_cpu->cycles) { + printf("Cycle rollover\n"); + driveSpinupCycles = g_cpu->cycles-1; +#ifndef TEENSYDUINO + exit(2); // for debugging, FIXME *** +#endif + } + + uint32_t cyclesPassed = g_cpu->cycles - driveSpinupCycles; + // printf("cy: %d cp: %d ", g_cpu->cycles, cyclesPassed); + + // bits = cycles * (us per cycle) * (bits/us) + //#define BITSPEED 4.0 + // uint64_t expectedDiskBits = (float)cyclesPassed * (float)(1.0/(1.023*BITSPEED)); // clock speed*2 b/c the disk clock runs at twice the speed? + // uint64_t expectedDiskBits = (float)cyclesPassed / 8.0; + uint64_t expectedDiskBits = (float) cyclesPassed / 3.52; + int64_t bitsToDeliver = expectedDiskBits - deliveredDiskBits; + + // printf("btd: %llu\n",bitsToDeliver); + // printf("mc>>2: %d; btd: %llu\n", missedCycles >> 2, bitsToDeliver); + //int64_t bitsToDeliver = missedCycles>>2; + // debugDeliveredDiskBits += (missedCycles >> 2); + + if (bitsToDeliver > 0) { + +#if 1 + /* TESTING - try delivering a byte, if there's a simple request, and let it drift forward in time very slightly */ + if (bitsToDeliver < 16) { + + // if (bitsToDeliver >= 8) { sequencer = 0; } + while (bitsToDeliver > -16 && ((sequencer & 0x80) == 0)) { + sequencer <<= 1; + sequencer |= disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]); + bitsToDeliver--; + deliveredDiskBits++; + } + updateCycles = true; + goto done; + } + /* END TESTING */ + + // printf("WARNING: missed data [%lld]\n", bitsToDeliver); +#endif updateCycles = true; - while (missedCycles) { - sequencer <<= 1; - sequencer |= disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]); - missedCycles--; + + // Something is wrong here. I don't know why + // debugDeliveredDiskBits doesn't match bitsToDeliver. In + // theory, debugDDB is just missedCycles/4. deliveredDiskBits + // should be pretty much the same (1.023/4.0, so off by + // 2.3%). But in reality the drift is much greater. + // + // Is it related to the disk on/off timers? How does + // missedCycles differ? I could use missedCycles, except that it + // loses precision when we're talking about using a 3.5us bit + // timing, so that's a problem -- which is why I'm trying to + // base it on "real time" from when the disk drive starts + // spinning... + + deliveredDiskBits += bitsToDeliver; + while (bitsToDeliver) { + sequencer <<= 1; + sequencer |= disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]); + bitsToDeliver--; + } } } @@ -494,14 +609,18 @@ uint8_t DiskII::readOrWriteByte() // cycles indicates that we did some sort of work... lastDiskRead[selectedDisk] = curCycles; } - + return sequencer; } const char *DiskII::DiskName(int8_t num) { - // *** + if (disk[num]) { + // *** need to get name from disk image FIXME + return "[inserted]"; + } + // Nothing inserted in that drive return ""; } @@ -539,6 +658,11 @@ void DiskII::maintenance(uint32_t cycle) // Stop the given disk drive spinning lastDiskRead[i] = 0; // FIXME: magic value. We need a tristate for this. *** diskIsSpinningUntil[i] = 0; + + if (disk[i]) { + disk[i]->flush(); + } + g_ui->drawOnOffUIElement(UIeDisk1_activity + i, false); // FIXME: queue for later drawing? } } diff --git a/apple/diskii.h b/apple/diskii.h index 4dea4b6..efc66d3 100644 --- a/apple/diskii.h +++ b/apple/diskii.h @@ -46,18 +46,26 @@ class DiskII : public Slot { void select(int8_t which); // 0 or 1 for drives 1 and 2, respectively uint8_t readOrWriteByte(); + void driveOn(); + void driveOff(); + #ifndef TEENSYDUINO void convertDskToNib(const char *outFN); #endif + public: + // debugging + WozSerializer *disk[2]; private: volatile int8_t curHalfTrack[2]; volatile uint8_t curWozTrack[2]; volatile int8_t curPhase[2]; uint8_t readWriteLatch; uint8_t sequencer, dataRegister; // diskII logic state sequencer vars - WozSerializer *disk[2]; uint32_t lastDiskRead[2]; + uint64_t driveSpinupCycles; + uint64_t deliveredDiskBits; + uint64_t debugDeliveredDiskBits; bool writeMode; bool writeProt; diff --git a/apple/woz-serializer.cpp b/apple/woz-serializer.cpp index becfa23..19eaa33 100644 --- a/apple/woz-serializer.cpp +++ b/apple/woz-serializer.cpp @@ -1,5 +1,9 @@ #include "woz-serializer.h" +WozSerializer::WozSerializer() : Woz(0,0) +{ +} + bool WozSerializer::Serialize(int8_t fd) { // *** diff --git a/apple/woz-serializer.h b/apple/woz-serializer.h index 379c426..21c85af 100644 --- a/apple/woz-serializer.h +++ b/apple/woz-serializer.h @@ -3,6 +3,9 @@ #include "woz.h" class WozSerializer: public virtual Woz { +public: + WozSerializer(); + public: bool Serialize(int8_t fd); bool Deserialize(int8_t fd); diff --git a/apple/woz.cpp b/apple/woz.cpp index 82d5375..6653a91 100644 --- a/apple/woz.cpp +++ b/apple/woz.cpp @@ -3,39 +3,39 @@ #include "crc32.h" #include "nibutil.h" #include "version.h" -#include "globals.h" // Block number we start packing data bits after (Woz 2.0 images) #define STARTBLOCK 3 -#define PREP_SECTION(f, t) { \ - uint32_t type = t; \ - if (!write32(f, type)) \ - return false; \ - if (!write32(f, 0)) \ - return false; \ - curpos = g_filemanager->getSeekPosition(f); \ +#define PREP_SECTION(fd, t) { \ + uint32_t type = t; \ + if (!write32(fd, type)) \ + return false; \ + if (!write32(fd, 0)) \ + return false; \ + curpos = lseek(fd, 0, SEEK_CUR); \ } -#define END_SECTION(f) { \ - uint32_t endpos = g_filemanager->getSeekPosition(f); \ - g_filemanager->setSeekPosition(f, curpos-4); \ +#define END_SECTION(fd) { \ + long endpos = lseek(fd, 0, SEEK_CUR); \ + lseek(fd, curpos-4, SEEK_SET); \ uint32_t chunksize = endpos - curpos; \ - if (!write32(f, chunksize)) \ + if (!write32(fd, chunksize)) \ return false; \ - g_filemanager->seekToEnd(f); \ + lseek(fd, 0, SEEK_END); \ } -Woz::Woz() +Woz::Woz(bool verbose, uint8_t dumpflags) { + fd = -1; trackPointer = 0; trackBitIdx = 0x80; trackBitCounter = 0; trackLoopCounter = 0; + imageType = T_AUTO; metaData = NULL; - - fh = -1; - autoFlushTrackData = false; + this->verbose = verbose; + this->dumpflags = dumpflags; memset(&quarterTrackMap, 255, sizeof(quarterTrackMap)); memset(&di, 0, sizeof(diskInfo)); @@ -45,44 +45,125 @@ Woz::Woz() Woz::~Woz() { - if (fh != -1) { - g_filemanager->closeFile(fh); - fh = -1; + if (fd != -1) { + close(fd); + fd = -1; } - + for (int i=0; i<160; i++) { if (tracks[i].trackData) { free(tracks[i].trackData); tracks[i].trackData = NULL; } } - if (metaData) { free(metaData); metaData = NULL; } } - -uint8_t Woz::getNextWozBit(uint8_t track) +// external interface for a disk subsystem to write a bit +bool Woz::writeNextWozBit(uint8_t datatrack, uint8_t bit) { - if (trackBitIdx == 0x80) { - if (!tracks[track].trackData) { - readAndDecodeTrack(track, fh); - } - // need another byte out of the track stream - trackByte = tracks[track].trackData[trackPointer++]; + if (datatrack == 0xFF) { + printf("ERROR: tried to write bit on half-track; not implemented\n"); + return true; } - if (trackBitCounter >= tracks[track].bitCount) { + if (!tracks[datatrack].trackData) { + fprintf(stderr, "ERROR: tried to writeNextWozBit to a data track that's not loaded, and we can't possibly tell which QT that should be\n"); + return false; + } + + if (trackBitCounter >= tracks[datatrack].bitCount) { + printf("WRITE counter reset [%u > %u]\n", trackBitCounter, tracks[datatrack].bitCount); trackPointer = 0; trackBitIdx = 0x80; - trackLoopCounter++; - trackByte = tracks[track].trackData[trackPointer++]; trackBitCounter = 0; } + + if (trackBitIdx == 0x80) { + trackByte = tracks[datatrack].trackData[trackPointer++]; + } + + if (bit) + trackByte |= trackBitIdx; + else + trackByte &= ~trackBitIdx; + + tracks[datatrack].trackData[trackPointer-1] = trackByte; trackBitCounter++; + + trackDirty = true; + + trackBitIdx >>= 1; + if (!trackBitIdx) { + trackBitIdx = 0x80; + } + + return true; +} +// external interface for a disk interface to write a byte +bool Woz::writeNextWozByte(uint8_t datatrack, uint8_t b) +{ + if (datatrack == 0xFF) { + // Not on a track, so pretend to write but throw it away. FIXME: + // probably want to create a new Woz track entry here. + fprintf(stderr, "ERROR: tried to write to a half track; not implemented\n"); + return true; + } + + if (!tracks[datatrack].trackData) { + fprintf(stderr, "ERROR: tried to write to a track that's not loaded, and it's not possible to tell what QT was meant\n"); + return false; + } + + // We could be byte-aligned, but it's not guaranteed, so this + // handles it bitwise. + printf("track %d write byte 0x%.2X @ ptr[%d] bitidx==0x%.2X ctr=%d\n", datatrack, b, trackPointer, trackBitIdx, trackBitCounter); + + // Debugging: aligning to bytes so I can see the effective bitstream + if (trackBitIdx != 0x80) { + while (trackBitIdx) { + trackBitCounter++; + trackBitIdx >>= 1; + } + trackBitIdx = 0x80; + } + // end debugging + + for (uint8_t i=0; i<8; i++) { + writeNextWozBit(datatrack, b & (1 << (7-i)) ? 1 : 0); + } + return true; +} + +uint8_t Woz::getNextWozBit(uint8_t datatrack) +{ + if (datatrack >= 160) { + return 0; + } + + if (!tracks[datatrack].trackData) { + // fprintf(stderr, "ERROR: getNextWozBit was called without the track being cached, and it can't possibly know which QT to load it from\n"); + return 0; + } + + if (trackBitIdx == 0x80) { + // need another byte out of the track stream + if (tracks[datatrack].trackData) { + trackByte = tracks[datatrack].trackData[trackPointer++]; + if (trackPointer >= tracks[datatrack].bitCount / 8) { + trackPointer = 0; + trackLoopCounter++; + } + } else { + trackPointer = 0; + trackLoopCounter++; + } + } + uint8_t ret = (trackByte & trackBitIdx) ? 1 : 0; trackBitIdx >>= 1; @@ -100,23 +181,36 @@ uint8_t Woz::fakeBit() if (randPtr == 0) { randPtr = 0x80; - randData = (uint8_t) ((float)256*rand()/(RAND_MAX+1.0)); + randData = (uint8_t) ((float)256 * rand() / (RAND_MAX + 1.0)); } uint8_t ret = (randData & randPtr) ? 1 : 0; randPtr >>= 1; - + return ret; } -uint8_t Woz::nextDiskBit(uint8_t track) +bool Woz::skipByte(uint8_t datatrack) { - if (track == 0xFF) - return fakeBit(); + // head_window = 0; // FIXME kludgy, but okay if we don't need just one bit after this + trackPointer++; + if (trackPointer >= tracks[datatrack].bitCount / 8) { + trackPointer = 0; + trackLoopCounter++; + } + return true; +} + +uint8_t Woz::nextDiskBit(uint8_t datatrack) +{ + if (!tracks[datatrack].trackData) { + fprintf(stderr, "ERROR: nextDiskBit was called without the track being cached, and it can't possibly know which QT to load it from\n"); + return 0; + } static uint8_t head_window = 0; head_window <<= 1; - head_window |= getNextWozBit(track); + head_window |= getNextWozBit(datatrack); if ((head_window & 0x0f) != 0x00) { return (head_window & 0x02) >> 1; } else { @@ -124,57 +218,64 @@ uint8_t Woz::nextDiskBit(uint8_t track) } } -uint8_t Woz::nextDiskByte(uint8_t track) +uint8_t Woz::nextDiskByte(uint8_t datatrack) { + if (!tracks[datatrack].trackData) { + fprintf(stderr, "ERROR: nextDiskByte was called without the track being cached, and it can't possibly know which QT to load it from\n"); + return 0; + } + uint8_t d = 0; while ((d & 0x80) == 0) { d <<= 1; - d |= nextDiskBit(track); + d |= nextDiskBit(datatrack); } return d; } -static bool write8(uint8_t fh, uint8_t v) +static bool write8(int fd, uint8_t v) { - if (!g_filemanager->writeByte(fh, v)) + if (write(fd, &v, 1) != 1) return false; return true; } -static bool write16(uint8_t fh, uint16_t v) +static bool write16(int fd, uint16_t v) { - if (!write8(fh, v & 0xFF)) + if (!write8(fd, v & 0xFF)) return false; v >>= 8; - if (!write8(fh, v & 0xFF)) + if (!write8(fd, v & 0xFF)) return false; return true; } -static bool write32(uint8_t fh, uint32_t v) +static bool write32(int fd, uint32_t v) { for (int i=0; i<4; i++) { - if (!write8(fh, v&0xFF)) + if (!write8(fd, v&0xFF)) return false; v >>= 8; } return true; } -static bool read8(uint8_t fd, uint8_t *toWhere) +static bool read8(int fd, uint8_t *toWhere) { - // FIXME: no error checking - *toWhere = g_filemanager->readByte(fd); + uint8_t r; + if (read(fd, &r, 1) != 1) + return false; + *toWhere = r; return true; } -static bool read16(uint8_t fh, uint16_t *toWhere) +static bool read16(int fd, uint16_t *toWhere) { uint16_t ret = 0; for (int i=0; i<2; i++) { uint8_t r; - if (!read8(fh, &r)) { + if (!read8(fd, &r)) { return false; } ret >>= 8; @@ -186,12 +287,12 @@ static bool read16(uint8_t fh, uint16_t *toWhere) return true; } -static bool read32(uint8_t fh, uint32_t *toWhere) +static bool read32(int fd, uint32_t *toWhere) { uint32_t ret = 0; for (int i=0; i<4; i++) { uint8_t r; - if (!read8(fh, &r)) { + if (!read8(fd, &r)) { return false; } ret >>= 8; @@ -203,9 +304,50 @@ static bool read32(uint8_t fh, uint32_t *toWhere) return true; } -bool Woz::writeFile(uint8_t version, const char *filename) +bool Woz::writeFile(const char *filename, uint8_t forceType) { - int8_t fh = -1; // filehandle (-1 == closed) + if (forceType == T_AUTO) { + // Try to determine type from the file extension + const char *p = strrchr(filename, '.'); + if (!p) { + printf("Unable to determine file type of '%s'\n", filename); + return false; + } + if (strcasecmp(p, ".woz") == 0) { + forceType = T_WOZ; + } else if (strcasecmp(p, ".dsk") == 0 || + strcasecmp(p, ".do") == 0) { + forceType = T_DSK; + } else if (strcasecmp(p, ".po") == 0) { + forceType = T_PO; + } else if (strcasecmp(p, ".nib") == 0) { + forceType = T_NIB; + } else { + printf("Unable to determine file type of '%s'\n", filename); + return false; + } + } + + switch (forceType) { + case T_WOZ: + return writeWozFile(filename, forceType); + case T_DSK: + case T_PO: + return writeDskFile(filename, forceType); + case T_NIB: + return writeNibFile(filename); + default: + printf("Unknown disk type; unable to write\n"); + return false; + } +} + + +bool Woz::writeWozFile(const char *filename, uint8_t subtype) +{ + int version = 2; // FIXME: determine from subtype + + int fdout = -1; bool retval = false; uint32_t tmp32; // scratch 32-bit value off_t crcPos, endPos; @@ -215,17 +357,13 @@ bool Woz::writeFile(uint8_t version, const char *filename) if (version > 2 || !version) { -#ifndef TEENSYDUINO fprintf(stderr, "ERROR: version must be 1 or 2\n"); -#endif goto done; } - fh = g_filemanager->openFile(filename); - if (fh==-1) { -#ifndef TEENSYDUINO - printf("ERROR: Unable to open output file\n"); -#endif + fdout = open(filename, O_TRUNC|O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); + if (fdout == -1) { + perror("ERROR: Unable to open output file"); goto done; } @@ -235,102 +373,83 @@ bool Woz::writeFile(uint8_t version, const char *filename) } else { tmp32 = 0x325A4F57; } - if (!write32(fh, tmp32)) { -#ifndef TEENSYDUINO + if (!write32(fdout, tmp32)) { fprintf(stderr, "ERROR: failed to write\n"); -#endif goto done; } tmp32 = 0x0A0D0AFF; - if (!write32(fh, tmp32)) { -#ifndef TEENSYDUINO + if (!write32(fdout, tmp32)) { fprintf(stderr, "ERROR: failed to write\n"); -#endif goto done; } // We'll come back and write the checksum later - crcPos = g_filemanager->getSeekPosition(fh); + crcPos = lseek(fdout, 0, SEEK_CUR); tmp32 = 0; - if (!write32(fh, tmp32)) { -#ifndef TEENSYDUINO + if (!write32(fdout, tmp32)) { fprintf(stderr, "ERROR: failed to write\n"); -#endif goto done; } - PREP_SECTION(fh, 0x4F464E49); // 'INFO' - if (!writeInfoChunk(version, fh)) { -#ifndef TEENSYDUINO + PREP_SECTION(fdout, 0x4F464E49); // 'INFO' + if (!writeInfoChunk(version, fdout)) { fprintf(stderr, "ERROR: failed to write INFO chunk\n"); -#endif goto done; } - END_SECTION(fh); + END_SECTION(fdout); - PREP_SECTION(fh, 0x50414D54); // 'TMAP' - if (!writeTMAPChunk(version, fh)) { -#ifndef TEENSYDUINO + PREP_SECTION(fdout, 0x50414D54); // 'TMAP' + if (!writeTMAPChunk(version, fdout)) { fprintf(stderr, "ERROR: failed to write TMAP chunk\n"); -#endif goto done; } - END_SECTION(fh); + END_SECTION(fdout); - PREP_SECTION(fh, 0x534B5254); // 'TRKS' - if (!writeTRKSChunk(version, fh)) { + PREP_SECTION(fdout, 0x534B5254); // 'TRKS' + if (!writeTRKSChunk(version, fdout)) { fprintf(stderr, "ERROR: failed to write TRKS chunk\n"); goto done; } - END_SECTION(fh); + END_SECTION(fdout); // Write the metadata if we have any if (metaData) { - PREP_SECTION(fh, 0x4154454D); // 'META' - for (int i=0; igetSeekPosition(fh); + endPos = lseek(fdout, 0, SEEK_CUR); crcDataSize = endPos-crcPos-4; crcData = (uint8_t *)malloc(crcDataSize); if (!crcData) { -#ifndef TEENSYDUINO fprintf(stderr, "ERROR: failed to malloc crc data chunk\n"); -#endif goto done; } // Read the data in for checksumming - // FIXME: no error checking on seek - g_filemanager->setSeekPosition(fh, crcPos+4); + if (lseek(fdout, crcPos+4, SEEK_SET) == -1) { + fprintf(stderr, "ERROR: failed to fseek to crcPos+4 (0x%llX)\n", crcPos+4); + goto done; + } - for (int i=0; isetSeekPosition(fh, crcPos); - if (!write32(fh, tmp32)) { -#ifndef TEENSYDUINO + lseek(fdout, crcPos, SEEK_SET); + if (!write32(fdout, tmp32)) { fprintf(stderr, "ERROR: failed to write CRC\n"); -#endif goto done; } @@ -339,12 +458,64 @@ bool Woz::writeFile(uint8_t version, const char *filename) done: if (crcData) free(crcData); - if (fh != -1) { - g_filemanager->closeFile(fh); - } + if (fdout != -1) + close(fdout); return retval; } +bool Woz::writeDskFile(const char *filename, uint8_t subtype) +{ + if (isSynchronized()) { + fprintf(stderr, "WARNING: disk image has synchronized tracks; it may not work as a DSK or NIB file.\n"); + } + + int fdout = open(filename, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); + if (fdout == -1) { + perror("Failed to open output file"); + exit(1); + } + uint8_t sectorData[256*16]; + for (int phystrack=0; phystrack<35; phystrack++) { + if (!decodeWozTrackToDsk(quarterTrackMap[phystrack*4], subtype, sectorData)) { + fprintf(stderr, "Failed to decode track %d; aborting\n", phystrack); + exit(1); + } + ssize_t numWritten = write(fdout, sectorData, 256*16); + if (numWritten != 256*16) { + perror("Failed[2] to write to track; aborting"); + exit(1); + } + } + close(fdout); + return true; +} + +bool Woz::writeNibFile(const char *filename) +{ + if (isSynchronized()) { + fprintf(stderr, "WARNING: disk image has synchronized tracks; it may not work as a DSK or NIB file.\n"); + } + + int fdout = open(filename, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); + if (fdout == -1) { + perror("Failed to open output file"); + exit(1); + } + nibSector nibData[16]; + for (int phystrack=0; phystrack<35; phystrack++) { + if (!decodeWozTrackToNib(quarterTrackMap[phystrack*4], nibData)) { + fprintf(stderr, "Failed to decode track %d; aborting\n", phystrack); + exit(1); + } + if (write(fdout, nibData, NIBTRACKSIZE) != NIBTRACKSIZE) { + fprintf(stderr, "Failed[1] to write track %d; aborting\n", phystrack); + exit(1); + } + } + close(fdout); + return true; +} + void Woz::_initInfo() { di.version = 2; @@ -374,72 +545,93 @@ void Woz::_initInfo() } } -bool Woz::readAndDecodeTrack(uint8_t track, int8_t fh) +// Only used if we didn't preload a data track; the load we perfrom +// differs based on the image type we originally read from +bool Woz::loadMissingTrackFromImage(uint8_t datatrack) { // If we're going to malloc a new one, then find all the other ones - // that might be malloc'd and purge them if we're autoFlushTrackData==true + // that might be malloc'd and purge them if we're + // autoFlushTrackData==true (trying to limit memory use) if (autoFlushTrackData == true) { + + if (trackDirty) { + printf("Hackily writing /tmp/auto.woz\n"); + trackDirty = false; + writeFile("/tmp/auto.woz"/*, T_WOZ2*/); // FIXME: debugging + } + for (int i=0; i<160; i++) { if (tracks[i].trackData) { - free(tracks[i].trackData); - tracks[i].trackData = NULL; + free(tracks[i].trackData); + tracks[i].trackData = NULL; } } } - + // Based on the source image type, load the data track we're looking for if (imageType == T_WOZ) { - return readWozTrackData(fh, track); - } else if (imageType == T_PO || - imageType == T_DSK) { + // If the source was WOZ, just load the datatrack directly + return readWozDataTrack(datatrack); + } else if (imageType == T_PO || + imageType == T_DSK) { + // If the source was a DSK file, then the datatrack was mapped directly + // from the physical track + if (datatrack >= 35) { + // There are only 35 tracks; the remainder are blank. + tracks[datatrack].trackData = (uint8_t *)calloc(NIBTRACKSIZE, 1); + return true; + } + + uint8_t phystrack = datatrack; // used for clarity of which kind of track we mean, below + static uint8_t sectorData[256*16]; - g_filemanager->setSeekPosition(fh, 256*16*track); - - for (int i=0; i<256*16; i++) { - // FIXME: no error checking - sectorData[i] = g_filemanager->readByte(fh); - } - - tracks[track].trackData = (uint8_t *)calloc(NIBTRACKSIZE, 1); - if (!tracks[track].trackData) { -#ifndef TEENSYDUINO - fprintf(stderr, "Failed to malloc track data\n"); -#endif + lseek(fd, 256*16*phystrack, SEEK_SET); + + if (read(fd, sectorData, 256*16) != 256*16) { + fprintf(stderr, "Failed to read sector\n"); return false; } - tracks[track].startingBlock = STARTBLOCK + 13*track; - tracks[track].blockCount = 13; - uint32_t sizeInBits = nibblizeTrack(tracks[track].trackData, sectorData, imageType, track); - tracks[track].bitCount = sizeInBits; // ... reality. + + tracks[datatrack].trackData = (uint8_t *)calloc(NIBTRACKSIZE, 1); + if (!tracks[datatrack].trackData) { + fprintf(stderr, "Failed to malloc track data\n"); + return false; + } + tracks[datatrack].startingBlock = STARTBLOCK + 13*phystrack; + tracks[datatrack].blockCount = 13; + uint32_t sizeInBits = nibblizeTrack(tracks[datatrack].trackData, sectorData, imageType, phystrack); + tracks[datatrack].bitCount = sizeInBits; // ... reality. return true; } else if (imageType == T_NIB) { - tracks[track].trackData = (uint8_t *)malloc(NIBTRACKSIZE); - if (!tracks[track].trackData) { -#ifndef TEENSYDUINO + if (datatrack >= 35) { + // There are only 35 tracks; the remainder are blank. + tracks[datatrack].trackData = (uint8_t *)calloc(NIBTRACKSIZE, 1); + return true; + } + // If the source was a NIB file, then the datatrack is directly + // mapped 1:1 to the physical track + uint8_t phystrack = datatrack; // used for clarity of which kind of track we mean, below + tracks[datatrack].trackData = (uint8_t *)malloc(NIBTRACKSIZE); + if (!tracks[datatrack].trackData) { printf("Failed to malloc track data\n"); -#endif return false; } - g_filemanager->setSeekPosition(fh, NIBTRACKSIZE * track); - for (int i=0; ireadByte(fh); - } - - tracks[track].startingBlock = STARTBLOCK + 13*track; - tracks[track].blockCount = 13; - tracks[track].bitCount = NIBTRACKSIZE*8; - + + tracks[datatrack].startingBlock = STARTBLOCK + 13*phystrack; + tracks[datatrack].blockCount = 13; + tracks[datatrack].bitCount = NIBTRACKSIZE*8; + return true; } - -#ifndef TEENSYDUINO + printf("ERROR: don't know how we reached this point\n"); -#endif return false; } @@ -447,14 +639,12 @@ bool Woz::readDskFile(const char *filename, bool preloadTracks, uint8_t subtype) { bool retval = false; autoFlushTrackData = !preloadTracks; - imageType = subtype; - fh = g_filemanager->openFile(filename); - if (fh == -1) { -#ifndef TEENSYDUINO + if (fd != -1) close(fd); + fd = open(filename, O_RDONLY); + if (fd == -1) { perror("Unable to open input file"); -#endif goto done; } @@ -462,47 +652,78 @@ bool Woz::readDskFile(const char *filename, bool preloadTracks, uint8_t subtype) // Now read in the 35 tracks of data from the DSK file and convert them to NIB if (preloadTracks) { - for (int track=0; track<35; track++) { - if (!readAndDecodeTrack(track, fh)) { + uint8_t sectorData[256*16]; + for (int phystrack=0; phystrack<35; phystrack++) { + uint32_t bytesRead = read(fd, sectorData, 256*16); + if (bytesRead != 256*16) { + fprintf(stderr, "Failed to read DSK data; got %d bytes, wanted %d\n", bytesRead, 256); goto done; } + uint8_t datatrack = quarterTrackMap[phystrack*4]; + tracks[datatrack].trackData = (uint8_t *)calloc(NIBTRACKSIZE, 1); + if (!tracks[datatrack].trackData) { + fprintf(stderr, "Failed to malloc track data\n"); + goto done; + } + tracks[datatrack].startingBlock = STARTBLOCK + 13*datatrack; + tracks[datatrack].blockCount = 13; + uint32_t sizeInBits = nibblizeTrack(tracks[datatrack].trackData, sectorData, subtype, phystrack); + tracks[datatrack].bitCount = sizeInBits; // ... reality. } - } - + } + retval = true; done: + if (preloadTracks && fd != -1) { + close(fd); + fd = -1; + } return retval; } bool Woz::readNibFile(const char *filename, bool preloadTracks) { - bool ret = false; autoFlushTrackData = !preloadTracks; - imageType = T_NIB; - fh = g_filemanager->openFile(filename); - if (fh == -1) { -#ifndef TEENSYDUINO + if (fd != -1) close(fd); + fd = open(filename, O_RDONLY); + if (fd == -1) { perror("Unable to open input file"); -#endif - goto done; + return false; } _initInfo(); + // Now read in the 35 tracks of data from the nib file if (preloadTracks) { - for (int track=0; track<35; track++) { - if (!readAndDecodeTrack(track, fh)) { - goto done; + nibSector nibData[16]; + for (int phystrack=0; phystrack<35; phystrack++) { + uint32_t bytesRead = read(fd, nibData, NIBTRACKSIZE); + if (bytesRead != NIBTRACKSIZE) { + printf("Failed to read NIB data; got %d bytes, wanted %d\n", bytesRead, NIBTRACKSIZE); + return false; } + uint8_t datatrack = quarterTrackMap[phystrack * 4]; + tracks[datatrack].trackData = (uint8_t *)calloc(NIBTRACKSIZE, 1); + if (!tracks[datatrack].trackData) { + printf("Failed to malloc track data\n"); + return false; + } + + memcpy(tracks[datatrack].trackData, nibData, NIBTRACKSIZE); + tracks[datatrack].startingBlock = STARTBLOCK + 13*phystrack; + tracks[datatrack].blockCount = 13; + tracks[datatrack].bitCount = NIBTRACKSIZE*8; } } - ret = true; + if (preloadTracks && fd != -1) { + close(fd); + fd = -1; + } - done: - return ret; + return true; } bool Woz::readWozFile(const char *filename, bool preloadTracks) @@ -510,45 +731,49 @@ bool Woz::readWozFile(const char *filename, bool preloadTracks) imageType = T_WOZ; autoFlushTrackData = !preloadTracks; - fh = g_filemanager->openFile(filename); - if (fh == -1) { -#ifndef TEENSYDUINO + if (fd != -1) close(fd); + fd = open(filename, O_RDONLY); + if (fd == -1) { perror("Unable to open input file"); -#endif return false; } // Header uint32_t h; - read32(fh, &h); + read32(fd, &h); if (h == 0x325A4F57 || h == 0x315A4F57) { -#ifndef TEENSYDUINO - printf("WOZ%c disk image\n", (h & 0xFF000000)>>24); -#endif + if (verbose) { + printf("WOZ%c disk image\n", (h & 0xFF000000)>>24); + } } else { -#ifndef TEENSYDUINO printf("Unknown disk image type; can't continue\n"); -#endif + if (preloadTracks && fd != -1) + close(fd); return false; } uint32_t tmp; - if (!read32(fh, &tmp)) { -#ifndef TEENSYDUINO + if (!read32(fd, &tmp)) { printf("Read failure\n"); -#endif + if (preloadTracks && fd != -1) + close(fd); return false; } if (tmp != 0x0A0D0AFF) { -#ifndef TEENSYDUINO printf("WOZ header failure; exiting\n"); -#endif + if (preloadTracks && fd != -1) + close(fd); return false; } uint32_t crc32; - read32(fh, &crc32); - // printf("Disk crc32 should be 0x%X\n", crc32); - // FIXME: check CRC. Remember that 0x00 means "don't check CRC" + read32(fd, &crc32); + // If CRC is set, then check it + if (crc32) { + // FIXME: check CRC + if (verbose) { + printf("Disk crc32 should be 0x%X\n", crc32); + } + } uint32_t fpos = 12; uint8_t haveData = 0; @@ -558,73 +783,98 @@ bool Woz::readWozFile(const char *filename, bool preloadTracks) #define cTRKS 4 while (1) { - if (!g_filemanager->setSeekPosition(fh, fpos)) + if (lseek(fd, fpos, SEEK_SET) == -1) { break; + } uint32_t chunkType; - if (!read32(fh, &chunkType)) { - printf("Failed to read chunktype; breaking from loop\n"); + if (!read32(fd, &chunkType)) { break; } uint32_t chunkDataSize; - read32(fh, &chunkDataSize); + read32(fd, &chunkDataSize); + if ((int32_t)chunkDataSize < 0) { + printf("ERROR: data size < 0?\n"); + exit(1); + } bool isOk; - printf("reading chunk type 0x%X\n", chunkType); + switch (chunkType) { case 0x4F464E49: // 'INFO' - isOk = parseInfoChunk(fh, chunkDataSize); + if (verbose) { + printf("Reading INFO chunk starting at byte 0x%llX\n", + lseek(fd, 0, SEEK_CUR)); + } + isOk = parseInfoChunk(chunkDataSize); haveData |= cINFO; break; case 0x50414D54: // 'TMAP' - isOk = parseTMAPChunk(fh, chunkDataSize); + if (verbose) { + printf("Reading TMAP chunk starting at byte 0x%llX\n", + lseek(fd, 0, SEEK_CUR)); + } + isOk = parseTMAPChunk(chunkDataSize); haveData |= cTMAP; break; case 0x534B5254: // 'TRKS' - isOk = parseTRKSChunk(fh, chunkDataSize); + if (verbose) { + printf("Reading TRKS chunk starting at byte 0x%llX\n", + lseek(fd, 0, SEEK_CUR)); + } + isOk = parseTRKSChunk(chunkDataSize); haveData |= cTRKS; break; case 0x4154454D: // 'META' - isOk = parseMetaChunk(fh, chunkDataSize); + if (verbose) { + printf("Reading META chunk starting at byte 0x%llX\n", + lseek(fd, 0, SEEK_CUR)); + } + isOk = parseMetaChunk(chunkDataSize); break; default: -#ifndef TEENSYDUINO - printf("Unknown chunk type 0x%X; failed to read woz file\n", chunkType); -#endif + printf("Unknown chunk type 0x%X\n", chunkType); + if (preloadTracks && fd != -1) + close(fd); return false; + break; } if (!isOk) { -#ifndef TEENSYDUINO printf("Chunk parsing [0x%X] failed; exiting\n", chunkType); -#endif + if (preloadTracks && fd != -1) + close(fd); return false; } - fpos += chunkDataSize + 8; // 8 bytes for the ChunkID and the ChunkSize } if (haveData != 0x07) { -#ifndef TEENSYDUINO printf("ERROR: missing one or more critical sections\n"); -#endif return false; } + // For a Woz file, we need to read *every* quarter-track; and if we've + // already got the target track's data, we don't need to re-read it. + // And if we're not preloading the tracks, then we'll wind up loading + // them on demand later. if (preloadTracks) { - for (int i=0; i<40*4; i++) { - if (!readQuarterTrackData(fh, i)) { -#ifndef TEENSYDUINO - printf("Failed to read QTD for track %d\n", i); -#endif + for (int i=0; i<160; i++) { + if (!readWozDataTrack(i)) { + printf("Failed to read Woz datatrack %d\n", i); + if (fd != -1) { + close(fd); + fd = -1; + } return false; } } } -#ifndef TEENSYDUINO - printf("File read successful\n"); -#endif + if (preloadTracks && fd != -1) { + fd = -1; + close(fd); + } return true; } @@ -634,9 +884,7 @@ bool Woz::readFile(const char *filename, bool preloadTracks, uint8_t forceType) // Try to determine type from the file extension const char *p = strrchr(filename, '.'); if (!p) { -#ifndef TEENSYDUINO printf("Unable to determine file type of '%s'\n", filename); -#endif return false; } if (strcasecmp(p, ".woz") == 0) { @@ -649,55 +897,40 @@ bool Woz::readFile(const char *filename, bool preloadTracks, uint8_t forceType) } else if (strcasecmp(p, ".nib") == 0) { forceType = T_NIB; } else { -#ifndef TEENSYDUINO printf("Unable to determine file type of '%s'\n", filename); -#endif return false; } } switch (forceType) { case T_WOZ: -#ifndef TEENSYDUINO - printf("reading woz file %s\n", filename); -#endif return readWozFile(filename, preloadTracks); case T_DSK: case T_PO: -#ifndef TEENSYDUINO - printf("reading DSK file %s\n", filename); -#endif return readDskFile(filename, preloadTracks, forceType); case T_NIB: -#ifndef TEENSYDUINO - printf("reading NIB file %s\n", filename); -#endif return readNibFile(filename, preloadTracks); default: -#ifndef TEENSYDUINO printf("Unknown disk type; unable to read\n"); -#endif return false; } } -bool Woz::parseTRKSChunk(int8_t fh, uint32_t chunkSize) +bool Woz::parseTRKSChunk(uint32_t chunkSize) { if (di.version == 2) { - printf("v2 parse\n"); for (int i=0; i<160; i++) { - if (!read16(fh, &tracks[i].startingBlock)) + if (!read16(fd, &tracks[i].startingBlock)) return false; - if (!read16(fh, &tracks[i].blockCount)) + if (!read16(fd, &tracks[i].blockCount)) return false; - if (!read32(fh, &tracks[i].bitCount)) + if (!read32(fd, &tracks[i].bitCount)) return false; tracks[i].startingByte = 0; // v1-specific } return true; } - printf("v1 parse\n"); // V1 parsing uint32_t ptr = 0; uint8_t trackNumber = 0; @@ -705,11 +938,17 @@ bool Woz::parseTRKSChunk(int8_t fh, uint32_t chunkSize) tracks[trackNumber].startingByte = trackNumber * 6656 + 256; tracks[trackNumber].startingBlock = 0; // v2-specific tracks[trackNumber].blockCount = 13; - g_filemanager->setSeekPosition(fh, (trackNumber * 6656 + 256) + 6648); // FIXME: no error checking + lseek(fd, (trackNumber * 6656 + 256) + 6648, SEEK_SET); uint16_t numBits; - if (!read16(fh, &numBits)) { + if (!read16(fd, &numBits)) { return false; } + if (verbose) { + printf("Track %d: read %d bits\n", trackNumber, numBits); + } + if (numBits > 6656 * 8) { + fprintf(stderr, "WARNING: track %d looks like it's too long (%d bits > 6656 bytes)?\n", trackNumber, numBits); + } tracks[trackNumber].bitCount = numBits; ptr += 6656; trackNumber++; @@ -718,79 +957,82 @@ bool Woz::parseTRKSChunk(int8_t fh, uint32_t chunkSize) return true; } -bool Woz::parseTMAPChunk(int8_t fh, uint32_t chunkSize) +bool Woz::parseTMAPChunk(uint32_t chunkSize) { if (chunkSize != 0xa0) { -#ifndef TEENSYDUINO printf("TMAP chunk is the wrong size; aborting\n"); -#endif return false; } for (int i=0; i<40*4; i++) { - if (!read8(fh, (uint8_t *)&quarterTrackMap[i])) + if (!read8(fd, (uint8_t *)&quarterTrackMap[i])) return false; chunkSize--; } + if (verbose){ + printf("Read quarter-track map:\n"); + for (int i=0; i<140; i+=4) { + printf("%2d %3d => %3d %3d => %3d %3d => %3d %3d => %3d\n", + i/4, + i, quarterTrackMap[i], + i+1, quarterTrackMap[i+1], + i+2, quarterTrackMap[i+2], + i+3, quarterTrackMap[i+3]); + } + } return true; } // return true if successful -bool Woz::parseInfoChunk(int8_t fh, uint32_t chunkSize) +bool Woz::parseInfoChunk(uint32_t chunkSize) { if (chunkSize != 60) { -#ifndef TEENSYDUINO - printf("INFO chunk size is not 60; aborting\n"); -#endif + fprintf(stderr, "INFO chunk size is not 60; aborting\n"); return false; } - if (!read8(fh, &di.version)) + if (!read8(fd, &di.version)) return false; if (di.version > 2) { -#ifndef TEENSYDUINO - printf("Incorrect version header; aborting\n"); -#endif + fprintf(stderr, "Incorrect version header; aborting\n"); return false; } - if (!read8(fh, &di.diskType)) + if (!read8(fd, &di.diskType)) return false; if (di.diskType != 1) { -#ifndef TEENSYDUINO - printf("Not a 5.25\" disk image; aborting\n"); -#endif + fprintf(stderr, "Not a 5.25\" disk image; aborting\n"); return false; } - if (!read8(fh, &di.writeProtected)) + if (!read8(fd, &di.writeProtected)) return false; - if (!read8(fh, &di.synchronized)) + if (!read8(fd, &di.synchronized)) return false; - if (!read8(fh, &di.cleaned)) + if (!read8(fd, &di.cleaned)) return false; di.creator[32] = 0; for (int i=0; i<32; i++) { - if (!read8(fh, (uint8_t *)&di.creator[i])) + if (!read8(fd, (uint8_t *)&di.creator[i])) return false; } if (di.version >= 2) { - if (!read8(fh, &di.diskSides)) + if (!read8(fd, &di.diskSides)) return false; - if (!read8(fh, &di.bootSectorFormat)) + if (!read8(fd, &di.bootSectorFormat)) return false; - if (!read8(fh, &di.optimalBitTiming)) + if (!read8(fd, &di.optimalBitTiming)) return false; - if (!read16(fh, &di.compatHardware)) + if (!read16(fd, &di.compatHardware)) return false; - if (!read16(fh, &di.requiredRam)) + if (!read16(fd, &di.requiredRam)) return false; - if (!read16(fh, &di.largestTrack)) + if (!read16(fd, &di.largestTrack)) return false; } else { di.diskSides = 0; @@ -798,107 +1040,126 @@ bool Woz::parseInfoChunk(int8_t fh, uint32_t chunkSize) di.compatHardware = 0; di.requiredRam = 0; di.largestTrack = 13; // 13 * 512 bytes = 6656. All tracks are - // padded to 6646 bytes in the v1 image. + // padded to 6646 (yes, 6646, not 6656)bytes + // in the v1 image. di.optimalBitTiming = 32; // "standard" disk bit timing for a 5.25" disk (4us per bit) } return true; } -bool Woz::parseMetaChunk(int8_t fh, uint32_t chunkSize) +bool Woz::parseMetaChunk(uint32_t chunkSize) { metaData = (char *)calloc(chunkSize+1, 1); if (!metaData) return false; - for (int i=0; ireadByte(fh); // FIXME: no error checking - } + if (read(fd, metaData, chunkSize) != chunkSize) + return false; metaData[chunkSize] = 0; return true; } -bool Woz::readWozTrackData(int8_t fh, uint8_t wt) +bool Woz::readWozDataTrack(uint8_t datatrack) { - // assume if it's malloc'd, then we've already read it - if (tracks[wt].trackData) + // If it's already loaded then there's nothing to do here + if (tracks[datatrack].trackData) return true; - uint16_t bitsStartBlock = tracks[wt].startingBlock; + // If we have no open FD, then assume anything missing is supposed + // to be missing + if (fd == -1) { + return true; + } + + uint16_t bitsStartBlock = tracks[datatrack].startingBlock; - // if (tracks[targetImageTrack].trackData) - // free(tracks[targetImageTrack].trackData); - // Allocate a new buffer for this track - uint32_t count = tracks[wt].blockCount * 512; - if (di.version == 1) count = (tracks[wt].bitCount / 8) + ((tracks[wt].bitCount % 8) ? 1 : 0); - tracks[wt].trackData = (uint8_t *)calloc(count, 1); - if (!tracks[wt].trackData) { -#ifndef TEENSYDUINO + uint32_t count = tracks[datatrack].blockCount * 512; + if (di.version == 1) count = (tracks[datatrack].bitCount / 8) + ((tracks[datatrack].bitCount % 8) ? 1 : 0); + if (tracks[datatrack].trackData) { + return true; // We've already read this track's data; don't re-read it + } + tracks[datatrack].trackData = (uint8_t *)calloc(count, 1); + if (!tracks[datatrack].trackData) { perror("Failed to alloc buf to read track magnetic data"); -#endif return false; } if (di.version == 1) { - g_filemanager->setSeekPosition(fh, tracks[wt].startingByte); // FIXME: no error checking + if (verbose) { + printf("Reading datatrack %d starting at byte 0x%X\n", + datatrack, + tracks[datatrack].startingByte); + } + if (lseek(fd, tracks[datatrack].startingByte, SEEK_SET) == -1) { + perror("Failed to seek to start of block"); + return false; + } } else { - g_filemanager->setSeekPosition(fh, bitsStartBlock*512); // FIXME: no error checking + if (verbose) { + printf("Reading datatrack %d starting at byte 0x%X\n", + datatrack, + bitsStartBlock*512); + } + if (lseek(fd, bitsStartBlock*512, SEEK_SET) == -1) { + perror("Failed to seek to start of block"); + return false; + } } - for (int i=0; ireadByte(fh); + uint32_t didRead = read(fd, tracks[datatrack].trackData, count); + if (didRead != count) { + printf("Failed to read all track data for track [read %d, wanted %d]\n", didRead, count); + return false; } return true; } -bool Woz::readQuarterTrackData(int8_t fh, uint8_t quartertrack) -{ - uint8_t targetImageTrack = quarterTrackMap[quartertrack]; - if (targetImageTrack == 0xFF) { - // It's a tween-track with no reliable data. - return true; - } - return readWozTrackData(fh, targetImageTrack); -} - - -bool Woz::readSectorData(uint8_t track, uint8_t sector, nibSector *sectorData) +bool Woz::readNibSectorData(uint8_t phystrack, uint8_t sector, nibSector *sectorData) { // Find the sector header for this sector... uint32_t ptr = 0; + uint8_t dataTrack = quarterTrackMap[phystrack*4]; + if (!tracks[dataTrack].trackData) { + // Load the cached track for this phys Nib track. + if (!loadMissingTrackFromImage(dataTrack)) { + fprintf(stderr, "Failed to read track %d\n", dataTrack); + return false; + } + } + memset(sectorData->gap1, 0xFF, sizeof(sectorData->gap1)); memset(sectorData->gap2, 0xFF, sizeof(sectorData->gap1)); // Allow two loops through the track data looking for the sector prolog - uint32_t endCount = tracks[track].blockCount*512*2; + uint32_t endCount = tracks[dataTrack].blockCount*512*2; if (di.version == 1) endCount = 2*6646; while (ptr < endCount) { sectorData->sectorProlog[0] = sectorData->sectorProlog[1]; sectorData->sectorProlog[1] = sectorData->sectorProlog[2]; - sectorData->sectorProlog[2] = nextDiskByte(track); + sectorData->sectorProlog[2] = nextDiskByte(dataTrack); ptr++; if (sectorData->sectorProlog[0] == 0xd5 && sectorData->sectorProlog[1] == 0xaa && sectorData->sectorProlog[2] == 0x96) { // Found *a* sector header. See if it's ours. - sectorData->volume44[0] = nextDiskByte(track); - sectorData->volume44[1] = nextDiskByte(track); - sectorData->track44[0] = nextDiskByte(track); - sectorData->track44[1] = nextDiskByte(track); - sectorData->sector44[0] = nextDiskByte(track); - sectorData->sector44[1] = nextDiskByte(track); - sectorData->checksum44[0] = nextDiskByte(track); - sectorData->checksum44[1] = nextDiskByte(track); - sectorData->sectorEpilog[0] = nextDiskByte(track); - sectorData->sectorEpilog[1] = nextDiskByte(track); - sectorData->sectorEpilog[2] = nextDiskByte(track); + sectorData->volume44[0] = nextDiskByte(dataTrack); + sectorData->volume44[1] = nextDiskByte(dataTrack); + sectorData->track44[0] = nextDiskByte(dataTrack); + sectorData->track44[1] = nextDiskByte(dataTrack); + sectorData->sector44[0] = nextDiskByte(dataTrack); + sectorData->sector44[1] = nextDiskByte(dataTrack); + sectorData->checksum44[0] = nextDiskByte(dataTrack); + sectorData->checksum44[1] = nextDiskByte(dataTrack); + sectorData->sectorEpilog[0] = nextDiskByte(dataTrack); + sectorData->sectorEpilog[1] = nextDiskByte(dataTrack); + sectorData->sectorEpilog[2] = nextDiskByte(dataTrack); if (sectorData->sectorEpilog[0] == 0xde && sectorData->sectorEpilog[1] == 0xaa && @@ -909,10 +1170,10 @@ bool Woz::readSectorData(uint8_t track, uint8_t sector, nibSector *sectorData) continue; } // It's our sector - find the data chunk and read it - while (ptr < tracks[track].blockCount*512*2) { + while (ptr < tracks[dataTrack].blockCount*512*2) { sectorData->dataProlog[0] = sectorData->dataProlog[1]; sectorData->dataProlog[1] = sectorData->dataProlog[2]; - sectorData->dataProlog[2] = nextDiskByte(track); + sectorData->dataProlog[2] = nextDiskByte(dataTrack); ptr++; if (sectorData->dataProlog[0] == 0xd5 && @@ -920,12 +1181,12 @@ bool Woz::readSectorData(uint8_t track, uint8_t sector, nibSector *sectorData) sectorData->dataProlog[2] == 0xad) { // Found the data; copy it in for (int i=0; i<342; i++) { - sectorData->data62[i] = nextDiskByte(track); + sectorData->data62[i] = nextDiskByte(dataTrack); } - sectorData->checksum = nextDiskByte(track); - sectorData->dataEpilog[0] = nextDiskByte(track); - sectorData->dataEpilog[1] = nextDiskByte(track); - sectorData->dataEpilog[2] = nextDiskByte(track); + sectorData->checksum = nextDiskByte(dataTrack); + sectorData->dataEpilog[0] = nextDiskByte(dataTrack); + sectorData->dataEpilog[1] = nextDiskByte(dataTrack); + sectorData->dataEpilog[2] = nextDiskByte(dataTrack); if (sectorData->dataEpilog[0] != 0xde || sectorData->dataEpilog[1] != 0xaa || sectorData->dataEpilog[2] != 0xeb) { @@ -942,17 +1203,17 @@ bool Woz::readSectorData(uint8_t track, uint8_t sector, nibSector *sectorData) return false; } -bool Woz::writeInfoChunk(uint8_t version, int8_t fh) +bool Woz::writeInfoChunk(uint8_t version, int fdout) { - if (!write8(fh, version) || - !write8(fh, di.diskType) || - !write8(fh, di.writeProtected) || - !write8(fh, di.synchronized) || - !write8(fh, di.cleaned)) + if (!write8(fdout, version) || + !write8(fdout, di.diskType) || + !write8(fdout, di.writeProtected) || + !write8(fdout, di.synchronized) || + !write8(fdout, di.cleaned)) return false; for (int i=0; i<32; i++) { - if (!write8(fh, di.creator[i])) + if (!write8(fdout, di.creator[i])) return false; } @@ -961,39 +1222,37 @@ bool Woz::writeInfoChunk(uint8_t version, int8_t fh) if (di.diskSides == 0) di.diskSides = 1; - if ( !write8(fh, di.diskSides) || - !write8(fh, di.bootSectorFormat) || - !write8(fh, di.optimalBitTiming) || - !write16(fh, di.compatHardware) || - !write16(fh, di.requiredRam) || - !write16(fh, di.largestTrack)) + if ( !write8(fdout, di.diskSides) || + !write8(fdout, di.bootSectorFormat) || + !write8(fdout, di.optimalBitTiming) || + !write16(fdout, di.compatHardware) || + !write16(fdout, di.requiredRam) || + !write16(fdout, di.largestTrack)) return false; } // Padding for (int i=0; i<((version==1)?23:14); i++) { - if (!write8(fh, 0)) + if (!write8(fdout, 0)) return false; } return true; } -bool Woz::writeTMAPChunk(uint8_t version, int8_t fh) +bool Woz::writeTMAPChunk(uint8_t version, int fdout) { for (int i=0; i<40*4; i++) { - if (!write8(fh, quarterTrackMap[i])) + if (!write8(fdout, quarterTrackMap[i])) return false; } return true; } -bool Woz::writeTRKSChunk(uint8_t version, int8_t fh) +bool Woz::writeTRKSChunk(uint8_t version, int fdout) { if (version == 1) { -#ifndef TEENSYDUINO printf("V1 write is not implemented\n"); -#endif return false; } @@ -1001,7 +1260,12 @@ bool Woz::writeTRKSChunk(uint8_t version, int8_t fh) // track. The bitCount should be correct. uint8_t numTracksPacked = 0; for (int i=0; i<160; i++) { - if (tracks[i].trackData) { + // If we didn't preload, and the track isn't loaded, then load it now + if (autoFlushTrackData && !tracks[i].trackData) { + loadMissingTrackFromImage(i); + } + + if (tracks[i].trackData && tracks[i].bitCount) { // For any tracks that have data, put it somewhere in the destination file tracks[i].startingBlock = STARTBLOCK + 13*(numTracksPacked++); // assume tracks[track].bitCount is correct, and recalculate the block size of this track @@ -1012,42 +1276,56 @@ bool Woz::writeTRKSChunk(uint8_t version, int8_t fh) tracks[i].startingBlock = 0; tracks[i].blockCount = 0; tracks[i].bitCount = 0; - } - - if (!write16(fh, tracks[i].startingBlock)) + } + if (!write16(fdout, tracks[i].startingBlock)) return false; - if (!write16(fh, tracks[i].blockCount)) + if (!write16(fdout, tracks[i].blockCount)) return false; - if (!write32(fh, tracks[i].bitCount)) + if (!write32(fdout, tracks[i].bitCount)) return false; } // All the track data for (int i=0; i<160; i++) { + // If we didn't preload, and the track isn't loaded, then load it now + if (autoFlushTrackData && !tracks[i].trackData) { + loadMissingTrackFromImage(i); + } + if (tracks[i].startingBlock && tracks[i].blockCount) { - g_filemanager->setSeekPosition(fh, tracks[i].startingBlock*512); // FIXME: no error checking - uint32_t writeSize = (tracks[i].bitCount / 8) + ((tracks[i].bitCount % 8) ? 1 : 0); - for (int j=0; jwriteByte(fh, tracks[i].trackData[j])) { - return false; - } + if (lseek(fdout, tracks[i].startingBlock * 512, SEEK_SET) == -1) { + fprintf(stderr, "Failed to seek before writing track\n"); + return false; } + // Technically, we only have this many bytes to write: + // uint32_t writeSize = (tracks[i].bitCount / 8) + ((tracks[i].bitCount % 8) ? 1 : 0); + // ... but in practice, the tracks are all padded to NIBTRACKSIZE bytes; + // and we alloc'd a buffer of that size, too; so write the whole thing, + // since it would have been calloc'd initially. + ssize_t numWritten = write(fdout, tracks[i].trackData, NIBTRACKSIZE); + if (numWritten != NIBTRACKSIZE) { + fprintf(stderr, "Failed to write track [%ld]\n", numWritten); + perror("error writing"); + return false; + } +#if 0 uint8_t c = 0; while (writeSize < tracks[i].blockCount * 512) { - if (!write8(fh, c)) + if (write(fdout, &c, 1) != 1) return false; writeSize++; } +#endif } } return true; } -bool Woz::decodeWozTrackToNib(uint8_t track, nibSector sectorData[16]) +bool Woz::decodeWozTrackToNib(uint8_t phystrack, nibSector sectorData[16]) { for (int sector=0; sector<16; sector++) { - if (!readSectorData(track, sector, (nibSector *)(§orData[sector]))) { + if (!readNibSectorData(phystrack, sector, (nibSector *)(§orData[sector]))) { return false; } } @@ -1055,20 +1333,35 @@ bool Woz::decodeWozTrackToNib(uint8_t track, nibSector sectorData[16]) return true; } -bool Woz::decodeWozTrackToDsk(uint8_t track, uint8_t subtype, uint8_t sectorData[256*16]) +bool Woz::decodeWozTrackToDsk(uint8_t phystrack, uint8_t subtype, uint8_t sectorData[256*16]) { // First read it to a NIB; then convert the NIB to a DSK. nibSector nibData[16]; - if (!decodeWozTrackToNib(track, nibData)) + if (!decodeWozTrackToNib(phystrack, nibData)) return false; - if (denibblizeTrack((const uint8_t *)nibData, sectorData, subtype, track) != errorNone) + if (denibblizeTrack((const uint8_t *)nibData, sectorData, subtype, phystrack) != errorNone) return false; return true; } -#ifndef TEENSYDUINO +bool Woz::checksumWozDataTrack(uint8_t datatrack, uint32_t *retCRC) +{ + if (!retCRC) + return false; + + if (!tracks[datatrack].trackData) { + *retCRC = 0; + return false; + } + + *retCRC = compute_crc_32(tracks[datatrack].trackData, tracks[datatrack].bitCount/8); + return true; +} + + + void Woz::dumpInfo() { printf("WOZ image version %d\n", di.version); @@ -1137,120 +1430,161 @@ void Woz::dumpInfo() printf("\n"); } - printf("Quarter-track map:\n"); - for (int i=0; i<140; i+=4) { - printf("%2d %3d => %3d %3d => %3d %3d => %3d %3d => %3d\n", - i/4, - i, quarterTrackMap[i], - i+1, quarterTrackMap[i+1], - i+2, quarterTrackMap[i+2], - i+3, quarterTrackMap[i+3]); + if (dumpflags & DUMP_QTMAP) { + printf("Quarter-track map:\n"); + for (int i=0; i<140; i+=4) { + printf("%2d %3d => %3d %3d => %3d %3d => %3d %3d => %3d\n", + i/4, + i, quarterTrackMap[i], + i+1, quarterTrackMap[i+1], + i+2, quarterTrackMap[i+2], + i+3, quarterTrackMap[i+3]); + } + } + + if (dumpflags & DUMP_QTCRC) { + printf("Woz internal quarter-track CRCs:\n"); + // Dump the CRC32 for each Woz quarter-track + for (int i=0 ;i<160; i++) { + uint32_t crc=0; + checksumWozDataTrack(i, &crc); + printf("Woz track %d CRC32: 0x%X\n", i, crc); + } } - for (int i=0; i<40; i++) { - printf("Track %d:\n", i); - if (di.version == 1) { - printf(" Starts at byte %d\n", tracks[i].startingByte); - } else { - printf(" Starts at block %d\n", tracks[i].startingBlock); - } - printf(" Number of blocks: %d\n", tracks[i].blockCount); - printf(" Number of bits: %d\n", tracks[i].bitCount); - if (tracks[i].bitCount && tracks[i].trackData) { -#if 1 - // Raw track dump - printf(" Raw track data:\n"); - for (int k=0; k<(tracks[i].bitCount/8)+((tracks[i].bitCount%8)?1:0); k+=16) { - printf(" 0x%.4X :", k); - for (int j=0; j<16; j++) { - if (k+j < (tracks[i].bitCount/8)+((tracks[i].bitCount%8)?1:0)) { - printf(" %.2X", tracks[i].trackData[k+j]); - } - } - printf("\n"); + if (dumpflags & DUMP_TRACK) { + for (int i=0; i<40; i++) { + printf("Track %d:\n", i); + if (di.version == 1) { + printf(" Starts at byte %d\n", tracks[i].startingByte); + } else { + printf(" Starts at block %d\n", tracks[i].startingBlock); } - -#else - // Sector parsing & dump -#if 1 - // Look at the sectors in numerical order - // FIXME: 13-sector support - nibSector sectorData; - for (int sector=0; sector<16; sector++) { - if (readSectorData(i, sector, §orData)) { - printf(" Volume ID: %d\n", de44(sectorData.volume44)); - printf(" Track ID: %d\n", de44(sectorData.track44)); - uint8_t sector = de44(sectorData.sector44); - printf(" Sector: %d\n", sector); - printf(" Cksum: %d\n", de44(sectorData.checksum44)); - - printf(" Sector Data:\n"); - for (int k=0; k<342; k+=16) { + printf(" Number of blocks: %d\n", tracks[i].blockCount); + printf(" Number of bits: %d\n", tracks[i].bitCount); + if (tracks[i].bitCount && tracks[i].trackData) { + if (dumpflags & DUMP_RAWTRACK) { + // Raw track dump + printf(" Raw track data:\n"); + for (int k=0; k<(tracks[i].bitCount/8)+((tracks[i].bitCount%8)?1:0); k+=16) { printf(" 0x%.4X :", k); for (int j=0; j<16; j++) { - if (k+j < 342) { - printf(" %.2X", sectorData.data62[k+j]); + if (k+j < (tracks[i].bitCount/8)+((tracks[i].bitCount%8)?1:0)) { + printf(" %.2X", tracks[i].trackData[k+j]); } } printf("\n"); } } - } -#else - // Look at the sectors found in order on the track - trackBitIdx = 0x80; trackPointer = 0; trackLoopCounter = 0; - uint16_t sectorsFound = 0; - do { - if (nextDiskByte(i) == 0xD5 && - nextDiskByte(i) == 0xAA && - nextDiskByte(i) == 0x96) { - printf(" Volume ID: %d\n", denib(nextDiskByte(i), nextDiskByte(i))); - printf(" Track ID: %d\n", denib(nextDiskByte(i), nextDiskByte(i))); - uint8_t sector = denib(nextDiskByte(i), nextDiskByte(i)); - printf(" Sector: %d\n", sector); - sectorsFound |= (1 << sector); - printf(" Cksum: %d\n", denib(nextDiskByte(i), nextDiskByte(i))); - - nextDiskByte(i); // skip epilog - nextDiskByte(i); - nextDiskByte(i); - // look for data prolog d5 aa ad - while (nextDiskByte(i) != 0xD5 && trackLoopCounter < 2) - ; - if (trackLoopCounter < 2) { - // Hope that's it and skip two bytes - nextDiskByte(i); - nextDiskByte(i); - // Dump the 6-and-2 data - printf(" Sector Data:\n"); - for (int k=0; k<342; k+=16) { - printf(" 0x%.4X :", k); - for (int j=0; j<16; j++) { - if (k+j < 342) { - printf(" %.2X", nextDiskByte(i)); + + if (dumpflags & DUMP_SECTOR) { + printf(" Sector dump:\n"); + // Look at the sectors in numerical order + // FIXME: 13-sector support + nibSector sectorData; + for (int sector=0; sector<16; sector++) { + if (readNibSectorData(i, sector, §orData)) { + printf(" Volume ID: %d\n", de44(sectorData.volume44)); + printf(" Track ID: %d\n", de44(sectorData.track44)); + uint8_t sector = de44(sectorData.sector44); + printf(" Sector: %d\n", sector); + printf(" Cksum: %d\n", de44(sectorData.checksum44)); + + printf(" Sector Data:\n"); + for (int k=0; k<342; k+=16) { + printf(" 0x%.4X :", k); + for (int j=0; j<16; j++) { + if (k+j < 342) { + printf(" %.2X", sectorData.data62[k+j]); + } } + printf("\n"); } - printf("\n"); } } } + } + + if (dumpflags & DUMP_TOFILE) { + // Dump each sector to a file for analysis + uint8_t sectorData[256*16]; + decodeWozTrackToDsk(quarterTrackMap[i*4], + T_DSK, + sectorData); + for (int j=0; j<16; j++) { + char buf[25]; + sprintf(buf, "t%ds%d", i, j); + int fdout = open(buf, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); + write(fdout, §orData[256*j], 256); + close(fdout); + } + } - } while (sectorsFound != 0xFFFF && trackLoopCounter < 2); -#endif -#endif + if (dumpflags & DUMP_ORDEREDSECTOR) { +#define denib(a, b) ((((a) & ~0xAA) << 1) | ((b) & ~0xAA)) + printf(" Track-ordered sector dump:\n"); + // Look at the sectors found in order on the track + trackBitIdx = 0x80; trackPointer = 0; trackLoopCounter = 0; + uint16_t sectorsFound = 0; + do { + if (nextDiskByte(i) == 0xD5 && + nextDiskByte(i) == 0xAA && + nextDiskByte(i) == 0x96) { + printf(" Volume ID: %d\n", denib(nextDiskByte(i), nextDiskByte(i))); + printf(" Track ID: %d\n", denib(nextDiskByte(i), nextDiskByte(i))); + uint8_t sector = denib(nextDiskByte(i), nextDiskByte(i)); + printf(" Sector: %d\n", sector); + sectorsFound |= (1 << sector); + printf(" Cksum: %d\n", denib(nextDiskByte(i), nextDiskByte(i))); + + nextDiskByte(i); // skip epilog + nextDiskByte(i); + nextDiskByte(i); + // look for data prolog d5 aa ad + while (nextDiskByte(i) != 0xD5 && trackLoopCounter < 2) + ; + if (trackLoopCounter < 2) { + // Hope that's it and skip two bytes + nextDiskByte(i); + nextDiskByte(i); + // Dump the 6-and-2 data + printf(" Sector Data:\n"); + for (int k=0; k<342; k+=16) { + printf(" 0x%.4X :", k); + for (int j=0; j<16; j++) { + if (k+j < 342) { + printf(" %.2X", nextDiskByte(i)); + } + } + printf("\n"); + } + } + } + + } while (sectorsFound != 0xFFFF && trackLoopCounter < 2); + } } } } -#endif bool Woz::isSynchronized() { return di.synchronized; } -uint8_t Woz::trackNumberForQuarterTrack(uint16_t qt) +uint8_t Woz::dataTrackNumberForQuarterTrack(uint16_t qt) { return quarterTrackMap[qt]; } - +#if 1 +bool Woz::flush() +{ + if (trackDirty) { + printf("Hackily writing /tmp/auto.woz\n"); + trackDirty = false; + return writeFile("/tmp/auto.woz"); // FIXME: debugging + } + return true; +} +#endif diff --git a/apple/woz.h b/apple/woz.h index ffe3fda..5f937e0 100644 --- a/apple/woz.h +++ b/apple/woz.h @@ -8,6 +8,16 @@ #include "nibutil.h" #include "disktypes.h" +#define DUMP_TRACK 0x01 +#define DUMP_QTMAP 0x02 +#define DUMP_QTCRC 0x04 +// these all require DUMP_TRACK: +#define DUMP_TOFILE 0x10 +#define DUMP_SECTOR 0x20 +#define DUMP_RAWTRACK 0x40 +#define DUMP_ORDEREDSECTOR 0x80 + + typedef struct _diskInfo { uint8_t version; // Woz format version # uint8_t diskType; // 1 = 5.25"; 2 = 3.5" @@ -33,57 +43,72 @@ typedef struct _trackInfo { class Woz { public: - Woz(); + Woz(bool verbose, uint8_t dumpflags); ~Woz(); bool readFile(const char *filename, bool preloadTracks, uint8_t forceType = T_AUTO); - bool writeFile(uint8_t version, const char *filename); + bool writeFile(const char *filename, uint8_t forceType = T_AUTO); - uint8_t getNextWozBit(uint8_t track); - uint8_t nextDiskBit(uint8_t track); - uint8_t nextDiskByte(uint8_t track); + uint8_t getNextWozBit(uint8_t datatrack); - bool decodeWozTrackToNib(uint8_t track, nibSector sectorData[16]); - bool decodeWozTrackToDsk(uint8_t track, uint8_t subtype, uint8_t sectorData[256*16]); - -#ifndef TEENSYDUINO void dumpInfo(); -#endif bool isSynchronized(); - uint8_t trackNumberForQuarterTrack(uint16_t qt); + uint8_t dataTrackNumberForQuarterTrack(uint16_t qt); + + bool flush(); + + //protected: + // Interface for AiiE + bool writeNextWozBit(uint8_t datatrack, uint8_t bit); + bool writeNextWozByte(uint8_t datatrack, uint8_t b); + uint8_t nextDiskBit(uint8_t datatrack); + uint8_t nextDiskByte(uint8_t datatrack); + bool skipByte(uint8_t datatrack); private: bool readWozFile(const char *filename, bool preloadTracks); bool readDskFile(const char *filename, bool preloadTracks, uint8_t subtype); bool readNibFile(const char *filename, bool preloadTracks); + bool decodeWozTrackToNib(uint8_t phystrack, nibSector sectorData[16]); + bool decodeWozTrackToDsk(uint8_t phystrack, uint8_t subtype, uint8_t sectorData[256*16]); + + bool writeWozFile(const char *filename, uint8_t subtype); + bool writeDskFile(const char *filename, uint8_t subtype); + bool writeNibFile(const char *filename); + uint8_t fakeBit(); - bool parseTRKSChunk(int8_t fh, uint32_t chunkSize); - bool parseTMAPChunk(int8_t fh, uint32_t chunkSize); - bool parseInfoChunk(int8_t fh, uint32_t chunkSize); - bool parseMetaChunk(int8_t fh, uint32_t chunkSize); + bool parseTRKSChunk(uint32_t chunkSize); + bool parseTMAPChunk(uint32_t chunkSize); + bool parseInfoChunk(uint32_t chunkSize); + bool parseMetaChunk(uint32_t chunkSize); - bool writeInfoChunk(uint8_t version, int8_t fh); - bool writeTMAPChunk(uint8_t version, int8_t fh); - bool writeTRKSChunk(uint8_t version, int8_t fh); + bool writeInfoChunk(uint8_t version, int fdout); + bool writeTMAPChunk(uint8_t version, int fdout); + bool writeTRKSChunk(uint8_t version, int fdout); - bool readQuarterTrackData(int8_t fh, uint8_t quartertrack); - bool readWozTrackData(int8_t fh, uint8_t wt); - bool readSectorData(uint8_t track, uint8_t sector, nibSector *sectorData); + bool readWozDataTrack(uint8_t datatrack); + bool readNibSectorData(uint8_t phystrack, uint8_t sector, nibSector *sectorData); - bool readAndDecodeTrack(uint8_t track, int8_t fh); + bool loadMissingTrackFromImage(uint8_t datatrack); + + bool checksumWozDataTrack(uint8_t datatrack, uint32_t *retCRC); void _initInfo(); - protected: + private: + int fd; uint8_t imageType; + + bool verbose; + uint8_t dumpflags; - int8_t fh; bool autoFlushTrackData; - + bool trackDirty; + uint8_t quarterTrackMap[40*4]; diskInfo di; trackInfo tracks[160]; diff --git a/filemanager.h b/filemanager.h index 2147c3f..25f826d 100644 --- a/filemanager.h +++ b/filemanager.h @@ -96,6 +96,8 @@ class FileManager { virtual int8_t openFile(const char *name) = 0; virtual void closeFile(int8_t fd) = 0; + virtual void truncate(int8_t fd) = 0; + virtual const char *fileName(int8_t fd) = 0; virtual int8_t readDir(const char *where, const char *suffix, char *outputFN, int8_t startIdx, uint16_t maxlen) = 0; diff --git a/nix/nix-filemanager.cpp b/nix/nix-filemanager.cpp index 386abe0..a67b248 100644 --- a/nix/nix-filemanager.cpp +++ b/nix/nix-filemanager.cpp @@ -56,6 +56,12 @@ void NixFileManager::closeFile(int8_t fd) cachedNames[fd][0] = '\0'; } +void NixFileManager::truncate(int8_t fd) +{ + FILE *f = fopen(cachedNames[fd], "w+"); + fclose(f); +} + const char *NixFileManager::fileName(int8_t fd) { if (fd < 0 || fd >= numCached) diff --git a/nix/nix-filemanager.h b/nix/nix-filemanager.h index c0b7c04..c6578fc 100644 --- a/nix/nix-filemanager.h +++ b/nix/nix-filemanager.h @@ -12,6 +12,8 @@ class NixFileManager : public FileManager { virtual int8_t openFile(const char *name); virtual void closeFile(int8_t fd); + virtual void truncate(int8_t fd); + virtual const char *fileName(int8_t fd); virtual int8_t readDir(const char *where, const char *suffix, char *outputFN, int8_t startIdx, uint16_t maxlen); diff --git a/sdl/aiie.cpp b/sdl/aiie.cpp index db854f7..65ad13e 100644 --- a/sdl/aiie.cpp +++ b/sdl/aiie.cpp @@ -52,6 +52,8 @@ void sigint_handler(int n) { // If we want control-C to reset the machine, then set this here... // send_rst = 1; + + ((AppleVM*)g_vm)->disk6->disk[0]->dumpInfo(); } void nonblock(int state) diff --git a/sdl/sdl-speaker.cpp b/sdl/sdl-speaker.cpp index a7ae0f2..490f9c0 100644 --- a/sdl/sdl-speaker.cpp +++ b/sdl/sdl-speaker.cpp @@ -104,7 +104,7 @@ void SDLSpeaker::begin() audioDevice.userdata = NULL; memset(&soundBuf[0], 0, SDLSIZE); - bufIdx = SDLSIZE/2; + bufIdx = SDLSIZE/2; // FIXME: why? Shouldn't this just be 0? SDL_OpenAudio(&audioDevice, &audioActual); // FIXME retval printf("Actual: freq %d channels %d samples %d\n",