替换 INT 9 ISR 时崩溃,但链接到它时不会崩溃

Crash when replacing INT 9 ISR, but not when chaining to it

这里有很多关于覆盖默认 int 9 ISR 的问题,我已经仔细阅读了它们。我的问题似乎很独特。有很多编写键盘中断处理程序的好例子,但它们涉及链接到原始 ISR。

我想完全取代原来的int 9 ISR。我不使用 int 16h BIOS 键盘服务,如果键盘数据区域不受管理,我也不介意。事实上,更换 ISR 对我有两方面的好处。

  1. 我自己的 ISR 可能会更快。我可以对传入的 make/break 代码快速做出反应,根据我的需要决定应该做什么,然后立即恢复我的程序。

  2. 我不再需要在主循环中包含代码来清除预输入缓冲区。

这是我编写的安装键盘 (int 9) 和定时器 (int 1ch) ISR 的程序。该程序通过将像素写入屏幕来响应击键。在这个例子中,键盘 ISR 链接到原始的 int 9 ISR,它在 DOSBox 和硬件(奔腾笔记本电脑 运行 DOS)中都工作得很好。定时器 ISR 每 55 毫秒更新一次缓冲区中的像素 0。它用作程序当前状态(崩溃或未崩溃)的指示器。因为我在弄乱键盘 ISR,所以我不能总是判断机器是否已锁定,例如,按下 caps lock 并查看 LED 指示灯是否亮起。

;----------------------------------------------------------------------------|                                                                     
 theStack SEGMENT STACK                                                     ;|
;____________________________________________________________________________|
 db 64 dup ('THESTACK')   
;----------------------------------------------------------------------------|
 theStack ENDS                                                              ;|
;____________________________________________________________________________|








;----------------------------------------------------------------------------|   
 varData SEGMENT                                                            ;|                            
;____________________________________________________________________________|


 pixCol db ?  ;current color index
 pixPos dw ?  ;current position in VGA pixel buffer

;----------------------------------------------------------------------------|
 varData ENDS                                                               ;|
;____________________________________________________________________________|







;----------------------------------------------------------------------------|
 code SEGMENT       

 assume cs:code,ds:varData

 oldInt9  dd 0                   ;original int 9 target
 oldInt1c dd 0                   ;original int 1c target                            

 main PROC                                                                  

 start:                                                                      

    mov ax, varData                            
    mov ds, ax                   ;Load the variable segment into ds           
;____________________________________________________________________________|






    call SET_VGA_256             ;set video mode 13h
    call INSTALL_ISR             ;install the ISRs

    mov ax, 40h
    mov es, ax                   ;access keyboard data area via segment 40h

loop0:
                                 ;clr type-ahead buff, prevents PC spkr beep

                                 ;(has to occur at some point if you don't 
                                 ;use int 16h BIOS keyboard services, and
                                 ;the original int 9 ISR is still handling
                                 ;keystrokes)

    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


    inc pixCol                   ;increment the current pixel color
    inc pixPos                   ;increment the current pixel position
    cmp pixPos, 63999d           ;Is pixPos < pixel area
    jb loop0                     ;If so, loop back
    mov pixPos, 0                ;If not, reinit pixPos

    in al, 60h                   ;al <- last key press
    cmp al, 1                    ;Was the key Esc
    jne loop0                    ;If not, loop back

    call EXIT2DOS                ;Uninstall ISRs, restore text mode, exit.                 






SET_VGA_256 PROC

;SETS VIDEO MODE TO 13H (VGA 320x200 256 COLORS)

    push ax
    mov ax, 13h   ;320x200 256 colors
    int 10h       ;video mode set 
    pop ax 
    ret

SET_VGA_256 ENDP




INSTALL_ISR PROC

