From aa7f94d246d7dc00f0a5be2adc77076d3d841e8d Mon Sep 17 00:00:00 2001 From: Michael Zapf Date: Sun, 9 Mar 2014 23:04:16 +0000 Subject: [PATCH] (MESS) Floppy modernization, part 2. --- src/lib/formats/ti99_dsk.c | 1151 ++++++++++++++++++++++++------- src/lib/formats/ti99_dsk.h | 76 +- src/mess/machine/ti99/bwg.c | 668 ++++++++++++++++-- src/mess/machine/ti99/bwg.h | 131 +++- src/mess/machine/ti99/peribox.c | 2 + src/mess/machine/ti99/ti_fdc.c | 5 +- 6 files changed, 1730 insertions(+), 303 deletions(-) diff --git a/src/lib/formats/ti99_dsk.c b/src/lib/formats/ti99_dsk.c index de6070b4c70..c3b2cfc2f34 100644 --- a/src/lib/formats/ti99_dsk.c +++ b/src/lib/formats/ti99_dsk.c @@ -1,28 +1,19 @@ +// license:BSD-3-Clause +// copyright-holders:Michael Zapf /********************************************************************* * * formats/ti99_dsk.c * - * TI99 and Geneve disk images - * Sector Dump and Track Dump format + * TI-99 family disk images * * used by TI-99/4, TI-99/4A, TI-99/8, SGCPU ("TI-99/4P"), and Geneve * - * The Sector Dump Format is also known as v9t9 (named after the first TI - * emulator to use this format). It is a contiguous sequence of sector contents - * without track data. The first sector of the disk is located at the start of - * the image, while the last sector is at its end. + * Sector Dump Format, aka "v9t9" format + * customized wd177x_format + * no track data * - * 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. - * - * 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. + * Track Dump Format, aka "pc99" format + * contains all track information, but no clock patterns * * Both formats allow for a broad range of medium sizes. All sectors are 256 * bytes long. The most common formats are 9 sectors per track, single-sided, @@ -31,7 +22,892 @@ * DSDD). There are rare occurances of 8/16 sectors/track * (prototypical TI double-density controller) and 35 track media. Newer * controllers and ROMs allow for up to 36 sectors per track and 80 tracks on - * both sides, which is 2,88 MiB (DSHD80). + * both sides, which is 1,44 MiB (DSHD80). + * + * The second half of this file contains the legacy implementation. + * + * Michael Zapf, Feb 2014 + * + ********************************************************************/ + +#include "emu.h" +#include +#include +#include + +#include "imageutl.h" +#include "ti99_dsk.h" + +#define SECTOR_SIZE 256 + +// 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] + + 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"; +} + +bool ti99_sdf_format::supports_save() const +{ + return true; +} + +int ti99_sdf_format::identify(io_generic *io, UINT32 form_factor) +{ + UINT64 file_size = io_generic_size(io); + int vote = 0; + + vib_t vib; + + // 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 + 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; + + // Formats with 16 sectors per track (rare) + if (((file_size % 163840)==0) + && ( (file_size/163840==1) + || (file_size/163840==2))) vote = 50; + + if (vote > 0) + { + // 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"); + } + } + 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; + + // 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"); + } + + for (int i=0; formats[i].form_factor != 0; i++) + { + if (formats[i].form_factor != form_factor) + { + 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); + + 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 (TRACE) logerror("ti99_dsk: format %d matches\n", i); + return i; + } + else + { + if (TRACE) logerror("ti99_dsk: format %d matches by size, but encoding does not match\n", i); + } + } + } + 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::FM, + 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, 6, 6 }, // 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; + +/* + 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) +{ + UINT64 file_size = io_generic_size(io); + int vote = 0; + UINT8 trackdata[500]; + + // Formats with 9 or 18 sectors per track + if ((file_size == 260240) || (file_size == 549760)) + 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, 500); + + 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"); + } + } + 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); + + for (int i=0; formats[i].form_factor != 0; i++) + { + if (formats[i].form_factor != form_factor) + { + 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; +} + +/* + Find out where the address marks are. Unfortunately, the TDF offers no + clock signals. So we are looking for this pattern: + + FM: (00)*6, FE, (xx)*6, (FF)*11, (00)*6, FB + MFM: (00)*10, (a1)*3, fe, (xx)*6, (4e)*22, (00)*12 + + 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; + + // 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) + { + 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; + } + pos1 += 22; + if (!has_byteseq(trackdata, track_size, 0x00, pos1, 12)) + { + pos++; + continue; + } + } + 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) +{ + int track_size_cells = 200000000/cell_size; + UINT32 *buffer = global_alloc_array_clear(UINT32, 200000000/cell_size); + + // The TDF has a long track lead-out that makes the track length sum up + // to 3253; this is too long for the number of cells in the real track. + // This was either an error when that format was defined, + // or it is due to the fact that when reading a track via + // the controller, after the track has been read, the controller still + // delivers some FF values until it times out. + + // Accordingly, we limit the track size to cell_number / 16, + // which means 3125 for FM + // This also means that Gap 4 (lead-out) is not 231 bytes long but only 103 bytes + + int track_size = track_size_cells / 16; + + int offset = 0; + short crc1, crc2, found_crc; + + int start = find_start(trackdata, track_size, floppy_image::FM); + bool broken = false; + + if (start == -1) + { + 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; + } + + // Found a track; we now know where the address marks are: + // (start is positioned at the pre-id gap) + // IDAM: start + 6 + n*334 + // DAM: start + 30 + n*334 + // and the CRCs are at + // CRC1: start + 11 + n*334 + // CRC2: start + 287 + n*334 + // If the CRCs are F7F7, we recalculate them. + for (int i=0; i < track_size; i++) + { + if (((i-start-6)%334==0) && (i < start + 9*334)) + { + // IDAM + raw_w(buffer, offset, 16, 0xf57e); + } + else + { + if (((i-start-30)%334==0) && (i < start + 9*334)) + { + // DAM + raw_w(buffer, offset, 16, 0xf56f); + } + else + { + if (((i-start-11)%334==0) && (i < start + 9*334)) + { + // CRC1 + crc1 = ccitt_crc16(0xffff, &trackdata[i-5], 5); + found_crc = (trackdata[i]<<8 | trackdata[i+1]); + if ((found_crc & 0xffff) == 0xf7f7) + { + // PC99 format: no real CRC; replace it + // 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) + { + logerror("ti99_dsk: Warning: CRC1 does not match (track=%d, head=%d). Found = %04x, calc = %04x\n", track, head, found_crc& 0xffff, crc1& 0xffff); + } + } + else + { + if (((i-start-287)%334==0) && (i < start + 9*334)) + { + // CRC2 + crc2 = ccitt_crc16(0xffff, &trackdata[i-SECTOR_SIZE-1], SECTOR_SIZE+1); + found_crc = (trackdata[i]<<8 | trackdata[i+1]); + if ((found_crc & 0xffff) == 0xf7f7) + { + // PC99 format: no real CRC; replace it + // logerror("Warning: PC99 format using pseudo CRC2; replace by %04x\n", crc2); + trackdata[i] = (crc2 >> 8) & 0xff; + trackdata[i+1] = crc2 & 0xff; + found_crc = crc2; + } + if ((crc2 != found_crc) && !broken) + { + logerror("ti99_dsk: Warning: CRC2 does not match (track=%d, head=%d). Found = %04x, calc = %04x\n", track, head, found_crc& 0xffff, crc2& 0xffff); + } + } + } + fm_w(buffer, offset, 8, trackdata[i]); + } + } + } + + generate_track_from_levels(track, head, buffer, track_size_cells, 0, image); + + global_free(buffer); +} + +void ti99_tdf_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); + + // See above + // We limit the track size to cell_number / 16, which means 6250 for MFM + // 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; + + if (start == -1) + { + 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; + } + + // Found a track; we now know where the address marks are: + // (start is positioned at the pre-id gap) + // IDAM: start + 10 + n*340 (starting at first a1) + // DAM: start + 54 + n*340 (starting at first a1) + // and the CRCs are at + // CRC1: start + 18 + n*340 + // CRC2: start + 314 + n*334 + + for (int i=0; i < track_size; i++) + { + if (((i-start-10)%340==0) && (i < start + 18*340)) + { + // IDAM + for (int j=0; j < 3; j++) + raw_w(buffer, offset, 16, 0x4489); // 3 times A1 + mfm_w(buffer, offset, 8, 0xfe); + i += 3; + } + else + { + if (((i-start-54)%340==0) && (i < start + 18*340)) + { + // DAM + for (int j=0; j < 3; j++) + raw_w(buffer, offset, 16, 0x4489); // 3 times A1 + mfm_w(buffer, offset, 8, 0xfb); + i += 3; + } + else + { + if (((i-start-18)%340==0) && (i < start + 18*340)) + { + // CRC1 + // The CRC also covers the three A1 bytes! + crc1 = ccitt_crc16(0xffff, &trackdata[i-8], 8); + found_crc = (trackdata[i]<<8 | trackdata[i+1]); + if ((found_crc & 0xffff) == 0xf7f7) + { + // PC99 format: pseudo CRC; replace it + // 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) + { + logerror("ti99_dsk: Warning: CRC1 does not match (track=%d, head=%d). Found = %04x, calc = %04x\n", track, head, found_crc & 0xffff, crc1& 0xffff); + } + } + else + { + if ((i > 340) && ((i-start-314)%340==0) && (i < start + 18*340)) + { + // CRC2 + crc2 = ccitt_crc16(0xffff, &trackdata[i-SECTOR_SIZE-4], SECTOR_SIZE+4); + found_crc = (trackdata[i]<<8 | trackdata[i+1]); + if ((found_crc & 0xffff) == 0xf7f7) + { + // PC99 format: pseudo CRC; replace it + // logerror("Warning: PC99 format using pseudo CRC2; replace by %04x\n", crc2); + trackdata[i] = (crc2 >> 8) & 0xff; + trackdata[i+1] = crc2 & 0xff; + found_crc = crc2; + } + if (crc2 != found_crc && !broken) + { + logerror("ti99_dsk: Warning: CRC2 does not match (track=%d, head=%d). Found = %04x, calc = %04x\n", track, head, found_crc& 0xffff, crc2& 0xffff); + } + } + } + mfm_w(buffer, offset, 8, trackdata[i]); + } + } + } + + generate_track_from_levels(track, head, buffer, track_size_cells, 0, image); + + global_free(buffer); +} + +/* + 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. +*/ +void ti99_tdf_format::extract_track_from_bitstream(const UINT8 *bitstream, int track_size, UINT8 *trackdata) +{ + int pos = 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]; + + // 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 + + int type = (io_generic_size(io) > 500000)? 1 : 0; + + // 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 + + bool goodformat = true; + for (int option=0; option < 3; option++) + { + const format &f = formats[type]; + if (TRACE) logerror("ti99_dsk: Trying format %d (TDF) for saving\n", type); + goodformat = true; + + // We expect a bitstream of length 50000 for FM and 100000 for MFM + for(int head=0; (head < 2) && goodformat; head++) + { + for(int track=0; (track < 40) && goodformat; track++) + { + // Retrieve the cells from the flux sequence + generate_bitstream_from_track(track, head, f.cell_size, bitstream, act_track_size, image); + + // Maybe the track has become longer due to moving splices + if (act_track_size > 200000000/f.cell_size) + { + // Truncate the track + act_track_size = 200000000/f.cell_size; + } + + // Get the bytes from the cells + extract_track_from_bitstream(bitstream, act_track_size, trackdata); + + // 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) + + if ((track == 0) && (head == 0) && (option < 2)) + { + // for (int i=0; i < 256; i++) + // logerror("%02x%c", trackdata[i], (i%16==15)? '\n' : ' '); + + goodformat = (find_start(trackdata, act_track_size, f.encoding) != -1); + if (!goodformat) logerror("ti99_dsk: Format %d (TDF) not suitable for saving\n", type); + } + + if (goodformat) + { + // Pad the trackdata according to the TDF spec + for (int i = act_track_size; i < f.track_size; i++) + trackdata[i] = f.gapbytes; + + // Save to the file + io_generic_write(io, trackdata, get_image_offset(f, head, track), f.track_size); + } + } + } + if (!goodformat) + { + // If 0 then next is 1 and fallback is 0. + // If 1 then next is 0 and fallback is 1. + type = (type+1) % 2; + } + else + return true; + } + + // We won't reach this line + return true; +} + +/* + Supported formats for the track dump format. + There are only two formats: FM double-sided and MFM double-sided. +*/ +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 } +}; + +const floppy_format_type FLOPPY_TI99_TDF_FORMAT = &floppy_image_format_creator; + + +//========================================================================== + +/************************************************************** + * + * Legacy implementation * * Different to earlier implementations, these format implementations provide * full track read/write capabilities. For the SDF format, the missing track @@ -72,245 +948,10 @@ * FIXME: If image is broken with good first track and defect higher tracks, * tdf_guess_geometry fails to define a geometry. The guessing should * always be done. - ********************************************************************/ - -#include "emu.h" -#include -#include -#include - -#include "imageutl.h" -#include "ti99_dsk.h" - -/* - New format implementation. See below for legacy implementation. -*/ -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"; -} - -bool ti99_sdf_format::supports_save() const -{ - return true; -} - -int ti99_sdf_format::identify(io_generic *io, UINT32 form_factor) -{ - UINT64 file_size = io_generic_size(io); - - // Adding support for another sector image format which adds 768 bytes - // as a bad sector map - if ((file_size / 256) % 10 == 3) - { - logerror("Stripping map of bad sectors at image end\n"); - file_size -= 768; - } - - // Formats with 9 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))) return 50; - - // Formats with 16 sectors per track (rare) - if (((file_size % 163840)==0) - && ( (file_size/163840==1) - || (file_size/163840==2))) return 50; - - logerror("Unrecognized disk image geometry\n"); - return 0; -} - -int ti99_sdf_format::find_size(io_generic *io, UINT32 form_factor) -{ - UINT64 file_size = io_generic_size(io); - - // See above - if ((file_size / 256) % 10 == 3) file_size -= 768; - - for (int i=0; formats[i].form_factor != 0; i++) - { - logerror("ti99_dsk: trying size=%d, sec=%d, tr=%d, head=%d, ss=%d\n", (int)file_size, formats[i].sector_count, formats[i].track_count, formats[i].head_count, formats[i].sector_base_size); - if (formats[i].sector_count * formats[i].track_count * formats[i].head_count * formats[i].sector_base_size == (int)file_size) - { - logerror("ti99_dsk: format %d matches\n", i); - return i; - } - } - return -1; -} - -int ti99_sdf_format::get_image_offset(const format &f, int head, int track) -{ - // Note that the logical track arrangement on TI disks is - // - // 00 01 02 03 ... 38 39 side 0 - // 79 78 77 76 ... 41 40 side 1 - // - // and that the SDF format stores the tracks in logical order - // 00 01 02 03 ... 38 39 [40 41 ... 79] - // - int logicaltrack = head * f.track_count; - logicaltrack += ((head&1)==0)? track : (f.track_count - 1 - track); - logerror("ti99_dsk: track %d, head %d -> logical track %d\n", track, head, logicaltrack); - return logicaltrack * compute_track_size(f); -} - -/* - * Common formats: - * 90 KiB = 40 tracks / 1 side / 9 sectors = SSSD - * 180 KiB = 40 tracks / 2 side / 9 sectors = DSSD (most common) - * = 40 tracks / 1 side / 18 sectors = SSDD (rare) - * = 80 tracks / 1 side / 9 sectors = SSSD80 (rare) - * 360 KiB = 40 tracks / 2 side / 18 sectors = DSDD (most common) - * = 80 tracks / 2 side / 9 sectors = DSSD80 (rare) - * = 80 tracks / 1 side / 18 sectors = SSDD80 (rare) - * = 40 tracks / 1 side / 36 sectors = SSHD (rare) - * 720 KiB = 80 tracks / 2 side / 18 sectors = DSDD80 (most common) - * 1440 KiB= 80 tracks / 2 side / 36 sectors = DSHD80 (most common) - * - * Moreover, there may be formats with 35 tracks (ancient) and 8 sectors/track. - * We only check for the 8 sector formats. - * 160 KiB = 40 tracks / 1 side / 16 sectors = SSDD8 - * 320 KiB = 40 tracks / 2 side / 16 sectors = DSDD8 - */ -/* const ti99_sdf_format::file_format ti99_sdf_format::file_formats[] = -{ - { 90, floppy_image::SSSD, 9, 40, 1, 4000 }, // 5.25, 3.5 with double-step - { 160, floppy_image::DSSD, 8, 40, 2, 4000 }, // 5.25, 3.5 with double-step - { 180, floppy_image::DSSD, 9, 40, 2, 4000 }, // 5.25, 3.5 with double-step - { 180, floppy_image::SSDD, 18, 40, 2, 2000 }, // 5.25, 3.5 with double-step - { 320, floppy_image::DSDD, 16, 40, 2, 2000 }, // 5.25, 3.5 with double-step - { 360, floppy_image::DSDD, 18, 40, 2, 2000 }, // 5.25, 3.5 with double-step - { 720, floppy_image::DSDD, 18, 80, 2, 2000 }, // 3.5 - { 1440, floppy_image::DSHD, 36, 80, 2, 1000 }, // 3.5 - { 0, 0, 0, 0, 0, 0 } - // Add more formats with VIB check -}; */ - -// 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 density single sided - floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM, - 4000, 9, 40, 1, 256, {}, 0, {}, 16, 11, 45 - }, - { // 180K 5.25" single density double sided - floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM, - 4000, 9, 40, 2, 256, {}, 0, {}, 16, 11, 45 - }, - { // 360K 5.25" double density double sided - floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM, - 2000, 18, 40, 2, 256, {}, 0, {}, 40, 22, 24 - }, - - { 0 } -}; - -/* - * FM track image - */ -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 + 256 + 2 + f.gap_3) + 0) * 16; - end_gap_index = 21; - - return desc_fm; -} - -/* - * MFM track image - */ -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, 10, 0 }, - { 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 + 256 + 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; - -//========================================================================== -#define SECTOR_SIZE 256 -#define TI99_IDAM_LENGTH 7 - - -/************************************************************** - - Legacy implementation - **************************************************************/ +#define TI99_IDAM_LENGTH 7 + /* Determines whether we are using 80 track drives. This variable is necessary unless we get information about the dip diff --git a/src/lib/formats/ti99_dsk.h b/src/lib/formats/ti99_dsk.h index 4643bc88ec8..9364a991e75 100644 --- a/src/lib/formats/ti99_dsk.h +++ b/src/lib/formats/ti99_dsk.h @@ -4,7 +4,8 @@ TI99 and Geneve disk images - Header file to be included by drivers which use these floppy options + Michael Zapf, Feb 2014 + *********************************************************************/ #ifndef TI99_DSK_H @@ -14,9 +15,7 @@ #include "wd177x_dsk.h" /* - New implementation. - TDF format still to be done; - also, this implementation just considers FM for now. + Modern implementation of the sector dump format. */ class ti99_sdf_format : public wd177x_format { @@ -38,15 +37,80 @@ private: 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); + + typedef struct ti99_vib + { + char name[10]; // volume name (10 characters, pad with spaces) + UINT8 totsecsMSB; // disk length in sectors (big-endian) (usually 360, 720 or 1440) + UINT8 totsecsLSB; + UINT8 secspertrack; // sectors per track (usually 9 (FM) or 18 (MFM)) + UINT8 id[3]; // String "DSK" + UINT8 protection; // 'P' if disk is protected, ' ' otherwise. + UINT8 tracksperside; // tracks per side (usually 40) + UINT8 sides; // sides (1 or 2) + UINT8 density; // 0,1 (FM) or 2,3,4 (MFM) + UINT8 res[36]; // Empty for traditional disks, or up to 3 directory pointers + UINT8 abm[200]; // allocation bitmap: a 1 for each sector in use (sector 0 is LSBit of byte 0, + // sector 7 is MSBit of byte 0, sector 8 is LSBit of byte 1, etc.) + } vib_t; }; +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 +{ +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: + + 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); +}; + +extern const floppy_format_type FLOPPY_TI99_TDF_FORMAT; + +// ======================================================================== /* Legacy implementation. */ LEGACY_FLOPPY_OPTIONS_EXTERN(ti99); - void ti99_set_80_track_drives(int use80); -extern const floppy_format_type FLOPPY_TI99_SDF_FORMAT; #endif /* TI99_DSK_H */ diff --git a/src/mess/machine/ti99/bwg.c b/src/mess/machine/ti99/bwg.c index b6f479aebb7..4033bc8b0c6 100644 --- a/src/mess/machine/ti99/bwg.c +++ b/src/mess/machine/ti99/bwg.c @@ -27,17 +27,11 @@ Michael Zapf, September 2010 January 2012: rewritten as class (MZ) + February 2014: rewritten for new floppy subsystem (MZ) - Known issues (Dec 2013): + Known issues (Feb 2014): - 1. The BwG controller may fail to read files from single density disks. This - is because the DSR tries to read the disk as double density first, which - usually fails for a single density disk image - unless we are in track 0. - The fix would be to enhance disk images in a way to tell the controller - whether they are single or double density and to refuse delivering data - when accessed wrongly. - - 2. The BwG controller cannot run with the Geneve or other non-9900 computers. + 1. The BwG controller cannot run with the Geneve or other non-9900 computers. The reason for that is the wait state logic. It assumes that when executing MOVB @>5FF6,*R2, first a value from 5FF7 is attempted to be read, just as the TI console does. In that case, wait states are inserted if @@ -45,6 +39,13 @@ only and therefore circumvent the wait state generation. This is in fact not an emulation glitch but the behavior of the real expansion card. + Resolved issues (Feb 2014): + + 1. The BwG controller failed to read single-density disks. This only occurs + with the legacy implementation because the modern floppy implementation + reproduces the correct recording format on the medium, so the controller + actually detects FM or MFM. + *******************************************************************************/ #include "emu.h" @@ -73,23 +74,612 @@ // Show address bus operations #define TRACE_ADDRESS 0 + +// Show address bus operations +#define TRACE_MOTOR 0 + // ---------------------------------- +#define FLOPPY_FORMATS_END_NO_DEFAULTS ,\ + NULL }; + #define MOTOR_TIMER 1 -#define FDC_TAG "wd1773" #define CLOCK_TAG "mm58274c" +#define FDC_TAG "wd1773" #define BUFFER "ram" +/* + Modern implementation +*/ + snug_bwg_device::snug_bwg_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : ti_expansion_card_device(mconfig, TI99_BWG, "SNUG BwG Floppy Controller", tag, owner, clock, "ti99_bwg", __FILE__), m_wd1773(*this, FDC_TAG), m_clock(*this, CLOCK_TAG) { } /* - Callback called at the end of DVENA pulse + Operate the wait state logic. +*/ +void snug_bwg_device::operate_ready_line() +{ + // This is the wait state logic + if (TRACE_SIGNALS) logerror("bwg: address=%04x, DRQ=%d, INTRQ=%d, MOTOR=%d\n", m_address & 0xffff, m_DRQ, m_IRQ, m_MOTOR_ON); + line_state nready = (m_dataregLB && // Are we accessing 5ff7 + m_WAITena && // and the wait state generation is active (SBO 2) + (m_DRQ==CLEAR_LINE) && // and we are waiting for a byte + (m_IRQ==CLEAR_LINE) && // and there is no interrupt yet + (m_MOTOR_ON==ASSERT_LINE) // and the motor is turning? + )? ASSERT_LINE : CLEAR_LINE; // In that case, clear READY and thus trigger wait states + + if (TRACE_READY) if (nready==ASSERT_LINE) logerror("bwg: READY line = %d\n", (nready==CLEAR_LINE)? 1:0); + m_slot->set_ready((nready==CLEAR_LINE)? ASSERT_LINE : CLEAR_LINE); +} + +/* + Callbacks from the WD1773 chip +*/ +void snug_bwg_device::irq_w(bool state) +{ + if (TRACE_SIGNALS) logerror("bwg: set intrq = %d\n", state); + m_IRQ = (line_state)state; + // Unlike the TI FDC, the BwG does not set the INTB line. Anyway, no one cares. + // We need to explicitly set the READY line to release the datamux + operate_ready_line(); +} + +void snug_bwg_device::drq_w(bool state) +{ + if (TRACE_SIGNALS) logerror("bwg: set drq = %d\n", state); + m_DRQ = (line_state)state; + + // We need to explicitly set the READY line to release the datamux + operate_ready_line(); +} + +SETADDRESS_DBIN_MEMBER( snug_bwg_device::setaddress_dbin ) +{ + // Selection login in the PAL and some circuits on the board + + // Is the card being selected? + m_address = offset; + m_inDsrArea = ((m_address & m_select_mask)==m_select_value); + + if (!m_inDsrArea) return; + + if (TRACE_ADDRESS) logerror("bwg: set address = %04x\n", offset & 0xffff); + + // Is the WD chip on the card being selected? + // We need the even and odd addresses for the wait state generation, + // but only the even addresses when we access it + m_WDsel0 = m_inDsrArea && !m_rtc_enabled + && ((state==ASSERT_LINE && ((m_address & 0x1ff8)==0x1ff0)) // read + || (state==CLEAR_LINE && ((m_address & 0x1ff8)==0x1ff8))); // write + + m_WDsel = m_WDsel0 && ((m_address & 1)==0); + + // Is the RTC selected on the card? (even addr) + m_RTCsel = m_inDsrArea && m_rtc_enabled && ((m_address & 0x1fe1)==0x1fe0); + + // RTC disabled: + // 5c00 - 5fef: RAM + // 5ff0 - 5fff: Controller (f0 = status, f2 = track, f4 = sector, f6 = data) + + // RTC enabled: + // 5c00 - 5fdf: RAM + // 5fe0 - 5fff: Clock (even addr) + + // Is RAM selected? We just check for the last 1K and let the RTC or WD + // just take control before + m_lastK = m_inDsrArea && ((m_address & 0x1c00)==0x1c00); + + // Is the data register port of the WD being selected? + // In fact, the address to read the data from is 5FF6, but the TI-99 datamux + // fetches both bytes from 5FF7 and 5FF6, the odd one first. The BwG uses + // the odd address to operate the READY line + m_dataregLB = m_WDsel0 && ((m_address & 0x07)==0x07); + + // Clear or assert the outgoing READY line + operate_ready_line(); +} + +/* + Read a byte from ROM, RAM, FDC, or RTC. See setaddress_dbin for selection + logic. +*/ +READ8Z_MEMBER(snug_bwg_device::readz) +{ + if (m_inDsrArea && m_selected) + { + // 010x xxxx xxxx xxxx + if (m_lastK) + { + // ...1 11xx xxxx xxxx + if (m_rtc_enabled) + { + if (m_RTCsel) + { + // .... ..11 111x xxx0 + if (!space.debugger_access()) *value = m_clock->read(space, (m_address & 0x001e) >> 1); + if (TRACE_RW) logerror("bwg: read RTC: %04x -> %02x\n", m_address & 0xffff, *value); + } + else + { + *value = m_buffer_ram[(m_ram_page<<10) | (m_address & 0x03ff)]; + if (TRACE_RW) logerror("bwg: read ram: %04x (page %d)-> %02x\n", m_address & 0xffff, m_ram_page, *value); + } + } + else + { + if (m_WDsel) + { + // .... ..11 1111 0xx0 + // Note that the value is inverted again on the board, + // so we can drop the inversion + if (!space.debugger_access()) *value = m_wd1773->gen_r((m_address >> 1)&0x03); + if (TRACE_RW) logerror("bwg: read FDC: %04x -> %02x\n", m_address & 0xffff, *value); + if (TRACE_DATA) + { + if ((m_address & 0xffff)==0x5ff6) logerror("%02x ", *value); + else logerror("\n%04x: %02x", m_address&0xffff, *value); + } + } + else + { + *value = m_buffer_ram[(m_ram_page<<10) | (m_address & 0x03ff)]; + if (TRACE_RW) logerror("bwg: read ram: %04x (page %d)-> %02x\n", m_address & 0xffff, m_ram_page, *value); + } + } + } + else + { + *value = m_dsrrom[(m_rom_page<<13) | (m_address & 0x1fff)]; + if (TRACE_RW) logerror("bwg: read dsr: %04x (page %d)-> %02x\n", m_address & 0xffff, m_rom_page, *value); + } + } +} + +/* + Write a byte + 4000 - 5bff: ROM, ignore write (4 banks) + + rtc disabled: + 5c00 - 5fef: RAM + 5ff0 - 5fff: Controller (f8 = command, fa = track, fc = sector, fe = data) + + rtc enabled: + 5c00 - 5fdf: RAM + 5fe0 - 5fff: Clock (even addr) +*/ +WRITE8_MEMBER(snug_bwg_device::write) +{ + if (m_inDsrArea && m_selected) + { + if (m_lastK) + { + if (m_rtc_enabled) + { + if (m_RTCsel) + { + // .... ..11 111x xxx0 + if (TRACE_RW) logerror("bwg: write RTC: %04x <- %02x\n", m_address & 0xffff, data); + if (!space.debugger_access()) m_clock->write(space, (m_address & 0x001e) >> 1, data); + } + else + { + if (TRACE_RW) logerror("bwg: write ram: %04x (page %d) <- %02x\n", m_address & 0xffff, m_ram_page, data); + m_buffer_ram[(m_ram_page<<10) | (m_address & 0x03ff)] = data; + } + } + else + { + if (m_WDsel) + { + // .... ..11 1111 1xx0 + // Note that the value is inverted again on the board, + // so we can drop the inversion + if (TRACE_RW) logerror("bwg: write FDC: %04x <- %02x\n", m_address & 0xffff, data); + if (!space.debugger_access()) m_wd1773->gen_w((m_address >> 1)&0x03, data); + } + else + { + if (TRACE_RW) logerror("bwg: write ram: %04x (page %d) <- %02x\n", m_address & 0xffff, m_ram_page, data); + m_buffer_ram[(m_ram_page<<10) | (m_address & 0x03ff)] = data; + } + } + } + } +} + +/* + CRU read handler. *=inverted. + bit 0: DSK4 connected* + bit 1: DSK1 connected* + bit 2: DSK2 connected* + bit 3: DSK3 connected* + bit 4: Dip 1 + bit 5: Dip 2 + bit 6: Dip 3 + bit 7: Dip 4 +*/ +READ8Z_MEMBER(snug_bwg_device::crureadz) +{ + UINT8 reply = 0; + + if ((offset & 0xff00)==m_cru_base) + { + if ((offset & 0x00ff)==0) + { + // Check what drives are not connected + reply = ((m_floppy[0] != NULL)? 0 : 0x02) // DSK1 + | ((m_floppy[1] != NULL)? 0 : 0x04) // DSK2 + | ((m_floppy[2] != NULL)? 0 : 0x08) // DSK3 + | ((m_floppy[3] != NULL)? 0 : 0x01); // DSK4 + + // DIP switches for step and date/time display + if (m_dip1 != 0) reply |= 0x10; + if (m_dip2 != 0) reply |= 0x20; + + // DIP switches for drive range selection + // 00 = only DSK1; 01 = DSK1+DSK2, 10=DSK1+DSK2+DSK3, 11=all + reply |= (m_dip34 << 6); + + // Invert all + *value = ~reply; + } + else + *value = 0; + if (TRACE_CRU) logerror("bwg: Read CRU = %02x\n", *value); + } +} + +WRITE8_MEMBER(snug_bwg_device::cruwrite) +{ + int drive, drivebit; + + if ((offset & 0xff00)==m_cru_base) + { + int bit = (offset >> 1) & 0x0f; + switch (bit) + { + case 0: + /* (De)select the card. Indicated by a LED on the board. */ + m_selected = (data != 0); + if (TRACE_CRU) logerror("bwg: Map DSR (bit 0) = %d\n", m_selected); + break; + + case 1: + // Activate motor + if (data==1 && m_lastval==0) + { // on rising edge, set motor_running for 4.23s + if (TRACE_CRU) logerror("bwg: trigger motor (bit 1)\n"); + set_floppy_motors_running(true); + } + m_lastval = data; + break; + + case 2: + /* Set disk ready/hold (bit 2) */ + // 0: ignore IRQ and DRQ + // 1: TMS9900 is stopped until IRQ or DRQ are set + // OR the motor stops rotating - rotates for 4.23s after write + // to CRU bit 1 + if (TRACE_CRU) logerror("bwg: arm wait state logic (bit 2) = %d\n", data); + m_WAITena = (data != 0); + break; + + case 3: + // Load disk heads (HLT pin) (bit 3). Not implemented. + if (TRACE_CRU) logerror("bwg: set head load (bit 3) = %d\n", data); + break; + + case 4: + // Select drive 0-2 (DSK1-DSK3) (bits 4-6) + m_DSEL = (data != 0)? (m_DSEL | 0x01) : (m_DSEL & 0xfe); + set_drive(); + break; + case 5: + m_DSEL = (data != 0)? (m_DSEL | 0x02) : (m_DSEL & 0xfd); + set_drive(); + break; + case 6: + m_DSEL = (data != 0)? (m_DSEL | 0x04) : (m_DSEL & 0xfb); + set_drive(); + break; + + case 7: + // Select side of disk (bit 7) + m_SIDSEL = (data==1)? ASSERT_LINE : CLEAR_LINE; + if (TRACE_CRU) logerror("bwg: set side (bit 7) = %d\n", data); + if (m_current_floppy != NULL) m_current_floppy->ss_w(data); + break; + + case 8: + // Select drive 3 (DSK4) (bit 8) */ + m_DSEL = (data != 0)? (m_DSEL | 0x08) : (m_DSEL & 0xf7); + set_drive(); + break; + + case 10: + /* double density enable (active low) */ + if (TRACE_CRU) logerror("bwg: set double density (bit 10) = %d\n", data); + m_wd1773->dden_w(data != 0); + break; + + case 11: + /* EPROM A13 */ + if (data != 0) + m_rom_page |= 1; + else + m_rom_page &= 0xfe; // 11111110 + if (TRACE_CRU) logerror("bwg: set ROM page (bit 11) = %d, page = %d\n", bit, m_rom_page); + break; + + case 13: + /* RAM A10 */ + m_ram_page = data; + if (TRACE_CRU) logerror("bwg: set RAM page (bit 13) = %d, page = %d\n", bit, m_ram_page); + break; + + case 14: + /* Override FDC with RTC (active high) */ + if (TRACE_CRU) logerror("bwg: turn on RTC (bit 14) = %d\n", data); + m_rtc_enabled = (data != 0); + break; + + case 15: + /* EPROM A14 */ + if (data != 0) + m_rom_page |= 2; + else + m_rom_page &= 0xfd; // 11111101 + if (TRACE_CRU) logerror("bwg: set ROM page (bit 15) = %d, page = %d\n", bit, m_rom_page); + break; + + case 9: + case 12: + /* Unused (bit 3, 9 & 12) */ + if (TRACE_CRU) logerror("bwg: set unknown bit %d = %d\n", bit, data); + break; + } + } +} + +/* + Set the current drive. See also ti_fdc.c +*/ +void snug_bwg_device::set_drive() +{ + if (TRACE_CRU) logerror("bwg: new DSEL = %d\n", m_DSEL); + + if ((m_DSEL != 0) && (m_DSEL != 1) && (m_DSEL != 2) && (m_DSEL != 4) && (m_DSEL != 8)) + { + logerror("bwg: Warning - multiple drives selected\n"); + } + + // The schematics do not reveal any countermeasures against multiple selection + // so we assume that the highest value wins. + + int bits = m_DSEL & 0x0f; + int i = -1; + + while (bits != 0) + { + bits >>= 1; + i++; + } + if (i != -1) + { + m_current_floppy = m_floppy[i]; + if (TRACE_CRU) logerror("bwg: Selected floppy %d\n", i); + } + else + { + m_current_floppy = NULL; + if (TRACE_CRU) logerror("bwg: All drives deselected\n"); + } + m_wd1773->set_floppy(m_current_floppy); +} + +/* + Monoflop has gone back to the OFF state. */ void snug_bwg_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + set_floppy_motors_running(false); +} + +/* + All floppy motors are operated by the same line. +*/ +void snug_bwg_device::set_floppy_motors_running(bool run) +{ + if (run) + { + if (TRACE_MOTOR) + if (m_MOTOR_ON==CLEAR_LINE) logerror("bwg: Motor START\n"); + m_MOTOR_ON = ASSERT_LINE; + m_motor_on_timer->adjust(attotime::from_msec(4230)); + } + else + { + if (TRACE_MOTOR) + if (m_MOTOR_ON==ASSERT_LINE) logerror("bwg: Motor STOP\n"); + m_MOTOR_ON = CLEAR_LINE; + } + + // The motor-on line is connected to pin 20 which is falsely called "MO" + // in the schematics; should be called "READY" as we are using the WD1773. + m_wd1773->set_force_ready(run); + + // Set all motors + for (int i=0; i < 4; i++) + if (m_floppy[i] != NULL) m_floppy[i]->mon_w((run)? 0 : 1); + + // The motor-on line also connects to the wait state logic + operate_ready_line(); +} + +void snug_bwg_device::device_start(void) +{ + logerror("bwg: BWG start\n"); + m_dsrrom = memregion(DSRROM)->base(); + m_buffer_ram = memregion(BUFFER)->base(); + m_motor_on_timer = timer_alloc(MOTOR_TIMER); + m_cru_base = 0x1100; + + // Connect the INTRQ and DRQ lines + m_wd1773->setup_intrq_cb(wd_fdc_t::line_cb(FUNC(snug_bwg_device::irq_w), this)); + m_wd1773->setup_drq_cb(wd_fdc_t::line_cb(FUNC(snug_bwg_device::drq_w), this)); +} + +void snug_bwg_device::device_reset() +{ + logerror("bwg: BWG reset\n"); + + if (m_genmod) + { + m_select_mask = 0x1fe000; + m_select_value = 0x174000; + } + else + { + m_select_mask = 0x7e000; + m_select_value = 0x74000; + } + + m_lastval = 0; + + m_DRQ = CLEAR_LINE; + m_IRQ = CLEAR_LINE; + m_MOTOR_ON = CLEAR_LINE; + + m_SIDSEL = CLEAR_LINE; + + m_wd1773->set_force_ready(false); + + m_DSEL = 0; + m_WAITena = false; + m_selected = false; + m_debug_dataout = false; + m_rtc_enabled = false; + + for (int i=0; i < 4; i++) + { + if (m_floppy[i] != NULL) + logerror("bwg: Connector %d with %s\n", i, m_floppy[i]->name()); + else + logerror("bwg: Connector %d has no floppy attached\n", i); + } + + m_wd1773->set_floppy(m_current_floppy = m_floppy[0]); + + m_dip1 = ioport("BWGDIP1")->read(); + m_dip2 = ioport("BWGDIP2")->read(); + m_dip34 = ioport("BWGDIP34")->read(); + + m_rom_page = 0; + m_ram_page = 0; +} + +void snug_bwg_device::device_config_complete() +{ + for (int i=0; i < 4; i++) + m_floppy[i] = NULL; + + // Seems to be null when doing a "-listslots" + if (subdevice("0")!=NULL) m_floppy[0] = static_cast(subdevice("0")->first_subdevice()); + if (subdevice("1")!=NULL) m_floppy[1] = static_cast(subdevice("1")->first_subdevice()); + if (subdevice("2")!=NULL) m_floppy[2] = static_cast(subdevice("2")->first_subdevice()); + if (subdevice("3")!=NULL) m_floppy[3] = static_cast(subdevice("3")->first_subdevice()); +} + +static const mm58274c_interface bwg_mm58274c_interface = +{ + 1, /* mode 24*/ + 0 /* first day of week */ +}; + +INPUT_PORTS_START( bwg_fdc ) + PORT_START( "BWGDIP1" ) + PORT_DIPNAME( 0x01, 0x00, "BwG step rate" ) + PORT_DIPSETTING( 0x00, "6 ms") + PORT_DIPSETTING( 0x01, "20 ms") + + PORT_START( "BWGDIP2" ) + PORT_DIPNAME( 0x01, 0x00, "BwG date/time display" ) + PORT_DIPSETTING( 0x00, "Hide") + PORT_DIPSETTING( 0x01, "Show") + + PORT_START( "BWGDIP34" ) + PORT_DIPNAME( 0x03, 0x03, "BwG drives" ) + PORT_DIPSETTING( 0x00, "DSK1 only") + PORT_DIPSETTING( 0x01, "DSK1-DSK2") + PORT_DIPSETTING( 0x02, "DSK1-DSK3") + PORT_DIPSETTING( 0x03, "DSK1-DSK4") +INPUT_PORTS_END + +FLOPPY_FORMATS_MEMBER(snug_bwg_device::floppy_formats) + FLOPPY_TI99_SDF_FORMAT, + FLOPPY_TI99_TDF_FORMAT +FLOPPY_FORMATS_END_NO_DEFAULTS + +static SLOT_INTERFACE_START( bwg_floppies ) + SLOT_INTERFACE( "525dd", FLOPPY_525_DD ) // 40 tracks + SLOT_INTERFACE( "525qd", FLOPPY_525_QD ) // 80 tracks + SLOT_INTERFACE( "35dd", FLOPPY_35_DD ) // 80 tracks +SLOT_INTERFACE_END + +MACHINE_CONFIG_FRAGMENT( bwg_fdc ) + MCFG_WD1773x_ADD(FDC_TAG, XTAL_8MHz) + MCFG_MM58274C_ADD(CLOCK_TAG, bwg_mm58274c_interface) + + MCFG_FLOPPY_DRIVE_ADD("0", bwg_floppies, "525dd", snug_bwg_device::floppy_formats) + MCFG_FLOPPY_DRIVE_ADD("1", bwg_floppies, "525dd", snug_bwg_device::floppy_formats) + MCFG_FLOPPY_DRIVE_ADD("2", bwg_floppies, NULL, snug_bwg_device::floppy_formats) + MCFG_FLOPPY_DRIVE_ADD("3", bwg_floppies, NULL, snug_bwg_device::floppy_formats) +MACHINE_CONFIG_END + +ROM_START( bwg_fdc ) + ROM_REGION(0x8000, DSRROM, 0) + ROM_LOAD("bwg.bin", 0x0000, 0x8000, CRC(06f1ec89) SHA1(6ad77033ed268f986d9a5439e65f7d391c4b7651)) /* BwG disk DSR ROM */ + ROM_REGION(0x0800, BUFFER, 0) /* BwG RAM buffer */ + ROM_FILL(0x0000, 0x0400, 0x00) +ROM_END + +machine_config_constructor snug_bwg_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( bwg_fdc ); +} + +ioport_constructor snug_bwg_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( bwg_fdc ); +} + +const rom_entry *snug_bwg_device::device_rom_region() const +{ + return ROM_NAME( bwg_fdc ); +} + +const device_type TI99_BWG = &device_creator; + +// ========================================================================== + +#define FDCLEG_TAG "wd1773" + +/* + Legacy implementation +*/ +snug_bwg_legacy_device::snug_bwg_legacy_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : ti_expansion_card_device(mconfig, TI99_BWG_LEG, "SNUG BwG Floppy Controller (legacy)", tag, owner, clock, "ti99_bwg", __FILE__), + m_wd1773(*this, FDCLEG_TAG), + m_clock(*this, CLOCK_TAG) { } + +/* + Callback called at the end of DVENA pulse +*/ +void snug_bwg_legacy_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { m_DVENA = CLEAR_LINE; set_ready_line(); @@ -99,7 +689,7 @@ void snug_bwg_device::device_timer(emu_timer &timer, device_timer_id id, int par /* Operate the wait state logic. */ -void snug_bwg_device::set_ready_line() +void snug_bwg_legacy_device::set_ready_line() { // This is the wait state logic if (TRACE_SIGNALS) logerror("bwg: address=%04x, DRQ=%d, INTRQ=%d, MOTOR=%d\n", m_address & 0xffff, m_DRQ, m_IRQ, m_DVENA); @@ -117,7 +707,7 @@ void snug_bwg_device::set_ready_line() /* Callback, called from the controller chip whenever DRQ/IRQ state change */ -WRITE_LINE_MEMBER( snug_bwg_device::intrq_w ) +WRITE_LINE_MEMBER( snug_bwg_legacy_device::intrq_w ) { if (TRACE_SIGNALS) logerror("bwg: set intrq = %d\n", state); m_IRQ = (line_state)state; @@ -131,7 +721,7 @@ WRITE_LINE_MEMBER( snug_bwg_device::intrq_w ) set_ready_line(); } -WRITE_LINE_MEMBER( snug_bwg_device::drq_w ) +WRITE_LINE_MEMBER( snug_bwg_legacy_device::drq_w ) { if (TRACE_SIGNALS) logerror("bwg: set drq = %d\n", state); m_DRQ = (line_state)state; @@ -140,7 +730,7 @@ WRITE_LINE_MEMBER( snug_bwg_device::drq_w ) set_ready_line(); } -SETADDRESS_DBIN_MEMBER( snug_bwg_device::setaddress_dbin ) +SETADDRESS_DBIN_MEMBER( snug_bwg_legacy_device::setaddress_dbin ) { // Selection login in the PAL and some circuits on the board @@ -190,7 +780,7 @@ SETADDRESS_DBIN_MEMBER( snug_bwg_device::setaddress_dbin ) Read a byte from ROM, RAM, FDC, or RTC. See setaddress_dbin for selection logic. */ -READ8Z_MEMBER(snug_bwg_device::readz) +READ8Z_MEMBER(snug_bwg_legacy_device::readz) { if (m_inDsrArea && m_selected) { @@ -247,7 +837,7 @@ READ8Z_MEMBER(snug_bwg_device::readz) the default implementation sets the drive geometry to the geometry of the medium. */ -void snug_bwg_device::set_geometry(device_t *drive, floppy_type_t type) +void snug_bwg_legacy_device::set_geometry(device_t *drive, floppy_type_t type) { // This assertion may fail when the names of the floppy devices change. // Unfortunately, the wd17xx device assumes the floppy drives at root @@ -256,7 +846,7 @@ void snug_bwg_device::set_geometry(device_t *drive, floppy_type_t type) floppy_drive_set_geometry(drive, type); } -void snug_bwg_device::set_all_geometries(floppy_type_t type) +void snug_bwg_legacy_device::set_all_geometries(floppy_type_t type) { set_geometry(machine().device(PFLOPPY_0), type); set_geometry(machine().device(PFLOPPY_1), type); @@ -275,7 +865,7 @@ void snug_bwg_device::set_all_geometries(floppy_type_t type) 5c00 - 5fdf: RAM 5fe0 - 5fff: Clock (even addr) */ -WRITE8_MEMBER(snug_bwg_device::write) +WRITE8_MEMBER(snug_bwg_legacy_device::write) { if (m_inDsrArea && m_selected) { @@ -326,7 +916,7 @@ WRITE8_MEMBER(snug_bwg_device::write) bit 6: Dip 3 bit 7: Dip 4 */ -READ8Z_MEMBER(snug_bwg_device::crureadz) +READ8Z_MEMBER(snug_bwg_legacy_device::crureadz) { UINT8 reply = 0; @@ -353,7 +943,7 @@ READ8Z_MEMBER(snug_bwg_device::crureadz) } } -WRITE8_MEMBER(snug_bwg_device::cruwrite) +WRITE8_MEMBER(snug_bwg_legacy_device::cruwrite) { int drive, drivebit; @@ -468,7 +1058,7 @@ WRITE8_MEMBER(snug_bwg_device::cruwrite) } } -void snug_bwg_device::device_start(void) +void snug_bwg_legacy_device::device_start(void) { logerror("bwg: BWG start\n"); m_dsrrom = memregion(DSRROM)->base(); @@ -477,7 +1067,7 @@ void snug_bwg_device::device_start(void) m_cru_base = 0x1100; } -void snug_bwg_device::device_reset() +void snug_bwg_legacy_device::device_reset() { logerror("bwg: BWG reset\n"); @@ -513,21 +1103,21 @@ void snug_bwg_device::device_reset() m_ram_page = 0; } -const wd17xx_interface bwg_wd17xx_interface = +const wd17xx_interface bwgleg_wd17xx_interface = { DEVCB_NULL, - DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, snug_bwg_device, intrq_w), - DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, snug_bwg_device, drq_w), + DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, snug_bwg_legacy_device, intrq_w), + DEVCB_DEVICE_LINE_MEMBER(DEVICE_SELF_OWNER, snug_bwg_legacy_device, drq_w), { PFLOPPY_0, PFLOPPY_1, PFLOPPY_2, PFLOPPY_3 } }; -static const mm58274c_interface floppy_mm58274c_interface = +static const mm58274c_interface floppyleg_mm58274c_interface = { 1, /* mode 24*/ 0 /* first day of week */ }; -INPUT_PORTS_START( bwg_fdc ) +INPUT_PORTS_START( bwg_fdc_legacy ) PORT_START( "BWGDIP1" ) PORT_DIPNAME( 0x01, 0x00, "BwG step rate" ) PORT_DIPSETTING( 0x00, "6 ms") @@ -546,31 +1136,31 @@ INPUT_PORTS_START( bwg_fdc ) PORT_DIPSETTING( 0x03, "DSK1-DSK4") INPUT_PORTS_END -MACHINE_CONFIG_FRAGMENT( bwg_fdc ) - MCFG_WD1773_ADD(FDC_TAG, bwg_wd17xx_interface ) - MCFG_MM58274C_ADD(CLOCK_TAG, floppy_mm58274c_interface) +MACHINE_CONFIG_FRAGMENT( bwg_fdc_legacy ) + MCFG_WD1773_ADD(FDCLEG_TAG, bwgleg_wd17xx_interface ) + MCFG_MM58274C_ADD(CLOCK_TAG, floppyleg_mm58274c_interface) MACHINE_CONFIG_END -ROM_START( bwg_fdc ) +ROM_START( bwg_fdc_legacy ) ROM_REGION(0x8000, DSRROM, 0) ROM_LOAD("bwg.bin", 0x0000, 0x8000, CRC(06f1ec89) SHA1(6ad77033ed268f986d9a5439e65f7d391c4b7651)) /* BwG disk DSR ROM */ ROM_REGION(0x0800, BUFFER, 0) /* BwG RAM buffer */ ROM_FILL(0x0000, 0x0400, 0x00) ROM_END -machine_config_constructor snug_bwg_device::device_mconfig_additions() const +machine_config_constructor snug_bwg_legacy_device::device_mconfig_additions() const { - return MACHINE_CONFIG_NAME( bwg_fdc ); + return MACHINE_CONFIG_NAME( bwg_fdc_legacy ); } -const rom_entry *snug_bwg_device::device_rom_region() const +const rom_entry *snug_bwg_legacy_device::device_rom_region() const { - return ROM_NAME( bwg_fdc ); + return ROM_NAME( bwg_fdc_legacy ); } -ioport_constructor snug_bwg_device::device_input_ports() const +ioport_constructor snug_bwg_legacy_device::device_input_ports() const { - return INPUT_PORTS_NAME(bwg_fdc); + return INPUT_PORTS_NAME( bwg_fdc_legacy ); } -const device_type TI99_BWG = &device_creator; +const device_type TI99_BWG_LEG = &device_creator; diff --git a/src/mess/machine/ti99/bwg.h b/src/mess/machine/ti99/bwg.h index bb4ecd8f90b..879654c8216 100644 --- a/src/mess/machine/ti99/bwg.h +++ b/src/mess/machine/ti99/bwg.h @@ -17,10 +17,13 @@ #include "ti99defs.h" #include "imagedev/flopdrv.h" #include "machine/mm58274c.h" -#include "machine/wd17xx.h" +#include "machine/wd_fdc.h" extern const device_type TI99_BWG; +/* + Implementation for modern floppy system. +*/ class snug_bwg_device : public ti_expansion_card_device { public: @@ -35,6 +38,132 @@ public: DECLARE_READ8Z_MEMBER(crureadz); DECLARE_WRITE8_MEMBER(cruwrite); + DECLARE_FLOPPY_FORMATS( floppy_formats ); + + void irq_w(bool state); + void drq_w(bool state); + +protected: + void device_start(); + void device_reset(); + void device_config_complete(); + + const rom_entry *device_rom_region() const; + machine_config_constructor device_mconfig_additions() const; + ioport_constructor device_input_ports() const; + +private: + void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + // Wait state logic + void operate_ready_line(); + + // Set the current floppy + void set_drive(); + + // Holds the status of the DRQ and IRQ lines. + line_state m_DRQ, m_IRQ; + + // DIP switch state + int m_dip1, m_dip2, m_dip34; + + // Page selection for ROM and RAM + int m_ram_page; // 0-1 + int m_rom_page; // 0-3 + + // Operate the floppy motors + void set_floppy_motors_running(bool run); + + // When true, the READY line will be cleared (create wait states) when + // waiting for data from the controller. + bool m_WAITena; + + // Address in card area + bool m_inDsrArea; + + // WD selected + bool m_WDsel, m_WDsel0; + + // RTC selected + bool m_RTCsel; + + // last 1K area selected + bool m_lastK; + + // Data register +1 selected + bool m_dataregLB; + + // Indicates whether the clock is mapped into the address space. + bool m_rtc_enabled; + + // Signal motor_on. When TRUE, makes all drives turning. + line_state m_MOTOR_ON; + + // Needed for triggering the motor monoflop + UINT8 m_lastval; + + // Recent address + int m_address; + + /* Indicates which drive has been selected. Values are 0, 1, 2, and 4. */ + // 000 = no drive + // 001 = drive 1 + // 010 = drive 2 + // 100 = drive 3 + int m_DSEL; + + // Signal SIDSEL. 0 or 1, indicates the selected head. + line_state m_SIDSEL; + + // count 4.23s from rising edge of motor_on + emu_timer* m_motor_on_timer; + + // DSR ROM + UINT8* m_dsrrom; + + // Buffer RAM + UINT8* m_buffer_ram; + + // Link to the attached floppy drives + floppy_image_device* m_floppy[4]; + + // Currently selected floppy drive + floppy_image_device* m_current_floppy; + + // Link to the WD1773 controller on the board. + required_device m_wd1773; + + // Link to the real-time clock on the board. + required_device m_clock; + + // Debugging + bool m_debug_dataout; +}; + +// ========================================================================= + +/* + Legacy implementation. +*/ + +#include "machine/wd17xx.h" + +extern const device_type TI99_BWG_LEG; + +class snug_bwg_legacy_device : public ti_expansion_card_device +{ +public: + snug_bwg_legacy_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + DECLARE_READ8Z_MEMBER(readz); + DECLARE_WRITE8_MEMBER(write); + DECLARE_SETADDRESS_DBIN_MEMBER(setaddress_dbin); + + DECLARE_WRITE_LINE_MEMBER( intrq_w ); + DECLARE_WRITE_LINE_MEMBER( drq_w ); + + DECLARE_READ8Z_MEMBER(crureadz); + DECLARE_WRITE8_MEMBER(cruwrite); + protected: virtual void device_start(void); virtual void device_reset(void); diff --git a/src/mess/machine/ti99/peribox.c b/src/mess/machine/ti99/peribox.c index 36c8a0daaff..706bb162e0c 100644 --- a/src/mess/machine/ti99/peribox.c +++ b/src/mess/machine/ti99/peribox.c @@ -413,6 +413,7 @@ SLOT_INTERFACE_END SLOT_INTERFACE_START( peribox_slot7 ) SLOT_INTERFACE("ide", TI99_IDE) SLOT_INTERFACE("usbsm", TI99_USBSM) + SLOT_INTERFACE("bwgleg", TI99_BWG_LEG) SLOT_INTERFACE("bwg", TI99_BWG) SLOT_INTERFACE("hfdc", TI99_HFDC) SLOT_INTERFACE_END @@ -420,6 +421,7 @@ SLOT_INTERFACE_END SLOT_INTERFACE_START( peribox_slot8 ) SLOT_INTERFACE("tifdcleg", TI99_FDC_LEG) SLOT_INTERFACE("tifdc", TI99_FDC) + SLOT_INTERFACE("bwgleg", TI99_BWG_LEG) SLOT_INTERFACE("bwg", TI99_BWG) SLOT_INTERFACE("hfdc", TI99_HFDC) SLOT_INTERFACE_END diff --git a/src/mess/machine/ti99/ti_fdc.c b/src/mess/machine/ti99/ti_fdc.c index 7577ed68eb7..071ed5ed217 100644 --- a/src/mess/machine/ti99/ti_fdc.c +++ b/src/mess/machine/ti99/ti_fdc.c @@ -397,7 +397,8 @@ void ti_fdc_device::device_config_complete() } FLOPPY_FORMATS_MEMBER(ti_fdc_device::floppy_formats) - FLOPPY_TI99_SDF_FORMAT + FLOPPY_TI99_SDF_FORMAT, + FLOPPY_TI99_TDF_FORMAT FLOPPY_FORMATS_END_NO_DEFAULTS static SLOT_INTERFACE_START( tifdc_floppies ) @@ -407,7 +408,7 @@ SLOT_INTERFACE_END MACHINE_CONFIG_FRAGMENT( ti_fdc ) MCFG_FD1771x_ADD(FDC_TAG, XTAL_1MHz) MCFG_FLOPPY_DRIVE_ADD("0", tifdc_floppies, "525dd", ti_fdc_device::floppy_formats) - MCFG_FLOPPY_DRIVE_ADD("1", tifdc_floppies, NULL, ti_fdc_device::floppy_formats) + MCFG_FLOPPY_DRIVE_ADD("1", tifdc_floppies, "525dd", ti_fdc_device::floppy_formats) MCFG_FLOPPY_DRIVE_ADD("2", tifdc_floppies, NULL, ti_fdc_device::floppy_formats) MACHINE_CONFIG_END