simplistic mouse cursor
[metatoy] / src / kern / psaux.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include "psaux.h"
20 #include "intr.h"
21 #include "asmops.h"
22 #include "keyb.h"
23 #include "kbregs.h"
24
25 static void init_mouse(void);
26 static void proc_mouse_data(unsigned char *data);
27 static void psaux_intr();
28
29 static int mx, my;
30 static unsigned int bnstate;
31 static int present;
32 static int bounds[4];
33 static int intr_mode;
34
35 void init_psaux(void)
36 {
37         interrupt(IRQ_TO_INTR(12), psaux_intr);
38         unmask_irq(12);
39
40         init_mouse();
41         set_mouse_bounds(0, 0, 319, 199);
42 }
43
44 static void init_mouse(void)
45 {
46         unsigned char val;
47
48         kb_send_cmd(KB_CMD_AUX_ENABLE);
49         if(kb_wait_read()) {
50                 val = kb_read_data();
51                 printf("aux enable: %02x\n", (unsigned int)val);
52         }
53
54         /*
55         kb_send_cmd(KB_CMD_PSAUX);
56         kb_send_data(AUX_CMD_REMOTE_MODE);
57         val = kb_read_data();
58         */
59
60         kb_send_cmd(KB_CMD_GET_CMDBYTE);
61         val = kb_read_data();
62         val &= ~KB_CCB_AUX_DISABLE;
63         val |= KB_CCB_AUX_INTREN;
64         kb_send_cmd(KB_CMD_SET_CMDBYTE);
65         kb_send_data(val);
66
67         if(kb_wait_read()) {
68                 val = kb_read_data();
69                 printf("set cmdbyte: %02x\n", (unsigned int)val);
70         }
71         intr_mode = 1;
72
73         kb_send_cmd(KB_CMD_PSAUX);
74         kb_send_data(AUX_CMD_DEFAULTS);
75         val = kb_read_data();
76
77         kb_send_cmd(KB_CMD_PSAUX);
78         kb_send_data(AUX_CMD_ENABLE);
79         val = kb_read_data();
80
81         present = (val == KB_ACK) ? 1 : 0;
82         printf("init_mouse: %spresent\n", present ? "" : "not ");
83 }
84
85 int have_mouse(void)
86 {
87         return present;
88 }
89
90 void set_mouse_bounds(int x0, int y0, int x1, int y1)
91 {
92         bounds[0] = x0;
93         bounds[1] = y0;
94         bounds[2] = x1;
95         bounds[3] = y1;
96 }
97
98 void set_mouse_pos(int x, int y)
99 {
100         mx = x;
101         my = y;
102 }
103
104 unsigned int mouse_state(int *xp, int *yp)
105 {
106         if(!intr_mode) {
107                 poll_mouse();
108         }
109
110         *xp = mx;
111         *yp = my;
112         return bnstate;
113 }
114
115 /* TODO: poll_mouse doesn't work (enable remote mode and disable interrupt on init to test) */
116 #define STAT_AUX_PENDING                (KB_STAT_OUTBUF_FULL | KB_STAT_AUX)
117 static inline int aux_pending(void)
118 {
119         return (inp(KB_STATUS_PORT) & STAT_AUX_PENDING) == STAT_AUX_PENDING ? 1 : 0;
120 }
121
122 void poll_mouse(void)
123 {
124         static int poll_state;
125         static unsigned char pkt[3];
126         unsigned char rd;
127
128         ser_printf("poll_mouse(%d)\n", poll_state);
129
130         switch(poll_state) {
131         case 0: /* send read mouse command */
132                 kb_send_cmd(KB_CMD_PSAUX);
133                 kb_send_data(AUX_CMD_READ_MOUSE);
134                 ++poll_state;
135                 break;
136
137         case 1: /* wait for ACK */
138                 if(kb_wait_read()) {// && aux_pending()) {
139                         if((rd = kb_read_data()) == KB_ACK) {
140                                 ++poll_state;
141                         } else {
142                                 ser_printf("poll_mouse state 1: expected ack: %02x\n", (unsigned int)rd);
143                         }
144                 }
145                 break;
146
147         case 2: /* read packet data */
148         case 3:
149         case 4:
150                 if(kb_wait_read() && aux_pending()) {
151                         int i = poll_state++ - 2;
152                         pkt[i] = kb_read_data();
153                 }
154                 if(poll_state == 5) {
155                         ser_printf("proc_mouse_data(%02x %02x %02x)\n", (unsigned int)pkt[0],
156                                         (unsigned int)pkt[1], (unsigned int)pkt[2]);
157                         proc_mouse_data(pkt);
158                         poll_state = 0;
159                 }
160                 break;
161
162         default:
163                 ser_printf("poll_mouse reached state: %d\n", poll_state);
164         }
165 }
166
167 static void proc_mouse_data(unsigned char *data)
168 {
169         int dx, dy;
170
171         if(data[0] & AUX_PKT0_OVF_BITS) {
172                 /* consensus seems to be that if overflow bits are set, something is
173                  * fucked, and it's best to re-initialize the mouse
174                  */
175                 /*init_mouse();*/
176         } else {
177                 bnstate = data[0] & AUX_PKT0_BUTTON_BITS;
178                 dx = data[1];
179                 dy = data[2];
180
181                 if(data[0] & AUX_PKT0_XSIGN) {
182                         dx |= 0xffffff00;
183                 }
184                 if(data[0] & AUX_PKT0_YSIGN) {
185                         dy |= 0xffffff00;
186                 }
187
188                 mx += dx;
189                 my -= dy;
190
191                 if(mx < bounds[0]) mx = bounds[0];
192                 if(mx > bounds[2]) mx = bounds[2];
193                 if(my < bounds[1]) my = bounds[1];
194                 if(my > bounds[3]) my = bounds[3];
195         }
196 }
197
198 static void psaux_intr()
199 {
200         static unsigned char data[3];
201         static int idx;
202
203         if(!(inp(KB_STATUS_PORT) & KB_STAT_AUX)) {
204                 /* no mouse data pending, ignore interrupt */
205                 return;
206         }
207
208         data[idx] = kb_read_data();
209         if(++idx >= 3) {
210                 idx = 0;
211
212                 proc_mouse_data(data);
213         }
214 }