;/* himem2.asm
; *
; * Microsoft Confidential
; * Copyright (C) Microsoft Corporation 1988-1991
; * All Rights Reserved.
; *
; * Modification History
; *
; * Sudeepb 14-May-1991 Ported for NT XMS support
; *
; * williamh 25-Sept-1992 added UMB initialization
; */

	page	95,160
	title   'HIMEM2 - Initialization code'

funky	segment word public 'funky'
	extrn	textseg:word		; in high segment
	extrn	KiddValley:word
	extrn	KiddValleyTop:word
	extrn	cHandles:word
	extrn	pack_and_truncate:near
	extrn	end_of_funky_seg:byte
	extrn	LEnblA20:word
	extrn	LDsblA20:word
funky	ends

	.xlist
	include	himem.inc		; get structures, equ's, etc.
					;  and open segment
	include xmssvc.inc

	.list

;	public	f000
	public	InitInterrupt
	public	MachineNum

;************************************************************************
;*									*
;*  Global Variables							*
;*									*
;************************************************************************

	extrn	pPPFIRET:word
	extrn	dd_int_loc:word
	extrn	Interrupt:near
	extrn	hiseg:word
	extrn	call_hi_in_di:near
	extrn	fCanChangeA20:byte
	extrn	fHMAMayExist:byte

ifndef NEC_98
	extrn	fVDISK:byte
	extrn	IsVDISKIn:near
endif   ;NEC_98

	extrn	A20Handler:near
	extrn	EnableCount:word
	extrn	MemCorr:word
	extrn	MinHMASize:word

	extrn	pReqHdr:dword
	extrn	PrevInt2f:dword
	extrn	TopOfTextSeg:word

	extrn	AddMem:near
	extrn	InstallA20:near
	extrn	Int2fHandler:near
;	extrn	Is6300Plus:near
	extrn	IsA20On:near

	extrn	SignOnMsg:byte
	extrn	ROMDisabledMsg:byte
	extrn	UnsupportedROMMsg:byte
	extrn	ROMHookedMsg:byte
	extrn	BadDOSMsg:byte
	extrn	NowInMsg:byte
	extrn	On8086Msg:byte
	extrn	NoExtMemMsg:byte
	extrn	FlushMsg:byte
	extrn	StartMsg:byte
	extrn	HandlesMsg:byte
	extrn	HMAMINMsg:byte
	extrn	KMsg:byte
	extrn	NoHMAMsg:byte
	extrn	A20OnMsg:byte
	extrn	HMAOKMsg:byte
ifndef NEC_98
	extrn	VDISKInMsg:byte
	extrn	BadArgMsg:byte
endif   ;NEC_98

	extrn	DevAttr:word
	extrn	Int15MemSize:word

	extrn	EndText:byte
        extrn   A20State:byte

        extrn   DOSTI:near
        extrn   DOCLI:near
ifdef NEC_98
	extrn	fAltA20Routine:byte
	extrn	LocalDisableA20:near
	extrn	LocalEnableA20:near
	extrn	cant_ena20_msg:byte
	extrn	cant_dia20_msg:byte
endif   ;NEC_98

;************************************************************************
;*									*
;*   Code/Data below here will be discarded after driver initialization *
;*									*
;************************************************************************

;	Discardable Initialization Data

public	fShadowOff, f1stWasWarning

fShadowOff	db	0	; NZ if shadow RAM should be disabled,
				;   0/1 set by command line switch, 0FFh
				;   set if little extended and hope to disable

f1stWasWarning	db	0	; NZ if 1st attempt to diddle A20 generated
				; a warning (and not an error)
	public	fA20Control

fA20Control	db	0ffh	; NZ if himem should take control of A20, even
				;   it was already on when himem loaded.

ifndef NEC_98
	public	fCPUClock

fCPUClock	db	0	; NZ if himem should try to preserve CPU clock
				;   speed when gating A20
endif   ;NEC_98

	public	StringParm, MachineNum, MachineName

StringParm	db	13 DUP (' ')

MachineNum	dw	-1

;  Note: the following table MUST be in the same order as the entries in the
;  A20_Scan_Table!  If you add entries here, also add one there!

MachineName	label	byte
	db	'ptlcascade',0		; Phoenix Cascade BIOS
	db	'att6300plus',0 	; AT&T 6300 Plus
	db	'ps2',0 		; IBM PS/2
	db	'hpvectra',0		; HP 'Classic' Vectra (A & A+)
	db	'acer1100',0		; Acer 1100
	db	'toshiba',0		; Toshiba 1600 & 1200XE
	db	'wyse',0		; Wyse 12.5 MHz 286 machine
ifndef NEC_98
	db	'tulip',0		; Tulip machines
	db	'zenith',0		; Zenith ZBIOS
	db	'at1',0 		; IBM AT/delay 0
	db	'at2',0 		; IBM AT/delay 1
	db	'at3',0 		; IBM AT/delay 2
	db	'philips',0		; Philips machines
	db	'css',0			; CSS Lab machines
	db	'fasthp',0		; Single byte method for HP Vectras
	db	'ibm7552',0		; IBM 7552 Industrial Computer
	db	'bullmicral',0		; Bull Micral 60 M004
endif   ;NEC_98
	db	'at',0			; IBM AT
	db	0FFh			; end of table

;NOTE: there is code in GetParms which depends on AltNameTbl coming
;      after MachineName table.

