【问题标题】:8086 assembler,INT 16,28086 汇编器,INT 16,2
【发布时间】:2026-02-09 20:40:01
【问题描述】:

我被这个东西卡住了,我想看看是否按下了右移按钮,所以我有这个 assambler 代码:

mov ah,2
int 16h           ;calling INT 16,2 - Read Keyboard Flags interrupt      
mov ah,10000000b
shl al,7 
and al,10000000b
cmp al,ah         ;check if first bit is 1
je rshift 
jmp final


rshift:
mov ah,9
lea dx,rsh  ;rsh is a string that says that "right shift button has been pressed"
int 21h
jmp final  

final:             ; quit program
mov ax,4c00h
int 21h

为什么它不工作?,我认为问题是 int 16,2 不能正常工作,如果是这样,为什么? 这是 INT 16,2 应该做的:

AH = 02
on return:
AL = BIOS keyboard flags (located in BIOS Data Area 40:17)

|7|6|5|4|3|2|1|0|  AL or BIOS Data Area 40:17
| | | | | | | `---- right shift key depressed
| | | | | | `----- left shift key depressed
| | | | | `------ CTRL key depressed
| | | | `------- ALT key depressed
| | | `-------- scroll-lock is active
| | `--------- num-lock is active
| `---------- caps-lock is active
`----------- insert is active

我从来没有看到这条消息,我在调试中查看了 AL 寄存器,在我调用 INT 16,2 后它似乎没有改变。我在 x86 架构上运行 Win 7,我是使用 tasm

【问题讨论】:

    标签: assembly x86-16


    【解决方案1】:

    你如何测试这个?

    出于好奇,我做了一个简单的程序(代码如下)。在 Windows 控制台下运行时,它检测到左移(Result: 2)但从未检测到右移(预期为Result: 1,但只检测到Result: 0)。

    在纯 DOS 下运行时(在 VMWare 中),它正确显示所有组合(0 到 3)。

    所以它似乎是 NTVDM(Windows DOS 仿真)的产物,虽然我没有任何来源可以引用。

    我的代码:

    .model small
    
    .code
    
    start:
        mov ax, seg msg
        mov ds, ax
    
        mov ah, 2
        int 16h
    
        and al,3        ; get two lower bits - both SHIFTs
        add digit, al   ; convert to decimal
    
        lea dx, msg
        mov ah, 9
        int 21h
    
        mov ax, 4c00h
        int 21h
    
    .data
    
    msg     db  'Result: '
    digit   db  '0'
            db  13,10,'$',0
    
    .stack
            db  16384 dup(?)
    
    end start
    

    【讨论】:

    • 这应该如何工作?我是否必须在运行程序之前按 shift,我已经对其进行了测试,并且始终显示结果为零。我正在运行 Win 7 x86,并且我已经用 tasm 测试它
    • 是的,您必须事先按 shift(s),因为代码中没有延迟(尽管添加延迟或将此代码包装到循环中并不难)。在 Windows 下,您也可以这样做:ping -n 5 localhost && test.exe(假设 test.exe 是上面的程序),并有 5 秒钟的时间按下(并按住)所需的键。
    • 我已经开始考虑循环了...在 Windows 命令行中运行以下命令,您将能够“交互式”地看到结果变化 :) for /L %I in (1,1,10000) do ( ping -n 2 localhost > nul & test.exe )(对不起,我不知道'目前我附近没有 Windows,所以上面的行未经测试)
    【解决方案2】:

    您应该可以通过以下方式清理您的支票:

    test al,1
    jnz rshift
    

    【讨论】:

    • 我不应该在 al 中移动位吗?
    • 我知道了,如果我要检查第二位,那么我需要右移一位,对吗?
    • @John 不,在这种情况下使用test al, 2
    • 没有理由移动,您传递给测试的值充当一个和掩码,这实际上意味着只检查位 0。它有点像在 C 中说如果(flag & 1) == 1 与说(flag & 1) != 0,即,您用某些东西对其进行掩码,并且您正在查看该位是否“通过掩码显示”,而不是担心哪个位正在通过掩码显示(鉴于掩码中只有一位,因此只能有一个位) .希望这能解释清楚。
    • @Pascal:你的意思是说 10b 而不是 2,不是吗:P
    【解决方案3】:

    我无法解释为什么您的代码不起作用,但您可以替换

    mov ah,10000000b
    shl al,7 
    and al,10000000b
    cmp al,ah         ;check if first bit is 1
    

    通过

    test al, 1
    

    如果在汇编中有成语这样的东西,它会做同样的事情并且更惯用。

    编辑: 正如迈克尔在下面的评论中指出的那样,如果您使用 test 指令,则需要反转条件跳转。当且仅al AND C 的按位与为零时,test al, C 设置 ZF。

    【讨论】:

    • 正如鲁本的回答间接暗示的那样,如果您使用测试指令,则需要 jnz 而不是 je(如果掩码中的任何位匹配,则 ZF=1,je 测试 ZF=0 )。
    • 现在你把我弄糊涂了。我认为你是对的,有必要反转条件跳转,但我认为你评论的括号部分都是向后的。如果所有位都不匹配,则 ZF=1,je 测试 ZF=1,对吗?
    • 呃,是的,当然 - 我的错,当你在漫长的一天后熬夜太晚时会发生这种情况。