mirror of
https://github.com/holub/mame
synced 2025-05-23 22:20:01 +03:00
2037 lines
58 KiB
C
2037 lines
58 KiB
C
/*********************************************************************
|
|
*
|
|
* formats/ti99_dsk.c
|
|
*
|
|
* TI99 and Geneve disk images
|
|
* Sector Dump and Track Dump format
|
|
*
|
|
* used by TI-99/4, TI-99/4A, TI-99/8, SGCPU ("TI-99/4P"), and Geneve
|
|
*
|
|
* The Sector Dump Format is also known as v9t9 (named after the first TI
|
|
* emulator to use this format). It is a contiguous sequence of sector contents
|
|
* without track data. The first sector of the disk is located at the start of
|
|
* the image, while the last sector is at its end.
|
|
*
|
|
* The Track Dump Format is also known as pc99 (again, named after the first
|
|
* TI emulator to use this format). It is a contiguous sequence of track
|
|
* contents, containing all information including address marks and CRC, but it
|
|
* does not contain clock signals. Therefore, the format requires that the
|
|
* address marks be at the same positions within each track.
|
|
*
|
|
* Both formats allow for a broad range of medium sizes. All sectors are 256
|
|
* bytes long. The most common formats are 9 sectors per track, single-sided,
|
|
* 40 tracks, which yields 90 KiB of sector data (known as SSSD), and 18
|
|
* sectors per track, double-sided, and 40 tracks, which is 360 KiB (known as
|
|
* DSDD). There are rare occurances of 8/16 sectors/track
|
|
* (prototypical TI double-density controller) and 35 track media. Newer
|
|
* controllers and ROMs allow for up to 36 sectors per track and 80 tracks on
|
|
* both sides, which is 2,88 MiB (DSHD80).
|
|
*
|
|
* Different to earlier implementations, these format implementations provide
|
|
* full track read/write capabilities. For the SDF format, the missing track
|
|
* information is recreated, and a common interleave is assumed.
|
|
*
|
|
* Note that in principle, writing tracks may change track sizes.
|
|
* As neither one of the formats contains meta-information about track lengths,
|
|
* and as the image consists of a contiguous sequence of the tracks, we
|
|
* cannot allow different track sizes currently. Accordingly, the track size
|
|
* cannot be changed after the image has been created by imgtool.
|
|
* Hence, also sector count per tracks and recording type (FM or MFM) cannot be
|
|
* changed without breaking the image.
|
|
* For example, MFM disks with 18 sectors per track can only be reformatted to
|
|
* MFM/18. Moreover, while this could be handled in a future revision,
|
|
* the number of tracks should also remain unchanged.
|
|
*
|
|
* You can use blank image files (filled with zeros or arbitrary data) and use
|
|
* the Disk Manager cartridge to properly format them. Also, this can be done
|
|
* by imgtool. The image file must match the format length (representing the
|
|
* space on the physical medium).
|
|
*
|
|
* 80 track drive handling
|
|
*
|
|
* For the TI system there may be a situation where 40 track disks are used
|
|
* in 80 track drives. In these cases, the disk driver applies double-stepping,
|
|
* that is, it advances the head twice in order to seek the next track. We
|
|
* emulate this by dividing the track number by 2, which means that two tracks
|
|
* are mapped to one (0,1 -> 0; 2,3 -> 1; 4,5 -> 2; ...; 78,79 -> 39)
|
|
*
|
|
* Technical detail: The size of the track must be given before actually
|
|
* starting write_track as the controller implementation uses buffered
|
|
* transfer.
|
|
*
|
|
* Michael Zapf, Feb 2010
|
|
*
|
|
* TODO: Link to imgtool. imgtool still uses its own format creation
|
|
*
|
|
* FIXME: If image is broken with good first track and defect higher tracks,
|
|
* tdf_guess_geometry fails to define a geometry. The guessing should
|
|
* always be done.
|
|
********************************************************************/
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
|
|
#include "imageutl.h"
|
|
#include "ti99_dsk.h"
|
|
|
|
#define SECTOR_SIZE 256
|
|
#define TI99_IDAM_LENGTH 7
|
|
|
|
/*
|
|
Determines whether we are using 80 track drives. This variable is
|
|
necessary unless we get information about the dip
|
|
switch settings. It is set by the disk controller implementations
|
|
(bwg, hfdc, ti_fdc).
|
|
*/
|
|
static int use_80_track_drives = FALSE;
|
|
|
|
struct ti99dsk_geometry
|
|
{
|
|
UINT8 sides;
|
|
UINT8 tracksperside;
|
|
UINT8 secspertrack;
|
|
UINT8 density;
|
|
};
|
|
|
|
struct ti99dsk_tag
|
|
{
|
|
int heads;
|
|
int tracks;
|
|
int sectors;
|
|
int track_size; // Complete track; to be calculated
|
|
int format;
|
|
int first_idam;
|
|
int dam_offset;
|
|
};
|
|
|
|
/*
|
|
Parametrizes the format to use 80 track drives. For TI systems we
|
|
need to be able to emulate 40 track drives and 80 track drives.
|
|
This is set in the configuration, but the format has no direct access.
|
|
So as a preliminary solution we use this global function which sets the
|
|
flag. This function is called by the disk controller implementations
|
|
(bwg, hfdc, ti_fdc).
|
|
*/
|
|
void ti99_set_80_track_drives(int use80)
|
|
{
|
|
use_80_track_drives = use80;
|
|
}
|
|
|
|
#define TI99_DSK_TAG "ti99dsktag"
|
|
#define TI99DSK_BLOCKNOTFOUND -1
|
|
|
|
/*
|
|
Searches a block containing number * byte, starting at the given
|
|
position. Returns the position of the first byte of the block.
|
|
*/
|
|
static int find_block(const UINT8 *buffer, int start, int stop, UINT8 byte, size_t number)
|
|
{
|
|
int i = start;
|
|
size_t current = number;
|
|
while (i < stop && current > 0)
|
|
{
|
|
if (buffer[i++] != byte)
|
|
{
|
|
current = number;
|
|
}
|
|
else
|
|
{
|
|
current--;
|
|
}
|
|
}
|
|
if (current==0)
|
|
{
|
|
return i - number;
|
|
}
|
|
else
|
|
return TI99DSK_BLOCKNOTFOUND;
|
|
}
|
|
|
|
/*
|
|
Queried by flopdrv. Instead of the image tracks, we return the tracks
|
|
of the floppy drive. We assume we always have two heads.
|
|
*/
|
|
static int ti99_get_heads_per_disk(floppy_image *floppy)
|
|
{
|
|
// struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
// return tag->heads;
|
|
return 2;
|
|
}
|
|
|
|
/*
|
|
Queried by flopdrv. Instead of the image tracks, we return the tracks
|
|
of the floppy drive.
|
|
*/
|
|
static int ti99_get_tracks_per_disk(floppy_image *floppy)
|
|
{
|
|
int drivetracks = 42;
|
|
|
|
// This may fail on startup; therefore we use a special function in
|
|
// in the floppy controller implementations to explicitly set the geometry.
|
|
if (use_80_track_drives)
|
|
{
|
|
drivetracks = 83;
|
|
}
|
|
return drivetracks;
|
|
}
|
|
|
|
static floperr_t ti99_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length)
|
|
{
|
|
*sector_length=SECTOR_SIZE;
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Return the track size. We do not allow different track
|
|
sizes, so we store the track size in the tag.
|
|
*/
|
|
static UINT32 ti99_get_track_size(floppy_image *floppy, int head, int track)
|
|
{
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
return tag->track_size;
|
|
}
|
|
|
|
/*
|
|
static void create_vib(UINT8 *sector0, option_resolution *params)
|
|
{
|
|
const char *name = "UNNAMED ";
|
|
int sides, tracksperside,secspertrack,totalsectors,ausize,allocbytes,i;
|
|
|
|
enum { NAME=0, TOTALSECS=10, SECSPERTRACK=11, SIG=12, PROT=16, TRACKS=17, SIDES=18, DENSITY=19, EXT=20, ALLOC=56 };
|
|
|
|
sides = option_resolution_lookup_int(params, PARAM_HEADS);
|
|
tracksperside = option_resolution_lookup_int(params, PARAM_TRACKS);
|
|
secspertrack = option_resolution_lookup_int(params, PARAM_SECTORS);
|
|
|
|
totalsectors = sides * tracksperside * secspertrack;
|
|
|
|
memcpy(§or0[NAME], name, 10);
|
|
|
|
sector0[TOTALSECS] = (totalsectors>>8)&0xff;
|
|
sector0[TOTALSECS+1] = (totalsectors&0xff);
|
|
|
|
sector0[SECSPERTRACK] = secspertrack;
|
|
sector0[SIG] = 'D';
|
|
sector0[SIG+1] = 'S';
|
|
sector0[SIG+2] = 'K';
|
|
|
|
sector0[PROT] = ' ';
|
|
sector0[TRACKS] = tracksperside;
|
|
sector0[SIDES] = sides;
|
|
sector0[DENSITY] = (secspertrack<10)? 0x01 : 0x02;
|
|
|
|
for (i=EXT; i < ALLOC; i++)
|
|
sector0[i] = 0;
|
|
|
|
ausize = (totalsectors / 1441) + 1;
|
|
|
|
allocbytes = totalsectors / ausize / 8;
|
|
|
|
for (i=ALLOC; i < ALLOC+allocbytes; i++)
|
|
sector0[i] = 0;
|
|
|
|
while (i++ < SECTOR_SIZE)
|
|
sector0[i] = 0xff;
|
|
}
|
|
*/
|
|
/* =========================================================================
|
|
Sector dump format
|
|
=========================================================================*/
|
|
|
|
static floperr_t ti99_sdf_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen);
|
|
static floperr_t ti99_sdf_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
|
|
|
|
/* -----------------------------------------------------------------------
|
|
* Guess the geometry of the sector dump disk.
|
|
*
|
|
* Sector size is always 256 bytes.
|
|
*
|
|
* Common formats:
|
|
* 90 KiB = 40 tracks / 1 side / 9 sectors = SSSD
|
|
* 180 KiB = 40 tracks / 2 side / 9 sectors = DSSD (most common)
|
|
* = 40 tracks / 1 side / 18 sectors = SSDD (rare)
|
|
* = 80 tracks / 1 side / 9 sectors = SSSD80 (rare)
|
|
* 360 KiB = 40 tracks / 2 side / 18 sectors = DSDD (most common)
|
|
* = 80 tracks / 2 side / 9 sectors = DSSD80 (rare)
|
|
* = 80 tracks / 1 side / 18 sectors = SSDD80 (rare)
|
|
* = 40 tracks / 1 side / 36 sectors = SSHD (rare)
|
|
* 720 KiB = 80 tracks / 2 side / 18 sectors = DSDD80 (most common)
|
|
* 1440 KiB= 80 tracks / 2 side / 36 sectors = DSHD80 (most common)
|
|
*
|
|
* Moreover, there may be formats with 35 tracks (ancient) and 8 sectors/track.
|
|
* We only check for the 8 sector formats.
|
|
* 160 KiB = 40 tracks / 1 side / 16 sectors = SSDD8
|
|
* 320 KiB = 40 tracks / 2 side / 16 sectors = DSDD8
|
|
*
|
|
* The Volume Information Block may contain suitable information, but not
|
|
* before the disk is formatted and initialized.
|
|
*
|
|
* returns 100 if recognized, 0 if not recognized
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
static int ti99_sdf_guess_geometry(floppy_image *floppy, UINT64 size,
|
|
struct ti99dsk_geometry *geometry)
|
|
{
|
|
int totsecs;
|
|
typedef struct ti99_vib
|
|
{
|
|
char name[10]; // volume name (10 characters, pad with spaces)
|
|
UINT8 totsecsMSB; // disk length in sectors (big-endian) (usually 360, 720 or 1440)
|
|
UINT8 totsecsLSB;
|
|
UINT8 secspertrack; // sectors per track (usually 9 (FM) or 18 (MFM))
|
|
UINT8 id[3]; // String "DSK"
|
|
UINT8 protection; // 'P' if disk is protected, ' ' otherwise.
|
|
UINT8 tracksperside; // tracks per side (usually 40)
|
|
UINT8 sides; // sides (1 or 2)
|
|
UINT8 density; // 0,1 (FM) or 2,3,4 (MFM)
|
|
UINT8 res[36]; // Empty for traditional disks, or up to 3 directory pointers
|
|
UINT8 abm[200]; // allocation bitmap: a 1 for each sector in use (sector 0 is LSBit of byte 0,
|
|
// sector 7 is MSBit of byte 0, sector 8 is LSBit of byte 1, etc.)
|
|
} ti99_vib;
|
|
|
|
ti99_vib vib;
|
|
UINT32 file_size;
|
|
struct ti99dsk_geometry dummy_geometry;
|
|
|
|
if (geometry)
|
|
memset(geometry, 0, sizeof(*geometry));
|
|
else
|
|
geometry = &dummy_geometry;
|
|
|
|
if (size > 0xFFFFFFFF)
|
|
return 0;
|
|
|
|
file_size = (UINT32) size;
|
|
|
|
// Read the Volume Information Block
|
|
floppy_image_read(floppy, &vib, 0, sizeof(vib));
|
|
|
|
// If we have read the sector successfully, let us parse it
|
|
totsecs = (vib.totsecsMSB << 8) | vib.totsecsLSB;
|
|
geometry->secspertrack = vib.secspertrack;
|
|
if (geometry->secspertrack == 0)
|
|
// Some images might be like this, because the original SSSD
|
|
// TI controller always assumes 9.
|
|
geometry->secspertrack = 9;
|
|
geometry->tracksperside = vib.tracksperside;
|
|
if (geometry->tracksperside == 0)
|
|
// Some images are like this, because the original SSSD TI controller
|
|
// always assumes 40.
|
|
geometry->tracksperside = 40;
|
|
geometry->sides = vib.sides;
|
|
if (geometry->sides == 0)
|
|
// Some images are like this, because the original SSSD TI controller
|
|
// always assumes that tracks beyond 40 are on side 2. */
|
|
geometry->sides = totsecs / (geometry->secspertrack * geometry->tracksperside);
|
|
geometry->density = vib.density;
|
|
// check that the format makes sense
|
|
if (((geometry->secspertrack * geometry->tracksperside * geometry->sides) == totsecs)
|
|
&& (geometry->density <= 4) && (totsecs >= 2) && (! memcmp(vib.id, "DSK", 3))
|
|
&& (file_size == totsecs*256))
|
|
{
|
|
LOG_FORMATS("SDF/VIB consistent; tracks = %d, heads = %d, sectors = %d\n", geometry->tracksperside, geometry->sides, geometry->secspertrack);
|
|
return 100;
|
|
}
|
|
LOG_FORMATS("SDF/VIB not consistent; guessing format\n");
|
|
|
|
// So that was not consistent. We guess the size from the file size
|
|
// and assume that the VIB did not contain reliable data. For the
|
|
// ambiguous case we choose the most common format.
|
|
|
|
switch (file_size)
|
|
{
|
|
case 1*40*9*256:
|
|
// 90kbytes: SSSD
|
|
case 0:
|
|
/*default:*/
|
|
geometry->sides = 1;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 9;
|
|
geometry->density = 1;
|
|
break;
|
|
|
|
case 2*40*9*256:
|
|
// 180kbytes: either DSSD or 18-sector-per-track SSDD.
|
|
// We assume DSSD since DSSD is more common and is supported by
|
|
// the original TI SD disk controller.
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 9;
|
|
geometry->density = 1;
|
|
break;
|
|
|
|
case 1*40*16*256:
|
|
// 160kbytes: 16-sector-per-track SSDD (standard format for TI
|
|
// DD disk controller prototype, and the TI hexbus disk
|
|
// controller?) */
|
|
geometry->sides = 1;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 16;
|
|
geometry->density = 2;
|
|
break;
|
|
|
|
case 2*40*16*256:
|
|
// 320kbytes: 16-sector-per-track DSDD (standard format for TI
|
|
// DD disk controller prototype, and TI hexbus disk
|
|
// controller?)
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 16;
|
|
geometry->density = 2;
|
|
break;
|
|
|
|
case 2*40*18*256:
|
|
// 360kbytes: 18-sector-per-track DSDD (standard format for most
|
|
// third-party DD disk controllers, but reportedly not supported by
|
|
// the original TI DD disk controller prototype)
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 18;
|
|
geometry->density = 2;
|
|
break;
|
|
|
|
case 2*80*18*256:
|
|
// 720kbytes: 18-sector-per-track 80-track DSDD (Myarc only)
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 80;
|
|
geometry->secspertrack = 18;
|
|
geometry->density = 2;
|
|
break;
|
|
|
|
case 2*80*36*256:
|
|
// 1.44Mbytes: DSHD (Myarc only)
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 80;
|
|
geometry->secspertrack = 36;
|
|
geometry->density = 3;
|
|
break;
|
|
|
|
default:
|
|
LOG_FORMATS("Unrecognized disk image geometry\n");
|
|
return 0;
|
|
}
|
|
|
|
LOG_FORMATS("SDF geometry guess: tracks = %d, heads = %d, sectors = %d\n", geometry->tracksperside, geometry->sides, geometry->secspertrack);
|
|
return 100;
|
|
}
|
|
|
|
/*
|
|
Figure out the interleave for the sector arrangement in the track. For
|
|
SDF format this is required for the read_track function.
|
|
|
|
Interleave:
|
|
0 7 5 3 1 8 6 4 2;
|
|
start with 0, 3, 6, 0, 3, 6, ...
|
|
0 11 4 15 8 1 12 5 16 9 2 13 6 17 10 3 14 7
|
|
start with 0 for every track
|
|
|
|
Rare formats: 8 sectors
|
|
0 3 6 1 4 7 2 5 (i*3)%8
|
|
|
|
16 sectors
|
|
0 9 2 11 4 13 6 15 8 1 10 3 12 5 14 7 (i*9)%16
|
|
|
|
36 sectors
|
|
0 11 22 ... 3 14 25 (i*11)%36
|
|
*/
|
|
static void guess_interleave(int sectors, int *step, int *initial)
|
|
{
|
|
switch (sectors)
|
|
{
|
|
case 8:
|
|
*step = 3;
|
|
/* initial is the offset from track to track+1 of the starting sector number */
|
|
*initial = 0;
|
|
break;
|
|
case 9:
|
|
*step = 7;
|
|
*initial = 3;
|
|
break;
|
|
case 16:
|
|
*step = 9;
|
|
*initial = 0;
|
|
break;
|
|
case 18:
|
|
*step = 11;
|
|
*initial = 0;
|
|
break;
|
|
case 36:
|
|
*step = 11;
|
|
*initial = 0;
|
|
break;
|
|
default:
|
|
*step = 11;
|
|
*initial = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Delivers a whole track. As the image consists only of
|
|
sector data, we must reconstruct the track data and
|
|
then insert the sector data.
|
|
|
|
The track layout is exactly the Track Dump Format, except that the
|
|
CRC values must be correctly calculated.
|
|
|
|
Total length (FM) = 247 + sectors*334; (sectors=8 or 9)
|
|
Total length (MFM) = 752 + sectors*340; (sectors=16 or 18)
|
|
*/
|
|
static floperr_t ti99_sdf_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen)
|
|
{
|
|
int sector;
|
|
int startstep;
|
|
int step;
|
|
int position=0;
|
|
unsigned short crc;
|
|
int i;
|
|
floperr_t err;
|
|
int imgtrack = track;
|
|
|
|
UINT8 *trackdata = (UINT8*)buffer;
|
|
|
|
/* Determine from the image geometry whether we have FM or MFM */
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
// Do we have a 40 track disk in an 80 track drive? In that case we
|
|
// double the "width" of the tracks. */
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
guess_interleave(tag->sectors, &step, &startstep);
|
|
|
|
if (tag->sectors < 10) /* must be FM */
|
|
{
|
|
/* Write lead-in. */
|
|
memset(trackdata, 0x00, 16);
|
|
position += 16;
|
|
for (i=0; i < tag->sectors; i++)
|
|
{
|
|
/* Create interleaving */
|
|
sector = (i*step + imgtrack*startstep) % tag->sectors;
|
|
|
|
memset(&trackdata[position], 0x00, 6);
|
|
position += 6;
|
|
/* Set IDAM */
|
|
trackdata[position++] = 0xfe;
|
|
trackdata[position++] = imgtrack;
|
|
trackdata[position++] = head;
|
|
trackdata[position++] = sector;
|
|
trackdata[position++] = 0x01;
|
|
/* Calculate CRC16. Preset to 0xffff (what is 0xcdb4?) */
|
|
crc = ccitt_crc16(0xffff, &trackdata[position-(TI99_IDAM_LENGTH-2)], TI99_IDAM_LENGTH - 2);
|
|
trackdata[position++] = (crc>>8)&0xff;
|
|
trackdata[position++] = crc & 0xff;
|
|
|
|
/* Write Gap2 */
|
|
memset(&trackdata[position], 0xff, 11);
|
|
position += 11;
|
|
memset(&trackdata[position], 0x00, 6);
|
|
position += 6;
|
|
/* Write DAM */
|
|
trackdata[position++] = 0xfb;
|
|
|
|
/* Locate the sector content in the image and load it. */
|
|
err = ti99_sdf_read_sector(floppy, head, imgtrack, sector, &trackdata[position], SECTOR_SIZE);
|
|
if (err)
|
|
return err;
|
|
|
|
position += SECTOR_SIZE;
|
|
|
|
/* Set CRC16 */
|
|
crc = ccitt_crc16(0xffff, &trackdata[position-(SECTOR_SIZE+1)], SECTOR_SIZE+1);
|
|
trackdata[position++] = (crc>>8)&0xff;
|
|
trackdata[position++] = crc & 0xff;
|
|
|
|
/* Write Gap3 */
|
|
memset(&trackdata[position], 0xff, 45);
|
|
position += 45;
|
|
}
|
|
/* Write lead-out */
|
|
memset(&trackdata[position], 0xff, 231);
|
|
position += 231;
|
|
}
|
|
else /* we have MFM */
|
|
{
|
|
/* Write lead-in. */
|
|
memset(trackdata, 0x4e, 40);
|
|
position += 40;
|
|
for (i=0; i < tag->sectors; i++)
|
|
{
|
|
sector = (i*step + imgtrack*startstep) % tag->sectors;
|
|
|
|
memset(&trackdata[position], 0x00, 10);
|
|
position += 10;
|
|
/* Write sync */
|
|
memset(&trackdata[position], 0xa1, 3);
|
|
position += 3;
|
|
/* Set IDAM */
|
|
trackdata[position++] = 0xfe;
|
|
trackdata[position++] = imgtrack;
|
|
trackdata[position++] = head;
|
|
trackdata[position++] = sector;
|
|
trackdata[position++] = 0x01;
|
|
/* Set CRC16 */
|
|
crc = ccitt_crc16(0xffff, &trackdata[position-TI99_IDAM_LENGTH+2], TI99_IDAM_LENGTH - 2);
|
|
trackdata[position++] = (crc>>8)&0xff;
|
|
trackdata[position++] = crc & 0xff;
|
|
|
|
/* Write Gap2 */
|
|
memset(&trackdata[position], 0x4e, 22);
|
|
position += 22;
|
|
memset(&trackdata[position], 0x00, 12);
|
|
position += 12;
|
|
/* Write sync */
|
|
memset(&trackdata[position], 0xa1, 3);
|
|
position += 3;
|
|
/* Write DAM */
|
|
trackdata[position++] = 0xfb;
|
|
|
|
/* Locate the sector content in the image and load it. */
|
|
err = ti99_sdf_read_sector(floppy, head, track, sector, &trackdata[position], SECTOR_SIZE);
|
|
if (err)
|
|
return err;
|
|
|
|
position += SECTOR_SIZE;
|
|
|
|
/* Set CRC16 */
|
|
crc = ccitt_crc16(0xffff, &trackdata[position-SECTOR_SIZE-1], SECTOR_SIZE+1);
|
|
trackdata[position++] = (crc>>8)&0xff;
|
|
trackdata[position++] = crc & 0xff;
|
|
|
|
/* Write Gap3 */
|
|
memset(&trackdata[position], 0x4e, 24);
|
|
position += 24;
|
|
}
|
|
/* Write lead-out */
|
|
memset(&trackdata[position], 0x4e, 712);
|
|
position += 712;
|
|
}
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Writes a whole track. As the sector dump format does not include track
|
|
data, we have to extract the sector contents and to write them
|
|
in sequence into the image. All the rest (gaps, crc, sync bytes) are
|
|
dropped here.
|
|
Bytes written to locations beyond the image end will be droppped
|
|
silently (as if the medium is unwritable from that point).
|
|
*/
|
|
static floperr_t ti99_sdf_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen)
|
|
{
|
|
int current_pos = 0;
|
|
UINT8 *track_image;
|
|
int leadin, gap1, gap2;
|
|
int is_fm, found;
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
int imgtrack = track;
|
|
|
|
// Find out if we are going to write an FM or MFM track. Note that the
|
|
// original TI controller is known to have a bug in the formatting
|
|
// routine (using a MOVB instead of a MOV instruction when setting the
|
|
// pointer to the track data), outputting false bytes at the
|
|
// beginning of the track. So we do not rely on absolute positions
|
|
// (and neither does the controller).
|
|
track_image = (UINT8*)buffer;
|
|
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
/* Only search in the first 100 bytes for the start. */
|
|
leadin = 40;
|
|
gap1 = 10;
|
|
gap2 = 12;
|
|
is_fm = FALSE;
|
|
|
|
current_pos = find_block(track_image, 0, 100, 0x4e, leadin);
|
|
|
|
// In case of defect formats, we continue as far as possible. This
|
|
// may lead to sectors not being written. */
|
|
if (current_pos==TI99DSK_BLOCKNOTFOUND)
|
|
{
|
|
/* Not MFM, possibly FM image */
|
|
/* Again, find Lead-in */
|
|
leadin = 16;
|
|
current_pos = find_block(track_image, 0, 100, 0x00, leadin);
|
|
if (current_pos==TI99DSK_BLOCKNOTFOUND)
|
|
{
|
|
/* If neither, forget about this process completely. */
|
|
LOG_FORMATS("Cannot find lead-in for track %d, head %d.\n", track, head);
|
|
return FLOPPY_ERROR_INVALIDIMAGE;
|
|
}
|
|
gap1 = 6;
|
|
gap2 = 6;
|
|
is_fm = TRUE;
|
|
}
|
|
|
|
found = FALSE;
|
|
/* Get behind lead-in */
|
|
current_pos += leadin;
|
|
while (current_pos < buflen)
|
|
{
|
|
/* We must find the address block to determine the sector. */
|
|
int new_pos = find_block(track_image, current_pos, buflen, 0x00, gap1);
|
|
if (new_pos==TI99DSK_BLOCKNOTFOUND)
|
|
{
|
|
/* Forget about the rest. */
|
|
if (found) break; /* we were already successful, so all ok */
|
|
LOG_FORMATS("Cannot find gap1 for track %d, head %d.\n", track, head);
|
|
return FLOPPY_ERROR_INVALIDIMAGE;
|
|
}
|
|
found = TRUE;
|
|
|
|
if (!is_fm)
|
|
new_pos += 3; /* skip sync bytes in MFM */
|
|
|
|
if (track_image[new_pos + gap1]==0xfe)
|
|
{
|
|
current_pos = new_pos + gap1 + 1;
|
|
/* IDAM found. */
|
|
int wtrack = track_image[current_pos];
|
|
int whead = track_image[current_pos+1];
|
|
int sector = track_image[current_pos+2];
|
|
if (wtrack == imgtrack && whead == head)
|
|
{
|
|
/* We skip the first part of gap2. */
|
|
new_pos = find_block(track_image, current_pos, buflen, 0x00, gap2);
|
|
if (current_pos==TI99DSK_BLOCKNOTFOUND)
|
|
{
|
|
LOG_FORMATS("Cannot find gap2 for track %d, head %d.\n", imgtrack, head);
|
|
return FLOPPY_ERROR_INVALIDIMAGE;
|
|
}
|
|
else
|
|
{
|
|
new_pos += gap2;
|
|
if (!is_fm)
|
|
new_pos += 3; /* skip sync */
|
|
current_pos = new_pos;
|
|
if (track_image[current_pos]==0xfb)
|
|
{
|
|
/* DAM found. We now write the sector content to the image. */
|
|
ti99_sdf_write_sector(floppy, head, track, sector, &track_image[current_pos+1], SECTOR_SIZE, 0);
|
|
current_pos += SECTOR_SIZE;
|
|
}
|
|
else
|
|
LOG_FORMATS("DAM not found. Not writing write sector %d\n", sector);
|
|
/* else not found, ignore this sector. */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// else the sector head data do not match the
|
|
// current track and head. Ignore the sector.
|
|
LOG_FORMATS("Wrong track: wtrack=%d, imgtrack=%d, whead=%d, imghead=%d\n",wtrack,imgtrack, whead,head);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* One step forward. Retry. */
|
|
current_pos++;
|
|
}
|
|
}
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
This method creates a properly formatted image.
|
|
|
|
For a proper image, we need to format all tracks, and to create a
|
|
minimal filesystem:
|
|
|
|
name = "UNNAMED "
|
|
number of sectors
|
|
sectors/track
|
|
"DSK"
|
|
protection = 0x00
|
|
tracks/side
|
|
sides
|
|
density (0,1 = FM, 2 = MFM)
|
|
0x14 - 0x37 : 0x00
|
|
Allocation bitmap: 0xFF except for the first #sectors/AU size
|
|
|
|
AU size = 1 (360 - 1440 sectors)
|
|
= 2 (1441 - 2880 sectors)
|
|
= 4 (2881 - 5760 sectors)
|
|
= 8 (5760 - 11520 sectors)
|
|
|
|
sector 0 =
|
|
sector 1 = 256* 0x00
|
|
*/
|
|
/* static floperr_t ti99_sdf_format_track(floppy_image *floppy, int head, int track, option_resolution *params)
|
|
{
|
|
UINT8 *sector0 = (UINT8*)malloc(SECTOR_SIZE);
|
|
create_vib(sector0, params);
|
|
free(sector0);
|
|
if (true)
|
|
{
|
|
printf("not implemented\n");
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
Get the offset from the start of the image file. In this format, logical
|
|
tracks on two-sided disks are arranged as follows (example for 40
|
|
tracks; accordingly for 35 or 80)
|
|
|
|
head-track
|
|
0-0
|
|
0-1
|
|
0-2
|
|
0-3
|
|
...
|
|
0-39
|
|
1-39
|
|
1-38
|
|
1-37
|
|
...
|
|
1-0
|
|
Note that we take the imgtrack, which may be half of the drive track.
|
|
*/
|
|
static floperr_t ti99_sdf_get_offset(floppy_image *floppy, int head, int imgtrack, int sector, UINT64 *offset)
|
|
{
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
if ((head < 0) || (head >= tag->heads)
|
|
|| (imgtrack < 0) || (imgtrack >= tag->tracks)
|
|
|| (sector < 0) || (sector > tag->sectors))
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
|
|
if (head == 0) /* track numbers increasing towards inner track */
|
|
{
|
|
*offset = (imgtrack * tag->sectors + sector) * SECTOR_SIZE;
|
|
}
|
|
else /* track numbers increasing towards outer track */
|
|
{
|
|
*offset = (((2*tag->tracks)-1-imgtrack) * tag->sectors + sector) * SECTOR_SIZE;
|
|
}
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Read one sector at the specified position.
|
|
*/
|
|
static floperr_t ti99_sdf_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen)
|
|
{
|
|
floperr_t err;
|
|
UINT64 offset;
|
|
int imgtrack = track;
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
err = ti99_sdf_get_offset(floppy, head, imgtrack, sector, &offset);
|
|
if (err)
|
|
{
|
|
return err;
|
|
}
|
|
floppy_image_read(floppy, buffer, offset, SECTOR_SIZE);
|
|
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
For the SDF format, sectors are always numbered in ascending order,
|
|
starting with the lowest numbered sector in each track. In other words,
|
|
we have nothing to calculate here.
|
|
*/
|
|
static floperr_t ti99_sdf_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen)
|
|
{
|
|
return ti99_sdf_read_sector(floppy, head, track, sector, buffer, buflen);
|
|
}
|
|
|
|
/*
|
|
Write one sector at the specified position.
|
|
*/
|
|
static floperr_t ti99_sdf_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
|
|
{
|
|
UINT64 offset;
|
|
floperr_t err;
|
|
int imgtrack = track;
|
|
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
err = ti99_sdf_get_offset(floppy, head, imgtrack, sector, &offset);
|
|
if (err)
|
|
{
|
|
return err;
|
|
}
|
|
floppy_image_write(floppy, buffer, offset, SECTOR_SIZE);
|
|
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
See above; indexing and numbering coincide.
|
|
*/
|
|
static floperr_t ti99_sdf_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
|
|
{
|
|
return ti99_sdf_write_sector(floppy, head, track, sector, buffer, buflen, ddam);
|
|
}
|
|
|
|
/*
|
|
Required for ReadAddress command
|
|
*/
|
|
static floperr_t ti99_sdf_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags)
|
|
{
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
track = track / 2;
|
|
}
|
|
|
|
if (sector_length)
|
|
*sector_length=SECTOR_SIZE;
|
|
if (cylinder)
|
|
*cylinder = track;
|
|
if (side)
|
|
*side = head;
|
|
if (sector)
|
|
*sector = sector_index;
|
|
if (flags)
|
|
*flags = 0;
|
|
if (sector_index > tag->sectors)
|
|
{
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Try to identify the image as an SDF format image.
|
|
*/
|
|
static FLOPPY_IDENTIFY(ti99_sdf_identify)
|
|
{
|
|
UINT64 size;
|
|
size = floppy_image_size(floppy);
|
|
*vote = ti99_sdf_guess_geometry(floppy, size, NULL);
|
|
LOG_FORMATS("SDF voting %d\n", *vote);
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Create the SDF data structures and set the callbacks.
|
|
*/
|
|
static FLOPPY_CONSTRUCT(ti99_sdf_construct)
|
|
{
|
|
struct ti99dsk_geometry geometry;
|
|
struct FloppyCallbacks *callbacks;
|
|
struct ti99dsk_tag *tag;
|
|
LOG_FORMATS("Reading image as SDF\n");
|
|
|
|
if (params)
|
|
{
|
|
/* create */
|
|
memset(&geometry, 0, sizeof(geometry));
|
|
geometry.sides = option_resolution_lookup_int(params, PARAM_HEADS);
|
|
geometry.tracksperside = option_resolution_lookup_int(params, PARAM_TRACKS);
|
|
geometry.secspertrack = option_resolution_lookup_int(params, PARAM_SECTORS);
|
|
|
|
/* We don't have headers for geometry */
|
|
/* check for usage in imgtool - we want to be able to create useful disks */
|
|
}
|
|
else
|
|
{
|
|
/* load */
|
|
if (ti99_sdf_guess_geometry(floppy, floppy_image_size(floppy), &geometry)<50)
|
|
return FLOPPY_ERROR_INVALIDIMAGE;
|
|
}
|
|
|
|
assert(geometry.sides);
|
|
assert(geometry.tracksperside);
|
|
assert(geometry.secspertrack);
|
|
|
|
tag = (struct ti99dsk_tag *) floppy_create_tag(floppy, sizeof(struct ti99dsk_tag));
|
|
|
|
if (!tag)
|
|
return FLOPPY_ERROR_OUTOFMEMORY;
|
|
|
|
tag->heads = geometry.sides;
|
|
tag->tracks = geometry.tracksperside;
|
|
tag->sectors = geometry.secspertrack;
|
|
if (tag->sectors < 10)
|
|
/* FM mode */
|
|
tag->track_size = 16 + tag->sectors*334 + 231;
|
|
else
|
|
tag->track_size = 40 + tag->sectors*340 + 712;
|
|
|
|
/* set up format callbacks */
|
|
callbacks = floppy_callbacks(floppy);
|
|
callbacks->read_sector = ti99_sdf_read_sector;
|
|
callbacks->write_sector = ti99_sdf_write_sector;
|
|
callbacks->read_indexed_sector = ti99_sdf_read_indexed_sector;
|
|
callbacks->write_indexed_sector = ti99_sdf_write_indexed_sector;
|
|
callbacks->read_track = ti99_sdf_read_track;
|
|
callbacks->write_track = ti99_sdf_write_track;
|
|
/* callbacks->format_track = ti99_sdf_format_track; */
|
|
callbacks->get_track_size = ti99_get_track_size;
|
|
/* post format unset */
|
|
callbacks->get_heads_per_disk = ti99_get_heads_per_disk;
|
|
callbacks->get_tracks_per_disk = ti99_get_tracks_per_disk;
|
|
callbacks->get_sector_length = ti99_get_sector_length;
|
|
callbacks->get_indexed_sector_info = ti99_sdf_get_indexed_sector_info;
|
|
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------
|
|
* Track Dump Format, aka PC99 format
|
|
* This format includes all track data, including address marks and gaps.
|
|
* However, CRC is not stored; instead, a f7f7 is stored at that location.
|
|
* Two track images exist, one for FM and one for MFM format.
|
|
*
|
|
* In this implementation, however, we do calculate the CRC.
|
|
*
|
|
* All tracks have exactly the same size, so offsets are calculated easily.
|
|
*
|
|
* This format is a precise map of the data contents of the track;
|
|
* however, it does not store clock patterns.
|
|
*
|
|
* The TDF format does not have a special index part, so any sector on it
|
|
* must be searched (as done by the disk controller)
|
|
*
|
|
* Interesting detail: The original TI disk controller has a bug which has
|
|
* just recently been discovered (using MESS!): The lead-in has another 9
|
|
* bytes as a prefix; this is due to a wrongly set pointer in the format
|
|
* routine. Strictly speaking, it produces invalid track images.
|
|
* The original hardware does not care, however, so neither should we.
|
|
* To keep the emulation realistic (and to allow for a
|
|
* possible future extension with clock bits), we search for the
|
|
* beginning of the track. From there on, however, we accept no further
|
|
* deviations. This may make the resulting format unusable with the PC99
|
|
* emulator, but it only appears when formatting with the TI disk
|
|
* controller anyway. If the image is created with another disk controller or
|
|
* using imgtool, the format is correctly created.
|
|
*
|
|
* FM track image
|
|
*
|
|
* Lead-in: 16* 00
|
|
* Pre-ID gap: 6* 00 ---+
|
|
* IDAM: fe |
|
|
* Track: tt |
|
|
* Head: hh |
|
|
* Sector: ss |
|
|
* Size: 01 |
|
|
* CRC: 2* f7 +--- repeat 9 times (8 in rare cases)
|
|
* Gap2: 11* ff |
|
|
* 6* 00 |
|
|
* DAM: fb |
|
|
* Content: 256* xx |
|
|
* CRC: 2* f7 |
|
|
* Gap3: 45* ff ---+
|
|
* Lead-out: 231* ff
|
|
*
|
|
* --------------
|
|
*
|
|
* MFM track image
|
|
*
|
|
* Lead-in: 40* 4e
|
|
* Pre-ID gap: 10* 00 ---+
|
|
* Sync: 3* a1 |
|
|
* IDAM: fe |
|
|
* Track: tt |
|
|
* Head: hh |
|
|
* Sector: ss |
|
|
* Size: 01 |
|
|
* CRC: 2* f7 +--- repeat 18 times (16 in rare cases)
|
|
* Gap2: 22* 4e |
|
|
* 12* 00 |
|
|
* Sync: 3* a1 |
|
|
* DAM: fb |
|
|
* Content: 256* xx |
|
|
* CRC: 2* f7 |
|
|
* Gap3: 24* 4e ---+
|
|
* Lead-out: 712* 4e
|
|
*
|
|
* ------------------
|
|
* The tracks are located on the image as follows (different to SDF!):
|
|
*
|
|
* head-track
|
|
* 0-0
|
|
* 0-1
|
|
* 0-2
|
|
* 0-3
|
|
* ...
|
|
* 0-39
|
|
* 1-0
|
|
* 1-1
|
|
* 1-2
|
|
* ...
|
|
* 1-39
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
#define TI99_FM 1
|
|
#define TI99_MFM 2
|
|
|
|
/* For the emulation of the clock pattern, we add a 9th bit. */
|
|
#define TI99_DAM 0x1fb
|
|
#define TI99_IDAM 0x1fe
|
|
|
|
#define TI99_FM_DAM_OFFSET 24
|
|
#define TI99_MFM_DAM_OFFSET 44
|
|
|
|
#define TI99_FM_TRACK_ADD 247
|
|
#define TI99_MFM_TRACK_ADD 752
|
|
|
|
#define TI99_FM_TOTAL_SECTOR 334
|
|
#define TI99_MFM_TOTAL_SECTOR 340
|
|
|
|
/*
|
|
Determine offset of the first IDAM. This is the base of the simulation
|
|
of the clock bytes. We use a heuristic to guess where the track begins
|
|
and simply check whether we can locate the first IDAM and DAM. Return
|
|
value is the offset to the first IDAM.
|
|
*/
|
|
static floperr_t determine_offset(int format, UINT8 *track, int *offset)
|
|
{
|
|
floperr_t retval;
|
|
int current_pos;
|
|
|
|
if (format==TI99_FM)
|
|
{
|
|
// printf("Trying FM\n");
|
|
// We are seeking the lead-in and pre-id gap from
|
|
// position 0 to position 50. This is just to make sure that
|
|
// if there are bad bytes at the beginning (original TI controller)
|
|
// we will skip them.
|
|
current_pos = find_block(track, 0, 50, 0x00, 22);
|
|
if (current_pos==TI99DSK_BLOCKNOTFOUND)
|
|
{
|
|
LOG_FORMATS("Lead-in not found\n");
|
|
retval = FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
else
|
|
{
|
|
current_pos += 22;
|
|
if (track[current_pos]==0xfe /* IDAM */ &&
|
|
track[current_pos+4]==0x01 /* sector length, always 256 for TI */ &&
|
|
track[current_pos+24]==0xfb) /* DAM */
|
|
{
|
|
/* We're pretty sure this is the beginning. */
|
|
*offset = current_pos;
|
|
retval = FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
LOG_FORMATS("IDAM or DAM not found\n");
|
|
retval = FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* MFM */
|
|
// printf("Trying MFM\n");
|
|
int track_start = find_block(track, 0, 100, 0x4e, 40);
|
|
if (track_start==TI99DSK_BLOCKNOTFOUND)
|
|
{
|
|
LOG_FORMATS("Lead-in not found\n");
|
|
retval = FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
else
|
|
{
|
|
track_start += 40;
|
|
current_pos = find_block(track, track_start, track_start+10, 0x00, 10);
|
|
if (current_pos==TI99DSK_BLOCKNOTFOUND)
|
|
{
|
|
LOG_FORMATS("Pre-gap not found\n");
|
|
retval = FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
else
|
|
{
|
|
current_pos += 10;
|
|
if (track[current_pos] == 0xa1 /* First sync */ &&
|
|
track[current_pos+3] == 0xfe /* IDAM */ &&
|
|
track[current_pos+44] == 0xa1 /* First sync */ &&
|
|
track[current_pos+47] == 0xfb) /* DAM */
|
|
{
|
|
/* We're pretty sure this is the beginning. */
|
|
*offset = current_pos + 3;
|
|
retval = FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
LOG_FORMATS("Sync/IDAM/DAM not found\n");
|
|
retval = FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* printf("TDF offset = 0x%x\n", *offset); */
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
Gets a byte from the track. In this format we do not have a clock
|
|
signal, so we will simulate it. Thus, this function returns a 9-bit
|
|
value, with the first bit set as a marker for an address mark.
|
|
|
|
This may be a little more complicated than necessary, but it correctly
|
|
emulates the behavior of the controller (and it allows for an
|
|
alternative format which actually supports clock and data).
|
|
*/
|
|
static int read_byte(int format, int first_idam, UINT8 *track, int position)
|
|
{
|
|
int totalseclen;
|
|
int dam_offset;
|
|
int byte;
|
|
|
|
/* Check whether we are at any IDAM or DAM in this track. */
|
|
if (format==TI99_FM)
|
|
{
|
|
totalseclen = TI99_FM_TOTAL_SECTOR;
|
|
dam_offset = TI99_FM_DAM_OFFSET;
|
|
}
|
|
else
|
|
{
|
|
totalseclen = TI99_MFM_TOTAL_SECTOR;
|
|
dam_offset = TI99_MFM_DAM_OFFSET;
|
|
}
|
|
|
|
int curpos = (position - first_idam) % totalseclen;
|
|
if (curpos==0 && track[position]==0xfe)
|
|
byte = TI99_IDAM;
|
|
else
|
|
{
|
|
if (curpos==dam_offset && track[position]==0xfb)
|
|
byte = TI99_DAM;
|
|
else
|
|
byte = track[position] & 0xff;
|
|
}
|
|
return byte;
|
|
}
|
|
|
|
/*
|
|
Determine the position of this track in the image.
|
|
*/
|
|
static floperr_t ti99_tdf_get_offset(floppy_image *floppy, int head, int imgtrack, UINT64 *offset)
|
|
{
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
if ((head < 0) || (head >= tag->heads) || (imgtrack < 0) || (imgtrack >= tag->tracks))
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
|
|
// For head 0, tracks increase from 0 to tracks-1; then for head 1,
|
|
// tracks also increase from 0 to tracks-1. */
|
|
*offset = head * (tag->tracks * tag->track_size) + imgtrack * tag->track_size;
|
|
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Reads a track. We just copy the contents from the format and recreate
|
|
the CRC if it is the "blank" CRC as F7F7.
|
|
This method assumes that the track division be done already.
|
|
*/
|
|
static floperr_t ti99_tdf_read_track_internal(floppy_image *floppy, int head, int imgtrack, UINT64 offset, void *buffer, size_t buflen)
|
|
{
|
|
floperr_t err;
|
|
UINT64 track_offset;
|
|
int first_idam = 0;
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
int i, byte, crc;
|
|
|
|
UINT8 *track_data = (UINT8*)buffer;
|
|
|
|
err = ti99_tdf_get_offset(floppy, head, imgtrack, &track_offset);
|
|
if (err)
|
|
return err;
|
|
|
|
floppy_image_read(floppy, buffer, track_offset, buflen);
|
|
|
|
// Rebuild the CRCs. PC99 did not store the CRC but put
|
|
// F7F7 in its place.
|
|
// The first CRC is at position IDAM + 5
|
|
// The second CRC is at position IDAM + 0x119 (FM) or +0x12d (MFM)
|
|
// All this is repeated for each sector in the track
|
|
|
|
if (determine_offset(tag->format, track_data, &first_idam)==FLOPPY_ERROR_SEEKERROR)
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
|
|
i = 0;
|
|
while (i < buflen)
|
|
{
|
|
byte = read_byte(tag->format, first_idam, track_data, i);
|
|
if (byte == TI99_IDAM)
|
|
{
|
|
// Do we have a valid CRCs already? Then this image
|
|
// already contains proper CRCs handling. Do not recreate them.
|
|
if (track_data[i+5] != 0xf7 || track_data[i+6] != 0xf7)
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
|
|
crc = ccitt_crc16(0xffff, &track_data[i], 5);
|
|
track_data[i+5] = (crc>>8) & 0xff;
|
|
track_data[i+6] = (crc & 0xff);
|
|
}
|
|
else
|
|
{
|
|
if (byte == TI99_DAM)
|
|
{
|
|
crc = ccitt_crc16(0xffff, &track_data[i], SECTOR_SIZE+1);
|
|
track_data[i+SECTOR_SIZE+1] = (crc>>8) & 0xff;
|
|
track_data[i+SECTOR_SIZE+2] = (crc & 0xff);
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
// dump_contents(track_data, buflen);
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Reads a track. We just copy the contents from the format, without
|
|
changes.
|
|
*/
|
|
static floperr_t ti99_tdf_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen)
|
|
{
|
|
int imgtrack = track;
|
|
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
return ti99_tdf_read_track_internal(floppy, head, imgtrack, offset, buffer, buflen);
|
|
}
|
|
|
|
/*
|
|
Writes a track.
|
|
*/
|
|
static floperr_t ti99_tdf_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen)
|
|
{
|
|
floperr_t err;
|
|
UINT64 track_offset;
|
|
int imgtrack = track;
|
|
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
err = ti99_tdf_get_offset(floppy, head, imgtrack, &track_offset);
|
|
if (err)
|
|
return err;
|
|
|
|
// According to the TDF format, we don't need the CRC but replace it
|
|
// with F7F7. For now we keep it and see whether PC99 can cope with
|
|
// that. (Otherwise we would have to copy the const buffer). */
|
|
floppy_image_write(floppy, buffer, offset + track_offset, buflen);
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
static floperr_t ti99_tdf_format_track(floppy_image *floppy, int head, int track, option_resolution *params)
|
|
{
|
|
int sectors;
|
|
int sector_length;
|
|
int interleave;
|
|
int first_sector_id;
|
|
sectors = option_resolution_lookup_int(params, PARAM_SECTORS);
|
|
sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH);
|
|
interleave = option_resolution_lookup_int(params, PARAM_INTERLEAVE);
|
|
first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID);
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
Get a sector from a track. This function takes a complete track,
|
|
searches for the sector, and passes a pointer to the sector contents
|
|
back.
|
|
As the original TI controller creates bad bytes at the start of a track
|
|
we do *not* assume a simple layout. The strategy is to search the
|
|
lead-in of the track, and from this offset, step through the track
|
|
in search for the sector. Since we do not have index marks (we cannot
|
|
distinguish them from ordinary data, as we do not store the clock), we
|
|
do not attempt to detect IDAM or DAM.
|
|
*/
|
|
static floperr_t ti99_tdf_seek_sector_in_track(floppy_image *floppy, int head, int track, int sector, UINT8 *track_data, UINT8 **sector_data)
|
|
{
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
int byte;
|
|
int state;
|
|
enum { SEARCHIDAM, SEARCHDAM };
|
|
|
|
state = SEARCHIDAM;
|
|
if (tag->first_idam==0) /* should we check for every track? */
|
|
{
|
|
if (determine_offset(tag->format, track_data, &tag->first_idam)==FLOPPY_ERROR_SEEKERROR)
|
|
{
|
|
LOG_FORMATS("could not determine offset\n");
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
}
|
|
|
|
for (int i=0; i < tag->track_size; i++)
|
|
{
|
|
byte = read_byte(tag->format, tag->first_idam, track_data, i);
|
|
|
|
switch (state)
|
|
{
|
|
case SEARCHIDAM:
|
|
/* search for the IDAM */
|
|
if (byte==TI99_IDAM)
|
|
{
|
|
if ((track_data[i+1]==track) && (track_data[i+2]==head) && (track_data[i+3]==sector))
|
|
state = SEARCHDAM;
|
|
else
|
|
state = SEARCHIDAM;
|
|
}
|
|
break;
|
|
case SEARCHDAM:
|
|
if (byte==TI99_DAM)
|
|
{
|
|
*sector_data = &track_data[i+1];
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
LOG_FORMATS("Sector not found.\n");
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
|
|
static floperr_t ti99_tdf_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen)
|
|
{
|
|
floperr_t err;
|
|
UINT8 *sector_data;
|
|
UINT8 *track_data;
|
|
int imgtrack = track;
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
track_data = (UINT8*)malloc(tag->track_size);
|
|
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
err = ti99_tdf_read_track_internal(floppy, head, imgtrack, 0, track_data, tag->track_size);
|
|
if (err)
|
|
return err;
|
|
|
|
err = ti99_tdf_seek_sector_in_track(floppy, head, imgtrack, sector, track_data, §or_data);
|
|
if (err)
|
|
return err;
|
|
/* verify CRC? */
|
|
|
|
memcpy(buffer, sector_data, SECTOR_SIZE);
|
|
free(track_data);
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
Writes a sector into the track. Note that the indexed_write method
|
|
may be used instead of this method, according to the implementation
|
|
of flopimg/flopdrv.
|
|
*/
|
|
static floperr_t ti99_tdf_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
|
|
{
|
|
floperr_t err;
|
|
UINT8 *sector_data;
|
|
UINT8 *track_data;
|
|
UINT64 track_offset;
|
|
int imgtrack = track;
|
|
UINT64 offset;
|
|
UINT8 crc_field[2];
|
|
int crc;
|
|
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
|
|
track_data = (UINT8*)malloc(tag->track_size);
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
err = ti99_tdf_read_track_internal(floppy, head, imgtrack, 0, track_data, tag->track_size);
|
|
if (err)
|
|
return err;
|
|
err = ti99_tdf_seek_sector_in_track(floppy, head, imgtrack, sector, track_data, §or_data);
|
|
if (err)
|
|
return err;
|
|
|
|
err = ti99_tdf_get_offset(floppy, head, imgtrack, &track_offset);
|
|
if (err)
|
|
return err;
|
|
|
|
offset = track_offset + (sector_data - track_data);
|
|
|
|
floppy_image_write(floppy, buffer, offset, SECTOR_SIZE);
|
|
|
|
// Recalculate CRC. We init the CRC with bf84 (DAM fb is
|
|
// already included; would be ffff without)
|
|
crc = ccitt_crc16(0xbf84, (UINT8*)buffer, SECTOR_SIZE);
|
|
crc_field[0] = (crc>>8) & 0xff;
|
|
crc_field[1] = (crc & 0xff);
|
|
floppy_image_write(floppy, crc_field, offset+SECTOR_SIZE, 2);
|
|
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
static floperr_t ti99_tdf_write_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, const void *buffer, size_t buflen, int ddam)
|
|
{
|
|
/* Read track, head */
|
|
int byte;
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
UINT8 *track_data;
|
|
UINT8 *sector_data;
|
|
UINT64 track_offset;
|
|
UINT64 offset;
|
|
UINT8 crc_field[2];
|
|
|
|
int crc;
|
|
int imgtrack = track;
|
|
|
|
int i;
|
|
floperr_t err;
|
|
|
|
track_data = (UINT8*)malloc(tag->track_size);
|
|
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
err = ti99_tdf_read_track_internal(floppy, head, imgtrack, 0, track_data, tag->track_size);
|
|
if (err)
|
|
{
|
|
free(track_data);
|
|
return err;
|
|
}
|
|
|
|
/* Search for the sector_index-th sector. */
|
|
if (tag->first_idam==0) /* should we check for every track? */
|
|
{
|
|
if (determine_offset(tag->format, track_data, &tag->first_idam)==FLOPPY_ERROR_SEEKERROR)
|
|
{
|
|
free(track_data);
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
}
|
|
|
|
/* Search for the IDAM of the n-th sector */
|
|
i=0;
|
|
while (i < tag->track_size && sector_index>=0)
|
|
{
|
|
byte = read_byte(tag->format, tag->first_idam, track_data, i);
|
|
if (byte == TI99_IDAM)
|
|
{
|
|
sector_index--;
|
|
}
|
|
i++;
|
|
}
|
|
i--;
|
|
|
|
/* Not that many sectors in this track? */
|
|
if (sector_index>0)
|
|
{
|
|
free(track_data);
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
|
|
/* Find the DAM. */
|
|
while (i < tag->track_size)
|
|
{
|
|
byte = read_byte(tag->format, tag->first_idam, track_data, i);
|
|
if (byte == TI99_DAM)
|
|
break;
|
|
else
|
|
i++;
|
|
}
|
|
|
|
/* There was no DAM. Image seems to be broken. */
|
|
if (i == tag->track_size)
|
|
{
|
|
free(track_data);
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
|
|
/* Sector data is right after the DAM. */
|
|
sector_data = &track_data[i+1];
|
|
|
|
/* Get the position of the track in the image. */
|
|
err = ti99_tdf_get_offset(floppy, head, imgtrack, &track_offset);
|
|
if (err)
|
|
return err;
|
|
|
|
offset = track_offset + (sector_data - track_data);
|
|
|
|
/* Write the sector data at that position. */
|
|
floppy_image_write(floppy, buffer, offset, SECTOR_SIZE);
|
|
|
|
// Recalculate CRC. We init the CRC with bf84 (DAM fb is
|
|
// already included; would be ffff without)
|
|
crc = ccitt_crc16(0xbf84, (UINT8*)buffer, SECTOR_SIZE);
|
|
crc_field[0] = (crc>>8) & 0xff;
|
|
crc_field[1] = (crc & 0xff);
|
|
floppy_image_write(floppy, crc_field, offset+SECTOR_SIZE, 2);
|
|
|
|
/* Free the temporary buffer */
|
|
free(track_data);
|
|
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
static floperr_t ti99_tdf_find_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, void *buffer, unsigned long *flags)
|
|
{
|
|
/* Read track, head */
|
|
int byte = 0;
|
|
struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy);
|
|
UINT8 *track_data;
|
|
int imgtrack = track;
|
|
int i;
|
|
floperr_t err;
|
|
floperr_t retval;
|
|
track_data = (UINT8*)malloc(tag->track_size);
|
|
|
|
if (use_80_track_drives && tag->tracks<=40)
|
|
{
|
|
imgtrack = track / 2;
|
|
}
|
|
|
|
err = ti99_tdf_read_track_internal(floppy, head, imgtrack, 0, track_data, tag->track_size);
|
|
if (err)
|
|
{
|
|
free(track_data);
|
|
return err;
|
|
}
|
|
|
|
/* Search for the sector_index-th sector. */
|
|
|
|
if (tag->first_idam==0) /* should we check for every track? */
|
|
{
|
|
if (determine_offset(tag->format, track_data, &tag->first_idam)==FLOPPY_ERROR_SEEKERROR)
|
|
{
|
|
free(track_data);
|
|
return FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
}
|
|
|
|
i=0;
|
|
sector_index = sector_index+1;
|
|
while (i++ < tag->track_size && sector_index>0)
|
|
{
|
|
byte = read_byte(tag->format, tag->first_idam, track_data, i);
|
|
if (byte == TI99_IDAM)
|
|
{
|
|
sector_index--;
|
|
}
|
|
}
|
|
|
|
retval = FLOPPY_ERROR_SEEKERROR;
|
|
if (sector_index==0)
|
|
{
|
|
/* If desired, return the ID field */
|
|
if (cylinder)
|
|
{
|
|
*cylinder = track_data[i];
|
|
// printf("cylinder=%d", *cylinder);
|
|
|
|
}
|
|
if (side)
|
|
{
|
|
*side = track_data[i+1];
|
|
// printf(", side=%d", *side);
|
|
}
|
|
if (sector)
|
|
{
|
|
*sector = track_data[i+2];
|
|
// printf(", sector=%d", *sector);
|
|
}
|
|
if (sector_length)
|
|
{
|
|
*sector_length = 128 << track_data[i+3];
|
|
// printf(", sector_length=%d\n", *sector_length);
|
|
}
|
|
if (flags)
|
|
{
|
|
*flags = 0;
|
|
}
|
|
retval = FLOPPY_ERROR_SUCCESS;
|
|
|
|
/* If desired, return the sector contents. */
|
|
if (buffer)
|
|
{
|
|
while (i++ < tag->track_size)
|
|
{
|
|
byte = read_byte(tag->format, tag->first_idam, track_data, i);
|
|
if (byte == TI99_DAM)
|
|
break;
|
|
}
|
|
if (byte == TI99_DAM)
|
|
{
|
|
memcpy(buffer, &track_data[i+1], SECTOR_SIZE);
|
|
}
|
|
else
|
|
{
|
|
retval = FLOPPY_ERROR_SEEKERROR;
|
|
}
|
|
}
|
|
}
|
|
free(track_data);
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
Required for ReadAddress command. This function gets the 1st, 2nd, 3rd,
|
|
sector (and so on) from the track, not sector 1, 2, 3. That is, the
|
|
returned sector info depends on the interleave and the start sector.
|
|
*/
|
|
static floperr_t ti99_tdf_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags)
|
|
{
|
|
return ti99_tdf_find_indexed_sector(floppy, head, track, sector_index, cylinder, side, sector, sector_length, NULL, flags);
|
|
}
|
|
|
|
static floperr_t ti99_tdf_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen)
|
|
{
|
|
return ti99_tdf_find_indexed_sector(floppy, head, track, sector, (int*)NULL, (int*)NULL, (int*)NULL, (UINT32*)NULL, buffer, (unsigned long*)NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
Guess the geometry of the disk.
|
|
1. Check whether the disk is formatted, and if so, whether it is
|
|
a FM or a MFM disk
|
|
2. If this fails, guess from the file size which format it should have
|
|
|
|
SDF formats have sizes which are multiples of 10KiB.
|
|
TDF: full sector size FM (MFM) = 334 (340) bytes
|
|
additional track data FM (MFM) = 247 (752) bytes
|
|
|
|
=> check whether size is dividable by 40 (or 35) and by 2
|
|
=> get track size
|
|
=> subtract track data
|
|
=> divide by 334 or 340
|
|
=> number of sectors should be 8, 9, 16, 18, 32, or 36
|
|
|
|
SSDD is 274880, but DSSD is 260240, so they differ (unlike in the SDF format)
|
|
Still, we cannot tell apart single-sided 80 track formats from double-sided
|
|
40 tracks. We assume the latter. Moreover, we only attempt to detect
|
|
9/18/36 sectors (no 8/16) and 40 tracks (no 35).
|
|
|
|
SSSD40 = 130120
|
|
DSSD = 260240
|
|
SSDD = 274880
|
|
DSDD = 549760
|
|
DSHD = 1039360
|
|
DSDD80 = 1099520
|
|
DSHD80 = 2078720
|
|
*/
|
|
static int ti99_tdf_guess_geometry(floppy_image *floppy, UINT64 size,
|
|
struct ti99dsk_geometry *geometry)
|
|
{
|
|
int idamcnt, state, track, head, i, totalseclen = 0, format = 0, byte, tracklength, trackadd = 0;
|
|
int first_idam = 0;
|
|
UINT8 *track_data;
|
|
/*int offset;*/
|
|
|
|
struct ti99dsk_geometry dummy_geometry;
|
|
|
|
if (geometry)
|
|
memset(geometry, 0, sizeof(*geometry));
|
|
else
|
|
geometry = &dummy_geometry;
|
|
|
|
/* Allocate enough bytes to hold the longest supported track. */
|
|
track_data = (UINT8*)malloc(13000);
|
|
floppy_image_read(floppy, track_data, 0, 13000);
|
|
|
|
if (determine_offset(TI99_MFM, track_data, &first_idam)==FLOPPY_ERROR_SEEKERROR)
|
|
{
|
|
/* MFM failed. */
|
|
if (determine_offset(TI99_FM, track_data, &first_idam)==FLOPPY_ERROR_SEEKERROR)
|
|
{
|
|
LOG_FORMATS("IDAM not found. Unformatted disk.\n");
|
|
|
|
// FM failed as well. Disk is not formatted. We assume
|
|
// a format that fits into the space provided by the file.
|
|
// We only give a moderate vote in this case, so SDF
|
|
// make take it.
|
|
|
|
free(track_data);
|
|
if (size < 130120 || size > 2078720)
|
|
{
|
|
LOG_FORMATS("Unknown format size: %d\n", (int)size);
|
|
return 0;
|
|
}
|
|
if (size >= 130120 && size < 260240)
|
|
{
|
|
/* SSSD */
|
|
geometry->sides = 1;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 9;
|
|
geometry->density = 1;
|
|
return 50;
|
|
}
|
|
if (size >= 260240 && size < 274880)
|
|
{
|
|
/* DSSD */
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 9;
|
|
geometry->density = 1;
|
|
return 50;
|
|
}
|
|
if (size >= 274880 && size < 549760)
|
|
{
|
|
/* SSDD */
|
|
geometry->sides = 1;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 18;
|
|
geometry->density = 2;
|
|
return 50;
|
|
}
|
|
if (size >= 549760 && size < 1039360)
|
|
{
|
|
/* DSDD */
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 18;
|
|
geometry->density = 2;
|
|
return 50;
|
|
}
|
|
if (size >= 1039360 && size < 1099520)
|
|
{
|
|
/* DSHD */
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 40;
|
|
geometry->secspertrack = 36;
|
|
geometry->density = 2;
|
|
return 50;
|
|
}
|
|
if (size >= 1099520 && size < 2078720)
|
|
{
|
|
/* DSDD80 */
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 80;
|
|
geometry->secspertrack = 18;
|
|
geometry->density = 2;
|
|
return 50;
|
|
}
|
|
if (size == 2078720)
|
|
{
|
|
/* DSHD80 */
|
|
geometry->sides = 2;
|
|
geometry->tracksperside = 80;
|
|
geometry->secspertrack = 36;
|
|
geometry->density = 2;
|
|
return 50;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* FM format */
|
|
totalseclen = TI99_FM_TOTAL_SECTOR;
|
|
trackadd = TI99_FM_TRACK_ADD; /* additional track data (gaps etc.) */
|
|
geometry->density = 1;
|
|
format = TI99_FM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* MFM format */
|
|
totalseclen = TI99_MFM_TOTAL_SECTOR;
|
|
trackadd = TI99_MFM_TRACK_ADD;
|
|
geometry->density = 2;
|
|
format = TI99_MFM;
|
|
}
|
|
|
|
/* Count the number of IDAMs on track 0, head 0 */
|
|
idamcnt = 0;
|
|
state = 0;
|
|
track = 0;
|
|
head = 0;
|
|
i=0;
|
|
|
|
while (i++ < 13000 && head == 0 && track == 0)
|
|
{
|
|
byte = read_byte(format, first_idam, track_data, i);
|
|
switch (state)
|
|
{
|
|
case 0:
|
|
if (byte==TI99_IDAM)
|
|
state = 1;
|
|
break;
|
|
case 1:
|
|
track = byte;
|
|
state = 2;
|
|
break;
|
|
case 2:
|
|
head = byte;
|
|
state = 3;
|
|
break;
|
|
case 3:
|
|
idamcnt++;
|
|
state = 0;
|
|
}
|
|
}
|
|
LOG_FORMATS("Determined %d sectors\n", idamcnt);
|
|
|
|
// Now calculate the geometry
|
|
// The track size, in theory, may change due to reformatting,
|
|
// and it may also vary between tracks, but the TDF does not support
|
|
// varying track lengths. So every track has the same length as track 0.
|
|
// Also, we don't yet know whether the drive and the disk have the
|
|
// same track count. For 80 track drives and 40 track disks, we need
|
|
// double-stepping; this will be known as soon as the controller is
|
|
// initialized; we need to check that in the access methods.
|
|
|
|
tracklength = idamcnt * totalseclen + trackadd;
|
|
/*offset = 0;*/
|
|
|
|
// Read the last track. We assume that all tracks are properly
|
|
// formatted; otherwise, the above calculations would already fail.
|
|
// That is, the last track in the image tells us whether it is for
|
|
// head 0 or for head 1.
|
|
|
|
geometry->sides = 1;
|
|
floppy_image_read(floppy, track_data, size-tracklength, tracklength);
|
|
if (determine_offset(format, track_data, &first_idam)==FLOPPY_ERROR_SEEKERROR)
|
|
{
|
|
/* error ... what now? */
|
|
LOG_FORMATS("Error when reading last track. Image broken.\n");
|
|
free(track_data);
|
|
return 50;
|
|
}
|
|
else
|
|
{
|
|
if (track_data[first_idam+2]==0)
|
|
{
|
|
geometry->sides = 1;
|
|
}
|
|
else
|
|
{
|
|
if (track_data[first_idam+2]==1)
|
|
{
|
|
geometry->sides = 2;
|
|
}
|
|
else
|
|
{
|
|
LOG_FORMATS("Error: Last track has invalid first IDAM: head = %d.\n", track_data[first_idam+2]);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
geometry->tracksperside = (size / tracklength) / geometry->sides;
|
|
geometry->secspertrack = idamcnt;
|
|
|
|
if (geometry->tracksperside < 35 || geometry->tracksperside > 80)
|
|
{
|
|
LOG_FORMATS("Unsupported track count: %d\n", geometry->tracksperside);
|
|
free(track_data);
|
|
return 0;
|
|
}
|
|
|
|
free(track_data);
|
|
return 100;
|
|
}
|
|
|
|
static FLOPPY_IDENTIFY(ti99_tdf_identify)
|
|
{
|
|
UINT64 size;
|
|
size = floppy_image_size(floppy);
|
|
*vote = ti99_tdf_guess_geometry(floppy, size, NULL);
|
|
LOG_FORMATS("TDF voting %d\n", *vote);
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
static FLOPPY_CONSTRUCT(ti99_tdf_construct)
|
|
{
|
|
struct ti99dsk_geometry geometry;
|
|
struct FloppyCallbacks *callbacks;
|
|
struct ti99dsk_tag *tag;
|
|
LOG_FORMATS("Reading image as TDF\n");
|
|
if (params)
|
|
{
|
|
/* create */
|
|
memset(&geometry, 0, sizeof(geometry));
|
|
/*
|
|
geometry.sides = option_resolution_lookup_int(params, PARAM_HEADS);
|
|
geometry.tracksperside = option_resolution_lookup_int(params, PARAM_TRACKS);
|
|
geometry.secspertrack = option_resolution_lookup_int(params, PARAM_SECTORS);
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
/* load */
|
|
if (ti99_tdf_guess_geometry(floppy, floppy_image_size(floppy), &geometry)<50)
|
|
return FLOPPY_ERROR_INVALIDIMAGE;
|
|
}
|
|
|
|
assert(geometry.sides);
|
|
assert(geometry.tracksperside);
|
|
assert(geometry.secspertrack);
|
|
|
|
tag = (struct ti99dsk_tag *) floppy_create_tag(floppy, sizeof(struct ti99dsk_tag));
|
|
|
|
tag->heads = geometry.sides;
|
|
tag->tracks = geometry.tracksperside;
|
|
|
|
if (geometry.density==TI99_FM)
|
|
tag->track_size = geometry.secspertrack * TI99_FM_TOTAL_SECTOR + TI99_FM_TRACK_ADD;
|
|
else
|
|
tag->track_size = geometry.secspertrack * TI99_MFM_TOTAL_SECTOR + TI99_MFM_TRACK_ADD;
|
|
|
|
tag->format = geometry.density;
|
|
tag->first_idam = 0; /* let's find out later */
|
|
tag->dam_offset = 0;
|
|
|
|
/* set up format callbacks */
|
|
callbacks = floppy_callbacks(floppy);
|
|
callbacks->read_sector = ti99_tdf_read_sector;
|
|
callbacks->write_sector = ti99_tdf_write_sector;
|
|
callbacks->read_indexed_sector = ti99_tdf_read_indexed_sector;
|
|
callbacks->write_indexed_sector = ti99_tdf_write_indexed_sector;
|
|
callbacks->read_track = ti99_tdf_read_track;
|
|
callbacks->write_track = ti99_tdf_write_track;
|
|
/* callbacks->format_track = ti99_tdf_format_track; */
|
|
callbacks->get_track_size = ti99_get_track_size;
|
|
/* post format unset */
|
|
callbacks->get_heads_per_disk = ti99_get_heads_per_disk;
|
|
callbacks->get_tracks_per_disk = ti99_get_tracks_per_disk;
|
|
callbacks->get_sector_length = ti99_get_sector_length;
|
|
callbacks->get_indexed_sector_info = ti99_tdf_get_indexed_sector_info;
|
|
|
|
return FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
FLOPPY_OPTIONS_START( ti99 )
|
|
FLOPPY_OPTION( ti99_sdf, "dsk", "TI99 sector dump (v9t9)", ti99_sdf_identify, ti99_sdf_construct, NULL,
|
|
HEADS([1]-2)
|
|
TRACKS(35-[40]-80)
|
|
SECTORS(8/9/16/[18]/36)
|
|
SECTOR_LENGTH([256])
|
|
FIRST_SECTOR_ID(0))
|
|
FLOPPY_OPTION( ti99_tdf, "dsk,dtk", "TI99 track dump (pc99)", ti99_tdf_identify, ti99_tdf_construct, NULL,
|
|
TRACKS(35-[40]-80)
|
|
SECTORS(8/9/16/[18]/36)
|
|
SECTOR_LENGTH([256])
|
|
FIRST_SECTOR_ID(0))
|
|
FLOPPY_OPTIONS_END
|