使用 Turbo C++ 3.0 处理键盘中断

2023-12-12

我有一个项目。那是一个简单的游戏,“落块”。游戏区域被视为一个网格,其大小为 20x20。屏幕顶部会有掉落的方块,底部有一个英雄,他会射击方块。游戏的目标是在方块到达底线之前将其射出。他始终坚守在底线。每当用户按下键盘的空格键时,我都会生成一个子弹,英雄用左右箭头键在底线上移动。我不知道如何使用 Turbo C++ 3.0 处理这些键盘中断。也禁止使用“dos.h”和“int 21H”。你能给我一些关于这些项目的提示吗?

编辑:我找到了此信息,但我无法理解如何实现它:

当按下键盘上的一个键时,会产生一个中断以及一个名为“make code”的扫描代码,而当释放该键时,键盘控制器会产生一个“break code”。在PC上,键盘由芯片控制并分配给端口号60h和61h。当按下键盘上的一个键时,扫描值被放入寄存器 60h 中。您可以使用以下命令获取此扫描码: 总共60小时 获取扫描码后,必须重置键盘,使用以下命令对芯片的命令寄存器 61h 进行编程: 在al,61h 或等,82小时 61 小时后 和 al,7fh 61 小时后 在每个中断服务例程结束时,您可以清除 PIC 服务位,发送中断结束 (EOI) 命令 20h 到地址 20h 处的 PIC 端口。 移动20小时 20 小时后


File kbdc.c:

#include <stdio.h>

extern void SetNewIrq9Isr(void);
extern void RestoreOldIrq9Isr(void);

#define SCAN_BUF_SIZE 1024

extern volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
extern volatile unsigned ScanReadIdx;
extern volatile unsigned ScanWriteIdx;

const char ScanToChar[] =
  "??1234567890-=??"
  "QWERTYUIOP[]??AS"
  "DFGHJKL;\"`?\\ZXCV"
  "BNM,./??? ";


int IsScanCodeAvailable(void)
{
  return ((ScanWriteIdx - ScanReadIdx) & (SCAN_BUF_SIZE - 1)) != 0;
}

unsigned char GetScanCode(void)
{
  unsigned char code;

  while (!IsScanCodeAvailable());

  code = ScanBuf[ScanReadIdx];

  ScanReadIdx++;
  ScanReadIdx &= SCAN_BUF_SIZE - 1;

  return code;
}

int main(void)
{
  SetNewIrq9Isr();

  printf("Press keys to see scan codes.\nPress ESC to exit.\n");

  for (;;)
  {
    unsigned code, symbol;

    code = GetScanCode();

    symbol = code & 0x7F;
    symbol = (symbol < sizeof(ScanToChar)) ? ScanToChar[symbol] : '?';

    printf("scan code: 0x%02X, symbol: \"%c\"\n", code, (char)symbol);

    if (code == 1)
    {
      break;
    }
  }

  RestoreOldIrq9Isr();
  return 0;
}

File kbda.asm:

GLOBAL _SetNewIrq9Isr, _RestoreOldIrq9Isr
GLOBAL _ScanBuf, _ScanReadIdx, _ScanWriteIdx

SEGMENT _TEXT PUBLIC CLASS=CODE USE16

; void SetNewIrq9Isr(void);
_SetNewIrq9Isr:
        push    bx
        push    es

        mov     bx, 9 * 4
        mov     ax, 0
        mov     es, ax

        cli

        mov     ax, [es:bx]
        mov     [_pOldIrq9Isr], ax
        mov     word [es:bx], _NewIrq9Isr

        mov     ax, [es:bx + 2]
        mov     [_pOldIrq9Isr + 2], ax
        mov     [es:bx + 2], cs

        sti

        pop     es
        pop     bx
        ret

; void RestoreOldIrq9Isr(void);
_RestoreOldIrq9Isr:
        push    bx
        push    es

        mov     bx, 9 * 4
        mov     ax, 0
        mov     es, ax

        cli

        mov     ax, [_pOldIrq9Isr]
        mov     [es:bx], ax

        mov     ax, [_pOldIrq9Isr + 2]
        mov     [es:bx + 2], ax

        sti

        pop     es
        pop     bx
        ret

