]> Shamusworld >> Repos - apple2/blob - src/floppydrive.cpp
9f8aecd2000894475b90cdd9e6ac279abf2fbab4
[apple2] / src / floppydrive.cpp
1 //
2 // Apple 2 floppy disk support
3 //
4 // by James Hammons
5 // (c) 2005-2018 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 "floppydrive.h"
17
18 #include <stdio.h>
19 #include <string.h>
20 #include "apple2.h"
21 #include "crc32.h"
22 #include "firmware.h"
23 #include "log.h"
24 #include "mmu.h"
25 #include "video.h"              // For message spawning... Though there's probably a
26                                                 // better approach than this!
27
28 // Useful enums
29
30 enum { IO_MODE_READ, IO_MODE_WRITE };
31
32 // FloppyDrive class variable initialization
33
34 uint8_t FloppyDrive::doSector[16] = {
35         0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
36 uint8_t FloppyDrive::poSector[16] = {
37         0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
38 uint8_t FloppyDrive::wozHeader[9] = "WOZ1\xFF\x0A\x0D\x0A";
39 uint8_t FloppyDrive::wozHeader2[9] = "WOZ2\xFF\x0A\x0D\x0A";
40 uint8_t FloppyDrive::standardTMAP[141] = {
41         0, 0, 0xFF, 1, 1, 1, 0xFF, 2, 2, 2, 0xFF, 3, 3, 3, 0xFF, 4, 4, 4, 0xFF,
42         5, 5, 5, 0xFF, 6, 6, 6, 0xFF, 7, 7, 7, 0xFF, 8, 8, 8, 0xFF, 9, 9, 9, 0xFF,
43         10, 10, 10, 0xFF, 11, 11, 11, 0xFF, 12, 12, 12, 0xFF, 13, 13, 13, 0xFF,
44         14, 14, 14, 0xFF, 15, 15, 15, 0xFF, 16, 16, 16, 0xFF, 17, 17, 17, 0xFF,
45         18, 18, 18, 0xFF, 19, 19, 19, 0xFF, 20, 20, 20, 0xFF, 21, 21, 21, 0xFF,
46         22, 22, 22, 0xFF, 23, 23, 23, 0xFF, 24, 24, 24, 0xFF, 25, 25, 25, 0xFF,
47         26, 26, 26, 0xFF, 27, 27, 27, 0xFF, 28, 28, 28, 0xFF, 29, 29, 29, 0xFF,
48         30, 30, 30, 0xFF, 31, 31, 31, 0xFF, 32, 32, 32, 0xFF, 33, 33, 33, 0xFF,
49         34, 34, 34, 0xFF, 0xFF, 0xFF
50 };
51 uint8_t FloppyDrive::bitMask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
52 uint8_t FloppyDrive::sequencerROM[256] = {
53 0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
54 0x2D, 0x38, 0x2D, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
55 0x38, 0x28, 0xD8, 0x08, 0x0A, 0x0A, 0x0A, 0x0A, 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B,
56 0x48, 0x48, 0xD8, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
57 0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
58 0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
59 0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
60 0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88,
61 0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
62 0x29, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
63 0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB,
64 0x59, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
65 0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
66 0x08, 0xE8, 0xD8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
67 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
68 0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08
69 };
70
71 char FloppyDrive::nameBuf[MAX_PATH];
72
73
74 // Static in-line functions, for clarity & speed, mostly for reading values out
75 // of the WOZ struct, which stores its data in LE; some for swapping variables
76 static inline uint16_t Uint16LE(uint16_t v)
77 {
78 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
79         return ((v & 0xFF) << 8) | ((v & 0xFF00) >> 8);
80 #else
81         return v;
82 #endif
83 }
84
85
86 static inline uint32_t Uint32LE(uint32_t v)
87 {
88 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
89         return ((v & 0xFF) << 24) | ((v & 0xFF00) << 8)
90                 | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24);
91 #else
92         return v;
93 #endif
94 }
95
96
97 static inline void Swap(uint8_t & a, uint8_t & b)
98 {
99         uint8_t t = a;
100         a = b;
101         b = t;
102 }
103
104
105 static inline void Swap(uint32_t & a, uint32_t & b)
106 {
107         uint32_t t = a;
108         a = b;
109         b = t;
110 }
111
112
113 static inline void Swap(bool & a, bool & b)
114 {
115         bool t = a;
116         a = b;
117         b = t;
118 }
119
120
121 static inline void Swap(uint8_t * & a, uint8_t * & b)
122 {
123         uint8_t * t = a;
124         a = b;
125         b = t;
126 }
127
128
129 static inline void Swap(WOZ * & a, WOZ * & b)
130 {
131         WOZ * t = a;
132         a = b;
133         b = t;
134 }
135
136
137 // FloppyDrive class implementation...
138
139 FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ),  ioHappened(false)
140 {
141         phase[0] = phase[1] = 0;
142         headPos[0] = headPos[1] = 0;
143         trackLength[0] = trackLength[1] = 51200;
144         disk[0] = disk[1] = NULL;
145         woz[0] = woz[1] = NULL;
146         diskSize[0] = diskSize[1] = 0;
147         diskType[0] = diskType[1] = DT_EMPTY;
148         imageDirty[0] = imageDirty[1] = false;
149         imageName[0][0] = imageName[1][0] = 0;                  // Zero out filenames
150 }
151
152
153 FloppyDrive::~FloppyDrive()
154 {
155         if (disk[0])
156                 delete[] disk[0];
157
158         if (disk[1])
159                 delete[] disk[1];
160 }
161
162
163 bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
164 {
165         WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
166
167         if (driveNum > 1)
168         {
169                 WriteLog("FLOPPY: Attempted to load image to drive #%u!\n", driveNum);
170                 return false;
171         }
172
173         // Zero out filename, in case it doesn't load
174         imageName[driveNum][0] = 0;
175
176         FILE * fp = fopen(filename, "rb");
177
178         if (fp == NULL)
179         {
180                 WriteLog("FLOPPY: Failed to open image file '%s' for reading...\n", filename);
181                 return false;
182         }
183
184         if (disk[driveNum])
185                 delete[] disk[driveNum];
186
187         fseek(fp, 0, SEEK_END);
188         diskSize[driveNum] = ftell(fp);
189         fseek(fp, 0, SEEK_SET);
190         disk[driveNum] =  new uint8_t[diskSize[driveNum]];
191         woz[driveNum] = (WOZ *)disk[driveNum];
192         fread(disk[driveNum], 1, diskSize[driveNum], fp);
193
194         fclose(fp);
195 //printf("Read disk image: %u bytes.\n", diskSize);
196         DetectImageType(filename, driveNum);
197         strcpy(imageName[driveNum], filename);
198
199         WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
200
201         return true;
202 }
203
204
205 bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
206 {
207 // comment out for now...
208 #if 0
209         // Various sanity checks...
210         if (driveNum > 1)
211         {
212                 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
213                 return false;
214         }
215
216         if (!imageDirty[driveNum])
217         {
218                 WriteLog("FLOPPY: No need to save unchanged image...\n");
219                 return false;
220         }
221
222         if (imageName[driveNum][0] == 0)
223         {
224                 WriteLog("FLOPPY: Attempted to save non-existant image!\n");
225                 return false;
226         }
227
228         // Finally, write the damn image
229         FILE * fp = fopen(imageName[driveNum], "wb");
230
231         if (fp == NULL)
232         {
233                 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
234                 return false;
235         }
236
237         fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
238         fclose(fp);
239
240         WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
241
242         return true;
243 #else
244         char * ext = strrchr(imageName[driveNum], '.');
245
246         if ((ext != NULL) && (diskType[driveNum] != DT_WOZ))
247                 memcpy(ext, ".woz", 4);
248
249         return SaveWOZ(driveNum);
250 #endif
251 }
252
253
254 bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
255 {
256         strncpy(imageName[driveNum], filename, MAX_PATH);
257         // Ensure a NULL terminated string here, as strncpy() won't terminate the
258         // string if the source length is >= MAX_PATH
259         imageName[driveNum][MAX_PATH - 1] = 0;
260         return SaveImage(driveNum);
261 }
262
263
264 void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
265 {
266         if (disk[driveNum] != NULL)
267                 delete disk[driveNum];
268
269         InitWOZ(driveNum);
270         diskType[driveNum] = DT_WOZ;
271         strcpy(imageName[driveNum], "newblank.woz");
272         SpawnMessage("New blank image inserted in drive %u...", driveNum);
273 }
274
275
276 void FloppyDrive::SwapImages(void)
277 {
278 #if 0
279 WriteLog("SwapImages BEFORE:\n");
280 WriteLog("\tdisk[0]=%X, disk[1]=%X\n", disk[0], disk[1]);
281 WriteLog("\twoz[0]=%X, woz[1]=%X\n", woz[0], woz[1]);
282 WriteLog("\tdiskSize[0]=%X, diskSize[1]=%X\n", diskSize[0], diskSize[1]);
283 WriteLog("\tdiskType[0]=%X, diskType[1]=%X\n", diskType[0], diskType[1]);
284 WriteLog("\timageDirty[0]=%X, imageDirty[1]=%X\n", imageDirty[0], imageDirty[1]);
285 WriteLog("\tphase[0]=%X, phase[1]=%X\n", phase[0], phase[1]);
286 WriteLog("\theadPos[0]=%X, headPos[1]=%X\n", headPos[0], headPos[1]);
287 WriteLog("\tcurrentPos[0]=%X, currentPos[1]=%X\n", currentPos[0], currentPos[1]);
288 #endif
289         char imageNameTmp[MAX_PATH];
290
291         memcpy(imageNameTmp, imageName[0], MAX_PATH);
292         memcpy(imageName[0], imageName[1], MAX_PATH);
293         memcpy(imageName[1], imageNameTmp, MAX_PATH);
294
295         Swap(disk[0], disk[1]);
296         Swap(woz[0], woz[1]);
297         Swap(diskSize[0], diskSize[1]);
298         Swap(diskType[0], diskType[1]);
299         Swap(imageDirty[0], imageDirty[1]);
300
301         Swap(phase[0], phase[1]);
302         Swap(headPos[0], headPos[1]);
303         Swap(currentPos[0], currentPos[1]);
304 SpawnMessage("Drive 0: %s...", imageName[0]);
305 #if 0
306 WriteLog("SwapImages AFTER:\n");
307 WriteLog("\tdisk[0]=%X, disk[1]=%X\n", disk[0], disk[1]);
308 WriteLog("\twoz[0]=%X, woz[1]=%X\n", woz[0], woz[1]);
309 WriteLog("\tdiskSize[0]=%X, diskSize[1]=%X\n", diskSize[0], diskSize[1]);
310 WriteLog("\tdiskType[0]=%X, diskType[1]=%X\n", diskType[0], diskType[1]);
311 WriteLog("\timageDirty[0]=%X, imageDirty[1]=%X\n", imageDirty[0], imageDirty[1]);
312 WriteLog("\tphase[0]=%X, phase[1]=%X\n", phase[0], phase[1]);
313 WriteLog("\theadPos[0]=%X, headPos[1]=%X\n", headPos[0], headPos[1]);
314 WriteLog("\tcurrentPos[0]=%X, currentPos[1]=%X\n", currentPos[0], currentPos[1]);
315 #endif
316 }
317
318
319 /*
320 Need to add some type of error checking here, so we can report back on bad images, etc.
321 */
322 void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
323 {
324         diskType[driveNum] = DFT_UNKNOWN;
325
326         if (memcmp(disk[driveNum], wozHeader, 8) == 0)
327         {
328                 diskType[driveNum] = DT_WOZ;
329                 /*bool r =*/ CheckWOZ(disk[driveNum], diskSize[driveNum], driveNum);
330         }
331         else if (diskSize[driveNum] == 143360)
332         {
333                 const char * ext = strrchr(filename, '.');
334
335                 if (ext == NULL)
336                         return;
337
338                 WriteLog("FLOPPY: Found extension [%s]...\n", ext);
339
340                 if (strcasecmp(ext, ".po") == 0)
341                         diskType[driveNum] = DT_PRODOS;
342                 else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
343                 {
344                         // We assume this, but check for a PRODOS fingerprint. Trust, but
345                         // verify. ;-)
346                         diskType[driveNum] = DT_DOS33;
347
348                         uint8_t fingerprint[4][4] = {
349                                 { 0x00, 0x00, 0x03, 0x00 },             // @ $400
350                                 { 0x02, 0x00, 0x04, 0x00 },             // @ $600
351                                 { 0x03, 0x00, 0x05, 0x00 },             // @ $800
352                                 { 0x04, 0x00, 0x00, 0x00 }              // @ $A00
353                         };
354
355                         bool foundProdos = true;
356
357                         for(uint32_t i=0; i<4; i++)
358                         {
359                                 for(uint32_t j=0; j<4; j++)
360                                 {
361                                         if (disk[driveNum][0x400 + (i * 0x200) + j] != fingerprint[i][j])
362                                         {
363                                                 foundProdos = false;
364                                                 break;
365                                         }
366                                 }
367                         }
368
369                         if (foundProdos)
370                                 diskType[driveNum] = DT_PRODOS;
371                 }
372
373 // Actually, it just might matter WRT to nybblyzing/denybblyzing
374 // (and, it does... :-P)
375                 WOZifyImage(driveNum);
376         }
377         else if (diskSize[driveNum] == 143488)
378         {
379                 diskType[driveNum] = DT_DOS33_HDR;
380                 WOZifyImage(driveNum);
381         }
382
383 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
384 // No, we don't nybblize anymore.  But we should tell the user that the loading failed with a return value
385
386         WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_DOS33 ?
387                 "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
388                 "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : (diskType[driveNum] == DT_WOZ ? "WOZ image" : "unknown")))));
389 }
390
391
392 //
393 // Write a bitstream (source left justified to bit 7) to destination buffer.
394 // Writes 'bits' number of bits to 'dest', starting at bit position 'dstPtr',
395 // updating 'dstPtr' for the caller.
396 //
397 void FloppyDrive::WriteBits(uint8_t * dest, uint8_t * src, uint16_t bits, uint16_t * dstPtr)
398 {
399         for(uint16_t i=0; i<bits; i++)
400         {
401                 // Get the destination location's bitmask
402                 uint8_t dstMask = bitMask[*dstPtr % 8];
403
404                 // Set the bit to one if there's a corresponding one in the source
405                 // data, otherwise set it to zero
406                 if (src[i / 8] & bitMask[i % 8])
407                         dest[*dstPtr / 8] |= dstMask;
408                 else
409                         dest[*dstPtr / 8] &= ~dstMask;
410
411                 (*dstPtr)++;
412         }
413 }
414
415
416 void FloppyDrive::WOZifyImage(uint8_t driveNum)
417 {
418         // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
419         // (not incl. 64 byte track marker)
420 // let's try 394 per sector... & see what happens
421 // let's go back to what we had, and see what happens  :-)
422 // [still need to expand them back to what they were]
423
424         uint8_t ff10[2] = { 0xFF, 0x00 };
425         uint8_t addressHeader[14] = {
426                 0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00,
427                 0x00, 0x00, 0x00, 0xDE, 0xAA, 0xEB };
428         uint8_t sectorHeader[3] = { 0xD5, 0xAA, 0xAD };
429         uint8_t footer[3] = { 0xDE, 0xAA, 0xEB };
430         uint8_t diskbyte[0x40] = {
431                 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
432                 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
433                 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
434                 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
435                 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
436                 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
437                 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
438                 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
439
440         uint8_t tmpNib[343];
441 //      memcpy(tmpDisk, disk[driveNum], diskSize[driveNum]);
442 //      delete[] disk[driveNum];
443         uint8_t * tmpDisk = disk[driveNum];
444         disk[driveNum] = NULL;//new uint8_t[diskSize[driveNum]];
445
446         // Set up track index...
447 //      memcpy(woz[driveNum]->tmap, standardTMAP, 141);
448         InitWOZ(driveNum);
449
450         // Upconvert data from DSK & friends format to WOZ tracks  :-)
451         for(uint8_t trk=0; trk<35; trk++)
452         {
453                 uint16_t dstBitPtr = 0;
454                 uint8_t * img = woz[driveNum]->track[trk].bits;
455 //already done
456 //              memset(img, 0, 6646);
457
458                 // Write self-sync header bytes (16, should it be 64? Dunno.)
459                 for(int i=0; i<64; i++)
460                         WriteBits(img, ff10, 10, &dstBitPtr);
461
462                 // Write out the following sectors
463                 for(uint8_t sector=0; sector<16; sector++)
464                 {
465                         // Set up the sector address header
466                         addressHeader[5] = ((trk >> 1) & 0x55) | 0xAA;
467                         addressHeader[6] =  (trk       & 0x55) | 0xAA;
468                         addressHeader[7] = ((sector >> 1) & 0x55) | 0xAA;
469                         addressHeader[8] =  (sector       & 0x55) | 0xAA;
470                         addressHeader[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA;
471                         addressHeader[10] = ((trk ^ sector ^ 0xFE)       & 0x55) | 0xAA;
472
473                         WriteBits(img, addressHeader, 14 * 8, &dstBitPtr);
474
475                         // Write 5 self-sync bytes for actual sector header
476                         for(int i=0; i<5; i++)
477                                 WriteBits(img, ff10, 10, &dstBitPtr);
478
479                         // Write sector header (D5 AA AD)
480                         WriteBits(img, sectorHeader, 3 * 8, &dstBitPtr);
481 //                      uint8_t * bytes = disk[driveNum];
482                         uint8_t * bytes = tmpDisk;
483
484 //Need to fix this so it writes the correct sector in the correct place *and* put the correct sector # into the header above as well.  !!! FIX !!!
485                         // Figure out location of sector data in disk image
486                         if (diskType[driveNum] == DT_DOS33)
487                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
488                         else if (diskType[driveNum] == DT_DOS33_HDR)
489                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
490                         else if (diskType[driveNum] == DT_PRODOS)
491                                 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
492                         else
493                                 bytes += (sector * 256) + (trk * 256 * 16);
494
495                         // Convert the 256 8-bit bytes into 342 6-bit bytes.
496                         for(uint16_t i=0; i<0x56; i++)
497                         {
498                                 tmpNib[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
499                                         | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
500                                         | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5)
501                                         | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3)
502                                         | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3)
503                                         | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1);
504                         }
505
506                         tmpNib[0x54] &= 0x3F;
507                         tmpNib[0x55] &= 0x3F;
508                         memcpy(tmpNib + 0x56, bytes, 256);
509
510                         // XOR the data block with itself, offset by one byte, creating a
511                         // 343rd byte which is used as a checksum.
512                         tmpNib[342] = 0x00;
513
514                         for(uint16_t i=342; i>0; i--)
515                                 tmpNib[i] = tmpNib[i] ^ tmpNib[i - 1];
516
517                         // Using a lookup table, convert the 6-bit bytes into disk bytes.
518                         for(uint16_t i=0; i<343; i++)
519                                 tmpNib[i] = diskbyte[tmpNib[i] >> 2];
520
521                         WriteBits(img, tmpNib, 343 * 8, &dstBitPtr);
522
523                         // Done with the nybblization, now add the epilogue...
524                         WriteBits(img, footer, 3 * 8, &dstBitPtr);
525
526                         // (Should the footer be 30 or 48? would be 45 FF10s here for 48)
527                         for(int i=0; i<27; i++)
528                                 WriteBits(img, ff10, 10, &dstBitPtr);
529                 }
530
531                 // Set the proper bit/byte lengths in the WOZ for this track
532                 woz[driveNum]->track[trk].bitCount = Uint16LE(dstBitPtr);
533                 woz[driveNum]->track[trk].byteCount = Uint16LE((dstBitPtr + 7) / 8);
534         }
535
536         delete[] tmpDisk;
537 }
538
539
540 const char * FloppyDrive::ImageName(uint8_t driveNum/*= 0*/)
541 {
542         // Set up a zero-length string for return value
543         nameBuf[0] = 0;
544
545         if (driveNum > 1)
546         {
547                 WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
548                 return nameBuf;
549         }
550
551         // Now we attempt to strip out extraneous paths/extensions to get just the filename
552         const char * startOfFile = strrchr(imageName[driveNum], '/');
553         const char * startOfExt = strrchr(imageName[driveNum], '.');
554
555         // If there isn't a path, assume we're starting at the beginning
556         if (startOfFile == NULL)
557                 startOfFile = &imageName[driveNum][0];
558         else
559                 startOfFile++;
560
561         // If there isn't an extension, assume it's at the terminating NULL
562         if (startOfExt == NULL)
563                 startOfExt = &imageName[driveNum][0] + strlen(imageName[driveNum]);
564
565         // Now copy the filename (may copy nothing!)
566         int j = 0;
567
568         for(const char * i=startOfFile; i<startOfExt; i++)
569                 nameBuf[j++] = *i;
570
571         nameBuf[j] = 0;
572
573         return nameBuf;
574 }
575
576
577 void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
578 {
579         // Sanity check
580         if (diskType[driveNum] == DT_EMPTY)
581                 return;
582
583         // Probably want to save a dirty image... ;-)
584         if (SaveImage(driveNum))
585                 WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
586
587         if (disk[driveNum])
588                 delete[] disk[driveNum];
589
590         disk[driveNum] = NULL;
591         woz[driveNum] = NULL;
592         diskSize[driveNum] = 0;
593         diskType[driveNum] = DT_EMPTY;
594         imageDirty[driveNum] = false;
595         imageName[driveNum][0] = 0;                     // Zero out filenames
596 }
597
598
599 bool FloppyDrive::IsEmpty(uint8_t driveNum/*= 0*/)
600 {
601         if (driveNum > 1)
602         {
603                 WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
604                 return true;
605         }
606
607         return (diskType[driveNum] == DT_EMPTY ? true : false);
608 }
609
610
611 bool FloppyDrive::IsWriteProtected(uint8_t driveNum/*= 0*/)
612 {
613         if (driveNum > 1)
614         {
615                 WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
616                 return true;
617         }
618
619         return (bool)woz[driveNum]->writeProtected;
620 }
621
622
623 void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
624 {
625         if (driveNum > 1)
626         {
627                 WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
628                 return;
629         }
630
631         woz[driveNum]->writeProtected = (uint8_t)state;
632 }
633
634
635 int FloppyDrive::DriveLightStatus(uint8_t driveNum/*= 0*/)
636 {
637         int retval = DLS_OFF;
638
639         if (activeDrive != driveNum)
640                 return DLS_OFF;
641
642         if (ioHappened)
643                 retval = (ioMode == IO_MODE_READ ? DLS_READ : DLS_WRITE);
644
645         ioHappened = false;
646         return retval;
647 }
648
649
650 void FloppyDrive::SaveState(FILE * file)
651 {
652         // Internal state vars
653         fputc(motorOn, file);
654         fputc(activeDrive, file);
655         fputc(ioMode, file);
656         fputc(dataRegister, file);
657         fputc((ioHappened ? 1 : 0), file);
658
659         // Disk #1
660         if (disk[0] != NULL)
661         {
662                 WriteLong(file, diskSize[0]);
663                 WriteLong(file, diskType[0]);
664                 fputc(phase[0], file);
665                 fputc(headPos[0], file);
666                 WriteLong(file, currentPos[0]);
667                 fputc((imageDirty[0] ? 1 : 0), file);
668                 fwrite(disk[0], 1, diskSize[0], file);
669                 fwrite(imageName[0], 1, MAX_PATH, file);
670         }
671         else
672                 WriteLong(file, 0);
673
674         // Disk #2
675         if (disk[1] != NULL)
676         {
677                 WriteLong(file, diskSize[1]);
678                 WriteLong(file, diskType[1]);
679                 fputc(phase[1], file);
680                 fputc(headPos[1], file);
681                 WriteLong(file, currentPos[1]);
682                 fputc((imageDirty[1] ? 1 : 0), file);
683                 fwrite(disk[1], 1, diskSize[1], file);
684                 fwrite(imageName[1], 1, MAX_PATH, file);
685         }
686         else
687                 WriteLong(file, 0);
688 }
689
690
691 void FloppyDrive::LoadState(FILE * file)
692 {
693         // Eject images if they're loaded
694         EjectImage(0);
695         EjectImage(1);
696
697         // Read internal state variables
698         motorOn = fgetc(file);
699         activeDrive = fgetc(file);
700         ioMode = fgetc(file);
701         dataRegister = fgetc(file);
702         ioHappened = (fgetc(file) == 1 ? true : false);
703
704         diskSize[0] = ReadLong(file);
705
706         if (diskSize[0])
707         {
708                 disk[0] = new uint8_t[diskSize[0]];
709                 diskType[0] = (uint8_t)ReadLong(file);
710                 phase[0] = fgetc(file);
711                 headPos[0] = fgetc(file);
712                 currentPos[0] = ReadLong(file);
713                 imageDirty[0] = (fgetc(file) == 1 ? true : false);
714                 fread(disk[0], 1, diskSize[0], file);
715                 fread(imageName[0], 1, MAX_PATH, file);
716                 woz[0] = (WOZ *)disk[0];
717         }
718
719         diskSize[1] = ReadLong(file);
720
721         if (diskSize[1])
722         {
723                 disk[1] = new uint8_t[diskSize[1]];
724                 diskType[1] = (uint8_t)ReadLong(file);
725                 phase[1] = fgetc(file);
726                 headPos[1] = fgetc(file);
727                 currentPos[1] = ReadLong(file);
728                 imageDirty[1] = (fgetc(file) == 1 ? true : false);
729                 fread(disk[1], 1, diskSize[1], file);
730                 fread(imageName[1], 1, MAX_PATH, file);
731                 woz[1] = (WOZ *)disk[1];
732         }
733 }
734
735
736 uint32_t FloppyDrive::ReadLong(FILE * file)
737 {
738         uint32_t r = 0;
739
740         for(int i=0; i<4; i++)
741                 r = (r << 8) | fgetc(file);
742
743         return r;
744 }
745
746
747 void FloppyDrive::WriteLong(FILE * file, uint32_t l)
748 {
749         for(int i=0; i<4; i++)
750         {
751                 fputc((l >> 24) & 0xFF, file);
752                 l = l << 8;
753         }
754 }
755
756
757 void FloppyDrive::WriteLongLE(FILE * file, uint32_t l)
758 {
759         for(int i=0; i<4; i++)
760         {
761                 fputc(l & 0xFF, file);
762                 l >>= 8;
763         }
764 }
765
766
767 void FloppyDrive::WriteWordLE(FILE * file, uint16_t w)
768 {
769         fputc(w & 0xFF, file);
770         fputc((w >> 8) & 0xFF, file);
771 }
772
773
774 void FloppyDrive::WriteZeroes(FILE * file, uint32_t num)
775 {
776         for(uint32_t i=0; i<num; i++)
777                 fputc(0, file);
778 }
779
780
781 // Memory mapped I/O functions + Logic State Sequencer
782
783 /*
784 The DSK format is a byte-for-byte image of a 16-sector Apple II floppy disk: 35
785 tracks of 16 sectors of 256 bytes each, making 143,360 bytes in total. The PO
786 format is exactly the same size as DSK and is also organized as 35 sequential
787 tracks, but the sectors within each track are in a different sequence. The NIB
788 format is a nybblized format: a more direct representation of the disk's data
789 as encoded by the Apple II floppy drive hardware. NIB contains 35 tracks of
790 6656 bytes each, for a total size of 232,960 bytes. Although this format is
791 much larger, it is also more versatile and can represent the older 13-sector
792 disks, many copy-protected disks, and other unusual encodings.
793
794 N.B.: Though the NIB format is *closer* to the representation of the disk's
795       data, it's not *quite* 100% as there can be zero bits lurking in the
796       interstices of the bytes written to the disk.  There's room for another
797       format that takes this into account (possibly even take phase 1 & 3
798       tracks into account as well).
799
800       As luck would have it, not long after I wrote that, I found out that some enterprising people have created it already--WOZ format.  Which is now supported by apple2.  :-D
801
802 According to Beneath Apple DOS, DOS checks the data register to see if it changes when spinning up a drive: "A sufficient delay should be provided to allow the motor time to come up to speed.  Shugart recommends one second, but DOS is able to reduce this delay by watching the read latch until data starts to change."  Which means, we can simulate an empty/off drive by leaving the data register alone.
803 */
804
805 uint64_t stepperTime = 0;
806 bool seenReadSinceStep = false;
807 uint16_t iorAddr;
808 void FloppyDrive::ControlStepper(uint8_t addr)
809 {
810         // $C0E0 - 7
811 /*
812 How It Works
813 ------------
814 The stepper motor has 4 phase solenoids (numbered 0-3) which corresponds to bits 1-2 of the address.  Bit 0 tells the phase solenoid to either energize (1) or de-energize (0).  By energizing the phase solenoids in ascending order, the stepper motor moves the head from a low numbered track to a higher numbered track; conversely, by energizing the solenoids in descending order, the stepper motor moves the head from a high numbered track to a lower one.  Given that this is a mechanical device, it takes a certain amount of time for the drum in the stepper motor to move from place to place--though pretty much all software written for the Disk II takes this into account.
815
816 Tracks can apparently go from 0 to 79, though typically only 0 to 69 are usuable.  Further, because of the limitations of the read/write head of the drive, not every track can be written to, so typically (about 99.99% of the time in my guesstimation) only every *other* track is written to (phases 0 and 2); some disks exist that have tracks written on phase 1 or 3, but these tend to be the exception rather than the rule.
817
818 Taking into account the head slew time: ATM nothing seems to look at it, though it could be problematic as how we emulate it is different from how it actually works; namely, the emulator zaps the head to a new track instantly when the write to the phase happens while in the real thing, obviously this takes a non-zero amount of time.  As such, none of the states where more than one phase solenoid is active at a time can be written so that they come on instantaneously; it would be fairly easy to write things that work on the real thing that don't on the emulator because of this.  But most software (pretty much everything that I've ever seen) is pretty well behaved and this isn't an issue.
819
820 If it ever *does* become a problem, doing the physical modeling of the head moving at a real velocity shouldn't be that difficult to do.
821 */
822
823         // This is an array of stub positions crossed with solenoid energize
824         // patterns.  The numbers represent how many quarter tracks the head will
825         // move given its current position and the pattern of energized solenoids.
826         // N.B.: Patterns for 11 & 13 haven't been filled in as I'm not sure how
827         //       the stub would react to those patterns.  :-/
828         int16_t step[16][8] = {
829                 {  0,  0,  0,  0,  0,  0,  0,  0 },  // [....]
830                 {  0, -1, -2,  0,  0,  0, +2, +1 },  // [|...]
831                 { +2, +1,  0, -1, -2,  0,  0,  0 },  // [.|..]
832                 { +1,  0, -1, -2, -3,  0, +3, +2 },  // [||..]
833                 {  0,  0, +2, +1,  0, -1, -2,  0 },  // [..|.]
834                 {  0, -1,  0, +1,  0, -1,  0, +1 },  // [|.|.]
835                 { +3, +2, +1,  0, -1, -2, -3,  0 },  // [.||.]
836                 { +2, +1,  0, -1, -2, -3,  0, +3 },  // [|||.]
837                 { -2,  0,  0,  0, +2, +1,  0, -1 },  // [...|]
838                 { -1, -2, -3,  0, +3, +2, +1,  0 },  // [|..|]
839                 {  0, +1,  0, -1,  0, +1,  0, -1 },  // [.|.|]
840                 {  0,  0,  0,  0,  0,  0,  0,  0 },  // [||.|] ???
841                 { -3,  0, +3, +2, +1,  0, -1, -2 },  // [..||]
842                 {  0,  0,  0,  0,  0,  0,  0,  0 },  // [|.||] ???
843                 {  0, +3, +2, +1,  0, -1, -2, -3 },  // [.|||]
844                 { -1, +2, +1,  0, -1, -2, +1,  0 }   // [||||]
845         };
846
847         // Sanity check
848         if (diskType[activeDrive] == DT_EMPTY)
849                 return;
850
851         // Convert phase solenoid number into a bit from 1 through 8 [1, 2, 4, 8]:
852         uint8_t phaseBit = 1 << ((addr >> 1) & 0x03);
853
854         // Set the state of the phase solenoid accessed using the phase bit
855         if (addr & 0x01)
856                 phase[activeDrive] |= phaseBit;
857         else
858                 phase[activeDrive] &= ~phaseBit;
859
860 #if 1
861         uint8_t oldHeadPos = headPos[activeDrive] & 0x07;
862         int16_t newStep = step[phase[activeDrive]][oldHeadPos];
863         int16_t newHeadPos = (int16_t)headPos[activeDrive] + newStep;
864
865         // Sanity check
866         if ((newHeadPos >= 0) && (newHeadPos <= 140))
867                 headPos[activeDrive] = (uint8_t)newHeadPos;
868 #else
869         // See if the new phase solenoid is energized, & move the stepper/head
870         // appropriately.
871         // N.B.: The head stub is located by bits 1 & 2 of the headPos variable
872         uint8_t oldHeadPos = headPos[activeDrive];
873         uint8_t nextUp     = 1 << (((oldHeadPos >> 1) + 1) & 0x03);
874         uint8_t nextDown   = 1 << (((oldHeadPos >> 1) - 1) & 0x03);
875
876         // We simulate cogging here by seeing if there's a valid up and/or down
877         // position to go to.  If both are valid, the head goes nowhere.
878         if (phase[activeDrive] & nextUp)
879                 headPos[activeDrive] += (headPos[activeDrive] < 140 ? 2 : 0);
880
881         if (phase[activeDrive] & nextDown)
882                 headPos[activeDrive] -= (headPos[activeDrive] > 0 ? 2 : 0);
883 #endif
884
885         if (oldHeadPos != headPos[activeDrive])
886         {
887                 uint8_t newTIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
888                 float newBitLen = (newTIdx == 0xFF ? 51200.0f
889                         : Uint16LE(woz[activeDrive]->track[newTIdx].bitCount));
890
891                 uint8_t oldTIdx = woz[activeDrive]->tmap[oldHeadPos];
892                 float oldBitLen = (oldTIdx == 0xFF ? 51200.0f
893                         : Uint16LE(woz[activeDrive]->track[oldTIdx].bitCount));
894                 currentPos[activeDrive] = (uint32_t)((float)currentPos[activeDrive] * (newBitLen / oldBitLen));
895
896                 trackLength[activeDrive] = (uint16_t)newBitLen;
897                 SpawnMessage("Stepping to track %u...", headPos[activeDrive] >> 2);
898         }
899
900 // only check the time since the phase was first set ON
901 if (addr & 0x01)
902 {
903         stepperTime = mainCPU.clock;
904         seenReadSinceStep = false;
905 }
906 WriteLog("FLOPPY: Stepper phase %d set to %s [%c%c%c%c] (track=%2.2f) [%u]\n", (addr >> 1) & 0x03, (addr & 0x01 ? "ON " : "off"), (phase[activeDrive] & 0x08 ? '|' : '.'), (phase[activeDrive] & 0x04 ? '|' : '.'), (phase[activeDrive] & 0x02 ? '|' : '.'), (phase[activeDrive] & 0x01 ? '|' : '.'), (float)headPos[activeDrive] / 4.0f, mainCPU.clock & 0xFFFFFFFF);
907 }
908
909
910 void FloppyDrive::ControlMotor(uint8_t addr)
911 {
912         // $C0E8 - 9
913         motorOn = addr;
914
915         if (motorOn)
916                 readPulse = 0;
917         else
918                 driveOffTimeout = 2000000;
919
920 WriteLog("FLOPPY: Turning drive motor %s\n", (motorOn ? "ON" : "off"));
921 }
922
923
924 void FloppyDrive::DriveEnable(uint8_t addr)
925 {
926         // $C0EA - B
927         activeDrive = addr;
928 WriteLog("FLOPPY: Selecting drive #%hhd\n", addr + 1);
929 }
930
931
932 /*
933 So for $C08C-F, we have two switches (Q6 & Q7) which combine to make four states ($C-D is off/on for Q6, $E-F is off/on for Q7).
934
935 So it forms a matrix like so:
936
937        $C08E                        $C08F
938       +-----------------------------------------------------------------------
939 $C08C |Enable READ sequencing      |Data reg SHL every 8th clock while writing
940       +----------------------------+------------------------------------------
941 $C08D |Check write prot./init write|Data reg LOAD every 8th clk while writing
942
943 Looks like reads from even addresses in $C080-F block transfer data from the sequencer to the MPU, does write from odd do the inverse (transfer from MPU to sequencer)?  Looks like it.
944
945 */
946
947
948 void FloppyDrive::SetShiftLoadSwitch(uint8_t state)
949 {
950         // $C0EC - D
951         slSwitch = state;
952 }
953
954
955 void FloppyDrive::SetReadWriteSwitch(uint8_t state)
956 {
957         // $C0EE - F
958         rwSwitch = state;
959 }
960
961
962 // MMIO: Reads from $C08x to $C0XX on even addresses
963 uint8_t FloppyDrive::DataRegister(void)
964 {
965         // Sanity check
966         if (diskType[activeDrive] != DT_EMPTY)
967         {
968                 uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
969                 uint32_t bitLen = (tIdx == 0xFF ? 51200
970                         : Uint16LE(woz[activeDrive]->track[tIdx].bitCount));
971                 SpawnMessage("%u:Reading $%02X from track %u, sector %u...",
972                         activeDrive, dataRegister, headPos[activeDrive] >> 2, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f));
973                 ioMode = IO_MODE_READ;
974                 ioHappened = true;
975
976 if ((seenReadSinceStep == false) && (slSwitch == false) && (rwSwitch == false) && ((iorAddr & 0x0F) == 0x0C))
977 {
978         seenReadSinceStep = true;
979         WriteLog("%u:Reading $%02X from track %u, sector %u (delta since seek: %lu cycles) [%u]...\n",
980                 activeDrive, dataRegister, headPos[activeDrive] >> 2, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f), mainCPU.clock - stepperTime, mainCPU.clock & 0xFFFFFFFF);
981 }
982         }
983
984         return dataRegister;
985 }
986
987
988 // MMIO: Writes from $C08x to $C0XX on odd addresses
989 void FloppyDrive::DataRegister(uint8_t data)
990 {
991         cpuDataBus = data;
992         ioMode = IO_MODE_WRITE;
993         ioHappened = true;
994 }
995
996
997 /*
998         OFF switches                ON switches
999 Switch  Addr   Func                 Addr   Func
1000 Q0      $C080  Phase 0 off          $C081  Phase 0 on
1001 Q1      $C082  Phase 1 off          $C083  Phase 1 on
1002 Q2      $C084  Phase 2 off          $C085  Phase 2 on
1003 Q3      $C086  Phase 3 off          $C087  Phase 3 on
1004 Q4      $C088  Drive off            $C089  Drive on
1005 Q5      $C08A  Select Drive 1       $C08B  Select Drive 2
1006 Q6      $C08C  Shift data register  $C08D  Load data register
1007 Q7      $C08E  Read                 $C08F  Write
1008
1009 From "Beneath Apple ProDOS", description of combinations of $C0EC-EF
1010
1011 $C08C, $C08E: Enable read sequencing
1012 $C08C, $C08F: Shift data register every four cycles while writing
1013 $C08D, $C08E: Check write protect and initialize sequencer for writing
1014 $C08D, $C08F: Load data register every four cycles while writing
1015
1016
1017 Sense Write Protect:
1018
1019         LDX #SLOT               Put slot number times 16 in X-register.
1020         LDA $C08D, X
1021         LDA $C08E, X    Sense write protect.
1022         BMI ERROR               If high bit set, protected.
1023
1024 */
1025
1026 /*
1027 PRODOS 8 MLI ERROR CODES
1028
1029 $00:    No error
1030 $01:    Bad system call number
1031 $04:    Bad system call parameter count
1032 $25:    Interrupt table full
1033 $27:    I/O error
1034 $28:    No device connected
1035 $2B:    Disk write protected
1036 $2E:    Disk switched
1037 $40:    Invalid pathname
1038 $42:    Maximum number of files open
1039 $43:    Invalid reference number
1040 $44:    Directory not found
1041 $45:    Volume not found
1042 $46:    File not found
1043 $47:    Duplicate filename
1044 $48:    Volume full
1045 $49:    Volume directory full
1046 $4A:    Incompatible file format, also a ProDOS directory
1047 $4B:    Unsupported storage_type
1048 $4C:    End of file encountered
1049 $4D:    Position out of range
1050 $4E:    File access error, also file locked
1051 $50:    File is open
1052 $51:    Directory structure damaged
1053 $52:    Not a ProDOS volume
1054 $53:    Invalid system call parameter
1055 $55:    Volume Control Block table full
1056 $56:    Bad buffer address
1057 $57:    Duplicate volume
1058 $5A:    File structure damaged
1059 */
1060
1061
1062 //
1063 // This is used mainly to initialize blank disks and upconvert non-WOZ disks
1064 //
1065 void FloppyDrive::InitWOZ(uint8_t driveNum/*= 0*/)
1066 {
1067         // Sanity check
1068         if (disk[driveNum] != NULL)
1069         {
1070                 WriteLog("FLOPPY: Attempted to initialize non-NULL WOZ structure\n");
1071                 return;
1072         }
1073
1074         diskSize[driveNum] = 256 + (35 * sizeof(WOZTrack));
1075         disk[driveNum] = new uint8_t[diskSize[driveNum]];
1076         woz[driveNum] = (WOZ *)disk[driveNum];
1077
1078         // Zero out WOZ image in memory
1079         memset(woz[driveNum], 0, diskSize[driveNum]);
1080
1081         // Set up header (leave CRC as 0 for now)
1082         memcpy(woz[driveNum]->magic, wozHeader, 8);
1083
1084         // INFO header
1085         memcpy(woz[driveNum]->infoTag, "INFO", 4);
1086         woz[driveNum]->infoSize = Uint32LE(60);
1087         woz[driveNum]->infoVersion = 1;
1088         woz[driveNum]->diskType = 1;
1089         woz[driveNum]->writeProtected = 0;
1090         woz[driveNum]->synchronized = 0;
1091         woz[driveNum]->cleaned = 1;
1092         memset(woz[driveNum]->creator, ' ', 32);
1093         memcpy(woz[driveNum]->creator, "Apple2 emulator v1.0.0", 22);
1094
1095         // TMAP header
1096         memcpy(woz[driveNum]->tmapTag, "TMAP", 4);
1097         woz[driveNum]->tmapSize = Uint32LE(160);
1098         memcpy(woz[driveNum]->tmap, standardTMAP, 141);
1099
1100         // TRKS header
1101         memcpy(woz[driveNum]->trksTag, "TRKS", 4);
1102         woz[driveNum]->trksSize = Uint32LE(35 * sizeof(WOZTrack));
1103
1104         for(int i=0; i<35; i++)
1105         {
1106                 woz[driveNum]->track[i].bitCount = Uint16LE(51200);
1107                 woz[driveNum]->track[i].byteCount = Uint16LE((51200 + 7) / 8);
1108         }
1109
1110         // META header (how to handle? prolly with a separate pointer)
1111 }
1112
1113
1114 //
1115 // Do basic sanity checks on the passed in contents (file loaded elsewhere).
1116 // Returns true if successful, false on failure.
1117 //
1118 bool FloppyDrive::CheckWOZ(const uint8_t * wozData, uint32_t wozSize, uint8_t driveNum/*= 0*/)
1119 {
1120         // Hey!  This reference works!!  :-D
1121         WOZ & woz1 = *((WOZ *)wozData);
1122         woz[driveNum] = (WOZ *)wozData;
1123
1124         // Basic sanity checking
1125         if (wozData == NULL)
1126         {
1127                 WriteLog("FLOPPY: NULL pointer passed in to CheckWOZ()...\n");
1128                 return false;
1129         }
1130
1131         if (memcmp(woz1.magic, wozHeader, 8) != 0)
1132         {
1133                 WriteLog("FLOPPY: Invalid WOZ header in file\n");
1134                 return false;
1135         }
1136
1137         uint32_t crc = CRC32(&wozData[12], wozSize - 12);
1138         uint32_t wozCRC = Uint32LE(woz1.crc32);
1139
1140         if ((wozCRC != 0) && (wozCRC != crc))
1141         {
1142                 WriteLog("FLOPPY: Corrupted data found in WOZ. CRC32: %08X, computed: %08X\n", wozCRC, crc);
1143                 return false;
1144         }
1145         else if (wozCRC == 0)
1146                 WriteLog("FLOPPY: Warning--WOZ file has no CRC...\n");
1147
1148 #if 1
1149         WriteLog("Track map:\n");
1150         WriteLog("                                        1   1   1   1   1   1   1   1\n");
1151         WriteLog("0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.\n");
1152         WriteLog("------------------------------------------------------------------------\n");
1153
1154         for(uint8_t j=0; j<2; j++)
1155         {
1156                 for(uint8_t i=0; i<72; i++)
1157                 {
1158                         char buf[64] = "..";
1159                         buf[0] = buf[1] = '.';
1160
1161                         if (woz1.tmap[i] != 0xFF)
1162                                 sprintf(buf, "%02d", woz1.tmap[i]);
1163
1164                         WriteLog("%c", buf[j]);
1165                 }
1166
1167                 WriteLog("\n");
1168         }
1169
1170         WriteLog("\n1   1   2   2   2   2   2   2   2   2   2   2   3   3   3   3   3   3\n");
1171         WriteLog("8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5\n");
1172         WriteLog("---------------------------------------------------------------------\n");
1173
1174         for(uint8_t j=0; j<2; j++)
1175         {
1176                 for(uint8_t i=72; i<141; i++)
1177                 {
1178                         char buf[64] = "..";
1179
1180                         if (woz1.tmap[i] != 0xFF)
1181                                 sprintf(buf, "%02d", woz1.tmap[i]);
1182
1183                         WriteLog("%c", buf[j]);
1184                 }
1185
1186                 WriteLog("\n");
1187         }
1188
1189         WriteLog("\n");
1190
1191         uint8_t numTracks = woz1.trksSize / sizeof(WOZTrack);
1192
1193         // N.B.: Need to check the track[] to have this tell the correct track...  Right now, it doesn't
1194         for(uint8_t i=0; i<numTracks; i++)
1195         {
1196                 WriteLog("WOZ: Track %2.2f: %d bits (packed into %d bytes)\n", (float)i / 4.0f, woz1.track[i].bitCount, woz1.track[i].byteCount);
1197         }
1198 #endif
1199
1200         WriteLog("FLOPPY: Well formed WOZ file found\n");
1201         return true;
1202 }
1203
1204
1205 bool FloppyDrive::SaveWOZ(uint8_t driveNum)
1206 {
1207         // Various sanity checks...
1208         if (driveNum > 1)
1209         {
1210                 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
1211                 return false;
1212         }
1213
1214         if (diskType[driveNum] == DT_EMPTY)
1215         {
1216                 WriteLog("FLOPPY: No image in drive #%u to save\n", driveNum);
1217                 return false;
1218         }
1219
1220         if (!imageDirty[driveNum])
1221         {
1222                 WriteLog("FLOPPY: No need to save unchanged image in drive #%u...\n", driveNum);
1223                 return false;
1224         }
1225
1226         // Set up CRC32 before writing
1227         woz[driveNum]->crc32 = Uint32LE(CRC32(&disk[driveNum][12], diskSize[driveNum] - 12));
1228
1229         // META header (skip for now) (actually, should be in the disk[] image already)
1230
1231         // Finally, write the damn image
1232         FILE * fp = fopen(imageName[driveNum], "wb");
1233
1234         if (fp == NULL)
1235         {
1236                 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
1237                 return false;
1238         }
1239
1240         fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
1241         fclose(fp);
1242
1243         WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
1244
1245         return true;
1246 }
1247
1248
1249 // N.B.: The WOZ documentation says that the bitstream is normalized to 4µs.
1250 //       Which means on the //e that you would have to run it at that clock
1251 //       rate (instead of the //e clock rate 0.9799µs/cycle) to get the
1252 //       simulated drive running at 300 RPM.  So, instead of doing that, we're
1253 //       just gonna run it at twice the clock rate of the base 6502 clock,
1254 //       which will make the simulated drive run in the neighborhood of around
1255 //       306 RPM.  Should be close enough to get away with it.  :-)  (And it
1256 //       seems to run OK, for the most part.)
1257
1258
1259 static bool logSeq = false;
1260 //
1261 // Logic State Sequencer & Data Register
1262 //
1263 void FloppyDrive::RunSequencer(uint32_t cyclesToRun)
1264 {
1265         static uint32_t prng = 1;
1266
1267         // Sanity checks
1268         if (diskType[activeDrive] == DT_EMPTY)
1269                 return;
1270         else if (motorOn == false)
1271         {
1272                 if (driveOffTimeout == 0)
1273                         return;
1274                 else
1275                         driveOffTimeout--;
1276         }
1277
1278         // It's x2 because the sequencer clock runs twice as fast as the CPU clock.
1279         cyclesToRun *= 2;
1280
1281 //extern bool dumpDis;
1282 //static bool tripwire = false;
1283 uint8_t chop = 0;
1284 //static uint32_t lastPos = 0;
1285 if (logSeq)
1286 {
1287         WriteLog("DISKSEQ: Running for %d cycles [rw=%hhd, sl=%hhd, reg=%02X, bus=%02X]\n", cyclesToRun, rwSwitch, slSwitch, dataRegister, cpuDataBus);
1288 }
1289
1290         while (cyclesToRun-- > 0)
1291         {
1292                 pulseClock = (pulseClock + 1) & 0x07;
1293
1294                 if (pulseClock == 0)
1295                 {
1296                         uint16_t bytePos = currentPos[activeDrive] / 8;
1297                         uint8_t bitPos = currentPos[activeDrive] % 8;
1298                         uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
1299
1300                         if (tIdx != 0xFF)
1301                         {
1302                                 if (woz[activeDrive]->track[tIdx].bits[bytePos] & bitMask[bitPos])
1303                                 {
1304                                         // According to Jim Sather (Understanding the Apple II),
1305                                         // the Read Pulse, when it happens, is 1µs long, which is 2
1306                                         // sequencer clock pulses long.
1307                                         readPulse = 2;
1308                                         zeroBitCount = 0;
1309                                 }
1310                                 else
1311                                         zeroBitCount++;
1312 #if 0
1313                                 currentPos[activeDrive] = (currentPos[activeDrive] + 1) % Uint16LE(woz[activeDrive]->track[tIdx].bitCount);
1314                         }
1315                         else
1316                                 currentPos[activeDrive] = (currentPos[activeDrive] + 1) % 51200;
1317 #else
1318                         }
1319
1320 //this doesn't work reliably for some reason...
1321 //seems to work OK now...
1322                         currentPos[activeDrive] = (currentPos[activeDrive] + 1) % trackLength[activeDrive];
1323 #endif
1324
1325                         // If we hit more than 2 zero bits in a row, simulate the disk head
1326                         // reader's Automatic Gain Control (AGC) turning itself up too high
1327                         // by stuffing random bits in the bitstream.  We also do this if
1328                         // the current track is marked as unformatted.
1329 /*
1330 N.B.: Had to up this to 3 because Up N' Down had some weird sync bytes (FE10).  May have to up it some more.
1331 */
1332                         if ((zeroBitCount > 3) || (tIdx == 0xFF))
1333                         {
1334                                 if (prng & 0x00001)
1335                                 {
1336                                         // This PRNG is called the "Galois configuration".
1337                                         prng ^= 0x24000;
1338                                         readPulse = 2;
1339                                 }
1340
1341                                 prng >>= 1;
1342                         }
1343                 }
1344
1345                 // Find and run the Sequencer's next state
1346                 uint8_t nextState = (sequencerState & 0xF0) | (rwSwitch << 3)
1347                         | (slSwitch << 2) | (readPulse ? 0x02 : 0)
1348                         | ((dataRegister & 0x80) >> 7);
1349 if (logSeq)
1350         WriteLog("[%02X:%02X]%s", sequencerState, nextState, (chop == 15 ? "\n" : ""));
1351 chop = (chop + 1) % 20;
1352                 sequencerState = sequencerROM[nextState];
1353
1354                 switch (sequencerState & 0x0F)
1355                 {
1356                 case 0x00:
1357                 case 0x01:
1358                 case 0x02:
1359                 case 0x03:
1360                 case 0x04:
1361                 case 0x05:
1362                 case 0x06:
1363                 case 0x07:
1364                         // CLR (clear data register)
1365                         dataRegister = 0;
1366                         break;
1367                 case 0x08:
1368                 case 0x0C:
1369                         // NOP (no operation)
1370                         break;
1371                 case 0x09:
1372                         // SL0 (shift left, 0 fill LSB)
1373                         dataRegister <<= 1;
1374 //if (!stopWriting)
1375 {
1376                         uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
1377
1378                         if (rwSwitch && (tIdx != 0xFF)
1379                                 && !woz[activeDrive]->writeProtected)
1380                         {
1381                                 imageDirty[activeDrive] = true;
1382                                 uint16_t bytePos = currentPos[activeDrive] / 8;
1383                                 uint8_t bitPos = currentPos[activeDrive] % 8;
1384
1385                                 if (dataRegister & 0x80)
1386                                         // Fill in the one, if necessary
1387                                         woz[activeDrive]->track[tIdx].bits[bytePos] |= bitMask[bitPos];
1388                                 else
1389                                         // Otherwise, punch in the zero
1390                                         woz[activeDrive]->track[tIdx].bits[bytePos] &= ~bitMask[bitPos];
1391
1392 #if 0
1393 if (dumpDis || tripwire)
1394 {
1395 tripwire = true;
1396 WriteLog("[%s]", (dataRegister & 0x80 ? "1" : "0"));
1397 if (lastPos == currentPos[activeDrive])
1398         WriteLog("{STOMP}");
1399 else if ((lastPos + 1) != currentPos[activeDrive])
1400         WriteLog("{LAG}");
1401 lastPos = currentPos[activeDrive];
1402 }
1403 #endif
1404                         }
1405 }
1406                         break;
1407                 case 0x0A:
1408                 case 0x0E:
1409                         // SR (shift right write protect bit)
1410                         dataRegister >>= 1;
1411                         dataRegister |= (woz[activeDrive]->writeProtected ? 0x80 : 0x00);
1412                         break;
1413                 case 0x0B:
1414                 case 0x0F:
1415                         // LD (load data register from data bus)
1416                         dataRegister = cpuDataBus;
1417 //if (!stopWriting)
1418 {
1419                         uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
1420
1421                         if (rwSwitch && (tIdx != 0xFF)
1422                                 && !woz[activeDrive]->writeProtected)
1423                         {
1424                                 imageDirty[activeDrive] = true;
1425                                 uint16_t bytePos = currentPos[activeDrive] / 8;
1426                                 uint8_t bitPos = currentPos[activeDrive] % 8;
1427                                 woz[activeDrive]->track[tIdx].bits[bytePos] |= bitMask[bitPos];
1428 #if 0
1429 if (dumpDis || tripwire)
1430 {
1431 tripwire = true;
1432 WriteLog("[%s]", (dataRegister & 0x80 ? "1" : "0"));
1433 if (lastPos == currentPos[activeDrive])
1434         WriteLog("{STOMP}");
1435 else if ((lastPos + 1) != currentPos[activeDrive])
1436         WriteLog("{LAG}");
1437 lastPos = currentPos[activeDrive];
1438 }
1439 #endif
1440                         }
1441 }
1442                         break;
1443                 case 0x0D:
1444                         // SL1 (shift left, 1 fill LSB)
1445                         dataRegister <<= 1;
1446                         dataRegister |= 0x01;
1447                         break;
1448                 }
1449
1450                 if (readPulse > 0)
1451                         readPulse--;
1452         }
1453
1454 if (logSeq)
1455         WriteLog("\n");
1456 }
1457
1458
1459 FloppyDrive floppyDrive[2];
1460
1461 static uint8_t SlotIOR(uint16_t address)
1462 {
1463         uint8_t state = address & 0x0F;
1464
1465         switch (state)
1466         {
1467         case 0x00:
1468         case 0x01:
1469         case 0x02:
1470         case 0x03:
1471         case 0x04:
1472         case 0x05:
1473         case 0x06:
1474         case 0x07:
1475                 floppyDrive[0].ControlStepper(state);
1476                 break;
1477         case 0x08:
1478         case 0x09:
1479                 floppyDrive[0].ControlMotor(state & 0x01);
1480                 break;
1481         case 0x0A:
1482         case 0x0B:
1483                 floppyDrive[0].DriveEnable(state & 0x01);
1484                 break;
1485         case 0x0C:
1486         case 0x0D:
1487                 floppyDrive[0].SetShiftLoadSwitch(state & 0x01);
1488                 break;
1489         case 0x0E:
1490         case 0x0F:
1491                 floppyDrive[0].SetReadWriteSwitch(state & 0x01);
1492                 break;
1493         }
1494
1495 //temp, for debugging
1496 iorAddr = address;
1497         // Even addresses return the data register, odd (we suppose) returns a
1498         // floating bus read...
1499         return (address & 0x01 ? ReadFloatingBus(0) : floppyDrive[0].DataRegister());
1500 }
1501
1502
1503 static void SlotIOW(uint16_t address, uint8_t byte)
1504 {
1505         uint8_t state = address & 0x0F;
1506
1507         switch (state)
1508         {
1509         case 0x00:
1510         case 0x01:
1511         case 0x02:
1512         case 0x03:
1513         case 0x04:
1514         case 0x05:
1515         case 0x06:
1516         case 0x07:
1517                 floppyDrive[0].ControlStepper(state);
1518                 break;
1519         case 0x08:
1520         case 0x09:
1521                 floppyDrive[0].ControlMotor(state & 0x01);
1522                 break;
1523         case 0x0A:
1524         case 0x0B:
1525                 floppyDrive[0].DriveEnable(state & 0x01);
1526                 break;
1527         case 0x0C:
1528         case 0x0D:
1529                 floppyDrive[0].SetShiftLoadSwitch(state & 0x01);
1530                 break;
1531         case 0x0E:
1532         case 0x0F:
1533                 floppyDrive[0].SetReadWriteSwitch(state & 0x01);
1534                 break;
1535         }
1536
1537         // Odd addresses write to the Data register, even addresses (we assume) go
1538         // into the ether
1539         if (state & 0x01)
1540                 floppyDrive[0].DataRegister(byte);
1541 }
1542
1543
1544 // This slot function doesn't need to differentiate between separate instances
1545 // of FloppyDrive
1546 static uint8_t SlotROM(uint16_t address)
1547 {
1548         return diskROM[address];
1549 }
1550
1551
1552 void InstallFloppy(uint8_t slot)
1553 {
1554         SlotData disk = { SlotIOR, SlotIOW, SlotROM, 0, 0, 0 };
1555         InstallSlotHandler(slot, &disk);
1556 }
1557