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