/********************************************************************* formats/coco_dsk.c Tandy Color Computer / Dragon disk images *********************************************************************/ #include #include #include #include "formats/coco_dsk.h" #include "formats/basicdsk.h" #include "imageutl.h" #include "coretmpl.h" /* ----------------------------------------------------------------------- * JVC (Jeff Vavasour CoCo) format * * Used by Jeff Vavasour's CoCo Emulators * * Documentation taken from Tim Linder's web site: * http://home.netcom.com/~tlindner/JVC.html * * A. Header length * The header length is determined by the file length modulo 256: * headerSize = fileLength % 256; * This means that the header is variable length and the minimum size * is zero bytes, and the maximum size of 255 bytes. * * B. Header * Here is a description of the header bytes: * Byte Offset Description Default * ----------- ----------------- ------- * 0 Sectors per track 18 * 1 Side count 1 * 2 Sector size code 1 * 3 First sector ID 1 * 4 Sector attribute flag 0 * * If the sector attribute flag is zero then the track count is determined * by the formula: * * (fileLength - headerSize) / (sectorsPerTrack * (128 << * sectorSizeCode)) / sideCount * * If the sector attribute flag is non zero then the track count is * determined by the more complex formula: * * (fileLength - headerSize) / (sectorsPerTrack * ((128 << * sectorSizeCode) + 1) ) / sideCount * * If the length of the header is to short to contain the geometry desired, * then the default values are assumed. If the header length is zero the all * of the geometry is assumed. When creating disk images it is desirable to * make the header length as short as possible. The header should only be * used to deviate from the default values. * * The sector data begins immediately after the header. If the header length * is zero then the sector data is at the beginning file. * * C. Sectors per track * This is the number of sectors per track (ones based). A value of 18 * means there are 18 sectors per track * * D. Side Count * This is the number of sides in the disk image. Values of 1 or 2 are * acceptable. If there are two sides then the tracks are interleaved. * The first track in the image file is track zero side 1, the second * track in the image file is track zero side 2. * * E. Sector size * The is the same value that is stored in the wd179x ID field to * determine sector size: * * 0x00 128 bytes * 0x01 256 bytes * 0x02 512 bytes * 0x03 1024 bytes * * Other values are undefined. Every sector in the disk image must be the * same size. * * F. First sector ID * This determines the first sector ID for each track. Each successive * sector adds one to the previous ID. If the first sector ID is 1, then * the second sector has an ID of 2, and the third has an ID of 3. * * G. Sector Attribute Flag * If this byte is non zero, then each sector contains an additional * byte prepended to the sector data. If the attribute flag is zero then * there are no extra bytes in front of the sector data. * * H. Sector attribute byte * This byte is put at the beginning of every sector if the header flag * is turned on. The information this byte contains is the same as the * status register (of the wd179x) would contain when a 'Read Sector' * command was issued. The bit fields are defined as: * * Bit position: * --------------- * 7 6 5 4 3 2 1 0 * | | | | | | | | * | | | | | | | +--- Not used. Set to zero. * | | | | | | +----- Not used. Set to zero. * | | | | | +------- Not used. Set to zero. * | | | | +--------- Set on CRC error. * | | | +----------- Set if sector not found. * | | +------------- Record type: 1 - Deleted Data Mark, 0 - Data Mark. * | +--------------- Not Used. Set to zero. * +----------------- Not Used. Set to zero. * * ----------------------------------------------------------------------- */ static int coco_jvc_decode_header(floppy_image_legacy *floppy, UINT64 size, struct basicdsk_geometry *geometry) { UINT8 header[256]; UINT8 sector_attribute_flag; UINT16 physical_bytes_per_sector; UINT32 header_size, file_size; struct basicdsk_geometry dummy_geometry; if (geometry) memset(geometry, 0, sizeof(*geometry)); else geometry = &dummy_geometry; if (size > 0xFFFFFFFF) return -1; file_size = (UINT32) size; /* read the header */ header_size = (UINT32) file_size % 0x100; floppy_image_read(floppy, header, 0, header_size); geometry->offset = header_size; /* byte offset 0 - sectors per track */ geometry->sectors = (header_size > 0) ? header[0] : 18; if (geometry->sectors <= 0) return -1; /* byte offset 1 - side count */ geometry->heads = (header_size > 1) ? header[1] : 1; if (geometry->heads <= 0) return -1; /* byte offset 2 - sector size code */ geometry->sector_length = 128 << ((header_size > 2) ? header[2] : 1); if (geometry->sector_length <= 0) return -1; /* byte offset 3 - first sector ID */ geometry->first_sector_id = (header_size > 3) ? header[3] : 1; /* byte offset 4 - sector attribute flag */ sector_attribute_flag = (header_size > 4) ? header[4] : 0; if (sector_attribute_flag != 0) return -1; /* we do not support sector attribute flags */ physical_bytes_per_sector = geometry->sector_length; if (sector_attribute_flag) physical_bytes_per_sector++; geometry->tracks = (file_size - header_size) / geometry->sectors / geometry->heads / physical_bytes_per_sector; /* do we have an oddball size? reject this file if not */ if ((file_size - header_size) % physical_bytes_per_sector) return -1; /* minimum of 35 tracks; support degenerate JVC files */ if (geometry->tracks < 35) geometry->tracks = 35; return 0; } static FLOPPY_IDENTIFY(coco_jvc_identify) { UINT64 size; size = floppy_image_size(floppy); *vote = coco_jvc_decode_header(floppy, size, NULL) ? 0 : 100; return FLOPPY_ERROR_SUCCESS; } static FLOPPY_CONSTRUCT(coco_jvc_construct) { struct basicdsk_geometry geometry; UINT8 header[5]; size_t header_size; if (params) { /* create */ memset(&geometry, 0, sizeof(geometry)); geometry.heads = option_resolution_lookup_int(params, PARAM_HEADS); geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); geometry.sectors = option_resolution_lookup_int(params, PARAM_SECTORS); geometry.first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); geometry.sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); header[0] = (UINT8) geometry.sectors; header[1] = (UINT8) geometry.heads; header[2] = (UINT8) compute_log2(geometry.sector_length) - 7; header[3] = (UINT8) geometry.first_sector_id; header[4] = 0; /* now that we have the header computed, figure out the header size */ header_size = 0; if (header[0] != 18) header_size = 1; if (header[1] != 1) header_size = 2; if (header[2] != 1) header_size = 3; if (header[3] != 1) header_size = 4; if (header[4] != 0) header_size = 5; geometry.offset = header_size; floppy_image_write(floppy, header, 0, header_size); } else { /* load */ if (coco_jvc_decode_header(floppy, floppy_image_size(floppy), &geometry)) return FLOPPY_ERROR_INVALIDIMAGE; } return basicdsk_construct(floppy, &geometry); } /* ----------------------------------------------------------------------- * OS-9 file format * * This file format is largely a hack because there are a large amount of * disk images that do not have geometry image separate from the disk image * itself. So we support OS-9 images with are simply basic disks whose * geometry is determined by the disk image. * * OS-9 images identified by an LSN; which are simply blocks of 256 bytes * * LSN0 * Byte size use * $00 3 sectors on disk * $03 1 track size in sectors * $04 2 bytes in allocation bit map; typically 1bit/sector so for * 35 tracks of 18 sectors each that's $4E (single sided disk) * 40 tracks per side, 18 sectors each = $B4 * $06 2 sectors per bit in allocation map; normally 1 * $08 3 LSN of root directory; normally 2 but depends on size of * $04 value * $0B 2 owner's user number; normally 0 * $0D 1 disk attributes; normally $FF * $0E 2 psuedo random number for identification * $10 1 disk format; typical is 3 * %00000001 0=single side 1=double side * %00000010 0=single density (non Coco) 1=double density * %00000100 0=48tracks/inch 1=96tracks/inch * $11 2 sectors per track; normal is $12 skip several not needed * for Format * $1A 5 date of creation Y:M:D:H:M * $1F 32 ASCII name of disk, last letter has $80 added to it, * the full 32 bytes do not need to be used. * * Allocation bit map, fill with zeros and set bits from low to high as * sectors are used. So, for a fresh disk sectors LSN0,LSN1,LSN2, and LSN3 * will be in use so the first byte will be $FF $C0 and all others * in the map are $00 * * Root directory LSN2 * Byte size use * $00 1 attributes will be $BF * $01 2 owners ID will be $0000 * $03 5 date last modified will be creation date Y:M:D:H:M * $08 1 link count; set to $02 * $09 4 file size in bytes, set to $40 * $0D 3 date created Y:M:D * $10 3 block LSN set to current sector number+1 ie $03 in this case * $13 2 size in sectors of directory block, set to $07 * All other bytes in sector set to $00 * LSN3 first sector of directory with names * Fill sector with all $00 and then set listed bytes * $00 2 value $2EAE which is .. with last byte+$80 * $1F 1 value $02 LSN for start of this directory as there is * none higher in tree * $20 1 value $AE which is . with $80 added * $3F 1 value $02 LSN for start of this directory * ----------------------------------------------------------------------- */ static floperr_t coco_os9_readheader(floppy_image_legacy *floppy, struct basicdsk_geometry *geometry) { UINT8 header[0x20]; int total_sectors; floppy_image_read(floppy, header, 0, sizeof(header)); total_sectors = (header[0x00] << 16) | (header[0x01] << 8) | header[0x02]; memset(geometry, 0, sizeof(*geometry)); geometry->first_sector_id = 1; geometry->sector_length = 256; geometry->sectors = (header[0x11] << 8) + header[0x12]; geometry->heads = (header[0x10] & 0x01) ? 2 : 1; if (!geometry->sectors) return FLOPPY_ERROR_INVALIDIMAGE; geometry->tracks = total_sectors / geometry->sectors / geometry->heads; if (total_sectors != geometry->tracks * geometry->sectors * geometry->heads) return FLOPPY_ERROR_INVALIDIMAGE; return FLOPPY_ERROR_SUCCESS; } static floperr_t coco_os9_post_format(floppy_image_legacy *floppy, option_resolution *params) { UINT8 header[0x0400]; floperr_t err; time_t t; struct tm *ltime; int heads, tracks, sectors, total_sectors; heads = option_resolution_lookup_int(params, PARAM_HEADS); tracks = option_resolution_lookup_int(params, PARAM_TRACKS); sectors = option_resolution_lookup_int(params, PARAM_SECTORS); total_sectors = heads * tracks * sectors; /* write the initial header */ time(&t); ltime = localtime(&t); memset(&header, 0, sizeof(header)); header[0x0000] = (UINT8) (total_sectors >> 16); header[0x0001] = (UINT8) (total_sectors >> 8); header[0x0002] = (UINT8) (total_sectors >> 0); header[0x0003] = (UINT8) sectors; header[0x0004] = (UINT8) (((total_sectors + 7) / 8) >> 8); header[0x0005] = (UINT8) (((total_sectors + 7) / 8) >> 0); header[0x0006] = 0x00; header[0x0007] = 0x01; header[0x0008] = 0x00; header[0x0009] = 0x00; header[0x000a] = 0x02; header[0x000b] = 0x00; header[0x000c] = 0x00; header[0x000d] = 0xff; header[0x000e] = floppy_random_byte(floppy); header[0x000f] = floppy_random_byte(floppy); header[0x0010] = (heads == 2) ? 3 : 2; header[0x0011] = (UINT8) (sectors >> 8); header[0x0012] = (UINT8) (sectors >> 0); header[0x001A] = (UINT8) ltime->tm_year; header[0x001B] = (UINT8) ltime->tm_mon + 1; header[0x001C] = (UINT8) ltime->tm_mday; header[0x001D] = (UINT8) ltime->tm_hour; header[0x001E] = (UINT8) ltime->tm_min; header[0x001F] = 0xA0; header[0x0100] = 0xFF; header[0x0101] = 0xC0; header[0x0200] = 0xBF; header[0x0201] = 0x00; header[0x0202] = 0x00; header[0x0203] = (UINT8) ltime->tm_year; header[0x0204] = (UINT8) ltime->tm_mon + 1; header[0x0205] = (UINT8) ltime->tm_mday; header[0x0206] = (UINT8) ltime->tm_hour; header[0x0207] = (UINT8) ltime->tm_min; header[0x0208] = 0x02; header[0x0209] = 0x00; header[0x020A] = 0x00; header[0x020B] = 0x00; header[0x020C] = 0x40; header[0x020D] = (UINT8) (ltime->tm_year % 100); header[0x020E] = (UINT8) ltime->tm_mon; header[0x020F] = (UINT8) ltime->tm_mday; header[0x0210] = 0x00; header[0x0211] = 0x00; header[0x0212] = 0x03; header[0x0213] = 0x00; header[0x0214] = 0x07; header[0x0300] = 0x2E; header[0x0301] = 0xAE; header[0x031F] = 0x02; header[0x0320] = 0xAE; header[0x033F] = 0x02; if (total_sectors % 8) header[0x0100 + (total_sectors / 8)] = 0xFF >> (total_sectors % 8); err = floppy_write_sector(floppy, 0, 0, 1, 0, &header[0x0000], 256, 0); if (err) return err; err = floppy_write_sector(floppy, 0, 0, 2, 0, &header[0x0100], 256, 0); if (err) return err; err = floppy_write_sector(floppy, 0, 0, 3, 0, &header[0x0200], 256, 0); if (err) return err; err = floppy_write_sector(floppy, 0, 0, 4, 0, &header[0x0300], 256, 0); if (err) return err; return FLOPPY_ERROR_SUCCESS; } static FLOPPY_IDENTIFY(coco_os9_identify) { struct basicdsk_geometry geometry; *vote = coco_os9_readheader(floppy, &geometry) ? 0 : 100; return FLOPPY_ERROR_SUCCESS; } static FLOPPY_CONSTRUCT(coco_os9_construct) { floperr_t err; struct basicdsk_geometry geometry; if (params) { /* create */ memset(&geometry, 0, sizeof(geometry)); geometry.heads = option_resolution_lookup_int(params, PARAM_HEADS); geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); geometry.sectors = option_resolution_lookup_int(params, PARAM_SECTORS); geometry.first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); geometry.sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); } else { /* open */ err = coco_os9_readheader(floppy, &geometry); if (err) return err; } /* actually construct the image */ err = basicdsk_construct(floppy, &geometry); floppy_callbacks(floppy)->post_format = coco_os9_post_format; return err; } /* ----------------------------------------------------------------------- * VDK file format * * Used by Paul Burgin's PC-Dragon emulator * * Offset Bytes Field Description * ------ ----- ------------ ----------- * 0 1 magic1 Signature byte 1 ('d') * 1 1 magic2 Signature byte 2 ('k') * 2 2 header_len Total header length (little endian) * 4 1 ver_actual Version of the VDK format (0x10) * 5 1 ver_compat Backwards compatibility version (0x10) * 6 1 source_id Identify of the file source * 7 1 source_ver Version of the file source * 8 1 tracks Number of tracks * 9 1 sides Number of sides (1-2) * 10 1 flags Various flags * bit 0: Write protect * bit 1: A Lock * bit 2: F Lock * bit 3: Disk set * 11 1 compression Compression flags (bits 0-2) and name length * ----------------------------------------------------------------------- */ static int coco_vdk_decode_header(floppy_image_legacy *floppy, struct basicdsk_geometry *geometry) { UINT8 header[12]; UINT8 heads, tracks, sectors; UINT16 sector_length, offset; UINT64 size; size = floppy_image_size(floppy); floppy_image_read(floppy, header, 0, sizeof(header)); if (header[0] != 'd') return -1; if (header[1] != 'k') return -1; if (header[5] != 0x10) return -1; if (header[11] & 0x07) return -1; heads = header[9]; tracks = header[8]; sectors = 18; sector_length = 0x100; offset = header[3] * 0x100 + header[2]; if (size != ((UINT32) heads * tracks * sectors * sector_length + offset)) return -1; if (geometry) { memset(geometry, 0, sizeof(*geometry)); geometry->heads = heads; geometry->tracks = tracks; geometry->sectors = sectors; geometry->first_sector_id = 1; geometry->sector_length = sector_length; geometry->offset = offset; } return 0; } static FLOPPY_IDENTIFY(coco_vdk_identify) { *vote = coco_vdk_decode_header(floppy, NULL) ? 0 : 100; return FLOPPY_ERROR_SUCCESS; } static FLOPPY_CONSTRUCT(coco_vdk_construct) { struct basicdsk_geometry geometry; UINT8 header[12]; if (params) { /* create */ memset(&geometry, 0, sizeof(geometry)); geometry.heads = option_resolution_lookup_int(params, PARAM_HEADS); geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); geometry.sectors = 18; geometry.first_sector_id = 1; geometry.sector_length = 256; geometry.offset = sizeof(header); memset(&header, 0, sizeof(header)); header[0] = 'd'; header[1] = 'k'; header[2] = (UINT8) (sizeof(header) >> 0); header[3] = (UINT8) (sizeof(header) >> 8); header[4] = 0x10; header[5] = 0x10; header[8] = (UINT8) geometry.tracks; header[9] = (UINT8) geometry.heads; floppy_image_write(floppy, header, 0, sizeof(header)); } else { /* load */ if (coco_vdk_decode_header(floppy, &geometry)) return FLOPPY_ERROR_INVALIDIMAGE; } return basicdsk_construct(floppy, &geometry); } /* ----------------------------------------------------------------------- * DMK file format * * David M. Keil's disk image format is aptly called an 'on disk' image * format. This means that whatever written to the disk is enocded into * the image file. IDAMS, sector headers, traling CRCs, and intra sector * spacing. * * * HEADER DESCRIPTION: * * Offset Bytes Field Description * ------ ----- ------------ ----------- * 0 1 write_prot 0xff = Writed Protected, 0x00 = R/W * 1 1 tracks Number of tracks * 2 2 track_length Bytes per track (little endian) * 4 1 disk_options Miscellaneous flags * bit 0-3: Unused * bit 4: 1=single sided 0=dbl * bit 5: Unused * bit 6: Single density? * bit 6: Ignore density flags? * 5 7 reserved Reserved for future use * 12 4 real_disk_code If this is 0x12345678 (little endian) * then access a real disk drive * (unsupported) * * Each track begins with a track TOC, consisting of 64 little endian 16-bit * integers. Each integer has the following format: * bit 0-13: Offset from beginning of track to 'FE' byte of IDAM * Note these are always sorted from first to last. All empty * entries are 0x00 * bit 14: Undefined (reserved) * bit 15: Sector double density (0=SD 1=DD) * ----------------------------------------------------------------------- */ struct dmk_tag { int heads; int tracks; UINT32 track_size; }; #define DMK_HEADER_LEN 16 #define DMK_TOC_LEN 64 #define DMK_IDAM_LENGTH 7 #define DMK_DATA_GAP 80 #define DMK_LEAD_IN 32 #define DMK_EXTRA_TRACK_LENGTH 156 #define dmk_idam_type(x) (x)[0] #define dmk_idam_track(x) (x)[1] #define dmk_idam_side(x) (x)[2] #define dmk_idam_sector(x) (x)[3] #define dmk_idam_sectorlength(x) (x)[4] #define dmk_idam_crc(x) (((x)[5] << 8) + (x)[6]) #define dmk_idam_set_crc(x, crc) (x)[5] = ((crc) >> 8); (x)[6] = ((crc) >> 0); static struct dmk_tag *get_dmk_tag(floppy_image_legacy *floppy) { return (dmk_tag *)floppy_tag(floppy); } static floperr_t coco_dmk_get_offset(floppy_image_legacy *floppy, int head, int track, UINT64 *offset) { struct dmk_tag *tag = get_dmk_tag(floppy); if ((head < 0) || (head >= tag->heads) || (track < 0) || (track >= tag->tracks)) return FLOPPY_ERROR_SEEKERROR; *offset = track; *offset *= tag->heads; *offset += head; *offset *= tag->track_size; *offset += DMK_HEADER_LEN; return FLOPPY_ERROR_SUCCESS; } static UINT32 coco_dmk_min_track_size(int sectors, int sector_length) { int sector_physical_length; sector_physical_length = 8 + 3 + DMK_IDAM_LENGTH + 22 + 12 + 3 + 1 + sector_length + 2 + 24; return DMK_TOC_LEN * 2 + DMK_LEAD_IN + (sectors * sector_physical_length); } static floperr_t coco_dmk_read_track(floppy_image_legacy *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) { floperr_t err; UINT64 track_offset; err = coco_dmk_get_offset(floppy, head, track, &track_offset); if (err) return err; floppy_image_read(floppy, buffer, offset + track_offset, buflen); return FLOPPY_ERROR_SUCCESS; } static floperr_t coco_dmk_write_track(floppy_image_legacy *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) { floperr_t err; UINT64 track_offset; err = coco_dmk_get_offset(floppy, head, track, &track_offset); if (err) return err; floppy_image_write(floppy, buffer, offset + track_offset, buflen); return FLOPPY_ERROR_SUCCESS; } static floperr_t coco_dmk_get_track_data_offset(floppy_image_legacy *floppy, int head, int track, UINT64 *offset) { *offset = DMK_TOC_LEN + 1; return FLOPPY_ERROR_SUCCESS; } static floperr_t coco_dmk_format_track(floppy_image_legacy *floppy, int head, int track, option_resolution *params) { int sectors; int sector_length; int interleave; int first_sector_id; floperr_t err; int physical_sector; int logical_sector; int track_position; UINT16 idam_offset; UINT16 crc; UINT8 *track_data; void *track_data_v; UINT32 max_track_size; dynamic_array sector_map; sectors = option_resolution_lookup_int(params, PARAM_SECTORS); sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); interleave = option_resolution_lookup_int(params, PARAM_INTERLEAVE); first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); max_track_size = get_dmk_tag(floppy)->track_size; if (sectors > DMK_TOC_LEN) { err = FLOPPY_ERROR_INTERNAL; goto done; } if (max_track_size < coco_dmk_min_track_size(sectors, sector_length)) { err = FLOPPY_ERROR_NOSPACE; goto done; } err = floppy_load_track(floppy, head, track, TRUE, &track_data_v, NULL); if (err) goto done; track_data = (UINT8 *) track_data_v; /* set up sector map */ sector_map.resize_and_clear(sectors, 0xFF); physical_sector = 0; for (logical_sector = 0; logical_sector < sectors; logical_sector++) { while(sector_map[physical_sector] >= 0) { physical_sector++; physical_sector %= sectors; } sector_map[physical_sector] = logical_sector + first_sector_id; physical_sector += interleave + 1; physical_sector %= sectors; } /* set up track table of contents */ physical_sector = 0; track_position = DMK_TOC_LEN * 2 + DMK_LEAD_IN; while(physical_sector < DMK_TOC_LEN) { if (physical_sector >= sectors) { /* no more sectors */ idam_offset = 0; } else { /* this is a sector */ logical_sector = sector_map[physical_sector]; /* write the sector */ memset(&track_data[track_position], 0x00, 8); track_position += 8; memset(&track_data[track_position], 0xA1, 3); track_position += 3; idam_offset = track_position | 0x8000; dmk_idam_type( &track_data[track_position]) = 0xFE; dmk_idam_track( &track_data[track_position]) = track; dmk_idam_side( &track_data[track_position]) = head; dmk_idam_sector( &track_data[track_position]) = logical_sector; dmk_idam_sectorlength( &track_data[track_position]) = compute_log2(sector_length / 128); crc = ccitt_crc16(0xcdb4, &track_data[track_position], DMK_IDAM_LENGTH - 2); dmk_idam_set_crc( &track_data[track_position], crc); track_position += DMK_IDAM_LENGTH; memset(&track_data[track_position], 0x4E, 22); track_position += 22; memset(&track_data[track_position], 0x00, 12); track_position += 12; memset(&track_data[track_position], 0xA1, 3); track_position += 3; /* write sector body */ track_data[track_position] = 0xFB; memset(&track_data[track_position + 1], floppy_get_filler(floppy), sector_length); crc = ccitt_crc16(0xcdb4, &track_data[track_position], sector_length + 1); track_data[track_position + sector_length + 1] = (UINT8) (crc >> 8); track_data[track_position + sector_length + 2] = (UINT8) (crc >> 0); track_position += sector_length + 3; /* write sector footer */ memset(&track_data[track_position], 0x4E, 24); track_position += 24; } /* write the TOC entry */ track_data[physical_sector * 2 + 0] = (UINT8) (idam_offset >> 0); track_data[physical_sector * 2 + 1] = (UINT8) (idam_offset >> 8); physical_sector++; } /* write track lead in */ memset(&track_data[physical_sector * 2], 0x4e, DMK_LEAD_IN); /* write track footer */ assert(max_track_size >= (UINT32)track_position); memset(&track_data[track_position], 0x4e, max_track_size - track_position); done: return err; } static int coco_dmk_get_heads_per_disk(floppy_image_legacy *floppy) { return get_dmk_tag(floppy)->heads; } static int coco_dmk_get_tracks_per_disk(floppy_image_legacy *floppy) { return get_dmk_tag(floppy)->tracks; } static UINT32 coco_dmk_get_track_size(floppy_image_legacy *floppy, int head, int track) { return get_dmk_tag(floppy)->track_size; } static floperr_t coco_dmk_seek_sector_in_track(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, int dirtify, UINT8 **sector_data, UINT32 *sector_length) { struct dmk_tag *tag = get_dmk_tag(floppy); floperr_t err; UINT32 idam_offset = 0; UINT16 calculated_crc; size_t i; size_t offs; int state; UINT8 *track_data; void *track_data_v; size_t track_length; size_t sec_len; err = floppy_load_track(floppy, head, track, dirtify, &track_data_v, &track_length); if (err) return err; track_data = (UINT8 *) track_data_v; /* search for matching IDAM */ for (i = 0; i < DMK_TOC_LEN; i++) { idam_offset = track_data[i * 2 + 1]; idam_offset <<= 8; idam_offset |= track_data[i * 2 + 0]; idam_offset &= 0x3FFF; if (idam_offset == 0) { /* we've reached the end of the road */ i = DMK_TOC_LEN; break; } if ((idam_offset + DMK_IDAM_LENGTH) >= tag->track_size) continue; calculated_crc = ccitt_crc16(0xCDB4, &track_data[idam_offset], DMK_IDAM_LENGTH - 2); if (calculated_crc == dmk_idam_crc(&track_data[idam_offset])) { if (sector_is_index) { /* the sector is indexed; decrement the index and go */ if (sector-- == 0) break; } else { /* check IDAM integrity and check for matching sector */ if (sector == dmk_idam_sector(&track_data[idam_offset]) /* && (track == dmk_idam_track(&track_data[idam_offset])) */ /* && (head == dmk_idam_side(&track_data[idam_offset])) */ ) break; } } } if (i >= DMK_TOC_LEN) return FLOPPY_ERROR_SEEKERROR; /* we found a matching sector ID */ state = 0; offs = idam_offset + DMK_IDAM_LENGTH; /* find pattern 0xA1A1FB; this represents the start of a data sector */ for (i = 0; i < DMK_DATA_GAP; i++) { /* overflowing the track? */ if ((i + offs) >= tag->track_size) return FLOPPY_ERROR_SEEKERROR; if (track_data[offs + i] == 0xA1) state++; else if ((track_data[offs + i] == 0xFB) && state) break; else state = 0; } if (i >= DMK_DATA_GAP) return FLOPPY_ERROR_SEEKERROR; offs += i + 1; sec_len = 128 << dmk_idam_sectorlength(&track_data[idam_offset]); if ((offs + sec_len) > track_length) return FLOPPY_ERROR_INVALIDIMAGE; if (sector_data) *sector_data = track_data + offs; if (sector_length) *sector_length = sec_len; return FLOPPY_ERROR_SUCCESS; } static floperr_t coco_dmk_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, UINT32 *sector_length) { return coco_dmk_seek_sector_in_track(floppy, head, track, sector, FALSE, FALSE, NULL, sector_length); } static floperr_t coco_dmk_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) { floperr_t err; UINT32 idam_offset; const UINT8 *track_data; void *track_data_v; if (sector_index*2 >= DMK_TOC_LEN) return FLOPPY_ERROR_SEEKERROR; err = floppy_load_track(floppy, head, track, FALSE, &track_data_v, NULL); if (err) return err; track_data = (UINT8 *) track_data_v; idam_offset = track_data[sector_index * 2 + 1]; idam_offset <<= 8; idam_offset |= track_data[sector_index * 2 + 0]; idam_offset &= 0x3FFF; if (idam_offset == 0) return FLOPPY_ERROR_SEEKERROR; if (cylinder) *cylinder = dmk_idam_track(&track_data[idam_offset]); if (side) *side = dmk_idam_side(&track_data[idam_offset]); if (sector) *sector = dmk_idam_sector(&track_data[idam_offset]); if (sector_length) *sector_length = 128 << dmk_idam_sectorlength(&track_data[idam_offset]); if (flags) /* TODO: read DAM or DDAM and determine flags */ *flags = 0; return FLOPPY_ERROR_SUCCESS; } static floperr_t internal_coco_dmk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) { floperr_t err; UINT32 sector_length; UINT16 crc_on_disk; UINT16 calculated_crc; UINT8 *sector_data; err = coco_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, FALSE, §or_data, §or_length); if (err) return err; crc_on_disk = sector_data[sector_length + 0]; crc_on_disk <<= 8; crc_on_disk += sector_data[sector_length + 1]; calculated_crc = ccitt_crc16(0xE295, sector_data, sector_length); if (calculated_crc != crc_on_disk) return FLOPPY_ERROR_INVALIDIMAGE; memcpy(buffer, sector_data, MIN(sector_length, buflen)); return FLOPPY_ERROR_SUCCESS; } static floperr_t internal_coco_dmk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) { floperr_t err; UINT32 sector_length; UINT8 *sector_data; UINT16 crc; err = coco_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, TRUE, §or_data, §or_length); if (err) return err; if (buflen > sector_length) return FLOPPY_ERROR_INTERNAL; memcpy(sector_data, buffer, buflen); crc = ccitt_crc16(0xE295, sector_data, sector_length); sector_data[sector_length + 0] = crc >> 8; sector_data[sector_length + 1] = crc >> 0; return FLOPPY_ERROR_SUCCESS; } static floperr_t coco_dmk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen) { return internal_coco_dmk_read_sector(floppy, head, track, sector, FALSE, buffer, buflen); } static floperr_t coco_dmk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) { return internal_coco_dmk_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam); } static floperr_t coco_dmk_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen) { return internal_coco_dmk_read_sector(floppy, head, track, sector, TRUE, buffer, buflen); } static floperr_t coco_dmk_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) { return internal_coco_dmk_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam); } static void coco_dmk_interpret_header(floppy_image_legacy *floppy, int *heads, int *tracks, int *track_size) { UINT8 header[DMK_HEADER_LEN]; floppy_image_read(floppy, header, 0, DMK_HEADER_LEN); if (tracks) *tracks = header[1]; if (heads) *heads = (header[4] & 0x10) ? 1 : 2; if (track_size) *track_size = ((int) header[3]) * 0x100 + header[2]; } FLOPPY_CONSTRUCT(coco_dmk_construct) { struct FloppyCallbacks *callbacks; struct dmk_tag *tag; UINT8 header[DMK_HEADER_LEN]; int heads, tracks, track_size, sectors, sector_length; if (params) { heads = option_resolution_lookup_int(params, PARAM_HEADS); tracks = option_resolution_lookup_int(params, PARAM_TRACKS); sectors = option_resolution_lookup_int(params, PARAM_SECTORS); sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); track_size = coco_dmk_min_track_size(sectors, sector_length) + DMK_EXTRA_TRACK_LENGTH; memset(header, 0, sizeof(header)); header[1] = tracks; header[2] = track_size >> 0; header[3] = track_size >> 8; header[4] = (heads == 2) ? 0x00 : 0x10; floppy_image_write(floppy, header, 0, sizeof(header)); } else { coco_dmk_interpret_header(floppy, &heads, &tracks, &track_size); } tag = (dmk_tag *)floppy_create_tag(floppy, sizeof(struct dmk_tag)); if (!tag) return FLOPPY_ERROR_OUTOFMEMORY; tag->heads = heads; tag->track_size = track_size; tag->tracks = tracks; callbacks = floppy_callbacks(floppy); callbacks->read_track = coco_dmk_read_track; callbacks->write_track = coco_dmk_write_track; callbacks->get_track_data_offset = coco_dmk_get_track_data_offset; callbacks->format_track = coco_dmk_format_track; callbacks->get_heads_per_disk = coco_dmk_get_heads_per_disk; callbacks->get_tracks_per_disk = coco_dmk_get_tracks_per_disk; callbacks->get_track_size = coco_dmk_get_track_size; callbacks->get_sector_length = coco_dmk_get_sector_length; callbacks->get_indexed_sector_info = coco_dmk_get_indexed_sector_info; callbacks->read_sector = coco_dmk_read_sector; callbacks->write_sector = coco_dmk_write_sector; callbacks->read_indexed_sector = coco_dmk_read_indexed_sector; callbacks->write_indexed_sector = coco_dmk_write_indexed_sector; return FLOPPY_ERROR_SUCCESS; } FLOPPY_IDENTIFY(coco_dmk_identify) { int heads, tracks, track_size; UINT64 size, expected_size; size = floppy_image_size(floppy); coco_dmk_interpret_header(floppy, &heads, &tracks, &track_size); expected_size = DMK_HEADER_LEN + (heads * tracks * track_size); *vote = (size == expected_size) ? 100 : 0; return FLOPPY_ERROR_SUCCESS; } /* ----------------------------------------------------------------------- */ LEGACY_FLOPPY_OPTIONS_START( coco ) LEGACY_FLOPPY_OPTION( coco_jvc, "dsk", "CoCo JVC disk image", coco_jvc_identify, coco_jvc_construct, NULL, HEADS([1]-2) TRACKS([35]-255) SECTORS(1-[18]-255) SECTOR_LENGTH(128/[256]/512/1024) FIRST_SECTOR_ID(0-[1])) LEGACY_FLOPPY_OPTION( coco_os9, "os9", "CoCo OS-9 disk image", coco_os9_identify, coco_os9_construct, NULL, HEADS([1]-2) TRACKS([35]-255) SECTORS(1-[18]-255) SECTOR_LENGTH([256]) FIRST_SECTOR_ID([1])) LEGACY_FLOPPY_OPTION( coco_vdk, "vdk", "CoCo VDK disk image", coco_vdk_identify, coco_vdk_construct, NULL, HEADS([1]-2) TRACKS([35]-255) SECTORS([18]) SECTOR_LENGTH([256]) FIRST_SECTOR_ID([1])) LEGACY_FLOPPY_OPTION( coco_dmk, "dsk,dmk", "CoCo DMK disk image", coco_dmk_identify, coco_dmk_construct, NULL, HEADS([1]-2) TRACKS([35]-255) SECTORS(1-[18]) SECTOR_LENGTH(128/[256]/512/1024/2048/4096/8192) INTERLEAVE(0-[6]-17) FIRST_SECTOR_ID(0-[1])) LEGACY_FLOPPY_OPTIONS_END