highload.inc

来自「Dos6.0」· INC 代码 · 共 1,910 行 · 第 1/5 页

INC
1,910
字号
;******************************************************************************
;
;   (C) Copyright MICROSOFT Corp. 1989-1991
;
;   Module:   HIGHLOAD.INC - Code used to prepare for LoadHigh or DeviceHigh
;
;   Date:     May 14, 1992
;
;******************************************************************************
;
;   Modification log:
;
;     DATE    WHO      DESCRIPTION
;   --------  -------  ----------------------------------------------
;   05/14/92  t-richj  Original
;   06/21/92  t-richj  Final revisions before check-in
;
;******************************************************************************
;
; This file contains routines needed to parse and implement user-given
; command-line options of the form "/S/L:3,0x500;2;7,127;0x0BE4".  InitVar()
; and Parsevar() are used to parse this data and place it in encoded form into
; the variables in highvar.inc, for use by the rest of the routines.
;
; DeviceHigh accepts this command-line (handled in sysconf.asm, not here):
;    DEVICEHIGH SIZE=hhhhhh module opts
; Or, DeviceHigh and LoadHigh accept any of the following:
;    DH/LH module opts
;    DH/LH [/S][/L:umb[,size][;umb[,size]]*] module opts
;    DH/LH [/L:umb[,size][;umb[,size]]*][/S] module opts
; The initial UMB,SIZE pair designates the module's load address; the remainder
; of the UMB and SIZE pairs are used to indicate specific UMBs to be left
; available during the load.
;
; When an actual load is ready to be performed, a call to HideUMBs() will
; temporarily allocate (as owner 8+"HIDDEN  ") all free elements in any
; upper-memory block which was not specified by the user... in addition, if
; UMBs were marked to shrink (/S option) to a certain size ("umb,size"), any
; elements in that umb SAVE the lower-half of the newly-shrunken one are also
; allocated.  After the load, the function UnHideUMBs() (in highexit.inc) will
; free any UMBs so allocated.
;
; When a device driver loads, there is the additional problem of allocating its
; initial load site; this should be restricted to the first UMB specified on
; the command-line.  The function FreezeUM temporarily allocates all remaining
; free upper-memory elements (as owner 8+"FROZEN  "), except those in the load
; UMB.  Then the initial allocation may be made, and a call to UnFreeze will
; return any so-allocated memory elements to FREE, for the true load.  Note
; that UnFreeze leaves HIDDEN elements allocated; it only frees FROZEN ones.
;
;******************************************************************************
;
;   Public:
;
;___PROCEDURES_________________________________________________________________
;
;   AddrToUmb   - converts a segment address in AX to its appropriate UMB #
;   BigFree     - makes ES:0 point to the largest free MCB in UMB given as AL
;   FixMem      - scans the UM chain and concatenates adjacent free MCBs
;   FreezeUM    - Marks FROZEN all UM elements now FREE, save those in load UMB
;   GetLoadSize - Returns the load UMB minimum size (0 if not specified)
;   GetLoadUMB  - Returns the load UMB number in AL (-1 if not specified)
;   GetSize     - Returns the UMB in AL's minimum size (0 if not specified)
;   GetXNum     - reads a 32-bit ASCII number at ES:SI and returns it in DX:AX
;   HideUMBs    - links UMBs and hides upper-memory as appropriate
;   InitVar     - initializes all the variables used in ParseVar and HideUMBs
;   NextMCB     - moves an MCB pointer forward to the next MCB
;   ParseVar    - parses [/S][/L:umb[,size][;umb[,size]]*] and builds the table
;   PrTable     - produces a printout of the variables in highvar.inc
;   StoLoadSize - Overrides the load UMB minimum size with what's in AX
;   StoLoadUMB  - Overrides the load UMB number with what's in AL
;   UmbHead     - returns in AX the address of the first UMB block (0x9FFF)
;   UnFreeze    - Marks FROZEN elements as FREE
;
;___VARIABLES__________________________________________________________________
;
;   gnradix     - After a call to GetXNum, is 16 or 10, depending on the # read
;
;   Internal:
;___PROCEDURES_________________________________________________________________
;
;   convUMB     - checks after GetXNum to convert an address to a UMB number
;   findUMB     - makes ES:0 point to the first MCB in UMB given as AL
;   fm_link     - links UMBs not already linked in
;   fm_unlink   - unlinks UMBs if fm_umb is set to 0
;   frezMCB     - marks as 8+FROZEN the MCB at ES:0
;   hideMCB     - marks as HIDDEN the MCB at ES:0
;   hideUMB     - marks as HIDDEN all FREE elements in UMB passed as AL
;   hideUMB?    - hides as appropriate the UMB in CL
;   hl_unlink   - unlinks UMBs if fm_umb is set to 0; restores strategy too
;   incArgc     - increments fm_argc, for use with LH command-line parsing
;   isEOL       - returns with ZF set iff AL contains CR or LF, or 0
;   isFreeMCB   - returns with ZF set if current MCB (ES:0) is FREE
;   isFrozMCB   - returns with ZF set if current MCB (ES:0) is FROZEN
;   isSpecified - sets ZF if UMB in AL wasn't specified in DH/LH line.
;   isSysMCB    - sets ZF iff ES points to an MCB owned by "SC" + (8 or 9)
;   isTiny      - returns with ZF set if user didn't specify /S
;   isWhite     - returns with ZF set iff AL contains whitespace (or "=")
;   loadLow     - returns AL==0 if UMB0 == 0, else AL==1
;   mul32       - multiplies the number in DX:AX by gnradix
;   parseL      - parses ":nnnn[,nnnn][;nnnn[,nnnn]]*" for ParseVar
;   setUMBs     - links umbs and sets allocation strategy for a load
;   shrinkMCB   - breaks an MCB into two pieces, the lowest one's size==AX
;   stowSiz     - marks a given UMB as having a given minimum size
;   stowUMB     - marks a given UMB as used, if it hasn't been so marked before
;   toDigit     - converts a character-digit to its binary counterpart
;   toPara      - divides DX:AX by 16; result in AX only
;   toUpper     - accepts one argument (probly a register), and upper-cases it.
;   unHideMCB   - marks as FREE the MCB at ES:0
;   unMarkUMB   - marks a given UMB as unused, even if previously marked used
;
;******************************************************************************

