1 // license:BSD-3-Clause
2 // copyright-holders:Patrick Mackinlay
3 
4 /*
5  * PS/2 mouse high-level emulation.
6  *
7  * This device emulates a mouse with three buttons, using the original three-
8  * byte PS/2 mouse protocol. Serial I/O is driven at 10kHz by a 40kHz timer
9  * to generate somewhat accurate clock rising and falling edges, as well as
10  * sampling or writing the data line in the middle of each high or low cycle
11  * as exoected by the protocol.
12  *
13  * The original IBM PS/2 mouse had only two buttons and the documented protocol
14  * reflects this, however it also allows a third button to be added without any
15  * significant changes. IBM later produced three-button mice which apparently
16  * took advantage of this, making it about as standard as it gets.
17  *
18  * Microsoft introduced the IntelliMouse in 1996 which adds another two buttons
19  * and a scroll wheel, requiring a change to the protocol from three to four
20  * byte data packets. The IntelliMouse protocol is only enabled after sending
21  * a specific sequence of "set sample rate" commands, without which the mouse
22  * uses the original protocol.
23  *
24  * Sources:
25  *
26  *   https://web.archive.org/web/20180126072045/http://www.computer-engineering.org/ps2mouse/
27  *   https://wiki.osdev.org/PS/2_Mouse
28  *   https://www.win.tue.nl/~aeb/linux/kbd/scancodes-13.html
29  *   http://read.pudn.com/downloads136/ebook/579116/docs/Designing%20a%20low%20cost%20CY7C63723%20combination%20mouse.pdf
30  *
31  * TODO
32  *   - IntelliMouse device/protocol (4-byte packet, 5 buttons, scroll wheel)
33  *   - configurable clock (10kHz-16.7kHz)
34  *   - receive parity error handling
35  */
36 
37 #include "emu.h"
38 #include "hle_mouse.h"
39 
40 #define LOG_GENERAL (1U << 0)
41 #define LOG_RXTX    (1U << 1)
42 #define LOG_COMMAND (1U << 2)
43 #define LOG_REPORT  (1U << 3)
44 #define LOG_STATE   (1U << 4)
45 
46 //#define VERBOSE (LOG_COMMAND)
47 
48 #include "logmacro.h"
49 
50 DEFINE_DEVICE_TYPE(HLE_PS2_MOUSE, hle_ps2_mouse_device, "hle_ps2_mouse", "HLE PS/2 Mouse")
51 
52 ALLOW_SAVE_TYPE(hle_ps2_mouse_device::serial_state);
53 
54 INPUT_PORTS_START(hle_ps2_mouse_device)
55 	PORT_START("mouse_x_axis")
56 	PORT_BIT(0xffff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(100)
57 
58 	PORT_START("mouse_y_axis")
59 	PORT_BIT(0xffff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(100)
60 
61 	PORT_START("mouse_buttons")
62 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Left Button")
63 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_CODE(MOUSECODE_BUTTON2) PORT_NAME("Right Button")
64 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_CODE(MOUSECODE_BUTTON3) PORT_NAME("Middle Button")
65 	PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED)
66 INPUT_PORTS_END
67 
68 static constexpr attotime serial_cycle = attotime::from_usec(25);
69 
hle_ps2_mouse_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)70 hle_ps2_mouse_device::hle_ps2_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
71 	: device_t(mconfig, HLE_PS2_MOUSE, tag, owner, clock)
72 	, device_pc_kbd_interface(mconfig, *this)
73 	, m_port_x_axis(*this, "mouse_x_axis")
74 	, m_port_y_axis(*this, "mouse_y_axis")
75 	, m_port_buttons(*this, "mouse_buttons")
76 {
77 }
78 
device_input_ports() const79 ioport_constructor hle_ps2_mouse_device::device_input_ports() const
80 {
81 	return INPUT_PORTS_NAME(hle_ps2_mouse_device);
82 }
83 
device_start()84 void hle_ps2_mouse_device::device_start()
85 {
86 	save_item(NAME(m_state));
87 	save_item(NAME(m_bit));
88 
89 	save_item(NAME(m_mode));
90 	save_item(NAME(m_sample_rate));
91 	save_item(NAME(m_resolution));
92 
93 	save_item(NAME(m_rx_len));
94 	save_item(NAME(m_rx_buf));
95 	save_item(NAME(m_tx_len));
96 	save_item(NAME(m_tx_pos));
97 	save_item(NAME(m_tx_buf));
98 	save_item(NAME(m_data));
99 	save_item(NAME(m_parity));
100 
101 	save_item(NAME(m_mouse_x));
102 	save_item(NAME(m_mouse_y));
103 	save_item(NAME(m_mouse_b));
104 
105 	set_pc_kbdc_device();
106 
107 	m_serial = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(hle_ps2_mouse_device::serial), this));
108 	m_sample = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(hle_ps2_mouse_device::sample), this));
109 }
110 
device_reset()111 void hle_ps2_mouse_device::device_reset()
112 {
113 	// configure default settings
114 	defaults();
115 
116 	// reset mouse state
117 	update();
118 
119 	// clear tx/rx buffers
120 	m_rx_len = 0;
121 	m_tx_len = 0;
122 	m_tx_pos = 0;
123 
124 	// enqueue bat result
125 	m_tx_buf[m_tx_len++] = 0xaa;
126 	m_tx_buf[m_tx_len++] = 0x00;
127 
128 	m_state = IDLE;
129 
130 	// release clock and data lines
131 	m_pc_kbdc->data_write_from_kb(1);
132 	m_pc_kbdc->clock_write_from_kb(1);
133 }
134 
clock_write(int state)135 void hle_ps2_mouse_device::clock_write(int state)
136 {
137 	// record when the clock signal last changed state
138 	m_clock_changed = machine().time();
139 
140 	// resume serial communication
141 	resume();
142 }
143 
resume()144 void hle_ps2_mouse_device::resume()
145 {
146 	if (clock_signal() && m_state == IDLE)
147 	{
148 		// check if receiving a start bit
149 		if (!data_signal())
150 			m_state = RX_START;
151 
152 		// start serial communication
153 		if (!m_serial->enabled())
154 			m_serial->adjust(serial_cycle, 0, serial_cycle);
155 	}
156 }
157 
serial(void * ptr,s32 param)158 void hle_ps2_mouse_device::serial(void *ptr, s32 param)
159 {
160 	// host may inhibit device communication by holding the clock low for 100µs
161 	if (!clock_signal() && clock_held(100))
162 	{
163 		m_state = IDLE;
164 
165 		// release clock and data lines
166 		m_pc_kbdc->data_write_from_kb(1);
167 		m_pc_kbdc->clock_write_from_kb(1);
168 
169 		// stop serial communication
170 		m_serial->enable(false);
171 		return;
172 	}
173 
174 	LOGMASKED(LOG_STATE, "state %d clock %d data %d\n", m_state, clock_signal(), data_signal());
175 
176 	switch (m_state)
177 	{
178 	case IDLE:
179 		if (clock_signal())
180 		{
181 			// stop serial communication if nothing to transmit
182 			if (m_tx_pos == m_tx_len)
183 				m_serial->enable(false);
184 			else
185 				// device may transmit after clock is high for 50µs
186 				if (clock_held(50))
187 					m_state = TX_START;
188 		}
189 		break;
190 
191 	case RX_START:
192 		// check for start bit
193 		if (!data_signal())
194 		{
195 			m_state = RX_CLOCK_LO0;
196 
197 			// prepare data
198 			m_data = 0;
199 			m_parity = 1;
200 			m_bit = 0;
201 		}
202 		break;
203 
204 	case RX_CLOCK_LO0:
205 		// assert clock
206 		m_state = RX_CLOCK_LO1;
207 		m_pc_kbdc->clock_write_from_kb(0);
208 		break;
209 
210 	case RX_CLOCK_LO1:
211 		// hold clock
212 		m_state = RX_CLOCK_HI0;
213 		break;
214 
215 	case RX_CLOCK_HI0:
216 		// release clock
217 		m_state = RX_CLOCK_HI1;
218 		m_pc_kbdc->clock_write_from_kb(1);
219 		break;
220 
221 	case RX_CLOCK_HI1:
222 		switch (m_bit)
223 		{
224 		case 8: // parity bit
225 			m_state = RX_CLOCK_LO0;
226 
227 			if (data_signal() == (m_parity & 1))
228 			{
229 				LOGMASKED(LOG_RXTX, "rx data 0x%02x\n", m_data);
230 				m_rx_buf[m_rx_len++] = m_data;
231 			}
232 			else
233 				// TODO: transmit unbuffered resend command
234 				LOGMASKED(LOG_RXTX, "rx error data 0x%02x parity %d\n", m_data, data_signal());
235 			m_bit++;
236 			break;
237 
238 		case 9: // acknowledge
239 			if (data_signal())
240 			{
241 				m_state = RX_CLOCK_LO0;
242 				m_pc_kbdc->data_write_from_kb(0);
243 				m_bit++;
244 			}
245 			break;
246 
247 		case 10: // finished
248 			if (m_rx_len)
249 				m_state = COMMAND;
250 			else
251 				m_state = IDLE;
252 			m_pc_kbdc->data_write_from_kb(1);
253 			break;
254 
255 		default: // data bit
256 			m_state = RX_CLOCK_LO0;
257 			m_parity += data_signal();
258 			m_data |= (data_signal() << m_bit++);
259 			break;
260 		}
261 		break;
262 
263 	case COMMAND:
264 		m_state = IDLE;
265 
266 		// execute the command
267 		command(m_rx_buf[0]);
268 
269 		// reset mouse state
270 		update();
271 		break;
272 
273 	case TX_START:
274 		// prepare data
275 		m_data = m_tx_buf[m_tx_pos++];
276 		m_parity = 1;
277 		m_bit = 0;
278 
279 		// start bit
280 		m_state = TX_CLOCK_LO0;
281 		m_pc_kbdc->data_write_from_kb(0);
282 		break;
283 
284 	case TX_CLOCK_LO0:
285 		// assert clock
286 		m_state = TX_CLOCK_LO1;
287 		m_pc_kbdc->clock_write_from_kb(0);
288 		break;
289 
290 	case TX_CLOCK_LO1:
291 		// hold clock
292 		m_state = TX_CLOCK_HI0;
293 		break;
294 
295 	case TX_CLOCK_HI0:
296 		// release clock
297 		m_state = TX_CLOCK_HI1;
298 		m_pc_kbdc->clock_write_from_kb(1);
299 		break;
300 
301 	case TX_CLOCK_HI1:
302 		switch (m_bit)
303 		{
304 		case 8: // parity bit
305 			m_state = TX_CLOCK_LO0;
306 			m_pc_kbdc->data_write_from_kb(m_parity & 1);
307 			LOGMASKED(LOG_RXTX, "tx data 0x%02x\n", m_data);
308 			m_bit++;
309 			break;
310 
311 		case 9: // stop bit
312 			m_state = TX_CLOCK_LO0;
313 			m_pc_kbdc->data_write_from_kb(1);
314 			m_bit++;
315 			break;
316 
317 		case 10: // finished
318 			m_state = IDLE;
319 			break;
320 
321 		default: // data
322 			m_state = TX_CLOCK_LO0;
323 			m_parity += BIT(m_data, m_bit);
324 			m_pc_kbdc->data_write_from_kb(BIT(m_data, m_bit));
325 			m_bit++;
326 			break;
327 		}
328 	}
329 }
330 
command(u8 const command)331 void hle_ps2_mouse_device::command(u8 const command)
332 {
333 	// consume the command byte
334 	m_rx_len--;
335 
336 	// reset the transmit position
337 	m_tx_pos = 0;
338 
339 	// special case for resend
340 	if (command == 0xfe)
341 	{
342 		LOGMASKED(LOG_COMMAND, "resend\n");
343 		return;
344 	}
345 	else
346 		m_tx_len = 0;
347 
348 	// special case for wrap mode
349 	if ((m_mode & WRAP) && command != 0xff && command != 0xec)
350 	{
351 		// echo the command
352 		m_tx_buf[m_tx_len++] = command;
353 		return;
354 	}
355 
356 	// handle the command
357 	switch (command)
358 	{
359 	case 0xe6: // set scaling 1:1
360 		LOGMASKED(LOG_COMMAND, "set scaling 1:1\n");
361 		m_mode &= ~SCALE;
362 		m_tx_buf[m_tx_len++] = 0xfa;
363 		break;
364 
365 	case 0xe7: // set scaling 2:1
366 		LOGMASKED(LOG_COMMAND, "set scaling 2:1\n");
367 		m_mode |= SCALE;
368 		m_tx_buf[m_tx_len++] = 0xfa;
369 		break;
370 
371 	case 0xe8: // set resolution
372 		if (m_rx_len == 1)
373 		{
374 			m_resolution = m_rx_buf[m_rx_len--];
375 			LOGMASKED(LOG_COMMAND, "set resolution 0x%02x\n", m_resolution);
376 		}
377 		else
378 			// re-enqueue the command and wait for the parameter
379 			m_rx_len++;
380 		m_tx_buf[m_tx_len++] = 0xfa;
381 		break;
382 
383 	case 0xe9: // status request
384 		LOGMASKED(LOG_COMMAND, "status request\n");
385 		m_tx_buf[m_tx_len++] = 0xfa;
386 		m_tx_buf[m_tx_len++] = m_mode | m_port_buttons->read();
387 		m_tx_buf[m_tx_len++] = m_resolution;
388 		m_tx_buf[m_tx_len++] = m_sample_rate;
389 		break;
390 
391 	case 0xea: // set stream mode
392 		LOGMASKED(LOG_COMMAND, "set stream mode\n");
393 		m_mode &= ~REMOTE;
394 		m_tx_buf[m_tx_len++] = 0xfa;
395 		break;
396 
397 	case 0xeb: // read data
398 		LOGMASKED(LOG_COMMAND, "read data\n");
399 		m_tx_buf[m_tx_len++] = 0xfa;
400 
401 		// force data sample after acknowledge transmitted
402 		if (!m_sample->enabled())
403 			m_sample->adjust(serial_cycle * 48, 1);
404 		break;
405 
406 	case 0xec: // reset wrap mode
407 		LOGMASKED(LOG_COMMAND, "reset wrap mode\n");
408 		m_mode &= ~WRAP;
409 		m_tx_buf[m_tx_len++] = 0xfa;
410 		break;
411 
412 	case 0xee: // set wrap mode
413 		LOGMASKED(LOG_COMMAND, "set wrap mode\n");
414 		m_mode |= WRAP;
415 		m_tx_buf[m_tx_len++] = 0xfa;
416 		break;
417 
418 	case 0xf0: // set remote mode
419 		LOGMASKED(LOG_COMMAND, "set remote mode\n");
420 		m_mode |= REMOTE;
421 		m_tx_buf[m_tx_len++] = 0xfa;
422 		break;
423 
424 	case 0xf2: // get device id
425 		LOGMASKED(LOG_COMMAND, "get device id\n");
426 		m_tx_buf[m_tx_len++] = 0xfa;
427 		m_tx_buf[m_tx_len++] = 0x00;
428 		break;
429 
430 	case 0xf3: // set sample rate
431 		if (m_rx_len == 1)
432 		{
433 			m_sample_rate = m_rx_buf[m_rx_len--];
434 			LOGMASKED(LOG_COMMAND, "set sample rate %d\n", m_sample_rate);
435 		}
436 		else
437 			// re-enqueue the command and wait for the parameter
438 			m_rx_len++;
439 		m_tx_buf[m_tx_len++] = 0xfa;
440 		break;
441 
442 	case 0xf4: // enable data reporting
443 		LOGMASKED(LOG_COMMAND, "enable data reporting\n");
444 		m_mode |= ENABLE;
445 		m_sample->adjust(attotime::from_hz(m_sample_rate), 0, attotime::from_hz(m_sample_rate));
446 		m_tx_buf[m_tx_len++] = 0xfa;
447 		break;
448 
449 	case 0xf5: // disable data reporting
450 		LOGMASKED(LOG_COMMAND, "disable data reporting\n");
451 		m_mode &= ~ENABLE;
452 		m_sample->enable(false);
453 		m_tx_buf[m_tx_len++] = 0xfa;
454 		break;
455 
456 	case 0xf6: // set defaults
457 		LOGMASKED(LOG_COMMAND, "set defaults\n");
458 		defaults();
459 		m_tx_buf[m_tx_len++] = 0xfa;
460 		break;
461 
462 	case 0xff: // reset
463 		LOGMASKED(LOG_COMMAND, "reset\n");
464 		defaults();
465 		m_tx_buf[m_tx_len++] = 0xfa;
466 		m_tx_buf[m_tx_len++] = 0xaa; // bat successful
467 		m_tx_buf[m_tx_len++] = 0x00; // device id
468 		break;
469 
470 	default:
471 		LOGMASKED(LOG_COMMAND, "unrecognized command 0x%02x\n", command);
472 		m_tx_buf[m_tx_len++] = 0xfc; // error
473 		break;
474 	}
475 }
476 
sample(void * ptr,s32 param)477 void hle_ps2_mouse_device::sample(void *ptr, s32 param)
478 {
479 	// read mouse state
480 	s16 const x = m_port_x_axis->read();
481 	s16 const y = m_port_y_axis->read();
482 	u8 const b = m_port_buttons->read();
483 
484 	// compute delta
485 	s16 dx = x - m_mouse_x;
486 	s16 dy = m_mouse_y - y;
487 	u8 const db = b ^ m_mouse_b;
488 
489 	// data report if transmit buffer empty and position or buttons changed
490 	if ((m_tx_pos == m_tx_len) && (dx || dy || db || param))
491 	{
492 		LOGMASKED(LOG_REPORT, "data report dx %d dy %d db %d\n", dx, dy, db);
493 
494 		// compute sign and overflow
495 		u8 const sx = (dx < 0) ? 0x10 : 0x00;
496 		u8 const sy = (dy < 0) ? 0x20 : 0x00;
497 		u8 const ox = ((dx < -256) || (dx > 255)) ? 0x40 : 0x00;
498 		u8 const oy = ((dy < -256) || (dy > 255)) ? 0x80 : 0x00;
499 
500 		// apply scaling
501 		if (m_mode & SCALE)
502 		{
503 			static s32 const scale[] = { -9, -6, -3, -1, -1, 0, 1, 1, 3, 6, 9 };
504 
505 			dx = (std::abs(dx) > 5) ? dx * 2 : scale[dx + 5];
506 			dy = (std::abs(dy) > 5) ? dy * 2 : scale[dy + 5];
507 		}
508 
509 		// transmit data report
510 		m_tx_len = 0;
511 		m_tx_buf[m_tx_len++] = oy | ox | sy | sx | 0x08 | b;
512 		m_tx_buf[m_tx_len++] = u8(dx);
513 		m_tx_buf[m_tx_len++] = u8(dy);
514 		m_tx_pos = 0;
515 
516 		// record mouse state
517 		m_mouse_x = x;
518 		m_mouse_y = y;
519 		m_mouse_b = b;
520 
521 		// resume serial communication
522 		resume();
523 	}
524 }
525 
defaults()526 void hle_ps2_mouse_device::defaults()
527 {
528 	m_mode = 0;
529 	m_sample_rate = 100;
530 	m_resolution = 2;
531 }
532 
update()533 void hle_ps2_mouse_device::update()
534 {
535 	m_mouse_x = m_port_x_axis->read();
536 	m_mouse_y = m_port_y_axis->read();
537 	m_mouse_b = m_port_buttons->read();
538 }
539