Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / dmake / msdos / exec.asm
blobdb745aece6b2b87080c9f8b51ca90570b8b6b969
1 ;
2 ; DESCRIPTION
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.
19 ; AUTHOR
20 ; Dennis Vadura, dvadura@watdragon.uwaterloo.ca
21 ; CS DEPT, University of Waterloo, Waterloo, Ont., Canada
23 ; COPYRIGHT
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.
40 ifdef have286
41 .286 ; define have286 with -D for 80286 processor or better
42 mpusha Macro
43 pusha
44 Endm
46 mpopa Macro
47 popa
48 Endm
50 else ; 8088/8086 compatible
51 mpusha Macro
52 push ax
53 push cx
54 push dx
55 push bx
56 push sp
57 push bp
58 push si
59 push di
60 Endm
62 mpopa Macro
63 pop di
64 pop si
65 pop bp
66 add sp,2
67 pop bx
68 pop dx
69 pop cx
70 pop ax
71 Endm
72 endif
74 ifdef msmall
75 .model small
76 argbase equ 4
77 endif
78 ifdef mcompact
79 .model compact
80 argbase equ 4
81 endif
82 ifdef mmedium
83 .model medium
84 argbase equ 6
85 endif
86 ifdef mlarge
87 .model large
88 argbase equ 6
89 endif
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.
109 .data
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
114 .code
115 assume cs:@code, ds:@code, ss:@code, es:@code
117 even
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
142 even
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, '$'
157 even
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
163 write_seg label word
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
173 read_seg label word
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
189 old_ctl_brk_off dw ?
190 old_ctl_brk_seg dw ?
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
212 ; duplicate write.
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.
225 simulate_21h Macro
226 pushf ;; direct call to DOS
227 call cs:[real_21h]
228 Endm
230 assume cs:@code, ds:nothing, es:nothing, ss:nothing
231 our_21h_handler proc far
232 pushf
233 cmp ah,40h ; is this a write?
234 jne call_dos ; --no
235 cmp bx,1 ; write on handle 1 (stdout?)
236 je duplicate_it
237 cmp bx,2 ; stderr?
238 je duplicate_it
240 call_dos:
241 popf
242 jmp [real_21h] ; far jump to real handler, which will do the sys call
243 ; and return to the original caller
245 duplicate_it:
246 mpusha
247 push ds
248 push es
249 mov bp,sp
251 mov di,std_fil_handle ; handle of the -C file
253 If @CodeSize eq 0
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?
260 je call_from_dmake
261 Endif
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
286 simulate_21h
287 mov si,dx ; save BREAK state in SI
288 sub dx,dx ; now turn break flag off
289 mov ax,3301h
290 simulate_21h ; don't want ^Break recoginized while PSP changed
291 mov bx,psp ; set dmake's PSP
292 mov ah,50h
293 simulate_21h
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
299 mov ah,40h
300 simulate_21h ; write the copy
302 mov bx,es ; caller's PSP
303 mov ah,50h ; set PSP
304 simulate_21h ; restore caller's PSP
305 mov dx,si ; break state before we changed it
306 mov ax,3301h
307 simulate_21h ; restore break state
309 jmp short do_real_write
311 use_dup:
312 mov ds,[bp+2] ; restore caller's DS
313 mov dx,[bp+14] ; DS:DX again points to caller's buffer
315 call_from_dmake:
316 mov bx,di ; handle of -C file
317 mov ah,40h ; write
318 ; CX still has caller's count
319 simulate_21h ; write to the file
321 do_real_write:
322 pop es
323 pop ds
324 mpopa
325 popf
326 jmp [real_21h] ; far jump to real handler, which will do the sys call
327 ; and return to the original caller
328 our_21h_handler endp
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 ...
338 pop ax
339 pop bx
340 pop cx
341 pop dx
342 pop si
343 pop di
344 pop bp
345 pop ds
346 pop es
347 push bp ; fix up the return flags
348 mov bp, sp
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.
352 pop bp
353 mov ax, 5 ; access denied
354 iret
355 crit_err_handler endp
358 ;-----------------------------------------------------------------------------
359 ; Here we set the interrupted flag, and terminate the currently running
360 ; process.
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.
369 cmp cs:in_exec,0
370 je just_return ; note this implies CF == 0
371 stc ; set CF to abort child
372 just_return: iret
373 ctl_brk_handler endp
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
382 jmp print_it
383 abort_exec_rseg label near
384 mov dx, offset error_rseg
385 jmp print_it
386 abort_exec_resize label near
387 mov dx, offset error_resize
388 jmp print_it
389 abort_exec_free label near
390 mov dx, offset error_free
391 jmp print_it
392 abort_exec_alloc label near
393 mov dx, offset error_alloc
394 jmp print_it
395 abort_exec proc near
396 mov dx, offset error_string
397 print_it: push dx
398 mov bx, [swap]
399 call [free_resource+bx]
400 mov ax, cs
401 mov ds, ax
402 pop dx
403 mov ah, 9
404 int 21H
405 kill_program: mov ax, 04cffH ; nuke it!
406 int 21H
407 abort_exec endp
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.
415 copy_data proc near
416 shr cx, 1 ; convert to word count
417 jnc copy_words
418 movsb
419 copy_words: rep movsw ; copy the words.
421 copy_data endp
425 ;=============================================================================
426 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ XMS RECORDS.
427 ;=============================================================================
428 rhdr_xms proc near
430 rhdr_xms endp
432 rseg_xms proc near
434 rseg_xms endp
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 ;=============================================================================
450 rhdr_ems proc near
452 rhdr_ems endp
454 rseg_ems proc near
456 rseg_ems endp
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.
481 rhdr_file proc near
482 mov dx, offset tmpseg ; read the header record out
483 mov cx, 7
484 mov bx, [tmphandle]
485 mov ah, 03fH
486 int 21H
487 jnc rhdr_done ; make sure it worked
488 jmp abort_exec_rhdr
490 rhdr_done: cmp ax, 7
491 je exit_rhdr_file
492 or ax, ax
493 je signal_eof
494 jmp abort_exec_rhdr
496 signal_eof: stc
497 exit_rhdr_file: ret
498 rhdr_file endp
501 ;-----------------------------------------------------------------------------
502 ; Read a segment from the temporary file whose handle is in cs:tmphandle.
503 ; The routine aborts if an error is detected.
504 rseg_file proc near
505 push ds
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
509 mov bx, cs:tmphandle
510 mov ah, 03fH
511 int 21H
512 pop ds
513 jnc rseg_done
514 jmp abort_exec_rseg
516 rseg_done: cmp ax, [word ptr tmpseg+4]
517 je exit_rseg_file
518 jmp abort_exec_rseg ; If we didn't get read full
519 exit_rseg_file: ret ; segment then abort
520 rseg_file endp
523 ;-----------------------------------------------------------------------------
524 ; Seek to the beginning of the file.
525 reset_file_resource proc near
526 mov bx, [tmphandle]
527 xor cx, cx
528 mov dx, cx
529 mov ax, 04200H ; seek to begining of file
530 int 21H
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
542 int 21H
543 mov dx, offset tmpname ; Now delete the temp file
544 mov ah, 041H
545 int 21H
547 free_file_resource endp
548 ;=============================================================================
552 ;=============================================================================
553 ; CODE TO SWAP THE IMAGE IN FROM SECONDARY STORAGE
554 ;=============================================================================
555 swap_in proc near
556 mov bx, [alstr] ; get previous alloc strategy
557 mov ax, 5801H ; and set it back
558 int 21H
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
563 mov ah, 04AH
564 int 21H
565 jnc read_seg_loop
566 jmp abort_exec
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
571 mov al, [tmpseg+6]
572 cmp al, seg_no_alloc ; see if dummy segment header
573 je read_seg_loop
574 cmp al, seg_alloc ; do we need to do an alloc?
575 jne read_data ; nope
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
583 int 21H
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]
596 swap_in endp
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
605 ; up to that point.
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
609 ; out.
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
616 swap_out proc near
617 mov ax, 05800H ; get memory alocation strategy
618 int 021H
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
624 dec ax
625 mov es, ax
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
632 shr ax, 1
633 shr ax, 1
634 shr ax, 1
635 shr ax, 1
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
639 mov rootsize, bx
640 mov resend, di ; cs:resend is saved start para
641 mov al, seg_no_alloc ; set no allocation for segment
642 call write_segment
643 jc abort_swap_out
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]
649 dec ax
650 mov es, ax
651 mov bx, offset write_segment_data
652 call walk_arena_chain
653 jc abort_swap_out
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]
659 dec ax
660 mov es,ax
661 mov bx, offset free_dos_segment
662 call walk_arena_chain
663 jnc resize_program
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
671 int 21H
672 jnc swap_out_ok
673 jmp abort_exec_resize ; disaster
674 swap_out_ok: ret
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]
680 xor ax, ax
681 mov [swap], ax ; clear the swap flag
684 swap_out endp
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.
692 do_exec proc near
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
697 call swap_out
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.
704 mov ax, [envseg]
705 or ax, ax
706 je setup_block
707 push ax
708 mov es, ax
709 mov ah, 49H
710 int 21H
711 pop ax
713 ; set up the parameter block for the DOS exec call.
714 ; offset contents
715 ; 00 segment address of environment to be passed,
716 ; 0 => use parents env.
717 ; 02 pointer to command tail for new process.
718 ; 06 pointer to fcb1
719 ; 0a pointer to fcb2
720 setup_block: mov ax, [envseg]
721 mov [ex_envseg], ax
722 mov cx, cs
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
730 mov es, cx
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.
735 push ds
736 mov [ex_sp], sp
737 mov [ex_ss], ss
738 mov [ex_error], 0 ; clear exec error code
739 inc [in_exec] ; set internal flag
740 mov ax, 04b00H
741 int 21H
743 ; returned from exec, so restore possibly clobbered registers.
744 mov ss, cs:ex_ss
745 mov sp, cs:ex_sp
746 pop ds
748 ; check to make certain the exec call worked.
749 jnc it_worked
751 ; exec call failed. Save return code from msdos.
752 mov [ex_error], ax
753 jmp leave_exec
755 it_worked: mov ah, 04dH ; get the return code
756 int 21H
757 cmp ah,1 ; check if terminated by ^C
758 jnz nosigint
759 inc interrupted ; yes so set flag
760 nosigint: xor ah, ah ; 8-bit return code, so clear ah
761 mov [eretcode], ax
763 leave_exec: cmp [swap], 0 ; check swap, if non-zero swap back in
764 je no_swap_in
765 call swap_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
769 ; the image in.
770 no_swap_in: mov [in_exec], 0
772 do_exec endp
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.
791 init_swap proc near
792 mov [swap], 0
793 ;call init_xms
794 ;jnc init_done
795 ;call init_ems
796 ;jnc init_done
797 call init_file
798 init_done: ret
799 init_swap endp
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.
808 memheader struc
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
812 memheader ends
814 walk_arena_chain proc near
815 mov si, word ptr es:3 ; get length
816 mov di, es
817 inc di
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
825 je next_block
826 cmp di, cs:psp ; skip our psp
827 je next_block
829 ; Now save state and call the routine pointed at by [bx].
830 push di
831 push si
832 push bx
833 call bx
834 pop bx
835 pop si
836 pop di
837 jc exit_walk ; if error then stop
838 mov al, byte ptr es:0 ; check if at end
839 cmp al, 'Z'
840 je walk_done
842 next_block: add di, si ; go on to next segment
843 mov es, di
844 jmp walk_arena_chain
845 walk_done: clc
846 exit_walk: ret
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
854 mov ah, 49H
855 int 21H
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
869 ; data out.
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
875 push di
876 push si
877 xor dx,dx
878 mov bx, [swap]
879 call [write_header+bx]
880 pop si
881 pop di
882 jc exit_wseg
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?
888 jle do_io
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
893 shl cx, 1
894 shl cx, 1
895 shl cx, 1
896 push di ; save the start, and count left
897 push si
898 mov si, cx
899 xor dx,dx
900 mov al, seg_data
901 mov bx, [swap]
902 push bx
903 call [write_header+bx]
904 pop bx
905 call [write_seg+bx]
906 pop si
907 pop di
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.
911 sub si, dx
912 jmp do_io_loop
913 exit_wseg: ret
914 write_segment endp
917 ;=============================================================================
918 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE XMS RECORDS.
919 ;=============================================================================
920 init_xms proc near
922 init_xms endp
924 whdr_xms proc near
926 whdr_xms endp
928 wseg_xms proc near
930 wseg_xms endp
931 ;=============================================================================
934 ;=============================================================================
935 ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE EMS RECORDS.
936 ;=============================================================================
937 init_ems proc near
939 init_ems endp
941 whdr_ems proc near
943 whdr_ems endp
945 wseg_ems proc near
947 wseg_ems endp
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.
957 init_file proc near
958 mov al, [tmpname]
959 or al, al
960 je err_init_file
961 mov dx, offset tmpname
962 xor cx, cx
963 mov ah, 03cH
964 int 21H
965 jc err_init_file ; if carry set then failure
966 mov [tmphandle], ax ; init swapping
967 mov [swap], swap_file
968 jmp exit_init_file
969 err_init_file: stc
970 exit_init_file: ret
971 init_file endp
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
986 whdr_file proc near
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
990 mov [tmpseg+6], al
991 mov dx, offset tmpseg ; write the header record out
992 mov cx, 7
993 mov bx, [tmphandle]
994 mov ah, 040H
995 int 21H
996 jc exit_whdr_file ; make sure it worked
997 cmp ax, 7
998 je exit_whdr_file ; oh oh, disk is full!
999 err_whdr_file: stc
1000 exit_whdr_file: ret
1001 whdr_file endp
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.
1008 wseg_file proc near
1009 push ds
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
1014 mov ah, 040H
1015 int 21H
1016 pop ds
1017 jc exit_wseg_file ; make sure it worked
1018 cmp ax, [word ptr tmpseg+4]
1019 je exit_wseg_file
1020 err_wseg_file: stc ; it failed (usually disk full)
1021 exit_wseg_file: ret
1022 wseg_file endp
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
1038 ; stack.
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
1042 ; status code.
1043 ; 5. get our psp
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
1050 ; interrupted.
1052 ; NOTE: When first called the segments here assume the standard segment
1053 ; settings.
1054 assume cs:@code, ds:DGROUP,es:DGROUP,ss:DGROUP
1056 public _exec
1057 _exec proc
1058 push bp ; set up the stack frame
1059 mov bp, sp
1060 push si ; save registers we shouldn't step on.
1061 push di
1062 push ds
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
1073 mov es:swap, ax
1074 mov ax, ss:[a_env] ; save env seg to use
1075 mov es:envseg, ax
1077 mov di, offset cs:cmdpath ; copy the command
1078 lds si, ss:[a_prog] ; 65 bytes worth
1079 mov cx, 65
1080 call copy_data
1082 mov di, offset cs:cmdtail ; copy the command tail
1083 lds si, ss:[a_tail] ; 129 bytes worth
1084 mov cx, 129
1085 call copy_data
1087 mov di, offset cs:tmpname ; copy the temp file name
1088 lds si, ss:[a_tmp] ; 65 bytes worth.
1089 mov cx, 65
1090 call copy_data
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
1096 mov es:old_ss, ax
1097 mov es:old_sp, sp
1098 mov ax, cs
1099 mov ds, ax
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
1110 int 21H
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
1115 int 21H
1117 mov ax, 03524H ; get int 24 handler address
1118 int 21H
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
1123 int 21H
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
1129 int 21H
1130 mov cs:psp, bx
1131 call do_exec
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
1136 mov ax, 02523H
1137 int 21H
1138 lds dx, cs:old_crit_err
1139 mov ax, 02524H
1140 int 21H
1142 ; Restore previous program stack segment registers, and data segment.
1143 mov ax, cs:old_ss
1144 mov ss, ax ; mov into ss first, that way
1145 mov sp, cs:old_sp ; no interrupts in this instr.
1146 pop ds
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
1153 mov es, ax
1154 mov ax, cs:interrupted
1155 mov es:_Interrupted, ax
1157 ; Set the global errno value to reflect the success/failure of the DOS
1158 ; exec call.
1159 mov ax, seg DGROUP:_errno
1160 mov es, ax
1161 mov ax, cs:ex_error
1162 mov es:_errno, ax
1164 ; Fetch the child's return code, pop rest of stuff off of the stack
1165 ; and return to the caller.
1166 mov ax, cs:eretcode
1167 pop di
1168 pop si
1169 pop bp
1171 _exec endp
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
1179 push bp
1180 mov bp,sp
1181 push di
1183 mov di, ss:[a_handle] ; handle of -C file
1184 mov std_fil_handle, di
1186 mov ah, 51h ; request our PSP
1187 int 21h
1188 mov [psp], bx ; save it
1190 mov es, bx
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
1202 push ds
1203 mov ax,cs
1204 mov ds,ax
1205 lea dx,our_21h_handler ; DS:DX is the new vector
1206 mov ax,2521h ; set vector 21h
1207 int 21h
1209 pop ds
1210 pop di
1211 pop bp
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
1221 push ds
1223 lds dx, [real_21h] ; put saved vector into DS:DX
1224 mov ax, ds
1225 or ax, dx
1226 jz unhook_return ; zero means we didn't hook 21h
1228 mov ax,2521h ; set vector 21h
1229 simulate_21h
1231 unhook_return: pop ds
1233 _do_unhook_std_writes endp