2 // Apple 2 floppy disk support
5 // (c) 2005 Underground Software
7 // JLH = James L. 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)
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*/)
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 if (diskType[driveNum] == DT_NYBBLE)
140 memcpy(disk[driveNum], nybblizedImage[driveNum], 232960);
142 DenybblizeImage(driveNum);
144 FILE * fp = fopen(imageName[driveNum], "wb");
148 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
152 fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
155 WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
161 bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
163 //WARNING: Buffer overflow possibility
164 #warning "Buffer overflow possible--!!! FIX !!!"
165 strcpy(imageName[driveNum], filename);
166 return SaveImage(driveNum);
170 void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
172 if (disk[driveNum] != NULL)
173 delete disk[driveNum];
175 disk[driveNum] = new uint8_t[143360];
176 diskSize[driveNum] = 143360;
177 memset(disk[driveNum], 0x00, 143360);
178 memset(nybblizedImage[driveNum], 0x00, 232960); // Set it to 0 instead of $FF for proper formatting...
179 diskType[driveNum] = DT_DOS33;
180 strcpy(imageName[driveNum], "newblank.dsk");
181 writeProtected[driveNum] = false;
182 SpawnMessage("New blank image inserted in drive %u...", driveNum);
186 void FloppyDrive::SwapImages(void)
188 uint8_t nybblizedImageTmp[232960];
189 char imageNameTmp[MAX_PATH];
191 memcpy(nybblizedImageTmp, nybblizedImage[0], 232960);
192 memcpy(nybblizedImage[0], nybblizedImage[1], 232960);
193 memcpy(nybblizedImage[1], nybblizedImageTmp, 232960);
195 memcpy(imageNameTmp, imageName[0], MAX_PATH);
196 memcpy(imageName[0], imageName[1], MAX_PATH);
197 memcpy(imageName[1], imageNameTmp, MAX_PATH);
199 uint8_t * diskTmp = disk[0];
203 uint32_t diskSizeTmp = diskSize[0];
204 diskSize[0] = diskSize[1];
205 diskSize[1] = diskSizeTmp;
207 uint8_t diskTypeTmp = diskType[0];
208 diskType[0] = diskType[1];
209 diskType[1] = diskTypeTmp;
211 uint8_t imageDirtyTmp = imageDirty[0];
212 imageDirty[0] = imageDirty[1];
213 imageDirty[1] = imageDirtyTmp;
215 uint8_t writeProtectedTmp = writeProtected[0];
216 writeProtected[0] = writeProtected[1];
217 writeProtected[1] = writeProtectedTmp;
218 SpawnMessage("Drive 0: %s...", imageName[0]);
222 void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
224 diskType[driveNum] = DT_UNKNOWN;
226 if (diskSize[driveNum] == 232960)
228 diskType[driveNum] = DT_NYBBLE;
229 memcpy(nybblizedImage[driveNum], disk[driveNum], 232960);
231 else if (diskSize[driveNum] == 143360)
233 const char * ext = strrchr(filename, '.');
237 WriteLog("FLOPPY: Found extension [%s]...\n", ext);
239 //Apparently .dsk can house either DOS order OR PRODOS order... !!! FIX !!!
240 //[DONE, see below why we don't need it]
241 if (strcasecmp(ext, ".po") == 0)
242 diskType[driveNum] = DT_PRODOS;
243 else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
245 diskType[driveNum] = DT_DOS33;
246 //WriteLog("Detected DOS 3.3 disk!\n");
248 This doesn't seem to be accurate... Maybe it's just a ProDOS disk in a DOS33 order...
249 That would seem to be the case--just because it's a ProDOS disk doesn't mean anything
250 WRT to the disk image itself.
251 // This could really be a ProDOS order disk with a .dsk extension, so let's see...
252 char fingerprint[3][4] = {
253 { 0x04, 0x00, 0x00, 0x00 }, // @ $500
254 { 0x03, 0x00, 0x05, 0x00 }, // @ $700
255 { 0x02, 0x00, 0x04, 0x00 } }; // @ $900
257 if ((strcmp((char *)(disk[driveNum] + 0x500), fingerprint[0]) == 0)
258 && (strcmp((char *)(disk[driveNum] + 0x700), fingerprint[1]) == 0)
259 && (strcmp((char *)(disk[driveNum] + 0x900), fingerprint[2]) == 0))
260 diskType[driveNum] = DT_PRODOS;
264 // Actually, it just might matter WRT to nybblyzing/denybblyzing
265 NybblizeImage(driveNum);
267 else if (diskSize[driveNum] == 143488)
269 diskType[driveNum] = DT_DOS33_HDR;
270 NybblizeImage(driveNum);
273 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
275 WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
276 "Nybble image" : (diskType[driveNum] == DT_DOS33 ?
277 "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
278 "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown")))));
282 void FloppyDrive::NybblizeImage(uint8_t driveNum)
284 // Format of a sector is header (23) + nybbles (343) + footer (30) = 396
285 // (short by 20 bytes of 416 [413 if 48 byte header is one time only])
286 // Hmph. Who'da thunk that AppleWin's nybblization routines would be wrong?
287 // This is now correct, BTW
288 // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
289 // (not incl. 64 byte track marker)
291 uint8_t footer[48] = {
292 0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF,
293 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
294 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
295 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
296 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
297 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
299 uint8_t diskbyte[0x40] = {
300 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
301 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
302 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
303 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
304 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
305 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
306 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
307 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
309 uint8_t * img = nybblizedImage[driveNum];
310 memset(img, 0xFF, 232960); // Doesn't matter if 00s or FFs...
312 for(uint8_t trk=0; trk<35; trk++)
314 memset(img, 0xFF, 64); // Write gap 1, 64 bytes (self-sync)
317 for(uint8_t sector=0; sector<16; sector++)
319 memcpy(img, header, 21); // Set up the sector header
321 img[5] = ((trk >> 1) & 0x55) | 0xAA;
322 img[6] = (trk & 0x55) | 0xAA;
323 img[7] = ((sector >> 1) & 0x55) | 0xAA;
324 img[8] = (sector & 0x55) | 0xAA;
325 img[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA;
326 img[10] = ((trk ^ sector ^ 0xFE) & 0x55) | 0xAA;
329 uint8_t * bytes = disk[driveNum];
331 if (diskType[driveNum] == DT_DOS33)
332 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
333 else if (diskType[driveNum] == DT_DOS33_HDR)
334 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
335 else if (diskType[driveNum] == DT_PRODOS)
336 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
338 bytes += (sector * 256) + (trk * 256 * 16);
340 // Convert the 256 8-bit bytes into 342 6-bit bytes.
342 for(uint16_t i=0; i<0x56; i++)
344 img[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
345 | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
346 | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5)
347 | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3)
348 | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3)
349 | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1);
354 memcpy(img + 0x56, bytes, 256);
356 // XOR the data block with itself, offset by one byte,
357 // creating a 343rd byte which is used as a cheksum.
361 for(uint16_t i=342; i>0; i--)
362 img[i] = img[i] ^ img[i - 1];
364 // Using a lookup table, convert the 6-bit bytes into disk bytes.
366 for(uint16_t i=0; i<343; i++)
367 //#define TEST_NYBBLIZATION
368 #ifdef TEST_NYBBLIZATION
370 WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img[i] >> 2]);
372 img[i] = diskbyte[img[i] >> 2];
373 #ifdef TEST_NYBBLIZATION
374 //WriteLog(" img[i] = %02X\n", img[i]);
379 // Done with the nybblization, now for the epilogue...
381 memcpy(img, footer, 48);
388 void FloppyDrive::DenybblizeImage(uint8_t driveNum)
390 uint8_t decodeNybble[0x80] = {
391 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
392 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
393 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
394 0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
395 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
396 0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
397 0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
398 0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
399 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
400 0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
401 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
402 0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
403 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
404 0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
405 0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
406 0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
409 if (disk[driveNum] == NULL || diskSize[driveNum] < 143360)
411 WriteLog("FLOPPY: Source disk image invalid! [drive=%u, disk=%08X, diskSize=%u]\n",
412 driveNum, disk[driveNum], diskSize[driveNum]);
416 uint8_t * srcImg = nybblizedImage[driveNum];
417 uint8_t * dstImg = disk[driveNum];
418 uint8_t buffer[345]; // 2 extra bytes for the unpack routine below...
420 for(uint8_t trk=0; trk<35; trk++)
422 uint8_t * trackBase = srcImg + (trk * 6656);
424 for(uint8_t sector=0; sector<16; sector++)
426 uint16_t sectorStart = (uint16_t)-1;
428 for(uint16_t i=0; i<6656; i++)
430 if (trackBase[i] == header[0]
431 && trackBase[(i + 1) % 6656] == header[1]
432 && trackBase[(i + 2) % 6656] == header[2]
433 && trackBase[(i + 3) % 6656] == header[3]
434 && trackBase[(i + 4) % 6656] == header[4])
436 //Could also check the track # at +5,6...
437 uint8_t foundSector = ((trackBase[(i + 7) % 6656] & 0x55) << 1)
438 | (trackBase[(i + 8) % 6656] & 0x55);
440 if (foundSector == sector)
442 sectorStart = (i + 21) % 6656;
449 if (sectorStart == (uint16_t)-1)
451 WriteLog("FLOPPY: Failed to find sector %u (track %u) in nybble image!\n",
456 // Using a lookup table, convert the disk bytes into 6-bit bytes.
458 for(uint16_t i=0; i<343; i++)
459 buffer[i] = decodeNybble[trackBase[(sectorStart + i) % 6656] & 0x7F];
461 // XOR the data block with itself, offset by one byte.
463 for(uint16_t i=1; i<342; i++)
464 buffer[i] = buffer[i] ^ buffer[i - 1];
466 // Convert the 342 6-bit bytes into 256 8-bit bytes (at buffer + $56).
468 for(uint16_t i=0; i<0x56; i++)
470 buffer[0x056 + i] |= ((buffer[i] >> 3) & 0x01) | ((buffer[i] >> 1) & 0x02);
471 buffer[0x0AC + i] |= ((buffer[i] >> 5) & 0x01) | ((buffer[i] >> 3) & 0x02);
472 buffer[0x102 + i] |= ((buffer[i] >> 7) & 0x01) | ((buffer[i] >> 5) & 0x02);
475 uint8_t * bytes = dstImg;
477 if (diskType[driveNum] == DT_DOS33)
478 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
479 else if (diskType[driveNum] == DT_DOS33_HDR)
480 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
481 else if (diskType[driveNum] == DT_PRODOS)
482 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
484 bytes += (sector * 256) + (trk * 256 * 16);//*/
486 memcpy(bytes, buffer + 0x56, 256);
492 const char * FloppyDrive::GetImageName(uint8_t driveNum/*= 0*/)
494 // Set up a zero-length string for return value
499 WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
503 // Now we attempt to strip out extraneous paths/extensions to get just the filename
504 const char * startOfFile = strrchr(imageName[driveNum], '/');
505 const char * startOfExt = strrchr(imageName[driveNum], '.');
507 // If there isn't a path, assume we're starting at the beginning
508 if (startOfFile == NULL)
509 startOfFile = &imageName[driveNum][0];
513 // If there isn't an extension, assume it's at the terminating NULL
514 if (startOfExt == NULL)
515 startOfExt = &imageName[driveNum][0] + strlen(imageName[driveNum]);
517 // Now copy the filename (may copy nothing!)
520 for(const char * i=startOfFile; i<startOfExt; i++)
529 void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
531 // Probably want to save a dirty image... ;-)
534 WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
537 delete[] disk[driveNum];
539 disk[driveNum] = NULL;
540 diskSize[driveNum] = 0;
541 diskType[driveNum] = DT_UNKNOWN;
542 imageDirty[driveNum] = false;
543 writeProtected[driveNum] = false;
544 imageName[driveNum][0] = 0; // Zero out filenames
545 memset(nybblizedImage[driveNum], 0xFF, 232960); // Doesn't matter if 00s or FFs...
549 bool FloppyDrive::DriveIsEmpty(uint8_t driveNum/*= 0*/)
553 WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
557 // This is kinda gay, but it works
558 return (imageName[driveNum][0] == 0 ? true : false);
562 bool FloppyDrive::DiskIsWriteProtected(uint8_t driveNum/*= 0*/)
566 WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
570 return writeProtected[driveNum];
574 void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
578 WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
582 writeProtected[driveNum] = state;
586 // Memory mapped I/O functions
589 The DSK format is a byte-for-byte image of a 16-sector Apple II floppy disk: 35 tracks of 16
590 sectors of 256 bytes each, making 143,360 bytes in total. The PO format is exactly the same
591 size as DSK and is also organized as 35 sequential tracks, but the sectors within each track
592 are in a different sequence. The NIB format is a nybblized format: a more direct representation
593 of the disk's data as encoded by the Apple II floppy drive hardware. NIB contains 35 tracks of
594 6656 bytes each, for a total size of 232,960 bytes. Although this format is much larger, it is
595 also more versatile and can represent the older 13-sector disks, many copy-protected disks, and
596 other unusual encodings.
599 void FloppyDrive::ControlStepper(uint8_t addr)
603 What I can gather here:
604 bits 1-2 are the "phase" of the track (which is 1/4 of a full track (?))
605 bit 0 is the "do something" bit.
609 uint8_t newPhase = (addr >> 1) & 0x03;
610 //WriteLog("*** Stepper change [%u]: track = %u, phase = %u, newPhase = %u\n", addr, track, phase, newPhase);
612 if (((phase + 1) & 0x03) == newPhase)
613 phase += (phase < 79 ? 1 : 0);
615 if (((phase - 1) & 0x03) == newPhase)
616 phase -= (phase > 0 ? 1 : 0);
620 track = ((phase >> 1) < 35 ? phase >> 1 : 34);
623 //WriteLog(" track = %u, phase = %u, newPhase = %u\n", track, phase, newPhase);
624 SpawnMessage("Stepping to track %u...", track);
627 // return something if read mode...
631 void FloppyDrive::ControlMotor(uint8_t addr)
638 void FloppyDrive::DriveEnable(uint8_t addr)
645 uint8_t FloppyDrive::ReadWrite(void)
647 SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
648 (ioMode == IO_MODE_READ ? "Read" : "Write"),
649 (ioMode == IO_MODE_READ ? "from" : "to"), track, currentPos / 396);
652 I think what happens here is that once a track is read its nybblized form
653 is fed through here, one byte at a time--which means for DO disks, we have
654 to convert the actual 256 byte sector to a 416 byte nybblized data "sector".
657 if (ioMode == IO_MODE_WRITE && (latchValue & 0x80))
659 // Does it behave like this?
660 #warning "Write protection kludged in--investigate real behavior!"
661 if (!writeProtected[activeDrive])
663 nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
664 imageDirty[activeDrive] = true;
667 //doesn't seem to do anything
668 return 0;//is this more like it?
671 uint8_t diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
672 currentPos = (currentPos + 1) % 6656;
674 //WriteLog("FL: diskByte=%02X, currentPos=%u\n", diskByte, currentPos);
679 uint8_t FloppyDrive::GetLatchValue(void)
686 void FloppyDrive::SetLatchValue(uint8_t value)
693 void FloppyDrive::SetReadMode(void)
696 ioMode = IO_MODE_READ;
700 void FloppyDrive::SetWriteMode(void)
703 ioMode = IO_MODE_WRITE;
707 PRODOS 8 MLI ERROR CODES
710 $01: Bad system call number
711 $04: Bad system call parameter count
712 $25: Interrupt table full
714 $28: No device connected
715 $2B: Disk write protected
717 $40: Invalid pathname
718 $42: Maximum number of files open
719 $43: Invalid reference number
720 $44: Directory not found
721 $45: Volume not found
723 $47: Duplicate filename
725 $49: Volume directory full
726 $4A: Incompatible file format, also a ProDOS directory
727 $4B: Unsupported storage_type
728 $4C: End of file encountered
729 $4D: Position out of range
730 $4E: File access error, also file locked
732 $51: Directory structure damaged
733 $52: Not a ProDOS volume
734 $53: Invalid system call parameter
735 $55: Volume Control Block table full
736 $56: Bad buffer address
737 $57: Duplicate volume
738 $5A: File structure damaged