// license:BSD-3-Clause // copyright-holders:Vas Crabb #include "emu.h" #include "vsmile_ctrl.h" #include #include //#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_interface(device, "vsmilectrl") , m_port(dynamic_cast(device.owner())) { } device_vsmile_ctrl_interface::~device_vsmile_ctrl_interface() { } void device_vsmile_ctrl_interface::interface_validity_check(validity_checker &valid) const { 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() { 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_single_card_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_resolve_objects() { m_device = get_card_device();; m_rts_cb.resolve_safe(); m_data_cb.resolve_safe(); } void vsmile_ctrl_port_device::device_start() { if (m_device) 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); }