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