我正在尝试学习如何防止键盘在DOS下向屏幕和scanf
发送多个字符。我正在将Turbo-C与内联汇编一起使用。
如果在键盘上输入的字符是:
mmmmmmmmyyyyy nnnnnaaaaammmmmmeeeeeeee iiiiiissss HHHHaaaaiiiimmmm
在控制台上看到并由scanf
处理的字符为:
我叫Haim
基本输出来自 C 中的代码,我无法触摸。我必须实现eliminate_multiple_press
和uneliminate_multiple_press
,而无需在两者之间碰触代码。
到目前为止,我编写的Turbo-C代码是:
#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();
}
我得到的与解决方案有关的信息是可以在this link上找到的键盘BIOS例程:
我遇到的问题可能与不了解标签notSet
上的操作有关。解决方案似乎与使用缓冲区和寄存器 AX (尤其是 AL )有关,但是我真的不知道如何制作scanf
来获得结果我需要。有谁知道如何完成此代码以达到预期的效果?
答案 0 :(得分:2)
BIOS,DOS和 C 库(包括scanf
)可以使用多层缓冲区。当计算机启动时,中断向量表被修改为将IRQ1 / INT 9h (外部键盘中断)指向BIOS例程以处理键入的字符。在最低级别,通常在circular buffer(BDA)中维护一个32字节的 6 BIOS Data Area,以跟踪字符。您可以使用Int 16h BIOS calls 1 与此低级键盘缓冲区进行交互。如果在中断时间从BIOS键盘缓冲区中删除字符,则DOS和 C 库scanf
5 例程将永远看不到它们。
看来,练习是通过通过中断9(IRQ1)拦截击键并丢弃重复项来消除输入到scanf
3 中的所有重复项 2 。一个新的键盘中断处理程序可以消除DOS(并最终scanf
)出现之前的重复项的想法:
Turbo-C 3.0x版本的代码 4 :
#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
之类的功能),所以scanf
永远不会看到它们。scanf
,因为它将间接进行BIOS调用以保持键盘缓冲区畅通。此代码不适用于所有通用情况,尤其是在BIOS可能缓冲击键的情况下。为了解决这个问题,键盘中断例程必须删除所有,而不是像此代码一样直接删除键盘缓冲区中的重复项。