8086 汇编中更快的键盘扫描代码检测
Faster keyboard scan code detection in 8086 assembly
是否有可能比仅从硬件端口 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
为了回答我自己的问题,通过端口 60h 提供的扫描码实际上并没有太慢,而是太快了。
在我的问题中给出的以下示例中:"Whenever I press a key, let's say the 'W' key, then very quickly press another key, the break code for the 'W' key is still returned by port 60h."
我认为端口 60h 仍然返回 'W' 的中断代码的原因是键盘控制器没有足够的时间来记录我按下了一个新键的事实。
其实我是先按了另一个键,确实返回了那个键的make code。然后,一瞬间后,我的手指从 'W' 键上移开,用 'W' 中断代码覆盖端口 60h 返回的字节。
解决方案:只有与已存储在 bp 中的 make 代码相对应时才接受 break 代码。
新 HANDLE_INPUT 程序,现在运行得更好:
HANDLE_INPUT PROC
;CLEARS THE KEYBOARD TYPEHEAD BUFFER AND COLLECTS A SCANCODE
;ALTERS BP
push ax
push bx
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
test al, 80h ;Is a break code in al?
jz ACCEPT_KEY ;If not, accept it.
;If so, check to see if it's the break code
;that corresponds with the make code in bp.
mov bx, bp ;bx -> make code
or bl, 80h ;change make code into it's break code
cmp bl, al ;Do the new and old break codes match?
je ACCEPT_KEY ;If so, accept the break code.
pop es ;If not, bp retains old make code.
pop bx
pop ax
ret
ACCEPT_KEY:
mov bp, ax ;bp -> scancode, accessible globally
pop es
pop bx
pop ax
ret
HANDLE_INPUT ENDP
是否有可能比仅从硬件端口 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
为了回答我自己的问题,通过端口 60h 提供的扫描码实际上并没有太慢,而是太快了。
在我的问题中给出的以下示例中:"Whenever I press a key, let's say the 'W' key, then very quickly press another key, the break code for the 'W' key is still returned by port 60h."
我认为端口 60h 仍然返回 'W' 的中断代码的原因是键盘控制器没有足够的时间来记录我按下了一个新键的事实。
其实我是先按了另一个键,确实返回了那个键的make code。然后,一瞬间后,我的手指从 'W' 键上移开,用 'W' 中断代码覆盖端口 60h 返回的字节。
解决方案:只有与已存储在 bp 中的 make 代码相对应时才接受 break 代码。
新 HANDLE_INPUT 程序,现在运行得更好:
HANDLE_INPUT PROC
;CLEARS THE KEYBOARD TYPEHEAD BUFFER AND COLLECTS A SCANCODE
;ALTERS BP
push ax
push bx
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
test al, 80h ;Is a break code in al?
jz ACCEPT_KEY ;If not, accept it.
;If so, check to see if it's the break code
;that corresponds with the make code in bp.
mov bx, bp ;bx -> make code
or bl, 80h ;change make code into it's break code
cmp bl, al ;Do the new and old break codes match?
je ACCEPT_KEY ;If so, accept the break code.
pop es ;If not, bp retains old make code.
pop bx
pop ax
ret
ACCEPT_KEY:
mov bp, ax ;bp -> scancode, accessible globally
pop es
pop bx
pop ax
ret
HANDLE_INPUT ENDP