1 // license:BSD-3-Clause
2 // copyright-holders:Vas Crabb
3 #include "emu.h"
4 #include "hlemouse.h"
5 
6 #define LOG_GENERAL (1U << 0)
7 #define LOG_BIT     (1U << 1)
8 
9 //#define VERBOSE (LOG_GENERAL | LOG_BIT)
10 //#define LOG_OUTPUT_STREAM std::cerr
11 
12 #include "logmacro.h"
13 
14 #define LOGBIT(...) LOGMASKED(LOG_BIT, __VA_ARGS__)
15 
16 
17 /*
18     Mostly compatible with the Mouse Systems "non-rotatable" protocol.
19     The only real difference is that the mouse may send a 3-byte frame
20     if the movement in each axis doesn't exceed the range -120 to 127
21     counts (values in the range -128 to -121 are indistinguishable from
22     button states).
23 */
24 
25 
26 /***************************************************************************
27     DEVICE TYPE GLOBALS
28 ***************************************************************************/
29 
30 DEFINE_DEVICE_TYPE_NS(SUN_1200BAUD_HLE_MOUSE, bus::sunmouse, hle_1200baud_device, "sunmouse_hle1200", "Sun Mouse (1200 Baud, HLE)")
31 DEFINE_DEVICE_TYPE_NS(SUN_4800BAUD_HLE_MOUSE, bus::sunmouse, hle_4800baud_device, "sunmouse_hle4800", "Sun Mouse (4800 Baud, HLE)")
32 
33 namespace bus { namespace sunmouse {
34 
35 namespace {
36 
37 INPUT_PORTS_START( mouse )
38 	PORT_START("BTN")
39 	PORT_BIT( 0xfff8, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_CODE(MOUSECODE_BUTTON1)40 	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_device_base, input_changed, 0)
41 	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CODE(MOUSECODE_BUTTON3) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_device_base, input_changed, 0)
42 	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CODE(MOUSECODE_BUTTON2) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_device_base, input_changed, 0)
43 
44 	PORT_START("X")
45 	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
46 	PORT_BIT( 0x0fff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_device_base, input_changed, 0)
47 
48 	PORT_START("Y")
49 	PORT_BIT( 0xf000, IP_ACTIVE_HIGH, IPT_UNUSED )
50 	PORT_BIT( 0x0fff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_CHANGED_MEMBER(DEVICE_SELF, hle_device_base, input_changed, 0)
51 INPUT_PORTS_END
52 
53 
54 uint8_t extract_delta_byte(int32_t &delta)
55 {
56 	int32_t const result(std::min<int32_t>(std::max<int32_t>(delta, -120), 127));
57 	delta -= result;
58 	return uint8_t(int8_t(result));
59 }
60 
61 } // anonymous namespace
62 
63 
64 
65 /***************************************************************************
66     BASE HLE MOUSE DEVICE
67 ***************************************************************************/
68 
hle_device_base(machine_config const & mconfig,device_type type,char const * tag,device_t * owner,uint32_t clock,unsigned multiplier)69 hle_device_base::hle_device_base(
70 		machine_config const &mconfig,
71 		device_type type,
72 		char const *tag,
73 		device_t *owner,
74 		uint32_t clock,
75 		unsigned multiplier)
76 	: device_t(mconfig, type, tag, owner, clock)
77 	, device_serial_interface(mconfig, *this)
78 	, device_sun_mouse_port_interface(mconfig, *this)
79 	, m_buttons(*this, "BTN")
80 	, m_x_axis(*this, "X")
81 	, m_y_axis(*this, "Y")
82 	, m_multiplier(multiplier)
83 	, m_x_delta(0)
84 	, m_y_delta(0)
85 	, m_x_val(0U)
86 	, m_y_val(0U)
87 	, m_btn_sent(0x00U)
88 	, m_btn_val(0x00U)
89 	, m_phase(0U)
90 {
91 }
92 
93 
~hle_device_base()94 hle_device_base::~hle_device_base()
95 {
96 }
97 
98 
INPUT_CHANGED_MEMBER(hle_device_base::input_changed)99 INPUT_CHANGED_MEMBER( hle_device_base::input_changed )
100 {
101 	if (is_transmit_register_empty())
102 	{
103 		assert((0U == m_phase) || (3U == m_phase));
104 
105 		check_inputs();
106 		if (0U == m_phase)
107 		{
108 			if (m_x_delta || m_y_delta || (m_btn_sent != m_btn_val))
109 			{
110 				LOG("Inputs changed (B=%X->%x X=%d Y=%d) - sending button state\n",
111 						m_btn_sent,
112 						m_btn_val,
113 						m_x_delta,
114 						m_y_delta);
115 				transmit_register_setup((m_btn_sent = m_btn_val) | 0x80U);
116 			}
117 		}
118 		else if (m_x_delta || m_y_delta)
119 		{
120 			LOG("Inputs changed (B=%X->%x X=%d Y=%d) - sending X delta\n",
121 					m_btn_sent,
122 					m_btn_val,
123 					m_x_delta,
124 					m_y_delta);
125 			transmit_register_setup(extract_delta_byte(m_x_delta));
126 		}
127 		else if (m_btn_sent != m_btn_val)
128 		{
129 			LOG("Inputs changed (B=%X->%x X=%d Y=%d) - sending button state\n",
130 					m_btn_sent,
131 					m_btn_val,
132 					m_x_delta,
133 					m_y_delta);
134 			m_phase = 0U;
135 			transmit_register_setup((m_btn_sent = m_btn_val) | 0x80U);
136 		}
137 	}
138 	else
139 	{
140 		LOG("Ignoring input changed while transmit register not empty\n");
141 	}
142 }
143 
144 
device_input_ports() const145 ioport_constructor hle_device_base::device_input_ports() const
146 {
147 	return INPUT_PORTS_NAME(mouse);
148 }
149 
150 
device_start()151 void hle_device_base::device_start()
152 {
153 	save_item(NAME(m_x_delta));
154 	save_item(NAME(m_y_delta));
155 	save_item(NAME(m_x_val));
156 	save_item(NAME(m_y_val));
157 	save_item(NAME(m_phase));
158 
159 	set_data_frame(START_BIT_COUNT, DATA_BIT_COUNT, PARITY, STOP_BITS);
160 	set_rate(BAUD * m_multiplier);
161 	receive_register_reset();
162 	transmit_register_reset();
163 
164 	output_rxd(0);
165 
166 	m_x_delta = 0;
167 	m_y_delta = 0;
168 	m_btn_sent = 0x00U;
169 	m_phase = 0U;
170 }
171 
172 
device_reset()173 void hle_device_base::device_reset()
174 {
175 	m_x_val = m_x_axis->read();
176 	m_y_val = m_y_axis->read();
177 	m_btn_val = m_buttons->read();
178 }
179 
180 
tra_callback()181 void hle_device_base::tra_callback()
182 {
183 	int const bit(transmit_register_get_data_bit() ? 0 : 1);
184 	LOGBIT("Send bit !%d\n", bit);
185 	output_rxd(bit);
186 }
187 
188 
tra_complete()189 void hle_device_base::tra_complete()
190 {
191 	check_inputs();
192 
193 	if (4U <= m_phase)
194 		m_phase = 0U;
195 	else
196 		++m_phase;
197 
198 	switch (m_phase)
199 	{
200 	// sent second Y delta - send button state if anything changed
201 	case 0U:
202 		if (m_x_delta || m_y_delta || (m_btn_sent != m_btn_val))
203 		{
204 			LOG("Sent Y delta (B=%X->%x X=%d Y=%d) - sending button state\n",
205 					m_btn_sent,
206 					m_btn_val,
207 					m_x_delta,
208 					m_y_delta);
209 			transmit_register_setup((m_btn_sent = m_btn_val) | 0x80U);
210 		}
211 		else
212 		{
213 			LOG("Sent Y delta (B=%X->%x X=%d Y=%d) - nothing to send\n",
214 					m_btn_sent,
215 					m_btn_val,
216 					m_x_delta,
217 					m_y_delta);
218 		}
219 		break;
220 
221 	// can wait after sending first Y delta
222 	case 3U:
223 		if (!m_x_delta && !m_y_delta && (m_btn_sent == m_btn_val))
224 		{
225 			LOG("Sent Y delta (B=%X->%x X=%d Y=%d) - nothing to send\n",
226 					m_btn_sent,
227 					m_btn_val,
228 					m_x_delta,
229 					m_y_delta);
230 			//break; uncommenting this causes problems with early versions of Solaris
231 		}
232 	case 1U:
233 		LOG("Sent %s (B=%X->%x X=%d Y=%d) - sending X delta\n",
234 				(1U == m_phase) ? "button state" : "Y delta",
235 				m_btn_sent,
236 				m_btn_val,
237 				m_x_delta,
238 				m_y_delta);
239 		transmit_register_setup(extract_delta_byte(m_x_delta));
240 		break;
241 
242 	// sent X delta - always follow with Y delta
243 	case 2U:
244 	case 4U:
245 		LOG("Sent X delta (B=%X->%x X=%d Y=%d) - sending Y delta\n",
246 				m_btn_sent,
247 				m_btn_val,
248 				m_x_delta,
249 				m_y_delta);
250 		transmit_register_setup(extract_delta_byte(m_y_delta));
251 		break;
252 	}
253 }
254 
255 
check_inputs()256 void hle_device_base::check_inputs()
257 {
258 	// read new axis values and get difference
259 	uint16_t const x_val(m_x_axis->read());
260 	uint16_t const y_val(m_y_axis->read());
261 	int16_t x_delta(x_val - m_x_val);
262 	int16_t y_delta(y_val - m_y_val);
263 
264 	// deal with wraparound
265 	if (0x0800 <= x_delta)
266 		x_delta -= 0x1000;
267 	else if (-0x0800 >= x_delta)
268 		x_delta += 0x1000;
269 	if (0x0800 <= y_delta)
270 		y_delta -= 0x1000;
271 	else if (-0x0800 >= y_delta)
272 		x_delta += 0x1000;
273 
274 	// update state
275 	m_x_delta += x_delta;
276 	m_y_delta -= y_delta;
277 	m_x_val = x_val;
278 	m_y_val = y_val;
279 
280 	// read new button values
281 	m_btn_val = m_buttons->read();
282 }
283 
284 
285 
286 /***************************************************************************
287     1200 BAUD HLE MOUSE DEVICE
288 ***************************************************************************/
289 
hle_1200baud_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)290 hle_1200baud_device::hle_1200baud_device(
291 		machine_config const &mconfig,
292 		char const *tag,
293 		device_t *owner,
294 		uint32_t clock)
295 	: hle_device_base(mconfig, SUN_1200BAUD_HLE_MOUSE, tag, owner, clock, 1U)
296 {
297 }
298 
299 
300 
301 /***************************************************************************
302     4800 BAUD HLE MOUSE DEVICE
303 ***************************************************************************/
304 
hle_4800baud_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)305 hle_4800baud_device::hle_4800baud_device(
306 		machine_config const &mconfig,
307 		char const *tag,
308 		device_t *owner,
309 		uint32_t clock)
310 	: hle_device_base(mconfig, SUN_4800BAUD_HLE_MOUSE, tag, owner, clock, 4U)
311 {
312 }
313 
314 } } // namespace bus::sunmouse
315