]> Shamusworld >> Repos - apple2/blob - src/floppy.cpp
942ffd9b088a245b59639f26e8aab8056099a83f
[apple2] / src / floppy.cpp
1 //
2 // Apple 2 floppy disk support
3 //
4 // by James L. Hammons
5 // (c) 2005 Underground Software
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
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
14 //
15
16 #include "floppy.h"
17
18 #include <stdio.h>
19 #include <string.h>
20 #include "apple2.h"
21 #include "log.h"
22 #include "applevideo.h"                                 // For message spawning... Though there's probably a better approach than this!
23
24 //using namespace std;
25
26 // Useful enums
27
28 enum { IO_MODE_READ, IO_MODE_WRITE };
29
30 // FloppyDrive class variable initialization
31
32 uint8 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 FloppyDrive::doSector[16] = {
37         0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
38 uint8 FloppyDrive::poSector[16] = {
39         0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
40
41 // FloppyDrive class implementation...
42
43 FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), phase(0), track(0)
44 {
45         disk[0] = disk[1] = NULL;
46         diskSize[0] = diskSize[1] = 0;
47         diskType[0] = diskType[1] = DT_UNKNOWN;
48         imageDirty[0] = imageDirty[1] = false;
49         imageName[0][0] = imageName[1][0] = 0;                  // Zero out filenames
50 }
51
52 FloppyDrive::~FloppyDrive()
53 {
54         if (disk[0])
55                 delete[] disk[0];
56
57         if (disk[1])
58                 delete[] disk[1];
59 }
60
61 bool FloppyDrive::LoadImage(const char * filename, uint8 driveNum/*= 0*/)
62 {
63         WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
64
65         if (driveNum > 1)
66         {
67                 WriteLog("FLOPPY: Attempted to load image to drive #%u!\n", driveNum);
68                 return false;
69         }
70
71         imageName[driveNum][0] = 0;                                     // Zero out filename, in case it doesn't load
72
73         FILE * fp = fopen(filename, "rb");
74
75         if (fp == NULL)
76         {
77                 WriteLog("FLOPPY: Failed to open image file '%s' for reading...\n", filename);
78                 return false;
79         }
80
81         if (disk[driveNum])
82                 delete[] disk[driveNum];
83
84         fseek(fp, 0, SEEK_END);
85         diskSize[driveNum] = ftell(fp);
86         fseek(fp, 0, SEEK_SET);
87         disk[driveNum] =  new uint8[diskSize[driveNum]];
88         fread(disk[driveNum], 1, diskSize[driveNum], fp);
89
90         fclose(fp);
91 //printf("Read disk image: %u bytes.\n", diskSize);
92         DetectImageType(filename, driveNum);
93         strcpy(imageName[driveNum], filename);
94
95 #if 0
96         WriteLog("FLOPPY: Opening image for drive #%u.\n", driveNum);
97         FILE * fp2 = fopen("bt-nybblized.nyb", "wb");
98
99         if (fp2 == NULL)
100                 WriteLog("FLOPPY: Failed to open image file 'bt-nybblized.nyb' for writing...\n");
101         else
102         {
103                 fwrite(nybblizedImage[driveNum], 1, 232960, fp2);
104                 fclose(fp2);
105         }
106 #endif
107         WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
108
109         return true;
110 }
111
112 bool FloppyDrive::SaveImage(uint8 driveNum/*= 0*/)
113 {
114         if (driveNum > 1)
115         {
116                 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
117                 return false;
118         }
119
120         if (!imageDirty[driveNum])
121         {
122                 WriteLog("FLOPPY: No need to save unchanged image...\n");
123                 return false;
124         }
125
126         if (imageName[driveNum][0] == 0)
127         {
128                 WriteLog("FLOPPY: Attempted to save non-existant image!\n");
129                 return false;
130         }
131
132         if (diskType[driveNum] == DT_NYBBLE)
133                 memcpy(disk[driveNum], nybblizedImage[driveNum], 232960);
134         else
135                 DenybblizeImage(driveNum);
136
137         FILE * fp = fopen(imageName[driveNum], "wb");
138
139         if (fp == NULL)
140         {
141                 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
142                 return false;
143         }
144
145         fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
146         fclose(fp);
147
148         return true;
149 }
150
151 bool FloppyDrive::SaveImageAs(const char * filename, uint8 driveNum/*= 0*/)
152 {
153 //WARNING: Buffer overflow possibility
154 #warning "Buffer overflow possible--!!! FIX !!!"
155         strcpy(imageName[driveNum], filename);
156         return SaveImage(driveNum);
157 }
158
159 void FloppyDrive::CreateBlankImage(uint8 driveNum/*= 0*/)
160 {
161         if (disk[driveNum] != NULL)
162                 delete disk[driveNum];
163
164         disk[driveNum] = new uint8[143360];
165         diskSize[driveNum] = 143360;
166         memset(disk[driveNum], 0x00, 143360);
167         memset(nybblizedImage[driveNum], 0x00, 232960); // Set it to 0 instead of $FF for proper formatting...
168         diskType[driveNum] = DT_DOS33;
169         strcpy(imageName[driveNum], "newblank.dsk");
170 SpawnMessage("New blank image inserted in drive %u...", driveNum);
171 }
172
173 void FloppyDrive::SwapImages(void)
174 {
175         uint8 nybblizedImageTmp[232960];
176         char imageNameTmp[MAX_PATH];
177
178         memcpy(nybblizedImageTmp, nybblizedImage[0], 232960);
179         memcpy(nybblizedImage[0], nybblizedImage[1], 232960);
180         memcpy(nybblizedImage[1], nybblizedImageTmp, 232960);
181
182         memcpy(imageNameTmp, imageName[0], MAX_PATH);
183         memcpy(imageName[0], imageName[1], MAX_PATH);
184         memcpy(imageName[1], imageNameTmp, MAX_PATH);
185
186         uint8 * diskTmp = disk[0];
187         disk[0] = disk[1];
188         disk[1] = diskTmp;
189
190         uint32 diskSizeTmp = diskSize[0];
191         diskSize[0] = diskSize[1];
192         diskSize[1] = diskSizeTmp;
193
194         uint8 diskTypeTmp = diskType[0];
195         diskType[0] = diskType[1];
196         diskType[1] = diskTypeTmp;
197
198         uint8 imageDirtyTmp = imageDirty[0];
199         imageDirty[0] = imageDirty[1];
200         imageDirty[1] = imageDirtyTmp;
201 SpawnMessage("Drive 0: %s...", imageName[0]);
202 }
203
204 void FloppyDrive::DetectImageType(const char * filename, uint8 driveNum)
205 {
206         diskType[driveNum] = DT_UNKNOWN;
207
208         if (diskSize[driveNum] == 232960)
209         {
210                 diskType[driveNum] = DT_NYBBLE;
211                 memcpy(nybblizedImage[driveNum], disk[driveNum], 232960);
212         }
213         else if (diskSize[driveNum] == 143360)
214         {
215                 const char * ext = strrchr(filename, '.');
216
217                 if (ext == NULL)
218                         return;
219 WriteLog("FLOPPY: Found extension [%s]...\n", ext);
220
221 //Apparently .dsk can house either DOS order OR PRODOS order... !!! FIX !!!
222 //[DONE, see below why we don't need it]
223                 if (strcasecmp(ext, ".po") == 0)
224                         diskType[driveNum] = DT_PRODOS;
225                 else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
226                 {
227                         diskType[driveNum] = DT_DOS33;
228 //WriteLog("Detected DOS 3.3 disk!\n");
229 /*
230 This doesn't seem to be accurate... Maybe it's just a ProDOS disk in a DOS33 order...
231 That would seem to be the case--just because it's a ProDOS disk doesn't mean anything
232 WRT to the disk image itself.
233                         // This could really be a ProDOS order disk with a .dsk extension, so let's see...
234                         char fingerprint[3][4] = {
235                                 { 0x04, 0x00, 0x00, 0x00 },             // @ $500
236                                 { 0x03, 0x00, 0x05, 0x00 },             // @ $700
237                                 { 0x02, 0x00, 0x04, 0x00 } };   // @ $900
238
239                         if ((strcmp((char *)(disk[driveNum] + 0x500), fingerprint[0]) == 0)
240                                 && (strcmp((char *)(disk[driveNum] + 0x700), fingerprint[1]) == 0)
241                                 && (strcmp((char *)(disk[driveNum] + 0x900), fingerprint[2]) == 0))
242                                 diskType[driveNum] = DT_PRODOS;
243 //*/
244                 }
245
246 // Actually, it just might matter WRT to nybblyzing/denybblyzing
247 // Here, we check for BT3
248 //Nope, no change...
249 //diskType[driveNum] = DT_PRODOS;
250
251                 NybblizeImage(driveNum);
252         }
253
254 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
255
256 WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
257         "Nybble image" : (diskType[driveNum] == DT_DOS33 ?
258         "DOS 3.3 image" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown"))));
259 }
260
261 void FloppyDrive::NybblizeImage(uint8 driveNum)
262 {
263         // Format of a sector is header (23) + nybbles (343) + footer (30) = 396
264         // (short by 20 bytes of 416 [413 if 48 byte header is one time only])
265 // Hmph. Who'da thunk that AppleWin's nybblization routines would be wrong?
266 // This is now correct, BTW
267         // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
268         // (not incl. 64 byte track marker)
269
270         uint8 footer[48] = {
271                 0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF,
272                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
273                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
274                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
275                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
276                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
277
278         uint8 diskbyte[0x40] = {
279                 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
280                 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
281                 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
282                 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
283                 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
284                 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
285                 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
286                 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
287
288         uint8 * img = nybblizedImage[driveNum];
289         memset(img, 0xFF, 232960);                                      // Doesn't matter if 00s or FFs...
290
291         for(uint8 trk=0; trk<35; trk++)
292         {
293                 memset(img, 0xFF, 64);                                  // Write gap 1, 64 bytes (self-sync)
294                 img += 64;
295
296                 for(uint8 sector=0; sector<16; sector++)
297                 {
298                         memcpy(img, header, 21);                        // Set up the sector header
299
300                         img[5] = ((trk >> 1) & 0x55) | 0xAA;
301                         img[6] =  (trk       & 0x55) | 0xAA;
302                         img[7] = ((sector >> 1) & 0x55) | 0xAA;
303                         img[8] =  (sector       & 0x55) | 0xAA;
304                         img[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA;
305                         img[10] = ((trk ^ sector ^ 0xFE)       & 0x55) | 0xAA;
306
307                         img += 21;
308                         uint8 * bytes = disk[driveNum];
309
310                         if (diskType[driveNum] == DT_DOS33)
311                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
312                         else if (diskType[driveNum] == DT_PRODOS)
313                                 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
314                         else
315                                 bytes += (sector * 256) + (trk * 256 * 16);
316
317                         // Convert the 256 8-bit bytes into 342 6-bit bytes.
318
319                         for(uint16 i=0; i<0x56; i++)
320                         {
321                                 img[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
322                                         | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
323                                         | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5)
324                                         | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3)
325                                         | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3)
326                                         | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1);
327                         }
328
329                         img[0x54] &= 0x3F;
330                         img[0x55] &= 0x3F;
331                         memcpy(img + 0x56, bytes, 256);
332
333                         // XOR the data block with itself, offset by one byte,
334                         // creating a 343rd byte which is used as a cheksum.
335
336                         img[342] = 0x00;
337
338                         for(uint16 i=342; i>0; i--)
339                                 img[i] = img[i] ^ img[i - 1];
340
341                         // Using a lookup table, convert the 6-bit bytes into disk bytes.
342
343                         for(uint16 i=0; i<343; i++)
344 //#define TEST_NYBBLIZATION
345 #ifdef TEST_NYBBLIZATION
346 {
347 WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img[i] >> 2]);
348 #endif
349                                 img[i] = diskbyte[img[i] >> 2];
350 #ifdef TEST_NYBBLIZATION
351 //WriteLog("            img[i] = %02X\n", img[i]);
352 }
353 #endif
354                         img += 343;
355
356                         // Done with the nybblization, now for the epilogue...
357
358                         memcpy(img, footer, 48);
359                         img += 48;
360                 }
361         }
362 }
363
364 void FloppyDrive::DenybblizeImage(uint8 driveNum)
365 {
366         uint8 decodeNybble[0x80] = {
367                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
368                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
369                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
370                 0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
371                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
372                 0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
373                 0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
374                 0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
375                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376                 0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
377                 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
378                 0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
379                 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
380                 0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
381                 0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
382                 0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
383
384         // Sanity checks...
385         if (disk[driveNum] == NULL || diskSize[driveNum] < 143360)
386         {
387                 WriteLog("FLOPPY: Source disk image invalid! [drive=%u, disk=%08X, diskSize=%u]\n",
388                         driveNum, disk[driveNum], diskSize[driveNum]);
389                 return;
390         }
391
392         uint8 * srcImg = nybblizedImage[driveNum];
393         uint8 * dstImg = disk[driveNum];
394         uint8 buffer[345];                                                      // 2 extra bytes for the unpack routine below...
395
396         for(uint8 trk=0; trk<35; trk++)
397         {
398                 uint8 * trackBase = srcImg + (trk * 6656);
399
400                 for(uint8 sector=0; sector<16; sector++)
401                 {
402                         uint16 sectorStart = (uint16)-1;
403
404                         for(uint16 i=0; i<6656; i++)
405                         {
406                                 if (trackBase[i] == header[0]
407                                         && trackBase[(i + 1) % 6656] == header[1]
408                                         && trackBase[(i + 2) % 6656] == header[2]
409                                         && trackBase[(i + 3) % 6656] == header[3]
410                                         && trackBase[(i + 4) % 6656] == header[4])
411                                 {
412 //Could also check the track # at +5,6...
413                                         uint8 foundSector = ((trackBase[(i + 7) % 6656] & 0x55) << 1)
414                                                 | (trackBase[(i + 8) % 6656] & 0x55);
415
416                                         if (foundSector == sector)
417                                         {
418                                                 sectorStart = (i + 21) % 6656;
419                                                 break;
420                                         }
421                                 }
422                         }
423
424                         // Sanity check...
425                         if (sectorStart == (uint16)-1)
426                         {
427                                 WriteLog("FLOPPY: Failed to find sector %u (track %u) in nybble image!\n",
428                                         sector, trk);
429                                 return;
430                         }
431
432                         // Using a lookup table, convert the disk bytes into 6-bit bytes.
433
434                         for(uint16 i=0; i<343; i++)
435                                 buffer[i] = decodeNybble[trackBase[(sectorStart + i) % 6656] & 0x7F];
436
437                         // XOR the data block with itself, offset by one byte.
438
439                         for(uint16 i=1; i<342; i++)
440                                 buffer[i] = buffer[i] ^ buffer[i - 1];
441
442                         // Convert the 342 6-bit bytes into 256 8-bit bytes (at buffer + $56).
443
444                         for(uint16 i=0; i<0x56; i++)
445                         {
446                                 buffer[0x056 + i] |= ((buffer[i] >> 3) & 0x01) | ((buffer[i] >> 1) & 0x02);
447                                 buffer[0x0AC + i] |= ((buffer[i] >> 5) & 0x01) | ((buffer[i] >> 3) & 0x02);
448                                 buffer[0x102 + i] |= ((buffer[i] >> 7) & 0x01) | ((buffer[i] >> 5) & 0x02);
449                         }
450
451                         uint8 * bytes = dstImg;
452
453                         if (diskType[driveNum] == DT_DOS33)
454                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
455                         else if (diskType[driveNum] == DT_PRODOS)
456                                 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
457                         else
458                                 bytes += (sector * 256) + (trk * 256 * 16);//*/
459
460                         memcpy(bytes, buffer + 0x56, 256);
461                 }
462         }
463 }
464
465 // Memory mapped I/O functions
466
467 /*
468 The DSK format is a byte-for-byte image of a 16-sector Apple II floppy disk: 35 tracks of 16
469 sectors of 256 bytes each, making 143,360 bytes in total. The PO format is exactly the same
470 size as DSK and is also organized as 35 sequential tracks, but the sectors within each track
471 are in a different sequence. The NIB format is a nybblized format: a more direct representation
472 of the disk's data as encoded by the Apple II floppy drive hardware. NIB contains 35 tracks of
473 6656 bytes each, for a total size of 232,960 bytes. Although this format is much larger, it is
474 also more versatile and can represent the older 13-sector disks, many copy-protected disks, and
475 other unusual encodings.
476 */
477
478 void FloppyDrive::ControlStepper(uint8 addr)
479 {
480         // $C0E0 - 7
481 /*
482 What I can gather here:
483 bits 1-2 are the "phase" of the track (which is 1/4 of a full track (?))
484 bit 0 is the "do something" bit.
485 */
486         if (addr & 0x01)
487         {
488                 uint8 newPhase = (addr >> 1) & 0x03;
489 //WriteLog("*** Stepper change [%u]: track = %u, phase = %u, newPhase = %u\n", addr, track, phase, newPhase);
490
491                 if (((phase + 1) & 0x03) == newPhase)
492                         phase += (phase < 79 ? 1 : 0);
493
494                 if (((phase - 1) & 0x03) == newPhase)
495                         phase -= (phase > 0 ? 1 : 0);
496
497                 if (!(phase & 0x01))
498                 {
499                         track = ((phase >> 1) < 35 ? phase >> 1 : 34);
500                         currentPos = 0;
501                 }
502 //WriteLog("                        track = %u, phase = %u, newPhase = %u\n", track, phase, newPhase);
503 SpawnMessage("Stepping to track %u...", track);
504         }
505
506 //      return something if read mode...
507 }
508
509 void FloppyDrive::ControlMotor(uint8 addr)
510 {
511         // $C0E8 - 9
512         motorOn = addr;
513 }
514
515 void FloppyDrive::DriveEnable(uint8 addr)
516 {
517         // $C0EA - B
518         activeDrive = addr;
519 }
520
521 uint8 FloppyDrive::ReadWrite(void)
522 {
523 SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
524         (ioMode == IO_MODE_READ ? "Read" : "Write"),
525         (ioMode == IO_MODE_READ ? "from" : "to"), track, currentPos / 396);
526         // $C0EC
527 /*
528 I think what happens here is that once a track is read its nybblized form
529 is fed through here, one byte at a time--which means for DO disks, we have
530 to convert the actual 256 byte sector to a 416 byte nybblized data "sector".
531 Which we now do. :-)
532 */
533         if (ioMode == IO_MODE_WRITE && (latchValue & 0x80))
534         {
535                 nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
536                 imageDirty[activeDrive] = true;
537         }
538
539         uint8 diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
540         currentPos = (currentPos + 1) % 6656;
541
542 //WriteLog("FL: diskByte=%02X, currentPos=%u\n", diskByte, currentPos);
543         return diskByte;
544 }
545
546 uint8 FloppyDrive::GetLatchValue(void)
547 {
548         // $C0ED
549         return latchValue;
550 }
551
552 void FloppyDrive::SetLatchValue(uint8 value)
553 {
554         // $C0ED
555         latchValue = value;
556 }
557
558 void FloppyDrive::SetReadMode(void)
559 {
560         // $C0EE
561         ioMode = IO_MODE_READ;
562 }
563
564 void FloppyDrive::SetWriteMode(void)
565 {
566         // $C0EF
567         ioMode = IO_MODE_WRITE;
568 }
569
570 /*
571 PRODOS 8 MLI ERROR CODES
572
573 $00:    No error
574 $01:    Bad system call number
575 $04:    Bad system call parameter count
576 $25:    Interrupt table full
577 $27:    I/O error
578 $28:    No device connected
579 $2B:    Disk write protected
580 $2E:    Disk switched
581 $40:    Invalid pathname
582 $42:    Maximum number of files open
583 $43:    Invalid reference number
584 $44:    Directory not found
585 $45:    Volume not found
586 $46:    File not found
587 $47:    Duplicate filename
588 $48:    Volume full
589 $49:    Volume directory full
590 $4A:    Incompatible file format, also a ProDOS directory
591 $4B:    Unsupported storage_type
592 $4C:    End of file encountered
593 $4D:    Position out of range
594 $4E:    File access error, also file locked
595 $50:    File is open
596 $51:    Directory structure damaged
597 $52:    Not a ProDOS volume
598 $53:    Invalid system call parameter
599 $55:    Volume Control Block table full
600 $56:    Bad buffer address
601 $57:    Duplicate volume
602 $5A:    File structure damaged
603 */