v19i043: dmake - dmake version 3.7, Part22/37
Dennis Vadura
dvadura at watdragon.waterloo.edu
Mon May 13 08:13:16 AEST 1991
Submitted-by: Dennis Vadura <dvadura at watdragon.waterloo.edu>
Posting-number: Volume 19, Issue 43
Archive-name: dmake/part22
Supersedes: dmake-3.6: Volume 15, Issue 52-77
---- Cut Here and feed the following to sh ----
#!/bin/sh
# this is dmake.shar.22 (part 22 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file dmake/msdos/config.mk continued
#
if test ! -r _shar_seq_.tmp; then
echo 'Please unpack part 1 first!'
exit 1
fi
(read Scheck
if test "$Scheck" != 22; then
echo Please unpack part "$Scheck" next!
exit 1
else
exit 0
fi
) < _shar_seq_.tmp || exit 1
if test -f _shar_wnt_.tmp; then
sed 's/^X//' << 'SHAR_EOF' >> 'dmake/msdos/config.mk' &&
# config file.
.SOURCE.h : $(OS)
X
# See if we modify anything in the lower levels.
.IF $(OSRELEASE) != $(NULL)
X .INCLUDE .IGNORE : $(OS)$(DIRSEPSTR)$(OSRELEASE)$(DIRSEPSTR)config.mk
.END
SHAR_EOF
chmod 0640 dmake/msdos/config.mk ||
echo 'restore of dmake/msdos/config.mk failed'
Wc_c="`wc -c < 'dmake/msdos/config.mk'`"
test 1663 -eq "$Wc_c" ||
echo 'dmake/msdos/config.mk: original size 1663, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dmake/msdos/dirbrk.c ==============
if test -f 'dmake/msdos/dirbrk.c' -a X"$1" != X"-c"; then
echo 'x - skipping dmake/msdos/dirbrk.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
sed 's/^X//' << 'SHAR_EOF' > 'dmake/msdos/dirbrk.c' &&
/* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/msdos/RCS/dirbrk.c,v 1.1 91/05/06 15:25:29 dvadura Exp $
-- SYNOPSIS -- define the directory separator string.
--
-- DESCRIPTION
-- Define this string for any character that may appear in a path name
-- and can be used as a directory separator.
--
-- AUTHOR
-- Dennis Vadura, dvadura at 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.
--
-- LOG
-- $Log: dirbrk.c,v $
X * Revision 1.1 91/05/06 15:25:29 dvadura
X * dmake Release Version 3.7
X *
*/
X
#include "extern.h"
X
/* dos uses /, \, and : */
char* DirBrkStr = "/\\:";
X
/*
** Return TRUE if the name is the full specification of a path name to a file
** starting at the root of the file system, otherwise return FALSE
*/
PUBLIC int
If_root_path(name)
char *name;
{
X return( (strchr(DirBrkStr, *name) != NIL(char)) ||
X (isalpha(*name) && name[1] == ':') );
}
SHAR_EOF
chmod 0640 dmake/msdos/dirbrk.c ||
echo 'restore of dmake/msdos/dirbrk.c failed'
Wc_c="`wc -c < 'dmake/msdos/dirbrk.c'`"
test 1776 -eq "$Wc_c" ||
echo 'dmake/msdos/dirbrk.c: original size 1776, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dmake/msdos/dirlib.h ==============
if test -f 'dmake/msdos/dirlib.h' -a X"$1" != X"-c"; then
echo 'x - skipping dmake/msdos/dirlib.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
sed 's/^X//' << 'SHAR_EOF' > 'dmake/msdos/dirlib.h' &&
/* DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89 */
X
#ifndef _DIRLIB_h_
#define _DIRLIB_h_
X
#include <stdio.h>
#include "stdmacs.h"
X
#define MAXNAMLEN 15
X
struct direct {
X long d_ino;
X unsigned short d_reclen;
X unsigned short d_namlen;
X char d_name[MAXNAMLEN+1];
};
X
typedef struct {
X char fcb[21];
X char attr;
X short time;
X short date;
X long size;
X char name[13];
} DTA;
X
typedef struct {
X DTA dd_dta; /* disk transfer area for this dir. */
X short dd_stat; /* status return from last lookup */
X char dd_name[1]; /* full name of file -- struct is extended */
} DIR;
X
extern DIR *opendir ANSI((char *));
extern struct direct *readdir ANSI((DIR *));
extern long telldir ANSI((DIR *));
extern void seekdir ANSI((DIR *, long));
extern void closedir ANSI((DIR *));
extern DTA *findfirst ANSI((char *, DTA *));
extern DTA *findnext ANSI((DTA *));
X
#define rewinddir(dirp) seekdir(dirp,0L)
#endif
SHAR_EOF
chmod 0640 dmake/msdos/dirlib.h ||
echo 'restore of dmake/msdos/dirlib.h failed'
Wc_c="`wc -c < 'dmake/msdos/dirlib.h'`"
test 1086 -eq "$Wc_c" ||
echo 'dmake/msdos/dirlib.h: original size 1086, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dmake/msdos/exec.asm ==============
if test -f 'dmake/msdos/exec.asm' -a X"$1" != X"-c"; then
echo 'x - skipping dmake/msdos/exec.asm (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
sed 's/^X//' << 'SHAR_EOF' > 'dmake/msdos/exec.asm' &&
;
; 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, int env_size, 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 at 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
X .286 ; define have286 with -D for 80286 processor or better
X
else ; 8088/8086 compatible
X mpusha Macro
X push ax
X push cx
X push dx
X push bx
X push sp
X push bp
X push si
X push di
X Endm
X
X mpopa Macro
X pop di
X pop si
X pop bp
X add sp,2
X pop bx
X pop dx
X pop cx
X pop ax
X Endm
endif
X
ifdef msmall
X .model small
argbase equ 4
endif
ifdef mcompact
X .model compact
argbase equ 4
endif
ifdef mmedium
X .model medium
argbase equ 6
endif
ifdef mlarge
X .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>
X
a_handle equ <bp+argbase>
X
X
; 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
X
X
; Define any global/external variables that we will be accessing from here.
X .data
X extrn _errno:word ; Set to dos ret code from exec
X public _Interrupted ; Set to 1 if interrupted 0
_Interrupted dw 0 ; otherwise
X
X .code
X assume cs:@code, ds:@code, ss:@code, es:@code
X
X even
execstack dw 64 dup (?) ; put the temporary exec stack right
exec_sp label word ; at the start.
X
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
X
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
X
tmpname db 65 dup(0) ; name of temporary file resource
X
X even
tmphandle dw ? ; handle for temporary file
real_21h dd 0 ; will be DOS's 21h vector if doing -C
X
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
X
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, '$'
X
X even
write_header label word
X whdr_xms_ptr dw word ptr whdr_xms
X whdr_ems_ptr dw word ptr whdr_ems
X whdr_file_ptr dw word ptr whdr_file
X
write_seg label word
X wseg_xms_ptr dw word ptr wseg_xms
X wseg_ems_ptr dw word ptr wseg_ems
X wseg_file_ptr dw word ptr wseg_file
X
read_header label word
X rhdr_xms_ptr dw word ptr rhdr_xms
X rhdr_ems_ptr dw word ptr rhdr_ems
X rhdr_file_ptr dw word ptr rhdr_file
X
read_seg label word
X rseg_xms_ptr dw word ptr rseg_xms
X rseg_ems_ptr dw word ptr rseg_ems
X rseg_file_ptr dw word ptr rseg_file
X
free_resource label word
X free_xms_ptr dw word ptr free_xms_resource
X free_ems_ptr dw word ptr free_ems_resource
X free_file_ptr dw word ptr free_file_resource
X
reset_resource label word
X reset_xms_ptr dw word ptr reset_xms_resource
X reset_ems_ptr dw word ptr reset_ems_resource
X reset_file_ptr dw word ptr reset_file_resource
X
old_ctl_brk label dword
X old_ctl_brk_off dw ?
X old_ctl_brk_seg dw ?
X
old_crit_err label dword
X old_crit_err_off dw ?
X old_crit_err_seg dw ?
X
exec_block label word
X ex_envseg dw ? ; env seg, use parent's if 0
X ex_cmdtail dd ? ; command tail for exec
X ex_fcb1 dd far ptr fcb ; fcb's aren't used by dmake
X ex_fcb2 dd far ptr fcb
X ex_ss dw ? ; saved ss for exec
X ex_sp dw ? ; saved sp for exec
X ex_error dw 0 ; error code for dos exec
X
X
; 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.
X
; 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.
X
; 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.
X
simulate_21h Macro
X pushf ;; direct call to DOS
X call cs:[real_21h]
X Endm
X
X assume cs:@code, ds:nothing, es:nothing, ss:nothing
our_21h_handler proc far
X pushf
X cmp ah,40h ; is this a write?
X jne call_dos ; --no
X cmp bx,1 ; write on handle 1 (stdout?)
X je duplicate_it
X cmp bx,2 ; stderr?
X je duplicate_it
X
call_dos:
X popf
X jmp [real_21h] ; far jump to real handler, which will do the sys call
X ; and return to the original caller
X
duplicate_it:
X mpusha
X push ds
X push es
X mov bp,sp
X
X mov di,std_fil_handle ; handle of the -C file
X
X If @codesize eq 0
X ; Small/compact models allow for quick test of us versus subprocess.
X ; False negative (it's us with a different CS) will be picked
X ; up by code just below. (Might happen due to call from C library.)
X ; False positives would be bad, but can't happen.
X mov ax,[bp+24] ; caller's CS
X cmp ax, at code ; same as us?
X je call_from_dmake
X Endif
X
X mov ah,51h ; get PSP ("undocumented version" works in DOS 2.0+)
X simulate_21h ; PSP segment returned in BX
X cmp bx,psp ; our PSP?
X je call_from_dmake ; --yes, no PSP changing needed
X
X mov es,bx ; set ES to current (caller's) PSP
X lds bx,es:[34h] ; set DS:BX pointing to caller's job file table
X
X mov si,[bp+12] ; file handle caller passed in (known to be 1 or 2)
X mov al,[bx+si] ; system file number corresponding to caller's handle
X cmp al,our_stdout ; same as our stdout?
X jne do_real_write ; no--subprocess must have redirected it
X
X mov al,[bx+di] ; see if caller has dup of -C file still open
X cmp al,std_fil_number
X je use_dup ; yes--we can write using caller's PSP
X
X ; Calling process (or some intermediate process) has closed
X ; the -C descriptor. We'll use dmake's (our) -C descriptor, but
X ; to do so we'll have to change the PSP. Disable BREAK handling
X ; so that ^break doesn't kill the wrong process.
X
X mov ax,3300h ; get BREAK flag
X simulate_21h
X mov si,dx ; save BREAK state in SI
X sub dx,dx ; now turn break flag off
X mov ax,3301h
X simulate_21h ; don't want ^Break recoginized while PSP changed
X mov bx,psp ; set dmake's PSP
X mov ah,50h
X simulate_21h
X
X mov bx,di ; handle of -C file
X ; CX still has caller's count
X mov ds,[bp+2] ; restore caller's DS
X mov dx,[bp+14] ; DS:DX again points to caller's buffer
X mov ah,40h
X simulate_21h ; write the copy
X
X mov bx,es ; caller's PSP
X mov ah,50h ; set PSP
X simulate_21h ; restore caller's PSP
X mov dx,si ; break state before we changed it
X mov ax,3301h
X simulate_21h ; restore break state
X
X jmp short do_real_write
X
use_dup:
X mov ds,[bp+2] ; restore caller's DS
X mov dx,[bp+14] ; DS:DX again points to caller's buffer
X
call_from_dmake:
X mov bx,di ; handle of -C file
X mov ah,40h ; write
X ; CX still has caller's count
X simulate_21h ; write to the file
X
do_real_write:
X pop es
X pop ds
X mpopa
X popf
X jmp [real_21h] ; far jump to real handler, which will do the sys call
X ; and return to the original caller
our_21h_handler endp
X
X assume cs:@code, ds:@code, ss:@code, es:@code
X
;-----------------------------------------------------------------------------
; 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
X add sp, 6 ; ip/cs/flags ...
X pop ax
X pop bx
X pop cx
X pop dx
X pop si
X pop di
X pop bp
X pop ds
X pop es
X push bp ; fix up the return flags
X mov bp, sp
X xchg ax, [bp+6] ; get the flag byte.
X or ax, 1 ; set the carry bit
X xchg ax, [bp+6] ; put it back.
X pop bp
X mov ax, 5 ; access denied
X iret
crit_err_handler endp
X
X
;-----------------------------------------------------------------------------
; Here we set the interrupted flag, and terminate the currently running
; process.
ctl_brk_handler proc far
X clc ; make sure carry is clear
X inc cs:interrupted ; set the flag
X
; 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.
X cmp cs:in_exec,0
X je just_return ; note this implies CF == 0
X stc ; set CF to abort child
just_return: iret
ctl_brk_handler endp
X
X
;-----------------------------------------------------------------------------
; 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
X mov dx, offset error_rhdr
X jmp print_it
abort_exec_rseg label near
X mov dx, offset error_rseg
X jmp print_it
abort_exec_resize label near
X mov dx, offset error_resize
X jmp print_it
abort_exec_free label near
X mov dx, offset error_free
X jmp print_it
abort_exec_alloc label near
X mov dx, offset error_alloc
X jmp print_it
abort_exec proc near
X mov dx, offset error_string
print_it: push dx
X mov bx, [swap]
X call [free_resource+bx]
X mov ax, cs
X mov ds, ax
X pop dx
X mov ah, 9
X int 21H
kill_program: mov ax, 04cffH ; nuke it!
X int 21H
abort_exec endp
X
X
;-----------------------------------------------------------------------------
; 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
X shr cx, 1 ; convert to word count
X jnc copy_words
X movsb
copy_words: rep movsw ; copy the words.
X ret
copy_data endp
X
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ XMS RECORDS.
;=============================================================================
rhdr_xms proc near
X ret
rhdr_xms endp
X
rseg_xms proc near
X ret
rseg_xms endp
X
reset_xms_resource proc near
X ret
reset_xms_resource endp
X
free_xms_resource proc near
X ret
free_xms_resource endp
;=============================================================================
X
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ EMS RECORDS.
;=============================================================================
rhdr_ems proc near
X ret
rhdr_ems endp
X
rseg_ems proc near
X ret
rseg_ems endp
X
reset_ems_resource proc near
X ret
reset_ems_resource endp
X
free_ems_resource proc near
X ret
free_ems_resource endp
;=============================================================================
X
X
X
;=============================================================================
; 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
X mov dx, offset tmpseg ; read the header record out
X mov cx, 7
X mov bx, [tmphandle]
X mov ah, 03fH
X int 21H
X jnc rhdr_done ; make sure it worked
X jmp abort_exec_rhdr
X
rhdr_done: cmp ax, 7
X je exit_rhdr_file
X or ax, ax
X je signal_eof
X jmp abort_exec_rhdr
X
signal_eof: stc
exit_rhdr_file: ret
rhdr_file endp
X
X
;-----------------------------------------------------------------------------
; 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
X push ds
X mov ds, word ptr cs:tmpseg; Now read the whole segment
X mov dx, word ptr cs:tmpseg+2
X mov cx, word ptr cs:tmpseg+4
X mov bx, cs:tmphandle
X mov ah, 03fH
X int 21H
X pop ds
X jnc rseg_done
X jmp abort_exec_rseg
X
rseg_done: cmp ax, [word ptr tmpseg+4]
X je exit_rseg_file
X jmp abort_exec_rseg ; If we didn't get read full
exit_rseg_file: ret ; segment then abort
rseg_file endp
X
X
;-----------------------------------------------------------------------------
; Seek to the beginning of the file.
reset_file_resource proc near
X mov bx, [tmphandle]
X xor cx, cx
X mov dx, cx
X mov ax, 04200H ; seek to begining of file
X int 21H
X ret
reset_file_resource endp
X
X
;-----------------------------------------------------------------------------
; 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
X mov bx, [tmphandle] ; get the file handle
X mov ah, 03eH ; close the file
X int 21H
X mov dx, offset tmpname ; Now delete the temp file
X mov ah, 041H
X int 21H
X ret
free_file_resource endp
;=============================================================================
X
X
X
;=============================================================================
; CODE TO SWAP THE IMAGE IN FROM SECONDARY STORAGE
;=============================================================================
swap_in proc near
X mov bx, [alstr] ; get previous alloc strategy
X mov ax, 5801H ; and set it back
X int 21H
X mov bx, [swap] ; get type of resource
X call [reset_resource+bx] ; reset the resource
X mov es, [psp] ; resize the program back
X mov bx, [progsize] ; to original size
X mov ah, 04AH
X int 21H
X jnc read_seg_loop
X jmp abort_exec
X
read_seg_loop: mov bx, [swap] ; get type of resource
X call [read_header+bx] ; get seg header
X jc exit_swap_in ; all done
X mov al, [tmpseg+6]
X cmp al, seg_no_alloc ; see if dummy segment header
X je read_seg_loop
X cmp al, seg_alloc ; do we need to do an alloc?
X jne read_data ; nope
X
; 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.
X mov bx, [word ptr tmpseg+4] ; get count of paragraphs
X mov ah, 048H ; dos_alloc
X int 21H
X jc alloc_error ; oops!
X cmp ax, [word ptr tmpseg] ; did we get the same segment?
X je read_seg_loop ; yup!
alloc_error: jmp abort_exec_alloc
X
read_data: mov bx, [swap]
X call [read_seg+bx] ; this must succeed, if fail
X jmp read_seg_loop ; we never come back here
X
exit_swap_in: mov bx, [swap] ; all done, so free resource
X call [free_resource+bx]
X ret
swap_in endp
X
X
;=============================================================================
; 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
X mov ax, 05800H ; get memory alocation strategy
X int 021H
X mov [alstr], ax ; and save it for future restoration.
X mov di, [psp] ; compute length of program to current
X mov bx, cs ; value of cs, and find program size
X sub bx, di ; by looking at length stored in
X mov ax, di ; arena header found in front of psp
X dec ax
X mov es, ax
X mov si, es:3 ; si is size of program in paragraphs
X mov [progsize], si ; progsize now contains the size.
X
; Now compute length of program segment to save.
; Length is: cs - psp + (offset overlay_code_here+15 >> 4)
X mov ax, offset overlay_code_here+15
X shr ax, 1
X shr ax, 1
X shr ax, 1
X shr ax, 1
X add bx, ax ; bx is size of program to keep
X sub si, bx ; si is # of paragraphs to save.
X add di, bx ; di is paragraph to start at
X mov rootsize, bx
X mov resend, di ; cs:resend is saved start para
X mov al, seg_no_alloc ; set no allocation for segment
X call write_segment
X jc abort_swap_out
X
; 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]
X dec ax
X mov es, ax
X mov bx, offset write_segment_data
X call walk_arena_chain
X jc abort_swap_out
X
; 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]
X dec ax
X mov es,ax
X mov bx, offset free_dos_segment
X call walk_arena_chain
X jnc resize_program
X jmp abort_exec_free ; can't fix it up now.
X
; 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.
X mov bx, [rootsize] ; bx is size of segment.
X mov ah, 04aH ; resize memory block
X int 21H
X jnc swap_out_ok
X jmp abort_exec_resize ; disaster
swap_out_ok: ret
X
; The swap out failed for some reason, so free any allocated resources
; and set the carry bit.
abort_swap_out: mov bx, [swap]
X call [free_resource+bx]
X xor ax, ax
X mov [swap], ax ; clear the swap flag
X stc
X ret
swap_out endp
X
X
;=============================================================================
; 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
X cmp [swap], 0 ; does the user want to swap?
X je no_swap_out ; nope
X call init_swap ; figger out where to swap to
X jc no_swap_out ; if carry set then don't swap
X call swap_out
X
no_swap_out: cmp [interrupted], 0 ; were we interrupted?
X jne leave_exec ; yep, so clean up, don't exec
X
; free passed in environment block if it is non zero.
; This way the parent program does not need to free it.
X mov ax, [envseg]
X or ax, ax
X je setup_block
X push ax
X mov es, ax
X mov ah, 49H
X int 21H
X pop ax
X
; 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]
X mov [ex_envseg], ax
X mov cx, cs
X mov [word ptr ex_cmdtail], offset cmdtail
X mov [word ptr ex_cmdtail+2], cx
X
; set up registers for exec call
; ds:dx - pointer to pathname of program to execute
; es:bx - pointer to above parameter block
X mov dx, offset cmdpath
X mov es, cx
X mov bx, offset exec_block
X
; Under DOS 2.x exec is notorious for clobbering registers and guarantees
; to preserve only cs:ip.
X push ds
X mov [ex_sp], sp
X mov [ex_ss], ss
X mov [ex_error], 0 ; clear exec error code
X inc [in_exec] ; set internal flag
X mov ax, 04b00H
X int 21H
X
; returned from exec, so restore possibly clobbered registers.
X mov ss, cs:ex_ss
X mov sp, cs:ex_sp
X pop ds
X
; check to make certain the exec call worked.
X jnc it_worked
X
; exec call failed. Save return code from msdos.
X mov [ex_error], ax
X jmp leave_exec
X
it_worked: mov ah, 04dH ; get the return code
X int 21H
X cbw
X mov [eretcode], ax
X
leave_exec: cmp [swap], 0 ; check swap, if non-zero swap back in
X je no_swap_in
X call swap_in
X
; 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
X ret
do_exec endp
X
X
X
;==============================================================================
; 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
X
;-----------------------------------------------------------------------------
; 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
X mov [swap], 0
;call init_xms
;jnc init_done
;call init_ems
;jnc init_done
X call init_file
init_done: ret
init_swap endp
X
X
;-----------------------------------------------------------------------------
; 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
X magic db ? ; either 'Z' for end or 'M' for allocated
X owner dw ? ; psp of owner block
X len dw ? ; length in paragraphs of segment
memheader ends
X
walk_arena_chain proc near
X mov si, word ptr es:3 ; get length
X mov di, es
X inc di
X mov ax, word ptr es:1
X
; Stop the search if the block is NOT owned by us. Ignore our own psp block
; and our environment segment block.
X cmp ax, cs:psp ; is it owned by us?
X jne walk_done ; NOPE! -- all done
X cmp di, cs:envseg ; skip our environment
X je next_block
X cmp di, cs:psp ; skip our psp
X je next_block
X
; Now save state and call the routine pointed at by [bx].
X push di
X push si
X push bx
X call bx
X pop bx
X pop si
X pop di
X jc exit_walk ; if error then stop
X mov al, byte ptr es:0 ; check if at end
X cmp al, 'Z'
X je walk_done
X
next_block: add di, si ; go on to next segment
X mov es, di
X jmp walk_arena_chain
walk_done: clc
exit_walk: ret
walk_arena_chain endp
X
X
;-----------------------------------------------------------------------------
; This routine takes a dos segment found in the di register and free's it.
free_dos_segment proc near
X mov es, di ; free dos memory block
X mov ah, 49H
X int 21H
X ret
free_dos_segment endp
X
X
;-----------------------------------------------------------------------------
; 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
X 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
X push di
X push si
X xor dx,dx
X mov bx, [swap]
X call [write_header+bx]
X pop si
X pop di
X jc exit_wseg
X
do_io_loop: cmp si, 0 ; are we done yet?
X je exit_wseg ; yup so leave.
X mov cx, si ; # of paragraphs to move
X cmp cx, 0fffH ; see if we have lots to move?
X jle do_io
X mov cx, 0fffH ; reset to max I/O size
X
do_io: push cx ; save # of paragraphs we are writing
X shl cx, 1 ; shift cx by four to the left
X shl cx, 1
X shl cx, 1
X shl cx, 1
X push di ; save the start, and count left
X push si
X mov si, cx
X xor dx,dx
X mov al, seg_data
X mov bx, [swap]
X push bx
X call [write_header+bx]
X pop bx
X call [write_seg+bx]
X pop si
X pop di
X pop dx ; original paragraph count in dx
X jc exit_wseg ; it failed so exit.
X add di, dx ; adjust the pointers, and continue.
X sub si, dx
X jmp do_io_loop
exit_wseg: ret
write_segment endp
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE XMS RECORDS.
;=============================================================================
init_xms proc near
X ret
init_xms endp
X
whdr_xms proc near
X ret
whdr_xms endp
X
wseg_xms proc near
X ret
wseg_xms endp
;=============================================================================
X
X
;=============================================================================
; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE EMS RECORDS.
;=============================================================================
init_ems proc near
X ret
init_ems endp
X
whdr_ems proc near
X ret
whdr_ems endp
X
wseg_ems proc near
X ret
wseg_ems endp
;=============================================================================
X
X
;=============================================================================
; 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
X mov al, [tmpname]
X or al, al
X je err_init_file
X mov dx, offset tmpname
X xor cx, cx
X mov ah, 03cH
X int 21H
X jc err_init_file ; if carry set then failure
X mov [tmphandle], ax ; init swapping
X mov [swap], swap_file
X jmp exit_init_file
err_init_file: stc
exit_init_file: ret
init_file endp
X
X
;-----------------------------------------------------------------------------
; 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
X mov [word ptr tmpseg], di ; save the segment/offset
X mov [word ptr tmpseg+2], dx
X mov [word ptr tmpseg+4], si ; save the segment length
X mov [tmpseg+6], al
X mov dx, offset tmpseg ; write the header record out
X mov cx, 7
X mov bx, [tmphandle]
X mov ah, 040H
X int 21H
X jc exit_whdr_file ; make sure it worked
X cmp ax, 7
X je exit_whdr_file ; oh oh, disk is full!
err_whdr_file: stc
exit_whdr_file: ret
whdr_file endp
X
X
;-----------------------------------------------------------------------------
; 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
X push ds
X mov ds, word ptr cs:tmpseg ; Now write the whole segment
X mov dx, word ptr cs:tmpseg+2
X mov cx, word ptr cs:tmpseg+4
X mov bx, cs:tmphandle
X mov ah, 040H
X int 21H
X pop ds
X jc exit_wseg_file ; make sure it worked
X cmp ax, [word ptr tmpseg+4]
X je exit_wseg_file
err_wseg_file: stc ; it failed (usually disk full)
exit_wseg_file: ret
wseg_file endp
;=============================================================================
X
X
;=============================================================================
; _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.
X
; NOTE: When first called the segments here assume the standard segment
; settings.
X assume cs:@code, ds:DGROUP,es:DGROUP,ss:DGROUP
X
X public _exec
_exec proc
X push bp ; set up the stack frame
X mov bp, sp
X push si ; save registers we shouldn't step on.
X push di
X push ds
X
; set up for copying of parameters passed in with long pointers.
X push cs ; going to use lodsb/stosb, set up es
X pop es ; as destination.
X assume es:@code ; let the assembler know :-)
X cld ; make sure direction is right
X
; 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.
X mov ax, ss:[a_swap] ; save swap
X mov es:swap, ax
X mov ax, ss:[a_env] ; save env seg to use
X mov es:envseg, ax
X
X mov di, offset cs:cmdpath ; copy the command
X lds si, ss:[a_prog] ; 65 bytes worth
X mov cx, 65
X call copy_data
X
X mov di, offset cs:cmdtail ; copy the command tail
X lds si, ss:[a_tail] ; 129 bytes worth
X mov cx, 129
X call copy_data
X
X mov di, offset cs:tmpname ; copy the temp file name
X lds si, ss:[a_tmp] ; 65 bytes worth.
X mov cx, 65
X call copy_data
X
; 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
X mov es:old_ss, ax
X mov es:old_sp, sp
X mov ax, cs
X mov ds, ax
X mov ss, ax ; set ss first, ints are then
X mov sp, offset cs:exec_sp ; disabled for this instr too
X assume ds:@code, ss:@code ; let the assembler know :-)
X
; 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
X mov [eretcode], 0 ; clear the return code
X mov ax, 03523H ; get int 23 handler address
X int 21H
X mov cs:old_ctl_brk_off, bx
X mov cs:old_ctl_brk_seg, es
X mov dx, offset ctl_brk_handler
X mov ax, 02523H ; set int 23 handler address
X int 21H
X
X mov ax, 03524H ; get int 24 handler address
X int 21H
X mov cs:old_crit_err_off, bx
X mov cs:old_crit_err_seg, es
SHAR_EOF
true || echo 'restore of dmake/msdos/exec.asm failed'
fi
echo 'End of part 22, continue with part 23'
echo 23 > _shar_seq_.tmp
exit 0
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent at sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent at uunet.uu.net.
More information about the Comp.sources.misc
mailing list