1 /***************************************************************************
2 * *
3 * LIBDSK: General floppy and diskimage access library *
4 * Copyright (C) 2001 John Elliott <seasip.webmaster@gmail.com> *
5 * *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU Library General Public *
8 * License as published by the Free Software Foundation; either *
9 * version 2 of the License, or (at your option) any later version. *
10 * *
11 * This library is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Library General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Library General Public *
17 * License along with this library; if not, write to the Free *
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
19 * MA 02111-1307, USA *
20 * *
21 ***************************************************************************/
22
23 /* Disc geometry probe and related code */
24
25 #include "drvi.h"
26
27 static unsigned char boot_pcw180[] =
28 {
29 0, 0, 40, 9, 2, 1, 3, 2, 0x2A, 0x52
30 };
31
32 static unsigned char boot_cpcsys[] =
33 {
34 0, 0, 40, 9, 2, 2, 3, 2, 0x2A, 0x52
35 };
36
37 static unsigned char boot_cpcdata[] =
38 {
39 0, 0, 40, 9, 2, 0, 3, 2, 0x2A, 0x52
40 };
41
42 /* We have detected a DOS superblock. Parse it for FAT filesystem info.
43 * NOTE: This is currently incomplete */
set_dos_fs(DSK_DRIVER * self,DSK_GEOMETRY * geom,unsigned char * bpb)44 static void set_dos_fs(DSK_DRIVER *self, DSK_GEOMETRY *geom, unsigned char *bpb)
45 {
46 dsk_isetoption(self, "FS:FAT:SECCLUS", bpb[2], 1);
47 dsk_isetoption(self, "FS:FAT:RESERVED", bpb[3] + 256 * bpb[4], 1);
48 dsk_isetoption(self, "FS:FAT:FATCOPIES", bpb[5], 1);
49 dsk_isetoption(self, "FS:FAT:DIRENTRIES", bpb[6] + 256 * bpb[7], 1);
50 dsk_isetoption(self, "FS:FAT:MEDIABYTE", bpb[10], 1);
51 dsk_isetoption(self, "FS:FAT:SECFAT", bpb[11] + 256 * bpb[12], 1);
52 }
53
54 /* We have detected a PCW superblock. Parse it for CP/M filesystem info */
set_pcw_fs(DSK_DRIVER * self,DSK_GEOMETRY * geom,unsigned char * buf)55 static void set_pcw_fs(DSK_DRIVER *self, DSK_GEOMETRY *geom, unsigned char *buf)
56 {
57 unsigned bsh, blocksize, secsize, dirblocks, drm, off, dsm, al;
58 unsigned tracks, sectors, exm;
59
60 /* If it's got a DOS spec at 0, CP/M spec at 0x80 */
61 if (buf[0] == 0xE9 || buf[0] == 0xEA)
62 {
63 set_dos_fs(self, geom, buf + 11);
64 buf += 0x80;
65 }
66 /* If it starts 0xE5 assume PCW 180k format */
67 if (buf[0] == 0xE5)
68 buf = boot_pcw180;
69 bsh = buf[6];
70 blocksize = 128 << bsh;
71 secsize = 128 << buf[4];
72 dirblocks = buf[7];
73 drm = dirblocks * (blocksize / 32);
74 off = buf[5];
75 al = (1L << 16) - (1L << (16 - dirblocks));
76 tracks = buf[2];
77 if (buf[1] & 3) tracks *= 2; /* Double-sided */
78 sectors = buf[3];
79 dsm = ((long)tracks - off) * sectors * secsize / blocksize;
80
81 if (dsm <= 256) exm = (blocksize / 1024) - 1;
82 else exm = (blocksize / 2048) - 1;
83
84 dsk_isetoption(self, "FS:CP/M:BSH", bsh, 1);
85 dsk_isetoption(self, "FS:CP/M:BLM", (1 << bsh) - 1, 1);
86 dsk_isetoption(self, "FS:CP/M:EXM", exm, 1);
87 dsk_isetoption(self, "FS:CP/M:DSM", dsm - 1, 1);
88 dsk_isetoption(self, "FS:CP/M:DRM", drm - 1, 1);
89 dsk_isetoption(self, "FS:CP/M:AL0", (al >> 8) & 0xFF, 1);
90 dsk_isetoption(self, "FS:CP/M:AL1", al & 0xFF, 1);
91 dsk_isetoption(self, "FS:CP/M:CKS", drm / 4, 1);
92 dsk_isetoption(self, "FS:CP/M:OFF", off, 1);
93 }
94
95 typedef struct minidpb
96 {
97 int type;
98 unsigned bsh;
99 unsigned blm;
100 unsigned exm;
101 unsigned dsm;
102 unsigned drm;
103 unsigned al0;
104 unsigned al1;
105 unsigned cks;
106 unsigned off;
107 } MINIDPB;
108
109 static MINIDPB cpm86_minidpb[] =
110 {
111 { 0x00, 3, 0x07, 0, 0x09B, 0x3F, 0xC0, 0x00, 0x10, 0x01, },
112 { 0x01, 4, 0x0F, 1, 0x09D, 0x3F, 0x80, 0x00, 0x10, 0x01, },
113 { 0x10, 4, 0x0F, 1, 0x0AA, 0x3F, 0x80, 0x00, 0x10, 0x04, },
114 { 0x40, 4, 0x0F, 1, 0x0AA, 0x3F, 0x80, 0x00, 0x10, 0x04, },
115 { 0x11, 4, 0x0F, 0, 0x15E, 0xFF, 0xF0, 0x00, 0x40, 0x04, },
116 { 0x48, 4, 0x0F, 0, 0x162, 0xFF, 0xF0, 0x00, 0x40, 0x02, },
117 { 0x0C, 5, 0x1F, 1, 0x127, 0xFF, 0xC0, 0x00, 0x40, 0x02, },
118 { 0x90, 5, 0x1F, 1, 0x162, 0xFF, 0xC0, 0x00, 0x40, 0x02, },
119 };
120
setup_minidpb(DSK_DRIVER * self,MINIDPB * p)121 static void setup_minidpb(DSK_DRIVER *self, MINIDPB *p)
122 {
123 dsk_isetoption(self, "FS:CP/M:BSH", p->bsh, 1);
124 dsk_isetoption(self, "FS:CP/M:BLM", p->blm, 1);
125 dsk_isetoption(self, "FS:CP/M:EXM", p->exm, 1);
126 dsk_isetoption(self, "FS:CP/M:DSM", p->dsm, 1);
127 dsk_isetoption(self, "FS:CP/M:DRM", p->drm, 1);
128 dsk_isetoption(self, "FS:CP/M:AL0", p->al0, 1);
129 dsk_isetoption(self, "FS:CP/M:AL1", p->al1, 1);
130 dsk_isetoption(self, "FS:CP/M:CKS", p->cks, 1);
131 dsk_isetoption(self, "FS:CP/M:OFF", p->off, 1);
132 }
133
134 /* We have detected a CP/M-86 superblock. Parse it for CP/M filesystem info */
set_cpm86_fs(DSK_DRIVER * self,DSK_GEOMETRY * geom,unsigned char * buf)135 static void set_cpm86_fs(DSK_DRIVER *self, DSK_GEOMETRY *geom,
136 unsigned char *buf)
137 {
138 unsigned n;
139 for (n = 0; n < sizeof(cpm86_minidpb) / sizeof(cpm86_minidpb[0]); n++)
140 {
141 MINIDPB *p = &cpm86_minidpb[n];
142 if (p->type == buf[511])
143 {
144 setup_minidpb(self, p);
145 break;
146 }
147 }
148 }
149
150
151 /* We have detected a format where we know the DPB. Populate it. */
152 static MINIDPB fixed_formats[] =
153 {
154 { FMT_AMPRO400D, 4, 0x0F, 1, 0x5E, 0x3F, 0x80, 0x00, 0x10, 0x02 },
155 { FMT_AMPRO800, 4, 0x0F, 0, 0x18A, 0xFF, 0xF0, 0x00, 0x40, 0x02 },
156 /* There may be more here in the future */
157 };
158
set_fixed_fs(DSK_DRIVER * self,dsk_format_t fmt)159 static void set_fixed_fs(DSK_DRIVER *self, dsk_format_t fmt)
160 {
161 unsigned n;
162
163 for (n = 0; n < sizeof(fixed_formats) / sizeof(fixed_formats[0]); n++)
164 {
165 if (fixed_formats[n].type == fmt)
166 setup_minidpb(self, &fixed_formats[n]);
167 }
168 }
169
170
171
172
173
174 /* Probe the geometry of a disc. This will use the boot sector or the
175 * driver's own probe */
176
dsk_getgeom(DSK_DRIVER * self,DSK_GEOMETRY * geom)177 LDPUBLIC32 dsk_err_t LDPUBLIC16 dsk_getgeom(DSK_DRIVER *self, DSK_GEOMETRY *geom)
178 {
179 DRV_CLASS *dc;
180 dsk_err_t e;
181
182 if (!self || !geom || !self->dr_class) return DSK_ERR_BADPTR;
183
184 /* Check if the driver has overridden this function. If it has,
185 * then use its geometry probe, which is probably more limited. */
186 dc = self->dr_class;
187 memset(geom, 0, sizeof(*geom));
188
189 if (dc->dc_getgeom)
190 {
191 e = (dc->dc_getgeom)(self, geom);
192 if (e != DSK_ERR_NOTME && e != DSK_ERR_NOTIMPL) return e;
193 }
194 return dsk_defgetgeom(self, geom);
195 }
196
197
198
199 /* Probe the geometry of a disc. This will always use the boot sector. */
dsk_defgetgeom(DSK_DRIVER * self,DSK_GEOMETRY * geom)200 dsk_err_t dsk_defgetgeom(DSK_DRIVER *self, DSK_GEOMETRY *geom)
201 {
202 DSK_FORMAT secid;
203 dsk_err_t e;
204 unsigned char *secbuf;
205 unsigned long dsksize;
206 dsk_rate_t oldrate;
207
208 if (!self || !geom || !self->dr_class) return DSK_ERR_BADPTR;
209
210 memset(geom, 0, sizeof(*geom));
211
212 /* Switch to a minimal format */
213 e = dg_stdformat(geom, FMT_180K, NULL, NULL);
214 if (e) return e;
215 /* Allocate buffer for boot sector (512 bytes) */
216 secbuf = dsk_malloc(geom->dg_secsize);
217 if (!secbuf) return DSK_ERR_NOMEM;
218
219
220 /* Check for CPC6128 type discs. Also probe the data rate; if we get a
221 * missing address mark, then the data rate is wrong.
222 */
223 e = dg_stdformat(geom, FMT_180K, NULL, NULL);
224 if (e) return e;
225 e = dsk_lsecid(self, geom, 0, &secid);
226 /* Check for HD discs */
227 if (e == DSK_ERR_NOADDR)
228 {
229 geom->dg_datarate = RATE_HD;
230 e = dsk_lsecid(self, geom, 0, &secid);
231 }
232 /* Check for DD 5.25" disc in HD 5.25" drive */
233 if (e == DSK_ERR_NOADDR)
234 {
235 geom->dg_datarate = RATE_DD;
236 e = dsk_lsecid(self, geom, 0, &secid);
237 }
238 /* Check for BBC micro DFS discs (FM encoded) */
239 if (e == DSK_ERR_NOADDR)
240 {
241 e = dg_stdformat(geom, FMT_BBC100, NULL, NULL);
242 if (!e) e = dsk_lsecid(self, geom, 0, &secid);
243 }
244 if (!e) /* We could get the sector ID */
245 {
246 if ((secid.fmt_sector & 0xF0) == 0x10 &&
247 secid.fmt_secsize == 512) /* Ampro 40 track double sided */
248 {
249 dsk_free(secbuf);
250 e = dg_stdformat(geom, FMT_AMPRO400D, NULL, NULL);
251 if (!e) set_fixed_fs(self, FMT_AMPRO400D);
252 return e;
253 }
254 if ((secid.fmt_sector & 0xC0) == 0x40 &&
255 secid.fmt_secsize == 512) /* CPC system */
256 {
257 dsk_free(secbuf);
258 e = dg_stdformat(geom, FMT_CPCSYS, NULL, NULL);
259 if (!e) set_pcw_fs(self, geom, boot_cpcsys);
260 return e;
261 }
262 if ((secid.fmt_sector & 0xC0) == 0xC0 &&
263 secid.fmt_secsize == 512) /* CPC data */
264 {
265 dsk_free(secbuf);
266 e = dg_stdformat(geom, FMT_CPCDATA, NULL, NULL);
267 if (!e) set_pcw_fs(self, geom, boot_cpcdata);
268 return e;
269 }
270 /* [v0.6.0] Handle discs with non-512 byte sectors */
271 if (secid.fmt_secsize == 256)
272 {
273 /* BBC Micro FM floppy? */
274 if ((geom->dg_fm & RECMODE_MASK) == RECMODE_FM)
275 {
276 unsigned int tot_sectors;
277 e = dsk_lread(self, geom, secbuf, 1);
278
279 tot_sectors = secbuf[7] + 256 * (secbuf[6] & 3);
280
281 /* If disc is FM recorded but does not have 400 or 800 sectors, fail. */
282 if (e == DSK_ERR_OK && tot_sectors != 400 && tot_sectors != 800) e = DSK_ERR_BADFMT;
283
284 geom->dg_cylinders = tot_sectors / (geom->dg_heads * geom->dg_sectors);
285 dsk_free(secbuf);
286 return e;
287 }
288 else /* MFM */
289 {
290 e = dg_stdformat(geom, FMT_ACORN160, NULL, NULL);
291 if (!e) e = dsk_lread(self, geom, secbuf, 0);
292 if (e)
293 {
294 dsk_free(secbuf);
295 return DSK_ERR_BADFMT;
296 }
297 /* Acorn ADFS discs have a size in sectors at 0xFC in the
298 * first sector */
299 dsksize = secbuf[0xFC] + 256 * secbuf[0xFD] +
300 65536L * secbuf[0xFE];
301 dsk_free(secbuf);
302 if (dsksize == 640) return dg_stdformat(geom, FMT_ACORN160, NULL, NULL);
303 if (dsksize == 1280) return dg_stdformat(geom, FMT_ACORN320, NULL, NULL);
304 if (dsksize == 2560) return dg_stdformat(geom, FMT_ACORN640, NULL, NULL);
305 /* The DOS Plus boot floppy has 2720 here for
306 * some reason */
307 if (dsksize == 2720) return dg_stdformat(geom, FMT_ACORN640, NULL, NULL);
308 return DSK_ERR_BADFMT;
309 }
310 }
311 if (secid.fmt_secsize == 1024)
312 {
313 dsk_rate_t rate;
314 /* Ampro 80 track double sided */
315 if ((secid.fmt_sector & 0xF0) == 0x10)
316 {
317 dsk_free(secbuf);
318 e = dg_stdformat(geom, FMT_AMPRO800, NULL, NULL);
319 if (!e) set_fixed_fs(self, FMT_AMPRO800);
320 return e;
321 }
322 /* Save the data rate, which we know to be correct */
323 rate = geom->dg_datarate;
324
325 dsk_free(secbuf);
326 /* Switch to a format with 1k sectors */
327 if (geom->dg_datarate == RATE_HD)
328 e = dg_stdformat(geom, FMT_ACORN1600, NULL, NULL);
329 else e = dg_stdformat(geom, FMT_ACORN800, NULL, NULL);
330 if (e) return e;
331 /* And restore it. */
332 geom->dg_datarate = rate;
333 /* Allocate buffer for boot sector (1k bytes) */
334 secbuf = dsk_malloc(geom->dg_secsize);
335 if (!secbuf) return DSK_ERR_NOMEM;
336 e = dsk_lread(self, geom, secbuf, 0);
337 if (!e)
338 {
339 dsksize = secbuf[0xFC] + 256 * secbuf[0xFD] +
340 65536L * secbuf[0xFE];
341 /* Check for 1600k-format */
342 if (geom->dg_datarate == RATE_HD)
343 {
344 /* XXX Need a better check for Acorn 1600k */
345 dsk_free(secbuf);
346 return DSK_ERR_OK;
347 }
348 /* Check for D-format magic */
349 if (dsksize == 3200)
350 {
351 dsk_free(secbuf);
352 return DSK_ERR_OK;
353 }
354 /* Check for E-format magic */
355 if (secbuf[4] == 10 && secbuf[5] == 5 &&
356 secbuf[6] == 2 && secbuf[7] == 2)
357 {
358 dsk_free(secbuf);
359 return DSK_ERR_OK;
360 }
361 }
362 /* Check for DOS Plus magic. DOS Plus has sectors
363 * based at 1, not 0. */
364 geom->dg_secbase = 1;
365 e = dsk_lread(self, geom, secbuf, 0);
366 if (!e)
367 {
368 if (secbuf[0] == 0xFD &&
369 secbuf[1] == 0xFF &&
370 secbuf[2] == 0xFF)
371 {
372 dsk_free(secbuf);
373 return DSK_ERR_OK;
374 }
375 }
376 dsk_free(secbuf);
377 return DSK_ERR_BADFMT;
378 }
379 /* Can't handle other discs with non-512 sector sizes. */
380 if ((secid.fmt_secsize != 512))
381 {
382 dsk_free(secbuf);
383 return DSK_ERR_BADFMT;
384 }
385 }
386 /* If the driver couldn't do a READ ID call, then ignore it */
387 if (e == DSK_ERR_NOTIMPL) e = DSK_ERR_OK;
388 /* Try to ID the disc from its boot sector */
389 if (!e) e = dsk_lread(self, geom, secbuf, 0);
390 if (e)
391 {
392 dsk_free(secbuf);
393 return e;
394 }
395
396 /* Save the data rate, because what we have is right, and what's
397 * in the sector might not be. */
398 oldrate = geom->dg_datarate;
399 /* We have the sector. Let's try to guess what it is */
400 e = dg_dosgeom(geom, secbuf);
401 if (e == DSK_ERR_OK)
402 {
403 set_dos_fs(self, geom, secbuf + 11);
404 }
405 if (e == DSK_ERR_BADFMT)
406 {
407 /* If dg_pcwgeom succeeded, we have a CP/M filesystem with known parameters */
408 e = dg_pcwgeom(geom, secbuf);
409 if (e == DSK_ERR_OK)
410 set_pcw_fs(self, geom, secbuf);
411 }
412 if (e == DSK_ERR_BADFMT)
413 {
414 e = dg_aprigeom(geom, secbuf);
415 if (e == DSK_ERR_OK)
416 {
417 set_dos_fs(self, geom, secbuf + 80);
418 }
419 }
420 if (e == DSK_ERR_BADFMT)
421 {
422 e = dg_cpm86geom(geom, secbuf);
423 if (e == DSK_ERR_OK)
424 set_cpm86_fs(self, geom, secbuf);
425 }
426 /* Check for Oups Discovery 1 */
427 if (e == DSK_ERR_BADFMT)
428 {
429 e = dg_opusgeom(geom, secbuf);
430 /* if (e == DSK_ERR_OK)
431 set_opus_fs(self, geom, secbuf); */
432 }
433 geom->dg_datarate = oldrate;
434
435 dsk_free(secbuf);
436 return e;
437 }
438
439
440 /* Interpret a DOS superblock */
dg_dosgeom(DSK_GEOMETRY * self,const unsigned char * bootsect)441 LDPUBLIC32 dsk_err_t LDPUBLIC16 dg_dosgeom(DSK_GEOMETRY *self, const unsigned char *bootsect)
442 {
443 dsk_lsect_t lsmax;
444
445 if (!self || !bootsect) return DSK_ERR_BADPTR;
446
447 /* If the boot sector starts 0xE9 or 0xEB, it's DOS. If it starts with
448 * three zeroes, it's Atari.
449 * In particular, we have to be careful not to try to identify a
450 * PCW 180k floppy, which starts 0x00 0x00 0x28 0x09 */
451
452 if (bootsect[0] != 0xE9 && bootsect[0] != 0xEB)
453 {
454 /* However, the Mini Office distribution floppies for the Atari have only
455 * two zeroes. So if bytes 0B 0C 15 and 1B look something like a BPB,
456 * allow them. This should be sufficient to reject PCW diskettes */
457 if (bootsect[0x0b] != 0 || bootsect[0x0c] != 2 ||
458 bootsect[0x15] < 0xF8 || bootsect[0x1b] != 0)
459 {
460 if (bootsect[0] || bootsect[1] || bootsect[2])
461 return DSK_ERR_BADFMT;
462 }
463 }
464
465 /* Reject fake DOS bootsectors created by 144FEAT */
466 if (bootsect[511] == 144 || bootsect[511] == 72 || bootsect[511] == 12)
467 return DSK_ERR_BADFMT;
468
469 self->dg_secsize = bootsect[11] + 256 * bootsect[12];
470 if ((self->dg_secsize % 128) || (self->dg_secsize == 0))
471 /* Possible Apricot bootdisk if sector size is 0, or not a multiple of 128 */
472 /* self->dg_secsize = 512; */
473 return DSK_ERR_BADFMT;
474 self->dg_secbase = 1;
475 self->dg_heads = bootsect[26] + 256 * bootsect[27];
476 self->dg_sectors = bootsect[24] + 256 * bootsect[25];
477 if (!self->dg_heads || !self->dg_sectors) return DSK_ERR_BADFMT;
478 lsmax = bootsect[19] + 256 * bootsect[20];
479 lsmax /= self->dg_heads;
480 lsmax /= self->dg_sectors;
481 self->dg_cylinders = (dsk_pcyl_t)lsmax;
482 /* DOS boot sector doesn't store the data rate. We guess that if there are >12
483 * sectors per track, it must have used high density to get them all in */
484 self->dg_datarate = (self->dg_sectors >= 12) ? RATE_HD : RATE_SD;
485 /* Similarly it doesn't store the gap lengths: */
486 switch(self->dg_sectors)
487 {
488 case 8: self->dg_rwgap = 0x2A; self->dg_fmtgap = 0x50; break;
489 case 9: self->dg_rwgap = 0x2A; self->dg_fmtgap = 0x52; break;
490 case 10: self->dg_rwgap = 0x0C; self->dg_fmtgap = 0x17; break;
491 case 15: self->dg_rwgap = 0x1B; self->dg_fmtgap = 0x50; break;
492 case 18: self->dg_rwgap = 0x1B; self->dg_fmtgap = 0x50; break;
493 default: self->dg_rwgap = 0x2A; self->dg_fmtgap = 0x52; break;
494 }
495 self->dg_fm = RECMODE_MFM;
496 self->dg_nomulti = 0;
497
498 return DSK_ERR_OK;
499 }
500
501
502 /* Interpret a PCW superblock */
dg_pcwgeom(DSK_GEOMETRY * dg,const unsigned char * bootsec)503 LDPUBLIC32 dsk_err_t LDPUBLIC16 dg_pcwgeom(DSK_GEOMETRY *dg, const unsigned char *bootsec)
504 {
505 static unsigned char alle5[10] = { 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
506 0xE5, 0xE5, 0xE5, 0xE5, 0xE5 };
507
508 /* Treat all 0xE5s as 180k */
509 if (!memcmp(bootsec, alle5, 10)) bootsec = boot_pcw180;
510 /* Check for PCW16 boot/root format */
511 if (bootsec[0] == 0xE9 || bootsec[0] == 0xEA)
512 {
513 if (memcmp(bootsec + 0x2B, "CP/M", 4) ||
514 memcmp(bootsec + 0x33, "DSK", 3) ||
515 memcmp(bootsec + 0x7C, "CP/M", 4)) return DSK_ERR_BADFMT;
516 /* Detected PCW16 boot+root, disc spec at 80h */
517 bootsec += 0x80;
518 }
519 if (bootsec[0] != 3 && bootsec[0] != 0) return DSK_ERR_BADFMT;
520
521 switch(bootsec[1] & 3)
522 {
523 case 0: dg->dg_heads = 1; dg->dg_sidedness = SIDES_ALT; break;
524 case 1: dg->dg_heads = 2; dg->dg_sidedness = SIDES_ALT; break;
525 case 2: dg->dg_heads = 2; dg->dg_sidedness = SIDES_OUTBACK; break;
526 default: return DSK_ERR_BADFMT;
527 }
528 dg->dg_cylinders = bootsec[2];
529 dg->dg_sectors = bootsec[3];
530 /* Zeroes here may mean an Apricot superblock */
531 if (!dg->dg_cylinders || !dg->dg_sectors) return DSK_ERR_BADFMT;
532 dg->dg_secbase = 1;
533 dg->dg_secsize = 128;
534 /* My PCW16 extension to the PCW superblock encodes data rate. Fancy that. */
535 dg->dg_datarate = (bootsec[1] & 0x40) ? RATE_HD : RATE_SD;
536 dg->dg_fm = RECMODE_MFM;
537 dg->dg_nomulti = 0;
538 dg->dg_rwgap = bootsec[8];
539 dg->dg_fmtgap = bootsec[9];
540 dg->dg_secsize = 128 << bootsec[4];
541
542 return DSK_ERR_OK;
543 }
544
545 /* Interpret a CP/M86 (floppy) superblock */
dg_cpm86geom(DSK_GEOMETRY * dg,const unsigned char * bootsec)546 LDPUBLIC32 dsk_err_t LDPUBLIC16 dg_cpm86geom(DSK_GEOMETRY *dg, const unsigned char *bootsec)
547 {
548 switch(bootsec[511])
549 {
550 case 0x00: return dg_stdformat(dg, FMT_160K, NULL, NULL);
551 case 0x01: return dg_stdformat(dg, FMT_320K, NULL, NULL);
552 case 0x0C: return dg_stdformat(dg, FMT_1200F, NULL, NULL);
553 case 0x40:
554 case 0x10: return dg_stdformat(dg, FMT_360K, NULL, NULL);
555 case 0x11: return dg_stdformat(dg, FMT_720K, NULL, NULL);
556 case 0x48: return dg_stdformat(dg, FMT_720F, NULL, NULL);
557 case 0x90: return dg_stdformat(dg, FMT_1440F, NULL, NULL);
558 }
559 return DSK_ERR_BADFMT;
560 }
561
562
563
564 /* Interpret an Apricot superblock */
dg_aprigeom(DSK_GEOMETRY * self,const unsigned char * bootsect)565 LDPUBLIC32 dsk_err_t LDPUBLIC16 dg_aprigeom(DSK_GEOMETRY *self, const unsigned char *bootsect)
566 {
567 int n;
568
569 if (!self || !bootsect) return DSK_ERR_BADPTR;
570
571 /* Check that the first 8 bytes are ASCII (OEM label) or all zeroes */
572 for (n = 0; n < 8; n++)
573 if (bootsect[n] != 0 && (bootsect[n] < 0x20 || bootsect[n] > 0x7E))
574 return DSK_ERR_BADFMT;
575
576 /* Sector size */
577 self->dg_secsize = bootsect[0x0E] + 256 * bootsect[0x0F];
578 /* [1.4.2] If sector size is not a reasonable value, this
579 * could be a non-Apricot disk that happens to have
580 * ASCII at the start of the boot sector */
581 if ((self->dg_secsize % 128) || (self->dg_secsize == 0))
582 return DSK_ERR_BADFMT;
583 self->dg_secbase = 1;
584 self->dg_heads = bootsect[0x16];
585 self->dg_sectors = bootsect[0x10] + 256 * bootsect[0x11];
586 if (!self->dg_heads || !self->dg_sectors) return DSK_ERR_BADFMT;
587 self->dg_cylinders = bootsect[0x12] + 256 * bootsect[0x13];
588 /* Sector doesn't store the data rate. We guess that if there are >12
589 * sectors per track, it must have used high density to get them all in */
590 self->dg_datarate = (self->dg_sectors >= 12) ? RATE_HD : RATE_SD;
591 /* Similarly it doesn't store the gap lengths: */
592 switch(self->dg_sectors)
593 {
594 case 8: self->dg_rwgap = 0x2A; self->dg_fmtgap = 0x50; break;
595 case 9: self->dg_rwgap = 0x2A; self->dg_fmtgap = 0x52; break;
596 case 10: self->dg_rwgap = 0x0C; self->dg_fmtgap = 0x17; break;
597 case 15: self->dg_rwgap = 0x1B; self->dg_fmtgap = 0x50; break;
598 case 18: self->dg_rwgap = 0x1B; self->dg_fmtgap = 0x50; break;
599 default: self->dg_rwgap = 0x2A; self->dg_fmtgap = 0x52; break;
600 }
601 self->dg_fm = RECMODE_MFM;
602 self->dg_nomulti = 0;
603
604 return DSK_ERR_OK;
605 }
606
607 /* Interpret an Opus Discovery boot sector */
dg_opusgeom(DSK_GEOMETRY * dg,const unsigned char * bootsec)608 LDPUBLIC32 dsk_err_t LDPUBLIC16 dg_opusgeom(DSK_GEOMETRY *dg,
609 const unsigned char *bootsec)
610 {
611 if (bootsec[0] != 0x18) /* Z80 relative jump */
612 return DSK_ERR_BADFMT;
613
614 dg->dg_cylinders = bootsec[2];
615 dg->dg_heads = bootsec[3];
616 dg->dg_sectors = bootsec[4];
617 dg->dg_sidedness = SIDES_OUTOUT; /* XXX Provisional */
618 dg->dg_secbase = 1;
619 dg->dg_secsize = 512;
620 dg->dg_datarate = RATE_SD;
621 dg->dg_fm = RECMODE_MFM;
622 dg->dg_nomulti = 0;
623 dg->dg_rwgap = 0x2A; /* XXX Provisional */
624 dg->dg_fmtgap = 0x52; /* XXX Provisional */
625 dg->dg_secsize = 128 << bootsec[4];
626
627 return DSK_ERR_OK;
628 }
629
630
631