2017-05-30 03:38:38 +02:00
|
|
|
/*
|
2019-12-05 21:36:28 +01:00
|
|
|
* 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.
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2019-12-05 21:36:28 +01:00
|
|
|
* This file is part of the 86Box distribution.
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2017-08-24 01:14:39 -04:00
|
|
|
* Implementation of the Teledisk floppy image format.
|
2017-05-30 03:38:38 +02:00
|
|
|
*
|
2020-03-25 00:46:02 +02:00
|
|
|
*
|
2017-08-24 01:14:39 -04:00
|
|
|
*
|
2019-12-05 21:36:28 +01:00
|
|
|
* Authors: Milodrag Milanovic,
|
2017-05-30 03:38:38 +02:00
|
|
|
* Haruhiko OKUMURA,
|
|
|
|
|
* Haruyasu YOSHIZAKI,
|
|
|
|
|
* Kenji RIKITAKE,
|
2019-12-05 21:36:28 +01:00
|
|
|
* Miran Grca, <mgrca8@gmail.com>
|
|
|
|
|
* Fred N. van Kempen, <decwiz@yahoo.com>
|
2018-03-19 01:02:04 +01:00
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
*
|
2019-12-05 21:36:28 +01:00
|
|
|
* 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.
|
2016-09-29 21:54:34 +02:00
|
|
|
*/
|
2018-05-21 19:04:05 +02:00
|
|
|
#include <stdarg.h>
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <stdint.h>
|
2018-05-21 19:04:05 +02:00
|
|
|
#include <stdio.h>
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <string.h>
|
2017-09-04 01:52:29 -04:00
|
|
|
#include <stdlib.h>
|
2017-09-25 04:31:20 -04:00
|
|
|
#include <wchar.h>
|
2018-05-21 19:04:05 +02:00
|
|
|
#define HAVE_STDARG_H
|
2020-03-29 14:24:42 +02:00
|
|
|
#include <86box/86box.h>
|
|
|
|
|
#include <86box/timer.h>
|
|
|
|
|
#include <86box/plat.h>
|
|
|
|
|
#include <86box/fdd.h>
|
|
|
|
|
#include <86box/fdd_86f.h>
|
|
|
|
|
#include <86box/fdd_td0.h>
|
|
|
|
|
#include <86box/fdc.h>
|
2016-09-29 21:54:34 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
#define BUFSZ 512 /* new input buffer */
|
|
|
|
|
#define TD0_MAX_BUFSZ (1024UL*1024UL*4UL)
|
2016-09-29 21:54:34 +02:00
|
|
|
|
|
|
|
|
/* LZSS Parameters */
|
2018-03-19 01:02:04 +01:00
|
|
|
#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 */
|
2016-09-29 21:54:34 +02:00
|
|
|
|
|
|
|
|
/* Huffman coding parameters */
|
2018-03-19 01:02:04 +01:00
|
|
|
#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
|
2016-09-29 21:54:34 +02:00
|
|
|
/* update when cumulative frequency */
|
|
|
|
|
/* reaches to this value */
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2018-03-19 01:02:04 +01:00
|
|
|
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 */
|
2016-09-29 21:54:34 +02:00
|
|
|
} tdlzhuf;
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
typedef struct {
|
|
|
|
|
FILE *fdd_file;
|
|
|
|
|
off_t fdd_file_offset;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
tdlzhuf tdctl;
|
|
|
|
|
uint8_t text_buf[N + F - 1];
|
|
|
|
|
uint16_t freq[T + 1]; /* cumulative freq table */
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/*
|
|
|
|
|
* pointing parent nodes.
|
|
|
|
|
* area [T..(T + N_CHAR - 1)] are pointers for leaves
|
|
|
|
|
*/
|
|
|
|
|
int16_t prnt[T + N_CHAR];
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* pointing children nodes (son[], son[] + 1)*/
|
|
|
|
|
int16_t son[T];
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
uint16_t getbuf;
|
|
|
|
|
uint8_t getlen;
|
2016-09-29 21:54:34 +02:00
|
|
|
} td0dsk_t;
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
typedef struct {
|
|
|
|
|
uint8_t track;
|
|
|
|
|
uint8_t head;
|
|
|
|
|
uint8_t sector;
|
|
|
|
|
uint8_t size;
|
2019-12-05 21:36:28 +01:00
|
|
|
uint8_t flags;
|
|
|
|
|
uint8_t fm;
|
2018-03-19 01:02:04 +01:00
|
|
|
uint8_t *data;
|
2016-09-29 21:54:34 +02:00
|
|
|
} td0_sector_t;
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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];
|
2019-12-05 21:36:28 +01:00
|
|
|
uint8_t max_sector_size;
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2016-09-29 21:54:34 +02:00
|
|
|
} td0_t;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Tables for encoding/decoding upper 6 bits of
|
|
|
|
|
* sliding dictionary pointer
|
|
|
|
|
*/
|
|
|
|
|
static const uint8_t d_code[256] = {
|
2018-03-19 01:02:04 +01:00
|
|
|
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,
|
2016-09-29 21:54:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const uint8_t d_len[256] = {
|
2018-03-19 01:02:04 +01:00
|
|
|
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,
|
2016-09-29 21:54:34 +02:00
|
|
|
};
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
static td0_t *td0[FDD_NUM];
|
|
|
|
|
|
|
|
|
|
|
2018-05-21 19:04:05 +02:00
|
|
|
#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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-19 00:39:32 +02:00
|
|
|
#else
|
|
|
|
|
#define td0_log(fmt, ...)
|
|
|
|
|
#endif
|
2018-05-21 19:04:05 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
static void
|
|
|
|
|
fdd_image_read(int drive, char *buffer, uint32_t offset, uint32_t len)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
td0_t *dev = td0[drive];
|
|
|
|
|
|
2020-01-15 04:58:28 +01:00
|
|
|
if (fseek(dev->f, offset, SEEK_SET) == -1)
|
|
|
|
|
fatal("fdd_image_read(): Error seeking to the beginning of the file\n");
|
|
|
|
|
if (fread(buffer, 1, len, dev->f) != len)
|
|
|
|
|
fatal("fdd_image_read(): Error reading data\n");
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
static int
|
|
|
|
|
dsk_identify(int drive)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
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);
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
state_data_read(td0dsk_t *state, uint8_t *buf, uint16_t size)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
uint32_t image_size = 0;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2020-01-15 02:31:52 +01:00
|
|
|
if (fseek(state->fdd_file, state->fdd_file_offset, SEEK_SET) == -1)
|
|
|
|
|
fatal("TD0: Failed to seek in state_data_read()\n");
|
2020-01-15 03:48:33 +01:00
|
|
|
if (fread(buf, 1, size, state->fdd_file) != size)
|
|
|
|
|
fatal("TD0: Error reading data in state_data_read()\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
state->fdd_file_offset += size;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(size);
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
static int
|
|
|
|
|
state_next_word(td0dsk_t *state)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* get one bit */
|
|
|
|
|
static int
|
|
|
|
|
state_GetBit(td0dsk_t *state)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
int16_t i;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (state_next_word(state) < 0)
|
|
|
|
|
return(-1);
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
i = state->getbuf;
|
|
|
|
|
state->getbuf <<= 1;
|
|
|
|
|
state->getlen--;
|
|
|
|
|
if (i < 0)
|
|
|
|
|
return(1);
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* get a byte */
|
|
|
|
|
static int
|
|
|
|
|
state_GetByte(td0dsk_t *state)
|
|
|
|
|
{
|
|
|
|
|
uint16_t i;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (state_next_word(state) != 0)
|
|
|
|
|
return(-1);
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
i = state->getbuf;
|
|
|
|
|
state->getbuf <<= 8;
|
|
|
|
|
state->getlen -= 8;
|
|
|
|
|
i = i >> 8;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return((int) i);
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
/* 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;
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* reconstruct freq tree */
|
|
|
|
|
static void
|
|
|
|
|
state_reconst(td0dsk_t *state)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
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++;
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2018-10-24 00:36:07 +02:00
|
|
|
/* 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);
|
2018-03-19 01:02:04 +01:00
|
|
|
state->freq[k] = f;
|
2018-10-24 00:36:07 +02:00
|
|
|
memmove(&state->son[k + 1], &state->son[k], l);
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
/* update freq tree */
|
|
|
|
|
static void
|
|
|
|
|
state_update(td0dsk_t *state, int c)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
int i, j, k, l;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (state->freq[R] == MAX_FREQ)
|
|
|
|
|
state_reconst(state);
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
c = state->prnt[c + T];
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* do it until reaching the root */
|
|
|
|
|
do {
|
|
|
|
|
k = ++state->freq[c];
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* 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;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
i = state->son[c];
|
|
|
|
|
state->prnt[i] = l;
|
|
|
|
|
if (i < T) state->prnt[i + 1] = l;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
j = state->son[l];
|
|
|
|
|
state->son[l] = i;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
state->prnt[j] = c;
|
|
|
|
|
if (j < T) state->prnt[j + 1] = c;
|
|
|
|
|
state->son[c] = j;
|
|
|
|
|
|
|
|
|
|
c = l;
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
} while ((c = state->prnt[c]) != 0);
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
state_update(state, c);
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(c);
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
static int16_t
|
|
|
|
|
state_DecodePosition(td0dsk_t *state)
|
|
|
|
|
{
|
|
|
|
|
int16_t bit;
|
|
|
|
|
uint16_t i, j, c;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* decode upper 6 bits from given table */
|
|
|
|
|
if ((bit = state_GetByte(state)) < 0)
|
|
|
|
|
return(-1);
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
i = (uint16_t) bit;
|
|
|
|
|
c = (uint16_t)d_code[i] << 6;
|
|
|
|
|
j = d_len[i];
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* input lower 6 bits directly */
|
|
|
|
|
j -= 2;
|
|
|
|
|
while (j--) {
|
|
|
|
|
if ((bit = state_GetBit(state)) < 0)
|
|
|
|
|
return(-1);
|
|
|
|
|
i = (i << 1) + bit;
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(c | (i & 0x3f));
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* DeCompression - split out initialization code to init_Decode() */
|
|
|
|
|
static void
|
|
|
|
|
state_init_Decode(td0dsk_t *state)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
state->getbuf = 0;
|
|
|
|
|
state->getlen = 0;
|
|
|
|
|
state->tdctl.ibufcnt= state->tdctl.ibufndx = 0; /* input buffer is empty */
|
|
|
|
|
state->tdctl.bufcnt = 0;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
state_StartHuff(state);
|
|
|
|
|
for (i = 0; i < N - F; i++)
|
|
|
|
|
state->text_buf[i] = ' ';
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
state->tdctl.r = N - F;
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
/* Decoding/Uncompressing */
|
|
|
|
|
static int
|
|
|
|
|
state_Decode(td0dsk_t *state, uint8_t *buf, int len)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
} 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++;
|
2016-09-30 02:16:27 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
/* reset bufcnt after copy string from text_buf[] */
|
|
|
|
|
if (state->tdctl.bufndx >= state->tdctl.bufcnt)
|
|
|
|
|
state->tdctl.bufndx = state->tdctl.bufcnt = 0;
|
2017-07-26 15:02:03 +02:00
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(count); /* count == len, success */
|
2016-09-30 02:16:27 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
get_raw_tsize(int side_flags, int slower_rpm)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
uint32_t size;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
switch(side_flags & 0x27) {
|
|
|
|
|
case 0x22:
|
|
|
|
|
size = slower_rpm ? 5314 : 5208;
|
|
|
|
|
break;
|
2016-09-30 02:16:27 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
default:
|
|
|
|
|
case 0x02:
|
|
|
|
|
case 0x21:
|
|
|
|
|
size = slower_rpm ? 6375 : 6250;
|
|
|
|
|
break;
|
2016-09-30 02:16:27 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
case 0x01:
|
|
|
|
|
size = slower_rpm ? 7650 : 7500;
|
|
|
|
|
break;
|
2016-09-30 02:16:27 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
case 0x20:
|
|
|
|
|
size = slower_rpm ? 10629 : 10416;
|
|
|
|
|
break;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
case 0x00:
|
|
|
|
|
size = slower_rpm ? 12750 : 12500;
|
|
|
|
|
break;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
case 0x23:
|
|
|
|
|
size = slower_rpm ? 21258 : 20833;
|
|
|
|
|
break;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
case 0x03:
|
|
|
|
|
size = slower_rpm ? 25500 : 25000;
|
|
|
|
|
break;
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
case 0x25:
|
|
|
|
|
size = slower_rpm ? 42517 : 41666;
|
|
|
|
|
break;
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
case 0x05:
|
|
|
|
|
size = slower_rpm ? 51000 : 50000;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(size);
|
|
|
|
|
}
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2016-11-02 22:39:07 +01:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2019-12-05 21:36:28 +01:00
|
|
|
int track_spt, track_spt_adjusted;
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2019-12-05 21:36:28 +01:00
|
|
|
uint32_t id_field = 0;
|
2018-03-19 01:02:04 +01:00
|
|
|
uint32_t pre_sector = 0;
|
2019-12-05 21:36:28 +01:00
|
|
|
int32_t track_size = 0;
|
|
|
|
|
int32_t raw_tsize = 0;
|
2018-03-19 01:02:04 +01:00
|
|
|
uint32_t minimum_gap3 = 0;
|
|
|
|
|
uint32_t minimum_gap4 = 0;
|
|
|
|
|
int i, j, k;
|
2019-12-05 21:36:28 +01:00
|
|
|
int size_diff, gap_sum;
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
if (dev->f == NULL) {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: Attempted to initialize without loading a file first\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
fseek(dev->f, 0, SEEK_END);
|
|
|
|
|
file_size = ftell(dev->f);
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (file_size < 12) {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: File is too small to even contain the header\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file_size > TD0_MAX_BUFSZ) {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: File exceeds the maximum size\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fseek(dev->f, 0, SEEK_SET);
|
|
|
|
|
fread(header, 1, 12, dev->f);
|
|
|
|
|
head_count = header[9];
|
|
|
|
|
|
|
|
|
|
if (header[0] == 't') {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: File is compressed\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
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 {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: File is uncompressed\n");
|
2020-01-15 05:24:33 +01:00
|
|
|
if (fseek(dev->f, 12, SEEK_SET) == -1)
|
|
|
|
|
fatal("td0_initialize(): Error seeking to offet 12\n");
|
|
|
|
|
if (fread(dev->imagebuf, 1, file_size - 12, dev->f) != (file_size - 12))
|
|
|
|
|
fatal("td0_initialize(): Error reading image buffer\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (header[7] & 0x80)
|
|
|
|
|
offset = 10 + dev->imagebuf[2] + (dev->imagebuf[3] << 8);
|
|
|
|
|
|
|
|
|
|
track_spt = dev->imagebuf[offset];
|
|
|
|
|
if (track_spt == 255) {
|
|
|
|
|
/* Empty file? */
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: File has no tracks\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
density = (header[5] >> 1) & 3;
|
|
|
|
|
|
|
|
|
|
if (density == 3) {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: Unknown density\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2019-12-05 21:36:28 +01:00
|
|
|
dev->max_sector_size = (density == 1) ? 6 : 5; /* 8192 or 4096 bytes. */
|
2018-03-19 01:02:04 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1: /* 5.25" 360k: 300 rpm */
|
|
|
|
|
case 3: /* 3.5" 720k: 300 rpm */
|
|
|
|
|
dev->default_track_flags = 0x02;
|
2019-12-05 21:36:28 +01:00
|
|
|
dev->max_sector_size = 5; /* 4096 bytes. */
|
2018-03-19 01:02:04 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 4: /* 3.5" 1.44M: 300 rpm */
|
|
|
|
|
dev->default_track_flags = (density == 1) ? 0x00 : 0x02;
|
2019-12-05 21:36:28 +01:00
|
|
|
dev->max_sector_size = (density == 1) ? 6 : 5; /* 8192 or 4096 bytes. */
|
2018-03-19 01:02:04 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6: /* 3.5" 2.88M: 300 rpm */
|
|
|
|
|
dev->default_track_flags = (density == 1) ? 0x00 : ((density == 2) ? 0x03 : 0x02);
|
2019-12-05 21:36:28 +01:00
|
|
|
dev->max_sector_size = (density == 1) ? 6 : ((density == 2) ? 7 : 5); /* 16384, 8192, or 4096 bytes. */
|
2018-03-19 01:02:04 +01:00
|
|
|
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) {
|
2019-12-05 21:36:28 +01:00
|
|
|
track_spt_adjusted = track_spt;
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2019-12-05 21:36:28 +01:00
|
|
|
if (density == 2)
|
|
|
|
|
id_field = fm ? 54 : 63;
|
|
|
|
|
else
|
|
|
|
|
id_field = fm ? 35 : 44;
|
|
|
|
|
pre_sector = id_field + (fm ? 7 : 16);
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
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];
|
2019-12-05 21:36:28 +01:00
|
|
|
dev->sects[track][head][i].flags = hs[4];
|
|
|
|
|
dev->sects[track][head][i].fm = !!fm;
|
2018-03-19 01:02:04 +01:00
|
|
|
dev->sects[track][head][i].data = dbuf;
|
|
|
|
|
|
|
|
|
|
size = 128 << hs[3];
|
|
|
|
|
if ((total_size + size) >= TD0_MAX_BUFSZ) {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: Processed buffer overflow\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-05 21:36:28 +01:00
|
|
|
if (hs[4] & 0x30)
|
|
|
|
|
memset(dbuf, (hs[4] & 0x10) ? 0xf6 : 0x00, size);
|
|
|
|
|
else {
|
2018-03-19 01:02:04 +01:00
|
|
|
offset += 3;
|
|
|
|
|
switch (hs[8]) {
|
|
|
|
|
default:
|
2018-10-24 00:36:07 +02:00
|
|
|
td0_log("TD0: Image uses an unsupported sector data encoding: %i\n", hs[8]);
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2016-09-30 02:16:27 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
dbuf += size;
|
|
|
|
|
total_size += size;
|
2019-12-05 21:36:28 +01:00
|
|
|
|
|
|
|
|
if (hs[4] & 0x20) {
|
|
|
|
|
track_size += id_field;
|
|
|
|
|
track_spt_adjusted--;
|
|
|
|
|
} else if (hs[4] & 0x40)
|
2019-12-06 02:37:52 +01:00
|
|
|
track_size += (pre_sector - id_field + 3);
|
2019-12-05 21:36:28 +01:00
|
|
|
else {
|
|
|
|
|
if ((hs[4] & 0x02) || (hs[3] > (dev->max_sector_size - fm)))
|
|
|
|
|
track_size += (pre_sector + 3);
|
|
|
|
|
else
|
|
|
|
|
track_size += (pre_sector + size + 2);
|
|
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 02:35:11 +01:00
|
|
|
if (track > track_count)
|
|
|
|
|
track_count = track;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (track_spt != 255) {
|
|
|
|
|
dev->track_spt[track][head] = track_spt;
|
2016-09-30 02:16:27 +02:00
|
|
|
|
2019-12-05 21:36:28 +01:00
|
|
|
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;
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
raw_tsize = get_raw_tsize(dev->side_flags[track][head], 0);
|
2019-12-05 21:36:28 +01:00
|
|
|
minimum_gap3 = 12 * track_spt_adjusted;
|
|
|
|
|
size_diff = raw_tsize - track_size;
|
|
|
|
|
gap_sum = minimum_gap3 + minimum_gap4;
|
|
|
|
|
if (size_diff < gap_sum) {
|
2018-03-19 01:02:04 +01:00
|
|
|
/* 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);
|
2019-12-05 21:36:28 +01:00
|
|
|
size_diff = raw_tsize - track_size;
|
2019-12-06 02:37:52 +01:00
|
|
|
if ((size_diff < gap_sum) && !fdd_get_turbo(drive)) {
|
2018-03-19 01:02:04 +01:00
|
|
|
/* If we can't fit the sectors with a reasonable minimum gap even at 2% slower RPM, abort. */
|
2019-12-06 02:37:52 +01:00
|
|
|
td0_log("TD0: Unable to fit the %i sectors into drive %i, track %i, side %i\n", track_spt_adjusted, drive, track, head);
|
2018-03-19 01:02:04 +01:00
|
|
|
return 0;
|
2016-09-30 02:16:27 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-12-05 21:36:28 +01:00
|
|
|
dev->calculated_gap3_lengths[track][head] = (size_diff - minimum_gap4) / track_spt_adjusted;
|
2016-09-30 02:16:27 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
track_spt = dev->imagebuf[offset];
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if ((dev->disk_flags & 0x60) == 0x60)
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: Disk will rotate 2% below perfect RPM\n");
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
dev->tracks = track_count + 1;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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. */
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (head_count == 2)
|
|
|
|
|
dev->disk_flags |= 8; /* 2 sides */
|
2016-11-02 22:39:07 +01:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (dev->tracks <= 43)
|
|
|
|
|
dev->track_width &= ~1;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
dev->sides = head_count;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
dev->current_side_flags[0] = dev->side_flags[0][0];
|
|
|
|
|
dev->current_side_flags[1] = dev->side_flags[0][1];
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-05-21 19:04:05 +02:00
|
|
|
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);
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
return(1);
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
|
disk_flags(int drive)
|
2016-10-04 17:25:16 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
td0_t *dev = td0[drive];
|
|
|
|
|
|
|
|
|
|
return(dev->disk_flags);
|
2016-10-04 17:25:16 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
|
side_flags(int drive)
|
2016-10-05 00:47:50 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
td0_t *dev = td0[drive];
|
|
|
|
|
int side = 0;
|
|
|
|
|
uint16_t sflags = 0;
|
2016-10-05 00:47:50 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
side = fdd_get_head(drive);
|
|
|
|
|
sflags = dev->current_side_flags[side];
|
2016-10-05 00:47:50 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(sflags);
|
|
|
|
|
}
|
2016-10-05 00:47:50 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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];
|
2020-01-15 02:31:52 +01:00
|
|
|
int i = 0, cyl = c;
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
dev->current_sector_index[side] = 0;
|
2020-01-15 02:31:52 +01:00
|
|
|
if (cyl != dev->track) return;
|
|
|
|
|
for (i = 0; i < dev->track_spt[cyl][side]; i++) {
|
|
|
|
|
if ((dev->sects[cyl][side][i].track == c) &&
|
|
|
|
|
(dev->sects[cyl][side][i].head == h) &&
|
|
|
|
|
(dev->sects[cyl][side][i].sector == r) &&
|
|
|
|
|
(dev->sects[cyl][side][i].size == n)) {
|
2018-03-19 01:02:04 +01:00
|
|
|
dev->current_sector_index[side] = i;
|
2016-10-05 00:47:50 +02:00
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
2016-10-05 00:47:50 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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]);
|
2016-10-05 00:47:50 +02:00
|
|
|
}
|
|
|
|
|
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if ((id[2] >= 0x01) && (id[2] <= 0x08)) {
|
|
|
|
|
low_sectors++;
|
|
|
|
|
dev->xdf_ordered_pos[id[2]][side] = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if ((effective_sectors == 3) && (xdf_sectors == 3)) {
|
|
|
|
|
dev->current_side_flags[side] = 0x28;
|
|
|
|
|
return(1); /* 5.25" 2HD XDF */
|
|
|
|
|
}
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if ((effective_sectors == 4) && (xdf_sectors == 4)) {
|
|
|
|
|
dev->current_side_flags[side] = 0x08;
|
|
|
|
|
return(2); /* 3.5" 2HD XDF */
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
2016-10-04 17:25:16 +02:00
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
static int
|
|
|
|
|
track_is_interleave(int drive, int side, int track)
|
|
|
|
|
{
|
|
|
|
|
td0_t *dev = td0[drive];
|
|
|
|
|
int i, effective_sectors;
|
|
|
|
|
int track_spt;
|
2016-10-05 00:47:50 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
effective_sectors = 0;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
|
dev->interleave_ordered_pos[i][side] = 0;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
track_spt = dev->track_spt[track][side];
|
2018-01-17 18:43:36 +01:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (track_spt != 21) return(0);
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
if (effective_sectors == track_spt) return(1);
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
return(0);
|
|
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2017-07-26 00:16:54 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
2019-12-06 02:37:52 +01:00
|
|
|
int fm, sector_adjusted;
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2018-01-17 18:43:36 +01:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
for (side = 0; side < dev->sides; side++) {
|
|
|
|
|
track_rate = dev->current_side_flags[side] & 7;
|
2019-12-06 02:37:52 +01:00
|
|
|
/* Make sure 300 kbps @ 360 rpm is treated the same as 250 kbps @ 300 rpm. */
|
2018-03-19 01:02:04 +01:00
|
|
|
if (!track_rate && (dev->current_side_flags[side] & 0x20))
|
|
|
|
|
track_rate = 4;
|
2019-12-06 02:37:52 +01:00
|
|
|
if ((dev->current_side_flags[side] & 0x27) == 0x21)
|
|
|
|
|
track_rate = 2;
|
2018-03-19 01:02:04 +01:00
|
|
|
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];
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
track_gap2 = ((dev->current_side_flags[side] & 7) >= 3) ? 41 : 22;
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
xdf_type = track_is_xdf(drive, side, track);
|
2016-10-04 17:25:16 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
interleave_type = track_is_interleave(drive, side, track);
|
2016-10-05 00:47:50 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
current_pos = d86f_prepare_pretrack(drive, side, 0);
|
2019-12-06 02:37:52 +01:00
|
|
|
sector_adjusted = 0;
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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];
|
2016-10-04 17:25:16 +02:00
|
|
|
}
|
2017-07-26 00:16:54 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
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;
|
PIC rewrite, proper SMRAM API, complete SiS 471 rewrite and addition of 40x, 460, and 461, changes to mem.c/h, disabled Voodoo memory dumping on exit, bumped SDL Hardware scale quality to 2, bumped IDE/ATAPI drives to ATA-6, finally bumped emulator version to 3.0, redid the bus type ID's to allow for planned ATAPI hard disks, made SST flash set its high mappings to the correct address if the CPU is 16-bit, and added the SiS 401 AMI 486 Clone, AOpen Vi15G, and the Soyo 4SA2 (486 with SiS 496/497 that can boot from CD-ROM), assorted 286+ protected mode fixes (for slightly more accuracy), and fixes to 808x emulation (MS Word 1.0 and 1.10 for DOS now work correctly from floppy).
2020-10-14 23:15:01 +02:00
|
|
|
pclog("track %i, side %i, %i,%i,%i,%i %i\n", track, side, id[0], id[1], id[2], id[3], dev->sects[track][side][actual_sector].flags);
|
2019-12-05 21:36:28 +01:00
|
|
|
fm = dev->sects[track][side][actual_sector].fm;
|
2019-12-06 02:37:52 +01:00
|
|
|
if (((dev->sects[track][side][actual_sector].flags & 0x42) || (id[3] > (dev->max_sector_size - fm))) && !fdd_get_turbo(drive))
|
2019-12-05 21:36:28 +01:00
|
|
|
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);
|
2018-03-19 01:02:04 +01:00
|
|
|
|
2019-12-06 02:37:52 +01:00
|
|
|
if (sector_adjusted == 0)
|
2018-03-19 01:02:04 +01:00
|
|
|
d86f_initialize_last_sector_id(drive, id[0], id[1], id[2], id[3]);
|
2019-12-06 02:37:52 +01:00
|
|
|
|
|
|
|
|
if (!(dev->sects[track][side][actual_sector].flags & 0x40))
|
|
|
|
|
sector_adjusted++;
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
|
|
|
|
} 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];
|
2019-12-05 21:36:28 +01:00
|
|
|
fm = dev->sects[track][side][ordered_pos].fm;
|
2019-12-06 02:37:52 +01:00
|
|
|
if (((dev->sects[track][side][ordered_pos].flags & 0x42) || (id[3] > (dev->max_sector_size - fm))) && !fdd_get_turbo(drive))
|
2019-12-05 21:36:28 +01:00
|
|
|
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);
|
2018-03-19 01:02:04 +01:00
|
|
|
|
2019-12-06 02:37:52 +01:00
|
|
|
if (sector_adjusted == 0)
|
2018-03-19 01:02:04 +01:00
|
|
|
d86f_initialize_last_sector_id(drive, id[0], id[1], id[2], id[3]);
|
2019-12-06 02:37:52 +01:00
|
|
|
|
|
|
|
|
if (!(dev->sects[track][side][ordered_pos].flags & 0x40))
|
|
|
|
|
sector_adjusted++;
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
void
|
|
|
|
|
td0_init(void)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
memset(td0, 0x00, sizeof(td0));
|
2016-09-29 21:54:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
2018-10-24 00:36:07 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
void
|
|
|
|
|
td0_load(int drive, wchar_t *fn)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
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]));
|
2016-09-29 21:54:34 +02:00
|
|
|
return;
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
2016-09-29 21:54:34 +02:00
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
fwriteprot[drive] = writeprot[drive];
|
|
|
|
|
|
|
|
|
|
if (! dsk_identify(drive)) {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: Not a valid Teledisk image\n");
|
2018-10-24 00:36:07 +02:00
|
|
|
td0_abort(drive);
|
2018-03-19 01:02:04 +01:00
|
|
|
return;
|
|
|
|
|
} else {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: Valid Teledisk image\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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)) {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: Failed to initialize\n");
|
2018-10-24 00:36:07 +02:00
|
|
|
td0_abort(drive);
|
2018-03-19 01:02:04 +01:00
|
|
|
return;
|
|
|
|
|
} else {
|
2018-05-21 19:04:05 +02:00
|
|
|
td0_log("TD0: Initialized successfully\n");
|
2018-03-19 01:02:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
2017-07-26 00:16:54 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
td0_close(int drive)
|
2016-09-29 21:54:34 +02:00
|
|
|
{
|
2018-03-19 01:02:04 +01:00
|
|
|
td0_t *dev = td0[drive];
|
|
|
|
|
int i, j, k;
|
|
|
|
|
|
|
|
|
|
if (dev == NULL) return;
|
|
|
|
|
|
|
|
|
|
d86f_unregister(drive);
|
|
|
|
|
|
2018-10-24 00:36:07 +02:00
|
|
|
if (dev->imagebuf)
|
|
|
|
|
free(dev->imagebuf);
|
|
|
|
|
if (dev->processed_buf)
|
|
|
|
|
free(dev->processed_buf);
|
2018-03-19 01:02:04 +01:00
|
|
|
|
|
|
|
|
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;
|
2017-05-05 01:49:42 +02:00
|
|
|
}
|