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,§or_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,§or_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