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