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