nuclear@0: /* nuclear@0: DOS interrupt-based keyboard driver. nuclear@0: Copyright (C) 2013 John Tsiombikas nuclear@0: nuclear@0: This program is free software: you can redistribute it and/or modify nuclear@0: it under the terms of the GNU General Public License as published by nuclear@0: the Free Software Foundation, either version 3 of the License, or nuclear@0: (at your option) any later version. nuclear@0: nuclear@0: This program is distributed in the hope that it will be useful, nuclear@0: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@0: GNU General Public License for more details. nuclear@0: nuclear@0: You should have received a copy of the GNU General Public License nuclear@0: along with the program. If not, see nuclear@0: */ nuclear@0: #define KEYB_C_ nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "keyb.h" nuclear@0: #include "scancode.h" nuclear@0: nuclear@0: #define KB_INTR 0x9 nuclear@0: #define KB_PORT 0x60 nuclear@0: nuclear@0: #define PIC1_CMD_PORT 0x20 nuclear@0: #define OCW2_EOI (1 << 5) nuclear@0: nuclear@0: #define DONE_INIT (prev_handler) nuclear@0: nuclear@0: static void __interrupt __far kbintr(); nuclear@0: nuclear@0: static void (__interrupt __far *prev_handler)(); nuclear@0: nuclear@0: static int *buffer; nuclear@0: static int buffer_size, buf_ridx, buf_widx; nuclear@0: static int last_key; nuclear@0: nuclear@0: static unsigned int num_pressed; nuclear@0: static unsigned char keystate[256]; nuclear@0: nuclear@0: #define ADVANCE(x) ((x) = ((x) + 1) % buffer_size) nuclear@0: nuclear@0: int kb_init(int bufsz) nuclear@0: { nuclear@0: if(DONE_INIT) { nuclear@0: fprintf(stderr, "keyboard driver already initialized!\n"); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: buffer_size = bufsz; nuclear@0: if(buffer_size && !(buffer = malloc(buffer_size * sizeof *buffer))) { nuclear@0: fprintf(stderr, "failed to allocate input buffer, continuing without\n"); nuclear@0: buffer_size = 0; nuclear@0: } nuclear@0: buf_ridx = buf_widx = 0; nuclear@0: last_key = -1; nuclear@0: nuclear@0: memset(keystate, 0, sizeof keystate); nuclear@0: num_pressed = 0; nuclear@0: nuclear@0: /* set our interrupt handler */ nuclear@0: _disable(); nuclear@0: prev_handler = _dos_getvect(KB_INTR); nuclear@0: _dos_setvect(KB_INTR, kbintr); nuclear@0: _enable(); nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void kb_shutdown(void) nuclear@0: { nuclear@0: if(!DONE_INIT) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: /* restore the original interrupt handler */ nuclear@0: _disable(); nuclear@0: _dos_setvect(KB_INTR, prev_handler); nuclear@0: _enable(); nuclear@0: nuclear@0: free(buffer); nuclear@0: } nuclear@0: nuclear@0: int kb_isdown(int key) nuclear@0: { nuclear@0: if(key == KB_ANY) { nuclear@0: return num_pressed; nuclear@0: } nuclear@0: return (int)keystate[key]; nuclear@0: } nuclear@0: nuclear@0: void kb_wait(void) nuclear@0: { nuclear@0: int key; nuclear@0: while((key = kb_getkey()) == -1) { nuclear@0: /* put the processor to sleep while waiting for keypresses, but first nuclear@0: * make sure interrupts are enabled, or we'll sleep forever nuclear@0: */ nuclear@0: __asm { nuclear@0: sti nuclear@0: hlt nuclear@0: } nuclear@0: } nuclear@0: kb_putback(key); nuclear@0: } nuclear@0: nuclear@0: int kb_getkey(void) nuclear@0: { nuclear@0: int res; nuclear@0: nuclear@0: if(buffer) { nuclear@0: if(buf_ridx == buf_widx) { nuclear@0: return -1; nuclear@0: } nuclear@0: res = buffer[buf_ridx]; nuclear@0: ADVANCE(buf_ridx); nuclear@0: } else { nuclear@0: res = last_key; nuclear@0: last_key = -1; nuclear@0: } nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: void kb_putback(int key) nuclear@0: { nuclear@0: if(buffer) { nuclear@0: /* go back a place */ nuclear@0: if(--buf_ridx < 0) { nuclear@0: buf_ridx += buffer_size; nuclear@0: } nuclear@0: nuclear@0: /* if the write end hasn't caught up with us, go back one place nuclear@0: * and put it there, otherwise just overwrite the oldest key which nuclear@0: * is right where we were. nuclear@0: */ nuclear@0: if(buf_ridx == buf_widx) { nuclear@0: ADVANCE(buf_ridx); nuclear@0: } nuclear@0: nuclear@0: buffer[buf_ridx] = key; nuclear@0: } else { nuclear@0: last_key = key; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static void __interrupt __far kbintr() nuclear@0: { nuclear@0: unsigned char code; nuclear@0: int key, press; nuclear@0: nuclear@0: code = inp(KB_PORT); nuclear@0: nuclear@0: if(code >= 128) { nuclear@0: press = 0; nuclear@0: code -= 128; nuclear@0: nuclear@0: if(num_pressed > 0) { nuclear@0: num_pressed--; nuclear@0: } nuclear@0: } else { nuclear@0: press = 1; nuclear@0: nuclear@0: num_pressed++; nuclear@0: } nuclear@0: nuclear@0: key = scantbl[code]; nuclear@0: nuclear@0: if(press) { nuclear@0: /* append to buffer */ nuclear@0: last_key = key; nuclear@0: if(buffer_size > 0) { nuclear@0: buffer[buf_widx] = key; nuclear@0: ADVANCE(buf_widx); nuclear@0: /* if the write end overtook the read end, advance the read end nuclear@0: * too, to discard the oldest keypress from the buffer nuclear@0: */ nuclear@0: if(buf_widx == buf_ridx) { nuclear@0: ADVANCE(buf_ridx); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: /* and update keystate table */ nuclear@0: keystate[key] = press; nuclear@0: nuclear@0: outp(PIC1_CMD_PORT, OCW2_EOI); /* send end-of-interrupt */ nuclear@0: }