1 // license:BSD-3-Clause
2 // copyright-holders:Vas Crabb
3 /**************************************************************************
4 
5     PC Serial Mouse Simulation
6 
7     Microsoft mouse is the classic two-button PC mouse.  Data is 7N1 at
8     1,200 Baud.  Mouse can be reset by de-asserting RTS.  On start, the
9     mouse identifies itself with the string "M".  Bit 6 is only set for
10     the first byte of a report - it's clear for all subsequent bytes of
11     a report, and also for all identification bytes.
12 
13     Reports consist of three bytes: the first byte contains the button
14     state and the two high bits of the Y and X delta; the second byte
15     contains the low six bits of the X delta; the third byte contains
16     the low six bits of the Y delta.  Button bits are set when pressed,
17     and positive delta is rightwards/downwards.
18 
19     1lryyxx 0xxxxxx 0yyyyyy
20 
21     Logitech extended the Microsoft protocol to support a third button.
22     The mouse identifies itself with the string "M3".  If the third
23     button changes state, and additional byte is sent indicating the
24     new state of the third button.
25 
26     1lryyxx 0xxxxxx 0yyyyyy 0m00000
27 
28     The serial wheel mouse protocol extends the Microsoft mouse protocol
29     in a different way.  The mouse identifies itself with the string
30     "MZ@\0\0\0".  If the third button or wheel state changes, a fourth
31     an additional byte is sent containing the third button state and a
32     four-bit wheel delta value.  The wheel value is positive for
33     downward movement.  Note that the third button is value is in a
34     different position to the logitech protocol.
35 
36     1lryyxx 0xxxxxx 0yyyyyy 00mwwww
37 
38     The Mouse Systems non-rotatable protocol provides two-axis movement
39     and three buttons.  Data is 8N1 at 1,200 Baud (the M-1 mouse could
40     also be configured for 300 Baud by turning DIP switch 1 off).  The
41     mouse does not send an identification string.  The first byte of a
42     report can be identified by a fixed pattern in the five most
43     significant bits.
44 
45     Reports are five bytes long.  The first byte contains the button
46     state; the second and fourth bytes contain X delta; the third and
47     fifth bytes contain Y delta.  The two delta values for each axis
48     should be summed.  Delta values range from -120 to 127 to prevent
49     being mistaken for the lead byte of a report.  Button bits are clear
50     when set, and positive delta is rightwards/upwards.  Delta values
51     are generated immediately before transmission - reports are not
52     atomic.
53 
54     10000lmr xxxxxxxx yyyyyyyy xxxxxxxx yyyyyyyy
55 
56     The Mouse systems rotatable protcol allows the host to infer
57     rotation around the third axis at the cost of halving the maximum
58     sustained movement speed.  The M-1 mouse has two sensors spaced 100
59     counts apart horizontally.  If DIP switch 2 is on, the X and Y delta
60     for each sensor is reported separately.  The right sensor delta is
61     reported before the left sensor delta.  If the mouse is rotated, the
62     delta values for the two sensors will differ.
63 
64 **************************************************************************/
65 
66 #include "emu.h"
67 #include "hlemouse.h"
68 
69 #include <cassert>
70 #include <cmath>
71 
72 
73 //**************************************************
74 //  Device type globals
75 //**************************************************
76 
77 DEFINE_DEVICE_TYPE_NS(MSFT_HLE_SERIAL_MOUSE,      bus::rs232, hle_msft_mouse_device,      "rs232_mouse_hle_msft",      "Microsoft 2-Button Serial Mouse (HLE)")
78 DEFINE_DEVICE_TYPE_NS(LOGITECH_HLE_SERIAL_MOUSE,  bus::rs232, hle_logitech_mouse_device,  "rs232_mouse_hle_logitech",  "Logitech 3-Button Serial Mouse (HLE)")
79 DEFINE_DEVICE_TYPE_NS(WHEEL_HLE_SERIAL_MOUSE,     bus::rs232, hle_wheel_mouse_device,     "rs232_mouse_hle_wheel",     "Microsoft Serial Mouse with Wheel (HLE)")
80 DEFINE_DEVICE_TYPE_NS(MSYSTEMS_HLE_SERIAL_MOUSE,  bus::rs232, hle_msystems_mouse_device,  "rs232_mouse_hle_msystems",  "Mouse Systems Non-rotatable Mouse (HLE)")
81 DEFINE_DEVICE_TYPE_NS(ROTATABLE_HLE_SERIAL_MOUSE, bus::rs232, hle_rotatable_mouse_device, "rs232_mouse_hle_rotatable", "Mouse Systems Rotatable Mouse (HLE)")
82 DEFINE_DEVICE_TYPE_NS(SGI_HLE_SERIAL_MOUSE,       bus::rs232, hle_sgi_mouse_device,       "rs232_mouse_hle_sgi",       "SGI IRIS Indigo Mouse (HLE)")
83 
84 namespace bus { namespace rs232 {
85 
86 namespace {
87 
88 INPUT_PORTS_START(msft)
89 	PORT_START("BTN")
90 	PORT_BIT( 0xfffc, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_CODE(MOUSECODE_BUTTON1)91 	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msmouse_device_base, input_changed, 0)
92 	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msmouse_device_base, input_changed, 0)
93 
94 	PORT_START("X")
95 	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
96 	PORT_BIT( 0x0fff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msmouse_device_base, input_changed, 0)
97 
98 	PORT_START("Y")
99 	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
100 	PORT_BIT( 0x0fff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msmouse_device_base, input_changed, 0)
101 INPUT_PORTS_END
102 
103 
104 INPUT_PORTS_START(logitech)
105 	PORT_INCLUDE(msft)
106 
107 	PORT_MODIFY("BTN")
108 	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON3) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msmouse_device_base, input_changed, 0)
109 	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msmouse_device_base, input_changed, 0)
110 INPUT_PORTS_END
111 
112 
113 INPUT_PORTS_START(wheel)
114 	PORT_INCLUDE(logitech)
115 
116 	PORT_START("WHEEL")
117 	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
118 	PORT_BIT( 0x0fff, 0x00, IPT_DIAL_V ) PORT_SENSITIVITY(10) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msmouse_device_base, input_changed, 0)
119 INPUT_PORTS_END
120 
121 
122 INPUT_PORTS_START(msystems)
123 	PORT_START("BTN")
124 	PORT_BIT( 0xfff8, IP_ACTIVE_HIGH, IPT_UNUSED )
125 	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msystems_device_base, input_changed, 0)
126 	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON3) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msystems_device_base, input_changed, 0)
127 	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msystems_device_base, input_changed, 0)
128 
129 	PORT_START("X")
130 	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
131 	PORT_BIT( 0x0fff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msystems_device_base, input_changed, 0)
132 
133 	PORT_START("Y")
134 	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
135 	PORT_BIT( 0x0fff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msystems_device_base, input_changed, 0)
136 INPUT_PORTS_END
137 
138 
139 INPUT_PORTS_START(rotatable)
140 	PORT_INCLUDE(msystems)
141 
142 	PORT_START("ROT")
143 	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
144 	PORT_BIT( 0x0fff, 0x00, IPT_DIAL ) PORT_SENSITIVITY(10) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_msystems_device_base, input_changed, 0)
145 INPUT_PORTS_END
146 
147 
148 template <unsigned Bits, typename T>
149 std::make_signed_t<T> read_axis(ioport_port &port, T &val)
150 {
151 	static_assert((1U < Bits) && ((sizeof(T) * 8) > Bits), "invalid field bits");
152 
153 	T const updated(T(std::make_unsigned_t<T>(port.read())) & make_bitmask<T>(Bits));
154 	std::make_signed_t<T> delta(std::make_signed_t<T>(updated) - std::make_signed_t<T>(val));
155 	if (std::make_signed_t<T>(std::make_unsigned_t<T>(1) << (Bits - 1)) <= delta)
156 		delta -= std::make_signed_t<T>(std::make_unsigned_t<T>(1) << Bits);
157 	else if (-std::make_signed_t<T>(std::make_unsigned_t<T>(1) << (Bits - 1)) >= delta)
158 		delta += std::make_signed_t<T>(std::make_unsigned_t<T>(1) << Bits);
159 	val = updated;
160 	return delta;
161 }
162 
163 
164 template <typename T>
report_axis(T & delta,T low,T high)165 uint8_t report_axis(T &delta, T low, T high)
166 {
167 	T const result(std::min<T>(std::max<T>(delta, low), high));
168 	delta -= result;
169 	return uint8_t(int8_t(result));
170 }
171 
172 } // anonymous namespace
173 
174 
175 //**************************************************
176 // Microsoft mouse base
177 //**************************************************
178 
INPUT_CHANGED_MEMBER(hle_msmouse_device_base::input_changed)179 INPUT_CHANGED_MEMBER(hle_msmouse_device_base::input_changed)
180 {
181 	if (fifo_empty() && is_transmit_register_empty())
182 		check_inputs();
183 }
184 
hle_msmouse_device_base(machine_config const & mconfig,device_type type,char const * tag,device_t * owner,uint32_t clock)185 hle_msmouse_device_base::hle_msmouse_device_base(
186 		machine_config const &mconfig,
187 		device_type type,
188 		char const *tag,
189 		device_t *owner,
190 		uint32_t clock)
191 	: buffered_rs232_device<8>(mconfig, type, tag, owner, clock)
192 	, m_buttons(*this, "BTN")
193 	, m_x_axis(*this, "X")
194 	, m_y_axis(*this, "Y")
195 	, m_x_delta(0)
196 	, m_y_delta(0)
197 	, m_x_val(0U)
198 	, m_y_val(0U)
199 	, m_btn_val(0x00U)
200 	, m_btn_sent(0x00U)
201 	, m_dtr(0U)
202 	, m_rts(0U)
203 	, m_enable(0U)
204 {
205 }
206 
device_resolve_objects()207 void hle_msmouse_device_base::device_resolve_objects()
208 {
209 	buffered_rs232_device<8>::device_resolve_objects();
210 
211 	m_dtr = 0U;
212 	m_rts = 0U;
213 }
214 
device_start()215 void hle_msmouse_device_base::device_start()
216 {
217 	buffered_rs232_device<8>::device_start();
218 
219 	save_item(NAME(m_x_delta));
220 	save_item(NAME(m_y_delta));
221 	save_item(NAME(m_x_val));
222 	save_item(NAME(m_y_val));
223 	save_item(NAME(m_btn_val));
224 	save_item(NAME(m_btn_sent));
225 	save_item(NAME(m_dtr));
226 	save_item(NAME(m_rts));
227 	save_item(NAME(m_enable));
228 
229 	set_data_frame(1, 7, PARITY_NONE, STOP_BITS_2);
230 	set_rate(1'200);
231 	receive_register_reset();
232 	transmit_register_reset();
233 
234 	m_x_delta = m_y_delta = 0;
235 	m_x_val = m_y_val = 0U;
236 	m_btn_val = m_btn_sent = 0x00U;
237 	m_enable = 0U;
238 
239 	machine().scheduler().synchronize(timer_expired_delegate(FUNC(hle_msmouse_device_base::start_mouse), this));
240 }
241 
WRITE_LINE_MEMBER(hle_msmouse_device_base::input_dtr)242 WRITE_LINE_MEMBER(hle_msmouse_device_base::input_dtr)
243 {
244 	m_dtr = state ? 1U : 0U;
245 	check_enable();
246 }
247 
WRITE_LINE_MEMBER(hle_msmouse_device_base::input_rts)248 WRITE_LINE_MEMBER(hle_msmouse_device_base::input_rts)
249 {
250 	m_rts = state ? 1U : 0U;
251 	check_enable();
252 }
253 
tra_complete()254 void hle_msmouse_device_base::tra_complete()
255 {
256 	buffered_rs232_device<8>::tra_complete();
257 	if (fifo_empty() && is_transmit_register_empty())
258 		check_inputs();
259 }
260 
received_byte(u8 byte)261 void hle_msmouse_device_base::received_byte(u8 byte)
262 {
263 }
264 
265 
TIMER_CALLBACK_MEMBER(hle_msmouse_device_base::start_mouse)266 TIMER_CALLBACK_MEMBER(hle_msmouse_device_base::start_mouse)
267 {
268 	check_enable();
269 }
270 
check_enable()271 void hle_msmouse_device_base::check_enable()
272 {
273 	bool const enable(!m_dtr && !m_rts);
274 	if (bool(m_enable) != enable)
275 	{
276 		m_enable = enable ? 1U : 0U;
277 		clear_fifo();
278 		receive_register_reset();
279 		transmit_register_reset();
280 		if (enable)
281 		{
282 			read_inputs();
283 			m_x_delta = m_y_delta = 0;
284 			m_btn_sent = m_btn_val;
285 			reset_and_identify();
286 		}
287 	}
288 }
289 
check_inputs()290 void hle_msmouse_device_base::check_inputs()
291 {
292 	if (m_enable && read_inputs())
293 	{
294 		uint8_t const x(report_axis<int16_t>(m_x_delta, -128, 127));
295 		uint8_t const y(report_axis<int16_t>(m_y_delta, -128, 127));
296 		transmit_byte(0x40U | ((m_btn_val << 4) & 0x30U) | ((y >> 4) & 0x0cU) | ((x >> 6) & 0x03U));
297 		transmit_byte(x & 0x3fU);
298 		transmit_byte(y & 0x3fU);
299 		transmit_extensions(m_btn_val, m_btn_sent);
300 		m_btn_sent = m_btn_val;
301 	}
302 }
303 
read_inputs()304 bool hle_msmouse_device_base::read_inputs()
305 {
306 	m_x_delta += read_axis<12>(*m_x_axis, m_x_val);
307 	m_y_delta += read_axis<12>(*m_y_axis, m_y_val);
308 	m_btn_val = m_buttons->read();
309 	return m_x_delta || m_y_delta || (m_btn_val != m_btn_sent);
310 }
311 
312 
313 //**************************************************
314 // Microsoft 2-button mouse
315 //**************************************************
316 
hle_msft_mouse_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)317 hle_msft_mouse_device::hle_msft_mouse_device(
318 		machine_config const &mconfig,
319 		char const *tag,
320 		device_t *owner,
321 		uint32_t clock)
322 	: hle_msmouse_device_base(mconfig, MSFT_HLE_SERIAL_MOUSE, tag, owner, clock)
323 {
324 }
325 
device_input_ports() const326 ioport_constructor hle_msft_mouse_device::device_input_ports() const
327 {
328 	return INPUT_PORTS_NAME(msft);
329 }
330 
reset_and_identify()331 void hle_msft_mouse_device::reset_and_identify()
332 {
333 	// assume ASCII host system
334 	transmit_byte('M');
335 }
336 
transmit_extensions(uint8_t btn_val,uint8_t btn_sent)337 void hle_msft_mouse_device::transmit_extensions(uint8_t btn_val, uint8_t btn_sent)
338 {
339 }
340 
341 
342 //**************************************************
343 //  Logitech 3-button mouse
344 //**************************************************
345 
hle_logitech_mouse_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)346 hle_logitech_mouse_device::hle_logitech_mouse_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock)
347 	: hle_msmouse_device_base(mconfig, LOGITECH_HLE_SERIAL_MOUSE, tag, owner, clock)
348 {
349 }
350 
device_input_ports() const351 ioport_constructor hle_logitech_mouse_device::device_input_ports() const
352 {
353 	return INPUT_PORTS_NAME(logitech);
354 }
355 
reset_and_identify()356 void hle_logitech_mouse_device::reset_and_identify()
357 {
358 	// assume ASCII host system
359 	transmit_byte('M');
360 	transmit_byte('3');
361 }
362 
transmit_extensions(uint8_t btn_val,uint8_t btn_sent)363 void hle_logitech_mouse_device::transmit_extensions(uint8_t btn_val, uint8_t btn_sent)
364 {
365 	if (BIT(btn_val | btn_sent, 2))
366 		transmit_byte(BIT(btn_val, 2) << 5);
367 }
368 
369 
370 
371 //**************************************************
372 // Microsoft wheel mouse
373 //**************************************************
374 
hle_wheel_mouse_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)375 hle_wheel_mouse_device::hle_wheel_mouse_device(
376 		machine_config const &mconfig,
377 		char const *tag,
378 		device_t *owner,
379 		uint32_t clock)
380 	: hle_msmouse_device_base(mconfig, WHEEL_HLE_SERIAL_MOUSE, tag, owner, clock)
381 	, m_wheel(*this, "WHEEL")
382 	, m_wheel_delta(0)
383 	, m_wheel_val(0U)
384 {
385 }
386 
device_input_ports() const387 ioport_constructor hle_wheel_mouse_device::device_input_ports() const
388 {
389 	return INPUT_PORTS_NAME(wheel);
390 }
391 
device_start()392 void hle_wheel_mouse_device::device_start()
393 {
394 	hle_msmouse_device_base::device_start();
395 
396 	save_item(NAME(m_wheel_delta));
397 	save_item(NAME(m_wheel_val));
398 
399 	m_wheel_delta = 0;
400 	m_wheel_val = 0U;
401 }
402 
read_inputs()403 bool hle_wheel_mouse_device::read_inputs()
404 {
405 	m_wheel_delta += read_axis<12>(*m_wheel, m_wheel_val);
406 	return hle_msmouse_device_base::read_inputs() || m_wheel_delta;
407 }
408 
reset_and_identify()409 void hle_wheel_mouse_device::reset_and_identify()
410 {
411 	m_wheel_delta = 0;
412 
413 	// assume ASCII host system
414 	transmit_byte('M');
415 	transmit_byte('Z');
416 	transmit_byte('@');
417 	transmit_byte('\0');
418 	transmit_byte('\0');
419 	transmit_byte('\0');
420 }
421 
transmit_extensions(uint8_t btn_val,uint8_t btn_sent)422 void hle_wheel_mouse_device::transmit_extensions(uint8_t btn_val, uint8_t btn_sent)
423 {
424 	if (BIT(btn_val | btn_sent, 2) || m_wheel_delta)
425 		transmit_byte((BIT(btn_val, 2) << 4) | (report_axis<int16_t>(m_wheel_delta, -8, 7) & 0x0fU));
426 }
427 
428 
429 //**************************************************
430 //  Mouse Systems mouse base
431 //**************************************************
432 
INPUT_CHANGED_MEMBER(hle_msystems_device_base::input_changed)433 INPUT_CHANGED_MEMBER(hle_msystems_device_base::input_changed)
434 {
435 	if (is_transmit_register_empty())
436 	{
437 		assert(0U == m_phase);
438 		tra_complete();
439 	}
440 }
441 
hle_msystems_device_base(machine_config const & mconfig,device_type type,char const * tag,device_t * owner,uint32_t clock)442 hle_msystems_device_base::hle_msystems_device_base(
443 		machine_config const &mconfig,
444 		device_type type,
445 		char const *tag,
446 		device_t *owner,
447 		uint32_t clock)
448 	: device_t(mconfig, type, tag, owner, clock)
449 	, device_rs232_port_interface(mconfig, *this)
450 	, device_serial_interface(mconfig, *this)
451 	, m_phase(0U)
452 {
453 }
454 
device_start()455 void hle_msystems_device_base::device_start()
456 {
457 	save_item(NAME(m_phase));
458 
459 	set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
460 	set_rate(1'200);
461 	receive_register_reset();
462 	transmit_register_reset();
463 
464 	m_phase = 0U;
465 
466 	machine().scheduler().synchronize(timer_expired_delegate(FUNC(hle_msystems_device_base::start_mouse), this));
467 }
468 
tra_callback()469 void hle_msystems_device_base::tra_callback()
470 {
471 	output_rxd(transmit_register_get_data_bit());
472 }
473 
tra_complete()474 void hle_msystems_device_base::tra_complete()
475 {
476 	if (4U <= m_phase)
477 		m_phase = 0U;
478 	else
479 		++m_phase;
480 
481 	bool const dirty(read_inputs());
482 	switch (m_phase)
483 	{
484 	case 0U:
485 		if (dirty)
486 			transmit_register_setup((report_buttons() & 0x07U) | 0x80U);
487 		break;
488 	case 1U:
489 		transmit_register_setup(report_x1_delta());
490 		break;
491 	case 2U:
492 		transmit_register_setup(report_y1_delta());
493 		break;
494 	case 3U:
495 		transmit_register_setup(report_x2_delta());
496 		break;
497 	case 4U:
498 		transmit_register_setup(report_y2_delta());
499 		break;
500 	};
501 }
502 
TIMER_CALLBACK_MEMBER(hle_msystems_device_base::start_mouse)503 TIMER_CALLBACK_MEMBER(hle_msystems_device_base::start_mouse)
504 {
505 	if (is_transmit_register_empty())
506 	{
507 		assert(0U == m_phase);
508 		tra_complete();
509 	}
510 }
511 
512 
513 //**************************************************
514 //  Mouse Systems non-rotatable mouse
515 //**************************************************
516 
hle_msystems_mouse_device(machine_config const & mconfig,device_type type,char const * tag,device_t * owner,uint32_t clock)517 hle_msystems_mouse_device::hle_msystems_mouse_device(
518 		machine_config const &mconfig,
519 		device_type type,
520 		char const *tag,
521 		device_t *owner,
522 		uint32_t clock)
523 	: hle_msystems_device_base(mconfig, type, tag, owner, clock)
524 	, m_buttons(*this, "BTN")
525 	, m_x_axis(*this, "X")
526 	, m_y_axis(*this, "Y")
527 	, m_x_delta(0)
528 	, m_y_delta(0)
529 	, m_x_val(0U)
530 	, m_y_val(0U)
531 	, m_btn_val(0x00U)
532 	, m_btn_sent(0x00U)
533 {
534 }
535 
hle_msystems_mouse_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)536 hle_msystems_mouse_device::hle_msystems_mouse_device(
537 		machine_config const &mconfig,
538 		char const *tag,
539 		device_t *owner,
540 		uint32_t clock)
541 	: hle_msystems_mouse_device(mconfig, MSYSTEMS_HLE_SERIAL_MOUSE, tag, owner, clock)
542 {
543 }
544 
device_input_ports() const545 ioport_constructor hle_msystems_mouse_device::device_input_ports() const
546 {
547 	return INPUT_PORTS_NAME(msystems);
548 }
549 
device_start()550 void hle_msystems_mouse_device::device_start()
551 {
552 	hle_msystems_device_base::device_start();
553 
554 	save_item(NAME(m_x_delta));
555 	save_item(NAME(m_y_delta));
556 	save_item(NAME(m_x_val));
557 	save_item(NAME(m_y_val));
558 	save_item(NAME(m_btn_val));
559 	save_item(NAME(m_btn_sent));
560 
561 	m_x_delta = m_y_delta = 0;
562 	m_x_val = m_y_val = 0U;
563 	m_btn_val = m_btn_sent = 0x00U;
564 }
565 
read_inputs()566 bool hle_msystems_mouse_device::read_inputs()
567 {
568 	m_x_delta += read_axis<12>(*m_x_axis, m_x_val);
569 	m_y_delta -= read_axis<12>(*m_y_axis, m_y_val);
570 	m_btn_val = m_buttons->read();
571 	return m_x_delta || m_y_delta || (m_btn_val != m_btn_sent);
572 }
573 
report_buttons()574 uint8_t hle_msystems_mouse_device::report_buttons()
575 {
576 	m_btn_sent = m_btn_val;
577 	return m_btn_sent;
578 }
579 
report_x1_delta()580 uint8_t hle_msystems_mouse_device::report_x1_delta()
581 {
582 	return report_axis<int16_t>(m_x_delta, -120, 127);
583 }
584 
report_y1_delta()585 uint8_t hle_msystems_mouse_device::report_y1_delta()
586 {
587 	return report_axis<int16_t>(m_y_delta, -120, 127);
588 }
589 
report_x2_delta()590 uint8_t hle_msystems_mouse_device::report_x2_delta()
591 {
592 	return report_axis<int16_t>(m_x_delta, -120, 127);
593 }
594 
report_y2_delta()595 uint8_t hle_msystems_mouse_device::report_y2_delta()
596 {
597 	return report_axis<int16_t>(m_y_delta, -120, 127);
598 }
599 
600 
601 //**************************************************
602 //  Mouse Systems rotatable mouse
603 //**************************************************
604 
hle_rotatable_mouse_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)605 hle_rotatable_mouse_device::hle_rotatable_mouse_device(
606 		machine_config const &mconfig,
607 		char const *tag,
608 		device_t *owner,
609 		uint32_t clock)
610 	: hle_msystems_device_base(mconfig, ROTATABLE_HLE_SERIAL_MOUSE, tag, owner, clock)
611 	, m_buttons(*this, "BTN")
612 	, m_x_axis(*this, "X")
613 	, m_y_axis(*this, "Y")
614 	, m_rotation(*this, "ROT")
615 	, m_x_delta{ 0, 0 }
616 	, m_y_delta{ 0, 0 }
617 	, m_x_val(0U)
618 	, m_y_val(0U)
619 	, m_rot_val(0U)
620 	, m_btn_val(0x00U)
621 	, m_btn_sent(0x00U)
622 {
623 }
624 
device_input_ports() const625 ioport_constructor hle_rotatable_mouse_device::device_input_ports() const
626 {
627 	return INPUT_PORTS_NAME(rotatable);
628 }
629 
device_start()630 void hle_rotatable_mouse_device::device_start()
631 {
632 	hle_msystems_device_base::device_start();
633 
634 	save_item(NAME(m_x_delta));
635 	save_item(NAME(m_y_delta));
636 	save_item(NAME(m_x_val));
637 	save_item(NAME(m_y_val));
638 	save_item(NAME(m_rot_val));
639 	save_item(NAME(m_btn_val));
640 	save_item(NAME(m_btn_sent));
641 
642 	m_x_delta[0] = m_x_delta[1] = m_y_delta[0] = m_y_delta[1] = 0;
643 	m_x_val = m_y_val = m_rot_val = 0U;
644 	m_btn_val = m_btn_sent = 0x00U;
645 }
646 
read_inputs()647 bool hle_rotatable_mouse_device::read_inputs()
648 {
649 	// translation is straighforward
650 	int16_t const x_delta(read_axis<12>(*m_x_axis, m_x_val));
651 	int16_t const y_delta(read_axis<12>(*m_y_axis, m_y_val));
652 	m_x_delta[0] += x_delta;
653 	m_x_delta[1] += x_delta;
654 	m_y_delta[0] -= y_delta;
655 	m_y_delta[1] -= y_delta;
656 
657 	// there are two sensors 100 counts apart, one inch below the centre of the mouse
658 	// for simplicity, assume the mouse is rotated around the point midway the sensors
659 	int16_t const rot_delta(read_axis<12>(*m_rotation, m_rot_val));
660 	if (rot_delta)
661 	{
662 		double const rot_angle(-0.15 * rot_delta);
663 		long const x_diff(std::lround((1 - std::cos(rot_angle)) * 100));
664 		long const y_diff(std::lround(std::sin(rot_angle) * 100));
665 		m_x_delta[0] -= x_diff / 2;
666 		m_x_delta[1] += x_diff - (x_diff / 2);
667 		m_y_delta[0] += y_diff / 2;
668 		m_y_delta[1] -= y_diff - (y_diff / 2);
669 	}
670 
671 	// this is straightforward, too
672 	m_btn_val = m_buttons->read();
673 	return m_x_delta[0] || m_x_delta[1] || m_y_delta[0] || m_y_delta[1] || (m_btn_val != m_btn_sent);
674 }
675 
report_buttons()676 uint8_t hle_rotatable_mouse_device::report_buttons()
677 {
678 	m_btn_sent = m_btn_val;
679 	return m_btn_sent;
680 }
681 
report_x1_delta()682 uint8_t hle_rotatable_mouse_device::report_x1_delta()
683 {
684 	return report_axis<int16_t>(m_x_delta[0], -120, 127);
685 }
686 
report_y1_delta()687 uint8_t hle_rotatable_mouse_device::report_y1_delta()
688 {
689 	return report_axis<int16_t>(m_y_delta[0], -120, 127);
690 }
691 
report_x2_delta()692 uint8_t hle_rotatable_mouse_device::report_x2_delta()
693 {
694 	return report_axis<int16_t>(m_x_delta[1], -120, 127);
695 }
696 
report_y2_delta()697 uint8_t hle_rotatable_mouse_device::report_y2_delta()
698 {
699 	return report_axis<int16_t>(m_y_delta[1], -120, 127);
700 }
701 
702 //**************************************************
703 //  SGI IRIS Indigo mouse
704 //**************************************************
705 
hle_sgi_mouse_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)706 hle_sgi_mouse_device::hle_sgi_mouse_device(
707 		machine_config const &mconfig,
708 		char const *tag,
709 		device_t *owner,
710 		uint32_t clock)
711 	: hle_msystems_mouse_device(mconfig, SGI_HLE_SERIAL_MOUSE, tag, owner, clock)
712 {
713 }
714 
device_start()715 void hle_sgi_mouse_device::device_start()
716 {
717 	hle_msystems_mouse_device::device_start();
718 	set_rate(4'800);
719 	receive_register_reset();
720 	transmit_register_reset();
721 }
722 
723 } } // namespace bus::rs232
724