Files
86Box-fork/src/floppy/fdd_imd.c
2021-03-30 09:46:49 +02:00

946 lines
26 KiB
C

/*
* 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 IMD floppy image format.
*
*
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2016-2019 Miran Grca.
* Copyright 2018,2019 Fred N. van Kempen.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/timer.h>
#include <86box/plat.h>
#include <86box/fdd.h>
#include <86box/fdd_86f.h>
#include <86box/fdd_imd.h>
#include <86box/fdc.h>
typedef struct {
uint8_t is_present;
uint32_t file_offs;
uint8_t params[5];
uint32_t r_map_offs;
uint32_t c_map_offs;
uint32_t h_map_offs;
uint32_t n_map_offs;
uint32_t data_offs;
uint32_t sector_data_offs[255];
uint32_t sector_data_size[255];
uint32_t gap3_len;
uint16_t side_flags;
uint8_t max_sector_size;
} imd_track_t;
typedef struct {
FILE *f;
char *buffer;
uint32_t start_offs;
int track_count, sides;
int track;
uint16_t disk_flags;
int track_width;
imd_track_t tracks[256][2];
uint16_t current_side_flags[2];
uint8_t xdf_ordered_pos[256][2];
uint8_t interleave_ordered_pos[256][2];
char *current_data[2];
uint8_t track_buffer[2][25000];
} imd_t;
static imd_t *imd[FDD_NUM];
static fdc_t *imd_fdc;
#ifdef ENABLE_IMD_LOG
int imd_do_log = ENABLE_IMD_LOG;
static void
imd_log(const char *fmt, ...)
{
va_list ap;
if (imd_do_log)
{
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define imd_log(fmt, ...)
#endif
static uint32_t
get_raw_tsize(int side_flags, int slower_rpm)
{
uint32_t size;
switch(side_flags & 0x27) {
case 0x22:
size = slower_rpm ? 5314 : 5208;
break;
default:
case 0x02:
case 0x21:
size = slower_rpm ? 6375 : 6250;
break;
case 0x01:
size = slower_rpm ? 7650 : 7500;
break;
case 0x20:
size = slower_rpm ? 10629 : 10416;
break;
case 0x00:
size = slower_rpm ? 12750 : 12500;
break;
case 0x23:
size = slower_rpm ? 21258 : 20833;
break;
case 0x03:
size = slower_rpm ? 25500 : 25000;
break;
case 0x25:
size = slower_rpm ? 42517 : 41666;
break;
case 0x05:
size = slower_rpm ? 51000 : 50000;
break;
}
return(size);
}
static int
track_is_xdf(int drive, int side, int track)
{
imd_t *dev = imd[drive];
int i, effective_sectors, xdf_sectors;
int high_sectors, low_sectors;
int max_high_id, expected_high_count, expected_low_count;
uint8_t *r_map;
uint8_t *n_map;
char *data_base;
char *cur_data;
effective_sectors = xdf_sectors = high_sectors = low_sectors = 0;
for (i = 0; i < 256; i++)
dev->xdf_ordered_pos[i][side] = 0;
if (dev->tracks[track][side].params[2] & 0xC0) return(0);
if ((dev->tracks[track][side].params[3] != 16) &&
(dev->tracks[track][side].params[3] != 19)) return(0);
r_map = (uint8_t *)(dev->buffer + dev->tracks[track][side].r_map_offs);
data_base = dev->buffer + dev->tracks[track][side].data_offs;
if (! track) {
if (dev->tracks[track][side].params[4] != 2) return(0);
if (! side) {
max_high_id = (dev->tracks[track][side].params[3] == 19) ? 0x8B : 0x88;
expected_high_count = (dev->tracks[track][side].params[3] == 19) ? 0x0B : 0x08;
expected_low_count = 8;
} else {
max_high_id = (dev->tracks[track][side].params[3] == 19) ? 0x93 : 0x90;
expected_high_count = (dev->tracks[track][side].params[3] == 19) ? 0x13 : 0x10;
expected_low_count = 0;
}
for (i = 0; i < dev->tracks[track][side].params[3]; i++) {
if ((r_map[i] >= 0x81) && (r_map[i] <= max_high_id)) {
high_sectors++;
dev->xdf_ordered_pos[(int) r_map[i]][side] = i;
}
if ((r_map[i] >= 0x01) && (r_map[i] <= 0x08)) {
low_sectors++;
dev->xdf_ordered_pos[(int) r_map[i]][side] = i;
}
if ((high_sectors == expected_high_count) && (low_sectors == expected_low_count)) {
dev->current_side_flags[side] = (dev->tracks[track][side].params[3] == 19) ? 0x08 : 0x28;
return((dev->tracks[track][side].params[3] == 19) ? 2 : 1);
}
}
return(0);
} else {
if (dev->tracks[track][side].params[4] != 0xFF) return(0);
n_map = (uint8_t *) (dev->buffer + dev->tracks[track][side].n_map_offs);
cur_data = data_base;
for (i = 0; i < dev->tracks[track][side].params[3]; i++) {
effective_sectors++;
if (!(r_map[i]) && !(n_map[i]))
effective_sectors--;
if (r_map[i] == (n_map[i] | 0x80)) {
xdf_sectors++;
dev->xdf_ordered_pos[(int) r_map[i]][side] = i;
}
cur_data += (128 << ((uint32_t) n_map[i]));
}
if ((effective_sectors == 3) && (xdf_sectors == 3)) {
dev->current_side_flags[side] = 0x28;
return(1); /* 5.25" 2HD XDF */
}
if ((effective_sectors == 4) && (xdf_sectors == 4)) {
dev->current_side_flags[side] = 0x08;
return(2); /* 3.5" 2HD XDF */
}
return(0);
}
return(0);
}
static int
track_is_interleave(int drive, int side, int track)
{
imd_t *dev = imd[drive];
int i, effective_sectors;
char *r_map;
int track_spt;
effective_sectors = 0;
for (i = 0; i < 256; i++)
dev->interleave_ordered_pos[i][side] = 0;
track_spt = dev->tracks[track][side].params[3];
r_map = dev->buffer + dev->tracks[track][side].r_map_offs;
if (dev->tracks[track][side].params[2] & 0xC0) return(0);
if (track_spt != 21) return(0);
if (dev->tracks[track][side].params[4] != 2) return(0);
for (i = 0; i < track_spt; i++) {
if ((r_map[i] >= 1) && (r_map[i] <= track_spt)) {
effective_sectors++;
dev->interleave_ordered_pos[(int) r_map[i]][side] = i;
}
}
if (effective_sectors == track_spt) return(1);
return(0);
}
static void
sector_to_buffer(int drive, int track, int side, uint8_t *buffer, int sector, int len)
{
imd_t *dev = imd[drive];
int type = dev->buffer[dev->tracks[track][side].sector_data_offs[sector]];
uint8_t fill_char;
if (type == 0)
memset(buffer, 0x00, len);
else {
if (type & 1)
memcpy(buffer, &(dev->buffer[dev->tracks[track][side].sector_data_offs[sector] + 1]), len);
else {
fill_char = dev->buffer[dev->tracks[track][side].sector_data_offs[sector] + 1];
memset(buffer, fill_char, len);
}
}
}
static void
imd_seek(int drive, int track)
{
uint32_t track_buf_pos[2] = { 0, 0 };
uint8_t id[4] = { 0, 0, 0, 0 };
uint8_t type;
imd_t *dev = imd[drive];
int sector, current_pos;
int side, c = 0, h, n;
int ssize = 512;
int track_rate = 0;
int track_gap2 = 22;
int track_gap3 = 12;
int xdf_type = 0;
int interleave_type = 0;
int is_trackx = 0;
int xdf_spt = 0;
int xdf_sector = 0;
int ordered_pos = 0;
int real_sector = 0;
int actual_sector = 0;
char *c_map = NULL;
char *h_map = NULL;
char *r_map;
char *n_map = NULL;
uint8_t *data;
int flags = 0x00;
if (dev->f == NULL) return;
if (!dev->track_width && fdd_doublestep_40(drive))
track /= 2;
d86f_set_cur_track(drive, track);
is_trackx = (track == 0) ? 0 : 1;
dev->track = track;
dev->current_side_flags[0] = dev->tracks[track][0].side_flags;
dev->current_side_flags[1] = dev->tracks[track][1].side_flags;
d86f_reset_index_hole_pos(drive, 0);
d86f_reset_index_hole_pos(drive, 1);
d86f_destroy_linked_lists(drive, 0);
d86f_destroy_linked_lists(drive, 1);
d86f_zero_track(drive);
if (track > dev->track_count)
return;
for (side = 0; side < dev->sides; side++) {
if (!dev->tracks[track][side].is_present)
continue;
track_rate = dev->current_side_flags[side] & 7;
if (!track_rate && (dev->current_side_flags[side] & 0x20))
track_rate = 4;
if ((dev->current_side_flags[side] & 0x27) == 0x21)
track_rate = 2;
r_map = dev->buffer + dev->tracks[track][side].r_map_offs;
h = dev->tracks[track][side].params[2];
if (h & 0x80)
c_map = dev->buffer + dev->tracks[track][side].c_map_offs;
else
c = dev->tracks[track][side].params[1];
if (h & 0x40)
h_map = dev->buffer + dev->tracks[track][side].h_map_offs;
n = dev->tracks[track][side].params[4];
if (n == 0xFF) {
n_map = dev->buffer + dev->tracks[track][side].n_map_offs;
track_gap3 = gap3_sizes[track_rate][(int) n_map[0]][dev->tracks[track][side].params[3]];
} else {
track_gap3 = gap3_sizes[track_rate][n][dev->tracks[track][side].params[3]];
}
if (! track_gap3)
track_gap3 = dev->tracks[track][side].gap3_len;
xdf_type = track_is_xdf(drive, side, track);
interleave_type = track_is_interleave(drive, side, track);
current_pos = d86f_prepare_pretrack(drive, side, 0);
if (! xdf_type) {
for (sector = 0; sector < dev->tracks[track][side].params[3]; sector++) {
if (interleave_type == 0) {
real_sector = r_map[sector];
actual_sector = sector;
} else {
real_sector = dmf_r[sector];
actual_sector = dev->interleave_ordered_pos[real_sector][side];
}
id[0] = (h & 0x80) ? c_map[actual_sector] : c;
id[1] = (h & 0x40) ? h_map[actual_sector] : (h & 1);
id[2] = real_sector;
id[3] = (n == 0xFF) ? n_map[actual_sector] : n;
data = dev->track_buffer[side] + track_buf_pos[side];
type = dev->buffer[dev->tracks[track][side].sector_data_offs[actual_sector]];
type = (type >> 1) & 7;
flags = 0x00;
if ((type == 2) || (type == 4))
flags |= SECTOR_DELETED_DATA;
if ((type == 3) || (type == 4))
flags |= SECTOR_CRC_ERROR;
if (((flags & 0x02) || (id[3] > dev->tracks[track][side].max_sector_size)) && !fdd_get_turbo(drive))
ssize = 3;
else
ssize = 128 << ((uint32_t) id[3]);
sector_to_buffer(drive, track, side, data, actual_sector, ssize);
current_pos = d86f_prepare_sector(drive, side, current_pos, id, data, ssize, 22, track_gap3, flags);
track_buf_pos[side] += ssize;
if (sector == 0)
d86f_initialize_last_sector_id(drive, id[0], id[1], id[2], id[3]);
}
} else {
xdf_type--;
xdf_spt = xdf_physical_sectors[xdf_type][is_trackx];
for (sector = 0; sector < xdf_spt; sector++) {
xdf_sector = (side * xdf_spt) + sector;
id[0] = track;
id[1] = side;
id[2] = xdf_disk_layout[xdf_type][is_trackx][xdf_sector].id.r;
id[3] = is_trackx ? (id[2] & 7) : 2;
ordered_pos = dev->xdf_ordered_pos[id[2]][side];
data = dev->track_buffer[side] + track_buf_pos[side];
type = dev->buffer[dev->tracks[track][side].sector_data_offs[ordered_pos]];
type = ((type - 1) >> 1) & 7;
flags = 0x00;
if (type & 0x01)
flags |= SECTOR_DELETED_DATA;
if (type & 0x02)
flags |= SECTOR_CRC_ERROR;
if (((flags & 0x02) || (id[3] > dev->tracks[track][side].max_sector_size)) && !fdd_get_turbo(drive))
ssize = 3;
else
ssize = 128 << ((uint32_t) id[3]);
sector_to_buffer(drive, track, side, data, ordered_pos, ssize);
if (is_trackx)
current_pos = d86f_prepare_sector(drive, side, xdf_trackx_spos[xdf_type][xdf_sector], id, data, ssize, track_gap2, xdf_gap3_sizes[xdf_type][is_trackx], flags);
else
current_pos = d86f_prepare_sector(drive, side, current_pos, id, data, ssize, track_gap2, xdf_gap3_sizes[xdf_type][is_trackx], flags);
track_buf_pos[side] += ssize;
if (sector == 0)
d86f_initialize_last_sector_id(drive, id[0], id[1], id[2], id[3]);
}
}
}
}
static uint16_t
disk_flags(int drive)
{
imd_t *dev = imd[drive];
return(dev->disk_flags);
}
static uint16_t
side_flags(int drive)
{
imd_t *dev = imd[drive];
int side = 0;
uint16_t sflags = 0;
side = fdd_get_head(drive);
sflags = dev->current_side_flags[side];
return(sflags);
}
static void
set_sector(int drive, int side, uint8_t c, uint8_t h, uint8_t r, uint8_t n)
{
imd_t *dev = imd[drive];
int track = dev->track;
int i, sc, sh, sn;
char *c_map = NULL, *h_map = NULL, *r_map = NULL, *n_map = NULL;
uint8_t id[4] = { 0, 0, 0, 0 };
sc = dev->tracks[track][side].params[1];
sh = dev->tracks[track][side].params[2];
sn = dev->tracks[track][side].params[4];
if (sh & 0x80)
c_map = dev->buffer + dev->tracks[track][side].c_map_offs;
if (sh & 0x40)
h_map = dev->buffer + dev->tracks[track][side].h_map_offs;
r_map = dev->buffer + dev->tracks[track][side].r_map_offs;
if (sn == 0xFF)
n_map = dev->buffer + dev->tracks[track][side].n_map_offs;
if (c != dev->track) return;
for (i = 0; i < dev->tracks[track][side].params[3]; i++) {
id[0] = (sh & 0x80) ? c_map[i] : sc;
id[1] = (sh & 0x40) ? h_map[i] : (sh & 1);
id[2] = r_map[i];
id[3] = (sn == 0xFF) ? n_map[i] : sn;
if ((id[0] == c) && (id[1] == h) && (id[2] == r) && (id[3] == n)) {
dev->current_data[side] = dev->buffer + dev->tracks[track][side].sector_data_offs[i];
}
}
}
static void
imd_writeback(int drive)
{
imd_t *dev = imd[drive];
int side;
int track = dev->track;
int i = 0;
char *n_map = 0;
uint8_t h, n, spt;
uint32_t ssize;
if (writeprot[drive]) return;
for (side = 0; side < dev->sides; side++) {
if (dev->tracks[track][side].is_present) {
fseek(dev->f, dev->tracks[track][side].file_offs, SEEK_SET);
h = dev->tracks[track][side].params[2];
spt = dev->tracks[track][side].params[3];
n = dev->tracks[track][side].params[4];
fwrite(dev->tracks[track][side].params, 1, 5, dev->f);
if (h & 0x80)
fwrite(dev->buffer + dev->tracks[track][side].c_map_offs, 1, spt, dev->f);
if (h & 0x40)
fwrite(dev->buffer + dev->tracks[track][side].h_map_offs, 1, spt, dev->f);
if (n == 0xFF) {
n_map = dev->buffer + dev->tracks[track][side].n_map_offs;
fwrite(n_map, 1, spt, dev->f);
}
for (i = 0; i < spt; i++) {
ssize = (n == 0xFF) ? n_map[i] : n;
ssize = 128 << ssize;
fwrite(dev->buffer + dev->tracks[track][side].sector_data_offs[i], 1, ssize, dev->f);
}
}
}
}
static uint8_t
poll_read_data(int drive, int side, uint16_t pos)
{
imd_t *dev = imd[drive];
int type = dev->current_data[side][0];
if ((type == 0) || (type > 8)) return(0xf6); /* Should never happen. */
if (type & 1)
return(dev->current_data[side][pos + 1]);
else
return(dev->current_data[side][1]);
}
static void
poll_write_data(int drive, int side, uint16_t pos, uint8_t data)
{
imd_t *dev = imd[drive];
int type = dev->current_data[side][0];
if (writeprot[drive]) return;
if ((type & 1) || (type == 0) || (type > 8)) return; /* Should never happen. */
dev->current_data[side][pos + 1] = data;
}
static int
format_conditions(int drive)
{
imd_t *dev = imd[drive];
int track = dev->track;
int side, temp;
side = fdd_get_head(drive);
temp = (fdc_get_format_sectors(imd_fdc) == dev->tracks[track][side].params[3]);
temp = temp && (fdc_get_format_n(imd_fdc) == dev->tracks[track][side].params[4]);
return(temp);
}
void
imd_init(void)
{
memset(imd, 0x00, sizeof(imd));
}
void
imd_load(int drive, char *fn)
{
uint32_t magic = 0;
uint32_t fsize = 0;
char *buffer;
char *buffer2;
imd_t *dev;
int i = 0;
int track_spt = 0;
int sector_size = 0;
int track = 0;
int side = 0;
int extra = 0;
uint32_t last_offset = 0;
uint32_t data_size = 512;
uint32_t mfm = 0;
uint32_t pre_sector = 0;
uint32_t track_total = 0;
uint32_t raw_tsize = 0;
uint32_t minimum_gap3 = 0;
uint32_t minimum_gap4 = 0;
uint8_t converted_rate;
uint8_t type;
int size_diff, gap_sum;
d86f_unregister(drive);
writeprot[drive] = 0;
/* Allocate a drive block. */
dev = (imd_t *)malloc(sizeof(imd_t));
memset(dev, 0x00, sizeof(imd_t));
dev->f = plat_fopen(fn, "rb+");
if (dev->f == NULL) {
dev->f = plat_fopen(fn, "rb");
if (dev->f == NULL) {
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
free(dev);
return;
}
writeprot[drive] = 1;
}
if (ui_writeprot[drive])
writeprot[drive] = 1;
fwriteprot[drive] = writeprot[drive];
if (fseek(dev->f, 0, SEEK_SET) == -1)
fatal("imd_load(): Error seeking to the beginning of the file\n");
if (fread(&magic, 1, 4, dev->f) != 4)
fatal("imd_load(): Error reading the magic number\n");
if (magic != 0x20444D49) {
imd_log("IMD: Not a valid ImageDisk image\n");
fclose(dev->f);
free(dev);
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
return;
} else
imd_log("IMD: Valid ImageDisk image\n");
if (fseek(dev->f, 0, SEEK_END) == -1)
fatal("imd_load(): Error seeking to the end of the file\n");
fsize = ftell(dev->f);
if (fsize <= 0) {
imd_log("IMD: Too small ImageDisk image\n");
fclose(dev->f);
free(dev);
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
return;
}
if (fseek(dev->f, 0, SEEK_SET) == -1)
fatal("imd_load(): Error seeking to the beginning of the file again\n");
dev->buffer = malloc(fsize);
if (fread(dev->buffer, 1, fsize, dev->f) != fsize)
fatal("imd_load(): Error reading data\n");
buffer = dev->buffer;
buffer2 = memchr(buffer, 0x1A, fsize);
if (buffer2 == NULL) {
imd_log("IMD: No ASCII EOF character\n");
fclose(dev->f);
free(dev);
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
return;
} else {
imd_log("IMD: ASCII EOF character found at offset %08X\n", buffer2 - buffer);
}
buffer2++;
if ((buffer2 - buffer) == fsize) {
imd_log("IMD: File ends after ASCII EOF character\n");
fclose(dev->f);
free(dev);
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
return;
} else {
imd_log("IMD: File continues after ASCII EOF character\n");
}
dev->start_offs = (buffer2 - buffer);
dev->disk_flags = 0x00;
dev->track_count = 0;
dev->sides = 1;
/* Set up the drive unit. */
imd[drive] = dev;
while(1) {
track = buffer2[1];
side = buffer2[2];
if (side & 1)
dev->sides = 2;
extra = side & 0xC0;
side &= 0x3F;
dev->tracks[track][side].side_flags = (buffer2[0] % 3);
if (! dev->tracks[track][side].side_flags)
dev->disk_flags |= (0x02);
dev->tracks[track][side].side_flags |= (!(buffer2[0] - dev->tracks[track][side].side_flags) ? 0 : 8);
mfm = dev->tracks[track][side].side_flags & 8;
track_total = mfm ? 146 : 73;
pre_sector = mfm ? 60 : 42;
track_spt = buffer2[3];
sector_size = buffer2[4];
if ((track_spt == 15) && (sector_size == 2))
dev->tracks[track][side].side_flags |= 0x20;
if ((track_spt == 16) && (sector_size == 2))
dev->tracks[track][side].side_flags |= 0x20;
if ((track_spt == 17) && (sector_size == 2))
dev->tracks[track][side].side_flags |= 0x20;
if ((track_spt == 8) && (sector_size == 3))
dev->tracks[track][side].side_flags |= 0x20;
if ((dev->tracks[track][side].side_flags & 7) == 1)
dev->tracks[track][side].side_flags |= 0x20;
if ((dev->tracks[track][side].side_flags & 0x07) == 0x00)
dev->tracks[track][side].max_sector_size = 6;
else
dev->tracks[track][side].max_sector_size = 5;
if (!mfm)
dev->tracks[track][side].max_sector_size--;
imd_log("Side flags for (%02i)(%01i): %02X\n", track, side, dev->tracks[track][side].side_flags);
dev->tracks[track][side].is_present = 1;
dev->tracks[track][side].file_offs = (buffer2 - buffer);
memcpy(dev->tracks[track][side].params, buffer2, 5);
dev->tracks[track][side].r_map_offs = dev->tracks[track][side].file_offs + 5;
last_offset = dev->tracks[track][side].r_map_offs + track_spt;
if (extra & 0x80) {
dev->tracks[track][side].c_map_offs = last_offset;
last_offset += track_spt;
}
if (extra & 0x40) {
dev->tracks[track][side].h_map_offs = last_offset;
last_offset += track_spt;
}
if (track_spt == 0x00) {
dev->tracks[track][side].n_map_offs = last_offset;
buffer2 = buffer + last_offset;
last_offset += track_spt;
dev->tracks[track][side].is_present = 0;
} else if (sector_size == 0xFF) {
dev->tracks[track][side].n_map_offs = last_offset;
buffer2 = buffer + last_offset;
last_offset += track_spt;
dev->tracks[track][side].data_offs = last_offset;
for (i = 0; i < track_spt; i++) {
data_size = buffer2[i];
data_size = 128 << data_size;
dev->tracks[track][side].sector_data_offs[i] = last_offset;
dev->tracks[track][side].sector_data_size[i] = 1;
if (dev->buffer[dev->tracks[track][side].sector_data_offs[i]] > 0x08) {
/* Invalid sector data type, possibly a malformed HxC IMG image (it outputs data errored
sectors with a variable amount of bytes, against the specification). */
imd_log("IMD: Invalid sector data type %02X\n", dev->buffer[dev->tracks[track][side].sector_data_offs[i]]);
fclose(dev->f);
free(dev);
imd[drive] = NULL;
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
return;
}
if (buffer[dev->tracks[track][side].sector_data_offs[i]] != 0)
dev->tracks[track][side].sector_data_size[i] += (buffer[dev->tracks[track][side].sector_data_offs[i]] & 1) ? data_size : 1;
last_offset += dev->tracks[track][side].sector_data_size[i];
if (!(buffer[dev->tracks[track][side].sector_data_offs[i]] & 1))
fwriteprot[drive] = writeprot[drive] = 1;
type = dev->buffer[dev->tracks[track][side].sector_data_offs[i]];
if (type != 0x00) {
type = ((type - 1) >> 1) & 7;
if (data_size > (128 << dev->tracks[track][side].max_sector_size))
track_total += (pre_sector + 3);
else
track_total += (pre_sector + data_size + 2);
}
}
} else {
dev->tracks[track][side].data_offs = last_offset;
for (i = 0; i < track_spt; i++) {
data_size = sector_size;
data_size = 128 << data_size;
dev->tracks[track][side].sector_data_offs[i] = last_offset;
dev->tracks[track][side].sector_data_size[i] = 1;
if (dev->buffer[dev->tracks[track][side].sector_data_offs[i]] > 0x08) {
/* Invalid sector data type, possibly a malformed HxC IMG image (it outputs data errored
sectors with a variable amount of bytes, against the specification). */
imd_log("IMD: Invalid sector data type %02X\n", dev->buffer[dev->tracks[track][side].sector_data_offs[i]]);
fclose(dev->f);
free(dev);
imd[drive] = NULL;
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
return;
}
if (buffer[dev->tracks[track][side].sector_data_offs[i]] != 0)
dev->tracks[track][side].sector_data_size[i] += (buffer[dev->tracks[track][side].sector_data_offs[i]] & 1) ? data_size : 1;
last_offset += dev->tracks[track][side].sector_data_size[i];
if (!(buffer[dev->tracks[track][side].sector_data_offs[i]] & 1))
fwriteprot[drive] = writeprot[drive] = 1;
type = dev->buffer[dev->tracks[track][side].sector_data_offs[i]];
if (type != 0x00) {
type = ((type - 1) >> 1) & 7;
if (data_size > (128 << dev->tracks[track][side].max_sector_size))
track_total += (pre_sector + 3);
else
track_total += (pre_sector + data_size + 2);
}
}
}
buffer2 = buffer + last_offset;
/* Leaving even GAP4: 80 : 40 */
/* Leaving only GAP1: 96 : 47 */
/* Not leaving even GAP1: 146 : 73 */
raw_tsize = get_raw_tsize(dev->tracks[track][side].side_flags, 0);
minimum_gap3 = 12 * track_spt;
if ((dev->tracks[track][side].side_flags == 0x0A) || (dev->tracks[track][side].side_flags == 0x29))
converted_rate = 2;
else if (dev->tracks[track][side].side_flags == 0x28)
converted_rate = 4;
else
converted_rate = dev->tracks[track][side].side_flags & 0x03;
if ((track_spt != 0x00) && (gap3_sizes[converted_rate][sector_size][track_spt] == 0x00)) {
size_diff = raw_tsize - track_total;
gap_sum = minimum_gap3 + minimum_gap4;
if (size_diff < gap_sum) {
/* If we can't fit the sectors with a reasonable minimum gap at perfect RPM, let's try 2% slower. */
raw_tsize = get_raw_tsize(dev->tracks[track][side].side_flags, 1);
/* Set disk flags so that rotation speed is 2% slower. */
dev->disk_flags |= (3 << 5);
size_diff = raw_tsize - track_total;
if (size_diff < gap_sum) {
/* If we can't fit the sectors with a reasonable minimum gap even at 2% slower RPM, abort. */
imd_log("IMD: Unable to fit the %i sectors in a track\n", track_spt);
fclose(dev->f);
free(dev);
imd[drive] = NULL;
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
return;
}
}
dev->tracks[track][side].gap3_len = (size_diff - minimum_gap4) / track_spt;
} else if ((track_spt == 0x00) || (gap3_sizes[converted_rate][sector_size][track_spt] != 0x00))
dev->tracks[track][side].gap3_len = gap3_sizes[converted_rate][sector_size][track_spt];
/* imd_log("GAP3 length for (%02i)(%01i): %i bytes\n", track, side, dev->tracks[track][side].gap3_len); */
if (track > dev->track_count)
dev->track_count = track;
if (last_offset >= fsize)
break;
}
/* If more than 43 tracks, then the tracks are thin (96 tpi). */
dev->track_count++;
dev->track_width = 0;
if (dev->track_count > 43)
dev->track_width = 1;
/* If 2 sides, mark it as such. */
if (dev->sides == 2)
dev->disk_flags |= 8;
/* imd_log("%i tracks, %i sides\n", dev->track_count, dev->sides); */
/* Attach this format to the D86F engine. */
d86f_handler[drive].disk_flags = disk_flags;
d86f_handler[drive].side_flags = side_flags;
d86f_handler[drive].writeback = imd_writeback;
d86f_handler[drive].set_sector = set_sector;
d86f_handler[drive].read_data = poll_read_data;
d86f_handler[drive].write_data = poll_write_data;
d86f_handler[drive].format_conditions = 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 = 1;
d86f_set_version(drive, 0x0063);
drives[drive].seek = imd_seek;
d86f_common_handlers(drive);
}
void
imd_close(int drive)
{
imd_t *dev = imd[drive];
if (dev == NULL) return;
d86f_unregister(drive);
if (dev->f != NULL) {
free(dev->buffer);
fclose(dev->f);
}
/* Release the memory. */
free(dev);
imd[drive] = NULL;
}
void
imd_set_fdc(void *fdc)
{
imd_fdc = (fdc_t *) fdc;
}