andyross@ddsw1.MCS.COM (Andrew Rossmann) (08/25/90)
In working on my INFOPLUS program, I was going nuts trying to figure out
why I couldn't detect a DPMI driver or Windows 3!! It worked fine in
assembly, but not using Intr. After some code searching, and a little
reading, I figured it out. The following code explains the problem, and
offers a solution.
This problem could also affect other Turbo C and maybe the Microsloth
languages.
Andrew Rossmann
andyross@ddsw1.MCS.COM
--------------- CUT HERE ---------------------
;--------------------------------------------------------------------
; 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