ifndef NEC_98
	public	AltName1, AltName2, AltName3, AltName4, AltName5
	public	AltName6, AltName7, AltName8, AltName9, AltName10
	public	AltName11, AltName12, AltName13, AltName14, AltName15
	public	AltName16                                    ;M004
else    ;NEC_98
	public	AltName1, AltName2, AltName3, AltName4, AltName5
	public	AltName6, AltName7, AltName8
endif   ;NEC_98

AltNameTbl	label	byte
AltName3    db	'3',0			; Phoenix Cascade BIOS
AltName5    db	'5',0			; AT&T 6300 Plus
AltName2    db	'2',0			; IBM PS/2
AltName4    db	'4',0			; HP 'Classic' Vectra (A & A+)
AltName6    db	'6',0			; Acer 1100
AltName7    db	'7',0			; Toshiba 1600 & 1200XE
AltName8    db	'8',0			; Wyse 12.5 Mhz 286 machine
ifndef NEC_98
AltName9    db	'9',0			; Tulip machine
AltName10   db	'10',0			; Zenith ZBIOS
AltName11   db	'11',0			; IBM AT/delay 0
AltName12   db	'12',0			; IBM AT/delay 1
AltName13   db	'13',0			; IBM AT/delay 2
	    db	'13',0			; Philips machines (same as AT3)
	    db	'12',0			; CSS machines
AltName14   db	'14',0			; Single byte HP Vectra m/cs
AltName15   db	'15',0			; IBM 7552 Industrial Computer
AltName16   db	'16',0			; Bull Micral 60          M004
endif   ;NEC_98
AltName1    db	'1',0			; IBM AT
	    db	0FFh			; end of table

ifdef	debug_tsr	;-----------------------------------------------

;*----------------------------------------------------------------------*
;*									*
;*  ExeStart -								*
;*									*
;*	Entry point when himem is invoked as an .EXE.			*
;*									*
;*----------------------------------------------------------------------*

lpCmdLine	dd	81h		; far ptr to command tail

	public	ExeStart

ExeStart:

	mov	word ptr cs:[lpCmdLine+2],es	; save PSP segment in pointer

	mov	ax,cs		; Setup segment regs to all be the same
	mov	ds,ax
	mov	es,ax

	call	InitDriver	; Initialize...

	mov	ax,TopOfTextSeg	; TopOfTextSeg == 0 is error installing
	or	ax,ax
	jnz	@f

	mov	ax,4C03h	; error, so just terminate
	int	21h
@@:
	mov	di,offset pack_and_truncate
	jmp	call_hi_in_di	; terminate and stay resident

endif			;------------------------------------------------



;*----------------------------------------------------------------------*
;*									*
;*  InitInterrupt -							*
;*									*
;*	Called by MS-DOS immediately after Strategy routine		*
;*									*
;*  ARGS:   None							*
;*  RETS:   Return code in Request Header's Status field		*
;*  REGS:   Preserved							*
;*									*
;*	This entry point is used only during initialization.		*
;*	It replaces itself with a much shorter version which only	*
;*	serves to report the appropriate errors when this driver	*
;*	is called in error.						*
;*									*
;*----------------------------------------------------------------------*

InitInterrupt   proc    far

	; Save the registers including flags.

	push    ax		; We cannot use pusha\popa because
	push    bx		;	we could be on an 8086 at this point
	push    cx
	push    dx
	push    ds
	push    es
	push    di
	push    si
	push    bp
	pushf

	push	cs		; Set DS=CS for access to global variables.
	pop	ds

	les	di,[pReqHdr]	; ES:DI = Request Header

	mov     bl,es:[di].Command ; Get Function code in BL

	or	bl,bl		; Only Function 00h (Init) is legal
	jz	IInit

	cmp     bl,16		; Test for "legal" DOS functions
	jle     IOtherFunc

IBogusFunc:
	mov     ax,8003h	; Return "Unknown Command"
	jmp     short IExit

IOtherFunc:
	xor     ax,ax		; Return zero for unsupported functions
	jmp     short IExit

IInit:
	call    InitDriver	; Initialize the driver
	les	di,[pReqHdr]	; Restore es:di = Request Header

IExit:
	or	ax,0100h	; Turn on the "Done" bit
	mov	es:[di].Status,ax ; Store return code

	popff			; restore the registers
	pop	bp
	pop	si
	pop	di
	pop	es
	pop	ds
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	mov	dd_int_loc,offset Interrupt	; replace Interrupt with
	ret					; tiny permanent stub

InitInterrupt   endp

;*----------------------------------------------------------------------*
;*									*
;*  InitDriver -							*
;*									*
;*	Called when driver is Initialized.				*
;*									*
;*  ARGS:   ES:DI = Address of the Request Header			*
;*  RETS:   pHdr.Address = Bottom of resident driver code		*
;*  REGS:   AX, CX and Flags are clobbered				*
;*									*
;*----------------------------------------------------------------------*

ifndef NEC_98
	public	InitDriver
endif   ;NEC_98

InitDriver  proc    near

	cld

	ifndef	debug_tsr
	call	LocateHiSeg	; locate the hiseg in low memory properly


	mov	ax,cs
	push	es
	mov	es,hiseg
	assume	es:funky
	add	textseg,ax	; relocate text segment pointer
	add	LEnblA20+2, ax	; update ptrs to enble & disable a20 rtns
	add	LDsblA20+2, ax
	pop	es
	assume	es:nothing
	endif

