;************************************************************************
;
; __CallIsr   Invoke an interrupt service routine
;
; procedure   __CallIsr(Vector : pointer; var Reg : registers);
;
;	      Vector	The address (CS:IP) of the interrupt service
;			routine to execute.  It must be the address of
;			code terminating with an IRET instruction.
;	      Reg	The register structure of register values
;			passed to and returned from the interrupt
;			service routine.
;
; Description This procedure makes a call to the procedure whose
;	      address is specified by the code vector, Vector.	The
;	      values of the register components in the registers data
;	      structure are passed to the routine, and the register
;	      values are returned here as well.
;
;	      __CallIsr invokes the code by setting up the machine
;	      registers with the appropriate values from Reg and then
;	      simulates an INT instruction.  The values of the machine
;	      registers upon return are passed in the Reg variable.
;	      In function, __CallIsr is very similar to the Turbo
;	      Pascal Intr procedure, but allows the specification of
;	      the location of the code, rather than an interrupt vec-
;	      tor.  This is useful for "filtering" interrupts, because
;	      the previously installed code can be executed by a user
;	      installed ISR.
;
; Version     4.00 (C)Copyright Blaise Computing Inc. 1987
;________________________________________________________________________

REGISTERS  struc
  reg_ax    dw	   ?
  reg_bx    dw	   ?
  reg_cx    dw	   ?
  reg_dx    dw	   ?
  reg_bp    dw	   ?
  reg_si    dw	   ?
  reg_di    dw	   ?
  reg_ds    dw	   ?
  reg_es    dw	   ?
  reg_flags dw	   ?
REGISTERS  ends

popff	   macro		       ;; Simulate POPF instruction
	   local   do_call,do_iret
	   jmp	   short do_call
do_iret:
	   iret 		       ;; Pop IP, CS and flags
do_call:
	   push    cs
	   call    do_iret	       ;; CS and IP now pushed
	   endm

code	   segment byte public
	   assume  cs:code

	   public  __CallIsr
__CallIsr  proc    far
	   jmp	   begin

vectptrofs dw	   ?		       ; Local storage for vector
vectptrseg dw	   ?

begin:				       ; Start of __CallIsr code
	   push    bp
	   mov	   bp,sp

; Equate mnemonics to the parameters on the stack

reg	   equ	   [bp + 06]
vector	   equ	   [bp + 10]

	   push    ax		       ; Save the registers for
	   push    bx		       ; restoration upon exit
	   push    cx
	   push    dx
	   push    si
	   push    di
	   push    ds
	   push    es

; First store the address of the code to be executed, and then set
; the registers to the values passed in the Reg record structure.
; Interrupts are disabled while the environment is established.

	   cli
	   les	   si,vector
	   mov	   cs:vectptrofs,si
	   mov	   cs:vectptrseg,es
	   lds	   di,dword ptr reg
	   push    ds		       ; Save the address of reg for use
	   push    di		       ; when the ISR returns
	   mov	   bx,[di].reg_bx
	   mov	   cx,[di].reg_cx

	   mov	   dx,[di].reg_dx
	   mov	   bp,[di].reg_bp
	   mov	   si,[di].reg_si
	   mov	   es,[di].reg_es
	   mov	   ax,[di].reg_flags

; To simulate an INT instruction, the flags (now in AX) are pushed on the
; stack, the interrupt and single-step flags are cleared, and a far call
; is made to the address specified by the vector parameter (now in
; vectptrofs and vectptrseg).

	   push    ax		       ; Flags are pushed
	   and	   ax,0FCFFh	       ; Clear IF and Trap
	   push    ax		       ; Now set the flags
	   popff		       ; Interrupts are still disabled
	   mov	   ax,[di].reg_ax      ; Set the last few registers, AX
	   lds	   di,dword ptr [di].reg_di  ;	DI and DS.

	   call    dword ptr cs:vectptrofs

; Upon return, restore the register structure with the values of the
; registers (the ISR may have altered them).  The registers values are
; recovered by pushing them on the stack and doing a string copy to
; the register structure.

	   pushf
	   sti			       ; Interrupts are OK now
	   push    es
	   push    ds
	   push    di
	   push    si
	   push    bp
	   push    dx
	   push    cx
	   push    bx
	   push    ax
	   mov	   bp,sp	       ; Set up to address the reg
	   les	   di,[bp + 20]        ; parameter; DS:DI was pushed.
	   push    ss		       ; Now ES:DI points to the
	   pop	   ds		       ; destination (the reg structure)
	   push    bp		       ; and DS:SI to the source (the
	   pop	   si		       ; registers on the stack).
	   mov	   cx,10	       ; Move 10 words
	   cld
	   rep	   movsw
	   add	   sp,24	       ; Remove the registers and the
				       ; saved address from the stack

	   pop	   es		       ; Restore the registers
	   pop	   ds
	   pop	   di
	   pop	   si
	   pop	   dx
	   pop	   cx
	   pop	   bx
	   pop	   ax
	   pop	   bp		       ; Restore BP and throw away
	   ret	   8		       ; the parameters.

__CallIsr  endp
code	   ends
	   end
