【发布时间】:2017-12-13 05:36:08
【问题描述】:
是否可以比仅从硬件端口 60h 读取更快地检测和收集键盘品牌和刹车?
每当我按下一个键,假设是“W”键,然后很快按下另一个键,“W”键的中断代码仍然由端口 60h 返回。
在我正在编写的游戏中,当用户试图快速改变方向时,这具有将玩家精灵锁定在适当位置的效果。
我曾尝试使用 int 16h 函数 01h 和 int 16h 函数 00,但与端口 60h 相比,它非常不稳定且缓慢。
这是我使用端口 60h 的输入代码。我只是将扫描码传递给 bp。我所有需要用户输入的程序都会检查 bp 中的扫描码。
HANDLE_INPUT PROC
;CLEARS THE KEYBOARD TYPEHEAD BUFFER AND COLLECTS A SCANCODE
;ALTERS BP
push ax
push es
mov ax, 40h
mov es, ax ;access keyboard data area via segment 40h
mov WORD PTR es:[1ah], 1eh ;set the kbd buff head to start of buff
mov WORD PTR es:[1ch], 1eh ;set the kbd buff tail to same as buff head
;the keyboard typehead buffer is now cleared
xor ah, ah
in al, 60h ;al -> scancode
mov bp, ax ;bp -> scancode, accessible globally
pop es
pop ax
ret
HANDLE_INPUT ENDP
这是使用 int 16h 的替代版本,它的工作方式几乎不如使用端口 60h 的上述版本。
HANDLE_INPUT PROC
;COLLECTS A SCANCODE
;ALTERS BP
push ax
xor bp, bp ;clear out bp
mov ah, 1 ;Function 1, check key status.
int 16h ;Is a key ready?
jz NO_KEY ;If zf cleared, then no.
xor ah, ah ;Otherwise, a key is waiting.
int 16h ;ah -> scancode
xor al, al
xchg al, ah ;ax -> scancode
mov bp, ax ;bp -> scancode, accessible globally
NO_KEY:
pop ax
ret
HANDLE_INPUT ENDP
【问题讨论】:
-
如果轮询端口 0x60,您应该等到状态寄存器的位 0 为 1,然后再尝试检索事件。如果更换键盘中断,请注意这可能会导致依赖键盘数据的 DOS 功能出现问题(可能不是您的代码的问题)。使用中断的好处是您不需要等待状态寄存器位 0 变为 1(只需记住发送中断结束)到主 PIC(发送 0x20 到端口 0x20)。
-
轮询的问题是您正在与 IRQ 1 ISR 竞争。通常,int 16h 对于任何游戏都足够好,但它当然有一些延迟。请注意,如果您想编写尽可能快的 ISR,您最终会得到一个循环缓冲区解决方案,这正是 BIOS 已经在做的事情。或者,您可以在 ISR 中处理每个扫描代码,但这会使其非常变慢。如果您在完成扫描代码处理之前返回 EOI,那么您必须决定如果扫描代码在处理例程中间到达时该怎么办。
-
屏蔽 IRQ1(或在 8042 级别禁用它)并正确使用轮询可能是更简单的解决方案,如果您负担得起 busy wait loop。
-
虽然你写了你对创建完全中断不感兴趣,但我在之前对问题的回答中有一个例子:stackoverflow.com/a/47115465/4271923 - IMO 应该很容易适应你,因为它正在处理0/1 特定键被按下的标志,对于游戏(中断安装/恢复代码在问题代码中,我的答案只有中断处理程序,所以你必须研究一下并找出如何连接它一切都在一起)。