home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1996 October / PCO_10.ISO / filesbbs / bsrc_260.arj / SRC.ZIP / spawn.asm < prev    next >
Encoding:
Assembly Source File  |  1996-02-20  |  49.3 KB  |  2,130 lines

  1. ;--------------------------------------------------------------------------;
  2. ;                                                                          ;
  3. ;                                                                          ;
  4. ;      ------------         Bit-Bucket Software, Co.                       ;
  5. ;      \ 10001101 /         Writers and Distributors of                    ;
  6. ;       \ 011110 /          Freely Available<tm> Software.                 ;
  7. ;        \ 1011 /                                                          ;
  8. ;         ------                                                           ;
  9. ;                                                                          ;
  10. ;              (C) Copyright 1987-96, Bit Bucket Software Co.              ;
  11. ;                                                                          ;
  12. ;               This module integrated by Vince Perriello                  ;
  13. ;                Based on original code by Thomas Wagner                   ;
  14. ;                DOS Spawn/Swapper code for BinkleyTerm                    ;
  15. ;                                                                          ;
  16. ;                                                                          ;
  17. ;    For complete  details  of the licensing restrictions, please refer    ;
  18. ;    to the License  agreement,  which  is published in its entirety in    ;
  19. ;    the MAKEFILE and BT.C, and also contained in the file LICENSE.260.    ;
  20. ;                                                                          ;
  21. ;    USE  OF THIS FILE IS SUBJECT TO THE  RESTRICTIONS CONTAINED IN THE    ;
  22. ;    BINKLEYTERM  LICENSING  AGREEMENT.  IF YOU DO NOT FIND THE TEXT OF    ;
  23. ;    THIS  AGREEMENT IN ANY OF THE  AFOREMENTIONED FILES,  OR IF YOU DO    ;
  24. ;    NOT HAVE THESE FILES,  YOU  SHOULD  IMMEDIATELY CONTACT BIT BUCKET    ;
  25. ;    SOFTWARE CO.  AT ONE OF THE  ADDRESSES  LISTED BELOW.  IN NO EVENT    ;
  26. ;    SHOULD YOU  PROCEED TO USE THIS FILE  WITHOUT HAVING  ACCEPTED THE    ;
  27. ;    TERMS  OF  THE  BINKLEYTERM  LICENSING  AGREEMENT,  OR  SUCH OTHER    ;
  28. ;    AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO.      ;
  29. ;                                                                          ;
  30. ;                                                                          ;
  31. ; You can contact Bit Bucket Software Co. at any one of the following      ;
  32. ; addresses:                                                               ;
  33. ;                                                                          ;
  34. ; Bit Bucket Software Co.        FidoNet  1:104/501, 1:343/491             ;
  35. ; P.O. Box 460398                AlterNet 7:42/1491                        ;
  36. ; Aurora, CO 80046               BBS-Net  86:2030/1                        ;
  37. ;                                Internet f491.n343.z1.fidonet.org         ;
  38. ;                                                                          ;
  39. ; Please feel free to contact us at any time to share your comments about  ;
  40. ; our software and/or licensing policies.                                  ;
  41. ;                                                                          ;
  42. ;--------------------------------------------------------------------------;
  43.  
  44. ;
  45. ;    --- Version 3.0 91-05-27 17:56 ---
  46. ;
  47. ;    SPAWN.ASM - Main function for memory swapping spawn call.
  48. ;
  49. ;    Public Domain Software written by
  50. ;        Thomas Wagner
  51. ;        Ferrari electronic GmbH
  52. ;        Beusselstrasse 27
  53. ;        D-1000 Berlin 21
  54. ;        Germany
  55. ;
  56. ;
  57. ; Assemble with
  58. ;
  59. ; tasm  /DPASCAL spawn,spawnp          - Turbo Pascal (Tasm only), near
  60. ; tasm  /DPASCAL /DFARCALL spawn,spawnp    - Turbo Pascal (Tasm only), far
  61. ; ?asm  spawn;                  - C, default model (small)
  62. ; ?asm  /DMODL=large spawn          - C, large model
  63. ;
  64. ;    NOTE:    For C, change the 'model' directive below according to your
  65. ;        memory model, or define MODL=xxx on the command line.
  66. ;
  67. ;        For Turbo C Huge model, you must give /DTC_HUGE on the
  68. ;        command line, or define it here.
  69. ;
  70. ;
  71. ; Main function:
  72. ;
  73. ;   PASCAL:
  74. ;       function do_spawn (swapping: integer; 
  75. ;                  execfname: string;
  76. ;               cmdtail: string; 
  77. ;                  envlen: word; 
  78. ;               var envp)
  79. ;
  80. ;   C:
  81. ;       int do_spawn (int swapping,
  82. ;              char *execfname, 
  83. ;              char *cmdtail,
  84. ;              unsigned envlen, 
  85. ;              char *envp)
  86. ;
  87. ;   Parameters:
  88. ;
  89. ;    swapping - swap/spawn/exec function:
  90. ;            < 0: Exec, don't swap
  91. ;                0: Spawn, don't swap
  92. ;            > 0: Spawn, swap
  93. ;                 in this case, prep_swap must have 
  94. ;                 been called beforehand (see below).
  95. ;
  96. ;    cmdtail - command tail for EXEC.
  97. ;
  98. ;    execfname - name and path of file to execute.
  99. ;
  100. ;    envlen - length of environment copy (may be 0).
  101. ;
  102. ;    envp -  pointer to environment block (must be aligned on
  103. ;        paragraph boundary). Unused if envlen is 0.
  104. ;
  105. ;    'cmdtail' and 'execfname' must be zero terminated, even when
  106. ;    calling from Pascal. For Pascal, the length byte of the string
  107. ;    is ignored.
  108. ;
  109. ;   Returns:
  110. ;    0000..00ff:    Returncode of EXECed program
  111. ;    03xx:        DOS-Error xx calling EXEC
  112. ;    0500:        Swapping requested, but prep_swap has not 
  113. ;            been called or returned an error
  114. ;    0501:        MCBs don't match expected setup
  115. ;    0502:        Error while swapping out
  116. ;
  117. ;
  118. ; For swapping, the swap method must be prepared before calling do_spawn.
  119. ;
  120. ;   PASCAL:
  121. ;    function prep_swap (method: word; swapfname: string): integer;
  122. ;   C:
  123. ;    int prep_swap (unsigned method, char *swapfname)
  124. ;
  125. ;   Parameters:
  126. ;
  127. ;    method    - bit-map of allowed swap devices:
  128. ;            01 - Allow EMS
  129. ;            02 - Allow XMS
  130. ;            04 - Allow File swap
  131. ;            10 - Try XMS first, then EMS
  132. ;            40 - Create file as "hidden"
  133. ;            80 - Use "create temp" call for file swap
  134. ;               100 - Don't preallocate file
  135. ;               200 - Check for Network, don't preallocate if net
  136. ;              4000 - Environment block will not be swapped
  137. ;
  138. ;    swapfname - swap file name (may be undefined if the
  139. ;            "method" parameters disallows file swap).
  140. ;            The string must be zero terminated, even
  141. ;            when calling from Pascal. For Pascal, the 
  142. ;            length byte of the string is ignored.
  143. ;
  144. ;   Returns:
  145. ;
  146. ;       A positive integer on success:
  147. ;        1 - EMS swap initialized
  148. ;        2 - XMS swap initialized
  149. ;        4 - File swap initialized
  150. ;    A negative integer on failure:
  151. ;        -1 - Couldn't allocate swap space
  152. ;        -2 - The spawn module is located too low in memory
  153. ;
  154. ;
  155.     IFDEF    PASCAL
  156.     .model    tpascal
  157. ;
  158.     extrn    prefixseg: word
  159. ;
  160. ptrsize    =    1
  161.     ELSE
  162.     IFNDEF    MODL
  163.     .model    small,c
  164.     ELSE
  165. %    .model    MODL,c
  166.     ENDIF
  167. ;
  168. ptrsize    =    @DataSize
  169. ;
  170.     extrn    _psp: word
  171.     ENDIF
  172. ;
  173.     public    do_spawn
  174.     public    prep_swap
  175. ;
  176. stacklen    =    256        ; local stack
  177. ;
  178. ;    "ems_size" is the EMS block size: 16k.
  179. ;
  180. ems_size    =    16 * 1024    ; EMS block size
  181. ems_parasize    =    ems_size / 16    ; same in paragraphs
  182. ems_shift    =    10        ; shift factor for paragraphs
  183. ems_paramask    =    ems_parasize-1    ; block mask
  184. ;
  185. ;    "xms_size" is the unit of measurement for XMS: 1k
  186. ;
  187. xms_size    =    1024        ; XMS block size
  188. xms_parasize    =    xms_size / 16    ; same in paragraphs
  189. xms_shift    =    6        ; shift factor for paragraphs
  190. xms_paramask    =    xms_parasize-1    ; block mask
  191. ;
  192. ;    Method flags
  193. ;
  194. USE_EMS        =    01h
  195. USE_XMS        =    02h
  196. USE_FILE    =    04h
  197. XMS_FIRST    =    10h
  198. HIDE_FILE    =    40h
  199. CREAT_TEMP    =    80h
  200. NO_PREALLOC    =    100h
  201. CHECK_NET    =    200h
  202. DONT_SWAP_ENV    =    4000h
  203. ;
  204. ;    Return codes
  205. ;
  206. RC_TOOLOW    =    0102h
  207. RC_BADPREP    =    0500h
  208. RC_MCBERROR    =    0501h
  209. RC_SWAPERROR    =    0502h
  210. ;
  211. EMM_INT        =    67h
  212. ;
  213. ;    The EXEC function parameter block
  214. ;
  215. exec_block    struc
  216. envseg    dw    ?        ; environment segment
  217. ppar    dw    ?        ; program parameter string offset
  218. pparseg    dw    ?        ; program parameter string segment
  219. fcb1    dw    ?        ; FCB offset
  220. fcb1seg    dw    ?        ; FCB segment
  221. fcb2    dw    ?        ; FCB offset
  222. fcb2seg    dw    ?        ; FCB segment
  223. exec_block    ends
  224. ;
  225. ;    Structure of an XMS move control block
  226. ;
  227. xms_control    struc
  228. lenlo        dw    ?    ; length to move (doubleword)
  229. lenhi        dw    ?
  230. srchnd        dw    ?    ; source handle (0 for standard memory)
  231. srclo        dw    ?    ; source address (doubleword or seg:off)
  232. srchi        dw    ?
  233. desthnd        dw    ?    ; destination handle (0 for standard memory)
  234. destlo        dw    ?    ; destination address (doubleword or seg:off)
  235. desthi        dw    ?
  236. xms_control    ends
  237. ;
  238. ;    The structure of the start of an MCB (memory control block)
  239. ;
  240. mcb        struc
  241. id        db    ?
  242. owner        dw    ?
  243. paras        dw    ?
  244. mcb        ends
  245. ;
  246. ;    The structure of an internal MCB descriptor.
  247. ;    CAUTION: This structure is assumed to be no larger than 16 bytes
  248. ;    in several places in the code, and to be exactly 16 bytes when
  249. ;    swapping in from file. Be careful when changing this structure.
  250. ;
  251. mcbdesc        struc
  252. addr        dw    ?    ; paragraph address of the MCB
  253. msize        dw    ?    ; size in paragraphs (excluding header)
  254. swoffset    dw    ?    ; swap offset (0 in all blocks except first)
  255. swsize        dw    ?    ; swap size (= msize + 1 except in first)
  256. num_follow    dw    ?    ; number of following MCBs
  257.         dw    3 dup(?) ; pad to paragraph (16 bytes)
  258. mcbdesc        ends
  259. ;
  260. ;    The variable block set up by prep_swap
  261. ;
  262. prep_block    struc
  263. xmm        dd    ?        ; XMM entry address
  264. first_mcb    dw    ?        ; Segment of first MCB
  265. psp_mcb        dw    ?        ; Segment of MCB of our PSP
  266. env_mcb        dw    ?        ; MCB of Environment segment
  267. noswap_mcb    dw    ?        ; MCB that may not be swapped
  268. ems_pageframe    dw    ?        ; EMS page frame address
  269. handle        dw    ?        ; EMS/XMS/File handle
  270. total_mcbs    dw    ?        ; Total number of MCBs
  271. swapmethod    db    ?        ; Method for swapping
  272. swapfilename    db    81 dup(?)    ; Swap file name if swapping to file
  273. prep_block    ends
  274. ;
  275. ;----------------------------------------------------------------------
  276. ;
  277. ;    Since we'll be moving code and data around in memory,
  278. ;    we can't address locations in the resident block with
  279. ;    normal address expressions. MASM does not support
  280. ;    defining variables with a fixed offset, so we have to resort
  281. ;    to a kludge, and define the shrunk-down code as a structure.
  282. ;    It would also be possible to use an absolute segment for the
  283. ;    definition, but this is not supported by the Turbo Pascal linker.
  284. ;
  285. ;    All references to low-core variables from low-core itself 
  286. ;    are made through DS, so we define a text macro "lmem" that 
  287. ;    expands to "ds:". When setting up low core from the normal
  288. ;    code, ES is used to address low memory, so this can't be used.
  289. ;
  290. lmem    equ    <ds:>
  291. ;
  292. ;    The memory structure for the shrunk-down code, excluding the
  293. ;    code itself. The code follows this block.
  294. ;
  295. parseg        struc
  296.         db    2ch dup(?)
  297. psp_envptr    dw    ?
  298.         db    5ch-2eh dup(?)    ; start after PSP
  299. ;
  300. save_ss        dw    ?        ; 5C - saved global ss
  301. save_sp        dw    ?        ; 5E - saved global sp
  302. xfcb1        db    16 dup(?)    ; 60..6F - default FCB
  303. xfcb2        db    16 dup(?)    ; 70..7F - default FCB
  304. zero        dw    ?        ; 80 Zero command tail length (dummy)
  305. ;
  306. expar        db    TYPE exec_block dup (?) ; exec-parameter-block
  307. spx        dw    ?        ; saved local sp
  308. div0_off    dw    ?        ; divide by zero vector save
  309. div0_seg    dw    ?
  310. filename    db    82 dup(?)    ; exec filename
  311. progpars    db    128 dup(?)    ; command tail
  312.         db    stacklen dup(?)    ; local stack space
  313. mystack        db    ?
  314. lprep        db    TYPE prep_block dup(?)    ; the swapping variables
  315. lcurrdesc    db    TYPE mcbdesc dup(?)    ; the current MCB descriptor
  316. lxmsctl        db    TYPE xms_control dup(?)
  317. eretcode    dw    ?        ; EXEC return code
  318. retflags    dw    ?        ; EXEC return flags
  319. cgetmcb        dw    ?        ; address of get_mcb
  320. ;
  321. parseg    ends
  322. ;
  323. param_len    =    ((TYPE parseg + 1) / 2) * 2    ; make even
  324. codebeg        =    param_len
  325. ;
  326.     .code
  327. ;
  328. ;------------------------------------------------------------------------
  329. ;
  330. lowcode_begin:
  331. ;
  332. ;       The following parts of the program code will be moved to
  333. ;    low core and executed there, so there must be no absolute 
  334. ;    memory references.
  335. ;    The call to get_mcb must be made indirect, since the offset
  336. ;    from the swap-in routine to get_mcb will not be the same
  337. ;    after moving.
  338. ;
  339. ;
  340. ;    get_mcb allocates a block of memory by modifying the MCB chain
  341. ;    directly.
  342. ;
  343. ;    On entry, lcurrdesc has the mcb descriptor for the block to
  344. ;          allocate.
  345. ;
  346. ;    On exit,  Carry is set if the block couldn't be allocated.
  347. ;
  348. ;    Uses     AX, BX, CX, ES
  349. ;    Modifies lprep.first_mcb
  350. ;
  351. get_mcb    proc    near
  352. ;
  353.     mov    ax,lmem lprep.first_mcb
  354.     mov    bx,lmem lcurrdesc.addr
  355. ;
  356. getmcb_loop:
  357.     mov    es,ax
  358.     cmp    ax,bx
  359.     ja    gmcb_abort        ; halt if MCB > wanted
  360.     je    mcb_found        ; jump if same addr as wanted
  361.     add    ax,es:paras        ; last addr
  362.     inc    ax            ; next mcb
  363.     cmp    ax,bx
  364.     jbe    getmcb_loop        ; Loop if next <= wanted
  365. ;
  366. ;
  367. ;    The wanted MCB starts within the current MCB. We now have to
  368. ;    create a new MCB at the wanted position, which is initially
  369. ;    free, and shorten the current MCB to reflect the reduced size.
  370. ;
  371.     cmp    es:owner,0
  372.     jne    gmcb_abort        ; halt if not free
  373.     mov    bx,es            ; current
  374.     inc    bx            ; + 1 (header doesn't count)
  375.     mov    ax,lmem lcurrdesc.addr
  376.     sub    ax,bx            ; paragraphs between MCB and wanted
  377.     mov    bx,es:paras        ; paras in current MCB
  378.     sub    bx,ax            ; remaining paras
  379.     dec    bx            ; -1 for header
  380.     mov    es:paras,ax        ; set new size for current
  381.     mov    cl,es:id        ; old id
  382.     mov    es:id,4dh        ; set id: there is a next
  383.     mov    ax,lmem lcurrdesc.addr
  384.     mov    es,ax
  385.     mov    es:id,cl        ; and init to free
  386.     mov    es:owner,0
  387.     mov    es:paras,bx
  388. ;
  389. ;    We have found an MCB at the right address. If it's not free,
  390. ;    abort. Else check the size. If the size is ok, we're done 
  391. ;    (more or less).
  392. ;
  393. mcb_found:
  394.     mov    es,ax
  395.     cmp    es:owner,0
  396.     je    mcb_check        ; continue if free
  397. ;
  398. gmcb_abort:
  399.     stc
  400.     ret
  401. ;
  402. mcb_check:
  403.     mov    ax,es:paras        ; size
  404.     cmp    ax,lmem lcurrdesc.msize    ; needed size
  405.     jae    mcb_ok            ; ok if enough space
  406. ;
  407. ;    If there's not enough room in this MCB, check if the next
  408. ;    MCB is free, too. If so, coalesce both MCB's and check again.
  409. ;
  410.     cmp    es:id,4dh
  411.     jnz    gmcb_abort        ; halt if no next
  412.     push    es            ; save current
  413.     mov    bx,es
  414.     add    ax,bx
  415.     inc    ax            ; next MCB
  416.     mov    es,ax
  417.     cmp    es:owner,0        ; next free ?
  418.     jne    gmcb_abort        ; halt if not
  419.     mov    ax,es:paras        ; else load size
  420.     inc    ax            ; + 1 for header
  421.     mov    cl,es:id        ; and load ID
  422.     pop    es            ; back to last MCB
  423.     add    es:paras,ax        ; increase size
  424.     mov    es:id,cl        ; and store ID
  425.     jmp    mcb_check        ; now try again
  426. ;
  427. ;    The MCB is free and large enough. If it's larger than the
  428. ;    wanted size, create another MCB after the wanted.
  429. ;
  430. mcb_ok:
  431.     mov    bx,es:paras
  432.     sub    bx,lmem lcurrdesc.msize
  433.     jz    mcb_no_next        ; ok, no next to create
  434.     push    es
  435.     dec    bx            ; size of next block
  436.     mov    ax,es
  437.     add    ax,lmem lcurrdesc.msize
  438.     inc    ax            ; next MCB addr
  439.     mov    cl,es:id        ; id of this block
  440.     mov    es,ax            ; address next
  441.     mov    es:id,cl        ; store id
  442.     mov    es:paras,bx        ; store size
  443.     mov    es:owner,0        ; and mark as free
  444.     pop    es            ; back to old MCB
  445.     mov    es:id,4dh        ; mark next block present
  446.     mov    ax,lmem lcurrdesc.msize    ; and set size to wanted
  447.     mov    es:paras,ax
  448. ;
  449. mcb_no_next:
  450.     mov    es:owner,cx        ; set owner to current PSP
  451. ;
  452. ;    Set the 'first_mcb' pointer to the current one, so we don't
  453. ;    walk through all the previous blocks the next time.
  454. ;    Also, check if the block we just allocated is the environment
  455. ;    segment of the program. If so, restore the environment pointer
  456. ;    in the PSP.
  457. ;
  458.     mov    ax,es
  459.     mov    lmem lprep.first_mcb,ax
  460.     cmp    lmem lprep.env_mcb,ax
  461.     jne    getmcb_finis
  462.     inc    ax
  463.     mov    lmem psp_envptr,ax
  464. ;
  465. getmcb_finis:
  466.     clc
  467.     ret                ; all finished (whew!)
  468. ;
  469. get_mcb    endp
  470. ;
  471. ;
  472. ireti:
  473.     iret
  474. ;
  475. ;
  476. ;    The actual EXEC call.
  477. ;    Registers on entry:
  478. ;        BX    = paragraphs to keep (0 if no swap)
  479. ;        CX     = length of environment to copy (words) or zero
  480. ;        DS:SI    = environment source
  481. ;        ES:DI    = environment destination
  482. ;        (ES = our low core code segment)
  483. ;
  484. ;
  485. ;    copy environment buffer down if present
  486. ;
  487. doexec:
  488.     jcxz    noenvcpy
  489.     rep movsw
  490. ;
  491. noenvcpy:
  492.     push    es            ; DS = ES = low core = PSP
  493.     pop    ds
  494.     or    bx,bx
  495.     jz    no_shrink
  496. ;
  497. ;    first, shrink the base memory block down.
  498. ;
  499.         mov    ah,04ah
  500.     int     21h                     ; resize memory block
  501. ;
  502. ;    Again walk all MCBs. This time, all blocks owned by the 
  503. ;    current process are released.
  504. ;
  505.     mov    si,lmem lprep.first_mcb
  506.     or    si,si
  507.     jz    no_shrink
  508.     mov    dx,lmem lprep.psp_mcb
  509.     mov    bx,dx
  510.     inc    bx            ; base PSP (MCB owner)
  511.     mov    di,lmem lprep.noswap_mcb
  512. ;
  513. free_loop:
  514.     cmp    si,dx
  515.     je    free_next        ; don't free base block
  516.     cmp    si,di
  517.     je    free_next
  518.     mov    es,si
  519.     cmp    bx,es:owner        ; our process?
  520.     jne    free_next        ; next if not
  521.     cmp    si,lmem lprep.env_mcb    ; is this the environment block?
  522.     jne    free_noenv
  523.     mov    ds:psp_envptr,0        ; else clear PSP pointer
  524. ;
  525. free_noenv:
  526.     inc    si
  527.     mov    es,si
  528.     dec    si
  529.     mov    ah,049h            ; free memory block
  530.     int    21h
  531. ;
  532. free_next:
  533.     mov    es,si
  534.     cmp    es:id,4dh        ; normal block?
  535.     jne    free_ready        ; ready if end of chain
  536.     add    si,es:paras        ; start + length
  537.     inc    si            ; next MCB
  538.     jmp    free_loop
  539. ;
  540. free_ready:
  541.     mov    ax,ds
  542.     mov    es,ax
  543. ;
  544. no_shrink:
  545.     mov    dx,filename        ; params for exec
  546.     mov    bx,expar
  547.     mov    ax,04b00h
  548.     int    21h            ; exec
  549. ;
  550. ;    Return from EXEC system call. Don't count on any register except
  551. ;    CS to be restored (DOS 2.11 and previous versions killed all regs).
  552. ;
  553.     mov    bx,cs
  554.     mov    ds,bx
  555.     mov    es,bx
  556.     mov    ss,bx
  557.     mov    sp,lmem spx
  558.     cld
  559.     mov    lmem eretcode,ax    ; save return code
  560.     pushf
  561.     pop    bx
  562.     mov    lmem retflags,bx    ; and returned flags
  563. ;
  564.     cmp    lmem lprep.swapmethod,0
  565.     je    exec_memok
  566.     jg    exec_expand
  567. ;
  568. ;    Terminate.
  569. ;
  570.     test    bx,1            ; carry?
  571.     jnz    exec_term        ; use EXEc retcode if set
  572.     mov    ah,4dh            ; else get program return code
  573.     int    21h
  574. ;
  575. exec_term:
  576.     mov    ah,4ch
  577.     int    21h
  578. ;
  579. ;
  580. exec_expand:
  581.     mov    ah,4ah            ; expand memory
  582.     mov    bx,lmem lcurrdesc.msize
  583.     int    21h
  584.     jnc    exec_memok
  585.     mov    ax,4cffh
  586.     int    21h            ; terminate on error
  587. ;
  588. ;    Swap memory back
  589. ;
  590.     nop
  591. ;
  592. exec_memok:
  593. ;
  594. ;    FALL THROUGH to the appropriate swap-in routine
  595. ;
  596. ;
  597. getmcboff    =    offset get_mcb - offset lowcode_begin
  598. iretoff        =    offset ireti - offset lowcode_begin
  599. doexec_entry    =    offset doexec - offset lowcode_begin
  600. base_length    =    offset $ - offset lowcode_begin
  601. ;
  602. ;-----------------------------------------------------------------------
  603. ;
  604. ;    The various swap in routines follow. Only one of the routines
  605. ;    is copied to low memory.
  606. ;    Note that the routines are never actually called, the EXEC return
  607. ;    code falls through. The final RET thus will return to the restored
  608. ;    memory image.
  609. ;
  610. ;    On entry, DS must point to low core.
  611. ;    On exit to the restored code, DS is unchanged.
  612. ;
  613. ;
  614. ;    swapin_ems:    swap in from EMS.
  615. ;
  616. swapin_ems    proc    far
  617. ;
  618.     xor    bx,bx
  619.     mov    si,ems_parasize
  620.     mov    dx,lmem lprep.handle    ; EMS handle
  621. ;
  622. swinems_main:
  623.     push    ds
  624.     mov    cx,lmem lcurrdesc.swsize    ; block length in paras
  625.     mov    di,lmem lcurrdesc.swoffset    ; swap offset
  626.     mov    es,lmem lcurrdesc.addr        ; segment to swap
  627.     mov    ds,lmem lprep.ems_pageframe    ; page frame address
  628. ;
  629.     mov    ax,ems_parasize        ; max length
  630.     sub    ax,si            ; minus current offset
  631.     jnz    swinems_ok        ; go copy if nonzero
  632. ;
  633. swinems_loop:
  634.     mov    ax,4400h        ; map in next page
  635.     int    EMM_INT
  636.     or    ah,ah
  637.     jnz    swinems_error
  638.     mov    si,0            ; reset offset
  639.     inc    bx            ; bump up page number
  640.     mov    ax,ems_parasize        ; max length to copy
  641. ;
  642. swinems_ok:
  643.     cmp    ax,cx            ; length to copy
  644.     jbe    swinems_doit        ; go do it if <= total length
  645.     mov    ax,cx            ; else use total length
  646. ;
  647. swinems_doit:
  648.     sub    cx,ax            ; subtract copy length from total
  649.     push    cx            ; and save
  650.     push    ax            ; save the copy length in paras
  651.     push    si
  652.     push    di
  653.     mov    cl,3
  654.     shl    ax,cl            ; convert to number of words (!)
  655.     inc    cl
  656.     shl    si,cl            ; convert to byte address
  657.     mov    cx,ax
  658.     rep movsw
  659.     pop    di
  660.     pop    si
  661.     pop    cx            ; copy length in paras
  662.     mov    ax,es
  663.     add    ax,cx            ; add copy length to dest segment
  664.     add    si,cx            ; and EMS page offset
  665.     mov    es,ax
  666.     pop    cx            ; remaining length
  667.     or    cx,cx            ; did we copy everything?
  668.     jnz    swinems_loop        ; go loop if not
  669. ;
  670.     pop    ds
  671.     cmp    lmem lcurrdesc.num_follow,0    ; another MCB?
  672.     je    swinems_complete    ; exit if not
  673. ;
  674. ;    Another MCB follows, read next mcb descriptor into currdesc
  675. ;
  676.     cmp    si,ems_parasize
  677.     jb    swinems_nonewpage    ; no new block needed
  678.     mov    ax,4400h        ; map page, phys = 0
  679.     int    EMM_INT
  680.     or    ah,ah
  681.     jnz    swinems_error1
  682.     mov    si,0
  683.     inc    bx
  684. ;
  685. swinems_nonewpage:
  686.     push    si
  687.     push    ds
  688.     mov    ax,ds
  689.     mov    es,ax
  690.     mov    ds,lmem lprep.ems_pageframe    ; page frame address
  691.     mov    cl,4
  692.     shl    si,cl            ; convert to byte address
  693.     mov    cx,TYPE mcbdesc
  694.     mov    di,lcurrdesc
  695.     rep movsb
  696.     pop    ds
  697.     pop    si
  698.     inc    si            ; one paragraph
  699. ;
  700.     push    bx
  701.     call    lmem cgetmcb
  702.     pop    bx
  703.     jc    swinems_error1
  704.     jmp    swinems_main
  705. ;
  706. swinems_complete:
  707.     mov    ah,45h            ; release EMS pages
  708.     int    EMM_INT
  709.     ret
  710. ;
  711. swinems_error:
  712.     pop    ds
  713. swinems_error1:
  714.     mov    ah,45h            ; release EMS pages on error
  715.     int    EMM_INT
  716.     mov    ax,4cffh
  717.     int    21h            ; terminate
  718. ;
  719. swapin_ems    endp
  720. ;
  721. swinems_length    = offset $ - offset swapin_ems
  722. ;
  723. ;
  724. ;    swapin_xms:    swap in from XMS.
  725. ;
  726. swapin_xms    proc    far
  727. ;
  728.     mov    ax,lmem lprep.handle    ; XMS handle
  729.     mov    lmem lxmsctl.srchnd,ax     ; source is XMS
  730.     mov    lmem lxmsctl.desthnd,0     ; dest is normal memory
  731.     mov    lmem lxmsctl.srclo,0
  732.     mov    lmem lxmsctl.srchi,0
  733. ;
  734. swinxms_main:
  735.     mov    ax,lmem lcurrdesc.swsize ; size in paragraphs
  736.     mov    cl,4
  737.     rol    ax,cl            ; size in bytes + high nibble
  738.     mov    dx,ax
  739.     and    ax,0fff0h        ; low word
  740.     and    dx,0000fh        ; high word
  741.     mov    lmem lxmsctl.lenlo,ax    ; into control block
  742.     mov    lmem lxmsctl.lenhi,dx
  743.     mov    ax,lmem lcurrdesc.swoffset    ; swap offset
  744.     mov    lmem lxmsctl.destlo,ax         ; into control block
  745.     mov    ax,lmem lcurrdesc.addr        ; segment to swap
  746.     mov    lmem lxmsctl.desthi,ax
  747.     mov    si,lxmsctl
  748.     mov    ah,0bh
  749.     call    lmem lprep.xmm        ; move it
  750.     or    ax,ax
  751.     jz    swinxms_error
  752.     mov    ax,lmem lxmsctl.lenlo    ; adjust source addr
  753.     add    lmem lxmsctl.srclo,ax
  754.     mov    ax,lmem lxmsctl.lenhi
  755.     adc    lmem lxmsctl.srchi,ax
  756. ;
  757.     cmp    lmem lcurrdesc.num_follow,0    ; another MCB?
  758.     je    swinxms_complete
  759. ;
  760.     mov    lmem lxmsctl.lenlo,TYPE mcbdesc
  761.     mov    lmem lxmsctl.lenhi,0
  762.     mov    lmem lxmsctl.desthi,ds
  763.     mov    lmem lxmsctl.destlo,lcurrdesc
  764.     mov    si,lxmsctl
  765.     mov    ah,0bh
  766.     call    lmem lprep.xmm        ; move it
  767.     or    ax,ax
  768.     jz    swinxms_error
  769.     add    lmem lxmsctl.srclo,16    ; one paragraph
  770.     adc    lmem lxmsctl.srchi,0
  771. ;
  772.     call    lmem cgetmcb
  773.     jc    swinxms_error
  774.     jmp    swinxms_main
  775. ;
  776. swinxms_complete:
  777.     mov    ah,0ah            ; release XMS frame
  778.     mov    dx,lmem lprep.handle       ; XMS handle
  779.     call    lmem lprep.xmm
  780.     ret
  781. ;
  782. swinxms_error:
  783.     mov    ah,0ah            ; release XMS frame on error
  784.     call    lmem lprep.xmm
  785.     mov    ax,4c00h
  786.     int    21h
  787. ;
  788. swapin_xms    endp
  789. ;
  790. swinxms_length    = offset $ - offset swapin_xms
  791. ;
  792. ;
  793. ;    swapin_file:    swap in from file.
  794. ;
  795. swapin_file    proc    far
  796. ;
  797.     mov    dx,lprep.swapfilename
  798.     mov    ax,3d00h            ; open file
  799.     int    21h
  800.     jc    swinfile_error2
  801.     mov    bx,ax                ; file handle
  802. ;
  803. swinfile_main:
  804.     push    ds
  805.     mov    cx,lmem lcurrdesc.swsize    ; size in paragraphs
  806.     mov    dx,lmem lcurrdesc.swoffset    ; swap offset
  807.     mov    ds,lmem lcurrdesc.addr        ; segment to swap
  808. ;
  809. swinfile_loop:
  810.     mov    ax,cx
  811.     cmp    ah,8h            ; above 32k?
  812.     jbe    swinfile_ok        ; go read if not
  813.     mov    ax,800h            ; else read 32k
  814. ;
  815. swinfile_ok:
  816.     sub    cx,ax            ; remaining length
  817.     push    cx            ; save it
  818.     push    ax            ; and save paras to read
  819.     mov    cl,4
  820.     shl    ax,cl            ; convert to bytes
  821.     mov    cx,ax
  822.     mov    ah,3fh            ; read
  823.     int    21h
  824.     jc    swinfile_error
  825.     cmp    ax,cx
  826.     jne    swinfile_error
  827.     pop    cx            ; paras read
  828.     mov    ax,ds
  829.     add    ax,cx            ; bump up dest segment
  830.     mov    ds,ax
  831.     pop    cx            ; remaining length
  832.     or    cx,cx            ; anything left?
  833.     jnz    swinfile_loop        ; go loop if yes
  834. ;
  835.     pop    ds
  836.     cmp    lmem lcurrdesc.num_follow,0    ; another MCB?
  837.     je    swinfile_complete    ; ready if not
  838.     mov    cx,16            ; read one paragraph
  839.     mov    dx,lcurrdesc
  840.     mov    ah,3fh
  841.     int    21h
  842.     jc    swinfile_error1
  843.     cmp    ax,cx
  844.     jne    swinfile_error1
  845. ;
  846.     push    bx
  847.     call    lmem cgetmcb
  848.     pop    bx
  849.     jc    swinfile_error1
  850.     jmp    swinfile_main
  851. ;
  852. ;
  853. swinfile_complete:
  854.     mov    ah,3eh            ; close file
  855.     int    21h
  856.     mov    dx,lprep.swapfilename
  857.     mov    ah,41h            ; delete file
  858.     int    21h
  859.     ret
  860. ;
  861. swinfile_error:
  862.     pop    cx
  863.     pop    cx
  864.     pop    ds
  865. swinfile_error1:
  866.     mov    ah,3eh            ; close file
  867.     int    21h
  868. swinfile_error2:
  869.     mov    dx,lprep.swapfilename
  870.     mov    ah,41h            ; delete file
  871.     int    21h
  872.     mov    ax,4cffh
  873.     int    21h
  874. ;
  875. swapin_file    endp
  876. ;
  877. swinfile_length    = offset $ - offset swapin_file
  878. ;
  879. ;
  880. ;    swapin_none:    no swap, return immediately.
  881. ;
  882. swapin_none    proc    far
  883. ;
  884.     ret
  885. ;
  886. swapin_none    endp
  887. ;
  888. ;
  889.     IF    swinems_length GT swinxms_length
  890. swcodelen    =    swinems_length
  891.     ELSE
  892. swcodelen    =    swinxms_length
  893.     ENDIF
  894.     IF    swinfile_length GT swcodelen
  895. swcodelen    =    swinfile_length
  896.     ENDIF
  897. ;
  898. swap_codelen    =    ((swcodelen + 1) / 2) * 2
  899. ;
  900. codelen        =    base_length + swap_codelen
  901. reslen        =    codebeg + codelen
  902. keep_paras    =    (reslen + 15) shr 4    ; paragraphs to keep
  903. swapbeg        =    keep_paras shl 4    ; start of swap space
  904. savespace    =    swapbeg - 5ch    ; length of overwritten area
  905. ;
  906. ;--------------------------------------------------------------------
  907. ;
  908.     IFDEF    PASCAL
  909.     .data
  910.     ELSE
  911.     IFDEF    TC_HUGE
  912.     .fardata?    my_data
  913.     ELSE
  914.     .data?
  915.     ENDIF
  916.     ENDIF
  917. ;
  918. ;
  919. ;    Space for saving the part of the memory image below the
  920. ;    swap area that is overwritten by our code.
  921. ;
  922. save_dat    db    savespace dup(?)
  923. ;
  924. ;    Variables used while swapping out.
  925. ;    The "prep" structure is initialized by prep_swap.
  926. ;
  927. prep        prep_block    <>
  928. nextmcb        mcbdesc        <>
  929. currdesc    mcbdesc        <>
  930. xmsctl        xms_control    <>
  931. ems_curpage    dw        ?    ; current EMS page number
  932. ems_curoff    dw        ?    ; current EMS offset (paragraph)
  933. ;
  934. ;--------------------------------------------------------------------
  935. ;       
  936.     .code
  937. ;
  938. ;    swapout_ems:    swap out an MCB block to EMS.
  939. ;
  940. ;    Entry:    "currdesc"     contains description of block to swap
  941. ;        "nextmcb"    contains MCB-descriptor of next block
  942. ;                if currdesc.num_follow is nonzero
  943. ;
  944. ;    Exit:    0 if OK, != 0 if error, Zero-flag set accordingly.
  945. ;
  946. ;    Uses:    All regs excpt DS
  947. ;
  948. swapout_ems    proc    near
  949. ;
  950.     push    ds
  951.     mov    cx,currdesc.swsize    ; block length in paras
  952.     mov    si,currdesc.swoffset    ; swap offset
  953.     mov    dx,prep.handle        ; EMS handle
  954.     mov    bx,ems_curpage        ; current EMS page
  955.     mov    di,ems_curoff        ; current EMS page offset (paras)
  956.     mov    es,prep.ems_pageframe    ; page frame address
  957.     mov    ds,currdesc.addr    ; segment to swap
  958. ;
  959.     mov    ax,ems_parasize        ; max length
  960.     sub    ax,di            ; minus current offset
  961.     jnz    swems_ok        ; go copy if there's room
  962. ;
  963. swems_loop:
  964.     mov    ax,4400h        ; map in next page
  965.     int    EMM_INT
  966.     or    ah,ah
  967.     jnz    swems_error
  968.     mov    di,0            ; reset offset
  969.     inc    bx            ; bump up page number
  970.     mov    ax,ems_parasize        ; max length to copy
  971. ;
  972. swems_ok:
  973.     cmp    ax,cx            ; length to copy
  974.     jbe    swems_doit        ; go do it if <= total length
  975.     mov    ax,cx            ; else use total length
  976. ;
  977. swems_doit:
  978.     sub    cx,ax            ; subtract copy length from total
  979.     push    cx            ; and save
  980.     push    ax            ; save the copy length in paras
  981.     push    si
  982.     push    di
  983.     mov    cl,3
  984.     shl    ax,cl            ; convert to number of words (!)
  985.     inc    cl
  986.     shl    di,cl            ; convert to byte address
  987.     mov    cx,ax
  988.     rep movsw
  989.     pop    di
  990.     pop    si
  991.     pop    cx            ; copy length in paras
  992.     mov    ax,ds
  993.     add    ax,cx            ; add copy length to source segment
  994.     add    di,cx            ; and EMS page offset
  995.     mov    ds,ax
  996.     pop    cx            ; remaining length
  997.     or    cx,cx            ; did we copy everything?
  998.     jnz    swems_loop        ; go loop if not
  999. ;
  1000.     pop    ds
  1001.     cmp    currdesc.num_follow,0    ; another MCB?
  1002.     je    swems_complete        ; exit if not
  1003. ;
  1004. ;    Another MCB follows, append nextmcb to save block.
  1005. ;
  1006.     cmp    di,ems_parasize
  1007.     jb    swems_nonewpage        ; no new block needed
  1008.     mov    ax,4400h        ; map page, phys = 0
  1009.     int    EMM_INT
  1010.     or    ah,ah
  1011.     jnz    swems_error1
  1012.     mov    di,0
  1013.     inc    bx
  1014. ;
  1015. swems_nonewpage:
  1016.     push    di
  1017.     mov    cl,4
  1018.     shl    di,cl            ; convert to byte address
  1019.     mov    cx,TYPE mcbdesc
  1020.     mov    si,offset nextmcb
  1021.     rep movsb
  1022.     pop    di
  1023.     inc    di            ; one paragraph
  1024. ;
  1025. swems_complete:
  1026.     mov    ems_curpage,bx
  1027.     mov    ems_curoff,di
  1028.     xor    ax,ax
  1029.     ret
  1030. ;
  1031. swems_error:
  1032.     pop    ds
  1033. swems_error1:
  1034.     mov    ah,45h            ; release EMS pages on error
  1035.     int    EMM_INT
  1036.     mov    ax,RC_SWAPERROR
  1037.     or    ax,ax
  1038.     ret
  1039. ;
  1040. swapout_ems    endp
  1041. ;
  1042. ;
  1043. ;    swapout_xms:    swap out an MCB block to XMS.
  1044. ;
  1045. ;    Entry:    "currdesc"     contains description of block to swap
  1046. ;        "nextmcb"    contains MCB-descriptor of next block
  1047. ;                if currdesc.num_follow is nonzero
  1048. ;
  1049. ;    Exit:    0 if OK, -1 if error, Zero-flag set accordingly.
  1050. ;
  1051. ;    Uses:    All regs excpt DS
  1052. ;
  1053. swapout_xms    proc    near
  1054. ;
  1055.     mov    ax,currdesc.swsize    ; size in paragraphs
  1056.     mov    cl,4
  1057.     rol    ax,cl            ; size in bytes + high nibble
  1058.     mov    dx,ax
  1059.     and    ax,0fff0h        ; low word
  1060.     and    dx,0000fh        ; high word
  1061.     mov    xmsctl.lenlo,ax        ; into control block
  1062.     mov    xmsctl.lenhi,dx
  1063.     mov    xmsctl.srchnd,0        ; source is normal memory
  1064.     mov    ax,currdesc.swoffset    ; swap offset
  1065.     mov    xmsctl.srclo,ax        ; into control block
  1066.     mov    ax,currdesc.addr    ; segment to swap
  1067.     mov    xmsctl.srchi,ax
  1068.     mov    ax,prep.handle        ; XMS handle
  1069.     mov    xmsctl.desthnd,ax
  1070.     mov    si,offset xmsctl
  1071.     mov    ah,0bh
  1072.     call    prep.xmm        ; move it
  1073.     or    ax,ax
  1074.     jz    swxms_error
  1075.     mov    ax,xmsctl.lenlo        ; adjust destination addr
  1076.     add    xmsctl.destlo,ax
  1077.     mov    ax,xmsctl.lenhi
  1078.     adc    xmsctl.desthi,ax
  1079. ;
  1080.     cmp    currdesc.num_follow,0    ; another MCB?
  1081.     je    swxms_complete
  1082. ;
  1083.     mov    xmsctl.lenlo,TYPE mcbdesc
  1084.     mov    xmsctl.lenhi,0
  1085.     mov    xmsctl.srchi,ds
  1086.     mov    xmsctl.srclo,offset nextmcb
  1087.     mov    si,offset xmsctl
  1088.     mov    ah,0bh
  1089.     call    prep.xmm        ; move it
  1090.     or    ax,ax
  1091.     jz    swxms_error
  1092.     add    xmsctl.destlo,16    ; one paragraph
  1093.     adc    xmsctl.desthi,0
  1094. ;
  1095. swxms_complete:
  1096.     xor    ax,ax
  1097.     ret
  1098. ;
  1099. swxms_error:
  1100.     mov    ah,0ah            ; release XMS frame on error
  1101.     mov    dx,prep.handle        ; XMS handle
  1102.     call    prep.xmm
  1103.     mov    ax,RC_SWAPERROR
  1104.     or    ax,ax
  1105.     ret
  1106. ;
  1107. swapout_xms    endp
  1108. ;
  1109. ;
  1110. ;    swapout_file:    swap out an MCB block to file.
  1111. ;
  1112. ;    Entry:    "currdesc"     contains description of block to swap
  1113. ;        "nextmcb"    contains MCB-descriptor of next block
  1114. ;                if currdesc.num_follow is nonzero
  1115. ;
  1116. ;    Exit:    0 if OK, -1 if error, Zero-flag set accordingly.
  1117. ;
  1118. ;    Uses:    All regs excpt DS
  1119. ;
  1120. swapout_file    proc    near
  1121. ;
  1122.     push    ds
  1123.     mov    cx,currdesc.swsize    ; size in paragraphs
  1124.     mov    bx,prep.handle        ; file handle
  1125.     mov    dx,currdesc.swoffset    ; swap offset
  1126.     mov    ds,currdesc.addr    ; segment to swap
  1127. ;
  1128. swfile_loop:
  1129.     mov    ax,cx
  1130.     cmp    ah,8h            ; above 32k?
  1131.     jbe    swfile_ok        ; go write if not
  1132.     mov    ax,800h            ; else write 32k
  1133. ;
  1134. swfile_ok:
  1135.     sub    cx,ax            ; remaining length
  1136.     push    cx            ; save it
  1137.     push    ax            ; and save paras to write
  1138.     mov    cl,4
  1139.     shl    ax,cl            ; convert to bytes
  1140.     mov    cx,ax
  1141.     mov    ah,40h            ; write
  1142.     int    21h
  1143.     jc    swfile_error
  1144.     cmp    ax,cx
  1145.     jne    swfile_error
  1146.     pop    cx            ; paras written
  1147.     mov    ax,ds
  1148.     add    ax,cx            ; bump up source segment
  1149.     mov    ds,ax
  1150.     pop    cx            ; remaining length
  1151.     or    cx,cx            ; anything left?
  1152.     jnz    swfile_loop        ; go loop if yes
  1153. ;
  1154.     pop    ds
  1155.     cmp    currdesc.num_follow,0    ; another MCB?
  1156.     je    swfile_complete        ; ready if not
  1157.     mov    cx,16            ; write one paragraph
  1158.     mov    dx,offset nextmcb
  1159.     mov    ah,40h
  1160.     int    21h
  1161.     jc    swfile_error1
  1162.     cmp    ax,cx
  1163.     jne    swfile_error1
  1164. ;
  1165. swfile_complete:
  1166.     xor    ax,ax
  1167.     ret
  1168. ;
  1169. swfile_error:
  1170.     pop    cx
  1171.     pop    cx
  1172.     pop    ds
  1173. swfile_error1:
  1174.     mov    ah,3eh            ; close file
  1175.     int    21h
  1176.     mov    dx,offset prep.swapfilename
  1177.     mov    ah,41h            ; delete file
  1178.     int    21h
  1179.     mov    ax,RC_SWAPERROR
  1180.     or    ax,ax
  1181.     ret
  1182. ;
  1183. swapout_file    endp
  1184. ;
  1185. ;--------------------------------------------------------------------------
  1186. ;--------------------------------------------------------------------------
  1187. ;
  1188. ;
  1189.     IFDEF    PASCAL
  1190.     IFDEF    FARCALL
  1191. do_spawn    PROC    far swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
  1192.     ELSE
  1193. do_spawn    PROC    near swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
  1194.     ENDIF
  1195.     ELSE
  1196. do_spawn    PROC    uses si di,swapping: word, execfname:ptr byte,params:ptr byte,envlen:word,envp:ptr byte
  1197.     ENDIF
  1198.     local    datseg,pspseg,currmcb
  1199. ;
  1200.     IFDEF    TC_HUGE
  1201.     mov    ax,SEG my_data
  1202.     mov    ds,ax
  1203.     ENDIF
  1204. ;
  1205.     mov    datseg,ds        ; save default DS
  1206. ;
  1207.     IFDEF    PASCAL
  1208.     cld
  1209.     mov    bx,prefixseg
  1210.     ELSE
  1211.     IFDEF    TC_HUGE
  1212.     mov    ax,SEG _psp
  1213.     mov    es,ax
  1214.     mov    bx,es:_psp
  1215.     ELSE
  1216.     mov    bx,_psp
  1217.     ENDIF
  1218.     ENDIF
  1219.     mov    pspseg,bx
  1220. ;
  1221. ;
  1222. ;    Check if spawn is too low in memory
  1223. ;
  1224.     mov    ax,cs
  1225.     mov    dx,offset lowcode_begin
  1226.     mov    cl,4
  1227.     shr    dx,cl
  1228.     add    ax,dx            ; normalized start of this code
  1229.     mov    dx,keep_paras        ; the end of the modified area
  1230.     add    dx,bx            ; plus PSP = end paragraph
  1231.     cmp    ax,dx
  1232.     ja    doswap_ok    ; ok if start of code > end of low mem
  1233.     mov    ax,RC_TOOLOW
  1234.     ret
  1235. ;
  1236. doswap_ok:
  1237.     cmp    swapping,0
  1238.     jle    method_ok
  1239. ;
  1240. ;    check the swap method, to make sure prep_swap has been called
  1241. ;
  1242.     mov    al,prep.swapmethod
  1243.     cmp    al,USE_EMS
  1244.     je    method_ok
  1245.     cmp    al,USE_XMS
  1246.     je    method_ok
  1247.     cmp    al,USE_FILE
  1248.     je    method_ok
  1249.     mov    ax,RC_BADPREP
  1250.     ret
  1251. ;
  1252. ;    Save the memory below the swap space.
  1253. ;    We must do this before swapping, so the saved memory is
  1254. ;    in the swapped out image.
  1255. ;    Anything else we'd want to save on the stack or anywhere
  1256. ;    else in "normal" memory also has to be saved here, any
  1257. ;    modifications done to memory after the swap will be lost.
  1258. ;
  1259. ;    Note that the memory save is done even when not swapping,
  1260. ;    because we use some of the variables in low core for
  1261. ;    simplicity.
  1262. ;
  1263. method_ok:
  1264.     push    ds
  1265.     pop    es
  1266.     push    ds
  1267.     mov    ds,pspseg        ; DS points to PSP
  1268.     mov    si,5ch
  1269.     mov    di,offset save_dat
  1270.     mov    cx,savespace / 2    ; NOTE: savespace is always even
  1271.     rep movsw
  1272.     pop    ds
  1273. ;
  1274.     mov    ax,swapping
  1275.     cmp    ax,0
  1276.     jg    begin_swap
  1277. ;
  1278. ;    not swapping, prep_swap wasn't called. Init those variables in
  1279. ;      the 'prep' block we need in any case.
  1280. ;
  1281.     mov    prep.swapmethod,al
  1282.     je    no_reduce
  1283. ;
  1284.     mov    ax,pspseg
  1285.     dec    ax
  1286.     mov    prep.psp_mcb,ax
  1287.     mov    prep.first_mcb,ax
  1288.     inc    ax
  1289.     mov    es,ax
  1290.     mov    bx,es:psp_envptr
  1291.     mov    prep.env_mcb,bx
  1292.     mov    prep.noswap_mcb,0
  1293.     cmp    envlen,0
  1294.     jne    swp_can_swap_env
  1295.     mov    prep.noswap_mcb,bx
  1296. ;
  1297. swp_can_swap_env:
  1298.     xor    bx,bx
  1299.     mov    es,bx
  1300.     mov    ah,52h            ; get list of lists
  1301.     int    21h
  1302.     mov    ax,es
  1303.     or    ax,bx
  1304.     jz    no_reduce
  1305.     mov    es,es:[bx-2]        ; first MCB
  1306.     cmp    es:id,4dh        ; normal ID?
  1307.     jne    no_reduce
  1308.     mov    prep.first_mcb,es
  1309. ;
  1310. no_reduce:
  1311.     jmp    no_swap1
  1312. ;
  1313. ;    set up first block descriptor
  1314. ;
  1315. begin_swap:
  1316.     mov    ax,prep.first_mcb
  1317.     mov    currmcb,ax
  1318.     mov    es,prep.psp_mcb        ; let ES point to base MCB
  1319.     mov    ax,es:paras
  1320.     mov    currdesc.msize,ax
  1321.     sub    ax,keep_paras
  1322.     mov    currdesc.swsize,ax
  1323.     mov    currdesc.addr,es
  1324.     mov    currdesc.swoffset,swapbeg + 16
  1325. ;        NOTE: swapbeg is 1 para higher when seen from MCB
  1326.     mov    ax,prep.total_mcbs
  1327.     mov    currdesc.num_follow,ax
  1328. ;
  1329. ;    init other vars
  1330. ;
  1331.     mov    xmsctl.destlo,0
  1332.     mov    xmsctl.desthi,0
  1333.     mov    ems_curpage,0
  1334.     mov    ems_curoff,ems_parasize
  1335. ;
  1336. ;    Do the swapping. Each MCB block (except the last) has an 
  1337. ;    "mcbdesc" structure appended that gives location and size 
  1338. ;    of the next MCB.
  1339. ;
  1340. swapout_main:
  1341.     cmp    currdesc.num_follow,0    ; next block?
  1342.     je    swapout_no_next        ; ok if not
  1343. ;
  1344. ;    There is another MCB block to be saved. So we don't have
  1345. ;    to do two calls to the save routine with complicated
  1346. ;    parameters, we set up the next MCB descriptor beforehand.
  1347. ;    Walk the MCB chain starting at the current MCB to find
  1348. ;    the next one belonging to this process.
  1349. ;
  1350.     mov    ax,currmcb
  1351.     mov    bx,pspseg
  1352.     mov    cx,prep.psp_mcb
  1353.     mov    dx,prep.noswap_mcb
  1354. ;
  1355. swm_mcb_walk:
  1356.     mov    es,ax
  1357.     cmp    ax,cx
  1358.     je    swm_next_mcb
  1359.     cmp    ax,dx
  1360.     je    swm_next_mcb
  1361. ;
  1362.     cmp    bx,es:owner        ; our process?
  1363.     je    swm_mcb_found        ; found it if yes
  1364. ;
  1365. swm_next_mcb:
  1366.     cmp    es:id,4dh        ; normal block?
  1367.     jne    swm_mcb_error        ; error if end of chain
  1368.     add    ax,es:paras        ; start + length
  1369.     inc    ax            ; next MCB
  1370.     jmp    swm_mcb_walk
  1371. ;
  1372. ;    MCB found, set up an mcbdesc in the "nextmcb" structure
  1373. ;
  1374. swm_mcb_found:
  1375.     mov    nextmcb.addr,es
  1376.     mov    ax,es:paras        ; get number of paragraphs
  1377.     mov    nextmcb.msize,ax    ; and save
  1378.     inc    ax
  1379.     mov    nextmcb.swsize,ax
  1380.     mov    bx,es
  1381.     add    bx,ax
  1382.     mov    currmcb,bx
  1383.     mov    nextmcb.swoffset,0
  1384.     mov    ax,currdesc.num_follow
  1385.     dec    ax
  1386.     mov    nextmcb.num_follow,ax
  1387. ;
  1388. swapout_no_next:
  1389.     cmp    prep.swapmethod,USE_EMS
  1390.     je    swm_ems
  1391.     cmp    prep.swapmethod,USE_XMS
  1392.     je    swm_xms
  1393.     call    swapout_file
  1394.     jmp    short swm_next
  1395. ;
  1396. swm_ems:
  1397.     call    swapout_ems
  1398.     jmp    short swm_next
  1399. ;
  1400. swm_xms:
  1401.     call    swapout_xms
  1402. ;
  1403. swm_next:
  1404.     jnz    swapout_error
  1405.     cmp    currdesc.num_follow,0
  1406.     je    swapout_complete
  1407. ;
  1408. ;    next MCB exists, copy the "nextmcb" descriptor into
  1409. ;    currdesc, and loop.
  1410. ;
  1411.     mov    es,datseg
  1412.     mov    si,offset nextmcb
  1413.     mov    di,offset currdesc
  1414.     mov    cx,TYPE mcbdesc
  1415.     rep movsb
  1416.     jmp    swapout_main
  1417. ;
  1418. ;
  1419. swm_mcb_error:
  1420.     cmp    prep.swapmethod,USE_FILE
  1421.     je    swm_mcberr_file
  1422.     cmp    prep.swapmethod,USE_EMS
  1423.     je    swm_mcberr_ems
  1424. ;
  1425.     mov    ah,0ah            ; release XMS frame on error
  1426.     mov    dx,prep.handle        ; XMS handle
  1427.     call    prep.xmm
  1428.     mov    ax,RC_MCBERROR
  1429.     jmp    short swapout_error
  1430. ;
  1431. swm_mcberr_ems:
  1432.     mov    dx,prep.handle        ; EMS handle
  1433.     mov    ah,45h            ; release EMS pages on error
  1434.     int    EMM_INT
  1435.     mov    ax,RC_MCBERROR
  1436.     jmp    short swapout_error
  1437. ;
  1438. swm_mcberr_file:
  1439.     mov    ah,3eh            ; close file
  1440.     mov    bx,prep.handle
  1441.     int    21h
  1442.     mov    dx,offset prep.swapfilename
  1443.     mov    ah,41h            ; delete file
  1444.     int    21h
  1445.     mov    ax,RC_MCBERROR
  1446. ;
  1447. swapout_error:
  1448.     ret
  1449. ;
  1450. ;
  1451. ;    Swapout complete. Close the handle (EMS/file only),
  1452. ;    then set up low memory.
  1453. ;
  1454. swapout_complete:
  1455.     cmp    prep.swapmethod,USE_FILE
  1456.     jne    swoc_nofile
  1457. ;
  1458. ;    File swap: Close the swap file to make the handle available
  1459. ;
  1460.     mov    bx,prep.handle
  1461.     mov    ah,3eh
  1462.     int    21h            ; close file
  1463.     mov    si,offset swapin_file
  1464.     jnc    swoc_ready
  1465.     mov    ax,RC_SWAPERROR
  1466.     jmp    swapout_error
  1467. ;
  1468. swoc_nofile:
  1469.     cmp    prep.swapmethod,USE_EMS
  1470.     jne    swoc_xms
  1471. ;
  1472. ;    EMS: Unmap page
  1473. ;
  1474.     mov    ax,4400h
  1475.     mov    bx,-1
  1476.     mov    dx,prep.handle
  1477.     int    EMM_INT
  1478.     mov    si,offset swapin_ems
  1479.     jmp    short swoc_ready
  1480. ;
  1481. swoc_xms:
  1482.     mov    si,offset swapin_xms
  1483.     jmp    short swoc_ready
  1484. ;
  1485. no_swap1:
  1486.     mov    si,offset swapin_none
  1487. ;    
  1488. ;    Copy the appropriate swap-in routine to low memory.
  1489. ;
  1490. swoc_ready:
  1491.     mov    es,pspseg
  1492.     mov    cx,swap_codelen / 2
  1493.     mov    di,codebeg + base_length
  1494.     push    ds
  1495.     mov    ax,cs
  1496.     mov    ds,ax
  1497.     rep movsw
  1498. ;
  1499. ;    And while we're at it, copy the MCB allocation routine (which
  1500. ;    also includes the initial MCB release and exec call) down.
  1501. ;
  1502.     mov    cx,base_length / 2
  1503.     mov    di,param_len
  1504.     mov    si,offset lowcode_begin
  1505.     rep movsw
  1506. ;
  1507.     pop    ds
  1508.     mov    bx,es
  1509.     dec    bx
  1510.     mov    es,bx        ; let ES point to base MCB
  1511. ;
  1512. ;    Again set up the base MCB descriptor, and copy it as well as
  1513. ;    the variables set up by prep_swap to low memory.
  1514. ;    This isn't too useful if we're not swapping, but it doesn't
  1515. ;    hurt, either. The only variable used when not swapping is
  1516. ;    lprep.swapmethod.
  1517. ;
  1518.     mov    ax,es:paras
  1519.     mov    currdesc.msize,ax
  1520.     sub    ax,keep_paras
  1521.     mov    currdesc.swsize,ax
  1522.     mov    currdesc.addr,es
  1523.     mov    currdesc.swoffset,swapbeg + 16
  1524.     mov    ax,prep.total_mcbs
  1525.     mov    currdesc.num_follow,ax
  1526. ;
  1527.     mov    es,pspseg        ; ES points to PSP again
  1528. ;
  1529.     mov    cx,TYPE prep_block
  1530.     mov    si,offset prep
  1531.     mov    di,lprep
  1532.     rep movsb
  1533.     mov    cx,TYPE mcbdesc
  1534.     mov    si,offset currdesc
  1535.     mov    di,lcurrdesc
  1536.     rep movsb
  1537. ;
  1538. ;    now set up other variables in low core
  1539. ;
  1540.     mov    es:cgetmcb,getmcboff + codebeg
  1541.     mov    es:eretcode,0
  1542.     mov    es:retflags,0
  1543. ;
  1544. ;    Prepare exec parameter block
  1545. ;
  1546.     mov    ax,es
  1547.     mov    es:expar.fcb1seg,ax
  1548.     mov    es:expar.fcb2seg,ax
  1549.     mov    es:expar.pparseg,ax
  1550.     mov    es:expar.envseg,0
  1551. ;
  1552. ;    The 'zero' word is located at 80h in the PSP, the start of
  1553. ;    the command line. So as not to confuse MCB walking programs,
  1554. ;    a command line length of zero is inserted here.
  1555. ;
  1556.     mov    es:zero,0d00h        ; 00h,0dh = empty command line
  1557. ;
  1558. ;    Init default fcb's by parsing parameter string
  1559. ;
  1560.     IF    ptrsize
  1561.     lds    si,params
  1562.     ELSE
  1563.     mov    si,params
  1564.     ENDIF
  1565.     IFDEF    PASCAL
  1566.     inc    si            ; skip length byte
  1567.     ENDIF
  1568.     push    si
  1569.     mov    di,xfcb1
  1570.     mov    es:expar.fcb1,di
  1571.     push    di
  1572.     mov    cx,16
  1573.     xor    ax,ax
  1574.     rep stosw            ; init both fcb's to 0
  1575.     pop    di
  1576.     mov    ax,2901h
  1577.     int    21h
  1578.     mov    di,xfcb2
  1579.     mov    es:expar.fcb2,di
  1580.     mov    ax,2901h
  1581.     int    21h
  1582.     pop    si
  1583. ;
  1584. ;    move command tail string into low core
  1585. ;
  1586.     mov    di,progpars
  1587.     mov    es:expar.ppar,di
  1588.     xor    cx,cx
  1589.     inc    di
  1590. cmdcpy:
  1591.     lodsb
  1592.     or    al,al
  1593.     jz    cmdcpy_end
  1594.     stosb
  1595.     inc    cx
  1596.     jmp    cmdcpy
  1597. ;
  1598. cmdcpy_end:
  1599.     mov    al,0dh
  1600.     stosb
  1601.     mov    es:progpars,cl
  1602. ;
  1603. ;    move filename string into low core
  1604. ;
  1605.     IF    ptrsize
  1606.     lds    si,execfname
  1607.     ELSE
  1608.     mov    si,execfname
  1609.     ENDIF
  1610.     IFDEF    PASCAL
  1611.     inc    si
  1612.     ENDIF
  1613.     mov    di,filename
  1614. fncpy:
  1615.     lodsb
  1616.     stosb
  1617.     or    al,al
  1618.     jnz    fncpy
  1619. ;
  1620. ;    Setup environment copy
  1621. ;
  1622.     mov    bx,keep_paras        ; paras to keep
  1623.     mov    cx,envlen        ; environment size
  1624.     jcxz    no_environ        ; go jump if no environment
  1625.     cmp    swapping,0
  1626.     jne    do_envcopy
  1627. ;
  1628. ;    Not swapping, use the environment pointer directly.
  1629. ;    Note that the environment copy must be paragraph aligned.
  1630. ;
  1631.     IF    ptrsize
  1632.     mov    ax,word ptr (envp)+2
  1633.     mov    bx,word ptr (envp)
  1634.     ELSE
  1635.     mov    ax,ds
  1636.     mov    bx,envp
  1637.     ENDIF
  1638.     add    bx,15            ; make sure it's paragraph aligned
  1639.     mov    cl,4
  1640.     shr    bx,cl            ; and convert to segment addr
  1641.     add    ax,bx
  1642.     mov    es:expar.envseg,ax    ; new environment segment
  1643.     xor    cx,cx            ; mark no copy
  1644.     xor    bx,bx            ; and no shrink
  1645.     jmp    short no_environ
  1646. ;
  1647. ;    Swapping or EXECing without return. Set up the pointers for
  1648. ;    an environment copy (we can't do the copy yet, it might overwrite
  1649. ;    this code).
  1650. ;
  1651. do_envcopy:
  1652.     inc    cx
  1653.     shr    cx,1            ; words to copy
  1654.     mov    ax,cx            ; convert envsize to paras
  1655.     add    ax,7
  1656.     shr    ax,1
  1657.     shr    ax,1
  1658.     shr    ax,1
  1659.     add    bx,ax            ; add envsize to paras to keep
  1660.     IF    ptrsize
  1661.     lds    si,envp
  1662.     ELSE
  1663.     mov    si,envp
  1664.     ENDIF
  1665. ;
  1666.     mov    ax,es            ; low core segment
  1667.     add    ax,keep_paras        ; plus fixed paras
  1668.     mov    es:expar.envseg,ax    ; = new environment segment
  1669. ;
  1670. ;    Save stack regs, switch to local stack
  1671. ;
  1672. no_environ:
  1673.     mov    es:save_ss,ss
  1674.     mov    es:save_sp,sp
  1675.     mov    ax,es
  1676.     mov    ss,ax
  1677.     mov    sp,mystack
  1678. ;
  1679.     push    cx            ; save env length
  1680.     push    si            ; save env pointer
  1681.     push    ds            ; save env segment
  1682. ;
  1683. ;    save and patch INT0 (division by zero) vector
  1684. ;
  1685.     xor    ax,ax
  1686.     mov    ds,ax
  1687.     mov    ax,word ptr ds:0
  1688.     mov    es:div0_off,ax
  1689.     mov    ax,word ptr ds:2
  1690.     mov    es:div0_seg,ax
  1691.     mov    word ptr ds:0,codebeg + iretoff
  1692.     mov    word ptr ds:2,es
  1693. ;
  1694.     pop    ds            ; pop environment segment
  1695.     pop    si            ; pop environment offset
  1696.     pop    cx            ; pop environment length
  1697.     mov    di,swapbeg        ; environment destination
  1698. ;
  1699. ;    Push return address on local stack
  1700. ;
  1701.     push    cs            ; push return segment
  1702.     mov    ax,offset exec_cont
  1703.     push    ax            ; push return offset
  1704.     mov    es:spx,sp        ; save stack pointer
  1705. ;
  1706. ;    Goto low core code
  1707. ;
  1708.     push    es            ; push entry segment
  1709.         mov    ax,codebeg + doexec_entry
  1710.         push    ax            ; push entry offset
  1711. ;    ret    far            ; can't use RET here because
  1712.     db    0cbh            ; of .model
  1713. ;
  1714. ;----------------------------------------------------------------
  1715. ;
  1716. ;    Low core code will return to this location, with DS set to
  1717. ;    the PSP segment.
  1718. ;
  1719. exec_cont:
  1720.     push    ds
  1721.     pop    es
  1722.     mov    ss,ds:save_ss        ; reload stack
  1723.     mov    sp,ds:save_sp
  1724. ;
  1725. ;    restore INT0 (division by zero) vector
  1726. ;
  1727.     xor    cx,cx
  1728.     mov    ds,cx
  1729.     mov    cx,es:div0_off
  1730.     mov    word ptr ds:0,cx
  1731.     mov    cx,es:div0_seg
  1732.     mov    word ptr ds:2,cx
  1733. ;
  1734.     mov    ax,es:eretcode
  1735.     mov    bx,es:retflags
  1736.     mov    ds,datseg
  1737. ;
  1738. ;    Restore overwritten part of program
  1739. ;
  1740.     mov    si,offset save_dat
  1741.     mov    di,5ch
  1742.     mov    cx,savespace
  1743.     rep movsb
  1744. ;
  1745.     test    bx,1            ; carry set?
  1746.     jnz    exec_fault        ; return EXEC error code if fault
  1747.     mov    ah,4dh            ; else get program return code
  1748.     int    21h
  1749.     ret
  1750. ;
  1751. exec_fault:
  1752.     mov    ah,3            ; return error as 03xx
  1753.     ret
  1754. ;    
  1755. do_spawn    ENDP
  1756. ;
  1757. ;----------------------------------------------------------------------------
  1758. ;----------------------------------------------------------------------------
  1759. ;
  1760. emm_name    db    'EMMXXXX0'
  1761. ;
  1762. ;    prep_swap - prepare for swapping.
  1763. ;
  1764. ;    This routine checks all parameters necessary for swapping,
  1765. ;    and attempts to set up the swap-out area in EMS/XMS, or on file.
  1766. ;    In detail:
  1767. ;
  1768. ;         1) Check whether the do_spawn routine is located
  1769. ;        too low in memory, so it would get overwritten.
  1770. ;        If this is true, return an error code (-2).
  1771. ;
  1772. ;         2) Walk the memory control block chain, adding up the
  1773. ;        paragraphs in all blocks assigned to this process.
  1774. ;
  1775. ;         3) Check EMS (if the method parameter allows EMS):
  1776. ;        - is an EMS driver installed?
  1777. ;        - are sufficient EMS pages available?
  1778. ;        if all goes well, the EMS pages are allocated, and the
  1779. ;        routine returns success (1).
  1780. ;
  1781. ;         4) Check XMS (if the method parameter allows XMS):
  1782. ;        - is an XMS driver installed?
  1783. ;        - is a sufficient XMS block available?
  1784. ;        if all goes well, the XMS block is allocated, and the
  1785. ;        routine returns success (2).
  1786. ;
  1787. ;         5) Check file swap (if the method parameter allows it):
  1788. ;        - try to create the file
  1789. ;        - pre-allocate the file space needed by seeking to the end
  1790. ;          and writing a byte.
  1791. ;        If the file can be written, the routine returns success (4).
  1792. ;
  1793. ;         6) Return an error code (-1).
  1794. ;
  1795.     IFDEF    PASCAL
  1796.     IFDEF    FARCALL
  1797. prep_swap    PROC    far pmethod: word, swapfname: dword
  1798.     ELSE
  1799. prep_swap    PROC    near pmethod: word, swapfname: dword
  1800.     ENDIF
  1801.     ELSE
  1802. prep_swap    PROC    uses si di,pmethod:word,swapfname:ptr byte
  1803.     ENDIF
  1804.     LOCAL    totparas: word
  1805. ;
  1806.     IFDEF    TC_HUGE
  1807.     mov    ax,SEG my_data
  1808.     mov    ds,ax
  1809.     ENDIF
  1810. ;
  1811.     IFDEF    PASCAL
  1812.     cld
  1813.     mov    ax,prefixseg
  1814.     ELSE
  1815.     IFDEF    TC_HUGE
  1816.     mov    ax,SEG _psp
  1817.     mov    es,ax
  1818.     mov    ax,es:_psp
  1819.     ELSE
  1820.     mov    ax,_psp
  1821.     ENDIF
  1822.     ENDIF
  1823. ;
  1824.     dec    ax
  1825.     mov    prep.psp_mcb,ax
  1826.     mov    prep.first_mcb,ax    ; init first MCB to PSP
  1827. ;
  1828. ;    Make a copy of the environment pointer in the PSP
  1829. ;
  1830.     inc    ax
  1831.     mov    es,ax
  1832.     mov    bx,es:psp_envptr
  1833.     dec    bx
  1834.     mov    prep.env_mcb,bx
  1835.     mov    prep.noswap_mcb,0
  1836.     test    pmethod,DONT_SWAP_ENV
  1837.     jz    can_swap_env
  1838.     mov    prep.noswap_mcb,bx
  1839. ;
  1840. ;    Check if spawn is too low in memory
  1841. ;
  1842. can_swap_env:
  1843.     mov    bx,cs
  1844.     mov    dx,offset lowcode_begin
  1845.     mov    cl,4
  1846.     shr    dx,cl
  1847.     add    bx,dx            ; normalized start of this code
  1848.     mov    dx,keep_paras        ; the end of the modified area
  1849.     add    dx,ax            ; plus PSP = end paragraph
  1850.     cmp    bx,dx
  1851.     ja    prepswap_ok    ; ok if start of code > end of low mem
  1852.     mov    ax,-2
  1853.     mov    prep.swapmethod,al
  1854.     ret
  1855. ;
  1856. ;    Walk the chain of memory blocks, adding up the paragraphs
  1857. ;    in all blocks belonging to this process.
  1858. ;    We try to find the first MCB by getting DOS's "list of lists",
  1859. ;    and fetching the word at offset -2 of the returned address.
  1860. ;    If this fails, we use our PSP as the starting point.
  1861. ;
  1862. prepswap_ok:
  1863.     xor    bx,bx
  1864.     mov    es,bx
  1865.     mov    ah,52h            ; get list of lists
  1866.     int    21h
  1867.     mov    ax,es
  1868.     or    ax,bx
  1869.     jz    prep_no_first
  1870.     mov    es,es:[bx-2]        ; first MCB
  1871.     cmp    es:id,4dh        ; normal ID?
  1872.     jne    prep_no_first
  1873.     mov    prep.first_mcb,es
  1874. ;
  1875. prep_no_first:
  1876.     mov    es,prep.psp_mcb        ; ES points to base MCB
  1877.     mov    cx,es            ; save this value
  1878.     mov    bx,es:owner        ; the current process
  1879.     mov    dx,es:paras        ; memory size in the base block
  1880.     sub    dx,keep_paras        ; minus resident paragraphs
  1881.     mov    si,0            ; number of MCBs except base
  1882.     mov    di,prep.noswap_mcb
  1883.     mov    ax,prep.first_mcb
  1884.     mov    prep.first_mcb,0
  1885. ;
  1886. prep_mcb_walk:
  1887.     mov    es,ax
  1888.     cmp    ax,cx            ; base block?
  1889.     je    prep_walk_next        ; then don't count again
  1890.     cmp    ax,di            ; Non-swap MCB?
  1891.     je    prep_walk_next        ; then don't count
  1892. ;
  1893.     cmp    bx,es:owner        ; our process?
  1894.     jne    prep_walk_next        ; next if not
  1895.     inc    si
  1896.     mov    ax,es:paras        ; else get number of paragraphs
  1897.     add    ax,2            ; + 1 for descriptor + 1 for MCB
  1898.     add    dx,ax            ; total number of paras
  1899.     cmp    prep.first_mcb,0
  1900.     jne    prep_walk_next
  1901.     mov    prep.first_mcb,es
  1902. ;
  1903. prep_walk_next:
  1904.     cmp    es:id,4dh        ; normal block?
  1905.     jne    prep_mcb_ready        ; ready if end of chain
  1906.     mov    ax,es
  1907.     add    ax,es:paras        ; start + length
  1908.     inc    ax            ; next MCB
  1909.     jmp    prep_mcb_walk
  1910. ;
  1911. prep_mcb_ready:
  1912.     mov    totparas,dx
  1913.     mov    prep.total_mcbs,si
  1914. ;
  1915.     test    pmethod,XMS_FIRST
  1916.     jnz    check_xms
  1917. ;
  1918. ;    Check for EMS swap
  1919. ;
  1920. check_ems:
  1921.     test    pmethod,USE_EMS
  1922.     jz    prep_no_ems
  1923. ;
  1924.     push    ds
  1925.     mov    al,EMM_INT
  1926.     mov    ah,35h
  1927.     int    21h            ; get EMM int vector
  1928.     mov    ax,cs
  1929.     mov    ds,ax
  1930.     mov    si,offset emm_name
  1931.     mov    di,10
  1932.     mov    cx,8
  1933.     repz cmpsb            ; EMM name present?
  1934.     pop    ds
  1935.     jnz    prep_no_ems
  1936. ;
  1937.     mov    ah,40h            ; get EMS status
  1938.     int    EMM_INT
  1939.     or    ah,ah            ; EMS ok?
  1940.     jnz    prep_no_ems
  1941. ;
  1942.     mov    ah,46h            ; get EMS version
  1943.     int    EMM_INT
  1944.     or    ah,ah            ; AH must be 0
  1945.     jnz    prep_no_ems
  1946. ;
  1947.     cmp    al,30h            ; >= version 3.0?
  1948.     jb    prep_no_ems
  1949. ;
  1950.     mov    ah,41h            ; Get page frame address
  1951.     int    EMM_INT
  1952.     or    ah,ah
  1953.     jnz    prep_no_ems
  1954. ;
  1955. ;    EMS present, try to allocate pages
  1956. ;
  1957.     mov    prep.ems_pageframe,bx
  1958.     mov    bx,totparas
  1959.     add    bx,ems_paramask
  1960.     mov    cl,ems_shift
  1961.     shr    bx,cl
  1962.     mov    ah,43h            ; allocate handle and pages
  1963.     int    EMM_INT
  1964.     or    ah,ah            ; success?
  1965.     jnz    prep_no_ems
  1966. ;
  1967. ;    EMS pages allocated, swap to EMS
  1968. ;
  1969.     mov    prep.handle,dx
  1970.     mov    ax,USE_EMS
  1971.     mov    prep.swapmethod,al
  1972.     ret
  1973. ;
  1974. ;    No EMS allowed, or EMS not present/full. Try XMS.
  1975. ;
  1976. prep_no_ems:
  1977.     test    pmethod,XMS_FIRST
  1978.     jnz    check_file        ; don't try again
  1979. ;
  1980. check_xms:
  1981.     test    pmethod,USE_XMS
  1982.     jz    prep_no_xms
  1983. ;
  1984.     mov    ax,4300h        ; check if XMM driver present
  1985.     int    2fh
  1986.     cmp    al,80h            ; is XMM installed?
  1987.     jne    prep_no_xms
  1988.     mov    ax,4310h        ; get XMM entrypoint
  1989.     int    2fh
  1990.     mov    word ptr prep.xmm,bx    ; save entry address
  1991.     mov    word ptr prep.xmm+2,es
  1992. ;
  1993.     mov    dx,totparas
  1994.     add    dx,xms_paramask        ; round to nearest multiple of 1k
  1995.     mov    cl,xms_shift
  1996.     shr    dx,cl            ; convert to k
  1997.     mov    ah,9            ; allocate extended memory block
  1998.     call    prep.xmm
  1999.     or    ax,ax
  2000.     jz    prep_no_xms
  2001. ;
  2002. ;    XMS block allocated, swap to XMS
  2003. ;
  2004.     mov    prep.handle,dx
  2005.     mov    ax,USE_XMS
  2006.     mov    prep.swapmethod,al
  2007.     ret
  2008. ;
  2009. ;    No XMS allowed, or XMS not present/full. Try File swap.
  2010. ;
  2011. prep_no_xms:
  2012.     test    pmethod,XMS_FIRST
  2013.     jz    check_file
  2014.     jmp    check_ems
  2015. ;
  2016. check_file:
  2017.     test    pmethod,USE_FILE
  2018.     jnz    prep_do_file
  2019.     jmp    prep_no_file
  2020. ;
  2021. prep_do_file:
  2022.     push    ds
  2023.     IF    ptrsize
  2024.     lds    dx,swapfname
  2025.     ELSE
  2026.     mov    dx,swapfname
  2027.     ENDIF
  2028.     IFDEF    PASCAL
  2029.     inc    dx            ; skip length byte
  2030.     ENDIF
  2031.     mov    cx,2            ; hidden attribute
  2032.     test    pmethod,HIDE_FILE
  2033.     jnz    prep_hide
  2034.     xor    cx,cx            ; normal attribute
  2035. ;
  2036. prep_hide:
  2037.     mov    ah,3ch            ; create file
  2038.     test    pmethod,CREAT_TEMP
  2039.     jz    prep_no_temp
  2040.     mov    ah,5ah
  2041. ;
  2042. prep_no_temp:
  2043.     int    21h            ; create/create temp
  2044.     jnc    prep_got_file
  2045.     jmp    prep_no_file
  2046. ;
  2047. prep_got_file:
  2048.     mov    bx,ax            ; handle
  2049. ;
  2050. ;    save the file name
  2051. ;
  2052.     pop    es
  2053.     push    es
  2054.     mov    di,offset prep.swapfilename
  2055.     mov    cx,81
  2056.     mov    si,dx
  2057.     rep movsb
  2058. ;
  2059.     pop    ds
  2060.     mov    prep.handle,bx
  2061. ;
  2062. ;    preallocate the file
  2063. ;
  2064.     test    pmethod,NO_PREALLOC
  2065.     jnz    prep_noprealloc
  2066.     test    pmethod,CHECK_NET
  2067.     jz    prep_nonetcheck
  2068. ;
  2069. ;    check whether file is on a network drive, and don't preallocate
  2070. ;    if so. preallocation can slow down swapping significantly when
  2071. ;    running on certain networks (Novell)
  2072. ;
  2073.     mov    ax,440ah    ; check if handle is remote
  2074.     int    21h
  2075.     jc    prep_nonetcheck    ; assume not remote if function fails
  2076.     test    dh,80h        ; DX bit 15 set ?
  2077.     jnz    prep_noprealloc    ; remote if yes
  2078. ;
  2079. prep_nonetcheck:
  2080.     mov    dx,totparas
  2081.     mov    cl,4
  2082.     rol    dx,cl
  2083.     mov    cx,dx
  2084.     and    dx,0fff0h
  2085.     and    cx,0000fh
  2086.     sub    dx,1
  2087.     sbb    cx,0
  2088.     mov    si,dx            ; save
  2089.     mov    ax,4200h        ; move file pointer, absolute
  2090.     int    21h
  2091.     jc    prep_file_err
  2092.     cmp    dx,cx
  2093.     jne    prep_file_err
  2094.     cmp    ax,si
  2095.     jne    prep_file_err
  2096.     mov    cx,1            ; write 1 byte
  2097.     mov    ah,40h
  2098.     int    21h
  2099.     jc    prep_file_err
  2100.     cmp    ax,cx
  2101.     jne    prep_file_err
  2102. ;
  2103.     mov    ax,4200h        ; move file pointer, absolute
  2104.     xor    dx,dx
  2105.     xor    cx,cx            ; rewind to beginning
  2106.     int    21h
  2107.     jc    prep_file_err
  2108. ;
  2109. prep_noprealloc:
  2110.     mov    ax,USE_FILE
  2111.     mov    prep.swapmethod,al
  2112.     ret
  2113. ;
  2114. prep_file_err:
  2115.     mov    ah,3eh            ; close file
  2116.     int    21h
  2117.     mov    dx,offset prep.swapfilename
  2118.     mov    ah,41h            ; delete file
  2119.     int    21h
  2120. ;
  2121. prep_no_file:
  2122.     mov    ax,-1
  2123.     mov    prep.swapmethod,al
  2124.     ret
  2125. ;
  2126. prep_swap    endp
  2127. ;
  2128.     end
  2129.  
  2130.