;========================================================================
;
; __DOSPgm    Invoke a DOS function (general DOS gate)
;
; procedure __DOSPgm(var Reg : registers);
;
;	      Reg	The structure of register variables.  Information
;			is passed to the DOS function call through these
;			variables, and information is returned back
;			through these variables.
;
; Description DOSPgm_ provides a general DOS gate to the DOS functions
;	      (interrupt 21 (hex)).  This is required because the
;	      Turbo Pascal Intr procedure does not preserve the stack
;	      (which can be destroyed by EXEC), and the Turbo Pascal 4.0
;	      library function Exec is not used for the following reasons:
;
;	      1. Exec always puts the command line in the standard
;		 unformatted FCBs.  This does not allow the child process
;		 to inherit open files of the parent.
;	      2. Exec does not perform a CLD before invoking DOS (this
;		 is a precaution agains a known DOS bug).
;	      3. Exec always sets DosError equal to AX; this should not
;		 be done unless the carry flag is set.
;
; 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
	 assume    cs:code
	 public    __dospgm
__dospgm proc	   far
	 jmp	   begin

savess	 dw	   0		       ; Save the stack segment and
savesp	 dw	   0		       ; pointer as EXEC may destroy them

begin:
	 push	   bp		       ; Set up the local environment
	 mov	   bp,sp

; Equate a mnemonic to the parameter on the stack

reg	 equ	   [bp + 06]	       ; First parameter, far proc

; Save the registers needed for addressing (and accessing the data
; locations used to save the stack), and then set DS and load the
; address of the registers structure.

	 push	   ds
	 lds	   di,dword ptr reg    ; DS:DI points to reg structure
	 push	   ds		       ; and save to be accessed after
	 push	   di		       ; return from DOS.

	 mov	   cs:savess,ss        ; Save the stack segment and stack
	 mov	   cs:savesp,sp        ; pointer for use upon return.


	 mov	   ax,[di].reg_ax
	 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
	 push	   ax
	 popff
	 mov	   ax,[di].reg_ax      ; Set the last few registers, AX
	 lds	   di,dword ptr [di].reg_di  ;	DI and DS.

	 cld			       ; Clear the direction flag
	 clc			       ; Clear the carry flag for error
	 int	   21h		       ; Invoke DOS.  Function is in AH

	 cli			       ; Turn off interrupts while the
	 mov	   ss,cs:savess        ; stack is recovered.
	 mov	   sp,cs:savesp
	 sti			       ; Reenable interrupts

; Now recover the current value of all registers in the reg structure.
; This is done by pushing all registers on the stack, and then doing
; a string copy from the stack to the reg structure.

	 pushf
	 push	   es
	 push	   ds
	 push	   di
	 push	   si
	 push	   bp
	 push	   dx
	 push	   cx
	 push	   bx
	 push	   ax

; To recover the address of reg, because its address was pushed on the
; stack just before DOS was invoked, use an offset of 20 off the stack
; pointer.

	 mov	   bp,sp	       ; Set up to address the reg
	 les	   di,[bp + 20]        ; structure; ES:DI points to reg.
	 push	   ss
	 pop	   ds
	 push	   bp
	 pop	   si		       ; DS:SI points to the stacked
	 mov	   cx,10	       ; registers.  Move 10 words
	 cld
	 rep	   movsw
	 add	   sp,24	       ; Remove the registers from
				       ; the stack and saved DS and DI
	 pop	   ds		       ; Restore DS (Turbo expects this)
	 pop	   bp		       ; the base pointer, and throw
	 ret	   4		       ; the (var) parameter.

__dospgm endp
code	 ends
	 end
