// license:GPL-2.0+ // copyright-holders:Karl-Ludwig Deisenhofer /********************************************************************** formats/rx50_dsk.c Floppies used by Rainbow 100 and 190. The RX50 drive: 5.25" format; 300 rpm; MFM 250 kbps; 96 - 100 tpi - single sided with two disk slots (1 drive motor served both). DEC used the RX50 in entirely different architectures (Pro / PDP-11). Native Rainbow 100 format: - SSQD - 80 tracks with 10 sectors per track (512 byte sectors) - first two tracks are reserved for loader code (DOS / CPM / custom) - FAT and root directory began immediately on track 2. - 2:1 sector interleave pattern - except in tracks 0 and 1. NOTE: PUTR tends to interleave loader tracks 0 + 1. Not recommended! Jeff's RBIMG is first choice for preservation, TeleDisk is second. Always check the track layout. Both have problems with weak disks. A container to handle copy protected RX 50 disks is needed. Some info can be derived from Mark Hittinger's RBACKUP (CP/M source from Nov-94). TODO: improve code to accept 40 T / single sided / 9 or 8 sector disks: a) disks from VT180 (9 sectors; READ ONLY - enforced by BIOS) b) 8 sector 160k MS-DOS disks (READ + WRITE support on DEC) FORMAT A: /F:160 on DOS; turn MEDIACHK ON ************************************************************************/ #include #include "flopimg.h" #include "formats/rx50_dsk.h" // Controller: WD1793 // TRACK LAYOUT IS UNVERIFED. SEE SOURCES: // - 'esq_dsk16' (uses WD 1772) // - SDC-RX50 Floppy Controller Manual // - 'PC 100 SYSTEM SPEC' 4.3 - page 42. (*) const floppy_image_format_t::desc_e rx50img_format::rx50_10_desc[] = { { MFM, 0x4e, 80 }, // (*) GAP (1) { MFM, 0x00, 12 }, // Value from (*). (?? = unverified) { RAW, 0x5224, 3 }, { MFM, 0xfc, 1 }, { MFM, 0x4e, 50 }, { MFM, 0x00, 12 }, { SECTOR_LOOP_START, 0, 9 }, // 0 ... f.sector_count-1 { CRC_CCITT_START, 1 }, { RAW, 0x4489, 3 }, { MFM, 0xfe, 1 }, { TRACK_ID }, { HEAD_ID }, { SECTOR_ID }, { SIZE_ID }, { CRC_END, 1 }, { CRC, 1 }, { MFM, 0x4e, 22 }, // (*) POST-ID GAP (2) { MFM, 0x00, 12 }, // (*) { CRC_CCITT_START, 2 }, { RAW, 0x4489, 3 }, { MFM, 0xfb, 1 }, { SECTOR_DATA, -1 }, { CRC_END, 2 }, { CRC, 2 }, { MFM, 0x4e, 48 }, // GAP (3) - taken from RBACKUP source. { MFM, 0x00, 12 }, { SECTOR_LOOP_END }, { MFM, 0x4e, 1 }, // UNVERIFIED - ('esq_16' has 170 x $4e) { END } }; rx50img_format::rx50img_format() { } const char *rx50img_format::name() const { return "img"; } const char *rx50img_format::description() const { return "DEC Rainbow 100 floppy image"; } const char *rx50img_format::extensions() const { return "img"; } bool rx50img_format::supports_save() const { return true; } void rx50img_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t §or_count) { head_count = 1; uint32_t expected_size = 0; uint64_t size = io_generic_size(io); track_count = 80; sector_count = 10; expected_size = 512 * track_count * head_count * sector_count; if (size == expected_size) // standard format has 409600 byte return; /* track_count = 40; sector_count = 9; // [VT 180] expected_size = 512 * track_count * head_count * sector_count; if (size == expected_size) return; track_count = 40; sector_count = 8; // [DOS] expected_size = 512 * track_count * head_count * sector_count; if (size == expected_size) return; */ track_count = head_count = sector_count = 0; } int rx50img_format::identify(io_generic *io, uint32_t form_factor) { uint8_t track_count, head_count, sector_count; find_size(io, track_count, head_count, sector_count); if(track_count) return 50; return 0; } // /* Sectors are numbered 1 to 10 */ bool rx50img_format::load(io_generic *io, uint32_t form_factor, floppy_image *image) { uint8_t track_count, head_count, sector_count; find_size(io, track_count, head_count, sector_count); if(track_count == 0) return false; uint8_t sectdata[10*512]; desc_s sectors[10]; for(int i=0; iset_variant(floppy_image::SSQD); return true; } bool rx50img_format::save(io_generic *io, floppy_image *image) { int track_count, head_count, sector_count; get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count); if(track_count != 80) track_count = 80; // Happens for a fully unformatted floppy if(!head_count) head_count = 1; if(sector_count == 9) // [VT180] 9 sector format : no save! return false; if(sector_count != 10) // either 8 or 10 sectors sector_count = 10; // [STANDARD] /* if(sector_count != 10) // either 8 or 10 sectors { if(sector_count == 8) { track_count = 40; // [DOS] } else { sector_count = 10; // [STANDARD] } } */ uint8_t sectdata[11*512]; int track_size = sector_count*512; for(int track=0; track < track_count; track++) { for(int head=0; head < head_count; head++) { get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata); io_generic_write(io, sectdata, (track*head_count + head)*track_size, track_size); } } return true; } const floppy_format_type FLOPPY_RX50IMG_FORMAT = &floppy_image_format_creator; /* // Native 400K format (80 T * 10 S * 512 bytes) on 'quad density' RX50 drives // ( 5.25" single sided; 300 rpm; MFM 250 kbps; 96 - 100 tpi ). // // The BIOS can also * read * VT-180 disks and access MS-DOS 160 k disks (R + W) // ( 40 tracks; single sided with 9 or 8 sectors per track ) static LEGACY_FLOPPY_OPTIONS_START( dec100_floppy ) LEGACY_FLOPPY_OPTION( dec100_floppy, "td0", "Teledisk floppy disk image", td0_dsk_identify, td0_dsk_construct, td0_dsk_destruct, nullptr ) LEGACY_FLOPPY_OPTION( dec100_floppy, "img", "DEC Rainbow 100", basicdsk_identify_default, basicdsk_construct_default, nullptr, HEADS([1]) TRACKS(40/[80]) SECTORS(8/9/[10]) SECTOR_LENGTH([512]) INTERLEAVE([0]) FIRST_SECTOR_ID([1]) ) LEGACY_FLOPPY_OPTIONS_END */