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