emu: deactivate debugger/memview when the other one is activated
[zymosis.git] / src / libfusefdc / wd_fdc.c
blob2b046afc7f8243ebbb6313eb812524b06fa4489d
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 <stdlib.h>
31 #include "crc.h"
32 #include "wd_fdc.h"
35 static uint32_t fdc_event, motor_off_event, timeout_event;
38 //==========================================================================
40 // crc_preset
42 //==========================================================================
43 static inline void crc_preset (wd_fdc *f) {
44 f->crc = 0xffff;
48 //==========================================================================
50 // crc_add
52 //==========================================================================
53 static inline void crc_add (wd_fdc *f, fdd_t *d) {
54 f->crc = crc_fdc(f->crc, d->data&0xff);
58 //==========================================================================
60 // wd_fdc_set_intrq
62 //==========================================================================
63 static void wd_fdc_set_intrq (wd_fdc *f) {
64 if ((f->type == WD1770 || f->type == WD1772) &&
65 (f->status_register&WD_FDC_SR_MOTORON))
67 /* 10 rev: 10 * 200 / 1000 */
68 fdd_ev_ifc->add_new(2*FDD_CPU_HZ, motor_off_event, f);
71 if ((f->type == WD1773 || f->type == FD1793 || f->type == WD2797) &&
72 f->head_load)
74 /* 15 revolution: 15 * 200 / 1000 */
75 fdd_ev_ifc->add_new(3*FDD_CPU_HZ, motor_off_event, f);
78 if (f->intrq != 1) {
79 f->intrq = 1;
80 if (f->set_intrq) f->set_intrq(f);
85 //==========================================================================
87 // wd_fdc_reset_intrq
89 //==========================================================================
90 static void wd_fdc_reset_intrq (wd_fdc *f) {
91 if (f->intrq == 1) {
92 f->intrq = 0;
93 if (f->reset_intrq) f->reset_intrq(f);
98 //==========================================================================
100 // wd_fdc_set_datarq
102 //==========================================================================
103 static void wd_fdc_set_datarq (wd_fdc *f) {
104 if (f->datarq != 1) {
105 f->status_register |= WD_FDC_SR_IDX_DRQ;
106 f->datarq = 1;
107 if (f->set_datarq) f->set_datarq(f);
112 //==========================================================================
114 // wd_fdc_reset_datarq
116 //==========================================================================
117 static void wd_fdc_reset_datarq (wd_fdc *f) {
118 if (f->datarq == 1) {
119 f->status_register &= ~WD_FDC_SR_IDX_DRQ;
120 f->datarq = 0;
121 if (f->reset_datarq) f->reset_datarq(f);
126 //==========================================================================
128 // wd_fdc_set_hlt
130 //==========================================================================
131 void wd_fdc_set_hlt (wd_fdc *f, int hlt) {
132 f->hlt = (hlt > 0 ? 1 : 0);
136 //==========================================================================
138 // disk_ready
140 //==========================================================================
141 static int disk_ready (wd_fdc *f) {
142 if (f->flags&WD_FLAG_BETA128) return f->head_load; /* Beta 128, READY = HLD */
143 if (f->flags&WD_FLAG_RDY) return f->extra_signal; /* MB-02+ set ready, if any of fdd selected */
144 return f->current_drive->ready;
148 //==========================================================================
150 // wd_fdc_wait_index
152 //==========================================================================
153 static void wd_fdc_wait_index (void *fdc) {
154 wd_fdc *f = fdc;
155 wd_fdc_set_intrq(f); /* generate an interrupt */
159 //==========================================================================
161 // read_id
163 // return 0 if found an ID
164 // return 1 if not found ID
165 // return 2 if found but with CRC error (READ ADDRESS command)
167 // what we can do, if disk not rotating, or head not loaded?
169 //==========================================================================
170 static int read_id (wd_fdc *f) {
171 int i = f->rev;
172 fdd_t *d = f->current_drive;
174 f->id_mark = WD_FDC_AM_NONE;
176 if (f->rev <= 0) return 1;
178 /* **FIXME d->motoron? */
179 while (i == f->rev) {
180 crc_preset(f);
181 if (f->dden) {
182 /* double density (MFM) */
183 fdd_read_data(d);
184 if (d->index) --f->rev;
185 crc_add(f, d);
186 if (d->data == 0xffa1) {
187 fdd_read_data(d);
188 crc_add(f, d);
189 if (d->index) --f->rev;
190 if (d->data != 0xffa1) continue;
191 fdd_read_data(d);
192 crc_add(f, d);
193 if (d->index) --f->rev;
194 if (d->data != 0xffa1) continue;
195 } else {
196 /* no 0xa1 with missing clock... */
197 continue;
200 fdd_read_data(d);
201 crc_add(f, d);
202 if (d->index) --f->rev;
203 if (f->dden) {
204 /* double density (MFM) */
205 if (d->data != 0x00fe) continue;
206 } else {
207 /* single density (FM) */
208 if (d->data != 0xfffe) continue;
210 fdd_read_data(d);
211 crc_add(f, d);
212 if (d->index) --f->rev;
213 f->id_track = d->data;
214 fdd_read_data(d);
215 crc_add(f, d);
216 if (d->index) --f->rev;
217 f->id_head = d->data;
218 fdd_read_data(d);
219 crc_add(f, d);
220 if (d->index) --f->rev;
221 f->id_sector = d->data;
222 fdd_read_data(d);
223 crc_add(f, d);
224 if (d->index) --f->rev;
225 f->id_length = d->data;
226 if (f->non_ibm_len_code) {
227 /* 00->256 01->512 10->1024 11->128 */
228 f->sector_length = 0x80<<((d->data+1)&0x03);
229 } else {
230 /* 00->128 01->256 10->512 11->1024 */
231 f->sector_length = 0x80<<(d->data&0x03);
233 fdd_read_data(d);
234 crc_add(f, d);
235 if (d->index) --f->rev;
236 fdd_read_data(d);
237 crc_add(f, d);
238 if (d->index) --f->rev;
239 if (f->crc != 0x0000) {
240 f->status_register |= WD_FDC_SR_CRCERR;
241 f->id_mark = WD_FDC_AM_ID;
242 return 2;
243 } else {
244 f->status_register &= ~WD_FDC_SR_CRCERR;
245 f->id_mark = WD_FDC_AM_ID;
246 return 0;
249 return 1;
253 //==========================================================================
255 // read_datamark
257 //==========================================================================
258 static int read_datamark (wd_fdc *f) {
259 fdd_t *d = f->current_drive;
260 int i;
262 f->id_mark = WD_FDC_AM_NONE;
264 if (f->dden) {
265 /* double density (MFM) */
266 for (i = 40; i > 0; --i) {
267 fdd_read_data(d);
268 if (d->data == 0x4e) continue; /* read next */
269 if (d->data == 0x00) break; /* go to PLL sync */
270 return 1; /* something wrong... */
272 for (; i > 0; --i) {
273 crc_preset(f);
274 fdd_read_data(d);
275 crc_add(f, d);
276 if (d->data == 0x00) continue;
277 if (d->data == 0xffa1) break; /* got to a1 mark */
278 return 1;
280 for (i = (d->data == 0xffa1 ? 2 : 3); i > 0; --i) {
281 fdd_read_data(d);
282 crc_add(f, d);
283 if (d->data != 0xffa1) return 1;
285 fdd_read_data(d);
286 crc_add(f, d);
287 if (d->data < 0x00f8 || d->data > 0x00fb) return 1; /* !fb deleted mark */
288 f->ddam = (d->data != 0x00fb ? 1 : 0);
289 f->id_mark = WD_FDC_AM_DATA;
290 return 0;
291 } else {
292 /* SD -> FM */
293 for (i = 30; i > 0; --i) {
294 fdd_read_data(d);
295 if (d->data == 0xff) continue; /* read next */
296 if (d->data == 0x00) break; /* go to PLL sync */
297 return 1; /* something wrong... */
299 for (; i > 0; --i) {
300 crc_preset(f);
301 fdd_read_data(d);
302 crc_add(f, d);
303 if (d->data == 0x00) continue;
304 if (d->data >= 0xfff8 && d->data <= 0xfffb) break; /* !fb deleted mark */
305 return 1;
307 if (i == 0) {
308 fdd_read_data(d);
309 crc_add(f, d);
310 if (d->data < 0xfff8 || d->data > 0xfffb) return 1; /* !fb deleted mark */
312 f->ddam = (d->data != 0x00fb ? 1 : 0);
313 f->id_mark = WD_FDC_AM_DATA;
314 return 0;
316 return 1;
320 //==========================================================================
322 // wd_fdc_seek_verify_read_id
324 //==========================================================================
325 static void wd_fdc_seek_verify_read_id (wd_fdc *f) {
326 fdd_t *d = f->current_drive;
327 int i;
328 f->read_id = 1;
330 fdd_ev_ifc->remove_all(fdc_event);
332 if (f->id_mark == WD_FDC_AM_NONE) {
333 while (f->rev) {
334 i = (d->diskpos.i >= d->disk->bpt ? 0 : d->diskpos.i); /* start position */
335 if (!read_id(f)) {
336 if (f->id_track != f->track_register) {
337 f->status_register |= WD_FDC_SR_RNF;
339 } else {
340 f->id_mark = WD_FDC_AM_NONE;
342 i = (d->disk->bpt ? (d->diskpos.i-i)*200/d->disk->bpt : 200);
343 if (i > 0) {
344 /* i * 1/20 revolution */
345 fdd_ev_ifc->add_new((fdd_turbo ? 1 : i)*FDD_CPU_HZ/1000, fdc_event, f);
346 return;
347 } else if (f->id_mark != WD_FDC_AM_NONE) {
348 break;
351 if (f->id_mark == WD_FDC_AM_NONE) f->status_register |= WD_FDC_SR_RNF;
354 f->state = WD_FDC_STATE_NONE;
355 f->status_register &= ~WD_FDC_SR_BUSY;
356 wd_fdc_set_intrq(f);
357 f->read_id = 0;
361 //==========================================================================
363 // wd_fdc_seek_verify
365 //==========================================================================
366 static void wd_fdc_seek_verify (wd_fdc *f) {
367 fdd_t *d = f->current_drive;
369 fdd_ev_ifc->remove_all(fdc_event);
370 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
371 if (!f->hlt) {
372 /* sample every 5 ms */
373 fdd_ev_ifc->add_new((fdd_turbo ? 1 : 5)*FDD_CPU_HZ/1000, fdc_event, f);
374 return;
376 if (f->head_load) {
377 f->status_register |= WD_FDC_SR_SPINUP;
378 /* when set, it indicates head is loaded and enganged.
379 This bit is logical "and" of HLD and "HLT" signals. */
383 if (d->tr00) {
384 f->status_register |= WD_FDC_SR_LOST;
385 } else {
386 f->status_register &= ~WD_FDC_SR_LOST;
389 f->rev = 5;
390 f->id_mark = WD_FDC_AM_NONE;
391 wd_fdc_seek_verify_read_id(f);
395 //==========================================================================
397 // wd_fdc_type_i
399 // if (f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY)
400 // also, in writing
402 //==========================================================================
403 static void wd_fdc_type_i (wd_fdc *f) {
404 uint8_t b = f->command_register;
405 fdd_t *d = f->current_drive;
407 if (f->state == WD_FDC_STATE_SEEK_DELAY) {
408 /* after delay */
409 if ((b&0x60) != 0x00) goto type_i_verify; /* STEP/STEP-IN/STEP-OUT */
410 goto type_i_loop;
411 } else {
412 /* WD_FDC_STATE_SEEK */
413 f->status_register |= WD_FDC_SR_SPINUP;
416 if ((b&0x60) != 0x00) {
417 /* STEP/STEP-IN/STEP-OUT */
418 if (b&0x40) f->direction = (b&0x20 ? FDD_STEP_OUT : FDD_STEP_IN);
419 if (b&0x10) goto type_i_update; /* update? */
420 goto type_i_noupdate;
423 /* SEEK or RESTORE */
424 if ((b&0x10) == 0) {
425 /* RESTORE */
426 f->track_register = 0xff;
427 f->data_register = 0;
430 type_i_loop:
431 if (f->track_register != f->data_register) {
432 f->direction = (f->track_register < f->data_register ? FDD_STEP_IN : FDD_STEP_OUT);
434 type_i_update:
435 f->track_register += (f->direction == FDD_STEP_IN ? 1 : -1);
437 type_i_noupdate:
438 if (d->tr00 && f->direction == FDD_STEP_OUT) {
439 f->track_register = 0;
440 } else {
441 fdd_step(d, f->direction);
442 f->state = WD_FDC_STATE_SEEK_DELAY;
443 fdd_ev_ifc->remove_all(fdc_event);
444 fdd_ev_ifc->add_new((fdd_turbo ? 1 : f->rates[b&0x03])*FDD_CPU_HZ/1000, fdc_event, f);
445 return;
449 type_i_verify:
450 if (b&0x04) {
451 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
452 f->head_load = 1;
453 fdd_ev_ifc->remove_all(motor_off_event);
454 if (f->flags&WD_FLAG_BETA128) fdd_motoron(d, 1); else fdd_head_load(d, 1);
455 fdd_ev_ifc->remove_all(fdc_event);
456 /* 15ms */
457 fdd_ev_ifc->add_new((fdd_turbo ? 1 : 15)*FDD_CPU_HZ/1000, fdc_event, f);
460 f->state = WD_FDC_STATE_VERIFY;
462 if ((f->type == WD1770 || f->type == WD1772) &&
463 (f->status_register&WD_FDC_SR_MOTORON) == 0)
465 f->status_register |= WD_FDC_SR_MOTORON;
466 fdd_motoron(f->current_drive, 1);
467 fdd_ev_ifc->remove_all(fdc_event);
468 /* 6 revolution 6 * 200 / 1000 */
469 fdd_ev_ifc->add_new((fdd_turbo ? 1 : 12)*FDD_CPU_HZ/10, fdc_event, f);
470 return;
473 wd_fdc_seek_verify(f);
474 return;
477 if (d->tr00) {
478 f->status_register |= WD_FDC_SR_LOST;
479 } else {
480 f->status_register &= ~WD_FDC_SR_LOST;
483 f->state = WD_FDC_STATE_NONE;
484 f->status_register &= ~WD_FDC_SR_BUSY;
485 wd_fdc_set_intrq(f);
489 //==========================================================================
491 // wd_fdc_type_ii_seek
493 // if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE)
494 // also, `wd_fdc_type_ii()`
496 //==========================================================================
497 static void wd_fdc_type_ii_seek (wd_fdc *f) {
498 uint8_t b = f->command_register;
499 fdd_t *d = f->current_drive;
500 int i;
502 fdd_ev_ifc->remove_all(fdc_event);
504 if (f->id_mark == WD_FDC_AM_NONE) {
505 f->read_id = 1;
506 while (f->rev) {
507 i = (d->diskpos.i >= d->disk->bpt ? 0 : d->diskpos.i); /* start position */
508 if (!read_id(f)) {
509 if ((f->data_check_head != -1 && f->data_check_head != !!(f->id_head)) ||
510 (f->id_track != f->track_register || f->id_sector != f->sector_register))
512 f->id_mark = WD_FDC_AM_NONE;
514 } else {
515 f->id_mark = WD_FDC_AM_NONE;
517 i = (d->disk->bpt ? (d->diskpos.i-i)*200/d->disk->bpt : 200);
518 if (i > 0) {
519 /* i * 1/20 revolution */
520 fdd_ev_ifc->add_new((fdd_turbo ? 1 : i)*FDD_CPU_HZ/1000, fdc_event, f);
521 return;
522 } else if (f->id_mark != WD_FDC_AM_NONE) {
523 break;
528 f->read_id = 0;
530 if (f->id_mark == WD_FDC_AM_NONE) {
531 f->status_register |= WD_FDC_SR_RNF;
532 f->status_register &= ~WD_FDC_SR_BUSY;
533 f->state = WD_FDC_STATE_NONE;
534 wd_fdc_set_intrq(f);
535 return;
538 if (f->state == WD_FDC_STATE_READ) {
539 if (f->id_mark == WD_FDC_AM_ID) read_datamark(f);
541 if (f->id_mark == WD_FDC_AM_NONE) {
542 /* not found */
543 f->status_register |= WD_FDC_SR_RNF;
544 f->status_register &= ~WD_FDC_SR_BUSY;
545 f->state = WD_FDC_STATE_NONE;
546 wd_fdc_set_intrq(f);
547 return;
550 if (f->ddam) f->status_register |= WD_FDC_SR_SPINUP; /* set deleted data mark */
551 f->data_offset = 0;
552 wd_fdc_set_datarq(f);
553 } else {
554 f->ddam = (b&0x01);
555 for (i = 11; i > 0; --i) fdd_read_data(d); /* "delay" 11 GAP byte */
556 wd_fdc_set_datarq(f);
557 f->data_offset = 0;
558 if (f->dden) {
559 for (i = 11; i > 0; --i) fdd_read_data(d); /* "delay" another 11 GAP byte */
561 d->data = 0x00;
562 for (i = (f->dden ? 12 : 6); i > 0; --i) fdd_write_data(d); /* write 6/12 zero */
563 crc_preset(f);
564 if (f->dden) {
565 /* MFM */
566 d->data = 0xffa1;
567 for (i = 3; i > 0; --i) {
568 /* write 3 0xa1 with clock mark */
569 fdd_write_data(d);
570 crc_add(f, d);
573 d->data = (f->ddam ? 0x00f8 : 0x00fb)|(f->dden ? 0x0000 : 0xff00); /* write data mark */
574 fdd_write_data(d);
575 crc_add(f, d);
578 fdd_ev_ifc->remove_all(timeout_event);
579 /* 5 revolutions: 5 * 200 / 1000 */
580 fdd_ev_ifc->add_new(FDD_CPU_HZ, timeout_event, f);
584 //==========================================================================
586 // wd_fdc_type_ii
588 // if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE)
589 // also, cr write
591 //==========================================================================
592 static void wd_fdc_type_ii (wd_fdc *f) {
593 uint8_t b = f->command_register;
594 fdd_t *d = f->current_drive;
596 fdd_ev_ifc->remove_all(fdc_event);
598 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
599 if (!disk_ready(f)) {
600 f->status_register &= ~WD_FDC_SR_BUSY;
601 f->state = WD_FDC_STATE_NONE;
602 wd_fdc_set_intrq(f);
603 return;
605 if (!f->hlt) {
606 fdd_ev_ifc->add_new((fdd_turbo ? 1 : 5)*FDD_CPU_HZ/1000, fdc_event, f);
607 return;
611 if (f->state == WD_FDC_STATE_WRITE) {
612 if (d->wrprot) {
613 f->status_register |= WD_FDC_SR_WRPROT;
614 f->status_register &= ~WD_FDC_SR_BUSY;
615 f->state = WD_FDC_STATE_NONE;
616 wd_fdc_set_intrq(f);
617 return;
619 f->status_register &= ~WD_FDC_SR_WRPROT;
622 f->data_multisector = (b&0x10 ? 1 : 0);
623 f->rev = 5;
624 f->id_mark = WD_FDC_AM_NONE;
625 wd_fdc_type_ii_seek(f);
629 //==========================================================================
631 // wd_fdc_type_iii
633 // if (f->state == WD_FDC_STATE_READID)
634 // if (f->state == WD_FDC_STATE_READTRACK ||
635 // f->state == WD_FDC_STATE_READID ||
636 // f->state == WD_FDC_STATE_WRITETRACK)
637 // also, crwrite
639 //==========================================================================
640 static void wd_fdc_type_iii (wd_fdc *f) {
641 int i;
642 fdd_t *d = f->current_drive;
644 fdd_ev_ifc->remove_all(fdc_event);
646 if (!f->read_id && (f->type == WD1773 || f->type == FD1793 || f->type == WD2797)) {
647 if (!disk_ready(f)) {
648 f->status_register &= ~WD_FDC_SR_BUSY;
649 f->state = WD_FDC_STATE_NONE;
650 wd_fdc_set_intrq(f);
651 return;
653 if (!f->hlt) {
654 fdd_ev_ifc->add_new((fdd_turbo ? 1 : 5)*FDD_CPU_HZ/1000, fdc_event, f);
655 return;
659 if (f->state == WD_FDC_STATE_WRITETRACK) {
660 /* ----WRITE TRACK---- */
661 if (d->wrprot) {
662 f->status_register |= WD_FDC_SR_WRPROT;
663 f->status_register &= ~WD_FDC_SR_BUSY;
664 f->state = WD_FDC_STATE_NONE;
665 wd_fdc_set_intrq(f);
666 return;
668 f->status_register &= ~WD_FDC_SR_WRPROT;
669 f->data_offset = 0;
670 fdd_wait_index_hole(d);
671 wd_fdc_set_datarq(f);
672 } else if (f->state == WD_FDC_STATE_READTRACK) {
673 /* ----READ TRACK---- */
674 fdd_wait_index_hole(d);
675 wd_fdc_set_datarq(f);
676 } else {
677 /* ----READID---- */
678 if (!f->read_id) {
679 f->read_id = 1;
680 f->rev = 5;
681 f->id_mark = WD_FDC_AM_NONE;
683 if (f->id_mark == WD_FDC_AM_NONE) {
684 while (f->rev) {
685 i = (d->diskpos.i >= d->disk->bpt ? 0 : d->diskpos.i); /* start position */
686 read_id(f);
687 i = (d->disk->bpt ? (d->diskpos.i-i)*200/d->disk->bpt : 200);
688 if (i > 0) {
689 /* i * 1/20 revolution */
690 fdd_ev_ifc->add_new((fdd_turbo ? 1 : i)*FDD_CPU_HZ/1000, fdc_event, f);
691 return;
692 } else if (f->id_mark != WD_FDC_AM_NONE) {
693 break;
696 if (f->id_mark == WD_FDC_AM_NONE) {
697 f->state = WD_FDC_STATE_NONE;
698 f->status_register |= WD_FDC_SR_RNF;
699 f->status_register &= ~WD_FDC_SR_BUSY;
700 wd_fdc_set_intrq(f);
701 f->read_id = 0;
702 return;
705 f->read_id = 0;
706 f->data_offset = 0;
707 wd_fdc_set_datarq(f);
710 fdd_ev_ifc->remove_all(timeout_event);
711 /* 2 revolutions: 2 * 200 / 1000 */
712 fdd_ev_ifc->add_new((fdd_turbo ? 1 : 2*20)*FDD_CPU_HZ/100, timeout_event, f);
716 //==========================================================================
718 // wd_fdc_event
720 //==========================================================================
721 static void wd_fdc_event (uint32_t event, void *user_data) {
722 wd_fdc *f = user_data;
723 fdd_t *d = f->current_drive;
725 if (event == timeout_event) {
726 if (f->state == WD_FDC_STATE_READ ||
727 f->state == WD_FDC_STATE_WRITE ||
728 f->state == WD_FDC_STATE_READTRACK ||
729 f->state == WD_FDC_STATE_WRITETRACK ||
730 f->state == WD_FDC_STATE_READID)
732 f->state = WD_FDC_STATE_NONE;
733 f->status_register |= WD_FDC_SR_LOST;
734 f->status_register &= ~WD_FDC_SR_BUSY;
735 wd_fdc_reset_datarq(f);
736 wd_fdc_set_intrq(f);
738 return;
741 if (event == motor_off_event) {
742 if (f->type == WD1770 || f->type == WD1772) {
743 f->status_register &= ~WD_FDC_SR_MOTORON;
744 fdd_motoron(d, 0);
745 } else {
746 /* WD1773/FD1973 */
747 f->head_load = 0;
748 if (f->flags&WD_FLAG_BETA128) fdd_motoron(d, 0); else fdd_head_load(d, 0);
750 return;
753 if ((f->type == WD1773 || f->type == FD1793 || f->type == WD2797) &&
754 f->hlt_time > 0 && f->head_load && !f->hlt)
756 f->hlt = 1;
759 if (((f->type == WD1770 || f->type == WD1772) &&
760 (f->status_register & WD_FDC_SR_MOTORON) &&
761 f->status_type == WD_FDC_STATUS_TYPE1) ||
762 ((f->type == WD1773 || f->type == FD1793 || f->type == WD2797) &&
763 (f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY) &&
764 f->head_load))
766 f->status_register |= WD_FDC_SR_SPINUP;
769 if (f->read_id) {
770 if (f->state == WD_FDC_STATE_VERIFY) {
771 wd_fdc_seek_verify_read_id(f);
772 } else if ((f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE) && f->datarq) {
773 f->datarq = 0;
774 wd_fdc_set_datarq(f);
775 } else if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE) {
776 wd_fdc_type_ii_seek(f);
777 } else if (f->state == WD_FDC_STATE_READID) {
778 wd_fdc_type_iii(f);
780 } else if (f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY) {
781 wd_fdc_type_i(f);
782 } else if (f->state == WD_FDC_STATE_VERIFY) {
783 wd_fdc_seek_verify(f);
784 } else if ((f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE) && f->datarq) {
785 f->datarq = 0;
786 wd_fdc_set_datarq(f);
787 } else if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_WRITE) {
788 wd_fdc_type_ii(f);
789 } else if ((f->state == WD_FDC_STATE_READTRACK ||
790 f->state == WD_FDC_STATE_READID ||
791 f->state == WD_FDC_STATE_WRITETRACK) && f->datarq)
793 f->datarq = 0;
794 wd_fdc_set_datarq(f);
795 } else if (f->state == WD_FDC_STATE_READTRACK ||
796 f->state == WD_FDC_STATE_READID ||
797 f->state == WD_FDC_STATE_WRITETRACK)
799 wd_fdc_type_iii(f);
804 /* this chart looks like most close of the reality...
806 +--+--+
807 / is \ no
808 ! MO = 0 !----->----+
809 \ ? / !
810 +--+--+ !
812 +--------------+--------+ !
813 ! set MO ! !
814 +--------------+--------+ v
816 +--+--+ !
817 / is \ no !
818 ! h = 0 !----->----+
819 \ ? / !
820 +--+--+ !
821 !yes !
822 +--------------+--------+ v
823 ! delay 6 index pulses ! !
824 +--------------+--------+ !
826 !-------<-------+
830 //==========================================================================
832 // wd_fdc_spinup
834 //==========================================================================
835 static int wd_fdc_spinup (wd_fdc *f, uint8_t b) {
836 uint32_t delay = 0;
837 fdd_t *d = f->current_drive;
839 if (f->state != WD_FDC_STATE_SEEK && (b&0x04)) delay = 30;
841 if (f->type == WD1770 || f->type == WD1772) {
842 if ((f->status_register&WD_FDC_SR_MOTORON) == 0) {
843 f->status_register |= WD_FDC_SR_MOTORON;
844 fdd_motoron(d, 1);
845 if ((b&0x08) == 0) delay += 6*200;
847 } else {
848 /* WD1773/FD1793/WD2797 */
849 fdd_ev_ifc->remove_all(motor_off_event);
850 if (f->state == WD_FDC_STATE_SEEK) {
851 if (b&0x08) {
852 f->head_load = 1;
853 if (f->flags & WD_FLAG_BETA128) fdd_motoron(d, 1); else fdd_head_load(d, 1);
854 } else if ((b&0x04) == 0) {
855 /* HLD reset only if V flag == 0 too */
856 f->head_load = 0;
857 if ((f->flags&WD_FLAG_NOHLT) == 0 && f->hlt_time > 0) f->hlt = 0; /* reset the trigger */
858 if (f->flags&WD_FLAG_BETA128) fdd_motoron(d, 0); else fdd_head_load(d, 0);
860 return 0;
861 } else {
862 f->head_load = 1;
863 if (f->flags&WD_FLAG_BETA128) fdd_motoron(d, 1); else fdd_head_load(d, 1);
864 if (f->hlt_time > 0) delay += f->hlt_time;
868 /* For Type III commands on WD2797 */
869 if (f->type == WD2797 && (b&0xc0) == 0xc0 && (b&0x30) != 0x10) {
870 fdd_set_head(d, (b&0x02 ? 1 : 0));
873 if (delay) {
874 fdd_ev_ifc->remove_all(fdc_event);
875 fdd_ev_ifc->add_new((fdd_turbo ? 1 : delay)*FDD_CPU_HZ/1000, fdc_event, f);
876 return 1;
879 return 0;
883 //==========================================================================
885 // wd_fdc_sr_read
887 //==========================================================================
888 uint8_t wd_fdc_sr_read (wd_fdc *f) {
889 if (!f) return 0xffU;
891 fdd_t *d = f->current_drive;
893 wd_fdc_reset_intrq(f);
895 if (f->status_type == WD_FDC_STATUS_TYPE1) {
896 f->status_register &= ~WD_FDC_SR_IDX_DRQ;
897 if (!d->loaded || d->index_pulse) {
898 f->status_register |= WD_FDC_SR_IDX_DRQ;
902 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
904 if (f->status_register & WD_FDC_SR_BUSY) f->status_register |= WD_FDC_SR_MOTORON;
905 else f->status_register &= ~WD_FDC_SR_MOTORON;
907 if (disk_ready(f)) {
908 f->status_register &= ~WD_FDC_SR_MOTORON;
909 } else {
910 f->status_register |= WD_FDC_SR_MOTORON;
914 return f->status_register;
918 //==========================================================================
920 // wd_fdc_cr_write
922 //==========================================================================
923 void wd_fdc_cr_write (wd_fdc *f, uint8_t b) {
924 if (!f) return;
926 fdd_t *d = f->current_drive;
928 wd_fdc_reset_intrq(f);
930 if ((b&0xf0) == 0xd0) {
931 /* Type IV - Force Interrupt */
932 fdd_ev_ifc->remove_all(fdc_event);
933 f->status_register &= ~(WD_FDC_SR_BUSY|WD_FDC_SR_WRPROT|WD_FDC_SR_CRCERR|WD_FDC_SR_IDX_DRQ);
934 f->state = WD_FDC_STATE_NONE;
935 f->status_type = WD_FDC_STATUS_TYPE1;
936 wd_fdc_reset_datarq(f);
938 if (b&0x08) {
939 wd_fdc_set_intrq(f);
940 } else if (b&0x04) {
941 d->fdc_index = wd_fdc_wait_index;
942 d->fdc = f;
945 if (d->tr00) {
946 f->status_register |= WD_FDC_SR_LOST;
947 } else {
948 f->status_register &= ~WD_FDC_SR_LOST;
951 wd_fdc_spinup(f, b&0xf7); /* spinup motor, but we do not have a 'h' bit! */
953 return;
956 if (f->status_register&WD_FDC_SR_BUSY) return;
958 f->command_register = b;
959 f->status_register |= WD_FDC_SR_BUSY;
961 /* keep spindle motor on: */
962 fdd_ev_ifc->remove_all(motor_off_event);
964 if ((b&0x80) == 0) {
965 /* Type I */
966 f->state = WD_FDC_STATE_SEEK;
967 f->status_type = WD_FDC_STATUS_TYPE1;
968 f->status_register &= ~(WD_FDC_SR_CRCERR|WD_FDC_SR_RNF|WD_FDC_SR_IDX_DRQ);
969 wd_fdc_reset_datarq(f);
970 f->rev = 5;
971 if (wd_fdc_spinup(f, b)) return;
972 wd_fdc_type_i(f);
973 } else if ((b&0x40) == 0) {
974 /* Type II */
975 if (f->type == WD1773 || f->type == FD1793) {
976 if (!disk_ready(f)) {
977 f->status_register &= ~WD_FDC_SR_BUSY;
978 f->state = WD_FDC_STATE_NONE;
979 wd_fdc_set_intrq(f);
980 return;
984 if (f->type == WD1773 && (b&0x02)) f->data_check_head = (b&0x08 ? 1 : 0);
985 else if (f->type == WD2797) f->data_check_head = (b&0x02 ? 1 : 0);
986 else f->data_check_head = -1;
988 /* WD2797 (and FD1797) can read sectors with non-IBM-compatible sector length codes */
989 f->non_ibm_len_code = (f->type == WD2797 && (b&0x08) == 0 ? 1 : 0);
991 f->state = (b&0x20 ? WD_FDC_STATE_WRITE : WD_FDC_STATE_READ);
992 f->status_type = WD_FDC_STATUS_TYPE2;
993 f->status_register &= ~(WD_FDC_SR_WRPROT|WD_FDC_SR_RNF|
994 WD_FDC_SR_IDX_DRQ|WD_FDC_SR_LOST|
995 WD_FDC_SR_SPINUP);
997 if (f->type == WD2797) fdd_set_head(d, (b&0x02 ? 1 : 0));
999 f->rev = 5;
1000 if (wd_fdc_spinup(f, b)) return;
1001 wd_fdc_type_ii(f);
1002 } else if ((b&0x30) != 0x10) {
1003 /* Type III */
1004 if (f->type == WD1773 || f->type == FD1793 || f->type == WD2797) {
1005 if (!disk_ready(f)) {
1006 f->status_register &= ~WD_FDC_SR_BUSY;
1007 f->state = WD_FDC_STATE_NONE;
1008 wd_fdc_set_intrq(f);
1009 return;
1013 f->state = (b&0x20 ? (b&0x10 ? WD_FDC_STATE_WRITETRACK : WD_FDC_STATE_READTRACK) : WD_FDC_STATE_READID);
1014 f->status_type = WD_FDC_STATUS_TYPE2;
1015 f->status_register &= ~(WD_FDC_SR_SPINUP|WD_FDC_SR_RNF|WD_FDC_SR_IDX_DRQ|WD_FDC_SR_LOST);
1017 f->rev = 5;
1018 if (wd_fdc_spinup(f, b)) return;
1019 wd_fdc_type_iii(f);
1024 //==========================================================================
1026 // wd_fdc_tr_read
1028 //==========================================================================
1029 uint8_t wd_fdc_tr_read (wd_fdc *f) {
1030 return (f ? f->track_register : 0xffU);
1034 //==========================================================================
1036 // wd_fdc_tr_write
1038 //==========================================================================
1039 void wd_fdc_tr_write (wd_fdc *f, uint8_t b) {
1040 if (f) f->track_register = b;
1044 //==========================================================================
1046 // wd_fdc_sec_read
1048 //==========================================================================
1049 uint8_t wd_fdc_sec_read (wd_fdc *f) {
1050 return (f ? f->sector_register : 0xffU);
1054 //==========================================================================
1056 // wd_fdc_sec_write
1058 //==========================================================================
1059 void wd_fdc_sec_write (wd_fdc *f, uint8_t b) {
1060 if (f) f->sector_register = b;
1064 //==========================================================================
1066 // wd_fdc_dr_read
1068 //==========================================================================
1069 uint8_t wd_fdc_dr_read (wd_fdc *f) {
1070 if (!f) return 0xffU;
1072 fdd_t *d = f->current_drive;
1074 if ((f->flags&WD_FLAG_DRQ) && (f->status_register&WD_FDC_SR_BUSY)) {
1075 fdd_ev_ifc->remove_all(fdc_event);
1078 if (f->state == WD_FDC_STATE_READ) {
1079 ++f->data_offset; /* count read bytes */
1080 fdd_read_data(d); crc_add(f, d); /* read a byte */
1081 if (d->data > 0xff) {
1082 /* no data */
1083 f->status_register |= WD_FDC_SR_RNF;
1084 f->status_register &= ~WD_FDC_SR_BUSY;
1085 f->status_type = WD_FDC_STATUS_TYPE2;
1086 f->state = WD_FDC_STATE_NONE;
1087 wd_fdc_set_intrq(f);
1088 wd_fdc_reset_datarq(f);
1089 } else {
1090 f->data_register = d->data;
1091 if (f->data_offset == f->sector_length) {
1092 /* read the CRC */
1093 fdd_read_data(d); crc_add(f, d);
1094 fdd_read_data(d); crc_add(f, d);
1096 /* FIXME: make this per-FDC */
1097 fdd_ev_ifc->remove_all(timeout_event); /* clear the timeout */
1099 if (f->crc == 0x0000 && f->data_multisector) {
1100 ++f->sector_register;
1101 f->rev = 5;
1102 wd_fdc_reset_datarq(f);
1103 /* 5 revolutions: 5 * 200 / 1000 */
1104 fdd_ev_ifc->add_new(FDD_CPU_HZ, timeout_event, f);
1105 /* 20 ms delay */
1106 fdd_ev_ifc->add_new((fdd_turbo ? 1 : 20)*FDD_CPU_HZ/1000, fdc_event, f);
1107 } else {
1108 f->status_register &= ~WD_FDC_SR_BUSY;
1109 if (f->crc == 0x0000) {
1110 f->status_register &= ~WD_FDC_SR_CRCERR;
1111 } else {
1112 f->status_register |= WD_FDC_SR_CRCERR;
1114 f->status_type = WD_FDC_STATUS_TYPE2;
1115 f->state = WD_FDC_STATE_NONE;
1116 wd_fdc_set_intrq(f);
1117 wd_fdc_reset_datarq(f);
1121 } else if (f->state == WD_FDC_STATE_READID) {
1122 switch (f->data_offset) {
1123 case 0: /* track */
1124 f->data_register = f->id_track;
1125 break;
1126 case 1: /* head */
1127 f->data_register = f->id_head;
1128 break;
1129 case 2: /* sector */
1130 f->data_register = f->id_sector;
1131 break;
1132 case 3: /* length */
1133 f->data_register = f->id_length;
1134 break;
1135 case 4: /* crc1 */
1136 f->data_register = f->crc >> 8;
1137 break;
1138 case 5: /* crc2 */
1139 f->sector_register = f->id_track;
1140 f->data_register = f->crc&0xff;
1141 f->status_register &= ~WD_FDC_SR_BUSY;
1142 f->status_type = WD_FDC_STATUS_TYPE2;
1143 f->state = WD_FDC_STATE_NONE;
1144 fdd_ev_ifc->remove_all(timeout_event); /* clear the timeout */
1145 wd_fdc_set_intrq(f);
1146 wd_fdc_reset_datarq(f);
1147 break;
1148 default:
1149 break;
1151 ++f->data_offset;
1152 } else if (f->state == WD_FDC_STATE_READTRACK) {
1153 /* unformatted/out of track looks like 1x 0x00 */
1154 fdd_read_data(d); /* read a byte and give to host */
1155 f->data_register = d->data&0x00ff; /* drop clock marks */
1156 if (d->index) {
1157 fdd_ev_ifc->remove_all(timeout_event); /* clear the timeout */
1158 f->status_register &= ~WD_FDC_SR_BUSY;
1159 f->status_type = WD_FDC_STATUS_TYPE2;
1160 f->state = WD_FDC_STATE_NONE;
1161 wd_fdc_set_intrq(f);
1162 wd_fdc_reset_datarq(f);
1166 if ((f->flags&WD_FLAG_DRQ) && (f->status_register&WD_FDC_SR_BUSY)) {
1167 /* we need a next datarq */
1168 /* 30 us delay */
1169 fdd_ev_ifc->add_new(30*FDD_CPU_HZ/1000000, fdc_event, f);
1172 return f->data_register;
1176 //==========================================================================
1178 // wd_fdc_dr_write
1180 //==========================================================================
1181 void wd_fdc_dr_write (wd_fdc *f, uint8_t b) {
1182 if (!f) return;
1184 fdd_t *d = f->current_drive;
1186 f->data_register = b;
1187 if (f->state == WD_FDC_STATE_WRITE) {
1188 d->data = b;
1189 ++f->data_offset; /* count bytes read */
1190 fdd_write_data(d);
1191 crc_add(f, d);
1192 if (f->data_offset == f->sector_length) {
1193 /* write the CRC */
1194 d->data = f->crc>>8;
1195 fdd_write_data(d); /* write crc1 */
1196 d->data = f->crc&0xff;
1197 fdd_write_data(d); /* write crc2 */
1198 d->data = 0xff;
1199 fdd_write_data(d); /* write 1 byte of ff? */
1201 fdd_ev_ifc->remove_all(timeout_event); /* clear the timeout */
1203 if (f->data_multisector) {
1204 ++f->sector_register;
1205 f->rev = 5;
1206 wd_fdc_reset_datarq(f);
1207 /* 5 revolutions: 5 * 200 / 1000 */
1208 fdd_ev_ifc->add_new(FDD_CPU_HZ, timeout_event, f);
1209 /* 20ms delay */
1210 fdd_ev_ifc->add_new((fdd_turbo ? 1 : 20)*FDD_CPU_HZ/1000, fdc_event, f);
1211 } else {
1212 f->status_register &= ~WD_FDC_SR_BUSY;
1213 f->status_type = WD_FDC_STATUS_TYPE2;
1214 f->state = WD_FDC_STATE_NONE;
1215 wd_fdc_set_intrq(f);
1216 wd_fdc_reset_datarq(f);
1219 } else if (f->state == WD_FDC_STATE_WRITETRACK) {
1220 d->data = b;
1221 if (f->dden) {
1222 /* MFM */
1223 if (b == 0xf7) {
1224 /* CRC */
1225 d->data = f->crc>>8;
1226 fdd_write_data(d); /* write crc1 */
1227 d->data = f->crc&0xff;
1228 } else if (b == 0xf5) {
1229 d->data = 0xffa1; /* and preset CRC */
1230 f->crc = 0xcdb4; /* 3x crc = crc_fdc( crc, 0xa1 )???? */
1231 } else if (b == 0xf6) {
1232 d->data = 0xffc2;
1233 } else {
1234 crc_add(f, d);
1236 } else {
1237 /* FM */
1238 if (b == 0xf7) {
1239 /* CRC */
1240 d->data = f->crc>>8;
1241 fdd_write_data(d); /* write crc1 */
1242 d->data = f->crc&0xff;
1243 } else if (b == 0xfe || (b >= 0xf8 && b <= 0xfb)) {
1244 crc_preset(f); /* preset CRC */
1245 crc_add(f, d);
1246 d->data |= 0xff00;
1247 } else if (b == 0xfc) {
1248 d->data |= 0xff00;
1249 } else {
1250 crc_add(f, d);
1253 fdd_write_data(d); /* write a byte */
1255 if (d->index) {
1256 fdd_ev_ifc->remove_all(timeout_event); /* clear the timeout */
1257 f->status_register &= ~WD_FDC_SR_BUSY;
1258 f->state = WD_FDC_STATE_NONE;
1259 wd_fdc_set_intrq(f);
1260 wd_fdc_reset_datarq(f);
1264 if ((f->flags&WD_FLAG_DRQ) &&
1265 (f->status_register&WD_FDC_SR_BUSY)) {
1266 /* we need a next datarq */
1267 /* wd_fdc_reset_datarq(f); */
1268 /* 30 us delay */
1269 fdd_ev_ifc->add_new(30*FDD_CPU_HZ/1000000, fdc_event, f);
1274 //==========================================================================
1276 // wd_fdc_master_reset
1278 //==========================================================================
1279 void wd_fdc_master_reset (wd_fdc *f) {
1280 if (!f) return;
1282 fdd_t *d = f->current_drive;
1284 f->spin_cycles = 0;
1285 f->direction = 0;
1286 f->head_load = 0;
1287 if (d) {
1288 if (f->flags&WD_FLAG_BETA128) {
1289 fdd_motoron(d, 0);
1290 } else {
1291 fdd_head_load(d, 0);
1294 f->read_id = 0;
1295 f->hlt = 1;
1296 if ((f->flags&WD_FLAG_NOHLT) == 0 && f->hlt_time > 0) f->hlt = 0;
1297 f->intrq = 0;
1298 f->datarq = 0;
1300 f->state = WD_FDC_STATE_NONE;
1301 f->status_type = WD_FDC_STATUS_TYPE1;
1303 if (d != NULL) {
1304 while (!d->tr00) fdd_step(d, FDD_STEP_OUT);
1307 f->track_register = 0;
1308 f->sector_register = 0;
1309 f->data_register = 0;
1310 f->status_register = WD_FDC_SR_LOST; /* track 0 */
1314 //==========================================================================
1316 // wd_fdc_alloc_fdc
1318 //==========================================================================
1319 wd_fdc *wd_fdc_alloc_fdc (wd_type_t type, int hlt_time, unsigned int flags) {
1320 wd_fdc *fdc = calloc(1, sizeof(wd_fdc));
1321 if (!fdc) return NULL;
1323 switch (type) {
1324 default:
1325 type = WD1770; /* illegal type converted to wd_fdc */
1326 /* fallthrough */
1327 case FD1793:
1328 case WD1773:
1329 case WD1770:
1330 case WD2797:
1331 fdc->rates[0] = 6;
1332 fdc->rates[1] = 12;
1333 fdc->rates[2] = 20;
1334 fdc->rates[3] = 30;
1335 break;
1336 case WD1772:
1337 fdc->rates[0] = 2;
1338 fdc->rates[1] = 3;
1339 fdc->rates[2] = 5;
1340 fdc->rates[3] = 6;
1341 break;
1343 fdc->type = type;
1344 fdc->current_drive = NULL;
1345 fdc->hlt_time = hlt_time;
1346 fdc->flags = flags; /* Beta128 connect HLD out to READY in and MOTOR ON */
1347 wd_fdc_master_reset(fdc);
1349 return fdc;
1353 //==========================================================================
1355 // wd_fdc_init_events
1357 //==========================================================================
1358 void wd_fdc_init_events (void) {
1359 fdc_event = fdd_ev_ifc->register_new(wd_fdc_event, "WD FDC event");
1360 motor_off_event = fdd_ev_ifc->register_new(wd_fdc_event, "WD FDC motor off");
1361 timeout_event = fdd_ev_ifc->register_new(wd_fdc_event, "WD FDC timeout");
1363 #endif /* LIBFUSE_DISK_IO_ONLY */