;	mov	ah,9		; display signon message
;	mov	dx,offset SignOnMsg
;	int	21h

	mov	ah,30h		; make sure we've got DOS 3.00 or higher
	int	21h		; Get DOS versions number
	cmp	al,3
	jae     IDCheckXMS

	mov	dx,offset BadDOSMsg
	jmp	IDFlushMe

IDCheckXMS:
ifndef NEC_98
	mov	ax,(INT2F_ID SHL 8) OR INT2F_INS_CHK
	int	2Fh		; make sure there's no other XMS installed
else    ;NEC_98
	mov     ax,4300h	; make sure there's no other XMS installed
	int	2Fh
endif   ;NEC_98
	cmp	al,80h		; Is INT 2F hooked?
	jne     IDNotInYet
	mov	dx,offset NowInMsg
	jmp	IDFlushMe

IDNotInYet:
ifdef NEC_98
	xor	ax,ax
	mov	es,ax
	test	byte ptr es:[501h],40h	; we're on an V30/V50
	jz	@f
	jmp	IDFlushMe		; so give out
@@:
	call	GetInt15Memory		; If Int 15h/88h reports < 384k of
	mov	dx,offset NoExtMemMsg
	or	ax,ax			; we have extend memory ?
	jnz	@f
	jmp	IDFlushMe		; so give out
@@:
	test	byte ptr es:[0501h],08h	; Q : HIRES CRT ?
	jz	@f			;  N : continue
	mov	ah,byte ptr es:[501h]	; BIOS_FLG
	mov	al,ah			;  save
	and	ah,07h			; get main memory size
	cmp	ah,4			; main memory > 512 ?
	jb	@f			; no. don't need to init RAMWindow
	test	byte ptr es:[458h],80h	; NPC ?
	jnz	not_XA			; yes
	test	al,30h			; system type = 0 ?
	jnz	not_XA			; no. other than XA
	call	Init_RamWindow_XA
	jmp	short @f
not_XA:
	call	Init_RamWindow
@@:
endif   ;NEC_98
	call	GetParms		; process command line parameters
;; don't call IsA20On at this moment because we haven't init it yet
	mov	ax, 2
	XMSSVC	XMS_A20
;;;;	call	IsA20On 		; Is A20 already enabled?
	or	ax,ax			;   (may zap cx, si, di)
	jz	IDInsA20		;   no, go install A20 handler

	mov	dx,offset A20OnMsg	; "A20 already on" message
	cmp	fA20Control,0		; should we take control of A20 anyway?
	jne	IDInsA20		;   yes, go muck with it
	mov	[fCanChangeA20],0	;   no,  don't allow changing of A20
	mov	ah,9			;	 and tell user about it
	int	21h
	jmp	short IDAfterA20

IDInsA20:
	call	InstallA20		; install proper A20 handler
	jc	IDFlushMe		; CY means fatal error

;	Note:  A side affect of the previous InstallA20 is that MemCorr
;	  is set to reflect the adjustment factor if we're on an AT&T 6300+

IDAfterA20:
	call	InitHandles	; initialize handle table

	call    GetInt15Memory	; how much extended memory is installed?
	cmp     ax,64		; Is there >= 64K of extended?
ifndef NEC_98
	jae	IDHMAOK
else    ;NEC_98
	jb	@f
	cmp	Int15MemSize, 0	; has the hma been allocated to INT 15 ?
	jz	IDHMAOK		; no, HMA is available
@@:
endif   ;NEC_98

	push	es
	mov	es,hiseg
	assume	es:funky
	mov	bx,[KiddValley]	; get size of memory we already have in tables
	mov	cx,[cHandles]

IDAnyMem:
	cmp	[bx].Flags,FREEFLAG
	jnz	IDAnyMem_1	; brif not a valid free block
	add	ax,[bx].Len	; accumulate total
IDAnyMem_1:
	add	bx,SIZE Handle
	loop	IDAnyMem

	pop	es
	assume	es:nothing

	mov	dx,offset NoHMAMsg
	or	ax,ax			; no HMA, any other memory to control?
	jnz	disp_hma_msg		; jmp if some memory

;	We can't find any memory to manage.

	mov	dx,offset NoExtMemMsg

;	Display the message in DX followed by the "Flush" message.

IDFlushMe:
	mov	ah,9
	int	21h
	mov	dx,offset FlushMsg
	mov	ah,9
	int	21h

	xor	ax,ax			; discard the driver
	mov	[TopOfTextSeg],ax

ifndef	debug_tsr			;-------------------------------
	les	di,[pReqHdr]
	mov	es:[di].Units,al
	and	cs:DevAttr,not 8000h	; clr bit 15 in attrib of driver header
endif
	jmp	short IDReturn		;-------------------------------
IDHMAOK:
	mov     [fHMAMayExist],1
	mov	dx,offset HMAOKMsg
disp_hma_msg:
;       mov     ah,9
;       int     21h

;; tell xms.lib where our variable is
        mov     ax, cs
        mov     bx, offset A20State
        XMSSVC  XMS_INITUMB

	call    HookInt2F		; "turn on" the driver

;	Initialization finished (or failed) -- return to caller

IDReturn:

ifndef	debug_tsr			;-------------------------------
	mov	di,offset pack_and_truncate
	jmp	call_hi_in_di		; pack stuff down and terminate
