8086汇编键盘ISR实现

时间:2015-10-13 20:14:44

标签: assembly keyboard interrupt x86-16 isr

我无法理解为什么我为我的程序编写的键盘中断服务程序(每次按键时都应该打印“hello world”)只在我执行dosbox上的.exe时出现一次。 这是代码:

while read -r i <&3; do
    nexec -i "$i" hostname 2>>"errorlog.$i.txt" || {
      echo "nexec for $i exited with status $?" >&2
      continue
    }
    # check for case where it claimed success but actually failed
    # if nexec is written correctly, you don't need any of this logic
    # ...and can completely remove the rest of the loop.
    if grep -q -e "$i" "errorlog.$i.txt"; then
      echo "error accessing $i" >&2
    else
      echo "was able to connect to $i" >&2
    fi
done 3<serverlist

# and combine all the individual logs into one file:
cat errorlog.*.txt >errorlog.txt && rm -f -- errorlog.*.txt

我尝试了一些东西,比如推送和弹出寄存器,使用另一个中断(系统时钟08h),但没有一个工作。 我知道ISR至少运行一次,因为屏幕上出现了“hello world”消息,但是每次按下一个键都会打印,我不知道它为什么没有。

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

你必须做一些工作来免费&#34;键盘和中断。看看{{3}}。最简单的方法是跳转到自己的处理程序末尾的旧IRQ处理程序:

NAME keyb

PILE    SEGMENT STACK
    db      20 dup ('LA PILE ')
PILE    ENDS

DONNEE SEGMENT
message db "Hello wolrd !$"
oldvec dw 0, 0
DONNEE ENDS

PROGRAMME SEGMENT
ASSUME CS:PROGRAMME , DS:DONNEE, ES:NOTHING, SS:PILE
debut:

    mov ax,DONNEE
    mov ds,ax

    cli

    xor ax,ax
    mov es, ax ; load ES with segment address of interrupt pointer table
    mov bx, 24h ; load BX with interrupt type

    ; Store the old IRQ vector
    mov ax, es:[bx]
    mov oldvec, ax
    mov ax, es:[bx+2]
    mov oldvec + 2, ax

    mov word ptr es:[bx], OFFSET SUBR ; isr address
    mov word ptr es:[bx]+2, SEG SUBR ; isr segment
    mov ax, 01h
    sti

BOUCLE: jmp BOUCLE

SUBR PROC NEAR
    push ax
    push dx

    mov ah,9
    mov dx,OFFSET message
    int 21h

    pop dx
    pop ax    

    jmp dword ptr [oldvec]
SUBR ENDP

PROGRAMME ENDS

END debut

答案 1 :(得分:1)

基本上,除非你在完成工作后调用默认处理程序,否则你必须告诉PIC(可编程中断控制器)你已经完成了 - 默认处理程序会为你做这件事 - 发送到PIC的EOI(中断信号结束)。 PIC不会再次触发中断,直到您告诉它您已完成当前的中断。

在32位保护模式下执行此操作的代码看起来像这样。请注意,我对所有IRQ使用通用处理程序,只需切换到适当注册的用户回调函数。我把两者都包括在内。

首先,通用IRQ处理程序

irq_handler:
    push    ebp
    mov     ebp, esp
    add     ebp, 8


    mov     eax, [ebp +registers_t.int_no]

    cmp     eax, IRQ7                   ; this just dumps spurious IRQ7 interrupts
    je      .irqHandlerDone

    cmp     eax, IRQ8                   ; if it's IRQ0 - IRQ7, the first controller fired the int, otherwise the slave controller did
    jb      .slaveResetDone
.resetSlave:
    mov     al,20H      ; send End-Of-Interrupt signal
    out     0xA0,al     ; to the 8259 _slave_ Programmable Interrupt Controller
.slaveResetDone:
.resetMaster:
    mov     al, 0x20    ; send End-Of-Interrupt signal
    out     0x20, al    ; to the 8259 master Programmable Interrupt Controller

    mov     eax, [ebp + registers_t.int_no]
    shl     eax, 2                              ; x4
    mov     esi, interrupt_handlers
    add     esi, eax                            ; esi --> interrupt_handlers[int_no]
    cmp     dword [esi], 0
    je      .irqHandlerDone

    call    [esi]
.irqHandlerDone:
    pop     ebp
    ret

接下来,IRQ1(键盘)处理程序。注册函数只是将函数的偏移量复制到32位地址的表(interrupt_handlers)中。 我处于平面内存模式(4GB可寻址,ES已经保存了可写数据段的段选择器.0xB8000 + 79 * 2只指向80x25 mode3文本屏幕右上角的字符)

; keyboard IRQ handler
irq1Handler:
    push    ebp
    mov     ebp, esp
    add     ebp, 8+4

    in      al, 0x60
    mov     bl, al
    mov     byte [port60], al

    in      al, 0x61
    mov     ah, al
    or      al, 0x80
    out     0x61, al
    xchg    ah, al
    out     0x61, al

    and     bl, 0x80
    jnz     .done

    pusha
    ;mov        al, [port60]
    ;call   outputChar

    mov     edi, 0xB8000 + 79*2
    mov     al, [port60]
    mov     [es:edi], al

    popa
.done:  
    pop     ebp
    ret
port60  db  0

代码来自James M的教程,在这里:http://www.jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html

在OSDev.org上可以阅读很多关于硬件接口的内容 - http://wiki.osdev.org/Main_Page

<强>更新 这是一个在DosBox中运行的16位ISR。

;-----------------------------------------------------
; handles int 0x09
;-----------------------------------------------------
keyhandler:
    cli
    pusha
    in al, 0x60                     ; get key data
    mov bl, al                      ; save it
    mov byte [port60], al

    in al, 0x61                     ; keybrd control
    mov ah, al
    or al, 0x80                     ; disable bit 7
    out 0x61, al                    ; send it back
    xchg ah, al                     ; get original
    out 0x61, al                    ; send that back

    mov al, 0x20                    ; End of Interrupt
    out 0x20, al                    ;

    and bl, 0x80                    ; key released
    jnz done                        ; don't repeat

    mov al, [port60]
    ;
    ; do something with the scan-code here
    ;
done:
    popa
    iret
port60    db    0        ; where we'll store the scan-code