3936 lines
118 KiB
C
3936 lines
118 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 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, <mgrca8@gmail.com>
|
|
* Fred N. van Kempen, <decwiz@yahoo.com>
|
|
*
|
|
* Copyright 2016-2019 Miran Grca.
|
|
* Copyright 2018-2019 Fred N. van Kempen.
|
|
*/
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <assert.h>
|
|
#include <wchar.h>
|
|
#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 <lzf.h>
|
|
#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;
|
|
}
|