endif					;-------------------------------
	ret

InitDriver	endp
;
;----------------------------------------------------------------------------
; procedure : LocateHiSeg
;
;		Locate the movable segment properly in the low seg.
;		taking care of the stripped ORG zeroes. This function
;		calculates the segment at which the hiseg should run
;		with the ORG. If the segment cvalue goes below zero the
;		code is moved up high enough to run the code from a seg value
;		of zero.
;
;		This function assumes that the 'funky' segment follows
;		immediately after the text seg.
;
;----------------------------------------------------------------------------
; 
LocateHiSeg	proc	near
	push	ds
	mov	ax, cs				; para start of text seg
	mov	cx, offset _text:EndText	; end of text seg
	add	cx, 15				; para round it
	shr	cx, 1
	shr	cx, 1
	shr	cx, 1
	shr	cx, 1
	add	ax, cx				; para start of funky seg
	cmp	ax, (HISEG_ORG shr 4)		; will the seg go below zero?
	jb	MoveHiSeg			; yeah, we have to move it
	sub	ax, (HISEG_ORG shr 4)		; no, it fits in
	pop	ds
	mov	hiseg, ax			; update the segment in which
						;   it is going to run from.
	ret
MoveHiSeg:
	mov	ds, ax				; segment at which funky
						;  resides without the ORG
 	xor	ax, ax
 	mov	es, ax				; we want to movve the code
						;  to 0:HISEG_ORG
	mov	di, offset funky:end_of_funky_seg
	mov	si, di
	sub	si, HISEG_ORG
	mov	cx, si
	dec	di
	dec	si
  	std					; move backward (safe when
						;  source & dest overlap
    	rep	movsb
	cld
    	pop	ds
    	mov	hiseg, 0			; funky is going to run from
						;  segment zero
    	ret
LocateHiSeg	endp


;*----------------------------------------------------------------------*
;*									*
;*  HookInt2F -								*
;*									*
;*	Insert the INT 2F hook						*
;*									*
;*  ARGS:   None							*
;*  RETS:   None							*
;*  REGS:   AX, SI, ES and Flags are clobbered				*
;*									*
;*----------------------------------------------------------------------*

	public	HookInt2F

HookInt2F   proc    near

        call    DOCLI
	xor	ax,ax
	mov	es,ax
	mov	si,2Fh * 4		; save previous int2f vector
	mov	ax,offset Int2FHandler	; and exchange with new one
	xchg    ax,es:[si][0]
	mov	word ptr [PrevInt2F][0],ax
	mov	ax,cs
	xchg    ax,es:[si][2]
	mov	word ptr [PrevInt2F][2],ax
        call    DOSTI
	ret

HookInt2F   endp

;*----------------------------------------------------------------------*
;*									*
;*  GetInt15Memory -							*
;*									*
;*	Returns the amount of memory INT 15h, Function 88h says is free	*
;*									*
;*  ARGS:   None							*
;*  RETS:   AX = Amount of free extended memory in K-bytes		*
;*  REGS:   AX and Flags are clobbered					*
;*									*
;*----------------------------------------------------------------------*


GetInt15Memory proc near

ifndef NEC_98
IFDEF WHEN_INT15_DONE
	mov	ah,88h			; snag the int 15h memory
	clc
	int	15h			; Is Function 88h around?
	jnc     xret_geti15
	xor	ax,ax			; No, return 0
xret_geti15:

ifndef NOLIMIT                                  ;M005
	cmp	ax,15*1024		; Limit himem.sys to using 15 meg
	jb	@f			;   of extended memory for apps
	mov	ax,15*1024		;   that don't deal with > 24 bit
@@:					;   addresses
endif						;M005

ELSE
	XMSSVC	XMS_EXTMEM		; return ext-mem in ax5
ENDIF

	ret
else    ;NEC_98
	push	es
	mov	ax,40h
	mov	es,ax
	mov	al,byte ptr es:[01h]	; get extend memory size
	pop	es
	sub	ah,ah
	shl	ax,7
	ret
endif   ;NEC_98

GetInt15Memory endp

;*----------------------------------------------------------------------*
;*									*
;*  GetParms -								*
;*									*
;*	Get any parameters off of the HIMEM command line		*
;*									*
;*  ARGS:   None							*
;*  RETS:   None							*
;*  REGS:   AX, BX, CX, DX, DI, SI, ES and Flags clobbered		*
;*									*
;*  Side Effects:   cHandles and MinHMASize may be changed		*
;*									*
;*----------------------------------------------------------------------*

ifndef NEC_98
GPArgPtr	dd	?
endif   ;NEC_98
GPRegSave	dw	?

	public	GetParms

GetParms    proc    near

	cld				; better safe than sorry

	push	ds

ifdef	debug_tsr			;-------------------------------
	lds	si,lpCmdLine
else					;-------------------------------
	les	di,[pReqHdr]		; Running as a device driver
	lds	si,es:[di].pCmdLine	; DS:SI points to first char
					;   after "DEVICE="
ifndef NEC_98
@@:	call	GPGetChar		; Skip over driver name, up to
	jc	GPDatsAll		;   first blank or / or eol
	jz	GPNextArg
	cmp	al,'/'
	jnz	@b
	dec	si			; Backup to get / again
endif   ;NEC_98
endif					;-------------------------------

	assume	ds:nothing,es:nothing

;	Scan until we see a non-blank or the end of line.

ifndef NEC_98
GPNextArg:
	call	GPGetChar
	jc	GPDatsAll		; eol
	jz	GPNextArg		; blank

	mov	word ptr cs:[GPArgPtr], si	; save ptr to start of arg
	mov	word ptr cs:[GPArgPtr+2], ds	;   incase we want to complain
	dec	word ptr cs:[GPArgPtr]		;   (GPGetChar points at next)

	cmp	al,'/'			; better be a / or not a valid arg
	jz	GPGotOne

;	Detected invalid parameter or value, complain to user

GPBadParm:

	mov	ah,9			; tell'm something isn't right
	push	cs
	pop	ds
	mov	dx,offset BadArgMsg
	int	21h

	lds	si,cs:[GPArgPtr]	; backup to last parameter

GPBadDisp:
	call	GPGetChar		; disp arg up to space or eol
	jc	GPDatsAll		;  skips over bad arg while we're at it
	jz	GPNextArg

	cmp	al,'/'				; start of next arg?
	jnz	@f
	dec	si				; maybe yes, maybe no--might
	cmp	si,word ptr cs:[GPArgPtr]	;   be same arg
	jnz	GPNextArg			;   next, go process new arg
	inc	si				;   same, keep displaying
@@:
	mov	dl,al
	mov	ah,2
	int	21h
	jmp	short GPBadDisp

;	Finished, we're outta here...

else    ;NEC_98
GPBadParm:
GPNextChar:
	call	GPGetChar
	jc	GPDatsAll
	cmp	al,'/'
	je	GPGotOne
	jmp	short GPNextChar
endif   ;NEC_98
GPDatsAll:
	pop	ds
	ret

;	Save what we found and get the number or string after it.

GPGotOne:
	lodsb
	mov	cs:[GPRegSave],ax

;	Scan past the rest of the parm for a number, EOL, or a space.

GPNeedParm:
	call	GPGetChar
ifndef NEC_98
	jc	GPBadParm
	jz	GPBadParm	; blank
else    ;NEC_98
	jc	GPDatsAll
	cmp	al,' '
	je	GPBadParm
endif   ;NEC_98
	cmp	al,':'		; start of string arg
	je	GPString
	cmp	al,'='
	jne	GPNeedParm

;	Read the number at DS:SI into DX

GPNeedNum:
	call	GPGetChar
	jc	GPDatsAll
	cmp	al,'0'
	jb	GPNeedNum
	cmp	al,'9'
	ja	GPNeedNum

	xor	dx,dx
GPNumLoop:
	sub	al,'0'
	cbw
	add	dx,ax
	call	GPGetChar
	jc	GPNumDone
ifndef NEC_98
	jz	GPNumDone
else    ;NEC_98
	cmp	al,' '
	je	GPNumDone
endif   ;NEC_98
	cmp	al,'0'
	jb	GPBadParm
	cmp	al,'9'
	ja	GPBadParm
	shl	dx,1		; multiply DX by 10
	mov	bx,dx
	shl	dx,1
	shl	dx,1
	add	dx,bx
	jmp	short GPNumLoop

;	Move the string arg from ds:si to StringParm

GPString:
	mov	cx,(SIZE StringParm) - 1
	push	cs
	pop	es
	mov	di,offset _text:StringParm

GPStrLoop:
	call	GPGetChar
	jc	GPStrDone
ifndef NEC_98
	jz	GPStrDone
else    ;NEC_98
	cmp	al,' '
	je	GPStrDone
endif   ;NEC_98
	stosb
	loop	GPStrLoop

GPStrDone:

	mov	byte ptr es:[di],0	; Null terminate the string
ifndef NEC_98
	mov	dx,-1			; In case parm expects a num, give'm
					;   a likely invalid one
endif   ;NEC_98

;	Which parameter are we dealing with here?

GPNumDone:
	xchg    ax,cs:[GPRegSave]
	cmp	al,'H'		; HMAMIN= parameter?
	jne	@f
	jmp	GPGotMin
@@:
	cmp	al,'N'		; NUMHANDLES= parameter?
	jne	@f
	jmp	GPGotHands
@@:	cmp	al,'M'		; MACHINE: parameter?
	je	GPGotMachine
	cmp	al,'A'		; A20CONTROL: parameter?
	je	GPGotA20Control
	cmp	al,'S'		; SHADOWRAM: parameter?
	jne	@f
	jmp	GPGotShadow
@@:	cmp	al, 'I' 	; INT15=
	jne	@f
	jmp	GPGotInt15
ifndef NEC_98
@@:	cmp	al, 'C' 	; CPUCLOCK:
	jne	@f
	jmp	GPGotCPUClock
endif   ;NEC_98
@@:	jmp	GPBadParm


;	Process /A20CONTROL: parameter

GPGotA20Control:
	mov	ax,word ptr [StringParm]
	or	ax,2020h
	mov	bl,0FFh
	cmp	ax,'no' 		; ON ?	- means we take control
	jz	GPSetA20
	inc	bl
	cmp	ax,'fo' 		; OFF ? - means we leave alone if on
	jz	GPSetA20
ifndef NEC_98
	jmp	GPBadParm
else    ;NEC_98
	jmp	GPNextParm
endif   ;NEC_98

GPSetA20:
	mov	fA20Control,bl		; Z if A20 should be left alone if
	jmp	GPNextParm		;   it's already on when we're loaded


;	Process /MACHINE: parameter.

GPGotMachine:
	push	si				; save current location
	push	ds				;    in param string

	push	cs
	pop	ds
	mov	di,offset _text:MachineName	; es:di -> MachineName

GPNextTbl:
	xor	bx,bx

GPNextName:
	mov	si,offset _text:StringParm	; ds:si -> StringParm

GPChkNext:
	cmp	byte ptr es:[di],0FFh		; end of name table?
ifndef NEC_98
	jz	GPNoName
else    ;NEC_98
	jz	GPBadName
endif   ;NEC_98

	lodsb				; char from StringParm
	cmp	al,'A'			; force to lower case for match
	jb	@f			; (might be numeric, so don't just OR)
	cmp	al,'Z'
	ja	@f
	or	al,20h
@@:
	cmp	al,es:[di]		; match so far?
	jnz	GPFlushName

	or	al,al			; finished if matched up to & incl NULL
	jz	GPFoundName

	inc	di			; still matches, check next char
	jmp	short GPChkNext

GPFlushName:
	inc	bx
GPFN2:
	inc	di
	cmp	byte ptr es:[di],0FFh
ifndef NEC_98
	jz	GPNoName
else    ;NEC_98
	jz	GPBadName
endif   ;NEC_98

	cmp	byte ptr es:[di],0
	jnz	GPFN2
	inc	di
	jmp	short GPNextName

GPFoundName:
	mov	cs:[MachineNum],bx	; found a match, remember which entry
	jmp	short GPNameDone	;   it is for later

ifndef NEC_98
GPNoName:

	cmp	di,offset _text:AltNameTbl
	ja	GPBadName
	mov	di,offset _text:AltNameTbl
	jmp	short GPNextTbl

GPNameDone:
	pop	ds			; recover parm line pointer
	pop	si
	jmp	GPNextParm

GPBadName:
	pop	ds			; clear stack and error out...
	pop	si
	jmp	GPBadParm
else    ;NEC_98
GPBadName:

	cmp	di,offset _text:AltNameTbl
	ja	GPNameDone
	mov	di,offset _text:AltNameTbl
	jmp	short GPNextTbl

GPNameDone:
	pop	ds			; recover parm line pointer
	pop	si

	jmp	GPNextParm

endif   ;NEC_98

;	Process /NUMHANDLES= parameter.

GPGotHands:
	cmp	dx,MAXHANDLES
	jna	@f
	jmp	GPBadParm
@@:
	or	dx,dx		; Zero?
	jnz	@f
	jmp	GPBadParm
@@:
	push	es
	mov	es,hiseg
	assume	es:funky
	mov     [cHandles],dx ; Store it
	pop	es
	assume	es:nothing


	mov	dx,offset StartMsg ; display descriptive message
	call    GPPrintIt

	push	es
	mov	es,hiseg
	assume	es:funky
	mov	ax,[cHandles]
	pop	es
	assume	es:nothing

	call    GPPrintAX
	mov	dx,offset HandlesMsg
	call    GPPrintIt
ifndef NEC_98
	jmp	GPNextParm
else    ;NEC_98
	jmp	short GPNextParm
endif   ;NEC_98

GPGotMin:
	cmp	dx,64		; process /hmamin= parameter
	jna	@f
	jmp	GPBadParm
@@:
	push    dx
	mov	cs:[MinHMASize],dx

	mov	dx,offset HMAMINMsg ; print a descriptive message
	call    GPPrintIt
	mov	ax,cs:[MinHMASize]
	call    GPPrintAX
	mov	dx,offset KMsg
	call    GPPrintIt

	pop	dx
	mov	cl,10		; Convert from K to bytes
	shl	dx,cl
	mov	cs:[MinHMASize],dx
	jmp	short GPNextParm


;	Process /SHADOWRAM: parameter

GPGotShadow:
	mov	ax,word ptr [StringParm]
	or	ax,2020h
	xor	bl,bl
	cmp	ax,'no' 		; ON ?	- means we leave it alone
	jz	GPSetShadow
	inc	bl
	cmp	ax,'fo' 		; OFF ? - means we turn it off
	jz	GPSetShadow
ifndef NEC_98
	jmp	GPBadParm
else    ;NEC_98
	jmp	short GPNextParm
endif   ;NEC_98

GPSetShadow:
	mov	fShadowOff,bl		; NZ if Shadow RAM should be turned off
	jmp	short GPNextParm

ifndef NEC_98

;	Process /CPUCLOCK: parameter

GPGotCPUClock:

	mov	ax,word ptr [StringParm]
	or	ax,2020h
	xor	bl,bl
	cmp	ax,'fo' 		; OFF ? - means we don't worry about it
	jz	GPSetClock
	inc	bl
	cmp	ax,'no' 		; ON ?	- means we preserve CPU clock
	jz	GPSetClock		;	  rate
	jmp	GPBadParm

GPSetClock:
	mov	fCPUClock,bl		; NZ if clock rate preserved
	jmp	short GPNextParm
endif   ;NEC_98


;	Process /INT15= parameter

GPGotInt15:
	cmp	dx, 64			; atleast 64K
	jae	@f
	jmp	GPBadParm
ifndef NEC_98
@@:	call	GetInt15Memory
	cmp	ax, dx			; enuf Ext Mem ?
else    ;NEC_98
	call	GetInt15Memory
@@:	cmp	ax, dx			; enuf Ext Mem ?
endif   ;NEC_98
	jae	@f
	jmp	GPBadParm
@@:	mov	[Int15MemSize], dx
	; Fall through to GetNextParm
ifdef NEC_98
	jmp	short GPNextParm
endif   ;NEC_98

GPNextParm:
	mov	ax,cs:[GPRegSave]	; are we at the end of the line?
	cmp	al,13			; may not be needed any longer...
	je	GPExit
	cmp	al,10
	je	GPExit
ifndef NEC_98
	jmp	GPNextArg
else    ;NEC_98
	jmp     GPNextChar
endif   ;NEC_98

GPExit:
	pop	ds
	ret

GetParms    endp

; Get the next character from DS:SI, set CY if it's an EOL (CR, LF), set
; Z if it's a space

ifndef NEC_98
GPOffEOL	dw	-1
endif   ;NEC_98

	public	GPGetChar

GPGetChar	proc	near
ifndef NEC_98

	cmp	si,cs:[GPOffEOL]	; are we already at EOL?
	jnb	GPAtEOL

	lodsb				; no, get next char
	cmp	al,10			; is this the EOL?
	je	GPHitEOL
	cmp	al,13
	je	GPHitEOL

	cmp	al,' '			; set Z if blank

	clc
	ret

GPHitEOL:
	mov	cs:[GPOffEOL],si	; save EOL offset once
GPAtEOL:
else    ;NEC_98
	lodsb
	cmp	al,10
	je	@f
	cmp	al,13
	je	@f

	clc
	ret
@@:
endif   ;NEC_98
	stc
	ret

GPGetChar	endp


;*----------------------------------------------------------------------*

GPPrintIt   proc    near

	push    ds		; Save current DS
	push    cs		; Set DS=CS
	pop	ds
	mov	ah,9
	int	21h
	pop	ds		; Restore DS
	ret

GPPrintIt   endp

;*----------------------------------------------------------------------*

GPPrintAX   proc    near

	mov	cx,10
	xor	dx,dx
	div	cx
	or	ax,ax
	jz	GPAPrint
	push    dx
	call    GPPrintAX
	pop	dx
GPAPrint:
	add	dl,'0'
	mov	ah,2
	int	21h
	ret

GPPrintAX   endp

;*----------------------------------------------------------------------*
;*									*
;*  InitHandles -							*
;*									*
;*	Initialize the Extended Memory Handle Table			*
;*									*
;*  ARGS:   None							*
;*  RETS:   None							*
;*  REGS:   AX, BX, CX, and Flags are clobbered				*
;*									*
;*----------------------------------------------------------------------*

	assume	ds:_text

	public	InitHandles

InitHandles proc    near
	push	es
	mov	es,hiseg
	assume	es:funky
	mov	cx,[cHandles]

;	Init the Handle table.

	mov	bx,[KiddValley]

	xor	ax,ax
IHTabLoop:
	mov	[bx].Flags,UNUSEDFLAG
	mov	[bx].cLock,al
	mov	[bx].Base,ax
	mov	[bx].Len,ax
	if	keep_cs
	mov	[bx].Acs,ax
	endif
	add	bx,SIZE Handle
	loop    IHTabLoop

	mov	[KiddValleyTop],bx	; save top for handle validation
	pop	es
	assume	es:nothing
	ret

InitHandles endp

ifdef NEC_98
;******************************************************************************
;   Init_Ram_Window - Initialize Ram Window Memory
;
;	MS-DOS
;	Ram Window	Memory			Ram Windows	Memory
;	 80		100			80		80
;	 90		110			90		90
;	 BO		120			B0		B0
;	 A0		130			A0		A0
;
;
;
;   Author:   (sbp)
;
;   ENTRY:  REAL MODE on 386 processor (CPU ID already performed)
;   EXIT:
;
;   USED:   flags
;   STACK:
;------------------------------------------------------------------------------

Init_RamWindow	proc	near

	push	ax
	push	bx
	push	dx
	push	ds

	cli
	mov	cs:[save_ss],ss
	mov	cs:[save_sp],sp
	push	cs
	pop	ss
	mov	sp,offset EISA_stack

	mov	al, 8
	out	37h, al

	mov	ax, 8000h		; �]�������ڽ
	mov	bx, 4000h		; �]������ڽ
	mov	cx, 4			; �]���o���N��
	call	movebank

	mov	al,08h			; 80 BANK memory
	mov	dx,91h			; 80 BANK ram windows		"
	out	dx,al			; set RAM WINDOW

	mov	al,0ah			; A0 BANK memory
	mov	dx,93h			; A0 BANK ram window		"
	out	dx,al			; set RAM WINDOW

	xor	ax, ax
	mov	ds, ax
	test	byte ptr ds:[481h], 04h	; Q : can use shadow ram ?
	jnz	@f
	call	initshadowram