;PATCHES NEW VECTOR ENTRIES INTO THE INTERRUPT VECTOR TABLE.
;THE DEFAULT INPUT (INT 9H) ISR IS REPLACED WITH MY OWN INPUT HANDLING ISR.
;Int 1C TIMER ISR IS CHAINED FOR DEBUGGING PURPOSES.
;THE OLD VECTOR ENTRIES ARE BACKED UP. THEY WILL BE RESTORED BEFORE PROGRAM
;TERMINATION.


    push es


    ;// BACKUP THE OLD IVT ENTRIES //

    cli                                      ;disable hardware interrupts
    xor ax, ax
    mov es, ax                               ;es <- 0
    mov ax, WORD PTR es:[9*4]                ;ax <- offset part of IVT entry 
    mov WORD PTR cs:oldInt9, ax              ;store it in MSW of oldInt9
    mov ax, WORD PTR es:[9*4+2]              ;si <- segment part of IVT entry 
    mov WORD PTR cs:oldInt9+2, ax            ;store it in LSW of oldInt9

    mov ax, WORD PTR es:[1ch*4]     
    mov WORD PTR cs:oldInt1c, ax   
    mov ax, WORD PTR es:[1ch*4+2]  
    mov WORD PTR cs:oldInt1c+2, ax           ;store INT 1C IVT entry



    ;// INSTALL THE NEW INTERRUPT HANDLERS //

    mov WORD PTR es:[9*4], OFFSET INPUT_ISR   ;copy my new ISR offset to IVT   
    mov WORD PTR es:[9*4+2], cs               ;copy my code segment to IVT

    mov WORD PTR es:[1ch*4], OFFSET TIMER_ISR  
    mov WORD PTR es:[1ch*4+2], cs             ;do the same for int 1c entry    
    sti                                       ;enable hardware interrupts


    pop es
    ret

INSTALL_ISR ENDP




INPUT_ISR PROC

;PRINTS A PIXEL OF SOME COLOR INDEX TO SOME LOCATION IN THE PIXEL BUFFER
;WHEN A KEYSTROKE OCCURS. ONE KEYSTROKE GENERATES TWO INTERRUPTS, ONE FOR 
;KEY-DOWN, AND ONE FOR KEY-UP. 


    pushf                       
    cli                            ;disable hardware interrupts
                                   ;(disabled anyway upon entry)
    push ds
    push es
    push bx
    push ax

    mov bx, varData              
    mov ds, bx                     ;ds <- data segment
    mov bx, 0a000h
    mov es, bx                     ;es <- VGA pixel buffer
    mov bx, pixPos                 ;bx <- current pixel position
    mov ah, pixCol                 ;ah <- current pixel color 
    mov BYTE PTR es:[bx], ah       ;write the pixel to the buffer

    pop ax
    pop bx
    pop es
    pop ds
    popf

    jmp cs:oldInt9                 ;now execute original ISR


INPUT_ISR ENDP             




TIMER_ISR PROC

;USED FOR DEBUGGING. IF THE COLOR OF PIXEL POSITION 0 STOPS UPDATING, THINGS
;HAVE GONE VERY WRONG. 



    pushf                       
    cli                            ;disable hardware interrupts
                                   ;(disabled anyway upon entry)
    push ds
    push es
    push bx
    push ax


    mov bx, varData              
    mov ds, bx                     ;ds <- data segment
    mov bx, 0a000h
    mov es, bx                     ;es <- VGA pixel buffer
    mov ah, pixCol                 ;ah <- current pixel color
    xor bx, bx                     ;bx <- pixel position 0 
    mov BYTE PTR es:[bx], ah       ;write the pixel to the buffer


    pop ax
    pop bx
    pop es
    pop ds
    popf

    jmp cs:oldInt1c                ;now execute original ISR


TIMER_ISR ENDP






EXIT2DOS PROC

