summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ss50/dc5.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/ss50/dc5.cpp')
-rw-r--r--src/devices/bus/ss50/dc5.cpp410
1 files changed, 310 insertions, 100 deletions
diff --git a/src/devices/bus/ss50/dc5.cpp b/src/devices/bus/ss50/dc5.cpp
index f258126292a..2522733d4e4 100644
--- a/src/devices/bus/ss50/dc5.cpp
+++ b/src/devices/bus/ss50/dc5.cpp
@@ -6,6 +6,25 @@
See: http://www.swtpc.com/mholley/DC_5/DC5_Index.htm
+ The DC5 features an extra control register compared with the DC4. This
+ extra register can be enabled or disabled via a jumper setting. This
+ register is write only, and reading back gives a version number of 0x02.
+
+ This register can be useful for setting the clock rate, and particular
+ clock rates are needed for difference floppy disk sizes and formats. The
+ clock rates are control by bits bits 2 and 3 as follows: 0x00 gives 1MHz,
+ 0x04 gives 1.2MHz, 0x80 gives 2MHz, and 0xC0 gives 2.4MHz.
+
+ This register also controls the single versus double density format
+ selection, via bit 5, so 0x00 gives double density and 0x20 gives single
+ density.
+
+ Software is typically not written for this DC5 extended control register,
+ but typically does not touch it. Thus is can be practical to work with a
+ purely single or double density disk by setting that format in this
+ control register. Most virtual disks appear to fit this pattern, rather
+ than mixing single and double density in the one disk.
+
**********************************************************************/
#include "emu.h"
@@ -25,10 +44,11 @@ public:
, m_floppy1(*this, "fdc:1")
, m_floppy2(*this, "fdc:2")
, m_floppy3(*this, "fdc:3")
- , m_clock(*this, "clock")
, m_interrupt_select(*this, "interrupt_select")
, m_address_mode(*this, "address_mode")
, m_two_control_regs(*this, "two_control_regs")
+ , m_ctrl_reg_bit7_side_select(*this, "ctrl_reg_bit7_side_select")
+ , m_expected_clock(*this, "expected_clock")
, m_expected_density(*this, "expected_density")
, m_expected_sectors(*this, "expected_sectors")
, m_track_zero_expected_sectors(*this, "track_zero_expected_sectors")
@@ -51,17 +71,20 @@ private:
DECLARE_FLOPPY_FORMATS(floppy_formats);
uint8_t m_fdc_status; // for floppy controller
- int m_floppy_motor_on;
+ uint8_t m_control_register;
+ uint8_t m_motor_timer_out;
emu_timer *m_floppy_motor_timer;
- floppy_image_device *m_fdc_floppy; // Current selected floppy.
+ floppy_image_device *m_fdc_selected_floppy; // Current selected floppy.
uint8_t m_fdc_side; // Current floppy side.
uint32_t m_fdc_clock; // Current clock frequency.
TIMER_CALLBACK_MEMBER(floppy_motor_callback);
void floppy_motor_trigger();
-
+ void control_register_update();
void validate_floppy_side(uint8_t cmd);
+ uint8_t validate_fdc_dden(uint8_t dden);
+ uint8_t validate_fdc_sso(uint8_t cmd);
uint8_t validate_fdc_sector_size(uint8_t cmd);
required_device<wd2797_device> m_fdc;
@@ -69,10 +92,11 @@ private:
required_device<floppy_connector> m_floppy1;
required_device<floppy_connector> m_floppy2;
required_device<floppy_connector> m_floppy3;
- required_ioport m_clock;
required_ioport m_interrupt_select;
required_ioport m_address_mode;
required_ioport m_two_control_regs;
+ required_ioport m_ctrl_reg_bit7_side_select;
+ required_ioport m_expected_clock;
required_ioport m_expected_density;
required_ioport m_expected_sectors;
required_ioport m_track_zero_expected_sectors;
@@ -96,16 +120,34 @@ static INPUT_PORTS_START( dc5 )
PORT_DIPSETTING(0, "No, DC4 compatible")
PORT_DIPSETTING(1, "Yes, DC5 extension")
- // single or double density 5.24" - 1.0MHz
- // 'standard' 3.5" - 1.2MHz
- // 3.5" hd - 2.0MHz
+ // This config setting allows checking of the FDC clock rate and
+ // overriding it to assist driver development. The DC5 supports
+ // programming of the clock rate via the second control register, and
+ // that setting can be validate using this setting. This setting also
+ // sets overrides the initial clock rate which is otherwise 1MHz.
+ //
+ // Some common rates:
+ // 5.25" single or double density - 1.0MHz
+ // 3.5" 'standard' - 1.2MHz
+ // 3.5" HD - 2.0MHz
// 8" 360rpm - 2.4MHz
- PORT_START("clock")
- PORT_DIPNAME(0xffffff, 1000000, "FDC clock")
- PORT_DIPSETTING(1000000, "1.0 MHz")
- PORT_DIPSETTING(1200000, "1.2 MHz")
- PORT_DIPSETTING(2000000, "2.0 MHz")
- PORT_DIPSETTING(2400000, "2.4 MHz")
+ PORT_START("expected_clock")
+ PORT_CONFNAME(0xffffff, 0, "FDC expected clock rate")
+ PORT_CONFSETTING(0, "-")
+ PORT_CONFSETTING(1000000, "1.0 MHz")
+ PORT_CONFSETTING(1200000, "1.2 MHz")
+ PORT_CONFSETTING(2000000, "2.0 MHz")
+ PORT_CONFSETTING(2400000, "2.4 MHz")
+
+ // It is not uncommon to see drivers using bit 7 of the control
+ // register as a side selection. This conflicts with the DC5, and
+ // earlier controllers in this series, which use bit 7 to inhibit
+ // drive selection. These drivers need to be corrected, but this
+ // option helps identify this issue and work around it.
+ PORT_START("ctrl_reg_bit7_side_select")
+ PORT_CONFNAME(0x01, 0, "Control register bit 7")
+ PORT_CONFSETTING(0, "Inhibits drive selection")
+ PORT_CONFSETTING(1, "Erroneous side select")
// Debug aid to hard code the density. The FLEX format uses single
// density for track zero. Many virtual disks 'fix' the format to be
@@ -206,17 +248,23 @@ void ss50_dc5_device::device_add_mconfig(machine_config &config)
void ss50_dc5_device::device_reset()
{
- uint32_t clock = m_clock->read();
+ // Initialize to the expected rate if any, otherwise to 1MHz.
+ uint32_t clock = m_expected_clock->read();
+ clock = clock ? clock : 1000000;
m_fdc->set_unscaled_clock(clock);
m_fdc_clock = clock;
+
+ // The reset state of DDEN for the DC5 is one, so selecting single density.
+ m_fdc->dden_w(1);
}
void ss50_dc5_device::device_start()
{
m_fdc_status = 0;
+ m_control_register = 0;
m_fdc_side = 0;
m_floppy_motor_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ss50_dc5_device::floppy_motor_callback),this));
- m_floppy_motor_on = 0;
+ m_motor_timer_out = 0;
}
@@ -224,21 +272,21 @@ void ss50_dc5_device::device_start()
void ss50_dc5_device::floppy_motor_trigger()
{
- m_floppy0->get_device()->mon_w(CLEAR_LINE);
- m_floppy1->get_device()->mon_w(CLEAR_LINE);
- m_floppy2->get_device()->mon_w(CLEAR_LINE);
- m_floppy3->get_device()->mon_w(CLEAR_LINE);
m_floppy_motor_timer->adjust(attotime::from_msec(30000));
- m_floppy_motor_on = 1;
+ if (!m_motor_timer_out)
+ {
+ m_motor_timer_out = 1;
+ control_register_update();
+ }
}
TIMER_CALLBACK_MEMBER(ss50_dc5_device::floppy_motor_callback)
{
- m_floppy0->get_device()->mon_w(ASSERT_LINE);
- m_floppy1->get_device()->mon_w(ASSERT_LINE);
- m_floppy2->get_device()->mon_w(ASSERT_LINE);
- m_floppy3->get_device()->mon_w(ASSERT_LINE);
- m_floppy_motor_on = 0;
+ if (m_motor_timer_out)
+ {
+ m_motor_timer_out = 0;
+ control_register_update();
+ }
}
// On a FDC command write, check that the floppy side is as expected given
@@ -261,9 +309,10 @@ void ss50_dc5_device::validate_floppy_side(uint8_t cmd)
if (m_fdc_side != expected_side)
{
- if (m_fdc_floppy)
+ logerror("%s Unexpected size %d for track %d sector %d expected side %d\n", machine().describe_context(), m_fdc_side, track, sector, expected_side);
+ if (m_fdc_selected_floppy)
{
- m_fdc_floppy->ss_w(expected_side);
+ m_fdc_selected_floppy->ss_w(expected_side);
m_fdc_side = expected_side;
}
}
@@ -275,9 +324,10 @@ void ss50_dc5_device::validate_floppy_side(uint8_t cmd)
if (m_fdc_side != expected_side)
{
- if (m_fdc_floppy)
+ logerror("%s Unexpected side %d for track %d sector %d expected side %d\n", machine().describe_context(), m_fdc_side, track, sector, expected_side);
+ if (m_fdc_selected_floppy)
{
- m_fdc_floppy->ss_w(expected_side);
+ m_fdc_selected_floppy->ss_w(expected_side);
m_fdc_side = expected_side;
}
}
@@ -285,16 +335,91 @@ void ss50_dc5_device::validate_floppy_side(uint8_t cmd)
}
}
+// Note the dden line is low for double density.
+uint8_t ss50_dc5_device::validate_fdc_dden(uint8_t dden)
+{
+ uint8_t expected_density = m_expected_density->read();
+ switch (expected_density)
+ {
+ case 1:
+ // Single density.
+ if (!dden)
+ logerror("%s Unexpected DDEN %d for single density\n", machine().describe_context(), dden);
+ return 1;
+ case 2:
+ {
+ // Double density with track zero single density.
+ uint8_t track = m_fdc->track_r();
+
+ if (track == 0)
+ {
+ // If this path is call on an update of the
+ // second control register then the track need
+ // not be accurate so this message might not
+ // be accurate.
+ if (!dden)
+ logerror("%s Unexpected DDEN %d for single density trak 0\n", machine().describe_context(), dden);
+ return 1;
+ }
+ if (dden)
+ logerror("%s Unexpected DDEN %d for double density\n", machine().describe_context(), dden);
+ return 0;
+ }
+ case 3:
+ // Pure double density.
+ if (dden)
+ logerror("%s Unexpected DDEN %d for double density\n", machine().describe_context(), dden);
+ return 0;
+ default:
+ return dden;
+ }
+}
+
+uint8_t ss50_dc5_device::validate_fdc_sso(uint8_t cmd)
+{
+ // The DC4 and DC5 invert the SSO output and wire it to the DDEN input
+ // to allow selection of single or double density.
+
+ // In the DC5 two-register mode the SSO pin does not control DDEN.
+ uint32_t two_regs = m_two_control_regs->read();
+ if (two_regs)
+ {
+ // How if drivers are attempting to use SSO to control the
+ // density then that might need correcting so report such
+ // usage.
+ logerror("%s Unexpected SSO flag set in two control registers mode\n", machine().describe_context());
+ return cmd;
+ }
+
+ // The SSO is only loaded for type II and III commands.
+ if ((cmd & 0xe1) == 0x80 ||
+ (cmd & 0xe0) == 0xa0 ||
+ (cmd & 0xf9) == 0xc0 ||
+ (cmd & 0xf9) == 0xe0 ||
+ (cmd & 0xf9) == 0xf0)
+ {
+ // The SSO output is inverted and input to DDEN.
+ uint8_t dden = !BIT(cmd, 1);
+ uint8_t sso = !validate_fdc_dden(dden);
+ cmd = (cmd & 0xfd) | (sso << 1);
+ }
+
+ return cmd;
+}
+
// The WD2797 supports an alternate interpretation of the sector size. Check
-// that the flag is as expected and return the corrected command if necessary.
+// that the flag is as expected for FLEX and return the corrected command if
+// necessary.
uint8_t ss50_dc5_device::validate_fdc_sector_size(uint8_t cmd)
{
if ((cmd & 0xe1) == 0x80 || (cmd & 0xe0) == 0xa0)
{
- // Check that the sector size L flag is set, as expected.
+ // Check that the sector length flag is set as expected.
uint8_t sector_length_default = cmd & 0x08;
- if (!sector_length_default)
+ if (sector_length_default != 0x08)
{
+ logerror("%s Unexpected sector length default %02x\n", machine().describe_context(), sector_length_default);
+ // Patch the sector length flag.
cmd |= 0x08;
}
}
@@ -330,49 +455,121 @@ WRITE_LINE_MEMBER( ss50_dc5_device::fdc_drq_w )
WRITE_LINE_MEMBER( ss50_dc5_device::fdc_sso_w )
{
- // The DC4 and DC5 invert the SSO output and wire it to the
- // DDEN input to allow selection of single or double density.
+ // The DC4 and DC5 invert the SSO output and wire it to the DDEN input
+ // to allow selection of single or double density.
- // A DC5 extension. In two-register mode the SSO pin does not
- // control DDEN.
- uint32_t two_regs = m_two_control_regs->read();
- if (two_regs)
- return;
+ // A DC5 extension. In two-register mode the SSO pin does not control
+ // DDEN.
+ uint32_t two_regs = m_two_control_regs->read();
+ if (two_regs)
+ return;
- // However there are a lot of boot loaders that will
- // not work properly with this, so allow other options.
- uint8_t expected_density = m_expected_density->read();
- switch (expected_density)
+ // However there are a lot of boot loaders that will
+ // not work properly with this, so allow other options.
+ uint8_t expected_density = m_expected_density->read();
+ switch (expected_density)
+ {
+ case 1:
+ // Single density.
+ if (state)
+ logerror("Unexpected SSO %d for single density\n", state);
+ m_fdc->dden_w(1);
+ break;
+ case 2:
{
- case 1:
- // Single density.
- m_fdc->dden_w(1);
- break;
- case 2:
- {
- // Double density with track zero single density.
- uint8_t track = m_fdc->track_r();
+ // Double density with track zero single density.
+ uint8_t track = m_fdc->track_r();
- if (track == 0)
- {
- m_fdc->dden_w(1);
- }
- else
- {
- m_fdc->dden_w(0);
- }
- break;
+ if (track == 0)
+ {
+ if (state)
+ logerror("Unexpected SSO %d for single density trak 0\n", state);
+ m_fdc->dden_w(1);
}
- case 3:
- // Pure double density.
+ else
+ {
+ if (!state)
+ logerror("Unexpected SSO %d for double density\n", state);
m_fdc->dden_w(0);
- break;
- default:
- m_fdc->dden_w(!state);
- break;
+ }
+ break;
+ }
+ case 3:
+ // Pure double density.
+ if (!state)
+ logerror("Unexpected SSO %d for double density\n", state);
+ m_fdc->dden_w(0);
+ break;
+ default:
+ m_fdc->dden_w(!state);
+ break;
+ }
+}
+
+// Called when state affected by the control register changes, when the
+// control register is modified, or when the motor starts or stops.
+void ss50_dc5_device::control_register_update()
+{
+ // The floppy drives do not gate the motor based on the select input,
+ // so the motors run, or not, irrespective of the drive selection.
+ uint8_t motor = m_motor_timer_out ? CLEAR_LINE : ASSERT_LINE;
+ m_floppy0->get_device()->mon_w(motor);
+ m_floppy1->get_device()->mon_w(motor);
+ m_floppy2->get_device()->mon_w(motor);
+ m_floppy3->get_device()->mon_w(motor);
+
+ // The DC2, DC3, DC4, DC5 have a drive inhibit feature controlled by
+ // bit 7, and the drives are de-selected if the motor timer has timed
+ // out.
+ uint8_t inhibit = BIT(m_control_register, 7);
+
+ // However it is common to see drivers using bit 7 for side selection,
+ // so offer any alternative interpretation as a work around.
+ uint8_t bit7_side_select = m_ctrl_reg_bit7_side_select->read();
+
+ floppy_image_device *floppy = nullptr;
+
+ if ((bit7_side_select || !inhibit) && m_motor_timer_out)
+ {
+ uint8_t drive = m_control_register & 3;
+ if (drive == 0) floppy = m_floppy0->get_device();
+ if (drive == 1) floppy = m_floppy1->get_device();
+ if (drive == 2) floppy = m_floppy2->get_device();
+ if (drive == 3) floppy = m_floppy3->get_device();
+ }
+
+ if (floppy != m_fdc_selected_floppy)
+ {
+ m_fdc->set_floppy(floppy);
+ m_fdc_selected_floppy = floppy;
+ }
+
+ if (floppy)
+ {
+ // The DC3, DC4, DC5 have a side select feature that is
+ // controlled by bit 6. The DC1 and DC2 do not have a floppy
+ // side output so did not support double sided disks.
+ uint8_t side = BIT(m_control_register, 6);
+
+ if (bit7_side_select)
+ {
+ uint8_t bit7 = BIT(m_control_register, 7);
+ if (bit7)
+ logerror("%s Unexpected use of DC5 control register bit 7 for side selection.\n", machine().describe_context());
+ // OR together the side selection bits. This appears
+ // the most useful choice because drivers in error are
+ // most likely just writing to the wrong bit rather
+ // than inconsistently to both bits. This allows
+ // incremental correction of driver while spotting
+ // remaining uses of bit 7 for side selection.
+ side |= bit7;
}
+ floppy->ss_w(side);
+ m_fdc_side = side;
+ }
}
+
//-------------------------------------------------
// register_read - read from a port register
//-------------------------------------------------
@@ -396,6 +593,9 @@ uint8_t ss50_dc5_device::register_read(offs_t offset)
{
uint32_t two_regs = m_two_control_regs->read();
+ // Note: access to this control register does not trigger the
+ // motor on and and does not reset the motor timer.
+
if (!two_regs || (offset & 2) == 0)
{
// Control register 8014 / e014 read.
@@ -444,60 +644,69 @@ void ss50_dc5_device::register_write(offs_t offset, uint8_t data)
// Control register(s) 8014-8017 E014-E017
uint32_t two_regs = m_two_control_regs->read();
+ // Note: access to this control register does not trigger the
+ // motor on and and does not reset the motor timer.
+
if (!two_regs || (offset & 2) == 0)
{
// Control register 8014 / e014 write.
- floppy_image_device *floppy = nullptr;
- uint8_t drive = data & 3;
-
- // The DC2, DC3, DC4, DC5 have a drive inhibit feature
- // controlled by bit 7. Have not seen software use that?
-
- if (drive == 0) floppy = m_floppy0->get_device();
- if (drive == 1) floppy = m_floppy1->get_device();
- if (drive == 2) floppy = m_floppy2->get_device();
- if (drive == 3) floppy = m_floppy3->get_device();
-
- if (floppy != m_fdc_floppy)
- {
- m_fdc->set_floppy(floppy);
- m_fdc_floppy = floppy;
- }
-
- if (floppy)
- {
- // The DC3, DC4, DC5 have a side select feature that
- // is controlled by bit 6. The DC1 and DC2 do not have
- // a floppy side output.
- uint8_t side = BIT(data, 6);
- floppy->ss_w(side);
- m_fdc_side = side;
- }
+ m_control_register = data;
+ control_register_update();
}
if (two_regs && (offset & 2) == 2)
{
// DC5 extended control register 8016 / E016.
-#if 0
- // TODO DC5 clocking extensions.
+
// Osc20 and osc12 control a clock divider.
uint8_t osc12 = BIT(data, 2);
uint8_t osc20 = BIT(data, 3);
uint32_t clock = 1000000;
- if (osc20)
+
+ // Note: supporting 2.4MHz is here is an extension to
+ // the published DC5 design, and appears necessary to
+ // support 360 rpm 8" drives. The case in which osc20
+ // and osc12 are high generates 2.0MHz in the
+ // published design, and that is redundant and is used
+ // here to generate 2.4MHz. The DC5 is an open design
+ // using a PLD and this variation can be programmed
+ // into the DC5 hardware. The 2.4MHz rate appears to
+ // be outside the limit for the WD2797 which appears
+ // to be 2.173MHz. TODO explore this further; were the
+ // 8" drives run at 300rpm; is there an error in the
+ // rates elsewhere?
+ if (osc20 && osc12)
+ clock = 2400000;
+ else if (osc20)
clock = 2000000;
else if (osc12)
clock = 1200000;
- m_fdc->set_unscaled_clock(clock);
- m_fdc_clock = clock;
+ // Compare to the expected clock rate if any.
+ uint32_t expected_clock = m_expected_clock->read();
+ if (expected_clock && clock != expected_clock)
+ {
+ logerror("%s Unexpected clock rate of %dHz expected %dHz.\n", machine().describe_context(), clock, expected_clock);
+ clock = expected_clock;
+ }
+
+ if (clock != m_fdc_clock)
+ {
+ m_fdc->set_unscaled_clock(clock);
+ m_fdc_clock = clock;
+ }
+
+#if 0
// Connects to the WD2979 5/8 pin.
// TODO is this needed for the emulator?
uint8_t five_or_eight = BIT(data, 4);
#endif
+
// In this two-register mode the FDC SSO output pin
- // does not control DDEN.
- uint8_t dden = BIT(data, 5);
+ // does not control DDEN. The bit 5 value is inverted
+ // and input to DDEN.
+ uint8_t dden = !BIT(data, 5);
+ dden = validate_fdc_dden(dden);
m_fdc->dden_w(dden);
}
}
@@ -511,6 +720,7 @@ void ss50_dc5_device::register_write(offs_t offset, uint8_t data)
if (offset == 0) {
validate_floppy_side(data);
data = validate_fdc_sector_size(data);
+ data = validate_fdc_sso(data);
}
m_fdc->wd2797_device::write(offset, data);