summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/vtech/memexp/floppy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/vtech/memexp/floppy.c')
-rw-r--r--src/devices/bus/vtech/memexp/floppy.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/src/devices/bus/vtech/memexp/floppy.c b/src/devices/bus/vtech/memexp/floppy.c
new file mode 100644
index 00000000000..ef169703035
--- /dev/null
+++ b/src/devices/bus/vtech/memexp/floppy.c
@@ -0,0 +1,298 @@
+// license:GPL-2.0+
+// copyright-holders:Dirk Best, Olivier Galibert
+/***************************************************************************
+
+ VTech Laser/VZ Floppy Controller Cartridge
+
+ Laser DD 20
+ Dick Smith Electronics X-7304
+
+***************************************************************************/
+
+#include "floppy.h"
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type FLOPPY_CONTROLLER = &device_creator<floppy_controller_device>;
+
+DEVICE_ADDRESS_MAP_START(map, 8, floppy_controller_device)
+ AM_RANGE(0, 0) AM_WRITE(latch_w)
+ AM_RANGE(1, 1) AM_READ(shifter_r)
+ AM_RANGE(2, 2) AM_READ(rd_r)
+ AM_RANGE(3, 3) AM_READ(wpt_r)
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+ROM_START( floppy )
+ ROM_REGION(0x3000, "software", 0)
+ ROM_LOAD("vzdos.rom", 0x0000, 0x2000, CRC(b6ed6084) SHA1(59d1cbcfa6c5e1906a32704fbf0d9670f0d1fd8b))
+ROM_END
+
+const rom_entry *floppy_controller_device::device_rom_region() const
+{
+ return ROM_NAME( floppy );
+}
+
+//-------------------------------------------------
+// machine_config_additions - device-specific
+// machine configurations
+//-------------------------------------------------
+
+static SLOT_INTERFACE_START( laser_floppies )
+ SLOT_INTERFACE("525", FLOPPY_525_SSSD)
+SLOT_INTERFACE_END
+
+static MACHINE_CONFIG_FRAGMENT( floppy_controller )
+ MCFG_MEMEXP_SLOT_ADD("mem")
+ MCFG_FLOPPY_DRIVE_ADD("0", laser_floppies, "525", floppy_image_device::default_floppy_formats)
+ MCFG_FLOPPY_DRIVE_ADD("1", laser_floppies, "525", floppy_image_device::default_floppy_formats)
+MACHINE_CONFIG_END
+
+machine_config_constructor floppy_controller_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( floppy_controller );
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// floppy_controller_device - constructor
+//-------------------------------------------------
+
+floppy_controller_device::floppy_controller_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, FLOPPY_CONTROLLER, "Laser/VZ Floppy Disk Controller", tag, owner, clock, "laserfdc", __FILE__),
+ device_memexp_interface(mconfig, *this),
+ m_memexp(*this, "mem"),
+ m_floppy0(*this, "0"),
+ m_floppy1(*this, "1")
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void floppy_controller_device::device_start()
+{
+ save_item(NAME(m_latch));
+ save_item(NAME(m_shifter));
+ save_item(NAME(m_latching_inverter));
+ save_item(NAME(m_current_cyl));
+ save_item(NAME(m_last_latching_inverter_update_time));
+ save_item(NAME(m_write_start_time));
+ save_item(NAME(m_write_position));
+
+ // TODO: save m_write_buffer and rebuild m_floppy after load
+
+ UINT8 *bios = memregion("software")->base();
+
+ // Obvious bugs... must have worked by sheer luck and very subtle
+ // timings. Our current z80 is not subtle enough.
+
+ bios[0x1678] = 0x75;
+ bios[0x1688] = 0x85;
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void floppy_controller_device::device_reset()
+{
+ m_memexp->set_io_space(m_slot->m_io);
+ m_memexp->set_program_space(m_slot->m_program);
+
+ m_slot->m_program->install_rom(0x4000, 0x5fff, memregion("software")->base());
+
+ m_slot->m_io->install_device(0x10, 0x1f, *this, &floppy_controller_device::map);
+
+ m_latch = 0x00;
+ m_floppy = NULL;
+ m_current_cyl = 0;
+ m_shifter = 0x00;
+ m_latching_inverter = false;
+ m_last_latching_inverter_update_time = machine().time();
+ m_write_start_time = attotime::never;
+ m_write_position = 0;
+ memset(m_write_buffer, 0, sizeof(m_write_buffer));
+}
+
+
+//**************************************************************************
+// IMPLEMENTATION
+//**************************************************************************
+
+// latch at +0 is linked to:
+// bits 0-3: track step motor phases
+// bit 5: write data (flux reversal on every level change)
+// bit 6: !write request
+// bits 4,7: floppy select
+
+WRITE8_MEMBER(floppy_controller_device::latch_w)
+{
+ UINT8 diff = m_latch ^ data;
+ m_latch = data;
+
+ floppy_image_device *newflop = NULL;
+ if(m_latch & 0x10)
+ newflop = m_floppy0->get_device();
+ else if(m_latch & 0x80)
+ newflop = m_floppy1->get_device();
+
+ if(newflop != m_floppy) {
+ update_latching_inverter();
+ flush_writes();
+ if(m_floppy) {
+ m_floppy->mon_w(1);
+ m_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
+ }
+ if(newflop) {
+ newflop->set_rpm(85);
+ newflop->mon_w(0);
+ newflop->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(FUNC(floppy_controller_device::index_callback), this));
+ m_current_cyl = newflop->get_cyl() << 1;
+ }
+ m_floppy = newflop;
+ }
+
+ if(m_floppy) {
+ int cph = m_current_cyl & 3;
+ int pcyl = m_current_cyl;
+ if(!(m_latch & (1 << cph))) {
+ if(m_current_cyl < 84*2 && (m_latch & (1 << ((cph+1) & 3))))
+ m_current_cyl++;
+ if(m_current_cyl && (m_latch & (1 << ((cph+3) & 3))))
+ m_current_cyl--;
+ if(m_current_cyl != pcyl && !(m_current_cyl & 1)) {
+ m_floppy->dir_w(m_current_cyl < pcyl);
+ m_floppy->stp_w(true);
+ m_floppy->stp_w(false);
+ m_floppy->stp_w(true);
+ }
+ }
+ }
+
+ if(diff & 0x40) {
+ if(!(m_latch & 0x40)) {
+ m_write_start_time = machine().time();
+ m_write_position = 0;
+ if(m_floppy)
+ m_floppy->set_write_splice(m_write_start_time);
+
+ } else {
+ update_latching_inverter();
+ flush_writes();
+ m_write_start_time = attotime::never;
+ }
+ }
+ if(!(m_latch & 0x40) && (diff & 0x20)) {
+ if(m_write_position == ARRAY_LENGTH(m_write_buffer)) {
+ update_latching_inverter();
+ flush_writes(true);
+ }
+ m_write_buffer[m_write_position++] = machine().time();
+ }
+}
+
+
+// The read data line is connected to a flip/flop with inverted input
+// connected to the input. That means it inverts its value on every
+// floppy flux reversal. We'll call it a latching inverter.
+//
+// The latching inverter is connected to a 8-bits shift register. On
+// reading the shifter address we get:
+// - the inverted inverter output is shifted through the lsb of the shift register
+// - the inverter is cleared
+
+READ8_MEMBER(floppy_controller_device::shifter_r)
+{
+ update_latching_inverter();
+ m_shifter = (m_shifter << 1) | !m_latching_inverter;
+ m_latching_inverter = false;
+ return m_shifter;
+}
+
+
+// Linked to the latching inverter on bit 7, rest is floating
+READ8_MEMBER(floppy_controller_device::rd_r)
+{
+ update_latching_inverter();
+ return m_latching_inverter ? 0x80 : 0x00;
+}
+
+
+// Linked to wp signal on bit 7, rest is floating
+READ8_MEMBER(floppy_controller_device::wpt_r)
+{
+ return m_floppy && m_floppy->wpt_r() ? 0x80 : 0x00;
+}
+
+void floppy_controller_device::update_latching_inverter()
+{
+ attotime now = machine().time();
+ if(!m_floppy) {
+ m_last_latching_inverter_update_time = now;
+ return;
+ }
+
+ attotime when = m_last_latching_inverter_update_time;
+ for(;;) {
+ when = m_floppy->get_next_transition(when);
+ if(when == attotime::never || when > now)
+ break;
+ m_latching_inverter = !m_latching_inverter;
+ }
+ m_last_latching_inverter_update_time = now;
+}
+
+void floppy_controller_device::index_callback(floppy_image_device *floppy, int state)
+{
+ update_latching_inverter();
+ flush_writes(true);
+}
+
+void floppy_controller_device::flush_writes(bool keep_margin)
+{
+ if(!m_floppy || m_write_start_time == attotime::never)
+ return;
+
+ // Beware of time travel. Index pulse callback (which flushes)
+ // can be called with a machine().time() inferior to the last
+ // m_write_buffer value if the calling cpu instructions are not
+ // suspendable.
+
+ attotime limit = machine().time();
+ int kept_pos = m_write_position;
+ int kept_count = 0;
+ while(kept_pos > 0 && m_write_buffer[kept_pos-1] >= limit) {
+ kept_pos--;
+ kept_count++;
+ }
+
+ if(keep_margin) {
+ attotime last = kept_pos ? m_write_buffer[kept_pos-1] : m_write_start_time;
+ attotime delta = limit-last;
+ delta = delta / 2;
+ limit = limit - delta;
+ }
+ m_write_position -= kept_count;
+ if(m_write_position && m_write_buffer[0] == m_write_start_time) {
+ if(m_write_position)
+ memmove(m_write_buffer, m_write_buffer+1, sizeof(m_write_buffer[0])*(m_write_position-1));
+ m_write_position--;
+ }
+ m_floppy->write_flux(m_write_start_time, limit, m_write_position, m_write_buffer);
+ m_write_start_time = limit;
+
+ if(kept_count != 0)
+ memmove(m_write_buffer, m_write_buffer+kept_pos, kept_count*sizeof(m_write_buffer[0]));
+ m_write_position = kept_count;
+}