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