diff options
| author | 2014-09-26 18:35:07 +0000 | |
|---|---|---|
| committer | 2014-09-26 18:35:07 +0000 | |
| commit | 437d5c500867b482350db534bb7f15558b4c03af (patch) | |
| tree | ea063f0dd5c8e728a12174a31bed3cd31db80823 /src/lib | |
| parent | c6d43e680ae5684e73af41d6479734afed8e5495 (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.c | 1397 | ||||
| -rw-r--r-- | src/lib/formats/ti99_dsk.h | 93 |
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 ¤t_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 ¤t_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 ¤t_size, int &end_gap_index); - floppy_image_format_t::desc_e* get_desc_mfm(const format &f, int ¤t_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; |
