当我按下键盘上的键时,如何防止重复的字符

我试图学习如何防止键盘发送多个字符到屏幕和DOS下扫描。 我正在使用带有内联汇编的Turbo-C。

如果键盘上输入的字符是:

嗯嗯嗯嗯

在控制台上看到并由scanf处理的字符将是:

我的名字是Haim

基本输出来自C中的代码,我不允许触摸。 我必须实现eliminate_multiple_pressuneliminate_multiple_press而不触及uneliminate_multiple_press的代码。 在此处输入图像描述

我到目前为止写的Turbo-C代码是:

 #include  #include  #include  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(); } 

我给出的与解决方案相关的信息是可在此链接中找到的键盘BIOS例程:

我遇到的问题可能与不了解标签notSet要做什么有关。 解决方案似乎与使用缓冲区和寄存器AX (尤其是AL )有关,但我真的不知道如何使scanf获得我需要的结果。 有没有人有任何想法如何完成此代码以达到预期的效果?

BIOS,DOS和C库(包括scanf )可以使用多层缓冲区。 当您的机器启动时,中断向量表被修改为将IRQ1 / INT 9h (外部键盘中断)指向BIOS例程以处理键入的字符。 在最低级别,通常在BIOS数据区 (BDA)中维护一个32字节的6 循环缓冲区以跟踪字符。 您可以使用Int 16h BIOS调用 1与此低级键盘缓冲区进行交互。 如果在中断时从BIOS键盘缓冲区中删除字符,则DOS和Cscanf 5例程将永远不会看到它们。


在BIOS /中断级别消除重复字符的方法

似乎练习是通过中断9(IRQ1)截取击键并丢弃重复项来消除输入到scanf 3中的所有重复的2个字符。 一个新的键盘中断处理程序的想法,以消除DOS之前(并最终scanf )看到它们的重复:

  • 跟踪变量中按下的前一个字符
  • 调用原始(已保存)中断9,以便BIOS在DOS期望它们出现时更新键盘缓冲区和键盘标志。
  • 查询键盘以查看Int 16h / AH = 1h是否有可用字符。如果没有可用字符,将设置零标志(ZF),如果有可用字符则清除。 此键盘BIOS调用将进入键盘缓冲区的开头,而不会实际删除下一个可用字符。
  • 如果某个角色可用,则将其与前一个角色进行比较。
    • 如果它们不同,则使用当前字符更新前一个字符并退出
    • 如果它们相同,则使用Int 16h / AH = 0h从键盘缓冲区中删除重复的字符并退出

Turbo-C 3.0x版本的代码4

 #include  #include  #include  #include  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(); } 

笔记

  • 1在键盘中断处理程序中,您希望避免任何键盘BIOS调用阻止等待键盘输入。 如果使用Int 16h / AH = 0,请确保首先使用Int 16h / AH = 1可用字符,否则Int 16h / AH = 0将在等待另一个字符到达时阻塞。
  • 2删除重复字符与禁用键盘重复率不同。
  • 3因为在DOS例程看到它们之前删除了重复项(以及依赖于DOS的scanf函数),所以scanf永远不会看到它们。
  • 4可能必须进行一些修改才能与除3.0x之外的Turbo-C版本兼容。
  • 5此方法仅起作用,因为scanf将间接进行BIOS调用以保持键盘缓冲区清晰。 此代码在所有通用情况下都不起作用,特别是在BIOS可以缓冲击键的情况下。 为了解决这个问题,键盘中断例程必须删除键盘缓冲区中的所有重复项,而不仅仅是在此代码中。
  • 6每次击键占用BIOS键盘缓冲区中的2个字节空间(在BDA中 )。 32个字节中的2个丢失,因为它们用于检测键盘缓冲区是已满还是空。 这意味着BIOS可以缓冲的最大击键次数是15。