;UNINSTALL ISR, CLEAR THE TYPE-AHEAD BUFFER, RESTORE TEXT MODE, AND EXIT.

    cli                             ;disable hardware interrupts
    xor ax, ax
    mov es, ax

    mov ax, WORD PTR cs:oldInt9
    mov WORD PTR es:[9*4], ax 
    mov ax, WORD PTR cs:oldInt9+2
    mov WORD PTR es:[9*4+2], ax     ;Original int 9 ISR restored to IVT

    mov ax, WORD PTR cs:oldInt1c
    mov WORD PTR es:[1ch*4], ax 
    mov ax, WORD PTR cs:oldInt1c+2
    mov WORD PTR es:[1ch*4+2], ax   ;Original int 1c ISR restored to IVT
    sti                             ;enable hardware interrupts


                                    ;clear type-ahead buffer just before exit 
                                    ;to prevent dumping garbage characters
                                    ;to DOS prompt.
    mov ax, 40h
    mov es, ax                      ;access kbd 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 kbd buff tail to same as buff head
                                    ;now the keyboard buffer is cleared.

    xor ah, ah                      ;select video mode function
    mov al, 3                       ;select 80x25 16 colors
    int 10h                         ;restore VIDEO back to text mode

    mov ax, 4c00h                   ;Terminate process DOS service
    int 21h                         ;Control returns to DOS

EXIT2DOS ENDP








;----------------------------------------------------------------------------|
 main ENDP                                                                  ;|
                                                                            ;|
 code ENDS                                                                  ;|
                                                                            ;|
 END start                                                                  ;| 
;____________________________________________________________________________|   

如果我稍微更改 INPUT_ISR 程序,使其向 PIC 发送 EOI 并执行 IRET,则原始 int 9 ISR 将永远不会执行。

INPUT_ISR PROC

    pushf                       
    cli                            ;disable hardware interrupts
                                   ;(disabled anyway upon entry)
    push ds
    push es
    push bx
    push ax

    mov bx, varData              
    mov ds, bx                     ;ds <- data segment
    mov bx, 0a000h
    mov es, bx                     ;es <- VGA pixel buffer
    mov bx, pixPos                 ;bx <- current pixel position
    mov ah, pixCol                 ;ah <- current pixel color 
    mov BYTE PTR es:[bx], ah       ;write the pixel to the buffer

    mov al, 20h
    out 20h, al                    ;EOI 

    pop ax
    pop bx
    pop es
    pop ds
    popf

    iret

INPUT_ISR ENDP 

但是,这会导致 DOSBox 和硬件出现问题。在 DOSBox 中它不会崩溃,但会出现不稳定的行为。在 window 的右上角,像素通常会相互堆叠,并且像素在屏幕上显示缓慢。定时器 ISR 仍然执行,程序可以正常终止。在硬件中会发生即时崩溃。定时器 ISR 停止更新它的像素,机器必须重新启动。

替换 int 9 ISR 是否安全?我知道它在幕后所做的事情因机器而异,但它是否经常做对系统至关重要的事情?据我所知,它只是管理键盘数据区,对吗?

正如 Michael Petch 在上述评论链中提到的那样,必须在结束 ISR 之前读取端口 60h,否则将不会发生进一步的键盘中断。

更正 INPUT_ISR 程序:

INPUT_ISR PROC

;PRINTS A PIXEL OF SOME COLOR INDEX TO SOME LOCATION IN THE PIXEL BUFFER
;WHEN A KEYSTROKE OCCURS. ONE KEYSTROKE GENERATES TWO INTERRUPTS, ONE FOR 
;KEY-DOWN, AND ONE FOR KEY-UP. 


    pushf                       
    cli                            ;disable hardware interrupts
                                   ;(disabled anyway upon entry)
    push ds
    push es
    push bx
    push ax



    mov bx, varData              
    mov ds, bx                     ;ds <- data segment
    mov bx, 0a000h
    mov es, bx                     ;es <- VGA pixel buffer
    mov bx, pixPos                 ;bx <- current pixel position
    mov ah, pixCol                 ;ah <- current pixel color 
    mov BYTE PTR es:[bx], ah       ;write the pixel to the buffer


    in al, 60h                     ;read port 60h, must always be done before
                                   ;terminating an int 9 ISR, or else no 
                                   ;keyboard interrupts will occur

    mov al, 20h
    out 20h, al                    ;EOI 

    pop ax
    pop bx
    pop es
    pop ds
    popf

    iret



INPUT_ISR ENDP