// license:BSD-3-Clause // copyright-holders:Ryan Holtz, Vas Crabb #include "emu.h" #include "pad.h" #include //#define VERBOSE 1 #include "logmacro.h" //************************************************************************** // GLOBAL VARIABLES //************************************************************************** DEFINE_DEVICE_TYPE(VSMILE_PAD, vsmile_pad_device, "vsmile_pad", "V.Smile Joystick") //************************************************************************** // V.Smile control pad //************************************************************************** vsmile_pad_device::vsmile_pad_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock) : vsmile_pad_device(mconfig, VSMILE_PAD, tag, owner, clock) { } vsmile_pad_device::vsmile_pad_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, uint32_t clock) : vsmile_ctrl_device_base(mconfig, type, tag, owner, clock) , m_io_joy(*this, "JOY") , m_io_colors(*this, "COLORS") , m_io_buttons(*this, "BUTTONS") , m_sent_joy(0x0000U) , m_sent_colors(0x0000U) , m_sent_buttons(0x0000U) , m_active(false) , m_idle_timer(nullptr) { std::fill(std::begin(m_ctrl_probe_history), std::end(m_ctrl_probe_history), 0U); m_stale = stale_all(); } vsmile_pad_device::~vsmile_pad_device() { } void vsmile_pad_device::device_start() { vsmile_ctrl_device_base::device_start(); m_idle_timer = timer_alloc(FUNC(vsmile_pad_device::handle_idle), this); m_idle_timer->adjust(attotime::from_seconds(1)); m_sent_joy = 0x0000U; m_sent_colors = 0x0000U; m_sent_buttons = 0x0000U; m_stale = stale_all(); m_active = false; save_item(NAME(m_sent_joy)); save_item(NAME(m_sent_colors)); save_item(NAME(m_sent_buttons)); save_item(NAME(m_stale)); save_item(NAME(m_active)); save_item(NAME(m_ctrl_probe_history)); } void vsmile_pad_device::tx_complete() { // update joystick if ((m_stale & STALE_JOY) != STALE_NONE) { m_sent_joy = m_io_joy->read(); if ((m_stale & STALE_UP_DOWN) != STALE_NONE) { if (BIT(m_sent_joy, 0)) uart_tx_fifo_push(0x87); // up else if (BIT(m_sent_joy, 1)) uart_tx_fifo_push(0x8f); // down else uart_tx_fifo_push(0x80); } if ((m_stale & STALE_LEFT_RIGHT) != STALE_NONE) { if (BIT(m_sent_joy, 2)) uart_tx_fifo_push(0xcf); // left else if (BIT(m_sent_joy, 3)) uart_tx_fifo_push(0xc7); // right else uart_tx_fifo_push(0xc0); } } // update colors if ((m_stale & STALE_COLORS) != STALE_NONE) { m_sent_colors = m_io_colors->read(); uart_tx_fifo_push(0x90 | m_sent_colors); } // update buttons if ((m_stale & STALE_BUTTONS) != STALE_NONE) { m_sent_buttons = m_io_buttons->read(); if (((m_stale & STALE_OK) != STALE_NONE) && BIT(m_sent_buttons, 0)) uart_tx_fifo_push(0xa1); if (((m_stale & STALE_QUIT) != STALE_NONE) && BIT(m_sent_buttons, 1)) uart_tx_fifo_push(0xa2); if (((m_stale & STALE_HELP) != STALE_NONE) && BIT(m_sent_buttons, 2)) uart_tx_fifo_push(0xa3); if (((m_stale & STALE_ABC) != STALE_NONE) && BIT(m_sent_buttons, 3)) uart_tx_fifo_push(0xa4); if (!m_sent_buttons) uart_tx_fifo_push(0xa0); } // if nothing happens in the next second we'll queue a keep-alive if (!m_active) LOG("entered active state\n"); m_idle_timer->adjust(attotime::from_seconds(1)); m_active = true; m_stale = STALE_NONE; } void vsmile_pad_device::tx_timeout() { if (m_active) { m_idle_timer->reset(); m_active = false; m_stale = stale_all(); std::fill(std::begin(m_ctrl_probe_history), std::end(m_ctrl_probe_history), 0U); LOG("left active state\n"); } uart_tx_fifo_push(0x55); } void vsmile_pad_device::rx_complete(uint8_t data, bool select) { if (select) { if (((data & 0xf0) == 0x70) || ((data & 0xf0) == 0xb0)) { m_ctrl_probe_history[0] = ((data & 0xf0) == 0x70) ? 0 : m_ctrl_probe_history[1]; m_ctrl_probe_history[1] = data; uint8_t const response = ((m_ctrl_probe_history[0] + m_ctrl_probe_history[1] + 0x0f) & 0x0f) ^ 0x05; LOG( "%s: received probe %02X, %02X, sending response %02X\n", machine().describe_context(), m_ctrl_probe_history[0], m_ctrl_probe_history[1], 0xb0 | response); uart_tx_fifo_push(0xb0 | response); } } } void vsmile_pad_device::uart_tx_fifo_push(uint8_t data) { m_idle_timer->reset(); queue_tx(data); } TIMER_CALLBACK_MEMBER(vsmile_pad_device::handle_idle) { LOG("idle timer expired, sending keep-alive 55\n"); queue_tx(0x55); } INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_joy_changed) { if (m_active) { if (!is_tx_empty()) { LOG("joy changed while transmission in progress, marking stale\n"); m_stale |= stale_pad_inputs(param); } else { uint8_t const joy = m_io_joy->read(); if ((joy ^ m_sent_joy) & 0x03) { if (BIT(joy, 0)) uart_tx_fifo_push(0x87); // up else if (BIT(joy, 1)) uart_tx_fifo_push(0x8f); // down else uart_tx_fifo_push(0x80); } if ((joy ^ m_sent_joy) & 0x0c) { if (BIT(joy, 2)) uart_tx_fifo_push(0xcf); // left else if (BIT(joy, 3)) uart_tx_fifo_push(0xc7); // right else uart_tx_fifo_push(0xc0); } m_sent_joy = joy; } } } INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_color_changed) { if (m_active) { if (!is_tx_empty()) { LOG("colors changed while transmission in progress, marking stale\n"); m_stale |= STALE_COLORS; } else { m_sent_colors = m_io_colors->read(); uart_tx_fifo_push(0x90 | m_sent_colors); } } } INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_button_changed) { if (m_active) { if (!is_tx_empty()) { LOG("buttons changed while transmission in progress, marking stale\n"); m_stale |= stale_pad_inputs(param); } else { uint8_t const buttons = m_io_buttons->read(); if (BIT((m_sent_buttons ^ buttons) & buttons, 0)) uart_tx_fifo_push(0xa1); if (BIT((m_sent_buttons ^ buttons) & buttons, 1)) uart_tx_fifo_push(0xa2); if (BIT((m_sent_buttons ^ buttons) & buttons, 2)) uart_tx_fifo_push(0xa3); if (BIT((m_sent_buttons ^ buttons) & buttons, 3)) uart_tx_fifo_push(0xa4); if (!buttons) uart_tx_fifo_push(0xa0); m_sent_buttons = buttons; } } } static INPUT_PORTS_START( vsmile_pad ) PORT_START("JOY") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_joy_changed, vsmile_pad_device::STALE_UP_DOWN) PORT_NAME("Joypad Up") PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_joy_changed, vsmile_pad_device::STALE_UP_DOWN) PORT_NAME("Joypad Down") PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_joy_changed, vsmile_pad_device::STALE_LEFT_RIGHT) PORT_NAME("Joypad Left") PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_joy_changed, vsmile_pad_device::STALE_LEFT_RIGHT) PORT_NAME("Joypad Right") PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("COLORS") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_color_changed, vsmile_pad_device::STALE_COLORS) PORT_NAME("Green") PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_color_changed, vsmile_pad_device::STALE_COLORS) PORT_NAME("Blue") PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_color_changed, vsmile_pad_device::STALE_COLORS) PORT_NAME("Yellow") PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_color_changed, vsmile_pad_device::STALE_COLORS) PORT_NAME("Red") PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("BUTTONS") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_button_changed, vsmile_pad_device::STALE_OK) PORT_NAME("OK") PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_button_changed, vsmile_pad_device::STALE_QUIT) PORT_NAME("Quit") PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_button_changed, vsmile_pad_device::STALE_HELP) PORT_NAME("Help") PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_CHANGED_MEMBER(DEVICE_SELF, vsmile_pad_device, pad_button_changed, vsmile_pad_device::STALE_ABC) PORT_NAME("ABC") PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) INPUT_PORTS_END ioport_constructor vsmile_pad_device::device_input_ports() const { return INPUT_PORTS_NAME( vsmile_pad ); }