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!
24 //using namespace std;
28 enum { IO_MODE_READ, IO_MODE_WRITE };
30 // FloppyDrive class variable initialization
32 uint8_t FloppyDrive::header[21] = {
33 0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00,
34 0x00, 0x00, 0x00, 0xDE, 0xAA, 0xFF, 0xFF, 0xFF,
35 0xFF, 0xFF, 0xD5, 0xAA, 0xAD };
36 uint8_t FloppyDrive::doSector[16] = {
37 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
38 uint8_t FloppyDrive::poSector[16] = {
39 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
40 char FloppyDrive::nameBuf[MAX_PATH];
43 // FloppyDrive class implementation...
45 FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), phase(0), track(0), ioHappened(false)
47 disk[0] = disk[1] = NULL;
48 diskSize[0] = diskSize[1] = 0;
49 diskType[0] = diskType[1] = DT_UNKNOWN;
50 imageDirty[0] = imageDirty[1] = false;
51 writeProtected[0] = writeProtected[1] = false;
52 imageName[0][0] = imageName[1][0] = 0; // Zero out filenames
56 FloppyDrive::~FloppyDrive()
66 bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
68 WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
72 WriteLog("FLOPPY: Attempted to load image to drive #%u!\n", driveNum);
76 imageName[driveNum][0] = 0; // Zero out filename, in case it doesn't load
78 FILE * fp = fopen(filename, "rb");
82 WriteLog("FLOPPY: Failed to open image file '%s' for reading...\n", filename);
87 delete[] disk[driveNum];
89 fseek(fp, 0, SEEK_END);
90 diskSize[driveNum] = ftell(fp);
91 fseek(fp, 0, SEEK_SET);
92 disk[driveNum] = new uint8_t[diskSize[driveNum]];
93 fread(disk[driveNum], 1, diskSize[driveNum], fp);
96 //printf("Read disk image: %u bytes.\n", diskSize);
97 DetectImageType(filename, driveNum);
98 strcpy(imageName[driveNum], filename);
101 WriteLog("FLOPPY: Opening image for drive #%u.\n", driveNum);
102 FILE * fp2 = fopen("bt-nybblized.nyb", "wb");
105 WriteLog("FLOPPY: Failed to open image file 'bt-nybblized.nyb' for writing...\n");
108 fwrite(nybblizedImage[driveNum], 1, 232960, fp2);
112 //writeProtected[driveNum] = true;
113 WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
119 bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
121 // Various sanity checks...
124 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
128 if (!imageDirty[driveNum])
130 WriteLog("FLOPPY: No need to save unchanged image...\n");
134 if (imageName[driveNum][0] == 0)
136 WriteLog("FLOPPY: Attempted to save non-existant image!\n");
140 // Handle nybbylization, if necessary
141 if (diskType[driveNum] == DT_NYBBLE)
142 memcpy(disk[driveNum], nybblizedImage[driveNum], 232960);
144 DenybblizeImage(driveNum);
146 // Finally, write the damn image
147 FILE * fp = fopen(imageName[driveNum], "wb");
151 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
155 fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
158 WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
164 bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
166 //WARNING: Buffer overflow possibility
167 #warning "Buffer overflow possible--!!! FIX !!!"
168 strcpy(imageName[driveNum], filename);
169 return SaveImage(driveNum);
173 void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
175 if (disk[driveNum] != NULL)
176 delete disk[driveNum];
178 disk[driveNum] = new uint8_t[143360];
179 diskSize[driveNum] = 143360;
180 memset(disk[driveNum], 0x00, 143360);
181 memset(nybblizedImage[driveNum], 0x00, 232960); // Set it to 0 instead of $FF for proper formatting...
182 diskType[driveNum] = DT_DOS33;
183 strcpy(imageName[driveNum], "newblank.dsk");
184 writeProtected[driveNum] = false;
185 SpawnMessage("New blank image inserted in drive %u...", driveNum);
189 void FloppyDrive::SwapImages(void)
191 uint8_t nybblizedImageTmp[232960];
192 char imageNameTmp[MAX_PATH];
194 memcpy(nybblizedImageTmp, nybblizedImage[0], 232960);
195 memcpy(nybblizedImage[0], nybblizedImage[1], 232960);
196 memcpy(nybblizedImage[1], nybblizedImageTmp, 232960);
198 memcpy(imageNameTmp, imageName[0], MAX_PATH);
199 memcpy(imageName[0], imageName[1], MAX_PATH);
200 memcpy(imageName[1], imageNameTmp, MAX_PATH);
202 uint8_t * diskTmp = disk[0];
206 uint32_t diskSizeTmp = diskSize[0];
207 diskSize[0] = diskSize[1];
208 diskSize[1] = diskSizeTmp;
210 uint8_t diskTypeTmp = diskType[0];
211 diskType[0] = diskType[1];
212 diskType[1] = diskTypeTmp;
214 uint8_t imageDirtyTmp = imageDirty[0];
215 imageDirty[0] = imageDirty[1];
216 imageDirty[1] = imageDirtyTmp;
218 uint8_t writeProtectedTmp = writeProtected[0];
219 writeProtected[0] = writeProtected[1];
220 writeProtected[1] = writeProtectedTmp;
221 SpawnMessage("Drive 0: %s...", imageName[0]);
225 void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
227 diskType[driveNum] = DT_UNKNOWN;
229 if (diskSize[driveNum] == 232960)
231 diskType[driveNum] = DT_NYBBLE;
232 memcpy(nybblizedImage[driveNum], disk[driveNum], 232960);
234 else if (diskSize[driveNum] == 143360)
236 const char * ext = strrchr(filename, '.');
240 WriteLog("FLOPPY: Found extension [%s]...\n", ext);
242 //Apparently .dsk can house either DOS order OR PRODOS order... !!! FIX !!!
243 if (strcasecmp(ext, ".po") == 0)
244 diskType[driveNum] = DT_PRODOS;
245 else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
247 // We assume this, but check for a PRODOS fingerprint. Trust, but
249 diskType[driveNum] = DT_DOS33;
251 uint8_t fingerprint[4][4] = {
252 { 0x00, 0x00, 0x03, 0x00 }, // @ $400
253 { 0x02, 0x00, 0x04, 0x00 }, // @ $600
254 { 0x03, 0x00, 0x05, 0x00 }, // @ $800
255 { 0x04, 0x00, 0x00, 0x00 } // @ $A00
258 bool foundProdos = true;
260 for(uint32_t i=0; i<4; i++)
262 for(uint32_t j=0; j<4; j++)
264 if (disk[driveNum][0x400 + (i * 0x200) + j] != fingerprint[i][j])
273 diskType[driveNum] = DT_PRODOS;
276 // Actually, it just might matter WRT to nybblyzing/denybblyzing
277 // (and, it does... :-P)
278 NybblizeImage(driveNum);
280 else if (diskSize[driveNum] == 143488)
282 diskType[driveNum] = DT_DOS33_HDR;
283 NybblizeImage(driveNum);
286 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
288 WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
289 "Nybble image" : (diskType[driveNum] == DT_DOS33 ?
290 "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
291 "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown")))));
295 void FloppyDrive::NybblizeImage(uint8_t driveNum)
297 // Format of a sector is header (23) + nybbles (343) + footer (30) = 396
298 // (short by 20 bytes of 416 [413 if 48 byte header is one time only])
299 // Hmph. Who'da thunk that AppleWin's nybblization routines would be wrong?
300 // This is now correct, BTW
301 // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
302 // (not incl. 64 byte track marker)
304 uint8_t footer[48] = {
305 0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 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,
310 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
312 uint8_t diskbyte[0x40] = {
313 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
314 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
315 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
316 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
317 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
318 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
319 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
320 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
322 uint8_t * img = nybblizedImage[driveNum];
323 memset(img, 0xFF, 232960); // Doesn't matter if 00s or FFs...
325 for(uint8_t trk=0; trk<35; trk++)
327 memset(img, 0xFF, 64); // Write gap 1, 64 bytes (self-sync)
330 for(uint8_t sector=0; sector<16; sector++)
332 memcpy(img, header, 21); // Set up the sector header
334 img[5] = ((trk >> 1) & 0x55) | 0xAA;
335 img[6] = (trk & 0x55) | 0xAA;
336 img[7] = ((sector >> 1) & 0x55) | 0xAA;
337 img[8] = (sector & 0x55) | 0xAA;
338 img[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA;
339 img[10] = ((trk ^ sector ^ 0xFE) & 0x55) | 0xAA;
342 uint8_t * bytes = disk[driveNum];
344 if (diskType[driveNum] == DT_DOS33)
345 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
346 else if (diskType[driveNum] == DT_DOS33_HDR)
347 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
348 else if (diskType[driveNum] == DT_PRODOS)
349 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
351 bytes += (sector * 256) + (trk * 256 * 16);
353 // Convert the 256 8-bit bytes into 342 6-bit bytes.
355 for(uint16_t i=0; i<0x56; i++)
357 img[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
358 | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
359 | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5)
360 | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3)
361 | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3)
362 | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1);
367 memcpy(img + 0x56, bytes, 256);
369 // XOR the data block with itself, offset by one byte,
370 // creating a 343rd byte which is used as a cheksum.
374 for(uint16_t i=342; i>0; i--)
375 img[i] = img[i] ^ img[i - 1];
377 // Using a lookup table, convert the 6-bit bytes into disk bytes.
379 for(uint16_t i=0; i<343; i++)
380 //#define TEST_NYBBLIZATION
381 #ifdef TEST_NYBBLIZATION
383 WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img[i] >> 2]);
385 img[i] = diskbyte[img[i] >> 2];
386 #ifdef TEST_NYBBLIZATION
387 //WriteLog(" img[i] = %02X\n", img[i]);
392 // Done with the nybblization, now for the epilogue...
394 memcpy(img, footer, 48);
401 void FloppyDrive::DenybblizeImage(uint8_t driveNum)
403 uint8_t decodeNybble[0x80] = {
404 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
405 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
406 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
407 0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
408 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
409 0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
410 0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
411 0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
412 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413 0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
414 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
415 0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
416 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
417 0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
418 0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
419 0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
422 if (disk[driveNum] == NULL || diskSize[driveNum] < 143360)
424 WriteLog("FLOPPY: Source disk image invalid! [drive=%u, disk=%08X, diskSize=%u]\n",
425 driveNum, disk[driveNum], diskSize[driveNum]);
429 uint8_t * srcImg = nybblizedImage[driveNum];
430 uint8_t * dstImg = disk[driveNum];
431 uint8_t buffer[345]; // 2 extra bytes for the unpack routine below...
433 for(uint8_t trk=0; trk<35; trk++)
435 uint8_t * trackBase = srcImg + (trk * 6656);
437 for(uint8_t sector=0; sector<16; sector++)
439 uint16_t sectorStart = (uint16_t)-1;
441 for(uint16_t i=0; i<6656; i++)
443 if (trackBase[i] == header[0]
444 && trackBase[(i + 1) % 6656] == header[1]
445 && trackBase[(i + 2) % 6656] == header[2]
446 && trackBase[(i + 3) % 6656] == header[3]
447 && trackBase[(i + 4) % 6656] == header[4])
449 //Could also check the track # at +5,6...
450 uint8_t foundSector = ((trackBase[(i + 7) % 6656] & 0x55) << 1)
451 | (trackBase[(i + 8) % 6656] & 0x55);
453 if (foundSector == sector)
455 sectorStart = (i + 21) % 6656;
462 if (sectorStart == (uint16_t)-1)
464 WriteLog("FLOPPY: Failed to find sector %u (track %u) in nybble image!\n",
469 // Using a lookup table, convert the disk bytes into 6-bit bytes.
471 for(uint16_t i=0; i<343; i++)
472 buffer[i] = decodeNybble[trackBase[(sectorStart + i) % 6656] & 0x7F];
474 // XOR the data block with itself, offset by one byte.
476 for(uint16_t i=1; i<342; i++)
477 buffer[i] = buffer[i] ^ buffer[i - 1];
479 // Convert the 342 6-bit bytes into 256 8-bit bytes (at buffer + $56).
481 for(uint16_t i=0; i<0x56; i++)
483 buffer[0x056 + i] |= ((buffer[i] >> 3) & 0x01) | ((buffer[i] >> 1) & 0x02);
484 buffer[0x0AC + i] |= ((buffer[i] >> 5) & 0x01) | ((buffer[i] >> 3) & 0x02);
485 buffer[0x102 + i] |= ((buffer[i] >> 7) & 0x01) | ((buffer[i] >> 5) & 0x02);
488 uint8_t * bytes = dstImg;
490 if (diskType[driveNum] == DT_DOS33)
491 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
492 else if (diskType[driveNum] == DT_DOS33_HDR)
493 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
494 else if (diskType[driveNum] == DT_PRODOS)
495 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
497 bytes += (sector * 256) + (trk * 256 * 16);//*/
499 memcpy(bytes, buffer + 0x56, 256);
505 const char * FloppyDrive::GetImageName(uint8_t driveNum/*= 0*/)
507 // Set up a zero-length string for return value
512 WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
516 // Now we attempt to strip out extraneous paths/extensions to get just the filename
517 const char * startOfFile = strrchr(imageName[driveNum], '/');
518 const char * startOfExt = strrchr(imageName[driveNum], '.');
520 // If there isn't a path, assume we're starting at the beginning
521 if (startOfFile == NULL)
522 startOfFile = &imageName[driveNum][0];
526 // If there isn't an extension, assume it's at the terminating NULL
527 if (startOfExt == NULL)
528 startOfExt = &imageName[driveNum][0] + strlen(imageName[driveNum]);
530 // Now copy the filename (may copy nothing!)
533 for(const char * i=startOfFile; i<startOfExt; i++)
542 void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
544 // Probably want to save a dirty image... ;-)
547 WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
550 delete[] disk[driveNum];
552 disk[driveNum] = NULL;
553 diskSize[driveNum] = 0;
554 diskType[driveNum] = DT_UNKNOWN;
555 imageDirty[driveNum] = false;
556 writeProtected[driveNum] = false;
557 imageName[driveNum][0] = 0; // Zero out filenames
558 memset(nybblizedImage[driveNum], 0xFF, 232960); // Doesn't matter if 00s or FFs...
562 bool FloppyDrive::DriveIsEmpty(uint8_t driveNum/*= 0*/)
566 WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
570 // This is kinda gay, but it works
571 return (imageName[driveNum][0] == 0 ? true : false);
575 bool FloppyDrive::DiskIsWriteProtected(uint8_t driveNum/*= 0*/)
579 WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
583 return writeProtected[driveNum];
587 void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
591 WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
595 writeProtected[driveNum] = state;
599 int FloppyDrive::DriveLightStatus(uint8_t driveNum/*= 0*/)
601 int retval = DLS_OFF;
603 if (activeDrive != driveNum)
607 retval = (ioMode == IO_MODE_READ ? DLS_READ : DLS_WRITE);
614 // Memory mapped I/O functions
617 The DSK format is a byte-for-byte image of a 16-sector Apple II floppy disk: 35 tracks of 16
618 sectors of 256 bytes each, making 143,360 bytes in total. The PO format is exactly the same
619 size as DSK and is also organized as 35 sequential tracks, but the sectors within each track
620 are in a different sequence. The NIB format is a nybblized format: a more direct representation
621 of the disk's data as encoded by the Apple II floppy drive hardware. NIB contains 35 tracks of
622 6656 bytes each, for a total size of 232,960 bytes. Although this format is much larger, it is
623 also more versatile and can represent the older 13-sector disks, many copy-protected disks, and
624 other unusual encodings.
627 void FloppyDrive::ControlStepper(uint8_t addr)
631 What I can gather here:
632 bits 1-2 are the "phase" of the track (which is 1/4 of a full track (?))
633 bit 0 is the "do something" bit.
637 uint8_t newPhase = (addr >> 1) & 0x03;
638 //WriteLog("*** Stepper change [%u]: track = %u, phase = %u, newPhase = %u\n", addr, track, phase, newPhase);
640 if (((phase + 1) & 0x03) == newPhase)
641 phase += (phase < 79 ? 1 : 0);
643 if (((phase - 1) & 0x03) == newPhase)
644 phase -= (phase > 0 ? 1 : 0);
648 track = ((phase >> 1) < 35 ? phase >> 1 : 34);
651 //WriteLog(" track = %u, phase = %u, newPhase = %u\n", track, phase, newPhase);
652 SpawnMessage("Stepping to track %u...", track);
655 // return something if read mode...
659 void FloppyDrive::ControlMotor(uint8_t addr)
666 void FloppyDrive::DriveEnable(uint8_t addr)
673 uint8_t FloppyDrive::ReadWrite(void)
675 SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
676 (ioMode == IO_MODE_READ ? "Read" : "Write"),
677 (ioMode == IO_MODE_READ ? "from" : "to"), track, currentPos / 396);
681 I think what happens here is that once a track is read its nybblized form
682 is fed through here, one byte at a time--which means for DO disks, we have
683 to convert the actual 256 byte sector to a 416 byte nybblized data "sector".
686 if (ioMode == IO_MODE_WRITE && (latchValue & 0x80))
688 // Does it behave like this?
689 #warning "Write protection kludged in--investigate real behavior!"
690 if (writeProtected[activeDrive])
691 //doesn't seem to do anything
692 return 0;//is this more like it?
694 nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
695 imageDirty[activeDrive] = true;
698 uint8_t diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
699 currentPos = (currentPos + 1) % 6656;
701 //WriteLog("FL: diskByte=%02X, currentPos=%u\n", diskByte, currentPos);
706 uint8_t FloppyDrive::GetLatchValue(void)
713 void FloppyDrive::SetLatchValue(uint8_t value)
720 void FloppyDrive::SetReadMode(void)
723 ioMode = IO_MODE_READ;
727 void FloppyDrive::SetWriteMode(void)
730 ioMode = IO_MODE_WRITE;
734 PRODOS 8 MLI ERROR CODES
737 $01: Bad system call number
738 $04: Bad system call parameter count
739 $25: Interrupt table full
741 $28: No device connected
742 $2B: Disk write protected
744 $40: Invalid pathname
745 $42: Maximum number of files open
746 $43: Invalid reference number
747 $44: Directory not found
748 $45: Volume not found
750 $47: Duplicate filename
752 $49: Volume directory full
753 $4A: Incompatible file format, also a ProDOS directory
754 $4B: Unsupported storage_type
755 $4C: End of file encountered
756 $4D: Position out of range
757 $4E: File access error, also file locked
759 $51: Directory structure damaged
760 $52: Not a ProDOS volume
761 $53: Invalid system call parameter
762 $55: Volume Control Block table full
763 $56: Bad buffer address
764 $57: Duplicate volume
765 $5A: File structure damaged