1 // license:BSD-3-Clause
2 // copyright-holders:Miodrag Milanovic
3 /*
4  *   D77 and D88 disk images
5  *
6  *
7  *   Header (total size = 0x2b0 bytes):
8  *     0x00 - Disk label
9  *     0x1a - Write protect (bit 3)
10  *     0x1b - 2D/2DD format (bit 3)
11  *     0x1c-1f - image size (should match file size)
12  *     0x20 - offsets for each track (max 164)
13  *
14  *   Sectors (0x110 bytes each, typically)
15  *     0x00 - Sector info
16  *          byte 0 - track number
17  *          byte 1 - side (0 or 1)
18  *          byte 2 - sector number
19  *     0x10 - sector data
20  *
21  *   Images can be concatenated together.
22  *   Sectors can be in any order.
23  *   Tracks are in the order:
24  *          Track 0 side 0
25  *          Track 0 side 1
26  *          Track 1 side 0
27  *          ...
28  *
29  *
30  */
31 
32 	#include <cassert>
33 
34 #include "flopimg.h"
35 #include "imageutl.h"
36 
37 #define D88_HEADER_LEN 0x2b0
38 
39 struct d88_tag
40 {
41 	uint32_t image_size;
42 	uint32_t trackoffset[164];
43 	uint8_t write_protect;
44 	uint8_t disk_type;
45 	uint8_t heads;
46 };
47 
get_d88_tag(floppy_image_legacy * floppy)48 static struct d88_tag *get_d88_tag(floppy_image_legacy *floppy)
49 {
50 	return (d88_tag *)floppy_tag(floppy);
51 }
52 
d88_get_sector_id(floppy_image_legacy * floppy,int head,int track,int sector_index)53 static int d88_get_sector_id(floppy_image_legacy *floppy, int head, int track, int sector_index)
54 {
55 	struct d88_tag* tag = get_d88_tag(floppy);
56 	uint32_t offset;
57 	uint8_t sector_hdr[16];
58 	int x;
59 
60 	offset = tag->trackoffset[(track*tag->heads)+head];
61 
62 	if(offset == 0)
63 		return 0;
64 
65 	floppy_image_read(floppy,sector_hdr,offset,16);
66 
67 	// get to sector indexed
68 	x=0;
69 	while(x<sector_index)
70 	{
71 		offset += ((sector_hdr[15] << 8) | sector_hdr[14]);
72 		offset += 16;
73 		floppy_image_read(floppy,sector_hdr,offset,16);
74 		x++;
75 	}
76 
77 	return sector_hdr[2];
78 }
79 
d88_get_tracks_per_disk(floppy_image_legacy * floppy)80 static int d88_get_tracks_per_disk(floppy_image_legacy *floppy)
81 {
82 	return 82;  // 82 tracks per side
83 }
84 
d88_get_heads_per_disk(floppy_image_legacy * floppy)85 static int d88_get_heads_per_disk(floppy_image_legacy *floppy)
86 {
87 	struct d88_tag* tag = get_d88_tag(floppy);
88 	return tag->heads;
89 }
90 
d88_get_sectors_per_track(floppy_image_legacy * floppy,int head,int track)91 static int d88_get_sectors_per_track(floppy_image_legacy *floppy, int head, int track)
92 {
93 	struct d88_tag* tag = get_d88_tag(floppy);
94 	uint32_t offset;
95 	uint8_t sector_hdr[16];
96 
97 	offset = tag->trackoffset[(track*tag->heads)+head];
98 
99 	floppy_image_read(floppy,sector_hdr,offset,16);
100 
101 	return sector_hdr[4];
102 }
103 
d88_get_sector_length(floppy_image_legacy * floppy,int head,int track,int sector,uint32_t * sector_length)104 static floperr_t d88_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
105 {
106 	struct d88_tag* tag = get_d88_tag(floppy);
107 	uint32_t offset;
108 	uint8_t sector_hdr[16];
109 	uint32_t len;
110 	int count,secs;
111 
112 	offset = tag->trackoffset[(track*tag->heads)+head];
113 
114 	floppy_image_read(floppy,sector_hdr,offset,16);
115 	secs = sector_hdr[4];
116 
117 	for(count=0;count<secs;count++)
118 	{
119 		floppy_image_read(floppy,sector_hdr,offset,16);
120 		if(sector == sector_hdr[2])
121 		{
122 			if(sector_length)
123 				*sector_length = (sector_hdr[15] << 8) | sector_hdr[14];
124 			return FLOPPY_ERROR_SUCCESS;
125 		}
126 		len = (sector_hdr[15] << 8) | sector_hdr[14];
127 		len += 16;
128 		offset += len;
129 	}
130 
131 	return FLOPPY_ERROR_SEEKERROR;
132 }
133 
d88_read_track(floppy_image_legacy * floppy,int head,int track,uint64_t offset,void * buffer,size_t buflen)134 static floperr_t d88_read_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen)
135 {
136 //  floperr_t err;
137 
138 	return FLOPPY_ERROR_UNSUPPORTED;
139 }
140 
d88_get_sector_offset(floppy_image_legacy * floppy,int head,int track,int sector)141 static uint32_t d88_get_sector_offset(floppy_image_legacy* floppy, int head, int track, int sector)
142 {
143 	struct d88_tag* tag = get_d88_tag(floppy);
144 	uint32_t offset = 0;
145 	uint8_t sector_hdr[16];
146 	uint32_t len;
147 	uint32_t secs;
148 	int count;
149 
150 	// get offset of the beginning of the track
151 	offset = tag->trackoffset[(track*tag->heads)+head];
152 
153 	floppy_image_read(floppy,sector_hdr,offset,16);
154 	secs = sector_hdr[4];
155 
156 	for(count=0;count<secs;count++)
157 	{
158 		floppy_image_read(floppy,sector_hdr,offset,16);
159 		if(sector == sector_hdr[2])
160 		{
161 			LOG_FORMATS("d88_get_sector_offset - track %i, side %i, sector %02x, returns %08x\n",track,head,sector,offset+16);
162 			return offset + 16;
163 		}
164 		len = (sector_hdr[15] << 8) | sector_hdr[14];
165 		len += 16;
166 		offset += len;
167 	}
168 	LOG_FORMATS("d88_get_sector_offset - track %i, side %i, sector %02x, not found\n",track,head,sector);
169 	return 0;
170 }
171 
d88_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)172 static floperr_t d88_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)
173 {
174 	struct d88_tag* tag = get_d88_tag(floppy);
175 	uint32_t offset;
176 	uint8_t sector_hdr[16];
177 	int x;
178 
179 	offset = tag->trackoffset[(track*tag->heads)+head];
180 
181 	if(offset == 0)
182 		return FLOPPY_ERROR_SEEKERROR;
183 
184 
185 	floppy_image_read(floppy,sector_hdr,offset,16);
186 
187 	if(sector_index >= sector_hdr[4])
188 		return FLOPPY_ERROR_SEEKERROR;
189 
190 	// get to sector indexed
191 	x=0;
192 	while(x<sector_index)
193 	{
194 		offset += ((sector_hdr[15] << 8) | sector_hdr[14]);
195 		offset += 16;
196 		floppy_image_read(floppy,sector_hdr,offset,16);
197 		x++;
198 	}
199 
200 	if(offset > tag->image_size || offset == 0)
201 		return FLOPPY_ERROR_SEEKERROR;
202 
203 	if(sector_length)
204 		*sector_length = (sector_hdr[15] << 8) | sector_hdr[14];
205 	if(cylinder)
206 		*cylinder = sector_hdr[0];
207 	if(side)
208 		*side = sector_hdr[1];
209 	if(sector)
210 		*sector = sector_hdr[2];
211 	if(flags)
212 		*flags = 0;
213 
214 	return FLOPPY_ERROR_SUCCESS;
215 }
216 
d88_read_sector(floppy_image_legacy * floppy,int head,int track,int sector,void * buffer,size_t buflen)217 static floperr_t d88_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
218 {
219 	uint64_t offset;
220 	uint32_t sector_length;
221 
222 	offset = d88_get_sector_offset(floppy,head,track,sector);
223 
224 	if(d88_get_sector_length(floppy,head,track,sector,&sector_length) != FLOPPY_ERROR_SUCCESS)
225 		return FLOPPY_ERROR_SEEKERROR;
226 
227 	if(offset == 0)
228 		return FLOPPY_ERROR_SEEKERROR;
229 
230 	if(buflen > sector_length)
231 		return FLOPPY_ERROR_INTERNAL;
232 
233 	floppy_image_read(floppy,buffer,offset,sector_length);
234 
235 	return FLOPPY_ERROR_SUCCESS;
236 }
237 
d88_read_indexed_sector(floppy_image_legacy * floppy,int head,int track,int sector,void * buffer,size_t buffer_len)238 static floperr_t d88_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buffer_len)
239 {
240 	int sec;
241 
242 	sec = d88_get_sector_id(floppy,head,track,sector);
243 	return d88_read_sector(floppy,head,track,sec,buffer,buffer_len);
244 }
245 
d88_write_sector(floppy_image_legacy * floppy,int head,int track,int sector,const void * buffer,size_t buflen,int ddam)246 static floperr_t d88_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
247 {
248 	uint64_t offset;
249 	uint32_t sector_length;
250 
251 	offset = d88_get_sector_offset(floppy,head,track,sector);
252 
253 	if(d88_get_sector_length(floppy,head,track,sector,&sector_length) != FLOPPY_ERROR_SUCCESS)
254 		return FLOPPY_ERROR_SEEKERROR;
255 
256 	if(offset == 0)
257 		return FLOPPY_ERROR_SEEKERROR;
258 
259 	if(buflen > sector_length)
260 		return FLOPPY_ERROR_INTERNAL;
261 
262 	floppy_image_write(floppy,buffer,offset,sector_length);
263 
264 	return FLOPPY_ERROR_SUCCESS;
265 }
266 
d88_write_indexed_sector(floppy_image_legacy * floppy,int head,int track,int sector,const void * buffer,size_t buflen,int ddam)267 static floperr_t d88_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
268 {
269 	int sec;
270 
271 	sec = d88_get_sector_id(floppy,head,track,sector);
272 	return d88_write_sector(floppy,head,track,sec,buffer,buflen,ddam);
273 }
274 
d88_get_header(floppy_image_legacy * floppy,uint32_t * size,uint8_t * prot,uint8_t * type,uint32_t * offsets)275 static void d88_get_header(floppy_image_legacy* floppy,uint32_t* size, uint8_t* prot, uint8_t* type, uint32_t* offsets)
276 {
277 	uint8_t header[D88_HEADER_LEN];
278 	int x,s;
279 
280 	floppy_image_read(floppy,header,0,D88_HEADER_LEN);
281 
282 #ifdef SPOT_DUPLICATES
283 		// there exist many .d88 files with same data and different headers and
284 		// this allows to spot duplicates, making easier to debug softlists.
285 		uint32_t temp_size = floppy_image_size(floppy);
286 		uint8_t tmp_copy[temp_size - D88_HEADER_LEN];
287 		floppy_image_read(floppy,tmp_copy,D88_HEADER_LEN,temp_size - D88_HEADER_LEN);
288 		printf("CRC16: %d\n", ccitt_crc16(0xffff, tmp_copy, temp_size - D88_HEADER_LEN));
289 #endif
290 
291 	if(prot)
292 		*prot = header[0x1a];
293 	if(type)
294 		*type = header[0x1b];
295 	if(size)
296 	{
297 		s = 0;
298 		s |= header[0x1f] << 24;
299 		s |= header[0x1e] << 16;
300 		s |= header[0x1d] << 8;
301 		s |= header[0x1c];
302 		*size = s;
303 	}
304 	if(offsets)
305 	{
306 		for(x=0;x<164;x++)
307 		{
308 			s = 0;
309 			s |= header[0x23 + (x*4)] << 24;
310 			s |= header[0x22 + (x*4)] << 16;
311 			s |= header[0x21 + (x*4)] << 8;
312 			s |= header[0x20 + (x*4)];
313 			*(offsets+x) = s;
314 		}
315 	}
316 }
317 
FLOPPY_IDENTIFY(d88_dsk_identify)318 FLOPPY_IDENTIFY(d88_dsk_identify)
319 {
320 	uint32_t size;
321 
322 	d88_get_header(floppy,&size,nullptr,nullptr,nullptr);
323 
324 	if(floppy_image_size(floppy) == size)
325 	{
326 		*vote = 100;
327 	}
328 	else
329 	{
330 		*vote = 0;
331 	}
332 	return FLOPPY_ERROR_SUCCESS;
333 }
334 
FLOPPY_CONSTRUCT(d88_dsk_construct)335 FLOPPY_CONSTRUCT(d88_dsk_construct)
336 {
337 	struct FloppyCallbacks *callbacks;
338 	struct d88_tag *tag;
339 	uint32_t size;
340 	uint8_t prot,type = 0;
341 	uint32_t offs[164];
342 	int x;
343 
344 	if(params)
345 	{
346 		// create
347 		return FLOPPY_ERROR_UNSUPPORTED;
348 	}
349 	else
350 	{
351 		// load
352 		d88_get_header(floppy,&size,&prot,&type,offs);
353 	}
354 
355 	tag = (d88_tag *)floppy_create_tag(floppy,sizeof(struct d88_tag));
356 	if (!tag)
357 		return FLOPPY_ERROR_OUTOFMEMORY;
358 
359 	tag->write_protect = prot;
360 	tag->disk_type = type;
361 	tag->heads = 2;
362 	if (tag->disk_type==0x30 || tag->disk_type==0x40) tag->heads = 1;
363 
364 	tag->image_size = size;
365 	for(x=0;x<164;x++)
366 		tag->trackoffset[x] = offs[x];
367 
368 	callbacks = floppy_callbacks(floppy);
369 	callbacks->read_track = d88_read_track;
370 	callbacks->get_heads_per_disk = d88_get_heads_per_disk;
371 	callbacks->get_tracks_per_disk = d88_get_tracks_per_disk;
372 	callbacks->get_sector_length = d88_get_sector_length;
373 	callbacks->read_sector = d88_read_sector;
374 	callbacks->read_indexed_sector = d88_read_indexed_sector;
375 	callbacks->write_sector = d88_write_sector;
376 	callbacks->write_indexed_sector = d88_write_indexed_sector;
377 	callbacks->get_indexed_sector_info = d88_get_indexed_sector_info;
378 	callbacks->get_sectors_per_track = d88_get_sectors_per_track;
379 
380 
381 	return FLOPPY_ERROR_SUCCESS;
382 }
383 
384 
385 
386 // license:BSD-3-Clause
387 // copyright-holders:Olivier Galibert
388 /*********************************************************************
389 
390     formats/d88_dsk.h
391 
392     D88 disk images
393 
394 *********************************************************************/
395 
396 #include "d88_dsk.h"
397 
d88_format()398 d88_format::d88_format()
399 {
400 }
401 
name() const402 const char *d88_format::name() const
403 {
404 	return "d88";
405 }
406 
description() const407 const char *d88_format::description() const
408 {
409 	return "D88 disk image";
410 }
411 
extensions() const412 const char *d88_format::extensions() const
413 {
414 	return "d77,d88,1dd";
415 }
416 
identify(io_generic * io,uint32_t form_factor)417 int d88_format::identify(io_generic *io, uint32_t form_factor)
418 {
419 	uint64_t size = io_generic_size(io);
420 	uint8_t h[32];
421 
422 	io_generic_read(io, h, 0, 32);
423 	if((little_endianize_int32(*(uint32_t *)(h+0x1c)) == size) &&
424 		(h[0x1b] == 0x00 || h[0x1b] == 0x10 || h[0x1b] == 0x20 || h[0x1b] == 0x30 || h[0x1b] == 0x40))
425 		return 100;
426 
427 	return 0;
428 }
429 
load(io_generic * io,uint32_t form_factor,floppy_image * image)430 bool d88_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
431 {
432 	uint8_t h[32];
433 
434 	io_generic_read(io, h, 0, 32);
435 
436 	int cell_count = 0;
437 	int track_count = 0;
438 	int head_count = 0;
439 	switch(h[0x1b]) {
440 	case 0x00:
441 		cell_count = 100000;
442 		track_count = 42;
443 		head_count = 2;
444 		image->set_variant(floppy_image::DSDD);
445 		break;
446 
447 	case 0x10:
448 		cell_count = 100000;
449 		track_count = 82;
450 		head_count = 2;
451 		image->set_variant(floppy_image::DSQD);
452 		break;
453 
454 	case 0x20:
455 		cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666;
456 		track_count = 82;
457 		head_count = 2;
458 		image->set_variant(floppy_image::DSHD);
459 		break;
460 
461 	case 0x30:
462 		cell_count = 100000;
463 		track_count = 42;
464 		head_count = 1;
465 		image->set_variant(floppy_image::SSDD);
466 		break;
467 
468 	case 0x40:
469 		cell_count = 100000;
470 		track_count = 82;
471 		head_count = 1;
472 		image->set_variant(floppy_image::SSQD);
473 		break;
474 	}
475 
476 	if(!head_count)
477 		return false;
478 
479 	uint32_t track_pos[164];
480 	io_generic_read(io, track_pos, 32, 164*4);
481 
482 	uint64_t file_size = io_generic_size(io);
483 
484 	for(int track=0; track < track_count; track++)
485 		for(int head=0; head < head_count; head++) {
486 			int pos = little_endianize_int32(track_pos[track * head_count + head]);
487 			if(!pos)
488 				continue;
489 			desc_pc_sector sects[256];
490 			uint8_t sect_data[65536];
491 			int sdatapos = 0;
492 			int sector_count = 1;
493 			for(int i=0; i<sector_count; i++) {
494 
495 				if (pos + 16 > file_size)
496 					return true;
497 
498 				uint8_t hs[16];
499 				io_generic_read(io, hs, pos, 16);
500 				pos += 16;
501 
502 				uint16_t size = little_endianize_int16(*(uint16_t *)(hs+14));
503 
504 				if(pos + size > file_size)
505 					return true;
506 
507 				if(i == 0) {
508 					sector_count = little_endianize_int16(*(uint16_t *)(hs+4));
509 					// Support broken vfman converter
510 					if(sector_count == 0x1000)
511 						sector_count = 0x10;
512 				}
513 
514 				sects[i].track       = hs[0];
515 				sects[i].head        = hs[1];
516 				sects[i].sector      = hs[2];
517 				sects[i].size        = hs[3];
518 				sects[i].actual_size = size;
519 				sects[i].deleted     = hs[7] != 0;
520 				sects[i].bad_crc     = hs[8] == 0xb0;  // according to hxc
521 
522 				if(size) {
523 					sects[i].data    = sect_data + sdatapos;
524 					io_generic_read(io, sects[i].data, pos, size);
525 					pos += size;
526 					sdatapos += size;
527 
528 				} else
529 					sects[i].data    = nullptr;
530 			}
531 
532 			build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
533 		}
534 
535 	return true;
536 }
537 
538 
save(io_generic * io,floppy_image * image)539 bool d88_format::save(io_generic *io, floppy_image *image)
540 {
541 	return false;
542 }
543 
supports_save() const544 bool d88_format::supports_save() const
545 {
546 	return false;
547 }
548 
549 const floppy_format_type FLOPPY_D88_FORMAT = &floppy_image_format_creator<d88_format>;
550