1 // license:BSD-3-Clause
2 // copyright-holders:Antoine Mine
3 /*********************************************************************
4 
5     formats/thom_dsk.c
6 
7     Thomson disk images
8 
9     Based on work of  Antoine Mine'
10 
11 *********************************************************************/
12 
13 #include <cstring>
14 #include <cassert>
15 #include "thom_dsk.h"
16 #include "basicdsk.h"
17 
18 static const int sap_magic_num = 0xB3; /* simple XOR crypt */
19 
20 
21 static const char sap_header[] =
22 	"\001SYSTEME D'ARCHIVAGE PUKALL S.A.P. "
23 	"(c) Alexandre PUKALL Avril 1998";
24 
25 
26 static const uint16_t sap_crc[] =
27 {
28 	0x0000, 0x1081, 0x2102, 0x3183,   0x4204, 0x5285, 0x6306, 0x7387,
29 	0x8408, 0x9489, 0xa50a, 0xb58b,   0xc60c, 0xd68d, 0xe70e, 0xf78f,
30 };
31 
32 struct sap_dsk_tag
33 {
34 	int tracks;
35 	int sector_size;
36 		int sector_pos[80][16]; /* remember sector position in file */
37 };
38 
thom_sap_crc(uint8_t * data,int size)39 static uint16_t thom_sap_crc( uint8_t* data, int size )
40 {
41 		int i;
42 		uint16_t crc = 0xffff, crc2;
43 		for ( i = 0; i < size; i++ )
44 		{
45 				crc2 = ( crc >> 4 ) ^ sap_crc[ ( crc ^ data[i] ) & 15 ];
46 				crc = ( crc2 >> 4 ) ^ sap_crc[ ( crc2 ^ (data[i] >> 4) ) & 15 ];
47 		}
48 		return crc;
49 }
50 
get_tag(floppy_image_legacy * floppy)51 static struct sap_dsk_tag *get_tag(floppy_image_legacy *floppy)
52 {
53 	struct sap_dsk_tag *tag;
54 	tag = (sap_dsk_tag *)floppy_tag(floppy);
55 	return tag;
56 }
57 
58 
FLOPPY_IDENTIFY(sap_dsk_identify)59 static FLOPPY_IDENTIFY(sap_dsk_identify)
60 {
61 	char header[0x100];
62 	floppy_image_read(floppy, header, 0, sizeof(sap_header));
63 	if (!memcmp( header, sap_header, sizeof(sap_header) ) )
64 	{
65 		*vote= 100;
66 	} else {
67 		*vote = 0;
68 	}
69 	return FLOPPY_ERROR_SUCCESS;
70 }
sap_get_heads_per_disk(floppy_image_legacy * floppy)71 static int sap_get_heads_per_disk(floppy_image_legacy *floppy)
72 {
73 	return 1;
74 }
75 
sap_get_tracks_per_disk(floppy_image_legacy * floppy)76 static int sap_get_tracks_per_disk(floppy_image_legacy *floppy)
77 {
78 	return get_tag(floppy)->tracks;
79 }
80 
81 
get_offset(floppy_image_legacy * floppy,int head,int track,int sector,bool sector_is_index,uint64_t * offset)82 static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, uint64_t *offset)
83 {
84 	uint64_t offs;
85 		struct sap_dsk_tag *tag = get_tag(floppy);
86 	/* translate the sector to a raw sector */
87 	if (!sector_is_index)
88 	{
89 		sector -= 1;
90 	}
91 	/* check to see if we are out of range */
92 	if ((head < 0) || (head >= 1) || (track < 0) || (track >=tag->tracks)
93 			|| (sector < 0) || (sector >= 16))
94 		return FLOPPY_ERROR_SEEKERROR;
95 
96 	offs = tag->sector_pos[track][sector];
97 	if (offs <= 0 )
98 		return FLOPPY_ERROR_SEEKERROR;
99 
100 	if (offset)
101 		*offset = offs;
102 	return FLOPPY_ERROR_SUCCESS;
103 }
104 
105 
106 
internal_sap_read_sector(floppy_image_legacy * floppy,int head,int track,int sector,bool sector_is_index,void * buffer,size_t buflen)107 static floperr_t internal_sap_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, void *buffer, size_t buflen)
108 {
109 	uint64_t offset;
110 	floperr_t err;
111 	int i;
112 	uint8_t *buf;
113 	err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
114 	if (err)
115 		return err;
116 
117 	floppy_image_read(floppy, buffer, offset+4, buflen);
118 	buf = (uint8_t*)buffer;
119 	for (i=0;i<buflen;i++) {
120 		buf[i] ^= sap_magic_num;
121 	}
122 	return FLOPPY_ERROR_SUCCESS;
123 }
124 
125 
126 
internal_sap_write_sector(floppy_image_legacy * floppy,int head,int track,int sector,bool sector_is_index,const void * buffer,size_t buflen,int ddam)127 static floperr_t internal_sap_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, const void *buffer, size_t buflen, int ddam)
128 {
129 	uint64_t offset;
130 	floperr_t err;
131 	uint8_t buf[256+6];
132 		uint16_t crc;
133 	int i;
134 	err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
135 	if (err)
136 		return err;
137 
138 		/* buf = 4-byte header + sector + 2-byte CRC */
139 	floppy_image_read(floppy, buf, offset, 4);
140 	for (i=0;i<buflen;i++) {
141 		buf[i+4] = ((uint8_t*)buffer)[i];
142 	}
143 		crc = thom_sap_crc( buf, buflen+4 );
144 		buf[buflen+4] = crc >> 8;
145 		buf[buflen+5] = crc & 0xff;
146 	for (i=0;i<buflen;i++) {
147 		buf[i+4] ^= sap_magic_num;
148 	}
149 	floppy_image_write(floppy, buf, offset, buflen+6);
150 
151 	return FLOPPY_ERROR_SUCCESS;
152 }
153 
154 
sap_read_sector(floppy_image_legacy * floppy,int head,int track,int sector,void * buffer,size_t buflen)155 static floperr_t sap_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
156 {
157 	return internal_sap_read_sector(floppy, head, track, sector, false, buffer, buflen);
158 }
159 
sap_write_sector(floppy_image_legacy * floppy,int head,int track,int sector,const void * buffer,size_t buflen,int ddam)160 static floperr_t sap_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
161 {
162 	return internal_sap_write_sector(floppy, head, track, sector, false, buffer, buflen, ddam);
163 }
164 
sap_read_indexed_sector(floppy_image_legacy * floppy,int head,int track,int sector,void * buffer,size_t buflen)165 static floperr_t sap_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
166 {
167 	return internal_sap_read_sector(floppy, head, track, sector, true, buffer, buflen);
168 }
169 
sap_write_indexed_sector(floppy_image_legacy * floppy,int head,int track,int sector,const void * buffer,size_t buflen,int ddam)170 static floperr_t sap_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
171 {
172 	return internal_sap_write_sector(floppy, head, track, sector, true, buffer, buflen, ddam);
173 }
174 
sap_get_sector_length(floppy_image_legacy * floppy,int head,int track,int sector,uint32_t * sector_length)175 static floperr_t sap_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
176 {
177 	floperr_t err;
178 	err = get_offset(floppy, head, track, sector, false, nullptr);
179 	if (err)
180 		return err;
181 
182 	if (sector_length) {
183 		*sector_length = get_tag(floppy)->sector_size;
184 	}
185 	return FLOPPY_ERROR_SUCCESS;
186 }
187 
188 
189 
sap_get_indexed_sector_info(floppy_image_legacy * floppy,int head,int track,int sector_index,int * cylinder,int * side,int * sector,uint32_t * sector_length,unsigned long * flags)190 static floperr_t sap_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags)
191 {
192 	floperr_t err;
193 	uint8_t header[4];
194 	uint64_t offset = 0;
195 	sector_index += 1;
196 	err = get_offset(floppy, head, track, sector_index, false, &offset);
197 
198 	floppy_image_read(floppy, header, offset, 4);
199 	if (cylinder)
200 		*cylinder = header[2];
201 	if (side)
202 		*side = head;
203 	if (sector)
204 		*sector = header[3];
205 	if (sector_length)
206 		*sector_length = get_tag(floppy)->sector_size;
207 	if (flags)
208 		/* TODO: read DAM or DDAM and determine flags */
209 		*flags = 0;
210 	return err;
211 }
212 
sap_post_format(floppy_image_legacy * floppy,util::option_resolution * params)213 static floperr_t sap_post_format(floppy_image_legacy *floppy, util::option_resolution *params)
214 {
215 		int track,sector;
216 		int pos;
217 		uint8_t buf[256], header[4];
218 	struct sap_dsk_tag *tag;
219 		tag = (struct sap_dsk_tag *) floppy_create_tag(floppy, sizeof(struct sap_dsk_tag));
220 
221 		/* default options */
222 		if ( !tag->tracks )
223 		{
224 				tag->tracks = 80;
225 				tag->sector_size = 256;
226 		}
227 
228 		/* create SAP file header */
229 		floppy_image_write( floppy, sap_header, 0, 66 );
230 
231 		for ( track = 0; track < 80; track++ )
232 				for ( sector = 0; sector < 16; sector++ )
233 						tag->sector_pos[track][sector] = 0;
234 
235 		/* create all sectors with valid header and CRC */
236 	memset(buf, 0xe5, 256);
237 		pos = 0x42;
238 		header[0] = (tag->sector_size==128) ? 1 : 0;
239 		header[1] = 0;
240 		for ( track = 0, pos = 0x42; track < tag->tracks; track++ )
241 				for ( sector = 0; sector < 16; sector++, pos += tag->sector_size + 6 ) {
242 						tag->sector_pos[track][sector] = pos;
243 						header[2] = track;
244 						header[3] = sector + 1;
245 						floppy_image_write(floppy, header, pos, 4);
246 						sap_write_indexed_sector( floppy, 0, track, sector, buf, tag->sector_size, 0 );
247 				}
248 
249 		return FLOPPY_ERROR_SUCCESS;
250 }
251 
252 
FLOPPY_CONSTRUCT(sap_dsk_construct)253 static FLOPPY_CONSTRUCT(sap_dsk_construct)
254 {
255 	struct FloppyCallbacks *callbacks;
256 	struct sap_dsk_tag *tag;
257 	int j;
258 	uint8_t fmt;
259 	tag = (struct sap_dsk_tag *) floppy_create_tag(floppy, sizeof(struct sap_dsk_tag));
260 	if (!tag)
261 		return FLOPPY_ERROR_OUTOFMEMORY;
262 
263 		/* guess format */
264 	floppy_image_read(floppy, &fmt, 0x42, 1);
265 	if ( fmt==1 ) tag->sector_size = 128; else tag->sector_size = 256;
266 
267 		/* start with an empty offset table */
268 		tag->tracks = 0;
269 		for ( int i = 0; i < 80; i++ )
270 				for ( j = 0; j < 16; j++ )
271 						tag->sector_pos[i][j] = 0;
272 
273 		/* count tracks & fill sector offset table */
274 	for ( uint64_t i = 0x42; i+4 < floppy_image_size(floppy); i += tag->sector_size + 6 ) // CRC 2 bytes + 4 bytes sector header
275 	{
276 				uint8_t sector, track;
277 		floppy_image_read(floppy, &track, i+2, 1);
278 		floppy_image_read(floppy, &sector, i+3, 1);
279 				if ( track >= 80 || sector < 1 || sector > 16 ) continue;
280 				if ( track > tag->tracks ) tag->tracks = track+1;
281 				tag->sector_pos[track][sector-1] = i;
282 	}
283 	callbacks = floppy_callbacks(floppy);
284 	callbacks->read_sector = sap_read_sector;
285 	callbacks->write_sector = sap_write_sector;
286 	callbacks->read_indexed_sector = sap_read_indexed_sector;
287 	callbacks->write_indexed_sector = sap_write_indexed_sector;
288 	callbacks->get_sector_length = sap_get_sector_length;
289 	callbacks->get_heads_per_disk = sap_get_heads_per_disk;
290 	callbacks->get_tracks_per_disk = sap_get_tracks_per_disk;
291 	callbacks->get_indexed_sector_info = sap_get_indexed_sector_info;
292 	callbacks->post_format = sap_post_format;
293 
294 	return FLOPPY_ERROR_SUCCESS;
295 }
296 
FLOPPY_IDENTIFY(qdd_dsk_identify)297 static FLOPPY_IDENTIFY(qdd_dsk_identify)
298 {
299 	*vote = (floppy_image_size(floppy) == (51200)) ? 100 : 0;
300 	return FLOPPY_ERROR_SUCCESS;
301 }
302 
303 /* fixed interlacing map for QDDs */
304 static int thom_qdd_map[400];
305 
qdd_translate_sector(floppy_image_legacy * floppy,int sector)306 static int qdd_translate_sector(floppy_image_legacy *floppy, int sector)
307 {
308 	return thom_qdd_map[sector-1];
309 }
310 
thom_qdd_compute_map(void)311 static void thom_qdd_compute_map ( void )
312 {
313 	/* this map is hardcoded in the QDD BIOS */
314 	static const int p[6][4] =
315 	{
316 		{ 20,  2, 14,  8 }, { 21, 19, 13,  7 },
317 		{ 22, 18, 12,  6 }, { 23, 17, 11,  5 },
318 		{ 24, 16, 10,  4 }, {  1, 15,  9,  3 }
319 	};
320 	static const int q[4] = { 0, 8, 4, 12 };
321 	int t, s;
322 	for ( t = 0; t < 24; t++ )
323 	{
324 		for ( s = 0; s < 16; s++ )
325 		{
326 			thom_qdd_map[ t*16 + s ] = p[ t/4 ][ s%4 ] * 16 + (s/4) + 4*(t%4);
327 		}
328 	}
329 	for ( s = 0; s < 16; s++ )
330 	{
331 		thom_qdd_map[ 24*16 + s ] = q[ s%4 ] + (s/4);
332 	}
333 }
334 
FLOPPY_CONSTRUCT(qdd_dsk_construct)335 static FLOPPY_CONSTRUCT(qdd_dsk_construct)
336 {
337 	struct basicdsk_geometry geometry;
338 
339 	thom_qdd_compute_map();
340 
341 	memset(&geometry, 0, sizeof(geometry));
342 	geometry.heads = 1;
343 	geometry.first_sector_id = 1;
344 	geometry.sector_length = 128;
345 	geometry.tracks = 1;
346 	geometry.sectors = 400;
347 	geometry.translate_sector =  qdd_translate_sector;
348 	return basicdsk_construct(floppy, &geometry);
349 }
350 
351 
352 /* ----------------------------------------------------------------------- */
353 
354 
fd_identify(floppy_image_legacy * floppy,int * vote,int tracks,int sector_size)355 static floperr_t fd_identify(floppy_image_legacy *floppy, int *vote, int tracks, int sector_size)
356 {
357 	uint64_t expected_size;
358 	expected_size = sector_size;
359 	expected_size *= tracks;
360 	expected_size *= 16; /* secetors */
361 	*vote = (floppy_image_size(floppy) == expected_size) ? 100 : 50;
362 	return FLOPPY_ERROR_SUCCESS;
363 }
364 
fd_construct(floppy_image_legacy * floppy,int tracks,int sector_size)365 static floperr_t fd_construct(floppy_image_legacy *floppy, int tracks, int sector_size)
366 {
367 	struct basicdsk_geometry geometry;
368 	memset(&geometry, 0, sizeof(geometry));
369 	geometry.heads = 1;
370 	geometry.first_sector_id = 1;
371 	geometry.sector_length = sector_size;
372 	geometry.tracks = tracks;
373 	geometry.sectors = 16;
374 	return basicdsk_construct(floppy, &geometry);
375 }
376 
377 
FLOPPY_IDENTIFY(fd_80_256_identify)378 static FLOPPY_IDENTIFY(fd_80_256_identify)
379 {
380 		return fd_identify(floppy, vote, 80, 256);
381 }
382 
FLOPPY_CONSTRUCT(fd_80_256_construct)383 static FLOPPY_CONSTRUCT(fd_80_256_construct)
384 {
385 		return fd_construct(floppy, 80, 256);
386 }
387 
FLOPPY_IDENTIFY(fd_40_256_identify)388 static FLOPPY_IDENTIFY(fd_40_256_identify)
389 {
390 		return fd_identify(floppy, vote, 40, 256);
391 }
392 
FLOPPY_CONSTRUCT(fd_40_256_construct)393 static FLOPPY_CONSTRUCT(fd_40_256_construct)
394 {
395 		return fd_construct(floppy, 40, 256);
396 }
397 
FLOPPY_IDENTIFY(fd_80_128_identify)398 static FLOPPY_IDENTIFY(fd_80_128_identify)
399 {
400 		return fd_identify(floppy, vote, 80, 128);
401 }
402 
FLOPPY_CONSTRUCT(fd_80_128_construct)403 static FLOPPY_CONSTRUCT(fd_80_128_construct)
404 {
405 		return fd_construct(floppy, 80, 128);
406 }
407 
FLOPPY_IDENTIFY(fd_40_128_identify)408 static FLOPPY_IDENTIFY(fd_40_128_identify)
409 {
410 		return fd_identify(floppy, vote, 40, 128);
411 }
412 
FLOPPY_CONSTRUCT(fd_40_128_construct)413 static FLOPPY_CONSTRUCT(fd_40_128_construct)
414 {
415 		return fd_construct(floppy, 40, 128);
416 }
417 
418 
419 /* ----------------------------------------------------------------------- */
420 
421 LEGACY_FLOPPY_OPTIONS_START(thomson)
422 
423 LEGACY_FLOPPY_OPTION(fdmfm2, "fd", "Thomson FD (MFM) 80 tracks disk image (3\"1/2 DD)",
424 					 fd_80_256_identify, fd_80_256_construct, nullptr, nullptr)
425 
426 // Note: no way to distinguish between FD files for 5"1/4 DD and 3"1/2 SD actually, as they have the same size
427 // however, we expect 3"1/2 SD to be rather rare, so, we simply put it after 5"1/4 DD
428 
429 LEGACY_FLOPPY_OPTION(fdmfm, "fd", "Thomson FD (MFM) 40 tracks disk image (5\"1/4 DD)",
430 					 fd_40_256_identify, fd_40_256_construct, nullptr, nullptr)
431 
432 LEGACY_FLOPPY_OPTION(fd2, "fd", "Thomson FD (FM) 80 tracks disk image (3\"1/2 SD)",
433 					 fd_80_128_identify, fd_80_128_construct, nullptr, nullptr)
434 
435 LEGACY_FLOPPY_OPTION(fd, "fd", "Thomson FD (FM) 40 tracks disk image (5\"1/4 SD)",
436 					 fd_40_128_identify, fd_40_128_construct, nullptr, nullptr)
437 
438 LEGACY_FLOPPY_OPTION(sap,"sap", "Thomson SAP floppy disk image",
439 					 sap_dsk_identify, sap_dsk_construct, nullptr, nullptr)
440 
441 LEGACY_FLOPPY_OPTION(qdd,"qd", "Thomson QDD floppy disk image (2\"8 SD)",
442 					 qdd_dsk_identify, qdd_dsk_construct, nullptr, nullptr)
443 
444 LEGACY_FLOPPY_OPTIONS_END
445