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" ); 
}