Epple-II/src/wozfile.cpp

368 lines
13 KiB
C++
Raw Normal View History

2018-12-16 04:39:08 +00:00
/*
epple2
Copyright © 2018, Christopher Alan Mosher, Shelton, CT, USA. <cmosher01@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY, without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "wozfile.h"
#include <istream>
#include <ostream>
#include <fstream>
WozFile::WozFile() : lastQuarterTrack(0) {
2018-12-16 04:39:08 +00:00
unload();
}
WozFile::~WozFile() {
}
bool WozFile::load(const std::string& filePath) {
std::ifstream in(filePath.c_str(),std::ios::binary|std::ios::in);
if (!in.is_open()) {
printf("Error opening file: %d\n", errno);
return false;
}
if (isLoaded()) {
unload();
}
std::uint32_t woz1;
in.read((char*)&woz1, sizeof(woz1));
if (woz1 != 0x315A4F57u) {
printf("WOZ1 magic bytes missing.");
throw "WOZ1 magic bytes missing";
}
printf("WOZ1 magic bytes present\n");
std::uint32_t sanity;
in.read((char*)&sanity, sizeof(sanity));
if (sanity != 0x0A0D0AFFu) {
printf("FF 0A 0D 0A bytes corrupt.\n");
throw "FF 0A 0D 0A bytes corrupt";
}
std::uint32_t crc_given;
in.read((char*)&crc_given, sizeof(crc_given));
printf("Read given CRC: %08x\n", crc_given);
// TODO verify CRC
std::uint32_t chunk_id;
std::uint32_t chunk_size;
bool five_25(false);
while (in.read((char*)&chunk_id, sizeof(chunk_id))) {
in.read((char*)&chunk_size, sizeof(chunk_size));
printf("Chunk %.4s of size 0x%08x\n", (char*)&chunk_id, chunk_size);
switch (chunk_id) {
case 0x4F464E49: { // INFO
std::uint8_t* buf = new std::uint8_t[chunk_size];
in.read((char*)buf, chunk_size);
printf("INFO version %d\n", *buf);
five_25 = (buf[1]==1);
printf("Disk type: %s\n", five_25 ? "5.25" : buf[1]==2 ? "3.5" : "?");
writable = !(buf[2]==1);
printf("Write protected?: %s\n", writable ? "No" : "Yes");
printf("Imaged with cross-track sync?: %s\n", buf[3]==1 ? "Yes" : "No");
printf("MC3470 fake bits removed?: %s\n", buf[4]==1 ? "Yes" : "No");
printf("Creator: \"%.32s\"\n", buf+5);
delete[] buf;
}
break;
case 0x50414D54: { // TMAP
std::uint8_t* buf = new std::uint8_t[chunk_size];
in.read((char*)buf, chunk_size);
if (!five_25) {
printf("Can only handle 5.25 floppy disk images.\n");
throw "Can only handle 5.25 floppy disk images.";
} else {
printf("\x1b[31;47m-------------------------------------------\x1b[0m\n");
std::uint8_t i(0);
for (std::uint16_t t(0); t <= 3500; t += 25) {
if (buf[i] == 0xFFu) {
printf("\x1b[31;47m");
}
if (t % 100) {
printf("TMAP track 0x%02X +.%02d: TRKS track index 0x%02X", t/100, t%100, buf[i++]);
} else {
printf("TMAP track 0x%02X : TRKS track index 0x%02X", t/100, buf[i++]);
}
printf("\x1b[0m\n");
}
printf("\x1b[31;47m-------------------------------------------\x1b[0m\n");
for (std::uint8_t qt(0); qt <= 140; ++qt) {
tmap[qt] = buf[qt];
}
}
delete[] buf;
}
break;
case 0x534B5254: { // TRKS
std::uint8_t* buf = new std::uint8_t[chunk_size];
in.read((char*)buf, chunk_size);
if (chunk_size % 6656) {
printf("chunk size is not an even multiple of 6656.");
}
c_trks = chunk_size / 6656;
printf("Count of tracks: 0x%02X\n", c_trks);
if (c_trks > 141) {
printf("Error: cannot handle more than 141 tracks.");
throw "Error: cannot handle more than 141 tracks";
}
for (std::uint8_t t(0); t < c_trks; ++t) {
printf("track 0x%02X:\n", t);
std::uint16_t usedBytes = *(std::uint16_t*)&buf[t*6656+6646+0];
printf(" used bytes: 0x%0X\n", usedBytes);
trk_bits[t] = *(std::uint16_t*)&buf[t*6656+6646+2];
printf(" count of bits: 0x%0X\n", trk_bits[t]);
std::uint16_t spliceBit = *(std::uint16_t*)&buf[t*6656+6646+4];
if (spliceBit == 0xFFFFu) {
printf(" no splice information exists\n");
} else {
printf(" bit after splice point: 0x%0X\n", spliceBit);
}
std::uint8_t spliceNib = *(std::uint8_t*)&buf[t*6656+6646+6];
printf(" Nibble value to use for splice: 0x%0X\n", spliceNib);
std::uint8_t cSpliceBit = *(std::uint8_t*)&buf[t*6656+6646+7];
printf(" Bit count of splice nibble: 0x%0X\n", cSpliceBit);
std::uint8_t *base = (std::uint8_t*)&buf[t*6656+0];
std::uint8_t *pd = base;
printf(" beginning of data: 0x: ");
for (int cd(0); cd < 32; ++cd) {
printf("%02X", *pd++);
}
printf("\n");
for (int i(0); i < 6646; ++i) {
trks[t][i] = *base++;
}
}
delete[] buf;
}
break;
case 0x4154454D: { // META
std::uint8_t* buf = new std::uint8_t[chunk_size];
in.read((char*)buf, chunk_size);
std::uint32_t i(0);
char* pc((char*)buf);
while (i++ < chunk_size) {
if (*pc == '\t') {
printf(": ");
} else {
printf("%c", *pc);
}
pc++;
}
delete[] buf;
}
break;
default: { // unknown type of chunk; safely skip past it and ignore it
in.seekg(chunk_size, in.cur);
}
break;
}
}
in.close();
this->filePath = filePath;
checkForWriteProtection();
this->loaded = true;
this->modified = false;
return true;
}
void WozFile::checkForWriteProtection() {
if (!this->writable) {
return;
}
std::ofstream outf(filePath.c_str(),std::ios::binary|std::ios::app);
this->writable = outf.is_open();
outf.close();
}
void WozFile::save() {
if (isWriteProtected() || !isLoaded()) {
2018-12-16 04:39:08 +00:00
return;
}
// std::ofstream out(filePath.c_str(),std::ios::binary);
// TODO SAVE FILE!
// out.flush();
// out.close();
// this->modified = false;
}
void WozFile::unload() {
this->bit = 0x80u;
this->byt = 0x00u;
2018-12-16 04:39:08 +00:00
this->writable = true;
this->loaded = false;
this->filePath = "";
this->modified = false;
}
static std::uint8_t bc(std::uint8_t bit) {
switch (bit) {
case 0x80u: return 0u;
case 0x40u: return 1u;
case 0x20u: return 2u;
case 0x10u: return 3u;
case 0x08u: return 4u;
case 0x04u: return 5u;
case 0x02u: return 6u;
case 0x01u: return 7u;
}
return 255u; // should never happen
}
static std::uint8_t cb(std::uint8_t bit) {
switch (bit) {
case 0u: return 0x80u;
case 1u: return 0x40u;
case 2u: return 0x20u;
case 3u: return 0x10u;
case 4u: return 0x08u;
case 5u: return 0x04u;
case 6u: return 0x02u;
case 7u: return 0x01u;
}
return 255u; // should never happen
}
static void dumpQTrack(std::uint8_t currentQuarterTrack) {
if (currentQuarterTrack % 4) {
const std::uint8_t hundredths((currentQuarterTrack%4) * 25);
printf(" Reading from <---------- track 0x%02X +.%02d\n", currentQuarterTrack/4, hundredths);
} else {
printf(" Reading from <---------- track 0x%02X\n", currentQuarterTrack/4);
}
}
/*
* Rotate the floppy disk by one bit.
* In real life we don't care what track we're one, but for the
* emulator we need to know. This is because the tracks within the
* WOZ file could be different lengths. So in order to know when
* we need to loop back to the beginning of the track (circular
* track on the floppy), we need to know the actual bit length
* of that track in our WOZ file.
*/
void WozFile::rotateOneBit(std::uint8_t currentQuarterTrack) {
if (!isLoaded()) {
return; // there's no disk to rotate
}
// In WOZ track image, bits (i.e., magnetic field reversal on disk,
// or lack thereof) are packed into bytes, high bit to low bit.
// Really, it's the stream of bits returned by the MC3470 (but also
// possibly with random bits zeroed out).
// std::uint16_t before = (this->byt*8+bc(this->bit));
// printf("disk at bit: %d\n", this->byt*8+bc(this->bit));
// Move to next bit:
this->bit >>= 1;
// If we hit end of this byte, move on to beginning of next byte
if (this->bit == 0) {
++this->byt;
this->bit = 0x80u;
}
if (this->tmap[currentQuarterTrack] == 0xFFu) {
return;
}
if (currentQuarterTrack != this->lastQuarterTrack) {
double oldLen = this->trk_bits[this->tmap[this->lastQuarterTrack]];
double newLen = this->trk_bits[this->tmap[currentQuarterTrack]];
double dif = newLen/oldLen;
if (dif < -0.000001 || 0.000001 < dif) {
// dumpQTrack(currentQuarterTrack);
// printf(" new track: bit pos: %d ", this->byt*8+bc(this->bit));
std::uint16_t newBit = (this->byt*8+bc(this->bit)) * dif;
this->byt = newBit / 8;
this->bit = cb(newBit % 8);
// printf("--> %d\n\n", this->byt*8+bc(this->bit));
}
this->lastQuarterTrack = currentQuarterTrack;
}
// Check for hitting the end of our track image,
// and if so, move back to the beginning.
// This is how we emulate a circular track on the floppy.
if (this->trk_bits[this->tmap[currentQuarterTrack]] <= this->byt*8+bc(this->bit)) {
// printf("\n<rewinding track here>\n");
this->byt = 0;
this->bit = 0x80u;
}
// std::uint16_t after = (this->byt*8+bc(this->bit));
// if (!(after % 0x100u)) {
// printf("\nnow at bit %04x\n", after);
// }
// if (after != before+1) {
// printf("\nbit changing from %04x to %04x\n", before, after);
// }
}
bool WozFile::getBit(std::uint8_t currentQuarterTrack) {
if (!isLoaded()) {
printf("\nNO DISK TO READ FROM (will generate random data)\n");
return false; // there's no disk, so no pulse
}
if (this->tmap[currentQuarterTrack] == 0xFFu) {
// printf("\nreading (random) from empty q-track: %d\n", currentQuarterTrack);
return false; // track doesn't exist
}
if (this->c_trks <= this->tmap[currentQuarterTrack]) { // shouldn't happen
printf("\nBAD TRACK quarterTrack %d mapped to TRKS index %d (count of tracks: %d)\n", currentQuarterTrack, this->tmap[currentQuarterTrack], this->c_trks);
return false; // track doesn't exist
}
// if (!(this->byt % 128) && this->bit == 0x01) {
// printf("\ngetBit--> ");
// }
// printf("%02x", this->byt*8+bc(this->bit));
return this->trks[this->tmap[currentQuarterTrack]][this->byt] & this->bit;
}
void WozFile::setBit(std::uint8_t currentQuarterTrack) {
if (!isLoaded()) {
return; // there's no disk to write data to
}
if (!this->writable) {
return; // write-protected
}
if (this->c_trks <= this->tmap[currentQuarterTrack]) { // shouldn't happen
return; // TODO track doesn't exist: create a new one
}
if (this->tmap[currentQuarterTrack] == 0xFFu) {
// track does not exist, create new one
}
// TODO extend track length if needed
this->trks[this->tmap[currentQuarterTrack]][this->byt] |= this->bit;
}