There are many questions here on SO related to overriding the default int 9 ISR, and I have read through them carefully. My issue appears to be unique. There are many good examples of writing keyboard interrupt handlers, but they involve chaining on to the original ISR.
I would like to totally replace the original int 9 ISR. I don't use the int 16h BIOS keyboard services and I don't mind if the keyboard data area isn't managed. In fact, replacing the ISR would be beneficial to me in two ways.
My own ISR would probably be faster. I could quickly react to the incoming make/break code, decide what should be done in a way that is specifc to my needs, then immediately resume my program.
I would no longer need to include code in my main loop to clear the type-ahead buffer.
Here's a program I wrote which installs keyboard (int 9) and timer (int 1ch) ISRs. The program responds to keystrokes by writing pixels to the screen. In this example the keyboard ISR is chained on to the original int 9 ISR and it works perfectly fine in both DOSBox, and hardware (Pentium laptop running DOS). The timer ISR justs updates pixel 0 in the buffer every 55 milliseconds. It's used as an indicator of the current state of the program (crashed, or not). Since i'm messing around with the keyboard ISR I can't always tell if the machine has locked up by, for instance, pressing caps lock and seeing if the LED indicator lights up.
;----------------------------------------------------------------------------|
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 ;|
;____________________________________________________________________________|
If I change the INPUT_ISR procedure slightly, so that it sends an EOI to the PIC and executes an IRET the original int 9 ISR will never execute.
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
However, this causes issues in both DOSBox and on hardware. In DOSBox it doesn't crash, but erratic behavior occurs. Pixels will often bunch up on top of each other in the upper right hand corner of the window, and the pixels are sluggish to appear on the screen. The timer ISR still executes and the program can be terminated normally. In hardware an instant crash occurs. The timer ISR stops updating it's pixel, and the machine has to be rebooted.
Is it even safe to replace the int 9 ISR? I know it's specific from machine to machine as to what it does behind the scenes, but does it often do things that are system critical? As far as I know it just manages the keyboard data area, right?