[comp.lang.pascal] Turbo Pascal semi-bug

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