doskeyb

view keyb.c @ 0:c2b210a70ce9

initial DOS keyboard driver commit
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 23 Sep 2013 03:42:39 +0300
parents
children da4c014bb055
line source
1 /*
2 DOS interrupt-based keyboard driver.
3 Copyright (C) 2013 John Tsiombikas <nuclear@member.fsf.org>
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.
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.
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_
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <conio.h>
24 #include <dos.h>
25 #include <i86.h>
26 #include "keyb.h"
27 #include "scancode.h"
29 #define KB_INTR 0x9
30 #define KB_PORT 0x60
32 #define PIC1_CMD_PORT 0x20
33 #define OCW2_EOI (1 << 5)
35 #define DONE_INIT (prev_handler)
37 static void __interrupt __far kbintr();
39 static void (__interrupt __far *prev_handler)();
41 static int *buffer;
42 static int buffer_size, buf_ridx, buf_widx;
43 static int last_key;
45 static unsigned char keystate[256];
47 #define ADVANCE(x) ((x) = ((x) + 1) % buffer_size)
49 int kb_init(int bufsz)
50 {
51 if(DONE_INIT) {
52 fprintf(stderr, "keyboard driver already initialized!\n");
53 return 0;
54 }
56 buffer_size = bufsz;
57 if(buffer_size && !(buffer = malloc(buffer_size * sizeof *buffer))) {
58 fprintf(stderr, "failed to allocate input buffer, continuing without\n");
59 buffer_size = 0;
60 }
61 buf_ridx = buf_widx = 0;
62 last_key = -1;
64 memset(keystate, 0, sizeof keystate);
66 /* set our interrupt handler */
67 _disable();
68 prev_handler = _dos_getvect(KB_INTR);
69 _dos_setvect(KB_INTR, kbintr);
70 _enable();
72 return 0;
73 }
75 void kb_shutdown(void)
76 {
77 if(!DONE_INIT) {
78 return;
79 }
81 /* restore the original interrupt handler */
82 _disable();
83 _dos_setvect(KB_INTR, prev_handler);
84 _enable();
86 free(buffer);
87 }
89 int kb_isdown(int key)
90 {
91 return (int)keystate[key];
92 }
94 void kb_wait(void)
95 {
96 int key;
97 while((key = kb_getkey()) == -1) {
98 /* put the processor to sleep while waiting for keypresses, but first
99 * make sure interrupts are enabled, or we'll sleep forever
100 */
101 __asm {
102 sti
103 hlt
104 }
105 }
106 kb_putback(key);
107 }
109 int kb_getkey(void)
110 {
111 int res;
113 if(buffer) {
114 if(buf_ridx == buf_widx) {
115 return -1;
116 }
117 res = buffer[buf_ridx];
118 ADVANCE(buf_ridx);
119 } else {
120 res = last_key;
121 last_key = -1;
122 }
123 return res;
124 }
126 void kb_putback(int key)
127 {
128 if(buffer) {
129 /* go back a place */
130 if(--buf_ridx < 0) {
131 buf_ridx += buffer_size;
132 }
134 /* if the write end hasn't caught up with us, go back one place
135 * and put it there, otherwise just overwrite the oldest key which
136 * is right where we were.
137 */
138 if(buf_ridx == buf_widx) {
139 ADVANCE(buf_ridx);
140 }
142 buffer[buf_ridx] = key;
143 } else {
144 last_key = key;
145 }
146 }
148 static void __interrupt __far kbintr()
149 {
150 unsigned char code;
151 int key, press;
153 code = inp(KB_PORT);
155 if(code >= 128) {
156 press = 0;
157 code -= 128;
158 } else {
159 press = 1;
160 }
162 key = scantbl[code];
164 if(press) {
165 /* append to buffer */
166 last_key = key;
167 if(buffer_size > 0) {
168 buffer[buf_widx] = key;
169 ADVANCE(buf_widx);
170 /* if the write end overtook the read end, advance the read end
171 * too, to discard the oldest keypress from the buffer
172 */
173 if(buf_widx == buf_ridx) {
174 ADVANCE(buf_ridx);
175 }
176 }
177 }
179 /* and update keystate table */
180 keystate[key] = press;
182 outp(PIC1_CMD_PORT, OCW2_EOI); /* send end-of-interrupt */
183 }