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