From 4af3d0ac83b3b46b7044cd75cbaef161d9a03a0f Mon Sep 17 00:00:00 2001 From: Joakim Larsson Edstrom Date: Sat, 8 Jul 2017 00:14:06 +0200 Subject: [PATCH] mc14411: NEW DEVICE Motorola MC14411 Bit Rate Generator --- scripts/src/machine.lua | 12 ++ scripts/target/mame/arcade.lua | 1 + scripts/target/mame/mess.lua | 1 + src/devices/machine/mc14411.cpp | 217 ++++++++++++++++++++++++++++++++ src/devices/machine/mc14411.h | 160 +++++++++++++++++++++++ 5 files changed, 391 insertions(+) create mode 100644 src/devices/machine/mc14411.cpp create mode 100644 src/devices/machine/mc14411.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index c1cc2afe1dc..db1feb75e54 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1574,6 +1574,18 @@ if (MACHINES["MC146818"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/mc14411.h,MACHINES["MC14411"] = true +--------------------------------------------------- + +if (MACHINES["MC14411"]~=null) then + files { + MAME_DIR .. "src/devices/machine/mc14411.cpp", + MAME_DIR .. "src/devices/machine/mc14411.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/mc2661.h,MACHINES["MC2661"] = true diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 05d5fbf5a62..dad0a154fec 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -472,6 +472,7 @@ MACHINES["MB87078"] = true --MACHINES["MB8795"] = true MACHINES["MB89352"] = true MACHINES["MB89371"] = true +--MACHINES["MC14411"] = true MACHINES["MC146818"] = true MACHINES["MC2661"] = true MACHINES["MC6843"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 74c73c806b5..0aa1e20b0d3 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -461,6 +461,7 @@ MACHINES["MB87078"] = true MACHINES["MB8795"] = true MACHINES["MB89352"] = true MACHINES["MB89371"] = true +MACHINES["MC14411"] = true MACHINES["MC146818"] = true MACHINES["MC2661"] = true MACHINES["MC6843"] = true diff --git a/src/devices/machine/mc14411.cpp b/src/devices/machine/mc14411.cpp new file mode 100644 index 00000000000..9433f36ed71 --- /dev/null +++ b/src/devices/machine/mc14411.cpp @@ -0,0 +1,217 @@ +// license:BSD-3-Clause +// copyright-holders:Joakim Larsson Edstrom +/********************************************************************* + + mc14411.cpp + + Motorola Bit Rate Generator + + This device assumed a fixed rate clock/crystal and does not support + changing it through pin 21. All other features are implemented. + +*********************************************************************/ + +#include "emu.h" +#include "mc14411.h" + +/*************************************************************************** + MACROS +***************************************************************************/ + +//#define LOG_GENERAL (1U << 0) // Already defined in logmacro.h +#define LOG_SETUP (1U << 1) + +//#define VERBOSE (LOG_GENERAL|LOG_SETUP) +//#define LOG_OUTPUT_FUNC printf + +#include "logmacro.h" + +//#define LOG(...) LOGMASKED(LOG_GENERAL, __VA_ARGS__) // Already defined in logmacro.h +#define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__) + +#ifdef _MSC_VER +#define FUNCNAME __func__ +#define LLFORMAT "%I64d" +#else +#define FUNCNAME __PRETTY_FUNCTION__ +#define LLFORMAT "%lld" +#endif + +/*************************************************************************** + LOCAL VARIABLES +***************************************************************************/ +// 0/0 0/1 1/0 1/1 RSB/RSA +// X1 X8 X16 X64 +const int mc14411_device::counter_divider[16][4] = { + { 192, 24, 12, 3 }, // F1 + { 256, 32, 16, 4 }, // F2 + { 384, 48, 24, 6 }, // F3 + { 512, 64, 32, 8 }, // F4 + { 768, 96, 48, 12 }, // F5 + { 1024, 128, 64, 16 }, // F6 + { 1536, 192, 96, 24 }, // F7 + { 3072, 384, 192, 48 }, // F8 + { 6144, 768, 384, 96 }, // F9 + { 9216, 1152, 576, 144 }, // F10 + { 12288, 1536, 768, 192 }, // F11 + { 13696, 1712, 856, 214 }, // F12 + { 16768, 2096, 1048, 262 }, // F13 + { 24576, 3072, 1536, 384 }, // F14 + { 2, 2, 2, 2 }, // F15 + { 1, 1, 1, 1 } // F16 +}; + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** +// device type definition +DEFINE_DEVICE_TYPE(MC14411, mc14411_device, "mc14411", "MC14411 BRG") + +/*************************************************************************** + LIVE DEVICE +***************************************************************************/ +mc14411_device::mc14411_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : mc14411_device(mconfig, MC14411, tag, owner, clock) +{ +} + +mc14411_device::mc14411_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) + , m_out_fx_cbs{*this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this } + , m_divider(0) + , m_reset(CLEAR_LINE) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- +void mc14411_device::device_start() +{ + LOGSETUP("%s\n", FUNCNAME); + + memset(m_fx_timer, '\0', sizeof(m_fx_timer)); + for (int i = F1; i <= F16; i++) + { + m_out_fx_cbs[i].resolve(); + if (!m_out_fx_cbs[i].isnull()) m_fx_timer[i] = timer_alloc(i); + } + + save_item(NAME(m_divider)); + save_item(NAME(m_reset)); + + m_reset_timer = timer_alloc(TIMER_ID_RESET); + +} + +//------------------------------------------------------------------------ +// device_reset - is called by the mame framework or by the owning device +// driver or by ASSERTING the reset line through set_reset_line +//------------------------------------------------------------------------ +void mc14411_device::device_reset() +{ + LOGSETUP("%s\n", FUNCNAME); + + for (int i = F1; i <= F16; i++) + { + if (!m_out_fx_cbs[i].isnull()) + { + // Reset line according to datasheet and remember it for transitions to come + (m_out_fx_cbs[i])(m_fx_state[i] = (i < F15 ? 0 : 1)); + + // Arm the timer based on the selected divider and the crystal value + double hz = clock()/counter_divider[i][m_divider] * 2; // 2 flanks per cycle + m_fx_timer[i]->adjust(attotime::from_hz(hz), i, attotime::from_hz(hz)); + LOGSETUP(" - arming timer for F%d at %fHz\n", i + 1, hz); + } + } + + if (m_reset == ASSERT_LINE) + { + m_reset_timer->adjust(attotime::from_nsec((double)900), TIMER_ID_RESET, attotime::from_nsec((double)900)); + } +} + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- +void mc14411_device::device_timer (emu_timer &timer, device_timer_id id, int32_t param, void *ptr) +{ + switch(id) + { + case F1: + case F2: + case F3: + case F4: + case F5: + case F6: + case F7: + case F8: + case F9: + case F10: + case F11: + case F12: + case F13: + case F14: + case F15: + case F16: + (m_out_fx_cbs[id])(m_fx_state[id]++ & 1); + break; + case TIMER_ID_RESET: + // NOTE: This check could be triggered by either faulty hardware design or non accurate emulation so is just informative if the reset line is handled + // explicitelly instead of relying on calling device_reset + if (!(m_reset == ASSERT_LINE)) + { + LOG("Reset pulse is too short, should be 900nS minimum"); + logerror("Reset pulse is too short, should be 900nS minimum"); + } + break; + default: + LOG("Unhandled Timer ID %d\n", id); + break; + } +} + +//-------------------------------------------------------- +// rate_select_w - implements the RSA and RSB input pins +// TODO: Needs to check real device behaviour how changing +// divider at run time affects wave forms +//-------------------------------------------------------- +WRITE8_MEMBER( mc14411_device::rate_select_w) +{ + LOGSETUP("%s %02x\n", FUNCNAME, data); + + m_divider = data & 3; + + for (int i = F1; i <= F16; i++) + { + if (!m_out_fx_cbs[i].isnull()) + { + // Re-arm the timer based on the new selected divider and the crystal value + double hz = clock()/counter_divider[i][m_divider] * 2; // 2 flanks per cycle + m_fx_timer[i]->adjust(attotime::from_hz(hz), i, attotime::from_hz(hz)); + LOGSETUP(" - Re-arming timer for F%d at %fHz\n", i + 1, hz); + } + } +} + +//------------------------------------------------ +// reset_w - implements software controlled reset +//------------------------------------------------ +WRITE_LINE_MEMBER( mc14411_device::reset_w) +{ + LOGSETUP("%s %02x\n", FUNCNAME, state); + + m_reset = state; + + if (m_reset == ASSERT_LINE) + { + LOGSETUP(" - Asserting reset\n"); + device_reset(); + } + else + { + LOGSETUP(" - Clearing reset\n"); + } +} diff --git a/src/devices/machine/mc14411.h b/src/devices/machine/mc14411.h new file mode 100644 index 00000000000..d5d6f29d662 --- /dev/null +++ b/src/devices/machine/mc14411.h @@ -0,0 +1,160 @@ +// license:BSD-3-Clause +// copyright-holders:Joakim Larsson Edstrom +/********************************************************************** +* +* Motorola MC14411 - bit rate generator. It utilizes a frequency divider +* network to provide a wide range of output frequencies. A crystal controlled +* oscillator is the clock source for the network. A 2 bit adress is used to +* select one of four multiple output clock rates. +* +* - Single 5V sower supply +* - Internal Oscillator Crystal Controlled for stability (1.8432 MHz) +* - Sixteen Different Output Clock Rates +* - 50% Output Duty Cycle +* - Programmable Time Bases for one of four Multiple Output Rates +* - Buffered Output compatible with low power TTL +* - Noice immunity = 45% of VDD Typlical +* - Diode protection on All Inputs +* - External Clock may be applied to pin 21 +* - Internal pullup on reset input +* _____ _____ +* F1 1 |* \_/ | 24 VDD +* F3 2 | | 23 Rate Select A +* F5 3 | | 22 Rate Select B +* F7 4 | | 21 Xtal In +* F8 5 | | 20 Xtal Out +* F10 6 | | 19 F16 +* F9 7 | MC14411 | 18 F15 +* F11 8 | | 17 F2 +* F14 9 | | 16 F4 +* Reset* 10 | | 15 F6 +* Not Used 11 | | 14 F12 +* VSS 12 |_____________| 13 F13 +* +* +* +----------+-------Output Rates (Hz)-------+ +* | Output | Rate Select B and A pins | +* | Number | X64 11| X16 10| X8 01 | X1 00 | +* |----------+-------------------------------+ +* | Fl | 614.4k| 153.6k| 76.8k| 9600 | +* | F2 | 46O.8k| 115.2k| 57.6k| 7200 | +* | F3 | 3O7.2k| 76.8k| 38.4k| 4800 | +* | F4 | 23O.4k| 57.6k| 28.8k| 3600 | +* | F5 | 153.6k| 38.4k| 19.2k| 2400 | +* | F6 | 115.2k| 28.8k| 14.4k| 1800 | +* | F7 | 76.8k| 19.2k| 9600 | 1200 | +* | FS | 38.4k| 9600 | 4800 | 600 | +* | F9 | 19.2k| 4800 | 2400 | 300 | +* | F1O | 12.8k| 3200 | 1600 | 200 | +* | Fll | 9600 | 2400 | 1200 | 150 | +* | F12 | 8613.2| 2153.3| 1076.6| 134.5| +* | F13 | 7035.5| 1758.8| 879.4| 109.9| +* | F14 | 4800 | 1200 | 600 | 75 | +* | F15 | 921.6k| 921.6k| 921.6k| 921.6k| +* | F16 | 1.843M| 1.843M| 1.843M| 1.843M| +* +------------------------------------------+ +* - F16 is a buffered oscillator output +* +* The device is designed to work with 1.843MHz crystal so it is assumed that +* an external clock source attached to pin 21 is also fixed thus not need to +* interface through a callback interface. +**********************************************************************/ + +#ifndef MAME_MACHINE_MC14411_H +#define MAME_MACHINE_MC14411_H + +#pragma once + +//************************************************************************** +// DEVICE CONFIGURATION MACROS +//************************************************************************** +#define MCFG_MC14411_ADD(_tag, _clock) MCFG_DEVICE_ADD(_tag, MC14411, _clock) + +#define MCFG_MC14411_F1_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 0, DEVCB_##_devcb); +#define MCFG_MC14411_F2_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 1, DEVCB_##_devcb); +#define MCFG_MC14411_F3_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 2, DEVCB_##_devcb); +#define MCFG_MC14411_F4_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 3, DEVCB_##_devcb); +#define MCFG_MC14411_F5_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 4, DEVCB_##_devcb); +#define MCFG_MC14411_F6_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 5, DEVCB_##_devcb); +#define MCFG_MC14411_F7_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 6, DEVCB_##_devcb); +#define MCFG_MC14411_F8_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 7, DEVCB_##_devcb); +#define MCFG_MC14411_F9_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 8, DEVCB_##_devcb); +#define MCFG_MC14411_F10_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 9, DEVCB_##_devcb); +#define MCFG_MC14411_F11_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 10, DEVCB_##_devcb); +#define MCFG_MC14411_F12_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 11, DEVCB_##_devcb); +#define MCFG_MC14411_F13_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 12, DEVCB_##_devcb); +#define MCFG_MC14411_F14_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 13, DEVCB_##_devcb); +#define MCFG_MC14411_F15_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 14, DEVCB_##_devcb); +#define MCFG_MC14411_F16_CB(_devcb) devcb = &mc14411_device::set_out_fx_cb(*device, 15, DEVCB_##_devcb); + +#define MCFG_MC14411_RSA 0x01 +#define MCFG_MC14411_RSB 0x02 + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** +class mc14411_device : public device_t +{ +public: + // construction/destruction + mc14411_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template static devcb_base &set_out_fx_cb(device_t &device, int index, Object &&cb) { return downcast(device).m_out_fx_cbs[index].set_callback(std::forward(cb)); } + + DECLARE_WRITE_LINE_MEMBER(reset_w); + DECLARE_WRITE8_MEMBER(rate_select_w); + +protected: + mc14411_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + +private: + // timers + enum // indexes + { + F1 = 0, + F2 = 1, + F3 = 2, + F4 = 3, + F5 = 4, + F6 = 5, + F7 = 6, + F8 = 7, + F9 = 8, + F10 = 9, + F11 = 10, + F12 = 11, + F13 = 12, + F14 = 13, + F15 = 14, + F16 = 15 + }; + + emu_timer *m_fx_timer[16]; + + enum + { + TIMER_ID_RESET = 16 + }; + emu_timer *m_reset_timer; + + // F1-F16 Output line states + uint32_t m_fx_state[16]; + + // divider matrix + static const int counter_divider[16][4]; + + devcb_write_line m_out_fx_cbs[16]; + + uint32_t m_divider; // main divider to use, 0-3 column index into counter_divider + uint32_t m_reset; // Reset line state +}; + +// device type definition +DECLARE_DEVICE_TYPE(MC14411, mc14411_device) + +#endif // MAME_MACHINE_MC14411_H