SWTCH	equ	'/'		; Switch character

DOS_CHECK_STRATEGY  equ	5800h	; Int 21h, Func 58h, Svc 0 = check alloc strat
DOS_SET_STRATEGY    equ	5801h	; Int 21h, Func 58h, Svc 1 = set alloc strategy
DOS_CHECK_UMBLINK   equ	5802h	; Int 21h, Func 58h, Svc 2 = check link state
DOS_SET_UMBLINK     equ	5803h	; Int 21h, Func 58h, Svc 3 = set link state
DOS_GET_DOS_LISTS   equ	  52h	; Int 21h, Func 52h = return list of lists
DOS_UMB_HEAD        equ	  8Ch	; Offset from ES (after func52h) to get UMBHead

CR	equ	0Dh		; Carriage Return
LF	equ	0Ah		; Line Feed
TAB	equ	09h		; Tab character (^I)

; -----------------------------------------------------------------------------
; This file needs arena.inc, but it should already have been included.  Because
; of MASM's strange method of two-pass definitions, we can't just include it
; inside of an ifndef, or it won't make it to stage two.  Odd odd odd.
; -----------------------------------------------------------------------------

; -----------------------------------------------------------------------------
;*** toUpper - accepts one argument (probly a register), and upper-cases it.
;    BUGBUG: t-richj 6/3/92 - This method, though used elsewhere (for example,
;            used in sysinit2.asm to upper-case config.sys), relies on ASCII
;            conventions.  Might be bad.
; -----------------------------------------------------------------------------

toUpper	macro	arg
	and	arg, 0DFh
	endm

; -----------------------------------------------------------------------------
;*** NextMCB - moves an MCB pointer forward to the next MCB
; -----------------------------------------------------------------------------
; ENTRY:       First arg - seg pointer (ES?); second, a scratch register (AX?)
; EXIT:        Seg pointer moved forward, scratch register = distance moved
; ERROR EXIT:  None
; USES:        Flags, arguments
; -----------------------------------------------------------------------------

