Files
86Box/src/disc_sector_86box.c
2016-08-14 22:45:58 -04:00

596 lines
19 KiB
C

/* Copyright holders: Tenshi
see COPYING for more details
*/
#include "ibm.h"
#include "disc.h"
#include "disc_sector.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 disc_sector_data[2][2][MAX_SECTORS];
static int disc_sector_count[2][2];
void (*disc_sector_writeback[2])(int drive, int track);
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 disc_sector_state[2] = {0, 0};
static int disc_sector_track[2] = {0, 0};
static int disc_sector_side[2] = {0, 0};
static int disc_sector_drive;
static int disc_sector_sector[2] = {0, 0};
static int disc_sector_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 disc_sector_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 disc_sector_reset_state(int drive);
void disc_sector_reset(int drive, int side)
{
disc_sector_count[drive][side] = 0;
if (side == 0)
{
disc_sector_reset_state(drive);
// cur_track_pos[drive] = 0;
disc_sector_state[drive] = STATE_SEEK;
}
}
void disc_sector_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 = &disc_sector_data[drive][side][disc_sector_count[drive][side]];
//pclog("disc_sector_add: drive=%i side=%i %i r=%i\n", drive, side, disc_sector_count[drive][side],r );
if (disc_sector_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;
disc_sector_count[drive][side]++;
}
static int get_bitcell_period(int drive)
{
// return (disc_sector_data[drive][disc_sector_side[drive]][cur_sector[drive]].rate * 300) / fdd_getrpm(drive);
return ((&disc_sector_data[drive][0][0])->rate * 300) / fdd_getrpm(drive);
return (cur_rate[drive] * 300) / fdd_getrpm(drive);
}
void disc_sector_readsector(int drive, int sector, int track, int side, int rate, int sector_size)
{
// pclog("disc_sector_readsector: fdc_period=%i img_period=%i rate=%i sector=%i track=%i side=%i\n", fdc_get_bitcell_period(), get_bitcell_period(drive), rate, sector, track, side);
disc_sector_track[drive] = track;
disc_sector_side[drive] = side;
disc_sector_drive = drive;
disc_sector_sector[drive] = sector;
disc_sector_n[drive] = sector_size;
disc_sector_reset_state(drive);
if (sector == SECTOR_FIRST)
disc_sector_state[drive] = STATE_READ_FIND_FIRST_SECTOR;
else if (sector == SECTOR_NEXT)
disc_sector_state[drive] = STATE_READ_FIND_NEXT_SECTOR;
else
disc_sector_state[drive] = STATE_READ_FIND_SECTOR;
}
void disc_sector_writesector(int drive, int sector, int track, int side, int rate, int sector_size)
{
// pclog("disc_sector_writesector: fdc_period=%i img_period=%i rate=%i\n", fdc_get_bitcell_period(), get_bitcell_period(), rate);
disc_sector_track[drive] = track;
disc_sector_side[drive] = side;
disc_sector_drive = drive;
disc_sector_sector[drive] = sector;
disc_sector_n[drive] = sector_size;
disc_sector_reset_state(drive);
disc_sector_state[drive] = STATE_WRITE_FIND_SECTOR;
}
void disc_sector_readaddress(int drive, int track, int side, int rate)
{
// pclog("disc_sector_readaddress: fdc_period=%i img_period=%i rate=%i track=%i side=%i\n", fdc_get_bitcell_period(), get_bitcell_period(), rate, track, side);
disc_sector_track[drive] = track;
disc_sector_side[drive] = side;
disc_sector_drive = drive;
disc_sector_reset_state(drive);
disc_sector_state[drive] = STATE_READ_FIND_ADDRESS;
}
void disc_sector_format(int drive, int track, int side, int rate, uint8_t fill)
{
disc_sector_track[drive] = track;
disc_sector_side[drive] = side;
disc_sector_drive = drive;
disc_sector_fill[drive] = fill;
disc_sector_reset_state(drive);
disc_sector_state[drive] = STATE_FORMAT_FIND;
}
void disc_sector_stop(int drive)
{
disc_sector_state[drive] = STATE_IDLE;
}
static void index_pulse(int drive)
{
if (disc_sector_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
void disc_sector_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(disc_sector_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 < disc_sector_count[drive][side]; j++)
{
s = &disc_sector_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) disc_sector_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 disc_sector_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 disc_sector_find_sector(int drive)
{
int side = disc_sector_side[drive];
int i = 0;
for (i = 0; i < disc_sector_count[drive][side]; i++)
{
if (id_positions[drive][side][i] == cur_track_pos[drive])
{
return i;
}
}
return -1;
}
int disc_sector_match(int drive)
{
int temp;
if (last_sector[drive] == NULL) return 0;
temp = (disc_sector_track[drive] == last_sector[drive]->c);
temp = temp && (disc_sector_side[drive] == last_sector[drive]->h);
temp = temp && (disc_sector_sector[drive] == last_sector[drive]->r);
if (disc_sector_n[drive])
{
temp = temp && (disc_sector_n[drive] == last_sector[drive]->n);
}
return temp;
}
int disc_sector_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 disc_sector_can_format(int drive)
{
int temp;
temp = !writeprot[drive];
temp = temp && !swwp;
temp = temp && disc_sector_can_read_address(drive);
temp = temp && (fdc_get_format_sectors() == disc_sector_count[drive][disc_sector_side[drive]]);
return temp;
}
int disc_sector_find_state(int drive)
{
int temp;
temp = (disc_sector_state[drive] == STATE_READ_FIND_SECTOR);
temp = temp || (disc_sector_state[drive] == STATE_READ_FIND_FIRST_SECTOR);
temp = temp || (disc_sector_state[drive] == STATE_READ_FIND_NEXT_SECTOR);
temp = temp || (disc_sector_state[drive] == STATE_WRITE_FIND_SECTOR);
temp = temp || (disc_sector_state[drive] == STATE_READ_FIND_ADDRESS);
temp = temp || (disc_sector_state[drive] == STATE_FORMAT_FIND);
return temp;
}
int disc_sector_read_state(int drive)
{
int temp;
temp = (disc_sector_state[drive] == STATE_READ_SECTOR);
temp = temp || (disc_sector_state[drive] == STATE_READ_FIRST_SECTOR);
temp = temp || (disc_sector_state[drive] == STATE_READ_NEXT_SECTOR);
return temp;
}
void disc_sector_poll()
{
sector_t *s;
int data;
int drive = disc_sector_drive;
int side = disc_sector_side[drive];
int found_sector = 0;
int b = 0;
if (disc_sector_state[drive] == STATE_SEEK)
{
cur_track_pos[drive]++;
cur_track_pos[drive] %= raw_tsize[drive];
return;
}
if (disc_sector_state[drive] == STATE_FORMAT_FIND)
{
if (!(disc_sector_can_format(drive)))
{
if (disc_sector_can_read_address(drive))
{
pclog("disc_sector_poll(): Disk is write protected or attempting to format wrong number of sectors per track\n");
fdc_writeprotect();
}
else
{
pclog("disc_sector_poll(): Unable to format at the requested density or bitcell period\n");
fdc_notfound();
}
disc_sector_state[drive] = STATE_IDLE;
disc_sector_reset_state(drive);
cur_track_pos[drive]++;
cur_track_pos[drive] %= raw_tsize[drive];
return;
}
}
// if (disc_sector_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 (disc_sector_read_state(drive) || (disc_sector_state[drive] == STATE_WRITE_SECTOR) || (disc_sector_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("disc_sector_poll(): Gap overrun at GAP4\n");
fdc_notfound();
disc_sector_state[drive] = STATE_IDLE;
disc_sector_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 (disc_sector_state[drive] != STATE_IDLE) index_count[drive]++;
if (disc_sector_read_state(drive) || (disc_sector_state[drive] == STATE_WRITE_SECTOR) || (disc_sector_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("disc_sector_poll(): Gap overrun at IAM\n");
fdc_notfound();
disc_sector_state[drive] = STATE_IDLE;
disc_sector_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 = disc_sector_find_sector(drive);
// pclog("Found sector: %i\n", found_sector);
cur_sector[drive] = found_sector;
last_sector[drive] = &disc_sector_data[drive][disc_sector_side[drive]][found_sector];
cur_rate[drive] = last_sector[drive]->rate;
if (!(disc_sector_can_read_address(drive))) last_sector[drive] = NULL;
if (disc_sector_read_state(drive) || (disc_sector_state[drive] == STATE_WRITE_SECTOR) || (disc_sector_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("disc_sector_poll(): Gap (%i) overrun at IDAM\n", fdc_get_gap());
fdc_notfound();
disc_sector_state[drive] = STATE_IDLE;
disc_sector_reset_state(drive);
cur_track_pos[drive]++;
cur_track_pos[drive] %= raw_tsize[drive];
return;
}
if ((disc_sector_state[drive] == STATE_FORMAT_FIND) && disc_sector_can_read_address(drive)) disc_sector_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 ((disc_sector_state[drive] == STATE_READ_FIND_ADDRESS) && !(disc_sector_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 ((disc_sector_state[drive] == STATE_READ_FIND_ADDRESS) && disc_sector_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);
disc_sector_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 (disc_sector_state[drive])
{
case STATE_READ_FIND_SECTOR:
if (disc_sector_match(drive) && disc_sector_can_read_address(drive)) disc_sector_state[drive] = STATE_READ_SECTOR;
break;
case STATE_READ_FIND_FIRST_SECTOR:
if ((cur_sector[drive] == 0) && disc_sector_can_read_address(drive)) disc_sector_state[drive] = STATE_READ_FIRST_SECTOR;
break;
case STATE_READ_FIND_NEXT_SECTOR:
if (disc_sector_can_read_address(drive)) disc_sector_state[drive] = STATE_READ_NEXT_SECTOR;
break;
case STATE_WRITE_FIND_SECTOR:
if (disc_sector_match(drive) && disc_sector_can_read_address(drive)) disc_sector_state[drive] = STATE_WRITE_SECTOR;
break;
}
}
else if (track_layout[drive][side][cur_track_pos[drive]] == BYTE_DATA)
{
if (disc_sector_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("disc_sector_poll(): Unable to send further data to the FDC\n");
disc_sector_state[drive] = STATE_IDLE;
disc_sector_reset_state(drive);
cur_track_pos[drive]++;
cur_track_pos[drive] %= raw_tsize[drive];
return;
}
}
if ((disc_sector_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("disc_sector_poll(): Unable to receive further data from the FDC\n");
disc_sector_state[drive] = STATE_IDLE;
disc_sector_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 ((disc_sector_state[drive] == STATE_FORMAT) && (last_sector[drive] != NULL))
{
if (!disable_write) last_sector[drive]->data[data_counter[drive]] = disc_sector_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 (disc_sector_read_state(drive) && (last_sector[drive] != NULL))
{
disc_sector_state[drive] = STATE_IDLE;
fdc_finishread(drive);
}
if ((disc_sector_state[drive] == STATE_WRITE_SECTOR) && (last_sector[drive] != NULL))
{
disc_sector_state[drive] = STATE_IDLE;
if (!disable_write) disc_sector_writeback[drive](drive, disc_sector_track[drive]);
fdc_finishread(drive);
}
if ((disc_sector_state[drive] == STATE_FORMAT) && (last_sector[drive] != NULL))
{
disc_sector_state[drive] = STATE_IDLE;
if (!disable_write) disc_sector_writeback[drive](drive, disc_sector_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 ((disc_sector_state[drive] != STATE_IDLE) && (disc_sector_state[drive] != STATE_SEEK))
{
if (index_count[drive] > 1)
{
if (disc_sector_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("disc_sector_poll(): Sector not found (%i %i %i %i)\n", disc_sector_track[drive], disc_sector_side[drive], disc_sector_sector[drive], disc_sector_n[drive]);
fdc_notfound();
disc_sector_state[drive] = STATE_IDLE;
disc_sector_reset_state(drive);
return;
}
}
}
if ((b != BYTE_GAP3) && (track_layout[drive][side][cur_track_pos[drive]] == BYTE_GAP3))
{
gap3_counter[drive] = 0;
}
}