1 /* 765: Library to emulate the uPD765a floppy controller (aka Intel 8272)
2 
3     Copyright (C) 2000  John Elliott <jce@seasip.demon.co.uk>
4 
5     Modifications to add dirty flags
6     (c) 2005 Philip Kendall <pak21-spectrum@srcf.ucam.org>
7 
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12 
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17 
18     You should have received a copy of the GNU Library General Public
19     License along with this library; if not, write to the Free
20     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <limits.h>
28 #include "config.h"
29 #include "765.h"
30 
31 
32 
33 typedef struct floppy_drive_vtable
34 {
35 	fd_err_t  (*fdv_seek_cylinder)(FDRV_PTR fd, int cylinder);
36 	fd_err_t  (*fdv_read_id)      (FDRV_PTR fd, int head,
37                                  int sector, fdc_byte *buf);
38 	fd_err_t  (*fdv_read_sector  )(FDRV_PTR fd, int xcylinder,
39 		int xhead, int head, int sector, fdc_byte *buf, int len,
40 		int *deleted, int skip_deleted, int mfm, int multi);
41 	fd_err_t  (*fdv_read_track   )(FDRV_PTR fd, int xcylinder,
42 		int xhead, int head, fdc_byte *buf, int *len);
43 	fd_err_t  (*fdv_write_sector )(FDRV_PTR fd, int xcylinder,
44 		int xhead, int head, int sector, fdc_byte *buf, int len,
45 		int deleted, int skip_deleted, int mfm, int multi);
46 	fd_err_t (*fdv_format_track )(FDRV_PTR fd, int head,
47 		int sectors, fdc_byte *buf, fdc_byte filler);
48 	fdc_byte (*fdv_drive_status )(FDRV_PTR fd);
49 	int      (*fdv_isready)(FDRV_PTR fd);
50 	int	 (*fdv_dirty  )(FDRV_PTR fd);
51 	void     (*fdv_eject  )(FDRV_PTR fd);
52 	void	 (*fdv_set_datarate)(FDRV_PTR fd, fdc_byte rate);
53 	void     (*fdv_reset  )(FDRV_PTR fd);
54 	void     (*fdv_destroy)(FDRV_PTR fd);
55 	int	 (*fdv_changed)(FDRV_PTR fd);
56 } FLOPPY_DRIVE_VTABLE;
57 
58 
59 typedef struct floppy_drive
60 {
61 /* PRIVATE variables
62  * The following points to the drive's method table. You should not need
63  * to use this; instead, use the fd_*() wrapper functions below. */
64 	FLOPPY_DRIVE_VTABLE * fd_vtable;
65 /* PUBLIC members */
66 	/* You should set the first three of these immediately after calling
67          * fd_init() or fdd_init() on a drive.*/
68 	int fd_type;	  /* 0 for none, 1 for 3", 2 for 3.5", 3 for 5.25" */
69 	int fd_heads;	  /* No. of heads in the drive: 1 or 2 */
70 	int fd_cylinders; /* No. of cylinders the drive can access:
71 			   * eg: a nominally 40-track drive can usually go up
72                            * to 42 tracks with a bit of "persuasion" */
73 	int fd_readonly;  /* Is the drive (or the disc therein) set to R/O? */
74 	int fd_changed;	  /* Default changeline implementation. This will be
75 			   * set to 1 when drive is ejected, 0 at controller
76 			   * partial reset. If you can write a better
77 			   * implementation of the changeline, override
78 			   * fdv_changed(). */
79 
80 /* READONLY variables */
81 	int fd_motor;	  /* Is the motor for this drive running? */
82 	int fd_cylinder;  /* Current cylinder. Note that if the drive is
83 			   * double-stepping, this is the "real" cylinder -
84 			   * so it could = 24 and be reading cylinder 12
85                            * of a 40-track DSK file. */
86 } FLOPPY_DRIVE;
87 
88 /* Subclass of FLOPPY_DRIVE: a drive which emulates discs using the CPCEMU
89  * .DSK format */
90 
91 typedef struct dsk_floppy_drive
92 {
93 /* PUBLIC variables: */
94 	FLOPPY_DRIVE fdd;		/* Base class */
95 	char  fdd_filename[PATH_MAX];	/* Filename to .DSK file. Before
96 					 * changing this call fd_eject() on
97                                          * the drive */
98 /* PRIVATE variables: */
99 	FILE *fdd_fp;			/* File of the .DSK file */
100 	fdc_byte fdd_disk_header[256];	/* .DSK header */
101 	fdc_byte fdd_track_header[256];	/* .DSK track header */
102 	int fdd_dirty;			/* Has this disk been written to? */
103 } DSK_FLOPPY_DRIVE;
104 
105 #ifdef DSK_ERR_OK	/* LIBDSK headers included */
106 typedef struct libdsk_floppy_drive
107 {
108 /* PUBLIC variables: */
109 	FLOPPY_DRIVE fdl;		/* Base class */
110 	char  fdl_filename[PATH_MAX];	/* Filename to .DSK file. Before
111 					 * changing this call fd_eject() on
112                                          * the drive */
113 	const char  *fdl_type;		/* LIBDSK drive type, NULL for auto */
114 	const char  *fdl_compress;	/* LIBDSK compression, NULL for auto */
115 /* PRIVATE variables: */
116 	DSK_PDRIVER  fdl_diskp;
117 	DSK_GEOMETRY fdl_diskg;		/* Autoprobed geometry */
118 } LIBDSK_FLOPPY_DRIVE;
119 #endif	/* ifdef DSK_ERR_OK */
120 
121 typedef struct nc9_floppy_drive
122 {
123 	FLOPPY_DRIVE fdd;		/* Base class */
124 	FLOPPY_DRIVE *nc9_fdd;		/* Pointer to the 9256's B drive */
125 } NC9_FLOPPY_DRIVE;
126 
127 
128 FDRV_PTR fd_inew(size_t size);
129 
130 
131 /* This class represents the controller itself. When you instantiate one, call
132  * fdc_reset() on it before doing anything else with it. */
133 
134 typedef struct fdc_765
135 {
136     /* PRIVATE variables */
137 	int fdc_interrupting;	/* 0 => Not interrupting
138 				 * 1 => Entering result phase of
139 				 *      Read/Write/Format/Scan
140 				 * 2 => Ready for data transfer
141 				 *      (execution phase)
142 				 * 4 => End of Seek/Recalibrate command */
143 	/* The results from the SPECIFY command */
144 	int fdc_specify[2];
145 	/* The last sector for which a DD READ ID request was made */
146 	int fdc_lastidread;
147 
148 	/* Current WRITE command is for deleted data? */
149 	int fdc_write_deleted;
150 
151 	/* Command phase buffer */
152 	int fdc_cmd_id;   	/* Current command */
153 	int fdc_cmd_len;  	/* No. of bytes remaining to transfer */
154 	int fdc_cmd_pos;	/* Next buffer position to write */
155 	fdc_byte fdc_cmd_buf[20];	/* The command as a byte string */
156 
157 	/* Execution phase buffer */
158 	/* [0.4.2] If we are doing multisector reads, this needs to hold a
159 	 * whole track's worth of sectors: let's say 16k. (A normal HD disc
160 	 * holds 9k / track) */
161 	fdc_byte fdc_exec_buf[16384];
162 	int  fdc_exec_len;	/* No. of bytes remaining to transfer */
163 	int  fdc_exec_pos;	/* Position in buffer */
164 
165 	/* Results phase buffer */
166 	fdc_byte fdc_result_buf[20];
167 	int fdc_result_len;	/* No. of bytes remaining to transfer */
168 	int fdc_result_pos;	/* Position in buffer */
169 
170 	int fdc_terminal_count;	/* Set to abort a transfer */
171 	int fdc_isr_countdown;	/* Countdown to interrupt */
172 
173 	int fdc_dor;		/* Are we using that horrible kludge, the
174 				 * Digital Output Register, rather than
175 				 * proper drive select lines? */
176 	/* Drive pointers after the DOR has had its wicked way */
177 	FLOPPY_DRIVE *fdc_dor_drive[4];
178 
179 	/* READONLY variables - these can be used in status displays */
180         /* The four uPD765A status registers */
181         int fdc_st0, fdc_st1, fdc_st2, fdc_st3;
182         /* The main status register */
183         int fdc_mainstat;
184 	int fdc_curunit, fdc_curhead;   /* Currently active unit & head */
185 
186 	/* Public variables */
187 	void (*fdc_isr)(struct fdc_765 *self, int status);
188 		/* EXT: Called when interrupt line is raised or lowered.
189 		 *     You must provide this if the FDC is to interrupt. */
190 	FLOPPY_DRIVE *fdc_drive[4];
191 		/* The FDC's four drives. You must set these pointers */
192 
193 } FDC_765;
194 
195 
196 
197