1 /*
2 * Z80SIM - a Z80-CPU simulator
3 *
4 * Common I/O devices used by various simulated machines
5 *
6 * Copyright (C) 2014-2019 by Udo Munk
7 *
8 * Emulation of a Cromemco 4FDC/16FDC S100 board
9 *
10 * History:
11 * 20-DEC-2014 first version
12 * 28-DEC-2014 second version with 16FDC, CP/M 2.2 boots
13 * 01-JAN-2015 fixed 16FDC, machine now also boots CDOS 2.58 from 8" and 5.25"
14 * 01-JAN-2015 fixed frontpanel switch settings, added boot flag to fp switch
15 * 12-JAN-2015 implemented dummy write track, so that programs won't hang
16 * 22-JAN-2015 fixed buggy ID field, fake index pulses for 300/360 RPM
17 * 26-JAN-2015 implemented write track to format SS/SD disks properly
18 * 02-FEB-2015 implemented DS/DD disk formats
19 * 05-FEB-2015 implemented DS/SD disk formats
20 * 06-FEB-2015 implemented write track for all formats
21 * 12-FEB-2015 implemented motor control, so that a 16FDC is recogniced by CDOS
22 * 20-FEB-2015 bug fixes for 1.25 release
23 * 08-MAR-2016 support user path for disk images
24 * 13-MAY-2016 find disk images at -d <path>, ./disks and DISKDIR
25 * 22-JUL-2016 added support for read only disks
26 * 22-JUN-2017 added reset function
27 * 26-JUL-2017 fixed buggy index pulse implementation
28 * 15-AUG-2017 and more fixes for index pulse
29 * 22-AUG-2017 provide write protect and track 0 bits for all commands
30 * 23-APR-2018 cleanup
31 * 20-MAY-2018 improved reset
32 * 15-JUL-2018 use logging
33 * 09-SEP-2019 added disk format without SD track 0 provided by Alan Cox
34 * 24-SEP-2019 restore and seek also affect step direction
35 */
36
37 #include <unistd.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include "sim.h"
44 #include "simglb.h"
45 #include "log.h"
46 #include "cromemco-fdc.h"
47
48 /* internal state of the fdc */
49 #define FDC_IDLE 0 /* idle state */
50 #define FDC_READ 1 /* reading sector */
51 #define FDC_WRITE 2 /* writing sector */
52 #define FDC_READADR 3 /* read address */
53 #define FDC_WRTTRK 4 /* write (format) track */
54
55 #define SEC_SZSD 128 /* sector size SD */
56 #define SEC_SZDD 512 /* sector size DD */
57 #define TRK8 77 /* # of tracks 8" */
58 #define SPT8SD 26 /* # of sectors per track 8" SD */
59 #define SPT8DD 16 /* # of sectors per track 8" DD */
60 #define TRK5 40 /* # of tracks 5.25" */
61 #define SPT5SD 18 /* # of sectors per track 5.25" SD */
62 #define SPT5DD 10 /* # of sectors per track 5.25" DD */
63
64 static const char *TAG = "16FDC";
65
66 /* BYTE fdc_flags = 16|64;*//* FDC flag register, no autoboot */
67 BYTE fdc_flags = 16; /* FDC flag register, autoboot */
68 static BYTE fdc_cmd; /* FDC command last send */
69 static BYTE fdc_stat; /* FDC status register */
70 static BYTE fdc_aux; /* FDC auxiliar status */
71 static BYTE fdc_track = 0; /* FDC track register */
72 static BYTE fdc_sec = 1; /* FDC sector register */
73 static int step_dir = -1; /* last stepping direction */
74 enum Disk_type dtype; /* disk type set */
75 static enum Disk_density ddens; /* disk density set */
76 static int disk = 0; /* current selected disk # */
77 static int side = 0; /* disk side */
78 static int secsz = SEC_SZSD; /* current used sector size */
79 static int state; /* internal fdc state */
80 static int dcnt; /* data counter read/write */
81 static int mflag; /* multiple sectors flag */
82 static char fn[MAX_LFN]; /* path/filename for disk image */
83 static int fd; /* fd for disk i/o */
84 static BYTE buf[SEC_SZDD]; /* buffer for one sector */
85 int index_pulse = 0; /* disk index pulse */
86 static int autowait; /* autowait flag */
87 int motoron; /* motor on flag */
88 int motortimer; /* motor on timer */
89 static int headloaded; /* head loaded flag */
90
91 /* these are our disk drives, 8" SS SD initially */
92 static Diskdef disks[4] = {
93 { "drivea.dsk", LARGE, SINGLE, ONE, TRK8, SPT8SD, SPT8SD, READWRITE, SINGLE },
94 { "driveb.dsk", LARGE, SINGLE, ONE, TRK8, SPT8SD, SPT8SD, READWRITE, SINGLE },
95 { "drivec.dsk", LARGE, SINGLE, ONE, TRK8, SPT8SD, SPT8SD, READWRITE, SINGLE },
96 { "drived.dsk", LARGE, SINGLE, ONE, TRK8, SPT8SD, SPT8SD, READWRITE, SINGLE }
97 };
98
99 /*
100 * find and set path for disk images
101 */
dsk_path(void)102 void dsk_path(void) {
103 struct stat sbuf;
104
105 /* if option -d is used disks are there */
106 if (diskdir != NULL) {
107 strcpy(fn, diskd);
108 } else {
109 /* if not first try ./disks */
110 if ((stat("./disks", &sbuf) == 0) && S_ISDIR(sbuf.st_mode)) {
111 strcpy(fn, "./disks");
112 /* nope, then DISKSDIR as set in Makefile */
113 } else {
114 strcpy(fn, DISKSDIR);
115 }
116 }
117 }
118
119 /*
120 * configure drive and disk geometry from image file size
121 * and set R/W or R/O mode for the disk
122 */
config_disk(int fd)123 void config_disk(int fd)
124 {
125 struct stat s;
126
127 fstat(fd, &s);
128 if (s.st_mode & S_IWUSR)
129 disks[disk].disk_m = READWRITE;
130 else
131 disks[disk].disk_m = READONLY;
132
133 switch (s.st_size) {
134 case 92160: /* 5.25" SS SD */
135 disks[disk].disk_t = SMALL;
136 disks[disk].disk_d = SINGLE;
137 disks[disk].disk_s = ONE;
138 disks[disk].tracks = TRK5;
139 disks[disk].sectors = SPT5SD;
140 disks[disk].sec0 = SPT5SD;
141 disks[disk].disk_d0 = SINGLE;
142 break;
143
144 case 184320: /* 5.25" DS SD */
145 disks[disk].disk_t = SMALL;
146 disks[disk].disk_d = SINGLE;
147 disks[disk].disk_s = TWO;
148 disks[disk].tracks = TRK5;
149 disks[disk].sectors = SPT5SD;
150 disks[disk].sec0 = SPT5SD;
151 disks[disk].disk_d0 = SINGLE;
152 break;
153
154 case 201984: /* 5.25" SS DD */
155 disks[disk].disk_t = SMALL;
156 disks[disk].disk_d = DOUBLE;
157 disks[disk].disk_s = ONE;
158 disks[disk].tracks = TRK5;
159 disks[disk].sectors = SPT5DD;
160 disks[disk].sec0 = SPT5SD;
161 disks[disk].disk_d0 = SINGLE;
162 break;
163
164 case 406784: /* 5.25" DS DD */
165 disks[disk].disk_t = SMALL;
166 disks[disk].disk_d = DOUBLE;
167 disks[disk].disk_s = TWO;
168 disks[disk].tracks = TRK5;
169 disks[disk].sectors = SPT5DD;
170 disks[disk].sec0 = SPT5SD;
171 disks[disk].disk_d0 = SINGLE;
172 break;
173
174 case 256256: /* 8" SS SD */
175 disks[disk].disk_t = LARGE;
176 disks[disk].disk_d = SINGLE;
177 disks[disk].disk_s = ONE;
178 disks[disk].tracks = TRK8;
179 disks[disk].sectors = SPT8SD;
180 disks[disk].sec0 = SPT8SD;
181 disks[disk].disk_d0 = SINGLE;
182 break;
183
184 case 512512: /* 8" DS SD */
185 disks[disk].disk_t = LARGE;
186 disks[disk].disk_d = SINGLE;
187 disks[disk].disk_s = TWO;
188 disks[disk].tracks = TRK8;
189 disks[disk].sectors = SPT8SD;
190 disks[disk].sec0 = SPT8SD;
191 disks[disk].disk_d0 = SINGLE;
192 break;
193
194 case 625920: /* 8" SS DD */
195 disks[disk].disk_t = LARGE;
196 disks[disk].disk_d = DOUBLE;
197 disks[disk].disk_s = ONE;
198 disks[disk].tracks = TRK8;
199 disks[disk].sectors = SPT8DD;
200 disks[disk].sec0 = SPT8SD;
201 disks[disk].disk_d0 = SINGLE;
202 break;
203
204 case 1256704: /* 8" DS DD */
205 disks[disk].disk_t = LARGE;
206 disks[disk].disk_d = DOUBLE;
207 disks[disk].disk_s = TWO;
208 disks[disk].tracks = TRK8;
209 disks[disk].sectors = SPT8DD;
210 disks[disk].sec0 = SPT8SD;
211 disks[disk].disk_d0 = SINGLE;
212 break;
213
214 case 1261568: /* 8" DS DD no SD track */
215 disks[disk].disk_t = LARGE;
216 disks[disk].disk_d = DOUBLE;
217 disks[disk].disk_s = TWO;
218 disks[disk].tracks = TRK8;
219 disks[disk].sectors = SPT8DD;
220 disks[disk].sec0 = SPT8DD;
221 disks[disk].disk_d0 = DOUBLE;
222 break;
223
224 default:
225 LOGW(TAG, "disk image %s has unknown format", disks[disk].fn);
226 disks[disk].disk_t = UNKNOWN;
227 break;
228 }
229 }
230
231 /*
232 * calculate disk image seek position for track/sector/side
233 */
get_pos(void)234 long get_pos(void)
235 {
236 long pos = -1L;
237
238 /* single sided */
239 if (disks[disk].disk_s == ONE) {
240
241 /* single density */
242 if (disks[disk].disk_d == SINGLE) {
243 pos = (fdc_track * disks[disk].sectors + fdc_sec - 1) *
244 SEC_SZSD;
245
246 /* double density */
247 } else {
248 if (disks[disk].disk_d0 == SINGLE) {
249 if (fdc_track == 0) {
250 pos = (fdc_sec - 1) * SEC_SZSD;
251 } else {
252 pos = (disks[disk].sec0 * SEC_SZSD) +
253 ((fdc_track - 1) * disks[disk].sectors
254 * SEC_SZDD) +
255 ((fdc_sec - 1) * SEC_SZDD);
256 }
257 } else {
258 pos = (fdc_track * disks[disk].sectors * SEC_SZDD) +
259 (fdc_sec - 1) * SEC_SZDD;
260 }
261 }
262
263 /* double sided */
264 } else {
265
266 /* single density */
267 if (disks[disk].disk_d == SINGLE) {
268 pos = fdc_track * 2 * disks[disk].sectors * SEC_SZSD;
269 if (side == 0) {
270 pos += (fdc_sec - 1) * SEC_SZSD;
271 } else {
272 pos += disks[disk].sectors * SEC_SZSD;
273 pos += (fdc_sec - 1) * SEC_SZSD;
274 }
275
276 /* double density */
277 } else {
278 if (disks[disk].disk_d0 == SINGLE) {
279 if ((fdc_track == 0) && (side == 0)) {
280 pos = (fdc_sec - 1) * SEC_SZSD;
281 goto done;
282 }
283 if ((fdc_track == 0) && (side == 1)) {
284 pos = disks[disk].sec0 * SEC_SZSD +
285 (fdc_sec - 1) * SEC_SZDD;
286 goto done;
287 }
288 pos = disks[disk].sec0 * SEC_SZSD +
289 disks[disk].sectors * SEC_SZDD;
290 pos += (fdc_track - 1) * 2 * disks[disk].sectors
291 * SEC_SZDD;
292 } else {
293 pos = fdc_track * 2 * disks[disk].sectors * SEC_SZDD;
294 }
295 pos = disks[disk].sec0 * SEC_SZSD + disks[disk].sectors
296 * SEC_SZDD;
297 pos += (fdc_track - 1) * 2 * disks[disk].sectors * SEC_SZDD;
298 if (side == 1)
299 pos += disks[disk].sectors * SEC_SZDD;
300 pos += (fdc_sec - 1) * SEC_SZDD;
301 }
302 }
303
304 done:
305 return(pos);
306 }
307
308 /*
309 * 4FDC 16FDC
310 * D7 DRQ DRQ
311 * D6 !BOOT !BOOT
312 * D5 HEADLOAD HEADLOAD
313 * D4 unassigned !INHIBIT INIT
314 * D3 unassigned MOTOR ON
315 * D2 unassigned MOTOR TIMEOUT
316 * D1 unassigned AUTOWAIT TIMEOUT
317 * D0 EOJ EOJ
318 */
cromemco_fdc_diskflags_in(void)319 BYTE cromemco_fdc_diskflags_in(void)
320 {
321 BYTE ret;
322
323 /* reset EOJ after it was read */
324 ret = fdc_flags;
325 fdc_flags &= ~1;
326
327 /* process autowait timeout */
328 if (!(ret & 1) && !(ret & 128) && (autowait == 1)) {
329 ret |= 2;
330 }
331
332 /* motor status & timer */
333 if (motoron) {
334 if (motortimer > 0)
335 ret |= 8;
336 else {
337 motoron = 0;
338 ret |= 4;
339 }
340 }
341
342 /* head loaded */
343 if (headloaded)
344 ret |= 32;
345
346 return(ret);
347 }
348
349 /*
350 * 4FDC 16FDC
351 * D7 AUTO WAIT AUTO WAIT
352 * D6 unassigned DOUBLE DENSITY
353 * D5 MOTOR ON MOTOR ON
354 * D4 MAXI MAXI
355 * D3 DS4 DS4
356 * D2 DS3 DS3
357 * D1 DS2 DS2
358 * D0 DS1 DS1
359 */
cromemco_fdc_diskctl_out(BYTE data)360 void cromemco_fdc_diskctl_out(BYTE data)
361 {
362 /* get autowait */
363 autowait = (data & 128) ? 1 : 0;
364
365 /* get selected disk */
366 if (data & 8)
367 disk = 3;
368 else if (data & 4)
369 disk = 2;
370 else if (data & 2)
371 disk = 1;
372 else if (data & 1)
373 disk = 0;
374 else
375 disk = 0;
376
377 /* get disk density */
378 if (data & 64) {
379 ddens = DOUBLE;
380 secsz = SEC_SZDD;
381 } else {
382 ddens = SINGLE;
383 secsz = SEC_SZSD;
384 }
385
386 /* get disk format */
387 if (data & 16) { /* MAXI means 8" */
388 dtype = LARGE;
389 } else { /* else 5.25" */
390 dtype = SMALL;
391 }
392
393 /* motor on/off */
394 if (data & 32) {
395 motoron = 1;
396 motortimer = 800;
397 } else {
398 motoron = 0;
399 motortimer = 0;
400 }
401 }
402
403 /*
404 * read data from FDC
405 */
cromemco_fdc_data_in(void)406 BYTE cromemco_fdc_data_in(void)
407 {
408 long pos; /* seek position */
409 int lastsec; /* last sector of a track */
410
411 switch (state) {
412 case FDC_READ: /* read data from disk sector */
413 /* first byte? */
414 if (dcnt == 0) {
415 motortimer = 800;
416 /* try to open disk image */
417 dsk_path();
418 strcat(fn, "/");
419 strcat(fn, disks[disk].fn);
420 if ((fd = open(fn, O_RDONLY)) == -1) {
421 state = FDC_IDLE; /* abort command */
422 fdc_flags |= 1; /* set EOJ */
423 fdc_flags &= ~128; /* reset DRQ */
424 fdc_stat = 0x80; /* not ready */
425 return((BYTE) 0);
426 }
427 /* get drive and disk geometry */
428 config_disk(fd);
429 if (disks[disk].disk_t == UNKNOWN) {
430 state = FDC_IDLE; /* abort command */
431 fdc_flags |= 1; /* set EOJ */
432 fdc_flags &= ~128; /* reset DRQ */
433 fdc_stat = 0x10; /* sector not found */
434 close(fd);
435 return((BYTE) 0);
436 }
437 /* check track/sector */
438 if ((fdc_track == 0) && (side == 0))
439 lastsec = disks[disk].sec0;
440 else
441 lastsec = disks[disk].sectors;
442 if ((fdc_track >= disks[disk].tracks) ||
443 (fdc_sec == 0) ||
444 (fdc_sec > lastsec)) {
445 state = FDC_IDLE; /* abort command */
446 fdc_flags |= 1; /* set EOJ */
447 fdc_flags &= ~128; /* reset DRQ */
448 fdc_stat = 0x10; /* sector not found */
449 close(fd);
450 return((BYTE) 0);
451 }
452 /* seek to sector */
453 pos = get_pos();
454 if (lseek(fd, pos, SEEK_SET) == -1L) {
455 state = FDC_IDLE; /* abort command */
456 fdc_flags |= 1; /* set EOJ */
457 fdc_flags &= ~128; /* reset DRQ */
458 fdc_stat = 0x10; /* sector not found */
459 close(fd);
460 return((BYTE) 0);
461 }
462 /* read the sector */
463 if (read(fd, &buf[0], secsz) != secsz) {
464 state = FDC_IDLE; /* abort command */
465 fdc_flags |= 1; /* set EOJ */
466 fdc_flags &= ~128; /* reset DRQ */
467 fdc_stat = 0x10; /* sector not found */
468 close(fd);
469 return((BYTE) 0);
470 }
471 close(fd);
472 }
473 /* last byte? */
474 if (dcnt == secsz - 1) {
475 if (!mflag) { /* single sector */
476 state = FDC_IDLE; /* done */
477 fdc_flags |= 1; /* set EOJ */
478 fdc_flags &= ~128; /* reset DRQ */
479 fdc_stat = 0;
480 } else { /* multiple sectors */
481 if ((fdc_track == 0) && (side == 0))
482 lastsec = disks[disk].sec0;
483 else
484 lastsec = disks[disk].sectors;
485 if (lastsec == fdc_sec) {
486 state = FDC_IDLE; /* done */
487 fdc_flags |= 1; /* set EOJ */
488 fdc_flags &= ~128; /* reset DRQ */
489 fdc_stat = 0x10; /* sector not found*/
490 } else {
491 dcnt = 0; /* read next sector */
492 fdc_sec++;
493 return(buf[secsz - 1]);
494 }
495 }
496 }
497 /* return next byte from buffer and increment counter */
498 return(buf[dcnt++]);
499 break;
500
501 case FDC_READADR: /* read disk address */
502 /* first byte? */
503 if (dcnt == 0) { /* build address field */
504 buf[0] = fdc_track;
505 buf[1] = side;
506 buf[2] = fdc_sec;
507 buf[3] = (ddens == SINGLE) ? 0 : 2;
508 buf[4] = 0;
509 buf[5] = 0;
510 }
511 /* last byte? */
512 if (dcnt == 5) {
513 state = FDC_IDLE; /* done */
514 fdc_flags |= 1; /* set EOJ */
515 fdc_flags &= ~128; /* reset DRQ */
516 fdc_stat = 0;
517 }
518 /* return next byte from buffer and increment counter */
519 return(buf[dcnt++]);
520 break;
521
522 default:
523 return((BYTE) 0);
524 break;
525 }
526 }
527
528 /*
529 * write data to FDC
530 */
cromemco_fdc_data_out(BYTE data)531 void cromemco_fdc_data_out(BYTE data)
532 {
533 long pos; /* seek position */
534 int lastsec; /* last sector of a track */
535 static int wrtstat; /* state while writing (formatting) tracks */
536 static int bcnt; /* byte counter for sector data */
537 static int secs; /* # of sectors written so far */
538
539 switch (state) {
540 case FDC_WRITE: /* write data to disk sector */
541 /* first byte? */
542 if (dcnt == 0) {
543 motortimer = 800;
544 /* try to open disk image */
545 dsk_path();
546 strcat(fn, "/");
547 strcat(fn, disks[disk].fn);
548 if ((fd = open(fn, O_RDWR)) == -1) {
549 if ((fd = open(fn, O_RDONLY)) != -1) {
550 close(fd);
551 fdc_stat = 0x40; /* read only */
552 } else {
553 fdc_stat = 0x80; /* not ready */
554 }
555 state = FDC_IDLE; /* abort command */
556 fdc_flags |= 1; /* set EOJ */
557 fdc_flags &= ~128; /* reset DRQ */
558 return;
559 }
560 /* get drive and disk geometry */
561 config_disk(fd);
562 if (disks[disk].disk_t == UNKNOWN) {
563 state = FDC_IDLE; /* abort command */
564 fdc_flags |= 1; /* set EOJ */
565 fdc_flags &= ~128; /* reset DRQ */
566 fdc_stat = 0x10; /* sector not found */
567 close(fd);
568 return;
569 }
570 /* check track/sector */
571 if ((fdc_track == 0) && (side == 0))
572 lastsec = disks[disk].sec0;
573 else
574 lastsec = disks[disk].sectors;
575 if ((fdc_track >= disks[disk].tracks) ||
576 (fdc_sec == 0) ||
577 (fdc_sec > lastsec)) {
578 state = FDC_IDLE; /* abort command */
579 fdc_flags |= 1; /* set EOJ */
580 fdc_flags &= ~128; /* reset DRQ */
581 fdc_stat = 0x10; /* sector not found */
582 close(fd);
583 return;
584 }
585 /* seek to sector */
586 pos = get_pos();
587 if (lseek(fd, pos, SEEK_SET) == -1L) {
588 state = FDC_IDLE; /* abort command */
589 fdc_flags |= 1; /* set EOJ */
590 fdc_flags &= ~128; /* reset DRQ */
591 fdc_stat = 0x10; /* sector not found */
592 close(fd);
593 return;
594 }
595 }
596 /* write data bytes into the sector buffer */
597 buf[dcnt++] = data;
598 /* last byte? */
599 if (dcnt == secsz) {
600 state = FDC_IDLE; /* done */
601 fdc_flags |= 1; /* set EOJ */
602 fdc_flags &= ~128; /* reset DRQ */
603 if (write(fd, &buf[0], secsz) == secsz)
604 fdc_stat = 0;
605 else
606 fdc_stat = 0x20; /* write fault */
607 close(fd);
608 }
609 break;
610
611 case FDC_WRTTRK: /* write (format) track */
612 if (dcnt == 0) {
613 motortimer = 800;
614 /* unlink disk image */
615 dsk_path();
616 strcat(fn, "/");
617 strcat(fn, disks[disk].fn);
618 if ((fdc_track == 0) && (side == 0))
619 unlink(fn);
620 /* try to create new disk image */
621 if ((fd = open(fn, O_RDWR|O_CREAT, 0644)) == -1) {
622 state = FDC_IDLE; /* abort command */
623 fdc_flags |= 1; /* set EOJ */
624 fdc_flags &= ~128; /* reset DRQ */
625 fdc_stat = 0x80; /* not ready */
626 return;
627 }
628 /* set initial drive and disk geometry */
629 if ((fdc_track == 0) && (side == 0)) {
630 if (dtype == LARGE) {
631 disks[disk].disk_t = LARGE;
632 disks[disk].disk_d = SINGLE;
633 disks[disk].disk_s = ONE;
634 disks[disk].tracks = TRK8;
635 disks[disk].sectors = SPT8SD;
636 disks[disk].sec0 = SPT8SD;
637 } else {
638 disks[disk].disk_t = SMALL;
639 disks[disk].disk_d = SINGLE;
640 disks[disk].disk_s = ONE;
641 disks[disk].tracks = TRK5;
642 disks[disk].sectors = SPT5SD;
643 disks[disk].sec0 = SPT5SD;
644 }
645 }
646 /* don't format tracks out of bounds */
647 if (fdc_track >= disks[disk].tracks) {
648 state = FDC_IDLE; /* abort command */
649 fdc_flags |= 1; /* set EOJ */
650 fdc_flags &= ~128; /* reset DRQ */
651 fdc_stat = 0;
652 close(fd);
653 return;
654 }
655 /* now learn more */
656 if (side == 1)
657 disks[disk].disk_s = TWO;
658 if (ddens == DOUBLE) {
659 disks[disk].disk_d = DOUBLE;
660 if (dtype == LARGE) {
661 disks[disk].sectors = SPT8DD;
662 } else {
663 disks[disk].sectors = SPT5DD;
664 }
665 }
666 /* seek to track */
667 fdc_sec = 1;
668 pos = get_pos();
669 if (lseek(fd, pos, SEEK_SET) == -1L) {
670 state = FDC_IDLE; /* abort command */
671 fdc_flags |= 1; /* set EOJ */
672 fdc_flags &= ~128; /* reset DRQ */
673 fdc_stat = 0;
674 close(fd);
675 return;
676 }
677 /* now wait for sector data */
678 wrtstat = 1;
679 secs = 0;
680 }
681 dcnt++;
682 /* wait for sector data address mark */
683 if (wrtstat == 1) {
684 if (data == 0xfb) {
685 wrtstat = 2;
686 bcnt = 0;
687 }
688 return;
689 }
690 /* collect bytes in buffer and write if sector complete */
691 if (wrtstat == 2) {
692 if ((data != 0xf7) && (bcnt < secsz)) {
693 buf[bcnt++] = data;
694 return;
695 } else {
696 secs++;
697 if (write(fd, buf, bcnt) == bcnt)
698 fdc_stat = 0;
699 else
700 fdc_stat = 0x20; /* write fault */
701 wrtstat = 1;
702 }
703 }
704 /* all sectors of track written? */
705 if ((fdc_track == 0) && (side == 0))
706 lastsec = disks[disk].sec0;
707 else
708 lastsec = disks[disk].sectors;
709 if (secs == lastsec) {
710 state = FDC_IDLE;
711 fdc_flags |= 1; /* set EOJ */
712 fdc_flags &= ~128; /* reset DRQ */
713 close(fd);
714 }
715 break;
716
717 default: /* track # for seek */
718 if (fdc_track != data)
719 step_dir = (data < fdc_track) ? -1 : 1;
720 fdc_track = data;
721 break;
722 }
723 }
724
725 /*
726 * read FDC sector register
727 */
cromemco_fdc_sector_in(void)728 BYTE cromemco_fdc_sector_in(void)
729 {
730 return(fdc_sec);
731 }
732
733 /*
734 * write FDC sector register
735 */
cromemco_fdc_sector_out(BYTE data)736 void cromemco_fdc_sector_out(BYTE data)
737 {
738 fdc_sec = data;
739 }
740
741 /*
742 * read FDC track register
743 */
cromemco_fdc_track_in(void)744 BYTE cromemco_fdc_track_in(void)
745 {
746 return(fdc_track);
747 }
748
749 /*
750 * write FDC track register
751 *
752 * Note: this does not set the FDC track register for a seek, the wanted
753 * track # for a seek is written to the data port, see above.
754 */
cromemco_fdc_track_out(BYTE data)755 void cromemco_fdc_track_out(BYTE data)
756 {
757 data++; /* to avoid compiler warning */
758 }
759
760 /*
761 * 4FDC 16FDC
762 * D7 DRQ DRQ
763 * D6 SEEK IN PROGRESS SEEK IN PROGRESS
764 * D5 unassigned unassigned
765 * D4 unassigned unassigned
766 * D3 unassigned !FP Sense Switch 5
767 * D2 unassigned !FP Sense Switch 6
768 * D1 unassigned !FP Sense Switch 7
769 * D0 unassigned !FP Sense Switch 8
770 *
771 * SEEK IN PROGESS is never set, we have no moving parts here.
772 */
cromemco_fdc_aux_in(void)773 BYTE cromemco_fdc_aux_in(void)
774 {
775 fdc_aux = 0;
776
777 /* get DRQ from flag register */
778 if (fdc_flags & 128)
779 fdc_aux |= 128;
780 else
781 fdc_aux &= ~128;
782
783 /* get front panel switch bits */
784 if ((address_switch >> 8) & 16)
785 fdc_aux &= ~8;
786 else
787 fdc_aux |= 8;
788
789 if ((address_switch >> 8) & 32)
790 fdc_aux &= ~4;
791 else
792 fdc_aux |= 4;
793
794 if ((address_switch >> 8) & 64)
795 fdc_aux &= ~2;
796 else
797 fdc_aux |= 2;
798
799 if ((address_switch >> 8) & 128)
800 fdc_aux &= ~1;
801 else
802 fdc_aux |= 1;
803
804 return(fdc_aux);
805 }
806
807 /*
808 * 4FDC 16FDC
809 * D7 unassigned unassigned
810 * D6 !EJECT LEFT !EJECT
811 * D5 !EJECT RIGHT !DRIVE SELECT OVERRIDE
812 * D4 !FAST SEEK !FAST SEEK
813 * D3 !RESTORE !RESTORE
814 * D2 !CONTROL OUT !CONTROL OUT
815 * D1 unassigend !SIDE SELECT
816 * D0 unassigend unassigned
817 */
cromemco_fdc_aux_out(BYTE data)818 void cromemco_fdc_aux_out(BYTE data)
819 {
820 /* fast seek for the PERSCI disk drives */
821 if (!(data & 8))
822 fdc_track = 0;
823
824 /* get disk side */
825 if (!(data & 2))
826 side = 1;
827 else
828 side = 0;
829 }
830
831 /*
832 * read disk status
833 *
834 * Depends on the last command, WD status table:
835 *
836 * Command D7 D6 D5 D4 D3 D2 D1 D0
837 * SEEK not write head not CRC track index busy
838 * ready protect down found error 0
839 *
840 * STEP not write head not CRC track index busy
841 * RESTORE ready protect down found error 0
842 *
843 * READ not 0 record not CRC lost DRQ busy
844 * RECORD(S) ready type found error data
845 *
846 * WRITE not write 0 not CRC lost DRQ busy
847 * RECORD(S) read protect found error data
848 *
849 * READ not 0 0 not CRC lost DRQ busy
850 * ADDRESS ready found error data
851 *
852 * READ not 0 0 0 0 lost DRQ busy
853 * TRACK ready data
854 *
855 * WRITE not write 0 0 0 lost DRQ busy
856 * TRACK ready protect data
857 */
cromemco_fdc_status_in(void)858 BYTE cromemco_fdc_status_in(void)
859 {
860 /* set/reset the index pulse, write protect and track 0 bits
861 depending on last command */
862 if (((fdc_cmd & 0xf0) == 0) || /* restore (seek track 0) */
863 ((fdc_cmd & 0xf0) == 0x10) || /* seek */
864 ((fdc_cmd & 0xe0) == 0x20) || /* step */
865 ((fdc_cmd & 0xe0) == 0x40) || /* step in */
866 ((fdc_cmd & 0xe0) == 0x60)) { /* step out */
867
868 if (index_pulse)
869 fdc_stat |= 2;
870 else
871 fdc_stat &= ~2;
872
873 if (disks[disk].disk_m == READWRITE)
874 fdc_stat &= ~64;
875 else
876 fdc_stat |= 64;
877
878 if (fdc_track == 0)
879 fdc_stat |= 4;
880 else
881 fdc_stat &= ~4;
882 }
883
884 return(fdc_stat);
885 }
886
887 /*
888 * write disk command
889 */
cromemco_fdc_cmd_out(BYTE data)890 void cromemco_fdc_cmd_out(BYTE data)
891 {
892 /* if controller busy ignore all commands but interrupt */
893 if ((fdc_stat & 1) && ((data & 0xf0) != 0xd0))
894 return;
895
896 /* new command, save it and reset EOJ */
897 fdc_cmd = data;
898 fdc_flags &= ~1;
899
900 if ((data & 0xf0) == 0) { /* restore (seek track 0) */
901 headloaded = (data & 8) ? 1 : 0;
902 if (fdc_track != 0)
903 step_dir = -1;
904 fdc_track = 0;
905 fdc_flags |= 1; /* set EOJ */
906 fdc_stat = 4; /* positioned to track 0 */
907 if (headloaded)
908 fdc_stat |= 32;
909
910 } else if ((data & 0xf0) == 0x10) { /* seek */
911 headloaded = (data & 8) ? 1 : 0;
912 fdc_flags |= 1; /* set EOJ */
913 if (fdc_track <= disks[disk].tracks)
914 fdc_stat = (fdc_track == 0) ? 4 : 0;
915 else
916 fdc_stat = 16; /* seek error */
917 if (headloaded)
918 fdc_stat |= 32;
919
920 } else if ((data & 0xe0) == 0x20) { /* step */
921 headloaded = (data & 8) ? 1 : 0;
922 fdc_track += step_dir;
923 fdc_flags |= 1; /* set EOJ */
924 if (fdc_track <= disks[disk].tracks)
925 fdc_stat = (fdc_track == 0) ? 4 : 0;
926 else
927 fdc_stat = 16; /* seek error */
928 if (headloaded)
929 fdc_stat |= 32;
930
931 } else if ((data & 0xe0) == 0x40) { /* step in */
932 headloaded = (data & 8) ? 1 : 0;
933 fdc_track++;
934 fdc_flags |= 1; /* set EOJ */
935 step_dir = 1;
936 if (fdc_track <= disks[disk].tracks)
937 fdc_stat = (fdc_track == 0) ? 4 : 0;
938 else
939 fdc_stat = 16; /* seek error */
940 if (headloaded)
941 fdc_stat |= 32;
942
943 } else if ((data & 0xe0) == 0x60) { /* step out */
944 headloaded = (data & 8) ? 1 : 0;
945 fdc_track--;
946 fdc_flags |= 1; /* set EOJ */
947 step_dir = -1;
948 if (fdc_track <= disks[disk].tracks)
949 fdc_stat = (fdc_track == 0) ? 4 : 0;
950 else
951 fdc_stat = 16; /* seek error */
952 if (headloaded)
953 fdc_stat |= 32;
954
955 } else if ((data & 0xf0) == 0xd0) { /* force interrupt */
956 state = FDC_IDLE; /* abort any command */
957 fdc_stat = 0;
958 fdc_flags &= ~128; /* clear DRQ */
959 if (data & 0x0f)
960 fdc_flags |= 1; /* set EOJ */
961
962 } else if ((data & 0xe0) == 0x80) { /* read sector(s) */
963 mflag = (data & 16) ? 1 : 0;
964 state = FDC_READ;
965 dcnt = 0;
966 fdc_stat = 3; /* set DRQ & busy */
967 fdc_flags |= 128; /* set DRQ */
968
969 } else if ((data & 0xf0) == 0xa0) { /* write single sector */
970 state = FDC_WRITE;
971 dcnt = 0;
972 fdc_stat = 3; /* set DRQ & busy */
973 fdc_flags |= 128; /* set DRQ */
974
975 } else if (data == 0xc4) { /* read address */
976 state = FDC_READADR;
977 dcnt = 0;
978 fdc_stat = 3; /* set DRQ & busy */
979 /*fdc_flags |= 128;*/ /* set DRQ */
980 fdc_flags |= 129; /* RDOS 2&3 seem to need EOJ already, why */
981
982 } else if ((data & 0xf0) == 0xe0) { /* read track */
983 LOGW(TAG, "read track not implemented");
984 fdc_stat = 16; /* not found */
985 fdc_flags |= 1; /* set EOJ */
986
987 } else if ((data &0xf0) == 0xf0) { /* write track */
988 state = FDC_WRTTRK;
989 dcnt = 0;
990 fdc_stat = 3; /* set DRQ & busy */
991 fdc_flags |= 128; /* set DRQ */
992
993 } else {
994 LOGW(TAG, "unknown command %02x", data);
995 fdc_stat = 16|8; /* not found & CRC error */
996 fdc_flags |= 1; /* set EOJ */
997 }
998 }
999
1000 /*
1001 * Reset FDC
1002 */
cromemco_fdc_reset(void)1003 void cromemco_fdc_reset(void)
1004 {
1005 state = dcnt = mflag = index_pulse = motortimer = headloaded = 0;
1006 }
1007