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
35 static uint32_t fdc_event
, motor_off_event
, timeout_event
;
38 //==========================================================================
42 //==========================================================================
43 static inline void crc_preset (wd_fdc
*f
) {
48 //==========================================================================
52 //==========================================================================
53 static inline void crc_add (wd_fdc
*f
, fdd_t
*d
) {
54 f
->crc
= crc_fdc(f
->crc
, d
->data
&0xff);
58 //==========================================================================
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
) &&
74 /* 15 revolution: 15 * 200 / 1000 */
75 fdd_ev_ifc
->add_new(3*FDD_CPU_HZ
, motor_off_event
, f
);
80 if (f
->set_intrq
) f
->set_intrq(f
);
85 //==========================================================================
89 //==========================================================================
90 static void wd_fdc_reset_intrq (wd_fdc
*f
) {
93 if (f
->reset_intrq
) f
->reset_intrq(f
);
98 //==========================================================================
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
;
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
;
121 if (f
->reset_datarq
) f
->reset_datarq(f
);
126 //==========================================================================
130 //==========================================================================
131 void wd_fdc_set_hlt (wd_fdc
*f
, int hlt
) {
132 f
->hlt
= (hlt
> 0 ? 1 : 0);
136 //==========================================================================
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 //==========================================================================
152 //==========================================================================
153 static void wd_fdc_wait_index (void *fdc
) {
155 wd_fdc_set_intrq(f
); /* generate an interrupt */
159 //==========================================================================
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
) {
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
) {
182 /* double density (MFM) */
184 if (d
->index
) --f
->rev
;
186 if (d
->data
== 0xffa1) {
189 if (d
->index
) --f
->rev
;
190 if (d
->data
!= 0xffa1) continue;
193 if (d
->index
) --f
->rev
;
194 if (d
->data
!= 0xffa1) continue;
196 /* no 0xa1 with missing clock... */
202 if (d
->index
) --f
->rev
;
204 /* double density (MFM) */
205 if (d
->data
!= 0x00fe) continue;
207 /* single density (FM) */
208 if (d
->data
!= 0xfffe) continue;
212 if (d
->index
) --f
->rev
;
213 f
->id_track
= d
->data
;
216 if (d
->index
) --f
->rev
;
217 f
->id_head
= d
->data
;
220 if (d
->index
) --f
->rev
;
221 f
->id_sector
= d
->data
;
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);
230 /* 00->128 01->256 10->512 11->1024 */
231 f
->sector_length
= 0x80<<(d
->data
&0x03);
235 if (d
->index
) --f
->rev
;
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
;
244 f
->status_register
&= ~WD_FDC_SR_CRCERR
;
245 f
->id_mark
= WD_FDC_AM_ID
;
253 //==========================================================================
257 //==========================================================================
258 static int read_datamark (wd_fdc
*f
) {
259 fdd_t
*d
= f
->current_drive
;
262 f
->id_mark
= WD_FDC_AM_NONE
;
265 /* double density (MFM) */
266 for (i
= 40; i
> 0; --i
) {
268 if (d
->data
== 0x4e) continue; /* read next */
269 if (d
->data
== 0x00) break; /* go to PLL sync */
270 return 1; /* something wrong... */
276 if (d
->data
== 0x00) continue;
277 if (d
->data
== 0xffa1) break; /* got to a1 mark */
280 for (i
= (d
->data
== 0xffa1 ? 2 : 3); i
> 0; --i
) {
283 if (d
->data
!= 0xffa1) return 1;
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
;
293 for (i
= 30; i
> 0; --i
) {
295 if (d
->data
== 0xff) continue; /* read next */
296 if (d
->data
== 0x00) break; /* go to PLL sync */
297 return 1; /* something wrong... */
303 if (d
->data
== 0x00) continue;
304 if (d
->data
>= 0xfff8 && d
->data
<= 0xfffb) break; /* !fb deleted mark */
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
;
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
;
330 fdd_ev_ifc
->remove_all(fdc_event
);
332 if (f
->id_mark
== WD_FDC_AM_NONE
) {
334 i
= (d
->diskpos
.i
>= d
->disk
->bpt
? 0 : d
->diskpos
.i
); /* start position */
336 if (f
->id_track
!= f
->track_register
) {
337 f
->status_register
|= WD_FDC_SR_RNF
;
340 f
->id_mark
= WD_FDC_AM_NONE
;
342 i
= (d
->disk
->bpt
? (d
->diskpos
.i
-i
)*200/d
->disk
->bpt
: 200);
344 /* i * 1/20 revolution */
345 fdd_ev_ifc
->add_new((fdd_turbo
? 1 : i
)*FDD_CPU_HZ
/1000, fdc_event
, f
);
347 } else if (f
->id_mark
!= WD_FDC_AM_NONE
) {
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
;
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
) {
372 /* sample every 5 ms */
373 fdd_ev_ifc
->add_new((fdd_turbo
? 1 : 5)*FDD_CPU_HZ
/1000, fdc_event
, f
);
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. */
384 f
->status_register
|= WD_FDC_SR_LOST
;
386 f
->status_register
&= ~WD_FDC_SR_LOST
;
390 f
->id_mark
= WD_FDC_AM_NONE
;
391 wd_fdc_seek_verify_read_id(f
);
395 //==========================================================================
399 // if (f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY)
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
) {
409 if ((b
&0x60) != 0x00) goto type_i_verify
; /* STEP/STEP-IN/STEP-OUT */
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 */
426 f
->track_register
= 0xff;
427 f
->data_register
= 0;
431 if (f
->track_register
!= f
->data_register
) {
432 f
->direction
= (f
->track_register
< f
->data_register
? FDD_STEP_IN
: FDD_STEP_OUT
);
435 f
->track_register
+= (f
->direction
== FDD_STEP_IN
? 1 : -1);
438 if (d
->tr00
&& f
->direction
== FDD_STEP_OUT
) {
439 f
->track_register
= 0;
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
);
451 if (f
->type
== WD1773
|| f
->type
== FD1793
|| f
->type
== WD2797
) {
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
);
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
);
473 wd_fdc_seek_verify(f
);
478 f
->status_register
|= WD_FDC_SR_LOST
;
480 f
->status_register
&= ~WD_FDC_SR_LOST
;
483 f
->state
= WD_FDC_STATE_NONE
;
484 f
->status_register
&= ~WD_FDC_SR_BUSY
;
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
;
502 fdd_ev_ifc
->remove_all(fdc_event
);
504 if (f
->id_mark
== WD_FDC_AM_NONE
) {
507 i
= (d
->diskpos
.i
>= d
->disk
->bpt
? 0 : d
->diskpos
.i
); /* start position */
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
;
515 f
->id_mark
= WD_FDC_AM_NONE
;
517 i
= (d
->disk
->bpt
? (d
->diskpos
.i
-i
)*200/d
->disk
->bpt
: 200);
519 /* i * 1/20 revolution */
520 fdd_ev_ifc
->add_new((fdd_turbo
? 1 : i
)*FDD_CPU_HZ
/1000, fdc_event
, f
);
522 } else if (f
->id_mark
!= WD_FDC_AM_NONE
) {
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
;
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
) {
543 f
->status_register
|= WD_FDC_SR_RNF
;
544 f
->status_register
&= ~WD_FDC_SR_BUSY
;
545 f
->state
= WD_FDC_STATE_NONE
;
550 if (f
->ddam
) f
->status_register
|= WD_FDC_SR_SPINUP
; /* set deleted data mark */
552 wd_fdc_set_datarq(f
);
555 for (i
= 11; i
> 0; --i
) fdd_read_data(d
); /* "delay" 11 GAP byte */
556 wd_fdc_set_datarq(f
);
559 for (i
= 11; i
> 0; --i
) fdd_read_data(d
); /* "delay" another 11 GAP byte */
562 for (i
= (f
->dden
? 12 : 6); i
> 0; --i
) fdd_write_data(d
); /* write 6/12 zero */
567 for (i
= 3; i
> 0; --i
) {
568 /* write 3 0xa1 with clock mark */
573 d
->data
= (f
->ddam
? 0x00f8 : 0x00fb)|(f
->dden
? 0x0000 : 0xff00); /* write data mark */
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 //==========================================================================
588 // if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_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
;
606 fdd_ev_ifc
->add_new((fdd_turbo
? 1 : 5)*FDD_CPU_HZ
/1000, fdc_event
, f
);
611 if (f
->state
== WD_FDC_STATE_WRITE
) {
613 f
->status_register
|= WD_FDC_SR_WRPROT
;
614 f
->status_register
&= ~WD_FDC_SR_BUSY
;
615 f
->state
= WD_FDC_STATE_NONE
;
619 f
->status_register
&= ~WD_FDC_SR_WRPROT
;
622 f
->data_multisector
= (b
&0x10 ? 1 : 0);
624 f
->id_mark
= WD_FDC_AM_NONE
;
625 wd_fdc_type_ii_seek(f
);
629 //==========================================================================
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)
639 //==========================================================================
640 static void wd_fdc_type_iii (wd_fdc
*f
) {
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
;
654 fdd_ev_ifc
->add_new((fdd_turbo
? 1 : 5)*FDD_CPU_HZ
/1000, fdc_event
, f
);
659 if (f
->state
== WD_FDC_STATE_WRITETRACK
) {
660 /* ----WRITE TRACK---- */
662 f
->status_register
|= WD_FDC_SR_WRPROT
;
663 f
->status_register
&= ~WD_FDC_SR_BUSY
;
664 f
->state
= WD_FDC_STATE_NONE
;
668 f
->status_register
&= ~WD_FDC_SR_WRPROT
;
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
);
681 f
->id_mark
= WD_FDC_AM_NONE
;
683 if (f
->id_mark
== WD_FDC_AM_NONE
) {
685 i
= (d
->diskpos
.i
>= d
->disk
->bpt
? 0 : d
->diskpos
.i
); /* start position */
687 i
= (d
->disk
->bpt
? (d
->diskpos
.i
-i
)*200/d
->disk
->bpt
: 200);
689 /* i * 1/20 revolution */
690 fdd_ev_ifc
->add_new((fdd_turbo
? 1 : i
)*FDD_CPU_HZ
/1000, fdc_event
, f
);
692 } else if (f
->id_mark
!= WD_FDC_AM_NONE
) {
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
;
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 //==========================================================================
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
);
741 if (event
== motor_off_event
) {
742 if (f
->type
== WD1770
|| f
->type
== WD1772
) {
743 f
->status_register
&= ~WD_FDC_SR_MOTORON
;
748 if (f
->flags
&WD_FLAG_BETA128
) fdd_motoron(d
, 0); else fdd_head_load(d
, 0);
753 if ((f
->type
== WD1773
|| f
->type
== FD1793
|| f
->type
== WD2797
) &&
754 f
->hlt_time
> 0 && f
->head_load
&& !f
->hlt
)
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
) &&
766 f
->status_register
|= WD_FDC_SR_SPINUP
;
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
) {
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
) {
780 } else if (f
->state
== WD_FDC_STATE_SEEK
|| f
->state
== WD_FDC_STATE_SEEK_DELAY
) {
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
) {
786 wd_fdc_set_datarq(f
);
787 } else if (f
->state
== WD_FDC_STATE_READ
|| f
->state
== WD_FDC_STATE_WRITE
) {
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
)
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
)
804 /* this chart looks like most close of the reality...
808 ! MO = 0 !----->----+
812 +--------------+--------+ !
814 +--------------+--------+ v
822 +--------------+--------+ v
823 ! delay 6 index pulses ! !
824 +--------------+--------+ !
830 //==========================================================================
834 //==========================================================================
835 static int wd_fdc_spinup (wd_fdc
*f
, uint8_t b
) {
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
;
845 if ((b
&0x08) == 0) delay
+= 6*200;
848 /* WD1773/FD1793/WD2797 */
849 fdd_ev_ifc
->remove_all(motor_off_event
);
850 if (f
->state
== WD_FDC_STATE_SEEK
) {
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 */
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);
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));
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
);
883 //==========================================================================
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;
908 f
->status_register
&= ~WD_FDC_SR_MOTORON
;
910 f
->status_register
|= WD_FDC_SR_MOTORON
;
914 return f
->status_register
;
918 //==========================================================================
922 //==========================================================================
923 void wd_fdc_cr_write (wd_fdc
*f
, uint8_t b
) {
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
);
941 d
->fdc_index
= wd_fdc_wait_index
;
946 f
->status_register
|= WD_FDC_SR_LOST
;
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! */
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
);
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
);
971 if (wd_fdc_spinup(f
, b
)) return;
973 } else if ((b
&0x40) == 0) {
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
;
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
|
997 if (f
->type
== WD2797
) fdd_set_head(d
, (b
&0x02 ? 1 : 0));
1000 if (wd_fdc_spinup(f
, b
)) return;
1002 } else if ((b
&0x30) != 0x10) {
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
);
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
);
1018 if (wd_fdc_spinup(f
, b
)) return;
1024 //==========================================================================
1028 //==========================================================================
1029 uint8_t wd_fdc_tr_read (wd_fdc
*f
) {
1030 return (f
? f
->track_register
: 0xffU
);
1034 //==========================================================================
1038 //==========================================================================
1039 void wd_fdc_tr_write (wd_fdc
*f
, uint8_t b
) {
1040 if (f
) f
->track_register
= b
;
1044 //==========================================================================
1048 //==========================================================================
1049 uint8_t wd_fdc_sec_read (wd_fdc
*f
) {
1050 return (f
? f
->sector_register
: 0xffU
);
1054 //==========================================================================
1058 //==========================================================================
1059 void wd_fdc_sec_write (wd_fdc
*f
, uint8_t b
) {
1060 if (f
) f
->sector_register
= b
;
1064 //==========================================================================
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) {
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
);
1090 f
->data_register
= d
->data
;
1091 if (f
->data_offset
== f
->sector_length
) {
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
;
1102 wd_fdc_reset_datarq(f
);
1103 /* 5 revolutions: 5 * 200 / 1000 */
1104 fdd_ev_ifc
->add_new(FDD_CPU_HZ
, timeout_event
, f
);
1106 fdd_ev_ifc
->add_new((fdd_turbo
? 1 : 20)*FDD_CPU_HZ
/1000, fdc_event
, f
);
1108 f
->status_register
&= ~WD_FDC_SR_BUSY
;
1109 if (f
->crc
== 0x0000) {
1110 f
->status_register
&= ~WD_FDC_SR_CRCERR
;
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
) {
1124 f
->data_register
= f
->id_track
;
1127 f
->data_register
= f
->id_head
;
1129 case 2: /* sector */
1130 f
->data_register
= f
->id_sector
;
1132 case 3: /* length */
1133 f
->data_register
= f
->id_length
;
1136 f
->data_register
= f
->crc
>> 8;
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
);
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 */
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 */
1169 fdd_ev_ifc
->add_new(30*FDD_CPU_HZ
/1000000, fdc_event
, f
);
1172 return f
->data_register
;
1176 //==========================================================================
1180 //==========================================================================
1181 void wd_fdc_dr_write (wd_fdc
*f
, uint8_t b
) {
1184 fdd_t
*d
= f
->current_drive
;
1186 f
->data_register
= b
;
1187 if (f
->state
== WD_FDC_STATE_WRITE
) {
1189 ++f
->data_offset
; /* count bytes read */
1192 if (f
->data_offset
== f
->sector_length
) {
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 */
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
;
1206 wd_fdc_reset_datarq(f
);
1207 /* 5 revolutions: 5 * 200 / 1000 */
1208 fdd_ev_ifc
->add_new(FDD_CPU_HZ
, timeout_event
, f
);
1210 fdd_ev_ifc
->add_new((fdd_turbo
? 1 : 20)*FDD_CPU_HZ
/1000, fdc_event
, f
);
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
) {
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) {
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 */
1247 } else if (b
== 0xfc) {
1253 fdd_write_data(d
); /* write a byte */
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); */
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
) {
1282 fdd_t
*d
= f
->current_drive
;
1288 if (f
->flags
&WD_FLAG_BETA128
) {
1291 fdd_head_load(d
, 0);
1296 if ((f
->flags
&WD_FLAG_NOHLT
) == 0 && f
->hlt_time
> 0) f
->hlt
= 0;
1300 f
->state
= WD_FDC_STATE_NONE
;
1301 f
->status_type
= WD_FDC_STATUS_TYPE1
;
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 //==========================================================================
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
;
1325 type
= WD1770
; /* illegal type converted to wd_fdc */
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
);
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 */