home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 2 BBS / 02-BBS.zip / BSRC_250.LZH / SPAWN.ASM < prev    next >
Assembly Source File  |  1991-08-04  |  47KB  |  2,087 lines

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