home *** CD-ROM | disk | FTP | other *** search
/ ftp.wwiv.com / ftp.wwiv.com.zip / ftp.wwiv.com / pub / MISC / MNLDOS.ZIP / src / spawn.asm < prev    next >
Assembly Source File  |  2004-07-11  |  68KB  |  2,135 lines

  1. ; $Id: spawn.asm,v 1.2 2004/07/11 09:29:15 ozzmosis Exp $
  2.  
  3. ;MODIFIED 5th October 1995 by Matthew Parker all changes are public domain.
  4.  
  5. ;       SPAWN.ASM - Main function for memory swapping spawn call.
  6. ;
  7. ;       Public Domain Software written by
  8. ;               Thomas Wagner
  9. ;               Ferrari electronic GmbH
  10. ;               Beusselstrasse 27
  11. ;               D-1000 Berlin 21
  12. ;               Germany
  13. ;
  14. ; Modified extensively by Matthew Parker, 3:711/934.31@fidonet, in late
  15. ; 1995, in order to make it assemble with Watcom 10.0's assembler.
  16. ; All changes are also public domain.  The following problems were
  17. ; encountered with WASM:
  18. ;
  19. ; 1. TYPE does not work on struc's
  20. ; 2. The symbol "addr" is confused as a instruction
  21. ; 3. Some assumptions about indirect addressing are wrong
  22. ;    when using struc's. Notably when making far indirect calls.
  23. ; 4. LOCAL statement doesn't like "local a, b, c" must use
  24. ;    "local a:word, b:word, c:word" it doesn't assume word length.
  25. ; 5. Segment override not allowed to be made an equate
  26. ; 6. Full PROC syntax not supported.
  27. ; 7. When model set @DataSize is not defined.
  28. ; 8. "arg" and "uses" is not supported after the PROC definition.
  29. ; 9. Cannot use "SIZE" to declare array size in variable declaration
  30. ;
  31. ; to assembler with WASM you must define "WATCOM".
  32.  
  33. ;
  34. ;
  35. ; Assemble with
  36. ;
  37. ; tasm  /DPASCAL_DEF spawn,spawnp       - Turbo Pascal (Tasm only), near
  38. ; tasm  /DPASCAL_DEF /DFARCALL spawn,spawnp - Turbo Pascal (Tasm only), far
  39. ; ?asm  spawn;                          - C, default model (small)
  40. ; ?asm  /DMODL=large spawn              - C, large model
  41. ;
  42. ;       NOTE:   For C, change the 'model' directive below according to your
  43. ;               memory model, or define MODL=xxx on the command line.
  44. ;
  45. ;               For Turbo C Huge model, you must give /DTC_HUGE on the
  46. ;               command line, or define it here.
  47. ;
  48. ;
  49. ; Main function:
  50. ;
  51. ;   PASCAL:
  52. ;       function do_spawn (swapping: integer;
  53. ;                          execfname: string;
  54. ;                          cmdtail: string;
  55. ;                          envlen: word;
  56. ;                          var envp)
  57. ;
  58. ;   C:
  59. ;       int do_spawn (int swapping,
  60. ;                     char *execfname,
  61. ;                     char *cmdtail,
  62. ;                     unsigned envlen,
  63. ;                     char *envp)
  64. ;
  65. ;   Parameters:
  66. ;
  67. ;       swapping - swap/spawn/exec function:
  68. ;                       < 0: Exec, don't swap
  69. ;                         0: Spawn, don't swap
  70. ;                       > 0: Spawn, swap
  71. ;                            in this case, prep_swap must have
  72. ;                            been called beforehand (see below).
  73. ;
  74. ;       cmdtail - command tail for EXEC.
  75. ;
  76. ;       execfname - name and path of file to execute.
  77. ;
  78. ;       envlen - length of environment copy (may be 0).
  79. ;
  80. ;       envp -  pointer to environment block (must be aligned on
  81. ;               paragraph boundary). Unused if envlen is 0.
  82. ;
  83. ;       'cmdtail' and 'execfname' must be zero terminated, even when
  84. ;       calling from Pascal. For Pascal, the length byte of the string
  85. ;       is ignored.
  86. ;
  87. ;   Returns:
  88. ;       0000..00ff:     Returncode of EXECed program
  89. ;       03xx:           DOS-Error xx calling EXEC
  90. ;       0500:           Swapping requested, but prep_swap has not
  91. ;                       been called or returned an error
  92. ;       0501:           MCBs don't match expected setup
  93. ;       0502:           Error while swapping out
  94. ;
  95. ;
  96. ; For swapping, the swap method must be prepared before calling do_spawn.
  97. ;
  98. ;   PASCAL:
  99. ;       function prep_swap (method: word; swapfname: string): integer;
  100. ;   C:
  101. ;       int prep_swap (unsigned method, char *swapfname)
  102. ;
  103. ;   Parameters:
  104. ;
  105. ;       method  - bit-map of allowed swap devices:
  106. ;                       01 - Allow EMS
  107. ;                       02 - Allow XMS
  108. ;                       04 - Allow File swap
  109. ;                       10 - Try XMS first, then EMS
  110. ;                       40 - Create file as "hidden"
  111. ;                       80 - Use "create temp" call for file swap
  112. ;                      100 - Don't preallocate file
  113. ;                      200 - Check for Network, don't preallocate if net
  114. ;                     4000 - Environment block will not be swapped
  115. ;
  116. ;       swapfname - swap file name (may be undefined if the
  117. ;                   "method" parameters disallows file swap).
  118. ;                   The string must be zero terminated, even
  119. ;                   when calling from Pascal. For Pascal, the
  120. ;                   length byte of the string is ignored.
  121. ;
  122. ;   Returns:
  123. ;
  124. ;       A positive integer on success:
  125. ;               1 - EMS swap initialized
  126. ;               2 - XMS swap initialized
  127. ;               4 - File swap initialized
  128. ;       A negative integer on failure:
  129. ;               -1 - Couldn't allocate swap space
  130. ;               -2 - The spawn module is located too low in memory
  131. ;
  132. ;
  133.         .model  large,c
  134. ;
  135. IFNDEF WATCOM
  136. ptrsize =       @DataSize
  137. ELSE
  138. do_spawn   equ _do_spawn
  139. prep_swap  equ _prep_swap
  140. _psp       equ __psp
  141. ptrsize = 1
  142. ENDIF
  143. ;
  144.         extrn   _psp: word
  145. ;
  146.         public  do_spawn
  147.         public  prep_swap
  148. ;
  149. stacklen        =       256             ; local stack
  150. ;
  151. ;       "ems_size" is the EMS block size: 16k.
  152. ;
  153. ems_size        =       16 * 1024       ; EMS block size
  154. ems_parasize    =       ems_size / 16   ; same in paragraphs
  155. ems_shift       =       10              ; shift factor for paragraphs
  156. ems_paramask    =       ems_parasize-1  ; block mask
  157. ;
  158. ;       "xms_size" is the unit of measurement for XMS: 1k
  159. ;
  160. xms_size        =       1024            ; XMS block size
  161. xms_parasize    =       xms_size / 16   ; same in paragraphs
  162. xms_shift       =       6               ; shift factor for paragraphs
  163. xms_paramask    =       xms_parasize-1  ; block mask
  164. ;
  165. ;       Method flags
  166. ;
  167. USE_EMS         =       01h
  168. USE_XMS         =       02h
  169. USE_FILE        =       04h
  170. XMS_FIRST       =       10h
  171. HIDE_FILE       =       40h
  172. CREAT_TEMP      =       80h
  173. NO_PREALLOC     =       100h
  174. CHECK_NET       =       200h
  175. DONT_SWAP_ENV   =       4000h
  176. ;
  177. ;       Return codes
  178. ;
  179. RC_TOOLOW       =       0102h
  180. RC_BADPREP      =       0500h
  181. RC_MCBERROR     =       0501h
  182. RC_SWAPERROR    =       0502h
  183. ;
  184. EMM_INT         =       67h
  185. ;
  186. ;       The EXEC function parameter block
  187. ;
  188. exec_block      struc
  189. envseg  dw      ?               ; environment segment
  190. ppar    dw      ?               ; program parameter string offset
  191. pparseg dw      ?               ; program parameter string segment
  192. fcb1    dw      ?               ; FCB offset
  193. fcb1seg dw      ?               ; FCB segment
  194. fcb2    dw      ?               ; FCB offset
  195. fcb2seg dw      ?               ; FCB segment
  196. exec_block      ends
  197. exec_block_size=0eh
  198. ;
  199. ;       Structure of an XMS move control block
  200. ;
  201. xms_control     struc
  202. lenlo           dw      ?       ; length to move (doubleword)
  203. lenhi           dw      ?
  204. srchnd          dw      ?       ; source handle (0 for standard memory)
  205. srclo           dw      ?       ; source address (doubleword or seg:off)
  206. srchi           dw      ?
  207. desthnd         dw      ?       ; destination handle (0 for standard memory)
  208. destlo          dw      ?       ; destination address (doubleword or seg:off)
  209. desthi          dw      ?
  210. xms_control     ends
  211. xms_control_size=10h
  212. ;
  213. ;       The structure of the start of an MCB (memory control block)
  214. ;
  215. mcb             struc
  216. id              db      ?
  217. owner           dw      ?
  218. paras           dw      ?
  219. mcb             ends
  220. ;
  221. ;       The structure of an internal MCB descriptor.
  222. ;       CAUTION: This structure is assumed to be no larger than 16 bytes
  223. ;       in several places in the code, and to be exactly 16 bytes when
  224. ;       swapping in from file. Be careful when changing this structure.
  225. ;
  226. mcbdesc         struc
  227. __addr            dw      ?   ;For wasm    ; paragraph address of the MCB
  228. msize           dw      ?       ; size in paragraphs (excluding header)
  229. swoffset        dw      ?       ; swap offset (0 in all blocks except first)
  230. swsize          dw      ?       ; swap size (= msize + 1 except in first)
  231. num_follow      dw      ?       ; number of following MCBs
  232.                 dw      3 dup(?) ; pad to paragraph (16 bytes)
  233. mcbdesc         ends
  234. mcbdesc_size=10h
  235. ;
  236. ;       The variable block set up by prep_swap
  237. ;
  238. prep_block      struc
  239. xmm             dd      ?               ; XMM entry address
  240. first_mcb       dw      ?               ; Segment of first MCB
  241. psp_mcb         dw      ?               ; Segment of MCB of our PSP
  242. env_mcb         dw      ?               ; MCB of Environment segment
  243. noswap_mcb      dw      ?               ; MCB that may not be swapped
  244. ems_pageframe   dw      ?               ; EMS page frame address
  245. handle          dw      ?               ; EMS/XMS/File handle
  246. total_mcbs      dw      ?               ; Total number of MCBs
  247. swapmethod      db      ?               ; Method for swapping
  248. swapfilename    db      81 dup(?)       ; Swap file name if swapping to file
  249. prep_block      ends
  250. prep_block_size=64h
  251. ;
  252. ;----------------------------------------------------------------------
  253. ;
  254. ;       Since we'll be moving code and data around in memory,
  255. ;       we can't address locations in the resident block with
  256. ;       normal address expressions. MASM does not support
  257. ;       defining variables with a fixed offset, so we have to resort
  258. ;       to a kludge, and define the shrunk-down code as a structure.
  259. ;       It would also be possible to use an absolute segment for the
  260. ;       definition, but this is not supported by the Turbo Pascal linker.
  261. ;
  262. ;       All references to low-core variables from low-core itself
  263. ;       are made through DS, so we define a text macro "lmem" that
  264. ;       expands to "ds:". When setting up low core from the normal
  265. ;       code, ES is used to address low memory, so this can't be used.
  266. ;
  267. ;lmem    equ     <ds:>
  268. ;
  269. ;       The memory structure for the shrunk-down code, excluding the
  270. ;       code itself. The code follows this block.
  271. ;
  272. parseg          struc
  273.                 db      2ch dup(?)
  274. psp_envptr      dw      ?
  275.                 db      5ch-2eh dup(?)  ; start after PSP
  276. ;
  277. save_ss         dw      ?               ; 5C - saved global ss
  278. save_sp         dw      ?               ; 5E - saved global sp
  279. xfcb1           db      16 dup(?)       ; 60..6F - default FCB
  280. xfcb2           db      16 dup(?)       ; 70..7F - default FCB
  281. zero            dw      ?               ; 80 Zero command tail length (dummy)
  282. ;
  283. expar           db      SIZE exec_block dup (?) ; exec-parameter-block
  284. spx             dw      ?               ; saved local sp
  285. div0_off        dw      ?               ; divide by zero vector save
  286. div0_seg        dw      ?
  287. filename        db      82 dup(?)       ; exec filename
  288. progpars        db      128 dup(?)      ; command tail
  289.                 db      stacklen dup(?) ; local stack space
  290. mystack         db      ?
  291. lprep           db      SIZE prep_block dup(?)  ; the swapping variables
  292. lcurrdesc       db      SIZE mcbdesc dup(?)     ; the current MCB descriptor
  293. lxmsctl         db      SIZE xms_control dup(?)
  294. eretcode        dw      ?               ; EXEC return code
  295. retflags        dw      ?               ; EXEC return flags
  296. cgetmcb         dw      ?               ; address of get_mcb
  297. ;
  298. parseg_size=$-parseg
  299. parseg  ends
  300.  
  301. ;
  302. IFNDEF WATCOM
  303. param_len       =       (((SIZE parseg + 1) / 2) * 2)     ; make even
  304. ELSE
  305. param_len       =       (((SIZE parseg + 1) / 2) * 2 + 200h)   ; make even
  306. ENDIF
  307. codebeg         =       param_len
  308. ;
  309.         .code
  310. ;
  311. ;------------------------------------------------------------------------
  312. ;
  313. lowcode_begin:
  314. ;
  315. ;       The following parts of the program code will be moved to
  316. ;       low core and executed there, so there must be no absolute
  317. ;       memory references.
  318. ;       The call to get_mcb must be made indirect, since the offset
  319. ;       from the swap-in routine to get_mcb will not be the same
  320. ;       after moving.
  321. ;
  322. ;
  323. ;       get_mcb allocates a block of memory by modifying the MCB chain
  324. ;       directly.
  325. ;
  326. ;       On entry, lcurrdesc has the mcb descriptor for the block to
  327. ;                 allocate.
  328. ;
  329. ;       On exit,  Carry is set if the block couldn't be allocated.
  330. ;
  331. ;       Uses    AX, BX, CX, ES
  332. ;       Modifies lprep.first_mcb
  333. ;
  334. get_mcb proc    near
  335. ;
  336.         mov     ax,ds: lprep.first_mcb
  337.         mov     bx,ds: lcurrdesc.__addr
  338. ;
  339. getmcb_loop:
  340.         mov     es,ax
  341.         cmp     ax,bx
  342.         ja      gmcb_abort              ; halt if MCB > wanted
  343.         je      mcb_found               ; jump if same addr as wanted
  344.         add     ax,es:paras             ; last addr
  345.         inc     ax                      ; next mcb
  346.         cmp     ax,bx
  347.         jbe     getmcb_loop             ; Loop if next <= wanted
  348. ;
  349. ;
  350. ;       The wanted MCB starts within the current MCB. We now have to
  351. ;       create a new MCB at the wanted position, which is initially
  352. ;       free, and shorten the current MCB to reflect the reduced size.
  353. ;
  354.         cmp     es:owner,0
  355.         jne     gmcb_abort              ; halt if not free
  356.         mov     bx,es                   ; current
  357.         inc     bx                      ; + 1 (header doesn't count)
  358.         mov     ax,ds: lcurrdesc.__addr
  359.         sub     ax,bx                   ; paragraphs between MCB and wanted
  360.         mov     bx,es:paras             ; paras in current MCB
  361.         sub     bx,ax                   ; remaining paras
  362.         dec     bx                      ; -1 for header
  363.         mov     es:paras,ax             ; set new size for current
  364.         mov     cl,es:id                ; old id
  365.         mov     es:id,4dh               ; set id: there is a next
  366.         mov     ax,ds: lcurrdesc.__addr
  367.         mov     es,ax
  368.         mov     es:id,cl                ; and init to free
  369.         mov     es:owner,0
  370.         mov     es:paras,bx
  371. ;
  372. ;       We have found an MCB at the right address. If it's not free,
  373. ;       abort. Else check the size. If the size is ok, we're done
  374. ;       (more or less).
  375. ;
  376. mcb_found:
  377.         mov     es,ax
  378.         cmp     es:owner,0
  379.         je      mcb_check               ; continue if free
  380. ;
  381. gmcb_abort:
  382.         stc
  383.         ret
  384. ;
  385. mcb_check:
  386.         mov     ax,es:paras             ; size
  387.         cmp     ax,ds: lcurrdesc.msize ; needed size
  388.         jae     mcb_ok                  ; ok if enough space
  389. ;
  390. ;       If there's not enough room in this MCB, check if the next
  391. ;       MCB is free, too. If so, coalesce both MCB's and check again.
  392. ;
  393.         cmp     es:id,4dh
  394.         jnz     gmcb_abort              ; halt if no next
  395.         push    es                      ; save current
  396.         mov     bx,es
  397.         add     ax,bx
  398.         inc     ax                      ; next MCB
  399.         mov     es,ax
  400.         cmp     es:owner,0              ; next free ?
  401.         jne     gmcb_abort              ; halt if not
  402.         mov     ax,es:paras             ; else load size
  403.         inc     ax                      ; + 1 for header
  404.         mov     cl,es:id                ; and load ID
  405.         pop     es                      ; back to last MCB
  406.         add     es:paras,ax             ; increase size
  407.         mov     es:id,cl                ; and store ID
  408.         jmp     mcb_check               ; now try again
  409. ;
  410. ;       The MCB is free and large enough. If it's larger than the
  411. ;       wanted size, create another MCB after the wanted.
  412. ;
  413. mcb_ok:
  414.         mov     bx,es:paras
  415.         sub     bx,ds: lcurrdesc.msize
  416.         jz      mcb_no_next             ; ok, no next to create
  417.         push    es
  418.         dec     bx                      ; size of next block
  419.         mov     ax,es
  420.         add     ax,ds: lcurrdesc.msize
  421.         inc     ax                      ; next MCB addr
  422.         mov     cl,es:id                ; id of this block
  423.         mov     es,ax                   ; address next
  424.         mov     es:id,cl                ; store id
  425.         mov     es:paras,bx             ; store size
  426.         mov     es:owner,0              ; and mark as free
  427.         pop     es                      ; back to old MCB
  428.         mov     es:id,4dh               ; mark next block present
  429.         mov     ax,ds: lcurrdesc.msize ; and set size to wanted
  430.         mov     es:paras,ax
  431. ;
  432. mcb_no_next:
  433.         mov     es:owner,cx             ; set owner to current PSP
  434. ;
  435. ;       Set the 'first_mcb' pointer to the current one, so we don't
  436. ;       walk through all the previous blocks the next time.
  437. ;       Also, check if the block we just allocated is the environment
  438. ;       segment of the program. If so, restore the environment pointer
  439. ;       in the PSP.
  440. ;
  441.         mov     ax,es
  442.         mov     ds: lprep.first_mcb,ax
  443.         cmp     ds: lprep.env_mcb,ax
  444.         jne     getmcb_finis
  445.         inc     ax
  446.         mov     ds: psp_envptr,ax
  447. ;
  448. getmcb_finis:
  449.         clc
  450.         ret                             ; all finished (whew!)
  451. ;
  452. get_mcb endp
  453. ;
  454. ;
  455. ireti:
  456.         iret
  457. ;
  458. ;
  459. ;       The actual EXEC call.
  460. ;       Registers on entry:
  461. ;               BX      = paragraphs to keep (0 if no swap)
  462. ;               CX      = length of environment to copy (words) or zero
  463. ;               DS:SI   = environment source
  464. ;               ES:DI   = environment destination
  465. ;               (ES = our low core code segment)
  466. ;
  467. ;
  468. ;       copy environment buffer down if present
  469. ;
  470. doexec:
  471.         jcxz    noenvcpy
  472.         rep movsw
  473. ;
  474. noenvcpy:
  475.         push    es                      ; DS = ES = low core = PSP
  476.         pop     ds
  477.         or      bx,bx
  478.         jz      no_shrink
  479. ;
  480. ;       first, shrink the base memory block down.
  481. ;
  482.         mov     ah,04ah
  483.         int     21h                     ; resize memory block
  484. ;
  485. ;       Again walk all MCBs. This time, all blocks owned by the
  486. ;       current process are released.
  487. ;
  488.         mov     si,ds: lprep.first_mcb
  489.         or      si,si
  490.         jz      no_shrink
  491.         mov     dx,ds: lprep.psp_mcb
  492.         mov     bx,dx
  493.         inc     bx                      ; base PSP (MCB owner)
  494.         mov     di,ds: lprep.noswap_mcb
  495. ;
  496. free_loop:
  497.         cmp     si,dx
  498.         je      free_next               ; don't free base block
  499.         cmp     si,di
  500.         je      free_next
  501.         mov     es,si
  502.         cmp     bx,es:owner             ; our process?
  503.         jne     free_next               ; next if not
  504.         cmp     si,ds: lprep.env_mcb   ; is this the environment block?
  505.         jne     free_noenv
  506.         mov     ds:psp_envptr,0         ; else clear PSP pointer
  507. ;
  508. free_noenv:
  509.         inc     si
  510.         mov     es,si
  511.         dec     si
  512.         mov     ah,049h                 ; free memory block
  513.         int     21h
  514. ;
  515. free_next:
  516.         mov     es,si
  517.         cmp     es:id,4dh               ; normal block?
  518.         jne     free_ready              ; ready if end of chain
  519.         add     si,es:paras             ; start + length
  520.         inc     si                      ; next MCB
  521.         jmp     free_loop
  522. ;
  523. free_ready:
  524.         mov     ax,ds
  525.         mov     es,ax
  526. ;
  527. no_shrink:
  528.         mov     dx,filename             ; params for exec
  529.         mov     bx,expar
  530.         mov     ax,04b00h
  531.         int     21h                     ; exec
  532. ;
  533. ;       Return from EXEC system call. Don't count on any register except
  534. ;       CS to be restored (DOS 2.11 and previous versions killed all regs).
  535. ;
  536.         mov     bx,cs
  537.         mov     ds,bx
  538.         mov     es,bx
  539.         mov     ss,bx
  540.         mov     sp,ds: spx
  541.         cld
  542.         mov     ds: eretcode,ax        ; save return code
  543.         pushf
  544.         pop     bx
  545.         mov     ds: retflags,bx        ; and returned flags
  546. ;
  547.         cmp     ds: lprep.swapmethod,0
  548.         je      exec_memok
  549.         jg      exec_expand
  550. ;
  551. ;       Terminate.
  552. ;
  553.         test    bx,1                    ; carry?
  554.         jnz     exec_term               ; use EXEc retcode if set
  555.         mov     ah,4dh                  ; else get program return code
  556.         int     21h
  557. ;
  558. exec_term:
  559.         mov     ah,4ch
  560.         int     21h
  561. ;
  562. ;
  563. exec_expand:
  564.         mov     ah,4ah                  ; expand memory
  565.         mov     bx,ds: lcurrdesc.msize
  566.         int     21h
  567.         jnc     exec_memok
  568.         mov     ax,4cffh
  569.         int     21h                     ; terminate on error
  570. ;
  571. ;       Swap memory back
  572. ;
  573.         nop
  574. ;
  575. exec_memok:
  576. ;
  577. ;       FALL THROUGH to the appropriate swap-in routine
  578. ;
  579. ;
  580. getmcboff       =       offset get_mcb - offset lowcode_begin
  581. iretoff         =       offset ireti - offset lowcode_begin
  582. doexec_entry    =       offset doexec - offset lowcode_begin
  583. base_length     =       offset $ - offset lowcode_begin
  584. ;
  585. ;-----------------------------------------------------------------------
  586. ;
  587. ;       The various swap in routines follow. Only one of the routines
  588. ;       is copied to low memory.
  589. ;       Note that the routines are never actually called, the EXEC return
  590. ;       code falls through. The final RET thus will return to the restored
  591. ;       memory image.
  592. ;
  593. ;       On entry, DS must point to low core.
  594. ;       On exit to the restored code, DS is unchanged.
  595. ;
  596. ;
  597. ;       swapin_ems:     swap in from EMS.
  598. ;
  599. swapin_ems      proc    far
  600. ;
  601.         xor     bx,bx
  602.         mov     si,ems_parasize
  603.         mov     dx,ds: lprep.handle    ; EMS handle
  604. ;
  605. swinems_main:
  606.         push    ds
  607.         mov     cx,ds: lcurrdesc.swsize        ; block length in paras
  608.         mov     di,ds: lcurrdesc.swoffset      ; swap offset
  609.         mov     es,ds: lcurrdesc.__addr          ; segment to swap
  610.         mov     ds,ds: lprep.ems_pageframe     ; page frame address
  611. ;
  612.         mov     ax,ems_parasize         ; max length
  613.         sub     ax,si                   ; minus current offset
  614.         jnz     swinems_ok              ; go copy if nonzero
  615. ;
  616. swinems_loop:
  617.         mov     ax,4400h                ; map in next page
  618.         int     EMM_INT
  619.         or      ah,ah
  620.         jnz     swinems_error
  621.         mov     si,0                    ; reset offset
  622.         inc     bx                      ; bump up page number
  623.         mov     ax,ems_parasize         ; max length to copy
  624. ;
  625. swinems_ok:
  626.         cmp     ax,cx                   ; length to copy
  627.         jbe     swinems_doit            ; go do it if <= total length
  628.         mov     ax,cx                   ; else use total length
  629. ;
  630. swinems_doit:
  631.         sub     cx,ax                   ; subtract copy length from total
  632.         push    cx                      ; and save
  633.         push    ax                      ; save the copy length in paras
  634.         push    si
  635.         push    di
  636.         mov     cl,3
  637.         shl     ax,cl                   ; convert to number of words (!)
  638.         inc     cl
  639.         shl     si,cl                   ; convert to byte address
  640.         mov     cx,ax
  641.         rep movsw
  642.         pop     di
  643.         pop     si
  644.         pop     cx                      ; copy length in paras
  645.         mov     ax,es
  646.         add     ax,cx                   ; add copy length to dest segment
  647.         add     si,cx                   ; and EMS page offset
  648.         mov     es,ax
  649.         pop     cx                      ; remaining length
  650.         or      cx,cx                   ; did we copy everything?
  651.         jnz     swinems_loop            ; go loop if not
  652. ;
  653.         pop     ds
  654.         cmp     ds: lcurrdesc.num_follow,0     ; another MCB?
  655.         je      swinems_complete        ; exit if not
  656. ;
  657. ;       Another MCB follows, read next mcb descriptor into currdesc
  658. ;
  659.         cmp     si,ems_parasize
  660.         jb      swinems_nonewpage       ; no new block needed
  661.         mov     ax,4400h                ; map page, phys = 0
  662.         int     EMM_INT
  663.         or      ah,ah
  664.         jnz     swinems_error1
  665.         mov     si,0
  666.         inc     bx
  667. ;
  668. swinems_nonewpage:
  669.         push    si
  670.         push    ds
  671.         mov     ax,ds
  672.         mov     es,ax
  673.         mov     ds,ds: lprep.ems_pageframe     ; page frame address
  674.         mov     cl,4
  675.         shl     si,cl                   ; convert to byte address
  676.         mov     cx,SIZE mcbdesc
  677.         mov     di,offset lcurrdesc
  678.         rep movsb
  679.         pop     ds
  680.         pop     si
  681.         inc     si                      ; one paragraph
  682. ;
  683.         push    bx
  684.         call    ds: cgetmcb
  685.         pop     bx
  686.         jc      swinems_error1
  687.         jmp     swinems_main
  688. ;
  689. swinems_complete:
  690.         mov     ah,45h                  ; release EMS pages
  691.         int     EMM_INT
  692.         ret
  693. ;
  694. swinems_error:
  695.         pop     ds
  696. swinems_error1:
  697.         mov     ah,45h                  ; release EMS pages on error
  698.         int     EMM_INT
  699.         mov     ax,4cffh
  700.         int     21h                     ; terminate
  701. ;
  702. swapin_ems      endp
  703. ;
  704. swinems_length  = offset $ - offset swapin_ems
  705. ;
  706. ;
  707. ;       swapin_xms:     swap in from XMS.
  708. ;
  709. swapin_xms      proc    far
  710. ;
  711.         mov     ax,ds: lprep.handle    ; XMS handle
  712.         mov     ds: lxmsctl.srchnd,ax  ; source is XMS
  713.         mov     ds: lxmsctl.desthnd,0  ; dest is normal memory
  714.         mov     ds: lxmsctl.srclo,0
  715.         mov     ds: lxmsctl.srchi,0
  716. ;
  717. swinxms_main:
  718.         mov     ax,ds: lcurrdesc.swsize ; size in paragraphs
  719.         mov     cl,4
  720.         rol     ax,cl                   ; size in bytes + high nibble
  721.         mov     dx,ax
  722.         and     ax,0fff0h               ; low word
  723.         and     dx,0000fh               ; high word
  724.         mov     ds: lxmsctl.lenlo,ax   ; into control block
  725.         mov     ds: lxmsctl.lenhi,dx
  726.         mov     ax,ds: lcurrdesc.swoffset      ; swap offset
  727.         mov     ds: lxmsctl.destlo,ax          ; into control block
  728.         mov     ax,ds: lcurrdesc.__addr          ; segment to swap
  729.         mov     ds: lxmsctl.desthi,ax
  730.         mov     si,offset lxmsctl
  731.         mov     ah,0bh
  732.         call    dword ptr ds:[lprep.xmm] ; move it
  733.         or      ax,ax
  734.         jz      swinxms_error
  735.         mov     ax,ds: lxmsctl.lenlo   ; adjust source addr
  736.         add     ds: lxmsctl.srclo,ax
  737.         mov     ax,ds: lxmsctl.lenhi
  738.         adc     ds: lxmsctl.srchi,ax
  739. ;
  740.         cmp     ds: lcurrdesc.num_follow,0     ; another MCB?
  741.         je      swinxms_complete
  742. ;
  743.         mov     ds: lxmsctl.lenlo,SIZE mcbdesc
  744.         mov     ds: lxmsctl.lenhi,0
  745.         mov     ds: lxmsctl.desthi,ds
  746.         mov     ds: lxmsctl.destlo,offset lcurrdesc
  747.         mov     si,offset lxmsctl
  748.         mov     ah,0bh
  749.         call    dword ptr ds:[lprep.xmm] ; move it
  750.         or      ax,ax
  751.         jz      swinxms_error
  752.         add     ds: lxmsctl.srclo,16   ; one paragraph
  753.         adc     ds: lxmsctl.srchi,0
  754. ;
  755.         call    ds: cgetmcb
  756.         jc      swinxms_error
  757.         jmp     swinxms_main
  758. ;
  759. swinxms_complete:
  760.         mov     ah,0ah                  ; release XMS frame
  761.         mov     dx,ds: lprep.handle    ; XMS handle
  762.         call    dword ptr ds:[lprep.xmm]
  763.         ret
  764. ;
  765. swinxms_error:
  766.         mov     ah,0ah                  ; release XMS frame on error
  767.         call    dword ptr ds:[lprep.xmm]
  768.         mov     ax,4c00h
  769.         int     21h
  770. ;
  771. swapin_xms      endp
  772. ;
  773. swinxms_length  = offset $ - offset swapin_xms
  774. ;
  775. ;
  776. ;       swapin_file:    swap in from file.
  777. ;
  778. swapin_file     proc    far
  779. ;
  780.         IFNDEF  WATCOM
  781.         mov     dx,offset lprep+offset swapfilename; for wasm ;offset lprep.swapfilename
  782.         ELSE
  783.         mov     dx,offset lprep+13h
  784.         ENDIF
  785.         mov     ax,3d00h                        ; open file
  786.         int     21h
  787.         jc      swinfile_error2
  788.         mov     bx,ax                           ; file handle
  789. ;
  790. swinfile_main:
  791.         push    ds
  792.         mov     cx,ds: lcurrdesc.swsize        ; size in paragraphs
  793.         mov     dx,ds: lcurrdesc.swoffset      ; swap offset
  794.         mov     ds,ds: lcurrdesc.__addr          ; segment to swap
  795. ;
  796. swinfile_loop:
  797.         mov     ax,cx
  798.         cmp     ah,8h                   ; above 32k?
  799.         jbe     swinfile_ok             ; go read if not
  800.         mov     ax,800h                 ; else read 32k
  801. ;
  802. swinfile_ok:
  803.         sub     cx,ax                   ; remaining length
  804.         push    cx                      ; save it
  805.         push    ax                      ; and save paras to read
  806.         mov     cl,4
  807.         shl     ax,cl                   ; convert to bytes
  808.         mov     cx,ax
  809.         mov     ah,3fh                  ; read
  810.         int     21h
  811.         jc      swinfile_error
  812.         cmp     ax,cx
  813.         jne     swinfile_error
  814.         pop     cx                      ; paras read
  815.         mov     ax,ds
  816.         add     ax,cx                   ; bump up dest segment
  817.         mov     ds,ax
  818.         pop     cx                      ; remaining length
  819.         or      cx,cx                   ; anything left?
  820.         jnz     swinfile_loop           ; go loop if yes
  821. ;
  822.         pop     ds
  823.         cmp     ds: lcurrdesc.num_follow,0     ; another MCB?
  824.         je      swinfile_complete       ; ready if not
  825.         mov     cx,16                   ; read one paragraph
  826.         mov     dx,offset lcurrdesc
  827.         mov     ah,3fh
  828.         int     21h
  829.         jc      swinfile_error1
  830.         cmp     ax,cx
  831.         jne     swinfile_error1
  832. ;
  833.         push    bx
  834.         call    ds: cgetmcb
  835.         pop     bx
  836.         jc      swinfile_error1
  837.         jmp     swinfile_main
  838. ;
  839. ;
  840. swinfile_complete:
  841.         mov     ah,3eh                  ; close file
  842.         int     21h
  843.         IFNDEF  WATCOM
  844.         mov     dx,offset lprep+offset swapfilename; for wasm ;offset lprep.swapfilename
  845.         ELSE
  846.         mov     dx,offset lprep+13h
  847.         ENDIF
  848.         mov     ah,41h                  ; delete file
  849.         int     21h
  850.         ret
  851. ;
  852. swinfile_error:
  853.         pop     cx
  854.         pop     cx
  855.         pop     ds
  856. swinfile_error1:
  857.         mov     ah,3eh                  ; close file
  858.         int     21h
  859. swinfile_error2:
  860.         IFNDEF  WATCOM
  861.         mov     dx,offset lprep+offset swapfilename; for wasm ;offset lprep.swapfilename
  862.         ELSE
  863.         mov     dx,offset lprep+13h
  864.         ENDIF
  865.         mov     ah,41h                  ; delete file
  866.         int     21h
  867.         mov     ax,4cffh
  868.         int     21h
  869. ;
  870. swapin_file     endp
  871. ;
  872. swinfile_length = offset $ - offset swapin_file
  873. ;
  874. ;
  875. ;       swapin_none:    no swap, return immediately.
  876. ;
  877. swapin_none     proc    far
  878. ;
  879.         ret
  880. ;
  881. swapin_none     endp
  882. ;
  883. ;
  884.         IF      swinems_length GT swinxms_length
  885. swcodelen       =       swinems_length
  886.         ELSE
  887. swcodelen       =       swinxms_length
  888.         ENDIF
  889.         IF      swinfile_length GT swcodelen
  890. swcodelen       =       swinfile_length
  891.         ENDIF
  892. ;
  893. swap_codelen    =       ((swcodelen + 1) / 2) * 2
  894. ;
  895. codelen         =       base_length + swap_codelen
  896. reslen          =       codebeg + codelen
  897. keep_paras      =       (reslen + 15) shr 4     ; paragraphs to keep
  898. swapbeg         =       keep_paras shl 4        ; start of swap space
  899. savespace       =       swapbeg - 5ch   ; length of overwritten area
  900. ;
  901. ;--------------------------------------------------------------------
  902. ;
  903.         IFDEF   PASCAL_DEF
  904.         .data
  905.         ELSE
  906.         IFDEF   TC_HUGE
  907.         .fardata?       my_data
  908.         ELSE
  909.         .data?
  910.         ENDIF
  911.         ENDIF
  912. ;
  913. ;
  914. ;       Space for saving the part of the memory image below the
  915. ;       swap area that is overwritten by our code.
  916. ;
  917. save_dat        db      savespace dup(?)
  918. ;
  919. ;       Variables used while swapping out.
  920. ;       The "prep" structure is initialized by prep_swap.
  921. ;
  922. prep            prep_block      <>
  923. nextmcb         mcbdesc         <>
  924. currdesc        mcbdesc         <>
  925. xmsctl          xms_control     <>
  926. ems_curpage     dw              ?       ; current EMS page number
  927. ems_curoff      dw              ?       ; current EMS offset (paragraph)
  928. ;
  929. ;--------------------------------------------------------------------
  930. ;
  931.         .code
  932. ;
  933. ;       swapout_ems:    swap out an MCB block to EMS.
  934. ;
  935. ;       Entry:  "currdesc"      contains description of block to swap
  936. ;               "nextmcb"       contains MCB-descriptor of next block
  937. ;                               if currdesc.num_follow is nonzero
  938. ;
  939. ;       Exit:   0 if OK, != 0 if error, Zero-flag set accordingly.
  940. ;
  941. ;       Uses:   All regs excpt DS
  942. ;
  943. swapout_ems     proc    near
  944. ;
  945.         push    ds
  946.         mov     cx,currdesc.swsize      ; block length in paras
  947.         mov     si,currdesc.swoffset    ; swap offset
  948.         mov     dx,prep.handle          ; EMS handle
  949.         mov     bx,ems_curpage          ; current EMS page
  950.         mov     di,ems_curoff           ; current EMS page offset (paras)
  951.         mov     es,prep.ems_pageframe   ; page frame address
  952.         mov     ds,currdesc.__addr        ; segment to swap
  953. ;
  954.         mov     ax,ems_parasize         ; max length
  955.         sub     ax,di                   ; minus current offset
  956.         jnz     swems_ok                ; go copy if there's room
  957. ;
  958. swems_loop:
  959.         mov     ax,4400h                ; map in next page
  960.         int     EMM_INT
  961.         or      ah,ah
  962.         jnz     swems_error
  963.         mov     di,0                    ; reset offset
  964.         inc     bx                      ; bump up page number
  965.         mov     ax,ems_parasize         ; max length to copy
  966. ;
  967. swems_ok:
  968.         cmp     ax,cx                   ; length to copy
  969.         jbe     swems_doit              ; go do it if <= total length
  970.         mov     ax,cx                   ; else use total length
  971. ;
  972. swems_doit:
  973.         sub     cx,ax                   ; subtract copy length from total
  974.         push    cx                      ; and save
  975.         push    ax                      ; save the copy length in paras
  976.         push    si
  977.         push    di
  978.         mov     cl,3
  979.         shl     ax,cl                   ; convert to number of words (!)
  980.         inc     cl
  981.         shl     di,cl                   ; convert to byte address
  982.         mov     cx,ax
  983.         rep movsw
  984.         pop     di
  985.         pop     si
  986.         pop     cx                      ; copy length in paras
  987.         mov     ax,ds
  988.         add     ax,cx                   ; add copy length to source segment
  989.         add     di,cx                   ; and EMS page offset
  990.         mov     ds,ax
  991.         pop     cx                      ; remaining length
  992.         or      cx,cx                   ; did we copy everything?
  993.         jnz     swems_loop              ; go loop if not
  994. ;
  995.         pop     ds
  996.         cmp     currdesc.num_follow,0   ; another MCB?
  997.         je      swems_complete          ; exit if not
  998. ;
  999. ;       Another MCB follows, append nextmcb to save block.
  1000. ;
  1001.         cmp     di,ems_parasize
  1002.         jb      swems_nonewpage         ; no new block needed
  1003.         mov     ax,4400h                ; map page, phys = 0
  1004.         int     EMM_INT
  1005.         or      ah,ah
  1006.         jnz     swems_error1
  1007.         mov     di,0
  1008.         inc     bx
  1009. ;
  1010. swems_nonewpage:
  1011.         push    di
  1012.         mov     cl,4
  1013.         shl     di,cl                   ; convert to byte address
  1014.         mov     cx,SIZE mcbdesc
  1015.         mov     si,offset nextmcb
  1016.         rep movsb
  1017.         pop     di
  1018.         inc     di                      ; one paragraph
  1019. ;
  1020. swems_complete:
  1021.         mov     ems_curpage,bx
  1022.         mov     ems_curoff,di
  1023.         xor     ax,ax
  1024.         ret
  1025. ;
  1026. swems_error:
  1027.         pop     ds
  1028. swems_error1:
  1029.         mov     ah,45h                  ; release EMS pages on error
  1030.         int     EMM_INT
  1031.         mov     ax,RC_SWAPERROR
  1032.         or      ax,ax
  1033.         ret
  1034. ;
  1035. swapout_ems     endp
  1036. ;
  1037. ;
  1038. ;       swapout_xms:    swap out an MCB block to XMS.
  1039. ;
  1040. ;       Entry:  "currdesc"      contains description of block to swap
  1041. ;               "nextmcb"       contains MCB-descriptor of next block
  1042. ;                               if currdesc.num_follow is nonzero
  1043. ;
  1044. ;       Exit:   0 if OK, -1 if error, Zero-flag set accordingly.
  1045. ;
  1046. ;       Uses:   All regs excpt DS
  1047. ;
  1048. swapout_xms     proc    near
  1049. ;
  1050.         mov     ax,currdesc.swsize      ; size in paragraphs
  1051.         mov     cl,4
  1052.         rol     ax,cl                   ; size in bytes + high nibble
  1053.         mov     dx,ax
  1054.         and     ax,0fff0h               ; low word
  1055.         and     dx,0000fh               ; high word
  1056.         mov     xmsctl.lenlo,ax         ; into control block
  1057.         mov     xmsctl.lenhi,dx
  1058.         mov     xmsctl.srchnd,0         ; source is normal memory
  1059.         mov     ax,currdesc.swoffset    ; swap offset
  1060.         mov     xmsctl.srclo,ax         ; into control block
  1061.         mov     ax,currdesc.__addr        ; segment to swap
  1062.         mov     xmsctl.srchi,ax
  1063.         mov     ax,prep.handle          ; XMS handle
  1064.         mov     xmsctl.desthnd,ax
  1065.         mov     si,offset xmsctl
  1066.         mov     ah,0bh
  1067.         call    dword ptr ds:[prep.xmm] ; move it
  1068.         or      ax,ax
  1069.         jz      swxms_error
  1070.         mov     ax,xmsctl.lenlo         ; adjust destination addr
  1071.         add     xmsctl.destlo,ax
  1072.         mov     ax,xmsctl.lenhi
  1073.         adc     xmsctl.desthi,ax
  1074. ;
  1075.         cmp     currdesc.num_follow,0   ; another MCB?
  1076.         je      swxms_complete
  1077. ;
  1078.         mov     xmsctl.lenlo,SIZE mcbdesc
  1079.         mov     xmsctl.lenhi,0
  1080.         mov     xmsctl.srchi,ds
  1081.         mov     xmsctl.srclo,offset nextmcb
  1082.         mov     si,offset xmsctl
  1083.         mov     ah,0bh
  1084.         call    dword ptr ds:[prep.xmm]                ; move it
  1085.         or      ax,ax
  1086.         jz      swxms_error
  1087.         add     xmsctl.destlo,16        ; one paragraph
  1088.         adc     xmsctl.desthi,0
  1089. ;
  1090. swxms_complete:
  1091.         xor     ax,ax
  1092.         ret
  1093. ;
  1094. swxms_error:
  1095.         mov     ah,0ah                  ; release XMS frame on error
  1096.         mov     dx,prep.handle          ; XMS handle
  1097.         call    dword ptr ds:[prep.xmm]
  1098.         mov     ax,RC_SWAPERROR
  1099.         or      ax,ax
  1100.         ret
  1101. ;
  1102. swapout_xms     endp
  1103. ;
  1104. ;
  1105. ;       swapout_file:   swap out an MCB block to file.
  1106. ;
  1107. ;       Entry:  "currdesc"      contains description of block to swap
  1108. ;               "nextmcb"       contains MCB-descriptor of next block
  1109. ;                               if currdesc.num_follow is nonzero
  1110. ;
  1111. ;       Exit:   0 if OK, -1 if error, Zero-flag set accordingly.
  1112. ;
  1113. ;       Uses:   All regs excpt DS
  1114. ;
  1115. swapout_file    proc    near
  1116. ;
  1117.         push    ds
  1118.         mov     cx,currdesc.swsize      ; size in paragraphs
  1119.         mov     bx,prep.handle          ; file handle
  1120.         mov     dx,currdesc.swoffset    ; swap offset
  1121.         mov     ds,currdesc.__addr        ; segment to swap
  1122. ;
  1123. swfile_loop:
  1124.         mov     ax,cx
  1125.         cmp     ah,8h                   ; above 32k?
  1126.         jbe     swfile_ok               ; go write if not
  1127.         mov     ax,800h                 ; else write 32k
  1128. ;
  1129. swfile_ok:
  1130.         sub     cx,ax                   ; remaining length
  1131.         push    cx                      ; save it
  1132.         push    ax                      ; and save paras to write
  1133.         mov     cl,4
  1134.         shl     ax,cl                   ; convert to bytes
  1135.         mov     cx,ax
  1136.         mov     ah,40h                  ; write
  1137.         int     21h
  1138.         jc      swfile_error
  1139.         cmp     ax,cx
  1140.         jne     swfile_error
  1141.         pop     cx                      ; paras written
  1142.         mov     ax,ds
  1143.         add     ax,cx                   ; bump up source segment
  1144.         mov     ds,ax
  1145.         pop     cx                      ; remaining length
  1146.         or      cx,cx                   ; anything left?
  1147.         jnz     swfile_loop             ; go loop if yes
  1148. ;
  1149.         pop     ds
  1150.         cmp     currdesc.num_follow,0   ; another MCB?
  1151.         je      swfile_complete         ; ready if not
  1152.         mov     cx,16                   ; write one paragraph
  1153.         mov     dx,offset nextmcb
  1154.         mov     ah,40h
  1155.         int     21h
  1156.         jc      swfile_error1
  1157.         cmp     ax,cx
  1158.         jne     swfile_error1
  1159. ;
  1160. swfile_complete:
  1161.         xor     ax,ax
  1162.         ret
  1163. ;
  1164. swfile_error:
  1165.         pop     cx
  1166.         pop     cx
  1167.         pop     ds
  1168. swfile_error1:
  1169.         mov     ah,3eh                  ; close file
  1170.         int     21h
  1171.         mov     dx,offset prep.swapfilename
  1172.         mov     ah,41h                  ; delete file
  1173.         int     21h
  1174.         mov     ax,RC_SWAPERROR
  1175.         or      ax,ax
  1176.         ret
  1177. ;
  1178. swapout_file    endp
  1179. ;
  1180. ;--------------------------------------------------------------------------
  1181. ;--------------------------------------------------------------------------
  1182. ;
  1183. ;
  1184.         IFDEF   PASCAL_DEF
  1185.         IFDEF   FARCALL
  1186. do_spawn        PROC    far swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
  1187.         ELSE
  1188. do_spawn        PROC    near swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
  1189.         ENDIF
  1190.         ELSE
  1191.         IFDEF   WATCOM
  1192. do_spawn        PROC    uses si di, swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
  1193.         ELSE
  1194. do_spawn        PROC    uses si di, swapping: word, execfname: ptr, params: ptr, envlen: word, envp: ptr
  1195.         ENDIF
  1196.         ENDIF
  1197.  
  1198.         local   datseg:word, pspseg:word, currmcb:word
  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_DEF
  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     word ptr 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,word ptr 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     word ptr 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,SIZE 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    dword ptr ds:[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,SIZE prep_block
  1530.         mov     si,offset prep
  1531.         mov     di,offset lprep
  1532.         rep movsb
  1533.         mov     cx,SIZE mcbdesc
  1534.         mov     si,offset currdesc
  1535.         mov     di,offset 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,dword ptr params
  1562.         ELSE
  1563.         mov     si,params
  1564.         ENDIF
  1565.         IFDEF   PASCAL_DEF
  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,dword ptr execfname
  1607.         ELSE
  1608.         mov     si,execfname
  1609.         ENDIF
  1610.         IFDEF   PASCAL_DEF
  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,word ptr envlen               ; environment size
  1624.         jcxz    no_environ              ; go jump if no environment
  1625.         cmp     word ptr 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,dword ptr 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,offset 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_DEF
  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.         IFDEF   WATCOM
  1803. prep_swap       PROC    uses si di, pmethod: word, swapfname: dword
  1804.         ELSE
  1805. prep_swap       PROC    uses si di, pmethod: word, swapfname: ptr
  1806.         ENDIF
  1807.         ENDIF
  1808.  
  1809.         LOCAL   totparas: word
  1810. ;
  1811.         IFDEF   TC_HUGE
  1812.         mov     ax,SEG my_data
  1813.         mov     ds,ax
  1814.         ENDIF
  1815. ;
  1816.         IFDEF   PASCAL_DEF
  1817.         cld
  1818.         mov     ax,prefixseg
  1819.         ELSE
  1820.         IFDEF   TC_HUGE
  1821.         mov     ax,SEG _psp
  1822.         mov     es,ax
  1823.         mov     ax,es:_psp
  1824.         ELSE
  1825.         mov     ax,_psp
  1826.         ENDIF
  1827.         ENDIF
  1828. ;
  1829.         dec     ax
  1830.         mov     prep.psp_mcb,ax
  1831.         mov     prep.first_mcb,ax       ; init first MCB to PSP
  1832. ;
  1833. ;       Make a copy of the environment pointer in the PSP
  1834. ;
  1835.         inc     ax
  1836.         mov     es,ax
  1837.         mov     bx,es:psp_envptr
  1838.         dec     bx
  1839.         mov     prep.env_mcb,bx
  1840.         mov     prep.noswap_mcb,0
  1841.         test    pmethod,DONT_SWAP_ENV
  1842.         jz      can_swap_env
  1843.         mov     prep.noswap_mcb,bx
  1844. ;
  1845. ;       Check if spawn is too low in memory
  1846. ;
  1847. can_swap_env:
  1848.         mov     bx,cs
  1849.         mov     dx,offset lowcode_begin
  1850.         mov     cl,4
  1851.         shr     dx,cl
  1852.         add     bx,dx                   ; normalized start of this code
  1853.         mov     dx,keep_paras           ; the end of the modified area
  1854.         add     dx,ax                   ; plus PSP = end paragraph
  1855.         cmp     bx,dx
  1856.         ja      prepswap_ok     ; ok if start of code > end of low mem
  1857.         mov     ax,-2
  1858.         mov     prep.swapmethod,al
  1859.         ret
  1860. ;
  1861. ;       Walk the chain of memory blocks, adding up the paragraphs
  1862. ;       in all blocks belonging to this process.
  1863. ;       We try to find the first MCB by getting DOS's "list of lists",
  1864. ;       and fetching the word at offset -2 of the returned address.
  1865. ;       If this fails, we use our PSP as the starting point.
  1866. ;
  1867. prepswap_ok:
  1868.         xor     bx,bx
  1869.         mov     es,bx
  1870.         mov     ah,52h                  ; get list of lists
  1871.         int     21h
  1872.         mov     ax,es
  1873.         or      ax,bx
  1874.         jz      prep_no_first
  1875.         mov     es,es:[bx-2]            ; first MCB
  1876.         cmp     es:id,4dh               ; normal ID?
  1877.         jne     prep_no_first
  1878.         mov     prep.first_mcb,es
  1879. ;
  1880. prep_no_first:
  1881.         mov     es,prep.psp_mcb         ; ES points to base MCB
  1882.         mov     cx,es                   ; save this value
  1883.         mov     bx,es:owner             ; the current process
  1884.         mov     dx,es:paras             ; memory size in the base block
  1885.         sub     dx,keep_paras           ; minus resident paragraphs
  1886.         mov     si,0                    ; number of MCBs except base
  1887.         mov     di,prep.noswap_mcb
  1888.         mov     ax,prep.first_mcb
  1889.         mov     prep.first_mcb,0
  1890. ;
  1891. prep_mcb_walk:
  1892.         mov     es,ax
  1893.         cmp     ax,cx                   ; base block?
  1894.         je      prep_walk_next          ; then don't count again
  1895.         cmp     ax,di                   ; Non-swap MCB?
  1896.         je      prep_walk_next          ; then don't count
  1897. ;
  1898.         cmp     bx,es:owner             ; our process?
  1899.         jne     prep_walk_next          ; next if not
  1900.         inc     si
  1901.         mov     ax,es:paras             ; else get number of paragraphs
  1902.         add     ax,2                    ; + 1 for descriptor + 1 for MCB
  1903.         add     dx,ax                   ; total number of paras
  1904.         cmp     prep.first_mcb,0
  1905.         jne     prep_walk_next
  1906.         mov     prep.first_mcb,es
  1907. ;
  1908. prep_walk_next:
  1909.         cmp     es:id,4dh               ; normal block?
  1910.         jne     prep_mcb_ready          ; ready if end of chain
  1911.         mov     ax,es
  1912.         add     ax,es:paras             ; start + length
  1913.         inc     ax                      ; next MCB
  1914.         jmp     prep_mcb_walk
  1915. ;
  1916. prep_mcb_ready:
  1917.         mov     totparas,dx
  1918.         mov     prep.total_mcbs,si
  1919. ;
  1920.         test    pmethod,XMS_FIRST
  1921.         jnz     check_xms
  1922. ;
  1923. ;       Check for EMS swap
  1924. ;
  1925. check_ems:
  1926.         test    pmethod,USE_EMS
  1927.         jz      prep_no_ems
  1928. ;
  1929.         push    ds
  1930.         mov     al,EMM_INT
  1931.         mov     ah,35h
  1932.         int     21h                     ; get EMM int vector
  1933.         mov     ax,cs
  1934.         mov     ds,ax
  1935.         mov     si,offset emm_name
  1936.         mov     di,10
  1937.         mov     cx,8
  1938.         repz cmpsb                      ; EMM name present?
  1939.         pop     ds
  1940.         jnz     prep_no_ems
  1941. ;
  1942.         mov     ah,40h                  ; get EMS status
  1943.         int     EMM_INT
  1944.         or      ah,ah                   ; EMS ok?
  1945.         jnz     prep_no_ems
  1946. ;
  1947.         mov     ah,46h                  ; get EMS version
  1948.         int     EMM_INT
  1949.         or      ah,ah                   ; AH must be 0
  1950.         jnz     prep_no_ems
  1951. ;
  1952.         cmp     al,30h                  ; >= version 3.0?
  1953.         jb      prep_no_ems
  1954. ;
  1955.         mov     ah,41h                  ; Get page frame address
  1956.         int     EMM_INT
  1957.         or      ah,ah
  1958.         jnz     prep_no_ems
  1959. ;
  1960. ;       EMS present, try to allocate pages
  1961. ;
  1962.         mov     prep.ems_pageframe,bx
  1963.         mov     bx,totparas
  1964.         add     bx,ems_paramask
  1965.         mov     cl,ems_shift
  1966.         shr     bx,cl
  1967.         mov     ah,43h                  ; allocate handle and pages
  1968.         int     EMM_INT
  1969.         or      ah,ah                   ; success?
  1970.         jnz     prep_no_ems
  1971. ;
  1972. ;       EMS pages allocated, swap to EMS
  1973. ;
  1974.         mov     prep.handle,dx
  1975.         mov     ax,USE_EMS
  1976.         mov     prep.swapmethod,al
  1977.         ret
  1978. ;
  1979. ;       No EMS allowed, or EMS not present/full. Try XMS.
  1980. ;
  1981. prep_no_ems:
  1982.         test    pmethod,XMS_FIRST
  1983.         jnz     check_file              ; don't try again
  1984. ;
  1985. check_xms:
  1986.         test    pmethod,USE_XMS
  1987.         jz      prep_no_xms
  1988. ;
  1989.         mov     ax,4300h                ; check if XMM driver present
  1990.         int     2fh
  1991.         cmp     al,80h                  ; is XMM installed?
  1992.         jne     prep_no_xms
  1993.         mov     ax,4310h                ; get XMM entrypoint
  1994.         int     2fh
  1995.         mov     word ptr prep.xmm,bx    ; save entry address
  1996.         mov     word ptr prep.xmm+2,es
  1997. ;
  1998.         mov     dx,totparas
  1999.         add     dx,xms_paramask         ; round to nearest multiple of 1k
  2000.         mov     cl,xms_shift
  2001.         shr     dx,cl                   ; convert to k
  2002.         mov     ah,9                    ; allocate extended memory block
  2003.         call    dword ptr ds:[prep.xmm]
  2004.         or      ax,ax
  2005.         jz      prep_no_xms
  2006. ;
  2007. ;       XMS block allocated, swap to XMS
  2008. ;
  2009.         mov     prep.handle,dx
  2010.         mov     ax,USE_XMS
  2011.         mov     prep.swapmethod,al
  2012.         ret
  2013. ;
  2014. ;       No XMS allowed, or XMS not present/full. Try File swap.
  2015. ;
  2016. prep_no_xms:
  2017.         test    pmethod,XMS_FIRST
  2018.         jz      check_file
  2019.         jmp     check_ems
  2020. ;
  2021. check_file:
  2022.         test    pmethod,USE_FILE
  2023.         jnz     prep_do_file
  2024.         jmp     prep_no_file
  2025. ;
  2026. prep_do_file:
  2027.         push    ds
  2028.         IF      ptrsize
  2029.         lds     dx,swapfname
  2030.         ELSE
  2031.         mov     dx,swapfname
  2032.         ENDIF
  2033.         IFDEF   PASCAL_DEF
  2034.         inc     dx                      ; skip length byte
  2035.         ENDIF
  2036.         mov     cx,2                    ; hidden attribute
  2037.         test    pmethod,HIDE_FILE
  2038.         jnz     prep_hide
  2039.         xor     cx,cx                   ; normal attribute
  2040. ;
  2041. prep_hide:
  2042.         mov     ah,3ch                  ; create file
  2043.         test    pmethod,CREAT_TEMP
  2044.         jz      prep_no_temp
  2045.         mov     ah,5ah
  2046. ;
  2047. prep_no_temp:
  2048.         int     21h                     ; create/create temp
  2049.         jnc     prep_got_file
  2050.         jmp     prep_no_file
  2051. ;
  2052. prep_got_file:
  2053.         mov     bx,ax                   ; handle
  2054. ;
  2055. ;       save the file name
  2056. ;
  2057.         pop     es
  2058.         push    es
  2059.         mov     di,offset prep.swapfilename
  2060.         mov     cx,81
  2061.         mov     si,dx
  2062.         rep movsb
  2063. ;
  2064.         pop     ds
  2065.         mov     prep.handle,bx
  2066. ;
  2067. ;       preallocate the file
  2068. ;
  2069.         test    pmethod,NO_PREALLOC
  2070.         jnz     prep_noprealloc
  2071.         test    pmethod,CHECK_NET
  2072.         jz      prep_nonetcheck
  2073. ;
  2074. ;       check whether file is on a network drive, and don't preallocate
  2075. ;       if so. preallocation can slow down swapping significantly when
  2076. ;       running on certain networks (Novell)
  2077. ;
  2078.         mov     ax,440ah        ; check if handle is remote
  2079.         int     21h
  2080.         jc      prep_nonetcheck ; assume not remote if function fails
  2081.         test    dh,80h          ; DX bit 15 set ?
  2082.         jnz     prep_noprealloc ; remote if yes
  2083. ;
  2084. prep_nonetcheck:
  2085.         mov     dx,totparas
  2086.         mov     cl,4
  2087.         rol     dx,cl
  2088.         mov     cx,dx
  2089.         and     dx,0fff0h
  2090.         and     cx,0000fh
  2091.         sub     dx,1
  2092.         sbb     cx,0
  2093.         mov     si,dx                   ; save
  2094.         mov     ax,4200h                ; move file pointer, absolute
  2095.         int     21h
  2096.         jc      prep_file_err
  2097.         cmp     dx,cx
  2098.         jne     prep_file_err
  2099.         cmp     ax,si
  2100.         jne     prep_file_err
  2101.         mov     cx,1                    ; write 1 byte
  2102.         mov     ah,40h
  2103.         int     21h
  2104.         jc      prep_file_err
  2105.         cmp     ax,cx
  2106.         jne     prep_file_err
  2107. ;
  2108.         mov     ax,4200h                ; move file pointer, absolute
  2109.         xor     dx,dx
  2110.         xor     cx,cx                   ; rewind to beginning
  2111.         int     21h
  2112.         jc      prep_file_err
  2113. ;
  2114. prep_noprealloc:
  2115.         mov     ax,USE_FILE
  2116.         mov     prep.swapmethod,al
  2117.         ret
  2118. ;
  2119. prep_file_err:
  2120.         mov     ah,3eh                  ; close file
  2121.         int     21h
  2122.         mov     dx,offset prep.swapfilename
  2123.         mov     ah,41h                  ; delete file
  2124.         int     21h
  2125. ;
  2126. prep_no_file:
  2127.         mov     ax,-1
  2128.         mov     prep.swapmethod,al
  2129.         ret
  2130. ;
  2131. prep_swap       endp
  2132. ;
  2133.         end
  2134.  
  2135.