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