Mon, 06 Dec 2010 01:43:04 +0000
fix side-select bug in WDC FDC driver, was causing all reads to occur on side0... now the Loader boots!
Loader will boot, but immediately gives up on the floppy drive... Not sure why.
1 #include <stdint.h>
2 #include <stdbool.h>
3 #include <malloc.h>
4 #include "musashi/m68k.h"
5 #include "wd279x.h"
7 /// WD2797 command constants
8 enum {
9 CMD_MASK = 0xF0, ///< Bit mask to detect command bits
10 CMD_RESTORE = 0x00, ///< Restore (recalibrate, seek to track 0)
11 CMD_SEEK = 0x10, ///< Seek to given track
12 CMD_STEP = 0x20, ///< Step
13 CMD_STEP_TU = 0x30, ///< Step and update track register
14 CMD_STEPIN = 0x40, ///< Step In
15 CMD_STEPIN_TU = 0x50, ///< Step In and update track register
16 CMD_STEPOUT = 0x60, ///< Step Out
17 CMD_STEPOUT_TU = 0x70, ///< Step Out and update track register
18 CMD_READ_SECTOR = 0x80, ///< Read Sector
19 CMD_READ_SECTOR_MULTI = 0x90, ///< Read Multiple Sectors
20 CMD_WRITE_SECTOR = 0xA0, ///< Write Sector
21 CMD_WRITE_SECTOR_MULTI = 0xB0, ///< Write Multiple Sectors
22 CMD_READ_ADDRESS = 0xC0, ///< Read Address (IDAM contents)
23 CMD_FORCE_INTERRUPT = 0xD0, ///< Force Interrupt
24 CMD_READ_TRACK = 0xE0, ///< Read Track
25 CMD_FORMAT_TRACK = 0xF0 ///< Format Track
26 };
29 void wd2797_init(WD2797_CTX *ctx)
30 {
31 // track, head and sector unknown
32 ctx->track = ctx->head = ctx->sector = 0;
34 // no IRQ pending
35 ctx->irql = ctx->irqe = false;
37 // no data available
38 ctx->data_pos = ctx->data_len = 0;
39 ctx->data = NULL;
41 // Status register clear, not busy
42 ctx->status = 0;
44 // Clear data register
45 ctx->data_reg = 0;
47 // Last step direction
48 ctx->last_step_dir = -1;
50 // No disc image loaded
51 ctx->disc_image = NULL;
52 ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = ctx->geom_tracks = 0;
53 }
56 void wd2797_reset(WD2797_CTX *ctx)
57 {
58 // track, head and sector unknown
59 ctx->track = ctx->head = ctx->sector = 0;
61 // no IRQ pending
62 ctx->irql = ctx->irqe = false;
64 // no data available
65 ctx->data_pos = ctx->data_len = 0;
67 // Status register clear, not busy
68 ctx->status = 0;
70 // Clear data register
71 ctx->data_reg = 0;
73 // Last step direction
74 ctx->last_step_dir = -1;
75 }
78 void wd2797_done(WD2797_CTX *ctx)
79 {
80 // Reset the WD2797
81 wd2797_reset(ctx);
83 // Free any allocated memory
84 if (ctx->data) {
85 free(ctx->data);
86 ctx->data = NULL;
87 }
88 }
91 bool wd2797_get_irq(WD2797_CTX *ctx)
92 {
93 // If an IRQ is pending, clear it and return true, otherwise return false
94 if (ctx->irqe) {
95 ctx->irqe = false;
96 return true;
97 } else {
98 return false;
99 }
100 }
103 bool wd2797_get_drq(WD2797_CTX *ctx)
104 {
105 return (ctx->data_pos < ctx->data_len);
106 }
109 WD2797_ERR wd2797_load(WD2797_CTX *ctx, FILE *fp, int secsz, int spt, int heads)
110 {
111 size_t filesize;
113 // Start by finding out how big the image file is
114 fseek(fp, 0, SEEK_END);
115 filesize = ftell(fp);
116 fseek(fp, 0, SEEK_SET);
118 // Now figure out how many tracks it contains
119 int tracks = filesize / secsz / spt / heads;
120 // Confirm...
121 if (tracks < 1) {
122 return WD2797_ERR_BAD_GEOM;
123 }
125 // Allocate enough memory to store one disc track
126 if (ctx->data) {
127 free(ctx->data);
128 }
129 ctx->data = malloc(secsz * spt);
130 if (!ctx->data)
131 return WD2797_ERR_NO_MEMORY;
133 // Load the image and the geometry data
134 ctx->disc_image = fp;
135 ctx->geom_tracks = tracks;
136 ctx->geom_secsz = secsz;
137 ctx->geom_heads = heads;
138 ctx->geom_spt = spt;
140 return WD2797_ERR_OK;
141 }
144 void wd2797_unload(WD2797_CTX *ctx)
145 {
146 // Free memory buffer
147 if (ctx->data) {
148 free(ctx->data);
149 ctx->data = NULL;
150 }
152 // Clear file pointer
153 ctx->disc_image = NULL;
155 // Clear the disc geometry
156 ctx->geom_tracks = ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = 0;
157 }
160 uint8_t wd2797_read_reg(WD2797_CTX *ctx, uint8_t addr)
161 {
162 uint8_t temp = 0;
164 switch (addr & 0x03) {
165 case WD2797_REG_STATUS: // Status register
166 // Read from status register clears IRQ
167 ctx->irql = false;
168 ctx->irqe = false;
170 // Get current status flags (set by last command)
171 // DRQ bit
172 if (ctx->cmd_has_drq) {
173 printf("\tWDFDC rd sr, has drq, pos=%lu len=%lu\n", ctx->data_pos, ctx->data_len);
174 temp = ctx->status & ~0x03;
175 temp |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
176 } else {
177 temp = ctx->status & ~0x01;
178 }
179 // FDC is busy if there is still data in the buffer
180 temp |= (ctx->data_pos < ctx->data_len) ? 0x01 : 0x00; // if data in buffer, then DMA hasn't copied it yet, and we're still busy!
181 // TODO: also if seek delay / read delay hasn't passed (but that's for later)
182 return temp;
184 case WD2797_REG_TRACK: // Track register
185 return ctx->track;
187 case WD2797_REG_SECTOR: // Sector register
188 return ctx->sector;
190 case WD2797_REG_DATA: // Data register
191 // If there's data in the buffer, return it. Otherwise return 0xFF.
192 if (ctx->data_pos < ctx->data_len) {
193 // set IRQ if this is the last data byte
194 if (ctx->data_pos == (ctx->data_len-1)) {
195 // Set IRQ only if IRQL has been cleared (no pending IRQs)
196 ctx->irqe = ctx->irql ? ctx->irqe : true;
197 ctx->irql = true;
198 }
199 // return data byte and increment pointer
200 return ctx->data[ctx->data_pos++];
201 } else {
202 // command finished
203 return 0xff;
204 }
206 default:
207 // shut up annoying compilers which don't recognise unreachable code when they see it
208 // (here's looking at you, gcc!)
209 return 0xff;
210 }
211 }
214 void wd2797_write_reg(WD2797_CTX *ctx, uint8_t addr, uint8_t val)
215 {
216 uint8_t cmd = val & CMD_MASK;
217 size_t lba;
218 bool is_type1 = false;
219 int temp;
221 m68k_end_timeslice();
223 switch (addr) {
224 case WD2797_REG_COMMAND: // Command register
225 // write to command register clears interrupt request
226 ctx->irql = false;
228 // Is the drive ready?
229 if (ctx->disc_image == NULL) {
230 // No disc image, thus the drive is busy.
231 ctx->status = 0x80;
232 return;
233 }
235 // Handle Type 1 commands
236 switch (cmd) {
237 case CMD_RESTORE:
238 // Restore. Set track to 0 and throw an IRQ.
239 is_type1 = true;
240 ctx->track = 0;
241 break;
243 case CMD_SEEK:
244 // Seek. Seek to the track specced in the Data Register.
245 is_type1 = true;
246 if (ctx->data_reg < ctx->geom_tracks) {
247 ctx->track = ctx->data_reg;
248 } else {
249 // Seek error. :(
250 ctx->status = 0x10;
251 }
253 case CMD_STEP:
254 // TODO! deal with trk0!
255 // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag.
256 is_type1 = true;
257 break;
259 case CMD_STEPIN:
260 case CMD_STEPOUT:
261 // TODO! deal with trk0!
262 // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag.
263 if (cmd == CMD_STEPIN) {
264 ctx->last_step_dir = 1;
265 } else {
266 ctx->last_step_dir = -1;
267 }
268 is_type1 = true;
269 break;
271 case CMD_STEP_TU:
272 case CMD_STEPIN_TU:
273 case CMD_STEPOUT_TU:
274 // if this is a Step In or Step Out cmd, set the step-direction
275 if (cmd == CMD_STEPIN_TU) {
276 ctx->last_step_dir = 1;
277 } else if (cmd == CMD_STEPOUT_TU) {
278 ctx->last_step_dir = -1;
279 }
281 // Seek one step in the last direction used.
282 ctx->track += ctx->last_step_dir;
283 if (ctx->track < 0) ctx->track = 0;
284 if (ctx->track >= ctx->geom_tracks) {
285 // Seek past end of disc... that'll be a Seek Error then.
286 ctx->status = 0x10;
287 ctx->track = ctx->geom_tracks - 1;
288 }
289 is_type1 = true;
290 break;
292 default:
293 break;
294 }
296 if (is_type1) {
297 // Terminate any sector reads or writes
298 ctx->data_len = ctx->data_pos = 0;
300 // No DRQ bit for these commands.
301 ctx->cmd_has_drq = false;
303 // Type1 status byte...
304 ctx->status = 0;
305 // S7 = Not Ready. Command executed, therefore the drive was ready... :)
306 // S6 = Write Protect. TODO: add this
307 // S5 = Head Loaded. For certain emulation-related reasons, the heads are always loaded...
308 ctx->status |= 0x20;
309 // S4 = Seek Error. Not bloody likely if we got down here...!
310 // S3 = CRC Error. Not gonna happen on a disc image!
311 // S2 = Track 0
312 ctx->status |= (ctx->track == 0) ? 0x04 : 0x00;
313 // S1 = Index Pulse. TODO -- need periodics to emulate this
314 // S0 = Busy. We just exec'd the command, thus we're not busy.
315 // TODO: Set a timer for seeks, and ONLY clear BUSY when that timer expires. Need periodics for that.
317 // Set IRQ only if IRQL has been cleared (no pending IRQs)
318 ctx->irqe = ctx->irql ? ctx->irqe : true;
319 ctx->irql = true;
320 return;
321 }
323 // That's the Type 1 (seek) commands sorted. Now for the others.
325 // All these commands return the DRQ bit...
326 ctx->cmd_has_drq = true;
328 // If drive isn't ready, then set status B7 and exit
329 if (ctx->disc_image == NULL) {
330 ctx->status = 0x80;
331 return;
332 }
334 // If this is a Write command, check write protect status too
335 // TODO!
336 if (false) {
337 // Write protected disc...
338 if ((cmd == CMD_WRITE_SECTOR) || (cmd == CMD_WRITE_SECTOR_MULTI) || (cmd == CMD_FORMAT_TRACK)) {
339 // Set Write Protect bit and bail.
340 ctx->status = 0x40;
342 // Set IRQ only if IRQL has been cleared (no pending IRQs)
343 ctx->irqe = ctx->irql ? ctx->irqe : true;
344 ctx->irql = true;
346 return;
347 }
348 }
350 // Disc is ready to go. Parse the command word.
351 switch (cmd) {
352 case CMD_READ_ADDRESS:
353 // Read Address
354 ctx->head = (val & 0x02) ? 1 : 0;
356 // reset data pointers
357 ctx->data_pos = ctx->data_len = 0;
359 // load data buffer
360 ctx->data[ctx->data_len++] = ctx->track;
361 ctx->data[ctx->data_len++] = ctx->head;
362 ctx->data[ctx->data_len++] = ctx->sector;
363 switch (ctx->geom_secsz) {
364 case 128: ctx->data[ctx->data_len++] = 0; break;
365 case 256: ctx->data[ctx->data_len++] = 1; break;
366 case 512: ctx->data[ctx->data_len++] = 2; break;
367 case 1024: ctx->data[ctx->data_len++] = 3; break;
368 default: ctx->data[ctx->data_len++] = 0xFF; break; // TODO: deal with invalid values better
369 }
370 ctx->data[ctx->data_len++] = 0; // TODO: IDAM CRC!
371 ctx->data[ctx->data_len++] = 0;
373 ctx->status = 0;
374 // B6, B5 = 0
375 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
376 // B3 = CRC Error. Not possible.
377 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
378 // B1 = DRQ. Data request.
379 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
380 break;
382 case CMD_READ_SECTOR:
383 case CMD_READ_SECTOR_MULTI:
384 ctx->head = (val & 0x02) ? 1 : 0;
385 printf("WD279X: READ SECTOR cmd=%02X chs=%d:%d:%d\n", cmd, ctx->track, ctx->head, ctx->sector);
386 // Read Sector or Read Sector Multiple
387 // reset data pointers
388 ctx->data_pos = ctx->data_len = 0;
390 // Calculate number of sectors to read from disc
391 if (cmd == CMD_READ_SECTOR_MULTI)
392 temp = ctx->geom_spt;
393 else
394 temp = 1;
396 for (int i=0; i<temp; i++) {
397 // Calculate the LBA address of the required sector
398 lba = ((((ctx->track * ctx->geom_heads) + ctx->head) * ctx->geom_spt) + ((ctx->sector + i - 1) % ctx->geom_spt)) * ctx->geom_secsz;
399 printf("\tREAD lba = %lu\n", lba);
401 // Read the sector from the file
402 fseek(ctx->disc_image, lba, SEEK_SET);
403 ctx->data_len += fread(&ctx->data[ctx->data_len], 1, ctx->geom_secsz, ctx->disc_image);
404 printf("\tREAD len=%lu, pos=%lu, ssz=%d\n", ctx->data_len, ctx->data_pos, ctx->geom_secsz);
405 // TODO: check fread return value! if < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr)
406 }
408 ctx->status = 0;
409 // B6 = 0
410 // B5 = Record Type -- 1 = deleted, 0 = normal. We can't emulate anything but normal data blocks.
411 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
412 // B3 = CRC Error. Not possible.
413 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
414 // B1 = DRQ. Data request.
415 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
416 break;
418 case CMD_READ_TRACK:
419 // Read Track
420 // TODO! implement this
421 ctx->head = (val & 0x02) ? 1 : 0;
422 ctx->status = 0;
423 // B6, B5, B4, B3 = 0
424 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
425 // B1 = DRQ. Data request.
426 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
427 break;
429 case CMD_WRITE_SECTOR:
430 case CMD_WRITE_SECTOR_MULTI:
431 // Write Sector or Write Sector Multiple
433 ctx->head = (val & 0x02) ? 1 : 0;
434 // reset data pointers
435 ctx->data_pos = ctx->data_len = 0;
437 // TODO: set "write pending" flag, and write LBA, and go from there.
439 ctx->status = 0;
440 // B6 = Write Protect. FIXME -- emulate this!
441 // B5 = 0
442 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
443 // B3 = CRC Error. Not possible.
444 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
445 // B1 = DRQ. Data request.
446 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
447 break;
449 case CMD_FORMAT_TRACK:
450 // Write Track (aka Format Track)
451 ctx->head = (val & 0x02) ? 1 : 0;
452 ctx->status = 0;
453 // B6 = Write Protect. FIXME -- emulate this!
454 // B5, B4, B3 = 0
455 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
456 // B1 = DRQ. Data request.
457 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
458 break;
460 case CMD_FORCE_INTERRUPT:
461 // Force Interrupt...
462 // Terminates current operation and sends an interrupt
463 // TODO!
464 ctx->status = 0;
465 ctx->data_pos = ctx->data_len = 0;
466 // Set IRQ only if IRQL has been cleared (no pending IRQs)
467 ctx->irqe = ctx->irql ? ctx->irqe : true;
468 ctx->irql = true;
469 break;
470 }
471 break;
473 case WD2797_REG_TRACK: // Track register
474 ctx->track = val;
475 break;
477 case WD2797_REG_SECTOR: // Sector register
478 ctx->sector = val;
479 break;
481 case WD2797_REG_DATA: // Data register
482 // Save the value written into the data register
483 ctx->data_reg = val;
485 // If we're processing a write command, and there's space in the
486 // buffer, allow the write.
487 if (ctx->data_pos < ctx->data_len) {
488 // set IRQ if this is the last data byte
489 if (ctx->data_pos == (ctx->data_len-1)) {
490 // Set IRQ only if IRQL has been cleared (no pending IRQs)
491 ctx->irqe = ctx->irql ? ctx->irqe : true;
492 ctx->irql = true;
493 }
495 // store data byte and increment pointer
496 ctx->data[ctx->data_pos++] = val;
497 }
498 break;
499 }
500 }