【问题标题】:Faster keyboard scan code detection in 8086 assembly8086 组件中更快的键盘扫描代码检测
【发布时间】: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 特定键被按下的标志,对于游戏(中断安装/恢复代码在问题代码中,我的答案只有中断处理程序,所以你必须研究一下并找出如何连接它一切都在一起)。

标签: assembly x86-16


【解决方案1】:

回答我自己的问题,通过端口 60h 提供的扫描码实际上并不算太慢,它们太快了。

在我的问题中给出的以下示例中:“每当我按下一个键,比如说‘W’键,然后很快按下另一个键,‘W’键的中断代码仍然由端口 60h 返回。 "

我认为端口 60h 仍然返回“W”的中断代码的原因是键盘控制器没有足够的时间来记录我敲击了新键的事实。

实际上,我是先按另一个键,然后确实返回了该键的 make 代码。然后,一瞬间,我的手指从“W”键上抬起,用“W”中断代码覆盖了端口 60h 返回的字节。

解决方案:仅在与已存储在 bp 中的 make 代码相对应的情况下才使用中断代码。

新的 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多