_NewIrq9Isr:
        pusha
        push    ds

        mov     ax, _DATA
        mov     ds, ax

        in      al, 60h
        push    ax

        in      al, 061h
        mov     ah, al
        or      al, 080h
        out     061h, al
        mov     al, ah
        out     061h, al

        pop     ax

        ; ScanBuf[ScanWriteIdx] = scan code;
        ; ScanWriteIdx = (ScanWriteIdx + 1) & (SCAN_BUF_SIZE - 1);
        mov     bx, [_ScanWriteIdx]
        mov     [_ScanBuf + bx], al
        inc     bx
        and     bx, 1023
        mov     [_ScanWriteIdx], bx

        mov     al, 20h
        out     20h, al

        pop     ds
        popa
        iret

SEGMENT _DATA PUBLIC CLASS=DATA

_pOldIrq9Isr      resd    1

; #define SCAN_BUF_SIZE 1024
; volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
; volatile unsigned ScanReadIdx = 0;
; volatile unsigned ScanWriteIdx = 0;
_ScanBuf          resb    1024
_ScanReadIdx      dw      0
_ScanWriteIdx     dw      0

Output:

Press keys to see scan codes.
Press ESC to exit.
scan code: 0x10, symbol: "Q"
scan code: 0x90, symbol: "Q"
scan code: 0x11, symbol: "W"
scan code: 0x91, symbol: "W"
scan code: 0x12, symbol: "E"
scan code: 0x92, symbol: "E"
scan code: 0x02, symbol: "1"
scan code: 0x82, symbol: "1"
scan code: 0x03, symbol: "2"
scan code: 0x83, symbol: "2"
scan code: 0x04, symbol: "3"
scan code: 0x84, symbol: "3"
scan code: 0x01, symbol: "?"

现在,谈谈如何编译它。

编译汇编文件NASM using nasm.exe -f obj kbda.asm。它会产生kbda.obj。在Borland/Turbo C/C++ IDE中创建一个项目,并包含在其中kbdc.c and kbda.obj。确保代码将在小型或微型内存模型中编译(基本上,我们需要确保SetNewIrq9Isr() and RestoreOldIrq9Isr()将被称为近函数)。编译它。

有一些注意事项。

首先,没有一个getc(), gets(), scanf()等函数如果在之间调用将起作用SetNewIrq9Isr() and RestoreOldIrq9Isr()。他们将挂起该程序。

其次,代码不跟踪shift, control and alt键。这对您来说意味着,如果您通过按ctrl+F9,当程序完成时,IDE很可能会认为ctrl仍被压制。要“解锁”键盘,您必须按下并释放ctrl。如果在该程序启动时按住其他类似的键,则同样的情况也可能适用。您可以添加额外的代码来等待所有shift, control and alt被释放。相信你可以在BIOS数据区找到它们当前的状态。

当然,您可以将汇编文件从 NASM 语法转换为 TASM 语法,并使用 TASM 进行编译。我只是使用免费工具 Turbo C++ 1.01 和 NASM。

UPDATE:这是 TASM 的 asm 文件:

PUBLIC _SetNewIrq9Isr, _RestoreOldIrq9Isr
PUBLIC _ScanBuf, _ScanReadIdx, _ScanWriteIdx

        .386

_TEXT SEGMENT PUBLIC 'CODE' USE16
        ASSUME CS:_TEXT, DS:_DATA

; void SetNewIrq9Isr(void);
_SetNewIrq9Isr PROC NEAR
        push    bx
        push    es

        mov     bx, 9 * 4
        mov     ax, 0
        mov     es, ax

        cli

        mov     ax, es:[bx]
        mov     _pOldIrq9IsrOfs, ax
        mov     word ptr es:[bx], offset _NewIrq9Isr

        mov     ax, es:[bx + 2]
        mov     _pOldIrq9IsrSeg, ax
        mov     es:[bx + 2], cs

        sti

        pop     es
        pop     bx
        ret
