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"
36 static uint32_t fdc_event
, motor_off_event
, timeout_event
;
39 //==========================================================================
43 //==========================================================================
44 static inline void crc_preset (wd_fdc
*f
) {
49 //==========================================================================
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
;
71 if (f
->flags
&WD_FLAG_BETA128
) {
79 if (!(f
->flags
&WD_FLAG_NOHLT
) && f
->hlt_time
> 0) f
->hlt
= 0;
83 f
->state
= WD_FDC_STATE_NONE
;
84 f
->status_type
= WD_FDC_STATUS_TYPE1
;
87 while (!d
->tr00
) fdd_step(d
, FDD_STEP_OUT
);
90 f
->track_register
= 0;
91 f
->sector_register
= 0;
93 f
->status_register
= WD_FDC_SR_LOST
; /* track 0 */
97 //==========================================================================
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
));
107 type
= WD1770
; /* illegal type converted to wd_fdc */
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
);
134 //==========================================================================
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
) &&
151 emuEvAdd( /*tstates +*/ 3 * /* 15 revolution: 15 * 200 / 1000 */
152 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
,
153 motor_off_event
, f
);
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
) {
171 if (f
->reset_intrq
) f
->reset_intrq(f
);
176 //==========================================================================
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
;
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
;
199 if (f
->reset_datarq
) f
->reset_datarq(f
);
204 //==========================================================================
208 //==========================================================================
209 void wd_fdc_set_hlt (wd_fdc
*f
, int hlt
) {
210 f
->hlt
= (hlt
> 0 ? 1 : 0);
214 //==========================================================================
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 //==========================================================================
230 //==========================================================================
231 static void wd_fdc_wait_index (void *fdc
) {
233 wd_fdc_set_intrq(f
); /* generate an interrupt */
237 //==========================================================================
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
) {
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
) {
260 /* double density (MFM) */
262 if (d
->index
) --f
->rev
;
264 if (d
->data
== 0xffa1) {
267 if (d
->index
) --f
->rev
;
268 if (d
->data
!= 0xffa1) continue;
271 if (d
->index
) --f
->rev
;
272 if (d
->data
!= 0xffa1) continue;
274 /* no 0xa1 with missing clock... */
280 if (d
->index
) --f
->rev
;
282 /* double density (MFM) */
283 if (d
->data
!= 0x00fe) continue;
285 /* single density (FM) */
286 if (d
->data
!= 0xfffe) continue;
290 if (d
->index
) --f
->rev
;
291 f
->id_track
= d
->data
;
294 if (d
->index
) --f
->rev
;
295 f
->id_head
= d
->data
;
298 if (d
->index
) --f
->rev
;
299 f
->id_sector
= d
->data
;
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);
308 /* 00->128 01->256 10->512 11->1024 */
309 f
->sector_length
= 0x80<<(d
->data
&0x03);
313 if (d
->index
) --f
->rev
;
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
;
322 f
->status_register
&= ~WD_FDC_SR_CRCERR
;
323 f
->id_mark
= WD_FDC_AM_ID
;
331 //==========================================================================
335 //==========================================================================
336 static int read_datamark (wd_fdc
*f
) {
337 fdd_t
*d
= f
->current_drive
;
340 f
->id_mark
= WD_FDC_AM_NONE
;
343 /* double density (MFM) */
344 for (i
= 40; i
> 0; --i
) {
346 if (d
->data
== 0x4e) continue; /* read next */
347 if (d
->data
== 0x00) break; /* go to PLL sync */
348 return 1; /* something wrong... */
354 if (d
->data
== 0x00) continue;
355 if (d
->data
== 0xffa1) break; /* got to a1 mark */
358 for (i
= (d
->data
== 0xffa1 ? 2 : 3); i
> 0; --i
) {
361 if (d
->data
!= 0xffa1) return 1;
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
;
371 for (i
= 30; i
> 0; --i
) {
373 if (d
->data
== 0xff) continue; /* read next */
374 if (d
->data
== 0x00) break; /* go to PLL sync */
375 return 1; /* something wrong... */
381 if (d
->data
== 0x00) continue;
382 if (d
->data
>= 0xfff8 && d
->data
<= 0xfffb) break; /* !fb deleted mark */
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
;
398 //==========================================================================
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;
421 f
->status_register
&= ~WD_FDC_SR_MOTORON
;
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
;
441 emuEvRemoveAllWithType(fdc_event
);
443 if (f
->id_mark
== WD_FDC_AM_NONE
) {
445 i
= (d
->disk
.i
>= d
->disk
.bpt
? 0 : d
->disk
.i
); /* start position */
447 if (f
->id_track
!= f
->track_register
) {
448 f
->status_register
|= WD_FDC_SR_RNF
;
451 f
->id_mark
= WD_FDC_AM_NONE
;
453 i
= (d
->disk
.bpt
? (d
->disk
.i
-i
)*200/d
->disk
.bpt
: 200);
455 emuEvAdd(/*tstates +*/ i
* /* i * 1/20 revolution */
456 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/ 1000,
459 } else if (f
->id_mark
!= WD_FDC_AM_NONE
) {
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
;
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
) {
484 emuEvAdd(/*tstates +*/ 5 * /* sample every 5 ms */
485 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/ 1000,
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. */
497 f
->status_register
|= WD_FDC_SR_LOST
;
499 f
->status_register
&= ~WD_FDC_SR_LOST
;
503 f
->id_mark
= WD_FDC_AM_NONE
;
504 wd_fdc_seek_verify_read_id(f
);
508 //==========================================================================
512 // if (f->state == WD_FDC_STATE_SEEK || f->state == WD_FDC_STATE_SEEK_DELAY)
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
) {
522 if ((b
&0x60) != 0x00) goto type_i_verify
; /* STEP/STEP-IN/STEP-OUT */
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 */
539 f
->track_register
= 0xff;
540 f
->data_register
= 0;
544 if (f
->track_register
!= f
->data_register
) {
545 f
->direction
= (f
->track_register
< f
->data_register
? FDD_STEP_IN
: FDD_STEP_OUT
);
548 f
->track_register
+= (f
->direction
== FDD_STEP_IN
? 1 : -1);
551 if (d
->tr00
&& f
->direction
== FDD_STEP_OUT
) {
552 f
->track_register
= 0;
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,
566 if (f
->type
== WD1773
|| f
->type
== FD1793
|| f
->type
== WD2797
) {
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,
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,
590 wd_fdc_seek_verify(f
);
595 f
->status_register
|= WD_FDC_SR_LOST
;
597 f
->status_register
&= ~WD_FDC_SR_LOST
;
600 f
->state
= WD_FDC_STATE_NONE
;
601 f
->status_register
&= ~WD_FDC_SR_BUSY
;
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
;
619 emuEvRemoveAllWithType(fdc_event
);
621 if (f
->id_mark
== WD_FDC_AM_NONE
) {
624 i
= (d
->disk
.i
>= d
->disk
.bpt
? 0 : d
->disk
.i
); /* start position */
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
;
632 f
->id_mark
= WD_FDC_AM_NONE
;
634 i
= (d
->disk
.bpt
? (d
->disk
.i
-i
)*200/d
->disk
.bpt
: 200);
636 emuEvAdd(/*tstates +*/ i
* /* i * 1/20 revolution */
637 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/1000,
640 } else if (f
->id_mark
!= WD_FDC_AM_NONE
) {
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
;
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
) {
661 f
->status_register
|= WD_FDC_SR_RNF
;
662 f
->status_register
&= ~WD_FDC_SR_BUSY
;
663 f
->state
= WD_FDC_STATE_NONE
;
668 if (f
->ddam
) f
->status_register
|= WD_FDC_SR_SPINUP
; /* set deleted data mark */
670 wd_fdc_set_datarq(f
);
673 for (i
= 11; i
> 0; --i
) fdd_read_data(d
); /* "delay" 11 GAP byte */
674 wd_fdc_set_datarq(f
);
677 for (i
= 11; i
> 0; --i
) fdd_read_data(d
); /* "delay" another 11 GAP byte */
680 for (i
= (f
->dden
? 12 : 6); i
> 0; --i
) fdd_write_data(d
); /* write 6/12 zero */
685 for (i
= 3; i
> 0; --i
) {
686 /* write 3 0xa1 with clock mark */
691 d
->data
= (f
->ddam
? 0x00f8 : 0x00fb)|(f
->dden
? 0x0000 : 0xff00); /* write data mark */
696 emuEvRemoveAllWithType(timeout_event
);
697 emuEvAdd(/*tstates +*/ /* 5 revolutions: 5 * 200 / 1000 */
698 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
,
703 //==========================================================================
707 // if (f->state == WD_FDC_STATE_READ || f->state == WD_FDC_STATE_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
;
725 emuEvAdd(/*tstates +*/ 5*
726 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/1000,
732 if (f
->state
== WD_FDC_STATE_WRITE
) {
734 f
->status_register
|= WD_FDC_SR_WRPROT
;
735 f
->status_register
&= ~WD_FDC_SR_BUSY
;
736 f
->state
= WD_FDC_STATE_NONE
;
740 f
->status_register
&= ~WD_FDC_SR_WRPROT
;
743 f
->data_multisector
= (b
&0x10 ? 1 : 0);
745 f
->id_mark
= WD_FDC_AM_NONE
;
746 wd_fdc_type_ii_seek(f
);
750 //==========================================================================
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)
760 //==========================================================================
761 static void wd_fdc_type_iii (wd_fdc
*f
) {
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
;
775 emuEvAdd(/*tstates +*/ 5*
776 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/1000,
782 if (f
->state
== WD_FDC_STATE_WRITETRACK
) {
783 /* ----WRITE TRACK---- */
785 f
->status_register
|= WD_FDC_SR_WRPROT
;
786 f
->status_register
&= ~WD_FDC_SR_BUSY
;
787 f
->state
= WD_FDC_STATE_NONE
;
791 f
->status_register
&= ~WD_FDC_SR_WRPROT
;
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
);
804 f
->id_mark
= WD_FDC_AM_NONE
;
806 if (f
->id_mark
== WD_FDC_AM_NONE
) {
808 i
= (d
->disk
.i
>= d
->disk
.bpt
? 0 : d
->disk
.i
); /* start position */
810 i
= (d
->disk
.bpt
? (d
->disk
.i
-i
)*200/d
->disk
.bpt
: 200);
812 emuEvAdd(/*tstates +*/ i
* /* i * 1/20 revolution */
813 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/1000,
816 } else if (f
->id_mark
!= WD_FDC_AM_NONE
) {
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
;
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,
841 //==========================================================================
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
);
866 if (event
== motor_off_event
) {
867 if (f
->type
== WD1770
|| f
->type
== WD1772
) {
868 f
->status_register
&= ~WD_FDC_SR_MOTORON
;
873 if (f
->flags
&WD_FLAG_BETA128
) fdd_motoron(d
, 0); else fdd_head_load(d
, 0);
878 if ((f
->type
== WD1773
|| f
->type
== FD1793
|| f
->type
== WD2797
) &&
879 f
->hlt_time
> 0 && f
->head_load
&& !f
->hlt
)
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
) &&
891 f
->status_register
|= WD_FDC_SR_SPINUP
;
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
) {
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
) {
905 } else if (f
->state
== WD_FDC_STATE_SEEK
|| f
->state
== WD_FDC_STATE_SEEK_DELAY
) {
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
) {
911 wd_fdc_set_datarq(f
);
912 } else if (f
->state
== WD_FDC_STATE_READ
|| f
->state
== WD_FDC_STATE_WRITE
) {
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
)
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
)
929 /* this chart looks like most close of the reality...
933 ! MO = 0 !----->----+
937 +--------------+--------+ !
939 +--------------+--------+ v
947 +--------------+--------+ v
948 ! delay 6 index pulses ! !
949 +--------------+--------+ !
955 //==========================================================================
959 //==========================================================================
960 static int wd_fdc_spinup (wd_fdc
*f
, uint8_t b
) {
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
;
970 if ((b
&0x08) == 0) delay
+= 6*200;
973 /* WD1773/FD1793/WD2797 */
974 emuEvRemoveAllWithType(motor_off_event
);
975 if (f
->state
== WD_FDC_STATE_SEEK
) {
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 */
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);
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));
999 emuEvRemoveAllWithType(fdc_event
);
1000 emuEvAdd(/*tstates +*/ delay
*
1001 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/1000,
1010 //==========================================================================
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
);
1029 wd_fdc_set_intrq(f
);
1030 } else if (b
&0x04) {
1031 d
->fdc_index
= wd_fdc_wait_index
;
1036 f
->status_register
|= WD_FDC_SR_LOST
;
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! */
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) {
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
);
1061 if (wd_fdc_spinup(f
, b
)) return;
1063 } else if ((b
&0x40) == 0) {
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
);
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
|
1087 if (f
->type
== WD2797
) fdd_set_head(d
, (b
&0x02 ? 1 : 0));
1090 if (wd_fdc_spinup(f
, b
)) return;
1092 } else if ((b
&0x30) != 0x10) {
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
);
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
);
1108 if (wd_fdc_spinup(f
, b
)) return;
1114 //==========================================================================
1118 //==========================================================================
1119 uint8_t wd_fdc_tr_read (wd_fdc
*f
) {
1120 return f
->track_register
;
1124 //==========================================================================
1128 //==========================================================================
1129 void wd_fdc_tr_write (wd_fdc
*f
, uint8_t b
) {
1130 f
->track_register
= b
;
1134 //==========================================================================
1138 //==========================================================================
1139 uint8_t wd_fdc_sec_read (wd_fdc
*f
) {
1140 return f
->sector_register
;
1144 //==========================================================================
1148 //==========================================================================
1149 void wd_fdc_sec_write (wd_fdc
*f
, uint8_t b
) {
1150 f
->sector_register
= b
;
1154 //==========================================================================
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 ) {
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
);
1178 f
->data_register
= d
->data
;
1179 if (f
->data_offset
== f
->sector_length
) {
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
;
1190 wd_fdc_reset_datarq(f
);
1191 emuEvAdd(/*tstates +*/ /* 5 revolutions: 5 * 200 / 1000 */
1192 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
,
1194 emuEvAdd(/*tstates +*/ 2* /* 20 ms delay */
1195 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/100,
1198 f
->status_register
&= ~WD_FDC_SR_BUSY
;
1199 if (f
->crc
== 0x0000) {
1200 f
->status_register
&= ~WD_FDC_SR_CRCERR
;
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
) {
1214 f
->data_register
= f
->id_track
;
1217 f
->data_register
= f
->id_head
;
1219 case 2: /* sector */
1220 f
->data_register
= f
->id_sector
;
1222 case 3: /* length */
1223 f
->data_register
= f
->id_length
;
1226 f
->data_register
= f
->crc
>> 8;
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
);
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 */
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,
1263 return f
->data_register
;
1267 //==========================================================================
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
) {
1278 ++f
->data_offset
; /* count bytes read */
1281 if (f
->data_offset
== f
->sector_length
) {
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 */
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
;
1295 wd_fdc_reset_datarq(f
);
1296 emuEvAdd(/*tstates +*/ /* 5 revolutions: 5 * 200 / 1000 */
1297 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
,
1299 emuEvAdd(/*tstates +*/ 2* /* 20ms delay */
1300 /*machine_current->timings.processor_speed*/machineInfo
.cpuSpeed
/ 100,
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
) {
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) {
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 */
1338 } else if (b
== 0xfc) {
1344 fdd_write_data(d
); /* write a byte */
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,
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 */