如何防止键盘按键时出现重复字符

2023-12-05

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

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

mmmmmmmmyyyyy nnnnnaaaaammmmmmeeeeeee iiiiiissss HHHHaaaaiiiimmmm

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

我的名字是海姆

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. enter image description here

到目前为止我编写的 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得到我需要的结果。有谁知道我如何完成这段代码以达到预期的效果?


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/中断级别消除重复字符的方法

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 期望的那样。
  • 查询键盘以查看某个字符是否可用累计16小时/AH=1小时如果没有可用字符,则零标志 (ZF) 将被设置;如果有可用字符,则将清除零标志。此键盘 BIOS 调用会查看键盘缓冲区的开头,而不会实际删除下一个可用字符。
  • If a character is available then compare it with the previous character.
    • 如果不同则用当前字符更新前一个字符并退出
    • 如果它们相同,则使用整数 16 小时/AH=0 小时从键盘缓冲区中删除重复字符并退出

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

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.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何防止键盘按键时出现重复字符 的相关文章

  • 如何通过在 Python 3.x 上按键来启动和中断循环

    我有这段代码 当按下 P 键时会中断循环 但除非我按下非 P 键 否则循环不会工作 def main openGame while True purchase imageGrab if a sum gt 1200 fleaButton ti
  • 如何在 Android NDK 中创建新的 NativeWindow 而无需 Android 操作系统源代码?

    我想编译一个 Android OpenGL 控制台应用程序 您可以直接从控制台启动 Android x86 运行 或者从 Android x86 GUI 内的 Android 终端应用程序运行 这个帖子 如何在 Android NDK 中创
  • 32 位应用程序的特征最大矩阵大小

    所以 我正在寻找Eigen http eigen tuxfamily org index php title Main Page当我尝试声明大于 10000x10000 的矩阵时 包崩溃 我需要声明一个像这样的矩阵 可靠地大约有 13000
  • 构造函数中显式关键字的使用

    我试图了解 C 中显式关键字的用法 并查看了这个问题C 中的explicit关键字是什么意思 https stackoverflow com questions 121162 但是 那里列出的示例 实际上是前两个答案 对于用法并不是很清楚
  • JSON 数组到 C# 列表

    如何将这个简单的 JSON 字符串反序列化为 C 中的列表 on4ThnU7 n71YZYVKD CVfSpM2W 10kQotV 这样 List
  • POCO HTTPSClientSession 发送请求时遇到问题 - 证书验证失败

    我正在尝试使用 POCO 库编写一个向服务器发出 HTTPS 请求的程序 出于测试目的 我正在连接到具有自签名证书的服务器 并且我希望允许客户端进行连接 为了允许这种情况发生 我尝试安装InvalidCertificateHandler这是
  • 访问者和模板化虚拟方法

    在一个典型的实现中Visitor模式 该类必须考虑基类的所有变体 后代 在许多情况下 访问者中的相同方法内容应用于不同的方法 在这种情况下 模板化的虚拟方法是理想的选择 但目前这是不允许的 那么 模板化方法可以用来解析父类的虚方法吗 鉴于
  • 如何从网站下载 .EXE 文件?

    我正在编写一个应用程序 需要从网站下载 exe 文件 我正在使用 Visual Studio Express 2008 我正在使用以下代码 private void button1 Click object sender EventArgs
  • 如何重置捕获像素的值

    我正在尝试创建一个 C 函数 该函数返回屏幕截图位图中每四个像素的 R G 和 B 值 这是我的代码的一部分 for int ix 4 ix lt 1366 ix ix 4 x x 4 for int iy 3 iy lt 768 iy i
  • 生产代码中的 LRU 实现

    我有一些 C 代码 需要使用 LRU 技术实现缓存替换 目前我知道两种实现LRU缓存替换的方法 每次访问缓存数据时使用时间戳 最后比较替换时的时间戳 使用缓存项的堆栈 如果最近访问过它们 则将它们移动到顶部 因此最后底部将包含 LRU 候选
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 获取 2 个数据集 c# 中的差异

    我正在编写一个简短的算法 它必须比较两个数据集 以便可以进一步处理两者之间的差异 我尝试通过合并这两个数据集并将结果更改放入新的数据集来实现此目标 我的方法如下所示 private DataSet ComputateDiff DataSet
  • 将代码拆分为标头/源文件

    我从 Asio 的示例页面中获取了以下代码 class tcp connection public boost enable shared from this
  • 耐用功能是否适合大量活动?

    我有一个场景 需要计算 500k 活动 都是小算盘 由于限制 我只能同时计算 30 个 想象一下下面的简单示例 FunctionName Crawl public static async Task
  • strcmp 给出分段错误[重复]

    这个问题在这里已经有答案了 这是我的代码给出分段错误 include
  • 剪贴板在 .NET 3.5 和 4 中的行为有所不同,但为什么呢?

    我们最近将一个非常大的项目从 NET Framework 3 5 升级到 4 最初一切似乎都工作正常 但现在复制粘贴操作开始出现错误 我已经成功制作了一个小型的可复制应用程序 它显示了 NET 3 5 和 4 中的不同行为 我还找到了一种解
  • 转到定义:“无法导航到插入符号下的符号。”

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我今天突然开始在我的项目中遇到一个问题 单击 转到定义 会出现一个奇怪的错误 无法导航到
  • 带重定向标准流的 C# + telnet 进程立即退出

    我正在尝试用 C 做一个 脚本化 telnet 项目 有点类似于Tcl期望 http expect nist gov 我需要为其启动 telnet 进程并重定向 和处理 其 stdin stdout 流 问题是 生成的 telnet 进程在
  • 实例化 Microsoft.Office.Interop.Excel.Application 对象时出现错误:800700c1

    实例化 Microsoft Office Interop Excel Application 以从 winforms 应用程序生成 Excel 时 出现以下错误 这之前是有效的 但突然间它停止工作了 尽管代码和 Excel 版本没有变化 我
  • 使用 Crypto++ 获取 ECDSA 签名

    我必须使用 Crypto 在变量中获取 ECDSA 签名 我在启动 SignMessage 后尝试获取它 但签名为空 我怎样才能得到它 你看过 Crypto wiki 吗 上面有很多东西椭圆曲线数字签名算法 http www cryptop

随机推荐