_SetNewIrq9Isr ENDP

; void RestoreOldIrq9Isr(void);
_RestoreOldIrq9Isr PROC NEAR
        push    bx
        push    es

        mov     bx, 9 * 4
        mov     ax, 0
        mov     es, ax

        cli

        mov     ax, _pOldIrq9IsrOfs
        mov     es:[bx], ax

        mov     ax, _pOldIrq9IsrSeg
        mov     es:[bx + 2], ax

        sti

        pop     es
        pop     bx
        ret
_RestoreOldIrq9Isr ENDP

_NewIrq9Isr PROC NEAR
        pusha
        push    ds

        mov     ax, _DATA
        mov     ds, ax

        in      al, 60h
        push    ax

        in      al, 061h
        mov     ah, al
        or      al, 080h
        out     061h, al
        mov     al, ah
        out     061h, al

        pop     ax

        ; ScanBuf[ScanWriteIdx] = scan code;
        ; ScanWriteIdx = (ScanWriteIdx + 1) & (SCAN_BUF_SIZE - 1);
        mov     bx, _ScanWriteIdx
        mov     _ScanBuf[bx], al
        inc     bx
        and     bx, 1023
        mov     _ScanWriteIdx, bx

        mov     al, 20h
        out     20h, al

        pop     ds
        popa
        iret
_NewIrq9Isr ENDP

_TEXT ENDS

_DATA SEGMENT PUBLIC 'DATA' USE16

_pOldIrq9IsrOfs   dw      ?
_pOldIrq9IsrSeg   dw      ?

; #define SCAN_BUF_SIZE 1024
; volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
; volatile unsigned ScanReadIdx = 0;
; volatile unsigned ScanWriteIdx = 0;
_ScanBuf          db      1024 dup (?)
_ScanReadIdx      dw      0
_ScanWriteIdx     dw      0

_DATA ENDS

END

你使用编译它tasm.exe /ml kbda.asm。其余的都是一样的。

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

使用 Turbo C++ 3.0 处理键盘中断 的相关文章

