diff options
Diffstat (limited to 'src/devices/bus/s100/vectordualmode.cpp')
-rw-r--r-- | src/devices/bus/s100/vectordualmode.cpp | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/src/devices/bus/s100/vectordualmode.cpp b/src/devices/bus/s100/vectordualmode.cpp new file mode 100644 index 00000000000..fbf66d0d06e --- /dev/null +++ b/src/devices/bus/s100/vectordualmode.cpp @@ -0,0 +1,358 @@ +// license:BSD-3-Clause +// copyright-holders:Eric Anderson +/*************************************************************************** + +Vector Graphic had two related disk controllers for the Vector 4. There was +the "dual-mode" ST506-interface HDD/5.25" FDD controller and a stripped-down +5.25" FDD-only controller. Both can handle four FDD. The dual-mode version +supports a HDD as drive 0, replacing a FDD when used. + +The floppy and hard drive formatting is not IBM compatible. Instead they are +based on the Micropolis MFM hard-sectored format which starts and ends the +sector with 0x00 preamble and postable bytes and starts sector data with a +0xFF sync byte. The FDD has 16 hard sectors, but the HDD uses a normal +soft-sectored drive with a PLL on the controller to emulate 32 hard sectors. +No abnormal MFM clock bits are used. + +https://www.bitsavers.org/pdf/vectorGraphic/hardware/7200-1200-02-1_Dual-Mode_Disk_Controller_Board_Engineering_Documentation_Feb81.pdf +https://archive.org/details/7200-0001-vector-4-technical-information-sep-82 + +TODO: +- HDD support +- ECC + +****************************************************************************/ + +#include "emu.h" +#include "vectordualmode.h" + +#include "formats/vgi_dsk.h" + +static const attotime half_bitcell_size = attotime::from_usec(2); + +/* Interleave 8 bits with zeros. abcdefgh -> 0a0b0c0d0e0f0g0h */ +static int deposit8(int data) +{ + int d = data; + d = ((d & 0xf0) << 4) | (d & 0x0f); + d = ((d << 2) | d) & 0x3333; + d = ((d << 1) | d) & 0x5555; + return d; +} + +static uint16_t mfm_byte(uint8_t data, unsigned int prev_data) +{ + const unsigned int ext_data = data | (prev_data << 8); + const unsigned int clock = ~(ext_data | (ext_data >> 1)); + return (deposit8(clock) << 1) | deposit8(ext_data); +} + +static uint8_t unmfm_byte(uint16_t mfm) +{ + unsigned int d = mfm; + d &= 0x5555; + d = ((d >> 1) | d) & 0x3333; + d = ((d >> 2) | d) & 0x0f0f; + d = ((d >> 4) | d) & 0x00ff; + return d; +} + +s100_vector_dualmode_device::s100_vector_dualmode_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, S100_VECTOR_DUALMODE, tag, owner, clock) + , device_s100_card_interface(mconfig, *this) + , m_floppy(*this, "floppy%u", 0U) + , m_ram{0} + , m_cmar(0) + , m_drive(0) + , m_sector(0) + , m_fdd_sector_counter(0xf) + , m_read(false) + , m_busy(false) + , m_last_sector_pulse(attotime::zero) + , m_pll() + , m_byte_timer(nullptr) + , m_pending_byte(0) + , m_pending_size(0) +{ +} + +TIMER_CALLBACK_MEMBER(s100_vector_dualmode_device::motor_off) +{ + for (int i = 0; i < m_floppy.size(); i++) { + floppy_image_device* flop = m_floppy[m_drive]->get_device(); + if (flop) + flop->mon_w(1); + } + m_byte_timer->enable(false); + m_busy = false; +} + +bool s100_vector_dualmode_device::hdd_selected() +{ + // TODO: HDD support + return m_drive == 0 && false; +} + +uint8_t s100_vector_dualmode_device::s100_sinp_r(offs_t offset) +{ + if (m_busy) + return 0xff; + // 7200-1200-02-1 page 16 (1-10) + uint8_t data; + if (offset == 0xc0) { // status (0) port + bool write_protect; // FDD + bool ready; // HDD + bool track0; + bool write_fault = false; // HDD + bool seek_complete; // HDD + bool loss_of_sync; // HDD + if (hdd_selected()) { + write_protect = false; + ready = true; + track0 = false; + seek_complete = true; + loss_of_sync = true; + } else { + floppy_image_device* flop = m_floppy[m_drive]->get_device(); + write_protect = flop && flop->wpt_r(); + ready = false; + track0 = flop && !flop->trk00_r(); + seek_complete = false; + loss_of_sync = false; + } + + data = (write_protect ? 0x01 : 0) + | (ready ? 0x02 : 0) + | (track0 ? 0x04 : 0) + | (write_fault ? 0x08 : 0) + | (seek_complete ? 0x10 : 0) + | (loss_of_sync ? 0x20 : 0) + | 0xc0; + } else if (offset == 0xc1) { // status (1) port + bool floppy_disk_selected; + bool controller_busy = m_busy; // returned early if true + bool motor_on; // FDD + bool type_of_hard_disk = true; + if (hdd_selected()) { + floppy_disk_selected = false; + motor_on = false; + } else { + floppy_disk_selected = true; + motor_on = m_motor_on_timer->enabled(); + } + data = (floppy_disk_selected ? 0x01 : 0) + | (controller_busy ? 0x02 : 0) + | (motor_on ? 0x04 : 0) + | (type_of_hard_disk ? 0x08 : 0) + | 0xf0; + } else if (offset == 0xc2) { // data port + data = m_ram[m_cmar]; + if (!machine().side_effects_disabled()) { + m_cmar++; + m_cmar &= 0x1ff; + } + } else if (offset == 0xc3) { // reset port + if (!machine().side_effects_disabled()) + m_cmar = 0; + data = 0xff; + } else { + data = 0xff; + } + return data; +} + +void s100_vector_dualmode_device::s100_sout_w(offs_t offset, uint8_t data) +{ + if (m_busy) + return; + // 7200-1200-02-1 page 14 (1-8) + if (offset == 0xc0) { // control (0) port + m_drive = BIT(data, 0, 2); + const uint8_t head = BIT(data, 2, 3); + const bool step = BIT(data, 5); + const bool step_in = BIT(data, 6); + //uint8_t low_current = BIT(data, 7); + + for (int i = 0; i < m_floppy.size(); i++) { + floppy_image_device* flop = m_floppy[m_drive]->get_device(); + if (flop) + flop->mon_w(0); + } + // WR0| triggers U60, a 74LS123 with 100uF cap and 100k res + m_motor_on_timer->adjust(attotime::from_usec(2819600)); + + floppy_image_device* flop = m_floppy[m_drive]->get_device(); + if (flop) { + flop->ss_w(head & 1); + // Software should not change other bits when pulsing step + flop->stp_w(!step); + flop->dir_w(!step_in); + flop->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&s100_vector_dualmode_device::floppy_index_cb, this)); + } + } else if (offset == 0xc1) { // control (1) port + m_sector = BIT(data, 0, 5); + m_read = BIT(data, 5); + } else if (offset == 0xc2) { // data port + m_ram[m_cmar++] = data; + m_cmar &= 0x1ff; + } else if (offset == 0xc3) { // start port + m_busy = m_motor_on_timer->enabled(); + } +} + +bool s100_vector_dualmode_device::get_next_bit(attotime &tm, const attotime &limit) +{ + int bit = m_pll.get_next_bit(tm, m_floppy[m_drive]->get_device(), limit); + if (bit < 0) + return false; + m_pending_byte <<= 1; + m_pending_byte |= bit; + m_pending_size++; + return true; +} + +void s100_vector_dualmode_device::floppy_index_cb(floppy_image_device *floppy, int state) +{ + if (hdd_selected() || m_floppy[m_drive]->get_device() != floppy) + return; + if (!state) + return; + attotime now = machine().time(); + // U25 74LS221: 61.9 KOhm * .22 uF * .75 + if (now - m_last_sector_pulse < attotime::from_nsec(10213500)) { + m_fdd_sector_counter = 0xf; + } else { + m_last_sector_pulse = now; + m_fdd_sector_counter++; + m_fdd_sector_counter &= 0xf; + start_of_sector(); + } +} + +void s100_vector_dualmode_device::start_of_sector() +{ + if (!m_busy) + return; + + if (m_byte_timer->enabled()) { + // op completed + m_byte_timer->enable(false); + m_busy = false; + if (m_read) + m_ram[274] = 0; // Ignore ECC + return; + } + + uint8_t cur_sector = m_fdd_sector_counter; + if (cur_sector == m_sector) { + if (m_read) { + m_pll.set_clock(half_bitcell_size); + m_pll.read_reset(machine().time()); + attotime tm; + attotime limit = machine().time() + half_bitcell_size*512; + while (get_next_bit(tm, limit)) {} // init PLL + limit += half_bitcell_size*16*30; + while (get_next_bit(tm, limit) && m_pending_byte != 0x5554) {} + if (m_pending_byte == 0x5554) { + m_pending_size = 1; + m_byte_timer->adjust(tm - machine().time()); + } + } else { + m_pending_size = 0; + m_byte_timer->adjust(attotime::zero); + } + } +} + +TIMER_CALLBACK_MEMBER(s100_vector_dualmode_device::byte_cb) +{ + if (m_read) { + if (m_pending_size == 16) { + m_pending_size = 0; + m_ram[m_cmar++] = unmfm_byte(m_pending_byte); + m_cmar &= 0x1ff; + } + attotime tm; + while (m_pending_size != 16 && get_next_bit(tm, attotime::never)) {} + m_byte_timer->adjust(tm - machine().time()); + } else { + if (m_pending_size == 16) { + attotime start_time = machine().time() - half_bitcell_size*m_pending_size; + attotime tm = start_time + attotime::from_usec(1); + attotime buf[8]; + int pos = 0; + while (m_pending_size) { + if (m_pending_byte & (1 << --m_pending_size)) + buf[pos++] = tm; + tm += half_bitcell_size; + } + floppy_image_device *floppy = m_floppy[m_drive]->get_device(); + if (floppy) + floppy->write_flux(start_time, machine().time(), pos, buf); + } + uint8_t last = m_cmar ? m_ram[m_cmar-1] : 0; + m_pending_byte = mfm_byte(m_ram[m_cmar++], last); + m_pending_size = 16; + m_cmar &= 0x1ff; + m_byte_timer->adjust(half_bitcell_size*16); + } +} + +void s100_vector_dualmode_device::device_start() +{ + m_motor_on_timer = timer_alloc(FUNC(s100_vector_dualmode_device::motor_off), this); + m_byte_timer = timer_alloc(FUNC(s100_vector_dualmode_device::byte_cb), this); + + for (auto& f : m_floppy) { + if (f->get_device()) + f->get_device()->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&s100_vector_dualmode_device::floppy_index_cb, this)); + } + + save_item(NAME(m_ram)); + save_item(NAME(m_cmar)); + save_item(NAME(m_drive)); + save_item(NAME(m_sector)); + save_item(NAME(m_fdd_sector_counter)); + save_item(NAME(m_read)); + save_item(NAME(m_busy)); + save_item(NAME(m_last_sector_pulse)); + save_item(NAME(m_pending_byte)); + save_item(NAME(m_pending_size)); +} + +void s100_vector_dualmode_device::device_reset() +{ + // POC| resets + // U9 + m_drive = 0; + // U18 + m_sector = 0; + m_read = false; + // U60 + m_motor_on_timer->enable(false); +} + +static void vector4_floppies(device_slot_interface &device) +{ + device.option_add("525", FLOPPY_525_QD); +} + +static void vector4_formats(format_registration &fr) +{ + fr.add_mfm_containers(); + fr.add(FLOPPY_VGI_FORMAT); +} + +void s100_vector_dualmode_device::device_add_mconfig(machine_config &config) +{ + FLOPPY_CONNECTOR(config, m_floppy[0], vector4_floppies, "525", vector4_formats).enable_sound(true); + m_floppy[0]->set_sectoring_type(floppy_image::H16); + FLOPPY_CONNECTOR(config, m_floppy[1], vector4_floppies, "525", vector4_formats).enable_sound(true); + m_floppy[1]->set_sectoring_type(floppy_image::H16); + FLOPPY_CONNECTOR(config, m_floppy[2], vector4_floppies, "525", vector4_formats).enable_sound(true); + m_floppy[2]->set_sectoring_type(floppy_image::H16); + FLOPPY_CONNECTOR(config, m_floppy[3], vector4_floppies, "525", vector4_formats).enable_sound(true); + m_floppy[3]->set_sectoring_type(floppy_image::H16); +} + +DEFINE_DEVICE_TYPE(S100_VECTOR_DUALMODE, s100_vector_dualmode_device, "vectordualmode", "Vector Dual-Mode Disk Controller") |