Creating your own Interrupt Handle in C

See below for a keyboard interrupt handler to allow more than one keystroke at a time.

To do this, we must pick an Interrupt that is called quite often. The "TIME KEEPER" interrupt (1CH) is called about 18.2 times a second, so let's use it?

C will do all of the dirty work for us if we use the _interrupt keyword.

All we have to do is declare it like a normal function/sub (notice the _interrupt _far keywords).
void _interrupt _far Timer(void) {

// your code goes here.
// see notes below

} // end Timer

NOTE: Inside this interrupt call we must be very careful on what we do. We can not call major interrupts or procedures. The simpler the better.

Now that we have our ISR written, we need to install it. We must install it and still allow DOS to run the original interrupt. (If DOS doesn't allow INT 1Ch to run, you will mess up allot of things....)

To install it:
void (_interrupt _far old_isr) ();   // Function pointer to save old ISR

old_isr = _dos_getvect(0x1C);  // Save old timekeeper ISR

_dos_setvect(0x1C,Timer);   // Install our new ISR

That's it. Once you are done with your program, on exit you must uninstall it.
_dos_setvect(0x1C, old_isr);

I got this info from "Tricks of the Game Programming Gurus". This is not a plug, but you should get this book. It has complete source on how to create a communications link for online gaming using this procedure.


The following code, when ran, will noticeably do nothing. But press the left ALT key and notice a smiley face appear in the top left corner of the screen. Release the key and it will restore the original character. Quite simple, but it gets the point across.

/***************************************************************************************
   This is a demo ISR to show how to create an ISR in C using the
   _interrupt keyword. */

#include "dos.h"

// pointer to shift status
#define ISLEFTALT   (*(unsigned char _far *)0x00400018)
// pointer to screen memory pos 0,0
#define OURSCRNPOS  (*(unsigned char _far *)0xB8000000)

char orgchar;    // orig. char

void (_interrupt _far *old_isr)();   // holds old interrupt handler

void _interrupt _far Timer() {

  if ((ISLEFTALT & 0x02) != 0)
    OURSCRNPOS = 0x01;   // smiley face
  else
    OURSCRNPOS = orgchar;  // orig. char
}

int main(int argc, char *argv[]) {
  printf("\nPress the ESC key to exit"
         "\nPress the Left ALT key");
  orgchar = OURSCRNPOS;                // get the current char

  old_isr = _dos_getvect(0x1C);        // install our ISR
  _dos_setvect(0x1C, Timer);           // Put our ISR in 0x1C

  while (getch() != 0x1B); {}          // wait for user to hit a key

  _dos_setvect(0x1C, old_isr);         // replace old ISR

  return 0;
}

Keyboard ISR demo

This handler example shows how to check for more than one keystroke at a time. For example; Have you ever wanted to check for two arrow keys pressed at the same time and only get the first one pressed? This example will show you how to test and use more than one arrow key at a time.

/***************************************************************************************
   This is a demo ISR to show how to use more than one keystroke
   at a time. 
   
   Press ESC key to exit demo .EXE

   I have added some code to slow it down for fast machines.  If this code is to slow,
     change the value of SDAMOUNT to a smaller value.  (I ran this on a Pentium 133mhz)

*/

#include "dos.h"
#include "graph.h"

#define SDAMOUNT        0x2FFFF       // Slow down amount
#define COLOR           0x05          // color for dot

#define KEYBOARD_INT    0x09
#define KEY_BUFFER      0x60
#define KEY_CONTROL     0x61
#define INT_CONTROL     0x20

#define MAKE_RIGHT      77   // make and break codes for the arrow keys
#define MAKE_LEFT       75   //
#define MAKE_UP         72   //
#define MAKE_DOWN       80   //
#define BREAK_RIGHT     205  //
#define BREAK_LEFT      203  //
#define BREAK_UP        200  //
#define BREAK_DOWN      208  //

#define INDEX_UP        0    // indices into arrow key state table
#define INDEX_DOWN      1    //
#define INDEX_RIGHT     2    //
#define INDEX_LEFT      3    //

void (_interrupt _far *Old_Isr)();   // holds old interrupt handler
unsigned char far *video_buffer = (char far *)0xA0000000L; // vram byte ptr
int raw_key;                         // the global raw keyboard data
int key_table[4] = {0,0,0,0};        // the arrow key state table

void _interrupt _far New_Key_Int() {
  _asm {
    sti                     ; re-enable interrupts
    in   al,KEY_BUFFER      ; get the key that was pressed
    xor  ah,ah              ; zero out upper 8 bits of AX
    mov  raw_key,ax         ; store the key in global
    in   al,KEY_CONTROL     ; set the control register
    or   al,82h             ; set the proper bits to reset the FF
    out  KEY_CONTROL,al     ; send the new data back to the control register
    and  al,7Fh
    out  KEY_CONTROL,al     ; complete the reset
    mov  al,20h
    out  INT_CONTROL,al     ; re-enable interrupts
  }

  switch(raw_key)  {
    case MAKE_UP: key_table[INDEX_UP] = 1;
      break;
    case MAKE_DOWN: key_table[INDEX_DOWN] = 1;
      break;
    case MAKE_RIGHT: key_table[INDEX_RIGHT] = 1;
      break;
    case MAKE_LEFT: key_table[INDEX_LEFT] = 1;
      break;
    case BREAK_UP: key_table[INDEX_UP] = 0;
      break;
    case BREAK_DOWN: key_table[INDEX_DOWN] = 0;
      break;
    case BREAK_RIGHT: key_table[INDEX_RIGHT] = 0;
      break;
    case BREAK_LEFT: key_table[INDEX_LEFT] = 0;
      break;
    default:
      break;
  }
}

int main(int argc, char *argv[]) {
  int x=160, y=100;
  unsigned long slowdown = 0;           // slow down fast machines

  _setvideomode(_MRES256COLOR);

  Old_Isr = _dos_getvect(KEYBOARD_INT);     // install our ISR
  _dos_setvect(KEYBOARD_INT, New_Key_Int);

  while(raw_key != 1) {              // if raw_key = 1 then ESC key (done)
    slowdown++;
    if (slowdown > SDAMOUNT) {
      slowdown = 0;
      if (key_table[INDEX_RIGHT]) x++;
      if (key_table[INDEX_LEFT]) x--;
      if (key_table[INDEX_UP]) y--;
      if (key_table[INDEX_DOWN]) y++;
      video_buffer[((y<<8) + (y<<6)) + x] = COLOR;
    }
  }

  _dos_setvect(KEYBOARD_INT, Old_Isr);   // replace old ISR
  _setvideomode(_DEFAULTMODE);           // set to text mode
}