@@:
	mov	ax, 4000h
	mov	bx, 8000h
	mov	cx, 4
	call	movebank

	mov	ss, cs:[save_ss]
	mov	sp, cs:[save_sp]

	mov	al, 09h
	out	37h, al
	sti

	pop	ds
	pop	dx
	pop	bx
	pop	ax
	ret
	
Init_RamWindow	endp

;******************************************************************************
;   Init_Ram_Window_XA - Initialize Ram Window Memory
;
;	MS-DOS					Himem
;	Ram Window	Memory			Ram Windows	Memory
;	 80		100			80		TOM-40  or  TOM-20
;	 90		110			90		TOM-30  or  TOM-10
;	 BO		120			B0		TOM-20  or  120
;	 A0		130			A0		TOM-10  or  130
;
;   Author:   (sbp)
;
;   ENTRY:  REAL MODE on 286
;	    AH = main memory size in blocks of 128k
;   EXIT:
;
;   USED:   flags
;   STACK:
;------------------------------------------------------------------------------

Init_RamWindow_XA	proc	near

	sub	ah,3
	cmp	byte ptr es:[401h],ah
	ja	IRX_Start
	mov	byte ptr es:[401h],0
	ret

IRX_Start:
	push	ax
	push	bx
	push	dx
	push	ds

	cli
	mov	cs:[save_ss],ss
	mov	cs:[save_sp],sp
	push	cs
	pop	ss
	mov	sp,offset EISA_stack

	mov	al, 8
	out	37h, al

	cmp	ah,1			; main memory = 640k ?
	je	IRX_640

	mov	ax, 0a000h		; �]�������ڽ
	mov	bx, 4000h		; �]������ڽ
	mov	cx, 2			; �]���o���N��
	call	movebank

	mov	al,byte ptr es:[401h]
	dec	al
	shl	al,1
	add	al,10h			; al = BANK #
	mov	dx,93h			; A0 BANK ram window		"
	out	dx,al			; set RAM WINDOW

	mov	ax, 4000h
	mov	bx, 0a000h
	mov	cx, 2
	call	movebank

	dec	byte ptr es:[401h]

