1 /*
2 * ps2mouse.c -- PS/2 mouse on userport emulation
3 *
4 * Written by
5 * Hannu Nuotio <hannu.nuotio@tut.fi>
6 * Based on code by
7 * Andreas Boose <viceteam@t-online.de>
8 *
9 * This file is part of VICE, the Versatile Commodore Emulator.
10 * See README for copyright notice.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25 * 02111-1307 USA.
26 *
27 */
28
29 #include "vice.h"
30
31 #include "cmdline.h"
32 #include "resources.h"
33 #include "log.h"
34 #include "ps2mouse.h"
35 #include "alarm.h"
36 #include "maincpu.h"
37 #include "mousedrv.h"
38
39 static void mouse_button_left(int pressed);
40 static void mouse_button_right(int pressed);
41 static void mouse_button_middle(int pressed);
42 static void mouse_button_up(int pressed);
43 static void mouse_button_down(int pressed);
44
45 static log_t ps2mouse_log = LOG_ERR;
46
47 #if 0
48 #define PS2MOUSE_DEBUG(args ...) log_message(ps2mouse_log, args)
49 #define PS2MOUSE_DEBUG_ENABLED
50 #endif
51
52 /* PS/2 mouse port bits */
53 #define PS2_CLK_BIT 0x40
54 #define PS2_DATA_BIT 0x80
55
56 /* PS/2 mouse timing */
57 #define PS2_BIT_DELAY_CLK 75
58
59 /* PS/2 mouse commands & replies (TODO: support more commands) */
60 #define PS2_REPLY_OK 0xfa
61 #define PS2_REPLY_ERROR 0xfc
62 #define PS2_CMD_SET_REMOTE_MODE 0xf0
63 #define PS2_CMD_READ_DATA 0xeb
64 #define PS2_CMD_GET_DEV_ID 0xf2
65 #define PS2_REPLY_DEV_ID 0x00
66
67 /* PS/2 mouse movement packet bits */
68 #define PS2_MDATA_YO 0x80
69 #define PS2_MDATA_XO 0x40
70 #define PS2_MDATA_YS 0x20
71 #define PS2_MDATA_XS 0x10
72 #define PS2_MDATA_A1 0x08
73 #define PS2_MDATA_MB 0x04
74 #define PS2_MDATA_RB 0x02
75 #define PS2_MDATA_LB 0x01
76
77 /* PS/2 mouse variables */
78 static uint8_t ps2mouse_value;
79 static uint8_t ps2mouse_in;
80 static uint8_t ps2mouse_out;
81 static uint8_t ps2mouse_prev;
82 static uint8_t ps2mouse_parity;
83 static int16_t ps2mouse_lastx;
84 static int16_t ps2mouse_lasty;
85 static uint8_t ps2mouse_buttons;
86
87 /* PS/2 transmission state */
88 enum {
89 PS2_FROMTO_IDLE=0,
90 PS2_FROM_START, /* mouse <- cpu */
91 PS2_FROM_D0,
92 PS2_FROM_D1,
93 PS2_FROM_D2,
94 PS2_FROM_D3,
95 PS2_FROM_D4,
96 PS2_FROM_D5,
97 PS2_FROM_D6,
98 PS2_FROM_D7,
99 PS2_FROM_PARITY,
100 PS2_FROM_STOP,
101 PS2_FROM_ACK,
102 PS2_CHECK_SEND, /* mouse -> cpu */
103 PS2_TO_D0,
104 PS2_TO_D1,
105 PS2_TO_D2,
106 PS2_TO_D3,
107 PS2_TO_D4,
108 PS2_TO_D5,
109 PS2_TO_D6,
110 PS2_TO_D7,
111 PS2_TO_PARITY,
112 PS2_TO_STOP
113 } ps2mouse_xmit_state = PS2_FROMTO_IDLE;
114
115 /* Output buffer */
116 #define PS2_QUEUE_SIZE 8
117 uint8_t ps2mouse_queue[PS2_QUEUE_SIZE];
118 uint8_t ps2mouse_queue_head;
119 uint8_t ps2mouse_queue_tail;
120
ps2mouse_queue_put(uint8_t value)121 static int ps2mouse_queue_put(uint8_t value)
122 {
123 uint8_t new_head = (ps2mouse_queue_head + 1) & (PS2_QUEUE_SIZE - 1);
124 if (new_head == ps2mouse_queue_tail) {
125 #ifdef PS2MOUSE_DEBUG_ENABLED
126 PS2MOUSE_DEBUG("queue full!");
127 #endif
128 return 0;
129 }
130 ps2mouse_queue[ps2mouse_queue_head] = value;
131 ps2mouse_queue_head = new_head;
132 return 1;
133 }
134
ps2mouse_queue_empty(void)135 static int ps2mouse_queue_empty(void)
136 {
137 return (ps2mouse_queue_head == ps2mouse_queue_tail);
138 }
139
ps2mouse_queue_get(void)140 static uint8_t ps2mouse_queue_get(void)
141 {
142 uint8_t retval = ps2mouse_queue[ps2mouse_queue_tail];
143 ++ps2mouse_queue_tail;
144 ps2mouse_queue_tail &= (PS2_QUEUE_SIZE - 1);
145 return retval;
146 }
147
148 /* ------------------------------------------------------------------------- */
149
150
ps2mouse_handle_command(uint8_t value)151 static int ps2mouse_handle_command(uint8_t value)
152 {
153 int16_t diff_x, diff_y, new_x, new_y;
154 uint8_t new_buttons;
155 #ifdef PS2MOUSE_DEBUG_ENABLED
156 PS2MOUSE_DEBUG("cmd: got %02x", value);
157 #endif
158 ps2mouse_xmit_state = PS2_CHECK_SEND;
159
160 if (ps2mouse_parity) {
161 #ifdef PS2MOUSE_DEBUG_ENABLED
162 PS2MOUSE_DEBUG("parity error");
163 #endif
164 return ps2mouse_queue_put(PS2_REPLY_ERROR);
165 }
166
167 switch (value) {
168 case PS2_CMD_GET_DEV_ID:
169 return (ps2mouse_queue_put(PS2_REPLY_OK)
170 && ps2mouse_queue_put(PS2_REPLY_DEV_ID));
171 break;
172
173 case PS2_CMD_SET_REMOTE_MODE:
174 #ifdef HAVE_MOUSE
175 mouse_get_int16(&ps2mouse_lastx, &ps2mouse_lasty);
176 #endif
177 return (ps2mouse_queue_put(PS2_REPLY_OK));
178 break;
179
180 case PS2_CMD_READ_DATA:
181 new_buttons = ps2mouse_buttons;
182 #ifdef HAVE_MOUSE
183 mouse_get_int16(&new_x, &new_y);
184 diff_x = (new_x - ps2mouse_lastx);
185 if (diff_x < 0) {
186 new_buttons |= PS2_MDATA_XS;
187 }
188 if (diff_x < -256 || diff_x > 255) {
189 new_buttons |= PS2_MDATA_XO;
190 }
191 ps2mouse_lastx = new_x;
192
193 diff_y = (int16_t)(new_y - ps2mouse_lasty);
194 if (diff_y < 0) {
195 new_buttons |= PS2_MDATA_YS;
196 }
197 if (diff_y < -256 || diff_y > 255) {
198 new_buttons |= PS2_MDATA_YO;
199 }
200 ps2mouse_lasty = new_y;
201 #else
202 diff_x = 0;
203 diff_y = 0;
204 #endif
205 #ifdef PS2MOUSE_DEBUG_ENABLED
206 PS2MOUSE_DEBUG("x/y/b: %02x, %02x, %02x", diff_x, diff_y, new_buttons);
207 #endif
208 return (ps2mouse_queue_put(PS2_REPLY_OK)
209 && ps2mouse_queue_put(new_buttons)
210 && ps2mouse_queue_put((uint8_t)diff_x)
211 && ps2mouse_queue_put((uint8_t)diff_y));
212 break;
213
214 default:
215 #ifdef PS2MOUSE_DEBUG_ENABLED
216 PS2MOUSE_DEBUG("unsupported command %02x", value);
217 #endif
218 return (ps2mouse_queue_put(PS2_REPLY_ERROR) & 0);
219 break;
220 }
221 return 0;
222 }
223
224
225 struct alarm_s *c64dtv_ps2mouse_alarm;
226
c64dtv_ps2mouse_alarm_handler(CLOCK offset,void * data)227 static void c64dtv_ps2mouse_alarm_handler(CLOCK offset, void *data)
228 {
229 int another_alarm = 1;
230
231 alarm_unset(c64dtv_ps2mouse_alarm);
232
233 ps2mouse_out ^= PS2_CLK_BIT;
234 ps2mouse_out &= ~PS2_DATA_BIT;
235
236 switch (ps2mouse_xmit_state) {
237 case PS2_FROM_START:
238 ps2mouse_value = 0;
239 ps2mouse_parity = 0;
240 ps2mouse_out |= (ps2mouse_in & PS2_DATA_BIT);
241 #ifdef PS2MOUSE_DEBUG_ENABLED
242 PS2MOUSE_DEBUG("start: clk/data = %i/%i", (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
243 #endif
244 if ((ps2mouse_out & PS2_CLK_BIT) == 0) {
245 ++ps2mouse_xmit_state;
246 }
247 break;
248
249 case PS2_FROM_D0:
250 case PS2_FROM_D1:
251 case PS2_FROM_D2:
252 case PS2_FROM_D3:
253 case PS2_FROM_D4:
254 case PS2_FROM_D5:
255 case PS2_FROM_D6:
256 case PS2_FROM_D7:
257 if (ps2mouse_out & PS2_CLK_BIT) {
258 ps2mouse_value >>= 1;
259 if (ps2mouse_in & PS2_DATA_BIT) {
260 ps2mouse_value |= 0x80;
261 ps2mouse_parity ^= PS2_DATA_BIT;
262 }
263 }
264 ps2mouse_out |= (ps2mouse_in & PS2_DATA_BIT);
265 #ifdef PS2MOUSE_DEBUG_ENABLED
266 PS2MOUSE_DEBUG("d%i: clk/data = %i/%i", ps2mouse_xmit_state - PS2_FROM_D0, (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
267 #endif
268 if ((ps2mouse_out & PS2_CLK_BIT) == 0) {
269 ++ps2mouse_xmit_state;
270 }
271 break;
272
273 case PS2_FROM_PARITY:
274 if (ps2mouse_out & PS2_CLK_BIT) {
275 if (ps2mouse_in & PS2_DATA_BIT) {
276 ps2mouse_parity ^= PS2_DATA_BIT;
277 }
278 }
279 ps2mouse_out |= PS2_DATA_BIT;
280 #ifdef PS2MOUSE_DEBUG_ENABLED
281 PS2MOUSE_DEBUG("parity: clk/data = %i/%i", (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
282 #endif
283 if ((ps2mouse_out & PS2_CLK_BIT) == 0) {
284 ++ps2mouse_xmit_state;
285 }
286 break;
287
288 case PS2_FROM_STOP:
289 if (ps2mouse_out & PS2_CLK_BIT) {
290 if (ps2mouse_in & PS2_DATA_BIT) {
291 ps2mouse_parity ^= PS2_DATA_BIT;
292 }
293 ps2mouse_out |= PS2_DATA_BIT;
294 } else {
295 ps2mouse_out |= ps2mouse_parity;
296 }
297 #ifdef PS2MOUSE_DEBUG_ENABLED
298 PS2MOUSE_DEBUG("stop: clk/data = %i/%i", (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
299 #endif
300 if ((ps2mouse_out & PS2_CLK_BIT) == 0) {
301 ++ps2mouse_xmit_state;
302 }
303 break;
304
305 case PS2_FROM_ACK:
306 ps2mouse_out |= PS2_DATA_BIT; /* ps2mouse_parity; */
307 #ifdef PS2MOUSE_DEBUG_ENABLED
308 PS2MOUSE_DEBUG("got %02x, parity: %02x, clk/data = %i/%i", ps2mouse_value, ps2mouse_parity, (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
309 #endif
310 another_alarm = ps2mouse_handle_command(ps2mouse_value);
311 break;
312
313 case PS2_CHECK_SEND:
314 if (ps2mouse_queue_empty()) {
315 ps2mouse_out |= (PS2_DATA_BIT | PS2_CLK_BIT);
316 another_alarm = 0;
317 ps2mouse_xmit_state = PS2_FROMTO_IDLE;
318 #ifdef PS2MOUSE_DEBUG_ENABLED
319 PS2MOUSE_DEBUG("all sent. clk/data = %i/%i", (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
320 #endif
321 break;
322 }
323 if ((ps2mouse_in & PS2_CLK_BIT) == 0) {
324 ps2mouse_out &= ~PS2_CLK_BIT;
325 #ifdef PS2MOUSE_DEBUG_ENABLED
326 PS2MOUSE_DEBUG("hold! clk/data = %i/%i", (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
327 #endif
328 break;
329 }
330
331 if ((ps2mouse_out & PS2_CLK_BIT) == 0) {
332 ps2mouse_parity = PS2_DATA_BIT;
333 ps2mouse_value = ps2mouse_queue_get();
334 #ifdef PS2MOUSE_DEBUG_ENABLED
335 PS2MOUSE_DEBUG("sending %02x, clk/data = %i/%i", ps2mouse_value, (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
336 #endif
337 ps2mouse_xmit_state = PS2_TO_D0;
338 }
339 break;
340
341 case PS2_TO_D0:
342 case PS2_TO_D1:
343 case PS2_TO_D2:
344 case PS2_TO_D3:
345 case PS2_TO_D4:
346 case PS2_TO_D5:
347 case PS2_TO_D6:
348 case PS2_TO_D7:
349 if (ps2mouse_out & PS2_CLK_BIT) {
350 ps2mouse_out |= (ps2mouse_value & 1) ? PS2_DATA_BIT : 0;
351 ps2mouse_prev = ps2mouse_out;
352 ps2mouse_parity ^= (ps2mouse_value & 1) ? PS2_DATA_BIT : 0;
353 ps2mouse_value >>= 1;
354 } else {
355 ps2mouse_out |= (ps2mouse_prev & PS2_DATA_BIT);
356 }
357 #ifdef PS2MOUSE_DEBUG_ENABLED
358 PS2MOUSE_DEBUG("to_d%i: clk/data = %i/%i", ps2mouse_xmit_state - PS2_TO_D0, (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
359 #endif
360 if ((ps2mouse_out & PS2_CLK_BIT) == 0) {
361 ++ps2mouse_xmit_state;
362 }
363 break;
364
365 case PS2_TO_PARITY:
366 if (ps2mouse_out & PS2_CLK_BIT) {
367 ps2mouse_out |= ps2mouse_parity;
368 ps2mouse_prev = ps2mouse_out;
369 } else {
370 ps2mouse_out |= (ps2mouse_prev & PS2_DATA_BIT);
371 }
372 #ifdef PS2MOUSE_DEBUG_ENABLED
373 PS2MOUSE_DEBUG("to_parity: clk/data = %i/%i", (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
374 #endif
375 if ((ps2mouse_out & PS2_CLK_BIT) == 0) {
376 ++ps2mouse_xmit_state;
377 }
378 break;
379
380 case PS2_TO_STOP:
381 ps2mouse_out |= PS2_DATA_BIT;
382 #ifdef PS2MOUSE_DEBUG_ENABLED
383 PS2MOUSE_DEBUG("stop: clk/data = %i/%i", (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
384 #endif
385 if ((ps2mouse_out & PS2_CLK_BIT) == 0) {
386 ps2mouse_xmit_state = PS2_CHECK_SEND;
387 }
388 break;
389
390 default:
391 ps2mouse_out |= (PS2_DATA_BIT | PS2_CLK_BIT);
392 another_alarm = 0;
393 ps2mouse_xmit_state = PS2_FROMTO_IDLE;
394 #ifdef PS2MOUSE_DEBUG_ENABLED
395 PS2MOUSE_DEBUG("bug! state %i. clk/data = %i/%i", ps2mouse_xmit_state, (ps2mouse_out >> 6) & 1, (ps2mouse_out >> 7) & 1);
396 #endif
397 break;
398 }
399
400 if (another_alarm) {
401 alarm_set(c64dtv_ps2mouse_alarm, maincpu_clk + PS2_BIT_DELAY_CLK);
402 }
403 }
404
405
ps2mouse_store(uint8_t value)406 void ps2mouse_store(uint8_t value)
407 {
408 ps2mouse_in = value;
409 if (((ps2mouse_prev & PS2_CLK_BIT) == 0) && (value & PS2_CLK_BIT)
410 && (ps2mouse_xmit_state == PS2_FROMTO_IDLE) && ((value & PS2_DATA_BIT) == 0)) {
411 ps2mouse_xmit_state = PS2_FROM_START;
412 ps2mouse_out = value;
413 alarm_set(c64dtv_ps2mouse_alarm, maincpu_clk + PS2_BIT_DELAY_CLK);
414 }
415 ps2mouse_prev = value;
416 return;
417 }
418
ps2mouse_read()419 uint8_t ps2mouse_read()
420 {
421 return ps2mouse_out;
422 }
423
424 /* ------------------------------------------------------------------------- */
425
426
c64dtv_ps2mouse_alarm_init(void)427 static void c64dtv_ps2mouse_alarm_init(void)
428 {
429 c64dtv_ps2mouse_alarm = alarm_new(maincpu_alarm_context, "PS2MOUSEAlarm",
430 c64dtv_ps2mouse_alarm_handler, NULL);
431 }
432
ps2mouse_reset(void)433 void ps2mouse_reset(void)
434 {
435 ps2mouse_in = 0xff;
436 ps2mouse_out = 0xff;
437 ps2mouse_prev = 0xff;
438 ps2mouse_xmit_state = PS2_FROMTO_IDLE;
439 ps2mouse_parity = 0;
440 ps2mouse_lastx = 0;
441 ps2mouse_lasty = 0;
442 ps2mouse_buttons = PS2_MDATA_A1;
443 ps2mouse_queue_head = 0;
444 ps2mouse_queue_tail = 0;
445 return;
446 }
447
448 /* ------------------------------------------------------------------------- */
449
450 int ps2mouse_enabled = 0;
451
set_ps2mouse_enable(int val,void * param)452 static int set_ps2mouse_enable(int val, void *param)
453 {
454 ps2mouse_enabled = (unsigned int)(val ? 1 : 0);
455
456 return 0;
457 }
458
459 static const resource_int_t resources_int[] = {
460 { "ps2mouse", 0, RES_EVENT_SAME, NULL,
461 &ps2mouse_enabled, set_ps2mouse_enable, NULL },
462 RESOURCE_INT_LIST_END
463 };
464
465 static const cmdline_option_t cmdline_options[] =
466 {
467 { "-ps2mouse", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
468 NULL, NULL, "PS2Mouse", (void *)1,
469 NULL, "Enable PS/2 mouse on userport" },
470 { "+ps2mouse", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
471 NULL, NULL, "PS2Mouse", (void *)0,
472 NULL, "Disable PS/2 mouse on userport" },
473 CMDLINE_LIST_END
474 };
475
476 /* ------------------------------------------------------------------------- */
477
478 static mouse_func_t mouse_funcs =
479 {
480 mouse_button_left,
481 mouse_button_right,
482 mouse_button_middle,
483 mouse_button_up,
484 mouse_button_down
485 };
486
mouse_ps2_resources_init(void)487 int mouse_ps2_resources_init(void)
488 {
489 if (resources_register_int(resources_int) < 0) {
490 return -1;
491 }
492
493 return mousedrv_resources_init(&mouse_funcs);
494 }
495
mouse_ps2_cmdline_options_init(void)496 int mouse_ps2_cmdline_options_init(void)
497 {
498 if (cmdline_register_options(cmdline_options) < 0) {
499 return -1;
500 }
501
502 return mousedrv_cmdline_options_init();
503 }
504
mouse_ps2_init(void)505 void mouse_ps2_init(void)
506 {
507 if (ps2mouse_log == LOG_ERR) {
508 ps2mouse_log = log_open("ps2mouse");
509 }
510
511 c64dtv_ps2mouse_alarm_init();
512
513 ps2mouse_reset();
514 mousedrv_init();
515 }
516
mouse_ps2_shutdown(void)517 void mouse_ps2_shutdown(void)
518 {
519 }
520
mouse_button_left(int pressed)521 static void mouse_button_left(int pressed)
522 {
523 if (pressed) {
524 ps2mouse_buttons |= PS2_MDATA_LB;
525 } else {
526 ps2mouse_buttons &= ~PS2_MDATA_LB;
527 }
528 }
529
mouse_button_middle(int pressed)530 static void mouse_button_middle(int pressed)
531 {
532 if (pressed) {
533 ps2mouse_buttons |= PS2_MDATA_MB;
534 } else {
535 ps2mouse_buttons &= ~PS2_MDATA_MB;
536 }
537 }
538
mouse_button_right(int pressed)539 static void mouse_button_right(int pressed)
540 {
541 if (pressed) {
542 ps2mouse_buttons |= PS2_MDATA_RB;
543 } else {
544 ps2mouse_buttons &= ~PS2_MDATA_RB;
545 }
546 }
547
mouse_button_up(int pressed)548 static void mouse_button_up(int pressed)
549 {
550 }
551
mouse_button_down(int pressed)552 static void mouse_button_down(int pressed)
553 {
554 }
555