home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
QBasic & Borland Pascal & C
/
Delphi5.iso
/
C
/
Samples
/
CASM.ARJ
/
EXEC2.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-07-27
|
14KB
|
571 lines
;_ 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