PAGE 55,132
;
; name		exec -- system call for .COM files
;
; synopsis	status = exec(cmd);
;		int status;		error returns
; 		char *cmd;		command to execute
;
; description	This function accepts a string with the pathname and
;		parameters of a command to be executed and executes it.
;		See Appendix F. of the DOS 2.0 Reference Manual for more
;		information.
;
; returns	0:	Successful
;		-1:	Insufficient Memory
;		-2:	Access Denied
;		-3:	No such command
;		-4:	Invalid command format
;		-5:	Memory control blocks destroyed
;		-6:	Invalid memory block address
;
; cautions	Use only with .COM files built with the Lattice C
;		compiler, linked with CC.OBJ, and translated to
;		.COM format with EXE2BIN.
;
; authors	Written by Darrel Plank.  Modified from the macro by Brad
;		Davis (b-davis@utah-cs).  The prolog and epilog are modified
;		from Jim Holtman's macros for Pascal.  Fixed by Marco Papa
;		(papa.use-cse@csnet-relay).
;

PROLOG	MACRO
	PUSH	BP
	MOV	BP,SP
	ENDM

EPILOG	MACRO	NUM
	POP	BP
	RET
	ENDM

EXECVAL	EQU	0
OVLVAL	EQU	3
FNCINT	EQU	21H
SETBLK	EQU	4AH
EXECF	EQU	4BH
CR	EQU	0DH

PSP     STRUC
INTVECT DW      ?
TOM     DW      ?
RES1    DB      ?
DOSLONG DB      5 DUP (?)
TERMINA DD      ?
CTRLBRK DD      ?
CRITERR DD      ?
DOS1    DB      22 DUP (?)
ENVIRO  DW      ?
DOS2    DB      46 DUP (?)
FPA1    DB      16 DUP (?)
FPA2    DB      20 DUP (?)
UPA     DB      128 DUP (?)
PSP     ENDS

EXECDEF STRUC
NENVIRO DW
COMMND  DW      2 DUP (0)
FCB5CH  DW      2 DUP (0)
FCB6CH  DW      2 DUP (0)
EXECDEF ENDS

PGROUP	GROUP PROG
PROG	SEGMENT BYTE PUBLIC 'prog'
	ASSUME CS:PGROUP

PUBLIC	EXEC
EXEC	PROC NEAR
        PROLOG
        PUSH    DS
        PUSH    ES
;
; free up as much memory as we can
;
	MOV	AX,CS
	PUSH	ES		; Save ES for later
	MOV	ES,AX
	MOV	BX,SS
	SUB	BX,AX
	ADD	BX,1000H	; 64K for stack segment
	MOV	AH,SETBLK
	INT	FNCINT
	JNC	NEAR PTR LBL1
	POP	ES		; ****
	JMP	NEAR PTR LBL2	; ****
LBL1:	POP	ES		; Get ES's original value
;
; Save SS and SP registers
;
        MOV     CS:SPSAVE,SP
        MOV     CS:SSSAVE,SS
;
; set up the parameter block
;
        MOV     CS:EXECBLK.NENVIRO,0	; Inherit envir. from parent
	MOV	AX,3700H		; Undocumented call for SWITCHAR
;
; W A R N I N G:  The following function call is undocumented and is
; liable to disappear or change in future versions of DOS.
;
	INT	FNCINT
	MOV	CS:COMMAND[1],DL	; Switchar
	MOV	DX,4[BP]		; Address of the command
	MOV	SI,DX
	MOV	DI,DX
	CLD
	XOR	AL,AL
	MOV	CX,100H			; Longest string can be 100h
	REPNE SCASB			; Find Null termination
	SUB	DX,DI
	NEG	DX
	MOV	CX,DX
	DEC	CX
	ADD	DX,2
	MOV	CS:COMMAND[0],DL	; Save command length
	LEA	DI,CS:COMMAND[4]
	MOV	AX,CS
	MOV	ES,AX
	REP MOVSB			; Copy command into our buffer
