mirror of
https://github.com/cmosher01/Epple-II.git
synced 2025-01-03 01:33:45 +00:00
expand and reduce track image in memory
This commit is contained in:
parent
97752f06ff
commit
a1bb8b9dd8
@ -83,7 +83,7 @@ void StepperMotor::setMagnet(const unsigned char magnet, const bool on) {
|
||||
this->mags &= ~mask;
|
||||
}
|
||||
|
||||
// const std::uint8_t oldQT = this->quarterTrack;
|
||||
const std::uint8_t oldQT = this->quarterTrack;
|
||||
|
||||
const char newPos = mapMagPos[this->mags];
|
||||
char d = 0;
|
||||
@ -91,6 +91,20 @@ void StepperMotor::setMagnet(const unsigned char magnet, const bool on) {
|
||||
d = calcDeltaPos(this->pos,newPos);
|
||||
this->pos = newPos;
|
||||
|
||||
// TODO: delay moving arm by a small amount
|
||||
// For example, Locksmith, in order to write "quarter tracks" (i.e., T+.25 or T+.75), it positions
|
||||
// to the correct track (by turning two adjacent magnets on), then turns them both off in rapid
|
||||
// succession. In real life the arm doesn't move in such a case. In order to emulate that, we need
|
||||
// to delay the arm move for a bit, to see if the magnets change in the meantime.
|
||||
/*
|
||||
* ARM: ph2 + [..*.] T$0D.00 +0.00
|
||||
* ARM: ph3 + [..**] T$0D.25 --> +0.25
|
||||
* switching from tmap[34] --> [35]
|
||||
*
|
||||
* ARM: ph3 - [..*.] T$0D.00 <-- -0.25 <-\
|
||||
* switching from tmap[35] --> [34] <--\-- this needs to get delayed
|
||||
* ARM: ph2 - [....] T$0D.00 +0.00
|
||||
*/
|
||||
this->quarterTrack += d;
|
||||
if (this->quarterTrack < 0)
|
||||
this->quarterTrack = 0;
|
||||
@ -98,18 +112,18 @@ void StepperMotor::setMagnet(const unsigned char magnet, const bool on) {
|
||||
this->quarterTrack = QTRACKS-1;
|
||||
}
|
||||
|
||||
// const std::uint8_t newQT = this->quarterTrack;
|
||||
// const std::int8_t deltaQT = newQT - oldQT;
|
||||
const std::uint8_t newQT = this->quarterTrack;
|
||||
const std::int8_t deltaQT = newQT - oldQT;
|
||||
|
||||
// printf("ARM: ph%d %s [%c%c%c%c] T$%02X.%02d %s %+0.2f\n",
|
||||
// (std::uint8_t)magnet,
|
||||
// on ? "+" : "-",
|
||||
// (mags&1)?'*':'.',
|
||||
// (mags&2)?'*':'.',
|
||||
// (mags&4)?'*':'.',
|
||||
// (mags&8)?'*':'.',
|
||||
// this->quarterTrack / 4,
|
||||
// (this->quarterTrack % 4) * 25,
|
||||
// deltaQT>0 ? "-->" : deltaQT<0 ? "<--" : " ",
|
||||
// (deltaQT % 4) / 4.0);
|
||||
printf("ARM: ph%d %s [%c%c%c%c] T$%02X.%02d %s %+0.2f\n",
|
||||
(std::uint8_t)magnet,
|
||||
on ? "+" : "-",
|
||||
(mags&1)?'*':'.',
|
||||
(mags&2)?'*':'.',
|
||||
(mags&4)?'*':'.',
|
||||
(mags&8)?'*':'.',
|
||||
this->quarterTrack / 4,
|
||||
(this->quarterTrack % 4) * 25,
|
||||
deltaQT>0 ? "-->" : deltaQT<0 ? "<--" : " ",
|
||||
(deltaQT % 4) / 4.0);
|
||||
}
|
||||
|
306
src/wozfile.cpp
306
src/wozfile.cpp
@ -58,8 +58,61 @@ static void print_compat(std::uint16_t compat, std::uint16_t mask, const char *n
|
||||
}
|
||||
}
|
||||
|
||||
void WozFile::dumpTmap() {
|
||||
printf("\x1b[31;47m-------------------------------------------------\x1b[0m\n");
|
||||
for (std::uint8_t qt(0); qt < C_QTRACK; ++qt) {
|
||||
const std::uint16_t t(qt*25);
|
||||
if (this->tmap[qt] == 0xFFu) {
|
||||
printf("\x1b[31;47m");
|
||||
}
|
||||
if (t % 100) {
|
||||
printf("TMAP[0x%02X] track 0x%02X +.%02d: TRKS track index 0x%02X", qt, t/100, t%100, this->tmap[qt]);
|
||||
} else {
|
||||
printf("TMAP[0x%02X] track 0x%02X : TRKS track index 0x%02X", qt, t/100, this->tmap[qt]);
|
||||
}
|
||||
printf("\x1b[0m");
|
||||
if (qt == this->initialQtrack && qt == this->finalQtrack) {
|
||||
printf(" <-- lone track");
|
||||
} else if (qt == this->initialQtrack) {
|
||||
printf(" <-- initial track");
|
||||
} else if (qt == this->finalQtrack) {
|
||||
printf(" <-- final track");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\x1b[31;47m-------------------------------------------------\x1b[0m\n");
|
||||
}
|
||||
|
||||
void WozFile::dumpTracks() {
|
||||
for (std::uint8_t qt(0); qt < C_QTRACK; ++qt) {
|
||||
if (this->trk[qt]) {
|
||||
printf("TRK index %02X: %08x bytes; %08x bits ", qt, this->trk_byts[qt], this->trk_bits[qt]);
|
||||
printf("("
|
||||
BYTE_TO_BINARY_PATTERN
|
||||
BYTE_TO_BINARY_PATTERN
|
||||
BYTE_TO_BINARY_PATTERN
|
||||
BYTE_TO_BINARY_PATTERN
|
||||
BYTE_TO_BINARY_PATTERN
|
||||
BYTE_TO_BINARY_PATTERN
|
||||
BYTE_TO_BINARY_PATTERN
|
||||
BYTE_TO_BINARY_PATTERN
|
||||
"...)\n",
|
||||
BYTE_TO_BINARY(this->trk[qt][0]),
|
||||
BYTE_TO_BINARY(this->trk[qt][1]),
|
||||
BYTE_TO_BINARY(this->trk[qt][2]),
|
||||
BYTE_TO_BINARY(this->trk[qt][3]),
|
||||
BYTE_TO_BINARY(this->trk[qt][4]),
|
||||
BYTE_TO_BINARY(this->trk[qt][5]),
|
||||
BYTE_TO_BINARY(this->trk[qt][6]),
|
||||
BYTE_TO_BINARY(this->trk[qt][7]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WozFile::load(const std::string& filePath) {
|
||||
std::ifstream in(filePath.c_str(),std::ios::binary|std::ios::in);
|
||||
printf("Reading WOZ 2.0 file: %s\n", filePath.c_str());
|
||||
|
||||
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;
|
||||
@ -159,28 +212,7 @@ bool WozFile::load(const std::string& filePath) {
|
||||
printf("Could not find any final track (%02X).\n", this->finalQtrack);
|
||||
}
|
||||
|
||||
printf("\x1b[31;47m-------------------------------------------\x1b[0m\n");
|
||||
for (std::uint8_t qt(0); qt < chunk_size; ++qt) {
|
||||
const std::uint16_t t(qt*25);
|
||||
if (tmap[qt] == 0xFFu) {
|
||||
printf("\x1b[31;47m");
|
||||
}
|
||||
if (t % 100) {
|
||||
printf("TMAP track 0x%02X +.%02d: TRKS track index 0x%02X", t/100, t%100, tmap[qt]);
|
||||
} else {
|
||||
printf("TMAP track 0x%02X : TRKS track index 0x%02X", t/100, tmap[qt]);
|
||||
}
|
||||
printf("\x1b[0m");
|
||||
if (qt == this->initialQtrack && qt == this->finalQtrack) {
|
||||
printf(" <-- lone track");
|
||||
} else if (qt == this->initialQtrack) {
|
||||
printf(" <-- initial track");
|
||||
} else if (qt == this->finalQtrack) {
|
||||
printf(" <-- final track");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\x1b[31;47m-------------------------------------------\x1b[0m\n");
|
||||
dumpTmap();
|
||||
}
|
||||
break;
|
||||
case 0x534B5254: { // TRKS
|
||||
@ -264,21 +296,7 @@ bool WozFile::load(const std::string& filePath) {
|
||||
this->loaded = true;
|
||||
this->modified = false;
|
||||
|
||||
// TODO if the file is not write protected, then "fill out" our tracks:
|
||||
// 1. create new TRKS entries and full copies of duplicate TMAP pointers
|
||||
// 2. create new TRKS entries (and fill with zeroes) for FF entries in TMAP. (with length of avg of adjacent tracks?)
|
||||
//
|
||||
// ...
|
||||
// 6.50: FF x 08 - 00 00 00
|
||||
// 6.75: 02 \ 02 - d5 aa 96
|
||||
// 7.00: 02 - d5 aa 96 03 - d5 aa 96
|
||||
// 7.25: 02 / 04 - d5 aa 96
|
||||
// 7.50: FF x 09 - 00 00 00
|
||||
// 7.75: 05 \ 05 - d5 aa 96
|
||||
// 8.00: 05 - d5 aa 96 06 - d5 aa 96
|
||||
// 8.25: 05 / 07 - d5 aa 96
|
||||
// 8.50: FF x 0A - 00 00 00
|
||||
// ...
|
||||
expandTracks();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -297,6 +315,11 @@ void WozFile::save() {
|
||||
if (isWriteProtected() || !isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Saving WOZ 2.0 file: %s\n", filePath.c_str());
|
||||
|
||||
reduceTracks();
|
||||
|
||||
std::ofstream out(filePath.c_str(), std::ios::binary);
|
||||
|
||||
std::uint32_t woz2(0x325A4F57u);
|
||||
@ -340,6 +363,7 @@ void WozFile::save() {
|
||||
std::uint16_t ram(0);
|
||||
out.write((char*)&ram, sizeof(ram));
|
||||
|
||||
|
||||
std::uint16_t largest_track(0);
|
||||
for (std::uint8_t qt(0); qt < C_QTRACK; ++qt) {
|
||||
if (largest_track < (this->trk_byts[qt]>>9)) {
|
||||
@ -376,10 +400,16 @@ void WozFile::save() {
|
||||
uint16_t block(3);
|
||||
for (std::uint8_t qt(0); qt < C_QTRACK; ++qt) {
|
||||
struct trk_t ts;
|
||||
ts.blockFirst = block;
|
||||
ts.blockCount = (this->trk_byts[qt]>>9);
|
||||
block += ts.blockCount;
|
||||
ts.bitCount = this->trk_bits[qt];
|
||||
if (this->trk_byts[qt]) {
|
||||
ts.blockFirst = block;
|
||||
ts.blockCount = (this->trk_byts[qt]>>9);
|
||||
block += ts.blockCount;
|
||||
ts.bitCount = this->trk_bits[qt];
|
||||
} else {
|
||||
ts.blockFirst = 0;
|
||||
ts.blockCount = 0;
|
||||
ts.bitCount = 0;
|
||||
}
|
||||
out.write((char*)&ts, sizeof(ts));
|
||||
}
|
||||
|
||||
@ -393,8 +423,9 @@ void WozFile::save() {
|
||||
out.close();
|
||||
|
||||
this->modified = false;
|
||||
}
|
||||
|
||||
expandTracks();
|
||||
}
|
||||
void WozFile::unload() {
|
||||
this->bit = 0x80u;
|
||||
this->byt = 0x00u;
|
||||
@ -402,18 +433,13 @@ void WozFile::unload() {
|
||||
this->loaded = false;
|
||||
this->filePath = "";
|
||||
this->modified = false;
|
||||
for (int i(0); i < C_QTRACK; ++i) {
|
||||
removeTrack(i);
|
||||
}
|
||||
if (this->tmap) {
|
||||
delete [] this->tmap;
|
||||
this->tmap = 0;
|
||||
}
|
||||
for (int i(0); i < C_QTRACK; ++i) {
|
||||
if (this->trk[i]) {
|
||||
delete [] this->trk[i];
|
||||
this->trk[i] = 0;
|
||||
}
|
||||
this->trk_bits[i] = 0;
|
||||
this->trk_byts[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static std::uint8_t bc(std::uint8_t bit) {
|
||||
@ -485,6 +511,7 @@ void WozFile::rotateOneBit(std::uint8_t currentQuarterTrack) {
|
||||
// previous, based on each track's length (tracks can be of
|
||||
// different lengths in the WOZ image).
|
||||
if (currentQuarterTrack != this->lastQuarterTrack) {
|
||||
printf("switching from tmap[%02x] --> [%02x]\n", this->lastQuarterTrack, currentQuarterTrack);
|
||||
const double oldLen = this->trk_bits[this->tmap[this->lastQuarterTrack]];
|
||||
const double newLen = this->trk_bits[this->tmap[currentQuarterTrack]];
|
||||
const double ratio = newLen/oldLen;
|
||||
@ -521,6 +548,14 @@ bool WozFile::getBit(std::uint8_t currentQuarterTrack) {
|
||||
return this->trk[this->tmap[currentQuarterTrack]][this->byt] & this->bit;
|
||||
}
|
||||
|
||||
void WozFile::rawSet(std::uint8_t currentQuarterTrack, bool on) {
|
||||
if (on) {
|
||||
this->trk[this->tmap[currentQuarterTrack]][this->byt] |= this->bit;
|
||||
} else {
|
||||
this->trk[this->tmap[currentQuarterTrack]][this->byt] &= ~this->bit;
|
||||
}
|
||||
}
|
||||
|
||||
void WozFile::setBit(std::uint8_t currentQuarterTrack, bool on) {
|
||||
if (!isLoaded()) {
|
||||
return; // there's no disk to write data to
|
||||
@ -531,13 +566,170 @@ void WozFile::setBit(std::uint8_t currentQuarterTrack, bool on) {
|
||||
}
|
||||
|
||||
// printf("%c",(on?'1':'0')); fflush(stdout);
|
||||
if (on) {
|
||||
this->trk[this->tmap[currentQuarterTrack]][this->byt] |= this->bit;
|
||||
} else {
|
||||
this->trk[this->tmap[currentQuarterTrack]][this->byt] &= ~this->bit;
|
||||
rawSet(currentQuarterTrack, on);
|
||||
|
||||
|
||||
|
||||
// also write preceding and following quarter tracks (at relative position, and if they exist)
|
||||
if (0 < currentQuarterTrack) {
|
||||
rawSet(currentQuarterTrack-1, on);
|
||||
}
|
||||
if (currentQuarterTrack < C_QTRACK-1) {
|
||||
rawSet(currentQuarterTrack+1, on);
|
||||
}
|
||||
|
||||
this->modified = true;
|
||||
|
||||
// TODO: also write preceding and following quarter tracks (at relative position, and if they exist)
|
||||
}
|
||||
|
||||
void WozFile::removeTrack(const int trackIndex) {
|
||||
if (this->trk[trackIndex]) {
|
||||
delete [] this->trk[trackIndex];
|
||||
this->trk[trackIndex] = 0;
|
||||
}
|
||||
this->trk_bits[trackIndex] = 0;
|
||||
this->trk_byts[trackIndex] = 0;
|
||||
}
|
||||
|
||||
bool WozFile::trackIsZeroes(std::uint8_t qt) {
|
||||
for (std::uint16_t byt = 0; byt < this->trk_byts[qt]; ++byt) {
|
||||
if (this->trk[qt][byt]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WozFile::tracksAreIdentical(std::uint8_t qt1, std::uint8_t qt2) {
|
||||
if (this->trk_bits[qt1] != this->trk_bits[qt2]) {
|
||||
return false;
|
||||
}
|
||||
for (std::uint16_t byt = 0; byt < this->trk_byts[qt1]; ++byt) {
|
||||
if (this->trk[qt1][byt] != this->trk[qt2][byt]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// opposite of expandTracks()
|
||||
void WozFile::reduceTracks() {
|
||||
for (std::uint8_t qt(0); qt < C_QTRACK; ++qt) {
|
||||
if (trackIsZeroes(this->tmap[qt])) {
|
||||
removeTrack(this->tmap[qt]);
|
||||
this->tmap[qt] = 0xFFu;
|
||||
}
|
||||
}
|
||||
for (std::uint8_t qt(0); qt < C_QTRACK-1; ++qt) {
|
||||
for (std::uint8_t qto(qt+1); qto < C_QTRACK; ++qto) {
|
||||
if (this->tmap[qt] != 0xFFu && this->tmap[qto] != 0xFFu && this->tmap[qt] != this->tmap[qto]) {
|
||||
if (tracksAreIdentical(this->tmap[qt], this->tmap[qto])) {
|
||||
removeTrack(this->tmap[qto]);
|
||||
this->tmap[qto] = this->tmap[qt];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// kludge? remove track $22.25 if standard disk
|
||||
for (std::uint8_t qt(C_QTRACK-1); qt >= 0x89; --qt) {
|
||||
if (this->tmap[qt] != 0xFFu) {
|
||||
if (qt == 0x89) {
|
||||
if (this->tmap[qt] == this->tmap[qt-1]) {
|
||||
this->tmap[qt] = 0xFFu;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
dumpTmap();
|
||||
dumpTracks();
|
||||
}
|
||||
|
||||
static std::uint16_t bytesForBits(const std::uint32_t c_bits) {
|
||||
std::uint16_t c_bytes = (c_bits + 7) / 8;
|
||||
return static_cast<std::uint16_t>(c_bytes + 0x1FFu) / 0x200 * 0x200;
|
||||
}
|
||||
|
||||
void WozFile::expandTracks() {
|
||||
for (std::uint8_t qt(0); qt < C_QTRACK; ++qt) {
|
||||
if (this->tmap[qt] != 0xFFu) {
|
||||
for (std::uint8_t qto(qt-1); qto != 0xFFu; --qto) {
|
||||
if (this->tmap[qt] == this->tmap[qto]) {
|
||||
copyTrack(qt, qto);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
createNewTrack(qt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WozFile::copyTrack(std::uint8_t qt_dest, std::uint8_t qt_src) {
|
||||
for (std::uint8_t t(0); t < C_QTRACK; ++t) {
|
||||
if (!this->trk[t]) {
|
||||
this->tmap[qt_dest] = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->trk_bits[this->tmap[qt_dest]] = this->trk_bits[this->tmap[qt_src]];
|
||||
this->trk_byts[this->tmap[qt_dest]] = this->trk_byts[this->tmap[qt_src]];
|
||||
this->trk[this->tmap[qt_dest]] = new std::uint8_t[this->trk_byts[this->tmap[qt_dest]]];
|
||||
memcpy(this->trk[this->tmap[qt_dest]], this->trk[this->tmap[qt_src]], this->trk_byts[this->tmap[qt_dest]]);
|
||||
}
|
||||
|
||||
void WozFile::createNewTrack(const std::uint8_t qt) {
|
||||
if (this->tmap[qt] != 0xFFu) { // track already exists
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::uint8_t t(0); t < C_QTRACK; ++t) {
|
||||
if (!this->trk[t]) {
|
||||
this->tmap[qt] = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this->tmap[qt] == 0xFFu) {
|
||||
printf("Cannot create track %d\n", qt);
|
||||
return;
|
||||
}
|
||||
|
||||
this->trk_bits[this->tmap[qt]] = calcNewTrackLengthBits(qt);
|
||||
this->trk_byts[this->tmap[qt]] = bytesForBits(this->trk_bits[this->tmap[qt]]);
|
||||
this->trk[this->tmap[qt]] = new std::uint8_t[this->trk_byts[this->tmap[qt]]];
|
||||
memset(this->trk[this->tmap[qt]], 0, this->trk_byts[this->tmap[qt]]);
|
||||
}
|
||||
|
||||
/*
|
||||
* example:
|
||||
* tmap[] track length in bits
|
||||
* 17 = 06 --> 50000
|
||||
* 18 = FF -X
|
||||
* 19 = 07 --> 50002
|
||||
*
|
||||
* calcNewTrackLengthBits(18) returns 50001
|
||||
*/
|
||||
std::uint32_t WozFile::calcNewTrackLengthBits(const std::uint8_t qt) {
|
||||
uint32_t t1 = 0;
|
||||
for (int t(qt-1); t >= 0; --t) {
|
||||
if (this->tmap[t] != 0xFFu) {
|
||||
t1 = this->trk_bits[this->tmap[t]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint32_t t2 = 0;
|
||||
for (int t(qt+1); t < C_QTRACK; ++t) {
|
||||
if (this->tmap[t] != 0xFFu) {
|
||||
t2 = this->trk_bits[this->tmap[t]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// corner case: no existing tracks at all
|
||||
if (!t1 && !t2) {
|
||||
return 0xC5C0u;
|
||||
}
|
||||
// nominal case: average flanking tracks
|
||||
if (t1 && t2) {
|
||||
return (t1+t2)/2;
|
||||
}
|
||||
// odd cases: first or last track
|
||||
return t1 ? t1 : t2;
|
||||
}
|
||||
|
@ -94,6 +94,17 @@ class WozFile {
|
||||
std::string creator;
|
||||
|
||||
void checkForWriteProtection();
|
||||
void expandTracks();
|
||||
void reduceTracks();
|
||||
bool trackIsZeroes(std::uint8_t qt);
|
||||
bool tracksAreIdentical(std::uint8_t qt1, std::uint8_t qt2);
|
||||
void removeTrack(const int trackIndex);
|
||||
std::uint32_t calcNewTrackLengthBits(std::uint8_t qt);
|
||||
void copyTrack(std::uint8_t qt_dest, std::uint8_t qt_src);
|
||||
void createNewTrack(const std::uint8_t qt);
|
||||
void dumpTmap();
|
||||
void dumpTracks();
|
||||
void rawSet(std::uint8_t currentQuarterTrack, bool on);
|
||||
|
||||
public:
|
||||
WozFile();
|
||||
@ -122,6 +133,7 @@ public:
|
||||
void rotateOneBit(std::uint8_t currentQuarterTrack);
|
||||
bool getBit(std::uint8_t currentQuarterTrack);
|
||||
void setBit(std::uint8_t currentQuarterTrack, bool on);
|
||||
void rawSet();
|
||||
};
|
||||
|
||||
#endif // WOZFILE_H
|
||||
|
Loading…
Reference in New Issue
Block a user