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