当我按下键盘上的键时如何防止重复字符 [英] How to prevent duplicate chars when I press keys on the keyboard

查看:28
本文介绍了当我按下键盘上的键时如何防止重复字符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 nnnnnaaaaammmmmmeeee iiiiiisss HHHHaaaaiiiimmmm

mmmmmmmmyyyyy nnnnnaaaaammmmmmeeeeee iiiiiissss HHHHaaaaiiiimmmm

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

The characters seen on the console and processed by scanf would be:

我叫海姆

基本输出来自 C 中的代码,我不允许触摸.我必须实现 eliminate_multiple_pressuneliminate_multiple_press 而不触及其间的代码.

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_presswithout touching the code in between.

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

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("
%s",str);
  uneliminate_multiple_press();
 }

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

Information I have been given that relate to a solution are the keyboard BIOS routines that can be found at this link:

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

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?

推荐答案

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

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 scanf5 routine will never see them.

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

看来这个练习是通过通过中断 9 (IRQ1) 拦截击键来消除输入到 scanf3 中的所有重复的2 字符和扔掉重复的东西.一个新的键盘中断处理程序的想法是在 DOS(以及最终 scanf)看到它们之前消除重复:

It appears that the exercise is to eliminate all duplicate2 characters entered into scanf3 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:

  • 跟踪变量中按下的前一个字符
  • 调用原始(已保存)中断 9,以便 BIOS 更新键盘缓冲区和 DOS 期望的键盘标志.
  • 使用 Int 16h/AH=1h 查询键盘以查看字符是否可用.如果没有可用的字符,将设置零标志 (ZF),如果有可用的字符则清除.此键盘 BIOS 调用会窥探键盘缓冲区的开头,而不会实际删除下一个可用字符.
  • 如果一个字符可用,则将其与前一个字符进行比较.
    • 如果它们不同,则用当前字符更新前一个字符并退出
    • 如果它们相同,则使用 Int 16h/AH=0h从键盘缓冲区中删除重复字符并退出
    • Keep track of the previous character pressed in a variable
    • Call the original (saved) Interrupt 9 so that the BIOS updates the keyboard buffer and the keyboard flags as DOS expects them to appear.
    • Query the keyboard to see if a character is available with Int 16h/AH=1h.The Zero Flag (ZF) will be set if no characters are available and clear if there is one available. This keyboard BIOS call peeks into the beginning of the keyboard buffer without actually removing the next character available.
    • If a character is available then compare it with the previous character.
      • If they are different then update the previous character with the current character and exit
      • If they are the same then use Int 16h/AH=0h to remove the duplicate character from the keyboard buffer and exit

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

      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[^
      ]s", &str);
          printf("
      %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.

      • Notes

        • 1Within the keyboard interrupt handler you want to avoid any keyboard BIOS call that will block waiting for keyboard input. If using Int 16h/AH=0 make sure there is a character available first with Int 16h/AH=1 otherwise Int 16h/AH=0 will block while waiting for another character to arrive.
        • 2Removing duplicate characters is not the same as disabling the keyboard repeat rate.
        • 3Because the duplicates are removed before DOS routines see them (and functions like scanf that rely on DOS), they will never be seen by scanf.
        • 4Some modifications may have to be made to be compatible with versions of Turbo-C other than 3.0x.
        • 5This method only works because 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.
        • 6Each keystroke takes up 2 bytes of space in the BIOS keyboard buffer (in the BDA). 2 of the 32 bytes are lost because they are used to detect if the keyboard buffer is full or empty. This means the maximum number of keystrokes the BIOS can buffer is 15.
        • 这篇关于当我按下键盘上的键时如何防止重复字符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