ASSUME	DS:PGROUP
	MOV	AX,CS
	MOV	DS,AX			; DS points at code segment
	MOV	BYTE PTR [DI],CR	; Put in Carriage Return
	LEA	DX,COMMAND
        MOV     EXECBLK.COMMND[0],DX
        MOV     EXECBLK.COMMND[2],DS
	MOV	BX,OFFSET EXECBLK
        XOR     SI,SI
	MOV	DS,[SI].ENVIRO		; Get environment address
ASSUME	DS:NOTHING
	LEA	SI,CS:COMSPEC		; Point SI at env. variable name
	PUSH	DS			; Swap
	PUSH	ES			; ES
	POP	DS			; and
	POP	ES			; DS
	CALL	GETENV
	PUSH	DS			; Swap
	PUSH	ES			; them
	POP	DS			; back
	POP	ES			; again
        MOV     AH,EXECF
	MOV	AL,EXECVAL		; OVLVAL here for overlay
        INT     FNCINT
	MOV     SS,CS:SSSAVE		; ****
        MOV     SP,CS:SPSAVE		; ****
	JC	NEAR PTR LBL2
	MOV	AX,0			; Successful exec
	JMP	NEAR PTR FINE
LBL2:	MOV	SI,AX
	MOV	AL,CS:ERRORS[SI]	; Get error code
	MOV	AH,0FFH			; Sign extension - Assume Negative
FINE:	POP     ES			; ****
        POP     DS
	EPILOG	1
EXECBLK EXECDEF <>
;
; first byte of command is length excluding the length byte and the
; trailing \r.  Second byte is switchar.
;
COMMAND	DB	2 DUP(?),"C ",254 DUP(?)
COMSPEC	DB	"COMSPEC",0
SPSAVE  DW
SSSAVE  DW
ERRORS	DB	?
	DB	?
	DB	-3	; No such command
	DB	?
	DB	?
	DB	-2	; Access denied
	DB	?
	DB	-5	; Memory control blocks destroyed
	DB	-1	; Insufficient memory
	DB	-6	; Invalid memory block address ****
	DB	?
	DB	-4	; Invalid command format

EXEC	ENDP

;
; Getenv expects ES to have the environment paragraph and DS:SI to point
; to an ASCIIZ string with the desired environment variable in it.
; It returns the address of the proper string in ES:DX.
;

	PUBLIC GETENV
GETENV	PROC NEAR

	PROLOG
	PUSH	AX
	PUSH	CX
	PUSH	SI
	PUSH	DI
	MOV	CS:VARNAME,SI		; Save offset of env. name
	XOR	DI,DI
;
; At this point ds:si points to dummy variable environment name and
; es:di points to environment.
;
	CLD				;Forward string operations
TOP:
	LODSB				;Get a char. of env. name
	CMP	AL,0			;If we're at the end
	JNE	NEAR PTR LBL3
	CMP	BYTE PTR ES:[DI],'='	;Check for match
	JNE	NEAR PTR LBL4
;
; We matched
;
	INC	DI			;Move beyond '='
	MOV	DX,DI
	POP	DI
	POP	SI
	POP	CX
	POP	AX
	EPILOG	2
LBL4:
;
; At this point we found the end of the Env. variable name but it didn't
; match because the env. string was too long
;
	MOV	CX,-1
	REPNE SCASB			;Find the end of the env. string
	CMP	BYTE PTR ES:[DI],0
	JNE	LBL3
	MOV	AX,-1			;End of environment area
	POP	DI
	POP	SI
	POP	CX
	POP	AX
	EPILOG	2
LBL3:
;
; Check if the next character matches
;
	AND	AX,11011111b		;Capitalize the character in ax
	SCASB
	JE	TOP
;
; If we get here we don't have a match so move on
;
	MOV	SI,CS:VARNAME		;Go back to start of env. string
	XOR	AX,AX
	MOV	CX,-1
	REPNE SCASB			;Go to next env. variable
	CMP	BYTE PTR ES:[DI],0
	JNE	TOP
	MOV	AX,-1			;End of environment area
	POP	DI
	POP	SI
	POP	CX
	POP	AX
	EPILOG	2

VARNAME	DW	?

GETENV	ENDP

PROG	ENDS
	END
