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