From 5bf175cb0599ca0f76621bb6ba5ac6195977e376 Mon Sep 17 00:00:00 2001 From: AJR Date: Thu, 24 Dec 2020 09:56:49 -0500 Subject: New device: Fairchild 4702B Bit Rate Generator --- scripts/src/machine.lua | 12 ++ scripts/target/mame/arcade.lua | 1 + scripts/target/mame/mess.lua | 1 + src/devices/machine/f4702.cpp | 274 +++++++++++++++++++++++++++++++++++++++++ src/devices/machine/f4702.h | 76 ++++++++++++ src/mame/drivers/altair.cpp | 26 +++- src/mame/drivers/mccpm.cpp | 100 ++++++++++++--- 7 files changed, 466 insertions(+), 24 deletions(-) create mode 100644 src/devices/machine/f4702.cpp create mode 100644 src/devices/machine/f4702.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 8d4069947f0..6a31c46a513 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1296,6 +1296,18 @@ if (MACHINES["F3853"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/f4702.h,MACHINES["F4702"] = true +--------------------------------------------------- + +if (MACHINES["F4702"]~=null) then + files { + MAME_DIR .. "src/devices/machine/f4702.cpp", + MAME_DIR .. "src/devices/machine/f4702.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/fga002.h,MACHINES["FGA002"] = true diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 6b40790f785..18f01cec358 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -489,6 +489,7 @@ MACHINES["EEPROMDEV"] = true --MACHINES["ER1400"] = true MACHINES["ER2055"] = true MACHINES["F3853"] = true +--MACHINES["F4702"] = true --MACHINES["HD63450"] = true --MACHINES["HD64610"] = true MACHINES["I2CMEM"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index b584cbaebe2..16df2ae0eb0 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -511,6 +511,7 @@ MACHINES["ER1400"] = true MACHINES["ER2055"] = true MACHINES["EXORTERM"] = true MACHINES["F3853"] = true +MACHINES["F4702"] = true MACHINES["HD63450"] = true MACHINES["HD64610"] = true MACHINES["HP_DC100_TAPE"] = true diff --git a/src/devices/machine/f4702.cpp b/src/devices/machine/f4702.cpp new file mode 100644 index 00000000000..40a1d0be517 --- /dev/null +++ b/src/devices/machine/f4702.cpp @@ -0,0 +1,274 @@ +// license:BSD-3-Clause +// copyright-holders:AJR +/**************************************************************************** + + Fairchild 4702B Programmable Bit Rate Generator + + Originally numbered 34702 in Fairchild's isoplanar CMOS series (whose + lower-numbered products were logical equivalents of RCA CD4000 and + Motorola MC14500 series devices), this BRG incorporates some unusually + sophisticated features into its 16-pin package, which may be why + Fairchild classified it as a LSI IC. + + The standard 2.4576 MHz master clock, either generated from a crystal + oscillator (Ix, Ox) or provided as a TTL input (CP), is first prescaled + by dividing by 8 and then divided further down through a network of + counters with differing periods. These counters are tapped at 13 points, + each frequency being 16 times a common baud rate. The rate output (Z), + whose changes are synchronized with the master clock, is multiplexed + from any one of these internal sources or an external rate input (Im) by + a 4-bit select code (S0, S1, S2, S3). + + The buffered clock output (CO) and the three prescaler divisions (Q0, + Q1, Q2) are not only conveniently available externally for clocking + additional 4702s or other devices, but can also be exploited to + generate 8 baud rates simultaneously, with the aid of a Fairchild 93L34 + (or compatible) addressable latch for demultiplexing the channels. In + this configuration, the CO, Q and Z outputs are respectively tied to the + latch's enable, address and data inputs, and the Q outputs are directly + or indirectly fed back into the S inputs. (Fairchild even suggested + scanning 9LS170 register files with the Q outputs so as to make each + rate individually programmable.) + + The rate select codes are arranged so that connecting a simple five- + point switch to the S inputs can obtain 110, 150, 300, 1200 or 2400 + baud, and only three binary switches are needed to select between all + five of those rates plus 1800, 4800 and 9600 baud. 19200 baud can also + be obtained by tapping the Q2 output, but no more than 4 internally + multiplexed rates can be used at once if Im is generated from Q2. + + All inputs and outputs except Ix and Ox are TTL-compatible, with the + inputs also having internal pull-ups so switches can be connected + directly. + + To enable the Ix/Ox oscillator circuit, Ecp must be high and CP must + remain low, since Ecp and CP both being high places the chip in a + continuous reset mode (except for the CO output). If Ecp is brought + low, the initialization circuit produces an internal master reset + pulse the first time CP goes high. Neither of these two reset methods + was used much in practice. + + Intersil, one of not many companies to second-source the original 4702, + later produced IM4712, a minor variant which requires fewer external + discrete components to drive the crystal oscillator but is otherwise + logically identical. + +****************************************************************************/ + +#include "emu.h" +#include "machine/f4702.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(F4702, f4702_device, "f4702", "Fairchild 4702B Bit Rate Generator") + + +//************************************************************************** +// DEVICE CONSTRUCTION AND INITIALIZATION +//************************************************************************** + +//------------------------------------------------- +// f4702_device - constructor +//------------------------------------------------- + +f4702_device::f4702_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, F4702, tag, owner, clock) + , device_execute_interface(mconfig, *this) + , m_s_callback(*this) + , m_z_callback(*this) + , m_main_counter(0) + , m_div_200_50(0) + , m_div_134_5(0) + , m_div_110(0) + , m_div_1800(0) + , m_im(true) + , m_s(0) + , m_icount(0) +{ +} + + +//------------------------------------------------- +// device_resolve_objects - resolve objects that +// may be needed for other devices to set +// initial conditions at start time +//------------------------------------------------- + +void f4702_device::device_resolve_objects() +{ + // resolve callbacks + m_s_callback.resolve_safe(15); + m_z_callback.resolve_safe(); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void f4702_device::device_start() +{ + set_icountptr(m_icount); + + save_item(NAME(m_main_counter)); + save_item(NAME(m_div_200_50)); + save_item(NAME(m_div_134_5)); + save_item(NAME(m_div_110)); + save_item(NAME(m_div_1800)); + save_item(NAME(m_im)); + save_item(NAME(m_s)); +} + + +//------------------------------------------------- +// reset_counters - optional master reset +//------------------------------------------------- + +void f4702_device::reset_counters() +{ + // Reset counter network + m_main_counter = 0; + m_div_200_50 = 0; + m_div_134_5 = 0; + m_div_1800 = 0; + + // Reset Q and Z outputs + m_z_callback(0, 0); +} + + +//************************************************************************** +// RATE GENERATION +//************************************************************************** + +//------------------------------------------------- +// im_w - set external rate input +//------------------------------------------------- + +WRITE_LINE_MEMBER(f4702_device::im_w) +{ + m_im = state; +} + + +//------------------------------------------------- +// z_output - recalculate output state +//------------------------------------------------- + +bool f4702_device::z_output() const +{ + // Select Z output from one of the 13 counter taps or the external input + switch (m_s) + { + // S3–S0 = LLLL or LLLH: multiplexed input + case 0: + case 1: + default: + return m_im; + + // S3–S0 = LLHL: 50 baud + case 2: + return m_div_200_50 >= 12; + + // S3–S0 = LLHH: 75 baud + case 3: + return BIT(m_main_counter, 10); + + // S3–S0 = LHLL: 134.5 baud (-0.87% error) + case 4: + return m_div_134_5 >= 9; + + // S3–S0 = LHLH: 200 baud + case 5: + return (m_div_200_50 % 6) >= 3; + + // S3–S0 = LHHL: 600 baud + case 6: + return BIT(m_main_counter, 7); + + // S3–S0 = LHHH or HHLL: 2400 baud + case 7: + case 12: + return BIT(m_main_counter, 5); + + // S3–S0 = HLLL: 9600 baud + case 8: + return BIT(m_main_counter, 3); + + // S3–S0 = HLLH: 4800 baud + case 9: + return BIT(m_main_counter, 4); + + // S3–S0 = HLHL: 1800 baud + case 10: + return (m_div_1800 % 5) >= 2; + + // S3–S0 = HLHH: 1200 baud + case 11: + return BIT(m_main_counter, 6); + + // S3–S0 = HHLH: 300 baud + case 13: + return BIT(m_main_counter, 8); + + // S3–S0 = HHHL: 150 baud + case 14: + return BIT(m_main_counter, 9); + + // S3–S0 = HHHH: 110 baud (-0.83% error) + case 15: + return m_div_110 >= 11; + } +} + + +//------------------------------------------------- +// execute_run - execute a timeslice's worth of +// counting +//------------------------------------------------- + +void f4702_device::execute_run() +{ + do { + // Drive the main scan and frequency counter + m_main_counter++; + if ((m_main_counter & 0x00f) == 0) + { + // Divide 9600 baud by 16/3 (16 = 5 + 5 + 6) + m_div_1800++; + if (m_div_1800 >= 16) + m_div_1800 = 0; + + if ((m_main_counter & 0x03f) == 0) + { + // Divide 2400 baud by 18 + m_div_134_5++; + if (m_div_134_5 >= 18) + m_div_134_5 = 0; + + // Divide 2400 baud by 22 as well + m_div_110++; + if (m_div_110 >= 22) + m_div_110 = 0; + + if ((m_main_counter & 0x07f) == 0) + { + // Divide 1200 baud by 6 and then again by 4 + m_div_200_50++; + if (m_div_200_50 >= 24) + m_div_200_50 = 0; + } + } + } + + // Update Q and Z outputs + m_z_callback(m_main_counter & 0x007, z_output()); + + // S3–S0 inputs are valid on the rising edge of CO + m_s = m_s_callback(m_main_counter & 0x007) & 15; + } while (--m_icount > 0); +} diff --git a/src/devices/machine/f4702.h b/src/devices/machine/f4702.h new file mode 100644 index 00000000000..c48601b1e3c --- /dev/null +++ b/src/devices/machine/f4702.h @@ -0,0 +1,76 @@ +// license:BSD-3-Clause +// copyright-holders:AJR +/**************************************************************************** + + Fairchild 4702B Programmable Bit Rate Generator + +***************************************************************************** + ____ ____ + Q0 1 |* \_/ | 16 Vdd + Q1 2 | | 15 Im + Q2 3 | | 14 S0 + _Ecp 4 | | 13 S1 + CP 5 | 4702BPC | 12 S2 + Ox 6 | | 11 S3 + Ix 7 | | 10 Z + Vss 8 |___________| 9 CO + +****************************************************************************/ + +#ifndef MAME_MACHINE_F4702_H +#define MAME_MACHINE_F4702_H + +#pragma once + + +// ======================> f4702_device + +class f4702_device : public device_t, public device_execute_interface +{ +public: + f4702_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + + // callback configuration + auto s_callback() { return m_s_callback.bind(); } + auto z_callback() { return m_z_callback.bind(); } + + // external rate input + DECLARE_WRITE_LINE_MEMBER(im_w); + + // reset control (optional) + void reset_counters(); + +protected: + // device-level overrides + virtual void device_resolve_objects() override; + virtual void device_start() override; + + // device_execute_interface overrides + virtual void execute_run() override; + +private: + // internal helpers + bool z_output() const; + + // input & output callbacks + devcb_read8 m_s_callback; + devcb_write8 m_z_callback; + + // internal counters + u16 m_main_counter; + u8 m_div_200_50; + u8 m_div_134_5; + u8 m_div_110; + u8 m_div_1800; + + // miscellaneous state + bool m_im; + u8 m_s; + s32 m_icount; +}; + +// device type declaration +DECLARE_DEVICE_TYPE(F4702, f4702_device) + +#endif // MAME_MACHINE_F4702_H + diff --git a/src/mame/drivers/altair.cpp b/src/mame/drivers/altair.cpp index 6ec9b56fa7c..d61d7979bce 100644 --- a/src/mame/drivers/altair.cpp +++ b/src/mame/drivers/altair.cpp @@ -28,7 +28,7 @@ //#include "bus/s100/s100.h" #include "cpu/i8085/i8085.h" #include "machine/6850acia.h" -#include "machine/clock.h" +#include "machine/f4702.h" #include "imagedev/snapquik.h" @@ -74,6 +74,22 @@ void altair_state::io_map(address_map &map) /* Input ports */ static INPUT_PORTS_START( altair ) + PORT_START("BAUD") + PORT_DIPNAME(0xf, 0x8, "Bit Rate") PORT_DIPLOCATION("S3-S0:4,3,2,1") + PORT_DIPSETTING(0x0, "External Rate") + PORT_DIPSETTING(0x2, "50") + PORT_DIPSETTING(0x3, "75") + PORT_DIPSETTING(0xf, "110") + PORT_DIPSETTING(0x4, "134.5") + PORT_DIPSETTING(0xe, "150") + PORT_DIPSETTING(0x5, "200") + PORT_DIPSETTING(0xd, "300") + PORT_DIPSETTING(0x6, "600") + PORT_DIPSETTING(0xb, "1200") + PORT_DIPSETTING(0xa, "1800") + PORT_DIPSETTING(0x7, "2400") + PORT_DIPSETTING(0x9, "4800") + PORT_DIPSETTING(0x8, "9600") INPUT_PORTS_END @@ -113,10 +129,12 @@ void altair_state::altair(machine_config &config) rs232.rxd_handler().set("acia", FUNC(acia6850_device::write_rxd)); rs232.dcd_handler().set("acia", FUNC(acia6850_device::write_dcd)); rs232.cts_handler().set("acia", FUNC(acia6850_device::write_cts)); + rs232.txc_handler().set("brg", FUNC(f4702_device::im_w)); // molex pin 7 to be connected to cable pin 15 - clock_device &uart_clock(CLOCK(config, "uart_clock", 153600)); // TODO: this is set using jumpers S3/S2/S1/S0 - uart_clock.signal_handler().set("acia", FUNC(acia6850_device::write_txc)); - uart_clock.signal_handler().append("acia", FUNC(acia6850_device::write_rxc)); + f4702_device &brg(F4702(config, "brg", 2.4576_MHz_XTAL)); + brg.s_callback().set_ioport("BAUD"); + brg.z_callback().set("acia", FUNC(acia6850_device::write_txc)); + brg.z_callback().append("acia", FUNC(acia6850_device::write_rxc)); /* quickload */ QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(altair_state::quickload_cb)); diff --git a/src/mame/drivers/mccpm.cpp b/src/mame/drivers/mccpm.cpp index 4879fc6f3cc..4929661b792 100644 --- a/src/mame/drivers/mccpm.cpp +++ b/src/mame/drivers/mccpm.cpp @@ -30,7 +30,7 @@ No software to test with, so we'll never know if the FDC works. #include "cpu/z80/z80.h" #include "machine/z80pio.h" #include "machine/z80sio.h" -#include "machine/clock.h" +#include "machine/f4702.h" #include "machine/bankdev.h" #include "machine/wd_fdc.h" #include "bus/rs232/rs232.h" @@ -44,13 +44,17 @@ public: : driver_device(mconfig, type, tag) , m_maincpu(*this, "maincpu") , m_bank(*this, "bankdev_map") - , m_fdc (*this, "fdc") - , m_floppy0(*this, "fdc:0") - , m_floppy1(*this, "fdc:1") + , m_fdc(*this, "fdc") + , m_fdd(*this, "fdc:%u", 0U) + , m_brg(*this, "brg%u", 1U) { } void mccpm(machine_config &config); +protected: + virtual void machine_reset() override; + virtual void machine_start() override; + private: void io_map(address_map &map); void mem_map(address_map &map); @@ -58,15 +62,16 @@ private: void port44_w(u8); u8 port44_r(); void fdc_irq(bool); + template void bd_q_w(offs_t offset, u8 data); + u8 m_fdc_status; floppy_image_device *m_floppy; - void machine_reset() override; - void machine_start() override; + required_device m_maincpu; required_device m_bank; required_device m_fdc; - required_device m_floppy0; - required_device m_floppy1; + required_device_array m_fdd; + required_device_array m_brg; }; @@ -97,16 +102,49 @@ void mccpm_state::io_map(address_map &map) /* Input ports */ static INPUT_PORTS_START( mccpm ) + PORT_START("BAUD1") + PORT_DIPNAME(0xf, 0x8, "Baud Rate B (Printer)") PORT_DIPLOCATION("S:5,6,7,8") + PORT_DIPSETTING(0x2, "50") + PORT_DIPSETTING(0x3, "75") + PORT_DIPSETTING(0xf, "110") + PORT_DIPSETTING(0x4, "134.5") + PORT_DIPSETTING(0xe, "150") + PORT_DIPSETTING(0x5, "200") + PORT_DIPSETTING(0xd, "300") + PORT_DIPSETTING(0x6, "600") + PORT_DIPSETTING(0xb, "1200") + PORT_DIPSETTING(0xa, "1800") + PORT_DIPSETTING(0x7, "2400") + PORT_DIPSETTING(0x9, "4800") + PORT_DIPSETTING(0x8, "9600") + PORT_DIPSETTING(0x0, "19200") + + PORT_START("BAUD2") + PORT_DIPNAME(0xf, 0x8, "Baud Rate A (Terminal)") PORT_DIPLOCATION("S:1,2,3,4") + PORT_DIPSETTING(0x2, "50") + PORT_DIPSETTING(0x3, "75") + PORT_DIPSETTING(0xf, "110") + PORT_DIPSETTING(0x4, "134.5") + PORT_DIPSETTING(0xe, "150") + PORT_DIPSETTING(0x5, "200") + PORT_DIPSETTING(0xd, "300") + PORT_DIPSETTING(0x6, "600") + PORT_DIPSETTING(0xb, "1200") + PORT_DIPSETTING(0xa, "1800") + PORT_DIPSETTING(0x7, "2400") + PORT_DIPSETTING(0x9, "4800") + PORT_DIPSETTING(0x8, "9600") + PORT_DIPSETTING(0x0, "19200") INPUT_PORTS_END void mccpm_state::port44_w(u8 data) { m_floppy = nullptr; if (BIT(data, 1)) - m_floppy = m_floppy1->get_device(); + m_floppy = m_fdd[1]->get_device(); else if (BIT(data, 0)) - m_floppy = m_floppy0->get_device(); + m_floppy = m_fdd[0]->get_device(); m_fdc->set_floppy(m_floppy); @@ -136,6 +174,12 @@ void mccpm_state::fdc_irq(bool state) m_maincpu->set_input_line(0, state ? ASSERT_LINE : CLEAR_LINE); } +template void mccpm_state::bd_q_w(offs_t offset, u8 data) +{ + // "19200 extern" obtained by connecting Q2 to Im + m_brg[N]->im_w(BIT(offset, 2)); +} + void mccpm_state::machine_reset() { m_bank->set_bank(0); @@ -168,20 +212,36 @@ void mccpm_state::mccpm(machine_config &config) /* Devices */ // clock supplied by pair of HD4702 baud rate generators - clock_device &uart_clock(CLOCK(config, "uart_clock", 153'600)); - uart_clock.signal_handler().set("sio", FUNC(z80sio_device::txca_w)); - uart_clock.signal_handler().append("sio", FUNC(z80sio_device::rxca_w)); + F4702(config, m_brg[0], 2.4576_MHz_XTAL); + m_brg[0]->s_callback().set_ioport("BAUD1"); + m_brg[0]->z_callback().set("sio", FUNC(z80sio_device::rxtxcb_w)); + m_brg[0]->z_callback().append(FUNC(mccpm_state::bd_q_w<0>)); + + F4702(config, m_brg[1], 2.4576_MHz_XTAL); + m_brg[1]->s_callback().set_ioport("BAUD2"); + m_brg[1]->z_callback().set("sio", FUNC(z80sio_device::txca_w)); + m_brg[1]->z_callback().append("sio", FUNC(z80sio_device::rxca_w)); + m_brg[1]->z_callback().append(FUNC(mccpm_state::bd_q_w<1>)); // Ch A: terminal; Ch B: printer z80sio_device& sio(Z80SIO(config, "sio", XTAL(4'000'000))); sio.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0); - sio.out_txda_callback().set("rs232", FUNC(rs232_port_device::write_txd)); - sio.out_dtra_callback().set("rs232", FUNC(rs232_port_device::write_dtr)); - sio.out_rtsa_callback().set("rs232", FUNC(rs232_port_device::write_rts)); - - rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal")); - rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w)); - rs232.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w)); + sio.out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd)); + sio.out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr)); + sio.out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts)); + sio.out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd)); + sio.out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr)); + sio.out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts)); + + rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal")); + rs232a.rxd_handler().set("sio", FUNC(z80sio_device::rxa_w)); + rs232a.cts_handler().set("sio", FUNC(z80sio_device::ctsa_w)); + rs232a.dcd_handler().set("sio", FUNC(z80sio_device::dcda_w)); + + rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr)); + rs232b.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w)); + rs232b.cts_handler().set("sio", FUNC(z80sio_device::ctsb_w)); + rs232b.dcd_handler().set("sio", FUNC(z80sio_device::dcdb_w)); Z80PIO(config, "pio", XTAL(4'000'000)); -- cgit v1.2.3