home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / msysjour / vol05 / 05 / swap / spawn.asm < prev   
Assembly Source File  |  1990-09-01  |  21KB  |  1,072 lines

  1. ;
  2. ;    PASCAL:
  3. ;        function do_spawn (method: byte; 
  4. ;                   swapfname, execfname, cmdtail: string; 
  5. ;                   envlen: word; var envp)
  6. ;    C:
  7. ;        int do_spawn (unsigned char method, 
  8. ;                  char *swapfname, char *execfname, char *cmdtail,
  9. ;                      unsigned envlen, char *envp)
  10. ;
  11. ;    Assemble with
  12. ;
  13. ;    tasm  /DPASCAL spawn          - Turbo Pascal (Tasm only), near
  14. ;    tasm  /DPASCAL /DFARCALL spawn    - Turbo Pascal (Tasm only), far
  15. ;    ?asm  spawn;              - C, default model (small)
  16. ;    ?asm  /DMODL=large spawn      - C, large model
  17. ;    
  18. ;    NOTE:    For C, change the 'model' directive below according to your
  19. ;        memory model, or define MODL=xxx on the command line.
  20. ;
  21. ;    The 'method' parameter determines the swap/spawn/exec
  22. ;    function:
  23. ;        00 = Spawn, don't swap
  24. ;        01 = Spawn, swap
  25. ;        80 = Exec, don't swap
  26. ;
  27. ;    If swapping (01), the following flags can be ORed into 'method':
  28. ;
  29. ;        02 = Use EMS if possible
  30. ;        04 = Use 'create temporary file' call (swapfname is path only)
  31. ;
  32. ;
  33. ;    For 'cmdtail' and 'execfname', the first byte must contain 
  34. ;    the length of the string, even when calling from C.
  35. ;
  36. ;    'swapfname' and 'execfname' must be zero terminated, even when
  37. ;    calling from Pascal.
  38. ;
  39.     IFDEF    PASCAL
  40.     .model    tpascal
  41. ;
  42. ptrsize    =    1
  43.     ELSE
  44.     IFNDEF    MODL
  45.     .model    small,c
  46.     ELSE
  47. %    .model    MODL,c
  48.     ENDIF
  49. ;
  50. ptrsize    =    @datasize
  51.     ENDIF
  52. ;
  53. stacklen    =    160        ; 80 word local stack
  54. ;
  55. blocksize    =    16 * 1024    ; Write block size
  56. blocksize_paras    =    blocksize / 16
  57. blockshift    =    10        ; shift factor for paragraphs
  58. blockmask    =    blocksize_paras - 1    ; Write block mask
  59. ;
  60. ; Method flags
  61. ;
  62. SWAPPING    =    01h
  63. USE_EMS        =    02h
  64. CREAT_TEMP    =    04h
  65. TERMINATE    =    80h
  66. ;
  67. EMM_INT        =    67h
  68. ;
  69. exec_block    struc
  70. envseg    dw    ?
  71. ppar    dw    ?
  72. pparseg    dw    ?
  73. fcb1    dw    ?
  74. fcb1seg    dw    ?
  75. fcb2    dw    ?
  76. fcb2seg    dw    ?
  77. exec_block    ends
  78. ;
  79. mcb        struc
  80. id        db    ?
  81. owner        dw    ?
  82. paras        dw    ?
  83.         db    3 dup(?)
  84. mcb_reserved    db    ?
  85. mcb        ends
  86. ;
  87. next_mcbs    struc
  88. next_addr    dw    ?
  89. next_size    dw    ?
  90. next_blocks    dw    ?
  91. next_rest    dw    ?
  92. next_mcbs    ends
  93. ;
  94. ;----------------------------------------------------------------------
  95. ;
  96. ;    The memory structure for the shrunk-down code
  97. ;
  98. parseg    struc
  99. parbeg    db    5ch dup(?)        ; start after PSP
  100. ;
  101. page_frame    dw    ?        ; EMS page frame (0 if no EMS)
  102. memsize        dw    ?        ; total paragraphs
  103. rd_blocks    dw    ?        ; number of blocks in swapfile
  104. rd_rest        dw    ?        ; number of bytes in last block
  105. save_ss        dw    ?        ; saved global ss
  106. save_sp        dw    ?        ; saved global sp
  107. spx        dw    ?        ; saved local sp
  108. next_mcb    db    TYPE next_mcbs dup(?)
  109. expar        db    TYPE exec_block dup (?) ; exec-parameter-block
  110. zero        dw    ?        ; Zero command tail length (dummy)
  111. method        db    ?        ; method parameter
  112. handle        dw    ?        ; swap file or EMS handle (0 for EXEC)
  113. ;
  114. parseg    ends
  115. ;
  116.     .data
  117.     IFDEF    PASCAL
  118.     extrn    prefixseg: word
  119.     ELSE
  120.     extrn    _psp: word
  121.     ENDIF
  122. ;
  123. ;
  124.     .code
  125. ;
  126. ;       This part of the program code will be moved to address
  127. ;    'codeloc' and executed there.
  128. ;
  129. ;    Registers on entry:
  130. ;        BX    = paragraphs to keep
  131. ;        CX     = length of environment to copy or zero
  132. ;        DS:SI    = environment source
  133. ;        ES:DI    = environment destination
  134. ;        (ES = our low core code segment)
  135. ;
  136. doexec:
  137.     jcxz    noenvcpy
  138.     rep movsb
  139. noenvcpy:
  140.     push    es            ; DS = ES = low core
  141.     pop    ds
  142.     cmp    ds:handle,0
  143.     je    no_shrink
  144.         mov    ah,04ah
  145.     int     21h                     ; free memory
  146.     mov    bx,ds:next_mcb.next_addr
  147. ;
  148. free_loop:
  149.     or    bx,bx
  150.     jz    no_shrink
  151.     mov    es,bx
  152.     mov    bx,es:mcb_reserved.next_addr
  153.     mov    ax,es
  154.     inc    ax
  155.     mov    es,ax
  156.     mov    ah,049h
  157.     int    21h
  158.     jmp    free_loop
  159. ;
  160. no_shrink:
  161.     push    ds
  162.     pop    es
  163.         mov     dx,offset filename      ; params for exec
  164.         mov     bx,offset expar
  165.         mov     ax,04b00h
  166.         int     21h                     ; exec
  167. ;
  168.     mov    bx,cs
  169.     mov    ds,bx
  170.     mov    es,bx
  171.     mov    ss,bx
  172.     mov    sp,ds:spx
  173.     cld
  174.     push    ax
  175.     pushf
  176. ;
  177.     test    ds:method,TERMINATE    ; exec?
  178.     jz    exec_ckswap
  179.     jmp    exec_term        ; terminate if yes
  180. ;
  181. exec_ckswap:
  182.     test    ds:method,SWAPPING    ; swap?
  183.     jnz    exec_noterm        ; go swap back in if yes
  184.     jmp    exec_retn        ; return if spawn and no swap
  185. ;
  186. exec_noterm:
  187.     mov    ah,4ah            ; expand memory
  188.     mov    bx,ds:memsize
  189.     int    21h
  190.     jnc    exec_memok
  191.     jmp    exec_term        ; terminate if error
  192. ;
  193. ;    Swap memory back
  194. ;
  195. exec_memok:
  196.     cmp    ds:page_frame,0
  197.     jne    exec_swems
  198.     jmp    exec_swfile        ; go swap from file if not EMS
  199. ;
  200. ;    Swap in memory from EMS
  201. ;
  202. exec_swems:
  203.     mov    ax,cs
  204.     dec    ax
  205.     push    ax            ; push current MCB
  206.     push    ds            ; push data segment
  207.     mov    ax,offset next_mcb
  208.     push    ax            ; and next MCB pointer
  209.     mov    dx,ds:handle
  210.     mov    cx,ds:rd_blocks
  211.     push    ds:rd_rest        ; push bytes in last block
  212.     mov    ds,ds:page_frame
  213.     xor    bx,bx
  214.     mov    di,swapbeg
  215. ;
  216. swap_in_ems:
  217. ;
  218.     jcxz    swin_ems_rest
  219. ;
  220. swin_ems:
  221.     push    cx
  222.     mov    ax,4400h
  223.     int    EMM_INT
  224.     or    ah,ah
  225.     jnz    exec_term
  226.     mov    cx,blocksize
  227.     mov    si,0
  228.     push    di
  229.     rep movsb
  230.     pop    di
  231.     mov    ax,es
  232.     add    ax,blocksize_paras
  233.     mov    es,ax
  234.     pop    cx
  235.     inc    bx
  236.     loop    swin_ems
  237. ;
  238. swin_ems_rest:
  239.     pop    cx
  240.     jcxz    swin_ems_rdy
  241.     mov    ax,4400h
  242.     int    EMM_INT
  243.     or    ah,ah
  244.     jnz    exec_term
  245.     mov    si,0
  246.     rep movsb
  247.     inc    bx
  248. ;
  249. swin_ems_rdy:
  250.     pop    si            ; next offset
  251.     pop    ds            ; segment
  252.     pop    es            ; current MCB
  253.     mov    ax,[si].next_addr
  254.     or    ax,ax
  255.     jz    swin_ems_complete
  256.     push    ax            ; next MCB
  257.     push    ax            ; also is data segment for next struc
  258.     mov    cx,offset mcb_reserved
  259.     push    cx            ; next offset
  260.     mov    ax,[si].next_rest
  261.     push    ax            ; bytes in last block
  262. ;
  263.     push    bx
  264.     push    dx
  265.     call    get_mcb            ; alloc MCB, ES = dest segment
  266.     pop    dx
  267.     pop    bx
  268.     mov    cx,[si].next_blocks    ; number of blocks
  269.     mov    ds,cs:page_frame    ; reload page frame addr
  270.     mov    di,0
  271.     jmp    swap_in_ems
  272. ;
  273. swin_ems_complete:
  274.     mov    ah,45h
  275.     int    EMM_INT
  276.     mov    ax,cs
  277.     mov    es,ax
  278.     mov    ds,ax
  279.     jmp    short exec_retn
  280. ;
  281. exec_term:
  282.     cmp    cs:page_frame,0
  283.     je    eterm_noems
  284.     mov    dx,cs:handle
  285.     mov    ah,45h
  286.     int    EMM_INT
  287. eterm_noems:
  288.     mov    ax,4c00h                ; terminate process
  289.         int     21h
  290. ;
  291. ;    Swap in memory from file
  292. ;
  293. exec_swfile:
  294.     mov    ax,cs
  295.     dec    ax
  296.     push    ax            ; push current MCB
  297.     push    ds            ; push data segment
  298.     mov    ax,offset next_mcb
  299.     push    ax            ; and next MCB pointer
  300.     push    ds:rd_rest        ; push bytes in last block
  301.     mov    bx,ds:handle
  302.     mov    cx,blocksize
  303.     mov    dx,swapbeg
  304.     mov    si,ds:rd_blocks
  305. ;
  306. exec_rdblocks:
  307.     mov    ah,3fh            ; read file
  308.     cmp    si,0
  309.     je    exec_rdr
  310.     dec    si
  311.     int    21h
  312.     jc    exec_term        ; terminate if error reading
  313.     mov    ax,ds
  314.     add    ax,blocksize_paras
  315.     mov    ds,ax
  316.     jmp    exec_rdblocks
  317. ;
  318. exec_rdr:
  319.     pop    cx            ; bytes in last block
  320.     jcxz    exec_norest
  321.     int    21h
  322.     jc    exec_term        ; terminate if error reading
  323. ;
  324. exec_norest:
  325.     pop    si            ; next offset
  326.     pop    ds            ; segment
  327.     pop    es            ; current MCB
  328.     mov    ax,[si].next_addr
  329.     or    ax,ax
  330.     jz    swin_file_complete
  331.     push    ax            ; next MCB
  332.     push    ax            ; also is data segment for next struc
  333.     mov    cx,offset mcb_reserved
  334.     push    cx            ; next offset
  335.     mov    ax,[si].next_rest
  336.     push    ax            ; bytes in last block
  337. ;
  338.     push    bx
  339.     call    get_mcb            ; alloc MCB, ES = dest segment
  340.     pop    bx
  341.     mov    si,[si].next_blocks    ; number of blocks
  342.     mov    ax,es
  343.     mov    ds,ax
  344.     mov    cx,blocksize
  345.     mov    dx,0
  346.     jmp    exec_rdblocks
  347. ;
  348. swin_file_complete:
  349.     mov    ah,3eh
  350.     int    21h            ; close file
  351.     mov    ax,cs
  352.     mov    ds,ax
  353.     mov    es,ax
  354. ;
  355. exec_retn:
  356.     popf
  357.     pop    ax
  358.     jc    exec_fault        ; return EXEC error code if fault
  359.     mov    ah,4dh            ; else get program return code
  360.     int    21h
  361. ;    ret    far            ; can't use a RET here since
  362.     db    0cbh            ; we are using .model
  363. ;
  364. exec_fault:
  365.     mov    ah,3            ; return error as 03xx
  366. ;    ret    far
  367.     db    0cbh
  368. ;
  369. ;
  370. ;    get_mcb allocates a block of memory by modifying the MCB chain
  371. ;    directly.
  372. ;
  373. ;    On entry, DS:SI points to the next_mcbs descriptor for the block,
  374. ;          ES:0  is the MCB for the current block.
  375. ;
  376. ;    On exit,  ES is the wanted MCB.
  377. ;
  378. ;    Uses     AX, BX, CX
  379. ;
  380. get_mcb    proc    near
  381. ;     
  382.     cmp    es:id,4dh        ; 4d means normal MCB
  383.     jnz    gmcb_abort        ; halt if no next
  384.     mov    ax,es            ; current MCB
  385.     add    ax,es:paras
  386.     inc    ax
  387.     mov    es,ax            ; next MCB
  388.     cmp    ax,[si].next_addr
  389.     ja    gmcb_abort        ; halt if next MCB > wanted
  390.     je    mcb_found        ; jump if same addr as wanted
  391.     add    ax,es:paras        ; last addr
  392.     cmp    ax,[si].next_addr
  393.     jb    get_mcb            ; loop if last < wanted
  394.     cmp    es:owner,0
  395.     jne    gmcb_abort        ; halt if not free
  396. ;
  397. ;    The wanted MCB starts within the current MCB. We now have to
  398. ;    create a new MCB at the wanted position, which is initially
  399. ;    free, and shorten the current MCB to reflect the reduced size.
  400. ;
  401.     mov    bx,es            ; current
  402.     inc    bx            ; + 1 (header doesn't count)
  403.     mov    ax,[si].next_addr
  404.     sub    ax,bx            ; paragraphs between MCB and wanted
  405.     mov    bx,es:paras        ; paras in current MCB
  406.     sub    bx,ax            ; remaining paras
  407.     dec    bx            ; -1 for header
  408.     mov    es:paras,ax        ; set new size for current
  409.     mov    cl,es:id        ; old id
  410.     mov    es:id,4dh        ; set id: there is a next
  411.     mov    ax,[si].next_addr    ; now point to new MCB
  412.     mov    es,ax
  413.     mov    es:id,cl        ; and init to free
  414.     mov    es:owner,0
  415.     mov    es:paras,bx
  416. ;
  417. ;    We have found an MCB at the right address. If it's not free,
  418. ;    abort. Else check the size. If the size is ok, we're done 
  419. ;    (more or less).
  420. ;
  421. mcb_found:
  422.     mov    es,ax
  423.     cmp    es:owner,0
  424.     je    mcb_check        ; continue if free
  425. ;
  426. gmcb_abort:
  427.     jmp    exec_term
  428. ;
  429. mcb_check:
  430.     mov    ax,es:paras        ; size
  431.     cmp    ax,[si].next_size    ; size needed
  432.     jae    mcb_ok            ; ok if enough space
  433. ;
  434. ;    If there's not enough room in this MCB, check if the next
  435. ;    MCB is free, too. If so, coalesce both MCB's and check again.
  436. ;
  437.     cmp    es:id,4dh
  438.     jnz    gmcb_abort        ; halt if no next
  439.     push    es            ; save current
  440.     mov    bx,es
  441.     add    ax,bx
  442.     inc    ax            ; next MCB
  443.     mov    es,ax
  444.     cmp    es:owner,0        ; next free ?
  445.     jne    gmcb_abort        ; halt if not
  446.     mov    ax,es:paras        ; else load size
  447.     inc    ax            ; + 1 for header
  448.     mov    cl,es:id        ; and load ID
  449.     pop    es            ; back to last MCB
  450.     add    es:paras,ax        ; increase size
  451.     mov    es:id,cl        ; and store ID
  452.     jmp    mcb_check        ; now try again
  453. ;
  454. ;    The MCB is free and large enough. If it's larger than the
  455. ;    wanted size, create another MCB after the wanted.
  456. ;
  457. mcb_ok:
  458.     mov    bx,es:paras
  459.     sub    bx,[si].next_size
  460.     jz    mcb_no_next        ; ok, no next to create
  461.     push    es
  462.     dec    bx            ; size of next block
  463.     mov    ax,es
  464.     add    ax,[si].next_size
  465.     inc    ax            ; next MCB addr
  466.     mov    cl,es:id        ; id of this block
  467.     mov    es,ax            ; address next
  468.     mov    es:id,cl        ; store id
  469.     mov    es:paras,bx        ; store size
  470.     mov    es:owner,0        ; and mark as free
  471.     pop    es            ; back to old MCB
  472.     mov    es:id,4dh        ; mark next block present
  473.     mov    ax,[si].next_size    ; and set size to wanted
  474.     mov    es:paras,ax
  475. ;
  476. mcb_no_next:
  477.     mov    es:owner,cx        ; set owner to current PSP
  478.     ret                ; all finished (whew!)
  479. ;
  480. get_mcb    endp
  481. ;
  482. ireti:
  483.     iret
  484. ;
  485. iretoff    =    offset ireti - offset doexec
  486. ;
  487. execlen    =    $-doexec
  488. ;
  489. ;--------------------------------------------------------------------
  490. ;
  491. parseg2    struc
  492.         db    TYPE parseg dup(?)
  493. ;
  494. codeloc        db    execlen dup(?)    ; code
  495. ;    
  496. div0_off    dw    ?        ; divide by zero vector save
  497. div0_seg    dw    ?
  498. xfcb1        db    16 dup(?)    ; default FCB
  499. xfcb2        db    16 dup(?)    ; default FCB
  500. filename    db    66 dup(?)    ; exec filename
  501. progpars    db    128 dup(?)    ; command tail
  502.         db    stacklen dup(?)    ; stack space
  503. ;
  504. parseg2    ends
  505. ;
  506. mystack        equ    progpars+128+stacklen
  507. parend        equ    mystack
  508. ;
  509. reslen    = (offset parend - offset parbeg)
  510. ;
  511. keep_paras    = (reslen + 15) shr 4    ; paragraphs to keep
  512. swapbeg        = keep_paras shl 4        ; start of swap space
  513. savespace    = swapbeg - 5ch        ; length of overwritten area
  514. ;
  515. ;    Space for saving the part of the memory image below the
  516. ;    swap area that is overwritten by our code.
  517. ;
  518.     .data
  519. save_dat    db    savespace dup(?)
  520. ;       
  521.     .code
  522. ;
  523. emm_name    db    'EMMXXXX0'
  524. ;
  525.     IFDEF    PASCAL
  526.     IFDEF    FARCALL
  527. do_spawn    PROC    far pmethod: byte, swapfname: dword, execfname: dword, params: dword, envlen: word, envp: dword
  528.     ELSE
  529. do_spawn    PROC    near pmethod: byte, swapfname: dword, execfname: dword, params: dword, envlen: word, envp: dword
  530.     ENDIF
  531.     ELSE
  532. do_spawn    PROC    uses si di,pmethod:byte,swapfname:ptr byte,execfname:ptr byte,params:ptr byte,envlen:word,envp:ptr byte
  533.     ENDIF
  534. ;
  535.     public    do_spawn
  536. ;
  537.     push    ds
  538.     mov    ax,ds
  539.     mov    es,ax
  540.     cld
  541. ;
  542.     IFDEF    PASCAL
  543.     mov    ax,prefixseg
  544.     ELSE
  545.     mov    ax,_psp
  546.     ENDIF
  547.     mov    ds,ax            ; DS points to PSP
  548. ;
  549. ;    Save the memory below the swap space
  550. ;
  551.     mov    si,5ch
  552.     mov    di,offset save_dat
  553.     mov    cx,savespace
  554.     rep movsb
  555. ;
  556.     mov    es,ax            ; ES points to PSP
  557.     dec    ax
  558.     mov    ds,ax            ; DS now points to MEM CTL
  559.     mov    bx,word ptr ds:3    ; allocated paragraphs
  560.     mov    es:memsize,bx
  561. ;
  562. ;    Now walk the chain of memory blocks, chaining all blocks
  563. ;    belonging to this process. Four unused words in the MCB are
  564. ;    used to store the paragraph address and size of the next block.
  565. ;
  566.     push    es
  567.     mov    bx,ds:owner        ; the current process
  568.     mov    di,offset next_mcb
  569. ;
  570. mcb_chain:
  571.     cmp    ds:id,4dh        ; normal block?
  572.     jne    mcb_ready        ; ready if end
  573.     mov    cx,ds
  574.     add    cx,ds:paras        ; start + length
  575.     inc    cx            ; next MCB
  576.     mov    ds,cx
  577.     cmp    bx,ds:owner        ; our process?
  578.     jne    mcb_chain        ; loop if not
  579.     mov    es:[di].next_addr,ds
  580.     mov    ax,ds:paras
  581.     mov    es:[di].next_size,ax
  582.     inc    ax
  583.     push    ax
  584.     mov    cl,blockshift
  585.     shr    ax,cl            ; number of blocks
  586.     mov    es:[di].next_blocks,ax
  587.     pop    ax
  588.     and    ax,blockmask
  589.     mov    es:[di].next_rest,ax
  590.     mov    ax,ds
  591.     mov    es,ax
  592.     mov    di,offset mcb_reserved
  593.     jmp    mcb_chain
  594. ;
  595. mcb_ready:
  596.     mov    es:[di].next_addr,0
  597.     pop    es
  598. ;
  599.     mov    es:page_frame,0
  600. ;
  601. ;    Swap out memory
  602. ;
  603.     mov    al,pmethod
  604.     mov    es:method,al
  605.     test    al,SWAPPING
  606.     jnz    do_swap
  607.     jmp    no_swap
  608. ;
  609. do_swap:
  610.     mov    bx,es:memsize
  611.     sub    bx,keep_paras        ; minus resident paragraphs
  612.     mov    ax,bx
  613.     mov    cl,blockshift
  614.     shr    ax,cl            ; number of blocks in swapfile
  615.     mov    es:rd_blocks,ax
  616.     xchg    ax,bx
  617.     and    ax,blockmask
  618.     mov    cl,4
  619.     shl    ax,cl            ; number of bytes in last block
  620.     mov    es:rd_rest,ax
  621. ;
  622. ;    Check for EMS swap
  623. ;
  624.     test    pmethod,USE_EMS
  625.     jnz    try_ems
  626.     jmp    no_ems
  627. ;
  628. try_ems:
  629.     or    ax,ax
  630.     jz    no_rest
  631.     inc    bx            ; number of EMS blocks needed
  632. ;
  633. ;    For EMS, we have to determine the total number of blocks
  634. ;    for all memory blocks.
  635. ;
  636. no_rest:
  637.     mov    si,offset next_mcb
  638.     push    es
  639.     pop    ds
  640. ;
  641. tot_blocks:
  642.     mov    cx,[si].next_addr
  643.     or    cx,cx
  644.     jz    tot_ready
  645.     add    bx,[si].next_blocks
  646.     mov    ax,[si].next_rest
  647.     mov    ds,cx
  648.     mov    si,offset mcb_reserved
  649.     or    ax,ax
  650.     jz    tot_blocks
  651.     inc    bx
  652.     jmp    tot_blocks
  653. ;
  654. tot_ready:
  655.     push    bx
  656.     push    es
  657.     mov    al,EMM_INT
  658.     mov    ah,35h
  659.     int    21h            ; get EMM int vector
  660.     mov    ax,cs
  661.     mov    ds,ax
  662.     mov    si,offset emm_name
  663.     mov    di,10
  664.     mov    cx,8
  665.     repz cmpsb
  666.     pop    es
  667.     pop    bx
  668.     jz    ems_ok_1
  669.     jmp    no_ems
  670. ;
  671. ems_ok_1:
  672.     mov    ah,40h
  673.     int    EMM_INT
  674.     or    ah,ah
  675.     jz    ems_ok_2
  676.     jmp    no_ems
  677. ;
  678. ems_ok_2:
  679.     mov    ah,46h
  680.     int    EMM_INT
  681.     or    ah,ah
  682.     jz    ems_ok_3
  683.     jmp    no_ems
  684. ;
  685. ems_ok_3:
  686.     cmp    al,30h
  687.     jae    ems_ok_4
  688.     jmp    no_ems
  689. ;
  690. ems_ok_4:
  691.     push    bx
  692.     mov    ah,41h
  693.     int    EMM_INT
  694.     mov    es:page_frame,bx
  695.     pop    bx
  696.     or    ah,ah
  697.     jz    ems_ok_5
  698.     jmp    no_ems
  699. ;
  700. ;    EMS present, try to allocate pages
  701. ;
  702. ems_ok_5:
  703.     mov    ah,43h
  704.     int    EMM_INT
  705.     or    ah,ah
  706.     jz    ems_ok_6
  707.     jmp    no_ems
  708. ;
  709. ;    EMS pages allocated, swap to EMS
  710. ;
  711. ems_ok_6:
  712.     mov    es:handle,dx
  713.     push    es
  714.     mov    ax,es
  715.     mov    ds,ax
  716.     push    ds
  717.     mov    ax,offset next_mcb
  718.     push    ax
  719.     push    es:rd_rest
  720.     mov    cx,es:rd_blocks
  721.     mov    es,es:page_frame
  722.     xor    bx,bx
  723.     mov    si,swapbeg
  724. ;
  725. swap_ems:
  726.     jcxz    swapout_erest        ; jump if no full blocks
  727. ;
  728. swapout_ems:
  729.     push    cx
  730.     mov    ax,4400h            ; map page, phys = 0
  731.     int    EMM_INT
  732.     or    ah,ah
  733.     jnz    ems_error
  734.     mov    cx,blocksize
  735.     mov    di,0
  736.     push    si
  737.     rep movsb
  738.     pop    si
  739.     mov    ax,ds
  740.     add    ax,blocksize_paras
  741.     mov    ds,ax
  742.     pop    cx
  743.     inc    bx
  744.     loop    swapout_ems
  745. ;
  746. swapout_erest:
  747.     pop    cx            ; remaining bytes
  748.     push    cx
  749.     push    cx
  750.     jcxz    swapout_erdy
  751.     mov    ax,4400h        ; map page, phys = 0
  752.     int    EMM_INT
  753.     or    ah,ah
  754.     jnz    ems_error
  755.     mov    di,0
  756.     rep movsb
  757.     inc    bx
  758. ;
  759. swapout_erdy:
  760.     add    sp,4
  761.     pop    si            ; next offset
  762.     pop    ds            ; segment
  763. ;
  764.     mov    ax,[si].next_addr
  765.     or    ax,ax
  766.     jz    ems_complete
  767.     push    ax
  768.     mov    cx,offset mcb_reserved
  769.     push    cx
  770.     mov    cx,[si].next_blocks
  771.     mov    si,[si].next_rest
  772.     push    si
  773.     mov    si,0
  774.     mov    ds,ax
  775.     jmp    swap_ems
  776. ;
  777. ems_complete:
  778.     pop    es
  779.     jmp    no_swap
  780. ;
  781. ems_error:
  782.     add    sp,8
  783.     pop    es
  784.     mov    ah,45h            ; release EMS pages on error
  785.     int    EMM_INT
  786. ;
  787. ;    No or not enough EMS storage, swap to file
  788. ;
  789. no_ems:
  790.     mov    es:page_frame,0
  791.     IF    ptrsize
  792.     lds    dx,swapfname
  793.     ELSE
  794.     pop    ds
  795.     push    ds
  796.     mov    dx,swapfname
  797.     ENDIF
  798.     inc    dx
  799.     mov    cx,2            ; hidden
  800.     mov    ah,3ch            ; create file
  801.     test    pmethod,CREAT_TEMP
  802.     jz    no_temp
  803.     mov    ah,5ah
  804. ;
  805. no_temp:
  806.     int    21h
  807.     jc    spawn_error
  808.     mov    es:handle,ax
  809.     mov    bx,ax
  810. ;
  811.     mov    ax,es
  812.     mov    ds,ax
  813.     push    ds
  814.     mov    si,offset next_mcb
  815.     mov    cx,es:rd_blocks
  816.     mov    di,es:rd_rest
  817.     mov    dx,swapbeg
  818. ;
  819. swap_file:
  820.     jcxz    swout_rest
  821. ;
  822. swout_blocks:
  823.     push    cx
  824.     mov    cx,blocksize
  825.     mov    ah,40h
  826.     int    21h
  827.     pop    cx
  828.     jc    spawn_error
  829.     mov    ax,ds
  830.     add    ax,blocksize_paras
  831.     mov    ds,ax
  832.     loop    swout_blocks
  833. ;
  834. swout_rest:
  835.     mov    cx,di
  836.     jcxz    swap_ready
  837.     mov    ah,40h
  838.     int    21h
  839.     jnc    swap_ready
  840. ;
  841. spawn_error:
  842.     add    sp,2
  843.     pop    ds
  844.     mov    ax,100h
  845.     ret
  846. ;
  847. swap_ready:
  848.     pop    ds
  849.     mov    ax,[si].next_addr
  850.     or    ax,ax
  851.     jz    swap_complete
  852.     mov    cx,[si].next_blocks
  853.     mov    di,[si].next_rest
  854.     mov    dx,0
  855.     mov    si,offset mcb_reserved
  856.     mov    ds,ax
  857.     push    ds
  858.     jmp    swap_file
  859. ;
  860. ;    Swapout complete
  861. ;
  862. swap_complete:
  863.     mov    ax,4200h
  864.     xor    cx,cx
  865.     mov    dx,cx
  866.     int    21h            ; rewind file
  867. ;
  868. no_swap:
  869. ;
  870. ;    Prepare exec parameter block
  871. ;
  872.     mov    ax,es
  873.     mov    es:expar.fcb1seg,ax
  874.     mov    es:expar.fcb2seg,ax
  875.     mov    es:expar.pparseg,ax
  876.     mov    es:expar.envseg,0
  877. ;
  878. ;    The 'zero' word is located at 80h in the PSP, the start of
  879. ;    the command line. So as not to confuse MCB walking programs,
  880. ;    a command line length of zero is inserted here.
  881. ;
  882.     mov    es:zero,0d00h        ; 00h,0dh = empty command line
  883. ;
  884. ;    Init default fcb's by parsing parameter string
  885. ;
  886.     IF    ptrsize
  887.     lds    si,params
  888.     ELSE
  889.     pop    ds
  890.     push    ds
  891.     mov    si,params
  892.     ENDIF
  893.     push    si
  894.     mov    di,offset xfcb1
  895.     mov    es:expar.fcb1,di
  896.     push    di
  897.     mov    cx,16
  898.     xor    ax,ax
  899.     rep stosw            ; init both fcb's to 0
  900.     pop    di
  901.     mov    ax,2901h
  902.     IFDEF    PASCAL
  903.     inc    si
  904.     ENDIF
  905.     int    21h
  906.     mov    di,offset xfcb2
  907.     mov    es:expar.fcb2,di
  908.     mov    ax,2901h
  909.     int    21h
  910.     pop    si
  911. ;
  912. ;    move command tail string into low core
  913. ;
  914.     mov    cl,byte ptr [si]
  915.     xor    ch,ch
  916.     mov    di,offset progpars
  917.     mov    es:expar.ppar,di
  918.     inc    cx
  919.     rep movsb
  920.     mov    al,0dh
  921.     stosb
  922. ;
  923. ;    move filename string into low core
  924. ;
  925.     IF    ptrsize
  926.     lds    si,execfname
  927.     ELSE
  928.     mov    si,execfname
  929.     ENDIF
  930.     lodsb
  931.     mov    cl,al
  932.     xor    ch,ch
  933.     mov    di,offset filename
  934.     rep movsb
  935.     xor    al,al
  936.     stosb
  937. ;
  938. ;    Setup environment copy
  939. ;
  940.     mov    bx,keep_paras        ; paras to keep
  941.     mov    cx,envlen        ; environment size
  942.     jcxz    no_environ        ; go jump if no environment
  943.     mov    ax,cx            ; convert envsize to paras
  944.     add    ax,15
  945.     shr    ax,1
  946.     shr    ax,1
  947.     shr    ax,1
  948.     shr    ax,1
  949.     add    bx,ax            ; add envsize to paras to keep
  950.     IF    ptrsize
  951.     lds    si,envp
  952.     ELSE
  953.     mov    si,envp
  954.     ENDIF
  955. ;
  956.     mov    ax,es            ; low core segment
  957.     add    ax,keep_paras        ; plus fixed paras
  958.     mov    es:expar.envseg,ax    ; = new environment segment
  959. ;
  960. ;    Save stack regs, switch to local stack
  961. ;
  962. no_environ:
  963.     mov    es:save_ss,ss
  964.     mov    es:save_sp,sp
  965.     mov    ax,es
  966.     mov    ss,ax
  967.     mov    sp,offset mystack
  968. ;
  969.     push    cx
  970.     push    si
  971.     push    ds
  972. ;
  973. ;    Move code into low core
  974. ;
  975.     mov    di,offset codeloc
  976.     call    near ptr nextloc        ; where are we ?
  977. nextloc:
  978.     pop    si            ; si = abs. addr of 'nextloc'
  979.     push    si
  980.         add     si,doexec-nextloc
  981.         mov     cx,execlen
  982.     push    cs
  983.     pop    ds
  984.         rep movsb                       ; move down code
  985. ;
  986. ;    save and patch INT0 (division by zero) vector
  987. ;
  988.     xor    ax,ax
  989.     mov    ds,ax
  990.     mov    ax,word ptr ds:0
  991.     mov    es:div0_off,ax
  992.     mov    ax,word ptr ds:2
  993.     mov    es:div0_seg,ax
  994.     mov    word ptr ds:0,offset codeloc + iretoff
  995.     mov    word ptr ds:2,es
  996. ;
  997. ;    Push return address on local stack
  998. ;
  999.     pop    ax
  1000.     add    ax,exec_cont-nextloc
  1001. ;
  1002.     pop    ds            ; pop environment segment
  1003.     pop    si            ; pop environment offset
  1004.     pop    cx            ; pop environment length
  1005.     mov    di,swapbeg        ; pop environment destination
  1006. ;
  1007.     push    cs            ; push return segment
  1008.     push    ax            ; push return offset
  1009.     mov    es:spx,sp        ; save stack pointer
  1010. ;
  1011. ;    Goto low core code
  1012. ;
  1013.     push    es            ; push entry segment
  1014.         mov    ax,offset codeloc
  1015.         push    ax            ; push entry offset
  1016. ;    ret    far            ; can't use RET here because
  1017.     db    0cbh            ; of .model
  1018. ;
  1019. ;----------------------------------------------------------------
  1020. ;
  1021. ;    low core code will return to this location
  1022. ;
  1023. exec_cont:
  1024.     mov    ss,es:save_ss        ; reload stack
  1025.     mov    sp,es:save_sp
  1026. ;
  1027. ;    restore INT0 (division by zero) vector
  1028. ;
  1029.     xor    cx,cx
  1030.     mov    ds,cx
  1031.     mov    cx,es:div0_off
  1032.     mov    word ptr ds:0,cx
  1033.     mov    cx,es:div0_seg
  1034.     mov    word ptr ds:2,cx
  1035. ;
  1036.     pop    ds
  1037. ;
  1038.     mov    bx,es:page_frame    ; save EMS status in BX
  1039. ;
  1040. ;    Restore overwritten part of program
  1041. ;
  1042.     mov    si,offset save_dat
  1043.     mov    di,5ch
  1044.     mov    cx,savespace
  1045.     rep movsb
  1046. ;
  1047.     or    bx,bx            ; EMS swap?
  1048.     jnz    exec_finish        ; ready if yes
  1049. ;
  1050.     push    ds            ; else delete swapfile
  1051.     push    ax
  1052.     IF    ptrsize
  1053.     lds    dx,swapfname
  1054.     ELSE
  1055.     mov    dx,swapfname
  1056.     ENDIF
  1057.     inc    dx
  1058. ;
  1059.     mov    ah,41h
  1060.     int    21h            ; delete file
  1061.     pop    ax
  1062.     pop    ds
  1063. ;
  1064. exec_finish:
  1065.     ret
  1066. ;    
  1067. do_spawn    ENDP
  1068. ;
  1069.         END
  1070.  
  1071.  
  1072.