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