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 "video.h" // For message spawning... Though there's probably a
23 // better approach than this!
27 enum { IO_MODE_READ, IO_MODE_WRITE };
29 // FloppyDrive class variable initialization
31 uint8_t FloppyDrive::header[21] = {
32 0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00,
33 0x00, 0x00, 0x00, 0xDE, 0xAA, 0xFF, 0xFF, 0xFF,
34 0xFF, 0xFF, 0xD5, 0xAA, 0xAD };
35 uint8_t FloppyDrive::doSector[16] = {
36 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
37 uint8_t FloppyDrive::poSector[16] = {
38 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
39 char FloppyDrive::nameBuf[MAX_PATH];
42 // FloppyDrive class implementation...
44 FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), phase(0), track(0), ioHappened(false)
46 disk[0] = disk[1] = NULL;
47 diskSize[0] = diskSize[1] = 0;
48 diskType[0] = diskType[1] = DFT_UNKNOWN;
49 imageDirty[0] = imageDirty[1] = false;
50 writeProtected[0] = writeProtected[1] = false;
51 imageName[0][0] = imageName[1][0] = 0; // Zero out filenames
55 FloppyDrive::~FloppyDrive()
65 bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
67 WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
71 WriteLog("FLOPPY: Attempted to load image to drive #%u!\n", driveNum);
75 imageName[driveNum][0] = 0; // Zero out filename, in case it doesn't load
77 FILE * fp = fopen(filename, "rb");
81 WriteLog("FLOPPY: Failed to open image file '%s' for reading...\n", filename);
86 delete[] disk[driveNum];
88 fseek(fp, 0, SEEK_END);
89 diskSize[driveNum] = ftell(fp);
90 fseek(fp, 0, SEEK_SET);
91 disk[driveNum] = new uint8_t[diskSize[driveNum]];
92 fread(disk[driveNum], 1, diskSize[driveNum], fp);
95 //printf("Read disk image: %u bytes.\n", diskSize);
96 DetectImageType(filename, driveNum);
97 strcpy(imageName[driveNum], filename);
100 WriteLog("FLOPPY: Opening image for drive #%u.\n", driveNum);
101 FILE * fp2 = fopen("bt-nybblized.nyb", "wb");
104 WriteLog("FLOPPY: Failed to open image file 'bt-nybblized.nyb' for writing...\n");
107 fwrite(nybblizedImage[driveNum], 1, 232960, fp2);
111 //writeProtected[driveNum] = true;
112 WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
118 bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
120 // Various sanity checks...
123 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
127 if (!imageDirty[driveNum])
129 WriteLog("FLOPPY: No need to save unchanged image...\n");
133 if (imageName[driveNum][0] == 0)
135 WriteLog("FLOPPY: Attempted to save non-existant image!\n");
139 // Handle nybbylization, if necessary
140 if (diskType[driveNum] == DT_NYBBLE)
141 memcpy(disk[driveNum], nybblizedImage[driveNum], 232960);
143 DenybblizeImage(driveNum);
145 // Finally, write the damn image
146 FILE * fp = fopen(imageName[driveNum], "wb");
150 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
154 fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
157 WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
163 bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
165 //WARNING: Buffer overflow possibility
166 #warning "Buffer overflow possible--!!! FIX !!!"
167 strcpy(imageName[driveNum], filename);
168 return SaveImage(driveNum);
172 void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
174 if (disk[driveNum] != NULL)
175 delete disk[driveNum];
177 disk[driveNum] = new uint8_t[143360];
178 diskSize[driveNum] = 143360;
179 memset(disk[driveNum], 0x00, 143360);
180 memset(nybblizedImage[driveNum], 0x00, 232960); // Set it to 0 instead of $FF for proper formatting...
181 diskType[driveNum] = DT_DOS33;
182 strcpy(imageName[driveNum], "newblank.dsk");
183 writeProtected[driveNum] = false;
184 SpawnMessage("New blank image inserted in drive %u...", driveNum);
188 void FloppyDrive::SwapImages(void)
190 uint8_t nybblizedImageTmp[232960];
191 char imageNameTmp[MAX_PATH];
193 memcpy(nybblizedImageTmp, nybblizedImage[0], 232960);
194 memcpy(nybblizedImage[0], nybblizedImage[1], 232960);
195 memcpy(nybblizedImage[1], nybblizedImageTmp, 232960);
197 memcpy(imageNameTmp, imageName[0], MAX_PATH);
198 memcpy(imageName[0], imageName[1], MAX_PATH);
199 memcpy(imageName[1], imageNameTmp, MAX_PATH);
201 uint8_t * diskTmp = disk[0];
205 uint32_t diskSizeTmp = diskSize[0];
206 diskSize[0] = diskSize[1];
207 diskSize[1] = diskSizeTmp;
209 uint8_t diskTypeTmp = diskType[0];
210 diskType[0] = diskType[1];
211 diskType[1] = diskTypeTmp;
213 uint8_t imageDirtyTmp = imageDirty[0];
214 imageDirty[0] = imageDirty[1];
215 imageDirty[1] = imageDirtyTmp;
217 uint8_t writeProtectedTmp = writeProtected[0];
218 writeProtected[0] = writeProtected[1];
219 writeProtected[1] = writeProtectedTmp;
220 SpawnMessage("Drive 0: %s...", imageName[0]);
224 void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
226 diskType[driveNum] = DFT_UNKNOWN;
228 if (diskSize[driveNum] == 232960)
230 diskType[driveNum] = DT_NYBBLE;
231 memcpy(nybblizedImage[driveNum], disk[driveNum], 232960);
233 else if (diskSize[driveNum] == 143360)
235 const char * ext = strrchr(filename, '.');
239 WriteLog("FLOPPY: Found extension [%s]...\n", ext);
241 //Apparently .dsk can house either DOS order OR PRODOS order... !!! FIX !!!
242 if (strcasecmp(ext, ".po") == 0)
243 diskType[driveNum] = DT_PRODOS;
244 else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
246 // We assume this, but check for a PRODOS fingerprint. Trust, but
248 diskType[driveNum] = DT_DOS33;
250 uint8_t fingerprint[4][4] = {
251 { 0x00, 0x00, 0x03, 0x00 }, // @ $400
252 { 0x02, 0x00, 0x04, 0x00 }, // @ $600
253 { 0x03, 0x00, 0x05, 0x00 }, // @ $800
254 { 0x04, 0x00, 0x00, 0x00 } // @ $A00
257 bool foundProdos = true;
259 for(uint32_t i=0; i<4; i++)
261 for(uint32_t j=0; j<4; j++)
263 if (disk[driveNum][0x400 + (i * 0x200) + j] != fingerprint[i][j])
272 diskType[driveNum] = DT_PRODOS;
275 // Actually, it just might matter WRT to nybblyzing/denybblyzing
276 // (and, it does... :-P)
277 NybblizeImage(driveNum);
279 else if (diskSize[driveNum] == 143488)
281 diskType[driveNum] = DT_DOS33_HDR;
282 NybblizeImage(driveNum);
285 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
287 WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
288 "Nybble image" : (diskType[driveNum] == DT_DOS33 ?
289 "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
290 "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown")))));
294 void FloppyDrive::NybblizeImage(uint8_t driveNum)
296 // Format of a sector is header (23) + nybbles (343) + footer (30) = 396
297 // (short by 20 bytes of 416 [413 if 48 byte header is one time only])
298 // Hmph. Who'da thunk that AppleWin's nybblization routines would be wrong?
299 // This is now correct, BTW
300 // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
301 // (not incl. 64 byte track marker)
303 uint8_t footer[48] = {
304 0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 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,
309 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
311 uint8_t diskbyte[0x40] = {
312 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
313 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
314 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
315 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
316 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
317 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
318 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
319 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
321 uint8_t * img = nybblizedImage[driveNum];
322 memset(img, 0xFF, 232960); // Doesn't matter if 00s or FFs...
324 for(uint8_t trk=0; trk<35; trk++)
326 memset(img, 0xFF, 64); // Write gap 1, 64 bytes (self-sync)
329 for(uint8_t sector=0; sector<16; sector++)
331 memcpy(img, header, 21); // Set up the sector header
333 img[5] = ((trk >> 1) & 0x55) | 0xAA;
334 img[6] = (trk & 0x55) | 0xAA;
335 img[7] = ((sector >> 1) & 0x55) | 0xAA;
336 img[8] = (sector & 0x55) | 0xAA;
337 img[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA;
338 img[10] = ((trk ^ sector ^ 0xFE) & 0x55) | 0xAA;
341 uint8_t * bytes = disk[driveNum];
343 if (diskType[driveNum] == DT_DOS33)
344 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
345 else if (diskType[driveNum] == DT_DOS33_HDR)
346 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
347 else if (diskType[driveNum] == DT_PRODOS)
348 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
350 bytes += (sector * 256) + (trk * 256 * 16);
352 // Convert the 256 8-bit bytes into 342 6-bit bytes.
354 for(uint16_t i=0; i<0x56; i++)
356 img[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
357 | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
358 | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5)
359 | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3)
360 | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3)
361 | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1);
366 memcpy(img + 0x56, bytes, 256);
368 // XOR the data block with itself, offset by one byte,
369 // creating a 343rd byte which is used as a cheksum.
373 for(uint16_t i=342; i>0; i--)
374 img[i] = img[i] ^ img[i - 1];
376 // Using a lookup table, convert the 6-bit bytes into disk bytes.
378 for(uint16_t i=0; i<343; i++)
379 //#define TEST_NYBBLIZATION
380 #ifdef TEST_NYBBLIZATION
382 WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img[i] >> 2]);
384 img[i] = diskbyte[img[i] >> 2];
385 #ifdef TEST_NYBBLIZATION
386 //WriteLog(" img[i] = %02X\n", img[i]);
391 // Done with the nybblization, now for the epilogue...
393 memcpy(img, footer, 48);
400 void FloppyDrive::DenybblizeImage(uint8_t driveNum)
402 uint8_t decodeNybble[0x80] = {
403 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
404 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
405 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
406 0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
407 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
408 0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
409 0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
410 0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
411 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412 0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
413 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
414 0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
415 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
416 0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
417 0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
418 0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
421 if (disk[driveNum] == NULL || diskSize[driveNum] < 143360)
423 WriteLog("FLOPPY: Source disk image invalid! [drive=%u, disk=%08X, diskSize=%u]\n",
424 driveNum, disk[driveNum], diskSize[driveNum]);
428 uint8_t * srcImg = nybblizedImage[driveNum];
429 uint8_t * dstImg = disk[driveNum];
430 uint8_t buffer[345]; // 2 extra bytes for the unpack routine below...
432 for(uint8_t trk=0; trk<35; trk++)
434 uint8_t * trackBase = srcImg + (trk * 6656);
436 for(uint8_t sector=0; sector<16; sector++)
438 uint16_t sectorStart = (uint16_t)-1;
440 for(uint16_t i=0; i<6656; i++)
442 if (trackBase[i] == header[0]
443 && trackBase[(i + 1) % 6656] == header[1]
444 && trackBase[(i + 2) % 6656] == header[2]
445 && trackBase[(i + 3) % 6656] == header[3]
446 && trackBase[(i + 4) % 6656] == header[4])
448 //Could also check the track # at +5,6...
449 uint8_t foundSector = ((trackBase[(i + 7) % 6656] & 0x55) << 1)
450 | (trackBase[(i + 8) % 6656] & 0x55);
452 if (foundSector == sector)
454 sectorStart = (i + 21) % 6656;
461 if (sectorStart == (uint16_t)-1)
463 WriteLog("FLOPPY: Failed to find sector %u (track %u) in nybble image!\n",
468 // Using a lookup table, convert the disk bytes into 6-bit bytes.
470 for(uint16_t i=0; i<343; i++)
471 buffer[i] = decodeNybble[trackBase[(sectorStart + i) % 6656] & 0x7F];
473 // XOR the data block with itself, offset by one byte.
475 for(uint16_t i=1; i<342; i++)
476 buffer[i] = buffer[i] ^ buffer[i - 1];
478 // Convert the 342 6-bit bytes into 256 8-bit bytes (at buffer + $56).
480 for(uint16_t i=0; i<0x56; i++)
482 buffer[0x056 + i] |= ((buffer[i] >> 3) & 0x01) | ((buffer[i] >> 1) & 0x02);
483 buffer[0x0AC + i] |= ((buffer[i] >> 5) & 0x01) | ((buffer[i] >> 3) & 0x02);
484 buffer[0x102 + i] |= ((buffer[i] >> 7) & 0x01) | ((buffer[i] >> 5) & 0x02);
487 uint8_t * bytes = dstImg;
489 if (diskType[driveNum] == DT_DOS33)
490 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
491 else if (diskType[driveNum] == DT_DOS33_HDR)
492 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
493 else if (diskType[driveNum] == DT_PRODOS)
494 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
496 bytes += (sector * 256) + (trk * 256 * 16);//*/
498 memcpy(bytes, buffer + 0x56, 256);
504 const char * FloppyDrive::ImageName(uint8_t driveNum/*= 0*/)
506 // Set up a zero-length string for return value
511 WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
515 // Now we attempt to strip out extraneous paths/extensions to get just the filename
516 const char * startOfFile = strrchr(imageName[driveNum], '/');
517 const char * startOfExt = strrchr(imageName[driveNum], '.');
519 // If there isn't a path, assume we're starting at the beginning
520 if (startOfFile == NULL)
521 startOfFile = &imageName[driveNum][0];
525 // If there isn't an extension, assume it's at the terminating NULL
526 if (startOfExt == NULL)
527 startOfExt = &imageName[driveNum][0] + strlen(imageName[driveNum]);
529 // Now copy the filename (may copy nothing!)
532 for(const char * i=startOfFile; i<startOfExt; i++)
541 void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
544 if (IsEmpty(driveNum))
547 // Probably want to save a dirty image... ;-)
548 if (SaveImage(driveNum))
549 WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
552 delete[] disk[driveNum];
554 disk[driveNum] = NULL;
555 diskSize[driveNum] = 0;
556 diskType[driveNum] = DFT_UNKNOWN;
557 imageDirty[driveNum] = false;
558 writeProtected[driveNum] = false;
559 imageName[driveNum][0] = 0; // Zero out filenames
560 memset(nybblizedImage[driveNum], 0xFF, 232960); // Doesn't matter if 00s or FFs...
564 bool FloppyDrive::IsEmpty(uint8_t driveNum/*= 0*/)
568 WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
572 // This is kinda gay, but it works
573 return (imageName[driveNum][0] == 0 ? true : false);
577 bool FloppyDrive::IsWriteProtected(uint8_t driveNum/*= 0*/)
581 WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
585 return writeProtected[driveNum];
589 void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
593 WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
597 writeProtected[driveNum] = state;
601 int FloppyDrive::DriveLightStatus(uint8_t driveNum/*= 0*/)
603 int retval = DLS_OFF;
605 if (activeDrive != driveNum)
609 retval = (ioMode == IO_MODE_READ ? DLS_READ : DLS_WRITE);
616 void FloppyDrive::SaveState(FILE * file)
618 // Internal state vars
619 fputc(motorOn, file);
620 fputc(activeDrive, file);
622 fputc(latchValue, file);
625 fputc((ioHappened ? 1 : 0), file);
626 WriteLong(file, currentPos);
631 WriteLong(file, diskSize[0]);
632 WriteLong(file, diskType[0]);
633 fputc((imageDirty[0] ? 1 : 0), file);
634 fputc((writeProtected[0] ? 1 : 0), file);
635 fwrite(nybblizedImage[0], 1, 232960, file);
636 fwrite(imageName[0], 1, MAX_PATH, file);
644 WriteLong(file, diskSize[1]);
645 WriteLong(file, diskType[1]);
646 fputc((imageDirty[1] ? 1 : 0), file);
647 fputc((writeProtected[1] ? 1 : 0), file);
648 fwrite(nybblizedImage[1], 1, 232960, file);
649 fwrite(imageName[1], 1, MAX_PATH, file);
656 void FloppyDrive::LoadState(FILE * file)
658 // Eject images if they're loaded
662 // Read internal state variables
663 motorOn = fgetc(file);
664 activeDrive = fgetc(file);
665 ioMode = fgetc(file);
666 latchValue = fgetc(file);
669 ioHappened = (fgetc(file) == 1 ? true : false);
670 currentPos = ReadLong(file);
672 diskSize[0] = ReadLong(file);
676 disk[0] = new uint8_t[diskSize[0]];
677 diskType[0] = (uint8_t)ReadLong(file);
678 imageDirty[0] = (fgetc(file) == 1 ? true : false);
679 writeProtected[0] = (fgetc(file) == 1 ? true : false);
680 fread(nybblizedImage[0], 1, 232960, file);
681 fread(imageName[0], 1, MAX_PATH, file);
684 diskSize[1] = ReadLong(file);
688 disk[1] = new uint8_t[diskSize[1]];
689 diskType[1] = (uint8_t)ReadLong(file);
690 imageDirty[1] = (fgetc(file) == 1 ? true : false);
691 writeProtected[1] = (fgetc(file) == 1 ? true : false);
692 fread(nybblizedImage[1], 1, 232960, file);
693 fread(imageName[1], 1, MAX_PATH, file);
698 uint32_t FloppyDrive::ReadLong(FILE * file)
702 for(int i=0; i<4; i++)
703 r = (r << 8) | fgetc(file);
709 void FloppyDrive::WriteLong(FILE * file, uint32_t l)
711 for(int i=0; i<4; i++)
713 fputc((l >> 24) & 0xFF, file);
719 // Memory mapped I/O functions
722 The DSK format is a byte-for-byte image of a 16-sector Apple II floppy disk: 35
723 tracks of 16 sectors of 256 bytes each, making 143,360 bytes in total. The PO
724 format is exactly the same size as DSK and is also organized as 35 sequential
725 tracks, but the sectors within each track are in a different sequence. The NIB
726 format is a nybblized format: a more direct representation of the disk's data
727 as encoded by the Apple II floppy drive hardware. NIB contains 35 tracks of
728 6656 bytes each, for a total size of 232,960 bytes. Although this format is
729 much larger, it is also more versatile and can represent the older 13-sector
730 disks, many copy-protected disks, and other unusual encodings.
733 void FloppyDrive::ControlStepper(uint8_t addr)
737 What I can gather here:
738 bits 1-2 are the "phase" of the track (which is 1/4 of a full track (?))
739 bit 0 is the "do something" bit.
743 uint8_t newPhase = (addr >> 1) & 0x03;
744 //WriteLog("*** Stepper change [%u]: track = %u, phase = %u, newPhase = %u\n", addr, track, phase, newPhase);
746 if (((phase + 1) & 0x03) == newPhase)
747 phase += (phase < 79 ? 1 : 0);
749 if (((phase - 1) & 0x03) == newPhase)
750 phase -= (phase > 0 ? 1 : 0);
754 track = ((phase >> 1) < 35 ? phase >> 1 : 34);
757 //WriteLog(" track = %u, phase = %u, newPhase = %u\n", track, phase, newPhase);
758 SpawnMessage("Stepping to track %u...", track);
761 // return something if read mode...
765 void FloppyDrive::ControlMotor(uint8_t addr)
772 void FloppyDrive::DriveEnable(uint8_t addr)
779 uint8_t FloppyDrive::ReadWrite(void)
781 SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
782 (ioMode == IO_MODE_READ ? "Read" : "Write"),
783 (ioMode == IO_MODE_READ ? "from" : "to"), track, currentPos / 396);
787 I think what happens here is that once a track is read its nybblized form
788 is fed through here, one byte at a time--which means for DO disks, we have
789 to convert the actual 256 byte sector to a 416 byte nybblized data "sector".
792 if (ioMode == IO_MODE_WRITE && (latchValue & 0x80))
794 // Does it behave like this?
795 #warning "Write protection kludged in--investigate real behavior!"
796 if (writeProtected[activeDrive])
797 //doesn't seem to do anything
798 return 0;//is this more like it?
800 nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
801 imageDirty[activeDrive] = true;
804 uint8_t diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
805 currentPos = (currentPos + 1) % 6656;
807 //WriteLog("FL: diskByte=%02X, currentPos=%u\n", diskByte, currentPos);
812 uint8_t FloppyDrive::GetLatchValue(void)
819 void FloppyDrive::SetLatchValue(uint8_t value)
826 void FloppyDrive::SetReadMode(void)
829 ioMode = IO_MODE_READ;
833 void FloppyDrive::SetWriteMode(void)
836 ioMode = IO_MODE_WRITE;
840 PRODOS 8 MLI ERROR CODES
843 $01: Bad system call number
844 $04: Bad system call parameter count
845 $25: Interrupt table full
847 $28: No device connected
848 $2B: Disk write protected
850 $40: Invalid pathname
851 $42: Maximum number of files open
852 $43: Invalid reference number
853 $44: Directory not found
854 $45: Volume not found
856 $47: Duplicate filename
858 $49: Volume directory full
859 $4A: Incompatible file format, also a ProDOS directory
860 $4B: Unsupported storage_type
861 $4C: End of file encountered
862 $4D: Position out of range
863 $4E: File access error, also file locked
865 $51: Directory structure damaged
866 $52: Not a ProDOS volume
867 $53: Invalid system call parameter
868 $55: Volume Control Block table full
869 $56: Bad buffer address
870 $57: Duplicate volume
871 $5A: File structure damaged