summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/vsmile/vsmile_ctrl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/vsmile/vsmile_ctrl.cpp')
-rw-r--r--src/devices/bus/vsmile/vsmile_ctrl.cpp315
1 files changed, 315 insertions, 0 deletions
diff --git a/src/devices/bus/vsmile/vsmile_ctrl.cpp b/src/devices/bus/vsmile/vsmile_ctrl.cpp
new file mode 100644
index 00000000000..a8cbf25278b
--- /dev/null
+++ b/src/devices/bus/vsmile/vsmile_ctrl.cpp
@@ -0,0 +1,315 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+#include "emu.h"
+#include "vsmile_ctrl.h"
+
+#include <algorithm>
+#include <cassert>
+
+//#define VERBOSE 1
+#include "logmacro.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE(VSMILE_CTRL_PORT, vsmile_ctrl_port_device, "vsmile_ctrl_port", "V.Smile Controller Port")
+
+
+//**************************************************************************
+// V.Smile controller interface
+//**************************************************************************
+
+device_vsmile_ctrl_interface::device_vsmile_ctrl_interface(const machine_config &mconfig, device_t &device)
+ : device_slot_card_interface(mconfig, device)
+ , m_port(dynamic_cast<vsmile_ctrl_port_device *>(device.owner()))
+{
+}
+
+device_vsmile_ctrl_interface::~device_vsmile_ctrl_interface()
+{
+}
+
+void device_vsmile_ctrl_interface::interface_validity_check(validity_checker &valid) const
+{
+ device_slot_card_interface::interface_validity_check(valid);
+
+ if (device().owner() && !m_port)
+ {
+ osd_printf_error(
+ "Owner device %s (%s) is not a vsmile_ctrl_port_device\n",
+ device().owner()->tag(),
+ device().owner()->name());
+ }
+}
+
+void device_vsmile_ctrl_interface::interface_pre_start()
+{
+ device_slot_card_interface::interface_pre_start();
+
+ if (m_port && !m_port->started())
+ throw device_missing_dependencies();
+}
+
+
+//**************************************************************************
+// V.Smile controller port
+//**************************************************************************
+
+vsmile_ctrl_port_device::vsmile_ctrl_port_device(
+ machine_config const &mconfig,
+ char const *tag,
+ device_t *owner,
+ uint32_t clock)
+ : device_t(mconfig, VSMILE_CTRL_PORT, tag, owner, clock)
+ , device_slot_interface(mconfig, *this)
+ , m_rts_cb(*this)
+ , m_data_cb(*this)
+{
+}
+
+vsmile_ctrl_port_device::~vsmile_ctrl_port_device()
+{
+}
+
+void vsmile_ctrl_port_device::device_validity_check(validity_checker &valid) const
+{
+ device_t *const card(get_card_device());
+ if (card && !dynamic_cast<device_vsmile_ctrl_interface *>(card))
+ {
+ osd_printf_error(
+ "Card device %s (%s) does not implement device_vsmile_ctrl_interface\n",
+ card->tag(),
+ card->name());
+ }
+}
+
+void vsmile_ctrl_port_device::device_resolve_objects()
+{
+ device_vsmile_ctrl_interface *const card(dynamic_cast<device_vsmile_ctrl_interface *>(get_card_device()));
+ if (card)
+ m_device = card;
+
+ m_rts_cb.resolve_safe();
+ m_data_cb.resolve_safe();
+}
+
+void vsmile_ctrl_port_device::device_start()
+{
+ device_t *const card(get_card_device());
+ if (card)
+ {
+ if (!m_device)
+ {
+ throw emu_fatalerror(
+ "vsmile_ctrl_port_device: card device %s (%s) does not implement device_vsmile_ctrl_interface\n",
+ card->tag(),
+ card->name());
+ }
+ else
+ {
+ m_device->select_w(0);
+ }
+ }
+}
+
+
+//**************************************************************************
+// V.Smile controller HLE base
+//**************************************************************************
+
+vsmile_ctrl_device_base::vsmile_ctrl_device_base(
+ machine_config const &mconfig,
+ device_type type,
+ char const *tag,
+ device_t *owner,
+ uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_vsmile_ctrl_interface(mconfig, *this)
+ , m_tx_timer(nullptr)
+ , m_rts_timer(nullptr)
+ , m_tx_fifo_head(0U)
+ , m_tx_fifo_tail(0U)
+ , m_tx_fifo_empty(true)
+ , m_tx_active(false)
+ , m_select(false)
+{
+ std::fill(std::begin(m_tx_fifo), std::end(m_tx_fifo), 0);
+}
+
+vsmile_ctrl_device_base::~vsmile_ctrl_device_base()
+{
+}
+
+void vsmile_ctrl_device_base::device_start()
+{
+ // allocate a timer to limit transmit rate to something realistic
+ m_tx_timer = machine().scheduler().timer_alloc(
+ timer_expired_delegate(FUNC(vsmile_ctrl_device_base::tx_timer_expired), this));
+
+ // allocate a timer for RTS timeouts
+ m_rts_timer = machine().scheduler().timer_alloc(
+ timer_expired_delegate(FUNC(vsmile_ctrl_device_base::rts_timer_expired), this));
+
+ // start with transmit queue empty
+ m_tx_fifo_head = m_tx_fifo_tail = 0U;
+ m_tx_fifo_empty = true;
+ m_tx_active = false;
+
+ // register for save states
+ save_item(NAME(m_tx_fifo));
+ save_item(NAME(m_tx_fifo_head));
+ save_item(NAME(m_tx_fifo_tail));
+ save_item(NAME(m_tx_fifo_empty));
+ save_item(NAME(m_select));
+}
+
+bool vsmile_ctrl_device_base::queue_tx(uint8_t data)
+{
+ // return false on overrun and drop byte
+ bool const was_empty(m_tx_fifo_empty);
+ if (!was_empty && (m_tx_fifo_head == m_tx_fifo_tail))
+ {
+ LOG(
+ "%s: discarding byte %02X because FIFO is full (length %u, Tx %sactive)\n",
+ machine().describe_context(),
+ data,
+ ARRAY_LENGTH(m_tx_fifo),
+ m_tx_active ? "" : "in");
+ return false;
+ }
+
+ // queue the byte
+ m_tx_fifo[m_tx_fifo_tail] = data;
+ m_tx_fifo_tail = (m_tx_fifo_tail + 1) % ARRAY_LENGTH(m_tx_fifo);
+ m_tx_fifo_empty = false;
+
+ // assert RTS and start transmitting if necessary
+ if (was_empty)
+ {
+ rts_out(1);
+ if (m_select)
+ {
+ LOG("%s: transmitting byte %02X immediately (Tx was %sactive)\n", machine().describe_context(), data, m_tx_active ? "" : "in");
+ m_tx_active = true;
+ m_tx_timer->adjust(attotime::from_hz(9600 / 10));
+ }
+ else
+ {
+ LOG("%s: asserting RTS to transmit byte %02X\n", machine().describe_context(), data);
+ m_rts_timer->adjust(attotime::from_msec(500));
+ }
+ }
+ else
+ {
+ unsigned const fifo_used((m_tx_fifo_tail + ARRAY_LENGTH(m_tx_fifo) - m_tx_fifo_head) % ARRAY_LENGTH(m_tx_fifo));
+ LOG("%s: queued byte %02X (%u bytes queued, Tx %sactive)\n", machine().describe_context(), data, fifo_used, m_tx_active ? "" : "in");
+ }
+
+ // data was queued
+ return true;
+}
+
+void vsmile_ctrl_device_base::select_w(int state)
+{
+ if (bool(state) != m_select)
+ {
+ if (state && !m_tx_fifo_empty && !m_tx_active)
+ {
+ m_rts_timer->reset();
+ unsigned const fifo_used((m_tx_fifo_tail + ARRAY_LENGTH(m_tx_fifo) - m_tx_fifo_head) % ARRAY_LENGTH(m_tx_fifo));
+ LOG("%s: select asserted, starting transmission (%u bytes queued)\n", machine().describe_context(), fifo_used);
+ m_tx_active = true;
+ m_tx_timer->adjust(attotime::from_hz(9600 / 10));
+ }
+ else
+ {
+ LOG("%s: select %sasserted (Tx %sactive)\n", machine().describe_context(), state ? "" : "de", m_tx_active ? "" : "in");
+ }
+ m_select = bool(state);
+ }
+}
+
+void vsmile_ctrl_device_base::data_w(uint8_t data)
+{
+ LOG(
+ "%s: received byte %02X (select %sasserted, Tx %sactive)\n",
+ machine().describe_context(),
+ data,
+ m_select ? "" : "de",
+ m_tx_active ? "" : "in");
+ rx_complete(data, m_select);
+}
+
+TIMER_CALLBACK_MEMBER(vsmile_ctrl_device_base::tx_timer_expired)
+{
+ assert(!m_tx_fifo_empty);
+ assert(m_tx_active);
+
+ // deliver the byte to the host (bits have shifted out now)
+ uint8_t const data(m_tx_fifo[m_tx_fifo_head]);
+ m_tx_fifo_head = (m_tx_fifo_head + 1) % ARRAY_LENGTH(m_tx_fifo);
+ if (m_tx_fifo_head == m_tx_fifo_tail)
+ m_tx_fifo_empty = true;
+ data_out(data);
+
+ // if queue is drained give implmentation a chance to queue more before dropping RTS
+ if (m_tx_fifo_empty)
+ {
+ LOG("transmitted byte %02X, queue empty (select %sasserted)\n", data, m_select ? "" : "de");
+ tx_complete();
+ }
+ else
+ {
+ unsigned const fifo_used((m_tx_fifo_tail + ARRAY_LENGTH(m_tx_fifo) - m_tx_fifo_head) % ARRAY_LENGTH(m_tx_fifo));
+ LOG("transmitted byte %02X (%u bytes queued, select %sasserted)\n", data, fifo_used, m_select ? "" : "de");
+ }
+
+ // drop RTS if no more data, otherwise keep transmitting if CTS is still high
+ if (m_tx_fifo_empty)
+ {
+ LOG("nothing to transmit, deasserting RTS\n");
+ m_tx_active = false;
+ rts_out(0);
+ }
+ else if (m_select)
+ {
+ LOG("select asserted, transmitting next byte %02X\n", m_tx_fifo[m_tx_fifo_head]);
+ m_tx_timer->adjust(attotime::from_hz(9600 / 10));
+ }
+ else
+ {
+ LOG("select deasserted, waiting to transmit\n");
+ m_tx_active = false;
+ m_rts_timer->adjust(attotime::from_msec(500));
+ }
+}
+
+
+TIMER_CALLBACK_MEMBER(vsmile_ctrl_device_base::rts_timer_expired)
+{
+ assert(!m_tx_fifo_empty);
+ assert(!m_tx_active);
+
+ // clear out anything queued and let the implementation deal with it
+ if (!m_tx_fifo_empty)
+ {
+ unsigned const fifo_used((m_tx_fifo_tail + ARRAY_LENGTH(m_tx_fifo) - m_tx_fifo_head) % ARRAY_LENGTH(m_tx_fifo));
+ LOG("timeout waiting for select after asserting RTS (%u bytes queued)\n", fifo_used);
+ m_tx_fifo_head = m_tx_fifo_tail = 0U;
+ m_tx_fifo_empty = true;
+ tx_timeout();
+ if (m_tx_fifo_empty)
+ rts_out(0);
+ }
+}
+
+
+#include "pad.h"
+
+void vsmile_controllers(device_slot_interface &device)
+{
+ device.option_add("joy", VSMILE_PAD);
+}