Results 1 to 2 of 2

Thread: Keyboard interrupt handler help

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Oct 2001
    Posts
    80

    Keyboard interrupt handler help

    Well most people who have ever tried to make a game or anything related to that know that the standard functions for keyboard input (getch(), bioskey(0), maybe more...) just s**k. I've looked at many tutorials about replacing the keyboard interrupt handler with one of your own, but could never fully understand it. If anyone could explain the whole process in detail or point me to a good tutorial about it (something that's easy to understand and is in c++ cause most that I found were in asm), I'd appreciate it.

  2. #2
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    I can tell you all that I know. That is not much, but a start.

    DJGPP provides some helper functions, here is code that set's a keyboard interrupt (written for the game Jump'n'Bump, commented by me).

    Code:
    struct {		// information for the keyboard handler
    	char enabled;		// hook enabled
    } keyb_handler_info;
    
    volatile char keyb[256];	// stores if a key is pressed (by ASCII code)
    
    // array to map scancodes to ascii codes, can be indexed by one byte
    unsigned char scancode2ascii[256] = {
      0, 0, 49, 50, 51, 52, 53, 54, 55, 56,//0-9
      57, 48, 45, 0, 0, 0, 113, 119, 101, 114,//10-19
      116, 121, 117, 105, 111, 112, 0, 0, 0, 0,//20-29
      97, 115, 100, 102, 103, 104, 106, 107, 108, 0,//30-39
      0, 0, 0, 0, 122, 120, 99, 118, 98, 110,//40-49
      109, 44, 46, 47, 0, 0, 0, 32, 0, 0,//50-59
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0};
    
    // this stores some cpu state info before and while the interrupt handler is set
    _go32_dpmi_seginfo old_keyb_handler_seginfo, new_keyb_handler_seginfo;
    
    
    void keyb_handler() {	// called by the system if a key is pressed, stores the key in the last_keys array and the keyb array
    	unsigned char key;		// temporarily stores the key
    	static char extended;	// saves if the last character used the 8th bit
    	int c1;				// iteration variable
    
    	key = inportb(0x60);	// 0x60 seems to be the keyboard port
    
    	if (key == 0xe0)		// if it is a character that uses the 8th bit
    		extended = 1;		// this will skip this and the nxt call
    	else {
    		if (extended == 0) {
    			if ( (key & 0x80) == 0) {
    				keyb[key & 0x7f] = 1;
    				for (c1 = 48; c1 > 0; c1--)		// each character in the last_keys array
    					last_keys[c1] = last_keys[c1 - 1];	// has to go one step back to
    				last_keys[0] = scancode2ascii[key & 0x7f];	// make place for the new character
    			}
    			else
    				keyb[key & 0x7f] = 0;
    		}
    		else {				// key is extended
    			if ( (key & 0x80) == 0) {
    				keyb[(key & 0x7f) + 0x80] = 1;
    				for (c1 = 48; c1 > 0; c1--)
    					last_keys[c1] = last_keys[c1 - 1];
    				last_keys[0] = scancode2ascii[(key & 0x7f) + 0x80];
    			}
    			else
    				keyb[(key & 0x7f) + 0x80] = 0;
    		}
    		if (extended == 1)
    			extended = 0;
    	}
    
    	outportb(0x20, 0x20);	// handled
    
    }
    
    void keyb_handler_end() {}			// marks the end of the code of keyb_handler()
    
    
    char hook_keyb_handler(void) {		// enables the keyboard hook
    
    	if (keyb_handler_info.enabled == 0) {	// only if not already enabled
    		_go32_dpmi_lock_data( (char *)&keyb, sizeof(keyb) );	// locks the memory region of the keyb array
    		_go32_dpmi_lock_code(keyb_handler, (unsigned long)keyb_handler_end - (unsigned long)keyb_handler);		// locks the code region of keyb_handler
    		_go32_dpmi_get_protected_mode_interrupt_vector(9, &old_keyb_handler_seginfo); // get the old interrupt handler
    		new_keyb_handler_seginfo.pm_offset = (int)keyb_handler;	// function to call if a key is pressed is keyb_handler
    		if (_go32_dpmi_allocate_iret_wrapper(&new_keyb_handler_seginfo) != 0)		// create a small function that just calls keyb_handler and then does iret
    			return 1;
    		if (_go32_dpmi_set_protected_mode_interrupt_vector(9, &new_keyb_handler_seginfo) != 0) {	// and set this function as the handler
    			_go32_dpmi_free_iret_wrapper(&new_keyb_handler_seginfo);
    			return 1;
    		}
    		keyb_handler_info.enabled = 1;		// keyboard hook is now enabled
    		memset(last_keys, 0, sizeof(last_keys) );	// clear the last_keys array
    	}
    
    	return 0;
    
    }
    
    
    void remove_keyb_handler(void) {
    
    	if (keyb_handler_info.enabled == 1) {
    		_go32_dpmi_set_protected_mode_interrupt_vector(9, &old_keyb_handler_seginfo);
    		_go32_dpmi_free_iret_wrapper(&new_keyb_handler_seginfo);
    		keyb_handler_info.enabled = 0;
    	}
    
    }
    
    
    char key_pressed(unsigned char key) {	// returns if a key is pressed
    
    	return keyb[key];
    
    }
    I don't know exactly what this all does, but basically you need to store all information about the old handler, then set your new handler. This handler can't simply be a C function because all interrupt handlers need to return with a iret instruction (not the normal ret). Borland had a keyword that made a function return with iret. DJGPP gives you the function ..._allocate_iret_wrapper that is a small assembly function probably written like this:
    Code:
    mov ebx, [global variable that holds the address of your handler]
    call [ebx]
    iret
    Then you need to set the interrupt handler to your function, don't know exactly how to do this, must probably be in assembler too.

    When you're done, restore the old handler with the saved info.

    There are some restrictions on an interrupt handler:
    You may not use the longjmp functions.
    You may not use functions that result in system calls (like printf).
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width