summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/vr0uart.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/vr0uart.cpp')
-rw-r--r--src/devices/machine/vr0uart.cpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/devices/machine/vr0uart.cpp b/src/devices/machine/vr0uart.cpp
new file mode 100644
index 00000000000..cfaf69e8863
--- /dev/null
+++ b/src/devices/machine/vr0uart.cpp
@@ -0,0 +1,220 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+ MagicEyes VRender0 UART sub-device
+
+ Device by Angelo Salese
+
+ TODO:
+ - The only current example (Trivia R Us touchscreen) expects to read
+ stuff before transmitting anything, except for loopback test and a
+ signal break enabling at POST (!?).
+
+***************************************************************************/
+
+#include "emu.h"
+#include "vrender0.h"
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(VRENDER0_UART, vr0uart_device, "vr0uart", "MagicEyes VRender0 UART")
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// vr0uart_device - constructor
+//-------------------------------------------------
+
+vr0uart_device::vr0uart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, VRENDER0_UART, tag, owner, clock),
+ device_serial_interface(mconfig, *this)
+{
+}
+
+void vr0uart_device::regs_map(address_map &map)
+{
+ map(0x00, 0x03).rw(FUNC(vr0uart_device::control_r), FUNC(vr0uart_device::control_w));
+ map(0x04, 0x07).r(FUNC(vr0uart_device::status_r));
+ map(0x08, 0x0b).w(FUNC(vr0uart_device::transmit_buffer_w));
+ map(0x0c, 0x0f).r(FUNC(vr0uart_device::receive_buffer_r));
+ map(0x10, 0x13).rw(FUNC(vr0uart_device::baud_rate_div_r), FUNC(vr0uart_device::baud_rate_div_w));
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void vr0uart_device::device_start()
+{
+ save_item(NAME(m_ucon));
+ save_item(NAME(m_ubdr));
+ save_item(NAME(m_ustat));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void vr0uart_device::device_reset()
+{
+ m_ucon = 0x001;
+ m_ubdr = 1;
+ m_urxb_fifo.clear();
+
+ update_serial_config();
+}
+
+inline void vr0uart_device::tx_send_byte(uint8_t val)
+{
+ transmit_register_setup(val);
+ m_ustat |= 0x20;
+}
+
+inline uint32_t vr0uart_device::calculate_baud_rate()
+{
+ uint32_t div_rate = ((m_ubdr & 0xffff) + 1) * 16;
+ // TODO: external / internal serial clock config
+ return (this->clock() / 2) / div_rate;
+}
+
+void vr0uart_device::update_serial_config()
+{
+ const parity_t parity_modes[4] = { PARITY_NONE, PARITY_NONE, PARITY_EVEN, PARITY_ODD };
+
+ uint8_t word_length = m_ucon & 1 ? 8 : 7;
+ parity_t parity_mode = parity_modes[(m_ucon & 0xc) >> 2];
+ stop_bits_t stop_bits = m_ucon & 2 ? STOP_BITS_2 : STOP_BITS_1;
+
+ set_data_frame(1, word_length, parity_mode, stop_bits);
+
+ if (m_ucon & 0x100) // UART Enable
+ {
+ uint32_t clock_rate = calculate_baud_rate();
+ set_rcv_rate(clock_rate);
+ set_tra_rate(clock_rate);
+ }
+ else
+ {
+ set_rcv_rate(0);
+ set_tra_rate(0);
+ }
+}
+
+void vr0uart_device::tra_callback()
+{
+ int bit = transmit_register_get_data_bit();
+ m_ustat |= 0x40;
+ m_parent->write_line_tx(m_channel_num, bit);
+}
+
+void vr0uart_device::tra_complete()
+{
+ m_ustat &= ~0x60;
+ m_parent->IntReq(m_channel_num ? 18 : 15);
+}
+
+void vr0uart_device::rcv_complete()
+{
+ receive_register_extract();
+ if (is_receive_parity_error())
+ m_ustat |= 2;
+ if (is_receive_framing_error())
+ m_ustat |= 4;
+
+ if (!m_urxb_fifo.full())
+ {
+ // TODO: break detection
+ m_urxb_fifo.enqueue(get_received_char());
+ }
+ else
+ m_ustat |= 1; // overrun
+
+ if (m_ucon & 0x20 && m_ustat & 0xf)
+ m_parent->IntReq(m_channel_num ? 16 : 13);
+ else
+ m_parent->IntReq(m_channel_num ? 17 : 14);
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+/*
+ * ---x ---- ---- UART enable
+ * ---- x--- ---- Loopback Test
+ * ---- -x-- ---- Send Break mode
+ * ---- --x- ---- Generate interrupt on break or error
+ * ---- ---x ---- Serial Clock selection (1=external)
+ * ---- ---- xx-- Parity Mode (0x=No Parity, 10=Even, 11=Odd)
+ * ---- ---- --x- Stop Bits (1=2 bits, 0=1 Bit)
+ * ---- ---- ---x Word Length (1=8 bits, 0=7 bits)
+ */
+READ32_MEMBER( vr0uart_device::control_r )
+{
+ return m_ucon;
+}
+
+WRITE32_MEMBER( vr0uart_device::control_w )
+{
+ COMBINE_DATA(&m_ucon);
+ update_serial_config();
+}
+
+/*
+ * xxxx ---- ---- Receive FIFO count
+ * ---- -x-- ---- Tx buffer not empty, Tx holding data
+ * ---- --x- ---- Tx buffer not empty
+ * ---- ---x ---- Rx buffer not empty
+ * ---- ---- x--- Break detect
+ * ---- ---- -x-- Frame error
+ * ---- ---- --x- Parity error
+ * ---- ---- ---x Overrun Error
+ */
+READ32_MEMBER( vr0uart_device::status_r )
+{
+ uint32_t res = m_ustat;
+ if (!m_urxb_fifo.empty())
+ {
+ res |= 0x10;
+ res |= (m_urxb_fifo.queue_length() << 8);
+ }
+ // Break detect and errors are cleared by reading this
+ m_ustat &= ~0xf;
+ return res;
+}
+
+WRITE32_MEMBER( vr0uart_device::transmit_buffer_w )
+{
+ if (ACCESSING_BITS_0_7)
+ tx_send_byte(data & 0xff);
+}
+
+READ32_MEMBER( vr0uart_device::receive_buffer_r )
+{
+ // TODO: unknown value & behaviour attempting to read this on empty FIFO (stall?)
+ uint8_t res = 0;
+
+ if (ACCESSING_BITS_0_7 && !m_urxb_fifo.empty())
+ res = m_urxb_fifo.dequeue();
+
+ return res;
+}
+
+READ32_MEMBER( vr0uart_device::baud_rate_div_r )
+{
+ return m_ubdr;
+}
+
+WRITE32_MEMBER( vr0uart_device::baud_rate_div_w )
+{
+ COMBINE_DATA(&m_ubdr);
+ update_serial_config();
+}