home *** CD-ROM | disk | FTP | other *** search
- ;_ exec2.asm Fri Feb 19 1988 Modified by: Walter Bright */
- ; Copyright (C) 1985-1988 by Northwest Software
- ; All Rights Reserved
-
- include macros.asm
-
- begdata
-
- c_extrn _doserrno,word, _psp,word
-
- header equ $ ; .EXE file header
- fcb1 db 37 dup (?)
- fcb2 db 37 dup (?) ; a sub-process may want these
-
- param_block equ $
- env dw ? ; segment address of environment
- comml dw ? ; DWORD ptr to command line
- dw ? ; segment of command line
- pfcb1 dw offset DGROUP:fcb1 ; DWORD points to first FCB
- dw ? ; segment of first FCB
- pfcb2 dw offset DGROUP:fcb2 ; DWORD ptr to second FCB
- dw ? ; segment of second FCB
-
- enddata
-
- begcode exec2
-
- ; Must store these in code segment so we can find them after the exec
- oldsp dw ? ; save SP
- oldss dw ? ; save SS
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ; Execute a command.
- ; Use:
- ; int _exec(command,commandline,envseg,chain)
- ; char *command; /* name of program to run */
- ; char *commandline; /* command line (128 bytes max preceeded */
- ; /* by a byte count and ended with a \r) */
- ; /* (an extra 0 at the end is recommended) */
- ; int envseg; /* segment of environment */
- ; int chain; /* if !=0, then 'chain' to program, and */
- ; /* terminate program when command terminates */
- ;
- ; Returns:
- ; -1 error (_doserrno has the MS-DOS error code)
- ; n success (n is the exit status of the executed command)
-
-
- c_public _exec
- func _exec
- push BP ; save old stack frame
- mov BP,SP
- .save <SI,DI>
- IF LPTR
- push DS ; save DS
- ENDIF
- mov AX,P+SIZEPTR+SIZEPTR[BP] ; get segment of environment
- mov env,AX
- mov CS:oldsp,SP
- mov CS:oldss,SS ; only thing preserved by exec is
- ; CS and IP, so we must save these
- ; in the code segment
- setESeqDS ; ES -> data segment
- IF SPTR
- mov SI,P+2[BP] ; SI = commandline
- mov comml,SI ; offset of command line
- mov comml+2,DS ; segment of command line
- ELSE
- lds SI,P+4[BP] ; DS:SI = commandline
- segES
- mov DGROUP:comml,SI ; offset of command line
- segES
- mov DGROUP:comml+2,DS ; segment of command line
- ENDIF
- cld
- lodsb ; AL = # of chars in command line
- tst AL
- jz L2 ; no chars in command line
- ; DS:SI -> command line
- mov DI,offset DGROUP:fcb1 ; ES:DI -> fcb1
- mov AX,2901h
- bdos ; parse filename into fcb1
- mov DI,offset DGROUP:fcb2 ; ES:DI -> fcb2
- mov AX,2901h
- bdos ; parse second filename into fcb2
- L2:
- .if <word ptr P+SIZEPTR+SIZEPTR+2[BP]> ne 0, L4 ; if chain to next program
- IF SPTR
- mov pfcb1+2, ES
- mov pfcb2+2, ES ; segment values
- mov DX,P[BP] ; DS:DX -> command
- ELSE
- segES
- mov DGROUP:pfcb1+2, ES
- segES
- mov DGROUP:pfcb2+2, ES ; segment values
- lds DX,P[BP] ; DS:DX -> command
- ENDIF
- mov BX,offset DGROUP:param_block ; ES:BX -> parameter block
- mov AX,4B00h ; load/execute program
- bdos ; perform exec
- L6: cld ; no direction flag bugs
-
- mov BX,CS:oldss
- cli ;for bug in old 8088's
- mov SS,BX ; restore SS
- mov SP,CS:oldsp ; restore SP
- sti ;for bug in old 8088's
- IF SPTR
- mov DS,BX
- mov ES,BX ; restore DS,ES
- ENDIF
- IF LPTR
- pop DS ; restore DS
- ENDIF
- jc L1 ; if (error)
-
- bdos 4Dh ; get exit status of sub-process
- L3: .restore <DI,SI>
- pop BP
- ret
-
- L1: mov _doserrno,AX ; save error code in _doserrno
- sbb AX,AX ; return (-1)
- jmp L3
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;Chain to command
- L4:
- ;At this point:
- ;ES -> data segment
- ;DS -> segment of command line
- ;Direction flag = forward
-
- ;From now on, we can't fix things as they were before the
- ;call to _exec(). Therefore, all errors simply abort back
- ;to DOS with an errorlevel of 1.
-
- ;Restore vectors 0x22, 0x23 and 0x24 from PSP
- segES
- mov AX,DGROUP:_psp
- push ES
- mov ES,AX
- mov CX,3 ;3 vectors to restore
- mov BX,0Ah
- mov AX,2522h ;DOS function 25, vector 22
- L8: mov DX,ES:[BX]
- mov DS,ES:2[BX]
- bdos ;set interrupt vector
- add BX,2
- inc AL
- loop L8
- pop ES
-
- IF 0
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;Use our own loader
-
- ;Rewrite the PSP of our own program, we will cause the
- ;exec'ed program to sit right on top of ours
-
- push ES
- pop DS ;now DS -> data segment
-
- ;Load values into the PSP
- mov ES,_psp
- mov DI,5Ch ;ES:DI -> where first FCB will go
- mov CX,(6Ch - 5Ch)/2 ;size of FCB area in words
- mov SI,offset DGROUP:fcb1
- rep movsw ;transfer first FCB
- mov CX,(6Ch - 5Ch)/2 ;size of FCB area in words
- mov SI,offset DGROUP:fcb2
- rep movsw ;transfer second FCB
- mov DI,80h ;offset of command line
- mov CX,128/2 ;128 bytes max in command line
- if SPTR
- mov SI,P+SIZEPTR[BP]
- rep movsw ;transfer command line to PSP
- else
- push DS
- lds SI,P+SIZEPTR[BP]
- rep movsw ;transfer command line to PSP
- pop DS
- endif
-
- ;Attempt to grow our segment as large as possible.
- ;Store new top of memory back into PSP
- mov BX,0FFFFh
- bdos 4Ah
- jnc E1 ;succeeded
- bdos ;try again with max allowable size
- jc err1 ;error (probably corrupted memory)
- E1: mov SI,ES
- add SI,BX
- mov ES:2,SI ;new top of memory in paragraphs
- mov DI,BX
-
- ;Open the executable file
- IF SPTR
- mov DX,P[BP]
- bdos 3Dh
- ELSE
- push DS
- lds DX,P[BP]
- bdos 3Dh
- pop BP
- ENDIF
- err1: jc error
-
- ;Read the first 28 bytes of the file. If a .EXE file, this
- ;will be the header
- mov DX,offset DGROUP:header
- mov CX,28
- bdos 3Fh
- jc error
-
- ;If the first two bytes are the EXE signature, assume we have
- ;an EXE file.
- .if <word ptr DS:header[0]> e 05A4Dh, dotexe
-
- ;Assume it's a .COM file.
- ;Rewind the file back to the beginning, as there is no header
- ;for a .COM file.
- clr CX
- clr DX
- mov AX,04200
- bdos
- jc error
-
- ;At this point:
- ;ES -> PSP
- ;DS -> data segment
- ;BX = .COM file handle
- ;SI = top paragraph of available memory
- ;DI = # of paragraphs available
-
- ;Compute into BP the offset of the top of memory for the .COM
- ;program (it gets chopped off at 64k)
- sub SI,8
- sub DI,8 ;allow 128 bytes for bootstrap + boot stack
- mov BP,1000h
- .if DI a BP, C2
- mov BP,DI
- C2: mov CL,4
- shl BP,CL ;*16 to get from paragraphs to offset
- mov ES:6,BP ;store # of bytes in segment in PSP
-
- ;We'll need a bootstrap routine and some stack that won't be
- ;overwritten when the .COM file is read in. Do this by
- ;blitting a boot program to the end of memory.
- mov AX,ES ;save PSP segment for later
- mov ES,SI
- cli ;for bug in old 8088's
- mov SS,SI
- mov SP,8*16 ;set stack to end
- sti ;for bug in old 8088's
- clr DI ;ES:DI -> relocation address
-
- push ES
- push DI ;put relocation addr on stack for retf
-
- push CS
- pop DS
- mov SI,offset $comstart ;DS:SI -> start of bootstrap
- mov CX,offset $comend - offset $comstart
- rep movsb
-
- ;Compute into CX the max number of bytes that we can read in
- mov CX,BP
- mov DX,0100h ;offset of start of .COM program
- sub CX,DX ;.COM files can't be larger than
- ;64k - sizeof(PSP)
- inc CX ;1 more byte to see if file is larger than
- ;will fit in memory
- ;Do the actual read
- mov ES,AX
- mov DS,AX ;DS = segment of PSP
- .retf ;continue $comstart
-
- $comstart:
- bdos 3Fh ;read
- jc error
- .if AX e CX, error ;read too much, file is too big
- bdos 3Eh ;close file
- jc error
-
- ;Initialize registers for a .COM program
- mov AX,DS
- cli ;for bug in old 8088's
- mov SS,AX
- mov SP,BP ;stack is at end of segment
- sti ;for bug in old 8088's
-
- clr AX
- push AX ;leave word of 0s on top of stack
- push DS
- push DX
- .retf ;start execution
-
- error: mov AX,04C01h ;return to DOS with error
- bdos
- $comend:
-
- ; .EXE file loader
- dotexe:
- ;At this point:
- ;ES -> PSP
- ;DS -> data segment
- ;BX = .COM file handle
- ;SI = top paragraph of available memory
- ;DI = # of paragraphs available
-
- ;Seek to the start of the load module
- mov DX,word ptr DS:header[8] ;offset in paragraphs to start of load module
- clr AX
- mov CX,4
- E2: shl DX,1
- rcl AX,1
- loop E2
- mov CX,AX ;CX,DX = offset of start of load module
- mov AX,04200h
- bdos
- jc error
-
- ;Adjust available memory downwards by amount we need
- ;for the header plus bootstrap loader plus stack
- sub SI,512/16
- sub DI,512/16
- mov DI,BP ;save # of paragraphs available
-
- cli ;for bug in old 8088's
- mov SS,SI
- mov SP,512/16 ;set stack to top of new area
- sti ;for bug in old 8088's
-
- ;Transfer header information to new area
- mov DX,ES ;DX = segment of PSP
- mov ES,SI
- mov SI,offset DGROUP:header ;DS:SI -> header in data segment
- clr DI ;ES:DI -> relocated header
- mov CX,(28-2)/2 ;sizeof(header) - (unnecessary stuff)
- rep movsw ;effect the transfer
-
- ;Transfer bootstrap loader to new area
- mov SI,offset $exestart
- push CS
- pop DS ;DS:SI -> original code
- mov AX,ES ;save segment of relocated code
- push ES
- push DI ;so we can retf to relocated code
- mov CX,offset $exeend - $exestart ;# of bytes to relocate
- rep movsw ;relocate
-
- ;Determine size of load module
- mov ES,AX ;ES -> segment of relocated code
- mov SI,ES:4 ;size of file in 512 byte increments
- mov CL,5
- shl SI,CL ;size of file in paragraphs
- sub SI,ES:8 ;AX = size of load module in paragraphs
- sub BP,100h/16 ;reserve room for PSP
- .if SI be BP, E8 ;if file will fit in memory
- jmp error ;too big
-
- E8: mov BP,DX ;BP = segment of PSP
- mov DS,DX ;DS = segment of PSP
- mov DX,100h ;start loading at offset 100
- .retf ;jmp to $exestart
-
- $exestart:
- E3: mov AX,0F000h/16 ;load big chunks at a time
- .if SI ae AX, E4 ;if not too much
- mov AX,SI ;read exactly enough
- E4: mov CL,4
- shl AX,CL
- mov CX,AX ;CX = # of bytes to read
- bdos 3Fh
- jc exeerror
- tst AX ;done loading file?
- jz E5 ;yes
- mov AX,DS
- add AX,0F000h/16
- mov DS,AX ;segment of where next chunk is going
- sub SI,0F000h/16
- ja E3 ;more to load
- E5:
-
- ;Start on the relocation items
- push CS
- pop DS ;DS:0 is the header
-
- ;Seek to where the relocation items are
- clr CX
- mov DX,DS:18h ;offset of first relocation item
- mov AX,4200h
- bdos
- jc exeerror
-
- .if <word ptr DS:6> e 0, E6 ;if no relocation items
- clr DX ;load relocation info into header
- mov CX,4 ;one relocation item is 4 bytes
- E7: bdos 3Fh ;read
- jc exeerror
- .if AX ne CX, exeerror
- mov AX,BP
- mov DI,DS:0 ;DI = relocation offset
- add AX,DS:2 ;AX = start segment + relocation segment
- mov ES,AX
- add ES:[DI],BP ;relocate with start segment value
- dec word ptr DS:6 ;relocation item count
- jne E7
-
- E6: bdos 3Eh ;close .EXE file
- jc exeerror
-
- add DS:16h,BP ;relocate code segment
- add DS:0Eh,BP ;relocate stack segment
- cli ;for bug in old 8088's
- mov SS,DS:0Eh
- mov SP,DS:10h ;stack of target executable
- sti ;for bug in old 8088's
- sub BP,100h/16 ;BP = segment of PSP
- mov DS,BP
- mov ES,BP ;DS and ES point to PSP
- jmp CS:dword ptr DS:14h ;jump to program
-
- exeerror:
- mov AX,04C01h ;return to DOS with error
- bdos
- $exeend:
- page
- ELSE
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;Use MS-DOS's exec function
-
- ;transfer routine to lowest address in program
- push ES ;save data segment value
- mov CX,offset $end - offset $start ;# of bytes to move
- segES
- mov AX,DGROUP:_psp ;AX = segment of _PSP
-
- if LCODE
- ;Starting code segment is 256 bytes past _psp
- add AX,10h ;+= 256 in paragraphs
- mov ES,AX
- clr DI ;ES:DI = start of code
- else
- mov BX,CS
- push CS ;only one code segment, and CS is it
- pop ES
- mov DI,100h ;starting offset for COM files
- .if AX e BX, EXEC1 ;COM file if (_PSP == CS)
- clr DI ;EXE file, offset is 0
- EXEC1: ;ES:DI = destination
- endif
-
- ;At this point, ES:DI -> start of code (just after _PSP)
-
- push CS
- pop DS
- mov SI,offset $start ;DS:SI = source
-
- cld
- rep movsb ;transfer
- pop DS ;DS = regular data segment value
-
- ;Transfer command tail to just after the routine
- mov comml,DI
- mov comml+2,ES ;new segment and offset of tail
- if SPTR
- mov SI,P+SIZEPTR[BP]
- else
- push DS
- lds SI,P+SIZEPTR[BP] ;DS:SI -> command tail
- endif
- mov CL,[SI]
- clr CH
- add CX,3 ;allow for count and \r and 0 at end
- rep movsb
- if LPTR
- pop DS
- endif
-
- ;Transfer fcbs to just after the command tail
- ;At this point:
- ; ES = segment of first code segment after PSP
- ; DI = offset into that
- ; DS = regular data segment
- mov pfcb1,DI
- mov pfcb2,DI
- add pfcb2,37
- mov pfcb1+2,ES
- mov pfcb2+2,ES
- mov SI,offset DGROUP:fcb1
- mov CX,37+37+(7*2)
- rep movsb
-
- mov AX,_psp ;get it before DS changes
-
- ;Get pointer to command before we move the stack
- if SPTR
- mov DX,P[BP] ; DS:DX -> command
- else
- lds DX,P[BP] ; DS:DX -> command
- endif
-
- ;Create a stack of 128 bytes right after this
- mov SI,DI
- add DI,128 + 1 ;stack + round
- and DI,0FFFEh ;round DI up to next word
- mov BX,ES
- cli ;for bug in old 8088's
- mov SS,BX
- mov SP,DI
- sti ;for bug in old 8088's
-
- push ES ;save segment of parameter block
- mov ES,AX ;segment of block to be modified (_psp)
- mov BX,DI
- add BX,100h+15 ;# of bytes in PSP + round up
- ;(assume that seg $progstart is
- ; just past the PSP)
- shr BX,1
- shr BX,1
- shr BX,1
- shr BX,1 ;convert to paragraphs
- ;public $test
- $test:
- if LCODE
- push AX
- mov AX,100h
- push AX
- .retf
- else
- mov CX,CS
- .if AX ne CX, EXEC2
- mov AX,100h ;.COM program
- push AX
- ret
-
- EXEC2: clr AX ;.EXE program
- push AX
- ret
- endif
- ; jmp $progstart
-
- $start:
- ;modify allocated block
- bdos 4Ah ;reset size of this program to ES:BX
-
- mov BX,SI
- sub BX,7*2 ; BX = offset of parameter block
- pop ES ; ES:BX -> parameter block
- mov AX,4B00h ;load and execute program
- bdos
- jnc L5 ;if no error
-
- mov AL,1 ;exit code for failure
- jmps L7
-
- L5: bdos 4Dh ;get return code
- L7: bdos 4Ch ;terminate with return code
- $end:
-
- ENDIF
-
- c_endp _exec
-
- endcode exec2
-
- end
-