]> Shamusworld >> Repos - apple2/blob - src/floppydrive.cpp
67fa079ba90bd40b41c1d29a845315d176f980af
[apple2] / src / floppydrive.cpp
1 //
2 // Apple 2 floppy disk support
3 //
4 // by James Hammons
5 // (c) 2005-2019 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 "fileio.h"
23 #include "firmware.h"
24 #include "log.h"
25 #include "mmu.h"
26 #include "video.h"              // For message spawning... Though there's probably a
27                                                 // better approach than this!
28
29 // Useful enums
30
31 enum { IO_MODE_READ, IO_MODE_WRITE };
32
33 // Misc. arrays (read only) that are needed
34
35 static const uint8_t bitMask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
36 static const uint8_t sequencerROM[256] = {
37         0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
38         0x2D, 0x38, 0x2D, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
39         0x38, 0x28, 0xD8, 0x08, 0x0A, 0x0A, 0x0A, 0x0A, 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B,
40         0x48, 0x48, 0xD8, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
41         0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
42         0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
43         0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
44         0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88,
45         0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
46         0x29, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
47         0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB,
48         0x59, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
49         0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
50         0x08, 0xE8, 0xD8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
51         0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
52         0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08
53 };
54
55 static char nameBuf[MAX_PATH];
56
57
58 // Static in-line functions, for clarity & speed, for swapping variables
59 static inline void Swap(uint8_t & a, uint8_t & b)
60 {
61         uint8_t t = a;
62         a = b;
63         b = t;
64 }
65
66
67 static inline void Swap(uint32_t & a, uint32_t & b)
68 {
69         uint32_t t = a;
70         a = b;
71         b = t;
72 }
73
74
75 static inline void Swap(bool & a, bool & b)
76 {
77         bool t = a;
78         a = b;
79         b = t;
80 }
81
82
83 static inline void Swap(uint8_t * & a, uint8_t * & b)
84 {
85         uint8_t * t = a;
86         a = b;
87         b = t;
88 }
89
90
91 // FloppyDrive class implementation...
92
93 FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ),  ioHappened(false), diskImageReady(false)
94 {
95         phase[0] = phase[1] = 0;
96         headPos[0] = headPos[1] = 0;
97         trackLength[0] = trackLength[1] = 51200;
98         disk[0] = disk[1] = NULL;
99 //      woz[0] = woz[1] = NULL;
100         diskSize[0] = diskSize[1] = 0;
101         diskType[0] = diskType[1] = DT_EMPTY;
102         imageDirty[0] = imageDirty[1] = false;
103         imageName[0][0] = imageName[1][0] = 0;                  // Zero out filenames
104 }
105
106
107 FloppyDrive::~FloppyDrive()
108 {
109         if (disk[0])
110                 free(disk[0]);
111
112         if (disk[1])
113                 free(disk[1]);
114 }
115
116
117 bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
118 {
119         WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
120
121         if (driveNum > 1)
122         {
123                 WriteLog("FLOPPY: Attempted to load image to drive #%u!\n", driveNum);
124                 return false;
125         }
126
127         // Zero out filename, in case it doesn't load
128         imageName[driveNum][0] = 0;
129 //prolly should load EjectImage() first, so we don't have to dick around with crap
130         uint8_t * buffer = ReadFile(filename, &diskSize[driveNum]);
131
132         if (buffer == NULL)
133         {
134                 WriteLog("FLOPPY: Failed to open image file '%s' for reading...\n", filename);
135                 return false;
136         }
137
138         if (disk[driveNum])
139                 free(disk[driveNum]);
140
141         disk[driveNum] = buffer;
142
143         diskImageReady = false;
144         DetectImageType(filename, driveNum);
145         strcpy(imageName[driveNum], filename);
146         diskImageReady = true;
147
148         WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
149
150         return true;
151 }
152
153
154 bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
155 {
156         // Various sanity checks...
157         if (driveNum > 1)
158         {
159                 WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
160                 return false;
161         }
162
163         if (diskType[driveNum] == DT_EMPTY)
164         {
165                 WriteLog("FLOPPY: No image in drive #%u to save\n", driveNum);
166                 return false;
167         }
168
169         if (!imageDirty[driveNum])
170         {
171                 WriteLog("FLOPPY: No need to save unchanged image in drive #%u...\n", driveNum);
172                 return false;
173         }
174
175         char * ext = strrchr(imageName[driveNum], '.');
176
177         if ((ext != NULL) && (diskType[driveNum] != DT_WOZ))
178                 memcpy(ext, ".woz", 4);
179
180         return SaveWOZ(imageName[driveNum], (WOZ2 *)disk[driveNum], diskSize[driveNum]);
181 }
182
183
184 bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
185 {
186         strncpy(imageName[driveNum], filename, MAX_PATH);
187         // Ensure a NULL terminated string here, as strncpy() won't terminate the
188         // string if the source length is >= MAX_PATH
189         imageName[driveNum][MAX_PATH - 1] = 0;
190         return SaveImage(driveNum);
191 }
192
193
194 void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
195 {
196         if (disk[driveNum] != NULL)
197                 free(disk[driveNum]);
198
199         disk[driveNum] = InitWOZ(&diskSize[driveNum]);
200         diskType[driveNum] = DT_WOZ;
201         strcpy(imageName[driveNum], "newblank.woz");
202         SpawnMessage("New blank image inserted in drive %u...", driveNum);
203 }
204
205
206 void FloppyDrive::SwapImages(void)
207 {
208         char imageNameTmp[MAX_PATH];
209
210         memcpy(imageNameTmp, imageName[0], MAX_PATH);
211         memcpy(imageName[0], imageName[1], MAX_PATH);
212         memcpy(imageName[1], imageNameTmp, MAX_PATH);
213
214         Swap(disk[0], disk[1]);
215         Swap(diskSize[0], diskSize[1]);
216         Swap(diskType[0], diskType[1]);
217         Swap(imageDirty[0], imageDirty[1]);
218
219         Swap(phase[0], phase[1]);
220         Swap(headPos[0], headPos[1]);
221         Swap(currentPos[0], currentPos[1]);
222 SpawnMessage("Drive 0: %s...", imageName[0]);
223 }
224
225
226 /*
227 Need to add some type of error checking here, so we can report back on bad images, etc. (basically, it does by returning DFT_UNKNOWN, but we could do better)
228 */
229 void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
230 {
231         diskType[driveNum] = DFT_UNKNOWN;
232
233         uint8_t wozType = CheckWOZType(disk[driveNum], diskSize[driveNum]);
234
235         if (wozType > 0)
236         {
237                 // Check WOZ integrity...
238                 CheckWOZIntegrity(disk[driveNum], diskSize[driveNum]);
239
240                 // If it's a WOZ type 1 file, upconvert it to type 2
241                 if (wozType == 1)
242                 {
243                         uint32_t size;
244                         uint8_t * buffer = UpconvertWOZ1ToWOZ2(disk[driveNum], diskSize[driveNum], &size);
245
246                         free(disk[driveNum]);
247                         disk[driveNum] = buffer;
248                         diskSize[driveNum] = size;
249                         WriteLog("FLOPPY: Upconverted WOZ type 1 to type 2...\n");
250                 }
251
252                 diskType[driveNum] = DT_WOZ;
253         }
254         else if (diskSize[driveNum] == 143360)
255         {
256                 const char * ext = strrchr(filename, '.');
257
258                 if (ext == NULL)
259                         return;
260
261                 WriteLog("FLOPPY: Found extension [%s]...\n", ext);
262
263                 if (strcasecmp(ext, ".po") == 0)
264                         diskType[driveNum] = DT_PRODOS;
265                 else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
266                 {
267                         // We assume this, but check for a PRODOS fingerprint.  Trust, but
268                         // verify.  ;-)
269                         diskType[driveNum] = DT_DOS33;
270
271                         uint8_t fingerprint[4][4] = {
272                                 { 0x00, 0x00, 0x03, 0x00 },             // @ $400
273                                 { 0x02, 0x00, 0x04, 0x00 },             // @ $600
274                                 { 0x03, 0x00, 0x05, 0x00 },             // @ $800
275                                 { 0x04, 0x00, 0x00, 0x00 }              // @ $A00
276                         };
277
278                         bool foundProdos = true;
279
280                         for(uint32_t i=0; i<4; i++)
281                         {
282                                 for(uint32_t j=0; j<4; j++)
283                                 {
284                                         if (disk[driveNum][0x400 + (i * 0x200) + j] != fingerprint[i][j])
285                                         {
286                                                 foundProdos = false;
287                                                 break;
288                                         }
289                                 }
290                         }
291
292                         if (foundProdos)
293                                 diskType[driveNum] = DT_PRODOS;
294                 }
295
296 // Actually, it just might matter WRT to nybblyzing/denybblyzing
297 // (and, it does... :-P)
298                 WOZifyImage(driveNum);
299         }
300         else if (diskSize[driveNum] == 143488)
301         {
302                 diskType[driveNum] = DT_DOS33_HDR;
303                 WOZifyImage(driveNum);
304         }
305
306 #warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
307 // No, we don't nybblize anymore.  But we should tell the user that the loading failed with a return value
308
309         WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_DOS33 ?
310                 "DOS 3.3" : (diskType[driveNum] == DT_DOS33_HDR ?
311                 "DOS 3.3 (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS" : (diskType[driveNum] == DT_WOZ ? "WOZ" : "unknown")))));
312 }
313
314
315 //
316 // Write a bitstream (source left justified to bit 7) to destination buffer.
317 // Writes 'bits' number of bits to 'dest', starting at bit position 'dstPtr',
318 // updating 'dstPtr' for the caller.
319 //
320 void FloppyDrive::WriteBits(uint8_t * dest, const uint8_t * src, uint16_t bits, uint16_t * dstPtr)
321 {
322         for(uint16_t i=0; i<bits; i++)
323         {
324                 // Get the destination location's bitmask
325                 uint8_t dstMask = bitMask[*dstPtr % 8];
326
327                 // Set the bit to one if there's a corresponding one in the source
328                 // data, otherwise set it to zero
329                 if (src[i / 8] & bitMask[i % 8])
330                         dest[*dstPtr / 8] |= dstMask;
331                 else
332                         dest[*dstPtr / 8] &= ~dstMask;
333
334                 (*dstPtr)++;
335         }
336 }
337
338
339 void FloppyDrive::WOZifyImage(uint8_t driveNum)
340 {
341         // hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
342         // (not incl. 64 byte track marker)
343 // let's try 394 per sector... & see what happens
344 // let's go back to what we had, and see what happens  :-)
345 // [still need to expand them back to what they were]
346
347         const uint8_t ff10[2] = { 0xFF, 0x00 };
348         uint8_t addressHeader[14] = {
349                 0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00,
350                 0x00, 0x00, 0x00, 0xDE, 0xAA, 0xEB };
351         const uint8_t sectorHeader[3] = { 0xD5, 0xAA, 0xAD };
352         const uint8_t footer[3] = { 0xDE, 0xAA, 0xEB };
353         const uint8_t diskbyte[0x40] = {
354                 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
355                 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
356                 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
357                 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
358                 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
359                 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
360                 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
361                 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
362         const uint8_t doSector[16] = {
363                 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
364         const uint8_t poSector[16] = {
365                 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
366
367         uint8_t tmpNib[343];
368         // Save current image until we're done converting
369         uint8_t * tmpDisk = disk[driveNum];
370
371         // Set up track index...
372         disk[driveNum] = InitWOZ(&diskSize[driveNum]);
373         WOZ2 & woz = *((WOZ2 *)disk[driveNum]);
374
375         // Upconvert data from DSK & friends format to WOZ tracks  :-)
376         for(uint8_t trk=0; trk<35; trk++)
377         {
378                 uint16_t dstBitPtr = 0;
379                 uint8_t * img = disk[driveNum] + (Uint16LE(woz.track[trk].startingBlock) * 512);
380 //printf("Converting track %u: startingBlock=%u, %u blocks, img=%X\n", trk, Uint16LE(woz.track[trk].startingBlock), Uint16LE(woz.track[trk].blockCount), img);
381
382                 // Write self-sync header bytes (16, should it be 64? Dunno.)
383                 for(int i=0; i<64; i++)
384                         WriteBits(img, ff10, 10, &dstBitPtr);
385
386                 // Write out the following sectors
387                 for(uint8_t sector=0; sector<16; sector++)
388                 {
389                         // Set up the sector address header
390                         addressHeader[5] = ((trk >> 1) & 0x55) | 0xAA;
391                         addressHeader[6] =  (trk       & 0x55) | 0xAA;
392                         addressHeader[7] = ((sector >> 1) & 0x55) | 0xAA;
393                         addressHeader[8] =  (sector       & 0x55) | 0xAA;
394                         addressHeader[9] = (((trk ^ sector ^ 0xFE) >> 1) & 0x55) | 0xAA;
395                         addressHeader[10] = ((trk ^ sector ^ 0xFE)       & 0x55) | 0xAA;
396
397                         WriteBits(img, addressHeader, 14 * 8, &dstBitPtr);
398
399                         // Write 5 self-sync bytes for actual sector header
400                         for(int i=0; i<5; i++)
401                                 WriteBits(img, ff10, 10, &dstBitPtr);
402
403                         // Write sector header (D5 AA AD)
404                         WriteBits(img, sectorHeader, 3 * 8, &dstBitPtr);
405                         uint8_t * bytes = tmpDisk;
406
407 //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 !!!
408                         // Figure out location of sector data in disk image
409                         if (diskType[driveNum] == DT_DOS33)
410                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16);
411                         else if (diskType[driveNum] == DT_DOS33_HDR)
412                                 bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
413                         else if (diskType[driveNum] == DT_PRODOS)
414                                 bytes += (poSector[sector] * 256) + (trk * 256 * 16);
415                         else
416                                 bytes += (sector * 256) + (trk * 256 * 16);
417
418                         // Convert the 256 8-bit bytes into 342 6-bit bytes.
419                         for(uint16_t i=0; i<0x56; i++)
420                         {
421                                 tmpNib[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
422                                         | ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
423                                         | ((bytes[(i + 0x56) & 0xFF] & 0x01) << 5)
424                                         | ((bytes[(i + 0x56) & 0xFF] & 0x02) << 3)
425                                         | ((bytes[(i + 0x00) & 0xFF] & 0x01) << 3)
426                                         | ((bytes[(i + 0x00) & 0xFF] & 0x02) << 1);
427                         }
428
429                         tmpNib[0x54] &= 0x3F;
430                         tmpNib[0x55] &= 0x3F;
431                         memcpy(tmpNib + 0x56, bytes, 256);
432
433                         // XOR the data block with itself, offset by one byte, creating a
434                         // 343rd byte which is used as a checksum.
435                         tmpNib[342] = 0x00;
436
437                         for(uint16_t i=342; i>0; i--)
438                                 tmpNib[i] = tmpNib[i] ^ tmpNib[i - 1];
439
440                         // Using a lookup table, convert the 6-bit bytes into disk bytes.
441                         for(uint16_t i=0; i<343; i++)
442                                 tmpNib[i] = diskbyte[tmpNib[i] >> 2];
443
444                         WriteBits(img, tmpNib, 343 * 8, &dstBitPtr);
445
446                         // Done with the nybblization, now add the epilogue...
447                         WriteBits(img, footer, 3 * 8, &dstBitPtr);
448
449                         // (Should the footer be 30 or 48? would be 45 FF10s here for 48)
450                         for(int i=0; i<27; i++)
451                                 WriteBits(img, ff10, 10, &dstBitPtr);
452                 }
453
454                 // Set the proper bit/byte lengths in the WOZ for this track
455                 woz.track[trk].bitCount = Uint16LE(dstBitPtr);
456         }
457
458         // Finally, free the non-WOZ image now that we're done converting
459         free(tmpDisk);
460 }
461
462
463 const char * FloppyDrive::ImageName(uint8_t driveNum/*= 0*/)
464 {
465         // Set up a zero-length string for return value
466         nameBuf[0] = 0;
467
468         if (driveNum > 1)
469         {
470                 WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
471                 return nameBuf;
472         }
473
474         // Now we attempt to strip out extraneous paths/extensions to get just the filename
475         const char * startOfFile = strrchr(imageName[driveNum], '/');
476         const char * startOfExt = strrchr(imageName[driveNum], '.');
477
478         // If there isn't a path, assume we're starting at the beginning
479         if (startOfFile == NULL)
480                 startOfFile = &imageName[driveNum][0];
481         else
482                 startOfFile++;
483
484         // If there isn't an extension, assume it's at the terminating NULL
485         if (startOfExt == NULL)
486                 startOfExt = &imageName[driveNum][0] + strlen(imageName[driveNum]);
487
488         // Now copy the filename (may copy nothing!)
489         int j = 0;
490
491         for(const char * i=startOfFile; i<startOfExt; i++)
492                 nameBuf[j++] = *i;
493
494         nameBuf[j] = 0;
495
496         return nameBuf;
497 }
498
499
500 void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
501 {
502         // Sanity check
503         if (diskType[driveNum] == DT_EMPTY)
504                 return;
505
506         // Probably want to save a dirty image... ;-)
507         if (SaveImage(driveNum))
508                 WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
509
510         if (disk[driveNum])
511                 free(disk[driveNum]);
512
513         disk[driveNum] = NULL;
514 //      woz[driveNum] = NULL;
515         diskSize[driveNum] = 0;
516         diskType[driveNum] = DT_EMPTY;
517         imageDirty[driveNum] = false;
518         imageName[driveNum][0] = 0;                     // Zero out filenames
519 }
520
521
522 bool FloppyDrive::IsEmpty(uint8_t driveNum/*= 0*/)
523 {
524         if (driveNum > 1)
525         {
526                 WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
527                 return true;
528         }
529
530         return (diskType[driveNum] == DT_EMPTY ? true : false);
531 }
532
533
534 bool FloppyDrive::IsWriteProtected(uint8_t driveNum/*= 0*/)
535 {
536         if (driveNum > 1)
537         {
538                 WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
539                 return true;
540         }
541
542         WOZ2 & woz = *((WOZ2 *)disk[driveNum]);
543         return (bool)woz.writeProtected;
544 }
545
546
547 void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
548 {
549         if (driveNum > 1)
550         {
551                 WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
552                 return;
553         }
554
555         WOZ2 & woz = *((WOZ2 *)disk[driveNum]);
556         woz.writeProtected = (uint8_t)state;
557 }
558
559
560 int FloppyDrive::DriveLightStatus(uint8_t driveNum/*= 0*/)
561 {
562         int retval = DLS_OFF;
563
564         if (activeDrive != driveNum)
565                 return DLS_OFF;
566
567         if (ioHappened)
568                 retval = (ioMode == IO_MODE_READ ? DLS_READ : DLS_WRITE);
569
570         ioHappened = false;
571         return retval;
572 }
573
574
575 void FloppyDrive::SaveState(FILE * file)
576 {
577         // Internal state vars
578         fputc(motorOn, file);
579         fputc(activeDrive, file);
580         fputc(ioMode, file);
581         fputc(dataRegister, file);
582         fputc((ioHappened ? 1 : 0), file);
583
584         // Disk #1
585         if (disk[0] != NULL)
586         {
587                 WriteLong(file, diskSize[0]);
588                 WriteLong(file, diskType[0]);
589                 fputc(phase[0], file);
590                 fputc(headPos[0], file);
591                 WriteLong(file, currentPos[0]);
592                 fputc((imageDirty[0] ? 1 : 0), file);
593                 fwrite(disk[0], 1, diskSize[0], file);
594                 fwrite(imageName[0], 1, MAX_PATH, file);
595         }
596         else
597                 WriteLong(file, 0);
598
599         // Disk #2
600         if (disk[1] != NULL)
601         {
602                 WriteLong(file, diskSize[1]);
603                 WriteLong(file, diskType[1]);
604                 fputc(phase[1], file);
605                 fputc(headPos[1], file);
606                 WriteLong(file, currentPos[1]);
607                 fputc((imageDirty[1] ? 1 : 0), file);
608                 fwrite(disk[1], 1, diskSize[1], file);
609                 fwrite(imageName[1], 1, MAX_PATH, file);
610         }
611         else
612                 WriteLong(file, 0);
613 }
614
615
616 void FloppyDrive::LoadState(FILE * file)
617 {
618         // Eject images if they're loaded
619         EjectImage(0);
620         EjectImage(1);
621
622         // Read internal state variables
623         motorOn = fgetc(file);
624         activeDrive = fgetc(file);
625         ioMode = fgetc(file);
626         dataRegister = fgetc(file);
627         ioHappened = (fgetc(file) == 1 ? true : false);
628
629         diskSize[0] = ReadLong(file);
630
631         if (diskSize[0])
632         {
633                 disk[0] = new uint8_t[diskSize[0]];
634                 diskType[0] = (uint8_t)ReadLong(file);
635                 phase[0] = fgetc(file);
636                 headPos[0] = fgetc(file);
637                 currentPos[0] = ReadLong(file);
638                 imageDirty[0] = (fgetc(file) == 1 ? true : false);
639                 fread(disk[0], 1, diskSize[0], file);
640                 fread(imageName[0], 1, MAX_PATH, file);
641         }
642
643         diskSize[1] = ReadLong(file);
644
645         if (diskSize[1])
646         {
647                 disk[1] = new uint8_t[diskSize[1]];
648                 diskType[1] = (uint8_t)ReadLong(file);
649                 phase[1] = fgetc(file);
650                 headPos[1] = fgetc(file);
651                 currentPos[1] = ReadLong(file);
652                 imageDirty[1] = (fgetc(file) == 1 ? true : false);
653                 fread(disk[1], 1, diskSize[1], file);
654                 fread(imageName[1], 1, MAX_PATH, file);
655         }
656 }
657
658
659 uint32_t FloppyDrive::ReadLong(FILE * file)
660 {
661         uint32_t r = 0;
662
663         for(int i=0; i<4; i++)
664                 r = (r << 8) | fgetc(file);
665
666         return r;
667 }
668
669
670 void FloppyDrive::WriteLong(FILE * file, uint32_t l)
671 {
672         for(int i=0; i<4; i++)
673         {
674                 fputc((l >> 24) & 0xFF, file);
675                 l = l << 8;
676         }
677 }
678
679
680 // Memory mapped I/O functions + Logic State Sequencer
681
682 /*
683 The DSK format is a byte-for-byte image of a 16-sector Apple II floppy disk: 35
684 tracks of 16 sectors of 256 bytes each, making 143,360 bytes in total. The PO
685 format is exactly the same size as DSK and is also organized as 35 sequential
686 tracks, but the sectors within each track are in a different sequence. The NIB
687 format is a nybblized format: a more direct representation of the disk's data
688 as encoded by the Apple II floppy drive hardware. NIB contains 35 tracks of
689 6656 bytes each, for a total size of 232,960 bytes. Although this format is
690 much larger, it is also more versatile and can represent the older 13-sector
691 disks, many copy-protected disks, and other unusual encodings.
692
693 N.B.: Though the NIB format is *closer* to the representation of the disk's
694       data, it's not *quite* 100% as there can be zero bits lurking in the
695       interstices of the bytes written to the disk.  There's room for another
696       format that takes this into account (possibly even take phase 1 & 3
697       tracks into account as well).
698
699       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
700
701 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.
702 */
703
704 uint64_t stepperTime = 0;
705 bool seenReadSinceStep = false;
706 uint16_t iorAddr;
707 void FloppyDrive::ControlStepper(uint8_t addr)
708 {
709         // $C0E0 - 7
710 /*
711 How It Works
712 ------------
713 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.
714
715 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.
716
717 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.
718
719 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.
720 */
721
722         // This is an array of stub positions crossed with solenoid energize
723         // patterns.  The numbers represent how many quarter tracks the head will
724         // move given its current position and the pattern of energized solenoids.
725         // N.B.: Patterns for 11 & 13 haven't been filled in as I'm not sure how
726         //       the stub(s) would react to those patterns.  :-/
727         int16_t step[16][8] = {
728                 {  0,  0,  0,  0,  0,  0,  0,  0 },  // [....]
729                 {  0, -1, -2,  0,  0,  0, +2, +1 },  // [|...]
730                 { +2, +1,  0, -1, -2,  0,  0,  0 },  // [.|..]
731                 { +1,  0, -1, -2, -3,  0, +3, +2 },  // [||..]
732                 {  0,  0, +2, +1,  0, -1, -2,  0 },  // [..|.]
733                 {  0, -1,  0, +1,  0, -1,  0, +1 },  // [|.|.]
734                 { +3, +2, +1,  0, -1, -2, -3,  0 },  // [.||.]
735                 { +2, +1,  0, -1, -2, -3,  0, +3 },  // [|||.]
736                 { -2,  0,  0,  0, +2, +1,  0, -1 },  // [...|]
737                 { -1, -2, -3,  0, +3, +2, +1,  0 },  // [|..|]
738                 {  0, +1,  0, -1,  0, +1,  0, -1 },  // [.|.|]
739                 {  0,  0,  0,  0,  0,  0,  0,  0 },  // [||.|] ???
740                 { -3,  0, +3, +2, +1,  0, -1, -2 },  // [..||]
741                 {  0,  0,  0,  0,  0,  0,  0,  0 },  // [|.||] ???
742                 {  0, +3, +2, +1,  0, -1, -2, -3 },  // [.|||]
743                 { -1, +2, +1,  0, -1, -2, +1,  0 }   // [||||]
744         };
745
746         // Sanity check
747         if (diskType[activeDrive] == DT_EMPTY)
748                 return;
749
750         // Convert phase solenoid number into a bit from 1 through 8 [1, 2, 4, 8]:
751         uint8_t phaseBit = 1 << ((addr >> 1) & 0x03);
752
753         // Set the state of the phase solenoid accessed using the phase bit
754         if (addr & 0x01)
755                 phase[activeDrive] |= phaseBit;
756         else
757                 phase[activeDrive] &= ~phaseBit;
758
759 #if 1
760         uint8_t oldHeadPos = headPos[activeDrive] & 0x07;
761         int16_t newStep = step[phase[activeDrive]][oldHeadPos];
762         int16_t newHeadPos = (int16_t)headPos[activeDrive] + newStep;
763
764         // Sanity check
765         if ((newHeadPos >= 0) && (newHeadPos <= 140))
766                 headPos[activeDrive] = (uint8_t)newHeadPos;
767 #else
768         // See if the new phase solenoid is energized, & move the stepper/head
769         // appropriately.
770         // N.B.: The head stub is located by bits 1 & 2 of the headPos variable
771         uint8_t oldHeadPos = headPos[activeDrive];
772         uint8_t nextUp     = 1 << (((oldHeadPos >> 1) + 1) & 0x03);
773         uint8_t nextDown   = 1 << (((oldHeadPos >> 1) - 1) & 0x03);
774
775         // We simulate cogging here by seeing if there's a valid up and/or down
776         // position to go to.  If both are valid, the head goes nowhere.
777         if (phase[activeDrive] & nextUp)
778                 headPos[activeDrive] += (headPos[activeDrive] < 140 ? 2 : 0);
779
780         if (phase[activeDrive] & nextDown)
781                 headPos[activeDrive] -= (headPos[activeDrive] > 0 ? 2 : 0);
782 #endif
783
784         if (oldHeadPos != headPos[activeDrive])
785         {
786                 WOZ2 & woz = *((WOZ2 *)disk[activeDrive]);
787                 uint8_t newTIdx = woz.tmap[headPos[activeDrive]];
788                 float newBitLen = (newTIdx == 0xFF ? 51200.0f
789                         : Uint16LE(woz.track[newTIdx].bitCount));
790
791                 uint8_t oldTIdx = woz.tmap[oldHeadPos];
792                 float oldBitLen = (oldTIdx == 0xFF ? 51200.0f
793                         : Uint16LE(woz.track[oldTIdx].bitCount));
794                 currentPos[activeDrive] = (uint32_t)((float)currentPos[activeDrive] * (newBitLen / oldBitLen));
795
796                 trackLength[activeDrive] = (uint16_t)newBitLen;
797                 SpawnMessage("Stepping to track %u...", headPos[activeDrive] >> 2);
798         }
799
800 // only check the time since the phase was first set ON
801 if (addr & 0x01)
802 {
803         stepperTime = mainCPU.clock;
804         seenReadSinceStep = false;
805 }
806 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);
807 }
808
809
810 void FloppyDrive::ControlMotor(uint8_t addr)
811 {
812         // $C0E8 - 9
813         motorOn = addr;
814
815         if (motorOn)
816                 readPulse = 0;
817         else
818                 driveOffTimeout = 2000000;
819
820 WriteLog("FLOPPY: Turning drive motor %s\n", (motorOn ? "ON" : "off"));
821 }
822
823
824 void FloppyDrive::DriveEnable(uint8_t addr)
825 {
826         // $C0EA - B
827         activeDrive = addr;
828 WriteLog("FLOPPY: Selecting drive #%hhd\n", addr + 1);
829 }
830
831
832 /*
833 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).
834
835 So it forms a matrix like so:
836
837        $C08E                        $C08F
838       +-----------------------------------------------------------------------
839 $C08C |Enable READ sequencing      |Data reg SHL every 8th clock while writing
840       +----------------------------+------------------------------------------
841 $C08D |Check write prot./init write|Data reg LOAD every 8th clk while writing
842
843 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.
844
845 */
846
847
848 void FloppyDrive::SetShiftLoadSwitch(uint8_t state)
849 {
850         // $C0EC - D
851         slSwitch = state;
852 }
853
854
855 void FloppyDrive::SetReadWriteSwitch(uint8_t state)
856 {
857         // $C0EE - F
858         rwSwitch = state;
859 }
860
861
862 // MMIO: Reads from $C08x to $C0XX on even addresses
863 uint8_t FloppyDrive::DataRegister(void)
864 {
865         // Sanity check
866         if (diskType[activeDrive] != DT_EMPTY)
867         {
868                 WOZ2 & woz = *((WOZ2 *)disk[activeDrive]);
869                 uint8_t tIdx = woz.tmap[headPos[activeDrive]];
870                 uint32_t bitLen = (tIdx == 0xFF ? 51200
871                         : Uint16LE(woz.track[tIdx].bitCount));
872                 SpawnMessage("%u:Reading $%02X from track %u, sector %u...",
873                         activeDrive, dataRegister, headPos[activeDrive] >> 2, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f));
874                 ioMode = IO_MODE_READ;
875                 ioHappened = true;
876
877 if ((seenReadSinceStep == false) && (slSwitch == false) && (rwSwitch == false) && ((iorAddr & 0x0F) == 0x0C))
878 {
879         seenReadSinceStep = true;
880         WriteLog("%u:Reading $%02X from track %u, sector %u (delta since seek: %lu cycles) [%u]...\n",
881                 activeDrive, dataRegister, headPos[activeDrive] >> 2, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f), mainCPU.clock - stepperTime, mainCPU.clock & 0xFFFFFFFF);
882 }
883         }
884
885         return dataRegister;
886 }
887
888
889 // MMIO: Writes from $C08x to $C0XX on odd addresses
890 void FloppyDrive::DataRegister(uint8_t data)
891 {
892         cpuDataBus = data;
893         ioMode = IO_MODE_WRITE;
894         ioHappened = true;
895 }
896
897
898 /*
899         OFF switches                ON switches
900 Switch  Addr   Func                 Addr   Func
901 Q0      $C080  Phase 0 off          $C081  Phase 0 on
902 Q1      $C082  Phase 1 off          $C083  Phase 1 on
903 Q2      $C084  Phase 2 off          $C085  Phase 2 on
904 Q3      $C086  Phase 3 off          $C087  Phase 3 on
905 Q4      $C088  Drive off            $C089  Drive on
906 Q5      $C08A  Select Drive 1       $C08B  Select Drive 2
907 Q6      $C08C  Shift data register  $C08D  Load data register
908 Q7      $C08E  Read                 $C08F  Write
909
910 From "Beneath Apple ProDOS", description of combinations of $C0EC-EF
911
912 $C08C, $C08E: Enable read sequencing
913 $C08C, $C08F: Shift data register every four cycles while writing
914 $C08D, $C08E: Check write protect and initialize sequencer for writing
915 $C08D, $C08F: Load data register every four cycles while writing
916
917
918 Sense Write Protect:
919
920         LDX #SLOT               Put slot number times 16 in X-register.
921         LDA $C08D, X
922         LDA $C08E, X    Sense write protect.
923         BMI ERROR               If high bit set, protected.
924
925 */
926
927 /*
928 PRODOS 8 MLI ERROR CODES
929
930 $00:    No error
931 $01:    Bad system call number
932 $04:    Bad system call parameter count
933 $25:    Interrupt table full
934 $27:    I/O error
935 $28:    No device connected
936 $2B:    Disk write protected
937 $2E:    Disk switched
938 $40:    Invalid pathname
939 $42:    Maximum number of files open
940 $43:    Invalid reference number
941 $44:    Directory not found
942 $45:    Volume not found
943 $46:    File not found
944 $47:    Duplicate filename
945 $48:    Volume full
946 $49:    Volume directory full
947 $4A:    Incompatible file format, also a ProDOS directory
948 $4B:    Unsupported storage_type
949 $4C:    End of file encountered
950 $4D:    Position out of range
951 $4E:    File access error, also file locked
952 $50:    File is open
953 $51:    Directory structure damaged
954 $52:    Not a ProDOS volume
955 $53:    Invalid system call parameter
956 $55:    Volume Control Block table full
957 $56:    Bad buffer address
958 $57:    Duplicate volume
959 $5A:    File structure damaged
960 */
961
962 // N.B.: The WOZ documentation says that the bitstream is normalized to 4µs.
963 //       Which means on the //e that you would have to run it at that clock
964 //       rate (instead of the //e clock rate 0.9799µs/cycle) to get the
965 //       simulated drive running at 300 RPM.  So, instead of doing that, we're
966 //       just gonna run it at twice the clock rate of the base 6502 clock,
967 //       which will make the simulated drive run in the neighborhood of around
968 //       306 RPM.  Should be close enough to get away with it.  :-)  (And it
969 //       seems to run OK, for the most part.)
970
971
972 static bool logSeq = false;
973 //
974 // Logic State Sequencer & Data Register
975 //
976 void FloppyDrive::RunSequencer(uint32_t cyclesToRun)
977 {
978         static uint32_t prng = 1;
979
980         // Sanity checks
981         if (!diskImageReady)
982                 return;
983         else if (diskType[activeDrive] == DT_EMPTY)
984                 return;
985         else if (motorOn == false)
986         {
987                 if (driveOffTimeout == 0)
988                         return;
989
990                 driveOffTimeout--;
991         }
992
993         WOZ2 & woz = *((WOZ2 *)disk[activeDrive]);
994         uint8_t tIdx = woz.tmap[headPos[activeDrive]];
995         uint8_t * tdata = disk[activeDrive] + (Uint16LE(woz.track[tIdx].startingBlock) * 512);
996
997         // It's x2 because the sequencer clock runs twice as fast as the CPU clock.
998         cyclesToRun *= 2;
999
1000 //extern bool dumpDis;
1001 //static bool tripwire = false;
1002 uint8_t chop = 0;
1003 //static uint32_t lastPos = 0;
1004 if (logSeq)
1005 {
1006         WriteLog("DISKSEQ: Running for %d cycles [rw=%hhd, sl=%hhd, reg=%02X, bus=%02X]\n", cyclesToRun, rwSwitch, slSwitch, dataRegister, cpuDataBus);
1007 }
1008
1009         while (cyclesToRun-- > 0)
1010         {
1011 //              pulseClock = (pulseClock + 1) & 0x07;
1012                 pulseClock = (pulseClock + 1) % 8;
1013 // 7 doesn't work...  Is that 3.5µs?  Seems to be.  Which means to get a 0.25µs granularity here, we need to double the # of cycles to run...
1014 //              pulseClock = (pulseClock + 1) % 7;
1015
1016                 if (pulseClock == 0)
1017                 {
1018                         uint16_t bytePos = currentPos[activeDrive] / 8;
1019                         uint8_t bitPos = currentPos[activeDrive] % 8;
1020
1021                         if (tIdx != 0xFF)
1022                         {
1023                                 if (tdata[bytePos] & bitMask[bitPos])
1024                                 {
1025                                         // According to Jim Sather (Understanding the Apple II),
1026                                         // the Read Pulse, when it happens, is 1µs long, which is 2
1027                                         // sequencer clock pulses long.
1028                                         readPulse = 2;
1029                                         zeroBitCount = 0;
1030                                 }
1031                                 else
1032                                         zeroBitCount++;
1033                         }
1034
1035                         currentPos[activeDrive] = (currentPos[activeDrive] + 1) % trackLength[activeDrive];
1036
1037                         // If we hit more than 2 zero bits in a row, simulate the disk head
1038                         // reader's Automatic Gain Control (AGC) turning itself up too high
1039                         // by stuffing random bits in the bitstream.  We also do this if
1040                         // the current track is marked as unformatted.
1041 /*
1042 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.
1043 */
1044                         if ((zeroBitCount > 3) || (tIdx == 0xFF))
1045                         {
1046                                 if (prng & 0x00001)
1047                                 {
1048                                         // This PRNG is called the "Galois configuration".
1049                                         prng ^= 0x24000;
1050                                         readPulse = 2;
1051                                 }
1052
1053                                 prng >>= 1;
1054                         }
1055                 }
1056
1057                 // Find and run the Sequencer's next state
1058                 uint8_t nextState = (sequencerState & 0xF0) | (rwSwitch << 3)
1059                         | (slSwitch << 2) | (readPulse ? 0x02 : 0)
1060                         | ((dataRegister & 0x80) >> 7);
1061 if (logSeq)
1062         WriteLog("[%02X:%02X]%s", sequencerState, nextState, (chop == 15 ? "\n" : ""));
1063 chop = (chop + 1) % 20;
1064                 sequencerState = sequencerROM[nextState];
1065
1066                 switch (sequencerState & 0x0F)
1067                 {
1068                 case 0x00:
1069                 case 0x01:
1070                 case 0x02:
1071                 case 0x03:
1072                 case 0x04:
1073                 case 0x05:
1074                 case 0x06:
1075                 case 0x07:
1076                         // CLR (clear data register)
1077                         dataRegister = 0;
1078                         break;
1079                 case 0x08:
1080                 case 0x0C:
1081                         // NOP (no operation)
1082                         break;
1083                 case 0x09:
1084                         // SL0 (shift left, 0 fill LSB)
1085                         dataRegister <<= 1;
1086 //if (!stopWriting)
1087 {
1088                         if (rwSwitch && (tIdx != 0xFF)
1089                                 && !woz.writeProtected)
1090                         {
1091                                 imageDirty[activeDrive] = true;
1092                                 uint16_t bytePos = currentPos[activeDrive] / 8;
1093                                 uint8_t bitPos = currentPos[activeDrive] % 8;
1094
1095                                 if (dataRegister & 0x80)
1096                                         // Fill in the one, if necessary
1097                                         tdata[bytePos] |= bitMask[bitPos];
1098                                 else
1099                                         // Otherwise, punch in the zero
1100                                         tdata[bytePos] &= ~bitMask[bitPos];
1101
1102 #if 0
1103 if (dumpDis || tripwire)
1104 {
1105 tripwire = true;
1106 WriteLog("[%s]", (dataRegister & 0x80 ? "1" : "0"));
1107 if (lastPos == currentPos[activeDrive])
1108         WriteLog("{STOMP}");
1109 else if ((lastPos + 1) != currentPos[activeDrive])
1110         WriteLog("{LAG}");
1111 lastPos = currentPos[activeDrive];
1112 }
1113 #endif
1114                         }
1115 }
1116                         break;
1117                 case 0x0A:
1118                 case 0x0E:
1119                         // SR (shift right write protect bit)
1120                         dataRegister >>= 1;
1121                         dataRegister |= (woz.writeProtected ? 0x80 : 0x00);
1122                         break;
1123                 case 0x0B:
1124                 case 0x0F:
1125                         // LD (load data register from data bus)
1126                         dataRegister = cpuDataBus;
1127 //if (!stopWriting)
1128 {
1129                         if (rwSwitch && (tIdx != 0xFF) && !woz.writeProtected)
1130                         {
1131                                 imageDirty[activeDrive] = true;
1132                                 uint16_t bytePos = currentPos[activeDrive] / 8;
1133                                 uint8_t bitPos = currentPos[activeDrive] % 8;
1134                                 tdata[bytePos] |= bitMask[bitPos];
1135 #if 0
1136 if (dumpDis || tripwire)
1137 {
1138 tripwire = true;
1139 WriteLog("[%s]", (dataRegister & 0x80 ? "1" : "0"));
1140 if (lastPos == currentPos[activeDrive])
1141         WriteLog("{STOMP}");
1142 else if ((lastPos + 1) != currentPos[activeDrive])
1143         WriteLog("{LAG}");
1144 lastPos = currentPos[activeDrive];
1145 }
1146 #endif
1147                         }
1148 }
1149                         break;
1150                 case 0x0D:
1151                         // SL1 (shift left, 1 fill LSB)
1152                         dataRegister <<= 1;
1153                         dataRegister |= 0x01;
1154                         break;
1155                 }
1156
1157                 if (readPulse > 0)
1158                         readPulse--;
1159         }
1160
1161 if (logSeq)
1162         WriteLog("\n");
1163 }
1164
1165
1166 FloppyDrive floppyDrive[2];
1167
1168 static uint8_t SlotIOR(uint16_t address)
1169 {
1170         uint8_t state = address & 0x0F;
1171
1172         switch (state)
1173         {
1174         case 0x00:
1175         case 0x01:
1176         case 0x02:
1177         case 0x03:
1178         case 0x04:
1179         case 0x05:
1180         case 0x06:
1181         case 0x07:
1182                 floppyDrive[0].ControlStepper(state);
1183                 break;
1184         case 0x08:
1185         case 0x09:
1186                 floppyDrive[0].ControlMotor(state & 0x01);
1187                 break;
1188         case 0x0A:
1189         case 0x0B:
1190                 floppyDrive[0].DriveEnable(state & 0x01);
1191                 break;
1192         case 0x0C:
1193         case 0x0D:
1194                 floppyDrive[0].SetShiftLoadSwitch(state & 0x01);
1195                 break;
1196         case 0x0E:
1197         case 0x0F:
1198                 floppyDrive[0].SetReadWriteSwitch(state & 0x01);
1199                 break;
1200         }
1201
1202 //temp, for debugging
1203 iorAddr = address;
1204         // Even addresses return the data register, odd (we suppose) returns a
1205         // floating bus read...
1206         return (address & 0x01 ? ReadFloatingBus(0) : floppyDrive[0].DataRegister());
1207 }
1208
1209
1210 static void SlotIOW(uint16_t address, uint8_t byte)
1211 {
1212         uint8_t state = address & 0x0F;
1213
1214         switch (state)
1215         {
1216         case 0x00:
1217         case 0x01:
1218         case 0x02:
1219         case 0x03:
1220         case 0x04:
1221         case 0x05:
1222         case 0x06:
1223         case 0x07:
1224                 floppyDrive[0].ControlStepper(state);
1225                 break;
1226         case 0x08:
1227         case 0x09:
1228                 floppyDrive[0].ControlMotor(state & 0x01);
1229                 break;
1230         case 0x0A:
1231         case 0x0B:
1232                 floppyDrive[0].DriveEnable(state & 0x01);
1233                 break;
1234         case 0x0C:
1235         case 0x0D:
1236                 floppyDrive[0].SetShiftLoadSwitch(state & 0x01);
1237                 break;
1238         case 0x0E:
1239         case 0x0F:
1240                 floppyDrive[0].SetReadWriteSwitch(state & 0x01);
1241                 break;
1242         }
1243
1244         // Odd addresses write to the Data register, even addresses (we assume) go
1245         // into the ether
1246         if (state & 0x01)
1247                 floppyDrive[0].DataRegister(byte);
1248 }
1249
1250
1251 // This slot function doesn't need to differentiate between separate instances
1252 // of FloppyDrive
1253 static uint8_t SlotROM(uint16_t address)
1254 {
1255         return diskROM[address];
1256 }
1257
1258
1259 void InstallFloppy(uint8_t slot)
1260 {
1261         SlotData disk = { SlotIOR, SlotIOW, SlotROM, 0, 0, 0 };
1262         InstallSlotHandler(slot, &disk);
1263 }
1264