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