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