首页 > 解决方案 > 当我按下键盘上的键时如何防止重复字符

问题描述

我正在尝试学习如何防止键盘向屏幕发送多个字符并scanfDOS 下。我正在使用带有内联汇编的 Turbo-C。

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

嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯

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

我叫海姆

基本输出来自我不允许触摸的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();
 }

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

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

标签: cassemblykeyboardx86-16turbo-c

解决方案


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


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

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

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

代码4的 Turbo-C 3.0x 版本:

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

笔记

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

推荐阅读