libfusefdc: reformatted UDI i/o code
[zymosis.git] / src / libfusefdc / wd_fdc.c
blob8a145c3f57b2f93b1319ac2c3f1b5bab6d3de875
1 /* wd_fdc.c: Western Digital floppy disk controller emulation
2 Copyright (c) 2002-2016 Stuart Brady, Fredrick Meunier, Philip Kendall,
3 Dmitry Sanarin, Gergely Szasz
4 Copyright (c) 2016 Sergio BaldovĂ­
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 Author contact information:
22 Philip: philip-fuse@shadowmagic.org.uk
24 Stuart: stuart.brady@gmail.com
27 #ifndef LIBFUSE_DISK_IO_ONLY
29 #include "../ZXEmuT/emuvars.h"
30 #include "../ZXEmuT/emuevents.h"
32 #include "crc.h"
33 #include "wd_fdc.h"
36 static uint32_t fdc_event, motor_off_event, timeout_event;
39 //==========================================================================
41 // crc_preset
43 //==========================================================================
44 static inline void crc_preset (wd_fdc *f) {
45 f->crc = 0xffff;
49 //==========================================================================
51 // crc_add
53 //==========================================================================
54 static inline void crc_add (wd_fdc *f, fdd_t *d) {
55 f->crc = crc_fdc( f->crc, d->data & 0xff );
59 //==========================================================================
61 // wd_fdc_master_reset
63 //==========================================================================
64 void wd_fdc_master_reset (wd_fdc *f) {
65 fdd_t *d = f->current_drive;
67 f->spin_cycles = 0;
68 f->direction = 0;
69 f->head_load = 0;
70 if (d) {
71 if (f->flags&WD_FLAG_BETA128) {
72 fdd_motoron( d, 0);
73 } else {
74 fdd_head_load(d, 0);
77 f->read_id = 0;
78 f->hlt = 1;
79 if (!(f->flags&WD_FLAG_NOHLT) && f->hlt_time > 0) f->hlt = 0;
80 f->intrq = 0;
81 f->datarq = 0;
83 f->state = WD_FDC_STATE_NONE;
84 f->status_type = WD_FDC_STATUS_TYPE1;
86 if (d != NULL) {
87 while (!d->tr00) fdd_step(d, FDD_STEP_OUT);
90 f->track_register = 0;
91 f->sector_register = 0;
92 f->data_register = 0;
93 f->status_register = WD_FDC_SR_LOST; /* track 0 */
97 //==========================================================================
99 // wd_fdc_alloc_fdc
101 //==========================================================================
102 wd_fdc *wd_fdc_alloc_fdc (wd_type_t type, int hlt_time, unsigned int flags) {
103 wd_fdc *fdc = calloc(1, sizeof(wd_fdc));
105 switch (type) {
106 default:
107 type = WD1770; /* illegal type converted to wd_fdc */
108 /* fallthrough */
109 case FD1793:
110 case WD1773:
111 case WD1770:
112 case WD2797:
113 fdc->rates[0] = 6;
114 fdc->rates[1] = 12;
115 fdc->rates[2] = 20;
116 fdc->rates[3] = 30;
117 break;
118 case WD1772:
119 fdc->rates[0] = 2;
120 fdc->rates[1] = 3;
121 fdc->rates[2] = 5;
122 fdc->rates[3] = 6;
123 break;
125 fdc->type = type;
126 fdc->current_drive = NULL;
127 fdc->hlt_time = hlt_time;
128 fdc->flags = flags; /* Beta128 connect HLD out to READY in and MOTOR ON */
129 wd_fdc_master_reset(fdc);
130 return fdc;
134 //==========================================================================
136 // wd_fdc_set_intrq
138 //==========================================================================
139 static void wd_fdc_set_intrq (wd_fdc *f) {
140 if ((f->type == WD1770 || f->type == WD1772) &&
141 (f->status_register&WD_FDC_SR_MOTORON))
143 emuEvAdd(/*tstates +*/ 2 * /* 10 rev: 10 * 200 / 1000 */
144 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed,
145 motor_off_event, f );
148 if ((f->type == WD1773 || f->type == FD1793 || f->type == WD2797) &&
149 f->head_load)
151 emuEvAdd( /*tstates +*/ 3 * /* 15 revolution: 15 * 200 / 1000 */
152 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed,
153 motor_off_event, f );
156 if (f->intrq != 1) {
157 f->intrq = 1;
158 if (f->set_intrq) f->set_intrq( f );
163 //==========================================================================
165 // wd_fdc_reset_intrq
167 //==========================================================================
168 static void wd_fdc_reset_intrq (wd_fdc *f) {
169 if (f->intrq == 1) {
170 f->intrq = 0;
171 if (f->reset_intrq) f->reset_intrq(f);
176 //==========================================================================
178 // wd_fdc_set_datarq
180 //==========================================================================
181 static void wd_fdc_set_datarq (wd_fdc *f) {
182 if (f->datarq != 1) {
183 f->status_register |= WD_FDC_SR_IDX_DRQ;
184 f->datarq = 1;
185 if (f->set_datarq) f->set_datarq(f);
190 //==========================================================================
192 // wd_fdc_reset_datarq
194 //==========================================================================
195 static void wd_fdc_reset_datarq (wd_fdc *f) {
196 if (f->datarq == 1) {
197 f->status_register &= ~WD_FDC_SR_IDX_DRQ;
198 f->datarq = 0;
199 if (f->reset_datarq) f->reset_datarq(f);
204 //==========================================================================
206 // wd_fdc_set_hlt
208 //==========================================================================
209 void wd_fdc_set_hlt (wd_fdc *f, int hlt) {
210 f->hlt = (hlt > 0 ? 1 : 0);
214 //==========================================================================
216 // disk_ready
218 //==========================================================================
219 static int disk_ready (wd_fdc *f) {
220 if (f->flags&WD_FLAG_BETA128) return f->head_load; /* Beta 128, READY = HLD */
221 if (f->flags&WD_FLAG_RDY) return f->extra_signal; /* MB-02+ set ready, if any of fdd selected */
222 return f->current_drive->ready;
226 //==========================================================================
228 // wd_fdc_wait_index
230 //==========================================================================
231 static void wd_fdc_wait_index (void *fdc) {
232 wd_fdc *f = fdc;
233 wd_fdc_set_intrq(f); /* generate an interrupt */
237 //==========================================================================
239 // read_id
241 // return 0 if found an ID
242 // return 1 if not found ID
243 // return 2 if found but with CRC error (READ ADDRESS command)
245 // what we can do, if disk not rotating, or head not loaded?
247 //==========================================================================
248 static int read_id (wd_fdc *f) {
249 int i = f->rev;
250 fdd_t *d = f->current_drive;
252 f->id_mark = WD_FDC_AM_NONE;
254 if (f->rev <= 0) return 1;
256 /* **FIXME d->motoron? */
257 while (i == f->rev) {
258 crc_preset(f);
259 if (f->dden) {
260 /* double density (MFM) */
261 fdd_read_data(d);
262 if (d->index) --f->rev;
263 crc_add(f, d);
264 if (d->data == 0xffa1) {
265 fdd_read_data(d);
266 crc_add(f, d);
267 if (d->index) --f->rev;
268 if (d->data != 0xffa1) continue;
269 fdd_read_data(d);
270 crc_add(f, d);
271 if (d->index) --f->rev;
272 if (d->data != 0xffa1) continue;
273 } else {
274 /* no 0xa1 with missing clock... */
275 continue;
278 fdd_read_data(d);
279 crc_add(f, d);
280 if (d->index) --f->rev;
281 if (f->dden) {
282 /* double density (MFM) */
283 if (d->data != 0x00fe) continue;
284 } else {
285 /* single density (FM) */
286 if (d->data != 0xfffe) continue;
288 fdd_read_data(d);
289 crc_add(f, d);
290 if (d->index) --f->rev;
291 f->id_track = d->data;
292 fdd_read_data(d);
293 crc_add(f, d);
294 if (d->index) --f->rev;
295 f->id_head = d->data;
296 fdd_read_data(d);
297 crc_add(f, d);
298 if (d->index) --f->rev;
299 f->id_sector = d->data;
300 fdd_read_data(d);
301 crc_add(f, d);
302 if (d->index) --f->rev;
303 f->id_length = d->data;
304 if (f->non_ibm_len_code) {
305 /* 00->256 01->512 10->1024 11->128 */
306 f->sector_length = 0x80<<((d->data+1)&0x03);
307 } else {
308 /* 00->128 01->256 10->512 11->1024 */
309 f->sector_length = 0x80<<(d->data&0x03);
311 fdd_read_data(d);
312 crc_add(f, d);
313 if (d->index) --f->rev;
314 fdd_read_data(d);
315 crc_add(f, d);
316 if (d->index) --f->rev;
317 if (f->crc != 0x0000) {
318 f->status_register |= WD_FDC_SR_CRCERR;
319 f->id_mark = WD_FDC_AM_ID;
320 return 2;
321 } else {
322 f->status_register &= ~WD_FDC_SR_CRCERR;
323 f->id_mark = WD_FDC_AM_ID;
324 return 0;
327 return 1;
331 //==========================================================================
333 // read_datamark
335 //==========================================================================
336 static int read_datamark (wd_fdc *f) {
337 fdd_t *d = f->current_drive;
338 int i;
340 f->id_mark = WD_FDC_AM_NONE;
342 if (f->dden) {
343 /* double density (MFM) */
344 for (i = 40; i > 0; --i) {
345 fdd_read_data( d );
346 if (d->data == 0x4e) continue; /* read next */
347 if (d->data == 0x00) break; /* go to PLL sync */
348 return 1; /* something wrong... */
350 for (; i > 0; --i) {
351 crc_preset(f);
352 fdd_read_data(d);
353 crc_add(f, d);
354 if (d->data == 0x00) continue;
355 if (d->data == 0xffa1) break; /* got to a1 mark */
356 return 1;
358 for (i = (d->data == 0xffa1 ? 2 : 3); i > 0; --i) {
359 fdd_read_data(d);
360 crc_add(f, d);
361 if (d->data != 0xffa1) return 1;
363 fdd_read_data(d);
364 crc_add(f, d);
365 if (d->data < 0x00f8 || d->data > 0x00fb) return 1; /* !fb deleted mark */
366 f->ddam = (d->data != 0x00fb ? 1 : 0);
367 f->id_mark = WD_FDC_AM_DATA;
368 return 0;
369 } else {
370 /* SD -> FM */
371 for (i = 30; i > 0; --i) {
372 fdd_read_data(d);
373 if (d->data == 0xff) continue; /* read next */
374 if (d->data == 0x00) break; /* go to PLL sync */
375 return 1; /* something wrong... */
377 for (; i > 0; --i) {
378 crc_preset(f);
379 fdd_read_data(d);
380 crc_add(f, d);
381 if (d->data == 0x00) continue;
382 if (d->data >= 0xfff8 && d->data <= 0xfffb) break; /* !fb deleted mark */
383 return 1;
385 if (i == 0) {
386 fdd_read_data(d);
387 crc_add(f, d);
388 if (d->data < 0xfff8 || d->data > 0xfffb) return 1; /* !fb deleted mark */
390 f->ddam = (d->data != 0x00fb ? 1 : 0);
391 f->id_mark = WD_FDC_AM_DATA;
392 return 0;
394 return 1;
398 //==========================================================================
400 // wd_fdc_sr_read
402 //==========================================================================
403 uint8_t wd_fdc_sr_read (wd_fdc *f) {
404 fdd_t *d = f->current_drive;
406 wd_fdc_reset_intrq(f);
408 if (f->status_type == WD_FDC_STATUS_TYPE1) {
409 f->status_register &= ~WD_FDC_SR_IDX_DRQ;
410 if (!d->loaded || d->index_pulse) {
411 f->status_register |= WD_FDC_SR_IDX_DRQ;
415 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
417 if (f->status_register & WD_FDC_SR_BUSY) f->status_register |= WD_FDC_SR_MOTORON;
418 else f->status_register &= ~WD_FDC_SR_MOTORON;
420 if (disk_ready(f)) {
421 f->status_register &= ~WD_FDC_SR_MOTORON;
422 } else {
423 f->status_register |= WD_FDC_SR_MOTORON;
427 return f->status_register;
431 //==========================================================================
433 // wd_fdc_seek_verify_read_id
435 //==========================================================================
436 static void wd_fdc_seek_verify_read_id (wd_fdc *f) {
437 fdd_t *d = f->current_drive;
438 int i;
439 f->read_id = 1;
441 emuEvRemoveAllWithType(fdc_event);
443 if (f->id_mark == WD_FDC_AM_NONE) {
444 while (f->rev) {
445 i = (d->disk.i >= d->disk.bpt ? 0 : d->disk.i); /* start position */
446 if (!read_id(f)) {
447 if (f->id_track != f->track_register) {
448 f->status_register |= WD_FDC_SR_RNF;
450 } else {
451 f->id_mark = WD_FDC_AM_NONE;
453 i = (d->disk.bpt ? (d->disk.i-i)*200/d->disk.bpt : 200);
454 if (i > 0) {
455 emuEvAdd(/*tstates +*/ i * /* i * 1/20 revolution */
456 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed / 1000,
457 fdc_event, f );
458 return;
459 } else if (f->id_mark != WD_FDC_AM_NONE) {
460 break;
463 if (f->id_mark == WD_FDC_AM_NONE) f->status_register |= WD_FDC_SR_RNF;
466 f->state = WD_FDC_STATE_NONE;
467 f->status_register &= ~WD_FDC_SR_BUSY;
468 wd_fdc_set_intrq(f);
469 f->read_id = 0;
473 //==========================================================================
475 // wd_fdc_seek_verify
477 //==========================================================================
478 static void wd_fdc_seek_verify (wd_fdc *f) {
479 fdd_t *d = f->current_drive;
481 emuEvRemoveAllWithType( fdc_event );
482 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
483 if (!f->hlt) {
484 emuEvAdd(/*tstates +*/ 5 * /* sample every 5 ms */
485 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed / 1000,
486 fdc_event, f );
487 return;
489 if (f->head_load) {
490 f->status_register |= WD_FDC_SR_SPINUP;
491 /* when set, it indicates head is loaded and enganged.
492 This bit is logical "and" of HLD and "HLT" signals. */
496 if (d->tr00) {
497 f->status_register |= WD_FDC_SR_LOST;
498 } else {
499 f->status_register &= ~WD_FDC_SR_LOST;
502 f->rev = 5;
503 f->id_mark = WD_FDC_AM_NONE;
504 wd_fdc_seek_verify_read_id(f);
508 //==========================================================================
510 // wd_fdc_type_i
512 // if (f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY)
513 // also, in writing
515 //==========================================================================
516 static void wd_fdc_type_i (wd_fdc *f) {
517 uint8_t b = f->command_register;
518 fdd_t *d = f->current_drive;
520 if (f->state == WD_FDC_STATE_SEEK_DELAY) {
521 /* after delay */
522 if ((b&0x60) != 0x00) goto type_i_verify; /* STEP/STEP-IN/STEP-OUT */
523 goto type_i_loop;
524 } else {
525 /* WD_FDC_STATE_SEEK */
526 f->status_register |= WD_FDC_SR_SPINUP;
529 if ((b&0x60) != 0x00) {
530 /* STEP/STEP-IN/STEP-OUT */
531 if (b&0x40) f->direction = (b&0x20 ? FDD_STEP_OUT : FDD_STEP_IN);
532 if (b&0x10) goto type_i_update; /* update? */
533 goto type_i_noupdate;
536 /* SEEK or RESTORE */
537 if ((b&0x10) == 0) {
538 /* RESTORE */
539 f->track_register = 0xff;
540 f->data_register = 0;
543 type_i_loop:
544 if (f->track_register != f->data_register) {
545 f->direction = (f->track_register < f->data_register ? FDD_STEP_IN : FDD_STEP_OUT);
547 type_i_update:
548 f->track_register += (f->direction == FDD_STEP_IN ? 1 : -1);
550 type_i_noupdate:
551 if (d->tr00 && f->direction == FDD_STEP_OUT) {
552 f->track_register = 0;
553 } else {
554 fdd_step(d, f->direction);
555 f->state = WD_FDC_STATE_SEEK_DELAY;
556 emuEvRemoveAllWithType(fdc_event);
557 emuEvAdd(/*tstates +*/ f->rates[b&0x03]*
558 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed / 1000,
559 fdc_event, f);
560 return;
564 type_i_verify:
565 if (b&0x04) {
566 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
567 f->head_load = 1;
568 emuEvRemoveAllWithType(motor_off_event);
569 if (f->flags&WD_FLAG_BETA128) fdd_motoron(d, 1); else fdd_head_load(d, 1);
570 emuEvRemoveAllWithType(fdc_event);
571 emuEvAdd(/*tstates +*/ 15* /* 15ms */
572 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/1000,
573 fdc_event, f);
576 f->state = WD_FDC_STATE_VERIFY;
578 if ((f->type == WD1770 || f->type == WD1772) &&
579 (f->status_register&WD_FDC_SR_MOTORON) == 0)
581 f->status_register |= WD_FDC_SR_MOTORON;
582 fdd_motoron(f->current_drive, 1);
583 emuEvRemoveAllWithType(fdc_event);
584 emuEvAdd(/*tstates +*/ 12* /* 6 revolution 6 * 200 / 1000 */
585 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/10,
586 fdc_event, f);
587 return;
590 wd_fdc_seek_verify(f);
591 return;
594 if (d->tr00) {
595 f->status_register |= WD_FDC_SR_LOST;
596 } else {
597 f->status_register &= ~WD_FDC_SR_LOST;
600 f->state = WD_FDC_STATE_NONE;
601 f->status_register &= ~WD_FDC_SR_BUSY;
602 wd_fdc_set_intrq(f);
606 //==========================================================================
608 // wd_fdc_type_ii_seek
610 // if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE)
611 // also, `wd_fdc_type_ii()`
613 //==========================================================================
614 static void wd_fdc_type_ii_seek (wd_fdc *f) {
615 uint8_t b = f->command_register;
616 fdd_t *d = f->current_drive;
617 int i;
619 emuEvRemoveAllWithType(fdc_event);
621 if (f->id_mark == WD_FDC_AM_NONE) {
622 f->read_id = 1;
623 while (f->rev) {
624 i = (d->disk.i >= d->disk.bpt ? 0 : d->disk.i); /* start position */
625 if (!read_id(f)) {
626 if ((f->data_check_head != -1 && f->data_check_head != !!(f->id_head)) ||
627 (f->id_track != f->track_register || f->id_sector != f->sector_register))
629 f->id_mark = WD_FDC_AM_NONE;
631 } else {
632 f->id_mark = WD_FDC_AM_NONE;
634 i = (d->disk.bpt ? (d->disk.i-i)*200/d->disk.bpt : 200);
635 if (i > 0) {
636 emuEvAdd(/*tstates +*/ i* /* i * 1/20 revolution */
637 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/1000,
638 fdc_event, f);
639 return;
640 } else if (f->id_mark != WD_FDC_AM_NONE) {
641 break;
646 f->read_id = 0;
648 if (f->id_mark == WD_FDC_AM_NONE) {
649 f->status_register |= WD_FDC_SR_RNF;
650 f->status_register &= ~WD_FDC_SR_BUSY;
651 f->state = WD_FDC_STATE_NONE;
652 wd_fdc_set_intrq(f);
653 return;
656 if (f->state == WD_FDC_STATE_READ) {
657 if (f->id_mark == WD_FDC_AM_ID) read_datamark(f);
659 if (f->id_mark == WD_FDC_AM_NONE) {
660 /* not found */
661 f->status_register |= WD_FDC_SR_RNF;
662 f->status_register &= ~WD_FDC_SR_BUSY;
663 f->state = WD_FDC_STATE_NONE;
664 wd_fdc_set_intrq(f);
665 return;
668 if (f->ddam) f->status_register |= WD_FDC_SR_SPINUP; /* set deleted data mark */
669 f->data_offset = 0;
670 wd_fdc_set_datarq(f);
671 } else {
672 f->ddam = (b&0x01);
673 for (i = 11; i > 0; --i) fdd_read_data(d); /* "delay" 11 GAP byte */
674 wd_fdc_set_datarq(f);
675 f->data_offset = 0;
676 if (f->dden) {
677 for (i = 11; i > 0; --i) fdd_read_data(d); /* "delay" another 11 GAP byte */
679 d->data = 0x00;
680 for (i = (f->dden ? 12 : 6); i > 0; --i) fdd_write_data(d); /* write 6/12 zero */
681 crc_preset(f);
682 if (f->dden) {
683 /* MFM */
684 d->data = 0xffa1;
685 for (i = 3; i > 0; --i) {
686 /* write 3 0xa1 with clock mark */
687 fdd_write_data(d);
688 crc_add(f, d);
691 d->data = (f->ddam ? 0x00f8 : 0x00fb)|(f->dden ? 0x0000 : 0xff00); /* write data mark */
692 fdd_write_data(d);
693 crc_add(f, d);
696 emuEvRemoveAllWithType(timeout_event);
697 emuEvAdd(/*tstates +*/ /* 5 revolutions: 5 * 200 / 1000 */
698 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed,
699 timeout_event, f);
703 //==========================================================================
705 // wd_fdc_type_ii
707 // if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE)
708 // also, cr write
710 //==========================================================================
711 static void wd_fdc_type_ii (wd_fdc *f) {
712 uint8_t b = f->command_register;
713 fdd_t *d = f->current_drive;
715 emuEvRemoveAllWithType(fdc_event);
717 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
718 if (!disk_ready(f)) {
719 f->status_register &= ~WD_FDC_SR_BUSY;
720 f->state = WD_FDC_STATE_NONE;
721 wd_fdc_set_intrq(f);
722 return;
724 if (!f->hlt) {
725 emuEvAdd(/*tstates +*/ 5*
726 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/1000,
727 fdc_event, f);
728 return;
732 if (f->state == WD_FDC_STATE_WRITE) {
733 if (d->wrprot) {
734 f->status_register |= WD_FDC_SR_WRPROT;
735 f->status_register &= ~WD_FDC_SR_BUSY;
736 f->state = WD_FDC_STATE_NONE;
737 wd_fdc_set_intrq(f);
738 return;
740 f->status_register &= ~WD_FDC_SR_WRPROT;
743 f->data_multisector = (b&0x10 ? 1 : 0);
744 f->rev = 5;
745 f->id_mark = WD_FDC_AM_NONE;
746 wd_fdc_type_ii_seek(f);
750 //==========================================================================
752 // wd_fdc_type_iii
754 // if (f->state == WD_FDC_STATE_READID)
755 // if (f->state == WD_FDC_STATE_READTRACK ||
756 // f->state == WD_FDC_STATE_READID ||
757 // f->state == WD_FDC_STATE_WRITETRACK)
758 // also, crwrite
760 //==========================================================================
761 static void wd_fdc_type_iii (wd_fdc *f) {
762 int i;
763 fdd_t *d = f->current_drive;
765 emuEvRemoveAllWithType(fdc_event);
767 if (!f->read_id && (f->type == WD1773 || f->type == FD1793 || f->type == WD2797)) {
768 if (!disk_ready(f)) {
769 f->status_register &= ~WD_FDC_SR_BUSY;
770 f->state = WD_FDC_STATE_NONE;
771 wd_fdc_set_intrq(f);
772 return;
774 if (!f->hlt) {
775 emuEvAdd(/*tstates +*/ 5*
776 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/1000,
777 fdc_event, f);
778 return;
782 if (f->state == WD_FDC_STATE_WRITETRACK) {
783 /* ----WRITE TRACK---- */
784 if (d->wrprot) {
785 f->status_register |= WD_FDC_SR_WRPROT;
786 f->status_register &= ~WD_FDC_SR_BUSY;
787 f->state = WD_FDC_STATE_NONE;
788 wd_fdc_set_intrq(f);
789 return;
791 f->status_register &= ~WD_FDC_SR_WRPROT;
792 f->data_offset = 0;
793 fdd_wait_index_hole(d);
794 wd_fdc_set_datarq(f);
795 } else if (f->state == WD_FDC_STATE_READTRACK) {
796 /* ----READ TRACK---- */
797 fdd_wait_index_hole(d);
798 wd_fdc_set_datarq(f);
799 } else {
800 /* ----READID---- */
801 if (!f->read_id) {
802 f->read_id = 1;
803 f->rev = 5;
804 f->id_mark = WD_FDC_AM_NONE;
806 if (f->id_mark == WD_FDC_AM_NONE) {
807 while (f->rev) {
808 i = (d->disk.i >= d->disk.bpt ? 0 : d->disk.i); /* start position */
809 read_id(f);
810 i = (d->disk.bpt ? (d->disk.i-i)*200/d->disk.bpt : 200);
811 if (i > 0) {
812 emuEvAdd(/*tstates +*/ i* /* i * 1/20 revolution */
813 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/1000,
814 fdc_event, f);
815 return;
816 } else if (f->id_mark != WD_FDC_AM_NONE) {
817 break;
820 if (f->id_mark == WD_FDC_AM_NONE) {
821 f->state = WD_FDC_STATE_NONE;
822 f->status_register |= WD_FDC_SR_RNF;
823 f->status_register &= ~WD_FDC_SR_BUSY;
824 wd_fdc_set_intrq(f);
825 f->read_id = 0;
826 return;
829 f->read_id = 0;
830 f->data_offset = 0;
831 wd_fdc_set_datarq(f);
834 emuEvRemoveAllWithType(timeout_event);
835 emuEvAdd(/*tstates +*/ 2*20* /* 2 revolutions: 2 * 200 / 1000 */
836 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/100,
837 timeout_event, f);
841 //==========================================================================
843 // wd_fdc_event
845 //==========================================================================
846 static void wd_fdc_event (uint32_t event, void *user_data) {
847 wd_fdc *f = user_data;
848 fdd_t *d = f->current_drive;
850 if (event == timeout_event) {
851 if (f->state == WD_FDC_STATE_READ ||
852 f->state == WD_FDC_STATE_WRITE ||
853 f->state == WD_FDC_STATE_READTRACK ||
854 f->state == WD_FDC_STATE_WRITETRACK ||
855 f->state == WD_FDC_STATE_READID)
857 f->state = WD_FDC_STATE_NONE;
858 f->status_register |= WD_FDC_SR_LOST;
859 f->status_register &= ~WD_FDC_SR_BUSY;
860 wd_fdc_reset_datarq(f);
861 wd_fdc_set_intrq(f);
863 return;
866 if (event == motor_off_event) {
867 if (f->type == WD1770 || f->type == WD1772) {
868 f->status_register &= ~WD_FDC_SR_MOTORON;
869 fdd_motoron(d, 0);
870 } else {
871 /* WD1773/FD1973 */
872 f->head_load = 0;
873 if (f->flags&WD_FLAG_BETA128) fdd_motoron(d, 0); else fdd_head_load(d, 0);
875 return;
878 if ((f->type == WD1773 || f->type == FD1793 || f->type == WD2797) &&
879 f->hlt_time > 0 && f->head_load && !f->hlt)
881 f->hlt = 1;
884 if (((f->type == WD1770 || f->type == WD1772) &&
885 (f->status_register & WD_FDC_SR_MOTORON) &&
886 f->status_type == WD_FDC_STATUS_TYPE1) ||
887 ((f->type == WD1773 || f->type == FD1793 || f->type == WD2797) &&
888 (f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY) &&
889 f->head_load))
891 f->status_register |= WD_FDC_SR_SPINUP;
894 if (f->read_id) {
895 if (f->state == WD_FDC_STATE_VERIFY) {
896 wd_fdc_seek_verify_read_id( f );
897 } else if ((f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE) && f->datarq) {
898 f->datarq = 0;
899 wd_fdc_set_datarq(f);
900 } else if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE) {
901 wd_fdc_type_ii_seek(f);
902 } else if (f->state == WD_FDC_STATE_READID) {
903 wd_fdc_type_iii(f);
905 } else if (f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY) {
906 wd_fdc_type_i(f);
907 } else if (f->state == WD_FDC_STATE_VERIFY) {
908 wd_fdc_seek_verify(f);
909 } else if ((f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE) && f->datarq) {
910 f->datarq = 0;
911 wd_fdc_set_datarq(f);
912 } else if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE) {
913 wd_fdc_type_ii(f);
914 } else if ((f->state == WD_FDC_STATE_READTRACK ||
915 f->state == WD_FDC_STATE_READID ||
916 f->state == WD_FDC_STATE_WRITETRACK) && f->datarq)
918 f->datarq = 0;
919 wd_fdc_set_datarq(f);
920 } else if (f->state == WD_FDC_STATE_READTRACK ||
921 f->state == WD_FDC_STATE_READID ||
922 f->state == WD_FDC_STATE_WRITETRACK)
924 wd_fdc_type_iii(f);
929 /* this chart looks like most close of the reality...
931 +--+--+
932 / is \ no
933 ! MO = 0 !----->----+
934 \ ? / !
935 +--+--+ !
937 +--------------+--------+ !
938 ! set MO ! !
939 +--------------+--------+ v
941 +--+--+ !
942 / is \ no !
943 ! h = 0 !----->----+
944 \ ? / !
945 +--+--+ !
946 !yes !
947 +--------------+--------+ v
948 ! delay 6 index pulses ! !
949 +--------------+--------+ !
951 !-------<-------+
955 //==========================================================================
957 // wd_fdc_spinup
959 //==========================================================================
960 static int wd_fdc_spinup (wd_fdc *f, uint8_t b) {
961 uint32_t delay = 0;
962 fdd_t *d = f->current_drive;
964 if (f->state != WD_FDC_STATE_SEEK && (b&0x04)) delay = 30;
966 if (f->type == WD1770 || f->type == WD1772) {
967 if ((f->status_register&WD_FDC_SR_MOTORON) == 0) {
968 f->status_register |= WD_FDC_SR_MOTORON;
969 fdd_motoron(d, 1);
970 if ((b&0x08) == 0) delay += 6*200;
972 } else {
973 /* WD1773/FD1793/WD2797 */
974 emuEvRemoveAllWithType(motor_off_event);
975 if (f->state == WD_FDC_STATE_SEEK) {
976 if (b&0x08) {
977 f->head_load = 1;
978 if (f->flags & WD_FLAG_BETA128) fdd_motoron(d, 1); else fdd_head_load(d, 1);
979 } else if ((b&0x04) == 0) {
980 /* HLD reset only if V flag == 0 too */
981 f->head_load = 0;
982 if ((f->flags&WD_FLAG_NOHLT) == 0 && f->hlt_time > 0) f->hlt = 0; /* reset the trigger */
983 if (f->flags&WD_FLAG_BETA128) fdd_motoron(d, 0); else fdd_head_load(d, 0);
985 return 0;
986 } else {
987 f->head_load = 1;
988 if (f->flags&WD_FLAG_BETA128) fdd_motoron(d, 1); else fdd_head_load(d, 1);
989 if (f->hlt_time > 0) delay += f->hlt_time;
993 /* For Type III commands on WD2797 */
994 if (f->type == WD2797 && (b&0xc0) == 0xc0 && (b&0x30) != 0x10) {
995 fdd_set_head(d, (b&0x02 ? 1 : 0));
998 if (delay) {
999 emuEvRemoveAllWithType(fdc_event);
1000 emuEvAdd(/*tstates +*/ delay*
1001 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/1000,
1002 fdc_event, f);
1003 return 1;
1006 return 0;
1010 //==========================================================================
1012 // wd_fdc_cr_write
1014 //==========================================================================
1015 void wd_fdc_cr_write (wd_fdc *f, uint8_t b) {
1016 fdd_t *d = f->current_drive;
1018 wd_fdc_reset_intrq(f);
1020 if ((b&0xf0) == 0xd0) {
1021 /* Type IV - Force Interrupt */
1022 emuEvRemoveAllWithType(fdc_event);
1023 f->status_register &= ~(WD_FDC_SR_BUSY|WD_FDC_SR_WRPROT|WD_FDC_SR_CRCERR|WD_FDC_SR_IDX_DRQ);
1024 f->state = WD_FDC_STATE_NONE;
1025 f->status_type = WD_FDC_STATUS_TYPE1;
1026 wd_fdc_reset_datarq(f);
1028 if (b&0x08) {
1029 wd_fdc_set_intrq(f);
1030 } else if (b&0x04) {
1031 d->fdc_index = wd_fdc_wait_index;
1032 d->fdc = f;
1035 if (d->tr00) {
1036 f->status_register |= WD_FDC_SR_LOST;
1037 } else {
1038 f->status_register &= ~WD_FDC_SR_LOST;
1041 wd_fdc_spinup(f, b&0xf7); /* spinup motor, but we do not have a 'h' bit! */
1043 return;
1046 if (f->status_register&WD_FDC_SR_BUSY) return;
1048 f->command_register = b;
1049 f->status_register |= WD_FDC_SR_BUSY;
1051 /* keep spindle motor on: */
1052 emuEvRemoveAllWithType(motor_off_event);
1054 if ((b&0x80) == 0) {
1055 /* Type I */
1056 f->state = WD_FDC_STATE_SEEK;
1057 f->status_type = WD_FDC_STATUS_TYPE1;
1058 f->status_register &= ~(WD_FDC_SR_CRCERR|WD_FDC_SR_RNF|WD_FDC_SR_IDX_DRQ);
1059 wd_fdc_reset_datarq(f);
1060 f->rev = 5;
1061 if (wd_fdc_spinup(f, b)) return;
1062 wd_fdc_type_i(f);
1063 } else if ((b&0x40) == 0) {
1064 /* Type II */
1065 if (f->type == WD1773 || f->type == FD1793) {
1066 if (!disk_ready(f)) {
1067 f->status_register &= ~WD_FDC_SR_BUSY;
1068 f->state = WD_FDC_STATE_NONE;
1069 wd_fdc_set_intrq(f);
1070 return;
1074 if (f->type == WD1773 && (b&0x02)) f->data_check_head = (b&0x08 ? 1 : 0);
1075 else if (f->type == WD2797) f->data_check_head = (b&0x02 ? 1 : 0);
1076 else f->data_check_head = -1;
1078 /* WD2797 (and FD1797) can read sectors with non-IBM-compatible sector length codes */
1079 f->non_ibm_len_code = (f->type == WD2797 && (b&0x08) == 0 ? 1 : 0);
1081 f->state = (b&0x20 ? WD_FDC_STATE_WRITE : WD_FDC_STATE_READ);
1082 f->status_type = WD_FDC_STATUS_TYPE2;
1083 f->status_register &= ~(WD_FDC_SR_WRPROT|WD_FDC_SR_RNF|
1084 WD_FDC_SR_IDX_DRQ|WD_FDC_SR_LOST|
1085 WD_FDC_SR_SPINUP);
1087 if (f->type == WD2797) fdd_set_head(d, (b&0x02 ? 1 : 0));
1089 f->rev = 5;
1090 if (wd_fdc_spinup(f, b)) return;
1091 wd_fdc_type_ii(f);
1092 } else if ((b&0x30) != 0x10) {
1093 /* Type III */
1094 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
1095 if (!disk_ready(f)) {
1096 f->status_register &= ~WD_FDC_SR_BUSY;
1097 f->state = WD_FDC_STATE_NONE;
1098 wd_fdc_set_intrq(f);
1099 return;
1103 f->state = (b&0x20 ? (b&0x10 ? WD_FDC_STATE_WRITETRACK : WD_FDC_STATE_READTRACK) : WD_FDC_STATE_READID);
1104 f->status_type = WD_FDC_STATUS_TYPE2;
1105 f->status_register &= ~(WD_FDC_SR_SPINUP|WD_FDC_SR_RNF|WD_FDC_SR_IDX_DRQ|WD_FDC_SR_LOST);
1107 f->rev = 5;
1108 if (wd_fdc_spinup(f, b)) return;
1109 wd_fdc_type_iii(f);
1114 //==========================================================================
1116 // wd_fdc_tr_read
1118 //==========================================================================
1119 uint8_t wd_fdc_tr_read (wd_fdc *f) {
1120 return f->track_register;
1124 //==========================================================================
1126 // wd_fdc_tr_write
1128 //==========================================================================
1129 void wd_fdc_tr_write (wd_fdc *f, uint8_t b) {
1130 f->track_register = b;
1134 //==========================================================================
1136 // wd_fdc_sec_read
1138 //==========================================================================
1139 uint8_t wd_fdc_sec_read (wd_fdc *f) {
1140 return f->sector_register;
1144 //==========================================================================
1146 // wd_fdc_sec_write
1148 //==========================================================================
1149 void wd_fdc_sec_write (wd_fdc *f, uint8_t b) {
1150 f->sector_register = b;
1154 //==========================================================================
1156 // wd_fdc_dr_read
1158 //==========================================================================
1159 uint8_t wd_fdc_dr_read (wd_fdc *f) {
1160 fdd_t *d = f->current_drive;
1162 if ((f->flags&WD_FLAG_DRQ) && (f->status_register&WD_FDC_SR_BUSY)) {
1163 emuEvRemoveAllWithType(fdc_event);
1166 if (f->state == WD_FDC_STATE_READ) {
1167 ++f->data_offset; /* count read bytes */
1168 fdd_read_data(d); crc_add(f, d); /* read a byte */
1169 if( d->data > 0xff ) {
1170 /* no data */
1171 f->status_register |= WD_FDC_SR_RNF;
1172 f->status_register &= ~WD_FDC_SR_BUSY;
1173 f->status_type = WD_FDC_STATUS_TYPE2;
1174 f->state = WD_FDC_STATE_NONE;
1175 wd_fdc_set_intrq(f);
1176 wd_fdc_reset_datarq(f);
1177 } else {
1178 f->data_register = d->data;
1179 if (f->data_offset == f->sector_length) {
1180 /* read the CRC */
1181 fdd_read_data(d); crc_add(f, d);
1182 fdd_read_data(d); crc_add(f, d);
1184 /* FIXME: make this per-FDC */
1185 emuEvRemoveAllWithType(timeout_event); /* clear the timeout */
1187 if (f->crc == 0x0000 && f->data_multisector) {
1188 ++f->sector_register;
1189 f->rev = 5;
1190 wd_fdc_reset_datarq(f);
1191 emuEvAdd(/*tstates +*/ /* 5 revolutions: 5 * 200 / 1000 */
1192 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed,
1193 timeout_event, f);
1194 emuEvAdd(/*tstates +*/ 2* /* 20 ms delay */
1195 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/100,
1196 fdc_event, f);
1197 } else {
1198 f->status_register &= ~WD_FDC_SR_BUSY;
1199 if (f->crc == 0x0000) {
1200 f->status_register &= ~WD_FDC_SR_CRCERR;
1201 } else {
1202 f->status_register |= WD_FDC_SR_CRCERR;
1204 f->status_type = WD_FDC_STATUS_TYPE2;
1205 f->state = WD_FDC_STATE_NONE;
1206 wd_fdc_set_intrq(f);
1207 wd_fdc_reset_datarq(f);
1211 } else if (f->state == WD_FDC_STATE_READID) {
1212 switch (f->data_offset) {
1213 case 0: /* track */
1214 f->data_register = f->id_track;
1215 break;
1216 case 1: /* head */
1217 f->data_register = f->id_head;
1218 break;
1219 case 2: /* sector */
1220 f->data_register = f->id_sector;
1221 break;
1222 case 3: /* length */
1223 f->data_register = f->id_length;
1224 break;
1225 case 4: /* crc1 */
1226 f->data_register = f->crc >> 8;
1227 break;
1228 case 5: /* crc2 */
1229 f->sector_register = f->id_track;
1230 f->data_register = f->crc&0xff;
1231 f->status_register &= ~WD_FDC_SR_BUSY;
1232 f->status_type = WD_FDC_STATUS_TYPE2;
1233 f->state = WD_FDC_STATE_NONE;
1234 emuEvRemoveAllWithType(timeout_event); /* clear the timeout */
1235 wd_fdc_set_intrq(f);
1236 wd_fdc_reset_datarq(f);
1237 break;
1238 default:
1239 break;
1241 ++f->data_offset;
1242 } else if (f->state == WD_FDC_STATE_READTRACK) {
1243 /* unformatted/out of track looks like 1x 0x00 */
1244 fdd_read_data(d); /* read a byte and give to host */
1245 f->data_register = d->data&0x00ff; /* drop clock marks */
1246 if (d->index) {
1247 emuEvRemoveAllWithType(timeout_event); /* clear the timeout */
1248 f->status_register &= ~WD_FDC_SR_BUSY;
1249 f->status_type = WD_FDC_STATUS_TYPE2;
1250 f->state = WD_FDC_STATE_NONE;
1251 wd_fdc_set_intrq(f);
1252 wd_fdc_reset_datarq(f);
1256 if ((f->flags&WD_FLAG_DRQ) && (f->status_register&WD_FDC_SR_BUSY)) {
1257 /* we need a next datarq */
1258 emuEvAdd(/*tstates +*/ 30* /* 30 us delay */
1259 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/1000000,
1260 fdc_event, f);
1263 return f->data_register;
1267 //==========================================================================
1269 // wd_fdc_dr_write
1271 //==========================================================================
1272 void wd_fdc_dr_write (wd_fdc *f, uint8_t b) {
1273 fdd_t *d = f->current_drive;
1275 f->data_register = b;
1276 if (f->state == WD_FDC_STATE_WRITE) {
1277 d->data = b;
1278 ++f->data_offset; /* count bytes read */
1279 fdd_write_data(d);
1280 crc_add(f, d);
1281 if (f->data_offset == f->sector_length) {
1282 /* write the CRC */
1283 d->data = f->crc>>8;
1284 fdd_write_data(d); /* write crc1 */
1285 d->data = f->crc&0xff;
1286 fdd_write_data(d); /* write crc2 */
1287 d->data = 0xff;
1288 fdd_write_data(d); /* write 1 byte of ff? */
1290 emuEvRemoveAllWithType(timeout_event); /* clear the timeout */
1292 if (f->data_multisector) {
1293 ++f->sector_register;
1294 f->rev = 5;
1295 wd_fdc_reset_datarq(f);
1296 emuEvAdd(/*tstates +*/ /* 5 revolutions: 5 * 200 / 1000 */
1297 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed,
1298 timeout_event, f);
1299 emuEvAdd(/*tstates +*/ 2* /* 20ms delay */
1300 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed / 100,
1301 fdc_event, f);
1302 } else {
1303 f->status_register &= ~WD_FDC_SR_BUSY;
1304 f->status_type = WD_FDC_STATUS_TYPE2;
1305 f->state = WD_FDC_STATE_NONE;
1306 wd_fdc_set_intrq(f);
1307 wd_fdc_reset_datarq(f);
1310 } else if (f->state == WD_FDC_STATE_WRITETRACK) {
1311 d->data = b;
1312 if (f->dden) {
1313 /* MFM */
1314 if (b == 0xf7) {
1315 /* CRC */
1316 d->data = f->crc>>8;
1317 fdd_write_data(d); /* write crc1 */
1318 d->data = f->crc&0xff;
1319 } else if (b == 0xf5) {
1320 d->data = 0xffa1; /* and preset CRC */
1321 f->crc = 0xcdb4; /* 3x crc = crc_fdc( crc, 0xa1 )???? */
1322 } else if (b == 0xf6) {
1323 d->data = 0xffc2;
1324 } else {
1325 crc_add(f, d);
1327 } else {
1328 /* FM */
1329 if (b == 0xf7) {
1330 /* CRC */
1331 d->data = f->crc>>8;
1332 fdd_write_data(d); /* write crc1 */
1333 d->data = f->crc&0xff;
1334 } else if (b == 0xfe || (b >= 0xf8 && b <= 0xfb)) {
1335 crc_preset(f); /* preset CRC */
1336 crc_add(f, d);
1337 d->data |= 0xff00;
1338 } else if (b == 0xfc) {
1339 d->data |= 0xff00;
1340 } else {
1341 crc_add(f, d);
1344 fdd_write_data(d); /* write a byte */
1346 if (d->index) {
1347 emuEvRemoveAllWithType(timeout_event); /* clear the timeout */
1348 f->status_register &= ~WD_FDC_SR_BUSY;
1349 f->state = WD_FDC_STATE_NONE;
1350 wd_fdc_set_intrq(f);
1351 wd_fdc_reset_datarq(f);
1355 if ((f->flags&WD_FLAG_DRQ) &&
1356 (f->status_register&WD_FDC_SR_BUSY)) {
1357 /* we need a next datarq */
1358 /* wd_fdc_reset_datarq( f ); */
1359 emuEvAdd(/*tstates +*/ 30* /* 30 us delay */
1360 /*machine_current->timings.processor_speed*/machineInfo.cpuSpeed/1000000,
1361 fdc_event, f);
1366 //==========================================================================
1368 // wd_fdc_init_events
1370 //==========================================================================
1371 void wd_fdc_init_events (void) {
1372 fdc_event = emuEvRegister(wd_fdc_event, "WD FDC event");
1373 motor_off_event = emuEvRegister(wd_fdc_event, "WD FDC motor off");
1374 timeout_event = emuEvRegister(wd_fdc_event, "WD FDC timeout");
1378 #endif /* LIBFUSE_DISK_IO_ONLY */