2 // Apple 2 floppy disk support
5 // (c) 2005 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 12/03/2005 Created this file
12 // JLH 12/15/2005 Fixed nybblization functions to work properly
13 // JLH 12/27/2005 Added blank disk creation, fixed saving to work properly
22 #include "applevideo.h" // For message spawning... Though there's probably a better approach than this!
26 enum { IO_MODE_READ, IO_MODE_WRITE };
28 // FloppyDrive class variable initialization
30 uint8_t FloppyDrive::header[21] = {
31 0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00,
32 0x00, 0x00, 0x00, 0xDE, 0xAA, 0xFF, 0xFF, 0xFF,
33 0xFF, 0xFF, 0xD5, 0xAA, 0xAD };
34 uint8_t FloppyDrive::doSector[16] = {
35 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
36 uint8_t FloppyDrive::poSector[16] = {
37 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
38 char FloppyDrive::nameBuf[MAX_PATH];
41 // FloppyDrive class implementation...
43 FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), phase(0), track(0), ioHappened(false)
45 disk[0] = disk[1] = NULL;
46 diskSize[0] = diskSize[1] = 0;
47 diskType[0] = diskType[1] = DFT_UNKNOWN;
48 imageDirty[0] = imageDirty[1] = false;
49 writeProtected[0] = writeProtected[1] = false;
50 imageName[0][0] = imageName[1][0] = 0; // Zero out filenames
54 FloppyDrive::~FloppyDrive()
64 bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
66 WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
70 WriteLog("FLOPPY: Attempted to load image to drive #%u!\n", driveNum);
74 imageName[driveNum][0] = 0; // Zero out filename, in case it doesn't load
76 FILE * fp = fopen(filename, "rb");
80 WriteLog("FLOPPY: Failed to open image file '%s' for reading...\n", filename);
85 delete[] disk[driveNum];
87 fseek(fp, 0, SEEK_END);
88 diskSize[driveNum] = ftell(fp);
89 fseek(fp, 0, SEEK_SET);
90 disk[driveNum] = new uint8_t[diskSize[driveNum]];
91 fread(disk[driveNum], 1, diskSize[driveNum], fp);
94 //printf("Read disk image: %u bytes.\n", diskSize);
95 DetectImageType(filename, driveNum);
96 strcpy(imageName[driveNum], filename);
99 WriteLog("FLOPPY: Opening image for drive #%u.\n", driveNum);
100 FILE * fp2 = fopen("bt-nybblized.nyb", "wb");
103 WriteLog("FLOPPY: Failed to open image file 'bt-nybblized.nyb' for writing...\n");
106 fwrite(nybblizedImage[driveNum], 1, 232960, fp2);
110 //writeProtected[driveNum] = true;
111 WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
117 bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
119 // Various sanity checks...
122 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
126 if (!imageDirty[driveNum])
128 WriteLog("FLOPPY: No need to save unchanged image...\n");
132 if (imageName[driveNum][0] == 0)
134 WriteLog("FLOPPY: Attempted to save non-existant image!\n");
138 // Handle nybbylization, if necessary
139 if (diskType[driveNum] == DT_NYBBLE)
140 memcpy(disk[driveNum], nybblizedImage[driveNum], 232960);
142 DenybblizeImage(driveNum);
144 // Finally, write the damn image
145 FILE * fp = fopen(imageName[driveNum], "wb");
149 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
153 fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
156 WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
162 bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
164 //WARNING: Buffer overflow possibility
165 #warning "Buffer overflow possible--!!! FIX !!!"
166 strcpy(imageName[driveNum], filename);
167 return SaveImage(driveNum);
171 void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
173 if (disk[driveNum] != NULL)
174 delete disk[driveNum];
176 disk[driveNum] = new uint8_t[143360];
177 diskSize[driveNum] = 143360;
178 memset(disk[driveNum], 0x00, 143360);
179 memset(nybblizedImage[driveNum], 0x00, 232960); // Set it to 0 instead of $FF for proper formatting...
180 diskType[driveNum] = DT_DOS33;
181 strcpy(imageName[driveNum], "newblank.dsk");
182 writeProtected[driveNum] = false;
183 SpawnMessage("New blank image inserted in drive %u...", driveNum);
187 void FloppyDrive::SwapImages(void)
189 uint8_t nybblizedImageTmp[232960];
190 char imageNameTmp[MAX_PATH];
192 memcpy(nybblizedImageTmp, nybblizedImage[0], 232960);
193 memcpy(nybblizedImage[0], nybblizedImage[1], 232960);
194 memcpy(nybblizedImage[1], nybblizedImageTmp, 232960);
196 memcpy(imageNameTmp, imageName[0], MAX_PATH);
197 memcpy(imageName[0], imageName[1], MAX_PATH);
198 memcpy(imageName[1], imageNameTmp, MAX_PATH);
200 uint8_t * diskTmp = disk[0];
204 uint32_t diskSizeTmp = diskSize[0];
205 diskSize[0] = diskSize[1];
206 diskSize[1] = diskSizeTmp;
208 uint8_t diskTypeTmp = diskType[0];
209 diskType[0] = diskType[1];
210 diskType[1] = diskTypeTmp;
212 uint8_t imageDirtyTmp = imageDirty[0];
213 imageDirty[0] = imageDirty[1];
214 imageDirty[1] = imageDirtyTmp;
216 uint8_t writeProtectedTmp = writeProtected[0];
217 writeProtected[0] = writeProtected[1];
218 writeProtected[1] = writeProtectedTmp;
219 SpawnMessage("Drive 0: %s...", imageName[0]);
223 void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
225 diskType[driveNum] = DFT_UNKNOWN;
227 if (diskSize[driveNum] == 232960)
229 diskType[driveNum] = DT_NYBBLE;
230 memcpy(nybblizedImage[driveNum], disk[driveNum], 232960);
232 else if (diskSize[driveNum] == 143360)
234 const char * ext = strrchr(filename, '.');
238 WriteLog("FLOPPY: Found extension [%s]...\n", ext);
240 //Apparently .dsk can house either DOS order OR PRODOS order... !!! FIX !!!
241 if (strcasecmp(ext, ".po") == 0)
242 diskType[driveNum] = DT_PRODOS;
243 else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
245 // We assume this, but check for a PRODOS fingerprint. Trust, but
247 diskType[driveNum] = DT_DOS33;
249 uint8_t fingerprint[4][4] = {
250 { 0x00, 0x00, 0x03, 0x00 }, // @ $400
251 { 0x02, 0x00, 0x04, 0x00 }, // @ $600
252 { 0x03, 0x00, 0x05, 0x00 }, // @ $800
253 { 0x04, 0x00, 0x00, 0x00 } // @ $A00
256 bool foundProdos = true;
258 for(uint32_t i=0; i<4; i++)
260 for(uint32_t j=0; j<4; j++)
262 if (disk[driveNum][0x400 + (i * 0x200) + j] != fingerprint[i][j])
271 diskType[driveNum] = DT_PRODOS;
274 // Actually, it just might matter WRT to nybblyzing/denybblyzing
275 // (and, it does... :-P)
276 NybblizeImage(driveNum);
278 else if (diskSize[driveNum] == 143488)
280 diskType[driveNum] = DT_DOS33_HDR;
281 NybblizeImage(driveNum);
284 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
286 WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
287 "Nybble image" : (diskType[driveNum] == DT_DOS33 ?
288 "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
289 "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown")))));
293 void FloppyDrive::NybblizeImage(uint8_t driveNum)
295 // Format of a sector is header (23) + nybbles (343) + footer (30) = 396
296 // (short by 20 bytes of 416 [413 if 48 byte header is one time only])
297 // Hmph. Who'da thunk that AppleWin's nybblization routines would be wrong?
298 // This is now correct, BTW
299 // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
300 // (not incl. 64 byte track marker)
302 uint8_t footer[48] = {
303 0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF,
304 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
305 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
306 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
307 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
308 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
310 uint8_t diskbyte[0x40] = {
311 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
312 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
313 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
314 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
315 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
316 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
317 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
318 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
320 uint8_t * img = nybblizedImage[driveNum];
321 memset(img, 0xFF, 232960); // Doesn't matter if 00s or FFs...
323 for(uint8_t trk=0; trk<35; trk++)
325 memset(img, 0xFF, 64); // Write gap 1, 64 bytes (self-sync)
328 for(uint8_t sector=0; sector<16; sector++)
330 memcpy(img, header, 21); // Set up the sector header
332 img[5] = ((trk >> 1) & 0x55) | 0xAA;
333 img[6] = (trk & 0x55) | 0xAA;
334 img[7] = ((sector >> 1) & 0x55) | 0xAA;
335 img[8] = (sector & 0x55) | 0xAA;
336 img[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA;
337 img[10] = ((trk ^ sector ^ 0xFE) & 0x55) | 0xAA;
340 uint8_t * bytes = disk[driveNum];
342 if (diskType[driveNum] == DT_DOS33)
343 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
344 else if (diskType[driveNum] == DT_DOS33_HDR)
345 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
346 else if (diskType[driveNum] == DT_PRODOS)
347 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
349 bytes += (sector * 256) + (trk * 256 * 16);
351 // Convert the 256 8-bit bytes into 342 6-bit bytes.
353 for(uint16_t i=0; i<0x56; i++)
355 img[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
356 | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
357 | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5)
358 | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3)
359 | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3)
360 | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1);
365 memcpy(img + 0x56, bytes, 256);
367 // XOR the data block with itself, offset by one byte,
368 // creating a 343rd byte which is used as a cheksum.
372 for(uint16_t i=342; i>0; i--)
373 img[i] = img[i] ^ img[i - 1];
375 // Using a lookup table, convert the 6-bit bytes into disk bytes.
377 for(uint16_t i=0; i<343; i++)
378 //#define TEST_NYBBLIZATION
379 #ifdef TEST_NYBBLIZATION
381 WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img[i] >> 2]);
383 img[i] = diskbyte[img[i] >> 2];
384 #ifdef TEST_NYBBLIZATION
385 //WriteLog(" img[i] = %02X\n", img[i]);
390 // Done with the nybblization, now for the epilogue...
392 memcpy(img, footer, 48);
399 void FloppyDrive::DenybblizeImage(uint8_t driveNum)
401 uint8_t decodeNybble[0x80] = {
402 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
403 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
404 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
405 0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
406 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
407 0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
408 0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
409 0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
410 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
411 0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
412 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
413 0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
414 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
415 0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
416 0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
417 0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
420 if (disk[driveNum] == NULL || diskSize[driveNum] < 143360)
422 WriteLog("FLOPPY: Source disk image invalid! [drive=%u, disk=%08X, diskSize=%u]\n",
423 driveNum, disk[driveNum], diskSize[driveNum]);
427 uint8_t * srcImg = nybblizedImage[driveNum];
428 uint8_t * dstImg = disk[driveNum];
429 uint8_t buffer[345]; // 2 extra bytes for the unpack routine below...
431 for(uint8_t trk=0; trk<35; trk++)
433 uint8_t * trackBase = srcImg + (trk * 6656);
435 for(uint8_t sector=0; sector<16; sector++)
437 uint16_t sectorStart = (uint16_t)-1;
439 for(uint16_t i=0; i<6656; i++)
441 if (trackBase[i] == header[0]
442 && trackBase[(i + 1) % 6656] == header[1]
443 && trackBase[(i + 2) % 6656] == header[2]
444 && trackBase[(i + 3) % 6656] == header[3]
445 && trackBase[(i + 4) % 6656] == header[4])
447 //Could also check the track # at +5,6...
448 uint8_t foundSector = ((trackBase[(i + 7) % 6656] & 0x55) << 1)
449 | (trackBase[(i + 8) % 6656] & 0x55);
451 if (foundSector == sector)
453 sectorStart = (i + 21) % 6656;
460 if (sectorStart == (uint16_t)-1)
462 WriteLog("FLOPPY: Failed to find sector %u (track %u) in nybble image!\n",
467 // Using a lookup table, convert the disk bytes into 6-bit bytes.
469 for(uint16_t i=0; i<343; i++)
470 buffer[i] = decodeNybble[trackBase[(sectorStart + i) % 6656] & 0x7F];
472 // XOR the data block with itself, offset by one byte.
474 for(uint16_t i=1; i<342; i++)
475 buffer[i] = buffer[i] ^ buffer[i - 1];
477 // Convert the 342 6-bit bytes into 256 8-bit bytes (at buffer + $56).
479 for(uint16_t i=0; i<0x56; i++)
481 buffer[0x056 + i] |= ((buffer[i] >> 3) & 0x01) | ((buffer[i] >> 1) & 0x02);
482 buffer[0x0AC + i] |= ((buffer[i] >> 5) & 0x01) | ((buffer[i] >> 3) & 0x02);
483 buffer[0x102 + i] |= ((buffer[i] >> 7) & 0x01) | ((buffer[i] >> 5) & 0x02);
486 uint8_t * bytes = dstImg;
488 if (diskType[driveNum] == DT_DOS33)
489 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
490 else if (diskType[driveNum] == DT_DOS33_HDR)
491 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
492 else if (diskType[driveNum] == DT_PRODOS)
493 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
495 bytes += (sector * 256) + (trk * 256 * 16);//*/
497 memcpy(bytes, buffer + 0x56, 256);
503 const char * FloppyDrive::ImageName(uint8_t driveNum/*= 0*/)
505 // Set up a zero-length string for return value
510 WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
514 // Now we attempt to strip out extraneous paths/extensions to get just the filename
515 const char * startOfFile = strrchr(imageName[driveNum], '/');
516 const char * startOfExt = strrchr(imageName[driveNum], '.');
518 // If there isn't a path, assume we're starting at the beginning
519 if (startOfFile == NULL)
520 startOfFile = &imageName[driveNum][0];
524 // If there isn't an extension, assume it's at the terminating NULL
525 if (startOfExt == NULL)
526 startOfExt = &imageName[driveNum][0] + strlen(imageName[driveNum]);
528 // Now copy the filename (may copy nothing!)
531 for(const char * i=startOfFile; i<startOfExt; i++)
540 void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
543 if (IsEmpty(driveNum))
546 // Probably want to save a dirty image... ;-)
547 if (SaveImage(driveNum))
548 WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
551 delete[] disk[driveNum];
553 disk[driveNum] = NULL;
554 diskSize[driveNum] = 0;
555 diskType[driveNum] = DFT_UNKNOWN;
556 imageDirty[driveNum] = false;
557 writeProtected[driveNum] = false;
558 imageName[driveNum][0] = 0; // Zero out filenames
559 memset(nybblizedImage[driveNum], 0xFF, 232960); // Doesn't matter if 00s or FFs...
563 bool FloppyDrive::IsEmpty(uint8_t driveNum/*= 0*/)
567 WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
571 // This is kinda gay, but it works
572 return (imageName[driveNum][0] == 0 ? true : false);
576 bool FloppyDrive::IsWriteProtected(uint8_t driveNum/*= 0*/)
580 WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
584 return writeProtected[driveNum];
588 void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
592 WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
596 writeProtected[driveNum] = state;
600 int FloppyDrive::DriveLightStatus(uint8_t driveNum/*= 0*/)
602 int retval = DLS_OFF;
604 if (activeDrive != driveNum)
608 retval = (ioMode == IO_MODE_READ ? DLS_READ : DLS_WRITE);
615 void FloppyDrive::SaveState(FILE * file)
617 // Internal state vars
618 fputc(motorOn, file);
619 fputc(activeDrive, file);
621 fputc(latchValue, file);
624 fputc((ioHappened ? 1 : 0), file);
625 WriteLong(file, currentPos);
630 WriteLong(file, diskSize[0]);
631 WriteLong(file, diskType[0]);
632 fputc((imageDirty[0] ? 1 : 0), file);
633 fputc((writeProtected[0] ? 1 : 0), file);
634 fwrite(nybblizedImage[0], 1, 232960, file);
635 fwrite(imageName[0], 1, MAX_PATH, file);
643 WriteLong(file, diskSize[1]);
644 WriteLong(file, diskType[1]);
645 fputc((imageDirty[1] ? 1 : 0), file);
646 fputc((writeProtected[1] ? 1 : 0), file);
647 fwrite(nybblizedImage[1], 1, 232960, file);
648 fwrite(imageName[1], 1, MAX_PATH, file);
655 void FloppyDrive::LoadState(FILE * file)
657 // Eject images if they're loaded
661 // Read internal state variables
662 motorOn = fgetc(file);
663 activeDrive = fgetc(file);
664 ioMode = fgetc(file);
665 latchValue = fgetc(file);
668 ioHappened = (fgetc(file) == 1 ? true : false);
669 currentPos = ReadLong(file);
671 diskSize[0] = ReadLong(file);
675 disk[0] = new uint8_t[diskSize[0]];
676 diskType[0] = (uint8_t)ReadLong(file);
677 imageDirty[0] = (fgetc(file) == 1 ? true : false);
678 writeProtected[0] = (fgetc(file) == 1 ? true : false);
679 fread(nybblizedImage[0], 1, 232960, file);
680 fread(imageName[0], 1, MAX_PATH, file);
683 diskSize[1] = ReadLong(file);
687 disk[1] = new uint8_t[diskSize[1]];
688 diskType[1] = (uint8_t)ReadLong(file);
689 imageDirty[1] = (fgetc(file) == 1 ? true : false);
690 writeProtected[1] = (fgetc(file) == 1 ? true : false);
691 fread(nybblizedImage[1], 1, 232960, file);
692 fread(imageName[1], 1, MAX_PATH, file);
697 uint32_t FloppyDrive::ReadLong(FILE * file)
701 for(int i=0; i<4; i++)
702 r = (r << 8) | fgetc(file);
708 void FloppyDrive::WriteLong(FILE * file, uint32_t l)
710 for(int i=0; i<4; i++)
712 fputc((l >> 24) & 0xFF, file);
718 // Memory mapped I/O functions
721 The DSK format is a byte-for-byte image of a 16-sector Apple II floppy disk: 35
722 tracks of 16 sectors of 256 bytes each, making 143,360 bytes in total. The PO
723 format is exactly the same size as DSK and is also organized as 35 sequential
724 tracks, but the sectors within each track are in a different sequence. The NIB
725 format is a nybblized format: a more direct representation of the disk's data
726 as encoded by the Apple II floppy drive hardware. NIB contains 35 tracks of
727 6656 bytes each, for a total size of 232,960 bytes. Although this format is
728 much larger, it is also more versatile and can represent the older 13-sector
729 disks, many copy-protected disks, and other unusual encodings.
732 void FloppyDrive::ControlStepper(uint8_t addr)
736 What I can gather here:
737 bits 1-2 are the "phase" of the track (which is 1/4 of a full track (?))
738 bit 0 is the "do something" bit.
742 uint8_t newPhase = (addr >> 1) & 0x03;
743 //WriteLog("*** Stepper change [%u]: track = %u, phase = %u, newPhase = %u\n", addr, track, phase, newPhase);
745 if (((phase + 1) & 0x03) == newPhase)
746 phase += (phase < 79 ? 1 : 0);
748 if (((phase - 1) & 0x03) == newPhase)
749 phase -= (phase > 0 ? 1 : 0);
753 track = ((phase >> 1) < 35 ? phase >> 1 : 34);
756 //WriteLog(" track = %u, phase = %u, newPhase = %u\n", track, phase, newPhase);
757 SpawnMessage("Stepping to track %u...", track);
760 // return something if read mode...
764 void FloppyDrive::ControlMotor(uint8_t addr)
771 void FloppyDrive::DriveEnable(uint8_t addr)
778 uint8_t FloppyDrive::ReadWrite(void)
780 SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
781 (ioMode == IO_MODE_READ ? "Read" : "Write"),
782 (ioMode == IO_MODE_READ ? "from" : "to"), track, currentPos / 396);
786 I think what happens here is that once a track is read its nybblized form
787 is fed through here, one byte at a time--which means for DO disks, we have
788 to convert the actual 256 byte sector to a 416 byte nybblized data "sector".
791 if (ioMode == IO_MODE_WRITE && (latchValue & 0x80))
793 // Does it behave like this?
794 #warning "Write protection kludged in--investigate real behavior!"
795 if (writeProtected[activeDrive])
796 //doesn't seem to do anything
797 return 0;//is this more like it?
799 nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
800 imageDirty[activeDrive] = true;
803 uint8_t diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
804 currentPos = (currentPos + 1) % 6656;
806 //WriteLog("FL: diskByte=%02X, currentPos=%u\n", diskByte, currentPos);
811 uint8_t FloppyDrive::GetLatchValue(void)
818 void FloppyDrive::SetLatchValue(uint8_t value)
825 void FloppyDrive::SetReadMode(void)
828 ioMode = IO_MODE_READ;
832 void FloppyDrive::SetWriteMode(void)
835 ioMode = IO_MODE_WRITE;
839 PRODOS 8 MLI ERROR CODES
842 $01: Bad system call number
843 $04: Bad system call parameter count
844 $25: Interrupt table full
846 $28: No device connected
847 $2B: Disk write protected
849 $40: Invalid pathname
850 $42: Maximum number of files open
851 $43: Invalid reference number
852 $44: Directory not found
853 $45: Volume not found
855 $47: Duplicate filename
857 $49: Volume directory full
858 $4A: Incompatible file format, also a ProDOS directory
859 $4B: Unsupported storage_type
860 $4C: End of file encountered
861 $4D: Position out of range
862 $4E: File access error, also file locked
864 $51: Directory structure damaged
865 $52: Not a ProDOS volume
866 $53: Invalid system call parameter
867 $55: Volume Control Block table full
868 $56: Bad buffer address
869 $57: Duplicate volume
870 $5A: File structure damaged