summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib
diff options
context:
space:
mode:
author Michael Zapf <michael.zapf@mizapf.de>2014-09-26 18:35:07 +0000
committer Michael Zapf <michael.zapf@mizapf.de>2014-09-26 18:35:07 +0000
commit437d5c500867b482350db534bb7f15558b4c03af (patch)
treeea063f0dd5c8e728a12174a31bed3cd31db80823 /src/lib
parentc6d43e680ae5684e73af41d6479734afed8e5495 (diff)
(MESS) hdc9234 still WIP, HFDC fixes, disk formats re-implemented for
modernized floppy system. (nw)
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/formats/ti99_dsk.c1397
-rw-r--r--src/lib/formats/ti99_dsk.h93
2 files changed, 868 insertions, 622 deletions
diff --git a/src/lib/formats/ti99_dsk.c b/src/lib/formats/ti99_dsk.c
index 67b6a56b982..8504578a4dc 100644
--- a/src/lib/formats/ti99_dsk.c
+++ b/src/lib/formats/ti99_dsk.c
@@ -24,9 +24,20 @@
* controllers and ROMs allow for up to 36 sectors per track and 80 tracks on
* both sides, which is 1,44 MiB (DSHD80).
*
+ * Double stepping
+ * --------------
+ * When using a 40-track disk in an 80-track drive, it seems as if each
+ * track appears twice in sequence (0,0,1,1,2,2,...,39,39).
+ * The system will write to each second track and leave the others untouched.
+ * When we write back that image, there is no simple way to know that
+ * the 80 tracks are in fact doubled 40 tracks. We will actually write
+ * all 80 tracks, doubling the size of the image file. This disk image will
+ * now become unusable in a 40-track drive - which is exactly what happens in reality.
+ *
+ * -------------------------------------------------------------------
* The second half of this file contains the legacy implementation.
*
- * Michael Zapf, Feb 2014
+ * Michael Zapf, Sep 2014
*
********************************************************************/
@@ -43,531 +54,234 @@
// Debugging
#define TRACE 0
-/*
- Sector Dump Format
- ------------------
- The Sector Dump Format is also known as v9t9 format (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 sectors
- are ordered by their logical number as used in the TI file system.
-
- In this implementation, the sector dump format is just a kind of
- wd177x_format with minor customizations, which allows us to keep the code
- small. The difference is the logical ordering of tracks and sectors.
-
- The TI file system orders all tracks on side 0 as going inwards,
- and then all tracks on side 1 going outwards.
-
- 00 01 02 03 ... 38 39 side 0
- 79 78 77 76 ... 41 40 side 1
-
- The SDF format stores the tracks and their sectors in logical order
- 00 01 02 03 ... 38 39 [40 41 ... 79]
+// ====================================================
+// Common methods for both formats.
+// ====================================================
- There is also a variant of the SDF which adds three sectors at the end
- containing a map of bad sectors. This was introduced by a tool to read
- real TI floppy disks on a PC. As other emulators tolerate this additional
- bad sector map, we just check whether there are 3 more sectors and ignore
- them.
+/*
+ Find out whether there are IDAMs and DAMs (without having clock bits).
+ As said above, let's not spend too much effort allowing format deviations.
+ If the image does not exactly adhere to the format, give up.
*/
-const char *ti99_sdf_format::name() const
+bool ti99_floppy_format::check_for_address_marks(UINT8* trackdata, int encoding)
{
- return "sdf";
-}
+ int i=0;
-const char *ti99_sdf_format::description() const
-{
- return "TI99 sector dump floppy disk image";
+ if (encoding==floppy_image::FM)
+ {
+ // Check 5 sectors of track 0
+ while (i < 5)
+ {
+ if (trackdata[16 + 6 + i*334] != 0xfe) break;
+ if (trackdata[16 + 30 + i*334] != 0xfb && trackdata[16 + 30 + i*334] != 0xf8) break;
+ i++;
+ }
+ }
+ else
+ {
+ // Try MFM
+ i = 0;
+ while (i < 5)
+ {
+ if (trackdata[40 + 13 + i*340] != 0xfe) break;
+ if (trackdata[40 + 57 + i*340] != 0xfb && trackdata[40 + 57 + i*334] != 0xf8) break;
+ i++;
+ }
+ }
+ return (i==5);
}
-const char *ti99_sdf_format::extensions() const
+int ti99_floppy_format::get_encoding(int cell_size)
{
- return "dsk";
+ return (cell_size==4000)? floppy_image::FM : floppy_image::MFM;
}
-bool ti99_sdf_format::supports_save() const
+/*
+ Load the image from disk and convert it into a sequence of flux levels.
+*/
+bool ti99_floppy_format::load(io_generic *io, UINT32 form_factor, floppy_image *image)
{
- return true;
-}
+ int cell_size = 0;
+ int sector_count = 0;
+ int heads = 0;
+ determine_sizes(io, cell_size, sector_count, heads);
-int ti99_sdf_format::identify(io_generic *io, UINT32 form_factor)
-{
- UINT64 file_size = io_generic_size(io);
- int vote = 0;
+ if (cell_size == 0) return false;
- vib_t vib;
+ // Be ready to hold a track of up to 36 sectors (with gaps and marks)
+ UINT8 trackdata[13000];
- // Adding support for another sector image format which adds 768 bytes
- // as a bad sector map
- if ((file_size / SECTOR_SIZE) % 10 == 3)
- {
- if (TRACE) logerror("ti99_dsk: Stripping map of bad sectors at image end\n");
- file_size -= SECTOR_SIZE*3;
- }
+ int maxtrack, maxheads;
+ image->get_maximal_geometry(maxtrack, maxheads);
- // Formats with 9 or 18 sectors per track
- if (((file_size % 92160)==0)
- && ( (file_size/92160==1)
- || (file_size/92160==2)
- || (file_size/92160==4)
- || (file_size/92160==8)
- || ((file_size/92160==16)&& (form_factor==floppy_image::FF_35)))) vote = 50;
+ int file_size = io_generic_size(io);
+ int track_size = get_track_size(cell_size, sector_count);
+ int track_count = file_size / (track_size*heads);
- // Formats with 16 sectors per track (rare)
- if (((file_size % 163840)==0)
- && ( (file_size/163840==1)
- || (file_size/163840==2))) vote = 50;
+ if (TRACE) logerror("ti99_dsk: track count = %d\n", track_count);
- if (vote > 0)
+ if (track_count > maxtrack)
{
- // Read first sector (Volume Information Block)
- io_generic_read(io, &vib, 0, sizeof(vib_t));
-
- // Check from contents
- if ((vib.id[0]=='D')&&(vib.id[1]=='S')&&(vib.id[2]=='K'))
- {
- if (TRACE) logerror("ti99_dsk: Found formatted SDF disk medium\n");
- vote = 100;
- }
- else
- {
- if (TRACE) logerror("ti99_dsk: No valid VIB found; disk may be unformatted\n");
- }
+ logerror("ti99_dsk: Floppy disk has too many tracks for this drive.\n");
+ return false;
}
- else if (TRACE) logerror("ti99_dsk: Disk image obviously not a SDF image\n");
- return vote;
-}
-
-int ti99_sdf_format::find_size(io_generic *io, UINT32 form_factor)
-{
- UINT64 file_size = io_generic_size(io);
- vib_t vib;
- int vib_enc = 0;
+ bool doubletracks = (track_count * 2 <= maxtrack);
- // See above
- if ((file_size / SECTOR_SIZE) % 10 == 3) file_size -= SECTOR_SIZE*3;
-
- // Read first sector
- io_generic_read(io, &vib, 0, sizeof(vib_t));
-
- // Check from contents
- if ((vib.id[0]=='D')&&(vib.id[1]=='S')&&(vib.id[2]=='K'))
- {
- // Find out more about the density. SSDD happens to be the same size
- // as DSSD in the sector dump format, so we need to ask the
- // VIB if available. Oherwise, we assume that we have a DSSD medium.
- vib_enc = (vib.density < 2)? floppy_image::FM : floppy_image::MFM;
- if (TRACE) logerror("ti99_dsk: VIB says that disk is %s density\n", (vib_enc==floppy_image::FM)? "single":"double");
- }
+ if (doubletracks) logerror("ti99_dsk: 40-track image in an 80-track drive. On save, image size will double.\n");
- for (int i=0; formats[i].form_factor != 0; i++)
+ // Read the image
+ for(int head=0; head < heads; head++)
{
- if (formats[i].form_factor != form_factor)
+ for(int track=0; track < track_count; track++)
{
- if (TRACE) logerror("ti99_dsk: format %d has different form factor\n", i);
- continue;
- }
- if (TRACE) logerror("ti99_dsk: trying sec=%d, tr=%d, head=%d, ss=%d for file size %d\n", formats[i].sector_count, formats[i].track_count, formats[i].head_count, formats[i].sector_base_size, (int)file_size);
+ load_track(io, trackdata, head, track, sector_count, track_count, cell_size);
- if (formats[i].sector_count * formats[i].track_count * formats[i].head_count * formats[i].sector_base_size == (int)file_size)
- {
- if (vib_enc == formats[i].encoding || vib_enc==0)
+ if (doubletracks)
{
- if (TRACE) logerror("ti99_dsk: format %d matches\n", i);
- return i;
+ // Create two tracks from each medium track. This reflects the
+ // fact that the drive head will find the same data after
+ // a single step
+ if (get_encoding(cell_size)==floppy_image::FM)
+ {
+ generate_track_fm(track*2, head, cell_size, trackdata, image);
+ generate_track_fm(track*2+1, head, cell_size, trackdata, image);
+ }
+ else
+ {
+ generate_track_mfm(track*2, head, cell_size, trackdata, image);
+ generate_track_mfm(track*2+1, head, cell_size, trackdata, image);
+ }
}
else
{
- if (TRACE) logerror("ti99_dsk: format %d matches by size, but encoding does not match\n", i);
+ // Normal tracks
+ if (get_encoding(cell_size)==floppy_image::FM)
+ generate_track_fm(track, head, cell_size, trackdata, image);
+ else
+ generate_track_mfm(track, head, cell_size, trackdata, image);
}
}
}
- return -1;
-}
-
-int ti99_sdf_format::get_image_offset(const format &f, int head, int track)
-{
- int logicaltrack = head * f.track_count;
- logicaltrack += ((head&1)==0)? track : (f.track_count - 1 - track);
- if (TRACE) logerror("ti99_dsk: track %d, head %d -> logical track %d\n", track, head, logicaltrack);
- return logicaltrack * compute_track_size(f);
-}
-
-// Format: form_factor, variant, encoding, cell_size, sector_count, track_count,
-// head_count, sector_base_size, per_sector_size, sector_base_id,
-// per_sector_id, gap1, gap2, gap3
-
-const ti99_sdf_format::format ti99_sdf_format::formats[] =
-{
- { // 90K 5.25" single sided single density
- floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
- 4000, 9, 40, 1, SECTOR_SIZE, {}, 0, {}, 16, 11, 45
- },
- { // 180K 5.25" double sided single density
- floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
- 4000, 9, 40, 2, SECTOR_SIZE, {}, 0, {}, 16, 11, 45
- },
- { // 160K 5.25" single sided double density 16 sectors (rare)
- floppy_image::FF_525, floppy_image::DSSD, floppy_image::MFM,
- 2000, 16, 40, 1, SECTOR_SIZE, {}, 0, {}, 40, 22, 24
- },
- { // 180K 5.25" single sided double density
- floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
- 2000, 18, 40, 1, SECTOR_SIZE, {}, 0, {}, 40, 22, 24
- },
- { // 320K 5.25" double sided double density 16 sectors (rare)
- floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
- 2000, 16, 40, 2, SECTOR_SIZE, {}, 0, {}, 40, 22, 24
- },
- { // 360K 5.25" double sided double density
- floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
- 2000, 18, 40, 2, SECTOR_SIZE, {}, 0, {}, 40, 22, 24
- },
- { // 720K 5.25" double sided quad density (80 tracks)
- floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
- 2000, 18, 80, 2, SECTOR_SIZE, {}, 0, {}, 40, 22, 24
- },
- { // 720K 3.5" double sided double density (80 tracks)
- floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
- 2000, 18, 80, 2, SECTOR_SIZE, {}, 0, {}, 40, 22, 24
- },
- { // 1440K 3.5" double sided high density (80 tracks)
- floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
- 1000, 36, 80, 2, SECTOR_SIZE, {}, 0, {}, 40, 22, 24
- },
- { 0 }
-};
-
-/*
- FM track image for sector dump format
-*/
-floppy_image_format_t::desc_e* ti99_sdf_format::get_desc_fm(const format &f, int &current_size, int &end_gap_index)
-{
- static floppy_image_format_t::desc_e desc_fm[] =
- {
- { SECTOR_INTERLEAVE_SKEW, 3, 3 }, // skew/2 because of track*2+head
- { FM, 0x00, f.gap_1 }, // Gap 1 (note that we have 00 instead of ff)
- { SECTOR_LOOP_START, 0, 8 }, // 9 sectors
- { FM, 0x00, 6 }, // pre-id gap
- { CRC_CCITT_FM_START, 1 },
- { RAW, 0xf57e, 1 }, // IDAM (fe, clock=c7; 11110101 01111110)
- { TRACK_ID_FM },
- { HEAD_ID_FM },
- { SECTOR_ID_FM },
- { SIZE_ID_FM },
- { CRC_END, 1 },
- { CRC, 1 },
- { FM, 0xff, f.gap_2 }, // Gap 2
- { FM, 0x00, 6 },
- { CRC_CCITT_FM_START, 2 },
- { RAW, 0xf56f, 1 }, // DAM (fb, clock=c7; 11110101 01101111)
- { SECTOR_DATA_FM, -1 }, // Sector data
- { CRC_END, 2 },
- { CRC, 2 },
- { FM, 0xff, f.gap_3 }, // Gap 3
- { SECTOR_LOOP_END },
- { FM, 0xff, 0 }, // track lead-out (end gap, 231)
- { END }
- };
- current_size = (f.gap_1 + 9 * (6 + 1 + 4 + 2 + f.gap_2 + 6 + 1 + SECTOR_SIZE + 2 + f.gap_3) + 0) * 16;
- end_gap_index = 21;
-
- return desc_fm;
-}
-
-/*
- MFM track image for sector dump format
-*/
-floppy_image_format_t::desc_e* ti99_sdf_format::get_desc_mfm(const format &f, int &current_size, int &end_gap_index)
-{
- static floppy_image_format_t::desc_e desc_mfm[] =
- {
- { SECTOR_INTERLEAVE_SKEW, 4, 0 }, // Possible ilv: 0, 4, 6, 10, 12, 16.
- { MFM, 0x4e, f.gap_1 }, // track lead-in
- { SECTOR_LOOP_START, 0, 17 }, // 18 sectors
- { CRC_CCITT_START, 1 },
- { RAW, 0x4489, 3 }, // A1 sync mark
- { MFM, 0xfe, 1 }, // IDAM
- { TRACK_ID },
- { HEAD_ID },
- { SECTOR_ID },
- { SIZE_ID },
- { CRC_END, 1 },
- { CRC, 1 },
- { MFM, 0x4e, f.gap_2 }, // Gap 2
- { MFM, 0x00, 12 },
- { CRC_CCITT_START, 2 },
- { RAW, 0x4489, 3 }, // A1
- { MFM, 0xfb, 1 }, // DAM
- { SECTOR_DATA, -1 }, // Sector data
- { CRC_END, 2 },
- { CRC, 2 },
- { MFM, 0x4e, f.gap_3 }, // Gap 3
- { SECTOR_LOOP_END },
- { MFM, 0x4e, 0 }, // track lead-out (712)
- { END }
- };
- current_size = (f.gap_1 + 18 * (3 + 1 + 4 + 2 + f.gap_2 + 12 + 3 + 1 + SECTOR_SIZE + 2 + f.gap_3) + 0) * 16;
- end_gap_index = 22;
-
- return desc_mfm;
-}
-
-const floppy_format_type FLOPPY_TI99_SDF_FORMAT = &floppy_image_format_creator<ti99_sdf_format>;
-
-/*
- Track Dump Format
- -----------------
- 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.
-
- For FM recording, each track is exactly 3253 bytes long in this format.
- For MFM recording, track length is 6872 bytes.
-
- Accordingly, we get a multiple of these values as the image length.
- There are no single-sided images (when single-sided, the upper half of
- the image is empty).
-
- DSSD: 260240 bytes
- DSDD: 549760 bytes
-
- We do not support other geometries in this format.
-
- Tracks are stored from outside to inside of head 0, then outside to inside of head 1:
-
- [Head0/track0] [Head0/track1] ... [Head0/track39] [Head1/track0] [Head1/track1] ... [Head1/track39]
-
- Known Issues:
- - When formatting a disk in MFM encoding, BwG with Disk Manager 2 creates
- a format that is not compatible with TDF.
- It differs not only with respect to the Gap_1 size but also wrt the
- pre-id gap length and therefore causes a different track layout
- (sectors are apart by 346 instead of 340 bytes).
-*/
-
-const char *ti99_tdf_format::name() const
-{
- return "tdf";
-}
-
-const char *ti99_tdf_format::description() const
-{
- return "TI99 track dump floppy disk image";
-}
-
-const char *ti99_tdf_format::extensions() const
-{
- return "dsk,dtk";
-}
-
-bool ti99_tdf_format::supports_save() const
-{
return true;
}
-/*
- Determine whether the image file can be interpreted as a track dump
-*/
-int ti99_tdf_format::identify(io_generic *io, UINT32 form_factor)
+bool ti99_floppy_format::save(io_generic *io, floppy_image *image)
{
- UINT64 file_size = io_generic_size(io);
- int vote = 0;
- UINT8 trackdata[500];
+ int act_track_size = 0;
- // Formats with 9 or 18 sectors per track
- if ((file_size == 260240) || (file_size == 549760))
- vote = 50;
+ UINT8 bitstream[500000/8];
+ UINT8 trackdata[9216]; // max size
- if (vote > 0)
- {
- if (TRACE) logerror("ti99_dsk: Image looks like a TDF image\n");
+ int cellsizes[] = { 2000, 4000, 1000, 2000 };
- // Get some bytes from track 0
- io_generic_read(io, trackdata, 0, 500);
+ // Do we use double-stepping?
+ // If our image was loaded into a 80-track drive, we will always write 80 tracks.
+ int maxtrack, maxheads;
+ image->get_maximal_geometry(maxtrack, maxheads);
- int start = find_start(trackdata, 500, (file_size < 500000)? floppy_image::FM : floppy_image::MFM);
- if (start != -1)
- {
- if (TRACE) logerror("ti99_dsk: Start of image complies with TDF\n");
- vote = 100;
- }
- else
- {
- logerror("ti99_dsk: Image does not comply with TDF; may be broken or unformatted\n");
- }
+ if (maxtrack > 80) maxtrack = 80;
+ else
+ {
+ if (maxtrack > 35 && maxtrack < 80) maxtrack = 40;
+ else maxtrack = 35;
}
- else logerror("ti99_dsk: Disk image obviously not a TDF image\n");
- return vote;
-}
-/*
- Find the proper format for this image.
-*/
-int ti99_tdf_format::find_size(io_generic *io, UINT32 form_factor)
-{
- UINT64 file_size = io_generic_size(io);
+ int attempt = 0;
+ int sector[36];
+ int maxsect = 18;
+ bool write = true;
- for (int i=0; formats[i].form_factor != 0; i++)
+ // We expect a bitstream of length 50000 for FM and 100000 for MFM
+ for(int head=0; head < 2; head++)
{
- if (formats[i].form_factor != form_factor)
+ int track = 0;
+ while (track < maxtrack)
{
- if (TRACE) logerror("ti99_dsk: format %d has different form factor\n", i);
- continue;
- }
- if (TRACE) logerror("ti99_dsk: trying format %d with encoding=%s, tr=%d, head=%d for file size %d\n", i, (formats[i].encoding==floppy_image::FM)? "FM" : "MFM", formats[i].track_count, formats[i].head_count, (int)file_size);
- if (formats[i].track_size * formats[i].track_count * formats[i].head_count == (int)file_size)
- {
- if (TRACE) logerror("ti99_dsk: format %d matches\n", i);
- return i;
- }
- }
- return -1;
-}
+ int cell_size = cellsizes[attempt];
+ int encoding = get_encoding(cell_size);
+ int track_size = get_track_size(cell_size, 36); // max number of sectors
-/*
- Find out where the address marks are. Unfortunately, the TDF offers no
- clock signals. So we are looking for this pattern:
+ // Retrieve the cells from the flux sequence
+ generate_bitstream_from_track(track, head, cell_size, bitstream, act_track_size, image);
- FM: (00)*6, FE, (xx)*6, (FF)*11, (00)*6, FB
- MFM: (00)*10, (a1)*3, fe, (xx)*6, (4e)*22, (00)*12
+ // Maybe the track has become longer due to moving splices
+ if (act_track_size > 200000000/cell_size) act_track_size = 200000000/cell_size;
- Returns the position of the start of the pre-IDAM gap.
-*/
-int ti99_tdf_format::find_start(UINT8* trackdata, int track_size, int encoding)
-{
- int pos = 0;
- int pos1 = 0;
+ int marks = decode_bitstream(bitstream, trackdata, sector, act_track_size, encoding, (encoding==floppy_image::FM)? 0xff:0x4e, track_size);
- // Does not make sense to look any further but the first 400 bytes.
- while (pos < 400)
- {
- if (encoding==floppy_image::FM)
- {
- // FM
- if (!has_byteseq(trackdata, track_size, 0x00, pos, 6))
- {
- pos++;
- continue;
- }
- pos1 = pos + 6;
- if (trackdata[pos1]!=0xfe)
+ if (track==0)
{
- pos++;
- continue;
- }
- pos1 += 7;
- if (!has_byteseq(trackdata, track_size, 0xff, pos1, 11))
- {
- pos++;
- continue;
- }
- pos1 += 11;
- if (!has_byteseq(trackdata, track_size, 0x00, pos1, 6))
- {
- pos++;
- continue;
- }
- pos1 += 6;
- if (trackdata[pos1]!=0xfb)
- {
- pos++;
- continue;
- }
- }
- else
- {
- // MFM
- if (!has_byteseq(trackdata, track_size, 0x00, pos, 10))
- {
- pos++;
- continue;
- }
- pos1 = pos + 10;
- if (!has_byteseq(trackdata, track_size, 0xa1, pos1, 3))
- {
- pos++;
- continue;
- }
- pos1 += 3;
- if (trackdata[pos1]!=0xfe)
- {
- pos++;
- continue;
- }
- pos1 += 7;
- if (!has_byteseq(trackdata, track_size, 0x4e, pos1, 22))
- {
- pos++;
- continue;
+ if (head==0)
+ {
+ // Find the highest sector in the track
+ // This is only needed for the SDF format
+ int i=35;
+ while (i>=0 && sector[i]==-1) i--;
+
+ if (i>18) maxsect = 36;
+ else
+ {
+ if (i>16) maxsect = 18;
+ else
+ {
+ if (i>9) maxsect = 16;
+ else maxsect = 9;
+ }
+ }
+ if (TRACE) logerror("ti99_dsk: Sectors/track: %d\n", maxsect);
+
+ // We try different cell sizes until we find a fitting size.
+ // If this fails, we fall back to a size of 2000 ns
+ // The criterion for a successful decoding is that we find at least
+ // 6 ID or data address marks on the track. It is highly unlikely
+ // to find so many marks with a wrong cell size.
+ if (marks < 6 && attempt < 4)
+ {
+ if (TRACE) logerror("ti99_dsk: Decoding with cell size %d failed.\n", cell_size);
+ attempt++;
+ write = false;
+ }
+ else write = true;
+ }
+ else
+ {
+ if (marks < 6)
+ {
+ if (min_heads()==1)
+ {
+ if (TRACE) logerror("ti99_dsk: We don't have a second side and the format allows for single-sided recording.\n");
+ return true;
+ }
+ else
+ {
+ logerror("ti99_dsk: No second side, but this format requires two-sided recording. Saving empty tracks.\n");
+ }
+ }
+ }
}
- pos1 += 22;
- if (!has_byteseq(trackdata, track_size, 0x00, pos1, 12))
+
+ if (write)
{
- pos++;
- continue;
+ if (TRACE)
+ {
+ if (head == 0 && track == 0)
+ {
+ if (marks >=6) { if (TRACE) logerror("ti99_dsk: Decoding with cell size %d successful.\n", cell_size); }
+ else logerror("ti99_dsk: No address marks found on track 0. Assuming MFM format.\n");
+ }
+ }
+ // Save to the file
+ write_track(io, trackdata, sector, track, head, maxsect, maxtrack, track_size);
+ track++;
}
}
- return pos;
}
- return -1;
-}
-
-/*
- Determine the offset from the start of the image, given the head and track.
- At this location the track will be saved to the file.
-*/
-int ti99_tdf_format::get_image_offset(const format &f, int head, int track)
-{
- return ((f.track_count * head) + track) * f.track_size;
-}
-/*
- Load the TDF image from disk and convert it into a sequence of flux levels.
-*/
-bool ti99_tdf_format::load(io_generic *io, UINT32 form_factor, floppy_image *image)
-{
- int type = find_size(io, form_factor);
- if(type == -1)
- return false;
-
- UINT8 trackdata[6872];
- const format &f = formats[type];
-
- // Read the image
- for(int head=0; head < f.head_count; head++)
- {
- for(int track=0; track < f.track_count; track++)
- {
- io_generic_read(io, trackdata, get_image_offset(f, head, track), f.track_size);
- if (f.encoding==floppy_image::FM)
- generate_track_fm(track, head, f.cell_size, trackdata, image);
- else
- generate_track_mfm(track, head, f.cell_size, trackdata, image);
- }
- }
- return true;
-}
-
-/*
- Check whether a sequence of bytes is at the given position.
-*/
-bool ti99_tdf_format::has_byteseq(UINT8* trackdata, int track_size, UINT8 byte, int pos, int count)
-{
- while ((pos < track_size) && (count > 0))
- {
- if (trackdata[pos] != byte) return false;
- pos++;
- count--;
- }
return true;
}
-void ti99_tdf_format::generate_track_fm(int track, int head, int cell_size, UINT8* trackdata, floppy_image *image)
+void ti99_floppy_format::generate_track_fm(int track, int head, int cell_size, UINT8* trackdata, floppy_image *image)
{
int track_size_cells = 200000000/cell_size;
UINT32 *buffer = global_alloc_array_clear(UINT32, 200000000/cell_size);
@@ -587,15 +301,13 @@ void ti99_tdf_format::generate_track_fm(int track, int head, int cell_size, UINT
int offset = 0;
short crc1, crc2, found_crc;
+ int start = 16;
- int start = find_start(trackdata, track_size, floppy_image::FM);
- bool broken = false;
-
- if (start == -1)
+ if (check_for_address_marks(trackdata, floppy_image::FM)==false)
{
- logerror("ti99_dsk: TDF: Start of track %d, head %d not found. Assuming broken format, starting at offset 16.\n", track, head);
- start = 16;
- broken = true;
+ if (head==0 && track==0) logerror("ti99_dsk: Cannot find FM address marks on track %d, head %d; likely broken or unformatted.\n", track, head);
+ global_free_array(buffer);
+ return;
}
// Found a track; we now know where the address marks are:
@@ -630,12 +342,13 @@ void ti99_tdf_format::generate_track_fm(int track, int head, int cell_size, UINT
if ((found_crc & 0xffff) == 0xf7f7)
{
// PC99 format: no real CRC; replace it
+ // Also, when converting from SDF we let this method create the proper CRC.
// logerror("Warning: PC99 format using pseudo CRC1; replace by %04x\n", crc1);
trackdata[i] = (crc1 >> 8) & 0xff;
trackdata[i+1] = crc1 & 0xff;
found_crc = crc1;
}
- if ((crc1 != found_crc) && !broken)
+ if (crc1 != found_crc)
{
logerror("ti99_dsk: Warning: CRC1 does not match (track=%d, head=%d). Found = %04x, calc = %04x\n", track, head, found_crc& 0xffff, crc1& 0xffff);
}
@@ -655,7 +368,7 @@ void ti99_tdf_format::generate_track_fm(int track, int head, int cell_size, UINT
trackdata[i+1] = crc2 & 0xff;
found_crc = crc2;
}
- if ((crc2 != found_crc) && !broken)
+ if (crc2 != found_crc)
{
logerror("ti99_dsk: Warning: CRC2 does not match (track=%d, head=%d). Found = %04x, calc = %04x\n", track, head, found_crc& 0xffff, crc2& 0xffff);
}
@@ -671,7 +384,7 @@ void ti99_tdf_format::generate_track_fm(int track, int head, int cell_size, UINT
global_free_array(buffer);
}
-void ti99_tdf_format::generate_track_mfm(int track, int head, int cell_size, UINT8* trackdata, floppy_image *image)
+void ti99_floppy_format::generate_track_mfm(int track, int head, int cell_size, UINT8* trackdata, floppy_image *image)
{
int track_size_cells = 200000000/cell_size;
UINT32 *buffer = global_alloc_array_clear(UINT32, 200000000/cell_size);
@@ -681,17 +394,16 @@ void ti99_tdf_format::generate_track_mfm(int track, int head, int cell_size, UIN
// Here, Gap 4 is actually only 90 bytes long
// (not 712 as specified in the TDF format)
int track_size = track_size_cells / 16;
- int start = find_start(trackdata, track_size, floppy_image::MFM);
- bool broken = false;
int offset = 0;
short crc1, crc2, found_crc;
+ int start = 40;
- if (start == -1)
+ if (check_for_address_marks(trackdata, floppy_image::MFM)==false)
{
- logerror("ti99_dsk: TDF: Start of track %d, head %d not found. Assuming broken format, starting at offset 40.\n", track, head);
- start = 40;
- broken = true;
+ if (track==0 && head==0) logerror("ti99_dsk: Cannot find MFM address marks on track %d, head %d; likely broken or unformatted.\n", track, head);
+ global_free_array(buffer);
+ return;
}
// Found a track; we now know where the address marks are:
@@ -738,7 +450,7 @@ void ti99_tdf_format::generate_track_mfm(int track, int head, int cell_size, UIN
trackdata[i+1] = crc1 & 0xff;
found_crc = crc1;
}
- if (crc1 != found_crc && !broken)
+ if (crc1 != found_crc)
{
logerror("ti99_dsk: Warning: CRC1 does not match (track=%d, head=%d). Found = %04x, calc = %04x\n", track, head, found_crc & 0xffff, crc1& 0xffff);
}
@@ -758,7 +470,7 @@ void ti99_tdf_format::generate_track_mfm(int track, int head, int cell_size, UIN
trackdata[i+1] = crc2 & 0xff;
found_crc = crc2;
}
- if (crc2 != found_crc && !broken)
+ if (crc2 != found_crc)
{
logerror("ti99_dsk: Warning: CRC2 does not match (track=%d, head=%d). Found = %04x, calc = %04x\n", track, head, found_crc& 0xffff, crc2& 0xffff);
}
@@ -774,131 +486,664 @@ void ti99_tdf_format::generate_track_mfm(int track, int head, int cell_size, UIN
global_free_array(buffer);
}
+// States for decoding the bitstream
+enum
+{
+ FMIDAM,
+ MFMIDAM,
+ HEADER,
+ FMDAM,
+ MFMDAM,
+ DATA
+};
+
/*
- Get the track bytes from the bitstream. We store this sequence as the
- contents of the TDF.
-
- Note that this is risky to some extend. Since write splices may shift
- in theory, we must be aware that dumping the bits may not be guaranteed
- to deliver the sector contents properly. On the other hand, the floppy
- implementation currently works very precisely, so it is not likely that
- we get shifted splices.
-
- To make this more stable we should better retrieve all sectors
- separately (by extract_sectors_from_bitstream), then paste the sectors
- back into the respective positions of the track image, then save this
- rebuilt track image. Hence, as the track data outside of the sectors remain
- unchanged after formatting, we can ensure a proper positioning of the
- sectors for the TDF.
+ Decodes the bitstream into a TDF track image.
+ Returns the number of detected marks.
*/
-void ti99_tdf_format::extract_track_from_bitstream(const UINT8 *bitstream, int track_size, UINT8 *trackdata)
-{
- int pos = 0;
+int ti99_floppy_format::decode_bitstream(const UINT8 *bitstream, UINT8 *trackdata, int* sector, int cell_count, int encoding, UINT8 gapbytes, int track_size)
+{
+ int databytes = 0;
+ int a1count = 0;
+ int lastpos = 0;
+ int headerbytes = 0;
+ int curpos = 0;
+ UINT8 curbyte = 0;
+ UINT16 shift_reg = 0;
int tpos = 0;
- int lastpos = -1;
- while ((pos < track_size) && (pos > lastpos)) {
- // When pos < lastpos, we wrapped around the track end
- lastpos = pos;
- UINT8 byte = sbyte_mfm_r(bitstream, pos, track_size);
- trackdata[tpos++] = byte;
- }
-}
-
-bool ti99_tdf_format::save(io_generic *io, floppy_image *image)
-{
- int act_track_size = 0;
-
- UINT8 bitstream[500000/8];
- UINT8 trackdata[6872];
+ int pos = 0;
+ int state;
+ bool good = false;
+ int marks = 0;
+ int current_sector = 0;
- // From the track_size alone we cannot tell whether this is FM or MFM
- // We will have to try both ways; start with the size of the existing file
+ // Init track
+ memset(trackdata, 0x00, track_size);
- int type = (io_generic_size(io) > 500000)? 1 : 0;
+ for (int i=0; i < 36; i++) sector[i] = -1;
- // If this choice fails we must try the other way
- // If this fails again, we go for the first option again and save a possibly
- // broken format
+ state = (encoding==floppy_image::MFM)? MFMIDAM : FMIDAM;
- bool goodformat = true;
- for (int option=0; option < 3; option++)
+ while (pos < cell_count && tpos < track_size)
{
- const format &f = formats[type];
- if (TRACE) logerror("ti99_dsk: Trying format %d (TDF) for saving\n", type);
- goodformat = true;
+ shift_reg = (shift_reg << 1) & 0xffff;
+ if ((pos & 0x07)==0) curbyte = bitstream[curpos++];
+ if ((curbyte & 0x80) != 0) shift_reg |= 1;
+ curbyte <<= 1;
- // We expect a bitstream of length 50000 for FM and 100000 for MFM
- for(int head=0; (head < 2) && goodformat; head++)
+ switch (state)
{
- for(int track=0; (track < 40) && goodformat; track++)
+ case FMIDAM:
+ if (shift_reg == 0xf57e)
{
- // Retrieve the cells from the flux sequence
- generate_bitstream_from_track(track, head, f.cell_size, bitstream, act_track_size, image);
+ marks++;
+ // Found a header
+ headerbytes = 5;
+ // Create GAP0 at the beginning or GAP3 later
+ if (tpos==0)
+ tpos += 16;
+ else
+ for (int i=0; i < 45; i++) trackdata[tpos++] = gapbytes;
- // Maybe the track has become longer due to moving splices
- if (act_track_size > 200000000/f.cell_size)
+ // IDAM sync bytes
+ tpos += 6;
+ trackdata[tpos++] = 0xfe;
+ state = HEADER;
+ lastpos = pos;
+ }
+ break;
+
+ case MFMIDAM:
+ // Count three subsequent A1
+ if (shift_reg == 0x4489)
+ {
+ if (lastpos > 0)
{
- // Truncate the track
- act_track_size = 200000000/f.cell_size;
+ if (pos - lastpos == 16) a1count++;
+ else a1count = 1;
}
+ else a1count = 1;
- // Get the bytes from the cells
- extract_track_from_bitstream(bitstream, act_track_size, trackdata);
+ lastpos = pos;
+ }
+ if (a1count == 3)
+ {
+ marks++;
+ // Found a header
+ headerbytes = 6;
+ // Create GAP0 at the beginning or GAP3 later
+ if (tpos==0)
+ for (int i=0; i < 40; i++) trackdata[tpos++] = gapbytes;
+ else
+ for (int i=0; i < 24; i++) trackdata[tpos++] = gapbytes;
+
+ // IDAM sync bytes
+ tpos += 10;
+ state = HEADER;
+ trackdata[tpos++] = 0xa1;
+ trackdata[tpos++] = 0xa1;
+ trackdata[tpos++] = 0xa1;
+ }
+ break;
- // Do we get a reasonable track format?
- // Test this for track 0 only (we won't be able to change that
- // for each single track, anyway)
+ case HEADER:
+ if (pos - lastpos == 16)
+ {
+ // Transfer header bytes
+ trackdata[tpos] = get_data_from_encoding(shift_reg);
+
+ if (headerbytes == 3) current_sector = trackdata[tpos];
+ tpos++;
+
+ if (headerbytes == 0)
+ {
+ state = (encoding==floppy_image::MFM)? MFMDAM : FMDAM;
+ a1count = 0;
+ }
+ else headerbytes--;
+ lastpos = pos;
+ }
+ break;
- if ((track == 0) && (head == 0) && (option < 2))
+ case FMDAM:
+ if (shift_reg == 0xf56a || shift_reg == 0xf56f)
+ {
+ if (pos - lastpos > 400)
{
- // for (int i=0; i < 256; i++)
- // logerror("%02x%c", trackdata[i], (i%16==15)? '\n' : ' ');
+ // Too far apart
+ state = FMIDAM;
+ // Abort this sector, skip to the next
+ tpos += 321;
+ a1count = 1;
+ break;
+ }
+ marks++;
+ // Add GAP2 and DAM sync
+ for (int i=0; i < 11; i++) trackdata[tpos++] = 0xff;
+ tpos += 6;
+ state = DATA;
+ databytes = 257;
+ trackdata[tpos++] = (shift_reg==0xf56a)? 0xf8 : 0xfb;
+ lastpos = pos;
+ sector[current_sector] = tpos;
+ }
+ break;
- goodformat = (find_start(trackdata, act_track_size, f.encoding) != -1);
- if (!goodformat) logerror("ti99_dsk: Format %d (TDF) not suitable for saving\n", type);
+ case MFMDAM:
+ // Count three subsequent A1
+ if (shift_reg == 0x4489)
+ {
+ if (pos - lastpos > 560)
+ {
+ // Too far apart
+ state = MFMIDAM;
+ // Abort this sector, skip to the next
+ tpos += 320;
+ a1count = 1;
+ break;
}
- if (goodformat)
+ if (lastpos > 0)
{
- // Pad the trackdata according to the TDF spec
- for (int i = act_track_size; i < f.track_size; i++)
- trackdata[i] = f.gapbytes;
+ if (pos - lastpos == 16) a1count++;
+ else a1count = 1;
+ }
+ else a1count = 1;
- // Save to the file
- io_generic_write(io, trackdata, get_image_offset(f, head, track), f.track_size);
+ lastpos = pos;
+ }
+ if (a1count == 3)
+ {
+ marks++;
+ // Add GAP2 and DAM sync
+ for (int i=0; i < 22; i++) trackdata[tpos++] = gapbytes;
+ tpos += 12;
+ trackdata[tpos++] = 0xa1;
+ trackdata[tpos++] = 0xa1;
+ trackdata[tpos++] = 0xa1;
+ state = DATA;
+ databytes = 258;
+ sector[current_sector] = tpos+1;
+ }
+ break;
+
+ case DATA:
+ if (pos - lastpos == 16)
+ {
+ // Ident byte
+ trackdata[tpos++] = get_data_from_encoding(shift_reg);
+ if (databytes > 0) databytes--;
+ else
+ {
+ state = (encoding==floppy_image::MFM)? MFMIDAM : FMIDAM;
+ a1count = 0;
}
+ lastpos = pos;
}
+ break;
}
- if (!goodformat)
+ pos++;
+ }
+ return marks;
+}
+
+UINT8 ti99_floppy_format::get_data_from_encoding(UINT16 raw)
+{
+ return (raw & 0x4000 ? 0x80 : 0x00) |
+ (raw & 0x1000 ? 0x40 : 0x00) |
+ (raw & 0x0400 ? 0x20 : 0x00) |
+ (raw & 0x0100 ? 0x10 : 0x00) |
+ (raw & 0x0040 ? 0x08 : 0x00) |
+ (raw & 0x0010 ? 0x04 : 0x00) |
+ (raw & 0x0004 ? 0x02 : 0x00) |
+ (raw & 0x0001 ? 0x01 : 0x00);
+}
+
+/*
+ Sector Dump Format
+ ------------------
+ The Sector Dump Format is also known as v9t9 format (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 sectors
+ are ordered by their logical number as used in the TI file system.
+
+ In this implementation, the sector dump format is just a kind of
+ wd177x_format with minor customizations, which allows us to keep the code
+ small. The difference is the logical ordering of tracks and sectors.
+
+ The TI file system orders all tracks on side 0 as going inwards,
+ and then all tracks on side 1 going outwards.
+
+ 00 01 02 03 ... 38 39 side 0
+ 79 78 77 76 ... 41 40 side 1
+
+ The SDF format stores the tracks and their sectors in logical order
+ 00 01 02 03 ... 38 39 [40 41 ... 79]
+
+ There is also a variant of the SDF which adds three sectors at the end
+ containing a map of bad sectors. This was introduced by a tool to read
+ real TI floppy disks on a PC. As other emulators tolerate this additional
+ bad sector map, we just check whether there are 3 more sectors and ignore
+ them.
+*/
+const char *ti99_sdf_format::name() const
+{
+ return "sdf";
+}
+
+const char *ti99_sdf_format::description() const
+{
+ return "TI99 sector dump floppy disk image";
+}
+
+const char *ti99_sdf_format::extensions() const
+{
+ return "dsk";
+}
+
+int ti99_sdf_format::identify(io_generic *io, UINT32 form_factor)
+{
+ UINT64 file_size = io_generic_size(io);
+ int vote = 0;
+
+ // Adding support for another sector image format which adds 768 bytes
+ // as a bad sector map
+ if ((file_size / SECTOR_SIZE) % 10 == 3)
+ {
+ if (TRACE) logerror("ti99_dsk: Stripping map of bad sectors at image end\n");
+ file_size -= SECTOR_SIZE*3;
+ }
+
+ // Formats with 9 or 18 sectors per track (multiples of SSSD)
+ // 40-track formats: SSSD/9/40 (1), DSSD/9/40 (2), SSDD/18/40 (2), DSDD/18/40 (4)
+ // 80-track formats: SSSD/9/80 (2), DSSD/9/80 (4), SSDD/18/80 (4), DSDD/18/80,(8)
+ // High density: DSQD/36/80 (16) (experimental)
+
+ // All formats apply for 5.25" and 3.5" form factors
+
+ // Default formats (when the geometry cannot be determined from the VIB)
+ // (1) -> SSSD
+ // (2) -> DSSD
+ // (4) -> DSDD
+ // (8) -> DSDD80
+ // (16) -> DSQD
+
+ if ((file_size % 92160)==0)
+ {
+ int multiple = file_size/92160;
+ if ((multiple == 1) || (multiple == 2) || (multiple == 4) || (multiple == 8) || (multiple == 16)) vote = 50;
+ }
+
+ // Formats with 16 sectors per track (rare, SSDD/16/40, DSDD/16/40).
+ if (file_size == 163840) vote = 50;
+ if (file_size == 327680) vote = 50;
+
+ if (vote > 0)
+ {
+ // Read first sector (Volume Information Block)
+ ti99vib vib;
+ io_generic_read(io, &vib, 0, sizeof(ti99vib));
+
+ // Check from contents
+ if ((vib.id[0]=='D')&&(vib.id[1]=='S')&&(vib.id[2]=='K'))
{
- // If 0 then next is 1 and fallback is 0.
- // If 1 then next is 0 and fallback is 1.
- type = (type+1) % 2;
+ if (TRACE) logerror("ti99_dsk: Found formatted SDF disk medium\n");
+ vote = 100;
}
else
- return true;
+ {
+ if (TRACE) logerror("ti99_dsk: No valid VIB found; disk may be unformatted\n");
+ }
}
+ else if (TRACE) logerror("ti99_dsk: Disk image is not a SDF image\n");
+ return vote;
+}
- // We won't reach this line
- return true;
+void ti99_sdf_format::determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads)
+{
+ UINT64 file_size = io_generic_size(io);
+ ti99vib vib;
+
+ cell_size = 0;
+ sector_count = 0;
+ heads = 2;
+
+ bool have_vib = false;
+
+ // See above
+ if ((file_size / SECTOR_SIZE) % 10 == 3) file_size -= SECTOR_SIZE*3;
+
+ // Read first sector
+ io_generic_read(io, &vib, 0, sizeof(ti99vib));
+
+ // Check from contents
+ if ((vib.id[0]=='D')&&(vib.id[1]=='S')&&(vib.id[2]=='K'))
+ {
+ sector_count = vib.secspertrack;
+ heads = vib.sides;
+
+ // Find out more about the density. SSDD happens to be the same size
+ // as DSSD in the sector dump format, so we need to ask the
+ // VIB if available. Otherwise, we assume that we have a DSSD medium.
+ if (vib.density < 2) cell_size = 4000;
+ else
+ {
+ if (vib.density < 4) cell_size = 2000;
+ else cell_size = 1000;
+ }
+ if (TRACE) logerror("ti99_dsk: VIB says that this disk is %s density with %d sectors per track, %d tracks, and %d heads\n", (cell_size==4000)? "single": ((cell_size==2000)? "double" : "high"), sector_count, vib.tracksperside, heads);
+ have_vib = true;
+ }
+
+ // We're also checking the size of the image
+ int cell_size1 = 0;
+ int sector_count1 = 0;
+
+ // 90 KiB -> SSSD, 9 sect, FM
+ // 160 KiB -> SSDD, 16 sect, MFM
+ // 180 KiB -> DSSD, 9 sect, FM
+ // 320 KiB -> DSDD, 16 sect, MFM
+ // 360 KiB -> DSDD, 18 sect, MFM
+ // 720 KiB -> DSDD, 18 sect, MFM, 80 tracks
+ // 1440 KiB-> DSQD, 36 sect, MFM, 80 tracks
+
+ if ((file_size == 163840) || (file_size == 327680))
+ {
+ cell_size1 = 2000;
+ sector_count1 = 16;
+ }
+ else
+ {
+ if (file_size < 300000) cell_size1 = 4000;
+ else
+ {
+ if (file_size < 1000000) cell_size1 = 2000;
+ else cell_size1 = 1000;
+ }
+ sector_count1 = 36000 / cell_size1;
+ }
+
+ if (have_vib)
+ {
+ if (sector_count == 16 && sector_count1 == 18)
+ {
+ logerror("ti99_dsk: Warning: Invalid 16-sector format. Assuming 18 sectors.\n");
+ sector_count = 18;
+ }
+ else
+ {
+ if (heads == 2 && ((cell_size1 != cell_size) || (sector_count1 != sector_count)))
+ logerror("ti99_dsk: Warning: Disk image size does not correspond with format information in VIB.\n");
+ }
+ }
+ else
+ {
+ heads = (file_size < 100000)? 1 : 2; // for SSSD
+ cell_size = cell_size1;
+ sector_count = sector_count1;
+ }
+}
+
+int ti99_sdf_format::get_track_size(int cell_size, int sector_count)
+{
+ return sector_count * SECTOR_SIZE;
}
/*
- Supported formats for the track dump format.
- There are only two formats: FM double-sided and MFM double-sided.
+ Load a SDF image track. Essentially, we want to end up in the same
+ kind of track image as with the TDF, so the easiest thing is to produce
+ a TDF image track from the sectors and process them as if it came from TDF.
*/
-const ti99_tdf_format::format ti99_tdf_format::formats[] =
-{
- { // 180K 5.25" double sided single density
- floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
- 4000, 9, 40, 2, 3253, 0xff
- },
- { // 360K 5.25" double sided double density
- floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
- 2000, 18, 40, 2, 6872, 0x4e
- },
- { 0 }
-};
+void ti99_sdf_format::load_track(io_generic *io, UINT8 *trackdata, int head, int track, int sectorcount, int trackcount, int cellsize)
+{
+ bool fm = (cellsize==4000);
+ int tracksize = sectorcount * SECTOR_SIZE;
+
+ // Calculate the track offset from the beginning of the image file
+ int logicaltrack = head * trackcount;
+ logicaltrack += ((head&1)==0)? track : (trackcount - 1 - track);
+
+ // Interleave and skew
+ int interleave = fm? 4 : 5;
+ int skew = fm? 6 : 0;
+
+ int secsize = fm? 334 : 340;
+ int secoff = fm? 33 : 58;
+ int position = 0;
+ int count = 0;
+
+ memset(trackdata, 0x00, 9216);
+
+ int secno = 0;
+ secno = (track * skew) % sectorcount;
+
+ // Gap 1
+ int gap1 = fm? 16 : 40;
+ for (int i=0; i < gap1; i++) trackdata[position+i] = fm? 0x00 : 0x4e;
+
+ for (int i=0; i < sectorcount; i++)
+ {
+ position = secno * secsize + gap1;
+ // Sync
+ count = fm? 6 : 10;
+ while (count-- > 0) trackdata[position++] = 0x00;
+
+ if (!fm)
+ {
+ trackdata[position++] = 0xa1;
+ trackdata[position++] = 0xa1;
+ trackdata[position++] = 0xa1;
+ }
+ trackdata[position++] = 0xfe; // IDAM / ident
+
+ // Header
+ trackdata[position++] = track;
+ trackdata[position++] = head;
+ trackdata[position++] = i;
+ trackdata[position++] = 1;
+
+ trackdata[position++] = 0xf7;
+ trackdata[position++] = 0xf7;
+
+ // Gap 2
+ count = fm? 11 : 22;
+ while (count-- > 0) trackdata[position++] = fm? 0xff : 0x4e;
+
+ // Sync
+ count = fm? 6 : 12;
+ while (count-- > 0) trackdata[position++] = 0x00;
+
+ if (!fm)
+ {
+ trackdata[position++] = 0xa1;
+ trackdata[position++] = 0xa1;
+ trackdata[position++] = 0xa1;
+ }
+ trackdata[position++] = 0xfb;
+
+ io_generic_read(io, trackdata + position, logicaltrack * tracksize + i*SECTOR_SIZE, SECTOR_SIZE);
+
+ position += SECTOR_SIZE;
+ trackdata[position++] = 0xf7;
+ trackdata[position++] = 0xf7;
+
+ // Gap 3
+ count = fm? 45 : 24;
+ while (count-- > 0) trackdata[position++] = fm? 0xff : 0x4e;
+ secno = (secno + interleave) % sectorcount;
+ }
+
+ // Gap 4
+ count = fm? 231 : 712;
+ position = sectorcount * secsize + gap1;
+ while (count-- > 0) trackdata[position++] = fm? 0xff : 0x4e;
+
+ // if (head==0 && track==0) showtrack(trackdata, 9216);
+}
+
+/*
+ For debugging. Outputs the byte array in a xxd-like way.
+*/
+void ti99_floppy_format::showtrack(UINT8* trackdata, int length)
+{
+ for (int i=0; i < length; i+=16)
+ {
+ logerror("%07x: ", i);
+ for (int j=0; j < 16; j++)
+ {
+ logerror("%02x", trackdata[i+j]);
+ if ((j&1)==1) logerror(" ");
+ }
+ logerror(" ");
+ for (int j=0; j < 16; j++)
+ {
+ if (trackdata[i+j] >= 32 && trackdata[i+j]<128) logerror("%c", trackdata[i+j]);
+ else logerror(".");
+ }
+ logerror("\n");
+ }
+}
+
+/*
+ Write the data to the disk. We have a list of sector positions, so we
+ just need to go through that list and save each sector in the track data.
+*/
+void ti99_sdf_format::write_track(io_generic *io, UINT8 *trackdata, int *sector, int track, int head, int maxsect, int maxtrack, int numbytes)
+{
+ int logicaltrack = head * maxtrack;
+ logicaltrack += ((head&1)==0)? track : (maxtrack - 1 - track);
+ int trackoffset = logicaltrack * maxsect * SECTOR_SIZE;
+
+// if (head==0 && track==0) showtrack(trackdata, 9216);
+
+ for (int i=0; i < maxsect; i++)
+ io_generic_write(io, trackdata + sector[i], trackoffset + i * SECTOR_SIZE, SECTOR_SIZE);
+}
+
+const floppy_format_type FLOPPY_TI99_SDF_FORMAT = &floppy_image_format_creator<ti99_sdf_format>;
+
+/*
+ Track Dump Format
+ -----------------
+ 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 the data values of the
+ 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.
+
+ Different to earlier implementations of the TDF format, we stay much
+ closer to the specification. Deviations (like different gap sizes) are
+ automatically rectified. It does not make sense to have a format that
+ pretends to be an almost precise track image and then fail to load in
+ other emulations because of small deviations.
+ For precise track images there are better suited formats.
+
+ For FM recording, each track is exactly 3253 bytes long in this format.
+ For MFM recording, track length is 6872 bytes.
+
+ Accordingly, we get a multiple of these values as the image length.
+ There are no single-sided images (when single-sided, the upper half of
+ the image is empty).
+
+ DSSD: 260240 bytes
+ DSDD: 549760 bytes
+
+ We do not support other geometries in this format. One exception: We accept
+ images of double size which may be generated when a 40-track disk is
+ modified in an 80-track drive. This will automatically cause the creation
+ of an 80-track image.
+
+ Tracks are stored from outside to inside of head 0, then outside to inside of head 1:
+
+ (Head,Track): (0,0) (0,1) (0,2) ... (0,38) (0,39) (1,0) (1,1) ... (1,39)
+*/
+const char *ti99_tdf_format::name() const
+{
+ return "tdf";
+}
+
+const char *ti99_tdf_format::description() const
+{
+ return "TI99 track dump floppy disk image";
+}
+
+const char *ti99_tdf_format::extensions() const
+{
+ return "dsk,dtk";
+}
+
+/*
+ Determine whether the image file can be interpreted as a track dump
+*/
+int ti99_tdf_format::identify(io_generic *io, UINT32 form_factor)
+{
+ UINT64 file_size = io_generic_size(io);
+ int vote = 0;
+ UINT8 trackdata[2000];
+
+ // Do we have a plausible image size? From the size alone we only give a 50 percent vote.
+ if ((file_size == 260240) || (file_size == 549760) || (file_size == 1099520))
+ vote = 50;
+
+ if (vote > 0)
+ {
+ if (TRACE) logerror("ti99_dsk: Image looks like a TDF image\n");
+
+ // Get some bytes from track 0
+ io_generic_read(io, trackdata, 0, 2000);
+
+ if (check_for_address_marks(trackdata, floppy_image::FM)==true || check_for_address_marks(trackdata, floppy_image::MFM)==true) vote=100;
+ else
+ {
+ if (TRACE) logerror("ti99_dsk: Image does not comply with TDF; may be broken or unformatted\n");
+ }
+ }
+ else if (TRACE) logerror("ti99_dsk: Disk image is not a TDF image\n");
+ return vote;
+}
+
+/*
+ Find the proper format for a given image file. We determine the cell size,
+ but we do not care about the sector size (only needed by the SDF converter).
+*/
+void ti99_tdf_format::determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads)
+{
+ UINT64 file_size = io_generic_size(io);
+ heads = 2; // TDF only supports two-sided recordings
+
+ if (file_size % get_track_size(2000, 0)==0) cell_size = 2000;
+ else if (file_size % get_track_size(4000, 0)==0) cell_size = 4000;
+
+ // We could check for the content, but if we find that the content is
+ // FM and the size implies MFM, the calculated track count will be wrong.
+}
+
+/*
+ For TDF this just amounts to loading the track from the image file.
+*/
+void ti99_tdf_format::load_track(io_generic *io, UINT8 *trackdata, int head, int track, int sectorcount, int trackcount, int cellsize)
+{
+ int offset = ((trackcount * head) + track) * get_track_size(cellsize, 0);
+ io_generic_read(io, trackdata, offset, get_track_size(cellsize, 0));
+}
+
+/*
+ Also here, we just need to write the complete track on the image file.
+*/
+void ti99_tdf_format::write_track(io_generic *io, UINT8 *trackdata, int *sector, int track, int head, int maxsect, int maxtrack, int numbytes)
+{
+ int offset = ((maxtrack * head) + track) * numbytes;
+ io_generic_write(io, trackdata, offset, numbytes);
+}
+
+int ti99_tdf_format::get_track_size(int cell_size, int sector_count)
+{
+ if (cell_size == 4000) return 3253;
+ if (cell_size == 2000) return 6872;
+ return 0;
+}
const floppy_format_type FLOPPY_TI99_TDF_FORMAT = &floppy_image_format_creator<ti99_tdf_format>;
diff --git a/src/lib/formats/ti99_dsk.h b/src/lib/formats/ti99_dsk.h
index 9364a991e75..64498f01a8e 100644
--- a/src/lib/formats/ti99_dsk.h
+++ b/src/lib/formats/ti99_dsk.h
@@ -4,7 +4,7 @@
TI99 and Geneve disk images
- Michael Zapf, Feb 2014
+ Michael Zapf, Sept 2014
*********************************************************************/
@@ -14,31 +14,57 @@
#include "flopimg.h"
#include "wd177x_dsk.h"
+class ti99_floppy_format : public floppy_image_format_t
+{
+public:
+ bool supports_save() const { return true; }
+ bool load(io_generic *io, UINT32 form_factor, floppy_image *image);
+ bool save(io_generic *io, floppy_image *image);
+
+protected:
+ int decode_bitstream(const UINT8 *bitstream, UINT8 *trackdata, int *sector, int cell_count, int encoding, UINT8 gapbytes, int track_size);
+ UINT8 get_data_from_encoding(UINT16 raw);
+
+ virtual int min_heads() =0;
+
+ virtual void determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads) =0;
+ virtual int get_track_size(int cell_size, int sector_count) =0;
+ virtual void load_track(io_generic *io, UINT8 *trackdata, int head, int track, int sectorcount, int trackcount, int cellsize) =0;
+ virtual void write_track(io_generic *io, UINT8 *trackdata, int *sector, int track, int head, int maxsect, int maxtrack, int numbytes) =0;
+
+ int get_encoding(int cell_size);
+ int get_track_size(int cell_size);
+
+ void generate_track_fm(int track, int head, int cell_size, UINT8* trackdata, floppy_image *image);
+ void generate_track_mfm(int track, int head, int cell_size, UINT8* trackdata, floppy_image *image);
+
+ bool check_for_address_marks(UINT8* trackdata, int encoding);
+
+ // Debugging
+ void showtrack(UINT8* trackdata, int length);
+};
+
/*
Modern implementation of the sector dump format.
*/
-class ti99_sdf_format : public wd177x_format
+class ti99_sdf_format : public ti99_floppy_format
{
public:
- ti99_sdf_format(): wd177x_format(formats) { }
-
int identify(io_generic *io, UINT32 form_factor);
-
const char *name() const;
const char *description() const;
const char *extensions() const;
- bool supports_save() const;
private:
- static const format formats[];
- floppy_image_format_t::desc_e* get_desc_fm(const format &f, int &current_size, int &end_gap_index);
- floppy_image_format_t::desc_e* get_desc_mfm(const format &f, int &current_size, int &end_gap_index);
+ void determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads);
+ int get_track_size(int cell_size, int sector_count);
+ void write_track(io_generic *io, UINT8 *trackdata, int *sector, int track, int head, int maxsect, int maxtrack, int numbytes);
+ void load_track(io_generic *io, UINT8 *trackdata, int head, int track, int sectorcount, int trackcount, int cellsize);
- int get_format_param(io_generic *io);
- int get_image_offset(const format &f, int head, int track);
- int find_size(io_generic *io, UINT32 form_factor);
+ // This format supports single-sided images
+ int min_heads() { return 1; }
- typedef struct ti99_vib
+ struct ti99vib
{
char name[10]; // volume name (10 characters, pad with spaces)
UINT8 totsecsMSB; // disk length in sectors (big-endian) (usually 360, 720 or 1440)
@@ -52,7 +78,7 @@ private:
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.)
- } vib_t;
+ };
};
extern const floppy_format_type FLOPPY_TI99_SDF_FORMAT;
@@ -60,47 +86,22 @@ extern const floppy_format_type FLOPPY_TI99_SDF_FORMAT;
/*
Modern implementation of the track dump format.
*/
-class ti99_tdf_format : public floppy_image_format_t
+class ti99_tdf_format : public ti99_floppy_format
{
public:
-
int identify(io_generic *io, UINT32 form_factor);
-
- bool load(io_generic *io, UINT32 form_factor, floppy_image *image);
- bool save(io_generic *io, floppy_image *image);
-
const char *name() const;
const char *description() const;
const char *extensions() const;
- bool supports_save() const;
private:
+ void determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads);
+ void load_track(io_generic *io, UINT8 *trackdata, int head, int track, int sectorcount, int trackcount, int cellsize);
+ void write_track(io_generic *io, UINT8 *trackdata, int *sector, int track, int head, int maxsect, int maxtrack, int numbytes);
+ int get_track_size(int cell_size, int sector_count);
- struct format {
- UINT32 form_factor; // See floppy_image for possible values
- UINT32 variant; // See floppy_image for possible values
- UINT32 encoding; // See floppy_image for possible values
- int cell_size; // See floppy_image_format_t for details
- int sector_count;
- int track_count;
- int head_count;
- int track_size;
- UINT8 gapbytes;
- };
-
- static const format formats[];
-
- int get_format_param(io_generic *io);
- int get_image_offset(const format &f, int head, int track);
- int find_size(io_generic *io, UINT32 form_factor);
-
- void generate_track_fm(int track, int head, int cell_size, UINT8* trackdata, floppy_image *image);
- void generate_track_mfm(int track, int head, int cell_size, UINT8* trackdata, floppy_image *image);
-
- int find_start(UINT8* trackdata, int track_size, int encoding);
- bool has_byteseq(UINT8* trackdata, int track_size, UINT8 byte, int pos, int count);
-
- void extract_track_from_bitstream(const UINT8 *bitstream, int track_size, UINT8 *trackdata);
+ // This format only supports double-sided images
+ int min_heads() { return 2; }
};
extern const floppy_format_type FLOPPY_TI99_TDF_FORMAT;