NextMCB	macro	seg, reg
	mov	reg, seg
	add	reg, seg:[arena_size]
	inc	reg			; Inc 1 paragraph for the header
	mov	seg, reg
	endm

; -----------------------------------------------------------------------------
;*** InitVar - initializes all the variables used in ParseVar and HideUMBs
; -----------------------------------------------------------------------------
; ENTRY:       None
; EXIT:        Variables listed in highvar.inc are initialized
; ERROR EXIT:  None
; USES:        Flags, variables in highvar.inc
; -----------------------------------------------------------------------------
; Note that element 0 references UMB 0 (conventional), not UMB 1.  Its contents
; are largely ignored, but it is initialized nonetheless.
; -----------------------------------------------------------------------------

	public	InitVar

InitVar	proc	near
	pushreg	<ax, cx, di, es>
	dataseg	es			;Point ES into appropriate data segment

	xor	ax, ax
	mov	fUmbTiny, al		;Shrink UMBs? (made 1 if /S given)
	mov	fInHigh,  al		;Set to 1 when DH/LH has been called
	mov	SegLoad,  ax		;Load Address (seg), used for DH only
	mov	UmbLoad, UNSPECIFIED	;Later is the # of the 1st spec'd UMB
	mov	fm_argc,  al		;Start with zero args having been read

	cld

	mov	cx, MAXUMB		;For each entry
	mov	di, offset ES:UmbUsed	;on the UmbUsed array,
	rep	stosb			;	Store 0

	mov	cx, MAXUMB		;Okay... for each entry
	mov	di, offset ES:UmbSize	;on the UmbSize array,
	rep	stosw			;	Store 0

	normseg	es		; Return ES

	popreg	<es, di, cx, ax>
 	ret

InitVar	endp

; -----------------------------------------------------------------------------
;*** FixMem - scans the upper memory chain and concatenates adjacent free MCBs
; -----------------------------------------------------------------------------
; ENTRY   : None
; EXIT    : None
; ERROR   : None
; USES    : Flags, fm_umb, fm_strat
; -----------------------------------------------------------------------------

	public	FixMem

