summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99x/mecmouse.cpp
blob: 6afcec642d4c09f19788e8058b11600745d6bba4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************

    TI-99 Mechatronic mouse with adapter

    The Mechatronic mouse is connected to the joystick port and occupies
    both joystick select lines and the switch lines. From these five
    lines, left/right/down are used for the motion (i.e. 3 motion steps
    for positive and four for negative motion and one for rest),
    the fire line is used for the secondary mouse button, and the up
    line is used for the primary button.
    The mouse motion is delivered by the same lines for both directions;
    this requires swapping the axes. According to the source code of
    the accompanying mouse driver, the readout of the current axis is
    done by selecting joystick 1, then joystick 2. The axis swapping is
    achieved by selecting stick 1 again. When selecting stick 2, the
    second axis is seen on the input lines.
    Interrupting this sequence will lead to swapped axes. This is
    prevented by resetting the toggle when the mouse is deselected
    (neither 1 nor 2 are selected).

    The joystick lines are selected as follows:
    TI-99/4:  Stick 1: P4=1, P3=0, P2=1 (5)
    Stick 2: P4=1, P3=1, P2=0 (6)

    TI-99/4A: Stick 1: P4=1, P3=1, P2=0 (6)
    Stick 2: P4=1, P3=1, P2=1 (7)

    TI-99/8:  Stick 1: P3=1, P2=1, P1=1, P0=0 (14)
    Stick 2: P3=1, P2=1, P1=1, P0=1 (15)

    Geneve: n/a, has own mouse handling via v9938

    As we can only deliver at max 3 steps positive and 4 steps negative,
    we split the delta so that subsequent queries add up to the actual
    delta. That is, one delta of +10 yields a 3+3+3+1.

    mecmouse_x holds the current delta to be counted down for x
    (y accordingly)

    mecmouse_x_buf is the current step count reported to CRU

    Michael Zapf, 2008-01-22

    2010-10-22 Rewriten as device
    February 2012: Rewritten as class

*****************************************************************************/

#include "mecmouse.h"

#define POLL_TIMER 1

mecmouse_device::mecmouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
	: joyport_attached_device(mconfig, MECMOUSE, "Mechatronics Mouse", tag, owner, clock, "mecmouse", __FILE__)
{
}


UINT8 mecmouse_device::read_dev()
{
	int answer;
	int buttons = ioport("MOUSE0")->read() & 3;

	answer = (m_read_y_axis? m_y_buf : m_x_buf) << 1;

	if ((buttons & 1)==0)
		/* action button */
		answer |= 0x01;
	if ((buttons & 2)==0)
		/* home button */
		answer |= 0x10;

	// answer: |0|0|0|B2|V|V|V|B1|

	return answer;
}

/*
    Used to select lines. data = 0x01 (Joy1), 0x02 (Joy2)
*/
void mecmouse_device::write_dev(UINT8 data)
{
	if (data == 0x02) {
		if (m_last_select == 0x01) {
			if (!m_read_y_axis)
			{
				/* Sample x motion. */
				if (m_x < -4)
					m_x_buf = -4;
				else if (m_x > 3)
					m_x_buf = 3;
				else
					m_x_buf = m_x;
				m_x -= m_x_buf;
				m_x_buf = (m_x_buf-1) & 7;
			}
			else
			{
				/* Sample y motion. */
				if (m_y < -4)
					m_y_buf = -4;
				else if (m_y > 3)
					m_y_buf = 3;
				else
					m_y_buf = m_y;
				m_y -= m_y_buf;
				m_y_buf = (m_y_buf-1) & 7;
			}
		}
		m_last_select = data;
	}
	else if (data == 0x01)
	{
		if (m_last_select == 0x02)
		{
			/* Swap the axes. */
			m_read_y_axis = !m_read_y_axis;
		}
		m_last_select = data;
	}
	else
	{
		// Reset the axis toggle when the mouse is deselected
		m_read_y_axis = false;
	}
}

void mecmouse_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	// Poll the movement
	int new_mx, new_my;
	int delta_x, delta_y;

	new_mx = ioport("MOUSEX")->read();
	new_my = ioport("MOUSEY")->read();

	// compute x delta
	delta_x = new_mx - m_last_mx;

	// check for wrap
	if (delta_x > 0x80)
		delta_x = -0x100+delta_x;
	if  (delta_x < -0x80)
		delta_x = 0x100+delta_x;

	// Prevent unplausible values at startup.
	if (delta_x > 100 || delta_x<-100) delta_x = 0;

	m_last_mx = new_mx;

	// compute y delta
	delta_y = new_my - m_last_my;

	// check for wrap
	if (delta_y > 0x80)
		delta_y = -0x100+delta_y;
	if  (delta_y < -0x80)
		delta_y = 0x100+delta_y;

	if (delta_y > 100 || delta_y<-100) delta_y = 0;

	m_last_my = new_my;

	// update state
	m_x += delta_x;
	m_y += delta_y;
}

void mecmouse_device::device_start(void)
{
	m_poll_timer = timer_alloc(POLL_TIMER);
	// The poll time cannot depend on the console settings, since the TI-99/4A
	// has no clock line on the joystick port. The rate is not mentioned in
	// the specs; however, if it is too low, the mouse pointer will do jumps
	m_poll_timer->adjust(attotime::from_hz(100), 0, attotime::from_hz(100));
}

void mecmouse_device::device_reset(void)
{
	m_poll_timer->enable(true);
	m_last_select = 0;
	m_read_y_axis = false;
	m_x = 0;
	m_y = 0;
	m_last_mx = 0;
	m_last_my = 0;
}

INPUT_PORTS_START( mecmouse )
	/* 3 ports for mouse */
	PORT_START("MOUSEX") /* Mouse - X AXIS */
		PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START("MOUSEY") /* Mouse - Y AXIS */
		PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)

	PORT_START("MOUSE0") /* Mouse - buttons */
		PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button 1") PORT_PLAYER(1)
		PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Mouse Button 2") PORT_PLAYER(1)
INPUT_PORTS_END

ioport_constructor mecmouse_device::device_input_ports() const
{
	return INPUT_PORTS_NAME( mecmouse );
}

const device_type MECMOUSE = &device_creator<mecmouse_device>;