[comp.sys.ibm.pc.misc] letting keypress always gen interrupt

mgvalen@cs.vu.nl (Valent MG) (05/01/91)

Hello everybody out there,

Here I have a problem handling the keyboard the way I want
it. I'm (still, for those who read my previous posted question)
trying to write a (fast) game, so reading & acting to a keypress
must be done fast. I've installed my own keyboarddriver (see below)
and ACKnowledge each key that has been pressed by setting bit 7 of
port 0x61 to 1 for a short time.
This works ALMOST the way I want. The problem is the following
(you can check it by running the program): 
	Hold down a key. You will see it's scancode being printed 
	continuously. Now, while holding the key, press another and kee
	it pressed also. This latter keypress will take over and
	it's scancode will be printed. Till so far, ok. Now release
	the key you have pressed down last. It's (release) scancode
	will be printed as expected, but after this, NO MORE printing
	(even though you're still holding down the key you pressed
	first !). This is my problem; I want to see the printing of
	the first scancode again (= let it generate interrupts again
	so my interrupt handler will be invoked). 
	The only reason I can think of why this happens is that the
	new keypressing overrules the old one, and the old one isn't
	queued or anything.

So what I'm asking you all is: how should I solve this ?
(One way could be to do some bookkeeping of which key has already
been released and which not, but that makes it all much more 
complicated: Not every keypress generates an interrupt anymore and
I'll have to check variables; there goes my fast reacting to a keypress.
The same goes for buffering the keys which are pressed.
THIS are not the ways I'm looking for.)
I hope there's another way to acknowledge the keypressing or 
do something with the keyboard and/or interrupt controller (like telling
it to queue interrupts). 


				In advance, thank you for your reaction,
						Marco V. 

PS1: I know there must be an efficient way, because check out (almost)
any action game. In such a game, it is
possible for the player to move to left/right, in between firing by 
pressing the firekey (while keeping the 'move to the left/right' 
pressed). This gives the effect as if moving to the left/right and 
firing happen simultaneous.)

PS2: Below is my keyboard interrupt controller (in Turbo C). 
I've added a simple main-loop to let each keypress to be printed.

---------------------------cut here---------------------------------
/* This simple program takes over the original keyboard handling
   routine. Each time a key is pressed or released, it's scancode
   is printed on the screen. 
   Exit this program by hitting the ESC key (scancode == 1). 
*/

#include "dos.h"

/* Ptr to original interrupt handler */
static void (interrupt far *origint) ();

static int code = 0; /* Will contain the scancode */

void interrupt far newhandler()
{
int val; /* Used for ack-ing the keystroke interrupt */

	/* Get scancode in code */
	code = inportb(0x60);
	/* Ack the receiving by making bit 7 of 0x61 temporarily 1 */
	val = inportb(0x61);
	/* Make bit7 1 */
	outportb(0x61, val|0x80);
	/* Set the port back to it's old value (set bit 7 to 0) */
	outportb(0x61, val);
	/* Enable interrupts again */
	outportb(0x20, 0x20);
}


main ()
{
	/* Save original kbd interrupt handler */
	origint = getvect(0x09);
	/* Set new handler */
	setvect(0x09, newhandler);
	/* Print each time something is pressed until ESC is pressed */
	while(code != 1) {
		code = 0;
		while (code == 0) ;
		printf("Just pressed an %d\n",code);
	}
	/* Restore old handler */
	setvect(0x09, origint);
}
---------------------------cut here---------------------------------

anicolao@watcgl.waterloo.edu (Alex Nicolaou) (05/01/91)

In article <9819@star.cs.vu.nl> mgvalen@cs.vu.nl (Valent MG) writes:
>	Hold down a key. You will see it's scancode being printed 
>	continuously. Now, while holding the key, press another and kee
>	it pressed also. This latter keypress will take over and
>	it's scancode will be printed. Till so far, ok. Now release
>	the key you have pressed down last. It's (release) scancode
>	will be printed as expected, but after this, NO MORE printing
>	(even though you're still holding down the key you pressed
>	first !). This is my problem; I want to see the printing of

 ** I've run into this problem before, but I don't think that there is
