initial commit
[midikeys] / keyb.c
1 /*
2 DOS interrupt-based keyboard driver.
3 Copyright (C) 2013  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 the program. If not, see <http://www.gnu.org/licenses/>
17 */
18 #define KEYB_C_
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <conio.h>
25 #include <dos.h>
26
27 #ifdef __WATCOMC__
28 #include <i86.h>
29 #endif
30 #ifdef __DJGPP__
31 #include <dpmi.h>
32 #include <go32.h>
33 #include <pc.h>
34 #endif
35
36 #include "keyb.h"
37 #include "scancode.h"
38
39 #define KB_INTR         0x9
40 #define KB_PORT         0x60
41
42 #define PIC1_CMD_PORT   0x20
43 #define OCW2_EOI                (1 << 5)
44
45 #ifdef __WATCOMC__
46 #define INTERRUPT __interrupt __far
47
48 #define DONE_INIT       (prev_handler)
49 static void (INTERRUPT *prev_handler)();
50 #endif
51
52 #ifdef __DJGPP__
53 #define INTERRUPT
54
55 #define DONE_INIT prev_intr.pm_offset
56 static _go32_dpmi_seginfo intr, prev_intr;
57 #endif
58
59 static void INTERRUPT kbintr();
60
61 #define BUFSZ   32
62 static struct kb_event evbuf[BUFSZ];
63 static int buf_ridx, buf_widx;
64 static int last_key;
65
66 static unsigned int num_pressed;
67 unsigned char keystate[256];
68
69 #define ADVANCE(x)      ((x) = ((x) + 1) & (BUFSZ - 1))
70
71 int kb_init(int bufsz)
72 {
73         if(DONE_INIT) {
74                 fprintf(stderr, "keyboard driver already initialized!\n");
75                 return 0;
76         }
77
78         buf_ridx = buf_widx = 0;
79         last_key = -1;
80
81         memset(keystate, 0, sizeof keystate);
82         num_pressed = 0;
83
84         /* set our interrupt handler */
85         _disable();
86 #ifdef __WATCOMC__
87         prev_handler = _dos_getvect(KB_INTR);
88         _dos_setvect(KB_INTR, kbintr);
89 #endif
90 #ifdef __DJGPP__
91         _go32_dpmi_get_protected_mode_interrupt_vector(KB_INTR, &prev_intr);
92         intr.pm_offset = (intptr_t)kbintr;
93         intr.pm_selector = _go32_my_cs();
94         _go32_dpmi_allocate_iret_wrapper(&intr);
95         _go32_dpmi_set_protected_mode_interrupt_vector(KB_INTR, &intr);
96 #endif
97         _enable();
98
99         return 0;
100 }
101
102 void kb_shutdown(void)
103 {
104         if(!DONE_INIT) {
105                 return;
106         }
107
108         /* restore the original interrupt handler */
109         _disable();
110 #ifdef __WATCOMC__
111         _dos_setvect(KB_INTR, prev_handler);
112 #endif
113 #ifdef __DJGPP__
114         _go32_dpmi_set_protected_mode_interrupt_vector(KB_INTR, &prev_intr);
115         _go32_dpmi_free_iret_wrapper(&intr);
116 #endif
117         _enable();
118 }
119
120 int kb_isdown(int key)
121 {
122         switch(key) {
123         case KB_ANY:
124                 return num_pressed;
125
126         case KB_ALT:
127                 return keystate[KB_LALT] + keystate[KB_RALT];
128
129         case KB_CTRL:
130                 return keystate[KB_LCTRL] + keystate[KB_RCTRL];
131         }
132
133         if(isalpha(key)) {
134                 key = tolower(key);
135         }
136         return keystate[key];
137 }
138
139 #ifdef __WATCOMC__
140 void halt(void);
141 #pragma aux halt = \
142         "sti" \
143         "hlt";
144 #endif
145
146 #ifdef __DJGPP__
147 #define halt() asm volatile("sti\n\thlt\n\t")
148 #endif
149
150
151 int kb_event(struct kb_event *ev)
152 {
153         struct kb_event tmp;
154
155         if(!ev) ev = &tmp;
156
157         _disable();
158         while(buf_ridx == buf_widx) {
159                 _enable();
160                 halt();
161                 _disable();
162         }
163
164         *ev = evbuf[buf_ridx];
165         ADVANCE(buf_ridx);
166         _enable();
167
168         return ev->press ? ev->key : (ev->key | 0x100);
169 }
170
171 static void INTERRUPT kbintr()
172 {
173         struct kb_event *ev;
174         unsigned char code;
175         int key, c, press;
176         static int ext;
177
178         code = inp(KB_PORT);
179
180         if(code == 0xe0) {
181                 ext = 1;
182                 goto eoi;
183         }
184
185         if(code & 0x80) {
186                 press = 0;
187                 code &= 0x7f;
188
189                 if(num_pressed > 0) {
190                         num_pressed--;
191                 }
192         } else {
193                 press = 1;
194
195                 num_pressed++;
196         }
197
198         if(ext) {
199                 key = scantbl_ext[code];
200                 c = key;
201                 ext = 0;
202         } else {
203                 key = scantbl[code];
204                 c = (keystate[KB_LSHIFT] | keystate[KB_RSHIFT]) ? scantbl_shift[code] : key;
205         }
206
207         ev = evbuf + buf_widx;
208         ADVANCE(buf_widx);
209         if(buf_widx == buf_ridx) {
210                 ADVANCE(buf_ridx);
211         }
212
213         ev->key = key;
214         ev->code = code;
215         ev->press = press;
216
217         /* and update keystate table */
218         keystate[key] = press;
219
220 eoi:
221         outp(PIC1_CMD_PORT, OCW2_EOI);  /* send end-of-interrupt */
222 }