;MODIFIED 5th October 1995 by Matthew Parker all changes are public domain.
; SPAWN.ASM - Main function for memory swapping spawn call.
;
; Public Domain Software written by
; Thomas Wagner
; Ferrari electronic GmbH
; Beusselstrasse 27
; D-1000 Berlin 21
; Germany
;
; Modified extensively by Matthew Parker, 3:711/934.31@fidonet, in late
; 1995, in order to make it assemble with Watcom 10.0's assembler.
; All changes are also public domain. The following problems were
; encountered with WASM:
;
; 1. TYPE does not work on struc's
; 2. The symbol "addr" is confused as a instruction
; 3. Some assumptions about indirect addressing are wrong
; when using struc's. Notably when making far indirect calls.
; 4. LOCAL statement doesn't like "local a, b, c" must use
; "local a:word, b:word, c:word" it doesn't assume word length.
; 5. Segment override not allowed to be made an equate
; 6. Full PROC syntax not supported.
; 7. When model set @DataSize is not defined.
; 8. "arg" and "uses" is not supported after the PROC definition.
; 9. Cannot use "SIZE" to declare array size in variable declaration
;
; to assembler with WASM you must define "WATCOM".
;
;
; Assemble with
;
; tasm /DPASCAL_DEF spawn,spawnp - Turbo Pascal (Tasm only), near
; tasm /DPASCAL_DEF /DFARCALL spawn,spawnp - Turbo Pascal (Tasm only), far
; ?asm spawn; - C, default model (small)
; ?asm /DMODL=large spawn - C, large model
;
; NOTE: For C, change the 'model' directive below according to your
; memory model, or define MODL=xxx on the command line.
;
; For Turbo C Huge model, you must give /DTC_HUGE on the
; command line, or define it here.
;
;
; Main function:
;
; PASCAL:
; function do_spawn (swapping: integer;
; execfname: string;
; cmdtail: string;
; envlen: word;
; var envp)
;
; C:
; int do_spawn (int swapping,
; char *execfname,
; char *cmdtail,
; unsigned envlen,
; char *envp)
;
; Parameters:
;
; swapping - swap/spawn/exec function:
; < 0: Exec, don't swap
; 0: Spawn, don't swap
; > 0: Spawn, swap
; in this case, prep_swap must have
; been called beforehand (see below).
;
; cmdtail - command tail for EXEC.
;
; execfname - name and path of file to execute.
;
; envlen - length of environment copy (may be 0).
;
; envp - pointer to environment block (must be aligned on
; paragraph boundary). Unused if envlen is 0.
;
; 'cmdtail' and 'execfname' must be zero terminated, even when
; calling from Pascal. For Pascal, the length byte of the string
; is ignored.
;
; Returns:
; 0000..00ff: Returncode of EXECed program
; 03xx: DOS-Error xx calling EXEC
; 0500: Swapping requested, but prep_swap has not
; been called or returned an error
; 0501: MCBs don't match expected setup
; 0502: Error while swapping out
;
;
; For swapping, the swap method must be prepared before calling do_spawn.
;
; PASCAL:
; function prep_swap (method: word; swapfname: string): integer;
; C:
; int prep_swap (unsigned method, char *swapfname)
;
; Parameters:
;
; method - bit-map of allowed swap devices:
; 01 - Allow EMS
; 02 - Allow XMS
; 04 - Allow File swap
; 10 - Try XMS first, then EMS
; 40 - Create file as "hidden"
; 80 - Use "create temp" call for file swap
; 100 - Don't preallocate file
; 200 - Check for Network, don't preallocate if net
; 4000 - Environment block will not be swapped
;
; swapfname - swap file name (may be undefined if the
; "method" parameters disallows file swap).
; The string must be zero terminated, even
; when calling from Pascal. For Pascal, the
; length byte of the string is ignored.
;
; Returns:
;
; A positive integer on success:
; 1 - EMS swap initialized
; 2 - XMS swap initialized
; 4 - File swap initialized
; A negative integer on failure:
; -1 - Couldn't allocate swap space
; -2 - The spawn module is located too low in memory
;
;
.model large,c
;
IFNDEF WATCOM
ptrsize = @DataSize
ELSE
do_spawn equ _do_spawn
prep_swap equ _prep_swap
_psp equ __psp
ptrsize = 1
ENDIF
;
extrn _psp: word
;
public do_spawn
public prep_swap
;
stacklen = 256 ; local stack
;
; "ems_size" is the EMS block size: 16k.
;
ems_size = 16 * 1024 ; EMS block size
ems_parasize = ems_size / 16 ; same in paragraphs
ems_shift = 10 ; shift factor for paragraphs
ems_paramask = ems_parasize-1 ; block mask
;
; "xms_size" is the unit of measurement for XMS: 1k
;
xms_size = 1024 ; XMS block size
xms_parasize = xms_size / 16 ; same in paragraphs
xms_shift = 6 ; shift factor for paragraphs
xms_paramask = xms_parasize-1 ; block mask
;
; Method flags
;
USE_EMS = 01h
USE_XMS = 02h
USE_FILE = 04h
XMS_FIRST = 10h
HIDE_FILE = 40h
CREAT_TEMP = 80h
NO_PREALLOC = 100h
CHECK_NET = 200h
DONT_SWAP_ENV = 4000h
;
; Return codes
;
RC_TOOLOW = 0102h
RC_BADPREP = 0500h
RC_MCBERROR = 0501h
RC_SWAPERROR = 0502h
;
EMM_INT = 67h
;
; The EXEC function parameter block
;
exec_block struc
envseg dw ? ; environment segment
ppar dw ? ; program parameter string offset
pparseg dw ? ; program parameter string segment
fcb1 dw ? ; FCB offset
fcb1seg dw ? ; FCB segment
fcb2 dw ? ; FCB offset
fcb2seg dw ? ; FCB segment
exec_block ends
exec_block_size=0eh
;
; Structure of an XMS move control block
;
xms_control struc
lenlo dw ? ; length to move (doubleword)
lenhi dw ?
srchnd dw ? ; source handle (0 for standard memory)
srclo dw ? ; source address (doubleword or seg:off)
srchi dw ?
desthnd dw ? ; destination handle (0 for standard memory)
destlo dw ? ; destination address (doubleword or seg:off)
desthi dw ?
xms_control ends
xms_control_size=10h
;
; The structure of the start of an MCB (memory control block)
;
mcb struc
id db ?
owner dw ?
paras dw ?
mcb ends
;
; The structure of an internal MCB descriptor.
; CAUTION: This structure is assumed to be no larger than 16 bytes
; in several places in the code, and to be exactly 16 bytes when
; swapping in from file. Be careful when changing this structure.
;
mcbdesc struc
__addr dw ? ;For wasm ; paragraph address of the MCB
msize dw ? ; size in paragraphs (excluding header)
swoffset dw ? ; swap offset (0 in all blocks except first)
swsize dw ? ; swap size (= msize + 1 except in first)
num_follow dw ? ; number of following MCBs
dw 3 dup(?) ; pad to paragraph (16 bytes)
mcbdesc ends
mcbdesc_size=10h
;
; The variable block set up by prep_swap
;
prep_block struc
xmm dd ? ; XMM entry address
first_mcb dw ? ; Segment of first MCB
psp_mcb dw ? ; Segment of MCB of our PSP
env_mcb dw ? ; MCB of Environment segment
noswap_mcb dw ? ; MCB that may not be swapped
ems_pageframe dw ? ; EMS page frame address
handle dw ? ; EMS/XMS/File handle
total_mcbs dw ? ; Total number of MCBs
swapmethod db ? ; Method for swapping
swapfilename db 81 dup(?) ; Swap file name if swapping to file
prep_block ends
prep_block_size=64h
;
;----------------------------------------------------------------------
;
; Since we'll be moving code and data around in memory,
; we can't address locations in the resident block with
; normal address expressions. MASM does not support
; defining variables with a fixed offset, so we have to resort
; to a kludge, and define the shrunk-down code as a structure.
; It would also be possible to use an absolute segment for the
; definition, but this is not supported by the Turbo Pascal linker.
;
; All references to low-core variables from low-core itself
; are made through DS, so we define a text macro "lmem" that
; expands to "ds:". When setting up low core from the normal
; code, ES is used to address low memory, so this can't be used.
;
;lmem equ <ds:>
;
; The memory structure for the shrunk-down code, excluding the
; code itself. The code follows this block.
;
parseg struc
db 2ch dup(?)
psp_envptr dw ?
db 5ch-2eh dup(?) ; start after PSP
;
save_ss dw ? ; 5C - saved global ss
save_sp dw ? ; 5E - saved global sp
xfcb1 db 16 dup(?) ; 60..6F - default FCB
xfcb2 db 16 dup(?) ; 70..7F - default FCB
zero dw ? ; 80 Zero command tail length (dummy)
;
expar db SIZE exec_block dup (?) ; exec-parameter-block
spx dw ? ; saved local sp
div0_off dw ? ; divide by zero vector save
div0_seg dw ?
filename db 82 dup(?) ; exec filename
progpars db 128 dup(?) ; command tail
db stacklen dup(?) ; local stack space
mystack db ?
lprep db SIZE prep_block dup(?) ; the swapping variables
lcurrdesc db SIZE mcbdesc dup(?) ; the current MCB descriptor
lxmsctl db SIZE xms_control dup(?)
eretcode dw ? ; EXEC return code
retflags dw ? ; EXEC return flags
cgetmcb dw ? ; address of get_mcb
;
parseg_size=$-parseg
parseg ends
;
IFNDEF WATCOM
param_len = (((SIZE parseg + 1) / 2) * 2) ; make even
ELSE
param_len = (((SIZE parseg + 1) / 2) * 2 + 200h) ; make even
ENDIF
codebeg = param_len
;
.code
;
;------------------------------------------------------------------------
;
lowcode_begin:
;
; The following parts of the program code will be moved to
; low core and executed there, so there must be no absolute
; memory references.
; The call to get_mcb must be made indirect, since the offset
; from the swap-in routine to get_mcb will not be the same
; after moving.
;
;
; get_mcb allocates a block of memory by modifying the MCB chain
; directly.
;
; On entry, lcurrdesc has the mcb descriptor for the block to
; allocate.
;
; On exit, Carry is set if the block couldn't be allocated.
;
; Uses AX, BX, CX, ES
; Modifies lprep.first_mcb
;
get_mcb proc near
;
mov ax,ds: lprep.first_mcb
mov bx,ds: lcurrdesc.__addr
;
getmcb_loop:
mov es,ax
cmp ax,bx
ja gmcb_abort ; halt if MCB > wanted
je mcb_found ; jump if same addr as wanted
add ax,es:paras ; last addr
inc ax ; next mcb
cmp ax,bx
jbe getmcb_loop ; Loop if next <= wanted
;
;
; The wanted MCB starts within the current MCB. We now have to
; create a new MCB at the wanted position, which is initially
; free, and shorten the current MCB to reflect the reduced size.
;
cmp es:owner,0
jne gmcb_abort ; halt if not free
mov bx,es ; current
inc bx ; + 1 (header doesn't count)
mov ax,ds: lcurrdesc.__addr
sub ax,bx ; paragraphs between MCB and wanted
mov bx,es:paras ; paras in current MCB
sub bx,ax ; remaining paras
dec bx ; -1 for header
mov es:paras,ax ; set new size for current
mov cl,es:id ; old id
mov es:id,4dh ; set id: there is a next
mov ax,ds: lcurrdesc.__addr
mov es,ax
mov es:id,cl ; and init to free
mov es:owner,0
mov es:paras,bx
;
; We have found an MCB at the right address. If it's not free,
; abort. Else check the size. If the size is ok, we're done
; (more or less).
;
mcb_found:
mov es,ax
cmp es:owner,0
je mcb_check ; continue if free
;
gmcb_abort:
stc
ret
;
mcb_check:
mov ax,es:paras ; size
cmp ax,ds: lcurrdesc.msize ; needed size
jae mcb_ok ; ok if enough space
;
; If there's not enough room in this MCB, check if the next
; MCB is free, too. If so, coalesce both MCB's and check again.
;
cmp es:id,4dh
jnz gmcb_abort ; halt if no next
push es ; save current
mov bx,es
add ax,bx
inc ax ; next MCB
mov es,ax
cmp es:owner,0 ; next free ?
jne gmcb_abort ; halt if not
mov ax,es:paras ; else load size
inc ax ; + 1 for header
mov cl,es:id ; and load ID
pop es ; back to last MCB
add es:paras,ax ; increase size
mov es:id,cl ; and store ID
jmp mcb_check ; now try again
;
; The MCB is free and large enough. If it's larger than the
; wanted size, create another MCB after the wanted.
;
mcb_ok:
mov bx,es:paras
sub bx,ds: lcurrdesc.msize
jz mcb_no_next ; ok, no next to create
push es
dec bx ; size of next block
mov ax,es
add ax,ds: lcurrdesc.msize
inc ax ; next MCB addr
mov cl,es:id ; id of this block
mov es,ax ; address next
mov es:id,cl ; store id
mov es:paras,bx ; store size
mov es:owner,0 ; and mark as free
pop es ; back to old MCB
mov es:id,4dh ; mark next block present
mov ax,ds: lcurrdesc.msize ; and set size to wanted
mov es:paras,ax
;
mcb_no_next:
mov es:owner,cx ; set owner to current PSP
;
; Set the 'first_mcb' pointer to the current one, so we don't
; walk through all the previous blocks the next time.
; Also, check if the block we just allocated is the environment
; segment of the program. If so, restore the environment pointer
; in the PSP.
;
mov ax,es
mov ds: lprep.first_mcb,ax
cmp ds: lprep.env_mcb,ax
jne getmcb_finis
inc ax
mov ds: psp_envptr,ax
;
getmcb_finis:
clc
ret ; all finished (whew!)
;
get_mcb endp
;
;
ireti:
iret
;
;
; The actual EXEC call.
; Registers on entry:
; BX = paragraphs to keep (0 if no swap)
; CX = length of environment to copy (words) or zero
; DS:SI = environment source
; ES:DI = environment destination
; (ES = our low core code segment)
;
;
; copy environment buffer down if present
;
doexec:
jcxz noenvcpy
rep movsw
;
noenvcpy:
push es ; DS = ES = low core = PSP
pop ds
or bx,bx
jz no_shrink
;
; first, shrink the base memory block down.
;
mov ah,04ah
int 21h ; resize memory block
;
; Again walk all MCBs. This time, all blocks owned by the
; current process are released.
;
mov si,ds: lprep.first_mcb
or si,si
jz no_shrink
mov dx,ds: lprep.psp_mcb
mov bx,dx
inc bx ; base PSP (MCB owner)
mov di,ds: lprep.noswap_mcb
;
free_loop:
cmp si,dx
je free_next ; don't free base block
cmp si,di
je free_next
mov es,si
cmp bx,es:owner ; our process?
jne free_next ; next if not
cmp si,ds: lprep.env_mcb ; is this the environment block?
jne free_noenv
mov ds:psp_envptr,0 ; else clear PSP pointer
;
free_noenv:
inc si
mov es,si
dec si
mov ah,049h ; free memory block
int 21h
;
free_next:
mov es,si
cmp es:id,4dh ; normal block?
jne free_ready ; ready if end of chain
add si,es:paras ; start + length
inc si ; next MCB
jmp free_loop
;
free_ready:
mov ax,ds
mov es,ax
;
no_shrink:
mov dx,filename ; params for exec
mov bx,expar
mov ax,04b00h
int 21h ; exec
;
; Return from EXEC system call. Don't count on any register except
; CS to be restored (DOS 2.11 and previous versions killed all regs).
;
mov bx,cs
mov ds,bx
mov es,bx
mov ss,bx
mov sp,ds: spx
cld
mov ds: eretcode,ax ; save return code
pushf
pop bx
mov ds: retflags,bx ; and returned flags
;
cmp ds: lprep.swapmethod,0
je exec_memok
jg exec_expand
;
; Terminate.
;
test bx,1 ; carry?
jnz exec_term ; use EXEc retcode if set
mov ah,4dh ; else get program return code
int 21h
;
exec_term:
mov ah,4ch
int 21h
;
;
exec_expand:
mov ah,4ah ; expand memory
mov bx,ds: lcurrdesc.msize
int 21h
jnc exec_memok
mov ax,4cffh
int 21h ; terminate on error
;
; Swap memory back
;
nop
;
exec_memok:
;
; FALL THROUGH to the appropriate swap-in routine
;
;
getmcboff = offset get_mcb - offset lowcode_begin
iretoff = offset ireti - offset lowcode_begin
doexec_entry = offset doexec - offset lowcode_begin
base_length = offset $ - offset lowcode_begin
;
;-----------------------------------------------------------------------
;
; The various swap in routines follow. Only one of the routines
; is copied to low memory.
; Note that the routines are never actually called, the EXEC return
; code falls through. The final RET thus will return to the restored
; memory image.
;
; On entry, DS must point to low core.
; On exit to the restored code, DS is unchanged.
;
;
; swapin_ems: swap in from EMS.
;
swapin_ems proc far
;
xor bx,bx
mov si,ems_parasize
mov dx,ds: lprep.handle ; EMS handle
;
swinems_main:
push ds
mov cx,ds: lcurrdesc.swsize ; block length in paras
mov di,ds: lcurrdesc.swoffset ; swap offset
mov es,ds: lcurrdesc.__addr ; segment to swap
mov ds,ds: lprep.ems_pageframe ; page frame address
;
mov ax,ems_parasize ; max length
sub ax,si ; minus current offset
jnz swinems_ok ; go copy if nonzero
;
swinems_loop:
mov ax,4400h ; map in next page
int EMM_INT
or ah,ah
jnz swinems_error
mov si,0 ; reset offset
inc bx ; bump up page number
mov ax,ems_parasize ; max length to copy
;
swinems_ok:
cmp ax,cx ; length to copy
jbe swinems_doit ; go do it if <= total length
mov ax,cx ; else use total length
;
swinems_doit:
sub cx,ax ; subtract copy length from total
push cx ; and save
push ax ; save the copy length in paras
push si
push di
mov cl,3
shl ax,cl ; convert to number of words (!)
inc cl
shl si,cl ; convert to byte address
mov cx,ax
rep movsw
pop di
pop si
pop cx ; copy length in paras
mov ax,es
add ax,cx ; add copy length to dest segment
add si,cx ; and EMS page offset
mov es,ax
pop cx ; remaining length
or cx,cx ; did we copy everything?
jnz swinems_loop ; go loop if not
;
pop ds
cmp ds: lcurrdesc.num_follow,0 ; another MCB?
je swinems_complete ; exit if not
;
; Another MCB follows, read next mcb descriptor into currdesc
;
cmp si,ems_parasize
jb swinems_nonewpage ; no new block needed
mov ax,4400h ; map page, phys = 0
int EMM_INT
or ah,ah
jnz swinems_error1
mov si,0
inc bx
;
swinems_nonewpage:
push si
push ds
mov ax,ds
mov es,ax
mov ds,ds: lprep.ems_pageframe ; page frame address
mov cl,4
shl si,cl ; convert to byte address
mov cx,SIZE mcbdesc
mov di,offset lcurrdesc
rep movsb
pop ds
pop si
inc si ; one paragraph
;
push bx
call ds: cgetmcb
pop bx
jc swinems_error1
jmp swinems_main
;
swinems_complete:
mov ah,45h ; release EMS pages
int EMM_INT
ret
;
swinems_error:
pop ds
swinems_error1:
mov ah,45h ; release EMS pages on error
int EMM_INT
mov ax,4cffh
int 21h ; terminate
;
swapin_ems endp
;
swinems_length = offset $ - offset swapin_ems
;
;
; swapin_xms: swap in from XMS.
;
swapin_xms proc far
;
mov ax,ds: lprep.handle ; XMS handle
mov ds: lxmsctl.srchnd,ax ; source is XMS
mov ds: lxmsctl.desthnd,0 ; dest is normal memory
mov ds: lxmsctl.srclo,0
mov ds: lxmsctl.srchi,0
;
swinxms_main:
mov ax,ds: lcurrdesc.swsize ; size in paragraphs
mov cl,4
rol ax,cl ; size in bytes + high nibble
mov dx,ax
and ax,0fff0h ; low word
and dx,0000fh ; high word
mov ds: lxmsctl.lenlo,ax ; into control block
mov ds: lxmsctl.lenhi,dx
mov ax,ds: lcurrdesc.swoffset ; swap offset
mov ds: lxmsctl.destlo,ax ; into control block
mov ax,ds: lcurrdesc.__addr ; segment to swap
mov ds: lxmsctl.desthi,ax
mov si,offset lxmsctl
mov ah,0bh
call dword ptr ds:[lprep.xmm] ; move it
or ax,ax
jz swinxms_error
mov ax,ds: lxmsctl.lenlo ; adjust source addr
add ds: lxmsctl.srclo,ax
mov ax,ds: lxmsctl.lenhi
adc ds: lxmsctl.srchi,ax
;
cmp ds: lcurrdesc.num_follow,0 ; another MCB?
je swinxms_complete
;
mov ds: lxmsctl.lenlo,SIZE mcbdesc
mov ds: lxmsctl.lenhi,0
mov ds: lxmsctl.desthi,ds
mov ds: lxmsctl.destlo,offset lcurrdesc
mov si,offset lxmsctl
mov ah,0bh
call dword ptr ds:[lprep.xmm] ; move it
or ax,ax
jz swinxms_error
add ds: lxmsctl.srclo,16 ; one paragraph
adc ds: lxmsctl.srchi,0
;
call ds: cgetmcb
jc swinxms_error
jmp swinxms_main
;
swinxms_complete:
mov ah,0ah ; release XMS frame
mov dx,ds: lprep.handle ; XMS handle
call dword ptr ds:[lprep.xmm]
ret
;
swinxms_error:
mov ah,0ah ; release XMS frame on error
call dword ptr ds:[lprep.xmm]
mov ax,4c00h
int 21h
;
swapin_xms endp
;
swinxms_length = offset $ - offset swapin_xms
;
;
; swapin_file: swap in from file.
;
swapin_file proc far
;
IFNDEF WATCOM
mov dx,offset lprep+offset swapfilename; for wasm ;offset lprep.swapfilename
ELSE
mov dx,offset lprep+13h
ENDIF
mov ax,3d00h ; open file
int 21h
jc swinfile_error2
mov bx,ax ; file handle
;
swinfile_main:
push ds
mov cx,ds: lcurrdesc.swsize ; size in paragraphs
mov dx,ds: lcurrdesc.swoffset ; swap offset
mov ds,ds: lcurrdesc.__addr ; segment to swap
;
swinfile_loop:
mov ax,cx
cmp ah,8h ; above 32k?
jbe swinfile_ok ; go read if not
mov ax,800h ; else read 32k
;
swinfile_ok:
sub cx,ax ; remaining length
push cx ; save it
push ax ; and save paras to read
mov cl,4
shl ax,cl ; convert to bytes
mov cx,ax
mov ah,3fh ; read
int 21h
jc swinfile_error
cmp ax,cx
jne swinfile_error
pop cx ; paras read
mov ax,ds
add ax,cx ; bump up dest segment
mov ds,ax
pop cx ; remaining length
or cx,cx ; anything left?
jnz swinfile_loop ; go loop if yes
;
pop ds
cmp ds: lcurrdesc.num_follow,0 ; another MCB?
je swinfile_complete ; ready if not
mov cx,16 ; read one paragraph
mov dx,offset lcurrdesc
mov ah,3fh
int 21h
jc swinfile_error1
cmp ax,cx
jne swinfile_error1
;
push bx
call ds: cgetmcb
pop bx
jc swinfile_error1
jmp swinfile_main
;
;
swinfile_complete:
mov ah,3eh ; close file
int 21h
IFNDEF WATCOM
mov dx,offset lprep+offset swapfilename; for wasm ;offset lprep.swapfilename
ELSE
mov dx,offset lprep+13h
ENDIF
mov ah,41h ; delete file
int 21h
ret
;
swinfile_error:
pop cx
pop cx
pop ds
swinfile_error1:
mov ah,3eh ; close file
int 21h
swinfile_error2:
IFNDEF WATCOM
mov dx,offset lprep+offset swapfilename; for wasm ;offset lprep.swapfilename
ELSE
mov dx,offset lprep+13h
ENDIF
mov ah,41h ; delete file
int 21h
mov ax,4cffh
int 21h
;
swapin_file endp
;
swinfile_length = offset $ - offset swapin_file
;
;
; swapin_none: no swap, return immediately.
;
swapin_none proc far
;
ret
;
swapin_none endp
;
;
IF swinems_length GT swinxms_length
swcodelen = swinems_length
ELSE
swcodelen = swinxms_length
ENDIF
IF swinfile_length GT swcodelen
swcodelen = swinfile_length
ENDIF
;
swap_codelen = ((swcodelen + 1) / 2) * 2
;
codelen = base_length + swap_codelen
reslen = codebeg + codelen
keep_paras = (reslen + 15) shr 4 ; paragraphs to keep
swapbeg = keep_paras shl 4 ; start of swap space
savespace = swapbeg - 5ch ; length of overwritten area
;
;--------------------------------------------------------------------
;
IFDEF PASCAL_DEF
.data
ELSE
IFDEF TC_HUGE
.fardata? my_data
ELSE
.data?
ENDIF
ENDIF
;
;
; Space for saving the part of the memory image below the
; swap area that is overwritten by our code.
;
save_dat db savespace dup(?)
;
; Variables used while swapping out.
; The "prep" structure is initialized by prep_swap.
;
prep prep_block <>
nextmcb mcbdesc <>
currdesc mcbdesc <>
xmsctl xms_control <>
ems_curpage dw ? ; current EMS page number
ems_curoff dw ? ; current EMS offset (paragraph)
;
;--------------------------------------------------------------------
;
.code
;
; swapout_ems: swap out an MCB block to EMS.
;
; Entry: "currdesc" contains description of block to swap
; "nextmcb" contains MCB-descriptor of next block
; if currdesc.num_follow is nonzero
;
; Exit: 0 if OK, != 0 if error, Zero-flag set accordingly.
;
; Uses: All regs excpt DS
;
swapout_ems proc near
;
push ds
mov cx,currdesc.swsize ; block length in paras
mov si,currdesc.swoffset ; swap offset
mov dx,prep.handle ; EMS handle
mov bx,ems_curpage ; current EMS page
mov di,ems_curoff ; current EMS page offset (paras)
mov es,prep.ems_pageframe ; page frame address
mov ds,currdesc.__addr ; segment to swap
;
mov ax,ems_parasize ; max length
sub ax,di ; minus current offset
jnz swems_ok ; go copy if there's room
;
swems_loop:
mov ax,4400h ; map in next page
int EMM_INT
or ah,ah
jnz swems_error
mov di,0 ; reset offset
inc bx ; bump up page number
mov ax,ems_parasize ; max length to copy
;
swems_ok:
cmp ax,cx ; length to copy
jbe swems_doit ; go do it if <= total length
mov ax,cx ; else use total length
;
swems_doit:
sub cx,ax ; subtract copy length from total
push cx ; and save
push ax ; save the copy length in paras
push si
push di
mov cl,3
shl ax,cl ; convert to number of words (!)
inc cl
shl di,cl ; convert to byte address
mov cx,ax
rep movsw
pop di
pop si
pop cx ; copy length in paras
mov ax,ds
add ax,cx ; add copy length to source segment
add di,cx ; and EMS page offset
mov ds,ax
pop cx ; remaining length
or cx,cx ; did we copy everything?
jnz swems_loop ; go loop if not
;
pop ds
cmp currdesc.num_follow,0 ; another MCB?
je swems_complete ; exit if not
;
; Another MCB follows, append nextmcb to save block.
;
cmp di,ems_parasize
jb swems_nonewpage ; no new block needed
mov ax,4400h ; map page, phys = 0
int EMM_INT
or ah,ah
jnz swems_error1
mov di,0
inc bx
;
swems_nonewpage:
push di
mov cl,4
shl di,cl ; convert to byte address
mov cx,SIZE mcbdesc
mov si,offset nextmcb
rep movsb
pop di
inc di ; one paragraph
;
swems_complete:
mov ems_curpage,bx
mov ems_curoff,di
xor ax,ax
ret
;
swems_error:
pop ds
swems_error1:
mov ah,45h ; release EMS pages on error
int EMM_INT
mov ax,RC_SWAPERROR
or ax,ax
ret
;
swapout_ems endp
;
;
; swapout_xms: swap out an MCB block to XMS.
;
; Entry: "currdesc" contains description of block to swap
; "nextmcb" contains MCB-descriptor of next block
; if currdesc.num_follow is nonzero
;
; Exit: 0 if OK, -1 if error, Zero-flag set accordingly.
;
; Uses: All regs excpt DS
;
swapout_xms proc near
;
mov ax,currdesc.swsize ; size in paragraphs
mov cl,4
rol ax,cl ; size in bytes + high nibble
mov dx,ax
and ax,0fff0h ; low word
and dx,0000fh ; high word
mov xmsctl.lenlo,ax ; into control block
mov xmsctl.lenhi,dx
mov xmsctl.srchnd,0 ; source is normal memory
mov ax,currdesc.swoffset ; swap offset
mov xmsctl.srclo,ax ; into control block
mov ax,currdesc.__addr ; segment to swap
mov xmsctl.srchi,ax
mov ax,prep.handle ; XMS handle
mov xmsctl.desthnd,ax
mov si,offset xmsctl
mov ah,0bh
call dword ptr ds:[prep.xmm] ; move it
or ax,ax
jz swxms_error
mov ax,xmsctl.lenlo ; adjust destination addr
add xmsctl.destlo,ax
mov ax,xmsctl.lenhi
adc xmsctl.desthi,ax
;
cmp currdesc.num_follow,0 ; another MCB?
je swxms_complete
;
mov xmsctl.lenlo,SIZE mcbdesc
mov xmsctl.lenhi,0
mov xmsctl.srchi,ds
mov xmsctl.srclo,offset nextmcb
mov si,offset xmsctl
mov ah,0bh
call dword ptr ds:[prep.xmm] ; move it
or ax,ax
jz swxms_error
add xmsctl.destlo,16 ; one paragraph
adc xmsctl.desthi,0
;
swxms_complete:
xor ax,ax
ret
;
swxms_error:
mov ah,0ah ; release XMS frame on error
mov dx,prep.handle ; XMS handle
call dword ptr ds:[prep.xmm]
mov ax,RC_SWAPERROR
or ax,ax
ret
;
swapout_xms endp
;
;
; swapout_file: swap out an MCB block to file.
;
; Entry: "currdesc" contains description of block to swap
; "nextmcb" contains MCB-descriptor of next block
; if currdesc.num_follow is nonzero
;
; Exit: 0 if OK, -1 if error, Zero-flag set accordingly.
;
; Uses: All regs excpt DS
;
swapout_file proc near
;
push ds
mov cx,currdesc.swsize ; size in paragraphs
mov bx,prep.handle ; file handle
mov dx,currdesc.swoffset ; swap offset
mov ds,currdesc.__addr ; segment to swap
;
swfile_loop:
mov ax,cx
cmp ah,8h ; above 32k?
jbe swfile_ok ; go write if not
mov ax,800h ; else write 32k
;
swfile_ok:
sub cx,ax ; remaining length
push cx ; save it
push ax ; and save paras to write
mov cl,4
shl ax,cl ; convert to bytes
mov cx,ax
mov ah,40h ; write
int 21h
jc swfile_error
cmp ax,cx
jne swfile_error
pop cx ; paras written
mov ax,ds
add ax,cx ; bump up source segment
mov ds,ax
pop cx ; remaining length
or cx,cx ; anything left?
jnz swfile_loop ; go loop if yes
;
pop ds
cmp currdesc.num_follow,0 ; another MCB?
je swfile_complete ; ready if not
mov cx,16 ; write one paragraph
mov dx,offset nextmcb
mov ah,40h
int 21h
jc swfile_error1
cmp ax,cx
jne swfile_error1
;
swfile_complete:
xor ax,ax
ret
;
swfile_error:
pop cx
pop cx
pop ds
swfile_error1:
mov ah,3eh ; close file
int 21h
mov dx,offset prep.swapfilename
mov ah,41h ; delete file
int 21h
mov ax,RC_SWAPERROR
or ax,ax
ret
;
swapout_file endp
;
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
;
;
IFDEF PASCAL_DEF
IFDEF FARCALL
do_spawn PROC far swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
ELSE
do_spawn PROC near swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
ENDIF
ELSE
IFDEF WATCOM
do_spawn PROC uses si di, swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
ELSE
do_spawn PROC uses si di, swapping: word, execfname: ptr, params: ptr, envlen: word, envp: ptr
ENDIF
ENDIF
local datseg:word, pspseg:word, currmcb:word
;
IFDEF TC_HUGE
mov ax,SEG my_data
mov ds,ax
ENDIF
;
mov datseg,ds ; save default DS
;
IFDEF PASCAL_DEF
cld
mov bx,prefixseg
ELSE
IFDEF TC_HUGE
mov ax,SEG _psp
mov es,ax
mov bx,es:_psp
ELSE
mov bx,_psp
ENDIF
ENDIF
mov pspseg,bx
;
;
; Check if spawn is too low in memory
;
mov ax,cs
mov dx,offset lowcode_begin
mov cl,4
shr dx,cl
add ax,dx ; normalized start of this code
mov dx,keep_paras ; the end of the modified area
add dx,bx ; plus PSP = end paragraph
cmp ax,dx
ja doswap_ok ; ok if start of code > end of low mem
mov ax,RC_TOOLOW
ret
;
doswap_ok:
cmp word ptr swapping,0
jle method_ok
;
; check the swap method, to make sure prep_swap has been called
;
mov al,prep.swapmethod
cmp al,USE_EMS
je method_ok
cmp al,USE_XMS
je method_ok
cmp al,USE_FILE
je method_ok
mov ax,RC_BADPREP
ret
;
; Save the memory below the swap space.
; We must do this before swapping, so the saved memory is
; in the swapped out image.
; Anything else we'd want to save on the stack or anywhere
; else in "normal" memory also has to be saved here, any
; modifications done to memory after the swap will be lost.
;
; Note that the memory save is done even when not swapping,
; because we use some of the variables in low core for
; simplicity.
;
method_ok:
push ds
pop es
push ds
mov ds,pspseg ; DS points to PSP
mov si,5ch
mov di,offset save_dat
mov cx,savespace / 2 ; NOTE: savespace is always even
rep movsw
pop ds
;
mov ax,word ptr swapping
cmp ax,0
jg begin_swap
;
; not swapping, prep_swap wasn't called. Init those variables in
; the 'prep' block we need in any case.
;
mov prep.swapmethod,al
je no_reduce
;
mov ax,pspseg
dec ax
mov prep.psp_mcb,ax
mov prep.first_mcb,ax
inc ax
mov es,ax
mov bx,es:psp_envptr
mov prep.env_mcb,bx
mov prep.noswap_mcb,0
cmp word ptr envlen,0
jne swp_can_swap_env
mov prep.noswap_mcb,bx
;
swp_can_swap_env:
xor bx,bx
mov es,bx
mov ah,52h ; get list of lists
int 21h
mov ax,es
or ax,bx
jz no_reduce
mov es,es:[bx-2] ; first MCB
cmp es:id,4dh ; normal ID?
jne no_reduce
mov prep.first_mcb,es
;
no_reduce:
jmp no_swap1
;
; set up first block descriptor
;
begin_swap:
mov ax,prep.first_mcb
mov currmcb,ax
mov es,prep.psp_mcb ; let ES point to base MCB
mov ax,es:paras
mov currdesc.msize,ax
sub ax,keep_paras
mov currdesc.swsize,ax
mov currdesc.__addr,es
mov currdesc.swoffset,swapbeg + 16
; NOTE: swapbeg is 1 para higher when seen from MCB
mov ax,prep.total_mcbs
mov currdesc.num_follow,ax
;
; init other vars
;
mov xmsctl.destlo,0
mov xmsctl.desthi,0
mov ems_curpage,0
mov ems_curoff,ems_parasize
;
; Do the swapping. Each MCB block (except the last) has an
; "mcbdesc" structure appended that gives location and size
; of the next MCB.
;
swapout_main:
cmp currdesc.num_follow,0 ; next block?
je swapout_no_next ; ok if not
;
; There is another MCB block to be saved. So we don't have
; to do two calls to the save routine with complicated
; parameters, we set up the next MCB descriptor beforehand.
; Walk the MCB chain starting at the current MCB to find
; the next one belonging to this process.
;
mov ax,currmcb
mov bx,pspseg
mov cx,prep.psp_mcb
mov dx,prep.noswap_mcb
;
swm_mcb_walk:
mov es,ax
cmp ax,cx
je swm_next_mcb
cmp ax,dx
je swm_next_mcb
;
cmp bx,es:owner ; our process?
je swm_mcb_found ; found it if yes
;
swm_next_mcb:
cmp es:id,4dh ; normal block?
jne swm_mcb_error ; error if end of chain
add ax,es:paras ; start + length
inc ax ; next MCB
jmp swm_mcb_walk
;
; MCB found, set up an mcbdesc in the "nextmcb" structure
;
swm_mcb_found:
mov nextmcb.__addr,es
mov ax,es:paras ; get number of paragraphs
mov nextmcb.msize,ax ; and save
inc ax
mov nextmcb.swsize,ax
mov bx,es
add bx,ax
mov currmcb,bx
mov nextmcb.swoffset,0
mov ax,currdesc.num_follow
dec ax
mov nextmcb.num_follow,ax
;
swapout_no_next:
cmp prep.swapmethod,USE_EMS
je swm_ems
cmp prep.swapmethod,USE_XMS
je swm_xms
call swapout_file
jmp short swm_next
;
swm_ems:
call swapout_ems
jmp short swm_next
;
swm_xms:
call swapout_xms
;
swm_next:
jnz swapout_error
cmp currdesc.num_follow,0
je swapout_complete
;
; next MCB exists, copy the "nextmcb" descriptor into
; currdesc, and loop.
;
mov es,datseg
mov si,offset nextmcb
mov di,offset currdesc
mov cx,SIZE mcbdesc
rep movsb
jmp swapout_main
;
;
swm_mcb_error:
cmp prep.swapmethod,USE_FILE
je swm_mcberr_file
cmp prep.swapmethod,USE_EMS
je swm_mcberr_ems
;
mov ah,0ah ; release XMS frame on error
mov dx,prep.handle ; XMS handle
call dword ptr ds:[prep.xmm]
mov ax,RC_MCBERROR
jmp short swapout_error
;
swm_mcberr_ems:
mov dx,prep.handle ; EMS handle
mov ah,45h ; release EMS pages on error
int EMM_INT
mov ax,RC_MCBERROR
jmp short swapout_error
;
swm_mcberr_file:
mov ah,3eh ; close file
mov bx,prep.handle
int 21h
mov dx,offset prep.swapfilename
mov ah,41h ; delete file
int 21h
mov ax,RC_MCBERROR
;
swapout_error:
ret
;
;
; Swapout complete. Close the handle (EMS/file only),
; then set up low memory.
;
swapout_complete:
cmp prep.swapmethod,USE_FILE
jne swoc_nofile
;
; File swap: Close the swap file to make the handle available
;
mov bx,prep.handle
mov ah,3eh
int 21h ; close file
mov si,offset swapin_file
jnc swoc_ready
mov ax,RC_SWAPERROR
jmp swapout_error
;
swoc_nofile:
cmp prep.swapmethod,USE_EMS
jne swoc_xms
;
; EMS: Unmap page
;
mov ax,4400h
mov bx,-1
mov dx,prep.handle
int EMM_INT
mov si,offset swapin_ems
jmp short swoc_ready
;
swoc_xms:
mov si,offset swapin_xms
jmp short swoc_ready
;
no_swap1:
mov si,offset swapin_none
;
; Copy the appropriate swap-in routine to low memory.
;
swoc_ready:
mov es,pspseg
mov cx,swap_codelen / 2
mov di,codebeg + base_length
push ds
mov ax,cs
mov ds,ax
rep movsw
;
; And while we're at it, copy the MCB allocation routine (which
; also includes the initial MCB release and exec call) down.
;
mov cx,base_length / 2
mov di,param_len
mov si,offset lowcode_begin
rep movsw
;
pop ds
mov bx,es
dec bx
mov es,bx ; let ES point to base MCB
;
; Again set up the base MCB descriptor, and copy it as well as
; the variables set up by prep_swap to low memory.
; This isn't too useful if we're not swapping, but it doesn't
; hurt, either. The only variable used when not swapping is
; lprep.swapmethod.
;
mov ax,es:paras
mov currdesc.msize,ax
sub ax,keep_paras
mov currdesc.swsize,ax
mov currdesc.__addr,es
mov currdesc.swoffset,swapbeg + 16
mov ax,prep.total_mcbs
mov currdesc.num_follow,ax
;
mov es,pspseg ; ES points to PSP again
;
mov cx,SIZE prep_block
mov si,offset prep
mov di,offset lprep
rep movsb
mov cx,SIZE mcbdesc
mov si,offset currdesc
mov di,offset lcurrdesc
rep movsb
;
; now set up other variables in low core
;
mov es:cgetmcb,getmcboff + codebeg
mov es:eretcode,0
mov es:retflags,0
;
; Prepare exec parameter block
;
mov ax,es
mov es:expar.fcb1seg,ax
mov es:expar.fcb2seg,ax
mov es:expar.pparseg,ax
mov es:expar.envseg,0
;
; The 'zero' word is located at 80h in the PSP, the start of
; the command line. So as not to confuse MCB walking programs,
; a command line length of zero is inserted here.
;
mov es:zero,0d00h ; 00h,0dh = empty command line
;
; Init default fcb's by parsing parameter string
;
IF ptrsize
lds si,dword ptr params
ELSE
mov si,params
ENDIF
IFDEF PASCAL_DEF
inc si ; skip length byte
ENDIF
push si
mov di,xfcb1
mov es:expar.fcb1,di
push di
mov cx,16
xor ax,ax
rep stosw ; init both fcb's to 0
pop di
mov ax,2901h
int 21h
mov di,xfcb2
mov es:expar.fcb2,di
mov ax,2901h
int 21h
pop si
;
; move command tail string into low core
;
mov di,progpars
mov es:expar.ppar,di
xor cx,cx
inc di
cmdcpy:
lodsb
or al,al
jz cmdcpy_end
stosb
inc cx
jmp cmdcpy
;
cmdcpy_end:
mov al,0dh
stosb
mov es:progpars,cl
;
; move filename string into low core
;
IF ptrsize
lds si,dword ptr execfname
ELSE
mov si,execfname
ENDIF
IFDEF PASCAL_DEF
inc si
ENDIF
mov di,filename
fncpy:
lodsb
stosb
or al,al
jnz fncpy
;
; Setup environment copy
;
mov bx,keep_paras ; paras to keep
mov cx,word ptr envlen ; environment size
jcxz no_environ ; go jump if no environment
cmp word ptr swapping,0
jne do_envcopy
;
; Not swapping, use the environment pointer directly.
; Note that the environment copy must be paragraph aligned.
;
IF ptrsize
mov ax,word ptr envp+2
mov bx,word ptr envp
ELSE
mov ax,ds
mov bx,envp
ENDIF
add bx,15 ; make sure it's paragraph aligned
mov cl,4
shr bx,cl ; and convert to segment addr
add ax,bx
mov es:expar.envseg,ax ; new environment segment
xor cx,cx ; mark no copy
xor bx,bx ; and no shrink
jmp short no_environ
;
; Swapping or EXECing without return. Set up the pointers for
; an environment copy (we can't do the copy yet, it might overwrite
; this code).
;
do_envcopy:
inc cx
shr cx,1 ; words to copy
mov ax,cx ; convert envsize to paras
add ax,7
shr ax,1
shr ax,1
shr ax,1
add bx,ax ; add envsize to paras to keep
IF ptrsize
lds si,dword ptr envp
ELSE
mov si,envp
ENDIF
;
mov ax,es ; low core segment
add ax,keep_paras ; plus fixed paras
mov es:expar.envseg,ax ; = new environment segment
;
; Save stack regs, switch to local stack
;
no_environ:
mov es:save_ss,ss
mov es:save_sp,sp
mov ax,es
mov ss,ax
mov sp,offset mystack
;
push cx ; save env length
push si ; save env pointer
push ds ; save env segment
;
; save and patch INT0 (division by zero) vector
;
xor ax,ax
mov ds,ax
mov ax,word ptr ds:0
mov es:div0_off,ax
mov ax,word ptr ds:2
mov es:div0_seg,ax
mov word ptr ds:0,codebeg + iretoff
mov word ptr ds:2,es
;
pop ds ; pop environment segment
pop si ; pop environment offset
pop cx ; pop environment length
mov di,swapbeg ; environment destination
;
; Push return address on local stack
;
push cs ; push return segment
mov ax,offset exec_cont
push ax ; push return offset
mov es:spx,sp ; save stack pointer
;
; Goto low core code
;
push es ; push entry segment
mov ax,codebeg + doexec_entry
push ax ; push entry offset
; ret far ; can't use RET here because
db 0cbh ; of .model
;
;----------------------------------------------------------------
;
; Low core code will return to this location, with DS set to
; the PSP segment.
;
exec_cont:
push ds
pop es
mov ss,ds:save_ss ; reload stack
mov sp,ds:save_sp
;
; restore INT0 (division by zero) vector
;
xor cx,cx
mov ds,cx
mov cx,es:div0_off
mov word ptr ds:0,cx
mov cx,es:div0_seg
mov word ptr ds:2,cx
;
mov ax,es:eretcode
mov bx,es:retflags
mov ds,datseg
;
; Restore overwritten part of program
;
mov si,offset save_dat
mov di,5ch
mov cx,savespace
rep movsb
;
test bx,1 ; carry set?
jnz exec_fault ; return EXEC error code if fault
mov ah,4dh ; else get program return code
int 21h
ret
;
exec_fault:
mov ah,3 ; return error as 03xx
ret
;
do_spawn ENDP
;
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;
emm_name db 'EMMXXXX0'
;
; prep_swap - prepare for swapping.
;
; This routine checks all parameters necessary for swapping,
; and attempts to set up the swap-out area in EMS/XMS, or on file.
; In detail:
;
; 1) Check whether the do_spawn routine is located
; too low in memory, so it would get overwritten.
; If this is true, return an error code (-2).
;
; 2) Walk the memory control block chain, adding up the
; paragraphs in all blocks assigned to this process.
;
; 3) Check EMS (if the method parameter allows EMS):
; - is an EMS driver installed?
; - are sufficient EMS pages available?
; if all goes well, the EMS pages are allocated, and the
; routine returns success (1).
;
; 4) Check XMS (if the method parameter allows XMS):
; - is an XMS driver installed?
; - is a sufficient XMS block available?
; if all goes well, the XMS block is allocated, and the
; routine returns success (2).
;
; 5) Check file swap (if the method parameter allows it):
; - try to create the file
; - pre-allocate the file space needed by seeking to the end
; and writing a byte.
; If the file can be written, the routine returns success (4).
;
; 6) Return an error code (-1).
;
IFDEF PASCAL_DEF
IFDEF FARCALL
prep_swap PROC far pmethod: word, swapfname: dword
ELSE
prep_swap PROC near pmethod: word, swapfname: dword
ENDIF
ELSE
IFDEF WATCOM
prep_swap PROC uses si di, pmethod: word, swapfname: dword
ELSE
prep_swap PROC uses si di, pmethod: word, swapfname: ptr
ENDIF
ENDIF
LOCAL totparas: word
;
IFDEF TC_HUGE
mov ax,SEG my_data
mov ds,ax
ENDIF
;
IFDEF PASCAL_DEF
cld
mov ax,prefixseg
ELSE
IFDEF TC_HUGE
mov ax,SEG _psp
mov es,ax
mov ax,es:_psp
ELSE
mov ax,_psp
ENDIF
ENDIF
;
dec ax
mov prep.psp_mcb,ax
mov prep.first_mcb,ax ; init first MCB to PSP
;
; Make a copy of the environment pointer in the PSP
;
inc ax
mov es,ax
mov bx,es:psp_envptr
dec bx
mov prep.env_mcb,bx
mov prep.noswap_mcb,0
test pmethod,DONT_SWAP_ENV
jz can_swap_env
mov prep.noswap_mcb,bx
;
; Check if spawn is too low in memory
;
can_swap_env:
mov bx,cs
mov dx,offset lowcode_begin
mov cl,4
shr dx,cl
add bx,dx ; normalized start of this code
mov dx,keep_paras ; the end of the modified area
add dx,ax ; plus PSP = end paragraph
cmp bx,dx
ja prepswap_ok ; ok if start of code > end of low mem
mov ax,-2
mov prep.swapmethod,al
ret
;
; Walk the chain of memory blocks, adding up the paragraphs
; in all blocks belonging to this process.
; We try to find the first MCB by getting DOS's "list of lists",
; and fetching the word at offset -2 of the returned address.
; If this fails, we use our PSP as the starting point.
;
prepswap_ok:
xor bx,bx
mov es,bx
mov ah,52h ; get list of lists
int 21h
mov ax,es
or ax,bx
jz prep_no_first
mov es,es:[bx-2] ; first MCB
cmp es:id,4dh ; normal ID?
jne prep_no_first
mov prep.first_mcb,es
;
prep_no_first:
mov es,prep.psp_mcb ; ES points to base MCB
mov cx,es ; save this value
mov bx,es:owner ; the current process
mov dx,es:paras ; memory size in the base block
sub dx,keep_paras ; minus resident paragraphs
mov si,0 ; number of MCBs except base
mov di,prep.noswap_mcb
mov ax,prep.first_mcb
mov prep.first_mcb,0
;
prep_mcb_walk:
mov es,ax
cmp ax,cx ; base block?
je prep_walk_next ; then don't count again
cmp ax,di ; Non-swap MCB?
je prep_walk_next ; then don't count
;
cmp bx,es:owner ; our process?
jne prep_walk_next ; next if not
inc si
mov ax,es:paras ; else get number of paragraphs
add ax,2 ; + 1 for descriptor + 1 for MCB
add dx,ax ; total number of paras
cmp prep.first_mcb,0
jne prep_walk_next
mov prep.first_mcb,es
;
prep_walk_next:
cmp es:id,4dh ; normal block?
jne prep_mcb_ready ; ready if end of chain
mov ax,es
add ax,es:paras ; start + length
inc ax ; next MCB
jmp prep_mcb_walk
;
prep_mcb_ready:
mov totparas,dx
mov prep.total_mcbs,si
;
test pmethod,XMS_FIRST
jnz check_xms
;
; Check for EMS swap
;
check_ems:
test pmethod,USE_EMS
jz prep_no_ems
;
push ds
mov al,EMM_INT
mov ah,35h
int 21h ; get EMM int vector
mov ax,cs
mov ds,ax
mov si,offset emm_name
mov di,10
mov cx,8
repz cmpsb ; EMM name present?
pop ds
jnz prep_no_ems
;
mov ah,40h ; get EMS status
int EMM_INT
or ah,ah ; EMS ok?
jnz prep_no_ems
;
mov ah,46h ; get EMS version
int EMM_INT
or ah,ah ; AH must be 0
jnz prep_no_ems
;
cmp al,30h ; >= version 3.0?
jb prep_no_ems
;
mov ah,41h ; Get page frame address
int EMM_INT
or ah,ah
jnz prep_no_ems
;
; EMS present, try to allocate pages
;
mov prep.ems_pageframe,bx
mov bx,totparas
add bx,ems_paramask
mov cl,ems_shift
shr bx,cl
mov ah,43h ; allocate handle and pages
int EMM_INT
or ah,ah ; success?
jnz prep_no_ems
;
; EMS pages allocated, swap to EMS
;
mov prep.handle,dx
mov ax,USE_EMS
mov prep.swapmethod,al
ret
;
; No EMS allowed, or EMS not present/full. Try XMS.
;
prep_no_ems:
test pmethod,XMS_FIRST
jnz check_file ; don't try again
;
check_xms:
test pmethod,USE_XMS
jz prep_no_xms
;
mov ax,4300h ; check if XMM driver present
int 2fh
cmp al,80h ; is XMM installed?
jne prep_no_xms
mov ax,4310h ; get XMM entrypoint
int 2fh
mov word ptr prep.xmm,bx ; save entry address
mov word ptr prep.xmm+2,es
;
mov dx,totparas
add dx,xms_paramask ; round to nearest multiple of 1k
mov cl,xms_shift
shr dx,cl ; convert to k
mov ah,9 ; allocate extended memory block
call dword ptr ds:[prep.xmm]
or ax,ax
jz prep_no_xms
;
; XMS block allocated, swap to XMS
;
mov prep.handle,dx
mov ax,USE_XMS
mov prep.swapmethod,al
ret
;
; No XMS allowed, or XMS not present/full. Try File swap.
;
prep_no_xms:
test pmethod,XMS_FIRST
jz check_file
jmp check_ems
;
check_file:
test pmethod,USE_FILE
jnz prep_do_file
jmp prep_no_file
;
prep_do_file:
push ds
IF ptrsize
lds dx,swapfname
ELSE
mov dx,swapfname
ENDIF
IFDEF PASCAL_DEF
inc dx ; skip length byte
ENDIF
mov cx,2 ; hidden attribute
test pmethod,HIDE_FILE
jnz prep_hide
xor cx,cx ; normal attribute
;
prep_hide:
mov ah,3ch ; create file
test pmethod,CREAT_TEMP
jz prep_no_temp
mov ah,5ah
;
prep_no_temp:
int 21h ; create/create temp
jnc prep_got_file
jmp prep_no_file
;
prep_got_file:
mov bx,ax ; handle
;
; save the file name
;
pop es
push es
mov di,offset prep.swapfilename
mov cx,81
mov si,dx
rep movsb
;
pop ds
mov prep.handle,bx
;
; preallocate the file
;
test pmethod,NO_PREALLOC
jnz prep_noprealloc
test pmethod,CHECK_NET
jz prep_nonetcheck
;
; check whether file is on a network drive, and don't preallocate
; if so. preallocation can slow down swapping significantly when
; running on certain networks (Novell)
;
mov ax,440ah ; check if handle is remote
int 21h
jc prep_nonetcheck ; assume not remote if function fails
test dh,80h ; DX bit 15 set ?
jnz prep_noprealloc ; remote if yes
;
prep_nonetcheck:
mov dx,totparas
mov cl,4
rol dx,cl
mov cx,dx
and dx,0fff0h
and cx,0000fh
sub dx,1
sbb cx,0
mov si,dx ; save
mov ax,4200h ; move file pointer, absolute
int 21h
jc prep_file_err
cmp dx,cx
jne prep_file_err
cmp ax,si
jne prep_file_err
mov cx,1 ; write 1 byte
mov ah,40h
int 21h
jc prep_file_err
cmp ax,cx
jne prep_file_err
;
mov ax,4200h ; move file pointer, absolute
xor dx,dx
xor cx,cx ; rewind to beginning
int 21h
jc prep_file_err
;
prep_noprealloc:
mov ax,USE_FILE
mov prep.swapmethod,al
ret
;
prep_file_err:
mov ah,3eh ; close file
int 21h
mov dx,offset prep.swapfilename
mov ah,41h ; delete file
int 21h
;
prep_no_file:
mov ax,-1
mov prep.swapmethod,al
ret
;
prep_swap endp
;
end
syntax highlighted by Code2HTML, v. 0.9.1