IRX_640:
	mov	ax, 8000h		; �]�������ڽ
	mov	bx, 4000h		; �]������ڽ
	mov	cx, 2			; �]���o���N��
	call	movebank

	mov	al,byte ptr es:[401h]
	dec	al
	shl	al,1
	add	al,10h			; al = BANK #
	mov	dx,91h			; 80 BANK ram windows		"
	out	dx,al			; set RAM WINDOW

	mov	ax, 4000h
	mov	bx, 8000h
	mov	cx, 2
	call	movebank

	dec	byte ptr es:[401h]

	mov	ss, cs:[save_ss]
	mov	sp, cs:[save_sp]

	mov	al, 09h
	out	37h, al
	sti

	pop	ds
	pop	dx
	pop	bx
	pop	ax
	ret

Init_RamWindow_XA	endp

;-------------------------------------------------------------------
;   movebank	move bank datat
;	entry	ax : source seg. address
;		bx : dest. seg. address
;		cx : bank count
;
;----------------------------------------------------------------

movebank proc	near
	push	es
	push	ds
	push	si
	push	di
@@:
	mov	ds, ax
	mov	es, bx
	push	cx
	mov	cx, 8000h
	cld
	xor	si, si
	xor	di, di
	rep	movsw
	pop	cx
	add	ax, 1000h		; get next bank address
	add	bx, 1000h		; get next bank address
	loop	@b
	pop	di
	pop	si
	pop	ds
	pop	es
	ret
