summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/s100/vectordualmode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/s100/vectordualmode.cpp')
-rw-r--r--src/devices/bus/s100/vectordualmode.cpp358
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")