mirror of
https://github.com/holub/mame
synced 2025-05-22 21:58:57 +03:00
1037 lines
24 KiB
C++
1037 lines
24 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Miodrag Milanovic
|
|
/*********************************************************************
|
|
|
|
formats/td0_dsk.c
|
|
|
|
TD0 disk images
|
|
|
|
*********************************************************************/
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "pool.h"
|
|
#include "flopimg.h"
|
|
|
|
#define BUFSZ 512 // new input buffer
|
|
|
|
/* 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)
|
|
/* character 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 */
|
|
|
|
struct td0dsk_tag
|
|
{
|
|
int heads;
|
|
int tracks;
|
|
int sector_size;
|
|
uint64_t track_offsets[84*2]; /* offset within data for each track */
|
|
uint8_t *data;
|
|
};
|
|
|
|
struct tdlzhuf {
|
|
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
|
|
};
|
|
|
|
|
|
struct td0dsk_t
|
|
{
|
|
io_generic *floppy_file;
|
|
uint64_t floppy_file_offset;
|
|
|
|
struct 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;
|
|
|
|
int data_read(uint8_t *buf, uint16_t size);
|
|
int next_word();
|
|
int GetBit();
|
|
int GetByte();
|
|
void StartHuff();
|
|
void reconst();
|
|
void update(int c);
|
|
int16_t DecodeChar();
|
|
int16_t DecodePosition();
|
|
void init_Decode();
|
|
int Decode(uint8_t *buf, int len);
|
|
};
|
|
|
|
//static td0dsk_t td0dsk;
|
|
|
|
struct floppy_image_legacy
|
|
{
|
|
struct io_generic io;
|
|
|
|
const struct FloppyFormat *floppy_option;
|
|
struct FloppyCallbacks format;
|
|
|
|
/* loaded track stuff */
|
|
int loaded_track_head;
|
|
int loaded_track_index;
|
|
uint32_t loaded_track_size;
|
|
void *loaded_track_data;
|
|
uint8_t loaded_track_status;
|
|
uint8_t flags;
|
|
|
|
/* tagging system */
|
|
object_pool *tags;
|
|
void *tag_data;
|
|
};
|
|
|
|
|
|
static struct td0dsk_tag *get_tag(floppy_image_legacy *floppy)
|
|
{
|
|
struct td0dsk_tag *tag;
|
|
tag = (td0dsk_tag *)floppy_tag((floppy_image_legacy *)floppy);
|
|
return tag;
|
|
}
|
|
|
|
|
|
|
|
FLOPPY_IDENTIFY( td0_dsk_identify )
|
|
{
|
|
uint8_t header[2];
|
|
|
|
floppy_image_read(floppy, header, 0, 2);
|
|
if (header[0]=='T' && header[1]=='D') {
|
|
*vote = 100;
|
|
} else if (header[0]=='t' && header[1]=='d') {
|
|
*vote = 100;
|
|
} else {
|
|
*vote = 0;
|
|
}
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
static int td0_get_heads_per_disk(floppy_image_legacy *floppy)
|
|
{
|
|
return get_tag(floppy)->heads;
|
|
}
|
|
|
|
static int td0_get_tracks_per_disk(floppy_image_legacy *floppy)
|
|
{
|
|
return get_tag(floppy)->tracks;
|
|
}
|
|
|
|
static uint64_t td0_get_track_offset(floppy_image_legacy *floppy, int head, int track)
|
|
{
|
|
return get_tag(floppy)->track_offsets[(track<<1) + head];
|
|
}
|
|
|
|
static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, uint64_t *offset)
|
|
{
|
|
uint64_t offs;
|
|
uint8_t *header;
|
|
uint8_t sectors_per_track;
|
|
int i;
|
|
|
|
if ((head < 0) || (head >= get_tag(floppy)->heads) || (track < 0) || (track >= get_tag(floppy)->tracks)
|
|
|| (sector < 0) )
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
|
|
// position on beginning of track data
|
|
offs = td0_get_track_offset(floppy, head, track);
|
|
|
|
// read track header
|
|
header = get_tag(floppy)->data + offs - 4;
|
|
|
|
// take number of sectors per track
|
|
sectors_per_track = header[0];
|
|
|
|
if (!sector_is_index) {
|
|
// when taking ID's return seek error if number is over counter
|
|
if (sector > sectors_per_track) {
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
}
|
|
|
|
// move trought sectors
|
|
for(i=0;i < sector-1;i++) {
|
|
header = get_tag(floppy)->data + offs;
|
|
offs+= 6;
|
|
if ((header[4] & 0x30)==0) {
|
|
offs+= 2;
|
|
offs+= header[6] + (header[7]<<8);
|
|
}
|
|
}
|
|
// read size of sector
|
|
header = get_tag(floppy)->data + offs;
|
|
get_tag(floppy)->sector_size = 1 << (header[3] + 7);
|
|
|
|
if (offset)
|
|
*offset = offs;
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static floperr_t internal_td0_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, void *buffer, size_t buflen)
|
|
{
|
|
uint64_t offset;
|
|
floperr_t err;
|
|
uint8_t *header;
|
|
int size,realsize,i;
|
|
int buff_pos;
|
|
int data_pos;
|
|
uint8_t *data;
|
|
uint8_t *buf;
|
|
|
|
buf = (uint8_t*)buffer;
|
|
// take sector offset
|
|
err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
|
|
if (err)
|
|
return err;
|
|
|
|
// read sector header
|
|
header = get_tag(floppy)->data + offset;
|
|
offset+=6;
|
|
// if there is no date just jump out
|
|
if ((header[4] & 0x30)!=0) return FLOPPY_ERROR_SUCCESS;
|
|
|
|
offset+=3;
|
|
// take data size
|
|
size = header[6] + (header[7]<<8)-1;
|
|
// take real sector size
|
|
realsize = 1 << (header[3] + 7);
|
|
|
|
// read sector data
|
|
data = get_tag(floppy)->data + offset;
|
|
buff_pos = 0;
|
|
data_pos = 0;
|
|
|
|
switch(header[8]) {
|
|
case 0:
|
|
// encoding type 0
|
|
// - plain data
|
|
memcpy(buffer,data,size);
|
|
break;
|
|
case 1:
|
|
// encoding type 1
|
|
// - 2 bytes size
|
|
// - 2 bytes of data
|
|
// data is reapeted specified number of times
|
|
while(buff_pos<realsize) {
|
|
for (i=0;i<data[data_pos]+(data[data_pos+1] << 8);i++) {
|
|
buf[buff_pos] = data[data_pos+2];buff_pos++;
|
|
buf[buff_pos] = data[data_pos+3];buff_pos++;
|
|
}
|
|
data_pos+=4;
|
|
}
|
|
break;
|
|
case 2:
|
|
// encoding type 2
|
|
// - if first byte is zero next byte represent size of
|
|
// plain data after it
|
|
// - if different then zero when multiply by 2 represent
|
|
// size of data that should be reapeted next byte times
|
|
while(buff_pos<realsize) {
|
|
if (data[data_pos]==0x00) {
|
|
int size_ = data[data_pos+1];
|
|
memcpy(buf+buff_pos,data + data_pos + 2,size_);
|
|
data_pos += 2 + size_;
|
|
buff_pos += size_;
|
|
} else {
|
|
int size_ = 2*data[data_pos];
|
|
int repeat = data[data_pos+1];
|
|
data_pos+=2;
|
|
|
|
for (i=0;i<repeat;i++) {
|
|
memcpy(buf + buff_pos,data + data_pos,size_);
|
|
buff_pos += size_;
|
|
}
|
|
data_pos += size_;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return FLOPPY_ERROR_INTERNAL;
|
|
}
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
static floperr_t td0_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
|
|
{
|
|
return internal_td0_read_sector(floppy, head, track, sector, false, buffer, buflen);
|
|
}
|
|
|
|
static floperr_t td0_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
|
|
{
|
|
return internal_td0_read_sector(floppy, head, track, sector, true, buffer, buflen);
|
|
}
|
|
|
|
static floperr_t td0_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
|
|
{
|
|
floperr_t err;
|
|
err = get_offset(floppy, head, track, sector, false, nullptr);
|
|
if (err)
|
|
return err;
|
|
|
|
if (sector_length) {
|
|
*sector_length = get_tag(floppy)->sector_size;
|
|
}
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
static floperr_t td0_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags)
|
|
{
|
|
floperr_t retVal;
|
|
uint64_t offset = 0;
|
|
uint8_t *sector_info;
|
|
|
|
retVal = get_offset(floppy, head, track, sector_index, false, &offset);
|
|
sector_info = get_tag(floppy)->data + offset;
|
|
if (cylinder)
|
|
*cylinder = sector_info[0];
|
|
if (side)
|
|
*side = sector_info[1];
|
|
if (sector)
|
|
*sector = sector_info[2];
|
|
if (sector_length) {
|
|
*sector_length = 1 << (sector_info[3] + 7);
|
|
}
|
|
if (flags) {
|
|
*flags = 0;
|
|
if (sector_info[4] & 0x02) *flags |= ID_FLAG_CRC_ERROR_IN_DATA_FIELD;
|
|
if (sector_info[4] & 0x04) *flags |= ID_FLAG_DELETED_DATA;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
int td0dsk_t::data_read(uint8_t *buf, uint16_t size)
|
|
{
|
|
uint64_t image_size = io_generic_size(floppy_file);
|
|
if (size > image_size - floppy_file_offset) {
|
|
size = image_size - floppy_file_offset;
|
|
}
|
|
io_generic_read(floppy_file,buf,floppy_file_offset,size);
|
|
floppy_file_offset += size;
|
|
return size;
|
|
}
|
|
|
|
|
|
/*
|
|
* Tables for encoding/decoding upper 6 bits of
|
|
* sliding dictionary pointer
|
|
*/
|
|
|
|
/* decoder table */
|
|
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,
|
|
};
|
|
|
|
int td0dsk_t::next_word()
|
|
{
|
|
if(tdctl.ibufndx >= tdctl.ibufcnt)
|
|
{
|
|
tdctl.ibufndx = 0;
|
|
tdctl.ibufcnt = data_read(tdctl.inbuf,BUFSZ);
|
|
if(tdctl.ibufcnt <= 0)
|
|
return(-1);
|
|
}
|
|
while (getlen <= 8) { // typically reads a word at a time
|
|
getbuf |= tdctl.inbuf[tdctl.ibufndx++] << (8 - getlen);
|
|
getlen += 8;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
int td0dsk_t::GetBit() /* get one bit */
|
|
{
|
|
int16_t i;
|
|
if(next_word() < 0)
|
|
return(-1);
|
|
i = getbuf;
|
|
getbuf <<= 1;
|
|
getlen--;
|
|
if(i < 0)
|
|
return(1);
|
|
else
|
|
return(0);
|
|
}
|
|
|
|
int td0dsk_t::GetByte() /* get a byte */
|
|
{
|
|
uint16_t i;
|
|
if(next_word() != 0)
|
|
return(-1);
|
|
i = getbuf;
|
|
getbuf <<= 8;
|
|
getlen -= 8;
|
|
i = i >> 8;
|
|
return((int) i);
|
|
}
|
|
|
|
|
|
|
|
/* initialize freq tree */
|
|
|
|
void td0dsk_t::StartHuff()
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < N_CHAR; i++) {
|
|
freq[i] = 1;
|
|
son[i] = i + T;
|
|
prnt[i + T] = i;
|
|
}
|
|
i = 0; j = N_CHAR;
|
|
while (j <= R) {
|
|
freq[j] = freq[i] + freq[i + 1];
|
|
son[j] = i;
|
|
prnt[i] = prnt[i + 1] = j;
|
|
i += 2; j++;
|
|
}
|
|
freq[T] = 0xffff;
|
|
prnt[R] = 0;
|
|
}
|
|
|
|
|
|
/* reconstruct freq tree */
|
|
|
|
void td0dsk_t::reconst()
|
|
{
|
|
int16_t i, j, k;
|
|
uint16_t f, l;
|
|
|
|
/* halven cumulative freq for leaf nodes */
|
|
j = 0;
|
|
for (i = 0; i < T; i++) {
|
|
if (son[i] >= T) {
|
|
freq[j] = (freq[i] + 1) / 2;
|
|
son[j] = 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 = freq[j] = freq[i] + freq[k];
|
|
for (k = j - 1; f < freq[k]; k--) {};
|
|
k++;
|
|
l = (j - k) * 2;
|
|
|
|
/* movmem() is Turbo-C dependent
|
|
rewritten to memmove() by Kenji */
|
|
|
|
/* movmem(&freq[k], &freq[k + 1], l); */
|
|
(void)memmove(&freq[k + 1], &freq[k], l);
|
|
freq[k] = f;
|
|
/* movmem(&son[k], &son[k + 1], l); */
|
|
(void)memmove(&son[k + 1], &son[k], l);
|
|
son[k] = i;
|
|
}
|
|
/* connect parent nodes */
|
|
for (i = 0; i < T; i++) {
|
|
if ((k = son[i]) >= T) {
|
|
prnt[k] = i;
|
|
} else {
|
|
prnt[k] = prnt[k + 1] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* update freq tree */
|
|
|
|
void td0dsk_t::update(int c)
|
|
{
|
|
int i, j, k, l;
|
|
|
|
if (freq[R] == MAX_FREQ) {
|
|
reconst();
|
|
}
|
|
c = prnt[c + T];
|
|
do {
|
|
k = ++freq[c];
|
|
|
|
/* swap nodes to keep the tree freq-ordered */
|
|
if (k > freq[l = c + 1]) {
|
|
while (k > freq[++l]) {};
|
|
l--;
|
|
freq[c] = freq[l];
|
|
freq[l] = k;
|
|
|
|
i = son[c];
|
|
prnt[i] = l;
|
|
if (i < T) prnt[i + 1] = l;
|
|
|
|
j = son[l];
|
|
son[l] = i;
|
|
|
|
prnt[j] = c;
|
|
if (j < T) prnt[j + 1] = c;
|
|
son[c] = j;
|
|
|
|
c = l;
|
|
}
|
|
} while ((c = prnt[c]) != 0); /* do it until reaching the root */
|
|
}
|
|
|
|
|
|
int16_t td0dsk_t::DecodeChar()
|
|
{
|
|
int ret;
|
|
uint16_t c;
|
|
|
|
c = 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 = GetBit()) < 0)
|
|
return(-1);
|
|
c += (unsigned) ret;
|
|
c = son[c];
|
|
}
|
|
c -= T;
|
|
update(c);
|
|
return c;
|
|
}
|
|
|
|
int16_t td0dsk_t::DecodePosition()
|
|
{
|
|
int16_t bit;
|
|
uint16_t i, j, c;
|
|
|
|
/* decode upper 6 bits from given table */
|
|
if((bit=GetByte()) < 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 = GetBit()) < 0)
|
|
return(-1);
|
|
i = (i << 1) + bit;
|
|
}
|
|
return(c | (i & 0x3f));
|
|
}
|
|
|
|
/* DeCompression
|
|
|
|
split out initialization code to init_Decode()
|
|
|
|
*/
|
|
|
|
void td0dsk_t::init_Decode()
|
|
{
|
|
int i;
|
|
getbuf = 0;
|
|
getlen = 0;
|
|
tdctl.ibufcnt= tdctl.ibufndx = 0; // input buffer is empty
|
|
tdctl.bufcnt = 0;
|
|
StartHuff();
|
|
for (i = 0; i < N - F; i++)
|
|
text_buf[i] = ' ';
|
|
tdctl.r = N - F;
|
|
}
|
|
|
|
|
|
int td0dsk_t::Decode(uint8_t *buf, int len) /* Decoding/Uncompressing */
|
|
{
|
|
int16_t c,pos;
|
|
int count; // was an unsigned long, seems unnecessary
|
|
for (count = 0; count < len; ) {
|
|
if(tdctl.bufcnt == 0) {
|
|
if((c = DecodeChar()) < 0)
|
|
return(count); // fatal error
|
|
if (c < 256) {
|
|
*(buf++) = c;
|
|
text_buf[tdctl.r++] = c;
|
|
tdctl.r &= (N - 1);
|
|
count++;
|
|
}
|
|
else {
|
|
if((pos = DecodePosition()) < 0)
|
|
return(count); // fatal error
|
|
tdctl.bufpos = (tdctl.r - pos - 1) & (N - 1);
|
|
tdctl.bufcnt = c - 255 + THRESHOLD;
|
|
tdctl.bufndx = 0;
|
|
}
|
|
}
|
|
else { // still chars from last string
|
|
while( tdctl.bufndx < tdctl.bufcnt && count < len ) {
|
|
c = text_buf[(tdctl.bufpos + tdctl.bufndx) & (N - 1)];
|
|
*(buf++) = c;
|
|
tdctl.bufndx++;
|
|
text_buf[tdctl.r++] = c;
|
|
tdctl.r &= (N - 1);
|
|
count++;
|
|
}
|
|
// reset bufcnt after copy string from text_buf[]
|
|
if(tdctl.bufndx >= tdctl.bufcnt)
|
|
tdctl.bufndx = tdctl.bufcnt = 0;
|
|
}
|
|
}
|
|
return(count); // count == len, success
|
|
}
|
|
|
|
|
|
FLOPPY_CONSTRUCT( td0_dsk_construct )
|
|
{
|
|
td0dsk_t state;
|
|
struct FloppyCallbacks *callbacks;
|
|
struct td0dsk_tag *tag;
|
|
uint8_t *header;
|
|
int number_of_sectors;
|
|
int position;
|
|
int i;
|
|
int track;
|
|
|
|
if(params)
|
|
{
|
|
// create
|
|
return FLOPPY_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
tag = (struct td0dsk_tag *) floppy_create_tag(floppy, sizeof(struct td0dsk_tag));
|
|
if (!tag)
|
|
return FLOPPY_ERROR_OUTOFMEMORY;
|
|
|
|
tag->data = (uint8_t*)malloc(floppy_image_size(floppy));
|
|
if (tag->data==nullptr) {
|
|
return FLOPPY_ERROR_OUTOFMEMORY;
|
|
}
|
|
floppy_image_read(floppy, tag->data, 0, floppy_image_size(floppy));
|
|
header = tag->data;
|
|
|
|
if (header[0]=='t') {
|
|
uint8_t obuf[BUFSZ];
|
|
int rd;
|
|
int off = 12;
|
|
int size = 0;
|
|
state.floppy_file = &(floppy->io);
|
|
state.init_Decode();
|
|
state.floppy_file_offset = 12;
|
|
do
|
|
{
|
|
if((rd = state.Decode(obuf, BUFSZ)) > 0) size += rd;
|
|
} while(rd == BUFSZ);
|
|
memcpy(obuf,tag->data,12);
|
|
free(tag->data);
|
|
tag->data = (uint8_t*)malloc(size+12);
|
|
if (tag->data==nullptr) {
|
|
return FLOPPY_ERROR_OUTOFMEMORY;
|
|
}
|
|
memcpy(tag->data,obuf,12);
|
|
state.floppy_file_offset = 12;
|
|
state.init_Decode();
|
|
do
|
|
{
|
|
if((rd = state.Decode(obuf, BUFSZ)) > 0) {
|
|
memcpy(tag->data+off,obuf,rd);
|
|
off += rd;
|
|
}
|
|
} while(rd == BUFSZ);
|
|
}
|
|
header = tag->data;
|
|
tag->heads = header[9];
|
|
if (tag->heads > 1) {
|
|
tag->heads = 2;
|
|
}
|
|
|
|
// header len + comment header + comment len
|
|
position = 12;
|
|
if (header[7] & 0x80) {
|
|
position += 10 + header[14] + (header[15]<<8);
|
|
}
|
|
tag->tracks = 0;
|
|
do {
|
|
// read track header
|
|
header = tag->data + position;
|
|
track = header[1];
|
|
number_of_sectors = header[0];
|
|
if (number_of_sectors!=0xff){
|
|
position+=4;
|
|
tag->track_offsets[(track<<1) + (header[2] & 1)] = position;
|
|
for(i=0;i<number_of_sectors;i++) {
|
|
// read sector header
|
|
header = tag->data + position;
|
|
position+=6;
|
|
// read sector size
|
|
if ((header[4] & 0x30)==0) {
|
|
// if there is sector data
|
|
header = tag->data + position;
|
|
position+=2;
|
|
// skip sector data
|
|
position+= header[0] + (header[1]<<8);
|
|
}
|
|
}
|
|
tag->tracks++;
|
|
}
|
|
} while(number_of_sectors!=0xff);
|
|
tag->tracks++;
|
|
|
|
callbacks = floppy_callbacks(floppy);
|
|
callbacks->read_sector = td0_read_sector;
|
|
callbacks->read_indexed_sector = td0_read_indexed_sector;
|
|
callbacks->get_sector_length = td0_get_sector_length;
|
|
callbacks->get_heads_per_disk = td0_get_heads_per_disk;
|
|
callbacks->get_tracks_per_disk = td0_get_tracks_per_disk;
|
|
callbacks->get_indexed_sector_info = td0_get_indexed_sector_info;
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
FLOPPY_DESTRUCT( td0_dsk_destruct )
|
|
{
|
|
struct td0dsk_tag *tag = get_tag(floppy);
|
|
free(tag->data);
|
|
tag->data = nullptr;
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*********************************************************************
|
|
|
|
formats/td0_dsk.h
|
|
|
|
Teledisk disk images
|
|
|
|
*********************************************************************/
|
|
|
|
#include "td0_dsk.h"
|
|
|
|
td0_format::td0_format()
|
|
{
|
|
}
|
|
|
|
const char *td0_format::name() const
|
|
{
|
|
return "td0";
|
|
}
|
|
|
|
const char *td0_format::description() const
|
|
{
|
|
return "Teledisk disk image";
|
|
}
|
|
|
|
const char *td0_format::extensions() const
|
|
{
|
|
return "td0";
|
|
}
|
|
|
|
int td0_format::identify(io_generic *io, uint32_t form_factor)
|
|
{
|
|
uint8_t h[7];
|
|
|
|
io_generic_read(io, h, 0, 7);
|
|
if(((h[0] == 'T') && (h[1] == 'D')) || ((h[0] == 't') && (h[1] == 'd')))
|
|
{
|
|
return 100;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
|
|
{
|
|
int track_count = 0;
|
|
int head_count = 0;
|
|
int track_spt;
|
|
int offset = 0;
|
|
const int max_size = 4*1024*1024; // 4MB ought to be large enough for any floppy
|
|
std::vector<uint8_t> imagebuf(max_size);
|
|
uint8_t header[12];
|
|
|
|
io_generic_read(io, header, 0, 12);
|
|
head_count = header[9];
|
|
|
|
if(header[0] == 't')
|
|
{
|
|
td0dsk_t disk_decode;
|
|
|
|
disk_decode.floppy_file = io;
|
|
disk_decode.init_Decode();
|
|
disk_decode.floppy_file_offset = 12;
|
|
disk_decode.Decode(&imagebuf[0], max_size);
|
|
}
|
|
else
|
|
io_generic_read(io, &imagebuf[0], 12, io_generic_size(io));
|
|
|
|
if(header[7] & 0x80)
|
|
offset = 10 + imagebuf[2] + (imagebuf[3] << 8);
|
|
|
|
track_spt = imagebuf[offset];
|
|
if(track_spt == 255) // Empty file?
|
|
return false;
|
|
|
|
switch(header[6])
|
|
{
|
|
case 2:
|
|
if((imagebuf[offset + 2] & 0x7f) == 2) // ?
|
|
{
|
|
if(head_count == 2)
|
|
image->set_variant(floppy_image::DSHD);
|
|
else
|
|
return false; // single side hd?
|
|
break;
|
|
}
|
|
/* no break; could be qd, won't know until tracks are counted */
|
|
case 1:
|
|
if(head_count == 2)
|
|
image->set_variant(floppy_image::DSDD);
|
|
else
|
|
image->set_variant(floppy_image::SSDD);
|
|
break;
|
|
case 4:
|
|
if((imagebuf[offset + 2] & 0x7f) == 2) // ?
|
|
{
|
|
if(head_count == 2)
|
|
image->set_variant(floppy_image::DSHD);
|
|
else
|
|
return false; // single side 3.5?
|
|
break;
|
|
} else
|
|
image->set_variant(floppy_image::SSDD);
|
|
break;
|
|
/* no break */
|
|
case 3:
|
|
if(head_count == 2)
|
|
{
|
|
if(form_factor == floppy_image::FF_525)
|
|
image->set_variant(floppy_image::DSQD);
|
|
else
|
|
image->set_variant(floppy_image::DSDD);
|
|
}
|
|
else
|
|
{
|
|
if(form_factor == floppy_image::FF_525)
|
|
image->set_variant(floppy_image::SSQD);
|
|
else
|
|
return false; // single side 3.5?
|
|
}
|
|
break;
|
|
}
|
|
|
|
static const int rates[3] = { 250000, 300000, 500000 };
|
|
int rate = (header[5] & 0x7f) >= 3 ? 500000 : rates[header[5] & 0x7f];
|
|
int rpm = form_factor == floppy_image::FF_8 || (form_factor == floppy_image::FF_525 && rate >= 300000) ? 360 : 300;
|
|
int base_cell_count = rate*60/rpm;
|
|
|
|
while(track_spt != 255)
|
|
{
|
|
desc_pc_sector sects[256];
|
|
uint8_t sect_data[65536];
|
|
int sdatapos = 0;
|
|
int track = imagebuf[offset + 1];
|
|
int head = imagebuf[offset + 2] & 1;
|
|
bool fm = (header[5] & 0x80) || (imagebuf[offset + 2] & 0x80); // ?
|
|
offset += 4;
|
|
for(int i = 0; i < track_spt; i++)
|
|
{
|
|
uint8_t *hs = &imagebuf[offset];
|
|
uint16_t size;
|
|
offset += 6;
|
|
|
|
sects[i].track = hs[0];
|
|
sects[i].head = hs[1];
|
|
sects[i].sector = hs[2];
|
|
sects[i].size = hs[3];
|
|
sects[i].deleted = (hs[4] & 4) == 4;
|
|
sects[i].bad_crc = (hs[4] & 2) == 2;
|
|
|
|
if(hs[4] & 0x30)
|
|
size = 0;
|
|
else
|
|
{
|
|
offset += 3;
|
|
size = 128 << hs[3];
|
|
int j, k;
|
|
switch(hs[8])
|
|
{
|
|
default:
|
|
return false;
|
|
case 0:
|
|
memcpy(§_data[sdatapos], &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)
|
|
{
|
|
sect_data[sdatapos + j] = hs[11];
|
|
sect_data[sdatapos + j + 1] = hs[12];
|
|
}
|
|
if(k < size)
|
|
memset(§_data[sdatapos + k], '\0', size - k);
|
|
break;
|
|
case 2:
|
|
k = 0;
|
|
while(k < size)
|
|
{
|
|
uint16_t len = imagebuf[offset];
|
|
uint16_t rep = imagebuf[offset + 1];
|
|
offset += 2;
|
|
if(!len)
|
|
{
|
|
memcpy(§_data[sdatapos + k], &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(§_data[sdatapos + j + k], &imagebuf[offset], len);
|
|
k += rep;
|
|
offset += len;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
sects[i].actual_size = size;
|
|
|
|
if(size)
|
|
{
|
|
sects[i].data = §_data[sdatapos];
|
|
sdatapos += size;
|
|
}
|
|
else
|
|
sects[i].data = nullptr;
|
|
}
|
|
track_count = track;
|
|
|
|
if(fm)
|
|
build_pc_track_fm(track, head, image, base_cell_count, track_spt, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
|
|
else
|
|
build_pc_track_mfm(track, head, image, base_cell_count*2, track_spt, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
|
|
|
|
track_spt = imagebuf[offset];
|
|
}
|
|
if((track_count > 50) && (form_factor == floppy_image::FF_525)) // ?
|
|
{
|
|
if(image->get_variant() == floppy_image::DSDD)
|
|
image->set_variant(floppy_image::DSQD);
|
|
else if(image->get_variant() == floppy_image::SSDD)
|
|
image->set_variant(floppy_image::SSQD);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool td0_format::save(io_generic *io, floppy_image *image)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool td0_format::supports_save() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const floppy_format_type FLOPPY_TD0_FORMAT = &floppy_image_format_creator<td0_format>;
|