PAGE ,132
TITLE DXBOOT.ASM -- Dos Extender Startup Code
; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
;****************************************************************
;* *
;* DXBOOT.ASM - Dos Extender Startup Code *
;* *
;****************************************************************
;* *
;* Module Description: *
;* *
;* This module contains real mode initialization code that *
;* initializes the dos extender itself. This includes *
;* allocating and initializing the descriptor tables, and *
;* relocating the dos extender for protected mode operation. *
;* *
;****************************************************************
;* Revision History: *
;* *
;* 01/29/92 mattfe Build for MIPS *
;* 12/19/90 amitc NetHeapSize default made 4k from 8k *
;* 12/03/90 amitc Added support for 'Win30CommDriver' switch *
; in system.ini *
;* 10/09/90 earleh split LDT from GDT and reduced XMS handles *
;* needed to boot program to 1 *
;* 08/08/90 earleh DOSX and client privilege ring determined *
;* by equate in pmdefs.inc *
;* 05/07/90 jimmat Started VCPI related changes. *
;* 04/09/90 jimmat Detect if on 286 & 287 installed. *
;* 04/02/90 jimmat Added PM Int 70h handler. *
;* 09/27/89 jimmat Changes to use FindFile to locate child *
;* program. *
;* 08/29/89 jimmat Now hooks real mode Int 2Fh chain. *
;* 08/20/89 jimmat Removed A20 code since HIMEM version 2.07 *
;* now works properly across processor resets. *
;* 07/28/89 jimmat Int PM Int 30h & 41h to be ignored, not *
;* reflected to real mode. *
;* 07/14/89 jimmat Added call to EMMDisable *
;* 06/16/89 jimmat Ifdef'd combined DOSX/child .EXE code *
;* 05/22/89 jimmat Added Int 13h/25h/26h/67h hooks. *
;* 05/18/89 jimmat Added setting of real mode Int 30h hook. *
;* 03/31/89 jimmat Added Priv Level to selectors during *
;* relocation and removed some dead code. *
;* 03/15/89 jimmat Added INT 31h hook *
;* 03/11/89 jimmat Added support for TSS & LDT & ring 1 *
;* 03/07/89 jimmat Converted to use WDEB386 debugger *
;* 02/25/89 (GeneA): added support for combined exe file where *
;* the Dos Extender and the child reside in the same exe *
;* file. *
;* 02/17/89 (GeneA): fixed error handling code during init. *
;* 02/14/89 (GeneA): added initialization of INT15h vector, *
;* and changed segment limit of BIOS_CODE segment to *
;* 64k. *
;* 02/14/89 (GeneA): added code to copy protected mode code *
;* segment up into extended memory. Also code to allocate *
;* and copy GDT and IDT in extended memory. *
;* 01/31/89 (GeneA): reorganization of code. This module now *
;* contains only code for initializing the Dos Extender *
;* itself. *
;* 01/25/89 (GeneA): moved code for loading and relocating *
;* the child program here from dxinit.asm *
;* 01/24/89 (Genea): removed routines for hooking into real *
;* mode int 2Fh. (part of removing real mode int 2Fh *
;* interface from the dos extender). *
;* 12/13/88 (GeneA): created by moving code from DXINIT.ASM *
;* *
;****************************************************************
.286p
.287
; -------------------------------------------------------
; INCLUDE FILE DEFINITIONS
; -------------------------------------------------------
.xlist
.sall
include segdefs.inc
include gendefs.inc
include pmdefs.inc
IFDEF OBSELETE
include smartdrv.inc
ENDIF
if VCPI
include dxvcpi.inc
endif
IFDEF ROM
include dxrom.inc
ENDIF
include dpmi.inc
ifdef wow
include vdmtib.inc
endif
include intmac.inc
include dbgsvc.inc
.list
; -------------------------------------------------------
; GENERAL SYMBOL DEFINITIONS
; -------------------------------------------------------
;
; This structure defines the format of the EXE file header.
EXEHDR struc
idExeFile dw ? ;magic number to identify EXE file
cbLastPage dw ? ;number of bytes in last page of the file
crgbFileLen dw ? ;number of 512 byte pages in the file
clpRelocLen dw ? ;number of relocation entries in the table
cparHdrSize dw ? ;number of 16 byte paragraphs in header
cparMinAlloc dw ?
cparMaxAlloc dw ?
segStackInit dw ? ;initial stack segment
offStackInit dw ? ;initial stack pointer
wCheckSum dw ?
offCodeInit dw ? ;initial program counter
segCodeInit dw ? ;initial code segment
wRelocOffset dw ? ;byte offset of relocation table
idOverlay dw ? ;overlay number
EXEHDR ends
;
; This structure defines the parameter block to the XMS driver block
; move function.
XMSMOVE struc
cbxmsLen dd ? ;length of memory block
hxmsSource dw ? ;XMS source block handle
oxmsSource dd ? ;source offset
hxmsDest dw ? ;XMS destination block handle
oxmsDest dd ? ;destination offset
XMSMOVE ends
F16_INQUIRE equ 6F00h ; HP Vectra check code
NOP_OPCODE equ 090h ; NO-OP opcode
IRET_OPCODE equ 0CFh ; IRET opcode
FAR_JMP_OPCODE equ 0EAh ; JMP FAR opcode
SHORT_JMP_OPCODE equ 0EBh ; JMP SHORT opcode
; -------------------------------------------------------
; EXTERNAL SYMBOL DEFINITIONS
; -------------------------------------------------------
extrn PMIntr31:NEAR
extrn PMIntr13:NEAR
extrn PMIntr19:NEAR
extrn PMIntr28:NEAR
extrn PMIntr25:NEAR
extrn PMIntr26:NEAR
extrn PMIntr4B:NEAR
extrn PMIntr70:NEAR
extrn PMIntrDos:NEAR
extrn PMIntrMisc:NEAR
extrn PMIntrVideo:NEAR
extrn PMIntrMouse:NEAR
extrn PMIntrIgnore:NEAR
extrn PMInt2FHandler:NEAR
extrn PMIntrEntryVector:NEAR
extrn PMFaultEntryVector:NEAR
extrn AllocateSelector:NEAR
extrn FindSelector:NEAR
extrn ReadINIFile:NEAR
if NOT VCPI
extrn EMMDisable:NEAR
endif
IFDEF OBSELETE
extrn HPxBIOS:NEAR
endif
extrn FindFIle:NEAR
if VCPI
extrn InitGDTVCPI:NEAR
extrn InitIDTVCPI:NEAR
extrn InitTSSVCPI:NEAR
extrn CheckForVCPI:NEAR
externNP FreeEMSHandle
endif
IFDEF OBSELETE
IFNDEF ROM
extrn RMVectraKbdHook:NEAR
extrn PMVectraKbdHook:NEAR
ENDIF
ENDIF
if DEBUG
extrn PMDebugInt:NEAR
endif
ifdef wow
extrn NpxExceptionHandler:near
extrn EndNpxExceptionHandler:near
endif
IFDEF ROM
DXCODE segment
externFP NSetSegmentDscr
DXCODE ends
ENDIF
DXPMCODE segment
extrn CodeEndPM:NEAR
IFNDEF ROM
externFP NSetSegmentDscr
ENDIF
extrn PMFaultReflectorIRET:FAR
extrn WowHwIntrEntryVector:NEAR
DXPMCODE ends
; -------------------------------------------------------
; DATA SEGMENT DEFINITIONS
; -------------------------------------------------------
DXDATA segment
extrn selGDT:WORD
extrn segGDT:WORD
extrn selIDT:WORD
extrn segIDT:WORD
extrn bpGDT:FWORD
extrn bpIDT:FWORD
extrn sysTSS:WORD
extrn segPSP:WORD
extrn selPSP:WORD
extrn hmemDOSX:WORD
extrn f286_287:BYTE
extrn bpRmIVT:FWORD
extrn fhExeFile:WORD
extrn idCpuType:WORD
extrn cdscGDTMax:WORD
extrn cdscIDTMax:WORD
extrn rgbXfrBuf0:BYTE
extrn rgbXfrBuf1:BYTE
extrn selPSPChild:WORD
extrn SMRTDRVName:BYTE
extrn SMRTDRVDelta:BYTE
extrn clpRelocItem:WORD
extrn plpRelocItem:WORD
extrn lpfnXMSFunc:DWORD
ifdef NOT_NTVDM_NOT
extrn fMicroChannel:BYTE
endif
extrn PMFaultVector:DWORD
extrn lpfnUserMouseHandler:DWORD
extrn fUsingHMA:BYTE
ifdef WOW
extrn rgwWowStack:word
extrn FastBop:fword
extrn pbHwIntrStack:word
endif
if VCPI
extrn fVCPI:BYTE
extrn segBootPmode:WORD
endif
IFDEF ROM
extrn segDXCode:WORD
extrn segDXData:WORD
extrn PrevInt2FHandler:DWORD
ENDIF
IFNDEF WOW
extrn IretBopTable:BYTE
ENDIF
extrn HwIntHandlers:DWORD
public fDebug
fDebug db 0
szModName db 'DOSX',0 ;Our module name for use by WDEB386
if DEBUG
public lpchFileName
lpchFileName dd ?
endif
ifdef NOT_NTVDM_NOT
public fHPVectra
fHPVectra db 0 ;NZ if running on HP Vectra
endif
INIFileName db 'SYSTEM.INI',0 ;.INI file to read
public NetHeapSize, Int28Filter, FasterModeSwitch
INIKeywords label byte
db '[standard]',0
db 'netheapsize',0
NetHeapSize dw 4 ;default is 8k
db 'int28filter',0
Int28Filter dw 10 ;default is every 10th
db 'fastermodeswitch',0
FasterModeSwitch dw 0 ; 286 only, 0: use shutdown code 9
; 1: use shutdown code 0Ah
public fWin30CommDrv
db 'win30commdriver',0
fWin30CommDrv dw 0 ;0:no WIN 3.0 Comm Driver
;1:WIN 3.0 Comm Driver present
if DEBUG ;------------------------------------------------------------
public fTraceDOS
db 'tracedos',0
fTraceDOS dw 0
public fTraceFault
db 'tracefault',0
fTraceFault dw 0
public fTraceA20
db 'tracea20',0
fTraceA20 dw 1
public fTraceBug
db 'tracebug',0
fTraceBug dw 0
public TrapDOS
db 'trapdos',0
TrapDOS dw 0
db 'tableslow',0
fTablesLow dw 0
public fTraceReflect
db 'tracereflect',0
fTraceReflect dw 0
public fTraceMode
db 'tracemode',0
fTraceMode dw 0
endif ;DEBUG --------------------------------------------------------
db 0
szExeExtension db '.exe',0
; The following set of variables are used when copying our Pmode data
; structures into a HIMEM-allocated block.
IFDEF ROM ;--------------------------------------------------------
public lmaIDT,lmaGDT
CBIDTOFF = 0
CBGDTOFF = CDSCIDTDEFAULT * 8
CBTABLESIZE = CBGDTOFF + GDT_SIZE
.errnz CBIDTOFF and 0fh
.errnz CBGDTOFF and 0fh
lmaIDT dd CBIDTOFF
lmaGDT dd CBGDTOFF
lmaLDT dd CBTABLESIZE
ELSE ;ROM ---------------------------------------------------------
public lmaIDT,lmaGDT,lmaDXPMCODE
CBIDTOFF = 0
CBGDTOFF = CDSCIDTDEFAULT * 8
IFNDEF WOW
CBDXPMCODEOFF = CBGDTOFF + GDT_SIZE
ELSE
;
; Since we have no GDT for wow, we do not need space for it.
;
CBDXPMCODEOFF = CBGDTOFF
ENDIF
CBTABLESIZE = CBDXPMCODEOFF
.errnz CBIDTOFF and 0fh
.errnz CBGDTOFF and 0fh
.errnz CBDXPMCODEOFF and 0fh
lmaIDT dd CBIDTOFF
lmaGDT dd CBGDTOFF
lmaDXPMCODE dd CBDXPMCODEOFF
lmaLDT dd CBDXPMCODEOFF
ENDIF ;ROM ---------------------------------------------------------
extrn rgwStack:WORD
DXDATA ends
DXSTACK segment
extrn rgw0Stack:WORD
DXSTACK ends
; -------------------------------------------------------
page
; -------------------------------------------------------
; CODE SEGMENT VARIABLES
; -------------------------------------------------------
DXCODE segment
;************************************************************************
;
; REMEMBER... any code segment variables defined in this file
; will be discarded after initialization.
;
;************************************************************************
extrn CodeEnd:NEAR
IFDEF ROM
extrn lmaRomDXPMCode:DWORD
ELSE
extrn segDXCode:WORD
extrn segDXData:WORD
extrn selDgroup:WORD
ENDIF
ErrMsg MACRO name
extrn ER_&name:BYTE
ERC_&name equ offset ER_&name
ENDM
if VCPI
ErrMsg VCPI
endif ;VCPI
ErrMsg CPUTYPE
ErrMsg DXINIT
ErrMsg PROTMODE
ErrMsg NOHIMEM
ErrMsg EXTMEM
ErrMsg NOEXE
extrn RMInt2FHandler:NEAR
IFNDEF ROM
extrn PrevInt2FHandler:DWORD
extrn PrevInt69Handler:DWORD
ENDIF
szWinKernel db 'krnl'
szWinKernelVer db '286.exe',0
lpfnPrevXMS dd 0
if VCPI
externNP VCPIBootStrap
endif ;VCPI
DXCODE ends
DXPMCODE segment
extrn selDgroupPM:WORD
IFNDEF ROM
extrn segDXCodePM:WORD
extrn segDXDataPM:WORD
ENDIF
DXPMCODE ends
; -------------------------------------------------------
page
DXCODE segment
assume cs:DXCODE
; -------------------------------------------------------
; MAIN INITIALIZATION ROUTINES
; -------------------------------------------------------
; InitDosExtender -- This routine is the executive
; for initializing the dos extender.
;
; Input: none
; Output: various global tables and variables initialized.
; Dos Extender relocated for protected mode execution
; and moved into extended memory.
; Errors: returns CY set if error occurs, pointer to error message
; in DX
; Uses:
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public InitDosExtender
InitDosExtender:
; Init the key code & data segment variables.
mov ax,cs
mov segDXCode,ax
mov ax,ds
mov segDXData,ax
IFNDEF ROM
mov selDgroup,ax
ENDIF
IFNDEF ROM
push es
mov ax,seg DXPMCODE
mov es,ax
assume es:DXPMCODE
mov selDgroupPM,SEL_DXDATA or STD_RING
mov segDXCodePM,cs
mov segDXDataPM,ds
pop es
assume es:DGROUP
ENDIF
IFNDEF ROM
; Do an initial shrink of our program memory. This assumes that DXPMCODE
; is the last segment in program.
mov bx,(offset DXPMCODE:CodeEndPM) + 10h
shr bx,4
add bx,seg DXPMCODE
sub bx,segPSP
mov es,segPSP
dossvc 4Ah
push ds
pop es
ENDIF
IFDEF ROM
; Perform ROM specific initialization
extrn ROMInitialization:NEAR
call ROMInitialization
ENDIF
; Determine the type of CPU we are running on and make sure it is
; at least an 80286.
call CheckCPUType
cmp ax,2
jae indx14
mov ax,ERC_CPUTYPE
jmp indx80
indx14:
; If running on a 286, see if there is a 287 coprocessor installed
cmp al,2 ;286 processor?
jnz @f
int 11h ;math coprocessor installed?
test al,2
jz @f
ifndef WOW
inc f286_287 ; yup, 286 & 287
endif
@@:
; If on a 386 or greater, try to disable the EMM drivers we know about
if NOT VCPI
call EMMDisable
endif
IFDEF OBSELETE
; If SMARTDRV.SYS is installed, try to shrink it down to release additional
; extended or expanded VCPI memory for the child app to use.
call ShrinkSmartDrv
ENDIF
if VCPI
; Check to see if a VCPI server is active--if so, several things are
; different (mode switching, A20, no himem.sys needed, memory allocation...)
call CheckForVCPI
or ax,ax
jnz @f
endif
; Check if the machine is already running in protected mode. If so, we
; can't run.
ifndef WOW
smsw ax
test ax,1 ;check the protected mode bit
jz @f
mov ax,ERC_PROTMODE
jmp indx80
endif
@@:
; Get the full pathname of our EXE file, it's needed in a couple of places
call GetExeName
jnc @F
mov ax,ERC_DXINIT
jmp indx80
@@:
; Determine if the real mode Int 28h vector points anywhere. If it doesn't
; then we don't need to reflect Int 28h calls from protected to real mode.
; The user can still override this by putting a Int28Filter= entry in
; SYSTEM.INI.
push es
mov ax,3528h
int 21h
assume es:NOTHING
cmp byte ptr es:[bx],IRET_OPCODE ;Int 28h -> IRET?
jne @f
mov Int28Filter,0 ; yes, don't need to reflect
@@:
pop es
assume es:DGROUP
; Read SYSTEM.INI for any parameter overrides - NOTE: requires GetExeName
; having been run first!
mov bx,offset INIKeywords
mov dx,offset INIFileName
call ReadINIFile
; Check that the HIMEM.SYS driver is installed so that we can use it
; for extended memory management.
call SetupHimemDriver
jnc @F ; Himem is OK.
if VCPI
cmp fVCPI,0 ; Don't need it if VCPI.
jnz @f
endif
mov ax,ERC_NOHIMEM
jmp indx80
@@:
IFDEF OBSELETE
; Special HACK for some versions of Multisoft's PC-Kwik Disk Cache. Some
; versions of this cache hook the himem.sys driver, and will quickly crash
; Standard mode Windows. Multisoft believes this was necessary with old
; versions of himem.sys, but is no longer necessary with the Windows 3.0
; himem.sys. Their new versions check the himem version #, and don't hook
; it if Standard mode will work with it, but there are lots of old cache
; versions around. If a version of PC-Kwik cache has hooked himem.sys,
; we unhook them.
call UnhookPCKwik
ENDIF
; NTVDM - skip the check for HP vectra, Micro channel machine
; as softpc bios is a plain vanilla AT type bios 27-May-1993 Jonle
ifdef NOT_NTVDM_NOT
; Determine if running on an HP Vectra. We need to support one of their
; special extended BIOS calls if so (for the mouse). We also check if
; this is a 'Classic' Vectra (A & A+ models)--these machines have a bug
; in their BIOS that effects the protected to real mode switching. See
; EnterRealMode for more information.
push es
mov ax, F16_INQUIRE
xor bx, bx
int 16h
cmp bx, 'HP'
jne not_vectra
or fHPVectra,HP_VECTRA ;some sort of Vectra
mov ax, 0F000h
mov es, ax
assume es:NOTHING
mov al, es:[0FAh] ; in F000:FA
and al, 00011111b ; 0 means A or A+
jnz not_vectra
or fHPVectra,HP_CLASSIC
not_vectra:
;*** NOTE: ES still pushed on stack! ***
; Check if we're running on a Micro Channel equiped system.
;*** NOTE: ES still pushed on stack! ***
xor bx,bx
mov es,bx
mov ah,0C0h ;make Get System Environment call
int 15h
mov ax,es ;returns ES:BX -> configuration tbl
or ax,bx
jz not_MC
test byte ptr es:[bx+5],02h ;tbl byte 5, bit 02h is Micro Channel
jz not_MC ; bit flag
inc fMicroChannel ;it's a micro channel
not_MC:
pop es
assume es:DGROUP
endif
; Hook the real mode int vectors
mov ax,352Fh ;get previous Int 2Fh vector
int 21h
assume es:NOTHING
mov word ptr [PrevInt2FHandler],bx
mov word ptr [PrevInt2FHandler+2],es
IFNDEF ROM ;--------------------------------------------------------
ifdef NOT_NTVDM_NOT
test fHPVectra,HP_CLASSIC
jz @f
mov ax,3569h ;get previous HP Vectra A & A+
int 21h ; keyboard int handler
mov word ptr [PrevInt69Handler],bx
mov word ptr [PrevInt69Handler+2],es
@@:
endif
ENDIF ;ROM ---------------------------------------------------------
push ds
pop es
assume es:DGROUP
mov ax,cs ;point to our rMode Int 2Fh
mov ds,ax
assume ds:NOTHING
mov dx,offset DXCODE:RMInt2FHandler
mov ax,252Fh
int 21h
IFNDEF ROM ;--------------------------------------------------------
ifdef NOT_NTVDM_NOT
test fHPVectra,HP_CLASSIC
jz @f
mov dx,offset DXCODE:RMVectraKbdHook ;hook Vectra A & A+
mov ax,2569h ; keyboard interrupt
int 21h
@@:
endif
ENDIF ;ROM ---------------------------------------------------------
push es
pop ds
assume ds:DGROUP
; Allocate and initialize the descriptor tables and TSS.
if VCPI
cmp fVCPI,0 ; Don't need it if VCPI.
jnz indx20
endif
cCall AllocateExtMem
jnc indx20
mov ax,ERC_EXTMEM
jmp indx80
indx20:
ifdef WOW
push ds
push es
push bx
push dx
DPMIBOP GetFastBopAddress
mov word ptr [FastBop],bx
mov word ptr [FastBop + 2],dx
mov word ptr [FastBop + 4],es
pop dx
pop bx
pop es
pop ds
endif
call InitGlobalDscrTable ;set up the GDT
call InitIntrDscrTable ;set up the IDT
ifndef WOW ;bugbug
call InitTaskStateSeg ;set up the TSS
; If we are running under a debugger, initialize it
; See if the int 68h vector points to anything.
push es
xor ax,ax
mov es,ax
mov ax,es:[68h*4]
or ax,es:[68h*4+2]
pop es
jz dbini8
; Check if WDEB386 is installed, and if so, initialize it.
mov ah,43h
int 68h
cmp ax,0F386h
jnz dbini8
push ds
push es
mov ax,4402h ;initialize, 286 DOS extender
if VCPI
cmp fVCPI,0 ;if under a VCPI server, tell
jz @f ; wdeb386 by using subfunction 3
mov al,03h
@@:
endif
mov bx,SEL_DEBUG
mov cx,SEL_DEB386
mov dx,SEL_GDT
mov es,segIDT
xor di,di
mov ds,segGDT
mov si,di
int 68h
pop es
pop ds ;get DGROUP addressability back
; Now tell WDEB386 where our code is so it can match up the symbols
IFDEF ROM ;--------------------------------------------------------
mov ax,5040h ;DXCODE first (seg and sel)
mov bx,1 ;segment # 1
mov cx,cs ;segment value
mov dx,SEL_DXCODE ;selector value
mov di,offset szModName ;es:di -> module name string
int 68h
mov ax,5041h ;DGROUP next (seg and sel)
inc bx ;segment # 2
mov cx,ds ;segment value
mov dx,SEL_DXDATA ;selector value
int 68h
mov ax,5000h ;now DXPMCODE (sel only)
inc bx ;segment # 3
mov cx,SEL_DXPMCODE ;selector value
int 68h
ELSE ;ROM ---------------------------------------------------------
mov ax,5041h ;DGROUP first (seg and sel)
xor bx,bx ;segment # 0
mov cx,ds ;DGROUP segment
mov dx,SEL_DXDATA ;PM DGROUP selector
mov di,offset szModName ;es:di -> module name string
int 68h
mov ax,5040h ;now DXCODE (seg and sel)
mov bx,seg DXCODE
mov dx,ds
sub bx,dx ;less load address
mov cx,seg DXCODE
mov dx,SEL_DXCODE
int 68h
mov ax,5000h ;now DXPMCODE (sel only)
mov bx,seg DXPMCODE
mov dx,ds
sub bx,dx
mov cx,SEL_DXPMCODE
int 68h
ENDIF ;ROM ---------------------------------------------------------
mov fDebug,0FFh ;we are being bugged!
if DEBUG
mov fTraceBug,0 ; so don't bug others
endif
dbini8:
endif ;WOW bugbug
IFNDEF ROM ;--------------------------------------------------------
if DEBUG
; DOSX is written such that it does not require any segment fix ups for
; protected mode operation. This wasn't always the case, and it's easy
; to make create dependencies so the routine CheckDOSXFixUps exists in
; the debug version to check for segment fix ups in non-initialization
; code.
call CheckDOSXFixUps
jnc @F
mov ax,ERC_DXINIT
jmp short indx80
@@:
endif ;DEBUG
; Move the Extended memory segment up into extended memory.
mov dx,seg DXPMCODE
call MoveDosExtender
jc indx80
ENDIF ;ROM ---------------------------------------------------------
; Move the GDT and IDT up into extended memory.
call MoveDscrTables
IFNDEF ROM ;--------------------------------------------------------
; Parse the command line, and locate the child exe file
call ParseCommandLine
IFNDEF WHEN_COMMAND_COM_WORKS
if WINDOWS ;--------------------------------------------------------
; If this is a Windows specific version of DOSX, we only run one child EXE
; (krnl?86.exe).
push ds
push cs
pop ds
assume ds:NOTHING
IFNDEF ROM
cmp idCpuType,2
je @F
inc szWinKernelVer
@@:
ENDIF
mov si,offset DXCODE:szWinKernel
mov di,offset RELOC_BUFFER
call strcpy
pop ds
assume ds:DGROUP
endif ;WINDOWS -------------------------------------------------
call FindFile ;setup done by ParseCommandLine
jnc indx70
mov ax,ERC_NOEXE
jmp short indx80
indx70:
ENDIF
ENDIF ;ROM ---------------------------------------------------------
; Initialized okay!
clc
jmp short indx90
; Error occured. Free any extended memory blocks allocated and then
; return the error code.
indx80: push ax ;save the error code
if VCPI
cmp fVCPI,0
jz @f
call FreeEMSHandle
jmp short indxEXIT
@@:
endif
;
; If we have allocated an extended memory block, then free it.
; If we have allocated the HMA, then free it.
;
mov dx,hmemDOSX
or dx,dx
jz @F
xmssvc 0Dh
xmssvc 0Ah
@@:
cmp fUsingHMA,0
je @F
xmssvc 2
@@:
indxEXIT:
pop ax ;restore the error code
stc ;set error flag
indx90: ret
if VCPI
;
; The following function provides a stub XMS function to call in the
; case where there is no real XMS available or the installed driver
; is too old. It is intended to be used when DOS Extender is a VCPI
; client, and we want to either disallow XMS services or emulate
; them if not available. (Use xmssvc macro, even if no himem driver.)
;
XMSStub proc far
mov ax,0
mov dx,0
mov bl,80h ; BX = 80h-not implemented.
retf
XMSStub endp
endif
; -------------------------------------------------------
; AllocateExtMem -- Allocates memory used by DOSX for
; system tables and protected mode
; code.
; Allocates a temporary buffer in
; DOS memory for building the
; IDT and GDT.
; Input: none
; Output: none
; Uses: Flags
; Error: Carry set if cannot allocate memory.
;
; History:
; 10/05/90 - earleh wrote it
assume ds:DGROUP,es:NOTHING,ss:NOTHING
cProc AllocateExtMem,<PUBLIC,NEAR>,<ax,bx,dx>
cBegin
; If there is sufficient XMS memory, increase the size of the GDT/LDT
; up to the max of 8k selectors.
IFNDEF ROM
add word ptr lmaLDT,offset DXPMCODE:CodeEndPM
adc word ptr lmaLDT+2,0
ENDIF
add word ptr lmaLDT,0Fh ;make sure LDT is para aligned
adc word ptr lmaLDT+2,0
and word ptr lmaLDT,0FFF0h
IF 0
;
; Since we use dosx as a tsr, it will normally be loaded. If it uses
; the hma, then the hma is not available for normal dos apps.
;
; We need an extended memory block to load our protected mode code
; and system tables. Try for the HMA first.
;
mov dx,0ffffh
xmssvc 1
or ax,ax ; Got the HMA?
jz @F ; No
xor ax,ax
sub ax,word ptr lmaLDT ; Got the HMA, figure out how
shr ax,3 ; many LDT descriptors will fit.
dec ax ; (Probably get about 5000-6000.)
mov cdscGDTMax,ax ; Use that many.
xor bx,bx ; Linear address is, of course,
mov dx,0010h ; 00100000h.
inc fUsingHMA ; Set flag that we are using HMA.
jmp axm_address ; Jump to common code.
ENDIF
@@:
xmssvc 08h ;Query Free Extended memory
cmp dx,1024 ;is there more than 1 meg available?
jb @f
mov cdscGDTMax,CDSCMAXLDT ; yes, max out the GDT size
@@:
mov ax,cdscGDTMax
xor dx,dx
shl ax,3
adc dx,0
add ax,word ptr lmaLDT
adc dx,word ptr lmaLDT+2
add ax,1023d
adc dx,0 ; DX:AX = total extended memory needed
shl dx,6
shr ax,10d
or dx,ax ; DX = kbytes needed
mov si,dx ; SI = kbytes needed
xmssvc 09h ; allocate the XMS block
or ax,ax
jz axm_error
mov hmemDOSX,dx
xmssvc 0Ch ; lock it, DX:BX = address
or ax,ax
jz axm_error
axm_address:
add word ptr lmaIDT,bx ; relocate tables & Pmode code
adc word ptr lmaIDT+2,dx
add word ptr lmaGDT,bx
adc word ptr lmaGDT+2,dx
IFNDEF ROM
add word ptr lmaDXPMCODE,bx
adc word ptr lmaDXPMCODE+2,dx
ENDIF
add word ptr lmaLDT,bx
adc word ptr lmaLDT+2,dx
mov bx,(CDSCIDTDEFAULT + GDT_SELECTORS + 1) shr 1
dossvc 48h ; get a DOS block for building tables
jc axm_error ; abort if error
mov segIDT,ax
mov selIDT,ax
add ax,CDSCIDTDEFAULT shr 1
mov segGDT,ax
mov selGDT,ax
DPMIBOP PassTableAddress
clc
jmp axm_exit
axm_error:
stc
axm_exit:
cEnd
; -------------------------------------------------------
; SetupHimemDriver -- This routine checks that an XMS driver
; is installed and sets up for calling it.
;
; Input: none
; Output: none
; Errors: returns CY set if no driver available
; Uses: AX, all other registers preserved
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public SetupHimemDriver
SetupHimemDriver proc near
push bx
push es
; Check to see if there is an XMS driver resident.
mov ax,4300h
int 2Fh
cmp al,80h
jnz sthd80
; There is an XMS driver resident, so init for calling it.
mov ax,4310h
int 2Fh
mov word ptr [lpfnXMSFunc],bx
mov word ptr [lpfnXMSFunc+2],es
; Make sure this is the proper XMS/driver version
xmssvc 0 ;returns XMS vers in ax, driver vers in bx
cmp ax,300h ;assume okay if XMS 3.0 or above
jae @f
cmp ax,200h ;require at least XMS 2.00
jb sthd80
cmp bx,21Ch ;if XMS 2.x, require driver version 2.28+
jb sthd80 ; (himem used to have minor vers in decimal)
@@:
; Verify that the XMS driver's A20 functions work
xmssvc 5 ;local enable
or ax,ax
jz sthd80
xmssvc 7 ;query A20
push ax
xmssvc 6 ;local disable
or ax,ax
pop ax ;recover query status
jz sthd80
or ax,ax ;should be NZ, (A20 enabled status)
jz sthd80
; Looks good to me...
clc
jmp short sthd90
; No XMS driver resident or wrong version or we couldn't enable A20.
sthd80: stc
if VCPI
lea ax,XMSStub
mov word ptr [lpfnXMSFunc],ax
mov word ptr [lpfnXMSFunc+2],cs
endif ; VCPI
sthd90: pop es
pop bx
ret
SetupHimemDriver endp
IFDEF OBSELETE
; -------------------------------------------------------
; ShrinkSmartDrv -- This routine will attempt to free up
; additional extended memory by shrinking the amount of
; XMS used by an installed copy of SMARTDRV.SYS.
;
; Modified 10-Jul-1990 by earleh to shrink EMS usage if VCPI
; is active.
;
; Input: none
; Output: none
; Errors: none
; Uses: nono
assume ds:DGROUP,es:NOTHING,ss:DGROUP
public ShrinkSmartDrv
ShrinkSmartDrv proc near
; By and large, this code directly stolen from Win/386
enter SIZE SD_IOCTL_Read,0
pusha
mov dx,offset DGROUP:SMRTDRVName
mov ax,3D02h
int 21h
jc short NoSmartShrink
mov bx,ax
mov ax,4400h
int 21h
jc short NoSmartShrinkCls
test dx,0080h
jz short NoSmartShrinkCls
test dx,4000h
jz short NoSmartShrinkCls
lea dx,[bp-SIZE SD_IOCTL_Read]
mov cx,SIZE SD_IOCTL_Read
mov ax,4402h
int 21h
mov si,dx
jc short NoSmartShrinkCls
cmp ax,cx
jne short NoSmartShrinkCls
if VCPI
xor cl,cl ; cl = EMS cache present
mov al,SD_CACHE_EMS
test ss:[si.SD_IR_Driver_Type],al ; EMS cache?
jz novcpi ; No.
push bx
RMvcpi vcpiVER ; See if vcpi is available.
; Real EMS returns error.
pop bx
or ah, ah ; AH = 0 means yes.
jnz novcpi ; No VCPI, cannot use EMS.
mov cl,SD_CACHE_EMS ; SmartDrv is using VCPI mem.
novcpi:
mov al,SD_CACHE_XMS
or al,cl
test ss:[si.SD_IR_Driver_Type],al
else
test ss:[si.SD_IR_Driver_Type],SD_CACHE_XMS
endif
jz NoSmartShrinkCls ; No
mov ax,ss:[si.SD_IR_Current_Size]
sub ax,ss:[si.SD_IR_Min_Cache_Size]
jbe short NoSmartShrinkCls
mov [SMRTDRVDelta.SD_I_W_FuncGS],SD_IOCTL_WR_Shrink_Cache
mov [SMRTDRVDelta.SD_I_W_GS_Size],ax
mov dx,offset DGROUP:SMRTDRVDelta
mov cx,SIZE SD_IOCTL_WR_GrwShrk
mov ax,4403h
int 21h
jc short SmartShrinkFailed
cmp ax,cx
je short NoSmartShrinkCls
SmartShrinkFailed:
mov [SMRTDRVDelta.SD_I_W_GS_Size],0
NoSmartShrinkCls:
mov ax,3E00h
int 21h
NoSmartShrink:
popa
leave
ret
ShrinkSmartDrv endp
ENDIF
IFNDEF ROM ;--------------------------------------------------------
; -------------------------------------------------------
; MoveDosExtender -- This routine will move the Dos Extender
; protected mode segment up into extended memory.
; The himem driver function for moving memory blocks is used.
; The parameter block for this function is built in rgbXfrBuf0.
;
; Input: DX - real mode segment address of the segment to move
; Output: none
; Errors: returns CY set if error, Error code in AX
; Uses: AX used, all else preserved
; modifies rgbXfrBuf0
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public MoveDosExtender
MoveDosExtender proc near
; If we're running as a VCPI client, the code gets moved into EMS
; memory, not extended memory. (Same thing, really.)
if VCPI
cmp fVCPI, 0
jz mdeNotVCPI
; Relocate the dos extender's pm code to the ems page frame:DXPMCODEOFF
cCall VCPIBootStrap
ret
; Not a VCPI client, move the code segment to extended memory.
mdeNotVCPI:
endif ;VCPI
push bx
push cx
push dx
push si
cmp fUsingHMA,0
je mvdx40
;
; Our extended memory block is actually the HMA. Enable A20 and do
; the move ourselves.
;
xmssvc 5 ;local enable
push di
push ds
push es
mov cx,offset DXPMCODE:CodeEndPM
inc cx
and cx,0FFFEh
mov di,CBDXPMCODEOFF+10h
xor si,si
dec si
mov es,si
inc si
mov ds,dx
assume ds:NOTHING
;
; DS:SI = segIDT:0
; ES:DI = 0FFFF:CBDXPMCODEOFF+10h
; CX = code size
;
cld
rep movsb
pop es
assume ds:DGROUP
pop ds
pop di
xmssvc 6 ;local disable
jmp mvdx65
mvdx40:
; Move the data up into extended memory using the XMS driver's function.
mov si,offset DGROUP:rgbXfrBuf0
mov cx,offset DXPMCODE:CodeEndPM
inc cx
and cx,0FFFEh
mov word ptr [si].cbxmsLen,cx
mov word ptr [si].oxmsSource+2,dx ;real mode code segment address
mov ax,hmemDOSX
mov word ptr [si].hxmsDest,ax
xor ax,ax
mov word ptr [si].cbxmsLen+2,ax
mov [si].hxmsSource,ax
mov word ptr [si].oxmsSource,ax
mov word ptr [si].oxmsDest,CBDXPMCODEOFF
mov word ptr [si].oxmsDest+2,ax
xmssvc 0Bh
mvdx65:
clc
jmp short mvdx90
; Error occured
mvdx80: stc
mvdx90: pop si
pop dx
pop cx
pop bx
ret
MoveDosExtender endp
ENDIF ;ROM ---------------------------------------------------------
; -------------------------------------------------------
; MoveDscrTables -- This routine will move the GDT
; and IDT up into extended memory. The himem driver
; function for moving memory blocks is used. The parameter
; block for this function is built in rgbXfrBuf0.
;
; Input: none
; Output: none
; Errors: returns CY set if error occurs. Error code in AX
; Uses: AX, all else preserved
; modifies rgbXfrBuf0
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public MoveDscrTables
MoveDscrTables proc near
if VCPI
; If we're running as a VCPI client, the tables are built in their
; proper location.
cmp fVCPI, 0
jz @f
ret ;VCPI, nothing to do here...
@@:
endif ;VCPI
push bx
push si
push es
cmp fUsingHMA,0
je @F
;
; Our extended memory block is actually the HMA. Enable A20 and do
; the move ourselves.
;
xmssvc 5 ;local enable
push ds
push di
push cx
mov cx,CBTABLESIZE
mov di,10h
xor si,si
dec si
mov es,si
inc si
mov ds,segIDT
assume ds:NOTHING
;
; DS:SI = segIDT:0
; ES:DI = 0FFFF:10
; CX = tables size
;
cld
rep movsb
pop cx
pop di
pop ds
assume ds:DGROUP
xmssvc 6 ;local disable
clc
jmp mvdt_ret
@@:
; Move the GDT and IDT together.
mov si,offset DGROUP:rgbXfrBuf0
mov word ptr [si].cbxmsLen,CBTABLESIZE
mov word ptr [si].cbxmsLen+2,0
mov ax,segIDT
mov word ptr [si].oxmsSource+2,ax
mov ax,hmemDOSX
mov word ptr [si].hxmsDest,ax
xor ax,ax
mov [si].hxmsSource,ax
mov word ptr [si].oxmsSource,ax
mov word ptr [si].oxmsDest,ax
mov word ptr [si].oxmsDest+2,ax
xmssvc 0Bh
IFDEF WOW
;
; Move the initialized selectors from the gdt to the ldt
;
mov word ptr [si].cbxmsLen,GDT_SIZE
mov word ptr [si].cbxmsLen+2,0
mov ax,segGDT
mov word ptr [si].oxmsSource+2,ax
mov ax,hmemDOSX
mov word ptr [si].hxmsDest,ax
xor ax,ax
mov word ptr [si].hxmsSource,ax
mov word ptr [si].oxmsSource,ax
mov word ptr [si].oxmsDest+2,ax
mov word ptr [si].oxmsDest,CBTABLESIZE + offset DXPMCODE:CodeEndPM
xmssvc 0Bh
ENDIF
mvdt_ret:
mov es,segIDT ;free the low memory copy
dossvc 49h
pop es
pop si
pop bx
ret
MoveDscrTables endp
; -------------------------------------------------------
; InitGlobalDscrTable -- This function will allocate a memory
; buffer from DOS and then initialize it as a global
; descriptor table. It will also initialize all global
; variables associated with GDT management.
; Descriptors in the range 0 - SEL_USER are given statically
; defined meanings. Descriptors from SEL_USER up are defined
; dynamically when a program is loaded or when dynamic memory
; management calls occur.
;
; NOTE: This routine works in real mode. The buffer where
; the GDT is built is in low memory.
;
; Input: AX - number of descriptors to initialize
; Output: none
; Errors: CY set if unable to obtain memory for the GDT
; Uses: AX used, all other registers preserved
; bpGDT initialized.
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public InitGlobalDscrTable
InitGlobalDscrTable proc near
if VCPI
; If running as a VCPI client, call a different routine to initialize
; the GDT.
cmp fVCPI,0
jz @f
call InitGDTVCPI ;VCPI, use alternate init routine
ret
@@:
endif ;VCPI
push bx
push cx
push dx
push di
push es
mov word ptr [bpGDT+0],GDT_SIZE - 1
mov ax,word ptr lmaGDT
mov word ptr [bpGDT+2],ax
mov ax,word ptr lmaGDT+2
mov word ptr [bpGDT+4],ax
;
; Start by initializing the GDT to 0.
;
mov cx,GDT_SIZE shr 1
mov es,segGDT
assume es:NOTHING
xor ax,ax
mov di,ax
rep stosw
; Next, initialize the statically defined descriptors.
;
; Set up a descriptor for our protected mode code.
xor ax,ax ;AX = 0
mov dx,cs ;our code segment paragraph address
call B_ParaToLinear ;convert to linear byte address
mov cx,offset CodeEnd
cCall NSetSegmentDscr,<SEL_DXCODE,bx,dx,ax,cx,STD_CODE>
; Set up another one, but ring 0 this time. Limit should be 0FFFFh
; or 386 reset to real mode will not work properly.
mov cx,0FFFFh
cCall NSetSegmentDscr,<SEL_DXCODE0,bx,dx,ax,cx,ARB_CODE0>
;
; Set up one for the other segment, and a Ring 0 alias.
;
mov cx,offset CodeEndPM
IFDEF ROM
mov bx,word ptr lmaRomDXPMCODE
mov dx,word ptr lmaRomDXPMCODE+2
ELSE
mov bx,word ptr lmaDXPMCODE
mov dx,word ptr lmaDXPMCODE+2
ENDIF
cCall NSetSegmentDscr,<SEL_DXPMCODE,dx,bx,0,cx,STD_CODE>
cCall NSetSegmentDscr,<SEL_NBPMCODE,dx,bx,0,cx,STD_CODE>
ifndef WOW
cCall NSetSegmentDscr,<SEL_EH,dx,bx,0,cx,EH_CODE>
else
cCall NSetSegmentDscr,<SEL_EH,dx,bx,0,cx,STD_CODE>
endif
mov cx,0FFFFh
; Set up a descriptor for our protected mode data and stack area.
mov dx,ds ;our data segment paragraph address
call B_ParaToLinear ;convert to linear byte address
cCall NSetSegmentDscr,<SEL_DXDATA,bx,dx,ax,cx,STD_DATA>
IFNDEF WOW
; Set up descriptor for IRET HOOKS
push dx
push bx
add dx,offset IretBopTable
adc bx,0
cCall NSetSegmentDscr,<SEL_IRETHOOK,bx,dx,ax,cx,STD_CODE>
pop bx
pop dx
ELSE
; Set up descriptor for IRET HOOKS
push dx
push bx
add dx,offset FastBop
adc bx,0
cCall NSetSegmentDscr,<SEL_IRETHOOK,bx,dx,ax,cx,STD_CODE>
pop bx
pop dx
ENDIF
IFNDEF WOW
; And another one of those for ring 0
cCall NSetSegmentDscr,<SEL_DXDATA0,bx,dx,ax,cx,ARB_DATA0>
ENDIF
;
; Set up the exception handler stack alias.
;
push cx
mov cx,offset DGROUP:rgwStack - 1
cCall NSetSegmentDscr,<SEL_STACK_ALIAS,bx,dx,ax,cx,STD_DATA>
pop cx
;
; Set up descriptors pointing to our PSP and environment.
mov dx,segPSP ;segment address of the PSP
call B_ParaToLinear ;convert to linear byte address
cCall NSetSegmentDscr,<SEL_PSP,bx,dx,ax,cx,STD_DATA>
mov selPSP,SEL_PSP
;
push es
mov es,segPSP
assume es:PSPSEG
mov dx,segEnviron
call B_ParaToLinear
cCall NSetSegmentDscr,<SEL_ENVIRON,bx,dx,ax,7FFFH,STD_DATA>
pop es
assume es:nothing
; Set up a descriptor that points to the GDT.
mov dx,word ptr [bpGDT+2] ;get the GDT linear byte address
mov bx,word ptr [bpGDT+4]
mov cx,word ptr [bpGDT+0] ;get the GDT segment size
cCall NSetSegmentDscr,<SEL_GDT,bx,dx,ax,cx,STD_DATA>
; Set up a descriptor for the LDT and an LDT data alias.
mov cx,cdscGDTMax ;get count of descriptors
shl cx,3
dec cx
mov dx,word ptr lmaLDT
mov bx,word ptr lmaLDT+2
IFNDEF WOW
cCall NSetSegmentDscr,<SEL_LDT,bx,dx,ax,cx,STD_LDT>
ENDIF
cCall NSetSegmentDscr,<SEL_LDT_ALIAS,bx,dx,ax,cx,STD_DATA>
IFDEF WOW
; set up a readonly selector to the LDT for the wow kernel
cCall NSetSegmentDscr,<SEL_WOW_LDT,bx,dx,ax,cx,STD_DATA>
ENDIF
; Set up descriptors pointing to the BIOS code and data areas
mov cx,0FFFFH ; CX = 0FFFFH
cCall NSetSegmentDscr,<SEL_BIOSCODE,000fh,ax,ax,cx,STD_CODE>
mov dx,40h*16
cCall NSetSegmentDscr,<SEL_BIOSDATA,ax,dx,ax,cx,STD_DATA>
; Set up a descriptor pointing to the real mode interrupt vector table.
cCall NSetSegmentDscr,<SEL_RMIVT,ax,ax,ax,cx,STD_DATA>
IFNDEF WOW
; Setup a selector and data alias for the TSS
mov dx,ds ;get base address of TSS
call B_ParaToLinear ; (it may not be para aligned)
add dx,offset DGROUP:sysTSS
adc bx,ax
mov cx,(TYPE TSS286) - 1
cCall NSetSegmentDscr,<SEL_TSS,bx,dx,ax,cx,STD_TSS>
cCall NSetSegmentDscr,<SEL_TSS_ALIAS,bx,dx,ax,cx,STD_DATA>
; BUGBUG Need to make this work
; And, set up a selector for WDEB386 to use to access all of memory.
; This only works on a '386.
mov cx,0FFFFh
cCall NSetSegmentDscr,<SEL_DEBUG,ax,ax,cx,cx,STD_DATA>
; Set up the call gate descriptor for the reset to real mode routine.
extrn Reset286:NEAR
extrn Reset386:NEAR
mov cx,offset DXCODE:Reset286 ;assume 286
cmp idCpuType,3
jb @f
mov cx,offset DXCODE:Reset386 ;really 386
@@:
cCall NSetSegmentDscr,<SEL_RESET,0,SEL_DXCODE0,0,cx,STD_CALL>
; Init call gate descriptors for all the DynaLink services.
if DEBUG ;------------------------------------------------------------
extrn DXOutDebugStr:NEAR
mov ax,SEL_DYNALINK + (OutDebugStr shl 3)
mov cx,offset DXCODE:DXOutDebugStr
cCall NSetSegmentDscr,<ax,8,SEL_DXCODE0,0,cx,STD_CALL>
extrn DXTestDebugIns:NEAR
mov ax,SEL_DYNALINK + (TestDebugIns shl 3)
mov cx,offset DXCODE:DXTestDebugIns
cCall NSetSegmentDscr,<ax,0,SEL_DXCODE0,0,cx,STD_CALL>
endif ;DEBUG ---------------------------------------------------------
; Set up the fault reflector IRET call gate.
mov ax,offset DXPMCODE:PMFaultReflectorIRET
cCall NSetSegmentDscr,<SEL_RZIRET,5,SEL_EH,0,ax,STD_CALL>
ENDIF
IFDEF WOW
;
; Pass address of HwIntr stack, and form pointer to lockcount in
; VdmTib. This enables us to coordinate stack switching with
; the nt kernel and the monitor. These components will switch
; the stack on Hw Interrupt reflection, dosx will switch it
; back at iret.
;
push es
mov ax,SEL_DXDATA or STD_RING
mov es, ax
mov bx, pbHwIntrStack
DPMIBOP InitializePmStackInfo
mov ax, SIZE VdmPmStackInfo
cCall NSetSegmentDscr,<SEL_VDMTIB,cx,dx,0,ax,STD_DATA>
pop es
;
; Create a code selector for the NPX emulation exception handler
;
mov ax,offset EndNpxExceptionHandler
sub ax,offset NpxExceptionHandler
mov bx,offset DXPMCODE:NpxExceptionHandler
add bx,word ptr lmaDXPMCODE
mov dx,word ptr lmaDXPMCODE + 2
cCall NSetSegmentDscr,<SEL_NPXHDLR,dx,bx,0,ax,STD_CODE>
ENDIF
IFDEF WOW
;
; Send load notification to the debugger for DXDATA
;
push 1 ; data
push ds ; exe name
push offset EXEC_DXNAME
push ds ; module name
push offset szModName
push 0
push SEL_DXDATA OR STD_RING
push DBG_SEGLOAD
BOP BOP_DEBUGGER
add sp,16
;
; Send load notification to the debugger for DXCODE
;
push 0 ; code
push ds ; exe name
push offset EXEC_DXNAME
push ds ; module name
push offset szModName
push 2
push SEL_DXCODE OR STD_RING
push DBG_SEGLOAD
BOP BOP_DEBUGGER
add sp,16
;
; Send load notification to the debugger
;
push 0 ; code
push ds ; exe name
push offset EXEC_DXNAME
push ds ; module name
push offset szModName
push 3
push SEL_DXPMCODE OR STD_RING
push DBG_SEGLOAD
BOP BOP_DEBUGGER
add sp,16
ENDIF
clc ;worked! make sure CY is clear
; All done
igdt90: pop es
pop di
pop dx
pop cx
pop bx
ret
InitGlobalDscrTable endp
; -------------------------------------------------------
; InitIntrDscrTable -- This function will initialize the
; specified memory buffer as an Interrupt Descriptor Table,
; and set up all of the control variables associated with
; the IDT.
;
; NOTE: This routine works in real mode. The buffer where
; the IDT is built is in low memory.
; NOTE: The InitGlobalDscrTable function must be called before
; this function can be called.
;
; Input: AX - number of descriptors to initialize
; Output: none
; Errors: CY set if unable to obtain the memory required
; Uses: AX, all other registers preserved
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public InitIntrDscrTable
InitIntrDscrTable proc near
; int 3; debugbug
if VCPI
; If running as a VCPI client, call a different routine to initialize
; the IDT.
cmp fVCPI,0
jz @f
call InitIDTVCPI ;VCPI, use alternate init routine
ret
@@:
endif ;VCPI
push bx
push cx
push dx
push si
push di
push es
ifndef WOW
; Save the current pointer to the real mode interrupt vector table.
sidt fword ptr bpRmIVT
endif
mov es,selIDT
assume es:NOTHING
mov cx,cdscIDTMax ;number of descriptors in table
shl cx,3 ;convert to count of bytes
dec cx ;compute segment size limit
mov word ptr [bpIDT+0],cx
mov dx,word ptr lmaIDT
mov word ptr [bpIDT+2],dx
mov bx,word ptr lmaIDT+2
mov word ptr [bpIDT+4],bx
cCall NSetSegmentDscr,<SEL_IDT,bx,dx,0,cx,STD_DATA>
; Fill the IDT with interrupt gates that point to the fault handler and
; interrupt reflector entry vector.
xor di,di
ifndef WOW
mov dx,offset DXPMCODE:PMFaultEntryVector ;the 1st 32 go here
mov cx,32
iidt15: mov es:[di].offDest,dx
mov es:[di].selDest,SEL_EH or EH_RING
mov es:[di].cwParam,0
mov es:[di].arbGate,STD_INTR
mov es:[di].rsvdGate,0
add dx,3
add di,8
loop iidt15
mov dx,offset DXPMCODE:PMIntrEntryVector+(32*3) ; the rest go here
mov cx,070h - 32
ELSE
mov dx,offset DXPMCODE:PMIntrEntryVector
mov cx,8
iidt16: mov es:[di].offDest,dx
mov es:[di].selDest,SEL_DXPMCODE or STD_RING
mov es:[di].cwParam,0
mov es:[di].arbGate,STD_TRAP
mov es:[di].rsvdGate,0
add dx,3
add di,8
loop iidt16
mov dx,offset DXPMCODE:WowHwIntrEntryVector
mov cx,8
iidt17: mov es:[di].offDest,dx
mov es:[di].selDest,SEL_DXPMCODE or STD_RING
mov es:[di].cwParam,0
mov es:[di].arbGate,STD_INTR
mov es:[di].rsvdGate,0
add dx,8
add di,8
loop iidt17
mov dx,offset DXPMCODE:PMIntrEntryVector + 3 * 16
mov cx,070h - 16
ENDIF
iidt20: mov es:[di].offDest,dx
mov es:[di].selDest,SEL_DXPMCODE or STD_RING
mov es:[di].cwParam,0
mov es:[di].arbGate,STD_TRAP
mov es:[di].rsvdGate,0
add dx,3
add di,8
loop iidt20
mov dx,offset DXPMCODE:WowHwIntrEntryVector + 8*8
mov cx,8
iidt22: mov es:[di].offDest,dx
mov es:[di].selDest,SEL_DXPMCODE or STD_RING
mov es:[di].cwParam,0
mov es:[di].arbGate,STD_INTR
mov es:[di].rsvdGate,0
add dx,8
add di,8
loop iidt22
mov dx,offset DXPMCODE:PmIntrEntryVector + 078h * 3
mov cx,cdscIdtMax
sub cx,078h
iidt23: mov es:[di].offDest,dx
mov es:[di].selDest,SEL_DXPMCODE or STD_RING
mov es:[di].cwParam,0
mov es:[di].arbGate,STD_TRAP
mov es:[di].rsvdGate,0
add dx,3
add di,8
loop iidt23
;
; Set up the hw interrupt entry points
;
mov dx,offset DXPMCODE:PmIntrEntryVector + 8*3
mov si,offset HwIntHandlers
mov cx,8
iidt24: mov [si],dx
mov word ptr [si + 2],0
mov word ptr [si + 4],SEL_DXPMCODE OR STD_RING
mov word ptr [si + 6],0
add si,8
add dx,3
loop iidt24
mov cx,8
mov dx,offset DXPMCODE:PmIntrEntryVector + 070h * 3
iidt24a: mov [si],dx
mov word ptr [si + 2],0
mov word ptr [si + 4],SEL_DXPMCODE OR STD_RING
mov word ptr [si + 6],0
add si,8
add dx,3
loop iidt24a
; Now, fix up the ones that don't point to the interrupt reflector.
IFDEF WOW
mov es:[1h*8].offDest,offset PMIntrIgnore
mov es:[3h*8].offDest,offset PMIntrIgnore
mov es:[10h*8].offDest,offset PMIntrVideo
mov es:[13h*8].offDest,offset PMIntr13
mov es:[15h*8].offDest,offset PMIntrMisc
mov es:[19h*8].offDest,offset PMIntr19
ENDIF
mov es:[21h*8].offDest,offset DXPMCODE:PMIntrDos
mov es:[25h*8].offDest,offset DXPMCODE:PMIntr25
mov es:[26h*8].offDest,offset DXPMCODE:PMIntr26
mov es:[28h*8].offDest,offset DXPMCODE:PMIntr28
mov es:[2Fh*8].offDest,offset DXPMCODE:PMInt2FHandler
mov es:[30h*8].offDest,offset DXPMCODE:PMIntrIgnore
mov es:[31h*8].offDest,offset DXPMCODE:PMIntr31
mov es:[33h*8].offDest,offset DXPMCODE:PMIntrMouse
mov es:[41h*8].offDest,offset DXPMCODE:PMIntrIgnore
if DEBUG ;-------------------------------------------------------------
cmp fTraceBug,0
jz @f
mov es:[41h*8].offDest,offset DXCODE:PMDebugInt
mov es:[41h*8].selDest,SEL_DXCODE or STD_RING
@@:
endif ;DEBUG ---------------------------------------------------------
ifndef WOW
mov es:[4Bh*8].offDest,offset DXPMCODE:PMIntr4B
ifdef NOT_NTVDM_NOT
; HP Extended BIOS System Call handler
test fHPVectra,0ffh ;only do this for an HP Vectra
jz NoHPBios
; Supposedly the system driver is going to force the HP Bios to
; use interrupt 6Fh while Windows is running, so we don't need to
; search for the moveable HP Bios interrupt--just use Int 6Fh.
mov es:[6Fh*8].offDest,offset HPxBios
NoHPBios:
endif
endif
; The target of the following instruction used to be "es:[70h*8].offDest",
; but DaveH can't remember why he changed it to HwIntHandlers. I remember
; reviewing the change, and there was a legitimate reason having to do
; with some unusual platform (like atari or something). Maybe Dave or I will
; remember one of these days. - NeilSa
mov word ptr HwIntHandlers[8*8],offset DXPMCODE:PMIntr70
ifdef WOW
.386p
xor di,di
mov dx,offset DXPMCODE:PmFaultEntryVector
movzx edx,dx
mov cx,32
iidt25:
push dword ptr VDM_INT_INT_GATE
push di
push SEL_EH OR STD_RING
push edx
push SEL_DXDATA OR STD_RING
push dword ptr (offset DXDATA:rgw0Stack)
DPMIBOP SetFaultHandler
add sp,18
inc di
add dx,3
loop iidt25
;
; Set up all of the "gates" between 0 and 7 to be trap gates
;
mov cx,8
mov bx,0
xor di,di
iidt30:
push word ptr (VDM_INT_TRAP_GATE OR VDM_INT_16)
push di
push es:[bx].selDest
push es:[bx].rsvdGate ; high half of handler address
push es:[bx].offDest ; low half of handler address
DPMIBOP SetProtectedModeInterrupt
add sp,10
inc di
add bx,8
loop iidt30
;
; Set up all of the "gates" between 8 and F to be int gates
;
mov cx,8
iidt40:
push word ptr (VDM_INT_INT_GATE OR VDM_INT_16)
push di
push es:[bx].selDest
push es:[bx].rsvdGate ; high half of handler address
push es:[bx].offDest ; low half of handler address
DPMIBOP SetProtectedModeInterrupt
add sp,10
inc di
add bx,8
loop iidt40
;
; Set up all of the "gates" between 10 and 70 to be trap gates
;
mov cx,70h - 10h
iidt50:
push word ptr (VDM_INT_TRAP_GATE OR VDM_INT_16)
push di
push es:[bx].selDest
push es:[bx].rsvdGate ; high half of handler address
push es:[bx].offDest ; low half of handler address
DPMIBOP SetProtectedModeInterrupt
add sp,10
inc di
add bx,8
loop iidt50
;
; Set up the "gates" for the hardware interrupts to be int gates
;
mov cx,8
iidt60:
push word ptr (VDM_INT_INT_GATE OR VDM_INT_16)
push di
push es:[bx].selDest
push es:[bx].rsvdGate ; high half of handler address
push es:[bx].offDest ; low half of handler address
DPMIBOP SetProtectedModeInterrupt
add sp,10
inc di
add bx,8
loop iidt60
;
; Set up the rest of the "gates" to be trap gates
mov cx,0ffh - 78h
iidt70:
push word ptr (VDM_INT_TRAP_GATE OR VDM_INT_16)
push di
push es:[bx].selDest
push es:[bx].rsvdGate ; high half of handler address
push es:[bx].offDest ; low half of handler address
DPMIBOP SetProtectedModeInterrupt
add sp,10
inc di
add bx,8
loop iidt70
.286p
endif
; All done
iidt90: pop es
pop di
pop si
pop dx
pop cx
pop bx
ret
InitIntrDscrTable endp
; -------------------------------------------------------
;
; InitTaskStateSeg -- This function initializes the
; TSS for the DOS Extender.
;
; Input: none
; Output: none
; Errors: returns CY if unable to allocate memory
; Uses: all registers preserved
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public InitTaskStateSeg
InitTaskStateSeg proc near
if VCPI
cmp fVCPI,0
jz @f
call InitTSSVCPI
ret
@@:
endif
push ax
push cx
push di
; As a start, zero out the TSS
xor al,al
mov cx,type TSS286
mov di,offset DGROUP:sysTSS
rep stosb
; Set the LDT selector
mov sysTSS.tss_ldt,SEL_LDT
; Set the ring 0 stack seg/pointer, we don't bother to set the others
; since nothing runs below DOSX's ring. Currently very little code runs
; ring 0 - just when switching between real/proteted modes.
mov sysTSS.tss_ss0,SEL_DXDATA0
mov sysTSS.tss_sp0,offset DGROUP:rgw0Stack
; That's all it takes
pop di
pop cx
pop ax
clc
ret
InitTaskStateSeg endp
IFDEF OBSELETE
;--------------------------------------------------------
; PC - KWIK CACHE UNHOOK HACK
;--------------------------------------------------------
; UnhookPCKwik -- This routine unhooks any version of
; Multisoft's PC-Kwik Disk Cache that may have
; attached itself to the himem.sys driver. If we
; get here, we assume that himem is installed.
; New versions of the cache don't do this, and the
; older versions work just fine with the new himem
; without the hook. Standard mode will not run with
; the hook installed. This fix is the result of a
; lot of work by Microsoft and Multisoft.
;
; Input: none
; Output: none
; Errors: none
; Uses: AX, BX, CX, DX, SI, DI
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public UnhookPCKwik
UnhookPCKwik proc near
push es
; First, use Multisoft's method of checking if the Cache is installed.
mov ah,2Bh ;DOS set date call
mov cx,'CX' ;invalid data
int 21h
jc PCKwikRet ;not installed if CY set, AL != 0, or
or al,al ; CX != 'cx'
jnz PCKwikRet
cmp cx,'cx'
jnz PCKwikRet
; The disk cache is installed, trace the XMS driver chain to see if
; this is a version that hooks himem.sys.
call FindPCKwik
jc PCKwikRet ;sets CY if PC Kwik Cache not found
; FindPCKwik returns with ES:DI -> the JMP FAR XXXX:XXXX which links to
; the PC Kwik Cache, not the Cache code itself. The XXXX:XXXX in the
; far jump is the address of the cache hook. Now unhook the cache.
assume es:nothing
push ds
cli ;just to be safe...
lds si,dword ptr es:[di+1] ;get address of cache hook
assume ds:nothing
; If the cache hook starts with a far jmp, then someone else has hooked
; them and we need to pull them out of the middle.
cmp byte ptr ds:[si],FAR_JMP_OPCODE ;have they been hooked too?
jnz PCKwikNoOtherHooks
lds si,dword ptr ds:[si+1] ;yes! get address of whoever
mov word ptr es:[di+1],si ; hooked them and cut the
mov word ptr es:[di+3],ds ; cache out of the list
jmp short PCKwikUnhooked
PCKwikNoOtherHooks:
; The cache is at the end of the XMS driver chain, unhook them by changing
; the previous driver's far jmp to a short jmp, nop, nop, nop sequence.
mov ax,(03h shl 8) or SHORT_JMP_OPCODE ;short jmp, disp 3
stosw
mov ax,(NOP_OPCODE shl 8) or NOP_OPCODE ;nop, nop
stosw
stosb ;nop
PCKwikUnhooked:
sti
pop ds
assume ds:DGROUP
PCKwikRet:
pop es
ret
UnhookPCKwik endp
;--------------------------------------------------------
; FindPCKwik -- This routine tries to locate the
; PC Kiwk Disk Cache XMS driver hook code. It
; assumes that some XMS driver is loaded, but the
; cache hook may or may not be present.
;
; Input: none
; Output: If CY set, cache hook not found
; If CY clear, ES:DI -> JMP FAR XXXX:XXXX of
; previous driver (not the cache itself)
; Errors: none
; Uses: ES, DI
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public FindPCKwik
FindPCKwik proc near
push bx
les di,lpfnXMSFunc ;point to 1st driver (can't be Kwik)
; Traverse the list of drivers, looking for PC-Kwik. Done when found or
; end of chain.
CheckNextDriver:
cmp byte ptr es:[di],FAR_JMP_OPCODE
jnz NotFarJmp
mov bx,5 ;PC Kwik hook might be at es:[di+5]
call IsItPCKwik ;is this their hook code?
jz FoundPCKwik ; Z set if so
mov word ptr cs:lpfnPrevXMS,di ;remember where we've been
mov word ptr cs:[lpfnPrevXMS+2],es
les di,dword ptr es:[di+1] ;point to next XMS driver
jmp short CheckNextDriver ; and check it out...
NotFarJmp:
; We get here at the end of the XMS driver chain. This is either PC Kwik
; cache, or it isn't. Most himem hooking versions of their cache don't
; have a proper short jmp, nop, nop, nop header, but the last one they put
; out did. So... we check for both cases.
cmp byte ptr es:[di],SHORT_JMP_OPCODE
jnz SlimyVersion
mov bx,5 ;check for version of PC Kwik with
call IsItPCKwik ; proper header
jz FoundPCKwik
jmp short NoPCKwikFound
SlimyVersion:
xor bx,bx
call IsItPCKwik ;might be version without header
jnz NoPCKwikFound
; We found the PC-Kwik XMS driver hook! Return the address of the previous
; driver to the caller in ES:DI, and CY clear
FoundPCKwik:
les di,cs:lpfnPrevXMS
mov ax,es ;sanity check...
or ax,di
jz NoPCKwikFound
clc
jmp short FindPCKwikRet
NoPCKwikFound:
stc
FindPCKwikRet:
pop bx
ret
FindPCKwik endp
;--------------------------------------------------------
; IsItPCKwik -- This routine determines if es:[di+bx]
; points to the PC Kwik Disk Cache XMS driver hook
; routine.
;
; The Cache hook code is as follows:
;
; 2EFE0Exxxx dec byte ptr cs:[xxxx]
; 7405 jz xxxx
; 0E push cs
; E8xxxx call xxxx
; FB sti
; 2EFF1Exxxx call far cs:[xxxx]
; ...
;
; Input: ES:[DI+BX] -> potential cache hook code
; Output: Z set if cache found, Z clear otherwise
; Errors: none
; Uses: all registers preserved
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public IsItPCKwik
IsItPCKwik proc near
cmp word ptr es:[di+bx],0FE2Eh
jnz IsItPCKwikRet
cmp byte ptr es:[di+bx+2],0Eh
jnz IsItPCKwikRet
cmp word ptr es:[di+bx+5],0574h
jnz IsItPCKwikRet
cmp word ptr es:[di+bx+7],0E80Eh
jnz IsItPCKwikRet
cmp word ptr es:[di+bx+11],2EFBh
jnz IsItPCKwikRet
cmp word ptr es:[di+bx+13],1EFFh
IsItPCKwikRet:
ret
IsItPCKwik endp
ENDIF
; -------------------------------------------------------
; MISC. STARTUP ROUTINES
; -------------------------------------------------------
; *** CheckCPUType - Set global variable for CPU type
;
; This routine relies on Intel-approved code that takes advantage
; of the documented behavior of the high nibble of the flag word
; in the REAL MODE of the various processors. The MSB (bit 15)
; is always a one on the 8086 and 8088 and a zero on the 286 and
; 386. Bit 14 (NT flag) and bits 13/12 (IOPL bit field) are
; always zero on the 286, but can be set on the 386.
;
; For future compatibility of this test, it is strongly recommended
; that this specific instruction sequence be used. The exit codes
; can of course be changed to fit a particular need.
;
; CALLABLE FROM REAL MODE ONLY
;
; ENTRY: NONE
;
; EXIT: AX holds CPU type ( 0=8086,80186; 2=80286; 3=80386; 4=80486 )
;
; USES: AX, DS must point to DX data segment
; idCpuType initialized
;
; Modified: 07-31-90 Earleh added code from Kernel, originally
; supplied by Intel, to check for 80486. Added check
; for V86 mode just in case a Limulator or something
; is active.
;
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public CheckCPUType
CheckCPUType proc near
.8086
pushf ; save flags during cpu test
pushf
pop ax ; flags to ax
and ax, 0fffh ; clear bits 12-15
push ax ; push immediate is bad op-code on 8086
npopf ; try to put that in the flags
pushf
pop ax ; look at what really went into flags
and ah,0f0h ; mask off high flag bits
cmp ah,0f0h ; Q: was high nibble all ones ?
mov ax, 0
jz cidx ; Y: 8086
.286p
smsw ax
test ax,1 ; Protected mode?
jnz cid386 ; V86! Gotta be at least a 386.
push 0f000h ; N: try to set the high bits
npopf ; ... in the flags
pushf
pop ax ; look at actual flags
and ah,0f0h ; Q: any high bits set ?
mov ax, 2 ; at least 286
jz cidx ; N: 80286 - exit w/ Z flag set
; Y: 80386 - Z flag reset
; 386 or 486? See if we can set the AC (Alignment check) bit in Eflags
; Need to insure stack is DWORD aligned for this to work properly
.386
cid386:
mov ax, 3
push cx
push ebx
mov cx,sp ; Assume stack aligned
and cx,0011b ; set "pop" count
sub sp,cx ; Move to DWORD aligned
pushfd ; save entry flags (DWORD)
push dword ptr 40000h ; AC bit
popfd
pushfd
pop ebx
popfd ; Recover entry flags (DWORD)
add sp,cx ; pop off alignment bytes
test ebx,40000h ; Did AC bit set?
pop ebx
pop cx
jz short cidx ; No, 386
.286p
inc ax ; At least 80486...
cidx:
mov idCpuType,ax ;store CPU type in global
npopf ; restore flags after cpu test
CheckCPUType endp
; -------------------------------------------------------
; B_ParaToLinear
;
; This function will convert a paragraph address in the lower
; megabyte of memory space into a linear address for use in
; a descriptor table. This is a local duplicate of the function
; ParaToLinear in DXUTIL.ASM. This is duplicated here to avoid
; having to make far calls to it during the initialization.
;
; Input: DX - paragraph address
; Output: DX - lower word of linear address
; BX - high word of linear address
; Errors: none
; Uses: DX, BX used, all else preserved
assume ds:NOTHING,es:NOTHING,ss:NOTHING
B_ParaToLinear proc near
xor bh,bh
mov bl,dh
shr bl,4
shl dx,4
ret
B_ParaToLinear endp
IFNDEF ROM
if DEBUG ;-------------------------------------------------------
; -------------------------------------------------------
; CheckDOSXFixUps -- This routine will check for segment fix ups
; in non-initialization code that need to be converted from
; a segment to selector.
;
; This routine works by opening the EXE file that we were
; loaded from and examining the relocation table.
;
; 10-09-90 Earleh modified so that references in initialization
; code are not edited.
;
; 11-12-90 JimMat renamed from RelocateDosExtender and it now
; only checks for fix ups in DEBUG version since all fix ups
; in post-initialization code have been removed.
;
; Input: none
; Output: none
; Errors: returns CY set if error occurs
; Uses: AX, all else preserved
; modifies lpchFileName
assume ds:DGROUP,es:NOTHING,ss:DGROUP
public CheckDOSXFixUps
CheckDOSXFixUps proc near
push bp
mov bp,sp
push bx
push dx
push si
push di
push es
; Find the path to our exe fie.
mov word ptr [lpchFileName],offset EXEC_DXNAME
mov word ptr [lpchFileName+2],ds
; Set up for reading the relocation table from the exe file.
call B_InitRelocBuffer
jc rldx90 ;get out if error
; Go down through the relocation table and for each fixup item,
; patch in our selector.
mov bx,segPSP
add bx,10h ;the relocation table items are relative
; to the initial load address of our program
; image which is immediately after the PSP
rldx40: call B_GetRelocationItem ;get next relocation table entry
jz rldx60 ;if end of table, get out
mov di,ax ;offset of relocation item
add dx,bx ;adjust relocation item segment for our load
; address
mov es,dx ;
;
; Do not fixup instructions in initialization code.
;
cmp dx,seg DXCODE
jne rldx41
cmp di,offset DXCODE:CodeEnd
jnc rldx40
rldx41:
cmp dx,seg DXPMCODE
jne rldx42
cmp di,offset DXPMCODE:CodeEndPM
jnc rldx40
rldx42:
mov ax,es:[di] ;get the current fixup contents
cmp ax,seg DXCODE ;is it the mixed mode segment?
jnz rldx44
extrn lCodeSegLoc:WORD
cmp di,offset DXCODE:lCodeSegLoc ;special far jmp to flush
jz rldx40 ; pre-fetch queue? ok if so.
; Shouldn't get here--tell developer he screwed something up!
int 3 ;****************************************
mov word ptr es:[di],SEL_DXCODE or STD_RING
jmp short rldx40
rldx44: cmp ax,seg DXPMCODE ;is it the protected mode only segment
jnz rldx40
; Shouldn't get here--tell developer he screwed something up!
int 3 ;****************************************
mov word ptr es:[di],SEL_DXPMCODE or STD_RING
jmp rldx40 ;and repeat for the next one
; We have gone through the entire relocation table, so close up the exe file
rldx60: mov bx,fhExeFile
dossvc 3Eh
;
clc
jmp short rldx90
;
; Error occured
rldx80: stc
;
; All done
rldx90: pop es
pop di
pop si
pop dx
pop bx
mov sp,bp
pop bp
ret
CheckDOSXFixUps endp
; -------------------------------------------------------
; B_InitRelocBuffer -- This routine will open the EXE
; file and initialize for reading the relocation table
; as part of relocating the program for protected mode
; execution.
;
; Input: lpchFileName - pointer to exe file name
; Output: none
; Errors: returns CY set if error occurs
; Uses: AX modified, all other registers preserved
; sets up static variables:
; clpRelocItem, plpRelocItem, fhExeFile
; modifies rgbXfrBuf1 at offset RELOC_BUFFER
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public B_InitRelocBuffer
B_InitRelocBuffer proc near
push bx
push cx
push dx
push si
;
; Open the EXE file.
push ds
lds dx,lpchFileName
mov al,0
dossvc 3Dh ;attempt to open the exe file
pop ds
jc inrl80 ;get out if error occurs
;
mov fhExeFile,ax ;store the file handle
mov bx,ax ;file handle to BX also
; Read the EXE file header, so that we can get information about
; the relocation table.
mov dx,offset RELOC_BUFFER
mov si,dx
mov cx,32
dossvc 3Fh
jc inrl80 ;get out if error
cmp ax,32
jnz inrl80
;
; Get the important values from the exe file header.
cmp [si].idExeFile,5A4Dh ;make sure it is an EXE file
jnz inrl80
mov ax,[si].clpRelocLen ;number of relocation items
mov clpRelocItem,ax
mov plpRelocItem,0FFFFh ;init the pointer to the first one
; to a bogus value to force the initial
; buffer to be loaded
;
; Get the location of the relocation table, and move the file pointer
; to its start.
xor cx,cx
mov dx,[si].wRelocOffset
mov al,cl
dossvc 42h
jnc inrl90
;
; Error occured
inrl80: stc
;
; All done
inrl90: pop si
pop dx
pop cx
pop bx
ret
B_InitRelocBuffer endp
; -------------------------------------------------------
; B_GetRelocationItem -- This routine will return the next
; relocation table entry from the exe file being relocated.
;
; Input: none
; Output: AX - offset of relocation item pointer
; DX - segment of relocation item pointer
; Errors: returns ZR true if end of table and no more items
; Uses: AX, DX modified, all other registers preserved
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public B_GetRelocationItem
B_GetRelocationItem proc near
push si
;
cmp clpRelocItem,0 ;are there any relocation items left?
jz gtrl90 ;get out if not
;
; Check if the buffer is empty. The buffer for the relocation table is
; at offset RELOC_BUFFER in the buffer rgbXfrBuf1, and is 512 bytes long.
cmp plpRelocItem,offset RELOC_BUFFER + 512
jc gtrl40
;
; The buffer is empty, so we need to read the next part of it in.
push cx
push bx
push dx
mov ax,clpRelocItem ;number of items left in file
shl ax,2 ;multiply by size of relocation item
jc gtrl22 ;check for overflow
cmp ax,512 ;check if bigger than the buffer
jc gtrl24
gtrl22: mov ax,512 ;use buffer size as size of transfer
gtrl24: mov cx,ax
mov dx,offset RELOC_BUFFER
mov plpRelocItem,dx ;pointer to next reloc item to return
mov bx,fhExeFile
dossvc 3Fh
pop dx
pop bx
jc gtrl28 ;if error occured
cmp ax,cx ;or, if we didn't get as much as we asked
jnz gtrl28 ; for, we have an error
pop cx
jmp short gtrl40
;
gtrl28: pop cx
stc
jmp short gtrl90
;
; Get the next relocation item from the buffer.
gtrl40: mov si,plpRelocItem
lods word ptr [si] ;get the offset part of the reloc item
mov dx,ax
lods word ptr [si] ;get the segment part of the reloc item
xchg dx,ax ;put offset in AX, and segment in DX
mov plpRelocItem,si ;store the updated pointer
dec clpRelocItem ;and bump the count down by 1
or si,si ;clear the zero flag
;
; All done.
gtrl90: pop si
ret
B_GetRelocationItem endp
endif ;DEBUG --------------------------------------------------------
ENDIF
; -------------------------------------------------------
; GetExeName -- This routine will put a copy of the complete
; path name to the dos extender's exe file. In a name
; buffer in rgbXfrBuf1.
;
; Input: none
; Output: EXEC_DXNAME buffer updated with complete pathname.
; Errors: returns CY set if environment not correctly built.
; Uses: all preserved
assume ds:DGROUP,es:DGROUP,ss:DGROUP
public GetExeName
GetExeName proc near
push ax
push si
push di
push ds
;;;IFDEF ROM ;--------------------------------------------------------
IF 0
; For ROM, what's really wanted here is to get a pointer to the system
; subdirectory for doing file searches later on. There is no ROM
; DOSX.EXE file. The ROM test code is not necessary now that WIN.COM
; and the swapper updated for ROM Windows. 5/13/91
; Until someone builds the environment/arg vector properly, force the
; exe path to be WINDIR\system32\dosx.exe
jmp short @f
szWINDIR db 'WINDIR',0
szSYSDOSX db '\SYSTEM32\DOSX.EXE',0
@@:
mov di, offset DXCODE:szWINDIR
push es
push cs
pop es ;es:di -> 'WINDIR'
extrn GetEnv:NEAR
call GetEnv ;find WINDIR env variable
assume ds:NOTHING
pop es
jnz gtxe80 ; or fail
mov di,offset EXEC_DXNAME ;copy WINDIR value
call strcpy
push cs ;append \system32\dosx.exe
pop ds
mov si,offset DXCODE:szSYSDOSX
call strcpy
clc
jmp short gtxe90
ENDIF
;;;ELSE ;ROM ---------------------------------------------------------
; The name of the current program is stored at the end of the environment
; table. There are two bytes of 0 to indicate end of table, a byte
; with a 1 in it followed by another byte of 0 and then the null terminated
; string with the current program name.
gtxe20: mov ds,segPSP
assume ds:PSPSEG
mov ds,segEnviron
assume ds:NOTHING
xor si,si
gtxe22: lods byte ptr [si] ;get next byte from environment
or al,al ;test if 0
jnz gtxe22 ;if not, keep looking
lods byte ptr [si] ;get next byte
or al,al ;see if it is 0 also
jnz gtxe22
; We have found the double 0 at the end of the environment. So
; we can now get the name. At the end of the environment is an
; argc, argv construct. (i.e. a word giving the count of strings
; followed by an array of strings). Under DOS, argc is always 1,
; so check that there is a word of 1 here. If not, this environment
; wasn't built correctly and we don't know what is here.
lods word ptr [si]
cmp ax,1
jnz gtxe80
; We have the pointer to the name, now copy it.
mov di,offset EXEC_DXNAME
call strcpy
clc
jmp short gtxe90
;;;ENDIF ;ROM ---------------------------------------------------------
; We have an error.
gtxe80: stc ;set error condition flag
gtxe90: pop ds
pop di
pop si
pop ax
ret
GetExeName endp
IFNDEF ROM ;--------------------------------------------------------
; -------------------------------------------------------
; COMMAND LINE PARSING ROUTINES
; -------------------------------------------------------
; ParseCommandLine -- This function will examine the dos
; command line that caused the Dos Extender to be exec'd
; and determine what the user wants done. It will set
; up the various buffers required for the child program
; to be loaded.
;
; NOTE: the child exe file name read from the command line
; is placed in RELOC_BUFFER in the case where the child
; name is specified on the command line. This buffer is
; used later when reading the relocation table while
; performing the fixups on the child.
;
; Input: none
; Output: AL - 0 if empty command line, else non-zero
; parse buffers in rgbXfrBuf1 set up.
; Errors: none
; Uses: AX, all else preserved
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public ParseCommandLine
ParseCommandLine proc near
push si
push di
push ds
mov ds,segPSP
assume ds:PSPSEG
mov si,81h ;pointer to command line in PSP
; Skip any white space in front of the child program name.
prsc12: lods byte ptr [si]
cmp al,' '
jz prsc12
; HACK - We Don't want this code for either MIPS or X86
; We should change ifdef WOW for ifdev x86
; Mattfe Jan 27.
if 0
; We don't do this for wow, since we will always run krnl?86.exe, but we
; want the rest of the command line passed to the windows kernel
; Get the child program name from the command line.
mov di,offset RELOC_BUFFER
mov byte ptr es:[di],0
cmp al,0Dh ;check for end of line
jz prsc90 ;if end of line, then we have a blank line.
dec si
prsc14: lods byte ptr [si] ;get the next file name character
call IsFileNameChar ;are we at the end of the name yet?
jnz prsc16
stos byte ptr [di] ;if not, continue
jmp short prsc14
; Check if the user entered the .EXE extension. If not, we need to
; add it ourselves.
prsc16: mov byte ptr es:[di],0
push si
push ds
mov ax,es
mov ds,ax
push di
sub di,4
mov si,offset DGROUP:szExeExtension
call strcmpi
pop di
jz @F
call strcpy
@@: pop ds
pop si
dec si
else
dec si
endif
; Copy the command line tail following the program name to the command
; line buffer for use when we load the child.
prsc40: push si ;save current point in parse
mov di,offset EXEC_CMNDLINE + 1
xor dl,dl ;count characters in command line tail
prsc42: lods byte ptr [si] ;get the next character
stos byte ptr [di] ;store it into the output buffer
cmp al,0Dh ;is it the end of the line?
jz prsc44
inc dl ;count the character
jmp prsc42
prsc44: mov es:[EXEC_CMNDLINE],dl ;store the character count
pop si ;restore the buffer pointer
; Now we want to set up the two default FCB's by letting DOS parse the
; first two parameters on the command line.
mov di,offset EXEC_FCB0
mov al,1
dossvc 29h
mov di,offset EXEC_FCB1
mov al,1
dossvc 29h
prsc90:
pop ds
pop di
pop si
ret
ParseCommandLine endp
ENDIF ;ROM ---------------------------------------------------------
; -------------------------------------------------------
; strcpy -- copy a null terminated string.
;
; Input: DS:SI - pointer to source string
; ES:DI - pointer to destination buffer
; Output: ES:DI - pointer to end of destination string
; Errors: none
; Uses: DI modified, all else preserved
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public strcpy
strcpy proc near
push ax
push si
stcp10: lods byte ptr [si]
stos byte ptr [di]
or al,al
jnz stcp10
dec di
pop si
pop ax
ret
strcpy endp
IFNDEF ROM ;--------------------------------------------------------
; -------------------------------------------------------
; strcmpi -- This function will perform a case insensitive
; comparison of two null terminated strings.
;
; Input: DS:SI - string 1
; ES:DI - string 2
; Output: ZR if the strings match, else NZ
; CY set if string 1 less than string 2
; Errors: none
; Uses: all registers preserved
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public strcmpi
strcmpi proc near
push si
push di
stcm20: mov al,byte ptr ds:[si]
call toupper
mov ah,al
mov al,byte ptr es:[di]
call toupper
cmp ah,al
jnz stcm90
or al,ah
jz stcm90
inc si
inc di
jmp stcm20
stcm90: pop di
pop si
ret
strcmpi endp
; -------------------------------------------------------
; IsFileNameChar -- This function will examine the
; character in AL and determine if it is a legal character
; in an MS-DOS file name.
;
; Input: AL - character to test
; Output: ZR true if character is legal in a file name
; Errors: none
; Uses: all registers preserved
assume ds:NOTHING,es:NOTHING,ss:NOTHING
IsFileNameChar proc near
push ax
cmp al,20h ;is it a control character
jbe isfc80 ;if so, it isn't valid
cmp al,':'
jz isfc80
cmp al,';'
jz isfc80
cmp al,','
jz isfc80
cmp al,'='
jz isfc80
cmp al,'+'
jz isfc80
cmp al,'<'
jz isfc80
cmp al,'>'
jz isfc80
cmp al,'|'
jz isfc80
cmp al,'/'
jz isfc80
cmp al,'"'
jz isfc80
cmp al,'['
jz isfc80
cmp al,']'
jz isfc80
xor al,al
jmp short isfc90
; Not a valid file name character
isfc80: or al,0FFh
isfc90: pop ax
ret
IsFileNameChar endp
ENDIF ;ROM ---------------------------------------------------------
; -------------------------------------------------------
; toupper -- This function will convert the character
; in AL into upper case.
;
; Input: AL - character to convert
; Output: AL - upper case character
; Errors: none
; Uses: AL modified, all else preserved
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public toupper
toupper proc near
cmp al,'a'
jb toup90
cmp al,'z'
ja toup90
sub al,'a'-'A'
toup90:
ret
toupper endp
; -------------------------------------------------------
DXCODE ends
;
;****************************************************************
end