📄 msspawn.asm
字号:
;
; --- Version 3.0 91-05-27 17:56 ---
;
; 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
;
;
; Assemble with
;
; ?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.
;
;
; Main function:
;
; 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.
;
; 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.
;
; 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).
;
; 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
;
;
IFDEF ?LARGE
.model large,c
%out large model
ELSE
IFDEF ?MEDIUM
.model medium,c
%out medium model
ELSE
.model small,c
%out small model
ENDIF
ENDIF
;
ptrsize = @DataSize
;
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
;
; 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
;
; 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 ? ; 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
;
; 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
;
;----------------------------------------------------------------------
;
; 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 TYPE 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 TYPE prep_block dup(?) ; the swapping variables
lcurrdesc db TYPE mcbdesc dup(?) ; the current MCB descriptor
lxmsctl db TYPE xms_control dup(?)
eretcode dw ? ; EXEC return code
retflags dw ? ; EXEC return flags
cgetmcb dw ? ; address of get_mcb
;
parseg ends
;
param_len = ((TYPE parseg + 1) / 2) * 2 ; make even
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,lmem lprep.first_mcb
mov bx,lmem 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,lmem 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,lmem 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,lmem 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,lmem lcurrdesc.msize
jz mcb_no_next ; ok, no next to create
push es
dec bx ; size of next block
mov ax,es
add ax,lmem 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,lmem 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 lmem lprep.first_mcb,ax
cmp lmem lprep.env_mcb,ax
jne getmcb_finis
inc ax
mov lmem 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,lmem lprep.first_mcb
or si,si
jz no_shrink
mov dx,lmem lprep.psp_mcb
mov bx,dx
inc bx ; base PSP (MCB owner)
mov di,lmem 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,lmem 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,lmem spx
cld
mov lmem eretcode,ax ; save return code
pushf
pop bx
mov lmem retflags,bx ; and returned flags
;
cmp lmem 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,lmem 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,lmem lprep.handle ; EMS handle
;
swinems_main:
push ds
mov cx,lmem lcurrdesc.swsize ; block length in paras
mov di,lmem lcurrdesc.swoffset ; swap offset
mov es,lmem lcurrdesc.addr ; segment to swap
mov ds,lmem 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 lmem 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,lmem lprep.ems_pageframe ; page frame address
mov cl,4
shl si,cl ; convert to byte address
mov cx,TYPE mcbdesc
mov di,lcurrdesc
rep movsb
pop ds
pop si
inc si ; one paragraph
;
push bx
call lmem 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,lmem lprep.handle ; XMS handle
mov lmem lxmsctl.srchnd,ax ; source is XMS
mov lmem lxmsctl.desthnd,0 ; dest is normal memory
mov lmem lxmsctl.srclo,0
mov lmem lxmsctl.srchi,0
;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -