diff options
-rw-r--r-- | scripts/src/bus.lua | 2 | ||||
-rw-r--r-- | src/devices/bus/hp9845_io/98046.cpp | 138 | ||||
-rw-r--r-- | src/devices/bus/hp9845_io/98046.h | 17 | ||||
-rw-r--r-- | src/devices/bus/rs232/rs232.cpp | 2 | ||||
-rw-r--r-- | src/devices/bus/rs232/rs232_sync_io.cpp | 213 | ||||
-rw-r--r-- | src/devices/bus/rs232/rs232_sync_io.h | 61 | ||||
-rw-r--r-- | src/mame/drivers/hp9845.cpp | 3 |
7 files changed, 351 insertions, 85 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index bae08d7d3d1..74f601e7499 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -2631,6 +2631,8 @@ if (BUSES["RS232"]~=null) then MAME_DIR .. "src/devices/bus/rs232/swtpc8212.h", MAME_DIR .. "src/devices/bus/rs232/exorterm.cpp", MAME_DIR .. "src/devices/bus/rs232/exorterm.h", + MAME_DIR .. "src/devices/bus/rs232/rs232_sync_io.cpp", + MAME_DIR .. "src/devices/bus/rs232/rs232_sync_io.h", } end diff --git a/src/devices/bus/hp9845_io/98046.cpp b/src/devices/bus/hp9845_io/98046.cpp index 8e977129a76..8b7e601f0f4 100644 --- a/src/devices/bus/hp9845_io/98046.cpp +++ b/src/devices/bus/hp9845_io/98046.cpp @@ -55,12 +55,6 @@ namespace { } } -// Timers -enum { - TMR_ID_RXC, - TMR_ID_TXC -}; - // device type definition DEFINE_DEVICE_TYPE(HP98046_IO_CARD, hp98046_io_card_device , "hp98046" , "HP98046 card") @@ -70,6 +64,8 @@ hp98046_io_card_device::hp98046_io_card_device(const machine_config &mconfig, co , m_cpu(*this, "cpu") , m_sio(*this, "sio") , m_rs232(*this, "rs232") + , m_tx_brg(*this , "tx_brg") + , m_rx_brg(*this , "rx_brg") , m_loopback_en(*this, "loop") { } @@ -195,7 +191,16 @@ void hp98046_io_card_device::device_add_mconfig(machine_config &config) m_rs232->dcd_handler().set(FUNC(hp98046_io_card_device::rs232_dcd_w)); m_rs232->dsr_handler().set(FUNC(hp98046_io_card_device::rs232_dsr_w)); m_rs232->cts_handler().set(FUNC(hp98046_io_card_device::rs232_cts_w)); - config.set_maximum_quantum(attotime::from_hz(5000)); + m_rs232->rxc_handler().set(FUNC(hp98046_io_card_device::rs232_rxc_w)); + m_rs232->txc_handler().set(FUNC(hp98046_io_card_device::rs232_txc_w)); + // There's just one 2.4576 MHz xtal, Tx BRG gets its clock from Rx BRG + F4702(config , m_tx_brg , 2.4576_MHz_XTAL); + m_tx_brg->s_callback().set([this]() { return m_txc_sel; }); + m_tx_brg->z_callback().set(FUNC(hp98046_io_card_device::txc_w)); + F4702(config , m_rx_brg , 2.4576_MHz_XTAL); + m_rx_brg->s_callback().set([this]() { return m_rxc_sel; }); + m_rx_brg->z_callback().set(FUNC(hp98046_io_card_device::rxc_w)); + config.set_maximum_quantum(attotime::from_hz(307200)); } static INPUT_PORTS_START(hp98046_port) @@ -233,9 +238,6 @@ void hp98046_io_card_device::device_start() { m_ram = std::make_unique<uint8_t[]>(1024); save_pointer(NAME(m_ram) , 1024); - - m_rxc_timer = timer_alloc(TMR_ID_RXC); - m_txc_timer = timer_alloc(TMR_ID_TXC); } void hp98046_io_card_device::device_reset() @@ -247,36 +249,6 @@ void hp98046_io_card_device::device_reset() update_sts(); update_irq(); m_loopback = m_loopback_en->read() != 0; - // Ensure timers are loaded the 1st time BRGs are configured - m_rxc_sel = ~0; - m_txc_sel = ~0; - m_rxc_timer->reset(); - m_txc_timer->reset(); -} - -void hp98046_io_card_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - switch (id) { - case TMR_ID_RXC: - m_rxc = !m_rxc; - m_sio->rxca_w(m_rxc); - if (m_loopback && (m_txc_sel == 0 || m_txc_sel == 1)) { - m_sio->txca_w(m_rxc); - m_sio->txcb_w(m_rxc); - m_sio->rxcb_w(m_rxc); - } - break; - - case TMR_ID_TXC: - m_txc = !m_txc; - m_sio->txca_w(m_txc); - m_sio->txcb_w(m_txc); - m_sio->rxcb_w(m_txc); - if (m_loopback && (m_rxc_sel == 0 || m_rxc_sel == 1)) { - m_sio->rxca_w(m_txc); - } - break; - } } void hp98046_io_card_device::cpu_program_map(address_map &map) @@ -405,7 +377,8 @@ void hp98046_io_card_device::cpu_w(offs_t offset, uint8_t data) case 3: // xxxx'x111: write to BRGs - set_brgs(data); + m_rxc_sel = (data >> 4) & 0xf; + m_txc_sel = data & 0xf; break; } } else { @@ -519,6 +492,20 @@ WRITE_LINE_MEMBER(hp98046_io_card_device::rs232_cts_w) } } +WRITE_LINE_MEMBER(hp98046_io_card_device::rs232_rxc_w) +{ + if (!m_loopback) { + machine().scheduler().synchronize(timer_expired_delegate(FUNC(hp98046_io_card_device::sync_rx_im_w) , this) , state); + } +} + +WRITE_LINE_MEMBER(hp98046_io_card_device::rs232_txc_w) +{ + if (!m_loopback) { + machine().scheduler().synchronize(timer_expired_delegate(FUNC(hp98046_io_card_device::sync_tx_im_w) , this) , state); + } +} + bool hp98046_io_card_device::rx_fifo_flag() const { return m_rx_fifo.queue_length() >= 16; @@ -643,43 +630,36 @@ uint8_t hp98046_io_card_device::get_hs_input() const return res; } -// Frequencies of HD4702 BRGs -// All frequencies are doubled here because the timers expire twice per RxC/TxC period -static const unsigned brg_freq[] = { - // Sel: frequency Divisor - // ============================ - 0, // 0: external clock - - 0, // 1: external clock - - 1600, // 2: 50 x16 /3072 - 2400, // 3: 75 x16 /2048 - 4267, // 4: ~134.5 x16 /1152 - 6400, // 5: 200 x16 /768 - 19200, // 6: 600 x16 /256 - 76800, // 7: 2400 x16 /64 - 307200, // 8: 9600 x16 /16 - 153600, // 9: 4800 x16 /32 - 57600, // 10: 1800 x16 / 256/3 - 38400, // 11: 1200 x16 /128 - 76800, // 12: 2400 x16 /64 - 9600, // 13: 300 x16 /512 - 4800, // 14: 150 x16 /1024 - 3491 // 15: ~110 x16 /1408 -}; - -void hp98046_io_card_device::set_brgs(uint8_t sel) -{ - LOG_MCU("BRG=%02x\n" , sel); - uint8_t new_rxc_sel = (sel >> 4) & 0xf; - uint8_t new_txc_sel = sel & 0xf; - - if (new_rxc_sel != m_rxc_sel) { - m_rxc_sel = new_rxc_sel; - auto period = attotime::from_hz(brg_freq[ m_rxc_sel ]); - m_rxc_timer->adjust(period , 0 , period); - } - if (new_txc_sel != m_txc_sel) { - m_txc_sel = new_txc_sel; - auto period = attotime::from_hz(brg_freq[ m_txc_sel ]); - m_txc_timer->adjust(period , 0 , period); +WRITE_LINE_MEMBER(hp98046_io_card_device::rxc_w) +{ + if (m_last_rxc != bool(state)) { + m_last_rxc = bool(state); + m_sio->rxca_w(m_last_rxc); + if (m_loopback) { + machine().scheduler().synchronize(timer_expired_delegate(FUNC(hp98046_io_card_device::sync_tx_im_w) , this) , state); + } } } + +WRITE_LINE_MEMBER(hp98046_io_card_device::txc_w) +{ + if (m_last_txc != bool(state)) { + m_last_txc = bool(state); + m_sio->txca_w(m_last_txc); + m_sio->txcb_w(m_last_txc); + m_sio->rxcb_w(m_last_txc); + if (m_loopback) { + machine().scheduler().synchronize(timer_expired_delegate(FUNC(hp98046_io_card_device::sync_rx_im_w) , this) , state); + } + } +} + +TIMER_CALLBACK_MEMBER(hp98046_io_card_device::sync_rx_im_w) +{ + m_rx_brg->im_w(param); +} + +TIMER_CALLBACK_MEMBER(hp98046_io_card_device::sync_tx_im_w) +{ + m_tx_brg->im_w(param); +} diff --git a/src/devices/bus/hp9845_io/98046.h b/src/devices/bus/hp9845_io/98046.h index fb8197de6fb..90debbb8c03 100644 --- a/src/devices/bus/hp9845_io/98046.h +++ b/src/devices/bus/hp9845_io/98046.h @@ -17,6 +17,7 @@ #include "cpu/mcs48/mcs48.h" #include "machine/z80sio.h" #include "bus/rs232/rs232.h" +#include "machine/f4702.h" class hp98046_io_card_device : public device_t, public device_hp9845_io_interface { @@ -37,12 +38,13 @@ protected: virtual const tiny_rom_entry *device_rom_region() const override; 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: required_device<i8048_device> m_cpu; required_device<z80sio_device> m_sio; required_device<rs232_port_device> m_rs232; + required_device<f4702_device> m_tx_brg; + required_device<f4702_device> m_rx_brg; required_ioport m_loopback_en; std::unique_ptr<uint8_t[]> m_ram; @@ -64,13 +66,11 @@ private: bool m_sio_int; uint8_t m_port_2; bool m_loopback; + bool m_last_rxc; + bool m_last_txc; - emu_timer *m_rxc_timer; - emu_timer *m_txc_timer; uint8_t m_rxc_sel; uint8_t m_txc_sel; - bool m_rxc; - bool m_txc; void cpu_program_map(address_map &map); void cpu_io_map(address_map &map); @@ -85,6 +85,8 @@ private: DECLARE_WRITE_LINE_MEMBER(rs232_dcd_w); DECLARE_WRITE_LINE_MEMBER(rs232_dsr_w); DECLARE_WRITE_LINE_MEMBER(rs232_cts_w); + DECLARE_WRITE_LINE_MEMBER(rs232_rxc_w); + DECLARE_WRITE_LINE_MEMBER(rs232_txc_w); bool rx_fifo_flag() const; bool tx_fifo_flag() const; void update_flg(); @@ -94,7 +96,10 @@ private: void load_tx_fifo(); void set_r6_r7_pending(bool state); uint8_t get_hs_input() const; - void set_brgs(uint8_t sel); + DECLARE_WRITE_LINE_MEMBER(rxc_w); + DECLARE_WRITE_LINE_MEMBER(txc_w); + TIMER_CALLBACK_MEMBER(sync_rx_im_w); + TIMER_CALLBACK_MEMBER(sync_tx_im_w); }; // device type definitions diff --git a/src/devices/bus/rs232/rs232.cpp b/src/devices/bus/rs232/rs232.cpp index d55628d64c4..ce3446a85b7 100644 --- a/src/devices/bus/rs232/rs232.cpp +++ b/src/devices/bus/rs232/rs232.cpp @@ -171,6 +171,7 @@ device_rs232_port_interface::~device_rs232_port_interface() #include "swtpc8212.h" #include "terminal.h" #include "ie15.h" +#include "rs232_sync_io.h" void default_rs232_devices(device_slot_interface &device) { @@ -182,6 +183,7 @@ void default_rs232_devices(device_slot_interface &device) device.option_add("printer", SERIAL_PRINTER); device.option_add("pty", PSEUDO_TERMINAL); device.option_add("rs_printer", RADIO_SHACK_SERIAL_PRINTER); + device.option_add("rs232_sync_io", RS232_SYNC_IO); device.option_add("sunkbd", SUN_KBD_ADAPTOR); device.option_add("swtpc8212", SERIAL_TERMINAL_SWTPC8212); device.option_add("terminal", SERIAL_TERMINAL); diff --git a/src/devices/bus/rs232/rs232_sync_io.cpp b/src/devices/bus/rs232/rs232_sync_io.cpp new file mode 100644 index 00000000000..8977ece64b6 --- /dev/null +++ b/src/devices/bus/rs232/rs232_sync_io.cpp @@ -0,0 +1,213 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + rs232_sync_io.cpp + + Synchronous I/O on RS232 port + + This device provides a bitbanger-based interface to a synchronous + RS232 port. + It has the following functions: + - Provides Tx and/or Rx clock to RS232 port; clocks can be + individually turned off when not needed (e.g. Tx clock can + disabled when receiving in half-duplex mode) + - In the tx direction, it gathers bits from TxD, + packs them into bytes LSB to MSB, then sends them out to + bitbanger + - In the rx direction, it extracts bytes from bitbanger + and serializes them to RxD, LSB to MSB. + - Supports both full-duplex and half-duplex modes + - Generates CTS signal + + There's no kind of synchronization to any higher-level framing: + bits are just clocked in/out of the port, packed/unpacked into + bytes and exchanged with bitbanger. + There's also no automatic stuffing of idle line characters, this + function is to be implemented externally if needed. + +*********************************************************************/ + +#include "emu.h" +#include "rs232_sync_io.h" + +// Debugging + +#include "logmacro.h" +//#undef VERBOSE +//#define VERBOSE LOG_GENERAL + +// Bit manipulation +namespace { + template<typename T> constexpr T BIT_MASK(unsigned n) + { + return (T)1U << n; + } + + template<typename T> void BIT_CLR(T& w , unsigned n) + { + w &= ~BIT_MASK<T>(n); + } + + template<typename T> void BIT_SET(T& w , unsigned n) + { + w |= BIT_MASK<T>(n); + } +} + +// Timers +enum { + TMR_ID_CLK +}; + +rs232_sync_io_device::rs232_sync_io_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig , RS232_SYNC_IO , tag , owner , clock) + , device_rs232_port_interface(mconfig , *this) + , m_stream(*this , "stream") + , m_rs232_baud(*this , "RS232_BAUD") + , m_rts_duplex(*this , "rtsduplex") + , m_txc_setting(*this , "txcontrol") + , m_rxc_setting(*this , "rxcontrol") +{ +} + +rs232_sync_io_device:: ~rs232_sync_io_device() +{ +} + +WRITE_LINE_MEMBER(rs232_sync_io_device::input_txd) +{ + m_txd = state; + LOG("TxD %d %u\n" , state , m_tx_counter); +} + +WRITE_LINE_MEMBER(rs232_sync_io_device::input_rts) +{ + m_rts = state; +} + +static INPUT_PORTS_START(rs232_sync_io) + PORT_RS232_BAUD("RS232_BAUD" , RS232_BAUD_2400 , "Baud rate" , rs232_sync_io_device , update_serial) + + PORT_START("rtsduplex") + PORT_CONFNAME(1 , 1 , "RTS controls half-duplex") + PORT_CONFSETTING(0 , DEF_STR(Off)) + PORT_CONFSETTING(1 , DEF_STR(On)) + + PORT_START("txcontrol") + PORT_CONFNAME(1 , 1 , "TxC generated") + PORT_CONFSETTING(0 , "Always") + PORT_CONFSETTING(1 , "When Tx is active") + + PORT_START("rxcontrol") + PORT_CONFNAME(3 , 1 , "RxC generated") + PORT_CONFSETTING(0 , "Always") + PORT_CONFSETTING(1 , "When Rx is active") + PORT_CONFSETTING(2 , "When a byte is available") +INPUT_PORTS_END + +ioport_constructor rs232_sync_io_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(rs232_sync_io); +} + +void rs232_sync_io_device::device_add_mconfig(machine_config &config) +{ + BITBANGER(config , m_stream , 0); +} + +void rs232_sync_io_device::device_start() +{ + m_clk_timer = timer_alloc(TMR_ID_CLK); +} + +void rs232_sync_io_device::device_reset() +{ + output_dcd(0); + output_dsr(0); + output_rxd(1); + output_cts(1); + + update_serial(0); + + m_clk = false; + m_rx_byte = 0xff; + m_rx_counter = 0; + m_tx_counter = 0; + m_tx_enabled = false; + m_rx_enabled = false; + + output_txc(1); + output_rxc(1); +} + +void rs232_sync_io_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) { + case TMR_ID_CLK: + m_clk = !m_clk; + if (m_clk) { + output_txc(1); + output_rxc(1); + if (m_tx_enabled) { + // Rising edge: capture TxD + m_tx_byte = (m_tx_byte >> 1) & 0x7f; + if (m_txd) { + BIT_SET(m_tx_byte , 7); + } + if (++m_tx_counter >= 8) { + LOG("Tx %02x @%s\n" , m_tx_byte , machine().time().to_string()); + m_tx_counter = 0; + m_stream->output(m_tx_byte); + m_tx_enabled = false; + } + } + } else { + if (!m_tx_enabled) { + m_tx_enabled = !m_rts || !m_rts_duplex->read(); + output_cts(!m_tx_enabled); + } + if (m_tx_enabled || m_txc_setting->read() == 0) { + output_txc(0); + } + if (!m_rx_enabled) { + m_rx_enabled = m_rts || !m_rts_duplex->read(); + if (m_rx_enabled) { + if (m_stream->input(&m_rx_byte , 1) == 0) { + m_rx_byte = ~0; + if (m_rxc_setting->read() == 2) { + m_rx_enabled = false; + } + } else { + LOG("Rx %02x @%s\n" , m_rx_byte , machine().time().to_string()); + } + } + } + if (m_rx_enabled || m_rxc_setting->read() == 0) { + output_rxc(0); + } + if (m_rx_enabled) { + // Falling edge: update RxD + output_rxd(BIT(m_rx_byte , 0)); + m_rx_byte >>= 1; + if (++m_rx_counter >= 8) { + m_rx_counter = 0; + m_rx_enabled = false; + } + } + } + break; + + default: + break; + } +} + +WRITE_LINE_MEMBER(rs232_sync_io_device::update_serial) +{ + auto baud_rate = convert_baud(m_rs232_baud->read()); + auto period = attotime::from_hz(baud_rate * 2); + m_clk_timer->adjust(period , 0 , period); +} + +DEFINE_DEVICE_TYPE(RS232_SYNC_IO, rs232_sync_io_device, "rs232_sync_io", "RS232 Synchronous I/O") diff --git a/src/devices/bus/rs232/rs232_sync_io.h b/src/devices/bus/rs232/rs232_sync_io.h new file mode 100644 index 00000000000..de43eda5ece --- /dev/null +++ b/src/devices/bus/rs232/rs232_sync_io.h @@ -0,0 +1,61 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + rs232_sync_io.h + + Synchronous I/O on RS232 port + +*********************************************************************/ + +#ifndef MAME_BUS_RS232_RS232_SYNC_IO_H +#define MAME_BUS_RS232_RS232_SYNC_IO_H + +#pragma once + +#include "rs232.h" +#include "imagedev/bitbngr.h" + +class rs232_sync_io_device : public device_t, public device_rs232_port_interface +{ +public: + // construction/destruction + rs232_sync_io_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + virtual ~rs232_sync_io_device(); + + virtual DECLARE_WRITE_LINE_MEMBER(input_txd) override; + virtual DECLARE_WRITE_LINE_MEMBER(input_rts) override; + + DECLARE_WRITE_LINE_MEMBER(update_serial); + +protected: + // device-level overrides + virtual ioport_constructor device_input_ports() const override; + virtual void device_add_mconfig(machine_config &config) override; + 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: + required_device<bitbanger_device> m_stream; + required_ioport m_rs232_baud; + required_ioport m_rts_duplex; + required_ioport m_txc_setting; + required_ioport m_rxc_setting; + + emu_timer *m_clk_timer; + bool m_clk; + bool m_txd; + bool m_rts; + bool m_tx_enabled; + bool m_rx_enabled; + uint8_t m_tx_byte; + uint8_t m_rx_byte; + uint8_t m_tx_counter; + uint8_t m_rx_counter; +}; + +// device type definitions +DECLARE_DEVICE_TYPE(RS232_SYNC_IO, rs232_sync_io_device) + +#endif // MAME_BUS_RS232_RS232_SYNC_IO_H diff --git a/src/mame/drivers/hp9845.cpp b/src/mame/drivers/hp9845.cpp index ed279a0d64c..7976c54cbc9 100644 --- a/src/mame/drivers/hp9845.cpp +++ b/src/mame/drivers/hp9845.cpp @@ -3641,11 +3641,14 @@ void hp9845_base_state::ppu_io_map(address_map &map) void hp9845_base_state::hp9845_base(machine_config &config) { HP_5061_3001(config , m_lpu , 5700000); + // Clock scaling takes into account the slowdown caused by DRAM refresh + m_lpu->set_clock_scale(0.93); m_lpu->set_addrmap(AS_PROGRAM , &hp9845_base_state::global_mem_map); m_lpu->set_9845_boot_mode(true); m_lpu->set_rw_cycles(6 , 6); m_lpu->set_relative_mode(true); HP_5061_3001(config , m_ppu , 5700000); + m_ppu->set_clock_scale(0.93); m_ppu->set_addrmap(AS_PROGRAM , &hp9845_base_state::global_mem_map); m_ppu->set_addrmap(AS_IO , &hp9845_base_state::ppu_io_map); m_ppu->set_9845_boot_mode(true); |