]> Shamusworld >> Repos - apple2/blob - src/floppydrive.cpp
056ca2b7c5a5b3578eccfba4579c8d85cbfe405a
[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 void FloppyDrive::ControlStepper(uint8_t addr)
806 {
807         // $C0E0 - 7
808 /*
809 How It Works
810 ------------
811 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.
812
813 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.
814 */
815         // Sanity check
816         if (diskType[activeDrive] == DT_EMPTY)
817                 return;
818
819         // Convert phase solenoid number into a bit from 1 through 8:
820         uint8_t phaseBit = 1 << ((addr >> 1) & 0x03);
821
822         // Set the state of the phase solenoid accessed using the phase bit
823         if (addr & 0x01)
824                 phase[activeDrive] |= phaseBit;
825         else
826                 phase[activeDrive] &= ~phaseBit;
827
828         // See if the new phase solenoid is energized, & move the stepper/head
829         // appropriately.
830         // N.B.: The head stub is located by bits 1 & 2 of the headPos variable
831         uint8_t oldHeadPos = headPos[activeDrive];
832         uint8_t nextUp     = 1 << (((oldHeadPos >> 1) + 1) & 0x03);
833         uint8_t nextDown   = 1 << (((oldHeadPos >> 1) - 1) & 0x03);
834
835         // We simulate cogging here by seeing if there's a valid up and/or down
836         // position to go to.  If both are valid, the head goes nowhere.
837         if (phase[activeDrive] & nextUp)
838                 headPos[activeDrive] += (headPos[activeDrive] < 140 ? 2 : 0);
839
840         if (phase[activeDrive] & nextDown)
841                 headPos[activeDrive] -= (headPos[activeDrive] > 0 ? 2 : 0);
842
843         if (oldHeadPos != headPos[activeDrive])
844         {
845                 uint8_t newTIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
846                 float newBitLen = (newTIdx == 0xFF ? 51200.0f
847                         : Uint16LE(woz[activeDrive]->track[newTIdx].bitCount));
848
849                 uint8_t oldTIdx = woz[activeDrive]->tmap[oldHeadPos];
850                 float oldBitLen = (oldTIdx == 0xFF ? 51200.0f
851                         : Uint16LE(woz[activeDrive]->track[oldTIdx].bitCount));
852                 currentPos[activeDrive] = (uint32_t)((float)currentPos[activeDrive] * (newBitLen / oldBitLen));
853
854                 trackLength[activeDrive] = (uint16_t)newBitLen;
855                 SpawnMessage("Stepping to track %u...", headPos[activeDrive] >> 2);
856         }
857
858 WriteLog("FLOPPY: Stepper phase %d set to %s [%c%c%c%c] (track=%2.2f)\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);
859 }
860
861
862 void FloppyDrive::ControlMotor(uint8_t addr)
863 {
864         // $C0E8 - 9
865         motorOn = addr;
866
867         if (motorOn)
868                 readPulse = 0;
869         else
870                 driveOffTimeout = 2000000;
871
872 WriteLog("FLOPPY: Turning drive motor %s\n", (motorOn ? "ON" : "off"));
873 }
874
875
876 void FloppyDrive::DriveEnable(uint8_t addr)
877 {
878         // $C0EA - B
879         activeDrive = addr;
880 WriteLog("FLOPPY: Selecting drive #%hhd\n", addr + 1);
881 }
882
883
884 /*
885 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).
886
887 So it forms a matrix like so:
888
889        $C08E                        $C08F
890       +-----------------------------------------------------------------------
891 $C08C |Enable READ sequencing      |Data reg SHL every 8th clock while writing
892       +----------------------------+------------------------------------------
893 $C08D |Check write prot./init write|Data reg LOAD every 8th clk while writing
894
895 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.
896
897 */
898
899
900 void FloppyDrive::SetShiftLoadSwitch(uint8_t state)
901 {
902         // $C0EC - D
903         slSwitch = state;
904 }
905
906
907 void FloppyDrive::SetReadWriteSwitch(uint8_t state)
908 {
909         // $C0EE - F
910         rwSwitch = state;
911 }
912
913
914 // MMIO: Reads from $C08x to $C0XX on even addresses
915 uint8_t FloppyDrive::DataRegister(void)
916 {
917         // Sanity check
918         if (diskType[activeDrive] != DT_EMPTY)
919         {
920                 uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
921                 uint32_t bitLen = (tIdx == 0xFF ? 51200
922                         : Uint16LE(woz[activeDrive]->track[tIdx].bitCount));
923                 SpawnMessage("%u:Reading $%02X from track %u, sector %u...",
924                         activeDrive, dataRegister, headPos[activeDrive] >> 2, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f));
925                 ioMode = IO_MODE_READ;
926                 ioHappened = true;
927         }
928
929         return dataRegister;
930 }
931
932
933 // MMIO: Writes from $C08x to $C0XX on odd addresses
934 void FloppyDrive::DataRegister(uint8_t data)
935 {
936         cpuDataBus = data;
937         ioMode = IO_MODE_WRITE;
938         ioHappened = true;
939 }
940
941
942 /*
943         OFF switches                ON switches
944 Switch  Addr   Func                 Addr   Func
945 Q0      $C080  Phase 0 off          $C081  Phase 0 on
946 Q1      $C082  Phase 1 off          $C083  Phase 1 on
947 Q2      $C084  Phase 2 off          $C085  Phase 2 on
948 Q3      $C086  Phase 3 off          $C087  Phase 3 on
949 Q4      $C088  Drive off            $C089  Drive on
950 Q5      $C08A  Select Drive 1       $C08B  Select Drive 2
951 Q6      $C08C  Shift data register  $C08D  Load data register
952 Q7      $C08E  Read                 $C08F  Write
953
954 From "Beneath Apple ProDOS", description of combinations of $C0EC-EF
955
956 $C08C, $C08E: Enable read sequencing
957 $C08C, $C08F: Shift data register every four cycles while writing
958 $C08D, $C08E: Check write protect and initialize sequencer for writing
959 $C08D, $C08F: Load data register every four cycles while writing
960
961
962 Sense Write Protect:
963
964         LDX #SLOT               Put slot number times 16 in X-register.
965         LDA $C08D, X
966         LDA $C08E, X    Sense write protect.
967         BMI ERROR               If high bit set, protected.
968
969 */
970
971 /*
972 PRODOS 8 MLI ERROR CODES
973
974 $00:    No error
975 $01:    Bad system call number
976 $04:    Bad system call parameter count
977 $25:    Interrupt table full
978 $27:    I/O error
979 $28:    No device connected
980 $2B:    Disk write protected
981 $2E:    Disk switched
982 $40:    Invalid pathname
983 $42:    Maximum number of files open
984 $43:    Invalid reference number
985 $44:    Directory not found
986 $45:    Volume not found
987 $46:    File not found
988 $47:    Duplicate filename
989 $48:    Volume full
990 $49:    Volume directory full
991 $4A:    Incompatible file format, also a ProDOS directory
992 $4B:    Unsupported storage_type
993 $4C:    End of file encountered
994 $4D:    Position out of range
995 $4E:    File access error, also file locked
996 $50:    File is open
997 $51:    Directory structure damaged
998 $52:    Not a ProDOS volume
999 $53:    Invalid system call parameter
1000 $55:    Volume Control Block table full
1001 $56:    Bad buffer address
1002 $57:    Duplicate volume
1003 $5A:    File structure damaged
1004 */
1005
1006
1007 //
1008 // This is used mainly to initialize blank disks and upconvert non-WOZ disks
1009 //
1010 void FloppyDrive::InitWOZ(uint8_t driveNum/*= 0*/)
1011 {
1012         // Sanity check
1013         if (disk[driveNum] != NULL)
1014         {
1015                 WriteLog("FLOPPY: Attempted to initialize non-NULL WOZ structure\n");
1016                 return;
1017         }
1018
1019         diskSize[driveNum] = 256 + (35 * sizeof(WOZTrack));
1020         disk[driveNum] = new uint8_t[diskSize[driveNum]];
1021         woz[driveNum] = (WOZ *)disk[driveNum];
1022
1023         // Zero out WOZ image in memory
1024         memset(woz[driveNum], 0, diskSize[driveNum]);
1025
1026         // Set up header (leave CRC as 0 for now)
1027         memcpy(woz[driveNum]->magic, wozHeader, 8);
1028
1029         // INFO header
1030         memcpy(woz[driveNum]->infoTag, "INFO", 4);
1031         woz[driveNum]->infoSize = Uint32LE(60);
1032         woz[driveNum]->infoVersion = 1;
1033         woz[driveNum]->diskType = 1;
1034         woz[driveNum]->writeProtected = 0;
1035         woz[driveNum]->synchronized = 0;
1036         woz[driveNum]->cleaned = 1;
1037         memset(woz[driveNum]->creator, ' ', 32);
1038         memcpy(woz[driveNum]->creator, "Apple2 emulator v1.0.0", 22);
1039
1040         // TMAP header
1041         memcpy(woz[driveNum]->tmapTag, "TMAP", 4);
1042         woz[driveNum]->tmapSize = Uint32LE(160);
1043         memcpy(woz[driveNum]->tmap, standardTMAP, 141);
1044
1045         // TRKS header
1046         memcpy(woz[driveNum]->trksTag, "TRKS", 4);
1047         woz[driveNum]->trksSize = Uint32LE(35 * sizeof(WOZTrack));
1048
1049         for(int i=0; i<35; i++)
1050         {
1051                 woz[driveNum]->track[i].bitCount = Uint16LE(51200);
1052                 woz[driveNum]->track[i].byteCount = Uint16LE((51200 + 7) / 8);
1053         }
1054
1055         // META header (how to handle? prolly with a separate pointer)
1056 }
1057
1058
1059 //
1060 // Do basic sanity checks on the passed in contents (file loaded elsewhere).
1061 // Returns true if successful, false on failure.
1062 //
1063 bool FloppyDrive::CheckWOZ(const uint8_t * wozData, uint32_t wozSize, uint8_t driveNum/*= 0*/)
1064 {
1065         // Hey!  This reference works!!  :-D
1066         WOZ & woz1 = *((WOZ *)wozData);
1067         woz[driveNum] = (WOZ *)wozData;
1068
1069         // Basic sanity checking
1070         if (wozData == NULL)
1071         {
1072                 WriteLog("FLOPPY: NULL pointer passed in to CheckWOZ()...\n");
1073                 return false;
1074         }
1075
1076         if (memcmp(woz1.magic, wozHeader, 8) != 0)
1077         {
1078                 WriteLog("FLOPPY: Invalid WOZ header in file\n");
1079                 return false;
1080         }
1081
1082         uint32_t crc = CRC32(&wozData[12], wozSize - 12);
1083         uint32_t wozCRC = Uint32LE(woz1.crc32);
1084
1085         if ((wozCRC != 0) && (wozCRC != crc))
1086         {
1087                 WriteLog("FLOPPY: Corrupted data found in WOZ. CRC32: %08X, computed: %08X\n", wozCRC, crc);
1088                 return false;
1089         }
1090         else if (wozCRC == 0)
1091                 WriteLog("FLOPPY: Warning--WOZ file has no CRC...\n");
1092
1093 #if 1
1094         WriteLog("Track map:\n");
1095         WriteLog("                                        1   1   1   1   1   1   1   1\n");
1096         WriteLog("0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.\n");
1097         WriteLog("------------------------------------------------------------------------\n");
1098
1099         for(uint8_t j=0; j<2; j++)
1100         {
1101                 for(uint8_t i=0; i<72; i++)
1102                 {
1103                         char buf[64] = "..";
1104                         buf[0] = buf[1] = '.';
1105
1106                         if (woz1.tmap[i] != 0xFF)
1107                                 sprintf(buf, "%02d", woz1.tmap[i]);
1108
1109                         WriteLog("%c", buf[j]);
1110                 }
1111
1112                 WriteLog("\n");
1113         }
1114
1115         WriteLog("\n1   1   2   2   2   2   2   2   2   2   2   2   3   3   3   3   3   3\n");
1116         WriteLog("8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5\n");
1117         WriteLog("---------------------------------------------------------------------\n");
1118
1119         for(uint8_t j=0; j<2; j++)
1120         {
1121                 for(uint8_t i=72; i<141; i++)
1122                 {
1123                         char buf[64] = "..";
1124
1125                         if (woz1.tmap[i] != 0xFF)
1126                                 sprintf(buf, "%02d", woz1.tmap[i]);
1127
1128                         WriteLog("%c", buf[j]);
1129                 }
1130
1131                 WriteLog("\n");
1132         }
1133
1134         WriteLog("\n");
1135
1136         uint8_t numTracks = woz1.trksSize / sizeof(WOZTrack);
1137
1138         // N.B.: Need to check the track[] to have this tell the correct track...  Right now, it doesn't
1139         for(uint8_t i=0; i<numTracks; i++)
1140         {
1141                 WriteLog("WOZ: Track %2.2f: %d bits (packed into %d bytes)\n", (float)i / 4.0f, woz1.track[i].bitCount, woz1.track[i].byteCount);
1142         }
1143 #endif
1144
1145         WriteLog("FLOPPY: Well formed WOZ file found\n");
1146         return true;
1147 }
1148
1149
1150 bool FloppyDrive::SaveWOZ(uint8_t driveNum)
1151 {
1152         // Various sanity checks...
1153         if (driveNum > 1)
1154         {
1155                 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
1156                 return false;
1157         }
1158
1159         if (diskType[driveNum] == DT_EMPTY)
1160         {
1161                 WriteLog("FLOPPY: No image in drive #%u to save\n", driveNum);
1162                 return false;
1163         }
1164
1165         if (!imageDirty[driveNum])
1166         {
1167                 WriteLog("FLOPPY: No need to save unchanged image in drive #%u...\n", driveNum);
1168                 return false;
1169         }
1170
1171         // Set up CRC32 before writing
1172         woz[driveNum]->crc32 = Uint32LE(CRC32(&disk[driveNum][12], diskSize[driveNum] - 12));
1173
1174         // META header (skip for now) (actually, should be in the disk[] image already)
1175
1176         // Finally, write the damn image
1177         FILE * fp = fopen(imageName[driveNum], "wb");
1178
1179         if (fp == NULL)
1180         {
1181                 WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
1182                 return false;
1183         }
1184
1185         fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
1186         fclose(fp);
1187
1188         WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
1189
1190         return true;
1191 }
1192
1193
1194 // N.B.: The WOZ documentation says that the bitstream is normalized to 4µs.
1195 //       Which means on the //e that you would have to run it at that clock
1196 //       rate (instead of the //e clock rate 0.9799µs/cycle) to get the
1197 //       simulated drive running at 300 RPM.  So, instead of doing that, we're
1198 //       just gonna run it at twice the clock rate of the base 6502 clock,
1199 //       which will make the simulated drive run in the neighborhood of around
1200 //       306 RPM.  Should be close enough to get away with it.  :-)  (And it
1201 //       seems to run OK, for the most part.)
1202
1203
1204 static bool logSeq = false;
1205 //
1206 // Logic State Sequencer & Data Register
1207 //
1208 void FloppyDrive::RunSequencer(uint32_t cyclesToRun)
1209 {
1210         static uint32_t prng = 1;
1211
1212         // Sanity checks
1213         if (diskType[activeDrive] == DT_EMPTY)
1214                 return;
1215         else if (motorOn == false)
1216         {
1217                 if (driveOffTimeout == 0)
1218                         return;
1219                 else
1220                         driveOffTimeout--;
1221         }
1222
1223         // It's x2 because the sequencer clock runs twice as fast as the CPU clock.
1224         cyclesToRun *= 2;
1225
1226 //extern bool dumpDis;
1227 //static bool tripwire = false;
1228 uint8_t chop = 0;
1229 //static uint32_t lastPos = 0;
1230 if (logSeq)
1231 {
1232         WriteLog("DISKSEQ: Running for %d cycles [rw=%hhd, sl=%hhd, reg=%02X, bus=%02X]\n", cyclesToRun, rwSwitch, slSwitch, dataRegister, cpuDataBus);
1233 }
1234
1235         while (cyclesToRun-- > 0)
1236         {
1237                 pulseClock = (pulseClock + 1) & 0x07;
1238
1239                 if (pulseClock == 0)
1240                 {
1241                         uint16_t bytePos = currentPos[activeDrive] / 8;
1242                         uint8_t bitPos = currentPos[activeDrive] % 8;
1243                         uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
1244
1245                         if (tIdx != 0xFF)
1246                         {
1247                                 if (woz[activeDrive]->track[tIdx].bits[bytePos] & bitMask[bitPos])
1248                                 {
1249                                         // According to Jim Sather (Understanding the Apple II),
1250                                         // the Read Pulse, when it happens, is 1µs long, which is 2
1251                                         // sequencer clock pulses long.
1252                                         readPulse = 2;
1253                                         zeroBitCount = 0;
1254                                 }
1255                                 else
1256                                         zeroBitCount++;
1257 #if 0
1258                                 currentPos[activeDrive] = (currentPos[activeDrive] + 1) % Uint16LE(woz[activeDrive]->track[tIdx].bitCount);
1259                         }
1260                         else
1261                                 currentPos[activeDrive] = (currentPos[activeDrive] + 1) % 51200;
1262 #else
1263                         }
1264
1265 //this doesn't work reliably for some reason...
1266 //seems to work OK now...
1267                         currentPos[activeDrive] = (currentPos[activeDrive] + 1) % trackLength[activeDrive];
1268 #endif
1269
1270                         // If we hit more than 2 zero bits in a row, simulate the disk head
1271                         // reader's Automatic Gain Control (AGC) turning itself up too high
1272                         // by stuffing random bits in the bitstream.  We also do this if
1273                         // the current track is marked as unformatted.
1274 /*
1275 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.
1276 */
1277                         if ((zeroBitCount > 3) || (tIdx == 0xFF))
1278                         {
1279                                 if (prng & 0x00001)
1280                                 {
1281                                         // This PRNG is called the "Galois configuration".
1282                                         prng ^= 0x24000;
1283                                         readPulse = 2;
1284                                 }
1285
1286                                 prng >>= 1;
1287                         }
1288                 }
1289
1290                 // Find and run the Sequencer's next state
1291                 uint8_t nextState = (sequencerState & 0xF0) | (rwSwitch << 3)
1292                         | (slSwitch << 2) | (readPulse ? 0x02 : 0)
1293                         | ((dataRegister & 0x80) >> 7);
1294 if (logSeq)
1295         WriteLog("[%02X:%02X]%s", sequencerState, nextState, (chop == 15 ? "\n" : ""));
1296 chop = (chop + 1) % 20;
1297                 sequencerState = sequencerROM[nextState];
1298
1299                 switch (sequencerState & 0x0F)
1300                 {
1301                 case 0x00:
1302                 case 0x01:
1303                 case 0x02:
1304                 case 0x03:
1305                 case 0x04:
1306                 case 0x05:
1307                 case 0x06:
1308                 case 0x07:
1309                         // CLR (clear data register)
1310                         dataRegister = 0;
1311                         break;
1312                 case 0x08:
1313                 case 0x0C:
1314                         // NOP (no operation)
1315                         break;
1316                 case 0x09:
1317                         // SL0 (shift left, 0 fill LSB)
1318                         dataRegister <<= 1;
1319 //if (!stopWriting)
1320 {
1321                         uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
1322
1323                         if (rwSwitch && (tIdx != 0xFF)
1324                                 && !woz[activeDrive]->writeProtected)
1325                         {
1326                                 imageDirty[activeDrive] = true;
1327                                 uint16_t bytePos = currentPos[activeDrive] / 8;
1328                                 uint8_t bitPos = currentPos[activeDrive] % 8;
1329
1330                                 if (dataRegister & 0x80)
1331                                         // Fill in the one, if necessary
1332                                         woz[activeDrive]->track[tIdx].bits[bytePos] |= bitMask[bitPos];
1333                                 else
1334                                         // Otherwise, punch in the zero
1335                                         woz[activeDrive]->track[tIdx].bits[bytePos] &= ~bitMask[bitPos];
1336
1337 #if 0
1338 if (dumpDis || tripwire)
1339 {
1340 tripwire = true;
1341 WriteLog("[%s]", (dataRegister & 0x80 ? "1" : "0"));
1342 if (lastPos == currentPos[activeDrive])
1343         WriteLog("{STOMP}");
1344 else if ((lastPos + 1) != currentPos[activeDrive])
1345         WriteLog("{LAG}");
1346 lastPos = currentPos[activeDrive];
1347 }
1348 #endif
1349                         }
1350 }
1351                         break;
1352                 case 0x0A:
1353                 case 0x0E:
1354                         // SR (shift right write protect bit)
1355                         dataRegister >>= 1;
1356                         dataRegister |= (woz[activeDrive]->writeProtected ? 0x80 : 0x00);
1357                         break;
1358                 case 0x0B:
1359                 case 0x0F:
1360                         // LD (load data register from data bus)
1361                         dataRegister = cpuDataBus;
1362 //if (!stopWriting)
1363 {
1364                         uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
1365
1366                         if (rwSwitch && (tIdx != 0xFF)
1367                                 && !woz[activeDrive]->writeProtected)
1368                         {
1369                                 imageDirty[activeDrive] = true;
1370                                 uint16_t bytePos = currentPos[activeDrive] / 8;
1371                                 uint8_t bitPos = currentPos[activeDrive] % 8;
1372                                 woz[activeDrive]->track[tIdx].bits[bytePos] |= bitMask[bitPos];
1373 #if 0
1374 if (dumpDis || tripwire)
1375 {
1376 tripwire = true;
1377 WriteLog("[%s]", (dataRegister & 0x80 ? "1" : "0"));
1378 if (lastPos == currentPos[activeDrive])
1379         WriteLog("{STOMP}");
1380 else if ((lastPos + 1) != currentPos[activeDrive])
1381         WriteLog("{LAG}");
1382 lastPos = currentPos[activeDrive];
1383 }
1384 #endif
1385                         }
1386 }
1387                         break;
1388                 case 0x0D:
1389                         // SL1 (shift left, 1 fill LSB)
1390                         dataRegister <<= 1;
1391                         dataRegister |= 0x01;
1392                         break;
1393                 }
1394
1395                 if (readPulse > 0)
1396                         readPulse--;
1397         }
1398
1399 if (logSeq)
1400         WriteLog("\n");
1401 }
1402
1403
1404 FloppyDrive floppyDrive[2];
1405
1406 static uint8_t SlotIOR(uint16_t address)
1407 {
1408         uint8_t state = address & 0x0F;
1409
1410         switch (state)
1411         {
1412         case 0x00:
1413         case 0x01:
1414         case 0x02:
1415         case 0x03:
1416         case 0x04:
1417         case 0x05:
1418         case 0x06:
1419         case 0x07:
1420                 floppyDrive[0].ControlStepper(state);
1421                 break;
1422         case 0x08:
1423         case 0x09:
1424                 floppyDrive[0].ControlMotor(state & 0x01);
1425                 break;
1426         case 0x0A:
1427         case 0x0B:
1428                 floppyDrive[0].DriveEnable(state & 0x01);
1429                 break;
1430         case 0x0C:
1431         case 0x0D:
1432                 floppyDrive[0].SetShiftLoadSwitch(state & 0x01);
1433                 break;
1434         case 0x0E:
1435         case 0x0F:
1436                 floppyDrive[0].SetReadWriteSwitch(state & 0x01);
1437                 break;
1438         }
1439
1440         // Even addresses return the data register, odd (we suppose) returns a
1441         // floating bus read...
1442         return (address & 0x01 ? ReadFloatingBus(0) : floppyDrive[0].DataRegister());
1443 }
1444
1445
1446 static void SlotIOW(uint16_t address, uint8_t byte)
1447 {
1448         uint8_t state = address & 0x0F;
1449
1450         switch (state)
1451         {
1452         case 0x00:
1453         case 0x01:
1454         case 0x02:
1455         case 0x03:
1456         case 0x04:
1457         case 0x05:
1458         case 0x06:
1459         case 0x07:
1460                 floppyDrive[0].ControlStepper(state);
1461                 break;
1462         case 0x08:
1463         case 0x09:
1464                 floppyDrive[0].ControlMotor(state & 0x01);
1465                 break;
1466         case 0x0A:
1467         case 0x0B:
1468                 floppyDrive[0].DriveEnable(state & 0x01);
1469                 break;
1470         case 0x0C:
1471         case 0x0D:
1472                 floppyDrive[0].SetShiftLoadSwitch(state & 0x01);
1473                 break;
1474         case 0x0E:
1475         case 0x0F:
1476                 floppyDrive[0].SetReadWriteSwitch(state & 0x01);
1477                 break;
1478         }
1479
1480         // Odd addresses write to the Data register, even addresses (we assume) go
1481         // into the ether
1482         if (state & 0x01)
1483                 floppyDrive[0].DataRegister(byte);
1484 }
1485
1486
1487 // This slot function doesn't need to differentiate between separate instances
1488 // of FloppyDrive
1489 static uint8_t SlotROM(uint16_t address)
1490 {
1491         return diskROM[address];
1492 }
1493
1494
1495 void InstallFloppy(uint8_t slot)
1496 {
1497         SlotData disk = { SlotIOR, SlotIOW, SlotROM, 0, 0, 0 };
1498         InstallSlotHandler(slot, &disk);
1499 }
1500