1 // license:BSD-3-Clause
2 // copyright-holders:Ryan Holtz, Vas Crabb
3 
4 #include "emu.h"
5 #include "pad.h"
6 
7 #include <algorithm>
8 
9 //#define VERBOSE 1
10 #include "logmacro.h"
11 
12 
13 //**************************************************************************
14 //  GLOBAL VARIABLES
15 //**************************************************************************
16 
17 DEFINE_DEVICE_TYPE(VSMILE_PAD, vsmile_pad_device, "vsmile_pad", "V.Smile Joystick")
18 
19 
20 //**************************************************************************
21 //    V.Smile control pad
22 //**************************************************************************
23 
vsmile_pad_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)24 vsmile_pad_device::vsmile_pad_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock)
25 	: vsmile_pad_device(mconfig, VSMILE_PAD, tag, owner, clock)
26 {
27 }
28 
vsmile_pad_device(machine_config const & mconfig,device_type type,char const * tag,device_t * owner,uint32_t clock)29 vsmile_pad_device::vsmile_pad_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, uint32_t clock)
30 	: vsmile_ctrl_device_base(mconfig, type, tag, owner, clock)
31 	, m_io_joy(*this, "JOY")
32 	, m_io_colors(*this, "COLORS")
33 	, m_io_buttons(*this, "BUTTONS")
34 	, m_sent_joy(0x0000U)
35 	, m_sent_colors(0x0000U)
36 	, m_sent_buttons(0x0000U)
37 	, m_active(false)
38 	, m_idle_timer(nullptr)
39 {
40 	std::fill(std::begin(m_ctrl_probe_history), std::end(m_ctrl_probe_history), 0U);
41 	m_stale = stale_all();
42 }
43 
~vsmile_pad_device()44 vsmile_pad_device::~vsmile_pad_device()
45 {
46 }
47 
device_start()48 void vsmile_pad_device::device_start()
49 {
50 	vsmile_ctrl_device_base::device_start();
51 
52 	m_idle_timer = machine().scheduler().timer_alloc(
53 			timer_expired_delegate(FUNC(vsmile_pad_device::handle_idle), this));
54 	m_idle_timer->adjust(attotime::from_seconds(1));
55 
56 	m_sent_joy = 0x0000U;
57 	m_sent_colors = 0x0000U;
58 	m_sent_buttons = 0x0000U;
59 	m_stale = stale_all();
60 	m_active = false;
61 
62 	save_item(NAME(m_sent_joy));
63 	save_item(NAME(m_sent_colors));
64 	save_item(NAME(m_sent_buttons));
65 	save_item(NAME(m_stale));
66 	save_item(NAME(m_active));
67 	save_item(NAME(m_ctrl_probe_history));
68 }
69 
tx_complete()70 void vsmile_pad_device::tx_complete()
71 {
72 	// update joystick
73 	if ((m_stale & STALE_JOY) != STALE_NONE)
74 	{
75 		m_sent_joy = m_io_joy->read();
76 		if ((m_stale & STALE_UP_DOWN) != STALE_NONE)
77 		{
78 			if (BIT(m_sent_joy, 0))
79 				uart_tx_fifo_push(0x87); // up
80 			else if (BIT(m_sent_joy, 1))
81 				uart_tx_fifo_push(0x8f); // down
82 			else
83 				uart_tx_fifo_push(0x80);
84 		}
85 		if ((m_stale & STALE_LEFT_RIGHT) != STALE_NONE)
86 		{
87 			if (BIT(m_sent_joy, 2))
88 				uart_tx_fifo_push(0xcf); // left
89 			else if (BIT(m_sent_joy, 3))
90 				uart_tx_fifo_push(0xc7); // right
91 			else
92 				uart_tx_fifo_push(0xc0);
93 		}
94 	}
95 
96 	// update colors
97 	if ((m_stale & STALE_COLORS) != STALE_NONE)
98 	{
99 		m_sent_colors = m_io_colors->read();
100 		uart_tx_fifo_push(0x90 | m_sent_colors);
101 	}
102 
103 	// update buttons
104 	if ((m_stale & STALE_BUTTONS) != STALE_NONE)
105 	{
106 		m_sent_buttons = m_io_buttons->read();
107 		if (((m_stale & STALE_OK) != STALE_NONE) && BIT(m_sent_buttons, 0))
108 			uart_tx_fifo_push(0xa1);
109 		if (((m_stale & STALE_QUIT) != STALE_NONE) && BIT(m_sent_buttons, 1))
110 			uart_tx_fifo_push(0xa2);
111 		if (((m_stale & STALE_HELP) != STALE_NONE) && BIT(m_sent_buttons, 2))
112 			uart_tx_fifo_push(0xa3);
113 		if (((m_stale & STALE_ABC) != STALE_NONE) && BIT(m_sent_buttons, 3))
114 			uart_tx_fifo_push(0xa4);
115 		if (!m_sent_buttons)
116 			uart_tx_fifo_push(0xa0);
117 	}
118 
119 	// if nothing happens in the next second we'll queue a keep-alive
120 	if (!m_active)
121 		LOG("entered active state\n");
122 	m_idle_timer->adjust(attotime::from_seconds(1));
123 	m_active = true;
124 	m_stale = STALE_NONE;
125 }
126 
tx_timeout()127 void vsmile_pad_device::tx_timeout()
128 {
129 	if (m_active)
130 	{
131 		m_idle_timer->reset();
132 		m_active = false;
133 		m_stale = stale_all();
134 		std::fill(std::begin(m_ctrl_probe_history), std::end(m_ctrl_probe_history), 0U);
135 		LOG("left active state\n");
136 	}
137 	uart_tx_fifo_push(0x55);
138 }
139 
rx_complete(uint8_t data,bool select)140 void vsmile_pad_device::rx_complete(uint8_t data, bool select)
141 {
142 	if (select)
143 	{
144 		if (((data & 0xf0) == 0x70) || ((data & 0xf0) == 0xb0))
145 		{
146 			m_ctrl_probe_history[0] = ((data & 0xf0) == 0x70) ? 0 : m_ctrl_probe_history[1];
147 			m_ctrl_probe_history[1] = data;
148 			uint8_t const response = ((m_ctrl_probe_history[0] + m_ctrl_probe_history[1] + 0x0f) & 0x0f) ^ 0x05;
149 			LOG(
150 					"%s: received probe %02X, %02X, sending response %02X\n",
151 					machine().describe_context(),
152 					m_ctrl_probe_history[0],
153 					m_ctrl_probe_history[1],
154 					0xb0 | response);
155 			uart_tx_fifo_push(0xb0 | response);
156 		}
157 	}
158 }
159 
uart_tx_fifo_push(uint8_t data)160 void vsmile_pad_device::uart_tx_fifo_push(uint8_t data)
161 {
162 	m_idle_timer->reset();
163 	queue_tx(data);
164 }
165 
TIMER_CALLBACK_MEMBER(vsmile_pad_device::handle_idle)166 TIMER_CALLBACK_MEMBER(vsmile_pad_device::handle_idle)
167 {
168 	LOG("idle timer expired, sending keep-alive 55\n");
169 	queue_tx(0x55);
170 }
171 
INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_joy_changed)172 INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_joy_changed)
173 {
174 	if (m_active)
175 	{
176 		if (!is_tx_empty())
177 		{
178 			LOG("joy changed while transmission in progress, marking stale\n");
179 			m_stale |= stale_pad_inputs(param);
180 		}
181 		else
182 		{
183 			uint8_t const joy = m_io_joy->read();
184 			if ((joy ^ m_sent_joy) & 0x03)
185 			{
186 				if (BIT(joy, 0))
187 					uart_tx_fifo_push(0x87); // up
188 				else if (BIT(joy, 1))
189 					uart_tx_fifo_push(0x8f); // down
190 				else
191 					uart_tx_fifo_push(0x80);
192 			}
193 			if ((joy ^ m_sent_joy) & 0x0c)
194 			{
195 				if (BIT(joy, 2))
196 					uart_tx_fifo_push(0xcf); // left
197 				else if (BIT(joy, 3))
198 					uart_tx_fifo_push(0xc7); // right
199 				else
200 					uart_tx_fifo_push(0xc0);
201 			}
202 			m_sent_joy = joy;
203 		}
204 	}
205 }
206 
INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_color_changed)207 INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_color_changed)
208 {
209 	if (m_active)
210 	{
211 		if (!is_tx_empty())
212 		{
213 			LOG("colors changed while transmission in progress, marking stale\n");
214 			m_stale |= STALE_COLORS;
215 		}
216 		else
217 		{
218 			m_sent_colors = m_io_colors->read();
219 			uart_tx_fifo_push(0x90 | m_sent_colors);
220 		}
221 	}
222 }
223 
INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_button_changed)224 INPUT_CHANGED_MEMBER(vsmile_pad_device::pad_button_changed)
225 {
226 	if (m_active)
227 	{
228 		if (!is_tx_empty())
229 		{
230 			LOG("buttons changed while transmission in progress, marking stale\n");
231 			m_stale |= stale_pad_inputs(param);
232 		}
233 		else
234 		{
235 			uint8_t const buttons = m_io_buttons->read();
236 			if (BIT((m_sent_buttons ^ buttons) & buttons, 0))
237 				uart_tx_fifo_push(0xa1);
238 			if (BIT((m_sent_buttons ^ buttons) & buttons, 1))
239 				uart_tx_fifo_push(0xa2);
240 			if (BIT((m_sent_buttons ^ buttons) & buttons, 2))
241 				uart_tx_fifo_push(0xa3);
242 			if (BIT((m_sent_buttons ^ buttons) & buttons, 3))
243 				uart_tx_fifo_push(0xa4);
244 			if (!buttons)
245 				uart_tx_fifo_push(0xa0);
246 			m_sent_buttons = buttons;
247 		}
248 	}
249 }
250 
251 static INPUT_PORTS_START( vsmile_pad )
252 	PORT_START("JOY")
PORT_CHANGED_MEMBER(DEVICE_SELF,vsmile_pad_device,pad_joy_changed,vsmile_pad_device::STALE_UP_DOWN)253 	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")
254 	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")
255 	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")
256 	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")
257 	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )
258 
259 	PORT_START("COLORS")
260 	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")
261 	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")
262 	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")
263 	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")
264 	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )
265 
266 	PORT_START("BUTTONS")
267 	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")
268 	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")
269 	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")
270 	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")
271 	PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED )
272 INPUT_PORTS_END
273 
274 ioport_constructor vsmile_pad_device::device_input_ports() const
275 {
276 	return INPUT_PORTS_NAME( vsmile_pad );
277 }
278