1235 lines
36 KiB
NASM
1235 lines
36 KiB
NASM
|
;
|
||
|
; DESCRIPTION
|
||
|
; This code is a model independent version of DOS exec that will swap
|
||
|
; the calling process out to secondary storage prior to running the
|
||
|
; child. The prototype for calling the exec function is below.
|
||
|
;
|
||
|
; exec( int swap, char far *program, char far *cmdtail,
|
||
|
; int environment_seg, char far *tmpfilename );
|
||
|
;
|
||
|
;
|
||
|
; To assemble this file issue the command:
|
||
|
;
|
||
|
; tasm /mx /t /dmmodel exec.asm
|
||
|
;
|
||
|
; where 'model' is one of {small, compact, medium, large}, you may
|
||
|
; also use MASM 5.1 to assemble this file, in this case simply replace
|
||
|
; 'tasm' with 'masm' in the above command line.
|
||
|
;
|
||
|
; AUTHOR
|
||
|
; Dennis Vadura, dvadura@watdragon.uwaterloo.ca
|
||
|
; CS DEPT, University of Waterloo, Waterloo, Ont., Canada
|
||
|
;
|
||
|
; COPYRIGHT
|
||
|
; Copyright (c) 1990 by Dennis Vadura. All rights reserved.
|
||
|
;
|
||
|
; This program is free software; you can redistribute it and/or
|
||
|
; modify it under the terms of the GNU General Public License
|
||
|
; (version 1), as published by the Free Software Foundation, and
|
||
|
; found in the file 'LICENSE' included with this distribution.
|
||
|
;
|
||
|
; This program is distributed in the hope that it will be useful,
|
||
|
; but WITHOUT ANY WARRANTY; without even the implied warrant of
|
||
|
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
; GNU General Public License for more details.
|
||
|
;
|
||
|
; You should have received a copy of the GNU General Public License
|
||
|
; along with this program; if not, write to the Free Software
|
||
|
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
;
|
||
|
ifdef have286
|
||
|
.286 ; define have286 with -D for 80286 processor or better
|
||
|
mpusha Macro
|
||
|
pusha
|
||
|
Endm
|
||
|
|
||
|
mpopa Macro
|
||
|
popa
|
||
|
Endm
|
||
|
|
||
|
else ; 8088/8086 compatible
|
||
|
mpusha Macro
|
||
|
push ax
|
||
|
push cx
|
||
|
push dx
|
||
|
push bx
|
||
|
push sp
|
||
|
push bp
|
||
|
push si
|
||
|
push di
|
||
|
Endm
|
||
|
|
||
|
mpopa Macro
|
||
|
pop di
|
||
|
pop si
|
||
|
pop bp
|
||
|
add sp,2
|
||
|
pop bx
|
||
|
pop dx
|
||
|
pop cx
|
||
|
pop ax
|
||
|
Endm
|
||
|
endif
|
||
|
|
||
|
ifdef msmall
|
||
|
.model small
|
||
|
argbase equ 4
|
||
|
endif
|
||
|
ifdef mcompact
|
||
|
.model compact
|
||
|
argbase equ 4
|
||
|
endif
|
||
|
ifdef mmedium
|
||
|
.model medium
|
||
|
argbase equ 6
|
||
|
endif
|
||
|
ifdef mlarge
|
||
|
.model large
|
||
|
argbase equ 6
|
||
|
endif
|
||
|
a_swap equ <bp+argbase+0>
|
||
|
a_prog equ <bp+argbase+2>
|
||
|
a_tail equ <bp+argbase+6>
|
||
|
a_env equ <bp+argbase+10>
|
||
|
a_tmp equ <bp+argbase+12>
|
||
|
|
||
|
a_handle equ <bp+argbase>
|
||
|
|
||
|
|
||
|
; Define all useful equ's
|
||
|
swap_xms equ 0 ; we swapped it out to xms
|
||
|
swap_ems equ 2 ; we swapped it out to ems
|
||
|
swap_file equ 4 ; we swapped it out to a file
|
||
|
seg_no_alloc equ 0 ; this is part of a segment
|
||
|
seg_alloc equ 1 ; this is a full segment header
|
||
|
seg_data equ 2 ; this is data for part of a segment
|
||
|
|
||
|
|
||
|
; Define any global/external variables that we will be accessing from here.
|
||
|
.data
|
||
|
extrn _errno:word ; Set to dos ret code from exec
|
||
|
public _Interrupted ; Set to 1 if interrupted 0
|
||
|
_Interrupted dw 0 ; otherwise
|
||
|
|
||
|
.code
|
||
|
assume cs:@code, ds:@code, ss:@code, es:@code
|
||
|
|
||
|
even
|
||
|
execstack dw 64 dup (?) ; put the temporary exec stack right
|
||
|
exec_sp label word ; at the start.
|
||
|
|
||
|
old_ss dw ? ; save stack seg across exec
|
||
|
old_sp dw ? ; save stack ptr across exec
|
||
|
progsize dw ? ; original size of the program
|
||
|
rootsize dw ? ; size of base root kept during swap
|
||
|
resend dw ? ; paragraph where resident code ends
|
||
|
envseg dw ? ; paragraph of environment segment
|
||
|
psp dw ? ; our own psp
|
||
|
swap dw ? ; swapping selection flag
|
||
|
eretcode dw ? ; return code from exec
|
||
|
interrupted dw ? ; interrupted flag for exec
|
||
|
arenahead dw ? ; start of memory block list
|
||
|
alstr dw ? ; allocation strategy save spot
|
||
|
in_exec dw 0 ; flag, 1 ==> in exec
|
||
|
|
||
|
cmdpath db 65 dup(?) ; file to exec
|
||
|
cmdtail db 129 dup(?) ; its command tail
|
||
|
fcb db 37 dup(0) ; dummy fcb
|
||
|
tmpseg db 7 dup(?) ; block header buffer
|
||
|
|
||
|
tmpname db 65 dup(0) ; name of temporary file resource
|
||
|
|
||
|
even
|
||
|
tmphandle dw ? ; handle for temporary file
|
||
|
real_21h dd 0 ; will be DOS's 21h vector if doing -C
|
||
|
|
||
|
std_fil_handle dw ? ; file handle for -C file
|
||
|
std_fil_number db ? ; system file number for -C file
|
||
|
our_stdout db ? ; sys file number our stdout handle
|
||
|
|
||
|
error_rhdr db "exec: Failure reading header block", 0DH, 0AH, '$'
|
||
|
error_rseg db "exec: Failure reading segment data", 0DH, 0AH, '$'
|
||
|
error_resize db "exec: Failure on resize", 0DH, 0AH, '$'
|
||
|
error_free db "exec: Failure to free a block", 0DH, 0AH, '$'
|
||
|
error_string db "exec: Program swap failure", 0DH, 0AH, '$'
|
||
|
error_alloc db "exec: Memory blocks don't match", 0DH, 0AH, '$'
|
||
|
|
||
|
even
|
||
|
write_header label word
|
||
|
whdr_xms_ptr dw word ptr whdr_xms
|
||
|
whdr_ems_ptr dw word ptr whdr_ems
|
||
|
whdr_file_ptr dw word ptr whdr_file
|
||
|
|
||
|
write_seg label word
|
||
|
wseg_xms_ptr dw word ptr wseg_xms
|
||
|
wseg_ems_ptr dw word ptr wseg_ems
|
||
|
wseg_file_ptr dw word ptr wseg_file
|
||
|
|
||
|
read_header label word
|
||
|
rhdr_xms_ptr dw word ptr rhdr_xms
|
||
|
rhdr_ems_ptr dw word ptr rhdr_ems
|
||
|
rhdr_file_ptr dw word ptr rhdr_file
|
||
|
|
||
|
read_seg label word
|
||
|
rseg_xms_ptr dw word ptr rseg_xms
|
||
|
rseg_ems_ptr dw word ptr rseg_ems
|
||
|
rseg_file_ptr dw word ptr rseg_file
|
||
|
|
||
|
free_resource label word
|
||
|
free_xms_ptr dw word ptr free_xms_resource
|
||
|
free_ems_ptr dw word ptr free_ems_resource
|
||
|
free_file_ptr dw word ptr free_file_resource
|
||
|
|
||
|
reset_resource label word
|
||
|
reset_xms_ptr dw word ptr reset_xms_resource
|
||
|
reset_ems_ptr dw word ptr reset_ems_resource
|
||
|
reset_file_ptr dw word ptr reset_file_resource
|
||
|
|
||
|
old_ctl_brk label dword
|
||
|
old_ctl_brk_off dw ?
|
||
|
old_ctl_brk_seg dw ?
|
||
|
|
||
|
old_crit_err label dword
|
||
|
old_crit_err_off dw ?
|
||
|
old_crit_err_seg dw ?
|
||
|
|
||
|
exec_block label word
|
||
|
ex_envseg dw ? ; env seg, use parent's if 0
|
||
|
ex_cmdtail dd ? ; command tail for exec
|
||
|
ex_fcb1 dd far ptr fcb ; fcb's aren't used by dmake
|
||
|
ex_fcb2 dd far ptr fcb
|
||
|
ex_ss dw ? ; saved ss for exec
|
||
|
ex_sp dw ? ; saved sp for exec
|
||
|
ex_error dw 0 ; error code for dos exec
|
||
|
|
||
|
|
||
|
; Special 21h (DOS call) handler to tee stdout/stderr writes to the -C file.
|
||
|
; Ignore 21h calls that aren't writes to 1 or 2; i.e., pass them to DOS handler.
|
||
|
; If write call was from this process, it's pretty simple to duplicate it
|
||
|
; to the -C file. If it's from another process, we try to write to its
|
||
|
; inherited handle. Worst case is where the handle wasn't inherited: someone
|
||
|
; closed it. In that instance we have to switch to dmake's PSP to do the
|
||
|
; duplicate write.
|
||
|
|
||
|
; Subprocesses do not get their stdout/stderr teed to the -C file if
|
||
|
; their stdout/stderr no longer points to the file/device that dmake's
|
||
|
; stdout points to. This is tested by looking at the process's job
|
||
|
; file table, which is a table that maps process handles to DOS system file
|
||
|
; table numbers. (The far pointer to the JFT is at the PSP offset 34h.)
|
||
|
; The JFT is also queried to see if the -C file was inherited.
|
||
|
|
||
|
; O_BINARY, O_TEXT problems are ignored here. These are fudged by the
|
||
|
; C library before it calls DOS; since we're working below that level
|
||
|
; we don't have to worry about it.
|
||
|
|
||
|
simulate_21h Macro
|
||
|
pushf ;; direct call to DOS
|
||
|
call cs:[real_21h]
|
||
|
Endm
|
||
|
|
||
|
assume cs:@code, ds:nothing, es:nothing, ss:nothing
|
||
|
our_21h_handler proc far
|
||
|
pushf
|
||
|
cmp ah,40h ; is this a write?
|
||
|
jne call_dos ; --no
|
||
|
cmp bx,1 ; write on handle 1 (stdout?)
|
||
|
je duplicate_it
|
||
|
cmp bx,2 ; stderr?
|
||
|
je duplicate_it
|
||
|
|
||
|
call_dos:
|
||
|
popf
|
||
|
jmp [real_21h] ; far jump to real handler, which will do the sys call
|
||
|
; and return to the original caller
|
||
|
|
||
|
duplicate_it:
|
||
|
mpusha
|
||
|
push ds
|
||
|
push es
|
||
|
mov bp,sp
|
||
|
|
||
|
mov di,std_fil_handle ; handle of the -C file
|
||
|
|
||
|
If @CodeSize eq 0
|
||
|
; Small/compact models allow for quick test of us versus subprocess.
|
||
|
; False negative (it's us with a different CS) will be picked
|
||
|
; up by code just below. (Might happen due to call from C library.)
|
||
|
; False positives would be bad, but can't happen.
|
||
|
mov ax,[bp+24] ; caller's CS
|
||
|
cmp ax,@code ; same as us?
|
||
|
je call_from_dmake
|
||
|
Endif
|
||
|
|
||
|
mov ah,51h ; get PSP ("undocumented version" works in DOS 2.0+)
|
||
|
simulate_21h ; PSP segment returned in BX
|
||
|
cmp bx,psp ; our PSP?
|
||
|
je call_from_dmake ; --yes, no PSP changing needed
|
||
|
|
||
|
mov es,bx ; set ES to current (caller's) PSP
|
||
|
lds bx,es:[34h] ; set DS:BX pointing to caller's job file table
|
||
|
|
||
|
mov si,[bp+12] ; file handle caller passed in (known to be 1 or 2)
|
||
|
mov al,[bx+si] ; system file number corresponding to caller's handle
|
||
|
cmp al,our_stdout ; same as our stdout?
|
||
|
jne do_real_write ; no--subprocess must have redirected it
|
||
|
|
||
|
mov al,[bx+di] ; see if caller has dup of -C file still open
|
||
|
cmp al,std_fil_number
|
||
|
je use_dup ; yes--we can write using caller's PSP
|
||
|
|
||
|
; Calling process (or some intermediate process) has closed
|
||
|
; the -C descriptor. We'll use dmake's (our) -C descriptor, but
|
||
|
; to do so we'll have to change the PSP. Disable BREAK handling
|
||
|
; so that ^break doesn't kill the wrong process.
|
||
|
|
||
|
mov ax,3300h ; get BREAK flag
|
||
|
simulate_21h
|
||
|
mov si,dx ; save BREAK state in SI
|
||
|
sub dx,dx ; now turn break flag off
|
||
|
mov ax,3301h
|
||
|
simulate_21h ; don't want ^Break recoginized while PSP changed
|
||
|
mov bx,psp ; set dmake's PSP
|
||
|
mov ah,50h
|
||
|
simulate_21h
|
||
|
|
||
|
mov bx,di ; handle of -C file
|
||
|
; CX still has caller's count
|
||
|
mov ds,[bp+2] ; restore caller's DS
|
||
|
mov dx,[bp+14] ; DS:DX again points to caller's buffer
|
||
|
mov ah,40h
|
||
|
simulate_21h ; write the copy
|
||
|
|
||
|
mov bx,es ; caller's PSP
|
||
|
mov ah,50h ; set PSP
|
||
|
simulate_21h ; restore caller's PSP
|
||
|
mov dx,si ; break state before we changed it
|
||
|
mov ax,3301h
|
||
|
simulate_21h ; restore break state
|
||
|
|
||
|
jmp short do_real_write
|
||
|
|
||
|
use_dup:
|
||
|
mov ds,[bp+2] ; restore caller's DS
|
||
|
mov dx,[bp+14] ; DS:DX again points to caller's buffer
|
||
|
|
||
|
call_from_dmake:
|
||
|
mov bx,di ; handle of -C file
|
||
|
mov ah,40h ; write
|
||
|
; CX still has caller's count
|
||
|
simulate_21h ; write to the file
|
||
|
|
||
|
do_real_write:
|
||
|
pop es
|
||
|
pop ds
|
||
|
mpopa
|
||
|
popf
|
||
|
jmp [real_21h] ; far jump to real handler, which will do the sys call
|
||
|
; and return to the original caller
|
||
|
our_21h_handler endp
|
||
|
|
||
|
assume cs:@code, ds:@code, ss:@code, es:@code
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; First define the critical-error and control-brk handlers.
|
||
|
; The critical error handler simply pops the machine state and returns an
|
||
|
; access denied result code.
|
||
|
crit_err_handler proc far
|
||
|
add sp, 6 ; ip/cs/flags ...
|
||
|
pop ax
|
||
|
pop bx
|
||
|
pop cx
|
||
|
pop dx
|
||
|
pop si
|
||
|
pop di
|
||
|
pop bp
|
||
|
pop ds
|
||
|
pop es
|
||
|
push bp ; fix up the return flags
|
||
|
mov bp, sp
|
||
|
xchg ax, [bp+6] ; get the flag byte.
|
||
|
or ax, 1 ; set the carry bit
|
||
|
xchg ax, [bp+6] ; put it back.
|
||
|
pop bp
|
||
|
mov ax, 5 ; access denied
|
||
|
iret
|
||
|
crit_err_handler endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; Here we set the interrupted flag, and terminate the currently running
|
||
|
; process.
|
||
|
ctl_brk_handler proc far
|
||
|
clc ; make sure carry is clear
|
||
|
inc cs:interrupted ; set the flag
|
||
|
|
||
|
; Make certain it isn't us that is going to get terminated.
|
||
|
; There is a small window where the in_exec flag is set but the child is
|
||
|
; not running yet, I assume that DOS doesn't test for ctl_brk at that time
|
||
|
; as it is bussily creating a new process.
|
||
|
cmp cs:in_exec,0
|
||
|
je just_return ; note this implies CF == 0
|
||
|
stc ; set CF to abort child
|
||
|
just_return: iret
|
||
|
ctl_brk_handler endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; Something really nasty happened, so abort the exec call and exit.
|
||
|
; This kills the calling process altogether, and is a very nasty way of
|
||
|
; termination since files may still be open etc.
|
||
|
abort_exec_rhdr label near
|
||
|
mov dx, offset error_rhdr
|
||
|
jmp print_it
|
||
|
abort_exec_rseg label near
|
||
|
mov dx, offset error_rseg
|
||
|
jmp print_it
|
||
|
abort_exec_resize label near
|
||
|
mov dx, offset error_resize
|
||
|
jmp print_it
|
||
|
abort_exec_free label near
|
||
|
mov dx, offset error_free
|
||
|
jmp print_it
|
||
|
abort_exec_alloc label near
|
||
|
mov dx, offset error_alloc
|
||
|
jmp print_it
|
||
|
abort_exec proc near
|
||
|
mov dx, offset error_string
|
||
|
print_it: push dx
|
||
|
mov bx, [swap]
|
||
|
call [free_resource+bx]
|
||
|
mov ax, cs
|
||
|
mov ds, ax
|
||
|
pop dx
|
||
|
mov ah, 9
|
||
|
int 21H
|
||
|
kill_program: mov ax, 04cffH ; nuke it!
|
||
|
int 21H
|
||
|
abort_exec endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; lodsw/stosw loop to copy data. Called only for word copy operations.
|
||
|
; ds:si - point at source
|
||
|
; es:di - point at destination
|
||
|
; cx - count of bytes to copy.
|
||
|
copy_data proc near
|
||
|
shr cx, 1 ; convert to word count
|
||
|
jnc copy_words
|
||
|
movsb
|
||
|
copy_words: rep movsw ; copy the words.
|
||
|
ret
|
||
|
copy_data endp
|
||
|
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ XMS RECORDS.
|
||
|
;=============================================================================
|
||
|
rhdr_xms proc near
|
||
|
ret
|
||
|
rhdr_xms endp
|
||
|
|
||
|
rseg_xms proc near
|
||
|
ret
|
||
|
rseg_xms endp
|
||
|
|
||
|
reset_xms_resource proc near
|
||
|
ret
|
||
|
reset_xms_resource endp
|
||
|
|
||
|
free_xms_resource proc near
|
||
|
ret
|
||
|
free_xms_resource endp
|
||
|
;=============================================================================
|
||
|
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ EMS RECORDS.
|
||
|
;=============================================================================
|
||
|
rhdr_ems proc near
|
||
|
ret
|
||
|
rhdr_ems endp
|
||
|
|
||
|
rseg_ems proc near
|
||
|
ret
|
||
|
rseg_ems endp
|
||
|
|
||
|
reset_ems_resource proc near
|
||
|
ret
|
||
|
reset_ems_resource endp
|
||
|
|
||
|
free_ems_resource proc near
|
||
|
ret
|
||
|
free_ems_resource endp
|
||
|
;=============================================================================
|
||
|
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ FILE RECORDS.
|
||
|
;=============================================================================
|
||
|
; This routine reads a segment header from a file.
|
||
|
; The header is a seven byte record formatted as follows:
|
||
|
; segment address - of data
|
||
|
; offset address - of data
|
||
|
; length in paragraphs - of data
|
||
|
; mode - 1 => segment header (allocate seg on read)
|
||
|
; 0 => subsegment, don't allocate on read.
|
||
|
; The information is placed into the tmpseg data area in the code segment.
|
||
|
; The routine aborts if an error is detected.
|
||
|
rhdr_file proc near
|
||
|
mov dx, offset tmpseg ; read the header record out
|
||
|
mov cx, 7
|
||
|
mov bx, [tmphandle]
|
||
|
mov ah, 03fH
|
||
|
int 21H
|
||
|
jnc rhdr_done ; make sure it worked
|
||
|
jmp abort_exec_rhdr
|
||
|
|
||
|
rhdr_done: cmp ax, 7
|
||
|
je exit_rhdr_file
|
||
|
or ax, ax
|
||
|
je signal_eof
|
||
|
jmp abort_exec_rhdr
|
||
|
|
||
|
signal_eof: stc
|
||
|
exit_rhdr_file: ret
|
||
|
rhdr_file endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; Read a segment from the temporary file whose handle is in cs:tmphandle.
|
||
|
; The routine aborts if an error is detected.
|
||
|
rseg_file proc near
|
||
|
push ds
|
||
|
mov ds, word ptr cs:tmpseg; Now read the whole segment
|
||
|
mov dx, word ptr cs:tmpseg+2
|
||
|
mov cx, word ptr cs:tmpseg+4
|
||
|
mov bx, cs:tmphandle
|
||
|
mov ah, 03fH
|
||
|
int 21H
|
||
|
pop ds
|
||
|
jnc rseg_done
|
||
|
jmp abort_exec_rseg
|
||
|
|
||
|
rseg_done: cmp ax, [word ptr tmpseg+4]
|
||
|
je exit_rseg_file
|
||
|
jmp abort_exec_rseg ; If we didn't get read full
|
||
|
exit_rseg_file: ret ; segment then abort
|
||
|
rseg_file endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; Seek to the beginning of the file.
|
||
|
reset_file_resource proc near
|
||
|
mov bx, [tmphandle]
|
||
|
xor cx, cx
|
||
|
mov dx, cx
|
||
|
mov ax, 04200H ; seek to begining of file
|
||
|
int 21H
|
||
|
ret
|
||
|
reset_file_resource endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; unlink the temporary file allocated for swapping.
|
||
|
; We close the file first, and then delete it. We ignore errors here since
|
||
|
; we can't do anything about them anyway.
|
||
|
free_file_resource proc near
|
||
|
mov bx, [tmphandle] ; get the file handle
|
||
|
mov ah, 03eH ; close the file
|
||
|
int 21H
|
||
|
mov dx, offset tmpname ; Now delete the temp file
|
||
|
mov ah, 041H
|
||
|
int 21H
|
||
|
ret
|
||
|
free_file_resource endp
|
||
|
;=============================================================================
|
||
|
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; CODE TO SWAP THE IMAGE IN FROM SECONDARY STORAGE
|
||
|
;=============================================================================
|
||
|
swap_in proc near
|
||
|
mov bx, [alstr] ; get previous alloc strategy
|
||
|
mov ax, 5801H ; and set it back
|
||
|
int 21H
|
||
|
mov bx, [swap] ; get type of resource
|
||
|
call [reset_resource+bx] ; reset the resource
|
||
|
mov es, [psp] ; resize the program back
|
||
|
mov bx, [progsize] ; to original size
|
||
|
mov ah, 04AH
|
||
|
int 21H
|
||
|
jnc read_seg_loop
|
||
|
jmp abort_exec
|
||
|
|
||
|
read_seg_loop: mov bx, [swap] ; get type of resource
|
||
|
call [read_header+bx] ; get seg header
|
||
|
jc exit_swap_in ; all done
|
||
|
mov al, [tmpseg+6]
|
||
|
cmp al, seg_no_alloc ; see if dummy segment header
|
||
|
je read_seg_loop
|
||
|
cmp al, seg_alloc ; do we need to do an alloc?
|
||
|
jne read_data ; nope
|
||
|
|
||
|
; Allocate back the memory for a segment that is not the [psp], note that this
|
||
|
; must come back to the same segment we had previously since other segments
|
||
|
; may have pointers stored in their variables that point to this segment using
|
||
|
; segment:offset long pointers.
|
||
|
mov bx, [word ptr tmpseg+4] ; get count of paragraphs
|
||
|
mov ah, 048H ; dos_alloc
|
||
|
int 21H
|
||
|
jc alloc_error ; oops!
|
||
|
cmp ax, [word ptr tmpseg] ; did we get the same segment?
|
||
|
je read_seg_loop ; yup!
|
||
|
alloc_error: jmp abort_exec_alloc
|
||
|
|
||
|
read_data: mov bx, [swap]
|
||
|
call [read_seg+bx] ; this must succeed, if fail
|
||
|
jmp read_seg_loop ; we never come back here
|
||
|
|
||
|
exit_swap_in: mov bx, [swap] ; all done, so free resource
|
||
|
call [free_resource+bx]
|
||
|
ret
|
||
|
swap_in endp
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; CODE TO SWAP THE IMAGE OUT TO SECONDARY STORAGE
|
||
|
;=============================================================================
|
||
|
; This routine is called to swap the non-resident portion of the program
|
||
|
; out to the resource specified by the value of [cs:swap]. If the swap out
|
||
|
; fails, then appropriate routines are called to free the resources allocated
|
||
|
; up to that point.
|
||
|
;
|
||
|
; The steps used to swap the program out are as follows:
|
||
|
; - calculate new size of program to remain resident and size to swap
|
||
|
; out.
|
||
|
; - write out non-resident portion of current segment
|
||
|
; - walk DOS allocation chain and write out all other segments owned by
|
||
|
; the current program that are contiguous with the _psp segment
|
||
|
; - copy the environment down to low memory
|
||
|
; - resize the current _psp segment to savesize
|
||
|
; - free all segments belonging to program except current _psp segment
|
||
|
swap_out proc near
|
||
|
mov ax, 05800H ; get memory alocation strategy
|
||
|
int 021H
|
||
|
mov [alstr], ax ; and save it for future restoration.
|
||
|
mov di, [psp] ; compute length of program to current
|
||
|
mov bx, cs ; value of cs, and find program size
|
||
|
sub bx, di ; by looking at length stored in
|
||
|
mov ax, di ; arena header found in front of psp
|
||
|
dec ax
|
||
|
mov es, ax
|
||
|
mov si, es:3 ; si is size of program in paragraphs
|
||
|
mov [progsize], si ; progsize now contains the size.
|
||
|
|
||
|
; Now compute length of program segment to save.
|
||
|
; Length is: cs - psp + (offset overlay_code_here+15 >> 4)
|
||
|
mov ax, offset overlay_code_here+15
|
||
|
shr ax, 1
|
||
|
shr ax, 1
|
||
|
shr ax, 1
|
||
|
shr ax, 1
|
||
|
add bx, ax ; bx is size of program to keep
|
||
|
sub si, bx ; si is # of paragraphs to save.
|
||
|
add di, bx ; di is paragraph to start at
|
||
|
mov rootsize, bx
|
||
|
mov resend, di ; cs:resend is saved start para
|
||
|
mov al, seg_no_alloc ; set no allocation for segment
|
||
|
call write_segment
|
||
|
jc abort_swap_out
|
||
|
|
||
|
; We have now saved the portion of the program segment that will not remain
|
||
|
; resident during the exec. We should now walk the DOS allocation chain and
|
||
|
; write out all other segments owned by the current process.
|
||
|
save_segments: mov ax, [psp]
|
||
|
dec ax
|
||
|
mov es, ax
|
||
|
mov bx, offset write_segment_data
|
||
|
call walk_arena_chain
|
||
|
jc abort_swap_out
|
||
|
|
||
|
; Now we must walk the chain of allocated memory blocks again and free
|
||
|
; all those that are owned by the current process, except the one that is
|
||
|
; the current process' psp.
|
||
|
free_segments: mov ax, [psp]
|
||
|
dec ax
|
||
|
mov es,ax
|
||
|
mov bx, offset free_dos_segment
|
||
|
call walk_arena_chain
|
||
|
jnc resize_program
|
||
|
jmp abort_exec_free ; can't fix it up now.
|
||
|
|
||
|
; We now resize the program to the size specified by cs:rootsize. This will
|
||
|
; free most of the memory taken up by the current program segment.
|
||
|
resize_program: mov es, [psp] ; es is segment to resize.
|
||
|
mov bx, [rootsize] ; bx is size of segment.
|
||
|
mov ah, 04aH ; resize memory block
|
||
|
int 21H
|
||
|
jnc swap_out_ok
|
||
|
jmp abort_exec_resize ; disaster
|
||
|
swap_out_ok: ret
|
||
|
|
||
|
; The swap out failed for some reason, so free any allocated resources
|
||
|
; and set the carry bit.
|
||
|
abort_swap_out: mov bx, [swap]
|
||
|
call [free_resource+bx]
|
||
|
xor ax, ax
|
||
|
mov [swap], ax ; clear the swap flag
|
||
|
stc
|
||
|
ret
|
||
|
swap_out endp
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; CODE TO SET-UP FOR AND EXEC THE CHILD PROCESS
|
||
|
;=============================================================================
|
||
|
; Actually execute the program. If cs:swap is set, this code will invoke the
|
||
|
; swap-out/swap-in code as required.
|
||
|
do_exec proc near
|
||
|
cmp [swap], 0 ; does the user want to swap?
|
||
|
je no_swap_out ; nope
|
||
|
call init_swap ; figger out where to swap to
|
||
|
jc no_swap_out ; if carry set then don't swap
|
||
|
call swap_out
|
||
|
|
||
|
no_swap_out: cmp [interrupted], 0 ; were we interrupted?
|
||
|
jne leave_exec ; yep, so clean up, don't exec
|
||
|
|
||
|
; free passed in environment block if it is non zero.
|
||
|
; This way the parent program does not need to free it.
|
||
|
mov ax, [envseg]
|
||
|
or ax, ax
|
||
|
je setup_block
|
||
|
push ax
|
||
|
mov es, ax
|
||
|
mov ah, 49H
|
||
|
int 21H
|
||
|
pop ax
|
||
|
|
||
|
; set up the parameter block for the DOS exec call.
|
||
|
; offset contents
|
||
|
; 00 segment address of environment to be passed,
|
||
|
; 0 => use parents env.
|
||
|
; 02 pointer to command tail for new process.
|
||
|
; 06 pointer to fcb1
|
||
|
; 0a pointer to fcb2
|
||
|
setup_block: mov ax, [envseg]
|
||
|
mov [ex_envseg], ax
|
||
|
mov cx, cs
|
||
|
mov [word ptr ex_cmdtail], offset cmdtail
|
||
|
mov [word ptr ex_cmdtail+2], cx
|
||
|
|
||
|
; set up registers for exec call
|
||
|
; ds:dx - pointer to pathname of program to execute
|
||
|
; es:bx - pointer to above parameter block
|
||
|
mov dx, offset cmdpath
|
||
|
mov es, cx
|
||
|
mov bx, offset exec_block
|
||
|
|
||
|
; Under DOS 2.x exec is notorious for clobbering registers and guarantees
|
||
|
; to preserve only cs:ip.
|
||
|
push ds
|
||
|
mov [ex_sp], sp
|
||
|
mov [ex_ss], ss
|
||
|
mov [ex_error], 0 ; clear exec error code
|
||
|
inc [in_exec] ; set internal flag
|
||
|
mov ax, 04b00H
|
||
|
int 21H
|
||
|
|
||
|
; returned from exec, so restore possibly clobbered registers.
|
||
|
mov ss, cs:ex_ss
|
||
|
mov sp, cs:ex_sp
|
||
|
pop ds
|
||
|
|
||
|
; check to make certain the exec call worked.
|
||
|
jnc it_worked
|
||
|
|
||
|
; exec call failed. Save return code from msdos.
|
||
|
mov [ex_error], ax
|
||
|
jmp leave_exec
|
||
|
|
||
|
it_worked: mov ah, 04dH ; get the return code
|
||
|
int 21H
|
||
|
cmp ah,1 ; check if terminated by ^C
|
||
|
jnz nosigint
|
||
|
inc interrupted ; yes so set flag
|
||
|
nosigint: xor ah, ah ; 8-bit return code, so clear ah
|
||
|
mov [eretcode], ax
|
||
|
|
||
|
leave_exec: cmp [swap], 0 ; check swap, if non-zero swap back in
|
||
|
je no_swap_in
|
||
|
call swap_in
|
||
|
|
||
|
; Clear the in_exec after the swap back in. This way we are guaranteed to
|
||
|
; get parent in and the resources freed should a ^C be hit when we are reading
|
||
|
; the image in.
|
||
|
no_swap_in: mov [in_exec], 0
|
||
|
ret
|
||
|
do_exec endp
|
||
|
|
||
|
|
||
|
|
||
|
;==============================================================================
|
||
|
; Everything past this point is overwriten with the environment and new
|
||
|
; program after the currently executing program is swapped out.
|
||
|
;==============================================================================
|
||
|
overlay_code_here label word
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; Figure out where we can swap to and initialize the resource we are going to
|
||
|
; use. We try XMS, EMS, and a tempfile (if specified), in that order. We set
|
||
|
; [cs:swap] to the correct value based on which of the resources exists.
|
||
|
; If none can be used, then [cs:swap] is set to 0, and no swap takes place.
|
||
|
; The exec code will still attempt to execute the child in this instance, but
|
||
|
; may fail due to lack of resources. Each swap_out_* routine must provide
|
||
|
; its own clean-up handler should it not be able to write all program
|
||
|
; segments to the swap resource.
|
||
|
init_swap proc near
|
||
|
mov [swap], 0
|
||
|
;call init_xms
|
||
|
;jnc init_done
|
||
|
;call init_ems
|
||
|
;jnc init_done
|
||
|
call init_file
|
||
|
init_done: ret
|
||
|
init_swap endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; This routine is used to walk the DOS allocated memory block chain
|
||
|
; starting at address supplied in the es register. For each block it
|
||
|
; calls the routine specified by the bx register with the segment length
|
||
|
; in si, and its address in di. It does not apply the routine to the
|
||
|
; segment if the segment is the same as the current program's [cs:psp] value.
|
||
|
memheader struc
|
||
|
magic db ? ; either 'Z' for end or 'M' for allocated
|
||
|
owner dw ? ; psp of owner block
|
||
|
len dw ? ; length in paragraphs of segment
|
||
|
memheader ends
|
||
|
|
||
|
walk_arena_chain proc near
|
||
|
mov si, word ptr es:3 ; get length
|
||
|
mov di, es
|
||
|
inc di
|
||
|
mov ax, word ptr es:1
|
||
|
|
||
|
; Stop the search if the block is NOT owned by us. Ignore our own psp block
|
||
|
; and our environment segment block.
|
||
|
cmp ax, cs:psp ; is it owned by us?
|
||
|
jne walk_done ; NOPE! -- all done
|
||
|
cmp di, cs:envseg ; skip our environment
|
||
|
je next_block
|
||
|
cmp di, cs:psp ; skip our psp
|
||
|
je next_block
|
||
|
|
||
|
; Now save state and call the routine pointed at by [bx].
|
||
|
push di
|
||
|
push si
|
||
|
push bx
|
||
|
call bx
|
||
|
pop bx
|
||
|
pop si
|
||
|
pop di
|
||
|
jc exit_walk ; if error then stop
|
||
|
mov al, byte ptr es:0 ; check if at end
|
||
|
cmp al, 'Z'
|
||
|
je walk_done
|
||
|
|
||
|
next_block: add di, si ; go on to next segment
|
||
|
mov es, di
|
||
|
jmp walk_arena_chain
|
||
|
walk_done: clc
|
||
|
exit_walk: ret
|
||
|
walk_arena_chain endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; This routine takes a dos segment found in the di register and free's it.
|
||
|
free_dos_segment proc near
|
||
|
mov es, di ; free dos memory block
|
||
|
mov ah, 49H
|
||
|
int 21H
|
||
|
ret
|
||
|
free_dos_segment endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; Called to invoke write_segment with proper values in the al register. Only
|
||
|
; ever called from walk_arena_chain, and so al should be set to seg_alloc.
|
||
|
write_segment_data label near
|
||
|
mov al, seg_alloc ; and fall through into write_segment
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; This routine writes a segment as a block of data segments if the number of
|
||
|
; paragraphs to write exceeds 0x0fff (rarely the case).
|
||
|
; It stuffs the info into tmpseg, and then calls wheader and wseg to get the
|
||
|
; data out.
|
||
|
;
|
||
|
; di:dx segment:offset of segment; offset is ALWAYS zero.
|
||
|
; si number of paragraphs to write.
|
||
|
; al mode of header to write
|
||
|
write_segment proc near
|
||
|
push di
|
||
|
push si
|
||
|
xor dx,dx
|
||
|
mov bx, [swap]
|
||
|
call [write_header+bx]
|
||
|
pop si
|
||
|
pop di
|
||
|
jc exit_wseg
|
||
|
|
||
|
do_io_loop: cmp si, 0 ; are we done yet?
|
||
|
je exit_wseg ; yup so leave.
|
||
|
mov cx, si ; # of paragraphs to move
|
||
|
cmp cx, 0fffH ; see if we have lots to move?
|
||
|
jle do_io
|
||
|
mov cx, 0fffH ; reset to max I/O size
|
||
|
|
||
|
do_io: push cx ; save # of paragraphs we are writing
|
||
|
shl cx, 1 ; shift cx by four to the left
|
||
|
shl cx, 1
|
||
|
shl cx, 1
|
||
|
shl cx, 1
|
||
|
push di ; save the start, and count left
|
||
|
push si
|
||
|
mov si, cx
|
||
|
xor dx,dx
|
||
|
mov al, seg_data
|
||
|
mov bx, [swap]
|
||
|
push bx
|
||
|
call [write_header+bx]
|
||
|
pop bx
|
||
|
call [write_seg+bx]
|
||
|
pop si
|
||
|
pop di
|
||
|
pop dx ; original paragraph count in dx
|
||
|
jc exit_wseg ; it failed so exit.
|
||
|
add di, dx ; adjust the pointers, and continue.
|
||
|
sub si, dx
|
||
|
jmp do_io_loop
|
||
|
exit_wseg: ret
|
||
|
write_segment endp
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE XMS RECORDS.
|
||
|
;=============================================================================
|
||
|
init_xms proc near
|
||
|
ret
|
||
|
init_xms endp
|
||
|
|
||
|
whdr_xms proc near
|
||
|
ret
|
||
|
whdr_xms endp
|
||
|
|
||
|
wseg_xms proc near
|
||
|
ret
|
||
|
wseg_xms endp
|
||
|
;=============================================================================
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE EMS RECORDS.
|
||
|
;=============================================================================
|
||
|
init_ems proc near
|
||
|
ret
|
||
|
init_ems endp
|
||
|
|
||
|
whdr_ems proc near
|
||
|
ret
|
||
|
whdr_ems endp
|
||
|
|
||
|
wseg_ems proc near
|
||
|
ret
|
||
|
wseg_ems endp
|
||
|
;=============================================================================
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE FILES.
|
||
|
;=============================================================================
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; Attempt to create a temporary file. If the tempfile name is NIL then return
|
||
|
; with the cary flag set.
|
||
|
init_file proc near
|
||
|
mov al, [tmpname]
|
||
|
or al, al
|
||
|
je err_init_file
|
||
|
mov dx, offset tmpname
|
||
|
xor cx, cx
|
||
|
mov ah, 03cH
|
||
|
int 21H
|
||
|
jc err_init_file ; if carry set then failure
|
||
|
mov [tmphandle], ax ; init swapping
|
||
|
mov [swap], swap_file
|
||
|
jmp exit_init_file
|
||
|
err_init_file: stc
|
||
|
exit_init_file: ret
|
||
|
init_file endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; This routine writes a segment header to a file.
|
||
|
; The header is a seven byte record formatted as follows:
|
||
|
; segment address - of data
|
||
|
; offset address - of data
|
||
|
; length in paragraphs - of data
|
||
|
; mode - 1 => segment header (allocate seg on read)
|
||
|
; 0 => subsegment, don't allocate on read.
|
||
|
; Routine takes three arguments:
|
||
|
; di:dx segment:offset of segment
|
||
|
; si number of paragraphs to write.
|
||
|
; al mode of header to write
|
||
|
whdr_file proc near
|
||
|
mov [word ptr tmpseg], di ; save the segment/offset
|
||
|
mov [word ptr tmpseg+2], dx
|
||
|
mov [word ptr tmpseg+4], si ; save the segment length
|
||
|
mov [tmpseg+6], al
|
||
|
mov dx, offset tmpseg ; write the header record out
|
||
|
mov cx, 7
|
||
|
mov bx, [tmphandle]
|
||
|
mov ah, 040H
|
||
|
int 21H
|
||
|
jc exit_whdr_file ; make sure it worked
|
||
|
cmp ax, 7
|
||
|
je exit_whdr_file ; oh oh, disk is full!
|
||
|
err_whdr_file: stc
|
||
|
exit_whdr_file: ret
|
||
|
whdr_file endp
|
||
|
|
||
|
|
||
|
;-----------------------------------------------------------------------------
|
||
|
; Write a segment to the temporary file whose handle is in cs:tmphandle
|
||
|
; Parameters for the write are assumed to be stored in the tmpseg data area.
|
||
|
; function returns carry set if failed, carry clear otherwise.
|
||
|
wseg_file proc near
|
||
|
push ds
|
||
|
mov ds, word ptr cs:tmpseg ; Now write the whole segment
|
||
|
mov dx, word ptr cs:tmpseg+2
|
||
|
mov cx, word ptr cs:tmpseg+4
|
||
|
mov bx, cs:tmphandle
|
||
|
mov ah, 040H
|
||
|
int 21H
|
||
|
pop ds
|
||
|
jc exit_wseg_file ; make sure it worked
|
||
|
cmp ax, [word ptr tmpseg+4]
|
||
|
je exit_wseg_file
|
||
|
err_wseg_file: stc ; it failed (usually disk full)
|
||
|
exit_wseg_file: ret
|
||
|
wseg_file endp
|
||
|
;=============================================================================
|
||
|
|
||
|
|
||
|
;=============================================================================
|
||
|
; _exec: THIS IS THE MAIN ENTRY ROUTINE TO THIS MODULE
|
||
|
;=============================================================================
|
||
|
; This is the main entry routine into the swap code and corresponds to the
|
||
|
; following C function call:
|
||
|
;
|
||
|
; exec( int swap, char far *program, char far *cmdtail, int environment_seg,
|
||
|
; char far *tmpfilename );
|
||
|
;
|
||
|
; Exec performs the following:
|
||
|
; 1. set up the local code segment copies of arguments to the exec call.
|
||
|
; 2. switch to a local stack frame so that we don't clobber the user
|
||
|
; stack.
|
||
|
; 3. save old interrupt vectors for ctrl-brk.
|
||
|
; 4. install our own handler for the ctrl-brk interrupt, our handler
|
||
|
; terminates the current running process, and returns with non-zero
|
||
|
; status code.
|
||
|
; 5. get our psp
|
||
|
; 6. setup arguments for exec call
|
||
|
; 7. exec the program, save result code on return.
|
||
|
; 8. restore previous ctrl-brk and crit-error handler.
|
||
|
; 9. restore previous process stack, and segment registers.
|
||
|
; 10. return from exec with child result code in AX
|
||
|
; and global _Interrupted flag set to true if child execution was
|
||
|
; interrupted.
|
||
|
|
||
|
; NOTE: When first called the segments here assume the standard segment
|
||
|
; settings.
|
||
|
assume cs:@code, ds:DGROUP,es:DGROUP,ss:DGROUP
|
||
|
|
||
|
public _exec
|
||
|
_exec proc
|
||
|
push bp ; set up the stack frame
|
||
|
mov bp, sp
|
||
|
push si ; save registers we shouldn't step on.
|
||
|
push di
|
||
|
push ds
|
||
|
|
||
|
; set up for copying of parameters passed in with long pointers.
|
||
|
push cs ; going to use lodsb/stosb, set up es
|
||
|
pop es ; as destination.
|
||
|
assume es:@code ; let the assembler know :-)
|
||
|
cld ; make sure direction is right
|
||
|
|
||
|
; Copy all parameters into the bottom of the code segment. After doing so we
|
||
|
; will immediately switch stacks, so that the user stack is preserved intact.
|
||
|
mov ax, ss:[a_swap] ; save swap
|
||
|
mov es:swap, ax
|
||
|
mov ax, ss:[a_env] ; save env seg to use
|
||
|
mov es:envseg, ax
|
||
|
|
||
|
mov di, offset cs:cmdpath ; copy the command
|
||
|
lds si, ss:[a_prog] ; 65 bytes worth
|
||
|
mov cx, 65
|
||
|
call copy_data
|
||
|
|
||
|
mov di, offset cs:cmdtail ; copy the command tail
|
||
|
lds si, ss:[a_tail] ; 129 bytes worth
|
||
|
mov cx, 129
|
||
|
call copy_data
|
||
|
|
||
|
mov di, offset cs:tmpname ; copy the temp file name
|
||
|
lds si, ss:[a_tmp] ; 65 bytes worth.
|
||
|
mov cx, 65
|
||
|
call copy_data
|
||
|
|
||
|
; Now we save the current ss:sp stack pointer and swap stack to our temporary
|
||
|
; stack located in the current code segment. At the same time we reset the
|
||
|
; segment pointers to point into the code segment only.
|
||
|
swap_stacks: mov ax, ss
|
||
|
mov es:old_ss, ax
|
||
|
mov es:old_sp, sp
|
||
|
mov ax, cs
|
||
|
mov ds, ax
|
||
|
mov ss, ax ; set ss first, ints are then
|
||
|
mov sp, offset cs:exec_sp ; disabled for this instr too
|
||
|
assume ds:@code, ss:@code ; let the assembler know :-)
|
||
|
|
||
|
; Now we save the old control break and critical error handler addresses.
|
||
|
; We replace them by our own routines found in the resident portion of the
|
||
|
; swapping exec code.
|
||
|
set_handlers: mov [interrupted], 0 ; clear interrupted flag
|
||
|
mov [eretcode], 0 ; clear the return code
|
||
|
mov ax, 03523H ; get int 23 handler address
|
||
|
int 21H
|
||
|
mov cs:old_ctl_brk_off, bx
|
||
|
mov cs:old_ctl_brk_seg, es
|
||
|
mov dx, offset ctl_brk_handler
|
||
|
mov ax, 02523H ; set int 23 handler address
|
||
|
int 21H
|
||
|
|
||
|
mov ax, 03524H ; get int 24 handler address
|
||
|
int 21H
|
||
|
mov cs:old_crit_err_off, bx
|
||
|
mov cs:old_crit_err_seg, es
|
||
|
mov dx, offset crit_err_handler
|
||
|
mov ax, 02524H ; set int 24 handler address
|
||
|
int 21H
|
||
|
|
||
|
; Go and execute the child, we've set up all of its parameters. The do_exec
|
||
|
; routine will attempt to perform a swap of the code if requested to do so by
|
||
|
; a non-zero value in the variable cs:swap.
|
||
|
mov ah, 051H ; get the psp
|
||
|
int 21H
|
||
|
mov cs:psp, bx
|
||
|
call do_exec
|
||
|
|
||
|
; We're back from the exec, so fix things up the way they were.
|
||
|
; Restore the old control-break and critical-error handlers.
|
||
|
lds dx, cs:old_ctl_brk
|
||
|
mov ax, 02523H
|
||
|
int 21H
|
||
|
lds dx, cs:old_crit_err
|
||
|
mov ax, 02524H
|
||
|
int 21H
|
||
|
|
||
|
; Restore previous program stack segment registers, and data segment.
|
||
|
mov ax, cs:old_ss
|
||
|
mov ss, ax ; mov into ss first, that way
|
||
|
mov sp, cs:old_sp ; no interrupts in this instr.
|
||
|
pop ds
|
||
|
|
||
|
; Tell the assembler we have swaped segments again.
|
||
|
assume ds:DGROUP,es:DGROUP,ss:DGROUP
|
||
|
|
||
|
; Set the global Interrupted flag so that parent can tell it was interrupted.
|
||
|
mov ax, seg DGROUP:_Interrupted
|
||
|
mov es, ax
|
||
|
mov ax, cs:interrupted
|
||
|
mov es:_Interrupted, ax
|
||
|
|
||
|
; Set the global errno value to reflect the success/failure of the DOS
|
||
|
; exec call.
|
||
|
mov ax, seg DGROUP:_errno
|
||
|
mov es, ax
|
||
|
mov ax, cs:ex_error
|
||
|
mov es:_errno, ax
|
||
|
|
||
|
; Fetch the child's return code, pop rest of stuff off of the stack
|
||
|
; and return to the caller.
|
||
|
mov ax, cs:eretcode
|
||
|
pop di
|
||
|
pop si
|
||
|
pop bp
|
||
|
ret
|
||
|
_exec endp
|
||
|
|
||
|
; void do_hook_std_writes(int handle);
|
||
|
; This saves the 21h interrupt vector and changes it to point
|
||
|
; into this code. Argument is the file handle of the -C file.
|
||
|
|
||
|
public _do_hook_std_writes
|
||
|
_do_hook_std_writes proc
|
||
|
push bp
|
||
|
mov bp,sp
|
||
|
push di
|
||
|
|
||
|
mov di, ss:[a_handle] ; handle of -C file
|
||
|
mov std_fil_handle, di
|
||
|
|
||
|
mov ah, 51h ; request our PSP
|
||
|
int 21h
|
||
|
mov [psp], bx ; save it
|
||
|
|
||
|
mov es, bx
|
||
|
les bx, es:[34h] ; pointer to job file table
|
||
|
mov al, es:[bx+1] ; system file # of our stdout
|
||
|
mov [our_stdout], al
|
||
|
mov al, es:[bx+di] ; system file number of -C file
|
||
|
mov std_fil_number, al
|
||
|
|
||
|
mov ax,3521h ; request vector 21h
|
||
|
int 21h ; it's returned in ES:BX
|
||
|
mov word ptr [real_21h], bx
|
||
|
mov word ptr [real_21h+2], es
|
||
|
|
||
|
push ds
|
||
|
mov ax,cs
|
||
|
mov ds,ax
|
||
|
lea dx,our_21h_handler ; DS:DX is the new vector
|
||
|
mov ax,2521h ; set vector 21h
|
||
|
int 21h
|
||
|
|
||
|
pop ds
|
||
|
pop di
|
||
|
pop bp
|
||
|
ret
|
||
|
_do_hook_std_writes endp
|
||
|
|
||
|
; void do_unhook_std_writes(void);
|
||
|
; This restores the 21h interrupt vector.
|
||
|
; The saved vector is zero if it wasn't changed (no -C option).
|
||
|
|
||
|
public _do_unhook_std_writes
|
||
|
_do_unhook_std_writes proc
|
||
|
push ds
|
||
|
|
||
|
lds dx, [real_21h] ; put saved vector into DS:DX
|
||
|
mov ax, ds
|
||
|
or ax, dx
|
||
|
jz unhook_return ; zero means we didn't hook 21h
|
||
|
|
||
|
mov ax,2521h ; set vector 21h
|
||
|
simulate_21h
|
||
|
|
||
|
unhook_return: pop ds
|
||
|
ret
|
||
|
_do_unhook_std_writes endp
|
||
|
end
|