census logo
[bootcensus] / src / 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
39         init_mouse();
40         set_mouse_bounds(0, 0, 319, 199);
41 }
42
43 static void init_mouse(void)
44 {
45         unsigned char val;
46
47         kb_send_cmd(KB_CMD_AUX_ENABLE);
48         if(kb_wait_read()) {
49                 val = kb_read_data();
50                 printf("aux enable: %02x\n", (unsigned int)val);
51         }
52
53         /*
54         kb_send_cmd(KB_CMD_PSAUX);
55         kb_send_data(AUX_CMD_REMOTE_MODE);
56         val = kb_read_data();
57         */
58
59         kb_send_cmd(KB_CMD_GET_CMDBYTE);
60         val = kb_read_data();
61         val &= ~KB_CCB_AUX_DISABLE;
62         val |= KB_CCB_AUX_INTREN;
63         kb_send_cmd(KB_CMD_SET_CMDBYTE);
64         kb_send_data(val);
65
66         if(kb_wait_read()) {
67                 val = kb_read_data();
68                 printf("set cmdbyte: %02x\n", (unsigned int)val);
69         }
70         intr_mode = 1;
71
72         kb_send_cmd(KB_CMD_PSAUX);
73         kb_send_data(AUX_CMD_DEFAULTS);
74         val = kb_read_data();
75
76         kb_send_cmd(KB_CMD_PSAUX);
77         kb_send_data(AUX_CMD_ENABLE);
78         val = kb_read_data();
79
80         present = (val == KB_ACK) ? 1 : 0;
81         printf("init_mouse: %spresent\n", present ? "" : "not ");
82 }
83
84 int have_mouse(void)
85 {
86         return present;
87 }
88
89 void set_mouse_bounds(int x0, int y0, int x1, int y1)
90 {
91         bounds[0] = x0;
92         bounds[1] = y0;
93         bounds[2] = x1;
94         bounds[3] = y1;
95 }
96
97 unsigned int mouse_state(int *xp, int *yp)
98 {
99         if(!intr_mode) {
100                 poll_mouse();
101         }
102
103         *xp = mx;
104         *yp = my;
105         return bnstate;
106 }
107
108 /* TODO: poll_mouse doesn't work (enable remote mode and disable interrupt on init to test) */
109 #define STAT_AUX_PENDING                (KB_STAT_OUTBUF_FULL | KB_STAT_AUX)
110 static inline int aux_pending(void)
111 {
112         return (inb(KB_STATUS_PORT) & STAT_AUX_PENDING) == STAT_AUX_PENDING ? 1 : 0;
113 }
114
115 void poll_mouse(void)
116 {
117         static int poll_state;
118         static unsigned char pkt[3];
119         unsigned char rd;
120
121         ser_printf("poll_mouse(%d)\n", poll_state);
122
123         switch(poll_state) {
124         case 0: /* send read mouse command */
125                 kb_send_cmd(KB_CMD_PSAUX);
126                 kb_send_data(AUX_CMD_READ_MOUSE);
127                 ++poll_state;
128                 break;
129
130         case 1: /* wait for ACK */
131                 if(kb_wait_read()) {// && aux_pending()) {
132                         if((rd = kb_read_data()) == KB_ACK) {
133                                 ++poll_state;
134                         } else {
135                                 ser_printf("poll_mouse state 1: expected ack: %02x\n", (unsigned int)rd);
136                         }
137                 }
138                 break;
139
140         case 2: /* read packet data */
141         case 3:
142         case 4:
143                 if(kb_wait_read() && aux_pending()) {
144                         int i = poll_state++ - 2;
145                         pkt[i] = kb_read_data();
146                 }
147                 if(poll_state == 5) {
148                         ser_printf("proc_mouse_data(%02x %02x %02x)\n", (unsigned int)pkt[0],
149                                         (unsigned int)pkt[1], (unsigned int)pkt[2]);
150                         proc_mouse_data(pkt);
151                         poll_state = 0;
152                 }
153                 break;
154
155         default:
156                 ser_printf("poll_mouse reached state: %d\n", poll_state);
157         }
158 }
159
160 static void proc_mouse_data(unsigned char *data)
161 {
162         int dx, dy;
163
164         if(data[0] & AUX_PKT0_OVF_BITS) {
165                 /* consensus seems to be that if overflow bits are set, something is
166                  * fucked, and it's best to re-initialize the mouse
167                  */
168                 /*init_mouse();*/
169         } else {
170                 bnstate = data[0] & AUX_PKT0_BUTTON_BITS;
171                 dx = data[1];
172                 dy = data[2];
173
174                 if(data[0] & AUX_PKT0_XSIGN) {
175                         dx |= 0xffffff00;
176                 }
177                 if(data[0] & AUX_PKT0_YSIGN) {
178                         dy |= 0xffffff00;
179                 }
180
181                 mx += dx;
182                 my -= dy;
183
184                 if(mx < bounds[0]) mx = bounds[0];
185                 if(mx > bounds[2]) mx = bounds[2];
186                 if(my < bounds[1]) my = bounds[1];
187                 if(my > bounds[3]) my = bounds[3];
188         }
189 }
190
191 static void psaux_intr()
192 {
193         static unsigned char data[3];
194         static int idx;
195
196         if(!(inb(KB_STATUS_PORT) & KB_STAT_AUX)) {
197                 /* no mouse data pending, ignore interrupt */
198                 return;
199         }
200
201         data[idx] = kb_read_data();
202         if(++idx >= 3) {
203                 idx = 0;
204
205                 proc_mouse_data(data);
206         }
207 }