fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/devices/fdc.c *
7 * Created: 2007-09-06 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2007-2023 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <stdlib.h>
24#include <string.h>
25
26#include <devices/fdc.h>
27
28#include <drivers/block/blkpsi.h>
29
30
31/*
32 * This is the glue code between the 8272 FDC, block devices and
33 * the rest of the emulator.
34 */
35
36
37static
38struct {
39 unsigned v1;
40 unsigned v2;
41} errmap[] = {
42 { PCE_BLK_PSI_NO_ID, E8272_ERR_NO_ID },
43 { PCE_BLK_PSI_NO_DATA, E8272_ERR_NO_DATA },
44 { PCE_BLK_PSI_CRC_ID, E8272_ERR_CRC_ID },
45 { PCE_BLK_PSI_CRC_DATA, E8272_ERR_CRC_DATA },
46 { PCE_BLK_PSI_DEL_DAM, E8272_ERR_DEL_DAM },
47 { PCE_BLK_PSI_DATALEN, E8272_ERR_DATALEN },
48 { PCE_BLK_PSI_WPROT, E8272_ERR_WPROT },
49 { 0, 0 }
50};
51
52
53static void dev_fdc_del_dev (device_t *dev);
54
55
56/*
57 * Map an error code from PCE_BLK_PSI_* to E8272_ERR_*.
58 */
59static
60unsigned dev_fdc_map_error (unsigned err)
61{
62 unsigned i;
63 unsigned ret;
64
65 ret = 0;
66
67 i = 0;
68
69 while (errmap[i].v1 != 0) {
70 if (err & errmap[i].v1) {
71 err &= ~errmap[i].v1;
72 ret |= errmap[i].v2;
73 }
74
75 i += 1;
76 }
77
78 if (err) {
79 ret |= E8272_ERR_OTHER;
80 }
81
82 return (ret);
83}
84
85static
86unsigned dev_fdc_diskop_read (dev_fdc_t *fdc, e8272_diskop_t *p)
87{
88 unsigned ret, err;
89 disk_t *dsk;
90
91 dsk = dsks_get_disk (fdc->dsks, fdc->drive[p->pd & 3]);
92
93 if (dsk == NULL) {
94 p->cnt = 0;
95 return (E8272_ERR_NO_DATA);
96 }
97
98 if (dsk_get_type (dsk) != PCE_DISK_PSI) {
99 if (p->cnt < 512) {
100 p->cnt = 0;
101 return (E8272_ERR_OTHER);
102 }
103
104 if (dsk_read_chs (dsk, p->buf, p->pc, p->ph, p->ls, 1)) {
105 p->cnt = 0;
106 return (E8272_ERR_NO_DATA);
107 }
108
109 p->cnt = 512;
110
111 return (0);
112 }
113
114 err = dsk_psi_read_chs (dsk->ext, p->buf, &p->cnt, p->pc, p->ph, p->ps, 1);
115
116 ret = dev_fdc_map_error (err);
117
118 return (ret);
119}
120
121static
122unsigned dev_fdc_diskop_write (dev_fdc_t *fdc, e8272_diskop_t *p)
123{
124 unsigned err, ret;
125 disk_t *dsk;
126
127 dsk = dsks_get_disk (fdc->dsks, fdc->drive[p->pd & 3]);
128
129 if (dsk == NULL) {
130 p->cnt = 0;
131 return (E8272_ERR_NO_DATA);
132 }
133
134 if (dsk_get_type (dsk) != PCE_DISK_PSI) {
135 if (p->cnt != 512) {
136 return (E8272_ERR_OTHER);
137 }
138
139 if (dsk_write_chs (dsk, p->buf, p->pc, p->ph, p->ls, 1)) {
140 p->cnt = 0;
141 return (E8272_ERR_OTHER);
142 }
143
144 return (0);
145 }
146
147 err = dsk_psi_write_chs (dsk->ext, p->buf, &p->cnt, p->pc, p->ph, p->ps, 1);
148
149 ret = dev_fdc_map_error (err);
150
151 return (ret);
152}
153
154static
155unsigned dev_fdc_diskop_format (dev_fdc_t *fdc, e8272_diskop_t *p)
156{
157 unsigned cnt;
158 disk_t *dsk;
159 unsigned char buf[512];
160
161 dsk = dsks_get_disk (fdc->dsks, fdc->drive[p->pd & 3]);
162
163 if (dsk == NULL) {
164 return (E8272_ERR_NO_DATA);
165 }
166
167 cnt = 128 << p->ln;
168
169 if (dsk_get_type (dsk) != PCE_DISK_PSI) {
170 if (cnt != 512) {
171 return (E8272_ERR_OTHER);
172 }
173
174 memset (buf, p->fill, 512);
175
176 if (dsk_write_chs (dsk, buf, p->pc, p->ph, p->ps + 1, 1)) {
177 return (E8272_ERR_OTHER);
178 }
179
180 return (0);
181 }
182
183 if (p->ps == 0) {
184 dsk_psi_erase_track (dsk->ext, p->pc, p->ph);
185 }
186
187 if (dsk_psi_format_sector (dsk->ext, p->pc, p->ph, p->lc, p->lh, p->ls, cnt, p->fill)) {
188 return (E8272_ERR_OTHER);
189 }
190
191 return (0);
192}
193
194static
195unsigned dev_fdc_diskop_readid (dev_fdc_t *fdc, e8272_diskop_t *p)
196{
197 disk_t *dsk;
198 unsigned char buf[512];
199 unsigned lc, lh, ls, cnt, cnt_id;
200 unsigned long pos;
201
202 dsk = dsks_get_disk (fdc->dsks, fdc->drive[p->pd & 3]);
203
204 if (dsk == NULL) {
205 return (E8272_ERR_NO_ID);
206 }
207
208 if (dsk_get_type (dsk) != PCE_DISK_PSI) {
209 if (dsk_read_chs (dsk, buf, p->pc, p->ph, p->ps + 1, 1)) {
210 return (E8272_ERR_NO_ID);
211 }
212
213 p->lc = p->pc;
214 p->lh = p->ph;
215 p->ls = p->ps + 1;
216 p->ln = 2;
217
218 return (0);
219 }
220
221 if (dsk_psi_read_id (dsk->ext, p->pc, p->ph, p->ps, &lc, &lh, &ls, &cnt, &cnt_id, &pos)) {
222 return (E8272_ERR_NO_ID);
223 }
224
225 p->lc = lc;
226 p->lh = lh;
227 p->ls = ls;
228 p->ln = 0;
229 p->pos = pos;
230
231 while (cnt_id > 128) {
232 cnt_id >>= 1;
233 p->ln += 1;
234 }
235
236 return (0);
237}
238static
239unsigned dev_fdc_diskop (dev_fdc_t *fdc, unsigned op, e8272_diskop_t *p)
240{
241 switch (op) {
242 case E8272_DISKOP_READ:
243 return (dev_fdc_diskop_read (fdc, p));
244
245 case E8272_DISKOP_WRITE:
246 return (dev_fdc_diskop_write (fdc, p));
247
248 case E8272_DISKOP_FORMAT:
249 return (dev_fdc_diskop_format (fdc, p));
250
251 case E8272_DISKOP_READID:
252 return (dev_fdc_diskop_readid (fdc, p));
253 }
254
255 return (E8272_ERR_OTHER);
256}
257
258
259static
260void dev_fdc_clock (dev_fdc_t *fdc, unsigned n)
261{
262 e8272_clock (&fdc->e8272, n);
263}
264
265dev_fdc_t *dev_fdc_new (unsigned long addr)
266{
267 unsigned i;
268 dev_fdc_t *fdc;
269
270 fdc = malloc (sizeof (dev_fdc_t));
271
272 if (fdc == NULL) {
273 return (NULL);
274 }
275
276 dev_init (&fdc->dev, fdc, "fdc");
277
278 fdc->dev.del = dev_fdc_del_dev;
279 fdc->dev.clock = (void *) dev_fdc_clock;
280
281 e8272_init (&fdc->e8272);
282 e8272_set_diskop_fct (&fdc->e8272, fdc, dev_fdc_diskop);
283
284 mem_blk_init (&fdc->blk, addr, 8, 0);
285
286 mem_blk_set_fct (&fdc->blk, &fdc->e8272,
287 e8272_get_uint8, NULL, NULL,
288 e8272_set_uint8, NULL, NULL
289 );
290
291 for (i = 0; i < 4; i++) {
292 fdc->drive[i] = 0xffff;
293 }
294
295 return (fdc);
296}
297
298void dev_fdc_del (dev_fdc_t *fdc)
299{
300 if (fdc != NULL) {
301 mem_blk_free (&fdc->blk);
302 e8272_free (&fdc->e8272);
303
304 free (fdc);
305 }
306}
307
308static
309void dev_fdc_del_dev (device_t *dev)
310{
311 dev_fdc_del (dev->ext);
312}
313
314void dev_fdc_mem_add_io (dev_fdc_t *fdc, memory_t *io)
315{
316 mem_add_blk (io, &fdc->blk, 0);
317}
318
319void dev_fdc_mem_rmv_io (dev_fdc_t *fdc, memory_t *io)
320{
321 mem_rmv_blk (io, &fdc->blk);
322}
323
324
325void dev_fdc_reset (dev_fdc_t *fdc)
326{
327 e8272_reset (&fdc->e8272);
328}
329
330void dev_fdc_set_disks (dev_fdc_t *fdc, disks_t *dsks)
331{
332 fdc->dsks = dsks;
333}
334
335void dev_fdc_set_drive (dev_fdc_t *fdc, unsigned fdcdrv, unsigned drive)
336{
337 if (fdcdrv < 4) {
338 fdc->drive[fdcdrv] = drive;
339 }
340}
341
342unsigned dev_fdc_get_drive (dev_fdc_t *fdc, unsigned fdcdrv)
343{
344 if (fdcdrv < 4) {
345 return (fdc->drive[fdcdrv]);
346 }
347
348 return (0xffff);
349}