dvadura@watdragon.waterloo.edu (Dennis Vadura) (05/13/91)
Submitted-by: Dennis Vadura <dvadura@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@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@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,@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@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@uunet.uu.net.