/* Copyright holders: Tenshi see COPYING for more details */ #include "ibm.h" #include "disc.h" #include "disc_d86f.h" #include "fdd.h" /*Handling for 'sector based' image formats (like .IMG) as opposed to 'stream based' formats (eg .FDI)*/ #define MAX_SECTORS 256 typedef struct { uint8_t c, h, r, n; int rate; uint8_t *data; } sector_t; static sector_t d86f_data[2][2][MAX_SECTORS]; static int d86f_count[2][2]; int cur_track_pos[2] = {0, 0}; int id_counter[2] = {0, 0}; int data_counter[2] = {0, 0}; int gap3_counter[2] = {0, 0}; int cur_rate[2] = {0, 0}; sector_t *last_sector[2]; enum { STATE_IDLE, STATE_READ_FIND_SECTOR, STATE_READ_SECTOR, STATE_READ_FIND_FIRST_SECTOR, STATE_READ_FIRST_SECTOR, STATE_READ_FIND_NEXT_SECTOR, STATE_READ_NEXT_SECTOR, STATE_WRITE_FIND_SECTOR, STATE_WRITE_SECTOR, STATE_READ_FIND_ADDRESS, STATE_READ_ADDRESS, STATE_FORMAT_FIND, STATE_FORMAT, STATE_SEEK }; static int d86f_state[2] = {0, 0}; static int d86f_track[2] = {0, 0}; static int d86f_side[2] = {0, 0}; static int d86f_drive; static int d86f_sector[2] = {0, 0}; static int d86f_n[2] = {0, 0}; static int disc_intersector_delay[2] = {0, 0}; static int disc_postdata_delay[2] = {0, 0}; static int disc_track_delay[2] = {0, 0}; static int disc_gap4_delay[2] = {0, 0}; static uint8_t d86f_fill[2] = {0, 0}; static int cur_sector[2], cur_byte[2]; static int index_count[2]; int raw_tsize[2] = {6250, 6250}; int gap2_size[2] = {22, 22}; int gap3_size[2] = {0, 0}; int gap4_size[2] = {0, 0}; int d86f_reset_state(int drive); static struct { FILE *f; uint8_t track_data[2][50000]; uint8_t track_layout[2][50000]; uint8_t track_flags[2][256]; uint32_t track_pos[2][256]; } d86f[2]; /* Needed for formatting! */ int d86f_realtrack(int drive, int track) { if (!(d86f[drive].track_flags[track] & 0x40) && fdd_doublestep_40(drive)) track /= 2; return track; } void d86f_writeback(int drive, int track); void d86f_init() { memset(d86f, 0, sizeof(d86f)); // adl[0] = adl[1] = 0; } void d86f_load(int drive, char *fn) { uint32_t magic = 0; uint32_t len = 0; uint16_t version = 0; writeprot[drive] = 0; d86f[drive].f = fopen(fn, "rb+"); if (!d86f[drive].f) { d86f[drive].f = fopen(fn, "rb"); if (!d86f[drive].f) return; writeprot[drive] = 1; } fwriteprot[drive] = writeprot[drive]; fseek(d86f[drive].f, 0, SEEK_END); len = ftell(f); fseek(d86f[drive].f, 0, SEEK_SET); if (len < 52056) { /* File too small, abort. */ fclose(d86f[drive].f); return; } fread(&magic, 4, 1, d86f[drive].f); if (magic != 0x464236368) { /* File is not of the valid format, abort. */ fclose(d86f[drive].f); return; } fread(&version, 2, 1, d86f[drive].f); if (version != 0x0100) { /* File is not of a recognized format version. */ fclose(d86f[drive].f); return; } if (strcmp(ext, "fdi") == 0) { /* This is a Japanese FDI image, so let's read the header */ pclog("d86f_load(): File is a Japanese FDI image...\n"); fseek(d86f[drive].f, 0x10, SEEK_SET); fread(&bpb_bps, 1, 2, d86f[drive].f); fseek(d86f[drive].f, 0x0C, SEEK_SET); fread(&size, 1, 4, d86f[drive].f); bpb_total = size / bpb_bps; fseek(d86f[drive].f, 0x08, SEEK_SET); fread(&(d86f[drive].base), 1, 4, d86f[drive].f); fseek(d86f[drive].f, d86f[drive].base + 0x15, SEEK_SET); bpb_mid = fgetc(d86f[drive].f); if (bpb_mid < 0xF0) bpb_mid = 0xF0; fseek(d86f[drive].f, 0x14, SEEK_SET); bpb_sectors = fgetc(d86f[drive].f); fseek(d86f[drive].f, 0x18, SEEK_SET); bpb_sides = fgetc(d86f[drive].f); fdi = 1; } else { /* Read the BPB */ pclog("d86f_load(): File is a raw image...\n"); fseek(d86f[drive].f, 0x0B, SEEK_SET); fread(&bpb_bps, 1, 2, d86f[drive].f); fseek(d86f[drive].f, 0x13, SEEK_SET); fread(&bpb_total, 1, 2, d86f[drive].f); fseek(d86f[drive].f, 0x15, SEEK_SET); bpb_mid = fgetc(d86f[drive].f); fseek(d86f[drive].f, 0x18, SEEK_SET); bpb_sectors = fgetc(d86f[drive].f); fseek(d86f[drive].f, 0x1A, SEEK_SET); bpb_sides = fgetc(d86f[drive].f); d86f[drive].base = 0; fdi = 0; fseek(d86f[drive].f, -1, SEEK_END); size = ftell(d86f[drive].f) + 1; } d86f[drive].sides = 2; d86f[drive].sector_size = 512; d86f[drive].hole = 0; pclog("BPB reports %i sides and %i bytes per sector\n", bpb_sides, bpb_bps); if (((bpb_sides < 1) || (bpb_sides > 2) || (bpb_bps < 128) || (bpb_bps > 2048)) && !fdi) { /* The BPB is giving us a wacky number of sides and/or bytes per sector, therefore it is most probably not a BPB at all, so we have to guess the parameters from file size. */ if (size <= (160*1024)) { d86f[drive].sectors = 8; d86f[drive].tracks = 40; d86f[drive].sides = 1; bit_rate_300 = 250; raw_tsize[drive] = 6250; } else if (size <= (180*1024)) { d86f[drive].sectors = 9; d86f[drive].tracks = 40; d86f[drive].sides = 1; bit_rate_300 = 250; raw_tsize[drive] = 6250; } else if (size <= (320*1024)) { d86f[drive].sectors = 8; d86f[drive].tracks = 40; bit_rate_300 = 250; raw_tsize[drive] = 6250; } else if (size <= (360*1024)) { d86f[drive].sectors = 9; d86f[drive].tracks = 40; bit_rate_300 = 250; raw_tsize[drive] = 6250; } /*Double density*/ else if (size <= (640*1024)) { d86f[drive].sectors = 8; d86f[drive].tracks = 80; bit_rate_300 = 250; raw_tsize[drive] = 6250; } /*Double density 640k*/ else if (size < (1024*1024)) { d86f[drive].sectors = 9; d86f[drive].tracks = 80; bit_rate_300 = 250; raw_tsize[drive] = 6250; } /*Double density*/ else if (size <= 1228800) { d86f[drive].sectors = 15; d86f[drive].tracks = 80; bit_rate_300 = (500.0 * 300.0) / 360.0; raw_tsize[drive] = 10416; } /*High density 1.2MB*/ else if (size <= 1261568) { d86f[drive].sectors = 8; d86f[drive].tracks = 77; d86f[drive].sector_size = 1024; bit_rate_300 = (500.0 * 300.0) / 360.0; raw_tsize[drive] = 10416; } /*High density 1.25MB Japanese format*/ else if (size <= (0x1A4000-1)) { d86f[drive].sectors = 18; d86f[drive].tracks = 80; bit_rate_300 = 500; raw_tsize[drive] = 12500; } /*High density (not supported by Tandy 1000)*/ else if (size <= 1556480) { d86f[drive].sectors = 19; d86f[drive].tracks = 80; bit_rate_300 = 500; raw_tsize[drive] = 12500; } /*High density (not supported by Tandy 1000)*/ else if (size <= 1638400) { d86f[drive].sectors = 10; d86f[drive].tracks = 80; d86f[drive].sector_size = 1024; bit_rate_300 = 500; raw_tsize[drive] = 12500; } /*High density (not supported by Tandy 1000)*/ // else if (size == 1884160) { d86f[drive].sectors = 23; d86f[drive].tracks = 80; bit_rate_300 = 500; } /*XDF format - used by OS/2 Warp*/ // else if (size == 1763328) { d86f[drive].sectors = 21; d86f[drive].tracks = 82; bit_rate_300 = 500; } /*XDF format - used by OS/2 Warp*/ else if (size <= 2000000) { d86f[drive].sectors = 21; d86f[drive].tracks = 80; bit_rate_300 = 500; raw_tsize[drive] = 12500; } /*DMF format - used by Windows 95 - changed by OBattler to 2000000, ie. the real unformatted capacity @ 500 kbps and 300 rpm */ else if (size <= 2949120) { d86f[drive].sectors = 36; d86f[drive].tracks = 80; bit_rate_300 = 1000; raw_tsize[drive] = 25000; } /*E density*/ temp_rate = 2; bpb_bps = d86f[drive].sector_size; bpt = bpb_bps * d86f[drive].sectors; if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][0] * bpb_bps)) { temp_rate = 2; raw_tsize[drive] = 5208; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][1] * bpb_bps)) { temp_rate = 2; raw_tsize[drive] = 6250; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][2] * bpb_bps)) { temp_rate = 1; raw_tsize[drive] = 7500; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][3] * bpb_bps)) { if (bpb_bps == 512) max_spt = (bit_rate_300 == 500) ? 21 : 17; temp_rate = (bit_rate_300 == 500) ? 0 : 4; raw_tsize[drive] = (bit_rate_300 == 500) ? 12500 : 10416; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][4] * bpb_bps)) { if (bpb_bps == 512) max_spt = 21; pclog("max_spt is %i\n", max_spt); temp_rate = 0; raw_tsize[drive] = 12500; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][5] * bpb_bps)) { if (bpb_bps == 512) max_spt = 41; temp_rate = 3; raw_tsize[drive] = 25000; } else /* Image too big, eject */ { pclog("Image is bigger than can fit on an ED floppy, ejecting...\n"); fclose(d86f[drive].f); return; } pclog("Temporary rate: %i (%i bytes per track)\n", temp_rate, bpt); d86f[drive].xdf_type = 0; } else { /* The BPB readings appear to be valid, so let's set the values. */ /* Number of tracks = number of total sectors divided by sides times sectors per track. */ if (fdi) { /* The image is a Japanese FDI, therefore we read the number of tracks from the header. */ fseek(d86f[drive].f, 0x1C, SEEK_SET); fread(&(d86f[drive].tracks), 1, 4, d86f[drive].f); } else { d86f[drive].tracks = ((uint32_t) bpb_total) / (((uint32_t) bpb_sides) * ((uint32_t) bpb_sectors)); } /* The rest we just set directly from the BPB. */ d86f[drive].sectors = bpb_sectors; d86f[drive].sides = bpb_sides; /* The sector size. */ d86f[drive].sector_size = bpb_bps; /* Now we calculate bytes per track, which is bpb_sectors * bpb_bps. */ bpt = (uint32_t) bpb_sectors * (uint32_t) bpb_bps; /* Now we should be able to calculate the bit rate. */ pclog("The image has %i bytes per track\n", bpt); temp_rate = 2; if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][0] * bpb_bps)) { bit_rate_300 = ((250.0 * 300.0) / 360.0); temp_rate = 2; raw_tsize[drive] = 5208; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][1] * bpb_bps)) { bit_rate_300 = 250; temp_rate = 2; raw_tsize[drive] = 6250; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][2] * bpb_bps)) { bit_rate_300 = 300; temp_rate = 1; raw_tsize[drive] = 7500; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][3] * bpb_bps)) { bit_rate_300 = (bpb_mid == 0xF0) ? 500 : ((500.0 * 300.0) / 360.0); if (bpb_bps == 512) max_spt = (bit_rate_300 == 500) ? 21 : 17; temp_rate = (bit_rate_300 == 500) ? 0 : 4; raw_tsize[drive] = (bit_rate_300 == 500) ? 12500 : 10416; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][4] * bpb_bps)) { bit_rate_300 = 500; if (bpb_bps == 512) max_spt = 21; pclog("max_spt is %i\n", max_spt); temp_rate = 0; raw_tsize[drive] = 12500; } else if (bpt <= (maximum_sectors[sector_size_code(bpb_bps)][5] * bpb_bps)) { bit_rate_300 = 1000; if (bpb_bps == 512) max_spt = 41; temp_rate = 3; raw_tsize[drive] = 25000; } else /* Image too big, eject */ { pclog("Image is bigger than can fit on an ED floppy, ejecting...\n"); fclose(d86f[drive].f); return; } if (bpb_bps == 512) /* BPB reports 512 bytes per sector, let's see if it's XDF or not */ { if (bit_rate_300 <= 300) /* Double-density disk, not XDF */ { d86f[drive].xdf_type = 0; } else { pclog("bpb_sectors is %i\n", bpb_sectors); if (bpb_sectors > max_spt) { switch(bpb_sectors) { case 19: /* High density XDF @ 360 rpm */ d86f[drive].xdf_type = 1; break; case 23: /* High density XDF @ 300 rpm */ d86f[drive].xdf_type = 2; pclog("XDF type is 2 @ %i kbps\n", bit_rate_300); break; #if 0 case 24: /* High density XXDF @ 300 rpm */ d86f[drive].xdf_type = 4; break; #endif case 46: /* Extended density XDF */ d86f[drive].xdf_type = 3; break; #if 0 case 48: /* Extended density XXDF */ d86f[drive].xdf_type = 5; break; #endif default: /* Unknown, as we're beyond maximum sectors, get out */ fclose(d86f[drive].f); return; } } else /* Amount of sectors per track that fits into a track, therefore not XDF */ { d86f[drive].xdf_type = 0; } } } else /* BPB reports sector size other than 512, can't possibly be XDF */ { d86f[drive].xdf_type = 0; } } gap2_size[drive] = (temp_rate == 3) ? 41 : 22; pclog("GAP2 size: %i bytes\n", gap2_size[drive]); gap3_size[drive] = gap3_sizes[temp_rate][sector_size_code(d86f[drive].sector_size)][d86f[drive].sectors]; if (gap3_size) { pclog("GAP3 size: %i bytes\n", gap3_size[drive]); } else { // fclose(d86f[drive].f); gap3_size[drive] = 40; pclog("WARNING: Floppy image of unknown format was inserted into drive %c:!\n", drive + 0x41); } gap4_size[drive] = raw_tsize[drive] - (((pre_gap + gap2_size[drive] + pre_data + d86f[drive].sector_size + post_gap + gap3_size[drive]) * d86f[drive].sectors) + pre_track); pclog("GAP4 size: %i bytes\n", gap4_size[drive]); if (d86f[drive].xdf_type) { gap4_size[drive] = 1; } if (bit_rate_300 == 250) { d86f[drive].hole = 0; /* If drive does not support 300 RPM, the medium is to be read at a period of 26 (300 kbps). */ d86f[drive].byte_period = 29; } else if (bit_rate_300 == 300) { d86f[drive].hole = 0; d86f[drive].byte_period = 26; } else if (bit_rate_300 == 1000) { d86f[drive].hole = 2; d86f[drive].byte_period = 8; } else if (bit_rate_300 < 250) { d86f[drive].hole = 0; d86f[drive].byte_period = 32; } else { d86f[drive].hole = 1; d86f[drive].byte_period = 16; } if (d86f[drive].xdf_type) /* In case of XDF-formatted image, write-protect */ { writeprot[drive] = 1; fwriteprot[drive] = writeprot[drive]; } drives[drive].seek = d86f_seek; drives[drive].readsector = disc_sector_readsector; drives[drive].writesector = disc_sector_writesector; drives[drive].readaddress = disc_sector_readaddress; drives[drive].hole = d86f_hole; drives[drive].byteperiod = d86f_byteperiod; drives[drive].poll = disc_sector_poll; drives[drive].format = disc_sector_format; drives[drive].realtrack = d86f_realtrack; drives[drive].stop = disc_sector_stop; disc_sector_writeback[drive] = d86f_writeback; d86f[drive].bitcell_period_300rpm = 1000000.0 / bit_rate_300*2.0; pclog("bit_rate_300=%g\n", bit_rate_300); pclog("bitcell_period_300=%g\n", d86f[drive].bitcell_period_300rpm); // d86f[drive].bitcell_period_300rpm = disc_get_bitcell_period(d86f[drive].rate); pclog("d86f_load %d %p sectors=%i tracks=%i sides=%i sector_size=%i hole=%i\n", drive, drives, d86f[drive].sectors, d86f[drive].tracks, d86f[drive].sides, d86f[drive].sector_size, d86f[drive].hole); } int d86f_hole(int drive) { return d86f[drive].hole; } int d86f_byteperiod(int drive) { if (d86f[drive].byte_period == 29) { return (fdd_get_type(drive) & 1) ? 32 : 26; } return d86f[drive].byte_period; } void d86f_close(int drive) { if (d86f[drive].f) fclose(d86f[drive].f); d86f[drive].f = NULL; } void d86f_seek(int drive, int track) { int side; int current_xdft = d86f[drive].xdf_type - 1; uint8_t sectors_fat, effective_sectors, sector_gap; /* Needed for XDF */ int sector, current_pos, sh, sr, spos, sside; if (!d86f[drive].f) return; // pclog("Seek drive=%i track=%i sectors=%i sector_size=%i sides=%i\n", drive, track, d86f[drive].sectors,d86f[drive].sector_size, d86f[drive].sides); // pclog(" %i %i\n", drive_type[drive], d86f[drive].tracks); #ifdef MAINLINE if ((d86f[drive].tracks <= 41) && fdd_doublestep_40(drive)) #else if ((d86f[drive].tracks <= 43) && fdd_doublestep_40(drive)) #endif track /= 2; // pclog("Disk seeked to track %i\n", track); disc_track[drive] = track; if (d86f[drive].sides == 2) { fseek(d86f[drive].f, d86f[drive].base + (track * d86f[drive].sectors * d86f[drive].sector_size * 2), SEEK_SET); // pclog("Seek: Current file position (H0) is: %08X\n", ftell(d86f[drive].f)); fread(d86f[drive].track_data[0], d86f[drive].sectors * d86f[drive].sector_size, 1, d86f[drive].f); // pclog("Seek: Current file position (H1) is: %08X\n", ftell(d86f[drive].f)); fread(d86f[drive].track_data[1], d86f[drive].sectors * d86f[drive].sector_size, 1, d86f[drive].f); } else { fseek(d86f[drive].f, d86f[drive].base + (track * d86f[drive].sectors * d86f[drive].sector_size), SEEK_SET); fread(d86f[drive].track_data[0], d86f[drive].sectors * d86f[drive].sector_size, 1, d86f[drive].f); } disc_sector_reset(drive, 0); disc_sector_reset(drive, 1); if (d86f[drive].xdf_type) { sectors_fat = xdf_track0[current_xdft][0]; effective_sectors = xdf_track0[current_xdft][1]; sector_gap = xdf_track0[current_xdft][2]; if (!track) { /* Track 0, register sectors according to track 0 layout. */ current_pos = 0; for (sector = 0; sector < (d86f[drive].sectors * 2); sector++) { if (xdf_track0_layout[current_xdft][sector]) { sh = xdf_track0_layout[current_xdft][sector] & 0xFF; sr = xdf_track0_layout[current_xdft][sector] >> 8; spos = current_pos; sside = 0; if (spos > (d86f[drive].sectors * d86f[drive].sector_size)) { spos -= (d86f[drive].sectors * d86f[drive].sector_size); sside = 1; } disc_sector_add(drive, sh, track, sh, sr, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[sside][spos]); } current_pos += 512; } #if 0 /* Track 0, register sectors according to track 0 map. */ /* First, the "Side 0" buffer, will also contain one sector from side 1. */ current_pos = 0; for (sector = 0; sector < sectors_fat; sector++) { if ((sector+0x81) >= 0x91) { disc_sector_add(drive, 0, track, 0, sector+0x85, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); } else { disc_sector_add(drive, 0, track, 0, sector+0x81, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); } current_pos += 512; } disc_sector_add(drive, 1, track, 1, 0x81, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); current_pos += 512; for (sector = 0; sector < 7; sector++) { disc_sector_add(drive, 0, track, 0, sector+1, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); current_pos += 512; } disc_sector_add(drive, 0, track, 0, 0x9B, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); current_pos += 512; disc_sector_add(drive, 0, track, 0, 0x9C, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); current_pos += 512; disc_sector_add(drive, 0, track, 0, 0x9D, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); current_pos += 512; disc_sector_add(drive, 0, track, 0, 0x9E, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); current_pos += 512; /* Now the "Side 1" buffer, will also contain one sector from side 0. */ current_pos = 0; for (sector = 0; (sector < effective_sectors - 1); sector++) { disc_sector_add(drive, 1, track, 1, sector+0x82, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[1][current_pos]); current_pos += 512; } disc_sector_add(drive, 0, track, 0, 8, 2, d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[1][current_pos]); current_pos += 512; #endif } else { /* Non-zero track, this will have sectors of various sizes. */ /* First, the "Side 0" buffer. */ current_pos = 0; for (sector = 0; sector < xdf_spt[current_xdft]; sector++) { disc_sector_add(drive, xdf_map[current_xdft][sector][0], track, xdf_map[current_xdft][sector][0], xdf_map[current_xdft][sector][2] + 0x80, xdf_map[current_xdft][sector][2], d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[0][current_pos]); current_pos += (128 << xdf_map[current_xdft][sector][2]); } /* Then, the "Side 1" buffer. */ current_pos = 0; for (sector = xdf_spt[current_xdft]; sector < (xdf_spt[current_xdft] << 1); sector++) { disc_sector_add(drive, xdf_map[current_xdft][sector][0], track, xdf_map[current_xdft][sector][0], xdf_map[current_xdft][sector][2] + 0x80, xdf_map[current_xdft][sector][2], d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[1][current_pos]); current_pos += (128 << xdf_map[current_xdft][sector][2]); } } } else { for (side = 0; side < d86f[drive].sides; side++) { for (sector = 0; sector < d86f[drive].sectors; sector++) disc_sector_add(drive, side, track, side, sector+1, d86f_sector_size_code(drive), d86f[drive].bitcell_period_300rpm, &d86f[drive].track_data[side][sector * d86f[drive].sector_size]); } } for (side = d86f[drive].sides - 1; side >= 0; side--) { disc_sector_prepare_track_layout(drive, side); } } void d86f_writeback(int drive, int track) { if (!d86f[drive].f) return; if (d86f[drive].xdf_type) return; /*Should never happen*/ if (d86f[drive].sides == 2) { fseek(d86f[drive].f, d86f[drive].base + (track * d86f[drive].sectors * d86f[drive].sector_size * 2), SEEK_SET); fwrite(d86f[drive].track_data[0], d86f[drive].sectors * d86f[drive].sector_size, 1, d86f[drive].f); fwrite(d86f[drive].track_data[1], d86f[drive].sectors * d86f[drive].sector_size, 1, d86f[drive].f); } else { fseek(d86f[drive].f, d86f[drive].base + (track * d86f[drive].sectors * d86f[drive].sector_size), SEEK_SET); fwrite(d86f[drive].track_data[0], d86f[drive].sectors * d86f[drive].sector_size, 1, d86f[drive].f); } } void d86f_reset(int drive, int side) { d86f_count[drive][side] = 0; if (side == 0) { d86f_reset_state(drive); // cur_track_pos[drive] = 0; d86f_state[drive] = STATE_SEEK; } } void d86f_add(int drive, int side, uint8_t c, uint8_t h, uint8_t r, uint8_t n, int rate, uint8_t *data) { sector_t *s = &d86f_data[drive][side][d86f_count[drive][side]]; //pclog("d86f_add: drive=%i side=%i %i r=%i\n", drive, side, d86f_count[drive][side],r ); if (d86f_count[drive][side] >= MAX_SECTORS) return; s->c = c; s->h = h; s->r = r; s->n = n; // pclog("Adding sector: %i %i %i %i\n", c, h, r, n); s->rate = rate; s->data = data; d86f_count[drive][side]++; } static int get_bitcell_period(int drive) { // return (d86f_data[drive][d86f_side[drive]][cur_sector[drive]].rate * 300) / fdd_getrpm(drive); return ((&d86f_data[drive][0][0])->rate * 300) / fdd_getrpm(drive); return (cur_rate[drive] * 300) / fdd_getrpm(drive); } void d86f_readsector(int drive, int sector, int track, int side, int rate, int sector_size) { // pclog("d86f_readsector: fdc_period=%i d86f_period=%i rate=%i sector=%i track=%i side=%i\n", fdc_get_bitcell_period(), get_bitcell_period(drive), rate, sector, track, side); d86f_track[drive] = track; d86f_side[drive] = side; d86f_drive = drive; d86f_sector[drive] = sector; d86f_n[drive] = sector_size; d86f_reset_state(drive); if (sector == SECTOR_FIRST) d86f_state[drive] = STATE_READ_FIND_FIRST_SECTOR; else if (sector == SECTOR_NEXT) d86f_state[drive] = STATE_READ_FIND_NEXT_SECTOR; else d86f_state[drive] = STATE_READ_FIND_SECTOR; } void d86f_writesector(int drive, int sector, int track, int side, int rate, int sector_size) { // pclog("d86f_writesector: fdc_period=%i d86f_period=%i rate=%i\n", fdc_get_bitcell_period(), get_bitcell_period(), rate); d86f_track[drive] = track; d86f_side[drive] = side; d86f_drive = drive; d86f_sector[drive] = sector; d86f_n[drive] = sector_size; d86f_reset_state(drive); d86f_state[drive] = STATE_WRITE_FIND_SECTOR; } void d86f_readaddress(int drive, int track, int side, int rate) { // pclog("d86f_readaddress: fdc_period=%i d86f_period=%i rate=%i track=%i side=%i\n", fdc_get_bitcell_period(), get_bitcell_period(), rate, track, side); d86f_track[drive] = track; d86f_side[drive] = side; d86f_drive = drive; d86f_reset_state(drive); d86f_state[drive] = STATE_READ_FIND_ADDRESS; } void d86f_format(int drive, int track, int side, int rate, uint8_t fill) { d86f_track[drive] = track; d86f_side[drive] = side; d86f_drive = drive; d86f_fill[drive] = fill; d86f_reset_state(drive); d86f_state[drive] = STATE_FORMAT_FIND; } void d86f_stop(int drive) { d86f_state[drive] = STATE_IDLE; } static void index_pulse(int drive) { if (d86f_state[drive] != STATE_IDLE) fdc_indexpulse(); } // char *track_buffer[2][2][25512]; char track_layout[2][2][25512]; int id_positions[2][2][MAX_SECTORS]; /* 0 = MFM, 1 = FM, 2 = MFM perpendicular, 3 = reserved */ /* 4 = ISO, 0 = IBM */ int media_type = 0; #define BYTE_GAP 0 #define BYTE_SYNC 1 #define BYTE_IAM 2 #define BYTE_IDAM 3 #define BYTE_ID 4 #define BYTE_ID_CRC 5 #define BYTE_DATA_AM 6 #define BYTE_DATA 7 #define BYTE_DATA_CRC 8 #define BYTE_SECTOR_GAP 9 #define BYTE_GAP3 10 #define BYTE_AM_SYNC 11 #define BYTE_INDEX_HOLE 12 #define BYTE_EMPTY 255 void d86f_prepare_track_layout(int drive, int side) { sector_t *s; int i = 0; int j = 0; int real_gap0_len = ((media_type & 3) == 1) ? 40 : 80; int sync_len = ((media_type & 3) == 1) ? 6 : 12; int am_len = ((media_type & 3) == 1) ? 1 : 4; int real_gap1_len = ((media_type & 3) == 1) ? 26 : 50; // track_layout[drive][side] = (char *) malloc(raw_tsize[drive]); // id_positions[drive][side] = (int *) malloc(d86f_count[drive][side] * 4); memset(track_layout[drive][side], BYTE_GAP, raw_tsize[drive]); memset(id_positions[drive][side], 0, 1024); i = 0; if (!(media_type & 4)) { memset(track_layout[drive][side] + i, BYTE_INDEX_HOLE, 1); i++; memset(track_layout[drive][side] + i, BYTE_GAP, real_gap0_len - 1); i += real_gap0_len - 1; memset(track_layout[drive][side] + i, BYTE_SYNC, sync_len); i += sync_len; if ((media_type & 3) != 1) { memset(track_layout[drive][side] + i, BYTE_AM_SYNC, 3); i += 3; } memset(track_layout[drive][side] + i, BYTE_IAM, 1); i++; memset(track_layout[drive][side] + i, BYTE_GAP, real_gap1_len); i += real_gap1_len; } else { memset(track_layout[drive][side] + i, BYTE_INDEX_HOLE, 1); i++; memset(track_layout[drive][side] + i, BYTE_GAP, real_gap1_len - 1); i += real_gap1_len - 1; } for (j = 0; j < d86f_count[drive][side]; j++) { s = &d86f_data[drive][side][j]; // pclog("Sector %i (%i)\n", j, s->n); memset(track_layout[drive][side] + i, BYTE_SYNC, sync_len); i += sync_len; if ((media_type & 3) != 1) { memset(track_layout[drive][side] + i, BYTE_AM_SYNC, 3); i += 3; } id_positions[drive][side][j] = i; memset(track_layout[drive][side] + i, BYTE_IDAM, 1); i++; memset(track_layout[drive][side] + i, BYTE_ID, 4); i += 4; memset(track_layout[drive][side] + i, BYTE_ID_CRC, 2); i += 2; memset(track_layout[drive][side] + i, BYTE_SECTOR_GAP, gap2_size[drive]); i += gap2_size[drive]; memset(track_layout[drive][side] + i, BYTE_SYNC, sync_len); i += sync_len; if ((media_type & 3) != 1) { memset(track_layout[drive][side] + i, BYTE_AM_SYNC, 3); i += 3; } memset(track_layout[drive][side] + i, BYTE_DATA_AM, 1); i++; memset(track_layout[drive][side] + i, BYTE_DATA, (128 << ((int) s->n))); i += (128 << ((int) s->n)); memset(track_layout[drive][side] + i, BYTE_DATA_CRC, 2); i += 2; memset(track_layout[drive][side] + i, BYTE_GAP3, gap3_size[drive]); i += gap3_size[drive]; } if (side == 0) d86f_state[drive] = STATE_IDLE; #if 0 FILE *f = fopen("layout.dmp", "wb"); fwrite(track_layout[drive][side], 1, raw_tsize[drive], f); fclose(f); fatal("good getpccache!\n"); #endif } int d86f_reset_state(int drive) { id_counter[drive] = data_counter[drive] = index_count[drive] = gap3_counter[drive] = cur_rate[drive] = 0; last_sector[drive] = NULL; } int d86f_find_sector(int drive) { int side = d86f_side[drive]; int i = 0; for (i = 0; i < d86f_count[drive][side]; i++) { if (id_positions[drive][side][i] == cur_track_pos[drive]) { return i; } } return -1; } int d86f_match(int drive) { int temp; if (last_sector[drive] == NULL) return 0; temp = (d86f_track[drive] == last_sector[drive]->c); temp = temp && (d86f_side[drive] == last_sector[drive]->h); temp = temp && (d86f_sector[drive] == last_sector[drive]->r); if (d86f_n[drive]) { temp = temp && (d86f_n[drive] == last_sector[drive]->n); } return temp; } int d86f_can_read_address(int drive) { int temp; temp = (fdc_get_bitcell_period() == get_bitcell_period(drive)); temp = temp && fdd_can_read_medium(drive ^ fdd_swap); return temp; } int d86f_can_format(int drive) { int temp; temp = !writeprot[drive]; temp = temp && !swwp; temp = temp && d86f_can_read_address(drive); temp = temp && (fdc_get_format_sectors() == d86f_count[drive][d86f_side[drive]]); return temp; } int d86f_find_state(int drive) { int temp; temp = (d86f_state[drive] == STATE_READ_FIND_SECTOR); temp = temp || (d86f_state[drive] == STATE_READ_FIND_FIRST_SECTOR); temp = temp || (d86f_state[drive] == STATE_READ_FIND_NEXT_SECTOR); temp = temp || (d86f_state[drive] == STATE_WRITE_FIND_SECTOR); temp = temp || (d86f_state[drive] == STATE_READ_FIND_ADDRESS); temp = temp || (d86f_state[drive] == STATE_FORMAT_FIND); return temp; } int d86f_read_state(int drive) { int temp; temp = (d86f_state[drive] == STATE_READ_SECTOR); temp = temp || (d86f_state[drive] == STATE_READ_FIRST_SECTOR); temp = temp || (d86f_state[drive] == STATE_READ_NEXT_SECTOR); return temp; } void d86f_poll() { sector_t *s; int data; int drive = d86f_drive; int side = d86f_side[drive]; int found_sector = 0; int b = 0; if (d86f_state[drive] == STATE_SEEK) { cur_track_pos[drive]++; cur_track_pos[drive] %= raw_tsize[drive]; return; } if (d86f_state[drive] == STATE_FORMAT_FIND) { if (!(d86f_can_format(drive))) { if (d86f_can_read_address(drive)) { pclog("d86f_poll(): Disk is write protected or attempting to format wrong number of sectors per track\n"); fdc_writeprotect(); } else { pclog("d86f_poll(): Unable to format at the requested density or bitcell period\n"); fdc_notfound(); } d86f_state[drive] = STATE_IDLE; d86f_reset_state(drive); cur_track_pos[drive]++; cur_track_pos[drive] %= raw_tsize[drive]; return; } } // if (d86f_state[drive] != STATE_IDLE) pclog("%04X: %01X\n", cur_track_pos[drive], track_layout[drive][side][cur_track_pos[drive]]); if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_GAP) { if (d86f_read_state(drive) || (d86f_state[drive] == STATE_WRITE_SECTOR) || (d86f_state[drive] == STATE_FORMAT)) { /* We're at GAP4b or even GAP4a and still in a read, write, or format state, this means we've overrun the gap. Return with sector not found. */ // pclog("d86f_poll(): Gap overrun at GAP4\n"); fdc_notfound(); d86f_state[drive] = STATE_IDLE; d86f_reset_state(drive); cur_track_pos[drive]++; cur_track_pos[drive] %= raw_tsize[drive]; return; } } else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_INDEX_HOLE) { index_pulse(drive); if (d86f_state[drive] != STATE_IDLE) index_count[drive]++; if (d86f_read_state(drive) || (d86f_state[drive] == STATE_WRITE_SECTOR) || (d86f_state[drive] == STATE_FORMAT)) { /* We're at the index address mark and still in a read, write, or format state, this means we've overrun the gap. Return with sector not found. */ // pclog("d86f_poll(): Gap overrun at IAM\n"); fdc_notfound(); d86f_state[drive] = STATE_IDLE; d86f_reset_state(drive); cur_track_pos[drive]++; cur_track_pos[drive] %= raw_tsize[drive]; return; } } else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_IDAM) { found_sector = d86f_find_sector(drive); // pclog("Found sector: %i\n", found_sector); cur_sector[drive] = found_sector; last_sector[drive] = &d86f_data[drive][d86f_side[drive]][found_sector]; cur_rate[drive] = last_sector[drive]->rate; if (!(d86f_can_read_address(drive))) last_sector[drive] = NULL; if (d86f_read_state(drive) || (d86f_state[drive] == STATE_WRITE_SECTOR) || (d86f_state[drive] == STATE_FORMAT)) { /* We're at a sector ID address mark and still in a read, write, or format state, this means we've overrun the gap. Return with sector not found. */ pclog("d86f_poll(): Gap (%i) overrun at IDAM\n", fdc_get_gap()); fdc_notfound(); d86f_state[drive] = STATE_IDLE; d86f_reset_state(drive); cur_track_pos[drive]++; cur_track_pos[drive] %= raw_tsize[drive]; return; } if ((d86f_state[drive] == STATE_FORMAT_FIND) && d86f_can_read_address(drive)) d86f_state[drive] = STATE_FORMAT; id_counter[drive] = 0; } else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_ID) { id_counter[drive]++; } else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_ID_CRC) { id_counter[drive]++; if (id_counter[drive] == 6) { /* ID CRC read, if state is read address, return address */ if ((d86f_state[drive] == STATE_READ_FIND_ADDRESS) && !(d86f_can_read_address(drive))) { if (fdc_get_bitcell_period() != get_bitcell_period(drive)) { pclog("Unable to read sector ID: Bitcell period mismatch (%i != %i)...\n", fdc_get_bitcell_period(), get_bitcell_period(drive)); } else { pclog("Unable to read sector ID: Media type not supported by the drive...\n"); } } if ((d86f_state[drive] == STATE_READ_FIND_ADDRESS) && d86f_can_read_address(drive)) { // pclog("Reading sector ID...\n"); fdc_sectorid(last_sector[drive]->c, last_sector[drive]->h, last_sector[drive]->r, last_sector[drive]->n, 0, 0); d86f_state[drive] = STATE_IDLE; } id_counter[drive] = 0; } } else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_DATA_AM) { data_counter[drive] = 0; switch (d86f_state[drive]) { case STATE_READ_FIND_SECTOR: if (d86f_match(drive) && d86f_can_read_address(drive)) d86f_state[drive] = STATE_READ_SECTOR; break; case STATE_READ_FIND_FIRST_SECTOR: if ((cur_sector[drive] == 0) && d86f_can_read_address(drive)) d86f_state[drive] = STATE_READ_FIRST_SECTOR; break; case STATE_READ_FIND_NEXT_SECTOR: if (d86f_can_read_address(drive)) d86f_state[drive] = STATE_READ_NEXT_SECTOR; break; case STATE_WRITE_FIND_SECTOR: if (d86f_match(drive) && d86f_can_read_address(drive)) d86f_state[drive] = STATE_WRITE_SECTOR; break; } } else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_DATA) { if (d86f_read_state(drive) && (last_sector[drive] != NULL)) { if (fdc_data(last_sector[drive]->data[data_counter[drive]])) { /* Data failed to be sent to the FDC, abort. */ pclog("d86f_poll(): Unable to send further data to the FDC\n"); d86f_state[drive] = STATE_IDLE; d86f_reset_state(drive); cur_track_pos[drive]++; cur_track_pos[drive] %= raw_tsize[drive]; return; } } if ((d86f_state[drive] == STATE_WRITE_SECTOR) && (last_sector[drive] != NULL)) { data = fdc_getdata(cur_byte[drive] == ((128 << ((uint32_t) last_sector[drive]->n)) - 1)); if (data == -1) { /* Data failed to be sent from the FDC, abort. */ pclog("d86f_poll(): Unable to receive further data from the FDC\n"); d86f_state[drive] = STATE_IDLE; d86f_reset_state(drive); cur_track_pos[drive]++; cur_track_pos[drive] %= raw_tsize[drive]; return; } if (!disable_write) last_sector[drive]->data[data_counter[drive]] = data; } if ((d86f_state[drive] == STATE_FORMAT) && (last_sector[drive] != NULL)) { if (!disable_write) last_sector[drive]->data[data_counter[drive]] = d86f_fill[drive]; } data_counter[drive]++; if (last_sector[drive] == NULL) { data_counter[drive] = 0; } else { data_counter[drive] %= (128 << ((uint32_t) last_sector[drive]->n)); if (!data_counter[drive]) { if (d86f_read_state(drive) && (last_sector[drive] != NULL)) { d86f_state[drive] = STATE_IDLE; fdc_finishread(drive); } if ((d86f_state[drive] == STATE_WRITE_SECTOR) && (last_sector[drive] != NULL)) { d86f_state[drive] = STATE_IDLE; if (!disable_write) d86f_writeback[drive](drive, d86f_track[drive]); fdc_finishread(drive); } if ((d86f_state[drive] == STATE_FORMAT) && (last_sector[drive] != NULL)) { d86f_state[drive] = STATE_IDLE; if (!disable_write) d86f_writeback[drive](drive, d86f_track[drive]); fdc_finishread(drive); } } } } else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_GAP3) { if (gap3_counter[drive] == fdc_get_gap()) { } gap3_counter[drive]++; gap3_counter[drive] %= gap3_size[drive]; // pclog("GAP3 counter = %i\n", gap3_counter[drive]); } else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_GAP) { if (last_sector[drive] != NULL) last_sector[drive] = NULL; } b = track_layout[drive][side][cur_track_pos[drive]]; cur_track_pos[drive]++; cur_track_pos[drive] %= raw_tsize[drive]; if ((d86f_state[drive] != STATE_IDLE) && (d86f_state[drive] != STATE_SEEK)) { if (index_count[drive] > 1) { if (d86f_find_state(drive)) { /* The index hole has been hit twice and we're still in a find state. This means sector finding has failed for whatever reason. Abort with sector not found and set state to idle. */ // pclog("d86f_poll(): Sector not found (%i %i %i %i)\n", d86f_track[drive], d86f_side[drive], d86f_sector[drive], d86f_n[drive]); fdc_notfound(); d86f_state[drive] = STATE_IDLE; d86f_reset_state(drive); return; } } } if ((b != BYTE_GAP3) && (track_layout[drive][side][cur_track_pos[drive]] == BYTE_GAP3)) { gap3_counter[drive] = 0; } }