【问题标题】:Handling A Keyboard Interrupt with Turbo C ++ 3.0使用 Turbo C++ 3.0 处理键盘中断
【发布时间】:2012-01-11 19:51:05
【问题描述】:

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

编辑:我找到了这些信息,但我不明白如何实现它:

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

【问题讨论】:

  • 您为什么使用 Turbo C++ 3.0? 20岁;从那时起,语言有了很大的改进。
  • :) 你是对的,但这是一个家庭作业:(
  • 试试 getch()。光标字符为 00 + another_byte
  • getch() ??我必须使用程序集 16 位
  • @hasan: asm?那么为什么选择 Turbo C++?您究竟应该做什么,可以使用什么,不能使用什么?

标签: c keyboard interrupt turbo-c


【解决方案1】:

文件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;
}

文件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

输出:

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.exe -f obj kbda.asm 编译带有NASM 的程序集文件。它将产生kbda.obj。在 Borland/Turbo C/C++ IDE 中创建一个项目,在其中包含 kbdc.ckbda.obj。确保代码将在小型或微型内存模型中编译(基本上,我们需要确保 SetNewIrq9Isr()RestoreOldIrq9Isr() 将被称为近函数)。编译它。

有一些注意事项。

首先,如果在SetNewIrq9Isr()RestoreOldIrq9Isr() 之间调用,getc()gets()scanf() 等函数将不起作用。他们会挂掉程序。

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

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

更新:这是 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 编译它。其余的都是一样的。

【讨论】:

  • 当然,它是 INT 9,IRQ 1,而不是 IRQ 9。但这只是名称。
【解决方案2】:

我以前也参加过类似的课程。基本上你需要做的是在系统键盘中断处理程序处理键盘中断之前捕获它。 您需要创建自己的中断处理程序,并将其绑定到键盘中断。 完成工作后,调用原始系统键盘中断处理程序。

【讨论】:

    猜你喜欢
    • 2022-12-02
    • 2015-08-22
    • 1970-01-01
    • 2014-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-25
    • 2010-11-27
    相关资源
    最近更新 更多