]> Shamusworld >> Repos - apple2/blob - src/floppy.cpp
Added Prodos detection for .dsk images, fixes to 80STORE switch.
[apple2] / src / floppy.cpp
1 //
2 // Apple 2 floppy disk support
3 //
4 // by James Hammons
5 // (c) 2005 Underground Software
6 //
7 // JLH = James 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                 if (strcasecmp(ext, ".po") == 0)
241                         diskType[driveNum] = DT_PRODOS;
242                 else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
243                 {
244                         // We assume this, but check for a PRODOS fingerprint. Trust, but
245                         // verify. ;-)
246                         diskType[driveNum] = DT_DOS33;
247
248                         uint8_t fingerprint[4][4] = {
249                                 { 0x00, 0x00, 0x03, 0x00 },             // @ $400
250                                 { 0x02, 0x00, 0x04, 0x00 },             // @ $600
251                                 { 0x03, 0x00, 0x05, 0x00 },             // @ $800
252                                 { 0x04, 0x00, 0x00, 0x00 }              // @ $A00
253                         };
254
255                         bool foundProdos = true;
256
257                         for(uint32_t i=0; i<4; i++)
258                         {
259                                 for(uint32_t j=0; j<4; j++)
260                                 {
261                                         if (disk[driveNum][0x400 + (i * 0x200) + j] != fingerprint[i][j])
262                                         {
263                                                 foundProdos = false;
264                                                 break;
265                                         }
266                                 }
267                         }
268
269                         if (foundProdos)
270                                 diskType[driveNum] = DT_PRODOS;
271                 }
272
273 // Actually, it just might matter WRT to nybblyzing/denybblyzing
274 // (and, it does... :-P)
275                 NybblizeImage(driveNum);
276         }
277         else if (diskSize[driveNum] == 143488)
278         {
279                 diskType[driveNum] = DT_DOS33_HDR;
280                 NybblizeImage(driveNum);
281         }
282
283 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
284
285 WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
286         "Nybble image" : (diskType[driveNum] == DT_DOS33 ?
287         "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
288         "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown")))));
289 }
290
291
292 void FloppyDrive::NybblizeImage(uint8_t driveNum)
293 {
294         // Format of a sector is header (23) + nybbles (343) + footer (30) = 396
295         // (short by 20 bytes of 416 [413 if 48 byte header is one time only])
296 // Hmph. Who'da thunk that AppleWin's nybblization routines would be wrong?
297 // This is now correct, BTW
298         // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
299         // (not incl. 64 byte track marker)
300
301         uint8_t footer[48] = {
302                 0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF,
303                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
304                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
305                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
306                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
307                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
308
309         uint8_t diskbyte[0x40] = {
310                 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
311                 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
312                 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
313                 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
314                 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
315                 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
316                 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
317                 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
318
319         uint8_t * img = nybblizedImage[driveNum];
320         memset(img, 0xFF, 232960);                                      // Doesn't matter if 00s or FFs...
321
322         for(uint8_t trk=0; trk<35; trk++)
323         {
324                 memset(img, 0xFF, 64);                                  // Write gap 1, 64 bytes (self-sync)
325                 img += 64;
326
327                 for(uint8_t sector=0; sector<16; sector++)
328                 {
329                         memcpy(img, header, 21);                        // Set up the sector header
330
331                         img[5] = ((trk >> 1) & 0x55) | 0xAA;
332                         img[6] =  (trk       & 0x55) | 0xAA;
333                         img[7] = ((sector >> 1) & 0x55) | 0xAA;
334                         img[8] =  (sector       & 0x55) | 0xAA;
335                         img[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA;
336                         img[10] = ((trk ^ sector ^ 0xFE)       & 0x55) | 0xAA;
337
338                         img += 21;
339                         uint8_t * bytes = disk[driveNum];
340
341                         if (diskType[driveNum] == DT_DOS33)
342                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
343                         else if (diskType[driveNum] == DT_DOS33_HDR)
344                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
345                         else if (diskType[driveNum] == DT_PRODOS)
346                                 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
347                         else
348                                 bytes += (sector * 256) + (trk * 256 * 16);
349
350                         // Convert the 256 8-bit bytes into 342 6-bit bytes.
351
352                         for(uint16_t i=0; i<0x56; i++)
353                         {
354                                 img[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
355                                         | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
356                                         | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5)
357                                         | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3)
358                                         | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3)
359                                         | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1);
360                         }
361
362                         img[0x54] &= 0x3F;
363                         img[0x55] &= 0x3F;
364                         memcpy(img + 0x56, bytes, 256);
365
366                         // XOR the data block with itself, offset by one byte,
367                         // creating a 343rd byte which is used as a cheksum.
368
369                         img[342] = 0x00;
370
371                         for(uint16_t i=342; i>0; i--)
372                                 img[i] = img[i] ^ img[i - 1];
373
374                         // Using a lookup table, convert the 6-bit bytes into disk bytes.
375
376                         for(uint16_t i=0; i<343; i++)
377 //#define TEST_NYBBLIZATION
378 #ifdef TEST_NYBBLIZATION
379 {
380 WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img[i] >> 2]);
381 #endif
382                                 img[i] = diskbyte[img[i] >> 2];
383 #ifdef TEST_NYBBLIZATION
384 //WriteLog("            img[i] = %02X\n", img[i]);
385 }
386 #endif
387                         img += 343;
388
389                         // Done with the nybblization, now for the epilogue...
390
391                         memcpy(img, footer, 48);
392                         img += 48;
393                 }
394         }
395 }
396
397
398 void FloppyDrive::DenybblizeImage(uint8_t driveNum)
399 {
400         uint8_t decodeNybble[0x80] = {
401                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
402                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
403                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
404                 0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
405                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
406                 0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
407                 0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
408                 0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
409                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
410                 0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
411                 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
412                 0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
413                 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
414                 0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
415                 0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
416                 0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
417
418         // Sanity checks...
419         if (disk[driveNum] == NULL || diskSize[driveNum] < 143360)
420         {
421                 WriteLog("FLOPPY: Source disk image invalid! [drive=%u, disk=%08X, diskSize=%u]\n",
422                         driveNum, disk[driveNum], diskSize[driveNum]);
423                 return;
424         }
425
426         uint8_t * srcImg = nybblizedImage[driveNum];
427         uint8_t * dstImg = disk[driveNum];
428         uint8_t buffer[345];                                                    // 2 extra bytes for the unpack routine below...
429
430         for(uint8_t trk=0; trk<35; trk++)
431         {
432                 uint8_t * trackBase = srcImg + (trk * 6656);
433
434                 for(uint8_t sector=0; sector<16; sector++)
435                 {
436                         uint16_t sectorStart = (uint16_t)-1;
437
438                         for(uint16_t i=0; i<6656; i++)
439                         {
440                                 if (trackBase[i] == header[0]
441                                         && trackBase[(i + 1) % 6656] == header[1]
442                                         && trackBase[(i + 2) % 6656] == header[2]
443                                         && trackBase[(i + 3) % 6656] == header[3]
444                                         && trackBase[(i + 4) % 6656] == header[4])
445                                 {
446 //Could also check the track # at +5,6...
447                                         uint8_t foundSector = ((trackBase[(i + 7) % 6656] & 0x55) << 1)
448                                                 | (trackBase[(i + 8) % 6656] & 0x55);
449
450                                         if (foundSector == sector)
451                                         {
452                                                 sectorStart = (i + 21) % 6656;
453                                                 break;
454                                         }
455                                 }
456                         }
457
458                         // Sanity check...
459                         if (sectorStart == (uint16_t)-1)
460                         {
461                                 WriteLog("FLOPPY: Failed to find sector %u (track %u) in nybble image!\n",
462                                         sector, trk);
463                                 return;
464                         }
465
466                         // Using a lookup table, convert the disk bytes into 6-bit bytes.
467
468                         for(uint16_t i=0; i<343; i++)
469                                 buffer[i] = decodeNybble[trackBase[(sectorStart + i) % 6656] & 0x7F];
470
471                         // XOR the data block with itself, offset by one byte.
472
473                         for(uint16_t i=1; i<342; i++)
474                                 buffer[i] = buffer[i] ^ buffer[i - 1];
475
476                         // Convert the 342 6-bit bytes into 256 8-bit bytes (at buffer + $56).
477
478                         for(uint16_t i=0; i<0x56; i++)
479                         {
480                                 buffer[0x056 + i] |= ((buffer[i] >> 3) & 0x01) | ((buffer[i] >> 1) & 0x02);
481                                 buffer[0x0AC + i] |= ((buffer[i] >> 5) & 0x01) | ((buffer[i] >> 3) & 0x02);
482                                 buffer[0x102 + i] |= ((buffer[i] >> 7) & 0x01) | ((buffer[i] >> 5) & 0x02);
483                         }
484
485                         uint8_t * bytes = dstImg;
486
487                         if (diskType[driveNum] == DT_DOS33)
488                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
489                         else if (diskType[driveNum] == DT_DOS33_HDR)
490                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
491                         else if (diskType[driveNum] == DT_PRODOS)
492                                 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
493                         else
494                                 bytes += (sector * 256) + (trk * 256 * 16);//*/
495
496                         memcpy(bytes, buffer + 0x56, 256);
497                 }
498         }
499 }
500
501
502 const char * FloppyDrive::GetImageName(uint8_t driveNum/*= 0*/)
503 {
504         // Set up a zero-length string for return value
505         nameBuf[0] = 0;
506
507         if (driveNum > 1)
508         {
509                 WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
510                 return nameBuf;
511         }
512
513         // Now we attempt to strip out extraneous paths/extensions to get just the filename
514         const char * startOfFile = strrchr(imageName[driveNum], '/');
515         const char * startOfExt = strrchr(imageName[driveNum], '.');
516
517         // If there isn't a path, assume we're starting at the beginning
518         if (startOfFile == NULL)
519                 startOfFile = &imageName[driveNum][0];
520         else
521                 startOfFile++;
522
523         // If there isn't an extension, assume it's at the terminating NULL
524         if (startOfExt == NULL)
525                 startOfExt = &imageName[driveNum][0] + strlen(imageName[driveNum]);
526
527         // Now copy the filename (may copy nothing!)
528         int j = 0;
529
530         for(const char * i=startOfFile; i<startOfExt; i++)
531                 nameBuf[j++] = *i;
532
533         nameBuf[j] = 0;
534
535         return nameBuf;
536 }
537
538
539 void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
540 {
541         // Probably want to save a dirty image... ;-)
542         SaveImage(driveNum);
543
544         WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
545
546         if (disk[driveNum])
547                 delete[] disk[driveNum];
548
549         disk[driveNum] = NULL;
550         diskSize[driveNum] = 0;
551         diskType[driveNum] = DT_UNKNOWN;
552         imageDirty[driveNum] = false;
553         writeProtected[driveNum] = false;
554         imageName[driveNum][0] = 0;                     // Zero out filenames
555         memset(nybblizedImage[driveNum], 0xFF, 232960); // Doesn't matter if 00s or FFs...
556 }
557
558
559 bool FloppyDrive::DriveIsEmpty(uint8_t driveNum/*= 0*/)
560 {
561         if (driveNum > 1)
562         {
563                 WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
564                 return true;
565         }
566
567         // This is kinda gay, but it works
568         return (imageName[driveNum][0] == 0 ? true : false);
569 }
570
571
572 bool FloppyDrive::DiskIsWriteProtected(uint8_t driveNum/*= 0*/)
573 {
574         if (driveNum > 1)
575         {
576                 WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
577                 return true;
578         }
579
580         return writeProtected[driveNum];
581 }
582
583
584 void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
585 {
586         if (driveNum > 1)
587         {
588                 WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
589                 return;
590         }
591
592         writeProtected[driveNum] = state;
593 }
594
595
596 // Memory mapped I/O functions
597
598 /*
599 The DSK format is a byte-for-byte image of a 16-sector Apple II floppy disk: 35 tracks of 16
600 sectors of 256 bytes each, making 143,360 bytes in total. The PO format is exactly the same
601 size as DSK and is also organized as 35 sequential tracks, but the sectors within each track
602 are in a different sequence. The NIB format is a nybblized format: a more direct representation
603 of the disk's data as encoded by the Apple II floppy drive hardware. NIB contains 35 tracks of
604 6656 bytes each, for a total size of 232,960 bytes. Although this format is much larger, it is
605 also more versatile and can represent the older 13-sector disks, many copy-protected disks, and
606 other unusual encodings.
607 */
608
609 void FloppyDrive::ControlStepper(uint8_t addr)
610 {
611         // $C0E0 - 7
612 /*
613 What I can gather here:
614 bits 1-2 are the "phase" of the track (which is 1/4 of a full track (?))
615 bit 0 is the "do something" bit.
616 */
617         if (addr & 0x01)
618         {
619                 uint8_t newPhase = (addr >> 1) & 0x03;
620 //WriteLog("*** Stepper change [%u]: track = %u, phase = %u, newPhase = %u\n", addr, track, phase, newPhase);
621
622                 if (((phase + 1) & 0x03) == newPhase)
623                         phase += (phase < 79 ? 1 : 0);
624
625                 if (((phase - 1) & 0x03) == newPhase)
626                         phase -= (phase > 0 ? 1 : 0);
627
628                 if (!(phase & 0x01))
629                 {
630                         track = ((phase >> 1) < 35 ? phase >> 1 : 34);
631                         currentPos = 0;
632                 }
633 //WriteLog("                        track = %u, phase = %u, newPhase = %u\n", track, phase, newPhase);
634 SpawnMessage("Stepping to track %u...", track);
635         }
636
637 //      return something if read mode...
638 }
639
640
641 void FloppyDrive::ControlMotor(uint8_t addr)
642 {
643         // $C0E8 - 9
644         motorOn = addr;
645 }
646
647
648 void FloppyDrive::DriveEnable(uint8_t addr)
649 {
650         // $C0EA - B
651         activeDrive = addr;
652 }
653
654
655 uint8_t FloppyDrive::ReadWrite(void)
656 {
657 SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
658         (ioMode == IO_MODE_READ ? "Read" : "Write"),
659         (ioMode == IO_MODE_READ ? "from" : "to"), track, currentPos / 396);
660         // $C0EC
661 /*
662 I think what happens here is that once a track is read its nybblized form
663 is fed through here, one byte at a time--which means for DO disks, we have
664 to convert the actual 256 byte sector to a 416 byte nybblized data "sector".
665 Which we now do. :-)
666 */
667         if (ioMode == IO_MODE_WRITE && (latchValue & 0x80))
668         {
669                 // Does it behave like this?
670 #warning "Write protection kludged in--investigate real behavior!"
671                 if (!writeProtected[activeDrive])
672                 {
673                         nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
674                         imageDirty[activeDrive] = true;
675                 }
676                 else
677 //doesn't seem to do anything
678                         return 0;//is this more like it?
679         }
680
681         uint8_t diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
682         currentPos = (currentPos + 1) % 6656;
683
684 //WriteLog("FL: diskByte=%02X, currentPos=%u\n", diskByte, currentPos);
685         return diskByte;
686 }
687
688
689 uint8_t FloppyDrive::GetLatchValue(void)
690 {
691         // $C0ED
692         return latchValue;
693 }
694
695
696 void FloppyDrive::SetLatchValue(uint8_t value)
697 {
698         // $C0ED
699         latchValue = value;
700 }
701
702
703 void FloppyDrive::SetReadMode(void)
704 {
705         // $C0EE
706         ioMode = IO_MODE_READ;
707 }
708
709
710 void FloppyDrive::SetWriteMode(void)
711 {
712         // $C0EF
713         ioMode = IO_MODE_WRITE;
714 }
715
716 /*
717 PRODOS 8 MLI ERROR CODES
718
719 $00:    No error
720 $01:    Bad system call number
721 $04:    Bad system call parameter count
722 $25:    Interrupt table full
723 $27:    I/O error
724 $28:    No device connected
725 $2B:    Disk write protected
726 $2E:    Disk switched
727 $40:    Invalid pathname
728 $42:    Maximum number of files open
729 $43:    Invalid reference number
730 $44:    Directory not found
731 $45:    Volume not found
732 $46:    File not found
733 $47:    Duplicate filename
734 $48:    Volume full
735 $49:    Volume directory full
736 $4A:    Incompatible file format, also a ProDOS directory
737 $4B:    Unsupported storage_type
738 $4C:    End of file encountered
739 $4D:    Position out of range
740 $4E:    File access error, also file locked
741 $50:    File is open
742 $51:    Directory structure damaged
743 $52:    Not a ProDOS volume
744 $53:    Invalid system call parameter
745 $55:    Volume Control Block table full
746 $56:    Bad buffer address
747 $57:    Duplicate volume
748 $5A:    File structure damaged
749 */