andyross@ddsw1.MCS.COM (Andrew Rossmann) (08/25/90)
In working on my INFOPLUS program, I came across a problem with Turbo
Pascal 5.5 that may also be present in other programming languages. It
deals with making your own interrupt calls (Intr(intno, regs) in Turbo
Pascal). Turbo Pascal does not really do an interrupt, but instead, gets
the address of the interrupt from the interrupt table, pushes it on the
stack, and then does a RETF!! This code works great normally, but may fail
under protected or Virtual 8086 mode, where a control program can trap
interrupts and act on them itself. Windows 3.0 DPMI driver does this.
What follows is some assembly code for Turbo Pascal 5.5, that might be
adaptable to other languages (I'd assume Turbo C might work similarly if
not identically.) that offers true interrupts.
Andrew Rossmann
andyross@ddsw1.MCS.COM
;--------------------------------------------------------------------
; AltIntr.ASM
; extracted from Infoplus 1.30 by Andrew Rossmann
;
; ALTINTR - calls interrupts with a true INT call
; ALTMSDOS - calls DOS with a true INT call
;
; Andrew Rossmann [andyross@ddsw1.MCS.COM]
; Aug. 25, 1990
;--------------------------------------------------------------------
public ALTINTR, ALTMSDOS
; This is coded in a very generic assembly format for best compatibility with
; all assemblers.
; Make sure you have a {$L altintr} in your program. You MUST define both
; ALTINTR and ALTMSDOS!!
CODE segment byte
; AltIntr is an alternative to the Intr function. The standard Intr function
; does not do a true Interrupt!! Instead, it gets the address of the interrupt
; from the interrupt table, loads all the registers, and then does a RETF!!!
; The address of a return routine has been pushed on the stack so that it
; returns to TP and unloads the registers. This was probably done because
; Intel saw to it that all interrupt numbers must be immediate, and Borland
; didn't want to use self-modifying code.
; Now, normally, the above procedure works perfectly fine, except under 1
; condition. When the CPU is under protected or Virtual 86 mode. When in those
; modes, a program with higher privileges can trap an interrupt and act on it.
; I found this out the hard way by going nuts wondering why I couldn't detect
; DPMI drivers or Windows!! My ALTernative INTerrupt functions identically to
; Borlands, but uses self-modifying code to generate a true interrupt.
; NOTE: The MsDos routine is ALSO affected by this problem. It just stuffs
; a 21h into the stack, and calls Intr!!! So you can use ALTMSDOS instead!
; If you need a proc far, increase the increments in the two equates by 2.
;
; Pascal format: procedure AltIntr(intno: byte; var regs: registers); external;
; You can have AltIntr always be used with this following code:
; procedure Intr(intno:byte; var regs: registers);
; begin
; AltIntr(intno, regs);
; end;
;
; You can always access the standard Intr by calling it
; with Dos.Intr(intno, regs); (provided you have a 'Uses Dos' statment!)
;
ALTINTR proc near
assume cs:CODE, ds:DATA, es:NOTHING
regaddr equ [bp + 4]
intno equ [bp + 8]
push bp
mov bp,sp
push ds ;save DS, because we screw it up
mov al,intno ;get interrupt number to use
mov cs:intrpt,al ;and modify our code
lds si,regaddr ;point DS:SI to var regs
mov cs:save_ds,ds ;save pointer for return
mov cs:save_si,si
cld ;go forward
lodsw ;load AX and hold it
push ax
lodsw ;load BX
mov bx,ax
lodsw ;load CX
mov cx,ax
lodsw ;load DX
mov dx,ax
lodsw ;load BP
mov bp,ax
lodsw ;load SI and hold it
push ax
lodsw ;load DI
mov di,ax
lodsw ;load DS and hold it
push ax
lodsw ;load ES
mov es,ax
lodsw ;load Flags
push ax
popf
pop ds ;get held up regs
pop si
pop ax
db 0cdh ;Int opcode
intrpt db ? ;loaded with real interrupt when run
pushf ;save flags and modified regs
push es
push di
mov es,cs:save_ds ;get var regs pointer into ES:DI
mov di,cs:save_si
cld ;go forward
stosw ;save AX
mov ax,bx
stosw ;save BX
mov ax,cx
stosw ;save CX
mov ax,dx
stosw ;save DX
mov ax,bp
stosw ;save BP
mov ax,si
stosw ;save SI
pop ax
stosw ;save DI
mov ax,ds
stosw ;save DS
pop ax
stosw ;save ES
pop ax
stosw ;save Flags
pop ds ;restore regs
pop bp
ret 6
;local storage
save_ds dw ?
save_si dw ?
ALTINTR endp
; AltMsDos pulls back the stack a bit, pushes in a 21h, restores the stack,
; then calls AltIntr. If you need a proc far, uncomment the two lines
;
; Pascal format: AltMsDos(var regs: registers); external;
; You can have AltIntr always be used with this following code:
; procedure MsDos(var regs: registers);
; begin
; AltMsDos(regs);
; end;
;
; You can always access the standard MsDos by calling it
; with Dos.MsDos(regs); (provided you have a 'Uses Dos' statment!)
ALTMSDOS proc near
assume cs:CODE, ds:DATA, es:NOTHING
pop si ;back track a bit so we can stuff
pop dx ;interrupt number in
pop cx
; pop bx ;uncomment if proc far
mov al,21h ;push interrupt number
push ax
; push bx ;uncomment if proc far
push cx ;restore other info
push dx
push si
jmp ALTINTR ;do interrupt call
ALTMSDOS endp
code ends
data segment byte
data ends
end