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

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

编辑:我发现了这些信息,但我无法理解如何实现它:

当在键盘上按下键时,产生中断以及名为“生成代码”的扫描代码,并且当释放该键时,键盘控制器产生“中断代码”。 在PC上,键盘由芯片控制并分配给端口号60h和61h。 在键盘上按下键时,扫描值将在60h时进行注册。 您可以使用以下命令获取此扫描代码:在al,60h获取扫描代码后,您必须使用以下命令重置键盘编程61h芯片的命令寄存器:al,61h或al,82h out 61h ,al和al,7fh out 61h,al在每个中断服务程序结束时,清除PIC服务位,发送中断结束(EOI)命令,20h到地址20h的PIC端口。 mov al,20h out 20h,al

文件kbdc.c

#include  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()将被称为near函数)。 编译它。

有几点需要注意。

首先,如果在SetNewIrq9Isr()RestoreOldIrq9Isr()之间调用,则getc()gets()scanf()等函数都不起作用。 他们将挂起该计划。

其次,代码不跟踪shiftcontrolalt键。 对你来说意味着,如果你通过按ctrl+F9从IDE中运行这个程序,当程序完成时,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编译它。 其余的都是一样的。

在这些日子里,我也采取了类似的做法。 基本上你需要做的是在系统键盘中断处理程序处理之前捕获键盘中断。 您需要创建自己的中断处理程序,并将其绑定到键盘中断。 完成工作后,调用原始系统键盘中断处理程序。