/* * (C) Copyright 1992, ..., 2003 the "DOSEMU-Development-Team". * * for details see file COPYING in the DOSEMU distribution */ /* * DANG_BEGIN_MODULE * * Description: Keyboard backend - interface to the DOS side * * Maintainer: Rainer Zimmermann * * REMARK * This module handles interfacing to the DOS side both on int9/port60h level * or on the bios buffer level. * Keycodes are buffered in a queue, which, however, has limited depth, so it * shouldn't be used for pasting. * * More information about this module is in doc/README.newkbd * * /REMARK * DANG_END_MODULE * */ #include #include "emu.h" #include "types.h" #include "keyboard.h" #include "keyb_server.h" #include "bios.h" #include "pic.h" #include "cpu.h" #ifdef X86_EMULATOR #include "cpu-emu.h" #endif /* If this is set to 1, the server will check whether the BIOS keyboard buffer * is full. * This is somewhat inaccurate emulation, as the state of BIOS variables really * shouldn't affect 'hardware' behaviour, but this seems the only way of knowing how * fast DOS is processing keystrokes, in particular for pasting. */ #define KEYBUF_HACK 1 t_shiftstate shiftstate; Bit16u bios_buffer; t_shiftstate shiftstate_buffer; /********** QUEUE ***********/ /* * This is the dosemu keyboard queue. * Each queue entry holds a data structure corresponding to (mostly) * one keypress or release event. [The exception are the braindead * 0xe02a / 0xe0aa shift key emulation codes the keyboard processor * 'decorates' some kinds of keyboard events, which for convenience * are treated as seperate events.] * Each queue entry holds a up to 4 bytes of raw keycodes for the * port 60h emulation, along with a 2-byte translated int16h keycode * and the shift state after this event was processed. * Note that the bios_key field can be empty (=0), e.g. for shift keys, * while the raw field should always contain something. */ #define QUEUE_SIZE (KEYB_QUEUE_LENGTH+1) typedef struct { Bit16u bios_key; /* for the seg 0x40 buffer */ t_shiftstate shiftstate; t_rawkeycode raw[4]; /* sub-queue for raw keycodes - read */ /* top-to-bottom, empty entries=0 */ } t_queueentry; t_queueentry queue[QUEUE_SIZE]; int q_head, q_tail; /* * queue handling */ static inline Boolean queue_empty(void) { return (q_head==q_tail); } #if 0 static inline Boolean queue_full(void) { } #endif int keyb_queuelevel(void) { int n; n=q_head-q_tail; return (n<0) ? n+QUEUE_SIZE : n; } /* * this has to work even if the variables are uninitailized! */ void clear_queue(void) { q_head=q_tail=0; } void write_queue(Bit16u bios_key,t_shiftstate shift,Bit32u raw) { int qh = q_head; #if 0 /* shall we make this a dosemu.conf option? --Hans */ switch (raw & ~0x80) { case KEY_PGUP: case KEY_PGDN: case KEY_HOME: case KEY_END: bios_key &= 0xFF00; raw &= 0xff; } #endif if (bios_key == 0x23e0) /* Cyrillic_er work around */ bios_key &= 0x00FF; k_printf("KBD: writing to queue: bios_key=%04x shift=%04x scan=%08x\n", bios_key,shift,(unsigned int)raw); if (++qh==QUEUE_SIZE) qh=0; if (qh==q_tail) { k_printf("KBD: queue overflow!\n"); return; } queue[q_head].bios_key=bios_key; queue[q_head].shiftstate=shift; *(Bit32u*)&queue[q_head].raw=raw; /* XXX this is endian dependent! */ q_head=qh; } /* * NOTE: this routine does NOT read one queue entry at a time! * rather, we first read the raw codes byte-wise and only return the bios * codes along with the last raw byte. */ void read_queue(Bit16u *bios_key, t_shiftstate *shift, t_rawkeycode *raw) { int i; t_queueentry *qp; *bios_key = 0; if (queue_empty()) { *raw = 0; return; } qp = &queue[q_tail]; for(i=3;i>=0;i--) if (qp->raw[i]) { *raw=qp->raw[i]; qp->raw[i]=0; break; } /* bios codes and shiftstates are only returned after all of the raw codes * in the current queue entry have been read. * This mimics the behaviour of a real keyboard bios, i.e. only after int9 * has read all scancodes of a particular keyboard event, the bios scancode * appears in the buffer. */ if (*((Bit32u*)qp->raw)==0) { *bios_key = qp->bios_key; *shift = qp->shiftstate; if (++q_tail==QUEUE_SIZE) q_tail=0; } } /****************** END QUEUE *******************/ /********************** NON-KEYBINT (BIOS) mode backend ***************/ /* * Interface to DOS (BIOS keyboard buffer/shiftstate flags) */ void clear_bios_keybuf() { #ifdef X86_EMULATOR int tmp = E_MUNPROT_STACK(0); /* no faults in BIOS area! */ #endif WRITE_WORD(BIOS_KEYBOARD_BUFFER_START,0x001e); WRITE_WORD(BIOS_KEYBOARD_BUFFER_END, 0x003e); WRITE_WORD(BIOS_KEYBOARD_BUFFER_HEAD, 0x001e); WRITE_WORD(BIOS_KEYBOARD_BUFFER_TAIL, 0x001e); MEMSET_DOS(BIOS_KEYBOARD_BUFFER,0,32); #ifdef X86_EMULATOR if (tmp) E_MPROT_STACK(0); #endif } static inline Boolean bios_keybuf_full(void) { int start,end,head,tail; start = READ_WORD(BIOS_KEYBOARD_BUFFER_START); end = READ_WORD(BIOS_KEYBOARD_BUFFER_END); head = READ_WORD(BIOS_KEYBOARD_BUFFER_HEAD); tail = READ_WORD(BIOS_KEYBOARD_BUFFER_TAIL); tail+=2; if (tail==end) tail=start; return (tail==head); } /* * update the seg 0x40 keyboard flags from dosemu's internal 'shiftstate' * variable. * This is called either from kbd_process() or the get_bios_key() helper. * It is never called if a dos application takes complete * control of int9. */ void copy_shift_state(t_shiftstate shift) { Bit8u flags1, flags2, flags3, leds; #ifdef X86_EMULATOR int tmp = E_MUNPROT_STACK(0); /* no faults in BIOS area! */ #endif #if 0 k_printf("KBD: copy_shift_state() %04x\n",shift); #endif flags1=flags3=leds=0; /* preserve pause bit */ flags2 = READ_BYTE(BIOS_KEYBOARD_FLAGS2) & PAUSE_MASK; if (shift & INS_LOCK) flags1 |= 0x80; if (shift & CAPS_LOCK) { flags1 |= 0x40; leds |= 0x04; } if (shift & NUM_LOCK) { flags1 |= 0x20; leds |= 0x02; } if (shift & SCR_LOCK) { flags1 |= 0x10; leds |= 0x01; } if (shift & ALT) flags1 |= 0x08; if (shift & CTRL) flags1 |= 0x04; if (shift & L_SHIFT) flags1 |= 0x02; if (shift & R_SHIFT) flags1 |= 0x01; if (shift & INS_PRESSED) flags2 |= 0x80; if (shift & CAPS_PRESSED) flags2 |= 0x40; if (shift & NUM_PRESSED) flags2 |= 0x20; if (shift & SCR_PRESSED) flags2 |= 0x10; if (shift & SYSRQ_PRESSED) flags2 |= 0x04; if (shift & L_ALT) flags2 |= 0x02; if (shift & L_CTRL) flags2 |= 0x01; flags3 |= 0x10; /* set MF101/102 keyboard flag */ if (shift & R_ALT) flags3 |= 0x08; if (shift & R_CTRL) flags3 |= 0x04; WRITE_BYTE(BIOS_KEYBOARD_FLAGS1,flags1); WRITE_BYTE(BIOS_KEYBOARD_FLAGS2,flags2); WRITE_BYTE(BIOS_KEYBOARD_FLAGS3,flags3); WRITE_BYTE(BIOS_KEYBOARD_LEDS,leds); #ifdef X86_EMULATOR if (tmp) E_MPROT_STACK(0); #endif } /****************** KEYBINT MODE BACKEND *******************/ /* run the queue backend * called either periodically from keyb_server_run or, for faster response, * when writing to the queue and after the IRQ1 handler is finished. */ void int_check_queue() { t_rawkeycode rawscan; #if 0 k_printf("KBD: int_check_queue(): queue_empty=%d port60_ready=%d bios_keybuf_full=%d\n", queue_empty(), port60_ready, bios_keybuf_full()); #endif if (queue_empty()) return; if (int9_running) { k_printf("KBD: int9 running\n"); return; } #if 1 if (port60_ready) { k_printf("KBD: port60 still has data\n"); return; } #endif if (!port60_ready #if KEYBUF_HACK && (!bios_keybuf_full() || (READ_BYTE(BIOS_KEYBOARD_FLAGS2) & PAUSE_MASK)) #endif ) { read_queue(&bios_buffer,&shiftstate_buffer,&rawscan); k_printf("KBD: read queue: bios_buffer=%04x shiftstate_buffer=%04x raw=%02x\n", bios_buffer,shiftstate_buffer,rawscan); k_printf("KBD: queuelevel=%d\n",keyb_queuelevel()); output_byte_8042(rawscan); } } /******************* GENERAL ********************************/ void backend_run(void) { static int running = 0; /* avoid re-entrance problems */ if (running) { k_printf("KBD: backend_run cancelled\n"); return; } running++; int_check_queue(); running--; } void backend_reset() { #ifdef X86_EMULATOR int tmp = E_MUNPROT_STACK(0); /* no faults in BIOS area! */ #endif clear_queue(); bios_buffer=0; shiftstate_buffer=0; /* initialise keyboard-related BIOS variables */ WRITE_BYTE(BIOS_KEYBOARD_TOKEN,0); /* buffer for Alt-XXX (not used by emulator) */ #ifdef X86_EMULATOR if (tmp) E_MPROT_STACK(0); #endif clear_bios_keybuf(); copy_shift_state(shiftstate); }