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