随机推荐

  • Python——加速导入?

    我有 10000 个自定义 编译为 so 模块 我想在其中使用python 模块的使用是有后果的 模块是一个接一个地使用的 而不是同时使用的 通常 代码看起来像这样 list with all the paths to all module
  • load() 顺序执行,而不是与 jQuery 同时执行

    我正在开发一个网站 我想从导航栏中获取所有链接 并将链接页面中的 div 加载到一个大容器中 到目前为止我所拥有的是以下内容 nav a each function index var to load this attr href slid
  • 不要在循环内创建函数。 - jslint 错误

    我收到此 jslint 错误 不要在循环内创建函数 我无法更改导致此问题的 javascript 但是由于修改它的限制 我无法更改 因此 我想关闭此验证以检查特定 javascript 文件中是否存在此错误 对于这个js错误可以这样做吗 不
  • 如何重现特定的友好URL?

    我们正在尝试将 JSP XSLT 应用程序重构为 JSF 我们使用 CMS 进行内容管理 技术 使用 Mojarra PrimeFaces 2 2 1 和 Tomcat 6 29 的 JSF 2 0 数据结构的组织如下 有网站 站点包含频道
  • Node.js 中的延迟加载

    我想知道是否使用require 在node js中相当于延迟加载 例如 如果我有一个函数需要特定的 node js 包 而我的代码中其他任何地方都不需要这个包 那么我最好使用require 仅在调用该函数时才包含所需的包 由于我对 Node
  • Laravel 5.3 身份验证后重定向的自定义路径

    我有两种类型的用户 供应商 和 客户 我目前正在使用Laravel s built in Auth Controllers 来自make auth命令 来进行我的客户端用户身份验证 由于我有两种用户 所以我改变了 redirectTo财产在
  • Flutter post api发送具有相同名称参数的多个值

    如何在flutter中的post api中使用相同名称参数发送多个数据 在php中 我们使用parameter 然后我们可以发送多个数据 但我对flutter很陌生 所以我想知道它是如何完成的 谢谢 示例图片 https i stack i
  • 删除 gtk.button 的边框

    我想删除 gtk button 的边框 但我不知道该怎么做 我尝试过 button gtk Button button set style inner border 0 但我有一个错误 该属性不存在 我也尝试创建一个新的 gtk Style
  • 捕获 SYCL 异常:错误:[ComputeCpp:RT0101] 无法创建内核((内核名称:SYCL_class_multiply))

    我克隆了https github com codeplaysoftware computecpp sdk git并修改了computecpp sdk samples accessors accessors cpp file 我刚刚添加了st
  • 如何将android触摸坐标转换为OpenCV图像坐标?

    我正在尝试做类似的事情 但在 Android 上 http docs opencv org doc tutorials imgproc imgtrans warp affine warp affine html我有一个绘制到 ImageVi
  • HTML 5 文件系统访问类型错误

    我正在开发一个网络应用程序 并尝试使用文件系统 API 访问目录 在根据规范访问目录之前 我需要向用户请求配额 我应该做这样的事情 navigator webkitPersistentStorage requestQuota PERSIST
  • C 函数中的全局变量和返回多个可变长度数组(指针)

    我有一些编程经验 但 C 语言不多 我有一个相当大的 C 文件 其中有多个按顺序执行的函数 因此在这种特殊情况下 实际上没有函数被调用两次 为了便于阅读 它们被分解 因为每个函数仍然有单独的目的 该程序对几个可变长度的 double 长数组
  • 如何使用 Windows 7 中的自动运行从闪存驱动器打开网页?

    我一直在尝试想出一种方法来自动运行一系列托管在 USB 闪存驱动器上的 HTML 页面 这些都是静态页面 并且都链接到我的闪存驱动器根目录中的index html 我尝试设置一个 autorun inf 文件 如下所示 autorun op
  • 将 require 与相对路径结合使用

    我们对 Protractor 有一组相当大的端到端测试 我们遵循页面对象模式 这有助于我们保持测试的干净和模块化 我们还有一组辅助函数可以帮助我们遵循干原则 问题 单个规范可能需要多个页面对象和辅助模块 例如 use strict var
  • 如何从 Process.GetCurrentProcess().Threads 获取托管线程

    我可以从 Process GetCurrentProcess Threads 获取正在运行的线程列表 但我需要知道以 Thread Start 启动的线程的托管名称 但它不是 ProcessThread 对象的属性 有没有办法从 Proce
  • 为什么这段代码的JTextArea占据了整个JFrame?

    我希望框架的一部分包含 JTextArea 但它完全占据了 我无法在这里追踪错误 import java awt import javax swing public class EchoServer public static void m
  • 在空手道中从 JSON 响应中的数组获取最大值

    我有以下 Json 作为 API 调用的响应 location name London region City of London Greater London country United Kingdom lat 51 52 lon 0
  • contentEditable 元素中的拖放事件

    当将某些内容放入 contentEditable 元素中 拖动后 时会触发什么事件 我说的是普通的旧式拖放 而不是 HTML5 拖放 其中任何元素都可以拖动 用例很简单 页面上有一个 contentEditable div 用作编辑器 用户
  • Tensorflow 2.0 - AttributeError:模块“tensorflow”没有属性“Session”

    当我执行命令时sess tf Session 在 Tensorflow 2 0 环境中 我收到如下错误消息 Traceback most recent call last File
  • 使用 Turbo C++ 3.0 处理键盘中断

    我有一个项目 那是一个简单的游戏 落块 游戏区域被视为一个网格 其大小为 20x20 屏幕顶部会有掉落的方块 底部有一个英雄 他会射击方块 游戏的目标是在方块到达底线之前将其射出 他始终坚守在底线 每当用户按下键盘的空格键时 我都会生成一个