www.pudn.com > kbdsrc > KEYINT.C
/*====================================================================*/ /* */ /* NOTE: Ensure this program is compiled for LARGE model. */ /* Non-ANSI stuff is pertinent to Microsoft QuickC v2.5. */ /* Porting to another compiler should not be too hard! */ /* */ /*====================================================================*/ #include#include #include #include #include #define FALSE 0 #define TRUE 1 typedef unsigned char BOOL; typedef unsigned char BYTE; /* The following typedef represents the SCAN CODE values of all of the keys. */ /* These values are independant of Shift, Alt and Ctrl key combinations. As */ /* you can see, these special keys have a value of their own which allows for */ /* their detection... */ typedef enum { GK_NULL =0x00, GK_ESC =0x01, GK_1 =0x02, GK_2 =0x03, GK_3 =0x04, GK_4 =0x05, GK_5 =0x06, GK_6 =0x07, GK_7 =0x08, GK_8 =0x09, GK_9 =0x0a, GK_0 =0x0b, GK_MINUS =0x0c, GK_EQUALS =0x0d, GK_BS =0x0e, GK_TAB =0x0f, GK_Q =0x10, GK_W =0x11, GK_E =0x12, GK_R =0x13, GK_T =0x14, GK_Y =0x15, GK_U =0x16, GK_I =0x17, GK_O =0x18, GK_P =0x19, GK_OSBRACKET =0x1a, GK_CSBRACKET =0x1b, GK_ENTER =0x1c, GK_CTRL =0x1d, GK_A =0x1e, GK_S =0x1f, GK_D =0x20, GK_F =0x21, GK_G =0x22, GK_H =0x23, GK_J =0x24, GK_K =0x25, GK_L =0x26, GK_SEMICOLON =0x27, GK_SCQUOTE =0x28, GK_SOQUOTE =0x29, GK_LSHIFT =0x2a, GK_BACKSLASH =0x2b, GK_Z =0x2c, GK_X =0x2d, GK_C =0x2e, GK_V =0x2f, GK_B =0x30, GK_N =0x31, GK_M =0x32, GK_COMMA =0x33, GK_FULLSTOP =0x34, GK_SLASH =0x35, GK_RSHIFT =0x36, GK_PRTSCRN =0x37, GK_ALT =0x38, GK_SPACEBAR =0x39, GK_CAPSLOCK =0x3a, GK_F1 =0x3b, GK_F2 =0x3c, GK_F3 =0x3d, GK_F4 =0x3e, GK_F5 =0x3f, GK_F6 =0x40, GK_F7 =0x41, GK_F8 =0x42, GK_F9 =0x43, GK_F10 =0x44, GK_NUMLOCK =0x45, GK_SCRLOCK =0x46, GK_HOME =0x47, GK_UP =0x48, GK_PAGEUP =0x49, GK_GREYMINUS =0x4a, GK_LEFT =0x4b, GK_NUM5 =0x4c, GK_RIGHT =0x4d, GK_GREYPLUS =0x4e, GK_END =0x4f, GK_DOWN =0x50, GK_PAGEDN =0x51, GK_INSERT =0x52, GK_DELETE =0x53, GK_F11 =0x57, GK_F12 =0x58, GK_PRINTSCRN =0x70, GK_PAUSEBREAK =0x71 } GAME_KEY; /* Define any necessary globals. We should always declare variables that are */ /* used within interrupt service routines as either global or static. It is */ /* NOT a good idea to allocate them dynamically off of the stack since we */ /* will not necessarily know the state of the stack at interrupt time... */ BYTE cbKeyTable[128]; /* This will hold our key states: */ /* 0 = nothing, 1 = DOWN, 2 = up */ /* The index into this array is */ /* the scan code of the key... */ BOOL bChainOldISR=FALSE; /* Do we still use the original */ /* keyboard interrupt routine? */ void (interrupt *OldKeyISR)( void ); /* Function pointer to old ISR */ /* The interrupt service routine itself! */ void interrupt KeyISR( void ) { /* NB: The following may look like overkill. In theory, a key is pressed */ /* and the scan code for said key should be retrieved from the keyboard */ /* controller. However, things aren't that simple! Certain keys, e.g. */ /* PrintScreen and Pause/Break, cause the keyboard controller to send a */ /* sequence of bytes back rather than a single scan code. These need to */ /* be filtered and interpreted - otherwise it could cause the wrong key */ /* pressed to be recorded... End of lecture */ static BYTE scan, /* Used to read the scan code from the keyboard */ updflag, /* Used to indicate if we have a 'real' key */ lctrl=0; /* Used to record the last 'special' key scan */ scan = inp(0x60); /* Ask keyboard controller for scan code */ updflag = TRUE; switch( scan ) { case 224: case 225: lctrl = scan; updflag = FALSE; /* Special, don't update this time! */ break; default: if ( lctrl==224 ) { if ( (scan&0x7f)==42 ) updflag=FALSE; else { if ( (scan&0x7f)==55 ) scan=GK_PRINTSCRN^(scan&0x80); } } else if ( lctrl==225 ) { if ( (scan&0x7f)==29 ) updflag=FALSE; else { if ( (scan&0x7f)==69 ) scan=GK_PAUSEBREAK^(scan&0x80); } } } if ( updflag ) { /* Yippee! Update our table. High bit of scan code says its released! */ cbKeyTable[scan&0x7f] = (scan&0x80)?2:1; lctrl = 0; } /* Do we call original? */ if ( !bChainOldISR ) { /* If we don't call the original ISR, we must service this interrupt */ /* ourselves. The following tells the keyboard controller and the */ /* Programmable Interrupt Controller (PIC) that we are done... */ _asm { in al, 61H mov ah, al or al, 80H out 61H, al mov al, ah out 61H, al mov al, 20H out 20H, al } } else _chain_intr( OldKeyISR ); } /*========================= main program ====================================*/ void main( void ) { int i, j; /* Start off by getting the address of the old interrupt handler and */ /* setting up our own */ OldKeyISR = _dos_getvect( 0x09 ); _dos_setvect( 0x09,KeyISR ); /* The global variable bChainOldISR is subsequently used to control */ /* whether the original keyboard handler is called after our own. */ /* The reason for this might not be immediately apparent but within */ /* normal game play the original routine is not required. However, */ /* once the player's lives have run out and the game is over he'll */ /* no doubt want to be added to some hall of fame (high score table) */ /* type thing. It is within this phase of the game that the original */ /* handler is desirable since it handles upper/lower case and all of */ /* the other shifted stuff. Furthermore, we can resort to using the */ /* simple getch() or whatever to use within our own edited input */ /* routine... */ bChainOldISR = FALSE; printf( "\n\nMultiple key press handler demo\n===============================\n\nPress ESCape to quit\n\n" ); /* Initialise our key state table */ _asm cli /* Disable interrupts for this bit!! */ memset( cbKeyTable,0x00,128 ); _asm sti /* Re-enable interrupts again! */ /* Display keys pressed/released */ while ( cbKeyTable[GK_ESC]!=0x01 ) { /* Quick linear scan of table, it's only a demo... */ for ( i=0; i<128; i++ ) { switch( cbKeyTable[i] ) { case 0x01: printf( "Key %02X is down\n",i ); cbKeyTable[i] = 0x00; /* Prevent print at next interation */ break; case 0x02: printf( "Key %02X is up\n",i ); cbKeyTable[i] = 0x00; } } } /* ESCape has been pressed, we MUST re-install the original keyboard */ /* handler BEFORE we terminate the program. If we don't we are sure */ /* to hang the machine once we get back to DOS... */ _dos_setvect( 0x09,OldKeyISR ); printf( "\n\nCoded by CyberFrog 8:)\n" ); }