1

我正在尝试通过 DOS 中的汇编程序 x86 学习一些深度编程,因为它启用了真实地址模式。但是在我尝试这样做时,我试图制作一个程序来打印用户是否按下了其中一个控制键;CTRL、CAPS LOCK 或 SCROLL LOCK,但问题是程序不打印出来。感觉好像缺乏某种基础知识,所以我问这里是否有人知道我的程序有什么问题。如果我按 q (如退出),它不会写出任何内容但能够关闭?..谢谢

;reads the key and prints out whether a control key has been entered 
; (CTRL, CAPS LOCK or SCROLL LOCK)

[BITS 16]

SEGMENT data
ctrlmsg           db    'ctrl has been pressed', '$'
capslockmsg       db    'caps lock has been pressed', '$'
scrollmsg         db    'scroll lock has been pressed', '$'

SEGMENT code
..start:
mov ax, pile
mov ss, ax
mov ax, topofstack
mov sp, ax
mov ax, data
mov ds, ax
mov ax, ctrlmsg

WAITER:
    mov ah, 00h
    int 16h
    cmp al, 71h          ; user pressed q, and wants to end program
    je END
    mov ah, 02h          ; wait until user press keyboard and return keyboard flag in AL
    int 16h
    add al, 71h          ; add 71h back to al, so it goes unchanged in other comparisons
    cmp al, 02h          ; if key board flag is 02h then I expect user to have pressed CTRL
    je CTRL              ; then jump to CTRL label, in the same way it goes...
    add al, 02h
    cmp al, 04h
    je SCROLLOCK
    add al, 04h
    cmp al, 06h
    je CAPSLOCK
    jmp WAITER

END:
    mov ax, 04c00h        ; ends program
    int 21h

WRITESTRING:
    mov ah, 13h           ; 13h of int 10 is print string
    mov al, 00h           ; write mode 0 (no attributes)
    mov bh, 0h            ; video page number
    mov bl, 07h           ; foreground color
    mov cx, 05h           ; string length
    mov dh, 25            ; row
    mov dl, 80            ; col
    int 10h
    ret

CTRL:                     ; the other labels CAPS LOCK and SCROLL LOCK are quite similar why I haven't included them in the codesnippet
    push ds               ; save ds for subroutine 
    pop es                ; pop it in es
    push bp
    move bp, ctrlmsg      ; base pointer point to string
    call WRITESTRING
    pop bs
    jmp waiter            ; loop
4

2 回答 2

1

我对 BIOS 调用的确切行为的记忆int 16h是模糊的,但我认为自己按下的切换(capslock/numlock/scrolllock)或修饰符(shift/ctrl/alt)键根本不会从int 16h. 一旦用户按下非修改键,这些键用于修改实际返回的内容

要获得实际的击键,您可能需要为 IRQ 9 编写 ISR(我很确定这是键盘硬件中断的编号)。那时,您可以获得键盘上每个键的原始“make”和“break”扫描码,并采取任何您想要的操作。(当然,在 ISR 中,该操作可能应该仅限于将按键信息存储在缓冲区中,以便以后在 ISR 之外进行处理。)

另外,我注意到您在程序开始时进行设置sssp但 DOS 会自动为您设置堆栈。通常,您根本不必这样做。但是,如果您这样做,则应始终在修改寄存器期间使用cli和禁用中断。如果在您已更改但尚未sti更改时发生中断,则 ISR 将覆盖您可能不打算的内容。sssp

于 2012-09-25T00:12:13.500 回答
1

格雷格是对的。您不必加载 ss:sp。我知道它在手册中的示例中显示了这一点,但是如果您的堆栈声明正确,dos 会为您执行此操作。你没有表现出来。应该是这样的:

segment pile stack ; the "stack" is important!
    resw 100h ; should be enough for anybody

你必须像你一样加载 ds 。我也会在这个时候做 es,而不是把它留到“CTRL:”。您的“Writestring”取决于它。(奇怪的中断,10h/13h!)

int 16h, ah=0 对于旧的 83 键键盘是正确的。它会“几乎”获得所有击键,但会漏掉一些。我已经把自己搞糊涂了!(很久以前,但我还记得!) ah=10h 用于现代键盘。无论哪种方式,您检查“q”并退出。到目前为止,一切都很好!

然后你会得到带有 int 16h/2 的标志。之后你的逻辑让我无法理解!您要在键盘标志中添加 71h 吗?然后将其与2进行比较?我不认为你的任何条件跳转都被采取了。这可以解释为什么什么都没有打印出来!:)(您还打印到第 25 行第 80 列,这看起来很奇怪)

我认为您想使用...(等待它)“测试”指令来测试标志。由于这些条件中的一个以上可能是真的,也许像......

    test al, 4 ; bit 2 - control key
    jnz notctrl
    call print_control
notctrl:
    test al, 16 ; bit 4 - scrollock active
    jnz not_scrl
    call print_scroll
notscrl:
    test al, 64 ; bit 6 - capslock active
; etc...

显然,您必须在子例程中保留 al 。Greg 也是对的,如果您正在为键盘编写自己的 int 9 (irq 1) 处理程序,您应该将此信息保存在缓冲区中。现有的处理程序正是这样做的。在 Bios 数据区的 40h 段...我忘记了偏移量...您会找到这些标志。这就是 int 16h/2 找到它们的地方,我很确定。您可以自己在那里访问它们,但 BIOS 中断可能更容易(也许可以作为练习尝试?)。

最后但同样重要的是,wtf 是“pop bs”?BS 不是段寄存器。Nasm不会接受任何BS!CPU不会接受任何BS!:)

最好的,弗兰克

于 2012-09-25T08:31:42.887 回答