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