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 #include "765i.h"
24 
25 /*
26  * Default implementations of the FDC functions. If the pointer to the drive
27  * or the function pointer is null, it returns results as if the drive
28  * were not present.
29  */
30 
31 /* Seek to a cylinder */
fd_seek_cylinder(FDRV_PTR fd,int cylinder)32 fd_err_t fd_seek_cylinder(FDRV_PTR fd, int cylinder)
33 {
34 	if (fd && (fd->fd_vtable->fdv_seek_cylinder))
35 	{
36 		return (*fd->fd_vtable->fdv_seek_cylinder)(fd, cylinder);
37 	}
38 	return FD_E_NOTRDY;
39 }
40 
41 
42 /* Read the ID of the next sector to pass under the head. "sector" is
43  * suggested since most emulated drives don't actually emulate the idea
44  * of a head being over one sector at a time */
fd_read_id(FDRV_PTR fd,int head,int sector,fdc_byte * buf)45 fd_err_t  fd_read_id(FDRV_PTR fd, int head, int sector, fdc_byte *buf)
46 {
47 	if (fd && (fd->fd_vtable->fdv_read_id))
48 	{
49 		return (*fd->fd_vtable->fdv_read_id)(fd, head, sector, buf);
50 	}
51 	return FD_E_NOTRDY;
52 }
53 
54 /* Read a sector. xcylinder and xhead are the expected values for the
55  * sector header; head is the actual head to use. */
fd_read_sector(FDRV_PTR fd,int xcylinder,int xhead,int head,int sector,fdc_byte * buf,int len,int * deleted,int skip_deleted,int mfm,int multi)56 fd_err_t  fd_read_sector(FDRV_PTR fd, int xcylinder,
57 		int xhead, int head, int sector, fdc_byte *buf, int len,
58 		int *deleted, int skip_deleted, int mfm, int multi)
59 {
60 	if (fd && (fd->fd_vtable->fdv_read_sector))
61 	{
62 		return (*fd->fd_vtable->fdv_read_sector)(fd, xcylinder,
63 			xhead, head, sector, buf, len, deleted,
64 			skip_deleted, mfm, multi);
65 	}
66 	return FD_E_NOTRDY;
67 }
68 
69 /* Read a track. xcylinder and xhead are the expected values for the
70  * sector header; head is the actual head to use. */
fd_read_track(FDRV_PTR fd,int xcylinder,int xhead,int head,fdc_byte * buf,int * len)71 fd_err_t  fd_read_track(FDRV_PTR fd, int xcylinder,
72                 int xhead, int head, fdc_byte *buf, int *len)
73 {
74         if (fd && (fd->fd_vtable->fdv_read_track))
75         {
76                 return (*fd->fd_vtable->fdv_read_track)(fd, xcylinder,
77                         xhead, head, buf, len);
78         }
79         return FD_E_NOTRDY;
80 }
81 
82 
83 
84 /* Write a sector. xcylinder and xhead are the expected values for the
85  * sector header; head is the actual head to use. */
fd_write_sector(FDRV_PTR fd,int xcylinder,int xhead,int head,int sector,fdc_byte * buf,int len,int deleted,int skip_deleted,int mfm,int multi)86 fd_err_t  fd_write_sector(FDRV_PTR fd, int xcylinder,
87                 int xhead, int head, int sector, fdc_byte *buf, int len,
88 		int deleted, int skip_deleted, int mfm, int multi)
89 {
90         if (fd && (fd->fd_vtable->fdv_write_sector))
91         {
92                 return (*fd->fd_vtable->fdv_write_sector)(fd, xcylinder,
93                         xhead, head, sector, buf, len, deleted,
94 			skip_deleted, mfm, multi);
95         }
96         return FD_E_NOTRDY;
97 }
98 
99 /* Format a track */
fd_format_track(struct floppy_drive * fd,int head,int sectors,fdc_byte * track,fdc_byte filler)100 fd_err_t  fd_format_track (struct floppy_drive *fd, int head,
101                 int sectors, fdc_byte *track, fdc_byte filler)
102 {
103 	if (fd && (fd->fd_vtable->fdv_format_track))
104 	{
105 		return (*fd->fd_vtable->fdv_format_track)(fd, head, sectors, track, filler);
106 	}
107 	return FD_E_NOTRDY;
108 }
109 
110 
111 /* Get the drive status (as given in bits 7-3 of DD_DRIVE_STATUS) */
fd_drive_status(FDRV_PTR fd)112 fdc_byte fd_drive_status(FDRV_PTR fd)
113 {
114 	if (fd && (fd->fd_vtable->fdv_drive_status))
115 	{
116 		fdc_byte result;
117 	     	result = (*fd->fd_vtable->fdv_drive_status)(fd);
118 		return result;
119 	}
120 	return 0;	/* Drive not present */
121 }
122 
123 
124 /* Is the drive ready? */
fd_isready(FDRV_PTR fd)125 fdc_byte fd_isready(FDRV_PTR fd)
126 {
127 	if (fd && (fd->fd_vtable->fdv_isready)) return (*fd->fd_vtable->fdv_isready)(fd);
128 	return 0;
129 }
130 
131 
132 
133 /* Is the drive ready? */
fd_changed(FDRV_PTR fd)134 fdc_byte fd_changed(FDRV_PTR fd)
135 {
136 	if (fd && (fd->fd_vtable->fdv_changed))
137 			return (*fd->fd_vtable->fdv_changed)(fd);
138 	else if (fd)	return fd->fd_changed;
139 	return 0;
140 }
141 
fd_dirty(FDRV_PTR fd)142 int fd_dirty(FDRV_PTR fd)
143 {
144 	if (fd && (fd->fd_vtable->fdv_dirty))
145 			return (*fd->fd_vtable->fdv_dirty)(fd);
146 	else return FD_D_UNAVAILABLE;
147 }
148 
149 /* Eject under computer's control */
fd_eject(FDRV_PTR fd)150 void fd_eject(FDRV_PTR fd)
151 {
152 	if (fd && (fd->fd_vtable->fdv_eject))
153 		(*fd->fd_vtable->fdv_eject)(fd);
154 	if (fd) fd->fd_changed = 1;
155 
156 }
157 
158 /* Reset the drive */
fd_reset(FDRV_PTR fd)159 void fd_reset(FDRV_PTR fd)
160 {
161 	if (fd && (fd->fd_vtable->fdv_reset))
162 		(*fd->fd_vtable->fdv_reset)(fd);
163 }
164 
165 /* Set data rate */
fd_set_datarate(FDRV_PTR fd,fdc_byte rate)166 void fd_set_datarate(FDRV_PTR fd, fdc_byte rate)
167 {
168 	if (fd && (fd->fd_vtable->fdv_set_datarate))
169 		(*fd->fd_vtable->fdv_set_datarate)(fd, rate);
170 }
171 
172 /* On the PCW9256, drives 2 and 3 always return ready. Drive 2 at least
173  * passes all its other commands to drive 1. */
174 
n9256_seek_cylinder(FDRV_PTR fd,int cylinder)175 static fd_err_t n9256_seek_cylinder(FDRV_PTR fd, int cylinder)
176 {
177         NC9_FLOPPY_DRIVE *nc9 = (NC9_FLOPPY_DRIVE *)fd;
178         if (nc9->nc9_fdd) return fd_seek_cylinder(nc9->nc9_fdd, cylinder);
179 
180 	return FD_E_NOTRDY;
181 }
182 
183 
n9256_drive_status(FDRV_PTR fd)184 static fdc_byte n9256_drive_status(FDRV_PTR fd)
185 {
186 	fdc_byte b = 0;
187 
188 	NC9_FLOPPY_DRIVE *nc9 = (NC9_FLOPPY_DRIVE *)fd;
189 	if (nc9->nc9_fdd) b = fd_drive_status(nc9->nc9_fdd);
190 
191 	return 0x20 | b;	/* Drive is always ready */
192 }
193 
194 
195 static FLOPPY_DRIVE_VTABLE dummy_vtbl;	/* all NULLs */
196 static FLOPPY_DRIVE_VTABLE d9256_vtbl =	/* nearly all NULLs */
197 {
198 	n9256_seek_cylinder,
199 	NULL,
200 	NULL,
201 	NULL,
202 	NULL,
203 	NULL,
204         n9256_drive_status,
205 	NULL,
206 	NULL,
207 	NULL,
208 	NULL
209 };
210 
211 /* Initialise a FLOPPY_DRIVE structure */
fd_inew(size_t size)212 FDRV_PTR fd_inew(size_t size)
213 {
214 	FDRV_PTR fd;
215 
216 	if (size < sizeof(FLOPPY_DRIVE)) return NULL;
217 	fd = malloc(size);
218 	if (!fd) return NULL;
219 
220 	fd->fd_type      = FD_NONE;
221 	fd->fd_heads     = 0;
222 	fd->fd_cylinders = 0;
223 	fd->fd_motor     = 0;
224 	fd->fd_cylinder  = 0;
225 	fd->fd_readonly  = 0;
226 	fd->fd_vtable    = &dummy_vtbl;
227 	return fd;
228 }
229 
fd_new(void)230 FDRV_PTR fd_new(void)
231 {
232 	return fd_inew(sizeof(FLOPPY_DRIVE));
233 }
234 
235 /* Initialise a 9256 dummy drive */
fd_newnc9(FDRV_PTR fd)236 FDRV_PTR fd_newnc9(FDRV_PTR fd)
237 {
238 	FDRV_PTR p = fd_inew(sizeof(NC9_FLOPPY_DRIVE));
239 
240 	fd_settype(p, FD_NC9256);
241 	((NC9_FLOPPY_DRIVE *)p)->nc9_fdd = fd;
242 //
243 // These are the only commands which CP/M executes on a 9256 dummy drive,
244 // and so these are the only ones I'm going to pass through to the
245 // underlying drive.
246 //
247 	p->fd_vtable    = &d9256_vtbl;
248 	return p;
249 }
250 
251 
fd_destroy(FDRV_PTR * fd)252 void     fd_destroy(FDRV_PTR *fd)
253 {
254 	if (!(*fd)) return;
255 
256 	fd_eject(*fd);
257 	if ((*fd)->fd_vtable->fdv_destroy)
258 	{
259 		(*(*fd)->fd_vtable->fdv_destroy)(*fd);
260 	}
261 	free(*fd);
262 	*fd = NULL;
263 }
264 
265 
fd_gettype(FDRV_PTR fd)266 int fd_gettype    (FDRV_PTR fd) { return fd->fd_type; }
fd_getheads(FDRV_PTR fd)267 int fd_getheads   (FDRV_PTR fd) { return fd->fd_heads; }
fd_getcyls(FDRV_PTR fd)268 int fd_getcyls    (FDRV_PTR fd) { return fd->fd_cylinders; }
fd_getreadonly(FDRV_PTR fd)269 int fd_getreadonly(FDRV_PTR fd) { return fd->fd_readonly; }
270 
fd_settype(FDRV_PTR fd,int type)271 void fd_settype    (FDRV_PTR fd, int type)  { fd->fd_type  = type;  }
fd_setheads(FDRV_PTR fd,int heads)272 void fd_setheads   (FDRV_PTR fd, int heads) { fd->fd_heads = heads; }
fd_setcyls(FDRV_PTR fd,int cyls)273 void fd_setcyls    (FDRV_PTR fd, int cyls)  { fd->fd_cylinders  = cyls;  }
fd_setreadonly(FDRV_PTR fd,int ro)274 void fd_setreadonly(FDRV_PTR fd, int ro)    { fd->fd_readonly = ro; }
275 
fd_getmotor(FDRV_PTR fd)276 int fd_getmotor    (FDRV_PTR fd) { return fd->fd_motor; }
fd_getcurcyl(FDRV_PTR fd)277 int fd_getcurcyl   (FDRV_PTR fd) { return fd->fd_cylinder; }
278 
279 
fd9_getproxy(FDRV_PTR self)280 FDRV_PTR fd9_getproxy(FDRV_PTR self)
281 {
282 	if (self->fd_vtable == &d9256_vtbl)
283 	{
284 		return ((NC9_FLOPPY_DRIVE *)self)->nc9_fdd;
285 	}
286 	return NULL;
287 }
288 
fd9_setproxy(FDRV_PTR self,FDRV_PTR proxy)289 void     fd9_setproxy(FDRV_PTR self, FDRV_PTR proxy)
290 {
291 	if (self->fd_vtable == &d9256_vtbl)
292 	{
293 		((NC9_FLOPPY_DRIVE *)self)->nc9_fdd = proxy;
294 	}
295 
296 }
297