FixMem	proc	near

	pushreg	<ax, bx, cx, dx, es>

	call	fm_link		; Link in UMBs

	call	UmbHead		; Get first upper-memory MCB address (0x9FFF)
	jc	fmX		; (if couldn't get it, leave now).

	mov	es, ax		; It returns in AX, so move it to ES.

;
; - Walk MCB Chain ------------------------------------------------------------
;

	xor	dx, dx		; We're keeping the address of the last MCB
	mov 	cx, dx		; in CX... and the last owner
	inc	dx		; in dx as we go through the loop:

; ------------------------------------------
; FM10--DX  = last MCB's owner's PSP address
;       CX  = last MCB's address (segment)
; ------------------------------------------

fm10:	mov	al, es:[arena_signature]	; if 'Z', don't repeat loop
	mov	bx, es:[arena_owner]		; if not zero, do nothing
	or	bx, dx				; dx was owner of previous MCB
	jnz	fm30				; If not both zero, don't cat.

	; - Coalesce memory blocks at ES:00 and CX:00 -------------------------

fm20:	mov	bx, es:[arena_size]		; Grab this block's Size,
	mov	es, cx				; Go back to prev MCB's address
	mov	es:[arena_signature], al	; & move the SECOND sig here

	add	bx, es:[arena_size]		; Size += first MCB's size
	add	bx, 1h				; And add one for the header
	mov	es:[arena_size], bx		; Write the size

	; ---------------------------------------------------------------------

fm30:	mov	cx, es			; Put this address on the stack
	mov	dx, es:[arena_owner]	; And remember its owner

	NextMCB	es, bx			; Move to the next MCB

	cmp	al, arena_signature_end
	jnz	fm10			; If signature != 'Z', there are more.

fmX:	call	fm_unlink		; Unlink UMBs

	popreg	<es, dx, cx, bx, ax>
	ret

FixMem	endp

; -----------------------------------------------------------------------------
;*** fm_link - links UMBs not already linked in
; -----------------------------------------------------------------------------
; ENTRY:    None
; EXIT:     fm_umb == 0 if not linked in previously, 1 if already linked in
; ERROR:    None
; USES:     AX, BX, fm_umb
; -----------------------------------------------------------------------------

fm_link	proc	near
	mov	ax, DOS_CHECK_UMBLINK
	int	21h			; Current link-state is now in al

	putdata	fm_umb, al		; So store it in fm_umb for later

	mov	ax, DOS_SET_UMBLINK
	mov	bx, 1
	int	21h

	ret
fm_link	endp

; -----------------------------------------------------------------------------
;*** fm_unlink - unlinks UMBs if fm_umb is set to 0
; -----------------------------------------------------------------------------
; ENTRY:    fm_umb == 1 : leave linked, else unlink
; EXIT:     None
; ERROR:    None
; USES:     AX, BX
; -----------------------------------------------------------------------------

fm_unlink	proc	near
	xor	bx, bx
	getdata	bl, fm_umb		; fm_umb already has the old link-state
	mov	ax, DOS_SET_UMBLINK
	int	21h			; so just use that, and call int 21h
	ret
fm_unlink	endp

; -----------------------------------------------------------------------------
;*** ParseVar - parses [/S][/L:umb[,size][;umb[,size]]*] and builds the table
; laid out in highvar.inc
; -----------------------------------------------------------------------------
; ENTRY:    ES:SI points to command tail of LoadHigh/DeviceHigh (whitespace ok)
; EXIT:     ES:SI points to first character in child program name
; ERROR:    ES:SI points to character which caused error, carry set, AX == code
; USES:     ES:SI, AX, flags, variables in highvar.inc
; -----------------------------------------------------------------------------
; Error codes (in AX if carry set on return):
;
PV_InvArg	equ	1	; Invalid argument passed
PV_BadUMB	equ	2	; Bad UMB number passed (duplicate?)
PV_InvSwt	equ	3	; Unrecognized switch passed
;
; This routine exects ES:SI to point to a string much like the following:
;    "/S/L:1,200;2 module options"
; Optionally, the string can begin with whitespace; neither /S nor /L is
; required, though that's what this routine is supposed to parse.
;
optS		equ	'S'	; /S
optL		equ	'L'	; /L:...
;
; -----------------------------------------------------------------------------
; LoadHigh has a list of arguments, returned by cparse, which is used to create
; a command-line for spawning a child process.  For a typical LH command, say,
;     lh /l:1,1000;2 print/d:lpt2
; the arguments would look like (one per line):
;     lh
;     /l
;     1
;     1000
;     2
;     print
;     /d
;     :lpt2
; In short, if "print" were, say, "43", there'd be no way to determine which
; arg was the filename.  So, inside this routine, we keep a running counter
; of the number of arguments LH will need to skip in order to get to the
; program name.  The "lh" is implicit--it'll always have to skip that.  So if
; there's no "/l" or "/s", fm_argc will be 0 ... other than that, 1 is added
; for:
;    Each /L
;    Each /S (there should be only one)
;    Each UMB number (they follow ":" or ";")
;    Each UMB size   (they follow ",")
; So, in the above example, fm_argc would be 4-- and LH would skip right to
; "print".  Note that InitVar initializes fm_argc to zero.
; -----------------------------------------------------------------------------

	public	ParseVar

ParseVar	proc	near
	pushreg	<di, ds, es>

	push	es		; Make DS:SI point to it, as well as ES:SI
	pop	ds		; (regardless if we're in devhigh or loadhigh)
	cld

; ------------------------------------------------
; PV10--ES:SI = any whitespace on the command-line
; ------------------------------------------------

pv10:	lodsb			; here, ES:SI=="  /L..."--must eat whitespace
	call	isWhite
	jz	pv10		;       ES:SI==" /L..."--keep eating.
	cmp	al, SWTCH
	jz	pv20		;       ES:SI=="/L..."--go process a switch

	dec	si		; Backup--it's now "odule options", and we need
	clc			; that "m" we just read (or whatever it is).
	jmp short pvX		; Then return with carry clear == we're done.

pv20:	lodsb			; Just read 'S' or 'L', hopefully
	toUpper	al		; So we make it upper-case, and...

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?