Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1093 lines
23 KiB

;/* 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
extrn fVDISK:byte
extrn IsVDISKIn:near
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
extrn VDISKInMsg:byte
extrn BadArgMsg:byte
extrn DevAttr:word
extrn Int15MemSize:word
extrn EndText:byte
extrn A20State:byte
extrn DOSTI:near
extrn DOCLI:near
;************************************************************************
;* *
;* 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.
public fCPUClock
fCPUClock db 0 ; NZ if himem should try to preserve CPU clock
; speed when gating A20
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
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
db 'at',0 ; IBM AT
db 0FFh ; end of table
;NOTE: there is code in GetParms which depends on AltNameTbl coming
; after MachineName table.
public AltName1, AltName2, AltName3, AltName4, AltName5
public AltName6, AltName7, AltName8, AltName9, AltName10
public AltName11, AltName12, AltName13, AltName14, AltName15
public AltName16 ;M004
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
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
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 *
;* *
;*----------------------------------------------------------------------*
public InitDriver
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:
mov ax,(INT2F_ID SHL 8) OR INT2F_INS_CHK
int 2Fh ; make sure there's no other XMS installed
cmp al,80h ; Is INT 2F hooked?
jne IDNotInYet
mov dx,offset NowInMsg
jmp IDFlushMe
IDNotInYet:
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?
jae IDHMAOK
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
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
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 *
;* *
;*----------------------------------------------------------------------*
GPArgPtr dd ?
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="
@@: 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 ;-------------------------------
assume ds:nothing,es:nothing
; Scan until we see a non-blank or the end of line.
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...
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
jc GPBadParm
jz GPBadParm ; blank
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
jz GPNumDone
cmp al,'0'
jb GPBadParm
cmp al,'9'
ja GPBadParm
shl dx,1 ; Stupid 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
jz GPStrDone
stosb
loop GPStrLoop
GPStrDone:
mov byte ptr es:[di],0 ; Null terminate the string
mov dx,-1 ; In case parm expects a num, give'm
; a likely invalid one
; 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
@@: cmp al, 'C' ; CPUCLOCK:
jne @f
jmp GPGotCPUClock
@@: 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
jmp GPBadParm
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?
jz GPNoName
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
jz GPNoName
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
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
; 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
jmp GPNextParm
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
jmp GPBadParm
GPSetShadow:
mov fShadowOff,bl ; NZ if Shadow RAM should be turned off
jmp short GPNextParm
; 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
; Process /INT15= parameter
GPGotInt15:
cmp dx, 64 ; atleast 64K
jae @f
jmp GPBadParm
@@: call GetInt15Memory
cmp ax, dx ; enuf Ext Mem ?
jae @f
jmp GPBadParm
@@: mov [Int15MemSize], dx
; Fall through to GetNextParm
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
jmp GPNextArg
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
GPOffEOL dw -1
public GPGetChar
GPGetChar proc near
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:
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
_text ends
ifdef debug_tsr
EndStmt equ <end ExeStart>
STACK segment stack 'STACK'
db 1024 dup (?)
STACK ends
else
EndStmt equ <end>
endif
EndStmt