diff options
Diffstat (limited to 'dmake/msdos/exec.asm')
-rw-r--r-- | dmake/msdos/exec.asm | 1234 |
1 files changed, 1234 insertions, 0 deletions
diff --git a/dmake/msdos/exec.asm b/dmake/msdos/exec.asm new file mode 100644 index 000000000000..db745aece6b2 --- /dev/null +++ b/dmake/msdos/exec.asm @@ -0,0 +1,1234 @@ +; +; 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 |