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