// license:BSD-3-Clause // copyright-holders:Miodrag Milanovic /* * D77 and D88 disk images * * * Header (total size = 0x2b0 bytes): * 0x00 - Disk label * 0x1a - Write protect (bit 3) * 0x1b - 2D/2DD format (bit 3) * 0x1c-1f - image size (should match file size) * 0x20 - offsets for each track (max 164) * * Sectors (0x110 bytes each, typically) * 0x00 - Sector info * byte 0 - track number * byte 1 - side (0 or 1) * byte 2 - sector number * 0x10 - sector data * * Images can be concatenated together. * Sectors can be in any order. * Tracks are in the order: * Track 0 side 0 * Track 0 side 1 * Track 1 side 0 * ... * * */ #include #include "flopimg.h" #include "imageutl.h" #define D88_HEADER_LEN 0x2b0 struct d88_tag { uint32_t image_size; uint32_t trackoffset[164]; uint8_t write_protect; uint8_t disk_type; uint8_t heads; }; static struct d88_tag *get_d88_tag(floppy_image_legacy *floppy) { return (d88_tag *)floppy_tag(floppy); } static int d88_get_sector_id(floppy_image_legacy *floppy, int head, int track, int sector_index) { struct d88_tag* tag = get_d88_tag(floppy); uint32_t offset; uint8_t sector_hdr[16]; int x; offset = tag->trackoffset[(track*tag->heads)+head]; if(offset == 0) return 0; floppy_image_read(floppy,sector_hdr,offset,16); // get to sector indexed x=0; while(xheads; } static int d88_get_sectors_per_track(floppy_image_legacy *floppy, int head, int track) { struct d88_tag* tag = get_d88_tag(floppy); uint32_t offset; uint8_t sector_hdr[16]; offset = tag->trackoffset[(track*tag->heads)+head]; floppy_image_read(floppy,sector_hdr,offset,16); return sector_hdr[4]; } static floperr_t d88_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length) { struct d88_tag* tag = get_d88_tag(floppy); uint32_t offset; uint8_t sector_hdr[16]; uint32_t len; int count,secs; offset = tag->trackoffset[(track*tag->heads)+head]; floppy_image_read(floppy,sector_hdr,offset,16); secs = sector_hdr[4]; for(count=0;counttrackoffset[(track*tag->heads)+head]; floppy_image_read(floppy,sector_hdr,offset,16); secs = sector_hdr[4]; for(count=0;counttrackoffset[(track*tag->heads)+head]; if(offset == 0) return FLOPPY_ERROR_SEEKERROR; floppy_image_read(floppy,sector_hdr,offset,16); if(sector_index >= sector_hdr[4]) return FLOPPY_ERROR_SEEKERROR; // get to sector indexed x=0; while(x tag->image_size || offset == 0) return FLOPPY_ERROR_SEEKERROR; if(sector_length) *sector_length = (sector_hdr[15] << 8) | sector_hdr[14]; if(cylinder) *cylinder = sector_hdr[0]; if(side) *side = sector_hdr[1]; if(sector) *sector = sector_hdr[2]; if(flags) *flags = 0; return FLOPPY_ERROR_SUCCESS; } static floperr_t d88_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen) { uint64_t offset; uint32_t sector_length; offset = d88_get_sector_offset(floppy,head,track,sector); if(d88_get_sector_length(floppy,head,track,sector,§or_length) != FLOPPY_ERROR_SUCCESS) return FLOPPY_ERROR_SEEKERROR; if(offset == 0) return FLOPPY_ERROR_SEEKERROR; if(buflen > sector_length) return FLOPPY_ERROR_INTERNAL; floppy_image_read(floppy,buffer,offset,sector_length); return FLOPPY_ERROR_SUCCESS; } static floperr_t d88_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buffer_len) { int sec; sec = d88_get_sector_id(floppy,head,track,sector); return d88_read_sector(floppy,head,track,sec,buffer,buffer_len); } static floperr_t d88_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) { uint64_t offset; uint32_t sector_length; offset = d88_get_sector_offset(floppy,head,track,sector); if(d88_get_sector_length(floppy,head,track,sector,§or_length) != FLOPPY_ERROR_SUCCESS) return FLOPPY_ERROR_SEEKERROR; if(offset == 0) return FLOPPY_ERROR_SEEKERROR; if(buflen > sector_length) return FLOPPY_ERROR_INTERNAL; floppy_image_write(floppy,buffer,offset,sector_length); return FLOPPY_ERROR_SUCCESS; } 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) { int sec; sec = d88_get_sector_id(floppy,head,track,sector); return d88_write_sector(floppy,head,track,sec,buffer,buflen,ddam); } static void d88_get_header(floppy_image_legacy* floppy,uint32_t* size, uint8_t* prot, uint8_t* type, uint32_t* offsets) { uint8_t header[D88_HEADER_LEN]; int x,s; floppy_image_read(floppy,header,0,D88_HEADER_LEN); #ifdef SPOT_DUPLICATES // there exist many .d88 files with same data and different headers and // this allows to spot duplicates, making easier to debug softlists. uint32_t temp_size = floppy_image_size(floppy); uint8_t tmp_copy[temp_size - D88_HEADER_LEN]; floppy_image_read(floppy,tmp_copy,D88_HEADER_LEN,temp_size - D88_HEADER_LEN); printf("CRC16: %d\n", ccitt_crc16(0xffff, tmp_copy, temp_size - D88_HEADER_LEN)); #endif if(prot) *prot = header[0x1a]; if(type) *type = header[0x1b]; if(size) { s = 0; s |= header[0x1f] << 24; s |= header[0x1e] << 16; s |= header[0x1d] << 8; s |= header[0x1c]; *size = s; } if(offsets) { for(x=0;x<164;x++) { s = 0; s |= header[0x23 + (x*4)] << 24; s |= header[0x22 + (x*4)] << 16; s |= header[0x21 + (x*4)] << 8; s |= header[0x20 + (x*4)]; *(offsets+x) = s; } } } FLOPPY_IDENTIFY(d88_dsk_identify) { uint32_t size; d88_get_header(floppy,&size,nullptr,nullptr,nullptr); if(floppy_image_size(floppy) == size) { *vote = 100; } else { *vote = 0; } return FLOPPY_ERROR_SUCCESS; } FLOPPY_CONSTRUCT(d88_dsk_construct) { struct FloppyCallbacks *callbacks; struct d88_tag *tag; uint32_t size; uint8_t prot,type = 0; uint32_t offs[164]; int x; if(params) { // create return FLOPPY_ERROR_UNSUPPORTED; } else { // load d88_get_header(floppy,&size,&prot,&type,offs); } tag = (d88_tag *)floppy_create_tag(floppy,sizeof(struct d88_tag)); if (!tag) return FLOPPY_ERROR_OUTOFMEMORY; tag->write_protect = prot; tag->disk_type = type; tag->heads = 2; if (tag->disk_type==0x30 || tag->disk_type==0x40) tag->heads = 1; tag->image_size = size; for(x=0;x<164;x++) tag->trackoffset[x] = offs[x]; callbacks = floppy_callbacks(floppy); callbacks->read_track = d88_read_track; callbacks->get_heads_per_disk = d88_get_heads_per_disk; callbacks->get_tracks_per_disk = d88_get_tracks_per_disk; callbacks->get_sector_length = d88_get_sector_length; callbacks->read_sector = d88_read_sector; callbacks->read_indexed_sector = d88_read_indexed_sector; callbacks->write_sector = d88_write_sector; callbacks->write_indexed_sector = d88_write_indexed_sector; callbacks->get_indexed_sector_info = d88_get_indexed_sector_info; callbacks->get_sectors_per_track = d88_get_sectors_per_track; return FLOPPY_ERROR_SUCCESS; } // license:BSD-3-Clause // copyright-holders:Olivier Galibert /********************************************************************* formats/d88_dsk.h D88 disk images *********************************************************************/ #include "d88_dsk.h" d88_format::d88_format() { } const char *d88_format::name() const { return "d88"; } const char *d88_format::description() const { return "D88 disk image"; } const char *d88_format::extensions() const { return "d77,d88,1dd"; } int d88_format::identify(io_generic *io, uint32_t form_factor) { uint64_t size = io_generic_size(io); uint8_t h[32]; io_generic_read(io, h, 0, 32); if((little_endianize_int32(*(uint32_t *)(h+0x1c)) == size) && (h[0x1b] == 0x00 || h[0x1b] == 0x10 || h[0x1b] == 0x20 || h[0x1b] == 0x30 || h[0x1b] == 0x40)) return 100; return 0; } bool d88_format::load(io_generic *io, uint32_t form_factor, floppy_image *image) { uint8_t h[32]; io_generic_read(io, h, 0, 32); int cell_count = 0; int track_count = 0; int head_count = 0; switch(h[0x1b]) { case 0x00: cell_count = 100000; track_count = 42; head_count = 2; image->set_variant(floppy_image::DSDD); break; case 0x10: cell_count = 100000; track_count = 82; head_count = 2; image->set_variant(floppy_image::DSQD); break; case 0x20: cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666; track_count = 82; head_count = 2; image->set_variant(floppy_image::DSHD); break; case 0x30: cell_count = 100000; track_count = 42; head_count = 1; image->set_variant(floppy_image::SSDD); break; case 0x40: cell_count = 100000; track_count = 82; head_count = 1; image->set_variant(floppy_image::SSQD); break; } if(!head_count) return false; uint32_t track_pos[164]; io_generic_read(io, track_pos, 32, 164*4); uint64_t file_size = io_generic_size(io); for(int track=0; track < track_count; track++) for(int head=0; head < head_count; head++) { int pos = little_endianize_int32(track_pos[track * head_count + head]); if(!pos) continue; desc_pc_sector sects[256]; uint8_t sect_data[65536]; int sdatapos = 0; int sector_count = 1; for(int i=0; i file_size) return true; uint8_t hs[16]; io_generic_read(io, hs, pos, 16); pos += 16; uint16_t size = little_endianize_int16(*(uint16_t *)(hs+14)); if(pos + size > file_size) return true; if(i == 0) { sector_count = little_endianize_int16(*(uint16_t *)(hs+4)); // Support broken vfman converter if(sector_count == 0x1000) sector_count = 0x10; } sects[i].track = hs[0]; sects[i].head = hs[1]; sects[i].sector = hs[2]; sects[i].size = hs[3]; sects[i].actual_size = size; sects[i].deleted = hs[7] != 0; sects[i].bad_crc = hs[8] == 0xb0; // according to hxc if(size) { sects[i].data = sect_data + sdatapos; io_generic_read(io, sects[i].data, pos, size); pos += size; sdatapos += size; } else sects[i].data = nullptr; } build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size)); } return true; } bool d88_format::save(io_generic *io, floppy_image *image) { return false; } bool d88_format::supports_save() const { return false; } const floppy_format_type FLOPPY_D88_FORMAT = &floppy_image_format_creator;