Thread: keyboard handlers & terminate and stay resident (tsr)

  1. #1
    Registered User
    Join Date
    Dec 2004
    Posts
    9

    keyboard handlers & terminate and stay resident (tsr)

    dear all, I would like to ask a question to all nice people here, hope that someone will help. I am developing a tsr program that can handle keyboard interrupt. I want to execute another program when I hit Ctrl+D while other program is still running. ISR routine in tsr program will service the interrupt, but how can I read/retrieve the BIOS keycode, that contains the Ctrl+D value using C? I read the value at port 60h, using inportb(0x60), but it just contains the raw/scan codes for the keypress not the BIOS keycode. and one more thing, can the tsr program terminate the currently running program as soon as the it detects Ctrl+D? basically what I want is like this:-

    install tsr -> run A.exe -> Ctrl+D detected! -> Service interrupt -> Close A.exe -> execute B.exe

    any suggestions/code examples/etc... would be very appreciated. thxs.

    -Triple_X, MY

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I'm assuming this is for DOS since in XP this would all be a mute point.

    There is no way possible for me to explain everything necessary for writing a well-behaved TSR program and what you should do, can do, shouldn't do, or can't do.

    The keyboard interrupt problem is an age old one that has about a trillion sites on the net specifically dedicated to explaining how to hook it.

    The best way to do it is to save the old interrupt handler, put your new one into the chain, and at shutdown - remove yours from the chain and put the old one back. I'm also not going to be able to explain how to extract all of the keycodes in this post. Needless to say the keyboard is a complex beasty - there are several websites that will show you how to do this - google it.

    But here is some code to start with.
    Code:
    //Keyboard handler stub
    #include <dos.h>
    #include <process.h>
    #include <stdio.h>
    #include <errno.h>
    
    
    
    typedef unsigned char BYTE;
    #define GETKEY 0x7F
    #define DOWN   0x01
    #define UP		 0x00
    #define QUERY_HANDLER 0xFF
    #define KEYBOARD 0x09
    
    void interrupt (*OldHandler)(...)=0;
    
    void interrupt NewHandler(...)
    {
    	static BYTE keys[255];
    
    	//Check for GETKEY status
    	if (_AX==GETKEY)
    	{
    		if (keys[_BX]==DOWN)
    		{
    			_AX=DOWN;
    		} else _AX=UP;
    	}
    	if (_AX==QUERY_HANDLER) _AX=0x2F2F;
    
    	BYTE key=inp(0x60);
    
    	if (key<128)
    	{
    		keys[key]=DOWN;
    	} else keys[key-128]=UP;
    
    	OldHandler();
    	outp(0x20,0x20);
    
    }
    
    int main(void)
    {
    	//Test to see if we are there or not
    	REGS regs;
    	regs.x.ax=0xFF;
    	int86(KEYBOARD,&regs,&regs);
    
    	if (_AX=0x2F2F)
    	{
    		printf("
    	//Install the new keyboard handler
    	printf("Saving current keyboard interrupt handler\n");
    
      OldHandler=getvect(KEYBOARD);
    
    	printf("Installing new keyboard interrupt handler\n");
    	setvect(KEYBOARD,NewHandler);
    	printf("Success.\n");
    
    
    	char *path=_argv[1];
    	printf("Executing program %s\n",path);
    
    	int program=spawnl(P_WAIT,path,NULL);
    
    	if (program!=0)
    	{
    		printf("Program %s aborted - cannot run child process.\n",path);
    		printf("DOS exit code %d\n",program);
    		if (program==-1)
    		{
    			switch(errno)
    			{
    				case E2BIG: printf("Argument list too long\n");break;
    				case EINVAL: printf("Invalid argument\n");break;
    				case ENOENT: printf("Path or file name not found\n");break;
    				case ENOEXEC: printf("Exec format error\n");break;
    				case ENOMEM: printf("Not enough core memory\n");break;
    			}
    		}
    	}
    	else
    	{
    		printf("Program terminated normally\n");
    		printf("Restoring keyboard interrupt vector\n");
    	}
    
    
    	setvect(KEYBOARD,OldHandler);
    	return(0);
    }
    DJGPP 32-bit C protected-mode keyboard handler
    Code:
    //DJGPP keyboard interrupt handler
    
    #include "keyboard.h"
    
    BOOL keydown[255];
    _go32_dpmi_seginfo oh, nh;
    
    BOOL GetKey(int scancode)
    {
    	return keydown[scancode];
    }
    
    void KeyHandler(void)
    {
    	asm ("cli; pusha");
      unsigned char al=0,ah=0;
      int lastkey=inportb(0x60);
    
      if (lastkey<128)
      {
    		keydown[lastkey]=1;
      } else keydown[lastkey-128]=0;
    
      al=inportb(0x61);
      al|=0x82;
      outportb(0x61,al);
      al&=0x7f;
      outportb(0x61,al);
    
      outportb(0x20,0x20);
    
      asm("popa; sti");
    }
    
    void StartKeyboard(void)
    {
    	nh.pm_offset=(int)KeyHandler;
      nh.pm_selector=_go32_my_cs();
      _go32_dpmi_get_protected_mode_interrupt_vector(0x09,&oh);
      _go32_dpmi_allocate_iret_wrapper(&nh);
      _go32_dpmi_set_protected_mode_interrupt_vector(0x09,&nh);
    }
    
    void StopKeyboard(void)
    {
    	_go32_dpmi_free_iret_wrapper(&nh);
      _go32_dpmi_set_protected_mode_interrupt_vector(0x09,&oh);
    }
    DJGPP header
    Code:
    #ifndef _KEYBOARD_
    #define _KEYBOARD_
    
    #include <go32.h>
    #include <dpmi.h>
    #include <pc.h>
    
    
    
    typedef unsigned char BOOL;
    
    void StartKeyboard(void);
    void StopKeyboard(void);
    
    BOOL GetKey(int scancode);
    
    
    
    #endif
    Old header that has the key codes to scan for
    Code:
    #ifndef _KEYBOARD_
    #define _KEYBOARD_
    
    //Keyboard header file
    
    #include <dos.h>
    #include <stdlib.h>
    
    
    #define VK_ESC			1
    #define VK_1      	2
    #define VK_!				2
    
    #define VK_2				3
    #define VK_@				3
    
    #define VK_3				4
    #define VK_#				4
    
    #define VK_4				5
    #define VK_$				5
    
    #define VK_5				6
    #define VK_%				6
    
    #define VK_6				7
    #define VK_^				7
    
    #define VK_7				8
    #define VK_&				8
    
    #define VK_8				9
    #define VK_*				9
    
    #define VK_9				10
    #define VK_(				10
    
    #define VK_0				11
    #define VK_)				11
    
    #define VK_-				12
    #define VK__				12
    
    #define VK_=				13
    #define VK_+				13
    
    #define VK_BACKSP		14
    #define VK_TAB			15
    #define VK_Q				16
    #define VK_W				17
    #define VK_E				18
    #define VK_R				19
    #define VK_T				20
    #define VK_Y				21
    #define VK_U				22
    #define VK_I				23
    #define VK_O				24
    #define VK_P				25
    #define VK_[				26
    #define VK_{				26
    
    #define VK_]				27
    #define VK_}				27
    
    #define VK_ENTER		28
    #define VK_RETURN		28
    #define VK_CTRL			29
    #define VK_A				30
    #define VK_S				31
    #define VK_D				32
    #define VK_F				33
    #define VK_G				34
    #define VK_H				35
    #define VK_J				36
    #define VK_K				37
    #define VK_L				38
    #define VK_;				39
    #define VK_:				39
    
    #define VK_'				40
    #define VK_"				40
    
    #define VK_`				41
    #define VK_~				41
    
    #define VK_LSHFT		42
    #define VK_\				43
    #define VK_|				43
    #define VK_Z				44
    #define VK_X				45
    #define VK_C				46
    #define VK_V				47
    #define VK_B				48
    #define VK_N				49
    #define VK_M				50
    
    #define VK_<				51
    #define VK_,				51
    
    #define VK_>				52
    #define VK_.				52
    
    #define VK_?				53
    #define VK_/				53
    
    #define VK_RSHFT		54
    #define VK_PRTSC		55
    #define VK_ALT			56
    #define VK_SPACE		57
    #define VK_CAPS			58
    #define VK_F1				59
    #define VK_F2				60
    #define VK_F3				61
    #define VK_F4				62
    #define VK_F5				63
    #define VK_F6				64
    #define VK_F7				65
    #define VK_F8				66
    #define VK_F9				67
    #define VK_F10			68
    #define VK_F11			133
    #define VK_F12			134
    #define VK_NUMLK		69
    #define VK_SCRLLK		70
    
    #define VK_HOME			71
    #define VK_NUM7			71
    
    #define VK_UP				72
    #define VK_NUM8			72
    
    #define VK_PGUP			73
    #define VK_NUM9			73
    #define VK_GRAY-		74
    
    #define VK_LEFT			75
    #define VK_NUM4			75
    
    #define VK_CENTER		76
    #define VK_NUM5			76
    
    #define VK_RIGHT		77
    #define VK_NUM6			77
    
    #define VK_GRAY+		78
    
    #define VK_END			79
    #define VK_NUM1			79
    
    #define VK_DOWN			80
    #define VK_NUM2			80
    
    #define VK_PGDN			81
    #define VK_NUM3			81
    
    #define VK_INS			82
    #define VK_NUM0			82
    
    #define VK_DEL			83
    #define VK_NUM.			83
    
    //Pointer to original interrupt handler
    void interrupt (*OldKeyboard)(...);
    
    
    void StartKeyboard(void);									//Called to install handler
    void EndKeyboard(void);										//Uninstalls handler
    
    
    #endif		//End of keyboard.h
    That should be enough to get you started.

  3. #3
    Registered User
    Join Date
    Dec 2004
    Posts
    9
    Thank Bubba for your reply. I have read through your code, and
    seems like you just test for scan codes (pardon me if I am wrong).
    Roughly, my code will look like this:

    Code:
    void interrupt newhandler();
    void interrupt (*oldhandler)();
    /* main() */
    void main() { // some people hate void main(), but i still want to use it ;-)
       oldhandler = getvect(9);
       setvect(9, newhandler);
       keep(0, (_SS + ((_SP+safetyspace)/16) - _psp); // resident
    }
    /* end main */
    
    
    /* keyboard isr */
    void interrupt newhandler() {
    /*
    step 1. 
    Keypress detected. I want to get BIOS keycode, not scan raw/scan codes.
    of course I can create a header file for this, like you did, but instead, 
    I want to handle 0x2004 only, that is BIOS keycode for Ctrl+D.
    
    step 2.
    Check for Ctrl+D (0x2004). If it is, goto step 3, else do nothing/just 
    eat the keypress
    
    step 3. 
    Ctrl+D detected! Now what I want to do is terminate current 
    application, let say A.exe. Then I want to run B.exe
    */
    
       if(BIOS keycode == 0x2004) {
    	-terminate application A. then..
    	-run application B.
       }
       else {
          (*oldhandler)(); // actual ISR
       }
    }
    please note that my concern here is to get BIOS keycode, NOT scan
    codes. I can get BIOS keycode by asking user like this:

    Code:
    int key;
    asm {
    	mov ah, 0x10
    	int 0x16
    	mov key, ax
    }
    now, after I press Ctrl+D, I will get 0x2004. but this code will wait
    until user press a key, obviously not a keyboard interrupt. I want to
    put something like this inside my isr routine. A code that can retrieve
    BIOS keycode. Is it possible? By the way, I am still waiting for
    Thantos comment on this. Thanks. ;-)

    rgds
    Triple_X, MY
    Last edited by Triple_X; 12-20-2004 at 03:52 AM.

  4. #4
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,820
    Read this on why void main is hated
    3.6.1 Main function
    1 A program shall contain a global function called main, which is the designated start of the program. It is
    implementation-defined
    whether a program in a freestanding environment is required to define a main
    function. [Note: in a freestanding environment, startup
    and termination is implementation-defined;
    startup
    contains the execution of constructors for objects of namespace scope with static storage duration; termination
    contains the execution of destructors for objects with static storage duration. ]
    2 An implementation shall not predefine the main function. This function shall not be overloaded. It shall
    have a return type of type int
    , but otherwise its type is implementation-defined.
    All implementations
    shall allow both of the following definitions of main:
    int main() { /* ... */ }
    and
    int main(int argc, char* argv[]) { /* ... */ }
    This would be the C++ Standard but it still applies
    Last edited by prog-bman; 12-20-2004 at 03:43 AM.
    Woop?

  5. #5
    ---
    Join Date
    May 2004
    Posts
    1,379
    Triple_X, can I just ask, out of curiosity, why are you trying to do this?

  6. #6
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    There's a simple solution. Just don't reply to people who have been told not to use void main, that continue to do so. If they aren't going to listen to correct advice, why bother replying to them? You could always simply add them to your ignore list. Not a bad solution for people who don't want to listen. Just ignore them.

    Quzah.
    Hope is the first step on the road to disappointment.

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You can retrieve the BIOS keycode using several methods. You can either use the _bioskey function in C, the int86x functions in C or you can use inline assembler.

    Unfortunately it has been a very long time since I've even messed with BIOS key codes because the default handler was so useless for my needs....I rarely even messed with it. Check Ralph Brown's Interrupt List for a complete explanation of the BIOS interrupt that handles keypresses.

    But my advice is to use the handler. It's much easier and it has less latency.

  8. #8
    Registered User
    Join Date
    Dec 2004
    Posts
    9
    My dearest quzah.. what is your deal? You are not helping at all.. either that or you dont
    post. I see that you and Thantos keep on criticise people instead of helping them. I did not
    use int main(), but I use void main() instead, is it wrong? If your computer crash when you
    declare void main(), dont blame me, throw your computer away... I just ask a favour, if you
    have expertise and willing to share, then give it. if you are not willing to help, and just to
    know how to bla bla bla... empty talk.. like big empty can.. talk much but useless why bother
    joining discussion groups! sorry if it is too harsh

    to sand_man: actually I want to implement this into my project.. embedded system project
    using x86 microcontroller. I want to detect a ctrl+d if in case I want to download my new
    firmware to disk-on-chip, to replace the old one. of course no os inside it, just bios and
    x-dos.

    to bubba: looks like I'm on my own now.. hmm... okay I will try look into it. anyway thanks
    for your help.. and for handing your codes. maybe I can use it in other application! ;-) but if you have solution how to solve this kinda a problem.. dont forget to share. as well as others....

    rgds,
    Triple_X, ;-)
    Last edited by Triple_X; 12-20-2004 at 07:35 PM.

  9. #9
    Registered User
    Join Date
    Sep 2001
    Posts
    4,912
    I did not use int main(), but I use void main() instead, is it wrong?
    Actually, yes it is wrong. You have already been told why.

  10. #10
    Registered User
    Join Date
    Dec 2004
    Posts
    9
    I think from now on, I have to teach myself not to use void main() anymore.. ;-)

  11. #11
    Registered User
    Join Date
    Sep 2001
    Posts
    4,912
    An excellent idea

  12. #12
    UT2004 Addict Kleid-0's Avatar
    Join Date
    Dec 2004
    Posts
    656
    At least you don't use goto :).. Plus I don't think void main() is TOO big of a deal, but it can cause big problems...so I guess it kind of is a big deal... *looks at Salem's avatar*

  13. #13
    ---
    Join Date
    May 2004
    Posts
    1,379
    main returns an int, there is no arguing that.

  14. #14
    Hello,

    Like sand_man said, there is no excuse for void main. Read this: Stroustrup: C++ Style and Technique FAQ - Can I write "void main()"?

    I agree with this statement: Even if your compiler accepts "void main()" avoid it, or risk being considered ignorant by C and C++ programmers.

    You may also want to consider these possibilities:

    Why not use "void main()"?
    • Because the standard says so.
    • Because the startup routines that call main could be assuming that the return value will be pushed onto the stack. If main() does not do this, then this could lead to stack corruption in the program's exit sequence, and cause it to crash.
    • Because you are likely to return a random value to the invokation environment. This is bad, because if someone wants to check whether your program failed, or to call your program from a makefile, then they won't be able to guarantee that a non-zero return code implies failure.

    Overall, declaring a function as void does not merely shut off or rearrange warnings: it may also result in a different function call/return sequence, incompatible with what the caller (in main's case, the C run-time startup code) expects. The ANSI standard says no to void main(), which should be an end of it.


    - Stack Overflow
    Segmentation Fault: I am an error in which a running program attempts to access memory not allocated to it and core dumps with a segmentation violation error. This is often caused by improper usage of pointers, attempts to access a non-existent or read-only physical memory address, re-use of memory if freed within the same scope, de-referencing a null pointer, or (in C) inadvertently using a non-pointer variable as a pointer.

  15. #15
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Okay I dug out my very old Waite Group Press book Build Your Own Flight Simulator in C++. It's also known as Flight's of Fantasy. It starts out explaining the BIOS functions to retrieve keypresses and then it creates its own handler because, as I said, the BIOS handler is fairly useless, especially in games.

    Here are the functions:

    Code:
    INT 16h
      Function 00h
       Read character from keyboard
    	 IN:
    		AH:  00h
    	 OUT:
    		AH: Keyboard scan code
    		AL: ASCII character
    	
       Function 01h
    	 Read keyboard status
    	   IN:
    		  AH: 01h
    	   OUT:
    		  if ZF (zero flag) clear then a character is waiting
    		  AH: Scan code
    		  AL: ASCII character
    		  if ZF set then no character waiting
     
    	Function 02h
    	  Return keyboard flags
    		IN:
    		  AH: 02h
    		OUT:
    		  AL: keyboard flags byte
    			BIT 7=Insert on
    			BIT 6=Caps Lock on
    			BIT 5=Num Lock on
    			BIT 4=Scroll Lock on
    			BIT 3=Alt key down
    			BIT 2=Ctrl key down
    			BIT 1=Left shift key down
    			BIT 0=Right shift key down
    Note that function 00h will bring the system to a halt until a key is pressed. So the proper method is to use function 01h to see if a key has been pressed and use function 00h to retrieve the actual key. Note that this is also very similar to how getch() and kbhit() work in C, getch() will pause if no key waiting but won't if a key is waiting.

    Here is an entire asm function that will do what you want:

    Code:
    _scankey PROC
    ;Get scan code of last key pressed
      mov ah,1	;Use Function 1
      int 16h	   ;Call BIOS keyboard interrupt
      jz nokey	 ;If no key pressed, short jump to nokey
      mov ah,0	;Else use function 0
      int 16h	   ;Call BIOS keyboard interrupt
      mov al,ah	;Get scan code into low byte of AX
      mov ah,0	;Zero high byte of AX
      ret			;Return value to C program (value is in AL)
    nokey:
      mov ax,0	;Return value of 0
      ret
    _scankey ENDP
    You will have to check for the return codes and the flags to figure out if CTRL is pressed. First check if D is pressed using the scankey function. Then use function 02h on int16h and check AL to see if CTRL is pressed. If it is then do your stuff.

    The scan codes I provided are correct so you can use them. This does not have to be written in pure assembler, it could be in inline assembler or you can use int86x() from inside of C to perform the interrupts.

Popular pages Recent additions subscribe to a feed