movebank endp

;*-----------------------------------------------------------
;*  initshadowram
;*
;*
;*----------------------------------------------------------
initshadowram proc	near
	push	es
	push	di
	push	cx
	cld
	mov	ax, 8000h
	mov	es, ax
	mov	cx, 0004h
	mov	ax,8000h
	mov	es,ax
	mov	cx,0004h
l3:
	xor	di, di
	push	cx
	mov	cx, 3333h
l1:
	mov	ax, 0ff01h
	stosw
	mov	ax, 55aah
	stosw
	mov	al, 00
	stosb
	loop	l1
	stosb

	xor	di, di
	mov	cx, 3333h
l2:
	mov	ax,0ff01h
	scasw
	jnz	isr_exit
	mov	ax,55aah
	scasw
	jnz	isr_exit
	mov	al,00h
	scasb	 
	jnz	isr_exit
	loop	l2 
	mov	ax, es
	add	ax, 1000h
	mov	es, ax
	pop	cx
	loop	l3

;	clean up memory

	mov	cx, 0004h
l4:
	push	cx
	mov	ax, es
	sub	ax, 1000h
	mov	es, ax
	xor	ax, ax
	mov	di, ax
	mov	cx, 8000h
	rep	stosw
	pop	cx
	loop	l4
	xor	ax,ax
	jmp	short isr_done
isr_exit:
	mov	ax,0001 
isr_done:
	pop	cx 
	pop	di 
	pop	es 
	ret

initshadowram endp
;*----------------------------------------------------------------------*
;*									*
;*  ScanEISA - poll any EISA devices through the BIOS's Int15(0d8h)	*
;*     and add any memory we find out about to our free memory table.	*
;*     Note:  this code (including a big buffer) gets thrown out after	*
;*     completion of the initialization sequence.			*
;*									*
;*	Note:  The COMPAQ BIOS uses up 1.5K of stack during int15(d80x) *
;*		so we'll set up a separate stack while we're here	*
;*									*
;*----------------------------------------------------------------------*

save_ss	dw	0
save_sp	dw	0
EISA_Stack:

endif   ;NEC_98
_text	ends

ifdef	debug_tsr

EndStmt equ	<end	ExeStart>

STACK	segment	stack 'STACK'
	db	1024 dup (?)
STACK	ends

else

EndStmt equ	<end>

endif

	EndStmt