/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * Implementation of the 86F floppy image format (stores the * data in the form of FM/MFM-encoded transitions) which also * forms the core of the emulator's floppy disk emulation. * * * * Authors: Miran Grca, * Fred N. van Kempen, * * Copyright 2016-2019 Miran Grca. * Copyright 2018-2019 Fred N. van Kempen. */ #include #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/timer.h> #include <86box/dma.h> #include <86box/nvr.h> #include <86box/random.h> #include <86box/plat.h> #include <86box/ui.h> #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/fdd_86f.h> #ifdef D86F_COMPRESS # include #endif /* * Let's give this some more logic: * * Bits 4,3 = Read/write (0 = read, 1 = write, 2 = scan, 3 = verify) * Bits 6,5 = Sector/track (0 = ID, 1 = sector, 2 = deleted sector, 3 = track) * Bit 7 = State type (0 = idle states, 1 = active states) */ enum { /* 0 ?? ?? ??? */ STATE_IDLE = 0x00, STATE_SECTOR_NOT_FOUND, /* 1 00 00 ??? */ STATE_0A_FIND_ID = 0x80, /* READ SECTOR ID */ STATE_0A_READ_ID, /* 1 01 00 ??? */ STATE_06_FIND_ID = 0xA0, /* READ DATA */ STATE_06_READ_ID, STATE_06_FIND_DATA, STATE_06_READ_DATA, /* 1 01 01 ??? */ STATE_05_FIND_ID = 0xA8, /* WRITE DATA */ STATE_05_READ_ID, STATE_05_FIND_DATA, STATE_05_WRITE_DATA, /* 1 01 10 ??? */ STATE_11_FIND_ID = 0xB0, /* SCAN EQUAL,SCAN LOW/EQUAL,SCAN HIGH/EQUAL */ STATE_11_READ_ID, STATE_11_FIND_DATA, STATE_11_SCAN_DATA, /* 1 01 11 ??? */ STATE_16_FIND_ID = 0xB8, /* VERIFY */ STATE_16_READ_ID, STATE_16_FIND_DATA, STATE_16_VERIFY_DATA, /* 1 10 00 ??? */ STATE_0C_FIND_ID = 0xC0, /* READ DELETED DATA */ STATE_0C_READ_ID, STATE_0C_FIND_DATA, STATE_0C_READ_DATA, /* 1 10 01 ??? */ STATE_09_FIND_ID = 0xC8, /* WRITE DELETED DATA */ STATE_09_READ_ID, STATE_09_FIND_DATA, STATE_09_WRITE_DATA, /* 1 11 00 ??? */ STATE_02_SPIN_TO_INDEX = 0xE0, /* READ TRACK */ STATE_02_FIND_ID, STATE_02_READ_ID, STATE_02_FIND_DATA, STATE_02_READ_DATA, /* 1 11 01 ??? */ STATE_0D_SPIN_TO_INDEX = 0xE8, /* FORMAT TRACK */ STATE_0D_FORMAT_TRACK, }; enum { FMT_PRETRK_GAP0, FMT_PRETRK_SYNC, FMT_PRETRK_IAM, FMT_PRETRK_GAP1, FMT_SECTOR_ID_SYNC, FMT_SECTOR_IDAM, FMT_SECTOR_ID, FMT_SECTOR_ID_CRC, FMT_SECTOR_GAP2, FMT_SECTOR_DATA_SYNC, FMT_SECTOR_DATAAM, FMT_SECTOR_DATA, FMT_SECTOR_DATA_CRC, FMT_SECTOR_GAP3, FMT_POSTTRK_CHECK, FMT_POSTTRK_GAP4 }; typedef struct sliding_buffer_t { uint8_t buffer[10]; uint32_t pos; uint32_t len; } sliding_buffer_t; typedef struct find_t { uint32_t bits_obtained; uint16_t bytes_obtained; uint16_t sync_marks; uint32_t sync_pos; } find_t; typedef struct split_byte_t { unsigned nibble0 : 4; unsigned nibble1 : 4; } split_byte_t; typedef union decoded_t { uint8_t byte; split_byte_t nibbles; } decoded_t; typedef struct sector_t { uint8_t c; uint8_t h; uint8_t r; uint8_t n; uint8_t flags; uint8_t pad; uint8_t pad0; uint8_t pad1; void *prev; } sector_t; /* Disk flags: * Bit 0 Has surface data (1 = yes, 0 = no) * Bits 2, 1 Hole (3 = ED + 2000 kbps, 2 = ED, 1 = HD, 0 = DD) * Bit 3 Sides (1 = 2 sides, 0 = 1 side) * Bit 4 Write protect (1 = yes, 0 = no) * Bits 6, 5 RPM slowdown (3 = 2%, 2 = 1.5%, 1 = 1%, 0 = 0%) * Bit 7 Bitcell mode (1 = Extra bitcells count specified after * disk flags, 0 = No extra bitcells) * The maximum number of extra bitcells is 1024 (which * after decoding translates to 64 bytes) * Bit 8 Disk type (1 = Zoned, 0 = Fixed RPM) * Bits 10, 9 Zone type (3 = Commodore 64 zoned, 2 = Apple zoned, * 1 = Pre-Apple zoned #2, 0 = Pre-Apple zoned #1) * Bit 11 Data and surface bits are stored in reverse byte endianness * Bit 12 If bits 6, 5 are not 0, they specify % of speedup instead * of slowdown; * If bits 6, 5 are 0, and bit 7 is 1, the extra bitcell count * specifies the entire bitcell count */ typedef struct d86f_t { FILE *fp; uint8_t state; uint8_t fill; uint8_t sector_count; uint8_t format_state; uint8_t error_condition; uint8_t id_found; uint16_t version; uint16_t disk_flags; uint16_t satisfying_bytes; uint16_t turbo_pos; uint16_t cur_track; uint16_t track_encoded_data[2][53048]; uint16_t *track_surface_data[2]; uint16_t thin_track_encoded_data[2][2][53048]; uint16_t *thin_track_surface_data[2][2]; uint16_t side_flags[2]; uint16_t preceding_bit[2]; uint16_t current_byte[2]; uint16_t current_bit[2]; uint16_t last_word[2]; #ifdef D86F_COMPRESS int is_compressed; #endif int32_t extra_bit_cells[2]; uint32_t file_size; uint32_t index_count; uint32_t track_pos; uint32_t datac; uint32_t id_pos; uint32_t dma_over; uint32_t index_hole_pos[2]; uint32_t track_offset[512]; sector_id_t last_sector; sector_id_t req_sector; find_t id_find; find_t data_find; crc_t calc_crc; crc_t track_crc; char original_file_name[2048]; uint8_t *filebuf; uint8_t *outbuf; sector_t *last_side_sector[2]; } d86f_t; static const uint8_t encoded_fm[64] = { 0xaa, 0xab, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf, 0xea, 0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xff, 0xaa, 0xab, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf, 0xea, 0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xff, 0xaa, 0xab, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf, 0xea, 0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xff, 0xaa, 0xab, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf, 0xea, 0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xff }; static const uint8_t encoded_mfm[64] = { 0xaa, 0xa9, 0xa4, 0xa5, 0x92, 0x91, 0x94, 0x95, 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55, 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15, 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55, 0xaa, 0xa9, 0xa4, 0xa5, 0x92, 0x91, 0x94, 0x95, 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55, 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15, 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55 }; static d86f_t *d86f[FDD_NUM]; static uint16_t CRCTable[256]; static fdc_t *d86f_fdc; uint64_t poly = 0x42F0E1EBA9EA3693LL; /* ECMA normal */ uint16_t d86f_side_flags(int drive); int d86f_is_mfm(int drive); void d86f_writeback(int drive); uint8_t d86f_poll_read_data(int drive, int side, uint16_t pos); void d86f_poll_write_data(int drive, int side, uint16_t pos, uint8_t data); int d86f_format_conditions(int drive); #ifdef ENABLE_D86F_LOG int d86f_do_log = ENABLE_D86F_LOG; static void d86f_log(const char *fmt, ...) { va_list ap; if (d86f_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define d86f_log(fmt, ...) #endif static void setup_crc(uint16_t poly) { int c = 256; int bc; uint16_t temp; while (c--) { temp = c << 8; bc = 8; while (bc--) { if (temp & 0x8000) temp = (temp << 1) ^ poly; else temp <<= 1; CRCTable[c] = temp; } } } void d86f_destroy_linked_lists(int drive, int side) { d86f_t *dev = d86f[drive]; sector_t *s; sector_t *t; if (dev == NULL) return; if (dev->last_side_sector[side]) { s = dev->last_side_sector[side]; while (s) { t = s->prev; free(s); s = NULL; if (!t) break; s = t; } dev->last_side_sector[side] = NULL; } } static int d86f_has_surface_desc(int drive) { return (d86f_handler[drive].disk_flags(drive) & 1); } int d86f_get_sides(int drive) { return ((d86f_handler[drive].disk_flags(drive) >> 3) & 1) + 1; } int d86f_get_rpm_mode(int drive) { return (d86f_handler[drive].disk_flags(drive) & 0x60) >> 5; } int d86f_get_speed_shift_dir(int drive) { return (d86f_handler[drive].disk_flags(drive) & 0x1000) >> 12; } int d86f_reverse_bytes(int drive) { return (d86f_handler[drive].disk_flags(drive) & 0x800) >> 11; } uint16_t d86f_disk_flags(int drive) { const d86f_t *dev = d86f[drive]; return dev->disk_flags; } uint32_t d86f_index_hole_pos(int drive, int side) { const d86f_t *dev = d86f[drive]; return dev->index_hole_pos[side]; } uint32_t null_index_hole_pos(UNUSED(int drive), UNUSED(int side)) { return 0; } uint16_t null_disk_flags(UNUSED(int drive)) { return 0x09; } uint16_t null_side_flags(UNUSED(int drive)) { return 0x0A; } void null_writeback(UNUSED(int drive)) { return; } void null_set_sector(UNUSED(int drive), UNUSED(int side), UNUSED(uint8_t c), UNUSED(uint8_t h), UNUSED(uint8_t r), UNUSED(uint8_t n)) { return; } void null_write_data(UNUSED(int drive), UNUSED(int side), UNUSED(uint16_t pos), UNUSED(uint8_t data)) { return; } int null_format_conditions(UNUSED(int drive)) { return 0; } int32_t d86f_extra_bit_cells(int drive, int side) { const d86f_t *dev = d86f[drive]; return dev->extra_bit_cells[side]; } int32_t null_extra_bit_cells(UNUSED(int drive), UNUSED(int side)) { return 0; } uint16_t * common_encoded_data(int drive, int side) { d86f_t *dev = d86f[drive]; return dev->track_encoded_data[side]; } void common_read_revolution(UNUSED(int drive)) { return; } uint16_t d86f_side_flags(int drive) { const d86f_t *dev = d86f[drive]; int side; side = fdd_get_head(drive); return dev->side_flags[side]; } uint16_t d86f_track_flags(int drive) { uint16_t dr; uint16_t rr; uint16_t tf; tf = d86f_handler[drive].side_flags(drive); rr = tf & 0x67; dr = fdd_get_flags(drive) & 7; tf &= ~0x67; switch (rr) { case 0x02: case 0x21: /* 1 MB unformatted medium, treat these two as equivalent. */ switch (dr) { case 0x06: /* 5.25" Single-RPM HD drive, treat as 300 kbps, 360 rpm. */ tf |= 0x21; break; default: /* Any other drive, treat as 250 kbps, 300 rpm. */ tf |= 0x02; break; } break; default: tf |= rr; break; } return tf; } uint32_t common_get_raw_size(int drive, int side) { double rate = 0.0; double rpm; double rpm_diff; double size = 100000.0; int mfm; int rm; int ssd; uint32_t extra_bc = 0; mfm = d86f_is_mfm(drive); rpm = ((d86f_track_flags(drive) & 0xE0) == 0x20) ? 360.0 : 300.0; rpm_diff = 1.0; rm = d86f_get_rpm_mode(drive); ssd = d86f_get_speed_shift_dir(drive); /* 0% speed shift and shift direction 1: special case where extra bit cells are the entire track size. */ if (!rm && ssd) extra_bc = d86f_handler[drive].extra_bit_cells(drive, side); if (extra_bc) return extra_bc; switch (rm) { case 1: rpm_diff = 1.01; break; case 2: rpm_diff = 1.015; break; case 3: rpm_diff = 1.02; break; default: rpm_diff = 1.0; break; } if (ssd) rpm_diff = 1.0 / rpm_diff; switch (d86f_track_flags(drive) & 7) { case 0: rate = 500.0; break; case 1: rate = 300.0; break; case 2: rate = 250.0; break; case 3: rate = 1000.0; break; case 5: rate = 2000.0; break; default: rate = 250.0; break; } if (!mfm) rate /= 2.0; size = (size / 250.0) * rate; size = (size * 300.0) / rpm; size *= rpm_diff; /* * Round down to a multiple of 16 and add the extra bit cells, * then return. */ return ((((uint32_t) size) >> 4) << 4) + d86f_handler[drive].extra_bit_cells(drive, side); } void d86f_set_version(int drive, uint16_t version) { d86f_t *dev = d86f[drive]; dev->version = version; } void d86f_unregister(int drive) { d86f_t *dev = d86f[drive]; if (dev == NULL) return; d86f_handler[drive].disk_flags = null_disk_flags; d86f_handler[drive].side_flags = null_side_flags; d86f_handler[drive].writeback = null_writeback; d86f_handler[drive].set_sector = null_set_sector; d86f_handler[drive].write_data = null_write_data; d86f_handler[drive].format_conditions = null_format_conditions; d86f_handler[drive].extra_bit_cells = null_extra_bit_cells; d86f_handler[drive].encoded_data = common_encoded_data; d86f_handler[drive].read_revolution = common_read_revolution; d86f_handler[drive].index_hole_pos = null_index_hole_pos; d86f_handler[drive].get_raw_size = common_get_raw_size; d86f_handler[drive].check_crc = 0; dev->version = 0x0063; /* Proxied formats report as version 0.99. */ } void d86f_register_86f(int drive) { d86f_handler[drive].disk_flags = d86f_disk_flags; d86f_handler[drive].side_flags = d86f_side_flags; d86f_handler[drive].writeback = d86f_writeback; d86f_handler[drive].set_sector = null_set_sector; d86f_handler[drive].write_data = null_write_data; d86f_handler[drive].format_conditions = d86f_format_conditions; d86f_handler[drive].extra_bit_cells = d86f_extra_bit_cells; d86f_handler[drive].encoded_data = common_encoded_data; d86f_handler[drive].read_revolution = common_read_revolution; d86f_handler[drive].index_hole_pos = d86f_index_hole_pos; d86f_handler[drive].get_raw_size = common_get_raw_size; d86f_handler[drive].check_crc = 1; } int d86f_get_array_size(int drive, int side, int words) { int array_size; int hole; int rm; int ssd; rm = d86f_get_rpm_mode(drive); ssd = d86f_get_speed_shift_dir(drive); hole = (d86f_handler[drive].disk_flags(drive) & 6) >> 1; if (!rm && ssd) /* Special case - extra bit cells size specifies entire array size. */ array_size = 0; else switch (hole) { default: case 0: case 1: array_size = 12500; switch (rm) { case 1: array_size = ssd ? 12376 : 12625; break; case 2: array_size = ssd ? 12315 : 12687; break; case 3: array_size = ssd ? 12254 : 12750; break; default: break; } break; case 2: array_size = 25000; switch (rm) { case 1: array_size = ssd ? 24752 : 25250; break; case 2: array_size = ssd ? 24630 : 25375; break; case 3: array_size = ssd ? 24509 : 25500; break; default: break; } break; case 3: array_size = 50000; switch (rm) { case 1: array_size = ssd ? 49504 : 50500; break; case 2: array_size = ssd ? 49261 : 50750; break; case 3: array_size = ssd ? 49019 : 51000; break; default: break; } break; } array_size <<= 4; array_size += d86f_handler[drive].extra_bit_cells(drive, side); if (array_size & 15) array_size = (array_size >> 4) + 1; else array_size = (array_size >> 4); if (!words) array_size <<= 1; return array_size; } int d86f_valid_bit_rate(int drive) { int hole; int rate; rate = fdc_get_bit_rate(d86f_fdc); hole = (d86f_handler[drive].disk_flags(drive) & 6) >> 1; switch (hole) { case 0: /* DD */ if (!rate && (fdd_get_flags(drive) & 0x10)) return 1; if ((rate < 1) || (rate > 2)) return 0; return 1; case 1: /* HD */ if (rate != 0) return 0; return 1; case 2: /* ED */ if (rate != 3) return 0; return 1; case 3: /* ED with 2000 kbps support */ if (rate < 3) return 0; return 1; default: break; } return 0; } int d86f_hole(int drive) { if (((d86f_handler[drive].disk_flags(drive) >> 1) & 3) == 3) return 2; return (d86f_handler[drive].disk_flags(drive) >> 1) & 3; } uint8_t d86f_get_encoding(int drive) { return (d86f_track_flags(drive) & 0x18) >> 3; } uint64_t d86f_byteperiod(int drive) { double dusec = (double) TIMER_USEC; double p = 2.0; switch (d86f_track_flags(drive) & 0x0f) { case 0x02: /* 125 kbps, FM */ p = 4.0; break; case 0x01: /* 150 kbps, FM */ p = 20.0 / 6.0; break; case 0x0a: /* 250 kbps, MFM */ case 0x00: /* 250 kbps, FM */ default: p = 2.0; break; case 0x09: /* 300 kbps, MFM */ p = 10.0 / 6.0; break; case 0x08: /* 500 kbps, MFM */ p = 1.0; break; case 0x0b: /* 1000 kbps, MFM */ p = 0.5; break; case 0x0d: /* 2000 kbps, MFM */ p = 0.25; break; } return (uint64_t) (p * dusec); } int d86f_is_mfm(int drive) { return ((d86f_track_flags(drive) & 0x18) == 0x08) ? 1 : 0; } uint32_t d86f_get_data_len(int drive) { const d86f_t *dev = d86f[drive]; uint32_t i; uint32_t ret = 128; if (dev->req_sector.id.n) ret = (uint32_t) 128 << dev->req_sector.id.n; else if ((i = fdc_get_dtl(d86f_fdc)) < 128) ret = i; return ret; } uint32_t d86f_has_extra_bit_cells(int drive) { return (d86f_handler[drive].disk_flags(drive) >> 7) & 1; } uint32_t d86f_header_size(UNUSED(int drive)) { return 8; } static uint16_t d86f_encode_get_data(uint8_t dat) { uint16_t temp; temp = 0; if (dat & 0x01) temp |= 1; if (dat & 0x02) temp |= 4; if (dat & 0x04) temp |= 16; if (dat & 0x08) temp |= 64; if (dat & 0x10) temp |= 256; if (dat & 0x20) temp |= 1024; if (dat & 0x40) temp |= 4096; if (dat & 0x80) temp |= 16384; return temp; } static uint16_t d86f_encode_get_clock(uint8_t dat) { uint16_t temp; temp = 0; if (dat & 0x01) temp |= 2; if (dat & 0x02) temp |= 8; if (dat & 0x40) temp |= 32; if (dat & 0x08) temp |= 128; if (dat & 0x10) temp |= 512; if (dat & 0x20) temp |= 2048; if (dat & 0x40) temp |= 8192; if (dat & 0x80) temp |= 32768; return temp; } int d86f_format_conditions(int drive) { return d86f_valid_bit_rate(drive); } int d86f_wrong_densel(int drive) { int is_3mode = 0; if ((fdd_get_flags(drive) & 7) == 3) is_3mode = 1; switch (d86f_hole(drive)) { default: case 0: if (fdd_is_dd(drive)) return 0; if (fdd_get_densel(drive)) return 1; else return 0; case 1: if (fdd_is_dd(drive)) return 1; if (fdd_get_densel(drive)) return 0; else { if (is_3mode) return 0; else return 1; } case 2: if (fdd_is_dd(drive) || !fdd_is_ed(drive)) return 1; if (fdd_get_densel(drive)) return 0; else return 1; } } int d86f_can_format(int drive) { int temp; temp = !writeprot[drive]; temp = temp && !fdc_get_swwp(d86f_fdc); temp = temp && fdd_can_read_medium(real_drive(d86f_fdc, drive)); temp = temp && d86f_handler[drive].format_conditions(drive); /* Allows proxied formats to add their own extra conditions to formatting. */ temp = temp && !d86f_wrong_densel(drive); return temp; } uint16_t d86f_encode_byte(int drive, int sync, decoded_t b, decoded_t prev_b) { uint8_t encoding = d86f_get_encoding(drive); uint8_t bits89AB = prev_b.nibbles.nibble0; uint8_t bits7654 = b.nibbles.nibble1; uint8_t bits3210 = b.nibbles.nibble0; uint16_t encoded_7654; uint16_t encoded_3210; uint16_t result; if (encoding > 1) return 0xffff; if (sync) { result = d86f_encode_get_data(b.byte); if (encoding) { switch (b.byte) { case 0xa1: return result | d86f_encode_get_clock(0x0a); case 0xc2: return result | d86f_encode_get_clock(0x14); case 0xf8: return result | d86f_encode_get_clock(0x03); case 0xfb: case 0xfe: return result | d86f_encode_get_clock(0x00); case 0xfc: return result | d86f_encode_get_clock(0x01); default: break; } } else { switch (b.byte) { case 0xf8: case 0xfb: case 0xfe: return result | d86f_encode_get_clock(0xc7); case 0xfc: return result | d86f_encode_get_clock(0xd7); default: break; } } } bits3210 += ((bits7654 & 3) << 4); bits7654 += ((bits89AB & 3) << 4); encoded_3210 = (encoding == 1) ? encoded_mfm[bits3210] : encoded_fm[bits3210]; encoded_7654 = (encoding == 1) ? encoded_mfm[bits7654] : encoded_fm[bits7654]; result = (encoded_7654 << 8) | encoded_3210; return result; } static int d86f_get_bitcell_period(int drive) { double rate = 0.0; int mfm = 0; int tflags = 0; double rpm = 0; double size = 8000.0; tflags = d86f_track_flags(drive); mfm = (tflags & 8) ? 1 : 0; rpm = ((tflags & 0xE0) == 0x20) ? 360.0 : 300.0; switch (tflags & 7) { case 0: rate = 500.0; break; case 1: rate = 300.0; break; case 2: rate = 250.0; break; case 3: rate = 1000.0; break; case 5: rate = 2000.0; break; default: break; } if (!mfm) rate /= 2.0; size = (size * 250.0) / rate; size = (size * 300.0) / rpm; size = (size * fdd_getrpm(real_drive(d86f_fdc, drive))) / 300.0; return (int) size; } int d86f_can_read_address(int drive) { int temp; temp = (fdc_get_bitcell_period(d86f_fdc) == d86f_get_bitcell_period(drive)); temp = temp && fdd_can_read_medium(real_drive(d86f_fdc, drive)); temp = temp && (fdc_is_mfm(d86f_fdc) == d86f_is_mfm(drive)); temp = temp && (d86f_get_encoding(drive) <= 1); return temp; } void d86f_get_bit(int drive, int side) { d86f_t *dev = d86f[drive]; uint32_t track_word; uint32_t track_bit; uint16_t encoded_data; uint16_t surface_data = 0; uint16_t current_bit; uint16_t surface_bit; track_word = dev->track_pos >> 4; /* We need to make sure we read the bits from MSB to LSB. */ track_bit = 15 - (dev->track_pos & 15); if (d86f_reverse_bytes(drive)) { /* Image is in reverse endianness, read the data as is. */ encoded_data = d86f_handler[drive].encoded_data(drive, side)[track_word]; } else { /* We store the words as big endian, so we need to convert them to little endian when reading. */ encoded_data = (d86f_handler[drive].encoded_data(drive, side)[track_word] & 0xFF) << 8; encoded_data |= (d86f_handler[drive].encoded_data(drive, side)[track_word] >> 8); } /* In some cases, misindentification occurs so we need to make sure the surface data array is not not NULL. */ if (d86f_has_surface_desc(drive) && dev->track_surface_data[side]) { if (d86f_reverse_bytes(drive)) { surface_data = dev->track_surface_data[side][track_word] & 0xFF; } else { surface_data = (dev->track_surface_data[side][track_word] & 0xFF) << 8; surface_data |= (dev->track_surface_data[side][track_word] >> 8); } } current_bit = (encoded_data >> track_bit) & 1; dev->last_word[side] <<= 1; if (d86f_has_surface_desc(drive) && dev->track_surface_data[side]) { surface_bit = (surface_data >> track_bit) & 1; if (!surface_bit) dev->last_word[side] |= current_bit; else { /* Bit is either 0 or 1 and is set to fuzzy, we randomly generate it. */ dev->last_word[side] |= (random_generate() & 1); } } else dev->last_word[side] |= current_bit; } void d86f_put_bit(int drive, int side, int bit) { d86f_t *dev = d86f[drive]; uint32_t track_word; uint32_t track_bit; uint16_t encoded_data; uint16_t surface_data = 0; uint16_t current_bit; uint16_t surface_bit; if (fdc_get_diswr(d86f_fdc)) return; track_word = dev->track_pos >> 4; /* We need to make sure we read the bits from MSB to LSB. */ track_bit = 15 - (dev->track_pos & 15); if (d86f_reverse_bytes(drive)) { /* Image is in reverse endianness, read the data as is. */ encoded_data = d86f_handler[drive].encoded_data(drive, side)[track_word]; } else { /* We store the words as big endian, so we need to convert them to little endian when reading. */ encoded_data = (d86f_handler[drive].encoded_data(drive, side)[track_word] & 0xFF) << 8; encoded_data |= (d86f_handler[drive].encoded_data(drive, side)[track_word] >> 8); } if (d86f_has_surface_desc(drive)) { if (d86f_reverse_bytes(drive)) { surface_data = dev->track_surface_data[side][track_word] & 0xFF; } else { surface_data = (dev->track_surface_data[side][track_word] & 0xFF) << 8; surface_data |= (dev->track_surface_data[side][track_word] >> 8); } } current_bit = (encoded_data >> track_bit) & 1; dev->last_word[side] <<= 1; if (d86f_has_surface_desc(drive)) { surface_bit = (surface_data >> track_bit) & 1; if (!surface_bit) { dev->last_word[side] |= bit; current_bit = bit; } else { if (current_bit) { /* Bit is 1 and is set to fuzzy, we overwrite it with a non-fuzzy bit. */ dev->last_word[side] |= bit; current_bit = bit; surface_bit = 0; } } surface_data &= ~(1 << track_bit); surface_data |= (surface_bit << track_bit); if (d86f_reverse_bytes(drive)) { dev->track_surface_data[side][track_word] = surface_data; } else { dev->track_surface_data[side][track_word] = (surface_data & 0xFF) << 8; dev->track_surface_data[side][track_word] |= (surface_data >> 8); } } else { dev->last_word[side] |= bit; current_bit = bit; } encoded_data &= ~(1 << track_bit); encoded_data |= (current_bit << track_bit); if (d86f_reverse_bytes(drive)) { d86f_handler[drive].encoded_data(drive, side)[track_word] = encoded_data; } else { d86f_handler[drive].encoded_data(drive, side)[track_word] = (encoded_data & 0xFF) << 8; d86f_handler[drive].encoded_data(drive, side)[track_word] |= (encoded_data >> 8); } } static uint8_t decodefm(UNUSED(int drive), uint16_t dat) { uint8_t temp = 0; /* * We write the encoded bytes in big endian, so we * process the two 8-bit halves swapped here. */ if (dat & 0x0001) temp |= 1; if (dat & 0x0004) temp |= 2; if (dat & 0x0010) temp |= 4; if (dat & 0x0040) temp |= 8; if (dat & 0x0100) temp |= 16; if (dat & 0x0400) temp |= 32; if (dat & 0x1000) temp |= 64; if (dat & 0x4000) temp |= 128; return temp; } void fdd_calccrc(uint8_t byte, crc_t *crc_var) { crc_var->word = (crc_var->word << 8) ^ CRCTable[(crc_var->word >> 8) ^ byte]; } static void d86f_calccrc(d86f_t *dev, uint8_t byte) { fdd_calccrc(byte, &(dev->calc_crc)); } int d86f_word_is_aligned(int drive, int side, uint32_t base_pos) { const d86f_t *dev = d86f[drive]; uint32_t adjusted_track_pos = dev->track_pos; if (base_pos == 0xFFFFFFFF) return 0; /* * This is very important, it makes sure alignment is detected * correctly even across the index hole of a track whose length * is not divisible by 16. */ if (adjusted_track_pos < base_pos) adjusted_track_pos += d86f_handler[drive].get_raw_size(drive, side); if ((adjusted_track_pos & 15) == (base_pos & 15)) return 1; return 0; } /* State 1: Find sector ID */ void d86f_find_address_mark_fm(int drive, int side, find_t *find, uint16_t req_am, uint16_t other_am, uint16_t wrong_am, uint16_t ignore_other_am) { d86f_t *dev = d86f[drive]; d86f_get_bit(drive, side); if (dev->last_word[side] == req_am) { dev->calc_crc.word = 0xFFFF; fdd_calccrc(decodefm(drive, dev->last_word[side]), &(dev->calc_crc)); find->sync_marks = find->bits_obtained = find->bytes_obtained = 0; find->sync_pos = 0xFFFFFFFF; dev->preceding_bit[side] = dev->last_word[side] & 1; dev->state++; return; } if (wrong_am && (dev->last_word[side] == wrong_am)) { dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->error_condition = 0; dev->state = STATE_IDLE; fdc_nodataam(d86f_fdc); return; } if ((ignore_other_am & 2) && (dev->last_word[side] == other_am)) { dev->calc_crc.word = 0xFFFF; fdd_calccrc(decodefm(drive, dev->last_word[side]), &(dev->calc_crc)); find->sync_marks = find->bits_obtained = find->bytes_obtained = 0; find->sync_pos = 0xFFFFFFFF; if (ignore_other_am & 1) { /* Skip mode, let's go back to finding ID. */ dev->state -= 2; } else { /* Not skip mode, process the sector anyway. */ fdc_set_wrong_am(d86f_fdc); dev->preceding_bit[side] = dev->last_word[side] & 1; dev->state++; } } } /* When writing in FM mode, we find the beginning of the address mark by looking for 352 (22 * 16) set bits (gap fill = 0xFF, 0xFFFF FM-encoded). */ void d86f_write_find_address_mark_fm(int drive, int side, find_t *find) { d86f_t *dev = d86f[drive]; d86f_get_bit(drive, side); if (dev->last_word[side] & 1) { find->sync_marks++; if (find->sync_marks == 352) { dev->calc_crc.word = 0xFFFF; dev->preceding_bit[side] = 1; find->sync_marks = 0; dev->state++; return; } } /* If we hadn't found enough set bits but have found a clear bit, null the counter of set bits. */ if (!(dev->last_word[side] & 1)) { find->sync_marks = find->bits_obtained = find->bytes_obtained = 0; find->sync_pos = 0xFFFFFFFF; } } void d86f_find_address_mark_mfm(int drive, int side, find_t *find, uint16_t req_am, uint16_t other_am, uint16_t wrong_am, uint16_t ignore_other_am) { d86f_t *dev = d86f[drive]; d86f_get_bit(drive, side); if (dev->last_word[side] == 0x4489) { find->sync_marks++; find->sync_pos = dev->track_pos; return; } if (wrong_am && (dev->last_word[side] == wrong_am) && (find->sync_marks >= 3)) { dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->error_condition = 0; dev->state = STATE_IDLE; fdc_nodataam(d86f_fdc); return; } if ((dev->last_word[side] == req_am) && (find->sync_marks >= 3)) { if (d86f_word_is_aligned(drive, side, find->sync_pos)) { dev->calc_crc.word = 0xCDB4; fdd_calccrc(decodefm(drive, dev->last_word[side]), &(dev->calc_crc)); find->sync_marks = find->bits_obtained = find->bytes_obtained = 0; find->sync_pos = 0xFFFFFFFF; dev->preceding_bit[side] = dev->last_word[side] & 1; dev->state++; return; } } if ((ignore_other_am & 2) && (dev->last_word[side] == other_am) && (find->sync_marks >= 3)) { if (d86f_word_is_aligned(drive, side, find->sync_pos)) { dev->calc_crc.word = 0xCDB4; fdd_calccrc(decodefm(drive, dev->last_word[side]), &(dev->calc_crc)); find->sync_marks = find->bits_obtained = find->bytes_obtained = 0; find->sync_pos = 0xFFFFFFFF; if (ignore_other_am & 1) { /* Skip mode, let's go back to finding ID. */ dev->state -= 2; } else { /* Not skip mode, process the sector anyway. */ fdc_set_wrong_am(d86f_fdc); dev->preceding_bit[side] = dev->last_word[side] & 1; dev->state++; } return; } } if (dev->last_word[side] != 0x4489) { if (d86f_word_is_aligned(drive, side, find->sync_pos)) { find->sync_marks = find->bits_obtained = find->bytes_obtained = 0; find->sync_pos = 0xFFFFFFFF; } } } /* When writing in MFM mode, we find the beginning of the address mark by looking for 3 0xA1 sync bytes. */ void d86f_write_find_address_mark_mfm(int drive, int side, find_t *find) { d86f_t *dev = d86f[drive]; d86f_get_bit(drive, side); if (dev->last_word[side] == 0x4489) { find->sync_marks++; find->sync_pos = dev->track_pos; if (find->sync_marks == 3) { dev->calc_crc.word = 0xCDB4; dev->preceding_bit[side] = 1; find->sync_marks = 0; dev->state++; return; } } /* If we hadn't found enough address mark sync marks, null the counter. */ if (dev->last_word[side] != 0x4489) { if (d86f_word_is_aligned(drive, side, find->sync_pos)) { find->sync_marks = find->bits_obtained = find->bytes_obtained = 0; find->sync_pos = 0xFFFFFFFF; } } } /* State 2: Read sector ID and CRC*/ void d86f_read_sector_id(int drive, int side, int match) { d86f_t *dev = d86f[drive]; if (dev->id_find.bits_obtained) { if (!(dev->id_find.bits_obtained & 15)) { /* We've got a byte. */ if (dev->id_find.bytes_obtained < 4) { dev->last_sector.byte_array[dev->id_find.bytes_obtained] = decodefm(drive, dev->last_word[side]); fdd_calccrc(dev->last_sector.byte_array[dev->id_find.bytes_obtained], &(dev->calc_crc)); } else if ((dev->id_find.bytes_obtained >= 4) && (dev->id_find.bytes_obtained < 6)) { dev->track_crc.bytes[(dev->id_find.bytes_obtained & 1) ^ 1] = decodefm(drive, dev->last_word[side]); } dev->id_find.bytes_obtained++; if (dev->id_find.bytes_obtained == 6) { /* We've got the ID. */ if ((dev->calc_crc.word != dev->track_crc.word) && (dev->last_sector.dword == dev->req_sector.dword)) { dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = 0; d86f_log("86F: ID CRC error: %04X != %04X (%08X)\n", dev->track_crc.word, dev->calc_crc.word, dev->last_sector.dword); if ((dev->state != STATE_02_READ_ID) && (dev->state != STATE_0A_READ_ID)) { dev->error_condition = 0; dev->state = STATE_IDLE; fdc_headercrcerror(d86f_fdc); } else if (dev->state == STATE_0A_READ_ID) dev->state--; else { dev->error_condition |= 1; /* Mark that there was an ID CRC error. */ dev->state++; } } else if ((dev->calc_crc.word == dev->track_crc.word) && (dev->state == STATE_0A_READ_ID)) { /* CRC is valid and this is a read sector ID command. */ dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = dev->error_condition = 0; fdc_sectorid(d86f_fdc, dev->last_sector.id.c, dev->last_sector.id.h, dev->last_sector.id.r, dev->last_sector.id.n, 0, 0); dev->state = STATE_IDLE; } else { /* CRC is valid. */ dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = 0; dev->id_found |= 1; if ((dev->last_sector.dword == dev->req_sector.dword) || !match) { d86f_handler[drive].set_sector(drive, side, dev->last_sector.id.c, dev->last_sector.id.h, dev->last_sector.id.r, dev->last_sector.id.n); if (dev->state == STATE_02_READ_ID) { /* READ TRACK command, we need some special handling here. */ /* Code corrected: Only the C, H, and N portions of the sector ID are compared, the R portion (the sector number) is ignored. */ if ((dev->last_sector.id.c != fdc_get_read_track_sector(d86f_fdc).id.c) || (dev->last_sector.id.h != fdc_get_read_track_sector(d86f_fdc).id.h) || (dev->last_sector.id.n != fdc_get_read_track_sector(d86f_fdc).id.n)) { /* Mark that the sector ID is not the one expected by the FDC. */ dev->error_condition |= 4; /* Make sure we use the sector size from the FDC. */ dev->last_sector.id.n = fdc_get_read_track_sector(d86f_fdc).id.n; } /* If the two ID's are identical, then we do not need to do anything regarding the sector size. */ } dev->state++; } else { if (dev->last_sector.id.c != dev->req_sector.id.c) { if (dev->last_sector.id.c == 0xFF) { dev->error_condition |= 8; } else { dev->error_condition |= 0x10; } } dev->state--; } } } } } d86f_get_bit(drive, side); dev->id_find.bits_obtained++; } uint8_t d86f_get_data(int drive, int base) { d86f_t *dev = d86f[drive]; int data; int byte_count; if (fdd_get_turbo(drive) && (dev->version == 0x0063)) byte_count = dev->turbo_pos; else byte_count = dev->data_find.bytes_obtained; if (byte_count < (d86f_get_data_len(drive) + base)) { data = fdc_getdata(d86f_fdc, byte_count == (d86f_get_data_len(drive) + base - 1)); if ((data & DMA_OVER) || (data == -1)) { dev->dma_over++; if (data == -1) data = 0; else data &= 0xff; } } else { data = 0; } return data; } void d86f_compare_byte(int drive, uint8_t received_byte, uint8_t disk_byte) { d86f_t *dev = d86f[drive]; switch (fdc_get_compare_condition(d86f_fdc)) { case 0: /* SCAN EQUAL */ if ((received_byte == disk_byte) || (received_byte == 0xFF)) dev->satisfying_bytes++; break; case 1: /* SCAN LOW OR EQUAL */ if ((received_byte <= disk_byte) || (received_byte == 0xFF)) dev->satisfying_bytes++; break; case 2: /* SCAN HIGH OR EQUAL */ if ((received_byte >= disk_byte) || (received_byte == 0xFF)) dev->satisfying_bytes++; break; default: break; } } /* State 4: Read sector data and CRC*/ void d86f_read_sector_data(int drive, int side) { d86f_t *dev = d86f[drive]; int data = 0; int recv_data = 0; int read_status = 0; uint32_t sector_len = dev->last_sector.id.n; uint32_t crc_pos = 0; sector_len = 1 << (7 + sector_len); crc_pos = sector_len + 2; if (dev->data_find.bits_obtained) { if (!(dev->data_find.bits_obtained & 15)) { /* We've got a byte. */ d86f_log("86F: We've got a byte.\n"); if (dev->data_find.bytes_obtained < sector_len) { if (d86f_handler[drive].read_data != NULL) data = d86f_handler[drive].read_data(drive, side, dev->data_find.bytes_obtained); else { #ifdef HACK_FOR_DBASE_III if ((dev->last_sector.id.c == 39) && (dev->last_sector.id.h == 0) && (dev->last_sector.id.r == 5) && (dev->data_find.bytes_obtained >= 272)) data = (random_generate() & 0xff); else #endif data = decodefm(drive, dev->last_word[side]); } if (dev->state == STATE_11_SCAN_DATA) { /* Scan/compare command. */ recv_data = d86f_get_data(drive, 0); d86f_compare_byte(drive, recv_data, data); } else { if (dev->data_find.bytes_obtained < d86f_get_data_len(drive)) { if (dev->state != STATE_16_VERIFY_DATA) { read_status = fdc_data(d86f_fdc, data, dev->data_find.bytes_obtained == (d86f_get_data_len(drive) - 1)); if (read_status == -1) dev->dma_over++; } } } fdd_calccrc(data, &(dev->calc_crc)); } else if (dev->data_find.bytes_obtained < crc_pos) dev->track_crc.bytes[(dev->data_find.bytes_obtained - sector_len) ^ 1] = decodefm(drive, dev->last_word[side]); dev->data_find.bytes_obtained++; if (dev->data_find.bytes_obtained == (crc_pos + fdc_get_gap(d86f_fdc))) { /* We've got the data. */ if ((dev->calc_crc.word != dev->track_crc.word) && (dev->state != STATE_02_READ_DATA)) { d86f_log("86F: Data CRC error: %04X != %04X (%08X)\n", dev->track_crc.word, dev->calc_crc.word, dev->last_sector.dword); dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->error_condition = 0; dev->state = STATE_IDLE; fdc_datacrcerror(d86f_fdc); } else if ((dev->calc_crc.word != dev->track_crc.word) && (dev->state == STATE_02_READ_DATA)) { dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->error_condition |= 2; /* Mark that there was a data error. */ dev->state = STATE_IDLE; fdc_track_finishread(d86f_fdc, dev->error_condition); } else { /* CRC is valid. */ d86f_log("86F: Data CRC OK: %04X == %04X (%08X)\n", dev->track_crc.word, dev->calc_crc.word, dev->last_sector.dword); dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->error_condition = 0; dev->state = STATE_IDLE; if (dev->state == STATE_02_READ_DATA) fdc_track_finishread(d86f_fdc, dev->error_condition); else if (dev->state == STATE_11_SCAN_DATA) fdc_sector_finishcompare(d86f_fdc, (dev->satisfying_bytes == ((128 << ((uint32_t) dev->last_sector.id.n)) - 1)) ? 1 : 0); else fdc_sector_finishread(d86f_fdc); } } } } d86f_get_bit(drive, side); dev->data_find.bits_obtained++; } void d86f_write_sector_data(int drive, int side, int mfm, uint16_t am) { d86f_t *dev = d86f[drive]; uint16_t bit_pos; uint16_t temp; uint32_t sector_len = dev->last_sector.id.n; uint32_t crc_pos = 0; sector_len = (1 << (7 + sector_len)) + 1; crc_pos = sector_len + 2; if (!(dev->data_find.bits_obtained & 15)) { if (dev->data_find.bytes_obtained < crc_pos) { if (!dev->data_find.bytes_obtained) { /* We're writing the address mark. */ dev->current_byte[side] = am; } else if (dev->data_find.bytes_obtained < sector_len) { /* We're in the data field of the sector, read byte from FDC and request new byte. */ dev->current_byte[side] = d86f_get_data(drive, 1); if (!fdc_get_diswr(d86f_fdc)) d86f_handler[drive].write_data(drive, side, dev->data_find.bytes_obtained - 1, dev->current_byte[side]); } else { /* We're in the data field of the sector, use a CRC byte. */ dev->current_byte[side] = dev->calc_crc.bytes[dev->data_find.bytes_obtained & 1]; } dev->current_bit[side] = (15 - (dev->data_find.bits_obtained & 15)) >> 1; /* Write the bit. */ temp = (dev->current_byte[side] >> dev->current_bit[side]) & 1; if ((!temp && !dev->preceding_bit[side]) || !mfm) { temp |= 2; } /* This is an even bit, so write the clock. */ if (!dev->data_find.bytes_obtained) { /* Address mark, write bit directly. */ d86f_put_bit(drive, side, am >> 15); } else { d86f_put_bit(drive, side, temp >> 1); } if (dev->data_find.bytes_obtained < sector_len) { /* This is a data byte, so CRC it. */ if (!dev->data_find.bytes_obtained) { fdd_calccrc(decodefm(drive, am), &(dev->calc_crc)); } else { fdd_calccrc(dev->current_byte[side], &(dev->calc_crc)); } } } } else { if (dev->data_find.bytes_obtained < crc_pos) { /* Encode the bit. */ bit_pos = 15 - (dev->data_find.bits_obtained & 15); dev->current_bit[side] = bit_pos >> 1; temp = (dev->current_byte[side] >> dev->current_bit[side]) & 1; if ((!temp && !dev->preceding_bit[side]) || !mfm) { temp |= 2; } if (!dev->data_find.bytes_obtained) { /* Address mark, write directly. */ d86f_put_bit(drive, side, am >> bit_pos); if (!(bit_pos & 1)) { dev->preceding_bit[side] = am >> bit_pos; } } else { if (bit_pos & 1) { /* Clock bit */ d86f_put_bit(drive, side, temp >> 1); } else { /* Data bit */ d86f_put_bit(drive, side, temp & 1); dev->preceding_bit[side] = temp & 1; } } } if ((dev->data_find.bits_obtained & 15) == 15) { dev->data_find.bytes_obtained++; if (dev->data_find.bytes_obtained == (crc_pos + fdc_get_gap(d86f_fdc))) { /* We've written the data. */ dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->error_condition = 0; dev->state = STATE_IDLE; fdc_sector_finishread(d86f_fdc); return; } } } dev->data_find.bits_obtained++; } void d86f_advance_bit(int drive, int side) { d86f_t *dev = d86f[drive]; dev->track_pos++; dev->track_pos %= d86f_handler[drive].get_raw_size(drive, side); if (dev->track_pos == d86f_handler[drive].index_hole_pos(drive, side)) { d86f_handler[drive].read_revolution(drive); if (dev->state != STATE_IDLE) dev->index_count++; } } void d86f_advance_word(int drive, int side) { d86f_t *dev = d86f[drive]; dev->track_pos += 16; dev->track_pos %= d86f_handler[drive].get_raw_size(drive, side); if ((dev->track_pos == d86f_handler[drive].index_hole_pos(drive, side)) && (dev->state != STATE_IDLE)) dev->index_count++; } void d86f_spin_to_index(int drive, int side) { d86f_t *dev = d86f[drive]; d86f_get_bit(drive, side); d86f_get_bit(drive, side ^ 1); d86f_advance_bit(drive, side); if (dev->track_pos == d86f_handler[drive].index_hole_pos(drive, side)) { if (dev->state == STATE_0D_SPIN_TO_INDEX) { /* When starting format, reset format state to the beginning. */ dev->preceding_bit[side] = 1; dev->format_state = FMT_PRETRK_GAP0; } /* This is to make sure both READ TRACK and FORMAT TRACK command don't end prematurely. */ dev->index_count = 0; dev->state++; } } void d86f_write_direct_common(int drive, int side, uint16_t byte, uint8_t type, uint32_t pos) { d86f_t *dev = d86f[drive]; uint16_t encoded_byte = 0; uint16_t mask_data; uint16_t mask_surface; uint16_t mask_hole; uint16_t mask_fuzzy; decoded_t dbyte; decoded_t dpbyte; if (fdc_get_diswr(d86f_fdc)) return; dbyte.byte = byte & 0xff; dpbyte.byte = dev->preceding_bit[side] & 0xff; if (type == 0) { /* Byte write. */ encoded_byte = d86f_encode_byte(drive, 0, dbyte, dpbyte); if (!d86f_reverse_bytes(drive)) { mask_data = encoded_byte >> 8; encoded_byte &= 0xFF; encoded_byte <<= 8; encoded_byte |= mask_data; } } else { /* Word write. */ encoded_byte = byte; if (d86f_reverse_bytes(drive)) { mask_data = encoded_byte >> 8; encoded_byte &= 0xFF; encoded_byte <<= 8; encoded_byte |= mask_data; } } dev->preceding_bit[side] = encoded_byte & 1; if (d86f_has_surface_desc(drive)) { mask_data = dev->track_encoded_data[side][pos] ^= 0xFFFF; mask_surface = dev->track_surface_data[side][pos]; mask_hole = (mask_surface & mask_data) ^ 0xFFFF; /* This will retain bits that are both fuzzy and 0, therefore physical holes. */ encoded_byte &= mask_hole; /* Filter out physical hole bits from the encoded data. */ mask_data ^= 0xFFFF; /* Invert back so bits 1 are 1 again. */ mask_fuzzy = (mask_surface & mask_data) ^ 0xFFFF; /* All fuzzy bits are 0. */ dev->track_surface_data[side][pos] &= mask_fuzzy; /* Remove fuzzy bits (but not hole bits) from the surface mask, making them regular again. */ } dev->track_encoded_data[side][pos] = encoded_byte; dev->last_word[side] = encoded_byte; } void d86f_write_direct(int drive, int side, uint16_t byte, uint8_t type) { const d86f_t *dev = d86f[drive]; d86f_write_direct_common(drive, side, byte, type, dev->track_pos >> 4); } uint16_t endian_swap(uint16_t word) { uint16_t temp; temp = word & 0xff; temp <<= 8; temp |= (word >> 8); return temp; } void d86f_format_finish(int drive, int side, int mfm, UNUSED(uint16_t sc), uint16_t gap_fill, int do_write) { d86f_t *dev = d86f[drive]; if (mfm && do_write) { if (do_write && (dev->track_pos == d86f_handler[drive].index_hole_pos(drive, side))) { d86f_write_direct_common(drive, side, gap_fill, 0, 0); } } dev->state = STATE_IDLE; if (do_write) d86f_handler[drive].writeback(drive); dev->error_condition = 0; dev->datac = 0; fdc_sector_finishread(d86f_fdc); } void d86f_format_turbo_finish(int drive, UNUSED(int side), int do_write) { d86f_t *dev = d86f[drive]; dev->state = STATE_IDLE; if (do_write) d86f_handler[drive].writeback(drive); dev->error_condition = 0; dev->datac = 0; fdc_sector_finishread(d86f_fdc); } void d86f_format_track(int drive, int side, int do_write) { d86f_t *dev = d86f[drive]; int data; uint16_t max_len; int mfm; uint16_t sc = 0; uint16_t dtl = 0; int gap_sizes[4] = { 0, 0, 0, 0 }; int am_len = 0; int sync_len = 0; uint16_t iam_mfm[4] = { 0x2452, 0x2452, 0x2452, 0x5255 }; uint16_t idam_mfm[4] = { 0x8944, 0x8944, 0x8944, 0x5455 }; uint16_t dataam_mfm[4] = { 0x8944, 0x8944, 0x8944, 0x4555 }; uint16_t iam_fm = 0xFAF7; uint16_t idam_fm = 0x7EF5; uint16_t dataam_fm = 0x6FF5; uint16_t gap_fill = 0x4E; mfm = d86f_is_mfm(drive); am_len = mfm ? 4 : 1; gap_sizes[0] = mfm ? 80 : 40; gap_sizes[1] = mfm ? 50 : 26; gap_sizes[2] = fdc_get_gap2(d86f_fdc, real_drive(d86f_fdc, drive)); gap_sizes[3] = fdc_get_gap(d86f_fdc); sync_len = mfm ? 12 : 6; sc = fdc_get_format_sectors(d86f_fdc); dtl = 128 << fdc_get_format_n(d86f_fdc); gap_fill = mfm ? 0x4E : 0xFF; switch (dev->format_state) { case FMT_POSTTRK_GAP4: max_len = 60000; if (do_write) d86f_write_direct(drive, side, gap_fill, 0); break; case FMT_PRETRK_GAP0: max_len = gap_sizes[0]; if (do_write) d86f_write_direct(drive, side, gap_fill, 0); break; case FMT_SECTOR_ID_SYNC: max_len = sync_len; if (dev->datac <= 3) { data = fdc_getdata(d86f_fdc, 0); if (data != -1) data &= 0xff; if ((data == -1) && (dev->datac < 3)) data = 0; d86f_fdc->format_sector_id.byte_array[dev->datac] = data & 0xff; if (dev->datac == 3) fdc_stop_id_request(d86f_fdc); } fallthrough; case FMT_PRETRK_SYNC: case FMT_SECTOR_DATA_SYNC: max_len = sync_len; if (do_write) d86f_write_direct(drive, side, 0x00, 0); break; case FMT_PRETRK_IAM: max_len = am_len; if (do_write) { if (mfm) d86f_write_direct(drive, side, iam_mfm[dev->datac], 1); else d86f_write_direct(drive, side, iam_fm, 1); } break; case FMT_PRETRK_GAP1: max_len = gap_sizes[1]; if (do_write) d86f_write_direct(drive, side, gap_fill, 0); break; case FMT_SECTOR_IDAM: max_len = am_len; if (mfm) { if (do_write) d86f_write_direct(drive, side, idam_mfm[dev->datac], 1); d86f_calccrc(dev, (dev->datac < 3) ? 0xA1 : 0xFE); } else { if (do_write) d86f_write_direct(drive, side, idam_fm, 1); d86f_calccrc(dev, 0xFE); } break; case FMT_SECTOR_ID: max_len = 4; if (do_write) { d86f_write_direct(drive, side, d86f_fdc->format_sector_id.byte_array[dev->datac], 0); d86f_calccrc(dev, d86f_fdc->format_sector_id.byte_array[dev->datac]); } else { if (dev->datac == 3) { d86f_handler[drive].set_sector(drive, side, d86f_fdc->format_sector_id.id.c, d86f_fdc->format_sector_id.id.h, d86f_fdc->format_sector_id.id.r, d86f_fdc->format_sector_id.id.n); } } break; case FMT_SECTOR_ID_CRC: case FMT_SECTOR_DATA_CRC: max_len = 2; if (do_write) d86f_write_direct(drive, side, dev->calc_crc.bytes[dev->datac ^ 1], 0); break; case FMT_SECTOR_GAP2: max_len = gap_sizes[2]; if (do_write) d86f_write_direct(drive, side, gap_fill, 0); break; case FMT_SECTOR_DATAAM: max_len = am_len; if (mfm) { if (do_write) d86f_write_direct(drive, side, dataam_mfm[dev->datac], 1); d86f_calccrc(dev, (dev->datac < 3) ? 0xA1 : 0xFB); } else { if (do_write) d86f_write_direct(drive, side, dataam_fm, 1); d86f_calccrc(dev, 0xFB); } break; case FMT_SECTOR_DATA: max_len = dtl; if (do_write) { d86f_write_direct(drive, side, dev->fill, 0); d86f_handler[drive].write_data(drive, side, dev->datac, dev->fill); } d86f_calccrc(dev, dev->fill); break; case FMT_SECTOR_GAP3: max_len = gap_sizes[3]; if (do_write) d86f_write_direct(drive, side, gap_fill, 0); break; default: max_len = 0; break; } dev->datac++; d86f_advance_word(drive, side); if ((dev->index_count) && ((dev->format_state < FMT_SECTOR_ID_SYNC) || (dev->format_state > FMT_SECTOR_GAP3))) { d86f_format_finish(drive, side, mfm, sc, gap_fill, do_write); return; } if (dev->datac >= max_len) { dev->datac = 0; dev->format_state++; switch (dev->format_state) { case FMT_SECTOR_ID_SYNC: fdc_request_next_sector_id(d86f_fdc); break; case FMT_SECTOR_IDAM: case FMT_SECTOR_DATAAM: dev->calc_crc.word = 0xffff; break; case FMT_POSTTRK_CHECK: if (dev->index_count) { d86f_format_finish(drive, side, mfm, sc, gap_fill, do_write); return; } dev->sector_count++; if (dev->sector_count < sc) { /* Sector within allotted amount, change state to SECTOR_ID_SYNC. */ dev->format_state = FMT_SECTOR_ID_SYNC; fdc_request_next_sector_id(d86f_fdc); } else { dev->format_state = FMT_POSTTRK_GAP4; dev->sector_count = 0; } break; default: break; } } } void d86f_initialize_last_sector_id(int drive, int c, int h, int r, int n) { d86f_t *dev = d86f[drive]; dev->last_sector.id.c = c; dev->last_sector.id.h = h; dev->last_sector.id.r = r; dev->last_sector.id.n = n; } static uint8_t d86f_sector_flags(int drive, int side, uint8_t c, uint8_t h, uint8_t r, uint8_t n) { const d86f_t *dev = d86f[drive]; sector_t *s; sector_t *t; if (dev->last_side_sector[side]) { s = dev->last_side_sector[side]; while (s) { if ((s->c == c) && (s->h == h) && (s->r == r) && (s->n == n)) return s->flags; if (!s->prev) break; t = s->prev; s = t; } } return 0x00; } void d86f_turbo_read(int drive, int side) { d86f_t *dev = d86f[drive]; uint8_t dat = 0; int recv_data = 0; int read_status = 0; uint8_t flags = d86f_sector_flags(drive, side, dev->req_sector.id.c, dev->req_sector.id.h, dev->req_sector.id.r, dev->req_sector.id.n); if (d86f_handler[drive].read_data != NULL) dat = d86f_handler[drive].read_data(drive, side, dev->turbo_pos); else dat = (random_generate() & 0xff); if (dev->state == STATE_11_SCAN_DATA) { /* Scan/compare command. */ recv_data = d86f_get_data(drive, 0); d86f_compare_byte(drive, recv_data, dat); } else { if (dev->turbo_pos < (128UL << dev->req_sector.id.n)) { if (dev->state != STATE_16_VERIFY_DATA) { read_status = fdc_data(d86f_fdc, dat, dev->turbo_pos == ((128UL << dev->req_sector.id.n) - 1)); if (read_status == -1) dev->dma_over++; } } } dev->turbo_pos++; if (dev->turbo_pos >= (128UL << dev->req_sector.id.n)) { dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; if ((flags & SECTOR_CRC_ERROR) && (dev->state != STATE_02_READ_DATA)) { #ifdef ENABLE_D86F_LOG d86f_log("86F: Data CRC error in turbo mode (%02X)\n", dev->state); #endif dev->error_condition = 0; dev->state = STATE_IDLE; fdc_datacrcerror(d86f_fdc); } else if ((flags & SECTOR_CRC_ERROR) && (dev->state == STATE_02_READ_DATA)) { #ifdef ENABLE_D86F_LOG d86f_log("86F: Data CRC error in turbo mode at READ TRACK command\n"); #endif dev->error_condition |= 2; /* Mark that there was a data error. */ dev->state = STATE_IDLE; fdc_track_finishread(d86f_fdc, dev->error_condition); } else { /* CRC is valid. */ #ifdef ENABLE_D86F_LOG d86f_log("86F: Data CRC OK in turbo mode\n"); #endif dev->error_condition = 0; dev->state = STATE_IDLE; if (dev->state == STATE_11_SCAN_DATA) fdc_sector_finishcompare(d86f_fdc, (dev->satisfying_bytes == ((128 << ((uint32_t) dev->last_sector.id.n)) - 1)) ? 1 : 0); else fdc_sector_finishread(d86f_fdc); } } } void d86f_turbo_write(int drive, int side) { d86f_t *dev = d86f[drive]; uint8_t dat = 0; dat = d86f_get_data(drive, 1); d86f_handler[drive].write_data(drive, side, dev->turbo_pos, dat); dev->turbo_pos++; if (dev->turbo_pos >= (128 << dev->last_sector.id.n)) { /* We've written the data. */ dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->error_condition = 0; dev->state = STATE_IDLE; d86f_handler[drive].writeback(drive); fdc_sector_finishread(d86f_fdc); } } void d86f_turbo_format(int drive, int side, int nop) { d86f_t *dev = d86f[drive]; int dat; uint16_t sc; uint16_t dtl; sc = fdc_get_format_sectors(d86f_fdc); dtl = 128 << fdc_get_format_n(d86f_fdc); if (dev->datac <= 3) { dat = fdc_getdata(d86f_fdc, 0); if (dat != -1) dat &= 0xff; if ((dat == -1) && (dev->datac < 3)) dat = 0; d86f_fdc->format_sector_id.byte_array[dev->datac] = dat & 0xff; if (dev->datac == 3) { fdc_stop_id_request(d86f_fdc); d86f_handler[drive].set_sector(drive, side, d86f_fdc->format_sector_id.id.c, d86f_fdc->format_sector_id.id.h, d86f_fdc->format_sector_id.id.r, d86f_fdc->format_sector_id.id.n); } } else if (dev->datac == 4) { if (!nop) { for (uint16_t i = 0; i < dtl; i++) d86f_handler[drive].write_data(drive, side, i, dev->fill); } dev->sector_count++; } dev->datac++; if (dev->datac == 6) { dev->datac = 0; if (dev->sector_count < sc) { /* Sector within allotted amount. */ fdc_request_next_sector_id(d86f_fdc); } else { dev->state = STATE_IDLE; d86f_format_turbo_finish(drive, side, nop); } } } int d86f_sector_is_present(int drive, int side, uint8_t c, uint8_t h, uint8_t r, uint8_t n) { const d86f_t *dev = d86f[drive]; sector_t *s; sector_t *t; if (dev->last_side_sector[side]) { s = dev->last_side_sector[side]; while (s) { if ((s->c == c) && (s->h == h) && (s->r == r) && (s->n == n)) return 1; if (!s->prev) break; t = s->prev; s = t; } } return 0; } void d86f_turbo_poll(int drive, int side) { d86f_t *dev = d86f[drive]; if ((dev->state != STATE_IDLE) && (dev->state != STATE_SECTOR_NOT_FOUND) && ((dev->state & 0xF8) != 0xE8)) { if (!d86f_can_read_address(drive)) { dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = dev->error_condition = 0; fdc_noidam(d86f_fdc); dev->state = STATE_IDLE; return; } } switch (dev->state) { case STATE_0D_SPIN_TO_INDEX: dev->sector_count = 0; dev->datac = 5; fallthrough; case STATE_02_SPIN_TO_INDEX: dev->state++; return; case STATE_02_FIND_ID: if (!d86f_sector_is_present(drive, side, fdc_get_read_track_sector(d86f_fdc).id.c, fdc_get_read_track_sector(d86f_fdc).id.h, fdc_get_read_track_sector(d86f_fdc).id.r, fdc_get_read_track_sector(d86f_fdc).id.n)) { dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = dev->error_condition = 0; fdc_nosector(d86f_fdc); dev->state = STATE_IDLE; return; } dev->last_sector.id.c = fdc_get_read_track_sector(d86f_fdc).id.c; dev->last_sector.id.h = fdc_get_read_track_sector(d86f_fdc).id.h; dev->last_sector.id.r = fdc_get_read_track_sector(d86f_fdc).id.r; dev->last_sector.id.n = fdc_get_read_track_sector(d86f_fdc).id.n; d86f_handler[drive].set_sector(drive, side, dev->last_sector.id.c, dev->last_sector.id.h, dev->last_sector.id.r, dev->last_sector.id.n); dev->turbo_pos = 0; dev->state++; return; case STATE_05_FIND_ID: case STATE_09_FIND_ID: case STATE_06_FIND_ID: case STATE_0C_FIND_ID: case STATE_11_FIND_ID: case STATE_16_FIND_ID: if (!d86f_sector_is_present(drive, side, dev->req_sector.id.c, dev->req_sector.id.h, dev->req_sector.id.r, dev->req_sector.id.n)) { dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = dev->error_condition = 0; fdc_nosector(d86f_fdc); dev->state = STATE_IDLE; return; } else if (d86f_sector_flags(drive, side, dev->req_sector.id.c, dev->req_sector.id.h, dev->req_sector.id.r, dev->req_sector.id.n) & SECTOR_NO_ID) { dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = dev->error_condition = 0; fdc_noidam(d86f_fdc); dev->state = STATE_IDLE; return; } dev->last_sector.id.c = dev->req_sector.id.c; dev->last_sector.id.h = dev->req_sector.id.h; dev->last_sector.id.r = dev->req_sector.id.r; dev->last_sector.id.n = dev->req_sector.id.n; d86f_handler[drive].set_sector(drive, side, dev->last_sector.id.c, dev->last_sector.id.h, dev->last_sector.id.r, dev->last_sector.id.n); fallthrough; case STATE_0A_FIND_ID: dev->turbo_pos = 0; dev->state++; return; case STATE_0A_READ_ID: dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = dev->error_condition = 0; fdc_sectorid(d86f_fdc, dev->last_sector.id.c, dev->last_sector.id.h, dev->last_sector.id.r, dev->last_sector.id.n, 0, 0); dev->state = STATE_IDLE; break; case STATE_02_READ_ID: case STATE_05_READ_ID: case STATE_09_READ_ID: case STATE_06_READ_ID: case STATE_0C_READ_ID: case STATE_11_READ_ID: case STATE_16_READ_ID: dev->state++; break; case STATE_02_FIND_DATA: case STATE_06_FIND_DATA: case STATE_11_FIND_DATA: case STATE_16_FIND_DATA: case STATE_05_FIND_DATA: case STATE_09_FIND_DATA: case STATE_0C_FIND_DATA: dev->state++; break; case STATE_02_READ_DATA: case STATE_06_READ_DATA: case STATE_0C_READ_DATA: case STATE_11_SCAN_DATA: case STATE_16_VERIFY_DATA: d86f_turbo_read(drive, side); break; case STATE_05_WRITE_DATA: case STATE_09_WRITE_DATA: d86f_turbo_write(drive, side); break; case STATE_0D_FORMAT_TRACK: d86f_turbo_format(drive, side, (side && (d86f_get_sides(drive) != 2))); return; case STATE_IDLE: case STATE_SECTOR_NOT_FOUND: default: break; } } void d86f_poll(int drive) { d86f_t *dev = d86f[drive]; int mfm; int side; side = fdd_get_head(drive); if (!fdd_is_double_sided(drive)) side = 0; mfm = fdc_is_mfm(d86f_fdc); if ((dev->state & 0xF8) == 0xE8) { if (!d86f_can_format(drive)) dev->state = STATE_SECTOR_NOT_FOUND; } if (fdd_get_turbo(drive) && (dev->version == 0x0063)) { d86f_turbo_poll(drive, side); return; } if ((dev->state != STATE_IDLE) && (dev->state != STATE_SECTOR_NOT_FOUND) && ((dev->state & 0xF8) != 0xE8)) { if (!d86f_can_read_address(drive)) dev->state = STATE_SECTOR_NOT_FOUND; } if ((dev->state != STATE_02_SPIN_TO_INDEX) && (dev->state != STATE_0D_SPIN_TO_INDEX)) d86f_get_bit(drive, side ^ 1); switch (dev->state) { case STATE_02_SPIN_TO_INDEX: case STATE_0D_SPIN_TO_INDEX: d86f_spin_to_index(drive, side); return; case STATE_02_FIND_ID: case STATE_05_FIND_ID: case STATE_09_FIND_ID: case STATE_06_FIND_ID: case STATE_0A_FIND_ID: case STATE_0C_FIND_ID: case STATE_11_FIND_ID: case STATE_16_FIND_ID: if (mfm) d86f_find_address_mark_mfm(drive, side, &(dev->id_find), 0x5554, 0, 0, 0); else d86f_find_address_mark_fm(drive, side, &(dev->id_find), 0xF57E, 0, 0, 0); break; case STATE_0A_READ_ID: case STATE_02_READ_ID: d86f_read_sector_id(drive, side, 0); break; case STATE_05_READ_ID: case STATE_09_READ_ID: case STATE_06_READ_ID: case STATE_0C_READ_ID: case STATE_11_READ_ID: case STATE_16_READ_ID: d86f_read_sector_id(drive, side, 1); break; case STATE_02_FIND_DATA: if (mfm) d86f_find_address_mark_mfm(drive, side, &(dev->data_find), 0x5545, 0x554A, 0x5554, 2); else d86f_find_address_mark_fm(drive, side, &(dev->data_find), 0xF56F, 0xF56A, 0xF57E, 2); break; case STATE_06_FIND_DATA: case STATE_11_FIND_DATA: case STATE_16_FIND_DATA: if (mfm) d86f_find_address_mark_mfm(drive, side, &(dev->data_find), 0x5545, 0x554A, 0x5554, fdc_is_sk(d86f_fdc) | 2); else d86f_find_address_mark_fm(drive, side, &(dev->data_find), 0xF56F, 0xF56A, 0xF57E, fdc_is_sk(d86f_fdc) | 2); break; case STATE_05_FIND_DATA: case STATE_09_FIND_DATA: if (mfm) d86f_write_find_address_mark_mfm(drive, side, &(dev->data_find)); else d86f_write_find_address_mark_fm(drive, side, &(dev->data_find)); break; case STATE_0C_FIND_DATA: if (mfm) d86f_find_address_mark_mfm(drive, side, &(dev->data_find), 0x554A, 0x5545, 0x5554, fdc_is_sk(d86f_fdc) | 2); else d86f_find_address_mark_fm(drive, side, &(dev->data_find), 0xF56A, 0xF56F, 0xF57E, fdc_is_sk(d86f_fdc) | 2); break; case STATE_02_READ_DATA: case STATE_06_READ_DATA: case STATE_0C_READ_DATA: case STATE_11_SCAN_DATA: case STATE_16_VERIFY_DATA: d86f_read_sector_data(drive, side); break; case STATE_05_WRITE_DATA: if (mfm) d86f_write_sector_data(drive, side, mfm, 0x5545); else d86f_write_sector_data(drive, side, mfm, 0xF56F); break; case STATE_09_WRITE_DATA: if (mfm) d86f_write_sector_data(drive, side, mfm, 0x554A); else d86f_write_sector_data(drive, side, mfm, 0xF56A); break; case STATE_0D_FORMAT_TRACK: if (!(dev->track_pos & 15)) d86f_format_track(drive, side, (!side || (d86f_get_sides(drive) == 2)) && (dev->version == D86FVER)); return; case STATE_IDLE: case STATE_SECTOR_NOT_FOUND: default: d86f_get_bit(drive, side); break; } d86f_advance_bit(drive, side); if (d86f_wrong_densel(drive) && (dev->state != STATE_IDLE)) { dev->state = STATE_IDLE; fdc_noidam(d86f_fdc); return; } if ((dev->index_count == 2) && (dev->state != STATE_IDLE)) { switch (dev->state) { case STATE_0A_FIND_ID: case STATE_SECTOR_NOT_FOUND: dev->state = STATE_IDLE; fdc_noidam(d86f_fdc); break; case STATE_02_FIND_DATA: case STATE_06_FIND_DATA: case STATE_11_FIND_DATA: case STATE_16_FIND_DATA: case STATE_05_FIND_DATA: case STATE_09_FIND_DATA: case STATE_0C_FIND_DATA: dev->state = STATE_IDLE; fdc_nodataam(d86f_fdc); break; case STATE_02_SPIN_TO_INDEX: case STATE_02_READ_DATA: case STATE_05_WRITE_DATA: case STATE_06_READ_DATA: case STATE_09_WRITE_DATA: case STATE_0C_READ_DATA: case STATE_0D_SPIN_TO_INDEX: case STATE_0D_FORMAT_TRACK: case STATE_11_SCAN_DATA: case STATE_16_VERIFY_DATA: /* In these states, we should *NEVER* care about how many index pulses there have been. */ break; default: dev->state = STATE_IDLE; if (dev->id_found) { if (dev->error_condition & 0x18) { if ((dev->error_condition & 0x18) == 0x08) fdc_badcylinder(d86f_fdc); if ((dev->error_condition & 0x10) == 0x10) fdc_wrongcylinder(d86f_fdc); else fdc_nosector(d86f_fdc); } else fdc_nosector(d86f_fdc); } else fdc_noidam(d86f_fdc); break; } } } void d86f_reset_index_hole_pos(int drive, int side) { d86f_t *dev = d86f[drive]; dev->index_hole_pos[side] = 0; } uint16_t d86f_prepare_pretrack(int drive, int side, int iso) { d86f_t *dev = d86f[drive]; uint16_t pos; int mfm; int real_gap0_len; int sync_len; int real_gap1_len; uint16_t gap_fill; uint32_t raw_size; uint16_t iam_fm = 0xFAF7; uint16_t iam_mfm = 0x5255; mfm = d86f_is_mfm(drive); real_gap0_len = mfm ? 80 : 40; sync_len = mfm ? 12 : 6; real_gap1_len = mfm ? 50 : 26; gap_fill = mfm ? 0x4E : 0xFF; raw_size = d86f_handler[drive].get_raw_size(drive, side); if (raw_size & 15) raw_size = (raw_size >> 4) + 1; else raw_size = (raw_size >> 4); dev->index_hole_pos[side] = 0; d86f_destroy_linked_lists(drive, side); for (uint32_t i = 0; i < raw_size; i++) d86f_write_direct_common(drive, side, gap_fill, 0, i); pos = 0; if (!iso) { for (int i = 0; i < real_gap0_len; i++) { d86f_write_direct_common(drive, side, gap_fill, 0, pos); pos = (pos + 1) % raw_size; } for (int i = 0; i < sync_len; i++) { d86f_write_direct_common(drive, side, 0, 0, pos); pos = (pos + 1) % raw_size; } if (mfm) { for (uint8_t i = 0; i < 3; i++) { d86f_write_direct_common(drive, side, 0x2452, 1, pos); pos = (pos + 1) % raw_size; } } d86f_write_direct_common(drive, side, mfm ? iam_mfm : iam_fm, 1, pos); pos = (pos + 1) % raw_size; } for (int i = 0; i < real_gap1_len; i++) { d86f_write_direct_common(drive, side, gap_fill, 0, pos); pos = (pos + 1) % raw_size; } return pos; } uint16_t d86f_prepare_sector(int drive, int side, int prev_pos, uint8_t *id_buf, uint8_t *data_buf, int data_len, int gap2, int gap3, int flags) { d86f_t *dev = d86f[drive]; uint16_t pos; int i; sector_t *s; int real_gap2_len = gap2; int real_gap3_len = gap3; int mfm; int sync_len; uint16_t gap_fill; uint32_t raw_size; uint16_t idam_fm = 0x7EF5; uint16_t dataam_fm = 0x6FF5; uint16_t datadam_fm = 0x6AF5; uint16_t idam_mfm = 0x5455; uint16_t dataam_mfm = 0x4555; uint16_t datadam_mfm = 0x4A55; if (fdd_get_turbo(drive) && (dev->version == 0x0063)) { s = (sector_t *) malloc(sizeof(sector_t)); memset(s, 0, sizeof(sector_t)); s->c = id_buf[0]; s->h = id_buf[1]; s->r = id_buf[2]; s->n = id_buf[3]; s->flags = flags; if (dev->last_side_sector[side]) s->prev = dev->last_side_sector[side]; dev->last_side_sector[side] = s; } mfm = d86f_is_mfm(drive); gap_fill = mfm ? 0x4E : 0xFF; raw_size = d86f_handler[drive].get_raw_size(drive, side); if (raw_size & 15) raw_size = (raw_size >> 4) + 1; else raw_size = (raw_size >> 4); pos = prev_pos; sync_len = mfm ? 12 : 6; if (!(flags & SECTOR_NO_ID)) { for (i = 0; i < sync_len; i++) { d86f_write_direct_common(drive, side, 0, 0, pos); pos = (pos + 1) % raw_size; } dev->calc_crc.word = 0xffff; if (mfm) { for (i = 0; i < 3; i++) { d86f_write_direct_common(drive, side, 0x8944, 1, pos); pos = (pos + 1) % raw_size; d86f_calccrc(dev, 0xA1); } } d86f_write_direct_common(drive, side, mfm ? idam_mfm : idam_fm, 1, pos); pos = (pos + 1) % raw_size; d86f_calccrc(dev, 0xFE); for (i = 0; i < 4; i++) { d86f_write_direct_common(drive, side, id_buf[i], 0, pos); pos = (pos + 1) % raw_size; d86f_calccrc(dev, id_buf[i]); } for (i = 1; i >= 0; i--) { d86f_write_direct_common(drive, side, dev->calc_crc.bytes[i], 0, pos); pos = (pos + 1) % raw_size; } for (i = 0; i < real_gap2_len; i++) { d86f_write_direct_common(drive, side, gap_fill, 0, pos); pos = (pos + 1) % raw_size; } } if (!(flags & SECTOR_NO_DATA)) { for (i = 0; i < sync_len; i++) { d86f_write_direct_common(drive, side, 0, 0, pos); pos = (pos + 1) % raw_size; } dev->calc_crc.word = 0xffff; if (mfm) { for (i = 0; i < 3; i++) { d86f_write_direct_common(drive, side, 0x8944, 1, pos); pos = (pos + 1) % raw_size; d86f_calccrc(dev, 0xA1); } } d86f_write_direct_common(drive, side, mfm ? ((flags & SECTOR_DELETED_DATA) ? datadam_mfm : dataam_mfm) : ((flags & SECTOR_DELETED_DATA) ? datadam_fm : dataam_fm), 1, pos); pos = (pos + 1) % raw_size; d86f_calccrc(dev, (flags & SECTOR_DELETED_DATA) ? 0xF8 : 0xFB); if (data_len > 0) { for (i = 0; i < data_len; i++) { d86f_write_direct_common(drive, side, data_buf[i], 0, pos); pos = (pos + 1) % raw_size; d86f_calccrc(dev, data_buf[i]); } if (!(flags & SECTOR_CRC_ERROR)) { for (i = 1; i >= 0; i--) { d86f_write_direct_common(drive, side, dev->calc_crc.bytes[i], 0, pos); pos = (pos + 1) % raw_size; } } for (i = 0; i < real_gap3_len; i++) { d86f_write_direct_common(drive, side, gap_fill, 0, pos); pos = (pos + 1) % raw_size; } } } return pos; } /* * Note on handling of tracks on thick track drives: * * - On seek, encoded data is constructed from both (track << 1) and * ((track << 1) + 1); * * - Any bits that differ are treated as thus: * - Both are regular but contents differ -> Output is fuzzy; * - One is regular and one is fuzzy -> Output is fuzzy; * - Both are fuzzy -> Output is fuzzy; * - Both are physical holes -> Output is a physical hole; * - One is regular and one is a physical hole -> Output is fuzzy, * the hole half is handled appropriately on writeback; * - One is fuzzy and one is a physical hole -> Output is fuzzy, * the hole half is handled appropriately on writeback; * - On write back, apart from the above notes, the final two tracks * are written; * - Destination ALWAYS has surface data even if the image does not. * * In case of a thin track drive, tracks are handled normally. */ void d86f_construct_encoded_buffer(int drive, int side) { d86f_t *dev = d86f[drive]; /* *_fuzm are fuzzy bit masks, *_holm are hole masks, dst_neim are masks is mask for bits that are neither fuzzy nor holes in both, and src1_d and src2_d are filtered source data. */ uint16_t src1_fuzm; uint16_t src2_fuzm; uint16_t dst_fuzm; uint16_t src1_holm; uint16_t src2_holm; uint16_t dst_holm; uint16_t dst_neim; uint16_t src1_d; uint16_t src2_d; uint32_t len; uint16_t *dst = dev->track_encoded_data[side]; uint16_t *dst_s = dev->track_surface_data[side]; const uint16_t *src1 = dev->thin_track_encoded_data[0][side]; const uint16_t *src1_s = dev->thin_track_surface_data[0][side]; const uint16_t *src2 = dev->thin_track_encoded_data[1][side]; const uint16_t *src2_s = dev->thin_track_surface_data[1][side]; len = d86f_get_array_size(drive, side, 1); for (uint32_t i = 0; i < len; i++) { /* The two bits differ. */ if (d86f_has_surface_desc(drive)) { /* Source image has surface description data, so we have some more handling to do. */ src1_fuzm = src1[i] & src1_s[i]; src2_fuzm = src2[i] & src2_s[i]; dst_fuzm = src1_fuzm | src2_fuzm; /* The bits that remain set are fuzzy in either one or the other or both. */ src1_holm = src1[i] | (src1_s[i] ^ 0xffff); src2_holm = src2[i] | (src2_s[i] ^ 0xffff); dst_holm = (src1_holm & src2_holm) ^ 0xffff; /* The bits that remain set are holes in both. */ dst_neim = (dst_fuzm | dst_holm) ^ 0xffff; /* The bits that remain set are those that are neither fuzzy nor are holes in both. */ src1_d = src1[i] & dst_neim; src2_d = src2[i] & dst_neim; dst_s[i] = (dst_neim ^ 0xffff); /* The set bits are those that are either fuzzy or are holes in both. */ dst[i] = (src1_d | src2_d); /* Initial data is remaining data from Source 1 and Source 2. */ dst[i] |= dst_fuzm; /* Add to it the fuzzy bytes (holes have surface bit set but data bit clear). */ } else { /* No surface data, the handling is much simpler - a simple OR. */ dst[i] = src1[i] | src2[i]; dst_s[i] = 0; } } } /* Decomposition is easier since we at most have to care about the holes. */ void d86f_decompose_encoded_buffer(int drive, int side) { d86f_t *dev = d86f[drive]; uint16_t temp; uint16_t temp2; uint32_t len; const uint16_t *dst = dev->track_encoded_data[side]; uint16_t *src1 = dev->thin_track_encoded_data[0][side]; uint16_t *src1_s = dev->thin_track_surface_data[0][side]; uint16_t *src2 = dev->thin_track_encoded_data[1][side]; uint16_t *src2_s = dev->thin_track_surface_data[1][side]; dst = d86f_handler[drive].encoded_data(drive, side); len = d86f_get_array_size(drive, side, 1); for (uint32_t i = 0; i < len; i++) { if (d86f_has_surface_desc(drive)) { /* Source image has surface description data, so we have some more handling to do. We need hole masks for both buffers. Holes have data bit clear and surface bit set. */ temp = src1[i] & (src1_s[i] ^ 0xffff); temp2 = src2[i] & (src2_s[i] ^ 0xffff); src1[i] = dst[i] & temp; src1_s[i] = temp ^ 0xffff; src2[i] = dst[i] & temp2; src2_s[i] = temp2 ^ 0xffff; } else { src1[i] = src2[i] = dst[i]; } } } int d86f_track_header_size(int drive) { int temp = 6; if (d86f_has_extra_bit_cells(drive)) temp += 4; return temp; } void d86f_read_track(int drive, int track, int thin_track, int side, uint16_t *da, uint16_t *sa) { d86f_t *dev = d86f[drive]; int logical_track = 0; int array_size = 0; if (d86f_get_sides(drive) == 2) logical_track = ((track + thin_track) << 1) + side; else logical_track = track + thin_track; if (dev->track_offset[logical_track]) { if (!thin_track) { if (fseek(dev->fp, dev->track_offset[logical_track], SEEK_SET) == -1) fatal("d86f_read_track(): Error seeking to offset dev->track_offset[logical_track]\n"); if (fread(&(dev->side_flags[side]), 1, 2, dev->fp) != 2) fatal("d86f_read_track(): Error reading side flags\n"); if (d86f_has_extra_bit_cells(drive)) { if (fread(&(dev->extra_bit_cells[side]), 1, 4, dev->fp) != 4) fatal("d86f_read_track(): Error reading number of extra bit cells\n"); /* If RPM shift is 0% and direction is 1, do not adjust extra bit cells, as that is the whole track length. */ if (d86f_get_rpm_mode(drive) || !d86f_get_speed_shift_dir(drive)) { if (dev->extra_bit_cells[side] < -32768) dev->extra_bit_cells[side] = -32768; if (dev->extra_bit_cells[side] > 32768) dev->extra_bit_cells[side] = 32768; } } else dev->extra_bit_cells[side] = 0; (void) !fread(&(dev->index_hole_pos[side]), 4, 1, dev->fp); } else fseek(dev->fp, dev->track_offset[logical_track] + d86f_track_header_size(drive), SEEK_SET); array_size = d86f_get_array_size(drive, side, 0); (void) !fread(da, 1, array_size, dev->fp); if (d86f_has_surface_desc(drive)) (void) !fread(sa, 1, array_size, dev->fp); } else { if (!thin_track) { switch ((dev->disk_flags >> 1) & 3) { default: case 0: dev->side_flags[side] = 0x0A; break; case 1: dev->side_flags[side] = 0x00; break; case 2: case 3: dev->side_flags[side] = 0x03; break; } dev->extra_bit_cells[side] = 0; } } } void d86f_zero_track(int drive) { d86f_t *dev = d86f[drive]; int sides; sides = d86f_get_sides(drive); for (int side = 0; side < sides; side++) { if (d86f_has_surface_desc(drive)) memset(dev->track_surface_data[side], 0, 106096); memset(dev->track_encoded_data[side], 0, 106096); } } void d86f_seek(int drive, int track) { d86f_t *dev = d86f[drive]; int sides; int side; int thin_track; sides = d86f_get_sides(drive); /* If the drive has thick tracks, shift the track number by 1. */ if (!fdd_doublestep_40(drive)) { track <<= 1; for (thin_track = 0; thin_track < sides; thin_track++) { for (side = 0; side < sides; side++) { if (d86f_has_surface_desc(drive)) memset(dev->thin_track_surface_data[thin_track][side], 0, 106096); memset(dev->thin_track_encoded_data[thin_track][side], 0, 106096); } } } d86f_zero_track(drive); dev->cur_track = track; if (!fdd_doublestep_40(drive)) { for (side = 0; side < sides; side++) { for (thin_track = 0; thin_track < 2; thin_track++) d86f_read_track(drive, track, thin_track, side, dev->thin_track_encoded_data[thin_track][side], dev->thin_track_surface_data[thin_track][side]); d86f_construct_encoded_buffer(drive, side); } } else { for (side = 0; side < sides; side++) d86f_read_track(drive, track, 0, side, dev->track_encoded_data[side], dev->track_surface_data[side]); } dev->state = STATE_IDLE; } void d86f_write_track(int drive, FILE **fp, int side, uint16_t *da0, uint16_t *sa0) { uint32_t array_size = d86f_get_array_size(drive, side, 0); uint16_t side_flags = d86f_handler[drive].side_flags(drive); uint32_t extra_bit_cells = d86f_handler[drive].extra_bit_cells(drive, side); uint32_t index_hole_pos = d86f_handler[drive].index_hole_pos(drive, side); fwrite(&side_flags, 1, 2, *fp); if (d86f_has_extra_bit_cells(drive)) fwrite(&extra_bit_cells, 1, 4, *fp); fwrite(&index_hole_pos, 1, 4, *fp); fwrite(da0, 1, array_size, *fp); if (d86f_has_surface_desc(drive)) fwrite(sa0, 1, array_size, *fp); } int d86f_get_track_table_size(int drive) { int temp = 2048; if (d86f_get_sides(drive) == 1) temp >>= 1; return temp; } void d86f_set_cur_track(int drive, int track) { d86f_t *dev = d86f[drive]; dev->cur_track = track; } void d86f_write_tracks(int drive, FILE **fp, uint32_t *track_table) { d86f_t *dev = d86f[drive]; int sides; int fdd_side; int side; int logical_track = 0; uint32_t *tbl; tbl = dev->track_offset; fdd_side = fdd_get_head(drive); sides = d86f_get_sides(drive); if (track_table != NULL) tbl = track_table; if (!fdd_doublestep_40(drive)) { d86f_decompose_encoded_buffer(drive, 0); if (sides == 2) d86f_decompose_encoded_buffer(drive, 1); for (uint8_t thin_track = 0; thin_track < 2; thin_track++) { for (side = 0; side < sides; side++) { fdd_set_head(drive, side); if (sides == 2) logical_track = ((dev->cur_track + thin_track) << 1) + side; else logical_track = dev->cur_track + thin_track; if (track_table && !tbl[logical_track]) { fseek(*fp, 0, SEEK_END); tbl[logical_track] = ftell(*fp); } if (tbl[logical_track]) { fseek(*fp, tbl[logical_track], SEEK_SET); d86f_write_track(drive, fp, side, dev->thin_track_encoded_data[thin_track][side], dev->thin_track_surface_data[thin_track][side]); } } } } else { for (side = 0; side < sides; side++) { fdd_set_head(drive, side); if (sides == 2) logical_track = (dev->cur_track << 1) + side; else logical_track = dev->cur_track; if (track_table && !tbl[logical_track]) { fseek(*fp, 0, SEEK_END); tbl[logical_track] = ftell(*fp); } if (tbl[logical_track]) { if (fseek(*fp, tbl[logical_track], SEEK_SET) == -1) fatal("d86f_write_tracks(): Error seeking to offset tbl[logical_track]\n"); d86f_write_track(drive, fp, side, d86f_handler[drive].encoded_data(drive, side), dev->track_surface_data[side]); } } } fdd_set_head(drive, fdd_side); } void d86f_writeback(int drive) { d86f_t *dev = d86f[drive]; uint8_t header[32]; int header_size; int size; #ifdef D86F_COMPRESS uint32_t len; int ret = 0; FILE *cf; #endif header_size = d86f_header_size(drive); if (!dev->fp) return; /* First write the track offsets table. */ if (fseek(dev->fp, 0, SEEK_SET) == -1) fatal("86F write_back(): Error seeking to the beginning of the file\n"); if (fread(header, 1, header_size, dev->fp) != header_size) fatal("86F write_back(): Error reading header size\n"); if (fseek(dev->fp, 8, SEEK_SET) == -1) fatal("86F write_back(): Error seeking\n"); size = d86f_get_track_table_size(drive); if (fwrite(dev->track_offset, 1, size, dev->fp) != size) fatal("86F write_back(): Error writing data\n"); d86f_write_tracks(drive, &dev->fp, NULL); #ifdef D86F_COMPRESS if (dev->is_compressed) { /* The image is compressed. */ /* Open the original, compressed file. */ cf = plat_fopen(dev->original_file_name, L"wb"); /* Write the header to the original file. */ fwrite(header, 1, header_size, cf); fseek(dev->fp, 0, SEEK_END); len = ftell(dev->fp); len -= header_size; fseek(dev->fp, header_size, SEEK_SET); /* Compress data from the temporary uncompressed file to the original, compressed file. */ dev->filebuf = (uint8_t *) malloc(len); dev->outbuf = (uint8_t *) malloc(len - 1); fread(dev->filebuf, 1, len, dev->fp); ret = lzf_compress(dev->filebuf, len, dev->outbuf, len - 1); if (!ret) d86f_log("86F: Error compressing file\n"); fwrite(dev->outbuf, 1, ret, cf); free(dev->outbuf); free(dev->filebuf); } #endif } void d86f_stop(int drive) { d86f_t *dev = d86f[drive]; if (dev) dev->state = STATE_IDLE; } int d86f_common_command(int drive, int sector, int track, int side, UNUSED(int rate), int sector_size) { d86f_t *dev = d86f[drive]; d86f_log("d86f_common_command (drive %i): fdc_period=%i img_period=%i rate=%i sector=%i track=%i side=%i\n", drive, fdc_get_bitcell_period(d86f_fdc), d86f_get_bitcell_period(drive), rate, sector, track, side); dev->req_sector.id.c = track; dev->req_sector.id.h = side; if (sector == SECTOR_FIRST) dev->req_sector.id.r = 1; else if (sector == SECTOR_NEXT) dev->req_sector.id.r++; else dev->req_sector.id.r = sector; dev->req_sector.id.n = sector_size; if (fdd_get_head(drive) && (d86f_get_sides(drive) == 1)) { fdc_noidam(d86f_fdc); dev->state = STATE_IDLE; dev->index_count = 0; return 0; } dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = 0; dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->index_count = dev->error_condition = dev->satisfying_bytes = 0; dev->id_found = 0; dev->dma_over = 0; return 1; } void d86f_readsector(int drive, int sector, int track, int side, int rate, int sector_size) { d86f_t *dev = d86f[drive]; int ret = 0; ret = d86f_common_command(drive, sector, track, side, rate, sector_size); if (!ret) return; if (sector == SECTOR_FIRST) dev->state = STATE_02_SPIN_TO_INDEX; else if (sector == SECTOR_NEXT) dev->state = STATE_02_FIND_ID; else dev->state = fdc_is_deleted(d86f_fdc) ? STATE_0C_FIND_ID : (fdc_is_verify(d86f_fdc) ? STATE_16_FIND_ID : STATE_06_FIND_ID); } void d86f_writesector(int drive, int sector, int track, int side, int rate, int sector_size) { d86f_t *dev = d86f[drive]; int ret = 0; if (writeprot[drive]) { fdc_writeprotect(d86f_fdc); dev->state = STATE_IDLE; dev->index_count = 0; return; } ret = d86f_common_command(drive, sector, track, side, rate, sector_size); if (!ret) return; dev->state = fdc_is_deleted(d86f_fdc) ? STATE_09_FIND_ID : STATE_05_FIND_ID; } void d86f_comparesector(int drive, int sector, int track, int side, int rate, int sector_size) { d86f_t *dev = d86f[drive]; int ret = 0; ret = d86f_common_command(drive, sector, track, side, rate, sector_size); if (!ret) return; dev->state = STATE_11_FIND_ID; } void d86f_readaddress(int drive, UNUSED(int side), UNUSED(int rate)) { d86f_t *dev = d86f[drive]; if (fdd_get_head(drive) && (d86f_get_sides(drive) == 1)) { fdc_noidam(d86f_fdc); dev->state = STATE_IDLE; dev->index_count = 0; return; } dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = 0; dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->index_count = dev->error_condition = dev->satisfying_bytes = 0; dev->id_found = 0; dev->dma_over = 0; dev->state = STATE_0A_FIND_ID; } void d86f_add_track(int drive, int track, int side) { d86f_t *dev = d86f[drive]; uint32_t array_size; int logical_track; array_size = d86f_get_array_size(drive, side, 0); if (d86f_get_sides(drive) == 2) { logical_track = (track << 1) + side; } else { if (side) return; logical_track = track; } if (!dev->track_offset[logical_track]) { /* Track is absent from the file, let's add it. */ dev->track_offset[logical_track] = dev->file_size; dev->file_size += (array_size + 6); if (d86f_has_extra_bit_cells(drive)) dev->file_size += 4; if (d86f_has_surface_desc(drive)) dev->file_size += array_size; } } void d86f_common_format(int drive, int side, UNUSED(int rate), uint8_t fill, int proxy) { d86f_t *dev = d86f[drive]; uint16_t temp; uint16_t temp2; uint32_t array_size; if (writeprot[drive]) { fdc_writeprotect(d86f_fdc); dev->state = STATE_IDLE; dev->index_count = 0; return; } if (!d86f_can_format(drive)) { fdc_cannotformat(d86f_fdc); dev->state = STATE_IDLE; dev->index_count = 0; return; } if (!side || (d86f_get_sides(drive) == 2)) { if (!proxy) { d86f_reset_index_hole_pos(drive, side); if (dev->cur_track > 256) { fdc_writeprotect(d86f_fdc); dev->state = STATE_IDLE; dev->index_count = 0; return; } array_size = d86f_get_array_size(drive, side, 0); if (d86f_has_surface_desc(drive)) { /* Preserve the physical holes but get rid of the fuzzy bytes. */ for (uint32_t i = 0; i < array_size; i++) { temp = dev->track_encoded_data[side][i] ^ 0xffff; temp2 = dev->track_surface_data[side][i]; temp &= temp2; dev->track_surface_data[side][i] = temp; } } /* Zero the data buffer. */ memset(dev->track_encoded_data[side], 0, array_size); d86f_add_track(drive, dev->cur_track, side); if (!fdd_doublestep_40(drive)) d86f_add_track(drive, dev->cur_track + 1, side); } } dev->fill = fill; if (!proxy) { dev->side_flags[side] = 0; dev->side_flags[side] |= (fdd_getrpm(real_drive(d86f_fdc, drive)) == 360) ? 0x20 : 0; dev->side_flags[side] |= fdc_get_bit_rate(d86f_fdc); dev->side_flags[side] |= fdc_is_mfm(d86f_fdc) ? 8 : 0; dev->index_hole_pos[side] = 0; } dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = 0; dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0; dev->index_count = dev->error_condition = dev->satisfying_bytes = dev->sector_count = 0; dev->dma_over = 0; dev->state = STATE_0D_SPIN_TO_INDEX; } void d86f_proxy_format(int drive, int side, int rate, uint8_t fill) { d86f_common_format(drive, side, rate, fill, 1); } void d86f_format(int drive, int side, int rate, uint8_t fill) { d86f_common_format(drive, side, rate, fill, 0); } void d86f_common_handlers(int drive) { drives[drive].readsector = d86f_readsector; drives[drive].writesector = d86f_writesector; drives[drive].comparesector = d86f_comparesector; drives[drive].readaddress = d86f_readaddress; drives[drive].byteperiod = d86f_byteperiod; drives[drive].poll = d86f_poll; drives[drive].format = d86f_proxy_format; drives[drive].stop = d86f_stop; } int d86f_export(int drive, char *fn) { uint32_t tt[512]; d86f_t *dev = d86f[drive]; d86f_t *temp86; FILE *fp; int tracks = 86; int inc = 1; uint32_t magic = 0x46423638; uint16_t version = 0x020C; uint16_t disk_flags = d86f_handler[drive].disk_flags(drive); memset(tt, 0, 512 * sizeof(uint32_t)); fp = plat_fopen(fn, "wb"); if (!fp) return 0; /* Allocate a temporary drive for conversion. */ temp86 = (d86f_t *) malloc(sizeof(d86f_t)); memcpy(temp86, dev, sizeof(d86f_t)); fwrite(&magic, 4, 1, fp); fwrite(&version, 2, 1, fp); fwrite(&disk_flags, 2, 1, fp); fwrite(tt, 1, ((d86f_get_sides(drive) == 2) ? 2048 : 1024), fp); /* In the case of a thick track drive, always increment track by two, since two tracks are going to get output at once. */ if (!fdd_doublestep_40(drive)) inc = 2; for (int i = 0; i < tracks; i += inc) { if (inc == 2) fdd_do_seek(drive, i >> 1); else fdd_do_seek(drive, i); dev->cur_track = i; d86f_write_tracks(drive, &fp, tt); } fclose(fp); fp = plat_fopen(fn, "rb+"); fseek(fp, 8, SEEK_SET); fwrite(tt, 1, ((d86f_get_sides(drive) == 2) ? 2048 : 1024), fp); fclose(fp); fdd_do_seek(drive, fdd_current_track(drive)); /* Restore the drive from temp. */ memcpy(dev, temp86, sizeof(d86f_t)); free(temp86); return 1; } void d86f_load(int drive, char *fn) { d86f_t *dev = d86f[drive]; uint32_t magic = 0; uint32_t len = 0; #ifdef D86F_COMPRESS char temp_file_name[2048]; uint16_t temp = 0; FILE *tf; #endif d86f_unregister(drive); writeprot[drive] = 0; dev->fp = plat_fopen(fn, "rb+"); if (!dev->fp) { dev->fp = plat_fopen(fn, "rb"); if (!dev->fp) { memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } writeprot[drive] = 1; } if (ui_writeprot[drive]) { writeprot[drive] = 1; } fwriteprot[drive] = writeprot[drive]; fseek(dev->fp, 0, SEEK_END); len = ftell(dev->fp); fseek(dev->fp, 0, SEEK_SET); (void) !fread(&magic, 4, 1, dev->fp); if (len < 16) { /* File is WAY too small, abort. */ fclose(dev->fp); dev->fp = NULL; memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } if ((magic != 0x46423638) && (magic != 0x66623638)) { /* File is not of the valid format, abort. */ d86f_log("86F: Unrecognized magic bytes: %08X\n", magic); fclose(dev->fp); memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } if (fread(&(dev->version), 1, 2, dev->fp) != 2) fatal("d86f_load(): Error reading format version\n"); if (dev->version != D86FVER) { /* File is not of a recognized format version, abort. */ if (dev->version == 0x0063) { d86f_log("86F: File has emulator-internal version 0.99, this version is not valid in a file\n"); } else if ((dev->version >= 0x0100) && (dev->version < D86FVER)) { d86f_log("86F: No longer supported development file version: %i.%02i\n", dev->version >> 8, dev->version & 0xff); } else { d86f_log("86F: Unrecognized file version: %i.%02i\n", dev->version >> 8, dev->version & 0xff); } fclose(dev->fp); dev->fp = NULL; memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } else { d86f_log("86F: Recognized file version: %i.%02i\n", dev->version >> 8, dev->version & 0xff); } (void) !fread(&(dev->disk_flags), 2, 1, dev->fp); if (d86f_has_surface_desc(drive)) { for (uint8_t i = 0; i < 2; i++) dev->track_surface_data[i] = (uint16_t *) malloc(53048 * sizeof(uint16_t)); for (uint8_t i = 0; i < 2; i++) { for (uint8_t j = 0; j < 2; j++) dev->thin_track_surface_data[i][j] = (uint16_t *) malloc(53048 * sizeof(uint16_t)); } } #ifdef D86F_COMPRESS dev->is_compressed = (magic == 0x66623638) ? 1 : 0; if ((len < 51052) && !dev->is_compressed) { #else if (len < 51052) { #endif /* File too small, abort. */ fclose(dev->fp); dev->fp = NULL; memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } #ifdef DO_CRC64 fseek(dev->fp, 8, SEEK_SET); fread(&read_crc64, 1, 8, dev->fp); fseek(dev->fp, 0, SEEK_SET); crc64 = 0xffffffffffffffff; dev->filebuf = malloc(len); fread(dev->filebuf, 1, len, dev->fp); *(uint64_t *) &(dev->filebuf[8]) = 0xffffffffffffffff; crc64 = (uint64_t) crc64speed(0, dev->filebuf, len); free(dev->filebuf); if (crc64 != read_crc64) { d86f_log("86F: CRC64 error\n"); fclose(dev->fp); dev->fp = NULL; memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } #endif #ifdef D86F_COMPRESS if (dev->is_compressed) { memcpy(temp_file_name, drive ? nvr_path("TEMP$$$1.$$$") : nvr_path("TEMP$$$0.$$$"), 256); memcpy(dev->original_file_name, fn, strlen(fn) + 1); fclose(dev->fp); dev->fp = NULL; dev->fp = plat_fopen(temp_file_name, "wb"); if (!dev->fp) { d86f_log("86F: Unable to create temporary decompressed file\n"); memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } tf = plat_fopen(fn, "rb"); for (uint8_t i = 0; i < 8; i++) { fread(&temp, 1, 2, tf); fwrite(&temp, 1, 2, dev->fp); } dev->filebuf = (uint8_t *) malloc(len); dev->outbuf = (uint8_t *) malloc(67108864); fread(dev->filebuf, 1, len, tf); temp = lzf_decompress(dev->filebuf, len, dev->outbuf, 67108864); if (temp) { fwrite(dev->outbuf, 1, temp, dev->fp); } free(dev->outbuf); free(dev->filebuf); fclose(tf); fclose(dev->fp); dev->fp = NULL; if (!temp) { d86f_log("86F: Error decompressing file\n"); plat_remove(temp_file_name); memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } dev->fp = plat_fopen(temp_file_name, "rb+"); } #endif if (dev->disk_flags & 0x100) { /* Zoned disk. */ d86f_log("86F: Disk is zoned (Apple or Sony)\n"); fclose(dev->fp); dev->fp = NULL; #ifdef D86F_COMPRESS if (dev->is_compressed) plat_remove(temp_file_name); #endif memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } if (dev->disk_flags & 0x600) { /* Zone type is not 0 but the disk is fixed-RPM. */ d86f_log("86F: Disk is fixed-RPM but zone type is not 0\n"); fclose(dev->fp); dev->fp = NULL; #ifdef D86F_COMPRESS if (dev->is_compressed) plat_remove(temp_file_name); #endif memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); return; } if (!writeprot[drive]) { writeprot[drive] = (dev->disk_flags & 0x10) ? 1 : 0; fwriteprot[drive] = writeprot[drive]; } if (writeprot[drive]) { fclose(dev->fp); dev->fp = NULL; #ifdef D86F_COMPRESS if (dev->is_compressed) dev->fp = plat_fopen(temp_file_name, "rb"); else #endif dev->fp = plat_fopen(fn, "rb"); } /* OK, set the drive data, other code needs it. */ d86f[drive] = dev; fseek(dev->fp, 8, SEEK_SET); (void) !fread(dev->track_offset, 1, d86f_get_track_table_size(drive), dev->fp); if (!(dev->track_offset[0])) { /* File has no track 0 side 0, abort. */ d86f_log("86F: No Track 0 side 0\n"); fclose(dev->fp); dev->fp = NULL; memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); d86f[drive] = NULL; return; } if ((d86f_get_sides(drive) == 2) && !(dev->track_offset[1])) { /* File is 2-sided but has no track 0 side 1, abort. */ d86f_log("86F: No Track 0 side 1\n"); fclose(dev->fp); dev->fp = NULL; memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); free(dev); d86f[drive] = NULL; return; } /* Load track 0 flags as default. */ if (fseek(dev->fp, dev->track_offset[0], SEEK_SET) == -1) fatal("d86f_load(): Track 0: Error seeking to the beginning of the file\n"); if (fread(&(dev->side_flags[0]), 1, 2, dev->fp) != 2) fatal("d86f_load(): Track 0: Error reading side flags\n"); if (dev->disk_flags & 0x80) { if (fread(&(dev->extra_bit_cells[0]), 1, 4, dev->fp) != 4) fatal("d86f_load(): Track 0: Error reading the amount of extra bit cells\n"); if ((dev->disk_flags & 0x1060) != 0x1000) { if (dev->extra_bit_cells[0] < -32768) dev->extra_bit_cells[0] = -32768; if (dev->extra_bit_cells[0] > 32768) dev->extra_bit_cells[0] = 32768; } } else { dev->extra_bit_cells[0] = 0; } if (d86f_get_sides(drive) == 2) { if (fseek(dev->fp, dev->track_offset[1], SEEK_SET) == -1) fatal("d86f_load(): Track 1: Error seeking to the beginning of the file\n"); if (fread(&(dev->side_flags[1]), 1, 2, dev->fp) != 2) fatal("d86f_load(): Track 1: Error reading side flags\n"); if (dev->disk_flags & 0x80) { if (fread(&(dev->extra_bit_cells[1]), 1, 4, dev->fp) != 4) fatal("d86f_load(): Track 4: Error reading the amount of extra bit cells\n"); if ((dev->disk_flags & 0x1060) != 0x1000) { if (dev->extra_bit_cells[1] < -32768) dev->extra_bit_cells[1] = -32768; if (dev->extra_bit_cells[1] > 32768) dev->extra_bit_cells[1] = 32768; } } else { dev->extra_bit_cells[1] = 0; } } else { switch ((dev->disk_flags >> 1) >> 3) { default: case 0: dev->side_flags[1] = 0x0a; break; case 1: dev->side_flags[1] = 0x00; break; case 2: case 3: dev->side_flags[1] = 0x03; break; } dev->extra_bit_cells[1] = 0; } fseek(dev->fp, 0, SEEK_END); dev->file_size = ftell(dev->fp); fseek(dev->fp, 0, SEEK_SET); d86f_register_86f(drive); drives[drive].seek = d86f_seek; d86f_common_handlers(drive); drives[drive].format = d86f_format; #ifdef D86F_COMPRESS d86f_log("86F: Disk is %scompressed and does%s have surface description data\n", dev->is_compressed ? "" : "not ", d86f_has_surface_desc(drive) ? "" : " not"); #else d86f_log("86F: Disk does%s have surface description data\n", d86f_has_surface_desc(drive) ? "" : " not"); #endif } void d86f_init(void) { setup_crc(0x1021); for (uint8_t i = 0; i < FDD_NUM; i++) d86f[i] = NULL; } void d86f_set_fdc(void *fdc) { d86f_fdc = (fdc_t *) fdc; } void d86f_close(int drive) { char temp_file_name[2048]; d86f_t *dev = d86f[drive]; /* Make sure the drive is alive. */ if (dev == NULL) return; memcpy(temp_file_name, drive ? nvr_path("TEMP$$$1.$$$") : nvr_path("TEMP$$$0.$$$"), 26); if (d86f_has_surface_desc(drive)) { for (uint8_t i = 0; i < 2; i++) { if (dev->track_surface_data[i]) { free(dev->track_surface_data[i]); dev->track_surface_data[i] = NULL; } } for (uint8_t i = 0; i < 2; i++) { for (uint8_t j = 0; j < 2; j++) { if (dev->thin_track_surface_data[i][j]) { free(dev->thin_track_surface_data[i][j]); dev->thin_track_surface_data[i][j] = NULL; } } } } if (dev->fp) { fclose(dev->fp); dev->fp = NULL; } #ifdef D86F_COMPRESS if (dev->is_compressed) plat_remove(temp_file_name); #endif } /* When an FDD is mounted, set up the D86F data structures. */ void d86f_setup(int drive) { d86f_t *dev; /* Allocate a drive structure. */ dev = (d86f_t *) malloc(sizeof(d86f_t)); memset(dev, 0x00, sizeof(d86f_t)); dev->state = STATE_IDLE; dev->last_side_sector[0] = NULL; dev->last_side_sector[1] = NULL; /* Set the drive as active. */ d86f[drive] = dev; } /* If an FDD is unmounted, unlink the D86F data structures. */ void d86f_destroy(int drive) { d86f_t *dev = d86f[drive]; if (dev == NULL) return; if (d86f_has_surface_desc(drive)) { for (uint8_t i = 0; i < 2; i++) { if (dev->track_surface_data[i]) { free(dev->track_surface_data[i]); dev->track_surface_data[i] = NULL; } } for (uint8_t i = 0; i < 2; i++) { for (uint8_t j = 0; j < 2; j++) { if (dev->thin_track_surface_data[i][j]) { free(dev->thin_track_surface_data[i][j]); dev->thin_track_surface_data[i][j] = NULL; } } } } d86f_destroy_linked_lists(drive, 0); d86f_destroy_linked_lists(drive, 1); free(d86f[drive]); d86f[drive] = NULL; d86f_handler[drive].read_data = NULL; }