我在编写空气曲棍球的过程中遇到了一个问题,就是我一次得到两个键/点击的原因,因为我需要同时点击两次才能同时移动两个玩家我尝试了很多,但我不认为我有办法做到这一点。 我听说我需要从缓冲区目录中读取并查看哪些键并单独阅读每个键但我不知道该怎么做。
答案 0 :(得分:5)
你介意使用扫描码吗?
我知道这不是您正在寻找的简单,简单的解决方案,但我担心没有。 所以我写这篇文章是希望,如果不是你,其他一些有问题的编码人员可以找到有用的东西。
我也知道你为TASM写的但是我忘了并且从NASM开始,转换然而应该非常简单(只需添加段声明,从括号中取出段寄存器并添加 PTR )
从硬件角度看,键盘只重复最后按下的键 1 ,所以如果两个玩家按下两个键,实际上只有一个按键发送键盘。
但是,软件会在任何地方同时处理多个密钥,他们是如何做到的?
诀窍是按下按键时键盘发送两个代码(扫描码):当按键向下时,键盘发出一个>许多其他的,当它被保持下来)和一个被释放 因此,软件可以在没有键盘 2 的其他通知的情况下判断键何时关闭。
我找不到任何处理按键上下事件的interrupt service。
唯一的解决方案是直接处理扫描码。
虽然8042 chip非常简单,但IO指令不需要弄脏
在没有解释8259A和IRQ映射如何工作的情况下,足以说明当按下/释放某个键时,15h/AH=4fh
中的扫描码会调用中断AL
。 < / p>
我们可以拦截该中断并检查扫描码是用于按键还是按键
发布扫描码设置 bit7
我们可以有一个128字节的数组,每个数组用于任何可能的扫描码值( bit0-6 ) 3 ,如果扫描码表示a,则存储在每个元素0ffh
中如果它指示发布,请按或00h
。
保持已处理扫描码的计数也很有用,因此程序可以通过简单的算术等待新的扫描码。
现在,我想你仍然感到困惑 我写了一个演示。
以下程序,对于NASM,等待您按 和 d 键退出。
警告由于我们使用的是扫描码,因此这取决于键盘布局!
BITS 16
ORG 100h ;COM
;Setup ISR for the scancode
call init
;Clear screen
mov ax, 03h
int 10h
;Print command
mov ah, 09h
mov dx, strCommand
int 21h
_main:
;Wait for a change in the scancode tables
call wait_for_scancode
;Remove unused keystrokes
call remove_keystrokes
;Check if a is pressed
mov al, 1eh ;a
call is_scancode_pressed
jz _main
;Check if 'd' is pressed
mov al, 20h ;d
call is_scancode_pressed
jz _main
;Both are pressed, print bye and ...
mov ah, 09h
mov dx, strDone
int 21h
;... restore the ISR and ...
call dispose
;... exit
mov ax, 4c00h
int 21h
strCommand db "Press 'a' and 'd' to exit", 13, 10, 24h
strDone db "Bye",13,10,13,10,24h
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll L
; Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll
;Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll Ll L
;S C A N C O D E F U N C T I O N S
;Set the ISR
init:
push ax
mov ax, cs
mov WORD [old_isr_15 + 02h], ax
;old_isr_15 is now a far pointer to new_isr_15
call swap_isr_15 ;Swap the current isr15 with the one in old_isr_15
pop ax
ret
;Restore the original ISR
dispose:
call swap_isr_15 ;Swap the current isr15 with the one in old_isr_15
ret
;Swap the pointer in the IVT for int 15h with the pointer in old_isr_15
swap_isr_15:
push eax
push es
xor ax, ax
mov es, ax
cli
mov eax, DWORD [es: 15h*4]
xchg eax, DWORD [old_isr_15]
mov DWORD [es: 15h*4], eax
sti
pop es
pop eax
ret
;Wait for a change in the scancode table
wait_for_scancode:
cli ;Prevent the ISR from messing things up
;At least one scancode processed?
cmp WORD [new_scancode], 0
jne _wfs_found ;Yes
;No, restore interrupt so the CPU can process the prending ones
sti
jmp wait_for_scancode
;New scancode, decrement the count and restore interrupts
_wfs_found:
dec WORD [new_scancode]
sti
ret
;THe BIOS is still saving keystrokes, we need to remove them or they
;will fill the buffer up (should not be a big deal in theory).
remove_keystrokes:
push ax
;Check if there are keystrokes to read.
;Release scancodes don't generate keystrokes
_rk_try:
mov ah, 01h
int 16h
jz _rk_end ;No keystrokes present, done
;Some keystroke present, read it (won't block)
xor ah, ah
int 16h
jmp _rk_try
_rk_end:
pop ax
ret
;Tell if a scancode is pressed
;
;al = scancode
;ZF clear is pressed
is_scancode_pressed:
push bx
movzx bx, al
cmp BYTE [scancode_status + bx], 0
pop bx
ret
;AL = scancode
new_isr_15:
;Check for right function
cmp ah, 4fh
jne _ni15_legacy
;Save used regs
push bx
push ax
movzx bx, al ;BX = scancode
and bl, 7fh ;BX = scancode value
sar al, 07h ;AL = 0ffh if scancode has bit7 set (release), 00h otherwise
not al ;AL = 00h if scancode has bit7 set (release), 0ffh otherwise
;Save the scancode status
mov BYTE [cs:bx + scancode_status], al
;Increment the count
inc WORD [cs:new_scancode]
pop ax
pop bx
_ni15_legacy:
;This is a far jump, in NASM is simply jmp FAR [cs:old_isr_15]
;Ended up this way for debug
push WORD [cs: old_isr_15 + 02h]
push WORD [cs: old_isr_15]
retf
;Original ISR
old_isr_15 dw new_isr_15, 0
;Scan code status table
scancode_status TIMES 128 db 0
;Scan code count
new_scancode dw 0
您需要使用的是:
init
设置扫描码侦听。dispose
拆除扫描码侦听。is_scancode_pressed
知道某个键是否被按下。其他所有内容,包括wait_for_scancode
和remove_keystrokes
都是附件,只是为了让您的程序表现得非常好。
如果要查找与某个键相关联的扫描码,可以使用this other program显示一个包含所按扫描码的表格(按 ESC 退出)。
例如,如果我按 a 和 d ,我会获得演示中使用的值
这只是上述演示的变体。
1 我创建this启动程序(用于NASM)来测试我的假设,至少在我的硬件中。
2 实际上,重复功能仅在键入软件时有用,其他每个应用程序(如游戏)明确检查键状态都不需要它。
3 实际上有扩展(多字节扫描码)但这里忽略它们。坚持使用普通键!