]> Shamusworld >> Repos - apple2/blob - src/floppy.cpp
b014af08bb4fe515344948b8e99fc8d1913a9c35
[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_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];
41
42
43 // FloppyDrive class implementation...
44
45 FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), phase(0), track(0)
46 {
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
53 }
54
55
56 FloppyDrive::~FloppyDrive()
57 {
58         if (disk[0])
59                 delete[] disk[0];
60
61         if (disk[1])
62                 delete[] disk[1];
63 }
64
65
66 bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
67 {
68         WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
69
70         if (driveNum > 1)
71         {
72                 WriteLog("FLOPPY: Attempted to load image to drive #%u!\n", driveNum);
73                 return false;
74         }
75
76         imageName[driveNum][0] = 0;                                     // Zero out filename, in case it doesn't load
77
78         FILE * fp = fopen(filename, "rb");
79
80         if (fp == NULL)
81         {
82                 WriteLog("FLOPPY: Failed to open image file '%s' for reading...\n", filename);
83                 return false;
84         }
85
86         if (disk[driveNum])
87                 delete[] disk[driveNum];
88
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);
94
95         fclose(fp);
96 //printf("Read disk image: %u bytes.\n", diskSize);
97         DetectImageType(filename, driveNum);
98         strcpy(imageName[driveNum], filename);
99
100 #if 0
101         WriteLog("FLOPPY: Opening image for drive #%u.\n", driveNum);
102         FILE * fp2 = fopen("bt-nybblized.nyb", "wb");
103
104         if (fp2 == NULL)
105                 WriteLog("FLOPPY: Failed to open image file 'bt-nybblized.nyb' for writing...\n");
106         else
107         {
108                 fwrite(nybblizedImage[driveNum], 1, 232960, fp2);
109                 fclose(fp2);
110         }
111 #endif
112 //writeProtected[driveNum] = true;
113         WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
114
115         return true;
116 }
117
118
119 bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
120 {
121         if (driveNum > 1)
122         {
123                 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
124                 return false;
125         }
126
127         if (!imageDirty[driveNum])
128         {
129                 WriteLog("FLOPPY: No need to save unchanged image...\n");
130                 return false;
131         }
132
133         if (imageName[driveNum][0] == 0)
134         {
135                 WriteLog("FLOPPY: Attempted to save non-existant image!\n");
136                 return false;
137         }
138
139         if (diskType[driveNum] == DT_NYBBLE)
140                 memcpy(disk[driveNum], nybblizedImage[driveNum], 232960);
141         else
142                 DenybblizeImage(driveNum);
143
144         FILE * fp = fopen(imageName[driveNum], "wb");
145
146         if (fp == NULL)
147         {
148                 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
149                 return false;
150         }
151
152         fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
153         fclose(fp);
154
155         WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
156
157         return true;
158 }
159
160
161 bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
162 {
163 //WARNING: Buffer overflow possibility
164 #warning "Buffer overflow possible--!!! FIX !!!"
165         strcpy(imageName[driveNum], filename);
166         return SaveImage(driveNum);
167 }
168
169
170 void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
171 {
172         if (disk[driveNum] != NULL)
173                 delete disk[driveNum];
174
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);
183 }
184
185
186 void FloppyDrive::SwapImages(void)
187 {
188         uint8_t nybblizedImageTmp[232960];
189         char imageNameTmp[MAX_PATH];
190
191         memcpy(nybblizedImageTmp, nybblizedImage[0], 232960);
192         memcpy(nybblizedImage[0], nybblizedImage[1], 232960);
193         memcpy(nybblizedImage[1], nybblizedImageTmp, 232960);
194
195         memcpy(imageNameTmp, imageName[0], MAX_PATH);
196         memcpy(imageName[0], imageName[1], MAX_PATH);
197         memcpy(imageName[1], imageNameTmp, MAX_PATH);
198
199         uint8_t * diskTmp = disk[0];
200         disk[0] = disk[1];
201         disk[1] = diskTmp;
202
203         uint32_t diskSizeTmp = diskSize[0];
204         diskSize[0] = diskSize[1];
205         diskSize[1] = diskSizeTmp;
206
207         uint8_t diskTypeTmp = diskType[0];
208         diskType[0] = diskType[1];
209         diskType[1] = diskTypeTmp;
210
211         uint8_t imageDirtyTmp = imageDirty[0];
212         imageDirty[0] = imageDirty[1];
213         imageDirty[1] = imageDirtyTmp;
214
215         uint8_t writeProtectedTmp = writeProtected[0];
216         writeProtected[0] = writeProtected[1];
217         writeProtected[1] = writeProtectedTmp;
218 SpawnMessage("Drive 0: %s...", imageName[0]);
219 }
220
221
222 void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
223 {
224         diskType[driveNum] = DT_UNKNOWN;
225
226         if (diskSize[driveNum] == 232960)
227         {
228                 diskType[driveNum] = DT_NYBBLE;
229                 memcpy(nybblizedImage[driveNum], disk[driveNum], 232960);
230         }
231         else if (diskSize[driveNum] == 143360)
232         {
233                 const char * ext = strrchr(filename, '.');
234
235                 if (ext == NULL)
236                         return;
237 WriteLog("FLOPPY: Found extension [%s]...\n", ext);
238
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))
244                 {
245                         diskType[driveNum] = DT_DOS33;
246 //WriteLog("Detected DOS 3.3 disk!\n");
247 /*
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
256
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;
261 //*/
262                 }
263
264 // Actually, it just might matter WRT to nybblyzing/denybblyzing
265                 NybblizeImage(driveNum);
266         }
267         else if (diskSize[driveNum] == 143488)
268         {
269                 diskType[driveNum] = DT_DOS33_HDR;
270                 NybblizeImage(driveNum);
271         }
272
273 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
274
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")))));
279 }
280
281
282 void FloppyDrive::NybblizeImage(uint8_t driveNum)
283 {
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)
290
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 };
298
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 };
308
309         uint8_t * img = nybblizedImage[driveNum];
310         memset(img, 0xFF, 232960);                                      // Doesn't matter if 00s or FFs...
311
312         for(uint8_t trk=0; trk<35; trk++)
313         {
314                 memset(img, 0xFF, 64);                                  // Write gap 1, 64 bytes (self-sync)
315                 img += 64;
316
317                 for(uint8_t sector=0; sector<16; sector++)
318                 {
319                         memcpy(img, header, 21);                        // Set up the sector header
320
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;
327
328                         img += 21;
329                         uint8_t * bytes = disk[driveNum];
330
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);
337                         else
338                                 bytes += (sector * 256) + (trk * 256 * 16);
339
340                         // Convert the 256 8-bit bytes into 342 6-bit bytes.
341
342                         for(uint16_t i=0; i<0x56; i++)
343                         {
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);
350                         }
351
352                         img[0x54] &= 0x3F;
353                         img[0x55] &= 0x3F;
354                         memcpy(img + 0x56, bytes, 256);
355
356                         // XOR the data block with itself, offset by one byte,
357                         // creating a 343rd byte which is used as a cheksum.
358
359                         img[342] = 0x00;
360
361                         for(uint16_t i=342; i>0; i--)
362                                 img[i] = img[i] ^ img[i - 1];
363
364                         // Using a lookup table, convert the 6-bit bytes into disk bytes.
365
366                         for(uint16_t i=0; i<343; i++)
367 //#define TEST_NYBBLIZATION
368 #ifdef TEST_NYBBLIZATION
369 {
370 WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img[i] >> 2]);
371 #endif
372                                 img[i] = diskbyte[img[i] >> 2];
373 #ifdef TEST_NYBBLIZATION
374 //WriteLog("            img[i] = %02X\n", img[i]);
375 }
376 #endif
377                         img += 343;
378
379                         // Done with the nybblization, now for the epilogue...
380
381                         memcpy(img, footer, 48);
382                         img += 48;
383                 }
384         }
385 }
386
387
388 void FloppyDrive::DenybblizeImage(uint8_t driveNum)
389 {
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 };
407
408         // Sanity checks...
409         if (disk[driveNum] == NULL || diskSize[driveNum] < 143360)
410         {
411                 WriteLog("FLOPPY: Source disk image invalid! [drive=%u, disk=%08X, diskSize=%u]\n",
412                         driveNum, disk[driveNum], diskSize[driveNum]);
413                 return;
414         }
415
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...
419
420         for(uint8_t trk=0; trk<35; trk++)
421         {
422                 uint8_t * trackBase = srcImg + (trk * 6656);
423
424                 for(uint8_t sector=0; sector<16; sector++)
425                 {
426                         uint16_t sectorStart = (uint16_t)-1;
427
428                         for(uint16_t i=0; i<6656; i++)
429                         {
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])
435                                 {
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);
439
440                                         if (foundSector == sector)
441                                         {
442                                                 sectorStart = (i + 21) % 6656;
443                                                 break;
444                                         }
445                                 }
446                         }
447
448                         // Sanity check...
449                         if (sectorStart == (uint16_t)-1)
450                         {
451                                 WriteLog("FLOPPY: Failed to find sector %u (track %u) in nybble image!\n",
452                                         sector, trk);
453                                 return;
454                         }
455
456                         // Using a lookup table, convert the disk bytes into 6-bit bytes.
457
458                         for(uint16_t i=0; i<343; i++)
459                                 buffer[i] = decodeNybble[trackBase[(sectorStart + i) % 6656] & 0x7F];
460
461                         // XOR the data block with itself, offset by one byte.
462
463                         for(uint16_t i=1; i<342; i++)
464                                 buffer[i] = buffer[i] ^ buffer[i - 1];
465
466                         // Convert the 342 6-bit bytes into 256 8-bit bytes (at buffer + $56).
467
468                         for(uint16_t i=0; i<0x56; i++)
469                         {
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);
473                         }
474
475                         uint8_t * bytes = dstImg;
476
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);
483                         else
484                                 bytes += (sector * 256) + (trk * 256 * 16);//*/
485
486                         memcpy(bytes, buffer + 0x56, 256);
487                 }
488         }
489 }
490
491
492 const char * FloppyDrive::GetImageName(uint8_t driveNum/*= 0*/)
493 {
494         // Set up a zero-length string for return value
495         nameBuf[0] = 0;
496
497         if (driveNum > 1)
498         {
499                 WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
500                 return nameBuf;
501         }
502
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], '.');
506
507         // If there isn't a path, assume we're starting at the beginning
508         if (startOfFile == NULL)
509                 startOfFile = &imageName[driveNum][0];
510         else
511                 startOfFile++;
512
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]);
516
517         // Now copy the filename (may copy nothing!)
518         int j = 0;
519
520         for(const char * i=startOfFile; i<startOfExt; i++)
521                 nameBuf[j++] = *i;
522
523         nameBuf[j] = 0;
524
525         return nameBuf;
526 }
527
528
529 void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
530 {
531         // Probably want to save a dirty image... ;-)
532         SaveImage(driveNum);
533
534         WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
535
536         if (disk[driveNum])
537                 delete[] disk[driveNum];
538
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...
546 }
547
548
549 bool FloppyDrive::DriveIsEmpty(uint8_t driveNum/*= 0*/)
550 {
551         if (driveNum > 1)
552         {
553                 WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
554                 return true;
555         }
556
557         // This is kinda gay, but it works
558         return (imageName[driveNum][0] == 0 ? true : false);
559 }
560
561
562 bool FloppyDrive::DiskIsWriteProtected(uint8_t driveNum/*= 0*/)
563 {
564         if (driveNum > 1)
565         {
566                 WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
567                 return true;
568         }
569
570         return writeProtected[driveNum];
571 }
572
573
574 void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
575 {
576         if (driveNum > 1)
577         {
578                 WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
579                 return;
580         }
581
582         writeProtected[driveNum] = state;
583 }
584
585
586 // Memory mapped I/O functions
587
588 /*
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.
597 */
598
599 void FloppyDrive::ControlStepper(uint8_t addr)
600 {
601         // $C0E0 - 7
602 /*
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.
606 */
607         if (addr & 0x01)
608         {
609                 uint8_t newPhase = (addr >> 1) & 0x03;
610 //WriteLog("*** Stepper change [%u]: track = %u, phase = %u, newPhase = %u\n", addr, track, phase, newPhase);
611
612                 if (((phase + 1) & 0x03) == newPhase)
613                         phase += (phase < 79 ? 1 : 0);
614
615                 if (((phase - 1) & 0x03) == newPhase)
616                         phase -= (phase > 0 ? 1 : 0);
617
618                 if (!(phase & 0x01))
619                 {
620                         track = ((phase >> 1) < 35 ? phase >> 1 : 34);
621                         currentPos = 0;
622                 }
623 //WriteLog("                        track = %u, phase = %u, newPhase = %u\n", track, phase, newPhase);
624 SpawnMessage("Stepping to track %u...", track);
625         }
626
627 //      return something if read mode...
628 }
629
630
631 void FloppyDrive::ControlMotor(uint8_t addr)
632 {
633         // $C0E8 - 9
634         motorOn = addr;
635 }
636
637
638 void FloppyDrive::DriveEnable(uint8_t addr)
639 {
640         // $C0EA - B
641         activeDrive = addr;
642 }
643
644
645 uint8_t FloppyDrive::ReadWrite(void)
646 {
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);
650         // $C0EC
651 /*
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".
655 Which we now do. :-)
656 */
657         if (ioMode == IO_MODE_WRITE && (latchValue & 0x80))
658         {
659                 // Does it behave like this?
660 #warning "Write protection kludged in--investigate real behavior!"
661                 if (!writeProtected[activeDrive])
662                 {
663                         nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
664                         imageDirty[activeDrive] = true;
665                 }
666                 else
667 //doesn't seem to do anything
668                         return 0;//is this more like it?
669         }
670
671         uint8_t diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
672         currentPos = (currentPos + 1) % 6656;
673
674 //WriteLog("FL: diskByte=%02X, currentPos=%u\n", diskByte, currentPos);
675         return diskByte;
676 }
677
678
679 uint8_t FloppyDrive::GetLatchValue(void)
680 {
681         // $C0ED
682         return latchValue;
683 }
684
685
686 void FloppyDrive::SetLatchValue(uint8_t value)
687 {
688         // $C0ED
689         latchValue = value;
690 }
691
692
693 void FloppyDrive::SetReadMode(void)
694 {
695         // $C0EE
696         ioMode = IO_MODE_READ;
697 }
698
699
700 void FloppyDrive::SetWriteMode(void)
701 {
702         // $C0EF
703         ioMode = IO_MODE_WRITE;
704 }
705
706 /*
707 PRODOS 8 MLI ERROR CODES
708
709 $00:    No error
710 $01:    Bad system call number
711 $04:    Bad system call parameter count
712 $25:    Interrupt table full
713 $27:    I/O error
714 $28:    No device connected
715 $2B:    Disk write protected
716 $2E:    Disk switched
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
722 $46:    File not found
723 $47:    Duplicate filename
724 $48:    Volume full
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
731 $50:    File is open
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
739 */