1280 lines
32 KiB
C
1280 lines
32 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 Teledisk floppy image format.
|
|
*
|
|
* Version: @(#)fdd_td0.c 1.0.9 2019/12/05
|
|
*
|
|
* Authors: Milodrag Milanovic,
|
|
* Haruhiko OKUMURA,
|
|
* Haruyasu YOSHIZAKI,
|
|
* Kenji RIKITAKE,
|
|
* Miran Grca, <mgrca8@gmail.com>
|
|
* Fred N. van Kempen, <decwiz@yahoo.com>
|
|
*
|
|
* Based on Japanese version 29-NOV-1988
|
|
* LZSS coded by Haruhiko OKUMURA
|
|
* Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI
|
|
* Edited and translated to English by Kenji RIKITAKE
|
|
*
|
|
* Copyright 2013-2019 Milodrag Milanovic.
|
|
* Copyright 1988-2019 Haruhiko OKUMURA.
|
|
* Copyright 1988-2019 Haruyasu YOSHIZAKI.
|
|
* Copyright 1988-2019 Kenji RIKITAKE.
|
|
* Copyright 2016-2019 Miran Grca.
|
|
*/
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <wchar.h>
|
|
#define HAVE_STDARG_H
|
|
#include "../86box.h"
|
|
#include "../timer.h"
|
|
#include "../plat.h"
|
|
#include "fdd.h"
|
|
#include "fdd_86f.h"
|
|
#include "fdd_td0.h"
|
|
#include "fdc.h"
|
|
|
|
|
|
#define BUFSZ 512 /* new input buffer */
|
|
#define TD0_MAX_BUFSZ (1024UL*1024UL*4UL)
|
|
|
|
/* LZSS Parameters */
|
|
#define N 4096 /* Size of string buffer */
|
|
#define F 60 /* Size of look-ahead buffer */
|
|
#define THRESHOLD 2
|
|
#define NIL N /* End of tree's node */
|
|
|
|
/* Huffman coding parameters */
|
|
#define N_CHAR (256-THRESHOLD+F) /* code (= 0..N_CHAR-1) */
|
|
#define T (N_CHAR*2-1) /* Size of table */
|
|
#define R (T-1) /* root position */
|
|
#define MAX_FREQ 0x8000
|
|
/* update when cumulative frequency */
|
|
/* reaches to this value */
|
|
|
|
typedef struct {
|
|
uint16_t r,
|
|
bufcnt,bufndx,bufpos, /* string buffer */
|
|
/* the following to allow block reads
|
|
from input in next_word() */
|
|
ibufcnt,ibufndx; /* input buffer counters */
|
|
uint8_t inbuf[BUFSZ]; /* input buffer */
|
|
} tdlzhuf;
|
|
|
|
typedef struct {
|
|
FILE *fdd_file;
|
|
off_t fdd_file_offset;
|
|
|
|
tdlzhuf tdctl;
|
|
uint8_t text_buf[N + F - 1];
|
|
uint16_t freq[T + 1]; /* cumulative freq table */
|
|
|
|
/*
|
|
* pointing parent nodes.
|
|
* area [T..(T + N_CHAR - 1)] are pointers for leaves
|
|
*/
|
|
int16_t prnt[T + N_CHAR];
|
|
|
|
/* pointing children nodes (son[], son[] + 1)*/
|
|
int16_t son[T];
|
|
|
|
uint16_t getbuf;
|
|
uint8_t getlen;
|
|
} td0dsk_t;
|
|
|
|
typedef struct {
|
|
uint8_t track;
|
|
uint8_t head;
|
|
uint8_t sector;
|
|
uint8_t size;
|
|
uint8_t flags;
|
|
uint8_t fm;
|
|
uint8_t *data;
|
|
} td0_sector_t;
|
|
|
|
typedef struct {
|
|
FILE *f;
|
|
|
|
int tracks;
|
|
int track_width;
|
|
int sides;
|
|
uint16_t disk_flags;
|
|
uint16_t default_track_flags;
|
|
uint16_t side_flags[256][2];
|
|
uint8_t max_sector_size;
|
|
uint8_t track_in_file[256][2];
|
|
td0_sector_t sects[256][2][256];
|
|
uint8_t track_spt[256][2];
|
|
uint8_t gap3_len;
|
|
uint16_t current_side_flags[2];
|
|
int track;
|
|
int current_sector_index[2];
|
|
uint8_t calculated_gap3_lengths[256][2];
|
|
uint8_t xdf_ordered_pos[256][2];
|
|
uint8_t interleave_ordered_pos[256][2];
|
|
|
|
uint8_t *imagebuf;
|
|
uint8_t *processed_buf;
|
|
} td0_t;
|
|
|
|
|
|
/*
|
|
* Tables for encoding/decoding upper 6 bits of
|
|
* sliding dictionary pointer
|
|
*/
|
|
static const uint8_t d_code[256] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
|
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
|
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
|
|
0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
|
|
0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
|
|
0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
|
|
0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
|
|
0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
|
|
0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
|
|
0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
|
|
0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
|
|
0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
|
|
0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
|
|
0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
|
|
0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
|
|
0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
|
|
0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
|
|
};
|
|
|
|
static const uint8_t d_len[256] = {
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
|
};
|
|
|
|
|
|
static td0_t *td0[FDD_NUM];
|
|
|
|
|
|
#ifdef ENABLE_TD0_LOG
|
|
int td0_do_log = ENABLE_TD0_LOG;
|
|
|
|
|
|
static void
|
|
td0_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (td0_do_log)
|
|
{
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
#define td0_log(fmt, ...)
|
|
#endif
|
|
|
|
|
|
static void
|
|
fdd_image_read(int drive, char *buffer, uint32_t offset, uint32_t len)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
|
|
fseek(dev->f, offset, SEEK_SET);
|
|
fread(buffer, 1, len, dev->f);
|
|
}
|
|
|
|
|
|
static int
|
|
dsk_identify(int drive)
|
|
{
|
|
char header[2];
|
|
|
|
fdd_image_read(drive, header, 0, 2);
|
|
if (header[0]=='T' && header[1]=='D')
|
|
return(1);
|
|
else if (header[0]=='t' && header[1]=='d')
|
|
return(1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int
|
|
state_data_read(td0dsk_t *state, uint8_t *buf, uint16_t size)
|
|
{
|
|
uint32_t image_size = 0;
|
|
|
|
fseek(state->fdd_file, 0, SEEK_END);
|
|
image_size = ftell(state->fdd_file);
|
|
if (size > image_size - state->fdd_file_offset)
|
|
size = (image_size - state->fdd_file_offset) & 0xffff;
|
|
fseek(state->fdd_file, state->fdd_file_offset, SEEK_SET);
|
|
fread(buf, 1, size, state->fdd_file);
|
|
state->fdd_file_offset += size;
|
|
|
|
return(size);
|
|
}
|
|
|
|
|
|
static int
|
|
state_next_word(td0dsk_t *state)
|
|
{
|
|
if (state->tdctl.ibufndx >= state->tdctl.ibufcnt) {
|
|
state->tdctl.ibufndx = 0;
|
|
state->tdctl.ibufcnt = state_data_read(state, state->tdctl.inbuf,BUFSZ);
|
|
if (state->tdctl.ibufcnt == 0)
|
|
return(-1);
|
|
}
|
|
|
|
while (state->getlen <= 8) { /* typically reads a word at a time */
|
|
state->getbuf |= state->tdctl.inbuf[state->tdctl.ibufndx++] << (8 - state->getlen);
|
|
state->getlen += 8;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* get one bit */
|
|
static int
|
|
state_GetBit(td0dsk_t *state)
|
|
{
|
|
int16_t i;
|
|
|
|
if (state_next_word(state) < 0)
|
|
return(-1);
|
|
|
|
i = state->getbuf;
|
|
state->getbuf <<= 1;
|
|
state->getlen--;
|
|
if (i < 0)
|
|
return(1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* get a byte */
|
|
static int
|
|
state_GetByte(td0dsk_t *state)
|
|
{
|
|
uint16_t i;
|
|
|
|
if (state_next_word(state) != 0)
|
|
return(-1);
|
|
|
|
i = state->getbuf;
|
|
state->getbuf <<= 8;
|
|
state->getlen -= 8;
|
|
i = i >> 8;
|
|
|
|
return((int) i);
|
|
}
|
|
|
|
|
|
/* initialize freq tree */
|
|
static void
|
|
state_StartHuff(td0dsk_t *state)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < N_CHAR; i++) {
|
|
state->freq[i] = 1;
|
|
state->son[i] = i + T;
|
|
state->prnt[i + T] = i;
|
|
}
|
|
i = 0; j = N_CHAR;
|
|
while (j <= R) {
|
|
state->freq[j] = state->freq[i] + state->freq[i + 1];
|
|
state->son[j] = i;
|
|
state->prnt[i] = state->prnt[i + 1] = j;
|
|
i += 2; j++;
|
|
}
|
|
state->freq[T] = 0xffff;
|
|
state->prnt[R] = 0;
|
|
}
|
|
|
|
|
|
/* reconstruct freq tree */
|
|
static void
|
|
state_reconst(td0dsk_t *state)
|
|
{
|
|
int16_t i, j, k;
|
|
uint16_t f, l;
|
|
|
|
/* halven cumulative freq for leaf nodes */
|
|
j = 0;
|
|
for (i = 0; i < T; i++) {
|
|
if (state->son[i] >= T) {
|
|
state->freq[j] = (state->freq[i] + 1) / 2;
|
|
state->son[j] = state->son[i];
|
|
j++;
|
|
}
|
|
}
|
|
|
|
/* make a tree : first, connect children nodes */
|
|
for (i = 0, j = N_CHAR; j < T; i += 2, j++) {
|
|
k = i + 1;
|
|
f = state->freq[j] = state->freq[i] + state->freq[k];
|
|
for (k = j - 1; f < state->freq[k]; k--) {};
|
|
k++;
|
|
l = (j - k) * 2;
|
|
|
|
/* These *HAVE* to be memmove's as destination and source
|
|
can overlap, which memcpy can't handle. */
|
|
memmove(&state->freq[k + 1], &state->freq[k], l);
|
|
state->freq[k] = f;
|
|
memmove(&state->son[k + 1], &state->son[k], l);
|
|
state->son[k] = i;
|
|
}
|
|
|
|
/* connect parent nodes */
|
|
for (i = 0; i < T; i++) {
|
|
if ((k = state->son[i]) >= T)
|
|
state->prnt[k] = i;
|
|
else
|
|
state->prnt[k] = state->prnt[k + 1] = i;
|
|
}
|
|
}
|
|
|
|
|
|
/* update freq tree */
|
|
static void
|
|
state_update(td0dsk_t *state, int c)
|
|
{
|
|
int i, j, k, l;
|
|
|
|
if (state->freq[R] == MAX_FREQ)
|
|
state_reconst(state);
|
|
|
|
c = state->prnt[c + T];
|
|
|
|
/* do it until reaching the root */
|
|
do {
|
|
k = ++state->freq[c];
|
|
|
|
/* swap nodes to keep the tree freq-ordered */
|
|
if (k > state->freq[l = c + 1]) {
|
|
while (k > state->freq[++l]) {};
|
|
l--;
|
|
state->freq[c] = state->freq[l];
|
|
state->freq[l] = k;
|
|
|
|
i = state->son[c];
|
|
state->prnt[i] = l;
|
|
if (i < T) state->prnt[i + 1] = l;
|
|
|
|
j = state->son[l];
|
|
state->son[l] = i;
|
|
|
|
state->prnt[j] = c;
|
|
if (j < T) state->prnt[j + 1] = c;
|
|
state->son[c] = j;
|
|
|
|
c = l;
|
|
}
|
|
} while ((c = state->prnt[c]) != 0);
|
|
}
|
|
|
|
|
|
static int16_t
|
|
state_DecodeChar(td0dsk_t *state)
|
|
{
|
|
int ret;
|
|
uint16_t c;
|
|
|
|
c = state->son[R];
|
|
|
|
/*
|
|
* start searching tree from the root to leaves.
|
|
* choose node #(son[]) if input bit == 0
|
|
* else choose #(son[]+1) (input bit == 1)
|
|
*/
|
|
while (c < T) {
|
|
if ((ret = state_GetBit(state)) < 0)
|
|
return(-1);
|
|
c += (unsigned) ret;
|
|
c = state->son[c];
|
|
}
|
|
c -= T;
|
|
|
|
state_update(state, c);
|
|
|
|
return(c);
|
|
}
|
|
|
|
|
|
static int16_t
|
|
state_DecodePosition(td0dsk_t *state)
|
|
{
|
|
int16_t bit;
|
|
uint16_t i, j, c;
|
|
|
|
/* decode upper 6 bits from given table */
|
|
if ((bit = state_GetByte(state)) < 0)
|
|
return(-1);
|
|
|
|
i = (uint16_t) bit;
|
|
c = (uint16_t)d_code[i] << 6;
|
|
j = d_len[i];
|
|
|
|
/* input lower 6 bits directly */
|
|
j -= 2;
|
|
while (j--) {
|
|
if ((bit = state_GetBit(state)) < 0)
|
|
return(-1);
|
|
i = (i << 1) + bit;
|
|
}
|
|
|
|
return(c | (i & 0x3f));
|
|
}
|
|
|
|
|
|
/* DeCompression - split out initialization code to init_Decode() */
|
|
static void
|
|
state_init_Decode(td0dsk_t *state)
|
|
{
|
|
int i;
|
|
|
|
state->getbuf = 0;
|
|
state->getlen = 0;
|
|
state->tdctl.ibufcnt= state->tdctl.ibufndx = 0; /* input buffer is empty */
|
|
state->tdctl.bufcnt = 0;
|
|
|
|
state_StartHuff(state);
|
|
for (i = 0; i < N - F; i++)
|
|
state->text_buf[i] = ' ';
|
|
|
|
state->tdctl.r = N - F;
|
|
}
|
|
|
|
|
|
/* Decoding/Uncompressing */
|
|
static int
|
|
state_Decode(td0dsk_t *state, uint8_t *buf, int len)
|
|
{
|
|
int16_t c, pos;
|
|
int count; /* was an unsigned long, seems unnecessary */
|
|
|
|
for (count = 0; count < len; ) {
|
|
if (state->tdctl.bufcnt == 0) {
|
|
if ((c = state_DecodeChar(state)) < 0)
|
|
return(count); /* fatal error */
|
|
if (c < 256) {
|
|
*(buf++) = c & 0xff;
|
|
state->text_buf[state->tdctl.r++] = c & 0xff;
|
|
state->tdctl.r &= (N - 1);
|
|
count++;
|
|
} else {
|
|
if ((pos = state_DecodePosition(state)) < 0)
|
|
return(count); /* fatal error */
|
|
state->tdctl.bufpos = (state->tdctl.r - pos - 1) & (N - 1);
|
|
state->tdctl.bufcnt = c - 255 + THRESHOLD;
|
|
state->tdctl.bufndx = 0;
|
|
}
|
|
} else {
|
|
/* still chars from last string */
|
|
while (state->tdctl.bufndx < state->tdctl.bufcnt && count < len) {
|
|
c = state->text_buf[(state->tdctl.bufpos + state->tdctl.bufndx) & (N - 1)];
|
|
*(buf++) = c & 0xff;
|
|
state->tdctl.bufndx++;
|
|
state->text_buf[state->tdctl.r++] = c & 0xff;
|
|
state->tdctl.r &= (N - 1);
|
|
count++;
|
|
}
|
|
|
|
/* reset bufcnt after copy string from text_buf[] */
|
|
if (state->tdctl.bufndx >= state->tdctl.bufcnt)
|
|
state->tdctl.bufndx = state->tdctl.bufcnt = 0;
|
|
}
|
|
}
|
|
|
|
return(count); /* count == len, success */
|
|
}
|
|
|
|
|
|
static uint32_t
|
|
get_raw_tsize(int side_flags, int slower_rpm)
|
|
{
|
|
uint32_t size;
|
|
|
|
switch(side_flags & 0x27) {
|
|
case 0x22:
|
|
size = slower_rpm ? 5314 : 5208;
|
|
break;
|
|
|
|
default:
|
|
case 0x02:
|
|
case 0x21:
|
|
size = slower_rpm ? 6375 : 6250;
|
|
break;
|
|
|
|
case 0x01:
|
|
size = slower_rpm ? 7650 : 7500;
|
|
break;
|
|
|
|
case 0x20:
|
|
size = slower_rpm ? 10629 : 10416;
|
|
break;
|
|
|
|
case 0x00:
|
|
size = slower_rpm ? 12750 : 12500;
|
|
break;
|
|
|
|
case 0x23:
|
|
size = slower_rpm ? 21258 : 20833;
|
|
break;
|
|
|
|
case 0x03:
|
|
size = slower_rpm ? 25500 : 25000;
|
|
break;
|
|
|
|
case 0x25:
|
|
size = slower_rpm ? 42517 : 41666;
|
|
break;
|
|
|
|
case 0x05:
|
|
size = slower_rpm ? 51000 : 50000;
|
|
break;
|
|
}
|
|
|
|
return(size);
|
|
}
|
|
|
|
|
|
static int
|
|
td0_initialize(int drive)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
uint8_t header[12];
|
|
int fm, head, track;
|
|
int track_count = 0;
|
|
int head_count = 0;
|
|
int track_spt, track_spt_adjusted;
|
|
int offset = 0;
|
|
int density = 0;
|
|
int temp_rate = 0;
|
|
uint32_t file_size;
|
|
uint16_t len, rep;
|
|
td0dsk_t disk_decode;
|
|
uint8_t *hs;
|
|
uint16_t size;
|
|
uint8_t *dbuf = dev->processed_buf;
|
|
uint32_t total_size = 0;
|
|
uint32_t id_field = 0;
|
|
uint32_t pre_sector = 0;
|
|
int32_t track_size = 0;
|
|
int32_t raw_tsize = 0;
|
|
uint32_t minimum_gap3 = 0;
|
|
uint32_t minimum_gap4 = 0;
|
|
int i, j, k;
|
|
int size_diff, gap_sum;
|
|
|
|
if (dev->f == NULL) {
|
|
td0_log("TD0: Attempted to initialize without loading a file first\n");
|
|
return(0);
|
|
}
|
|
|
|
fseek(dev->f, 0, SEEK_END);
|
|
file_size = ftell(dev->f);
|
|
|
|
if (file_size < 12) {
|
|
td0_log("TD0: File is too small to even contain the header\n");
|
|
return(0);
|
|
}
|
|
|
|
if (file_size > TD0_MAX_BUFSZ) {
|
|
td0_log("TD0: File exceeds the maximum size\n");
|
|
return(0);
|
|
}
|
|
|
|
fseek(dev->f, 0, SEEK_SET);
|
|
fread(header, 1, 12, dev->f);
|
|
head_count = header[9];
|
|
|
|
if (header[0] == 't') {
|
|
td0_log("TD0: File is compressed\n");
|
|
disk_decode.fdd_file = dev->f;
|
|
state_init_Decode(&disk_decode);
|
|
disk_decode.fdd_file_offset = 12;
|
|
state_Decode(&disk_decode, dev->imagebuf, TD0_MAX_BUFSZ);
|
|
} else {
|
|
td0_log("TD0: File is uncompressed\n");
|
|
fseek(dev->f, 12, SEEK_SET);
|
|
fread(dev->imagebuf, 1, file_size - 12, dev->f);
|
|
}
|
|
|
|
if (header[7] & 0x80)
|
|
offset = 10 + dev->imagebuf[2] + (dev->imagebuf[3] << 8);
|
|
|
|
track_spt = dev->imagebuf[offset];
|
|
if (track_spt == 255) {
|
|
/* Empty file? */
|
|
td0_log("TD0: File has no tracks\n");
|
|
return(0);
|
|
}
|
|
|
|
density = (header[5] >> 1) & 3;
|
|
|
|
if (density == 3) {
|
|
td0_log("TD0: Unknown density\n");
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* We determine RPM from the drive type as well as we possibly can.
|
|
* This byte is actually the BIOS floppy drive type read by Teledisk
|
|
* from the CMOS.
|
|
*/
|
|
switch (header[6]) {
|
|
case 0: /* 5.25" 360k in 1.2M drive: 360 rpm
|
|
CMOS Drive type: None, value probably
|
|
reused by Teledisk */
|
|
case 2: /* 5.25" 1.2M 360 rpm */
|
|
case 5: /* 8"/5.25"/3.5" 1.25M 360 rpm */
|
|
dev->default_track_flags = (density == 1) ? 0x20 : 0x21;
|
|
dev->max_sector_size = (density == 1) ? 6 : 5; /* 8192 or 4096 bytes. */
|
|
break;
|
|
|
|
case 1: /* 5.25" 360k: 300 rpm */
|
|
case 3: /* 3.5" 720k: 300 rpm */
|
|
dev->default_track_flags = 0x02;
|
|
dev->max_sector_size = 5; /* 4096 bytes. */
|
|
break;
|
|
|
|
case 4: /* 3.5" 1.44M: 300 rpm */
|
|
dev->default_track_flags = (density == 1) ? 0x00 : 0x02;
|
|
dev->max_sector_size = (density == 1) ? 6 : 5; /* 8192 or 4096 bytes. */
|
|
break;
|
|
|
|
case 6: /* 3.5" 2.88M: 300 rpm */
|
|
dev->default_track_flags = (density == 1) ? 0x00 : ((density == 2) ? 0x03 : 0x02);
|
|
dev->max_sector_size = (density == 1) ? 6 : ((density == 2) ? 7 : 5); /* 16384, 8192, or 4096 bytes. */
|
|
break;
|
|
}
|
|
|
|
dev->disk_flags = header[5] & 0x06;
|
|
|
|
dev->track_width = (header[7] & 1) ^ 1;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
memset(dev->side_flags[i], 0, 4);
|
|
memset(dev->track_in_file[i], 0, 2);
|
|
memset(dev->calculated_gap3_lengths[i], 0, 2);
|
|
for (j = 0; j < 2; j++)
|
|
memset(dev->sects[i][j], 0, sizeof(td0_sector_t));
|
|
}
|
|
|
|
while (track_spt != 255) {
|
|
track_spt_adjusted = track_spt;
|
|
|
|
track = dev->imagebuf[offset + 1];
|
|
head = dev->imagebuf[offset + 2] & 1;
|
|
fm = (header[5] & 0x80) || (dev->imagebuf[offset + 2] & 0x80); /* ? */
|
|
dev->side_flags[track][head] = dev->default_track_flags | (fm ? 0 : 8);
|
|
dev->track_in_file[track][head] = 1;
|
|
offset += 4;
|
|
track_size = fm ? 73 : 146;
|
|
if (density == 2)
|
|
id_field = fm ? 54 : 63;
|
|
else
|
|
id_field = fm ? 35 : 44;
|
|
pre_sector = id_field + (fm ? 7 : 16);
|
|
|
|
for (i = 0; i < track_spt; i++) {
|
|
hs = &dev->imagebuf[offset];
|
|
offset += 6;
|
|
|
|
dev->sects[track][head][i].track = hs[0];
|
|
dev->sects[track][head][i].head = hs[1];
|
|
dev->sects[track][head][i].sector = hs[2];
|
|
dev->sects[track][head][i].size = hs[3];
|
|
dev->sects[track][head][i].flags = hs[4];
|
|
dev->sects[track][head][i].fm = !!fm;
|
|
dev->sects[track][head][i].data = dbuf;
|
|
|
|
size = 128 << hs[3];
|
|
if ((total_size + size) >= TD0_MAX_BUFSZ) {
|
|
td0_log("TD0: Processed buffer overflow\n");
|
|
return(0);
|
|
}
|
|
|
|
if (hs[4] & 0x30)
|
|
memset(dbuf, (hs[4] & 0x10) ? 0xf6 : 0x00, size);
|
|
else {
|
|
offset += 3;
|
|
switch (hs[8]) {
|
|
default:
|
|
td0_log("TD0: Image uses an unsupported sector data encoding: %i\n", hs[8]);
|
|
return(0);
|
|
|
|
case 0:
|
|
memcpy(dbuf, &dev->imagebuf[offset], size);
|
|
offset += size;
|
|
break;
|
|
|
|
case 1:
|
|
offset += 4;
|
|
k = (hs[9] + (hs[10] << 8)) * 2;
|
|
k = (k <= size) ? k : size;
|
|
for(j = 0; j < k; j += 2) {
|
|
dbuf[j] = hs[11];
|
|
dbuf[j + 1] = hs[12];
|
|
}
|
|
if (k < size)
|
|
memset(&(dbuf[k]), 0, size - k);
|
|
break;
|
|
|
|
case 2:
|
|
k = 0;
|
|
while (k < size) {
|
|
len = dev->imagebuf[offset];
|
|
rep = dev->imagebuf[offset + 1];
|
|
offset += 2;
|
|
if (! len) {
|
|
memcpy(&(dbuf[k]), &dev->imagebuf[offset], rep);
|
|
offset += rep;
|
|
k += rep;
|
|
} else {
|
|
len = (1 << len);
|
|
rep = len * rep;
|
|
rep = ((rep + k) <= size) ? rep : (size - k);
|
|
for(j = 0; j < rep; j += len)
|
|
memcpy(&(dbuf[j + k]), &dev->imagebuf[offset], len);
|
|
k += rep;
|
|
offset += len;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
dbuf += size;
|
|
total_size += size;
|
|
|
|
if (hs[4] & 0x20) {
|
|
track_size += id_field;
|
|
track_spt_adjusted--;
|
|
} else if (hs[4] & 0x40)
|
|
track_size += (pre_sector - id_field + size + 2);
|
|
else {
|
|
if ((hs[4] & 0x02) || (hs[3] > (dev->max_sector_size - fm)))
|
|
track_size += (pre_sector + 3);
|
|
else
|
|
track_size += (pre_sector + size + 2);
|
|
}
|
|
}
|
|
|
|
if (track > track_count)
|
|
track_count = track;
|
|
|
|
if (track_spt != 255) {
|
|
dev->track_spt[track][head] = track_spt;
|
|
|
|
if ((dev->track_spt[track][head] == 8) && (dev->sects[track][head][0].size == 3))
|
|
dev->side_flags[track][head] = (dev->side_flags[track][head] & ~0x67) | 0x20;
|
|
|
|
raw_tsize = get_raw_tsize(dev->side_flags[track][head], 0);
|
|
minimum_gap3 = 12 * track_spt_adjusted;
|
|
size_diff = raw_tsize - track_size;
|
|
gap_sum = minimum_gap3 + minimum_gap4;
|
|
if (size_diff < gap_sum) {
|
|
/* If we can't fit the sectors with a reasonable minimum gap at perfect RPM, let's try 2% slower. */
|
|
raw_tsize = get_raw_tsize(dev->side_flags[track][head], 1);
|
|
/* Set disk flags so that rotation speed is 2% slower. */
|
|
dev->disk_flags |= (3 << 5);
|
|
size_diff = raw_tsize - track_size;
|
|
if (size_diff < gap_sum) {
|
|
/* If we can't fit the sectors with a reasonable minimum gap even at 2% slower RPM, abort. */
|
|
td0_log("TD0: Unable to fit the %i sectors in a track\n", track_spt_adjusted);
|
|
return 0;
|
|
}
|
|
}
|
|
dev->calculated_gap3_lengths[track][head] = (size_diff - minimum_gap4) / track_spt_adjusted;
|
|
|
|
track_spt = dev->imagebuf[offset];
|
|
}
|
|
}
|
|
|
|
if ((dev->disk_flags & 0x60) == 0x60)
|
|
td0_log("TD0: Disk will rotate 2% below perfect RPM\n");
|
|
|
|
dev->tracks = track_count + 1;
|
|
|
|
temp_rate = dev->default_track_flags & 7;
|
|
if ((dev->default_track_flags & 0x27) == 0x20)
|
|
temp_rate = 4;
|
|
dev->gap3_len = gap3_sizes[temp_rate][dev->sects[0][0][0].size][dev->track_spt[0][0]];
|
|
if (! dev->gap3_len)
|
|
dev->gap3_len = dev->calculated_gap3_lengths[0][0]; /* If we can't determine the GAP3 length, assume the smallest one we possibly know of. */
|
|
|
|
if (head_count == 2)
|
|
dev->disk_flags |= 8; /* 2 sides */
|
|
|
|
if (dev->tracks <= 43)
|
|
dev->track_width &= ~1;
|
|
|
|
dev->sides = head_count;
|
|
|
|
dev->current_side_flags[0] = dev->side_flags[0][0];
|
|
dev->current_side_flags[1] = dev->side_flags[0][1];
|
|
|
|
td0_log("TD0: File loaded: %i tracks, %i sides, disk flags: %02X, side flags: %02X, %02X, GAP3 length: %02X\n", dev->tracks, dev->sides, dev->disk_flags, dev->current_side_flags[0], dev->current_side_flags[1], dev->gap3_len);
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
static uint16_t
|
|
disk_flags(int drive)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
|
|
return(dev->disk_flags);
|
|
}
|
|
|
|
|
|
static uint16_t
|
|
side_flags(int drive)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
int side = 0;
|
|
uint16_t sflags = 0;
|
|
|
|
side = fdd_get_head(drive);
|
|
sflags = dev->current_side_flags[side];
|
|
|
|
return(sflags);
|
|
}
|
|
|
|
|
|
static void
|
|
set_sector(int drive, int side, uint8_t c, uint8_t h, uint8_t r, uint8_t n)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
int i = 0;
|
|
|
|
dev->current_sector_index[side] = 0;
|
|
if (c != dev->track) return;
|
|
for (i = 0; i < dev->track_spt[c][side]; i++) {
|
|
if ((dev->sects[c][side][i].track == c) &&
|
|
(dev->sects[c][side][i].head == h) &&
|
|
(dev->sects[c][side][i].sector == r) &&
|
|
(dev->sects[c][side][i].size == n)) {
|
|
dev->current_sector_index[side] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static uint8_t
|
|
poll_read_data(int drive, int side, uint16_t pos)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
|
|
return(dev->sects[dev->track][side][dev->current_sector_index[side]].data[pos]);
|
|
}
|
|
|
|
|
|
static int
|
|
track_is_xdf(int drive, int side, int track)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
uint8_t id[4] = { 0, 0, 0, 0 };
|
|
int i, effective_sectors, xdf_sectors;
|
|
int high_sectors, low_sectors;
|
|
int max_high_id, expected_high_count, expected_low_count;
|
|
|
|
effective_sectors = xdf_sectors = high_sectors = low_sectors = 0;
|
|
|
|
memset(dev->xdf_ordered_pos[side], 0, 256);
|
|
|
|
if (! track) {
|
|
if ((dev->track_spt[track][side] == 16) || (dev->track_spt[track][side] == 19)) {
|
|
if (! side) {
|
|
max_high_id = (dev->track_spt[track][side] == 19) ? 0x8B : 0x88;
|
|
expected_high_count = (dev->track_spt[track][side] == 19) ? 0x0B : 0x08;
|
|
expected_low_count = 8;
|
|
} else {
|
|
max_high_id = (dev->track_spt[track][side] == 19) ? 0x93 : 0x90;
|
|
expected_high_count = (dev->track_spt[track][side] == 19) ? 0x13 : 0x10;
|
|
expected_low_count = 0;
|
|
}
|
|
|
|
for (i = 0; i < dev->track_spt[track][side]; i++) {
|
|
id[0] = dev->sects[track][side][i].track;
|
|
id[1] = dev->sects[track][side][i].head;
|
|
id[2] = dev->sects[track][side][i].sector;
|
|
id[3] = dev->sects[track][side][i].size;
|
|
if (!(id[0]) && (id[1] == side) && (id[3] == 2)) {
|
|
if ((id[2] >= 0x81) && (id[2] <= max_high_id)) {
|
|
high_sectors++;
|
|
dev->xdf_ordered_pos[id[2]][side] = i;
|
|
}
|
|
|
|
if ((id[2] >= 0x01) && (id[2] <= 0x08)) {
|
|
low_sectors++;
|
|
dev->xdf_ordered_pos[id[2]][side] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((high_sectors == expected_high_count) && (low_sectors == expected_low_count)) {
|
|
dev->current_side_flags[side] = (dev->track_spt[track][side] == 19) ? 0x08 : 0x28;
|
|
return((dev->track_spt[track][side] == 19) ? 2 : 1);
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < dev->track_spt[track][side]; i++) {
|
|
id[0] = dev->sects[track][side][i].track;
|
|
id[1] = dev->sects[track][side][i].head;
|
|
id[2] = dev->sects[track][side][i].sector;
|
|
id[3] = dev->sects[track][side][i].size;
|
|
effective_sectors++;
|
|
if ((id[0] == track) && (id[1] == side) && !(id[2]) && !(id[3])) {
|
|
effective_sectors--;
|
|
}
|
|
if ((id[0] == track) && (id[1] == side) && (id[2] == (id[3] | 0x80))) {
|
|
xdf_sectors++;
|
|
dev->xdf_ordered_pos[id[2]][side] = i;
|
|
}
|
|
}
|
|
|
|
if ((effective_sectors == 3) && (xdf_sectors == 3)) {
|
|
dev->current_side_flags[side] = 0x28;
|
|
return(1); /* 5.25" 2HD XDF */
|
|
}
|
|
|
|
if ((effective_sectors == 4) && (xdf_sectors == 4)) {
|
|
dev->current_side_flags[side] = 0x08;
|
|
return(2); /* 3.5" 2HD XDF */
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int
|
|
track_is_interleave(int drive, int side, int track)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
int i, effective_sectors;
|
|
int track_spt;
|
|
|
|
effective_sectors = 0;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
dev->interleave_ordered_pos[i][side] = 0;
|
|
|
|
track_spt = dev->track_spt[track][side];
|
|
|
|
if (track_spt != 21) return(0);
|
|
|
|
for (i = 0; i < track_spt; i++) {
|
|
if ((dev->sects[track][side][i].track == track) && (dev->sects[track][side][i].head == side) && (dev->sects[track][side][i].sector >= 1) && (dev->sects[track][side][i].sector <= track_spt) && (dev->sects[track][side][i].size == 2)) {
|
|
effective_sectors++;
|
|
dev->interleave_ordered_pos[dev->sects[track][side][i].sector][side] = i;
|
|
}
|
|
}
|
|
|
|
if (effective_sectors == track_spt) return(1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
static void
|
|
td0_seek(int drive, int track)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
int side;
|
|
uint8_t id[4] = { 0, 0, 0, 0 };
|
|
int sector, current_pos;
|
|
int ssize = 512;
|
|
int track_rate = 0;
|
|
int track_gap2 = 22;
|
|
int track_gap3 = 12;
|
|
int xdf_type = 0;
|
|
int interleave_type = 0;
|
|
int is_trackx = 0;
|
|
int xdf_spt = 0;
|
|
int xdf_sector = 0;
|
|
int ordered_pos = 0;
|
|
int real_sector = 0;
|
|
int actual_sector = 0;
|
|
int fm;
|
|
|
|
if (dev->f == NULL) return;
|
|
|
|
if (!dev->track_width && fdd_doublestep_40(drive))
|
|
track /= 2;
|
|
|
|
d86f_set_cur_track(drive, track);
|
|
|
|
is_trackx = (track == 0) ? 0 : 1;
|
|
dev->track = track;
|
|
|
|
dev->current_side_flags[0] = dev->side_flags[track][0];
|
|
dev->current_side_flags[1] = dev->side_flags[track][1];
|
|
|
|
d86f_reset_index_hole_pos(drive, 0);
|
|
d86f_reset_index_hole_pos(drive, 1);
|
|
|
|
d86f_destroy_linked_lists(drive, 0);
|
|
d86f_destroy_linked_lists(drive, 1);
|
|
|
|
if (track > dev->tracks) {
|
|
d86f_zero_track(drive);
|
|
return;
|
|
}
|
|
|
|
for (side = 0; side < dev->sides; side++) {
|
|
track_rate = dev->current_side_flags[side] & 7;
|
|
if (!track_rate && (dev->current_side_flags[side] & 0x20))
|
|
track_rate = 4;
|
|
track_gap3 = gap3_sizes[track_rate][dev->sects[track][side][0].size][dev->track_spt[track][side]];
|
|
if (! track_gap3)
|
|
track_gap3 = dev->calculated_gap3_lengths[track][side];
|
|
|
|
track_gap2 = ((dev->current_side_flags[side] & 7) >= 3) ? 41 : 22;
|
|
|
|
xdf_type = track_is_xdf(drive, side, track);
|
|
|
|
interleave_type = track_is_interleave(drive, side, track);
|
|
|
|
current_pos = d86f_prepare_pretrack(drive, side, 0);
|
|
|
|
if (! xdf_type) {
|
|
for (sector = 0; sector < dev->track_spt[track][side]; sector++) {
|
|
if (interleave_type == 0) {
|
|
real_sector = dev->sects[track][side][sector].sector;
|
|
actual_sector = sector;
|
|
} else {
|
|
real_sector = dmf_r[sector];
|
|
actual_sector = dev->interleave_ordered_pos[real_sector][side];
|
|
}
|
|
|
|
id[0] = dev->sects[track][side][actual_sector].track;
|
|
id[1] = dev->sects[track][side][actual_sector].head;
|
|
id[2] = real_sector;
|
|
id[3] = dev->sects[track][side][actual_sector].size;
|
|
fm = dev->sects[track][side][actual_sector].fm;
|
|
if (((dev->sects[track][side][actual_sector].flags & 0x02) || (id[3] > (dev->max_sector_size - fm))) && !fdd_get_turbo(drive))
|
|
ssize = 3;
|
|
else
|
|
ssize = 128 << ((uint32_t) id[3]);
|
|
current_pos = d86f_prepare_sector(drive, side, current_pos, id, dev->sects[track][side][actual_sector].data, ssize, track_gap2, track_gap3, dev->sects[track][side][actual_sector].flags);
|
|
|
|
if (sector == 0)
|
|
d86f_initialize_last_sector_id(drive, id[0], id[1], id[2], id[3]);
|
|
}
|
|
} else {
|
|
xdf_type--;
|
|
xdf_spt = xdf_physical_sectors[xdf_type][is_trackx];
|
|
for (sector = 0; sector < xdf_spt; sector++) {
|
|
xdf_sector = (side * xdf_spt) + sector;
|
|
id[0] = track;
|
|
id[1] = side;
|
|
id[2] = xdf_disk_layout[xdf_type][is_trackx][xdf_sector].id.r;
|
|
id[3] = is_trackx ? (id[2] & 7) : 2;
|
|
ordered_pos = dev->xdf_ordered_pos[id[2]][side];
|
|
fm = dev->sects[track][side][ordered_pos].fm;
|
|
if (((dev->sects[track][side][ordered_pos].flags & 0x02) || (id[3] > (dev->max_sector_size - fm))) && !fdd_get_turbo(drive))
|
|
ssize = 3;
|
|
else
|
|
ssize = 128 << ((uint32_t) id[3]);
|
|
if (is_trackx)
|
|
current_pos = d86f_prepare_sector(drive, side, xdf_trackx_spos[xdf_type][xdf_sector], id, dev->sects[track][side][ordered_pos].data, ssize, track_gap2, xdf_gap3_sizes[xdf_type][is_trackx], dev->sects[track][side][ordered_pos].flags);
|
|
else
|
|
current_pos = d86f_prepare_sector(drive, side, current_pos, id, dev->sects[track][side][ordered_pos].data, ssize, track_gap2, xdf_gap3_sizes[xdf_type][is_trackx], dev->sects[track][side][ordered_pos].flags);
|
|
|
|
if (sector == 0)
|
|
d86f_initialize_last_sector_id(drive, id[0], id[1], id[2], id[3]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
td0_init(void)
|
|
{
|
|
memset(td0, 0x00, sizeof(td0));
|
|
}
|
|
|
|
|
|
void
|
|
td0_abort(int drive)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
|
|
if (dev->imagebuf)
|
|
free(dev->imagebuf);
|
|
if (dev->processed_buf)
|
|
free(dev->processed_buf);
|
|
if (dev->f)
|
|
fclose(dev->f);
|
|
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
|
|
free(dev);
|
|
td0[drive] = NULL;
|
|
}
|
|
|
|
|
|
void
|
|
td0_load(int drive, wchar_t *fn)
|
|
{
|
|
td0_t *dev;
|
|
uint32_t i;
|
|
|
|
d86f_unregister(drive);
|
|
|
|
writeprot[drive] = 1;
|
|
|
|
dev = (td0_t *)malloc(sizeof(td0_t));
|
|
memset(dev, 0x00, sizeof(td0_t));
|
|
td0[drive] = dev;
|
|
|
|
dev->f = plat_fopen(fn, L"rb");
|
|
if (dev->f == NULL) {
|
|
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
|
|
return;
|
|
}
|
|
|
|
fwriteprot[drive] = writeprot[drive];
|
|
|
|
if (! dsk_identify(drive)) {
|
|
td0_log("TD0: Not a valid Teledisk image\n");
|
|
td0_abort(drive);
|
|
return;
|
|
} else {
|
|
td0_log("TD0: Valid Teledisk image\n");
|
|
}
|
|
|
|
/* Allocate the processing buffers. */
|
|
i = 1024UL * 1024UL * 4UL;
|
|
dev->imagebuf = (uint8_t *)malloc(i);
|
|
memset(dev->imagebuf, 0x00, i);
|
|
dev->processed_buf = (uint8_t *)malloc(i);
|
|
memset(dev->processed_buf, 0x00, i);
|
|
|
|
if (! td0_initialize(drive)) {
|
|
td0_log("TD0: Failed to initialize\n");
|
|
td0_abort(drive);
|
|
return;
|
|
} else {
|
|
td0_log("TD0: Initialized successfully\n");
|
|
}
|
|
|
|
/* Attach this format to the D86F engine. */
|
|
d86f_handler[drive].disk_flags = disk_flags;
|
|
d86f_handler[drive].side_flags = side_flags;
|
|
d86f_handler[drive].writeback = null_writeback;
|
|
d86f_handler[drive].set_sector = set_sector;
|
|
d86f_handler[drive].read_data = poll_read_data;
|
|
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 = 1;
|
|
d86f_set_version(drive, 0x0063);
|
|
|
|
drives[drive].seek = td0_seek;
|
|
|
|
d86f_common_handlers(drive);
|
|
}
|
|
|
|
|
|
void
|
|
td0_close(int drive)
|
|
{
|
|
td0_t *dev = td0[drive];
|
|
int i, j, k;
|
|
|
|
if (dev == NULL) return;
|
|
|
|
d86f_unregister(drive);
|
|
|
|
if (dev->imagebuf)
|
|
free(dev->imagebuf);
|
|
if (dev->processed_buf)
|
|
free(dev->processed_buf);
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
for (j = 0; j < 2; j++) {
|
|
for (k = 0; k < 256; k++)
|
|
dev->sects[i][j][k].data = NULL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
memset(dev->side_flags[i], 0, 4);
|
|
memset(dev->track_in_file[i], 0, 2);
|
|
memset(dev->calculated_gap3_lengths[i], 0, 2);
|
|
for (j = 0; j < 2; j++)
|
|
memset(dev->sects[i][j], 0, sizeof(td0_sector_t));
|
|
}
|
|
|
|
if (dev->f != NULL)
|
|
fclose(dev->f);
|
|
|
|
/* Release resources. */
|
|
free(dev);
|
|
td0[drive] = NULL;
|
|
}
|