3 ; This code is a model independent version of DOS exec that will swap
4 ; the calling process out to secondary storage prior to running the
5 ; child. The prototype for calling the exec function is below.
7 ; exec( int swap, char far *program, char far *cmdtail,
8 ; int environment_seg, char far *tmpfilename );
11 ; To assemble this file issue the command:
13 ; tasm /mx /t /dmmodel exec.asm
15 ; where 'model' is one of {small, compact, medium, large}, you may
16 ; also use MASM 5.1 to assemble this file, in this case simply replace
17 ; 'tasm' with 'masm' in the above command line.
20 ; Dennis Vadura, dvadura@watdragon.uwaterloo.ca
21 ; CS DEPT, University of Waterloo, Waterloo, Ont., Canada
24 ; Copyright (c) 1990 by Dennis Vadura. All rights reserved.
26 ; This program is free software; you can redistribute it and/or
27 ; modify it under the terms of the GNU General Public License
28 ; (version 1), as published by the Free Software Foundation, and
29 ; found in the file 'LICENSE' included with this distribution.
31 ; This program is distributed in the hope that it will be useful,
32 ; but WITHOUT ANY WARRANTY; without even the implied warrant of
33 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 ; GNU General Public License for more details.
36 ; You should have received a copy of the GNU General Public License
37 ; along with this program; if not, write to the Free Software
38 ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
41 .286 ; define have286 with -D for 80286 processor or better
50 else
; 8088/8086 compatible
90 a_swap
equ <bp+argbase
+0>
91 a_prog
equ <bp+argbase
+2>
92 a_tail
equ <bp+argbase
+6>
93 a_env
equ <bp+argbase
+10>
94 a_tmp
equ <bp+argbase
+12>
96 a_handle
equ <bp+argbase
>
99 ; Define all useful equ's
100 swap_xms
equ 0 ; we swapped it out to xms
101 swap_ems
equ 2 ; we swapped it out to ems
102 swap_file
equ 4 ; we swapped it out to a file
103 seg_no_alloc
equ 0 ; this is part of a segment
104 seg_alloc
equ 1 ; this is a full segment header
105 seg_data
equ 2 ; this is data for part of a segment
108 ; Define any global/external variables that we will be accessing from here.
110 extrn
_errno:word ; Set to dos ret code from exec
111 public _Interrupted
; Set to 1 if interrupted 0
112 _Interrupted
dw 0 ; otherwise
115 assume cs:@code, ds:@code, ss:@code, es:@code
118 execstack
dw 64 dup
(?) ; put the temporary exec stack right
119 exec_sp
label word ; at the start.
121 old_ss
dw ? ; save stack seg across exec
122 old_sp
dw ? ; save stack ptr across exec
123 progsize
dw ? ; original size of the program
124 rootsize
dw ? ; size of base root kept during swap
125 resend
dw ? ; paragraph where resident code ends
126 envseg
dw ? ; paragraph of environment segment
127 psp
dw ? ; our own psp
128 swap
dw ? ; swapping selection flag
129 eretcode
dw ? ; return code from exec
130 interrupted
dw ? ; interrupted flag for exec
131 arenahead
dw ? ; start of memory block list
132 alstr
dw ? ; allocation strategy save spot
133 in_exec
dw 0 ; flag, 1 ==> in exec
135 cmdpath
db 65 dup
(?) ; file to exec
136 cmdtail
db 129 dup
(?) ; its command tail
137 fcb
db 37 dup
(0) ; dummy fcb
138 tmpseg
db 7 dup
(?) ; block header buffer
140 tmpname
db 65 dup
(0) ; name of temporary file resource
143 tmphandle
dw ? ; handle for temporary file
144 real_21h
dd 0 ; will be DOS's 21h vector if doing -C
146 std_fil_handle
dw ? ; file handle for -C file
147 std_fil_number
db ? ; system file number for -C file
148 our_stdout
db ? ; sys file number our stdout handle
150 error_rhdr
db "exec: Failure reading header block", 0DH, 0AH, '$'
151 error_rseg
db "exec: Failure reading segment data", 0DH, 0AH, '$'
152 error_resize
db "exec: Failure on resize", 0DH, 0AH, '$'
153 error_free
db "exec: Failure to free a block", 0DH, 0AH, '$'
154 error_string
db "exec: Program swap failure", 0DH, 0AH, '$'
155 error_alloc
db "exec: Memory blocks don't match", 0DH, 0AH, '$'
158 write_header
label word
159 whdr_xms_ptr
dw word ptr whdr_xms
160 whdr_ems_ptr
dw word ptr whdr_ems
161 whdr_file_ptr
dw word ptr whdr_file
164 wseg_xms_ptr
dw word ptr wseg_xms
165 wseg_ems_ptr
dw word ptr wseg_ems
166 wseg_file_ptr
dw word ptr wseg_file
168 read_header
label word
169 rhdr_xms_ptr
dw word ptr rhdr_xms
170 rhdr_ems_ptr
dw word ptr rhdr_ems
171 rhdr_file_ptr
dw word ptr rhdr_file
174 rseg_xms_ptr
dw word ptr rseg_xms
175 rseg_ems_ptr
dw word ptr rseg_ems
176 rseg_file_ptr
dw word ptr rseg_file
178 free_resource
label word
179 free_xms_ptr
dw word ptr free_xms_resource
180 free_ems_ptr
dw word ptr free_ems_resource
181 free_file_ptr
dw word ptr free_file_resource
183 reset_resource
label word
184 reset_xms_ptr
dw word ptr reset_xms_resource
185 reset_ems_ptr
dw word ptr reset_ems_resource
186 reset_file_ptr
dw word ptr reset_file_resource
188 old_ctl_brk
label dword
192 old_crit_err
label dword
193 old_crit_err_off
dw ?
194 old_crit_err_seg
dw ?
196 exec_block
label word
197 ex_envseg
dw ? ; env seg, use parent's if 0
198 ex_cmdtail
dd ? ; command tail for exec
199 ex_fcb1
dd far ptr fcb
; fcb's aren't used by dmake
200 ex_fcb2
dd far ptr fcb
201 ex_ss
dw ? ; saved ss for exec
202 ex_sp
dw ? ; saved sp for exec
203 ex_error
dw 0 ; error code for dos exec
206 ; Special 21h (DOS call) handler to tee stdout/stderr writes to the -C file.
207 ; Ignore 21h calls that aren't writes to 1 or 2; i.e., pass them to DOS handler.
208 ; If write call was from this process, it's pretty simple to duplicate it
209 ; to the -C file. If it's from another process, we try to write to its
210 ; inherited handle. Worst case is where the handle wasn't inherited: someone
211 ; closed it. In that instance we have to switch to dmake's PSP to do the
214 ; Subprocesses do not get their stdout/stderr teed to the -C file if
215 ; their stdout/stderr no longer points to the file/device that dmake's
216 ; stdout points to. This is tested by looking at the process's job
217 ; file table, which is a table that maps process handles to DOS system file
218 ; table numbers. (The far pointer to the JFT is at the PSP offset 34h.)
219 ; The JFT is also queried to see if the -C file was inherited.
221 ; O_BINARY, O_TEXT problems are ignored here. These are fudged by the
222 ; C library before it calls DOS; since we're working below that level
223 ; we don't have to worry about it.
226 pushf ;; direct call to DOS
230 assume cs:@code, ds:nothing
, es:nothing
, ss:nothing
231 our_21h_handler
proc far
233 cmp ah,40h ; is this a write?
235 cmp bx,1 ; write on handle 1 (stdout?)
242 jmp [real_21h
] ; far jump to real handler, which will do the sys call
243 ; and return to the original caller
251 mov di,std_fil_handle
; handle of the -C file
254 ; Small/compact models allow for quick test of us versus subprocess.
255 ; False negative (it's us with a different CS) will be picked
256 ; up by code just below. (Might happen due to call from C library.)
257 ; False positives would be bad, but can't happen.
258 mov ax,[bp+24] ; caller's CS
259 cmp ax,@code ; same as us?
263 mov ah,51h ; get PSP ("undocumented version" works in DOS 2.0+)
264 simulate_21h
; PSP segment returned in BX
265 cmp bx,psp
; our PSP?
266 je call_from_dmake
; --yes, no PSP changing needed
268 mov es,bx ; set ES to current (caller's) PSP
269 lds bx,es:[34h] ; set DS:BX pointing to caller's job file table
271 mov si,[bp+12] ; file handle caller passed in (known to be 1 or 2)
272 mov al,[bx+si] ; system file number corresponding to caller's handle
273 cmp al,our_stdout
; same as our stdout?
274 jne do_real_write
; no--subprocess must have redirected it
276 mov al,[bx+di] ; see if caller has dup of -C file still open
277 cmp al,std_fil_number
278 je use_dup
; yes--we can write using caller's PSP
280 ; Calling process (or some intermediate process) has closed
281 ; the -C descriptor. We'll use dmake's (our) -C descriptor, but
282 ; to do so we'll have to change the PSP. Disable BREAK handling
283 ; so that ^break doesn't kill the wrong process.
285 mov ax,3300h ; get BREAK flag
287 mov si,dx ; save BREAK state in SI
288 sub dx,dx ; now turn break flag off
290 simulate_21h
; don't want ^Break recoginized while PSP changed
291 mov bx,psp
; set dmake's PSP
295 mov bx,di ; handle of -C file
296 ; CX still has caller's count
297 mov ds,[bp+2] ; restore caller's DS
298 mov dx,[bp+14] ; DS:DX again points to caller's buffer
300 simulate_21h
; write the copy
302 mov bx,es ; caller's PSP
304 simulate_21h
; restore caller's PSP
305 mov dx,si ; break state before we changed it
307 simulate_21h
; restore break state
309 jmp short do_real_write
312 mov ds,[bp+2] ; restore caller's DS
313 mov dx,[bp+14] ; DS:DX again points to caller's buffer
316 mov bx,di ; handle of -C file
318 ; CX still has caller's count
319 simulate_21h
; write to the file
326 jmp [real_21h
] ; far jump to real handler, which will do the sys call
327 ; and return to the original caller
330 assume cs:@code, ds:@code, ss:@code, es:@code
332 ;-----------------------------------------------------------------------------
333 ; First define the critical-error and control-brk handlers.
334 ; The critical error handler simply pops the machine state and returns an
335 ; access denied result code.
336 crit_err_handler
proc far
337 add sp, 6 ; ip/cs/flags ...
347 push bp ; fix up the return flags
349 xchg ax, [bp+6] ; get the flag byte.
350 or ax, 1 ; set the carry bit
351 xchg ax, [bp+6] ; put it back.
353 mov ax, 5 ; access denied
355 crit_err_handler
endp
358 ;-----------------------------------------------------------------------------
359 ; Here we set the interrupted flag, and terminate the currently running
361 ctl_brk_handler
proc far
362 clc ; make sure carry is clear
363 inc cs:interrupted
; set the flag
365 ; Make certain it isn't us that is going to get terminated.
366 ; There is a small window where the in_exec flag is set but the child is
367 ; not running yet, I assume that DOS doesn't test for ctl_brk at that time
368 ; as it is bussily creating a new process.
370 je just_return
; note this implies CF == 0
371 stc ; set CF to abort child
376 ;-----------------------------------------------------------------------------
377 ; Something really nasty happened, so abort the exec call and exit.
378 ; This kills the calling process altogether, and is a very nasty way of
379 ; termination since files may still be open etc.
380 abort_exec_rhdr
label near
381 mov dx, offset error_rhdr
383 abort_exec_rseg
label near
384 mov dx, offset error_rseg
386 abort_exec_resize
label near
387 mov dx, offset error_resize
389 abort_exec_free
label near
390 mov dx, offset error_free
392 abort_exec_alloc
label near
393 mov dx, offset error_alloc
396 mov dx, offset error_string
399 call [free_resource
+bx]
405 kill_program: mov ax, 04cffH
; nuke it!
410 ;-----------------------------------------------------------------------------
411 ; lodsw/stosw loop to copy data. Called only for word copy operations.
412 ; ds:si - point at source
413 ; es:di - point at destination
414 ; cx - count of bytes to copy.
416 shr cx, 1 ; convert to word count
419 copy_words: rep movsw ; copy the words.
425 ;=============================================================================
426 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ XMS RECORDS.
427 ;=============================================================================
436 reset_xms_resource
proc near
438 reset_xms_resource
endp
440 free_xms_resource
proc near
442 free_xms_resource
endp
443 ;=============================================================================
447 ;=============================================================================
448 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ EMS RECORDS.
449 ;=============================================================================
458 reset_ems_resource
proc near
460 reset_ems_resource
endp
462 free_ems_resource
proc near
464 free_ems_resource
endp
465 ;=============================================================================
469 ;=============================================================================
470 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ FILE RECORDS.
471 ;=============================================================================
472 ; This routine reads a segment header from a file.
473 ; The header is a seven byte record formatted as follows:
474 ; segment address - of data
475 ; offset address - of data
476 ; length in paragraphs - of data
477 ; mode - 1 => segment header (allocate seg on read)
478 ; 0 => subsegment, don't allocate on read.
479 ; The information is placed into the tmpseg data area in the code segment.
480 ; The routine aborts if an error is detected.
482 mov dx, offset tmpseg
; read the header record out
487 jnc rhdr_done
; make sure it worked
501 ;-----------------------------------------------------------------------------
502 ; Read a segment from the temporary file whose handle is in cs:tmphandle.
503 ; The routine aborts if an error is detected.
506 mov ds, word ptr cs:tmpseg
; Now read the whole segment
507 mov dx, word ptr cs:tmpseg
+2
508 mov cx, word ptr cs:tmpseg
+4
516 rseg_done: cmp ax, [word ptr tmpseg
+4]
518 jmp abort_exec_rseg
; If we didn't get read full
519 exit_rseg_file: ret ; segment then abort
523 ;-----------------------------------------------------------------------------
524 ; Seek to the beginning of the file.
525 reset_file_resource
proc near
529 mov ax, 04200H ; seek to begining of file
532 reset_file_resource
endp
535 ;-----------------------------------------------------------------------------
536 ; unlink the temporary file allocated for swapping.
537 ; We close the file first, and then delete it. We ignore errors here since
538 ; we can't do anything about them anyway.
539 free_file_resource
proc near
540 mov bx, [tmphandle
] ; get the file handle
541 mov ah, 03eH
; close the file
543 mov dx, offset tmpname
; Now delete the temp file
547 free_file_resource
endp
548 ;=============================================================================
552 ;=============================================================================
553 ; CODE TO SWAP THE IMAGE IN FROM SECONDARY STORAGE
554 ;=============================================================================
556 mov bx, [alstr
] ; get previous alloc strategy
557 mov ax, 5801H ; and set it back
559 mov bx, [swap
] ; get type of resource
560 call [reset_resource
+bx] ; reset the resource
561 mov es, [psp
] ; resize the program back
562 mov bx, [progsize
] ; to original size
568 read_seg_loop: mov bx, [swap
] ; get type of resource
569 call [read_header
+bx] ; get seg header
570 jc exit_swap_in
; all done
572 cmp al, seg_no_alloc
; see if dummy segment header
574 cmp al, seg_alloc
; do we need to do an alloc?
577 ; Allocate back the memory for a segment that is not the [psp], note that this
578 ; must come back to the same segment we had previously since other segments
579 ; may have pointers stored in their variables that point to this segment using
580 ; segment:offset long pointers.
581 mov bx, [word ptr tmpseg
+4] ; get count of paragraphs
582 mov ah, 048H ; dos_alloc
584 jc alloc_error
; oops!
585 cmp ax, [word ptr tmpseg
] ; did we get the same segment?
586 je read_seg_loop
; yup!
587 alloc_error: jmp abort_exec_alloc
589 read_data: mov bx, [swap
]
590 call [read_seg
+bx] ; this must succeed, if fail
591 jmp read_seg_loop
; we never come back here
593 exit_swap_in: mov bx, [swap
] ; all done, so free resource
594 call [free_resource
+bx]
599 ;=============================================================================
600 ; CODE TO SWAP THE IMAGE OUT TO SECONDARY STORAGE
601 ;=============================================================================
602 ; This routine is called to swap the non-resident portion of the program
603 ; out to the resource specified by the value of [cs:swap]. If the swap out
604 ; fails, then appropriate routines are called to free the resources allocated
607 ; The steps used to swap the program out are as follows:
608 ; - calculate new size of program to remain resident and size to swap
610 ; - write out non-resident portion of current segment
611 ; - walk DOS allocation chain and write out all other segments owned by
612 ; the current program that are contiguous with the _psp segment
613 ; - copy the environment down to low memory
614 ; - resize the current _psp segment to savesize
615 ; - free all segments belonging to program except current _psp segment
617 mov ax, 05800H ; get memory alocation strategy
619 mov [alstr
], ax ; and save it for future restoration.
620 mov di, [psp
] ; compute length of program to current
621 mov bx, cs ; value of cs, and find program size
622 sub bx, di ; by looking at length stored in
623 mov ax, di ; arena header found in front of psp
626 mov si, es:3 ; si is size of program in paragraphs
627 mov [progsize
], si ; progsize now contains the size.
629 ; Now compute length of program segment to save.
630 ; Length is: cs - psp + (offset overlay_code_here+15 >> 4)
631 mov ax, offset overlay_code_here
+15
636 add bx, ax ; bx is size of program to keep
637 sub si, bx ; si is # of paragraphs to save.
638 add di, bx ; di is paragraph to start at
640 mov resend
, di ; cs:resend is saved start para
641 mov al, seg_no_alloc
; set no allocation for segment
645 ; We have now saved the portion of the program segment that will not remain
646 ; resident during the exec. We should now walk the DOS allocation chain and
647 ; write out all other segments owned by the current process.
648 save_segments: mov ax, [psp
]
651 mov bx, offset write_segment_data
652 call walk_arena_chain
655 ; Now we must walk the chain of allocated memory blocks again and free
656 ; all those that are owned by the current process, except the one that is
657 ; the current process' psp.
658 free_segments: mov ax, [psp
]
661 mov bx, offset free_dos_segment
662 call walk_arena_chain
664 jmp abort_exec_free
; can't fix it up now.
666 ; We now resize the program to the size specified by cs:rootsize. This will
667 ; free most of the memory taken up by the current program segment.
668 resize_program: mov es, [psp
] ; es is segment to resize.
669 mov bx, [rootsize
] ; bx is size of segment.
670 mov ah, 04aH ; resize memory block
673 jmp abort_exec_resize
; disaster
676 ; The swap out failed for some reason, so free any allocated resources
677 ; and set the carry bit.
678 abort_swap_out: mov bx, [swap
]
679 call [free_resource
+bx]
681 mov [swap
], ax ; clear the swap flag
687 ;=============================================================================
688 ; CODE TO SET-UP FOR AND EXEC THE CHILD PROCESS
689 ;=============================================================================
690 ; Actually execute the program. If cs:swap is set, this code will invoke the
691 ; swap-out/swap-in code as required.
693 cmp [swap
], 0 ; does the user want to swap?
694 je no_swap_out
; nope
695 call init_swap
; figger out where to swap to
696 jc no_swap_out
; if carry set then don't swap
699 no_swap_out: cmp [interrupted
], 0 ; were we interrupted?
700 jne leave_exec
; yep, so clean up, don't exec
702 ; free passed in environment block if it is non zero.
703 ; This way the parent program does not need to free it.
713 ; set up the parameter block for the DOS exec call.
715 ; 00 segment address of environment to be passed,
716 ; 0 => use parents env.
717 ; 02 pointer to command tail for new process.
720 setup_block: mov ax, [envseg
]
723 mov [word ptr ex_cmdtail
], offset cmdtail
724 mov [word ptr ex_cmdtail
+2], cx
726 ; set up registers for exec call
727 ; ds:dx - pointer to pathname of program to execute
728 ; es:bx - pointer to above parameter block
729 mov dx, offset cmdpath
731 mov bx, offset exec_block
733 ; Under DOS 2.x exec is notorious for clobbering registers and guarantees
734 ; to preserve only cs:ip.
738 mov [ex_error
], 0 ; clear exec error code
739 inc [in_exec
] ; set internal flag
743 ; returned from exec, so restore possibly clobbered registers.
748 ; check to make certain the exec call worked.
751 ; exec call failed. Save return code from msdos.
755 it_worked: mov ah, 04dH ; get the return code
757 cmp ah,1 ; check if terminated by ^C
759 inc interrupted
; yes so set flag
760 nosigint: xor ah, ah ; 8-bit return code, so clear ah
763 leave_exec: cmp [swap
], 0 ; check swap, if non-zero swap back in
767 ; Clear the in_exec after the swap back in. This way we are guaranteed to
768 ; get parent in and the resources freed should a ^C be hit when we are reading
770 no_swap_in: mov [in_exec
], 0
776 ;==============================================================================
777 ; Everything past this point is overwriten with the environment and new
778 ; program after the currently executing program is swapped out.
779 ;==============================================================================
780 overlay_code_here
label word
782 ;-----------------------------------------------------------------------------
783 ; Figure out where we can swap to and initialize the resource we are going to
784 ; use. We try XMS, EMS, and a tempfile (if specified), in that order. We set
785 ; [cs:swap] to the correct value based on which of the resources exists.
786 ; If none can be used, then [cs:swap] is set to 0, and no swap takes place.
787 ; The exec code will still attempt to execute the child in this instance, but
788 ; may fail due to lack of resources. Each swap_out_* routine must provide
789 ; its own clean-up handler should it not be able to write all program
790 ; segments to the swap resource.
802 ;-----------------------------------------------------------------------------
803 ; This routine is used to walk the DOS allocated memory block chain
804 ; starting at address supplied in the es register. For each block it
805 ; calls the routine specified by the bx register with the segment length
806 ; in si, and its address in di. It does not apply the routine to the
807 ; segment if the segment is the same as the current program's [cs:psp] value.
809 magic
db ? ; either 'Z' for end or 'M' for allocated
810 owner
dw ? ; psp of owner block
811 len
dw ? ; length in paragraphs of segment
814 walk_arena_chain
proc near
815 mov si, word ptr es:3 ; get length
818 mov ax, word ptr es:1
820 ; Stop the search if the block is NOT owned by us. Ignore our own psp block
821 ; and our environment segment block.
822 cmp ax, cs:psp
; is it owned by us?
823 jne walk_done
; NOPE! -- all done
824 cmp di, cs:envseg
; skip our environment
826 cmp di, cs:psp
; skip our psp
829 ; Now save state and call the routine pointed at by [bx].
837 jc exit_walk
; if error then stop
838 mov al, byte ptr es:0 ; check if at end
842 next_block: add di, si ; go on to next segment
847 walk_arena_chain
endp
850 ;-----------------------------------------------------------------------------
851 ; This routine takes a dos segment found in the di register and free's it.
852 free_dos_segment
proc near
853 mov es, di ; free dos memory block
857 free_dos_segment
endp
860 ;-----------------------------------------------------------------------------
861 ; Called to invoke write_segment with proper values in the al register. Only
862 ; ever called from walk_arena_chain, and so al should be set to seg_alloc.
863 write_segment_data
label near
864 mov al, seg_alloc
; and fall through into write_segment
865 ;-----------------------------------------------------------------------------
866 ; This routine writes a segment as a block of data segments if the number of
867 ; paragraphs to write exceeds 0x0fff (rarely the case).
868 ; It stuffs the info into tmpseg, and then calls wheader and wseg to get the
871 ; di:dx segment:offset of segment; offset is ALWAYS zero.
872 ; si number of paragraphs to write.
873 ; al mode of header to write
874 write_segment
proc near
879 call [write_header
+bx]
884 do_io_loop: cmp si, 0 ; are we done yet?
885 je exit_wseg
; yup so leave.
886 mov cx, si ; # of paragraphs to move
887 cmp cx, 0fffH
; see if we have lots to move?
889 mov cx, 0fffH
; reset to max I/O size
891 do_io: push cx ; save # of paragraphs we are writing
892 shl cx, 1 ; shift cx by four to the left
896 push di ; save the start, and count left
903 call [write_header
+bx]
908 pop dx ; original paragraph count in dx
909 jc exit_wseg
; it failed so exit.
910 add di, dx ; adjust the pointers, and continue.
917 ;=============================================================================
918 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE XMS RECORDS.
919 ;=============================================================================
931 ;=============================================================================
934 ;=============================================================================
935 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE EMS RECORDS.
936 ;=============================================================================
948 ;=============================================================================
951 ;=============================================================================
952 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE FILES.
953 ;=============================================================================
954 ;-----------------------------------------------------------------------------
955 ; Attempt to create a temporary file. If the tempfile name is NIL then return
956 ; with the cary flag set.
961 mov dx, offset tmpname
965 jc err_init_file
; if carry set then failure
966 mov [tmphandle
], ax ; init swapping
967 mov [swap
], swap_file
974 ;-----------------------------------------------------------------------------
975 ; This routine writes a segment header to a file.
976 ; The header is a seven byte record formatted as follows:
977 ; segment address - of data
978 ; offset address - of data
979 ; length in paragraphs - of data
980 ; mode - 1 => segment header (allocate seg on read)
981 ; 0 => subsegment, don't allocate on read.
982 ; Routine takes three arguments:
983 ; di:dx segment:offset of segment
984 ; si number of paragraphs to write.
985 ; al mode of header to write
987 mov [word ptr tmpseg
], di ; save the segment/offset
988 mov [word ptr tmpseg
+2], dx
989 mov [word ptr tmpseg
+4], si ; save the segment length
991 mov dx, offset tmpseg
; write the header record out
996 jc exit_whdr_file
; make sure it worked
998 je exit_whdr_file
; oh oh, disk is full!
1004 ;-----------------------------------------------------------------------------
1005 ; Write a segment to the temporary file whose handle is in cs:tmphandle
1006 ; Parameters for the write are assumed to be stored in the tmpseg data area.
1007 ; function returns carry set if failed, carry clear otherwise.
1010 mov ds, word ptr cs:tmpseg
; Now write the whole segment
1011 mov dx, word ptr cs:tmpseg
+2
1012 mov cx, word ptr cs:tmpseg
+4
1013 mov bx, cs:tmphandle
1017 jc exit_wseg_file
; make sure it worked
1018 cmp ax, [word ptr tmpseg
+4]
1020 err_wseg_file: stc ; it failed (usually disk full)
1023 ;=============================================================================
1026 ;=============================================================================
1027 ; _exec: THIS IS THE MAIN ENTRY ROUTINE TO THIS MODULE
1028 ;=============================================================================
1029 ; This is the main entry routine into the swap code and corresponds to the
1030 ; following C function call:
1032 ; exec( int swap, char far *program, char far *cmdtail, int environment_seg,
1033 ; char far *tmpfilename );
1035 ; Exec performs the following:
1036 ; 1. set up the local code segment copies of arguments to the exec call.
1037 ; 2. switch to a local stack frame so that we don't clobber the user
1039 ; 3. save old interrupt vectors for ctrl-brk.
1040 ; 4. install our own handler for the ctrl-brk interrupt, our handler
1041 ; terminates the current running process, and returns with non-zero
1044 ; 6. setup arguments for exec call
1045 ; 7. exec the program, save result code on return.
1046 ; 8. restore previous ctrl-brk and crit-error handler.
1047 ; 9. restore previous process stack, and segment registers.
1048 ; 10. return from exec with child result code in AX
1049 ; and global _Interrupted flag set to true if child execution was
1052 ; NOTE: When first called the segments here assume the standard segment
1054 assume cs:@code, ds:DGROUP
,es:DGROUP
,ss:DGROUP
1058 push bp ; set up the stack frame
1060 push si ; save registers we shouldn't step on.
1064 ; set up for copying of parameters passed in with long pointers.
1065 push cs ; going to use lodsb/stosb, set up es
1066 pop es ; as destination.
1067 assume es:@code ; let the assembler know :-)
1068 cld ; make sure direction is right
1070 ; Copy all parameters into the bottom of the code segment. After doing so we
1071 ; will immediately switch stacks, so that the user stack is preserved intact.
1072 mov ax, ss:[a_swap
] ; save swap
1074 mov ax, ss:[a_env
] ; save env seg to use
1077 mov di, offset cs:cmdpath
; copy the command
1078 lds si, ss:[a_prog
] ; 65 bytes worth
1082 mov di, offset cs:cmdtail
; copy the command tail
1083 lds si, ss:[a_tail
] ; 129 bytes worth
1087 mov di, offset cs:tmpname
; copy the temp file name
1088 lds si, ss:[a_tmp
] ; 65 bytes worth.
1092 ; Now we save the current ss:sp stack pointer and swap stack to our temporary
1093 ; stack located in the current code segment. At the same time we reset the
1094 ; segment pointers to point into the code segment only.
1095 swap_stacks: mov ax, ss
1100 mov ss, ax ; set ss first, ints are then
1101 mov sp, offset cs:exec_sp
; disabled for this instr too
1102 assume ds:@code, ss:@code ; let the assembler know :-)
1104 ; Now we save the old control break and critical error handler addresses.
1105 ; We replace them by our own routines found in the resident portion of the
1106 ; swapping exec code.
1107 set_handlers: mov [interrupted
], 0 ; clear interrupted flag
1108 mov [eretcode
], 0 ; clear the return code
1109 mov ax, 03523H ; get int 23 handler address
1111 mov cs:old_ctl_brk_off
, bx
1112 mov cs:old_ctl_brk_seg
, es
1113 mov dx, offset ctl_brk_handler
1114 mov ax, 02523H ; set int 23 handler address
1117 mov ax, 03524H ; get int 24 handler address
1119 mov cs:old_crit_err_off
, bx
1120 mov cs:old_crit_err_seg
, es
1121 mov dx, offset crit_err_handler
1122 mov ax, 02524H ; set int 24 handler address
1125 ; Go and execute the child, we've set up all of its parameters. The do_exec
1126 ; routine will attempt to perform a swap of the code if requested to do so by
1127 ; a non-zero value in the variable cs:swap.
1128 mov ah, 051H ; get the psp
1133 ; We're back from the exec, so fix things up the way they were.
1134 ; Restore the old control-break and critical-error handlers.
1135 lds dx, cs:old_ctl_brk
1138 lds dx, cs:old_crit_err
1142 ; Restore previous program stack segment registers, and data segment.
1144 mov ss, ax ; mov into ss first, that way
1145 mov sp, cs:old_sp
; no interrupts in this instr.
1148 ; Tell the assembler we have swaped segments again.
1149 assume ds:DGROUP
,es:DGROUP
,ss:DGROUP
1151 ; Set the global Interrupted flag so that parent can tell it was interrupted.
1152 mov ax, seg DGROUP:_Interrupted
1154 mov ax, cs:interrupted
1155 mov es:_Interrupted
, ax
1157 ; Set the global errno value to reflect the success/failure of the DOS
1159 mov ax, seg DGROUP:_errno
1164 ; Fetch the child's return code, pop rest of stuff off of the stack
1165 ; and return to the caller.
1173 ; void do_hook_std_writes(int handle);
1174 ; This saves the 21h interrupt vector and changes it to point
1175 ; into this code. Argument is the file handle of the -C file.
1177 public _do_hook_std_writes
1178 _do_hook_std_writes
proc
1183 mov di, ss:[a_handle
] ; handle of -C file
1184 mov std_fil_handle
, di
1186 mov ah, 51h ; request our PSP
1188 mov [psp
], bx ; save it
1191 les bx, es:[34h] ; pointer to job file table
1192 mov al, es:[bx+1] ; system file # of our stdout
1193 mov [our_stdout
], al
1194 mov al, es:[bx+di] ; system file number of -C file
1195 mov std_fil_number
, al
1197 mov ax,3521h ; request vector 21h
1198 int 21h ; it's returned in ES:BX
1199 mov word ptr [real_21h
], bx
1200 mov word ptr [real_21h
+2], es
1205 lea dx,our_21h_handler
; DS:DX is the new vector
1206 mov ax,2521h ; set vector 21h
1213 _do_hook_std_writes
endp
1215 ; void do_unhook_std_writes(void);
1216 ; This restores the 21h interrupt vector.
1217 ; The saved vector is zero if it wasn't changed (no -C option).
1219 public _do_unhook_std_writes
1220 _do_unhook_std_writes
proc
1223 lds dx, [real_21h
] ; put saved vector into DS:DX
1226 jz unhook_return
; zero means we didn't hook 21h
1228 mov ax,2521h ; set vector 21h
1231 unhook_return: pop ds
1233 _do_unhook_std_writes
endp