From ea22a3567f9f1f903f0c4fca839169a243dbf8e3 Mon Sep 17 00:00:00 2001 From: Nigel Barnes Date: Fri, 16 Feb 2024 18:27:54 +0000 Subject: machine/saa5070: New SAA5070 Viewdata Input/Output Peripheral (VIOP) device. --- scripts/src/machine.lua | 12 ++ src/devices/machine/saa5070.cpp | 353 ++++++++++++++++++++++++++++++++++++++++ src/devices/machine/saa5070.h | 151 +++++++++++++++++ 3 files changed, 516 insertions(+) create mode 100644 src/devices/machine/saa5070.cpp create mode 100644 src/devices/machine/saa5070.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index a0e4792a5fd..f1869389ae2 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -3309,6 +3309,18 @@ if (MACHINES["SAA1043"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/saa5070.h,MACHINES["SAA5070"] = true +--------------------------------------------------- + +if (MACHINES["SAA5070"]~=null) then + files { + MAME_DIR .. "src/devices/machine/saa5070.cpp", + MAME_DIR .. "src/devices/machine/saa5070.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/scc66470.h,MACHINES["SCC66470"] = true diff --git a/src/devices/machine/saa5070.cpp b/src/devices/machine/saa5070.cpp new file mode 100644 index 00000000000..3b40d13c844 --- /dev/null +++ b/src/devices/machine/saa5070.cpp @@ -0,0 +1,353 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/********************************************************************* + + SAA5070 Viewdata Input/Output Peripheral (VIOP) + + https://bitsavers.org/components/philips/_dataBooks/1985_IC02Na_Philips_Bipolar_MOS_Video_and_Associated_Systems.pdf + + TODO: + - tape UART + - IBUS receiver/transmitter + +*********************************************************************/ + +#include "emu.h" +#include "saa5070.h" + + +DEFINE_DEVICE_TYPE(SAA5070, saa5070_device, "saa5070", "SAA5070 Viewdata Input/Output Peripheral") +DEFINE_DEVICE_TYPE(SAA5070_UART, saa5070_uart_device, "saa5070_uart", "SAA5070 UART") + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +saa5070_uart_device::saa5070_uart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SAA5070_UART, tag, owner, clock) + , device_serial_interface(mconfig, *this) + , m_txd_handler(*this) + , m_rx_enabled(0) + , m_tx_enabled(0) + , m_rxd(1) +{ +} + + +void saa5070_uart_device::device_start() +{ + receive_register_reset(); + transmit_register_reset(); + + save_item(NAME(m_status)); +} + +void saa5070_uart_device::device_reset() +{ + m_status = 0x80; // TXRDY +} + + +void saa5070_uart_device::set_baud_rate(int rxbaud, int txbaud, int parity) +{ + if (parity) + set_data_frame(1, 7, (parity == 1) ? PARITY_ODD : PARITY_EVEN, STOP_BITS_1); + else + set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); + + set_rcv_rate(rxbaud); + set_tra_rate(txbaud); + + receive_register_reset(); + transmit_register_reset(); +} + +void saa5070_uart_device::rcv_callback() +{ + if (m_rx_enabled) // UART enabled + receive_register_update_bit(m_rxd); +} + +void saa5070_uart_device::rcv_complete() +{ + receive_register_extract(); + + m_status |= 0x40; // RXRDY + + if (is_receive_framing_error()) + m_status |= 0x10; // FERR + else + m_status &= ~0x10; + + if (is_receive_parity_error()) + m_status |= 0x08; // PERR + else + m_status &= ~0x08; +} + +void saa5070_uart_device::tra_callback() +{ + if (m_tx_enabled) // UART enabled + m_txd_handler(transmit_register_get_data_bit()); +} + +void saa5070_uart_device::tra_complete() +{ + m_status |= 0x80; // TXRDY +} + + +void saa5070_uart_device::write_rxd(int state) +{ + m_rxd = state; + device_serial_interface::rx_w(state); +} + +void saa5070_uart_device::write_dcd(int state) +{ + if (state) + m_status |= 0x24; // DCD/DCD + else + m_status &= ~0x24; +} + +void saa5070_uart_device::tx_byte(uint8_t data) +{ + m_status &= ~0x80; // TXRDY + + transmit_register_setup(data); +} + +uint8_t saa5070_uart_device::rx_byte() +{ + m_status &= ~0x40; // RXRDY + + return get_received_char(); +} + + +saa5070_device::saa5070_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SAA5070, tag, owner, clock) + , m_line_uart(*this, "line_uart") + , m_tape_uart(*this, "tape_uart") + , m_in_a_handler(*this, 0x00) + , m_in_b_handler(*this, 0x00) + , m_out_a_handler(*this) + , m_out_b_handler(*this) + , m_imp_handler(*this) + , m_txdata_handler(*this) + , m_timeout_timer(nullptr) + , m_dial_timer(nullptr) +{ +} + + +void saa5070_device::device_start() +{ + memset(m_sr, 0, sizeof(m_sr)); + + m_timeout_timer = timer_alloc(FUNC(saa5070_device::timeout), this); + m_dial_timer = timer_alloc(FUNC(saa5070_device::dial), this); + + save_item(NAME(m_index)); + save_item(NAME(m_sr)); +} + +void saa5070_device::device_reset() +{ + write_direct(0, 0x81); // Status 0: LTXRDY, IBTXRDY + write_direct(1, 0x80); // Status 1: TTXRDY + write_direct(2, 0x00); // Mode + write_direct(3, 0x00); // Command + write_direct(6, 0x00); // Port A + write_direct(7, 0x00); // Port B + write_direct(8, 0x00); // Dial Control and Timing + + m_line_uart->reset(); + m_tape_uart->reset(); +} + + +void saa5070_device::device_add_mconfig(machine_config &config) +{ + SAA5070_UART(config, m_line_uart, DERIVED_CLOCK(1, 1)); + m_line_uart->txd_handler().set(FUNC(saa5070_device::write_txdata)); + SAA5070_UART(config, m_tape_uart, DERIVED_CLOCK(1, 1)); +} + + +TIMER_CALLBACK_MEMBER(saa5070_device::timeout) +{ + m_sr[8] &= ~0x10; // 1.5s TIMER + m_sr[8] &= ~0x40; // 60s TIMER +} + +TIMER_CALLBACK_MEMBER(saa5070_device::dial) +{ + m_sr[8] &= ~0x20; // DIAL GO +} + + +void saa5070_device::address_w(uint8_t data) +{ + m_index = data & 0x0f; +} + +uint8_t saa5070_device::data_r() +{ + switch (m_index) + { + case 0x0: // Status Register 0 + // b0 IBTXRDY - IBUS B transmitter ready - previous transmission complete + // b1 IBRXRDY - IBUS B receiver ready - data available + // b2 LIDCD - line instantaneous data carrier detect + // b3 LPERR - line receiver parity error flag + // b4 LFERR - line receiver framing error - received stop bit not HIGH + // b5 LDCD - line data carrier detected + // b6 LRXRDY - line receiver ready - data available + // b7 LTXRDY - line transmitter ready - transmit holding register empty + m_sr[0] = (m_sr[0] & 0x27) | m_line_uart->status(); + break; + case 0x1: // Status Register 1 + // b0 + // b1 IARXRDY - IBUS A receiver ready - data available + // b2 + // b3 TPERR - tape receiver parity error flag + // b4 TFERR - tape receiver framing error - received stop nit not HIGH + // b5 TDCD - tape data carrier detected + // b6 TRXRDY - tape receiver ready - data available + // b7 TTXRDY - tape transmitter ready - transmit holding register empty + m_sr[1] = (m_sr[1] & 0x27) | m_tape_uart->status(); + break; + case 0x2: // Mode Register + break; + case 0x3: // Command Register + break; + case 0x4: // Line Receive Holding Register + m_sr[4] = m_line_uart->rx_byte(); + break; + case 0x5: // Tape Receive Holding Register + m_sr[5] = m_tape_uart->rx_byte(); + break; + case 0x6: // Port A + m_sr[6] = m_in_a_handler() & 0x1f; + break; + case 0x7: // Port B + m_sr[7] = m_in_b_handler() & 0x0f; + break; + case 0x8: // Dial Control and Timing Register + break; + case 0xa: // IBUS A Registers + logerror("IBUS Receiver A not implemented %02x\n", m_sr[m_index]); + break; + case 0xb: // IBUS B Registers + logerror("IBUS Receiver B not implemented %02x\n", m_sr[m_index]); + break; + } + + return m_sr[m_index]; +} + +void saa5070_device::data_w(uint8_t data) +{ + m_sr[m_index] = data; + + switch (m_index) + { + case 0x2: // Mode Register + { + // b0 + // b1 + // b2 nTPEN - tape parity enable command + // b3 TPO/nE - tape parity odd/even command + // b4 + // b5 n75/1200 - baud rate selection command for line modulator and line transmit shift register + // b6 nLPEN - line parity enable command + // b7 LPO/nE - line parity odd/even command + int tape_parity = device_serial_interface::PARITY_NONE; + if (BIT(data, 2)) + tape_parity = BIT(data, 3) ? device_serial_interface::PARITY_ODD : device_serial_interface::PARITY_EVEN; + m_tape_uart->set_baud_rate(1300, 1300, tape_parity); + int line_parity = device_serial_interface::PARITY_NONE; + if (BIT(data, 6)) + line_parity = BIT(data, 7) ? device_serial_interface::PARITY_ODD : device_serial_interface::PARITY_EVEN; + m_line_uart->set_baud_rate(1200, BIT(data, 5) ? 1200 : 75, line_parity); // V23 Modem (1200/75 baud) + break; + } + case 0x3: // Command Register + // b0 nDLEN A/nDLIM A - three line/two line control for IBUS A receiver + // b1 CLCK IN/nOUT - input/output control for 62.5 kHz clock pin + // b2 TTXEN - tape transmitter enable + // b3 TRXEN - tape receiver enable + // b4 LDBEN - line demodulator output buffer enable + // b5 RESET + // b6 LTXEN - line transmitter and modulator enable + // b7 LRXEN - line receiver enable + m_tape_uart->tx_enable(BIT(data, 2)); + m_tape_uart->rx_enable(BIT(data, 3)); + + if (BIT(data, 5)) + { + device_reset(); + } + + m_line_uart->tx_enable(BIT(data, 6)); + m_line_uart->rx_enable(BIT(data, 7)); + break; + case 0x4: // Line Transmit Holding Register + m_line_uart->tx_byte(data); + break; + case 0x5: // Tape Transmit Holding Register + m_tape_uart->tx_byte(data); + break; + case 0x6: // Port A + m_out_a_handler(data & 0x1f); + break; + case 0x7: // Port B + m_out_b_handler(data & 0x0f); + break; + case 0x8: // Dial Control and Timing Register + // b0 D10 + // b1 D11 + // b2 D12 + // b3 D13 + // b4 1.5s TIMER + // b5 DIAL GO + // b6 60s TIMER + // b7 UK/nEUR - impulsing ratio control for UK and European standards + if (BIT(data, 4)) + { + m_timeout_timer->adjust(attotime::from_msec(1'500)); + } + if (BIT(data, 5)) + { + m_sr[0] &= ~0x24; // LIDCD/LDCD + int digit_counter = (data & 0x0f) == 0 ? 10 : (data & 0x0f); + // each digit impulse = 100ms, inter digit pause = 800ms + m_dial_timer->adjust(attotime::from_msec((100 * digit_counter) + 800)); // TODO: digit impulse generator output on IMP line + } + if (BIT(data, 6)) + { + m_timeout_timer->adjust(attotime::from_msec(60'000)); + } + break; + case 0xb: // IBUS B Registers + logerror("IBUS Transmitter B not implemented %02x\n", m_sr[m_index]); + break; + } +} + +uint8_t saa5070_device::read_direct(offs_t offset) +{ + m_index = offset & 0x0f; + + return data_r(); +} + +void saa5070_device::write_direct(offs_t offset, uint8_t data) +{ + m_index = offset & 0x0f; + + data_w(data); +} diff --git a/src/devices/machine/saa5070.h b/src/devices/machine/saa5070.h new file mode 100644 index 00000000000..5561e596517 --- /dev/null +++ b/src/devices/machine/saa5070.h @@ -0,0 +1,151 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/********************************************************************* + + SAA5070 Viewdata Input/Output Peripheral (VIOP) + +********************************************************************** + + Pinning: ____ ____ + | \/ | + Vss 1 | | 40 DON + IMP 2 | | 39 CARDET + TFSKIN 3 | | 38 FSKIN + TFSKOUT 4 | | 37 DOCDI + FSKOUT 5 | | 36 CS + TXDATA 6 | | 35 nWR + RXDATA 7 | | 34 nRD + F1 8 | | 33 D7 + DLIM B 9 | | 32 D6 + nDATA B 10 | SAA5070 | 31 D5 + nDLEN B 11 | | 30 D4 + nDATA A 12 | | 29 D3 + DLIM A/nDLEN A 13 | | 28 D2 + IBCLK 14 | | 27 D1 + PA4 15 | | 26 D0 + PA3 16 | | 25 ALE + PA2 17 | | 24 PB0 + PA1 18 | | 23 PB1 + PA0 19 | | 22 PB2 + Vdd 20 | | 21 PB3 + |__________| + +*********************************************************************/ + +#ifndef MAME_MACHINE_SAA5070_H +#define MAME_MACHINE_SAA5070_H + +#pragma once + +#include "diserial.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> saa5070_uart_device + +class saa5070_uart_device : public device_t, public device_serial_interface +{ +public: + saa5070_uart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + auto txd_handler() { return m_txd_handler.bind(); } + + void set_baud_rate(int rxbaud, int txbaud, int parity); + uint8_t status() { return m_status; } + + void tx_byte(uint8_t data); + uint8_t rx_byte(); + + void rx_enable(int state) { m_rx_enabled = state; } + void tx_enable(int state) { m_tx_enabled = state; } + + void write_rxd(int state); + void write_dcd(int state); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void rcv_callback() override; + virtual void rcv_complete() override; + virtual void tra_callback() override; + virtual void tra_complete() override; + +private: + devcb_write_line m_txd_handler; + + uint8_t m_status; + int m_rx_enabled; + int m_tx_enabled; + int m_rxd; +}; + + +// ======================> saa5070_device + +class saa5070_device : public device_t +{ +public: + saa5070_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // port handlers + auto readpa_handler() { return m_in_a_handler.bind(); } + auto readpb_handler() { return m_in_b_handler.bind(); } + auto writepa_handler() { return m_out_a_handler.bind(); } + auto writepb_handler() { return m_out_b_handler.bind(); } + + // autodial handler + auto imp_handler() { return m_imp_handler.bind(); } + + // uart handlers + auto txdata_handler() { return m_txdata_handler.bind(); } + + void write_rxdata(int state) { m_line_uart->write_rxd(state); } + void write_cardet(int state) { m_line_uart->write_dcd(state); } + + // read/write access + void address_w(uint8_t data); + uint8_t data_r(); + void data_w(uint8_t data); + + // direct-mapped read/write access + uint8_t read_direct(offs_t offset); + void write_direct(offs_t offset, uint8_t data); + +protected: + // device_t overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + +private: + required_device m_line_uart; + required_device m_tape_uart; + + devcb_read8 m_in_a_handler; + devcb_read8 m_in_b_handler; + devcb_write8 m_out_a_handler; + devcb_write8 m_out_b_handler; + devcb_write_line m_imp_handler; + devcb_write_line m_txdata_handler; + + emu_timer *m_timeout_timer; + emu_timer *m_dial_timer; + + void write_txdata(int state) { m_txdata_handler(state); } + + TIMER_CALLBACK_MEMBER(timeout); + TIMER_CALLBACK_MEMBER(dial); + + uint8_t m_index = 0; + uint8_t m_sr[16]; +}; + + +// device type definition +DECLARE_DEVICE_TYPE(SAA5070, saa5070_device) + + +#endif // MAME_MACHINE_SAA5070_H -- cgit v1.2.3