home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / C / DMAKE37S.ZIP / DMAKE / MSDOS / EXEC.ASM < prev    next >
Encoding:
Assembly Source File  |  1991-03-20  |  36.3 KB  |  1,225 lines

  1. ; DESCRIPTION
  2. ;      This code is a model independent version of DOS exec that will swap
  3. ;      the calling process out to secondary storage prior to running the
  4. ;      child.  The prototype for calling the exec function is below.
  5. ;
  6. ;      exec( int swap, char far *program, char far *cmdtail,
  7. ;         int environment_seg, int env_size, char far *tmpfilename );
  8. ;
  9. ;
  10. ;      To assemble this file issue the command:
  11. ;
  12. ;         tasm /mx /t /dmmodel exec.asm
  13. ;
  14. ;      where 'model' is one of {small, compact, medium, large}, you may
  15. ;      also use MASM 5.1 to assemble this file, in this case simply replace
  16. ;      'tasm' with 'masm' in the above command line.
  17. ;
  18. ; AUTHOR
  19. ;      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
  20. ;      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
  21. ;
  22. ; COPYRIGHT
  23. ;      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
  24. ;      This program is free software; you can redistribute it and/or
  25. ;      modify it under the terms of the GNU General Public License
  26. ;      (version 1), as published by the Free Software Foundation, and
  27. ;      found in the file 'LICENSE' included with this distribution.
  28. ;      This program is distributed in the hope that it will be useful,
  29. ;      but WITHOUT ANY WARRANTY; without even the implied warrant of
  30. ;      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  31. ;      GNU General Public License for more details.
  32. ;      You should have received a copy of the GNU General Public License
  33. ;      along with this program;  if not, write to the Free Software
  34. ;      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  35. ;
  36. ifdef have286
  37.  .286    ; define have286 with -D for 80286 processor or better
  38.  
  39. else    ; 8088/8086 compatible
  40.     mpusha Macro
  41.         push ax
  42.     push cx
  43.     push dx
  44.     push bx
  45.     push sp
  46.     push bp
  47.     push si
  48.     push di
  49.     Endm
  50.  
  51.     mpopa Macro
  52.         pop di
  53.     pop si
  54.     pop bp
  55.     add sp,2
  56.     pop bx
  57.     pop dx
  58.     pop cx
  59.     pop ax
  60.     Endm
  61. endif
  62.  
  63. ifdef msmall
  64.             .model    small
  65. argbase        equ    4
  66. endif
  67. ifdef mcompact
  68.         .model  compact
  69. argbase        equ    4
  70. endif
  71. ifdef mmedium
  72.         .model    medium
  73. argbase        equ    6
  74. endif
  75. ifdef mlarge
  76.         .model    large
  77. argbase        equ    6
  78. endif
  79. a_swap        equ    <bp+argbase+0>
  80. a_prog        equ    <bp+argbase+2>
  81. a_tail        equ    <bp+argbase+6>
  82. a_env         equ    <bp+argbase+10>
  83. a_tmp        equ    <bp+argbase+12>
  84.  
  85. a_handle    equ    <bp+argbase>
  86.  
  87.  
  88. ; Define all useful equ's
  89. swap_xms    equ    0        ; we swapped it out to xms
  90. swap_ems    equ    2        ; we swapped it out to ems
  91. swap_file    equ    4        ; we swapped it out to a file
  92. seg_no_alloc    equ    0        ; this is part of a segment
  93. seg_alloc    equ    1        ; this is a full segment header
  94. seg_data    equ    2        ; this is data for part of a segment
  95.  
  96.  
  97. ; Define any global/external variables that we will be accessing from here.
  98.             .data
  99.         extrn    _errno:word        ; Set to dos ret code from exec
  100.         public  _Interrupted        ; Set to 1 if interrupted 0
  101. _Interrupted    dw    0            ; otherwise
  102.  
  103.             .code
  104.         assume    cs:@code, ds:@code, ss:@code, es:@code
  105.  
  106.         even
  107. execstack    dw    64  dup (?)    ; put the temporary exec stack right
  108. exec_sp        label    word        ; at the start.
  109.  
  110. old_ss        dw    ?        ; save stack seg across exec
  111. old_sp        dw    ?        ; save stack ptr across exec
  112. progsize    dw    ?        ; original size of the program
  113. rootsize    dw    ?        ; size of base root kept during swap
  114. resend        dw    ?        ; paragraph where resident code ends
  115. envseg        dw    ?        ; paragraph of environment segment
  116. psp        dw    ?        ; our own psp
  117. swap        dw    ?        ; swapping selection flag
  118. eretcode    dw    ?        ; return code from exec
  119. interrupted    dw    ?        ; interrupted flag for exec
  120. arenahead    dw    ?        ; start of memory block list
  121. alstr        dw    ?        ; allocation strategy save spot
  122. in_exec        dw    0        ; flag, 1 ==> in exec
  123.  
  124. cmdpath        db    65  dup(?)    ; file to exec
  125. cmdtail        db    129 dup(?)    ; its command tail
  126. fcb        db    37  dup(0)    ; dummy fcb
  127. tmpseg        db    7   dup(?)    ; block header buffer
  128.  
  129. tmpname        db    65  dup(0)    ; name of temporary file resource
  130.  
  131.         even
  132. tmphandle    dw    ?        ; handle for temporary file
  133. real_21h    dd    0        ; will be DOS's 21h vector if doing -C
  134.  
  135. std_fil_handle    dw    ?        ; file handle for -C file
  136. std_fil_number    db    ?        ; system file number for -C file
  137. our_stdout    db    ?        ; sys file number our stdout handle
  138.  
  139. error_rhdr    db    "exec: Failure reading header block", 0DH, 0AH, '$'
  140. error_rseg    db    "exec: Failure reading segment data", 0DH, 0AH, '$'
  141. error_resize    db    "exec: Failure on resize", 0DH, 0AH, '$'
  142. error_free    db    "exec: Failure to free a block", 0DH, 0AH, '$'
  143. error_string    db    "exec: Program swap failure", 0DH, 0AH, '$'
  144. error_alloc    db    "exec: Memory blocks don't match", 0DH, 0AH, '$'
  145.  
  146.         even
  147. write_header label word
  148.    whdr_xms_ptr        dw    word ptr whdr_xms
  149.    whdr_ems_ptr        dw    word ptr whdr_ems
  150.    whdr_file_ptr    dw    word ptr whdr_file
  151.  
  152. write_seg label word
  153.    wseg_xms_ptr        dw    word ptr wseg_xms
  154.    wseg_ems_ptr        dw    word ptr wseg_ems
  155.    wseg_file_ptr    dw    word ptr wseg_file
  156.  
  157. read_header label word
  158.    rhdr_xms_ptr        dw    word ptr rhdr_xms
  159.    rhdr_ems_ptr        dw    word ptr rhdr_ems
  160.    rhdr_file_ptr    dw    word ptr rhdr_file
  161.  
  162. read_seg label word
  163.    rseg_xms_ptr        dw    word ptr rseg_xms
  164.    rseg_ems_ptr        dw    word ptr rseg_ems
  165.    rseg_file_ptr    dw    word ptr rseg_file
  166.  
  167. free_resource label word
  168.    free_xms_ptr        dw    word ptr free_xms_resource
  169.    free_ems_ptr        dw    word ptr free_ems_resource
  170.    free_file_ptr    dw    word ptr free_file_resource
  171.  
  172. reset_resource label word
  173.    reset_xms_ptr    dw    word ptr reset_xms_resource
  174.    reset_ems_ptr    dw    word ptr reset_ems_resource
  175.    reset_file_ptr    dw    word ptr reset_file_resource
  176.  
  177. old_ctl_brk label dword
  178.    old_ctl_brk_off    dw    ?
  179.    old_ctl_brk_seg     dw    ?
  180.  
  181. old_crit_err label dword
  182.    old_crit_err_off    dw    ?
  183.    old_crit_err_seg     dw    ?
  184.  
  185. exec_block label word
  186.   ex_envseg    dw    ?            ; env seg, use parent's if 0
  187.   ex_cmdtail    dd    ?            ; command tail for exec
  188.   ex_fcb1    dd    far ptr fcb        ; fcb's aren't used by dmake
  189.   ex_fcb2    dd    far ptr fcb
  190.   ex_ss        dw    ?            ; saved ss for exec
  191.   ex_sp        dw    ?            ; saved sp for exec
  192.   ex_error    dw    0            ; error code for dos exec
  193.  
  194.  
  195. ; Special 21h (DOS call) handler to tee stdout/stderr writes to the -C file.
  196. ; Ignore 21h calls that aren't writes to 1 or 2; i.e., pass them to DOS handler.
  197. ; If write call was from this process, it's pretty simple to duplicate it
  198. ; to the -C file.  If it's from another process, we try to write to its
  199. ; inherited handle.  Worst case is where the handle wasn't inherited: someone
  200. ; closed it.  In that instance we have to switch to dmake's PSP to do the
  201. ; duplicate write.
  202.  
  203. ; Subprocesses do not get their stdout/stderr teed to the -C file if
  204. ; their stdout/stderr no longer points to the file/device that dmake's
  205. ; stdout points to.  This is tested by looking at the process's job
  206. ; file table, which is a table that maps process handles to DOS system file
  207. ; table numbers.  (The far pointer to the JFT is at the PSP offset 34h.)
  208. ; The JFT is also queried to see if the -C file was inherited.
  209.  
  210. ; O_BINARY, O_TEXT problems are ignored here.  These are fudged by the
  211. ; C library before it calls DOS; since we're working below that level
  212. ; we don't have to worry about it.
  213.  
  214. simulate_21h Macro
  215.     pushf            ;; direct call to DOS
  216.     call cs:[real_21h]
  217.     Endm
  218.  
  219.     assume cs:@code, ds:nothing, es:nothing, ss:nothing
  220. our_21h_handler proc far
  221.     pushf
  222.     cmp ah,40h        ; is this a write?
  223.     jne call_dos    ; --no
  224.     cmp bx,1        ; write on handle 1 (stdout?)
  225.     je duplicate_it
  226.     cmp bx,2        ; stderr?
  227.     je duplicate_it
  228.  
  229. call_dos:
  230.     popf
  231.     jmp [real_21h]    ; far jump to real handler, which will do the sys call
  232.                 ; and return to the original caller
  233.  
  234. duplicate_it:
  235.     mpusha
  236.     push ds
  237.     push es
  238.     mov bp,sp
  239.  
  240.     mov di,std_fil_handle    ; handle of the -C file
  241.  
  242.   If @codesize eq 0
  243.       ; Small/compact models allow for quick test of us versus subprocess.
  244.     ; False negative (it's us with a different CS) will be picked
  245.     ; up by code just below.  (Might happen due to call from C library.)
  246.     ; False positives would be bad, but can't happen.
  247.     mov ax,[bp+24]    ; caller's CS
  248.     cmp ax,@code    ; same as us?
  249.     je call_from_dmake
  250.   Endif
  251.  
  252.     mov ah,51h        ; get PSP ("undocumented version" works in DOS 2.0+)
  253.     simulate_21h    ; PSP segment returned in BX
  254.     cmp bx,psp        ; our PSP?
  255.     je call_from_dmake    ; --yes, no PSP changing needed
  256.  
  257.     mov es,bx        ; set ES to current (caller's) PSP
  258.     lds bx,es:[34h]    ; set DS:BX pointing to caller's job file table
  259.  
  260.     mov si,[bp+12]    ; file handle caller passed in (known to be 1 or 2)
  261.     mov al,[bx+si]    ; system file number corresponding to caller's handle
  262.     cmp al,our_stdout    ; same as our stdout?
  263.     jne do_real_write    ; no--subprocess must have redirected it
  264.  
  265.     mov al,[bx+di]    ; see if caller has dup of -C file still open
  266.     cmp al,std_fil_number
  267.     je use_dup        ; yes--we can write using caller's PSP
  268.  
  269.         ; Calling process (or some intermediate process) has closed
  270.     ; the -C descriptor.  We'll use dmake's (our) -C descriptor, but
  271.     ; to do so we'll have to change the PSP.  Disable BREAK handling
  272.     ; so that ^break doesn't kill the wrong process.
  273.  
  274.     mov ax,3300h    ; get BREAK flag
  275.     simulate_21h
  276.     mov si,dx        ; save BREAK state in SI
  277.     sub dx,dx        ; now turn break flag off
  278.     mov ax,3301h
  279.     simulate_21h    ; don't want ^Break recoginized while PSP changed
  280.     mov bx,psp        ; set dmake's PSP
  281.     mov ah,50h
  282.     simulate_21h
  283.  
  284.     mov bx,di            ; handle of -C file
  285.     ; CX still has caller's count
  286.     mov ds,[bp+2]        ; restore caller's DS
  287.     mov dx,[bp+14]        ; DS:DX again points to caller's buffer
  288.     mov ah,40h
  289.     simulate_21h        ; write the copy
  290.  
  291.     mov bx,es        ; caller's PSP
  292.     mov ah,50h        ; set PSP
  293.     simulate_21h    ; restore caller's PSP
  294.     mov dx,si        ; break state before we changed it
  295.     mov ax,3301h
  296.     simulate_21h    ; restore break state
  297.  
  298.     jmp short do_real_write
  299.  
  300. use_dup:
  301.     mov ds,[bp+2]        ; restore caller's DS
  302.     mov dx,[bp+14]        ; DS:DX again points to caller's buffer
  303.  
  304. call_from_dmake:
  305.     mov bx,di            ; handle of -C file
  306.     mov ah,40h            ; write
  307.     ; CX still has caller's count
  308.     simulate_21h        ; write to the file
  309.  
  310. do_real_write:
  311.     pop es
  312.     pop ds
  313.     mpopa
  314.     popf
  315.     jmp [real_21h]    ; far jump to real handler, which will do the sys call
  316.                 ; and return to the original caller
  317. our_21h_handler endp
  318.  
  319.     assume    cs:@code, ds:@code, ss:@code, es:@code
  320.  
  321. ;-----------------------------------------------------------------------------
  322. ; First define the critical-error and control-brk handlers. 
  323. ; The critical error handler simply pops the machine state and returns an
  324. ; access denied result code.
  325. crit_err_handler proc far
  326.         add    sp, 6        ; ip/cs/flags ...
  327.         pop    ax
  328.         pop    bx
  329.         pop    cx
  330.         pop    dx
  331.         pop    si
  332.         pop    di
  333.         pop    bp
  334.         pop    ds
  335.         pop    es
  336.         push    bp        ; fix up the return flags
  337.         mov    bp, sp
  338.         xchg    ax, [bp+6]    ; get the flag byte.
  339.         or    ax, 1        ; set the carry bit
  340.         xchg    ax, [bp+6]    ; put it back.
  341.         pop    bp
  342.         mov    ax, 5        ; access denied
  343.         iret
  344. crit_err_handler endp
  345.  
  346.  
  347. ;-----------------------------------------------------------------------------
  348. ; Here we set the interrupted flag, and terminate the currently running
  349. ; process.
  350. ctl_brk_handler proc far
  351.         clc                ; make sure carry is clear
  352.         inc    cs:interrupted        ; set the flag
  353.  
  354. ; Make certain it isn't us that is going to get terminated.
  355. ; There is a small window where the in_exec flag is set but the child is
  356. ; not running yet, I assume that DOS doesn't test for ctl_brk at that time
  357. ; as it is bussily creating a new process.
  358.         cmp    cs:in_exec,0
  359.         je    just_return        ; note this implies CF == 0
  360.         stc                ; set CF to abort child
  361. just_return:    iret
  362. ctl_brk_handler endp
  363.  
  364.  
  365. ;-----------------------------------------------------------------------------
  366. ; Something really nasty happened, so abort the exec call and exit.
  367. ; This kills the calling process altogether, and is a very nasty way of
  368. ; termination since files may still be open etc.
  369. abort_exec_rhdr label near
  370.         mov    dx, offset error_rhdr
  371.         jmp    print_it
  372. abort_exec_rseg label near
  373.         mov    dx, offset error_rseg
  374.         jmp    print_it
  375. abort_exec_resize label near
  376.         mov    dx, offset error_resize
  377.         jmp    print_it
  378. abort_exec_free label near
  379.         mov    dx, offset error_free
  380.         jmp    print_it
  381. abort_exec_alloc label near
  382.         mov    dx, offset error_alloc
  383.         jmp    print_it
  384. abort_exec proc near
  385.         mov    dx, offset error_string
  386. print_it:    push    dx
  387.         mov    bx, [swap]
  388.         call    [free_resource+bx]
  389.         mov    ax, cs
  390.         mov    ds, ax
  391.         pop    dx
  392.         mov    ah, 9
  393.         int    21H
  394. kill_program:    mov    ax, 04cffH            ; nuke it!
  395.         int    21H
  396. abort_exec endp
  397.  
  398.  
  399. ;-----------------------------------------------------------------------------
  400. ; lodsw/stosw loop to copy data.  Called only for word copy operations.
  401. ;     ds:si  - point at source
  402. ;    es:di  - point at destination
  403. ;    cx     - count of bytes to copy.
  404. copy_data proc near
  405.         shr    cx, 1        ; convert to word count
  406.         jnc    copy_words
  407.         movsb
  408. copy_words:    rep    movsw        ; copy the words.
  409.         ret
  410. copy_data endp
  411.  
  412.  
  413.  
  414. ;=============================================================================
  415. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ XMS RECORDS.
  416. ;=============================================================================
  417. rhdr_xms proc near
  418.         ret
  419. rhdr_xms endp
  420.  
  421. rseg_xms proc near
  422.         ret
  423. rseg_xms endp
  424.  
  425. reset_xms_resource proc near
  426.         ret
  427. reset_xms_resource endp
  428.  
  429. free_xms_resource proc near
  430.         ret
  431. free_xms_resource endp
  432. ;=============================================================================
  433.  
  434.  
  435.  
  436. ;=============================================================================
  437. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ EMS RECORDS.
  438. ;=============================================================================
  439. rhdr_ems proc near
  440.         ret
  441. rhdr_ems endp
  442.  
  443. rseg_ems proc near
  444.         ret
  445. rseg_ems endp
  446.  
  447. reset_ems_resource proc near
  448.         ret
  449. reset_ems_resource endp
  450.  
  451. free_ems_resource proc near
  452.         ret
  453. free_ems_resource endp
  454. ;=============================================================================
  455.  
  456.  
  457.  
  458. ;=============================================================================
  459. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ FILE RECORDS.
  460. ;=============================================================================
  461. ; This routine reads a segment header from a file.
  462. ; The header is a seven byte record formatted as follows:
  463. ;    segment address        - of data
  464. ;    offset address        - of data
  465. ;     length in paragraphs    - of data
  466. ;    mode            - 1 => segment header (allocate seg on read)
  467. ;                  0 => subsegment, don't allocate on read.
  468. ; The information is placed into the tmpseg data area in the code segment.
  469. ; The routine aborts if an error is detected.
  470. rhdr_file proc near
  471.         mov    dx, offset tmpseg    ; read the header record out
  472.         mov    cx, 7
  473.         mov    bx, [tmphandle]
  474.         mov    ah, 03fH
  475.         int    21H
  476.         jnc    rhdr_done        ; make sure it worked
  477.         jmp    abort_exec_rhdr
  478.  
  479. rhdr_done:    cmp    ax, 7
  480.         je    exit_rhdr_file
  481.         or    ax, ax
  482.         je    signal_eof
  483.         jmp    abort_exec_rhdr
  484.  
  485. signal_eof:    stc
  486. exit_rhdr_file:    ret
  487. rhdr_file endp
  488.  
  489.  
  490. ;-----------------------------------------------------------------------------
  491. ; Read a segment from the temporary file whose handle is in cs:tmphandle.
  492. ; The routine aborts if an error is detected.
  493. rseg_file proc near
  494.         push    ds
  495.         mov    ds, word ptr cs:tmpseg; Now read the whole segment
  496.         mov    dx, word ptr cs:tmpseg+2
  497.         mov    cx, word ptr cs:tmpseg+4
  498.         mov    bx, cs:tmphandle
  499.         mov    ah, 03fH
  500.         int    21H
  501.         pop    ds
  502.         jnc    rseg_done
  503.         jmp    abort_exec_rseg
  504.  
  505. rseg_done:    cmp    ax, [word ptr tmpseg+4]
  506.         je    exit_rseg_file
  507.         jmp    abort_exec_rseg        ; If we didn't get read full
  508. exit_rseg_file:    ret                ; segment then abort
  509. rseg_file endp
  510.  
  511.  
  512. ;-----------------------------------------------------------------------------
  513. ; Seek to the beginning of the file.
  514. reset_file_resource proc near
  515.         mov    bx, [tmphandle]
  516.         xor    cx, cx
  517.         mov    dx, cx
  518.         mov    ax, 04200H        ; seek to begining of file
  519.         int    21H
  520.         ret
  521. reset_file_resource endp
  522.  
  523.  
  524. ;-----------------------------------------------------------------------------
  525. ; unlink the temporary file allocated for swapping.
  526. ; We close the file first, and then delete it.   We ignore errors here since
  527. ; we can't do anything about them anyway.
  528. free_file_resource proc near
  529.         mov    bx, [tmphandle]        ; get the file handle
  530.         mov    ah, 03eH        ; close the file
  531.         int    21H
  532.         mov    dx, offset tmpname    ; Now delete the temp file
  533.         mov    ah, 041H
  534.         int    21H
  535.         ret
  536. free_file_resource endp
  537. ;=============================================================================
  538.  
  539.  
  540.  
  541. ;=============================================================================
  542. ; CODE TO SWAP THE IMAGE IN FROM SECONDARY STORAGE
  543. ;=============================================================================
  544. swap_in proc near
  545.         mov    bx, [alstr]        ; get previous alloc strategy
  546.         mov    ax, 5801H        ; and set it back
  547.         int    21H
  548.         mov    bx, [swap]        ; get type of resource
  549.         call    [reset_resource+bx]    ; reset the resource
  550.         mov    es, [psp]        ; resize the program back
  551.         mov    bx, [progsize]        ; to original size
  552.         mov    ah, 04AH
  553.         int    21H
  554.         jnc    read_seg_loop
  555.         jmp    abort_exec
  556.  
  557. read_seg_loop:    mov    bx, [swap]        ; get type of resource
  558.         call    [read_header+bx]    ; get seg header
  559.         jc    exit_swap_in        ; all done
  560.         mov    al, [tmpseg+6]
  561.         cmp    al, seg_no_alloc    ; see if dummy segment header
  562.         je    read_seg_loop
  563.         cmp    al, seg_alloc        ; do we need to do an alloc?
  564.         jne    read_data        ; nope
  565.  
  566. ; Allocate back the memory for a segment that is not the [psp], note that this
  567. ; must come back to the same segment we had previously since other segments
  568. ; may have pointers stored in their variables that point to this segment using
  569. ; segment:offset long pointers.
  570.         mov    bx, [word ptr tmpseg+4]    ; get count of paragraphs
  571.         mov    ah, 048H        ; dos_alloc
  572.         int    21H
  573.         jc    alloc_error        ; oops!
  574.         cmp    ax, [word ptr tmpseg]    ; did we get the same segment?
  575.         je    read_seg_loop        ; yup!
  576. alloc_error:    jmp    abort_exec_alloc
  577.  
  578. read_data:    mov    bx, [swap]
  579.         call    [read_seg+bx]        ; this must succeed, if fail
  580.         jmp    read_seg_loop        ; we never come back here
  581.  
  582. exit_swap_in:    mov    bx, [swap]        ; all done, so free resource
  583.         call    [free_resource+bx]
  584.         ret
  585. swap_in endp
  586.  
  587.  
  588. ;=============================================================================
  589. ; CODE TO SWAP THE IMAGE OUT TO SECONDARY STORAGE
  590. ;=============================================================================
  591. ; This routine is called to swap the non-resident portion of the program
  592. ; out to the resource specified by the value of [cs:swap].  If the swap out
  593. ; fails, then appropriate routines are called to free the resources allocated
  594. ; up to that point.
  595. ;
  596. ; The steps used to swap the program out are as follows:
  597. ;    - calculate new size of program to remain resident and size to swap
  598. ;      out.
  599. ;    - write out non-resident portion of current segment
  600. ;    - walk DOS allocation chain and write out all other segments owned by
  601. ;      the current program that are contiguous with the _psp segment
  602. ;    - copy the environment down to low memory
  603. ;    - resize the current _psp segment to savesize
  604. ;    - free all segments belonging to program except current _psp segment
  605. swap_out proc near
  606.         mov    ax, 05800H    ; get memory alocation strategy
  607.         int    021H
  608.         mov    [alstr], ax    ; and save it for future restoration.
  609.         mov    di, [psp]    ; compute length of program to current
  610.         mov    bx, cs        ; value of cs, and find program size
  611.         sub    bx, di        ; by looking at length stored in
  612.         mov    ax, di        ; arena header found in front of psp
  613.         dec    ax
  614.         mov    es, ax
  615.         mov    si, es:3    ; si is size of program in paragraphs
  616.         mov    [progsize], si    ; progsize now contains the size.
  617.  
  618. ; Now compute length of program segment to save.
  619. ; Length is:   cs - psp + (offset overlay_code_here+15 >> 4)
  620.         mov    ax, offset overlay_code_here+15
  621.         shr    ax, 1
  622.         shr    ax, 1
  623.         shr    ax, 1
  624.         shr    ax, 1
  625.         add    bx, ax            ; bx is size of program to keep
  626.         sub    si, bx            ; si is # of paragraphs to save.
  627.         add    di, bx            ; di is paragraph to start at
  628.         mov    rootsize, bx
  629.         mov    resend, di        ; cs:resend is saved start para
  630.         mov    al, seg_no_alloc    ; set no allocation for segment
  631.         call    write_segment
  632.         jc    abort_swap_out
  633.  
  634. ; We have now saved the portion of the program segment that will not remain
  635. ; resident during the exec.  We should now walk the DOS allocation chain and
  636. ; write out all other segments owned by the current process.
  637. save_segments:    mov    ax, [psp]
  638.         dec    ax
  639.         mov    es, ax
  640.         mov    bx, offset write_segment_data
  641.         call    walk_arena_chain
  642.         jc    abort_swap_out
  643.  
  644. ; Now we must walk the chain of allocated memory blocks again and free
  645. ; all those that are owned by the current process, except the one that is
  646. ; the current process' psp.
  647. free_segments:    mov    ax, [psp]
  648.         dec    ax
  649.         mov    es,ax
  650.         mov    bx, offset free_dos_segment
  651.         call    walk_arena_chain
  652.         jnc    resize_program
  653.         jmp    abort_exec_free        ; can't fix it up now.
  654.  
  655. ; We now resize the program to the size specified by cs:rootsize.  This will
  656. ; free most of the memory taken up by the current program segment.
  657. resize_program: mov    es, [psp]        ; es is segment to resize.
  658.         mov    bx, [rootsize]        ; bx is size of segment.
  659.         mov    ah, 04aH        ; resize memory block
  660.         int    21H
  661.         jnc    swap_out_ok
  662.         jmp    abort_exec_resize    ; disaster
  663. swap_out_ok:    ret
  664.  
  665. ; The swap out failed for some reason, so free any allocated resources
  666. ; and set the carry bit.
  667. abort_swap_out:    mov    bx, [swap]
  668.         call    [free_resource+bx]
  669.         xor    ax, ax
  670.         mov    [swap], ax        ; clear the swap flag
  671.         stc
  672.         ret
  673. swap_out endp
  674.  
  675.  
  676. ;=============================================================================
  677. ; CODE TO SET-UP FOR AND EXEC THE CHILD PROCESS
  678. ;=============================================================================
  679. ; Actually execute the program.  If cs:swap is set, this code will invoke the
  680. ; swap-out/swap-in code as required.
  681. do_exec proc near
  682.         cmp    [swap], 0        ; does the user want to swap?
  683.         je    no_swap_out        ; nope
  684.         call    init_swap        ; figger out where to swap to
  685.         jc    no_swap_out        ; if carry set then don't swap
  686.         call    swap_out
  687.  
  688. no_swap_out:    cmp    [interrupted], 0    ; were we interrupted?
  689.         jne    leave_exec        ; yep, so clean up, don't exec
  690.  
  691. ; free passed in environment block if it is non zero.
  692. ; This way the parent program does not need to free it.
  693.         mov    ax, [envseg]
  694.         or    ax, ax
  695.         je    setup_block
  696.         push    ax
  697.         mov    es, ax
  698.         mov    ah, 49H
  699.         int    21H
  700.         pop    ax
  701.  
  702. ; set up the parameter block for the DOS exec call.
  703. ;    offset  contents
  704. ;        00  segment address of environment to be passed,
  705. ;          0 => use parents env.
  706. ;        02  pointer to command tail for new process.
  707. ;        06  pointer to fcb1
  708. ;        0a  pointer to fcb2
  709. setup_block:    mov    ax, [envseg]
  710.         mov    [ex_envseg], ax
  711.         mov    cx, cs
  712.         mov    [word ptr ex_cmdtail], offset cmdtail
  713.         mov    [word ptr ex_cmdtail+2], cx
  714.  
  715. ; set up registers for exec call
  716. ;    ds:dx    - pointer to pathname of program to execute
  717. ;    es:bx    - pointer to above parameter block
  718.         mov    dx, offset cmdpath
  719.         mov    es, cx
  720.         mov    bx, offset exec_block
  721.  
  722. ; Under DOS 2.x exec is notorious for clobbering registers and guarantees
  723. ; to preserve only cs:ip.
  724.         push    ds
  725.         mov    [ex_sp], sp
  726.         mov    [ex_ss], ss
  727.         mov    [ex_error], 0        ; clear exec error code
  728.         inc    [in_exec]        ; set internal flag
  729.         mov    ax, 04b00H
  730.         int    21H
  731.  
  732. ; returned from exec, so restore possibly clobbered registers.
  733.         mov    ss, cs:ex_ss
  734.         mov    sp, cs:ex_sp
  735.         pop    ds
  736.  
  737. ; check to make certain the exec call worked.
  738.         jnc    it_worked
  739.  
  740. ; exec call failed.  Save return code from msdos.
  741.         mov    [ex_error], ax
  742.         jmp    leave_exec
  743.  
  744. it_worked:    mov    ah, 04dH    ; get the return code
  745.         int    21H
  746.         cbw
  747.         mov    [eretcode], ax
  748.  
  749. leave_exec:    cmp    [swap], 0    ; check swap, if non-zero swap back in
  750.         je    no_swap_in
  751.         call    swap_in
  752.  
  753. ; Clear the in_exec after the swap back in.  This way we are guaranteed to
  754. ; get parent in and the resources freed should a ^C be hit when we are reading
  755. ; the image in.
  756. no_swap_in:    mov    [in_exec], 0
  757.         ret
  758. do_exec endp                
  759.  
  760.  
  761.  
  762. ;==============================================================================
  763. ; Everything past this point is overwriten with the environment and new
  764. ; program after the currently executing program is swapped out.
  765. ;==============================================================================
  766. overlay_code_here label word
  767.  
  768. ;-----------------------------------------------------------------------------
  769. ; Figure out where we can swap to and initialize the resource we are going to
  770. ; use.  We try XMS, EMS, and a tempfile (if specified), in that order.  We set
  771. ; [cs:swap] to the correct value based on which of the resources exists.
  772. ; If none can be used, then [cs:swap] is set to 0, and no swap takes place.
  773. ; The exec code will still attempt to execute the child in this instance, but
  774. ; may fail due to lack of resources.   Each swap_out_* routine must provide
  775. ; its own clean-up handler should it not be able to write all program
  776. ; segments to the swap resource.
  777. init_swap proc near
  778.         mov    [swap], 0
  779. ;call    init_xms
  780. ;jnc    init_done
  781. ;call    init_ems
  782. ;jnc    init_done
  783.         call    init_file
  784. init_done:    ret
  785. init_swap endp
  786.  
  787.  
  788. ;-----------------------------------------------------------------------------
  789. ; This routine is used to walk the DOS allocated memory block chain
  790. ; starting at address supplied in the es register.  For each block it
  791. ; calls the routine specified by the bx register with the segment length
  792. ; in si, and its address in di.  It does not apply the routine to the
  793. ; segment if the segment is the same as the current program's [cs:psp] value.
  794. memheader struc
  795.    magic    db    ?    ; either 'Z' for end or 'M' for allocated
  796.    owner    dw    ?    ; psp of owner block
  797.    len        dw    ?    ; length in paragraphs of segment
  798. memheader ends
  799.  
  800. walk_arena_chain proc near
  801.         mov    si, word ptr es:3        ; get length
  802.         mov    di, es
  803.         inc    di
  804.         mov    ax, word ptr es:1
  805.  
  806. ; Stop the search if the block is NOT owned by us.  Ignore our own psp block
  807. ; and our environment segment block.
  808.         cmp    ax, cs:psp            ; is it owned by us?
  809.         jne    walk_done            ; NOPE!  -- all done
  810.         cmp    di, cs:envseg            ; skip our environment
  811.         je    next_block
  812.         cmp    di, cs:psp            ; skip our psp
  813.         je    next_block
  814.  
  815. ; Now save state and call the routine pointed at by [bx].
  816.         push    di
  817.         push    si
  818.         push    bx
  819.         call    bx
  820.         pop    bx
  821.         pop    si
  822.         pop    di
  823.         jc    exit_walk            ; if error then stop
  824.         mov    al, byte ptr es:0        ; check if at end 
  825.         cmp    al, 'Z'
  826.         je    walk_done
  827.  
  828. next_block:    add    di, si                ; go on to next segment
  829.         mov    es, di
  830.         jmp    walk_arena_chain
  831. walk_done:    clc
  832. exit_walk:    ret
  833. walk_arena_chain endp
  834.  
  835.  
  836. ;-----------------------------------------------------------------------------
  837. ; This routine takes a dos segment found in the di register and free's it.
  838. free_dos_segment proc near
  839.         mov    es, di        ; free dos memory block
  840.         mov    ah, 49H
  841.         int    21H
  842.         ret
  843. free_dos_segment endp
  844.  
  845.  
  846. ;-----------------------------------------------------------------------------
  847. ; Called to invoke write_segment with proper values in the al register.  Only
  848. ; ever called from walk_arena_chain, and so al should be set to seg_alloc.
  849. write_segment_data label near
  850.         mov    al, seg_alloc    ; and fall through into write_segment
  851. ;-----------------------------------------------------------------------------
  852. ; This routine writes a segment as a block of data segments if the number of
  853. ; paragraphs to write exceeds 0x0fff (rarely the case).
  854. ; It stuffs the info into tmpseg, and then calls wheader and wseg to get the
  855. ; data out.
  856. ;
  857. ;    di:dx    segment:offset of segment;  offset is ALWAYS zero.
  858. ;    si    number of paragraphs to write.
  859. ;    al    mode of header to write
  860. write_segment proc near
  861.         push    di
  862.         push    si
  863.         xor    dx,dx
  864.         mov    bx, [swap]
  865.         call    [write_header+bx]
  866.         pop    si
  867.         pop    di
  868.         jc    exit_wseg
  869.  
  870. do_io_loop:    cmp    si, 0        ; are we done yet?
  871.         je    exit_wseg    ; yup so leave.
  872.         mov    cx, si        ; # of paragraphs to move
  873.         cmp    cx, 0fffH    ; see if we have lots to move?
  874.         jle    do_io
  875.         mov    cx, 0fffH    ; reset to max I/O size
  876.  
  877. do_io:        push    cx        ; save # of paragraphs we are writing
  878.         shl    cx, 1        ; shift cx by four to the left
  879.         shl    cx, 1
  880.         shl    cx, 1
  881.         shl    cx, 1
  882.         push    di        ; save the start, and count left
  883.         push    si
  884.         mov    si, cx
  885.         xor    dx,dx
  886.         mov    al, seg_data
  887.         mov    bx, [swap]
  888.         push    bx
  889.         call    [write_header+bx]
  890.         pop    bx
  891.         call    [write_seg+bx]
  892.         pop    si
  893.         pop    di
  894.         pop    dx        ; original paragraph count in dx
  895.         jc    exit_wseg    ; it failed so exit.
  896.         add    di, dx        ; adjust the pointers, and continue.
  897.         sub    si, dx
  898.         jmp     do_io_loop
  899. exit_wseg:    ret
  900. write_segment endp
  901.  
  902.  
  903. ;=============================================================================
  904. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE XMS RECORDS.
  905. ;=============================================================================
  906. init_xms proc near
  907.         ret
  908. init_xms endp
  909.  
  910. whdr_xms proc near
  911.         ret
  912. whdr_xms endp
  913.  
  914. wseg_xms proc near
  915.         ret
  916. wseg_xms endp
  917. ;=============================================================================
  918.  
  919.  
  920. ;=============================================================================
  921. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE EMS RECORDS.
  922. ;=============================================================================
  923. init_ems proc near
  924.         ret
  925. init_ems endp
  926.  
  927. whdr_ems proc near
  928.         ret
  929. whdr_ems endp
  930.  
  931. wseg_ems proc near
  932.         ret
  933. wseg_ems endp
  934. ;=============================================================================
  935.  
  936.  
  937. ;=============================================================================
  938. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE FILES.
  939. ;=============================================================================
  940. ;-----------------------------------------------------------------------------
  941. ; Attempt to create a temporary file.  If the tempfile name is NIL then return
  942. ; with the cary flag set.
  943. init_file proc near
  944.         mov    al, [tmpname]
  945.         or    al, al
  946.         je    err_init_file
  947.         mov    dx, offset tmpname
  948.         xor     cx, cx
  949.         mov    ah, 03cH
  950.         int    21H
  951.         jc    err_init_file        ; if carry set then failure
  952.         mov    [tmphandle], ax        ; init swapping
  953.         mov    [swap], swap_file
  954.         jmp    exit_init_file
  955. err_init_file:    stc
  956. exit_init_file: ret
  957. init_file endp
  958.  
  959.  
  960. ;-----------------------------------------------------------------------------
  961. ; This routine writes a segment header to a file.
  962. ; The header is a seven byte record formatted as follows:
  963. ;    segment address        - of data
  964. ;    offset address        - of data
  965. ;     length in paragraphs    - of data
  966. ;    mode            - 1 => segment header (allocate seg on read)
  967. ;                  0 => subsegment, don't allocate on read.
  968. ; Routine takes three arguments:
  969. ;    di:dx    segment:offset of segment
  970. ;    si    number of paragraphs to write.
  971. ;    al    mode of header to write
  972. whdr_file proc near
  973.         mov    [word ptr tmpseg], di    ; save the segment/offset
  974.         mov    [word ptr tmpseg+2], dx
  975.         mov    [word ptr tmpseg+4], si    ; save the segment length
  976.         mov    [tmpseg+6], al
  977.         mov    dx, offset tmpseg    ; write the header record out
  978.         mov    cx, 7
  979.         mov    bx, [tmphandle]
  980.         mov    ah, 040H
  981.         int    21H
  982.         jc    exit_whdr_file        ; make sure it worked
  983.         cmp    ax, 7
  984.         je    exit_whdr_file        ; oh oh, disk is full!
  985. err_whdr_file:    stc
  986. exit_whdr_file:    ret
  987. whdr_file endp
  988.  
  989.  
  990. ;-----------------------------------------------------------------------------
  991. ; Write a segment to the temporary file whose handle is in cs:tmphandle
  992. ; Parameters for the write are assumed to be stored in the tmpseg data area.
  993. ; function returns carry set if failed, carry clear otherwise.
  994. wseg_file proc near
  995.         push    ds
  996.         mov    ds, word ptr cs:tmpseg ; Now write the whole segment
  997.         mov    dx, word ptr cs:tmpseg+2
  998.         mov    cx, word ptr cs:tmpseg+4
  999.         mov    bx, cs:tmphandle
  1000.         mov    ah, 040H
  1001.         int    21H
  1002.         pop    ds
  1003.         jc    exit_wseg_file        ; make sure it worked
  1004.         cmp    ax, [word ptr tmpseg+4]
  1005.         je    exit_wseg_file
  1006. err_wseg_file:    stc                ; it failed (usually disk full)
  1007. exit_wseg_file:    ret
  1008. wseg_file endp
  1009. ;=============================================================================
  1010.  
  1011.  
  1012. ;=============================================================================
  1013. ; _exec: THIS IS THE MAIN ENTRY ROUTINE TO THIS MODULE
  1014. ;=============================================================================
  1015. ; This is the main entry routine into the swap code and corresponds to the
  1016. ; following C function call:
  1017. ;
  1018. ; exec( int swap, char far *program, char far *cmdtail, int environment_seg,
  1019. ;    char far *tmpfilename );
  1020. ;
  1021. ; Exec performs the following:
  1022. ;    1. set up the local code segment copies of arguments to the exec call.
  1023. ;    2. switch to a local stack frame so that we don't clobber the user
  1024. ;       stack.
  1025. ;    3. save old interrupt vectors for ctrl-brk.
  1026. ;    4. install our own handler for the ctrl-brk interrupt, our handler
  1027. ;       terminates the current running process, and returns with non-zero
  1028. ;       status code.
  1029. ;    5. get our psp
  1030. ;    6. setup arguments for exec call
  1031. ;    7. exec the program, save result code on return.
  1032. ;       8. restore previous ctrl-brk and crit-error handler.
  1033. ;       9. restore previous process stack, and segment registers.
  1034. ;      10. return from exec with child result code in AX
  1035. ;       and global _Interrupted flag set to true if child execution was
  1036. ;       interrupted.
  1037.  
  1038. ; NOTE:  When first called the segments here assume the standard segment
  1039. ;        settings.
  1040.         assume cs:@code, ds:DGROUP,es:DGROUP,ss:DGROUP
  1041.  
  1042.         public    _exec
  1043. _exec proc
  1044.             push    bp        ; set up the stack frame
  1045.         mov    bp, sp
  1046.         push    si        ; save registers we shouldn't step on.
  1047.         push    di
  1048.         push    ds
  1049.  
  1050. ; set up for copying of parameters passed in with long pointers.
  1051.         push    cs        ; going to use lodsb/stosb, set up es
  1052.         pop    es        ; as destination.
  1053.         assume  es:@code    ; let the assembler know :-)
  1054.         cld            ; make sure direction is right
  1055.  
  1056. ; Copy all parameters into the bottom of the code segment.  After doing so we
  1057. ; will immediately switch stacks, so that the user stack is preserved intact.
  1058.         mov    ax, ss:[a_swap]        ; save swap
  1059.         mov    es:swap, ax
  1060.         mov    ax, ss:[a_env]        ; save env seg to use
  1061.         mov    es:envseg, ax
  1062.  
  1063.         mov     di, offset cs:cmdpath    ; copy the command
  1064.         lds     si, ss:[a_prog]        ; 65 bytes worth
  1065.         mov    cx, 65
  1066.         call    copy_data
  1067.  
  1068.         mov    di, offset cs:cmdtail    ; copy the command tail
  1069.         lds    si, ss:[a_tail]        ; 129 bytes worth
  1070.         mov    cx, 129
  1071.         call    copy_data
  1072.  
  1073.         mov    di, offset cs:tmpname    ; copy the temp file name
  1074.         lds    si, ss:[a_tmp]        ; 65 bytes worth.
  1075.         mov    cx, 65
  1076.         call    copy_data
  1077.  
  1078. ; Now we save the current ss:sp stack pointer and swap stack to our temporary
  1079. ; stack located in the current code segment.  At the same time we reset the
  1080. ; segment pointers to point into the code segment only.
  1081. swap_stacks:    mov    ax, ss
  1082.         mov    es:old_ss, ax
  1083.         mov    es:old_sp, sp
  1084.         mov    ax, cs
  1085.         mov    ds, ax
  1086.         mov    ss, ax            ; set ss first, ints are then
  1087.         mov    sp, offset cs:exec_sp    ; disabled for this instr too
  1088.         assume  ds:@code, ss:@code    ; let the assembler know :-)
  1089.  
  1090. ; Now we save the old control break and critical error handler addresses.
  1091. ; We replace them by our own routines found in the resident portion of the
  1092. ; swapping exec code.
  1093. set_handlers:    mov    [interrupted], 0    ; clear interrupted flag
  1094.         mov    [eretcode], 0        ; clear the return code
  1095.         mov    ax, 03523H        ; get int 23 handler address
  1096.         int    21H
  1097.         mov    cs:old_ctl_brk_off, bx
  1098.         mov    cs:old_ctl_brk_seg, es
  1099.         mov    dx, offset ctl_brk_handler
  1100.         mov    ax, 02523H        ; set int 23 handler address
  1101.         int    21H
  1102.  
  1103.         mov    ax, 03524H        ; get int 24 handler address
  1104.         int    21H
  1105.         mov    cs:old_crit_err_off, bx
  1106.         mov    cs:old_crit_err_seg, es
  1107.         mov    dx, offset crit_err_handler
  1108.         mov    ax, 02524H        ; set int 24 handler address
  1109.         int    21H
  1110.  
  1111. ; Go and execute the child, we've set up all of its parameters.  The do_exec
  1112. ; routine will attempt to perform a swap of the code if requested to do so by
  1113. ; a non-zero value in the variable cs:swap.
  1114.         mov    ah, 051H        ; get the psp
  1115.         int    21H
  1116.         mov    cs:psp, bx
  1117.         call    do_exec
  1118.  
  1119. ; We're back from the exec, so fix things up the way they were.
  1120. ; Restore the old control-break and critical-error handlers.
  1121.         lds    dx, cs:old_ctl_brk
  1122.         mov    ax, 02523H
  1123.         int    21H
  1124.         lds    dx, cs:old_crit_err
  1125.         mov    ax, 02524H
  1126.         int    21H
  1127.  
  1128. ; Restore previous program stack segment registers, and data segment.
  1129.         mov    ax, cs:old_ss
  1130.         mov    ss, ax            ; mov into ss first, that way
  1131.         mov    sp, cs:old_sp        ; no interrupts in this instr.
  1132.         pop    ds
  1133.  
  1134. ; Tell the assembler we have swaped segments again.
  1135.         assume    ds:DGROUP,es:DGROUP,ss:DGROUP
  1136.  
  1137. ; Set the global Interrupted flag so that parent can tell it was interrupted.
  1138.         mov    ax, seg DGROUP:_Interrupted
  1139.         mov    es, ax
  1140.         mov    ax, cs:interrupted
  1141.         mov    es:_Interrupted, ax
  1142.  
  1143. ; Set the global errno value to reflect the success/failure of the DOS
  1144. ; exec call.
  1145.         mov    ax, seg DGROUP:_errno
  1146.         mov    es, ax
  1147.         mov    ax, cs:ex_error
  1148.         mov    es:_errno, ax
  1149.  
  1150. ; Fetch the child's return code, pop rest of stuff off of the stack
  1151. ; and return to the caller.
  1152.         mov    ax, cs:eretcode
  1153.         pop    di
  1154.         pop    si
  1155.         pop    bp
  1156.         ret
  1157. _exec endp
  1158.  
  1159. ; void do_hook_std_writes(int handle);
  1160. ;    This saves the 21h interrupt vector and changes it to point
  1161. ;    into this code.  Argument is the file handle of the -C file.
  1162.  
  1163.     public _do_hook_std_writes
  1164. _do_hook_std_writes proc
  1165.             push    bp
  1166.         mov    bp,sp
  1167.         push    di
  1168.  
  1169.         mov    di, ss:[a_handle]    ; handle of -C file
  1170.         mov    std_fil_handle, di
  1171.  
  1172.         mov    ah, 51h            ; request our PSP
  1173.         int    21h
  1174.         mov    [psp], bx        ; save it
  1175.  
  1176.         mov    es, bx
  1177.         les    bx, es:[34h]        ; pointer to job file table
  1178.         mov    al, es:[bx+1]        ; system file # of our stdout
  1179.         mov    [our_stdout], al
  1180.         mov    al, es:[bx+di]        ; system file number of -C file
  1181.         mov    std_fil_number, al
  1182.  
  1183.         mov    ax,3521h        ; request vector 21h
  1184.         int    21h            ; it's returned in ES:BX
  1185.         mov    word ptr [real_21h], bx
  1186.         mov    word ptr [real_21h+2], es
  1187.  
  1188.         push    ds
  1189.         mov    ax,cs
  1190.         mov    ds,ax
  1191.         lea    dx,our_21h_handler    ; DS:DX is the new vector
  1192.         mov    ax,2521h        ; set vector 21h
  1193.         int    21h
  1194.  
  1195.         pop    ds
  1196.         pop    di
  1197.         pop    bp
  1198.         ret
  1199. _do_hook_std_writes endp
  1200.  
  1201. ; void do_unhook_std_writes(void);
  1202. ;    This restores the 21h interrupt vector.
  1203. ;    The saved vector is zero if it wasn't changed (no -C option).
  1204.  
  1205.     public _do_unhook_std_writes
  1206. _do_unhook_std_writes proc
  1207.         push    ds
  1208.  
  1209.             lds    dx, [real_21h]    ; put saved vector into DS:DX
  1210.         mov    ax, ds
  1211.         or    ax, dx
  1212.         jz    unhook_return    ; zero means we didn't hook 21h
  1213.  
  1214.         mov    ax,2521h    ; set vector 21h
  1215.         simulate_21h
  1216.  
  1217. unhook_return:    pop ds
  1218.         ret
  1219. _do_unhook_std_writes endp
  1220. end
  1221.