I am trying to learn how to prevent the keyboard sending multiple chars to the screen and to scanf
under DOS. I am using Turbo-C with inline assembly.
If the characters entered on the keyboard are:
mmmmmmmmyyyyy nnnnnaaaaammmmmmeeeeee iiiiiissss HHHHaaaaiiiimmmm
The characters seen on the console and processed by scanf
would be:
my name is Haim
The basic output comes from the code in C which I am not allowed to touch. I must implement eliminate_multiple_press
and uneliminate_multiple_press
without touching the code in between.
The Turbo-C code I've written so far is:
#include <stdio.h>
#include <dos.h>
#include <string.h>
volatile char key;
volatile int i=0;
void interrupt (*Int9save) (void);
void interrupt kill_multiple_press()
{
asm{
MOV AL, 0
MOV AH,1
INT 16h
PUSHF
CALL DWORD PTR Int9save
MOV AX,0
}
asm{
JZ notSet
MOV key, AL
MOV AH, 04H
INT 16H
}
notSet:
//I am not sure what to do from here...............
I also know that it should be related to the zero flag, but what I
wrote so far didn`t effect on multiple characters.
}
void eliminate_multiple_press()
{
Int9save=getvect(9);
setvect(9,kill_multiple_press);
}
void uneliminate_multiple_press()
{
setvect(9,Int9save);
}
void main()
{
char str[10000]="";
clrscr();
eliminate_multiple_press();
printf("Enter your string: ");
scanf("%s",&str);
printf("\n%s",str);
uneliminate_multiple_press();
}
Information I have been given that relate to a solution are the keyboard BIOS routines that can be found at this link:
The problems I'm having are probably related to not understanding what to do at the label notSet
. The solution seems to be related to using a buffer and the register AX (especially AL), but I really have no Idea how to make scanf
to get the result I need. Does anyone have any ideas how I can complete this code to achieve the desired effect?
There are multiple layers of buffers that may be used by the BIOS, DOS, and the C library (including scanf
). When your machine starts up the interrupt vector table is modified to point IRQ1/INT 9h (the external keyboard interrupt) to a BIOS routine to handle characters as they are typed. At the lowest level there is usually a 32 byte6 circular buffer that is maintained in the BIOS Data Area (BDA) to keep track of the characters. You can use the Int 16h BIOS calls1 to interact with this low level keyboard buffer. If you remove characters from the BIOS keyboard buffer at interrupt time then DOS and the C library scanf
5 routine will never see them.
It appears that the exercise is to eliminate all duplicate2 characters entered into scanf
3 by intercepting keystrokes via Interrupt 9 (IRQ1) and throwing duplicates away. One idea for a new keyboard interrupt handler to eliminate the duplicates before DOS (and eventually scanf
) ever see them:
A Turbo-C 3.0x version of the code4:
#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <conio.h>
volatile char key = 0;
void interrupt (*Int9save)(void);
void interrupt kill_multiple_press(void)
{
asm {
PUSHF
CALL DWORD PTR Int9save /* Fake an interrupt call to original handler */
MOV AH, 1 /* Peek at next key in buffer without removing it */
INT 16h
JZ noKey /* If no keystroke then we are finished */
/* If ZF=1 then no key */
CMP AL, [key] /* Compare key to previous key */
JNE updChar /* If characters are not same, update */
/* last character and finish */
/* Last character and current character are same (duplicate)
* Read keystroke from keyboard buffer and throw it away (ignore it)
* When it is thrown away DOS and eventually `scanf` will never see it */
XOR AH, AH /* AH = 0, Read keystroke BIOS Call */
INT 16h /* Read keystroke that has been identified as a */
/* duplicate in keyboard buffer and throw away */
JMP noKey /* We are finished */
}
updChar:
asm {
MOV [key], AL /* Update last character pressed */
}
noKey: /* We are finished */
}
void eliminate_multiple_press()
{
Int9save = getvect(9);
setvect(9, kill_multiple_press);
}
void uneliminate_multiple_press()
{
setvect(9, Int9save);
}
void main()
{
char str[1000];
clrscr();
eliminate_multiple_press();
printf("Enter your string: ");
/* Get a string terminated by a newline. Max 999 chars + newline */
scanf("%999[^\n]s", &str);
printf("\n%s", str);
uneliminate_multiple_press();
}
scanf
that rely on DOS), they will never be seen by scanf
.scanf
will be indirectly making BIOS calls keeping the keyboard buffer clear. This code does't work in all generic cases especially where keystrokes may be buffered by the BIOS. To get around that the keyboard interrupt routine would have to remove all the duplicates in the keyboard buffer not just at the head as this code does.