MS-DOS patches to perl.
Apply this patch to the standard perl source, version 4, patch level 19,
using "patch -p."  Do this in the root directory of the perl source
distribution.

You can cat all these patches together and pipe the output to patch -p.

Len Reed
Holos Software, Inc.
..!gatech!holos0!lbr
holos0!lbr@gatech.edu
--------------------------------------
*** msdos/exec.asm.old	Sun Feb 23 08:48:16 1992
--- msdos/exec.asm	Thu Nov 14 08:56:34 1991
***************
*** 0 ****
--- 1,959 ----
+ ; Dennis Vadura, dvadura@watdragon.uwaterloo.ca, wrote this code
+ ; originally, though it has been reworked.  It comes from dmake 3.6, a
+ ; high-power portable (Unix, DOS, OS/2) make program, available
+ ; for anonymous ftp from watmsg.uwaterloo.ca.  Address is 129.97.129.9.
+ ; It is in the pub/src directory, set your mode to binary, and copy
+ ; either:
+ ;    dmake-3.6.tar.Z		- compressed tar format
+ ;    dmake-3.6.zoo		- zoo archive
+ 
+ 
+ ; Define MIN_DOS_VER to be minimum version of DOS to support times 100,
+ ; using the -D assembler option.  If undefined, assume 2.0.
+ ; Can be defined with the -d switch
+ 
+ Ifndef MIN_DOS_VER
+  MIN_DOS_VER Equ 200	; have to at least have handle I/O, in ver 2.00
+ Endif
+ 
+ 
+ ; You must assemble this file with masm (or tasm) flag
+ ;    -dmmodel where model is one of {small, compact, medium, large}
+ ;
+ 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>
+ 
+ ; 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
+ 
+ 		.data
+     extrn __osversion:word	; MS-DOS version, set by C library startup
+ 
+ 	        .code
+ 		assume	cs:@code, ds:@code, ss:@code, es:@code
+ 
+ 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
+ resend		dw	?		; paragraph where resident code ends
+ psp		dw	?		; our own psp
+ swap		dw	?		; swapping selection flag
+ retcode		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
+ os_major	db	?		; DOS major revision number
+ 
+ cmdpath		db	65  dup(?)	; file to exec
+ cmdtail		db	129 dup(?)	; it's command tail
+ fcb		db	37  dup(0)	; dummy fcb
+ tmpseg		db	7   dup(?)	; block header buffer
+ 
+ tmphandle	dw	?		; handle for temporary file
+ 
+ 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, '$'
+ 
+ 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
+ If MIN_DOS_VER lt 300
+   ex_ss		dw	?			; saved ss for exec
+   ex_sp		dw	?			; saved sp for exec
+ Endif
+ 
+ ;-----------------------------------------------------------------------------
+ ; 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 for DOS 2.x compatibility.  If we're sure
+ ; that we have >= 3.1, we fail the function call.
+ crit_err_handler proc far
+ If MIN_DOS_VER lt 310
+ 		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
+ Else
+ 		mov 	al,3		; fail the function
+ Endif
+ 		iret
+ crit_err_handler endp
+ 
+ 
+ ;-----------------------------------------------------------------------------
+ ; Here we set the interrupted flag, and terminate the currently running
+ ; process if it's the child.
+ ctl_brk_handler proc far
+ 		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 ess. proc 
+ 		cmp	cs:in_exec,0
+ 		je	just_return		; note this implies CF == 0
+ 		stc				; set CF to abort child
+ just_return:	ret
+ ctl_brk_handler endp
+ 
+ 
+ ;-----------------------------------------------------------------------------
+ ; Something really nasty happened, so abort the exec call and exit.
+ ; This kills the calling ess proc 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, cs:[tmphandle]
+ 		xor	cx, cx
+ 		mov	dx, cx
+ 		mov	ax, 04200H		; seek to begining of file
+ 		int	21H
+ 		ret
+ reset_file_resource endp
+ 
+ 
+ ;-----------------------------------------------------------------------------
+ ; Swap file cleanup now external to this code
+ free_file_resource proc near
+ 		ret
+ free_file_resource endp
+ ;=============================================================================
+ 
+ 
+ 
+ ;=============================================================================
+ ; CODE TO SWAP THE IMMAGE 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 IMMAGE 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
+ ;	  with the exception of the child environment segment
+ ;	- resize the current _psp segment to savesize
+ ;	- free the child environment segment (it will be cloned by DOS).
+ ;	  yes, this means that DOS is cloning from free space, but command.com
+ ;	  does it this way.
+ ;	- 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	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 ess. proc 
+ 		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 ess, proc except the one that is
+ ; the current ess' proc 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 minimum size.  This will
+ ; free the memory taken up by the current program segment.
+ resize_program: mov	es, [psp]		; es is segment to resize.
+ 		mov	ax, es
+ 		mov	bx, [resend]		; first free paragraph
+ 		sub	bx, ax			; compute size
+ 		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 our copy of the child's environment.  DOS will clone it from the
+ ; free space!  Yes, it's weird, but if we don't do it this way we waste
+ ; a copy of this space when the child is running.  COMMAND.COM does this.
+ 
+ 		mov	ax, cs:ex_envseg
+ 		or	ax, ax
+ 		jz	using_parent_env
+ 		mov	es, ax
+ 		mov	ah, 49h		; release the block
+ 		int 21h
+ using_parent_env:
+ 
+ ; 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 ess. proc 
+ ;        06  pointer to fcb1
+ ;        0a  pointer to fcb2
+ 		mov	cx, cs
+ 		mov	[word ptr ex_cmdtail], offset cmdtail
+ 		mov	[word ptr ex_cmdtail+2], cx
+ 	; ex_envseg already set
+ 
+ ; 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.
+ If MIN_DOS_VER lt 300
+ 		push	ds
+ 		mov	[ex_sp], sp
+ 		mov	[ex_ss], ss
+ Endif
+ 		inc	[in_exec]		; set internal flag
+ 		mov	ax, 04b00H
+ 		int	21H
+ 
+ ; returned from exec, so restore possibly clobbered registers.
+ If MIN_DOS_VER lt 300
+ 		mov	ss, cs:ex_ss
+ 		mov	sp, cs:ex_sp
+ 		pop	ds
+ Endif
+ 
+ ; check to make certain the exec call worked.
+ 		jnc	it_worked
+ 
+ ; exec call failed.  Make it look like child did exit(-1).
+ 		mov	[retcode], 0FFh
+ 		jmp	leave_exec
+ 
+ it_worked:	mov	ah, 04dH	; get the return code
+ 		int	21H
+ 		mov	[retcode], ax
+ 
+ leave_exec: 	mov	[in_exec], 0	; all done, reset in_exec flag
+ 		cmp	[swap], 0	; check swap, if non-zero swap back in
+ 		je	no_swap_in
+ 		call	swap_in
+ no_swap_in:	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
+ ; it's 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 alocated memory block chain and,
+ ; 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 it's 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
+ 		cmp	ax, cs:psp			; is it owned by us?
+ 		jne	walk_done			; NOPE!  -- all done
+ 		cmp	di, cs:ex_envseg		; is it child's env
+ 		je	walk_done			; hope this is last
+ 		cmp	di, cs:psp			; make sure we don't
+ 		je	next_block			; touch our psp
+ 		push	di
+ 		push	si
+ 		push	bx
+ 		call	bx				; handle the segment
+ 		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.
+ ;=============================================================================
+ ;-----------------------------------------------------------------------------
+ ; Rewind the temp file to the beginning.  Creation of file is now external
+ ; to this code.
+ init_file proc near
+ 		mov	ax, 4200h	; rewind file to beginning
+ 		mov	bx, [tmphandle]	; file handle
+ 		xor	cx, cx
+ 		mov	dx, cx		; offset zero
+ 		int	21h
+ 		jc	init_file_err
+ 		mov	[swap], swap_file
+ 
+ init_file_err:	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, int swap_file_handle)
+ ;
+ ; 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 ess, proc 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 ess proc stack, and segment registers.
+ ;      10. fudge child return code to look like "killed by Ctrl-break" if
+ ;	   interrupted flag was set.
+ ;      11. return from exec with child result code in AX.
+ 
+ ; 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
+ 
+ 		mov	ax, __osversion	; get MS-DOS revision
+ 		mov	os_major,al	; major revision number
+ 
+ ; 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:ex_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	ax, ss:[a_tmp]		; temp file handle
+ 		mov	[tmphandle], ax		; save the file handle
+ 
+ ; 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	[retcode], 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 it's 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, 062H		; 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
+ 
+ ; Test the interrupted flag and fudge the return code if it is set.
+ 		cmp	cs:interrupted, 0
+ 		je	not_interrupted
+ 		mov	cs:retcode, 100h ; DOS return value for Ctrl-break kill
+ not_interrupted:
+ 
+ ; Fetch the child's return code, pop rest of stuff off of the stack
+ ; and return to the caller.
+ 		mov	ax, cs:retcode
+ 		pop	di
+ 		pop	si
+ 		pop	bp
+ 		ret
+ _exec endp
+ 
+ end