a software solution. IF anyone knows of one, post it, don't use mail...
>
>PS1: I know there must be an efficient way, because check out (almost)
>any action game. In such a game, it is
>possible for the player to move to left/right, in between firing by 
>pressing the firekey (while keeping the 'move to the left/right' 
>pressed). This gives the effect as if moving to the left/right and 
>firing happen simultaneous.)
>
 ** Now this is not a problem if my memory serves me correctly. When 
you release the key you should still get an interrupt even though it 
wasn't generating interrupts by being held down. So, when the key goes
down, you set the status such that `player is moving left'. Regardless 
of whether the key repeats, the player keeps moving left until you get
the interrupt saying the key was released, at which time you set the 
player to `player is not moving left'. The in-between interrupts caused
by the key being held can be safely ignored - or if you like, you can 
just continually set the appropriate flag - setting the flag is probably
less work than checking to see if it is already set.

alex

mas35638@uxa.cso.uiuc.edu (Mike) (05/01/91)

What about watching for the release of said key?  The msb of the
scan code will be one when you release a key.

Mike Stangel
m-stangel@uiuc.edu

fisher@sc2a.unige.ch (05/03/91)

In article <9819@star.cs.vu.nl>, mgvalen@cs.vu.nl (Valent MG) writes:
> 
> Hello everybody out there,
> 
> Here I have a problem handling the keyboard the way I want
> it. [...]
> 	Hold down a key. You will see it's scancode being printed 
> 	continuously. Now, while holding the key, press another and kee
> 	it pressed also. This latter keypress will take over and
> 	it's scancode will be printed. Till so far, ok. Now release
> 	the key you have pressed down last. It's (release) scancode
> 	will be printed as expected, but after this, NO MORE printing
> 	(even though you're still holding down the key you pressed
> 	first !).  [...]

Well, this is not a software problem.  You see, it's the keyboard that
generates the repeat, if you hold it down long enough.  The programs that
let you set the repeat factor (or that have an increasing speed of repeat)
usualy drop the hardware repeat, and simulate their own.

One example of this can be found in WordPerfect (version 3.12 and 4.2, at
least) : when you hold down the UpArrow to scroll the screen, the scrolling
stops imediately after you release it, even if there should still be a dozen
"Ups" in the buffer.  This means either that the repeat is done internally,
or that the buffer is purged of any remaining "Ups" whenever the "key release"
code is recieved.

> PS1: I know there must be an efficient way, because check out (almost)
> any action game. In such a game, it is
> possible for the player to move to left/right, in between firing by 
> pressing the firekey (while keeping the 'move to the left/right' 
> pressed). This gives the effect as if moving to the left/right and 
> firing happen simultaneous.)

What you would like is a keybord that sends simultaneously repeat codes for
every key that is pressed...  wou'd have to buil your own.

This is what I would do (it can be quite fast, too):

Have your keyboard driver read the scan-code, and update an array of chars:

	#define key_release(code) ((code) & 0x80)
	#define clean(code)       ((code) & 0x7F)

	if (key_release(code)) {
		state[clean(code)] = NO;
		}
	else {
		if (key_state[clean(code)]==NO) {
			key_state[clean(code)] = YES;
			waiting_keys[clean(code)]++;
			}
		/* else drop keyboard's repeat... */
		}

The information in "key_state" and in "waiting_keys" must be public.  They
should probably be combined into a single char...
Your main game loop will have to do the following:

	char *fire =    waiting_keys[SCAN_CODE_FOR_SPACEBAR];
	char *fire_state = key_state[SCAN_CODE_FOR_SPACEBAR];
	char *left =    waiting_keys[SCAN_CODE_FOR_LEFT];
	char *left_state = key_state[SCAN_CODE_FOR_LEFT];
	char *sing_a_song = ...

	do {
		if (*fire) {
			*fire--;
			shoot_the_gun();
			}
		else if (*fire_state==YES) {
			shoot_the_gun();
			}
		if (*left) {
			*left--;
			move_to_the_(LEFT);
			}
		else if (*left_state==YES) {
			move_to_the_(LEFT);
			}
		if (*sing_a_song) {
			...

(Hmm, I got carried away...)

Anyways, you have now a system in which at every game loop, you will react
according to the current key status, and what's more, the user will be
guaranteed to fire at least three times if he stuck the spacebar three
times...

And don't tell me this is slower than the standard keyboard interrupt driver
in MS-DOS...


Hope this helps,

Markus G. Fischer, Dept of Anthropology, Geneva CH