summaryrefslogtreecommitdiffstats
path: root/private/mvdm/dpmi
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/mvdm/dpmi
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/mvdm/dpmi')
-rw-r--r--private/mvdm/dpmi/486/dxboot.asm3214
-rw-r--r--private/mvdm/dpmi/486/dxfunc.asm1631
-rw-r--r--private/mvdm/dpmi/486/dxint31.asm2966
-rw-r--r--private/mvdm/dpmi/486/dxintr.asm3194
-rw-r--r--private/mvdm/dpmi/486/dxmain.asm3369
-rw-r--r--private/mvdm/dpmi/486/dxstrt.asm1736
-rw-r--r--private/mvdm/dpmi/486/dxutil.asm2980
-rw-r--r--private/mvdm/dpmi/cmacros.inc1230
-rw-r--r--private/mvdm/dpmi/cmacros.mas2450
-rw-r--r--private/mvdm/dpmi/dosx.def26
-rw-r--r--private/mvdm/dpmi/dosx.lnk21
-rw-r--r--private/mvdm/dpmi/dosxi.lnk22
-rw-r--r--private/mvdm/dpmi/dxboot.asm3307
-rw-r--r--private/mvdm/dpmi/dxbug.asm1007
-rw-r--r--private/mvdm/dpmi/dxdisk.asm953
-rw-r--r--private/mvdm/dpmi/dxdma.asm714
-rw-r--r--private/mvdm/dpmi/dxemm.asm425
-rw-r--r--private/mvdm/dpmi/dxemm2.asm139
-rw-r--r--private/mvdm/dpmi/dxend.asm74
-rw-r--r--private/mvdm/dpmi/dxendpm.asm46
-rw-r--r--private/mvdm/dpmi/dxfind.asm426
-rw-r--r--private/mvdm/dpmi/dxfunc.asm1718
-rw-r--r--private/mvdm/dpmi/dxhpbios.asm478
-rw-r--r--private/mvdm/dpmi/dxini.asm467
-rw-r--r--private/mvdm/dpmi/dxint31.asm3183
-rw-r--r--private/mvdm/dpmi/dxintr.asm5217
-rw-r--r--private/mvdm/dpmi/dxmain.asm3376
-rw-r--r--private/mvdm/dpmi/dxmmgr.asm2740
-rw-r--r--private/mvdm/dpmi/dxnetbio.asm2417
-rw-r--r--private/mvdm/dpmi/dxoem.asm179
-rw-r--r--private/mvdm/dpmi/dxrom.asm805
-rw-r--r--private/mvdm/dpmi/dxrom.inc92
-rw-r--r--private/mvdm/dpmi/dxstrt.asm1833
-rw-r--r--private/mvdm/dpmi/dxutil.asm3177
-rw-r--r--private/mvdm/dpmi/dxvcpi.asm586
-rw-r--r--private/mvdm/dpmi/dxvcpi.inc374
-rw-r--r--private/mvdm/dpmi/dxvcpibt.asm1606
-rw-r--r--private/mvdm/dpmi/gendefs.inc654
-rw-r--r--private/mvdm/dpmi/hostdata.inc79
-rw-r--r--private/mvdm/dpmi/interupt.inc52
-rw-r--r--private/mvdm/dpmi/makefile220
-rw-r--r--private/mvdm/dpmi/migrate.bat20
-rw-r--r--private/mvdm/dpmi/mk.cmd57
-rw-r--r--private/mvdm/dpmi/netbios.inc106
-rw-r--r--private/mvdm/dpmi/ntintr32.asm937
-rw-r--r--private/mvdm/dpmi/ntnpxem.asm128
-rw-r--r--private/mvdm/dpmi/pmdefs.inc497
-rw-r--r--private/mvdm/dpmi/program.sed7
-rw-r--r--private/mvdm/dpmi/prot386.inc36
-rw-r--r--private/mvdm/dpmi/segdefs.inc119
-rw-r--r--private/mvdm/dpmi/smartdrv.inc147
-rw-r--r--private/mvdm/dpmi/stackchk.inc85
-rw-r--r--private/mvdm/dpmi/usa/dxmsg.asm158
-rw-r--r--private/mvdm/dpmi/woaswapi.inc130
-rw-r--r--private/mvdm/dpmi/wowmac.inc59
55 files changed, 61669 insertions, 0 deletions
diff --git a/private/mvdm/dpmi/486/dxboot.asm b/private/mvdm/dpmi/486/dxboot.asm
new file mode 100644
index 000000000..ad997da6e
--- /dev/null
+++ b/private/mvdm/dpmi/486/dxboot.asm
@@ -0,0 +1,3214 @@
+ 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_x86
+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_x86
+ 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
+ifndef WOW_x86
+externFP NSetGDTSegmentDscr
+endif
+ENDIF
+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 lpfnUserMouseHandler:DWORD
+ extrn fUsingHMA:BYTE
+ifdef WOW_x86
+ 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_x86
+ extrn IretBopTable:BYTE
+ENDIF
+
+
+ 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_x86
+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
+ extrn ResetStack: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_x86
+ 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_x86
+ 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_x86
+ 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_x86 ;bugbug
+ call InitTaskStateSeg ;set up the TSS
+endif
+
+ifndef WOW
+; 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_x86 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_x86
+ 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_x86
+; 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_x86
+; 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_x86
+ cCall NSetSegmentDscr,<SEL_LDT,bx,dx,ax,cx,STD_LDT>
+ENDIF
+ cCall NSetSegmentDscr,<SEL_LDT_ALIAS,bx,dx,ax,cx,STD_DATA>
+IFDEF WOW_x86
+ ; 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_x86
+; 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.
+
+ifndef WOW
+ mov cx,0FFFFh
+ cCall NSetSegmentDscr,<SEL_DEBUG,ax,ax,cx,cx,STD_DATA>
+endif
+
+; 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.
+
+ENDIF
+
+IFDEF WOW_x86
+;
+; 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_x86
+; 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
+
+ mov dx,offset DXPMCODE:PmIntrEntryVector
+ mov cx,cdscIdtMax
+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
+
+; Now, fix up the ones that don't point to the interrupt reflector.
+ 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
+
+ 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_x86
+ 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
+ mov es:[70h*8].offdest,offset DXPMCODE:PMIntr70
+ .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_DXPMCODE OR STD_RING
+ push edx
+ push 0
+ push 0
+ push 0
+ DPMIBOP SetFaultHandler
+ add sp,18
+ inc di
+ add dx,5
+ 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
+; 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:ResetStack
+
+; 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
diff --git a/private/mvdm/dpmi/486/dxfunc.asm b/private/mvdm/dpmi/486/dxfunc.asm
new file mode 100644
index 000000000..2bf42d636
--- /dev/null
+++ b/private/mvdm/dpmi/486/dxfunc.asm
@@ -0,0 +1,1631 @@
+
+
+ PAGE ,132
+ TITLE DXFUNC.ASM -- Dos Extender Function Handlers
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXFUNC.ASM - Dos Extender Function Handlers *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains the functions for handling the Dos *
+;* Extender user functions. These are functions called by *
+;* the client application to request special Dos Extender *
+;* services. *
+;* *
+;* Any INT 2Fh requests that aren't Dos Extender functions *
+;* are handled by switching to real mode and passing control *
+;* on to the previous owner of the real mode INT 2Fh vector. *
+;* This is accomplished by jumping into the interrupt *
+;* reflector entry vector at the location for int 2fh. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 01/09/91 amitc At switch out time Co-Processor being reset*
+;* 11/29/90 amitc Replaced FnSuspend/FnResume by FnObsolete *
+;* These are not needed anymore for 3.1 *
+;* 11/29/90 amitc Modified RMInt2FHandler to respond to the *
+;* BuildChain SWAPI call. *
+;* 11/29/90 amitc Added a SWAPI CallIn function to be called *
+;* by the task switcher. *
+;* 11/16/90 jimmat Added DPMI MS-DOS Extension support *
+;* 08/08/90 earleh Started changes to make DOSX a DPMI server *
+;* 8/29/89 jimmat Added real mode Int 2Fh hook *
+;* 6/23/89 jimmat Added DOSX Info Int 2Fh *
+;* 6/16/89 jimmat Ifdef'd out most DOSX Int 2Fh services *
+;* 6/15/89 jimmat Added suspend/resume Int 2Fh hooks, and *
+;* Win/386 compatible Int 31h check *
+;* 6/14/89 jimmat Removed PTRACE hooks & unused DynaLink code *
+;* 5/19/89 jimmat Reduce # mode switches by ignoring Win/386 *
+;* Int 2Fh/1680h idle calls *
+;* 5/07/89 jimmat Added Int 2Fh protected mode hook to XMS *
+;* driver *
+;* 3/21/89 jimmat Corrected problem with jmping to wrong int *
+;* 2Fh handler if not for the DOS extender *
+;* 3/09/89 jimmat Added FNDynaLink function *
+;* 02/10/89 (GeneA): change Dos Extender from small model to *
+;* medium model *
+;* 01/24/89 (GeneA): removed all real mode dos extender *
+;* function handlers. *
+;* 09/29/88 (GeneA): created *
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;* *
+;****************************************************************
+
+ .286p
+ .287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include dosx.inc
+include woaswapi.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include hostdata.inc
+include intmac.inc
+include dpmi.inc
+include stackchk.inc
+include bop.inc
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+XMS_ID equ 43h ;XMS driver Int 2Fh ID
+XMS_INS_CHK equ 00h ;Installition check function
+XMS_CTRL_FUNC equ 10h ;Get Control Function Addr
+
+WIN386_FUNC equ 16h ;Windows Enhanced mode Int 2Fh ID
+
+WIN386_VER equ 00h ;Windows 386 version
+
+WIN386_INIT equ 05h ;Windows/386 & DOSX startup call
+
+WIN386_IDLE equ 80h ;Windows/386 idle notification
+W386_Get_Device_API equ 84h ;es:di -> device API
+W386_VCD_ID equ 0Eh ;Virtual Comm Device ID
+WIN386_INT31 equ 86h ;Windows/386 Int 31h availability check
+
+DPMI_DETECT equ 87h ;WIN386/DPMI detection call
+
+WIN386_GETLDT equ 88h ;Windows/386 Get LDT Base Selector call
+WIN386_KRNLIDLE equ 89h ;Windows/386 special Kernel idle notification
+
+DPMI_MSDOS_EXT equ 8Ah ;WIN386/DPMI MS-DOS Extensions detection call
+
+
+DPMI_VER equ 005ah ;version 0.90 served here
+DPMI_SUCCESS equ 0000h ;zero to indicate success
+DPMI_FAILURE equ 0001h ;non-zero for failure
+DPMI_FLAGS equ 0001h ;32 bit support
+ ;DPMI client requesting 32-bit support
+
+DPMI_MSDOS_VER equ 0100h ;WIN386/DPMI MS-DOS Extensions version 01.00
+
+DPMI_MSDOS_API_GET_VER equ 0000h ;Get MS-DOS Extension version call
+DPMI_MSDOS_API_GET_LDT equ 0100h ;Get LDT Base selector call
+
+
+DISPCRIT_FUNC equ 40h ;Display driver critical section function
+DISPCRIT_ENTER equ 03h ;Enter critical section
+DISPCRIT_EXIT equ 04h ;Exit critical section
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn AllocateSelector:NEAR
+ extrn AllocateSelectorBlock:NEAR
+ extrn FreeSelector:NEAR
+ extrn FreeSelectorBlock:NEAR
+ extrn ParaToLinear:NEAR
+externFP NSetSegmentDscr
+externNP NSetSegmentAccess
+ extrn GetSegmentAddress:NEAR
+ extrn DupSegmentDscr:NEAR
+ extrn PMIntrEntryVector:NEAR
+ extrn XMScontrol:NEAR
+ extrn PMIntrDos:NEAR
+IFDEF WOW
+ extrn Wow16TransitionToUserMode:near
+ extrn Wow32TransitionToUserMode:near
+ extrn Wow32IntrRefl:near
+ENDIF
+ extrn HookNetBiosHwInt:NEAR
+
+ extrn AllocateExceptionStack:NEAR
+
+DXSTACK segment
+
+ extrn rgw2FStack:WORD
+
+DXSTACK ends
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn segGDT:WORD
+ extrn segIDT:WORD
+ extrn bpGDT:FWORD
+ extrn bpIDT:FWORD
+ extrn selGDT:WORD
+ extrn selPSPChild:WORD
+ extrn segPSPChild:WORD
+ extrn idCpuType:WORD
+ extrn pbReflStack:WORD
+ extrn regUserSS:WORD
+ extrn regUserSP:WORD
+ extrn NoAsyncSwitching:BYTE
+ extrn HCB_List:WORD
+ extrn f286_287:BYTE
+
+ extrn DtaSegment:WORD
+ extrn DtaSelector:WORD
+ extrn DtaOffset:WORD
+
+
+if VCPI
+ extrn fVCPI:BYTE
+endif
+
+IFDEF ROM
+ extrn segDXCode:WORD
+ extrn PrevInt2FHandler:DWORD
+ENDIF
+IFDEF WOW
+ extrn WowTransitionToUserMode:WORD
+ extrn Wow16BitHandlers:WORD
+ extrn selEHStack:WORD
+ENDIF
+IFDEF WOW_x86
+ extrn FastBop:FWORD
+ENDIF
+ extrn bReflStack:WORD
+ extrn HighestDxSel:WORD
+ extrn HighestSel:WORD
+
+; The following variables are used as temporary storage during
+; function entry and exit.
+
+wPMUserAX dw ?
+selPMUserCS dw ?
+offPMUserIP dw ?
+selPMUserDS dw ?
+selPMUserSS dw ?
+offPMUserSP dw ?
+
+
+;
+; Count of DPMI clients active (that have entered protected mode).
+;
+
+ public cDPMIClients
+cDPMIClients dw 0
+
+ public selCurrentHostData, segCurrentHostData, DpmiFlags, DpmiSegAttr
+selCurrentHostData dw 0
+segCurrentHostData dw 0
+DpmiSegAttr dw 0
+DpmiFlags dw 0
+
+; define a switcher API call back info structure
+
+DxSwapiCallBackBlock db SIZE Switch_Call_Back_Info dup (0)
+DxSwapiApiInfo API_Info_Struc <SIZE API_Info_Struc,API_NETBIOS,3,10,API_SL_API>
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+ extrn ChildTerminationHandler:NEAR
+
+IFNDEF ROM
+ extrn segDXCode:WORD
+ extrn segDXData:WORD
+ extrn PrevInt2FHandler:DWORD
+ENDIF
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+ extrn DelayNetPosting:NEAR
+ extrn ResumeNetPosting:NEAR
+ extrn selDgroupPM:WORD
+
+
+;
+; This table dispatches to the function handlers for the Dos
+; Extender functions defined for the protected mode int 2Fh handler.
+
+pfnPmFunc label word
+ dw FnQueryDosExtender ;AL=0
+ dw FnObsolete ;AL=1 (removed for Windows 3.1)
+ dw FnObsolete ;AL=2 (removed for Windows 3.1)
+ dw FnAbort ;AL=3
+
+ EXTRN MakeLowSegment:PROC
+
+szMSDOS db 'MS-DOS',0
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PROTECTED MODE FUNCTION HANDLER
+; -------------------------------------------------------
+;
+; PMInt2FHandler -- This routine will check for Dos Extender
+; function requests on the INT 2Fh vector. Valid Dos
+; Extender function requests are dispatched to the
+; appropriate function to process the request. Other
+; INT 2Fh requests will be reflected down to the real
+; mode INT 2Fh handler.
+;
+; This routine also handles XMS driver installation
+; check and get control function calls.
+;
+; Input: AH - DOS Extender Multiplex ID, OR XMS driver ID
+; AL - function number
+; Output: depends on function requested
+; Errors: depends on function requested
+; Uses: depends on function requested
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMInt2FHandler
+
+PMInt2FHandler:
+
+
+ cld ;practice 'safe programming'
+
+; Check if this is a Dos Extender Function.
+
+ cmp ah,WIN386_FUNC
+ jnz NotWin386
+
+; -------------------------------------------------------
+
+ cmp al,WIN386_KRNLIDLE ;If this is a Windows/386 idle call,
+ jnz @f ; just ignore it. This cuts down on
+Iret2f: iret ; pMode to rMode switching and helps
+@@: ; things like the Windows comm driver.
+ cmp al,WIN386_IDLE
+ jz Iret2f
+
+; -------------------------------------------------------
+
+ cmp al,WIN386_GETLDT ;Win/386 Get LDT Base Selector call?
+ jnz @f
+
+ cmp bx,0BADh ; yes, BX have the secret word?
+ jnz @f ; no, don't do it.
+
+ xor ax,ax ;yes, give it to 'em
+IFNDEF WOW_x86
+ mov bx,SEL_LDT_ALIAS or STD_RING
+ELSE
+ mov bx,SEL_WOW_LDT or STD_RING
+ENDIF
+ iret
+@@:
+; -------------------------------------------------------
+
+ cmp al,WIN386_INT31 ;Windows/386 Int 31h availability check?
+ jnz @f
+
+ xor ax,ax ; yes, indicate Int 31h services are available
+ iret ; by setting AX to 0!
+@@:
+; WOW
+; -------------------------------------------------------
+
+ cmp al,W386_Get_Device_API ;returns es:di -> device API
+ jne @f
+
+IFDEF WOW
+ cmp bx,W386_VCD_ID
+ jne not_W386_VCD_ID
+ mov di, cs
+ mov es, di
+ mov di, offset DXPMCODE:VCD_PM_Svc_Call
+ iret
+
+not_W386_VCD_ID:
+ENDIF
+ xor di,di
+ mov es,di
+ iret
+@@:
+; -------------------------------------------------------
+; WOW
+
+ cmp al,DPMI_MSDOS_EXT ;Detect DPMI MS-DOS Extensions?
+ jnz Iret2f ;some other random Win386 to ignore
+
+ push es
+ push si
+ push di
+ push cx
+
+ push cs ;does DS:SI -> 'MS-DOS',0?
+ pop es
+ mov di,offset DXPMCODE:szMSDOS
+ mov cx,7
+ cld
+ rep cmpsb
+
+ pop cx
+ pop di
+ pop si
+ pop es
+ jnz Chain2F ;Chain int if not MS-DOS
+
+ xor ax,ax ;Indicate services are available
+ push cs ;Return API entry point in ES:DI
+ pop es
+ mov di,offset DXPMCODE:DPMI_MsDos_API
+ iret
+
+; -------------------------------------------------------
+
+NotWin386:
+ cmp ah,DISPCRIT_FUNC ;Display driver critical section?
+ jnz NotDisplay
+
+ cmp al,DISPCRIT_ENTER ;Simply eat the Display driver's
+ jz Iret2f ; enter/exit critical section calls.
+ cmp al,DISPCRIT_EXIT ; The don't apply in Standard mode.
+ jz Iret2f ; Again, to cut down on mode switches.
+
+; -------------------------------------------------------
+
+NotDisplay:
+ cmp ah,DOSXFunc ;DOS Extender Int 2Fh ID?
+ jnz @f ; no, what about XMS?
+
+ cmp al,DOSXLast ;valid DOSX func request?
+ jbe do2F ; yes, go do it
+
+; -------------------------------------------------------
+
+Chain2F:
+notDX: jmp PMIntrEntryVector + 3*2Fh ;reflect request to real mode
+
+@@:
+ cmp ah,XMS_ID ;XMS Driver Int 2Fh ID?
+ jnz notDX ; no, it's not ours
+
+ cmp al,XMS_INS_CHK ;It better be an XMS installation chk
+ jz do2F
+ cmp al,XMS_CTRL_FUNC ; or obtain XMS control func address
+ jnz notDX
+
+; -------------------------------------------------------
+
+do2F:
+
+; Save the caller's entry state and switch to the Dos Extender INT 2F stack.
+
+ push bp ;stack has following: [0] [2] [4] [6]
+ ; BP, IP, CS, FLAGS
+ mov bp,sp
+ push ds ;preserve the caller's DS
+ push SEL_DXDATA or STD_RING
+ pop ds
+ assume ds:DGROUP
+ pop selPMUserDS ;keep caller's DS here until we can get it
+ ; on the stack frame
+ mov wPMUserAX,ax ;preserve caller's AX also
+ mov ax,[bp+2] ;caller's IP
+ mov offPMUserIP,ax
+ mov ax,[bp+4] ;caller's CS
+ mov selPMUserCS,ax
+ mov ax,[bp+6] ;get the caller's flags
+ and ax,0FFFEh ;force carry flag to 0
+
+ mov selPMUserSS,ss ;preserve location of caller's stack
+ mov offPMUserSP,sp
+
+ push SEL_DXDATA or STD_RING
+ pop ss
+ mov sp,offset DGROUP:rgw2FStack
+
+ push offPMUserSP ;save caller's stack location on our own stack
+ push selPMUserSS
+
+ push ax ;caller's flags go on our stack frame
+ push selPMUserCS ;then his CS and IP
+ push offPMUserIP
+
+ mov ax,wPMUserAX
+ pusha ;now all of caller's general registers
+ push selPMUserDS
+ push es
+ push SEL_DXDATA or STD_RING
+ pop es
+ mov bp,sp ;set up stack frame pointer
+
+; Dispatch to the function handler for the requested function. The stack
+; will have the following contents upon entry to the handler.
+
+; SP[1E] Users SP
+; SP[1C] Users SS
+; SP[1A] Users FLAGS (with carry cleared)
+; SP[18] Users CS
+; SP[16] Users IP
+; SP[14] Users AX
+; SP[12] Users CX
+; SP[10] Users DX
+; SP[E] Users BX
+; SP[C] Our SP + 16h
+; SP[A] Users BP
+; SP[8] Users SI
+; SP[6] Users DI
+; SP[4] Users DS
+; SP[2] Users ES
+; SP[0] Return Offset
+
+ FSTI
+
+ cmp ah,XMS_ID ;XMS Int 2Fh request?
+ jnz @f
+ call XMSfunc ; yes, use this handler
+ jmp short i2F_ret
+@@:
+ xor ah,ah
+ add ax,ax
+ mov bx,ax
+
+ call pfnPmFunc[bx] ; no, use this one instead
+
+i2F_ret:
+
+; Return from Dos Extender function back to caller.
+
+ FCLI
+ pop es
+ pop selPMUserDS
+ popa
+ mov wPMUserAX,ax
+ pop offPMUserIP
+ pop selPMUserCS
+ pop ax ;get back new flags
+ pop selPMUserSS
+ pop offPMUserSP
+ mov ss,selPMUserSS
+ mov sp,offPMUserSP
+ mov [bp+6],ax ;store flags back into user iret frame
+ mov ax,selPMUserCS ;user's CS
+ mov [bp+4],ax
+ mov ax,offPMUserIP ;user's IP
+ mov [bp+2],ax
+ mov ax,wPMUserAX
+ mov ds,selPMUserDS
+ pop bp
+ riret
+
+
+; WOW
+; -------------------------------------------------------
+;
+; Simulate VCD API's. DX contains function number.
+;
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public VCD_PM_Svc_Call
+
+VCD_PM_Svc_Call:
+ DPMIBOP VcdPmSvcCall32
+ retf
+
+; -------------------------------------------------------
+; WOW
+
+; -------------------------------------------------------
+;
+; -------------------------------------------------------
+ subttl Dos Extender Function Routines
+ page
+; -------------------------------------------------------
+; DOS EXTENDER FUNCTION ROUTINES
+; -------------------------------------------------------
+;
+; The following functions are the entry points for handling
+; the Dos Extender functions. The general convention for
+; these functions is that BP points to a stack frame that
+; has all of the original caller's registers. Any parameters
+; needed and values to be returned to the user are taken from
+; or placed in this stack frame. The layout of the stack frame
+; is defined by the structure FUNCSTACK.
+;
+; -------------------------------------------------------
+; FnQueryDosExtender -- This function identifies if the
+; Dos Extender is resident in the machine.
+;
+; Input:
+; Output: user AX - dos extender version number
+; user BX - 'DX'
+; Errors: none
+; Uses: all registers used
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public FnQueryDosExtender
+
+FnQueryDosExtender:
+ mov [bp].fnsUserAX,DXVERSION
+ mov [bp].fnsUserBX,'DX'
+;
+fnqx90: ret
+
+; -------------------------------------------------------
+; FnObsolete -- This function is invoked when the now obsolete
+; DOSX INT 2FH function is called with AL=1 or 2.
+; These were valid functions for Windows 3.0.
+;
+; Input:
+; Output:
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+
+FnObsolete proc near
+
+ ret
+
+FnObsolete endp
+; -------------------------------------------------------
+; FnAbort -- This function is invoked when the application
+; asks to be aborted. This is an emergency exit that
+; should only be used in the most dire circumstance.
+;
+; Input:
+; Output:
+; Errors: none
+; Uses: all registers used
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public FnAbort
+
+FnAbort proc near
+
+; Tell DOS that we are the child app, and then do a DOS exit. This
+; should result in DOS going to ChildTerminationHandler, where we
+; do final clean-up and exit ourselves.
+
+ mov ah,50h ;tell dos that the child is running
+ mov bx,selPSPChild
+
+ rpushf ;Don't do an Int 21h in case the
+ FCLI ; child has hooked it for some reason
+ push cs
+ call PMIntrDOS
+
+ mov ax,4C02h ;now exit the child with abort status
+
+ rpushf
+ FCLI
+ push cs
+ call PMIntrDOS
+
+FnAbort endp
+
+; -------------------------------------------------------
+; XMSfunc - The following routine provides a protected mode
+; service for XMS Int 2Fh services. Two services are
+; implemented: XMS driver installation check, and obtain
+; XMS driver control function address.
+;
+; Input: UserAL - function request
+; Output: UserAL - XMS driver installed flag, or
+; UserES:BX - XMS driver control function address
+; Errors: none
+; Uses: all registers may be used
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public XMSfunc
+
+XMSfunc proc near
+
+ cmp al,XMS_INS_CHK ;XMS driver installation check?
+ jnz @f
+
+ mov byte ptr [bp].fnsUserAX,80h ;indicate driver is installed
+ ret
+
+@@:
+
+; It must be an obtain XMS driver control function address request
+
+ mov [bp].fnsUserBX,offset DXPMCODE:XMScontrol
+ mov [bp].fnsUserES,cs
+
+ ret
+
+XMSfunc endp
+
+; -------------------------------------------------------
+ subttl DPMI MS-DOS Extension API
+ page
+; -------------------------------------------------------
+; DPMI MS-DOS EXTENSION API
+; -------------------------------------------------------
+;
+; The following routine implements the DPMI MS-DOS Extensions
+; API. This API must be 'detected' by the use of the
+; DMPI_MSDOS_EXT Int 2F function (above).
+
+DPMI_MsDos_API proc far
+
+ cmp ax, DPMI_MSDOS_API_GET_VER ;Get version call?
+ jne DPMI_MsDos_API_Not_Ver
+
+ mov ax,DPMI_MSDOS_VER
+ jmp short DPMI_MsDos_API_Exit
+
+DPMI_MsDos_API_Not_Ver:
+
+ cmp ax, DPMI_MSDOS_API_GET_LDT ;Get LDT Base call?
+ jne DPMI_MsDos_Api_Failed
+ifdef WOW_x86
+ mov ax,SEL_WOW_LDT or STD_RING ; yup, give it to 'em
+else
+ mov ax,SEL_LDT_ALIAS or STD_RING ; yup, give it to 'em
+endif
+
+DPMI_MsDos_API_Exit:
+
+ clc ;Succss
+ ret
+
+DPMI_MsDos_API_Failed:
+
+ stc ;Unsupported function
+ ret
+
+DPMI_MsDos_API endp
+
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+; -------------------------------------------------------
+;
+; -------------------------------------------------------
+;
+; DPMI_Client_Pmode_Entry -- This routine is the entry
+; point for a DPMI client to switch to protected mode.
+; Reference: DOS Protected Mode Interface Specification 0.9
+; Section 5.2: Calling the Real to Protected
+; Mode Switch Entry Point
+;
+; Entry: AX = Flags
+; Bit 0 = 1 if program is a 32-bit application
+; ES = Real mode segment of DPMI host data area. This is the
+; size of the data area we specified in RMInt2FHandler,
+; below.
+;
+; Returns:
+; Success: Carry clear.
+; Program is in protected mode.
+; CS = 16-bit selector with base of real mode
+; CS and a 64k limit.
+; SS = 16-bit selector with base of real mode
+; SS and a 64k limit.
+; DS = 16-bit selector with base of real mode
+; DS and a 64k limit.
+; ES = Selector to program's PSP with a 100h
+; byte limit.
+; 80386, 80486:
+; FS, GS = 0
+; All other registers preserved.
+;
+; Failure: Carry flag set.
+; Program is in real mode.
+;
+; Exceptions:
+; 32-bit programs not (yet) supported. Any attempt to load
+; a 32-bit program by this mechanism returns failure.
+;
+; The only error that can occur here is a failure to allocate
+; sufficient selectors.
+;
+
+;
+; Structure of the stack frame used to store the client's registers
+; while implementing the DPMI protected mode entry.
+;
+
+DPMI_Client_Frame STRUC
+ Client_ES dw ? ; Client's ES
+ Client_DS dw ? ; Client's DS
+ Client_DI dw ? ; Client's DI
+ Client_SI dw ? ; Client's SI
+ Client_Pusha_BP dw ? ; BP at pusha
+ Client_Pusha_SP dw ? ; SP at pusha
+ Client_BX dw ? ; Client's BX
+ Client_DX dw ? ; Client's DX
+ Client_CX dw ? ; Client's CX
+ Client_AX dw ? ; Client's AX
+ Client_Flags dw ? ; Client's flags
+ Client_IP dw ? ; Client's IP, lsw of return
+ Client_CS dw ? ; Client's CS, msw of return
+DPMI_Client_Frame ENDS
+
+ public DPMI_Client_Pmode_Entry
+DPMI_Client_Pmode_Entry proc far
+;
+; Reject any 32-bit program requests.
+;
+
+IFNDEF WOW
+ test ax, DPMI_32BIT ; 32-bit application?
+ stc ; yep, refuse to do it
+ jz dcpe_flags_ok ; no, try to get into Pmode
+ jmp dcpe_x
+dcpe_flags_ok:
+ENDIF
+
+IFDEF WOW
+ stc
+ENDIF
+ pushf ;save client's flags (with carry set)
+ pusha ;save caller's general registers
+ push ds ;save caller's DS
+ push es ;save caller's ES
+ mov bp, sp ;create the stack frame
+ dossvc 62h ;now get caller's PSP address
+ mov di, bx ;and store it here for a while
+IFDEF FLATAPIXLAT
+ push es
+ dossvc 2fh ;get caller's dta address
+ENDIF
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ax, segDXData ;get our DGROUP
+ mov ds, ax ;point to it
+ENDIF
+
+IFDEF FLATAPIXLAT
+ mov DtaSegment,es
+ mov DtaOffset,bx
+ pop es
+ENDIF
+
+;
+; For now, we only support one DPMI client application at a time.
+;
+;; cmp cDPMIClients,0 ;Any active?
+;; je @F
+;; jmp dcpe_return
+@@:
+
+ ;
+ ; Remember the highest selector if we haven't yet
+ ;
+ cmp HighestDxSel,0
+ jne @f
+
+ mov ax,HighestSel
+ mov HighestDxSel,ax
+
+@@: mov ax,[segCurrentHostData]
+ mov es:[HdSegParent],ax ; save rm link
+ mov ax,es
+ mov [segCurrentHostData],ax
+ mov ax,[bp].Client_AX ; get dpmi flags
+ mov es:[HdFlags],ax ; save for future reference
+ mov DpmiFlags,ax
+ test ax,DPMI_32BIT
+ jne cpe10
+
+ mov DpmiSegAttr,0
+ jmp cpe20
+
+cpe10: mov DpmiSegAttr,AB_BIG
+
+cpe20: mov si, ss ;SI = caller SS
+
+ mov ax, ds ;use a DOSX stack during the mode
+ ;switch
+ FCLI
+ mov ss, ax
+ mov sp, offset DGROUP:rgw2FStack
+
+ SwitchToProtectedMode
+
+ mov ax, si ;make a selector for client's stack
+ mov bx, STD_DATA
+ or bx, DpmiSegAttr
+ call MakeLowSegment
+ jnc got_client_stack_selector
+ jmp dcpe_error_exit ;back out if error
+got_client_stack_selector:
+
+ or al,STD_TBL_RING
+
+ mov ss, ax ;back to client's stack
+.386p
+ movzx esp,bp
+.286p
+
+; push [bp.Client_Flags] ;enable interupts if client had
+; npopf ;them enabled
+
+;
+; After DOSX enters protected mode, convert the caller's segment registers
+; to PMODE selectors, replacing the values in the client's register image
+; on the stack. First, allocate the three or four selectors we will need.
+;
+ xor ax,ax ;an invalid selector
+ push ax ;marker
+
+ mov cx,4 ;CS, PSP, Environ, Host data
+ cmp si,[bp.Client_DS] ;Client SS == Client DS ?
+ je dcpe_allocate_loop
+ inc cx
+dcpe_allocate_loop:
+ call AllocateSelector
+ jnc @F
+ jmp dcpe_pfail
+@@:
+ or al,STD_TBL_RING
+ push ax
+ loop dcpe_allocate_loop
+
+ mov dx,[bp.Client_CS] ;get client CS paragraph
+ call ParaToLinear ;convert to linear address in BX:DX
+ mov cx,0ffffh ;limit = 64k
+ pop ax ;get one of the selectors allocated
+ mov [bp.Client_CS],ax ;save value for client
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_CODE>
+
+
+ mov dx, [bp.Client_DS]
+ mov [bp.Client_DS],ss ;DS = SS for now
+ cmp dx, si ;need separate DS selector?
+ je dcpe_do_child_PSP
+
+ call ParaToLinear ;convert to linear address in BL:DX
+ mov cx,0ffffh ;limit = 64k
+ pop ax ;get another selector
+ mov [bp.Client_DS],ax ;save value for client
+ push di
+ mov di,STD_DATA
+ or di,DpmiSegAttr
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,di>
+ pop di
+
+dcpe_do_child_PSP:
+ mov dx,[bp.Client_ES] ; get HostData selector
+ call ParaToLinear
+ mov cx,HOST_DATA_SIZE ; limit = size of HostData
+ pop ax ; get another selector
+ push [selCurrentHostData]
+ mov [selCurrentHostData],ax ; save for us
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+ mov es,ax
+ pop ax
+ mov es:[HdSelParent],ax
+ mov ax,SelPSPChild
+ mov es:[HdPSPParent],ax
+
+ mov dx, di ;get client PSP paragraph
+ mov segPSPChild, di
+ call ParaToLinear ;convert to linear address in BL:DX
+ mov cx,100h ;limit = 100h
+ pop ax ;get another selector
+ mov [bp.Client_ES],ax ;save value for client
+ mov selPSPChild, ax ;save a copy for DOSX
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ mov es:[HdSelPSP],ax
+ mov es,ax ;point to client's PSP
+ mov dx,es:segEnviron ;fetch client's environment pointer
+ call ParaToLinear ;convert to linear address in BL:DX
+ mov cx,0ffffh ;limit = 32k
+ pop ax ;get another selector
+ mov es:segEnviron,ax ;save client's environment selector
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+IFDEF FLATAPIXLAT
+; We need to set up the DTA selector
+ mov dx,DtaSegment
+ cmp dx,segPSPChild
+ jne dcpe_50
+
+ mov dx,selPSPChild
+ mov DtaSelector,dx
+ jmp dcpe_60
+
+dcpe_50:
+ mov cx,1
+ call AllocateSelector
+ jnc @f
+
+ jmp dcpe_free_client_stack
+
+@@: or al,STD_TBL_RING
+ mov DtaSelector,ax
+ call ParaToLinear
+ cCall NSetSegmentDscr,<ax,bx,dx,0,0ffffh,STD_DATA>
+
+dcpe_60:
+ENDIF
+
+ inc cDPMIClients ;increment count of Pmode clients
+ cmp cDPMIClients, 1 ; first client?
+ jne @f ; already taken care of
+
+ call AllocateExceptionStack
+
+ call DpmiStackSizeInit
+ call DpmiSizeInit
+ FBOP BOP_DPMI,DpmiInUse,FastBop
+;
+@@:
+;
+; Everything OK. Clear error flag, and return to caller.
+;
+ not byte ptr [bp.Client_Flags]
+ ;reverse status flags, clearing carry
+
+
+; Let 32 bit code know if this is a 32 or 16 bit application
+ mov ax,DpmiFlags
+ push selPSPChild
+ push DtaSelector
+ push DtaOffset
+ FBOP BOP_DPMI,InitApp,FastBop
+ add sp,4
+
+ cmp cDPMIClients, 1 ; first client?
+ jne @f ; already taken care of
+ ; Note: We have to do InitApp before we try to hook the netbios
+ ; interrupt. If we don't, we will fault in the dos extender.
+ ; (HookNetBiosHwInt calls int 21, enabling interrupts)
+ call HookNetBiosHwInt
+@@:
+
+; jmp far ptr dcpe_return ;avoid need for fix ups
+ db 0EAh
+ dw offset DXCODE:dcpe_return
+ dw SEL_DXCODE OR STD_RING
+;
+; If we get here, it means DOSX failed to allocate enough selectors for the
+; client. Deallocate those which have been allocated, switch back to
+; real mode, and return an error to the caller. Selectors to deallocate
+; are on the stack, pushed after a zero word. Then switch to a DOSX stack,
+; deallocate the client stack selector, and switch to real mode.
+;
+dcpe_pfail:
+ pop ax ;any selectors allocated?
+ or ax,ax ; (we pushed a zero before allocating)
+ jz dcpe_free_client_stack ;done
+ call FreeSelector ;free the selector
+ jnc dcpe_pfail ;free any more
+dcpe_free_client_stack:
+ mov di, ss ;make copy of client stack selector
+ mov ax, ds ;have to be on a DOSX stack to do this
+ FCLI
+ mov ss, ax
+ mov sp, offset DGROUP:rgw2FStack
+ mov ax, di ;free client stack selector
+ call FreeSelector
+dcpe_error_exit:
+;
+; Error exit from protected mode. Any allocated selectors have already
+; been freed. Switch to real mode, restore client stack, pop off client's
+; registers, return with the carry flag set.
+;
+ SwitchToRealMode
+ mov ss, si ;restore client stack
+
+ errnz <dcpe_return-$>
+
+dcpe_return: ; The next line must restore the stack.
+
+ mov sp, bp
+
+ jc dcpe_return_1 ; error return
+;
+; Pop the client's registers off the stack frame, switch back to the
+; client's stack, and return.
+;
+dcpe_return_1:
+ pop es ;pop copy of PSP selector/segment
+ pop ds ;pop client DS selector/segment
+ popa ;pop client's general registers
+ npopf ;restore interrupt flag, return status
+dcpe_x:
+ retf ;and out of here
+DPMI_Client_Pmode_Entry endp
+
+; -------------------------------------------------------
+; REAL MODE FUNCTION HANDLER
+; -------------------------------------------------------
+;
+; RMInt2FHandler -- This routine hooks the real mode Int 2Fh chain
+; and watches for 'interesting' Int 2Fh calls.
+;
+; WIN386/DOSX startup broadcast
+; DPMI server detection
+; Switcher API functions
+;
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public RMInt2FHandler
+
+RMInt2FHandler proc near
+
+
+IFDEF ROM
+ push ds
+ SetRMDataSeg
+ assume ds:DGROUP
+ENDIF
+ cmp ah,WIN386_FUNC ;WIN386/DOSX/DPMI call?
+ jz rm2f_0
+ cmp ax,SWAPI_BUILD_CHAIN ;build chain call ?
+ jz RM2F_SwAPI ;yes.
+
+rm2f_chain:
+
+IFDEF ROM
+
+; Chain the interrupt without a code segment variable
+
+ push bp
+ mov bp,sp ; bp -> [bp] [ds] [ip] [cs] [fl]
+
+ push word ptr PrevInt2FHandler[2]
+ push word ptr PrevInt2FHandler
+
+ mov ds,[bp+2] ; restore entry DS
+ mov bp,[bp] ; .. BP ..
+ retf 4 ; .. chain & clean up stack
+ELSE
+ jmp [PrevInt2FHandler] ;no, just chain it on...
+ENDIF
+
+RM2F_SwAPI:
+
+; call down stream first.
+
+IFDEF ROM
+
+ pop ds ;restore the saved ds
+ assume ds:NOTHING
+
+; prepare an iret frame on the stack.
+
+ pushf
+ FCLI ;prepare call-iret frame
+ push cs ;the chaining code pops this
+ push offset RM2F_SWAPI_BackFromChaining
+ push ds ;save ds again.
+ SetRMDataSeg
+ jmp short rm2f_chain
+
+RM2F_SWAPI_BackFromChaining:
+
+ELSE
+
+ pushf
+ FCLI
+ call [PrevInt2FHandler]
+
+ENDIF
+
+ push ax
+ push dx ;save
+ mov ax,es
+ mov dx,bx ;ax:bx has current ES:BX
+
+; get to our data segment
+
+ push ds ;save
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,[segDXData] ;load our data segment
+ENDIF
+
+ assume ds:DGROUP
+
+; fill up the Switcher Info Call Back Block
+
+ push ds
+ pop es
+ lea bx, DxSwapiCallBackBlock;ES:BX points to our node
+
+; save the address of the next node.
+
+ mov word ptr es:[bx.SCBI_Next],dx
+ mov word ptr es:[bx.SCBI_Next][2],ax
+
+; save the far address of our SWAPI call in function.
+
+ lea ax,DxSwapiCallIn ;address of call in function
+ mov word ptr es:[bx.SCBI_Entry_Pt],ax
+ mov word ptr es:[bx.SCBI_Entry_Pt][2],cs
+ lea ax,DxSwapiApiInfo ;address of call in function
+ mov word ptr es:[bx.SCBI_Api_Ptr],ax
+ mov word ptr es:[bx.SCBI_Api_Ptr][2],ds
+
+RM2F_SWAPI_Ret:
+
+ pop ds ;restore
+ assume ds:NOTHING
+
+ pop dx
+ pop ax ;restore
+ iret
+
+rm2f_0:
+
+IFDEF ROM
+ assume ds:DGROUP
+ENDIF
+if 0 ; don't claim to be win 3.1 in enhanced mode
+ cmp al,WIN386_INIT ;WIN386/DOSX startup attempt?
+ jnz rm2f_1 ;no
+ mov cx,-1 ;yes, don't let'm load
+ jmp rm2f_x
+endif
+ cmp al,W386_Get_Device_API ;not supported
+ jne rm2f_1
+
+ xor di,di
+ mov es,di
+ jmp rm2f_x
+
+rm2f_1:
+ cmp al,DPMI_DETECT ;DPMI detection?
+ jnz rm2f_2 ;no
+
+ mov ax,DPMI_SUCCESS ;yes, return Pmode switch entry
+ mov bx,DPMI_FLAGS ;flags
+
+IFDEF ROM
+ mov cl,byte ptr [idCpuType] ;CPU type
+ELSE
+ push segDXData
+ pop es
+ assume es:DXDATA
+ mov cl,byte ptr es:[idCpuType] ;CPU type
+ assume es:nothing
+ENDIF
+ mov dx,DPMI_VER ;DPMI server version
+ mov si,(HOST_DATA_SIZE + 15) / 16
+ push cs ;entry point is in this segment
+ pop es ;prospective client wants
+ lea di,DPMI_Client_Pmode_Entry ;switch entry point in ES:DI
+ jmp rm2f_x ;done
+rm2f_2:
+if 0 ; don't claim to be windows
+ cmp al,WIN386_VER ;Windows 386 version check?
+ jnz rm2f_chain ;no, chain the interrupt
+
+ mov ax, 0a03h
+else
+ jmp rm2f_chain
+endif
+rm2f_x:
+IFDEF ROM
+ pop ds
+ assume ds:NOTHING
+ENDIF
+ iret
+
+RMInt2FHandler endp
+
+;--------------------------------------------------------------------------------
+; DxSwapiCallIn
+;
+; DESCRIPTION This routine handles all the Call Outs that the Switcher may
+; make. We are only interested in the SWAPI_SUSPEND and
+; SWAPI_SESSION_ACTIVE calls. Between these two calls, the main
+; DOSX code will not be active and we have to make sure that
+; the Global NetBios stub code does not call the main POST
+; routine in the DOSX.
+;
+; ENTRY:
+; AX = SWAPI CallOut function code.
+;
+; EXIT:
+; AX = 0 (OK to Switch or resume)
+; CY = Clear
+;
+; USES:
+; AX, Flags
+;------------------------------------------------------------------------------
+DxSwapiCallIn proc far
+
+ assume ds:NOTHING, es:NOTHING, ss:NOTHING
+
+; check to see if we are interested in the function or not.
+
+ cmp ax,SWAPI_SUSPEND ;suspend call ?
+ jz DXSCI_Interested ;yes, we are interested in it.
+ cmp ax,SWAPI_SESSION_ACTIVE ;session active call ?
+ jnz DXSCI_Ret ;ignore call with success
+
+DXSCI_Interested:
+
+; we need access to our data segment.
+
+ push ds ;save
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,[segDXData] ;load our data segment
+ENDIF
+
+ assume ds:DGROUP ;we have our data segment
+
+; now switch to protected mode
+
+ mov regUserSP,sp
+ mov regUSerSS,ss
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,segDXData
+ENDIF
+ mov sp,pbReflStack
+
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+
+ push regUserSS ;save current stack loc on our stack
+ push regUserSP ; so we can restore it later
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ push ax ;save function code
+ SwitchToProtectedMode ;destroys ax
+
+; --------------- START OF PROTECTED MODE CODE -------------------------
+
+ pop ax ;restore function code
+
+; check the function type
+
+ cmp ax,SWAPI_SUSPEND ;is it a suspend call ?
+ jz DXSCI_Suspend ;yes it is
+
+; it must be a resume session call. If [NoAsyncSwitching] is set, we would
+; have switched out only because there were no asynchronous requests pending
+; in this case we do not have to do the ResumeNetPosting call.
+
+ cmp [NoAsyncSwitching],0 ;is this the case discussed above ?
+ jnz DXSCI_SessionActiveOk ;yes.
+
+ call ResumeNetPosting ;call the code to allow normal posting
+
+DXSCI_SessionActiveOk:
+
+ xor ax,ax ;successful
+ jmp short DXSCI_BackToRM ;go back to real mode and return
+
+DXSCI_Suspend:
+
+; check to see whether we can switch out when asynchronous calls are pending
+; or not.
+
+ cmp [NoAsyncSwitching],0 ;can we switch out with async calls pending ?
+ jz DXSCI_Suspend_OK ;yes, global stub will take care
+
+; do we have any outstanding asynchronous requests ?
+
+ xor ax,ax ;assume no requests outstanding
+ cmp [HCB_List],0 ;requests outstanding ?
+ jz DXSCI_BackToRM ;no, ok to switch.
+
+; asynchronous requests are outstanding, we should allow a switch now.
+
+ mov ax,1 ;suspend should fail
+ jmp short DXSCI_BackToRM ;go back.
+
+DXSCI_Suspend_OK:
+
+; suspend normal net posting.
+
+ call DelayNetPosting ;Net posting delayed
+
+; if this is a 286 system with a 287 Co-Processor, we should reset the
+; Co-Processor to get it out of protected mode.
+; Note: The Switcher is going to save the Co-Processor state after this,
+; however, according to C conventions, the Co-Processor state may not be
+; maintained accross function call and the Switcher switches Windows out
+; from within a message body in Winoldap. Thus, it is OK to alter the state
+; by resting the Co-Processor.
+
+ xor ax,ax ;successfull.
+ cmp [f286_287],0 ;is this a 80286 & 80287 combination?
+ jz DXSCI_BackToRM ;no.
+
+; we have a 286 and 287 configuration. Reset the Co-Processor to get it into
+; real mode.
+
+ out 0F1H,al ;reset the Co Processor
+ or al,al ;set back zero flag
+
+DXSCI_BackToRM:
+
+ pop regUserSP ;recover previous stack location
+ pop regUserSS
+
+ push ax ;save return code
+ SwitchToRealMode ;Switch back to real mode.
+
+; --------------- START OF REAL MODE CODE ------------------------------
+
+ pop ax ;restore return code
+
+; Switch back to the original stack, deallocate the interrupt stack frame,
+; and return to the network software
+
+ CHECK_STACK
+ mov ss,regUserSS
+ mov sp,regUserSP
+ add pbReflStack,CB_STKFRAME
+
+ pop ds ;restore
+ assume ds:NOTHING
+
+ jmp short DXSCI_End ;ax has return code
+
+DXSCI_Ret:
+
+ xor ax,ax ;indicate success
+
+DXSCI_End:
+
+ ret
+
+DxSwapiCallIn endp
+;--------------------------------------------------------------------------------
+
+DXCODE ends
+
+IFDEF WOW
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+;----------------------------------------------------------------------
+;
+; DpmiSizeInit -- This routine insures that the appropriately sized
+; interrupt handlers will be called
+;
+; Inputs: None
+; Outputs: None
+;
+ public DpmiSizeInit
+ assume ds:dgroup,es:nothing,ss:nothing
+DpmiSizeInit proc
+
+ push ax
+ push bx
+ push cx
+ push si
+ push di
+ push es
+ rpushf
+ FCLI
+ call AllocateSelector
+ mov bx,ax
+ mov ax,cs
+ call DupSegmentDscr
+ cCall NSetSegmentAccess,<bx,STD_DATA>
+ mov es,bx
+ assume es:DXPMCODE
+ test DpmiFlags,DPMI_32BIT
+ jnz dsi20
+
+ mov [WowTransitionToUserMode],offset DXPMCODE:Wow16TransitionToUserMode
+ assume es:nothing
+ xor ax,ax
+ mov es,ax
+ mov ax,bx
+ call FreeSelector
+
+ cCall NSetSegmentAccess,<selDgroupPM,STD_DATA>
+ cCall NSetSegmentAccess,<selEHStack,STD_DATA>
+
+ jmp dsi90
+dsi20:
+ assume es:DXPMCODE
+ mov [WowTransitionToUserMode],offset DXPMCODE:Wow32TransitionToUserMode
+ assume es:nothing
+ xor ax,ax
+ mov es,ax
+ mov ax,bx
+ call FreeSelector
+;
+; Copy 16 bit handler addresses
+;
+.386p
+ lea di,Wow16BitHandlers
+
+ mov ax,ds
+ mov es,ax
+ assume es:DGROUP
+
+ push ds
+ mov ax,SEL_IDT OR STD_RING
+ mov ds,ax
+ assume ds:nothing
+
+ mov si,0
+ mov cx,256
+dsi40: movsd
+ add si,4
+ loop dsi40
+ pop ds
+
+;
+; Put 32 bit handlers into IDT
+;
+
+ push ds
+ mov ax,SEL_IDT OR STD_RING
+ mov ds,ax
+
+ mov bx,0
+ mov si,bx
+ mov di,offset DXPMCODE:Wow32IntrRefl
+ mov cx,8
+dsi50: mov [si],di
+ mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_TRAP_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi50
+
+ mov cx,8
+dsi55: mov [si],di
+ mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_INT_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi55
+
+ mov cx,70h - 10h
+dsi60: mov [si],di
+ mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_TRAP_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi60
+
+ mov cx,8
+dsi70: mov [si],di
+ mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_INT_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi70
+
+ mov cx,0ffh - 78h
+dsi80: mov [si],di
+ mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_TRAP_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi80
+
+ pop ds
+ assume ds:DGROUP
+
+dsi90: rpopf
+ pop es
+ pop di
+ pop si
+ pop cx
+ pop bx
+ pop ax
+ ret
+DpmiSizeInit endp
+
+ assume ds:DGROUP, es:NOTHING, ss:NOTHING
+DpmiStackSizeInit proc
+
+ push ax
+ test DpmiFlags,DPMI_32BIT
+ jz @f
+;
+; Make the dgroup selector 32 bit
+;
+; NOTE: The following equ is only necessary to get the cmacro package
+; to pass the correct value to NSetSegmentAccess
+
+NEW_DX_DATA equ STD_DATA OR AB_BIG
+ cCall NSetSegmentAccess,<selDgroupPM,NEW_DX_DATA>
+ cCall NSetSegmentAccess,<selEHStack,NEW_DX_DATA>
+.286p
+
+@@:
+ pop ax
+ ret
+
+DpmiStackSizeInit endp
+
+DXPMCODE ends
+ENDIF
+;
+;****************************************************************
+
+ end
diff --git a/private/mvdm/dpmi/486/dxint31.asm b/private/mvdm/dpmi/486/dxint31.asm
new file mode 100644
index 000000000..e53083233
--- /dev/null
+++ b/private/mvdm/dpmi/486/dxint31.asm
@@ -0,0 +1,2966 @@
+ PAGE ,132
+ TITLE DXINT31.ASM -- Dos Extender Int 31h Handler
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXINT31.ASM -- DOS Extender Int 31h Handler
+;
+;-----------------------------------------------------------------------
+;
+; This module provides the Int 31h services to the protected mode
+; application running under the DOS Extender.
+;
+;-----------------------------------------------------------------------
+;
+; 12/03/90 amitc 'i31_GetSetRMInt' will map vectors in the range 50-57h
+; to the range 8-fh if 'Win30CommDriver' switch is set in
+; system.ini
+; 12/18/89 jimmat Service 0003 changed from Get LDT Base to Get Sel Incr,
+; and added Virtual Interrupt State services.
+; 09/18/89 jimmat Added Allocate/Free Real Mode Call-Back services
+; 08/20/89 jimmat Changed A20 diddling to use XMS local enable/disable
+; 06/14/89 jimmat Added a few missing and new services.
+; 05/17/89 jimmat Added protected to real mode call/int services.
+; 05/12/89 jimmat Original version (split out from DXINTR.ASM)
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include interupt.inc
+include int31.inc
+include dpmi.inc
+
+ Int_Get_PMode_Vec EQU 04h
+ Int_Set_PMode_Vec EQU 05h
+
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+IFDEF XMEMNT
+include dpmi.inc
+ENDIF
+include stackchk.inc
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+RealMode_SaveBP equ word ptr RealMode_EBP+4
+RealMode_SaveSP equ word ptr RealMode_EBP+6
+
+cRM_CALL_BACK equ 16 ;Count of Real Mode Call-Backs supported
+ ;DPMI 0.90 spec says 16 required.
+CallBackTableStruc struc ;Structure of Real Mode Call-Back Table
+fInUse db 0 ;use/free flag
+PM_CS_IP dd ? ;pMode EIP to call
+ dw ? ;pMode CS to call
+PM_Client_Frame dd ? ;Client Register Frame
+ dw ? ; segment of above
+CallBackTableStruc ends
+
+SelectorIncrement equ 8 ;DOSX increments consecutive selectors by 8
+
+Trans_Reset_HW equ 01h ;Reset PIC/A20 line on PM->Call services
+
+I31VERSION equ 0090d ;Int 31 services major/minor version #'s
+ ; version 00.90 (not quite ready for DPMI)
+I31FLAGS equ 000Dh ; 386 extender, pMode NetBIOS
+I31MasterPIC equ 08h ;Master PIC Interrupts start at 08h
+I31SlavePIC equ 70h ;Slave PIC Interrupts start at 70h
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn EnterIntHandler:NEAR
+ extrn LeaveIntHandler:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn GetSegmentAddress:NEAR
+ extrn SetSegmentAddress:NEAR
+ extrn FreeSelector:NEAR
+ extrn FreeSelectorBlock:NEAR
+ extrn AllocateSelector:NEAR
+ extrn AllocateSelectorBlock:NEAR
+ extrn ParaToLDTSelector:NEAR
+ extrn DupSegmentDscr:NEAR
+ extrn GetFaultVector:NEAR
+ extrn PutFaultVector:NEAR
+ extrn AllocateXmemBlock:NEAR
+ extrn FreeXmemBlock:NEAR
+ extrn ModifyXmemBlock:NEAR
+ extrn FreeLowBlock:NEAR
+ extrn AllocateLowBlock:NEAR
+ extrn AllocateLDTSelector:NEAR
+ extrn ParaToLinear:NEAR
+ extrn GetIntrVector:NEAR, PutIntrVector:NEAR
+ extrn NSetSegmentLimit:near
+ extrn NMoveDescriptor:near
+ extrn NWOWSetDescriptor:near
+ extrn RMIntrEntryVector:near
+ extrn EndRMIntrEntry:near
+ extrn DFSetIntrVector:near
+ extrn RmSaveRestoreState:far
+ extrn PmSaveRestoreState:far
+ extrn RmRawModeSwitch:far
+ extrn PmRawModeSwitch:far
+ extrn IsSelectorFree:near
+ extrn gtpara:near
+
+externNP NSetSegmentAccess
+externFP NSetSegmentDscr
+externNP FreeSpace
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn selGDT:WORD
+ extrn segPSP:WORD
+ extrn idCpuType:WORD
+ extrn npXfrBuf1:WORD
+ extrn rgbXfrBuf1:BYTE
+ extrn pbReflStack:WORD
+ extrn bReflStack:WORD
+ extrn rglpfnRmISR:DWORD
+ extrn lpfnXMSFunc:DWORD
+ extrn A20EnableCount:WORD
+ extrn regUserAX:WORD, regUserFL:WORD, regUserSS:WORD
+ extrn regUserSP:WORD, regUserDS:WORD, regUserES:WORD
+ extrn fWin30CommDrv:WORD
+ extrn PMInt24Handler:DWORD
+ extrn DpmiFlags:WORD
+IFDEF WOW_x86
+ extrn FastBop:fword
+ENDIF
+
+ extrn selPspChild:WORD
+ extrn RmHwIsr:DWORD
+ extrn LowMemAllocFn:DWORD
+ extrn LowMemFreeFn:DWORD
+
+ public i31HWReset
+
+i31HWReset db 0 ;NZ if in 'standard' real mode state
+
+i31_dsp_rtn dw 0 ;Int 31h service routine to dispatch
+
+ifdef DEBUG
+debugsavess dw 0 ; Usefull when debugging WOW KERNEL
+debugsavesp dw 0 ;
+debugsavebp dw 0 ;
+debugsavecx dw 0 ;
+endif
+
+ public i31_selectorbitmap
+i31_selectorbitmap dw 0000000000000000b
+ ;Reserved LDT Selectors
+
+CallBackTable label byte ;Real Mode Call-Back Table
+ rept cRM_CALL_BACK
+ CallBackTableStruc <>
+ endm
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+IFNDEF ROM
+ extrn segDXData:WORD
+ extrn selDgroup:WORD
+ENDIF
+
+ extrn segDXCode:word
+
+RMCallBackEntryVector label near ;Real Mode Call-Back Entry Points
+ rept cRM_CALL_BACK
+ call RMCallBackHook
+ endm
+
+ assume cs:NOTHING
+
+DXCODE ends
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+ extrn segDXCodePM:WORD
+
+i31_dispatch label word
+
+ dw 0000h, offset i31_AllocSel
+ dw 0001h, offset i31_FreeSel
+ dw 0002h, offset i31_MapSeg2Sel
+ dw 0003h, offset i31_GetSelIncr
+ dw 0004h, offset i31_Success ;lock selector memory
+ dw 0005h, offset i31_Success ;unlock selector mem
+ dw 0006h, offset i31_GetSegAddr
+ dw 0007h, offset i31_SetSegAddr
+ dw 0008h, offset i31_SetLimit
+ dw 0009h, offset i31_SetAccess
+ dw 000Ah, offset i31_CreateDataAlias
+ dw 000Bh, offset i31_GetSetDescriptor
+ dw 000Ch, offset i31_GetSetDescriptor
+ dw 000Dh, offset i31_SpecificSel ; Allocate specific descriptor
+ dw 0100h, offset i31_AllocDOSMem
+ dw 0101h, offset i31_FreeDOSMem
+ dw 0102h, offset i31_SizeDOSMem
+
+ dw 0200h, offset i31_GetSetRMInt
+ dw 0201h, offset i31_GetSetRMInt
+ dw 0202h, offset i31_GetSetFaultVector
+ dw 0203h, offset i31_GetSetFaultVector
+ dw 0204h, offset i31_GetSetPMInt
+ dw 0205h, offset i31_GetSetPMInt
+ dw 0300h, offset i31_RMCall
+ dw 0301h, offset i31_RMCall
+ dw 0302h, offset i31_RMCall
+ dw 0303h, offset i31_AllocCallBack
+ dw 0304h, offset i31_FreeCallBack
+ dw 0305h, offset i31_GetStateSaveRestore
+ dw 0306h, offset i31_GetRawModeSwitch
+ dw 0400h, offset i31_Version
+
+ dw 04f1h, offset i31_WOW_AllocSel
+ dw 04f2h, offset i31_WOW_SetDescriptor
+ dw 04f3h, offset i31_WOW_SetAllocFunctions
+
+;
+; INCOMPLETE !!!!!!!!!!!
+; Needed by kernel.
+;
+ dw 0500h, offset i31_GetFreeMem
+ dw 0501h, offset i31_AllocMem
+ dw 0502h, offset i31_FreeMem
+;
+; Fails by design if block is to be extended and this cannot be done
+; in place.
+;
+ dw 0503h, offset i31_SizeMem
+
+ dw 0600h, offset i31_Success ;lock linear region
+ dw 0601h, offset i31_Success ;unlock linear region
+ dw 0602h, offset i31_Success ;mark real mode rgn pageable
+ dw 0603h, offset i31_Success ;relock real mode region
+ dw 0604h, offset i31_PageSize ;get page size
+
+ dw 0700h, offset i31_fail ;reserved
+ dw 0701h, offset i31_fail ;reserved
+ dw 0702h, offset i31_Success ;demand page candidate
+ dw 0703h, offset i31_Success ;discard page contents
+
+ dw 0800h, offset i31_fail ;physical addr mapping
+
+ dw 0900h, offset i31_VirtualInt ;get & disable Int state
+ dw 0901h, offset i31_VirtualInt ;get & enable Int state
+ dw 0902h, offset i31_VirtualInt ;get Int state
+;
+; UNIMPLEMENTED !!!!!!!!!!!
+; To be used for MSDOS protected mode API?
+;
+ dw 0A00h, offset i31_unimplemented ;get vendor specific API
+
+IFDEF WOW_x86
+ NO386 = 0
+else
+ NO386 = 1
+ENDIF
+ife NO386
+;
+; 386 Debug Register access routines.
+;
+ dw 0B00h, offset i31_Debug_Register_Access
+ ;set debug watchpoint
+ dw 0B01h, offset i31_Debug_Register_Access
+ ;clear debug watchpoint
+ dw 0B02h, offset i31_Debug_Register_Access
+ ;get debug watchpoint state
+ dw 0B03h, offset i31_Debug_Register_Access
+ ;reset debug watchpoint
+endif ; NO386
+
+ dw -1, offset i31_unimplemented
+
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl INT 31h Entry Point
+ page
+; -------------------------------------------------------
+; INT 31h SERVICE ENTRY POINT
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntr31 -- Service routine for the Protect Mode INT 31h
+; services. These functions duplicate the
+; Windows/386 VMM INT 31h services for protected
+; mode applications. They were implemented to
+; support a protect mode version of Windows/286.
+;
+; Input: Various registers
+; Output: Various registers
+; Errors:
+; Uses: All registers preserved, other than return values
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr31
+
+PMIntr31 proc near
+
+ cld ;practice 'safe programming'
+
+; Determine if this one of the services we support.
+
+ push bx
+ mov bx,offset i31_dispatch ;cs:bx -> dispatch table
+
+@@:
+ cmp ax,cs:[bx] ;scan dispatch table for
+ jz i31_do_it ; service code in AH/AL
+
+ cmp word ptr cs:[bx],-1 ;end of table is -1
+ jz i31_do_it
+
+ add bx,4
+ jmp short @b
+
+; BX contains the offset of the routine to service this request!
+
+i31_do_it:
+ push ds ;save the service routine address
+ mov ds,selDgroupPM ; in our DGROUP for now
+ assume ds:DGROUP
+
+ mov bx,cs:[bx+2]
+ FCLI ;needed for [i31_dsp_rtn]
+ mov i31_dsp_rtn,bx
+
+ifdef DEBUG
+ mov debugsavess,ss ; usefule when debugging WOW kernel
+ mov debugsavesp,sp
+ mov debugsavebp,bp
+ mov debugsavecx,cx
+endif
+
+ pop ds
+ pop bx
+
+i31_doit:
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:DGROUP ; also sets up addressability
+
+ push i31_dsp_rtn ;routine address on stack
+
+ DEBUG_TRACE DBGTR_ENTRY, 31h, ax, 0
+
+ FSTI ;no need to keep interrupts disabled
+
+ retn ;go perform the service
+
+i31_unimplemented: ;not implemented or undefined
+
+ Debug_Out "Unsupported Int 31h requested (AX = #AX)"
+
+; Int 31h service routines return (jmp) here to indicate failure. The
+; standard failure return sets the carry flag, and sets ax = 0.
+
+i31_fail:
+
+ mov [bp].intUserAX,0
+
+i31_fail_CY:
+
+ or byte ptr [bp].intUserFL,1
+ jmp short i31_exit
+
+; Int 31h service routines return (jmp) here -- they jump back instead
+; of returning because they expect the stack to be setup by EnterIntHandler,
+; no extra items should be pushed.
+
+i31_done:
+
+ and byte ptr [bp].intUserFL,not 1 ;clear carry flag
+
+i31_exit:
+ assume ds:NOTHING,es:NOTHING
+
+ FCLI ;LeaveIntHandler needs them off
+ call LeaveIntHandler ;restore caller's registers, and
+ DEBUG_TRACE DBGTR_EXIT, 31h, 0, 0
+ riret ; get on down the road
+
+PMIntr31 endp
+
+
+; -------------------------------------------------------
+; This routine is for Int 31h services that the 286 DOS extender
+; allows, but doesn't actually perform any work. Most of these
+; services are related to demand paging under Windows/386.
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_Success
+
+i31_Success proc near
+
+ jmp i31_done ;nothing to do currently
+
+i31_Success endp
+
+; -------------------------------------------------------
+; Page Size. Gotta be 1000h, even if we don't do anything
+; with it.
+;
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_PageSize
+
+i31_PageSize proc near
+
+ mov [bp].intUserBX,0
+ mov [bp].intUserCX,1000h
+ jmp i31_done ;nothing to do currently
+
+i31_pageSize endp
+
+; -------------------------------------------------------
+ subttl INT 31h LDT/Heap Services
+ page
+; -------------------------------------------------------
+; LDT/HEAP INTERRUPT (INT 31h) SERVICE ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Service 00/00 - Allocate Space in LDT for Selector
+; in: cx - # selectors required
+; out: ax - *index* of first selector
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_AllocSel
+
+i31_AllocSel proc near
+
+ jcxz short i31_15
+ cmp cx,1 ;1 selector or more?
+ ja @f
+
+ call AllocateSelector ;allocate 1 selector
+ jc i31_15
+ jmp short i31_10
+
+@@: mov ax,cx
+ push cx ; save # of selector to be allocated
+ xor cx,cx ; allocate from lower range
+ call AllocateSelectorBlock ;allocate a block of selectors
+ pop cx
+ jc i31_15
+
+i31_10:
+ or al,STD_TBL_RING ;add standard table/ring bits
+ mov [bp].intUserAX,ax ;return 1st/only selector in AX
+
+setsel: mov bx,STD_DATA ;all the selectors we allocate
+; xor bl,bl ; get initialized to data/ring ?
+ xor dx,dx ; 0 base, 0 limit
+@@:
+ cCall NSetSegmentDscr,<ax,dx,dx,dx,dx,bx>
+ add ax,SelectorIncrement
+ loop @b
+
+ jmp i31_done
+
+i31_15:
+ jmp i31_fail ;fail the request
+
+i31_AllocSel endp
+
+
+; -------------------------------------------------------
+; Service 00/01 - Free LDT Selector
+; in: bx - selector to free
+; out: none
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_FreeSel
+
+i31_FreeSel proc near
+
+ mov ax,bx ;release the selector
+ cmp ax,SEL_DPMI_LAST ; reserved selector?
+ ja i31_Free_Regular_LDT_Selector ; No.
+ mov cx,ax
+ shr cl,3 ; Selector to selector index in LDT
+ mov ax,1
+ shl ax,cl ; AX = bit in i31_selectorbitmap
+ test i31_selectorbitmap,ax ; test for already allocated
+ jz i31_FreeSel_Fail ; already free!
+ not ax
+ and i31_selectorbitmap,ax ; mark as free
+ jmp i31_done
+
+i31_Free_Regular_LDT_Selector:
+
+ and ax,SELECTOR_INDEX ; only pass it the index
+ or ax,SELECTOR_TI ;only allow LDT selectors
+ jz i31_Check_DS_ES
+ call FreeSelector
+ jnc i31_Check_DS_ES
+i31_FreeSel_Fail:
+ jmp i31_fail_CY
+
+i31_Check_DS_ES:
+
+ ; check in case user frees the selector in DS or ES - we
+ ; don't want to fault when popping his regs
+
+ and bl,NOT SELECTOR_RPL ;compare without ring bits
+
+ mov ax,[bp].pmUserDS
+ and al,NOT SELECTOR_RPL
+ cmp ax,bx
+ jnz @f
+
+ mov [bp].pmUserDS,0
+
+@@: mov ax,[bp].pmUserES
+ and al,NOT SELECTOR_RPL
+ cmp ax,bx
+ jnz @f
+
+ mov [bp].pmUserES,0
+@@:
+ jmp i31_done
+
+i31_FreeSel endp
+
+
+; -------------------------------------------------------
+; Service 00/02 - Map Segment to Selector
+; in: bx - real mode segment value
+; out: ax - selector which maps area
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_MapSeg2Sel
+
+i31_MapSeg2Sel proc near
+
+ mov ax,bx ;find/make selector for real memory
+ mov bx,STD_DATA ;assume it's a data selector
+ call ParaToLDTSelector
+ jnc @f
+
+ jmp i31_fail_CY ;Falied!?
+@@:
+ mov [bp].intUserAX,ax
+ jmp i31_done
+
+i31_MapSeg2Sel endp
+
+
+; -------------------------------------------------------
+; Service 00/03 - Get Next Selector Increment Value
+; in: none
+; out: ax - Next Selector Increment Value
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSelIncr
+
+i31_GetSelIncr proc near
+
+ mov [bp].intUserAX,SelectorIncrement ;DOSX incr value
+ jmp i31_done
+
+i31_GetSelIncr endp
+
+
+; -------------------------------------------------------
+; Service 00/06 - Get Segment Base address.
+; in: bx - selector
+; out: cx:dx - 32 bit lma of segment
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSegAddr
+
+i31_GetSegAddr proc near
+
+ mov ax,selGDT
+ assume es:nothing
+ lsl ax,ax
+
+ push bx
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax
+ jnc gsa10 ; not in ldt
+
+ call IsSelectorFree
+ jc gsa20
+
+gsa10: pop bx
+ jmp i31_fail_CY
+
+gsa20: pop bx
+
+ mov ax,bx
+ call GetSegmentAddress
+
+ mov [bp].intUserCX,bx
+ mov [bp].intUserDX,dx
+
+ jmp i31_done
+
+i31_GetSegAddr endp
+
+
+; -------------------------------------------------------
+; Service 00/07 - Set Segment Base address.
+; in: bx - selector
+; cx:dx - 32 bit lma of segment
+; out: none
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SetSegAddr
+
+i31_SetSegAddr proc near
+
+ mov ax,selGDT
+ assume es:nothing
+ lsl ax,ax
+
+ push bx
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax
+ jnc ssa10 ; not in ldt
+
+ call IsSelectorFree
+ jc ssa20
+
+ssa10: pop bx
+ jmp i31_fail_CY
+
+ssa20: pop bx
+ mov ax,bx
+ mov bx,cx
+ call SetSegmentAddress
+
+ jmp i31_done
+
+i31_SetSegAddr endp
+
+
+; -------------------------------------------------------
+; Service 00/08 - Set Segment Limit.
+; in: bx - selector
+; cx:dx - 32 bit limit of segment
+; out: none
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SetLimit
+
+i31_SetLimit proc near
+
+ mov ax,selGDT
+ assume es:nothing
+ lsl ax,ax
+
+ push bx
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax
+ jnc sl10 ; not in ldt
+
+ call IsSelectorFree
+ jc sl20
+
+sl10: pop bx
+ jmp i31_fail_CY
+
+sl20: pop bx
+ mov es,selGDT
+ and bx,SELECTOR_INDEX
+ and es:[bx].cbLimitHi386,070h ; clear 'G' bit and old
+ ; extended limit bits
+ test cx,0fff0h ; bits 20-31 set?
+ jz i31_SetLimit_0 ; No
+ shr dx,12d ; Yes
+ mov ax,cx
+ shl ax,4d
+ or dx,ax
+ shr cx,12d
+ or es:[bx].cbLimitHi386,080h ; set 'G' bit
+i31_SetLimit_0:
+ mov es:[bx].cbLimit,dx
+ or es:[bx].cbLimitHi386,cl
+ cCall NSetSegmentLimit,<bx>
+ jmp i31_done
+
+i31_SetLimit endp
+
+
+; -------------------------------------------------------
+; Service 00/09 - Set Segment Access Rights
+; in: bx - selector
+; cl - segment access rights byte
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SetAccess
+
+i31_SetAccess proc near
+
+ mov ax,selGDT
+ assume es:nothing
+ lsl ax,ax
+
+ push bx
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax
+ jnc sa10 ; not in ldt
+
+ call IsSelectorFree
+ jc sa20
+
+sa10: pop bx
+sa11:
+ jmp i31_fail_CY
+
+sa20: pop bx
+
+ test cl,010000b ; system segment?
+ jz sa11 ; y: error
+ push cx
+ and cl,AB_DPL ; mask off all but DPL bits
+ cmp cl,STD_DPL ; is this the correct DPL?
+ pop cx
+ jnz sa11 ; n: error
+
+ cCall NSetSegmentAccess,<bx,cx>
+ jmp i31_done
+
+i31_SetAccess endp
+
+
+; -------------------------------------------------------
+; Service 00/0A - Create Data Segment Alias (for a code seg)
+; in: bx - selector
+; out: ax - new data selector
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_CreateDataAlias
+
+i31_CreateDataAlias proc near
+
+ mov ax,bx ;make sure it's a vaild selector
+ verr ax
+ jnz cda_failed
+
+ mov bx,ax ;get a new selector for the alias
+ call AllocateSelector
+ jc cda_failed
+
+ xchg ax,bx ;copy old to new
+ call DupSegmentDscr
+
+ mov es,selGDT
+ and bx,SELECTOR_INDEX
+ mov al,es:[bx].arbSegAccess
+ and al,11100000b ;mask off all but present and DPL bits
+ or al,AB_DATA or AB_WRITE
+ mov ah, es:[bx].cbLimitHi386
+ cCall NSetSegmentAccess,<bx,ax>
+ or bl,STD_TBL_RING ;set standard table/ring bits
+ mov [bp].intUserAX,bx
+
+ jmp i31_done
+
+cda_failed:
+ jmp i31_fail_CY
+
+i31_CreateDataAlias endp
+
+
+; -------------------------------------------------------
+; Service 00/0B - Get Descriptor
+; Service 00/0C - Set Descriptor
+; in: bx - selector
+; es:di -> buffer to hold copy of descriptor
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSetDescriptor
+
+i31_GetSetDescriptor proc near
+
+ .386p
+ push esi
+ push edi
+ .286p
+ mov ax,selGDT
+ mov es,ax
+ assume es:NOTHING
+ lsl ax,ax
+
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax ; range-test selector against
+ jc @f ; the limit of the GDT/LDT
+
+.386p
+ pop edi
+ pop esi
+.286p
+ jmp i31_fail_CY ;fail if invalid selector specified
+@@:
+ call IsSelectorFree
+ jc @f
+
+.386p
+ pop edi
+ pop esi
+.286p
+ jmp i31_fail_CY
+@@:
+
+ cmp byte ptr [bp].intUserAX,SelMgt_Get_Desc ;Get or Set?
+ jz i31_GetDscr
+
+;
+; Set Descriptor
+;
+ test DpmiFlags,DPMI_32BIT
+ jnz gsd10
+.386p
+ mov esi,0 ; zero high half
+ mov edi,0
+ jmp gsd20
+
+gsd10: mov esi,edi ; get high half of edi
+ mov edi,0 ; zero high half
+.286p
+gsd20:
+ push ds ;Set -
+ mov ds,[bp].pmUserES
+ assume ds:nothing
+
+ mov si,[bp].pmUserDI ; ds:si -> caller's buffer
+ mov di,bx ; es:di -> dscr slot in GDT/LDT
+
+ .386p
+ mov cl,ds:[esi].arbSegAccess386
+ .286p
+ test cl,010000b ; system segment?
+ jz gsd25 ; y: error
+ and cl,AB_DPL ; mask off all but DPL bits
+ cmp cl,STD_DPL ; is this the correct DPL?
+ jnz gsd25 ; n: error
+ jmp short i31_MovDscr
+
+gsd25: ; set ldt format error
+ pop ds
+.386p
+ pop edi
+ pop esi
+.286p
+ jmp i31_fail_CY
+
+i31_GetDscr:
+ assume ds:DGROUP
+;
+; Get Descriptor
+;
+ test DpmiFlags,DPMI_32BIT
+ jnz gsd30
+.386p
+ mov edi,0 ; zero high half of edi
+gsd30: mov esi,0 ; zero high half of esi
+
+.286p
+ push ds ;Get -
+ push es
+ pop ds
+ assume ds:nothing
+ mov si,bx ; ds:si -> dscr slot in GDT/LDT
+ mov es,[bp].pmUserES
+ mov di,[bp].pmUserDI ; es:di -> caller's buffer
+
+i31_MovDscr:
+ .386p
+ cCall NMoveDescriptor,<ds,esi,es,edi>
+ .286p
+i31_gs25:
+ pop ds
+ assume ds:DGROUP
+
+.386p
+ pop edi
+ pop esi
+.286p
+ jmp i31_done
+
+i31_GetSetDescriptor endp
+
+; -------------------------------------------------------
+; Service 00/0D - Allocate Specific LDT Selector
+; in: bx - selector
+; out: carry clear if selector was allocated
+; carry set if selector was not allocated
+ assume ds:DGROUP,es:DGROUP
+ public i31_SpecificSel
+
+i31_SpecificSel proc near
+
+ and bx,SELECTOR_INDEX
+ cmp bx,SEL_DPMI_LAST
+ ja i31_SpecificSel_Fail
+ mov cx,bx
+ shr cl,3 ; Selector to selector index in LDT
+ mov ax,1
+ shl ax,cl ; AX = bit in i31_selectorbitmap
+ test i31_selectorbitmap,ax ; test for already allocated
+ jnz i31_SpecificSel_Fail ; allocated, fail
+ or i31_selectorbitmap,ax ; mark as allocated
+
+ ;
+ ; Set up the DPL and size
+ ;
+ mov ax,bx
+ mov cx,1
+
+ jmp setsel
+
+i31_SpecificSel_Fail: ; couldn't get that one
+ jmp i31_fail_CY
+
+i31_SpecificSel endp
+
+; -------------------------------------------------------
+ subttl INT 31h DOS Memory Services
+ page
+; -------------------------------------------------------
+; INT 31h DOS MEMORY SERVICE ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Service 01/00 - Allocate DOS Memory Block
+;
+; In: BX - # paragraphs to allocate
+; Out: If successful: Carry Clear
+; AX - segment address of block
+; DX - selector to access block
+;
+; If unsuccessful: Carry Set
+; AX - DOS error code
+; BX - size of largest available block in paragraphs
+;
+; 13-Feb-1991 -- ERH This call is not supported under Windows 3.1,
+; but try to find a block in our net heap to
+; satisfy the request, anyway.
+;
+
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_AllocDOSMem
+
+i31_AllocDOSMem proc near
+ and [bp].intUserFL,NOT 1 ; clear carry in status
+ mov cx,[bp].intUserBX
+ mov dx,[bp].intUserBX
+ shr dx,12 ; high half of byte count
+ shl cx,4 ; low half of byte count
+
+ call AllocateLowBlock
+
+ jc adm30
+
+ mov [bp].intUserDX,ax
+ mov si,ax
+
+ call GTPARA ; get paragraph address
+
+ mov [bp].intUserAX,ax
+ jmp i31_done
+
+adm30: shr cx,4
+ shl dx,12
+ or cx,dx ; paragraph size of largest
+ mov [bp].intUserBX,cx
+ mov [bp].intUserAX,ax ; error code
+ jmp i31_fail_CY
+
+i31_AllocDOSMem endp
+
+
+; -------------------------------------------------------
+; Service 01/01 - Release DOS Memory Block
+;
+; In: DX - SELECTOR of block to release
+; Out: If successful: Carry Clear
+;
+; If unsuccessful: Carry Set
+; AX - DOS error code
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_FreeDOSMem
+
+i31_FreeDOSMem proc near
+
+ mov ax,[bp].intUserDX
+ verw ax
+ jnz fdm60
+
+ call IsSelectorFree
+ jnc fdm60
+
+ mov ax,[bp].intUserDX
+ call FreeLowBlock
+
+ jc fdm60
+ jmp i31_Success
+
+fdm60: mov [bp].intUserAX,ax
+ jmp i31_fail_CY
+
+i31_FreeDOSMem endp
+
+
+; -------------------------------------------------------
+; Service 01/02 - Resize DOS Memory Block
+;
+; In: BX - new block size in paragraphs
+; DX - SELECTOR of block to release
+; Out: If successful: Carry Clear
+;
+; If unsuccessful: Carry Set
+; AX - DOS error code
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SizeDOSMem
+
+i31_SizeDOSMem proc near
+
+ mov [bp].intUserBX,0
+ mov [bp].intUserAX,08h ; insufficient mem. available
+ jmp i31_fail_CY
+
+i31_SizeDOSMem endp
+
+
+; -------------------------------------------------------
+ subttl INT 31h Real Mode Int Vector Routines
+ page
+; -------------------------------------------------------
+; INT 31h INTERRUPT SERVICES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; i31_GetSetRMInt -- Get/Set Real Mode Interrupt Vector
+;
+; In: bl - Interrupt #
+; cx:dx - SEG:Offset of real mode int vector (if set)
+; Out: cx:dx - SEG:Offset of real mode int vector (if get)
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSetRMInt
+
+i31_GetSetRMInt proc near
+
+ push SEL_RMIVT or STD_RING ;address the real mode IVT
+ pop es
+ assume es:NOTHING
+
+; HACK. Starting with Windows 3.1, we do not remap the PIC anymore. However,
+; Windows 3.0 Comm Drivers, thinking that the PIC will always be remapped,
+; deal with interrupts in the range 50h to 57h. There is now a SYSTEM.INI
+; switch to tell us that a Windows 3.0 Comm Driver is being used. If this
+; switch is set, we will map 50-57h to 8-0fh in this call.
+
+ cmp fWin30CommDrv,0 ;are we using a Windows 3.0 Comm Drv ?
+ je i31_GSRMInt_not_50to57h ;no.
+ cmp bl,50h ;is it in the range 50 to 57h ?
+ jb i31_GSRMInt_not_50to57h ;no.
+ cmp bl,57h ;is it above 57h
+ ja i31_GSRMInt_not_50to57h ;yes, no hack needed.
+
+; map from the range 50-57h to 08-0fh.
+
+ sub bl,50h-8h
+
+i31_GSRMInt_not_50to57h:
+
+ xor bh,bh ;convert int # to offset
+ shl bx,2
+
+ FCLI ;play it safe
+
+ cmp al,Int_Get_Real_Vec ;Get or Set?
+ jnz i31_gs_set
+
+ mov cx,word ptr es:[bx+2] ;get segment:offset
+ mov dx,word ptr es:[bx]
+
+ ;
+ ; If we have installed a reflector, return the original handler
+ ;
+ cmp cx,segDXCodePM
+ jne gsr20
+
+ cmp dx,offset RMIntrEntryVector
+ jb gsr20
+
+ cmp dx,offset EndRMIntrEntry
+ jae gsr20
+
+ mov cx,word ptr RmHwIsr[bx + 2]
+ mov dx,word ptr RmHwIsr[bx]
+
+gsr20: mov [bp].intUserCX,cx ;return them to caller
+ mov [bp].intUserDX,dx
+
+ jmp short i31_gs_ret
+
+i31_gs_set: ;setting the vector...
+
+ mov es:[bx],dx ;set the real mode IDT vector
+ mov es:[bx+2],cx
+
+i31_gs_ret:
+
+ FSTI
+ jmp i31_done
+
+i31_GetSetRMInt endp
+
+
+; -------------------------------------------------------
+; i31_GetSetFaultVector -- Get/Set Protected Mode Fault Vector (0h-1Fh)
+;
+; In: bl - Interrupt #
+; cx:dx - Sel:Offset of pMode int vector (if set)
+; Out: cx:dx - Sel:Offset of pMode int vector (if get)
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSetFaultVector
+
+i31_GetSetFaultVector proc near
+
+ cmp bl,10h ; zero to 10h are defined for 80386
+ jbe @f
+ jmp i31_fail_CY ;must be <= 10h or fail it
+@@:
+ xor bh,bh
+ mov ax,bx ;interrupt # to AX
+
+ cmp byte ptr [bp].intUserAX,Int_Get_Excep_Vec ;Get or Set?
+ jne i31_gfv_set
+
+ call GetFaultVector ;wants to get the vector
+ mov [bp].intUserCX,cx
+ mov [bp].intUserDX,dx
+
+ jmp short i31_gfv_ret
+
+i31_gfv_set:
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jnz sfv10
+
+ movzx edx,dx ; zero high half
+.286p
+sfv10: call PutFaultVector ;doing a set (args already set)
+
+i31_gfv_ret:
+ jmp i31_done
+
+i31_GetSetFaultVector endp
+
+; -------------------------------------------------------
+; i31_GetSetPMInt -- Get/Set Protected Mode Interrupt Vector
+;
+; In: bl - Interrupt #
+; cx:dx - SEL:Offset of protected mode int vector (if set)
+; Out: cx:dx - SEL:Offset of protected mode int vector (if get)
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSetPMInt
+
+i31_GetSetPMInt proc near
+
+ xchg al,bl
+ xor ah,ah
+
+ cmp bl,Int_Get_PMode_Vec ;Get or Set?
+ jnz i31_gsp_set
+
+; NOTE: we don't call DFGetIntrVector here, because all it does is a call to
+; the following routine
+
+ call GetIntrVector
+ mov [bp].intUserCX,cx
+ mov [bp].intUserDX,dx
+
+ jmp i31_gsp_done
+
+i31_gsp_set:
+
+ ; set up the appropriate real mode reflector, and hook the int.
+ call DFSetIntrVector
+
+i31_gsp_done:
+ FSTI
+ jmp i31_done
+
+i31_GetSetPMInt endp
+
+; -------------------------------------------------------
+ subttl INT 31h Protected-to-Real Mode Call/Int
+ page
+; -------------------------------------------------------
+; INT 31h PROTECTED-TO-REAL MODE CALL/INT SERVICES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Service 03/00 -- Simulate real mode interrupt
+; Service 03/01 -- Call real mode procedure with far return frame
+; Service 03/02 -- Call real mode procedure with iret return frame
+;
+; In: es:di -> client register structure for real mode
+; bl = interrupt number (03/00 only)
+; cx = # words to copy from protected mode stack
+; Out: es:di -> updated client register structure
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_RMCall
+
+i31_RMCall proc near
+
+; First off, we need to copy the client register structure down to
+; real mode addressable memory... Lets use rgbXfrBuf1 for now...
+; Changed to use the interrupt reflector stack because these routines
+; need to be reentrant. This also lets us leave interrupts enabled
+; longer.
+; Earleh - 27-Jun-1990.
+
+ mov di,sp ; DI = SP = client regs. buffer
+
+ sub di,size Real_Mode_Call_Struc+2 ; Make room for client
+ sub di,[bp].intUserCX ; call structure plus
+ sub di,[bp].intUserCX ; any optional stack params
+ sub di,32 ; pushad
+ push di ; plus copy of di.
+.386p
+ pushad ; have to save high 16 bits
+.286p
+
+ mov sp,di
+
+; --------------------------------------------------------------
+;
+; The interrupt reflector stack frame now looks like this...
+;
+; pbReflStack + CB_STKFRAME-> ---------------------------
+; (old pbReflStack) | |
+; | INTRSTACK Struc |
+; | from EnterIntHandler |
+; | |
+; ---------------------------
+; pbReflStack + CB_STKFRAME | word pointer |>>--\
+; - (SIZE INTRSTACK) - 2 -> --------------------------- |
+IFDEF WOW_x86
+; | | |
+; | Pushad frame | |
+; | | |
+; --------------------------- |
+ENDIF
+; | | |
+; | optional stack params | |
+; | | |
+; --------------------------- |
+; | | |
+; | | |
+; | Real_Mode_Call_Struc | |
+; | | |
+; | | |
+; ---------------------------<<--/
+; | |
+; | Available stack space |
+; | |
+; pbReflStack---------->---------------------------
+;
+; After returning from the real mode procedure, we will need
+; to fetch the word pointer stored just below the EnterIntHandler
+; frame, and use it to access our temporary Real_Mode_Call_Struc.
+; In addition to holding the client register values, this also
+; holds the SP and BP values we will need to switch back to our
+; stack when we return from the real mode procedure.
+;
+; !!! -- Storing the optional stack parameters on top of our
+; Real_Mode_Call_Struc has several problems. It eats up stack
+; space if we call the real mode procedure on our stack, because
+; we then have to make another copy of these. It also means we have to
+; carry around a pointer to where the Real_Mode_Call_Struc lives.
+; If we didn't have the stack parameters on top of the Real_Mode_Call_Struc,
+; then we could find things strictly by offset from the value in
+; pbReflStack. The picture above should be rearranged to make optimal
+; use of space. I basically did it this way so I could have a minimum-
+; change fix for a bug in Windows 3.0.
+; !!!
+; --------------------------------------------------------------
+;
+;
+
+ cld
+ .386p
+ xor ecx,ecx
+ .286p
+ mov cx,(size Real_Mode_Call_Struc) / 2
+
+ mov bx,di ;bx used to reference client regs below
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz rmc10
+
+ mov esi,edi ; copy over high 16 bits
+ jmp rmc20
+
+rmc10: xor esi,esi ; clear high 16 bits esi
+rmc20: xor edi,edi ; clear high 16 bits edi
+ mov di,bx
+.286p
+ mov si,[bp].pmUserDI
+ mov ds,[bp].pmUserES
+ assume ds:NOTHING
+.386p
+ rep movs word ptr [esi],word ptr [edi]
+.286p
+
+
+; Copy stack parameters from PM to RM stack if requested by caller. To
+; avoid excessive selector munging, we do this in two steps (under the
+; assumption the # words to copy will be small). First the PM stack args
+; are copied to a buffer in DXDATA, then after switching to real mode,
+; to the real mode stack. If the caller has more stack words than will
+; fit in our buffer, or the real mode stack, bad things will happen...
+
+ .386p
+ xor ecx,ecx
+ .286p
+ mov cx,[bp].intUserCX ;caller's CX has # stack words to copy
+ jcxz @f
+
+ Trace_Out "Int 31h PM-to-RM int/call copying #CX stack words"
+
+.386p
+ xor esi,esi
+.286p
+ mov ds,[bp].pmUserSS
+ mov si,[bp].pmUserSP
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz rmc30
+;
+; Have to go groping for user stack, since we switched stacks to get
+; here.
+;
+; | |
+; +----------------+
+; | |
+; +---- SS -----+
+; | |
+; +----------------+
+; | |
+; +---- ESP -----+
+; | |
+; +----------------+
+; | Flags |
+; +----------------+
+; | CS |
+; +----------------+
+; | IP |
+; ds:si -> +----------------+
+
+ push dword ptr ds:[si + 6]
+ push word ptr ds:[si + 10]
+ pop ds
+ pop esi
+ add esi,6 ; other half of 32 bit stack frame
+
+rmc30: add esi,6 ;ds:si -> PM stack args
+ rep movs word ptr [esi],word ptr [edi]
+.286p
+ ;es:di already points to buffer
+@@:
+ push es ;restore ds -> DGROUP
+ pop ds
+ assume ds:DGROUP
+
+; Switch to real mode, set up the real mode stack
+
+ SwitchToRealMode
+ assume ds:DGROUP,es:DGROUP
+
+i31_rmcall_hw_ok:
+ FSTI ;RestoreHardwareIntr disables ints
+ ;don't need them disabled now
+
+ mov [bx].RealMode_SaveBP,bp ;save our stack in reserved area of
+ mov [bx].RealMode_SaveSP,sp ; real mode register frame
+
+ mov cx,[bp].pmUserCX ;cx = caller's CX (# stk words)
+ mov dh,byte ptr [bp].pmUserAX ;dh = caller's AL (subfunction)
+ mov dl,byte ptr [bp].pmUserBX ;dl = caller's BL (RM int #)
+
+ mov ax,[bx].RealMode_SS ;did caller specify his own stack?
+ or ax,[bx].RealMode_SP
+
+ jz @f
+
+ ; NOTE: can't reference [bp].xxUserXX varaibles after switching stacks
+
+ mov ss,[bx].RealMode_SS ;switch to caller's real mode stack
+ mov sp,[bx].RealMode_SP
+
+ assume ss:NOTHING
+@@:
+
+; Copy stack args to real mode stack if there are any
+
+ jcxz @f
+
+ sub sp,cx
+ sub sp,cx ;make space on stack for args
+
+ mov di,sp
+ mov ax,ss
+ mov es,ax ;es:di -> real mode stack
+ assume es:NOTHING
+
+ lea si,[bx + size Real_Mode_Call_Struc]
+
+ cld
+ rep movsw
+
+ push ds
+ pop es
+ assume es:DGROUP
+@@:
+
+; Put a far ret or iret frame on stack to return to us
+
+ cmp dh,Trans_Far_Call ;Does this service use a far ret or
+ jz i31_rmcall_retf ; an iret frame?
+
+ mov ax, [bx].RealMode_Flags ;real mode routine thinks these were
+ and ax, NOT 100h ;remove TF
+ push ax
+ push cs ; the prior flags and CS:IP
+ push offset i31_rmcall_ret
+
+ FCLI ;flags with interrupts disabled -- real
+ pushf ; mode rtn entered with these flags
+ FSTI
+ jmp short @f
+
+i31_rmcall_retf:
+
+ push cs ;push a far ret frame so the
+ push offset i31_rmcall_ret ; real mode routine returns to us
+
+ mov ax, [bx].RealMode_Flags ;real mode rtn entered with these flags
+ and ax, NOT 100h ;remove TF
+ push ax
+@@:
+ cmp dh,Trans_Sim_Int ;use an int vector, or caller's spec'd
+ jnz i31_rmcall_csip ; cs:ip?
+
+ mov al,dl ;push CS:IP for interrupt
+ xor ah,ah ; number in caller's BL
+ mov si,ax
+ shl si,2
+
+ xor ax,ax ;address real mode IDT
+ mov es,ax
+ assume es:NOTHING
+
+ push word ptr es:[si+2]
+ push word ptr es:[si]
+
+ jmp short @f
+
+i31_rmcall_csip:
+
+ push [bx].RealMode_CS ;execute the real mode routine at
+ push [bx].RealMode_IP ; specified CS:IP
+@@:
+
+; Load the clients registers, and pass control to the real mode routine
+.386p
+ mov edi,dword ptr [bx].RealMode_DI
+ mov esi,dword ptr [bx].RealMode_SI
+ mov ebp,dword ptr [bx].RealMode_BP
+ mov edx,dword ptr [bx].RealMode_DX
+ mov ecx,dword ptr [bx].RealMode_CX
+ mov eax,dword ptr [bx].RealMode_AX
+ mov es,[bx].RealMode_ES
+ assume es:NOTHING
+
+ push [bx].RealMode_DS
+ push dword ptr [bx].RealMode_BX
+
+ pop ebx
+ pop ds
+ assume ds:NOTHING
+.286p
+ iret
+
+
+; The real mode routine returns here when finished
+
+i31_rmcall_ret:
+
+ pushf ;save returned flags, ds, bx on stack
+ FSTI ;don't need ints disabled
+ push ds
+.386p
+ push ebx
+.286p
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,segDXData ;address our DGROUP
+ENDIF
+ assume ds:DGROUP
+;
+; Fetch word pointer to temporary client register save area, that we
+; saved before switching stacks.
+;
+ mov bx,[pbReflStack]
+ mov bx,[bx + CB_STKFRAME - (SIZE INTRSTACK) - 2]
+
+; Save the real mode registers in client frame
+
+.386p
+ mov dword ptr [bx].RealMode_DI,edi
+ mov dword ptr [bx].RealMode_SI,esi
+ mov dword ptr [bx].RealMode_BP,ebp
+ mov dword ptr [bx].RealMode_DX,edx
+ mov dword ptr [bx].RealMode_CX,ecx
+ mov dword ptr [bx].RealMode_AX,eax
+ mov [bx].RealMode_ES,es
+
+ pop dword ptr [bx].RealMode_BX
+.286p
+ pop [bx].RealMode_DS
+ pop [bx].RealMode_Flags
+ or [bx].RealMode_Flags,03000h ; IOPL always has to be 3
+
+
+; Restore our stack and return to protected mode
+; SP will now point to base of temporary client register save area
+; on our stack. BP points to stack frame set up for us by EnterIntHandler.
+; SP must be restored to value in BP before calling LeaveIntHandler.
+
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,segDXData ;address our DGROUP
+ENDIF
+ mov sp,[bx].RealMode_SaveSP
+ mov bp,[bx].RealMode_SaveBP
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI ;still don't need ints disabled
+
+; Apparently in win31 standard mode, the pm caller's flags are set to the
+; value of the rm flags at the handler's iret. On win31 enhanced mode, this
+; was *not* done, and the pm flags are, except for carry, basically
+; preserved. Since setting the flags kills some apps (winfax deltest),
+; we should adhere to the enhanced mode convention. Thus, the following
+; code is if'd out.
+if 0
+ mov ax,[bx].RealMode_Flags
+ mov [bp].intUserFL,ax
+endif
+
+; Copy the updated client register frame to the caller, and we're finished
+
+ cld
+ .386p
+ xor ecx,ecx
+ .286p
+ mov cx,(size Real_Mode_Call_Struc) / 2
+.386p
+ xor esi,esi
+ xor edi,edi
+
+ mov si,bx
+ add si,[bp].pmUserCX
+ add si,[bp].pmUserCX
+ add si,size Real_Mode_Call_Struc
+;
+; Si now points at pushad frame
+;
+ push si
+
+ test DpmiFlags,DPMI_32BIT
+ jz rmc80
+ mov edi,[si]
+rmc80:
+.286p
+ mov si,bx
+ mov di,[bp].pmUserDI
+ mov es,[bp].pmUserES
+ assume es:NOTHING
+.386p
+ rep movs word ptr [esi],word ptr [edi]
+
+ pop sp ; value calculated above
+ popad
+ mov sp,bp
+.286p
+
+ jmp i31_done ;finished!
+
+i31_RMCall endp
+
+
+; -------------------------------------------------------
+; Service 03/03 -- Allocate Real Mode Call-Back Address
+;
+; In: ds:si -> pMode CS:IP to be called when rMode
+; call-back address executed
+; es:di -> client register structure to be updated
+; when call-back address executed
+; Out: cx:dx -> SEGMENT:offset of real mode call-back hook
+; CY clear if successful, CY set if can't allocate
+; call back
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_AllocCallBack
+
+i31_AllocCallBack proc near
+
+; Search call-back table for a free entry
+
+ mov bx,offset DGROUP:CallBackTable
+ mov cx,cRM_CALL_BACK
+@@:
+ test [bx].fInUse,0FFh ;in use?
+ jz i31_avail_callback
+ add bx,size CallBackTableStruc
+ loop @b
+
+ jmp i31_fail_CY ;no call-backs available, fail
+
+; Fill in the call back table entry with caller's info
+
+i31_avail_callback:
+
+ mov [bx].fInUse,0FFh ;mark as in use
+
+ mov ax,[bp].pmUserDS
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz acb20
+ mov [bx].PM_CS_IP,esi
+ mov [bx].PM_Client_Frame,edi
+ mov word ptr [bx].PM_CS_IP + 4,ax
+ jmp acb30
+
+acb20: mov word ptr [bx].PM_CS_IP,si
+ mov word ptr [bx].PM_Client_Frame,di
+ mov word ptr [bx].PM_CS_IP + 4,ax
+ mov ax,0
+ mov word ptr [bx].PM_CS_IP + 2,ax
+ mov word ptr [bx].PM_Client_Frame + 2,ax
+acb30:
+.286p
+
+ mov ax,[bp].pmUserES
+.386p
+ mov word ptr [bx].PM_Client_Frame + 4,ax
+.286p
+
+; Return Seg:Off of Real Mode call-back to caller in CX:DX
+
+ mov ax,segDXCodePM ;segment
+ mov [bp].intUserCX,ax
+
+ sub cx,cRM_CALL_BACK
+ neg cx ;cx = allocated entry # (0..n)
+
+ errnz <cRM_CALL_BACK gt 255> ;code assumes cx <= 255
+
+ mov al,3 ;convert entry # to IP
+ mul cl ;3 bytes per entry
+ add ax,offset DXCODE:RMCallBackEntryVector
+ mov [bp].intUserDX,ax
+
+ jmp i31_done
+
+i31_AllocCallBack endp
+
+
+; -------------------------------------------------------
+; Service 03/04 -- Free Real Mode Call-Back Address
+;
+; In: cx:dx -> SEGMENT:offset of rMode call-back to free
+; Out: CY clear if successful, CY set if failure
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_FreeCallBack
+
+i31_FreeCallBack proc near
+
+; Determine which Real Mode Call-Back is being freed
+
+ cmp cx,segDXCodePM ;sanity check the incoming CS:IP
+ jne i31_FreeCBFail
+
+ cmp dx,offset DXCODE:RMCallBackEntryVector
+ jb i31_FreeCBFail
+
+ mov ax,dx
+ sub ax,offset DXCODE:RMCallBackEntryVector
+ mov bl,3
+ div bl ;al = entry # (0..n)
+
+ cmp al,cRM_CALL_BACK ;past end of table?
+ jae i31_FreeCBFail
+
+ errnz <size CallBackTableStruc gt 255>
+
+ mov bl,size CallBackTableStruc
+ mul bl
+ mov bx,ax
+ add bx,offset DGROUP:CallBackTable ;bx -> CallBackTable entry
+
+ mov [bx].fInUse,0 ;mark it free
+
+ jmp i31_done ;finished!
+
+i31_FreeCBFail:
+
+ jmp i31_fail_CY
+
+i31_FreeCallBack endp
+
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; RMCallBackHook -- Real Mode to Protected Mode Call-Back Hook
+;
+; This routine gets control whenever a user's real mode call-back
+; hook is executed. This routine saves the current registers in
+; the specified client register frame, switches to protected mode,
+; and invokes the specified pMode CS:IP. Upon return from the
+; pMode call-back, the registers are restored from a client frame
+; and control is passed to the CS:IP specified in the frame.
+;
+
+ public RMCallBackHook
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+RMCallBackHook proc
+
+ pushf ;save current flags
+
+ FCLI ;protect global variables
+ cld ;and we expect direction to be 'normal'
+
+ push ds ;address our DGROUP
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+
+ pop regUserDS ;save regs in global vars -- keep ints off!
+ pop regUserFL
+ mov regUserAX,ax
+ mov regUserES,es
+
+ pop ax ;ax = entry vector return address
+
+; Convert the entry vector return address into a call-back table pointer
+
+ push bx
+
+ sub ax,offset RMCallBackEntryVector+3
+ mov bl,3
+ div bl
+ mov bl,size CallBackTableStruc
+ mul bl
+ add ax,offset DGROUP:CallBackTable
+
+ pop bx
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame, and switch to pMode.
+
+ mov regUserSS,ss ;save current user stack
+ mov regUserSP,sp
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,selDgroup ;switch to the reflector stack frame
+ENDIF
+ mov sp,pbReflStack
+ sub pbReflStack,2 * CB_STKFRAME ;adjust pointer to next stack frame
+
+ FIX_STACK
+ DEBUG_TRACE_RM DBGTR_STACKPTR, pbReflStack, 0, 0
+ push ax ;save CallBackTable pointer
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ mov ax,bx ;save bx in ax
+ pop bx ;CallBackTable pointer to bx
+
+; Store current registers into Client register frame
+
+.386p
+ push edi
+
+ les edi,fword ptr [bx].PM_Client_Frame
+ assume es:NOTHING
+
+ pop dword ptr es:[di].RealMode_DI
+ push bx
+ mov bx,ax
+ mov dword ptr es:[di].RealMode_BX,ebx
+ pop bx
+ mov dword ptr es:[di].RealMode_CX,ecx
+ mov dword ptr es:[di].RealMode_DX,edx
+ mov dword ptr es:[di].RealMode_SI,esi
+ mov dword ptr es:[di].RealMode_BP,ebp
+ mov ax,regUserAX
+ mov dword ptr es:[di].RealMode_AX,eax
+.286p
+ mov ax,regUserDS
+ mov es:[di].REalMode_DS,ax
+ mov ax,regUserES
+ mov es:[di].RealMode_ES,ax
+ mov dx,regUserSS ;keep real mode SS in DX
+ mov es:[di].RealMode_SS,dx
+ mov si,regUserSP ; and rMode SP in SI
+ mov es:[di].RealMode_SP,si
+ mov ax,regUserFL
+ mov es:[di].RealMode_Flags,ax
+
+
+; Map a selector to the real mode stack segment
+
+ call AllocateLDTSelector
+ jnc @f
+ mov ax,SEL_USERSCR or STD_TBL_RING ;use a scratch if can't alloc
+@@:
+ push ax ;save selector on our stack
+
+ push bx ;save CallBackTable entry pointer
+
+ call ParaToLinear ;convert SS in DX to BX:DX lma
+ mov cx,-1
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ pop bx
+
+; Build an IRET frame on the stack for the pMode routine to return to us
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz rmcbh10
+
+ pushfd
+ sub sp,2
+ push cs
+ push dword ptr (offset i31_CBret)
+ jmp rmcbh20
+.286p
+
+rmcbh10:
+ pushf
+ push cs
+ push offset i31_CBret
+
+rmcbh20:
+; Build a far ret frame to give control to user's Call-Back routine
+.386p
+ push dword ptr [bx].PM_CS_IP+4
+ push dword ptr [bx].PM_CS_IP
+
+; Do final register setups, and give control to pMode call-back routine
+
+ mov ds,ax ;DS:SI -> real mode stack
+ assume ds:NOTHING
+
+ public PMCallBack
+PMCallBack:
+ db 066h
+ retf ;and away we go...
+.286p
+; The pMode call-back routine returns here when finished
+
+i31_CBret:
+ cld ;take no chances
+
+ mov ds,selDgroupPM ;restore DGROUP addressability
+ assume ds:DGROUP
+
+ pop ax ;recover selector for RM stack seg
+ and al,SELECTOR_INDEX ;clear ring/table bits
+
+ cmp ax,SEL_USERSCR ;free the selector, unless it was
+ jz @f ; a scratch selector
+ call FreeSelector
+@@:
+
+; Copy the returned register Client frame to our stack so we can access
+; it from real mode
+
+ sub sp,size Real_Mode_Call_Struc+2
+ mov si,sp
+
+ push es ;ES:DI, DS:SI need to be reversed
+ mov es,selDgroupPM ; for rep movs
+ assume es:DGROUP
+ pop ds
+ assume ds:NOTHING
+
+ .386p
+ push ecx
+ xor ecx,ecx
+ .286p
+ mov cx,(size Real_Mode_Call_Struc) / 2
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz rmcbh80
+
+ push si
+ xor esi,esi
+ pop si
+ xchg esi,edi
+ rep movs word ptr [esi], word ptr [edi]
+ jmp rmcbh90
+.386p
+rmcbh80:
+ xchg si, di
+ rep movsw
+
+rmcbh90:
+ .386p
+ pop ecx
+ .286p
+ push es ;everybody assumes ds=dgroup
+ pop ds
+ assume ds:DGROUP
+
+; Switch to real mode, and switch to user specified stack
+
+ SwitchToRealMode ;leaves ints disabled
+ assume ds:DGROUP,es:DGROUP
+
+ mov bx,sp ;use bx as ptr to client reg frame
+
+ CHECK_STACK
+ mov ss,[bx].RealMode_SS
+ mov sp,[bx].RealMode_SP
+
+; Restore real mode registers as specified in client frame (all but DS & BX)
+
+.386p
+ mov eax,dword ptr [bx].RealMode_AX
+ mov ecx,dword ptr [bx].RealMode_CX
+ mov edx,dword ptr [bx].RealMode_DX
+ mov esi,dword ptr [bx].RealMode_SI
+ mov edi,dword ptr [bx].RealMode_DI
+ mov ebp,dword ptr [bx].RealMode_BP
+.286p
+ mov es,[bx].RealMode_ES
+ assume es:NOTHING
+
+; Build an IRET frame to return to specified CS:IP, and do it
+
+ push [bx].RealMode_Flags
+ push [bx].RealMode_CS
+ push [bx].RealMode_IP
+
+ push [bx].RealMode_DS ;last 2 regs to restore
+.386p
+ push dword ptr [bx].RealMode_BX
+ pop ebx
+.286p
+ add pbReflStack,2 * CB_STKFRAME ;deallocate our stack frame
+
+ pop ds
+
+ iret ;finished!
+
+RMCallBackHook endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+ subttl INT 31h Memory Management Services
+ page
+; -------------------------------------------------------
+; INT 31h MEMORY MANAGEMENT SERVICES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Service 05/00 -- Get Free Memory Information
+;
+; In: es:di -> 30h byte buffer
+; Out: es:di -> Largest free block (in bytes) placed at
+; start of caller's buffer
+; Rest of buffer filled in with -1.
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetFreeMem
+
+i31_GetFreeMem proc near
+ mov es,[bp].pmUserES ; Point to user buffer
+ mov di,[bp].pmUserDI
+ assume es:NOTHING
+
+
+ FBOP BOP_DPMI, GetMemoryInformation, FastBop
+ jmp i31_done
+
+i31_GetFreeMem endp
+
+
+
+; -------------------------------------------------------
+; Service 05/01 -- Allocate Memory Block
+;
+; In: bx:cx - size of block to allocate in bytes
+; Out: successful, carry clear &
+; bx:cx = linear memory address of block
+; si:di = handle to block
+; failed, carry set
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_AllocMem
+
+i31_AllocMem proc near
+
+ mov bx,[bp].pmUserBX
+ mov cx,[bp].pmUserCX
+ mov dx, selPSPChild
+
+ FBOP BOP_DPMI, AllocXmem, FastBop
+
+ jnc @f
+
+ jmp i31_fail_CY
+
+@@: mov [bp].intUserBX,bx
+ mov [bp].intUserCX,cx
+ mov [bp].intUserSI,si
+ mov [bp].intUserDI,di
+ jmp i31_Done
+
+i31_AllocMem endp
+
+
+; -------------------------------------------------------
+; Service 05/02 -- Free Memory Block
+;
+; In: si:di - 'handle' of block to free
+; Out: successful, carry clear
+; failed, carry set
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_FreeMem
+
+i31_FreeMem proc near
+
+ mov si,[bp].pmUserSI
+ mov di,[bp].pmUserDI
+
+ FBOP BOP_DPMI, FreeXmem, FastBop
+
+ jnc @f
+
+ jmp i31_fail_CY
+
+@@: jmp i31_Done
+
+i31_FreeMem endp
+
+
+; -------------------------------------------------------
+; Service 05/03 -- Resize Memory Block
+;
+; In: bx:cx - new size of block to allocate in bytes
+; si:di - 'handle' of block to free
+; Out: successful, carry clear &
+; bx:cx = linear memory address of block
+; si:di = handle to block
+; failed, carry set
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SizeMem
+
+i31_SizeMem proc near
+
+ mov bx,[bp].pmUserBX
+ mov cx,[bp].pmUserCX
+ mov si,[bp].pmUserSI
+ mov di,[bp].pmUserDI
+
+ FBOP BOP_DPMI, ReallocXmem, FastBop
+
+ jnc @f
+
+ jmp i31_fail_CY
+
+@@: mov [bp].intUserBX,bx
+ mov [bp].intUserCX,cx
+ mov [bp].intUserSI,si
+ mov [bp].intUserDI,di
+ jmp i31_Done
+
+i31_SizeMem endp
+
+
+; -------------------------------------------------------
+; Service 09/00 -- Get and Disable Virtual Interrupt State
+; 09/01 -- Get and Enable Virtual Interrupt State
+; 09/02 -- Get Virtual Interrupt State
+;
+; In: none
+; Out: AL = previous interrupt state
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_VirtualInt
+
+i31_VirtualInt proc near
+
+ mov ah,byte ptr [bp].intUserFL+1 ;get/isolate user's IF in AH
+ shr ah,1
+ and ah,1
+
+ cmp byte ptr [bp].intUserAX,Get_Int_State ;only getting state?
+ jz @f ; yes, skip set
+
+ mov al,byte ptr [bp].intUserAX ;get desired state
+ shl al,1 ; move into IF position
+
+ and byte ptr [bp].intUserFL+1,not 02h ;clr old IF bit
+ or byte ptr [bp].intUserFL+1,al ; set desired
+@@:
+ mov byte ptr [bp].intUserAX,ah ;return old state in user's AL
+
+ jmp i31_done
+
+i31_VirtualInt endp
+
+; -------------------------------------------------------
+ subttl INT 31h Utility Routines
+ page
+; -------------------------------------------------------
+; INT 31h UTILITY ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; i31_Version -- Return Int 31h version information.
+;
+; In: none
+; Out: ah - major version
+; al - minor version
+; bx - flags
+; cl - processor type
+; dh - master PIC base interrupt
+; dl - slave PIC bas interrupt
+;
+
+ public i31_Version
+ assume ds:DGROUP,es:DGROUP
+
+i31_Version proc near
+
+ mov [bp].intUserAX,I31VERSION
+ mov [bp].intUserBX,I31FLAGS
+ mov al,byte ptr idCpuType
+ mov byte ptr [bp].intUserCX,al
+ mov [bp].intUserDX,(I31MasterPIC SHL 8) OR I31SlavePIC
+
+ jmp i31_done
+
+i31_Version endp
+
+; -------------------------------------------------------
+; i31_MemGetHeader -- Get selector to our header on a Int 31h allocated
+; DOS memory block.
+;
+; In: DX - SELECTOR of block to get header of
+; Out: ES - selector pointing to our header for block
+; Uses: none
+
+ public i31_MemGetHeader
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+i31_MemGetHeader proc near
+
+; User wants to release block pointed to by selector in DX. Use a scratch
+; selector to point 1 paragraph before that to make sure this is a block
+; we allocated, and get some misc info
+
+ push ax
+ push bx
+ push cx
+ push dx
+
+ mov ax,dx
+ call GetSegmentAddress ;BX:DX -> user's data area
+
+ sub dx,10h ;backup one paragraph
+ sbb bx,0
+
+ mov cx,0fh
+ mov ax,SEL_SCR0 or STD_TBL_RING
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ mov es,ax
+
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+i31_MemGetHeader endp
+
+; -------------------------------------------------------
+; RZCall -- Utility routine to call a Ring
+; Zero procedure. Stack parameter is the NEAR
+; address of the routine in the DXPMCODE segment to
+; call. The called routine must be declared FAR
+; and take no stack parameters.
+;
+; USES: Whatever Ring 0 routine uses
+; +Flags
+; RETURNS: Whatever Ring 0 routine returns
+;
+; NOTE: Assumes that interrupts must be disabled
+; for the Ring 0 routine.
+;
+; History:
+; 12-Feb-1991 -- ERH wrote it!!!
+; -------------------------------------------------------
+
+My_Call_Gate dd (SEL_SCR0 or STD_TBL_RING) shl 10h
+
+ public RZCall
+RZCall proc near
+
+ pushf
+ FCLI
+ push bp
+ mov bp,sp
+ cCall NSetSegmentDscr,<SEL_SCR0,0,SEL_EH,0,[bp+6],STD_CALL>
+ pop bp
+
+ call dword ptr My_Call_Gate
+
+ cCall NSetSegmentDscr,<SEL_SCR0,0,0,0,-1,STD_DATA>
+
+ npopf
+
+ retn 2
+
+RZCall endp
+
+ife NO386
+
+i31_Debug_Register_Access proc near
+
+ or byte ptr [bp].intUserFL,1 ;set carry flag
+ cmp idCpuType,3 ;at least 386?
+ jb i31_Debug_Register_Access_Done ;No.
+
+IFNDEF WOW_x86
+ push offset DXPMCODE:I31_Win386_Call
+ call RZCall
+ELSE
+ call far ptr I31_Win386_Call
+ENDIF
+i31_Debug_Register_Access_Done:
+ jmp i31_exit
+
+i31_Debug_Register_Access endp
+
+ .386
+
+;******************************************************************************
+;
+; I31_Win386_Call
+;
+; The routine to implement the INT 31h debug register functions is copied
+; directly from the WIN386 routine in vmm/int31.asm. This routine sets
+; up the 386 registers so that the actual API is callable from the 16-bit
+; DOS Extender.
+;
+;******************************************************************************
+I31_Win386_Call proc far ; Called via ring-transition call gate
+;
+; Save 386 extended registers that are used by the WIN386 code.
+;
+ pushad
+;
+; Call the actual routine
+;
+ movzx ebp,bp
+
+ call I31_Debug_Register_Support
+;
+; Restore 386 extended registers that are used by the WIN386 code.
+;
+ popad
+ ret
+
+I31_Win386_Call endp
+
+DXPMCODE ends
+
+;******************************************************************************
+; R E C O R D S
+;******************************************************************************
+
+;
+; The following record defines the debug status register.
+;
+DBG6 RECORD d6_r1:16,d6_bt:1,d6_bs:1,d6_bd:1,d6_r2:9,d6_b3:1,d6_b2:1,d6_b1:1,d6_b0:1
+DBG6_RESERVED EQU (MASK d6_r1) OR (MASK D6_r2)
+
+;
+; The following record defines the debug control register.
+;
+DBG7 RECORD d7_ln3:2,d7_rw3:2,d7_ln2:2,d7_rw2:2,d7_ln1:2,d7_rw1:2,d7_ln0:2,d7_rw0:2,d7_rs:6,d7_ge:1,d7_le:1,d7_g3:1,d7_l3:1,d7_g2:1,d7_l2:1,d7_g1:1,d7_l1:1,d7_g0:1,d7_l0:1
+DBG7_RESERVED EQU (MASK d7_rs)
+
+Num_Watchpoints EQU 4 ; Only 4 live watchpoints in 386, 486.
+
+;******************************************************************************
+; m a c r o s
+;******************************************************************************
+
+;******************************************************************************
+;
+; The following set of macros allows us to copy WIN386 code directly
+;
+;******************************************************************************
+
+BeginProc macro fname
+ fname proc near
+endm
+
+EndProc macro fname
+ fname endp
+endm
+
+Assert_Client_Ptr macro dummy
+endm
+
+Client_Flags equ intUserFL
+Client_EAX equ intUserAX
+Client_AX equ intUserAX
+Client_BX equ intUserBX
+Client_CX equ intUserCX
+Client_DX equ intUserDX
+
+CF_MASK equ 1
+
+OFFSET32 equ <OFFSET>
+
+
+
+;******************************************************************************
+; d a t a
+;******************************************************************************
+
+DXDATA segment
+
+PUBLIC Debug_Regs
+;
+; A memory image of the debug registers. The first version assumes
+; that we have complete control over the debug registers, so only
+; one copy is needed.
+;
+ ALIGN DWORD
+Debug_Regs LABEL DWORD
+DD_DR0 dd 0
+DD_DR1 dd 0
+DD_DR2 dd 0
+DD_DR3 dd 0
+
+DD_DR6 dd 0
+DD_DR7 dd 0
+
+DXDATA ends
+
+ .386p
+
+DXPMCODE segment
+
+;******************************************************************************
+;
+; I31_Debug_Register_Support
+;
+; ENTRY: AL = Function code
+; 01 -- Set debug watchpoint
+; BX:CX = linear address of watchpoint
+; DL = size of watchpoint (1, 2, or 4)
+; DH = type of watchpoint
+; 0 = Execute
+; 1 = Write
+; 2 = Read/Write
+;
+; Returns
+; BX = debug watchpoint handle
+;
+; 02 -- Clear debug watchpoint
+;
+; 03 -- Get state of debug watchpoint
+; Returns
+; AX = bit 0 set if watch point has been executed
+;
+; 04 -- Reset debug watchpoint
+;
+; EXIT: Carry clear in client flags if successful
+; Carry set in client flags if not
+;
+; USES: EDI, EAX, ECX, EDX, ESI
+;
+; History:
+; 08-Feb-1991 -- ERH wrote it.
+;
+;******************************************************************************
+BeginProc I31_Debug_Register_Support
+
+IFNDEF WOW_x86
+ call Store_DBG_Regs ; make copy of debug regs.
+ENDIF
+
+ test [DD_DR6],MASK d6_bd
+ jnz DD_I31_Error ; ICE-386 is active!
+ and [ebp.Client_Flags],NOT CF_MASK
+
+ mov eax, dword ptr [ebp.Client_EAX]
+ cmp al, 03h
+ ja DD_I31_Error
+ je DD_I31_Reset_Watchpoint
+ cmp al, 01h
+ ja DD_I31_Get_Status
+ je DD_I31_Clear_Watchpoint
+
+;
+; Function AX = 0A00h -- Set Debug Watchpoint
+;
+PUBLIC DD_I31_Set_Watchpoint
+DD_I31_Set_Watchpoint:
+ xor ecx, ecx
+ mov edx,(MASK d7_l0) OR (MASK d7_g0); EDX = DR0 enable bits
+DD_I31_Search_Loop:
+;
+; Look for an unused breakpoint. In order for a breakpoint to be
+; unused, the corresponding global and local enable bits must be clear.
+;
+ test [DD_DR7],edx
+ jz SHORT DD_I31_SW_Found_One
+DD_I31_SkipIt:
+ shl edx,2 ; EDX = next BP's enable bits
+ inc ecx
+ cmp ecx, Num_Watchpoints
+ jb DD_I31_Search_Loop
+ jmp DD_I31_Error
+
+DD_I31_SW_Found_One:
+ mov esi, OFFSET32 Debug_Regs
+ mov eax,ecx
+ shl eax,2
+ add esi,eax ; ESI -> BP address buffer
+ mov ax, [ebp.Client_BX]
+ shl eax, 16
+ mov ax, [ebp.Client_CX] ; EAX = linear address
+ mov dword ptr [esi],eax ; record address
+ or [DD_DR7],edx ; enable the break point
+
+ mov edx,NOT((MASK d7_ln0) OR (MASK d7_rw0))
+ shl cl,2
+ rol edx,cl
+ shr cl,2
+ and [DD_DR7],edx ; clear type and size bits
+;
+; Client_DX =
+; DH = 0 : execute
+; DH = 1 : write
+; DH = 2 : read/write
+;
+; DL = 1 : byte
+; DL = 2 : word
+; DL = 4 : dword
+;
+ movzx edx,[ebp.Client_DX] ; load BP size and type
+ cmp dh,0 ; execute?
+ jne SHORT @F ; no
+ mov dl,1 ; yes, force size zero
+@@:
+
+ cmp dl,4 ; check for valid values
+ ja DD_I31_Error ; size in dl is 1, 2, or 4
+ cmp dl,3
+ je DD_I31_Error
+ dec dl ; DL = 0, 1, or 3 for DR7
+ js DD_I31_Error ; len field
+
+ cmp dh,2 ; type in dh is 0, 1, or 2
+ ja DD_I31_Error
+
+ jne SHORT @F
+ inc dh ; 386 wants 3, not 2
+@@: ; DH = RWn field
+ shl dl,2
+ or dl,dh
+ xor dh,dh
+ shl edx,d7_rw0
+ shl cl,2
+ shl edx,cl
+ or [DD_DR7],edx ; set size, type
+ shr cl,2
+
+ mov edx,NOT (MASK d6_b0) ; clear triggered bit
+ rol edx,cl ; EDX = mask to clear hit
+ and [DD_DR6],edx ; clear triggered bit
+ mov [ebp.Client_BX],cx ; return address register
+ ; number as BP handle
+ or [DD_DR7],(MASK d7_ge) OR (MASK d7_le)
+ ; enable debugging
+ call Load_DBG_Regs ; load changes into debug registers
+ ret
+;
+; Function AX = 0A01h -- Clear Debug Watchpoint
+;
+; Error if Watchpoint not previously set. In that case, do nothing.
+; If Watchpoint was set, then clear enable bits, triggered bit, and
+; breakpoint address.
+;
+PUBLIC DD_I31_Clear_Watchpoint
+DD_I31_Clear_Watchpoint:
+ movzx ecx,[ebp.Client_BX]
+ cmp ecx, Num_Watchpoints
+ jnb DD_I31_Error
+
+ mov edx,(MASK d7_l0) OR (MASK d7_g0); EDX = enable DR0 mask
+ shl edx,cl
+ shl edx,cl ; EDX = enable DRn mask
+ test [DD_DR7],edx ; BP set?
+ jz DD_I31_Error ; No, error.
+ not edx
+ and [DD_DR7],edx ; disable the BP
+ mov edx,NOT (MASK d6_b0) ; EDX = DR0 not hit
+ rol edx,cl ; EDX = DRn not hit
+ and [DD_DR6],edx ; clear triggered bit
+ mov esi, OFFSET32 Debug_Regs ; ESI -> DRn table
+ shl ecx,2 ; ECX = DWORD offset
+ add esi,ecx ; ESI -> DRn
+ mov dword ptr [esi],0 ; clear address
+ mov edx,NOT((MASK d7_ln0) OR (MASK d7_rw0))
+ rol edx,cl
+ and [DD_DR7],edx
+;
+; Test whether this leaves any breakpoints active. If not, disable
+; the exact match condition. Note: the following is a long line.
+;
+ test [DD_DR7],(MASK d7_g3) OR (MASK d7_l3) OR (MASK d7_g2) OR (MASK d7_l2) OR (MASK d7_g1) OR (MASK d7_l1) OR (MASK d7_g0) OR (MASK d7_l0)
+ jne SHORT @F
+ and [DD_DR7],NOT ((MASK d7_ge) OR (MASK d7_le))
+@@:
+ call Load_DBG_Regs ; load changes into debug registers
+ ret
+;
+; Function AX = 0A02h -- Get Status of Debug Watchpoint
+;
+PUBLIC DD_I31_Get_Status
+DD_I31_Get_Status:
+ movzx ecx,[ebp.Client_BX]
+ cmp ecx, Num_Watchpoints
+ jnb SHORT DD_I31_Error
+
+ mov edx,(MASK d7_g0) OR (MASK d7_l0); EDX = DR0 enable bits
+ shl edx,cl
+ shl edx,cl ; EDX = DRn enable bits
+ test [DD_DR7],edx ; DRn enabled?
+ jz SHORT DD_I31_Error ; No, error.
+ mov edx,MASK d6_b0 ; EDX = DR0 hit mask
+ shl edx,cl ; EDX = DRn hit mask
+ xor eax,eax
+ test [DD_DR6],edx ; DRn hit?
+ jne SHORT @F ; no
+ inc al ; yes, store result
+@@:
+ mov [ebp.Client_AX],ax
+ ret
+;
+; Function AX = 0A03h -- Reset Debug Watchpoint
+;
+PUBLIC DD_I31_Reset_Watchpoint
+DD_I31_Reset_Watchpoint:
+ movzx ecx,[ebp.Client_BX]
+ cmp ecx, Num_Watchpoints
+ jnb SHORT DD_I31_Error
+
+ mov edx,(MASK d7_g0) OR (MASK d7_l0); EDX = DR0 enable bits
+ shl edx,cl
+ shl edx,cl ; EDX = DRn enable bits
+ test [DD_DR7],edx ; DRn enabled?
+ jz SHORT DD_I31_Error ; No, error.
+ mov edx,NOT (MASK d6_b0) ; EDX = DR0 hit mask
+ rol edx,cl ; EDX = DRn hit mask
+ and [DD_DR6],edx ; clear triggered bit
+ call Load_DBG_Regs ; load changes into debug registers
+ ret
+
+DD_I31_Error:
+ Assert_Client_Ptr ebp
+ or [ebp.Client_Flags], CF_Mask
+ ret
+
+EndProc I31_Debug_Register_Support
+
+
+;******************************************************************************
+;
+; Load_DBG_Regs - Load debug registers from memory.
+; Do not change undefined bits.
+;
+; ENTRY: NONE
+; EXIT: Memory image copied to debug registers, undefined bits unchanged.
+; USES: eax, ecx, edi, esi
+;
+;******************************************************************************
+
+BeginProc Load_DBG_Regs
+
+ mov esi,OFFSET32 Debug_Regs
+ DPMIBOP SetDebugRegisters
+ jnc short ldr_exit
+
+ cld
+ xor eax, eax
+ mov ecx, 6
+ mov edi, OFFSET32 Debug_Regs
+ rep stosd ;clear local copy
+ldr_exit:
+ ret
+
+EndProc Load_DBG_Regs
+
+; -------------------------------------------------------
+
+IFNDEF WOW_x86
+;******************************************************************************
+;
+; Load_DBG_Regs - Load debug registers from memory.
+; Do not change undefined bits.
+;
+; ENTRY: NONE
+; EXIT: Memory image copied to debug registers, undefined bits unchanged.
+; USES: NONE
+;
+;******************************************************************************
+
+BeginProc Load_DBG_Regs
+
+ push esi
+ push edx
+ push eax
+
+ cld
+
+ mov esi,OFFSET32 Debug_Regs
+ lods dword ptr ds:[esi]
+ mov dr0, eax
+ lods dword ptr ds:[esi]
+ mov dr1, eax
+ lods dword ptr ds:[esi]
+ mov dr2, eax
+ lods dword ptr ds:[esi]
+ mov dr3, eax
+ lods dword ptr ds:[esi]
+ and eax,NOT DBG6_RESERVED
+ mov edx,dr6
+ and edx,DBG6_RESERVED
+ or eax,edx
+ mov dr6, eax
+.ERRNZ dd_dr6 - dd_dr3 - 4
+ lods dword ptr ds:[esi]
+ and eax,NOT DBG7_RESERVED
+ mov edx,dr7
+ and edx,DBG7_RESERVED
+ or eax,edx
+ mov dr7, eax
+
+ pop eax
+ pop edx
+ pop esi
+
+ ret
+
+EndProc Load_DBG_Regs
+
+;******************************************************************************
+;
+; Store_DBG_Regs - Copy debug registers to memory.
+;
+; ENTRY: NONE
+; EXIT: Debug registers copied to memory image.
+; Undefined bits = don't care.
+; USES: NONE
+;
+;******************************************************************************
+
+BeginProc Store_DBG_Regs
+
+ push eax
+ push edi
+
+ cld
+
+ mov edi,OFFSET32 Debug_Regs
+ mov eax, dr0
+ stos dword ptr es:[edi]
+ mov eax, dr1
+ stos dword ptr es:[edi]
+ mov eax, dr2
+ stos dword ptr es:[edi]
+ mov eax, dr3
+ stos dword ptr es:[edi]
+ mov eax, dr6
+.ERRNZ dd_dr6 - dd_dr3 - 4
+ stos dword ptr es:[edi]
+ mov eax, dr7
+ stos dword ptr es:[edi]
+
+ pop edi
+ pop eax
+
+ ret
+
+EndProc Store_DBG_Regs
+ENDIF ; WOW_x86
+
+; -------------------------------------------------------
+endif ; NO386
+
+
+; -------------------------------------------------------
+ subttl INT 31h Raw Modeswitch Routines
+ page
+; -------------------------------------------------------
+; INT 31h Raw Modeswitch Routines
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; i31_GetSaveRestoreState -- Return Int 31h Save/Restore State addresses.
+;
+; In: none
+; Out: ax - size of buffer required to save state
+; bx:cx - real mode address used to save/restore state
+; si:di - protected mode address used to save/restore state
+;
+
+ public i31_GetStateSaveRestore
+ assume ds:DGROUP,es:DGROUP
+
+i31_GetStateSaveRestore proc near
+
+ mov [bp].intUserAX,0
+ push es
+ push SEL_DXCODE OR STD_RING
+ pop es
+ assume es:DXCODE
+ mov ax,segDXCode
+ pop es
+ assume es:DGROUP
+ mov [bp].intUserBX,ax
+ mov [bp].intUserCX,offset DXCODE:RmSaveRestoreState
+ mov [bp].intUserSI,SEL_DXPMCODE OR STD_RING
+ test DpmiFlags,DPMI_32BIT
+ jz gssr10
+
+ mov edi,dword ptr (offset DXPMCODE:PmSaveRestoreState)
+gssr10: mov [bp].intUserDI,offset DXPMCODE:PmSaveRestoreState
+
+ jmp i31_done
+
+i31_GetStateSaveRestore endp
+
+; -------------------------------------------------------
+; i31_GetRawModeSwitch -- Return Int 31h Save/Restore State addresses.
+;
+; In: none
+; Out: bx:cx - real -> protected mode switch address
+; si:di - protected -> real mode switch address
+;
+
+ public i31_GetRawModeSwitch
+ assume ds:DGROUP,es:DGROUP
+
+i31_GetRawModeSwitch proc near
+
+ push es
+ push SEL_DXCODE OR STD_RING
+ pop es
+ assume es:DXCODE
+ mov ax,segDXCode
+ pop es
+ assume es:DGROUP
+ mov [bp].intUserBX,ax
+ mov [bp].intUserCX,offset DXCODE:RmRawModeSwitch
+ mov [bp].intUserSI,SEL_DXPMCODE OR STD_RING
+ test DpmiFlags,DPMI_32BIT
+ jz grms10
+
+ mov edi,dword ptr (offset DXPMCODE:PmRawModeSwitch)
+grms10: mov [bp].intUserDI,offset DXPMCODE:PmRawModeSwitch
+
+ jmp i31_done
+
+i31_GetRawModeSwitch endp
+
+
+;
+; make sure 286 protect mode else code generated for mips will be wrong.
+;
+ .286p
+
+; -------------------------------------------------------
+; Service 04/f1 - Allocate Space in LDT for Selector (WOW only)
+; Don't initialize the descriptor to Zero.
+; in: cx - # selectors required
+; out: ax - *index* of first selector
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_WOW_AllocSel
+
+i31_WOW_AllocSel proc near
+
+ cmp cx,1 ;1 selector or more?
+ ja @f
+
+ call AllocateSelector ;allocate 1 selector
+ jc i31_WOW_15
+ jmp short i31_WOW_10
+
+@@: mov ax,cx ;WOW, cx != 0, allocate from higher range
+ call AllocateSelectorBlock ;allocate a block of selectors
+ jc i31_WOW_15
+
+i31_WOW_10:
+ or al,STD_TBL_RING ;add standard table/ring bits
+ mov [bp].intUserAX,ax ;return 1st/only selector in AX
+
+ jmp i31_done
+
+i31_WOW_15:
+ jmp i31_fail ;fail the request
+
+i31_WOW_AllocSel endp
+
+
+; -------------------------------------------------------
+; Service 04/f2 - Set Descriptor (WOW only)
+; in: bx - selector
+; cx - number of contiguous selectors
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_WOW_SetDescriptor
+
+i31_WOW_SetDescriptor proc near
+
+ mov ax,selGDT
+ mov es,ax
+ assume es:NOTHING
+ lsl ax,ax
+
+ and bx,SELECTOR_INDEX ; input selector
+ cmp bx,ax ; range-test selector against
+ jc @F ; the limit of the GDT/LDT
+ jmp i31_fail_CY
+
+@@:
+ cCall NWOWSetDescriptor,<cx,es,bx>
+ jmp i31_done
+
+i31_WOW_SetDescriptor endp
+
+; -------------------------------------------------------
+; Service 04/f3 - Set Descriptor (WOW only)
+; in: bx:dx -- pointer to low memory allocation routine
+; si:di -- pointer to low memory free routine
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_WOW_SetAllocFunctions
+
+i31_WOW_SetAllocFunctions proc near
+
+ mov word ptr [LowMemAllocFn],dx
+ mov word ptr [LowMemAllocFn + 2],bx
+ mov word ptr [LowMemFreeFn],di
+ mov word ptr [LowMemFreeFn + 2], si
+ jmp i31_done
+
+i31_WOW_SetAllocFunctions endp
+
+DXPMCODE ends
+
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/486/dxintr.asm b/private/mvdm/dpmi/486/dxintr.asm
new file mode 100644
index 000000000..974266e52
--- /dev/null
+++ b/private/mvdm/dpmi/486/dxintr.asm
@@ -0,0 +1,3194 @@
+ PAGE ,132
+ TITLE DXINTR.ASM -- Dos Extender Interrupt Reflector
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXINTR.ASM - Dos Extender Interrupt Reflector *
+;* *
+;****************************************************************
+;* *
+;* Revision History: *
+;* *
+;* *
+;* 09/13/90 earleh Fault handlers Ring 0 *
+;* 09/06/90 earleh Fault handlers DPMI compliant *
+;* PIC remapping no longer required *
+;* 08/08/90 earleh DOSX and client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 05/09/90 jimmat Started VCPI changes. *
+;* 04/02/90 jimmat Added PM Int 70h handler. *
+;* 01/08/90 jimmat Don't allow nested PS/2 mouse interrupts *
+;* (later removed!) *
+;* 09/15/89 jimmat Support for 'Classic' HP Vectras which *
+;* have 3 8259 interrupt controllers *
+;* 07/28/89 jimmat Save A20 state when reflecting an int to *
+;* protected mode, removed Int 30h handler *
+;* that did code patch-ups, point debugger *
+;* to faulting instruction, not Int 3. *
+;* 07/13/89 jimmat Improved termination due to faults when *
+;* not running under a debugger--also ifdef'd *
+;* out code to dynamically fixup code seg *
+;* references on GP faults *
+;* 06/05/89 jimmat Ints 0h-1Fh are now vectored through a 2nd *
+;* table. This allows Wdeb386 interaction *
+;* more like Windows/386. *
+;* 05/23/89 jimmat Added wParam & lParam to interrupt frame. *
+;* 05/07/89 jimmat Added XMScontrol function to map protected *
+;* mode XMS requests to real mode driver. *
+;* 05/02/89 jimmat 8259 interrupt mask saved around changing *
+;* of hardware interrupt base *
+;* 04/24/89 jimmat Added support for PS/2 Int 15h/C2h/07 Set *
+;* Pointing Device Handler Address function *
+;* 04/12/89 jimmat Added PMIntr24 routine to support PM *
+;* Critical Error Handlers *
+;* 03/15/89 jimmat Added INT 31h LDT/heap interface a la *
+;* Windows/386 *
+;* 03/14/89 jimmat Changes to run child in ring 1 with LDT *
+;* 02/24/89 (GeneA): fixed problem in IntEntryVideo and *
+;* IntExitVideo for processing function 10h subfunction *
+;* for reading and writing the VGA palette. *
+;* 02/22/89 (GeneA): added handlers for Int 10h, Int 15h, and *
+;* Int 33h. Added support for more general mechanism for *
+;* handling interrupts require special servicing and *
+;* allowing nesting of these interrupts. Allocation and *
+;* deallocation of stack frames is supported to allow *
+;* nested paths through the interrupt reflection code to *
+;* a depth of 8. *
+;* There is still a problem that if an interrupt handler *
+;* is using a static buffer to transfer data, another *
+;* interrupt that uses the same static buffer could come *
+;* in and trash it. Solving the problem in a completely *
+;* general way would require having a buffer allocation *
+;* deallocation scheme for doing the transfers between *
+;* real mode memory and protected mode memory. *
+;* 02/14/89 (GeneA): added code in TrapGP to print error msg *
+;* and quit when running a non-debugging version. *
+;* 02/10/89 (GeneA): changed Dos Extender from small model to *
+;* medium model. Added function LoaderTrap to handle *
+;* loader interrupts when the program contains overlays. *
+;* 11/20/88 (GeneA): changed both RM and PM interrupt reflector*
+;* routines to pass the flags returned by the ISR back to *
+;* the originator of the interrupt, rather than returning *
+;* the original flags. *
+;* 10/28/88 (GeneA): created *
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;* *
+;****************************************************************
+
+ .286p
+ .287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include interupt.inc
+if VCPI
+include dxvcpi.inc
+endif
+IFDEF ROM
+include dxrom.inc
+ENDIF
+ifdef WOW_x86
+include vdmtib.inc
+endif
+ .list
+include intmac.inc
+include stackchk.inc
+include bop.inc
+include dpmi.inc
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+DS_ForcedGO equ 0F003h ;Wdeb386 go with breakpoint command
+DebOut_Int equ 41h ;Wdeb386 pMode interface Interrupt
+
+; Offsets to fields in DOSX header for DOS allocated memory blocks
+
+MemCookie equ 0
+MemSelector equ 2
+MemSegment equ 4
+MemParas equ 6
+MemSelCount equ 8
+
+MemGoodCookie equ 'SF' ;memory header magic cookie value
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn PMIntr13:NEAR
+ extrn PmIntrDos:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn ParaToLinear:NEAR
+externFP NSetSegmentDscr
+ extrn GetSegmentAddress:NEAR
+ extrn DupSegmentDscr:NEAR
+ extrn ParaToLDTSelector:NEAR
+ extrn FreeSelector:NEAR
+ extrn FreeSelectorBlock:NEAR
+ extrn AllocateSelector:NEAR
+ extrn AllocateSelectorBlock:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn fDebug:BYTE
+ extrn selIDT:WORD
+ extrn pmusrss:WORD
+ extrn pmusrsp:WORD
+ifdef NOT_NTVDM_NOT
+ extrn fHPVectra:BYTE
+endif
+ extrn idCpuType:WORD
+ extrn npXfrBuf0:WORD
+ extrn npXfrBuf1:WORD
+ extrn rgbXfrBuf0:BYTE
+ extrn rgbXfrBuf1:BYTE
+ extrn selPSPChild:WORD
+ extrn fFaultAbort:BYTE
+ extrn lpfnXMSFunc:DWORD
+ extrn Int28Filter:WORD
+ extrn A20EnableCount:WORD
+
+if DEBUG
+ extrn fTraceReflect:WORD
+endif
+
+if VCPI
+ extrn fVCPI:BYTE
+endif
+
+IFDEF ROM
+ extrn segDXData:WORD
+ extrn segDXCode:WORD
+ENDIF
+ extrn DpmiFlags:WORD
+
+;
+; Variables used to store register values while mode switching.
+
+ public regUserSS, regUserSP, regUserFL, regUserAX, regUserDS
+ public regUserES
+
+regUserSS dw ?
+regUserSP dw ?
+regUserCS dw ?
+regUserIP dw ?
+regUserFL dw ?
+regUserAX dw ?
+regUserDS dw ?
+regUserES dw ?
+pfnReturnAddr dw ?
+
+Int28Count dw -1 ;Count of idle Int 28h's not reflected to RM
+
+;
+; Far pointer to the user's mouse callback function.
+
+ public lpfnUserMouseHandler
+
+lpfnUserMouseHandler dd 0 ;Entry point to the users mouse handler
+cbMouseState dw 0 ;size of mouse state buffer in bytes
+
+
+; Far pointer to PS/2 Pointing device handler address
+
+ public lpfnUserPointingHandler
+
+lpfnUserPointingHandler dd 0 ;Sel:Off to user's handler
+
+ public PMInt24Handler
+
+PMInt24Handler dd 0 ;Address of protect mode Int 24h handler
+ dd 0 ; other half
+
+ align 2
+
+if DEBUG
+ extrn StackGuard:WORD
+endif
+ extrn pbReflStack:WORD
+ extrn bReflStack:WORD
+;
+; This buffer contains the original real mode interrupt vectors. The
+; PM->RM interrupt reflector uses the addresses in this vector as the
+; address to receive control when it reflects an interrupt to real mode.
+
+ public rglpfnRmISR
+
+ align 2
+rglpfnRmISR dd 256 dup (?)
+
+;
+; This buffer contains the real mode hardware interrupt vectors.
+; If a hardware interrupt is hooked in protected mode, a reflector
+; is put into the IVT. If we were trying to reflect down the real
+; mode chain, we call these handlers if the IVT contains a reflector
+;
+ public RmHwIsr
+RmHwIsr dd 256 dup(0)
+
+;
+; This flag indicates if the hardware interrupts have been remapped.
+ public fHardwareIntMoved
+fHardwareIntMoved db 0
+
+ifdef SEG_FIXUP ;-----------------------------------------------
+
+errGP dw ? ;this variable is used to hold the error
+
+endif ;SEG_FIXUP ------------------------------------------------
+
+
+; PMFaultVector is a table of selector:offsets for routines to process
+; protected mode processor faults/traps/exceptions. If we don't handle
+; the exception as an exception, we vector it through PMReservedEntryVector.
+
+FltRtn macro off
+ dw DXPMCODE:off
+ dw 0
+ dw SEL_DXPMCODE or STD_RING
+ dw 0
+ endm
+ public PMFaultVector
+
+ align 4
+
+PMFaultVector label DWORD
+ FltRtn PMFaultEntryVector+5*0h ; int 0
+ FltRtn PMFaultEntryVector+5*1h ; int 1
+ FltRtn PMFaultEntryVector+5*2h ; int 2
+ FltRtn PMFaultEntryVector+5*3h ; int 3
+ FltRtn PMFaultEntryVector+5*4h ; int 4
+ FltRtn PMFaultEntryVector+5*5h ; int 5
+ FltRtn PMFaultEntryVector+5*6h ; int 6
+ FltRtn PMFaultEntryVector+5*7h ; int 7
+ FltRtn PMFaultEntryVector+5*8h ; int 8
+ FltRtn PMFaultEntryVector+5*9h ; int 9
+ FltRtn PMFaultEntryVector+5*0Ah ; int a
+ FltRtn PMFaultEntryVector+5*0Bh ; int b
+ FltRtn PMFaultEntryVector+5*0Ch ; int c
+ FltRtn PMFaultEntryVector+5*0Dh ; int d
+ FltRtn PMFaultEntryVector+5*0Eh ; int e
+ FltRtn PMFaultEntryVector+5*0Fh ; int f
+ FltRtn PMFaultEntryVector+5*10h ; int 10h
+ FltRtn PMFaultEntryVector+5*11h ; int 11h
+ FltRtn PMFaultEntryVector+5*12h ; int 12h
+ FltRtn PMFaultEntryVector+5*13h ; int 13h
+ FltRtn PMFaultEntryVector+5*14h ; int 14h
+ FltRtn PMFaultEntryVector+5*15h ; int 15h
+ FltRtn PMFaultEntryVector+5*16h ; int 16h
+ FltRtn PMFaultEntryVector+5*17h ; int 17h
+ FltRtn PMFaultEntryVector+5*18h ; int 18h
+ FltRtn PMFaultEntryVector+5*19h ; int 19h
+ FltRtn PMFaultEntryVector+5*1Ah ; int 1ah
+ FltRtn PMFaultEntryVector+5*1Bh ; int 1bh
+ FltRtn PMFaultEntryVector+5*1Ch ; int 1ch
+ FltRtn PMFaultEntryVector+5*1Dh ; int 1Dh
+ FltRtn PMFaultEntryVector+5*1Eh ; int 1Eh
+ FltRtn PMFaultEntryVector+5*1Fh ; int 1Fh
+
+ifdef OVERLAY_SUPPORT ;-----------------------------------------------
+
+offDestination dw ?
+selDestination dw ?
+
+endif ;---------------------------------------------------------------
+
+
+; if DEBUG ;------------------------------------------------------------
+; For MIPS we need to see where we are faulting - remove for final release
+; LATER
+
+; extrn fA20:BYTE
+; extrn fTraceFault:WORD
+
+ public PMIntNo
+PMIntNo dw 0
+
+szRegDump db 'AX=#### BX=#### CX=#### DX=#### SI=#### DI=#### BP=####',0dh,0ah
+ db 'DS=#### ES=#### EC=#### CS=#### IP=#### SS=#### SP=####',0dh,0ah
+ db '$'
+
+; endif ;DEBUG --------------------------------------------------------
+
+ extrn rgwStack:word
+ extrn npEHStackLimit:word
+ extrn npEHStacklet:word
+ extrn selEHStack:word
+IFDEF WOW
+ public WowTransitionToUserMode
+ public Wow16BitHandlers
+WowTransitionToUserMode dw offset DXPMCODE:Wow16TransitionToUserMode
+Wow16BitHandlers dw 256 dup (0,0)
+
+ENDIF
+
+DXDATA ends
+
+
+DXSTACK segment
+
+ public rgw0Stack, rgw2FStack
+
+ dw 64 dup (?) ; INT 2Fh handler stack
+
+rgw2FStack label word
+
+ dw 64 dup (?) ; DOSX Ring -> Ring 0 transition stack
+;
+; Interrupts in the range 0-1fh cause a ring transition and leave
+; an outer ring IRET frame right here.
+;
+Ring0_EH_DS dw ? ; place to put user DS
+Ring0_EH_AX dw ? ; place to put user AX
+Ring0_EH_BX dw ? ; place to put user BX
+Ring0_EH_CX dw ? ; place to put user CX
+Ring0_EH_BP dw ? ; place to put user BP
+Ring0_EH_PEC dw ? ; lsw of error code for 386 page fault
+ ; also near return to PMFaultEntryVector
+Ring0_EH_EC dw ? ; error code passed to EH
+Ring0_EH_IP dw ? ; interrupted code IP
+Ring0_EH_EIP dw ? ; high half eip
+Ring0_EH_CS dw ? ; interrupted code CS
+ dw ? ; high half of cs
+Ring0_EH_Flags dw ? ; interrupted code flags
+Ring0_EH_EFlags dw ? ; high half of flags
+Ring0_EH_SP dw ? ; interrupted code SP
+Rin0_EH_ESP dw ? ; high half of esp
+Ring0_EH_SS dw ? ; interrupted code SS
+ dw ? ; high half of ss
+rgw0Stack label word
+
+ dw 64 dup (?) ; stack for switching to ring0
+ public ResetStack
+ResetStack label word
+ifdef WOW_x86
+ dw 64 dup (?) ; wow stack for initial int field
+ public rgwWowStack
+rgwWowStack label word
+endif
+
+DXSTACK ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn selDgroup:WORD
+ENDIF
+
+DXCODE ends
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+ extrn segDXCodePM:WORD
+ extrn szFaultMessage:BYTE
+ extrn szRing0FaultMessage:BYTE
+ extrn RZCall:NEAR
+
+IFNDEF ROM
+ extrn segDXDataPM:WORD
+ENDIF
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ page
+ subttl Protected Mode Interrupt Reflector
+; -------------------------------------------------------
+; PROTECTED MODE INTERRUPT REFLECTOR
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+; -------------------------------------------------------
+; PMIntrEntryVector -- This table contains a vector of
+; near jump instructions to the protected mode interrupt
+; reflector. The protected mode interrupt descriptor
+; table is initialized so that all interrupts jump to
+; locations in this table, which transfers control to
+; the interrupt reflection code for reflecting the
+; interrupt to real mode.
+
+ public PMIntrEntryVector
+
+PMIntrEntryVector:
+
+ rept 256
+ call PMIntrReflector
+ endm
+
+
+StartFaultBopTable macro
+ ?fault = 0
+ endm
+
+FaultBop macro
+ DPMIBOP DpmiUnhandledException
+ db ?fault
+ ?fault = ?fault+1
+ endm
+
+ public PMFaultEntryVector
+
+; -------------------------------------------------------
+; PMFaultEntryVector -- This table contains a vector of
+; near jump instructions to the protected mode fault
+; analyzer.
+;
+PMFaultEntryVector:
+
+ StartFaultBopTable
+ rept 32
+ FaultBop
+ endm
+
+ assume ds:nothing,es:nothing,ss:nothing
+
+ public PMFaultHandlerIRET
+PMFaultHandlerIRET:
+ DPMIBOP FaultHandlerIret
+
+ public PMFaultHandlerIRETD
+PMFaultHandlerIRETD:
+ DPMIBOP FaultHandlerIretd
+
+ public PMIntHandlerIRET
+PMIntHandlerIRET:
+ DPMIBOP IntHandlerIret
+
+ public PMIntHandlerIRETD
+PMIntHandlerIRETD:
+ DPMIBOP IntHandlerIretd
+
+ public PMDosxIret
+PMDosxIret:
+ iret
+
+ public PMDosxIretd
+PMDosxIretd:
+ db 66h
+ iret
+
+;
+; -------------------------------------------------------
+; PMIntrReflector -- This routine is the entry point for
+; the protected mode interrupt reflector. This routine
+; is entered when an interrupt occurs (either software
+; or hardware). It switches the processor to real mode
+; and transfers control to the appropriate interrupt
+; service routine for the interrupt. After the interrupt
+; service routine completes, it switches back to protected
+; mode and returns control to the originally interrupted
+; protected mode code.
+; Entry to this routine comes from the PMIntrEntryVector,
+; which contains a vector of near call instructions, which
+; all call here. The interrupt number is determined from
+; the return address of the near call from the interrupt
+; entry vector.
+; The address of the real mode interrupt service routine to
+; execute is determined from the real mode interrupt vector
+; table and the interrupt number.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: The segment registers are explicitly preserved by
+; this routine. Other registers are as preserved or
+; modified by the interrutp service routine.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrReflector
+
+PMIntrReflector:
+;
+; On entry, the stack layout is:
+; [6] FLAGS - "
+; [4] CS - "
+; [2] IP - from original interrupt
+; [0] IP - from interrupt entry vector call
+;
+
+ FCLI
+ cld
+ push ds
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ mov regUserAX,ax ;save user AX for later
+
+if DEBUG
+;
+; Are we on a DOSX interrupt reflector stack?
+;
+ mov ax,ss
+ cmp ax,selDgroupPM
+ jne @F
+ cmp sp,offset bReflStack
+ jb @F
+ cmp sp,offset pbReflStack
+ jnb @F
+;
+; If so, have we overflowed a stacklet?
+;
+ mov ax,pbReflStack
+ cmp sp,ax
+ ja @F
+ add ax,CB_STKFRAME
+ cmp sp,ax
+ jb @F
+ mov ax,regUserAX
+ Debug_Out "DOSX:PMIntrReflector--Reflector stack overflow."
+@@:
+endif; DEBUG
+
+ push bp ;stack -> BP DS IP IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [A]
+ mov ax,[bp+0Ah] ;get the interrupted routine's flags
+ and ax,NOT 4100h ;clear the trace flag in case we got
+ ; an interrupt on an instruction about
+ ; to be single stepped
+ mov regUserFL,ax ;and save for later
+ mov ax,es
+ xchg ax,[bp+4] ;save ES and get entry vector address
+ pop bp
+ ;stack -> DS ES IP CS FL
+ ; [0] [2] [4] [6] [8]
+
+; The state that we want to save on the user's stack has been set up.
+; Convert the entry vector return address into an interrupt number.
+
+ sub ax,offset PMIntrEntryVector+3
+ push cx
+ mov cl,3
+ div cl
+ pop cx
+
+;if DEBUG ; debugbug
+ mov PMIntNo,ax
+;endif
+ DEBUG_TRACE DBGTR_ENTRY, ax, 0, 1000h
+
+ shl ax,2 ;turn interrupt number into interrupt
+ ; table offset
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame.
+ mov regUserSP,sp ;save entry stack pointer so we can
+ mov regUSerSS,ss ; switch to our own stack
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroupPM ;switch to the reflector stack frame
+ mov sp,pbReflStack
+ FIX_STACK
+ push pbReflStack ;save stack frame ptr on stack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+
+
+if DEBUG ;--------------------------------------------------------
+
+ push 0ABCDh ;a debugging marker & interrupt value
+ push PMIntNo
+
+ cmp fTraceReflect,0
+ jz @f
+ push ax
+ mov ax,PMIntNo
+ Trace_Out "[pr#AL]",x
+ pop ax
+@@:
+
+; Perform a too-late-to-save-us-now-but-we-want-to-know check on the
+; reflector stack.
+
+ cmp StackGuard,1022h
+ jz @f
+ Debug_Out "DOSX:PMIntrReflector--Global reflector stack overflow."
+@@:
+endif ;DEBUG ---------------------------------------------------------
+
+; We are now running on our own stack, so we can switch into real mode.
+
+ push ax ;save interrupt vector table offset
+ SwitchToRealMode
+ assume es:nothing
+
+ xor ax,ax
+ mov es,ax
+ pop ax
+
+; Build an IRET frame on the stack so that the real mode interrupt service
+; routine will return to us when it is finished.
+
+ push regUserSS ;save user stack address on our own stack
+ push regUserSP ; frame so we can restore it later
+ push ds
+ push regUserFL
+ push cs
+ push offset pmrf50
+
+; Build an IRET frame on the stack to use to transfer control to the
+; real mode interrupt routine.
+
+ xchg bx,ax ;interrupt vector offset to BX, preserve BX
+
+ and byte ptr regUserFL+1,not 02h ;use entry flags less
+ push regUserFL ; the interrupt flag (IF)
+
+ push ax
+ mov ax,word ptr RmHwIsr[bx]
+ or ax,word ptr RmHwIsr[bx + 2]
+ je pmrf20
+
+;
+; Don't reflect to the reflector that will reflect back to pmode
+;
+ pop ax
+ push word ptr RmHwIsr[bx+2]
+ push word ptr RmHwIsr[bx]
+ jmp pmrf30
+
+pmrf20: pop ax
+ push word ptr es:[bx+2] ;push segment of isr
+ push word ptr es:[bx] ;push offset of isr
+pmrf30: xchg bx,ax
+ mov ax,regUserAX ;restore entry value of AX
+;
+; At this point the interrupt reflector stack looks like this:
+;
+; [18] previous stack frame pointer
+; [16] stack segment of original stack
+; [14] stack pointer of original stack
+; [12] real mode dos extender data segment
+; [10] dos extender flags
+; [8] segment of return address back to interupt reflector
+; [6] offset of return address back to interrupt reflector
+; [4] user flags as on entry from original interrupt
+; [2] segment of real mode ISR
+; [0] offset of real mode ISR
+
+; Execute the real mode interrupt service routine
+
+ iret
+
+; The real mode ISR will return here after it is finsished.
+
+pmrf50: pop ds
+ pushf
+
+ FCLI ;We have to clear interrupts here, because
+ cld ; the interrupt routine may have returned
+ ; with interrupts on and our code that uses
+ ; static variables must be protected. We
+ ; turn them off after to pushf instruction so
+ ; that we can preserve the state of the
+ ; interrupt flag as returned by the ISR.
+
+ mov regUserAX,ax
+ pop ax
+ pop regUserSP
+ pop regUserSS
+
+if DEBUG
+ add sp,4 ;'pop' off debugging info
+endif
+ CHECK_STACK
+ ASSERT_REFLSTK_OK
+ pop pbReflStack ;deallocate stack frame(s)--it used to be that
+ ; we'd simply add CB_STKFRAME to pbReflStack
+ ; to deallocate a frame. But we found a TSR
+ ; that would pop up on an Int 28h and iret
+ ; on the Int 28h frame from an Int 8h! This
+ ; left some stack allocated, and soon resulted
+ ; in running out of frames. Keeping the frame
+ ; pointer on the stack allows us to pop
+ ; multiple frames at once.
+ ASSERT_REFLSTK_OK
+
+; Switch back to protected mode.
+
+ push ax ;preserve AX
+ SwitchToProtectedMode
+ pop ax
+
+ DEBUG_TRACE DBGTR_EXIT, 0, 0, 1000h
+; Switch back to the original stack.
+
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Put the flags returned by the real mode interrupt routine back into
+; the caller's stack so that they will be returned properly.
+
+ push bp ;stack -> BP DS ES IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [10]
+ and [bp+10],0300h ;clear all but the interrupt and trace flags
+ ; in the caller's original flags
+ or [bp+10],ax ;combine in the flags returned by the
+ ; interrupt service routine. This will cause
+ ; us to return to the original routine with
+ ; interrupts on if they were on when the
+ ; interrupt occured, or if the ISR returned
+ ; with them on.
+ pop bp
+
+; And return to the original interrupted program.
+
+ mov ax,regUserAX
+ pop ds
+ pop es
+ riret
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Real Mode Interrupt Reflector
+ page
+; -------------------------------------------------------
+; REAL MODE INTERRUPT REFLECTOR
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+; -------------------------------------------------------
+; RMIntrEntryVector -- This table contains a vector of
+; near jump instructions to the real mode interrupt
+; reflector. Real mode interrupts that have been hooked
+; by the protected mode application have their vector
+; set to entry the real mode reflector through this table.
+
+ public RMIntrEntryVector,EndRmIntrEntry
+
+RMIntrEntryVector:
+
+ rept 256
+ call RMIntrReflector
+ endm
+
+EndRMIntrEntry:
+
+; -------------------------------------------------------
+; RMIntrReflector -- This routine is the entry point for
+; the real mode interrupt reflector. This routine
+; is entered when an interrupt occurs (either software
+; or hardware) that has been hooked by the protected mode
+; application. It switches the processor to protected mode
+; and transfers control to the appropriate interrupt
+; service routine for the interrupt. After the interrupt
+; service routine completes, it switches back to real
+; mode and returns control to the originally interrupted
+; real mode code.
+; Entry to this routine comes from the RMIntrEntryVector,
+; which contains a vector of near call instructions, which
+; all call here. The interrupt number is determined from
+; the return address of the near call from the interrupt
+; entry vector.
+; The address of the protected mode interrupt service routine
+; to execute is determined from the protected mode interrupt
+; descriptor tabel and the interrupt number.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: The segment registers are explicitly preserved by
+; this routine. Other registers are as preserved or
+; modified by the interrutp service routine.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public RMIntrReflector
+
+RMIntrReflector:
+;
+; On entry, the stack layout is:
+; [6] FLAGS - "
+; [4] CS - "
+; [2] IP - from original interrupt
+; [0] IP - from interrupt entry vector call
+;
+ FCLI
+ cld
+ push ds
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+if DEBUG
+;
+; Are we on a DOSX interrupt reflector stack?
+;
+ push ax
+ push cx
+ mov ax,ss
+ mov cx,ds
+ cmp ax,cx
+ pop cx
+ jne @F
+
+ cmp sp,offset bReflStack
+ jb @F
+ cmp sp,offset pbReflStack
+ jnb @F
+;
+; If so, have we overflowed a stacklet?
+;
+ mov ax,pbReflStack
+ cmp sp,ax
+ ja @F
+ add ax,CB_STKFRAME
+ cmp sp,ax
+ jb @F
+ pop ax
+ Real_Debug_Out "DOSX:RMIntrReflector--Reflector stack overflow."
+ push ax
+@@:
+ pop ax
+endif ;DEBUG
+ mov regUserAX,ax ;save user AX for later
+ push bp ;stack -> BP DS IP IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [A]
+ mov ax,[bp+0Ah] ;get the interrupted routine's flags
+ and ax,NOT 4100h ;clear the trace flag in case we got
+ ; an interrupt on an instruction about
+ ; to be single stepped
+ mov regUserFL,ax ;and save for later
+ mov ax,es
+ xchg ax,[bp+4] ;save ES and get entry vector address
+ pop bp
+
+; Some software (like older versions of Smartdrv.sys) may enable A20 on
+; their own, and get very 'suprised' to find it turned off by our PM->RM
+; mode switch. If they used Himem.sys, this wouldn't be necessary, but...
+
+if VCPI
+ cmp fVCPI,0
+ jnz @f
+endif
+ push ax ;get/save current A20 state on stack
+ push bx
+ xmssvc 7
+ mov regUserSP,ax ;use regUserSP as a temp var
+ pop bx
+ pop ax
+@@:
+ push regUserSP
+
+; The state that we want to save on the user's stack has been set up.
+; Convert the entry vector return address into an interrupt number.
+
+ sub ax,offset RMIntrEntryVector+3
+ push cx
+ mov cl,3
+ div cl
+ pop cx
+
+;if DEBUG ; debugbug
+ mov PMIntNo,ax
+;endif
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame.
+
+ mov regUserSP,sp ;save entry stack pointer so we can
+ mov regUSerSS,ss ; switch to our own stack
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroup ;switch to the reflector stack frame
+ENDIF
+ mov sp,pbReflStack
+ FIX_STACK
+ push pbReflStack ;save stack frame ptr on stack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ push ax ;save interrupt vector table offset
+ SwitchToProtectedMode
+ pop ax
+
+if DEBUG ;--------------------------------------------------------
+
+ push 0DEADh ;debugging id & interrupt number
+ push PMIntNo
+
+ cmp fTraceReflect,0
+ jz @f
+ push ax
+ mov ax,PMIntNo
+ Trace_Out "(rp#AL)",x
+ pop ax
+@@:
+
+; Perform a too-late-to-save-us-now-but-we-want-to-know check on the
+; reflector stack.
+
+ cmp StackGuard,1022h
+ jz @f
+ Debug_Out "DOSX:RMIntrReflector--Global reflector stack overflow."
+@@:
+endif ;DEBUG ---------------------------------------------------------
+
+; Build an IRET frame on the stack so that the protected mode interrupt service
+; routine will return to us when it is finished.
+
+ push regUserSS ;save user stack address on our own stack
+ push regUserSP ; frame so we can restore it later
+ push ds
+
+ test DpmiFlags,DPMI_32BIT
+ jnz rmrf_32
+ push regUserFL
+ push cs
+ push offset rmrf50
+rmrf_hwint_cont:
+
+; Build an IRET frame on the stack to use to transfer control to the
+; protected mode ISR
+
+ and byte ptr regUserFL+1,not 02h ;use entry flags less the
+ push regUserFL ; interrupt flag (IF)
+
+ xchg bx,ax ;interrupt vector offset to BX, preserve BX
+ shl bx,3
+ mov es,selIDT
+ push word ptr es:[bx+2] ;push segment of isr
+rmrf_setISROff:
+ push word ptr es:[bx] ;push offset of isr
+ xchg bx,ax
+ mov ax,regUserAX ;restore entry value of AX
+ push ds
+ pop es
+
+; At this point the interrupt reflector stack looks like this:
+;
+; [18] previous stack frame pointer
+; [16] stack segment of original stack
+; [14] stack pointer of original stack
+; [12] protected mode dos extender data segment
+; [10] dos extender flags
+; [8] segment of return address back to interupt reflector
+; [6] offset of return address back to interrupt reflector
+; [4] user flags as on entry from original interrupt
+; [2] segment of protected mode ISR
+; [0] offset of protected mode ISR
+;
+; Execute the protected mode interrupt service routine
+
+ iret
+.386p
+rmrf_32:
+ pushfd
+ push ax
+ mov ax,regUserFL
+ mov word ptr [esp + 2],ax
+ pop ax
+ sub esp,2
+ push cs
+ push dword ptr (offset rmrf50)
+
+; Build an IRET frame on the stack to use to transfer control to the
+; protected mode ISR
+
+ and byte ptr regUserFL+1,not 02h ;use entry flags less the
+ pushfd
+ push ax
+ mov ax,regUserFL ; interrupt flag (IF)
+ mov word ptr [esp + 2],ax
+ pop ax
+
+ xchg bx,ax ;interrupt vector offset to BX, preserve BX
+ shl bx,3
+ mov es,selIDT
+rmrf_32setISR:
+ ; bugbug this is not correct. For vectors above 32, it will
+ ; grab the segment from the wrong part of the IDT.
+
+ push 0 ;hiword of segment
+ push word ptr es:[bx+2] ;segment
+ push word ptr es:[bx+6] ;hiword of offset
+ push word ptr es:[bx] ;loword of offset
+
+ xchg bx,ax
+ mov ax,regUserAX ;restore entry value of AX
+ push ds
+ pop es
+ iretd
+.286p
+
+; The protected mode ISR will return here after it is finsished.
+
+rmrf50: pop ds
+ pushf ;save flags as returned by PM Int routine
+
+ FCLI ;We have to clear interrupts here, because
+ cld ; the interrupt routine may have returned
+ ; with interrupts on and our code that uses
+ ; static variables must be protected. We
+ ; turn them off after to pushf instruction so
+ ; that we can preserve the state of the
+ ; interrupt flag as returned by the ISR.
+ mov regUserAX,ax
+ pop ax
+ pop regUserSP
+ pop regUserSS
+
+if DEBUG
+ add sp,4 ;'pop' off debugging info
+endif
+
+ ASSERT_REFLSTK_OK
+ CHECK_STACK
+ pop pbReflStack ;deallocate stack frame(s)
+ ASSERT_REFLSTK_OK
+
+; Switch back to real mode.
+
+ push ax ;preserve AX
+ SwitchToRealMode
+ pop ax
+
+; Switch back to the original stack.
+
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Make sure the A20 line matches whatever state it was when the int occured.
+; This is for the benefit of any software that diddles A20 without using
+; an XMS driver
+
+ pop regUserSP ;A20 state at time of interrupt to temp var
+if VCPI
+ cmp fVCPI,0
+ jnz rmrf75
+endif
+ push ax ;save current ax
+ mov ax,regUserSP ;ax = A20 state at time of interrupt
+ or ax,ax ;if it was off, don't sweat it
+ jz rmrf70
+ push bx ;save bx (XMS calls destroy bl)
+ push ax
+ xmssvc 7 ;ax = current A20 state
+ pop bx ;bx = old A20 state
+ cmp ax,bx ;if A20 is still on, don't need to diddle
+ jz @f
+ xmssvc 5 ;force A20 back on
+ inc A20EnableCount ; and remember that we did this
+if DEBUG
+ or fA20,04h
+endif
+@@:
+ pop bx
+rmrf70:
+ pop ax
+rmrf75:
+
+; Put the flags returned by the real mode interrupt routine back into
+; the caller's stack so that they will be returned properly.
+
+ push bp ;stack -> BP DS ES IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [10]
+ and [bp+10],0300h ;clear all but the interrupt and trace flags
+ ; in the caller's original flags
+ or [bp+10],ax ;combine in the flags returned by the
+ ; interrupt service routine. This will cause
+ ; us to return to the original routine with
+ ; interrupts on if they were on when the
+ ; interrupt occured, or if the ISR returned
+ ; with them on.
+ pop bp
+
+; And return to the original interrupted program.
+
+ mov ax,regUserAX
+ pop ds
+ pop es
+ iret
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl INT 24h Critical Error Mapper
+ page
+; -------------------------------------------------------
+; DOS CRITICAL ERROR MAPPER
+; -------------------------------------------------------
+
+DXCODE segment
+
+; -------------------------------------------------------
+; RMDefaultInt24Handler -- Default action for a DOS critical
+; error is to fail the call.
+;
+ public RMDefaultInt24Handler
+RMDefaultInt24Handler proc far
+ mov al,3
+ iret
+RMDefaultInt24Handler endp
+
+DXCODE ends
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; RMIntr24 -- This routine is a real-mode hook that traps
+; DOS critical errors, and maps them to protect mode
+; handlers. To make the critical error realistic to
+; the application, we switch to the applications
+; stack and copy the critical error stack frame to
+; there.
+
+; On entry, the stack layout is:
+;
+; [0] [2] [4] [6] [8] [10] [12] [14] [16] [18] [20] [22] [24] [26] [28]
+; IP CS FL AX BX CX DX SI DI BP DS ES IP CS FL
+;
+; |------------| |------------------------------------------| |------------|
+;
+; IRET TO DOS REGS AT TIME OF INT 24H IRET TO APP
+;
+
+ public RMIntr24
+
+RMIntr24 proc far
+
+ FCLI
+ cld
+ push es
+ push ds
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup ;stack -> DS ES IP CS FL ...
+ENDIF
+ assume ds:DGROUP ; [0] [2] [4] [6] [8] ...
+
+; We need a temporary stack to do real->protect mode switching, etc.
+; Allocate and use an interrupt frame for that.
+
+ mov regUserSP,sp ;save entry stack pointer so we can
+ mov regUSerSS,ss ; switch to our own stack
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroup ;switch to the reflector stack frame
+ENDIF
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+ push ax ;save ax, switch to protected mode
+ SwitchToProtectedMode ; need to be on our stack
+ pop ax
+
+; Now switch to the applications stack frame. We assume that the dos function
+; generating the critical error came from a protected mode app and was passed
+; through PMIntrDos, who saved the app's current stack in regusrss:regusrsp.
+
+ mov ss,pmusrss ;switch (back) to app's stack
+.386p
+ movzx esp,word ptr pmusrsp
+.286p
+
+ push regUserSS ;save prior stack address on app's stack
+ push regUserSP ; frame so we can restore it later
+
+; Copy critical error stack info to application's stack
+
+ pushf ;we don't really know where the original
+ push cs ; int 21h service was requested, so fake
+ push offset rm24trap ; one to point at a routine of ours
+
+ sub sp,9*2 ;temp save the general regs further down the
+ pusha ; stack, they'll get poped in a little while
+
+ mov ax,regUserSS ;we need a selector to the previous stack
+ mov bx,STD_DATA ;(it is almost certainly the PMIntrDos
+ call ParaToLDTSelector ; real mode stack, but this is playing safe)
+
+ mov cx,9 ;okay, now copy the 9 register values from
+ mov si,regUserSP ; the DOS visable stack to the app's
+ add si,5*2
+ mov ds,ax
+ assume ds:NOTHING
+ push ss
+ pop es
+ mov di,sp
+ add di,8*2
+ cld
+ rep movsw
+
+ mov ds,selDgroupPM ;restore addressability to our DGROUP
+ assume ds:DGROUP
+
+; On entry, BP:SI points to a device header. Map BP from a seg to a selector.
+
+ mov ax,bp
+ mov bx,STD_DATA
+ call ParaToLDTSelector
+ mov regUserAX,ax
+
+ popa ;restore initial register values
+
+ mov bp,regUserAX ;give them the selector, not the segment
+
+; Invoke the protected mode handler
+
+.386p
+ test DpmiFlags,word ptr DPMI_32BIT
+ jz ri2410
+
+ pushfd
+ push 0
+ push cs
+ push 0
+ push offset rm24ret
+ jmp ri2420
+.286p
+ri2410: pushf ;put our return address on the stack so the
+ push cs ; handler will return to us when it's done.
+ push offset rm24ret
+
+if 0 ;; ifndef WOW_x86
+ pushf ;transfer control to the
+ push word ptr PMInt24Handler+2 ; pm handler
+ push word ptr PMInt24Handler
+ iret
+else
+.386p
+ri2420: pushfd ;transfer control to the
+ push dword ptr PMInt24Handler+4 ; pm handler
+ push dword ptr PMInt24Handler
+ iretd
+.286p
+endif
+
+; The protected mode critical error handler returns here when it's finished
+; (at least it had better return here!)
+
+ public rm24ret
+rm24ret:
+
+ assume ds:NOTHING,es:NOTHING
+ FCLI
+ cld
+
+ add sp,12*2 ;clear critical error junk from stack
+
+rm24exit:
+ mov ds,selDgroupPM ;DOS extender data segment
+ assume ds:DGROUP
+
+ mov regUserAX,ax ;save action code from pm handler
+ pop regUserSP ;pop prior stack location
+ pop regUserSS
+
+; Switch back to the temp interrupt stack frame, drop to real mode, back
+; to the initial stack, and return to DOS.
+
+ ASSERT_REFLSTK_OK
+ CHECK_STACK
+ add pbReflStack,CB_STKFRAME ;in the reverse order from above so
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroupPM ; that we wind up in the same place
+.386p
+ movzx esp,pbReflStack
+.286p
+
+
+ SwitchToRealMode ;gotta be on our own stack to do this
+
+ mov ax,regUserAX ;recover AX from pm critical error handler
+
+ mov ss,regUserSS ;switch back to the original stack.
+ mov sp,regUserSP
+
+ pop ds ;return to DOS
+ pop es
+ iret
+
+; -------------------------------------------------------
+;
+; rm24trap -- This code gets executed if the protected mode critical
+; error handler attempts to bypass DOS and return directly
+; to the application. Currently this is not allowed, and
+; we just return to DOS anyway--most likely to die!
+;
+; Note: THIS IS NOT SUPPORTED! DON'T DO THIS!
+
+BeginHighSegment
+
+ public rm24trap
+
+rm24trap:
+
+ Debug_Out "Critical error handler tried to return to application!"
+
+ jmp short rm24exit
+
+EndHighSegment
+
+RMIntr24 endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl INT 28h Idle Handler
+ page
+; -------------------------------------------------------
+; INT 28H IDLE HANDLER
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntr28 -- Protected mode handler for Idle Int 28h calls.
+; The purpose of this routine is simply to cut down on the
+; number of protected mode to real mode switches by ignoring
+; many of the Int 28h idle calls made by the Windows PM
+; kernel.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr28
+
+PMIntr28 proc near
+
+
+ cld
+ push ds ;address our DGROUP
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+
+ cmp Int28Filter,0 ;are we passing any through?
+ jz @f
+
+ inc Int28Count ;should this one be reflected?
+ jz i28_reflect
+@@:
+ pop ds
+ iret ; no, just ignore it
+
+i28_reflect: ; yes, reset count and
+ push ax ; reflecto to real mode
+ mov ax,Int28Filter
+ neg ax
+ mov Int28Count,ax
+ pop ax
+ pop ds
+ assume ds:NOTHING
+
+ jmp PMIntrEntryVector + 3*28h
+
+PMIntr28 endp
+
+; -------------------------------------------------------
+ subttl Real-Time Clock Int 70h Handler
+ page
+; -------------------------------------------------------
+; REAL-TIME CLOCK INT 70h HANDLER
+; -------------------------------------------------------
+
+; PMIntr70 -- Protected mode handler for Real-Time clock
+; interrupts. This routine intercepts real-time clock
+; interrupts, and may cause them to be ignored. On 286
+; hardware, the mode switch time is a big problem in trying
+; to service the 0.976 ms periodic interrupt. So, if this
+; is a 286 machine, and periodic interrupts are enabaled,
+; we EOI the slave & master PICs, and IRET. A Tandy 2500 XL
+; machine was having a problem with the interrupt reflector
+; stack overrunning because the PS/2 style mouse was causing
+; mode switches while the RTC was programmed for periodic
+; interrupts.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr70
+
+PMIntr70 proc near
+
+ cld
+ push ds ;address our DGROUP
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+
+ cmp idCpuType,3 ;assume we can mode switch fast enough
+ jae i70_reflect ; on 386 + processors
+
+ push ax ;on a 286, are periodic interrupts
+ mov al,0Bh ; enabled? Read clock register B
+ call ReadCMOS
+
+ and al,40h ;periodic interrupts enabled?
+ jz i70_286_reflect ; no, something else, so reflect it
+
+ mov al,0Ch ;read register C to clear int bits
+ call ReadCMOS
+
+ mov al,20h ;EOI the slave PIC
+ out INTB00,al
+ IO_Delay
+ out INTA00,al ;EOI the master PIC
+
+ pop ax ;back to the shadows again...
+ pop ds
+ iret
+
+i70_286_reflect:
+ pop ax
+
+i70_reflect: ;reflect interrupt to real mode
+ pop ds
+ assume ds:NOTHING
+
+ jmp PMIntrEntryVector + 3*70h
+
+PMIntr70 endp
+
+; -------------------------------------------------------
+; ReadCMOS -- Read selected location from CMOS ram/Real-Time clock.
+;
+; in: al - CMOS location to read
+; out: al - CMOS valus
+; uses: All registers perserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public ReadCMOS
+
+ReadCMOS proc near
+
+ out CMOSLoc,al
+ IO_Delay
+ in al,CMOSValue
+ ret
+
+ReadCMOS endp
+
+; -------------------------------------------------------
+ subttl Ignore Interrupt Handlers
+ page
+; -------------------------------------------------------
+; IGNORE INTERRUPT HANDLER
+; -------------------------------------------------------
+
+; PMIntrIgnore -- Service routine for protected mode interrupts
+; that should be ignored, and not reflected to real mode.
+; Currently used for:
+;
+; Int 30h - used to be Win/386 Virtualize I/O, now
+; unused but no int handler in real mode
+; Int 41h - Wdeb386 interface, no int handler in
+; real mode
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrIgnore
+
+PMIntrIgnore proc near
+
+ iret
+
+PMIntrIgnore endp
+
+; -------------------------------------------------------
+
+ public PMIntr19
+PMIntr19 proc near
+
+ push offset DXPMCODE:Reboot
+ call RZCall
+
+bpRebootIDT df 0
+
+Reboot:
+ mov ax,40h
+ mov es,ax
+ mov word ptr es:[0072h],1234h
+ lidt bpRebootIDT
+ int 3
+
+PMIntr19 endp
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl XMS Driver Interface
+ page
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; XMScontrol - This function implements a protected mode
+; interface to a real mode XMS driver. Unlike other
+; routines in this module, this routine is called by
+; the user, not invoked via an INT instruction.
+;
+; Input: User's regs for XMS driver
+; Output: regs from XMS driver
+; Uses: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public XMScontrol
+
+XMScontrol proc far
+
+ jmp short XMSentry ;'standard' XMS control function
+ nop ; just to be consistant
+ nop
+ nop
+
+XMSentry:
+
+; Modify the stack so it looks like we got here via an INT (except that
+; we may still have interrupts enabled)
+
+ pushf
+ cld
+
+ push bp
+ mov bp,sp ;bp -> [BP] [FL] [IP] [CS]
+ push ax
+ push bx
+
+ mov ax,[bp+4]
+ mov bx,[bp+6]
+ xchg ax,[bp+2]
+ mov [bp+4],bx
+ mov [bp+6],ax ;bp -> [BP] [IP] [CS] [FL]
+ pop bx
+ pop ax
+ pop bp
+
+; We don't support XMS function 0Bh (Move Extended Memory Block) because
+; it requires mapping of data between hi/low memory. Maybe someday...
+
+ cmp ah,0Bh
+ jnz xms_2
+xms_deny:
+ xor ax,ax ;if function 0Bh, return failure
+ mov bl,80h ; (ax = 0, bl = 80h-not implemented)
+ jmp short XMSret
+xms_2:
+
+; We are not really an Int handler, but close enough...
+
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:DGROUP ; also sets up addressability
+
+if 0
+if VCPI
+;
+; If we're using VCPI, then fail the call. This is because XMS memory
+; would not be useful in protected mode unless we paged it into our
+; page tables.
+;
+ cmp fVCPI,0
+ jz xms_3
+ call LeaveIntHandler
+ mov ax,0
+ mov dx,0
+ mov bl,80h ; BX = 80h-not implemented.
+ jmp XMSret
+xms_3:
+endif
+endif
+
+ SwitchToRealMode
+
+ pop es ;load regs for driver
+ pop ds
+ assume ds:NOTHING,es:NOTHING,ss:DGROUP
+ popa
+ npopf
+
+ call lpfnXMSFunc ;call real mode driver
+
+ pushf ;rebuild stack frame
+ FCLI
+ cld
+ pusha
+ push ds
+ push es
+
+ mov bp,sp ;restore stack frame pointer
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ call LeaveIntHandler
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+XMSret:
+ riret
+
+XMScontrol endp
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Special Interrupt Handler Routines
+ page
+; -------------------------------------------------------
+;
+; The following sets of routines handle interrupts that
+; are function call interfaces and require special servicing
+; by the Dos Extender. These interrupts are such things as
+; the mouse driver function call interrupt, various PC BIOS
+; function call interrupts, etc. Note that INT 21h (the Dos
+; function call interrupt) is not handled here. These
+; interrupts typically require that register values be modified
+; and parameter data be copied between real mode memory and
+; extended memory. The following conventions are used for these
+; interrupt function handler routines.
+;
+; A stack is allocated from the interrupt reflector stack for these
+; routines to use. This allows nested servicing of interrupts.
+; A stack frame is built in the allocated stack which contains the
+; following information:
+; original caller's stack address
+; caller's original flags and general registers (in pusha form)
+; caller's original segment registers (DS & ES)
+; flags and general registers to be passed to interrupt routine
+; (initially the same as caller's original values)
+; segment registers (DS & ES) to be passed to interrupt routine
+; (initially set to the Dos Extender data segment address)
+; This stack frame is built by the routine EnterIntHandler, and its
+; format is defined by the structure INTRSTACK. The stack frame is
+; destroyed and the processor registers set up for return to the user
+; by the function LeaveIntHandler.
+;
+; For each interrupt, there is an entry function and an exit function.
+; The entry function performs any modifications to parameter values and
+; data buffering necessary before the interrupt service routine is called.
+; The exit function performs any data buffering and register value
+; modifications after return from the interrupt service routine.
+;
+; There are two sets of general registers and two sets of segment
+; registers (DS & ES) on the stack frame. One set of register values
+; has member names of the form intUserXX. The values in these stack
+; frame members will be passed to the interrupt service routine when
+; it is called, and will be loaded with the register values returned
+; by the interrupt service routine. The other set of registers values
+; has member names of the form pmUserXX. These stack frame members
+; contain the original values in the registers on entry from the
+; user program that called the interrupt.
+;
+; When we return to the original caller, we want to pass back the
+; general registers as returned by the interrupt routine (and possibly
+; modified by the exit handler), and the same segment registers as
+; on entry, unless the interrupt routine returns a value in a segment
+; register. (in this case, there must be some code in the exit routine
+; to handle this). This means that when we return to the caller, we
+; return the general register values from the intUserXX set of stack
+; frame members, but we return the segment registers from the pmUserXX
+; set of frame members. By doing it this way, we don't have to do
+; any work for the case where the interrupt subfuntion doesn't require
+; any parameter manipulation. NOTE however, this means that when
+; manipulating register values to be returned to the user, the segment
+; registers are treated opposite to the way the general registers are
+; treated. For general registers, to return a value to the user,
+; store it in a intUserXX stack frame member. To return a segment
+; value to the user, store it in a pmUserXX stack frame member.
+;
+; -------------------------------------------------------
+ subttl BIOS Video Interrupt (Int 10h) Service Routine
+ page
+; -------------------------------------------------------
+; BIOS VIDEO INTERRUPT (INT 10h) SERVICE ROUTINE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntrVideo - Entry point into interrupt reflector code
+; for IBM PC Bios video (int 10h) calls.
+;
+; Input: normal registers for Bios calls
+; Output: normal register returns for Bios calls
+; Errors: normal Bios errors
+; Uses: as per Bios calls
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrVideo
+
+PMIntrVideo:
+
+ call EnterIntHandler ;build a stack frame and fix up the
+ cld ; return address so that the interrupt
+ ;service routine will return to us.
+;
+; Perform fixups on the entry register values
+ call IntEntryVideo
+;
+; Execute the interrupt service routine
+ SwitchToRealMode
+ assume ss:DGROUP
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+ popa
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset piv_10
+ mov ax,es:[10h*4]
+ mov [bp + 2],ax
+ mov ax,es:[10h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+piv_10: pushf
+ FCLI
+ cld
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+;
+; Perform fixups on the return register values.
+ mov ax,[bp].pmUserAX ;get original function code
+ call IntExitVideo
+;
+; And return to the original caller.
+ call LeaveIntHandler
+
+ riret
+
+; -------------------------------------------------------
+; IntEntryVideo -- This routine performs any register
+; fixups and data copying needed on entry to the
+; PC BIOS video interrupt (Int 10h)
+;
+; Input: register values on stack frame
+; Output: register values on stack frame
+; Errors: none
+; Uses: any registers modified,
+; possibly modifies buffers rgbXfrBuf0 or rgbXfrBuf1
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntEntryVideo
+
+IntEntryVideo:
+
+ cmp ah,10h
+ jnz ienv20
+;
+; Video palette control function. Check for subfunctions that require
+; special actions.
+ienv10: cmp al,2 ;update all palette registers?
+ jnz @F
+ mov cx,17 ;palette data is 17 bytes long
+ jmp short ienv70 ;go copy the data
+;
+@@: cmp al,9 ;read all palette registers
+ jz ienv72
+;
+ cmp al,12h ;update video DAC color registers
+ jnz @F
+ mov cx,[bp].pmUserCX ;count of table entries is in caller CX
+ add cx,cx ;each entry is 3 bytes long
+ add cx,[bp].pmUserCX
+ jmp short ienv70 ;go copy the data down
+
+@@: cmp al,17h ;read a block of video DAC registers
+ jz ienv72
+;
+ jmp short ienv90
+;
+;
+ienv20: cmp ah,11h
+ jnz ienv30
+;
+; Character generator interface function.
+; NOTE: a number of subfunctions of function 11h need to have munging
+; and data buffering performed. However, function 30h is the only
+; one used by Codeview, so this is the only one currently implemented.
+; For this one, nothing needs to be done on entry, only on exit.
+ jmp short ienv90
+;
+;
+ienv30: cmp ah,1Bh
+ jnz ienv40
+;
+; Video BIOS functionality/state information.
+; On entry, we need to fix up ES:DI to point to our buffer.
+ mov [bp].intUserDI,offset DGROUP:rgbXfrBuf0
+ jmp short ienv90
+;
+;
+ienv40:
+ jmp short ienv90
+;
+; Copy the buffer from the user ES:DX to our transfer buffer and set
+; the value to DX passed to the interrupt routine to point to our buffer.
+ienv70: cld
+ jcxz ienv90
+ push ds
+ mov si,[bp].pmUserDX
+ mov ds,[bp].pmUserES
+ mov di,offset DGROUP:rgbXfrBuf1
+ cld
+ rep movsb
+ pop ds
+;
+ienv72: mov [bp].intUserDX,offset DGROUP:rgbXfrBuf1
+ jmp short ienv90
+
+;
+; All done
+ienv90:
+ ret
+
+; -------------------------------------------------------
+; IntExitVideo: This routine performs any register
+; fixups and data copying needed on exit from the
+; PC BIOS video interrupt (Int 10h).
+;
+; Input: register values on stack frame
+; Output: register values on stack frame
+; Errors: none
+; Uses: any registers modified
+; possibly modifies buffers rgbXfrBuf0 or rgbXfrBuf1
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntExitVideo
+
+IntExitVideo:
+
+ cmp ah,10h
+ jnz iexv20
+;
+; Palette control function.
+ cmp al,9 ;read palette data function
+ jnz @F
+ mov cx,17
+ jmp short iexv70
+;
+@@: cmp al,17h ;read video DAC registers
+ jnz @F
+ mov cx,[bp].pmUserCX ;each entry in table is 3 bytes long
+ add cx,cx
+ add cx,[bp].pmUserCX
+ jmp short iexv70
+;
+@@: jmp short iexv72
+;
+;
+iexv20: cmp ah,11h
+ jnz iexv30
+;
+; Character generator interface function.
+; NOTE: a number of subfunctions of function 11h need to have munging
+; and data buffering performed. However, function 30h is the only
+; one used by Codeview, so this is the only one currently implemented
+ cmp al,30h
+ jnz @F
+ mov ax,[bp].intUserES ;get the paragraph address returned by BIOS
+ mov bx,STD_DATA
+ call ParaToLDTSelector ;get a selector for that address
+ mov [bp].pmUserES,ax ;store the selector so that it will be
+ ; returned to the caller
+@@: jmp short iexv90
+;
+;
+iexv30: cmp ah,1Bh
+ jnz iexv40
+;
+; Video BIOS functionality/state information.
+; On exit, we need to fix up the pointer at the beginning of the
+; data put in our buffer by the BIOS, and then transfer the buffer up
+; to the user.
+ mov ax,word ptr rgbXfrBuf0[2] ;get segment of pointer to
+ ; 'static functionallity table'
+ mov bx,STD_DATA
+ call ParaToLDTSelector ;convert paragraph to selector
+ mov word ptr rgbXfrBuf0[2],ax ;store back into table
+ push es
+ mov si,offset rgbXfrBuf0 ;pointer to our copy of the table
+ mov di,[bp].pmUserDI ;where the user wants it
+ mov [bp].intUserDi,di ;restore the DI returned to the user
+ mov es,[bp].pmUserES
+ mov cx,64 ;the table is 64 bytes long
+ cld
+ rep movsb ;copy the table to the user's buffer
+ pop es
+
+ jmp short iexv90
+;
+;
+iexv40:
+ jmp short iexv90
+
+;
+; Copy data from our buffer to the caller's buffer pointed to by ES:DX
+iexv70: cld
+ push es
+ mov di,[bp].pmUserDX
+ mov es,[bp].pmUserES
+ mov si,offset DGROUP:rgbXfrBuf1
+ rep movsb
+ pop es
+;
+; Restore the caller's DX
+iexv72: mov ax,[bp].pmUserDX
+ mov [bp].intUserDX,ax
+;
+; All done
+iexv90:
+ ret
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl BIOS Misc. Interrupt (Int 15h) Service Routine
+ page
+; -------------------------------------------------------
+; BIOS MISC. INTERRUPT (INT 15h) SERVICE ROUTINE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntrMisc -- Entry point into the interrupt processing code
+; for the BIOS misc functions interrupt (INT 15h).
+;
+; Input: normal registers for Bios calls
+; Output: normal register returns for Bios calls
+; Errors: normal Bios errors
+; Uses: as per Bios calls
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrMisc
+
+PMIntrMisc:
+;
+ call EnterIntHandler ;build a stack frame and fix up the
+ cld ; return address so that the interrupt
+ ;service routine will return to us.
+;
+; Perform fixups on the entry register values
+ call IntEntryMisc
+;
+; Execute the interrupt service routine
+ SwitchToRealMode
+ assume ss:DGROUP
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+ popa
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset pim_10
+ mov ax,es:[15h*4]
+ mov [bp + 2],ax
+ mov ax,es:[15h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+pim_10: pushf
+ FCLI
+ cld
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+;
+; Perform fixups on the return register values.
+ mov ax,[bp].pmUserAX ;get original function code
+ call IntExitMisc
+;
+; And return to the original caller.
+ call LeaveIntHandler
+ riret
+
+; -------------------------------------------------------
+; MISC INTERRUPT SUPPORT ROUTINES
+; -------------------------------------------------------
+;
+; IntEntryMisc -- This function performs data transfer
+; and register translation on entry to the BIOS Misc.
+; functions interrupt. (INT 15h).
+;
+; Input: AX - BIOS function being performed
+; Output:
+; Errors:
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntEntryMisc
+
+IntEntryMisc:
+
+; Map requests to set the PS/2 Pointing Device Handler Address
+
+ cmp ax,0C207h ;PS/2 Set Pointing Device Handler adr?
+ jnz iem90
+
+ mov ax,[bp].pmUserBX ;User's ES:BX -> handler
+ mov word ptr lpfnUserPointingHandler,ax
+ mov ax,[bp].pmUserES
+ mov word ptr [lpfnUserPointingHandler+2],ax
+
+ mov ax,segDXCodePM ;pass BIOS address of our handler
+ mov [bp].intUserES,ax
+ mov ax,offset PointDeviceHandler
+ mov [bp].intUserBX,ax
+
+iem90:
+ ret
+
+; -------------------------------------------------------
+; IntExitMisc -- This function performs data transfer
+; and register translation on exit from the BIOS Misc.
+; Functions interrupt (INT 15h).
+;
+; Input: AX - BIOS function being performed
+; Output:
+; Errors:
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntExitMisc
+
+IntExitMisc:
+ push ax
+ push bx
+ push cx
+ push dx
+;
+; Check for function 0C0h - Return System Configuration Parameters
+ cmp ah,0C0h
+ jnz ixmi30
+ test [bp].intUserFL,1 ;check if the bios call returned an error
+ jnz ixmi90 ;(carry flag set in returned flags)
+;
+; The BIOS call succeeded. This means that ES:BX points to a configuration
+; vector. We need to fix up the segment to be a selector.
+ mov dx,[bp].intUserES
+ cmp dx,0F000h ;does it point to normal BIOS segment
+ jnz ixmi22
+ mov ax,SEL_BIOSCODE or STD_RING
+ jmp short ixmi24
+
+ixmi22: call ParaToLinear
+ mov cx,0FFFFh
+ mov ax,SEL_USERSCR or STD_TBL_RING
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+ixmi24: mov [bp].pmUserES,ax
+ jmp short ixmi90
+
+; Chack for function 0C207h - PS/2 Set Pointing Device Handler Address
+
+ixmi30:
+ cmp ax,0C207h
+ jne ixmi90
+
+ mov ax,[bp].pmUserBX ;restore user's BX
+ mov [bp].intUserBX,ax
+
+; All done
+ixmi90:
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Mouse Function Interrupt (Int 33h) Service Routine
+ page
+; -------------------------------------------------------
+; MOUSE FUNCTION INTERRUPT (INT 33h) SERVICE ROUTINE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntrMouse - Entry point into interrupt reflector code
+; for mouse driver (int 33h) calls.
+;
+; Input: normal registers for mouse calls
+; Output: normal register returns for mouse calls
+; Errors: normal mouse errors
+; Uses: as per mouse calls
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrMouse
+
+PMIntrMouse:
+;
+ call EnterIntHandler ;build a stack frame and fix up the
+ cld ; return address so that the interrupt
+ ;service routine will return to us.
+;
+; Perform fixups on the entry register values
+ call IntEntryMouse
+;
+; Execute the interrupt service routine
+ SwitchToRealMode
+ assume ss:DGROUP
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+ popa
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset pimo_10
+ mov ax,es:[33h*4]
+ mov [bp + 2],ax
+ mov ax,es:[33h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+pimo_10: pushf
+ FCLI
+ cld
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+;
+; Perform fixups on the return register values.
+ mov ax,[bp].pmUserAX ;get original function code
+ call IntExitMouse
+;
+; And return to the original caller.
+ call LeaveIntHandler
+ riret
+
+; -------------------------------------------------------
+; MOUSE SUPPORT ROUTINES
+; -------------------------------------------------------
+
+; IntEntryMouse -- This function performs data transfer and
+; register translation on entry to mouse driver functions.
+; (INT 33h)
+;
+; Input: AX - mouse function being performed
+; Output:
+; Errors:
+; Uses: NOTHING
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntEntryMouse
+
+IntEntryMouse:
+ cld
+ push ax
+ push cx
+ push si
+ push di
+;
+ cmp al,9 ;Set graphics cursor block?
+ jnz ment10
+;
+; The user is setting a graphics cursor. We need to copy the masks
+; down to low memory so that the mouse driver can get at them and then
+; fix up the pointer in DX.
+ mov cx,32
+ jmp short ment92
+;
+; Mouse interrupt handler establishment
+ment10: cmp al,12 ;Set user defined interrupt subroutine ?
+ jnz ment20
+;
+; This command has the effect of causing a call to the address es:ds
+; Whenever an event of one of the types specified by the mask in cx.
+; The address es:dx must be saved in lpfnUserMouseHandler and the
+; real mode address of MouseInterruptHandler substituted.
+ mov ax,[bp].pmUserDX ; Load users handler offset
+ mov word ptr lpfnUserMouseHandler,ax ; Store for future use
+ mov ax,[bp].pmUserES ; Load users handler segment value
+ mov word ptr lpfnUserMouseHandler + 2,ax ; Store for future use
+ mov ax,segDXCodePM ; Load real mode code segment value
+ mov [bp].intUserES,ax ; Store in real mode es register image
+ mov ax,offset MouseInterruptHandler ; Load handler offset
+ mov [bp].intUserDX,ax ; Store in real mode dx register image
+ jmp short ment99 ;Return
+ ;
+ment20: cmp al,20
+ jc ment99
+ jnz ment30
+;
+; This is the swap interrupt subroutine function. Not currently implemented
+ jmp short ment99
+;
+ment30: cmp al,22 ;Save mouse driver state?
+ jnz ment40
+;
+; This is the save mouse driver state function. We need to pass a pointer
+; to the transer buffer down to the mouse driver.
+ mov ax,npXfrBuf1
+ mov [bp].intUserDX,ax
+ jmp short ment99
+
+ment40: cmp al,23 ;Restore mouse driver state?
+ jnz ment99
+;
+; This is the restore mouse driver state function. We need to copy the
+; mouse state buffer from the pm user location to the transfer buffer,
+; and then pass the pointer to the transfer buffer on to the mouse driver.
+ mov cx,cbMouseState
+ jcxz ment99
+;
+; Transfer the data pointed to by the user ES:DX to the scratch buffer, and
+; fix up the pointer that is passed on to the mouse driver.
+ment92: mov si,[bp].pmUserDX
+ mov di,npXfrBuf1
+ mov [bp].intUserDX,di
+ push ds
+ mov ds,[bp].pmUserES
+ cld
+ rep movs word ptr [di],word ptr [si]
+ pop ds
+;
+ment99: pop di
+ pop si
+ pop cx
+ pop ax
+ ret
+
+; -------------------------------------------------------
+; IntExitMouse -- This function performs data transfer and
+; register translation on exit from mouse driver functions.
+; (INT 33h)
+;
+; Input: AX - mouse function being performed
+; Output:
+; Errors:
+; Uses:
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntExitMouse
+
+IntExitMouse:
+ cld
+ cmp al,21 ;get state buffer size?
+ jnz mxit20
+;
+; We need to remember the state buffer size, so that later we will know
+; how many bytes to transfer when we do the save/restore state fucntions.
+ mov ax,[bp].intUserBX
+ mov cbMouseState,ax
+ return
+;
+mxit20: cmp al,22 ;Save mouse driver state?
+ jnz mxit30
+;
+; We need to restore the original values of ES:DX and transfer the mouse
+; state data from the real mode buffer to the user's protected mode buffer.
+ mov cx,cbMouseState
+ jcxz mxit28
+ push es
+ mov si,npXfrBuf1
+ mov di,[bp].pmUserDX
+ mov [bp].intUserDX,di
+ mov es,[bp].pmUserES
+ rep movs byte ptr [di],byte ptr [si]
+ pop es
+mxit28: return
+;
+mxit30: cmp al,23 ;Restore mouse driver state?
+ jnz mxit99
+ mov ax,[bp].pmUserDX
+ mov [bp].intUserDX,ax
+;
+mxit99: ret
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl PM Interrupt Support Routines
+ page
+; -------------------------------------------------------
+; PM INTERRUPT SUPPORT ROUTINES
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; EnterIntHandler -- This routine will allocate a stack
+; frame on the interrupt reflector stack and make
+; a copy of the registers on the allocated stack.
+;
+; Note: This routine expects the current stack to contain a near
+; return address and a normal [IP] [CS] [FL] interrupt stack
+; frame. Don't have anything else on the stack before calling
+; this routine!
+;
+; Note: This routine disables interrupts, and leaves them disabled.
+; Most callers already have them disabled, so it doesn't
+; really make a difference, except that this routine
+; requires that they be disabled.
+;
+; Input: none
+; Output: stack frame set up
+; Errors: none
+; Uses: all registers preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public EnterIntHandler
+
+EnterIntHandler proc near
+
+ FCLI ;we really want int's disabled (and
+ ; XMScontrol doesn't do that)
+ push ds
+ mov ds,selDgroupPM ;save user's DS and address our DGROUP
+ assume ds:DGROUP
+ pop regUserDS
+
+ push bp
+ mov bp,sp ;bp -> [BP] [IP] [IP] [CS] [FL]
+ push word ptr [bp+8]
+ pop regUserFL ;user's flags before doing INT
+ pop bp
+
+ pop pfnReturnAddr ;near return to our immediate caller
+
+ mov regUserSS,ss ;save caller's stack address
+ mov regUserSP,sp
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroupPM ;switch to interrupt reflector stack
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+
+; Build the stack frame. The stack frame contains the following:
+; dword & word parameter locations
+; original caller's stack address
+; caller's original flags and general registers (in pusha form)
+; caller's original segment registers (DS & ES)
+; flags and general registers to be passed to interrupt routine
+; (initially the same as caller's original values)
+; segment registers (DS & ES) to be passed to interrupt routine
+; (initially set to the Dos Extender data segment address)
+;
+; The parameter words and then the caller's original register values go on top.
+
+ sub sp,8 ;space for a dd & 2 dw's
+
+ push regUserSP
+ push regUserSS
+ push regUserFL
+ pusha
+ push regUserDS
+ push es
+
+; Now, put all of the general registers, and values for the segment
+; registers to be passed to the interrupt service routine. We pass
+; the Dos Extender data segment address to the interrupt routine.
+
+ push regUserFL
+ pusha
+IFDEF ROM
+ push segDXData
+ push segDXData
+ELSE
+ push segDXDataPM
+ push segDXDataPM
+ENDIF
+
+; And we are done.
+
+ mov bp,sp ;set up frame pointer
+ mov es,selDgroupPM
+ jmp pfnReturnAddr ;return to the caller.
+
+EnterIntHandler endp
+
+
+; -------------------------------------------------------
+; LeaveIntHandler -- This routine will restore the user registers,
+; release the stack frame, and restore the original user's stack
+; for exit from an interrupt reflector routine.
+;
+; Note: Interrupts must be off when this routine is called.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: All registers modified
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public LeaveIntHandler
+
+LeaveIntHandler proc near
+
+ FCLI
+ pop pfnReturnAddr
+
+; The copy of the register values returned from the interrupt routine
+; (and then possibly modified by the exit handler for the particular
+; interrupt) are what gets returned to the caller. We discard the original
+; register values saved on entry. (They were there so that the exit
+; routine could refer to them if necessary)
+
+ add sp,4 ;skip over interrupt service routine's
+ ; segment register values
+ popa ;restore general register values
+ pop regUserFL ;flags returned by interrupt routine
+ pop es ;get segment registers from pmUserES
+ pop regUserDS ; and pmUserDS
+ add sp,18 ;skip over the original user registers
+ ; and flags
+ pop regUserSS ;original interrupted routine's stack
+ pop regUserSP
+ mov regUserAX,ax
+
+; Switch back to the original user's stack.
+
+ ASSERT_REFLSTK_OK
+ ASSERT_CLI
+ CHECK_STACK
+ mov ss,regUserSS
+ mov sp,regUserSP
+ add pbReflStack,CB_STKFRAME
+ ASSERT_REFLSTK_OK
+
+; We need to replace the image of the flags in the original int return
+; address on the user's stack with the new flags returned from the interrupt
+; service routine.
+
+ push bp
+ mov bp,sp ;stack -> BP IP CS FL
+ mov ax,regUserFL ;flags returned by interrupt service routine
+ and ax,0BFFFh ;clear the nested task flag
+ and [bp+6],0300h ;clear all but the interrupt and trace flags
+ ; in the caller's original flags
+ or [bp+6],ax ;combine in the flags returned by the
+ ; interrupt service routine. This will cause
+ ; us to return to the original routine with
+ ; interrupts on if they were on when the
+ ; interrupt occured, or if the ISR returned
+ ; with them on.
+ pop bp
+
+; And now, return to the caller.
+
+ push pfnReturnAddr
+ mov ax,regUserAX
+ mov ds,regUserDS
+ assume ds:NOTHING
+ ret
+
+LeaveIntHandler endp
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Mouse Interrupt Callback Function Handler
+ page
+; -------------------------------------------------------
+; MOUSE INTERRUPT CALLBACK FUNCTION HANDLER
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; MouseInterruptHandler -- This routine is the entry point for
+; user requested mouse event interrupts. It switches the
+; processor to protected mode and transfers control to the
+; user protected mode mouse handling routine. When that
+; completes, it switches back to real mode and returns control
+; to the mouse driver.
+; Entry to this routine will have been requested by an
+; INT 33H code 12 with the real address of this routine
+; substituted for the users entry point.
+; The address of the user specified mouse handler as specified
+; in the original INT 33H is stored in the variable
+; lpfnUserMouseHandler.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: The segment registers are explicitly preserved by
+; this routine. Other registers are as preserved or
+; modified by the users mouse handler.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public MouseInterruptHandler
+
+MouseInterruptHandler proc far
+;
+; On entry, the stack layout is:
+; [2] CS - System mouse handler code segment
+; [0] IP - System mouse handler return offset
+;
+
+ push es
+ push ds
+ pushf
+ FCLI
+ cld
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+ pop regUserFL
+;
+; Allocate a new stack frame, and then switch to the local stack
+; frame.
+ mov regUserSP,sp ;save entry stack pointer so we can restore it
+ mov regUSerSS,ss ;save segment too
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,selDgroup ;switch to our own stack frame
+ENDIF
+ ASSERT_REFLSTK_OK
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+;
+; We are now running on our own stack, so we can switch into protected mode.
+ push ax ;preserve caller's AX
+ SwitchToProtectedMode
+ pop ax
+;
+; Build a far return frame on the stack so that the user's
+; routine will return to us when it is finished.
+ push regUserSS ; save system mouse handler stack address
+ push regUserSP ; so we can restore it later
+ push ds
+ push cs
+ push offset mih50
+;
+; Build an IRET frame on the stack to use to transfer control to the
+; user's protected mode routine
+ push regUserFL
+ push word ptr lpfnUserMouseHandler+2 ;push segment of user routine
+ push word ptr lpfnUserMouseHandler ;push offset of user routine
+;
+; At this point the interrupt reflector stack looks like this:
+;
+; [14] stack segment of original stack
+; [12] stack pointer of original stack
+; [10] real mode dos extender data segment
+; [8] segment of return address back to here
+; [6] offset of return address back here
+; [4] Users flags
+; [2] segment of user routine
+; [0] offset of user routine
+;
+; Execute the users mouse handler
+ iret
+;
+; The users handler will return here after it is finsished.
+mih50: FCLI
+ cld
+ pop ds
+ pop regUserSP
+ pop regUserSS
+;
+; Switch back to real mode.
+ push ax ;preserve AX
+ SwitchToRealMode
+ pop ax
+ CHECK_STACK
+;
+; Switch back to the original stack.
+ mov ss,regUserSS
+ mov sp,regUserSP
+ ASSERT_REFLSTK_OK
+;
+; Deallocate the stack frame that we are using.
+ add pbReflStack,CB_STKFRAME
+ ASSERT_REFLSTK_OK
+;
+; And return to the original interrupted program.
+ pop ds
+ pop es
+
+ ret
+
+MouseInterruptHandler endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl PS/2 Pointing Device Handler
+ page
+; -------------------------------------------------------
+; PS/2 POINTING DEVICE HANDLER
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; PointDeviceHandler -- This routine is the entry point for
+; the PS/2 Pointing Device Handler. It switches the
+; processor to protected mode and transfers control to the
+; user pointing device handler. When that completes,
+; it switches back to real mode and returns control to
+; the PS/2 BIOS.
+;
+; Note: The BIOS calls us with interrutps enabled!
+
+; Input: none
+; Output: none
+; Errors: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PointDeviceHandler
+
+PointDeviceHandler proc far
+
+; On entry, the stack layout is:
+;
+; [10] status
+; [8] X coordinate
+; [6] Y coordinate
+; [4] Z coordinate
+; [2] CS - PS/2 BIOS code segment
+; [0] IP - PS/2 BIOS return offset
+
+ cld
+ push es ;save PS/2 BIOS ds/es on it's stack
+ push ds
+
+IFDEF ROM
+ push ax
+ GetRMDataSeg
+ mov ds,ax
+ mov es,ax
+ pop ax
+ELSE
+ mov ds,selDgroup ;addressability to DOSX DGROUP
+ push ds
+ pop es
+ENDIF
+ assume ds:DGROUP,es:DGROUP
+
+ FCLI ;protect global regUserXX vars
+
+; Allocate a new stack frame, and then switch to the local stack
+; frame.
+
+ mov regUserSP,sp ;save entry stack pointer so we can restore it
+ mov regUSerSS,ss ;save segment too
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroup ;switch to our own stack frame
+ENDIF
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+
+ push regUserSS ;save PS/2 BIOS stack address
+ push regUserSP ; so we can restore it later
+
+ push SEL_DXDATA or STD_RING ;DOSX DS to be poped in PM
+
+ sub sp,4*2 ;temp save the general regs further down the
+ pusha ; stack, they'll get poped in a little while
+
+; Copy PS/2 pointing device stack info to our (soon to be) protected mode stack
+
+ mov si,regUserSP ;PS/2 stack pointer
+ mov ds,regUserSS ;PS/2 stack segment
+ assume ds:NOTHING
+
+ FSTI ;no more references to global regUserXX vars
+
+ add si,4*2 ;skip over es,ds,cs,ip
+ mov di,sp ;loc for pointing device
+ add di,8*2 ; data on our stack
+ mov cx,4
+ cld
+ rep movsw
+
+ push es ;restore ds = DGROUP
+ pop ds
+ assume ds:DGROUP
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ SwitchToProtectedMode ;disables interrupts again
+ FSTI ; but we don't want them disabled
+
+ popa ;restore general registers
+
+; At this point the stack looks like this:
+;
+; [12] stack segment of original stack
+; [10] stack pointer of original stack
+; [8] protect mode dos extender data segment
+; [6] status
+; [4] X coordinate
+; [2] Y coordinate
+; [0] Z coordinate
+
+; Execute the user's pointing device handler
+
+ call [lpfnUserPointingHandler]
+
+; The users handler will return here after it is finsished.
+
+pdh50:
+ cld
+ add sp,4*2 ;discard pointing device info
+ pop ds
+
+ FCLI ;protect global regUserXX vars
+ pop regUserSP
+ pop regUserSS
+
+; Switch back to real mode.
+
+ push ax ;preserve AX
+ SwitchToRealMode
+ pop ax
+
+; Switch back to the original stack.
+
+ CHECK_STACK
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Deallocate the stack frame that we are using.
+
+ ASSERT_REFLSTK_OK
+ add pbReflStack,CB_STKFRAME
+ ASSERT_REFLSTK_OK
+
+; And return to the PS/2 BIOS
+
+ FSTI ;we came in with ints enabled
+
+ pop ds
+ pop es
+
+ ret
+
+PointDeviceHandler endp
+
+; -------------------------------------------------------
+;
+; -------------------------------------------------------
+ subttl Utility Function Definitions
+ page
+; -------------------------------------------------------
+; UTILITY FUNCTION DEFINITIONS
+; -------------------------------------------------------
+;
+; SaveRMIntrVectors -- This routine copies the current
+; real mode interrupt vector table to the shadow
+; vector table used by the interrupt reflector.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses; all registers preserved
+;
+; NOTE: This routine can only be called in REAL MODE.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public SaveRMIntrVectors
+
+SaveRMIntrVectors:
+ push cx
+ push si
+ push di
+ push ds
+ push es
+;
+ cld
+ push ds
+ pop es
+ xor cx,cx
+ mov si,cx
+ mov ds,cx
+ mov di,offset DGROUP:rglpfnRmISR
+ mov cx,2*256
+ rep movs word ptr [di],word ptr [si]
+;
+ pop es
+ pop ds
+ pop di
+ pop si
+ pop cx
+ ret
+
+; -------------------------------------------------------
+; RestoreRMIntrVectors -- This routine copies the
+; interrupt vectors from the real mode interrupt
+; vector shadow table back down to the real interrupt
+; vectors.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses; all registers preserved
+;
+; NOTE: This routine can only be called in REAL MODE.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public RestoreRMIntrVectors
+
+RestoreRMIntrVectors:
+ push cx
+ push si
+ push di
+ push ds
+ push es
+;
+ FCLI
+ cld
+ xor cx,cx
+ mov di,cx
+ mov es,cx
+ mov si,offset DGROUP:rglpfnRmISR
+ mov cx,2*256
+ rep movs word ptr [di],word ptr [si]
+ FSTI
+;
+ pop es
+ pop ds
+ pop di
+ pop si
+ pop cx
+ ret
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+IFDEF WOW
+
+;--------------------------------------------------------
+;
+; Wow16TransitionToUserMode -- This routine simulates a
+; ring transition from the kernelmode dos extender
+; code to the usermode dos extender code. It does this
+; by restoring the user regs from the dosx stack, restoring
+; user bp from user stack, and retf
+;
+; Inputs: ss:sp -> user ds
+; user ax
+; user bx
+; user cx
+; user sp
+; user ss
+; user ss:sp -> user bp
+; user ip
+; user cs
+; Outputs: none
+;
+
+ assume ds:nothing,es:nothing,ss:DGROUP
+ public Wow16TransitionToUserMode
+Wow16TransitionToUserMode proc
+
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+ mov bp,sp
+.386p
+ lss sp,[bp]
+.286p
+ pop bp
+ retf
+
+Wow16TransitionToUserMode endp
+
+;--------------------------------------------------------
+;
+; wow32TransitionToUserMode -- This routine simulates a
+; ring transition from the kernelmode dos extender
+; code to the usermode dos extender code. It does this
+; by restoring the user regs from the dosx stack, restoring
+; user bp from user stack, and retf
+;
+; Inputs: ss:sp -> user ds
+; user ax
+; user bx
+; user cx
+; user esp
+; user ss
+; user ss:sp -> user bp
+; user eip
+; user cs
+; Outputs: none
+;
+
+ assume ds:nothing,es:nothing,ss:DGROUP
+ public wow32TransitionToUserMode
+wow32TransitionToUserMode proc
+
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+ mov bp,sp
+.386p
+ lss esp,[bp]
+.286p
+ pop bp
+ db 066h ; operand override
+ retf
+
+wow32TransitionToUserMode endp
+
+
+ public Wow32IntrRefl
+Wow32IntrRefl label word
+??intnum = 0
+rept 256
+ push word ptr ??intnum
+ jmp Wow32Intr16Reflector
+ ??intnum = ??intnum + 1
+endm
+;--------------------------------------------------------
+;
+; Wow32Intr16Reflector -- This routine reflects a 32 bit
+; interrupt to a 16 bit handler. It switches to the
+; dos extender stack to do so.
+;
+; Inputs: none
+; Outputs: none
+;
+ assume ds:nothing,es:nothing,ss:nothing
+ public Wow32Intr16Reflector
+Wow32Intr16Reflector proc
+.386p
+ push ebp
+ mov ebp,esp
+ push ds
+ push eax
+ push ebx
+ push edi
+ mov ax,ss
+ movzx eax,ax
+ lar eax,eax
+ test eax,(AB_BIG SHL 8)
+ jnz w32i16r10
+
+ movzx ebp,bp
+w32i16r10:
+
+;
+; Get a frame on the dosx stack.
+;
+ mov ax,selDgroupPM
+ mov ds,ax
+ assume ds:DGROUP
+
+ movzx ebx,pbReflStack
+ sub pbReflStack,CB_STKFRAME
+
+;
+; Build a frame on the stack
+;
+ sub bx,30
+ mov eax, [ebp+6] ; eip
+ mov [bx+20], eax
+ mov eax, [ebp+10] ; cs
+ mov [bx+24], eax
+
+ mov [bx + 18],ss ; ss for stack switch back
+ mov eax,ebp
+ add eax,6 ; ebp, int number
+ mov [bx + 14],eax ; esp for stack switch back
+ mov ax,[ebp + 14] ; get flags
+ mov [bx + 12],ax
+ mov ax,cs
+ mov [bx + 10],ax
+ mov [bx + 8],offset DXPMCODE:w3216r30
+ mov eax,[ebp]
+ mov [bx],eax ; put ebp on other stack for pop
+;
+; Get handler
+;
+ mov di,[ebp + 4] ; int number
+ shl di,2 ; al * 4
+ add di,offset DGROUP:Wow16BitHandlers
+ mov ax,[di]
+ mov [bx + 4],ax ; handler ip
+ mov ax,[di + 2]
+ mov [bx + 6],ax ; handler cs
+
+;
+; Set up for stack switch
+;
+ push ds
+ push ebx
+;
+; Restore registers
+;
+ mov ax,[ebp - 2]
+ mov ds,ax
+ mov eax,[ebp - 6]
+ mov ebx,[ebp - 10]
+ mov edi,[ebp - 14]
+;
+; Switch stacks, restore ebp, and call handler
+;
+ lss esp,[ebp - 20]
+ pop ebp
+ DEBUG_TRACE DBGTR_ENTRY, 0, 0, 2000h
+ retf
+;
+; N.B. i31_RMCall looks on the stack to get the original user stack pointer.
+; if you change the stack frame the is passed to the 16 bit int
+; handlers, that WILL break.
+;
+
+w3216r30:
+ DEBUG_TRACE DBGTR_EXIT, 0, 0, 2000h
+;
+; Switch stacks, deallocate frame from dosx stack and return
+;
+ push ebx
+ push eax
+ push ds
+ lds ebx,[esp+10] ;get ss:esp
+ mov eax,[esp+16]
+ mov [ebx],eax ;eip
+ mov eax,[esp+20]
+ mov [ebx+4],eax ;cs
+ pop ds
+ pop eax
+ pop ebx
+
+ lss esp,[esp]
+ push ebx
+
+
+ pushfd
+ push eax
+ mov ax,ss
+ movzx eax,ax
+ lar eax,eax
+ test eax,(AB_BIG SHL 8) ; is the stack big?
+ jnz w32i16r40 ; jif yes, use 32bit operations
+ pop eax ; restore regs
+ popfd
+
+ rpushfd ; save flags, set virtual int bit
+ pop ebx
+ push ebp
+ movzx ebp, sp
+ mov [ebp + 16],ebx ; put flags on iret frame
+ pop ebp
+ push ds
+ mov bx,selDgroupPM
+ mov ds,bx
+ add pbReflStack,CB_STKFRAME
+ pop ds
+ pop ebx
+ riretd
+
+w32i16r40: ; stack is big
+ pop eax ; restore regs
+ popfd
+
+ rpushfd32
+ pop ebx
+ mov [esp + 12],ebx
+ push ds
+ mov bx,selDgroupPM
+ mov ds,bx
+ add pbReflStack,CB_STKFRAME
+ pop ds
+ pop ebx
+ riretd32
+
+.286p
+Wow32Intr16Reflector endp
+ENDIF
+DXPMCODE ends
+;
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/486/dxmain.asm b/private/mvdm/dpmi/486/dxmain.asm
new file mode 100644
index 000000000..3c4aa8707
--- /dev/null
+++ b/private/mvdm/dpmi/486/dxmain.asm
@@ -0,0 +1,3369 @@
+ PAGE ,132
+ TITLE DXMAIN.ASM -- Main Module for Dos Extender
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXMAIN.ASM - Dos Extender Main Module *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains the main routines for the Dos *
+;* Extender. This is based on code written for Microsoft *
+;* by Murray Sargent of Scroll Systems from Tucson Arizona. *
+;* *
+;* The Dos Extender provides support to allows specially *
+;* written programs to run in protected mode mode on the *
+;* 80286 and 80386 under MS-DOS. The following areas of *
+;* support are provided to accomplish this: *
+;* *
+;* Program Loading and Initialization *
+;* This involves creating a program segment prefix and *
+;* then loading and relocating the exe file. When *
+;* loading an exe for protected mode operation, it is *
+;* necessary to create segment descriptors for all *
+;* segments used by the program and to then substitute *
+;* the corresponding selectors when fixing up the segment *
+;* references in the code. *
+;* *
+;* Dos Function Call Support *
+;* Since Dos must execute in real mode, it is necessary *
+;* to perform mode switching into real mode and the back *
+;* to protected mode when the application makes Dos calls. *
+;* Also, any far pointers that are parameters to the *
+;* function must be converted from the selector:offset *
+;* form that the application uses to a segment:offset form *
+;* that Dos can use, with the corresponding data being *
+;* buffered from the application's extended memory address *
+;* space to Dos's real mode address space. *
+;* *
+;* Other Interrupt Support *
+;* Hardware interrupts are processed in real mode, and *
+;* so the Dos Extender performs mode switching on each *
+;* interrupt. Also other system resources (such as the *
+;* mouse driver and the bios) are entered through software *
+;* interrupts, and require the same kind of buffering *
+;* and parameter translation that the Dos functions *
+;* require. *
+;* *
+;* Extended Memory Management *
+;* The protected mode application has access to the full *
+;* address space of the machine, and a memory manager is *
+;* provided that duplicates the functions of the Dos *
+;* memory manager over the entire address space of the *
+;* machine. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 08/08/90 earleh DOSX and Client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 03/23/90 davidw Added the reflecting of it 23h, ^C. *
+;* 11/09/89 jimmat Added more IOCTL 0Dh support for Windows. *
+;* 10/11/89 jimmat Changed hooking of Int 1,2,3 under a *
+;* debugger to work better with CVW. *
+;* 07/28/89 jimmat Fixed Int 21h/56h (Rename), fixed Int 21 *
+;* calls that just returned a pointer. *
+;* 06/07/89 jimmat Fixed length of FCB moves and special *
+;* case hooking Int 1Eh. *
+;* 05/30/89 jimmat Completed Int 21h/5Ah processing. *
+;* 04/25/89 jimmat Added support for undocumented INT 21h *
+;* 5Fh/05 DOS call. *
+;* 04/12/89 jimmat Allow one level of nested DOS calls to *
+;* support PM critical error handlers *
+;* 04/10/89 jimmat Supported INT 21h/5Eh & 5Fh--also small *
+;* clean-up of the dosentry/dosexit code. *
+;* 04/05/89 jimmat Fixed MOVDAT FCB length check. *
+;* 04/04/89 jimmat Stop reflecting real mode software ints *
+;* to protect mode. This is how Windows/386 *
+;* works, and it fixes a problem with DOS *
+;* networks since the real mode redirector *
+;* expects to have access to the DOS stack, *
+;* not a DOS extender interrupt stack frame. *
+;* 03/28/89 jimmat Incorporated bug fixes from GeneA *
+;* 03/17/89 jimmat Some code clean-up and debugging checks *
+;* 03/15/89 jimmat Minor changes to run child in ring 1 *
+;* 02/22/89 (GeneA): removed dead code and data left over *
+;* from the Murray Sargent/SST days. *
+;* 02/22/89 (GeneA): moved handlers for all interrupts but *
+;* Int 21h to DXINTR.ASM. Fixed problem with re-entrancy *
+;* caused when the other interrupts were executed while *
+;* in DOS. (int 15h was causing the specific problem). *
+;* 02/14/89 (GeneA): fixed bug in IntExitMisc. Was storing *
+;* return value in rmrg.xes, changed to pmrg.xes. *
+;* 02/10/89 (GeneA): changed Dos Extender from small model to *
+;* medium model. *
+;* 12/01/88 (GeneA): Changed name of mentry and mexit to *
+;* IntEntryMouse and IntExitMouse. Added functions *
+;* IntEntryMisc and IntExitMisc to handle entry and *
+;* exit processing for BIOS INT 15h. *
+;* 11/20/88 (GeneA): modified DFSetVector so that is checks to *
+;* see if a vector has already been hooked before saving *
+;* the old value in the real mode interrupt vector shadow *
+;* buffer. *
+;* 09/15/88 (GeneA): created by extracting code from the *
+;* SST debugger modules DOSXTND.ASM, VIRTMD.ASM, *
+;* VRTUTIL.ASM, and INTERRPT.ASM *
+;* *
+;****************************************************************
+
+ .286p
+ .287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+include bop.inc
+include dpmi.inc
+include hostdata.inc
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+; STKSTR -- stack layout structure for user registers in the pmrg and
+; rmrg arrays in the data segment DXDATA. pmrg is an exact replica of the pm
+; user registers on entry to the Dos Extender (DE). rmrg is a translated
+; version used to communicate with the real-mode world. The rmrg array is
+; initially set equal to the pm user values by the instructions push ds, push
+; es, pusha. The pmrg array es and ds are inevitably set equal to the pm user
+; values and its general register values are defined if data translations may be
+; required (int-10/21).
+
+stkstr struc ;Level-0 Stack structure. bp is set = sp here
+xdi dw ?
+xsi dw ?
+xbp dw ?
+xasp dw ? ;Alternate sp
+xbx dw ?
+xdx dw ?
+xcx dw ?
+xax dw ? ;pusha pushes xax to xdi
+xes dw ?
+xds dw ?
+stkstr ends
+
+
+; -------------------------------------------------------
+; This structure describes the EXEC parameter block used
+; by MS-DOS function 4Bh.
+
+execblk struc ;INT-21 ah=4bh EXEC parameter block
+evrnmt dw ? ;Paragraph of environment string to be passed
+cmdptr dd ? ;Ptr to command line to be placed at PSP+80h
+fcb1ptr dd ? ;Ptr to default FCB to be passed at PSP+5ch
+fcb2ptr dd ? ;Ptr to default FCB to be passed at PSP+6ch
+xsssp dd ? ;Initial program stack ss:sp
+xcsip dd ? ;Program entry point cs:ip
+execblk ends
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn gtpara:NEAR
+ extrn RMIntr24:NEAR
+ extrn ParaToLinear:NEAR
+ extrn GetSegmentAddress:NEAR
+ extrn RMIntrEntryVector:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn SelOff2SegOff:NEAR
+ extrn ParaToLDTSelector:NEAR
+ extrn MouseInterruptHandler:NEAR
+ extrn GetIntrVector:NEAR, PutIntrVector:NEAR
+ extrn AllocateXmemBlock:NEAR, FreeXmemBlock:NEAR, ModifyXmemBlock:NEAR
+ifdef WOW_x86
+ extrn NSetSegmentDscr:FAR
+endif
+ extrn ChildTerminationHandler:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn fDebug:BYTE
+ extrn selGDT:WORD
+ extrn selIDT:WORD
+ extrn idCpuType:WORD
+ extrn selDOSScr:WORD
+ extrn segPSPChild:WORD
+ extrn rglpfnRmISR:DWORD
+ extrn RmHwIsr:DWORD
+ extrn PMInt24Handler:DWORD
+ extrn lpfnUserMouseHandler:DWORD
+ extrn npXfrBuf0:WORD, npXfrBuf1:WORD
+ extrn rgbXfrBuf0:BYTE, rgbXfrBuf1:BYTE
+IFDEF WOW_x86
+ extrn FastBop:FWORD
+ENDIF
+
+IFDEF ROM
+ extrn segDXData:WORD
+ENDIF
+
+ extrn segDXCode:word
+ extrn segCurrentHostData:word
+; -------------------------------------------------------
+; General DOS EXTENDER Variables
+; -------------------------------------------------------
+
+pmdta dw 2 dup(?) ;PM DTA. Used for getting dir info
+
+EntryFlag db -1 ;Flag to check for nested DOS interrupts
+
+rcount dw ? ;Remaining requested file byte count to read/write
+ccount dw ? ;Current count of total read/written
+cbSector dw ? ;sector size for IOCTL/0D/41&61 transfers
+lpTrackData dd ? ;pointer to track data for IOCTL/0D/41&61 transfers
+
+;=======================================================================
+;Keep from here thru Iend in the following order:
+
+ align 2
+Ibegin label byte ;start of PMIntrDos nested 'Instance' data
+
+ dw 128 dup(?) ;Extra stack for real mode
+
+rmrg stkstr <> ;Corresponding real-mode set
+rmivip dw ? ;Real-mode interrupt-vector offset
+rmivcs dw ? ;Real-mode interrupt-vector segment
+rmivfl dw ? ;Real-mode interrupt flags to be used
+rmroff dw ? ;Real-mode return address offset
+rmrseg dw ? ;Real-mode return address segment
+rmflags dw ? ;flags
+
+pmrg stkstr <> ;Protected-mode user registers
+
+ public pmusrss, pmusrsp
+
+pmusrsp dw ? ;PM user sp
+pmusrss dw ? ;PM user ss
+
+enxseg dw ? ;transfer segment used on dos function exit
+
+Iend label byte ;end of PMIntrDos nested 'Instance' data
+;=======================================================================
+
+ILENGTH equ Iend - Ibegin ;length of instance data
+
+Isave db ILENGTH dup (?) ;instance data save area
+
+if DEBUG
+ extrn fTraceDOS:WORD
+ extrn TrapDOS:WORD
+endif
+
+DXDATA ends
+
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn segDXData:WORD
+ extrn selDgroup:WORD
+ENDIF
+
+DXCODE ends
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+ extrn segDXCodePM:WORD
+
+IFNDEF ROM
+ extrn segDXDataPM:WORD
+ENDIF
+
+; -------------------------------------------------------
+; Dos Function Parameter Tables
+; -------------------------------------------------------
+
+; The Dos Extender provides parameter buffering and translation
+; on entry to and exit from Dos system calls. The following table
+; is used to describe the operations to be performed for this process.
+; The basic idea is that there is an entry parameter code and an
+; exit paramter code. This code describes whether any data buffering
+; is necessary. If buffering is necessary, it describes the nature of
+; the data being transferred (e.g. ASCIIZ string, FCB, etc.) which is
+; used to determine the transfer length. The entry/exit code also
+; describes which temporary buffer to use for the transfer, and which
+; user register (e.g. DS:DX, ES:BX, etc) points to the data to transfer.
+;
+; The data transfers described by this table is sufficient to handle
+; the majority of the Dos system calls. However, any Dos call which
+; requires the transfer of more than one buffer of data, or which requires
+; that additional processing be performed other than simply copying a
+; buffer is handled with special case code.
+
+
+; The following symbols define various parts of the entry/exit codes
+; used in the parameter table.
+
+; Nibble 0 of a transfer word is a code that specifies the length of the
+; transfer as follows:
+
+; 0 no xfer
+; 1 FCB to/from (f,10,11,12,13,16,17,23,24,29
+; 2 PTR$ from (1b,1c,34)
+; 3 ASCZ to/from (39,3a,3b,3c,3d,41,43,4b,4e,56
+; 5a,5b)
+; 4 DOLLAR terminated to DOS (9)
+; 5 AX$ from (3f)
+; 6 CX$ to/from (3f,40,44)
+; 7 KEYBUF to/from DOS (a)
+; 8 Country INFO (34) to/from (38)
+; 9 EXTERR (22) to (5d)
+; a DIRectory (64) from (47)
+; b EXEC parm (14) to (4b)
+; c file MATCH (43) to/from (4e,4f)
+; d CMND line (128) to (29)
+; e PALETTE (17) to (int-10/1002)
+; f VBIOS (64) from (int-10/1b)
+;
+;
+; Nibble 1 specifies the segment value transferred as follows (DOS entries
+; affected are listed in parentheses):
+;
+; Segment ptr
+;
+; 0 none
+; 1 ds:dx to/from DOS (9,a,f,10,11,12,13,14,16,17,21,22,23,24,25,27
+; 28,39,3a,3b,3c,3d,3f,40,41,43,44,4b,4e,4f,56
+; 5a,5b)
+; 2 DTA to/from (11,12,14,15,21,22,27,28,4e)
+; 3 ds:bx from (1b,1c) (Allocation table info)
+; 4 ds:si to/from (29,47) (Parse FN, get dir)
+; 5 es:di to/from (29,56) (Parse FN, rename)
+; 6 es:bx to/from (2f,35,4b) (Get DTA, intvct, EXEC)
+; 7 es to (49,4a) (RAM allocation)
+;
+; Byte 1 (high byte) on has meanings:
+;
+; bit 0 = 1 (A0) use small data area 0 (limited to 60h bytes)
+; bit 1 = 1 (A1) use large area 1 (4K)
+; bit 2 = 1 (RP) Real/Protect mode transfer needed (for int 21 ah = 3f/40)
+; bit 7 = 1/0 (EX) exitcd/entrycd
+
+
+entrycd record EnArea:4,EnSegCod:4,EnLenCod:4
+exitcd record ExArea:8,ExSegCod:4,ExLenCod:4
+
+;Length codes:
+
+FCB equ 1
+PTR$ equ 2 ;only supports DSBX & ESBX for now (and DSSI if DBCS)
+ASCZ equ 3
+DOL equ 4
+AX$ equ 5
+CX$ equ 6
+KYB equ 7
+INFO equ 8 ;Constant length transfers from here down vvvvvvvv
+EXTERR equ 9
+DIR equ 0Ah
+EXEC equ 0Bh
+MTCH equ 0Ch
+CMD equ 0Dh
+PALETTE equ 0Eh
+VBIOS equ 0Fh ;Constant length transfers from here up ^^^^^^^^^^
+
+;Segment codes:
+
+DSDX equ 1
+DTA equ 2
+DSBX equ 3
+DSSI equ 4
+ESDI equ 5 ;Also used by int-10/ah=1bh
+ESBX equ 6 ;Also used by int-10/ah=1ch
+ES$ equ 7
+ESDX equ 8 ;Used by int-10/ah=10,12, int-33/ah=22,23
+ESBP equ 9 ;Used by int-10/ah=11,13
+
+
+;RAM area codes:
+
+A0 equ 1
+A1 equ 2
+RP equ 4
+EX equ 80h
+
+
+pmrmxfr entrycd <> ;0 - Program Terminate
+ exitcd <>
+ entrycd <> ;1 - Keyboard Input
+ exitcd <>
+ entrycd <> ;2 - Display output
+ exitcd <>
+ entrycd <> ;3 - Auxiliary Input
+ exitcd <>
+ entrycd <> ;4 - Auxiliary Output
+ exitcd <>
+ entrycd <> ;5 - Printer Output
+ exitcd <>
+ entrycd <> ;6 - Direct Console I/O
+ exitcd <>
+ entrycd <> ;7 - Direct Console Input Without Echo
+ exitcd <>
+ entrycd <> ;8 - Console Input Without Echo
+ exitcd <>
+ entrycd <A1,DSDX,DOL> ;9 - Print string
+ exitcd <>
+ entrycd <A1,DSDX,KYB> ;0A - Buffered Keyboard Input
+ exitcd <EX+A0,DSDX,KYB>
+ entrycd <> ;0B - Check Standard Input Status
+ exitcd <>
+ entrycd <> ;0C - Clear Kbd Buffer and Invoke Kbd Function
+ exitcd <>
+ entrycd <> ;0D - Disk Reset
+ exitcd <>
+ entrycd <> ;0E - Select Disk
+ exitcd <>
+ entrycd <> ;0F - Open File ** Unsupported! **
+ exitcd <>
+ entrycd <> ;10 - Close File ** Unsupported! **
+ exitcd <>
+ entrycd <A0,DSDX,FCB> ;11 - Search for First Entry
+ exitcd <EX+A1,DTA,FCB>
+ entrycd <A0,DSDX,FCB> ;12 - Search for Next Entry
+ exitcd <EX+A1,DTA,FCB>
+ entrycd <A0,DSDX,FCB> ;13 - Delete File
+ exitcd <>
+ entrycd <> ;14 - Sequential Read ** Unsupported! **
+ exitcd <>
+ entrycd <> ;15 - Sequential Write ** Unsupported! **
+ exitcd <>
+ entrycd <> ;16 - Create File ** Unsupported! **
+ exitcd <>
+ entrycd <A0,DSDX,FCB> ;17 - Rename File
+ exitcd <>
+ entrycd <> ;18 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;19 - Current Disk
+ exitcd <>
+ entrycd <A1,DSDX,> ;1A - Set Disk Transfer Address
+ exitcd <>
+ entrycd <> ;1B - Allocation Table Info
+ exitcd <,DSBX,PTR$>
+ entrycd <> ;1C - Alloc Table Info for Specific Device
+ exitcd <,DSBX,PTR$>
+ entrycd <> ;1D - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;1E - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;1F - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;20 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;21 - Random Read ** Unsupported! **
+ exitcd <>
+ entrycd <> ;22 - Random Write ** Unsupported! **
+ exitcd <>
+ entrycd <> ;23 - File Size ** Unsupported! **
+ exitcd <>
+ entrycd <> ;24 - Set Relative Record Field ** Unsupported! **
+ exitcd <>
+ entrycd <,DSDX,> ;25 - Set Interrupt Vector (0/ds:dx/0)
+ exitcd <>
+ entrycd <,DSDX,> ;26 - Create new PSP
+ exitcd <>
+ entrycd <> ;27 - Random Block Read ** Unsupported! **
+ exitcd <>
+ entrycd <> ;28 - Random Block Write ** Unsupported! **
+ exitcd <>
+ entrycd <A0,DSSI,ASCZ> ;29 - Parse Filename
+ exitcd <EX+A1,ESDI,FCB>
+ entrycd <> ;2A - Get Date
+ exitcd <>
+ entrycd <> ;2B - Set Date
+ exitcd <>
+ entrycd <> ;2C - Get Time
+ exitcd <>
+ entrycd <> ;2D - Set Time
+ exitcd <>
+ entrycd <> ;2E - Set/Reset Verify Switch
+ exitcd <>
+ entrycd <> ;2F - Get Disk Transfer Address
+ exitcd <EX+A0,ESBX,>
+ entrycd <> ;30 - Get DOS Version Number
+ exitcd <>
+ entrycd <> ;31 - Terminate and Stay Resident
+ exitcd <>
+ entrycd <> ;32 - Get Drive Parameter Block
+ exitcd <,DSBX,PTR$>
+ entrycd <> ;33 - Ctrl-Break Check
+ exitcd <>
+ entrycd <> ;34 - Get InDOS flag address
+ exitcd <,ESBX,PTR$>
+ entrycd <> ;35 - Get Interrupt Vector
+ exitcd <EX,ESBX,>
+ entrycd <> ;36 - Get Disk Free Space
+ exitcd <>
+ entrycd <> ;37 - Used Internally by DOS
+ exitcd <>
+ entrycd <A1,DSDX,> ;38 - Set/Get Country Dependent Info
+ exitcd <EX+A1,DSDX,INFO>
+ entrycd <A0,DSDX,ASCZ> ;39 - MKDIR
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;3A - RMDIR
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;3B - CHDIR
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;3C - Create a File
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;3D - Open a File
+ exitcd <>
+ entrycd <> ;3E - Close a File Handle
+ exitcd <>
+ entrycd <RP,DSDX,> ;3F - Read from a File or Device
+ exitcd <EX+RP,DSDX,AX$>
+ entrycd <RP,DSDX,CX$> ;40 - Write to a File or Device
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;41 - Delete a File from a Specified Directory
+ exitcd <>
+ entrycd <> ;42 - Move File Read/Write Pointer
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;43 - Change File Mode
+ exitcd <>
+ entrycd <> ;44 - I/O Control for Devices
+ exitcd <> ;See ioctlw for write
+ entrycd <> ;45 - Duplicate a File Handle
+ exitcd <>
+ entrycd <> ;46 - Force a Duplicate of a File Handle
+ exitcd <>
+ entrycd <A0,DSSI,> ;47 - Get Current Directory
+ exitcd <EX+A0,DSSI,ASCZ>
+ entrycd <> ;48 - Allocate Memory
+ exitcd <>
+ entrycd <,ES$,> ;49 - Free Allocated Memory
+ exitcd <>
+ entrycd <,ES$,> ;4A - Modify Allocated Memory Blocks
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;4B - Load or Execute a Program (EXEC)
+ exitcd <>
+ entrycd <> ;4C - Terminate a Process
+ exitcd <>
+ entrycd <> ;4D - Get Return Code of a Sub-process
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;4E - Find First Matching File
+ exitcd <EX+A1,DTA, MTCH>
+ entrycd <A1,DTA,MTCH> ;4F - Find Next Matching File
+ exitcd <EX+A1,DTA, MTCH>
+ entrycd <,ESBX,> ;50 - Set current PSP (code restores bx)
+ exitcd <>
+ entrycd <> ;51 - Get current PSP
+ exitcd <>
+ entrycd <> ;52 - Get Pointer to SysInit Variables
+ exitcd <,ESBX,PTR$>
+ entrycd <A1,DSSI,DIR> ;53 - Set Drive Parameter Block
+ exitcd <>
+ entrycd <> ;54 - Get Verify Setting
+ exitcd <>
+ entrycd <,DSDX,> ;55 - Duplicate PSP
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;56 - Rename a File
+ exitcd <>
+ entrycd <> ;57 - Get/Set a File's Date and Time
+ exitcd <>
+ entrycd <> ;58 - Get/Set Allocation Strategy
+ exitcd <>
+ entrycd <> ;59 - Get Extended Error
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;5A - Create Temporary File
+ exitcd <EX+A0,DSDX,ASCZ>
+ entrycd <A0,DSDX,ASCZ> ;5B - Create New File
+ exitcd <>
+ entrycd <> ;5C - Lock/Unlock File Access
+ exitcd <>
+ entrycd <A0,DSDX,EXTERR> ;5D - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;5E - Network Machine Name/Printer Setup
+ exitcd <>
+ entrycd <> ;5F - Get/Make Assign-List Entry
+ exitcd <>
+ entrycd <> ;60 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;61 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;62 - Get PSP Address
+ exitcd <>
+ entrycd <> ;63 - Get Lead Byte Table ** Unsupported! **
+ exitcd <>
+ entrycd <> ;64 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;65 - Get Extended Country Info
+ exitcd <EX+A1,ESDI,CX$>; ** Only Partially Supported **
+ entrycd <> ;66 - Get/Set Code Page
+ exitcd <>
+ entrycd <> ;67 - Set Handle Count
+ exitcd <>
+ entrycd <> ;68 - Commit File
+ exitcd <>
+ entrycd <> ;69 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;6A - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;6B - Used Internally by DOS
+ exitcd <>
+ entrycd <A0,DSSI,ASCZ> ;6C - Extended Open File
+ exitcd <>
+
+MaxInt21 equ 06Ch ;max supported Int 21h function
+
+UnSupported entrycd <> ;for unsupported DOS calls
+ exitcd <>
+
+if DEBUG ;------------------------------------------------------------
+
+; Table of partially supported/unsupported/unknown Int 21h functions
+
+ifdef DBCS
+tblBad21 db 18h,1Dh,1Eh,1Fh,20h,37h,5Dh,60h,61h
+ db 64h,65h,6Ah,6Bh,0
+else
+tblBad21 db 18h,1Dh,1Eh,1Fh,20h,37h,5Dh,60h,61h,63h
+ db 64h,65h,6Ah,6Bh,0
+endif
+endif ;DEBUG --------------------------------------------------------
+;
+; For compatibility with WIN386, the following FCB calls are failed
+; unconditionally.
+;
+MIN_REAL_BAD_21 equ 0fh
+MAX_REAL_BAD_21 equ 28h
+
+tblRealBad21 db 0fh,10h,14h,15h,16h,21h,22h,23h,24h,27h,28h,0
+
+
+
+; Special codes for special INT 21h functions
+
+int215E entrycd <A0,DSDX,> ;5E/00 - Get Machine Name
+ exitcd <EX+A0,DSDX,ASCZ>
+ entrycd <A0,DSDX,ASCZ> ;5E/01 - Set Machine name
+ exitcd <>
+ entrycd <A0,DSSI,CX$> ;5E/02 - Set Printer Setup Str
+ exitcd <>
+ entrycd <A0,ESDI,> ;5E/03 - Get Printer Setup Str
+ exitcd <EX+A0,ESDI,CX$>
+
+int215F02 entrycd <A0,DSSI> ;5F/02 - Get Redir List Entry
+ exitcd <EX+A0,DSSI,ASCZ>
+ entrycd <A0,DSSI,ASCZ> ;5F/03 - Set Redir List Entry
+ exitcd <>
+ entrycd <A0,DSSI,ASCZ> ;5F/04 - Cancel Redirection
+ exitcd <>
+ entrycd <A0,DSSI> ;5F/05 - Get Redir List Entry
+ exitcd <EX+A0,DSSI,ASCZ>
+
+ifdef DBCS
+int2163 entrycd <> ;63/00 - Get Lead Byte Table Entry
+ exitcd <,DSSI,PTR$>
+endif ; DBCS
+
+int21esdi entrycd <A1,ESDI,ASCZ> ;56 & 5F/02&03&05 eXtra buffer
+ exitcd <EX+A1,ESDI,ASCZ>
+
+
+;
+; We only use the entry code from the following. If we don't
+; we trash the stack in applications like borland c++
+;
+int21pfn entrycd <A1,ESDI,FCB> ;29, fcb buffer
+ exitcd <>
+
+
+; Additional tables for run time support associated with register
+; translation and buffering.
+
+; 8 9 a b c d e f
+; INFO EXTERR DIR EXEC MATCH CMND PALETTE VBIOS
+mvcnt db 34, 22, 40h, 0Eh, 43, 80h, 17, 64
+
+; 1 2 3 4 5 6 7 8 9
+; DSDX, DTA, DSBX, DSSI, ESDI, ESBX, ES$, esdx, esbp
+regoffs dw xdx, 0, xbx, xsi, xdi, xbx, 0, xdx, xbp
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Main Program
+ page
+; -------------------------------------------------------
+; MAIN PROGRAM
+; -------------------------------------------------------
+
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntrDos -- This function is the main handler for int 21h calls
+; that require special case processing. Most interrupts
+; go through the interrupt reflector (PMIntrReflector in
+; dxintr.asm). DOS int 21h interrupts are vectored here.
+;
+; This routine performs any register manipulation, data buffering
+; etc. needed to pass the interrupt parameters from the
+; protected mode caller to the real mode handler. Register
+; manipulation and data transfers can occur either on entry
+; to the interrupt handler or on exit from the interrupt
+; handler (to fix up return values.)
+;
+; Input: normal registers for Dos calls
+; Output: normal register returns for Dos calls
+; Errors: normal Dos errors
+; Uses: In general, all registers are preserved. For interrupts where
+; register translation or buffering occurs, some registers may
+; be changed, depending on the interrupt and its parameters.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrDos
+
+PMIntrDos proc far
+
+ push ds
+ push ax
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ assume ds:DGROUP
+ mov ax,ss
+ mov pmusrss,ax
+ mov ax,sp
+ add ax,4
+ mov pmusrsp,ax
+ pop ax
+
+ DEBUG_TRACE DBGTR_ENTRY, 21h, ax, 0
+
+ FBOP BOP_DPMI,XlatInt21Call,FastBop
+; If we get here, the api wasn't translated. The translation code will
+; simulate the iret
+ assume ds:nothing
+
+NotXlated:
+ cld ;Code assumes direction flag is cleared
+
+if DEBUG ;------------------------------------------------------------
+
+; Trace levels: 1 - print AX on all calls except 3F, 40, 42 (read, write,
+; seek--Windows does so many of these...)
+; 2 - like level 1, but includes 3F, 40, 42
+; 3 - print all regs on all calls
+
+ push bp
+ push ds ;tracing of DOS calls wanted?
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ mov bp,fTraceDOS
+ pop ds
+ assume ds:NOTHING
+ or bp,bp
+ jnz @f
+
+notracefileio:
+ jmp notracedos
+@@:
+ cmp bp,2
+ jae @f
+ cmp ah,3Fh
+ jz notracefileio
+ cmp ah,40h
+ jz notracefileio
+ cmp ah,42h
+ jz notracefileio
+@@:
+ Trace_Out "I21-#AX",x
+
+ cmp bp,2 ;how much detail wanted?
+ jbe tracestr
+
+ Trace_Out " bx #BX cx #CX dx #DX si #SI di #DI",x
+ push ax
+ push bx
+ mov ax,ds
+ mov bx,es
+ Trace_Out " ds #AX es #BX",x
+ pop bx
+ pop ax
+
+tracestr:
+
+ cmp ah,3Dh
+ jz @f
+ cmp ah,5Bh
+ jz @f
+ cmp ah,5Ah
+ jz @f
+ cmp ah,43h
+ jz @f
+ cmp ah,41h
+ jz @f
+ cmp ah,3Ch
+ jz @f
+
+ cmp ah,3Fh
+ jnz notraceread
+ cmp bp,1
+ je notraceread
+ push ax
+ mov ax,ds
+ Trace_Out " L #CX @ #AX:#DX",x
+ pop ax
+
+notraceread:
+ jmp short notracedos
+@@:
+ push ax
+ mov ax,ds
+ cmp bp,2
+ jbe @f
+ Trace_Out " " ;output end of line
+@@:
+ Trace_Out " ->@AX:DX<-",x
+ pop ax
+
+notracedos:
+ pop bp
+
+ push ds
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ cmp ah,byte ptr TrapDOS
+ jnz @f
+ or ah,ah
+ jz @f
+ Debug_Out "Trapped DOS call #ax"
+@@:
+ pop ds
+ assume ds:NOTHING
+
+endif ;DEBUG --------------------------------------------------------
+
+; Save PM user ds, es, and flags, and switch to DOS extender stack
+
+ push ax ;Save caller's AX
+ push bp
+ mov bp,sp ;[bp] = bp ax ip cs fl - 286 int gates assumed
+ ; 0 2 4 6 8
+ push es
+
+ mov es,selDgroupPM ;Address our DGROUP
+ assume es:DGROUP
+
+
+; Check for nested DOS interrupts--this code was written with the assumption
+; that we would never be reentered, but it turns out that we may need to
+; support nested DOS calls from a critical error handler. If this is one
+; of those ultra-rare occasions, save our previous 'instance' data in Isave.
+
+ inc EntryFlag ;The normal case will be to jump
+ jz @f
+
+ push cx ;Being reentered, save last instance data
+ push di
+ push si
+
+ mov cx,ILENGTH ;NOTE!!! The next movs has
+ mov si,offset DGROUP:Ibegin ; an es override, if ints
+ mov di,offset DGROUP:Isave ; are enabled, an interrupt
+ rep movs byte ptr [di],byte ptr es:[si] ; on this instr can 'forget'
+ ; about the es override with
+ pop si ; some processors
+ pop di
+ pop cx
+@@:
+
+; Start saving callers state.
+
+ mov pmrg.xds,ds ;Save PM user ds
+ mov ax,[bp+8] ;Get ax = flags when int occurred
+ mov rmflags,ax ;Store flags for real-mode handler
+
+ pop pmrg.xes ;Save PM user es
+
+ pop bp
+ pop ax ;Recover user ax. [sp] = ip cs fl
+
+; At this point all general registers (but not ES) have the user's values
+
+ push es ;Address DGROUP, user's DS already
+ pop ds ; saved in pmrg.xds
+ assume ds:DGROUP
+
+ mov pmusrss,ss ;Save PM user stack ptr
+ mov pmusrsp,sp ;[sp] = ds ip cs fl
+ ; 0 2 4 6
+ mov pmrg.xsi,si ;Save PM si since need to use before pusha
+
+ push ds ;Switch to rmrg stack for this routine
+ pop ss
+ mov sp,offset DGROUP:rmflags ;PM flags already on stack
+ FSTI ;We don't really need interrupts disabled
+
+
+; Setup iret frames for iret'ing to real-mode handler and for that handler
+; returning to the DOS extender
+
+ pop si ;Get rmflags
+ and si,not 4100h ;Kill NT, TF
+ push si ;Push flags for iret to BackFromDOS
+ push segDXCodePM ;Push return cs:ip for iret
+ push offset BackFromDOS
+
+ and si,not 0200h ;Kill IF
+ push si ;Push flags for iret to real-mode handler
+
+ sub sp,4 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ mov ax,SEL_RMIVT OR STD_RING
+ mov es,ax
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+
+
+; Setup protected mode and real mode copies of registers.
+
+ mov si,pmrg.xsi ;Restore si
+ push ds ;Save space for real mode ds
+ push es ; and es
+ pusha ;Save user general registers
+
+ mov si,offset DGROUP:rmrg ;Copy user values to PM set for
+ mov di,offset DGROUP:pmrg ; reference (real-mode set may change)
+ mov cx,8 ;8 general registers (es and ds already stored)
+ rep movsw
+
+IFDEF ROM
+ mov ax,segDXData
+ELSE
+ mov ax,segDXDataPM ;ax = DOS extender real-mode dgroup segment
+ENDIF
+ mov rmrg.xds,ax ;Default real-mode data segments
+ mov rmrg.xes,ax ; (dosentry may change them)
+
+ mov ax,rmrg.xax
+
+if DEBUG ;------------------------------------------------------------
+
+; Check for partially supported/unsupported/unknown DOS calls
+
+ cmp ah,0DCh ;krnl286 is doing this now, quit
+ jz goodfunc ; complaining about it
+
+ cmp ax,5D0Ah ;Set Extended Error is the only
+ jz goodfunc ;5Dh function handled properly!
+
+ cmp ah,MaxInt21 ;is the request within our range?
+ ja badfunc
+
+ mov bx,offset DXPMCODE:tblBad21
+@@:
+ cmp ah,cs:[bx]
+ jb goodfunc
+ jz badfunc
+ inc bx
+ jmp short @b
+
+badfunc: Trace_Out "Possible Unsupported DOS Call! AX=#AX"
+
+goodfunc:
+
+endif ;DEBUG --------------------------------------------------------
+
+; Check for FCB calls that we fail unconditionally (because WIN386 does.)
+
+ cmp ah,MIN_REAL_BAD_21
+ jb goodfunc1
+ cmp ah,MAX_REAL_BAD_21
+ ja goodfunc1
+
+mov bx,offset DXPMCODE:tblRealBad21
+@@:
+ cmp ah,cs:[bx]
+ je badfunc1
+ inc bx
+ cmp byte ptr cs:[bx],0
+ jz goodfunc1 ; Ran off end of table.
+ jmp short @b
+
+badfunc1:
+
+if DEBUG
+ Debug_Out "Unsupported DOS Call! AX=#AX"
+endif ;DEBUG
+
+ or byte ptr rmflags,1 ; Set CF
+ call xfrflg
+ jmp LeaveDOSExtender
+
+goodfunc1:
+
+
+; int 21 entry register translations and data transfers
+
+ cmp ah,25h ;Set interrupt vector ?
+ jnz @f
+ xor ah,ah
+ mov cx,pmrg.xds
+ mov dx,pmrg.xdx
+ call DFSetIntrVector ;perform special case processing
+ jmp LeaveDOSExtender ;No real-mode cycle needed
+@@:
+ cmp ah,35h ;Get interrupt vector ?
+ jnz @f
+ xor ah,ah
+ call DFGetIntrVector
+ mov rmrg.xbx,dx
+ mov pmrg.xes,cx
+ jmp LeaveDOSExtender
+@@:
+ cmp ah,00h ;old style DOS Exit call?
+ jnz @f
+ call DosExitCall ;sets CY if it handles the call, otherwise
+ jnc @f ; do it normally...
+ jmp LeaveDOSExtender
+@@:
+ ;
+ ; Handle terminate specially. We mess with the PSP here to set
+ ; up a terminate vector we like. We don't do anything special for
+ ; TSR (31h)
+ ;
+ cmp ah,4ch ; terminate?
+ jnz @f
+
+ call TerminateProcess
+@@:
+
+ cmp ah, 5dh ; check for unsupported 5d codes
+ jnz short @f
+ cmp al, 0ah
+ jz short @f
+ jmp LeaveDOSExtender
+@@:
+
+ mov rcount,0 ;Default no remaining bytes to read/write
+ mov ccount,0 ;No bytes read or written yet
+
+ cmp ah,3Fh ;If read
+ jz @f
+ cmp ah,40h ; or write,
+ jnz TransferLoop
+@@:
+ mov cx,pmrg.xcx ; initialize remaining count = requested value
+ mov rcount,cx
+
+
+; Start of loop for doing large read/write transfers
+
+TransferLoop:
+
+ call dosentry ;Do selector translations, data buffering
+
+
+; Come here after entry register translations and data transfers are complete
+
+ SwitchToRealMode ;Switch back to real mode. ds = es = rm dgroup
+ ;Stack is at same place in memory
+
+; Set registers to possibly translated values and iret to real-mode DOS.
+; DOS then irets to BackFromDOS.
+
+ popa ;Set appropriate general register values
+
+ pop es
+ pop ds
+
+ public GoingToDOS
+
+GoingToDOS: ;for debugging, etc.
+
+ iret ;invoke real mode DOS
+
+ assume ds:NOTHING, es:NOTHING, ss:NOTHING
+
+
+; Return here from real-mode interrupt handler (DOS)
+
+ public BackFromDOS
+
+BackFromDOS:
+
+ pushf ;Save return flags (to rmflags)
+ cld ; (better safe than sorry)
+
+ push cs ;Push return cs:ip for multiple xfers
+ push offset BackFromDOS
+
+ sub sp,2*3 ;Bypass room for iret to interrupt handler
+ ; (to keep stack layout the same as on entry)
+
+ push ds ;Save register set
+ push es
+ pusha
+
+IFDEF ROM
+ mov ax,ss ;SS already points to our data segment
+ mov ds,ax
+ELSE
+ mov ds,segDXData
+ENDIF
+ assume ds:DGROUP
+
+
+; "push" iret frame for real mode int 21 rtn in case we need to do it again
+
+ mov ax,rmflags
+ and ax,not 4300h ;Kill NT, TF, and IF
+ mov rmivfl,ax
+
+ xor ax,ax
+ mov es,ax
+ mov ax,word ptr es:[21h*4+2]
+ mov rmivcs,ax
+ mov ax,word ptr es:[21h*4]
+ mov rmivip,ax
+
+
+; Switch back to protected mode
+
+ SwitchToProtectedMode ;Switch back to protected mode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI ;Don't need ints disabled
+
+ call xfrflg ;Transfer relevant return flags over to pm iret frame
+
+ mov ax,pmrg.xax ;Recover AX from caller
+
+
+; Perform any int-21 selector translations, data buffering
+
+ call dosexit
+
+
+; Check for large xfers (Read File 3Fh, Write File 40h, some IOCTL 44h)
+
+ cmp rcount,0 ;Read/write more bytes?
+ jz TransferDone
+
+ mov cx,rmrg.xax ;Maybe. cx = count transferred (if 3Fh or 40h)
+ mov ax,pmrg.xax ;Restore entry code
+ mov rmrg.xax,ax
+
+ cmp ah,40h ;Write?
+ jnz @f
+
+ sub rcount,cx ;Yes. Written all originally requested?
+ jz TransferDone
+
+ cmp cx,rmrg.xcx ; No. Written all last specified?
+ jz @f ; Yes. Go do some more
+
+ mov ax,ccount ;A large write has failed! ccount has already
+ sub ax,rmrg.xcx ; been updated assuming success, back out the
+ add ax,cx ; attempted xfer amount, and add in
+ jmp short TransferCount ; the actual, then split
+@@:
+ jmp TransferLoop ;Yep (or 3Fh or 44h). Do another xfer
+
+TransferDone:
+ mov ax,ccount ;Multiple count xfer?
+ or ax,ax
+ jz LeaveDOSExtender
+
+TransferCount:
+ mov rmrg.xax,ax ;Yes update return amount
+ mov ax,pmrg.xcx
+ mov rmrg.xcx,ax ;Restore initial request count
+
+
+; Restore possibly translated registers and to return to pm caller
+
+ public LeaveDOSExtender
+
+LeaveDOSExtender:
+
+
+if DEBUG ;------------------------------------------------------------
+
+; If we're tracing DOS calls, say we're returning to the app...
+
+ cmp fTraceDOS,0
+ jz @f
+ cmp fTraceDOS,2
+ jae tracedoit
+ mov ax,pmrg.xax
+ cmp ah,3Fh
+ jz @f
+ cmp ah,40h
+ jz @f
+ cmp ah,42h
+ jz @f
+tracedoit:
+ Trace_Out " *"
+@@:
+
+endif ;DEBUG ---------------------------------------------------------
+
+ popa ;Restore possibly changed user registers
+
+ mov ss,pmusrss ;Restore pm user stack
+ mov sp,pmusrsp
+ assume ss:NOTHING
+
+ push pmrg.xds ;push user seg regs on user stack
+ push pmrg.xes
+
+ dec EntryFlag ;dec nested entry flag - normal case is to jmp
+ jnz NotNested
+ ;If this was a nested DOS call (from
+ push cx ; a critical error handler), restore
+ push si ; the state for the prior DOS call
+ push di ; which is still in progress
+
+ mov cx,ds ;make darn sure es -> DGROUP
+ mov es,cx
+
+ cld ;NOTE: we need to retreive
+ mov cx,ILENGTH ; all current user registers
+ mov di,offset DGROUP:Ibegin ; before moving this data
+ mov si,offset DGROUP:Isave
+ rep movsb
+
+ pop di
+ pop si
+ pop cx
+
+NotNested:
+ pop es ;restore user seg regs
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+
+ public DOSXiret
+DOSXiret: ;for debugging, etc.
+
+ iret ;return to caller
+
+PMIntrDos endp
+
+
+; -------------------------------------------------------
+; DOSENTRY -- This function performs buffering and register
+; translation for entry into MS-DOS functions.
+;
+; Input: AX: AX value at time of INT 21h
+; Output:
+; Errors:
+; Uses:
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public dosentry
+
+dosentry:
+ cmp ah,44h
+ jnz @F
+;
+; DOS call 44h (IOCTL) is weird enough to warrant special attention.
+; NOTE: We jump to IOCTLEnter here, and it returns to our caller.
+;
+ jmp IOCTLEnter ;several special cases here
+@@:
+ cmp ah,48h ;Allocate memory?
+ jmpz pmallc ;Yes, try for XRAM
+
+ cmp ah,49h ;free memory block
+ jmpz pmfree
+
+ cmp ah,4Ah ;modify memory block
+ jmpz pmmodb
+
+ cmp ah,26h ;Create new PSP?
+ jnz @f
+ mov si,rmrg.xdx ;yes, translate selector to paragraph
+ call gtpara
+ mov rmrg.xdx,ax
+ return
+@@:
+ cmp ah,53h ;Set drive parameter block?
+ jnz @f
+ push ax
+ mov si,pmrg.xes ;int 21h/53h has an extra parameter in ES:BP
+ call gtpara ; we change the selector to a segment, but
+ mov rmrg.xes,ax ; the segment had better already be in
+ pop ax ; conventional memory
+ jmp short dentr2b
+@@:
+ cmp ah,50h ;Set current PSP?
+ jnz dentr1
+ mov si,rmrg.xbx ;Yes. Translate selector to paragraph
+ call gtpara
+ mov rmrg.xbx,ax
+ return
+
+dentr1: cmp ah,55h ;Duplicate PSP?
+ jnz dentr2
+ mov si,rmrg.xbx ;Translate selector bx to paragraph
+ call gtpara
+ mov rmrg.xbx,ax
+ mov si,rmrg.xdx ; and dx also
+ call gtpara
+ mov rmrg.xdx,ax
+ return
+
+dentr2:
+ cmp ah,56h ;Rename?
+ jnz dentr2a
+ push ax ;rename has a second ASCIIZ buffer
+ push pmrg.xes ; pointed to by es:di -- move that
+ pop enxseg ; now, the ds:dx one below
+ mov ax,int21esdi
+ call gtarea ;let the 'standard' gtarea/movdat
+ mov dx,enxseg ; routines take care of it
+ call movdat
+ pop ax
+ jmp short dentr2b
+
+dentr2a:
+ cmp ah,5Fh ;Get/Make Assign-List Entry?
+ jne dentr2a1
+ call net5Fenter ; Yes, may require extra buffering
+ jmp short dentr2b
+
+dentr2a1:
+ cmp ah,29h ; parse filename?
+ jne dentr2b
+
+ push ax
+ push pmrg.xes
+ pop enxseg
+ mov ax,int21pfn
+ call gtarea
+ mov dx,enxseg
+ call movdat
+ pop ax
+;; jmp short dentr2b
+
+dentr2b:
+ call GetEntryCd ;ax = func entry code, di = ptr to entry cd
+
+ or ax,ax ;Entry code specify something to do?
+ rz
+
+ cmp byte ptr pmrg.xax+1,1Ah ;Store DTA?
+ jnz dentr3
+
+ mov pmdta,dx ; Yes. Save it for data returns
+ push pmrg.xds
+ pop pmdta+2
+ jmp short dentr4
+
+dentr3: cmp byte ptr pmrg.xax+1,4Bh ;EXEC program?
+ callz dosxec
+
+; DENTR4 - enter with ax = entrycd/exitcd. Translate ptr's for real-
+; mode calls and transfer any data indicated.
+
+dentr4: push pmrg.xds
+ pop enxseg
+ call gtarea ;Get es:di = area for transfer
+ rz ;Something to xfer?
+ mov dx,enxseg ;Yes. Fall thru to movdat
+
+ errnz <movdat-$>
+
+
+; -------------------------------------------------------
+; MOVDAT -- This routine performs the buffer transfer
+; for entry into or exit from an MS-DOS function. The data
+; is copied from DX:SI to ES:DI. The code in CX determines
+; the type of data being transferred, which is used to determine
+; the length.
+;
+; Input: DX:SI - far pointer to source buffer
+; ES:DI - far pointer to destination buffer
+; CX - transfer length code
+; Output: none
+; Errors: none
+; Uses: AX, BX, CS, SI, DI modified
+
+ assume ds:DGROUP,es:NOTHING,ss:DGROUP
+ public movdat
+
+movdat:
+ push ds
+ mov bx,cx ;Simple count?
+ sub bl,INFO
+ jc movda2
+ cmp bl,PALETTE-INFO ;Yes. Use pm es?
+ jc movda0
+ mov dx,pmrg.xes ;Yes
+movda0: mov cl,mvcnt[bx] ;cx = count
+movda1: mov ds,dx
+
+if DEBUG ;------------------------------------------------------------
+
+ push ax
+ mov ax,es
+ lsl ax,ax
+ sub ax,di
+ jc movbad
+ cmp ax,cx
+ jnc @f
+movbad:
+ Debug_Out "Movdat: copy beyond end of dest seg!"
+@@:
+ pop ax
+
+endif ;DEBUG --------------------------------------------------------
+
+movd1a: rep movsb ;Move data
+ pop ds
+ return
+
+movda2: cmp cl,CX$ ;Use pmrg.xcx?
+ jnz movda3
+ mov cx,rmrg.xcx ;cx usually = pmrg.xcx, but in any event
+ ; cx < CB_XFRBUF1
+movd21: add ccount,cx
+ jmp short movda1
+
+movda3: mov ah,0
+ cmp cl,ASCZ
+ jz movda4
+ cmp cl,DOL
+ jnz movda5
+ mov ah,"$"
+movda4: mov ds,dx
+movd42: lodsb
+ stosb
+ cmp al,ah
+ jnz movd42
+ pop ds
+ return
+
+movda5: cmp cl,AX$ ;Use rmrg.xax?
+ jnz movda6
+ mov cx,rmrg.xax ;Yes (only occurs for ah=3fh - read - on exit)
+ jmp short movd21
+
+movda6:
+ cmp cl,FCB
+ jnz movda7
+ mov ds,dx
+ mov cl,byte ptr ds:[si]
+ cmp cl,0ffh ;standard or extended FCB?
+ mov cx,37 ;standard FCB len
+ jnz movd1a
+ mov cx,44 ;extended FCB len
+ jmp short movd1a
+
+movda7: ;KYB remains
+ pop ds
+ return
+
+
+; -------------------------------------------------------
+; DOSEXIT -- This function is called on exit from the MS-DOS
+; functions to perform any data buffering and register translation
+; needed.
+;
+; Input: AX: AX value at time of INT 21h
+; Output:
+; Errors:
+; Uses:
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public dosexit
+
+dosexit:
+ cmp ah,44h
+ jnz @F
+;
+; DOS call 44h (IOCTL) is weird enough to warrant special attention.
+; NOTE: We jump to IOCTLExit here, and it returns to our caller.
+;
+ jmp IOCTLExit ;several special cases here
+@@:
+ cmp ah,51h ;Get current PSP?
+ jz dose0a
+ cmp ah,62h ;Get PSP address?
+ jnz dose00
+dose0a:
+ mov ax,rmrg.xbx ;Yes. Translate segment to selector
+ mov bx,STD_DATA
+ call ParaToLDTSelector
+ mov rmrg.xbx,ax
+ return
+
+dose00: cmp ah,2fh ;Get current DTA?
+ jnz dose01
+ mov ax,pmdta ;Yes. Load PM DTA into caller's registers
+ mov rmrg.xbx,ax
+ mov ax,pmdta+2
+ verr ax ; if the dta selector is no longer valid,
+ jz @f ; return the NULL selector instead (so we
+ xor ax,ax ; don't GP fault in DOSX).
+@@: mov pmrg.xes,ax
+ return
+
+dose01: cmp ah,55h ;Duplicate PSP?
+ jnz dosex1
+ mov ax,rmrg.xbx ;Yes, translate segments to selectors
+ mov bx,STD_DATA
+ call ParaToLDTSelector
+ mov rmrg.xbx,ax
+ mov ax,rmrg.xdx
+ mov bx,STD_DATA
+ call ParaToLDTSelector
+ mov rmrg.xdx,ax
+ return
+
+dosex1: cmp ah,56h ;Rename?
+ jnz dosex2
+ push pmrg.xdi ;Rename has a second pointer in ES:DI--we
+ pop rmrg.xdi ; need to restore DI here, DX below
+ jmp short dosex3
+
+dosex2: cmp ah,5Fh ;Get/Make Assign-List Entry?
+ callz net5Fexit ; Yes, extra buffering may be needed
+
+dosex3:
+ call GetEntryCd ;ax=func entry code, di=ptr to entry code
+
+ call rstreg ;Restore entry register?
+ jz dosex6
+
+ cmp byte ptr pmrg.xax+1,29h ;Yes, Parse filename?
+ jnz dosex4
+
+ add ax,rmrg.xsi ;Yes. Increment past string
+ sub ax,offset DGROUP:rgbXfrBuf0 ; that was parsed
+ push pmrg.xdi
+ pop rmrg.xdi ;Restore pm di (for es:di ptr)
+
+dosex4: mov word ptr rmrg[si],ax ;Restore register
+
+ cmp byte ptr pmrg.xax+1,4Bh ;EXEC program
+ jnz dosex6
+
+ push di
+ mov di,pmrg.xbx ;Yes, restore bx too (dx restored above)
+ mov rmrg.xbx,di ;es and ds are restored automatically
+ cmp byte ptr pmrg.xax,1 ;INT-21/4b01h (undocumented debug)?
+ jnz @f
+
+ mov si,npXfrBuf1 ;Yes. Pass back user ss:sp and cs:ip
+ lea si,[si].xsssp
+ lea di,[di].xsssp
+ mov es,pmrg.xes
+ movsw ;Move ss:sp
+ movsw
+ movsw ;Move cs:ip
+ movsw
+@@:
+ pop di
+
+dosex6: mov ax,cs:[di+2] ;Exit xfer?
+ or ax,ax
+ rz
+
+dosex7: call CheckStatus ;Check the DOS return status to see if the
+ rnz ; data should be transfered back to PM
+
+ mov cx,ax ;Is a pointer being returned? (no data
+ and cl,0fh ; transfer)
+ cmp cl,PTR$
+ jnz dosex8
+
+ shr al,4 ; yes, isolate pointer type
+ mov si,offset rmrg.xds
+ mov di,offset pmrg.xds
+ cmp al,DSBX
+ jz dosex7a
+ifdef DBCS ; for function 63h (Get Lead Byte)
+ cmp al,DSSI
+ jz dosex7a
+endif ; DBCS
+ mov si,offset rmrg.xes
+ mov di,offset pmrg.xes
+dosex7a:
+ mov ax,[si] ; get a selector for the segment, and
+ mov bx,STD_DATA ; setup to return it to caller
+ call ParaToLDTSelector
+ mov [di],ax
+ return
+
+dosex8:
+ push pmrg.xds
+ pop enxseg
+ call gtarea ;Get area for xfer from PM to DOS
+ rz ;Something to move?
+
+ xchg si,di ;Turn pointers around
+ mov dx,ds ;dx:si -> DOS xfer area in dgroup
+ mov es,enxseg ;es:di -> PM-caller data area
+ jmp movdat ;Yes
+
+; -------------------------------------------------------
+; IOCTLEnter -- Special entry processing for the Generic I/O Control
+; Three cases require data transfer.
+;
+; AX = 4402h Read control data from device
+; AX = 4403h Write control data to device
+; AX = 4404h Read control data from device
+; AX = 4405h Write control data to device
+;
+; AX = 440Ch Generic I/O control for character devices
+; (only minor codes CL=45h and CL=65h supported)
+;
+; AX = 440Dh Generic I/O control for block devices
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public IOCTLEnter
+
+IOCTLEnter proc near
+
+ mov di,npXfrBuf1 ;assume this buffer will be used
+ xor cx,cx
+
+ cmp al,0dh
+ je IOCTL_0D_Enter
+
+ cmp al,0ch
+ je IOCTL_0C_Enter
+
+ cmp al,3 ;write control data
+ je IOCTLWriteEnter
+ cmp al,5 ;write control data
+ je IOCTLWriteEnter
+ cmp al,2 ;read control data
+ je IOCTLReadEnter
+ cmp al,4 ;read control data
+ je IOCTLReadEnter
+
+ jmp IOCTLEnterRet ;register based call
+
+IOCTLWriteEnter:
+ mov cx,rmrg.xcx ;Write length to CX
+IOCTLReadEnter:
+
+ push es
+ push ax
+ push si
+ mov si,pmrg.xds
+ mov es,selGDT
+ mov al,es:[si].adrBaseHigh
+ mov ah,es:[si].adrbBaseHi386
+ test ax,0FFF0h ; check for transfer to extended
+ ; memory
+ pop si
+ pop ax
+ pop es
+
+ jz IOCTLEnterDOSMem
+;
+; These transfers cannot be broken up into smaller pieces. If the
+; requested read or write size exceeds our transfer buffer size, then
+; we truncate it.
+;
+ cmp cx,CB_XFRBUF1
+ jb @F
+ mov cx,CB_XFRBUF1
+@@:
+ cmp rmrg.xcx,CB_XFRBUF1
+ jb @F
+ mov rmrg.xcx,CB_XFRBUF1
+@@:
+ jmp IOCTLEnterMove
+
+IOCTLEnterDOSMem:
+
+ push ax
+ push dx
+ mov dx,rmrg.xdx
+ mov ax,rmrg.xds
+ call SelOff2SegOff ;translate to seg:off if in conventional mem
+ mov rmrg.xdx,dx
+ mov rmrg.xds,ax
+
+ jmp IOCTLEnterRet
+
+IOCTL_0C_Enter:
+ cmp rmrg.xcx,45h ; Set iteration count
+ jne @F
+ mov cx,2
+@@:
+ jmp IOCTLEnterMove
+
+IOCTL_0D_Enter:
+ mov di,npXfrBuf1 ;assume this buffer will be used
+
+ mov cx,rmrg.xcx ;IOCTL subfunction code to CL
+
+ cmp cl,40h ;Set Device Parameters?
+ jnz @f
+ mov cx,28h ;set device parameters has a variable
+ push es ; length table--the word at offset
+ mov es,pmrg.xds ; 26h contains the number of following
+ assume es:NOTHING ; 4 byte entries
+ mov si,pmrg.xdx
+ mov ax,word ptr es:[si+26h]
+ shl ax,2
+ add cx,ax
+ pop es
+ assume es:DGROUP
+ jmp IOCTLEnterMove
+@@:
+ cmp cl,41h ;Write Track?
+ jnz @f
+ mov di,npXfrBuf0 ;going to use this buffer instead
+ cmp rcount,0 ;continuation of large buffered write?
+ jnz WriteTrackMove ; yes, only need to move track data
+
+ mov cx,9 ;only copy 9 bytes of param block
+ call Enter0DTrack ;setup for track read/write
+
+ cmp rcount,0 ;now indicates if buffering needed?
+ jz IOCTLEnterMove ; 0 == if no buffering
+
+WriteTrackMove:
+ push cx
+ push ds
+
+ mov ax,word ptr [di+07] ;get # sectors to transfer
+ mul cbSector ;calc # bytes to move
+ mov cx,ax
+ shr cx,1 ;cx = # words to move
+
+ lds si,lpTrackData ;move source to
+ assume ds:NOTHING
+ mov di,npXfrBuf1 ; dest...
+ rep movsw
+ mov word ptr lpTrackData,si ;for next time
+
+ pop ds
+ assume ds:DGROUP
+ pop cx
+ mov di,npXfrBuf0 ;point to param block
+
+ cmp cl,41h ;used as a flag to see if Enter0DTrack
+ jnz IOCTLEnterMove ; was called or not--Don't want to
+ jmp short IOCTLEnterFinished ; move param block if not called
+@@:
+ cmp cl,42h ;Format & Verify Track?
+ jnz @f
+ mov cx,5
+ jmp short IOCTLEnterMove
+@@:
+ cmp cl,47h ;Set Access Flag?
+ jnz @f
+ mov cx,2
+ jmp short IOCTLEnterMove
+@@:
+ cmp cl,60h ;Get Device Parameters?
+ jnz @f ; Some caller's expect some result
+ mov cx,26h ; fields to be initialized with
+ jmp short IOCTLEnterMove ; their values so copy'm down to DOS
+@@:
+ cmp cl,68h ; DOS5: Media Sense?
+ jnz @F
+ mov cx,4
+ jmp short IOCTLEnterMove
+@@:
+ cmp cl,61h ;Read Track?
+ jnz IOCTLEnterFinished
+ mov di,npXfrBuf0 ;going to use this buffer instead
+ cmp rcount,0 ;continuation of large buffered read?
+ jnz IOCTLEnterFinished ; yes, nothing to do here
+
+ mov cx,9 ;only copy 9 bytes of param block
+ call Enter0DTrack ;setup for track read/write
+
+ errnz <$-IOCTLEnterMove>
+
+IOCTLEnterMove:
+
+ push ds
+ push di
+ mov si,pmrg.xdx
+ mov ds,pmrg.xds
+ assume ds:NOTHING
+ cld
+ rep movsb ;copy param data to DOS visible buffer
+ pop di
+ pop ds
+ assume ds:DGROUP
+
+IOCTLEnterFinished:
+
+ mov rmrg.xdx,di ;point real mode DS:DX to our buffer
+ ; (DS already done by other code)
+IOCTLEnterRet:
+
+ ret
+
+IOCTLEnter endp
+
+
+; Enter0DTrack -- setup for IOCTL 0Dh read/write track
+; On exit DI -> small transfer buffer (npXfrBuf0)
+
+Enter0DTrack proc
+
+ push es
+ mov es,pmrg.xds
+ assume es:NOTHING
+ mov di,pmrg.xdx ;es:di -> caller's param block
+
+ mov dx,word ptr es:[di+09]
+ mov ax,word ptr es:[di+0Bh] ;ax:dx = sel:off of caller's track data
+
+ mov word ptr lpTrackData,dx
+ mov word ptr [lpTrackData+2],ax
+
+ call SelOff2SegOff ;convert to segment:off
+
+ jz set_buf_ptr ; Z set if in low memory
+
+ ; The track data is not in conventional memory--we need to buffer
+ ; it through the large transfer buffer--UGLY!
+
+ mov ax,word ptr es:[di+07] ;get # sectors to transfer
+ mov rcount,ax
+
+ SwitchToRealMode ;find out how large a sector
+ assume ds:DGROUP,es:DGROUP ; is by dropping to real mode and
+ FSTI ; doing a Get Device Parameters IOCTL
+
+ mov ax,440Dh ;IOCTL
+ mov bx,pmrg.xbx ; caller's drive
+ mov cx,0860h ; Get Device Parameters
+ mov dx,npXfrBuf0 ; temp buffer
+ pushf
+ FCLI
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset edt_10)
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+edt_10: mov cx,512 ;default to 512
+ mov di,dx ; CY shouldn't be set, but...
+ jc @f ; offset 7 in parameter block
+ mov cx,word ptr [di+07] ; is the # bytes/sector
+@@:
+ mov cbSector,cx
+
+ SwitchToProtectedMode ;back to pMode
+ FSTI
+
+ xor dx,dx ;calc # sectors that can be
+ mov ax,CB_XFRBUF1 ; read/written per transfer
+ div cx
+
+ mov cx,rcount
+ cmp ax,cx
+ jb @f
+ mov ax,cx ;ax = # sectors to transfer this time
+@@:
+ mov [di+07],ax ;store # sectors in our read/write
+ ; param block
+
+ mov ax,rmrg.xds
+ mov dx,npXfrBuf1 ;ax:dx = our real mode track buffer
+
+ mov cx,7 ;only move 7 bytes of param block
+ ; (don't use caller's # sectors)
+set_buf_ptr:
+ assume es:NOTHING
+
+ mov di,npXfrBuf0 ;use this buffer for param block
+ mov [di+09],dx ; plug in segment:offset of track data
+ mov [di+0Bh],ax
+
+ pop es
+ assume es:DGROUP
+
+ ret
+
+Enter0DTrack endp
+
+; -------------------------------------------------------
+; IOCTLExit -- Special exit processing for the Generic I/O Control.
+; Three cases require data transfer.
+;
+; AX = 4402h Read/Write control data to device
+; AX = 4403h
+; AX = 4404h
+; AX = 4405h
+;
+; AX = 440Ch Generic I/O control for character devices
+; (only minor codes CL=45h and CL=65h supported)
+;
+; AX = 440Dh Generic I/O control for block devices
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public IOCTLExit
+
+IOCTLExit proc near
+
+ cmp al,0dh
+ je IOCTL_0D_Exit
+
+ cmp al,0ch
+ je IOCTL_0C_Exit
+
+ cmp al,3 ;write control data
+ je IOCTLWriteExit
+ cmp al,5 ;write control data
+ je IOCTLWriteExit
+ cmp al,2 ;read control data
+ je IOCTLReadExit
+ cmp al,4 ;read control data
+ je IOCTLReadExit
+
+ jmp IOCTLExitRet ;register based call
+
+IOCTLWriteExit:
+ jmp IOCTLExitFinished ;make sure caller's DX is restored
+
+IOCTLReadExit: ;fill caller's buffer with result
+
+ push es
+ push ax
+ push si
+ mov si,pmrg.xds
+ mov es,selGDT
+ mov al,es:[si].adrBaseHigh
+ mov ah,es:[si].adrbBaseHi386
+ test ax,0FFF0h ; check for transfer to extended
+ ; memory
+ pop si
+ pop ax
+ pop es
+
+ mov cx,rmrg.xax
+ jz @F
+ jmp IOCTLExitMove
+@@:
+ jmp IOCTLExitFinished
+
+IOCTL_0C_Exit:
+ cmp rmrg.xcx,65h ; Get iteration count
+ je IOCTL_0C_ExitMove
+ cmp rmrg.xcx,45h ; Set iteration count
+ je IOCTL_0C_ExitNoMove
+ or byte ptr rmflags,1 ;Not supported!
+IOCTL_0C_ExitNoMove:
+ xor cx,cx
+ jmp IOCTLExitMove
+IOCTL_0C_ExitMove:
+ mov cx,2
+ jmp IOCTLExitMove
+
+IOCTL_0D_Exit:
+ test byte ptr rmflags,1 ;CY set? (error on the IOCTL)
+ jz @f
+ xor ax,ax ;yes, halt buffered read/writes
+ mov rcount,ax
+ jmp IOCTLExitFinished ; and don't do anything else either
+@@:
+ mov cx,rmrg.xcx ;IOCTL subfunction code to CL
+
+ cmp cl,41h ;Write track?
+ jz read_write_0D
+ cmp cl,61h ;Read track?
+ jnz not_read_write_0D
+
+read_write_0D:
+ mov ax,rcount ;nothing to do if not buffering
+ or ax,ax
+ jz IOCTLExitFinished
+
+ mov di,npXfrBuf0 ;point to param buffer
+ mov bx,word ptr [di+07] ;bx = # sectors transfered
+ add word ptr [di+05],bx ;update starting sector for next time
+ sub ax,bx
+ mov rcount,ax ;update remaining count of sectors
+ jz @f ;don't need to setup again if no more
+
+ cmp ax,bx ;can transfer at most as many as last
+ jae @f ; time--already set if more remaining
+ mov word ptr [di+07],ax ; otherwise just transfer remaining
+@@:
+ cmp cl,61h ;transfer track data if read track
+ jnz IOCTLExitFinished
+
+ mov ax,bx ;ax = # sectors read
+ mul cbSector ;ax = # bytes read
+ mov cx,ax
+ shr cx,1 ;cx = # words read
+
+ push es ;copy track data to caller's buffer
+ les di,lpTrackData
+ assume es:NOTHING
+ mov si,npXfrBuf1
+ cld
+ rep movsw
+ mov word ptr lpTrackData,di ;for next time
+ pop es
+ assume es:DGROUP
+
+ jmp short IOCTLExitFinished
+
+not_read_write_0D:
+ cmp cl,60h ;Get Device Parameters?
+ jnz @f
+ mov cx,26h
+ jmp short IOCTLExitMove
+@@:
+ cmp cl,62h ;Verify Track?
+ jnz @f
+ mov cx,5
+ jmp short IOCTLExitMove
+@@:
+ cmp cl,68h ; DOS5: Media Sense?
+ jnz @F
+ mov cx,4
+ jmp short IOCTLExitMove
+@@:
+ cmp cl,67h ;Get Access Flag?
+ jnz IOCTLExitFinished
+ mov cx,2
+
+ errnz <$-IOCTLExitMove>
+
+IOCTLExitMove:
+
+ push es ;caller's ds:dx -> result buffer
+ mov es,pmrg.xds
+ assume es:NOTHING
+ mov si,npXfrBuf1
+ mov di,pmrg.xdx
+ cld
+ rep movsb ;move result to caller's buffer
+ pop es
+
+IOCTLExitFinished:
+
+ mov ax,pmrg.xdx ;restore caller's dx register
+ mov rmrg.xdx,ax
+
+IOCTLExitRet:
+ ret
+
+IOCTLExit endp
+
+; -------------------------------------------------------
+; DosExitCall -- Special processing for DOS exit call service (Int 21h/00h)
+;
+; This procedure handles the obsolete Int 21h/00h terminate process
+; call in a special slimy way for Windows. Instead of doing a 00h
+; DOS call in real mode, it hacks up the parent's PSP and does a 4Ch
+; that causes control to return to the extender, who then RETURNS TO
+; THE CALLER!!! The caller must then do appropriate set PSP calls, etc.
+; This was implemented so pMode Windows kernel could have DOS clean up
+; after a Windows app terminates, but still have kernel get control
+; back.
+;
+; Note: This code assumes that the Parent PID field contains a
+; SELECTOR!
+;
+; Note^2: If for some reason it's the DOS Extender's child that's 1800doing
+; the terminate call, we let it go through normally.
+;
+; Input: none
+; Output: CY set if exit call processed by this routine, clear otherwise
+; Errors:
+; Uses: none
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public DosExitCall
+
+DosExitCall proc near
+
+ push ax
+ push bx
+ push dx
+ push es
+
+ SwitchToRealMode ;much of this is easier in real mode
+ FSTI ;allow interrupts
+
+ mov ah,51h ;get PSP of current task
+ pushf
+ FCLI
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset dec_10)
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+dec_10: cmp bx,segPSPChild ;is this our child terminating?
+ jnz @f ;if not, go process the call ourselves
+ jmp decTermChild ; yes...
+@@:
+ FCLI ;we want to preserve the current
+ xor ax,ax ; rMode Int 24h handler across this
+ mov es,ax ; exit call (the terminating PSP
+ mov ax,es:[24h*4] ; has the pMode handler address).
+ mov dx,es:[24h*4+2] ; So get the current Int 24h handler
+ FSTI ; address from the rMode IDT.
+
+ mov es,bx ;address terminating PSP
+ assume es:PSPSEG
+
+ mov word ptr [lpfnInt24],ax ;point PSP to same Int 24h
+ mov word ptr [lpfnInt24+2],dx ; handler
+
+ mov ax,offset DXCODE:decTermHandler ;point PSP to our termination
+ mov word ptr [lpfnParent],ax ; handler
+ mov word ptr [lpfnParent+2],cs
+
+ push es
+ mov ax,segParentPSP ;Windows has the PSP's parent
+ push ax ; field as a selector, we need the seg
+ mov dx, [segEnviron]
+ push dx
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI
+ pop ax ;get selector for environment
+ call GetSegmentAddress
+ shr dx, 4
+ shl bx, 12
+ or bx, dx ;seg of environment
+
+ pop ax ;get parent psp off stack
+ push bx ;save seg of environment
+
+ call GetSegmentAddress ;returns BX:DX = lma of segment
+ shr dx,4
+ shl bx,12
+ or bx,dx ;bx now = seg of parent psp
+
+ SwitchToRealMode ;back to the shadows again...
+ FSTI
+ pop cx ;seg of environment
+
+ pop dx ;terminating PSP segment from stack
+ mov es,bx ;address the parent's PSP
+ assume es:PSPSEG
+
+ mov ax,sp
+ sub ax,12*2 ;some magic for DOS
+ mov word ptr [lpStack],ax ;set our stack in parent's PSP
+ mov word ptr [lpStack+2],ss
+
+ mov es,dx ;(re)address terminating PSP
+ assume es:PSPSEG
+ mov [segEnviron], cx
+
+ mov segParentPSP,bx ;real DOS doesn't like selectors in
+ ; parent PSP field, zap it to segment
+
+ mov ax,pmrg.xax ;terminate the process
+ mov ah,4Ch ; with a 4Ch DOS call
+ pushf
+ FCLI
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset decTermHandler
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING ;play it safe
+
+decTermHandler: ;should return back here
+
+ push ss
+ pop ds
+
+ SwitchToProtectedMode ;back to pMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI
+
+ les bx,dword ptr pmusrsp ;es:bx -> ip cs flag
+ and byte ptr es:[bx+2*2],not 1 ;clear CY in caller's flags
+
+ stc ;exit call has been processed
+
+dec90:
+ pop es
+ pop dx
+ pop bx
+ pop ax
+
+ ret
+
+EndHighSegment
+
+BeginLowSegment
+
+decTermChild:
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI
+ clc
+
+ jmp short dec90
+
+DosExitCall endp
+
+
+; -------------------------------------------------------
+; net5Fenter -- Additional entry processing for INT 21h/5Fh
+; functions.
+;
+; INT 21h/5Fh subfunctions 2, 3, and 5 have two buffers of data to
+; transfer. The normal DOSENTRY processing only does one, so we
+; setup the other buffer here.
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public net5Fenter
+
+net5Fenter proc near
+
+ cmp al,2 ;This routine only works on
+ rc ; subfunctions 2, 3, and 5
+ cmp al,4
+ rz
+ cmp al,6
+ rnc
+
+ push ax
+ mov ax,int21esdi ;entry code for INT 21h/5Fh extra buff
+ call gtarea ;let gtarea set it up
+ pop ax
+
+ cmp al,3 ;Make redirection function?
+ rnz
+
+; 5F/03 needs a buffer copied down to A1, but it's non standard in that
+; the buffer contains two (count'em 2) asciiz strings
+
+ push ax
+ push cx
+ push ds
+
+ mov ds,pmrg.xes ;user's ES:DI -> buffer, gtarea sets
+ xor ah,ah ; up our si to have user's di
+ mov cl,2
+
+@@: lodsb ;copy one asciiz string
+ stosb
+ cmp al,ah
+ jnz @b
+
+ dec cl ; and then the other
+ jnz @b
+
+ pop ds
+ pop cx
+ pop ax
+ return
+
+net5Fenter endp
+
+
+; -------------------------------------------------------
+; net5Fexit -- Additional exit processing for INT 21h/5Fh
+; functions.
+;
+; INT 21h/5Fh subfunctions 2, 3, & 5 have 2 buffers of data to transfer.
+; The normal DOSENTRY processing only does one, so do the other
+; buffer here.
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public net5Fexit
+
+net5Fexit proc near
+
+ cmp al,2 ;This routine only works on
+ rc ; subfunctions 2, 3 and 5
+ cmp al,4
+ rz
+ cmp al,6
+ rnc
+
+ push pmrg.xdi ;Restore protected mode DI register
+ pop rmrg.xdi
+
+ cmp al,2 ;Get redirection function?
+ jz @f
+ cmp al,5
+ rnz
+@@:
+
+; 5F/02 & 05 need a buffer copied from A1
+
+ test byte ptr rmflags,1 ;Success? (carry flag)
+ rnz ; No, don't transfer anything
+
+ push ax
+
+ mov ax,int21esdi+2 ;exit code for int 21/5F extra buffer
+ push pmrg.xes
+ pop enxseg
+ call gtarea ;let gtarea setup the move
+ xchg si,di
+ mov dx,ds
+ mov es,enxseg
+ call movdat ; and let movdat move it
+
+ pop ax
+ return
+
+net5Fexit endp
+
+
+; -------------------------------------------------------
+; RSTREG -- This function sets up to restore the original
+; protected-mode registers. This cleans up after register
+; translations performed when going into the or returning
+; from an MS-DOS call. On entry, AX contains the entry code
+; value from the entry/exit operations table. If this code
+; implies that a register needs to be restored this function
+; will return with NZ true and AX = original register value
+; and SI pointing to the appropriate location in the PMRG array.
+; If no register needs to be restored, return with Z true.
+;
+; Input: AX - entry code value
+; Output: NZ true if register needs to be restores
+; AX - register value to restore
+; SI - pointer into PMRG to the register image
+; ZR true if no restoration needed
+; Errors: none
+; Uses: AX, SI modified
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public rstreg
+
+rstreg: or ax,ax
+ rz
+
+ shr ax,3
+ and ax,1Eh
+ rz
+
+ cmp al,2*DTA ;DTA?
+ rz
+
+ xchg si,ax ;No. Restore appropriate register, e.g., dx
+ mov si,regoffs[si-2]
+ mov ax,word ptr pmrg[si]
+ return
+
+
+; -------------------------------------------------------
+; GTAREA -- This function examines the entry code/exit code
+; parameter and determines if any data transfer needs to be
+; performed. If so, it sets up pointers and length codes for
+; the transfer.
+; There are two transfer buffers used. The A0 buffer is 60h bytes
+; long and the A1 buffer is CB_XFRBUF1 bytes (about 4k) long.
+;
+; Input: AX - entry code/exit code
+; Output: NZ true if something needs to be transferred
+; SI - offset of source pointer
+; DI - offset of destination pointer
+; ENXSEG - segment for caller's buffer
+; (source on entry, destination on exit)
+; CX - transfer length/type code
+; Errors: none
+; Uses: AX, CX, SI, DI, ES modified
+; ENXSEG modified
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public gtarea
+
+gtarea:
+ test ah,RP ;Real/PM xfer (ah = 3f/40)?
+ jz gtare2
+
+ mov si,pmrg.xds ;Yes. *si:pmrg.xdx = pm caller's xfer area
+ and si,SELECTOR_INDEX
+ test ah,EX ;Exit call?
+ jz gtare1
+ push es
+ push ax
+ mov es,selGDT
+ mov al,es:[si].adrBaseHigh
+ mov ah,es:[si].adrbBaseHi386
+ test ax,0FFF0h ; check for transfer to extended
+ ; memory
+ pop ax
+ pop es
+ jnz @f
+ jmp gtar54
+@@:
+ mov cx,rmrg.xax ;Yes. cx = amount read/written
+ sub rcount,cx ;Update remaining count
+ cmp cx,rmrg.xcx ;All that was requested?
+ jnc gtare3
+ mov rcount,0 ;No: done
+ jmp short gtare3
+
+gtare1: push ax ;Read/Write entry
+ mov ax,si
+ mov dx,pmrg.xdx ;ax:dx = selector:offset of buffer
+ call SelOff2SegOff ;translate to seg:off if in conventional mem
+ jnz gtar12
+ mov rmrg.xds,ax ;store corresponding paragraph
+ mov rmrg.xdx,dx ; and offset
+ pop ax
+ jmp short gtar54 ;No more setup needed
+
+gtar12: pop ax ;XRAM/RRAM read/write entry
+ mov cx,rcount ;Try to xfer remaining amount
+ cmp cx,CB_XFRBUF1 ;Too much to fit in buffer?
+ jbe gtar14
+ mov cx,CB_XFRBUF1 ;Yes, only transfer a buffer size
+gtar14: mov rmrg.xcx,cx
+ jmp short gtare3
+
+gtare2: test ah,A0+A1 ;xfer area?
+ jz gtare4
+
+gtare3: mov di,offset DGROUP:rgbXfrBuf0 ;Point at small buffer (90h bytes)
+ test ah,1 ;Area 0 (small area) ?
+ jnz gtare4
+ mov di,npXfrBuf1 ;No. Point at large buffer (4K)
+
+gtare4: push ax ;Store ptr to communication area for DOS
+ mov si,di
+ shr al,3 ;Get al = 2 * data ptr type, e.g., DSDX (ds:dx)
+ and al,1Eh ;al = word offset for data ptr type
+ jz gtare7
+ cmp al,2*DTA ;DOS Data Transfer Area?
+ jnz gtare5
+ mov si,pmdta ;Yes, load DTA offset
+ push pmdta+2 ;and the segment
+ pop enxseg
+ jmp short gtare7
+
+gtare5: cmp al,2*ES$
+ jnz gtare6
+ test ah,80h ;INT-21 49/4A. Ignore exit call
+ mov ax,0
+ jnz gtar52
+ mov si,pmrg.xes ;Entry call. si = RAM xfer selector
+ call gtpara ;Get paragraph given by [si].gdt
+ jnz gtar52 ;XRAM?
+ mov rmrg.xes,ax ;No, store RRAM paragraph
+gtar52: pop cx ;Kill saved ptr
+
+gtar54: xor cx,cx ;RZ with zero count, i.e., no RM/PM xfer needed
+ mov rcount,cx
+ return
+
+gtare6: test ah,80h ;Entry?
+ cbw
+ push ax
+ xchg di,ax ;Save real-mode area offset
+ mov di,regoffs[di-2] ;di = offset of saved register value
+ mov si,word ptr pmrg[di] ;si = saved value
+ jnz gtar62
+ mov word ptr rmrg[di],ax ;Entry. Store real-mode area offset
+ cmp byte ptr pmrg.xax+1,29h ;Parse filename?
+ jnz gtar62
+ mov cx,npXfrBuf1 ;Yes. Use npXfrBuf1 for FCB info
+ mov word ptr rmrg.xdi,cx
+gtar62: xchg di,ax ;Restore di = real-mode area offset
+
+ pop ax
+ cmp ax,ESDI*2
+ jne gtare7
+
+ push pmrg.xes
+ pop enxseg
+
+gtare7: pop cx ;Recover entrycd/exitcd
+ and cx,0Fh ;RNZ if something to xfer
+ rz ;No
+
+ mov dx,pmrg.xds ;Xfer needed. dx = original XRAM data selector
+ cmp cl,AX$ ;Use rmrg.xax?
+ jz gtare8
+ cmp cl,CX$ ;Use pmrg.xcx?
+ rnz ;No, just RNZ
+
+
+;Return dx:0 = pmrg.xds:si+ccount, where dx is a scratch selector
+;and si = 0. This ensures that dx:si can address a bufferful of bytes
+;without overflowing si (sort of like a huge ptr).
+
+gtare8: xchg ax,si ;ax = original offset
+ mov si,dx ;si = pmrg.xds (PM data selector)
+ and si,SELECTOR_INDEX
+ xor dx,dx ;dxax = original offset in 32-bit form
+ add ax,ccount
+ adc dx,0 ;dxax += ccount (current xfer count)
+ push es
+ mov es,selGDT ;Point at GDT
+ add ax,es:[si].adrBaseLow
+ adc dl,es:[si].adrBaseHigh ;dxax absolute XRAM address
+ adc dh,es:[si].adrbBaseHi386
+ mov si,selDOSScr
+ and si,SELECTOR_INDEX
+ifndef WOW_x86
+ mov es:[si].adrBaseLow,ax
+ mov es:[si].adrBaseHigh,dl
+ mov es:[si].adrbBaseHi386,dh
+ mov es:[si].cbLimit,0FFFFh
+ mov es:[si].arbSegAccess,STD_DATA
+else
+ cCall NSetSegmentDscr,<si,dx,ax,0,0ffffh,STD_DATA>
+endif ; WOW_x86
+ pop es
+
+; original code... changed to line below to fix file read problem.
+; may cause other bugs.....
+; mov dx,si ;Return scratch selector with 0 offset since
+;
+ or si,SELECTOR_TI
+ mov enxseg,si ;Return scratch selector with 0 offset since
+ xor si,si ; it points directly to the transfer location
+ or sp,sp ;RNZ to indicate xfer needed
+ return
+
+; -------------------------------------------------------
+; GetEntryCd -- This routine puts the entry code for the
+; DOS function into ax.
+;
+; Input: AH - MS-DOS function number
+; Output: AX - entry code for function
+; DI - address of entry code returned
+;
+; Errors: none
+; Uses: AX, DI modified
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public GetEntryCd
+
+GetEntryCd proc near
+
+ push bx
+
+ cmp ah,MaxInt21 ;Check for unsupported DOS call
+ jbe @f
+ mov di,offset DXPMCODE:Unsupported
+ jmp gec90
+@@:
+ mov di,offset DXPMCODE:pmrmxfr
+
+ifdef DBCS
+ cmp ah,63h ;Get Lead Byte Table?
+ jnz gec10
+
+if DEBUG ;------------------------------------------------------------
+
+ cmp al,2
+ jbe gec15
+
+ Debug_Out "Int 21h/63h Unsupported Function (#AX)"
+ jmp short gec80
+gec15:
+
+endif ;DEBUG --------------------------------------------------------
+
+ cmp al,0 ;Int 21/63/00 is special
+ jne gec80
+
+ mov di,offset DXPMCODE:int2163
+ jmp short gec90
+gec10:
+ENDIF ; DBCS
+
+gec20: cmp ah,5Eh ;Network Machine Name/Printer Setup Str?
+ jnz gec40
+
+if DEBUG ;------------------------------------------------------------
+
+ cmp al,3
+ jna gec25
+
+ Debug_Out "Int 21h/5Eh Unsupported Function (#AX)"
+ jmp short gec80
+gec25:
+
+endif ;DEBUG --------------------------------------------------------
+
+ cmp al,3 ;Int 21-5E/00-03 are special
+ ja gec80
+
+ mov bl,al
+ mov di,offset DXPMCODE:int215E
+ jmp short gec85
+
+
+gec40: cmp ah,5Fh ;Get/Make Assign-List Entry?
+ jnz gec80
+
+if DEBUG ;------------------------------------------------------------
+
+ cmp al,5
+ ja @f
+ cmp al,2
+ jnb gec45
+@@:
+ cmp al,30h ;Register based. Get Redirector version
+ ;used by Lanman Enhanced.
+ je gec80
+ Debug_Out "Int 21h/5Fh Unsupported Function (#AX)"
+ jmp short gec80
+gec45:
+
+endif ;DEBUG --------------------------------------------------------
+
+ cmp al,2 ;Int 21/5F/02-05 are special
+ jb gec80
+ cmp al,5
+ ja gec80
+
+ mov bl,al
+ sub bl,2
+ mov di,offset DXPMCODE:int215F02
+ jmp short gec85
+
+
+gec80: mov bl,ah
+gec85: xor bh,bh
+ shl bx,2
+
+ add di,bx ;Address of entry code
+gec90:
+ mov ax,word ptr cs:[di] ;The entry code itself
+ pop bx
+ return
+
+GetEntryCd endp
+
+
+; -------------------------------------------------------
+; CheckStatus -- This routine determines if data should be copied
+; back to protect mode by checking the DOS function
+; return status.
+;
+; Input: none
+; Output: NZ - if data should NOT be copied, Z otherwise
+;
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public CheckStatus
+
+CheckStatus proc near
+
+; For now, only worry about the functions that return variable length
+; results, like ASCIIZ strings.
+
+ cmp byte ptr pmrg.xax+1,32h
+ jz @f
+ cmp byte ptr pmrg.xax+1,47h ;Get Current Directory
+ jz @f
+ cmp byte ptr pmrg.xax+1,5Ah ;Create Temporary File
+ jz @f
+ cmp byte ptr pmrg.xax+1,5Eh ;Get Machine Name/Printer Str
+ jc cks90
+ cmp byte ptr pmrg.xax+1,5Fh ;Get Redirection Entry
+ ja cks90
+@@:
+ test byte ptr rmflags,1 ;Carry set?
+ return
+
+cks90: cmp al,al ;Assume status is okay (or doesn't
+ return ; matter) -- set Z and return
+
+CheckStatus endp
+
+
+; -------------------------------------------------------
+; DOSXEC -- This function performs the transfer of the
+; DOS exec parameter block on entry to DOS function 4Bh.
+;
+; transfer int-21 ah = 4b0x EXEC block and associated strings
+; to Area 1. This area is laid out partly analogously to a PSP as follows:
+;
+; 0-1f EXEC block defined according to the execblk struc above
+; 20-2f FCB1
+; 30-3f FCB2
+; 40-bf command line
+;
+; cx, si, di changed.
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public dosxec
+
+dosxec:
+ push ax ;Save entrcd code
+ push bx
+ push dx
+
+ mov cx,10h ;Setup parameter block. Xfer pm user block first
+ mov bx,npXfrBuf1 ;Point at larger buffer
+ mov di,bx
+IFDEF ROM
+ mov dx,segDXData
+ELSE
+ mov dx,segDXDataPM
+ENDIF
+ mov rmrg.xbx,di ;int-21 4b0x expects es:bx -> exec block
+ mov si,pmrg.xbx ;npXfrBuf1:0 - 1f (xtra room for undocumented)
+ mov ds,pmrg.xes
+ rep movsw ;copy caller's exec param block to our buffer
+
+; Copy FCB1 down if the user has specified one.
+
+dsxc20: mov ax,word ptr es:[bx].fcb1ptr
+ cmp ax,0FFFFh
+ jz dsxc22
+ or ax,word ptr es:[bx].fcb1ptr+2
+ jz dsxc22
+
+ lds si,es:[bx].fcb1ptr ;Get pointer to FCB1
+ mov word ptr es:[bx].fcb1ptr,di ;store new pointer in the copy of the
+ mov word ptr es:[bx].fcb1ptr+2,dx ; exec block we are building
+ mov cl,8 ;copy FCB1 down to our buffer
+ rep movsw
+ jmp short dsxc24
+dsxc22: add di,10h
+
+; Copy FCB2 down if the user has specified one.
+
+dsxc24: mov ax,word ptr es:[bx].fcb2ptr
+ cmp ax,0FFFFh
+ jz dsxc26
+ or ax,word ptr es:[bx].fcb2ptr+2
+ jz dsxc26
+
+ lds si,es:[bx].fcb2ptr ;Move FCB2
+ mov word ptr es:[bx].fcb2ptr,di
+ mov word ptr es:[bx].fcb2ptr+2,dx
+ mov cl,8
+ rep movsw
+ jmp short dsxc30
+dsxc26: add di,10h
+
+; Copy the command line down.
+
+dsxc30: lds si,es:[bx].cmdptr ;Move command line
+ mov word ptr es:[bx].cmdptr,di
+ mov word ptr es:[bx].cmdptr+2,dx
+ mov cl,[si]
+ inc cx ;Include count
+ inc cx ;Include final CR not included in count
+ rep movsb
+
+; Now, we need to set up the enviroment table to be passed to the
+; child program.
+
+ mov di,bx ;di = npXfrBuf1
+ mov dx,es:[di] ;Setup desired environment
+ or dx,dx ;Use parent's environment?
+ jnz dosxegotenv
+
+; pick up the environment segment from the current PDB. It's a selector,
+; so it has to be translated.
+ push bx
+ push di
+ push es
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+
+ SwitchToRealMode ;much of this is easier in real mode
+
+ mov ah,51h ;get PSP of current task
+ pushf
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset dosxeret)
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+dosxeret:
+ assume es:PSPSEG
+ mov es, bx ;current PSP
+ mov dx, es:[segEnviron] ;get environment (currently selector)
+ push dx ;save over call (bugbug is this needed?)
+ SwitchToProtectedMode
+ pop dx ;bugbug is this needed?
+
+ pop es
+ pop di
+ pop bx
+
+
+dosxegotenv:
+ xor si,si ;No. Setup to copy desired environment down
+ mov ds,dx ;ds = dx has caller's selector. Use 0 offset
+ add di,100h
+ shr di,4 ;Convert offset to paragraph
+IFDEF ROM
+ GetPMDataSeg
+ mov dx,ax
+ELSE
+ mov dx,segDXDataPM
+ENDIF
+ add dx,di ;dx = absolute paragraph within larger buffer
+ shl di,4 ;Convert back (with low nibble cleared)
+ mov cx,CB_XFRBUF1 ;Max room available for environment
+ sub cx,100h
+
+dosxe2: lodsw ;Copy environment down
+ stosw
+ or ax,ax
+ jz dosxe4
+ dec si ;Check every byte offset in environment for double 0
+ dec di
+ loop dosxe2
+ xor dx,dx ;Environment too large for buffer: use parent's
+ ;Issue error message? Program might run with parent's
+ ; and not with desired monster environment
+dosxe4: push es ;Fix up parameter block entry
+ pop ds ;ds:dgroup
+ mov [bx].evrnmt,dx
+
+ pop dx
+ pop bx
+ pop ax ;Restore entrcd code
+ return
+
+
+; -------------------------------------------------------
+; XFRFLG -- This function will transfer the relevant real-mode
+; flags to the protected mode IRET return information on the
+; stack for returning to the protected mode caller.
+;
+; Input:
+; Output:
+; Errors:
+; Uses: AX, CX, DI modified
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public xfrflg
+
+xfrflg: push es
+ les di,dword ptr pmusrsp ;es:[di] = ip cs fl (assume 80286)
+ mov ax,rmflags
+ mov cx,es:[di+2*2] ;Get pm user entry flags
+ and ch,not 19h ;Only allow real-mode program to
+ and ah,18h ; change OF and DF in high byte of flags
+ or ah,ch
+ mov es:[di+2*2],ax
+ pop es
+ return
+
+; -------------------------------------------------------
+ subttl Handlers for Special Case Dos Functions
+ page
+; -------------------------------------------------------
+; HANDLERS FOR SPECIAL CASE DOS FUNCTIONS
+; -------------------------------------------------------
+;
+; DFGetIntrVector -- This function handles the special
+; case processing required for the DOS function 35h
+; (Get Interrupt Vector).
+;
+; Input: AX - interrupt number
+; Output: CX - selector of the interrupt routine
+; DX - offset of the interrupt routine
+; Errors: none
+; Uses: CX, DX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public DFGetIntrVector
+
+DFGetIntrVector:
+
+ push ax
+
+ cmp ax,1Eh ;special case int 1Eh which points to
+ jnz dfgv10 ; disk parameter table
+
+ push es
+ push bx
+
+ push SEL_RMIVT or STD_RING ;address real mode IDT via es
+ pop es
+ assume es:NOTHING
+
+ mov dx,es:[1Eh*4] ;get current 1Eh SEG:offset to ax:dx
+ mov ax,es:[1Eh*4+2]
+
+ mov bx,STD_DATA ;make/find a data selector to table
+ call ParaToLDTSelector
+ mov cx,ax ;return selector:offset in cx:dx
+
+ pop bx
+ pop es
+
+ jmp short dfgv90
+
+dfgv10:
+
+dfgv20: call GetIntrVector
+
+dfgv90:
+
+if 0
+if DEBUG ;------------------------------------------------------------
+ cmp al,2
+ jnz @f
+ Trace_Out "Returning Int 2 vector #CX:#DX"
+@@:
+endif ;DEBUG --------------------------------------------------------
+endif
+
+ pop ax
+ ret
+
+; -------------------------------------------------------
+; DFSetIntrVector -- This functions handles the processing
+; for the DOS function 25h (Set Interrupt Vector).
+;
+; Input: AX - interrupt vector number
+; CX - selector of interrupt routine to set
+; DX - offset of interrupt routine to set
+; Output: none
+; Errors: none
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public DFSetIntrVector
+
+DFSetIntrVector:
+
+ push ax
+ push dx
+ push di
+ push es
+
+set_as_is:
+
+; The first thing that we want to do is to store the given vector
+; into the interrupt descriptor table.
+
+dfsv20: call PutIntrVector
+
+; Setting the interrupt vector is now finished, unless it's a hardware
+; interrupt (50h-57h & 70h-77h) or one of the special software interrupts
+; (^Break, timer tick, ^C, critical error). At one time, the DOS extender
+; allowed any interrupt to be reflected to protected mode, but Windows/386
+; doesn't, so we no longer do either. Note reflecting INT 2Fh to protected
+; mode breaks the MS-NET redirector, so don't do that.
+
+
+ cmp al,1Bh ;^Break?
+ jz @f
+ cmp al,1Ch ;Timer Tick?
+ jz @f
+ cmp al,23h ;Ctrl-C?
+ jz @f
+ cmp al,24h ;Critical Error Handler?
+ jz @f
+ cmp al,02h ;Math co-processor exception used by math
+ jz @f ; library routines!
+
+ cmp al,08h ;Hardware?
+ jb dfsv30
+ cmp al,0fh
+ jna @f
+
+ cmp al,70h
+ jb dfsv30
+ cmp al,77h
+ jna @f
+
+dfsv30: jmp dfsv90
+@@:
+
+
+; We are going to need to look at the real mode interrupt vector table,
+; so set up for that.
+
+ mov di,ax ;interrupt vector number to DI
+ add di,di ;multiply by 4 to turn into dword offset
+ add di,di
+ push SEL_RMIVT or STD_RING ;selector to real mode int table
+ pop es
+
+; Now, we need to see if the user is trying to restore the original
+; handler for a vector he had previously hooked, or he is trying to
+; hook a new vector. If he is trying to hook a new one, the address
+; he is setting will be one of his routines, if he is trying to restore
+; the original value of a previously hooked vector, the address will
+; be a location in the Dos Extender interrrupt reflector entry table.
+; So, if the selector of the address being set is a Dos Extender code
+; address we think he is trying to restore an old vector.
+
+ push cx
+ and cx,SELECTOR_INDEX
+ cmp cx,SEL_DXPMCODE ;check if it is the dos extender code segment
+ pop cx
+ jnz dfsv50
+
+; The application is trying to restore a previously hooked vector.
+; We want to restore the original value of the real mode interrupt
+; vector with the value stored in the real mode interrupt vector
+; shadow table. However, we only want to do this if no one else has
+; hooked the vector after we gave it to the protected mode child. If
+; someone else has also hooked it, then we want to leave it alone. We
+; can tell if someone else has hooked it by looking at the value
+; currently in the real mode interrupt vector. If it points to the
+; Dos Extender real mode interrupt reflector, then no one else has
+; hooked it, and it is safe to restore the original vector.
+
+ mov dx,segDXCodePM ;Dos Extender real mode code segment
+ cmp es:[di+2],dx ;does the segment of the vector point
+ jnz dfsv45 ; to the dos extender code segment?
+ FCLI
+ mov ax,word ptr RmHwISR[di] ;get offset of old RM vector
+ mov es:[di],ax
+ mov ax,word ptr RmHwISR[di+2] ;get segment of old RM vector
+ mov es:[di+2],ax
+; xor ax,ax
+; mov word ptr RmHwISR[di],ax ; forget old handler
+; mov word ptr RmHwISR[di+2],ax ; forget old handler
+ FSTI
+
+dfsv45: jmp dfsv90
+
+; The application is trying to hook a new vector. We need to redirect
+; the real mode vector for this interrupt to the corresponding entry
+; point in the real mode -> protected mode interrupt reflector. So that
+; the application interrupt routine will receive control if the interrupt
+; occurs while the processor is in real mode. In case the application
+; installs more than one hook routine, we need to check to see if it is
+; already hooked, and not hook it again if so.
+
+dfsv50: cmp al,24h ;The critical error handler gets a
+ jnz @f ; special handler address
+ mov dx,offset RMIntr24
+ jmp short dfsv51
+@@:
+ mov dx,offset RMIntrEntryVector ;address of beginning of
+ ;real mode reflector entry table
+ add dx,ax ;add in 3 times the interrupt vector
+ add dx,ax ; number to get the offset of the
+ add dx,ax ; correct entry point in the real mode
+ ; interrupt reflector entry table
+dfsv51: mov ax,word ptr RmHwIsr[di]
+ or ax,word ptr RmHwIsr[di + 2]
+ jz dfsv56
+
+ mov ax,word ptr RmHwIsr[di]
+ cmp ax,es:[di]
+ jne dfsv90
+
+ mov ax,word ptr RmHwIsr[di + 2]
+ cmp ax,es:[di + 2]
+ jne dfsv90
+
+; The interrupt isn't already hooked, so we want to redirect it to point
+; to the real mode entry vector.
+
+dfsv56: FCLI
+
+;
+; save the actual vector in RmHWIsr
+;
+ mov ax,es:[di]
+ mov word ptr RmHwIsr[di],ax
+ mov ax,es:[di+2]
+ mov word ptr RmHwIsr[di+2],ax
+
+ mov es:[di],dx ;set the vector to point to our reflector
+ mov dx,segDXCodePM ;Dos Extender real mode code segment
+ mov es:[di+2],dx
+ FSTI
+;
+; All done
+dfsv90: pop es
+ pop di
+ pop dx
+ pop ax
+ ret
+
+; -------------------------------------------------------
+ page
+; -------------------------------------------------------
+; EXTENDED MEMORY MANAGEMENT
+; -------------------------------------------------------
+
+
+comment |PMALLC - protected-mode XRAM allocation routine. RNC with ax =
+selector for bx paragraphs of allocated XRAM. If too little XRAM is
+available, RC with bx = # paragraphs available.
+|
+
+ public pmallc
+
+pmallc: mov cx,pmrg.xbx ;number of paragraphs requested
+ mov dx,cx
+ shl cx,4
+ shr dx,12
+ xor bl,bl
+ call AllocateXmemBlock
+ jnc pmal70
+;
+; Error occured. Return carry flag set, error code in AX, and
+; size of largest available memory block in BX.
+ mov rmrg.xax,ax ;save error code
+ shr cx,4 ;divide return value by 16
+ mov al,dh
+ shl al,4
+ or ch,al
+ shr dx,4
+ jz pmal22 ;see if more than 1 Meg available
+ mov cx,0FFFFh ;and limit it to 1 meg if so
+pmal22: mov rmrg.xbx,cx ;store memory size available
+ or byte ptr rmflags,1 ;Some there. Set CF
+ jmp short pmal72
+;
+; The allocate succeeded. Clear the carry flag returned to the user
+; and return the initial selector in AX.
+pmal70: and byte ptr rmflags, NOT 1
+pmal72: mov rmrg.xax,ax
+ call xfrflg
+ pop ax ;kill return from DOSENTRY
+ jmp LeaveDOSExtender
+
+; -------------------------------------------------------
+; pmfree - Free the specified extended memory block.
+;
+
+ public pmfree
+
+pmfree:
+;
+; Get the selector for the data block to free and ensure that it
+; is a valid selector.
+ mov ax,pmrg.xes
+ cmp ax,SEL_USER ;make sure it is in the user selector range
+ jb pmfr80
+ verw ax ;ensure that we were given a valid selector
+ jnz pmfr80 ; to a data block.
+ call FreeXmemBlock
+ jc pmfr80
+ mov pmrg.xes,0 ;the entry selector no longer is valid, so
+ ; change ES to return a legal value
+ jmp short pmfr90
+;
+; We were given an invalid selector to free.
+pmfr80: mov rmrg.xax,9 ;invalid memory selector error code
+ or byte ptr rmflags,1 ;Some there. Set CF
+ jmp short pmfr92
+;
+; All done
+pmfr90: and byte ptr rmflags, NOT 1
+pmfr92: call xfrflg
+ pop ax ;kill return from DOSENTRY
+ jmp LeaveDOSExtender
+
+; -------------------------------------------------------
+; pmmodb - modify extended memory block size
+
+ public pmmodb
+
+pmmodb:
+ mov cx,pmrg.xbx ;number of paragraphs requested
+ mov dx,cx
+ shl cx,4
+ shr dx,12
+ mov ax,pmrg.xes
+ cmp ax,SEL_USER ;make sure it is in the user selector range
+ jb pmod80
+ verw ax ;ensure that we were given a valid selector
+ jnz pmod80 ; to a data block.
+ xor bl,bl
+ call ModifyXmemBlock
+ jnc pmod90
+;
+; Error occured. Return carry flag set, error code in AX, and
+; size of largest available memory block in BX.
+ mov rmrg.xax,ax ;save error code
+ shr cx,4 ;divide return value by 16
+ mov al,dh
+ shl al,4
+ or ch,al
+ shr dx,4
+ jz pmod22 ;see if more than 1 Meg available
+ mov cx,0FFFFh ;and limit it to 1 meg if so
+pmod22: mov rmrg.xbx,cx ;store memory size available
+ or byte ptr rmflags,1 ;Some there. Set CF
+ jmp short pmod92
+;
+; We were given an invalid selector to modify.
+pmod80: mov rmrg.xax,9 ;invalid memory selector error code
+ or byte ptr rmflags,1 ;Some there. Set CF
+ jmp short pmod92
+;
+pmod90: and byte ptr rmflags,NOT 1
+pmod92: call xfrflg
+ pop ax
+ jmp LeaveDOSExtender
+
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Terminate process -- This routine replaces the apps
+; termination vector in the PSP with ours. This allows
+; us to clean up the dos extender.
+;
+; Entry: nothing
+; Exit: nothing
+; Uses: none
+;
+ assume ds:dgroup,es:nothing
+ public TerminateProcess
+TerminateProcess proc near
+ pusha
+ push es
+ ;
+ ; Get the childs PSP (bugbug do we need to get the current psp?)
+ ;
+
+ SwitchToRealMode
+
+ mov ah,51h
+ pushf
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset tp_10
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+tp_10: FSTI
+
+ ;
+ ; Change the termination vector to point to the dos extender
+ ;
+ mov es,bx
+ mov bx,es:[0ah]
+ mov ax,es:[0ch]
+ mov cx,offset ChildTerminationHandler
+ mov es:[0ah],cx
+ mov cx,segDXCode
+ mov es:[0ch],cx
+
+ ;
+ ; Save the old termination vector for restoration later
+ ;
+ mov cx,segCurrentHostData
+ mov es,cx
+ mov word ptr es:[HdPspTerminate],bx
+ mov word ptr es:[HdPspTerminate+2],ax
+
+ SwitchToProtectedMode
+
+ pop es
+ popa
+ ret
+
+TerminateProcess endp
+
+DXPMCODE ends
+
+;****************************************************************
+
+ end
diff --git a/private/mvdm/dpmi/486/dxstrt.asm b/private/mvdm/dpmi/486/dxstrt.asm
new file mode 100644
index 000000000..d7bdc6f04
--- /dev/null
+++ b/private/mvdm/dpmi/486/dxstrt.asm
@@ -0,0 +1,1736 @@
+ PAGE ,132
+ TITLE DXSTRT.ASM -- Dos Extender Startup Code
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXSTRT.ASM - Dos Extender Startup Code *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains the executive initialization code for *
+;* the Dos Extender. The module DXBOOT.ASM contains the code *
+;* specific to starting up the Dos Extender. The module *
+;* DXINIT.ASM contains the code specific to starting up the *
+;* child program of the Dos Extender. The code in these *
+;* two modules is discarded at the end of the initialization. *
+;* The code in this module calls the initialization routines *
+;* in the other to init modules, and then performs the final *
+;* juggling of things to throw away the low memory init code, *
+;* and transfer control to start up the child program. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 01/09/91 amitc At exit time Co-Processor being reset *
+;* 08/08/90 earleh DOSX and client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 12/08/89 jimmat Added call to reenable EMM driver. *
+;* 08/29/89 jimmat Restores Int 2Fh vector at exit *
+;* 08/20/89 jimmat Removed A20 code, since HIMEM version 2.07 *
+;* A20 code now works properly. *
+;* 06/28/89 jimmat Now calls OEM layer instead of NetMapper *
+;* 06/16/89 jimmat Implemented Windows/386 startup/exit Int *
+;* 2Fh calls and ifdef'd combined EXE code *
+;* 05/17/89 jimmat ChildTerminationHandler sets its SS:SP *
+;* 04/18/89 jimmat Added calls to init/term NetBIOS mapper *
+;* 03/28/89 jimmat Incorporated bug fix from GeneA related *
+;* to nested GP faults in ChildTermHandler *
+;* 03/11/89 jimmat Added support for TSS & LDT *
+;* 03/07/89 jimmat converted to use WDEB386 *
+;* 02/27/89 (GeneA): shrink initial memory block size on entry *
+;* 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 termination code for startup *
+;* errors that occur while in protected mode *
+;* 02/14/89 (GeneA): added code to reduce size of real mode *
+;* code segment to throw away initialization code. *
+;* 01/31/89 (GeneA): created by copying code from the old *
+;* DXINIT.ASM *
+;* 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;****************************************************************
+
+.286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+.sall
+.xlist
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+if VCPI
+include dxvcpi.inc
+endif
+include smartdrv.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+
+include hostdata.inc
+include dpmi.inc
+include intmac.inc
+.list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+WIN386_INIT equ 1605h ;Win/386 startup Int 2Fh
+WIN386_EXIT equ 1606h ;Win/386 shutdown Int 2Fh
+
+WIN386_DOSX equ 0001h ;Win/386 init/exit really DOSX flag
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn InitDosExtender:NEAR
+; extrn ReleaseXmemHeap:NEAR
+ extrn RestoreRMIntrVectors:NEAR
+ extrn InitXmemHeap:NEAR
+ extrn AllocateLDTSelector:NEAR
+ extrn SaveRMIntrVectors:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn InitializeOEM:NEAR
+ extrn TerminateOEM:NEAR
+ extrn AllocateSelector:NEAR
+ extrn FreeSelector:NEAR
+externNP NSetSegmentAccess
+ extrn DupSegmentDscr:NEAR
+ extrn RmUnsimulateProc:FAR
+ extrn Wow16TransitionToUserMode:near
+
+ extrn PMFaultHandlerIRET:NEAR
+ extrn PMFaultHandlerIRETD:NEAR
+ extrn PMIntHandlerIRET:NEAR
+ extrn PMIntHandlerIRETD:NEAR
+ extrn PMDosxIret:NEAR
+ extrn PMDosxIretd:NEAR
+
+if NOT VCPI
+ extrn EMMEnable:NEAR
+endif
+
+if VCPI
+externNP FreeEMSHandle
+endif
+ extrn AllocateXmem32:NEAR
+
+IFNDEF ROM
+ifdef WOW_x86
+ externFP NSetSegmentDscr
+else
+ externFP NSetGDTSegmentDscr
+ENDIF; WOW_x86
+ENDIF; rom
+
+IFDEF ROM
+ extrn ROMEntry:NEAR
+ extrn ROMCleanUp:NEAR
+ extrn ROMInitLDT:NEAR
+ extrn InvokeROMChild:NEAR
+ENDIF
+
+ extrn FreeMemByOwner:NEAR
+ extrn PMIntr19:NEAR
+ extrn PMIntr13:NEAR
+ extrn PMIntr31: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 FarParaToLDTSelector:FAR
+IFNDEF WOW_x86
+ extrn FreeXmemHeap:NEAR
+ENDIF
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXSTACK segment
+
+extrn rgw0Stack:WORD
+extrn rgw2FStack:WORD
+
+DXSTACK ends
+
+; -------------------------------------------------------
+
+DXDATA segment
+ifdef NOT_NTVDM_NOT
+ extrn fHPVectra:BYTE
+endif
+ extrn A20EnableCount:WORD
+ extrn lpfnUserMouseHandler:DWORD
+ extrn lpfnUserPointingHandler:DWORD
+ extrn f286_287:BYTE
+
+if VCPI
+ extrn fVCPI:BYTE
+ extrn segBootPmode:WORD
+endif
+ extrn cDPMIClients:WORD
+ extrn selCurrentHostData:WORD
+ extrn segCurrentHostData:WORD
+
+ extrn fDebug:BYTE
+ extrn lmaIdt:DWORD
+
+ifdef WOW_x86
+ extrn rgwWowStack:word
+ extrn FastBop:fword
+ extrn WowTransitionToUserMode:WORD
+endif
+ extrn RmHwISR:DWORD
+ extrn DpmiFlags:WORD
+
+ org 0
+
+ public rgwStack,npEHStackLimit,npEHStacklet, selEHStack,
+;
+; Pmode fault handler stack used during initialization only.
+;
+ dw 80h dup (?)
+rgwStack label word
+
+;
+; This is the stack area used while running in the interrupt reflector.
+; This is divided up into a number of stack frames to allow reentrant
+; execution of the interrupt reflector. The stack pointer pbReflStack
+; indicates the current stack frame to use. Each entry into the interrupt
+; reflector decrements this variable by the size of the stack frame, and
+; each exit from the interrupt reflector increments it.
+
+C_STKFRAME = 36
+CB_REFLSTACK = C_STKFRAME * CB_STKFRAME
+
+if DEBUG ;--------------------------------------------------------
+ public StackGuard
+StackGuard dw 1022h ;unlikely value to check for stk overrun
+endif ;--------------------------------------------------------
+
+ public pbReflStack,bReflStack
+if DBG
+bReflStack db CB_REFLSTACK dup (0AAh)
+else
+bReflStack db CB_REFLSTACK dup (?)
+endif
+
+;----------------------------------------------------------------------------
+; The following stack is used for hw interrupts on platforms (e.g. x86) where
+; the kernel or ntvdm switches the stack before an interrupt is reflected.
+;----------------------------------------------------------------------------
+IFDEF WOW_x86
+CB_HWINTRSTACK = 1000h
+if DBG
+bHwIntrStack db CB_HWINTRSTACK dup (0AAh)
+else
+bHwIntrStack db CB_HWINTRSTACK dup (?)
+endif
+ public pbHwIntrStack
+pbHwIntrStack dw bHwIntrStack + CB_HWINTRSTACK
+ENDIF ; WOW_x86
+
+pbReflStack dw bReflStack + CB_REFLSTACK
+
+npEHStackLimit dw offset DGROUP:rgwStack
+npEHStacklet dw offset DGROUP:rgwStack
+selEHStack dw 0
+
+ public cdscGDTMax, selGDTFree, segGDT, selGDT
+
+cdscGDTMax dw CDSCGDTDEFAULT
+
+selGDTFree dw ? ;head of list of free descriptors in GDT
+
+segGDT dw 0 ;real mode paragraph address of the GDT
+ ; This variable always stores the real mode
+ ; paragraph address
+
+selGDT dw 0 ;current mode segment/selector for the GDT
+ ; segment. This variable holds the real
+ ; mode paragraph address during initialization
+ ; and then holds the protected mode selector
+ ; when running in protected mode.
+
+ public bpGDT, bpGDTcb, bpGDTbase
+
+bpGDT label fword
+bpGDTcb dw ?
+bpGDTbase dd ?
+
+
+ public cdscIDTMax, segIDT, selIDT
+
+cdscIDTMax dw CDSCIDTDEFAULT
+segIDT dw 0
+selIDT dw 0
+
+ public bpIDT, bpIDTcb, bpIDTbase
+
+bpIDT label fword
+bpIDTcb dw ?
+bpIDTbase dd ?
+
+ public bpRmIVT
+
+bpRmIVT dq 0FFFFh ;This is the segment descriptor for the real
+ ; mode interrupt vector table.
+
+ public selDOSScr
+
+selDOSScr dw 0 ;Scratch selector used for DOS mapping
+
+ public lpfnXMSFunc
+
+lpfnXMSFunc dd 0 ;far pointer to XMS memory driver entry point
+
+ public idCpuType
+
+idCpuType dw 0
+
+ public segPSP, selPSP
+
+segPSP dw ? ;segment address of Dos Extender PSP
+selPSP dw ? ;selector for Dos Extender PSP
+ ; code during processing of GP faults
+
+ public selPSPChild, segPSPChild
+
+segPSPChild dw ? ;real mode segment address of child's PSP
+; note the following in 1, so that in low mem heap management, we can use
+; selPSPChild to mark the owner of the memory.
+selPSPChild dw 1 ;selector of child program's PSP
+
+
+ public DtaSegment, DtaOffset, DtaSelector
+DtaSegment dw 0
+DtaSelector dw 0
+DtaOffset dw 0
+
+
+ public regChildSP, regChildSS, regChildIP, regChildCS
+
+regChildSP dw ? ;initial user stack pointer from exe file
+regChildSS dw ? ;initial user stack segment from exe file
+regChildIP dw ? ;initial user program counter from exe file
+regChildCS dw ? ;initial user code segment from exe file
+
+ public hmemDOSX
+
+hmemDOSX dw 0
+
+IFDEF ROM
+ public segDXCode, segDXData
+
+segDXCode dw ? ;holds real mode paragraph address
+ ; of the code segment
+segDXData dw ? ;holds real mode paragraph address
+ ; of the data segment
+ENDIF
+
+ifdef NOT_NTVDM_NOT
+ public fMicroChannel
+
+fMicroChannel db 0 ;NZ if running on a Micro Channel system
+endif
+
+ public fFaultAbort, ExitCode
+
+fFaultAbort db 0 ;NZ if terminating due to unrecoverable fault
+ExitCode db 0FFh ;exit code to use when terminating
+fQuitting db 0
+
+fEMbit db 0FFh ;MSW EM bit at startup (FF = not checked yet)
+
+ public fUsingHMA
+fUsingHMA db 0
+
+if NTDEBUG
+ public fDebugTrace
+fDebugTrace db 0
+endif
+
+; The one and only Task State Segment is here (it's too small to allocate
+; a block for it in extended memory)
+
+ public sysTSS
+
+sysTSS TSS286 <>
+
+ public HighestDxSel
+HighestDxSel dw 0
+
+; After initialization is complete, the following buffers (rgbXfrBuf0 and
+; rgbXfrBuf1) are used to transfer data between real mode address space
+; and protected mode address space during the processing of various interrupt
+; function calls. During the initialization process, these buffers are also
+; used as temporary work space as described below:
+; CheckCPUType uses the first 6 bytes of rgbXfrBuf0 as scratch space.
+; The functions for moving the Dos Extender protected mode code segment
+; and the GDT and IDT use rgbXfrBuf0 as buffer space for building a
+; parameter block. The child loading code in DXINIT.ASM uses the buffer
+; RELOC_BUFFER for holding the base part of the file name (name.exe portion)
+; of the child exe file while determining the complete path to the child
+; in the case where the child name is specified on the command line. Note,
+; this buffer is also used for holding sectors from the relocation table
+; while loading the program, but that occurs after the child exe file name
+; has been determined.
+; The child loading code in DXINIT.ASM also uses rgbXfrBuf1 to hold several
+; buffers as well. The locations and purposes of these buffers are
+; described in GENDEFS.INC. All of the buffers are defined with equates
+; named EXEC_?????
+;
+; The file search logic in DXFIND.ASM assumes some parameters are setup in
+; the rgbXfrBuf1 file name buffers. Some of the code was moved from
+; DXINIT.ASM to DXFIND.ASM.
+
+ public rgbXfrBuf0, rgbXfrBuf1
+ public npXfrBuf0, npXfrBuf1
+
+ align 2
+
+rgbXfrBuf0 db CB_XFRBUF0 dup (?)
+rgbXfrBuf1 db CB_XFRBUF1 dup (?)
+
+npXfrBuf0 dw offset DGROUP:rgbXfrBuf0
+npXfrBuf1 dw offset DGROUP:rgbXfrBuf1
+
+ifdef FLATAPIXLAT
+DtaBuffer dw 128 dup (0) ; used as the dta for PM if dta changed
+endif
+IFNDEF ROM ;--------------------------------------------------------
+
+; Parameter block for passing to DOS EXEC call to run the child
+; program.
+;
+public exec_par_blk
+
+exec_par_blk dw 0
+cmd_off dw OFFSET EXEC_CMNDLINE
+cmd_seg dw DXDATA
+ dw OFFSET EXEC_FCB0
+fcb1_seg dw DXDATA
+ dw OFFSET EXEC_FCB1
+fcb2_seg dw DXDATA
+
+ENDIF ;ROM ---------------------------------------------------------
+
+
+; The following variables are used during reading the relocation table
+; from the exe file and relocating the child program.
+
+ public fhExeFile
+ public clpRelocItem
+ public plpRelocItem
+
+fhExeFile dw ? ;DOS file handle for the exe file
+clpRelocItem dw ? ;number of relocation items in the exe file
+plpRelocItem dw ? ;pointer to next relocation item in the table
+
+ public SMRTDRVDelta, SMRTDRVName
+
+SMRTDRVDelta SD_IOCTL_WR_GrwShrk <,0> ; Struc to IOCTL write SMARTDRV
+SMRTDRVName db SD_DEV_NAME ; Name for smart drive opens
+
+szDebugHello label byte
+if DEBUG
+ db 'DOSX: Beginning protected mode initialization.',13,10,0
+endif
+ db 0
+
+IFDEF ROM
+ public PrevInt2FHandler
+
+PrevInt2FHandler dd ? ;previous real mode Int 2F handler
+ENDIF
+
+FreeMem dw 0
+
+DXDATA ends
+
+; -------------------------------------------------------
+ page
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+ extrn CodeEnd:NEAR
+ extrn ER_DXINIT:BYTE
+ extrn ER_REALMEM:BYTE
+ extrn RMDefaultInt24Handler:FAR
+
+if VCPI
+
+externNP QEMM386Trap
+
+endif
+
+IFNDEF ROM
+ public segDXCode, segDXData, selDgroup
+
+segDXCode dw ? ;holds the real mode paragraph address
+ ; of the code segment
+segDXData dw ? ;holds the real mode paragraph address
+ ; of the data segment
+
+selDgroup dw ? ;holds the paragraph address/selector for
+ ; DGROUP depending on the current mode
+
+ public PrevInt2FHandler, PrevInt69Handler
+
+PrevInt2FHandler dd ? ;previous real mode Int 2F handler
+PrevInt69Handler dd ? ;previous HP Vectra A & A+ keyboard interrupt
+
+ENDIF
+
+IFDEF ROM
+externFP NSetSegmentDscr
+ENDIF
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+ extrn CodeEndPM:NEAR
+
+ org 0
+
+IFDEF ROM ;--------------------------------------------------------
+
+ extrn segRomDXCODE:ABS
+
+ public selDgroupPM, segDXCodePM
+
+selDgroupPM dw SEL_DXDATA OR STD_RING ;This variable contains the
+ ; data segment selector
+segDXCodePM dw segRomDXCODE ;This variable contains the paragraph
+ ; address of the real mode code segment
+
+ELSE ;ROM ---------------------------------------------------------
+
+ public selDgroupPM, segDXCodePM, segDXDataPM
+
+selDgroupPM dw ? ;This variable always contains the
+ ; data segment selector
+segDXCodePM dw ? ;This variable contains the paragraph
+ ; address of the real mode code segment
+segDXDataPM dw ? ;This variable contains the paragraph
+ ; address of the data segment
+ENDIF ;ROM ---------------------------------------------------------
+
+
+IFNDEF ROM
+externFP NSetSegmentDscr
+ENDIF
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+BeginLowSegment
+
+; -------------------------------------------------------
+; DOS EXTENDER ENTRY FUNCTION
+; -------------------------------------------------------
+;
+; This is the program entry point for the Dos Extender. This
+; function decides if the Dos Extender is being run as a single
+; time operation to extend a single program, or if it is being
+; run as a TSR to wait for programs to request its services
+; later on.
+;
+
+ assume ds:PSPSEG,es:PSPSEG,ss:NOTHING
+ public start
+
+start:
+
+IFDEF ROM ;--------------------------------------------------------
+
+ call ROMEntry ;setup special ROM environment
+ assume ds:DGROUP
+
+ mov ax,ds ;code below expects DGROUP in ax
+
+ELSE ;ROM ---------------------------------------------------------
+
+; Set up the initial segment registers.
+
+ mov ax,DGROUP
+ mov ds,ax
+ assume ds:DGROUP
+
+ENDIF ;ROM ---------------------------------------------------------
+
+ mov segPSP,es ;save the PSP segment address
+
+ mov ss,ax
+ mov sp,offset DGROUP:rgwStack
+ assume ss:DGROUP
+
+;
+; Set up the INT 24h handler. The default INT 24h handler fails the
+; system call in progress, for DPMI compatibility.
+;
+
+ push ds
+ mov ax,cs
+ mov ds,ax
+ mov dx,offset DXCODE:RMDefaultInt24Handler
+ mov ax,2524h
+ int 21h
+ pop ds
+
+; Issue the Win/386 startup Int 2Fh (almost) first thing
+
+ push ds
+
+ mov ax,WIN386_INIT ;gives other PM software a chance
+ xor bx,bx ; to disable itself, release XMS, etc.
+ mov cx,bx
+ mov si,bx
+ mov ds,bx
+ mov es,bx
+ assume ds:NOTHING, es:NOTHING
+ mov dx,WIN386_DOSX
+ mov di,DXVERSION
+ int 2Fh
+
+ pop ax ;restore ds/es DGROUP addressability
+ mov ds,ax
+ mov es,ax
+ assume ds:DGROUP, es:DGROUP
+
+
+ jcxz Allow_Startup ;if cx still == 0, keep going (we ignore
+ ; all the other possible return values)
+if VCPI
+
+ cCall QEMM386Trap
+ je Allow_Startup
+
+endif ; VCPI
+
+ mov ExitCode,2 ; otherwise we should abort now
+ jmp RealModeCleanUp
+
+Allow_Startup:
+
+; Initialize the Dos Extender
+
+ call InitDosExtender ;NOTE: passes data to InitChildProgram
+ jnc @F ; in rgbXfrBuf1 -- don't overwrite it!
+ jmp strt88
+@@:
+
+; Save the state of the MSW EM bit (Emulate Math coprocessor), and turn
+; it off (win87em wants it off).
+
+ifndef WOW_x86
+ smsw ax
+
+ out1 <What to do about this?>
+
+ test al,01h ;in V86 mode?
+ jnz @f ; can't do the lmsw if so
+
+ push ax
+ and al,04h
+ mov fEMbit,al
+ pop ax
+ and al,not 04h
+ lmsw ax
+@@:
+endif ; WOW_x86
+; Switch the machine into protected mode.
+
+ FCLI
+ call SaveRMIntrVectors
+
+ SwitchToProtectedMode
+
+ public DXPMInit
+DXPMInit LABEL BYTE
+
+if DEBUG
+ Trace_Out "*******************************************************************************"
+ Trace_Out "*******************************************************************************"
+ Trace_Out "**** ****"
+ Trace_Out "**** THIS IS A DEBUG RELEASE THIS IS A DEBUG RELEASE ****"
+ Trace_Out "**** ****"
+ Trace_Out "*******************************************************************************"
+ Trace_Out "*******************************************************************************"
+endif
+;
+; The following code MUST appear in both the retail and debug versions.
+; It uses a side effect of the WDEB386 conditional breakpoint command
+; to cause WDEB386 to initialize its copy of the protected mode IDT
+; register. [ERH] 06-Dec-1990
+;
+ cmp fDebug,0
+ je @F
+ lea si,szDebugHello
+ mov ax,0F001h ; WDEB386 conditional breakpoint
+ int 41h
+@@:
+
+ assume ds:DGROUP,es:NOTHING
+
+ mov ax,SEL_IDT or STD_RING
+ mov selIDT,ax
+ mov ax,SEL_LDT_ALIAS OR STD_RING
+ mov selGDT,ax
+
+; Initialize the LDT now that we are in protected mode. First, set the
+; contents to zero.
+
+ mov es,selGDT ; actually LDT
+ assume es:NOTHING
+ mov cx,cdscGDTMax
+IFDEF WOW
+ mov di,GDT_SIZE
+ sub cx,di
+ENDIF
+ shl cx,2 ; CX = words in LDT segment
+ xor ax,ax ; AX = 0
+IFNDEF WOW
+ mov di,ax
+ENDIF
+ cld
+ rep stosw ; CX = 0
+
+ dec cx ; CX = 0FFFFh
+
+ push ax
+ mov ax,es
+ DPMIBOP PassTableAddress
+ pop ax
+
+;
+; Set the two scratch selectors to 64k data starting at zero. Actual
+; addresses set as used.
+;
+ cCall NSetSegmentDscr,<SEL_SCR0,ax,ax,ax,cx,STD_DATA>
+ cCall NSetSegmentDscr,<SEL_SCR1,ax,ax,ax,cx,STD_DATA>
+ifndef WOW_x86
+ mov dx,40h*16
+ cCall NSetGDTSegmentDscr,<040h,ax,dx,ax,cx,STD_DATA>
+endif
+
+; Place all descriptors beginning at SEL_USER on a linked list of
+; free descriptors. A descriptor is identified as being free by
+; being on this list, and also by having the access rights byte set
+; to 0. The cbLimit field of the descriptor is used as the link to
+; the next descriptor on the list. Note: the GDT was initialized
+; to 0 above, so the access rights byte is already 0 on these.
+
+ mov di,SEL_USER and SELECTOR_INDEX
+ mov selGDTFree,di ;init head of free list pointer
+ mov dx,cdscGDTMax
+ shl dx,3
+ jnc @F
+ dec dx
+@@:
+ mov ax,di ;pointer to current descriptor
+igdt50: add ax,8 ;point to following one
+ mov es:[di].cbLimit,ax ;store as link in this descriptor
+ jc igdt58 ;in case of segment wrap around
+ cmp ax,dx ;are we at the end of the table
+ jnc igdt58 ;if so, then get out of loop
+ mov di,ax ;point at the next one
+ jmp igdt50 ;and repeat
+
+; We just set the link field of the last one in the list to point to
+; a bogus location. So we need to go back and set it to -1 to indicate
+; end of list.
+
+igdt58: mov es:[di].cbLimit,0ffffh
+
+
+IFDEF ROM
+
+; Initialize the predefined LDT descriptors/selectors for the ROM version.
+; This has to be done now since the ROM Image Builder starts allocating
+; at SEL_USER.
+
+ call ROMInitLDT
+
+ENDIF
+
+
+; Allocate a scratch selector for DOS mapping
+
+
+ call AllocateLDTSelector
+ mov selDOSScr,ax
+
+
+; Initialize the extended memory heap
+
+; call InitXmemHeap
+
+; Bop to initialize 32 bit support.
+
+ push es
+ mov ax,SEL_DXCODE OR STD_RING
+ mov es,ax
+ assume es:DXCODE
+ mov di, sp ;original stack offset
+
+ push SEL_DXPMCODE OR STD_RING
+ push offset DXPMCODE:PMDosxIretd
+ push SEL_DXPMCODE OR STD_RING
+ push offset DXPMCODE:PMDosxIret
+
+ push SEL_DXPMCODE OR STD_RING
+ push offset DXPMCODE:PMIntHandlerIRETD
+ push SEL_DXPMCODE OR STD_RING
+ push offset DXPMCODE:PMIntHandlerIRET
+ push SEL_DXPMCODE OR STD_RING
+ push offset DXPMCODE:PMFaultHandlerIRETD
+ push SEL_DXPMCODE OR STD_RING
+ push offset DXPMCODE:PMFaultHandlerIRET
+
+ push SEL_DXPMCODE OR STD_RING
+ push offset DXPMCODE:FarParaToLDTSelector
+ push SEL_DXCODE OR STD_RING
+ push SEL_DXDATA OR STD_RING
+ push ds
+ push offset DGROUP:DtaBuffer
+ push segDXCode
+ push segDXCode
+ push offset DXCODE:RmUnsimulateProc
+ push word ptr CB_STKFRAME
+ push ds
+ push offset DGROUP:pbReflStack
+ push ds
+ push offset DGROUP:rgbXfrBuf1
+ push ds
+ push offset DGROUP:rgbXfrBuf0
+ push segDXData
+ mov si,sp ;pass stack offset
+ FBOP BOP_DPMI,InitDosx,FastBop
+ mov sp, di ;restore stack
+ pop es
+ assume es:nothing
+
+ call AllocateExceptionStack
+ FSTI ;don't need ints disabled
+IFNDEF ROM ;--------------------------------------------------------
+
+; Shrink the size of our real mode code segment to throw away init code.
+
+ SwitchToRealMode
+
+if VCPI
+ mov bx,segBootPmode
+ add bx,CBPAGE386 shr 4
+ cmp fVCPI,0
+ jne @F
+endif
+ mov bx,(offset CodeEnd) + 15
+ shr bx,4
+ add bx,segDXCode
+if VCPI
+@@:
+endif
+ sub bx,segPSP
+ push es
+ mov es,segPSP
+ dossvc 4Ah
+ pop es
+
+ call DetermineFreeMemory
+ mov FreeMem,bx
+ SwitchToProtectedMode
+
+ENDIF ;ROM ---------------------------------------------------------
+
+; Initialize the OEM layer. This can allocate DOS memory, so it goes
+; after the final program shrink.
+
+ call InitializeOEM ;currently initializes NetBIOS mapper
+
+if1
+%OUT InitializeOEM can fail!!!!!
+endif
+
+
+; Exec the child application
+
+ SwitchToRealMode
+ FSTI
+;; bugbug hack ...... williamh
+;; turn off A20 line before we terminate and keep resident.
+ cmp A20EnableCount, 0
+ jz A20IsOff
+@@:
+ xmssvc 6
+ dec A20EnableCount
+ jnz @B
+A20IsOff:
+
+ mov ax,segPSP
+ mov es,ax
+ assume es:nothing
+ mov ax,es:[2ch]
+ mov es,ax
+ dossvc 49h ; free env block
+
+ call DetermineFreeMemory
+ mov dx,(offset CodeEnd) + 15
+ shr dx,4
+ add dx,FreeMem
+ sub dx,bx
+ add dx,segDXCode
+ sub dx,segPSP
+ mov al,0
+ dossvc 31h ; tsr.
+help: int 3 ; should never get here
+ jmp help
+
+IFDEF ROM ;--------------------------------------------------------
+
+ call InvokeROMChild ;setup ROM child environment
+ ; and invoke the child app
+
+ ;DOS will transfer directly to ChildTerminationHandler if child
+ ;does a DOS Exit call, comes back here if child app returns (usually
+ ;an init error). The registers may be in a strange state here so be
+ ;careful....
+
+ELSE ;ROM ---------------------------------------------------------
+
+ lea bx, exec_par_blk
+ lea dx, EXEC_PROGNAME
+ xor al, al
+ dossvc 4bh
+
+ENDIF ;ROM ---------------------------------------------------------
+
+; If we get here, the EXEC failed for some reason!
+
+ mov bx,offset DXCODE:ER_REALMEM ;assume insufficient memory
+ xchg ax,bx
+ cmp bx,8 ;is it really?
+ jz strt88
+ mov ax,offset DXCODE:ER_DXINIT ;no, use generic msg
+
+strt88:
+ mov ExitCode,1 ;return NZ exit code
+
+ mov dx,cs ;pass msg ptr in DX:AX (ax already set)
+
+ push cs ;fake a far call so no fixup
+ push offset DXCODE:RealModeCleanUp ; needed -- return to clean up
+ jmp near ptr DisplayErrorMsg
+
+; -------------------------------------------------------
+; ChildTerminationHandler -- This routine receives control
+; when the child program running under the Dos Extender
+; terminates. It will free all resources being used by
+; the child. If we were not running TSR, then the Dos
+; Extender will complete cleaning up and terminate itself.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses:
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+ public DosxTerminationHandler
+DosxTerminationHandler:
+
+IFDEF ROM
+ GetRMDataSeg
+ mov ds,ax
+ mov es,ax
+ mov ss,ax
+ELSE
+ mov ds,selDgroup
+ mov es,selDgroup
+ mov ss,selDgroup ;make sure we know where the
+ENDIF
+ mov sp,offset DGROUP:rgwStack ; stack is when we get here
+
+ assume ds:DGROUP,es:DGROUP
+
+; Check if we are already in the middle of a termination sequence and
+; bail out if so. This will prevent us from hanging up in an infinite
+; loop if we get a GP fault while we are quitting.
+
+ cmp fQuitting,0
+ jz @f
+ jmp chtm90
+@@:
+ mov fQuitting,1
+
+; Terminate the OEM layer
+
+ call TerminateOEM ;current term's NetBIOS mapper & low net heap
+
+; Make sure that no interrupt vectors still point to us.
+
+ call RestoreRMIntrVectors
+
+; if this is a 80286 & 80287 configuration, we should reset the Co-Processor.
+
+ cmp [f286_287],0 ;286 and 287 config ?
+ jz CTH_Not286And287 ;no.
+
+; reset the 80287 Co-Processor to make sure that it gets out of protected
+; mode.
+
+ xor al,al ;need to out 0
+ out 0F1H,al ;reset the coprocessor
+
+CTH_Not286And287:
+
+; If we're aborting due to a processor fault, do some extra clean-up on
+; the mouse (just to be nice). If this is a normal termination, we leave
+; it up to the child to save/restore the mouse state.
+
+ test fFaultAbort,0FFh
+ jz normal_exit
+
+; Check if the mouse driver callback function has been set, and
+; reset the mouse driver if so.
+
+ mov ax,word ptr lpfnUserMouseHandler
+ or ax,word ptr lpfnUserMouseHandler+2
+ jz @F
+ xor ax,ax
+ int 33h
+@@:
+
+; Check if the PS/2 Pointing Device Handler Address has been set and
+; disable it if so.
+
+ mov ax,word ptr lpfnUserPointingHandler
+ or ax,word ptr lpfnUserPointingHandler+2
+ jz @f
+ mov ax,0C200h
+ xor bh,bh
+ int 15h
+@@:
+
+; Hmmm, we have HP mouse code in dxhpbios.asm, but no clean up here...
+
+normal_exit:
+
+; Release the extended memory heap.
+
+; call ReleaseXmemHeap
+
+; Release the space being used for the descriptor tables and
+; the protected mode code segment.
+
+if VCPI
+ cmp fVCPI,0 ;space not in himem area if under VCPI
+ jz @f
+ call FreeEmsHandle ;under VCPI, we free EMS memory this way
+ jmp short chtm90
+@@:
+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
+@@:
+
+; Clean up after real mode code (and possible real mode incomplete
+; initialization) -- restore real mode interrupt vectors.
+
+chtm90:
+RealModeCleanUp:
+
+; Disable A20 if it was left enabled (normally 1 on 386 systems, 0 on 286)
+
+if VCPI
+ cmp fVCPI,0 ;don't diddle A20 if under VCPI
+ jnz A20Okay
+endif
+ mov cx,A20EnableCount
+ jcxz A20Okay
+@@: xmssvc 6
+ loop @b
+A20Okay:
+
+; Restore the MSW EM bit (Emulate Math coprocessor) to its initial state
+ifndef WOW_x86
+ inc fEMbit ;if fEMbit = FF, we never got far 'nuff to
+ jz @f ; change it
+ dec fEMbit
+
+ smsw ax
+if VCPI
+ out1 <What to do about this? FreeEMSHandle may put us in real mode.>
+endif ; VCPI
+ test al,01h ;in V86 mode?
+ jnz @f ; can't do the lmsw if so
+ or al,fEMbit
+ lmsw ax
+@@:
+endif
+; Make sure DOS knows that the DOS extender is the current process
+
+ mov bx,segPSP
+ dossvc 50h
+
+; Restore SmartDrv cache size if it was shrunk
+
+ cmp [SMRTDRVDelta.SD_I_W_GS_Size],0
+ je short NoSmartReset
+
+ mov dx,offset DGROUP:SMRTDRVName
+ mov ax,3D02h
+ int 21h
+ jc short NoSmartReset
+
+ mov bx,ax
+ mov [SMRTDRVDelta.SD_I_W_FuncGS],SD_IOCTL_WR_Grow_Cache
+ mov dx,offset DGROUP:SMRTDRVDelta
+ mov cx,SIZE SD_IOCTL_WR_GrwShrk
+ mov ax,4403h
+ int 21h
+
+ mov ax,3E00H
+ int 21h
+
+NoSmartReset:
+
+; Reenable a friendly EMM driver if we disabled it at startup
+
+if NOT VCPI
+ call EMMEnable
+endif
+
+; Restore real mode interrupt vectors
+
+ push ds
+
+ lds dx,PrevInt2FHandler ;Int 2Fh handler
+ assume ds:NOTHING
+
+ mov ax,ds ;may not have gotten far enough to
+ or ax,dx ; set interrupt vectors, so make
+ jz @f ; sure before resotring.
+
+ mov ax,252Fh
+ int 21h
+
+IFNDEF ROM
+ifdef NOT_NTVDM_NOT
+ test fHPVectra,HP_CLASSIC
+ jz @f
+
+ mov ax,2569h
+ lds dx,PrevInt69Handler ;HP Vectra A & A+ keyboard slime
+ int 21h
+endif
+ENDIF
+@@:
+ pop ds
+ assume ds:DGROUP
+
+
+IFDEF ROM ;Do any ROM specific clean up
+ call ROMCleanUp
+ENDIF
+
+; We have cleaned up after ourselves, so now we can quit.
+
+ mov ax,WIN386_EXIT ;use Win/386 exit Int 2Fh to tell any
+ mov dx,WIN386_DOSX ; interested parties that we are
+ int 2Fh ; terminating
+
+ mov al,ExitCode ;exit the extender with either the exit
+ cmp al,0FFH ; status from the child, or a status
+ jnz @f ; that we have forced
+ dossvc 4Dh ;get child exit status if we haven't forced it
+@@:
+ dossvc 4Ch ;say goodnight Gracy...
+
+; -------------------------------------------------------
+; ChildTerminationHandler --
+; Input: none
+; Output: none
+; Errors: none
+; Uses:
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public ChildTerminationHandler
+
+ChildTerminationHandler:
+ sub sp,4 ; room for far ret
+ push ds
+ push es
+ pusha
+ mov si,ss
+ mov di,sp
+
+ mov ds,selDgroup
+ mov es,selDgroup
+ mov ss,selDgroup ;make sure we know where the
+
+ mov sp,offset DGROUP:rgwStack ; stack is when we get here
+
+ assume ds:DGROUP,es:DGROUP
+
+ ;bugbug less than zero?
+ dec cDPMIClients
+
+ ; free xmem allocated to this client
+ SwitchToProtectedMode
+ mov ax,[SelCurrentHostData]
+ mov es,ax
+ mov bx,es:[HdSelPSP] ; get "owner"
+ call FreeMemByOwner
+
+ mov dx, selPspChild
+ DPMIBOP TerminateApp
+
+ test DpmiFlags,DPMI_32BIT
+ jz cth05
+
+;
+; Return the stack frame used by the Wow32Intr16 interrupt handler
+;
+ add pbReflStack,CB_STKFRAME
+cth05: cmp cDPMIClients,0
+ jne cth10
+
+
+ mov ax,selEHStack
+ or ax,ax ;did we allocate one?
+ jz short @f
+ call FreeSelector
+@@:
+;
+; Reset the exception stack pointer
+;
+ mov selEHStack,0
+
+;
+; Give back the xms memory
+;
+ FCLI
+ FBOP BOP_DPMI,FreeAllXmem,FastBop
+
+ call ReInitIdt
+ push 0
+ pop es
+ call ReInitGdt
+ FBOP BOP_DPMI,DpmiNoLongerInUse,FastBop
+
+cth10:
+ SwitchToRealMode
+
+ ;bugbug put resource cleanup code here
+ cmp cDPMIClients,0
+ jne cth20
+
+cth20: mov ax,[SegCurrentHostData]
+ mov es,ax
+ mov ax,es:[HdSegParent]
+ mov [SegCurrentHostData],ax
+ mov ax,es:[HdSelParent]
+ mov [SelCurrentHostData],ax
+ mov ax,es:[HdPSPParent]
+ mov SelPSPChild,ax
+ mov ax,word ptr es:[HdPspTerminate + 2]
+ mov bx,word ptr es:[HdPspTerminate]
+ mov cx,segPSPChild
+ mov ds,cx
+ assume ds:nothing
+ ;
+ ; Restore termination vector (app may think it knows what's here)
+ ;
+ mov ds:[0ah],bx
+ mov ds:[0ch],ax
+ xor cx,cx
+ mov ds,cx
+ ;
+ ; Restore int 22 vector (terminate) Just in case
+ ;
+ mov ds:[22h * 4],bx
+ mov ds:[22h * 4 + 2],ax
+ mov es,si
+ mov es:[di+20],bx
+ mov es:[di+22],ax
+ mov ss,si
+ mov sp,di
+ popa
+ pop es
+ pop ds
+ retf
+
+; -------------------------------------------------------
+; DisplayErrorMsg -- Display an error message on the screen. We set the
+; video adapter to a text mode so the msg is visable (and gets rid
+; of any bizarre mode that others may have set (like WIN.COM)).
+;
+; Note: This routine can be executed in real OR protected mode, so
+; don't do anything mode specific--keep is short and sweet.
+;
+; Input: AX - offset of msg to display
+; DX - segment of msg to display
+; Output: None.
+; Uses: AX, DX modified, all else preserved
+
+ assume ds:NOTHING, es:NOTHING
+ public DisplayErrorMsg
+
+DisplayErrorMsg proc far
+
+ push ds ;save current DS
+ push ax ;save msg offset
+
+; Set a text mode (normally 3, but possibly 7)
+
+ mov ax,0003h ;set video mode 3
+ int 10h
+ mov ah,0Fh ;get video mode
+ int 10h
+ cmp al,03h ;did we change to 3?
+ jz @f
+ mov ax,0007h ;no, must be mode 7
+ int 10h
+@@:
+
+; Display the msg
+
+ mov ds,dx
+ pop dx
+ dossvc 9
+
+ pop ds
+
+ ret
+
+DisplayErrorMsg endp
+
+; -------------------------------------------------------
+; DetermineFreeMemory -- Determine how much memory is free
+;
+; Input: none
+; Output: bx = #paragraphs free memory
+; Uses: bx
+;
+ assume ds:dgroup,es:nothing
+ public DetermineFreeMemory
+
+DetermineFreeMemory proc near
+ push ax
+ mov bx,0ffffh ; allocate all of memory
+ dossvc 48h
+ jnc @f
+
+ pop ax
+ ret
+
+;bugbug report error
+@@: mov bx,0ffffh
+ pop ax
+ ret
+DetermineFreeMemory endp
+EndLowSegment
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+;--------------------------------------------------------
+; ReInitIdt -- Set Idt entries back to default values
+;
+; Input: none
+; Output: none
+; uses: none
+;
+ assume ds:dgroup,es:nothing
+ public ReInitIdt
+
+ReInitIdt proc near
+
+ push bx
+ push cx
+ push dx
+ push si
+ push di
+ push es
+ mov ax,SEL_IDT OR STD_RING
+ mov es,ax
+
+; Fill the IDT with interrupt gates that point to the fault handler and
+; interrupt reflector entry vector.
+
+ xor di,di
+
+ mov dx,offset DXPMCODE:PmIntrEntryVector
+ mov cx,cdscIdtMax
+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
+
+; Now, fix up the ones that don't point to the interrupt reflector.
+IFDEF WOW_x86
+ 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_x86
+ 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
+ mov es:[70h*8].offDest, offset DXPMCODE:PMIntr70
+ .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_DXPMCODE OR STD_RING
+ push edx
+ push 0
+ push 0
+ push 0
+ DPMIBOP SetFaultHandler
+ add sp,18
+ inc di
+ add dx,5
+ 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
+ifdef WOW_x86
+
+
+;
+; Put back the 16 bit transition code
+;
+ call AllocateSelector
+ mov bx,ax
+ mov ax,cs
+ call DupSegmentDscr
+ cCall NSetSegmentAccess,<bx,STD_DATA>
+ mov es,bx
+ assume es:DXPMCODE
+
+ mov [WowTransitionToUserMode],offset DXPMCODE:Wow16TransitionToUserMode
+ assume es:nothing
+ mov ax,es
+ call FreeSelector
+ xor ax,ax
+ mov es,ax
+
+ .286p
+endif
+
+;
+; Restore hooked RM handlers
+;
+ push ds
+ mov bx,SEL_DXCODE OR STD_RING
+ mov ds,bx
+ assume ds:DXCODE
+ mov bx,segDXCode
+ pop ds
+ assume ds:DGROUP
+
+ push SEL_RMIVT or STD_RING ;selector to real mode int table
+ pop es
+ xor di, di
+ mov cx,78h ;last possible hooked interrupt+1
+
+iidt80:
+ cmp word ptr RmHwISR[di],0 ;is this a hooked int?
+ jnz @f ;jif yes
+ cmp word ptr RmHwISR[di+2],0 ;is this a hooked int?
+ jz iidt85 ;jif no
+@@:
+ mov ax,es:[di + 2]
+ cmp ax,bx ; if it isn't really hooked,
+ jne iidt84 ; don't change it
+
+ mov ax,word ptr RmHwISR[di] ;get offset of old RM vector
+ mov es:[di],ax
+ mov ax,word ptr RmHwISR[di+2] ;get segment of old RM vector
+ mov es:[di+2],ax
+iidt84: xor ax,ax
+ mov word ptr RmHwISR[di],ax ; forget old handler
+ mov word ptr RmHwISR[di+2],ax ; forget old handler
+iidt85:
+ add di,4
+ loop iidt80
+
+IFDEF WOW_x86
+; make the stacks 16 bit again
+
+ cCall NSetSegmentAccess,<selDgroupPM,STD_DATA>
+ cCall NSetSegmentAccess,<selEHStack,STD_DATA>
+ENDIF
+; All done
+
+iidt90: pop es
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ ret
+ReInitIdt endp
+
+ assume ds:DGROUP,es:NOTHING
+ public ReInitGdt
+ReInitGdt proc near
+
+ push ax
+ push dx
+ push di
+ push es
+
+ mov dx,cdscGDTMax
+ shl dx,3
+ jnc @f
+ dec dx
+@@:
+ mov ax,HighestDxSel
+ and ax,NOT SELECTOR_TI
+ add ax,8
+ mov selGDTFree,ax
+ mov es,selGDT
+
+rig30: mov di,ax
+ add ax,8
+ mov word ptr es:[di],ax ; set limit as link to next
+ mov word ptr es:[di + 2],0
+ mov word ptr es:[di + 4],0
+ mov word ptr es:[di + 6],0
+ jc rig40
+ cmp ax,dx
+ jnc rig40
+ jmp rig30
+
+rig40: pop es
+ pop di
+ pop dx
+ pop ax
+ ret
+ReInitGdt endp
+
+;--------------------------------------------------------
+; AllocateExceptionStack -- Get space for exception handler
+;
+; Input: none
+; Output: none
+; carry set on error
+; uses: AX, BX, CX, DX, SI, DI
+;
+ assume ds:dgroup,es:nothing
+ public AllocateExceptionStack
+AllocateExceptionStack proc near
+
+ cmp selEHStack, 0 ;have we allocated one already
+ jnz aes_ok ;yes, return no carry
+
+ xor bx,bx
+ mov dx,bx
+ mov cx,1000h ;length of block
+ call AllocateXmem32
+ jc @F
+
+ call AllocateSelector
+ or ax,STD_RING
+ mov selEHStack,ax
+
+ cCall NSetSegmentDscr,<ax,bx,cx,0,0fffh,STD_DATA>
+ mov ax,selEHStack
+
+ mov cx,1000h ;reload length
+ dec cx
+ and cx,0fffeh ; Make sure SP is WORD aligned
+ mov npEHStackLimit,cx
+
+ ;; mark the stack with 0DEADh
+ mov bx, cx
+ push ds
+ mov ds,ax
+ sub bx,2
+ mov word ptr [bx],0DEADh
+ pop ds
+ mov npEHStacklet, bx
+
+ push es
+ mov ax, selEHStack
+ mov es, ax
+ mov bx, npEHStackLimit
+ DPMIBOP InitializePmStackInfo
+ pop es
+
+aes_ok:
+ clc
+@@:
+ ret
+
+AllocateExceptionStack endp
+
+DXPMCODE ends
+
+;****************************************************************
+
+ end start
diff --git a/private/mvdm/dpmi/486/dxutil.asm b/private/mvdm/dpmi/486/dxutil.asm
new file mode 100644
index 000000000..7e3f5ceb1
--- /dev/null
+++ b/private/mvdm/dpmi/486/dxutil.asm
@@ -0,0 +1,2980 @@
+ PAGE ,132
+ TITLE DXUTIL.ASM -- Dos Extender Miscellaneous Routines
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXUTIL.ASM - Dos Extender Miscellaneous *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains miscellaneous routines for the Dos *
+;* Extender. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 08/08/90 earleh DOSX and client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 04/09/90 jimmat If 286 with 287, put 287 into pMode too. *
+;* 08/20/89 jimmat Removed local A20 code since HIMEM 2.07 *
+;* works properly across processor resets *
+;* 07/28/89 jimmat Added A20 check/set routines, added *
+;* SelOff2SegOff & Lma2SegOff routines. *
+;* 06/19/89 jimmat Set direction flag before REP MOVS *
+;* 05/25/89 jimmat Added GetSegmentAccess routine *
+;* 03/30/89 jimmat Set IOPL = 3 when entering protect mode *
+;* 03/16/89 jimmat Added more debug sanity checks *
+;* 03/15/89 jimmat Minor changes to run child in ring 1 *
+;* 03/13/89 jimmat Added support for LDT & TSS *
+;* 02/10/89 (GeneA): changed Dos Extender from small model to *
+;* medium model. Also added MoveMemBlock function. *
+;* 01/25/89 (GeneA): changed initialization of real mode code *
+;* segment address in EnterRealMode. caused by adding *
+;* new method of relocationg dos extender for PM operation *
+;* 12/13/88 (GeneA): moved EnterProtectedMode and EnterReal- *
+;* Mode here from dxinit.asm *
+;* 09/16/88 (GeneA): created by extracting code from the *
+;* SST debugger modules DOSXTND.ASM, VIRTMD.ASM, *
+;* VRTUTIL.ASM, and INTERRPT.ASM *
+;* 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI *
+;* 24-Jan-1992 v-simonf Added WOW callout when INT 8 hooked *
+;* *
+;****************************************************************
+
+.286p
+.287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+; .sall
+; .xlist
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include dpmi.inc
+if VCPI
+include dxvcpi.inc
+endif
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+.list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+SHUT_DOWN = 8Fh ;address in CMOS ram of the shutdown code
+CMOS_ADDR = 70h ;i/o address of the cmos ram address register
+CMOS_DATA = 71h ;i/o address of the cmos ram data register
+
+DMAServiceSegment equ 040h ;40:7B bit 5 indicates DMA services
+DMAServiceByte equ 07Bh ; are currently required
+DMAServiceBit equ 020h
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn segGDT:WORD
+ extrn segIDT:WORD
+ extrn selGDT:WORD
+ extrn selIDT:WORD
+ extrn selGDTFree:WORD
+ extrn selEHStack:WORD, npEHStackLimit:WORD
+ extrn bpGDT:FWORD
+ extrn bpIDT:FWORD
+ extrn bpRmIVT:FWORD
+ extrn idCpuType:WORD
+ extrn rgbXfrBuf1:BYTE
+ extrn i31HWReset:BYTE
+ifdef NOT_NTVDM_NOT
+ extrn fHPVectra:BYTE
+endif
+ extrn PMFaultVector:DWORD
+ extrn lpfnXMSFunc:DWORD
+ extrn PMInt24Handler:DWORD
+
+if VCPI
+ extrn fVCPI:BYTE
+endif
+
+IFDEF ROM
+ extrn segDXCode:WORD
+ extrn segDXData:WORD
+ENDIF
+ extrn pbReflStack:WORD
+
+bIntMask db 0
+
+bpBogusIDT df 0 ;This is loaded into the IDT register to
+ ; force a bogus IDT to be defined. When we
+ ; then do an interrupt a triple fault will
+ ; occur forcing the processor to reset. This
+ ; is when doing a mode switch to real mode.
+
+IDTSaveArea dw 3 DUP (?) ;save area for IDT during mode switch
+
+ public A20EnableCount
+
+A20EnableCount dw 0
+
+ShutDownSP dw 0 ;stack pointer during 286 reset
+
+ public f286_287
+
+f286_287 db 0 ;NZ if this is a 286 with 287 coprocessor
+
+
+ EXTRN FasterModeSwitch:WORD
+
+if DEBUG ;------------------------------------------------------------
+
+ extrn fTraceA20:WORD
+ extrn fTraceMode:WORD
+
+ public fA20
+
+fA20 db 0
+
+endif ;DEBUG --------------------------------------------------------
+
+selPmodeFS dw 0
+selPmodeGS dw 0
+
+ public HighestSel
+HighestSel dw 0
+
+ifndef WOW_x86
+ public IretBopTable
+IretBopTable label byte
+ irp x,<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>
+ db 0c4h, 0c4h, 05dh, x
+ endm
+else
+ public FastBop
+FastBop df 0
+
+IretBopTable label byte
+ irp x,<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>
+ db 02eh, 066h, 0FFh, 01eh, 00h, 00h, 05dh, x
+ endm
+
+NullSel dd 0
+ dd 0
+endif
+ extrn DpmiFlags:WORD
+DXDATA ends
+
+ifndef WOW_x86
+DXSTACK segment
+
+extrn rgw0Stack:WORD
+
+DXSTACK ends
+endif
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn segDXData:WORD
+ extrn segDXCode:WORD
+ extrn selDgroup:WORD
+ENDIF
+
+if VCPI
+ extrn fnVCPI:FWORD
+
+ EXTRN laVTP:DWORD
+
+endif
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Real/Protected Mode Switch Routines
+ page
+
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; REAL/PROTECTED MODE SWITCH ROUTINES
+; -------------------------------------------------------
+;
+; EnterProtectedMode -- This routine will switch the processor
+; into protected mode. It will return with the processor
+; in protected mode and all of the segment registers loaded
+; with the selectors for the protected mode segments.
+; (CS with the selector for DXCODE and DS,ES,SS with the
+; selector for DXDATA)
+; It will also switch mode dependent memory variables.
+; It assumes that InitGlobalDscrTable and InitIntrDscrTable
+; have been called to set up the descriptor tables appropriately.
+;
+; Note: Except for a very brief time in this routine and in
+; EnterRealMode, the DOS Extender runs in the same ring along
+; with it's child app. This has the benefit of eliminating
+; ring transitions on hardware and software interrupts.
+; It also makes it possible for the child to hook their
+; own interrupt routine into the IDT.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: AX, DS, ES, SS, CS modified, all others preserved
+;
+; NOTE: This routine turns interrupts of and does not turn them
+; back on.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public EnterProtectedMode
+
+EnterProtectedMode proc near
+
+ FCLI
+
+IFNDEF ROM
+; Update the mode dependent variables.
+
+ mov ax,SEL_DXDATA or STD_RING
+ mov selDgroup,ax
+ENDIF
+
+; Set the DMA services required bit for pMode users.
+
+ mov ax,DMAServiceSegment
+ mov es,ax
+ or byte ptr es:[DMAServiceByte],DMAServiceBit
+
+if VCPI
+ cmp fVCPI,0 ;if vcpi, don't touch
+ jne epmVCPIstart ; the a20 line, et al
+endif
+
+; 'local enable' the A20 line via HIMEM before switching to pMode.
+; This is more complicated than you might think. Some real mode code
+; (like old versions of SMARTDRV.SYS) may diddle with A20 on their own.
+; These programs may not want us to change A20 on them. RMIntrReflector
+; may do a XMS 'local enable' to turn A20 back on for one of these pgms.
+; Also, on a 386 where we actually do the mode switch, we try to leave
+; A20 enabled so as to not waste time diddling for nothing. The
+; A20EnabledCount variable tracks if we've 'local enabled' A20 or not.
+; Since we can't really trust real mode to leave A20 alone, we double
+; check that it's really really on when we think it should be.
+
+ push bx ;save bx around XMS calls
+
+ cmp A20EnableCount,0 ;should A20 already be enabled?
+ jz enpm10 ; no, (normal 4 286) just go enable it
+
+ xmssvc 7 ; yes, is it really enabled?
+ or ax,ax
+ jnz enpm15 ; yes, we be done!
+
+if DEBUG ;------------------------------------------------------------
+ or fA20,1 ; somebody done us wrong
+endif ;---------------------------------------------------------------
+
+ xmssvc 6 ;keep enable/disable calls balanced
+ dec A20EnableCount
+enpm10:
+ xmssvc 5 ;local enable A20
+ inc A20EnableCount
+
+if DEBUG ;------------------------------------------------------------
+
+ or ax,ax
+ jnz @f
+ or fA20,2 ;enable failed!
+@@:
+ cmp fTraceA20,0
+ jz @f
+ xmssvc 7 ;in debug mode, make double sure
+ or ax,ax ; A20 was enabled. Slows things
+ jnz @f ; down, but it's better to know.
+ or fA20,2
+@@:
+endif ;DEBUG --------------------------------------------------------
+
+enpm15: pop bx
+
+ifndef WOW_x86
+; Make sure that the nested task flag is clear
+
+ pushf
+ pop ax
+ and ax,NOT 4000h
+ push ax
+ npopf
+
+; Make sure that we have the appropriate descriptor tables in effect,
+; and switch the machine into protected mode
+
+enpr20: smsw ax ;get current machine state
+ or ax,1 ;set the protected mode bit
+ lgdt bpGDT
+ lidt bpIDT
+ lmsw ax ;and away we go
+
+; Flush the instruction queue and load the code segment selector
+; by doing a far jump.
+
+ db 0EAh ;jump far opcode
+ dw offset enpm40 ;offset of far pointer
+ dw SEL_DXCODE0 ;selector part of PM far pointer (ring 0)
+
+if VCPI
+
+; Switch when we are under VCPI
+
+epmVCPIstart:
+
+ .386
+
+ push esi ;save regs modified by VCPI server
+ push eax ; (saving high word of EAX)
+
+ push bp ;save bp on stack & sp in bp
+ mov bp,sp
+
+ mov esi, laVTP ;linear address of structure
+
+ RMvcpi vcpiSWITCHTOPM ;go for it
+
+; Entry point (PM) when running under vcpi
+
+PUBLIC epmVCPI
+epmVCPI:
+ mov ax,SEL_DXDATA0 ;stack has gotta be ring 0
+ mov ss,ax
+ mov sp,bp ;restore sp
+
+ pop bp ;restore old bp
+
+ pop eax ;restore regs that VCPI server may have zapped
+ pop esi
+
+ .286p
+
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds,ax ;ds to our DGROUP
+ mov es,ax ;es too
+
+ jmp short enpmSwitchRing ;rejoin common code
+endif
+
+
+; Load the other segment registers with valid selectors (not under VCPI)
+
+enpm40: mov ax,SEL_DXDATA0 ;stack has gotta be ring 0 also
+ mov ss,ax
+
+; Load the LDT register and the Task Register
+
+ mov ax,SEL_LDT
+ lldt ax ;load the LDT register
+
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds,ax ;ds to our DGROUP
+
+ mov ax,SEL_GDT
+ mov es,ax ;es to GDT
+
+ push si ;make sure busy bit is off
+ mov si,SEL_TSS ; in the TSS descriptor
+ mov es:[si].arbSegAccess,STD_TSS ; before trying to load it
+ ltr si ;now load the task register
+ pop si
+else
+ .386p
+ push ebp
+if 0
+ movzx ebp,sp
+else
+ mov ebp,esp
+endif
+ push SEL_DXCODE or STD_RING ; new cs
+ push 0 ; high half eip
+ push offset epmwow ; new eip
+ push SEL_DXDATA or STD_RING ; new ss
+ push ebp
+ push SEL_DXDATA or STD_RING ; new ds
+ DPMIBOP DPMISwitchToProtectedMode
+epmwow:
+ pop ebp
+ .286p
+endif
+
+ push ds ;point es to DGROUP
+ pop es
+
+; If this is a 286 machine with a 287 math coprocessor, put the coprocessor
+; into protected mode also.
+
+ cmp f286_287,0 ;286 and 287?
+ jz @f
+
+ xor al,al ; yup, clear co-processor busy line
+ out 0F0h,al
+ fsetpm ; and put it in pMode
+@@:
+
+; We're currently running in ring 0. Setup an interlevel iret frame
+; to switch to our normal ring, and also force IOPL=3. I spent 1+ day
+; debugging on a 286 system (with no debugger!) because the 286 seemed
+; switch into protected mode with IOPL=0, and once we got to an outer
+; ring, we would fault on things like CLI instructions.
+
+enpmSwitchRing:
+ifndef WOW_x86
+ mov ax,sp ;still points to return address
+ push SEL_DXDATA or STD_RING ;new ss
+ push ax ;new sp
+ pushf
+ pop ax
+ or ah,30h
+ push ax ;new flags, with IOPL=3
+ push SEL_DXCODE or STD_RING ;new cs
+ push offset DXCODE:epm_ret ;new ip
+ iret
+endif
+; When we get here, we are now in an outer ring.
+
+epm_ret:
+
+ cmp idCpuType, 3
+ jc SegRegsOK
+.386
+ mov ax, selPmodeFS
+ mov fs, ax
+ mov ax, selPmodeGS
+ mov gs, ax
+.286p
+
+SegRegsOK:
+if DEBUG ;------------------------------------------------------------
+
+ cmp fTraceMode,0
+ jz @f
+ Trace_Out "^",x
+@@:
+if VCPI
+ cmp fVCPI,0
+ jz @f
+ jmp a20okay
+@@:
+endif ; VCPI
+ cmp A20EnableCount,1
+ jz @f
+ Debug_Out "EnterProtectedMode: A20EnableCount != 1"
+@@:
+ cmp fTraceA20,0
+ jz a20okay
+ test fA20,0FFh
+ jz a20okay
+ test fA20,01h
+ jz @f
+ Trace_Out "EPM: A20 was wrong!"
+@@:
+ test fA20,02h
+ jz @f
+ Trace_Out "EPM: A20 enable failed!"
+@@:
+ test fA20,04h
+ jz @f
+ Trace_Out "rM2pMInt: A20 was on"
+@@:
+ mov fA20,0
+a20okay:
+endif ;DEBUG --------------------------------------------------------
+
+ ret ;near return to caller in pMode
+
+EnterProtectedMode endp
+
+
+; -------------------------------------------------------
+; EnterRealMode -- This routine will switch the processor
+; from protected mode back into real mode. It will also
+; reset the various mode dependent variables to their
+; real mode values and load the segment registers with
+; the real mode segment addresses.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: AX, DS, ES, SS, CS modified
+;
+; NOTE: This routine must be called with the stack segment set
+; to the Dos Extender data segment, as it resets the stack
+; segment register to the Dos Extender real mode data segment
+; but does not modify the stack pointer.
+; NOTE: This routine turns interrupts off and and does not turn
+; them back on.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public EnterRealMode
+
+EnterRealMode proc near
+
+
+if DEBUG ;--------------------------------------------------------
+
+ cmp fTraceMode,0
+ jz @f
+ Trace_Out "v",x
+@@:
+endif ;DEBUG ---------------------------------------------------------
+
+ cmp idCpuType,3
+ jc RegsOkay
+.386
+ mov ax,fs
+ mov selPmodeFS, ax
+ mov ax,gs
+ mov selPmodeGS, ax
+RegsOkay:
+.286p
+
+ FCLI
+
+IFDEF ROM
+ push ds
+ pop es
+ELSE
+ mov es,selDgroup
+ENDIF
+
+IFNDEF WOW_x86
+; If we are running on an 80386, we can do the switch more efficiently.
+
+ cmp idCpuType,3 ;80386?
+ jc enrm20
+
+; We're on an 386 (or better)--do a faster mode switch. Call a ring 0 proc
+; to actually do the switch. The call gate has been setup to select either
+; the roll-our-own 386 switch, or the VCPI switch.
+
+enrm10:
+ db 9Ah ;call far SEL_RESET:0
+ dw 0,SEL_RESET or STD_RING
+
+; The Reset386 routine does a near return (cause the far CS on the stack
+; is for protected mode, and it returns in real mode). Discard the PM
+; CS value.
+
+ pop ax ;discard old PM CS from stack
+
+; The transition to the ring 0 procedure caused a stack switch. The return
+; back to here (in real mode) didn't restore the old stack, so do it now...
+
+ pop sp ;restore previous sp offset
+
+ jmp enrm70 ;jmp to common exit code
+
+
+; On the 80286, it is a lot more complicated. The PC BIOS start up code
+; will check some special locations in the BIOS data segment to see if the
+; machine was reset because it wants to do a mode switch. If this is the
+; case, it will restore the machine state from these variables and return
+; to the original process. We need to set up the state so that the BIOS
+; will return control to us, and then generate a reset. The reset is
+; generated by causing the machine to triple fault. This is an undocumented
+; feature (i.e. bug) in the '286.
+
+enrm20:
+
+; Set up the stack for the BIOS to use after the reset.
+
+ cmp [FasterModeSwitch],0
+ jne enrm21
+
+ pushf ;Push an IRET type return address of where
+ ; we want to come back to after the reset
+ push segDXCode
+ push offset enrm50
+
+enrm21:
+
+ mov ax,segDXData ;Our real mode data segment address
+
+IFNDEF ROM
+ifdef NOT_NTVDM_NOT
+ test fHPVectra,HP_CLASSIC ;HP Vectra A & A+ systems have a bug
+ jz enrm25 ; in their rom that requires a
+ ; different stack setup
+
+ push 0 ;HP Vectra A & A+ trashes this
+ push ax ;real mode data seg
+ pusha
+ push ax ;es?
+ jmp short enrm30
+
+enrm25:
+endif
+ENDIF
+
+ pusha ;The BIOS will do a POPA, so put all of the
+ ; registers on the stack for it.
+ push ax ;It will also pop these into ES and DS
+ push ax
+enrm30:
+
+ push SEL_BIOSDATA ;BIOS data segment selector
+ pop es
+ assume es:BIOS_DATA
+
+ cmp [FasterModeSwitch],0
+ jne enrm31
+; Tell the BIOS where the stack is.
+
+ mov IO_ROM_SEG,ax ;Set up the address of the stack for the
+ mov IO_ROM_INIT,sp ; BIOS to use after the reset
+ jmp enrm32
+enrm31:
+; Tell the BIOS where to transfer control.
+ mov bx,segDXCode
+ mov IO_ROM_SEG,bx
+ mov IO_ROM_INIT,offset DXCODE:enrm45
+
+ mov ShutDownSP,sp ;save stack pointer in data seg
+enrm32:
+; IDT save/restore taken from OS/2 mode switching code...
+;
+; Now preserve three words of the vector table at 03FAh
+; so we can restore it after the mode switch. The ROM
+; trashes the top three words of the real mode IDT by
+; putting a stack at 30:100.
+
+ push ds
+ pop es
+ push SEL_RMIVT or STD_RING ;address real mode IDT
+ pop ds
+ assume ds:NOTHING,es:DGROUP
+
+ MOV SI,03FAH ;BIOS will pop regs, so
+ MOV DI,OFFSET DGROUP:IDTSaveArea ; we don't need to save
+ CLD ; them here
+ MOVSW
+ MOVSW
+ MOVSW
+
+ push es
+ pop ds
+ assume ds:DGROUP
+
+; Write the shutdown type code into the CMOS ram. Code 9 causes the BIOS
+; to load SS:SP from IO_ROM_SEG:IO_ROM_INIT, restore the registers and then
+; do an IRET. Code 0Ah loads a CS:IP and jumps to it.
+
+ mov al,SHUT_DOWN
+ out CMOS_ADDR,al
+ mov al,9
+ cmp [FasterModeSwitch],0
+ je enrm33
+ mov al,0ah
+enrm33:
+ out CMOS_DATA,al
+
+; Shut out all interrupts while we are resetting the processor.
+
+ in al,INTA01
+ mov bIntMask,al
+ mov al,0FFh
+ out INTA01,al
+
+; Now, force a reset by causing a triple fault. We do this via a far call
+; to a call gate. The ring 0 procedure (Reset286) then loads the IDT
+; register with a bogus IDT and does an INT 3. This causes the infamous
+; "triple fault" which resets the processor.
+
+ db 9Ah ;call far SEL_RESET:0
+ dw 0,SEL_RESET or STD_RING
+
+; After the BIOS has finished its reset processing, control will come back
+; to here in real mode. We will be using the same stack as before, all
+; regular registers have been preserved, and DS and ES contain the real mode
+; address of DXDATA.
+
+enrm45:
+ FCLI
+
+IFDEF ROM
+ %OUT What stack is in use here?
+ GetRMDataSeg
+ mov ss,ax
+ELSE
+ mov ss,segDXData ;restore our stack
+ENDIF
+ mov sp,ss:ShutDownSP
+
+ pop es ;restore registers from stack
+ pop ds
+ popa
+
+; Reset processing is almost finished. We are using the same stack as
+; before, all regular registers have been preserved, and DS and ES
+; contain the real mode seg of DXDATA.
+
+ assume ds:DGROUP,es:DGROUP,SS:DGROUP
+
+enrm50:
+ FCLI
+
+if DEBUG
+ lgdt bpGDT ;We need to do this so that DEB386 can still
+ ; dump things after we switch to real mode.
+ ;When we went through the reset for '286 we
+ ; have trashed the GDTR in the cpu.
+endif
+
+; Restore trashed interrupt vector area
+
+ xor ax,ax ;address real mode IDT
+ mov es,ax
+
+ push si ;restore the three IDT words
+ push di
+ mov si,offset DGROUP:IDTSaveArea
+ mov di,03FAh
+ cld
+ movsw
+ movsw
+ movsw
+
+ pop di
+ pop si
+
+ push ds ;resotre es -> DXDATA
+ pop es
+
+; Restore the state of the interrupt mask register
+
+ mov al,bIntMask
+ out INTA01,al
+
+ cmp [FasterModeSwitch],0
+ jne enrm51
+
+; Back in real mode, 'local disable' the A20 line via HIMEM. We only do
+; this on a 286 'cause the BIOS has already turned off A20. The 'local
+; disable' lets the XMS driver know A20 is off, and possibly causes it to
+; actually enable A20 if someone loaded prior to DOSX (like a TSR) wanted
+; A20 on.
+
+ push bx
+ xmssvc 6
+ dec A20EnableCount
+ pop bx
+
+ jmp enrm70
+
+enrm51:
+; Enable NMIs
+
+ mov al,0Dh
+ out CMOS_ADDR,al
+
+; This is common exit code that is performed for both '386 and '286
+; processors.
+else ; WOW_x86
+
+ push SegDxCode
+ push offset DXCODE:enrmwow
+ push SegDxData
+ push sp
+ push SegDxData
+.386p
+ FBOP BOP_SWITCHTOREALMODE,,FastBop
+.286p
+enrmwow: add sp,6 ; remove rest of parameters
+ push ds
+ pop es ; es not set by mode switch
+endif ; WOW_x86
+
+enrm70:
+ push es ;clear DMA services required
+ mov ax,DMAServiceSegment ; bit for real mode
+ mov es,ax
+ and byte ptr es:[DMAServiceByte],not DMAServiceBit
+ pop es
+
+IFNDEF ROM
+ mov ax,segDXData
+ mov selDgroup,ax
+ENDIF
+
+ ret
+
+EnterRealMode endp
+
+
+; -------------------------------------------------------
+; Reset286 -- This procedure is called via a gate as
+; part of the switch to real mode. Most of
+; the DOS Extender runs in the child application's
+; ring. The lidt instruction has to execute in ring 0.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public Reset286
+
+Reset286 proc far
+
+ifndef KBD_RESET ;----------------------------------------
+
+ lidt bpBogusIDT ;Load up IDTR with 0 length IDT
+
+ int 3 ;Interrupt thru interrupt table with 0 length
+ ;Causes "triple fault" which resets the 286
+
+else ;----------------------------------------
+
+ call Sync8042
+ mov al,0feh ; Send 0feh - system reset command
+ out 64h,al
+@@:
+ hlt
+ jmp short @b
+
+endif ;----------------------------------------
+
+Reset286 endp
+
+ifdef KBD_RESET ;----------------------------------------
+
+Sync8042 proc near
+
+ xor cx,cx
+S8InSync:
+ in al,64h
+ and al,2
+ loopnz S8InSync
+ ret
+
+Sync8042 endp
+
+endif ;----------------------------------------
+
+
+; -------------------------------------------------------
+; Reset386 -- This procedure is called via a gate as
+; part of the switch to real mode. Most of
+; the DOS Extender runs in ring 1. This
+; routine has a few ring 0 instructions.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public Reset386
+
+Reset386 proc far
+
+ .386p
+
+ push dx
+ push eax ;save high word of eax
+
+ mov dx,segDXData ;get the real mode data segment
+
+ mov eax,cr0 ;turn off protected mode bit
+ and al,0FEh
+ lidt fword ptr bpRmIVT ;Load real-mode IDT
+
+ mov cr0,eax
+
+IFDEF ROM ;--------------------------------------------------------
+
+ extrn segRomDXCODE:ABS
+
+ db 0EAh ;jmp far to purge prefetch queue
+ dw r386_10
+ public lCodeSegLoc
+lCodeSegLoc label word
+ dw segRomDXCODE
+
+ELSE ;ROM ---------------------------------------------------------
+
+; Note, the following far jmp has special dispensation to use a SEG DXCODE
+; operand which would normally require a fixup. DOSX used to relocate its
+; code for protected mode and plug the segment value in at lCodeSegLoc. It
+; doesn't perform the relocation any longer, but debug only code still
+; checks for fix ups that might require fix ups. It knows lCodeSegLoc is ok.
+
+ db 0EAh ;jmp far to purge prefetch queue
+ dw r386_10
+ public lCodeSegLoc
+lCodeSegLoc label word
+ dw seg DXCODE
+
+ENDIF ;ROM ---------------------------------------------------------
+
+r386_10:
+ mov ss,dx ;real mode data segment address
+ mov ds,dx
+ mov es,dx
+
+ pop eax
+ pop dx
+
+ .286p
+
+; We entered this routine via a far call, even though it was from code in
+; the same segment. However, the CS value on the stack is a protected
+; mode selector, not a real mode segment. Do a near return, the caller
+; will pop off the PM CS value.
+
+ retn ;NOTE: near return even though far call!
+
+Reset386 endp
+
+if VCPI ;----------------------------------------------------------------
+
+; -------------------------------------------------------
+; ResetVCPI -- This procedure is called via a gate as
+; part of the switch to real (V86) mode when
+; running under a VCPI server. Most of
+; the DOS Extender runs in the user code ring. This
+; routine runs in ring 0.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public ResetVCPI
+
+ResetVCPI proc far
+
+ .386p
+
+ push eax ;save eax & edx for after switch
+ push edx
+
+IFDEF ROM
+ GetPMDataSeg
+ movzx edx, ax
+ELSE
+ movzx edx,segDXData ;real mode DGROUP segment
+ENDIF
+ mov eax, esp ;save esp for stack frame of dosext
+
+ push 0 ;gs begin frame for VCPI
+ push 0 ;gs
+ push 0 ;fs
+ push 0 ;fs
+ push edx ;ds
+ push edx ;es
+ push edx ;ss
+ push eax ;esp
+ push eax ;eflags reserved
+ push 0 ;cs
+ push cs:[lCodeSegLoc] ;cs
+ push 0 ;eip (high)
+ push offset DXCODE:retVCPI ;eip (low)
+
+ mov ax, SEL_VCPIALLMEM ;setup ds to be
+ mov ds, ax ; selector for all memory
+
+ mov ax, vcpiSWITCHTOV86
+ call [fnVCPI] ;call the VCPI server
+
+; Return point for pMode to V86 switch. The VCPI server sets up
+; segment registers & stack pointer.
+
+retVCPI:
+ pop edx ;restore eax & edx
+ pop eax
+
+ .286p
+
+; We entered this routine via a far call, even though it was from code in
+; the same segment. However, the CS value on the stack is a protected
+; mode selector, not a real mode segment. Do a near return, the caller
+; will pop off the PM CS value.
+
+ retn ;NOTE: near return even though far call!
+
+ResetVCPI endp
+
+
+; -------------------------------------------------------
+; CallVCPI -- This procedure is invoked via a call gate in
+; order to call the VCPI protected mode entry
+; point in ring 0. Most of the DOS Extender
+; runs in the user code ring, this routine runs in ring 0.
+;
+; Note: This routine requires DS to point to the DOSX
+; DGROUP, not a parameter to VCPI!
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public CallVCPI
+
+CallVCPI proc far
+
+ .386p
+
+ call [fnVCPI] ;call the VCPI server
+
+ .286p
+
+ ret
+
+CallVCPI endp
+
+endif ;VCPI ---------------------------------------------------------
+
+ public RmUnsimulateProc
+
+RmUnsimulateProc proc far
+ BOP BOP_UNSIMULATE
+RmUnsimulateProc endp
+
+; -------------------------------------------------------
+DXCODE ends
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+; -------------------------------------------------------
+; RAW MODE SWITCH ROUTINES
+; -------------------------------------------------------
+
+; ------------------------------------------------------
+; PmRawModeSwitch -- This routine performs a raw mode switch from
+; protected mode to real mode. NOTE: applications will JUMP at this
+; routine
+;
+; Input: ax - new DS
+; cx - new ES
+; dx - new SS
+; bx - new sp
+; si - new CS
+; di - new ip
+; Output: DS, ES, SS, sp, CS, ip contain new values
+; Errors: none
+; Uses:
+;
+;
+;
+ assume ds:nothing, ss:nothing, es:nothing
+ public PmRawModeSwitch
+PmRawModeSwitch proc far
+
+ push ss
+ pop ds
+ push bx
+.386p
+ mov bx,ss
+ movzx ebx,bx
+ lar ebx,ebx
+ test ebx,(AB_BIG SHL 8)
+ mov ebx,esp
+ jnz prms10
+
+ movzx ebx,bx
+prms10:
+.286p
+
+; Switch to dosx stack (since switch to real mode will do that to us anyway
+; NOTE: no-one can call EnterIntHandler or ExitIntHandler until we switch to
+; the user's new stack. If they do, they will use the area we stored
+; the parameters for this call for a stack frame
+
+ rpushf
+ FCLI
+ push SEL_DXDATA OR STD_RING
+ pop ss
+ assume ss:DGROUP
+.386p
+ movzx esp,word ptr pbReflStack
+.286p
+
+; Save user registers
+
+ push dx ; ss
+.386p
+ push word ptr [ebx]
+ push word ptr [ebx - 2]; flags pushed before cli
+.286p
+ push si ; cs
+ push di ; ip
+ push ax ; ds
+ push cx ; es
+
+; switch modes
+
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ SwitchToRealMode
+
+; set the registers, switch stacks, and return to the user
+
+ pop es
+ pop ds
+ pop ax ; ip
+ pop bx ; cs
+ pop cx ; flags
+ pop si ; sp
+ pop ss
+ assume ss:nothing
+ mov sp,si
+ push cx
+ popf
+ push bx
+ push ax
+ ret
+
+PmRawModeSwitch endp
+
+; NOTE: this is now the DXCODE segment, NOT the DXPMCODE segment (courtesy
+; of SwitchToRealMode
+
+; ------------------------------------------------------
+; RmRawModeSwitch -- This routine performs a raw mode switch from
+; protected mode to real mode. NOTE: applications will JUMP at this
+; routine
+;
+; Input: ax - new DS
+; cx - new ES
+; dx - new SS
+; bx - new sp
+; si - new CS
+; di - new ip
+; Output: DS, ES, SS, sp, CS, ip contain new values
+; Errors: none
+; Uses:
+;
+;
+;
+ assume ds:nothing, ss:nothing, es:nothing
+ public RmRawModeSwitch
+RmRawModeSwitch proc far
+
+ push ss
+ pop ds
+ push bx
+ mov bx,sp
+
+; Switch to dosx stack (since switch to real mode will do that to us anyway
+; NOTE: no-one can call EnterIntHandler or ExitIntHandler until we switch to
+; the user's new stack. If they do, they will use the area we stored
+; the parameters for this call for a stack frame
+
+ pushf
+ FCLI
+ push segDxData
+ pop ss
+ assume ss:DGROUP
+ mov sp,pbReflStack
+
+; Save user registers
+
+ push dx ; ss
+ push word ptr [bx] ; sp
+ push word ptr [bx - 2] ; flags from before cli
+ push si ; cs
+ push di ; ip
+ push ax ; ds
+ push cx ; es
+
+; switch modes
+
+ mov ax,segDxData
+ mov ds,ax
+ SwitchToProtectedMode
+
+; set the registers, switch stacks, and return to the user
+
+ pop es
+ pop ds
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jnz rrms10
+
+ xor eax,eax ; clear high 16 bits
+ xor edi,edi ; clear high 16 bits
+.286p
+rrms10: pop di ; ip
+ pop ax ; cs
+ pop cx ; flags from before cli
+ pop bx ; sp
+ assume ss:nothing
+ pop ss
+.386p
+ mov esp,ebx
+.286p
+ push cx
+ rpopf
+
+.386p
+ push eax
+ push edi
+ db 066h
+ retf
+.286p
+
+RmRawModeSwitch endp
+
+DXPMCODE ENDS
+
+DXCODE SEGMENT
+
+; -------------------------------------------------------
+; STATE SAVE/RESTORE ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; RmSaveRestoreState -- This routine exists as a placeholder. It
+; is not currently necessary to perform any state saving/restoring
+; for raw mode switch. The DPMI spec states that the user can call
+; this routine with no adverse effect.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: none
+;
+ assume ds:nothing, ss:nothing, es:nothing
+ public RmSaveRestoreState
+RmSaveRestoreState proc far
+ ret
+RmSaveRestoreState endp
+
+DXCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; RmSaveRestoreState -- This routine exists as a placeholder. It
+; is not currently necessary to perform any state saving/restoring
+; for raw mode switch. The DPMI spec states that the user can call
+; this routine with no adverse effect.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: none
+;
+ assume ds:DGROUP, ss:nothing, es:nothing
+ public PmSaveRestoreState
+PmSaveRestoreState proc far
+ push ax
+ push ds
+ mov ax, SEL_DXDATA or STD_RING
+ mov ds, ax
+ test DpmiFlags,DPMI_32BIT
+ pop ds
+ pop ax
+ jnz short @f ; 32-bit return
+ ret
+@@:
+ db 66h
+ ret
+
+PmSaveRestoreState endp
+
+
+; -------------------------------------------------------
+; GetIntrVector -- This routine will return the interrupt
+; vector from the Interrupt Descriptor Table for the
+; specified interrupt.
+;
+; Input: AX - interrupt number of interrupt to return
+; Output: CX - selector of the interrupt service routine
+; DX - offset of the interrupt service routine
+; Errors: none
+; Uses: CX, DX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetIntrVector
+
+GetIntrVector proc near
+
+ push si
+
+ mov si,ax
+ push es
+ shl si,3 ;otherwise it's in the IDT
+ mov es,selIDT
+ test DpmiFlags,DPMI_32BIT
+ je giv20
+
+ ; Get upper 16 bits of ip
+.386p
+ mov dx,es:[si + 6]
+ shl edx,16
+.286p
+giv20: mov dx,es:[si].offDest
+ mov cx,es:[si].selDest
+ pop es
+ pop si
+ return
+
+GetIntrVector endp
+; -------------------------------------------------------
+; PutIntrVector -- This routine will place the specified
+; interrupt vector address into the Interrupt Descriptor
+; Table entry for the specified interrupt.
+;
+; Input: AX - interrupt number
+; CX - selector of interrupt routine
+; DX - offset of interrupt routine
+; Output: none
+; Errors: none
+; Uses: all registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public PutIntrVector
+
+PutIntrVector proc near
+.386p
+ push di
+ push ax
+ push ebx
+ push edx
+
+ test DpmiFlags,DPMI_32BIT
+ jz piv10
+
+ mov ebx,VDM_INT_32
+ jmp piv20
+
+piv10: mov ebx,VDM_INT_16
+ movzx edx,dx
+
+piv20: mov di,ax
+ push es
+ shl di,3 ;otherwise it goes directly in the IDT
+ mov es,selIDT
+
+ FCLI
+
+
+ ; Put upper 16 bits of ip
+ push edx
+ shr edx,16
+ mov es:[di + 6],dx
+ pop edx
+
+
+ mov es:[di].offDest,dx
+ mov es:[di].selDest,cx
+ FSTI
+ pop es
+
+; If setting the Critical Error Handler, store the routine's address for
+; easy access later.
+
+ cmp al,24h
+ jnz piv23
+
+ mov word ptr PMInt24Handler+4,cx
+ mov dword ptr PMInt24Handler,edx
+
+;
+; set the handler in the actual "ivt", so it will get called on interrupts
+;
+piv23:
+ cmp ax,8
+ jb piv30
+
+ cmp ax,0fh
+ jbe piv24
+
+ cmp ax,70h
+ jb piv30
+
+ cmp ax,77h
+ ja piv30
+
+piv24:
+;
+; Hardware interrupts get interrupt gates
+;
+ or ebx,VDM_INT_INT_GATE
+ jmp piv40
+
+;
+; Everyone else gets trap gates
+;
+piv30: or ebx,VDM_INT_TRAP_GATE
+piv40: push bx
+ push ax
+ push cx
+ push edx
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+
+piv90:
+ pop edx
+ pop ebx
+ pop ax
+ pop di
+ ret
+
+PutIntrVector endp
+.286p
+
+; -------------------------------------------------------
+; GetFaultVector -- This routine will return the fault
+; handler address from the fault handler vector.
+;
+; Input: AX - fault number of fault handler to return
+; Output: CX - selector of the fault handler routine
+; DX - offset of the fault handler routine
+; Errors: none
+; Uses: CX, DX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetFaultVector
+
+GetFaultVector proc near
+
+ push si
+
+ mov si,ax ;if the Int # is below CRESERVED
+ cmp ax,CRESERVED ; then we do it
+ jnb @f
+
+ shl si,3
+.386p
+ mov edx,dword ptr PMFaultVector[si]
+.286p
+ mov cx,word ptr PMFaultVector[si+4]
+@@:
+ pop si
+ return
+
+GetFaultVector endp
+
+
+; -------------------------------------------------------
+; PutFaultVector -- This routine will place the specified
+; fault handler address into the fault handler vector.
+;
+; Input: AX - fault number
+; CX - selector of fault handler routine
+; DX - offset of fault handler routine
+; Output: none
+; Errors: none
+; Uses: all registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public PutFaultVector
+
+PutFaultVector proc near
+
+ push di
+
+ mov di,ax ;if the fault # is below CRESERVED
+ cmp ax,CRESERVED ; then we do it
+ jnb short pfv_exit
+
+ shl di,3
+.386p
+ mov dword ptr PMFaultVector[di],edx
+.286p
+ mov word ptr PMFaultVector[di+4],cx
+
+ .386p
+ push ebx
+
+ mov ebx,VDM_INT_16
+ test DpmiFlags,DPMI_32BIT
+ jz short @f
+ mov ebx,VDM_INT_32
+@@:
+ or ebx, VDM_INT_INT_GATE
+
+ push ebx
+ push ax
+ push cx
+ push edx
+ push 0
+ push 0
+ push 0
+ DPMIBOP SetFaultHandler
+ add sp,18
+
+ pop ebx
+ .286p
+
+pfv_exit:
+ pop di
+ ret
+
+PutFaultVector endp
+
+; -------------------------------------------------------
+; GTPARA -- This routine will return the real mode paragraph
+; address of the specified protected mode memory segment.
+;
+; Input: SI - selector of the segment
+; Output: returns ZR true if segment is in lower 1MB range
+; AX - real mode paragraph address
+; returns ZR false if segment is in extended memory
+; Errors: returns CY true if selector isn't valid
+; Uses: AX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public gtpara
+
+gtpara:
+ push cx
+ push es
+ push si
+
+ push bx
+ mov bx,selGDT ;selector for the GDT segment
+ mov es,bx
+ lsl bx,bx
+ and bx,SELECTOR_INDEX
+ and si,SELECTOR_INDEX
+ cmp si,bx ;check the given selector against
+ ; the GDT segment limit
+ pop bx
+ jc gtpr20
+
+; The given selector is beyond the end of the GDT, so return error.
+
+ or sp,sp
+ stc
+
+ Debug_Out "gtpara: invalid selector (#si)"
+
+ jmp short gtpr90
+
+; The selector specified is inside the range of defined descriptors in
+; the GDT. Get the address from the descriptor.
+
+gtpr20: mov cl,es:[si].adrBaseHigh
+ test cl,0F0h
+ jnz gtpr90
+
+ shl cl,4
+ mov ax,es:[si].adrBaseLow
+
+if DEBUG ;------------------------------------------------------------
+ test al,0Fh
+ jz @f
+ Debug_Out "gtpara: segment not on para boundry, sel #si at #cl#ax"
+@@:
+endif ;DEBUG --------------------------------------------------------
+
+ shr ax,4
+ or ah,cl
+ cmp ax,ax
+;
+gtpr90:
+ pop si
+ pop es
+ pop cx
+ ret
+
+
+; -------------------------------------------------------
+; SelOff2SegOff -- This routine will return will translate a
+; protected mode selector:offset address to the corresponding
+; real mode segment:offset address.
+;
+; Input: AX - PM selector
+; DX - PM offset
+; Output: if Z set:
+; AX - RM segment
+; DX - RM offset
+; if NZ set, address is not in conventional memory, and
+; cannot be translated
+;
+; Errors: none
+; Uses: AX, DX; all else preserved
+;
+; Note: This routine is very similar to gtpara, and could replace it!
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public SelOff2SegOff
+
+SelOff2SegOff proc near
+
+ push bx
+ push cx
+ push dx
+
+ call GetSegmentAddress ;bx:dx = lma of segment
+
+ pop cx ;cx = offset
+
+ test bl,0f0h ;above 1 Meg line?
+ jnz @f ; yes, cut out now
+
+ add dx,cx
+ adc bx,0 ;bx:dx = lma of segment:offset
+
+ call Lma2SegOff ;bx:dx = seg:off
+ mov ax,bx ;dx:ax = seg:off
+
+ cmp ax,ax ;under 1 Meg, set Z flag
+@@:
+ pop cx
+ pop bx
+
+ ret
+
+SelOff2SegOff endp
+
+
+; ------------------------------------------------------
+; Lma2SegOff -- This routine converts a linear memory address
+; in BX:DX to a normalized SEG:OFF in BX:DX.
+;
+; Input: BX:DX = lma
+; Output: BX:DX = normalized SEG:OFF
+; Uses: none
+
+
+ public Lma2SegOff
+
+Lma2SegOff proc near
+
+ push dx
+ shl bx,12
+ shr dx,4
+ or bx,dx
+ pop dx
+ and dx,0fh
+
+ ret
+
+Lma2SegOff endp
+
+
+; -------------------------------------------------------
+; GetSegmentAddress -- This routine will return with
+; the linear address of the specified segment.
+;
+; Input: AX - segment selector
+; Output: DX - low word of segment address
+; BX - high word of segment address
+; Errors: none
+; Uses: BX, DX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetSegmentAddress
+
+GetSegmentAddress:
+ push es
+ push di
+
+ mov es,selGDT
+ mov di,ax
+ and di,SELECTOR_INDEX
+ mov dx,es:[di].adrBaseLow
+ mov bl,es:[di].adrBaseHigh
+ mov bh,es:[di].adrbBaseHi386
+
+ pop di
+ pop es
+ ret
+
+; -------------------------------------------------------
+; SetSegmentAddress -- This routine will modify the
+; segment base address of the specified segment.
+;
+; Input: AX - segment selector
+; Output: DX - low word of segment address
+; BX - high word of segment address
+; Errors: None
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public SetSegmentAddress
+
+SetSegmentAddress:
+ push si
+ push es
+
+ mov es,selGDT
+ mov si,ax
+ and si,SELECTOR_INDEX
+ mov es:[si].adrBaseLow,dx
+ mov es:[si].adrBaseHigh,bl
+ mov es:[si].adrbBaseHi386,bh
+ push ax
+ push bx
+ push cx
+ mov ax,si
+ mov cx,1
+ mov bx,si
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ pop cx
+ pop bx
+ pop ax
+ pop es
+ pop si
+ ret
+
+; -------------------------------------------------------
+; NSetSegmentAccess -- This routine will modify the
+; access rights byte of a specified segment.
+;
+; Input: Selector - segment selector
+; Access - Access rights byte value
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetSegmentAccess,<PUBLIC,NEAR>,<es,si>
+ parmW Selector
+ parmW Access
+cBegin
+ mov es,selGDT
+ mov si,Selector
+ and si,SELECTOR_INDEX
+ mov ax,Access
+ mov es:[si].arbSegAccess,al ; Set access byte.
+ and ah,0F0h ; Mask off reserved bits.
+ and es:[si].cbLimitHi386,0fh ; Clear old extended bits.
+ or es:[si].cbLimitHi386,ah ; Set new extended bits.
+IFDEF WOW_x86
+ push ax
+ push bx
+ push cx
+ mov ax,si
+ mov cx,1
+ mov bx,si
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ pop cx
+ pop bx
+ pop ax
+ENDIF
+
+cEnd
+
+; -------------------------------------------------------
+; NSetSegmentLimit -- This routine will modify the
+; limit of a specified segment.
+;
+; Input: Selector - segment selector
+; Limit - Limit value
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetSegmentLimit,<PUBLIC,NEAR>,<es,si>
+ parmW Selector
+cBegin
+ mov es,selGDT
+ mov si,Selector
+ and si,SELECTOR_INDEX
+ push ax
+ push bx
+ push cx
+ mov ax,si
+ mov cx,1
+ mov bx,si
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ pop cx
+ pop bx
+ pop ax
+cEnd
+
+; -------------------------------------------------------
+; NSetSegmentBase -- This routine will modify the
+; base address of a specified segment.
+;
+; Input: Selector - segment selector
+; Base - base address
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetSegmentBase,<PUBLIC,NEAR>,<es,si>
+ parmW Selector
+ parmD Base
+cBegin
+ mov es,selGDT
+ mov si,Selector
+ and si,SELECTOR_INDEX
+ mov ax,word ptr Base
+ mov es:[si].adrBaseLow,ax
+ mov ax,word ptr Base + 2
+ mov es:[si].adrbBaseMid386,al
+ mov es:[si].adrbBaseHi386,ah
+ clc
+ push ax
+ push bx
+ push cx
+ mov ax,si
+ mov cx,1
+ mov bx,si
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ pop cx
+ pop bx
+ pop ax
+
+cEnd
+
+; -------------------------------------------------------
+; NMoveDescriptor -- This routine copy a descriptor from
+; the source address to the destination address. This
+; can be to or from the descriptor table. If it copied
+; to the descriptor table, the system will be notified as
+; appropriate.
+;
+; Input: Source -- address of source descriptor
+; Dest -- address of destination descriptor
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+.386p
+cProc NMoveDescriptor,<PUBLIC,NEAR>,<es,esi,ds,edi,ebx,ecx>
+ ParmW SourceSel
+ ParmD SourceOff
+ ParmW DestSel
+ ParmD DestOff
+cBegin
+.386p
+ xor ecx,ecx
+ mov cx,SourceSel
+ mov ds,cx
+ mov esi,SourceOff
+ mov cx,DestSel
+ mov es,cx
+ mov edi,DestOff
+ mov cx,8
+ rep movs byte ptr [esi], byte ptr [edi]
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ mov cx,es
+ cmp cx,ds:selGdt
+ jne nm20
+
+ mov cx,1
+ mov ebx,DestOff
+ mov ax,bx
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.386p
+nm20:
+cEnd
+.286p
+
+; -------------------------------------------------------
+; NWOWSetDescriptor --
+; The Descriptors are set on the system side.
+;
+; Input: Source -- address of source descriptors
+; Count -- count of descriptors to set
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+cProc NWOWSetDescriptor,<PUBLIC,NEAR>,<es,ds,bx,cx>
+ ParmW Count
+ ParmD Source
+cBegin
+ les bx, Source
+ mov cx, Count
+ mov ax, bx
+
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+cEnd
+
+; -------------------------------------------------------
+; ParaToLDTSelector -- This routine will convert a segment
+; address relative to the start of the exe file into the
+; corresponding selector for the segment. It searches the
+; LDT to see if a segment is already defined at that address.
+; If so, its selector is returned. If not, a new segment
+; descriptor will be defined.
+;
+; Note: The LDT and GDT are currently one and the same.
+;
+; Input: AX - paragraph aaddress of real mode segment
+; BX - access rights byte for the segment
+; Output: AX - selector for the segment
+; Errors: returns CY set if unable to define a new segment
+; Uses: AX, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public ParaToLDTSelector
+
+ParaToLDTSelector proc near
+
+ push bx
+ push cx
+ push dx
+
+; Convert the paragraph address to a linear address and see if there
+; is a segment defined at that address.
+
+ mov dx,ax
+ call FindLowSelector
+ jnc @f ;if so, we don't need to make one
+
+; This segment isn't defined, so we need to create a descriptor for it.
+
+ mov ax,dx
+ call MakeLowSegment
+
+if DEBUG ;------------------------------------------------------------
+ jnc ptos80
+ Debug_Out "ParaToLDTSelector: can't make selector!"
+ptos80:
+endif ;DEBUG --------------------------------------------------------
+
+ jc ptos90
+
+@@: or al,SELECTOR_TI or STD_RING ;look like LDT selector
+
+; All done
+
+ptos90: pop dx
+ pop cx
+ pop bx
+ ret
+
+ParaToLDTSelector endp
+
+ public FarParaToLDTSelector
+FarParaToLDTSelector proc far
+ call ParaToLDTSelector
+ ret
+FarParaToLDTSelector endp
+
+; -------------------------------------------------------
+ page
+; -------------------------------------------------------
+; DESCRIPTOR TABLE MANIPULATION ROUTINES
+; -------------------------------------------------------
+;
+; AllocateLDTSelector -- This function will obtain the
+; next free descriptor in the local descriptor table.
+;
+; Note: Currently the LDT and GDT are in the same table!
+;
+; Note: The function InitGlobalDscrTable must have been
+; called before calling this function.
+;
+; Input: none
+; Output: AX - selector if one is available
+; Errors: CY clear if successful, AX=0 and CY set if not free selectors
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AllocateLDTSelector
+
+AllocateLDTSelector proc near
+
+ call AllocateSelector ;get a GDT selector
+ jc @f
+
+ or al,SELECTOR_TI or STD_RING ;say it's in the LDT
+
+@@: ret
+
+AllocateLDTSelector endp
+
+
+; -------------------------------------------------------
+; AllocateSelector -- This function will obtain the
+; next free descriptor in the global descriptor table.
+; The descriptors in the GDT are stored on a linked list
+; of free descriptors. The cbLimit field of the descriptor
+; is used as the link to the next element of the list. In
+; addition, free descriptors have the access rights byte
+; set to 0.
+;
+; Note: The function InitGlobalDscrTable must have been
+; called before calling this function.
+;
+; Input: none
+; Output: AX - selector if one is available
+; Errors: CY clear if successful, AX=0 and CY set if not free selectors
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AllocateSelector
+
+AllocateSelector proc near
+
+; Get the next free descriptor on the list.
+
+ mov ax,selGDTFree ;get head of free list
+ cmp ax,-1 ;is the list empty?
+ jnz alsl20 ;if so, report error.
+ stc ;set error flag
+
+ Debug_Out "AllocateSelector: out of selectors!"
+
+ jmp short alsl90 ;and get out
+
+; We now need to update the new head of list to point to the
+; following one on the list.
+; Whenever we allocate a descriptor, the access rights byte is set
+; to 0Fh. This marks it as a '386 task gate, which is not legal to
+; have in the GDT. We need to stick something in this byte, because
+; having the access rights byte be 0 means that it is free, which is
+; no longer the case.
+
+alsl20:
+
+if DEBUG ;------------------------------------------------------------
+ test al,not SELECTOR_INDEX
+ jz @f
+ Debug_Out "AllocateSelector: selGDTFree invalid! #AX"
+ and al,SELECTOR_INDEX
+@@:
+endif ;DEBUG --------------------------------------------------------
+
+ push bx
+ push es
+ mov bx,ax ;BX points to the descriptor
+ mov es,selGDT ;ES points to the GDT segment
+ push es:[bx].cbLimit ;push the link field of the descriptor
+ pop selGDTFree ;make it be the new head of the list
+
+ mov es:[bx].adrbBaseHi386,0
+ mov es:[bx].arbSegAccess,0Fh
+ pop es
+ pop bx
+
+ or al,SELECTOR_TI
+ ;
+ ; Remember the highest selector alloced, so we can reinit the ldt
+ ;
+ cmp ax,HighestSel
+ jbe alsl50
+
+ mov HighestSel,ax
+alsl50: clc
+
+;
+; All done
+alsl90: ret
+
+AllocateSelector endp
+
+
+; -------------------------------------------------------
+; AllocateSelectorBlock -- This function will allocate
+; a set of contiguous descriptors from the global
+; descriptor table. It will search the GDT from the
+; beginning looking for a sufficiently large contiguous
+; set of descriptors. When if finds a set, it will
+; then remove each one from the free descriptor list.
+;
+; Input: AX - Number of selectors needed
+; CX - non-zero if allocation should be started from higher range
+; first.
+; Output: AX - starting selector of the block
+; Errors: return CY set if error occurs
+; Uses: AX modified, all other registers preserved
+; modifies global descriptor table free list
+;
+; *******************************************************
+; The strategy of allocating selectors has been modified to
+; give preference to selector values above 1000h. This is an
+; attempt to emulate typical values that are returned by win31.
+; -neilsa
+;
+; Some DPMI DOS applications demand that all selectors(no matter it comes
+; from AllocateLDTSelector or this function) be contiguous, so
+; the strategy for WOW doesn't work for DPMI DOS applications.
+; For this reason, a new parameter is added so the caller can control
+; where to start searching for free selectors.
+; -williamh
+;
+SEL_START_HI equ 1000h
+
+
+ .erre SEL_START_HI GE SEL_USER ;make sure SEL_START_HI really
+ ;is larger than SEL_USER
+
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AllocateSelectorBlock
+
+AllocateSelectorBlock proc near
+
+ push cx
+ push dx
+ push si
+ push di
+ push es
+
+ mov es,selGDT
+ mov dx,ax ;remember descriptor count in DX
+ lsl di,selGDT ;stop search at table limit
+
+; Start at the first user selector and search until we find a
+; sufficiently large block of contiguous selectors.
+
+ mov si,SEL_USER
+ jcxz alsb10
+ mov si, SEL_START_HI ;no branch on WOW, so it is faster
+alsb10:
+ push cx ; save the starting selector indicator
+alsb20:
+ mov ax,si ;remember the starting one
+ mov cx,dx ;number of descriptors to check
+if 0
+alsb30: cmp es:[si].arbSegAccess,0 ;is this one free
+else
+alsb30: cmp word ptr es:[si].arbSegAccess,0 ;is this one free
+endif
+ jnz alsb40 ;if not, we have to continue looking
+ add si,8 ;bump to next descriptor
+ cmp si,di ;check if at end of table
+ jae alsb80 ;if so, get out with error
+ loop alsb30 ;repeat for the number of selectors requested
+ jmp short alsb50;we found the block
+
+; This one wasn't free, try the next one
+
+alsb40: add si,8 ;bump to next descriptor
+ cmp si,di ;are we at the end of the table.
+ jc alsb20 ;and repeat if not
+ jmp short alsb80;we didn't find it, so report error
+
+; AX has the starting selector of the block of descriptors. We need
+; to remove each of them from the free list.
+
+alsb50: push ax ;remember the starting point
+ mov cx,dx ;get descriptor count
+ mov dx,ax ;remember current selector number
+alsb52: mov ax,dx
+ call RemoveFreeDescriptor
+ add dx,8
+ loop alsb52
+ ;
+ ; Remember the highest number
+ ;
+ or dl,SELECTOR_TI
+ cmp dx,HighestSel
+ jbe alsb60
+
+ mov HighestSel,dx
+alsb60: pop ax ;restore starting selector
+
+ or al,SELECTOR_TI
+
+ add sp, 2 ; throw away starting selector indicator
+ clc
+ jmp short alsb90
+
+; Couldn't find them, so return error.
+
+alsb80:
+ pop cx ; get back starting selector indicator
+ jcxz alsb81 ; we started at SEL_USER, no more search
+ cmp si, SEL_START_HI+SEL_USER+8 ;is this really the end?
+ jbe alsb81 ;yes, now failure
+ mov di, SEL_START_HI+SEL_USER ;new end location
+ mov si,SEL_USER ;restart low
+ jmp alsb10 ;try again
+
+alsb81:
+ stc
+
+ Debug_Out "AllocSelectorBlock: Failed!"
+
+alsb90: pop es
+ pop di
+ pop si
+ pop dx
+ pop cx
+ ret
+
+AllocateSelectorBlock endp
+
+
+; -------------------------------------------------------
+; RemoveFreeDescriptor -- This routine will remove the
+; specified descriptor from the free descriptor list
+; of the GDT.
+;
+; Input: AX - selector of the descriptor to remove
+; ES - segment address of GDT
+; Output: none
+; Errors: none
+; Uses: AX used, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public RemoveFreeDescriptor
+
+RemoveFreeDescriptor proc near
+
+ push si
+ push di
+
+ and ax,SELECTOR_INDEX ;clear table/pl bits
+
+; Check that the segment is really free.
+
+ mov si,ax ;Segment index
+if 0
+ cmp es:[si].arbSegAccess,0 ; Should be 0 if free
+else
+ cmp word ptr es:[si].arbSegAccess,0 ; Should be 0 if free
+endif
+ jnz rmfd80 ;Error if segment is not free!
+;
+ xor di,di
+ mov si,selGDTFree ;start at the head of the list.
+
+; Look for a selector matching the one to free
+
+rmfd20: cmp si,-1 ;check for end of list.
+ jz rmfd90 ;and get out if so
+ cmp ax,si ;is this the one we are looking for
+ jz rmfd40
+ mov di,si
+ mov si,es:[si].cbLimit ;point SI at next one in the list
+ jmp rmfd20 ;and repeat
+
+; We found the one we want, so now remove it from the list.
+
+rmfd40: mov es:[si].adrbBaseHi386,0
+ mov es:[si].arbSegAccess,0Fh
+ mov ax,es:[si].cbLimit
+ or di,di ;is it the head of the list
+ jz rmfd50
+
+; The one we have isn't the head of the list, so make the previous
+; list element point at the one beyond the one being removed.
+
+ mov es:[di].cbLimit,ax
+ jmp short rmfd90
+
+; The one we have is the head of the list. Make the head of the list
+; point to the one following the one being removed.
+
+rmfd50:
+ mov selGDTFree,ax
+ jmp short rmfd90
+
+rmfd80: stc ;Flag allocation error
+
+ Debug_Out "RemoveFreeDescriptor: Failed!"
+
+rmfd90: pop di
+ pop si
+ ret
+
+RemoveFreeDescriptor endp
+
+; -------------------------------------------------------
+; IsSelectorFree -- This routine determines if the specified
+; selector is on the free list. It is used to prevent
+; apps from corrupting the free list by doing things like
+; set selector on a descriptor in the free list.
+;
+; Input: BX - Descriptor index
+; Output: CY - set if decriptor is not free
+; clear otherwise
+;
+ assume ds:dgroup,es:nothing,ss:nothing
+ public IsSelectorFree
+IsSelectorFree proc near
+
+ push es
+ push si
+
+ mov si,selGdt
+ mov es,si
+
+ test byte ptr es:[bx].adrbBaseHi386,080h
+ jnz isf30
+
+ stc
+ jmp isf40
+
+isf30: clc
+isf40: pop si
+ pop es
+ ret
+
+IsSelectorFree endp
+
+
+; -------------------------------------------------------
+; FreeSelector -- This routine will mark the segment
+; descriptor for the specified selector as free. This
+; is used to release a temporary selector when no longer
+; needed. The descriptor is marked as free by setting the
+; access rights byte to 0 and placing it on the free list.
+;
+; Note: This routine can only be called in protected mode.
+;
+; Input: AX - selector to free
+; Output: none
+; Errors: CY clear if no error, set if selector is invalid
+; Uses: AX used, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FreeSelector
+
+FreeSelector proc near
+ push bx
+ push es
+
+; Check for this being a valid selector.
+
+ cmp ax,SEL_USER ;make sure it is a user selector
+ jb frsl80
+ mov bx,SEL_LDT_ALIAS
+ mov es,selGDT
+ mov bx,es
+ lsl bx,bx
+ cmp ax,bx ;make sure it is in the range of the table
+ jnc frsl80
+
+; We have a legitimate selector, so set the access rights byte to 0, and
+; place it at the head of the free list.
+
+ mov bx,ax
+ and bx,SELECTOR_INDEX ;clear unwanted bits
+
+if 0
+ cmp es:[bx].arbSegAccess,0 ;already marked as free?
+else
+ cmp word ptr es:[bx].arbSegAccess,0 ;already marked as free?
+endif
+ jz frsl80 ; yes, don't free it again!
+
+if 0
+ mov es:[bx].arbSegAccess,0
+else
+ mov word ptr es:[bx].arbSegAccess,0
+ mov byte ptr es:[bx].adrbBaseHi386,080h
+endif
+ mov ax,selGDTFree ;pointer to current head of list
+ mov es:[bx].cbLimit,ax ;store in link field of this dscr.
+ mov selGDTFree,bx ;make this one be the head of list.
+if 0
+BUGBUG fix this
+IF 1 ; DaveHart was IFDEF WOW_x86, need it for MIPS too
+ int 3; debugbug
+ push ax
+ push bx
+ push cx
+ mov ax,bx
+ mov bx,offset NullSel
+ push ds
+ pop es
+ mov cx,1
+IFDEF WOW_x86
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ELSE
+ DPMIBOP SetDescriptorTableEntries
+ENDIF
+ pop cx
+ pop bx
+ pop ax
+ENDIF
+endif
+ clc
+ jmp short frsl90
+
+; Bogus selector given. Return error.
+
+frsl80: stc
+
+ Debug_Out "FreeSelector failed, #AX invalid or not used!?"
+
+frsl90: pop es
+ pop bx
+frsl99: ret
+
+FreeSelector endp
+
+; -------------------------------------------------------
+; FreeSelectorBlock -- This routine will free the specified
+; range of segment descriptors in the global descriptor
+; table.
+;
+; Input: AX - starting selector in the range
+; CX - number of selectors to free
+; Output: none
+; Errors: returns CY set if error occurs
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FreeSelectorBlock
+
+FreeSelectorBlock proc near
+
+ jcxz frsb99
+ push cx
+frsb20: push ax
+ call FreeSelector
+ pop ax
+ jc frsb90
+ add ax,8
+ loop frsb20
+frsb90: pop cx
+frsb99: ret
+
+FreeSelectorBlock endp
+
+
+; -------------------------------------------------------
+; FindLowSelector -- This function will search the global
+; descriptor table for a descriptor matching the given
+; address.
+;
+; Input: AX - real mode paragraph to search for
+; BX - access rights byte for the segment
+; Output: AX - selector corresponding to input paragraph address
+; Errors: returns CY set if specified descriptor not found
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FindLowSelector
+
+FindLowSelector:
+ push bx
+ push dx
+;
+ mov dx,ax
+ push bx
+ call ParaToLinear
+ pop ax
+ mov bh,al
+ call FindSelector
+;
+ pop dx
+ pop bx
+ ret
+
+
+; -------------------------------------------------------
+; FindSelector -- This function will search the global
+; descriptor table for a segment descriptor matching
+; the specified linear byte address.
+;
+; Note that this routine cannot be used to find
+; selectors pointing to addresses above 16 Megabytes.
+; This is not really a problem, since the routine
+; is used to find selectors in real mode DOS land
+; most of the time.
+;
+; Input: DX - low word of linear byte address
+; BL - high byte of linear address
+; BH - access rights byte for the segment
+; Output: AX - selector of corresponding segment
+; Errors: returns CY set if specified descriptor not found
+; Uses: AX used, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FindSelector
+
+FindSelector proc near
+
+ push si
+ push di
+ push es
+ push cx
+
+; Get segment limit of the GDT to use as a limit on the search.
+
+ lsl di,selGDT
+ mov es,selGDT
+
+; Look for a descriptor matching the address in BL:AX
+
+ mov si,SEL_USER ;search starting here
+if 0
+fnds20: cmp es:[si].arbSegAccess,0
+else
+fnds20: cmp word ptr es:[si].arbSegAccess,0
+endif
+ jz fnds28 ;skip if unused descriptor
+ cmp bl,es:[si].adrBaseHigh
+ jnz fnds28
+ cmp dx,es:[si].adrBaseLow
+ jnz fnds28
+if 0
+ cmp es:[si].cbLimit,0
+ jz fnds28 ;skip if dscr has 0 limit
+else
+ cmp es:[si].cbLimit,0ffffh
+ jnz fnds28 ;skip unless dscr has 64k limit
+endif
+ mov cl,bh
+ xor cl,es:[si].arbSegAccess
+ and cl,NOT AB_ACCESSED
+ jz fnds90
+fnds28: add si,8 ;bump to next descriptor
+ jc fnds80
+ cmp si,di ;check against end of GDT
+ jc fnds20 ;if still less, continue on.
+
+; Hit the end of the GDT and didn't find one. So return error.
+
+fnds80: stc
+ jmp short fnds99
+
+; We found it, so return the selector
+
+fnds90: mov ax,si
+fnds99: pop cx
+ pop es
+ pop di
+ pop si
+ ret
+
+FindSelector endp
+
+
+
+; -------------------------------------------------------
+; DupSegmentDscr -- This function will duplicate the specified
+; segment descriptor into the specified destination descriptor. The
+; end result is a second segment descriptor pointing to the same place
+; in memory as the first.
+;
+; Input: AX - selector of segment descriptor to duplicate
+; BX - selector of the segment descriptor to receive duplicate
+; Output: none
+; Errors: none
+; Uses: All registers preserved. Modifies the segment
+; descriptor for the specified segment. If this selector happens
+; to be in a segment register when this routine is called, that
+; segment register may end up pointing to the new location.
+
+ assume ds:DGROUP,es:NOTHING
+ public DupSegmentDscr
+
+DupSegmentDscr proc near
+
+ push cx
+ push si
+ push di
+ push ds
+ push es
+
+ mov si,ax
+ mov di,bx
+ and si,SELECTOR_INDEX
+ and di,SELECTOR_INDEX
+ mov es,selGDT
+ mov ds,selGDT
+ assume ds:NOTHING
+ mov cx,4
+ cld
+ rep movs word ptr [di],word ptr [si]
+
+ pop es
+ pop ds
+ pop di
+ pop si
+ pop cx
+ ret
+
+DupSegmentDscr endp
+
+IFDEF ROM
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+ENDIF
+
+; -------------------------------------------------------
+; NSetSegmentDscr -- This function will initialize the
+; specified descriptor table entry with the specified data.
+;
+; This function can be called in real mode or protected mode.
+;
+; Input:
+; Param1 - WORD segment selector
+; Param2 - DWORD 32-bit segment base address
+; Param3 - DWORD 32-bit segment limit
+; param4 - WORD segment access/type
+; Output: returns selector for the segment
+; Errors: none
+; Uses: Flags
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetSegmentDscr,<PUBLIC,FAR>,<es,di,ax,bx>
+ parmW Selector
+ parmD Base
+ parmD Limit
+ parmW Access
+cBegin
+ mov es,selGDT
+ mov di,Selector
+ and di,SELECTOR_INDEX
+
+ mov ax,off_Base ; Set segment base
+ mov es:[di].adrBaseLow,ax
+ mov ax,seg_Base
+ mov es:[di].adrBaseHigh,al
+ mov es:[di].adrbBaseHi386,ah
+
+ mov ax,word ptr Access
+ and ax,070ffh ; clear 'G' bit and
+ ; extended limit bits
+ mov word ptr es:[di].arbSegAccess,ax
+ ; set access
+ mov ax,seg_Limit
+ mov bx,off_Limit ; AX:BX = segment limit
+ test ax,0fff0h ; big?
+ jz ssd_0 ; No
+ shr bx,12d ; Yes, make it page granular.
+ shl ax,4d
+ or bx,ax
+ mov ax,seg_Limit
+ shr ax,12d
+ or al,080h ; set 'G' bit
+ssd_0:
+ or es:[di].cbLimitHi386,al ; set high limit
+ mov es:[di].cbLimit,bx ; set low limit
+ push ax
+ push bx
+ push cx
+ mov ax,di
+ mov cx,1
+ mov bx,di
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ pop cx
+ pop bx
+ pop ax
+cEnd
+
+ifndef WOW_x86
+; -------------------------------------------------------
+; NSetGDTSegmentDscr -- This function will initialize the
+; specified descriptor table entry with the specified data.
+;
+; This function can be called in real mode or protected mode.
+;
+; Input:
+; Param1 - WORD segment selector
+; Param2 - DWORD 32-bit segment base address
+; Param3 - DWORD 32-bit segment limit
+; param4 - WORD segment access/type
+; Output: returns selector for the segment
+; Errors: none
+; Uses: Flags
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetGDTSegmentDscr,<PUBLIC,FAR>,<es,di,ax,bx>
+ parmW Selector
+ parmD Base
+ parmD Limit
+ parmW Access
+cBegin
+ mov ax,SEL_GDT
+ mov es,ax
+ mov di,Selector
+ and di,SELECTOR_INDEX
+
+ mov ax,off_Base ; Set segment base
+ mov es:[di].adrBaseLow,ax
+ mov ax,seg_Base
+ mov es:[di].adrBaseHigh,al
+ mov es:[di].adrbBaseHi386,ah
+
+ mov ax,word ptr Access
+ and ax,070ffh ; clear 'G' bit and
+ ; extended limit bits
+ mov word ptr es:[di].arbSegAccess,ax
+ ; set access
+ mov ax,seg_Limit
+ mov bx,off_Limit ; AX:BX = segment limit
+ test ax,0fff0h ; big?
+ jz @f ; No
+ shr bx,12d ; Yes, make it page granular.
+ shl ax,4d
+ or bx,ax
+ mov ax,seg_Limit
+ shr ax,12d
+ or al,080h ; set 'G' bit
+@@:
+ or es:[di].cbLimitHi386,al ; set high limit
+ mov es:[di].cbLimit,bx ; set low limit
+cEnd
+endif ; WOW_x86
+
+
+IFDEF ROM
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+ENDIF
+
+; -------------------------------------------------------
+; MakeLowSegment -- This function will create a segment
+; descriptor for the specified low memory paragraph address.
+; The segment length will be set to 64k. The difference
+; between this and MakeScratchSelector is that this function
+; allocates a new segment descriptor in the user area of
+; the global descriptor table, thus creating a more or less
+; permanent selector. MakeScratchSelector always uses the
+; same descriptor location in the descriptor table, thus
+; creating a very temporary selector.
+;
+; Input: AX - paragraph address in low memory
+; BX - access rights word for the segment
+; Output: AX - selector to use to access the memory
+; Errors: returns CY clear if no error, CY set if unable to
+; allocate a segment descriptor
+; Uses: AX used, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public MakeLowSegment
+
+MakeLowSegment proc near
+
+; We need to allocate a segment descriptor, convert the paragraph address
+; to a linear byte address, and then initialize the allocated segment
+; descriptor.
+
+ push dx
+ push cx
+
+ mov cx,bx
+ mov dx,ax ;paragraph address to DX
+ call AllocateSelector ;get a segment descriptor to use
+ jc mksl90 ;get out if error
+ call ParaToLinear
+ cCall NSetSegmentDscr,<ax,bx,dx,0,0FFFFh,cx>
+
+ clc
+mksl90:
+ pop cx
+ pop dx
+ ret
+
+MakeLowSegment endp
+
+; -------------------------------------------------------
+; 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.
+;
+; Input: DX - paragraph address
+; Output: DX - lower word of linear address
+; BX - high word of linear address
+; Errors: none
+; Uses: DX, BL used, all else preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public ParaToLinear
+
+ParaToLinear proc near
+
+ mov bl,dh
+ shr bl,4
+ shl dx,4
+ xor bh,bh
+ ret
+
+ParaToLinear endp
+
+
+; -------------------------------------------------------
+; MoveMemBlock -- This routine will copy a block
+; from one place to another. It copies from the bottom
+; up, so if the address ranges overlap it can only be
+; used to copy down.
+;
+; Input: BX - selector of GDT
+; CX - low word of block length
+; DX - high word of block length
+; DS - selector pointing to source address
+; ES - selector pointing to destination address
+; Output: none
+; Errors: none
+; Uses: modifies segment descriptors selected by ES and DS
+; AX used, other registers preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public MoveMemBlock
+
+MoveMemBlock:
+ cld
+ push cx
+ push dx
+ push si
+ push di
+;
+mvsd30: xor si,si
+ mov di,si
+ or dx,dx ;is there more than 64k left to move
+ jz mvsd32 ;if not, move the amount left
+ dec dx
+ push cx
+ mov cx,8000h ;move 64k bytes this time
+ rep movs word ptr [di],word ptr [si]
+ pop cx
+ jmp short mvsd36
+mvsd32: jcxz mvsd90
+ shr cx,1
+ rep movs word ptr [di],word ptr [si]
+ jnc mvsd90
+ movs byte ptr [di],byte ptr [si]
+ jmp short mvsd90
+;
+; There is still something to move, so bump the segment descriptors to
+; point to the next chunk of memory and repeat.
+mvsd36:
+ mov di,es
+ push di
+ and di,SELECTOR_INDEX
+ mov es,bx
+ inc es:[di].adrBaseHigh ;bump the destination segment to
+ pop di ; the next 64k boundary
+
+ mov si,ds
+ push si
+ and si,SELECTOR_INDEX
+ inc es:[si].adrBaseHigh ;bump the source segment to the
+ pop si ; next 64k boundary
+
+ mov ds,si
+ mov es,di
+ jmp mvsd30
+;
+; All done
+mvsd90: pop di
+ pop si
+ pop dx
+ pop cx
+ ret
+
+; -------------------------------------------------------
+ subttl Utility Routines
+ page
+; -------------------------------------------------------
+; UTILITY ROUTINES
+; -------------------------------------------------------
+
+BeginLowSegment
+
+; -------------------------------------------------------
+if DEBUG
+; MemCopy -- This routine is for use in a debugger to
+; copy a block of extended memory down to real mode
+; memory so that it can be looked at. The data is
+; copied to rgbXfrBuf1. (This buffer is 4k bytes in
+; size, so don't copy more than this amount)
+;
+; Input: CX - Number of bytes to copy
+; DS:SI - protected mode address of start of copy
+; Output; none
+; Errors: none
+; Uses: all registers trashed.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public MemCopy
+
+MemCopy:
+ in al,INTA01
+ mov dl,al
+ mov al,0FFh
+ out INTA01,al
+ push ds
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,segDXData
+ENDIF
+ call EnterProtectedMode
+ pop ds
+ push SEL_DXDATA
+ pop es
+ mov di,offset DGROUP:rgbXfrBuf1
+ cld
+ rep movsb
+IFDEF ROM
+ push SEL_DXDATA OR STD_RING
+ pop ds
+ELSE
+ mov ds,selDgroup
+ENDIF
+ call EnterRealMode
+ mov al,dl
+ out INTA01,al
+ int 3
+
+endif
+
+; -------------------------------------------------------
+
+EndLowSegment
+
+;
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+;
+;****************************************************************
+
+ end
diff --git a/private/mvdm/dpmi/cmacros.inc b/private/mvdm/dpmi/cmacros.inc
new file mode 100644
index 000000000..2e9f29071
--- /dev/null
+++ b/private/mvdm/dpmi/cmacros.inc
@@ -0,0 +1,1230 @@
+comment $
+cmacros - assembly macros for interfacing to hhls
+(C)Copyright Microsoft Corp. 1984-1988
+$
+.xcref
+.xcref ??_out
+??_out macro t
+ifndef ?QUIET
+%out t
+endif
+endm
+outif macro name,defval,onmsg,offmsg
+ifndef name
+ifb <defval>
+name=0
+else
+name=defval
+endif
+endif
+if name
+name=1
+ifnb <onmsg>
+??_out <! onmsg>
+endif
+else
+ifnb <offmsg>
+??_out <! offmsg>
+endif
+endif
+endm
+.xcref ??error
+??error macro msg
+e r r o r ----- msg
+.err
+endm
+.xcref ASMpass
+.xcref memS,memM,memL,memC,memH,memMOD,sizec,sized
+if1
+ASMpass=1
+ifdef ?SMALL
+memS=1
+endif
+ifdef ?MEDIUM
+memM=1
+endif
+ifdef ?COMPACT
+memC=1
+endif
+ifdef ?LARGE
+memL=1
+endif
+ifdef ?HUGE
+memH=1
+endif
+??_out <cMacros Version 5.20 - Copyright (c) Microsoft Corp. 1984-1988>
+outif memS,0,<Small model>
+outif memM,0,<Medium model>
+outif memL,0,<Large model>
+outif memC,0,<Compact model>
+outif memH,0,<Huge model>
+memMOD= memS + memM + memL + memC + memH
+if memMOD ne 1
+if memMOD eq 0
+memS = 1
+else
+??error <more than 1 memory model selected>
+endif
+endif
+sizec= memM + memL + memH
+sized= memL + memC + (memH*2)
+outif ?DF,0,<No segments or groups will be defined>
+outif ?TF,0,<Epilog sequences assume valid SP>
+outif ?WIN,1,<Windows support>
+if ?WIN eq 1
+outif ?PLM,1,<>
+else
+outif ?PLM,1,<PL/M calling convention>
+endif
+ifndef ?NODATA
+?nodata1=0
+else
+?nodata1=1
+??_out <! NODATA module>
+endif
+ifndef ?CHKSTK
+?chkstk1=0
+else
+?chkstk1=1
+ifdef ?CHKSTKPROC
+??_out <! Private stack checking enabled>
+else
+??_out <! Stack checking enabled>
+endif
+endif
+ifndef DOS5
+?DOS5=0
+else
+?DOS5=1
+??_out <! DOS5 module>
+endif
+ifdef ?PROFILE
+??_out <! Native profiling enabled>
+endif
+else
+ASMpass=2
+endif
+.xcref ?n,?ax,?eax,?bx,?ebx
+.xcref ?cx,?ecx,?dx,?edx
+.xcref ?si,?esi,?di,?edi,?es,?ds,?fs
+.xcref ?gs
+.xcref ?rsl,?cpd,?argl,?argc,?ba
+.xcref ?acb,???,?po
+.xcref ?pas,?pc
+.xcref uconcat,mpush,mpop
+.xcref ?ri,?pp,?pp1,?al1
+.xcref ?ad,?ap,?atal,?dd,?dd1,?dd2
+.xcref ?pg,?pg1,?aloc,?cs1,?cs2
+.xcref ?DF,?TF,?ff,?PLM,?WIN,?ia,?pu,?adj
+.xcref ?uf,?rp,?nx,?nd,?nodata1,?chkstk1,?DOS5
+.xcref ?wfp,arg,cCall,cProc,assumes,?cs3,?cs2,?cs1
+.xcref defgrp,addseg,createSeg
+.xcref save,outif,errnz,errn$,errnz1
+.xcref ?PLMPrevParm,?gcc
+.xcref ?cCall1,?pcc
+?rsl = 0
+?cpd = 0
+?argl = 0
+?argc = 0
+?ba = 0
+?acb = 0
+??? = 0
+?po = 0
+?pas = 0
+?pc = 0
+?ia = 0
+?pu = 0
+?adj = 0
+?rp = 0
+?uf = 0
+?nd = 0
+?nx = 0
+?wfp = 0
+?ff = 0
+?dd2 = 0
+?cCall1 = 0
+?pcc = 0
+?PLMPrevParm = 0
+.xcref ?casen
+if1
+?casen = 0
+endif
+?n = 0000000000000000b
+?ax = 0000000000000001b
+?eax = 0000000000000010b
+?bx = 0000000000000100b
+?ebx = 0000000000001000b
+?cx = 0000000000010000b
+?ecx = 0000000000100000b
+?dx = 0000000001000000b
+?edx = 0000000010000000b
+?si = 0000000100000000b
+?esi = 0000001000000000b
+?di = 0000010000000000b
+?edi = 0000100000000000b
+?ds = 0001000000000000b
+?es = 0010000000000000b
+?fs = 0100000000000000b
+?gs = 1000000000000000b
+.cref
+uconcat macro a,b,c,d,e,f,g
+a&b c&d e&f&g
+endm
+mpush macro r
+irp x,<ax,eax,bx,ebx,cx,ecx,dx,edx,si,esi,di,edi,ds,es,fs,gs>
+if (r and ?&&x)
+ push x
+endif
+endm
+endm
+mpop macro r
+irp x,<gs,fs,es,ds,edi,di,esi,si,edx,dx,ecx,cx,ebx,bx,eax,ax>
+if (r and ?&&x)
+ pop x
+endif
+endm
+endm
+save macro r
+?rsl=0
+?ri ?rsl,<r>
+endm
+?ri macro n,r
+irp x,<r>
+.ERRNDEF ?&&x
+n=n or ?&&x
+endm
+endm
+.xcref
+.xcref parmB,parmW,parmD,parmQ,parmT,parmCP,parmDP
+.cref
+parmB macro n
+?pp <n>,<byte>,2,1
+endm
+parmW macro n
+?pp <n>,<word>,2,2
+endm
+parmD macro n
+ife ?PLM
+irp x,<n>
+?pp <&&x>,<dword>,0,4
+?pp <off_&&x>,<word>,2,2
+?pp <seg_&&x>,<word>,2,2
+endm
+else
+irp x,<n>
+?pp <seg_&&x>,<word>,2,2
+?pp <off_&&x>,<word>,2,2
+?pp <&&x>,<dword>,0,4
+endm
+endif
+endm
+parmQ macro n
+?pp <n>,<qword>,8,8
+endm
+parmT macro n
+?pp <n>,<tbyte>,10,10
+endm
+if sizec
+parmCP macro n
+parmD <n>
+endm
+else
+parmCP macro n
+parmW <n>
+endm
+endif
+if sized
+parmDP macro n
+parmD <n>
+endm
+else
+parmDP macro n
+parmW <n>
+endm
+endif
+?pp macro n,t,l,s
+if ?cpd
+.xcref
+irp x,<n>
+.xcref ?t&&x
+?t&&x=s
+ife ?PLM
+?pp1 x,<t>,,,%(?po+?adj)
+?po=?po+l
+else
+?PLMPrevParm=?PLMPrevParm+1
+?po=?po+l
+?pp1 x,<t>,%?po,%?adj,,%?PLMPrevParm,%(?PLMPrevParm-1)
+endif
+endm
+.cref
+else
+??error <parm(s) "&n" declared outside proc def>
+endif
+endm
+?pp1 macro n,t,o,a,b,cpc,ppc
+ife ?PLM
+n equ (t ptr [bp+b])
+else
+.xcref
+.xcref ?PLMParm&cpc
+.cref
+?PLMParm&cpc &macro po
+uconcat <n>,,<equ>,,<(t ptr [bp+>,%(a+po-o),<])>
+?PLMParm&ppc po
+purge ?PLMParm&cpc
+&endm
+endif
+endm
+ifndef ?NOPARMR
+.xcref
+.xcref ?pr,parmR
+.cref
+parmR macro n,r,r2
+?pr n,r,r2,%?rp,%(?ia+2)
+endm
+?pr macro n,r,r2,i,o
+.xcref
+ifnb <r2>
+parmR seg_&n,r
+parmR off_&n,r2
+n equ (dword ptr [bp-o-2])
+.xcref ?t&n
+?t&n=4
+else
+.xcref ?rp&i
+?rp&i=0
+ifdef ?&r
+?rp&i=?&r
+endif
+if ??? or (?cpd eq 0) or (?rp&i eq 0)
+??error <invalid parmR encountered: &n,&r>
+exitm
+endif
+n equ (word ptr [bp-o])
+?t&n=2
+irp x,<bh,ch,dh,bl,cl,dl,ah,al>
+if ?&&x eq ?&r
+n equ (byte ptr [bp-o])
+?t&n=1
+exitm
+endif
+endm
+?ia=?ia+2
+?rp=?rp+1
+endif
+.cref
+endm
+endif
+.xcref
+.xcref localB,localW,localD,localQ,localT,localCP,localDP,localV
+.cref
+localB macro n
+?aloc <n>,<byte ptr>,1,1,0
+endm
+localW macro n
+?aloc <n>,<word ptr>,2,2,1
+endm
+localD macro n
+irp x,<n>
+?aloc <seg_&&x>,<word ptr>,2,2,1
+?aloc <off_&&x>,<word ptr>,2,2,1
+?aloc <&&x>,<dword ptr>,0,4,1
+endm
+endm
+localQ macro n
+?aloc <n>,<qword ptr>,8,8,1
+endm
+localT macro n
+?aloc <n>,<tbyte ptr>,10,10,1
+endm
+if sizec
+localCP macro n
+localD <n>
+endm
+else
+localCP macro n
+localW <n>
+endm
+endif
+if sized
+localDP macro n
+localD <n>
+endm
+else
+localDP macro n
+localW <n>
+endm
+endif
+localV macro n,a
+?aloc <n>,,%(a),0,1
+endm
+?aloc macro n,t,l,s,a
+if ?cpd
+.xcref
+irp x,<n>
+???=???+l
+if a
+???=((??? + 1) and 0fffeh)
+endif
+?al1 x,<t>,%(???+?ia)
+.xcref ?t&&x
+?t&&x=s
+endm
+.cref
+else
+??error <locals "&n" declared outside procedure def>
+endif
+endm
+?al1 macro n,t,o
+n equ (t [bp-o])
+endm
+?gcc macro s,i,cc
+s = i
+ifnb <cc>
+ifidn <cc>,<C>
+s=0
+endif
+ifidn <cc>,<PLM>
+s=1
+endif
+ifidn <cc>,<PASCAL>
+s=1
+endif
+endif
+endm
+ifndef ?NOGLOBAL
+.xcref
+.xcref globalB,globalW,globalD,globalQ,globalT,globalCP,globalDP
+.cref
+globalB macro n,i,s,c
+?ad <n>,1
+?dd n,1,<byte>,<db>,<i>,<s>,<c>
+endm
+globalW macro n,i,s,c
+?ad <n>,2
+?dd n,1,<word>,<dw>,<i>,<s>,<c>
+endm
+globalD macro n,i,s,c
+?ad <n>,4
+?dd n,1,<dword>,<dd>,<i>,<s>,<c>
+off_&n equ n
+seg_&n equ n[2]
+endm
+globalQ macro n,i,s,c
+?ad <n>,8
+?dd n,1,<qword>,<dq>,<i>,<s>,<c>
+endm
+globalT macro n,i,s,c
+?ad <n>,10
+?dd n,1,<tbyte>,<dt>,<i>,<s>,<c>
+endm
+if sizec
+globalCP macro n,i,s,c
+globalD n,<i>,<s>,<c>
+endm
+else
+globalCP macro n,i,s,c
+globalW n,<i>,<s>,<c>
+endm
+endif
+if sized
+globalDP macro n,i,s,c
+globalD n,<i>,<s>,<c>
+endm
+else
+globalDP macro n,i,s,c
+globalW n,<i>,<s>,<c>
+endm
+endif
+endif
+ifndef ?NOSTATIC
+.xcref
+.xcref staticB,staticW,staticD,staticQ,staticT,staticCP,staticDP
+.cref
+staticB macro n,i,s
+?ad <n>,1
+?dd n,0,<byte>,<db>,<i>,<s>,<PLM>
+endm
+staticW macro n,i,s
+?ad <n>,2
+?dd n,0,<word>,<dw>,<i>,<s>,<PLM>
+endm
+staticD macro n,i,s
+?ad <n>,4
+?dd n,0,<dword>,<dd>,<i>,<s>,<PLM>
+endm
+staticQ macro n,i,s
+?ad <n>,8
+?dd n,0,<qword>,<dq>,<i>,<s>,<PLM>
+endm
+staticT macro n,i,s
+?ad <n>,10
+?dd n,0,<tbyte>,<dt>,<i>,<s>,<PLM>
+endm
+if sizec
+staticCP macro n,i,s
+staticD n,<i>,<s>
+endm
+else
+staticCP macro n,i,s
+staticW n,<i>,<s>
+endm
+endif
+if sized
+staticDP macro n,i,s
+staticD n,<i>,<s>
+endm
+else
+staticDP macro n,i,s
+staticW n,<i>,<s>
+endm
+endif
+endif
+?dd macro n,p,t,d,i,s,c
+?gcc ?dd2,%?PLM,<c>
+ife ?dd2
+n label t
+?dd1 _&n,p,<d>,<i>,<s>
+else
+?dd1 n,p,<d>,<i>,<s>
+endif
+endm
+?dd1 macro n,p,d,i,s
+if p
+public n
+endif
+ifb <s>
+n d i
+else
+ifb <i>
+n d s dup (?)
+else
+n d s dup (i)
+endif
+endif
+endm
+ifndef ?NOEXTERN
+.xcref
+.xcref ?ex1,?ex2,externB,externW,externD,externQ,externT
+.xcref externNP,externFP,externP,externCP,externDP,externA
+.cref
+?ex2 = 0
+externA macro n,c
+?ex1 <n>,40h,<abs>,<c>,<>
+endm
+externB macro n,c
+?ex1 <n>,1,<byte>,<c>,<>
+endm
+externW macro n,c
+?ex1 <n>,2,<word>,<c>,<>
+endm
+externD macro n,c
+?ex1 <n>,4,<dword>,<c>,<>
+endm
+externQ macro n,c
+?ex1 <n>,8,<qword>,<c>,<>
+endm
+externT macro n,c
+?ex1 <n>,10,<tbyte>,<c>,<>
+endm
+externNP macro n,c
+?ex1 <n>,2,<near>,<c>,<cc>
+endm
+externFP macro n,c
+?ex1 <n>,4,<far>,<c>,<cc>
+endm
+if sizec
+externP macro n,c
+?ex1 <n>,4,<far>,<c>,<cc>
+endm
+else
+externP macro n,c
+?ex1 <n>,2,<near>,<c>,<cc>
+endm
+endif
+if sizec
+externCP macro n,c
+?ex1 <n>,4,<dword>,<c>,<>
+endm
+else
+externCP macro n,c
+?ex1 <n>,2,<word>,<c>,<>
+endm
+endif
+if sized
+externDP macro n,c
+?ex1 <n>,4,<dword>,<c>,<>
+endm
+else
+externDP macro n,c
+?ex1 <n>,2,<word>,<c>,<>
+endm
+endif
+?ex1 macro n,s,d,c,scv
+?gcc ?ex2,%?PLM,<c>
+irp x,<n>
+.xcref
+.xcref ?t&&x
+.cref
+?t&&x=s
+ife ?ex2
+extrn _&&x:&d
+x equ _&&x
+else
+extrn x:&d
+endif
+ifidn <scv>,<cc>
+.xcref
+.xcref ?CC&&x
+.cref
+?CC&&x=?ex2
+endif
+endm
+endm
+endif
+ifndef ?NOLABEL
+.xcref
+.xcref ?lb1,?lblpu,?lb2
+.xcref labelB,labelW,labelD,labelQ,labelT
+.xcref labelNP,labelFP,labelP,labelCP,labelDP
+.cref
+?lblpu = 0
+?lb2 = 0
+labelB macro n,c
+?lb1 <n>,1,<byte>,<c>
+endm
+labelW macro n,c
+?lb1 <n>,2,<word>,<c>
+endm
+labelD macro n,c
+?lb1 <n>,4,<dword>,<c>
+endm
+labelQ macro n,c
+?lb1 <n>,8,<qword>,<c>
+endm
+labelT macro n,c
+?lb1 <n>,10,<tbyte>,<c>
+endm
+labelNP macro n,c
+?lb1 <n>,2,<near>,<c>
+endm
+labelFP macro n,c
+?lb1 <n>,4,<far>,<c>
+endm
+if sizec
+labelP macro n,c
+?lb1 <n>,4,<far>,<c>
+endm
+else
+labelP macro n,c
+?lb1 <n>,2,<near>,<c>
+endm
+endif
+if sizec
+labelCP macro n,c
+?lb1 <n>,4,<dword>,<c>
+endm
+else
+labelCP macro n,c
+?lb1 <n>,2,<word>,<c>
+endm
+endif
+if sized
+labelDP macro n,c
+?lb1 <n>,4,<dword>,<c>
+endm
+else
+labelDP macro n,c
+?lb1 <n>,2,<word>,<c>
+endm
+endif
+?lb1 macro n,s,d,c
+?gcc ?lb2,%?PLM,<c>
+?lblpu=0
+irp x,<n>
+ifidn <x>,<PUBLIC>
+?lblpu=1
+else
+.xcref
+.xcref ?t&&x
+.cref
+?t&&x=s
+ife ?lb2
+if ?lblpu
+public _&&x
+endif
+_&&x label &d
+x equ _&&x
+else
+if ?lblpu
+public x
+endif
+x label &d
+endif
+endif
+endm
+endm
+endif
+ifndef ?NODEF
+.xcref
+.xcref defB,defW,defD,defQ,defT,defCP,defDP
+.cref
+defB macro n
+?ad <n>,1
+endm
+defW macro n
+?ad <n>,2
+endm
+defD macro n
+?ad <n>,4
+endm
+defQ macro n
+?ad <n>,8
+endm
+defT macro n
+?ad <n>,10
+endm
+if sizec
+defCP macro n
+defD <n>
+endm
+else
+defCP macro n
+defW <n>
+endm
+endif
+if sized
+defDP macro n
+defD <n>
+endm
+else
+defDP macro n
+defW <n>
+endm
+endif
+endif
+?ad macro n,s
+irp x,<n>
+.xcref
+.xcref ?t&&x
+.cref
+?t&&x=s
+endm
+endm
+ifndef ?NOPTR
+.xcref
+.xcref regPtr,farPtr
+.cref
+regPtr macro n,s,o
+farPtr n,s,o
+endm
+farPtr macro n,s,o
+.xcref
+.xcref ?t&n
+.cref
+n &macro
+ push s
+ push o
+&endm
+?t&n=80h
+endm
+endif
+arg macro a
+irp x,<a>
+?argc=?argc+1
+?atal <x>,%?argc
+endm
+endm
+?atal macro n,i
+.xcref
+.xcref ?ali&i
+.cref
+?ali&i &macro
+?ap n
+&endm
+endm
+?ap macro n
+?argl=?argl+2
+ifdef ?t&n
+ife ?t&n-1
+ push word ptr (n)
+exitm
+endif
+ife ?t&n-2
+ push n
+exitm
+endif
+ife ?t&n-4
+ push word ptr (n)[2]
+ push word ptr (n)
+?argl=?argl+2
+exitm
+endif
+ife ?t&n-8
+ push word ptr (n)[6]
+ push word ptr (n)[4]
+ push word ptr (n)[2]
+ push word ptr (n)
+?argl=?argl+6
+exitm
+endif
+if ?t&n and 80h
+n
+?argl=?argl+2
+exitm
+endif
+ife ?t&n
+ push word ptr (n)
+exitm
+endif
+endif
+ push n
+endm
+cCall macro n,a,c
+ifnb <a>
+arg <a>
+endif
+mpush %?rsl
+ifdef ?CC&n
+?cCall1=?CC&n
+else
+?cCall1=?PLM
+endif
+ifnb <c>
+?gcc ?cCall1,%?cCall1,<c>
+endif
+?argl=0
+ife ?cCall1
+?acb=?argc
+else
+?acb=1
+endif
+rept ?argc
+uconcat <?ali>,%?acb
+uconcat <purge>,,<?ali>,%?acb
+ife ?cCall1
+?acb=?acb-1
+else
+?acb=?acb+1
+endif
+endm
+ call n
+if ((?cCall1 eq 0) and (?argl ne 0))
+ add sp,?argl
+endif
+mpop %?rsl
+?rsl=0
+?argc= 0
+?argl= 0
+endm
+cProc macro n,cf,a
+if ?cpd
+?utpe
+endif
+?cpd=1
+???=0
+?argc=0
+?ba=0
+?po=0
+?pu=0
+?ia=0
+?adj=4
+?rp=0
+?uf=0
+?wfp=?WIN
+?ff=0
+?pas=0
+?pcc=?PLM
+ifnb <a>
+?ri ?pas,<a>
+endif
+?pc=sizec
+?nd=?nodata1
+?nx=0
+irp x,<cf>
+ifidn <x>,<FAR>
+?pc=1
+endif
+ifidn <x>,<NEAR>
+?pc=0
+endif
+ifidn <x>,<PUBLIC>
+?pu=1
+endif
+ifidn <x>,<SMALL>
+?uf=1
+endif
+ifidn <x>,<DATA>
+?nd=0
+endif
+ifidn <x>,<NODATA>
+?nd=1
+endif
+ifidn <x>,<ATOMIC>
+?nx=1
+endif
+ifidn <x>,<C>
+?pcc=0
+endif
+ifidn <x>,<PLM>
+?pcc=1
+endif
+ifidn <x>,<PASCAL>
+?pcc=1
+endif
+ifidn <x>,<WIN>
+?wfp=1
+endif
+ifidn <x>,<NONWIN>
+?wfp=0
+endif
+endm
+if ?pcc
+?PLMPrevParm=0
+.xcref
+.xcref ?PLMParm0
+.cref
+?PLMParm0 &macro
+purge ?PLMParm0
+&endm
+endif
+.xcref
+.xcref ?CC&n
+.cref
+?CC&n=?pcc
+if (?nx eq 1) and (?nd eq 0)
+?nx = 0
+??error <ATOMIC specified without NODATA - ATOMIC ignored>
+endif
+if ?pc
+if ?wfp
+ife ?nx
+?ia=2
+?pas = ?pas and (not ?ds)
+endif
+endif
+?adj=?adj+2
+else
+?wfp=0
+endif
+if ?uf
+?pas = ?pas and (not (?si+?di))
+endif
+ife ?pcc
+?pg <_&n>,%?pu,%?pc,%?pas,%?wfp,<n>,%?pcc
+else
+?pg <n>,%?pu,%?pc,%?pas,%?wfp,<n>,%?pcc
+endif
+endm
+?pg macro n,p,c,a,w,nnu,cc
+.xcref
+if ?uf
+if ?nd
+??error <NODATA encountered in &n - user frame ignored>
+?uf=0
+endif
+endif
+.xcref cBegin
+cBegin &macro g
+.xcref
+if cc
+uconcat <?PLMParm>,%?PLMPrevParm,%?po
+endif
+if ?uf
+if ?rp
+??error <parmR encountered in &n - user frame ignored>
+?uf=0
+endif
+endif
+?pg1 <n>,c,a,%?po,w,%?uf,%?nd,%?rp,cc
+?cpd=0
+?argc=0
+?ba=1
+???=(???+1) and 0fffeh
+if p
+public n
+endif
+ife c
+n proc near
+else
+n proc far
+endif
+ife cc
+nnu equ n
+endif
+ifidn <g>,<nogen>
+if ???+?po+a+?rp
+??_out <cBegin - possible invalid use of nogen>
+endif
+else
+if ?uf
+?mf c,%???,%?po
+mpush a
+else
+if w
+ife ?nd
+ mov ax,ds
+ nop
+endif
+ife ?nx
+ife ?DOS5
+ inc bp
+endif
+ push bp
+ mov bp,sp
+ push ds
+else
+if ?ff+???+?po+?rp
+ push bp
+ mov bp,sp
+endif
+endif
+ife ?nd
+ mov ds,ax
+endif
+else
+if ?ff+???+?po+?rp
+ push bp
+ mov bp,sp
+endif
+endif
+if ?rp
+?uf=0
+rept ?rp
+uconcat mpush,,?rp,%?uf
+?uf=?uf+1
+endm
+endif
+if ???
+if ?chkstk1
+ifdef ?CHKSTKPROC
+?CHKSTKPROC %???
+else
+ mov ax,???
+ife cc
+ call _chkstk
+else
+ call chkstk
+endif
+endif
+else
+ sub sp,???
+endif
+endif
+mpush a
+endif
+ifdef ?PROFILE
+if c
+ call StartNMeas
+endif
+endif
+endif
+.cref
+purge cBegin
+&endm
+.xcref ?utpe
+?utpe &macro
+??error <unterminated procedure definition: "&n">
+&endm
+.cref
+endm
+?pg1 macro n,c,a,o,w,f,d,r,cc
+.xcref
+.xcref cEnd
+cEnd &macro g
+.xcref
+?ba=0
+ifidn <g>,<nogen>
+if o+a+r
+??_out <cEnd - possible invalid use of nogen>
+endif
+else
+ifdef ?PROFILE
+if c
+call StopNMeas
+endif
+endif
+mpop a
+if f
+ db 0c3h
+else
+if w
+ife ?nx
+if (?TF eq 0) or (???+?rp)
+ lea sp,-2[bp]
+endif
+ pop ds
+ pop bp
+ife ?DOS5
+ dec bp
+endif
+else
+if (?TF eq 0) or (???+?rp)
+ mov sp,bp
+endif
+if ???+?po+?rp
+ pop bp
+endif
+endif
+else
+if ?ff+???+?po+?rp
+if (?TF eq 0) or (???+?rp)
+ mov sp,bp
+endif
+ pop bp
+endif
+endif
+ife cc
+ ret
+else
+ ret o
+endif
+endif
+endif
+n endp
+.cref
+purge cEnd
+&endm
+.cref
+endm
+assumes macro s,ln
+ifndef ln&_assumes
+assume s:ln
+else
+ln&_assumes s
+endif
+endm
+createSeg macro n,ln,a,co,cl,grp
+ifnb <grp>
+addseg grp,n
+else
+ln&OFFSET equ offset n:
+ln&BASE equ n
+?cs3 <ln>,<n>
+endif
+ifnb <cl>
+n segment a co '&cl'
+else
+n segment a co
+endif
+n ends
+?cs1 <ln>,<n>
+endm
+addseg macro grp,seg
+.xcref
+.xcref grp&_def
+.cref
+ifndef grp&_def
+grp&_def=0
+endif
+if grp&_def ne ASMpass
+.xcref
+.xcref grp&_add
+.cref
+grp&_add &macro s
+grp&_in <seg>,s
+&endm
+.xcref
+.xcref grp&_in
+.cref
+grp&_in &macro sl,s
+ifb <s>
+grp group sl
+else
+grp&_add &macro ns
+grp&_in <sl,s>,ns
+&endm
+endif
+&endm
+grp&_def=ASMpass
+else
+grp&_add seg
+endif
+endm
+defgrp macro grp,ln
+addseg grp
+ifnb <ln>
+irp x,<ln>
+?cs3 <&x>,<grp>
+x&&OFFSET equ offset grp:
+x&&BASE equ grp
+endm
+endif
+endm
+?cs1 macro ln,n
+.xcref
+.xcref ln&_sbegin
+.cref
+ln&_sbegin &macro
+.xcref
+.xcref ?mf
+.cref
+?mf &&macro c,l,p
+if c
+ extrn n&_FARFRAME:near
+ call n&_FARFRAME
+else
+ extrn n&_NEARFRAME:near
+ call n&_NEARFRAME
+endif
+ db l shr 1
+ db p shr 1
+&&endm
+?cs2 <ln>,<n>
+n segment
+&endm
+endm
+?cs2 macro ln,n
+.xcref
+.xcref sEnd
+.cref
+sEnd &macro
+n ends
+purge ?mf
+purge sEnd
+&endm
+endm
+?cs3 macro ln,n
+.xcref
+.xcref ln&_assumes
+.cref
+ln&_assumes &macro s
+assume s:&n
+&endm
+endm
+.xcref
+.xcref sBegin
+.cref
+sBegin macro ln
+ln&_sbegin
+endm
+ife ?DF
+createSeg _TEXT,Code,word,public,CODE
+ife ?nodata1
+createSeg _DATA,Data,word,public,DATA,DGROUP
+defgrp DGROUP,Data
+endif
+if ?chkstk1
+ifndef ?CHKSTKPROC
+externp <chkstk>
+endif
+endif
+endif
+errnz macro x
+if2
+if x
+errnz1 <x>,%(x)
+endif
+endif
+endm
+errnz1 macro x1,x2
+= *errnz* x1 = x2
+.err
+endm
+errn$ macro l,x
+errnz <offset $ - offset l x>
+endm
+ifdef ?PROFILE
+externFP <StartNMeas,StopNMeas>
+endif
diff --git a/private/mvdm/dpmi/cmacros.mas b/private/mvdm/dpmi/cmacros.mas
new file mode 100644
index 000000000..0f719fd82
--- /dev/null
+++ b/private/mvdm/dpmi/cmacros.mas
@@ -0,0 +1,2450 @@
+comment $
+
+cmacros - assembly macros for interfacing to hhls
+
+(C)Copyright Microsoft Corp. 1984-1988
+
+$
+
+;; Revision History
+;;
+;; 1.00 05/03/84 Initial Release
+;;
+;; 1.01 05/06/84 Greg Whitten
+;; Added defgrp and changed cMerge to Microsoft C
+;; Added copyright message and changed to 1.01
+;; Changes should have no affect on working programs
+;;
+;; 1.02 07/10/84 Steve Wood
+;; Added labelx macros
+;;
+;; 1.03 07/14/84 Greg Whitten
+;; Added defines for ?pu, ?adj, ?lblpu
+;; (removes undefined errors)
+;; Changes should have no affect on working programs
+;;
+;; 1.04 07/18/84 Greg Whitten
+;; Added local control from PL/M or C conventions
+;; except for cCall macro
+;;
+;; 1.05 08/06/84 Steve Wood
+;; Made ?PLM and ?WIN be the defaults
+;;
+;; 1.06 01/02/85 Steve Wood
+;; Changed createSeg and defgrp to automatically
+;; define the ln_assumes macro and the lnoffset
+;; and lnbase equates for each logical segment
+;; name.
+;;
+;; 1.07 02/19/85 Walt Moore
+;; Added farptr macro for defining a far pointer
+;; to be used in a cCall. Folded regptr into
+;; farptr. Space compaction in macros. Changed
+;; ?pp to be smaller. Moved ?ia out of ?al1 into
+;; ?aloc. Merged cProc and ?pd into one macro.
+;; Changed some %outs to use the error macro so
+;; an error would be generated. Added makeframe
+;; and parmR to cProc. Changed error to also put
+;; the error message in the listing.
+;; Deleted the smashes macro.
+;;
+;; 1.08 03/18/85 Steve Wood
+;; Added NODATA support.
+;;
+;; 1.09 03/27/85 Steve Wood
+;; Added ?definitions
+;;
+;; 2.00 04/01/85 Steve Wood
+;; April fools
+;;
+;; 2.01 06/17/85 Steve Wood
+;; Changed NODATA to always generate POP DS
+;; for return address patching
+;;
+;; 2.02 02/11/86 Steve Wood
+;; Added ATOMIC keyword to cProc macro
+;; Changed far epilog to use LEA SP,BP-2
+;; Changed error macro to ??error to avoid
+;; conflict
+;;
+;; 2.03 03/06/86 Steve Wood
+;; Fixed bug with ATOMIC and locals in far proc
+;; Added DOS5 switch to disable INC/DEC BP
+;; instructions in special far prologs/epilogs
+;;
+;; 2.04 08/07/86 Scott Randell
+;; Fixed bug with ATOMIC and ?TF
+;; (was doing unnecessary MOV SP,BP)
+;; Added pcode profile ?PROFILE
+;;
+;; 2.05 08/12/86 Walt Moore
+;; Changed _TEXT alignment to word.
+;; Added/corrected some comments.
+;; Removed redundant init of ?pc in cProc
+;; Made ATOMIC require NODATA
+;; Moved definition of non-underscored 'C' label
+;; from the cProc to the cBegin macro
+;; Minor clean-up of code
+;;
+;; 2.06 09/11/86 Walt Moore
+;; Added private stack checking
+;; Put local control for PL/M or C into cCall
+;;
+;;
+;; 2.07 09/19/86 Steve Wood
+;; Added ?SMALL, ?MEDIUM, etc. symbols
+;; Added forceframe keyword to cProc macro.
+;; Interpret ?TF for all epilogs.
+;;
+;; 3.xx.a 02/26/87 Massive rework. Documentation coming.
+;;
+;; 10/15/90 Earle Horton in Windows Development
+;; reworked to allow saving 386 extended
+;; registers in mpush and mpop macros.
+;;
+;;
+;; Assembly macros for interfacing to C
+;;
+;; User setable conditional assembly flags used within the cmacros
+;;
+;; Memory model flags. Set only one of the following. memS is the
+;; default. The symbols with ? are for defining from the command line
+;; and the memx symbols are numeric symbols that can be set in your source
+;; file prior to including this file.
+;;
+;; ?SMALL memS - small model
+;; ?MEDIUM memM - medium model
+;; ?LARGE memL - large model
+;; ?COMPACT memC - compact model
+;; ?HUGE memH - huge model
+;;
+;; ?DF Define flag. If this flag is 0, then defines default segment
+;; and group definitions based on the compiler flag. If this
+;; flag is 1, then does not define any segments or groups.
+;;
+;; ?TF Tight flag. If this flag is 0, then use longer epilog
+;; sequence that safely cleans up a stack frame. If this flag is
+;; 1, then use more efficient epilog that assumes the stack is
+;; valid (SP)
+;;
+;; ?WIN Windows flag. Enables generation of special prolog/epilog
+;; for far procedures. Default value is 1 (Windows).
+;;
+;; DOS5 If defined, then special far prolog/epilog seqeuences will not
+;; include the INC/DEC BP instructions.
+;;
+;; ?PLM Calling convention flag. If this flag is 0, then the
+;; calling convention used is that of C. If this flag
+;; is 1, then the PL/M calling convention is used.
+;; The default value is 1. The PL/M calling convention
+;; is used by pascal, fortran, basic, and cobol.
+;;
+;; In the C calling convention, arguments are passed
+;; in reverse order; arg0 is the last pushed, argn is the
+;; first pushed. also, it is the callers responsibility
+;; to remove the arguments from the stack upon a return
+;; from a call.
+;;
+;; In the PL/M calling comvention, arguments are passed
+;; as encountered; arg0 is the first pushed, argn is the
+;; last pushed. also, it is the called procedure's
+;; responsibility to remove parameters from the stack
+;; before returning (using the RET n instruction)
+;;
+;; ?NODATA If defined, then no data segment or DGROUP is defined and
+;; the special prolog/epilog sequences will not contain the
+;; code needed to setup DS.
+;;
+;; ?CHKSTK If defined, then prolog sequences for cProcs with local
+;; parameters will call the CHKSTK procedure to allocate
+;; the stack space.
+;;
+;; ?CHKSTKPROC If defined, then this macro will be invoked to
+;; perform the stack checking, otherwise the
+;; standard stack checking procedure will be
+;; performed. ?CHKSTKPROC must be declared
+;; before the cmacros are included in the source
+;; else the standard chkstk routine will be declared
+;; as an external symbol.
+;;
+;; On entry to the user's stack checking procedure,
+;; the frame has been setup except for allocating
+;; local variable space and saving autosave registers.
+;;
+;; The user supplied macro is passed as an argument
+;; the number of byte of stack space requested.
+;;
+;; ?PROFILE If defined then all far cBegin entries will have StartNMeas,
+;; and all cEnd will have StopNMeas calls, StartNMeas and
+;; StopNMeas will be defined as externfp
+;;
+;; ?NOPARMR If defined, then the "parmR" macro will not be defined.
+;;
+;; ?NOGLOBAL If defined, then the "globalx" macros will not be defined.
+;;
+;; ?NOSTATIC If defined, then the "staticx" macros will not be defined.
+;;
+;; ?NOEXTERN If defined, then the "externx" macros will not be defined.
+;;
+;; ?NOLABEL If defined, then the "labelx" macros will not be defined.
+;;
+;; ?NODEF If defined, then the "defx" macros will not be defined.
+;;
+;; ?NOPTR If defined, then "farptr & regptr" will not be defined.
+;;
+;; ?QUIET If defined, then only error messages will be issued to
+;; the console. If undefined, then certain cmacro text will
+;; be generated to the console.
+
+
+.xcref ;;Get rid of a lot of symbols
+
+
+
+; ??_out - output given message to the console unless ?QUIET has
+; been specified.
+;
+; usage:
+; ??_out <t>
+;
+; where:
+; <t> is the message to output
+
+.xcref ??_out
+??_out macro t
+ ifndef ?QUIET
+ %out t
+ endif
+endm
+
+
+
+; outif - output msg if name is non-zero. if name is undefined,
+; set name = 0, else set name to the default value.
+;
+; usage:
+; outif name,defval,onmsg,offmsg
+; where:
+; name name of symbol
+; defval default value to give symbol if not defined
+; if blank, then 0 will be used
+; onmsg text to display if symbol is non-zero
+; offmsg test to be displayed if symbol is zero
+
+
+outif macro name,defval,onmsg,offmsg
+ ifndef name
+ ifb <defval>
+ name=0
+ else
+ name=defval
+ endif
+ endif
+ if name
+ name=1
+ ifnb <onmsg>
+ ??_out <! onmsg>
+ endif
+ else
+ ifnb <offmsg>
+ ??_out <! offmsg>
+ endif
+ endif
+endm
+
+
+
+; ??error - output msg and generate an assembly time error
+;
+; usage:
+; ??error <t>
+; where:
+; t is the text to be output
+
+
+.xcref ??error
+??error macro msg
+ e r r o r ----- msg ;;to console
+ .err ;;for good measure, force this also
+endm
+
+
+.xcref ASMpass
+.xcref memS,memM,memL,memC,memH,memMOD,sizec,sized
+
+if1 ;;Only on pass 1
+ ASMpass=1
+ ifdef ?SMALL ;;inform user what is going on
+ memS=1
+ endif
+ ifdef ?MEDIUM
+ memM=1
+ endif
+ ifdef ?COMPACT
+ memC=1
+ endif
+ ifdef ?LARGE
+ memL=1
+ endif
+ ifdef ?HUGE
+ memH=1
+ endif
+
+ ??_out <cMacros Version 5.20 - Copyright (c) Microsoft Corp. 1984-1988>
+ outif memS,0,<Small model>
+ outif memM,0,<Medium model>
+ outif memL,0,<Large model>
+ outif memC,0,<Compact model>
+ outif memH,0,<Huge model>
+
+ memMOD= memS + memM + memL + memC + memH
+ if memMOD ne 1
+ if memMOD eq 0
+ memS = 1 ; assume small model
+ else
+ ??error <more than 1 memory model selected>
+ endif
+ endif
+
+ sizec= memM + memL + memH ; large code
+ sized= memL + memC + (memH*2) ; large data (2 if huge)
+
+ outif ?DF,0,<No segments or groups will be defined>
+ outif ?TF,0,<Epilog sequences assume valid SP>
+ outif ?WIN,1,<Windows support>
+ if ?WIN eq 1
+ outif ?PLM,1,<>
+ else
+ outif ?PLM,1,<PL/M calling convention>
+ endif
+
+ ifndef ?NODATA
+ ?nodata1=0
+ else
+ ?nodata1=1
+ ??_out <! NODATA module>
+ endif
+
+ ifndef ?CHKSTK
+ ?chkstk1=0
+ else
+ ?chkstk1=1
+ ifdef ?CHKSTKPROC
+ ??_out <! Private stack checking enabled>
+ else
+ ??_out <! Stack checking enabled>
+ endif
+ endif
+
+ ifndef DOS5
+ ?DOS5=0
+ else
+ ?DOS5=1
+ ??_out <! DOS5 module>
+ endif
+
+ ifdef ?PROFILE
+ ??_out <! Native profiling enabled>
+ endif
+else
+ ASMpass=2
+endif
+
+;; Initialize all symbols used in the macros. Theses symbols will not be
+;; included in any cross reference listing.
+
+ .xcref ?n,?ax,?eax,?bx,?ebx
+ .xcref ?cx,?ecx,?dx,?edx
+ .xcref ?si,?esi,?di,?edi,?es,?ds,?fs
+ .xcref ?gs
+
+ .xcref ?rsl,?cpd,?argl,?argc,?ba
+ .xcref ?acb,???,?po
+ .xcref ?pas,?pc
+
+ .xcref uconcat,mpush,mpop
+ .xcref ?ri,?pp,?pp1,?al1
+ .xcref ?ad,?ap,?atal,?dd,?dd1,?dd2
+ .xcref ?pg,?pg1,?aloc,?cs1,?cs2
+ .xcref ?DF,?TF,?ff,?PLM,?WIN,?ia,?pu,?adj
+ .xcref ?uf,?rp,?nx,?nd,?nodata1,?chkstk1,?DOS5
+ .xcref ?wfp,arg,cCall,cProc,assumes,?cs3,?cs2,?cs1
+ .xcref defgrp,addseg,createSeg
+ .xcref save,outif,errnz,errn$,errnz1
+ .xcref ?PLMPrevParm,?gcc
+ .xcref ?cCall1,?pcc
+
+
+;; conditionals set by the macros
+;;
+;; ?pc Procedure class. If this is set to 1, then the procedure
+;; is a far procedure, else it is a near procedure.
+;;
+;; ?ia Interface adjustment count for far procedures. The
+;; interface adjustment defines the number of bytes of
+;; storage allocated between BP and the first frame variable
+;; allocated on the stack.
+;;
+;; Normally zero, it will be adjusted for both far windows
+;; procedures and by register parameters.
+;;
+;; ?cpd Current procedure defined. This is set to a non-zero
+;; value if a procedure is being defined (i.e a cProc has
+;; been encountered, and cBegin has not).
+;;
+;; ?ba Begin active. This is set to a non-zero value if a
+;; cBegin is active (i.e. a cBegin has been encountered,
+;; and cEnd has not).
+;;
+;; ?wfp Windows far procedure. Set if a windows far procedure
+
+
+;; Other variables that are defined once so that the .xcref command
+;; doesn't get too upset if they show up missing!
+
+?rsl = 0 ;;0 = no register to save
+?cpd = 0 ;;<> 0 if in a procedure definition
+?argl = 0 ;;length of arguments pushed on stack
+?argc = 0 ;;# of arguments so far
+?ba = 0 ;;<>0 if in a procedure (xbegin)
+?acb = 0 ;;number of arguments to a call
+??? = 0 ;;byte count of local storage
+?po = 0 ;;byte count of parameters
+?pas = 0 ;;autosave value for procedure
+?pc = 0 ;;class of a procedure (near/far)
+?ia = 0 ;;no adjustment
+?pu = 0 ;;public flag for some macros
+?adj = 0 ;;initial define for .xcref
+?rp = 0 ;;count of register parameters
+?uf = 0 ;;user's frame code specified
+?nd = 0 ;;NODATA keyword specified
+?nx = 0 ;;ATOMIC keyword specified
+?wfp = 0 ;;window far procedure
+?ff = 0 ;;forceframe keyword specified
+?dd2 = 0 ;;used for globalx and staticx
+?cCall1 = 0 ;;used for cCalls
+?pcc = 0 ;;procedure calling convention
+?PLMPrevParm = 0 ;;Used in parameter processing
+
+ .xcref ?casen
+if1 ;;only define ?casen on pass 1
+?casen = 0 ;;case sensitive assembly if <> 0
+endif
+
+
+
+?n = 0000000000000000b ;;register none
+?ax = 0000000000000001b ;;register ax
+?eax = 0000000000000010b ;;register eax
+?bx = 0000000000000100b ;;register bx
+?ebx = 0000000000001000b ;;register ebx
+?cx = 0000000000010000b ;;register cx
+?ecx = 0000000000100000b ;;register ecx
+?dx = 0000000001000000b ;;register dx
+?edx = 0000000010000000b ;;register edx
+?si = 0000000100000000b ;;register si
+?esi = 0000001000000000b ;;register esi
+?di = 0000010000000000b ;;register di
+?edi = 0000100000000000b ;;register edi
+?ds = 0001000000000000b ;;register ds
+?es = 0010000000000000b ;;register es
+?fs = 0100000000000000b ;;register fs
+?gs = 1000000000000000b ;;register gs
+ .cref
+
+
+
+;; uconcat - unconditionally generate a statement from a field
+;; of given parameters
+;;
+;; usage:
+;; uconcat a,b,c,d,e,f,g
+;;
+;; where:
+;; a,b are concatenated for field 1
+;; c,d are concatenated for field 2
+;; e,f,g are concatenated for field 3
+
+uconcat macro a,b,c,d,e,f,g
+ a&b c&d e&f&g
+endm
+
+
+
+;; mpush pushes multiple registers onto the stack according to
+;; a register specification.
+;;
+;; format:
+;; mpush r
+;;
+;; where:
+;; r is a numeric expression returned from ?ri
+;; or any other valid register expression
+
+mpush macro r
+ irp x,<ax,eax,bx,ebx,cx,ecx,dx,edx,si,esi,di,edi,ds,es,fs,gs>
+ if (r and ?&&x)
+ push x ;@
+ endif
+ endm
+endm
+
+
+
+;; mpop pops multiple registers from the stack according to
+;; a register specification.
+;;
+;; format:
+;; mpop r
+;;
+;; where:
+;; r is a numeric expression returned from ?ri
+;; or any other valid register expression
+
+mpop macro r
+ irp x,<gs,fs,es,ds,edi,di,esi,si,edx,dx,ecx,cx,ebx,bx,eax,ax>
+ if (r and ?&&x)
+ pop x ;@
+ endif
+ endm
+endm
+
+
+;; save - flag that the indicated registers are to be saved/restored
+;;
+;; A flag is created which indicates which registers are to be saved
+;; when the cCall macro is invoked, and then restored after the call.
+;;
+;; usage:
+;; save <r>
+;;
+;; where r is the list of registers to save, which may be:
+;;
+;; register saves
+;; AX AX
+;; EAX EAX
+;; BX BX
+;; EBX EBX
+;; CX CX
+;; ECX ECX
+;; DX DX
+;; EDX EDX
+;; SI SI
+;; ESI ESI
+;; DI DI
+;; EDI EDI
+;; DS DS
+;; ES ES
+;; FS FS
+;; GS GS
+;;
+;; none nothing
+;;
+;; the macro generates a value for the variable ?rsl
+
+save macro r
+ ?rsl=0 ;;initialize save list
+ ?ri ?rsl,<r> ;;generate magic number
+endm
+
+
+
+;; ?ri - or register indexes to variable
+;;
+;; ?ri is a macro that examines the passed argument list and computes
+;; a register index variable.
+;;
+;; The values ORed with the variable are:
+;;
+;; ?n = 0000000000000000b ;;register none
+;; ?ax = 0000000000000001b ;;register ax
+;; ?eax = 0000000000000010b ;;register eax
+;; ?bx = 0000000000000100b ;;register bx
+;; ?ebx = 0000000000001000b ;;register ebx
+;; ?cx = 0000000000010000b ;;register cx
+;; ?ecx = 0000000000100000b ;;register ecx
+;; ?dx = 0000000001000000b ;;register dx
+;; ?edx = 0000000010000000b ;;register edx
+;; ?si = 0000000100000000b ;;register si
+;; ?esi = 0000001000000000b ;;register esi
+;; ?di = 0000010000000000b ;;register di
+;; ?edi = 0000100000000000b ;;register edi
+;; ?ds = 0001000000000000b ;;register ds
+;; ?es = 0010000000000000b ;;register es
+;; ?fs = 0100000000000000b ;;register fs
+;; ?gs = 1000000000000000b ;;register gs
+;;
+;; usage:
+;; ?ri n,<r>
+;; where:
+;; n is the variable to contain the new index value
+;; r is the register list
+
+?ri macro n,r
+ irp x,<r>
+ .ERRNDEF ?&&x ;; Generate error if unknown register
+ n=n or ?&&x
+ endm
+endm
+
+
+
+;; parmx - generate reference to parameter(s) on the stack
+;;
+;; An equate is generated for addressing a paramter(s)
+;; on the stack for the current procedural frame.
+;;
+;; An error message is generated if there isn't a current frame.
+;;
+;; usage:
+;; parmx n
+;; where:
+;; x is the type of the argument(s) b=byte, w=word, d=dword
+;; n is the name(s) to be given the parameter(s).
+;;
+;; Bytes are considered to be two bytes long for alignment.
+;;
+;; The parmd form of the macro generates three equates:
+;;
+;; name - for accessing the parameter as a double word
+;; off_name - for accessing the offset (lsw) of the parameter
+;; seg_name - for accessing the segment (msw) of the parameter
+
+.xcref
+.xcref parmB,parmW,parmD,parmQ,parmT,parmCP,parmDP
+.cref
+
+parmB macro n
+ ?pp <n>,<byte>,2,1
+endm
+
+parmW macro n
+ ?pp <n>,<word>,2,2
+endm
+
+parmD macro n
+ ife ?PLM ;;if to assemble for C
+ irp x,<n>
+ ?pp <&&x>,<dword>,0,4
+ ?pp <off_&&x>,<word>,2,2
+ ?pp <seg_&&x>,<word>,2,2
+ endm
+ else ;;if to assemble for PL/M
+ irp x,<n>
+ ?pp <seg_&&x>,<word>,2,2
+ ?pp <off_&&x>,<word>,2,2
+ ?pp <&&x>,<dword>,0,4
+ endm
+ endif
+endm
+
+parmQ macro n
+ ?pp <n>,<qword>,8,8
+endm
+
+parmT macro n
+ ?pp <n>,<tbyte>,10,10
+endm
+
+if sizec
+ parmCP macro n
+ parmD <n>
+ endm
+else
+ parmCP macro n
+ parmW <n>
+ endm
+endif
+
+if sized
+ parmDP macro n
+ parmD <n>
+ endm
+else
+ parmDP macro n
+ parmW <n>
+ endm
+endif
+
+
+
+;; ?pp is the generalized parameter definition macro
+;;
+;; usage:
+;; ?pp m,t,l,s
+;;
+;; where:
+;; n is the name(s) of the parameters
+;; t is the type (word, dword)
+;; l is the length to update parameter byte count by
+;; s is the internal typing size
+
+
+?pp macro n,t,l,s ;;process parameter
+ if ?cpd ;;must be in a procedure definition
+ .xcref
+ irp x,<n>
+ .xcref ?t&&x ;;don't want this in xref
+ ?t&&x=s ;;save size info
+ ife ?PLM ;;if C calling convention
+ ?pp1 x,<t>,,,%(?po+?adj)
+ ?po=?po+l ;;update parameter offset
+ else ;;else assemble for PL/M
+ ?PLMPrevParm=?PLMPrevParm+1 ;;Show next parameter
+ ?po=?po+l ;;update parameter offset
+ ?pp1 x,<t>,%?po,%?adj,,%?PLMPrevParm,%(?PLMPrevParm-1)
+ endif
+ endm
+ .cref
+ else
+ ??error <parm(s) "&n" declared outside proc def>
+ endif
+endm
+
+
+
+;; ?pp1 is the macro that generates the text equate for the
+;; parameter. Two options exist, one for the C calling
+;; convention where the last parameter was the first pushed onto
+;; the stack ('C' convention), and one for the PL/M calling
+;; convention where the first parameter was the first
+;; pushed (also the same as ms-pascal).
+;;
+;; The text generated will be of one of two forms:
+;;
+;; name equ (type ptr [bp+(adj+offset)]) for C
+;; or
+;; name equ (type ptr [bp+adj+?po-offset]) for PL/M
+;;
+;;
+;; For C, since parameters are pushed first last, the offset
+;; plus the adjust will point to the correct parameter.
+;;
+;; For PL/M, since parameters are pushed first first, the offset
+;; of a parameter is much more complicated. A known portion of
+;; the offset can be computed when the text equate is generated.
+;;
+;; What is known is the number of garbage bytes between BP and
+;; the nearest parameter (in this case the last parameter), and
+;; also how many bytes of parameters have preceeded this parameter.
+;;
+;; What is unknown is how many total bytes of parameters there will
+;; be, which affects all the generated text equates since the offset
+;; from bp must be determined at some point.
+;;
+;; Well, the offset from BP can be computed with one variable if
+;; the following is remembered:
+;;
+;; the offset of any parameter from the first parameter is always
+;; the current parameter offset (?po).
+;;
+;; With this in mind, you just have to figure out where the first
+;; parameter is, which is:
+;;
+;; bp + garbage adjustment + distance to first parameter
+;; or
+;; bp + ?adj + ?po
+;;
+;; This implies that any parameter can be defined as:
+;;
+;; bp + ?adj + ?po -%?po
+;;
+;; Make any sense?
+;;
+;; For PL/M, a chain of self-purging macros will be generated
+;; which will pass the evaluated ?po to any previous incarnation
+;; of the macro. This will allow the text equate to be generated
+;; with the actual offset instead of the symbolic ?po.
+;;
+;;
+;; usage:
+;; ?pp1 n,t,o,a,b,cpc,ppc
+;;
+;; where:
+;; n is the name to be given the equate
+;; t is the type (byte, word, dword)
+;; o is the offset from the first parameter
+;; a is the adjustment
+;; b is the adjustment plus the offset from the first parameter
+;; cpc is the number of parameters so far
+;; ppc is cpc - 1
+
+
+?pp1 macro n,t,o,a,b,cpc,ppc
+ ife ?PLM ;;if to generate for C
+ n equ (t ptr [bp+b])
+ else ;;else generate for PL/M
+ .xcref
+ .xcref ?PLMParm&cpc
+ .cref
+ ?PLMParm&cpc &macro po
+ uconcat <n>,,<equ>,,<(t ptr [bp+>,%(a+po-o),<])>
+ ?PLMParm&ppc po
+ purge ?PLMParm&cpc
+ &endm
+ endif
+endm
+
+
+
+;; parmR - register parameter
+;;
+;; parmR is the macro used for generating register parameters.
+;; The space allocated for the register parameters will be
+;; the ?ia (interface adjust) area which is between the old
+;; BP and the first parameter. Normally this is empty (?ia=0),
+;; or has the saved ds for a windows far procedure.
+;;
+;; Byte and dword register parameters will be allowed.
+;;
+;; usage:
+;; parmR n,r,r2
+;; where:
+;; n is the name of the parameter
+;; r is the register it is in
+;; r2 is the offset register if a dword
+
+
+ifndef ?NOPARMR
+ .xcref
+ .xcref ?pr,parmR
+ .cref
+
+ parmR macro n,r,r2
+ ?pr n,r,r2,%?rp,%(?ia+2)
+ endm
+
+ ;; ?pr - register parameter
+ ;;
+ ;; ?pr is the actual macro for generating the equates for
+ ;; register parameters.
+ ;;
+ ;; usage:
+ ;; parmR n,r,r2,i,o
+ ;; where:
+ ;; n is the name of the parameter
+ ;; r is the register it is in
+ ;; r2 is the offset register if a dword
+ ;; i is the index of the ?rp to generate
+ ;; o is the offset from bp where the parm will be
+
+ ?pr macro n,r,r2,i,o
+ .xcref
+ ifnb <r2> ;;if a dword parameter
+ parmR seg_&n,r ;;define segment equate
+ parmR off_&n,r2 ;;define offset equate
+ n equ (dword ptr [bp-o-2]) ;;define dword equate
+ .xcref ?t&n
+ ?t&n=4 ;;show a dword to cmacros
+ else
+ .xcref ?rp&i
+ ?rp&i=0 ;;show no register(s)
+ ifdef ?&r ;;define register if valid
+ ?rp&i=?&r
+ endif
+
+ if ??? or (?cpd eq 0) or (?rp&i eq 0)
+ ??error <invalid parmR encountered: &n,&r>
+ exitm
+ endif
+
+ n equ (word ptr [bp-o]) ;;assume a word register
+ ?t&n=2 ;;show a word to cmacros
+ irp x,<bh,ch,dh,bl,cl,dl,ah,al>
+ if ?&&x eq ?&r ;;if really a byte register
+ n equ (byte ptr [bp-o]) ;; then make it a byte
+ ?t&n=1 ;;show a byte to cmacros
+ exitm
+ endif
+ endm
+ ?ia=?ia+2 ;;show this guy is out there
+ ?rp=?rp+1 ;;show one more register parameter
+ endif
+ .cref
+ endm
+endif
+
+
+
+;; localx - generate reference to a local variable on the stack
+;;
+;; An equate is generated for addressing a local variable
+;; on the stack for the current procedural frame.
+;;
+;; usage:
+;; localx n
+;; where:
+;; x is the type b=byte, w=word, d=dword, v=variable size
+;; n is the name(s) to be given the variable(s).
+;;
+;; Bytes are considered to be two bytes long for alignment reasons
+;;
+;; The locald form of the macro generates three equates:
+;;
+;; name - for accessing the variable as a double word
+;; off_name - for accessing the offset (lsw) of the variable
+;; seg_name - for accessing the segment (msw) of the variable
+
+
+.xcref
+.xcref localB,localW,localD,localQ,localT,localCP,localDP,localV
+.cref
+
+localB macro n
+ ?aloc <n>,<byte ptr>,1,1,0 ;; no alignment
+endm
+
+localW macro n
+ ?aloc <n>,<word ptr>,2,2,1 ;; word aligned
+endm
+
+localD macro n
+ irp x,<n>
+ ?aloc <seg_&&x>,<word ptr>,2,2,1 ;; word aligned
+ ?aloc <off_&&x>,<word ptr>,2,2,1 ;; word aligned
+ ?aloc <&&x>,<dword ptr>,0,4,1 ;; word aligned
+ endm
+endm
+
+localQ macro n
+ ?aloc <n>,<qword ptr>,8,8,1 ;; word aligned
+endm
+
+localT macro n
+ ?aloc <n>,<tbyte ptr>,10,10,1 ;; word aligned
+endm
+
+if sizec
+ localCP macro n
+ localD <n>
+ endm
+else
+ localCP macro n
+ localW <n>
+ endm
+endif
+
+if sized
+ localDP macro n
+ localD <n>
+ endm
+else
+ localDP macro n
+ localW <n>
+ endm
+endif
+
+localV macro n,a
+ ?aloc <n>,,%(a),0,1 ;; word aligned
+endm
+
+
+;; ?aloc is the macro that actually allocates local storage.
+;; it is only invoked by the localx macros.
+;;
+;; usage:
+;; ?aloc n,t,l,s,a
+;; where:
+;; n is a list of names of local variable of the
+;; given type.
+;; t is the text string for the given variable
+;; and is one of:
+;; word ptr
+;; dword ptr
+;; byte ptr
+;; or alternatively left blank for variable size
+;; allocations (no implicit type).
+;; l is the size of the variable in bytes
+;; s is the internal type flag (size), and is one of:
+;; word - 2
+;; dword - 4
+;; byte - 1
+;; variable - 0
+;; a is a flag indicating that word alignment is to be
+;; forced for this type of item.
+;;
+;; NOTE: It is assumed that the stack is already aligned on a word
+;; boundary when the cProc is invoked. The macros will guarantee
+;; to allocate an even number of bytes on the stack to maintain
+;; word alignment.
+
+
+?aloc macro n,t,l,s,a
+ if ?cpd ;;must be in a proc def
+ .xcref
+ irp x,<n> ;;generate symbol equates
+ ???=???+l ;;update length of locals
+ if a ;;if align, then force word alignment
+ ???=((??? + 1) and 0fffeh)
+ endif
+ ?al1 x,<t>,%(???+?ia) ;;?ia will always be valid (0 or 2)
+ .xcref ?t&&x
+ ?t&&x=s ;;save size info
+ endm
+ .cref
+ else
+ ??error <locals "&n" declared outside procedure def>
+ endif
+endm
+
+
+
+;; ?al1 - allocate local, continued.
+;;
+;; ?al1 actually generates the text equate for the local variable.
+;; The form of the text equate generated is more or less:
+;;
+;; name equ (type ptr [bp-?ia-nn])
+;; or
+;; name equ ([bp-?ia-nn])
+;;
+;; where:
+;; ?ia is defined to be either zero, or is defined to be
+;; the number of bytes between the saved BP and the first
+;; local. ?ia is only applicable if the current cProc is
+;; a windows far procedure or if parmRs have been
+;; encountered. If not, the ?ia will be zero. since ?ia
+;; is determinable prior to invoking this macro, it will be
+;; added into the offset ("nn") passed to this macro
+;;
+;; usage:
+;; ?al1 n,t,o
+;; where:
+;; n is the name for the text equate
+;; t is the type of the equate
+;; o is the offset of the equate
+
+
+?al1 macro n,t,o
+ n equ (t [bp-o])
+endm
+
+
+;; ?gcc - get calling convention
+;;
+;; ?gcv sets the given symbol to the calling convention
+;; to be used.
+;;
+;; usage:
+;; ?gcc s,i,cc
+;;
+;; where:
+;; s is the symbol to return the convention in
+;; s = 0 if 'C' calling convention
+;; s = 1 if PL/M (PASCAL) calling convention
+;; i is the initial value for s
+;; cc is the calling convention override, and may be one of
+;; C use 'C' convention
+;; PLM use PL/M calling convention
+;; PASCAL use PL/M calling convention
+
+?gcc macro s,i,cc
+ s = i ;;Set default calling convention
+ ifnb <cc>
+ ifidn <cc>,<C> ;;If overriding default
+ s=0 ;; 'C' calling convention
+ endif
+ ifidn <cc>,<PLM>
+ s=1 ;; PL/M calling convention
+ endif
+ ifidn <cc>,<PASCAL>
+ s=1 ;; PL/M calling convention
+ endif
+ endif
+endm
+
+
+
+ifndef ?NOGLOBAL
+ .xcref
+ .xcref globalB,globalW,globalD,globalQ,globalT,globalCP,globalDP
+ .cref
+
+ ;; globalx - define global data of type x
+ ;;
+ ;; usage:
+ ;; globalx n,i,s,c
+ ;; where:
+ ;; x is the type of the variable b=byte, w=word, d=dword
+ ;; q=quad word, t=tenbytes, cp=code pointer, dp=data pointer
+ ;; n is the name to be given the variable.
+ ;; i is the initial value of the variable.
+ ;; s is the duplication factor
+ ;; c is the convention, C for C, PLM or PASCAL for PL/M.
+ ;; The default (?PLM flag) will be used if not specified.
+ ;;
+ ;; The D form will generate two extra equates of the form off_n and seg_n.
+
+ globalB macro n,i,s,c
+ ?ad <n>,1
+ ?dd n,1,<byte>,<db>,<i>,<s>,<c>
+ endm
+
+ globalW macro n,i,s,c
+ ?ad <n>,2
+ ?dd n,1,<word>,<dw>,<i>,<s>,<c>
+ endm
+
+ globalD macro n,i,s,c
+ ?ad <n>,4
+ ?dd n,1,<dword>,<dd>,<i>,<s>,<c>
+ off_&n equ n
+ seg_&n equ n[2]
+ endm
+
+ globalQ macro n,i,s,c
+ ?ad <n>,8
+ ?dd n,1,<qword>,<dq>,<i>,<s>,<c>
+ endm
+
+ globalT macro n,i,s,c
+ ?ad <n>,10
+ ?dd n,1,<tbyte>,<dt>,<i>,<s>,<c>
+ endm
+
+ if sizec
+ globalCP macro n,i,s,c
+ globalD n,<i>,<s>,<c>
+ endm
+ else
+ globalCP macro n,i,s,c
+ globalW n,<i>,<s>,<c>
+ endm
+ endif
+
+ if sized
+ globalDP macro n,i,s,c
+ globalD n,<i>,<s>,<c>
+ endm
+ else
+ globalDP macro n,i,s,c
+ globalW n,<i>,<s>,<c>
+ endm
+ endif
+
+endif
+
+
+ifndef ?NOSTATIC
+ .xcref
+ .xcref staticB,staticW,staticD,staticQ,staticT,staticCP,staticDP
+ .cref
+
+ ;; staticx - define static data of type x
+ ;;
+ ;; usage:
+ ;; staticx n,i,s
+ ;; where:
+ ;; x is the type of the variable b=byte, w=word, d=dword
+ ;; q=quad word, t=tenbytes, cp=code pointer, dp=data pointer
+ ;; n is the name to be given the variable.
+ ;; i is the initial value of the variable.
+ ;; s is the duplication factor
+ ;;
+ ;; statics do not generate an underscored version of the symbol
+ ;; since they are intended to be internal symbols. If they are
+ ;; required to be public, use globlax.
+
+
+ staticB macro n,i,s
+ ?ad <n>,1
+ ?dd n,0,<byte>,<db>,<i>,<s>,<PLM> ;;PLM to keep from generating _
+ endm
+
+ staticW macro n,i,s
+ ?ad <n>,2
+ ?dd n,0,<word>,<dw>,<i>,<s>,<PLM>
+ endm
+
+ staticD macro n,i,s
+ ?ad <n>,4
+ ?dd n,0,<dword>,<dd>,<i>,<s>,<PLM>
+ endm
+
+ staticQ macro n,i,s
+ ?ad <n>,8
+ ?dd n,0,<qword>,<dq>,<i>,<s>,<PLM>
+ endm
+
+ staticT macro n,i,s
+ ?ad <n>,10
+ ?dd n,0,<tbyte>,<dt>,<i>,<s>,<PLM>
+ endm
+
+ if sizec
+ staticCP macro n,i,s
+ staticD n,<i>,<s>
+ endm
+ else
+ staticCP macro n,i,s
+ staticW n,<i>,<s>
+ endm
+ endif
+
+ if sized
+ staticDP macro n,i,s
+ staticD n,<i>,<s>
+ endm
+ else
+ staticDP macro n,i,s
+ staticW n,<i>,<s>
+ endm
+ endif
+endif
+
+
+
+;; ?dd is the generalized data definition macro.
+;;
+;; format:
+;; ?dd n,p,t,d,i,s,c
+;; where:
+;; n is the name of the procedure
+;; p is the public flag
+;; t is the assembler type (byte, word, dword)
+;; d is the assembler directive (db,dw or dd)
+;; i is the initial value
+;; s is a duplication factor
+;; c is the convention, C for C, PLM or PSACAL for PL/M.
+;; The default (?PLM flag) will be used if not specified.
+
+
+?dd macro n,p,t,d,i,s,c
+ ?gcc ?dd2,%?PLM,<c> ;;Set calling convention
+ ife ?dd2 ;;If 'C'
+ n label t
+ ?dd1 _&n,p,<d>,<i>,<s> ;;Microsoft C uses leading underscores
+ else
+ ?dd1 n,p,<d>,<i>,<s> ;;If PL/M
+ endif
+endm
+
+
+
+;; ?dd1 is the generalized data definition macro.
+;;
+;; format:
+;; ?dd1 n,p,d,i,s
+;; where:
+;; n is the name of the procedure
+;; p is the public flag
+;; d is the assembler directive (db,dw or dd)
+;; i is the initial value
+;; s is a duplication factor
+
+
+?dd1 macro n,p,d,i,s
+ if p
+ public n
+ endif
+ ifb <s>
+ n d i
+ else
+ ifb <i>
+ n d s dup (?)
+ else
+ n d s dup (i)
+ endif
+ endif
+endm
+
+
+
+ifndef ?NOEXTERN
+ .xcref
+ .xcref ?ex1,?ex2,externB,externW,externD,externQ,externT
+ .xcref externNP,externFP,externP,externCP,externDP,externA
+ .cref
+ ?ex2 = 0
+
+ ;; externx - define external data of type x
+ ;;
+ ;; usage:
+ ;; externx n,c
+ ;; where:
+ ;; x is the type of the variable b=byte, w=word, d=dword
+ ;; q=quad word, t=tenbytes, cp=code pointer
+ ;; dp=data pointer, a=absolute
+ ;; n is a list of names to define
+ ;; c is the convention, C for C, PLM or PSACAL forPL/M.
+ ;; The default (?PLM flag) will be used if not specified.
+
+ externA macro n,c ;;40h is reserved for whatever will
+ ?ex1 <n>,40h,<abs>,<c>,<> ;; be done in the future for ASB
+ endm ;; externals
+
+ externB macro n,c
+ ?ex1 <n>,1,<byte>,<c>,<>
+ endm
+
+ externW macro n,c
+ ?ex1 <n>,2,<word>,<c>,<>
+ endm
+
+ externD macro n,c
+ ?ex1 <n>,4,<dword>,<c>,<>
+ endm
+
+ externQ macro n,c
+ ?ex1 <n>,8,<qword>,<c>,<>
+ endm
+
+ externT macro n,c
+ ?ex1 <n>,10,<tbyte>,<c>,<>
+ endm
+
+ externNP macro n,c
+ ?ex1 <n>,2,<near>,<c>,<cc>
+ endm
+
+ externFP macro n,c
+ ?ex1 <n>,4,<far>,<c>,<cc>
+ endm
+
+ if sizec
+ externP macro n,c
+ ?ex1 <n>,4,<far>,<c>,<cc>
+ endm
+ else
+ externP macro n,c
+ ?ex1 <n>,2,<near>,<c>,<cc>
+ endm
+ endif
+
+ if sizec
+ externCP macro n,c
+ ?ex1 <n>,4,<dword>,<c>,<>
+ endm
+ else
+ externCP macro n,c
+ ?ex1 <n>,2,<word>,<c>,<>
+ endm
+ endif
+
+ if sized
+ externDP macro n,c
+ ?ex1 <n>,4,<dword>,<c>,<>
+ endm
+ else
+ externDP macro n,c
+ ?ex1 <n>,2,<word>,<c>,<>
+ endm
+ endif
+
+
+
+ ;; ?ex1 is the generalized external definition macro
+ ;;
+ ;; format:
+ ;; ?ex1 n,s,d,c,scv
+ ;; where:
+ ;; n is are the names of the externals
+ ;; s is the size in bytes (used for typing)
+ ;; d is the type
+ ;; c is the convention, C for C, PLM or PSACAL for PL/M.
+ ;; The default (?PLM flag) will be used if not specified.
+ ;; scv save calling convention. If this field is "cc", then
+ ;; the calling convention will be saved in a ?CCn equ.
+
+ ?ex1 macro n,s,d,c,scv
+ ?gcc ?ex2,%?PLM,<c>
+ irp x,<n>
+ .xcref
+ .xcref ?t&&x
+ .cref
+ ?t&&x=s ;;save size info
+ ife ?ex2
+ extrn _&&x:&d
+ x equ _&&x
+ else
+ extrn x:&d
+ endif
+ ifidn <scv>,<cc> ;;save calling convention (C or PL/M)
+ .xcref ;; if NP, FP, or P
+ .xcref ?CC&&x
+ .cref
+ ?CC&&x=?ex2
+ endif
+ endm
+ endm
+endif
+
+
+
+ifndef ?NOLABEL
+ .xcref
+ .xcref ?lb1,?lblpu,?lb2
+ .xcref labelB,labelW,labelD,labelQ,labelT
+ .xcref labelNP,labelFP,labelP,labelCP,labelDP
+ .cref
+ ?lblpu = 0
+ ?lb2 = 0
+
+ ;; labelx - define label of data type x
+ ;;
+ ;; usage:
+ ;; labelx n,c
+ ;; where:
+ ;; x is the type of the variable b=byte, w=word, d=dword
+ ;; q=quad word, t=tenbytes, cp=code pointer, dp=data pointer
+ ;; n is a list of names to define, the first of which can
+ ;; be the keyword public
+ ;; c is the convention, C for C, PLM or PSACAL for PL/M.
+ ;; The default (?PLM flag) will be used if not specified.
+
+ labelB macro n,c
+ ?lb1 <n>,1,<byte>,<c>
+ endm
+
+ labelW macro n,c
+ ?lb1 <n>,2,<word>,<c>
+ endm
+
+ labelD macro n,c
+ ?lb1 <n>,4,<dword>,<c>
+ endm
+
+ labelQ macro n,c
+ ?lb1 <n>,8,<qword>,<c>
+ endm
+
+ labelT macro n,c
+ ?lb1 <n>,10,<tbyte>,<c>
+ endm
+
+ labelNP macro n,c
+ ?lb1 <n>,2,<near>,<c>
+ endm
+
+ labelFP macro n,c
+ ?lb1 <n>,4,<far>,<c>
+ endm
+
+ if sizec
+ labelP macro n,c
+ ?lb1 <n>,4,<far>,<c>
+ endm
+ else
+ labelP macro n,c
+ ?lb1 <n>,2,<near>,<c>
+ endm
+ endif
+
+ if sizec
+ labelCP macro n,c
+ ?lb1 <n>,4,<dword>,<c>
+ endm
+ else
+ labelCP macro n,c
+ ?lb1 <n>,2,<word>,<c>
+ endm
+ endif
+
+ if sized
+ labelDP macro n,c
+ ?lb1 <n>,4,<dword>,<c>
+ endm
+ else
+ labelDP macro n,c
+ ?lb1 <n>,2,<word>,<c>
+ endm
+ endif
+
+
+ ;; ?lb1 is the generalized label definition macro
+ ;;
+ ;; format:
+ ;; ?lb1 n,s,d
+ ;; where:
+ ;; n are the names of the labels
+ ;; s is the size in bytes (used for typing)
+ ;; d is the type
+ ;; c is the convention, C for C, PLM or PSACAL for PL/M.
+ ;; The default (?PLM flag) will be used if not specified.
+
+ ?lb1 macro n,s,d,c
+ ?gcc ?lb2,%?PLM,<c>
+ ?lblpu=0
+ irp x,<n>
+ ifidn <x>,<PUBLIC>
+ ?lblpu=1
+ else
+ .xcref
+ .xcref ?t&&x
+ .cref
+ ?t&&x=s ;;save size info
+ ife ?lb2 ;;If C
+ if ?lblpu
+ public _&&x
+ endif
+ _&&x label &d
+ x equ _&&x
+ else ;;If PL/M
+ if ?lblpu
+ public x
+ endif
+ x label &d
+ endif
+ endif
+ endm
+ endm
+endif
+
+
+
+ifndef ?NODEF
+
+ ;; defx - inform macros that name is of type x
+ ;;
+ ;; The given name(s) is flaged to be of the given type. This macro
+ ;; is intended for giving types to variables that were not generated
+ ;; by the macros (i.e., static storage). There must be a type definition
+ ;; for all parameters in a call list.
+ ;;
+ ;; usage:
+ ;; defx n
+ ;; where:
+ ;; x is the type of the variable b=byte, w=word, d=dword
+ ;; n is the name(s) to be given the variable(s).
+ ;;
+ ;; Bytes are considered to be two bytes long for alignment reasons
+
+ .xcref
+ .xcref defB,defW,defD,defQ,defT,defCP,defDP
+ .cref
+
+ defB macro n
+ ?ad <n>,1
+ endm
+
+ defW macro n
+ ?ad <n>,2
+ endm
+
+ defD macro n
+ ?ad <n>,4
+ endm
+
+ defQ macro n
+ ?ad <n>,8
+ endm
+
+ defT macro n
+ ?ad <n>,10
+ endm
+
+ if sizec
+ defCP macro n
+ defD <n>
+ endm
+ else
+ defCP macro n
+ defW <n>
+ endm
+ endif
+
+ if sized
+ defDP macro n
+ defD <n>
+ endm
+ else
+ defDP macro n
+ defW <n>
+ endm
+ endif
+endif
+
+
+
+; ?ad is the macro which creates a definition for the given
+; symbol
+;
+; usage:
+; ?ad <n>,s
+; where:
+; n is a list of names to define
+; s is the size info (1,2,4,8,10)
+
+
+?ad macro n,s
+ irp x,<n>
+ .xcref
+ .xcref ?t&&x
+ .cref
+ ?t&&x=s ;;save size info
+ endm
+endm
+
+
+
+ifndef ?NOPTR
+ .xcref
+ .xcref regPtr,farPtr
+ .cref
+
+ ;; regPtr generates information allowing a 32-bit pointer currently
+ ;; in a register to be pushed as a parameter to a subroutine using
+ ;; the cCall macro.
+ ;;
+ ;; usage:
+ ;; regptr n,s,o
+ ;; where:
+ ;; n is the name the argument will be known as
+ ;; s is the register containing the segment portion
+ ;; of the pointer
+ ;; o is the register containing the offset portion
+ ;; of the pointer
+ ;;
+ ;; 2/14/85 - made obsolete with farptr
+
+ regPtr macro n,s,o
+ farPtr n,s,o
+ endm
+
+
+
+ ;; farPtr generates information allowing a 32-bit pointer to be
+ ;; pushed as a parameter to a subroutine using the cCall macro.
+ ;;
+ ;; usage:
+ ;; farptr n,s,o
+ ;; where:
+ ;; n is the name the argument will be known as
+ ;; s is the segment portion of the pointer
+ ;; o is the offset portion of the pointer
+ ;;
+ ;; Note that any cast must have been made in the argument itself
+ ;; (i.e. regptr ptr1,ds,<word ptr 3[si]>)
+
+
+ farPtr macro n,s,o
+ .xcref
+ .xcref ?t&n
+ .cref
+ n &macro
+ push s ;@
+ push o ;@
+ &endm
+ ?t&n=80h
+ endm
+endif
+
+
+
+;; arg - declare argument
+;;
+;; The given argument(s) is added to the argument list structure
+;;
+;; format:
+;; arg a
+;;
+;; where:
+;; a is any valid argument to push.
+;;
+;; If any element in arglist has not been defined or isn't a 16-bit
+;; register, then a complete specification must have been given in a
+;; text equate and a defx also given (if not, you'll pay the penalty!)
+
+
+arg macro a
+ irp x,<a>
+ ?argc=?argc+1 ;;increment the arg count
+ ?atal <x>,%?argc ;;generate argument
+ endm
+endm
+
+
+
+;; ?atal (add to argument list) generates a macro that will cause
+;; the given argument to be processed when invoked. It is used by
+;; the arg macro only.
+
+
+?atal macro n,i
+ .xcref
+ .xcref ?ali&i
+ .cref
+ ?ali&i &macro
+ ?ap n
+ &endm
+endm
+
+
+
+;; ?ap - process arguments and place onto stack
+;;
+;; The given argument is processed (type checking) and place on
+;; the stack for a pending call. There must be a type definition
+;; for all arguments (except words). This can be done by using
+;; text equates and the defx macro.
+;;
+;; format:
+;; ?ap n
+;; where:
+;; n is the name of the argument to be pushed
+;;
+;; The variable ?argl is updated by the length of the arguments
+;; pushed so that the stack can be cleaned up after the call.
+
+
+?ap macro n
+ ?argl=?argl+2 ;;assume one word is pushed
+ ifdef ?t&n
+ ife ?t&n-1 ;;byte type
+ push word ptr (n) ;@
+ exitm
+ endif
+
+ ife ?t&n-2 ;;word type
+ push n ;@
+ exitm
+ endif
+
+ ife ?t&n-4 ;;dword type
+ push word ptr (n)[2] ;@
+ push word ptr (n) ;@
+ ?argl=?argl+2
+ exitm
+ endif
+
+ ife ?t&n-8 ;;qword type
+ push word ptr (n)[6] ;@
+ push word ptr (n)[4] ;@
+ push word ptr (n)[2] ;@
+ push word ptr (n) ;@
+ ?argl=?argl+6
+ exitm
+ endif
+
+ if ?t&n and 80h ;;far pointer type
+ n
+ ?argl=?argl+2
+ exitm
+ endif
+
+ ife ?t&n ;;variable storage
+ push word ptr (n) ;@
+ exitm
+ endif
+ endif
+
+ push n ;;unknown or register or immediate ;@
+endm
+
+
+
+;; cCall - call a 'c' language procedure
+;;
+;; The given procedure is called with the given parameters.
+;; If the calling convention is C, the arguments are pushed
+;; in reverse order, and removed after the called procedure
+;; returns. If the calling conventing is PL/M, the arguments
+;; are pushed as they were encountered, and the called procedure
+;; is assumed to have removed them from the stack.
+;;
+;; The calling convention priority will be:
+;; 1) that specified on the cCall if present
+;; 2) that defined by the target
+;; 3) the default (?PLM flag)
+;;
+;; format:
+;; ccall n,<a>,c
+;;
+;; where:
+;; n is the name of the procedure to call
+;; a are arguments to be pushed (optional, may be
+;; specified with the "arg" macro.
+;; c is the convention, C for C, PLM or PSACAL for PL/M.
+;; The default (?PLM flag) will be used if not specified.
+
+
+cCall macro n,a,c
+ ifnb <a> ;;add any arguments to list
+ arg <a>
+ endif
+ mpush %?rsl ;;save registers (if any)
+
+ ifdef ?CC&n ;;if calling convention has been
+ ?cCall1=?CC&n ;; defined for target, use it
+ else ;;else use the default
+ ?cCall1=?PLM
+ endif
+
+ ifnb <c> ;;If possible override, check it out
+ ?gcc ?cCall1,%?cCall1,<c>
+ endif
+
+ ?argl=0 ;;init argument length
+ ife ?cCall1 ;;if C calling convention
+ ?acb=?argc ;;initialize for looping
+ else
+ ?acb=1 ;;initialize for looping
+ endif
+
+ rept ?argc ;;push arguments and purge macros
+ uconcat <?ali>,%?acb
+ uconcat <purge>,,<?ali>,%?acb
+ ife ?cCall1 ;;if C calling convention
+ ?acb=?acb-1
+ else
+ ?acb=?acb+1
+ endif
+ endm
+ call n ;;call the procedure ;@
+ if ((?cCall1 eq 0) and (?argl ne 0)) ;;If C calling convention and arguments
+ add sp,?argl ;; then remove them ;@
+ endif
+ mpop %?rsl ;;pop all specified regs
+ ?rsl=0 ;;invalidate save list
+ ?argc= 0 ;; " arguments
+ ?argl= 0
+endm
+
+
+
+
+;; cProc - define a 'c' procedure
+;;
+;; cProc is the procedure definition for procedures.
+;;
+;; format:
+;; cProc n,cf,a
+;; where:
+;; n is the name of the procedure
+;;
+;; cf controls certain definitions, and may be:
+;; NEAR proc is to be a near label
+;; FAR proc is to be a far label
+;; PUBLIC proc is to be defined as public
+;; SMALL call makeframe procedure
+;; NODATA dont create prolog code to setup DS
+;; ATOMIC don't link stack if not needed
+;; NODATA must be specified for ATOMIC
+;; FORCEFRAME Force generation of a frame
+;; C proc is to be a C procedure
+;; PLM proc is to be a PL/M procedure
+;; PASCAL proc is to be a PL/M procedure
+;; WIN proc is to be a windows procedure
+;; NONWIN proc isn't to be a windows procedure
+;;
+;; a is a list of registers that are to be saved whenever
+;; the procedure is invoked.
+;;
+;; makeframe procedure: If small is specified, then
+;; the "makeframe procedure" is invoked instead of
+;; generating normal prologues/epilogues
+;;
+;; A call is performed to the makeframe procedure. The
+;; call is followed by two bytes. the first byte is the
+;; number of locals to allocate for the frame, the second
+;; is the number of bytes of parameters. The makeframe
+;; procedure will in turn call the cProc routine at the
+;; address following the data bytes. When the cProc is
+;; finished, it will do a near return to the makeframe
+;; procedure to clean up the frame and exit.
+;;
+;; Note that register parameters and makeframe are
+;; incompatible and cannot be used together.
+;;
+;; The makeframe procedure will save SI, DI, and also
+;; DS if a far procedure. These registers will be
+;; removed from the autosave list if specified.
+
+
+cProc macro n,cf,a
+ if ?cpd
+ ?utpe ;;generate unterminated proc error
+ endif
+
+ ?cpd=1 ;;a procdef is active now
+ ???=0 ;;no locals are defined yet
+ ?argc=0 ;;no arguments are defined
+ ?ba=0 ;;not in a procedure
+ ?po=0 ;;initial parameter offset
+ ?pu=0 ;;initial public setting
+ ?ia=0 ;;no special prolog/epilog
+ ?adj=4 ;;parameter adjustment (near ret+bp)
+ ?rp=0 ;;no register parameters
+ ?uf=0 ;;don't use makeframe
+ ?wfp=?WIN ;;default far procedure (win or not)
+ ?ff=0 ;;don't force frame setup
+ ?pas=0 ;;process register save list
+ ?pcc=?PLM ;;calling convention (C or PL/M)
+
+ ifnb <a> ;;register save list
+ ?ri ?pas,<a>
+ endif
+
+ ?pc=sizec ;;default size
+ ?nd=?nodata1 ;;default NODATA flag
+ ?nx=0 ;;default is not ATOMIC
+ irp x,<cf>
+ ifidn <x>,<FAR> ;;if far,
+ ?pc=1 ;; set far flag
+ endif
+ ifidn <x>,<NEAR> ;;if near,
+ ?pc=0 ;; set near flag
+ endif
+ ifidn <x>,<PUBLIC> ;;if public,
+ ?pu=1 ;; set public flag
+ endif
+ ifidn <x>,<SMALL> ;;if small
+ ?uf=1 ;; set small flag
+ endif
+ ifidn <x>,<DATA> ;;if data
+ ?nd=0 ;; reset NODATA flag
+ endif
+ ifidn <x>,<NODATA> ;;if NODATA
+ ?nd=1 ;; set NODATA flag
+ endif
+ ifidn <x>,<ATOMIC> ;;if ATOMIC
+ ?nx=1 ;; set ATOMIC flag
+ endif
+ ifidn <x>,<C> ;;if to force C calling convention
+ ?pcc=0 ;; set flag
+ endif
+ ifidn <x>,<PLM> ;;if to force PLM calling convention
+ ?pcc=1 ;; set flag
+ endif
+ ifidn <x>,<PASCAL> ;;if to force PLM calling convention
+ ?pcc=1 ;; set flag
+ endif
+ ifidn <x>,<WIN> ;;if to force a Window's frame
+ ?wfp=1 ;; set flag
+ endif
+ ifidn <x>,<NONWIN> ;;if not to be a Window's frame
+ ?wfp=0 ;; set flag
+ endif
+ endm
+
+ if ?pcc ;;If PLM
+ ?PLMPrevParm=0 ;; show no previous parameter
+ .xcref
+ .xcref ?PLMParm0
+ .cref
+ ?PLMParm0 &macro ;;Null macro to terminate
+ purge ?PLMParm0
+ &endm
+ endif
+
+ .xcref
+ .xcref ?CC&n
+ .cref
+ ?CC&n=?pcc ;;Save procedure type
+
+ if (?nx eq 1) and (?nd eq 0) ;;ATOMIC requires NODATA
+ ?nx = 0 ;;clear the ATOMIC keyword
+ ??error <ATOMIC specified without NODATA - ATOMIC ignored>
+ endif
+
+ if ?pc ;;if a far procedure
+ if ?wfp ;;if windows
+ ife ?nx ;;if not ATOMIC
+ ?ia=2 ;; adjust locals for saved ds
+ ?pas = ?pas and (not ?ds) ;;no need for extra save
+ endif
+ endif
+ ?adj=?adj+2 ;;far, make parameter adjustment
+ else
+ ?wfp=0 ;;not a far windows procedure
+ endif
+
+ if ?uf ;;don't save these if user frame
+ ?pas = ?pas and (not (?si+?di))
+ endif
+
+ ife ?pcc
+ ?pg <_&n>,%?pu,%?pc,%?pas,%?wfp,<n>,%?pcc
+ else
+ ?pg <n>,%?pu,%?pc,%?pas,%?wfp,<n>,%?pcc
+ endif
+endm
+
+
+
+
+;; ?pg - generate begin and nested macros for current procedure
+;;
+;; format:
+;; ?pg n,p,c,a,w,nnu,cc
+;; where:
+;; n is the name of the procedure
+;; p is the public flag
+;; c is the class definition for the procedure
+;; a is an enumerated list of registers to save
+;; at entry and restore at exit
+;; w true if a far windows procedure
+;; nnu procedure name without any underscore
+;; cc calling convention (C or PL/M)
+;;
+;;
+;; local stack allocation will be forced to an even byte count to
+;; maintain stack word alignment.
+
+
+?pg macro n,p,c,a,w,nnu,cc
+ .xcref
+ if ?uf ;;if user frame
+ if ?nd
+ ??error <NODATA encountered in &n - user frame ignored>
+ ?uf=0
+ endif
+ endif
+
+ .xcref cBegin
+ cBegin &macro g ;;generate cBegin macro
+ .xcref
+ if cc ;;Finish definition of parameters
+ uconcat <?PLMParm>,%?PLMPrevParm,%?po
+ endif
+
+ if ?uf ;;if user frame
+ if ?rp
+ ??error <parmR encountered in &n - user frame ignored>
+ ?uf=0
+ endif
+ endif
+ ?pg1 <n>,c,a,%?po,w,%?uf,%?nd,%?rp,cc ;;generate cEnd macro
+ ?cpd=0 ;;terminate current proc def
+ ?argc=0 ;;no arguments are defined yet
+ ?ba=1 ;;have reached a begin
+ ???=(???+1) and 0fffeh ;;word align local storage
+
+ if p ;;If to be public
+ public n
+ endif
+
+ ife c ;;declare procedure type
+ n proc near
+ else
+ n proc far
+ endif
+
+ ife cc ;;if 'C' calling convention
+ nnu equ n ;; generate label without underscore
+ endif
+
+ ifidn <g>,<nogen> ;;if nogen specified, shouldn't have
+ if ???+?po+a+?rp ;; parms, locals, or saved regs
+ ??_out <cBegin - possible invalid use of nogen>
+ endif
+ else ;;must generate a frame
+ if ?uf ;;if user frame code specified
+ ?mf c,%???,%?po ;; call user's makeframe
+ mpush a ;; save specified registers
+ else
+ if w ;;if a far windows procedure
+ ife ?nd ;;if not NODATA,
+ mov ax,ds ;; then set AX = currentds, and ;@
+ nop ;; leave room for MOV AX,1234h ;@
+ endif
+ ife ?nx ;;if not ATOMIC, far frame must be set
+ ife ?DOS5 ;;if not DOS5, then set far frame flag
+ inc bp ;; by incrementing the old bp ;@
+ endif
+ push bp ;@
+ mov bp,sp ;@
+ push ds ;@
+ else ;;ATOMIC procedure
+ if ?ff+???+?po+?rp ;;if any locals or parameters
+ push bp ;; then must set frame pointer ;@
+ mov bp,sp ;; to be able to access them ;@
+ endif
+ endif
+ ife ?nd ;;if not NODATA, then AX should
+ mov ds,ax ;; have the ds to use ;@
+ endif
+ else ;;not windows. use standard prolog
+ if ?ff+???+?po+?rp ;;if any locals or parameters
+ push bp ;; then must set frame pointer ;@
+ mov bp,sp ;; to be able to access them ;@
+ endif
+ endif
+
+ if ?rp ;;if parmR's, push them before
+ ?uf=0 ;; allocating locals and saving
+ rept ?rp ;; the autosave registers
+ uconcat mpush,,?rp,%?uf
+ ?uf=?uf+1
+ endm
+ endif
+
+ if ??? ;;if locals to allocate
+ if ?chkstk1 ;;if stack checking enabled
+ ifdef ?CHKSTKPROC ;;if user supplied stack checking
+ ?CHKSTKPROC %??? ;; invoke it with bytes requested
+ else
+ mov ax,??? ;;invoke default stack checking ;@
+ ife cc
+ call _chkstk ;@
+ else
+ call chkstk ;@
+ endif
+ endif
+ else ;;no stack checking
+ sub sp,??? ;; allocate any local storage ;@
+ endif
+ endif
+
+ mpush a ;;save autosave registers
+ endif
+
+ ifdef ?PROFILE ;;if profiling enabled
+ if c ;; and a far procedure
+ call StartNMeas ;; invoke profile start procedure ;@
+ endif
+ endif
+
+ endif
+
+ .cref
+ purge cBegin ;;remove the macro
+ &endm ;;end of cBegin macro
+
+ .xcref ?utpe
+ ?utpe &macro
+ ??error <unterminated procedure definition: "&n">
+ &endm
+ .cref
+endm ;;end of ?pg macro
+
+
+
+
+;; ?pg1 - generate end macro for current procedure
+;;
+;; format:
+;; ?pg1 n,c,a,o,w,f,d,r,cc
+;; where:
+;; n is the name of the procedure
+;; c is the class definition for the procedure
+;; a is an enumerated list of registers to save
+;; at entry and restore at exit
+;; o is the number of bytes of paramteres to remove at exit
+;; w true if a far windows procedure
+;; f is 1 if to use the user's makeframe procedure
+;; d is 1 if NODATA procedure
+;; r number of register parameters
+;; cc calling convention (C or PL/M)
+
+
+?pg1 macro n,c,a,o,w,f,d,r,cc
+ .xcref
+ .xcref cEnd
+ cEnd &macro g ;;start of cEnd macro
+ .xcref
+ ?ba=0 ;;no longer in a procedure
+ ifidn <g>,<nogen> ;;validate nogen usage
+ if o+a+r
+ ??_out <cEnd - possible invalid use of nogen>
+ endif
+ else ;;must generate an epilog
+ ifdef ?PROFILE ;;if profiling enabled
+ if c ;; and a far procedure
+ call StopNMeas ;; invoke profile stop procedure
+ endif ;; (doesn't trash DX:AX)
+ endif
+
+ mpop a ;;restore autosaved registers
+ if f ;;if to use the "makeframe" procedure
+ db 0c3h ;; near return to user's makeframe @
+ else
+ if w ;;if far win proc, use special epilog
+ ife ?nx ;;if not ATOMIC, bp was pushed
+ if (?TF eq 0) or (???+?rp) ;;if cannot assume valid sp
+ lea sp,-2[bp] ;; or locals or parmR's, get valid SP @
+ endif
+ pop ds ;;restore saved ds and bp @
+ pop bp ;; @
+ ife ?DOS5 ;;if not DOS5, bp was
+ dec bp ;; incremented to mark far frame @
+ endif
+ else ;;ATOMIC frame was set up
+ if (?TF eq 0) or (???+?rp) ;;if cannot assume valid sp
+ mov sp,bp ;; or locals or parmR's, get valid SP @
+ endif
+ if ???+?po+?rp
+ pop bp ;@
+ endif
+ endif
+
+ else ;;non-windows standard epilog
+ if ?ff+???+?po+?rp ;;if any parameters
+ if (?TF eq 0) or (???+?rp) ;;if cannot assume valid SP
+ mov sp,bp ;; or locals or parmR's, get valid SP;@
+ endif
+ pop bp ;@
+ endif
+ endif
+
+ ife cc ;;if C calling convention
+ ret ;; return ;@
+ else ;;else
+ ret o ;; return and remove paramteres ;@
+ endif
+ endif
+ endif
+ n endp ;;end of process
+ .cref
+ purge cEnd ;;remove the macro
+ &endm
+ .cref
+endm
+
+
+
+
+; assumes is a macro that will set up the assumes for a segment
+; or group created with the createSeg macro. If the assumed
+; value passed in isn't known, then a normal assume is made.
+;
+; usage:
+; assumes s,g
+;
+; where:
+; s is the register to make the assumption about
+; g is the value to assume is in it
+
+
+assumes macro s,ln
+ ifndef ln&_assumes
+ assume s:ln
+ else
+ ln&_assumes s
+ endif
+endm
+
+
+
+; createSeg is a macro that sets up a segment definition and
+; a logical name for that segment. The logical name can be
+; used to enter the segment, but it cannot be used for anything
+; else.
+;
+; usage:
+; createSeg n,ln,a,co,cl,grp
+; where:
+; n is the physical name of the segment
+; ln is the name it is to be invoked by
+; a is the alignment, and is optional
+; co is the combine type, and is optional
+; cl is the class, and is optional
+; grp is the name of the group that contains this segment
+
+
+createSeg macro n,ln,a,co,cl,grp
+ ifnb <grp>
+ addseg grp,n
+ else
+ ln&OFFSET equ offset n:
+ ln&BASE equ n
+ ?cs3 <ln>,<n>
+ endif
+ ifnb <cl>
+ n segment a co '&cl'
+ else
+ n segment a co
+ endif
+ n ends
+ ?cs1 <ln>,<n>
+endm
+
+
+addseg macro grp,seg
+ .xcref
+ .xcref grp&_def
+ .cref
+ ifndef grp&_def
+ grp&_def=0
+ endif
+ if grp&_def ne ASMpass
+ .xcref
+ .xcref grp&_add
+ .cref
+ grp&_add &macro s
+ grp&_in <seg>,s
+ &endm
+ .xcref
+ .xcref grp&_in
+ .cref
+ grp&_in &macro sl,s
+ ifb <s>
+ grp group sl
+ else
+ grp&_add &macro ns
+ grp&_in <sl,s>,ns
+ &endm
+ endif
+ &endm
+ grp&_def=ASMpass
+ else
+ grp&_add seg
+ endif
+endm
+
+
+defgrp macro grp,ln
+ addseg grp
+ ifnb <ln>
+ irp x,<ln>
+ ?cs3 <&x>,<grp>
+ x&&OFFSET equ offset grp:
+ x&&BASE equ grp
+ endm
+ endif
+endm
+
+
+?cs1 macro ln,n
+ .xcref
+ .xcref ln&_sbegin
+ .cref
+ ln&_sbegin &macro
+ .xcref
+ .xcref ?mf
+ .cref
+ ?mf &&macro c,l,p ;;when sBegin is invoked, generate
+ if c ;; the makeframe macro
+ extrn n&_FARFRAME:near ;; make frame for far procedures ;@
+ call n&_FARFRAME ;@
+ else
+ extrn n&_NEARFRAME:near ;; make frame for near procedures ;@
+ call n&_NEARFRAME ;@
+ endif
+ db l shr 1 ;;define number of locals ;@
+ db p shr 1 ;;define number of parameters ;@
+ &&endm
+ ?cs2 <ln>,<n>
+ n segment
+ &endm
+endm
+
+
+?cs2 macro ln,n
+ .xcref
+ .xcref sEnd
+ .cref
+ sEnd &macro
+ n ends
+ purge ?mf ;;delete the makeframe macro
+ purge sEnd
+ &endm
+endm
+
+
+?cs3 macro ln,n
+ .xcref
+ .xcref ln&_assumes
+ .cref
+ ln&_assumes &macro s
+ assume s:&n
+ &endm
+endm
+
+
+
+; sBegin is the macro that opens up the definition of a segment.
+; The segment must have already been defined with the createSeg
+; macro.
+;
+; usage:
+; sBegin ln
+;
+; where:
+; ln is the logical name given to the segment when
+; it was declared.
+
+.xcref
+.xcref sBegin
+.cref
+sBegin macro ln
+ ln&_sbegin
+endm
+
+
+
+ife ?DF
+
+ ; Define all segments that will be used. This will allow the
+ ; assume and groups to be set up at one given place, and also
+ ; allow quick changes to be made
+ ;
+ ; createSeg name,logname,align,combine,class,group
+
+ createSeg _TEXT,Code,word,public,CODE
+ ife ?nodata1
+ createSeg _DATA,Data,word,public,DATA,DGROUP
+ defgrp DGROUP,Data
+ endif
+
+ if ?chkstk1
+ ifndef ?CHKSTKPROC
+ externp <chkstk>
+ endif
+ endif
+endif
+
+
+
+; errnz exp - generate error message if expression isn't zero
+;
+; The errnz will generate an error message if the expression "exp"
+; does not evaluate to zero. This macro is very useful for testing
+; relationships between items, labels, and data that was coded into
+; an application.
+;
+; errnz <offset $ - offset label> ;error if not at "label"
+; errnz <eofflag and 00000001b> ;eofflag must be bit 0
+;
+; For expressions involving more than one token, the angle brackets
+; must be used.
+;
+; The macro is only evaluated on pass 2, so forward references may be
+; used in the expression.
+
+errnz macro x ;;display error if expression is <>0
+ if2
+ if x ;;if expression is non-zero,
+ errnz1 <x>,%(x)
+ endif
+ endif
+endm
+
+errnz1 macro x1,x2
+ = *errnz* x1 = x2
+ .err
+endm
+
+
+
+; errn$ label,exp - generate error message if label (exp) <> $
+;
+; The errnz will generate an error message if the label and "exp"
+; does not evaluate to the current value of the location counter.
+; This macro is very useful for testing relationships between
+; labels and the location counter that was coded into an application.
+;
+; examples: errn$ label ;error if not at "label"
+; errn$ label,+3 ;error if not three bytes from "label"
+; errn$ label,-3 ;error if not three bytes past "label"
+;
+; If no "exp" is given, it is the same as specifying 0
+;
+; The macro is only evaluated on pass 2, so forward references may be
+; used in the expression.
+
+errn$ macro l,x ;;error if <$-label1 (exp2)> <>0
+ errnz <offset $ - offset l x>
+endm
+
+
+
+;; If profile has been specified, declare the profile routines
+;; to be external and far. It would be best if this could be done
+;; when the call is actually made, but then the fix-up would be
+;; generated as segment-relative.
+
+
+ifdef ?PROFILE
+ externFP <StartNMeas,StopNMeas>
+endif
diff --git a/private/mvdm/dpmi/dosx.def b/private/mvdm/dpmi/dosx.def
new file mode 100644
index 000000000..1c3e13264
--- /dev/null
+++ b/private/mvdm/dpmi/dosx.def
@@ -0,0 +1,26 @@
+LIBRARY DosX
+
+DESCRIPTION 'Windows DOS Extender'
+
+EXETYPE WINDOWS
+
+SEGMENTS
+ DXCODE PRELOAD NONDISCARDABLE
+ DXPMCODE PRELOAD NONDISCARDABLE
+ DXDATA PRELOAD NONDISCARDABLE
+
+EXPORTS
+ __selDXCODE @1 NODATA
+ __selDXDGROUP @2 NODATA
+ __selDXPMCODE @3 NODATA
+ __selFirstLDT @10 NODATA
+ __selLDTAlias @11 NODATA
+
+
+IMPORTS
+
+ lmaROMToc = ROMWIN.1
+ lmaDXPMCODE = ROMWIN.100
+ lmaDXDGROUP = ROMWIN.101
+ cparDXDGROUP = ROMWIN.102
+ segRomDXCODE = ROMWIN.103
diff --git a/private/mvdm/dpmi/dosx.lnk b/private/mvdm/dpmi/dosx.lnk
new file mode 100644
index 000000000..f8a0f53da
--- /dev/null
+++ b/private/mvdm/dpmi/dosx.lnk
@@ -0,0 +1,21 @@
+dxstrt.obj +
+dxmain.obj +
+dxintr.obj +
+dxfunc.obj +
+dxutil.obj +
+dxmmgr.obj +
+dxint31.obj +
+dxdisk.obj +
+dxemm2.obj +
+dxdma.obj +
+dxnetbio.obj+
+dxoem.obj +
+dxrom.obj +
+dxbug.obj +
+dxend.obj +
+dxmsg.obj +
+dxendpm.obj +
+dxemm.obj +
+dxini.obj +
+dxfind.obj +
+dxboot.obj,
diff --git a/private/mvdm/dpmi/dosxi.lnk b/private/mvdm/dpmi/dosxi.lnk
new file mode 100644
index 000000000..c7efa5f26
--- /dev/null
+++ b/private/mvdm/dpmi/dosxi.lnk
@@ -0,0 +1,22 @@
+dxstrt.obj +
+dxmain.obj +
+dxintr.obj +
+dxfunc.obj +
+dxutil.obj +
+dxmmgr.obj +
+dxint31.obj +
+dxdisk.obj +
+dxemm2.obj +
+dxdma.obj +
+dxnetbio.obj+
+dxoem.obj +
+dxrom.obj +
+dxbug.obj +
+dxend.obj +
+dxmsg.obj +
+ntnpxem.obj +
+dxendpm.obj +
+dxemm.obj +
+dxini.obj +
+dxfind.obj +
+dxboot.obj,
diff --git a/private/mvdm/dpmi/dxboot.asm b/private/mvdm/dpmi/dxboot.asm
new file mode 100644
index 000000000..178d547ea
--- /dev/null
+++ b/private/mvdm/dpmi/dxboot.asm
@@ -0,0 +1,3307 @@
+ 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
diff --git a/private/mvdm/dpmi/dxbug.asm b/private/mvdm/dpmi/dxbug.asm
new file mode 100644
index 000000000..83e39c7d2
--- /dev/null
+++ b/private/mvdm/dpmi/dxbug.asm
@@ -0,0 +1,1007 @@
+ PAGE ,132
+ TITLE DXBUG.ASM -- Dos Extender Debug Services
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXBUG.ASM - Dos Extender Debug Services *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains protect mode debug services for the *
+;* DOS extender. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 11/17/89 Jimmat Added TraceBug stuff for debugging on *
+;* 286 machines. *
+;* 03/09/89 JimMat Initial version. *
+;* *
+;****************************************************************
+
+.286p
+.287
+
+ifndef DEBUG
+DEBUG = 0
+endif
+
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+
+if DEBUG ;********** ENTIRE FILE IS DEBUG CODE ***********
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+Debug_Serv_Int equ 41h ;WDEB386 service codes
+DS_Out_Char equ 0
+DS_Out_Symbol equ 0fh
+
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn fDebug:BYTE
+ extrn fTraceBug:WORD
+ extrn idCpuType:WORD
+
+DXDATA ends
+
+; -------------------------------------------------------
+ page
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; PMDebugInt -- Process (display) the pMode debugger interrupt calls
+;
+
+ public PMDebugInt
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+PMDebugInt proc near
+
+ cmp al,4Fh ;debugger installation check?
+ jnz notInsChk
+ mov ax,0F386h ;say we're here...
+ jmp DebugIntExit
+
+notInsChk:
+ cmp al,50h ;load segment call?
+ jnz notLoadSeg
+
+ push ax
+ mov ax,es
+ Trace_Out "Int 41h Load code ->@AX:DI<- ",x
+ pop ax
+ test si,1
+ jz LoadCode
+ Trace_Out "data ",x
+ jmp short @f
+LoadCode:
+ Trace_Out "code ",x
+@@:
+ Trace_Out "seg #BX sel #CX inst #DX"
+ jmp short DebugIntExit
+notLoadSeg:
+
+DebugIntExit:
+
+ iret
+
+PMDebugInt endp
+
+
+; -------------------------------------------------------
+; DXTestDebugIns -- Returns with Z flag set if running
+; under the debugger, NZ means not
+; under debugger.
+;
+; Input: none
+; Output: Z - no debugger, NZ - under debugger
+; Uses: Flags
+
+ public DXTestDebugIns
+
+DXTestDebugIns proc far
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+ push ds
+
+ push SEL_DXDATA ;address our data seg
+ pop ds
+ assume ds:DGROUP
+
+ cmp fDebug,0
+
+ pop ds
+ ret
+
+DXTestDebugIns endp
+
+DXCODE ends
+
+
+;******************************************************************************
+;
+; DXOutputDebugStr
+;
+; Basically stolen from Windows/386 code by Ralph Lipe -- hacked up for
+; 286 instead of 386. Here in RalphL's own words is the description:
+;
+; DESCRIPTION:
+; The following code is not pretty but it does what it needs to. It will
+; only be included in DEBUG versions of Win386. It accepts an ASCIIZ
+; string which it will output to the COM1 serial port. If the string
+; contains #(Register) (for example #AX) then the value of that register
+; will be output. It will not work for segment registers.
+;
+; If the string contains ?(Register)[:(Register)] (for example ?AX or
+; ?AX:BX) then the value of the register(s) is passed to the debugger
+; to display the label nearest to the given address. (It, also, will
+; not work with segment registers. If ?AX is given, then the segment is
+; assumed to be the DX code segment.
+;
+; ENTRY:
+; DS:SI -> ASCIIZ string
+;
+; EXIT:
+; All registers and flags trashed
+;
+; ASSUMES:
+; This procedure was called by the Trace_Out macro. It assumes that
+; the stack is a pushad followed by a FAR call to this procedure.
+;
+;------------------------------------------------------------------------------
+
+DXDATA segment
+
+Reg_Offset_Table LABEL WORD ; Order of PUSHA
+ dw "DI"
+ dw "SI"
+ dw "BP"
+ dw "SP"
+ dw "BX"
+ dw "DX"
+ dw "CX"
+ dw "AX"
+
+DXDATA ends
+
+DXCODE segment
+ assume cs:DXCODE
+
+ public DXOutDebugStr
+
+DXOutDebugStr proc far
+
+ push bp
+ mov bp, sp ; Assumes BP+6 = Pusha
+ pushf
+ push es
+
+ push SEL_DXDATA ; Address our own data seg
+ pop es
+ assume ds:NOTHING,es:DGROUP
+
+ cld
+ cli
+
+OSC1_Loop:
+ lodsb ; Get the next character
+ test al, al ; Q: End of string?
+ jz short OSC1_Done ; Y: Return
+ cmp al, "#" ; N: Q: Special register out?
+ je SHORT OSC1_Hex ; Y: Find out which one
+ cmp al, "?" ; Q: special lable out?
+ je short OSC1_Label ; Y: find out which one
+ cmp al, "@" ; Q: special string out?
+ je short OSC1_Str
+OSC1_out:
+ xor ah, ah ; N: Send char to COM
+ call Out_Debug_Chr
+ jmp OSC1_Loop ; Loop until done
+
+OSC1_Hex:
+ call Get_Register
+ jnc short OSC1_not_special
+
+ cmp bl, 2 ; Q: Word output?
+ jb SHORT OSC1_Out_Byte ; N: display byte
+OSC1_Out_Word:
+ call Out_Hex_Word ; Display AX in hex
+ jmp OSC1_Loop ; Output next char
+
+OSC1_Out_Byte:
+ xchg al, ah ; swap bytes to print just
+ call Out_Hex_Byte ; the low one!
+ jmp OSC1_Loop ; Output next char
+
+OSC1_Label:
+ call Get_Register
+ jc short show_label
+OSC1_not_special:
+ lodsb ; Get special char again
+ jmp OSC1_out ; display it, and continue
+
+show_label:
+ mov cx, ax ; save first value
+ cmp byte ptr [si], ':' ;Q: selector separator?
+ jne short flat_offset ; N:
+ lodsb ; Y: eat the ':'
+ call Get_Register ; and attempt to get the selector
+ jc short sel_offset
+flat_offset:
+ mov ax, cs ; default selector value
+sel_offset:
+ call Show_Near_Label
+ jmp OSC1_Loop
+
+OSC1_Str:
+ call Get_Register
+ jnc short OSC1_not_special
+ mov cx,ax
+ cmp byte ptr [si],':'
+ jne short no_selector
+ lodsb
+ push cx
+ call Get_Register
+ pop cx
+ xchg ax,cx
+ jc short got_sel_off
+ mov cx,ax
+no_selector:
+ mov ax,ds ; default selector for strings
+got_sel_off:
+ call Show_String
+ jmp OSC1_Loop
+
+OSC1_Done: ; The end
+
+ pop es
+ npopf
+ pop bp
+ ret 10h
+
+DXOutDebugStr endp
+
+
+;******************************************************************************
+;
+; Get_Register
+;
+; DESCRIPTION:
+;
+; ENTRY:
+;
+; EXIT: Carry set if register value found
+; AX = register value
+; BL = value size (1, 2, 4)
+;
+; USES:
+;
+;==============================================================================
+
+
+Get_Register proc near
+
+ push si ; Save string pointer
+ xor ax, ax ; Zero AX
+ mov bl, 2 ; BL = 2 (assume word output)
+ call Load_Up_Char ; Get 1st char
+ mov ah, al ; Move 1st char to AH
+ call Load_Up_Char ; Get second char
+ cmp al, 'L' ; Q: "L" (ie AL, BL, etc)?
+ jne short OSC1_WORD ; N: word reg
+ mov al, 'X' ; Y: change to X for pos match
+ mov bl, 1 ; BL = 1 -- byte output
+OSC1_WORD:
+ xor di, di ; DI = 0
+ mov cx, 8 ; Size of a pusha
+OSC1_Special_Loop:
+ push di
+ shl di,1
+ cmp ax, Reg_Offset_Table[di] ; Q: Is this the register?
+ pop di
+ je SHORT OSC1_Out_Reg ; Y: Output it
+ inc di ; N: Try the next one
+ loop OSC1_Special_Loop ; until CX = 0
+
+OSC1_Ignore_Special: ; OOPS! backup and ignore special
+ pop si ; Restore original string ptr
+ dec si ; back up to special char
+ clc
+ jmp short gr_exit
+
+OSC1_Out_Reg:
+ push di
+ shl di,1
+ mov ax, SS:[bp.6][di] ; AX = Value to output
+ pop di
+ add sp, 2 ; Trash pushed SI
+ cmp bl, 2 ;Q: byte or word value?
+ je short value_fnd ; jump if word value
+ xor ah, ah ; clear ah, if byte value
+value_fnd:
+ stc
+
+gr_exit:
+ ret
+
+Get_Register endp
+
+
+;******************************************************************************
+;
+; Load_Up_Char
+;
+; Moves the character at DS:SI into AL and converts it to an upper case
+; character. SI is incremented.
+;
+;------------------------------------------------------------------------------
+
+Load_Up_Char proc near
+
+ lodsb
+ cmp al, "Z"
+ jb SHORT LUC_Done
+ sub al, "a" - "A"
+LUC_Done:
+ ret
+
+Load_Up_Char endp
+
+
+;******************************************************************************
+;
+; Out_Hex_Word
+;
+; Outputs the value in AX to the COM port in hexadecimal.
+;
+;------------------------------------------------------------------------------
+
+Out_Hex_Word proc near
+
+ rol ax, 4
+ call Out_Hex_Char
+ rol ax, 4
+ call Out_Hex_Char
+Out_Hex_Byte:
+ rol ax, 4
+ call Out_Hex_Char
+ rol ax, 4
+ call Out_Hex_Char
+
+ ret
+
+Out_Hex_Word endp
+
+
+;******************************************************************************
+;
+; Out_Hex_Char
+;
+; Outputs the low nibble in AL to the COM port.
+;
+;------------------------------------------------------------------------------
+
+DXDATA segment
+
+Hex_Char_Tab LABEL BYTE
+ db "0123456789ABCDEF"
+
+DXDATA ends
+
+Out_Hex_Char proc near
+
+ push ax
+ push bx
+ mov bx, ax
+ and bx, 1111b
+ mov al, Hex_Char_Tab[bx]
+ call Out_Debug_Chr
+ pop bx
+ pop ax
+ ret
+
+Out_Hex_Char endp
+
+
+;******************************************************************************
+;
+; Out_Debug_Chr
+;
+; DESCRIPTION:
+;
+; ENTRY:
+; AL contains character to output
+;
+; EXIT:
+;
+; USES:
+; Nothing
+;
+;==============================================================================
+
+Out_Debug_Chr proc near
+ assume ds:nothing,es:DGROUP
+
+ push ax
+ mov ax,cs
+ cmp ax,SEL_DXCODE0 ; Are we in real mode?
+ pop ax
+ jne out_com
+
+ cmp fDebug,0 ; debugger installed?
+ je out_com ; N: go output it ourselves
+
+ push ax
+ push dx
+ mov dl, al
+ mov ax, DS_Out_Char
+ int Debug_Serv_Int
+ pop dx
+ pop ax
+ ret
+
+Out_Debug_Chr endp
+
+
+;******************************************************************************
+;
+; out_com
+;
+; DESCRIPTION: Reprograms COM and outputs a character (stolen from DEB386).
+;
+; ENTRY: AL character to output
+;
+; EXIT:
+;
+; USES:
+;
+;==============================================================================
+
+
+UR_DAT equ 0000h ; Data port
+UR_IEN equ 0001h ; Interrupt enable
+UR_IER equ 0002h ; interrupt ID
+UR_LCR equ 0003h ; line control registers
+UR_MCR equ 0004h ; modem control register
+UR_LSR equ 0005h ; line status register
+UR_MSR equ 0006h ; modem status regiser
+UR_DLL equ 0000h ; divisor latch least sig
+UR_DLM equ 0001h ; divisor latch most sig
+
+ public out_com
+
+out_com proc near
+
+ push dx
+ push ax
+
+ push ds ;get first com port address from BIOS area
+ mov ax,40h
+ mov ds,ax
+ mov dx,ds:[0]
+ pop ds
+
+ or dx,dx ;forget it if no comm port
+ jnz @f
+
+ pop ax
+ pop dx
+ ret
+@@:
+
+;
+; Initialize the port on EVERY CHARACTER just to make sure
+;
+ add dx, UR_LCR
+ mov al, 80h
+ out dx, al
+ IO_Delay
+ sub al, al
+ add dx, UR_DLM - UR_LCR
+ out dx, al
+ IO_Delay
+ add dx, UR_DLL - UR_DLM
+ mov al, 12
+ out dx, al
+ IO_Delay
+ mov al, 3
+ add dx, UR_LCR - UR_DLL
+ out dx, al
+ IO_Delay
+ add dx, UR_LSR - UR_LCR
+
+Wait_Til_Ready:
+ in al, dx
+ IO_Delay
+ test al, 020h
+ jz Wait_Til_Ready
+;
+; Crank it out!
+;
+ add dx, UR_DAT - UR_LSR
+ pop ax
+ out dx, al
+ IO_Delay
+ pop dx
+
+ ret
+
+out_com endp
+
+
+;******************************************************************************
+;
+; Show_Near_Label
+;
+; DESCRIPTION: call the debugger to display a label less than or equal
+; to the given address
+;
+; ENTRY: AX is selector, CX is offset of address to try to find
+; a symbol for
+; ES selector to DOSX data segment
+; EXIT:
+;
+; USES:
+;
+;==============================================================================
+
+Show_Near_Label proc near
+
+ push ax
+ mov ax,cs
+ cmp ax,SEL_DXCODE0 ; Are we in real mode?
+ pop ax
+ jne Show_Near_Label_ret
+
+ cmp es:idCpuType,3 ;use 32 bit regs?
+ jae debug_386
+
+ push ax ;on a 286, use 16 bit regs
+ push bx
+ push cx
+ mov bx,cx
+ mov cx,ax
+ mov ax,DS_Out_Symbol
+ int Debug_Serv_Int
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+debug_386:
+ .386p
+ push eax
+ push ebx
+ push ecx
+
+ mov bx, cx
+
+ movzx ecx, ax ;WDEB386 wants a 32 bit offset
+
+ mov ax, DS_Out_Symbol
+ int Debug_Serv_Int
+
+ pop ecx
+ pop ebx
+ pop eax
+ ret
+
+ .286p
+
+Show_Near_Label_ret:
+
+Show_Near_Label endp
+
+
+;******************************************************************************
+;
+; Show_String
+;
+; DESCRIPTION: Display an asciiz string
+;
+; ENTRY: AX is selector, CX is offset of address to find string
+;
+; EXIT:
+;
+; USES:
+;
+;==============================================================================
+
+Show_String proc near
+
+ push ax
+ push ds
+ push si
+
+ mov ds,ax
+ mov si,cx
+ xor ax,ax
+@@:
+ lodsb
+ or al,al
+ jz @f
+ call Out_Debug_Chr
+ jmp short @b
+@@:
+ pop si
+ pop ds
+ pop ax
+
+ ret
+
+Show_String endp
+
+
+DXCODE ends
+
+endif ; DEBUG
+
+
+if NTDEBUG
+;******************************************************************************
+;
+; The following macros and routines implement DOSX's internal trace tables.
+;
+;==============================================================================
+
+ifdef WOW_x86
+
+SAVE_STATE_PM MACRO
+ push ebp
+ mov ebp, esp
+ pushfd
+ pushad
+ push ss
+ push es
+ push ds
+ push cs
+
+ mov ax, ss
+ lar eax,eax
+ test eax,(AB_BIG SHL 8)
+ jnz @f
+ movzx ebp,bp
+@@:
+ENDM
+RESTORE_STATE_PM MACRO
+ add esp, 2
+ pop ds
+ pop es
+ pop ss
+ popad
+ popfd
+ pop ebp
+ENDM
+
+SAVE_STATE_V86 MACRO
+ push ebp
+ mov ebp, esp
+ pushfd
+ pushad
+ push ss
+ push es
+ push ds
+ push cs
+ENDM
+RESTORE_STATE_V86 MACRO
+ add esp, 2
+ pop ds
+ pop es
+ pop ss
+ popad
+ popfd
+ pop ebp
+ENDM
+
+TRACEDS_PM MACRO
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds, ax
+ENDM
+
+TRACEDS_V86 MACRO
+ mov ds,selDgroup
+ENDM
+
+DEBUG_TRACE_PROC MACRO mode
+ local dbgtr_exit
+
+ .386p
+ SAVE_STATE_&mode
+
+ TRACEDS_&mode
+ assume ds:DGROUP
+
+ test fDebugTrace, 0ffh
+ jnz short @f
+ jmp dbgtr_exit ;just return
+@@:
+;------------------------------------------------------------------------------
+; Update DebugTraceBuffer
+;------------------------------------------------------------------------------
+ mov di, DebugTracePointer
+ add di, TRACE_ENTRY_SIZE
+ cmp di, OFFSET DebugTraceBuffer + TRACE_ENTRY_SIZE*(NUM_TRACE_ENTRIES-1)
+ jbe short @f
+ mov di, OFFSET DebugTraceBuffer
+@@:
+ mov DebugTracePointer, di
+ movzx edi, di
+
+ push edi
+ push ds
+
+ push ds
+ pop es
+ push ss
+ pop ds
+ mov ecx, 8 ;four parameters on stack
+ mov esi, ebp ;point to input parameters
+ add esi, 6
+ cld
+ rep movsb ;move them to debugtracebuffer
+ pop ds
+ pop edi
+
+ mov ax, DebugTraceCounter
+ mov word ptr es:[di+8], ax
+ inc DebugTraceCounter
+
+ push ds
+ mov ax, SEL_VDMTIB or STD_RING
+ mov ds, ax
+ mov ax, word ptr ds:[0]
+ mov word ptr es:[di+10], ax
+ pop ds
+
+ mov ax, word ptr ss:[ebp+4]
+ mov word ptr es:[di+12], ax
+
+;------------------------------------------------------------------------------
+; Update RegisterTraceBuffer
+;------------------------------------------------------------------------------
+ mov ax, RegisterTracePointer
+ add ax, REGTRACE_ENTRY_SIZE
+ cmp ax, OFFSET RegisterTraceBuffer + REGTRACE_ENTRY_SIZE*(NUM_TRACE_ENTRIES-1)
+ jbe short @f
+ mov ax, OFFSET RegisterTraceBuffer
+@@:
+ mov RegisterTracePointer, ax
+ mov word ptr es:[di+14], ax ;save pointer to register buff
+ mov di, ax
+
+
+ push ds
+ pop es
+ push ss
+ pop ds
+ mov ecx, REGTRACE_ENTRY_SIZE
+ mov esi, ebp
+ sub esi, 44 ;point to saved register values
+ cld
+ rep movsb
+
+dbgtr_exit:
+ RESTORE_STATE_&mode
+ ENDM
+
+else
+
+SAVE_STATE_PM MACRO
+ push bp
+ mov bp, sp
+ pushf
+ pusha
+ push ss
+ push es
+ push ds
+ push cs
+ENDM
+RESTORE_STATE_PM MACRO
+ add sp, 2
+ pop ds
+ pop es
+ pop ss
+ popa
+ popf
+ pop bp
+ENDM
+
+SAVE_STATE_V86 MACRO
+ push bp
+ mov bp, sp
+ pushf
+ pusha
+ push ss
+ push es
+ push ds
+ push cs
+ENDM
+RESTORE_STATE_V86 MACRO
+ add sp, 2
+ pop ds
+ pop es
+ pop ss
+ popa
+ popf
+ pop bp
+ENDM
+
+DEBUG_TRACE_PROC MACRO mode
+ local dbgtr_exit
+
+ .286p
+
+ SAVE_STATE_&mode
+
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds, ax
+ assume ds:DXDATA
+
+ test fDebugTrace, 0ffh
+ jnz short @f
+ jmp dbgtr_exit ;just return
+@@:
+;------------------------------------------------------------------------------
+; Update DebugTraceBuffer
+;------------------------------------------------------------------------------
+ mov di, DebugTracePointer
+ add di, TRACE_ENTRY_SIZE
+ cmp di, OFFSET DebugTraceBuffer + TRACE_ENTRY_SIZE*(NUM_TRACE_ENTRIES-1)
+ jbe short @f
+ mov di, OFFSET DebugTraceBuffer
+@@:
+ mov DebugTracePointer, di
+
+ push di
+ push ds
+
+ push ds
+ pop es
+ push ss
+ pop ds
+ mov cx, 8 ;four parameters on stack
+ mov si, bp ;point to input parameters
+ add si, 6
+ cld
+ rep movsb ;move them to debugtracebuffer
+ pop ds
+ pop di
+
+ mov ax, DebugTraceCounter
+ mov word ptr es:[di+8], ax
+ inc DebugTraceCounter
+
+ mov word ptr es:[di+10], 0
+ mov ax, word ptr ss:[bp+4]
+ mov word ptr es:[di+12], ax
+
+;------------------------------------------------------------------------------
+; Update RegisterTraceBuffer
+;------------------------------------------------------------------------------
+ mov ax, RegisterTracePointer
+ add ax, REGTRACE_ENTRY_SIZE
+ cmp ax, OFFSET RegisterTraceBuffer + REGTRACE_ENTRY_SIZE*(NUM_TRACE_ENTRIES-1)
+ jbe short @f
+ mov ax, OFFSET RegisterTraceBuffer
+@@:
+ mov RegisterTracePointer, ax
+ mov word ptr es:[di+14], ax ;save pointer to register buff
+ mov di, ax
+
+
+ push ds
+ pop es
+ push ss
+ pop ds
+ mov cx, REGTRACE_ENTRY_SIZE
+ mov si, bp
+ sub si, 44 ;point to saved register values
+ cld
+ rep movsb
+
+dbgtr_exit:
+ RESTORE_STATE_&mode
+ ENDM
+
+endif ; WOW_x86
+
+DXDATA segment
+
+NUM_TRACE_PARAMETERS equ 4 ;# of pass parameters to proc
+NUM_TRACE_ENTRIES equ 256
+TRACE_ENTRY_SIZE equ 2*8
+REGTRACE_ENTRY_SIZE equ 12*4
+
+ public DebugTraceBuffer, DebugTracePointer
+ public RegisterTraceBuffer, RegisterTracePointer
+ ALIGN 16
+DebugTraceBuffer db TRACE_ENTRY_SIZE*NUM_TRACE_ENTRIES dup (?)
+RegisterTraceBuffer db REGTRACE_ENTRY_SIZE*NUM_TRACE_ENTRIES dup (?)
+
+DebugTracePointer dw OFFSET DebugTraceBuffer
+DebugTraceCounter dw 0
+RegisterTracePointer dw OFFSET RegisterTraceBuffer
+DXDATA ends
+
+ extrn pbReflStack:WORD
+ extrn fDebugTrace:BYTE
+
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+;******************************************************************************
+;
+; DebugTrace
+;
+; DESCRIPTION:
+;
+; ENTRY:
+;
+; EXIT:
+;
+; USES:
+;
+;==============================================================================
+
+ public DebugTrace
+DebugTrace proc near
+
+ DEBUG_TRACE_PROC PM
+ ret 2*NUM_TRACE_PARAMETERS
+
+DebugTrace endp
+
+DXPMCODE ends
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn selDgroup:WORD
+ENDIF
+
+ assume cs:DXCODE
+;******************************************************************************
+;
+; Stack_Trace
+;
+; DESCRIPTION:
+;
+; ENTRY:
+;
+; EXIT:
+;
+; USES:
+;
+;==============================================================================
+
+ public DebugTraceRm
+DebugTraceRm proc near
+
+ DEBUG_TRACE_PROC V86
+ ret 2*NUM_TRACE_PARAMETERS
+
+DebugTraceRm endp
+
+DXCODE ends
+
+endif ; NTDEBUG
+ end
diff --git a/private/mvdm/dpmi/dxdisk.asm b/private/mvdm/dpmi/dxdisk.asm
new file mode 100644
index 000000000..c8722c366
--- /dev/null
+++ b/private/mvdm/dpmi/dxdisk.asm
@@ -0,0 +1,953 @@
+ PAGE ,132
+ TITLE DXDISK.ASM -- Dos Extender Low Level Disk Interface
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXDISK.ASM -- Dos Extender Low Level Disk Interface
+;
+;-----------------------------------------------------------------------
+;
+; This module provides the 286 DOS extender's low level protected-to-
+; real mode disk interface. It supports a subset of the BIOS Int 13h
+; and DOS Int 25h/26h services.
+;
+;-----------------------------------------------------------------------
+;
+; 05/22/89 jimmat Original version
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include interupt.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn EnterIntHandler:NEAR
+ extrn LeaveIntHandler:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn GetSegmentAddress:NEAR
+ extrn SetSegmentAddress:NEAR
+externFP NSetSegmentDscr
+ extrn FreeSelector:NEAR
+ extrn AllocateSelector:NEAR
+ extrn ParaToLDTSelector:NEAR
+
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn rgbXfrBuf0:BYTE
+ extrn rgbXfrBuf1:BYTE
+ extrn rglpfnRmISR:DWORD
+
+cbSectorSize dw ? ;sector size for target drive
+cSectorsTransfered dw ? ;# sectors transfered so far
+cSectorsToTransfer dw ? ;# sectors to read/write
+cSectorsPerTransfer dw ? ;# sectors to R/W at a time
+cSectorsThisTransfer dw ? ;# sectors to R/W this time
+lpSectorData dd ? ;far pointer to caller's buffer
+
+lpRmISR dd ? ;real mode int service rtn to invoke
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+IFNDEF ROM
+ extrn segDXDataPM:WORD
+ENDIF
+
+DXPMCODE ends
+
+
+; -------------------------------------------------------
+ subttl INT 13h Mapping Services
+ page
+; -------------------------------------------------------
+; INT 13h MAPPING SERVICES
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntr13 -- Service routine for the Protect Mode INT 13h
+; interface to the real mode BIOS.
+;
+; Input: Various registers
+; Output: Various registers
+; Errors:
+; Uses: All registers preserved, other than return values
+;
+; Currently, the following Int 13h services are supported:
+;
+; ah= 0 - Reset Disk System (no mapping required)
+; 1 - Get Disk System Status (no mapping required)
+; 2 - Read Sector (mapping required)
+; 3 - Write Sector (mapping required)
+; 4 - Verify Sector (mapping required)
+; 5 - Fromat Track (mapping required)
+; 6 - Format Bad Track (no mapping required)
+; 7 - Format Drive (no mapping required)
+; 8 - Get Drive Parameters (mapping required)
+; 9 - Init Fixed Disk Characteristics (no mapping required)
+; C - Seek (no mapping required)
+; D - Reset Disk System (no mapping required)
+; 10 - Get Drive Status (no mapping required)
+; 11 - Recalibrate Drive (no mapping required)
+; 12 - Controller RAM Diagnostic (no mapping required)
+; 13 - Controller Drive Diagnostic (no mapping required)
+; 14 - Controller Internal Diagnostic (no mapping required)
+; 15 - Get Disk Type (no mapping required)
+; 16 - Get Disk Change Status (no mapping required)
+; 17 - Set Disk Type (no mapping required)
+; 18 - Set Media Type for Format (mapping required)
+; 19 - Park Heads (no mapping required)
+;
+; Functions not listed above will most likely not work properly!
+;
+; NOTE: several functions take 2 bits of the cylinder number in CL
+; if the operation is on a fixed disk. The code currently does
+; not account for these bits, and may not work properly if
+; the request must be split into smaller operations for real/
+; extended memory buffering.
+;
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr13
+
+PMIntr13 proc near
+
+ cld ;cya...
+
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:DGROUP ; also sets up addressability
+
+ FSTI ;allow HW interrupts
+
+ call IntEntry13 ;perform translations/buffering
+
+; Execute the real mode BIOS routine
+
+ push es
+ assume es:nothing
+ mov ax,SEL_RMIVT OR STD_RING
+ mov es,ax
+ mov ax,word ptr es:[4*13h] ;move real mode Int 13h
+ mov word ptr [bp].lParam,ax ; handler address to
+ mov ax,word ptr es:[4*13h+2]; lParam on stack frame
+ mov word ptr [bp].lParam+2,ax
+ pop es
+ assume es:DGROUP
+
+ mov ah,13h ;wParam1 = int #, function
+ mov al,byte ptr [bp].intUserAX+1
+ mov [bp].wParam1,ax
+
+ cmp al,02 ;call special read/write routine
+ jb i13_not_rw ; if this is a read/write sectors
+ cmp al,03 ; request
+ ja i13_not_rw
+
+ call ReadWriteSectors ;common Int 13h/25h/26h read/write code
+ jmp short i13_done
+
+i13_not_rw:
+ SwitchToRealMode ;otherwise, do the service ourself
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING,ss:DGROUP
+ popa
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset i13_10)
+ mov ax,es:[13h*4]
+ mov [bp + 2],ax
+ mov ax,es:[13h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+i13_10: pushf
+ FCLI
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+
+ FSTI ;allow HW interrupts
+
+; Perform fixups on the return register values.
+
+i13_done:
+ mov ax,[bp].pmUserAX ;get original function code
+ call IntExit13
+
+ FCLI ;LeaveIntHandler requires ints off
+ call LeaveIntHandler ;restore caller's registers, stack
+ assume ds:NOTHING,es:NOTHING
+
+ riret
+
+PMIntr13 endp
+
+
+; -------------------------------------------------------
+; IntEntry13 -- This routine performs translations and
+; buffering of Int 13h requests on entry.
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntEntry13
+
+IntEntry13 proc near
+
+ cmp ah,02 ;Read sectors?
+ jb @f
+ cmp ah,03 ;Write sectors?
+ ja @f
+
+ mov [bp].intUserBX,offset DGROUP:rgbXfrBuf1 ;use DOSX buffer
+ ret
+@@:
+ cmp ah,04h ;Verify sectors?
+ jnz @f
+
+ mov [bp].intUserES,0F000h ;older versions of verify need a buff,
+ mov [bp].intUserBX,0 ; we just point them at the BIOS!
+ ret
+@@:
+ cmp ah,05h ;Format track?
+ jnz @f
+
+ mov si,bx ;es:bx -> 512 byte buffer to copy down
+ mov di,offset DGROUP:rgbXfrBuf1
+ mov [bp].intUserBX,di
+ mov ds,[bp].pmUserES
+ mov cx,256 ;might be good to check segment limit
+ cld ; on callers source!
+ rep movsw
+
+ push es
+ pop ds
+
+ ret
+@@:
+
+ ret
+
+IntEntry13 endp
+
+
+; -------------------------------------------------------
+; IntExit13 -- This routine performs translations and
+; buffering of Int 13h requests on exit.
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntExit13
+
+IntExit13 proc near
+
+; Functions 02h (Read sectors) and 03h (Write sectors) return a count of
+; sectors transfered in AL. Since we may break the transfer up into a
+; number of transfers, we have to return the total # that we transfered,
+; not the number of the last bios request.
+
+ cmp ah,02h
+ jb @f
+ cmp ah,03h
+ ja @f
+
+ mov al,byte ptr cSectorsTransfered
+ mov byte ptr [bp].intUserAX,al
+@@:
+
+; Functions 02h (Read sectors), 03h (Write sectors), 04h (Verify sectors),
+; and 05h (Format track) need to have the caller's value of bx restored.
+
+ cmp ah,02h ;Read sectors?
+ jb @f
+ cmp ah,05 ;Format track?
+ ja @f
+
+ mov ax,[bp].pmUserBX ;restore caller's BX value
+ mov [bp].intUserBX,ax
+ ret
+@@:
+
+; Functions 08h (Get Drive Parameters) and 18h (Set Drive Type for Format)
+; return a pointer in ES:DI. Map the segment in ES to a selector
+
+ cmp ah,08h ;Get Drive Parameters
+ jz i13_map_es
+ cmp ah,18h
+ jnz @f
+
+i13_map_es:
+ test byte ptr [bp].intUserFL,1 ;don't bother to map ES if
+ jnz @f ; function failed (carry set)
+
+i13_do_it:
+ mov ax,[bp].intUserES ;returns a pointer in ES:DI, get
+ mov bx,STD_DATA ; a selector for it
+ call ParaToLDTSelector
+ mov [bp].pmUserES,ax
+ ret
+@@:
+
+ ret
+
+IntExit13 endp
+
+
+; -------------------------------------------------------
+ subttl INT 25h/26h Absolute Disk Read/Write
+ page
+; -------------------------------------------------------
+; INT 25h/26h ABSOLUTE DISK READ/WRITE
+; -------------------------------------------------------
+; PMIntr25 -- This routine provides the protected-to-real
+; mode mapping for Int 25h (Absolute Disk Read)
+;
+; In: al - drive # (0 = A, 1 = B, ...)
+; cx - # of sectors to read
+; dx - starting sector #
+; ds:bx - selector:offset of buffer
+;
+; -- or --
+;
+; al - drive #
+; cx - -1
+; ds:bx - pointer to 5 word parameter block
+;
+; Out: if successful, carry clear
+; if unsuccessful, carry set and
+; ax - error code
+
+ assume ds:DGROUP,es:DGROUP
+ public PMIntr25
+
+PMIntr25 proc near
+
+ cld ;cya...
+
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:DGROUP ; also sets up addressability
+
+ FSTI ;allow HW interrupts
+
+ mov ah,25h
+ call IntEntry2X ;perform translations/buffering
+
+; Do the read
+
+ push es
+ mov ax,SEL_RMIVT OR STD_RING
+ mov es,ax
+ assume es:nothing
+ mov ax,word ptr es:[4*25h] ;move real mode Int 25h
+ mov word ptr [bp].lParam,ax ; handler address to
+ mov ax,word ptr es:[4*25h+2]; lParam on stack frame
+ mov word ptr [bp].lParam+2,ax
+ pop es
+ assume es:DGROUP
+
+ mov ah,25h ;wParam1 = int #
+ mov [bp].wParam1,ax
+
+ call ReadWriteSectors ;common Int 13h/25h/26h read/write code
+
+; Perform fixups on the return register values.
+
+ mov ah,25h
+ call IntExit2X ;perform translations/buffering
+
+ FCLI
+ call LeaveIntHandler ;restore caller's registers, stack
+ assume ds:NOTHING,es:NOTHING
+
+; Int 25 & 26 leave the caller's flags on the stack, but we want to return
+; with the flags returned by the real mode ISR (which LeaveIntHandler has
+; incorporated into the caller's flags), so make a copy of the flags and
+; pop them into the flags register before returning.
+
+ push ax
+ push bp
+ mov bp,sp ;bp -> BP AX IP CS FL
+ mov ax,[bp+8]
+ xchg ax,[bp+2] ;bp -> BP FL IP CS FL
+ pop bp
+ npopf
+
+ retf
+
+PMIntr25 endp
+
+
+; -------------------------------------------------------
+; PMIntr26 -- This routine provides the protected-to-real
+; mode mapping for Int 26h (Absolute Disk Write)
+;
+; In: al - drive # (0 = A, 1 = B, ...)
+; cx - # of sectors to write
+; dx - starting sector #
+; ds:bx - selector:offset of buffer
+;
+; -- or --
+;
+; al - drive #
+; cx - -1
+; ds:bx - pointer to 5 word parameter block
+;
+; Out: if successful, carry clear
+; if unsuccessful, carry set and
+; ax - error code
+
+ assume ds:DGROUP,es:DGROUP
+ public PMIntr26
+
+PMIntr26 proc near
+
+ cld ;cya...
+
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:DGROUP ; also sets up addressability
+
+ FSTI ;allow HW interrupts
+
+ mov ah,26h
+ call IntEntry2X ;perform translations/buffering
+
+; Do the write
+
+ push es
+ mov ax,SEL_RMIVT OR STD_RING
+ mov es,ax
+ assume es:nothing
+ mov ax,word ptr es:[4*26h] ;move real mode Int 25h
+ mov word ptr [bp].lParam,ax ; handler address to
+ mov ax,word ptr es:[4*26h+2]; lParam on stack frame
+ mov word ptr [bp].lParam+2,ax
+ pop es
+ assume es:DGROUP
+
+ mov ah,26h ;wParam1 = int #
+ mov [bp].wParam1,ax
+
+ call ReadWriteSectors ;common Int 13h/25h/26h read/write code
+
+; Perform fixups on the return register values.
+
+ mov ah,26h
+ call IntExit2X ;perform translations/buffering
+
+ FCLI
+ call LeaveIntHandler ;restore caller's registers, stack
+ assume ds:NOTHING,es:NOTHING
+
+; Int 25 & 26 leave the caller's flags on the stack, but we want to return
+; with the flags returned by the real mode ISR (which LeaveIntHandler has
+; incorporated into the caller's flags), so make a copy of the flags and
+; pop them into the flags register before returning.
+
+ push ax
+ push bp
+ mov bp,sp ;bp -> BP AX IP CS FL
+ mov ax,[bp+8]
+ xchg ax,[bp+2] ;bp -> BP FL IP CS FL
+ pop bp
+ npopf
+
+ retf
+
+PMIntr26 endp
+
+
+; -------------------------------------------------------
+; IntEntry2X -- This routine performs translations and
+; buffering of Int 25h and 26h requests on entry.
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntEntry2X
+
+IntEntry2X proc near
+
+ cmp [bp].intUserCX,-1 ;DOS 4.0 extended read/write?
+ jnz e2x_dsbx ; no, just go map DS:BX
+
+ mov ds,[bp].pmUserDS ; yes, copy down parameter blk
+ assume ds:NOTHING
+ mov si,[bp].pmUserBX
+ mov di,offset rgbXfrBuf0
+ cld
+ movsw ;32-bit sector #
+ movsw
+ movsw ;# sectors to read/write
+
+ mov ax,offset rgbXfrBuf1 ;replace pointer with addr of
+ stosw ; our own low buffer
+IFDEF ROM
+ GetPMDataSeg
+ELSE
+ mov ax,segDXDataPM ;segment, not selector
+ENDIF
+ stosw
+
+ push es
+ pop ds
+ assume ds:DGROUP
+
+ mov [bp].intUserBX,offset rgbXfrBuf0
+
+ ret
+
+e2x_dsbx: ;standard read/write, just redirect DS:BX
+
+ mov [bp].intUserBX,offset rgbXfrBuf1
+
+ ret
+
+IntEntry2X endp
+
+
+; -------------------------------------------------------
+; IntExit2X -- This routine performs translations and
+; buffering of Int 25h and 26h requests on exit.
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntExit2X
+
+IntExit2X proc near
+
+
+ mov ax,[bp].pmUserBX ;restore caller's BX
+ mov [bp].intUserBX,ax
+
+ ret
+
+IntExit2X endp
+
+
+; -------------------------------------------------------
+ subttl Disk Utility Routines
+ page
+; -------------------------------------------------------
+; DISK UTILITY ROUTINES
+; -------------------------------------------------------
+; ReadWriteSectors -- Common code to read/write disk sectors for
+; Int 13h/25h/26h.
+;
+; In: lParam - seg:off of real mode interrupt handler
+; wParam1 - int #, and possible subfunction
+; regs on stack
+
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public ReadWriteSectors
+
+ReadWriteSectors proc near
+
+ pop [bp].wParam2 ;save return addr higher on stack
+
+; Setup the global data items for the read/write--pointer to caller's
+; buffer, # sectors to read/write, and sector size.
+
+ cmp byte ptr [bp].wParam1+1,13h ;Int 13h?
+ jnz rws_dos_size
+
+ mov ax,[bp].pmUserBX ;ES:BX points to caller's buf
+ mov word ptr lpSectorData,ax
+ mov ax,[bp].pmUserES
+ mov word ptr [lpSectorData+2],ax
+
+ mov al,byte ptr [bp].intUserAX ;# sectors caller wants to
+ xor ah,ah ; read or write
+ mov cSectorsToTransfer,ax
+
+ mov ah,08h ;get drive parameters
+ mov dx,[bp].intUserDX ; for drive in DL
+
+ push ax
+ SwitchToRealMode
+ pop ax
+
+ pushf ;have BIOS get the drive data
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset rws_10)
+ mov ax,es:[13h*4]
+ mov [bp + 2],ax
+ mov ax,es:[13h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+rws_10: jnc @f
+
+ mov cx,512 ;according to PS/2 tech ref, some
+ jmp short rws_to_pm ; old bios versions may fail this,
+@@: ; just use 512 in that case
+
+ mov cl,es:[di+3] ;sector size shift factor (0,1,2,3)
+ mov ax,128
+ shl ax,cl ;ax now = sector size
+ mov cx,ax
+
+rws_to_pm:
+ SwitchToProtectedMode
+
+ FSTI ;don't need them disabled
+
+if DEBUG ;------------------------------------------------------------
+
+ cmp cx,512
+ jz @f
+ Debug_Out "Odd sector size = #CX"
+@@:
+
+endif ;DEBUG --------------------------------------------------------
+
+ jmp short rws_have_size
+
+; Before DOS 4.0, CX was the # sectors to read/write. Starting with 4.0,
+; if CX == -1, DS:BX points to a parameter block which contains the
+; sector size at offset 4.
+
+rws_dos_size:
+
+ mov cx,[bp].intUserCX ;caller's cs == -1?
+ inc cx
+ jcxz rws_dos_4
+ dec cx ; no, then cx has sector count
+
+ mov ax,[bp].pmUserBX ; and DS:BX points to buffer
+ mov word ptr lpSectorData,ax
+ mov ax,[bp].pmUserDS
+ mov word ptr [lpSectorData+2],ax
+
+ jmp short rws_dos_num_secs
+
+rws_dos_4:
+
+ mov cx,word ptr rgbXfrBuf0+4 ; yes, get count from low param block
+
+ push ds ; and DS:BX points to param block
+ mov ds,[bp].pmUserDS ; which contains pointer to buffer
+ assume ds:NOTHING
+ mov bx,[bp].pmUserBX
+ mov ax,word ptr ds:[bx+6]
+ mov word ptr lpSectorData,ax
+ mov ax,word ptr ds:[bx+8]
+ mov word ptr [lpSectorData+2],ax
+ pop ds
+ assume DS:DGROUP
+
+rws_dos_num_secs:
+ mov cSectorsToTransfer,cx ;number sectors to read/write
+
+ mov cx,512 ;I've been assured by a WINFILE developer
+ ; that the Int 25/26 sector size will always
+ ; be 512 bytes.
+
+; CX now has the drive's sector size. Determine how many sectors we can
+; transfer at a time
+
+rws_have_size:
+
+ mov cbSectorSize,cx ;save sector size for later
+
+ xor dx,dx
+ mov ax,CB_XFRBUF1 ;buf size / sector size = sectors per
+ div cx ; transfer
+
+if DEBUG ;------------------------------------------------------------
+ or ax,ax
+ jnz @f
+ Debug_Out "Sectors per transfer = 0!"
+@@:
+endif ;DEBUG --------------------------------------------------------
+
+ mov cSectorsPerTransfer,ax
+
+ xor ax,ax
+ mov cSectorsTransfered,ax ;sectors transfered so far = 0
+ mov cSectorsThisTransfer,ax ;sectors transfered last time = 0
+
+; Get/init a selector that we'll use to reference the caller's buffer.
+
+ mov ax,word ptr [lpSectorData+2] ;get lma of caller's buffer
+ call GetSegmentAddress
+ add dx,word ptr lpSectorData
+ adc bx,0
+
+ call AllocateSelector ;build a sel/dscr pointing
+ mov cx,0FFFFh
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+ xor bx,bx
+ mov word ptr lpSectorData,bx ;use that as the buffer ptr
+ mov word ptr [lpSectorData+2],ax
+
+
+; ======================================================================
+; Main sector read/write loop ------------------------------------------
+; ======================================================================
+
+rws_do_it_loop:
+
+; Calculate how many sectors to transfer this time around, set starting
+; sector number based on how many transfered last time.
+
+ mov ax,cSectorsToTransfer
+ sub ax,cSectorsTransfered
+ jnz @f
+ jmp rws_done
+@@:
+ cmp ax,cSectorsPerTransfer
+ jna @f
+ mov ax,cSectorsPerTransfer
+@@:
+ mov bx,cSectorsThisTransfer ;STIll # R/W from last loop
+
+ cmp byte ptr [bp].wParam1+1,13h ;BIOS read/write?
+ jnz rws_use_dos_size
+
+ push [bp].pmUserAX ;the BIOS does not save
+ pop [bp].intUserAX ; registers across calls to
+ push [bp].pmUserCX ; it so if we're doing
+ pop [bp].intUserCX ; multiple calls to buffer
+ push [bp].pmUserDX ; data, restore the initial
+ pop [bp].intUserDX ; register values
+
+ mov byte ptr [bp].intUserAX,al ;# sectors in AL
+
+ add byte ptr [bp].intUserCX,bl ;update new start sector in CL
+
+ jmp short rws_size_start_set
+
+rws_use_dos_size:
+
+ cmp [bp].intUserCX,0FFFFh ;normal or extended DOS?
+ jz rws_dos4_size
+ mov [bp].intUserCX,ax ; normal, # sectors in CX
+
+ add [bp].intUserDX,bx ; new start sector in DX
+
+ jmp short rws_size_start_set
+
+rws_dos4_size:
+
+ mov word ptr rgbXfrBuf0+4,ax ; extended, # sectors & 32 bit
+ add word ptr rgbXfrBuf0,bx ; start sector in parameter
+ adc word ptr rgbXfrBuf0+2,0 ; block
+
+rws_size_start_set:
+
+; At this point, AX has the number of sectors to transfer. If this is a
+; write, copy a buffer of data from the caller's buffer.
+
+ mov cSectorsThisTransfer,ax ;in case it's a read
+
+ cmp [bp].wParam1,1303h ;BIOS write?
+ jz rws_buf_write
+ cmp byte ptr [bp].wParam1+1,26h ;DOS write?
+ jnz rws_not_write
+
+rws_buf_write:
+
+ mul cbSectorSize ;AX now = # bytes to transfer
+ mov cx,ax ;can safely assume < 64k
+ shr cx,1 ;# words to move
+ lds si,lpSectorData
+ assume ds:NOTHING
+ mov di,offset rgbXfrBuf1
+ cld
+ rep movsw
+
+ push es
+ pop ds
+ assume ds:DGROUP
+
+ mov word ptr lpSectorData,si ;update src ptr for next time
+ call NormalizeBufPtr ; and normalize it
+
+rws_not_write:
+
+
+; Switch to real mode, do the transfer.
+
+ SwitchToRealMode
+ assume ds:DGROUP,es:DGROUP
+
+ push word ptr [bp].lParam
+ pop word ptr lpRmISR
+ push word ptr [bp].lParam+2
+ pop word ptr lpRmISR+2
+
+ cmp byte ptr [bp].wParam1+1,13h
+ jnz rws_call_dos
+
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING,ss:DGROUP
+ popa
+ call lpRmISR
+ pushf
+ FCLI
+ jmp short rws_save_regs
+
+rws_call_dos:
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING,ss:DGROUP
+ popa
+ call lpRmISR
+ pop word ptr lpRmISR ;int 25/26 leave flags on stack,
+ pushf ; pop them to nowhere
+ FCLI
+
+rws_save_regs:
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+
+ FSTI ;allow HW interrupts
+
+; If the call failed, then cut out now without further processing...
+
+ test byte ptr [bp].intUserFL,1 ;CY set?
+ jnz rws_done
+
+; If this was a successful read, copy the data back to the caller.
+
+ cmp [bp].wParam1,1302h ;BIOS read?
+ jz rws_buf_read
+ cmp byte ptr [bp].wParam1+1,25h ;DOS read?
+ jnz rws_not_read
+
+rws_buf_read:
+ mov ax,cSectorsThisTransfer ;calc size of data to move
+ mul cbSectorSize
+ mov cx,ax
+ shr cx,1 ;in words
+ les di,lpSectorData ;caller's buffer pointer
+ assume es:NOTHING
+ mov si,offset rgbXfrBuf1
+ cld
+ rep movsw
+
+ push ds
+ pop es
+ assume es:DGROUP
+
+ mov word ptr lpSectorData,di ;update dest ptr for next time
+ call NormalizeBufPtr ; and normailize it
+
+rws_not_read:
+
+ mov ax,cSectorsThisTransfer ;count total sectors transfered
+ add cSectorsTransfered,ax
+
+ jmp rws_do_it_loop ;go do another buffer full
+
+rws_done:
+
+ mov ax,word ptr [lpSectorData+2] ;release our temp buffer sel
+ call FreeSelector
+
+ jmp [bp].wParam2
+
+ReadWriteSectors endp
+
+
+; -------------------------------------------------------
+
+
+; This routine 'normalizes' the far pointer in lpSectorData such that
+; the selector/descriptor points to where the selector:offset currently
+; points
+
+ assume ds:DGROUP,es:NOTHING
+
+NormalizeBufPtr proc near
+
+ mov ax,word ptr [lpSectorData+2] ;get segment base address
+ call GetSegmentAddress
+ add dx,word ptr lpSectorData ;add in current offset
+ adc bx,0
+ call SetSegmentAddress ;make that the new seg base
+ xor bx,bx
+ mov word ptr lpSectorData,bx ; with a zero offset
+
+ ret
+
+NormalizeBufPtr endp
+
+DXPMCODE ends
+
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxdma.asm b/private/mvdm/dpmi/dxdma.asm
new file mode 100644
index 000000000..dfc7fe7de
--- /dev/null
+++ b/private/mvdm/dpmi/dxdma.asm
@@ -0,0 +1,714 @@
+ PAGE ,132
+ TITLE DXDMA.ASM -- Dos Extender DMA Services
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXDMA.ASM -- Dos Extender DMA Services
+;
+;-----------------------------------------------------------------------
+;
+; This module provides the protect mode DMA services for the 286 DOS
+; Extender. It supports a subset of the services documented in
+; "DMA Services for DOS Virtual 8086 and Protected Mode Environments"
+; by Microsoft Corporation.
+;
+;-----------------------------------------------------------------------
+;
+; 12/06/89 jimmat Minor changes to reflect updates in DMA Service Spec.
+; 11/01/89 jimmat Started.
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include interupt.inc
+if VCPI
+include dxvcpi.inc
+endif
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+PhysicalPageSize equ 4096 ;size of 80386 physical page
+PageShift equ 12
+
+DMAServiceByte equ 7Bh
+
+ChainReserved equ 08h ;set if unsupported services chained
+
+DMAServiceID equ 81h ;Int 4Bh/AH=81h are the DMA services
+FirstValidSvc equ 02h ;First valid DMA service #
+LastValidSvc equ 0Ch ;Last valid DMA service #
+
+; Get Version information
+
+MajorVersion equ 01h ;Major specification version
+MinorVersion equ 00h ;Minor specification version
+
+DOSXProductNumber equ 02h ;286 DOS Extender product number
+DOSXProductRevision equ 01h ;286 DOS Extender revision number
+
+MemoryContiguous equ 08h ;All memory physically contigious flag
+AutoRemapSupported equ 04h ;Automatic remap supported
+
+; DX flags on calls to DMA services
+
+AutoBufferCopy equ 02h ;set if data to be copied into DMA buf
+NoAutoBufferAlloc equ 04h ;set if NO automatic buff allocation
+NoAutoRemap equ 08h ;set if NO automatic remap attempted
+Align64k equ 10h ;set if region can't cross 64k boundry
+Align128k equ 20h ;set if region can't cross 128k boundry
+PageTableFmt equ 40h ;set for page table Scatter/Gather lock
+
+ValidDXFlags equ 007Eh ;valid DX register flag bits (above)
+
+; Error return codes
+
+ErrRegionCrossedBoundry equ 02h
+ErrNoBufferAvail equ 04h
+ErrTooManyRegions equ 09h
+ErrInvalidBufferID equ 0Ah
+ErrFuncNotSupported equ 0Fh
+ErrReservedFlagBits equ 10h
+
+; DMA Descriptor Structure(s)
+
+DDS STRUC ;normal DDS
+DDS_RegionSize dd ?
+DDS_Offset dd ?
+DDS_Selector dw ?
+DDS_BufferID dw ?
+DDS_PhyAddress dd ?
+DDS ENDS
+
+SGDDS1 STRUC ;Extended DDS for Scatter/Gather
+ dd 3 dup (?) ; Region format
+DDS_NumAvail dw ?
+DDS_NumUsed dw ?
+DDS_Region0Addr dd ?
+DDS_Region0Size dd ?
+SGDDS1 ENDS
+
+SGDDS2 STRUC ;Extended DDS for Scatter/Gather
+ dd 4 dup (?) ; Page Table format
+DDS_PageTblEnt0 dd ?
+SGDDS2 ENDS
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn EnterIntHandler:NEAR
+ extrn LeaveIntHandler:NEAR
+ extrn GetSegmentAddress:NEAR
+ extrn PMIntrEntryVector:NEAR
+
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+ifdef NOT_NTVDM_NOT
+ extrn fMicroChannel:BYTE
+endif
+
+if VCPI
+ extrn fVCPI:BYTE
+endif
+;
+; Contains a copy of bit 5 of the byte at 40:7b, that indicates
+; whether VDS should be used.
+;
+ public bDMAServiceBit
+bDMAServiceBit db 0
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+DXCODE ends
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+
+DMASvcTbl label word
+
+ dw offset DXPMCODE:GetVersion
+ dw offset DXPMCODE:LockDMARegion
+ dw offset DXPMCODE:DoNothing ;UnlockDMARegion
+ dw offset DXPMCODE:ScatterGatherLock
+ dw offset DXPMCODE:DoNothing ;ScatterGatherUnlock
+ dw offset DXPMCODE:Fail4NoBuffer ;RequestDMABuffer
+ dw offset DXPMCODE:DoNothing ;ReleaseDMABuffer
+ dw offset DXPMCODE:Fail4BufferID ;CopyIntoDMABuffer
+ dw offset DXPMCODE:Fail4BufferID ;CopyOutOfDMABuffer
+ dw offset DXPMCODE:DoNothing ;DisableDMATranslation
+ dw offset DXPMCODE:DoNothing ;EnableDMATranalation
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl DMA Service Dispatcher
+ page
+; -------------------------------------------------------
+; DMA SERVICE DISPATCHER
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntr4B -- Entry routine/dispatcher for protected mode DMA
+; services. The DMA services are invoked with an Int 4Bh
+; interrupt. The 286 DOS Extender only supports the DMA
+; services in protected mode. Other systems that use Virtual
+; 8086 mode on 386 processors will most likely need to support
+; the services in virtual mode also.
+;
+; The following services are supported (Int 4Bh/AH = 81h):
+;
+; AL = 00 Reserved
+; 01 Reserved
+; 02 Get Version
+; 03 Lock DMA Region
+; 04 Unlock DMA Region
+; 05 Scatter/Gather Lock Region
+; 06 Scatter/Gather Unlock Region
+; 07 Request DMA Buffer
+; 08 Release DMA Buffer
+; 09 Copy Into DMA Buffer
+; 0A Copy Out Of DMA Buffer
+; 0B Disable DMA Translation
+; 0C Enable DMA Translation
+; 0D Reserved
+; ...
+; FF Reserved
+
+ public PMIntr4B
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+PMIntr4B proc near
+
+; Is this one of the supported DMA services?
+
+ cmp ah,DMAServiceID ;is this a DMA service request?
+ jz @f
+ jmp short i4b_other_service ; no, see if it should be chained
+@@:
+ call EnterIntHandler ;saves regs, switches stacks, etc.
+ assume ds:DGROUP ;sets DS/ES = DGROUP
+ mov es,[bp].pmUserES ; but we want caller's ES
+
+ cld ;cya...
+
+ cmp al,FirstValidSvc
+ jb i4b_reserved
+
+ cmp al,LastValidSvc
+ ja i4b_reserved
+
+ test dx,NOT ValidDXFlags ;any reserved flags set?
+ jnz i4b_bad_flags
+
+; -------------------------------------------------------
+
+; Setup local environment and dispatch the service
+
+i4b_valid_svc:
+
+ push offset DXPMCODE:i4b_svc_ret ;save dispatch return address
+
+ sub al,FirstValidSvc ;get address of service routine
+ cbw
+ shl ax,1
+ add ax,offset DXPMCODE:DMASvcTbl
+
+ xchg ax,bx
+ mov bx,cs:[bx]
+ xchg ax,bx
+
+ push ax ;save routine address on stack
+ mov ax,[bp].pmUserAX ;restore entry AX
+
+ ret ;invoke service routine
+
+i4b_svc_ret: ;service routines return here
+
+ jnc i4b_good_return ;CY set if service failed
+
+i4b_error_return:
+
+ or byte ptr [bp].intUserFL,1 ;set CY in caller's flags
+ jmp short i4b_exit
+
+i4b_good_return:
+
+ and byte ptr [bp].intUserFL,not 1 ;clear CY in caller's flags
+
+i4b_exit:
+
+ call LeaveIntHandler ;resotre stack, regs, etc.
+ iret
+
+; -------------------------------------------------------
+
+; Reserved DMA service 00, 01, 0D-FF; return with CY set and
+; AL = ErrFuncNotSupported
+
+i4b_reserved:
+
+ mov byte ptr [bp].intUserAX,ErrFuncNotSupported
+ jmp short i4b_error_return
+
+; -------------------------------------------------------
+
+; User made a DMA service call with a reserved flag bit set. Fail the
+; call with AL = ErrReservedFlagBits
+
+i4b_bad_flags:
+
+ mov byte ptr [bp].intUserAX,ErrReservedFlagBits
+ jmp short i4b_error_return
+
+; -------------------------------------------------------
+
+; This is a non-DMA Int 4B call. On Micro Channel systems, bit 3
+; in location 40:007B indicates if we should chain the call along
+; or not. If the bit is set, we chain. If not Micro Channel, or the
+; bit is not set, we check the real mode Int 4Bh vector to see if someone
+; other than the BIOS has it hooked--if so, we chain anyway. If not,
+; return without changing any regs or flags.
+
+i4b_other_service:
+
+ push ds
+
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+
+ifdef NOT_NTVDM_NOT
+ test fMicroChannel,0FFh ;if micro channel system
+ jz i4b_check_vector ; and 40:7B bit 3 set,
+ ; chain the call to real mode
+ push SEL_BIOSDATA or STD_RING
+ pop ds
+ assume ds:NOTHING
+
+ test byte ptr ds:DMAServiceByte,ChainReserved
+ jnz i4b_chain
+endif
+
+i4b_check_vector: ;not micro channel, or bit 3 not set
+
+ push ax ;check if the real mode Int 4Bh
+ mov ax,SEL_RMIVT or STD_RING ; points somewhere and not
+ mov ds,ax ; at the BIOS--if so, chain
+ mov ax,word ptr ds:[4Bh*4] ; anyway.
+ or ax,word ptr ds:[4Bh*4+2]
+ pop ax
+ jz i4b_dont_chain
+
+ cmp word ptr ds:[4bh*4+2],0E000h
+ jz i4b_dont_chain
+
+ cmp word ptr ds:[4bh*4+2],0F000h
+ jz i4b_dont_chain
+
+i4b_chain:
+
+ pop ds ;chain the request to real mode
+ jmp PMIntrEntryVector + 3*4Bh ; (no one can have pMode
+ ; hooked before us)
+
+i4b_dont_chain: ;don't chain the interrupt,
+ ; just return quietly
+ pop ds
+ iret
+
+PMIntr4B endp
+
+; -------------------------------------------------------
+ subttl DMA Service Routines
+ page
+; -------------------------------------------------------
+; DMA SERVICE ROUTINES
+; -------------------------------------------------------
+
+
+; -------------------------------------------------------
+; RM4B -- Call the real mode INT 4Bh handler to
+; perform a DMA service, most likely something to
+; do with the VCPI provider's buffer when we are
+; running under VCPI.
+;
+; Input: depends on call
+; Output: Flags, registers from real mode call
+;
+; Notes: This is strictly an internal DOSX call,
+; so if a long pointer is used, then it
+; is assumed to point into DOSX's data
+; segment.
+;
+cProc RM4B,<NEAR,PUBLIC>
+cBegin
+ pushf
+ push cs
+ call near ptr PMIntrEntryVector + 3*4Bh
+cEnd
+
+; -------------------------------------------------------
+; DoNothing -- This routine does nothing other than return
+; indicating that the DMA service succeeded.
+;
+; Input: none
+; Output: AL = 0, CY clear
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+DoNothing proc near
+
+ clc ;indicate success
+ ret
+
+DoNothing endp
+
+
+; -------------------------------------------------------
+; Fail4BufferID -- This routine does nothing other than return
+; indicating that the DMA service failed with 'Invalid Buffer ID'
+;
+; Input: none
+; Output: AL = 0Ah, CY set
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+Fail4BufferID proc near
+
+ mov byte ptr [bp].intUserAX,ErrInvalidBufferID
+ stc
+ ret
+
+Fail4BufferID endp
+
+
+; -------------------------------------------------------
+; Fail4NoBuffer -- This routine does nothing other than return
+; indicating that the DMA service failed with 'No Buffer Available'
+;
+; Input: none
+; Output: AL = 04h, CY set
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+Fail4NoBuffer proc near
+
+ mov byte ptr [bp].intUserAX,ErrNoBufferAvail
+ stc
+ ret
+
+Fail4NoBuffer endp
+
+
+; -------------------------------------------------------
+; GetVersion -- This routine processes the DMA Get Version
+; service (AL = 02).
+;
+; Input: none
+; Output: AH/AL - Major/Minor specification level
+; BX - Product number
+; CX - Product revision number
+; DX - flags
+; SI:DI - 32 bit max buffer size available
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+GetVersion proc near
+
+ mov [bp].intUserAX,(MajorVersion shl 8) or MinorVersion
+ mov [bp].intUserBX,DOSXProductNumber
+ mov [bp].intUserCX,DOSXProductRevision
+ mov [bp].intUserDX,MemoryContiguous
+
+
+ xor ax,ax ;0 buffer size supported, also clears
+ mov [bp].intUserSI,ax ; carry flag
+ mov [bp].intUserDI,ax
+
+if VCPI
+ cmp fVCPI,0
+ je gv_x
+ mov ax,8102h
+ xor dx,dx ;DX = 0
+ cmp bDMAServiceBit,0 ;VCPI provider supports VDS?
+ je gv_e
+ call RM4B
+ jc gv_x
+ mov [bp].intUserSI,si
+ mov [bp].intUserDI,di
+ and dx,NOT (AutoRemapSupported or MemoryContiguous)
+gv_e:
+ mov [bp].intUserDX,dx
+gv_x:
+endif
+ ret
+
+GetVersion endp
+
+
+; -------------------------------------------------------
+; LockDMARegion -- This routine processes the Lock DMA Region
+; service (AL = 03).
+;
+; Input: DX - flags
+; ES:DI - ptr to DDS
+; Output: if successful, CY clear; else CY set and AL = error code
+; DDS updated
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+LockDMARegion proc near
+
+; Since we don't support paging or anything interesting like that, the only
+; thing that can fail us is an alignment problem--check for that.
+
+ test dl,Align64k or Align128k ;if they don't care,
+ jz lock_ok ; we don't care
+
+ mov si,dx ;save flags in SI
+
+ call CalcDDSPhyAddress ;see where the region is
+
+ push dx ;save start address
+ push ax
+
+ mov bx,dx ;bx = hi word start addr
+
+ add ax,word ptr es:[di].DDS_RegionSize ;see where it ends
+ adc dx,word ptr es:[di].DDS_RegionSize+2
+
+ sub ax,1 ;less 1 to point at
+ sbb dx,0 ; last byte, not next
+
+ mov cx,dx ;cx = hi word end addr
+
+ test si,Align128k ;64k or 128k alignment wanted?
+ jz @f ; already setup for 64k
+ and bl,not 1 ;mask to 128k alignment
+ and cl,not 1
+@@:
+ cmp bx,cx ;within the boundry?
+ jz lock_ok_clr_stk ; yes, 'lock' it
+
+; The region crosses an alignment boundary, we need to update the allowed
+; region size in the DDS, and fail the call.
+
+ pop cx
+ pop dx ;dx:cx = region start address
+
+ neg cx ;cx = len to next 64k boundry
+ xor bx,bx
+ test si,Align128k
+ jz @f
+ mov bl,dl
+ and bl,1
+ xor bl,1 ;bx:cx = len to next alignment boundry
+@@:
+ mov word ptr es:[di].DDS_RegionSize,cx ;update size in DDS
+ mov word ptr es:[di].DDS_RegionSize+2,bx
+
+ mov byte ptr [bp].intUserAX,ErrRegionCrossedBoundry ;flag failure
+ stc
+ ret
+
+
+lock_ok_clr_stk:
+
+ add sp,4 ;clear start address from stack
+
+; No alignment problem, we can 'lock' the region.
+
+lock_ok:
+
+ call CalcDDSPhyAddress ;get physical address of region DX:AX
+ mov word ptr es:[di].DDS_PhyAddress,ax
+ mov word ptr es:[di].DDS_PhyAddress+2,dx
+
+ xor ax,ax ;*** also clears CY! ***
+ mov es:[di].DDS_BufferID,ax ;no buffer used
+
+ ret
+
+LockDMARegion endp
+
+
+; -------------------------------------------------------
+; ScatterGatterLock -- This routine implements the Scatter/Gather
+; Lock Region DMA service (AL = 05h).
+;
+; Input: DX - flags
+; ES:DI - ptr to extended DDS
+; Output: if successful, CY clear; else CY set & AL = error code
+; DDS updated
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+ScatterGatherLock proc near
+
+ test dl,PageTableFmt ;Scatter/Gather page table form?
+ jnz do_page_tbl_lock
+
+; This is the region form of Scatter/Gather Lock Region -- for us this
+; is easy, since memory is contiguous -- one region covers the entire area.
+
+ mov ax,1 ;we need one region entry
+ mov es:[di].DDS_NumUsed,ax
+
+ cmp es:[di].DDS_NumAvail,ax ; is it available?
+ jb not_enough_entries
+
+ call CalcDDSPhyAddress ;get physical address
+
+ mov word ptr es:[di].DDS_Region0Addr,ax ;store in extended DDS
+ mov word ptr es:[di].DDS_Region0Addr+2,dx
+
+ mov ax,word ptr es:[di].DDS_RegionSize ;copy over the size
+ mov word ptr es:[di].DDS_Region0Size,ax
+ mov ax,word ptr es:[di].DDS_RegionSize+2
+ mov word ptr es:[di].DDS_Region0Size+2,ax
+
+ clc ;indicate success
+ ret
+
+; This is the page table form of Scatter/Gather Lock Region -- we need to
+; build a fake page table (even though we may be on an 80286!?) to return
+; in the extended DDS -- silly, but easy enough...
+
+do_page_tbl_lock:
+
+ call CalcDDSPhyAddress ;get region start address
+
+ mov cx,word ptr es:[di].DDS_RegionSize ;calc # pages needed
+ mov bx,word ptr es:[di].DDS_RegionSize+2 ; for region of this
+ add cx,PhysicalPageSize-1 ; size
+ adc bx,0
+ shr cx,PageShift
+ shl bx,16-PageShift
+ or cx,bx ;cx = # pages
+
+ test ax,PhysicalPageSize-1 ;if region doesn't start on a page
+ jz @f ; boundry, add another page to
+ inc cx ; the region size
+@@:
+ mov es:[di].DDS_NumUsed,cx ;tell caller how many used/needed
+ cmp es:[di].DDS_NumAvail,cx ;did caller supply enough page entries?
+ jb not_enough_entries ; no!
+
+ push ax ;save low word of region start address
+
+ and ax,NOT PhysicalPageSize-1 ;round down to page boundry
+ or al,1 ;set page present/locked bit
+ mov bx,di ;es:bx -> page table entries
+
+ jcxz page_ents_done ;better safe than sorry
+@@:
+ mov word ptr es:[bx].DDS_PageTblEnt0,ax ;build fake page
+ mov word ptr es:[bx].DDS_PageTblEnt0+2,dx ; table entries...
+ add ax,PhysicalPageSize
+ adc dx,0
+ add bx,4
+ loop @b
+
+page_ents_done:
+
+ pop ax ;recover low word of start address
+ and ax,PhysicalPageSize-1 ; and get offset into first page
+ mov [bp].intUserBX,ax ; return to caller in BX
+
+ clc ;indicate success
+ ret
+
+; Fail the request for insufficient # of region/page tbl entries
+
+not_enough_entries:
+
+ mov byte ptr [bp].intUserAX,ErrTooManyRegions ;AL = error code
+
+ mov ax,es:[di].DDS_NumAvail ;store max lockable
+ mov dx,ax ; size (bytes) in
+ shl ax,PageShift ; DDS region size
+ shr dx,16-PageShift
+ mov word ptr es:[di].DDS_RegionSize,ax
+ mov word ptr es:[di].DDS_RegionSize+2,dx
+
+ stc ;indicate failure
+ ret
+
+
+ScatterGatherLock endp
+
+
+; -------------------------------------------------------
+ subttl DMA Service Utility Routines
+ page
+; -------------------------------------------------------
+; DMA SERVICE UTILITY ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; CalcDDSPhyAddress -- This routine calculates the physical
+; address of the region specified in a DDS.
+;
+; Input: ES:DI - ptr to DDS
+; Output: DX:AX - 32 bit physical address
+; Uses: none.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+CalcDDSPhyAddress proc near
+
+ push bx
+ xor bx,bx
+ mov dx,bx
+
+ mov ax,es:[di.DDS_Selector] ;if a selector is given,
+ or ax,ax ; get it's base address
+ jz @f
+
+ call GetSegmentAddress ;bx:dx = segment base
+@@:
+ add dx,word ptr es:[di.DDS_Offset] ;add 32 bit offset
+ adc bx,word ptr es:[di.DDS_Offset+2]
+
+ mov ax,dx ;32 bit address to dx:ax
+ mov dx,bx
+ pop bx
+
+ ret
+
+CalcDDSPhyAddress endp
+
+
+DXPMCODE ends
+
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxemm.asm b/private/mvdm/dpmi/dxemm.asm
new file mode 100644
index 000000000..7e86b9e6c
--- /dev/null
+++ b/private/mvdm/dpmi/dxemm.asm
@@ -0,0 +1,425 @@
+ PAGE ,132
+ TITLE DXEMM.ASM -- Dos Extender MEMM Disable Code
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXEMM.ASM -- Dos Extender MEMM Disable Code
+;
+;-----------------------------------------------------------------------
+;
+; This module provides routines that attempt to disable MEMM/CEMM/EMM386
+; drivers. DOSX tries to disable MEMM when starting up, and enables MEMM
+; when terminating.
+;
+; NOTE: All the code in this module is consider initialization, and
+; is discarded before going operational. This includes code
+; segment variables. The MEMM enable code is not in this file
+; since that cannot be discarded.
+;
+;-----------------------------------------------------------------------
+;
+; 12/08/89 jimmat Minor changes so enable code could be finished.
+; 07/14/89 jimmat Original version - but largely taken from Windows/386
+; code from ArronR
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+ .list
+
+if NOT VCPI
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+EMM_OK equ 0
+
+; Device driver header for Microsoft 386 EMM drivers
+;
+emm_hdr STRUC
+;
+ DW ? ;Null segment address
+ DW ? ;Null offset address
+ DW ? ;Attribute - Char
+ DW ? ;Strategy routine entry
+ DW ? ;Interrupt routine entry
+ DB 'EMMXXXX0' ;Character device name
+;
+; GENERAL FUNCTIONS ENTRY POINT
+; ELIM_Entry is a entry point for executing general MEMM
+; functions. (e.g. ON, OFF function).
+;
+ELIM_Entry_off dw ? ; general entry point
+
+;
+; MEMM signature
+;
+emmsig db ? ; MEMM signature
+
+emm_hdr ENDS
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn MEMM_State:BYTE ; initial on/off/auto state
+ extrn MEMM_Call:DWORD ; far call address into MEMM driver
+ extrn fMEMM_Disabled:BYTE ; NZ if MEMM was disabled
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn segDXData:WORD
+ENDIF
+
+EMMDevNameRM DB "EMMXXXX0" ;Character device name
+
+MEMMsig db 'MICROSOFT EXPANDED MEMORY MANAGER 386'
+MEMMsiglen equ $ - MEMMsig
+
+CEMMsig db 'COMPAQ EXPANDED MEMORY MANAGER 386'
+CEMMsiglen equ $ - CEMMsig
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+DXPMCODE ends
+
+
+; -------------------------------------------------------
+ subttl MEMM/CEMM/EMM386 Disable Routines
+ page
+; -------------------------------------------------------
+; MEMM/CEMM/EMM386 DISABLE ROUTINES
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; EMMDisable -- This routine attempts to disable any installed
+; MEMM/CEMM/EMM386 driver.
+;
+; Input: none
+; Output: CY off - EMM driver disabled (or not installed)
+; CY set - EMM installed, and can't disable
+; Errors:
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public EMMDisable
+
+EMMDisable proc near
+
+ pusha
+ push ds
+ push es
+
+ call Check_for_EMM_Driver ;is there and EMM driver?
+ jc emmd_ok ; no, then we're already done
+
+ call MEMM_Inst_chk ;is it one we know about?
+ jc emmd_bad ; no, then we can't disable it
+
+; Get the current EMM driver state before checking for open handles. The
+; process of checking for handles may change the driver from AUTO to ON.
+
+ xor ax,ax ; get & save current emm state
+ call [MEMM_Call] ; returns ah = 0 - on, 1 - off,
+ mov MEMM_state,ah ; 2 - auto & off, 3 - auto & on
+
+ call AnyMEMMHandles ;does it have any handles allocated?
+ jc emmd_bad ; yes, then we can't disable it
+
+ call TurnMEMMOff ;try to disable it
+ jc emmd_bad
+
+ mov fMEMM_Disabled,1 ;remember that we disabled MEMM
+
+emmd_ok:
+ clc ;indicate disabled (or not installed)
+
+emmd_ret:
+ pop es
+ pop ds
+ popa
+
+ ret
+
+emmd_bad:
+ stc ;can't disable!
+ jmp short emmd_ret
+
+EMMDisable endp
+
+
+; -------------------------------------------------------
+; Windows/386 EMM Disable Code
+; -------------------------------------------------------
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+BeginProc macro name
+name proc near
+ endm
+
+EndProc macro name
+name endp
+ endm
+
+;--------------------------------------------------------
+
+;******************************************************************************
+;
+; MEMM_Inst_chk - Check to see if MEMM/CEMM is already installed
+;
+; ENTRY:
+; Know there is an EMM driver so INT 67 vector points to something
+;
+; EXIT:
+; Carry set
+; No MEMM/CEMM driver
+; Carry Clear
+; [entry_seg] = segment of driver header
+; [entry_off] = offset of status routine in MEMM
+;
+; USES: AX,CX,SI,DI,FLAGS
+;
+;******************************************************************************
+
+ assume ds:NOTHING, es:NOTHING
+
+BeginProc MEMM_Inst_chk
+
+ push ds
+ push es
+
+ xor ax,ax
+ mov ds,ax
+ mov ax,word ptr ds:[(67h * 4)+2] ; get segment pointed to by int 67
+ mov ds,ax
+ mov si,emmsig
+ cld ; strings foward
+ mov di,offset MEMMsig
+ push cs
+ pop es
+ mov cx,MEMMsiglen
+ cld
+ repe cmpsb ; q: is the MEMM signature out there?
+ je short found_sig ; y: return one
+ mov si,emmsig
+ mov di,offset CEMMsig
+ mov cx,CEMMsiglen
+ cld
+ repe cmpsb ; q: is the CEMM signature out there?
+ jne short Not_Found ; n: done, not found
+
+found_sig:
+IFDEF ROM
+ GetRMDataSeg
+ mov es,ax
+ELSE
+ mov es,segDXData
+ENDIF
+ xor si,si
+ mov word ptr es:[MEMM_Call+2],ds ; save segment for far call
+ mov cx,ds:[si.ELIM_Entry_off]
+ mov word ptr es:[MEMM_Call],cx ; Offset for far call
+
+ clc
+
+MEMM_Inst_Done:
+ pop es
+ pop ds
+ ret
+
+Not_Found:
+ stc
+ jmp short MEMM_Inst_Done
+
+EndProc MEMM_Inst_chk
+
+;******************************************************************************
+;
+; TurnMEMMOff
+;
+; Turn MEMM off (CEMM, IEFF, MEMM)
+;
+; ENTRY:
+; entry_seg entry_off set to CEMM/MEMM enable disable routine
+;
+; EXIT:
+; Carry Set
+; Could not disable EMM
+; Carry Clear
+; MEMM CEMM EMM turned off
+;
+; USES: EAX,FLAGS
+;
+;******************************************************************************
+
+ assume ds:DGROUP, es:NOTHING
+
+BeginProc TurnMEMMOff
+
+ cmp MEMM_state,1 ; MEMM already off?
+ jz short memm_off ; yes, nothing to do
+
+ mov AX,0101h ; no, turn it OFF
+ call [MEMM_Call]
+ jc short memm_err
+memm_off:
+ clc
+memm_done:
+ ret
+
+memm_err:
+ stc ; Error, set carry
+ jmp short memm_done
+
+EndProc TurnMEMMOff
+
+;******************************************************************************
+;
+; AnyMEMMHandles/Check_For_EMM_Handles
+;
+; Are there any open MEMM handles
+;
+; ENTRY:
+; entry_seg entry_off set to CEMM/MEMM enable disable routine
+;
+; EXIT:
+; Carry Set
+; There are open handles
+; Carry Clear
+; There are no open handles
+;
+; USES: EAX,EBX,ECX,FLAGS
+;
+;******************************************************************************
+
+ assume ds:DGROUP, es:NOTHING
+
+BeginProc AnyMEMMHandles
+
+ mov ax,4600h
+ int 67h
+ cmp ah,EMM_OK
+ jne short memm_is_off
+ mov cx,ax
+ mov ax,4B00h
+ int 67h
+ cmp ah,EMM_OK
+ jne short memm_is_off
+ cmp cl,40h
+ jb short Check_Cnt
+ or bx,bx ; Don't dec through 0!!!
+ jz short Check_Cnt
+ dec bx ; Do not include handle 0 on 4.0 drivers
+Check_Cnt:
+ cmp bx,0
+ stc
+ jne short HaveHandles
+memm_is_off:
+ clc
+HaveHandles:
+ ret
+
+EndProc AnyMEMMHandles
+
+;******************************************************************************
+;
+; Check_For_EMM_Driver
+;
+; See if an EMM driver is around
+;
+; ENTRY:
+; None
+;
+; EXIT:
+; Carry Set
+; No EMM driver around
+; Carry Clear
+; EMM driver is around
+;
+; USES: AX,CX,SI,DI,FLAGS
+;
+;******************************************************************************
+
+ assume ds:NOTHING,es:NOTHING
+
+BeginProc Check_For_EMM_Driver
+
+ push ds
+ push es
+
+; Note, DS:SI & ES:DI used to be swapped, but on at least one system where
+; Int 67h pointed to F000 and there was not ram or rom at F000:000A (rom
+; started at F0000:8000), bus noise made the compare work when it shouldn't
+; have. Swapping ES:DI / DS:SI corrected this.
+
+ xor ax,ax
+ mov es,ax
+ mov ax,word ptr es:[(67h * 4)+2] ; get segment pointed to by int 67
+ mov es,ax
+ mov di,000Ah ; Offset of device name
+ mov si,offset EMMDevNameRM
+ push cs
+ pop ds
+ mov cx,8
+ cld
+ repe cmpsb
+ jne short NoEMM_Seen
+ clc
+
+EMMTstDone:
+ pop es
+ pop ds
+ ret
+
+NoEMM_Seen:
+ stc
+ jmp short EMMTstDone
+
+EndProc Check_For_EMM_Driver
+
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+;****************************************************************
+
+endif ; NOT VCPI
+
+ end
diff --git a/private/mvdm/dpmi/dxemm2.asm b/private/mvdm/dpmi/dxemm2.asm
new file mode 100644
index 000000000..eba5fda50
--- /dev/null
+++ b/private/mvdm/dpmi/dxemm2.asm
@@ -0,0 +1,139 @@
+ PAGE ,132
+ TITLE DXEMM2.ASM -- Dos Extender MEMM Enable Code
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXEMM2.ASM -- Dos Extender MEMM Enable Code
+;
+;-----------------------------------------------------------------------
+;
+; This module provides routines that attempt to enable MEMM/CEMM/EMM386
+; drivers. DOSX tries to disable MEMM when starting up, and enables MEMM
+; when terminating.
+;
+; NOTE: This code is in a seperate file from the disable logic because
+; the disable code is discarded after initialization.
+;
+;-----------------------------------------------------------------------
+;
+; 12/08/89 jimmat Finally got around to implementing this.
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+ .list
+
+if NOT VCPI
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ public fMEMM_Disabled, MEMM_State, MEMM_Call
+
+fMEMM_Disabled db 0 ; NZ if MEMM disabled
+MEMM_state db 0 ; Initial MEMM state
+
+MEMM_Call dd 0 ; far call address into EMM driver
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+DXPMCODE ends
+
+
+; -------------------------------------------------------
+ subttl MEMM/CEMM/EMM386 Enable Routines
+ page
+; -------------------------------------------------------
+; MEMM/CEMM/EMM386 ENABLE ROUTINES
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; EMMEnable -- This routine attempts to re-enable any installed
+; MEMM/CEMM/EMM386 driver.
+;
+; Input: none
+; Output: CY off - EMM driver enabled (or never disabled)
+; CY set - EMM installed, and can't disable
+; Errors:
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public EMMEnable
+
+EMMEnable proc near
+
+ push ax
+
+ cmp fMEMM_Disabled,0 ; Did we disable MEMM before?
+ jz enable_exit ; no, don't need to enable then
+
+ mov ah,01 ; Set state command
+ mov al,MEMM_state ; Get initial state
+ cmp al,2 ; They return 0 (on), 1 (off),
+ jbe @f ; 2 (auto on), 3 (auto off) -- but
+ mov al,2 ; we can only set 1 - 2
+@@:
+ call [MEMM_Call] ; and restore it
+ jc not_enabled
+
+ mov fMEMM_Disabled,0 ; no longer disabled
+
+ clc
+ jmp short enable_exit
+
+not_enabled:
+ stc
+
+enable_exit:
+ pop ax
+
+ ret
+
+EMMEnable endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+;****************************************************************
+
+endif ; NOT VCPI
+
+ end
diff --git a/private/mvdm/dpmi/dxend.asm b/private/mvdm/dpmi/dxend.asm
new file mode 100644
index 000000000..334da603c
--- /dev/null
+++ b/private/mvdm/dpmi/dxend.asm
@@ -0,0 +1,74 @@
+ PAGE ,132
+ TITLE DXEND.ASM -- Special End Module for Dos Extender
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXEND.ASM - Dos Extender End Module *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains the definition for special symbols *
+;* which define the end of the Dos Extender code and data *
+;* segments. It must be the last module linked in the *
+;* Dos Extender. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 01/09/90 jimmat Remove DataEnd symbol since it wasn't *
+;* really useful. *
+;* 08/20/89 jimmat Removed A20 space since HIMEM 2.07 works *
+;* properly across processor resets *
+;* 08/02/89 jimmat Moved CodeEndPM to DXENDPM.ASM *
+;* 07/21/89 jimmat: Added space for A20 handler *
+;* 02/10/89 (GeneA): changed Dos Extender from small model to *
+;* medium model *
+;* 09/20/88 (GeneA): created *
+;* *
+;****************************************************************
+;
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+if VCPI
+include dxvcpi.inc
+endif
+
+; -------------------------------------------------------
+; CODE SEGMENT DEFINITIONS
+; -------------------------------------------------------
+;
+DXCODE segment
+ assume cs:DXCODE
+
+ public CodeEnd
+
+
+if VCPI
+
+if1
+%OUT VCPI option not ROMable.
+endif
+
+CodeEnd db ( LDTOFF + CBPAGE386 ) dup (0)
+
+else
+
+CodeEnd:
+
+endif ; VCPI
+
+DXCODE ends
+
+
+;****************************************************************
+
+ end
diff --git a/private/mvdm/dpmi/dxendpm.asm b/private/mvdm/dpmi/dxendpm.asm
new file mode 100644
index 000000000..7a04b873a
--- /dev/null
+++ b/private/mvdm/dpmi/dxendpm.asm
@@ -0,0 +1,46 @@
+ PAGE ,132
+ TITLE DXENDPM.ASM -- Special End Module for Dos Extender
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXENDPM.ASM - Dos Extender End Module *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains the end symbol for the DOS Extender's *
+;* protected mode code segment. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 08/02/89 jimmat Split out from dxend.asm *
+;* 09/20/88 (GeneA): created *
+;* *
+;****************************************************************
+;
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+include segdefs.inc
+
+; -------------------------------------------------------
+; CODE SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+ public CodeEndPM
+CodeEndPM:
+
+DXPMCODE ends
+
+;****************************************************************
+
+ end
diff --git a/private/mvdm/dpmi/dxfind.asm b/private/mvdm/dpmi/dxfind.asm
new file mode 100644
index 000000000..205ecabe9
--- /dev/null
+++ b/private/mvdm/dpmi/dxfind.asm
@@ -0,0 +1,426 @@
+ PAGE ,132
+ TITLE DXFIND.ASM -- Dos Extender Find File Routine
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXFIND.ASM -- Dos Extender Find File Routine
+;
+;-----------------------------------------------------------------------
+;
+; This module provides the locate file logic for the 286 DOS Extender.
+;
+;-----------------------------------------------------------------------
+;
+; 09/27/89 jimmat Original version -- considerable code taken from
+; old GetChildName routine in dxinit.asm.
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn strcpy:NEAR
+ extrn toupper:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn segPSP:WORD
+ extrn rgbXfrBuf1:BYTE
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+szPath db 'PATH',0
+szWindir db 'WINDIR',0
+
+DXCODE ends
+
+
+; -------------------------------------------------------
+ subttl Find File Routine
+ page
+; -------------------------------------------------------
+; FIND FILE ROUTINE
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; FindFile -- This routine is used to locate a particular file.
+; If successful, this routine will setup the buffer in rgbXfrBuf1
+; at offset EXEC_PROGNAME with the string for the file name that
+; can be used in a DOS open call.
+;
+; This routine searches for the file in the following sequence:
+;
+; 1) If the file name contains a relative or complete path
+; component, only look for that particular file,
+;
+; otherwise:
+;
+; 1) Look int the environment for a WINDIR= variable, and if
+; found, check that directory first.
+; 2) Look in the directory the dos extender was loaded from
+; 3) Look in the current directory
+; 4) Look in all directories in the PATH environment variable
+;
+; NOTE: This routine must be called in real mode!
+;
+; Input: RELOC_BUFFER has the file name to search for.
+; EXEC_DXNAME has the complete path the dos extender
+; Output: EXEC_PROGNAME has complete path to child program.
+; Errors: returns CY set if unable to find child
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FindFile
+
+FindFile proc near
+
+ pusha
+ push ds
+ push es
+
+ push ds
+ pop es
+ assume es:DGROUP
+
+; If the base file name contains a ':' '\' or '/', then we'll see if the
+; file can be found with that name. This isn't exactly the way Windows wants
+; us to search for kernel, but allows a user to specify a relative or complete
+; path on the command line. If we get run for Windows/286 pMode, the base
+; child name will not include any of these characters, so we will not make
+; this check.
+
+ mov si,offset RELOC_BUFFER
+@@: lodsb
+ or al,al
+ jz fndf20 ;no path characters, go do other search
+ cmp al,':'
+ jz fndf10
+ cmp al,'\'
+ jz fndf10
+ cmp al,'/'
+ jnz @b
+
+; The name seems to include a path component. We just want to look for that
+; file, and only that file.
+
+fndf10: mov si,offset RELOC_BUFFER
+ mov di,offset EXEC_PROGNAME
+ mov dx,di
+ call strcpy
+
+ mov ax,4300h ;use get file attributes to check
+ int 21h
+ jmp fndf90 ;found it or not, either way we're done
+
+; The file name doesn't include a path component. If we were run for pMode
+; Windows, the environment should include a WINDIR= entry that points to
+; the Windows directory. Look for this env variable, and check that directory
+; first.
+
+fndf20:
+ push es ;look for the
+ push cs ;WINDIR= env variable
+ pop es
+ assume es:NOTHING
+ mov di,offset DXCODE:szWindir
+ call GetEnv
+ pop es
+ assume ds:NOTHING,es:DGROUP
+
+ jnz fndf_dosx_dir
+
+; Found WINDIR, copy over the path name, add child name and search
+
+ mov di,offset EXEC_PROGNAME ;copy WINDIR path
+ mov dx,di
+ call strcpy
+
+ cmp byte ptr es:[di-1],'\' ;add trailing \ if necessary
+ jz @f
+ mov byte ptr es:[di],'\'
+ inc di
+@@:
+ push es ;ds back to DGROUP
+ pop ds
+ assume ds:DGROUP
+
+ mov si,offset RELOC_BUFFER ;add child name
+ call strcpy
+
+ mov ax,4300h ;use get file attributes to check
+ int 21h
+ jc fndf_dosx_dir ;no there, go do next check
+ jmp fndf90 ;found it!
+
+; Next, we try looking in the directory that the Dos Extender was loaded
+; from. Start with the complete path to the Dos Extender program file.
+
+fndf_dosx_dir:
+
+ push es
+ pop ds
+ assume ds:DGROUP
+
+ mov si,offset EXEC_DXNAME
+ mov di,offset EXEC_PROGNAME
+ mov dx,di
+ call strcpy
+
+; Now, search backward from the end of the string until we find the
+; first backslash or colon. This will take us back past the file name
+; and leave us with the raw path.
+
+fndf24: dec di
+ cmp di,dx ;check if we have gone back past the start
+ ; of the string. (DX still has the address
+ ; of the start of the buffer from above)
+ jb fndf_cd ;and if so skip this part as the path is null.
+ mov al,es:[di]
+ cmp al,':'
+ jz fndf26
+ cmp al,'\'
+ jnz fndf24
+
+; Add the file name string onto the raw path and see if the file exists.
+
+fndf26: inc di
+ mov si,offset RELOC_BUFFER
+ call strcpy
+
+ mov ax,4300h ;use get file attributes to check
+ int 21h
+ jc fndf_cd
+ jmp short fndf90
+
+; We didn't find the file in the same directory as the Extender itself.
+; Now, try looking for the file in the current directory!
+
+fndf_cd:
+ mov di,offset EXEC_PROGNAME ;build current directory path string
+ dossvc 19h
+ add al,'A'
+ stosb ;drive letter
+
+ mov ax,'\:'
+ stosw
+
+ xor dl,dl
+ mov si,di
+ dossvc 47h ;current directory
+
+@@: lodsb ;find end of string
+ or al,al
+ jnz @b
+
+ mov byte ptr [si-1],'\' ;add ending \
+
+ mov di,si ;add base child name
+ mov si,offset RELOC_BUFFER
+ call strcpy
+
+ mov dx,offset EXEC_PROGNAME ;use get file attributes to check
+ mov ax,4300h
+ int 21h
+
+ jc fndf30
+ jmp short fndf90
+
+; We didn't find it in the current directory, look at the PATH
+; environment variable and see if the file can be found in any of
+; its directories. First off, search for the PATH environment
+; string.
+
+fndf30:
+ push es
+ mov di,offset DXCODE:szPath ;point ES:DI to path str
+ push cs
+ pop es
+ assume es:NOTHING
+
+ call GetEnv ;find the path env var
+ assume ds:NOTHING
+
+ pop es
+ assume es:DGROUP
+
+ jnz fndf80 ;Z flag set if FOUND
+
+; We are pointing at the beginning of the path environment variable. We need
+; to loop for each directory specified to see if we can find the file in
+; that directory.
+
+ mov bx,ds ;keep env segment in BX
+
+fndf40: mov ds,bx ;environment segment to DS
+ assume ds:NOTHING
+ mov al,ds:[si]
+ or al,al ;check for end of path variable.
+ jz fndf80 ;if so, we didn't find any directory with
+ ; the desired file in it.
+
+ mov di,offset EXEC_PROGNAME
+fndf42: lods byte ptr [si]
+ cmp al,';' ;is it the separator between strings in
+ jz fndf52 ; the environment variable?
+ or al,al ;is it the 0 at the end of the environment
+ jz fndf50 ; string?
+ stos byte ptr [di]
+ jmp fndf42
+
+fndf50: dec si
+fndf52: push si ;save pointer to start of next string
+ mov al,'\'
+ cmp al,byte ptr es:[di-1] ;dir name already end with \ (root dir?)
+ jnz fndf54
+ dec di ; yes, don't make it two \'s
+fndf54: stos byte ptr [di]
+ mov si,offset RELOC_BUFFER ;pointer to file base name
+ mov ax,es ;put our data segment address in DS
+ mov ds,ax
+ assume ds:DGROUP
+ call strcpy ;append file base name to path
+
+ mov ax,4300h ;use get file attributes to check
+ int 21h
+
+ pop si ;restore pointer to start of next string
+ jc fndf40
+ jmp short fndf90
+
+; Unable to find the file
+
+fndf80: stc
+
+; Finished, successful or not
+
+fndf90:
+ pop es
+ pop ds
+ popa
+ ret
+
+FindFile endp
+
+
+; -------------------------------------------------------
+; GetEnv -- This routine searches the environment for a
+; specific variable.
+;
+; Input: ES:DI - far pointer to wanted variable name
+; Output: DS:SI - far pointer to variable value string
+; Errors: return Z true if variable located
+; Uses: DS:SI modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetEnv
+
+GetEnv proc near
+
+ push ax
+ push dx
+ push di
+
+; Point DS:SI to our environment block
+
+ mov ds,segPSP
+ assume ds:PSPSEG
+ mov ds,segEnviron
+ xor si,si
+ assume ds:NOTHING
+
+; See if DS:SI is pointing at desired environment variable
+
+ mov dx,di ;keep var name offset in dx
+
+gete10:
+ cmp byte ptr es:[di],0 ;at end of variable name?
+ jz gete50 ; yes, go check '=' & optional blanks
+
+gete20:
+ lodsb ;get variable char
+ call toupper ;just in case...
+ cmp al,es:[di] ;match desired name so far?
+ jnz gete30 ; no, go find the next var name
+
+ inc di ;bump var pointer
+ jmp short gete10 ; and keep on checking
+
+gete30:
+ mov di,dx ;reset source name pointer
+
+ or al,al ;already at end of this env var?!
+ jz gete35
+
+@@: lodsb
+ or al,al ;find next environment var name
+ jnz @b
+gete35:
+ cmp byte ptr ds:[si],0 ;at end of environment?
+ jz gete80 ; yes, go fail the call
+ jmp short gete10 ; no, try try again
+
+; Found the env variable, now skip the '=' and any spaces
+
+gete50:
+ cmp byte ptr ds:[si],'=' ;when we get here, better be pointing
+ jnz gete30 ; at an '='
+ inc si
+
+@@: cmp byte ptr ds:[si],' ' ;skip any optional blanks
+ jnz @f
+ inc si
+ jmp short @b
+@@:
+ xor ax,ax ;set the Z flag
+ jmp short @f
+
+gete80:
+ or ax,si ;pretty sure this clears Z
+@@:
+ pop di
+ pop dx
+ pop ax
+
+ ret
+
+GetEnv endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxfunc.asm b/private/mvdm/dpmi/dxfunc.asm
new file mode 100644
index 000000000..510a48f8b
--- /dev/null
+++ b/private/mvdm/dpmi/dxfunc.asm
@@ -0,0 +1,1718 @@
+ PAGE ,132
+ TITLE DXFUNC.ASM -- Dos Extender Function Handlers
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXFUNC.ASM - Dos Extender Function Handlers *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains the functions for handling the Dos *
+;* Extender user functions. These are functions called by *
+;* the client application to request special Dos Extender *
+;* services. *
+;* *
+;* Any INT 2Fh requests that aren't Dos Extender functions *
+;* are handled by switching to real mode and passing control *
+;* on to the previous owner of the real mode INT 2Fh vector. *
+;* This is accomplished by jumping into the interrupt *
+;* reflector entry vector at the location for int 2fh. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 01/09/91 amitc At switch out time Co-Processor being reset*
+;* 11/29/90 amitc Replaced FnSuspend/FnResume by FnObsolete *
+;* These are not needed anymore for 3.1 *
+;* 11/29/90 amitc Modified RMInt2FHandler to respond to the *
+;* BuildChain SWAPI call. *
+;* 11/29/90 amitc Added a SWAPI CallIn function to be called *
+;* by the task switcher. *
+;* 11/16/90 jimmat Added DPMI MS-DOS Extension support *
+;* 08/08/90 earleh Started changes to make DOSX a DPMI server *
+;* 8/29/89 jimmat Added real mode Int 2Fh hook *
+;* 6/23/89 jimmat Added DOSX Info Int 2Fh *
+;* 6/16/89 jimmat Ifdef'd out most DOSX Int 2Fh services *
+;* 6/15/89 jimmat Added suspend/resume Int 2Fh hooks, and *
+;* Win/386 compatible Int 31h check *
+;* 6/14/89 jimmat Removed PTRACE hooks & unused DynaLink code *
+;* 5/19/89 jimmat Reduce # mode switches by ignoring Win/386 *
+;* Int 2Fh/1680h idle calls *
+;* 5/07/89 jimmat Added Int 2Fh protected mode hook to XMS *
+;* driver *
+;* 3/21/89 jimmat Corrected problem with jmping to wrong int *
+;* 2Fh handler if not for the DOS extender *
+;* 3/09/89 jimmat Added FNDynaLink function *
+;* 02/10/89 (GeneA): change Dos Extender from small model to *
+;* medium model *
+;* 01/24/89 (GeneA): removed all real mode dos extender *
+;* function handlers. *
+;* 09/29/88 (GeneA): created *
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;* *
+;****************************************************************
+
+ .286p
+ .287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include dosx.inc
+include woaswapi.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include hostdata.inc
+include intmac.inc
+include dpmi.inc
+include stackchk.inc
+include bop.inc
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+XMS_ID equ 43h ;XMS driver Int 2Fh ID
+XMS_INS_CHK equ 00h ;Installition check function
+XMS_CTRL_FUNC equ 10h ;Get Control Function Addr
+
+WIN386_FUNC equ 16h ;Windows Enhanced mode Int 2Fh ID
+
+WIN386_VER equ 00h ;Windows 386 version
+
+WIN386_INIT equ 05h ;Windows/386 & DOSX startup call
+
+WIN386_IDLE equ 80h ;Windows/386 idle notification
+W386_Get_Device_API equ 84h ;es:di -> device API
+W386_VCD_ID equ 0Eh ;Virtual Comm Device ID
+WIN386_INT31 equ 86h ;Windows/386 Int 31h availability check
+
+DPMI_DETECT equ 87h ;WIN386/DPMI detection call
+
+WIN386_GETLDT equ 88h ;Windows/386 Get LDT Base Selector call
+WIN386_KRNLIDLE equ 89h ;Windows/386 special Kernel idle notification
+
+DPMI_MSDOS_EXT equ 8Ah ;WIN386/DPMI MS-DOS Extensions detection call
+
+
+DPMI_VER equ 005ah ;version 0.90 served here
+DPMI_SUCCESS equ 0000h ;zero to indicate success
+DPMI_FAILURE equ 0001h ;non-zero for failure
+IFNDEF WOW
+DPMI_FLAGS equ 0000h ;flags (bit 0 = 32-bit program support)
+ELSE
+DPMI_FLAGS equ 0001h ;32 bit support
+ENDIF
+ ;DPMI client requesting 32-bit support
+
+DPMI_MSDOS_VER equ 0100h ;WIN386/DPMI MS-DOS Extensions version 01.00
+
+DPMI_MSDOS_API_GET_VER equ 0000h ;Get MS-DOS Extension version call
+DPMI_MSDOS_API_GET_LDT equ 0100h ;Get LDT Base selector call
+
+
+DISPCRIT_FUNC equ 40h ;Display driver critical section function
+DISPCRIT_ENTER equ 03h ;Enter critical section
+DISPCRIT_EXIT equ 04h ;Exit critical section
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn AllocateSelector:NEAR
+ extrn AllocateSelectorBlock:NEAR
+ extrn FreeSelector:NEAR
+ extrn FreeSelectorBlock:NEAR
+ extrn ParaToLinear:NEAR
+externFP NSetSegmentDscr
+externNP NSetSegmentAccess
+ extrn GetSegmentAddress:NEAR
+ extrn DupSegmentDscr:NEAR
+ extrn PMIntrEntryVector:NEAR
+ extrn XMScontrol:NEAR
+ extrn PMIntrDos:NEAR
+IFDEF WOW
+ extrn Wow16TransitionToUserMode:near
+ extrn Wow16CopyEhStack:near
+ extrn Wow16CopyIretStack:near
+ extrn Wow32TransitionToUserMode:near
+ extrn Wow32CopyEhStack:near
+ extrn Wow32CopyIretStack:near
+ extrn Wow32ReservedReflector:near
+ extrn Wow32IntrRefl:near
+ extrn PMReservedReflector:near
+ extrn Wow32HwIntrReflector:near
+ENDIF
+ extrn HookNetBiosHwInt:NEAR
+ extrn Wow16HwIntrReflector:near
+
+IFNDEF WOW
+ extrn InitXmemHeap:near
+ENDIF
+ extrn AllocateExceptionStack:NEAR
+
+DXSTACK segment
+
+ extrn rgw2FStack:WORD
+
+DXSTACK ends
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn segGDT:WORD
+ extrn segIDT:WORD
+ extrn bpGDT:FWORD
+ extrn bpIDT:FWORD
+ extrn selGDT:WORD
+ extrn selPSPChild:WORD
+ extrn segPSPChild:WORD
+ extrn idCpuType:WORD
+ extrn pbReflStack:WORD
+ extrn regUserSS:WORD
+ extrn regUserSP:WORD
+ extrn NoAsyncSwitching:BYTE
+ extrn HCB_List:WORD
+ extrn f286_287:BYTE
+
+ extrn DtaSegment:WORD
+ extrn DtaSelector:WORD
+ extrn DtaOffset:WORD
+
+
+if VCPI
+ extrn fVCPI:BYTE
+endif
+
+IFDEF ROM
+ extrn segDXCode:WORD
+ extrn PrevInt2FHandler:DWORD
+ENDIF
+IFDEF WOW
+ extrn WowTransitionToUserMode:WORD
+ extrn WowCopyEhStack:WORD
+ extrn WowCopyIretStack:WORD
+ extrn Wow16BitHandlers:WORD
+ extrn PMIntelVector:WORD
+ extrn selEHStack:WORD
+ extrn FastBop:FWORD
+ENDIF
+ extrn bReflStack:WORD
+ extrn HighestDxSel:WORD
+ extrn HighestSel:WORD
+ extrn HwIntHandlers:DWORD
+
+; The following variables are used as temporary storage during
+; function entry and exit.
+
+wPMUserAX dw ?
+selPMUserCS dw ?
+offPMUserIP dw ?
+selPMUserDS dw ?
+selPMUserSS dw ?
+offPMUserSP dw ?
+
+
+;
+; Count of DPMI clients active (that have entered protected mode).
+;
+
+ public cDPMIClients
+cDPMIClients dw 0
+
+ public selCurrentHostData, segCurrentHostData, DpmiFlags, DpmiSegAttr
+selCurrentHostData dw 0
+segCurrentHostData dw 0
+DpmiSegAttr dw 0
+DpmiFlags dw 0
+
+; define a switcher API call back info structure
+
+DxSwapiCallBackBlock db SIZE Switch_Call_Back_Info dup (0)
+DxSwapiApiInfo API_Info_Struc <SIZE API_Info_Struc,API_NETBIOS,3,10,API_SL_API>
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+ extrn ChildTerminationHandler:NEAR
+
+IFNDEF ROM
+ extrn segDXCode:WORD
+ extrn segDXData:WORD
+ extrn PrevInt2FHandler:DWORD
+ENDIF
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+ extrn DelayNetPosting:NEAR
+ extrn ResumeNetPosting:NEAR
+ extrn selDgroupPM:WORD
+ extrn WowHwIntDispatchProc:WORD
+
+
+;
+; This table dispatches to the function handlers for the Dos
+; Extender functions defined for the protected mode int 2Fh handler.
+
+pfnPmFunc label word
+ dw FnQueryDosExtender ;AL=0
+ dw FnObsolete ;AL=1 (removed for Windows 3.1)
+ dw FnObsolete ;AL=2 (removed for Windows 3.1)
+ dw FnAbort ;AL=3
+
+ EXTRN MakeLowSegment:PROC
+
+szMSDOS db 'MS-DOS',0
+
+IFDEF WOW
+ extrn WowReservedReflector:WORD
+ENDIF
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PROTECTED MODE FUNCTION HANDLER
+; -------------------------------------------------------
+;
+; PMInt2FHandler -- This routine will check for Dos Extender
+; function requests on the INT 2Fh vector. Valid Dos
+; Extender function requests are dispatched to the
+; appropriate function to process the request. Other
+; INT 2Fh requests will be reflected down to the real
+; mode INT 2Fh handler.
+;
+; This routine also handles XMS driver installation
+; check and get control function calls.
+;
+; Input: AH - DOS Extender Multiplex ID, OR XMS driver ID
+; AL - function number
+; Output: depends on function requested
+; Errors: depends on function requested
+; Uses: depends on function requested
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMInt2FHandler
+
+PMInt2FHandler:
+
+
+ cld ;practice 'safe programming'
+
+; Check if this is a Dos Extender Function.
+
+ cmp ah,WIN386_FUNC
+ jnz NotWin386
+
+; -------------------------------------------------------
+
+ cmp al,WIN386_KRNLIDLE ;If this is a Windows/386 idle call,
+ jnz @f ; just ignore it. This cuts down on
+Iret2f: iret ; pMode to rMode switching and helps
+@@: ; things like the Windows comm driver.
+ cmp al,WIN386_IDLE
+ jz Iret2f
+
+; -------------------------------------------------------
+
+ cmp al,WIN386_GETLDT ;Win/386 Get LDT Base Selector call?
+ jnz @f
+
+ cmp bx,0BADh ; yes, BX have the secret word?
+ jnz @f ; no, don't do it.
+
+ xor ax,ax ;yes, give it to 'em
+IFNDEF WOW
+ mov bx,SEL_LDT_ALIAS or STD_RING
+ELSE
+ mov bx,SEL_WOW_LDT or STD_RING
+ENDIF
+ iret
+@@:
+; -------------------------------------------------------
+
+ cmp al,WIN386_INT31 ;Windows/386 Int 31h availability check?
+ jnz @f
+
+ xor ax,ax ; yes, indicate Int 31h services are available
+ iret ; by setting AX to 0!
+@@:
+; WOW
+; -------------------------------------------------------
+
+ cmp al,W386_Get_Device_API ;returns es:di -> device API
+ jne @f
+
+IFDEF WOW ;VCD is installed on x86 only.
+ cmp bx,W386_VCD_ID
+ jne not_W386_VCD_ID
+ mov di, cs
+ mov es, di
+ mov di, offset DXPMCODE:VCD_PM_Svc_Call
+ iret
+
+not_W386_VCD_ID:
+ENDIF
+ xor di,di
+ mov es,di
+ iret
+@@:
+; -------------------------------------------------------
+; WOW
+
+ cmp al,DPMI_MSDOS_EXT ;Detect DPMI MS-DOS Extensions?
+ jnz Iret2f ;some other random Win386 to ignore
+
+ push es
+ push si
+ push di
+ push cx
+
+ push cs ;does DS:SI -> 'MS-DOS',0?
+ pop es
+ mov di,offset DXPMCODE:szMSDOS
+ mov cx,7
+ cld
+ rep cmpsb
+
+ pop cx
+ pop di
+ pop si
+ pop es
+ jnz Chain2F ;Chain int if not MS-DOS
+
+ xor ax,ax ;Indicate services are available
+ push cs ;Return API entry point in ES:DI
+ pop es
+ mov di,offset DXPMCODE:DPMI_MsDos_API
+ iret
+
+; -------------------------------------------------------
+
+NotWin386:
+ cmp ah,DISPCRIT_FUNC ;Display driver critical section?
+ jnz NotDisplay
+
+ cmp al,DISPCRIT_ENTER ;Simply eat the Display driver's
+ jz Iret2f ; enter/exit critical section calls.
+ cmp al,DISPCRIT_EXIT ; The don't apply in Standard mode.
+ jz Iret2f ; Again, to cut down on mode switches.
+
+; -------------------------------------------------------
+
+NotDisplay:
+ cmp ah,DOSXFunc ;DOS Extender Int 2Fh ID?
+ jnz @f ; no, what about XMS?
+
+ cmp al,DOSXLast ;valid DOSX func request?
+ jbe do2F ; yes, go do it
+
+; -------------------------------------------------------
+
+Chain2F:
+notDX: jmp PMIntrEntryVector + 3*2Fh ;reflect request to real mode
+
+@@:
+ cmp ah,XMS_ID ;XMS Driver Int 2Fh ID?
+ jnz notDX ; no, it's not ours
+
+ cmp al,XMS_INS_CHK ;It better be an XMS installation chk
+ jz do2F
+ cmp al,XMS_CTRL_FUNC ; or obtain XMS control func address
+ jnz notDX
+
+; -------------------------------------------------------
+
+do2F:
+
+; Save the caller's entry state and switch to the Dos Extender INT 2F stack.
+
+ push bp ;stack has following: [0] [2] [4] [6]
+ ; BP, IP, CS, FLAGS
+ mov bp,sp
+ push ds ;preserve the caller's DS
+ push SEL_DXDATA or STD_RING
+ pop ds
+ assume ds:DGROUP
+ pop selPMUserDS ;keep caller's DS here until we can get it
+ ; on the stack frame
+ mov wPMUserAX,ax ;preserve caller's AX also
+ mov ax,[bp+2] ;caller's IP
+ mov offPMUserIP,ax
+ mov ax,[bp+4] ;caller's CS
+ mov selPMUserCS,ax
+ mov ax,[bp+6] ;get the caller's flags
+ and ax,0FFFEh ;force carry flag to 0
+
+ mov selPMUserSS,ss ;preserve location of caller's stack
+ mov offPMUserSP,sp
+
+ push SEL_DXDATA or STD_RING
+ pop ss
+ mov sp,offset DGROUP:rgw2FStack
+
+ push offPMUserSP ;save caller's stack location on our own stack
+ push selPMUserSS
+
+ push ax ;caller's flags go on our stack frame
+ push selPMUserCS ;then his CS and IP
+ push offPMUserIP
+
+ mov ax,wPMUserAX
+ pusha ;now all of caller's general registers
+ push selPMUserDS
+ push es
+ push SEL_DXDATA or STD_RING
+ pop es
+ mov bp,sp ;set up stack frame pointer
+
+; Dispatch to the function handler for the requested function. The stack
+; will have the following contents upon entry to the handler.
+
+; SP[1E] Users SP
+; SP[1C] Users SS
+; SP[1A] Users FLAGS (with carry cleared)
+; SP[18] Users CS
+; SP[16] Users IP
+; SP[14] Users AX
+; SP[12] Users CX
+; SP[10] Users DX
+; SP[E] Users BX
+; SP[C] Our SP + 16h
+; SP[A] Users BP
+; SP[8] Users SI
+; SP[6] Users DI
+; SP[4] Users DS
+; SP[2] Users ES
+; SP[0] Return Offset
+
+ FSTI
+
+ cmp ah,XMS_ID ;XMS Int 2Fh request?
+ jnz @f
+ call XMSfunc ; yes, use this handler
+ jmp short i2F_ret
+@@:
+ xor ah,ah
+ add ax,ax
+ mov bx,ax
+
+ call pfnPmFunc[bx] ; no, use this one instead
+
+i2F_ret:
+
+; Return from Dos Extender function back to caller.
+
+ FCLI
+ pop es
+ pop selPMUserDS
+ popa
+ mov wPMUserAX,ax
+ pop offPMUserIP
+ pop selPMUserCS
+ pop ax ;get back new flags
+ pop selPMUserSS
+ pop offPMUserSP
+ mov ss,selPMUserSS
+ mov sp,offPMUserSP
+ mov [bp+6],ax ;store flags back into user iret frame
+ mov ax,selPMUserCS ;user's CS
+ mov [bp+4],ax
+ mov ax,offPMUserIP ;user's IP
+ mov [bp+2],ax
+ mov ax,wPMUserAX
+ mov ds,selPMUserDS
+ pop bp
+ riret
+
+
+; WOW
+; -------------------------------------------------------
+;
+; Simulate VCD API's. DX contains function number.
+;
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public VCD_PM_Svc_Call
+
+VCD_PM_Svc_Call:
+ DPMIBOP VcdPmSvcCall32
+ retf
+
+; -------------------------------------------------------
+; WOW
+
+; -------------------------------------------------------
+;
+; -------------------------------------------------------
+ subttl Dos Extender Function Routines
+ page
+; -------------------------------------------------------
+; DOS EXTENDER FUNCTION ROUTINES
+; -------------------------------------------------------
+;
+; The following functions are the entry points for handling
+; the Dos Extender functions. The general convention for
+; these functions is that BP points to a stack frame that
+; has all of the original caller's registers. Any parameters
+; needed and values to be returned to the user are taken from
+; or placed in this stack frame. The layout of the stack frame
+; is defined by the structure FUNCSTACK.
+;
+; -------------------------------------------------------
+; FnQueryDosExtender -- This function identifies if the
+; Dos Extender is resident in the machine.
+;
+; Input:
+; Output: user AX - dos extender version number
+; user BX - 'DX'
+; Errors: none
+; Uses: all registers used
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public FnQueryDosExtender
+
+FnQueryDosExtender:
+ mov [bp].fnsUserAX,DXVERSION
+ mov [bp].fnsUserBX,'DX'
+;
+fnqx90: ret
+
+; -------------------------------------------------------
+; FnObsolete -- This function is invoked when the now obsolete
+; DOSX INT 2FH function is called with AL=1 or 2.
+; These were valid functions for Windows 3.0.
+;
+; Input:
+; Output:
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+
+FnObsolete proc near
+
+ ret
+
+FnObsolete endp
+; -------------------------------------------------------
+; FnAbort -- This function is invoked when the application
+; asks to be aborted. This is an emergency exit that
+; should only be used in the most dire circumstance.
+;
+; Input:
+; Output:
+; Errors: none
+; Uses: all registers used
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public FnAbort
+
+FnAbort proc near
+
+; Tell DOS that we are the child app, and then do a DOS exit. This
+; should result in DOS going to ChildTerminationHandler, where we
+; do final clean-up and exit ourselves.
+
+ mov ah,50h ;tell dos that the child is running
+ mov bx,selPSPChild
+
+ rpushf ;Don't do an Int 21h in case the
+ FCLI ; child has hooked it for some reason
+ push cs
+ call PMIntrDOS
+
+ mov ax,4C02h ;now exit the child with abort status
+
+ rpushf
+ FCLI
+ push cs
+ call PMIntrDOS
+
+FnAbort endp
+
+; -------------------------------------------------------
+; XMSfunc - The following routine provides a protected mode
+; service for XMS Int 2Fh services. Two services are
+; implemented: XMS driver installation check, and obtain
+; XMS driver control function address.
+;
+; Input: UserAL - function request
+; Output: UserAL - XMS driver installed flag, or
+; UserES:BX - XMS driver control function address
+; Errors: none
+; Uses: all registers may be used
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public XMSfunc
+
+XMSfunc proc near
+
+ cmp al,XMS_INS_CHK ;XMS driver installation check?
+ jnz @f
+
+ mov byte ptr [bp].fnsUserAX,80h ;indicate driver is installed
+ ret
+
+@@:
+
+; It must be an obtain XMS driver control function address request
+
+ mov [bp].fnsUserBX,offset DXPMCODE:XMScontrol
+ mov [bp].fnsUserES,cs
+
+ ret
+
+XMSfunc endp
+
+; -------------------------------------------------------
+ subttl DPMI MS-DOS Extension API
+ page
+; -------------------------------------------------------
+; DPMI MS-DOS EXTENSION API
+; -------------------------------------------------------
+;
+; The following routine implements the DPMI MS-DOS Extensions
+; API. This API must be 'detected' by the use of the
+; DMPI_MSDOS_EXT Int 2F function (above).
+
+DPMI_MsDos_API proc far
+
+ cmp ax, DPMI_MSDOS_API_GET_VER ;Get version call?
+ jne DPMI_MsDos_API_Not_Ver
+
+ mov ax,DPMI_MSDOS_VER
+ jmp short DPMI_MsDos_API_Exit
+
+DPMI_MsDos_API_Not_Ver:
+
+ cmp ax, DPMI_MSDOS_API_GET_LDT ;Get LDT Base call?
+ jne DPMI_MsDos_Api_Failed
+ifdef WOW
+ mov ax,SEL_WOW_LDT or STD_RING ; yup, give it to 'em
+else
+ mov ax,SEL_LDT_ALIAS or STD_RING ; yup, give it to 'em
+endif
+
+DPMI_MsDos_API_Exit:
+
+ clc ;Succss
+ ret
+
+DPMI_MsDos_API_Failed:
+
+ stc ;Unsupported function
+ ret
+
+DPMI_MsDos_API endp
+
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+; -------------------------------------------------------
+;
+; -------------------------------------------------------
+;
+; DPMI_Client_Pmode_Entry -- This routine is the entry
+; point for a DPMI client to switch to protected mode.
+; Reference: DOS Protected Mode Interface Specification 0.9
+; Section 5.2: Calling the Real to Protected
+; Mode Switch Entry Point
+;
+; Entry: AX = Flags
+; Bit 0 = 1 if program is a 32-bit application
+; ES = Real mode segment of DPMI host data area. This is the
+; size of the data area we specified in RMInt2FHandler,
+; below.
+;
+; Returns:
+; Success: Carry clear.
+; Program is in protected mode.
+; CS = 16-bit selector with base of real mode
+; CS and a 64k limit.
+; SS = 16-bit selector with base of real mode
+; SS and a 64k limit.
+; DS = 16-bit selector with base of real mode
+; DS and a 64k limit.
+; ES = Selector to program's PSP with a 100h
+; byte limit.
+; 80386, 80486:
+; FS, GS = 0
+; All other registers preserved.
+;
+; Failure: Carry flag set.
+; Program is in real mode.
+;
+; Exceptions:
+; 32-bit programs not (yet) supported. Any attempt to load
+; a 32-bit program by this mechanism returns failure.
+;
+; The only error that can occur here is a failure to allocate
+; sufficient selectors.
+;
+
+;
+; Structure of the stack frame used to store the client's registers
+; while implementing the DPMI protected mode entry.
+;
+
+DPMI_Client_Frame STRUC
+ Client_ES dw ? ; Client's ES
+ Client_DS dw ? ; Client's DS
+ Client_DI dw ? ; Client's DI
+ Client_SI dw ? ; Client's SI
+ Client_Pusha_BP dw ? ; BP at pusha
+ Client_Pusha_SP dw ? ; SP at pusha
+ Client_BX dw ? ; Client's BX
+ Client_DX dw ? ; Client's DX
+ Client_CX dw ? ; Client's CX
+ Client_AX dw ? ; Client's AX
+ Client_Flags dw ? ; Client's flags
+ Client_IP dw ? ; Client's IP, lsw of return
+ Client_CS dw ? ; Client's CS, msw of return
+DPMI_Client_Frame ENDS
+
+ public DPMI_Client_Pmode_Entry
+DPMI_Client_Pmode_Entry proc far
+;
+; Reject any 32-bit program requests.
+;
+
+IFNDEF WOW
+ test ax, DPMI_32BIT ; 32-bit application?
+ stc ; yep, refuse to do it
+ jz dcpe_flags_ok ; no, try to get into Pmode
+ jmp dcpe_x
+dcpe_flags_ok:
+ENDIF
+
+IFDEF WOW
+ stc
+ENDIF
+ pushf ;save client's flags (with carry set)
+ pusha ;save caller's general registers
+ push ds ;save caller's DS
+ push es ;save caller's ES
+ mov bp, sp ;create the stack frame
+ dossvc 62h ;now get caller's PSP address
+ mov di, bx ;and store it here for a while
+IFDEF FLATAPIXLAT
+ push es
+ dossvc 2fh ;get caller's dta address
+ENDIF
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ax, segDXData ;get our DGROUP
+ mov ds, ax ;point to it
+ENDIF
+
+IFDEF FLATAPIXLAT
+ mov DtaSegment,es
+ mov DtaOffset,bx
+ pop es
+ENDIF
+
+;
+; For now, we only support one DPMI client application at a time.
+;
+;; cmp cDPMIClients,0 ;Any active?
+;; je @F
+;; jmp dcpe_return
+@@:
+
+ ;
+ ; Remember the highest selector if we haven't yet
+ ;
+ cmp HighestDxSel,0
+ jne @f
+
+ mov ax,HighestSel
+ mov HighestDxSel,ax
+
+@@: mov ax,[segCurrentHostData]
+ mov es:[HdSegParent],ax ; save rm link
+ mov ax,es
+ mov [segCurrentHostData],ax
+ mov ax,[bp].Client_AX ; get dpmi flags
+ mov es:[HdFlags],ax ; save for future reference
+ mov DpmiFlags,ax
+ test ax,DPMI_32BIT
+ jne cpe10
+
+ mov DpmiSegAttr,0
+ jmp cpe20
+
+cpe10: mov DpmiSegAttr,AB_BIG
+
+cpe20: mov si, ss ;SI = caller SS
+
+ mov ax, ds ;use a DOSX stack during the mode
+ ;switch
+ FCLI
+ mov ss, ax
+ mov sp, offset DGROUP:rgw2FStack
+
+ SwitchToProtectedMode
+
+ mov ax, si ;make a selector for client's stack
+ mov bx, STD_DATA
+ or bx, DpmiSegAttr
+ call MakeLowSegment
+ jnc got_client_stack_selector
+ jmp dcpe_error_exit ;back out if error
+got_client_stack_selector:
+
+ or al,STD_TBL_RING
+
+ mov ss, ax ;back to client's stack
+IFNDEF WOW
+ mov sp, bp
+ELSE
+.386p
+ movzx esp,bp
+.286p
+ENDIF
+
+; push [bp.Client_Flags] ;enable interupts if client had
+; npopf ;them enabled
+
+;
+; After DOSX enters protected mode, convert the caller's segment registers
+; to PMODE selectors, replacing the values in the client's register image
+; on the stack. First, allocate the three or four selectors we will need.
+;
+ xor ax,ax ;an invalid selector
+ push ax ;marker
+
+ mov cx,4 ;CS, PSP, Environ, Host data
+ cmp si,[bp.Client_DS] ;Client SS == Client DS ?
+ je dcpe_allocate_loop
+ inc cx
+dcpe_allocate_loop:
+ call AllocateSelector
+ jnc @F
+ jmp dcpe_pfail
+@@:
+ or al,STD_TBL_RING
+ push ax
+ loop dcpe_allocate_loop
+
+ mov dx,[bp.Client_CS] ;get client CS paragraph
+ call ParaToLinear ;convert to linear address in BX:DX
+ mov cx,0ffffh ;limit = 64k
+ pop ax ;get one of the selectors allocated
+ mov [bp.Client_CS],ax ;save value for client
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_CODE>
+
+
+ mov dx, [bp.Client_DS]
+ mov [bp.Client_DS],ss ;DS = SS for now
+ cmp dx, si ;need separate DS selector?
+ je dcpe_do_child_PSP
+
+ call ParaToLinear ;convert to linear address in BL:DX
+ mov cx,0ffffh ;limit = 64k
+ pop ax ;get another selector
+ mov [bp.Client_DS],ax ;save value for client
+ push di
+ mov di,STD_DATA
+ or di,DpmiSegAttr
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,di>
+ pop di
+
+dcpe_do_child_PSP:
+ mov dx,[bp.Client_ES] ; get HostData selector
+ call ParaToLinear
+ mov cx,HOST_DATA_SIZE ; limit = size of HostData
+ pop ax ; get another selector
+ push [selCurrentHostData]
+ mov [selCurrentHostData],ax ; save for us
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+ mov es,ax
+ pop ax
+ mov es:[HdSelParent],ax
+ mov ax,SelPSPChild
+ mov es:[HdPSPParent],ax
+
+ mov dx, di ;get client PSP paragraph
+ mov segPSPChild, di
+ call ParaToLinear ;convert to linear address in BL:DX
+ mov cx,100h ;limit = 100h
+ pop ax ;get another selector
+ mov [bp.Client_ES],ax ;save value for client
+ mov selPSPChild, ax ;save a copy for DOSX
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ mov es:[HdSelPSP],ax
+ mov es,ax ;point to client's PSP
+ mov dx,es:segEnviron ;fetch client's environment pointer
+ call ParaToLinear ;convert to linear address in BL:DX
+ mov cx,0ffffh ;limit = 32k
+ pop ax ;get another selector
+ mov es:segEnviron,ax ;save client's environment selector
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+IFDEF FLATAPIXLAT
+; We need to set up the DTA selector
+ mov dx,DtaSegment
+ cmp dx,segPSPChild
+ jne dcpe_50
+
+ mov dx,selPSPChild
+ mov DtaSelector,dx
+ jmp dcpe_60
+
+dcpe_50:
+ mov cx,1
+ call AllocateSelector
+ jnc @f
+
+ jmp dcpe_free_client_stack
+
+@@: or al,STD_TBL_RING
+ mov DtaSelector,ax
+ call ParaToLinear
+ cCall NSetSegmentDscr,<ax,bx,dx,0,0ffffh,STD_DATA>
+
+dcpe_60:
+ENDIF
+
+ inc cDPMIClients ;increment count of Pmode clients
+ cmp cDPMIClients, 1 ; first client?
+ jne @f ; already taken care of
+
+ call AllocateExceptionStack
+
+IFDEF WOW
+ ; Note: We have to do this before we try to hook the netbios
+ ; interrupt. If we don't, we will fault in the dos extender.
+ ; (HookNetBiosHwInt calls int 21)
+ call DpmiStackSizeInit
+ENDIF
+ call HookNetBiosHwInt
+IFDEF WOW
+ call DpmiSizeInit
+ FBOP BOP_DPMI,DpmiInUse,FastBop
+ELSE
+;
+; We initialize the extended memory heap here for mips, so
+; we can free it when the last application terminates. Extended
+; memory allocations before this point are allocated directly from
+; xms
+;
+ call InitXmemHeap
+ DPMIBOP DpmiInUse
+ENDIF
+@@:
+;
+; Everything OK. Clear error flag, and return to caller.
+;
+ not byte ptr [bp.Client_Flags]
+ ;reverse status flags, clearing carry
+
+
+; Let 32 bit code know if this is a 32 or 16 bit application
+ mov ax,DpmiFlags
+ push selPSPChild
+ push DtaSelector
+ push DtaOffset
+ FBOP BOP_DPMI,InitApp,FastBop
+ add sp,4
+
+
+; jmp far ptr dcpe_return ;avoid need for fix ups
+ db 0EAh
+ dw offset DXCODE:dcpe_return
+ dw SEL_DXCODE OR STD_RING
+;
+; If we get here, it means DOSX failed to allocate enough selectors for the
+; client. Deallocate those which have been allocated, switch back to
+; real mode, and return an error to the caller. Selectors to deallocate
+; are on the stack, pushed after a zero word. Then switch to a DOSX stack,
+; deallocate the client stack selector, and switch to real mode.
+;
+dcpe_pfail:
+ pop ax ;any selectors allocated?
+ or ax,ax ; (we pushed a zero before allocating)
+ jz dcpe_free_client_stack ;done
+ call FreeSelector ;free the selector
+ jnc dcpe_pfail ;free any more
+dcpe_free_client_stack:
+ mov di, ss ;make copy of client stack selector
+ mov ax, ds ;have to be on a DOSX stack to do this
+ FCLI
+ mov ss, ax
+ mov sp, offset DGROUP:rgw2FStack
+ mov ax, di ;free client stack selector
+ call FreeSelector
+dcpe_error_exit:
+;
+; Error exit from protected mode. Any allocated selectors have already
+; been freed. Switch to real mode, restore client stack, pop off client's
+; registers, return with the carry flag set.
+;
+ SwitchToRealMode
+ mov ss, si ;restore client stack
+
+ errnz <dcpe_return-$>
+
+dcpe_return: ; The next line must restore the stack.
+
+ mov sp, bp
+
+ jc dcpe_return_1 ; error return
+;
+; Pop the client's registers off the stack frame, switch back to the
+; client's stack, and return.
+;
+dcpe_return_1:
+ pop es ;pop copy of PSP selector/segment
+ pop ds ;pop client DS selector/segment
+ popa ;pop client's general registers
+ npopf ;restore interrupt flag, return status
+dcpe_x:
+ retf ;and out of here
+DPMI_Client_Pmode_Entry endp
+
+; -------------------------------------------------------
+; REAL MODE FUNCTION HANDLER
+; -------------------------------------------------------
+;
+; RMInt2FHandler -- This routine hooks the real mode Int 2Fh chain
+; and watches for 'interesting' Int 2Fh calls.
+;
+; WIN386/DOSX startup broadcast
+; DPMI server detection
+; Switcher API functions
+;
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public RMInt2FHandler
+
+RMInt2FHandler proc near
+
+
+IFDEF ROM
+ push ds
+ SetRMDataSeg
+ assume ds:DGROUP
+ENDIF
+ cmp ah,WIN386_FUNC ;WIN386/DOSX/DPMI call?
+ jz rm2f_0
+ cmp ax,SWAPI_BUILD_CHAIN ;build chain call ?
+ jz RM2F_SwAPI ;yes.
+
+rm2f_chain:
+
+IFDEF ROM
+
+; Chain the interrupt without a code segment variable
+
+ push bp
+ mov bp,sp ; bp -> [bp] [ds] [ip] [cs] [fl]
+
+ push word ptr PrevInt2FHandler[2]
+ push word ptr PrevInt2FHandler
+
+ mov ds,[bp+2] ; restore entry DS
+ mov bp,[bp] ; .. BP ..
+ retf 4 ; .. chain & clean up stack
+ELSE
+ jmp [PrevInt2FHandler] ;no, just chain it on...
+ENDIF
+
+RM2F_SwAPI:
+
+; call down stream first.
+
+IFDEF ROM
+
+ pop ds ;restore the saved ds
+ assume ds:NOTHING
+
+; prepare an iret frame on the stack.
+
+ pushf
+ FCLI ;prepare call-iret frame
+ push cs ;the chaining code pops this
+ push offset RM2F_SWAPI_BackFromChaining
+ push ds ;save ds again.
+ SetRMDataSeg
+ jmp short rm2f_chain
+
+RM2F_SWAPI_BackFromChaining:
+
+ELSE
+
+ pushf
+ FCLI
+ call [PrevInt2FHandler]
+
+ENDIF
+
+ push ax
+ push dx ;save
+ mov ax,es
+ mov dx,bx ;ax:bx has current ES:BX
+
+; get to our data segment
+
+ push ds ;save
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,[segDXData] ;load our data segment
+ENDIF
+
+ assume ds:DGROUP
+
+; fill up the Switcher Info Call Back Block
+
+ push ds
+ pop es
+ lea bx, DxSwapiCallBackBlock;ES:BX points to our node
+
+; save the address of the next node.
+
+ mov word ptr es:[bx.SCBI_Next],dx
+ mov word ptr es:[bx.SCBI_Next][2],ax
+
+; save the far address of our SWAPI call in function.
+
+ lea ax,DxSwapiCallIn ;address of call in function
+ mov word ptr es:[bx.SCBI_Entry_Pt],ax
+ mov word ptr es:[bx.SCBI_Entry_Pt][2],cs
+ lea ax,DxSwapiApiInfo ;address of call in function
+ mov word ptr es:[bx.SCBI_Api_Ptr],ax
+ mov word ptr es:[bx.SCBI_Api_Ptr][2],ds
+
+RM2F_SWAPI_Ret:
+
+ pop ds ;restore
+ assume ds:NOTHING
+
+ pop dx
+ pop ax ;restore
+ iret
+
+rm2f_0:
+
+IFDEF ROM
+ assume ds:DGROUP
+ENDIF
+if 0 ; don't claim to be win 3.1 in enhanced mode
+ cmp al,WIN386_INIT ;WIN386/DOSX startup attempt?
+ jnz rm2f_1 ;no
+ mov cx,-1 ;yes, don't let'm load
+ jmp rm2f_x
+endif
+ cmp al,W386_Get_Device_API ;not supported
+ jne rm2f_1
+
+ xor di,di
+ mov es,di
+ jmp rm2f_x
+
+rm2f_1:
+ cmp al,DPMI_DETECT ;DPMI detection?
+ jnz rm2f_2 ;no
+
+ mov ax,DPMI_SUCCESS ;yes, return Pmode switch entry
+ mov bx,DPMI_FLAGS ;flags
+
+IFDEF ROM
+ mov cl,byte ptr [idCpuType] ;CPU type
+ELSE
+ push segDXData
+ pop es
+ assume es:DXDATA
+ mov cl,byte ptr es:[idCpuType] ;CPU type
+ assume es:nothing
+ENDIF
+ mov dx,DPMI_VER ;DPMI server version
+ mov si,(HOST_DATA_SIZE + 15) / 16
+ push cs ;entry point is in this segment
+ pop es ;prospective client wants
+ lea di,DPMI_Client_Pmode_Entry ;switch entry point in ES:DI
+ jmp rm2f_x ;done
+rm2f_2:
+if 0 ; don't claim to be windows
+ cmp al,WIN386_VER ;Windows 386 version check?
+ jnz rm2f_chain ;no, chain the interrupt
+
+ mov ax, 0a03h
+else
+ jmp rm2f_chain
+endif
+rm2f_x:
+IFDEF ROM
+ pop ds
+ assume ds:NOTHING
+ENDIF
+ iret
+
+RMInt2FHandler endp
+
+;--------------------------------------------------------------------------------
+; DxSwapiCallIn
+;
+; DESCRIPTION This routine handles all the Call Outs that the Switcher may
+; make. We are only interested in the SWAPI_SUSPEND and
+; SWAPI_SESSION_ACTIVE calls. Between these two calls, the main
+; DOSX code will not be active and we have to make sure that
+; the Global NetBios stub code does not call the main POST
+; routine in the DOSX.
+;
+; ENTRY:
+; AX = SWAPI CallOut function code.
+;
+; EXIT:
+; AX = 0 (OK to Switch or resume)
+; CY = Clear
+;
+; USES:
+; AX, Flags
+;------------------------------------------------------------------------------
+DxSwapiCallIn proc far
+
+ assume ds:NOTHING, es:NOTHING, ss:NOTHING
+
+; check to see if we are interested in the function or not.
+
+ cmp ax,SWAPI_SUSPEND ;suspend call ?
+ jz DXSCI_Interested ;yes, we are interested in it.
+ cmp ax,SWAPI_SESSION_ACTIVE ;session active call ?
+ jnz DXSCI_Ret ;ignore call with success
+
+DXSCI_Interested:
+
+; we need access to our data segment.
+
+ push ds ;save
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,[segDXData] ;load our data segment
+ENDIF
+
+ assume ds:DGROUP ;we have our data segment
+
+; now switch to protected mode
+
+ mov regUserSP,sp
+ mov regUSerSS,ss
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,segDXData
+ENDIF
+ mov sp,pbReflStack
+
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+
+ push regUserSS ;save current stack loc on our stack
+ push regUserSP ; so we can restore it later
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ push ax ;save function code
+ SwitchToProtectedMode ;destroys ax
+
+; --------------- START OF PROTECTED MODE CODE -------------------------
+
+ pop ax ;restore function code
+
+; check the function type
+
+ cmp ax,SWAPI_SUSPEND ;is it a suspend call ?
+ jz DXSCI_Suspend ;yes it is
+
+; it must be a resume session call. If [NoAsyncSwitching] is set, we would
+; have switched out only because there were no asynchronous requests pending
+; in this case we do not have to do the ResumeNetPosting call.
+
+ cmp [NoAsyncSwitching],0 ;is this the case discussed above ?
+ jnz DXSCI_SessionActiveOk ;yes.
+
+ call ResumeNetPosting ;call the code to allow normal posting
+
+DXSCI_SessionActiveOk:
+
+ xor ax,ax ;successful
+ jmp short DXSCI_BackToRM ;go back to real mode and return
+
+DXSCI_Suspend:
+
+; check to see whether we can switch out when asynchronous calls are pending
+; or not.
+
+ cmp [NoAsyncSwitching],0 ;can we switch out with async calls pending ?
+ jz DXSCI_Suspend_OK ;yes, global stub will take care
+
+; do we have any outstanding asynchronous requests ?
+
+ xor ax,ax ;assume no requests outstanding
+ cmp [HCB_List],0 ;requests outstanding ?
+ jz DXSCI_BackToRM ;no, ok to switch.
+
+; asynchronous requests are outstanding, we should allow a switch now.
+
+ mov ax,1 ;suspend should fail
+ jmp short DXSCI_BackToRM ;go back.
+
+DXSCI_Suspend_OK:
+
+; suspend normal net posting.
+
+ call DelayNetPosting ;Net posting delayed
+
+; if this is a 286 system with a 287 Co-Processor, we should reset the
+; Co-Processor to get it out of protected mode.
+; Note: The Switcher is going to save the Co-Processor state after this,
+; however, according to C conventions, the Co-Processor state may not be
+; maintained accross function call and the Switcher switches Windows out
+; from within a message body in Winoldap. Thus, it is OK to alter the state
+; by resting the Co-Processor.
+
+ xor ax,ax ;successfull.
+ cmp [f286_287],0 ;is this a 80286 & 80287 combination?
+ jz DXSCI_BackToRM ;no.
+
+; we have a 286 and 287 configuration. Reset the Co-Processor to get it into
+; real mode.
+
+ out 0F1H,al ;reset the Co Processor
+ or al,al ;set back zero flag
+
+DXSCI_BackToRM:
+
+ pop regUserSP ;recover previous stack location
+ pop regUserSS
+
+ push ax ;save return code
+ SwitchToRealMode ;Switch back to real mode.
+
+; --------------- START OF REAL MODE CODE ------------------------------
+
+ pop ax ;restore return code
+
+; Switch back to the original stack, deallocate the interrupt stack frame,
+; and return to the network software
+
+ CHECK_STACK
+ mov ss,regUserSS
+ mov sp,regUserSP
+ add pbReflStack,CB_STKFRAME
+
+ pop ds ;restore
+ assume ds:NOTHING
+
+ jmp short DXSCI_End ;ax has return code
+
+DXSCI_Ret:
+
+ xor ax,ax ;indicate success
+
+DXSCI_End:
+
+ ret
+
+DxSwapiCallIn endp
+;--------------------------------------------------------------------------------
+
+DXCODE ends
+
+IFDEF WOW
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+;----------------------------------------------------------------------
+;
+; DpmiSizeInit -- This routine insures that the appropriately sized
+; interrupt handlers will be called
+;
+; Inputs: None
+; Outputs: None
+;
+ public DpmiSizeInit
+ assume ds:dgroup,es:nothing,ss:nothing
+DpmiSizeInit proc
+
+ push ax
+ push bx
+ push cx
+ push si
+ push di
+ push es
+ rpushf
+ FCLI
+ call AllocateSelector
+ mov bx,ax
+ mov ax,cs
+ call DupSegmentDscr
+ cCall NSetSegmentAccess,<bx,STD_DATA>
+ mov es,bx
+ assume es:DXPMCODE
+ test DpmiFlags,DPMI_32BIT
+ jnz dsi20
+
+ mov [WowTransitionToUserMode],offset DXPMCODE:Wow16TransitionToUserMode
+ mov [WowCopyEhStack],offset DXPMCODE:Wow16CopyEhStack
+ mov [WowCopyIretStack],offset DXPMCODE:Wow16CopyIretStack
+ mov [WowReservedReflector],offset DXPMCODE:PMReservedReflector
+ mov [WowHwIntDispatchProc], offset DXPMCODE:Wow16HwIntrReflector
+ assume es:nothing
+ mov ax,es
+ call FreeSelector
+ xor ax,ax
+ mov es,ax
+
+ cCall NSetSegmentAccess,<selDgroupPM,STD_DATA>
+ cCall NSetSegmentAccess,<selEHStack,STD_DATA>
+
+ jmp dsi90
+dsi20:
+ assume es:DXPMCODE
+ mov [WowTransitionToUserMode],offset DXPMCODE:Wow32TransitionToUserMode
+ mov [WowCopyEhStack],offset DXPMCODE:Wow32CopyEhStack
+ mov [WowCopyIretStack],offset DXPMCODE:Wow32CopyIretStack
+ mov [WowReservedReflector],offset DXPMCODE:Wow32ReservedReflector
+ mov [WowHwIntDispatchProc], offset DXPMCODE:Wow32HwIntrReflector
+ assume es:nothing
+ mov ax,es
+ call FreeSelector
+ xor ax,ax
+ mov es,ax
+;
+; Copy 16 bit handler addresses
+;
+.386p
+ lea di,Wow16BitHandlers
+
+ mov ax,ds
+ mov es,ax
+ assume es:DGROUP
+
+ push ds
+ mov ax,SEL_IDT OR STD_RING
+ mov ds,ax
+ assume ds:nothing
+
+ mov si,0
+ mov cx,256
+dsi40: movsd
+ add si,4
+ loop dsi40
+ pop ds
+
+;
+; get the correct hw interrupt handlers
+;
+ mov di,offset Wow16BitHandlers + 8*4
+ mov si,offset HwIntHandlers
+ mov cx,8
+dsi41: mov ax,word ptr [si]
+ mov [di],ax
+ mov ax,word ptr [si + 4]
+ mov [di + 2],ax
+ add di,4
+ add si,8
+ loop dsi41
+
+ mov di,offset Wow16BitHandlers + 070h * 4
+ mov cx,8
+dsi43: mov ax,word ptr [si]
+ mov [di],ax
+ mov ax,word ptr [si + 4]
+ mov [di + 2],ax
+ add di,4
+ add si,8
+ loop dsi43
+
+;
+; Put 32 bit handlers into IDT
+;
+
+ push ds
+ mov ax,SEL_IDT OR STD_RING
+ mov ds,ax
+
+ mov bx,0
+ mov si,bx
+ mov di,offset DXPMCODE:Wow32IntrRefl
+ mov cx,8
+dsi50: mov [si],di
+ mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_TRAP_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi50
+
+ mov cx,8
+dsi55: mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_INT_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi55
+
+ mov cx,70h - 10h
+dsi60: mov [si],di
+ mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_TRAP_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi60
+
+ mov cx,8
+dsi70: mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_INT_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi70
+
+ mov cx,0ffh - 78h
+dsi80: mov [si],di
+ mov word ptr [si + 2],SEL_DXPMCODE OR STD_RING
+ mov word ptr [si + 6],0
+ push word ptr VDM_INT_TRAP_GATE OR VDM_INT_32
+ push bx
+ push word ptr [si + 2]
+ push word ptr [si + 6]
+ push word ptr [si]
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+ inc bx
+ add si,8
+ add di,6
+ loop dsi80
+
+ pop ds
+ assume ds:DGROUP
+
+;
+; Set up HwIntHandlers
+;
+
+ mov dx,offset DXPMCODE:Wow32IntrRefl + 8*6
+ mov si,offset HwIntHandlers
+ mov cx,8
+dsi83: 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,6
+ loop dsi83
+
+ mov cx,8
+ mov dx,offset DXPMCODE:Wow32IntrRefl + 070h * 6
+dsi87: mov word ptr [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,6
+ loop dsi87
+
+dsi90: rpopf
+ pop es
+ pop di
+ pop si
+ pop cx
+ pop bx
+ pop ax
+ ret
+DpmiSizeInit endp
+
+ assume ds:DGROUP, es:NOTHING, ss:NOTHING
+DpmiStackSizeInit proc
+
+ push ax
+ test DpmiFlags,DPMI_32BIT
+ jz @f
+;
+; Make the dgroup selector 32 bit
+;
+; NOTE: The following equ is only necessary to get the cmacro package
+; to pass the correct value to NSetSegmentAccess
+
+NEW_DX_DATA equ STD_DATA OR AB_BIG
+ cCall NSetSegmentAccess,<selDgroupPM,NEW_DX_DATA>
+ cCall NSetSegmentAccess,<selEHStack,NEW_DX_DATA>
+.286p
+
+@@:
+ pop ax
+ ret
+
+DpmiStackSizeInit endp
+
+DXPMCODE ends
+ENDIF
+;
+;****************************************************************
+
+ end
diff --git a/private/mvdm/dpmi/dxhpbios.asm b/private/mvdm/dpmi/dxhpbios.asm
new file mode 100644
index 000000000..09c32311e
--- /dev/null
+++ b/private/mvdm/dpmi/dxhpbios.asm
@@ -0,0 +1,478 @@
+ PAGE ,132
+ TITLE DXHPBIOS.ASM -- Dos Extender HP Extended BIOS Mapping
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXHPBIOS.ASM -- Dos Extender HP Extended BIOS Mapping
+;
+;-----------------------------------------------------------------------
+;
+; This module provides the 286 DOS extender's protected-to-real mode
+; mapping of selected HP Vectra Extended BIOS services.
+;
+;-----------------------------------------------------------------------
+;
+; 08/25/89 jimmat Original version
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include interupt.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+include stackchk.inc
+include bop.inc
+
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+F_INS_XCHGFIX equ 06h
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn EnterIntHandler:NEAR
+ extrn LeaveIntHandler:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn ParaToLDTSelector:NEAR
+ extrn PMIntrEntryVector:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn regUserSS:WORD
+ extrn regUserSP:WORD
+ extrn pbReflStack:WORD
+ extrn bReflStack:WORD
+ extrn fHardwareIntMoved:BYTE
+
+ public HPxBiosVectorRM
+
+HPxBiosVectorRM dd ? ;offset to RM HP Int handler
+
+PMCallBack dd 0 ;protected mode call back CS:IP
+
+HPDriverHeader dw ? ;segment of HP driver header block
+
+HPDriverSegSel dw 0,0 ;segment/selector pairs
+ dw 0,0
+ dw 0,0
+ dw -1
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn segDXData:WORD
+ extrn selDgroup:WORD
+ extrn PrevInt69Handler:DWORD
+ENDIF
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl HP Extended BIOS Mapping Interface
+ page
+; -------------------------------------------------------
+; HP EXTENDED BIOS MAPPING INTERFACE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; HPxBIOS -- Interrupt routine for the HP Vectra Extended
+; BIOS service calls. Currently, on the F_INS_XCHGFIX
+; service is supported, and this is not mapped transparently!
+; This support was added for the Windows HP mouse driver.
+;
+; Input: Various registers
+; Output: Various registers
+; Errors:
+; Uses: All registers preserved, other than return values
+;
+; The following services are supported:
+;
+; AH=06 - F_INS_XCHGFIX (non transparent pm->rm mapping)
+;
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public HPxBIOS
+
+HPxBIOS proc near
+
+ cmp ah,F_INS_XCHGFIX ;is this F_INS_XCHGFIX?
+ jz @f
+
+ jmp PMIntrEntryVector + 3*6Fh ;if not, just pass it on
+@@:
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:DGROUP ; also sets up addressability
+
+ cld ;cya...
+
+; Save the protected mode CS:IP. NOTE: we only support one call back
+; address (the last one)! This works for the current mouse driver, but
+; may not work for other drivers.
+
+ mov ax,[bp].pmUserDI
+ mov word ptr PMCallBack,ax
+ mov ax,[bp].pmUserES
+ mov word ptr [PMCallBack+2],ax
+
+; Execute the real mode HP Extended BIOS service
+
+ SwitchToRealMode
+ assume ds:DGROUP,es:DGROUP
+
+ xor ax,ax
+ mov es,ax
+ assume es:NOTHING
+ mov ax,es:[6Fh*4]
+ mov word ptr [HPxBiosVectorRM],ax
+ mov ax,es:[6Fh*4+2]
+ mov word ptr [HPxBiosVectorRM+2],ax
+
+ test byte ptr [bp].pmUserFL+1,02h ;enable interrupts if
+ jz @f ; caller had them enabled
+ FSTI
+@@:
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+ popa
+
+ push ax ;set our own call back routine,
+ mov ax,cs ; which will invoke the PM one
+ mov es,ax
+ pop ax
+ mov di,offset RMCallBack
+
+ FCLI
+ call ss:[HPxBiosVectorRM]
+
+ pushf
+ FCLI
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+
+IFDEF ROM
+ push ss
+ pop ds
+ELSE
+ mov ds,selDgroup ;HP BIOS seems to change DS on us
+ENDIF
+ assume ds:DGROUP
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+; Perform fixups on the return values.
+
+ mov ax,[bp].intUserES ;we return real mode ES in BP!
+ mov [bp].intUserBP,ax
+
+ call LeaveIntHandler ;restore caller's registers, stack
+ assume ds:NOTHING,es:NOTHING
+
+ iret
+
+HPxBIOS endp
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl HP Pointing Device Handler
+ page
+; -------------------------------------------------------
+; HP POINTING DEVICE HANDLER
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; RMCallBack -- This routine is the RM entry point for
+; the HP Pointing Device Handler. It switches the
+; processor to protected mode and transfers control to the
+; user pointing device handler. When that completes,
+; it switches back to real mode and returns control to
+; the HP BIOS.
+;
+; Input: none
+; Output: none
+; Errors: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public RMCallBack
+
+RMCallBack proc near
+
+ cld
+ push es ;save BIOS ds/es on it's stack
+ push ds
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup ;setup addressability to DOSX DGROUP
+ENDIF
+ assume ds:DGROUP
+
+ mov HPDriverHeader,es ;save ES driver header block segment
+
+; Allocate a new stack frame, and then switch to the local stack
+; frame.
+
+ FCLI ;protect global regUserXX vars
+
+ mov regUserSP,sp ;save entry stack pointer so we can restore it
+ mov regUSerSS,ss ;save segment too
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,selDgroup ;switch to our own stack frame
+ENDIF
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+
+ FIX_STACK
+ push regUserSS ;save HP BIOS stack address
+ push regUserSP ; so we can restore it later
+
+ push SEL_DXDATA or STD_RING ;DOSX DS to be poped in PM
+
+ pusha ;save general registers
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+; See if we've already mapped a selector to the HPDriverHeader segment. We
+; have a table of 3 segment/selector pairs because the current Windows
+; mouse driver support up to 3 pointing devices (all with the same call
+; back address).
+
+ mov ax,HPDriverHeader ;get segment to map
+
+ FSTI ;don't need ints disabled now
+
+ mov bx,offset DGROUP:HPDriverSegSel-4
+rmcb_cmp_seg:
+ add bx,4
+ cmp word ptr [bx],ax ;same segment?
+ jne @f
+ mov es,word ptr [bx+2] ; yes, get selector to ES
+ jmp short rmcb_sel_set
+@@:
+ cmp word ptr [bx],0 ;empty table slot?
+ je rmcb_new_seg
+ cmp word ptr [bx],-1 ;end of table?
+ jne rmcb_cmp_seg
+
+; Haven't seen this segment before, map a selector for it
+
+rmcb_new_seg:
+ mov cx,ax ;save segment in cx
+ mov dx,bx ;save table offset in dx
+ mov bx,STD_DATA ;want a data selector
+ call ParaToLDTSelector
+ jnc @f ;BIG TROUBLE if can't create selector!
+ popa ; don't even call users routine
+ jmp short rmcb50
+@@:
+ mov es,ax
+ assume es:NOTHING
+
+ mov bx,dx ;save this seg/sel pair if not
+ cmp word ptr [bx],-1 ; at the end of the table
+ je rmcb_sel_set
+
+ mov word ptr [bx],cx
+ mov word ptr [bx+2],ax
+
+rmcb_sel_set:
+
+ popa ;restore general registers
+
+; Build an iret frame on the stack so that the user's
+; routine will return to us when it is finished.
+
+ pushf
+ push cs
+ push offset rmcb50
+
+; Build a far return frame on the stack to use to transfer control to the
+; user's protected mode routine
+
+ push word ptr [PMCallBack+2]
+ push word ptr [PMCallBack]
+
+; At this point the stack looks like this:
+;
+; [14] stack segment of original stack
+; [12] stack pointer of original stack
+; [10] protect mode dos extender data segment
+; [8] flags
+; [6] segment of return address back to here
+; [4] offset of return address back here
+; [2] segment of user routine
+; [0] offset of user routine
+
+; Execute the user's pointing device handler
+
+ retf
+
+; The users handler will return here after it is finsished.
+
+rmcb50:
+ cld
+ pop ds ;restore DOSX DS
+ assume ds:DGROUP,es:NOTHING
+
+ FCLI ;protect global regUserXX vars
+ pop regUserSP
+ pop regUserSS
+
+; Switch back to real mode.
+
+ push ax ;preserve AX
+ SwitchToRealMode
+ assume ds:DGROUP,es:DGROUP
+ pop ax
+
+; Switch back to the original stack.
+
+ CHECK_STACK
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Deallocate the stack frame that we are using.
+
+ add pbReflStack,CB_STKFRAME
+
+; And return to the HP BIOS
+
+ pop ds
+ pop es
+
+ iret
+
+RMCallBack endp
+
+; -------------------------------------------------------
+ subttl Classic HP Vectra Keyboard Hook
+ page
+; -------------------------------------------------------
+; CLASSIC HP VECTRA KEYBOARD HOOK
+; -------------------------------------------------------
+
+IFNDEF ROM
+
+ public RMVectraKbdHook
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+; If the master PIC has been remapped, we process the interrupt ourselves,
+; otherwise, we just pass it on to the previous Int 69h handler (which
+; is most likely the HP Vectra BIOS).
+
+RMVectraKbdHook proc near
+
+ push ds
+ mov ds,segDXData
+ assume ds:DGROUP
+
+ test fHardwareIntMoved,0FFh ;PIC been remapped?
+
+ pop ds
+ assume ds:NOTHING
+
+ jnz @f
+
+ jmp [PrevInt69Handler] ; no, get out of the way
+@@:
+ push ax
+
+ mov al,61h ; yes, EOI the third slave PIC
+ out 7Ch,al
+
+ pop ax
+
+ int 51h ; and simulate an IRQ 1 interrupt
+ iret
+
+RMVectraKbdHook endp
+
+ENDIF
+
+DXCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+
+IFNDEF ROM
+
+ public PMVectraKbdHook
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+PMVectraKbdHook proc near
+
+ push ax ;EOI the third slave PIC
+
+ mov al,61h
+ out 7Ch,al
+
+ pop ax
+
+ int 51h ;simulate an IRQ 1 interrupt
+
+ iret ;back we go
+
+PMVectraKbdHook endp
+
+ENDIF
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxini.asm b/private/mvdm/dpmi/dxini.asm
new file mode 100644
index 000000000..029433e46
--- /dev/null
+++ b/private/mvdm/dpmi/dxini.asm
@@ -0,0 +1,467 @@
+ PAGE ,132
+ TITLE DXINI.ASM -- Dos Extender INI File Processing
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXINI.ASM -- Dos Extender INI FIle Processing
+;
+;-----------------------------------------------------------------------
+;
+; This module provides the 286 DOS extender's ...
+;
+;-----------------------------------------------------------------------
+;
+; 09/27/89 jimmat Modified to use FindFile instead of using its
+; own file search logic
+; 05/24/89 w-glenns Original (UNCUT, UNCENSORED!) version
+;
+;***********************************************************************
+
+ .286
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include intmac.inc
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+CR equ 13
+LF equ 10
+TAB equ 9
+EOF equ 26
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn strcpy:NEAR
+ extrn FindFile:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn rgbXfrBuf1:BYTE
+
+DXDATA ends
+
+; -------------------------------------------------------
+ subttl Read INI File Routine
+ page
+; -------------------------------------------------------
+; READ INI FILE ROUTINE
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+;******************************************************************************
+;
+; ReadIniFile
+;
+; DESCRIPTION: read and parse a .INI file for the 286 DOS Extender
+; initialization.
+;
+; ENTRY: dx points to the file name
+; bx points to structure to fill with ini fields
+;
+; EXIT: Carry set, if file not found, or not enough memory
+;
+; USES: ax, cx
+;
+;==============================================================================
+
+ assume ds:DGROUP
+ public ReadIniFile
+
+ReadIniFile PROC NEAR
+
+ push es
+ push bx
+ push si
+ push di
+
+ push ds
+ pop es
+ assume es:DGROUP
+
+ push bx ; ptr to ini structure to fill
+
+ mov si,dx
+ mov di,offset RELOC_BUFFER ; FindFile wants the name here
+ call strcpy
+
+ call FindFile ; locate the .INI file
+ jc ri_error
+
+ mov ax,3D00h ; open the .INI file
+ mov dx,offset EXEC_PROGNAME ; FindFile puts path name here
+ int 21h
+ jc ri_error ; shouldn't happen, but...
+
+ mov si, ax ; file handle
+
+ mov ah, 48h ; alloc DOS conventional memory
+ mov bx, 4096d ; want 64k block
+ int 21h
+ jc ri_error
+
+ pop dx ; ptr to ini structure to fill
+
+ call parse_ini ; do the work, and come back
+ assume es:NOTHING
+
+ pushf ; save parse_ini flags
+
+ mov ah, 49h ; dealloc DOS conventional memory
+ int 21h ; es already points to block
+
+ npopf ; carry set = problem
+ri_end:
+ pop di
+ pop si
+ pop bx
+ pop es
+ ret
+
+ri_error: ; error exit
+ pop bx ; clear stack
+ stc ; force carry on
+ jmp short ri_end ; split
+
+ReadIniFile ENDP
+
+
+;******************************************************************************
+;
+; Parse_Ini
+;
+; DESCRIPTION: Read in, and parse the ini file opened
+; and find the variable values specified
+;
+; ENTRY: ax points to the memory block for the file image buffer
+; dx points to structure to fill with ini fields
+; si has the handle to the file opened
+;
+; EXIT: Carry set, if file not found, or not enough memory
+;
+; USES: ax, bx, cx, es
+;
+;==============================================================================
+
+Parse_Ini PROC NEAR
+
+ push bp
+ push dx
+ mov bp,dx ; bp = index into structure (es:)
+
+ assume ds:NOTHING
+ push ds
+
+ mov ds, ax ; ptr to mem block
+
+ mov ah, 3Fh
+ mov bx, si
+ mov cx, 0FFFFh ; guess extremely high
+ xor dx, dx ; offset 0
+ int 21h
+
+ pop es
+ assume es:DGROUP ; NOTE:!!! es is now data segment !!!
+
+ pushf ; save flags from read
+ push ax ; save # bytes read
+
+ mov ah, 3Eh ; close file
+ mov bx, si
+ int 21h
+
+ pop di ; number bytes read
+ npopf ; CY flag
+
+ jnc @f
+ jmp parse_done_jump ; if couldn't read, return bad
+@@:
+
+ mov byte ptr ds:[di], EOF ; write EOF char'r in case none present
+
+ ; ds:si points to the file image buffer
+ ; es:di/bx structure to fill with ini stuff
+ xor si,si
+
+ ; search until section found
+find_section:
+ call Get_Char
+ jc short parse_done_jump ; end of file, and section not found
+ cmp al, '[' ; a section ?
+ jne short find_section
+
+ mov di, bp ; point to ini structure
+ ; a section has been found, but is it the right one ?
+ xor bx, bx ; use as secondary index
+cmp_section:
+ mov al, byte ptr ds:[si+bx] ; char'r from section name in file
+
+ inc bx ; bx starts at zero for file pointer
+ ; index, and starts at one for
+ ; structure index
+ mov ah, byte ptr es:[di+bx]
+ or ah, ah
+ jz short find_keyword_start ; yes: found the right section !
+
+ call Conv_Char_Lower ; convert char'r in AL to lower case
+ cmp al, ah ; same so far ?
+ jne short find_section
+ jmp short cmp_section
+
+find_keyword_start:
+ add si,bx ; update file pointer past section name
+
+ add bp,bx ; update structure ptr past section name
+
+ ; now that section is found, want to find keywords
+find_keyword:
+ call Get_Char ; points to 1st char'r of next keyword
+ jc short parse_done_jump ; end of file, and keyword not found
+
+ cmp al, '[' ; new section ?
+ je short parse_done_jump ; hit a new section, so we're done
+
+search_keyword:
+ xor di,di
+ ; use beginning of file image buffer for temporary storage of the
+ ; keyword name currently being checked
+
+find_keyword_loop:
+ mov byte ptr ds:[di], al ; copy the char'r
+ inc di
+
+ mov dx,si ; save position in file image buffer
+ call Get_Char ; points to 1st char'r of next keyword
+ jc short parse_done_jump ; end of file, and keyword not found
+ pushf
+ cmp al, '='
+ je short compare_keyword
+ ; yes: found a keyword, lets do some comparisons
+
+ npopf
+ jz short find_keyword_loop ; no white space yet...copy keyword
+
+skip_keyword:
+ ; white space has been skipped, yet there is no '='
+ ; must be an error...ignore, and get next keyword
+ mov si,dx ; point to last char'r in keyword
+ mov byte ptr ds:[si], ';'
+ ; fake a comment, so that the rest of the
+ ; line is ignored in the next Get_Char call
+ ; and the next keyword is pointed to
+ jmp short find_keyword
+
+
+parse_done_jump:
+ jmp parse_done
+
+ ; char'r is an equals, so compare this keyword to the list
+compare_keyword:
+ npopf ; clean top-of-stack
+ mov byte ptr ds:[di], 0 ; NULL at end of keyword for compare
+ mov bx,bp ; get index into INI structure in
+ ; data segment DGROUP (where es points)
+
+cmp_keyword1:
+ xor di,di ; point to start of keyword found
+
+cmp_keyword:
+ inc bx
+ mov ah, byte ptr es:[bx] ; next char'r in ini struct keyword
+ mov al, byte ptr ds:[di] ; next char'r of found keyword
+ inc di
+ or al, ah
+ jz short convert_number ; yes: found the right keyword
+
+ call Conv_Char_Lower ; convert char'r in AL to lower case
+ cmp al, ah ; same so far ?
+ je short cmp_keyword
+
+ xor al,al
+ dec bx
+cmp_keyword_loop:
+ inc bx
+ cmp byte ptr es:[bx], al ; next keyword yet?
+ jne cmp_keyword_loop ; nope: go back until done
+
+ ; keywords don't match..try next key word in ini structure
+ inc bx
+ inc bx ; jump over variable space (1 word)
+ cmp byte ptr es:[bx+1], al ; more keywords to compare with ?
+ jne short cmp_keyword1 ; yes: compare the next one
+ jmp short skip_keyword
+ ; no: search file for next keyword
+
+convert_number:
+ push si ; save current file pointer
+ call Get_Char
+ dec si ; point to first char'r position in number to convert
+
+ xor di,di
+ mov ax,di
+ mov cx,ax
+
+ cmp byte ptr ds:[si], '+' ; positive number ? (default)
+ jne short cn_1
+
+ inc si ; just skip the char'r - positive is default anyway
+
+cn_1: cmp byte ptr ds:[si], '-' ; negative number ?
+ jne short cn_2
+
+ inc si
+ inc cl ; negative number - flag so we can negate it later
+
+cn_2: push bx
+ mov bx,si ; save ptr in file buffer - check later if it changed
+ push cx ; save flag
+
+convert_get_loop:
+ mov cl, byte ptr ds:[si]
+ cmp cl, '0'
+ jb short convert_done
+ cmp cl, '9'
+ ja short convert_done
+
+ sub cx,'0' ; de-ascii'ize cx ==> 0h - 09h
+
+ inc si ; increment pointer
+ mov dx,010d
+ mul dx ; multiply ax by 10 : dx is set to zero
+ add ax,cx ; add number in
+ jmp short convert_get_loop
+
+convert_done:
+ pop cx ; restore -ve/+ve flag
+ jcxz convert_done_1 ; Q: -ve number ?
+
+ neg ax ; negate the number
+
+convert_done_1:
+ cmp si,bx ; Q: has index changed, i.e.
+ ; is the first char'r invalid ?
+ pop bx
+ je short convert_done_2 ; Y: don't save number
+
+ inc bx ; N: point to number in structure
+ mov word ptr es:[bx], ax ; save value into ini structure
+
+convert_done_2:
+ pop si ; get old file pointer
+ mov byte ptr ds:[si], ';'
+ ; fake a comment, so that the rest of the
+ ; line is ignored in the next Get_Char call
+ ; and the next keyword is pointed to
+ jmp find_keyword ; go back & get another
+
+
+ ; *** single exit point for parsing code
+parse_done:
+ mov ax,es ; swap extended and data segment ptrs
+ mov bx,ds
+ mov es,bx
+ mov ds,ax
+
+ pop dx
+ pop bp
+ ret
+
+Parse_Ini ENDP
+
+;******************************************************************************
+;
+; Get_Char
+;
+; DESCRIPTION: Local routine which gets the next valid ascii
+; character from the file image, while skipping white
+; space.
+;
+; ENTRY: ds:si -> buffer pointer
+;
+; EXIT: ds:si -> new buffer pointer
+; al --> character
+; Z flag --> set = no white space between last char'r and current
+; C flag --> set = reached end of file
+;
+; USES: cx
+;
+;==============================================================================
+
+Get_Char PROC NEAR
+
+ mov cx,si
+ inc cx
+
+get_char_loop:
+ lodsb ; get char from file image
+ cmp al,EOF
+ je short get_char_EOF
+ cmp al,CR
+ je short get_char_loop
+ cmp al,LF
+ je short get_char_loop
+ cmp al,TAB
+ je short get_char_loop
+ cmp al,' '
+ je short get_char_loop
+ cmp al,';'
+ je short get_char_skip_comment
+
+ ; must have got a good character finally...
+ call Conv_Char_Lower
+ cmp cx,si ; skipped a white space ?
+ clc ; continue
+ ret
+
+get_char_EOF:
+ stc ; flag end of the file
+ ret
+
+get_char_skip_comment:
+ lodsb ; get char from file image
+ cmp al,EOF
+ je short get_char_EOF
+ cmp al,CR
+ jne short get_char_skip_comment
+ jmp short get_char_loop
+
+Get_Char ENDP
+
+Conv_Char_Lower PROC NEAR
+
+ cmp al, 'A' ; want char'r 'A'-'Z' to be
+ jb short lower_done ; converted to 'a'-'z'
+ cmp al, 'Z'
+ ja short lower_done
+ or al, 020h ; convert to lower case
+lower_done:
+ ret
+
+Conv_Char_Lower ENDP
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxint31.asm b/private/mvdm/dpmi/dxint31.asm
new file mode 100644
index 000000000..3b3c43276
--- /dev/null
+++ b/private/mvdm/dpmi/dxint31.asm
@@ -0,0 +1,3183 @@
+ PAGE ,132
+ TITLE DXINT31.ASM -- Dos Extender Int 31h Handler
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXINT31.ASM -- DOS Extender Int 31h Handler
+;
+;-----------------------------------------------------------------------
+;
+; This module provides the Int 31h services to the protected mode
+; application running under the DOS Extender.
+;
+;-----------------------------------------------------------------------
+;
+; 12/03/90 amitc 'i31_GetSetRMInt' will map vectors in the range 50-57h
+; to the range 8-fh if 'Win30CommDriver' switch is set in
+; system.ini
+; 12/18/89 jimmat Service 0003 changed from Get LDT Base to Get Sel Incr,
+; and added Virtual Interrupt State services.
+; 09/18/89 jimmat Added Allocate/Free Real Mode Call-Back services
+; 08/20/89 jimmat Changed A20 diddling to use XMS local enable/disable
+; 06/14/89 jimmat Added a few missing and new services.
+; 05/17/89 jimmat Added protected to real mode call/int services.
+; 05/12/89 jimmat Original version (split out from DXINTR.ASM)
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include interupt.inc
+include int31.inc
+include dpmi.inc
+
+ Int_Get_PMode_Vec EQU 04h
+ Int_Set_PMode_Vec EQU 05h
+
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+IFDEF XMEMNT
+include dpmi.inc
+ENDIF
+include stackchk.inc
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+RealMode_SaveBP equ word ptr RealMode_EBP+4
+RealMode_SaveSP equ word ptr RealMode_EBP+6
+
+cRM_CALL_BACK equ 16 ;Count of Real Mode Call-Backs supported
+ ;DPMI 0.90 spec says 16 required.
+IFNDEF WOW
+CallBackTableStruc struc ;Structure of Real Mode Call-Back Table
+fInUse db 0 ;use/free flag
+PM_CS_IP dd ? ;pMode CS:IP to call
+PM_Client_Frame dd ? ;Client Register Frame
+CallBackTableStruc ends
+ELSE
+CallBackTableStruc struc ;Structure of Real Mode Call-Back Table
+fInUse db 0 ;use/free flag
+PM_CS_IP dd ? ;pMode EIP to call
+ dw ? ;pMode CS to call
+PM_Client_Frame dd ? ;Client Register Frame
+ dw ? ; segment of above
+CallBackTableStruc ends
+ENDIF
+
+SelectorIncrement equ 8 ;DOSX increments consecutive selectors by 8
+
+Trans_Reset_HW equ 01h ;Reset PIC/A20 line on PM->Call services
+
+I31VERSION equ 0090d ;Int 31 services major/minor version #'s
+ ; version 00.90 (not quite ready for DPMI)
+IFNDEF WOW
+I31FLAGS equ 000Ah ;Running under 286 Extender & pMode NetBIOS
+ELSE
+I31FLAGS equ 000Dh ; 386 extender, pMode NetBIOS
+ENDIF
+I31MasterPIC equ 08h ;Master PIC Interrupts start at 08h
+I31SlavePIC equ 70h ;Slave PIC Interrupts start at 70h
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn EnterIntHandler:NEAR
+ extrn LeaveIntHandler:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn GetSegmentAddress:NEAR
+ extrn SetSegmentAddress:NEAR
+ extrn FreeSelector:NEAR
+ extrn FreeSelectorBlock:NEAR
+ extrn AllocateSelector:NEAR
+ extrn AllocateSelectorBlock:NEAR
+ extrn ParaToLDTSelector:NEAR
+ extrn DupSegmentDscr:NEAR
+ extrn GetFaultVector:NEAR
+ extrn PutFaultVector:NEAR
+ extrn AllocateXmemBlock:NEAR
+ extrn FreeXmemBlock:NEAR
+ extrn ModifyXmemBlock:NEAR
+ extrn FreeLowBlock:NEAR
+ extrn AllocateLowBlock:NEAR
+ extrn AllocateLDTSelector:NEAR
+ extrn ParaToLinear:NEAR
+ extrn GetIntrVector:NEAR, PutIntrVector:NEAR
+ifdef WOW
+ extrn NSetSegmentLimit:near
+ extrn NMoveDescriptor:near
+ extrn NWOWSetDescriptor:near
+endif
+ extrn RMIntrEntryVector:near
+ extrn EndRMIntrEntry:near
+ extrn DFSetIntrVector:near
+ extrn RmSaveRestoreState:far
+ extrn PmSaveRestoreState:far
+ extrn RmRawModeSwitch:far
+ extrn PmRawModeSwitch:far
+ extrn IsSelectorFree:near
+ extrn gtpara:near
+
+externNP NSetSegmentAccess
+externFP NSetSegmentDscr
+externNP FreeSpace
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn selGDT:WORD
+ extrn segPSP:WORD
+ extrn idCpuType:WORD
+ extrn npXfrBuf1:WORD
+ extrn rgbXfrBuf1:BYTE
+ extrn pbReflStack:WORD
+ extrn bReflStack:WORD
+ extrn rglpfnRmISR:DWORD
+ extrn lpfnXMSFunc:DWORD
+ extrn A20EnableCount:WORD
+ extrn regUserAX:WORD, regUserFL:WORD, regUserSS:WORD
+ extrn regUserSP:WORD, regUserDS:WORD, regUserES:WORD
+ extrn fWin30CommDrv:WORD
+ extrn PMInt24Handler:DWORD
+IFDEF WOW
+ extrn DpmiFlags:WORD
+ extrn FastBop:fword
+ENDIF
+
+ extrn selPspChild:WORD
+ extrn RmHwIsr:DWORD
+ extrn LowMemAllocFn:DWORD
+ extrn LowMemFreeFn:DWORD
+
+ public i31HWReset
+
+i31HWReset db 0 ;NZ if in 'standard' real mode state
+
+i31_dsp_rtn dw 0 ;Int 31h service routine to dispatch
+
+ifdef DEBUG
+debugsavess dw 0 ; Usefull when debugging WOW KERNEL
+debugsavesp dw 0 ;
+debugsavebp dw 0 ;
+debugsavecx dw 0 ;
+endif
+
+ public i31_selectorbitmap
+i31_selectorbitmap dw 0000000000000000b
+ ;Reserved LDT Selectors
+
+CallBackTable label byte ;Real Mode Call-Back Table
+ rept cRM_CALL_BACK
+ CallBackTableStruc <>
+ endm
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+IFNDEF ROM
+ extrn segDXData:WORD
+ extrn selDgroup:WORD
+ENDIF
+
+ extrn segDXCode:word
+
+RMCallBackEntryVector label near ;Real Mode Call-Back Entry Points
+ rept cRM_CALL_BACK
+ call RMCallBackHook
+ endm
+
+ assume cs:NOTHING
+
+DXCODE ends
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+ extrn segDXCodePM:WORD
+
+i31_dispatch label word
+
+ dw 0000h, offset i31_AllocSel
+ dw 0001h, offset i31_FreeSel
+ dw 0002h, offset i31_MapSeg2Sel
+ dw 0003h, offset i31_GetSelIncr
+ dw 0004h, offset i31_Success ;lock selector memory
+ dw 0005h, offset i31_Success ;unlock selector mem
+ dw 0006h, offset i31_GetSegAddr
+ dw 0007h, offset i31_SetSegAddr
+ dw 0008h, offset i31_SetLimit
+ dw 0009h, offset i31_SetAccess
+ dw 000Ah, offset i31_CreateDataAlias
+ dw 000Bh, offset i31_GetSetDescriptor
+ dw 000Ch, offset i31_GetSetDescriptor
+ dw 000Dh, offset i31_SpecificSel ; Allocate specific descriptor
+ dw 0100h, offset i31_AllocDOSMem
+ dw 0101h, offset i31_FreeDOSMem
+ dw 0102h, offset i31_SizeDOSMem
+
+ dw 0200h, offset i31_GetSetRMInt
+ dw 0201h, offset i31_GetSetRMInt
+ dw 0202h, offset i31_GetSetFaultVector
+ dw 0203h, offset i31_GetSetFaultVector
+ dw 0204h, offset i31_GetSetPMInt
+ dw 0205h, offset i31_GetSetPMInt
+ dw 0300h, offset i31_RMCall
+ dw 0301h, offset i31_RMCall
+ dw 0302h, offset i31_RMCall
+ dw 0303h, offset i31_AllocCallBack
+ dw 0304h, offset i31_FreeCallBack
+ dw 0305h, offset i31_GetStateSaveRestore
+ dw 0306h, offset i31_GetRawModeSwitch
+ dw 0400h, offset i31_Version
+
+ dw 04f1h, offset i31_WOW_AllocSel
+ dw 04f2h, offset i31_WOW_SetDescriptor
+ dw 04f3h, offset i31_WOW_SetAllocFunctions
+
+;
+; INCOMPLETE !!!!!!!!!!!
+; Needed by kernel.
+;
+ dw 0500h, offset i31_GetFreeMem
+ dw 0501h, offset i31_AllocMem
+ dw 0502h, offset i31_FreeMem
+;
+; Fails by design if block is to be extended and this cannot be done
+; in place.
+;
+ dw 0503h, offset i31_SizeMem
+
+ dw 0600h, offset i31_Success ;lock linear region
+ dw 0601h, offset i31_Success ;unlock linear region
+ dw 0602h, offset i31_Success ;mark real mode rgn pageable
+ dw 0603h, offset i31_Success ;relock real mode region
+ dw 0604h, offset i31_PageSize ;get page size
+
+ dw 0700h, offset i31_fail ;reserved
+ dw 0701h, offset i31_fail ;reserved
+ dw 0702h, offset i31_Success ;demand page candidate
+ dw 0703h, offset i31_Success ;discard page contents
+
+ dw 0800h, offset i31_fail ;physical addr mapping
+
+ dw 0900h, offset i31_VirtualInt ;get & disable Int state
+ dw 0901h, offset i31_VirtualInt ;get & enable Int state
+ dw 0902h, offset i31_VirtualInt ;get Int state
+;
+; UNIMPLEMENTED !!!!!!!!!!!
+; To be used for MSDOS protected mode API?
+;
+ dw 0A00h, offset i31_unimplemented ;get vendor specific API
+
+IFDEF WOW
+ NO386 = 0
+else
+ NO386 = 1
+ENDIF
+ife NO386
+;
+; 386 Debug Register access routines.
+;
+ dw 0B00h, offset i31_Debug_Register_Access
+ ;set debug watchpoint
+ dw 0B01h, offset i31_Debug_Register_Access
+ ;clear debug watchpoint
+ dw 0B02h, offset i31_Debug_Register_Access
+ ;get debug watchpoint state
+ dw 0B03h, offset i31_Debug_Register_Access
+ ;reset debug watchpoint
+endif ; NO386
+
+ dw -1, offset i31_unimplemented
+
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl INT 31h Entry Point
+ page
+; -------------------------------------------------------
+; INT 31h SERVICE ENTRY POINT
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntr31 -- Service routine for the Protect Mode INT 31h
+; services. These functions duplicate the
+; Windows/386 VMM INT 31h services for protected
+; mode applications. They were implemented to
+; support a protect mode version of Windows/286.
+;
+; Input: Various registers
+; Output: Various registers
+; Errors:
+; Uses: All registers preserved, other than return values
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr31
+
+PMIntr31 proc near
+
+ cld ;practice 'safe programming'
+
+; Determine if this one of the services we support.
+
+ push bx
+ mov bx,offset i31_dispatch ;cs:bx -> dispatch table
+
+@@:
+ cmp ax,cs:[bx] ;scan dispatch table for
+ jz i31_do_it ; service code in AH/AL
+
+ cmp word ptr cs:[bx],-1 ;end of table is -1
+ jz i31_do_it
+
+ add bx,4
+ jmp short @b
+
+; BX contains the offset of the routine to service this request!
+
+i31_do_it:
+ push ds ;save the service routine address
+ mov ds,selDgroupPM ; in our DGROUP for now
+ assume ds:DGROUP
+
+ mov bx,cs:[bx+2]
+ FCLI ;needed for [i31_dsp_rtn]
+ mov i31_dsp_rtn,bx
+
+ifdef DEBUG
+ mov debugsavess,ss ; usefule when debugging WOW kernel
+ mov debugsavesp,sp
+ mov debugsavebp,bp
+ mov debugsavecx,cx
+endif
+
+ pop ds
+ pop bx
+
+i31_doit:
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:DGROUP ; also sets up addressability
+
+ push i31_dsp_rtn ;routine address on stack
+
+ DEBUG_TRACE DBGTR_ENTRY, 31h, ax, 0
+
+ FSTI ;no need to keep interrupts disabled
+
+ retn ;go perform the service
+
+i31_unimplemented: ;not implemented or undefined
+
+ Debug_Out "Unsupported Int 31h requested (AX = #AX)"
+
+; Int 31h service routines return (jmp) here to indicate failure. The
+; standard failure return sets the carry flag, and sets ax = 0.
+
+i31_fail:
+
+ mov [bp].intUserAX,0
+
+i31_fail_CY:
+
+ or byte ptr [bp].intUserFL,1
+ jmp short i31_exit
+
+; Int 31h service routines return (jmp) here -- they jump back instead
+; of returning because they expect the stack to be setup by EnterIntHandler,
+; no extra items should be pushed.
+
+i31_done:
+
+ and byte ptr [bp].intUserFL,not 1 ;clear carry flag
+
+i31_exit:
+ assume ds:NOTHING,es:NOTHING
+
+ FCLI ;LeaveIntHandler needs them off
+ call LeaveIntHandler ;restore caller's registers, and
+ DEBUG_TRACE DBGTR_EXIT, 31h, 0, 0
+ riret ; get on down the road
+
+PMIntr31 endp
+
+
+; -------------------------------------------------------
+; This routine is for Int 31h services that the 286 DOS extender
+; allows, but doesn't actually perform any work. Most of these
+; services are related to demand paging under Windows/386.
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_Success
+
+i31_Success proc near
+
+ jmp i31_done ;nothing to do currently
+
+i31_Success endp
+
+; -------------------------------------------------------
+; Page Size. Gotta be 1000h, even if we don't do anything
+; with it.
+;
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_PageSize
+
+i31_PageSize proc near
+
+ mov [bp].intUserBX,0
+ mov [bp].intUserCX,1000h
+ jmp i31_done ;nothing to do currently
+
+i31_pageSize endp
+
+; -------------------------------------------------------
+ subttl INT 31h LDT/Heap Services
+ page
+; -------------------------------------------------------
+; LDT/HEAP INTERRUPT (INT 31h) SERVICE ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Service 00/00 - Allocate Space in LDT for Selector
+; in: cx - # selectors required
+; out: ax - *index* of first selector
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_AllocSel
+
+i31_AllocSel proc near
+
+ jcxz short i31_15
+ cmp cx,1 ;1 selector or more?
+ ja @f
+
+ call AllocateSelector ;allocate 1 selector
+ jc i31_15
+ jmp short i31_10
+
+@@: mov ax,cx
+ call AllocateSelectorBlock ;allocate a block of selectors
+ jc i31_15
+
+i31_10:
+ or al,STD_TBL_RING ;add standard table/ring bits
+ mov [bp].intUserAX,ax ;return 1st/only selector in AX
+
+setsel: mov bx,STD_DATA ;all the selectors we allocate
+; xor bl,bl ; get initialized to data/ring ?
+ xor dx,dx ; 0 base, 0 limit
+@@:
+ cCall NSetSegmentDscr,<ax,dx,dx,dx,dx,bx>
+ add ax,SelectorIncrement
+ loop @b
+
+ jmp i31_done
+
+i31_15:
+ jmp i31_fail ;fail the request
+
+i31_AllocSel endp
+
+
+; -------------------------------------------------------
+; Service 00/01 - Free LDT Selector
+; in: bx - selector to free
+; out: none
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_FreeSel
+
+i31_FreeSel proc near
+
+ mov ax,bx ;release the selector
+ cmp ax,SEL_DPMI_LAST ; reserved selector?
+ ja i31_Free_Regular_LDT_Selector ; No.
+ mov cx,ax
+ shr cl,3 ; Selector to selector index in LDT
+ mov ax,1
+ shl ax,cl ; AX = bit in i31_selectorbitmap
+ test i31_selectorbitmap,ax ; test for already allocated
+ jz i31_FreeSel_Fail ; already free!
+ not ax
+ and i31_selectorbitmap,ax ; mark as free
+ jmp i31_done
+
+i31_Free_Regular_LDT_Selector:
+
+ and ax,SELECTOR_INDEX ; only pass it the index
+ or ax,SELECTOR_TI ;only allow LDT selectors
+ jz i31_Check_DS_ES
+ call FreeSelector
+ jnc i31_Check_DS_ES
+i31_FreeSel_Fail:
+ jmp i31_fail_CY
+
+i31_Check_DS_ES:
+
+ ; check in case user frees the selector in DS or ES - we
+ ; don't want to fault when popping his regs
+
+ and bl,NOT SELECTOR_RPL ;compare without ring bits
+
+ mov ax,[bp].pmUserDS
+ and al,NOT SELECTOR_RPL
+ cmp ax,bx
+ jnz @f
+
+ mov [bp].pmUserDS,0
+
+@@: mov ax,[bp].pmUserES
+ and al,NOT SELECTOR_RPL
+ cmp ax,bx
+ jnz @f
+
+ mov [bp].pmUserES,0
+@@:
+ jmp i31_done
+
+i31_FreeSel endp
+
+
+; -------------------------------------------------------
+; Service 00/02 - Map Segment to Selector
+; in: bx - real mode segment value
+; out: ax - selector which maps area
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_MapSeg2Sel
+
+i31_MapSeg2Sel proc near
+
+ mov ax,bx ;find/make selector for real memory
+ mov bx,STD_DATA ;assume it's a data selector
+ call ParaToLDTSelector
+ jnc @f
+
+ jmp i31_fail_CY ;Falied!?
+@@:
+ mov [bp].intUserAX,ax
+ jmp i31_done
+
+i31_MapSeg2Sel endp
+
+
+; -------------------------------------------------------
+; Service 00/03 - Get Next Selector Increment Value
+; in: none
+; out: ax - Next Selector Increment Value
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSelIncr
+
+i31_GetSelIncr proc near
+
+ mov [bp].intUserAX,SelectorIncrement ;DOSX incr value
+ jmp i31_done
+
+i31_GetSelIncr endp
+
+
+; -------------------------------------------------------
+; Service 00/06 - Get Segment Base address.
+; in: bx - selector
+; out: cx:dx - 32 bit lma of segment
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSegAddr
+
+i31_GetSegAddr proc near
+
+ mov ax,selGDT
+ assume es:nothing
+ lsl ax,ax
+
+ push bx
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax
+ jnc gsa10 ; not in ldt
+
+ call IsSelectorFree
+ jc gsa20
+
+gsa10: pop bx
+ jmp i31_fail_CY
+
+gsa20: pop bx
+
+ mov ax,bx
+ call GetSegmentAddress
+
+ mov [bp].intUserCX,bx
+ mov [bp].intUserDX,dx
+
+ jmp i31_done
+
+i31_GetSegAddr endp
+
+
+; -------------------------------------------------------
+; Service 00/07 - Set Segment Base address.
+; in: bx - selector
+; cx:dx - 32 bit lma of segment
+; out: none
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SetSegAddr
+
+i31_SetSegAddr proc near
+
+ mov ax,selGDT
+ assume es:nothing
+ lsl ax,ax
+
+ push bx
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax
+ jnc ssa10 ; not in ldt
+
+ call IsSelectorFree
+ jc ssa20
+
+ssa10: pop bx
+ jmp i31_fail_CY
+
+ssa20: pop bx
+ mov ax,bx
+ mov bx,cx
+ call SetSegmentAddress
+
+ jmp i31_done
+
+i31_SetSegAddr endp
+
+
+; -------------------------------------------------------
+; Service 00/08 - Set Segment Limit.
+; in: bx - selector
+; cx:dx - 32 bit limit of segment
+; out: none
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SetLimit
+
+i31_SetLimit proc near
+
+ mov ax,selGDT
+ assume es:nothing
+ lsl ax,ax
+
+ push bx
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax
+ jnc sl10 ; not in ldt
+
+ call IsSelectorFree
+ jc sl20
+
+sl10: pop bx
+ jmp i31_fail_CY
+
+sl20: pop bx
+ mov es,selGDT
+ and bx,SELECTOR_INDEX
+ and es:[bx].cbLimitHi386,070h ; clear 'G' bit and old
+ ; extended limit bits
+ test cx,0fff0h ; bits 20-31 set?
+ jz i31_SetLimit_0 ; No
+ shr dx,12d ; Yes
+ mov ax,cx
+ shl ax,4d
+ or dx,ax
+ shr cx,12d
+ or es:[bx].cbLimitHi386,080h ; set 'G' bit
+i31_SetLimit_0:
+ mov es:[bx].cbLimit,dx
+ or es:[bx].cbLimitHi386,cl
+ifdef WOW
+ cCall NSetSegmentLimit,<bx>
+endif ; WOW
+ jmp i31_done
+
+i31_SetLimit endp
+
+
+; -------------------------------------------------------
+; Service 00/09 - Set Segment Access Rights
+; in: bx - selector
+; cl - segment access rights byte
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SetAccess
+
+i31_SetAccess proc near
+
+ mov ax,selGDT
+ assume es:nothing
+ lsl ax,ax
+
+ push bx
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax
+ jnc sa10 ; not in ldt
+
+ call IsSelectorFree
+ jc sa20
+
+sa10: pop bx
+sa11:
+ jmp i31_fail_CY
+
+sa20: pop bx
+
+ test cl,010000b ; system segment?
+ jz sa11 ; y: error
+ push cx
+ and cl,AB_DPL ; mask off all but DPL bits
+ cmp cl,STD_DPL ; is this the correct DPL?
+ pop cx
+ jnz sa11 ; n: error
+
+ cCall NSetSegmentAccess,<bx,cx>
+ jmp i31_done
+
+i31_SetAccess endp
+
+
+; -------------------------------------------------------
+; Service 00/0A - Create Data Segment Alias (for a code seg)
+; in: bx - selector
+; out: ax - new data selector
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_CreateDataAlias
+
+i31_CreateDataAlias proc near
+
+ mov ax,bx ;make sure it's a vaild selector
+ verr ax
+ jnz cda_failed
+
+ mov bx,ax ;get a new selector for the alias
+ call AllocateSelector
+ jc cda_failed
+
+ xchg ax,bx ;copy old to new
+ call DupSegmentDscr
+
+ mov es,selGDT
+ and bx,SELECTOR_INDEX
+ mov al,es:[bx].arbSegAccess
+ and al,11100000b ;mask off all but present and DPL bits
+ or al,AB_DATA or AB_WRITE
+ifndef WOW
+ mov es:[bx].arbSegAccess,al
+else
+ mov ah, es:[bx].cbLimitHi386
+ cCall NSetSegmentAccess,<bx,ax>
+endif ; WOW
+ or bl,STD_TBL_RING ;set standard table/ring bits
+ mov [bp].intUserAX,bx
+
+ jmp i31_done
+
+cda_failed:
+ jmp i31_fail_CY
+
+i31_CreateDataAlias endp
+
+
+; -------------------------------------------------------
+; Service 00/0B - Get Descriptor
+; Service 00/0C - Set Descriptor
+; in: bx - selector
+; es:di -> buffer to hold copy of descriptor
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSetDescriptor
+
+i31_GetSetDescriptor proc near
+IFNDEF WOW
+ test bx,4
+ jz getgdtdsc
+ENDIF
+
+IFDEF WOW
+ .386p
+ push esi
+ push edi
+ .286p
+ENDIF
+ mov ax,selGDT
+ mov es,ax
+ assume es:NOTHING
+ lsl ax,ax
+
+ and bx,SELECTOR_INDEX
+
+ cmp bx,ax ; range-test selector against
+ jc @f ; the limit of the GDT/LDT
+
+IFDEF WOW
+.386p
+ pop edi
+ pop esi
+.286p
+ENDIF
+ jmp i31_fail_CY ;fail if invalid selector specified
+@@:
+ call IsSelectorFree
+ jc @f
+
+IFDEF WOW
+.386p
+ pop edi
+ pop esi
+.286p
+ENDIF
+ jmp i31_fail_CY
+@@:
+
+ cmp byte ptr [bp].intUserAX,SelMgt_Get_Desc ;Get or Set?
+ jz i31_GetDscr
+
+;
+; Set Descriptor
+;
+IFDEF WOW
+ test DpmiFlags,DPMI_32BIT
+ jnz gsd10
+.386p
+ mov esi,0 ; zero high half
+ mov edi,0
+ jmp gsd20
+
+gsd10: mov esi,edi ; get high half of edi
+ mov edi,0 ; zero high half
+.286p
+gsd20:
+ENDIF
+ push ds ;Set -
+ mov ds,[bp].pmUserES
+ assume ds:nothing
+
+ mov si,[bp].pmUserDI ; ds:si -> caller's buffer
+ mov di,bx ; es:di -> dscr slot in GDT/LDT
+
+IFDEF WOW
+ .386p
+ mov cl,ds:[esi].arbSegAccess386
+ .286p
+ELSE
+ mov cl,ds:[si].arbSegAccess386
+ENDIF
+ test cl,010000b ; system segment?
+ jz gsd25 ; y: error
+ and cl,AB_DPL ; mask off all but DPL bits
+ cmp cl,STD_DPL ; is this the correct DPL?
+ jnz gsd25 ; n: error
+ jmp short i31_MovDscr
+
+gsd25: ; set ldt format error
+ pop ds
+IFDEF WOW
+.386p
+ pop edi
+ pop esi
+.286p
+ENDIF
+ jmp i31_fail_CY
+
+i31_GetDscr:
+ assume ds:DGROUP
+;
+; Get Descriptor
+;
+IFDEF WOW
+ test DpmiFlags,DPMI_32BIT
+ jnz gsd30
+.386p
+ mov edi,0 ; zero high half of edi
+gsd30: mov esi,0 ; zero high half of esi
+
+.286p
+ENDIF
+ push ds ;Get -
+ push es
+ pop ds
+ assume ds:nothing
+ mov si,bx ; ds:si -> dscr slot in GDT/LDT
+ mov es,[bp].pmUserES
+ mov di,[bp].pmUserDI ; es:di -> caller's buffer
+
+i31_MovDscr:
+ifndef WOW
+ cld
+ mov cx,4 ;copy the descriptor
+ rep movsw
+
+ cmp byte ptr [bp].intUserAX,SelMgt_Get_Desc ; Get or Set?
+ jz i31_gs25 ; Done if get.
+
+ push ax ; notify dpmi32 of segment base change
+ mov ax,bx ; ax -> selector
+ mov cx,1 ; cx -> selector count
+ DPMIBOP SetDescriptorTableEntries
+ pop ax
+else
+ .386p
+ cCall NMoveDescriptor,<ds,esi,es,edi>
+ .286p
+endif ; WOW
+i31_gs25:
+ pop ds
+ assume ds:DGROUP
+
+IFDEF WOW
+.386p
+ pop edi
+ pop esi
+.286p
+ENDIF
+ jmp i31_done
+
+IFNDEF WOW
+getgdtdsc:
+ cmp byte ptr [bp].intUserAX,SelMgt_Get_Desc
+ je @f
+ jmp i31_Fail_CY
+
+@@: and bx, NOT 3
+ push ds
+ mov ax,SEL_GDT or STD_RING
+ mov ds,ax
+ assume ds:nothing
+ lsl ax,ax
+ sub ax,8
+ cmp ax,bx
+ ja @f
+ pop ds
+ jmp i31_Fail_CY
+
+@@: mov ax,[bp].pmUserES
+ mov es,ax
+ mov di,[bp].pmUserDI
+ mov si,bx
+ mov cx,4
+ rep movsw
+ pop ds
+ jmp i31_done
+endif
+i31_GetSetDescriptor endp
+
+; -------------------------------------------------------
+; Service 00/0D - Allocate Specific LDT Selector
+; in: bx - selector
+; out: carry clear if selector was allocated
+; carry set if selector was not allocated
+ assume ds:DGROUP,es:DGROUP
+ public i31_SpecificSel
+
+i31_SpecificSel proc near
+
+ and bx,SELECTOR_INDEX
+ cmp bx,SEL_DPMI_LAST
+ ja i31_SpecificSel_Fail
+ mov cx,bx
+ shr cl,3 ; Selector to selector index in LDT
+ mov ax,1
+ shl ax,cl ; AX = bit in i31_selectorbitmap
+ test i31_selectorbitmap,ax ; test for already allocated
+ jnz i31_SpecificSel_Fail ; allocated, fail
+ or i31_selectorbitmap,ax ; mark as allocated
+
+ ;
+ ; Set up the DPL and size
+ ;
+ mov ax,bx
+ mov cx,1
+
+ jmp setsel
+
+i31_SpecificSel_Fail: ; couldn't get that one
+ jmp i31_fail_CY
+
+i31_SpecificSel endp
+
+; -------------------------------------------------------
+ subttl INT 31h DOS Memory Services
+ page
+; -------------------------------------------------------
+; INT 31h DOS MEMORY SERVICE ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Service 01/00 - Allocate DOS Memory Block
+;
+; In: BX - # paragraphs to allocate
+; Out: If successful: Carry Clear
+; AX - segment address of block
+; DX - selector to access block
+;
+; If unsuccessful: Carry Set
+; AX - DOS error code
+; BX - size of largest available block in paragraphs
+;
+; 13-Feb-1991 -- ERH This call is not supported under Windows 3.1,
+; but try to find a block in our net heap to
+; satisfy the request, anyway.
+;
+
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_AllocDOSMem
+
+i31_AllocDOSMem proc near
+ and [bp].intUserFL,NOT 1 ; clear carry in status
+ mov cx,[bp].intUserBX
+ mov dx,[bp].intUserBX
+ shr dx,12 ; high half of byte count
+ shl cx,4 ; low half of byte count
+
+ call AllocateLowBlock
+
+ jc adm30
+
+ mov [bp].intUserDX,ax
+ mov si,ax
+
+ call GTPARA ; get paragraph address
+
+ mov [bp].intUserAX,ax
+ jmp i31_done
+
+adm30: shr cx,4
+ shl dx,12
+ or cx,dx ; paragraph size of largest
+ mov [bp].intUserBX,cx
+ mov [bp].intUserAX,ax ; error code
+ jmp i31_fail_CY
+
+i31_AllocDOSMem endp
+
+
+; -------------------------------------------------------
+; Service 01/01 - Release DOS Memory Block
+;
+; In: DX - SELECTOR of block to release
+; Out: If successful: Carry Clear
+;
+; If unsuccessful: Carry Set
+; AX - DOS error code
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_FreeDOSMem
+
+i31_FreeDOSMem proc near
+
+ mov ax,[bp].intUserDX
+ verw ax
+ jnz fdm60
+
+ call IsSelectorFree
+ jnc fdm60
+
+ mov ax,[bp].intUserDX
+ call FreeLowBlock
+
+ jc fdm60
+ jmp i31_Success
+
+fdm60: mov [bp].intUserAX,ax
+ jmp i31_fail_CY
+
+i31_FreeDOSMem endp
+
+
+; -------------------------------------------------------
+; Service 01/02 - Resize DOS Memory Block
+;
+; In: BX - new block size in paragraphs
+; DX - SELECTOR of block to release
+; Out: If successful: Carry Clear
+;
+; If unsuccessful: Carry Set
+; AX - DOS error code
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SizeDOSMem
+
+i31_SizeDOSMem proc near
+
+ mov [bp].intUserBX,0
+ mov [bp].intUserAX,08h ; insufficient mem. available
+ jmp i31_fail_CY
+
+i31_SizeDOSMem endp
+
+
+; -------------------------------------------------------
+ subttl INT 31h Real Mode Int Vector Routines
+ page
+; -------------------------------------------------------
+; INT 31h INTERRUPT SERVICES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; i31_GetSetRMInt -- Get/Set Real Mode Interrupt Vector
+;
+; In: bl - Interrupt #
+; cx:dx - SEG:Offset of real mode int vector (if set)
+; Out: cx:dx - SEG:Offset of real mode int vector (if get)
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSetRMInt
+
+i31_GetSetRMInt proc near
+
+ push SEL_RMIVT or STD_RING ;address the real mode IVT
+ pop es
+ assume es:NOTHING
+
+; HACK. Starting with Windows 3.1, we do not remap the PIC anymore. However,
+; Windows 3.0 Comm Drivers, thinking that the PIC will always be remapped,
+; deal with interrupts in the range 50h to 57h. There is now a SYSTEM.INI
+; switch to tell us that a Windows 3.0 Comm Driver is being used. If this
+; switch is set, we will map 50-57h to 8-0fh in this call.
+
+ cmp fWin30CommDrv,0 ;are we using a Windows 3.0 Comm Drv ?
+ je i31_GSRMInt_not_50to57h ;no.
+ cmp bl,50h ;is it in the range 50 to 57h ?
+ jb i31_GSRMInt_not_50to57h ;no.
+ cmp bl,57h ;is it above 57h
+ ja i31_GSRMInt_not_50to57h ;yes, no hack needed.
+
+; map from the range 50-57h to 08-0fh.
+
+ sub bl,50h-8h
+
+i31_GSRMInt_not_50to57h:
+
+ xor bh,bh ;convert int # to offset
+ shl bx,2
+
+ FCLI ;play it safe
+
+ cmp al,Int_Get_Real_Vec ;Get or Set?
+ jnz i31_gs_set
+
+ mov cx,word ptr es:[bx+2] ;get segment:offset
+ mov dx,word ptr es:[bx]
+
+ ;
+ ; If we have installed a reflector, return the original handler
+ ;
+ cmp cx,segDXCodePM
+ jne gsr20
+
+ cmp dx,offset RMIntrEntryVector
+ jb gsr20
+
+ cmp dx,offset EndRMIntrEntry
+ jae gsr20
+
+ mov cx,word ptr RmHwIsr[bx + 2]
+ mov dx,word ptr RmHwIsr[bx]
+
+gsr20: mov [bp].intUserCX,cx ;return them to caller
+ mov [bp].intUserDX,dx
+
+ jmp short i31_gs_ret
+
+i31_gs_set: ;setting the vector...
+
+ mov es:[bx],dx ;set the real mode IDT vector
+ mov es:[bx+2],cx
+
+i31_gs_ret:
+
+ FSTI
+ jmp i31_done
+
+i31_GetSetRMInt endp
+
+
+; -------------------------------------------------------
+; i31_GetSetFaultVector -- Get/Set Protected Mode Fault Vector (0h-1Fh)
+;
+; In: bl - Interrupt #
+; cx:dx - Sel:Offset of pMode int vector (if set)
+; Out: cx:dx - Sel:Offset of pMode int vector (if get)
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSetFaultVector
+
+i31_GetSetFaultVector proc near
+
+ cmp bl,10h ; zero to 10h are defined for 80386
+ jbe @f
+ jmp i31_fail_CY ;must be <= 10h or fail it
+@@:
+ xor bh,bh
+ mov ax,bx ;interrupt # to AX
+
+ cmp byte ptr [bp].intUserAX,Int_Get_Excep_Vec ;Get or Set?
+ jne i31_gfv_set
+
+ call GetFaultVector ;wants to get the vector
+ mov [bp].intUserCX,cx
+ mov [bp].intUserDX,dx
+
+ jmp short i31_gfv_ret
+
+i31_gfv_set:
+IFDEF WOW
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jnz sfv10
+
+ movzx edx,dx ; zero high half
+.286p
+ENDIF
+sfv10: call PutFaultVector ;doing a set (args already set)
+
+i31_gfv_ret:
+ jmp i31_done
+
+i31_GetSetFaultVector endp
+
+; -------------------------------------------------------
+; i31_GetSetPMInt -- Get/Set Protected Mode Interrupt Vector
+;
+; In: bl - Interrupt #
+; cx:dx - SEL:Offset of protected mode int vector (if set)
+; Out: cx:dx - SEL:Offset of protected mode int vector (if get)
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetSetPMInt
+
+i31_GetSetPMInt proc near
+
+ xchg al,bl
+ xor ah,ah
+
+ cmp bl,Int_Get_PMode_Vec ;Get or Set?
+ jnz i31_gsp_set
+
+; NOTE: we don't call DFGetIntrVector here, because all it does is a call to
+; the following routine
+
+ call GetIntrVector
+ mov [bp].intUserCX,cx
+ mov [bp].intUserDX,dx
+
+ jmp i31_gsp_done
+
+i31_gsp_set:
+
+ ; set up the appropriate real mode reflector, and hook the int.
+ call DFSetIntrVector
+
+i31_gsp_done:
+ FSTI
+ jmp i31_done
+
+i31_GetSetPMInt endp
+
+; -------------------------------------------------------
+ subttl INT 31h Protected-to-Real Mode Call/Int
+ page
+; -------------------------------------------------------
+; INT 31h PROTECTED-TO-REAL MODE CALL/INT SERVICES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Service 03/00 -- Simulate real mode interrupt
+; Service 03/01 -- Call real mode procedure with far return frame
+; Service 03/02 -- Call real mode procedure with iret return frame
+;
+; In: es:di -> client register structure for real mode
+; bl = interrupt number (03/00 only)
+; cx = # words to copy from protected mode stack
+; Out: es:di -> updated client register structure
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_RMCall
+
+i31_RMCall proc near
+
+; First off, we need to copy the client register structure down to
+; real mode addressable memory... Lets use rgbXfrBuf1 for now...
+; Changed to use the interrupt reflector stack because these routines
+; need to be reentrant. This also lets us leave interrupts enabled
+; longer.
+; Earleh - 27-Jun-1990.
+
+ mov di,sp ; DI = SP = client regs. buffer
+
+ sub di,size Real_Mode_Call_Struc+2 ; Make room for client
+ sub di,[bp].intUserCX ; call structure plus
+ sub di,[bp].intUserCX ; any optional stack params
+IFDEF WOW
+ sub di,32 ; pushad
+ENDIF
+ push di ; plus copy of di.
+IFDEF WOW
+.386p
+ pushad ; have to save high 16 bits
+.286p
+ENDIF
+
+ mov sp,di
+
+; --------------------------------------------------------------
+;
+; The interrupt reflector stack frame now looks like this...
+;
+; pbReflStack + CB_STKFRAME-> ---------------------------
+; (old pbReflStack) | |
+; | INTRSTACK Struc |
+; | from EnterIntHandler |
+; | |
+; ---------------------------
+; pbReflStack + CB_STKFRAME | word pointer |>>--\
+; - (SIZE INTRSTACK) - 2 -> --------------------------- |
+IFDEF WOW
+; | | |
+; | Pushad frame | |
+; | | |
+; --------------------------- |
+ENDIF
+; | | |
+; | optional stack params | |
+; | | |
+; --------------------------- |
+; | | |
+; | | |
+; | Real_Mode_Call_Struc | |
+; | | |
+; | | |
+; ---------------------------<<--/
+; | |
+; | Available stack space |
+; | |
+; pbReflStack---------->---------------------------
+;
+; After returning from the real mode procedure, we will need
+; to fetch the word pointer stored just below the EnterIntHandler
+; frame, and use it to access our temporary Real_Mode_Call_Struc.
+; In addition to holding the client register values, this also
+; holds the SP and BP values we will need to switch back to our
+; stack when we return from the real mode procedure.
+;
+; !!! -- Storing the optional stack parameters on top of our
+; Real_Mode_Call_Struc has several problems. It eats up stack
+; space if we call the real mode procedure on our stack, because
+; we then have to make another copy of these. It also means we have to
+; carry around a pointer to where the Real_Mode_Call_Struc lives.
+; If we didn't have the stack parameters on top of the Real_Mode_Call_Struc,
+; then we could find things strictly by offset from the value in
+; pbReflStack. The picture above should be rearranged to make optimal
+; use of space. I basically did it this way so I could have a minimum-
+; change fix for a bug in Windows 3.0.
+; !!!
+; --------------------------------------------------------------
+;
+;
+
+ cld
+IFDEF WOW
+ .386p
+ xor ecx,ecx
+ .286p
+ENDIF
+ mov cx,(size Real_Mode_Call_Struc) / 2
+
+ mov bx,di ;bx used to reference client regs below
+IFDEF WOW
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz rmc10
+
+ mov esi,edi ; copy over high 16 bits
+ jmp rmc20
+
+rmc10: xor esi,esi ; clear high 16 bits esi
+rmc20: xor edi,edi ; clear high 16 bits edi
+ mov di,bx
+.286p
+ENDIF
+ mov si,[bp].pmUserDI
+ mov ds,[bp].pmUserES
+ assume ds:NOTHING
+IFNDEF WOW
+ rep movsw
+ELSE
+.386p
+ rep movs word ptr [esi],word ptr [edi]
+.286p
+ENDIF
+
+
+; Copy stack parameters from PM to RM stack if requested by caller. To
+; avoid excessive selector munging, we do this in two steps (under the
+; assumption the # words to copy will be small). First the PM stack args
+; are copied to a buffer in DXDATA, then after switching to real mode,
+; to the real mode stack. If the caller has more stack words than will
+; fit in our buffer, or the real mode stack, bad things will happen...
+
+IFDEF WOW
+ .386p
+ xor ecx,ecx
+ .286p
+ENDIF
+ mov cx,[bp].intUserCX ;caller's CX has # stack words to copy
+ jcxz @f
+
+ Trace_Out "Int 31h PM-to-RM int/call copying #CX stack words"
+
+IFDEF WOW
+.386p
+ xor esi,esi
+.286p
+ENDIF
+ mov ds,[bp].pmUserSS
+ mov si,[bp].pmUserSP
+IFDEF WOW
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz rmc30
+;
+; Have to go groping for user stack, since we switched stacks to get
+; here.
+;
+; | |
+; +----------------+
+; | |
+; +---- SS -----+
+; | |
+; +----------------+
+; | |
+; +---- ESP -----+
+; | |
+; +----------------+
+; | Flags |
+; +----------------+
+; | CS |
+; +----------------+
+; | IP |
+; ds:si -> +----------------+
+
+ push dword ptr ds:[si + 6]
+ push word ptr ds:[si + 10]
+ pop ds
+ pop esi
+ add esi,6 ; other half of 32 bit stack frame
+
+rmc30: add esi,6 ;ds:si -> PM stack args
+ rep movs word ptr [esi],word ptr [edi]
+.286p
+ELSE
+ add si,6
+ rep movsw
+ENDIF
+ ;es:di already points to buffer
+@@:
+ push es ;restore ds -> DGROUP
+ pop ds
+ assume ds:DGROUP
+
+; Switch to real mode, set up the real mode stack
+
+ SwitchToRealMode
+ assume ds:DGROUP,es:DGROUP
+
+i31_rmcall_hw_ok:
+ FSTI ;RestoreHardwareIntr disables ints
+ ;don't need them disabled now
+
+ mov [bx].RealMode_SaveBP,bp ;save our stack in reserved area of
+ mov [bx].RealMode_SaveSP,sp ; real mode register frame
+
+ mov cx,[bp].pmUserCX ;cx = caller's CX (# stk words)
+ mov dh,byte ptr [bp].pmUserAX ;dh = caller's AL (subfunction)
+ mov dl,byte ptr [bp].pmUserBX ;dl = caller's BL (RM int #)
+
+ mov ax,[bx].RealMode_SS ;did caller specify his own stack?
+ or ax,[bx].RealMode_SP
+
+ jz @f
+
+ ; NOTE: can't reference [bp].xxUserXX varaibles after switching stacks
+
+ mov ss,[bx].RealMode_SS ;switch to caller's real mode stack
+ mov sp,[bx].RealMode_SP
+
+ assume ss:NOTHING
+@@:
+
+; Copy stack args to real mode stack if there are any
+
+ jcxz @f
+
+ sub sp,cx
+ sub sp,cx ;make space on stack for args
+
+ mov di,sp
+ mov ax,ss
+ mov es,ax ;es:di -> real mode stack
+ assume es:NOTHING
+
+ lea si,[bx + size Real_Mode_Call_Struc]
+
+ cld
+ rep movsw
+
+ push ds
+ pop es
+ assume es:DGROUP
+@@:
+
+; Put a far ret or iret frame on stack to return to us
+
+ cmp dh,Trans_Far_Call ;Does this service use a far ret or
+ jz i31_rmcall_retf ; an iret frame?
+
+ push [bx].RealMode_Flags ;real mode routine thinks these were
+ push cs ; the prior flags and CS:IP
+ push offset i31_rmcall_ret
+
+ FCLI ;flags with interrupts disabled -- real
+ pushf ; mode rtn entered with these flags
+ FSTI
+ jmp short @f
+
+i31_rmcall_retf:
+
+ push cs ;push a far ret frame so the
+ push offset i31_rmcall_ret ; real mode routine returns to us
+
+ push [bx].RealMode_Flags ;real mode rtn entered with these flags
+@@:
+ cmp dh,Trans_Sim_Int ;use an int vector, or caller's spec'd
+ jnz i31_rmcall_csip ; cs:ip?
+
+ mov al,dl ;push CS:IP for interrupt
+ xor ah,ah ; number in caller's BL
+ mov si,ax
+ shl si,2
+
+ xor ax,ax ;address real mode IDT
+ mov es,ax
+ assume es:NOTHING
+
+ push word ptr es:[si+2]
+ push word ptr es:[si]
+
+ jmp short @f
+
+i31_rmcall_csip:
+
+ push [bx].RealMode_CS ;execute the real mode routine at
+ push [bx].RealMode_IP ; specified CS:IP
+@@:
+
+; Load the clients registers, and pass control to the real mode routine
+IFNDEF WOW
+ mov di,[bx].RealMode_DI
+ mov si,[bx].RealMode_SI
+ mov bp,[bx].RealMode_BP
+ mov dx,[bx].RealMode_DX
+ mov cx,[bx].RealMode_CX
+ mov ax,[bx].RealMode_AX
+ mov es,[bx].RealMode_ES
+ assume es:NOTHING
+
+ push [bx].RealMode_DS
+ push [bx].RealMode_BX
+
+ pop bx
+ pop ds
+ assume ds:NOTHING
+ELSE
+.386p
+ mov edi,dword ptr [bx].RealMode_DI
+ mov esi,dword ptr [bx].RealMode_SI
+ mov ebp,dword ptr [bx].RealMode_BP
+ mov edx,dword ptr [bx].RealMode_DX
+ mov ecx,dword ptr [bx].RealMode_CX
+ mov eax,dword ptr [bx].RealMode_AX
+ mov es,[bx].RealMode_ES
+ assume es:NOTHING
+
+ push [bx].RealMode_DS
+ push dword ptr [bx].RealMode_BX
+
+ pop ebx
+ pop ds
+ assume ds:NOTHING
+.286p
+ENDIF
+ iret
+
+
+; The real mode routine returns here when finished
+
+i31_rmcall_ret:
+
+ pushf ;save returned flags, ds, bx on stack
+ FSTI ;don't need ints disabled
+ push ds
+IFNDEF WOW
+ push bx
+ELSE
+.386p
+ push ebx
+.286p
+ENDIF
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,segDXData ;address our DGROUP
+ENDIF
+ assume ds:DGROUP
+;
+; Fetch word pointer to temporary client register save area, that we
+; saved before switching stacks.
+;
+ mov bx,[pbReflStack]
+ mov bx,[bx + CB_STKFRAME - (SIZE INTRSTACK) - 2]
+
+; Save the real mode registers in client frame
+
+IFNDEF WOW
+ mov [bx].RealMode_DI,di
+ mov [bx].RealMode_SI,si
+ mov [bx].RealMode_BP,bp
+ mov [bx].RealMode_DX,dx
+ mov [bx].RealMode_CX,cx
+ mov [bx].RealMode_AX,ax
+ mov [bx].RealMode_ES,es
+
+ pop [bx].RealMode_BX
+ELSE
+.386p
+ mov dword ptr [bx].RealMode_DI,edi
+ mov dword ptr [bx].RealMode_SI,esi
+ mov dword ptr [bx].RealMode_BP,ebp
+ mov dword ptr [bx].RealMode_DX,edx
+ mov dword ptr [bx].RealMode_CX,ecx
+ mov dword ptr [bx].RealMode_AX,eax
+ mov [bx].RealMode_ES,es
+
+ pop dword ptr [bx].RealMode_BX
+.286p
+ENDIF
+ pop [bx].RealMode_DS
+ pop [bx].RealMode_Flags
+ or [bx].RealMode_Flags,03000h ; IOPL always has to be 3
+
+
+; Restore our stack and return to protected mode
+; SP will now point to base of temporary client register save area
+; on our stack. BP points to stack frame set up for us by EnterIntHandler.
+; SP must be restored to value in BP before calling LeaveIntHandler.
+
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,segDXData ;address our DGROUP
+ENDIF
+ mov sp,[bx].RealMode_SaveSP
+ mov bp,[bx].RealMode_SaveBP
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI ;still don't need ints disabled
+
+; Apparently in win31 standard mode, the pm caller's flags are set to the
+; value of the rm flags at the handler's iret. On win31 enhanced mode, this
+; was *not* done, and the pm flags are, except for carry, basically
+; preserved. Since setting the flags kills some apps (winfax deltest),
+; we should adhere to the enhanced mode convention. Thus, the following
+; code is if'd out.
+if 0
+ mov ax,[bx].RealMode_Flags
+ mov [bp].intUserFL,ax
+endif
+
+; Copy the updated client register frame to the caller, and we're finished
+
+ cld
+IFDEF WOW
+ .386p
+ xor ecx,ecx
+ .286p
+ENDIF
+ mov cx,(size Real_Mode_Call_Struc) / 2
+IFDEF WOW
+.386p
+ xor esi,esi
+ xor edi,edi
+
+ mov si,bx
+ add si,[bp].pmUserCX
+ add si,[bp].pmUserCX
+ add si,size Real_Mode_Call_Struc
+;
+; Si now points at pushad frame
+;
+ push si
+
+ test DpmiFlags,DPMI_32BIT
+ jz rmc80
+ mov edi,[si]
+rmc80:
+.286p
+ENDIF
+ mov si,bx
+ mov di,[bp].pmUserDI
+ mov es,[bp].pmUserES
+ assume es:NOTHING
+IFNDEF WOW
+ rep movsw
+
+ mov sp,bp ;Restore sp for LeaveIntHandler
+ELSE
+.386p
+ rep movs word ptr [esi],word ptr [edi]
+
+ pop sp ; value calculated above
+ popad
+ mov sp,bp
+.286p
+ENDIF
+
+ jmp i31_done ;finished!
+
+i31_RMCall endp
+
+
+; -------------------------------------------------------
+; Service 03/03 -- Allocate Real Mode Call-Back Address
+;
+; In: ds:si -> pMode CS:IP to be called when rMode
+; call-back address executed
+; es:di -> client register structure to be updated
+; when call-back address executed
+; Out: cx:dx -> SEGMENT:offset of real mode call-back hook
+; CY clear if successful, CY set if can't allocate
+; call back
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_AllocCallBack
+
+i31_AllocCallBack proc near
+
+; Search call-back table for a free entry
+
+ mov bx,offset DGROUP:CallBackTable
+ mov cx,cRM_CALL_BACK
+@@:
+ test [bx].fInUse,0FFh ;in use?
+ jz i31_avail_callback
+ add bx,size CallBackTableStruc
+ loop @b
+
+ jmp i31_fail_CY ;no call-backs available, fail
+
+; Fill in the call back table entry with caller's info
+
+i31_avail_callback:
+
+ mov [bx].fInUse,0FFh ;mark as in use
+
+ mov ax,[bp].pmUserDS
+IFNDEF WOW
+ mov word ptr [bx].PM_CS_IP,si ;store pMode call-back CS:IP
+ mov word ptr [bx].PM_CS_IP+2,ax
+ELSE
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz acb20
+ mov [bx].PM_CS_IP,esi
+ mov [bx].PM_Client_Frame,edi
+ mov word ptr [bx].PM_CS_IP + 4,ax
+ jmp acb30
+
+acb20: mov word ptr [bx].PM_CS_IP,si
+ mov word ptr [bx].PM_Client_Frame,di
+ mov word ptr [bx].PM_CS_IP + 4,ax
+ mov ax,0
+ mov word ptr [bx].PM_CS_IP + 2,ax
+ mov word ptr [bx].PM_Client_Frame + 2,ax
+acb30:
+.286p
+ENDIF
+
+ mov ax,[bp].pmUserES
+IFNDEF WOW
+ mov word ptr [bx].PM_Client_Frame,di
+ mov word ptr [bx].PM_Client_Frame+2,ax
+ELSE
+.386p
+ mov word ptr [bx].PM_Client_Frame + 4,ax
+.286p
+ENDIF
+
+; Return Seg:Off of Real Mode call-back to caller in CX:DX
+
+ mov ax,segDXCodePM ;segment
+ mov [bp].intUserCX,ax
+
+ sub cx,cRM_CALL_BACK
+ neg cx ;cx = allocated entry # (0..n)
+
+ errnz <cRM_CALL_BACK gt 255> ;code assumes cx <= 255
+
+ mov al,3 ;convert entry # to IP
+ mul cl ;3 bytes per entry
+ add ax,offset DXCODE:RMCallBackEntryVector
+ mov [bp].intUserDX,ax
+
+ jmp i31_done
+
+i31_AllocCallBack endp
+
+
+; -------------------------------------------------------
+; Service 03/04 -- Free Real Mode Call-Back Address
+;
+; In: cx:dx -> SEGMENT:offset of rMode call-back to free
+; Out: CY clear if successful, CY set if failure
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_FreeCallBack
+
+i31_FreeCallBack proc near
+
+; Determine which Real Mode Call-Back is being freed
+
+ cmp cx,segDXCodePM ;sanity check the incoming CS:IP
+ jne i31_FreeCBFail
+
+ cmp dx,offset DXCODE:RMCallBackEntryVector
+ jb i31_FreeCBFail
+
+ mov ax,dx
+ sub ax,offset DXCODE:RMCallBackEntryVector
+ mov bl,3
+ div bl ;al = entry # (0..n)
+
+ cmp al,cRM_CALL_BACK ;past end of table?
+ jae i31_FreeCBFail
+
+ errnz <size CallBackTableStruc gt 255>
+
+ mov bl,size CallBackTableStruc
+ mul bl
+ mov bx,ax
+ add bx,offset DGROUP:CallBackTable ;bx -> CallBackTable entry
+
+ mov [bx].fInUse,0 ;mark it free
+
+ jmp i31_done ;finished!
+
+i31_FreeCBFail:
+
+ jmp i31_fail_CY
+
+i31_FreeCallBack endp
+
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; RMCallBackHook -- Real Mode to Protected Mode Call-Back Hook
+;
+; This routine gets control whenever a user's real mode call-back
+; hook is executed. This routine saves the current registers in
+; the specified client register frame, switches to protected mode,
+; and invokes the specified pMode CS:IP. Upon return from the
+; pMode call-back, the registers are restored from a client frame
+; and control is passed to the CS:IP specified in the frame.
+;
+
+ public RMCallBackHook
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+RMCallBackHook proc
+
+ pushf ;save current flags
+
+ FCLI ;protect global variables
+ cld ;and we expect direction to be 'normal'
+
+ push ds ;address our DGROUP
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+
+ pop regUserDS ;save regs in global vars -- keep ints off!
+ pop regUserFL
+ mov regUserAX,ax
+ mov regUserES,es
+
+ pop ax ;ax = entry vector return address
+
+; Convert the entry vector return address into a call-back table pointer
+
+ push bx
+
+ sub ax,offset RMCallBackEntryVector+3
+ mov bl,3
+ div bl
+ mov bl,size CallBackTableStruc
+ mul bl
+ add ax,offset DGROUP:CallBackTable
+
+ pop bx
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame, and switch to pMode.
+
+ mov regUserSS,ss ;save current user stack
+ mov regUserSP,sp
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,selDgroup ;switch to the reflector stack frame
+ENDIF
+ mov sp,pbReflStack
+ sub pbReflStack,2 * CB_STKFRAME ;adjust pointer to next stack frame
+
+ FIX_STACK
+ DEBUG_TRACE_RM DBGTR_STACKPTR, pbReflStack, 0, 0
+ push ax ;save CallBackTable pointer
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ mov ax,bx ;save bx in ax
+ pop bx ;CallBackTable pointer to bx
+
+; Store current registers into Client register frame
+
+IFNDEF WOW
+ push di
+
+ les di,[bx].PM_Client_Frame
+ assume es:NOTHING
+
+ pop es:[di].RealMode_DI
+ mov es:[di].RealMode_BX,ax
+ mov es:[di].RealMode_CX,cx
+ mov es:[di].RealMode_DX,dx
+ mov es:[di].RealMode_SI,si
+ mov es:[di].RealMode_BP,bp
+ mov ax,regUserAX
+ mov es:[di].RealMode_AX,ax
+ELSE
+.386p
+ push edi
+
+ les edi,fword ptr [bx].PM_Client_Frame
+ assume es:NOTHING
+
+ pop dword ptr es:[di].RealMode_DI
+ push bx
+ mov bx,ax
+ mov dword ptr es:[di].RealMode_BX,ebx
+ pop bx
+ mov dword ptr es:[di].RealMode_CX,ecx
+ mov dword ptr es:[di].RealMode_DX,edx
+ mov dword ptr es:[di].RealMode_SI,esi
+ mov dword ptr es:[di].RealMode_BP,ebp
+ mov ax,regUserAX
+ mov dword ptr es:[di].RealMode_AX,eax
+.286p
+ENDIF
+ mov ax,regUserDS
+ mov es:[di].REalMode_DS,ax
+ mov ax,regUserES
+ mov es:[di].RealMode_ES,ax
+ mov dx,regUserSS ;keep real mode SS in DX
+ mov es:[di].RealMode_SS,dx
+ mov si,regUserSP ; and rMode SP in SI
+ mov es:[di].RealMode_SP,si
+ mov ax,regUserFL
+ mov es:[di].RealMode_Flags,ax
+
+
+; Map a selector to the real mode stack segment
+
+ call AllocateLDTSelector
+ jnc @f
+ mov ax,SEL_USERSCR or STD_TBL_RING ;use a scratch if can't alloc
+@@:
+ push ax ;save selector on our stack
+
+ push bx ;save CallBackTable entry pointer
+
+ call ParaToLinear ;convert SS in DX to BX:DX lma
+ mov cx,-1
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ pop bx
+
+; Build an IRET frame on the stack for the pMode routine to return to us
+IFDEF WOW
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz rmcbh10
+
+ pushfd
+ sub sp,2
+ push cs
+ push dword ptr (offset i31_CBret)
+ jmp rmcbh20
+.286p
+ENDIF
+
+rmcbh10:
+ pushf
+ push cs
+ push offset i31_CBret
+
+rmcbh20:
+; Build a far ret frame to give control to user's Call-Back routine
+
+IFNDEF WOW
+ push word ptr [bx].PM_CS_IP+2
+ push word ptr [bx].PM_CS_IP
+
+; Do final register setups, and give control to pMode call-back routine
+
+ mov ds,ax ;DS:SI -> real mode stack
+ assume ds:NOTHING
+
+ public PMCallBack
+PMCallBack:
+ retf ;and away we go...
+ELSE
+.386p
+ push dword ptr [bx].PM_CS_IP+4
+ push dword ptr [bx].PM_CS_IP
+
+; Do final register setups, and give control to pMode call-back routine
+
+ mov ds,ax ;DS:SI -> real mode stack
+ assume ds:NOTHING
+
+ public PMCallBack
+PMCallBack:
+ db 066h
+ retf ;and away we go...
+.286p
+ENDIF
+; The pMode call-back routine returns here when finished
+
+i31_CBret:
+ cld ;take no chances
+
+ mov ds,selDgroupPM ;restore DGROUP addressability
+ assume ds:DGROUP
+
+ pop ax ;recover selector for RM stack seg
+ and al,SELECTOR_INDEX ;clear ring/table bits
+
+ cmp ax,SEL_USERSCR ;free the selector, unless it was
+ jz @f ; a scratch selector
+ call FreeSelector
+@@:
+
+; Copy the returned register Client frame to our stack so we can access
+; it from real mode
+
+ sub sp,size Real_Mode_Call_Struc+2
+ mov si,sp
+
+ push es ;ES:DI, DS:SI need to be reversed
+ mov es,selDgroupPM ; for rep movs
+ assume es:DGROUP
+ pop ds
+ assume ds:NOTHING
+
+IFDEF WOW
+ .386p
+ push ecx
+ xor ecx,ecx
+ .286p
+ENDIF
+ mov cx,(size Real_Mode_Call_Struc) / 2
+IFDEF WOW
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jz rmcbh80
+
+ push si
+ xor esi,esi
+ pop si
+ xchg esi,edi
+ rep movs word ptr [esi], word ptr [edi]
+ jmp rmcbh90
+.386p
+ENDIF
+rmcbh80:
+ xchg si, di
+ rep movsw
+
+rmcbh90:
+IFDEF WOW
+ .386p
+ pop ecx
+ .286p
+ENDIF
+ push es ;everybody assumes ds=dgroup
+ pop ds
+ assume ds:DGROUP
+
+; Switch to real mode, and switch to user specified stack
+
+ SwitchToRealMode ;leaves ints disabled
+ assume ds:DGROUP,es:DGROUP
+
+ mov bx,sp ;use bx as ptr to client reg frame
+
+ CHECK_STACK
+ mov ss,[bx].RealMode_SS
+ mov sp,[bx].RealMode_SP
+
+; Restore real mode registers as specified in client frame (all but DS & BX)
+
+IFNDEF WOW
+ mov ax,[bx].RealMode_AX
+ mov cx,[bx].RealMode_CX
+ mov dx,[bx].RealMode_DX
+ mov si,[bx].RealMode_SI
+ mov di,[bx].RealMode_DI
+ mov bp,[bx].RealMode_BP
+ELSE
+.386p
+ mov eax,dword ptr [bx].RealMode_AX
+ mov ecx,dword ptr [bx].RealMode_CX
+ mov edx,dword ptr [bx].RealMode_DX
+ mov esi,dword ptr [bx].RealMode_SI
+ mov edi,dword ptr [bx].RealMode_DI
+ mov ebp,dword ptr [bx].RealMode_BP
+.286p
+ENDIF
+ mov es,[bx].RealMode_ES
+ assume es:NOTHING
+
+; Build an IRET frame to return to specified CS:IP, and do it
+
+ push [bx].RealMode_Flags
+ push [bx].RealMode_CS
+ push [bx].RealMode_IP
+
+ push [bx].RealMode_DS ;last 2 regs to restore
+IFNDEF WOW
+ push [bx].RealMode_BX
+ pop bx
+ELSE
+.386p
+ push dword ptr [bx].RealMode_BX
+ pop ebx
+.286p
+ENDIF
+ add pbReflStack,2 * CB_STKFRAME ;deallocate our stack frame
+
+ pop ds
+
+ iret ;finished!
+
+RMCallBackHook endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+ subttl INT 31h Memory Management Services
+ page
+; -------------------------------------------------------
+; INT 31h MEMORY MANAGEMENT SERVICES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Service 05/00 -- Get Free Memory Information
+;
+; In: es:di -> 30h byte buffer
+; Out: es:di -> Largest free block (in bytes) placed at
+; start of caller's buffer
+; Rest of buffer filled in with -1.
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_GetFreeMem
+
+i31_GetFreeMem proc near
+ mov es,[bp].pmUserES ; Point to user buffer
+ mov di,[bp].pmUserDI
+ assume es:NOTHING
+
+
+ FBOP BOP_DPMI, GetMemoryInformation, FastBop
+ jmp i31_done
+
+i31_GetFreeMem endp
+
+
+
+; -------------------------------------------------------
+; Service 05/01 -- Allocate Memory Block
+;
+; In: bx:cx - size of block to allocate in bytes
+; Out: successful, carry clear &
+; bx:cx = linear memory address of block
+; si:di = handle to block
+; failed, carry set
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_AllocMem
+
+i31_AllocMem proc near
+
+ mov bx,[bp].pmUserBX
+ mov cx,[bp].pmUserCX
+ mov dx, selPSPChild
+
+ FBOP BOP_DPMI, AllocXmem, FastBop
+
+ jnc @f
+
+ jmp i31_fail_CY
+
+@@: mov [bp].intUserBX,bx
+ mov [bp].intUserCX,cx
+ mov [bp].intUserSI,si
+ mov [bp].intUserDI,di
+ jmp i31_Done
+
+i31_AllocMem endp
+
+
+; -------------------------------------------------------
+; Service 05/02 -- Free Memory Block
+;
+; In: si:di - 'handle' of block to free
+; Out: successful, carry clear
+; failed, carry set
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_FreeMem
+
+i31_FreeMem proc near
+
+ mov si,[bp].pmUserSI
+ mov di,[bp].pmUserDI
+
+ FBOP BOP_DPMI, FreeXmem, FastBop
+
+ jnc @f
+
+ jmp i31_fail_CY
+
+@@: jmp i31_Done
+
+i31_FreeMem endp
+
+
+; -------------------------------------------------------
+; Service 05/03 -- Resize Memory Block
+;
+; In: bx:cx - new size of block to allocate in bytes
+; si:di - 'handle' of block to free
+; Out: successful, carry clear &
+; bx:cx = linear memory address of block
+; si:di = handle to block
+; failed, carry set
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_SizeMem
+
+i31_SizeMem proc near
+
+ mov bx,[bp].pmUserBX
+ mov cx,[bp].pmUserCX
+ mov si,[bp].pmUserSI
+ mov di,[bp].pmUserDI
+
+ FBOP BOP_DPMI, ReallocXmem, FastBop
+
+ jnc @f
+
+ jmp i31_fail_CY
+
+@@: mov [bp].intUserBX,bx
+ mov [bp].intUserCX,cx
+ mov [bp].intUserSI,si
+ mov [bp].intUserDI,di
+ jmp i31_Done
+
+i31_SizeMem endp
+
+
+; -------------------------------------------------------
+; Service 09/00 -- Get and Disable Virtual Interrupt State
+; 09/01 -- Get and Enable Virtual Interrupt State
+; 09/02 -- Get Virtual Interrupt State
+;
+; In: none
+; Out: AL = previous interrupt state
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_VirtualInt
+
+i31_VirtualInt proc near
+
+ mov ah,byte ptr [bp].intUserFL+1 ;get/isolate user's IF in AH
+ shr ah,1
+ and ah,1
+
+ cmp byte ptr [bp].intUserAX,Get_Int_State ;only getting state?
+ jz @f ; yes, skip set
+
+ mov al,byte ptr [bp].intUserAX ;get desired state
+ shl al,1 ; move into IF position
+
+ and byte ptr [bp].intUserFL+1,not 02h ;clr old IF bit
+ or byte ptr [bp].intUserFL+1,al ; set desired
+@@:
+ mov byte ptr [bp].intUserAX,ah ;return old state in user's AL
+
+ jmp i31_done
+
+i31_VirtualInt endp
+
+; -------------------------------------------------------
+ subttl INT 31h Utility Routines
+ page
+; -------------------------------------------------------
+; INT 31h UTILITY ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; i31_Version -- Return Int 31h version information.
+;
+; In: none
+; Out: ah - major version
+; al - minor version
+; bx - flags
+; cl - processor type
+; dh - master PIC base interrupt
+; dl - slave PIC bas interrupt
+;
+
+ public i31_Version
+ assume ds:DGROUP,es:DGROUP
+
+i31_Version proc near
+
+ mov [bp].intUserAX,I31VERSION
+ mov [bp].intUserBX,I31FLAGS
+ mov al,byte ptr idCpuType
+ mov byte ptr [bp].intUserCX,al
+ mov [bp].intUserDX,(I31MasterPIC SHL 8) OR I31SlavePIC
+
+ jmp i31_done
+
+i31_Version endp
+
+; -------------------------------------------------------
+; i31_MemGetHeader -- Get selector to our header on a Int 31h allocated
+; DOS memory block.
+;
+; In: DX - SELECTOR of block to get header of
+; Out: ES - selector pointing to our header for block
+; Uses: none
+
+ public i31_MemGetHeader
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+i31_MemGetHeader proc near
+
+; User wants to release block pointed to by selector in DX. Use a scratch
+; selector to point 1 paragraph before that to make sure this is a block
+; we allocated, and get some misc info
+
+ push ax
+ push bx
+ push cx
+ push dx
+
+ mov ax,dx
+ call GetSegmentAddress ;BX:DX -> user's data area
+
+ sub dx,10h ;backup one paragraph
+ sbb bx,0
+
+ mov cx,0fh
+ mov ax,SEL_SCR0 or STD_TBL_RING
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ mov es,ax
+
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+i31_MemGetHeader endp
+
+; -------------------------------------------------------
+; RZCall -- Utility routine to call a Ring
+; Zero procedure. Stack parameter is the NEAR
+; address of the routine in the DXPMCODE segment to
+; call. The called routine must be declared FAR
+; and take no stack parameters.
+;
+; USES: Whatever Ring 0 routine uses
+; +Flags
+; RETURNS: Whatever Ring 0 routine returns
+;
+; NOTE: Assumes that interrupts must be disabled
+; for the Ring 0 routine.
+;
+; History:
+; 12-Feb-1991 -- ERH wrote it!!!
+; -------------------------------------------------------
+
+My_Call_Gate dd (SEL_SCR0 or STD_TBL_RING) shl 10h
+
+ public RZCall
+RZCall proc near
+
+ pushf
+ FCLI
+ push bp
+ mov bp,sp
+ cCall NSetSegmentDscr,<SEL_SCR0,0,SEL_EH,0,[bp+6],STD_CALL>
+ pop bp
+
+ call dword ptr My_Call_Gate
+
+ cCall NSetSegmentDscr,<SEL_SCR0,0,0,0,-1,STD_DATA>
+
+ npopf
+
+ retn 2
+
+RZCall endp
+
+ife NO386
+
+i31_Debug_Register_Access proc near
+
+ or byte ptr [bp].intUserFL,1 ;set carry flag
+ cmp idCpuType,3 ;at least 386?
+ jb i31_Debug_Register_Access_Done ;No.
+
+IFNDEF WOW
+ push offset DXPMCODE:I31_Win386_Call
+ call RZCall
+ELSE
+ call far ptr I31_Win386_Call
+ENDIF
+i31_Debug_Register_Access_Done:
+ jmp i31_exit
+
+i31_Debug_Register_Access endp
+
+ .386
+
+;******************************************************************************
+;
+; I31_Win386_Call
+;
+; The routine to implement the INT 31h debug register functions is copied
+; directly from the WIN386 routine in vmm/int31.asm. This routine sets
+; up the 386 registers so that the actual API is callable from the 16-bit
+; DOS Extender.
+;
+;******************************************************************************
+I31_Win386_Call proc far ; Called via ring-transition call gate
+;
+; Save 386 extended registers that are used by the WIN386 code.
+;
+ pushad
+;
+; Call the actual routine
+;
+ movzx ebp,bp
+
+ call I31_Debug_Register_Support
+;
+; Restore 386 extended registers that are used by the WIN386 code.
+;
+ popad
+ ret
+
+I31_Win386_Call endp
+
+DXPMCODE ends
+
+;******************************************************************************
+; R E C O R D S
+;******************************************************************************
+
+;
+; The following record defines the debug status register.
+;
+DBG6 RECORD d6_r1:16,d6_bt:1,d6_bs:1,d6_bd:1,d6_r2:9,d6_b3:1,d6_b2:1,d6_b1:1,d6_b0:1
+DBG6_RESERVED EQU (MASK d6_r1) OR (MASK D6_r2)
+
+;
+; The following record defines the debug control register.
+;
+DBG7 RECORD d7_ln3:2,d7_rw3:2,d7_ln2:2,d7_rw2:2,d7_ln1:2,d7_rw1:2,d7_ln0:2,d7_rw0:2,d7_rs:6,d7_ge:1,d7_le:1,d7_g3:1,d7_l3:1,d7_g2:1,d7_l2:1,d7_g1:1,d7_l1:1,d7_g0:1,d7_l0:1
+DBG7_RESERVED EQU (MASK d7_rs)
+
+Num_Watchpoints EQU 4 ; Only 4 live watchpoints in 386, 486.
+
+;******************************************************************************
+; m a c r o s
+;******************************************************************************
+
+;******************************************************************************
+;
+; The following set of macros allows us to copy WIN386 code directly
+;
+;******************************************************************************
+
+BeginProc macro fname
+ fname proc near
+endm
+
+EndProc macro fname
+ fname endp
+endm
+
+Assert_Client_Ptr macro dummy
+endm
+
+Client_Flags equ intUserFL
+Client_EAX equ intUserAX
+Client_AX equ intUserAX
+Client_BX equ intUserBX
+Client_CX equ intUserCX
+Client_DX equ intUserDX
+
+CF_MASK equ 1
+
+OFFSET32 equ <OFFSET>
+
+
+
+;******************************************************************************
+; d a t a
+;******************************************************************************
+
+DXDATA segment
+
+PUBLIC Debug_Regs
+;
+; A memory image of the debug registers. The first version assumes
+; that we have complete control over the debug registers, so only
+; one copy is needed.
+;
+ ALIGN DWORD
+Debug_Regs LABEL DWORD
+DD_DR0 dd 0
+DD_DR1 dd 0
+DD_DR2 dd 0
+DD_DR3 dd 0
+
+DD_DR6 dd 0
+DD_DR7 dd 0
+
+DXDATA ends
+
+ .386p
+
+DXPMCODE segment
+
+;******************************************************************************
+;
+; I31_Debug_Register_Support
+;
+; ENTRY: AL = Function code
+; 01 -- Set debug watchpoint
+; BX:CX = linear address of watchpoint
+; DL = size of watchpoint (1, 2, or 4)
+; DH = type of watchpoint
+; 0 = Execute
+; 1 = Write
+; 2 = Read/Write
+;
+; Returns
+; BX = debug watchpoint handle
+;
+; 02 -- Clear debug watchpoint
+;
+; 03 -- Get state of debug watchpoint
+; Returns
+; AX = bit 0 set if watch point has been executed
+;
+; 04 -- Reset debug watchpoint
+;
+; EXIT: Carry clear in client flags if successful
+; Carry set in client flags if not
+;
+; USES: EDI, EAX, ECX, EDX, ESI
+;
+; History:
+; 08-Feb-1991 -- ERH wrote it.
+;
+;******************************************************************************
+BeginProc I31_Debug_Register_Support
+
+IFNDEF WOW
+ call Store_DBG_Regs ; make copy of debug regs.
+ENDIF
+
+ test [DD_DR6],MASK d6_bd
+ jnz DD_I31_Error ; ICE-386 is active!
+ and [ebp.Client_Flags],NOT CF_MASK
+
+ mov eax, dword ptr [ebp.Client_EAX]
+ cmp al, 03h
+ ja DD_I31_Error
+ je DD_I31_Reset_Watchpoint
+ cmp al, 01h
+ ja DD_I31_Get_Status
+ je DD_I31_Clear_Watchpoint
+
+;
+; Function AX = 0A00h -- Set Debug Watchpoint
+;
+PUBLIC DD_I31_Set_Watchpoint
+DD_I31_Set_Watchpoint:
+ xor ecx, ecx
+ mov edx,(MASK d7_l0) OR (MASK d7_g0); EDX = DR0 enable bits
+DD_I31_Search_Loop:
+;
+; Look for an unused breakpoint. In order for a breakpoint to be
+; unused, the corresponding global and local enable bits must be clear.
+;
+ test [DD_DR7],edx
+ jz SHORT DD_I31_SW_Found_One
+DD_I31_SkipIt:
+ shl edx,2 ; EDX = next BP's enable bits
+ inc ecx
+ cmp ecx, Num_Watchpoints
+ jb DD_I31_Search_Loop
+ jmp DD_I31_Error
+
+DD_I31_SW_Found_One:
+ mov esi, OFFSET32 Debug_Regs
+ mov eax,ecx
+ shl eax,2
+ add esi,eax ; ESI -> BP address buffer
+ mov ax, [ebp.Client_BX]
+ shl eax, 16
+ mov ax, [ebp.Client_CX] ; EAX = linear address
+ mov dword ptr [esi],eax ; record address
+ or [DD_DR7],edx ; enable the break point
+
+ mov edx,NOT((MASK d7_ln0) OR (MASK d7_rw0))
+ shl cl,2
+ rol edx,cl
+ shr cl,2
+ and [DD_DR7],edx ; clear type and size bits
+;
+; Client_DX =
+; DH = 0 : execute
+; DH = 1 : write
+; DH = 2 : read/write
+;
+; DL = 1 : byte
+; DL = 2 : word
+; DL = 4 : dword
+;
+ movzx edx,[ebp.Client_DX] ; load BP size and type
+ cmp dh,0 ; execute?
+ jne SHORT @F ; no
+ mov dl,1 ; yes, force size zero
+@@:
+
+ cmp dl,4 ; check for valid values
+ ja DD_I31_Error ; size in dl is 1, 2, or 4
+ cmp dl,3
+ je DD_I31_Error
+ dec dl ; DL = 0, 1, or 3 for DR7
+ js DD_I31_Error ; len field
+
+ cmp dh,2 ; type in dh is 0, 1, or 2
+ ja DD_I31_Error
+
+ jne SHORT @F
+ inc dh ; 386 wants 3, not 2
+@@: ; DH = RWn field
+ shl dl,2
+ or dl,dh
+ xor dh,dh
+ shl edx,d7_rw0
+ shl cl,2
+ shl edx,cl
+ or [DD_DR7],edx ; set size, type
+ shr cl,2
+
+ mov edx,NOT (MASK d6_b0) ; clear triggered bit
+ rol edx,cl ; EDX = mask to clear hit
+ and [DD_DR6],edx ; clear triggered bit
+ mov [ebp.Client_BX],cx ; return address register
+ ; number as BP handle
+ or [DD_DR7],(MASK d7_ge) OR (MASK d7_le)
+ ; enable debugging
+ call Load_DBG_Regs ; load changes into debug registers
+ ret
+;
+; Function AX = 0A01h -- Clear Debug Watchpoint
+;
+; Error if Watchpoint not previously set. In that case, do nothing.
+; If Watchpoint was set, then clear enable bits, triggered bit, and
+; breakpoint address.
+;
+PUBLIC DD_I31_Clear_Watchpoint
+DD_I31_Clear_Watchpoint:
+ movzx ecx,[ebp.Client_BX]
+ cmp ecx, Num_Watchpoints
+ jnb DD_I31_Error
+
+ mov edx,(MASK d7_l0) OR (MASK d7_g0); EDX = enable DR0 mask
+ shl edx,cl
+ shl edx,cl ; EDX = enable DRn mask
+ test [DD_DR7],edx ; BP set?
+ jz DD_I31_Error ; No, error.
+ not edx
+ and [DD_DR7],edx ; disable the BP
+ mov edx,NOT (MASK d6_b0) ; EDX = DR0 not hit
+ rol edx,cl ; EDX = DRn not hit
+ and [DD_DR6],edx ; clear triggered bit
+ mov esi, OFFSET32 Debug_Regs ; ESI -> DRn table
+ shl ecx,2 ; ECX = DWORD offset
+ add esi,ecx ; ESI -> DRn
+ mov dword ptr [esi],0 ; clear address
+ mov edx,NOT((MASK d7_ln0) OR (MASK d7_rw0))
+ rol edx,cl
+ and [DD_DR7],edx
+;
+; Test whether this leaves any breakpoints active. If not, disable
+; the exact match condition. Note: the following is a long line.
+;
+ test [DD_DR7],(MASK d7_g3) OR (MASK d7_l3) OR (MASK d7_g2) OR (MASK d7_l2) OR (MASK d7_g1) OR (MASK d7_l1) OR (MASK d7_g0) OR (MASK d7_l0)
+ jne SHORT @F
+ and [DD_DR7],NOT ((MASK d7_ge) OR (MASK d7_le))
+@@:
+ call Load_DBG_Regs ; load changes into debug registers
+ ret
+;
+; Function AX = 0A02h -- Get Status of Debug Watchpoint
+;
+PUBLIC DD_I31_Get_Status
+DD_I31_Get_Status:
+ movzx ecx,[ebp.Client_BX]
+ cmp ecx, Num_Watchpoints
+ jnb SHORT DD_I31_Error
+
+ mov edx,(MASK d7_g0) OR (MASK d7_l0); EDX = DR0 enable bits
+ shl edx,cl
+ shl edx,cl ; EDX = DRn enable bits
+ test [DD_DR7],edx ; DRn enabled?
+ jz SHORT DD_I31_Error ; No, error.
+ mov edx,MASK d6_b0 ; EDX = DR0 hit mask
+ shl edx,cl ; EDX = DRn hit mask
+ xor eax,eax
+ test [DD_DR6],edx ; DRn hit?
+ jne SHORT @F ; no
+ inc al ; yes, store result
+@@:
+ mov [ebp.Client_AX],ax
+ ret
+;
+; Function AX = 0A03h -- Reset Debug Watchpoint
+;
+PUBLIC DD_I31_Reset_Watchpoint
+DD_I31_Reset_Watchpoint:
+ movzx ecx,[ebp.Client_BX]
+ cmp ecx, Num_Watchpoints
+ jnb SHORT DD_I31_Error
+
+ mov edx,(MASK d7_g0) OR (MASK d7_l0); EDX = DR0 enable bits
+ shl edx,cl
+ shl edx,cl ; EDX = DRn enable bits
+ test [DD_DR7],edx ; DRn enabled?
+ jz SHORT DD_I31_Error ; No, error.
+ mov edx,NOT (MASK d6_b0) ; EDX = DR0 hit mask
+ rol edx,cl ; EDX = DRn hit mask
+ and [DD_DR6],edx ; clear triggered bit
+ call Load_DBG_Regs ; load changes into debug registers
+ ret
+
+DD_I31_Error:
+ Assert_Client_Ptr ebp
+ or [ebp.Client_Flags], CF_Mask
+ ret
+
+EndProc I31_Debug_Register_Support
+
+
+;******************************************************************************
+;
+; Load_DBG_Regs - Load debug registers from memory.
+; Do not change undefined bits.
+;
+; ENTRY: NONE
+; EXIT: Memory image copied to debug registers, undefined bits unchanged.
+; USES: eax, ecx, edi, esi
+;
+;******************************************************************************
+
+BeginProc Load_DBG_Regs
+
+ mov esi,OFFSET32 Debug_Regs
+ DPMIBOP SetDebugRegisters
+ jnc short ldr_exit
+
+ cld
+ xor eax, eax
+ mov ecx, 6
+ mov edi, OFFSET32 Debug_Regs
+ rep stosd ;clear local copy
+ldr_exit:
+ ret
+
+EndProc Load_DBG_Regs
+
+; -------------------------------------------------------
+
+IFNDEF WOW
+;******************************************************************************
+;
+; Load_DBG_Regs - Load debug registers from memory.
+; Do not change undefined bits.
+;
+; ENTRY: NONE
+; EXIT: Memory image copied to debug registers, undefined bits unchanged.
+; USES: NONE
+;
+;******************************************************************************
+
+BeginProc Load_DBG_Regs
+
+ push esi
+ push edx
+ push eax
+
+ cld
+
+ mov esi,OFFSET32 Debug_Regs
+ lods dword ptr ds:[esi]
+ mov dr0, eax
+ lods dword ptr ds:[esi]
+ mov dr1, eax
+ lods dword ptr ds:[esi]
+ mov dr2, eax
+ lods dword ptr ds:[esi]
+ mov dr3, eax
+ lods dword ptr ds:[esi]
+ and eax,NOT DBG6_RESERVED
+ mov edx,dr6
+ and edx,DBG6_RESERVED
+ or eax,edx
+ mov dr6, eax
+.ERRNZ dd_dr6 - dd_dr3 - 4
+ lods dword ptr ds:[esi]
+ and eax,NOT DBG7_RESERVED
+ mov edx,dr7
+ and edx,DBG7_RESERVED
+ or eax,edx
+ mov dr7, eax
+
+ pop eax
+ pop edx
+ pop esi
+
+ ret
+
+EndProc Load_DBG_Regs
+
+;******************************************************************************
+;
+; Store_DBG_Regs - Copy debug registers to memory.
+;
+; ENTRY: NONE
+; EXIT: Debug registers copied to memory image.
+; Undefined bits = don't care.
+; USES: NONE
+;
+;******************************************************************************
+
+BeginProc Store_DBG_Regs
+
+ push eax
+ push edi
+
+ cld
+
+ mov edi,OFFSET32 Debug_Regs
+ mov eax, dr0
+ stos dword ptr es:[edi]
+ mov eax, dr1
+ stos dword ptr es:[edi]
+ mov eax, dr2
+ stos dword ptr es:[edi]
+ mov eax, dr3
+ stos dword ptr es:[edi]
+ mov eax, dr6
+.ERRNZ dd_dr6 - dd_dr3 - 4
+ stos dword ptr es:[edi]
+ mov eax, dr7
+ stos dword ptr es:[edi]
+
+ pop edi
+ pop eax
+
+ ret
+
+EndProc Store_DBG_Regs
+ENDIF ; WOW
+
+; -------------------------------------------------------
+endif ; NO386
+
+
+; -------------------------------------------------------
+ subttl INT 31h Raw Modeswitch Routines
+ page
+; -------------------------------------------------------
+; INT 31h Raw Modeswitch Routines
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; i31_GetSaveRestoreState -- Return Int 31h Save/Restore State addresses.
+;
+; In: none
+; Out: ax - size of buffer required to save state
+; bx:cx - real mode address used to save/restore state
+; si:di - protected mode address used to save/restore state
+;
+
+ public i31_GetStateSaveRestore
+ assume ds:DGROUP,es:DGROUP
+
+i31_GetStateSaveRestore proc near
+
+ mov [bp].intUserAX,0
+ push es
+ push SEL_DXCODE OR STD_RING
+ pop es
+ assume es:DXCODE
+ mov ax,segDXCode
+ pop es
+ assume es:DGROUP
+ mov [bp].intUserBX,ax
+ mov [bp].intUserCX,offset DXCODE:RmSaveRestoreState
+ mov [bp].intUserSI,SEL_DXPMCODE OR STD_RING
+IFDEF WOW
+ test DpmiFlags,DPMI_32BIT
+ jz gssr10
+
+ mov edi,dword ptr (offset DXPMCODE:PmSaveRestoreState)
+ENDIF
+gssr10: mov [bp].intUserDI,offset DXPMCODE:PmSaveRestoreState
+
+ jmp i31_done
+
+i31_GetStateSaveRestore endp
+
+; -------------------------------------------------------
+; i31_GetRawModeSwitch -- Return Int 31h Save/Restore State addresses.
+;
+; In: none
+; Out: bx:cx - real -> protected mode switch address
+; si:di - protected -> real mode switch address
+;
+
+ public i31_GetRawModeSwitch
+ assume ds:DGROUP,es:DGROUP
+
+i31_GetRawModeSwitch proc near
+
+ push es
+ push SEL_DXCODE OR STD_RING
+ pop es
+ assume es:DXCODE
+ mov ax,segDXCode
+ pop es
+ assume es:DGROUP
+ mov [bp].intUserBX,ax
+ mov [bp].intUserCX,offset DXCODE:RmRawModeSwitch
+ mov [bp].intUserSI,SEL_DXPMCODE OR STD_RING
+IFDEF WOW
+ test DpmiFlags,DPMI_32BIT
+ jz grms10
+
+ mov edi,dword ptr (offset DXPMCODE:PmRawModeSwitch)
+ENDIF
+grms10: mov [bp].intUserDI,offset DXPMCODE:PmRawModeSwitch
+
+ jmp i31_done
+
+i31_GetRawModeSwitch endp
+
+
+;
+; make sure 286 protect mode else code generated for mips will be wrong.
+;
+ .286p
+
+; -------------------------------------------------------
+; Service 04/f1 - Allocate Space in LDT for Selector (WOW only)
+; Don't initialize the descriptor to Zero.
+; in: cx - # selectors required
+; out: ax - *index* of first selector
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_WOW_AllocSel
+
+i31_WOW_AllocSel proc near
+
+ cmp cx,1 ;1 selector or more?
+ ja @f
+
+ call AllocateSelector ;allocate 1 selector
+ jc i31_WOW_15
+ jmp short i31_WOW_10
+
+@@: mov ax,cx
+ call AllocateSelectorBlock ;allocate a block of selectors
+ jc i31_WOW_15
+
+i31_WOW_10:
+ or al,STD_TBL_RING ;add standard table/ring bits
+ mov [bp].intUserAX,ax ;return 1st/only selector in AX
+
+ jmp i31_done
+
+i31_WOW_15:
+ jmp i31_fail ;fail the request
+
+i31_WOW_AllocSel endp
+
+
+; -------------------------------------------------------
+; Service 04/f2 - Set Descriptor (WOW only)
+; in: bx - selector
+; cx - number of contiguous selectors
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_WOW_SetDescriptor
+
+i31_WOW_SetDescriptor proc near
+
+ mov ax,selGDT
+ mov es,ax
+ assume es:NOTHING
+ lsl ax,ax
+
+ and bx,SELECTOR_INDEX ; input selector
+ cmp bx,ax ; range-test selector against
+ jc @F ; the limit of the GDT/LDT
+ jmp i31_fail_CY
+
+@@:
+ifndef WOW
+ ; notify dpmi32 so it can maintain FlatAddress array (MIPS case)
+ mov ax,bx
+ DPMIBOP SetDescriptorTableEntries
+else
+ cCall NWOWSetDescriptor,<cx,es,bx>
+endif ; WOW
+ jmp i31_done
+
+i31_WOW_SetDescriptor endp
+
+; -------------------------------------------------------
+; Service 04/f3 - Set Descriptor (WOW only)
+; in: bx:dx -- pointer to low memory allocation routine
+; si:di -- pointer to low memory free routine
+
+ assume ds:DGROUP,es:DGROUP
+ public i31_WOW_SetAllocFunctions
+
+i31_WOW_SetAllocFunctions proc near
+
+ mov word ptr [LowMemAllocFn],dx
+ mov word ptr [LowMemAllocFn + 2],bx
+ mov word ptr [LowMemFreeFn],di
+ mov word ptr [LowMemFreeFn + 2], si
+ jmp i31_done
+
+i31_WOW_SetAllocFunctions endp
+
+DXPMCODE ends
+
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxintr.asm b/private/mvdm/dpmi/dxintr.asm
new file mode 100644
index 000000000..10612818a
--- /dev/null
+++ b/private/mvdm/dpmi/dxintr.asm
@@ -0,0 +1,5217 @@
+ PAGE ,132
+ TITLE DXINTR.ASM -- Dos Extender Interrupt Reflector
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXINTR.ASM - Dos Extender Interrupt Reflector *
+;* *
+;****************************************************************
+;* *
+;* Revision History: *
+;* *
+;* *
+;* 09/13/90 earleh Fault handlers Ring 0 *
+;* 09/06/90 earleh Fault handlers DPMI compliant *
+;* PIC remapping no longer required *
+;* 08/08/90 earleh DOSX and client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 05/09/90 jimmat Started VCPI changes. *
+;* 04/02/90 jimmat Added PM Int 70h handler. *
+;* 01/08/90 jimmat Don't allow nested PS/2 mouse interrupts *
+;* (later removed!) *
+;* 09/15/89 jimmat Support for 'Classic' HP Vectras which *
+;* have 3 8259 interrupt controllers *
+;* 07/28/89 jimmat Save A20 state when reflecting an int to *
+;* protected mode, removed Int 30h handler *
+;* that did code patch-ups, point debugger *
+;* to faulting instruction, not Int 3. *
+;* 07/13/89 jimmat Improved termination due to faults when *
+;* not running under a debugger--also ifdef'd *
+;* out code to dynamically fixup code seg *
+;* references on GP faults *
+;* 06/05/89 jimmat Ints 0h-1Fh are now vectored through a 2nd *
+;* table. This allows Wdeb386 interaction *
+;* more like Windows/386. *
+;* 05/23/89 jimmat Added wParam & lParam to interrupt frame. *
+;* 05/07/89 jimmat Added XMScontrol function to map protected *
+;* mode XMS requests to real mode driver. *
+;* 05/02/89 jimmat 8259 interrupt mask saved around changing *
+;* of hardware interrupt base *
+;* 04/24/89 jimmat Added support for PS/2 Int 15h/C2h/07 Set *
+;* Pointing Device Handler Address function *
+;* 04/12/89 jimmat Added PMIntr24 routine to support PM *
+;* Critical Error Handlers *
+;* 03/15/89 jimmat Added INT 31h LDT/heap interface a la *
+;* Windows/386 *
+;* 03/14/89 jimmat Changes to run child in ring 1 with LDT *
+;* 02/24/89 (GeneA): fixed problem in IntEntryVideo and *
+;* IntExitVideo for processing function 10h subfunction *
+;* for reading and writing the VGA palette. *
+;* 02/22/89 (GeneA): added handlers for Int 10h, Int 15h, and *
+;* Int 33h. Added support for more general mechanism for *
+;* handling interrupts require special servicing and *
+;* allowing nesting of these interrupts. Allocation and *
+;* deallocation of stack frames is supported to allow *
+;* nested paths through the interrupt reflection code to *
+;* a depth of 8. *
+;* There is still a problem that if an interrupt handler *
+;* is using a static buffer to transfer data, another *
+;* interrupt that uses the same static buffer could come *
+;* in and trash it. Solving the problem in a completely *
+;* general way would require having a buffer allocation *
+;* deallocation scheme for doing the transfers between *
+;* real mode memory and protected mode memory. *
+;* 02/14/89 (GeneA): added code in TrapGP to print error msg *
+;* and quit when running a non-debugging version. *
+;* 02/10/89 (GeneA): changed Dos Extender from small model to *
+;* medium model. Added function LoaderTrap to handle *
+;* loader interrupts when the program contains overlays. *
+;* 11/20/88 (GeneA): changed both RM and PM interrupt reflector*
+;* routines to pass the flags returned by the ISR back to *
+;* the originator of the interrupt, rather than returning *
+;* the original flags. *
+;* 10/28/88 (GeneA): created *
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;* *
+;****************************************************************
+
+ .286p
+ .287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include interupt.inc
+if VCPI
+include dxvcpi.inc
+endif
+IFDEF ROM
+include dxrom.inc
+ENDIF
+ifdef wow
+include vdmtib.inc
+endif
+ .list
+include intmac.inc
+include stackchk.inc
+include bop.inc
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+DS_ForcedGO equ 0F003h ;Wdeb386 go with breakpoint command
+DebOut_Int equ 41h ;Wdeb386 pMode interface Interrupt
+
+; Offsets to fields in DOSX header for DOS allocated memory blocks
+
+MemCookie equ 0
+MemSelector equ 2
+MemSegment equ 4
+MemParas equ 6
+MemSelCount equ 8
+
+MemGoodCookie equ 'SF' ;memory header magic cookie value
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn PMIntr13:NEAR
+ extrn PmIntrDos:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn ParaToLinear:NEAR
+externFP NSetSegmentDscr
+ extrn GetSegmentAddress:NEAR
+ extrn DupSegmentDscr:NEAR
+ extrn ParaToLDTSelector:NEAR
+ extrn FreeSelector:NEAR
+ extrn FreeSelectorBlock:NEAR
+ extrn AllocateSelector:NEAR
+ extrn AllocateSelectorBlock:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn fDebug:BYTE
+ extrn selIDT:WORD
+ extrn pmusrss:WORD
+ extrn pmusrsp:WORD
+ifdef NOT_NTVDM_NOT
+ extrn fHPVectra:BYTE
+endif
+ extrn idCpuType:WORD
+ extrn npXfrBuf0:WORD
+ extrn npXfrBuf1:WORD
+ extrn rgbXfrBuf0:BYTE
+ extrn rgbXfrBuf1:BYTE
+ extrn selPSPChild:WORD
+ extrn fFaultAbort:BYTE
+ extrn lpfnXMSFunc:DWORD
+ extrn Int28Filter:WORD
+ extrn A20EnableCount:WORD
+
+if DEBUG
+ extrn fTraceReflect:WORD
+endif
+
+if VCPI
+ extrn fVCPI:BYTE
+endif
+
+IFDEF ROM
+ extrn segDXData:WORD
+ extrn segDXCode:WORD
+ENDIF
+IFDEF WOW
+ extrn DpmiFlags:WORD
+ENDIF
+
+;
+; Variables used to store register values while mode switching.
+
+ public regUserSS, regUserSP, regUserFL, regUserAX, regUserDS
+ public regUserES
+
+regUserSS dw ?
+regUserSP dw ?
+regUserCS dw ?
+regUserIP dw ?
+regUserFL dw ?
+regUserAX dw ?
+regUserDS dw ?
+regUserES dw ?
+pfnReturnAddr dw ?
+
+Int28Count dw -1 ;Count of idle Int 28h's not reflected to RM
+
+;
+; Far pointer to the user's mouse callback function.
+
+ public lpfnUserMouseHandler
+
+lpfnUserMouseHandler dd 0 ;Entry point to the users mouse handler
+cbMouseState dw 0 ;size of mouse state buffer in bytes
+
+
+; Far pointer to PS/2 Pointing device handler address
+
+ public lpfnUserPointingHandler
+
+lpfnUserPointingHandler dd 0 ;Sel:Off to user's handler
+
+ public PMInt24Handler
+
+PMInt24Handler dd 0 ;Address of protect mode Int 24h handler
+ifdef WOW
+ dd 0 ; other half
+endif
+
+ align 2
+
+if DEBUG
+ extrn StackGuard:WORD
+endif
+ extrn pbReflStack:WORD
+ extrn bReflStack:WORD
+;
+; This buffer contains the original real mode interrupt vectors. The
+; PM->RM interrupt reflector uses the addresses in this vector as the
+; address to receive control when it reflects an interrupt to real mode.
+
+ public rglpfnRmISR
+
+ align 2
+rglpfnRmISR dd 256 dup (?)
+
+;
+; This buffer contains the real mode hardware interrupt vectors.
+; If a hardware interrupt is hooked in protected mode, a reflector
+; is put into the IVT. If we were trying to reflect down the real
+; mode chain, we call these handlers if the IVT contains a reflector
+;
+ public RmHwIsr
+RmHwIsr dd 256 dup(0)
+
+;
+; This flag indicates if the hardware interrupts have been remapped.
+ public fHardwareIntMoved
+fHardwareIntMoved db 0
+
+ifdef SEG_FIXUP ;-----------------------------------------------
+
+errGP dw ? ;this variable is used to hold the error
+
+endif ;SEG_FIXUP ------------------------------------------------
+
+
+; PMFaultVector is a table of selector:offsets for routines to process
+; protected mode processor faults/traps/exceptions. If we don't handle
+; the exception as an exception, we vector it through PMReservedEntryVector.
+
+IFNDEF WOW
+FltRtn macro off
+ dw DXPMCODE:off
+ dw SEL_DXPMCODE or STD_RING
+ endm
+ELSE
+FltRtn macro off
+ dw DXPMCODE:off
+ dw 0
+ dw SEL_DXPMCODE or STD_RING
+ dw 0
+ endm
+ENDIF
+ public PMFaultVector
+
+ align 4
+
+PMFaultVector label DWORD
+IFNDEF WOW
+ FltRtn PMReservedEntryVector+3*0h ; int 0
+ FltRtn PMReservedEntryVector+3*1h ; int 1
+ FltRtn PMReservedEntryVector+3*2h ; int 2
+ FltRtn PMReservedEntryVector+3*3h ; int 3
+ FltRtn PMReservedEntryVector+3*4h ; int 4
+ FltRtn PMReservedEntryVector+3*5h ; int 5
+ FltRtn TrapInvalidOpcode ; int 6
+ FltRtn PMReservedEntryVector+3*7h ; int 7
+ FltRtn TrapDoubleFault ; int 8
+ FltRtn TrapExtensionOverrun ; int 9
+ FltRtn TrapInvalidTSS ; int A
+ FltRtn TrapSegmentNotPresent ; int b
+ FltRtn TrapStackOverrun ; int c
+ FltRtn TrapGP ; int d
+ FltRtn TrapPageFault ; int e
+ FltRtn PMReservedEntryVector+3*0Fh ; int f
+ FltRtn PMReservedEntryVector+3*10h ; int 10h
+ FltRtn PMReservedEntryVector+3*11h ; int 11h
+ FltRtn PMReservedEntryVector+3*12h ; int 12h
+ FltRtn PMReservedEntryVector+3*13h ; int 13h
+ FltRtn PMReservedEntryVector+3*14h ; int 14h
+ FltRtn PMReservedEntryVector+3*15h ; int 15h
+ FltRtn PMReservedEntryVector+3*16h ; int 16h
+ FltRtn PMReservedEntryVector+3*17h ; int 17h
+ FltRtn PMReservedEntryVector+3*18h ; int 18h
+ FltRtn PMReservedEntryVector+3*19h ; int 19h
+ FltRtn PMReservedEntryVector+3*1Ah ; int 1ah
+ FltRtn PMReservedEntryVector+3*1Bh ; int 1bh
+ FltRtn PMReservedEntryVector+3*1Ch ; int 1ch
+ FltRtn PMReservedEntryVector+3*1Dh ; int 1Dh
+ FltRtn PMReservedEntryVector+3*1Eh ; int 1Eh
+ FltRtn PMReservedEntryVector+3*1Fh ; int 1Fh
+ELSE
+ FltRtn PMReservedEntryVector+5*0h ; int 0
+ FltRtn PMReservedEntryVector+5*1h ; int 1
+ FltRtn PMReservedEntryVector+5*2h ; int 2
+ FltRtn PMReservedEntryVector+5*3h ; int 3
+ FltRtn PMReservedEntryVector+5*4h ; int 4
+ FltRtn PMReservedEntryVector+5*5h ; int 5
+ FltRtn TrapInvalidOpcode ; int 6
+ FltRtn PMReservedEntryVector+5*7h ; int 7
+ FltRtn TrapDoubleFault ; int 8
+ FltRtn TrapExtensionOverrun ; int 9
+ FltRtn TrapInvalidTSS ; int A
+ FltRtn TrapSegmentNotPresent ; int b
+ FltRtn TrapStackOverrun ; int c
+ FltRtn TrapGP ; int d
+ FltRtn TrapPageFault ; int e
+ FltRtn PMReservedEntryVector+5*0Fh ; int f
+ FltRtn PMReservedEntryVector+5*10h ; int 10h
+ FltRtn PMReservedEntryVector+5*11h ; int 11h
+ FltRtn PMReservedEntryVector+5*12h ; int 12h
+ FltRtn PMReservedEntryVector+5*13h ; int 13h
+ FltRtn PMReservedEntryVector+5*14h ; int 14h
+ FltRtn PMReservedEntryVector+5*15h ; int 15h
+ FltRtn PMReservedEntryVector+5*16h ; int 16h
+ FltRtn PMReservedEntryVector+5*17h ; int 17h
+ FltRtn PMReservedEntryVector+5*18h ; int 18h
+ FltRtn PMReservedEntryVector+5*19h ; int 19h
+ FltRtn PMReservedEntryVector+5*1Ah ; int 1ah
+ FltRtn PMReservedEntryVector+5*1Bh ; int 1bh
+ FltRtn PMReservedEntryVector+5*1Ch ; int 1ch
+ FltRtn PMReservedEntryVector+5*1Dh ; int 1Dh
+ FltRtn PMReservedEntryVector+5*1Eh ; int 1Eh
+ FltRtn PMReservedEntryVector+5*1Fh ; int 1Fh
+ENDIF
+; PMIntelVector is a table of selector:offsets for routines to process
+; protected mode interrupts in the range 0-1fh. These interrupt numbers
+; are in the range which is "reserved by Intel" for processor exceptions,
+; so the exception handler gets first crack at them.
+
+ public PMIntelVector
+PMIntelVector Label DWORD
+
+ FltRtn PMIntrEntryVector+3*0h ; Int 0
+ FltRtn PMIntrIgnore ; int 1
+ FltRtn PMIntrEntryVector+3*2h ; Int 2
+ FltRtn PMIntrIgnore ; int 3
+ FltRtn PMIntrEntryVector+3*4h ; Int 4
+ FltRtn PMIntrEntryVector+3*5h ; Int 5
+ FltRtn PMIntrEntryVector+3*6h ; Int 6
+ FltRtn PMIntrEntryVector+3*7h ; Int 7
+ FltRtn WowHwIntrEntryVector+8*0h ; Int 8
+ FltRtn WowHwIntrEntryVector+8*1h ; Int 9
+ FltRtn WowHwIntrEntryVector+8*2h ; Int 0ah
+ FltRtn WowHwIntrEntryVector+8*3h ; Int 0bh
+ FltRtn WowHwIntrEntryVector+8*4h ; Int 0ch
+ FltRtn WowHwIntrEntryVector+8*5h ; Int 0dh
+ FltRtn WowHwIntrEntryVector+8*6h ; Int 0eh
+ FltRtn WowHwIntrEntryVector+8*7h ; Int 0fh
+ FltRtn PMIntrVideo ; int 10h
+ FltRtn PMIntrEntryVector+3*11h ; Int 11h
+ FltRtn PMIntrEntryVector+3*12h ; Int 12h
+ FltRtn PMIntr13 ; int 13h
+ FltRtn PMIntrEntryVector+3*14h ; Int 14h
+ FltRtn PMIntrMisc ; int 15h
+ FltRtn PMIntrEntryVector+3*16h ; Int 16h
+ FltRtn PMIntrEntryVector+3*17h ; Int 17h
+ FltRtn PMIntrEntryVector+3*18h ; Int 18h
+ FltRtn PMIntr19 ; Int 19h
+ FltRtn PMIntrEntryVector+3*1ah ; Int 1ah
+ FltRtn PMIntrEntryVector+3*1bh ; Int 1bh
+ FltRtn PMIntrEntryVector+3*1ch ; Int 1ch
+ FltRtn PMIntrEntryVector+3*1dh ; Int 1dh
+ FltRtn PMIntrEntryVector+3*1eh ; Int 1eh
+ FltRtn PMIntrEntryVector+3*1fh ; Int 1fh
+
+ifdef OVERLAY_SUPPORT ;-----------------------------------------------
+
+offDestination dw ?
+selDestination dw ?
+
+endif ;---------------------------------------------------------------
+
+
+; if DEBUG ;------------------------------------------------------------
+; For MIPS we need to see where we are faulting - remove for final release
+; LATER
+
+; extrn fA20:BYTE
+; extrn fTraceFault:WORD
+
+ public PMIntNo
+PMIntNo dw 0
+
+szRegDump db 'AX=#### BX=#### CX=#### DX=#### SI=#### DI=#### BP=####',0dh,0ah
+ db 'DS=#### ES=#### EC=#### CS=#### IP=#### SS=#### SP=####',0dh,0ah
+ db '$'
+
+; endif ;DEBUG --------------------------------------------------------
+
+ extrn rgwStack:word
+ extrn npEHStackLimit:word
+ extrn npEHStacklet:word
+ extrn selEHStack:word
+IFDEF WOW
+ public WowTransitionToUserMode, WowCopyEhStack,WowCopyIretStack
+ public WowReservedReflector,Wow16BitHandlers
+WowTransitionToUserMode dw offset DXPMCODE:Wow16TransitionToUserMode
+WowCopyEhStack dw offset DXPMCODE:Wow16CopyEhStack
+WowCopyIretStack dw offset DXPMCODE:Wow16CopyIretStack
+Wow16BitHandlers dw 256 dup (0,0)
+ENDIF
+
+ public HwIntHandlers
+HwIntHandlers dd 16 dup (0,0)
+
+DXDATA ends
+
+
+DXSTACK segment
+
+ public rgw0Stack, rgw2FStack
+
+ dw 64 dup (?) ; INT 2Fh handler stack
+
+rgw2FStack label word
+
+ dw 64 dup (?) ; DOSX Ring -> Ring 0 transition stack
+;
+; Interrupts in the range 0-1fh cause a ring transition and leave
+; an outer ring IRET frame right here.
+;
+Ring0_EH_DS dw ? ; place to put user DS
+Ring0_EH_AX dw ? ; place to put user AX
+Ring0_EH_BX dw ? ; place to put user BX
+Ring0_EH_CX dw ? ; place to put user CX
+Ring0_EH_BP dw ? ; place to put user BP
+Ring0_EH_PEC dw ? ; lsw of error code for 386 page fault
+ ; also near return to PMFaultEntryVector
+Ring0_EH_EC dw ? ; error code passed to EH
+Ring0_EH_IP dw ? ; interrupted code IP
+ifdef WOW
+Ring0_EH_EIP dw ? ; high half eip
+endif
+Ring0_EH_CS dw ? ; interrupted code CS
+ifdef WOW
+ dw ? ; high half of cs
+endif
+Ring0_EH_Flags dw ? ; interrupted code flags
+ifdef WOW
+Ring0_EH_EFlags dw ? ; high half of flags
+endif
+Ring0_EH_SP dw ? ; interrupted code SP
+ifdef WOW
+Rin0_EH_ESP dw ? ; high half of esp
+endif
+Ring0_EH_SS dw ? ; interrupted code SS
+ifdef WOW
+ dw ? ; high half of ss
+endif
+rgw0Stack label word
+
+ifdef WOW
+ dw 64 dup (?) ; wow stack for initial int field
+ public rgwWowStack
+rgwWowStack label word
+endif
+
+DXSTACK ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn selDgroup:WORD
+ENDIF
+
+DXCODE ends
+
+DXPMCODE segment
+
+ public WowHwIntDispatchProc
+WowHwIntDispatchProc dw offset DXPMCODE:Wow16HwIntrReflector
+
+ extrn selDgroupPM:WORD
+ extrn segDXCodePM:WORD
+ extrn szFaultMessage:BYTE
+ extrn szRing0FaultMessage:BYTE
+ extrn RZCall:NEAR
+
+IFNDEF ROM
+ extrn segDXDataPM:WORD
+ENDIF
+
+;
+; The following is a code segment variable, because we need to call
+; through it at a time when none of the other segment register contents
+; can be determined.
+;
+IFDEF WOW
+WowReservedReflector dw offset DXPMCODE:PMReservedReflector
+ENDIF
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ page
+ subttl Protected Mode Interrupt Reflector
+; -------------------------------------------------------
+; PROTECTED MODE INTERRUPT REFLECTOR
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+; -------------------------------------------------------
+; PMIntrEntryVector -- This table contains a vector of
+; near jump instructions to the protected mode interrupt
+; reflector. The protected mode interrupt descriptor
+; table is initialized so that all interrupts jump to
+; locations in this table, which transfers control to
+; the interrupt reflection code for reflecting the
+; interrupt to real mode.
+
+ public PMIntrEntryVector
+
+PMIntrEntryVector:
+
+ rept 256
+ call PMIntrReflector
+ endm
+
+
+ public PMFaultEntryVector
+
+; -------------------------------------------------------
+; PMFaultEntryVector -- This table contains a vector of
+; near jump instructions to the protected mode fault
+; analyzer.
+;
+PMFaultEntryVector:
+
+ rept 32
+ call PMFaultAnalyzer
+ endm
+
+ assume ds:nothing,es:nothing,ss:nothing
+ public PMReservedEntryVector
+PMReservedEntryVector:
+ rept 32
+ifdef WOW
+ call [WowReservedReflector]
+ELSE
+ call PMReservedReflector
+ENDIF
+ endm
+
+; ------------------------------------------------------------------
+; PMFaultAnalyzer -- This routine is the entry point for
+; the protected mode fault/trap/exception handler. It tries
+; to distinguish between bona fide processor faults and
+; hardware/software interrupts which use the range of
+; interrupts that is reserved by Intel. If a fault is
+; detected, then format the stack for a DPMI fault handler,
+; then vector to the handler whose address is stored in
+; PMFaultVector. If it looks more like an interrupt, then
+; set up the stack for an interrupt handler, jump to the
+; handler whose address is stored in PMIntelVector.
+;
+; Input: none
+; Output: none
+
+ assume ds:NOTHING,es:NOTHING,ss:DGROUP
+ public PMFaultAnalyzer
+
+PMFaultAnalyzer proc near
+
+;
+; Make sure we are on the right stack. Else, something fishy is going on.
+; Note that stack faults won't do too well here.
+;
+ push ax
+ mov ax,ss
+ifndef WOW
+ cmp ax,SEL_DXDATA0
+else
+ cmp ax,SEL_DXDATA or STD_RING
+endif
+ pop ax
+ je pmfa_stack_passed
+ jmp pmfa_bad_stack
+pmfa_stack_passed:
+;
+; Is the stack pointer pointing at a word error code (minus 2)?
+;
+ifndef WOW
+ cmp sp,offset DGROUP:Ring0_EH_PEC
+else
+ cmp sp,offset DGROUP:Ring0_EH_BP ; wow will always have 32bit
+ ; error code, and NO page fault
+endif
+ je pmfa_fault ; Yes, processor fault.
+
+;
+; Is the stack pointer pointing at a DWORD error code?
+;
+ jc pmfa_pfault ; Yes, page fault.
+ ; No, fault without EC or interrupt.
+;
+; Is it pointing to where it is supposed to be for a hardware or
+; software interrupt?
+;
+ cmp sp,offset DGROUP:Ring0_EH_EC
+ je pmfa_20
+ jmp pmfa_bad_stack
+pmfa_20:jmp pmfa_inspect
+pmfa_pfault:
+;
+; Is the stack pointer pointing to the right place for a page fault?
+;
+ cmp sp,offset DGROUP:Ring0_EH_BP
+ je pmfa_pfaultOK ; Yes, it's a page fault
+ jmp pmfa_bad_stack
+pmfa_pfaultOK:
+;
+; Error code is a dword, but only a few bits in the lsw are significant
+; on the 80386 and 80486.
+; Put the lsw in the place where the msw was, then fix up the stack
+; pointer to agree with the word EC case, by popping the return into
+; the lsw!
+;
+ push Ring0_EH_PEC
+ pop Ring0_EH_EC
+ pop Ring0_EH_PEC
+pmfa_fault:
+;
+; Getting here, we have a known exception with a word error code of some
+; sort on the stack. Perform an outward ring transition, switch to the
+; client stack, then vector through the exception handler vector to the
+; appropriate handler.
+;
+ push bp
+ push cx
+ push bx
+ push ax
+ push ds
+ lea bp,Ring0_EH_SS
+ mov ax,word ptr [bp]
+ mov cx,selEHStack
+ mov ds,cx
+ ;Get the Next available EH stacklet
+ mov bx, npEHStacklet
+
+
+if DBG
+ cmp bx, CB_STKFRAME ; do we have any stacklets left ?
+ jb pmfa_stkerr
+ cmp word ptr [bx],0DEADh ; Did app overflow last stacklet ?
+ jne pmfa_stkerr
+endif
+ ; mark next stacklet
+ sub bx, CB_STKFRAME
+ mov word ptr [bx],0DEADh
+ mov npEHStacklet, bx
+ add bx, CB_STKFRAME
+
+if DBG
+ jmp pmfa_copy_stack
+pmfa_stkerr:
+ BOP BOP_DBGBREAKPOINT
+ jmp pmfa_copy_stack
+ db 'dosx:pmfa_stkerr' ; string to search for
+endif
+
+pmfa_copy_stack: ; DS:BX = user SS:SP
+
+
+ifndef WOW
+ mov cx,7
+pmfa_copy_stack_loop:
+ dec bx
+ dec bx
+ mov ax,word ptr [bp]
+ mov word ptr [bx],ax
+ dec bp
+ dec bp
+ loop pmfa_copy_stack_loop
+
+; DS:BX points to stack on entry to PMFaultReflector
+
+ lea bp,Ring0_EH_PEC
+ mov word ptr [bp+6],ds
+ mov word ptr [bp+4],bx
+ mov word ptr [bp+2],SEL_DXPMCODE or STD_RING
+ mov word ptr [bp],offset DXPMCODE:PMFaultReflector
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+ pop bp
+ retf
+
+else
+;
+; Build the necessary frames on the user and dosx stack to
+; "switch" to usermode, and dispatch an exception.
+;
+
+ call [WowCopyEhStack]
+
+; N.B. There are now some extra words on our stack. If you
+; pushed something before calling [WowCopyEhStack], you
+; won't find it by poping.
+
+;
+; switch to user stack, and return
+;
+ jmp [WowTransitionToUserMode]
+endif
+
+
+pmfa_inspect:
+;
+; Stack is set up as for an interrupt or exception without error code.
+; Adjust the stack pointer and put an error code of zero. Then try to
+; determine whether we have an exception or an interrupt. First test
+; is the interrupt number.
+;
+ push Ring0_EH_EC
+ mov Ring0_EH_EC,0
+ cmp Ring0_EH_PEC,offset PMFaultEntryVector + ((7 + 1) * 3)
+ ja pfma_50
+IFDEF WOW
+;
+; build dword error code for fault dispatch
+;
+ push Ring0_EH_PEC
+ mov Ring0_EH_PEC,0
+ENDIF
+ jmp pmfa_fault ; Yes, definitely a fault.
+pfma_50:
+;
+; At this point, all valid exceptions have been eliminated except for
+; exception 9, coprocessor segment overrun, and exception 16, coprocessor
+; error.
+;
+
+; **********************************************
+; * Your code to detect these exceptions here. *
+; **********************************************
+
+ push bp
+ push cx
+ push bx
+ push ax
+ push ds ; SP -> Ring0_EH_DS
+;
+; Point to the user's stack.
+;
+ifndef WOW
+ lea bp,Ring0_EH_SS
+ mov cx,[bp]
+ mov ds,cx
+ mov bx,[bp-2] ; DS:[BX] -> user stack
+;
+; Copy the IRET frame to the user stack.
+;
+ lea bp,Ring0_EH_Flags
+ mov cx,3d
+pmfa_copy_IRET:
+ mov ax,[bp]
+ dec bx
+ dec bx
+ mov [bx],ax
+ dec bp
+ dec bp
+ loop pmfa_copy_IRET
+;
+; Point BP at vector entry for this (reserved) interrupt.
+;
+ mov ax,Ring0_EH_PEC ; fetch near return address
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov cl,3
+ div cl ; AX = interrupt number
+ shl ax,2 ; AX = vector entry offset
+ lea bp,PMIntelVector
+ add bp,ax ; BP -> interrupt handler address
+ mov ax,[bp] ; AX = IP of handler
+ mov cx,[bp+2] ; CX = CS of handler
+;
+; Build the far ret frame for the outward ring transition.
+;
+ lea bp,Ring0_EH_PEC
+ mov [bp],ax
+ mov [bp+2],cx
+ mov [bp+4],bx
+ mov ax,ds
+ mov [bp+6],ax
+
+ pop ds ; Pop 'em.
+ pop ax
+ pop bx
+ pop cx
+ pop bp
+ retf ; Out of here.
+else
+ call [WowCopyIretStack]
+;
+; Build a far return frame on user stack, switch to user stack, and return
+;
+ jmp [WowTransitionToUserMode]
+endif
+
+pmfa_bad_stack:
+
+if DEBUG
+ mov ax,ss
+ mov bx,sp
+ Trace_Out "Fault Handler Aborting with SS:SP = #AX:#BX"
+ pop ax
+ sub ax, (offset DXPMCODE:PMFaultEntryVector) + 3
+ mov bx,3
+ div bl
+ Trace_Out "Fault Number #AX"
+ pop ax
+ pop bx
+ pop cx
+ pop dx
+ Trace_Out "First four stack words: #AX #BX #CX #DX."
+endif
+ push selDgroupPM
+ push offset DGROUP:rgwStack
+ rpushf
+ push SEL_DXPMCODE or STD_RING
+ push offset DXPMCODE:pmfr_death
+ riret
+pmfr_death:
+ mov ax,cs
+ mov ds,ax
+ mov dx,offset szRing0FaultMessage
+ pmdossvc 09h
+ jmp PMAbort
+
+PMFaultAnalyzer endp
+
+;
+; This is the format of the stack frame used by the Protected Mode
+; fault reflector and by the reserved interrupt reflector. The
+; three fields in the middle have different meanings, depending on
+; whether we are handling a fault or passing it off to an interrupt
+; handler.
+;
+
+FR_Stack Struc
+ FR_BP dw ?
+ FR_AX dw ?
+ FR_BX dw ?
+ FR_DS dw ?
+ FR_ENTRY dw ? ; SS:[SP] points here on entry
+ ; to PMReservedReflector
+ FR_Toss dw ? ; DPMI return IP
+ FR_Ret_IP dw ? ; actual fault handler gets put
+ FR_Ret_CS dw ? ; here to return to
+ FR_IP dw ?
+ FR_CS dw ?
+ FR_FL dw ?
+ FR_SP dw ?
+ FR_SS dw ?
+FR_Stack Ends
+;
+; Alternate names so the structure above makes more sense to
+; PMFaultReflector.
+;
+FR_Handler_IP equ FR_DS
+FR_Handler_CS equ FR_ENTRY
+FR_Handler_Ret_IP equ FR_Toss
+FR_Handler_Ret_CS equ FR_Ret_IP
+FR_Handler_Entry equ FR_Handler_Ret_CS
+FR_EC equ FR_Ret_CS
+
+;
+; Stack as seen by a fault handler on entry.
+;
+
+EH_Stack Struc
+ EH_Ret_IP dw ? ; DPMI fault handler dispatcher
+ EH_Ret_CS dw ? ; return address
+ EH_EC dw ? ; fault error code
+ EH_IP dw ?
+ EH_CS dw ?
+ EH_FL dw ?
+ EH_SP dw ?
+ EH_SS dw ?
+EH_Stack Ends
+
+; ------------------------------------------------------------------
+; PMFaultReflector -- Dispatch a fault to a fault handler installed
+; in PMFaultVector. When the fault handler returns, return
+; to the faulting code, using the addresses placed on the
+; DPMI fault handler stack by the last called fault handler.
+;
+; Input:
+; Entry is by a NEAR call, with an IP within the range
+; of PMFaultEntryVector on the stack. The stack has been
+; set up for use by a DPMI fault handler.
+;
+; Output:
+; Controlled by fault handler.
+;
+; Uses:
+; Controlled by fault handler.
+;
+; Notes:
+; Fault handlers are called on a static stack. This routine
+; is NOT REENTRANT.
+;
+ public PMFaultReflector
+ public PMFaultReflectorIRET
+PMFaultReflector proc near
+ assume ss:nothing,ds:nothing,es:nothing
+
+ sub sp,6
+ push bx
+ push ax
+ push bp
+ mov bp,sp
+ push ds
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds,ax
+ assume ds:dgroup
+ mov ax,[bp.FR_Handler_Entry]
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov bl,3
+ div bl ; AX = interrupt number
+IFNDEF WOW
+ shl ax,2 ; AX = offset of fault handler
+ELSE
+ shl ax,3 ; AX = offset of fault handler
+ENDIF
+ lea bx,PMFaultVector
+ add bx,ax ; SS:[BX] -> fault vector entry
+ mov ax,word ptr ds:[bx]
+ mov [bp.FR_Handler_IP],ax
+IFNDEF WOW
+ mov ax,word ptr ds:[bx+2]
+ELSE
+ mov ax,word ptr ds:[bx+4]
+ENDIF
+ mov [bp.FR_Handler_CS],ax
+
+ lea ax,pmfr_cleanup
+ mov [bp.FR_Handler_Ret_IP],ax
+ push cs
+ pop [bp.FR_Handler_Ret_CS]
+
+ pop ds
+ assume ds:nothing
+ pop bp
+ pop ax
+ pop bx
+ retf ; This calls the fault handler.
+
+PMFaultReflectorIRETCall:
+ dd (SEL_RZIRET or STD_RING) shl 10h
+
+pmfr_cleanup:
+;
+; Unwind the fault handler stack. Return to the faulting code.
+; This works by calling a Ring 0 procedure to do the actual IRET.
+; If we do it that way, we can return to the faulting code without
+; actually touching the faulting code's stack.
+;
+ifndef WOW
+ add sp,2 ; pop off error code
+ cli ; can't take interrupts at ring 0
+ call dword ptr [PMFaultReflectorIRETCall]
+PMFaultReflectorIRET:
+ add sp,4 ; pop off call frame
+
+ push ax ; Free EH stacklet
+ push ds
+ mov ax,SEL_DXDATA0
+ mov ds, ax
+ add ds:npEHStacklet,CB_STKFRAME
+ pop ds
+ pop ax
+
+ iret
+else
+PMFaultReflectorIRET:
+;;; if DBG
+ BOP BOP_DBGBREAKPOINT ; x86 should never get here
+ jmp PMFaultReflectorIret
+ db 'PMFaultReflectorIret-X86!'
+;;; endif
+endif
+
+
+PMFaultReflector endp
+;
+; -------------------------------------------------------
+; PMReservedReflector -- This routine is for reflecting
+; exceptions to a protected mode interrupt handler.
+; The default for exceptions 1, 2, and 3 is to have
+; a near call to this routine placed in the PMFaultVector.
+;
+; This routine strips off the fault handler stack set
+; up by PMFaultAnalyzer, switches to the stack pointed
+; to by the pushed SS and SP values, sets up an IRET
+; frame for use by PMIntrReflector, and jumps to
+; PMIntrReflector. Eventual return is via an IRET
+; from PMIntrReflector.
+;
+; Input:
+; Entry is by a NEAR call, with an IP within the range
+; of PMReservedEntryVector on the stack. The stack has been
+; set up for use by a DPMI fault handler.
+;
+; Output:
+; Switch to stack registers set up by any previous fault
+; handler, jump to PMIntrReflector with an IRET frame set up
+; for direct return to the interrupted code.
+;
+; Errors: none
+;
+; Uses: Modifies SS, SP. Does not return to caller.
+;
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMReservedReflector
+PMReservedReflector:
+
+ push ds
+ push bx
+ push ax
+ push bp
+ mov bp,sp
+;
+; BP now points to a stack frame described by the structure
+; above. This will be copied to a stack frame on the stack pointed to
+; by FR_SS:FR_SS. In most cases, the destination stack is actually
+; the same as the present stack, offset by four bytes. The following
+; block of code is therefore very likely an overlapping copy. Think
+; carefully before modifying how it works.
+;
+
+ mov bx,[bp.FR_SS]
+ mov ds,bx
+ mov bx,[bp.FR_SP] ; DS:[BX] -> interrupted code's stack
+ sub bx, (size FR_Stack) - 4 ; (not copying SP or SS)
+ ; DS:[BX] -> place to copy our stack frame
+
+ mov ax,[bp.FR_FL] ; Push user IRET frame onto the destination
+ mov [bx.FR_FL],ax ; stack.
+ mov ax,[bp.FR_CS]
+ mov [bx.FR_CS],ax
+ mov ax,[bp.FR_IP]
+ mov [bx.FR_IP],ax
+
+ mov ax,[bp.FR_ENTRY] ; Copy our caller's near return.
+ mov [bx.FR_ENTRY],ax
+
+ mov ax,[bp.FR_DS] ; Copy saved registers.
+ mov [bx.FR_DS],ax
+ mov ax,[bp.FR_BX]
+ mov [bx.FR_BX],ax
+ mov ax,[bp.FR_AX]
+ mov [bx.FR_AX],ax
+ mov ax,[bp.FR_BP]
+ mov [bx.FR_BP],ax
+
+ mov ax,ds ; Switch to user stack.
+ mov ss,ax
+ mov sp,bx
+ mov bp,sp
+
+ ;
+ ; Deallocate exception stack frame
+ ;
+ push ds
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ assume ds:DGROUP
+ add npEHStacklet,CB_STKFRAME
+ pop ds
+ assume ds:nothing
+ mov ax,[bp.FR_ENTRY] ; AX = offset of caller
+IFNDEF WOW
+ sub ax,offset DXPMCODE:PMReservedEntryVector + 3
+ mov bl,3
+ div bl ; AX = interrupt number
+ shl ax,2 ; AX = offset into PMIntelVector
+ELSE
+ sub ax,offset DXPMCODE:PMReservedEntryVector + 5
+ mov bl,5
+ div bl ; AX = interrupt number
+ shl ax,3
+ENDIF
+ mov ds,SelDgroupPM
+ assume ds:DGROUP
+IFNDEF WOW
+ lea bx,PMIntelVector
+ add bx,ax ; DS:[BX] -> interrupt handler
+
+ mov ax,[bx] ; Place vector entry just below
+ mov [bp.FR_Ret_IP],ax ; IRET frame.
+ mov ax,[bx+2]
+ mov [bp.FR_Ret_CS],ax
+ELSE
+ push es
+ push SEL_IDT OR STD_RING
+ pop es
+
+ mov bx,ax
+ mov ax,es:[bx].offDest
+ mov [bp.FR_Ret_IP],ax ; IRET frame.
+ mov ax,es:[bx].selDest
+ mov [bp.FR_Ret_CS],ax
+ pop es
+ENDIF
+ lea sp,[bp.FR_BP] ; Point to saved registers.
+ pop bp ; Pop 'em.
+ pop ax
+ pop bx
+ pop ds
+ add sp,4 ; Fix up stack.
+
+ retf ; jump to interrupt handler via far return
+
+;
+; -------------------------------------------------------
+; PMIntrReflector -- This routine is the entry point for
+; the protected mode interrupt reflector. This routine
+; is entered when an interrupt occurs (either software
+; or hardware). It switches the processor to real mode
+; and transfers control to the appropriate interrupt
+; service routine for the interrupt. After the interrupt
+; service routine completes, it switches back to protected
+; mode and returns control to the originally interrupted
+; protected mode code.
+; Entry to this routine comes from the PMIntrEntryVector,
+; which contains a vector of near call instructions, which
+; all call here. The interrupt number is determined from
+; the return address of the near call from the interrupt
+; entry vector.
+; The address of the real mode interrupt service routine to
+; execute is determined from the real mode interrupt vector
+; table and the interrupt number.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: The segment registers are explicitly preserved by
+; this routine. Other registers are as preserved or
+; modified by the interrutp service routine.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrReflector
+
+PMIntrReflector:
+;
+; On entry, the stack layout is:
+; [6] FLAGS - "
+; [4] CS - "
+; [2] IP - from original interrupt
+; [0] IP - from interrupt entry vector call
+;
+
+ FCLI
+ cld
+ push ds
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ mov regUserAX,ax ;save user AX for later
+
+if DEBUG
+;
+; Are we on a DOSX interrupt reflector stack?
+;
+ mov ax,ss
+ cmp ax,selDgroupPM
+ jne @F
+ cmp sp,offset bReflStack
+ jb @F
+ cmp sp,offset pbReflStack
+ jnb @F
+;
+; If so, have we overflowed a stacklet?
+;
+ mov ax,pbReflStack
+ cmp sp,ax
+ ja @F
+ add ax,CB_STKFRAME
+ cmp sp,ax
+ jb @F
+ mov ax,regUserAX
+ Debug_Out "DOSX:PMIntrReflector--Reflector stack overflow."
+@@:
+endif; DEBUG
+
+ push bp ;stack -> BP DS IP IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [A]
+ mov ax,[bp+0Ah] ;get the interrupted routine's flags
+ and ax,NOT 4100h ;clear the trace flag in case we got
+ ; an interrupt on an instruction about
+ ; to be single stepped
+ mov regUserFL,ax ;and save for later
+ mov ax,es
+ xchg ax,[bp+4] ;save ES and get entry vector address
+ pop bp
+ ;stack -> DS ES IP CS FL
+ ; [0] [2] [4] [6] [8]
+
+; The state that we want to save on the user's stack has been set up.
+; Convert the entry vector return address into an interrupt number.
+
+ sub ax,offset PMIntrEntryVector+3
+ push cx
+ mov cl,3
+ div cl
+ pop cx
+
+;if DEBUG ; debugbug
+ mov PMIntNo,ax
+;endif
+ DEBUG_TRACE DBGTR_ENTRY, ax, 0, 1000h
+
+ shl ax,2 ;turn interrupt number into interrupt
+ ; table offset
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame.
+ mov regUserSP,sp ;save entry stack pointer so we can
+ mov regUSerSS,ss ; switch to our own stack
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroupPM ;switch to the reflector stack frame
+ mov sp,pbReflStack
+ FIX_STACK
+ push pbReflStack ;save stack frame ptr on stack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+
+
+if DEBUG ;--------------------------------------------------------
+
+ push 0ABCDh ;a debugging marker & interrupt value
+ push PMIntNo
+
+ cmp fTraceReflect,0
+ jz @f
+ push ax
+ mov ax,PMIntNo
+ Trace_Out "[pr#AL]",x
+ pop ax
+@@:
+
+; Perform a too-late-to-save-us-now-but-we-want-to-know check on the
+; reflector stack.
+
+ cmp StackGuard,1022h
+ jz @f
+ Debug_Out "DOSX:PMIntrReflector--Global reflector stack overflow."
+@@:
+endif ;DEBUG ---------------------------------------------------------
+
+; We are now running on our own stack, so we can switch into real mode.
+
+ push ax ;save interrupt vector table offset
+ SwitchToRealMode
+ assume es:nothing
+
+ xor ax,ax
+ mov es,ax
+ pop ax
+
+; Build an IRET frame on the stack so that the real mode interrupt service
+; routine will return to us when it is finished.
+
+ push regUserSS ;save user stack address on our own stack
+ push regUserSP ; frame so we can restore it later
+ push ds
+ push regUserFL
+ push cs
+ push offset pmrf50
+
+; Build an IRET frame on the stack to use to transfer control to the
+; real mode interrupt routine.
+
+ xchg bx,ax ;interrupt vector offset to BX, preserve BX
+
+ and byte ptr regUserFL+1,not 02h ;use entry flags less
+ push regUserFL ; the interrupt flag (IF)
+
+ push ax
+ mov ax,word ptr RmHwIsr[bx]
+ or ax,word ptr RmHwIsr[bx + 2]
+ je pmrf20
+
+;
+; Don't reflect to the reflector that will reflect back to pmode
+;
+ pop ax
+ push word ptr RmHwIsr[bx+2]
+ push word ptr RmHwIsr[bx]
+ jmp pmrf30
+
+pmrf20: pop ax
+ push word ptr es:[bx+2] ;push segment of isr
+ push word ptr es:[bx] ;push offset of isr
+pmrf30: xchg bx,ax
+ mov ax,regUserAX ;restore entry value of AX
+;
+; At this point the interrupt reflector stack looks like this:
+;
+; [18] previous stack frame pointer
+; [16] stack segment of original stack
+; [14] stack pointer of original stack
+; [12] real mode dos extender data segment
+; [10] dos extender flags
+; [8] segment of return address back to interupt reflector
+; [6] offset of return address back to interrupt reflector
+; [4] user flags as on entry from original interrupt
+; [2] segment of real mode ISR
+; [0] offset of real mode ISR
+
+; Execute the real mode interrupt service routine
+
+ iret
+
+; The real mode ISR will return here after it is finsished.
+
+pmrf50: pop ds
+ pushf
+
+ FCLI ;We have to clear interrupts here, because
+ cld ; the interrupt routine may have returned
+ ; with interrupts on and our code that uses
+ ; static variables must be protected. We
+ ; turn them off after to pushf instruction so
+ ; that we can preserve the state of the
+ ; interrupt flag as returned by the ISR.
+
+ mov regUserAX,ax
+ pop ax
+ pop regUserSP
+ pop regUserSS
+
+if DEBUG
+ add sp,4 ;'pop' off debugging info
+endif
+ CHECK_STACK
+ ASSERT_REFLSTK_OK
+ pop pbReflStack ;deallocate stack frame(s)--it used to be that
+ ; we'd simply add CB_STKFRAME to pbReflStack
+ ; to deallocate a frame. But we found a TSR
+ ; that would pop up on an Int 28h and iret
+ ; on the Int 28h frame from an Int 8h! This
+ ; left some stack allocated, and soon resulted
+ ; in running out of frames. Keeping the frame
+ ; pointer on the stack allows us to pop
+ ; multiple frames at once.
+ ASSERT_REFLSTK_OK
+
+; Switch back to protected mode.
+
+ push ax ;preserve AX
+ SwitchToProtectedMode
+ pop ax
+
+ DEBUG_TRACE DBGTR_EXIT, 0, 0, 1000h
+; Switch back to the original stack.
+
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Put the flags returned by the real mode interrupt routine back into
+; the caller's stack so that they will be returned properly.
+
+ push bp ;stack -> BP DS ES IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [10]
+ and [bp+10],0300h ;clear all but the interrupt and trace flags
+ ; in the caller's original flags
+ or [bp+10],ax ;combine in the flags returned by the
+ ; interrupt service routine. This will cause
+ ; us to return to the original routine with
+ ; interrupts on if they were on when the
+ ; interrupt occured, or if the ISR returned
+ ; with them on.
+ pop bp
+
+; And return to the original interrupted program.
+
+ mov ax,regUserAX
+ pop ds
+ pop es
+ riret
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Real Mode Interrupt Reflector
+ page
+; -------------------------------------------------------
+; REAL MODE INTERRUPT REFLECTOR
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+; -------------------------------------------------------
+; RMIntrEntryVector -- This table contains a vector of
+; near jump instructions to the real mode interrupt
+; reflector. Real mode interrupts that have been hooked
+; by the protected mode application have their vector
+; set to entry the real mode reflector through this table.
+
+ public RMIntrEntryVector,EndRmIntrEntry
+
+RMIntrEntryVector:
+
+ rept 256
+ call RMIntrReflector
+ endm
+
+EndRMIntrEntry:
+
+; -------------------------------------------------------
+; RMIntrReflector -- This routine is the entry point for
+; the real mode interrupt reflector. This routine
+; is entered when an interrupt occurs (either software
+; or hardware) that has been hooked by the protected mode
+; application. It switches the processor to protected mode
+; and transfers control to the appropriate interrupt
+; service routine for the interrupt. After the interrupt
+; service routine completes, it switches back to real
+; mode and returns control to the originally interrupted
+; real mode code.
+; Entry to this routine comes from the RMIntrEntryVector,
+; which contains a vector of near call instructions, which
+; all call here. The interrupt number is determined from
+; the return address of the near call from the interrupt
+; entry vector.
+; The address of the protected mode interrupt service routine
+; to execute is determined from the protected mode interrupt
+; descriptor tabel and the interrupt number.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: The segment registers are explicitly preserved by
+; this routine. Other registers are as preserved or
+; modified by the interrutp service routine.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public RMIntrReflector
+
+RMIntrReflector:
+;
+; On entry, the stack layout is:
+; [6] FLAGS - "
+; [4] CS - "
+; [2] IP - from original interrupt
+; [0] IP - from interrupt entry vector call
+;
+ FCLI
+ cld
+ push ds
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+if DEBUG
+;
+; Are we on a DOSX interrupt reflector stack?
+;
+ push ax
+ push cx
+ mov ax,ss
+ mov cx,ds
+ cmp ax,cx
+ pop cx
+ jne @F
+
+ cmp sp,offset bReflStack
+ jb @F
+ cmp sp,offset pbReflStack
+ jnb @F
+;
+; If so, have we overflowed a stacklet?
+;
+ mov ax,pbReflStack
+ cmp sp,ax
+ ja @F
+ add ax,CB_STKFRAME
+ cmp sp,ax
+ jb @F
+ pop ax
+ Real_Debug_Out "DOSX:RMIntrReflector--Reflector stack overflow."
+ push ax
+@@:
+ pop ax
+endif ;DEBUG
+ mov regUserAX,ax ;save user AX for later
+ push bp ;stack -> BP DS IP IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [A]
+ mov ax,[bp+0Ah] ;get the interrupted routine's flags
+ and ax,NOT 4100h ;clear the trace flag in case we got
+ ; an interrupt on an instruction about
+ ; to be single stepped
+ mov regUserFL,ax ;and save for later
+ mov ax,es
+ xchg ax,[bp+4] ;save ES and get entry vector address
+ pop bp
+
+; Some software (like older versions of Smartdrv.sys) may enable A20 on
+; their own, and get very 'suprised' to find it turned off by our PM->RM
+; mode switch. If they used Himem.sys, this wouldn't be necessary, but...
+
+if VCPI
+ cmp fVCPI,0
+ jnz @f
+endif
+ push ax ;get/save current A20 state on stack
+ push bx
+ xmssvc 7
+ mov regUserSP,ax ;use regUserSP as a temp var
+ pop bx
+ pop ax
+@@:
+ push regUserSP
+
+; The state that we want to save on the user's stack has been set up.
+; Convert the entry vector return address into an interrupt number.
+
+ sub ax,offset RMIntrEntryVector+3
+ push cx
+ mov cl,3
+ div cl
+ pop cx
+
+;if DEBUG ; debugbug
+ mov PMIntNo,ax
+;endif
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame.
+
+ mov regUserSP,sp ;save entry stack pointer so we can
+ mov regUSerSS,ss ; switch to our own stack
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroup ;switch to the reflector stack frame
+ENDIF
+ mov sp,pbReflStack
+ FIX_STACK
+ push pbReflStack ;save stack frame ptr on stack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ push ax ;save interrupt vector table offset
+ SwitchToProtectedMode
+ pop ax
+
+if DEBUG ;--------------------------------------------------------
+
+ push 0DEADh ;debugging id & interrupt number
+ push PMIntNo
+
+ cmp fTraceReflect,0
+ jz @f
+ push ax
+ mov ax,PMIntNo
+ Trace_Out "(rp#AL)",x
+ pop ax
+@@:
+
+; Perform a too-late-to-save-us-now-but-we-want-to-know check on the
+; reflector stack.
+
+ cmp StackGuard,1022h
+ jz @f
+ Debug_Out "DOSX:RMIntrReflector--Global reflector stack overflow."
+@@:
+endif ;DEBUG ---------------------------------------------------------
+
+; Build an IRET frame on the stack so that the protected mode interrupt service
+; routine will return to us when it is finished.
+
+ push regUserSS ;save user stack address on our own stack
+ push regUserSP ; frame so we can restore it later
+ push ds
+IFDEF WOW
+ DEBUG_TRACE DBGTR_ENTRY, ax, 0, 0
+
+ cmp ax, 8
+ jb short rmrf_nothw
+ cmp ax, 10h
+ jb short rmrf_hw
+ cmp ax, 70h
+ jb short rmrf_nothw
+ cmp ax, 78h
+ jnb short rmrf_nothw
+
+rmrf_hw:
+ push es
+ push ax
+ push bx
+ mov ax, SEL_VDMTIB or STD_RING
+ mov es, ax
+ inc word ptr es:[VDMTIB_LockCount]
+ cmp word ptr es:[VDMTIB_LockCount], 1
+ jnz short rmrf_nosw
+
+ mov ax, ss
+ mov es:[VDMTIB_SaveSsSelector], ax
+ mov ax, sp
+ add ax, 6
+ mov word ptr es:[VDMTIB_SaveEsp], ax
+ mov word ptr es:[VDMTIB_SaveEsp+2], 0
+
+ mov bx, word ptr es:[VDMTIB_Esp]
+ sub bx, 6
+ pop ax
+ mov ds:[bx], ax ;bx
+ pop ax
+ mov ds:[bx+2], ax ;ax
+ pop ax
+ mov ds:[bx+4], ax ;es
+ push word ptr es:[VDMTIB_SsSelector]
+ pop ss
+ mov sp, bx
+
+rmrf_nosw:
+ pop bx
+ pop ax
+ pop es
+
+ test DpmiFlags,DPMI_32BIT
+ jnz rmrf_32
+ .386p
+ push regUserFL
+ popf ;BUGBUG perf! this will trap
+ pushfd
+ sub esp,2
+ push cs
+ push dword ptr (offset rmrf50)
+ .286p
+ jmp short rmrf_hwint_cont
+rmrf_nothw:
+
+ test DpmiFlags,DPMI_32BIT
+ jnz rmrf_32
+ENDIF
+ push regUserFL
+ push cs
+ push offset rmrf50
+rmrf_hwint_cont:
+
+; Build an IRET frame on the stack to use to transfer control to the
+; protected mode ISR
+
+ and byte ptr regUserFL+1,not 02h ;use entry flags less the
+ push regUserFL ; interrupt flag (IF)
+
+ xchg bx,ax ;interrupt vector offset to BX, preserve BX
+IFNDEF WOW
+ cmp bx,CRESERVED ;Interrupt in reserved range?
+ jc rmrf_reserved
+ENDIF
+ shl bx,3
+ mov es,selIDT
+ jmp rmrf_setISR
+rmrf_reserved:
+IFNDEF WOW
+ shl bx,2
+ mov es,SelDgroupPM
+ add bx,offset DGROUP:PMIntelVector
+ENDIF
+rmrf_setISR:
+ push word ptr es:[bx+2] ;push segment of isr
+rmrf_setISROff:
+ push word ptr es:[bx] ;push offset of isr
+ xchg bx,ax
+ mov ax,regUserAX ;restore entry value of AX
+ push ds
+ pop es
+
+; At this point the interrupt reflector stack looks like this:
+;
+; [18] previous stack frame pointer
+; [16] stack segment of original stack
+; [14] stack pointer of original stack
+; [12] protected mode dos extender data segment
+; [10] dos extender flags
+; [8] segment of return address back to interupt reflector
+; [6] offset of return address back to interrupt reflector
+; [4] user flags as on entry from original interrupt
+; [2] segment of protected mode ISR
+; [0] offset of protected mode ISR
+;
+; Execute the protected mode interrupt service routine
+
+ iret
+IFDEF WOW
+.386p
+rmrf_32:
+ pushfd
+ push ax
+ mov ax,regUserFL
+ mov word ptr [esp + 2],ax
+ pop ax
+ sub esp,2
+ push cs
+ push dword ptr (offset rmrf50)
+
+; Build an IRET frame on the stack to use to transfer control to the
+; protected mode ISR
+
+ and byte ptr regUserFL+1,not 02h ;use entry flags less the
+ pushfd
+ push ax
+ mov ax,regUserFL ; interrupt flag (IF)
+ mov word ptr [esp + 2],ax
+ pop ax
+
+ xchg bx,ax ;interrupt vector offset to BX, preserve BX
+ shl bx,3
+ mov es,selIDT
+rmrf_32setISR:
+ ; bugbug this is not correct. For vectors above 32, it will
+ ; grab the segment from the wrong part of the IDT.
+
+ push 0 ;hiword of segment
+ push word ptr es:[bx+2] ;segment
+ push word ptr es:[bx+6] ;hiword of offset
+ push word ptr es:[bx] ;loword of offset
+
+ xchg bx,ax
+ mov ax,regUserAX ;restore entry value of AX
+ push ds
+ pop es
+ iretd
+.286p
+endif
+
+; The protected mode ISR will return here after it is finsished.
+
+rmrf50: pop ds
+ pushf ;save flags as returned by PM Int routine
+
+ FCLI ;We have to clear interrupts here, because
+ cld ; the interrupt routine may have returned
+ ; with interrupts on and our code that uses
+ ; static variables must be protected. We
+ ; turn them off after to pushf instruction so
+ ; that we can preserve the state of the
+ ; interrupt flag as returned by the ISR.
+ mov regUserAX,ax
+ pop ax
+ pop regUserSP
+ pop regUserSS
+
+if DEBUG
+ add sp,4 ;'pop' off debugging info
+endif
+
+ ASSERT_REFLSTK_OK
+ CHECK_STACK
+ pop pbReflStack ;deallocate stack frame(s)
+ ASSERT_REFLSTK_OK
+
+; Switch back to real mode.
+
+ push ax ;preserve AX
+ SwitchToRealMode
+ pop ax
+
+; Switch back to the original stack.
+
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Make sure the A20 line matches whatever state it was when the int occured.
+; This is for the benefit of any software that diddles A20 without using
+; an XMS driver
+
+ pop regUserSP ;A20 state at time of interrupt to temp var
+if VCPI
+ cmp fVCPI,0
+ jnz rmrf75
+endif
+ push ax ;save current ax
+ mov ax,regUserSP ;ax = A20 state at time of interrupt
+ or ax,ax ;if it was off, don't sweat it
+ jz rmrf70
+ push bx ;save bx (XMS calls destroy bl)
+ push ax
+ xmssvc 7 ;ax = current A20 state
+ pop bx ;bx = old A20 state
+ cmp ax,bx ;if A20 is still on, don't need to diddle
+ jz @f
+ xmssvc 5 ;force A20 back on
+ inc A20EnableCount ; and remember that we did this
+if DEBUG
+ or fA20,04h
+endif
+@@:
+ pop bx
+rmrf70:
+ pop ax
+rmrf75:
+
+; Put the flags returned by the real mode interrupt routine back into
+; the caller's stack so that they will be returned properly.
+
+ push bp ;stack -> BP DS ES IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [10]
+ and [bp+10],0300h ;clear all but the interrupt and trace flags
+ ; in the caller's original flags
+ or [bp+10],ax ;combine in the flags returned by the
+ ; interrupt service routine. This will cause
+ ; us to return to the original routine with
+ ; interrupts on if they were on when the
+ ; interrupt occured, or if the ISR returned
+ ; with them on.
+ pop bp
+
+; And return to the original interrupted program.
+
+ mov ax,regUserAX
+ pop ds
+ pop es
+ iret
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl INT 24h Critical Error Mapper
+ page
+; -------------------------------------------------------
+; DOS CRITICAL ERROR MAPPER
+; -------------------------------------------------------
+
+DXCODE segment
+
+; -------------------------------------------------------
+; RMDefaultInt24Handler -- Default action for a DOS critical
+; error is to fail the call.
+;
+ public RMDefaultInt24Handler
+RMDefaultInt24Handler proc far
+ mov al,3
+ iret
+RMDefaultInt24Handler endp
+
+DXCODE ends
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; RMIntr24 -- This routine is a real-mode hook that traps
+; DOS critical errors, and maps them to protect mode
+; handlers. To make the critical error realistic to
+; the application, we switch to the applications
+; stack and copy the critical error stack frame to
+; there.
+
+; On entry, the stack layout is:
+;
+; [0] [2] [4] [6] [8] [10] [12] [14] [16] [18] [20] [22] [24] [26] [28]
+; IP CS FL AX BX CX DX SI DI BP DS ES IP CS FL
+;
+; |------------| |------------------------------------------| |------------|
+;
+; IRET TO DOS REGS AT TIME OF INT 24H IRET TO APP
+;
+
+ public RMIntr24
+
+RMIntr24 proc far
+
+ FCLI
+ cld
+ push es
+ push ds
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup ;stack -> DS ES IP CS FL ...
+ENDIF
+ assume ds:DGROUP ; [0] [2] [4] [6] [8] ...
+
+; We need a temporary stack to do real->protect mode switching, etc.
+; Allocate and use an interrupt frame for that.
+
+ mov regUserSP,sp ;save entry stack pointer so we can
+ mov regUSerSS,ss ; switch to our own stack
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroup ;switch to the reflector stack frame
+ENDIF
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+ push ax ;save ax, switch to protected mode
+ SwitchToProtectedMode ; need to be on our stack
+ pop ax
+
+; Now switch to the applications stack frame. We assume that the dos function
+; generating the critical error came from a protected mode app and was passed
+; through PMIntrDos, who saved the app's current stack in regusrss:regusrsp.
+
+ mov ss,pmusrss ;switch (back) to app's stack
+ifndef WOW
+ mov sp,pmusrsp
+else
+.386p
+ movzx esp,word ptr pmusrsp
+.286p
+endif
+
+ push regUserSS ;save prior stack address on app's stack
+ push regUserSP ; frame so we can restore it later
+
+; Copy critical error stack info to application's stack
+
+ pushf ;we don't really know where the original
+ push cs ; int 21h service was requested, so fake
+ push offset rm24trap ; one to point at a routine of ours
+
+ sub sp,9*2 ;temp save the general regs further down the
+ pusha ; stack, they'll get poped in a little while
+
+ mov ax,regUserSS ;we need a selector to the previous stack
+ mov bx,STD_DATA ;(it is almost certainly the PMIntrDos
+ call ParaToLDTSelector ; real mode stack, but this is playing safe)
+
+ mov cx,9 ;okay, now copy the 9 register values from
+ mov si,regUserSP ; the DOS visable stack to the app's
+ add si,5*2
+ mov ds,ax
+ assume ds:NOTHING
+ push ss
+ pop es
+ mov di,sp
+ add di,8*2
+ cld
+ rep movsw
+
+ mov ds,selDgroupPM ;restore addressability to our DGROUP
+ assume ds:DGROUP
+
+; On entry, BP:SI points to a device header. Map BP from a seg to a selector.
+
+ mov ax,bp
+ mov bx,STD_DATA
+ call ParaToLDTSelector
+ mov regUserAX,ax
+
+ popa ;restore initial register values
+
+ mov bp,regUserAX ;give them the selector, not the segment
+
+; Invoke the protected mode handler
+
+ifdef WOW
+.386p
+ test DpmiFlags,word ptr DPMI_32BIT
+ jz ri2410
+
+ pushfd
+ push 0
+ push cs
+ push 0
+ push offset rm24ret
+ jmp ri2420
+.286p
+endif
+ri2410: pushf ;put our return address on the stack so the
+ push cs ; handler will return to us when it's done.
+ push offset rm24ret
+
+ifndef WOW
+ pushf ;transfer control to the
+ push word ptr PMInt24Handler+2 ; pm handler
+ push word ptr PMInt24Handler
+ iret
+else
+.386p
+ri2420: pushfd ;transfer control to the
+ push dword ptr PMInt24Handler+4 ; pm handler
+ push dword ptr PMInt24Handler
+ iretd
+.286p
+endif
+
+; The protected mode critical error handler returns here when it's finished
+; (at least it had better return here!)
+
+ public rm24ret
+rm24ret:
+
+ assume ds:NOTHING,es:NOTHING
+ FCLI
+ cld
+
+ add sp,12*2 ;clear critical error junk from stack
+
+rm24exit:
+ mov ds,selDgroupPM ;DOS extender data segment
+ assume ds:DGROUP
+
+ mov regUserAX,ax ;save action code from pm handler
+ pop regUserSP ;pop prior stack location
+ pop regUserSS
+
+; Switch back to the temp interrupt stack frame, drop to real mode, back
+; to the initial stack, and return to DOS.
+
+ ASSERT_REFLSTK_OK
+ CHECK_STACK
+ add pbReflStack,CB_STKFRAME ;in the reverse order from above so
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroupPM ; that we wind up in the same place
+ifndef WOW
+ mov sp,pbReflStack
+else
+.386p
+ movzx esp,pbReflStack
+.286p
+endif
+
+
+ SwitchToRealMode ;gotta be on our own stack to do this
+
+ mov ax,regUserAX ;recover AX from pm critical error handler
+
+ mov ss,regUserSS ;switch back to the original stack.
+ mov sp,regUserSP
+
+ pop ds ;return to DOS
+ pop es
+ iret
+
+; -------------------------------------------------------
+;
+; rm24trap -- This code gets executed if the protected mode critical
+; error handler attempts to bypass DOS and return directly
+; to the application. Currently this is not allowed, and
+; we just return to DOS anyway--most likely to die!
+;
+; Note: THIS IS NOT SUPPORTED! DON'T DO THIS!
+
+BeginHighSegment
+
+ public rm24trap
+
+rm24trap:
+
+ Debug_Out "Critical error handler tried to return to application!"
+
+ jmp short rm24exit
+
+EndHighSegment
+
+RMIntr24 endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl INT 28h Idle Handler
+ page
+; -------------------------------------------------------
+; INT 28H IDLE HANDLER
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntr28 -- Protected mode handler for Idle Int 28h calls.
+; The purpose of this routine is simply to cut down on the
+; number of protected mode to real mode switches by ignoring
+; many of the Int 28h idle calls made by the Windows PM
+; kernel.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr28
+
+PMIntr28 proc near
+
+
+ cld
+ push ds ;address our DGROUP
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+
+ cmp Int28Filter,0 ;are we passing any through?
+ jz @f
+
+ inc Int28Count ;should this one be reflected?
+ jz i28_reflect
+@@:
+ pop ds
+ iret ; no, just ignore it
+
+i28_reflect: ; yes, reset count and
+ push ax ; reflecto to real mode
+ mov ax,Int28Filter
+ neg ax
+ mov Int28Count,ax
+ pop ax
+ pop ds
+ assume ds:NOTHING
+
+ jmp PMIntrEntryVector + 3*28h
+
+PMIntr28 endp
+
+; -------------------------------------------------------
+ subttl Real-Time Clock Int 70h Handler
+ page
+; -------------------------------------------------------
+; REAL-TIME CLOCK INT 70h HANDLER
+; -------------------------------------------------------
+
+; PMIntr70 -- Protected mode handler for Real-Time clock
+; interrupts. This routine intercepts real-time clock
+; interrupts, and may cause them to be ignored. On 286
+; hardware, the mode switch time is a big problem in trying
+; to service the 0.976 ms periodic interrupt. So, if this
+; is a 286 machine, and periodic interrupts are enabaled,
+; we EOI the slave & master PICs, and IRET. A Tandy 2500 XL
+; machine was having a problem with the interrupt reflector
+; stack overrunning because the PS/2 style mouse was causing
+; mode switches while the RTC was programmed for periodic
+; interrupts.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr70
+
+PMIntr70 proc near
+
+ cld
+ push ds ;address our DGROUP
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+
+ cmp idCpuType,3 ;assume we can mode switch fast enough
+ jae i70_reflect ; on 386 + processors
+
+ push ax ;on a 286, are periodic interrupts
+ mov al,0Bh ; enabled? Read clock register B
+ call ReadCMOS
+
+ and al,40h ;periodic interrupts enabled?
+ jz i70_286_reflect ; no, something else, so reflect it
+
+ mov al,0Ch ;read register C to clear int bits
+ call ReadCMOS
+
+ mov al,20h ;EOI the slave PIC
+ out INTB00,al
+ IO_Delay
+ out INTA00,al ;EOI the master PIC
+
+ pop ax ;back to the shadows again...
+ pop ds
+ iret
+
+i70_286_reflect:
+ pop ax
+
+i70_reflect: ;reflect interrupt to real mode
+ pop ds
+ assume ds:NOTHING
+
+ jmp PMIntrEntryVector + 3*70h
+
+PMIntr70 endp
+
+; -------------------------------------------------------
+; ReadCMOS -- Read selected location from CMOS ram/Real-Time clock.
+;
+; in: al - CMOS location to read
+; out: al - CMOS valus
+; uses: All registers perserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public ReadCMOS
+
+ReadCMOS proc near
+
+ out CMOSLoc,al
+ IO_Delay
+ in al,CMOSValue
+ ret
+
+ReadCMOS endp
+
+; -------------------------------------------------------
+ subttl Ignore Interrupt Handlers
+ page
+; -------------------------------------------------------
+; IGNORE INTERRUPT HANDLER
+; -------------------------------------------------------
+
+; PMIntrIgnore -- Service routine for protected mode interrupts
+; that should be ignored, and not reflected to real mode.
+; Currently used for:
+;
+; Int 30h - used to be Win/386 Virtualize I/O, now
+; unused but no int handler in real mode
+; Int 41h - Wdeb386 interface, no int handler in
+; real mode
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrIgnore
+
+PMIntrIgnore proc near
+
+ iret
+
+PMIntrIgnore endp
+
+; -------------------------------------------------------
+
+ public PMIntr19
+PMIntr19 proc near
+
+ push offset DXPMCODE:Reboot
+ call RZCall
+
+bpRebootIDT df 0
+
+Reboot:
+ mov ax,40h
+ mov es,ax
+ mov word ptr es:[0072h],1234h
+ lidt bpRebootIDT
+ int 3
+
+PMIntr19 endp
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl XMS Driver Interface
+ page
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; XMScontrol - This function implements a protected mode
+; interface to a real mode XMS driver. Unlike other
+; routines in this module, this routine is called by
+; the user, not invoked via an INT instruction.
+;
+; Input: User's regs for XMS driver
+; Output: regs from XMS driver
+; Uses: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public XMScontrol
+
+XMScontrol proc far
+
+ jmp short XMSentry ;'standard' XMS control function
+ nop ; just to be consistant
+ nop
+ nop
+
+XMSentry:
+
+; Modify the stack so it looks like we got here via an INT (except that
+; we may still have interrupts enabled)
+
+ pushf
+ cld
+
+ push bp
+ mov bp,sp ;bp -> [BP] [FL] [IP] [CS]
+ push ax
+ push bx
+
+ mov ax,[bp+4]
+ mov bx,[bp+6]
+ xchg ax,[bp+2]
+ mov [bp+4],bx
+ mov [bp+6],ax ;bp -> [BP] [IP] [CS] [FL]
+ pop bx
+ pop ax
+ pop bp
+
+; We don't support XMS function 0Bh (Move Extended Memory Block) because
+; it requires mapping of data between hi/low memory. Maybe someday...
+
+ cmp ah,0Bh
+ jnz xms_2
+xms_deny:
+ xor ax,ax ;if function 0Bh, return failure
+ mov bl,80h ; (ax = 0, bl = 80h-not implemented)
+ jmp short XMSret
+xms_2:
+
+; We are not really an Int handler, but close enough...
+
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:DGROUP ; also sets up addressability
+
+if 0
+if VCPI
+;
+; If we're using VCPI, then fail the call. This is because XMS memory
+; would not be useful in protected mode unless we paged it into our
+; page tables.
+;
+ cmp fVCPI,0
+ jz xms_3
+ call LeaveIntHandler
+ mov ax,0
+ mov dx,0
+ mov bl,80h ; BX = 80h-not implemented.
+ jmp XMSret
+xms_3:
+endif
+endif
+
+ SwitchToRealMode
+
+ pop es ;load regs for driver
+ pop ds
+ assume ds:NOTHING,es:NOTHING,ss:DGROUP
+ popa
+ npopf
+
+ call lpfnXMSFunc ;call real mode driver
+
+ pushf ;rebuild stack frame
+ FCLI
+ cld
+ pusha
+ push ds
+ push es
+
+ mov bp,sp ;restore stack frame pointer
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ call LeaveIntHandler
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+XMSret:
+ riret
+
+XMScontrol endp
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Special Interrupt Handler Routines
+ page
+; -------------------------------------------------------
+;
+; The following sets of routines handle interrupts that
+; are function call interfaces and require special servicing
+; by the Dos Extender. These interrupts are such things as
+; the mouse driver function call interrupt, various PC BIOS
+; function call interrupts, etc. Note that INT 21h (the Dos
+; function call interrupt) is not handled here. These
+; interrupts typically require that register values be modified
+; and parameter data be copied between real mode memory and
+; extended memory. The following conventions are used for these
+; interrupt function handler routines.
+;
+; A stack is allocated from the interrupt reflector stack for these
+; routines to use. This allows nested servicing of interrupts.
+; A stack frame is built in the allocated stack which contains the
+; following information:
+; original caller's stack address
+; caller's original flags and general registers (in pusha form)
+; caller's original segment registers (DS & ES)
+; flags and general registers to be passed to interrupt routine
+; (initially the same as caller's original values)
+; segment registers (DS & ES) to be passed to interrupt routine
+; (initially set to the Dos Extender data segment address)
+; This stack frame is built by the routine EnterIntHandler, and its
+; format is defined by the structure INTRSTACK. The stack frame is
+; destroyed and the processor registers set up for return to the user
+; by the function LeaveIntHandler.
+;
+; For each interrupt, there is an entry function and an exit function.
+; The entry function performs any modifications to parameter values and
+; data buffering necessary before the interrupt service routine is called.
+; The exit function performs any data buffering and register value
+; modifications after return from the interrupt service routine.
+;
+; There are two sets of general registers and two sets of segment
+; registers (DS & ES) on the stack frame. One set of register values
+; has member names of the form intUserXX. The values in these stack
+; frame members will be passed to the interrupt service routine when
+; it is called, and will be loaded with the register values returned
+; by the interrupt service routine. The other set of registers values
+; has member names of the form pmUserXX. These stack frame members
+; contain the original values in the registers on entry from the
+; user program that called the interrupt.
+;
+; When we return to the original caller, we want to pass back the
+; general registers as returned by the interrupt routine (and possibly
+; modified by the exit handler), and the same segment registers as
+; on entry, unless the interrupt routine returns a value in a segment
+; register. (in this case, there must be some code in the exit routine
+; to handle this). This means that when we return to the caller, we
+; return the general register values from the intUserXX set of stack
+; frame members, but we return the segment registers from the pmUserXX
+; set of frame members. By doing it this way, we don't have to do
+; any work for the case where the interrupt subfuntion doesn't require
+; any parameter manipulation. NOTE however, this means that when
+; manipulating register values to be returned to the user, the segment
+; registers are treated opposite to the way the general registers are
+; treated. For general registers, to return a value to the user,
+; store it in a intUserXX stack frame member. To return a segment
+; value to the user, store it in a pmUserXX stack frame member.
+;
+; -------------------------------------------------------
+ subttl BIOS Video Interrupt (Int 10h) Service Routine
+ page
+; -------------------------------------------------------
+; BIOS VIDEO INTERRUPT (INT 10h) SERVICE ROUTINE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntrVideo - Entry point into interrupt reflector code
+; for IBM PC Bios video (int 10h) calls.
+;
+; Input: normal registers for Bios calls
+; Output: normal register returns for Bios calls
+; Errors: normal Bios errors
+; Uses: as per Bios calls
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrVideo
+
+PMIntrVideo:
+
+ call EnterIntHandler ;build a stack frame and fix up the
+ cld ; return address so that the interrupt
+ ;service routine will return to us.
+;
+; Perform fixups on the entry register values
+ call IntEntryVideo
+;
+; Execute the interrupt service routine
+ SwitchToRealMode
+ assume ss:DGROUP
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+ popa
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset piv_10
+ mov ax,es:[10h*4]
+ mov [bp + 2],ax
+ mov ax,es:[10h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+piv_10: pushf
+ FCLI
+ cld
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+;
+; Perform fixups on the return register values.
+ mov ax,[bp].pmUserAX ;get original function code
+ call IntExitVideo
+;
+; And return to the original caller.
+ call LeaveIntHandler
+
+ riret
+
+; -------------------------------------------------------
+; IntEntryVideo -- This routine performs any register
+; fixups and data copying needed on entry to the
+; PC BIOS video interrupt (Int 10h)
+;
+; Input: register values on stack frame
+; Output: register values on stack frame
+; Errors: none
+; Uses: any registers modified,
+; possibly modifies buffers rgbXfrBuf0 or rgbXfrBuf1
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntEntryVideo
+
+IntEntryVideo:
+
+ cmp ah,10h
+ jnz ienv20
+;
+; Video palette control function. Check for subfunctions that require
+; special actions.
+ienv10: cmp al,2 ;update all palette registers?
+ jnz @F
+ mov cx,17 ;palette data is 17 bytes long
+ jmp short ienv70 ;go copy the data
+;
+@@: cmp al,9 ;read all palette registers
+ jz ienv72
+;
+ cmp al,12h ;update video DAC color registers
+ jnz @F
+ mov cx,[bp].pmUserCX ;count of table entries is in caller CX
+ add cx,cx ;each entry is 3 bytes long
+ add cx,[bp].pmUserCX
+ jmp short ienv70 ;go copy the data down
+
+@@: cmp al,17h ;read a block of video DAC registers
+ jz ienv72
+;
+ jmp short ienv90
+;
+;
+ienv20: cmp ah,11h
+ jnz ienv30
+;
+; Character generator interface function.
+; NOTE: a number of subfunctions of function 11h need to have munging
+; and data buffering performed. However, function 30h is the only
+; one used by Codeview, so this is the only one currently implemented.
+; For this one, nothing needs to be done on entry, only on exit.
+ jmp short ienv90
+;
+;
+ienv30: cmp ah,1Bh
+ jnz ienv40
+;
+; Video BIOS functionality/state information.
+; On entry, we need to fix up ES:DI to point to our buffer.
+ mov [bp].intUserDI,offset DGROUP:rgbXfrBuf0
+ jmp short ienv90
+;
+;
+ienv40:
+ jmp short ienv90
+;
+; Copy the buffer from the user ES:DX to our transfer buffer and set
+; the value to DX passed to the interrupt routine to point to our buffer.
+ienv70: cld
+ jcxz ienv90
+ push ds
+ mov si,[bp].pmUserDX
+ mov ds,[bp].pmUserES
+ mov di,offset DGROUP:rgbXfrBuf1
+ cld
+ rep movsb
+ pop ds
+;
+ienv72: mov [bp].intUserDX,offset DGROUP:rgbXfrBuf1
+ jmp short ienv90
+
+;
+; All done
+ienv90:
+ ret
+
+; -------------------------------------------------------
+; IntExitVideo: This routine performs any register
+; fixups and data copying needed on exit from the
+; PC BIOS video interrupt (Int 10h).
+;
+; Input: register values on stack frame
+; Output: register values on stack frame
+; Errors: none
+; Uses: any registers modified
+; possibly modifies buffers rgbXfrBuf0 or rgbXfrBuf1
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntExitVideo
+
+IntExitVideo:
+
+ cmp ah,10h
+ jnz iexv20
+;
+; Palette control function.
+ cmp al,9 ;read palette data function
+ jnz @F
+ mov cx,17
+ jmp short iexv70
+;
+@@: cmp al,17h ;read video DAC registers
+ jnz @F
+ mov cx,[bp].pmUserCX ;each entry in table is 3 bytes long
+ add cx,cx
+ add cx,[bp].pmUserCX
+ jmp short iexv70
+;
+@@: jmp short iexv72
+;
+;
+iexv20: cmp ah,11h
+ jnz iexv30
+;
+; Character generator interface function.
+; NOTE: a number of subfunctions of function 11h need to have munging
+; and data buffering performed. However, function 30h is the only
+; one used by Codeview, so this is the only one currently implemented
+ cmp al,30h
+ jnz @F
+ mov ax,[bp].intUserES ;get the paragraph address returned by BIOS
+ mov bx,STD_DATA
+ call ParaToLDTSelector ;get a selector for that address
+ mov [bp].pmUserES,ax ;store the selector so that it will be
+ ; returned to the caller
+@@: jmp short iexv90
+;
+;
+iexv30: cmp ah,1Bh
+ jnz iexv40
+;
+; Video BIOS functionality/state information.
+; On exit, we need to fix up the pointer at the beginning of the
+; data put in our buffer by the BIOS, and then transfer the buffer up
+; to the user.
+ mov ax,word ptr rgbXfrBuf0[2] ;get segment of pointer to
+ ; 'static functionallity table'
+ mov bx,STD_DATA
+ call ParaToLDTSelector ;convert paragraph to selector
+ mov word ptr rgbXfrBuf0[2],ax ;store back into table
+ push es
+ mov si,offset rgbXfrBuf0 ;pointer to our copy of the table
+ mov di,[bp].pmUserDI ;where the user wants it
+ mov [bp].intUserDi,di ;restore the DI returned to the user
+ mov es,[bp].pmUserES
+ mov cx,64 ;the table is 64 bytes long
+ cld
+ rep movsb ;copy the table to the user's buffer
+ pop es
+
+ jmp short iexv90
+;
+;
+iexv40:
+ jmp short iexv90
+
+;
+; Copy data from our buffer to the caller's buffer pointed to by ES:DX
+iexv70: cld
+ push es
+ mov di,[bp].pmUserDX
+ mov es,[bp].pmUserES
+ mov si,offset DGROUP:rgbXfrBuf1
+ rep movsb
+ pop es
+;
+; Restore the caller's DX
+iexv72: mov ax,[bp].pmUserDX
+ mov [bp].intUserDX,ax
+;
+; All done
+iexv90:
+ ret
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl BIOS Misc. Interrupt (Int 15h) Service Routine
+ page
+; -------------------------------------------------------
+; BIOS MISC. INTERRUPT (INT 15h) SERVICE ROUTINE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntrMisc -- Entry point into the interrupt processing code
+; for the BIOS misc functions interrupt (INT 15h).
+;
+; Input: normal registers for Bios calls
+; Output: normal register returns for Bios calls
+; Errors: normal Bios errors
+; Uses: as per Bios calls
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrMisc
+
+PMIntrMisc:
+;
+ call EnterIntHandler ;build a stack frame and fix up the
+ cld ; return address so that the interrupt
+ ;service routine will return to us.
+;
+; Perform fixups on the entry register values
+ call IntEntryMisc
+;
+; Execute the interrupt service routine
+ SwitchToRealMode
+ assume ss:DGROUP
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+ popa
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset pim_10
+ mov ax,es:[15h*4]
+ mov [bp + 2],ax
+ mov ax,es:[15h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+pim_10: pushf
+ FCLI
+ cld
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+;
+; Perform fixups on the return register values.
+ mov ax,[bp].pmUserAX ;get original function code
+ call IntExitMisc
+;
+; And return to the original caller.
+ call LeaveIntHandler
+ riret
+
+; -------------------------------------------------------
+; MISC INTERRUPT SUPPORT ROUTINES
+; -------------------------------------------------------
+;
+; IntEntryMisc -- This function performs data transfer
+; and register translation on entry to the BIOS Misc.
+; functions interrupt. (INT 15h).
+;
+; Input: AX - BIOS function being performed
+; Output:
+; Errors:
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntEntryMisc
+
+IntEntryMisc:
+
+; Map requests to set the PS/2 Pointing Device Handler Address
+
+ cmp ax,0C207h ;PS/2 Set Pointing Device Handler adr?
+ jnz iem90
+
+ mov ax,[bp].pmUserBX ;User's ES:BX -> handler
+ mov word ptr lpfnUserPointingHandler,ax
+ mov ax,[bp].pmUserES
+ mov word ptr [lpfnUserPointingHandler+2],ax
+
+ mov ax,segDXCodePM ;pass BIOS address of our handler
+ mov [bp].intUserES,ax
+ mov ax,offset PointDeviceHandler
+ mov [bp].intUserBX,ax
+
+iem90:
+ ret
+
+; -------------------------------------------------------
+; IntExitMisc -- This function performs data transfer
+; and register translation on exit from the BIOS Misc.
+; Functions interrupt (INT 15h).
+;
+; Input: AX - BIOS function being performed
+; Output:
+; Errors:
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntExitMisc
+
+IntExitMisc:
+ push ax
+ push bx
+ push cx
+ push dx
+;
+; Check for function 0C0h - Return System Configuration Parameters
+ cmp ah,0C0h
+ jnz ixmi30
+ test [bp].intUserFL,1 ;check if the bios call returned an error
+ jnz ixmi90 ;(carry flag set in returned flags)
+;
+; The BIOS call succeeded. This means that ES:BX points to a configuration
+; vector. We need to fix up the segment to be a selector.
+ mov dx,[bp].intUserES
+ cmp dx,0F000h ;does it point to normal BIOS segment
+ jnz ixmi22
+ mov ax,SEL_BIOSCODE or STD_RING
+ jmp short ixmi24
+
+ixmi22: call ParaToLinear
+ mov cx,0FFFFh
+ mov ax,SEL_USERSCR or STD_TBL_RING
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+ixmi24: mov [bp].pmUserES,ax
+ jmp short ixmi90
+
+; Chack for function 0C207h - PS/2 Set Pointing Device Handler Address
+
+ixmi30:
+ cmp ax,0C207h
+ jne ixmi90
+
+ mov ax,[bp].pmUserBX ;restore user's BX
+ mov [bp].intUserBX,ax
+
+; All done
+ixmi90:
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Mouse Function Interrupt (Int 33h) Service Routine
+ page
+; -------------------------------------------------------
+; MOUSE FUNCTION INTERRUPT (INT 33h) SERVICE ROUTINE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntrMouse - Entry point into interrupt reflector code
+; for mouse driver (int 33h) calls.
+;
+; Input: normal registers for mouse calls
+; Output: normal register returns for mouse calls
+; Errors: normal mouse errors
+; Uses: as per mouse calls
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrMouse
+
+PMIntrMouse:
+;
+ call EnterIntHandler ;build a stack frame and fix up the
+ cld ; return address so that the interrupt
+ ;service routine will return to us.
+;
+; Perform fixups on the entry register values
+ call IntEntryMouse
+;
+; Execute the interrupt service routine
+ SwitchToRealMode
+ assume ss:DGROUP
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+ popa
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset pimo_10
+ mov ax,es:[33h*4]
+ mov [bp + 2],ax
+ mov ax,es:[33h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+pimo_10: pushf
+ FCLI
+ cld
+ pusha
+ push ds
+ push es
+ mov bp,sp ;restore stack frame pointer
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+;
+; Perform fixups on the return register values.
+ mov ax,[bp].pmUserAX ;get original function code
+ call IntExitMouse
+;
+; And return to the original caller.
+ call LeaveIntHandler
+ riret
+
+; -------------------------------------------------------
+; MOUSE SUPPORT ROUTINES
+; -------------------------------------------------------
+
+; IntEntryMouse -- This function performs data transfer and
+; register translation on entry to mouse driver functions.
+; (INT 33h)
+;
+; Input: AX - mouse function being performed
+; Output:
+; Errors:
+; Uses: NOTHING
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntEntryMouse
+
+IntEntryMouse:
+ cld
+ push ax
+ push cx
+ push si
+ push di
+;
+ cmp al,9 ;Set graphics cursor block?
+ jnz ment10
+;
+; The user is setting a graphics cursor. We need to copy the masks
+; down to low memory so that the mouse driver can get at them and then
+; fix up the pointer in DX.
+ mov cx,32
+ jmp short ment92
+;
+; Mouse interrupt handler establishment
+ment10: cmp al,12 ;Set user defined interrupt subroutine ?
+ jnz ment20
+;
+; This command has the effect of causing a call to the address es:ds
+; Whenever an event of one of the types specified by the mask in cx.
+; The address es:dx must be saved in lpfnUserMouseHandler and the
+; real mode address of MouseInterruptHandler substituted.
+ mov ax,[bp].pmUserDX ; Load users handler offset
+ mov word ptr lpfnUserMouseHandler,ax ; Store for future use
+ mov ax,[bp].pmUserES ; Load users handler segment value
+ mov word ptr lpfnUserMouseHandler + 2,ax ; Store for future use
+ mov ax,segDXCodePM ; Load real mode code segment value
+ mov [bp].intUserES,ax ; Store in real mode es register image
+ mov ax,offset MouseInterruptHandler ; Load handler offset
+ mov [bp].intUserDX,ax ; Store in real mode dx register image
+ jmp short ment99 ;Return
+ ;
+ment20: cmp al,20
+ jc ment99
+ jnz ment30
+;
+; This is the swap interrupt subroutine function. Not currently implemented
+ jmp short ment99
+;
+ment30: cmp al,22 ;Save mouse driver state?
+ jnz ment40
+;
+; This is the save mouse driver state function. We need to pass a pointer
+; to the transer buffer down to the mouse driver.
+ mov ax,npXfrBuf1
+ mov [bp].intUserDX,ax
+ jmp short ment99
+
+ment40: cmp al,23 ;Restore mouse driver state?
+ jnz ment99
+;
+; This is the restore mouse driver state function. We need to copy the
+; mouse state buffer from the pm user location to the transfer buffer,
+; and then pass the pointer to the transfer buffer on to the mouse driver.
+ mov cx,cbMouseState
+ jcxz ment99
+;
+; Transfer the data pointed to by the user ES:DX to the scratch buffer, and
+; fix up the pointer that is passed on to the mouse driver.
+ment92: mov si,[bp].pmUserDX
+ mov di,npXfrBuf1
+ mov [bp].intUserDX,di
+ push ds
+ mov ds,[bp].pmUserES
+ cld
+ rep movs word ptr [di],word ptr [si]
+ pop ds
+;
+ment99: pop di
+ pop si
+ pop cx
+ pop ax
+ ret
+
+; -------------------------------------------------------
+; IntExitMouse -- This function performs data transfer and
+; register translation on exit from mouse driver functions.
+; (INT 33h)
+;
+; Input: AX - mouse function being performed
+; Output:
+; Errors:
+; Uses:
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public IntExitMouse
+
+IntExitMouse:
+ cld
+ cmp al,21 ;get state buffer size?
+ jnz mxit20
+;
+; We need to remember the state buffer size, so that later we will know
+; how many bytes to transfer when we do the save/restore state fucntions.
+ mov ax,[bp].intUserBX
+ mov cbMouseState,ax
+ return
+;
+mxit20: cmp al,22 ;Save mouse driver state?
+ jnz mxit30
+;
+; We need to restore the original values of ES:DX and transfer the mouse
+; state data from the real mode buffer to the user's protected mode buffer.
+ mov cx,cbMouseState
+ jcxz mxit28
+ push es
+ mov si,npXfrBuf1
+ mov di,[bp].pmUserDX
+ mov [bp].intUserDX,di
+ mov es,[bp].pmUserES
+ rep movs byte ptr [di],byte ptr [si]
+ pop es
+mxit28: return
+;
+mxit30: cmp al,23 ;Restore mouse driver state?
+ jnz mxit99
+ mov ax,[bp].pmUserDX
+ mov [bp].intUserDX,ax
+;
+mxit99: ret
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl PM Interrupt Support Routines
+ page
+; -------------------------------------------------------
+; PM INTERRUPT SUPPORT ROUTINES
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; EnterIntHandler -- This routine will allocate a stack
+; frame on the interrupt reflector stack and make
+; a copy of the registers on the allocated stack.
+;
+; Note: This routine expects the current stack to contain a near
+; return address and a normal [IP] [CS] [FL] interrupt stack
+; frame. Don't have anything else on the stack before calling
+; this routine!
+;
+; Note: This routine disables interrupts, and leaves them disabled.
+; Most callers already have them disabled, so it doesn't
+; really make a difference, except that this routine
+; requires that they be disabled.
+;
+; Input: none
+; Output: stack frame set up
+; Errors: none
+; Uses: all registers preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public EnterIntHandler
+
+EnterIntHandler proc near
+
+ FCLI ;we really want int's disabled (and
+ ; XMScontrol doesn't do that)
+ push ds
+ mov ds,selDgroupPM ;save user's DS and address our DGROUP
+ assume ds:DGROUP
+ pop regUserDS
+
+ push bp
+ mov bp,sp ;bp -> [BP] [IP] [IP] [CS] [FL]
+ push word ptr [bp+8]
+ pop regUserFL ;user's flags before doing INT
+ pop bp
+
+ pop pfnReturnAddr ;near return to our immediate caller
+
+ mov regUserSS,ss ;save caller's stack address
+ mov regUserSP,sp
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroupPM ;switch to interrupt reflector stack
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+
+; Build the stack frame. The stack frame contains the following:
+; dword & word parameter locations
+; original caller's stack address
+; caller's original flags and general registers (in pusha form)
+; caller's original segment registers (DS & ES)
+; flags and general registers to be passed to interrupt routine
+; (initially the same as caller's original values)
+; segment registers (DS & ES) to be passed to interrupt routine
+; (initially set to the Dos Extender data segment address)
+;
+; The parameter words and then the caller's original register values go on top.
+
+ sub sp,8 ;space for a dd & 2 dw's
+
+ push regUserSP
+ push regUserSS
+ push regUserFL
+ pusha
+ push regUserDS
+ push es
+
+; Now, put all of the general registers, and values for the segment
+; registers to be passed to the interrupt service routine. We pass
+; the Dos Extender data segment address to the interrupt routine.
+
+ push regUserFL
+ pusha
+IFDEF ROM
+ push segDXData
+ push segDXData
+ELSE
+ push segDXDataPM
+ push segDXDataPM
+ENDIF
+
+; And we are done.
+
+ mov bp,sp ;set up frame pointer
+ mov es,selDgroupPM
+ jmp pfnReturnAddr ;return to the caller.
+
+EnterIntHandler endp
+
+
+; -------------------------------------------------------
+; LeaveIntHandler -- This routine will restore the user registers,
+; release the stack frame, and restore the original user's stack
+; for exit from an interrupt reflector routine.
+;
+; Note: Interrupts must be off when this routine is called.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: All registers modified
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public LeaveIntHandler
+
+LeaveIntHandler proc near
+
+ FCLI
+ pop pfnReturnAddr
+
+; The copy of the register values returned from the interrupt routine
+; (and then possibly modified by the exit handler for the particular
+; interrupt) are what gets returned to the caller. We discard the original
+; register values saved on entry. (They were there so that the exit
+; routine could refer to them if necessary)
+
+ add sp,4 ;skip over interrupt service routine's
+ ; segment register values
+ popa ;restore general register values
+ pop regUserFL ;flags returned by interrupt routine
+ pop es ;get segment registers from pmUserES
+ pop regUserDS ; and pmUserDS
+ add sp,18 ;skip over the original user registers
+ ; and flags
+ pop regUserSS ;original interrupted routine's stack
+ pop regUserSP
+ mov regUserAX,ax
+
+; Switch back to the original user's stack.
+
+ ASSERT_REFLSTK_OK
+ ASSERT_CLI
+ CHECK_STACK
+ mov ss,regUserSS
+ mov sp,regUserSP
+ add pbReflStack,CB_STKFRAME
+ ASSERT_REFLSTK_OK
+
+; We need to replace the image of the flags in the original int return
+; address on the user's stack with the new flags returned from the interrupt
+; service routine.
+
+ push bp
+ mov bp,sp ;stack -> BP IP CS FL
+ mov ax,regUserFL ;flags returned by interrupt service routine
+ and ax,0BFFFh ;clear the nested task flag
+ and [bp+6],0300h ;clear all but the interrupt and trace flags
+ ; in the caller's original flags
+ or [bp+6],ax ;combine in the flags returned by the
+ ; interrupt service routine. This will cause
+ ; us to return to the original routine with
+ ; interrupts on if they were on when the
+ ; interrupt occured, or if the ISR returned
+ ; with them on.
+ pop bp
+
+; And now, return to the caller.
+
+ push pfnReturnAddr
+ mov ax,regUserAX
+ mov ds,regUserDS
+ assume ds:NOTHING
+ ret
+
+LeaveIntHandler endp
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Mouse Interrupt Callback Function Handler
+ page
+; -------------------------------------------------------
+; MOUSE INTERRUPT CALLBACK FUNCTION HANDLER
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; MouseInterruptHandler -- This routine is the entry point for
+; user requested mouse event interrupts. It switches the
+; processor to protected mode and transfers control to the
+; user protected mode mouse handling routine. When that
+; completes, it switches back to real mode and returns control
+; to the mouse driver.
+; Entry to this routine will have been requested by an
+; INT 33H code 12 with the real address of this routine
+; substituted for the users entry point.
+; The address of the user specified mouse handler as specified
+; in the original INT 33H is stored in the variable
+; lpfnUserMouseHandler.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: The segment registers are explicitly preserved by
+; this routine. Other registers are as preserved or
+; modified by the users mouse handler.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public MouseInterruptHandler
+
+MouseInterruptHandler proc far
+;
+; On entry, the stack layout is:
+; [2] CS - System mouse handler code segment
+; [0] IP - System mouse handler return offset
+;
+
+ push es
+ push ds
+ pushf
+ FCLI
+ cld
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+ pop regUserFL
+;
+; Allocate a new stack frame, and then switch to the local stack
+; frame.
+ mov regUserSP,sp ;save entry stack pointer so we can restore it
+ mov regUSerSS,ss ;save segment too
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,selDgroup ;switch to our own stack frame
+ENDIF
+ ASSERT_REFLSTK_OK
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+;
+; We are now running on our own stack, so we can switch into protected mode.
+ push ax ;preserve caller's AX
+ SwitchToProtectedMode
+ pop ax
+;
+; Build a far return frame on the stack so that the user's
+; routine will return to us when it is finished.
+ push regUserSS ; save system mouse handler stack address
+ push regUserSP ; so we can restore it later
+ push ds
+ push cs
+ push offset mih50
+;
+; Build an IRET frame on the stack to use to transfer control to the
+; user's protected mode routine
+ push regUserFL
+ push word ptr lpfnUserMouseHandler+2 ;push segment of user routine
+ push word ptr lpfnUserMouseHandler ;push offset of user routine
+;
+; At this point the interrupt reflector stack looks like this:
+;
+; [14] stack segment of original stack
+; [12] stack pointer of original stack
+; [10] real mode dos extender data segment
+; [8] segment of return address back to here
+; [6] offset of return address back here
+; [4] Users flags
+; [2] segment of user routine
+; [0] offset of user routine
+;
+; Execute the users mouse handler
+ iret
+;
+; The users handler will return here after it is finsished.
+mih50: FCLI
+ cld
+ pop ds
+ pop regUserSP
+ pop regUserSS
+;
+; Switch back to real mode.
+ push ax ;preserve AX
+ SwitchToRealMode
+ pop ax
+ CHECK_STACK
+;
+; Switch back to the original stack.
+ mov ss,regUserSS
+ mov sp,regUserSP
+ ASSERT_REFLSTK_OK
+;
+; Deallocate the stack frame that we are using.
+ add pbReflStack,CB_STKFRAME
+ ASSERT_REFLSTK_OK
+;
+; And return to the original interrupted program.
+ pop ds
+ pop es
+
+ ret
+
+MouseInterruptHandler endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl PS/2 Pointing Device Handler
+ page
+; -------------------------------------------------------
+; PS/2 POINTING DEVICE HANDLER
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; PointDeviceHandler -- This routine is the entry point for
+; the PS/2 Pointing Device Handler. It switches the
+; processor to protected mode and transfers control to the
+; user pointing device handler. When that completes,
+; it switches back to real mode and returns control to
+; the PS/2 BIOS.
+;
+; Note: The BIOS calls us with interrutps enabled!
+
+; Input: none
+; Output: none
+; Errors: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PointDeviceHandler
+
+PointDeviceHandler proc far
+
+; On entry, the stack layout is:
+;
+; [10] status
+; [8] X coordinate
+; [6] Y coordinate
+; [4] Z coordinate
+; [2] CS - PS/2 BIOS code segment
+; [0] IP - PS/2 BIOS return offset
+
+ cld
+ push es ;save PS/2 BIOS ds/es on it's stack
+ push ds
+
+IFDEF ROM
+ push ax
+ GetRMDataSeg
+ mov ds,ax
+ mov es,ax
+ pop ax
+ELSE
+ mov ds,selDgroup ;addressability to DOSX DGROUP
+ push ds
+ pop es
+ENDIF
+ assume ds:DGROUP,es:DGROUP
+
+ FCLI ;protect global regUserXX vars
+
+; Allocate a new stack frame, and then switch to the local stack
+; frame.
+
+ mov regUserSP,sp ;save entry stack pointer so we can restore it
+ mov regUSerSS,ss ;save segment too
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ ASSERT_REFLSTK_OK
+ mov ss,selDgroup ;switch to our own stack frame
+ENDIF
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+ FIX_STACK
+
+ push regUserSS ;save PS/2 BIOS stack address
+ push regUserSP ; so we can restore it later
+
+ push SEL_DXDATA or STD_RING ;DOSX DS to be poped in PM
+
+ sub sp,4*2 ;temp save the general regs further down the
+ pusha ; stack, they'll get poped in a little while
+
+; Copy PS/2 pointing device stack info to our (soon to be) protected mode stack
+
+ mov si,regUserSP ;PS/2 stack pointer
+ mov ds,regUserSS ;PS/2 stack segment
+ assume ds:NOTHING
+
+ FSTI ;no more references to global regUserXX vars
+
+ add si,4*2 ;skip over es,ds,cs,ip
+ mov di,sp ;loc for pointing device
+ add di,8*2 ; data on our stack
+ mov cx,4
+ cld
+ rep movsw
+
+ push es ;restore ds = DGROUP
+ pop ds
+ assume ds:DGROUP
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ SwitchToProtectedMode ;disables interrupts again
+ FSTI ; but we don't want them disabled
+
+ popa ;restore general registers
+
+; At this point the stack looks like this:
+;
+; [12] stack segment of original stack
+; [10] stack pointer of original stack
+; [8] protect mode dos extender data segment
+; [6] status
+; [4] X coordinate
+; [2] Y coordinate
+; [0] Z coordinate
+
+; Execute the user's pointing device handler
+
+ call [lpfnUserPointingHandler]
+
+; The users handler will return here after it is finsished.
+
+pdh50:
+ cld
+ add sp,4*2 ;discard pointing device info
+ pop ds
+
+ FCLI ;protect global regUserXX vars
+ pop regUserSP
+ pop regUserSS
+
+; Switch back to real mode.
+
+ push ax ;preserve AX
+ SwitchToRealMode
+ pop ax
+
+; Switch back to the original stack.
+
+ CHECK_STACK
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Deallocate the stack frame that we are using.
+
+ ASSERT_REFLSTK_OK
+ add pbReflStack,CB_STKFRAME
+ ASSERT_REFLSTK_OK
+
+; And return to the PS/2 BIOS
+
+ FSTI ;we came in with ints enabled
+
+ pop ds
+ pop es
+
+ ret
+
+PointDeviceHandler endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+; PROTECTED MODE FAULT HANDLERS
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+;------------------------------------------------------------------
+; TrapHandler -- Handle Protect Mode Processor Exceptions.
+;
+; Stack layout inside of this routine:
+; [34] user SS \
+; [32] user SP |
+; [30] user flags |_ placed by PMFaultAnalyzer
+; [28] user cs |
+; [26] user ip |
+; [24] error code /
+; [22] PMFaultReflector return CS
+; [20] PMFaultReflector return IP
+; [18] AX \
+; [16] CX |
+; [14] DX |
+; [12] BX |- placed by PUSHA instruction
+; [10] SP |
+; [8] BP |
+; [6] SI |
+; [4] DI /
+; [2] DS
+; [0] ES
+
+regSS equ [bp+34]
+regSP equ [bp+32]
+regFL equ [bp+30]
+regCS equ [bp+28]
+regIP equ [bp+26]
+idError equ [bp+24]
+
+regRetCS equ [bp+22]
+regRetIP equ [bp+20]
+
+regAX equ [bp+18]
+regCX equ [bp+16]
+regDX equ [bp+14]
+regBX equ [bp+12]
+regBP equ [bp+8]
+regSI equ [bp+6]
+regDI equ [bp+4]
+regDS equ [bp+2]
+regES equ [bp+0]
+
+
+ public TrapHandler
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+TrapHandler proc far
+
+ pusha
+ push ds
+ push es
+ mov bp,sp
+ cld
+
+; No current way to fix #GP fault: print the GP fault message, and if
+; we have a debugging version dump the registers so that we can see what
+; happened.
+
+ cld
+ push SEL_DXDATA or STD_RING
+ pop ds
+ assume ds:DGROUP
+
+if DEBUG
+ DXcall TestDebugIns ;running under a debugger?
+else
+ cmp fDebug,0 ;running under a debugger?
+endif
+ jz noDebugger
+ jmp tpgp85 ;No, go trap out to debugger
+noDebugger:
+
+; We have an unrecoverable fault. Print an error message telling the
+; user what has happened, and then try to quit.
+
+ mov ax,3
+ int 10h
+
+ push ds
+ mov ax,cs
+ mov ds,ax
+ assume ds:NOTHING
+ mov dx,offset szFaultMessage
+ pmdossvc 9h
+
+ pop ds
+ assume ds:DGROUP
+
+; if DEBUG ;------------------------------------------------------------
+
+; A fault with no debugger! Dump out the registers at the time of the fault.
+
+ push ds
+ pop es
+
+ mov di,offset DGROUP:szRegDump+3
+ mov ax,regAX
+ call Hex2Asc
+ add di,4
+ mov ax,regBX ;How is this for brute force
+ call Hex2Asc ; programming? :-)
+ add di,4
+ mov ax,regCX
+ call Hex2Asc
+ add di,4
+ mov ax,regDX
+ call Hex2Asc
+ add di,4
+ mov ax,regSI
+ call Hex2Asc
+ add di,4
+ mov ax,regDI
+ call Hex2Asc
+ add di,4
+ mov ax,regBP
+ call Hex2Asc
+ add di,5
+ mov ax,regDS
+ call Hex2Asc
+ add di,4
+ mov ax,regES
+ call Hex2Asc
+ add di,4
+ mov ax,idError
+ call Hex2Asc
+ add di,4
+ mov ax,regCS
+ call Hex2Asc
+ add di,4
+ mov ax,regIP
+ call Hex2Asc
+ add di,4
+ mov ax,regSS
+ call Hex2Asc
+ add di,4
+ mov ax,regSP
+ call Hex2Asc
+
+ mov dx,offset DGROUP:szRegDump
+ pmdossvc 9h
+
+; endif ;DEBUG --------------------------------------------------------
+
+; To exit, we want to make sure that the Dos Extender's child is the
+; current program and then exit through DOS. This will take us out
+; through the normal dos extender termination code which will clean
+; up.
+
+ public PMAbort
+PMAbort:
+ mov ds,selDgroupPM
+
+ mov bx,selPSPChild
+ pmdossvc 50h ;set psp function
+
+ mov fFaultAbort,01 ;set aborting flag
+
+ mov al,0FFh ;error code of FF
+ pmdossvc 4Ch ;and quit
+
+ int 3 ;should never get here!
+
+tpgp85:
+
+; We can't fix the fault, so trap out to the debugger so we can look at it.
+
+ mov ax,DS_ForcedGO ;Wdeb386 command to set a breakpoint
+ mov cx,word ptr regCS
+
+ cmp idCpuType,3 ;need to pass 32 bit reg to debugger?
+ jae debug_386
+
+ push bx ; no, we're on a 286
+ mov bx,word ptr regIP
+ int DebOut_Int
+ pop bx
+ jmp short @f
+
+debug_386:
+ .386
+ push ebx ; yup... 32 bits it is
+ movzx ebx,word ptr regIP
+ int DebOut_Int
+ pop ebx
+
+@@:
+ .286p
+ pop es ;restore regs and restart faulting
+ pop ds ; instruction which will trap at the
+ assume ds:NOTHING,es:NOTHING ; wdeb386 breakpoint
+ popa
+
+ ret
+
+TrapHandler endp
+
+; -------------------------------------------------------
+; TrapInvalidOpcode
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public TrapInvalidOpcode
+
+TrapInvalidOpcode:
+
+ Trace_Out "Invalid Opcode Fault!"
+ jmp TrapHandler
+
+; -------------------------------------------------------
+; TrapDoubleFault
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public TrapDoubleFault
+
+TrapDoubleFault:
+
+
+ Trace_Out "Double Fault!"
+ jmp TrapHandler
+
+; -------------------------------------------------------
+; TrapExtensionOverrun
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public TrapExtensionOverrun
+
+TrapExtensionOverrun:
+ jmp TrapHandler
+
+; -------------------------------------------------------
+; TrapInvalidTSS
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public TrapInvalidTSS
+
+TrapInvalidTSS:
+
+ Trace_Out "Invalid TSS Fault!"
+ jmp TrapHandler
+
+; -------------------------------------------------------
+; TrapSegmentNotPresent
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public TrapSegmentNotPresent
+
+TrapSegmentNotPresent:
+
+ Trace_Out "Segment Not Present Fault!"
+ jmp TrapHandler
+
+; -------------------------------------------------------
+; TrapStackOverrun
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public TrapStackOverrun
+
+TrapStackOverrun:
+
+ Trace_Out "Stack Overrun Fault!"
+ jmp TrapHandler
+
+; -------------------------------------------------------
+; TrapPageFault
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public TrapPageFault
+
+TrapPageFault:
+
+ Trace_Out "Page Fault!"
+ jmp TrapHandler
+
+; -------------------------------------------------------
+; TrapGP -- This routine handles General Protection faults.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public TrapGP
+
+TrapGP:
+ Trace_Out "GENERAL PROTECTION VIOLATION!"
+ jmp TrapHandler
+
+; if DEBUG ;------------------------------------------------------------
+
+; -------------------------------------------------------
+; Hex2Asc -- convert AX to ascii and store at es:di
+;
+ assume ds:NOTHING,es:NOTHING
+
+Hex2Asc proc near
+
+ rol ax, 4
+ call Hex2AscCH
+ rol ax, 4
+ call Hex2AscCH
+ rol ax, 4
+ call Hex2AscCH
+ rol ax, 4
+ call Hex2AscCH
+ ret
+
+Hex2Asc endp
+
+
+DXDATA segment
+
+Hex_Char_Tab LABEL BYTE
+ db "0123456789ABCDEF"
+
+DXDATA ends
+
+ assume ds:DGROUP,es:NOTHING
+
+Hex2AscCH proc near
+
+ push ax
+ push bx
+ mov bx, ax
+ and bx, 1111b
+ mov al, Hex_Char_Tab[bx]
+ stosb
+ pop bx
+ pop ax
+ ret
+
+Hex2AscCH endp
+
+; endif ;DEBUG --------------------------------------------------------
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Hardware Interrupt Support Code
+ page
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+;
+; -------------------------------------------------------
+ subttl Utility Function Definitions
+ page
+; -------------------------------------------------------
+; UTILITY FUNCTION DEFINITIONS
+; -------------------------------------------------------
+;
+; SaveRMIntrVectors -- This routine copies the current
+; real mode interrupt vector table to the shadow
+; vector table used by the interrupt reflector.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses; all registers preserved
+;
+; NOTE: This routine can only be called in REAL MODE.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public SaveRMIntrVectors
+
+SaveRMIntrVectors:
+ push cx
+ push si
+ push di
+ push ds
+ push es
+;
+ cld
+ push ds
+ pop es
+ xor cx,cx
+ mov si,cx
+ mov ds,cx
+ mov di,offset DGROUP:rglpfnRmISR
+ mov cx,2*256
+ rep movs word ptr [di],word ptr [si]
+;
+ pop es
+ pop ds
+ pop di
+ pop si
+ pop cx
+ ret
+
+; -------------------------------------------------------
+; RestoreRMIntrVectors -- This routine copies the
+; interrupt vectors from the real mode interrupt
+; vector shadow table back down to the real interrupt
+; vectors.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses; all registers preserved
+;
+; NOTE: This routine can only be called in REAL MODE.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public RestoreRMIntrVectors
+
+RestoreRMIntrVectors:
+ push cx
+ push si
+ push di
+ push ds
+ push es
+;
+ FCLI
+ cld
+ xor cx,cx
+ mov di,cx
+ mov es,cx
+ mov si,offset DGROUP:rglpfnRmISR
+ mov cx,2*256
+ rep movs word ptr [di],word ptr [si]
+ FSTI
+;
+ pop es
+ pop ds
+ pop di
+ pop si
+ pop cx
+ ret
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+ public WowHwIntrEntryVector
+
+
+WowHwIntrEntryVector:
+ irp x,<8, 9, 0ah, 0bh, 0ch, 0dh, 0eh, 0fh, 070h, 071h, 072h, 073h, 074h, 075h, 076h, 077h>
+ push word ptr x
+ jmp [WowHwIntDispatchProc]
+ endm
+
+ifndef WOW
+;--------------------------------------------------------
+;
+; Wow16HwIntrReflector -- This routine switches
+; to the dosx stack and dispatches to an interrupt
+; handler. If we are already using the dosx stack,
+; no stack switch is performed.
+;
+; Input: ss:sp -> interrupt # (word)
+;
+ assume ds:nothing,es:nothing,ss:nothing
+ public Wow16HwIntrReflector
+Wow16HwIntrReflector proc near
+ ;
+ ; Get access to DXDATA, and point es:ebx at old stack
+ ;
+
+ push ds
+ push es
+ push bx
+ push di
+ mov di,SEL_DXDATA OR STD_RING
+ mov ds,di
+ assume ds:DGROUP
+ mov di,ss
+ mov es,di
+ mov bx,sp
+
+ ;
+ ; Switch stacks if necessary
+ ;
+ mov di,es
+
+;;; Removing these tests because hardware interrupts were recursing
+;;; and valid stack data was being overwritten because pbReflStack
+;;; was no longer an accurate reflection of the state of the stack. (neilsa)
+;;; cmp di,SEL_DXDATA OR STD_RING
+;;; je w16sti20
+
+ ASSERT_REFLSTK_OK
+ mov di,SEL_DXDATA OR STD_RING
+ mov ss,di
+ assume ss:DGROUP
+ mov sp,pbReflStack
+ sub pbReflStack,CB_STKFRAME
+
+ ;
+ ; Save app ss:sp
+ ;
+w16sti20:
+ push es
+ push bx
+
+ ;
+ ; Rebuild original iret frame (for things like win87em.dll
+ ;
+ push word ptr es:[bx + 14] ; iret frame flags
+ push word ptr es:[bx + 12] ; iret frame cs
+ push word ptr es:[bx + 10] ; iret frame ip
+
+ ;
+ ; Build iret frame to return to stack switcher
+ ;
+ pushf
+ push cs
+ push offset w16sti40
+
+ ;
+ ; Build a frame to call the interrupt handler
+ ;
+ mov di,word ptr es:[bx]+8
+
+ ; Note: this magic needs to change for use with other than HW ints
+ sub di,8
+ cmp di,8
+ jna w16sti30
+
+ sub di,070h - 16
+w16sti30:
+ shl di,3 ; di = di * 8 -> make di an index
+ push word ptr HwIntHandlers[di + 4] ; handler cs
+ push word ptr HwIntHandlers[di] ; handler ip
+
+ ;
+ ; restore ds, es, ebx, edi for int handler
+ ;
+ push word ptr es:[bx] ; di
+ push word ptr es:[bx + 2] ; bx
+ push word ptr es:[bx + 4] ; es
+ push word ptr es:[bx + 6] ; ds
+ pop ds
+ assume ds:nothing
+ pop es
+ pop bx
+ pop di
+
+ retf
+
+ ;
+ ; Back from interrupt handler
+ ;
+w16sti40:
+ add sp,6 ; remove extra iret frame
+ FCLI
+ push bp
+ mov bp,sp
+;;; cmp word ptr [bp + 4],SEL_DXDATA OR STD_RING ;removed (neilsa)
+;;; je w16sti50
+
+ ASSERT_REFLSTK_OK
+ add pbReflStack,CB_STKFRAME
+ ASSERT_REFLSTK_OK
+w16sti50:
+ push ax
+ push bx
+ push es
+ mov es,[bp + 4]
+ mov bx,[bp + 2]
+ mov ax,[bp - 2] ; ax
+ mov es:[bx],ax
+ mov ax,[bp - 4] ; bx
+ mov es:[bx + 2],ax
+ mov ax,[bp - 6] ; es
+ mov es:[bx + 4],ax
+ mov ax,[bp] ; bp
+ mov es:[bx + 6],ax
+ mov ax,es
+ mov ss,ax
+ mov sp,bx
+ assume ss:NOTHING
+ pop ax
+ pop bx
+ pop es
+ pop bp
+ add sp,2 ; remove stuff from stack
+ riret
+Wow16HwIntrReflector endp
+
+else ; wow
+
+;*****************************************************************************
+; The following two routines are the entry points for hardware interrupts.
+; The monitor or the kernel may or may not have explicitly switched to the
+; "locked" pm stack. These routines dispatch the interrupt, and then will
+; switch stacks back if it is finally "unwound" out of all nested interrupts.
+;*****************************************************************************
+
+;--------------------------------------------------------
+;
+; Wow16HwIntrReflector -- This routine switches
+; to the dosx stack and dispatches to an interrupt
+; handler. If we are already using the dosx stack,
+; no stack switch is performed.
+;
+; Input: ss:sp -> interrupt # (word)
+;
+ assume ds:nothing,es:nothing,ss:nothing
+ public Wow16HwIntrReflector
+Wow16HwIntrReflector proc near
+.386p
+ ASSERT_CLI
+ ;
+ ; Get access to DXDATA, and point es:ebx at old stack
+ ;
+
+ push ds
+ push es
+ push ebx
+ push eax
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ assume ds:DGROUP
+ mov ax,ss
+ mov es,ax
+ mov ebx,esp
+ lar eax,eax
+ test eax,(AB_BIG SHL 8)
+ jnz w16sti10
+
+ movzx ebx,bx
+w16sti10:
+ push es
+ push ebx
+
+ ;
+ ; Rebuild original iret frame (for things like win87em.dll
+ ;
+ push word ptr es:[ebx + 22] ; iret frame flags
+ push word ptr es:[ebx + 18] ; iret frame cs
+ push word ptr es:[ebx + 14] ; iret frame ip
+
+ ;
+ ; Build iret frame to return to stack switcher
+ ;
+ pushf
+ push cs
+ push offset w16hwi40
+
+ ;
+ ; Build a frame to call the interrupt handler
+ ;
+ movzx eax,word ptr es:[ebx]+12
+
+ DEBUG_TRACE DBGTR_HWINT, ax, 16h, 0
+ ; Note: this magic needs to change for use with other than HW ints
+ sub eax,8
+ cmp eax,8
+ jna w16hwi30
+ sub eax,070h - 16
+w16hwi30:
+
+ shl eax,3 ; ax = ax * 8 -> make ax an index
+ push word ptr HwIntHandlers[eax + 4] ; handler cs
+ push word ptr HwIntHandlers[eax] ; handler ip
+
+ ;
+ ; restore ds, es, ebx, eax for int handler
+ ;
+ push dword ptr es:[ebx] ; eax
+ push dword ptr es:[ebx + 4] ; ebx
+ push word ptr es:[ebx + 8] ; es
+ push word ptr es:[ebx + 10] ; ds
+ pop ds
+ assume ds:nothing
+ pop es
+ pop ebx
+ pop eax
+
+ retf
+
+ ;
+ ; Back from interrupt handler
+ ;
+w16hwi40:
+ add sp,6 ; remove extra iret frame
+ FCLI
+ DEBUG_TRACE DBGTR_EXIT, 0, 16h, 0
+ push bp
+ mov bp,sp
+ push eax
+ push ebx
+ push es
+
+ mov ax, SEL_VDMTIB or STD_RING
+ mov es, ax
+ dec word ptr es:[VDMTIB_LockCount]
+ jnz short w16hwi60
+
+ mov ss, es:[VDMTIB_SaveSsSelector]
+ mov esp, es:[VDMTIB_SaveEsp]
+ assume ss:NOTHING
+
+ mov ax, SEL_DXDATA OR STD_RING ;BUGBUG pick it off the stack?
+ mov es, ax
+
+ mov eax, es:[bp+8]
+ mov ebx, es:[bp+12]
+
+ push dword ptr es:[bp+30] ;flags
+ push dword ptr es:[bp+26] ;cs
+ push dword ptr es:[bp+22] ;eip
+ push word ptr es:[bp]
+ push word ptr es:[bp+16]
+ push word ptr es:[bp+18]
+
+ pop ds
+ pop es
+ pop bp
+ jmp short w16hwi70
+w16hwi60:
+ pop es
+ pop ebx
+ pop eax
+ pop bp
+ add esp, 20
+w16hwi70:
+
+ riretd
+Wow16HwIntrReflector endp
+
+
+
+;--------------------------------------------------------
+;
+; Wow32HwIntrReflector -- This routine switches
+; to the dosx stack and dispatches to an interrupt
+; handler. If we are already using the dosx stack,
+; no stack switch is performed.
+;
+; Input: ss:sp -> interrupt # (word)
+;
+ assume ds:nothing,es:nothing,ss:nothing
+ public Wow32HwIntrReflector
+Wow32HwIntrReflector proc near
+
+ ;
+ ; Get access to DXDATA, and point es:ebx at old stack
+ ;
+
+ push ds
+ push es
+ push ebx
+ push eax
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ assume ds:DGROUP
+ mov ax,ss
+ mov es,ax
+ mov ebx,esp
+ lar eax,eax
+ test eax,(AB_BIG SHL 8)
+ jnz w32hwi10
+
+ movzx ebx,bx
+w32hwi10:
+ push es
+ push ebx
+
+ ;
+ ; Rebuild original iret frame (for things like win87em.dll
+ ;
+ push dword ptr es:[ebx + 22] ; iret frame flags
+ push dword ptr es:[ebx + 18] ; iret frame cs
+ push dword ptr es:[ebx + 14] ; iret frame ip
+
+ ;
+ ; Build iret frame to return to stack switcher
+ ;
+ pushfd
+ push 0
+ push cs
+ push 0
+ push offset w32hwi40
+
+ ;
+ ; Build a frame to call the interrupt handler
+ ;
+ movzx eax,es:[ebx]+12
+
+ DEBUG_TRACE DBGTR_HWINT, ax, 32h, 0
+ ; Note: this magic needs to change for use with other than HW ints
+ sub eax,8
+ cmp eax,8
+ jna w32hwi30
+
+ sub eax,070h - 16
+w32hwi30:
+ shl eax,3 ; ax = ax * 8 -> make ax an index
+ push dword ptr HwIntHandlers[eax + 4] ; handler cs
+ push dword ptr HwIntHandlers[eax] ; handler ip
+
+ ;
+ ; restore ds, es, ebx, eax for int handler
+ ;
+ push dword ptr es:[ebx] ; eax
+ push dword ptr es:[ebx + 4] ; ebx
+ push word ptr es:[ebx + 8] ; es
+ push word ptr es:[ebx + 10] ; ds
+ pop ds
+ assume ds:nothing
+ pop es
+ pop ebx
+ pop eax
+
+ db 066h
+ retf ; far 32 bit retf
+
+ ;
+ ; Back from interrupt handler
+ ;
+w32hwi40:
+ add sp,12 ; remove additional iret frame
+ FCLI
+ DEBUG_TRACE DBGTR_EXIT, 0, 32h, 0
+ push bp
+ mov bp,sp
+ push eax
+ push ebx
+ push es
+
+ mov ax, SEL_VDMTIB or STD_RING
+ mov es, ax
+ dec word ptr es:[0] ;BUGBUG LockCount
+ jnz short w32hwi60
+
+ mov ss, es:[4] ;BUGBUG SaveSsSelector
+ mov esp, es:[10] ;BUGBUG SaveEsp
+ assume ss:NOTHING
+
+ mov ax, SEL_DXDATA OR STD_RING ;BUGBUG pick it off the stack?
+ mov es, ax
+
+ mov eax, es:[bp+8]
+ mov ebx, es:[bp+12]
+
+ push dword ptr es:[bp+30] ;flags
+ push dword ptr es:[bp+26] ;cs
+ push dword ptr es:[bp+22] ;eip
+ push word ptr es:[bp]
+ push word ptr es:[bp+16]
+ push word ptr es:[bp+18]
+
+ pop ds
+ pop es
+ pop bp
+ jmp short w32hwi70
+w32hwi60:
+ pop es
+ pop ebx
+ pop eax
+ pop bp
+ add esp, 20
+w32hwi70:
+ push ebx
+ mov bx,ss
+ lar ebx,ebx
+ test ebx,(AB_BIG SHL 8)
+ jz w32hwi90
+
+ pop ebx
+ riretd32
+
+w32hwi90:
+ pop ebx
+ riretd
+Wow32HwIntrReflector endp
+
+.286p
+
+;--------------------------------------------------------
+;
+; Wow16TransitionToUserMode -- This routine simulates a
+; ring transition from the kernelmode dos extender
+; code to the usermode dos extender code. It does this
+; by restoring the user regs from the dosx stack, restoring
+; user bp from user stack, and retf
+;
+; Inputs: ss:sp -> user ds
+; user ax
+; user bx
+; user cx
+; user sp
+; user ss
+; user ss:sp -> user bp
+; user ip
+; user cs
+; Outputs: none
+;
+
+ assume ds:nothing,es:nothing,ss:DGROUP
+ public Wow16TransitionToUserMode
+Wow16TransitionToUserMode proc
+
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+ mov bp,sp
+.386p
+ lss sp,[bp]
+.286p
+ pop bp
+ retf
+
+Wow16TransitionToUserMode endp
+
+;--------------------------------------------------------
+;
+; wow32TransitionToUserMode -- This routine simulates a
+; ring transition from the kernelmode dos extender
+; code to the usermode dos extender code. It does this
+; by restoring the user regs from the dosx stack, restoring
+; user bp from user stack, and retf
+;
+; Inputs: ss:sp -> user ds
+; user ax
+; user bx
+; user cx
+; user esp
+; user ss
+; user ss:sp -> user bp
+; user eip
+; user cs
+; Outputs: none
+;
+
+ assume ds:nothing,es:nothing,ss:DGROUP
+ public wow32TransitionToUserMode
+wow32TransitionToUserMode proc
+
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+ mov bp,sp
+.386p
+ lss esp,[bp]
+.286p
+ pop bp
+ db 066h ; operand override
+ retf
+
+wow32TransitionToUserMode endp
+
+;--------------------------------------------------------
+;
+; Wow16CopyEhStack -- build a frame on the user stack
+; for dispatching an exception.
+;
+; Inputs: ds:bx -> exception handling stack
+; Outputs: stack frames built on eh and dosx stack
+;
+;
+ assume ds:nothing,es:nothing,ss:DGROUP
+ public Wow16CopyEhStack
+Wow16CopyEhStack proc
+
+ push si
+
+ mov cx,6
+ lea si,Ring0_EH_SS
+ces1610:
+ sub bx,2
+ mov ax,word ptr ss:[si]
+ mov word ptr [bx],ax
+ sub si,4
+ loop ces1610
+
+;
+; Put address of fault handler clean up routine on stack
+;
+ sub bx,2
+ mov word ptr [bx],SEL_DXPMCODE or STD_RING
+ sub bx,2
+ mov word ptr [bx],offset DXPMCODE:Wow16FaultCleanup
+;
+; Get return addr to exception header (used to figure out which exception)
+;
+ add si,2
+ mov ax,word ptr ss:[si]
+;
+; Put address of fault reflector on user stack
+;
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov cl,3
+ div cl
+ shl ax,3
+ lea bp,PmFaultVector
+ add bp,ax
+ mov ax,word ptr [bp + 4]
+ sub bx,2
+ mov word ptr [bx],ax
+ sub bx,2
+ mov ax,word ptr [bp]
+ mov [bx],ax
+;
+; Put user bp on user stack
+;
+ mov ax,Ring0_EH_CX
+ sub bx,2
+ mov [bx],ax
+
+;
+; Push ss:sp onto current stack
+;
+ mov ax,ds
+ mov Ring0_EH_BP,ax ; ss
+ mov Ring0_EH_CX,bx ; sp
+
+ pop si
+ ret
+Wow16CopyEhStack endp
+
+;--------------------------------------------------------
+;
+; Wow16FaultCleanup -- Removes the fault handler frame from
+; the stack, and continues.
+;
+; Inputs: None
+; Outputs: fault handler stack frame removed.
+;
+;
+ assume ds:nothing,es:nothing,ss:nothing
+ public Wow16FaultCleanup
+Wow16FaultCleanup proc
+.386p
+ FCLI
+ add sp,2 ; pop error code
+ push bp
+ mov bp,sp
+ push bx
+ push ds
+ push ax
+
+ mov ax,SEL_DXDATA or STD_RING ; Free EH stacklet
+ mov ds, ax
+ add ds:npEHStacklet,CB_STKFRAME
+
+ mov ax,[bp + 10]
+ mov ds,ax
+ mov bx,[bp + 8] ; ds:bx -> user stack
+ sub bx,2
+ mov ax,[bp + 6]
+ mov ds:[bx],ax ; push flags
+ sub bx,2
+ mov ax,[bp + 4]
+ mov ds:[bx],ax ; push cs
+ sub bx,2
+ mov ax,[bp + 2]
+ mov ds:[bx],ax ; push ip
+ sub bx,2
+ mov ax,[bp]
+ mov ds:[bx],ax ; push bp
+ mov [bp + 8],bx
+ pop ax
+ pop ds
+ pop bx
+ pop bp
+
+ add sp,6 ; point to ss:sp
+ mov bp,sp
+
+ lss sp,[bp]
+.286p
+ pop bp ; restore bp
+ riret
+Wow16FaultCleanup endp
+
+
+;--------------------------------------------------------
+;
+; Wow32CopyEhStack -- build a frame on the user stack
+; for dispatching an exception.
+;
+; Inputs: ds:bx -> exception handling stack
+; Outputs: stack frames built on eh and dosx stack
+;
+;
+ assume ds:nothing,es:nothing,ss:DGROUP
+ public Wow32CopyEhStack
+Wow32CopyEhStack proc
+.386p
+ push si
+ push eax
+
+ mov cx,6
+ lea si,Ring0_EH_SS
+ces3210:
+ sub bx,4
+ mov eax,dword ptr ss:[si]
+ mov dword ptr [bx],eax
+ sub si,4
+ loop ces3210
+
+;
+; Put address of fault handler clean up routine on stack
+;
+ sub bx,4
+ mov dword ptr [bx],SEL_DXPMCODE or STD_RING
+ sub bx,4
+ mov dword ptr [bx],offset DXPMCODE:Wow32FaultCleanup
+
+;
+; Get return addr to exception header (used to figure out which exception)
+;
+ mov ax,word ptr ss:[si + 2]
+
+;
+; Put address of fault reflector on user stack
+;
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov cl,3
+ div cl
+ shl ax,3
+ lea bp,PmFaultVector
+ add bp,ax
+ mov ax,word ptr [bp + 4]
+ sub bx,4
+ mov word ptr [bx],ax
+ sub bx,4
+ mov eax,dword ptr [bp]
+ mov [bx],eax
+
+;
+; Put user bp on user stack
+;
+ mov ax,Ring0_EH_CX
+ sub bx,2
+ mov [bx],ax
+
+;
+; Stick user ss:esp on our stack
+;
+ mov ax,ds
+ mov Ring0_EH_PEC,ax
+ mov Ring0_EH_BP,0 ; high half esp
+ mov Ring0_EH_CX,bx
+
+ pop eax
+ pop si
+ ret
+.286p
+Wow32CopyEhStack endp
+
+;--------------------------------------------------------
+;
+; wow32FaultCleanup -- Removes the fault handler frame from
+; the stack, and continues.
+;
+; Inputs: None
+; Outputs: fault handler stack frame removed.
+;
+;
+ assume ds:nothing,es:nothing,ss:nothing
+ public Wow32FaultCleanup
+wow32FaultCleanup proc
+.386p
+ FCLI
+ add esp,4 ; pop error code
+ push ebp
+ mov ebp,esp
+ push ebx
+ push ds
+ push eax
+
+ mov ax,SEL_DXDATA or STD_RING ; Free EH stacklet
+ mov ds, ax
+ add ds:npEHStacklet,CB_STKFRAME
+
+ movzx eax,word ptr [ebp + 20]
+ mov ds,ax
+ mov ebx,[ebp + 16] ; ds:bx -> user stack
+ lar eax,eax
+ test eax,(AB_BIG SHL 8)
+ jnz w32fc10
+
+ movzx ebx,bx ; high half should be zero
+w32fc10:
+ sub ebx,4
+ mov eax,[ebp + 12]
+ mov ds:[ebx],eax ; push flags
+ sub ebx,4
+ mov eax,[ebp + 8]
+ mov ds:[ebx],eax ; push cs
+ sub ebx,4
+ mov eax,[ebp + 4]
+ mov ds:[ebx],eax ; push ip
+ sub ebx,4
+ mov eax,[ebp]
+ mov ds:[ebx],eax ; push bp
+ mov [ebp + 16],ebx ; update esp
+ pop eax
+ pop ds
+ pop ebx
+ pop ebp
+
+ add esp,12 ; point to ss:sp
+ mov ebp,esp
+
+ lss esp,[ebp]
+ pop ebp ; restore bp
+ push ebx
+ mov bx,ss
+ lar ebx,ebx
+ test ebx,(AB_BIG SHL 8)
+ jz w32fc20
+
+ pop ebx
+ riretd32
+
+w32fc20:
+ pop ebx
+ riretd
+.286p
+wow32FaultCleanup endp
+
+;--------------------------------------------------------
+;
+; Wow16CopyIretStack -- build a frame on the user stack
+; for dispatching an exception.
+;
+; Inputs: none
+; Outputs: stack frames built on eh and dosx stack
+;
+;
+ assume ds:nothing,es:nothing,ss:DGROUP
+ public Wow16CopyIretStack
+Wow16CopyIretStack proc
+
+ push si
+ mov ax,Ring0_EH_SS
+ mov ds,ax
+ mov bx,Ring0_EH_SP
+ lea si,Ring0_EH_Flags
+ mov cx,3
+cis1610:
+ sub bx,2
+ mov ax,ss:[si]
+ mov [bx],ax
+ sub si,4
+ loop cis1610
+
+;
+; Get the address of the interrupt handler
+;
+ mov ax,Ring0_EH_PEC
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov cl,3
+ div cl ; AX = interrupt number
+ shl ax,3 ; AX = vector entry offset
+ push es
+ push SEL_IDT OR STD_RING
+ pop es
+ mov si,ax ; BP -> interrupt handler address
+ mov ax,es:[si].offDest ; AX = IP of handler
+ mov cx,es:[si].selDest ; CX = CS of handler
+ pop es
+ sub bx,2
+ mov [bx],cx ; "push" ip
+ sub bx,2
+ mov [bx],ax ; "push" cs
+;
+; Push user bp on user stack
+;
+ mov ax,Ring0_EH_BP
+ sub bx,2
+ mov [bx],ax
+;
+; Push ss:sp onto current stack
+;
+ mov ax,ds
+ mov Ring0_EH_PEC,ax ; ss
+ mov Ring0_EH_BP,bx ; sp
+
+ pop si
+ ret
+Wow16CopyIretStack endp
+
+;--------------------------------------------------------
+;
+; wow32copyiretStack -- build a frame on the user stack
+; for dispatching an exception.
+;
+; Inputs: none
+; Outputs: stack frames built on eh and dosx stack
+;
+;
+ assume ds:nothing,es:nothing,ss:DGROUP
+ public wow32copyiretStack
+wow32copyiretStack proc
+.386p
+ push si
+ push ebx
+ push eax
+ movzx eax,word ptr Ring0_EH_SS
+ mov ds,ax
+ mov ebx,dword ptr Ring0_EH_SP
+ lar eax,eax
+ test eax,(AB_BIG SHL 8)
+ jnz cis3205
+
+ movzx ebx,bx
+cis3205:
+ lea si,Ring0_EH_Flags
+ mov cx,3
+cis3210:
+ sub ebx,4
+ mov eax,dword ptr ss:[si]
+ mov [ebx],eax
+ sub si,4
+ loop cis3210
+
+;
+; Get the address of the interrupt handler
+;
+ mov ax,Ring0_EH_PEC
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov cl,3
+ div cl ; AX = interrupt number
+ shl ax,3 ; AX = vector entry offset
+ push es
+ push SEL_IDT OR STD_RING
+ pop es
+ mov si,ax ; BP -> interrupt handler address
+ mov ax,es:[si].rsvdGate ; AX = IP of handler
+ shl eax,16
+ mov ax,es:[si].offDest
+ mov cx,es:[si].selDest ; CX = CS of handler
+ pop es
+ sub ebx,4
+ mov [ebx],cx ; "push" cs
+ sub ebx,4
+ mov [ebx],eax ; "push" ip
+;
+; Push user bp on user stack
+;
+ mov ax,Ring0_EH_BP
+ sub ebx,2
+ mov [ebx],ax
+;
+; Push ss:sp onto current stack
+;
+ mov ax,ds
+ mov Ring0_EH_EC,ax
+ mov dword ptr Ring0_EH_BP,ebx
+
+ pop eax
+ pop ebx
+ pop si
+ ret
+.286p
+wow32copyiretStack endp
+
+
+;--------------------------------------------------------
+;
+; Wow32ReservedReflector -- This routine removes the fault
+; frame from the DPMI stack, and reflects to the
+; appropriate interrupt handler on the original stack
+;
+; Inputs: none
+; Outputs: none
+;
+ assume ds:nothing, es:nothing, ss:nothing
+ public Wow32ReservedReflector
+Wow32ReservedReflector proc
+.386p
+ push ds
+ push ebx
+ push eax
+ push edi
+ push ebp
+ mov ebp,esp
+
+;
+; Copy stack frame.
+; N.B. If the interrupt occurred when we were already on the dos extender
+; stack, the copy will be overlapping.
+;
+ movzx eax,word ptr [ebp + 48] ; get ss
+ mov ds,ax
+ mov ebx,[ebp + 44] ; get esp
+ lar eax,eax
+ test eax,(AB_BIG SHL 8)
+ jnz w32rr10
+
+ movzx ebx,bx
+w32rr10:
+ sub ebx,24 ; room for frame + ebp + far ret addr
+ mov eax,[ebp + 40] ; EFlags
+ mov [ebx + 20],eax ; push eflags
+ mov eax,[ebp + 36] ; CS
+ mov [ebx + 16],eax ; push cs
+ mov eax,[ebp + 32] ; Eip
+ mov [ebx + 12],eax ; push Eip
+ mov eax,[ebp] ; ebp
+ mov [ebx],eax ; push ebp
+ mov [ebp + 20],ebx ; save esp for lss esp
+ mov [ebp + 24],ds ; save ss for lss esp
+;
+; Get the address of the interrupt handler
+;
+ mov ax,selDGroupPM
+ mov es,ax
+ assume es:DGROUP
+ mov ax,[ebp + 18]
+ sub ax,offset DXPMCODE:PMReservedEntryVector + 5
+ mov dl,5
+ div dl
+ shl ax,3
+ push es
+ push SEL_IDT OR STD_RING
+ pop es
+ mov di,ax
+ mov ax,es:[di].rsvdGate
+ shl eax,16
+ mov ax,es:[di].offDest
+ mov [ebx + 4],eax
+ movzx eax,word ptr es:[di].selDest
+ mov [ebx + 8],eax
+ pop es
+;
+; Restore Registers, switch stacks, and return
+;
+ add esp,4 ; ebp comes off user stack
+ pop edi
+ pop eax
+ pop ebx
+ pop ds
+ assume ds:nothing
+ lss esp,[ebp + 20]
+ pop ebp
+
+ db 066h ; 48 bit far return
+ retf
+.286p
+Wow32ReservedReflector endp
+
+ public Wow32IntrRefl
+Wow32IntrRefl label word
+??intnum = 0
+rept 256
+ push word ptr ??intnum
+ jmp Wow32Intr16Reflector
+ ??intnum = ??intnum + 1
+endm
+;--------------------------------------------------------
+;
+; Wow32Intr16Reflector -- This routine reflects a 32 bit
+; interrupt to a 16 bit handler. It switches to the
+; dos extender stack to do so.
+;
+; Inputs: none
+; Outputs: none
+;
+ assume ds:nothing,es:nothing,ss:nothing
+ public Wow32Intr16Reflector
+Wow32Intr16Reflector proc
+.386p
+ push ebp
+ mov ebp,esp
+ push ds
+ push eax
+ push ebx
+ push edi
+ mov ax,ss
+ movzx eax,ax
+ lar eax,eax
+ test eax,(AB_BIG SHL 8)
+ jnz w32i16r10
+
+ movzx ebp,bp
+w32i16r10:
+
+;
+; Get a frame on the dosx stack.
+;
+ mov ax,selDgroupPM
+ mov ds,ax
+ assume ds:DGROUP
+
+ movzx ebx,pbReflStack
+ sub pbReflStack,CB_STKFRAME
+
+;
+; Build a frame on the stack
+;
+ sub bx,30
+ mov eax, [ebp+6] ; eip
+ mov [bx+20], eax
+ mov eax, [ebp+10] ; cs
+ mov [bx+24], eax
+
+ mov [bx + 18],ss ; ss for stack switch back
+ mov eax,ebp
+ add eax,6 ; ebp, int number
+ mov [bx + 14],eax ; esp for stack switch back
+ mov ax,[ebp + 14] ; get flags
+ mov [bx + 12],ax
+ mov ax,cs
+ mov [bx + 10],ax
+ mov [bx + 8],offset DXPMCODE:w3216r30
+ mov eax,[ebp]
+ mov [bx],eax ; put ebp on other stack for pop
+;
+; Get handler
+;
+ mov di,[ebp + 4] ; int number
+ shl di,2 ; al * 4
+ add di,offset DGROUP:Wow16BitHandlers
+ mov ax,[di]
+ mov [bx + 4],ax ; handler ip
+ mov ax,[di + 2]
+ mov [bx + 6],ax ; handler cs
+
+;
+; Set up for stack switch
+;
+ push ds
+ push ebx
+;
+; Restore registers
+;
+ mov ax,[ebp - 2]
+ mov ds,ax
+ mov eax,[ebp - 6]
+ mov ebx,[ebp - 10]
+ mov edi,[ebp - 14]
+;
+; Switch stacks, restore ebp, and call handler
+;
+ lss esp,[ebp - 20]
+ pop ebp
+ DEBUG_TRACE DBGTR_ENTRY, 0, 0, 2000h
+ retf
+;
+; N.B. i31_RMCall looks on the stack to get the original user stack pointer.
+; if you change the stack frame the is passed to the 16 bit int
+; handlers, that WILL break.
+;
+
+w3216r30:
+ DEBUG_TRACE DBGTR_EXIT, 0, 0, 2000h
+;
+; Switch stacks, deallocate frame from dosx stack and return
+;
+ push ebx
+ push eax
+ push ds
+ lds ebx,[esp+10] ;get ss:esp
+ mov eax,[esp+16]
+ mov [ebx],eax ;eip
+ mov eax,[esp+20]
+ mov [ebx+4],eax ;cs
+ pop ds
+ pop eax
+ pop ebx
+
+ lss esp,[esp]
+ push ebx
+
+
+ pushfd
+ push eax
+ mov ax,ss
+ movzx eax,ax
+ lar eax,eax
+ test eax,(AB_BIG SHL 8) ; is the stack big?
+ jnz w32i16r40 ; jif yes, use 32bit operations
+ pop eax ; restore regs
+ popfd
+
+ rpushfd ; save flags, set virtual int bit
+ pop ebx
+ push ebp
+ movzx ebp, sp
+ mov [ebp + 16],ebx ; put flags on iret frame
+ pop ebp
+ push ds
+ mov bx,selDgroupPM
+ mov ds,bx
+ add pbReflStack,CB_STKFRAME
+ pop ds
+ pop ebx
+ riretd
+
+w32i16r40: ; stack is big
+ pop eax ; restore regs
+ popfd
+
+ rpushfd32
+ pop ebx
+ mov [esp + 12],ebx
+ push ds
+ mov bx,selDgroupPM
+ mov ds,bx
+ add pbReflStack,CB_STKFRAME
+ pop ds
+ pop ebx
+ riretd32
+
+.286p
+Wow32Intr16Reflector endp
+ENDIF
+DXPMCODE ends
+;
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxmain.asm b/private/mvdm/dpmi/dxmain.asm
new file mode 100644
index 000000000..777b4302d
--- /dev/null
+++ b/private/mvdm/dpmi/dxmain.asm
@@ -0,0 +1,3376 @@
+ PAGE ,132
+ TITLE DXMAIN.ASM -- Main Module for Dos Extender
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXMAIN.ASM - Dos Extender Main Module *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains the main routines for the Dos *
+;* Extender. This is based on code written for Microsoft *
+;* by Murray Sargent of Scroll Systems from Tucson Arizona. *
+;* *
+;* The Dos Extender provides support to allows specially *
+;* written programs to run in protected mode mode on the *
+;* 80286 and 80386 under MS-DOS. The following areas of *
+;* support are provided to accomplish this: *
+;* *
+;* Program Loading and Initialization *
+;* This involves creating a program segment prefix and *
+;* then loading and relocating the exe file. When *
+;* loading an exe for protected mode operation, it is *
+;* necessary to create segment descriptors for all *
+;* segments used by the program and to then substitute *
+;* the corresponding selectors when fixing up the segment *
+;* references in the code. *
+;* *
+;* Dos Function Call Support *
+;* Since Dos must execute in real mode, it is necessary *
+;* to perform mode switching into real mode and the back *
+;* to protected mode when the application makes Dos calls. *
+;* Also, any far pointers that are parameters to the *
+;* function must be converted from the selector:offset *
+;* form that the application uses to a segment:offset form *
+;* that Dos can use, with the corresponding data being *
+;* buffered from the application's extended memory address *
+;* space to Dos's real mode address space. *
+;* *
+;* Other Interrupt Support *
+;* Hardware interrupts are processed in real mode, and *
+;* so the Dos Extender performs mode switching on each *
+;* interrupt. Also other system resources (such as the *
+;* mouse driver and the bios) are entered through software *
+;* interrupts, and require the same kind of buffering *
+;* and parameter translation that the Dos functions *
+;* require. *
+;* *
+;* Extended Memory Management *
+;* The protected mode application has access to the full *
+;* address space of the machine, and a memory manager is *
+;* provided that duplicates the functions of the Dos *
+;* memory manager over the entire address space of the *
+;* machine. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 08/08/90 earleh DOSX and Client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 03/23/90 davidw Added the reflecting of it 23h, ^C. *
+;* 11/09/89 jimmat Added more IOCTL 0Dh support for Windows. *
+;* 10/11/89 jimmat Changed hooking of Int 1,2,3 under a *
+;* debugger to work better with CVW. *
+;* 07/28/89 jimmat Fixed Int 21h/56h (Rename), fixed Int 21 *
+;* calls that just returned a pointer. *
+;* 06/07/89 jimmat Fixed length of FCB moves and special *
+;* case hooking Int 1Eh. *
+;* 05/30/89 jimmat Completed Int 21h/5Ah processing. *
+;* 04/25/89 jimmat Added support for undocumented INT 21h *
+;* 5Fh/05 DOS call. *
+;* 04/12/89 jimmat Allow one level of nested DOS calls to *
+;* support PM critical error handlers *
+;* 04/10/89 jimmat Supported INT 21h/5Eh & 5Fh--also small *
+;* clean-up of the dosentry/dosexit code. *
+;* 04/05/89 jimmat Fixed MOVDAT FCB length check. *
+;* 04/04/89 jimmat Stop reflecting real mode software ints *
+;* to protect mode. This is how Windows/386 *
+;* works, and it fixes a problem with DOS *
+;* networks since the real mode redirector *
+;* expects to have access to the DOS stack, *
+;* not a DOS extender interrupt stack frame. *
+;* 03/28/89 jimmat Incorporated bug fixes from GeneA *
+;* 03/17/89 jimmat Some code clean-up and debugging checks *
+;* 03/15/89 jimmat Minor changes to run child in ring 1 *
+;* 02/22/89 (GeneA): removed dead code and data left over *
+;* from the Murray Sargent/SST days. *
+;* 02/22/89 (GeneA): moved handlers for all interrupts but *
+;* Int 21h to DXINTR.ASM. Fixed problem with re-entrancy *
+;* caused when the other interrupts were executed while *
+;* in DOS. (int 15h was causing the specific problem). *
+;* 02/14/89 (GeneA): fixed bug in IntExitMisc. Was storing *
+;* return value in rmrg.xes, changed to pmrg.xes. *
+;* 02/10/89 (GeneA): changed Dos Extender from small model to *
+;* medium model. *
+;* 12/01/88 (GeneA): Changed name of mentry and mexit to *
+;* IntEntryMouse and IntExitMouse. Added functions *
+;* IntEntryMisc and IntExitMisc to handle entry and *
+;* exit processing for BIOS INT 15h. *
+;* 11/20/88 (GeneA): modified DFSetVector so that is checks to *
+;* see if a vector has already been hooked before saving *
+;* the old value in the real mode interrupt vector shadow *
+;* buffer. *
+;* 09/15/88 (GeneA): created by extracting code from the *
+;* SST debugger modules DOSXTND.ASM, VIRTMD.ASM, *
+;* VRTUTIL.ASM, and INTERRPT.ASM *
+;* *
+;****************************************************************
+
+ .286p
+ .287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+IFDEF WOW_x86
+include bop.inc
+include dpmi.inc
+ENDIF
+include hostdata.inc
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+; STKSTR -- stack layout structure for user registers in the pmrg and
+; rmrg arrays in the data segment DXDATA. pmrg is an exact replica of the pm
+; user registers on entry to the Dos Extender (DE). rmrg is a translated
+; version used to communicate with the real-mode world. The rmrg array is
+; initially set equal to the pm user values by the instructions push ds, push
+; es, pusha. The pmrg array es and ds are inevitably set equal to the pm user
+; values and its general register values are defined if data translations may be
+; required (int-10/21).
+
+stkstr struc ;Level-0 Stack structure. bp is set = sp here
+xdi dw ?
+xsi dw ?
+xbp dw ?
+xasp dw ? ;Alternate sp
+xbx dw ?
+xdx dw ?
+xcx dw ?
+xax dw ? ;pusha pushes xax to xdi
+xes dw ?
+xds dw ?
+stkstr ends
+
+
+; -------------------------------------------------------
+; This structure describes the EXEC parameter block used
+; by MS-DOS function 4Bh.
+
+execblk struc ;INT-21 ah=4bh EXEC parameter block
+evrnmt dw ? ;Paragraph of environment string to be passed
+cmdptr dd ? ;Ptr to command line to be placed at PSP+80h
+fcb1ptr dd ? ;Ptr to default FCB to be passed at PSP+5ch
+fcb2ptr dd ? ;Ptr to default FCB to be passed at PSP+6ch
+xsssp dd ? ;Initial program stack ss:sp
+xcsip dd ? ;Program entry point cs:ip
+execblk ends
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn gtpara:NEAR
+ extrn RMIntr24:NEAR
+ extrn ParaToLinear:NEAR
+ extrn GetSegmentAddress:NEAR
+ extrn RMIntrEntryVector:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn SelOff2SegOff:NEAR
+ extrn ParaToLDTSelector:NEAR
+ extrn MouseInterruptHandler:NEAR
+ extrn GetIntrVector:NEAR, PutIntrVector:NEAR
+ extrn AllocateXmemBlock:NEAR, FreeXmemBlock:NEAR, ModifyXmemBlock:NEAR
+ifdef WOW_x86
+ extrn NSetSegmentDscr:FAR
+endif
+ extrn ChildTerminationHandler:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn fDebug:BYTE
+ extrn selGDT:WORD
+ extrn selIDT:WORD
+ extrn idCpuType:WORD
+ extrn selDOSScr:WORD
+ extrn segPSPChild:WORD
+ extrn rglpfnRmISR:DWORD
+ extrn RmHwIsr:DWORD
+ extrn PMIntelVector:DWORD
+ extrn PMInt24Handler:DWORD
+ extrn lpfnUserMouseHandler:DWORD
+ extrn npXfrBuf0:WORD, npXfrBuf1:WORD
+ extrn rgbXfrBuf0:BYTE, rgbXfrBuf1:BYTE
+IFDEF WOW_x86
+ extrn FastBop:FWORD
+ENDIF
+
+IFDEF ROM
+ extrn segDXData:WORD
+ENDIF
+
+ extrn segDXCode:word
+ extrn segCurrentHostData:word
+; -------------------------------------------------------
+; General DOS EXTENDER Variables
+; -------------------------------------------------------
+
+pmdta dw 2 dup(?) ;PM DTA. Used for getting dir info
+
+EntryFlag db -1 ;Flag to check for nested DOS interrupts
+
+rcount dw ? ;Remaining requested file byte count to read/write
+ccount dw ? ;Current count of total read/written
+cbSector dw ? ;sector size for IOCTL/0D/41&61 transfers
+lpTrackData dd ? ;pointer to track data for IOCTL/0D/41&61 transfers
+
+;=======================================================================
+;Keep from here thru Iend in the following order:
+
+ align 2
+Ibegin label byte ;start of PMIntrDos nested 'Instance' data
+
+ dw 128 dup(?) ;Extra stack for real mode
+
+rmrg stkstr <> ;Corresponding real-mode set
+rmivip dw ? ;Real-mode interrupt-vector offset
+rmivcs dw ? ;Real-mode interrupt-vector segment
+rmivfl dw ? ;Real-mode interrupt flags to be used
+rmroff dw ? ;Real-mode return address offset
+rmrseg dw ? ;Real-mode return address segment
+rmflags dw ? ;flags
+
+pmrg stkstr <> ;Protected-mode user registers
+
+ public pmusrss, pmusrsp
+
+pmusrsp dw ? ;PM user sp
+pmusrss dw ? ;PM user ss
+
+enxseg dw ? ;transfer segment used on dos function exit
+
+Iend label byte ;end of PMIntrDos nested 'Instance' data
+;=======================================================================
+
+ILENGTH equ Iend - Ibegin ;length of instance data
+
+Isave db ILENGTH dup (?) ;instance data save area
+
+if DEBUG
+ extrn fTraceDOS:WORD
+ extrn TrapDOS:WORD
+endif
+
+DXDATA ends
+
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn segDXData:WORD
+ extrn selDgroup:WORD
+ENDIF
+
+DXCODE ends
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+ extrn segDXCodePM:WORD
+
+IFNDEF ROM
+ extrn segDXDataPM:WORD
+ENDIF
+
+; -------------------------------------------------------
+; Dos Function Parameter Tables
+; -------------------------------------------------------
+
+; The Dos Extender provides parameter buffering and translation
+; on entry to and exit from Dos system calls. The following table
+; is used to describe the operations to be performed for this process.
+; The basic idea is that there is an entry parameter code and an
+; exit paramter code. This code describes whether any data buffering
+; is necessary. If buffering is necessary, it describes the nature of
+; the data being transferred (e.g. ASCIIZ string, FCB, etc.) which is
+; used to determine the transfer length. The entry/exit code also
+; describes which temporary buffer to use for the transfer, and which
+; user register (e.g. DS:DX, ES:BX, etc) points to the data to transfer.
+;
+; The data transfers described by this table is sufficient to handle
+; the majority of the Dos system calls. However, any Dos call which
+; requires the transfer of more than one buffer of data, or which requires
+; that additional processing be performed other than simply copying a
+; buffer is handled with special case code.
+
+
+; The following symbols define various parts of the entry/exit codes
+; used in the parameter table.
+
+; Nibble 0 of a transfer word is a code that specifies the length of the
+; transfer as follows:
+
+; 0 no xfer
+; 1 FCB to/from (f,10,11,12,13,16,17,23,24,29
+; 2 PTR$ from (1b,1c,34)
+; 3 ASCZ to/from (39,3a,3b,3c,3d,41,43,4b,4e,56
+; 5a,5b)
+; 4 DOLLAR terminated to DOS (9)
+; 5 AX$ from (3f)
+; 6 CX$ to/from (3f,40,44)
+; 7 KEYBUF to/from DOS (a)
+; 8 Country INFO (34) to/from (38)
+; 9 EXTERR (22) to (5d)
+; a DIRectory (64) from (47)
+; b EXEC parm (14) to (4b)
+; c file MATCH (43) to/from (4e,4f)
+; d CMND line (128) to (29)
+; e PALETTE (17) to (int-10/1002)
+; f VBIOS (64) from (int-10/1b)
+;
+;
+; Nibble 1 specifies the segment value transferred as follows (DOS entries
+; affected are listed in parentheses):
+;
+; Segment ptr
+;
+; 0 none
+; 1 ds:dx to/from DOS (9,a,f,10,11,12,13,14,16,17,21,22,23,24,25,27
+; 28,39,3a,3b,3c,3d,3f,40,41,43,44,4b,4e,4f,56
+; 5a,5b)
+; 2 DTA to/from (11,12,14,15,21,22,27,28,4e)
+; 3 ds:bx from (1b,1c) (Allocation table info)
+; 4 ds:si to/from (29,47) (Parse FN, get dir)
+; 5 es:di to/from (29,56) (Parse FN, rename)
+; 6 es:bx to/from (2f,35,4b) (Get DTA, intvct, EXEC)
+; 7 es to (49,4a) (RAM allocation)
+;
+; Byte 1 (high byte) on has meanings:
+;
+; bit 0 = 1 (A0) use small data area 0 (limited to 60h bytes)
+; bit 1 = 1 (A1) use large area 1 (4K)
+; bit 2 = 1 (RP) Real/Protect mode transfer needed (for int 21 ah = 3f/40)
+; bit 7 = 1/0 (EX) exitcd/entrycd
+
+
+entrycd record EnArea:4,EnSegCod:4,EnLenCod:4
+exitcd record ExArea:8,ExSegCod:4,ExLenCod:4
+
+;Length codes:
+
+FCB equ 1
+PTR$ equ 2 ;only supports DSBX & ESBX for now (and DSSI if DBCS)
+ASCZ equ 3
+DOL equ 4
+AX$ equ 5
+CX$ equ 6
+KYB equ 7
+INFO equ 8 ;Constant length transfers from here down vvvvvvvv
+EXTERR equ 9
+DIR equ 0Ah
+EXEC equ 0Bh
+MTCH equ 0Ch
+CMD equ 0Dh
+PALETTE equ 0Eh
+VBIOS equ 0Fh ;Constant length transfers from here up ^^^^^^^^^^
+
+;Segment codes:
+
+DSDX equ 1
+DTA equ 2
+DSBX equ 3
+DSSI equ 4
+ESDI equ 5 ;Also used by int-10/ah=1bh
+ESBX equ 6 ;Also used by int-10/ah=1ch
+ES$ equ 7
+ESDX equ 8 ;Used by int-10/ah=10,12, int-33/ah=22,23
+ESBP equ 9 ;Used by int-10/ah=11,13
+
+
+;RAM area codes:
+
+A0 equ 1
+A1 equ 2
+RP equ 4
+EX equ 80h
+
+
+pmrmxfr entrycd <> ;0 - Program Terminate
+ exitcd <>
+ entrycd <> ;1 - Keyboard Input
+ exitcd <>
+ entrycd <> ;2 - Display output
+ exitcd <>
+ entrycd <> ;3 - Auxiliary Input
+ exitcd <>
+ entrycd <> ;4 - Auxiliary Output
+ exitcd <>
+ entrycd <> ;5 - Printer Output
+ exitcd <>
+ entrycd <> ;6 - Direct Console I/O
+ exitcd <>
+ entrycd <> ;7 - Direct Console Input Without Echo
+ exitcd <>
+ entrycd <> ;8 - Console Input Without Echo
+ exitcd <>
+ entrycd <A1,DSDX,DOL> ;9 - Print string
+ exitcd <>
+ entrycd <A1,DSDX,KYB> ;0A - Buffered Keyboard Input
+ exitcd <EX+A0,DSDX,KYB>
+ entrycd <> ;0B - Check Standard Input Status
+ exitcd <>
+ entrycd <> ;0C - Clear Kbd Buffer and Invoke Kbd Function
+ exitcd <>
+ entrycd <> ;0D - Disk Reset
+ exitcd <>
+ entrycd <> ;0E - Select Disk
+ exitcd <>
+ entrycd <> ;0F - Open File ** Unsupported! **
+ exitcd <>
+ entrycd <> ;10 - Close File ** Unsupported! **
+ exitcd <>
+ entrycd <A0,DSDX,FCB> ;11 - Search for First Entry
+ exitcd <EX+A1,DTA,FCB>
+ entrycd <A0,DSDX,FCB> ;12 - Search for Next Entry
+ exitcd <EX+A1,DTA,FCB>
+ entrycd <A0,DSDX,FCB> ;13 - Delete File
+ exitcd <>
+ entrycd <> ;14 - Sequential Read ** Unsupported! **
+ exitcd <>
+ entrycd <> ;15 - Sequential Write ** Unsupported! **
+ exitcd <>
+ entrycd <> ;16 - Create File ** Unsupported! **
+ exitcd <>
+ entrycd <A0,DSDX,FCB> ;17 - Rename File
+ exitcd <>
+ entrycd <> ;18 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;19 - Current Disk
+ exitcd <>
+ entrycd <A1,DSDX,> ;1A - Set Disk Transfer Address
+ exitcd <>
+ entrycd <> ;1B - Allocation Table Info
+ exitcd <,DSBX,PTR$>
+ entrycd <> ;1C - Alloc Table Info for Specific Device
+ exitcd <,DSBX,PTR$>
+ entrycd <> ;1D - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;1E - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;1F - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;20 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;21 - Random Read ** Unsupported! **
+ exitcd <>
+ entrycd <> ;22 - Random Write ** Unsupported! **
+ exitcd <>
+ entrycd <> ;23 - File Size ** Unsupported! **
+ exitcd <>
+ entrycd <> ;24 - Set Relative Record Field ** Unsupported! **
+ exitcd <>
+ entrycd <,DSDX,> ;25 - Set Interrupt Vector (0/ds:dx/0)
+ exitcd <>
+ entrycd <,DSDX,> ;26 - Create new PSP
+ exitcd <>
+ entrycd <> ;27 - Random Block Read ** Unsupported! **
+ exitcd <>
+ entrycd <> ;28 - Random Block Write ** Unsupported! **
+ exitcd <>
+ entrycd <A0,DSSI,ASCZ> ;29 - Parse Filename
+ exitcd <EX+A1,ESDI,FCB>
+ entrycd <> ;2A - Get Date
+ exitcd <>
+ entrycd <> ;2B - Set Date
+ exitcd <>
+ entrycd <> ;2C - Get Time
+ exitcd <>
+ entrycd <> ;2D - Set Time
+ exitcd <>
+ entrycd <> ;2E - Set/Reset Verify Switch
+ exitcd <>
+ entrycd <> ;2F - Get Disk Transfer Address
+ exitcd <EX+A0,ESBX,>
+ entrycd <> ;30 - Get DOS Version Number
+ exitcd <>
+ entrycd <> ;31 - Terminate and Stay Resident
+ exitcd <>
+ entrycd <> ;32 - Get Drive Parameter Block
+ exitcd <,DSBX,PTR$>
+ entrycd <> ;33 - Ctrl-Break Check
+ exitcd <>
+ entrycd <> ;34 - Get InDOS flag address
+ exitcd <,ESBX,PTR$>
+ entrycd <> ;35 - Get Interrupt Vector
+ exitcd <EX,ESBX,>
+ entrycd <> ;36 - Get Disk Free Space
+ exitcd <>
+ entrycd <> ;37 - Used Internally by DOS
+ exitcd <>
+ entrycd <A1,DSDX,> ;38 - Set/Get Country Dependent Info
+ exitcd <EX+A1,DSDX,INFO>
+ entrycd <A0,DSDX,ASCZ> ;39 - MKDIR
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;3A - RMDIR
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;3B - CHDIR
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;3C - Create a File
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;3D - Open a File
+ exitcd <>
+ entrycd <> ;3E - Close a File Handle
+ exitcd <>
+ entrycd <RP,DSDX,> ;3F - Read from a File or Device
+ exitcd <EX+RP,DSDX,AX$>
+ entrycd <RP,DSDX,CX$> ;40 - Write to a File or Device
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;41 - Delete a File from a Specified Directory
+ exitcd <>
+ entrycd <> ;42 - Move File Read/Write Pointer
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;43 - Change File Mode
+ exitcd <>
+ entrycd <> ;44 - I/O Control for Devices
+ exitcd <> ;See ioctlw for write
+ entrycd <> ;45 - Duplicate a File Handle
+ exitcd <>
+ entrycd <> ;46 - Force a Duplicate of a File Handle
+ exitcd <>
+ entrycd <A0,DSSI,> ;47 - Get Current Directory
+ exitcd <EX+A0,DSSI,ASCZ>
+ entrycd <> ;48 - Allocate Memory
+ exitcd <>
+ entrycd <,ES$,> ;49 - Free Allocated Memory
+ exitcd <>
+ entrycd <,ES$,> ;4A - Modify Allocated Memory Blocks
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;4B - Load or Execute a Program (EXEC)
+ exitcd <>
+ entrycd <> ;4C - Terminate a Process
+ exitcd <>
+ entrycd <> ;4D - Get Return Code of a Sub-process
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;4E - Find First Matching File
+ exitcd <EX+A1,DTA, MTCH>
+ entrycd <A1,DTA,MTCH> ;4F - Find Next Matching File
+ exitcd <EX+A1,DTA, MTCH>
+ entrycd <,ESBX,> ;50 - Set current PSP (code restores bx)
+ exitcd <>
+ entrycd <> ;51 - Get current PSP
+ exitcd <>
+ entrycd <> ;52 - Get Pointer to SysInit Variables
+ exitcd <,ESBX,PTR$>
+ entrycd <A1,DSSI,DIR> ;53 - Set Drive Parameter Block
+ exitcd <>
+ entrycd <> ;54 - Get Verify Setting
+ exitcd <>
+ entrycd <,DSDX,> ;55 - Duplicate PSP
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;56 - Rename a File
+ exitcd <>
+ entrycd <> ;57 - Get/Set a File's Date and Time
+ exitcd <>
+ entrycd <> ;58 - Get/Set Allocation Strategy
+ exitcd <>
+ entrycd <> ;59 - Get Extended Error
+ exitcd <>
+ entrycd <A0,DSDX,ASCZ> ;5A - Create Temporary File
+ exitcd <EX+A0,DSDX,ASCZ>
+ entrycd <A0,DSDX,ASCZ> ;5B - Create New File
+ exitcd <>
+ entrycd <> ;5C - Lock/Unlock File Access
+ exitcd <>
+ entrycd <A0,DSDX,EXTERR> ;5D - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;5E - Network Machine Name/Printer Setup
+ exitcd <>
+ entrycd <> ;5F - Get/Make Assign-List Entry
+ exitcd <>
+ entrycd <> ;60 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;61 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;62 - Get PSP Address
+ exitcd <>
+ entrycd <> ;63 - Get Lead Byte Table ** Unsupported! **
+ exitcd <>
+ entrycd <> ;64 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;65 - Get Extended Country Info
+ exitcd <EX+A1,ESDI,CX$>; ** Only Partially Supported **
+ entrycd <> ;66 - Get/Set Code Page
+ exitcd <>
+ entrycd <> ;67 - Set Handle Count
+ exitcd <>
+ entrycd <> ;68 - Commit File
+ exitcd <>
+ entrycd <> ;69 - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;6A - Used Internally by DOS
+ exitcd <>
+ entrycd <> ;6B - Used Internally by DOS
+ exitcd <>
+ entrycd <A0,DSSI,ASCZ> ;6C - Extended Open File
+ exitcd <>
+
+MaxInt21 equ 06Ch ;max supported Int 21h function
+
+UnSupported entrycd <> ;for unsupported DOS calls
+ exitcd <>
+
+if DEBUG ;------------------------------------------------------------
+
+; Table of partially supported/unsupported/unknown Int 21h functions
+
+ifdef DBCS
+tblBad21 db 18h,1Dh,1Eh,1Fh,20h,37h,5Dh,60h,61h
+ db 64h,65h,6Ah,6Bh,0
+else
+tblBad21 db 18h,1Dh,1Eh,1Fh,20h,37h,5Dh,60h,61h,63h
+ db 64h,65h,6Ah,6Bh,0
+endif
+endif ;DEBUG --------------------------------------------------------
+;
+; For compatibility with WIN386, the following FCB calls are failed
+; unconditionally.
+;
+MIN_REAL_BAD_21 equ 0fh
+MAX_REAL_BAD_21 equ 28h
+
+tblRealBad21 db 0fh,10h,14h,15h,16h,21h,22h,23h,24h,27h,28h,0
+
+
+
+; Special codes for special INT 21h functions
+
+int215E entrycd <A0,DSDX,> ;5E/00 - Get Machine Name
+ exitcd <EX+A0,DSDX,ASCZ>
+ entrycd <A0,DSDX,ASCZ> ;5E/01 - Set Machine name
+ exitcd <>
+ entrycd <A0,DSSI,CX$> ;5E/02 - Set Printer Setup Str
+ exitcd <>
+ entrycd <A0,ESDI,> ;5E/03 - Get Printer Setup Str
+ exitcd <EX+A0,ESDI,CX$>
+
+int215F02 entrycd <A0,DSSI> ;5F/02 - Get Redir List Entry
+ exitcd <EX+A0,DSSI,ASCZ>
+ entrycd <A0,DSSI,ASCZ> ;5F/03 - Set Redir List Entry
+ exitcd <>
+ entrycd <A0,DSSI,ASCZ> ;5F/04 - Cancel Redirection
+ exitcd <>
+ entrycd <A0,DSSI> ;5F/05 - Get Redir List Entry
+ exitcd <EX+A0,DSSI,ASCZ>
+
+ifdef DBCS
+int2163 entrycd <> ;63/00 - Get Lead Byte Table Entry
+ exitcd <,DSSI,PTR$>
+endif ; DBCS
+
+int21esdi entrycd <A1,ESDI,ASCZ> ;56 & 5F/02&03&05 eXtra buffer
+ exitcd <EX+A1,ESDI,ASCZ>
+
+
+;
+; We only use the entry code from the following. If we don't
+; we trash the stack in applications like borland c++
+;
+int21pfn entrycd <A1,ESDI,FCB> ;29, fcb buffer
+ exitcd <>
+
+
+; Additional tables for run time support associated with register
+; translation and buffering.
+
+; 8 9 a b c d e f
+; INFO EXTERR DIR EXEC MATCH CMND PALETTE VBIOS
+mvcnt db 34, 22, 40h, 0Eh, 43, 80h, 17, 64
+
+; 1 2 3 4 5 6 7 8 9
+; DSDX, DTA, DSBX, DSSI, ESDI, ESBX, ES$, esdx, esbp
+regoffs dw xdx, 0, xbx, xsi, xdi, xbx, 0, xdx, xbp
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Main Program
+ page
+; -------------------------------------------------------
+; MAIN PROGRAM
+; -------------------------------------------------------
+
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntrDos -- This function is the main handler for int 21h calls
+; that require special case processing. Most interrupts
+; go through the interrupt reflector (PMIntrReflector in
+; dxintr.asm). DOS int 21h interrupts are vectored here.
+;
+; This routine performs any register manipulation, data buffering
+; etc. needed to pass the interrupt parameters from the
+; protected mode caller to the real mode handler. Register
+; manipulation and data transfers can occur either on entry
+; to the interrupt handler or on exit from the interrupt
+; handler (to fix up return values.)
+;
+; Input: normal registers for Dos calls
+; Output: normal register returns for Dos calls
+; Errors: normal Dos errors
+; Uses: In general, all registers are preserved. For interrupts where
+; register translation or buffering occurs, some registers may
+; be changed, depending on the interrupt and its parameters.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntrDos
+
+PMIntrDos proc far
+
+IFDEF WOW_x86
+;IF 0
+; BUGBUG filter functions FIRST!!
+ push ds
+ push ax
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ assume ds:DGROUP
+ mov ax,ss
+ mov pmusrss,ax
+ mov ax,sp
+ add ax,4
+ mov pmusrsp,ax
+ pop ax
+
+ DEBUG_TRACE DBGTR_ENTRY, 21h, ax, 0
+
+ FBOP BOP_DPMI,XlatInt21Call,FastBop
+; If we get here, the api wasn't translated. The translation code will
+; simulate the iret
+ assume ds:nothing
+
+NotXlated:
+ENDIF
+ cld ;Code assumes direction flag is cleared
+
+if DEBUG ;------------------------------------------------------------
+
+; Trace levels: 1 - print AX on all calls except 3F, 40, 42 (read, write,
+; seek--Windows does so many of these...)
+; 2 - like level 1, but includes 3F, 40, 42
+; 3 - print all regs on all calls
+
+ push bp
+ push ds ;tracing of DOS calls wanted?
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ mov bp,fTraceDOS
+ pop ds
+ assume ds:NOTHING
+ or bp,bp
+ jnz @f
+
+notracefileio:
+ jmp notracedos
+@@:
+ cmp bp,2
+ jae @f
+ cmp ah,3Fh
+ jz notracefileio
+ cmp ah,40h
+ jz notracefileio
+ cmp ah,42h
+ jz notracefileio
+@@:
+ Trace_Out "I21-#AX",x
+
+ cmp bp,2 ;how much detail wanted?
+ jbe tracestr
+
+ Trace_Out " bx #BX cx #CX dx #DX si #SI di #DI",x
+ push ax
+ push bx
+ mov ax,ds
+ mov bx,es
+ Trace_Out " ds #AX es #BX",x
+ pop bx
+ pop ax
+
+tracestr:
+
+ cmp ah,3Dh
+ jz @f
+ cmp ah,5Bh
+ jz @f
+ cmp ah,5Ah
+ jz @f
+ cmp ah,43h
+ jz @f
+ cmp ah,41h
+ jz @f
+ cmp ah,3Ch
+ jz @f
+
+ cmp ah,3Fh
+ jnz notraceread
+ cmp bp,1
+ je notraceread
+ push ax
+ mov ax,ds
+ Trace_Out " L #CX @ #AX:#DX",x
+ pop ax
+
+notraceread:
+ jmp short notracedos
+@@:
+ push ax
+ mov ax,ds
+ cmp bp,2
+ jbe @f
+ Trace_Out " " ;output end of line
+@@:
+ Trace_Out " ->@AX:DX<-",x
+ pop ax
+
+notracedos:
+ pop bp
+
+ push ds
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ cmp ah,byte ptr TrapDOS
+ jnz @f
+ or ah,ah
+ jz @f
+ Debug_Out "Trapped DOS call #ax"
+@@:
+ pop ds
+ assume ds:NOTHING
+
+endif ;DEBUG --------------------------------------------------------
+
+; Save PM user ds, es, and flags, and switch to DOS extender stack
+
+ push ax ;Save caller's AX
+ push bp
+ mov bp,sp ;[bp] = bp ax ip cs fl - 286 int gates assumed
+ ; 0 2 4 6 8
+ push es
+
+ mov es,selDgroupPM ;Address our DGROUP
+ assume es:DGROUP
+
+
+; Check for nested DOS interrupts--this code was written with the assumption
+; that we would never be reentered, but it turns out that we may need to
+; support nested DOS calls from a critical error handler. If this is one
+; of those ultra-rare occasions, save our previous 'instance' data in Isave.
+
+ inc EntryFlag ;The normal case will be to jump
+ jz @f
+
+ push cx ;Being reentered, save last instance data
+ push di
+ push si
+
+ mov cx,ILENGTH ;NOTE!!! The next movs has
+ mov si,offset DGROUP:Ibegin ; an es override, if ints
+ mov di,offset DGROUP:Isave ; are enabled, an interrupt
+ rep movs byte ptr [di],byte ptr es:[si] ; on this instr can 'forget'
+ ; about the es override with
+ pop si ; some processors
+ pop di
+ pop cx
+@@:
+
+; Start saving callers state.
+
+ mov pmrg.xds,ds ;Save PM user ds
+ mov ax,[bp+8] ;Get ax = flags when int occurred
+ mov rmflags,ax ;Store flags for real-mode handler
+
+ pop pmrg.xes ;Save PM user es
+
+ pop bp
+ pop ax ;Recover user ax. [sp] = ip cs fl
+
+; At this point all general registers (but not ES) have the user's values
+
+ push es ;Address DGROUP, user's DS already
+ pop ds ; saved in pmrg.xds
+ assume ds:DGROUP
+
+ mov pmusrss,ss ;Save PM user stack ptr
+ mov pmusrsp,sp ;[sp] = ds ip cs fl
+ ; 0 2 4 6
+ mov pmrg.xsi,si ;Save PM si since need to use before pusha
+
+ push ds ;Switch to rmrg stack for this routine
+ pop ss
+ mov sp,offset DGROUP:rmflags ;PM flags already on stack
+ FSTI ;We don't really need interrupts disabled
+
+
+; Setup iret frames for iret'ing to real-mode handler and for that handler
+; returning to the DOS extender
+
+ pop si ;Get rmflags
+ and si,not 4100h ;Kill NT, TF
+ push si ;Push flags for iret to BackFromDOS
+ push segDXCodePM ;Push return cs:ip for iret
+ push offset BackFromDOS
+
+ and si,not 0200h ;Kill IF
+ push si ;Push flags for iret to real-mode handler
+
+ sub sp,4 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ mov ax,SEL_RMIVT OR STD_RING
+ mov es,ax
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+
+
+; Setup protected mode and real mode copies of registers.
+
+ mov si,pmrg.xsi ;Restore si
+ push ds ;Save space for real mode ds
+ push es ; and es
+ pusha ;Save user general registers
+
+ mov si,offset DGROUP:rmrg ;Copy user values to PM set for
+ mov di,offset DGROUP:pmrg ; reference (real-mode set may change)
+ mov cx,8 ;8 general registers (es and ds already stored)
+ rep movsw
+
+IFDEF ROM
+ mov ax,segDXData
+ELSE
+ mov ax,segDXDataPM ;ax = DOS extender real-mode dgroup segment
+ENDIF
+ mov rmrg.xds,ax ;Default real-mode data segments
+ mov rmrg.xes,ax ; (dosentry may change them)
+
+ mov ax,rmrg.xax
+
+if DEBUG ;------------------------------------------------------------
+
+; Check for partially supported/unsupported/unknown DOS calls
+
+ cmp ah,0DCh ;krnl286 is doing this now, quit
+ jz goodfunc ; complaining about it
+
+ cmp ax,5D0Ah ;Set Extended Error is the only
+ jz goodfunc ;5Dh function handled properly!
+
+ cmp ah,MaxInt21 ;is the request within our range?
+ ja badfunc
+
+ mov bx,offset DXPMCODE:tblBad21
+@@:
+ cmp ah,cs:[bx]
+ jb goodfunc
+ jz badfunc
+ inc bx
+ jmp short @b
+
+badfunc: Trace_Out "Possible Unsupported DOS Call! AX=#AX"
+
+goodfunc:
+
+endif ;DEBUG --------------------------------------------------------
+
+; Check for FCB calls that we fail unconditionally (because WIN386 does.)
+
+ cmp ah,MIN_REAL_BAD_21
+ jb goodfunc1
+ cmp ah,MAX_REAL_BAD_21
+ ja goodfunc1
+
+mov bx,offset DXPMCODE:tblRealBad21
+@@:
+ cmp ah,cs:[bx]
+ je badfunc1
+ inc bx
+ cmp byte ptr cs:[bx],0
+ jz goodfunc1 ; Ran off end of table.
+ jmp short @b
+
+badfunc1:
+
+if DEBUG
+ Debug_Out "Unsupported DOS Call! AX=#AX"
+endif ;DEBUG
+
+ or byte ptr rmflags,1 ; Set CF
+ call xfrflg
+ jmp LeaveDOSExtender
+
+goodfunc1:
+
+
+; int 21 entry register translations and data transfers
+
+ cmp ah,25h ;Set interrupt vector ?
+ jnz @f
+ xor ah,ah
+ mov cx,pmrg.xds
+ mov dx,pmrg.xdx
+ call DFSetIntrVector ;perform special case processing
+ jmp LeaveDOSExtender ;No real-mode cycle needed
+@@:
+ cmp ah,35h ;Get interrupt vector ?
+ jnz @f
+ xor ah,ah
+ call DFGetIntrVector
+ mov rmrg.xbx,dx
+ mov pmrg.xes,cx
+ jmp LeaveDOSExtender
+@@:
+ cmp ah,00h ;old style DOS Exit call?
+ jnz @f
+ call DosExitCall ;sets CY if it handles the call, otherwise
+ jnc @f ; do it normally...
+ jmp LeaveDOSExtender
+@@:
+ ;
+ ; Handle terminate specially. We mess with the PSP here to set
+ ; up a terminate vector we like. We don't do anything special for
+ ; TSR (31h)
+ ;
+ cmp ah,4ch ; terminate?
+ jnz @f
+
+ call TerminateProcess
+@@:
+
+ cmp ah, 5dh ; check for unsupported 5d codes
+ jnz short @f
+ cmp al, 0ah
+ jz short @f
+ jmp LeaveDOSExtender
+@@:
+
+ mov rcount,0 ;Default no remaining bytes to read/write
+ mov ccount,0 ;No bytes read or written yet
+
+ cmp ah,3Fh ;If read
+ jz @f
+ cmp ah,40h ; or write,
+ jnz TransferLoop
+@@:
+ mov cx,pmrg.xcx ; initialize remaining count = requested value
+ mov rcount,cx
+
+
+; Start of loop for doing large read/write transfers
+
+TransferLoop:
+
+ call dosentry ;Do selector translations, data buffering
+
+
+; Come here after entry register translations and data transfers are complete
+
+ SwitchToRealMode ;Switch back to real mode. ds = es = rm dgroup
+ ;Stack is at same place in memory
+
+; Set registers to possibly translated values and iret to real-mode DOS.
+; DOS then irets to BackFromDOS.
+
+ popa ;Set appropriate general register values
+
+ pop es
+ pop ds
+
+ public GoingToDOS
+
+GoingToDOS: ;for debugging, etc.
+
+ iret ;invoke real mode DOS
+
+ assume ds:NOTHING, es:NOTHING, ss:NOTHING
+
+
+; Return here from real-mode interrupt handler (DOS)
+
+ public BackFromDOS
+
+BackFromDOS:
+
+ pushf ;Save return flags (to rmflags)
+ cld ; (better safe than sorry)
+
+ push cs ;Push return cs:ip for multiple xfers
+ push offset BackFromDOS
+
+ sub sp,2*3 ;Bypass room for iret to interrupt handler
+ ; (to keep stack layout the same as on entry)
+
+ push ds ;Save register set
+ push es
+ pusha
+
+IFDEF ROM
+ mov ax,ss ;SS already points to our data segment
+ mov ds,ax
+ELSE
+ mov ds,segDXData
+ENDIF
+ assume ds:DGROUP
+
+
+; "push" iret frame for real mode int 21 rtn in case we need to do it again
+
+ mov ax,rmflags
+ and ax,not 4300h ;Kill NT, TF, and IF
+ mov rmivfl,ax
+
+ xor ax,ax
+ mov es,ax
+ mov ax,word ptr es:[21h*4+2]
+ mov rmivcs,ax
+ mov ax,word ptr es:[21h*4]
+ mov rmivip,ax
+
+
+; Switch back to protected mode
+
+ SwitchToProtectedMode ;Switch back to protected mode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI ;Don't need ints disabled
+
+ call xfrflg ;Transfer relevant return flags over to pm iret frame
+
+ mov ax,pmrg.xax ;Recover AX from caller
+
+
+; Perform any int-21 selector translations, data buffering
+
+ call dosexit
+
+
+; Check for large xfers (Read File 3Fh, Write File 40h, some IOCTL 44h)
+
+ cmp rcount,0 ;Read/write more bytes?
+ jz TransferDone
+
+ mov cx,rmrg.xax ;Maybe. cx = count transferred (if 3Fh or 40h)
+ mov ax,pmrg.xax ;Restore entry code
+ mov rmrg.xax,ax
+
+ cmp ah,40h ;Write?
+ jnz @f
+
+ sub rcount,cx ;Yes. Written all originally requested?
+ jz TransferDone
+
+ cmp cx,rmrg.xcx ; No. Written all last specified?
+ jz @f ; Yes. Go do some more
+
+ mov ax,ccount ;A large write has failed! ccount has already
+ sub ax,rmrg.xcx ; been updated assuming success, back out the
+ add ax,cx ; attempted xfer amount, and add in
+ jmp short TransferCount ; the actual, then split
+@@:
+ jmp TransferLoop ;Yep (or 3Fh or 44h). Do another xfer
+
+TransferDone:
+ mov ax,ccount ;Multiple count xfer?
+ or ax,ax
+ jz LeaveDOSExtender
+
+TransferCount:
+ mov rmrg.xax,ax ;Yes update return amount
+ mov ax,pmrg.xcx
+ mov rmrg.xcx,ax ;Restore initial request count
+
+
+; Restore possibly translated registers and to return to pm caller
+
+ public LeaveDOSExtender
+
+LeaveDOSExtender:
+
+
+if DEBUG ;------------------------------------------------------------
+
+; If we're tracing DOS calls, say we're returning to the app...
+
+ cmp fTraceDOS,0
+ jz @f
+ cmp fTraceDOS,2
+ jae tracedoit
+ mov ax,pmrg.xax
+ cmp ah,3Fh
+ jz @f
+ cmp ah,40h
+ jz @f
+ cmp ah,42h
+ jz @f
+tracedoit:
+ Trace_Out " *"
+@@:
+
+endif ;DEBUG ---------------------------------------------------------
+
+ popa ;Restore possibly changed user registers
+
+ mov ss,pmusrss ;Restore pm user stack
+ mov sp,pmusrsp
+ assume ss:NOTHING
+
+ push pmrg.xds ;push user seg regs on user stack
+ push pmrg.xes
+
+ dec EntryFlag ;dec nested entry flag - normal case is to jmp
+ jnz NotNested
+ ;If this was a nested DOS call (from
+ push cx ; a critical error handler), restore
+ push si ; the state for the prior DOS call
+ push di ; which is still in progress
+
+ mov cx,ds ;make darn sure es -> DGROUP
+ mov es,cx
+
+ cld ;NOTE: we need to retreive
+ mov cx,ILENGTH ; all current user registers
+ mov di,offset DGROUP:Ibegin ; before moving this data
+ mov si,offset DGROUP:Isave
+ rep movsb
+
+ pop di
+ pop si
+ pop cx
+
+NotNested:
+ pop es ;restore user seg regs
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+
+ public DOSXiret
+DOSXiret: ;for debugging, etc.
+
+ iret ;return to caller
+
+PMIntrDos endp
+
+
+; -------------------------------------------------------
+; DOSENTRY -- This function performs buffering and register
+; translation for entry into MS-DOS functions.
+;
+; Input: AX: AX value at time of INT 21h
+; Output:
+; Errors:
+; Uses:
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public dosentry
+
+dosentry:
+ cmp ah,44h
+ jnz @F
+;
+; DOS call 44h (IOCTL) is weird enough to warrant special attention.
+; NOTE: We jump to IOCTLEnter here, and it returns to our caller.
+;
+ jmp IOCTLEnter ;several special cases here
+@@:
+ cmp ah,48h ;Allocate memory?
+ jmpz pmallc ;Yes, try for XRAM
+
+ cmp ah,49h ;free memory block
+ jmpz pmfree
+
+ cmp ah,4Ah ;modify memory block
+ jmpz pmmodb
+
+ cmp ah,26h ;Create new PSP?
+ jnz @f
+ mov si,rmrg.xdx ;yes, translate selector to paragraph
+ call gtpara
+ mov rmrg.xdx,ax
+ return
+@@:
+ cmp ah,53h ;Set drive parameter block?
+ jnz @f
+ push ax
+ mov si,pmrg.xes ;int 21h/53h has an extra parameter in ES:BP
+ call gtpara ; we change the selector to a segment, but
+ mov rmrg.xes,ax ; the segment had better already be in
+ pop ax ; conventional memory
+ jmp short dentr2b
+@@:
+ cmp ah,50h ;Set current PSP?
+ jnz dentr1
+ mov si,rmrg.xbx ;Yes. Translate selector to paragraph
+ call gtpara
+ mov rmrg.xbx,ax
+ return
+
+dentr1: cmp ah,55h ;Duplicate PSP?
+ jnz dentr2
+ mov si,rmrg.xbx ;Translate selector bx to paragraph
+ call gtpara
+ mov rmrg.xbx,ax
+ mov si,rmrg.xdx ; and dx also
+ call gtpara
+ mov rmrg.xdx,ax
+ return
+
+dentr2:
+ cmp ah,56h ;Rename?
+ jnz dentr2a
+ push ax ;rename has a second ASCIIZ buffer
+ push pmrg.xes ; pointed to by es:di -- move that
+ pop enxseg ; now, the ds:dx one below
+ mov ax,int21esdi
+ call gtarea ;let the 'standard' gtarea/movdat
+ mov dx,enxseg ; routines take care of it
+ call movdat
+ pop ax
+ jmp short dentr2b
+
+dentr2a:
+ cmp ah,5Fh ;Get/Make Assign-List Entry?
+ jne dentr2a1
+ call net5Fenter ; Yes, may require extra buffering
+ jmp short dentr2b
+
+dentr2a1:
+ cmp ah,29h ; parse filename?
+ jne dentr2b
+
+ push ax
+ push pmrg.xes
+ pop enxseg
+ mov ax,int21pfn
+ call gtarea
+ mov dx,enxseg
+ call movdat
+ pop ax
+;; jmp short dentr2b
+
+dentr2b:
+ call GetEntryCd ;ax = func entry code, di = ptr to entry cd
+
+ or ax,ax ;Entry code specify something to do?
+ rz
+
+ cmp byte ptr pmrg.xax+1,1Ah ;Store DTA?
+ jnz dentr3
+
+ mov pmdta,dx ; Yes. Save it for data returns
+ push pmrg.xds
+ pop pmdta+2
+ jmp short dentr4
+
+dentr3: cmp byte ptr pmrg.xax+1,4Bh ;EXEC program?
+ callz dosxec
+
+; DENTR4 - enter with ax = entrycd/exitcd. Translate ptr's for real-
+; mode calls and transfer any data indicated.
+
+dentr4: push pmrg.xds
+ pop enxseg
+ call gtarea ;Get es:di = area for transfer
+ rz ;Something to xfer?
+ mov dx,enxseg ;Yes. Fall thru to movdat
+
+ errnz <movdat-$>
+
+
+; -------------------------------------------------------
+; MOVDAT -- This routine performs the buffer transfer
+; for entry into or exit from an MS-DOS function. The data
+; is copied from DX:SI to ES:DI. The code in CX determines
+; the type of data being transferred, which is used to determine
+; the length.
+;
+; Input: DX:SI - far pointer to source buffer
+; ES:DI - far pointer to destination buffer
+; CX - transfer length code
+; Output: none
+; Errors: none
+; Uses: AX, BX, CS, SI, DI modified
+
+ assume ds:DGROUP,es:NOTHING,ss:DGROUP
+ public movdat
+
+movdat:
+ push ds
+ mov bx,cx ;Simple count?
+ sub bl,INFO
+ jc movda2
+ cmp bl,PALETTE-INFO ;Yes. Use pm es?
+ jc movda0
+ mov dx,pmrg.xes ;Yes
+movda0: mov cl,mvcnt[bx] ;cx = count
+movda1: mov ds,dx
+
+if DEBUG ;------------------------------------------------------------
+
+ push ax
+ mov ax,es
+ lsl ax,ax
+ sub ax,di
+ jc movbad
+ cmp ax,cx
+ jnc @f
+movbad:
+ Debug_Out "Movdat: copy beyond end of dest seg!"
+@@:
+ pop ax
+
+endif ;DEBUG --------------------------------------------------------
+
+movd1a: rep movsb ;Move data
+ pop ds
+ return
+
+movda2: cmp cl,CX$ ;Use pmrg.xcx?
+ jnz movda3
+ mov cx,rmrg.xcx ;cx usually = pmrg.xcx, but in any event
+ ; cx < CB_XFRBUF1
+movd21: add ccount,cx
+ jmp short movda1
+
+movda3: mov ah,0
+ cmp cl,ASCZ
+ jz movda4
+ cmp cl,DOL
+ jnz movda5
+ mov ah,"$"
+movda4: mov ds,dx
+movd42: lodsb
+ stosb
+ cmp al,ah
+ jnz movd42
+ pop ds
+ return
+
+movda5: cmp cl,AX$ ;Use rmrg.xax?
+ jnz movda6
+ mov cx,rmrg.xax ;Yes (only occurs for ah=3fh - read - on exit)
+ jmp short movd21
+
+movda6:
+ cmp cl,FCB
+ jnz movda7
+ mov ds,dx
+ mov cl,byte ptr ds:[si]
+ cmp cl,0ffh ;standard or extended FCB?
+ mov cx,37 ;standard FCB len
+ jnz movd1a
+ mov cx,44 ;extended FCB len
+ jmp short movd1a
+
+movda7: ;KYB remains
+ pop ds
+ return
+
+
+; -------------------------------------------------------
+; DOSEXIT -- This function is called on exit from the MS-DOS
+; functions to perform any data buffering and register translation
+; needed.
+;
+; Input: AX: AX value at time of INT 21h
+; Output:
+; Errors:
+; Uses:
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public dosexit
+
+dosexit:
+ cmp ah,44h
+ jnz @F
+;
+; DOS call 44h (IOCTL) is weird enough to warrant special attention.
+; NOTE: We jump to IOCTLExit here, and it returns to our caller.
+;
+ jmp IOCTLExit ;several special cases here
+@@:
+ cmp ah,51h ;Get current PSP?
+ jz dose0a
+ cmp ah,62h ;Get PSP address?
+ jnz dose00
+dose0a:
+ mov ax,rmrg.xbx ;Yes. Translate segment to selector
+ mov bx,STD_DATA
+ call ParaToLDTSelector
+ mov rmrg.xbx,ax
+ return
+
+dose00: cmp ah,2fh ;Get current DTA?
+ jnz dose01
+ mov ax,pmdta ;Yes. Load PM DTA into caller's registers
+ mov rmrg.xbx,ax
+ mov ax,pmdta+2
+ verr ax ; if the dta selector is no longer valid,
+ jz @f ; return the NULL selector instead (so we
+ xor ax,ax ; don't GP fault in DOSX).
+@@: mov pmrg.xes,ax
+ return
+
+dose01: cmp ah,55h ;Duplicate PSP?
+ jnz dosex1
+ mov ax,rmrg.xbx ;Yes, translate segments to selectors
+ mov bx,STD_DATA
+ call ParaToLDTSelector
+ mov rmrg.xbx,ax
+ mov ax,rmrg.xdx
+ mov bx,STD_DATA
+ call ParaToLDTSelector
+ mov rmrg.xdx,ax
+ return
+
+dosex1: cmp ah,56h ;Rename?
+ jnz dosex2
+ push pmrg.xdi ;Rename has a second pointer in ES:DI--we
+ pop rmrg.xdi ; need to restore DI here, DX below
+ jmp short dosex3
+
+dosex2: cmp ah,5Fh ;Get/Make Assign-List Entry?
+ callz net5Fexit ; Yes, extra buffering may be needed
+
+dosex3:
+ call GetEntryCd ;ax=func entry code, di=ptr to entry code
+
+ call rstreg ;Restore entry register?
+ jz dosex6
+
+ cmp byte ptr pmrg.xax+1,29h ;Yes, Parse filename?
+ jnz dosex4
+
+ add ax,rmrg.xsi ;Yes. Increment past string
+ sub ax,offset DGROUP:rgbXfrBuf0 ; that was parsed
+ push pmrg.xdi
+ pop rmrg.xdi ;Restore pm di (for es:di ptr)
+
+dosex4: mov word ptr rmrg[si],ax ;Restore register
+
+ cmp byte ptr pmrg.xax+1,4Bh ;EXEC program
+ jnz dosex6
+
+ push di
+ mov di,pmrg.xbx ;Yes, restore bx too (dx restored above)
+ mov rmrg.xbx,di ;es and ds are restored automatically
+ cmp byte ptr pmrg.xax,1 ;INT-21/4b01h (undocumented debug)?
+ jnz @f
+
+ mov si,npXfrBuf1 ;Yes. Pass back user ss:sp and cs:ip
+ lea si,[si].xsssp
+ lea di,[di].xsssp
+ mov es,pmrg.xes
+ movsw ;Move ss:sp
+ movsw
+ movsw ;Move cs:ip
+ movsw
+@@:
+ pop di
+
+dosex6: mov ax,cs:[di+2] ;Exit xfer?
+ or ax,ax
+ rz
+
+dosex7: call CheckStatus ;Check the DOS return status to see if the
+ rnz ; data should be transfered back to PM
+
+ mov cx,ax ;Is a pointer being returned? (no data
+ and cl,0fh ; transfer)
+ cmp cl,PTR$
+ jnz dosex8
+
+ shr al,4 ; yes, isolate pointer type
+ mov si,offset rmrg.xds
+ mov di,offset pmrg.xds
+ cmp al,DSBX
+ jz dosex7a
+ifdef DBCS ; for function 63h (Get Lead Byte)
+ cmp al,DSSI
+ jz dosex7a
+endif ; DBCS
+ mov si,offset rmrg.xes
+ mov di,offset pmrg.xes
+dosex7a:
+ mov ax,[si] ; get a selector for the segment, and
+ mov bx,STD_DATA ; setup to return it to caller
+ call ParaToLDTSelector
+ mov [di],ax
+ return
+
+dosex8:
+ push pmrg.xds
+ pop enxseg
+ call gtarea ;Get area for xfer from PM to DOS
+ rz ;Something to move?
+
+ xchg si,di ;Turn pointers around
+ mov dx,ds ;dx:si -> DOS xfer area in dgroup
+ mov es,enxseg ;es:di -> PM-caller data area
+ jmp movdat ;Yes
+
+; -------------------------------------------------------
+; IOCTLEnter -- Special entry processing for the Generic I/O Control
+; Three cases require data transfer.
+;
+; AX = 4402h Read control data from device
+; AX = 4403h Write control data to device
+; AX = 4404h Read control data from device
+; AX = 4405h Write control data to device
+;
+; AX = 440Ch Generic I/O control for character devices
+; (only minor codes CL=45h and CL=65h supported)
+;
+; AX = 440Dh Generic I/O control for block devices
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public IOCTLEnter
+
+IOCTLEnter proc near
+
+ mov di,npXfrBuf1 ;assume this buffer will be used
+ xor cx,cx
+
+ cmp al,0dh
+ je IOCTL_0D_Enter
+
+ cmp al,0ch
+ je IOCTL_0C_Enter
+
+ cmp al,3 ;write control data
+ je IOCTLWriteEnter
+ cmp al,5 ;write control data
+ je IOCTLWriteEnter
+ cmp al,2 ;read control data
+ je IOCTLReadEnter
+ cmp al,4 ;read control data
+ je IOCTLReadEnter
+
+ jmp IOCTLEnterRet ;register based call
+
+IOCTLWriteEnter:
+ mov cx,rmrg.xcx ;Write length to CX
+IOCTLReadEnter:
+
+ push es
+ push ax
+ push si
+ mov si,pmrg.xds
+ mov es,selGDT
+ mov al,es:[si].adrBaseHigh
+ mov ah,es:[si].adrbBaseHi386
+ test ax,0FFF0h ; check for transfer to extended
+ ; memory
+ pop si
+ pop ax
+ pop es
+
+ jz IOCTLEnterDOSMem
+;
+; These transfers cannot be broken up into smaller pieces. If the
+; requested read or write size exceeds our transfer buffer size, then
+; we truncate it.
+;
+ cmp cx,CB_XFRBUF1
+ jb @F
+ mov cx,CB_XFRBUF1
+@@:
+ cmp rmrg.xcx,CB_XFRBUF1
+ jb @F
+ mov rmrg.xcx,CB_XFRBUF1
+@@:
+ jmp IOCTLEnterMove
+
+IOCTLEnterDOSMem:
+
+ push ax
+ push dx
+ mov dx,rmrg.xdx
+ mov ax,rmrg.xds
+ call SelOff2SegOff ;translate to seg:off if in conventional mem
+ mov rmrg.xdx,dx
+ mov rmrg.xds,ax
+
+ jmp IOCTLEnterRet
+
+IOCTL_0C_Enter:
+ cmp rmrg.xcx,45h ; Set iteration count
+ jne @F
+ mov cx,2
+@@:
+ jmp IOCTLEnterMove
+
+IOCTL_0D_Enter:
+ mov di,npXfrBuf1 ;assume this buffer will be used
+
+ mov cx,rmrg.xcx ;IOCTL subfunction code to CL
+
+ cmp cl,40h ;Set Device Parameters?
+ jnz @f
+ mov cx,28h ;set device parameters has a variable
+ push es ; length table--the word at offset
+ mov es,pmrg.xds ; 26h contains the number of following
+ assume es:NOTHING ; 4 byte entries
+ mov si,pmrg.xdx
+ mov ax,word ptr es:[si+26h]
+ shl ax,2
+ add cx,ax
+ pop es
+ assume es:DGROUP
+ jmp IOCTLEnterMove
+@@:
+ cmp cl,41h ;Write Track?
+ jnz @f
+ mov di,npXfrBuf0 ;going to use this buffer instead
+ cmp rcount,0 ;continuation of large buffered write?
+ jnz WriteTrackMove ; yes, only need to move track data
+
+ mov cx,9 ;only copy 9 bytes of param block
+ call Enter0DTrack ;setup for track read/write
+
+ cmp rcount,0 ;now indicates if buffering needed?
+ jz IOCTLEnterMove ; 0 == if no buffering
+
+WriteTrackMove:
+ push cx
+ push ds
+
+ mov ax,word ptr [di+07] ;get # sectors to transfer
+ mul cbSector ;calc # bytes to move
+ mov cx,ax
+ shr cx,1 ;cx = # words to move
+
+ lds si,lpTrackData ;move source to
+ assume ds:NOTHING
+ mov di,npXfrBuf1 ; dest...
+ rep movsw
+ mov word ptr lpTrackData,si ;for next time
+
+ pop ds
+ assume ds:DGROUP
+ pop cx
+ mov di,npXfrBuf0 ;point to param block
+
+ cmp cl,41h ;used as a flag to see if Enter0DTrack
+ jnz IOCTLEnterMove ; was called or not--Don't want to
+ jmp short IOCTLEnterFinished ; move param block if not called
+@@:
+ cmp cl,42h ;Format & Verify Track?
+ jnz @f
+ mov cx,5
+ jmp short IOCTLEnterMove
+@@:
+ cmp cl,47h ;Set Access Flag?
+ jnz @f
+ mov cx,2
+ jmp short IOCTLEnterMove
+@@:
+ cmp cl,60h ;Get Device Parameters?
+ jnz @f ; Some caller's expect some result
+ mov cx,26h ; fields to be initialized with
+ jmp short IOCTLEnterMove ; their values so copy'm down to DOS
+@@:
+ cmp cl,68h ; DOS5: Media Sense?
+ jnz @F
+ mov cx,4
+ jmp short IOCTLEnterMove
+@@:
+ cmp cl,61h ;Read Track?
+ jnz IOCTLEnterFinished
+ mov di,npXfrBuf0 ;going to use this buffer instead
+ cmp rcount,0 ;continuation of large buffered read?
+ jnz IOCTLEnterFinished ; yes, nothing to do here
+
+ mov cx,9 ;only copy 9 bytes of param block
+ call Enter0DTrack ;setup for track read/write
+
+ errnz <$-IOCTLEnterMove>
+
+IOCTLEnterMove:
+
+ push ds
+ push di
+ mov si,pmrg.xdx
+ mov ds,pmrg.xds
+ assume ds:NOTHING
+ cld
+ rep movsb ;copy param data to DOS visible buffer
+ pop di
+ pop ds
+ assume ds:DGROUP
+
+IOCTLEnterFinished:
+
+ mov rmrg.xdx,di ;point real mode DS:DX to our buffer
+ ; (DS already done by other code)
+IOCTLEnterRet:
+
+ ret
+
+IOCTLEnter endp
+
+
+; Enter0DTrack -- setup for IOCTL 0Dh read/write track
+; On exit DI -> small transfer buffer (npXfrBuf0)
+
+Enter0DTrack proc
+
+ push es
+ mov es,pmrg.xds
+ assume es:NOTHING
+ mov di,pmrg.xdx ;es:di -> caller's param block
+
+ mov dx,word ptr es:[di+09]
+ mov ax,word ptr es:[di+0Bh] ;ax:dx = sel:off of caller's track data
+
+ mov word ptr lpTrackData,dx
+ mov word ptr [lpTrackData+2],ax
+
+ call SelOff2SegOff ;convert to segment:off
+
+ jz set_buf_ptr ; Z set if in low memory
+
+ ; The track data is not in conventional memory--we need to buffer
+ ; it through the large transfer buffer--UGLY!
+
+ mov ax,word ptr es:[di+07] ;get # sectors to transfer
+ mov rcount,ax
+
+ SwitchToRealMode ;find out how large a sector
+ assume ds:DGROUP,es:DGROUP ; is by dropping to real mode and
+ FSTI ; doing a Get Device Parameters IOCTL
+
+ mov ax,440Dh ;IOCTL
+ mov bx,pmrg.xbx ; caller's drive
+ mov cx,0860h ; Get Device Parameters
+ mov dx,npXfrBuf0 ; temp buffer
+ pushf
+ FCLI
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset edt_10)
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+edt_10: mov cx,512 ;default to 512
+ mov di,dx ; CY shouldn't be set, but...
+ jc @f ; offset 7 in parameter block
+ mov cx,word ptr [di+07] ; is the # bytes/sector
+@@:
+ mov cbSector,cx
+
+ SwitchToProtectedMode ;back to pMode
+ FSTI
+
+ xor dx,dx ;calc # sectors that can be
+ mov ax,CB_XFRBUF1 ; read/written per transfer
+ div cx
+
+ mov cx,rcount
+ cmp ax,cx
+ jb @f
+ mov ax,cx ;ax = # sectors to transfer this time
+@@:
+ mov [di+07],ax ;store # sectors in our read/write
+ ; param block
+
+ mov ax,rmrg.xds
+ mov dx,npXfrBuf1 ;ax:dx = our real mode track buffer
+
+ mov cx,7 ;only move 7 bytes of param block
+ ; (don't use caller's # sectors)
+set_buf_ptr:
+ assume es:NOTHING
+
+ mov di,npXfrBuf0 ;use this buffer for param block
+ mov [di+09],dx ; plug in segment:offset of track data
+ mov [di+0Bh],ax
+
+ pop es
+ assume es:DGROUP
+
+ ret
+
+Enter0DTrack endp
+
+; -------------------------------------------------------
+; IOCTLExit -- Special exit processing for the Generic I/O Control.
+; Three cases require data transfer.
+;
+; AX = 4402h Read/Write control data to device
+; AX = 4403h
+; AX = 4404h
+; AX = 4405h
+;
+; AX = 440Ch Generic I/O control for character devices
+; (only minor codes CL=45h and CL=65h supported)
+;
+; AX = 440Dh Generic I/O control for block devices
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public IOCTLExit
+
+IOCTLExit proc near
+
+ cmp al,0dh
+ je IOCTL_0D_Exit
+
+ cmp al,0ch
+ je IOCTL_0C_Exit
+
+ cmp al,3 ;write control data
+ je IOCTLWriteExit
+ cmp al,5 ;write control data
+ je IOCTLWriteExit
+ cmp al,2 ;read control data
+ je IOCTLReadExit
+ cmp al,4 ;read control data
+ je IOCTLReadExit
+
+ jmp IOCTLExitRet ;register based call
+
+IOCTLWriteExit:
+ jmp IOCTLExitFinished ;make sure caller's DX is restored
+
+IOCTLReadExit: ;fill caller's buffer with result
+
+ push es
+ push ax
+ push si
+ mov si,pmrg.xds
+ mov es,selGDT
+ mov al,es:[si].adrBaseHigh
+ mov ah,es:[si].adrbBaseHi386
+ test ax,0FFF0h ; check for transfer to extended
+ ; memory
+ pop si
+ pop ax
+ pop es
+
+ mov cx,rmrg.xax
+ jz @F
+ jmp IOCTLExitMove
+@@:
+ jmp IOCTLExitFinished
+
+IOCTL_0C_Exit:
+ cmp rmrg.xcx,65h ; Get iteration count
+ je IOCTL_0C_ExitMove
+ cmp rmrg.xcx,45h ; Set iteration count
+ je IOCTL_0C_ExitNoMove
+ or byte ptr rmflags,1 ;Not supported!
+IOCTL_0C_ExitNoMove:
+ xor cx,cx
+ jmp IOCTLExitMove
+IOCTL_0C_ExitMove:
+ mov cx,2
+ jmp IOCTLExitMove
+
+IOCTL_0D_Exit:
+ test byte ptr rmflags,1 ;CY set? (error on the IOCTL)
+ jz @f
+ xor ax,ax ;yes, halt buffered read/writes
+ mov rcount,ax
+ jmp IOCTLExitFinished ; and don't do anything else either
+@@:
+ mov cx,rmrg.xcx ;IOCTL subfunction code to CL
+
+ cmp cl,41h ;Write track?
+ jz read_write_0D
+ cmp cl,61h ;Read track?
+ jnz not_read_write_0D
+
+read_write_0D:
+ mov ax,rcount ;nothing to do if not buffering
+ or ax,ax
+ jz IOCTLExitFinished
+
+ mov di,npXfrBuf0 ;point to param buffer
+ mov bx,word ptr [di+07] ;bx = # sectors transfered
+ add word ptr [di+05],bx ;update starting sector for next time
+ sub ax,bx
+ mov rcount,ax ;update remaining count of sectors
+ jz @f ;don't need to setup again if no more
+
+ cmp ax,bx ;can transfer at most as many as last
+ jae @f ; time--already set if more remaining
+ mov word ptr [di+07],ax ; otherwise just transfer remaining
+@@:
+ cmp cl,61h ;transfer track data if read track
+ jnz IOCTLExitFinished
+
+ mov ax,bx ;ax = # sectors read
+ mul cbSector ;ax = # bytes read
+ mov cx,ax
+ shr cx,1 ;cx = # words read
+
+ push es ;copy track data to caller's buffer
+ les di,lpTrackData
+ assume es:NOTHING
+ mov si,npXfrBuf1
+ cld
+ rep movsw
+ mov word ptr lpTrackData,di ;for next time
+ pop es
+ assume es:DGROUP
+
+ jmp short IOCTLExitFinished
+
+not_read_write_0D:
+ cmp cl,60h ;Get Device Parameters?
+ jnz @f
+ mov cx,26h
+ jmp short IOCTLExitMove
+@@:
+ cmp cl,62h ;Verify Track?
+ jnz @f
+ mov cx,5
+ jmp short IOCTLExitMove
+@@:
+ cmp cl,68h ; DOS5: Media Sense?
+ jnz @F
+ mov cx,4
+ jmp short IOCTLExitMove
+@@:
+ cmp cl,67h ;Get Access Flag?
+ jnz IOCTLExitFinished
+ mov cx,2
+
+ errnz <$-IOCTLExitMove>
+
+IOCTLExitMove:
+
+ push es ;caller's ds:dx -> result buffer
+ mov es,pmrg.xds
+ assume es:NOTHING
+ mov si,npXfrBuf1
+ mov di,pmrg.xdx
+ cld
+ rep movsb ;move result to caller's buffer
+ pop es
+
+IOCTLExitFinished:
+
+ mov ax,pmrg.xdx ;restore caller's dx register
+ mov rmrg.xdx,ax
+
+IOCTLExitRet:
+ ret
+
+IOCTLExit endp
+
+; -------------------------------------------------------
+; DosExitCall -- Special processing for DOS exit call service (Int 21h/00h)
+;
+; This procedure handles the obsolete Int 21h/00h terminate process
+; call in a special slimy way for Windows. Instead of doing a 00h
+; DOS call in real mode, it hacks up the parent's PSP and does a 4Ch
+; that causes control to return to the extender, who then RETURNS TO
+; THE CALLER!!! The caller must then do appropriate set PSP calls, etc.
+; This was implemented so pMode Windows kernel could have DOS clean up
+; after a Windows app terminates, but still have kernel get control
+; back.
+;
+; Note: This code assumes that the Parent PID field contains a
+; SELECTOR!
+;
+; Note^2: If for some reason it's the DOS Extender's child that's 1800doing
+; the terminate call, we let it go through normally.
+;
+; Input: none
+; Output: CY set if exit call processed by this routine, clear otherwise
+; Errors:
+; Uses: none
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public DosExitCall
+
+DosExitCall proc near
+
+ push ax
+ push bx
+ push dx
+ push es
+
+ SwitchToRealMode ;much of this is easier in real mode
+ FSTI ;allow interrupts
+
+ mov ah,51h ;get PSP of current task
+ pushf
+ FCLI
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset dec_10)
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+dec_10: cmp bx,segPSPChild ;is this our child terminating?
+ jnz @f ;if not, go process the call ourselves
+ jmp decTermChild ; yes...
+@@:
+ FCLI ;we want to preserve the current
+ xor ax,ax ; rMode Int 24h handler across this
+ mov es,ax ; exit call (the terminating PSP
+ mov ax,es:[24h*4] ; has the pMode handler address).
+ mov dx,es:[24h*4+2] ; So get the current Int 24h handler
+ FSTI ; address from the rMode IDT.
+
+ mov es,bx ;address terminating PSP
+ assume es:PSPSEG
+
+ mov word ptr [lpfnInt24],ax ;point PSP to same Int 24h
+ mov word ptr [lpfnInt24+2],dx ; handler
+
+ mov ax,offset DXCODE:decTermHandler ;point PSP to our termination
+ mov word ptr [lpfnParent],ax ; handler
+ mov word ptr [lpfnParent+2],cs
+
+ push es
+ mov ax,segParentPSP ;Windows has the PSP's parent
+ push ax ; field as a selector, we need the seg
+ mov dx, [segEnviron]
+ push dx
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI
+ pop ax ;get selector for environment
+ call GetSegmentAddress
+ shr dx, 4
+ shl bx, 12
+ or bx, dx ;seg of environment
+
+ pop ax ;get parent psp off stack
+ push bx ;save seg of environment
+
+ call GetSegmentAddress ;returns BX:DX = lma of segment
+ shr dx,4
+ shl bx,12
+ or bx,dx ;bx now = seg of parent psp
+
+ SwitchToRealMode ;back to the shadows again...
+ FSTI
+ pop cx ;seg of environment
+
+ pop dx ;terminating PSP segment from stack
+ mov es,bx ;address the parent's PSP
+ assume es:PSPSEG
+
+ mov ax,sp
+ sub ax,12*2 ;some magic for DOS
+ mov word ptr [lpStack],ax ;set our stack in parent's PSP
+ mov word ptr [lpStack+2],ss
+
+ mov es,dx ;(re)address terminating PSP
+ assume es:PSPSEG
+ mov [segEnviron], cx
+
+ mov segParentPSP,bx ;real DOS doesn't like selectors in
+ ; parent PSP field, zap it to segment
+
+ mov ax,pmrg.xax ;terminate the process
+ mov ah,4Ch ; with a 4Ch DOS call
+ pushf
+ FCLI
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset decTermHandler
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING ;play it safe
+
+decTermHandler: ;should return back here
+
+ push ss
+ pop ds
+
+ SwitchToProtectedMode ;back to pMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI
+
+ les bx,dword ptr pmusrsp ;es:bx -> ip cs flag
+ and byte ptr es:[bx+2*2],not 1 ;clear CY in caller's flags
+
+ stc ;exit call has been processed
+
+dec90:
+ pop es
+ pop dx
+ pop bx
+ pop ax
+
+ ret
+
+EndHighSegment
+
+BeginLowSegment
+
+decTermChild:
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI
+ clc
+
+ jmp short dec90
+
+DosExitCall endp
+
+
+; -------------------------------------------------------
+; net5Fenter -- Additional entry processing for INT 21h/5Fh
+; functions.
+;
+; INT 21h/5Fh subfunctions 2, 3, and 5 have two buffers of data to
+; transfer. The normal DOSENTRY processing only does one, so we
+; setup the other buffer here.
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public net5Fenter
+
+net5Fenter proc near
+
+ cmp al,2 ;This routine only works on
+ rc ; subfunctions 2, 3, and 5
+ cmp al,4
+ rz
+ cmp al,6
+ rnc
+
+ push ax
+ mov ax,int21esdi ;entry code for INT 21h/5Fh extra buff
+ call gtarea ;let gtarea set it up
+ pop ax
+
+ cmp al,3 ;Make redirection function?
+ rnz
+
+; 5F/03 needs a buffer copied down to A1, but it's non standard in that
+; the buffer contains two (count'em 2) asciiz strings
+
+ push ax
+ push cx
+ push ds
+
+ mov ds,pmrg.xes ;user's ES:DI -> buffer, gtarea sets
+ xor ah,ah ; up our si to have user's di
+ mov cl,2
+
+@@: lodsb ;copy one asciiz string
+ stosb
+ cmp al,ah
+ jnz @b
+
+ dec cl ; and then the other
+ jnz @b
+
+ pop ds
+ pop cx
+ pop ax
+ return
+
+net5Fenter endp
+
+
+; -------------------------------------------------------
+; net5Fexit -- Additional exit processing for INT 21h/5Fh
+; functions.
+;
+; INT 21h/5Fh subfunctions 2, 3, & 5 have 2 buffers of data to transfer.
+; The normal DOSENTRY processing only does one, so do the other
+; buffer here.
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public net5Fexit
+
+net5Fexit proc near
+
+ cmp al,2 ;This routine only works on
+ rc ; subfunctions 2, 3 and 5
+ cmp al,4
+ rz
+ cmp al,6
+ rnc
+
+ push pmrg.xdi ;Restore protected mode DI register
+ pop rmrg.xdi
+
+ cmp al,2 ;Get redirection function?
+ jz @f
+ cmp al,5
+ rnz
+@@:
+
+; 5F/02 & 05 need a buffer copied from A1
+
+ test byte ptr rmflags,1 ;Success? (carry flag)
+ rnz ; No, don't transfer anything
+
+ push ax
+
+ mov ax,int21esdi+2 ;exit code for int 21/5F extra buffer
+ push pmrg.xes
+ pop enxseg
+ call gtarea ;let gtarea setup the move
+ xchg si,di
+ mov dx,ds
+ mov es,enxseg
+ call movdat ; and let movdat move it
+
+ pop ax
+ return
+
+net5Fexit endp
+
+
+; -------------------------------------------------------
+; RSTREG -- This function sets up to restore the original
+; protected-mode registers. This cleans up after register
+; translations performed when going into the or returning
+; from an MS-DOS call. On entry, AX contains the entry code
+; value from the entry/exit operations table. If this code
+; implies that a register needs to be restored this function
+; will return with NZ true and AX = original register value
+; and SI pointing to the appropriate location in the PMRG array.
+; If no register needs to be restored, return with Z true.
+;
+; Input: AX - entry code value
+; Output: NZ true if register needs to be restores
+; AX - register value to restore
+; SI - pointer into PMRG to the register image
+; ZR true if no restoration needed
+; Errors: none
+; Uses: AX, SI modified
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public rstreg
+
+rstreg: or ax,ax
+ rz
+
+ shr ax,3
+ and ax,1Eh
+ rz
+
+ cmp al,2*DTA ;DTA?
+ rz
+
+ xchg si,ax ;No. Restore appropriate register, e.g., dx
+ mov si,regoffs[si-2]
+ mov ax,word ptr pmrg[si]
+ return
+
+
+; -------------------------------------------------------
+; GTAREA -- This function examines the entry code/exit code
+; parameter and determines if any data transfer needs to be
+; performed. If so, it sets up pointers and length codes for
+; the transfer.
+; There are two transfer buffers used. The A0 buffer is 60h bytes
+; long and the A1 buffer is CB_XFRBUF1 bytes (about 4k) long.
+;
+; Input: AX - entry code/exit code
+; Output: NZ true if something needs to be transferred
+; SI - offset of source pointer
+; DI - offset of destination pointer
+; ENXSEG - segment for caller's buffer
+; (source on entry, destination on exit)
+; CX - transfer length/type code
+; Errors: none
+; Uses: AX, CX, SI, DI, ES modified
+; ENXSEG modified
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public gtarea
+
+gtarea:
+ test ah,RP ;Real/PM xfer (ah = 3f/40)?
+ jz gtare2
+
+ mov si,pmrg.xds ;Yes. *si:pmrg.xdx = pm caller's xfer area
+ and si,SELECTOR_INDEX
+ test ah,EX ;Exit call?
+ jz gtare1
+ push es
+ push ax
+ mov es,selGDT
+ mov al,es:[si].adrBaseHigh
+ mov ah,es:[si].adrbBaseHi386
+ test ax,0FFF0h ; check for transfer to extended
+ ; memory
+ pop ax
+ pop es
+ jnz @f
+ jmp gtar54
+@@:
+ mov cx,rmrg.xax ;Yes. cx = amount read/written
+ sub rcount,cx ;Update remaining count
+ cmp cx,rmrg.xcx ;All that was requested?
+ jnc gtare3
+ mov rcount,0 ;No: done
+ jmp short gtare3
+
+gtare1: push ax ;Read/Write entry
+ mov ax,si
+ mov dx,pmrg.xdx ;ax:dx = selector:offset of buffer
+ call SelOff2SegOff ;translate to seg:off if in conventional mem
+ jnz gtar12
+ mov rmrg.xds,ax ;store corresponding paragraph
+ mov rmrg.xdx,dx ; and offset
+ pop ax
+ jmp short gtar54 ;No more setup needed
+
+gtar12: pop ax ;XRAM/RRAM read/write entry
+ mov cx,rcount ;Try to xfer remaining amount
+ cmp cx,CB_XFRBUF1 ;Too much to fit in buffer?
+ jbe gtar14
+ mov cx,CB_XFRBUF1 ;Yes, only transfer a buffer size
+gtar14: mov rmrg.xcx,cx
+ jmp short gtare3
+
+gtare2: test ah,A0+A1 ;xfer area?
+ jz gtare4
+
+gtare3: mov di,offset DGROUP:rgbXfrBuf0 ;Point at small buffer (90h bytes)
+ test ah,1 ;Area 0 (small area) ?
+ jnz gtare4
+ mov di,npXfrBuf1 ;No. Point at large buffer (4K)
+
+gtare4: push ax ;Store ptr to communication area for DOS
+ mov si,di
+ shr al,3 ;Get al = 2 * data ptr type, e.g., DSDX (ds:dx)
+ and al,1Eh ;al = word offset for data ptr type
+ jz gtare7
+ cmp al,2*DTA ;DOS Data Transfer Area?
+ jnz gtare5
+ mov si,pmdta ;Yes, load DTA offset
+ push pmdta+2 ;and the segment
+ pop enxseg
+ jmp short gtare7
+
+gtare5: cmp al,2*ES$
+ jnz gtare6
+ test ah,80h ;INT-21 49/4A. Ignore exit call
+ mov ax,0
+ jnz gtar52
+ mov si,pmrg.xes ;Entry call. si = RAM xfer selector
+ call gtpara ;Get paragraph given by [si].gdt
+ jnz gtar52 ;XRAM?
+ mov rmrg.xes,ax ;No, store RRAM paragraph
+gtar52: pop cx ;Kill saved ptr
+
+gtar54: xor cx,cx ;RZ with zero count, i.e., no RM/PM xfer needed
+ mov rcount,cx
+ return
+
+gtare6: test ah,80h ;Entry?
+ cbw
+ push ax
+ xchg di,ax ;Save real-mode area offset
+ mov di,regoffs[di-2] ;di = offset of saved register value
+ mov si,word ptr pmrg[di] ;si = saved value
+ jnz gtar62
+ mov word ptr rmrg[di],ax ;Entry. Store real-mode area offset
+ cmp byte ptr pmrg.xax+1,29h ;Parse filename?
+ jnz gtar62
+ mov cx,npXfrBuf1 ;Yes. Use npXfrBuf1 for FCB info
+ mov word ptr rmrg.xdi,cx
+gtar62: xchg di,ax ;Restore di = real-mode area offset
+
+ pop ax
+ cmp ax,ESDI*2
+ jne gtare7
+
+ push pmrg.xes
+ pop enxseg
+
+gtare7: pop cx ;Recover entrycd/exitcd
+ and cx,0Fh ;RNZ if something to xfer
+ rz ;No
+
+ mov dx,pmrg.xds ;Xfer needed. dx = original XRAM data selector
+ cmp cl,AX$ ;Use rmrg.xax?
+ jz gtare8
+ cmp cl,CX$ ;Use pmrg.xcx?
+ rnz ;No, just RNZ
+
+
+;Return dx:0 = pmrg.xds:si+ccount, where dx is a scratch selector
+;and si = 0. This ensures that dx:si can address a bufferful of bytes
+;without overflowing si (sort of like a huge ptr).
+
+gtare8: xchg ax,si ;ax = original offset
+ mov si,dx ;si = pmrg.xds (PM data selector)
+ and si,SELECTOR_INDEX
+ xor dx,dx ;dxax = original offset in 32-bit form
+ add ax,ccount
+ adc dx,0 ;dxax += ccount (current xfer count)
+ push es
+ mov es,selGDT ;Point at GDT
+ add ax,es:[si].adrBaseLow
+ adc dl,es:[si].adrBaseHigh ;dxax absolute XRAM address
+ adc dh,es:[si].adrbBaseHi386
+ mov si,selDOSScr
+ and si,SELECTOR_INDEX
+ifndef WOW_x86
+ mov es:[si].adrBaseLow,ax
+ mov es:[si].adrBaseHigh,dl
+ mov es:[si].adrbBaseHi386,dh
+ mov es:[si].cbLimit,0FFFFh
+ mov es:[si].arbSegAccess,STD_DATA
+else
+ cCall NSetSegmentDscr,<si,dx,ax,0,0ffffh,STD_DATA>
+endif ; WOW_x86
+ pop es
+
+; original code... changed to line below to fix file read problem.
+; may cause other bugs.....
+; mov dx,si ;Return scratch selector with 0 offset since
+;
+ or si,SELECTOR_TI
+ mov enxseg,si ;Return scratch selector with 0 offset since
+ xor si,si ; it points directly to the transfer location
+ or sp,sp ;RNZ to indicate xfer needed
+ return
+
+; -------------------------------------------------------
+; GetEntryCd -- This routine puts the entry code for the
+; DOS function into ax.
+;
+; Input: AH - MS-DOS function number
+; Output: AX - entry code for function
+; DI - address of entry code returned
+;
+; Errors: none
+; Uses: AX, DI modified
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public GetEntryCd
+
+GetEntryCd proc near
+
+ push bx
+
+ cmp ah,MaxInt21 ;Check for unsupported DOS call
+ jbe @f
+ mov di,offset DXPMCODE:Unsupported
+ jmp gec90
+@@:
+ mov di,offset DXPMCODE:pmrmxfr
+
+ifdef DBCS
+ cmp ah,63h ;Get Lead Byte Table?
+ jnz gec10
+
+if DEBUG ;------------------------------------------------------------
+
+ cmp al,2
+ jbe gec15
+
+ Debug_Out "Int 21h/63h Unsupported Function (#AX)"
+ jmp short gec80
+gec15:
+
+endif ;DEBUG --------------------------------------------------------
+
+ cmp al,0 ;Int 21/63/00 is special
+ jne gec80
+
+ mov di,offset DXPMCODE:int2163
+ jmp short gec90
+gec10:
+ENDIF ; DBCS
+
+gec20: cmp ah,5Eh ;Network Machine Name/Printer Setup Str?
+ jnz gec40
+
+if DEBUG ;------------------------------------------------------------
+
+ cmp al,3
+ jna gec25
+
+ Debug_Out "Int 21h/5Eh Unsupported Function (#AX)"
+ jmp short gec80
+gec25:
+
+endif ;DEBUG --------------------------------------------------------
+
+ cmp al,3 ;Int 21-5E/00-03 are special
+ ja gec80
+
+ mov bl,al
+ mov di,offset DXPMCODE:int215E
+ jmp short gec85
+
+
+gec40: cmp ah,5Fh ;Get/Make Assign-List Entry?
+ jnz gec80
+
+if DEBUG ;------------------------------------------------------------
+
+ cmp al,5
+ ja @f
+ cmp al,2
+ jnb gec45
+@@:
+ cmp al,30h ;Register based. Get Redirector version
+ ;used by Lanman Enhanced.
+ je gec80
+ Debug_Out "Int 21h/5Fh Unsupported Function (#AX)"
+ jmp short gec80
+gec45:
+
+endif ;DEBUG --------------------------------------------------------
+
+ cmp al,2 ;Int 21/5F/02-05 are special
+ jb gec80
+ cmp al,5
+ ja gec80
+
+ mov bl,al
+ sub bl,2
+ mov di,offset DXPMCODE:int215F02
+ jmp short gec85
+
+
+gec80: mov bl,ah
+gec85: xor bh,bh
+ shl bx,2
+
+ add di,bx ;Address of entry code
+gec90:
+ mov ax,word ptr cs:[di] ;The entry code itself
+ pop bx
+ return
+
+GetEntryCd endp
+
+
+; -------------------------------------------------------
+; CheckStatus -- This routine determines if data should be copied
+; back to protect mode by checking the DOS function
+; return status.
+;
+; Input: none
+; Output: NZ - if data should NOT be copied, Z otherwise
+;
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public CheckStatus
+
+CheckStatus proc near
+
+; For now, only worry about the functions that return variable length
+; results, like ASCIIZ strings.
+
+ cmp byte ptr pmrg.xax+1,32h
+ jz @f
+ cmp byte ptr pmrg.xax+1,47h ;Get Current Directory
+ jz @f
+ cmp byte ptr pmrg.xax+1,5Ah ;Create Temporary File
+ jz @f
+ cmp byte ptr pmrg.xax+1,5Eh ;Get Machine Name/Printer Str
+ jc cks90
+ cmp byte ptr pmrg.xax+1,5Fh ;Get Redirection Entry
+ ja cks90
+@@:
+ test byte ptr rmflags,1 ;Carry set?
+ return
+
+cks90: cmp al,al ;Assume status is okay (or doesn't
+ return ; matter) -- set Z and return
+
+CheckStatus endp
+
+
+; -------------------------------------------------------
+; DOSXEC -- This function performs the transfer of the
+; DOS exec parameter block on entry to DOS function 4Bh.
+;
+; transfer int-21 ah = 4b0x EXEC block and associated strings
+; to Area 1. This area is laid out partly analogously to a PSP as follows:
+;
+; 0-1f EXEC block defined according to the execblk struc above
+; 20-2f FCB1
+; 30-3f FCB2
+; 40-bf command line
+;
+; cx, si, di changed.
+
+ assume ds:DGROUP,es:DGROUP,ss:DGROUP
+ public dosxec
+
+dosxec:
+ push ax ;Save entrcd code
+ push bx
+ push dx
+
+ mov cx,10h ;Setup parameter block. Xfer pm user block first
+ mov bx,npXfrBuf1 ;Point at larger buffer
+ mov di,bx
+IFDEF ROM
+ mov dx,segDXData
+ELSE
+ mov dx,segDXDataPM
+ENDIF
+ mov rmrg.xbx,di ;int-21 4b0x expects es:bx -> exec block
+ mov si,pmrg.xbx ;npXfrBuf1:0 - 1f (xtra room for undocumented)
+ mov ds,pmrg.xes
+ rep movsw ;copy caller's exec param block to our buffer
+
+; Copy FCB1 down if the user has specified one.
+
+dsxc20: mov ax,word ptr es:[bx].fcb1ptr
+ cmp ax,0FFFFh
+ jz dsxc22
+ or ax,word ptr es:[bx].fcb1ptr+2
+ jz dsxc22
+
+ lds si,es:[bx].fcb1ptr ;Get pointer to FCB1
+ mov word ptr es:[bx].fcb1ptr,di ;store new pointer in the copy of the
+ mov word ptr es:[bx].fcb1ptr+2,dx ; exec block we are building
+ mov cl,8 ;copy FCB1 down to our buffer
+ rep movsw
+ jmp short dsxc24
+dsxc22: add di,10h
+
+; Copy FCB2 down if the user has specified one.
+
+dsxc24: mov ax,word ptr es:[bx].fcb2ptr
+ cmp ax,0FFFFh
+ jz dsxc26
+ or ax,word ptr es:[bx].fcb2ptr+2
+ jz dsxc26
+
+ lds si,es:[bx].fcb2ptr ;Move FCB2
+ mov word ptr es:[bx].fcb2ptr,di
+ mov word ptr es:[bx].fcb2ptr+2,dx
+ mov cl,8
+ rep movsw
+ jmp short dsxc30
+dsxc26: add di,10h
+
+; Copy the command line down.
+
+dsxc30: lds si,es:[bx].cmdptr ;Move command line
+ mov word ptr es:[bx].cmdptr,di
+ mov word ptr es:[bx].cmdptr+2,dx
+ mov cl,[si]
+ inc cx ;Include count
+ inc cx ;Include final CR not included in count
+ rep movsb
+
+; Now, we need to set up the enviroment table to be passed to the
+; child program.
+
+ mov di,bx ;di = npXfrBuf1
+ mov dx,es:[di] ;Setup desired environment
+ or dx,dx ;Use parent's environment?
+ jnz dosxegotenv
+
+; pick up the environment segment from the current PDB. It's a selector,
+; so it has to be translated.
+ push bx
+ push di
+ push es
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+
+ SwitchToRealMode ;much of this is easier in real mode
+
+ mov ah,51h ;get PSP of current task
+ pushf
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset dosxeret)
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+dosxeret:
+ assume es:PSPSEG
+ mov es, bx ;current PSP
+ mov dx, es:[segEnviron] ;get environment (currently selector)
+ push dx ;save over call (bugbug is this needed?)
+ SwitchToProtectedMode
+ pop dx ;bugbug is this needed?
+
+ pop es
+ pop di
+ pop bx
+
+
+dosxegotenv:
+ xor si,si ;No. Setup to copy desired environment down
+ mov ds,dx ;ds = dx has caller's selector. Use 0 offset
+ add di,100h
+ shr di,4 ;Convert offset to paragraph
+IFDEF ROM
+ GetPMDataSeg
+ mov dx,ax
+ELSE
+ mov dx,segDXDataPM
+ENDIF
+ add dx,di ;dx = absolute paragraph within larger buffer
+ shl di,4 ;Convert back (with low nibble cleared)
+ mov cx,CB_XFRBUF1 ;Max room available for environment
+ sub cx,100h
+
+dosxe2: lodsw ;Copy environment down
+ stosw
+ or ax,ax
+ jz dosxe4
+ dec si ;Check every byte offset in environment for double 0
+ dec di
+ loop dosxe2
+ xor dx,dx ;Environment too large for buffer: use parent's
+ ;Issue error message? Program might run with parent's
+ ; and not with desired monster environment
+dosxe4: push es ;Fix up parameter block entry
+ pop ds ;ds:dgroup
+ mov [bx].evrnmt,dx
+
+ pop dx
+ pop bx
+ pop ax ;Restore entrcd code
+ return
+
+
+; -------------------------------------------------------
+; XFRFLG -- This function will transfer the relevant real-mode
+; flags to the protected mode IRET return information on the
+; stack for returning to the protected mode caller.
+;
+; Input:
+; Output:
+; Errors:
+; Uses: AX, CX, DI modified
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public xfrflg
+
+xfrflg: push es
+ les di,dword ptr pmusrsp ;es:[di] = ip cs fl (assume 80286)
+ mov ax,rmflags
+ mov cx,es:[di+2*2] ;Get pm user entry flags
+ and ch,not 19h ;Only allow real-mode program to
+ and ah,18h ; change OF and DF in high byte of flags
+ or ah,ch
+ mov es:[di+2*2],ax
+ pop es
+ return
+
+; -------------------------------------------------------
+ subttl Handlers for Special Case Dos Functions
+ page
+; -------------------------------------------------------
+; HANDLERS FOR SPECIAL CASE DOS FUNCTIONS
+; -------------------------------------------------------
+;
+; DFGetIntrVector -- This function handles the special
+; case processing required for the DOS function 35h
+; (Get Interrupt Vector).
+;
+; Input: AX - interrupt number
+; Output: CX - selector of the interrupt routine
+; DX - offset of the interrupt routine
+; Errors: none
+; Uses: CX, DX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public DFGetIntrVector
+
+DFGetIntrVector:
+
+ push ax
+
+ cmp ax,1Eh ;special case int 1Eh which points to
+ jnz dfgv10 ; disk parameter table
+
+ push es
+ push bx
+
+ push SEL_RMIVT or STD_RING ;address real mode IDT via es
+ pop es
+ assume es:NOTHING
+
+ mov dx,es:[1Eh*4] ;get current 1Eh SEG:offset to ax:dx
+ mov ax,es:[1Eh*4+2]
+
+ mov bx,STD_DATA ;make/find a data selector to table
+ call ParaToLDTSelector
+ mov cx,ax ;return selector:offset in cx:dx
+
+ pop bx
+ pop es
+
+ jmp short dfgv90
+
+dfgv10:
+
+dfgv20: call GetIntrVector
+
+dfgv90:
+
+if 0
+if DEBUG ;------------------------------------------------------------
+ cmp al,2
+ jnz @f
+ Trace_Out "Returning Int 2 vector #CX:#DX"
+@@:
+endif ;DEBUG --------------------------------------------------------
+endif
+
+ pop ax
+ ret
+
+; -------------------------------------------------------
+; DFSetIntrVector -- This functions handles the processing
+; for the DOS function 25h (Set Interrupt Vector).
+;
+; Input: AX - interrupt vector number
+; CX - selector of interrupt routine to set
+; DX - offset of interrupt routine to set
+; Output: none
+; Errors: none
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public DFSetIntrVector
+
+DFSetIntrVector:
+
+ push ax
+ push dx
+ push di
+ push es
+
+set_as_is:
+
+; The first thing that we want to do is to store the given vector
+; into the interrupt descriptor table.
+
+dfsv20: call PutIntrVector
+
+; Setting the interrupt vector is now finished, unless it's a hardware
+; interrupt (50h-57h & 70h-77h) or one of the special software interrupts
+; (^Break, timer tick, ^C, critical error). At one time, the DOS extender
+; allowed any interrupt to be reflected to protected mode, but Windows/386
+; doesn't, so we no longer do either. Note reflecting INT 2Fh to protected
+; mode breaks the MS-NET redirector, so don't do that.
+
+
+ cmp al,1Bh ;^Break?
+ jz @f
+ cmp al,1Ch ;Timer Tick?
+ jz @f
+ cmp al,23h ;Ctrl-C?
+ jz @f
+ cmp al,24h ;Critical Error Handler?
+ jz @f
+ cmp al,02h ;Math co-processor exception used by math
+ jz @f ; library routines!
+
+ cmp al,08h ;Hardware?
+ jb dfsv30
+ cmp al,0fh
+ jna @f
+
+ cmp al,70h
+ jb dfsv30
+ cmp al,77h
+ jna @f
+
+dfsv30: jmp dfsv90
+@@:
+
+
+; We are going to need to look at the real mode interrupt vector table,
+; so set up for that.
+
+ mov di,ax ;interrupt vector number to DI
+ add di,di ;multiply by 4 to turn into dword offset
+ add di,di
+ push SEL_RMIVT or STD_RING ;selector to real mode int table
+ pop es
+
+; Now, we need to see if the user is trying to restore the original
+; handler for a vector he had previously hooked, or he is trying to
+; hook a new vector. If he is trying to hook a new one, the address
+; he is setting will be one of his routines, if he is trying to restore
+; the original value of a previously hooked vector, the address will
+; be a location in the Dos Extender interrrupt reflector entry table.
+; So, if the selector of the address being set is a Dos Extender code
+; address we think he is trying to restore an old vector.
+
+ push cx
+ and cx,SELECTOR_INDEX
+ cmp cx,SEL_DXPMCODE ;check if it is the dos extender code segment
+ pop cx
+ jnz dfsv50
+
+; The application is trying to restore a previously hooked vector.
+; We want to restore the original value of the real mode interrupt
+; vector with the value stored in the real mode interrupt vector
+; shadow table. However, we only want to do this if no one else has
+; hooked the vector after we gave it to the protected mode child. If
+; someone else has also hooked it, then we want to leave it alone. We
+; can tell if someone else has hooked it by looking at the value
+; currently in the real mode interrupt vector. If it points to the
+; Dos Extender real mode interrupt reflector, then no one else has
+; hooked it, and it is safe to restore the original vector.
+
+ mov dx,segDXCodePM ;Dos Extender real mode code segment
+ cmp es:[di+2],dx ;does the segment of the vector point
+ jnz dfsv45 ; to the dos extender code segment?
+ FCLI
+ mov ax,word ptr RmHwISR[di] ;get offset of old RM vector
+ mov es:[di],ax
+ mov ax,word ptr RmHwISR[di+2] ;get segment of old RM vector
+ mov es:[di+2],ax
+; xor ax,ax
+; mov word ptr RmHwISR[di],ax ; forget old handler
+; mov word ptr RmHwISR[di+2],ax ; forget old handler
+ FSTI
+
+dfsv45: jmp dfsv90
+
+; The application is trying to hook a new vector. We need to redirect
+; the real mode vector for this interrupt to the corresponding entry
+; point in the real mode -> protected mode interrupt reflector. So that
+; the application interrupt routine will receive control if the interrupt
+; occurs while the processor is in real mode. In case the application
+; installs more than one hook routine, we need to check to see if it is
+; already hooked, and not hook it again if so.
+
+dfsv50: cmp al,24h ;The critical error handler gets a
+ jnz @f ; special handler address
+ mov dx,offset RMIntr24
+ jmp short dfsv51
+@@:
+ mov dx,offset RMIntrEntryVector ;address of beginning of
+ ;real mode reflector entry table
+ add dx,ax ;add in 3 times the interrupt vector
+ add dx,ax ; number to get the offset of the
+ add dx,ax ; correct entry point in the real mode
+ ; interrupt reflector entry table
+dfsv51: mov ax,word ptr RmHwIsr[di]
+ or ax,word ptr RmHwIsr[di + 2]
+ jz dfsv56
+
+ mov ax,word ptr RmHwIsr[di]
+ cmp ax,es:[di]
+ jne dfsv90
+
+ mov ax,word ptr RmHwIsr[di + 2]
+ cmp ax,es:[di + 2]
+ jne dfsv90
+
+; The interrupt isn't already hooked, so we want to redirect it to point
+; to the real mode entry vector.
+
+dfsv56: FCLI
+
+;
+; save the actual vector in RmHWIsr
+;
+ mov ax,es:[di]
+ mov word ptr RmHwIsr[di],ax
+ mov ax,es:[di+2]
+ mov word ptr RmHwIsr[di+2],ax
+
+ mov es:[di],dx ;set the vector to point to our reflector
+ mov dx,segDXCodePM ;Dos Extender real mode code segment
+ mov es:[di+2],dx
+ FSTI
+;
+; All done
+dfsv90: pop es
+ pop di
+ pop dx
+ pop ax
+ ret
+
+; -------------------------------------------------------
+ page
+; -------------------------------------------------------
+; EXTENDED MEMORY MANAGEMENT
+; -------------------------------------------------------
+
+
+comment |PMALLC - protected-mode XRAM allocation routine. RNC with ax =
+selector for bx paragraphs of allocated XRAM. If too little XRAM is
+available, RC with bx = # paragraphs available.
+|
+
+ public pmallc
+
+pmallc: mov cx,pmrg.xbx ;number of paragraphs requested
+ mov dx,cx
+ shl cx,4
+ shr dx,12
+ xor bl,bl
+ call AllocateXmemBlock
+ jnc pmal70
+;
+; Error occured. Return carry flag set, error code in AX, and
+; size of largest available memory block in BX.
+ mov rmrg.xax,ax ;save error code
+ shr cx,4 ;divide return value by 16
+ mov al,dh
+ shl al,4
+ or ch,al
+ shr dx,4
+ jz pmal22 ;see if more than 1 Meg available
+ mov cx,0FFFFh ;and limit it to 1 meg if so
+pmal22: mov rmrg.xbx,cx ;store memory size available
+ or byte ptr rmflags,1 ;Some there. Set CF
+ jmp short pmal72
+;
+; The allocate succeeded. Clear the carry flag returned to the user
+; and return the initial selector in AX.
+pmal70: and byte ptr rmflags, NOT 1
+pmal72: mov rmrg.xax,ax
+ call xfrflg
+ pop ax ;kill return from DOSENTRY
+ jmp LeaveDOSExtender
+
+; -------------------------------------------------------
+; pmfree - Free the specified extended memory block.
+;
+
+ public pmfree
+
+pmfree:
+;
+; Get the selector for the data block to free and ensure that it
+; is a valid selector.
+ mov ax,pmrg.xes
+ cmp ax,SEL_USER ;make sure it is in the user selector range
+ jb pmfr80
+ verw ax ;ensure that we were given a valid selector
+ jnz pmfr80 ; to a data block.
+ call FreeXmemBlock
+ jc pmfr80
+ mov pmrg.xes,0 ;the entry selector no longer is valid, so
+ ; change ES to return a legal value
+ jmp short pmfr90
+;
+; We were given an invalid selector to free.
+pmfr80: mov rmrg.xax,9 ;invalid memory selector error code
+ or byte ptr rmflags,1 ;Some there. Set CF
+ jmp short pmfr92
+;
+; All done
+pmfr90: and byte ptr rmflags, NOT 1
+pmfr92: call xfrflg
+ pop ax ;kill return from DOSENTRY
+ jmp LeaveDOSExtender
+
+; -------------------------------------------------------
+; pmmodb - modify extended memory block size
+
+ public pmmodb
+
+pmmodb:
+ mov cx,pmrg.xbx ;number of paragraphs requested
+ mov dx,cx
+ shl cx,4
+ shr dx,12
+ mov ax,pmrg.xes
+ cmp ax,SEL_USER ;make sure it is in the user selector range
+ jb pmod80
+ verw ax ;ensure that we were given a valid selector
+ jnz pmod80 ; to a data block.
+ xor bl,bl
+ call ModifyXmemBlock
+ jnc pmod90
+;
+; Error occured. Return carry flag set, error code in AX, and
+; size of largest available memory block in BX.
+ mov rmrg.xax,ax ;save error code
+ shr cx,4 ;divide return value by 16
+ mov al,dh
+ shl al,4
+ or ch,al
+ shr dx,4
+ jz pmod22 ;see if more than 1 Meg available
+ mov cx,0FFFFh ;and limit it to 1 meg if so
+pmod22: mov rmrg.xbx,cx ;store memory size available
+ or byte ptr rmflags,1 ;Some there. Set CF
+ jmp short pmod92
+;
+; We were given an invalid selector to modify.
+pmod80: mov rmrg.xax,9 ;invalid memory selector error code
+ or byte ptr rmflags,1 ;Some there. Set CF
+ jmp short pmod92
+;
+pmod90: and byte ptr rmflags,NOT 1
+pmod92: call xfrflg
+ pop ax
+ jmp LeaveDOSExtender
+
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; Terminate process -- This routine replaces the apps
+; termination vector in the PSP with ours. This allows
+; us to clean up the dos extender.
+;
+; Entry: nothing
+; Exit: nothing
+; Uses: none
+;
+ assume ds:dgroup,es:nothing
+ public TerminateProcess
+TerminateProcess proc near
+ pusha
+ push es
+ ;
+ ; Get the childs PSP (bugbug do we need to get the current psp?)
+ ;
+
+ SwitchToRealMode
+
+ mov ah,51h
+ pushf
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],offset tp_10
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+tp_10: FSTI
+
+ ;
+ ; Change the termination vector to point to the dos extender
+ ;
+ mov es,bx
+ mov bx,es:[0ah]
+ mov ax,es:[0ch]
+ mov cx,offset ChildTerminationHandler
+ mov es:[0ah],cx
+ mov cx,segDXCode
+ mov es:[0ch],cx
+
+ ;
+ ; Save the old termination vector for restoration later
+ ;
+ mov cx,segCurrentHostData
+ mov es,cx
+ mov word ptr es:[HdPspTerminate],bx
+ mov word ptr es:[HdPspTerminate+2],ax
+
+ SwitchToProtectedMode
+
+ pop es
+ popa
+ ret
+
+TerminateProcess endp
+
+DXPMCODE ends
+
+;****************************************************************
+
+ end
diff --git a/private/mvdm/dpmi/dxmmgr.asm b/private/mvdm/dpmi/dxmmgr.asm
new file mode 100644
index 000000000..befb2842a
--- /dev/null
+++ b/private/mvdm/dpmi/dxmmgr.asm
@@ -0,0 +1,2740 @@
+ PAGE ,132
+ TITLE DXMMGR - Dos Extender Memory Management Routines
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXMMGR.ASM - Dos Extender Memory Manager *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains routines for maintaining a dynamic *
+;* memory heap in extended memory for use in the Dos Extender. *
+;* *
+;* There are two kinds of objects in the memory heap. There *
+;* are block headers and data blocks. The block headers are *
+;* each 16 bytes long, and are organized in a doubly linked *
+;* list. There is a segment descriptor in the global *
+;* descriptor table for each block header, with each header *
+;* containing the selectors for the next and previous block *
+;* to form the list. Block headers can either control a free *
+;* block or they can control a data block. In either case, *
+;* the block immediately follows the header in memory, and *
+;* the header contains a field that gives its size. Free *
+;* blocks do not have a descriptor in the global descriptor *
+;* table, but in use data blocks do. In the case where the *
+;* in use data block is larger than 64k, there will be a *
+;* contiguous range of selectors for the block. The block *
+;* header contains a field giving the initial selector for *
+;* the data block for in use blocks. *
+;* *
+;* A special type of free block serve as sentinels. They mark *
+;* start and end of each physical block of memory available to *
+;* the memory manager. They are identified by the reserved *
+;* value of 0FFFE (start sentinel) and 0FFFF (end sentinel) in *
+;* the selBlkOwner field. Unlike other blocks, they may not be *
+;* moved without appeal to some higher level of memory *
+;* allocation. *
+;* *
+;* Except for a pair of sentinels, a pair of blocks are *
+;* contiguous in memory if and only if their headers are *
+;* adjacent in the header chain. *
+;* *
+;* The blocks at the start and end of the header chain are *
+;* sentinals. Their selectors are stored in global variables. *
+;* The start and end of the block header chain are identified *
+;* by NULL (0) pointers in the Next and Previous pointer fields*
+;* respectively. *
+;* *
+;****************************************************************
+;* Naming Conventions Used: *
+;* *
+;* The following hungarian prefixes are used in this module: *
+;* lp - far pointer (selector:offset) *
+;* bp - linear byte pointer (32 bit byte offset from *
+;* the beginning of memory) *
+;* p - near pointer (16 bit offset) *
+;* sel - protected mode segment selector *
+;* seg - real mode paragraph address *
+;* cb - count of bytes *
+;* id - generic byte that contains an ID code *
+;* hmem - handle to XMS extended memory block *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 11/28/90 amitc IntLowHeap allocates memory from Switcher *
+;* if it can. *
+;* *
+;* 08/08/90 earleh DOSX and client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 05/09/90 jimmat Started VCPI changes. *
+;* 05/09/90 jimmat Incorporated bug fix from CodeView folks, *
+;* changes marked with [01] *
+;* 06/23/89 jimmat: Added init of variable with heap handle *
+;* 04/17/89 jimmat: Added routines for special low memory *
+;* heap for use by network mapping *
+;* 03/28/89 jimmat: Incorporated bug fixes from GeneA *
+;* 02/10/89 (GeneA): changed Dos Extender from small model to *
+;* medium model *
+;* 12/07/88 (GeneA): moved SetupHimemDriver to dxinit.asm *
+;* 08/30/88 (GeneA): created *
+;* *
+;****************************************************************
+
+.286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+include gendefs.inc
+include segdefs.inc
+include pmdefs.inc
+include woaswapi.inc
+if VCPI
+include dxvcpi.inc
+endif
+include dpmi.inc
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn XMScontrol:FAR
+
+pmxmssvc macro fcn
+ ifnb <fcn>
+ mov ah, fcn
+ endif
+ call XMScontrol
+endm
+
+ extrn FreeSelector:NEAR
+ extrn FreeSelectorBlock:NEAR
+ extrn AllocateSelector:NEAR
+ extrn AllocateSelectorBlock:NEAR
+ extrn GetSegmentAddress:NEAR
+ extrn SetSegmentAddress:NEAR
+ extrn DupSegmentDscr:NEAR
+externFP NSetSegmentDscr
+ extrn RemoveFreeDescriptor:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn EnterRealMode:NEAR
+ extrn MoveMemBlock:NEAR
+ifdef WOW_x86
+externNP NSetSegmentAccess
+externNP NSetSegmentBase
+externNP NSetSegmentLimit
+endif
+
+DXDATA segment
+
+ extrn selPSPChild:WORD
+ extrn selGDT:WORD
+ extrn bpGDT:FWORD
+ extrn rglpfnRmISR:DWORD
+if VCPI
+ extrn fVCPI:byte
+endif
+
+ifdef WOW_x86
+ extrn FastBop:fword
+endif
+DXDATA ends
+
+; -------------------------------------------------------
+; DATA SEGMENT DECLARATIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+; Minimum xmem allocation in K
+; Note: This value MUST be a power of 2!!!
+; Note: We will allocate a smaller block if only smaller blocks
+; are available from xms
+XMEM_MIN_ALLOC equ 256
+
+ extrn lpfnXmsFunc:DWORD
+;
+; These variables control access to the extended memory heap.
+
+ public cKbInitialHeapSize
+cKbInitialHeapSize dw -1 ; Optional specification for first block
+ ; of extended memory in KB.
+
+ public dsegCurrent
+dsegCurrent dw 0 ; Paragraph count if the current block comes from DOS
+
+ public hmemHeap
+hmemHeap dw ? ;XMS memory manager handle to the memory
+ ; block containing the heap
+bpHeapStart dd ? ;byte address of start of heap
+ ; (points to block header of first heap block)
+bpHeapEnd dd ? ;byte address of end of heap
+ ; (points to block header of last heap block)
+cbHeapMove dd ? ;number of bytes by which a subheap moved
+
+ public cbHeapSize,selLowHeapStart,selHiHeapStart,selHeapStart
+cbHeapSize dd ? ;number of bytes in the heap
+
+selHeapStart dw ? ;selector for the sentinel block at the
+ ; start of the heap list
+bpCurTop dd 0 ;current top of compacted heap
+ ; (used during heap compaction)
+
+cbHeapBlkMax dd ? ;size of largest free block seen while
+ ; searching the heap
+
+cbHeapFreeSpace dd ? ;total free space in the heap
+
+selHiHeapStart dw ? ;selector for the sentinel block at the start
+ ; of the higm memory heap
+selLowHeapStart dw ? ;selector for the sentinel block at the start
+ ; of the special low heap
+segLowHeap dw ? ;segment address of low memory heap
+selLowHeapEnd dw ? ;selector for the sentinel block at the end
+ ; of the special low heap
+fDosErr db ?
+
+ public hmem_XMS_Table,hmem_XMS_Count
+
+hmem_XMS_Table dw CXMSBLOCKSDX DUP(0)
+hmem_XMS_Count dw 0
+
+ public NoAsyncSwitching
+
+NoAsyncSwitching db 0 ;0=> async switching allowed
+
+ public LowMemAllocFn,LowMemFreeFn
+
+LowMemAllocFn dd 0
+LowMemFreeFn dd 0
+
+DXDATA ends
+
+; -------------------------------------------------------
+; MISCELLANEOUS DATA STRUCTURE DECLARATIONS
+; -------------------------------------------------------
+
+; ****** also in dxvcpi.asm ******
+
+.ERRE CB_MEMHDR EQ 10h ;size of memory block header
+
+; ****** also in dxvcpi.asm ******
+
+; This segment declaration describes the format of the memory
+; block header at the beginning of each object in the heap.
+
+MEMHDR segment at 0 ;Analogous to MS-DOS arenas
+
+fBlkStatus db ? ;Status bits about the arena block
+cselBlkData db ? ;number of selectors used by the data block
+ ; that this arena header controls
+selBlkData dw ? ;initial selector for the data block that this
+ ; arena header controls.
+ ; (if the block is >64k, this is the first of the
+ ; range of selectors assigned to the block)
+selBlkOwner dw ? ;PSP selector of owner. 0 if free.
+;
+; !!!!! There is code in ModifyXmemBlock which depends on the previous
+; !!!!! four fields (and only those) being at lower addresses than selBlkHdr.
+;
+selBlkHdr dw ? ;the selector of the block header. This points
+ ; back at itself, so that we can find the selector
+ ; to the header from its address
+selBlkNext dw ? ; next block header selector
+selBlkPrev dw ? ; previous block header selector
+cbBlkLen LABEL DWORD ; Block size in bytes
+hBlockHandle dw ? ; The handle returned by DOS or HIMEM
+dsegBlock dw ? ; 0 => HIMEM ELSE DOS paragraph count
+
+MEMHDR ends
+
+
+; -------------------------------------------------------
+ subttl Initialization Routines
+ page
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+if VCPI
+externNP FreeVCPIHeap
+externNP VCPISpace
+externNP AddVCPIHeap
+endif
+
+; -------------------------------------------------------
+; INITIALIZATION ROUTINES
+; -------------------------------------------------------
+;
+; InitLowHeap -- Allocate and initialize the special low
+; memory heap for use by network buffers
+; and the like.
+;
+; Input: AX - number of K bytes of LOW heap needed
+; (assumed to be < 1 Meg)
+; Output: none
+; Errors: CY set if error occurs
+; Uses: All registers preserved
+;
+; Note: This routine must be called in protected mode
+; and it assumes that interrupts should be enabled
+; while it runs.
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public InitLowHeap
+
+InitLowHeap proc near
+
+ ret
+
+InitLowHeap endp
+
+; -------------------------------------------------------
+; AddToXmemHeap -- This routine will add a block of memory
+; to the extended memory heap. It creates the sentinel header
+; blocks and a header block for a free object containing
+; the new memory.
+;
+; Input: CX - Least significant part of minimum block length required
+; DX - Most significant part of minimum block length required
+; Output: None
+; Errors: None
+; Uses:
+; NOTES: This routine must be called in protected mode.
+; With XMS-only providing memory, all XMS memory is allocated
+; the first time this routine is called.
+;
+; If VCPI is active, and VCPI memory is being used, then
+; enough memory is allocated from the server to satisfy
+; the request. The allocation is handled in dxvcpi.asm.
+;
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+cProc AddToXmemHeap,<NEAR,PUBLIC>,<ax,bx,cx,dx,si,es>
+cBegin
+
+if VCPI
+ cmp fVCPI, 0 ;running under a VCPI server?
+ jz atxh100
+ cCall AddVCPIHeap ;yes, alloc memory from server
+ jc atxhDone ;no luck from vcpi
+ jmp atxh300 ;rejoin common code
+
+ ; should have 32 bit address in bx:dx when coming out
+ ; of AddVCPIHeap
+@@:
+endif
+
+;
+; Add to requested size the size of our header overhead
+;
+ add cx,3*CB_MEMHDR ; Adjust for header overhead
+ adc dx,0
+;
+; Round allocation up to next MIN_ALLOC size by first rounding up to the
+; nearest 1K, then converting to K and rounding
+;
+
+ add cx,1023
+ adc dx,0
+ shr cx,10
+ push dx
+ shl dx,6
+ or cx,dx ; cx is now size rounded to K
+ pop dx
+ add cx,XMEM_MIN_ALLOC - 1
+ adc dx,0
+ and cx,NOT (XMEM_MIN_ALLOC - 1) ; rounded to next higher MIN_ALLOC
+
+
+; See How much memory is available from the HIMEM driver
+
+atxh100:
+ pmxmssvc 8 ; query freespace available
+ cmp bl,0 ; Test for error
+ jnz atxhDone ; No luck - Try DOS
+ cmp ax,0
+ je atxhDone
+
+; cmp ax,cx ; is largest block > allocation desired?
+; jb atxh110 ; no, use largest block
+
+; mov ax,cx ; yes, use desired alloc
+atxh110:
+ mov dx,ax ; Load reported size of largest free block
+ mov bx,ax ; Calculate number of bytes available
+ shl bx,10
+ shr ax,6 ; AX:BX = available bytes
+
+ sub bx,3*CB_MEMHDR ; Adjust for header overhead
+ sbb ax,0
+ mov word ptr cbHeapSize,bx ; Save lower order bytes available
+ mov word ptr [cbHeapSize+2],ax ; Save higher order bytes available
+
+ pmxmssvc 9 ; Allocate the largest possible block
+ cmp ax,0 ; Check for error
+ jz atxhDone ; HIMEM driver speak with forked tongue
+ mov hmemHeap,dx ; Save the handle
+
+ pmxmssvc 0Ch ; Lock and query address
+ xchg bx,dx
+ mov dsegCurrent,0 ; Flag this allocate was not from DOS
+ cmp ax,0 ; Test result
+ jnz atxh300 ; Rejoin common code
+
+ Trace_Out "AddToXmemHeap: Lock of XMS handle #DX failed."
+
+ jmp atxhDone ; Jump on error
+
+atxh300:
+
+ call StructureHeap ; Build sentinels and free block
+
+ mov ax,selHeapStart ; Remember the high heap start selector
+ mov selHiHeapStart,ax
+
+if VCPI
+ cmp fVCPI, 0 ;running under a VCPI server?
+ jnz atxhDone ;Yes, AddVCPIHeap should have got it all
+endif
+; jmp short atxh100
+
+; Done allocating memory.
+
+atxhDone:
+
+cEnd
+
+; -------------------------------------------------------
+; InitXmemHeap -- This routine will initialize the
+; extended memory heap.
+;
+; Input:
+; Output:
+; Errors:
+; Uses:
+;
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public InitXmemHeap
+
+InitXmemHeap:
+ ret
+
+IFNDEF WOW_x86
+; -------------------------------------------------------
+; FreeXmemHeap -- This routine gives the xms memory back to
+; the xms driver. The selectors for the heap are not
+; freed. Shortly after we release the heap, the LDT will
+; be reinitialized, and that will free the selectors
+;
+ public FreeXmemHeap
+FreeXmemHeap:
+ ret ; bugbug
+ENDIF
+; -------------------------------------------------------
+; StructureHeap -- This routine creates the sentinel header
+; blocks and a header block for a free object containing
+; the new memory.
+;
+; Input: BX - Most significant part of heap block address
+; DX - Least significant part of heap block address
+;
+; Output: None
+; Errors: Carry flag set if there was not enough memory available
+; Uses: BX, DX
+;
+; NOTE: This routine must be called in protected mode.
+;
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public StructureHeap
+
+StructureHeap proc near
+
+ push ax
+ push cx
+ push es
+
+ push bx ; most significant part of address
+ push dx ; least significant part of address
+
+; Allocate selectors for the two sentinel blocks, and the initial free
+; block and link them at the start of the chain.
+;
+; Create the end sentinel
+
+ add dx,word ptr cbHeapSize ;Calculate address of end sentinel
+ adc bx,word ptr [cbHeapSize + 2]
+ add dx,2*CB_MEMHDR
+ adc bx,0
+ call AddSelectorToChain ;Allocate/link the end sentinel
+
+; Initialize the ending sentinel block.
+
+ assume es:MEMHDR
+ xor ax,ax
+ mov fBlkStatus,al
+ mov cselBlkData,al
+ mov selBlkData,ax
+ mov selBlkOwner,0FFFFh
+ mov cx,hmemHeap ; Save handle
+ mov hBlockHandle,cx
+ mov cx,dsegCurrent ; Save paragraph count
+ mov dsegBlock,cx
+
+; Create the free block
+
+ pop dx ; least significant part of address
+ pop bx ; most significant part of address
+ add dx,CB_MEMHDR ; Calculate address of Free block
+ adc bx,0
+ call AddSelectorToChain ; Allocate and link the Free Block
+
+; Initialize the header for the free data block.
+
+ mov fBlkStatus,al
+ mov cselBlkData,al
+ mov selBlkData,ax
+ mov selBlkOwner,ax
+ mov cx,word ptr [cbHeapSize]
+ mov word ptr [cbBlkLen],cx
+ mov cx,word ptr [cbHeapSize+2]
+ mov word ptr [cbBlkLen+2],cx
+
+; Create the starting sentinel
+
+ sub dx,CB_MEMHDR ; Calculate address of start sentinel
+ sbb bx,0
+ call AddSelectorToChain ; Allocate and link the start sentinel
+
+; Initialize the starting sentinel block.
+
+ mov fBlkStatus,al
+ mov cselBlkData,al
+ mov selBlkData,ax
+ mov selBlkOwner,0FFFEh ;mark it as in use
+ mov cx,hmemHeap ; Save handle
+ mov hBlockHandle,cx
+ mov cx,dsegCurrent ; Save paragraph count
+ mov dsegBlock,cx
+
+ pop es
+ pop cx
+ pop ax
+
+ ret
+
+StructureHeap endp
+
+
+; -------------------------------------------------------
+; AddSelectorToChain -- This function will create a header block at a
+; specified address and link it to the head of the
+; header chain. It is the caller's responsibility
+; to initialize all fields in the header other
+; than the chain link pointers themselves.
+;
+; This function can only be called in protected mode.
+;
+; Input: BX - Most significant part of header address
+; DX - Least significant part of header address
+; Output: ES - selector for the new header
+; Errors: Carry flag set on error
+; Uses: all registers except ES preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AddSelectorToChain
+
+AddSelectorToChain:
+ push ax ; Save callers regs
+ push bx ;
+ push cx ;
+ call AllocateSelector ; Get a free selector
+ jc astcFail ; Jump on error
+ mov cx,CB_MEMHDR - 1 ;
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ mov cx,selHeapStart ; Load old start of chain
+ jcxz @f
+ mov es,cx ; Point to old start of chain
+ assume es:MEMHDR ;
+ mov selBlkPrev,ax ; Link to new start of chain
+@@:
+ mov es,ax ; Point to new head of chain
+ mov selBlkNext,cx ; Link it to old head
+ mov selBlkPrev,0 ; NULL back pointer
+ mov selBlkHdr,ax ; Block header points to itself
+ mov selHeapStart,ax ; Store new head of chain
+ clc ; Flag no error
+astcFail:
+ pop cx ; Restore Users regs
+ pop bx ;
+ pop ax ;
+ ret ; AddSelectorToChain
+
+
+; -------------------------------------------------------
+; AllocateXmem32 -- This function will return a 32-bit address and
+; handle of a block of memory allocated in extended
+; memory.
+;
+; Input: BX:CX - Size of block desired
+; DX - Owner of block
+; Output: BX:CX - Address of memory
+; SI:DI - handle of memory
+; Errors: Carry flag set on error
+; Uses:
+; -------------------------------------------------------
+
+ public AllocateXmem32
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+AllocateXmem32 proc near
+
+ FBOP BOP_DPMI, AllocXmem, FastBop
+ ret
+
+AllocateXmem32 endp
+
+
+; -------------------------------------------------------
+; FreeXmem32 -- This function will free a block of extended memory
+; allocated by AllocateXmem.
+;
+; Input: SI:DI - Handle of memory
+; Errors: Carry flag set on error
+; Uses:
+; -------------------------------------------------------
+
+ public FreeXmem32
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+FreeXmem32 proc near
+
+ FBOP BOP_DPMI, FreeXmem, FastBop
+ ret
+
+FreeXmem32 endp
+
+
+DXPMCODE ends
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; ReleaseLowHeap -- This routine will release the
+; special low memory heap. After this function is
+; called, InitLowHeap must be called again before
+; any other low heap functions can be used.
+;
+; Currently this routine doesn't bother to release
+; selectors used by the blocks in the low heap (like
+; ReleaseXmemHeap does) under the assumption that
+; the DOS extender is about to terminate. If you are
+; really going reinitialize the low heap with InitLowHeap,
+; then you should do more clean up here.
+;
+; Input: none
+; Output: none
+; Errors:
+; Uses: All preserved
+;
+; Note: This routine must be called in real mode!
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public ReleaseLowHeap
+
+ReleaseLowHeap proc near
+
+ push ax
+ push es
+
+ mov ax,segLowHeap ;make sure there really is a low heap
+ or ax,ax
+ jz rlh90
+
+ mov es,ax ;now use DOS to get rid of it
+ mov ah,49h
+ pushf
+ sub sp,8 ; make room for stack frame
+ push bp
+ mov bp,sp
+ push es
+ push ax
+
+ xor ax,ax
+ mov es,ax
+ mov [bp + 8],cs
+ mov [bp + 6],word ptr (offset rlh10)
+ mov ax,es:[21h*4]
+ mov [bp + 2],ax
+ mov ax,es:[21h*4 + 2]
+ mov [bp + 4],ax
+ pop ax
+ pop es
+ pop bp
+ retf
+
+rlh10: xor ax,ax ;just to be tidy
+ mov segLowHeap,ax
+ mov selLowHeapStart,ax
+
+rlh90:
+ pop es
+ pop ax
+ ret
+
+ReleaseLowHeap endp
+
+
+; -------------------------------------------------------
+; ReleaseXmemHeap -- This routine will release memory
+; used by the extended memory heap. After this function
+; is called, no extended memory manager routines can
+; be called except InitXmemHeap.
+;
+; Input:
+; Output:
+; Errors:
+; Uses:
+;
+; Note: Do NOT enable interrupts while in protected mode!
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public ReleaseXmemHeap
+
+ReleaseXmemHeap:
+ push ax
+ push bx
+ push cx
+ push dx
+ push ds
+ push es
+
+ SwitchToProtectedMode ; Needed for selector juggling
+
+ifdef MD
+ call CheckXmemHeap
+endif
+ mov bx,selHeapStart ; Point to the start of the block chain
+ mov es,bx ;
+ assume es:MEMHDR ;
+;
+; This is the start of a loop on all blocks. ES and BX both contain the
+; selector for the current block, or zero if at the end of the chain.
+rxh100:
+ or bx,bx ; End of chain?
+ jz rxhEnd ; Yes - Jump
+ mov cl,cselBlkData ; Load number of data segments
+ xor ch,ch ;
+ jcxz rxh200 ; Jump if nothing to do
+ mov ax,selBlkData ; Load first data segment
+ and ax,SELECTOR_INDEX ; Treat like GDT entry for FreeSelector
+rxh110:
+ push ax ; Since it is destroyed by FreeSelector
+ call FreeSelector ; Free each of the data selectors
+ pop ax ;
+ add ax,8 ; Point to the next data descriptor
+ loop rxh110 ;
+;
+; All data descriptors for this block now free (if there ever were any)
+rxh200:
+ push selBlkNext ; Push pointer to the next block in the chain
+ push es ; Push the pointer to this one
+ cmp selBlkOwner,0FFFFh ; Is this an end sentinel ?
+ jne rxh300 ; No - jump
+;
+; Time to free a HIMEM allocated memory blcok
+rxh210:
+if VCPI
+ cmp fVCPI, 0
+ jnz rxh300
+endif
+ mov dx,hBlockHandle ;
+ push dx ; Since the data may move after the unlock
+ ASSUME es:NOTHING ;
+ pmxmssvc 0Dh,disable ;unlock the memory block containing the heap
+ pop dx ;
+ pmxmssvc 0Ah,disable ;free the block
+;
+; Time to free the header selector.
+rxh300:
+ pop ax ; Retrieve the selector for the current header
+ pop bx ; Retrieve the selector for the next one
+ mov es,bx ;
+ call FreeSelector ; Free the selector for the current header
+ jmp rxh100 ; Loop for the next block
+;
+; All done
+rxhEnd:
+ mov selHeapStart,0 ; Point to the start of the block chain
+ifdef MD
+ call CheckXmemHeap
+endif
+if VCPI
+ cmp fVCPI, 0
+ jz @F
+.386
+ cCall FreeVCPIHeap
+.286p
+@@:
+endif
+ SwitchToRealMode ; Restore callers environment
+ sti ;
+ pop es
+ pop ds
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl Main Routines
+ page
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; MAIN MEMORY MANAGEMENT ROUTINES
+; -------------------------------------------------------
+;
+; AllocateLowBlock -- This function will allocate a block
+; of memory from the special low memory heap.
+; If the requested block is larger than 64k, multiple segment
+; descriptors will be allocated, one for each full 64k and one
+; for whatever is left over. When multiple segment descriptors
+; are created, the selectors will differ by 8 for each segment.
+; (i.e. the returned selector accesses the first 64k, add 8 to
+; get to the next 64k, etc.)
+;
+; Input: CX - low word of requested block size
+; DX - high word of requested block size
+; Output: AX - initial selector for the memory block
+; Errors: returns CY set and size of largest free memory block in CX,DX.
+; This can occur either because there isn't a large enough
+; free block in the memory pool, or there isn't a contiguous
+; range of free segment descriptors that is large enough.
+; Uses: AX, all else preserved. Modifies the segment descriptors
+; for the SEL_SCR0 and SEL_SCR1 segments
+;
+; Note: This routine is _very_ similar to AllocateXmemBlock,
+; but just different enough that it's a separate routine.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AllocateLowBlock
+
+AllocateLowBlock proc near
+
+ push bp
+ mov bp,sp
+ sub sp,14
+ push es
+ push si
+ push bx
+ push cx
+ push dx
+
+Segm equ word ptr [bp - 2]
+StartSel equ word ptr [bp - 4]
+Largest equ word ptr [bp - 6]
+MemSize equ [bp - 10]
+cSelector equ word ptr [bp - 12]
+selHeader equ word ptr [bp - 14]
+
+ mov word ptr MemSize,cx
+ mov word ptr MemSize + 2,dx
+
+;
+; See if we need to use wow kernel to manage low memory
+;
+ mov ax,word ptr [LowMemAllocFn]
+ or ax,word ptr [LowMemAllocFn + 2]
+ je alm3
+ jmp alm130
+;
+; Round up to the next paragraph
+;
+alm3: add cx,15
+ adc dx,0
+ and cx,0FFF0h
+
+;
+; Form a paragraph count
+;
+ mov bx,cx
+ shr bx,4
+ shl dx,12
+ or bx,dx
+
+;
+; Add one to make room for the heap header
+;
+ cmp bx,0ffffh
+ je alm5 ; won't succed, but get size
+
+ inc bx
+alm5:
+;
+; Switch to real mode and allocate the memory
+;
+ SwitchToRealMode
+
+ mov ax,4800h
+ int 21h ; call dos
+
+ jnc alm10
+
+ mov Segm,ax
+ mov Largest,bx
+ mov cx,0
+ jmp alm20
+;
+; Remember the values returned
+;
+
+alm10: mov Segm,ax
+ mov cx,1
+
+alm20: SwitchToProtectedMode
+ cmp cx,0
+ jne alm40
+
+ ;
+ ; return an error
+ ;
+
+alm30: mov cx,bx
+ mov dx,bx
+ shr dx,12
+ shl cx,4 ; form bytes available
+ mov ax,Segm ; actually error code for fail
+ add sp,4 ; skip cx,dx
+ stc
+ jmp alm110
+
+alm40: mov ax,word ptr MemSize + 2
+ mov bx,word ptr MemSize
+
+ or bx, bx
+ jnz short alm44
+ or ax, ax
+ jz short alm45 ; if zero then don't adjust
+
+alm44:
+ sub bx,1
+ sbb ax,0 ; go from size to limit
+alm45:
+ mov word ptr MemSize + 2,ax
+ mov word ptr MemSize,bx
+ inc ax ; we always want 2 more
+ inc ax
+ mov cSelector,ax
+ xor cx, cx ;allocate from lower range
+ call AllocateSelectorBlock
+ jnc alm50
+
+ mov ax,8 ; insufficient memory
+ mov bx,Largest
+ jmp alm30
+
+alm50: or ax,STD_RING
+ mov si,ax
+ mov StartSel,ax
+ mov ax,word ptr MemSize + 2
+ mov bx,word ptr MemSize ; ax:bx - block size
+ mov cx,Segm
+ mov dx,cx
+ shr cx,12
+ shl dx,4 ; cx:dx - block base
+ ;
+ ; Set up the first one to have the entire limit
+ ;
+ cCall NSetSegmentDscr,<si,cx,dx,ax,bx,STD_DATA>
+
+ ;
+ ; Set up the rest to have 64K limits
+ ;
+
+ dec ax ; already set one
+ cmp ax,0FFFFh
+ je alm80
+
+ cmp ax,0
+ je alm70
+
+ mov di,0FFFFh
+alm60: add si,8
+ inc cx ; add 64K to base
+
+ cCall NSetSegmentDscr,<si,cx,dx,0,di,STD_DATA>
+
+ dec ax
+ cmp ax,0
+ jne alm60
+
+ ;
+ ; Just one selector left, so set the correct limit
+ ;
+
+alm70: add si,8
+ inc cx
+ cCall NSetSegmentDscr,<si,cx,dx,0,bx,STD_DATA>
+
+ ;
+ ; Set up header
+ ;
+
+alm80: mov ax,Segm
+ mov bx,ax
+ shr ax,12
+ shl bx,4 ; ax:bx = base of header
+ add bx,word ptr MemSize
+ adc ax,word ptr MemSize + 2
+ add si,8
+ cCall NSetSegmentDscr,<si,ax,bx,0,CB_MEMHDR,STD_DATA>
+
+ ;
+ ; Set up values in header and add to chain
+ ;
+ mov es,si
+ mov selHeader,si
+ assume es:MEMHDR
+ mov [selBlkHdr],si
+ mov ax,StartSel
+ mov [selBlkData],ax
+ mov ax,cSelector
+ mov [cselBlkData],al
+ mov ax,selPSPChild
+ mov [selBlkOwner],ax
+ mov ax,Segm
+ mov [hBlockHandle],ax
+ mov ax,selLowHeapStart
+ mov [selBlkNext],ax
+ mov bx,0
+ mov [selBlkPrev],bx
+ or ax,ax
+ jz alm100
+
+ mov bx,es
+ mov es,ax
+ mov [selBlkPrev],bx
+
+ ;
+ ; set up return values
+ ;
+alm100: mov ax,selHeader
+ mov selLowHeapStart,ax
+ mov ax,StartSel
+ clc
+alm105: pop dx
+ pop cx
+alm110: pop bx
+ pop si
+ pop es
+ mov sp,bp
+ pop bp
+ ret
+
+alm130: push dx
+ push cx
+ call [LowMemAllocFn]
+ or ax,ax
+ jz alm140
+ clc
+ jmp alm105
+
+alm140: xor cx,cx
+ stc
+ add sp,4
+ jmp alm110
+
+AllocateLowBlock endp
+
+
+; -------------------------------------------------------
+; FreeLowBlock -- This function will free the low heap
+; memory block specified by the given selector. It
+; will return the memory block to the free memory pool,
+; and release all selectors used by this block.
+;
+; Input: AX - selector of the data block to free
+; Output: none
+; Errors: returns CY set if invalid selector
+; Uses: AX, all other registers preserved
+; Modifies the descriptor for segment SEL_SCR0
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FreeLowBlock
+
+FreeLowBlock proc near
+ push bp
+ mov bp,sp
+ sub sp,4
+Segm equ word ptr [bp - 2]
+HeaderSel equ word ptr [bp - 4]
+ push es
+ push bx
+ push cx
+
+ ;
+ ; See if we need to use the wow kernel to manage memory
+ ;
+ mov bx,word ptr [LowMemFreeFn]
+ or bx,word ptr [LowMemFreeFn + 2]
+ jz flm5
+ jmp flm130
+
+ ;
+ ; search for the block to free
+ ;
+flm5: mov bx,selLowHeapStart
+flm10: or bx,bx ; any low blocks?
+ jz flm100
+
+ mov es,bx
+ assume es:MEMHDR
+
+ cmp [selBlkData],ax
+ je flm30 ; found the correct block
+
+ mov bx,[selBlkNext]
+ jmp flm10
+
+ ;
+ ; Unlink the block from the list
+ ;
+flm30: mov ax,es
+ mov HeaderSel,ax
+
+ mov ax,[selBlkPrev]
+ mov bx,[selBlkNext]
+ cmp ax,0
+ je flm40
+
+ mov es,ax
+ mov [selBlkNext],bx
+ jmp flm50
+
+flm40: mov SelLowHeapStart,bx
+flm50: cmp bx,0
+ je flm60
+
+ mov es,bx
+ mov [selBlkPrev],ax
+
+flm60: mov ax,HeaderSel
+ mov es,ax
+ mov ax,[hBlockHandle]
+ mov Segm,ax
+
+ mov ax,[selBlkData]
+ xor cx,cx
+ mov cl,[cselBlkData]
+ push 0
+ pop es
+ call FreeSelectorBlock
+
+ SwitchToRealMode
+ mov ax,Segm
+ mov es,ax
+ mov ax,4900h
+ int 21h ; free the memory
+
+ push ax ; save return code
+ jnc flm70
+
+ mov cx,0 ; error
+ jmp flm80
+
+flm70: mov cx,1 ; no error
+flm80: SwitchToProtectedMode
+ pop ax
+ cmp cx,0
+ je flm100 ; error path
+
+flm85: clc
+flm90: pop cx
+ pop bx
+ pop es
+ mov sp,bp
+ pop bp
+ ret
+
+flm100: stc
+ jmp flm90
+
+flm130: push ax
+ call [LowMemFreeFn]
+ or ax,ax
+ jz flm85
+
+ mov ax,9
+ stc
+ jmp flm90
+
+FreeLowBlock endp
+
+; -------------------------------------------------------
+;
+; AllocateXmemBlock -- This function will allocate a block
+; of extended memory and return selectors for accessing it.
+; If the requested block is larger than 64k, multiple segment
+; descriptors will be allocated, one for each full 64k and one
+; for whatever is left over. When multiple segment descriptors
+; are created, the selectors will differ by 8 for each segment.
+; (i.e. the returned selector accesses the first 64k, add 8 to
+; get to the next 64k, etc.)
+;
+; Input: BL - flag controlling allocation of selectors,
+; if 0 - a selector will be allocated for each 64k
+; if != 0 - only one selector will be allocated to
+; the block.
+; CX - low word of requested block size
+; DX - high word of requested block size
+; Output: AX - initial selector for the memory block
+; Errors: returns CY set and size of largest free memory block in CX,DX.
+; This can occur either because there isn't a large enough
+; free block in the memory pool, or there isn't a contiguous
+; range of free segment descriptors that is large enough.
+; Uses: AX, all else preserved. Modifies the segment descriptors
+; for the SEL_SCR0 and SEL_SCR1 segments
+;
+; Notes: Hard coding an int 3 in this routine is fatal. This routine
+; is used to allocate the stack used for exception handling.
+;
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AllocateXmemBlock
+
+AllocateXmemBlock proc near
+
+axb1: push bp
+ mov bp,sp
+ sub sp,20
+ push es
+ push bx
+ push si
+ push di
+ push cx
+ push dx
+
+AllocFlag equ byte ptr [bp - 2]
+cSelector equ word ptr [bp - 4]
+MemHandle equ [bp - 8]
+MemSize equ [bp - 12]
+MemAddr equ [bp - 16]
+StartSel equ word ptr [bp - 18]
+HeaderSel equ word ptr [bp - 20]
+
+ mov AllocFlag,bl
+ mov word ptr MemSize,cx
+ mov word ptr MemSize + 2,dx
+ mov word ptr MemHandle,0
+ mov word ptr MemHandle + 2,0
+
+ ;
+ ; Save room for header
+ ;
+ add cx,16
+ adc dx,0
+
+ mov bx,dx
+ mov dx, selPSPChild
+
+ call AllocateXmem32
+
+ jnc axb40
+ jmp axb130
+
+axb40: mov word ptr MemAddr,cx
+ mov word ptr MemAddr + 2,bx
+ mov word ptr MemHandle,di
+ mov word ptr MemHandle + 2,si
+ ;
+ ; Change size to limit
+ ;
+ mov ax,word ptr MemSize + 2
+ mov bx,word ptr MemSize
+ sub bx,1
+ sbb ax,0 ; size -> limit
+ mov word ptr MemSize,bx
+ mov word ptr MemSize + 2,ax
+
+ ;
+ ; Figure out how many selectors to allocate
+ ;
+ cmp AllocFlag,0
+ jne axb50
+
+ inc ax
+ jmp axb60
+
+axb50: mov ax,1
+
+ ;
+ ; Add one additional for the header block
+ ;
+axb60: inc ax
+ mov cSelector,ax
+
+ ;
+ ; Allocate the selectors
+ ;
+ xor cx,cx ; allocate from lower range
+ call AllocateSelectorBlock
+ jnc axb65
+
+ jmp axb120
+
+axb65: or ax,STD_RING
+ mov StartSel,ax
+
+ ;
+ ; Set up the first selector to have the entire limit
+ ;
+ mov di,cSelector
+ mov si,ax
+ mov ax,word ptr MemSize + 2
+ mov bx,word ptr MemSize
+ mov cx,word ptr MemAddr + 2
+ mov dx,word ptr MemAddr
+ add dx,16 ; room for header
+ adc cx,0
+ cCall NSetSegmentDscr,<si,cx,dx,ax,bx,STD_DATA>
+ dec di
+ dec ax
+ add si,8
+
+ cmp di,1
+ je axb90
+
+ ;
+ ; Set up the rest with 64K limits
+ ;
+axb70: cmp ax,0
+ je axb80
+
+ inc cx
+ cCall NSetSegmentDscr,<si,cx,dx,0,0FFFFh,STD_DATA>
+ dec ax
+ add si,8
+ jmp axb70
+
+ ;
+ ; Set up the last one with the remaining limit
+ ;
+axb80: inc cx
+ cCall NSetSegmentDscr,<si,cx,dx,0,bx,STD_DATA>
+ add si,8
+
+ ;
+ ; Set up Header selector
+ ;
+axb90: mov ax,word ptr MemAddr + 2
+ mov bx,word ptr MemAddr
+ cCall NSetSegmentDscr,<si,ax,bx,0,CB_MEMHDR,STD_DATA>
+ mov HeaderSel,si
+
+ ;
+ ; Set up header
+ ;
+ mov es,si
+ assume es:MEMHDR
+ mov [selBlkHdr],si
+ mov ax,StartSel
+ mov [selBlkData],ax
+ mov ax,cSelector
+ mov [cselBlkData],al
+ mov ax,selPSPChild
+ mov [selBlkOwner],ax
+ mov ax,MemHandle
+ mov [hBlockHandle],ax
+ mov ax,word ptr MemHandle + 2
+ mov [dsegBlock],ax ; use for high half handle
+ mov ax,selHiHeapStart
+ mov [selBlkNext],ax
+ mov bx,0
+ mov [selBlkPrev],bx
+ or ax,ax
+ jz axb100
+
+ mov bx,es
+ mov es,ax
+ mov [selBlkPrev],bx
+axb100: mov ax,HeaderSel
+ mov selHiHeapStart,ax
+
+ ;
+ ; return information to the caller
+ ;
+ clc
+ mov ax,StartSel
+ pop dx
+ pop cx
+axb110: pop di
+ pop si
+ pop bx
+ pop es
+ mov sp,bp
+ pop bp
+ ret
+
+ ;
+ ; Error
+ ;
+
+axb120: mov dx,word ptr MemHandle
+ or dx,word ptr MemHandle + 2
+ jz axb130
+
+ mov di,word ptr MemHandle
+ mov si,word ptr MemHandle + 2
+
+ FBOP BOP_DPMI, FreeXmem, FastBop
+ ;
+ ; Get largest free block
+ ;
+axb130: pmxmssvc 08h
+ mov dx,ax
+ mov cx,ax
+ shl cx,10
+ shr dx,6
+ add sp,4 ; skip cx,dx on stack
+ stc
+ jmp axb110
+
+AllocateXmemBlock endp
+
+; -------------------------------------------------------
+; FreeXmemBlock -- This function will free the extended
+; memory block specified by the given selector. It
+; will return the memory block to the free memory pool,
+; and release all selectors used by this block.
+;
+; Input: AX - selector of the data block to free
+; Output: none
+; Errors: returns CY set if invalid selector
+; Uses: AX, all other registers preserved
+; Modifies the descriptor for segment SEL_SCR0
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FreeXmemBlock
+
+
+FreeXmemBlock:
+
+fxb1: push bp
+ mov bp,sp
+ sub sp,6
+HeaderSel equ word ptr [bp - 2]
+MemHandle equ [bp - 6]
+ push es
+ push bx
+ push si
+ push di
+ push dx
+ push cx
+
+ ;
+ ; search for the block to free
+ ;
+ mov bx,selHiHeapStart
+fxb10: or bx,bx ; any hi blocks?
+ jnz fxb20
+ jmp fxb100
+
+fxb20: mov es,bx
+ assume es:MEMHDR
+
+ cmp [selBlkData],ax
+ je fxb30 ; found the correct block
+
+ mov bx,[selBlkNext]
+ jmp fxb10
+
+ ;
+ ; Unlink the block from the list
+ ;
+fxb30: mov ax,es
+ mov HeaderSel,ax
+
+ mov ax,[selBlkPrev]
+ mov bx,[selBlkNext]
+ cmp ax,0
+ je fxb40
+
+ mov es,ax
+ mov [selBlkNext],bx
+ jmp fxb50
+
+fxb40: mov SelHiHeapStart,bx
+fxb50: cmp bx,0
+ je fxb60
+
+ mov es,bx
+ mov [selBlkPrev],ax
+
+fxb60: mov ax,HeaderSel
+
+ mov es,ax
+ mov dx,[dsegBlock] ; high half handle
+ mov word ptr MemHandle + 2,dx
+ mov dx,[hBlockHandle]
+ mov word ptr MemHandle,dx
+
+ ;
+ ; Free the selectors
+ ;
+ mov ax,[selBlkData]
+ xor cx,cx
+ mov cl,[cselBlkData]
+ push 0
+ pop es
+ assume es:nothing
+ call FreeSelectorBlock
+
+ mov si,word ptr MemHandle + 2
+ mov di,word ptr MemHandle
+
+ call FreeXmem32
+ clc
+
+fxb90: pop cx
+ pop dx
+ pop di
+ pop si
+ pop bx
+ pop es
+ mov sp,bp
+ pop bp
+ ret
+
+fxb100: stc
+ jmp fxb90
+
+; -------------------------------------------------------
+; ModifyXmemBlock -- This function will modify the size
+; of the specified extended memory block to the new
+; size specified by CX,DX. If the block is being
+; shrunk, the new size must be at least CB_HDRSIZ bytes
+; smaller than the old size, or nothing is done.If the
+; block grows beyond the next 64k boundary,
+; it may be necessary to allocate another segment
+; descriptor for it. If this is not possible, the grow
+; will fail.
+;
+; Input: AX - segment selector for the data block to modify
+; BL - flag controlling allocation of selectors to block,
+; if 0 - selectors will be allocated for each 64k
+; if != 0 - only one selector is allocated to block
+; CX - low word of new desired size
+; DX - high word of new desired size
+; Output: none
+; Errors: CY set if unable to accomplish the request.
+; returns error code in AX.
+; CX,DX will be set to the largest possible size that the
+; block could be modified to have
+; Uses: AX,CX,DX,Flags
+
+ assume ds:DGROUP,es:NOTHING
+ public ModifyXmemBlock
+
+ModifyXmemBlock proc near
+mxb1: push bp
+ mov bp,sp
+ sub sp,20
+HeaderSel equ word ptr [bp - 2]
+AllocFlags equ byte ptr [bp - 4]
+MemSize equ [bp - 8]
+MemAddr equ [bp - 12]
+Success equ word ptr [bp - 14]
+cSelector equ word ptr [bp - 16]
+BlockHandle equ word ptr [bp - 20]
+ push es
+ push bx
+ push cx
+ push dx
+
+ mov word ptr MemSize + 2,dx
+ mov word ptr MemSize,cx
+ mov AllocFlags,bl
+ mov Success,1
+
+ ;
+ ; Search for the block to resize
+ ;
+ mov bx,selHiHeapStart
+mxb10: or bx,bx ; any hi blocks?
+ jnz mxb15
+ jmp mxb140
+
+mxb15: mov es,bx
+ assume es:MEMHDR
+
+ cmp [selBlkData],ax
+ je mxb30 ; found the correct block
+
+ mov bx,[selBlkNext]
+ jmp mxb10
+
+ ;
+ ; Calculate the number of selectors needed
+ ;
+mxb30: mov HeaderSel,es
+ test AllocFlags,1
+ jne mxb40
+
+ sub cx, 1 ; size -> limit
+ sbb dx,0
+ inc dl
+ jmp mxb50
+
+mxb40: mov dl,1
+mxb50: inc dl ; always need 1 more
+
+ ;
+ ; See if we have enough selectors
+ ;
+ cmp dl,[cselBlkData]
+ jna mxb55
+ jmp mxb120
+
+mxb55: and dl,0ffh
+ mov cSelector,dx
+ mov dx,[dsegBlock]
+ mov word ptr BlockHandle + 2,dx
+ mov dx,[hBlockHandle]
+ mov word ptr BlockHandle,dx
+
+ mov bx,word ptr MemSize + 2
+ mov cx,word ptr MemSize
+ add cx,010h
+ adc bx,0
+ mov si,word ptr BlockHandle + 2
+ mov di,word ptr BlockHandle
+
+ FBOP BOP_DPMI, ReallocXmem, FastBop
+ jnc mxb60
+
+ mov Success,0
+
+mxb60: mov word ptr MemAddr,cx
+ mov word ptr MemAddr + 2,bx
+ mov word ptr BlockHandle,di
+ mov word ptr BlockHandle + 2,si
+ ;
+ ; Fix the base of the Header selector
+ ;
+ mov bx,word ptr MemAddr + 2
+ mov dx,word ptr MemAddr
+ mov ax,HeaderSel
+ call SetSegmentAddress
+ mov es,ax
+
+ ;
+ ; Fix the handle (may have changed
+ ;
+ mov dx,word ptr BlockHandle
+ mov [hBlockHandle],dx
+ mov dx,word ptr BlockHandle + 2
+ mov [dsegBlock],dx
+
+ ;
+ ; Change size to limit
+ ;
+ mov ax,word ptr MemSize + 2
+ mov bx,word ptr MemSize
+ sub bx,1
+ sbb ax,0 ; size -> limit
+ mov word ptr MemSize,bx
+ mov word ptr MemSize + 2,ax
+
+ ;
+ ; Set up the first selector to have the entire limit
+ ;
+ mov di,cSelector
+ mov si,[selBlkData]
+ mov ax,word ptr MemSize + 2
+ mov bx,word ptr MemSize
+ mov cx,word ptr MemAddr + 2
+ mov dx,word ptr MemAddr
+ add dx,16 ; room for header
+ adc cx,0
+ cCall NSetSegmentDscr,<si,cx,dx,ax,bx,STD_DATA>
+ dec di
+ dec ax
+ add si,8
+
+ cmp di,1
+ je mxb100
+
+ ;
+ ; Set up the rest with 64K limits
+ ;
+mxb80: cmp ax,0
+ je mxb90
+
+ inc cx
+ cCall NSetSegmentDscr,<si,cx,dx,0,0FFFFh,STD_DATA>
+ dec ax
+ add si,8
+ jmp mxb80
+
+ ;
+ ; Set up the last one with the remaining limit
+ ;
+mxb90: inc cx
+ cCall NSetSegmentDscr,<si,cx,dx,0,bx,STD_DATA>
+ add si,8
+
+ ;
+ ; Were we successfull?
+ ;
+mxb100: cmp Success,1
+ jne mxb130
+
+ clc
+ pop dx
+ pop cx
+mxb110: pop bx
+ pop es
+ mov sp,bp
+ pop bp
+ ret
+
+ ;
+ ; We had an error, not enough selectors. Figure out how large
+ ; it could have been
+ ;
+mxb120: xor dx,dx
+ xor cx,cx
+ mov dl,[cselBlkData]
+ dec dl ; don't count header sel
+ mov ax,8
+ add sp,4 ; pop cx,dx
+ stc
+ jmp mxb110
+
+ ;
+ ; We had an error calling xms. Get the largest block
+ ;
+mxb130: pmxmssvc 08h
+ mov cx,ax ; convert to bytes
+ mov dx,ax
+ shl cx,10
+ shr dx,6
+ mov ax,8
+ add sp,4
+ stc
+ jmp mxb110
+
+ ;
+ ; We had an error, invalid selector
+ ;
+mxb140: mov ax,9
+ add sp,4
+ stc
+ jmp mxb110
+
+ModifyXmemBlock endp
+ ret
+
+
+;
+; -------------------------------------------------------
+ subttl Utility Routines
+ page
+; -------------------------------------------------------
+; UTIITY ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; CalculateMaximumSegmentSpace -- This function will see if there
+; are enough free segments available
+; to expand a block of memory.
+; If not it returns with carry set
+; and the maximum space available.
+;
+; Input; ES - segment selector for the data block to modify
+; CX - low word of new desired size
+; DX - high word of new desired size
+;
+; Output CX - / If CY set: The maximum available
+; DX - \ If CY not set: Unchanged
+; Errors: CY set if unable to accomplish the request.
+; CX,DX will be set to the largest possible size that the
+; block could be modified to have
+; Uses: AX,CX,DX,Flags
+
+ assume ds:DGROUP,es:MEMHDR
+ public CalculateMaximumSegmentSpace
+
+CalculateMaximumSegmentSpace:
+ push bx
+ push es
+ xor bx,bx
+ mov bl,cselBlkData ; Allocated selector count
+ shl bx,3 ; Convert to byte offset
+ add bx,selBlkData ; Add starting data selector offset
+ and bx,SELECTOR_INDEX ; Treat it as a GDT selector
+ mov es,selGDT ; Base the global descriptor table
+ assume es:NOTHING ;
+
+; Count through the immediately following selectors
+
+cmssLoop:
+ cmp bx,word ptr bpgdt ; Off the end of the GDT ?
+ ja cmssOutOfSelectors ; Yes - Jump
+if 0
+ cmp es:[bx].arbSegAccess,0 ; Is the next selector free ?
+else
+ cmp word ptr es:[bx].arbSegAccess,0 ; Is the next selector free ?
+endif
+ jne cmssOutOfSelectors ; No - jump
+
+; Point to the next selector
+
+ add bx,8 ; Increment to next selector
+ jnc cmssLoop ; Try the next one
+
+; BX points to the first following selector which is not free
+
+cmssOutOfSelectors:
+ pop es ; Recover block header segment
+ assume es:MEMHDR
+ push dx ; Subtract base selector address
+ mov dx,selBlkData
+ and dx,SELECTOR_INDEX
+ sub bx,dx
+ pop dx
+ shr bx,3 ; Number of contiguous selectors available
+ cmp bx,dx ; Is it enough ?
+ jb cmssNotEnough ; No - jump
+ jne cmssOK ; Yes - jump
+ or cx,cx ; Don't know - Look at less significant part
+ jnz cmssNotEnough ; Yes it will fit after all - jump
+
+; Not enough free selectors
+
+cmssNotEnough:
+ mov dx,bx ; Put max available in CX&DX
+ xor cx,cx ;
+ stc ; Flag error
+ jmp short cmssExit ; Leave
+;
+; There are enough selectors to satisfy the request
+cmssOK:
+ clc ; Reset the error flag
+;
+; All Done
+cmssExit:
+ pop bx ; Restore registers
+ ret ; CalculateMaximumSegmentSpace
+
+; -------------------------------------------------------
+; CalculateMaximumSpace -- This function will see if there
+; is room to expand a block of memory.
+; If not it returns with carry set
+; and the maximum space available.
+;
+; Input: AL - check selectors yes/no flag - 0 = yes, !0 = no
+; ES - segment selector for the data block to modify
+; CX - low word of new desired size
+; DX - high word of new desired size
+; Output: AX - Strategy used: 0 = expand into same block
+; 1 = expand into next block
+; 2 = expand into next and previous blocks
+; 3 = allocate a new block and transfer data
+; 4 = move lower and higher blocks
+; CX - / If CY set: The maximum available
+; DX - \ If CY not set: Unchanged
+; Errors: CY set if unable to accomplish the request.
+; CX,DX will be set to the largest possible size that the
+; block could be modified to have
+; Uses: AX,CX,DX,Flags
+
+ assume ds:DGROUP,es:MEMHDR
+ public CalculateMaximumSpace
+
+CalculateMaximumSpace proc near
+
+ push bp
+ mov bp,sp
+ push es
+ push cx ; Save required length
+ push dx
+ push ax ; Save segment/selector flag
+
+ mov cx,word ptr [cbBlkLen] ; Load present length
+ mov dx,word ptr [cbBlkLen+2] ;
+ mov ax,0 ; Load strategy code
+ cmp dx,word ptr [bp - 6] ; Compare available with needed
+ jb cms90 ; Jump if it doesn't fit
+ ja cmsOKa ; Jump if it fits
+ cmp cx,word ptr [bp - 4] ;
+ jb cms90 ;
+cmsOKa:
+ jmp cmsOK
+
+; There is not enough room in the current block. See if the following
+; one is free
+
+cms90:
+ mov es,[selBlkNext] ; Point to the following block
+ cmp [selBlkOwner],0 ; Is it in use?
+ jnz cmsFail ; Yes - cannot use it
+ add cx,CB_MEMHDR ; Add the header of next block
+ adc dx,0
+ add cx,word ptr [cbBlkLen] ; Add the body of the next block
+ adc dx,word ptr [cbBlkLen+2];
+ mov ax,1 ; Load strategy code
+ cmp dx,word ptr [bp - 6] ; Compare available with needed
+ ja cmsOK ; Jump if it fits
+ cmp cx,word ptr [bp - 4] ;
+ jae cmsOK ;
+
+; Cannot expand. The max available is in CX&DX
+
+cmsFail:
+ add sp,6 ; Throw away requested size, and strat
+ pop es ; Point to original header
+
+ cmp byte ptr [bp-8],0 ; Should we check the # of
+ jz @f ; available selectors?
+ stc
+ jmp short cmsExit
+@@:
+ call CalculateMaximumSegmentSpace ;Check for enough selectors
+ stc ;Flag error
+ jmp short cmsExit
+
+; Expand will succeed. Strategy number is in ax
+
+cmsOK:
+ add sp,2 ; discard strategy
+ pop dx ; Restore requested size
+ pop cx ;
+cms900:
+ pop es ; Point to original header
+
+ cmp byte ptr [bp-8],0 ; Should we check the # of
+ jz @f ; available selectors?
+ clc
+ jmp short cmsExit
+@@:
+ call CalculateMaximumSegmentSpace ;Check for enough selectors
+
+cmsExit:
+ pop bp
+ ret
+
+CalculateMaximumSpace endp
+
+
+; -------------------------------------------------------
+;
+;
+; CombineFreeBlocks -- This routine is used when freeing
+; an extended memory block. It will examine the previous
+; and following blocks to see if they are free also, and
+; combine them into a single larger block if so.
+;
+; Input: AX - selector of segment descriptor for the block
+; Output: AX - selector for new free memory block
+; Errors: none
+; Uses: AX, ES modified, all other registers preserved
+;
+; NOTE: This routine may free the segment descriptor for the
+; entry block as part of the process of combining blocks.
+; The original entry value may no longer be used after
+; calling this routine. If access to the block header
+; for the free segment is still needed, you must use
+; the value returned in AX to access it.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public CombineFreeBlocks
+
+CombineFreeBlocks:
+ push bx
+ push dx
+;
+ mov bx,ax ;save entry value in BX
+;
+; Look at the previous block and see if it is free.
+ mov es,bx ;look at block header of entry block
+ assume es:MEMHDR
+ mov es,[selBlkPrev] ;look at block header of previous block
+ cmp [selBlkOwner],0 ;is it in use?
+ jnz cfrb30 ;if so, continue on
+;
+; The previous memory block is free. We need to combine it with the
+; current one.
+ mov ax,bx
+ call SpliceBlocks
+ mov bx,es
+;
+; Look at the following block and see if it is free.
+cfrb30: mov es,bx ;look at the current lower free block
+ assume es:MEMHDR
+ mov es,[selBlkNext] ;look at the following block
+ cmp [selBlkOwner],0 ;is it in use?
+ jnz cfrb90
+;
+; The following memory block is free. We need to combine it with the
+; current one.
+ mov ax,es
+ mov es,bx
+ call SpliceBlocks
+;
+; All done
+cfrb90: mov ax,bx
+ pop dx
+ pop bx
+ ret
+
+; -------------------------------------------------------
+; SpliceBlocks -- This routine is used by CombineFreeBlocks
+; to perform the actual combination of two adjacent free
+; blocks. The space occupied by the upper block is assigned
+; to the lower block, and the upper block is then eliminated.
+;
+; Input: AX - selector to the upper block
+; ES - selector to the lower block
+; Output: none
+; Errors: none
+; Uses: AX, DX modified
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+SpliceBlocks:
+ push ds
+ mov ds,ax
+ assume ds:NOTHING ;DS points at upper block
+ ;ES points at lower block
+ push ax
+;
+ mov dx,word ptr ds:[cbBlkLen]
+ mov ax,word ptr ds:[cbBlkLen+2]
+ add dx,CB_MEMHDR
+ adc ax,0
+ add word ptr es:[cbBlkLen],dx
+ adc word ptr es:[cbBlkLen+2],ax
+ mov ax,ds:[selBlkNext]
+ mov es:[selBlkNext],ax
+ mov ds,ax ;DS points at block following entry block
+ mov ds:[selBlkPrev],es
+;
+ pop ax
+ pop ds
+
+ call FreeSelector ;release the segment descriptor for the
+ ; upper block
+ ret
+
+; -------------------------------------------------------
+; FindFreeBlock -- This function will search the extended
+; memory heap looking for a free memory block of at
+; least the requested size.
+;
+; Input: CX - low word of requested block size
+; DX - high word or requested block size
+; Output: AX - selector for the block header of the free block
+; cbHeapBlkMax - size of largest free block found
+; cbHeapFreeSpace - total free space seen
+; Errors: Returns CY set if large enough free block not found.
+; If that happens, then cbHeapBlkMax contains the size of the
+; largest free block, and cbHeapFreeSpace contains the total
+; of all free blocks.
+; Uses: AX modified, all other registers preserved.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FindFreeBlock
+
+FindFreeBlock:
+ push cx
+ push dx
+ push es
+;
+ mov word ptr [cbHeapBlkMax],0
+ mov word ptr [cbHeapBlkMax+2],0
+ mov word ptr [cbHeapFreeSpace],0
+ mov word ptr [cbHeapFreeSpace+2],0
+;
+ cmp selHeapStart,0 ;
+ jz ffrb80 ; No heap - no allocate - jump
+ mov es,selHeapStart ; ES points at the beginning of the heap
+ assume es:MEMHDR
+;
+; Is the current memory block free?
+ffrb20:
+ cmp selBlkOwner,0 ;if the block isn't free, try the next one
+ jnz ffrb26
+;
+; Accumulate size into free space count.
+ mov ax,word ptr [cbBlkLen]
+ add word ptr [cbHeapFreeSpace],ax
+ mov ax,word ptr [cbBlkLen+2]
+ adc word ptr [cbHeapFreeSpace+2],ax
+;
+; Update our view of what the largest free block is.
+ mov ax,word ptr [cbBlkLen+2]
+ cmp ax,word ptr [cbHeapBlkMax+2]
+ jc ffrb26
+ jnz ffrb22
+ mov ax,word ptr [cbBlkLen]
+ cmp ax,word ptr [cbHeapBlkMax]
+ jc ffrb26
+ffrb22: mov ax,word ptr [cbBlkLen+2]
+ mov word ptr [cbHeapBlkMax+2],ax
+ mov ax,word ptr [cbBlkLen]
+ mov word ptr [cbHeapBlkMax],ax
+;
+; Check the size of this memory block to see if it is large enough.
+ffrb24: cmp dx,word ptr [cbBlkLen+2]
+ jc ffrb40
+ jnz ffrb26
+ cmp cx,word ptr [cbBlkLen]
+ jna ffrb40
+;
+; Go to the next block in the heap
+ffrb26:
+ cmp selBlkNext,0 ; End of chain?
+ jz ffrb80 ; Yes - jump
+ mov es,[selBlkNext]
+ jmp ffrb20
+;
+; Found a free block that is large enough
+ffrb40: mov ax,es
+ clc
+ jmp short ffrb90
+;
+; Didn't find one, return error.
+ffrb80: stc
+;
+; All done
+ffrb90: pop es
+ pop dx
+ pop cx
+ ret
+
+; -------------------------------------------------------
+; FreeSpace -- This function is used to find out how
+; much free space there is in the heap, plus that
+; which is potentially available from an XMS driver.
+;
+; Input: None
+; Output: AX:DX -- Total free space
+; BX:CX -- Largest free block
+; Errors: none
+; Uses: AX, all else preserved
+;
+; History:10/09/90 -- earleh wrote it.
+;
+cProc FreeSpace,<NEAR,PUBLIC>,
+cBegin
+
+ ;
+ ; Call xms to get free memory space
+ ;
+ pmxmssvc 08h
+
+ ;
+ ; convert to bytes
+ ;
+ mov bx,dx
+ mov dx,ax
+ mov cx,bx
+ shl dx,10
+ shr ax,6
+ shl cx,10
+ shr bx,6
+
+ ;
+ ; account for heap header
+ ;
+ sub dx,CB_MEMHDR
+ sbb ax,0
+ sub cx,CB_MEMHDR
+ sbb bx,0
+
+ ret
+cEnd
+
+; -------------------------------------------------------
+; SplitBlock -- This function will split the specified
+; memory block into two blocks, with the first one
+; being the specified size, and the second one containing
+; the remaining space.
+;
+; Input: AX - selector of block header for memory block to split
+; CX - low word of requested size
+; DX - high word of requested size
+; Output: none
+; Errors: none
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public SplitBlock
+
+SplitBlock:
+ push bx
+ push cx
+ push dx
+ push si
+ push es
+;
+ mov es,ax
+ assume es:MEMHDR
+;
+; We are going to need a segment descriptor for the remainder block
+; when we do the split. Make sure that we can allocate the selector
+; now, before we change anything.
+ call AllocateSelector
+ jnc spbl20 ;
+ jmp spbl90 ;get out if error
+spbl20: mov si,ax ;save selector in SI for later
+;
+; Adjust the size of the current block and figure the size of the
+; remainder.
+ xchg cx,word ptr [cbBlkLen] ;store the new block length
+ xchg dx,word ptr [cbBlkLen+2] ; and get the old block length
+ push cx ;Save the original size for recovery
+ push dx
+ sub cx,word ptr [cbBlkLen] ;compute the amount of space
+ sbb dx,word ptr [cbBlkLen+2] ; remaining in the block
+ sub cx,CB_MEMHDR ;also account for the new block header
+ sbb dx,0
+ jc spbl25 ;Jump if not enough memory to split
+ jnz spbl30 ;if there is some remaining memory, then
+ cmp cx,0 ; we have additional work to do
+ jnz spbl30
+;
+; There is no remaining memory. Free the selector that we allocated
+; earlier and then get out.
+spbl25: mov ax,si
+ pop dx ;Recover old size
+ pop cx
+ mov word ptr [cbBlkLen],cx ;restore the old block length
+ mov word ptr [cbBlkLen+2],dx
+ call FreeSelector
+ jmp spbl90
+;
+; We need to create a segment descriptor for the new memory block header
+; for the remainder block.
+spbl30: add sp,4 ;Dispose of unneeded recovery data
+ push cx ;save memory block length for later
+ push dx
+ mov ax,es ;selector of current memory header
+ call GetSegmentAddress
+ mov cx,CB_MEMHDR - 1
+ add dx,word ptr [cbBlkLen] ;bump by size of current block
+ adc bx,word ptr [cbBlkLen+2]
+ add dx,CB_MEMHDR ;plus size of block header
+ adc bx,0
+ mov ax,si
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+ pop dx
+ pop cx
+;
+; Now, we need to build a new memory block header for the remainder of
+; the original block. CX,DX has the size of the new block.
+ push ds
+;
+ mov ds,si
+ assume ds:NOTHING ;DS points at new block header
+ assume es:NOTHING ;ES points at old block header
+ xor ax,ax
+ mov ds:[fBlkStatus],al
+ mov ds:[cselBlkData],al
+ mov ds:[selBlkData],ax
+ mov ds:[selBlkOwner],ax
+ mov ds:[selBlkHdr],ds
+ mov word ptr ds:[cbBlkLen],cx
+ mov word ptr ds:[cbBlkLen+2],dx
+ mov ds:[selBlkPrev],es
+ mov ax,es:[selBlkNext]
+ mov ds:[selBlkNext],ax
+ mov es:[selBlkNext],si
+ mov ds,ax ;DS points at following block
+ mov ds:[selBlkPrev],si
+ cmp ds:[selBlkOwner],0 ;is it in use?
+;
+ pop ds
+ assume ds:DGROUP
+ jnz spbl90 ;Jump if the following block is not free
+;
+; The following memory block is free. We need to combine it with the
+; current one.
+ mov es,si
+ call SpliceBlocks
+;
+; All done
+spbl90: pop es
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ ret
+
+; -------------------------------------------------------
+; GetBlockHeader -- This function will return the selector
+; for the block header of the specified memory data
+; block.
+;
+; Input: AX - selector of the data block
+; Output: AX - selector of the block header
+; Errors: returns CY set if the entry selector doesn't point
+; to a valid data block
+; Uses: AX, all other registers preserved
+; Modifies the descriptor for segment SEL_SCR0
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+GetBlockHeader:
+ push bx
+ push cx
+ push dx
+ push es
+
+ push ax ;save entry value for later
+ mov bx,SEL_SCR0 or STD_TBL_RING
+ call DupSegmentDscr ;duplicate input descriptor to scratch
+ mov ax,bx
+ call GetSegmentAddress
+ sub dx,CB_MEMHDR ;backup to supposed header
+ sbb bx,0
+ mov cx,CB_MEMHDR-1 ; has the proper limit!
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ mov es,ax
+ assume es:MEMHDR
+
+ pop cx ;recover data block selector
+ cmp cx,[selBlkData] ;does the header point back to the
+ jz gtbh50 ; data block.
+
+ stc ;if not, then what were given wasn't a
+ jmp short gtbh90 ; selector to a memory block
+
+gtbh50: mov ax,[selBlkHdr] ;get the real selector to the header
+
+gtbh90: pop es
+ pop dx
+ pop cx
+ pop bx
+ ret
+
+; -------------------------------------------------------
+; DisposeBlock -- This routine will free all segment
+; descriptors associated with the specified memory block,
+; and remove the block header from the heap list.
+;
+; Input: AX - selector of block to dispose
+; Output: none
+; Errors: none
+; Uses: All registers preserved
+;
+; NOTE: This routine frees selectors for the specified memory block.
+; If this selector is already in a segment register when this
+; routine is called a GP fault will occur it that segment
+; register is later saved or restored. Also, any references to
+; memory using that segment register might destroy random data
+; in memory.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public DisposeBlock
+
+DisposeBlock:
+ push cx
+ push si
+ push di
+ push es
+;
+; Remove the block header from the heap list.
+ mov es,ax ;header selector to ES
+ mov si,es:[selBlkNext] ;SI points to following block
+ mov di,es:[selBlkPrev] ;DI points to previous block
+ mov es,di ;ES points to previous block
+ mov es:[selBlkNext],si ;previous block points to following block
+ mov es,si ;ES points to following block
+ mov es:[selBLkPrev],di ;following block points back to prev. block
+;
+; Free any data block descriptors associated with this memory object.
+ mov es,ax
+ cmp es:[selBlkOwner],0
+ jz dspb40
+ xor ch,ch
+ mov cl,es:[cselBlkData]
+ mov ax,es:[selBlkData]
+ and ax,SELECTOR_INDEX ;treat as GDT FreeSelectorBlock
+ call FreeSelectorBlock
+;
+; Free the descriptor for the block header.
+dspb40: mov ax,es
+ pop es
+ call FreeSelector
+;
+; All done
+dspb90: pop di
+ pop si
+ pop cx
+ ret
+
+; -------------------------------------------------------
+; SetUpDataDescriptors -- This function will initialize
+; all of the data segment descriptors associated with
+; a block. Those that should point to data will be
+; initialized accordingly. Those corresponding to
+; addresses beyond the allocated data will be initialized
+; as not present. If insufficient data segment
+; descriptors have been allocated, an attempt
+; will be made to allocate them. If this fails an error
+; occurs.
+;
+; Input: AX - Selector of the header segment.
+;
+; Output: None
+;
+; Errors: Returns CY set if not enough data segment descriptors
+; are available.
+;
+; Uses: Segment descriptor table is modified. Possibly the
+; data segment count in the header is modified.
+; No registers affected.
+;
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+ public SetUpDataDescriptors
+
+SetUpDataDescriptors:
+ push es
+ push si
+ push bx
+ push cx
+ push dx
+ push ax
+
+ mov es,ax
+ mov si,ax ;save header segment for dup
+ assume es:MEMHDR
+ mov bx,selBlkData ;Starting data selector index
+ and bx,SELECTOR_INDEX
+ xor cx,cx
+ mov cl,cselBlkData ;Load segment count
+ inc cx
+ mov dx,word ptr es:[cbBlkLen+2] ;Full data segment count
+ mov es,selGDT
+ assume es:NOTHING
+sudd30:
+ or dx,dx ;is this the last segment?
+ jnz sudd35
+ pop es ;Get the header segment
+ push es
+ cmp word ptr es:[cbBlkLen],0 ;Test partial length
+ mov es,selGDT ;Rebase the global descriptor table
+ jnz sudd35 ;Jump if the partial length is 0
+ jmp sudd60
+sudd35: loop short sudd40 ;Out of segments?
+ xchg bx,ax
+ call RemoveFreeDescriptor ;Attempt to get segment
+ xchg bx,ax
+ jnc sudd37
+ jmp sudd80
+sudd37: inc cx
+sudd40: mov ax,si
+ call DupSegmentDscr ; replicate the previous seg (in si)
+ mov si,bx
+
+; Set the DPL of allocated blocks to user DPL
+
+ifndef WOW_x86
+ mov es:[si].arbSegAccess,STD_DATA
+else
+ push ax
+ xor ax,ax
+ mov ah,es:[si].cbLimitHi386
+ or ax,STD_DATA
+ cCall NSetSegmentAccess,<si,ax>
+ pop ax
+endif
+
+; Increment the segment start address past the previous one
+
+ cmp es:[si].cbLimit,CB_MEMHDR-1 ; Is this the first block?
+ jne sudd41 ; No - jump
+ifndef WOW_x86
+ add es:[si].adrBaseLow,CB_MEMHDR ; bump segment start to point
+ adc es:[si].adrBaseHigh,0 ;past the block header
+else
+ push ax
+ push dx
+ mov ax,es:[si].adrBaseLow
+ mov dl,es:[si].adrBaseHigh
+ mov dh,es:[si].adrbBaseHi386
+ add ax,CB_MEMHDR
+ adc dx,0
+ cCall NSetSegmentBase,<si,dx,ax>
+ pop dx
+ pop ax
+endif
+ jmp short sudd42
+ifndef WOW_x86
+sudd41: add es:[si].adrBaseHigh,1 ; bump segment start by 64k
+else
+sudd41: push ax
+ push dx
+ mov ax, es:[si].adrBaseLow
+ mov dl, es:[si].adrBaseHigh
+ mov dh, es:[si].adrbBaseHi386
+ inc dx
+ cCall NSetSegmentBase,<si,dx,ax>
+ pop dx
+ pop ax
+endif
+
+; Set the segment length
+
+sudd42: add bx,8 ; Offset of next segment descriptor
+ or dx,dx ; Was this a partial segment?
+ jnz sudd45 ; jump if not
+;
+; We are at the partial (<64k) chunk at the end.
+ pop es ;Get the header segment
+ push es
+ mov dx,word ptr es:[cbBlkLen] ; Segment length
+ dec dx ; Last legal offset
+ mov es,selGDT
+ mov es:[si].cblimit,dx ; Store in segment descriptor
+ifdef WOW_x86
+ cCall NSetSegmentLimit,<si>
+endif
+ jmp sudd60 ; All valid data segs now exist
+sudd45:
+ mov es:[si].cbLimit,0FFFFh ;set segment limit at 64k
+ifdef WOW_x86
+ cCall NSetSegmentLimit,<si>
+endif
+ dec dx ; On to the next data segment
+ jmp sudd30
+;
+; Invalidate remaining segments
+sudd50: mov ax,si
+ call DupSegmentDscr ; replicate the previous one and
+ mov si,bx
+if 0
+ mov es:[si].arbSegAccess,ARB_DATA0NP ; Invalidate the segment
+else
+ mov word ptr es:[si].arbSegAccess,0 ; Invalidate the segment
+endif
+ add bx,8 ; Offset of next segment descriptor
+sudd60: loop sudd50
+;
+; All done
+ clc
+ jmp short sudd90
+;
+; Allocate of a segment descriptor failed.
+sudd80: pop ax ;Header Segment
+ mov es,ax ;
+ mov dx,es:selBlkData ;Calculate # segments
+ and dx,SELECTOR_INDEX ; successfully allocated
+ sub bx,dx
+ shr bx,3 ;Succesfully allocated
+ mov es:[cselBlkData],bl ;Store new value
+ mov dx,bx ;Return max allocatable space
+ mov cx,0 ;
+ add sp,4 ;Pop redundant stack data
+ stc ;Flag error
+ jmp short sudd100 ;Join common exit code
+;
+; Tidy up
+sudd90: pop ax ;Header segment
+ mov es,ax
+ mov dx,es:selBlkData ;Calculate # segments
+ and dx,SELECTOR_INDEX
+ sub bx,dx
+ shr bx,3
+ mov es:[cselBlkData],bl ;Store new value
+ pop dx
+ pop cx
+sudd100:
+ pop bx
+ pop si
+ pop es
+ ret
+;
+ifdef MD
+
+; -------------------------------------------------------
+; CheckXmemHeap -- This routine will check the integrity
+; of the chain of block headers.
+;
+; Input: NONE
+; Output: None
+; Errors: INT3 If an error is found
+; Uses: All registers preserved
+;
+; NOTE: This routine must be called in protected mode.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public CheckXmemHeap
+
+CheckXmemHeap:
+ push bp
+ mov bp,sp
+ pushf
+ push bx
+ push cx
+ push dx
+ push es
+ xor cx,cx
+ mov dx,cx
+ mov bx,selHeapStart ; Load start of chain
+cxmh100:
+ or bx,bx ;
+ jnz cxmh101
+ jmp cxmh200 ;
+cxmh101:
+ mov es,bx ;
+ assume es:MEMHDR ;
+ cmp selBlkHdr,bx ;
+ je cxmh110 ;
+ int 3
+ jmp cxmh200
+;; Debug_Out "CheckXmemHeap: Block header does not point to block header!"
+cxmh110:
+ cmp selBlkPrev,dx ;
+ je cxmh120 ;
+ int 3
+ jmp cxmh200
+;; Debug_Out "CheckXmemHeap: Previous block does not point to previous block!"
+cxmh120:
+ mov dx,bx ;
+ mov bx,selBlkNext ;
+ loop cxmh100 ;
+ int 3
+;; Debug_Out "CheckXmemHeap: Can't find end of arena chain!"
+cxmh200:
+ pop es
+ pop dx
+ pop cx
+ pop bx
+ popf
+ pop bp
+ ret
+endif
+
+; -------------------------------------------------------
+; FreeMemByOwner -- This routine frees all of the mem
+; blocks that belong to the specified owner.
+;
+; Input: bx = owner
+; Output: none
+; Uses: ax
+;
+; Note: We have to start at the beginning of the heap
+; after we free a block, because the free may
+; coalesce the heap, and the selector for the
+; next block may no longer be correct. We know we
+; are done when we have traversed the entire heap,
+; and not found a block to free.
+
+ assume ds:DGROUP, es:nothing, ss:nothing
+ public FreeMemByOwner
+
+FreeMemByOwner:
+ push es
+ push cx
+
+ cmp bx,0 ; owner is free owner??
+ je fbo40
+
+; First traverse xmem heap
+ push selHeapStart
+ mov ax,selHiHeapStart
+ mov selHeapStart,ax
+
+ call FreeHiMemByOwnerW
+ call FreeLowMemByOwnerW
+ pop selHeapStart
+
+
+fbo40:
+ pop cx
+ pop es
+ ret
+
+FreeHiMemByOwnerW:
+fhbow10: mov ax,selHiHeapStart
+fhbow20: cmp ax,0
+ je @f
+ lar cx, ax ;is this a valid selector?
+ jz fhbow25 ;yes, go ahead
+@@:
+ ret
+
+fhbow25: mov es,ax
+ assume es:MEMHDR
+ cmp selBlkOwner,bx
+ jne fhbow30
+
+ mov ax,selBlkDAta
+ push 0
+ pop es
+ call FreeXmemBlock
+ jmp fhbow10 ; check again.
+
+fhbow30: mov ax,selBlkNext ; check next block
+ jmp fhbow20
+
+FreeLowMemByOwnerW:
+flbow10: mov ax,selLowHeapStart
+flbow20: cmp ax,0
+ je @f
+ lar cx, ax ;is this a valid selector?
+ jz flbow25 ;yes, go ahead
+@@:
+ ret
+
+flbow25: mov es,ax
+ assume es:MEMHDR
+ cmp selBlkOwner,bx
+ jne flbow30
+
+ mov ax,selBlkData
+ push 0
+ pop es
+ call FreeLowBlock
+ jmp flbow10 ; check again.
+
+flbow30: mov ax,selBlkNext ; check next block
+ jmp flbow20
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+
+DXPMCODE ends
+;
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxnetbio.asm b/private/mvdm/dpmi/dxnetbio.asm
new file mode 100644
index 000000000..32dcb4748
--- /dev/null
+++ b/private/mvdm/dpmi/dxnetbio.asm
@@ -0,0 +1,2417 @@
+ PAGE ,132
+ TITLE DXNETBIO.ASM -- Dos Extender NetBIOS API Mapper
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXNETBIO.ASM - DOS Extender NetBIOS API Mapper
+;
+;-----------------------------------------------------------------------
+;
+; This module provides the 286 DOS Extender's API mapping of Int 2Ah and
+; 5Ch NetBIOS requests. It allows a protected mode application to
+; issue NetBIOS requests without worrying about segment to selector
+; translations and mapping of buffers between extended and conventional
+; memory.
+;
+;-----------------------------------------------------------------------
+;
+; 11/29/90 amitc Modified code to have the POST routine in GlobalMemory
+; and queue the POST when DOSX is not around.
+; 11/29/90 amitc Call to InitLowHeap moved to this file from DXOEM.ASM
+; 02/01/90 jimmat Update Api mapping table for Ungerman-Bass extensions
+; as per new data from UB.
+; 06/15/89 w-glenns Finished TermNetMapper, added Delay/ResumeNetPosting
+; 04/18/89 jimmat Original version.
+; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include interupt.inc
+include netbios.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+include stackchk.inc
+
+include bop.inc
+include rdrsvc.inc
+
+ .list
+
+; -------------------------------------------------------
+; FLAG FOR PM NCB HANDLING
+; -------------------------------------------------------
+
+PM_NCB_HANDLING equ 1 ; set to 0 for NCB handling in VM
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+; The following equates define the codes that map between a NCB Command code
+; and the mapping flags used to process it. To be compatible with Windows/386,
+; these values are all increments of 4 (they use 32 bit offsets). These
+; values should always correspond to the values used by the Windows/386
+; NetBIOS mapping!
+
+ApiMapUnknown EQU 00h
+ApiMapNone EQU 04h
+ApiMapIn EQU 08h
+ApiMapOut EQU 0Ch
+ApiMapInOut EQU 10h
+ApiChainSend EQU 14h
+ApiCancel EQU 18h
+ApiBufferIn EQU 1Ch
+ApiBufferOut EQU 20h
+ApiBufferInOut EQU 24h
+
+
+; The following equates define the bit flags which identify the actions
+; to take on entry and exit of a NetBIOS request.
+
+BUFF_IN EQU 01h ;Buffer data to the int handler
+BUFF_OUT EQU 02h ;Buffer data from the int handler
+BUFF_CHAIN EQU 04h ;Special chain send buffering
+BUFF_CANCEL EQU 08h ;Special cancel buffering
+
+REPLACE_MAP_TABLE EQU 1607h ;Int2fh replace net map table service
+VNETBIOS_DEV_ID EQU 14h ;VNETBIOS device ID
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn EnterIntHandler:NEAR
+ extrn LeaveIntHandler:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn AllocateLowBlock:NEAR
+ extrn FreeLowBlock:NEAR
+ extrn GetSegmentAddress:NEAR
+externFP NSetSegmentDscr
+ extrn AllocateLDTSelector:NEAR
+ extrn Lma2SegOff:NEAR
+ extrn InitLowHeap:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn pbReflStack:WORD
+ extrn bReflStack:WORD
+ extrn rglpfnRmISR:DWORD
+ extrn regUserSS:WORD
+ extrn regUserSP:WORD
+ extrn rgbXfrBuf1:BYTE
+ extrn segDXCode:WORD
+ extrn NetHeapSize:WORD
+
+ifdef WOW_x86
+ extrn FastBop:fword
+endif
+
+OldInt76 dd 0
+ public HCB_List
+
+Cancel_NCB EQU rgbXfrBuf1 ;Easier for commenting/readability
+
+HCB_List dw 0 ;linked list of low HCB/NCB/Buffers
+
+selNetScr dw 0 ;scratch selector
+
+lpfnOldInt2A dd ? ;old int vector 2Ah & 5Ch routines
+lpfnOldInt5C dd ?
+
+lpfnRmNetIsr dd ? ;Real Mode vector to Network Int Rtn
+
+SelNetStubCode dw 0 ;sel for stub code
+SegNetStubCode dw 0 ;segment for stub code
+AddrfStubDelayNetPosting dw ? ;address of fStubDelayNetPosting in StubSeg
+AddrfStubTermNetRequest dw ? ;address of fStubTermNetRequest in StubSeg
+
+NBPSp dw 0
+
+; The ApiMapTbl converts an NCB Command code (0-7F) into a code that
+; identifies the particular mapping routine to execute.
+
+ApiMapTbl label byte
+ db ApiMapUnknown ; 00h -
+ db ApiMapUnknown ; 01h -
+ db ApiMapUnknown ; 02h -
+ db ApiMapUnknown ; 03h -
+ db ApiMapUnknown ; 04h -
+ db ApiMapUnknown ; 05h -
+ db ApiMapUnknown ; 06h -
+ db ApiMapUnknown ; 07h -
+ db ApiMapUnknown ; 08h -
+ db ApiMapUnknown ; 09h -
+ db ApiMapUnknown ; 0Ah -
+ db ApiMapUnknown ; 0Bh -
+ db ApiMapUnknown ; 0Ch -
+ db ApiMapUnknown ; 0Dh -
+ db ApiMapUnknown ; 0Eh -
+ db ApiMapUnknown ; 0Fh -
+ db ApiMapNone ; 10h - Call
+ db ApiMapNone ; 11h - Listen
+ db ApiMapNone ; 12h - Hang up
+ db ApiMapUnknown ; 13h -
+ db ApiBufferIn ; 14h - Send
+ db ApiBufferOut ; 15h - Receive
+ db ApiBufferOut ; 16h - Receive any
+ db ApiChainSend ; 17h - Chain send
+ db ApiMapUnknown ; 18h -
+ db ApiMapUnknown ; 19h -
+ db ApiMapUnknown ; 1Ah -
+ db ApiMapUnknown ; 1Bh -
+ db ApiMapUnknown ; 1Ch -
+ db ApiMapUnknown ; 1Dh -
+ db ApiMapUnknown ; 1Eh -
+ db ApiMapUnknown ; 1Fh -
+ db ApiBufferIn ; 20h - Send datagram
+ db ApiBufferOut ; 21h - Receive datagram
+ db ApiBufferIn ; 22h - Send broadcast datagram
+ db ApiBufferOut ; 23h - Receive broadcast dgram
+ db ApiMapUnknown ; 24h -
+ db ApiMapUnknown ; 25h -
+ db ApiMapUnknown ; 26h -
+ db ApiMapUnknown ; 27h -
+ db ApiMapUnknown ; 28h -
+ db ApiMapUnknown ; 29h -
+ db ApiMapUnknown ; 2Ah -
+ db ApiMapUnknown ; 2Bh -
+ db ApiMapUnknown ; 2Ch -
+ db ApiMapUnknown ; 2Dh -
+ db ApiMapUnknown ; 2Eh -
+ db ApiMapUnknown ; 2Fh -
+ db ApiMapNone ; 30h - Add name
+ db ApiMapNone ; 31h - Delete name
+ db ApiMapNone ; 32h - Reset
+ db ApiMapOut ; 33h - Adapter status
+ db ApiMapOut ; 34h - Session status
+ db ApiCancel ; 35h - Cancel
+ db ApiMapNone ; 36h - Add group name
+ db ApiMapUnknown ; 37h -
+ db ApiMapUnknown ; 38h -
+ db ApiMapUnknown ; 39h -
+ db ApiMapUnknown ; 3Ah -
+ db ApiMapUnknown ; 3Bh -
+ db ApiMapUnknown ; 3Ch -
+ db ApiMapUnknown ; 3Dh -
+ db ApiMapUnknown ; 3Eh -
+ db ApiMapUnknown ; 3Fh -
+ db ApiMapUnknown ; 40h -
+ db ApiMapUnknown ; 41h -
+ db ApiMapUnknown ; 42h -
+ db ApiMapUnknown ; 43h -
+ db ApiMapUnknown ; 44h -
+ db ApiMapUnknown ; 45h -
+ db ApiMapUnknown ; 46h -
+ db ApiMapUnknown ; 47h -
+ db ApiMapUnknown ; 48h -
+ db ApiMapUnknown ; 49h -
+ db ApiMapUnknown ; 4Ah -
+ db ApiMapUnknown ; 4Bh -
+ db ApiMapUnknown ; 4Ch -
+ db ApiMapUnknown ; 4Dh -
+ db ApiMapUnknown ; 4Eh -
+ db ApiMapUnknown ; 4Fh -
+ db ApiMapUnknown ; 50h -
+ db ApiMapUnknown ; 51h -
+ db ApiMapUnknown ; 52h -
+ db ApiMapUnknown ; 53h -
+ db ApiMapUnknown ; 54h -
+ db ApiMapUnknown ; 55h -
+ db ApiMapUnknown ; 56h -
+ db ApiMapUnknown ; 57h -
+ db ApiMapUnknown ; 58h -
+ db ApiMapUnknown ; 59h -
+ db ApiMapUnknown ; 5Ah -
+ db ApiMapUnknown ; 5Bh -
+ db ApiMapUnknown ; 5Ch -
+ db ApiMapUnknown ; 5Dh -
+ db ApiMapUnknown ; 5Eh -
+ db ApiMapUnknown ; 5Fh -
+ db ApiMapUnknown ; 60h -
+ db ApiMapUnknown ; 61h -
+ db ApiMapUnknown ; 62h -
+ db ApiMapUnknown ; 63h -
+ db ApiMapUnknown ; 64h -
+ db ApiMapUnknown ; 65h -
+ db ApiMapUnknown ; 66h -
+ db ApiMapUnknown ; 67h -
+ db ApiMapUnknown ; 68h -
+ db ApiMapUnknown ; 69h -
+ db ApiMapUnknown ; 6Ah -
+ db ApiMapUnknown ; 6Bh -
+ db ApiMapUnknown ; 6Ch -
+ db ApiMapUnknown ; 6Dh -
+ db ApiMapUnknown ; 6Eh -
+ db ApiMapUnknown ; 6Fh -
+ db ApiMapNone ; 70h - Unlink
+ db ApiMapUnknown ; 71h -
+ db ApiMapNone ; 72h - Ungerman Bass Register
+ db ApiBufferIn ; 73h - Ungerman Bass SendNmc
+ db ApiMapNone ; 74h - Ungerman Bass Callniu
+ db ApiMapNone ; 75h - Ungerman Bass Calladdr
+ db ApiMapNone ; 76h - Ungerman Bass Listenaddr
+ db ApiBufferIn ; 77h - Ungerman Bass SendPkt
+ db ApiBufferOut ; 78h - Ungerman Bass RcvPkt
+ db ApiBufferIn ; 79h - Ungerman Bass SendAttn
+ db ApiBufferOut ; 7Ah - Ungerman Bass RcvAttn
+ db ApiBufferOut ; 7Bh - Ungerman Bass Listenniu
+ db ApiBufferOut ; 7Ch - Ungerman Bass RcvRaw
+ db ApiBufferIn ; 7Dh - Ungerman Bass SendNmc2
+ db ApiMapUnknown ; 7Eh -
+ db ApiMapNone ; 7Fh - Install check
+
+; The next table maps the (Windows/386 compatible) code from ApiMapTbl
+; to the bit flags which control our entry/exit mapping.
+
+EntryExitFlags label byte
+ db BUFF_IN+BUFF_OUT ;ApiMapUnknown
+ db 0 ;ApiMapNone
+ db BUFF_IN ;ApiMapIn
+ db BUFF_OUT ;ApiMapOut
+ db BUFF_IN+BUFF_OUT ;ApiMapInOut
+ db BUFF_CHAIN ;ApiChainSend
+ db BUFF_CANCEL ;ApiCancel
+ db BUFF_IN ;ApiBufferIn
+ db BUFF_OUT ;ApiBufferOut
+ db BUFF_IN+BUFF_OUT ;ApiBufferInOut
+
+DXDATA ends
+
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn selDgroup:WORD
+ENDIF
+
+DXCODE ends
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+
+DXPMCODE ends
+
+ page
+; ----------------------------------------------------------------------
+;
+; The following routines handle INT 2Ah and 5Ch interrupts that
+; request NetBIOS services. Typically, these interrupts require
+; that a NCB and/or buffer be copied between conventional and
+; extended memory, and register values be modified due to real-
+; mode/protected-mode addressing differences.
+;
+; (Note: this comment copied almost unchanged from DXINTR.ASM)
+;
+; The following conventions are used:
+;
+; A stack is allocated from the interrupt reflector stack for these
+; routines to use. This allows nested servicing of interrupts.
+; A stack frame is built in the allocated stack which contains the
+; following information:
+;
+; original caller's stack address
+; caller's original flags and general registers (in pusha form)
+; caller's original segment registers (DS & ES)
+; flags and general registers to be passed to interrupt routine
+; (initially the same as caller's original values)
+; segment registers (DS & ES) to be passed to interrupt routine
+; (initially set to the Dos Extender data segment address)
+;
+; This stack frame is built by the routine EnterIntHandler, and its
+; format is defined by the structure INTRSTACK. The stack frame is
+; destroyed and the processor registers set up for return to the user
+; by the function LeaveIntHandler.
+;
+; There are two sets of general registers and two sets of segment
+; registers (DS & ES) on the stack frame. One set of register values
+; has member names of the form intUserXX. The values in these stack
+; frame members will be passed to the interrupt service routine when
+; it is called, and will be loaded with the register values returned
+; by the interrupt service routine. The other set of registers values
+; has member names of the form pmUserXX. These stack frame members
+; contain the original values in the registers on entry from the
+; user program that called the interrupt.
+;
+; When we return to the original caller, we want to pass back the
+; general registers as returned by the interrupt routine (and possibly
+; modified by the exit handler), and the same segment registers as
+; on entry, unless the interrupt routine returns a value in a segment
+; register. (in this case, there must be some code in the exit routine
+; to handle this). This means that when we return to the caller, we
+; return the general register values from the intUserXX set of stack
+; frame members, but we return the segment registers from the pmUserXX
+; set of frame members. By doing it this way, we don't have to do
+; any work for the case where the interrupt subfuntion doesn't require
+; any parameter manipulation. NOTE however, this means that when
+; manipulating register values to be returned to the user, the segment
+; registers are treated opposite to the way the general registers are
+; treated. For general registers, to return a value to the user,
+; store it in a intUserXX stack frame member. To return a segment
+; value to the user, store it in a pmUserXX stack frame member.
+;
+
+
+; -------------------------------------------------------
+ subttl NetBIOS API Mapper Initialization Routine
+ page
+; -------------------------------------------------------
+; NetBIOS API MAPPER INITIALIZATION ROUTINE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+;--------------------------------------------------------
+; InitNetMapper -- This routine initializates the NetBIOS API mapper.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: all registers preserved
+;
+; Note: This routine expects to be called late enough in the DOS
+; extender initialization cycle that it can use other interrupt
+; mapping functions (like INT 21h). It must be called in
+; PROTECTED MODE!
+; And, assumes interrupts are to be enabled.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public InitNetMapper
+
+InitNetMapper proc near
+
+ pusha
+ push es
+
+; Do an installation check before doing anything else
+
+ SwitchToRealMode
+ assume ds:DGROUP,es:DGROUP
+ FSTI
+
+ mov ax,355Ch ;make sure the Int 5Ch vector
+ int 21h ; really points somewhere
+ assume es:NOTHING
+
+ mov ax,es ;can't be installed if 0 vector
+ or ax,bx
+ jz inm20
+
+ push ds
+ pop es
+ assume es:DGROUP
+
+ mov cx,(size NCB_Struc)/2 ;build a dummy NCB with an invalid
+ mov di,offset rgbXfrBuf1 ; 7Fh (Install) command code
+ mov bx,di
+ xor ax,ax
+ cld
+ rep stosw
+
+ mov [bx].NCB_Command,Install ;issue invalid request
+ int 5Ch
+
+ xor bx,bx ;assume not installed
+ cmp al,RC_Invalid_Cmd ;if it says invalid command, then
+ jnz inm20 ; it must be installed
+
+ dec bx ;bx !=0 means NetBIOS is installed
+
+inm20:
+ SwitchToProtectedMode
+ FSTI
+
+ or bx,bx ;skip install if no NetBIOS
+ jnz inm20A ;net bios is there
+ jmp inm80 ;no NetBios
+
+inm20A:
+
+; we are going to have our net mapping layer installed. For this we need
+; global memory for the post stub code. Allocate and initialize global
+; memory now.
+
+ mov ax,NetHeapSize ;get size of low heap
+ call InitLowHeap ;init the low net heap
+ jnc inm20B ;low memory successfully initialized
+ jmp inm80 ;return with failure
+
+inm20B:
+
+; we need to allocate a block of memory in the low heap and copy some code
+; down there which would handle the POST calls from the NetWork. It is
+; necessary to have a piece in global memory do the POST handling because
+; DOSX might have been swapped out by the Switcher when a POST request
+; comes in.
+
+ mov cx, SIZE_OF_GLOBAL_NET_STUB_CODE
+ xor dx,dx ;DX:CX has the size
+ call AllocateLowBlock ;allocate a block in low memory
+ jnc inm20C ;allocation succeeded
+ jmp inm80 ;fail the initialization
+
+inm20C:
+
+; AX has a selector pointing to the start of the block. Copy the stub routine
+; down into the block.
+
+ push es ;save
+ mov [SelNetStubCode],ax ;save the selector value
+ push ds ;save
+ mov es,ax ;destination sel of the xfer
+ push cs
+ pop ds
+ xor di,di ;destination offset
+ lea si,NetBiosStubCode ;source offset of xfer
+ mov cx,SIZE_OF_GLOBAL_NET_STUB_CODE ;size of the block
+ cld
+ rep movsb ;copy the block down
+ pop ds ;restore our DS
+
+; now get the real mode segment value for the selector value in AX
+
+ mov bx,ax ;get selector value in BX
+ mov ax,6 ;get base DPMI call
+ int 31h ;CX:DX has the base address
+ shr dx,4 ;discard last 4 bits
+ and cx,0fh ;discarded bits were 0
+ shl cl,4 ;get it in MS nibble
+ or dh,cl ;DX has the RM segment
+ mov [SegNetStubCode],dx ;save it
+
+
+; now copy the FAR address of the RMPostRtn into the stub code segment
+
+ lea di,FarAddrOfRMPostRtn ;variable where address to be saved
+ lea ax,NetBiosStubCode ;offset of the routine
+ sub di,ax ;DI has the offset in the stub
+ mov ax,segDXCode ;segment of real mode DosX
+ mov es:[di][2],ax ;save the segment
+ lea ax,RMPostRtn ;offset of the POST routine
+ mov es:[di][0],ax ;save the offset
+
+; patch the address 'NBS_Patch_CalFarAddr' with the address in DI
+
+ mov si,NBS_Patch_CallFarAddr ;address to patch
+ mov es:[si],di ;patch it.
+
+; calculate the offset to the other flag bytes in the stub code area that
+; we will have to access.
+
+ lea di,fStubDelayNetPosting ;address when assembled
+ lea ax, NetBiosStubCode ;address of start of routine
+ sub di,ax ;address after move
+ mov [AddrfStubDelayNetPosting],di ;save it
+
+; patch the address 'NBS_Patch_fsDelayNetPosting' with the value in DI
+
+ mov si,NBS_Patch_fsDelayNetPosting ;address to patch
+ mov es:[si],di ;patch it.
+
+ lea di,fStubTermNetRequests ;address when assembled
+ sub di,ax ;address after move
+ mov [AddrfStubTermNetRequest],di ;save it
+
+; patch the address 'NBS_Patch_fsTermNetRequests' with the value in DI
+
+ mov si,NBS_Patch_fsTermNetRequests ;address to patch
+ mov es:[si],di ;patch it.
+ pop es
+
+; The stub routine in global memory has now been put in place to handle POST
+; calls. We now need to hook the INT 2AH and 5CH PM vectors to trap the network
+; calls.
+
+; The network seems to be installed, hook the INT 2Ah & 5Ch PM vectors
+
+ mov ax,352Ah ;get/store old Int 2Ah vector
+ int 21h
+ assume es:NOTHING
+
+ mov word ptr lpfnOldInt2A,bx
+ mov word ptr [lpfnOldInt2A+2],es
+
+ mov ax,355Ch ;get/store old Int 5Ch vector
+ int 21h
+
+ mov word ptr lpfnOldInt5C,bx
+ mov word ptr [lpfnOldInt5C+2],es
+
+ push ds
+ pop es
+
+ push cs
+ pop ds
+ assume ds:NOTHING,es:DGROUP
+
+ mov ax,252Ah ;set new Int 2Ah handler
+ mov dx,offset DXPMCODE:PMIntr2A
+ int 21h
+
+ mov ax,255Ch ;set new Int 5Ch handler
+ mov dx,offset DXPMCODE:PMIntr5C
+ int 21h
+
+ push es
+ pop ds
+ assume ds:DGROUP
+
+
+; Allocate a selector to use for scratch purposes later
+
+ call AllocateLDTSelector
+ mov selNetScr,ax
+
+; See if anybody wants to provide a different NetBIOS mapping table
+
+ SwitchToRealMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI ;don't need 'em disabled
+
+ xor cx,cx ;subf'n zero
+ mov es,cx ;preset address to copy to NULL
+ mov di,cx
+ mov ax,REPLACE_MAP_TABLE
+ ; DOSX net extender API call to substitute alternate NETBIOS
+ ; mapping table
+ mov bx,VNETBIOS_DEV_ID ;VNETBIOS device ID
+ int 2Fh
+ ; ES:DI contains address of alternate mapping table or NULL
+
+ mov cx,es ;Q: valid table pointer ?
+ jcxz inm70 ;N: NULL...keep current table
+
+ ;Y: copy table pointed to by es:di to table area (simple replace)
+
+ mov ax,ds ; string move source (int 2F provided) table
+ mov es,ax ; in ds:si, and destination ApiMapTbl in the
+ mov ds,cx ; data segment now in es:di
+ mov si,di
+ mov di,offset ApiMapTbl ;ptr to table
+ mov cx,64 ;copy over 128 byte table
+ cld
+ rep movsw
+ mov ds,ax ;recall data segment
+
+inm70:
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+
+ FSTI
+ clc
+ jmp short inm90
+
+inm80:
+ stc ;tell caller we didn't install
+
+inm90:
+ pop es
+ popa
+
+ ret
+
+InitNetMapper endp
+
+;-----------------------------------------------------------------------------
+; NetBiosStubCode
+;
+; DESCRIPTION This routine is actually relocated to a block of Global Memory
+; obtained from the Switcher. It handles the POST calls from the
+; NETBIOS layer. If DosX has been swapped out, fStubDelayPosting
+; would be true and this routine would set a flag in the HCB
+; to imply that the POSTing must be done. If DosX is active, this
+; routine will call off to the RMPostRtn in DosX via a far call
+; pointer.
+;
+; ENTRY:
+; ES:BX - HCB
+;
+; EXIT:
+; All registers preserved but for flags.
+;
+; USES:
+; Flags
+;
+; NOTE:
+; This routine will actually be copied to a global memory stub segment
+; and will execute from there. However, since the routine would be moved
+; while we are in protected mode, the routine is being assembled in the
+; protected mode code segment.
+;-----------------------------------------------------------------------------
+NetBiosStubCode proc far
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+ FCLI ;just to be sure
+
+; Don't post anything if in the process of terminating
+
+;-----------------------------------------------------------------------------
+; The following instruction will have to be patched after the move:
+;
+; cmp cs:[fStubTermNetRequests],1
+;
+; we will mark the place that has to be patched.
+;-----------------------------------------------------------------------------
+
+ db 2eh, 80h, 3eh ;CS: CMP BYTE PTR
+
+NBS_Patch_fsTermNetRequests equ $ - NetBiosStubCode
+
+ dw ? ;address to compare
+ db 1 ;value to comapare
+;-----------------------------------------------------------------------------
+
+ je NBSC_post_done
+
+; Check if posting should be delayed
+
+;-----------------------------------------------------------------------------
+; The following instruction will have to be patched after the move:
+;
+; cmp cs:[fStubDelayNetPosting],0;Q: delay postings ?
+;
+; we will mark the place that has to be patched.
+;-----------------------------------------------------------------------------
+
+ db 2eh, 80h, 3eh ;CS: CMP BYTE PTR
+
+NBS_Patch_fsDelayNetPosting equ $ - NetBiosStubCode
+
+ dw ? ;address to compare
+ db 0 ;value to comapare
+;-----------------------------------------------------------------------------
+
+ je NBSC_post_no_delay ;N: don't delay this posting
+
+; we can do no posting. DosX has been swapped out. Set a flag in the HCB to
+; imply that POSTing must be done later.
+
+ or byte ptr es:[bx.HCB_Flags],HCB_DELAY ;Y: mark as delayed
+ jmp short NBSC_post_done
+
+NBSC_post_no_delay:
+
+;-----------------------------------------------------------------------------
+; call off to RMPostRtn in DosX. We will have to patch the following
+; instruction after the move.
+;
+; call cs:[FarAddrOfRMPostRtn] ;call routine in DosX proper
+;
+; we will mark the place to match
+;-----------------------------------------------------------------------------
+
+ db 2eh,0ffh,1eh ;CALL CS: DWORD PTR
+
+NBS_Patch_CallFarAddr equ $ - NetBiosStubCode
+
+ dw ? ;call far address
+;------------------------------------------------------------------------------
+
+NBSC_post_done:
+
+ riret
+
+NetBiosStubCode endp
+;----------------------------------------------------------------------------;
+; allocate space for some of the variables that the Stub Code uses. These ;
+; will be filled in by PMODE code in the DosX. ;
+;----------------------------------------------------------------------------;
+
+fStubTermNetRequests db 0 ;DosX is terminating
+fStubDelayNetPosting db 0 ;delay posting, DosX swapped
+FarAddrOfRMPostRtn dd ? ;address of the actual POST rtn
+
+SIZE_OF_GLOBAL_NET_STUB_CODE equ $ - NetBiosStubCode
+;-----------------------------------------------------------------------------;
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl NetBIOS API Mapper Termination Routine
+ page
+; -------------------------------------------------------
+; NetBIOS API MAPPER TERMINATION ROUTINE
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; TermNetMapper -- This routine is called when the 286 DOS extender
+; is winding down so that any pending network requests from the
+; protected mode application can be canceled.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: ax,bx,cx,dx,si,di,es
+;
+; Note: This routine must be called in REAL MODE!
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public TermNetMapper
+
+TermNetMapper proc near
+
+; set a flag in the stub code to imply that DosX is terminating and no more
+; POSTs to be done.
+
+ mov di,[SelNetStubCode] ;selector for the stub area
+ or di,di ;is global heap in place ?
+ jnz @f ;yes.
+ ret
+@@:
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+
+ push es
+ mov es,di
+ assume es:nothing
+
+; get the address of the flag byte fStubTermNetRequest after the block was
+; relocated
+
+ mov di,[AddrfStubTermNetRequest]
+
+; ES:DI has the flag byte address. Set the flag
+
+ mov byte ptr es:[di],1 ;set flag to delay posting
+ pop es
+ assume es:dgroup
+
+ ; terminating the net driver interface...no more postings allowed
+
+ FSTI
+
+ mov cx,size NCB_Struc / 2
+ mov si,offset Cancel_NCB ;cancel control block buffer
+ mov di,si
+ xor ax,ax ;zero fill structure
+ cld
+ rep stosw
+
+ mov ds:[si.NCB_Command],Cancel ;Cancel command NCB
+
+; We don't need to release the NCB/buffer(s) from the low heap because
+; the entire low heap will soon be released.
+
+; Search low heap list looking for copy of target NCB
+
+ mov cx,HCB_List
+ mov di,HCB_Header_Size ;offset of NCB in low heap block
+
+term_next:
+ jcxz term_done
+
+ mov ax,cx
+ call GetSegmentAddress
+ add dx,di
+ adc bx,0 ;BX:DX = lma of low target NCB
+
+ call Lma2SegOff ;BX:DX = normalized SEG:OFF
+
+ dec bx ;return the same slightly unnormalized
+ add dx,10h ; SEG:OFF that was used initially
+
+ mov ds:[si.NCB_Buffer_Seg],bx ; point to NCB to cancel
+ mov ds:[si.NCB_Buffer_Off],dx
+
+ SwitchToRealMode ;also disables ints for us
+ assume ds:DGROUP,es:DGROUP
+
+ mov bx,si ;ES:BX points to cancelling NCB
+
+ pushf
+ call lpfnRmNetIsr ;do the cancel call
+ ; return code in AL
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+ FSTI
+ mov es,cx
+ mov cx,es:[di.HCB_Next]
+ jmp short term_next
+
+term_done:
+
+ SwitchToRealMode
+ assume ds:DGROUP,es:DGROUP
+
+ FSTI
+ ret
+
+TermNetMapper endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl NetBIOS API Int 2Ah Mapper Interrupt Hook
+ page
+; -------------------------------------------------------
+; NetBIOS API INT 2Ah MAPPER INTERRUPT HOOK
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; PMIntr2A -- This routine traps Int 2Ah requests from a
+; protected mode application. NetBIOS requests (ah =
+; 1 or 4) are passed on to the Int 5Ch handler for
+; further processing. Other requests are passed on
+; to the next Int 2Ah handler in the interrupt chain.
+;
+; Input: User regs at time of interrupt
+; Output: none
+; Errors: none
+; Uses: none
+;
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr2A
+
+PMIntr2A proc near
+
+ cld ;cya...
+
+ cmp ah,1 ;we only need to map the Int 2Ah
+ jz @f ; ah = 1 & 4 services--if it's
+ cmp ah,4 ; not one of those, just pass it
+ jz @f ; on down the chain...
+
+ sub sp,4 ; build a stack frame
+ push bp
+ mov bp,sp
+ push ax
+ push ds
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ mov ax,word ptr [lpfnOldInt2A+2] ; store previous INT 2A
+ mov word ptr [bp+4],ax ; handler
+ mov ax,word ptr [lpfnOldInt2A]
+ mov word ptr [bp+2],ax
+ pop ds
+ assume ds:NOTHING
+ pop ax
+ pop bp ; SS:SP -> previous handler
+ ;let someone else deal with it
+ retf ; (most likely PMIntrReflector)
+@@:
+
+if PM_NCB_HANDLING
+
+;
+; if this is NT then we have decided we are making a NetBIOS call (es:bx points
+; to an NCB in protect-mode memory). INT 0x2A, ah=1 or ah=4 just ends up calling
+; the 0x5C entry point; ah is returned as 1 or 0, depending on whether an error
+; is returned from 0x5C or not
+;
+
+ push dx
+ mov dl,2ah
+
+else
+
+ push bx ;save caller's bx
+ mov bx,4*2Ah ;indicate we came from int 2A handler
+
+endif ; PM_NCB_HANDLING
+
+ jmp short DoNetRqst ;int 2Ah/5Ch common code
+
+PMIntr2A endp
+
+
+; -------------------------------------------------------
+ subttl NetBIOS API Int 5Ch Mapper Interrupt Hook
+ page
+; -------------------------------------------------------
+; NetBIOS API INT 5Ch MAPPER INTERRUPT HOOK
+; -------------------------------------------------------
+; PMIntr5C -- This routine maps Int 5Ch (and selected Int 2Ah)
+; NetBIOS requests between a protected mode application, and
+; the real mode interrupt handler.
+;
+; Input: User regs at time of interrupt
+; Output: regs as returned by interrupt handler
+; Errors:
+; Uses: none
+;
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMIntr5C
+
+PMIntr5C proc near
+
+if PM_NCB_HANDLING
+;
+; we no longer want to make the call through the real-mode handler, since all
+; it does is make the BOP
+;
+
+ cld ; just in case
+ push dx
+ mov dl,5ch
+
+DoNetRqst:
+
+;
+; Do a sync/async NETBIOS presence check. Don't BOP for this simple case.
+; Also don't do the async call because Delrina WinFax Pro hasn't initialized
+; The post address in the NCB.
+;
+ cmp byte ptr es:[bx],7fh ; sync NETBIOS presence check
+ je skip_bop
+ cmp byte ptr es:[bx],0ffh ; async NETBIOS presence check
+ je skip_bop
+ifdef WOW_x86
+.386p
+ push ds
+ mov ds,selDgroupPM
+
+ assume ds:DGROUP
+
+ FBOP BOP_REDIR, SVC_NETBIOS5C, FastBop
+ pop ds
+.286p
+else
+ SVC SVC_NETBIOS5C
+endif ; WOW_x86
+
+ cmp dl,2ah
+ jne @f
+
+;
+; this was a 0x2A call. Return ah = 0 if NetBIOS returned al = 0, else return
+; ah = 1
+;
+
+ sub ah,ah
+ or al,al
+ jz @f
+ inc ah
+@@: pop dx
+ riret
+
+skip_bop:
+ mov al,3 ; INVALID COMMAND error
+ mov es:[bx].ncb_retcode,al ; returned in NCB_RETCODE && al
+ mov es:[bx].ncb_cmd_cplt,al ; and NCB_CMD_CPLT
+ pop dx
+ riret
+
+
+else
+
+ cld ;cya...
+
+ push bx ;save caller's bx
+ mov bx,4*5Ch ;indicate we came from int 5C
+
+DoNetRqst label near ;start of int 2Ah/5Ch common code
+
+ push ax ;move the address of the
+ push ds ; proper real mode interrupt
+ mov ds,selDgroupPM ; handler (2A or 5C) to global var
+ assume ds:DGROUP
+ push es
+
+ mov ax,SEL_RMIVT OR STD_RING
+ mov es,ax
+ mov ax,word ptr es:[bx]
+ mov word ptr lpfnRmNetIsr,ax
+ mov ax,word ptr es:[bx+2]
+ mov word ptr [lpfnRmNetIsr+2],ax
+
+ pop es
+ pop ds ;restore caller's environment
+ assume ds:NOTHING
+ pop ax
+ pop bx
+
+ call EnterIntHandler ;build an interrupt stack frame
+ assume ds:DGROUP,es:NOTHING
+
+ FSTI ;-----------------------------------------------
+
+; Allocate space in the low heap for the HCB/NCB and maybe a buffer
+
+ xor dx,dx
+ mov cx,HCB_SIZE ;DX:CX = # bytes low heap required
+
+ call GetEntryExitFlags ;AX=Entry flags / ES:BX -> PM NCB
+
+ test al,BUFF_IN+BUFF_OUT+BUFF_CHAIN ;need to allocate a low buffer?
+ jz i5c_alloc
+
+;
+; RLF 06/09/93
+;
+; from the command type, we think we have a buffer address and size. If either
+; of these is 0, then we actually don't have a buffer; skip it
+;
+
+ mov ah,al ; set default no buffer
+ and al,not (BUFF_IN or BUFF_OUT)
+ mov dx,es:[bx.NCB_Buffer_Off]
+ or dx,es:[bx.NCB_Buffer_Seg]
+ jz i5c_alloc
+ mov dx,es:[bx.NCB_Length]
+ or dx,dx
+ jz i5c_alloc
+ xor dx,dx ; restore dx to 0
+ mov al,ah ; restore buffer flags
+
+ add cx,es:[bx.NCB_Length] ;if so, add in it's length
+ adc dx,0
+ add cx,1fh ;allow space to align buffer
+ adc dx,0 ; on a paragraph boundry
+ and cx,not 0fh
+
+ test al,BUFF_CHAIN ;a 2nd weird-o buffer?
+ jz i5c_alloc
+
+ add cx,word ptr es:[bx.NCB_CallName] ;yes, add in it's len also
+ adc dx,0
+ add cx,1fh ;allow space to align buffer
+ adc dx,0 ; on a paragraph boundry
+ and cx,not 0fh
+
+i5c_alloc:
+ push ax ;save entry/exit flags
+
+ FCLI ;treat allocate as critical section ------------
+
+ call AllocateLowBlock ;allocate conventional memory for
+ jnc @f ; HCB/NCB and maybe buffer
+
+ Debug_Out "Low heap allocation failed!"
+
+ pop ax ;clear stack
+
+ mov byte ptr [bp].intUserAX,RC_Resources
+ mov es:[bx.NCB_RetCode],RC_Resources
+ mov es:[bx.NCB_Cmd_Cplt],RC_Resources
+ jmp i5c_done
+@@:
+
+; Copy the PM NCB to the low heap and add our extra HCB fields
+
+ mov es,ax
+ xor di,di ;es:di -> low heap block
+
+ cld
+ stosw ;HCB_Handle
+ mov ax,bx
+ stosw ;HCB_PM_NCB_Off
+ mov ax,[bp].pmUserES
+ stosw ;HCB_PM_NCB_Seg
+ mov ax,HCB_List
+ stosw ;HCB_Next
+ xor ax,ax
+ stosw ;HCB_Flags = 0
+
+ mov HCB_List,es ;link HCB to head of list
+
+ FSTI ;-----------------------------------------------
+
+ push ds
+ mov ds,[bp].pmUserES
+ assume ds:NOTHING
+
+ errnz <size NCB_Struc and 1> ;if odd # bytes, can't just movsw
+
+ mov si,bx ;ES:DI->low NCB, DS:SI->PM NCB
+ mov cx,size NCB_Struc / 2
+ rep movsw
+
+ pop ds
+ assume ds:DGROUP
+
+
+; Update the interrupt handler's registers to point to the low NCB
+
+ mov ax,es
+ call GetSegmentAddress
+ add dx,HCB_Header_Size
+ adc bx,0 ;BX:DX = lma of low NCB
+
+ pop ax ;refresh entry flags
+ push ax
+
+ push bx ;save lma of NCB
+ push dx
+
+ call Lma2SegOff ;BX:DX = normalized SEG:OFF of low NCB
+
+ dec bx ;we want to do a negative offset on the
+ add dx,10h ; NCB segment, so un-normalize it some
+
+ mov [bp].intUserES,bx ;point int handler's ES:BX to low NCB
+ mov [bp].intUserBX,dx
+
+
+; If this is a cancel request, find the target NCB in the low heap
+
+ test al,BUFF_CANCEL ;cancel request?
+ jz i5c_not_cancel
+
+ mov di,HCB_Header_Size ;ES:DI -> low heap Cancel NCB
+
+ FCLI ;don't want list changing while looking --------
+
+ call FindTargetNCB ;BX:DX = SEG:OFF of target NCB
+
+ FSTI ;-----------------------------------------------
+
+if DEBUG ;------------------------------------------------------------
+ jnc @f
+ Debug_Out "FindTargetNCB didn't!"
+@@:
+endif ;DEBUG --------------------------------------------------------
+
+ jc @f
+
+ mov es:[di.NCB_Buffer_Off],dx ;point cancel NCB to low
+ mov es:[di.NCB_Buffer_Seg],bx ; heap target NCB
+@@:
+
+i5c_not_cancel:
+
+ pop dx ;restore lma of NCB
+ pop bx
+
+
+; If necessary, update NCB buffer pointer(s) and copy buffer(s) to low heap
+
+ pop ax ;recover entry flags
+
+ test al,BUFF_IN+BUFF_OUT+BUFF_CHAIN ;a buffer to point to?
+ jnz @f
+ jmp i5c_no_buf_in
+@@:
+
+ add dx,size NCB_Struc + 0fh ;get lma of buffer, rounded up
+ adc bx,0 ; to the next higher paragraph
+ and dx,not 0fh
+
+ push ds
+ mov ds,[bp].pmUserES
+ mov si,[bp].pmUserBX ;DS:SI -> PM NCB
+ assume ds:NOTHING
+
+ push bx
+ push dx
+
+ call Lma2SegOff ;BX:DX = SEG:OFF of low buffer
+
+ mov di,HCB_Header_Size ;ES:DI -> low NCB
+ mov es:[di.NCB_Buffer_Off],dx
+ mov es:[di.NCB_Buffer_Seg],bx
+
+ pop dx
+ pop bx
+
+ test al,BUFF_IN+BUFF_CHAIN ;actually need to copy buffer?
+ jz i5c_buf_in_done
+
+ mov cx,ds:[si.NCB_Length] ;CX = buffer len
+ lds si,dword ptr ds:[si.NCB_Buffer_Off] ;DS:SI -> buffer
+
+ push ax
+ xor ax,ax
+ call CopyBuffer ;copies DS:SI to lma BX:DX len CX
+ pop ax
+
+ test al,BUFF_CHAIN
+ jz i5c_buf_in_done
+
+ add dx,es:[di.NCB_Length] ;BX:DX = lma of 2nd low heap buffer
+ adc bx,0
+ add dx,0fh
+ adc bx,0
+ and dx,not 0fh
+
+ push bx ;update low heap NCB with SEG:OFF
+ push dx ; of 2nd buffer
+
+ call Lma2SegOff
+
+ mov word ptr es:[di.NCB_CallName+2],dx ;2nd buffer loc stored
+ mov word ptr es:[di.NCB_CallName+4],bx ; at callname + 2
+
+ pop dx
+ pop bx
+
+ mov ds,[bp].pmUserES
+ mov si,[bp].pmUserBX ;DS:SI -> PM NCB
+
+ mov cx,word ptr ds:[si.NCB_CallName] ;CX = buffer len
+ lds si,dword ptr ds:[si.NCB_CallName+2] ;DS:SI -> buffer
+
+ xor ax,ax
+ call CopyBuffer ;copies DS:SI to lma BX:DX len CX
+
+
+i5c_buf_in_done:
+
+ pop ds
+ assume ds:DGROUP
+
+i5c_no_buf_in:
+
+; Switch to real mode, and load the mapped real mode registers.
+
+ SwitchToRealMode ;also disables ints
+ assume ss:DGROUP
+
+; --------------- START OF REAL MODE CODE ------------------------------
+
+ pop es
+ pop ds
+ assume ds:NOTHING,es:NOTHING
+ popa ;restore all the other registers
+
+; If this is a NoWait command, hook the mapped NCB to point to our post
+; routine. At this time ES:BX -> mapped NCB
+
+ test byte ptr es:[bx.NCB_Command],NoWait
+ jz @f
+
+ push ax ;save
+
+;
+; RLF 06/09/93
+;
+; although this is an async command, there may be no post address (app just
+; polls return code until it goes non-0xff). In this case, don't set up our
+; asynchronous notification routine
+;
+
+ mov ax,es:[bx.NCB_Post_Off]
+ or ax,es:[bx.NCB_Post_Seg]
+ jz no_post_routine
+
+ assume ss:DGROUP ;ss has DOSX's data seg
+ mov ax,[SegNetStubCode] ;get segment of stub code
+
+; the address of the stub code that will handle the POST is AX:0. Save this
+; in the HCB.
+
+ mov word ptr es:[bx.NCB_Post_Off],0
+ mov word ptr es:[bx.NCB_Post_Seg],ax
+
+no_post_routine:
+ pop ax ;restore
+@@:
+ or byte ptr es:[bx.HCB_FLAGS],HCB_ISSUED
+
+; Invoke the appropriate real mode interrupt handler (2Ah or 5Ch),
+; reestablish our stack frame with the handler's returned registers,
+; and then back to protected mode
+
+ call lpfnRmNetIsr ;exectue real mode interrupt handler
+ pushf
+ FCLI
+ pusha
+ push ds
+ push es
+
+ mov bp,sp ;restore stack frame pointer
+
+ mov ax,es:[bx.HCB_Handle] ;recover selector to current NCB/buff
+ push ax
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+
+; --------------- START OF PROTECTED MODE CODE -------------------------
+
+; With some network drivers, and some NoWait commands, the operation may
+; get 'posted' even before the driver IRETs from the issue call. If this
+; has happened, the HCB_POSTED flag will be set. In this case, the caller's
+; NCB and (possibly) buffer has already been updated, so we just discard
+; the low heap block and exit. If the operation hasn't been posted already,
+; we copy back the updated NCB info, and maybe a buffer. Note that interrupts
+; are disabled, so we don't get 'posted' half way through this operation--
+; that would be bad.
+
+ pop ax ;handle (selector) of low heap block
+ mov es,ax
+ mov di,HCB_Header_Size ;es:di -> low heap NCB
+
+ test byte ptr es:[di.HCB_Flags],HCB_POSTED ;already posted?
+ jnz i5c_release ; yes, just discard
+
+ and byte ptr es:[di.HCB_Flags],not HCB_ISSUED ;so post rtn knows
+ ;we updated already
+
+ push ax ;save handle for later
+
+ call UpdateNCB ;update the caller's NCB
+
+ call GetEntryExitFlags ;AX=Exit Flags / ES:BX->PM NCB
+
+ test es:[bx.NCB_Command],NoWait ;don't copy buff now if NoWait
+ jnz i5c_no_buf_out
+
+ test al,BUFF_OUT
+ jz i5c_no_buf_out
+
+;
+; RLF 06/09/93
+;
+; although the flags for this command say we have an output buffer, we may
+; not have one - address or length is 0; check it out
+;
+
+ mov ax,es:[bx.NCB_Buffer_Off]
+ or ax,es:[bx.NCB_Buffer_Seg]
+ jz i5c_no_buf_out
+ mov ax,es:[bx.NCB_Length]
+ or ax,ax
+ jz i5c_no_buf_out
+
+ pop ax ;handle to low block back
+ push ax
+
+ call CopyBufferOut ;copy low heap buffer to pm app buffer
+
+i5c_no_buf_out:
+
+
+; If this was a Wait operation (or NoWait that failed), we are finished with
+; the low heap block and can release it now.
+
+ pop ax ;recover handle to low heap block
+
+ test es:[bx.NCB_Command],NoWait ;if Wait, go release now
+ jz i5c_release
+
+ cmp es:[bx.NCB_RetCode],RC_Pending
+ jz i5c_done
+
+; Most NetBIOS implementations seem to (correctly) set the RetCode to
+; RC_Pending on NoWait requests. However, it seems that some Novell
+; NetBIOS implementations can return RetCode == 00 but Cmd_Cplt ==
+; RC_Pending (FFh). So, if it is a NoWait request, and RetCode isn't
+; Pending, also check the Cmd_Cplt code.
+
+ cmp es:[bx.NCB_Cmd_Cplt],RC_Pending
+ jz i5c_done
+
+ Debug_Out "NoWait cmd with non Pending retcode!"
+
+
+i5c_release:
+
+ push ds ;make sure es != low heap block sel
+ pop es ; (else FreeLowBlock will GP fault)
+
+ call DeLink ;DeLink HCB/NCB/Buffers from lnk list
+
+ call FreeLowBlock ;free the low heap block
+
+
+; Finished! (at least for now) Restore caller's regs/stack and return
+
+i5c_done:
+ mov ax,[bp].pmUserBX ;restore possibly modified BX to
+ mov [bp].intUserBX,ax ; PM NCB offset
+
+ call LeaveIntHandler
+
+ riret
+
+endif ; if PM_NCB_HANDLING
+
+PMIntr5C endp
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl NetBIOS API Mapper Post Routine
+ page
+; -------------------------------------------------------
+; NetBIOS API MAPPER POST ROUTINE
+; -------------------------------------------------------
+
+;*******************************************************************************
+;*
+;* The code from here on down is only used if we are switching between real
+;* and protect mode when making NetBIOS requests. It is ifdef'd out for WOW
+;* because we now BOP the NetBIOS requests without switching modes
+;*
+;******************************************************************************/
+
+;ife PM_NCB_HANDLING
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; RMPostRtn -- This REAL MODE routine is invoked by the network
+; software when a NoWait NetBIOS command completes. This
+; routine must update the applications copy of the NCB,
+; possibly copy a buffer of data to the application, and
+; possibly invoke the application's own post routine.
+;
+; Note, this will now be called when it is OK to POST the request
+; back to the application. The Stub Code in global mrmory makes
+; sure that this is OK to do.
+;
+; Input:
+; Output:
+; Errors:
+; Uses:
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public RMPostRtn
+
+RMPostRtn proc far
+
+ FCLI ;just to be sure
+ cld ;cya...
+
+ push ax
+ push bx
+ push es
+ push ds ; NBP assumes ds saved, greaseballs
+
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame.
+
+ mov regUserSP,sp
+ mov regUSerSS,ss
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,selDgroup
+ENDIF
+ mov sp,pbReflStack
+
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+
+ FIX_STACK
+ push regUserSS ;save current stack loc on our stack
+ push regUserSP ; so we can restore it later
+
+ push es:[bx.HCB_Handle] ;selector to low heap block
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ SwitchToProtectedMode ;destroys ax
+
+; --------------- START OF PROTECTED MODE CODE -------------------------
+
+ pop ax ;ax = selector to low heap block
+
+ call NetBiosPostRoutine ;do the actual posting
+
+ pop regUserSP ;recover previous stack location
+ pop regUserSS
+
+ SwitchToRealMode ; Switch back to real mode.
+
+; --------------- START OF REAL MODE CODE ------------------------------
+
+; Switch back to the original stack, deallocate the interrupt stack frame,
+; and return to the network software
+
+ CHECK_STACK
+ mov ss,regUserSS
+ mov sp,regUserSP
+ add pbReflStack,CB_STKFRAME
+
+ pop ds ; give ds back to NBP/XNS
+ pop es
+ pop bx
+ pop ax
+ ret
+
+RMPostRtn endp
+
+DXCODE ends
+
+; -------------------------------------------------------
+ subttl NetBIOS API Mapper Utility Routines
+ page
+; -------------------------------------------------------
+; NetBIOS API MAPPER UTILITY ROUTINES
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; NetBiosPostRoutine -- local routine
+; This PROTECT MODE routine is called when the network
+; software completes a NoWait NetBIOS command. This
+; routine must update the applications copy of the NCB,
+; possibly copy a buffer of data to the application, and
+; possibly invoke the application's own post routine.
+;
+; Input: ax --> selector to low heap block
+; Output:
+; Errors:
+; Uses: ax,bx,es
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public NetBiosPostRoutine
+
+NetBiosPostRoutine proc near
+
+IFNDEF WOW_x86
+ pusha
+else
+.386p
+ pushad
+ push fs
+ push gs
+.286p
+endif
+
+; Update the protected mode copy of the NCB and copy buffer if required
+
+ push ax ;handle (selector) of low heap block
+
+ call UpdateNCB ;always update the caller's NCB
+
+ mov es,ax
+ mov bx,HCB_Header_Size ;ES:BX -> low heap NCB
+ les bx,dword ptr es:[bx.HCB_PM_NCB_Off] ;ES:BX -> PM NCB
+
+ call GetExitFlags2 ;AX=Exit Flags
+
+ test al,BUFF_OUT
+ jz postsub_no_buf
+
+;
+; RLF 06/09/93
+;
+; once more unto the breach dear friends: although we think this command has
+; an output buffer, the app may think otherwise; let's check...
+;
+
+ mov ax,es:[bx.NCB_Buffer_Off]
+ or ax,es:[bx.NCB_Buffer_Seg]
+ jz postsub_no_buf
+ mov ax,es:[bx.NCB_Length]
+ or ax,ax
+ jz postsub_no_buf
+
+ pop ax ;handle to low block back
+ push ax
+
+ call CopyBufferOut ;copy low heap buffer to pm app buffer
+
+postsub_no_buf:
+
+; Release the low heap space, unless the HCB_ISSUED flag is set (meaning
+; that the net driver called us before returning from the initial Int 5Ch).
+; In that case, the low heap block will be released by the Int 5Ch mapper.
+
+ pop ax ;recover handle to low heap block
+ push ax
+
+ push es
+ mov es,ax
+ mov di,HCB_Header_Size
+ or byte ptr es:[di.HCB_Flags],HCB_POSTED ;mark as posted
+ pop es
+
+; Invoke the user's PM post routine, if there is one - AL=retcode, ES:BX->NCB
+
+ mov ax,es:[bx.NCB_Post_Off] ;did user specify a post routine?
+ or ax,es:[bx.NCB_Post_Seg]
+ jz postsub_done
+
+ mov [NBPSp],sp
+ rpushf ;build iret frame for user's
+ push cs ; routine to return to us
+ push offset postsub_ret
+
+ mov al,es:[bx.NCB_RetCode] ;pass ret code in al
+
+ push es:[bx.NCB_Post_Seg] ;invoke the user's post routine
+ push es:[bx.NCB_Post_Off]
+
+ retf
+
+; PM app's post routine returns here when finished
+
+postsub_ret:
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ mov sp,[NBPSp]
+
+postsub_done:
+ pop ax
+ mov es,ax
+ test byte ptr es:[di.HCB_Flags],HCB_ISSUED ;still in Int 5C code?
+ jnz postsub_no_release ; yes, don't release
+
+ push ds ;make sure es != low heap block sel
+ pop es ; (else FreeLowBlock will GP fault)
+
+ call DeLink ;DeLink HCB/NCB/Buffers from lnk list
+
+ call FreeLowBlock ;free the low heap block
+postsub_no_release:
+
+IFNDEF WOW_x86
+ popa
+ELSE
+.386p
+ pop gs
+ pop fs
+ popad
+.286p
+ENDIF
+ ret
+
+NetBiosPostRoutine endp
+
+; -------------------------------------------------------
+; DelayNetPostings -- This function is called when NetBIOS completions
+; are to be delayed. We simply set a flag that causes the async
+; post routine not to post to the application.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public DelayNetPosting
+
+DelayNetPosting proc near
+
+; Make sure posting is delayed. We need to set a flag in the StubCode area.
+
+ push es ;save
+ push di
+ mov di,[SelNetStubCode] ;selector for the stub area
+ or di,di ;did we copy to global memory ?
+ jz DelayNetPostingRet ;no, nothing to do.
+ mov es,di
+
+; get the address of the flag byte fStubDelayNetPosting after the block was
+; relocated
+
+ mov di,[AddrfStubDelayNetPosting]
+
+; ES:DI has the flag byte address. Set the flag
+
+ mov byte ptr es:[di],1 ;set flag to delay posting
+
+DelayNetPostingRet:
+
+ pop di
+ pop es ;restore
+ ret
+
+DelayNetPosting endp
+
+
+; -------------------------------------------------------
+; ResumeNetPostings -- This function is called when completed NetBIOS
+; postings can be resumed to the application. We traverse the
+; list of NetBIOS requests, and 'post' the application on any that
+; completed while posting were delayed.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public ResumeNetPosting
+
+ResumeNetPosting proc near
+
+ push es
+ push di
+ push bx ; used by NetBiosPostRoutine
+ push ax
+
+; set a flag in the StubCode area to imply that POSTing need not be delayed.
+
+ mov di,[SelNetStubCode] ;selector for the stub area
+ or di,di ;global heap in place ?
+ jnz dummy1
+ jmp ResumeNetPostingRet ;no.
+dummy1:
+ mov es,di
+
+; get the address of the flag byte fStubDelayNetPosting after the block was
+; relocated
+
+ mov di,[AddrfStubDelayNetPosting]
+
+; ES:DI has the flag byte address. ReSet the flag
+
+ mov byte ptr es:[di],0 ;reset the flag
+
+
+ FCLI ;protect access to linked list
+
+ mov cx,HCB_List
+ mov di,HCB_Header_Size ;offset of NCB in low heap block
+
+resume_next:
+ jcxz resume_done
+
+ mov es,cx
+ mov cx,es:[di.HCB_Next]
+
+ test byte ptr es:[di.HCB_Flags],HCB_DELAY ;Has this one completed?
+ jz resume_next ; no, skip it
+
+ ;Y: this packet was delayed...post it NOW !
+
+ mov ax,es:[di.HCB_Handle] ;selector to low heap block
+
+ FSTI
+
+ call NetBiosPostRoutine ;post the NCB finally
+
+ FCLI
+
+ ; *** modification warning ***
+ ; note: block has just been de-linked from list, so we MUST
+ ; get the pointer to the next block before NetBiosPostRoutine
+ ; is called (which calls DeLink)!
+
+ jmp short resume_next
+
+resume_done:
+
+ FSTI
+
+
+ResumeNetPostingRet:
+
+ pop ax
+ pop bx
+ pop di
+ pop es
+ ret
+
+ResumeNetPosting endp
+
+
+; ------------------------------------------------------
+; CopyBufferOut -- This routine copies a buffer from the low heap
+; block up to the PM app's buffer area.
+;
+; Input: AX = Selector to low heap block
+; ES:BX -> PM NCB
+; Output: none
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:NOTHING
+ public CopyBufferOut
+
+CopyBufferOut proc near
+
+ pusha
+
+ mov cx,es:[bx.NCB_Buffer_Seg]
+ or cx,es:[bx.NCB_Buffer_Off]
+ jz CBO_Buffer_Is_Null
+
+ push ds
+
+ mov di,bx ;ES:DI -> PM NCB
+ call GetSegmentAddress ;BX:DX = lma of low heap block
+
+ lds si,dword ptr es:[di.NCB_Buffer_Off] ;DS:SI -> PM buffer
+ assume ds:NOTHING
+
+ add dx,size NCB_Struc + HCB_Header_Size + 0fh
+ adc bx,0
+ and dx,not 0fh ;BX:DX = lma of low heap buffer
+
+ mov cx,es:[di.NCB_Length] ;CX = buffer length
+
+ mov al,1
+ call CopyBuffer ;copies from lma BX:DX to DS:SI, len CX
+
+ pop ds
+ assume ds:DGROUP
+
+CBO_Buffer_Is_Null:
+ popa
+
+ ret
+
+CopyBufferOut endp
+
+
+; ------------------------------------------------------
+; CopyBuffer -- This routine copies a buffer of length CX
+; from DS:SI to the lma in BX:DX _or_ from the lma in
+; BX:DX to DS:SI.
+;
+; Input: AX = direction flag, Z = from DS:SI -> lma,
+; NZ = from lma -> DS:SI
+; BX:DX = lma of source or dest
+; CX = length in bytes
+; DS:SI = pointer to source or dest
+; Output: none
+; Errors: none
+; Uses: none
+
+ assume ds:NOTHING,es:NOTHING
+ public CopyBuffer
+
+CopyBuffer proc near
+
+ pusha
+ push ds
+ push es
+
+
+ rpushf
+ FCLI
+
+; Setup a selector/descriptor for the linear memory address
+
+ push ax ;save direction flag
+
+ push ds
+ mov ds,selDgroupPM ;temp dgroup addressability
+ assume ds:DGROUP
+
+ mov ax,selNetScr ;our scratch selector to use
+ dec cx ;length to limit
+
+
+ cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
+
+ pop ds
+ assume ds:NOTHING
+
+ inc cx ;back to length
+
+; If necessary, adjust the length so we don't fault due by overrunning the
+; DS segment
+
+ mov bx,ds ;get limit for DS segment
+ lsl bx,bx
+ sub bx,si ;less the DS offset
+ inc bx ; (limit is len - 1)
+ cmp bx,cx ;at least CX bytes left in segment?
+ jae @f
+ mov cx,bx ; no, only do # remaining
+@@:
+
+; Copy the buffer
+
+ xor di,di ;AX:DI is now SEL:OFF to lma
+
+ pop bx ;recover direction flag
+ or bx,bx
+ jnz @f
+
+ mov es,ax ;from DS:SI -> AX:DI, almost ready
+ jmp short cb_copy
+@@:
+ xchg si,di ;from AX:DI -> DS:SI, adjust
+ push ds ; regs for movs instruction
+ pop es
+ mov ds,ax
+
+cb_copy:
+ cld
+ shr cx,1 ;byte count to words, low bit to CY
+ rep movsw
+ jnc @f
+ movsb ;get any odd byte
+@@:
+ npopf
+
+ pop es
+ pop ds
+ popa
+
+ ret
+
+CopyBuffer endp
+
+
+; ------------------------------------------------------
+; DeLink -- This routine will unlink a HCB/NCB/Buffer low heap
+; block from the HCB_List linked list.
+;
+; Input: AX = selector to block to unlink
+; Output: block unlinked
+; Uses: none
+
+ assume ds:DGROUP,es:NOTHING
+ public DeLink
+
+DeLink proc near
+
+ push bx
+ push cx
+ push es
+
+ mov bx,HCB_Header_Size
+
+ cmp ax,HCB_List ;special case likely condition
+ jnz ul_search
+
+ mov es,ax ;ES:BX -> block to unlink
+
+ mov cx,es:[bx.HCB_Next] ;block is first in linked list
+ mov HCB_List,cx
+ jmp short ul_done
+
+ul_search:
+ mov es,HCB_List ;ES:BX -> first block in list
+
+ul_loop:
+ mov cx,es:[bx.HCB_Next] ;is this just before the block?
+ cmp ax,cx
+ jz ul_got_it
+
+ mov es,cx ; no, try the next one
+ jmp short ul_loop
+
+ul_got_it:
+ push es ;okay, cut the selected block
+ mov es,cx ; out of the linked list
+ mov cx,es:[bx.HCB_Next]
+ pop es
+ mov es:[bx.HCB_Next],cx
+
+ul_done:
+ pop es
+ pop cx
+ pop bx
+
+ ret
+
+DeLink endp
+
+
+; ------------------------------------------------------
+; FindTargetNCB -- This routine searches the low memory heap
+; to locate an NCB pointed to by another user PM NCB.
+;
+; Input: ES:DI -> Cancel NCB pointing to PM target NCB
+; Output: BX:DX = RM SEG:OFF of target NCB in low heap
+; Error: CY if target NCB can't be found
+; Uses: none
+
+ assume ds:DGROUP,es:NOTHING
+ public FindTargetNCB
+
+FindTargetNCB proc near
+
+ push ax
+ push cx
+ push di
+ push es
+
+ mov bx,es:[di.NCB_Buffer_Seg] ;get selector:offset of PM
+ mov dx,es:[di.NCB_Buffer_Off] ; target NCB to cancel
+
+
+; Search low heap list looking for copy of target NCB
+
+ mov cx,HCB_List
+ mov di,HCB_Header_Size ;offset of NCB in low heap block
+
+ft_next:
+ jcxz ft_err
+
+ mov es,cx ;ES:DI -> first/next HCB/NCB in list
+
+ cmp bx,es:[di.HCB_PM_NCB_Seg] ;is this the one?
+ jnz ft_not_it
+ cmp dx,es:[di.HCB_PM_NCB_Off]
+ jz ft_got_it
+
+ft_not_it: ; no, get ptr to next one
+ mov cx,es:[di.HCB_Next]
+ jmp short ft_next
+
+ft_got_it:
+
+; ES:DI now points at the low heap copy of the target NCB, convert to SEG:OFF
+
+ mov ax,es
+ call GetSegmentAddress
+ add dx,di
+ adc bx,0 ;BX:DX = lma of low target NCB
+
+ call Lma2SegOff ;BX:DX = normalized SEG:OFF
+
+ dec bx ;return the same slightly unnormalized
+ add dx,10h ; SEG:OFF that was used initially
+
+ clc ;found it!
+ jmp short ft_done
+
+ft_err: stc ;couldn't find the target ?!
+
+ft_done:
+ pop es
+ pop di
+ pop cx
+ pop ax
+
+ ret
+
+FindTargetNCB endp
+
+
+;--------------------------------------------------------
+; GetEntryExitFlags -- This routine looks up the entry/exit mapping
+; flags for the current NetBIOS command.
+;
+; Input: user regs on stack frame
+; Output: AX = flags
+; ES:BX -> caller's PM NCB
+; Errors: none
+; Uses:
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetEntryExitFlags
+
+GetEntryExitFlags proc near
+
+ mov es,[bp].pmUserES ;point ES:BX to pm app's NCB
+ mov bx,[bp].pmUserBX ; and get command code
+
+GetExitFlags2 label near
+
+ push di
+
+ mov al,es:[bx.NCB_Command]
+ and al,7Fh
+ cbw
+
+ mov di,ax ;map NCB command code to API
+ mov al,ApiMapTbl[di] ; mapping code via ApiMapTbl
+
+ shr ax,2 ;use map code to select entry/exit
+ mov di,ax ; flags via EntryExitCode
+ mov al,EntryExitFlags[di]
+
+ pop di
+ ret
+
+GetEntryExitFlags endp
+
+
+; ------------------------------------------------------
+; UpdateNCB -- This routine updates the user's PM NCB copy from the
+; low heap real mode copy.
+;
+; Input: AX = selector pointing to low heap HCB/NCB
+; Output: User's PM NCB updated
+; Errors: none
+; Uses: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public UpdateNCB
+
+UpdateNCB proc near
+
+ push ax
+ push cx
+ push si
+ push di
+ push ds
+ push es
+
+ mov ds,ax
+ mov si,HCB_Header_Size ;DS:SI -> low heap NCB
+
+ les di,dword ptr [si.HCB_PM_NCB_Off] ;ES:DI -> protect mode NCB
+
+ mov al,[si.NCB_Command] ;want to check the command code later
+ and al,not NoWait
+
+ cld
+ movsw
+ movsw ;copy command, retcode, LSN, Num
+
+ add si,4 ;skip the buffer pointer
+ add di,4
+
+ movsw ;move the length
+
+ mov cx,34 / 2 ;len of callname, name, rto, sto (words)
+
+ cmp al,ChainSend ;funkey chain send command?
+ jnz @f
+
+ add si,16 ; yes, skip callname
+ add di,16
+ sub cx,16 / 2
+@@:
+ rep movsw ;move name, rto, sto, maybe callname
+
+ add si,4 ;skip the post address
+ add di,4
+
+ mov cx,16 / 2 ;move the rest
+ rep movsw
+
+ pop es
+ pop ds
+ pop di
+ pop si
+ pop cx
+ pop ax
+
+ ret
+
+UpdateNCB endp
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+;endif ; ife PM_NCB_HANDLING
+
+DXDATA segment
+
+my_sp dw ?
+
+DXDATA ends
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+;*******************************************************************************
+;*
+;* HandleNetbiosAnr
+;*
+;* Called when a simulated h/w interrupt for net callback functions occurs.
+;* Checks if the call is a PM netbios ANR. If it is, we get the NCB
+;* information and call the PM post routine
+;*
+;* ENTRY Nothing
+;*
+;* EXIT Nothing
+;*
+;* RETURNS Nothing
+;*
+;* ASSUMES Nothing
+;*
+;******************************************************************************/
+
+ public HandleNetbiosAnr
+HandleNetbiosAnr proc
+
+ assume cs:DXPMCODE
+ assume ds:DGROUP
+ assume es:nothing
+ assume ss:nothing
+
+;
+; perform a BOP to discover if this is a protect mode netbios ANR. If it is then
+; es and bx will point to the NCB
+;
+
+ push ds
+ push SEL_DXDATA OR STD_RING
+ pop ds
+
+ifdef WOW_x86
+.386p
+ FBOP BOP_REDIR, SVC_NETBIOSCHECK, FastBop
+.286p
+else
+ SVC SVC_NETBIOSCHECK
+endif ; WOW_x86
+
+ jnz @f
+ jmp chain_previous_int ; not PM Netbios ANR
+
+@@:
+
+;
+; this is a PM Netbios ANR. Save state and call the post routine. There MUST be
+; a post routine if we're here
+;
+
+IFNDEF WOW_x86
+ pusha
+ELSE
+.386p
+ pushad ; save all 32 bits
+ push fs
+ push gs
+.286p
+ENDIF
+ push es
+
+ifdef WOW_x86
+.386p
+ FBOP BOP_REDIR, SVC_NETBIOS5CINTERRUPT, FastBop
+.286p
+else
+ SVC SVC_NETBIOS5CINTERRUPT
+endif ; WOW_x86
+
+;
+; save the stack pointer - apparently some apps will RETF, not IRET from the
+; ANR. NB - this ISR cannot be re-entered since we only have the one previous
+; stack pointer saved. This should be okay as long as the EOI is issued at the
+; end of the ISR
+;
+
+ mov my_sp,sp
+
+;
+; perform a fake interrupt to the PM post routine. ES:BX point at the NCB, AL is
+; the return code. Post routine will IRET back (supposedly)
+;
+
+ mov al,es:[bx.NCB_RetCode] ; pass ret code in al
+ pushf
+ call dword ptr es:[bx.NCB_Post_Off]
+
+;
+; restore our data segment and stack pointer, lest the app didn't IRET
+;
+
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ mov sp,my_sp
+
+;
+; restore the interrupted state, then perform a BOP to reset the PIC and clean
+; up any interrupt state
+;
+
+ pop es
+IFNDEF WOW_x86
+ popa
+ELSE
+.386p
+ pop gs
+ pop fs
+ popad
+.286p
+ENDIF
+
+;
+; this BOP will clear the emulated PICs (sends non-specific EOIs), and cause the
+; next NET interrupt to be generated, if one is queued
+;
+
+ifdef WOW_x86
+.386p
+ FBOP BOP_REDIR, SVC_RDRINTACK2, FastBop
+.286p
+else
+ SVC SVC_RDRINTACK2
+endif ; WOW_x86
+
+;
+; restore the rest of the interrupted context and resume
+;
+
+ pop ds
+ riret
+
+chain_previous_int:
+ push word ptr OldInt76+2 ; selector of previous handler
+ push word ptr OldInt76 ; offset of previous handler
+ push bp
+ mov bp,sp
+ mov ds,[bp+6] ; retrieve interrupted ds
+ pop bp
+ retf 2 ; chain previous handler, removing space
+ ; used for ds
+
+HandleNetbiosAnr endp
+
+ public HookNetBiosHwInt
+
+HookNetBiosHwInt proc near
+
+ ;
+ ; Save everything!!
+ ;
+ pusha
+ push ds
+ push es
+
+ mov ax,3576h ; get old handler
+ int 21h
+
+ mov word ptr [OldInt76],bx
+ mov word ptr [OldInt76 + 2],es
+
+ mov dx,SEL_NBPMCODE OR STD_RING
+ mov ds,dx
+ mov dx,offset HandleNetbiosAnr
+ mov ah,25h
+ int 21h ; set new handler
+
+ pop es
+ pop ds
+ popa
+
+ ret
+HookNetBiosHwInt endp
+
+
+
+
+DXPMCODE ends
+
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxoem.asm b/private/mvdm/dpmi/dxoem.asm
new file mode 100644
index 000000000..c671fe3ac
--- /dev/null
+++ b/private/mvdm/dpmi/dxoem.asm
@@ -0,0 +1,179 @@
+ PAGE ,132
+ TITLE DXOEM.ASM -- Dos Extender OEM Interface
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXOEM.ASM - DOS Extender OEM Interface
+;
+;-----------------------------------------------------------------------
+;
+; This module contains the routines that may need to be modified by OEMs
+; when adding new device/interface support to the Microsoft 286 DOS
+; Extender portion of Windows/286. There are four routines contained
+; in this module:
+;
+; InitializeOEM - called during DOSX initialization
+;
+; SuspendOEM - called when the protected mode app is about
+; to be suspended (currently when running a
+; standard DOS 'old' app from Windows).
+;
+; ResumeOEM - called when the protected mode app is about
+; to be resumed (currently when returning from
+; a standard DOS 'old' app to Windows).
+;
+; TerminateOEM - called during DOSX termination
+;
+; Note: when this module refers to the 'OEM layer,' it is refering to
+; the 286 DOS Extender device drivers, API mappers, etc., not the
+; Windows OEM layer.
+;
+;-----------------------------------------------------------------------
+;
+; 06/28/89 jimmat Original version.
+; 11/29/90 amitc Removed SuspendOEM/ResumeOEM - not required for 3.1
+; 11/29/90 amitc Moved call to 'InitLowHeap' from here to DXNETBIO.ASM
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn InitNetMapper:NEAR
+ extrn TermNetMapper:NEAR
+ extrn ReleaseLowHeap:NEAR
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn NetHeapSize:WORD
+
+DXDATA ends
+
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+DXCODE ends
+
+DXPMCODE segment
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl OEM Initialization Routine
+ page
+; -------------------------------------------------------
+; OEM INITIALIZATION ROUTINE
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+;--------------------------------------------------------
+; InitializeOEM -- This routine is called during DOSX initialization
+; in order to initialize the OEM layer (device drivers, API
+; mappers, etc.). It expectes to be called late enough in the
+; initialization process that other interrupt mapping functions
+; (like Int 21h) are available.
+;
+; This routine is called in protected mode, and can enable/disable
+; interrupts if it so requires.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: ax, all others preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public InitializeOEM
+
+InitializeOEM proc near
+
+; Initialize the NetBios mapper.
+
+ mov ax,NetHeapSize ;don't initialize if net heap
+ or ax,ax ; size set to zero
+ jz @f
+
+ call InitNetMapper ;initialize the NetBIOS mapper--CY set
+ ; if NetBIOS not installed
+@@:
+ ret
+
+InitializeOEM endp
+
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl OEM Termination Routine
+ page
+; -------------------------------------------------------
+; OEM TERMINATION ROUTINE
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; TerminateOEM -- This routine is called during DOSX termination
+; to disable the OEM layer.
+;
+; Note: This routine must is called in REAL MODE! If some termination
+; code must run in protected mode, the routine must switch to
+; protected mode itself, and switch back to real mode before
+; returning.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: ax,bx,cx,dx,si,di,es
+;
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public TerminateOEM
+
+TerminateOEM proc near
+
+ call TermNetMapper ;terminate the NetBIOS mapper
+
+ call ReleaseLowHeap ; and release the low net heap
+
+ ret
+
+TerminateOEM endp
+
+; -------------------------------------------------------
+
+DXCODE ends
+;****************************************************************
+ end
diff --git a/private/mvdm/dpmi/dxrom.asm b/private/mvdm/dpmi/dxrom.asm
new file mode 100644
index 000000000..30b8dd89f
--- /dev/null
+++ b/private/mvdm/dpmi/dxrom.asm
@@ -0,0 +1,805 @@
+ PAGE ,132
+ TITLE DXROM.ASM -- Dos Extender ROM Specific Code
+
+; Copyright (c) Microsoft Corporation 1990-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXROM.ASM -- Dos Extender ROM Specific Code
+;
+;-----------------------------------------------------------------------
+;
+; This module contains code specific to the ROM version of the DOS
+; Extender.
+;
+;-----------------------------------------------------------------------
+;
+; 11/05/90 jimmat Created.
+;
+;***********************************************************************
+
+ .286p
+
+IFDEF ROM
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include dxrom.inc
+ .list
+
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+; Define some pubilc symbols to be exported for the ROM Image Builder
+
+ public __selDXCODE, __selDXDGROUP, __selDXPMCODE
+
+__selDXCODE = SEL_DXCODE
+__selDXDGROUP = SEL_DXDATA
+__selDXPMCODE = SEL_DXPMCODE
+
+ public __selFirstLDT, __selLDTAlias
+
+__selFirstLDT = SEL_USER
+__selLDTAlias = SEL_LDT_ALIAS
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn ParaToLinear:NEAR
+ extrn AllocateLDTSelector:NEAR
+ extrn ChildTerminationHandler:NEAR
+
+externFP NSetSegmentDscr
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn selGDTFree:WORD
+ extrn segDXData:WORD
+ extrn ExitCode:BYTE
+
+SaveROMVector dw ?
+
+cparChildMem dw ? ; size of child DOS mem block in para's
+regOurSSSP dd ? ; SS:SP save location
+
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+; For the ROM version, the following items locate (and size) the
+; DGROUP and DXPMCODE segments. These items are 'imported' from
+; the ROM Image Builder which sets the values when the ROM image
+; is created.
+
+ extrn lmaDXDGROUP:DWORD, cparDXDGROUP:ABS, lmaDXPMCODE:DWORD
+ public lmaRomDXPMCode
+
+cparDgroup dw cparDXDGROUP
+lmaDGroup dd lmaDXDGROUP
+lmaRomDXPMCode dd lmaDXPMCODE
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+
+ extrn lmaROMTOC:DWORD
+
+MyLmaROMTOC dd lmaROMTOC
+
+DXPMCODE ends
+
+
+; -------------------------------------------------------
+ subttl ROM Real Mode ROM Specific Routines
+ page
+; -------------------------------------------------------
+; ROM REAL MODE ROM SPECIFIC ROUTINES
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; ROMEntry -- Setup the special ROM environment needed
+; immediately by DOSX.
+;
+; Note: This routine runs in real mode only!
+;
+; Input: ES -> PSP for use by DOSX
+; Output: CY clear if successful, set if init failed
+; DS -> DOSX RAM DGROUP
+; DX:AX = far return address to app that invoked DOSX
+; Errors: none
+; Uses: All except ES
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public ROMEntry
+
+ROMEntry proc near
+
+; Allocate a data segment in RAM and copy down the ROM version
+
+ mov ah,48h ;allocate RAM for DGROUP
+ mov bx,cparDgroup
+ int 21h
+ jc REAllocFailed ;big trouble if this happens
+
+ push ax ;save RAM DGROUP
+
+ mov dx, word ptr lmaDGroup[0]
+ mov bx, word ptr lmaDGroup[2]
+ mov cx, 4
+ xor si, si
+@@:
+ shr bx, 1 ;determine if its in conventional
+ rcr dx, 1 ;memory and if so the canonical
+ rcr si, 1 ;segment:offset in dx:si
+ loop @B
+
+ or bx, bx
+ jnz RECopyFromExtended
+
+RECopyFromConventional:
+ push es ;save es
+ mov es,ax
+ mov ds,dx
+ mov cx,cparDgroup ; (CS variable)
+ shl cx,3 ;DGROUP size in words
+ xor di,di
+ cld
+ rep movsw
+ pop es ;restore es
+
+ pop ds ;point to RAM dgroup
+ assume ds:DGROUP, es:NOTHING
+
+ clc ;worked!
+
+REAllocFailed:
+
+ ret
+
+RECopyFromExtended:
+
+ sub sp, 30h
+ mov si, sp
+ push ss
+ pop es
+ mov di, si
+ push ax
+ xor ax,ax
+ mov cx, 18h
+ rep stosw
+ dec ax
+ mov es:[si+10h], ax ; Limits to FFFF
+ mov es:[si+18h], ax
+ mov al, 93h
+ mov es:[si+15h], al ; access to data rw
+ mov es:[si+1Dh], al
+ pop ax
+ mov cx, 4
+ sub dx, dx
+@@:
+ shl ax, 1 ;compute new DGROUP lma
+ rcl dx, 1
+ loop @B
+ mov es:[si+1Ah], ax ;stuff into dest descr
+ mov es:[si+1Ch], dl
+ mov ax, word ptr lmaDGroup[0]
+ mov dx, word ptr lmaDGroup[2]
+ mov es:[si+12h], ax
+ mov es:[si+14h], dl ;put ROM lma in source descr
+ mov cx, cparDGroup
+ shl cx, 3 ;length of dgroup ***in words ***
+ mov ah, 87h ;fn 87h = copy extended memory
+ int 15h
+ jc REExtCopyFailed
+ add sp, 30h
+ pop ds ;get ram address in ds
+ clc
+ ret
+
+REExtCopyFailed:
+ add sp, 32h ;remove stuff from stack
+ stc
+ ret
+
+ROMEntry endp
+
+
+; -------------------------------------------------------
+; InvokeROMChild -- ROM specific method of invoking the
+; child application.
+;
+; Note: This routine must be called in real mode!
+; It returns to the caller after the child returns. Control
+; passes directly to ChildTerminationHandler if child does a
+; DOS exit call.
+;
+; Input: none
+; Output: CY clear if child executed successfully, set if failed
+; AX = error code if failure (8 = insufficient DOS memory)
+; Errors: none
+; Uses: All except DS
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public InvokeROMChild
+
+InvokeROMChild proc near
+
+; Setup the environment for the ROM child app. First, allocate
+; the largest conventional memory block available and build a PSP
+; at the start of it.
+
+ mov bx,-1 ; how big is the largest avail block?
+ dossvc 48h
+
+ cmp bx,64 SHL 6 ; is there at least 64k available?
+ jae @f
+ mov ax,8 ; DOS insufficient memory error
+ jmp short IRCFail
+@@:
+ mov cparChildMem,bx ; save block size
+ dossvc 48h ; allocate block
+
+IF DEBUG
+ jnc @f ; shouldn't fail, but...
+ int 3
+@@:
+ENDIF
+
+; Got conventional memory, now build a PSP at the start of it.
+
+ mov dx,ax ; new PSP here
+ mov si,10h ; use this as mem length for now
+ dossvc 55h ; duplicate PSP call
+
+; Set up the termination vector in the child's PSP to point to
+; the Dos Extender termination code.
+
+ mov es,dx
+ assume es:PSPSEG
+
+ mov word ptr [lpfnParent],offset ChildTerminationHandler
+ mov word ptr [lpfnParent+2],cs
+
+; Switch to protected mode to complete initialization of child environment
+
+ SwitchToProtectedMode
+ assume ds:DGROUP,es:DGROUP
+
+ sti
+
+; Allocate/init a selector mapping first 64k of child memory block & PSP
+
+ call AllocateLDTSelector ; get new selector in AX
+
+ ; DX still has PSP segment,
+ call ParaToLinear ; convert to linear memory address
+
+ push bx ; save lma for later
+ push dx
+ push ax ; save mem/PSP selector too
+
+ cCall NSetSegmentDscr,<ax,bx,dx,0,0FFFFh,STD_DATA>
+
+; Allocate/init a zero length selector pointing to just past the end of
+; the child's memory block and plug the selector into the PSP.
+
+ call AllocateLDTSelector ; get another selector
+
+ pop es
+ assume es:PSPSEG
+
+ mov segMemEnd,ax ; store in child's PSP
+
+ mov dx,cparChildMem ; get size of memory block in bytes
+ call ParaToLinear ; in BX:DX
+
+ pop ax ; recover lma of block start
+ pop cx ; in CX:AX
+
+ add dx,ax
+ adc bx,cx ; BX:DX = lma of block end
+
+ cCall NSetSegmentDscr,<segMemEnd,bx,dx,0,0,STD_DATA>
+
+; Allocate/init a selector pointin to our DOS environment block--plug this
+; into the child's PSP so it can party on the same env block.
+
+ call AllocateLDTSelector
+
+ mov dx,segEnviron ; segment from PSP
+ call ParaToLinear
+
+ cCall NSetSegmentDscr,<ax,bx,dx,0,7FFFH,STD_DATA>
+
+ mov segEnviron,ax ; selector to PSP
+
+; The child environment is now build, invoke the child with DS = ES =
+; memory/PSP block. Set the stack 64k (less one word) into the block,
+; push our far return address on the stack.
+
+ mov word ptr [regOurSSSP],sp ;save DOSX stack location
+ mov word ptr [regOurSSSP+2],ss
+
+ mov ax,es ; switch to child stack
+ mov ss,ax
+ mov sp,0FFFEh
+
+ push cs ; far return address to us
+ push offset IRCReturn
+
+ call GetROMTOCPointer ; push the kernel entry CS:IP on stack
+ assume es:ROMTOC
+
+ push word ptr [KRNL_CSIP+2]
+ push word ptr [KRNL_CSIP]
+
+ mov ds,ax
+ mov es,ax
+ assume ds:NOTHING,es:NOTHING
+
+ xor cx, cx ; tell KRNL386 linear == physical
+ xor dx, dx
+
+ retf ; invoke the kernel
+
+ public IRCReturn ;public for debugging
+IRCReturn:
+
+ mov ds,selDgroupPM ; restore our DS
+ assume ds:DGROUP
+
+ mov ss,word ptr [regOurSSSP+2] ; and our stack
+ mov sp,word ptr [regOurSSSP]
+
+ mov ExitCode,al ; save the child exit code
+
+ SwitchToRealMode
+ sti
+
+ clc
+ ret
+
+IRCFail:
+ stc
+ ret
+
+InvokeROMChild endp
+
+
+; -------------------------------------------------------
+; ROMInitialization -- This routine performs the initial ROM
+; specific DOS Extender initialization.
+;
+; Note: This routine runs in real mode only!
+;
+; Input: none
+; Output: CY clear if successful, set if init failed
+; Errors: none
+; Uses: AX
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public ROMInitialization
+
+ROMInitialization proc near
+
+; Point 2nd word of DOSX ROM Int vector to the data segment.
+
+ push es
+ xor ax,ax
+ mov es,ax
+
+ mov ax, word ptr es:[ROMIntVector*4][2] ;save current vector
+ mov SaveROMVector,ax ; contents
+
+ mov word ptr es:[ROMIntVector*4][2],ds ;point vector to data
+ pop es
+
+ clc
+
+ROMInitFailed:
+
+ ret
+
+ROMInitialization endp
+
+
+; -------------------------------------------------------
+; ROMCleanUp -- This routine performs the ROM specific
+; DOS Extender termination clean up.
+;
+; Note: This routine runs in real mode only!
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public ROMCleanUp
+
+ROMCleanUp proc near
+
+; Remove DOSX Int vector pointer to the data segment.
+
+ push es
+ xor ax,ax
+ mov es,ax
+
+ mov ax,SaveROMVector ;restore prior contents
+ mov word ptr es:[ROMIntVector*4][2],ax
+
+ pop es
+
+ ret
+
+ROMCleanUp endp
+
+
+; -------------------------------------------------------
+ subttl ROM Real Mode Utility Routines
+ page
+; -------------------------------------------------------
+; ROM REAL MODE UTILITY ROUTINES
+; -------------------------------------------------------
+
+; GetDXDataRM -- This routine returns the paragraph address of
+; the DOS Extender data segment. It should only be called
+; by real mode code.
+;
+; Input: none
+; Output: AX = DOSX data segment paragraph address
+; Errors: none
+; Uses: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public GetDXDataRM
+
+GetDXDataRM proc near
+
+; Get data segment value from 2nd word of DOSX Int vector
+
+ push ds
+
+ xor ax,ax
+ mov ds,ax
+ mov ax,word ptr ds:[ROMIntVector*4][2]
+
+IF DEBUG
+ mov ds,ax
+ cmp ax,ds:[segDXData] ; Make sure ax points to the
+ jz @f ; right place
+ int 3
+@@:
+ENDIF
+ pop ds
+ ret
+
+GetDXDataRM endp
+
+
+; -------------------------------------------------------
+; SetDXDataRM -- This routine sets DS equal to the DOS Extender
+; data segment address. It should only be called by real
+; mode code.
+;
+; Input: none
+; Output: DS -> DOSX data segment
+; Errors: none
+; Uses: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public SetDXDataRM
+
+SetDXDataRM proc near
+
+; Get data segment value from 2nd word of DOSX Int vector
+
+ push ax
+
+ xor ax,ax
+ mov ds,ax
+ mov ds,word ptr ds:[ROMIntVector*4][2]
+
+IF DEBUG
+ mov ax,ds ; Make sure DS points to the
+ cmp ax,ds:[segDXData] ; right place
+ jz @f
+ int 3
+@@:
+ENDIF
+ pop ax
+ ret
+
+SetDXDataRM endp
+
+
+DXCODE ends
+
+;****************************************************************
+
+DXPMCODE SEGMENT
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+ subttl ROM Protect Mode Utility Routines
+ page
+; -------------------------------------------------------
+; ROM PROTECT MODE UTILITY ROUTINES
+; -------------------------------------------------------
+
+; ROMInitLDT -- This routine initializes the LDT descriptors
+; defined in the ROM prototype LDT.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public ROMInitLDT
+
+ROMInitLDT proc near
+
+; Access the ROM Table of Contents to find out where the proto LDT
+; is, and how many descriptors it contains.
+
+ pusha
+ push ds
+ push es
+
+ call GetROMTOCPointer ; Note: uses SEL_SCR0
+ assume es:ROMTOC
+
+ mov cx,cROMsels ; # descriptors defined in proto LDT
+ mov bx,word ptr [lmaROMLDT+2]
+ mov dx,word ptr [lmaROMLDT] ; bx:dx = lma of proto LDT
+ mov ax,FirstROMSel
+ mov di, ax
+
+; Do a quick (and dirty?) allocation of the correct number of LDT selectors.
+; DANGER: this code has intimate knowledge of how the LDT free list is kept!
+; We could make CX calls to AllocateLDTSelector, but that is slow and would
+; do zillions and zillions of segment loads.
+;
+ cmp ax,selGDTFree ; next free descriptor in LDT
+ jne alloc_in_chain
+
+IF DEBUG
+ cmp ax,__selFirstLDT ; first one free better be the one
+ jz @f ; the ROM Image Builder used.
+ int 3
+@@:
+ENDIF
+ shl cx,3 ; 'allocate' all these by setting
+ add ax,cx ; the next free to be beyond them
+ mov selGDTFree,ax
+ push SEL_LDT_ALIAS OR STD_RING
+ pop es
+ jmp short sels_alloced
+
+alloc_in_chain:
+ mov si, ax
+ sub si, 8
+ push SEL_LDT_ALIAS OR STD_RING
+ pop es
+ shl cx, 3
+ add ax, cx
+ mov es:[si], ax
+
+sels_alloced:
+
+; Copy the prototype descriptors into the actual LDT
+
+ cCall NSetSegmentDscr,<SEL_SCR0,bx,dx,0,-1,STD_DATA>
+
+ push SEL_SCR0 OR STD_TBL_RING
+ pop ds ; ds -> proto LDT descriptors
+ assume ds:NOTHING
+
+ xor si,si
+
+ shr cx,1 ; # words of descriptors to copy
+
+ cld
+ rep movsw ; move'm into the LDT
+
+ pop es
+ pop ds
+ assume ds:DGROUP,es:NOTHING
+ popa
+
+ ret
+
+ROMInitLDT endp
+
+
+; -------------------------------------------------------
+; GetROMTOCPointer -- return ES:0 pointing to the ROM Table of
+; Contents.
+;
+; Note: modifies the SEL_SCR0 descriptor!
+;
+; Input: none
+; Output: ES:0 -> ROM TOC
+; Errors: none
+; Uses: none
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetROMTOCPointer
+
+GetROMTOCPointer proc near
+
+ push bx
+ push dx
+
+ mov bx,word ptr [MyLmaROMTOC+2]
+ mov dx,word ptr [MyLmaROMTOC] ;bx:dx = lma of ROMTOC
+
+ cCall NSetSegmentDscr,<SEL_SCR0,bx,dx,0,-1,STD_DATA>
+
+ push SEL_SCR0 OR STD_TBL_RING
+ pop es
+
+ pop dx
+ pop bx
+
+ ret
+
+GetROMTOCPointer endp
+
+
+; -------------------------------------------------------
+; GetDXDataPM -- This routine returns the paragraph address of
+; the DOS Extender data segment. It should only be called
+; by protected mode code.
+;
+; Input: none
+; Output: AX = DOSX data segment paragraph address
+; Errors: none
+; Uses: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public GetDXDataPM
+
+GetDXDataPM proc near
+
+; Get data segment value from 1st word of DOSX Int vector
+
+ push ds
+
+ mov ax,SEL_RMIVT or STD_RING
+ mov ds,ax
+ mov ax,word ptr ds:[ROMIntVector*4][2]
+
+ pop ds
+ ret
+
+GetDXDataPM endp
+
+if 0 ;***************************************************************
+; -------------------------------------------------------
+; SetDXDataPM -- This routine sets DS equal to the DOS Extender
+; data segment address. It should only be called by protected
+; mode code.
+;
+; Input: none
+; Output: DS -> DOSX data segment
+; Errors: none
+; Uses: none
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public SetDXDataPM
+
+SetDXDataPM proc near
+
+; Set DS = data segment selector.
+
+ push SEL_DXDATA OR STD_RING
+ pop ds
+ ret
+
+SetDXDataPM endp
+endif ;***************************************************************
+
+
+DXPMCODE ends
+
+;****************************************************************
+
+ifdef ROMSTUB ;--------------------------------------------------------
+
+ *********************************************
+ *********************************************
+ ***** *****
+ ***** THIS CODE IS NO LONGER USED *****
+ ***** *****
+ *********************************************
+ *********************************************
+
+DXSTUB SEGMENT
+ assume cs:DXSTUB
+
+; -------------------------------------------------------
+ subttl Real Mode RAM Stub Segment
+ page
+; -------------------------------------------------------
+; REAL MODE RAM STUB SEGMENT
+; -------------------------------------------------------
+
+; This segment contains code and data that is moved to conventional
+; RAM during initialization. The code is only executed from RAM
+; mode and exists for those few cases where it is most convenient
+; to have code segement variables.
+;
+; Note: Since this code is moved to RAM, be very carefull of the
+; instructions it contains. In particular, realize the the code segment
+; value is going to be different from the copy in ROM.
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+ public PrevInt2FHandler
+
+PrevInt2FHandler dd ?
+
+; -------------------------------------------------------
+; StubInt2FHook -- This code is used by the DOS Extender real mode
+; Int 2Fh hook to chain the interrupt to the previous Int 2Fh
+; handler.
+;
+; Note: This code executes in real mode only!
+;
+; Input: stack = [DS] [IP] [CS] [FL]
+; Output: none
+; Errors: none
+; Uses: none
+
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public StubInt2FHook
+
+StubInt2FHook proc far
+
+ pop ds
+ jmp [PrevInt2FHandler]
+
+StubInt2FHook endp
+
+; -------------------------------------------------------
+
+DXSTUB ends
+
+endif ;----------------------------------------------------------------
+
+;****************************************************************
+
+ENDIF ;ROM
+
+ end
diff --git a/private/mvdm/dpmi/dxrom.inc b/private/mvdm/dpmi/dxrom.inc
new file mode 100644
index 000000000..c7619e026
--- /dev/null
+++ b/private/mvdm/dpmi/dxrom.inc
@@ -0,0 +1,92 @@
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXROM.INC -- ROM Defintions for Dos Extender *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 11/05/90 jimmat Created. *
+;* *
+;****************************************************************
+
+IFDEF ROM
+
+; Define the ROM Table of Contents structure. Note that this structure
+; is defined in other pieces of software and all must be updated if any
+; are. Currently known to be defined in the ROM Image Builder (RIB) and
+; the ROM Test loader.
+
+ROMTOC SEGMENT AT 0
+szID db 10 dup (?) ; ID string
+DOSX_CSIP dd ? ; DOSX initial CS:IP (seg:off)
+KRNL_CSIP dd ? ; Kernel initial CS:IP (sel:off)
+lmaROMLDT dd ? ; lma of ROM prototype LDT
+cROMsels dw ? ; # descriptors used in ROM proto LDT
+FirstROMsel dw ? ; first LDT selector in ROM proto LDT
+offSysDir dw ? ; offset to system directory string
+cModules dw ? ; # modules defined in ROM TOC
+ModEntries db ? ; start of module entries
+
+; module entries follow at this point...
+
+ROMTOC ENDS
+
+
+RomIntVector = 31h ;Interrupt vector to use
+
+
+; This macro returns the DOS Extender data segment paragraph address in AX.
+; It must only be used by real mode code.
+
+GetRMDataSeg macro
+
+ ifndef GetDXDataRM
+ extrn GetDXDataRM:NEAR
+ endif
+
+ call GetDXDataRM
+ endm
+
+
+; This macro sets DS to point to the DOS Extender data segment. It must
+; only be used by real mode code.
+
+SetRMDataSeg macro
+
+ ifndef SetDXDataRM
+ extrn SetDXDataRM:NEAR
+ endif
+
+ call SetDXDataRM
+ endm
+
+
+; This macro returns the DOS Extender data segment paragraph address in AX.
+; It must only be used by protected mode code.
+
+GetPMDataSeg macro
+
+ ifndef GetDXDataPM
+ extrn GetDXDataPM:NEAR
+ endif
+
+ call GetDXDataPM
+ endm
+
+
+; This macro sets DS to point to the DOS Extender data segment. It must
+; only be used by protected mode code.
+
+SetPMDataSeg macro
+
+ ifndef SetDXDataPM
+ extrn SetDXDataPM:NEAR
+ endif
+
+ call SetDXDataPM
+ endm
+
+ENDIF
diff --git a/private/mvdm/dpmi/dxstrt.asm b/private/mvdm/dpmi/dxstrt.asm
new file mode 100644
index 000000000..e79e6c504
--- /dev/null
+++ b/private/mvdm/dpmi/dxstrt.asm
@@ -0,0 +1,1833 @@
+ PAGE ,132
+ TITLE DXSTRT.ASM -- Dos Extender Startup Code
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXSTRT.ASM - Dos Extender Startup Code *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains the executive initialization code for *
+;* the Dos Extender. The module DXBOOT.ASM contains the code *
+;* specific to starting up the Dos Extender. The module *
+;* DXINIT.ASM contains the code specific to starting up the *
+;* child program of the Dos Extender. The code in these *
+;* two modules is discarded at the end of the initialization. *
+;* The code in this module calls the initialization routines *
+;* in the other to init modules, and then performs the final *
+;* juggling of things to throw away the low memory init code, *
+;* and transfer control to start up the child program. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 01/09/91 amitc At exit time Co-Processor being reset *
+;* 08/08/90 earleh DOSX and client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 12/08/89 jimmat Added call to reenable EMM driver. *
+;* 08/29/89 jimmat Restores Int 2Fh vector at exit *
+;* 08/20/89 jimmat Removed A20 code, since HIMEM version 2.07 *
+;* A20 code now works properly. *
+;* 06/28/89 jimmat Now calls OEM layer instead of NetMapper *
+;* 06/16/89 jimmat Implemented Windows/386 startup/exit Int *
+;* 2Fh calls and ifdef'd combined EXE code *
+;* 05/17/89 jimmat ChildTerminationHandler sets its SS:SP *
+;* 04/18/89 jimmat Added calls to init/term NetBIOS mapper *
+;* 03/28/89 jimmat Incorporated bug fix from GeneA related *
+;* to nested GP faults in ChildTermHandler *
+;* 03/11/89 jimmat Added support for TSS & LDT *
+;* 03/07/89 jimmat converted to use WDEB386 *
+;* 02/27/89 (GeneA): shrink initial memory block size on entry *
+;* 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 termination code for startup *
+;* errors that occur while in protected mode *
+;* 02/14/89 (GeneA): added code to reduce size of real mode *
+;* code segment to throw away initialization code. *
+;* 01/31/89 (GeneA): created by copying code from the old *
+;* DXINIT.ASM *
+;* 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
+;****************************************************************
+
+.286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+.sall
+.xlist
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+if VCPI
+include dxvcpi.inc
+endif
+include smartdrv.inc
+IFDEF ROM
+include dxrom.inc
+ENDIF
+
+include hostdata.inc
+include dpmi.inc
+include intmac.inc
+.list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+WIN386_INIT equ 1605h ;Win/386 startup Int 2Fh
+WIN386_EXIT equ 1606h ;Win/386 shutdown Int 2Fh
+
+WIN386_DOSX equ 0001h ;Win/386 init/exit really DOSX flag
+
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn InitDosExtender:NEAR
+; extrn ReleaseXmemHeap:NEAR
+ extrn RestoreRMIntrVectors:NEAR
+ extrn InitXmemHeap:NEAR
+ extrn AllocateLDTSelector:NEAR
+ extrn SaveRMIntrVectors:NEAR
+ extrn EnterRealMode:NEAR
+ extrn EnterProtectedMode:NEAR
+ extrn InitializeOEM:NEAR
+ extrn TerminateOEM:NEAR
+ extrn AllocateSelector:NEAR
+ extrn FreeSelector:NEAR
+externNP NSetSegmentAccess
+ extrn DupSegmentDscr:NEAR
+IFDEF WOW
+ extrn RmUnsimulateProc:FAR
+ extrn Wow16TransitionToUserMode:near
+ extrn Wow16CopyEhStack:near
+ extrn Wow16CopyIretStack:near
+ENDIF
+if NOT VCPI
+ extrn EMMEnable:NEAR
+endif
+
+if VCPI
+externNP FreeEMSHandle
+endif
+ extrn AllocateXmem32:NEAR
+
+IFNDEF ROM
+ifdef WOW
+ externFP NSetSegmentDscr
+else
+ externFP NSetGDTSegmentDscr
+ENDIF; wow
+ENDIF; rom
+
+IFDEF ROM
+ extrn ROMEntry:NEAR
+ extrn ROMCleanUp:NEAR
+ extrn ROMInitLDT:NEAR
+ extrn InvokeROMChild:NEAR
+ENDIF
+
+ extrn FreeMemByOwner:NEAR
+ extrn PMIntr19:NEAR
+ extrn PMIntr13:NEAR
+ extrn PMIntr31: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 PMReservedEntryVector:NEAR
+ extrn TrapInvalidOpcode:NEAR
+ extrn TrapDoubleFault:NEAR
+ extrn TrapExtensionOverrun:NEAR
+ extrn TrapInvalidTss:NEAR
+ extrn TrapSegmentNotPresent:NEAR
+ extrn TrapStackOverrun:NEAR
+ extrn TrapGP:NEAR
+ extrn TrapPageFault:NEAR
+ extrn FarParaToLDTSelector:FAR
+ extrn WowHwIntrEntryVector:NEAR
+ extrn Wow16HwIntrReflector:NEAR
+IFNDEF WOW
+ extrn FreeXmemHeap:NEAR
+ENDIF
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXSTACK segment
+
+extrn rgw0Stack:WORD
+extrn rgw2FStack:WORD
+
+DXSTACK ends
+
+; -------------------------------------------------------
+
+DXDATA segment
+ifdef NOT_NTVDM_NOT
+ extrn fHPVectra:BYTE
+endif
+ extrn A20EnableCount:WORD
+ extrn lpfnUserMouseHandler:DWORD
+ extrn lpfnUserPointingHandler:DWORD
+ extrn f286_287:BYTE
+
+if VCPI
+ extrn fVCPI:BYTE
+ extrn segBootPmode:WORD
+endif
+ extrn cDPMIClients:WORD
+ extrn selCurrentHostData:WORD
+ extrn segCurrentHostData:WORD
+
+ extrn fDebug:BYTE
+ extrn lmaIdt:DWORD
+
+ifdef WOW
+ extrn rgwWowStack:word
+ extrn FastBop:fword
+ extrn WowTransitionToUserMode:WORD
+ extrn WowCopyEhStack:WORD
+ extrn WowCopyIretStack:WORD
+ extrn PMReservedReflector:near
+endif
+ extrn PMFaultVector:DWORD
+ extrn PMIntelVector:DWORD
+ extrn RmHwISR:DWORD
+ extrn DpmiFlags:WORD
+ extrn HwIntHandlers:WORD
+
+ org 0
+
+ public rgwStack,npEHStackLimit,npEHStacklet, selEHStack,
+;
+; Pmode fault handler stack used during initialization only.
+;
+ dw 80h dup (?)
+rgwStack label word
+
+;
+; This is the stack area used while running in the interrupt reflector.
+; This is divided up into a number of stack frames to allow reentrant
+; execution of the interrupt reflector. The stack pointer pbReflStack
+; indicates the current stack frame to use. Each entry into the interrupt
+; reflector decrements this variable by the size of the stack frame, and
+; each exit from the interrupt reflector increments it.
+
+C_STKFRAME = 36
+CB_REFLSTACK = C_STKFRAME * CB_STKFRAME
+
+if DEBUG ;--------------------------------------------------------
+ public StackGuard
+StackGuard dw 1022h ;unlikely value to check for stk overrun
+endif ;--------------------------------------------------------
+
+ public pbReflStack,bReflStack
+if DBG
+bReflStack db CB_REFLSTACK dup (0AAh)
+else
+bReflStack db CB_REFLSTACK dup (?)
+endif
+
+;----------------------------------------------------------------------------
+; The following stack is used for hw interrupts on platforms (e.g. x86) where
+; the kernel or ntvdm switches the stack before an interrupt is reflected.
+;----------------------------------------------------------------------------
+IFDEF WOW
+CB_HWINTRSTACK = 1000h
+if DBG
+bHwIntrStack db CB_HWINTRSTACK dup (0AAh)
+else
+bHwIntrStack db CB_HWINTRSTACK dup (?)
+endif
+ public pbHwIntrStack
+pbHwIntrStack dw bHwIntrStack + CB_HWINTRSTACK
+ENDIF ; WOW
+
+pbReflStack dw bReflStack + CB_REFLSTACK
+
+npEHStackLimit dw offset DGROUP:rgwStack
+npEHStacklet dw offset DGROUP:rgwStack
+selEHStack dw SEL_STACK_ALIAS OR STD_RING
+
+ public cdscGDTMax, selGDTFree, segGDT, selGDT
+
+cdscGDTMax dw CDSCGDTDEFAULT
+
+selGDTFree dw ? ;head of list of free descriptors in GDT
+
+segGDT dw 0 ;real mode paragraph address of the GDT
+ ; This variable always stores the real mode
+ ; paragraph address
+
+selGDT dw 0 ;current mode segment/selector for the GDT
+ ; segment. This variable holds the real
+ ; mode paragraph address during initialization
+ ; and then holds the protected mode selector
+ ; when running in protected mode.
+
+ public bpGDT, bpGDTcb, bpGDTbase
+
+bpGDT label fword
+bpGDTcb dw ?
+bpGDTbase dd ?
+
+
+ public cdscIDTMax, segIDT, selIDT
+
+cdscIDTMax dw CDSCIDTDEFAULT
+segIDT dw 0
+selIDT dw 0
+
+ public bpIDT, bpIDTcb, bpIDTbase
+
+bpIDT label fword
+bpIDTcb dw ?
+bpIDTbase dd ?
+
+ public bpRmIVT
+
+bpRmIVT dq 0FFFFh ;This is the segment descriptor for the real
+ ; mode interrupt vector table.
+
+ public selDOSScr
+
+selDOSScr dw 0 ;Scratch selector used for DOS mapping
+
+ public lpfnXMSFunc
+
+lpfnXMSFunc dd 0 ;far pointer to XMS memory driver entry point
+
+ public idCpuType
+
+idCpuType dw 0
+
+ public segPSP, selPSP
+
+segPSP dw ? ;segment address of Dos Extender PSP
+selPSP dw ? ;selector for Dos Extender PSP
+ ; code during processing of GP faults
+
+ public selPSPChild, segPSPChild
+
+segPSPChild dw ? ;real mode segment address of child's PSP
+; note the following in 1, so that in low mem heap management, we can use
+; selPSPChild to mark the owner of the memory.
+selPSPChild dw 1 ;selector of child program's PSP
+
+
+ public DtaSegment, DtaOffset, DtaSelector
+DtaSegment dw 0
+DtaSelector dw 0
+DtaOffset dw 0
+
+
+ public regChildSP, regChildSS, regChildIP, regChildCS
+
+regChildSP dw ? ;initial user stack pointer from exe file
+regChildSS dw ? ;initial user stack segment from exe file
+regChildIP dw ? ;initial user program counter from exe file
+regChildCS dw ? ;initial user code segment from exe file
+
+ public hmemDOSX
+
+hmemDOSX dw 0
+
+IFDEF ROM
+ public segDXCode, segDXData
+
+segDXCode dw ? ;holds real mode paragraph address
+ ; of the code segment
+segDXData dw ? ;holds real mode paragraph address
+ ; of the data segment
+ENDIF
+
+ifdef NOT_NTVDM_NOT
+ public fMicroChannel
+
+fMicroChannel db 0 ;NZ if running on a Micro Channel system
+endif
+
+ public fFaultAbort, ExitCode
+
+fFaultAbort db 0 ;NZ if terminating due to unrecoverable fault
+ExitCode db 0FFh ;exit code to use when terminating
+fQuitting db 0
+
+fEMbit db 0FFh ;MSW EM bit at startup (FF = not checked yet)
+
+ public fUsingHMA
+fUsingHMA db 0
+
+if NTDEBUG
+ public fDebugTrace
+fDebugTrace db 0
+endif
+
+; The one and only Task State Segment is here (it's too small to allocate
+; a block for it in extended memory)
+
+ public sysTSS
+
+sysTSS TSS286 <>
+
+ public HighestDxSel
+HighestDxSel dw 0
+
+; After initialization is complete, the following buffers (rgbXfrBuf0 and
+; rgbXfrBuf1) are used to transfer data between real mode address space
+; and protected mode address space during the processing of various interrupt
+; function calls. During the initialization process, these buffers are also
+; used as temporary work space as described below:
+; CheckCPUType uses the first 6 bytes of rgbXfrBuf0 as scratch space.
+; The functions for moving the Dos Extender protected mode code segment
+; and the GDT and IDT use rgbXfrBuf0 as buffer space for building a
+; parameter block. The child loading code in DXINIT.ASM uses the buffer
+; RELOC_BUFFER for holding the base part of the file name (name.exe portion)
+; of the child exe file while determining the complete path to the child
+; in the case where the child name is specified on the command line. Note,
+; this buffer is also used for holding sectors from the relocation table
+; while loading the program, but that occurs after the child exe file name
+; has been determined.
+; The child loading code in DXINIT.ASM also uses rgbXfrBuf1 to hold several
+; buffers as well. The locations and purposes of these buffers are
+; described in GENDEFS.INC. All of the buffers are defined with equates
+; named EXEC_?????
+;
+; The file search logic in DXFIND.ASM assumes some parameters are setup in
+; the rgbXfrBuf1 file name buffers. Some of the code was moved from
+; DXINIT.ASM to DXFIND.ASM.
+
+ public rgbXfrBuf0, rgbXfrBuf1
+ public npXfrBuf0, npXfrBuf1
+
+ align 2
+
+rgbXfrBuf0 db CB_XFRBUF0 dup (?)
+rgbXfrBuf1 db CB_XFRBUF1 dup (?)
+
+npXfrBuf0 dw offset DGROUP:rgbXfrBuf0
+npXfrBuf1 dw offset DGROUP:rgbXfrBuf1
+
+ifdef FLATAPIXLAT
+DtaBuffer dw 128 dup (0) ; used as the dta for PM if dta changed
+endif
+IFNDEF ROM ;--------------------------------------------------------
+
+; Parameter block for passing to DOS EXEC call to run the child
+; program.
+;
+public exec_par_blk
+
+exec_par_blk dw 0
+cmd_off dw OFFSET EXEC_CMNDLINE
+cmd_seg dw DXDATA
+ dw OFFSET EXEC_FCB0
+fcb1_seg dw DXDATA
+ dw OFFSET EXEC_FCB1
+fcb2_seg dw DXDATA
+
+ENDIF ;ROM ---------------------------------------------------------
+
+
+; The following variables are used during reading the relocation table
+; from the exe file and relocating the child program.
+
+ public fhExeFile
+ public clpRelocItem
+ public plpRelocItem
+
+fhExeFile dw ? ;DOS file handle for the exe file
+clpRelocItem dw ? ;number of relocation items in the exe file
+plpRelocItem dw ? ;pointer to next relocation item in the table
+
+ public SMRTDRVDelta, SMRTDRVName
+
+SMRTDRVDelta SD_IOCTL_WR_GrwShrk <,0> ; Struc to IOCTL write SMARTDRV
+SMRTDRVName db SD_DEV_NAME ; Name for smart drive opens
+
+szDebugHello label byte
+if DEBUG
+ db 'DOSX: Beginning protected mode initialization.',13,10,0
+endif
+ db 0
+
+IFDEF ROM
+ public PrevInt2FHandler
+
+PrevInt2FHandler dd ? ;previous real mode Int 2F handler
+ENDIF
+
+FreeMem dw 0
+
+DXDATA ends
+
+; -------------------------------------------------------
+ page
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+ extrn CodeEnd:NEAR
+ extrn ER_DXINIT:BYTE
+ extrn ER_REALMEM:BYTE
+ extrn RMDefaultInt24Handler:FAR
+
+if VCPI
+
+externNP QEMM386Trap
+
+endif
+
+IFNDEF ROM
+ public segDXCode, segDXData, selDgroup
+
+segDXCode dw ? ;holds the real mode paragraph address
+ ; of the code segment
+segDXData dw ? ;holds the real mode paragraph address
+ ; of the data segment
+
+selDgroup dw ? ;holds the paragraph address/selector for
+ ; DGROUP depending on the current mode
+
+ public PrevInt2FHandler, PrevInt69Handler
+
+PrevInt2FHandler dd ? ;previous real mode Int 2F handler
+PrevInt69Handler dd ? ;previous HP Vectra A & A+ keyboard interrupt
+
+ENDIF
+
+IFDEF ROM
+externFP NSetSegmentDscr
+ENDIF
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+ extrn CodeEndPM:NEAR
+ extrn WowHwIntDispatchProc:WORD
+
+ org 0
+
+IFDEF ROM ;--------------------------------------------------------
+
+ extrn segRomDXCODE:ABS
+
+ public selDgroupPM, segDXCodePM
+
+selDgroupPM dw SEL_DXDATA OR STD_RING ;This variable contains the
+ ; data segment selector
+segDXCodePM dw segRomDXCODE ;This variable contains the paragraph
+ ; address of the real mode code segment
+
+ELSE ;ROM ---------------------------------------------------------
+
+ public selDgroupPM, segDXCodePM, segDXDataPM
+
+selDgroupPM dw ? ;This variable always contains the
+ ; data segment selector
+segDXCodePM dw ? ;This variable contains the paragraph
+ ; address of the real mode code segment
+segDXDataPM dw ? ;This variable contains the paragraph
+ ; address of the data segment
+ENDIF ;ROM ---------------------------------------------------------
+
+
+IFNDEF ROM
+externFP NSetSegmentDscr
+ENDIF
+
+IFDEF WOW
+ extrn WowReservedReflector:WORD
+ENDIF
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+BeginLowSegment
+
+; -------------------------------------------------------
+; DOS EXTENDER ENTRY FUNCTION
+; -------------------------------------------------------
+;
+; This is the program entry point for the Dos Extender. This
+; function decides if the Dos Extender is being run as a single
+; time operation to extend a single program, or if it is being
+; run as a TSR to wait for programs to request its services
+; later on.
+;
+
+ assume ds:PSPSEG,es:PSPSEG,ss:NOTHING
+ public start
+
+start:
+
+IFDEF ROM ;--------------------------------------------------------
+
+ call ROMEntry ;setup special ROM environment
+ assume ds:DGROUP
+
+ mov ax,ds ;code below expects DGROUP in ax
+
+ELSE ;ROM ---------------------------------------------------------
+
+; Set up the initial segment registers.
+
+ mov ax,DGROUP
+ mov ds,ax
+ assume ds:DGROUP
+
+ENDIF ;ROM ---------------------------------------------------------
+
+ mov segPSP,es ;save the PSP segment address
+
+ mov ss,ax
+ mov sp,offset DGROUP:rgwStack
+ assume ss:DGROUP
+
+;
+; Set up the INT 24h handler. The default INT 24h handler fails the
+; system call in progress, for DPMI compatibility.
+;
+
+ push ds
+ mov ax,cs
+ mov ds,ax
+ mov dx,offset DXCODE:RMDefaultInt24Handler
+ mov ax,2524h
+ int 21h
+ pop ds
+
+; Issue the Win/386 startup Int 2Fh (almost) first thing
+
+ push ds
+
+ mov ax,WIN386_INIT ;gives other PM software a chance
+ xor bx,bx ; to disable itself, release XMS, etc.
+ mov cx,bx
+ mov si,bx
+ mov ds,bx
+ mov es,bx
+ assume ds:NOTHING, es:NOTHING
+ mov dx,WIN386_DOSX
+ mov di,DXVERSION
+ int 2Fh
+
+ pop ax ;restore ds/es DGROUP addressability
+ mov ds,ax
+ mov es,ax
+ assume ds:DGROUP, es:DGROUP
+
+
+ jcxz Allow_Startup ;if cx still == 0, keep going (we ignore
+ ; all the other possible return values)
+if VCPI
+
+ cCall QEMM386Trap
+ je Allow_Startup
+
+endif ; VCPI
+
+ mov ExitCode,2 ; otherwise we should abort now
+ jmp RealModeCleanUp
+
+Allow_Startup:
+
+; Initialize the Dos Extender
+
+ call InitDosExtender ;NOTE: passes data to InitChildProgram
+ jnc @F ; in rgbXfrBuf1 -- don't overwrite it!
+ jmp strt88
+@@:
+
+; Save the state of the MSW EM bit (Emulate Math coprocessor), and turn
+; it off (win87em wants it off).
+
+ifndef WOW
+ smsw ax
+
+ out1 <What to do about this?>
+
+ test al,01h ;in V86 mode?
+ jnz @f ; can't do the lmsw if so
+
+ push ax
+ and al,04h
+ mov fEMbit,al
+ pop ax
+ and al,not 04h
+ lmsw ax
+@@:
+endif ; wow
+; Switch the machine into protected mode.
+
+ FCLI
+ call SaveRMIntrVectors
+
+ SwitchToProtectedMode
+
+ public DXPMInit
+DXPMInit LABEL BYTE
+
+if DEBUG
+ Trace_Out "*******************************************************************************"
+ Trace_Out "*******************************************************************************"
+ Trace_Out "**** ****"
+ Trace_Out "**** THIS IS A DEBUG RELEASE THIS IS A DEBUG RELEASE ****"
+ Trace_Out "**** ****"
+ Trace_Out "*******************************************************************************"
+ Trace_Out "*******************************************************************************"
+endif
+;
+; The following code MUST appear in both the retail and debug versions.
+; It uses a side effect of the WDEB386 conditional breakpoint command
+; to cause WDEB386 to initialize its copy of the protected mode IDT
+; register. [ERH] 06-Dec-1990
+;
+ cmp fDebug,0
+ je @F
+ lea si,szDebugHello
+ mov ax,0F001h ; WDEB386 conditional breakpoint
+ int 41h
+@@:
+
+ assume ds:DGROUP,es:NOTHING
+
+ mov ax,SEL_IDT or STD_RING
+ mov selIDT,ax
+ mov ax,SEL_LDT_ALIAS OR STD_RING
+ mov selGDT,ax
+
+; Initialize the LDT now that we are in protected mode. First, set the
+; contents to zero.
+
+ mov es,selGDT ; actually LDT
+ assume es:NOTHING
+ mov cx,cdscGDTMax
+IFDEF WOW
+ mov di,GDT_SIZE
+ sub cx,di
+ENDIF
+ shl cx,2 ; CX = words in LDT segment
+ xor ax,ax ; AX = 0
+IFNDEF WOW
+ mov di,ax
+ENDIF
+ cld
+ rep stosw ; CX = 0
+
+ dec cx ; CX = 0FFFFh
+
+ push ax
+ mov ax,es
+ DPMIBOP PassTableAddress
+ pop ax
+
+;
+; Set the two scratch selectors to 64k data starting at zero. Actual
+; addresses set as used.
+;
+ cCall NSetSegmentDscr,<SEL_SCR0,ax,ax,ax,cx,STD_DATA>
+ cCall NSetSegmentDscr,<SEL_SCR1,ax,ax,ax,cx,STD_DATA>
+
+; Place all descriptors beginning at SEL_USER on a linked list of
+; free descriptors. A descriptor is identified as being free by
+; being on this list, and also by having the access rights byte set
+; to 0. The cbLimit field of the descriptor is used as the link to
+; the next descriptor on the list. Note: the GDT was initialized
+; to 0 above, so the access rights byte is already 0 on these.
+
+ mov di,SEL_USER and SELECTOR_INDEX
+ mov selGDTFree,di ;init head of free list pointer
+ mov dx,cdscGDTMax
+ shl dx,3
+ jnc @F
+ dec dx
+@@:
+ mov ax,di ;pointer to current descriptor
+igdt50: add ax,8 ;point to following one
+ mov es:[di].cbLimit,ax ;store as link in this descriptor
+ jc igdt58 ;in case of segment wrap around
+ cmp ax,dx ;are we at the end of the table
+ jnc igdt58 ;if so, then get out of loop
+ mov di,ax ;point at the next one
+ jmp igdt50 ;and repeat
+
+; We just set the link field of the last one in the list to point to
+; a bogus location. So we need to go back and set it to 0 to indicate
+; end of list.
+
+igdt58: mov es:[di].cbLimit,0
+
+
+IFDEF ROM
+
+; Initialize the predefined LDT descriptors/selectors for the ROM version.
+; This has to be done now since the ROM Image Builder starts allocating
+; at SEL_USER.
+
+ call ROMInitLDT
+
+ENDIF
+
+
+; Allocate a scratch selector for DOS mapping
+
+
+ call AllocateLDTSelector
+ mov selDOSScr,ax
+
+ FSTI ;don't need ints disabled
+
+; Initialize the extended memory heap
+
+; call InitXmemHeap
+
+IFDEF WOW
+; Bop to initialize 32 bit support. Eventually this needs to be done
+; on mips too.
+
+ push es
+ mov ax,SEL_DXCODE OR STD_RING
+ mov es,ax
+ assume es:DXCODE
+ push SEL_DXPMCODE OR STD_RING
+ push offset DXPMCODE:FarParaToLDTSelector
+ push SEL_DXCODE OR STD_RING
+ push SEL_DXDATA OR STD_RING
+ push ds
+ push offset DGROUP:DtaBuffer
+ push segDXCode
+ push segDXCode
+ push offset DXCODE:RmUnsimulateProc
+ push word ptr CB_STKFRAME
+ push ds
+ push offset DGROUP:pbReflStack
+ push ds
+ push offset DGROUP:rgbXfrBuf1
+ push ds
+ push offset DGROUP:rgbXfrBuf0
+ push segDXData
+ mov si,sp
+ FBOP BOP_DPMI,InitDosx,FastBop
+ add sp,34
+ pop es
+ assume es:nothing
+ENDIF
+IFNDEF ROM ;--------------------------------------------------------
+
+; Shrink the size of our real mode code segment to throw away init code.
+
+ SwitchToRealMode
+
+if VCPI
+ mov bx,segBootPmode
+ add bx,CBPAGE386 shr 4
+ cmp fVCPI,0
+ jne @F
+endif
+ mov bx,(offset CodeEnd) + 15
+ shr bx,4
+ add bx,segDXCode
+if VCPI
+@@:
+endif
+ sub bx,segPSP
+ push es
+ mov es,segPSP
+ dossvc 4Ah
+ pop es
+
+ call DetermineFreeMemory
+ mov FreeMem,bx
+ SwitchToProtectedMode
+
+ENDIF ;ROM ---------------------------------------------------------
+
+; Initialize the OEM layer. This can allocate DOS memory, so it goes
+; after the final program shrink.
+
+ call InitializeOEM ;currently initializes NetBIOS mapper
+
+if1
+%OUT InitializeOEM can fail!!!!!
+endif
+
+
+; Exec the child application
+
+ SwitchToRealMode
+ FSTI
+;; bugbug hack ...... williamh
+;; turn off A20 line before we terminate and keep resident.
+ cmp A20EnableCount, 0
+ jz A20IsOff
+@@:
+ xmssvc 6
+ dec A20EnableCount
+ jnz @B
+A20IsOff:
+
+ mov ax,segPSP
+ mov es,ax
+ assume es:nothing
+ mov ax,es:[2ch]
+ mov es,ax
+ dossvc 49h ; free env block
+
+ call DetermineFreeMemory
+ mov dx,(offset CodeEnd) + 15
+ shr dx,4
+ add dx,FreeMem
+ sub dx,bx
+ add dx,segDXCode
+ sub dx,segPSP
+ mov al,0
+ dossvc 31h ; tsr.
+help: int 3 ; should never get here
+ jmp help
+
+IFDEF ROM ;--------------------------------------------------------
+
+ call InvokeROMChild ;setup ROM child environment
+ ; and invoke the child app
+
+ ;DOS will transfer directly to ChildTerminationHandler if child
+ ;does a DOS Exit call, comes back here if child app returns (usually
+ ;an init error). The registers may be in a strange state here so be
+ ;careful....
+
+ELSE ;ROM ---------------------------------------------------------
+
+ lea bx, exec_par_blk
+ lea dx, EXEC_PROGNAME
+ xor al, al
+ dossvc 4bh
+
+ENDIF ;ROM ---------------------------------------------------------
+
+; If we get here, the EXEC failed for some reason!
+
+ mov bx,offset DXCODE:ER_REALMEM ;assume insufficient memory
+ xchg ax,bx
+ cmp bx,8 ;is it really?
+ jz strt88
+ mov ax,offset DXCODE:ER_DXINIT ;no, use generic msg
+
+strt88:
+ mov ExitCode,1 ;return NZ exit code
+
+ mov dx,cs ;pass msg ptr in DX:AX (ax already set)
+
+ push cs ;fake a far call so no fixup
+ push offset DXCODE:RealModeCleanUp ; needed -- return to clean up
+ jmp near ptr DisplayErrorMsg
+
+; -------------------------------------------------------
+; ChildTerminationHandler -- This routine receives control
+; when the child program running under the Dos Extender
+; terminates. It will free all resources being used by
+; the child. If we were not running TSR, then the Dos
+; Extender will complete cleaning up and terminate itself.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses:
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+
+ public DosxTerminationHandler
+DosxTerminationHandler:
+
+IFDEF ROM
+ GetRMDataSeg
+ mov ds,ax
+ mov es,ax
+ mov ss,ax
+ELSE
+ mov ds,selDgroup
+ mov es,selDgroup
+ mov ss,selDgroup ;make sure we know where the
+ENDIF
+ mov sp,offset DGROUP:rgwStack ; stack is when we get here
+
+ assume ds:DGROUP,es:DGROUP
+
+; Check if we are already in the middle of a termination sequence and
+; bail out if so. This will prevent us from hanging up in an infinite
+; loop if we get a GP fault while we are quitting.
+
+ cmp fQuitting,0
+ jz @f
+ jmp chtm90
+@@:
+ mov fQuitting,1
+
+; Terminate the OEM layer
+
+ call TerminateOEM ;current term's NetBIOS mapper & low net heap
+
+; Make sure that no interrupt vectors still point to us.
+
+ call RestoreRMIntrVectors
+
+; if this is a 80286 & 80287 configuration, we should reset the Co-Processor.
+
+ cmp [f286_287],0 ;286 and 287 config ?
+ jz CTH_Not286And287 ;no.
+
+; reset the 80287 Co-Processor to make sure that it gets out of protected
+; mode.
+
+ xor al,al ;need to out 0
+ out 0F1H,al ;reset the coprocessor
+
+CTH_Not286And287:
+
+; If we're aborting due to a processor fault, do some extra clean-up on
+; the mouse (just to be nice). If this is a normal termination, we leave
+; it up to the child to save/restore the mouse state.
+
+ test fFaultAbort,0FFh
+ jz normal_exit
+
+; Check if the mouse driver callback function has been set, and
+; reset the mouse driver if so.
+
+ mov ax,word ptr lpfnUserMouseHandler
+ or ax,word ptr lpfnUserMouseHandler+2
+ jz @F
+ xor ax,ax
+ int 33h
+@@:
+
+; Check if the PS/2 Pointing Device Handler Address has been set and
+; disable it if so.
+
+ mov ax,word ptr lpfnUserPointingHandler
+ or ax,word ptr lpfnUserPointingHandler+2
+ jz @f
+ mov ax,0C200h
+ xor bh,bh
+ int 15h
+@@:
+
+; Hmmm, we have HP mouse code in dxhpbios.asm, but no clean up here...
+
+normal_exit:
+
+; Release the extended memory heap.
+
+; call ReleaseXmemHeap
+
+; Release the space being used for the descriptor tables and
+; the protected mode code segment.
+
+if VCPI
+ cmp fVCPI,0 ;space not in himem area if under VCPI
+ jz @f
+ call FreeEmsHandle ;under VCPI, we free EMS memory this way
+ jmp short chtm90
+@@:
+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
+@@:
+
+; Clean up after real mode code (and possible real mode incomplete
+; initialization) -- restore real mode interrupt vectors.
+
+chtm90:
+RealModeCleanUp:
+
+; Disable A20 if it was left enabled (normally 1 on 386 systems, 0 on 286)
+
+if VCPI
+ cmp fVCPI,0 ;don't diddle A20 if under VCPI
+ jnz A20Okay
+endif
+ mov cx,A20EnableCount
+ jcxz A20Okay
+@@: xmssvc 6
+ loop @b
+A20Okay:
+
+; Restore the MSW EM bit (Emulate Math coprocessor) to its initial state
+ifndef WOW
+ inc fEMbit ;if fEMbit = FF, we never got far 'nuff to
+ jz @f ; change it
+ dec fEMbit
+
+ smsw ax
+if VCPI
+ out1 <What to do about this? FreeEMSHandle may put us in real mode.>
+endif ; VCPI
+ test al,01h ;in V86 mode?
+ jnz @f ; can't do the lmsw if so
+ or al,fEMbit
+ lmsw ax
+@@:
+endif
+; Make sure DOS knows that the DOS extender is the current process
+
+ mov bx,segPSP
+ dossvc 50h
+
+; Restore SmartDrv cache size if it was shrunk
+
+ cmp [SMRTDRVDelta.SD_I_W_GS_Size],0
+ je short NoSmartReset
+
+ mov dx,offset DGROUP:SMRTDRVName
+ mov ax,3D02h
+ int 21h
+ jc short NoSmartReset
+
+ mov bx,ax
+ mov [SMRTDRVDelta.SD_I_W_FuncGS],SD_IOCTL_WR_Grow_Cache
+ mov dx,offset DGROUP:SMRTDRVDelta
+ mov cx,SIZE SD_IOCTL_WR_GrwShrk
+ mov ax,4403h
+ int 21h
+
+ mov ax,3E00H
+ int 21h
+
+NoSmartReset:
+
+; Reenable a friendly EMM driver if we disabled it at startup
+
+if NOT VCPI
+ call EMMEnable
+endif
+
+; Restore real mode interrupt vectors
+
+ push ds
+
+ lds dx,PrevInt2FHandler ;Int 2Fh handler
+ assume ds:NOTHING
+
+ mov ax,ds ;may not have gotten far enough to
+ or ax,dx ; set interrupt vectors, so make
+ jz @f ; sure before resotring.
+
+ mov ax,252Fh
+ int 21h
+
+IFNDEF ROM
+ifdef NOT_NTVDM_NOT
+ test fHPVectra,HP_CLASSIC
+ jz @f
+
+ mov ax,2569h
+ lds dx,PrevInt69Handler ;HP Vectra A & A+ keyboard slime
+ int 21h
+endif
+ENDIF
+@@:
+ pop ds
+ assume ds:DGROUP
+
+
+IFDEF ROM ;Do any ROM specific clean up
+ call ROMCleanUp
+ENDIF
+
+; We have cleaned up after ourselves, so now we can quit.
+
+ mov ax,WIN386_EXIT ;use Win/386 exit Int 2Fh to tell any
+ mov dx,WIN386_DOSX ; interested parties that we are
+ int 2Fh ; terminating
+
+ mov al,ExitCode ;exit the extender with either the exit
+ cmp al,0FFH ; status from the child, or a status
+ jnz @f ; that we have forced
+ dossvc 4Dh ;get child exit status if we haven't forced it
+@@:
+ dossvc 4Ch ;say goodnight Gracy...
+
+; -------------------------------------------------------
+; ChildTerminationHandler --
+; Input: none
+; Output: none
+; Errors: none
+; Uses:
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public ChildTerminationHandler
+
+ChildTerminationHandler:
+ sub sp,4 ; room for far ret
+ push ds
+ push es
+ pusha
+ mov si,ss
+ mov di,sp
+
+ mov ds,selDgroup
+ mov es,selDgroup
+ mov ss,selDgroup ;make sure we know where the
+
+ mov sp,offset DGROUP:rgwStack ; stack is when we get here
+
+ assume ds:DGROUP,es:DGROUP
+
+ ;bugbug less than zero?
+ dec cDPMIClients
+
+ ; free xmem allocated to this client
+ SwitchToProtectedMode
+ mov ax,[SelCurrentHostData]
+ mov es,ax
+ mov bx,es:[HdSelPSP] ; get "owner"
+ call FreeMemByOwner
+
+ mov dx, selPspChild
+ DPMIBOP TerminateApp
+
+ test DpmiFlags,DPMI_32BIT
+ jz cth05
+
+;
+; Return the stack frame used by the Wow32Intr16 interrupt handler
+;
+ add pbReflStack,CB_STKFRAME
+cth05: cmp cDPMIClients,0
+ jne cth10
+
+
+;
+; Reset the exception stack pointer
+;
+ mov selEHStack,SEL_STACK_ALIAS OR STD_RING
+
+;
+; Give back the xms memory
+;
+ FCLI
+ FBOP BOP_DPMI,FreeAllXmem,FastBop
+
+ call ReInitIdt
+ push 0
+ pop es
+ call ReInitGdt
+ FBOP BOP_DPMI,DpmiNoLongerInUse,FastBop
+
+cth10:
+ SwitchToRealMode
+
+ ;bugbug put resource cleanup code here
+ cmp cDPMIClients,0
+ jne cth20
+
+cth20: mov ax,[SegCurrentHostData]
+ mov es,ax
+ mov ax,es:[HdSegParent]
+ mov [SegCurrentHostData],ax
+ mov ax,es:[HdSelParent]
+ mov [SelCurrentHostData],ax
+ mov ax,es:[HdPSPParent]
+ mov SelPSPChild,ax
+ mov ax,word ptr es:[HdPspTerminate + 2]
+ mov bx,word ptr es:[HdPspTerminate]
+ mov cx,segPSPChild
+ mov ds,cx
+ assume ds:nothing
+ ;
+ ; Restore termination vector (app may think it knows what's here)
+ ;
+ mov ds:[0ah],bx
+ mov ds:[0ch],ax
+ xor cx,cx
+ mov ds,cx
+ ;
+ ; Restore int 22 vector (terminate) Just in case
+ ;
+ mov ds:[22h * 4],bx
+ mov ds:[22h * 4 + 2],ax
+ mov es,si
+ mov es:[di+20],bx
+ mov es:[di+22],ax
+ mov ss,si
+ mov sp,di
+ popa
+ pop es
+ pop ds
+ retf
+
+; -------------------------------------------------------
+; DisplayErrorMsg -- Display an error message on the screen. We set the
+; video adapter to a text mode so the msg is visable (and gets rid
+; of any bizarre mode that others may have set (like WIN.COM)).
+;
+; Note: This routine can be executed in real OR protected mode, so
+; don't do anything mode specific--keep is short and sweet.
+;
+; Input: AX - offset of msg to display
+; DX - segment of msg to display
+; Output: None.
+; Uses: AX, DX modified, all else preserved
+
+ assume ds:NOTHING, es:NOTHING
+ public DisplayErrorMsg
+
+DisplayErrorMsg proc far
+
+ push ds ;save current DS
+ push ax ;save msg offset
+
+; Set a text mode (normally 3, but possibly 7)
+
+ mov ax,0003h ;set video mode 3
+ int 10h
+ mov ah,0Fh ;get video mode
+ int 10h
+ cmp al,03h ;did we change to 3?
+ jz @f
+ mov ax,0007h ;no, must be mode 7
+ int 10h
+@@:
+
+; Display the msg
+
+ mov ds,dx
+ pop dx
+ dossvc 9
+
+ pop ds
+
+ ret
+
+DisplayErrorMsg endp
+
+; -------------------------------------------------------
+; DetermineFreeMemory -- Determine how much memory is free
+;
+; Input: none
+; Output: bx = #paragraphs free memory
+; Uses: bx
+;
+ assume ds:dgroup,es:nothing
+ public DetermineFreeMemory
+
+DetermineFreeMemory proc near
+ push ax
+ mov bx,0ffffh ; allocate all of memory
+ dossvc 48h
+ jnc @f
+
+ pop ax
+ ret
+
+;bugbug report error
+@@: mov bx,0ffffh
+ pop ax
+ ret
+DetermineFreeMemory endp
+EndLowSegment
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+;--------------------------------------------------------
+; ReInitIdt -- Set Idt entries back to default values
+;
+; Input: none
+; Output: none
+; uses: none
+;
+ assume ds:dgroup,es:nothing
+ public ReInitIdt
+
+ReInitIdt proc near
+
+ push bx
+ push cx
+ push dx
+ push si
+ push di
+ push es
+ mov ax,SEL_IDT OR STD_RING
+ mov es,ax
+
+; 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
+ mov 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
+
+;
+; Put the 16 bit hanlders into PMFaultVector
+;
+ mov dx,offset PMReservedEntryVector
+ mov bx,offset PMFaultVector
+ mov cx,32
+iidt75: mov [bx],dx
+ mov word ptr [bx +4],SEL_DXPMCODE OR STD_RING
+ add bx,8
+IFNDEF WOW
+ add dx,3
+%out BUGBUG ERROR! Should NEVER see this
+ELSE
+ add dx,5
+ENDIF
+ loop iidt75
+
+ mov word ptr (PMFaultVector + 8*06h),offset TrapInvalidOpcode
+ mov word ptr (PMFaultVector + 8*08h),offset TrapDoubleFault
+ mov word ptr (PMFaultVector + 8*09h),offset TrapExtensionOverrun
+ mov word ptr (PMFaultVector + 8*0Ah),offset TrapInvalidTss
+ mov word ptr (PMFaultVector + 8*0bh),offset TrapSegmentNotPresent
+ mov word ptr (PMFaultVector + 8*0ch),offset TrapStackOverrun
+ mov word ptr (PMFaultVector + 8*0dh),offset TrapGP
+ mov word ptr (PMFaultVector + 8*0eh),offset TrapPageFault
+
+;
+; Put back the 16 bit transition code
+;
+ call AllocateSelector
+ mov bx,ax
+ mov ax,cs
+ call DupSegmentDscr
+ cCall NSetSegmentAccess,<bx,STD_DATA>
+ mov es,bx
+ assume es:DXPMCODE
+
+ mov [WowTransitionToUserMode],offset DXPMCODE:Wow16TransitionToUserMode
+ mov [WowCopyEhStack],offset DXPMCODE:Wow16CopyEhStack
+ mov [WowCopyIretStack],offset DXPMCODE:Wow16CopyIretStack
+ mov [WowReservedReflector],offset DXPMCODE:PMReservedReflector
+ mov [WowHwIntDispatchProc], offset DXPMCODE:Wow16HwIntrReflector
+ assume es:nothing
+ mov ax,es
+ call FreeSelector
+ xor ax,ax
+ mov es,ax
+
+ .286p
+endif
+
+;
+; Restore hooked RM handlers
+;
+ push ds
+ mov bx,SEL_DXCODE OR STD_RING
+ mov ds,bx
+ assume ds:DXCODE
+ mov bx,segDXCode
+ pop ds
+ assume ds:DGROUP
+
+ push SEL_RMIVT or STD_RING ;selector to real mode int table
+ pop es
+ xor di, di
+ mov cx,78h ;last possible hooked interrupt+1
+
+iidt80:
+ cmp word ptr RmHwISR[di],0 ;is this a hooked int?
+ jnz @f ;jif yes
+ cmp word ptr RmHwISR[di+2],0 ;is this a hooked int?
+ jz iidt85 ;jif no
+@@:
+ mov ax,es:[di + 2]
+ cmp ax,bx ; if it isn't really hooked,
+ jne iidt84 ; don't change it
+
+ mov ax,word ptr RmHwISR[di] ;get offset of old RM vector
+ mov es:[di],ax
+ mov ax,word ptr RmHwISR[di+2] ;get segment of old RM vector
+ mov es:[di+2],ax
+iidt84: xor ax,ax
+ mov word ptr RmHwISR[di],ax ; forget old handler
+ mov word ptr RmHwISR[di+2],ax ; forget old handler
+iidt85:
+ add di,4
+ loop iidt80
+
+IFDEF WOW
+; make the stacks 16 bit again
+
+ cCall NSetSegmentAccess,<selDgroupPM,STD_DATA>
+ cCall NSetSegmentAccess,<selEHStack,STD_DATA>
+ENDIF
+; All done
+
+iidt90: pop es
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ ret
+ReInitIdt endp
+
+ assume ds:DGROUP,es:NOTHING
+ public ReInitGdt
+ReInitGdt proc near
+
+ push ax
+ push dx
+ push di
+ push es
+
+ mov dx,cdscGDTMax
+ shl dx,3
+ jnc @f
+ dec dx
+@@:
+ mov ax,HighestDxSel
+ and ax,NOT SELECTOR_TI
+ add ax,8
+ mov selGDTFree,ax
+ mov es,selGDT
+
+rig30: mov di,ax
+ add ax,8
+ mov word ptr es:[di],ax ; set limit as link to next
+ mov word ptr es:[di + 2],0
+ mov word ptr es:[di + 4],0
+ mov word ptr es:[di + 6],0
+ jc rig40
+ cmp ax,dx
+ jnc rig40
+ jmp rig30
+
+rig40: pop es
+ pop di
+ pop dx
+ pop ax
+ ret
+ReInitGdt endp
+
+;--------------------------------------------------------
+; AllocateExceptionStack -- Get space for exception handler
+;
+; Input: none
+; Output: none
+; carry set on error
+; uses: AX, BX, CX, DX, SI, DI
+;
+ assume ds:dgroup,es:nothing
+ public AllocateExceptionStack
+AllocateExceptionStack proc near
+
+ xor bx,bx
+ mov dx,bx
+ mov cx,1000h ;length of block
+ call AllocateXmem32
+ jc @F
+
+ call AllocateSelector
+ or ax,STD_RING
+ mov selEHStack,ax
+
+ cCall NSetSegmentDscr,<ax,bx,cx,0,0fffh,STD_DATA>
+ mov ax,selEHStack
+
+ mov cx,1000h ;reload length
+ dec cx
+ and cx,0fffeh ; Make sure SP is WORD aligned
+ mov npEHStackLimit,cx
+
+ ;; mark the stack with 0DEADh
+ mov bx, cx
+ push ds
+ mov ds,ax
+ sub bx,2
+ mov word ptr [bx],0DEADh
+ pop ds
+ mov npEHStacklet, bx
+ clc
+@@:
+ ret
+
+AllocateExceptionStack endp
+
+DXPMCODE ends
+
+;****************************************************************
+
+ end start
diff --git a/private/mvdm/dpmi/dxutil.asm b/private/mvdm/dpmi/dxutil.asm
new file mode 100644
index 000000000..ab4930cc4
--- /dev/null
+++ b/private/mvdm/dpmi/dxutil.asm
@@ -0,0 +1,3177 @@
+ PAGE ,132
+ TITLE DXUTIL.ASM -- Dos Extender Miscellaneous Routines
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* DXUTIL.ASM - Dos Extender Miscellaneous *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* *
+;* This module contains miscellaneous routines for the Dos *
+;* Extender. *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 08/08/90 earleh DOSX and client privilege ring determined *
+;* by equate in pmdefs.inc *
+;* 04/09/90 jimmat If 286 with 287, put 287 into pMode too. *
+;* 08/20/89 jimmat Removed local A20 code since HIMEM 2.07 *
+;* works properly across processor resets *
+;* 07/28/89 jimmat Added A20 check/set routines, added *
+;* SelOff2SegOff & Lma2SegOff routines. *
+;* 06/19/89 jimmat Set direction flag before REP MOVS *
+;* 05/25/89 jimmat Added GetSegmentAccess routine *
+;* 03/30/89 jimmat Set IOPL = 3 when entering protect mode *
+;* 03/16/89 jimmat Added more debug sanity checks *
+;* 03/15/89 jimmat Minor changes to run child in ring 1 *
+;* 03/13/89 jimmat Added support for LDT & TSS *
+;* 02/10/89 (GeneA): changed Dos Extender from small model to *
+;* medium model. Also added MoveMemBlock function. *
+;* 01/25/89 (GeneA): changed initialization of real mode code *
+;* segment address in EnterRealMode. caused by adding *
+;* new method of relocationg dos extender for PM operation *
+;* 12/13/88 (GeneA): moved EnterProtectedMode and EnterReal- *
+;* Mode here from dxinit.asm *
+;* 09/16/88 (GeneA): created by extracting code from the *
+;* SST debugger modules DOSXTND.ASM, VIRTMD.ASM, *
+;* VRTUTIL.ASM, and INTERRPT.ASM *
+;* 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI *
+;* 24-Jan-1992 v-simonf Added WOW callout when INT 8 hooked *
+;* *
+;****************************************************************
+
+.286p
+.287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+; .sall
+; .xlist
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include dpmi.inc
+if 0
+ifdef WOW
+include bop.inc
+endif
+endif
+if VCPI
+include dxvcpi.inc
+endif
+IFDEF ROM
+include dxrom.inc
+ENDIF
+include intmac.inc
+.list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+SHUT_DOWN = 8Fh ;address in CMOS ram of the shutdown code
+CMOS_ADDR = 70h ;i/o address of the cmos ram address register
+CMOS_DATA = 71h ;i/o address of the cmos ram data register
+
+DMAServiceSegment equ 040h ;40:7B bit 5 indicates DMA services
+DMAServiceByte equ 07Bh ; are currently required
+DMAServiceBit equ 020h
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn segGDT:WORD
+ extrn segIDT:WORD
+ extrn selGDT:WORD
+ extrn selIDT:WORD
+ extrn selGDTFree:WORD
+ extrn bpGDT:FWORD
+ extrn bpIDT:FWORD
+ extrn bpRmIVT:FWORD
+ extrn idCpuType:WORD
+ extrn rgbXfrBuf1:BYTE
+ extrn i31HWReset:BYTE
+ifdef NOT_NTVDM_NOT
+ extrn fHPVectra:BYTE
+endif
+ extrn PMIntelVector:DWORD
+ extrn PMFaultVector:DWORD
+ extrn lpfnXMSFunc:DWORD
+ extrn PMInt24Handler:DWORD
+
+if VCPI
+ extrn fVCPI:BYTE
+endif
+
+IFDEF ROM
+ extrn segDXCode:WORD
+ extrn segDXData:WORD
+ENDIF
+ extrn pbReflStack:WORD
+ extrn HwIntHandlers:DWORD
+
+bIntMask db 0
+
+bpBogusIDT df 0 ;This is loaded into the IDT register to
+ ; force a bogus IDT to be defined. When we
+ ; then do an interrupt a triple fault will
+ ; occur forcing the processor to reset. This
+ ; is when doing a mode switch to real mode.
+
+IDTSaveArea dw 3 DUP (?) ;save area for IDT during mode switch
+
+ public A20EnableCount
+
+A20EnableCount dw 0
+
+ShutDownSP dw 0 ;stack pointer during 286 reset
+
+ public f286_287
+
+f286_287 db 0 ;NZ if this is a 286 with 287 coprocessor
+
+
+ EXTRN FasterModeSwitch:WORD
+
+if DEBUG ;------------------------------------------------------------
+
+ extrn fTraceA20:WORD
+ extrn fTraceMode:WORD
+
+ public fA20
+
+fA20 db 0
+
+endif ;DEBUG --------------------------------------------------------
+
+selPmodeFS dw 0
+selPmodeGS dw 0
+
+ public HighestSel
+HighestSel dw 0
+
+ifndef WOW
+ public IretBopTable
+IretBopTable label byte
+ irp x,<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>
+ db 0c4h, 0c4h, 05dh, x
+ endm
+else
+ public FastBop
+FastBop df 0
+
+IretBopTable label byte
+ irp x,<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>
+ db 02eh, 066h, 0FFh, 01eh, 00h, 00h, 05dh, x
+ endm
+
+ extrn DpmiFlags:WORD
+NullSel dd 0
+ dd 0
+endif
+DXDATA ends
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+IFNDEF ROM
+ extrn segDXData:WORD
+ extrn segDXCode:WORD
+ extrn selDgroup:WORD
+ENDIF
+
+if VCPI
+ extrn fnVCPI:FWORD
+
+ EXTRN laVTP:DWORD
+
+endif
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+ extrn selDgroupPM:WORD
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Real/Protected Mode Switch Routines
+ page
+
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+
+; -------------------------------------------------------
+; REAL/PROTECTED MODE SWITCH ROUTINES
+; -------------------------------------------------------
+;
+; EnterProtectedMode -- This routine will switch the processor
+; into protected mode. It will return with the processor
+; in protected mode and all of the segment registers loaded
+; with the selectors for the protected mode segments.
+; (CS with the selector for DXCODE and DS,ES,SS with the
+; selector for DXDATA)
+; It will also switch mode dependent memory variables.
+; It assumes that InitGlobalDscrTable and InitIntrDscrTable
+; have been called to set up the descriptor tables appropriately.
+;
+; Note: Except for a very brief time in this routine and in
+; EnterRealMode, the DOS Extender runs in the same ring along
+; with it's child app. This has the benefit of eliminating
+; ring transitions on hardware and software interrupts.
+; It also makes it possible for the child to hook their
+; own interrupt routine into the IDT.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: AX, DS, ES, SS, CS modified, all others preserved
+;
+; NOTE: This routine turns interrupts of and does not turn them
+; back on.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public EnterProtectedMode
+
+EnterProtectedMode proc near
+
+ FCLI
+
+IFNDEF ROM
+; Update the mode dependent variables.
+
+ mov ax,SEL_DXDATA or STD_RING
+ mov selDgroup,ax
+ENDIF
+
+; Set the DMA services required bit for pMode users.
+
+ mov ax,DMAServiceSegment
+ mov es,ax
+ or byte ptr es:[DMAServiceByte],DMAServiceBit
+
+if VCPI
+ cmp fVCPI,0 ;if vcpi, don't touch
+ jne epmVCPIstart ; the a20 line, et al
+endif
+
+; 'local enable' the A20 line via HIMEM before switching to pMode.
+; This is more complicated than you might think. Some real mode code
+; (like old versions of SMARTDRV.SYS) may diddle with A20 on their own.
+; These programs may not want us to change A20 on them. RMIntrReflector
+; may do a XMS 'local enable' to turn A20 back on for one of these pgms.
+; Also, on a 386 where we actually do the mode switch, we try to leave
+; A20 enabled so as to not waste time diddling for nothing. The
+; A20EnabledCount variable tracks if we've 'local enabled' A20 or not.
+; Since we can't really trust real mode to leave A20 alone, we double
+; check that it's really really on when we think it should be.
+
+ push bx ;save bx around XMS calls
+
+ cmp A20EnableCount,0 ;should A20 already be enabled?
+ jz enpm10 ; no, (normal 4 286) just go enable it
+
+ xmssvc 7 ; yes, is it really enabled?
+ or ax,ax
+ jnz enpm15 ; yes, we be done!
+
+if DEBUG ;------------------------------------------------------------
+ or fA20,1 ; somebody done us wrong
+endif ;---------------------------------------------------------------
+
+ xmssvc 6 ;keep enable/disable calls balanced
+ dec A20EnableCount
+enpm10:
+ xmssvc 5 ;local enable A20
+ inc A20EnableCount
+
+if DEBUG ;------------------------------------------------------------
+
+ or ax,ax
+ jnz @f
+ or fA20,2 ;enable failed!
+@@:
+ cmp fTraceA20,0
+ jz @f
+ xmssvc 7 ;in debug mode, make double sure
+ or ax,ax ; A20 was enabled. Slows things
+ jnz @f ; down, but it's better to know.
+ or fA20,2
+@@:
+endif ;DEBUG --------------------------------------------------------
+
+enpm15: pop bx
+
+ifndef WOW
+; Make sure that the nested task flag is clear
+
+ pushf
+ pop ax
+ and ax,NOT 4000h
+ push ax
+ npopf
+
+; Make sure that we have the appropriate descriptor tables in effect,
+; and switch the machine into protected mode
+
+enpr20: smsw ax ;get current machine state
+ or ax,1 ;set the protected mode bit
+ lgdt bpGDT
+ lidt bpIDT
+ lmsw ax ;and away we go
+
+; Flush the instruction queue and load the code segment selector
+; by doing a far jump.
+
+ db 0EAh ;jump far opcode
+ dw offset enpm40 ;offset of far pointer
+ dw SEL_DXCODE0 ;selector part of PM far pointer (ring 0)
+
+if VCPI
+
+; Switch when we are under VCPI
+
+epmVCPIstart:
+
+ .386
+
+ push esi ;save regs modified by VCPI server
+ push eax ; (saving high word of EAX)
+
+ push bp ;save bp on stack & sp in bp
+ mov bp,sp
+
+ mov esi, laVTP ;linear address of structure
+
+ RMvcpi vcpiSWITCHTOPM ;go for it
+
+; Entry point (PM) when running under vcpi
+
+PUBLIC epmVCPI
+epmVCPI:
+ mov ax,SEL_DXDATA0 ;stack has gotta be ring 0
+ mov ss,ax
+ mov sp,bp ;restore sp
+
+ pop bp ;restore old bp
+
+ pop eax ;restore regs that VCPI server may have zapped
+ pop esi
+
+ .286p
+
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds,ax ;ds to our DGROUP
+ mov es,ax ;es too
+
+ jmp short enpmSwitchRing ;rejoin common code
+endif
+
+
+; Load the other segment registers with valid selectors (not under VCPI)
+
+enpm40: mov ax,SEL_DXDATA0 ;stack has gotta be ring 0 also
+ mov ss,ax
+
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds,ax ;ds to our DGROUP
+
+; Load the LDT register and the Task Register
+
+ mov ax,SEL_LDT
+ lldt ax ;load the LDT register
+
+ mov ax,SEL_GDT
+ mov es,ax ;es to GDT
+
+ push si ;make sure busy bit is off
+ mov si,SEL_TSS ; in the TSS descriptor
+ mov es:[si].arbSegAccess,STD_TSS ; before trying to load it
+ ltr si ;now load the task register
+ pop si
+else
+ .386p
+ push ebp
+if 0
+ movzx ebp,sp
+else
+ mov ebp,esp
+endif
+ push SEL_DXCODE or STD_RING ; new cs
+ push 0 ; high half eip
+ push offset epmwow ; new eip
+ push SEL_DXDATA or STD_RING ; new ss
+ push ebp
+ push SEL_DXDATA or STD_RING ; new ds
+ DPMIBOP DPMISwitchToProtectedMode
+epmwow:
+ pop ebp
+ .286p
+endif
+
+ push ds ;point es to DGROUP
+ pop es
+
+; If this is a 286 machine with a 287 math coprocessor, put the coprocessor
+; into protected mode also.
+
+ cmp f286_287,0 ;286 and 287?
+ jz @f
+
+ xor al,al ; yup, clear co-processor busy line
+ out 0F0h,al
+ fsetpm ; and put it in pMode
+@@:
+
+; We're currently running in ring 0. Setup an interlevel iret frame
+; to switch to our normal ring, and also force IOPL=3. I spent 1+ day
+; debugging on a 286 system (with no debugger!) because the 286 seemed
+; switch into protected mode with IOPL=0, and once we got to an outer
+; ring, we would fault on things like CLI instructions.
+
+enpmSwitchRing:
+ifndef WOW
+ mov ax,sp ;still points to return address
+ push SEL_DXDATA or STD_RING ;new ss
+ push ax ;new sp
+ pushf
+ pop ax
+ or ah,30h
+ push ax ;new flags, with IOPL=3
+ push SEL_DXCODE or STD_RING ;new cs
+ push offset DXCODE:epm_ret ;new ip
+ iret
+endif
+; When we get here, we are now in an outer ring.
+
+epm_ret:
+
+ cmp idCpuType, 3
+ jc SegRegsOK
+.386
+ mov ax, selPmodeFS
+ mov fs, ax
+ mov ax, selPmodeGS
+ mov gs, ax
+.286p
+
+SegRegsOK:
+if DEBUG ;------------------------------------------------------------
+
+ cmp fTraceMode,0
+ jz @f
+ Trace_Out "^",x
+@@:
+if VCPI
+ cmp fVCPI,0
+ jz @f
+ jmp a20okay
+@@:
+endif ; VCPI
+ cmp A20EnableCount,1
+ jz @f
+ Debug_Out "EnterProtectedMode: A20EnableCount != 1"
+@@:
+ cmp fTraceA20,0
+ jz a20okay
+ test fA20,0FFh
+ jz a20okay
+ test fA20,01h
+ jz @f
+ Trace_Out "EPM: A20 was wrong!"
+@@:
+ test fA20,02h
+ jz @f
+ Trace_Out "EPM: A20 enable failed!"
+@@:
+ test fA20,04h
+ jz @f
+ Trace_Out "rM2pMInt: A20 was on"
+@@:
+ mov fA20,0
+a20okay:
+endif ;DEBUG --------------------------------------------------------
+
+ ret ;near return to caller in pMode
+
+EnterProtectedMode endp
+
+
+; -------------------------------------------------------
+; EnterRealMode -- This routine will switch the processor
+; from protected mode back into real mode. It will also
+; reset the various mode dependent variables to their
+; real mode values and load the segment registers with
+; the real mode segment addresses.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: AX, DS, ES, SS, CS modified
+;
+; NOTE: This routine must be called with the stack segment set
+; to the Dos Extender data segment, as it resets the stack
+; segment register to the Dos Extender real mode data segment
+; but does not modify the stack pointer.
+; NOTE: This routine turns interrupts off and and does not turn
+; them back on.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public EnterRealMode
+
+EnterRealMode proc near
+
+
+if DEBUG ;--------------------------------------------------------
+
+ cmp fTraceMode,0
+ jz @f
+ Trace_Out "v",x
+@@:
+endif ;DEBUG ---------------------------------------------------------
+
+ cmp idCpuType,3
+ jc RegsOkay
+.386
+ mov ax,fs
+ mov selPmodeFS, ax
+ mov ax,gs
+ mov selPmodeGS, ax
+RegsOkay:
+.286p
+
+ FCLI
+
+IFDEF ROM
+ push ds
+ pop es
+ELSE
+ mov es,selDgroup
+ENDIF
+
+IFNDEF WOW
+; If we are running on an 80386, we can do the switch more efficiently.
+
+ cmp idCpuType,3 ;80386?
+ jc enrm20
+
+; We're on an 386 (or better)--do a faster mode switch. Call a ring 0 proc
+; to actually do the switch. The call gate has been setup to select either
+; the roll-our-own 386 switch, or the VCPI switch.
+
+enrm10:
+ db 9Ah ;call far SEL_RESET:0
+ dw 0,SEL_RESET or STD_RING
+
+; The Reset386 routine does a near return (cause the far CS on the stack
+; is for protected mode, and it returns in real mode). Discard the PM
+; CS value.
+
+ pop ax ;discard old PM CS from stack
+
+; The transition to the ring 0 procedure caused a stack switch. The return
+; back to here (in real mode) didn't restore the old stack, so do it now...
+
+ pop sp ;restore previous sp offset
+
+ jmp enrm70 ;jmp to common exit code
+
+
+; On the 80286, it is a lot more complicated. The PC BIOS start up code
+; will check some special locations in the BIOS data segment to see if the
+; machine was reset because it wants to do a mode switch. If this is the
+; case, it will restore the machine state from these variables and return
+; to the original process. We need to set up the state so that the BIOS
+; will return control to us, and then generate a reset. The reset is
+; generated by causing the machine to triple fault. This is an undocumented
+; feature (i.e. bug) in the '286.
+
+enrm20:
+
+; Set up the stack for the BIOS to use after the reset.
+
+ cmp [FasterModeSwitch],0
+ jne enrm21
+
+ pushf ;Push an IRET type return address of where
+ ; we want to come back to after the reset
+ push segDXCode
+ push offset enrm50
+
+enrm21:
+
+ mov ax,segDXData ;Our real mode data segment address
+
+IFNDEF ROM
+ifdef NOT_NTVDM_NOT
+ test fHPVectra,HP_CLASSIC ;HP Vectra A & A+ systems have a bug
+ jz enrm25 ; in their rom that requires a
+ ; different stack setup
+
+ push 0 ;HP Vectra A & A+ trashes this
+ push ax ;real mode data seg
+ pusha
+ push ax ;es?
+ jmp short enrm30
+
+enrm25:
+endif
+ENDIF
+
+ pusha ;The BIOS will do a POPA, so put all of the
+ ; registers on the stack for it.
+ push ax ;It will also pop these into ES and DS
+ push ax
+enrm30:
+
+ push SEL_BIOSDATA ;BIOS data segment selector
+ pop es
+ assume es:BIOS_DATA
+
+ cmp [FasterModeSwitch],0
+ jne enrm31
+; Tell the BIOS where the stack is.
+
+ mov IO_ROM_SEG,ax ;Set up the address of the stack for the
+ mov IO_ROM_INIT,sp ; BIOS to use after the reset
+ jmp enrm32
+enrm31:
+; Tell the BIOS where to transfer control.
+ mov bx,segDXCode
+ mov IO_ROM_SEG,bx
+ mov IO_ROM_INIT,offset DXCODE:enrm45
+
+ mov ShutDownSP,sp ;save stack pointer in data seg
+enrm32:
+; IDT save/restore taken from OS/2 mode switching code...
+;
+; Now preserve three words of the vector table at 03FAh
+; so we can restore it after the mode switch. The ROM
+; trashes the top three words of the real mode IDT by
+; putting a stack at 30:100.
+
+ push ds
+ pop es
+ push SEL_RMIVT or STD_RING ;address real mode IDT
+ pop ds
+ assume ds:NOTHING,es:DGROUP
+
+ MOV SI,03FAH ;BIOS will pop regs, so
+ MOV DI,OFFSET DGROUP:IDTSaveArea ; we don't need to save
+ CLD ; them here
+ MOVSW
+ MOVSW
+ MOVSW
+
+ push es
+ pop ds
+ assume ds:DGROUP
+
+; Write the shutdown type code into the CMOS ram. Code 9 causes the BIOS
+; to load SS:SP from IO_ROM_SEG:IO_ROM_INIT, restore the registers and then
+; do an IRET. Code 0Ah loads a CS:IP and jumps to it.
+
+ mov al,SHUT_DOWN
+ out CMOS_ADDR,al
+ mov al,9
+ cmp [FasterModeSwitch],0
+ je enrm33
+ mov al,0ah
+enrm33:
+ out CMOS_DATA,al
+
+; Shut out all interrupts while we are resetting the processor.
+
+ in al,INTA01
+ mov bIntMask,al
+ mov al,0FFh
+ out INTA01,al
+
+; Now, force a reset by causing a triple fault. We do this via a far call
+; to a call gate. The ring 0 procedure (Reset286) then loads the IDT
+; register with a bogus IDT and does an INT 3. This causes the infamous
+; "triple fault" which resets the processor.
+
+ db 9Ah ;call far SEL_RESET:0
+ dw 0,SEL_RESET or STD_RING
+
+; After the BIOS has finished its reset processing, control will come back
+; to here in real mode. We will be using the same stack as before, all
+; regular registers have been preserved, and DS and ES contain the real mode
+; address of DXDATA.
+
+enrm45:
+ FCLI
+
+IFDEF ROM
+ %OUT What stack is in use here?
+ GetRMDataSeg
+ mov ss,ax
+ELSE
+ mov ss,segDXData ;restore our stack
+ENDIF
+ mov sp,ss:ShutDownSP
+
+ pop es ;restore registers from stack
+ pop ds
+ popa
+
+; Reset processing is almost finished. We are using the same stack as
+; before, all regular registers have been preserved, and DS and ES
+; contain the real mode seg of DXDATA.
+
+ assume ds:DGROUP,es:DGROUP,SS:DGROUP
+
+enrm50:
+ FCLI
+
+if DEBUG
+ lgdt bpGDT ;We need to do this so that DEB386 can still
+ ; dump things after we switch to real mode.
+ ;When we went through the reset for '286 we
+ ; have trashed the GDTR in the cpu.
+endif
+
+; Restore trashed interrupt vector area
+
+ xor ax,ax ;address real mode IDT
+ mov es,ax
+
+ push si ;restore the three IDT words
+ push di
+ mov si,offset DGROUP:IDTSaveArea
+ mov di,03FAh
+ cld
+ movsw
+ movsw
+ movsw
+
+ pop di
+ pop si
+
+ push ds ;resotre es -> DXDATA
+ pop es
+
+; Restore the state of the interrupt mask register
+
+ mov al,bIntMask
+ out INTA01,al
+
+ cmp [FasterModeSwitch],0
+ jne enrm51
+
+; Back in real mode, 'local disable' the A20 line via HIMEM. We only do
+; this on a 286 'cause the BIOS has already turned off A20. The 'local
+; disable' lets the XMS driver know A20 is off, and possibly causes it to
+; actually enable A20 if someone loaded prior to DOSX (like a TSR) wanted
+; A20 on.
+
+ push bx
+ xmssvc 6
+ dec A20EnableCount
+ pop bx
+
+ jmp enrm70
+
+enrm51:
+; Enable NMIs
+
+ mov al,0Dh
+ out CMOS_ADDR,al
+
+; This is common exit code that is performed for both '386 and '286
+; processors.
+else ; wow
+
+ push SegDxCode
+ push offset DXCODE:enrmwow
+ push SegDxData
+ push sp
+ push SegDxData
+.386p
+ FBOP BOP_SWITCHTOREALMODE,,FastBop
+.286p
+enrmwow: add sp,6 ; remove rest of parameters
+ push ds
+ pop es ; es not set by mode switch
+endif ; wow
+
+enrm70:
+ push es ;clear DMA services required
+ mov ax,DMAServiceSegment ; bit for real mode
+ mov es,ax
+ and byte ptr es:[DMAServiceByte],not DMAServiceBit
+ pop es
+
+IFNDEF ROM
+ mov ax,segDXData
+ mov selDgroup,ax
+ENDIF
+
+ ret
+
+EnterRealMode endp
+
+
+; -------------------------------------------------------
+; Reset286 -- This procedure is called via a gate as
+; part of the switch to real mode. Most of
+; the DOS Extender runs in the child application's
+; ring. The lidt instruction has to execute in ring 0.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public Reset286
+
+Reset286 proc far
+
+ifndef KBD_RESET ;----------------------------------------
+
+ lidt bpBogusIDT ;Load up IDTR with 0 length IDT
+
+ int 3 ;Interrupt thru interrupt table with 0 length
+ ;Causes "triple fault" which resets the 286
+
+else ;----------------------------------------
+
+ call Sync8042
+ mov al,0feh ; Send 0feh - system reset command
+ out 64h,al
+@@:
+ hlt
+ jmp short @b
+
+endif ;----------------------------------------
+
+Reset286 endp
+
+ifdef KBD_RESET ;----------------------------------------
+
+Sync8042 proc near
+
+ xor cx,cx
+S8InSync:
+ in al,64h
+ and al,2
+ loopnz S8InSync
+ ret
+
+Sync8042 endp
+
+endif ;----------------------------------------
+
+
+; -------------------------------------------------------
+; Reset386 -- This procedure is called via a gate as
+; part of the switch to real mode. Most of
+; the DOS Extender runs in ring 1. This
+; routine has a few ring 0 instructions.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public Reset386
+
+Reset386 proc far
+
+ .386p
+
+ push dx
+ push eax ;save high word of eax
+
+ mov dx,segDXData ;get the real mode data segment
+
+ mov eax,cr0 ;turn off protected mode bit
+ and al,0FEh
+ lidt fword ptr bpRmIVT ;Load real-mode IDT
+
+ mov cr0,eax
+
+IFDEF ROM ;--------------------------------------------------------
+
+ extrn segRomDXCODE:ABS
+
+ db 0EAh ;jmp far to purge prefetch queue
+ dw r386_10
+ public lCodeSegLoc
+lCodeSegLoc label word
+ dw segRomDXCODE
+
+ELSE ;ROM ---------------------------------------------------------
+
+; Note, the following far jmp has special dispensation to use a SEG DXCODE
+; operand which would normally require a fixup. DOSX used to relocate its
+; code for protected mode and plug the segment value in at lCodeSegLoc. It
+; doesn't perform the relocation any longer, but debug only code still
+; checks for fix ups that might require fix ups. It knows lCodeSegLoc is ok.
+
+ db 0EAh ;jmp far to purge prefetch queue
+ dw r386_10
+ public lCodeSegLoc
+lCodeSegLoc label word
+ dw seg DXCODE
+
+ENDIF ;ROM ---------------------------------------------------------
+
+r386_10:
+ mov ss,dx ;real mode data segment address
+ mov ds,dx
+ mov es,dx
+
+ pop eax
+ pop dx
+
+ .286p
+
+; We entered this routine via a far call, even though it was from code in
+; the same segment. However, the CS value on the stack is a protected
+; mode selector, not a real mode segment. Do a near return, the caller
+; will pop off the PM CS value.
+
+ retn ;NOTE: near return even though far call!
+
+Reset386 endp
+
+if VCPI ;----------------------------------------------------------------
+
+; -------------------------------------------------------
+; ResetVCPI -- This procedure is called via a gate as
+; part of the switch to real (V86) mode when
+; running under a VCPI server. Most of
+; the DOS Extender runs in the user code ring. This
+; routine runs in ring 0.
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public ResetVCPI
+
+ResetVCPI proc far
+
+ .386p
+
+ push eax ;save eax & edx for after switch
+ push edx
+
+IFDEF ROM
+ GetPMDataSeg
+ movzx edx, ax
+ELSE
+ movzx edx,segDXData ;real mode DGROUP segment
+ENDIF
+ mov eax, esp ;save esp for stack frame of dosext
+
+ push 0 ;gs begin frame for VCPI
+ push 0 ;gs
+ push 0 ;fs
+ push 0 ;fs
+ push edx ;ds
+ push edx ;es
+ push edx ;ss
+ push eax ;esp
+ push eax ;eflags reserved
+ push 0 ;cs
+ push cs:[lCodeSegLoc] ;cs
+ push 0 ;eip (high)
+ push offset DXCODE:retVCPI ;eip (low)
+
+ mov ax, SEL_VCPIALLMEM ;setup ds to be
+ mov ds, ax ; selector for all memory
+
+ mov ax, vcpiSWITCHTOV86
+ call [fnVCPI] ;call the VCPI server
+
+; Return point for pMode to V86 switch. The VCPI server sets up
+; segment registers & stack pointer.
+
+retVCPI:
+ pop edx ;restore eax & edx
+ pop eax
+
+ .286p
+
+; We entered this routine via a far call, even though it was from code in
+; the same segment. However, the CS value on the stack is a protected
+; mode selector, not a real mode segment. Do a near return, the caller
+; will pop off the PM CS value.
+
+ retn ;NOTE: near return even though far call!
+
+ResetVCPI endp
+
+
+; -------------------------------------------------------
+; CallVCPI -- This procedure is invoked via a call gate in
+; order to call the VCPI protected mode entry
+; point in ring 0. Most of the DOS Extender
+; runs in the user code ring, this routine runs in ring 0.
+;
+; Note: This routine requires DS to point to the DOSX
+; DGROUP, not a parameter to VCPI!
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public CallVCPI
+
+CallVCPI proc far
+
+ .386p
+
+ call [fnVCPI] ;call the VCPI server
+
+ .286p
+
+ ret
+
+CallVCPI endp
+
+endif ;VCPI ---------------------------------------------------------
+
+IFDEF WOW
+ public RmUnsimulateProc
+
+RmUnsimulateProc proc far
+ BOP BOP_UNSIMULATE
+RmUnsimulateProc endp
+
+ENDIF
+
+; -------------------------------------------------------
+DXCODE ends
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+; -------------------------------------------------------
+; RAW MODE SWITCH ROUTINES
+; -------------------------------------------------------
+
+; ------------------------------------------------------
+; PmRawModeSwitch -- This routine performs a raw mode switch from
+; protected mode to real mode. NOTE: applications will JUMP at this
+; routine
+;
+; Input: ax - new DS
+; cx - new ES
+; dx - new SS
+; bx - new sp
+; si - new CS
+; di - new ip
+; Output: DS, ES, SS, sp, CS, ip contain new values
+; Errors: none
+; Uses:
+;
+;
+;
+ assume ds:nothing, ss:nothing, es:nothing
+ public PmRawModeSwitch
+PmRawModeSwitch proc far
+
+ push ss
+ pop ds
+ push bx
+IFNDEF WOW
+ mov bx,sp
+ELSE
+.386p
+ mov bx,ss
+ movzx ebx,bx
+ lar ebx,ebx
+ test ebx,(AB_BIG SHL 8)
+ mov ebx,esp
+ jnz prms10
+
+ movzx ebx,bx
+prms10:
+.286p
+ENDIF
+
+; Switch to dosx stack (since switch to real mode will do that to us anyway
+; NOTE: no-one can call EnterIntHandler or ExitIntHandler until we switch to
+; the user's new stack. If they do, they will use the area we stored
+; the parameters for this call for a stack frame
+
+ rpushf
+ FCLI
+ push SEL_DXDATA OR STD_RING
+ pop ss
+ assume ss:DGROUP
+IFNDEF WOW
+ mov sp,pbReflStack
+ELSE
+.386p
+ movzx esp,word ptr pbReflStack
+.286p
+ENDIF
+
+; Save user registers
+
+ push dx ; ss
+IFDEF WOW
+.386p
+ push word ptr [ebx]
+ push word ptr [ebx - 2]; flags pushed before cli
+.286p
+ELSE
+ push word ptr [bx] ; sp
+ push word ptr [bx - 2] ; flags pushed before cli
+ENDIF
+ push si ; cs
+ push di ; ip
+ push ax ; ds
+ push cx ; es
+
+; switch modes
+
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ SwitchToRealMode
+
+; set the registers, switch stacks, and return to the user
+
+ pop es
+ pop ds
+ pop ax ; ip
+ pop bx ; cs
+ pop cx ; flags
+ pop si ; sp
+ pop ss
+ assume ss:nothing
+ mov sp,si
+ push cx
+ popf
+ push bx
+ push ax
+ ret
+
+PmRawModeSwitch endp
+
+; NOTE: this is now the DXCODE segment, NOT the DXPMCODE segment (courtesy
+; of SwitchToRealMode
+
+; ------------------------------------------------------
+; RmRawModeSwitch -- This routine performs a raw mode switch from
+; protected mode to real mode. NOTE: applications will JUMP at this
+; routine
+;
+; Input: ax - new DS
+; cx - new ES
+; dx - new SS
+; bx - new sp
+; si - new CS
+; di - new ip
+; Output: DS, ES, SS, sp, CS, ip contain new values
+; Errors: none
+; Uses:
+;
+;
+;
+ assume ds:nothing, ss:nothing, es:nothing
+ public RmRawModeSwitch
+RmRawModeSwitch proc far
+
+ push ss
+ pop ds
+ push bx
+ mov bx,sp
+
+; Switch to dosx stack (since switch to real mode will do that to us anyway
+; NOTE: no-one can call EnterIntHandler or ExitIntHandler until we switch to
+; the user's new stack. If they do, they will use the area we stored
+; the parameters for this call for a stack frame
+
+ pushf
+ FCLI
+ push segDxData
+ pop ss
+ assume ss:DGROUP
+ mov sp,pbReflStack
+
+; Save user registers
+
+ push dx ; ss
+ push word ptr [bx] ; sp
+ push word ptr [bx - 2] ; flags from before cli
+ push si ; cs
+ push di ; ip
+ push ax ; ds
+ push cx ; es
+
+; switch modes
+
+ mov ax,segDxData
+ mov ds,ax
+ SwitchToProtectedMode
+
+; set the registers, switch stacks, and return to the user
+
+ pop es
+ pop ds
+IFDEF WOW
+.386p
+ test DpmiFlags,DPMI_32BIT
+ jnz rrms10
+
+ xor eax,eax ; clear high 16 bits
+ xor edi,edi ; clear high 16 bits
+.286p
+ENDIF
+rrms10: pop di ; ip
+ pop ax ; cs
+ pop cx ; flags from before cli
+ pop bx ; sp
+ assume ss:nothing
+ pop ss
+IFDEF WOW
+.386p
+ mov esp,ebx
+.286p
+ELSE
+ mov sp,bx
+ENDIF
+ push cx
+ rpopf
+
+IFDEF WOW
+.386p
+ push eax
+ push edi
+ db 066h
+ retf
+.286p
+ELSE
+ push ax
+ push di
+ retf
+ENDIF
+
+RmRawModeSwitch endp
+
+DXPMCODE ENDS
+
+DXCODE SEGMENT
+
+; -------------------------------------------------------
+; STATE SAVE/RESTORE ROUTINES
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; RmSaveRestoreState -- This routine exists as a placeholder. It
+; is not currently necessary to perform any state saving/restoring
+; for raw mode switch. The DPMI spec states that the user can call
+; this routine with no adverse effect.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: none
+;
+ assume ds:nothing, ss:nothing, es:nothing
+ public RmSaveRestoreState
+RmSaveRestoreState proc far
+ ret
+RmSaveRestoreState endp
+
+DXCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+; -------------------------------------------------------
+; RmSaveRestoreState -- This routine exists as a placeholder. It
+; is not currently necessary to perform any state saving/restoring
+; for raw mode switch. The DPMI spec states that the user can call
+; this routine with no adverse effect.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: none
+;
+ assume ds:nothing, ss:nothing, es:nothing
+ public PmSaveRestoreState
+PmSaveRestoreState proc far
+ ret
+PmSaveRestoreState endp
+
+IFNDEF WOW
+; -------------------------------------------------------
+; GetIntrVector -- This routine will return the interrupt
+; vector from the Interrupt Descriptor Table for the
+; specified interrupt.
+;
+; Input: AX - interrupt number of interrupt to return
+; Output: CX - selector of the interrupt service routine
+; DX - offset of the interrupt service routine
+; Errors: none
+; Uses: CX, DX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetIntrVector
+
+GetIntrVector proc near
+
+ push si
+
+ mov si,ax
+ cmp si,8
+ jb @f
+
+ cmp si,0fh
+ jbe giv30
+
+ cmp si,070h
+ jb @f
+
+ cmp si,077h
+ jbe giv30
+ ;if the Int # is below CRESERVED
+@@: cmp ax,CRESERVED ; then the handler address is
+ jnb @f ; in PMIntelVector
+ shl si,2
+ mov dx,word ptr PMIntelVector[si]
+ mov cx,word ptr PMIntelVector[si+2]
+ jmp short giv90
+@@:
+ push es
+ shl si,3 ;otherwise it's in the IDT
+ mov es,selIDT
+giv20: mov dx,es:[si].offDest
+ mov cx,es:[si].selDest
+ pop es
+ jmp short giv90
+
+giv30: sub si,8
+ cmp si,8
+ jb @f
+
+ sub si,070h - 16
+@@: shl si,3
+ mov dx,word ptr HwIntHandlers[si]
+ mov cx,word ptr HwIntHandlers[si + 4]
+giv90:
+ pop si
+ return
+
+GetIntrVector endp
+
+; -------------------------------------------------------
+; PutIntrVector -- This routine will place the specified
+; interrupt vector address into the Interrupt Descriptor
+; Table entry for the specified interrupt.
+;
+; Input: AX - interrupt number
+; CX - selector of interrupt routine
+; DX - offset of interrupt routine
+; Output: none
+; Errors: none
+; Uses: all registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public PutIntrVector
+
+PutIntrVector proc near
+
+ push di
+
+ mov di,ax
+ mov di,ax
+ cmp di,8
+ jb @f
+
+ cmp di,0fh
+ jbe piv30
+
+ cmp di,070h
+ jb @f
+
+ cmp di,077h
+ jbe piv30
+ ;if the Int # is below CRESERVED
+@@: cmp ax,CRESERVED ; then the handler address gets
+ jnb @f ; put into PMIntelVector
+
+ shl di,2
+ mov word ptr PMIntelVector[di],dx
+ mov word ptr PMIntelVector[di+2],cx
+ jmp piv90
+
+@@:
+ push es
+ shl di,3 ;otherwise it goes directly in the IDT
+ mov es,selIDT
+ FCLI
+
+piv20: mov es:[di].offDest,dx
+ mov es:[di].selDest,cx
+ FSTI
+ pop es
+
+; If setting the Critical Error Handler, store the routine's address for
+; easy access later.
+
+ cmp al,24h
+ jnz piv90
+
+ mov word ptr PMInt24Handler+2,cx
+ mov word ptr PMInt24Handler,dx
+ jmp piv90
+
+piv30: sub di,8
+ cmp di,8
+ jb @f
+
+ sub di,070h - 16
+@@: shl di,3
+ mov word ptr HwIntHandlers[di],dx
+ mov word ptr HwIntHandlers[di + 4],cx
+
+piv90:
+ pop di
+ ret
+
+PutIntrVector endp
+
+ELSE
+
+; -------------------------------------------------------
+; GetIntrVector -- This routine will return the interrupt
+; vector from the Interrupt Descriptor Table for the
+; specified interrupt.
+;
+; Input: AX - interrupt number of interrupt to return
+; Output: CX - selector of the interrupt service routine
+; DX - offset of the interrupt service routine
+; Errors: none
+; Uses: CX, DX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetIntrVector
+
+GetIntrVector proc near
+
+ push si
+
+ mov si,ax
+ cmp si,8
+ jb @f
+
+ cmp si,0fh
+ jbe giv30
+
+ cmp si,070h
+ jb @f
+
+ cmp si,077h
+ jbe giv30
+
+@@: push es
+ shl si,3 ;otherwise it's in the IDT
+ mov es,selIDT
+ test DpmiFlags,DPMI_32BIT
+ je giv20
+
+ ; Get upper 16 bits of ip
+.386p
+ mov dx,es:[si + 6]
+ shl edx,16
+.286p
+giv20: mov dx,es:[si].offDest
+ mov cx,es:[si].selDest
+ pop es
+ jmp giv90
+
+giv30: sub si,8
+ cmp si,8
+ jb giv40
+
+ sub si,070h - 16
+giv40: shl si,3
+ test DpmiFlags,DPMI_32BIT
+ jz giv50
+
+.386p
+ mov edx,HwIntHandlers[si]
+.286p
+ jmp giv60
+
+giv50: mov dx,word ptr HwIntHandlers[si]
+giv60: mov cx,word ptr HwIntHandlers[si + 4]
+giv90:
+ pop si
+ return
+
+GetIntrVector endp
+; -------------------------------------------------------
+; PutIntrVector -- This routine will place the specified
+; interrupt vector address into the Interrupt Descriptor
+; Table entry for the specified interrupt.
+;
+; Input: AX - interrupt number
+; CX - selector of interrupt routine
+; DX - offset of interrupt routine
+; Output: none
+; Errors: none
+; Uses: all registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public PutIntrVector
+
+PutIntrVector proc near
+.386p
+ push di
+ push ax
+ push ebx
+ push edx
+
+ test DpmiFlags,DPMI_32BIT
+ jz piv10
+
+ mov ebx,VDM_INT_32
+ jmp piv20
+
+piv10: mov ebx,VDM_INT_16
+ movzx edx,dx
+
+piv20: mov di,ax
+ cmp di,8
+ jb @f
+
+ cmp di,0fh
+ jbe piv21
+
+ cmp di,070h
+ jb @f
+
+ cmp di,077h
+ jbe piv21
+
+@@: push es
+ shl di,3 ;otherwise it goes directly in the IDT
+ mov es,selIDT
+
+ FCLI
+
+
+ ; Put upper 16 bits of ip
+ push edx
+ shr edx,16
+ mov es:[di + 6],dx
+ pop edx
+
+
+ mov es:[di].offDest,dx
+ mov es:[di].selDest,cx
+ FSTI
+ pop es
+
+; If setting the Critical Error Handler, store the routine's address for
+; easy access later.
+
+ cmp al,24h
+ jnz piv23
+
+ mov word ptr PMInt24Handler+4,cx
+ mov dword ptr PMInt24Handler,edx
+ jmp piv23
+
+piv21: sub di,8
+ cmp di,8
+ jb @f
+
+ sub di,070h - 16
+@@: shl di,3
+ mov HwIntHandlers[di],edx
+ mov word ptr HwIntHandlers[di + 4],cx
+
+ ;
+ ; Don't put the users handler into the "system ivt" otherwise,
+ ; stack switch doesn't happen!!
+ ;
+ jmp piv90
+
+;
+; set the handler in the actual "ivt", so it will get called on interrupts
+;
+piv23: cmp ax,70h
+ jb piv30
+
+ cmp ax,77h
+ ja piv30
+
+;
+; Hardware interrupts get interrupt gates
+;
+ or ebx,VDM_INT_INT_GATE
+ jmp piv40
+
+;
+; Everyone else gets trap gates
+;
+piv30: or ebx,VDM_INT_TRAP_GATE
+piv40: push bx
+ push ax
+ push cx
+ push edx
+ DPMIBOP SetProtectedModeInterrupt
+ add sp,10
+
+piv90:
+ pop edx
+ pop ebx
+ pop ax
+ pop di
+ ret
+
+PutIntrVector endp
+.286p
+ENDIF
+
+; -------------------------------------------------------
+; GetFaultVector -- This routine will return the fault
+; handler address from the fault handler vector.
+;
+; Input: AX - fault number of fault handler to return
+; Output: CX - selector of the fault handler routine
+; DX - offset of the fault handler routine
+; Errors: none
+; Uses: CX, DX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetFaultVector
+
+GetFaultVector proc near
+
+ push si
+
+ mov si,ax ;if the Int # is below CRESERVED
+ cmp ax,CRESERVED ; then we do it
+ jnb @f
+
+IFNDEF WOW
+ shl si,2
+ mov dx,word ptr PMFaultVector[si]
+ mov cx,word ptr PMFaultVector[si+2]
+ELSE
+ shl si,3
+.386p
+ mov edx,dword ptr PMFaultVector[si]
+.286p
+ mov cx,word ptr PMFaultVector[si+4]
+ENDIF
+@@:
+ pop si
+ return
+
+GetFaultVector endp
+
+
+; -------------------------------------------------------
+; PutFaultVector -- This routine will place the specified
+; fault handler address into the fault handler vector.
+;
+; Input: AX - fault number
+; CX - selector of fault handler routine
+; DX - offset of fault handler routine
+; Output: none
+; Errors: none
+; Uses: all registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public PutFaultVector
+
+PutFaultVector proc near
+
+ push di
+
+ mov di,ax ;if the fault # is below CRESERVED
+ cmp ax,CRESERVED ; then we do it
+ jnb @f
+
+IFNDEF WOW
+ shl di,2
+ mov word ptr PMFaultVector[di],dx
+ mov word ptr PMFaultVector[di+2],cx
+ELSE
+ shl di,3
+.386p
+ mov dword ptr PMFaultVector[di],edx
+.286p
+ mov word ptr PMFaultVector[di+4],cx
+ENDIF
+@@:
+ pop di
+ ret
+
+PutFaultVector endp
+
+; -------------------------------------------------------
+; GTPARA -- This routine will return the real mode paragraph
+; address of the specified protected mode memory segment.
+;
+; Input: SI - selector of the segment
+; Output: returns ZR true if segment is in lower 1MB range
+; AX - real mode paragraph address
+; returns ZR false if segment is in extended memory
+; Errors: returns CY true if selector isn't valid
+; Uses: AX modified, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public gtpara
+
+gtpara:
+ push cx
+ push es
+ push si
+
+ push bx
+ mov bx,selGDT ;selector for the GDT segment
+ mov es,bx
+ lsl bx,bx
+ and bx,SELECTOR_INDEX
+ and si,SELECTOR_INDEX
+ cmp si,bx ;check the given selector against
+ ; the GDT segment limit
+ pop bx
+ jc gtpr20
+
+; The given selector is beyond the end of the GDT, so return error.
+
+ or sp,sp
+ stc
+
+ Debug_Out "gtpara: invalid selector (#si)"
+
+ jmp short gtpr90
+
+; The selector specified is inside the range of defined descriptors in
+; the GDT. Get the address from the descriptor.
+
+gtpr20: mov cl,es:[si].adrBaseHigh
+ test cl,0F0h
+ jnz gtpr90
+
+ shl cl,4
+ mov ax,es:[si].adrBaseLow
+
+if DEBUG ;------------------------------------------------------------
+ test al,0Fh
+ jz @f
+ Debug_Out "gtpara: segment not on para boundry, sel #si at #cl#ax"
+@@:
+endif ;DEBUG --------------------------------------------------------
+
+ shr ax,4
+ or ah,cl
+ cmp ax,ax
+;
+gtpr90:
+ pop si
+ pop es
+ pop cx
+ ret
+
+
+; -------------------------------------------------------
+; SelOff2SegOff -- This routine will return will translate a
+; protected mode selector:offset address to the corresponding
+; real mode segment:offset address.
+;
+; Input: AX - PM selector
+; DX - PM offset
+; Output: if Z set:
+; AX - RM segment
+; DX - RM offset
+; if NZ set, address is not in conventional memory, and
+; cannot be translated
+;
+; Errors: none
+; Uses: AX, DX; all else preserved
+;
+; Note: This routine is very similar to gtpara, and could replace it!
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public SelOff2SegOff
+
+SelOff2SegOff proc near
+
+ push bx
+ push cx
+ push dx
+
+ call GetSegmentAddress ;bx:dx = lma of segment
+
+ pop cx ;cx = offset
+
+ test bl,0f0h ;above 1 Meg line?
+ jnz @f ; yes, cut out now
+
+ add dx,cx
+ adc bx,0 ;bx:dx = lma of segment:offset
+
+ call Lma2SegOff ;bx:dx = seg:off
+ mov ax,bx ;dx:ax = seg:off
+
+ cmp ax,ax ;under 1 Meg, set Z flag
+@@:
+ pop cx
+ pop bx
+
+ ret
+
+SelOff2SegOff endp
+
+
+; ------------------------------------------------------
+; Lma2SegOff -- This routine converts a linear memory address
+; in BX:DX to a normalized SEG:OFF in BX:DX.
+;
+; Input: BX:DX = lma
+; Output: BX:DX = normalized SEG:OFF
+; Uses: none
+
+
+ public Lma2SegOff
+
+Lma2SegOff proc near
+
+ push dx
+ shl bx,12
+ shr dx,4
+ or bx,dx
+ pop dx
+ and dx,0fh
+
+ ret
+
+Lma2SegOff endp
+
+
+; -------------------------------------------------------
+; GetSegmentAddress -- This routine will return with
+; the linear address of the specified segment.
+;
+; Input: AX - segment selector
+; Output: DX - low word of segment address
+; BX - high word of segment address
+; Errors: none
+; Uses: BX, DX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public GetSegmentAddress
+
+GetSegmentAddress:
+ push es
+ push di
+
+ mov es,selGDT
+ mov di,ax
+ and di,SELECTOR_INDEX
+ mov dx,es:[di].adrBaseLow
+ mov bl,es:[di].adrBaseHigh
+ mov bh,es:[di].adrbBaseHi386
+
+ pop di
+ pop es
+ ret
+
+; -------------------------------------------------------
+; SetSegmentAddress -- This routine will modify the
+; segment base address of the specified segment.
+;
+; Input: AX - segment selector
+; Output: DX - low word of segment address
+; BX - high word of segment address
+; Errors: None
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public SetSegmentAddress
+
+SetSegmentAddress:
+ push si
+ push es
+
+ mov es,selGDT
+ mov si,ax
+ and si,SELECTOR_INDEX
+ mov es:[si].adrBaseLow,dx
+ mov es:[si].adrBaseHigh,bl
+ mov es:[si].adrbBaseHi386,bh
+IF 1 ; was IFDEF WOW, but I need this on MIPS too
+ push ax
+ push bx
+ push cx
+ mov ax,si
+ mov cx,1
+ mov bx,si
+IFDEF I386
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ELSE
+ DPMIBOP SetDescriptorTableEntries
+ENDIF
+ pop cx
+ pop bx
+ pop ax
+ENDIF
+ pop es
+ pop si
+ ret
+
+; -------------------------------------------------------
+; NSetSegmentAccess -- This routine will modify the
+; access rights byte of a specified segment.
+;
+; Input: Selector - segment selector
+; Access - Access rights byte value
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetSegmentAccess,<PUBLIC,NEAR>,<es,si>
+ parmW Selector
+ parmW Access
+cBegin
+ mov es,selGDT
+ mov si,Selector
+ and si,SELECTOR_INDEX
+ mov ax,Access
+ mov es:[si].arbSegAccess,al ; Set access byte.
+ and ah,0F0h ; Mask off reserved bits.
+ and es:[si].cbLimitHi386,0fh ; Clear old extended bits.
+ or es:[si].cbLimitHi386,ah ; Set new extended bits.
+IFDEF WOW
+ push ax
+ push bx
+ push cx
+ mov ax,si
+ mov cx,1
+ mov bx,si
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ pop cx
+ pop bx
+ pop ax
+ENDIF
+
+cEnd
+
+ifdef WOW
+
+; -------------------------------------------------------
+; NSetSegmentLimit -- This routine will modify the
+; limit of a specified segment.
+;
+; Input: Selector - segment selector
+; Limit - Limit value
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetSegmentLimit,<PUBLIC,NEAR>,<es,si>
+ parmW Selector
+cBegin
+ mov es,selGDT
+ mov si,Selector
+ and si,SELECTOR_INDEX
+ push ax
+ push bx
+ push cx
+ mov ax,si
+ mov cx,1
+ mov bx,si
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ pop cx
+ pop bx
+ pop ax
+cEnd
+
+; -------------------------------------------------------
+; NSetSegmentBase -- This routine will modify the
+; base address of a specified segment.
+;
+; Input: Selector - segment selector
+; Base - base address
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetSegmentBase,<PUBLIC,NEAR>,<es,si>
+ parmW Selector
+ parmD Base
+cBegin
+ mov es,selGDT
+ mov si,Selector
+ and si,SELECTOR_INDEX
+ mov ax,word ptr Base
+ mov es:[si].adrBaseLow,ax
+ mov ax,word ptr Base + 2
+ mov es:[si].adrbBaseMid386,al
+ mov es:[si].adrbBaseHi386,ah
+ clc
+ push ax
+ push bx
+ push cx
+ mov ax,si
+ mov cx,1
+ mov bx,si
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ pop cx
+ pop bx
+ pop ax
+
+cEnd
+
+; -------------------------------------------------------
+; NMoveDescriptor -- This routine copy a descriptor from
+; the source address to the destination address. This
+; can be to or from the descriptor table. If it copied
+; to the descriptor table, the system will be notified as
+; appropriate.
+;
+; Input: Source -- address of source descriptor
+; Dest -- address of destination descriptor
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+.386p
+cProc NMoveDescriptor,<PUBLIC,NEAR>,<es,esi,ds,edi,ebx,ecx>
+ ParmW SourceSel
+ ParmD SourceOff
+ ParmW DestSel
+ ParmD DestOff
+cBegin
+.386p
+ xor ecx,ecx
+ mov cx,SourceSel
+ mov ds,cx
+ mov esi,SourceOff
+ mov cx,DestSel
+ mov es,cx
+ mov edi,DestOff
+ mov cx,8
+ rep movs byte ptr [esi], byte ptr [edi]
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+ mov cx,es
+ cmp cx,ds:selGdt
+ jne nm20
+
+ mov cx,1
+ mov ebx,DestOff
+ mov ax,bx
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.386p
+nm20:
+cEnd
+.286p
+
+; -------------------------------------------------------
+; NWOWSetDescriptor --
+; The Descriptors are set on the system side.
+;
+; Input: Source -- address of source descriptors
+; Count -- count of descriptors to set
+; Output: none
+; Errors: Carry set, AX = error code
+; Uses: All registers preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+cProc NWOWSetDescriptor,<PUBLIC,NEAR>,<es,ds,bx,cx>
+ ParmW Count
+ ParmD Source
+cBegin
+ les bx, Source
+ mov cx, Count
+ mov ax, bx
+
+ mov ds,selDgroupPM
+ assume ds:DGROUP
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+cEnd
+endif
+; -------------------------------------------------------
+; ParaToLDTSelector -- This routine will convert a segment
+; address relative to the start of the exe file into the
+; corresponding selector for the segment. It searches the
+; LDT to see if a segment is already defined at that address.
+; If so, its selector is returned. If not, a new segment
+; descriptor will be defined.
+;
+; Note: The LDT and GDT are currently one and the same.
+;
+; Input: AX - paragraph aaddress of real mode segment
+; BX - access rights byte for the segment
+; Output: AX - selector for the segment
+; Errors: returns CY set if unable to define a new segment
+; Uses: AX, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public ParaToLDTSelector
+
+ParaToLDTSelector proc near
+
+ push bx
+ push cx
+ push dx
+
+; Convert the paragraph address to a linear address and see if there
+; is a segment defined at that address.
+
+ mov dx,ax
+ call FindLowSelector
+ jnc @f ;if so, we don't need to make one
+
+; This segment isn't defined, so we need to create a descriptor for it.
+
+ mov ax,dx
+ call MakeLowSegment
+
+if DEBUG ;------------------------------------------------------------
+ jnc ptos80
+ Debug_Out "ParaToLDTSelector: can't make selector!"
+ptos80:
+endif ;DEBUG --------------------------------------------------------
+
+ jc ptos90
+
+@@: or al,SELECTOR_TI or STD_RING ;look like LDT selector
+
+; All done
+
+ptos90: pop dx
+ pop cx
+ pop bx
+ ret
+
+ParaToLDTSelector endp
+
+ public FarParaToLDTSelector
+FarParaToLDTSelector proc far
+ call ParaToLDTSelector
+ ret
+FarParaToLDTSelector endp
+
+; -------------------------------------------------------
+ page
+; -------------------------------------------------------
+; DESCRIPTOR TABLE MANIPULATION ROUTINES
+; -------------------------------------------------------
+;
+; AllocateLDTSelector -- This function will obtain the
+; next free descriptor in the local descriptor table.
+;
+; Note: Currently the LDT and GDT are in the same table!
+;
+; Note: The function InitGlobalDscrTable must have been
+; called before calling this function.
+;
+; Input: none
+; Output: AX - selector if one is available
+; Errors: CY clear if successful, AX=0 and CY set if not free selectors
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AllocateLDTSelector
+
+AllocateLDTSelector proc near
+
+ call AllocateSelector ;get a GDT selector
+ jc @f
+
+ or al,SELECTOR_TI or STD_RING ;say it's in the LDT
+
+@@: ret
+
+AllocateLDTSelector endp
+
+
+; -------------------------------------------------------
+; AllocateSelector -- This function will obtain the
+; next free descriptor in the global descriptor table.
+; The descriptors in the GDT are stored on a linked list
+; of free descriptors. The cbLimit field of the descriptor
+; is used as the link to the next element of the list. In
+; addition, free descriptors have the access rights byte
+; set to 0.
+;
+; Note: The function InitGlobalDscrTable must have been
+; called before calling this function.
+;
+; Input: none
+; Output: AX - selector if one is available
+; Errors: CY clear if successful, AX=0 and CY set if not free selectors
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AllocateSelector
+
+AllocateSelector proc near
+
+; Get the next free descriptor on the list.
+
+ mov ax,selGDTFree ;get head of free list
+ or ax,ax ;is the list empty
+ jnz alsl20 ;if so, report error.
+ stc ;set error flag
+
+ Debug_Out "AllocateSelector: out of selectors!"
+
+ jmp short alsl90 ;and get out
+
+; We now need to update the new head of list to point to the
+; following one on the list.
+; Whenever we allocate a descriptor, the access rights byte is set
+; to 0Fh. This marks it as a '386 task gate, which is not legal to
+; have in the GDT. We need to stick something in this byte, because
+; having the access rights byte be 0 means that it is free, which is
+; no longer the case.
+
+alsl20:
+
+if DEBUG ;------------------------------------------------------------
+ test al,not SELECTOR_INDEX
+ jz @f
+ Debug_Out "AllocateSelector: selGDTFree invalid! #AX"
+ and al,SELECTOR_INDEX
+@@:
+endif ;DEBUG --------------------------------------------------------
+
+ push bx
+ push es
+ mov bx,ax ;BX points to the descriptor
+ mov es,selGDT ;ES points to the GDT segment
+ push es:[bx].cbLimit ;push the link field of the descriptor
+ pop selGDTFree ;make it be the new head of the list
+
+ mov es:[bx].adrbBaseHi386,0
+ mov es:[bx].arbSegAccess,0Fh
+ pop es
+ pop bx
+
+ or al,SELECTOR_TI
+ ;
+ ; Remember the highest selector alloced, so we can reinit the ldt
+ ;
+ cmp ax,HighestSel
+ jbe alsl50
+
+ mov HighestSel,ax
+alsl50: clc
+
+;
+; All done
+alsl90: ret
+
+AllocateSelector endp
+
+
+; -------------------------------------------------------
+; AllocateSelectorBlock -- This function will allocate
+; a set of contiguous descriptors from the global
+; descriptor table. It will search the GDT from the
+; beginning looking for a sufficiently large contiguous
+; set of descriptors. When if finds a set, it will
+; then remove each one from the free descriptor list.
+;
+; Input: AX - Number of selectors needed
+; Output: AX - starting selector of the block
+; Errors: return CY set if error occurs
+; Uses: AX modified, all other registers preserved
+; modifies global descriptor table free list
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public AllocateSelectorBlock
+
+AllocateSelectorBlock proc near
+
+ push cx
+ push dx
+ push si
+ push di
+ push es
+
+ mov es,selGDT
+ mov dx,ax ;remember descriptor count in DX
+ lsl di,selGDT ;stop search at table limit
+
+; Start at the first user selector and search until we find a
+; sufficiently large block of contiguous selectors.
+
+ mov si,SEL_USER
+alsb20: mov ax,si ;remember the starting one
+ mov cx,dx ;number of descriptors to check
+if 0
+alsb30: cmp es:[si].arbSegAccess,0 ;is this one free
+else
+alsb30: cmp word ptr es:[si].arbSegAccess,0 ;is this one free
+endif
+ jnz alsb40 ;if not, we have to continue looking
+ add si,8 ;bump to next descriptor
+ cmp si,di ;check if at end of table
+ jae alsb80 ;if so, get out with error
+ loop alsb30 ;repeat for the number of selectors requested
+ jmp short alsb50;we found the block
+
+; This one wasn't free, try the next one
+
+alsb40: add si,8 ;bump to next descriptor
+ cmp si,di ;are we at the end of the table.
+ jc alsb20 ;and repeat if not
+ jmp short alsb80;we didn't find it, so report error
+
+; AX has the starting selector of the block of descriptors. We need
+; to remove each of them from the free list.
+
+alsb50: push ax ;remember the starting point
+ mov cx,dx ;get descriptor count
+ mov dx,ax ;remember current selector number
+alsb52: mov ax,dx
+ call RemoveFreeDescriptor
+ add dx,8
+ loop alsb52
+ ;
+ ; Remember the highest number
+ ;
+ or dl,SELECTOR_TI
+ cmp dx,HighestSel
+ jbe alsb60
+
+ mov HighestSel,dx
+alsb60: pop ax ;restore starting selector
+
+ or al,SELECTOR_TI
+
+ clc
+ jmp short alsb90
+
+; Couldn't find them, so return error.
+
+alsb80: stc
+
+ Debug_Out "AllocSelectorBlock: Failed!"
+
+alsb90: pop es
+ pop di
+ pop si
+ pop dx
+ pop cx
+ ret
+
+AllocateSelectorBlock endp
+
+
+; -------------------------------------------------------
+; RemoveFreeDescriptor -- This routine will remove the
+; specified descriptor from the free descriptor list
+; of the GDT.
+;
+; Input: AX - selector of the descriptor to remove
+; ES - segment address of GDT
+; Output: none
+; Errors: none
+; Uses: AX used, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public RemoveFreeDescriptor
+
+RemoveFreeDescriptor proc near
+
+ push si
+ push di
+
+ and ax,SELECTOR_INDEX ;clear table/pl bits
+
+; Check that the segment is really free.
+
+ mov si,ax ;Segment index
+if 0
+ cmp es:[si].arbSegAccess,0 ; Should be 0 if free
+else
+ cmp word ptr es:[si].arbSegAccess,0 ; Should be 0 if free
+endif
+ jnz rmfd80 ;Error if segment is not free!
+;
+ xor di,di
+ mov si,selGDTFree ;start at the head of the list.
+
+; Look for a selector matching the one to free
+
+rmfd20: or si,si ;check for end of list.
+ jz rmfd90 ;and get out if so
+ cmp ax,si ;is this the one we are looking for
+ jz rmfd40
+ mov di,si
+ mov si,es:[si].cbLimit ;point SI at next one in the list
+ jmp rmfd20 ;and repeat
+
+; We found the one we want, so now remove it from the list.
+
+rmfd40: mov es:[si].adrbBaseHi386,0
+ mov es:[si].arbSegAccess,0Fh
+ mov ax,es:[si].cbLimit
+ or di,di ;is it the head of the list
+ jz rmfd50
+
+; The one we have isn't the head of the list, so make the previous
+; list element point at the one beyond the one being removed.
+
+ mov es:[di].cbLimit,ax
+ jmp short rmfd90
+
+; The one we have is the head of the list. Make the head of the list
+; point to the one following the one being removed.
+
+rmfd50:
+ mov selGDTFree,ax
+ jmp short rmfd90
+
+rmfd80: stc ;Flag allocation error
+
+ Debug_Out "RemoveFreeDescriptor: Failed!"
+
+rmfd90: pop di
+ pop si
+ ret
+
+RemoveFreeDescriptor endp
+
+; -------------------------------------------------------
+; IsSelectorFree -- This routine determines if the specified
+; selector is on the free list. It is used to prevent
+; apps from corrupting the free list by doing things like
+; set selector on a descriptor in the free list.
+;
+; Input: BX - Descriptor index
+; Output: CY - set if decriptor is not free
+; clear otherwise
+;
+ assume ds:dgroup,es:nothing,ss:nothing
+ public IsSelectorFree
+IsSelectorFree proc near
+
+ push es
+ push si
+
+ mov si,selGdt
+ mov es,si
+
+ test byte ptr es:[bx].adrbBaseHi386,080h
+ jnz isf30
+
+ stc
+ jmp isf40
+
+isf30: clc
+isf40: pop si
+ pop es
+ ret
+
+IsSelectorFree endp
+
+
+; -------------------------------------------------------
+; FreeSelector -- This routine will mark the segment
+; descriptor for the specified selector as free. This
+; is used to release a temporary selector when no longer
+; needed. The descriptor is marked as free by setting the
+; access rights byte to 0 and placing it on the free list.
+;
+; Note: This routine can only be called in protected mode.
+;
+; Input: AX - selector to free
+; Output: none
+; Errors: CY clear if no error, set if selector is invalid
+; Uses: AX used, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FreeSelector
+
+FreeSelector proc near
+ push bx
+ push es
+
+; Check for this being a valid selector.
+
+ cmp ax,SEL_USER ;make sure it is a user selector
+ jb frsl80
+ mov bx,SEL_LDT_ALIAS
+ mov es,selGDT
+ mov bx,es
+ lsl bx,bx
+ cmp ax,bx ;make sure it is in the range of the table
+ jnc frsl80
+
+; We have a legitimate selector, so set the access rights byte to 0, and
+; place it at the head of the free list.
+
+ mov bx,ax
+ and bx,SELECTOR_INDEX ;clear unwanted bits
+
+if 0
+ cmp es:[bx].arbSegAccess,0 ;already marked as free?
+else
+ cmp word ptr es:[bx].arbSegAccess,0 ;already marked as free?
+endif
+ jz frsl80 ; yes, don't free it again!
+
+if 0
+ mov es:[bx].arbSegAccess,0
+else
+ mov word ptr es:[bx].arbSegAccess,0
+ mov byte ptr es:[bx].adrbBaseHi386,080h
+endif
+ mov ax,selGDTFree ;pointer to current head of list
+ mov es:[bx].cbLimit,ax ;store in link field of this dscr.
+ mov selGDTFree,bx ;make this one be the head of list.
+if 0
+BUGBUG fix this
+IF 1 ; DaveHart was IFDEF WOW, need it for MIPS too
+ int 3; debugbug
+ push ax
+ push bx
+ push cx
+ mov ax,bx
+ mov bx,offset NullSel
+ push ds
+ pop es
+ mov cx,1
+IFDEF I386
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ELSE
+ DPMIBOP SetDescriptorTableEntries
+ENDIF
+ pop cx
+ pop bx
+ pop ax
+ENDIF
+endif
+ clc
+ jmp short frsl90
+
+; Bogus selector given. Return error.
+
+frsl80: stc
+
+ Debug_Out "FreeSelector failed, #AX invalid or not used!?"
+
+frsl90: pop es
+ pop bx
+frsl99: ret
+
+FreeSelector endp
+
+; -------------------------------------------------------
+; FreeSelectorBlock -- This routine will free the specified
+; range of segment descriptors in the global descriptor
+; table.
+;
+; Input: AX - starting selector in the range
+; CX - number of selectors to free
+; Output: none
+; Errors: returns CY set if error occurs
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FreeSelectorBlock
+
+FreeSelectorBlock proc near
+
+ jcxz frsb99
+ push cx
+frsb20: push ax
+ call FreeSelector
+ pop ax
+ jc frsb90
+ add ax,8
+ loop frsb20
+frsb90: pop cx
+frsb99: ret
+
+FreeSelectorBlock endp
+
+
+; -------------------------------------------------------
+; FindLowSelector -- This function will search the global
+; descriptor table for a descriptor matching the given
+; address.
+;
+; Input: AX - real mode paragraph to search for
+; BX - access rights byte for the segment
+; Output: AX - selector corresponding to input paragraph address
+; Errors: returns CY set if specified descriptor not found
+; Uses: AX, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FindLowSelector
+
+FindLowSelector:
+ push bx
+ push dx
+;
+ mov dx,ax
+ push bx
+ call ParaToLinear
+ pop ax
+ mov bh,al
+ call FindSelector
+;
+ pop dx
+ pop bx
+ ret
+
+if 0
+; -------------------------------------------------------
+; FindLDTSelector -- This function will search the local
+; descriptor table for a segment descriptor matching
+; the specified linear byte address.
+;
+; Note: The LDT and GDT are currently one and the same!
+;
+; Input: DX - low word of linear byte address
+; BL - high byte of linear address
+; BH - access rights byte for the segment
+; Output: AX - selector of corresponding segment
+; Errors: returns CY set if specified descriptor not found
+; Uses: AX used, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FindLDTSelector
+
+FindLDTSelector proc near
+
+ call FindSelector
+ jc @f
+
+ or al,SELECTOR_TI or STD_RING ;say it's in the LDT
+
+@@: ret
+
+FindLDTSelector endp
+endif
+
+; -------------------------------------------------------
+; FindSelector -- This function will search the global
+; descriptor table for a segment descriptor matching
+; the specified linear byte address.
+;
+; Note that this routine cannot be used to find
+; selectors pointing to addresses above 16 Megabytes.
+; This is not really a problem, since the routine
+; is used to find selectors in real mode DOS land
+; most of the time.
+;
+; Input: DX - low word of linear byte address
+; BL - high byte of linear address
+; BH - access rights byte for the segment
+; Output: AX - selector of corresponding segment
+; Errors: returns CY set if specified descriptor not found
+; Uses: AX used, all other registers preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public FindSelector
+
+FindSelector proc near
+
+ push si
+ push di
+ push es
+ push cx
+
+; Get segment limit of the GDT to use as a limit on the search.
+
+ lsl di,selGDT
+ mov es,selGDT
+
+; Look for a descriptor matching the address in BL:AX
+
+ mov si,SEL_USER ;search starting here
+if 0
+fnds20: cmp es:[si].arbSegAccess,0
+else
+fnds20: cmp word ptr es:[si].arbSegAccess,0
+endif
+ jz fnds28 ;skip if unused descriptor
+ cmp bl,es:[si].adrBaseHigh
+ jnz fnds28
+ cmp dx,es:[si].adrBaseLow
+ jnz fnds28
+if 0
+ cmp es:[si].cbLimit,0
+ jz fnds28 ;skip if dscr has 0 limit
+else
+ cmp es:[si].cbLimit,0ffffh
+ jnz fnds28 ;skip unless dscr has 64k limit
+endif
+ mov cl,bh
+ xor cl,es:[si].arbSegAccess
+ and cl,NOT AB_ACCESSED
+ jz fnds90
+fnds28: add si,8 ;bump to next descriptor
+ jc fnds80
+ cmp si,di ;check against end of GDT
+ jc fnds20 ;if still less, continue on.
+
+; Hit the end of the GDT and didn't find one. So return error.
+
+fnds80: stc
+ jmp short fnds99
+
+; We found it, so return the selector
+
+fnds90: mov ax,si
+fnds99: pop cx
+ pop es
+ pop di
+ pop si
+ ret
+
+FindSelector endp
+
+
+
+; -------------------------------------------------------
+; DupSegmentDscr -- This function will duplicate the specified
+; segment descriptor into the specified destination descriptor. The
+; end result is a second segment descriptor pointing to the same place
+; in memory as the first.
+;
+; Input: AX - selector of segment descriptor to duplicate
+; BX - selector of the segment descriptor to receive duplicate
+; Output: none
+; Errors: none
+; Uses: All registers preserved. Modifies the segment
+; descriptor for the specified segment. If this selector happens
+; to be in a segment register when this routine is called, that
+; segment register may end up pointing to the new location.
+
+ assume ds:DGROUP,es:NOTHING
+ public DupSegmentDscr
+
+DupSegmentDscr proc near
+
+ push cx
+ push si
+ push di
+ push ds
+ push es
+
+ mov si,ax
+ mov di,bx
+ and si,SELECTOR_INDEX
+ and di,SELECTOR_INDEX
+ mov es,selGDT
+ mov ds,selGDT
+ assume ds:NOTHING
+ mov cx,4
+ cld
+ rep movs word ptr [di],word ptr [si]
+
+ pop es
+ pop ds
+ pop di
+ pop si
+ pop cx
+ ret
+
+DupSegmentDscr endp
+
+IFDEF ROM
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+ENDIF
+
+; -------------------------------------------------------
+; NSetSegmentDscr -- This function will initialize the
+; specified descriptor table entry with the specified data.
+;
+; This function can be called in real mode or protected mode.
+;
+; Input:
+; Param1 - WORD segment selector
+; Param2 - DWORD 32-bit segment base address
+; Param3 - DWORD 32-bit segment limit
+; param4 - WORD segment access/type
+; Output: returns selector for the segment
+; Errors: none
+; Uses: Flags
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetSegmentDscr,<PUBLIC,FAR>,<es,di,ax,bx>
+ parmW Selector
+ parmD Base
+ parmD Limit
+ parmW Access
+cBegin
+ mov es,selGDT
+ mov di,Selector
+ and di,SELECTOR_INDEX
+
+ mov ax,off_Base ; Set segment base
+ mov es:[di].adrBaseLow,ax
+ mov ax,seg_Base
+ mov es:[di].adrBaseHigh,al
+ mov es:[di].adrbBaseHi386,ah
+
+ mov ax,word ptr Access
+ and ax,070ffh ; clear 'G' bit and
+ ; extended limit bits
+ mov word ptr es:[di].arbSegAccess,ax
+ ; set access
+ mov ax,seg_Limit
+ mov bx,off_Limit ; AX:BX = segment limit
+ test ax,0fff0h ; big?
+ jz ssd_0 ; No
+ shr bx,12d ; Yes, make it page granular.
+ shl ax,4d
+ or bx,ax
+ mov ax,seg_Limit
+ shr ax,12d
+ or al,080h ; set 'G' bit
+ssd_0:
+ or es:[di].cbLimitHi386,al ; set high limit
+ mov es:[di].cbLimit,bx ; set low limit
+IF 1; DaveHart was IFDEF WOW, I need it on MIPS too
+ push ax
+ push bx
+ push cx
+ mov ax,di
+ mov cx,1
+ mov bx,di
+IFDEF I386
+.386p
+ FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
+.286p
+ELSE
+ DPMIBOP SetDescriptorTableEntries
+ENDIF
+ pop cx
+ pop bx
+ pop ax
+ENDIF
+cEnd
+
+ifndef WOW
+; -------------------------------------------------------
+; NSetGDTSegmentDscr -- This function will initialize the
+; specified descriptor table entry with the specified data.
+;
+; This function can be called in real mode or protected mode.
+;
+; Input:
+; Param1 - WORD segment selector
+; Param2 - DWORD 32-bit segment base address
+; Param3 - DWORD 32-bit segment limit
+; param4 - WORD segment access/type
+; Output: returns selector for the segment
+; Errors: none
+; Uses: Flags
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+cProc NSetGDTSegmentDscr,<PUBLIC,FAR>,<es,di,ax,bx>
+ parmW Selector
+ parmD Base
+ parmD Limit
+ parmW Access
+cBegin
+ mov ax,SEL_GDT
+ mov es,ax
+ mov di,Selector
+ and di,SELECTOR_INDEX
+
+ mov ax,off_Base ; Set segment base
+ mov es:[di].adrBaseLow,ax
+ mov ax,seg_Base
+ mov es:[di].adrBaseHigh,al
+ mov es:[di].adrbBaseHi386,ah
+
+ mov ax,word ptr Access
+ and ax,070ffh ; clear 'G' bit and
+ ; extended limit bits
+ mov word ptr es:[di].arbSegAccess,ax
+ ; set access
+ mov ax,seg_Limit
+ mov bx,off_Limit ; AX:BX = segment limit
+ test ax,0fff0h ; big?
+ jz @f ; No
+ shr bx,12d ; Yes, make it page granular.
+ shl ax,4d
+ or bx,ax
+ mov ax,seg_Limit
+ shr ax,12d
+ or al,080h ; set 'G' bit
+@@:
+ or es:[di].cbLimitHi386,al ; set high limit
+ mov es:[di].cbLimit,bx ; set low limit
+cEnd
+endif ; WOW
+
+
+IFDEF ROM
+; -------------------------------------------------------
+
+DXCODE ends
+
+; -------------------------------------------------------
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+ENDIF
+
+; -------------------------------------------------------
+; MakeLowSegment -- This function will create a segment
+; descriptor for the specified low memory paragraph address.
+; The segment length will be set to 64k. The difference
+; between this and MakeScratchSelector is that this function
+; allocates a new segment descriptor in the user area of
+; the global descriptor table, thus creating a more or less
+; permanent selector. MakeScratchSelector always uses the
+; same descriptor location in the descriptor table, thus
+; creating a very temporary selector.
+;
+; Input: AX - paragraph address in low memory
+; BX - access rights word for the segment
+; Output: AX - selector to use to access the memory
+; Errors: returns CY clear if no error, CY set if unable to
+; allocate a segment descriptor
+; Uses: AX used, all else preserved
+
+ assume ds:DGROUP,es:NOTHING,ss:NOTHING
+ public MakeLowSegment
+
+MakeLowSegment proc near
+
+; We need to allocate a segment descriptor, convert the paragraph address
+; to a linear byte address, and then initialize the allocated segment
+; descriptor.
+
+ push dx
+ push cx
+
+ mov cx,bx
+ mov dx,ax ;paragraph address to DX
+ call AllocateSelector ;get a segment descriptor to use
+ jc mksl90 ;get out if error
+ call ParaToLinear
+ cCall NSetSegmentDscr,<ax,bx,dx,0,0FFFFh,cx>
+
+ clc
+ pop cx
+mksl90: pop dx
+ ret
+
+MakeLowSegment endp
+
+; -------------------------------------------------------
+; 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.
+;
+; Input: DX - paragraph address
+; Output: DX - lower word of linear address
+; BX - high word of linear address
+; Errors: none
+; Uses: DX, BL used, all else preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public ParaToLinear
+
+ParaToLinear proc near
+
+ mov bl,dh
+ shr bl,4
+ shl dx,4
+ xor bh,bh
+ ret
+
+ParaToLinear endp
+
+
+; -------------------------------------------------------
+; MoveMemBlock -- This routine will copy a block
+; from one place to another. It copies from the bottom
+; up, so if the address ranges overlap it can only be
+; used to copy down.
+;
+; Input: BX - selector of GDT
+; CX - low word of block length
+; DX - high word of block length
+; DS - selector pointing to source address
+; ES - selector pointing to destination address
+; Output: none
+; Errors: none
+; Uses: modifies segment descriptors selected by ES and DS
+; AX used, other registers preserved
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public MoveMemBlock
+
+MoveMemBlock:
+ cld
+ push cx
+ push dx
+ push si
+ push di
+;
+mvsd30: xor si,si
+ mov di,si
+ or dx,dx ;is there more than 64k left to move
+ jz mvsd32 ;if not, move the amount left
+ dec dx
+ push cx
+ mov cx,8000h ;move 64k bytes this time
+ rep movs word ptr [di],word ptr [si]
+ pop cx
+ jmp short mvsd36
+mvsd32: jcxz mvsd90
+ shr cx,1
+ rep movs word ptr [di],word ptr [si]
+ jnc mvsd90
+ movs byte ptr [di],byte ptr [si]
+ jmp short mvsd90
+;
+; There is still something to move, so bump the segment descriptors to
+; point to the next chunk of memory and repeat.
+mvsd36:
+ mov di,es
+ push di
+ and di,SELECTOR_INDEX
+ mov es,bx
+ inc es:[di].adrBaseHigh ;bump the destination segment to
+ pop di ; the next 64k boundary
+
+ mov si,ds
+ push si
+ and si,SELECTOR_INDEX
+ inc es:[si].adrBaseHigh ;bump the source segment to the
+ pop si ; next 64k boundary
+
+ mov ds,si
+ mov es,di
+ jmp mvsd30
+;
+; All done
+mvsd90: pop di
+ pop si
+ pop dx
+ pop cx
+ ret
+
+; -------------------------------------------------------
+ subttl Utility Routines
+ page
+; -------------------------------------------------------
+; UTILITY ROUTINES
+; -------------------------------------------------------
+
+BeginLowSegment
+
+; -------------------------------------------------------
+if DEBUG
+; MemCopy -- This routine is for use in a debugger to
+; copy a block of extended memory down to real mode
+; memory so that it can be looked at. The data is
+; copied to rgbXfrBuf1. (This buffer is 4k bytes in
+; size, so don't copy more than this amount)
+;
+; Input: CX - Number of bytes to copy
+; DS:SI - protected mode address of start of copy
+; Output; none
+; Errors: none
+; Uses: all registers trashed.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public MemCopy
+
+MemCopy:
+ in al,INTA01
+ mov dl,al
+ mov al,0FFh
+ out INTA01,al
+ push ds
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,segDXData
+ENDIF
+ call EnterProtectedMode
+ pop ds
+ push SEL_DXDATA
+ pop es
+ mov di,offset DGROUP:rgbXfrBuf1
+ cld
+ rep movsb
+IFDEF ROM
+ push SEL_DXDATA OR STD_RING
+ pop ds
+ELSE
+ mov ds,selDgroup
+ENDIF
+ call EnterRealMode
+ mov al,dl
+ out INTA01,al
+ int 3
+
+endif
+
+; -------------------------------------------------------
+
+EndLowSegment
+
+;
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+
+DXPMCODE ends
+
+;
+;****************************************************************
+
+ end
diff --git a/private/mvdm/dpmi/dxvcpi.asm b/private/mvdm/dpmi/dxvcpi.asm
new file mode 100644
index 000000000..b9d9a0308
--- /dev/null
+++ b/private/mvdm/dpmi/dxvcpi.asm
@@ -0,0 +1,586 @@
+ PAGE ,132
+ TITLE DXVCPI.ASM -- Dos Extender VCPI Support Code
+
+; Copyright (c) Microsoft Corporation 1990-1991. All Rights Reserved.
+
+;*** dxvcpi.asm - vcpi detection/maintenance/cleanup code (resident)
+;
+; Copyright <C> 1990, Microsoft Corporation
+;
+; Purpose:
+;
+; Revision History:
+;
+;
+; 08-07-90 earleh Allow program to boot without LIM 3.2 page frame.
+; 05/07/90 jimmat Started incorporating VCPI changes from languages group.
+;
+; [] 20-Feb-1990 Dans Created
+;
+;************************************************************************/
+
+.286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+.xlist
+.sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+.list
+
+; This entire file is only for VCPI support
+
+if VCPI
+
+.xlist
+.sall
+include dxvcpi.inc
+.list
+;
+; miscellaneous equates
+;
+;
+; also in dxmmgr.asm
+;
+.ERRE CB_MEMHDR EQ 10h ;size of memory block header
+;
+; also in dxmmgr.asm
+;
+;
+; data
+;
+
+DXDATA segment
+
+;
+; Externs
+;
+
+extrn idCpuType:word
+extrn bpGDT:fword
+
+extrn cKbInitialHeapSize:word
+extrn cbHeapSize:dword
+extrn dsegCurrent:word
+extrn hmemHeap:word
+extrn lpfnXMSFunc:DWORD
+
+extrn selGDT:WORD
+;
+; Definitions, data
+;
+
+public fEms
+fEMS db 0 ; ems present
+
+public fVCPI
+fVCPI db 0 ; vcpi present
+
+public iddxsystype
+iddxsystype db DXINDOS ; type of memory used to hold
+ ; final dx Pmode data.
+public segBootPmode
+segBootPmode dw 0 ; segment of block used to
+ ; build system tables in real
+align 4
+
+public cFreePages
+cFreePages dd 0 ; free 4k pages, updated on each
+ ; allocation.
+public bpdxsyspages
+bpdxsyspages dd DXPMPAGES-1 dup(0)
+;
+; pointer (off of SEL_DXPT) where next free page table entry is
+;
+; we initialize dxpt1 to have cptdx+2 entries in it (see
+; SetupPageTables in dxvcpibt.asm)
+;
+
+;
+; Offset in zeroth page table of first page table entry that belongs
+; to DX. Set by initial call to GetVCPIInterface.
+;
+public pPteFirst
+pPteFirst dd 0
+
+;
+; Offset in block pointed to by SEL_DXPT of where to put next page
+; table entry when allocating extended memory by the page.
+;
+public pPteNext
+pPteNext dd 0
+
+;
+; Maximum number of user page table entries that will fit in our
+; original page table buffers.
+;
+public cPteMax
+cPteMax dd 0
+
+EXTRN hmem_XMS_Table:WORD
+EXTRN hmem_XMS_Count:WORD
+
+public hmem_System_Block
+hmem_System_Block dw 0
+
+DXDATA ends
+
+
+DXPMCODE segment
+
+IFDEF ROM
+ %OUT VCPI Support not compatible with ROM! Code segment variables!
+ .ERR
+ENDIF
+
+;
+; Data set up in real mode prior to relocation of DX into vcpi
+; (read only after init time anyway)
+;
+
+public fnVCPIPM, fnVCPIPMoff
+
+fnVCPIPM label fword ; vcpi interface entrypoint
+fnVCPIPMoff dd 0 ; set up in GetVCPIInterface
+ dw SEL_VCPI ; we know what this is.
+
+externFP NSetSegmentDscr
+ extrn XMScontrol:FAR
+
+pmxmssvc macro fcn
+ ifnb <fcn>
+ mov ah, fcn
+ endif
+ call XMScontrol
+endm
+
+DXPMCODE ends
+
+DXCODE segment
+;
+; Data set up in real mode prior to relocation of DX into vcpi
+; (read only after init time anyway)
+;
+
+public WdebVCPI, fnVCPI, fnVCPIoff
+
+;--------- WdebVCPIInfo structure -----------
+
+public laVTP, V86ToPm
+
+;
+; Begin WDEB386 VCPI notification structure. The following variables
+; are copied by WDEB386 when we send it a VCPI presence notification,
+; just after entering protected mode. The structure of this block of variables
+; must not be changed without also changing the format of the data
+; structure used by WDEB386.
+;
+;--------- WdebVCPIInfo structure -----------
+WdebVCPI LABEL BYTE
+
+fnVCPI label fword ; vcpi interface entrypoint
+fnVCPIoff dd 0 ; set up in GetVCPIInterface
+ dw SEL_VCPI ; we know what this is.
+
+ dw SEL_VCPIALLMEM ; for Wdeb386 information
+
+laVTP dd 0 ; linear address of next structure
+;
+; End WDEB386 VCPI notification structure.
+;
+
+; Structure for switching from v86 mode to protect mode via VCPI
+
+ EXTRN epmVCPI:BYTE
+
+V86ToPm VTP <,,, SEL_LDT, SEL_TSS, OFFSET epmVCPI, 0, SEL_DXCODE0>
+
+ externFP AddXMStoVCPIHeap
+
+DXCODE ends
+
+
+DXCODE segment
+
+ assume cs:DXCODE, ds:DXDATA, es:nothing
+
+
+;*** FreeEMSHandle - free DX system memory
+;
+; Purpose: free the VCPI pages OR XMS block we allocated for system use
+;
+; Register
+; Usage: eax, edx, si, cx
+;
+; Input: none
+;
+; Output: VCPI DOS Extender system pages freed
+; XMS handle deallocated
+;
+; Returns: nothing
+;
+; Exceptions: No operation if none of these entities have been
+; allocated yet
+;
+; Notes:
+;
+;************************************************************************/
+cProc FreeEMSHandle,<NEAR,PUBLIC,<>
+cBegin
+ lea si,bpdxsyspages ; Load VCPI system pages array.
+ mov cx,DXPMPAGES-1 ; (First page was in PSP block.)
+ cld
+.386
+FreeVCPI_syspage:
+ lodsd ; fetch next page
+ or eax,eax ; page present?
+ jz FreeVCPI_Done ; No, could have been XMS.
+ and ax,0f000h ; Yes, clear lower 12 bits.
+ mov edx,eax
+ RMvcpi vcpiFREEPAGE ; and free it.
+ loop FreeVCPI_syspage ; loop until all freed
+.286p
+FreeVCPI_Done:
+ test iddxsystype,DXINXMS ; loaded in XMS?
+ jz FreeEMSHandle_ret ; No.
+ mov dx,hmem_System_Block ; Yes, load block
+ xmssvc 0dh ; unlock XMS block
+ mov dx,hmem_System_Block
+ xmssvc 0ah ; free XMS block
+FreeEMSHandle_ret:
+cEnd
+
+DXCODE ends
+
+;**************************************************************
+; 386 only code from here on down!!!
+;**************************************************************
+.386p
+
+include prot386.inc
+
+DXCODE segment
+
+ assume cs:DXCODE, ds:DXDATA, es:nothing
+
+DXCODE ends
+
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+;**************************************************************
+;*** CallVCPIPM
+;
+; Utility routine to call VCPI server in protected mode. Masks out
+; interrupts during the call because QEMM enables the processor
+; interrupt flag when you call it.
+;
+; Entry: AX = VCPI function code.
+; Uses: Depends upon call.
+;
+; Note: There is a copy of this routine in dxvcpi.asm and another
+; in dxvcpibt.asm. This is to allow near calls. The copy
+; in dxvcpibt.asm is discarded after initialization time.
+;
+;**************************************************************
+cProc CallVCPIPM,<NEAR>,<si>
+cBegin
+
+ push ax ; save function code
+;
+; Shut out all interrupts.
+; QEMM 5.0 enables interrupts during this call. All our interrupt
+; handlers are in the user code ring. A workaround is to shut off
+; hardware interrupts during the call.
+;
+
+ in al,INTA01
+ IO_Delay
+ mov si, ax
+ mov al,0FFh
+ out INTA01,al
+ IO_Delay
+
+ pop ax ;restore function code
+ db 9Ah ;call far SEL_CALLVCPI:0
+ dw 0,SEL_CALLVCPI or STD_RING
+
+; Restore the state of the interrupt mask register
+
+ xchg si, ax
+ out INTA01,al
+ IO_Delay
+ xchg si, ax
+cEnd
+
+;************************************************************************/
+;*** AllocVCPIMem
+;
+; Purpose: to allocate a block of memory from VCPI
+;
+; Register
+; Usage: eax, ebx, edx, ecx, es
+;
+; Input: ECX has number of 4k pages to allocate
+; ES:EDI points to page table entries to fill.
+;
+; Output: pPteNext updated with next free pte
+; cFreePages updated with number of free 4k pages from vcpi
+;
+; Returns: if success, linear ptr in eax
+; if fail, eax 0, ebx has number of 4k pages available.
+;
+; Exceptions:
+;
+; Notes: maximum allocation is 65535 4k pages (more than enough)
+; at one time.
+; Also, this is PROTECT MODE ONLY.
+;
+;************************************************************************/
+cProc AllocVCPIMem,<NEAR,PUBLIC>,<bx,dx>
+cBegin
+ ;
+ ; Compute the number of entries free in our page table
+ ;
+ mov edx, cPteMax
+ cmp ecx, edx ; compare request with PTEs
+ jb @F
+ ;
+ ; our page tables have less room than the vcpi server can allocate,
+ ; so adjust our count downward to reflect that
+ ;
+ mov ecx, edx
+@@:
+ cmp ecx, cFreePages ; compare request with pages we are
+ ; allowed to allocate
+ jb @F ; request < max.?
+ mov ecx, cFreePages ; No, clip.
+@@:
+ jecxz AVM_exit ; ECX = pages to allocate
+
+AVM_getpage:
+
+ PMvcpi vcpiALLOCPAGE
+
+ or ah, ah
+ jnz AVM_exit ; something happened...not as much
+ ; as vcpi said was there.
+ dec cPteMax ; fix up free PTEs
+ dec cFreePages ; and free VCPI pages
+;
+; make it a page table entry, and store into page table
+; don't need to worry about the tlb here, since not-present
+; pages are not cached in the tlb.
+;
+ or dx, NEWPTEMASK
+ mov eax, edx
+ stos dword ptr es:[edi]
+
+ dec ecx
+ jnz AVM_getpage ; next allocate
+
+AVM_exit:
+cEnd
+
+;************************************************************************/
+;*** VCPISpace
+;
+; Purpose: Return maximum possible VCPI memory allocation.
+;
+; Uses:
+;
+; Input:
+;
+; Output:
+;
+; Return: EAX = maximum possible VCPI pages we can get.
+;
+; Exceptions:
+;
+; Notes:
+;
+;************************************************************************/
+cProc VCPISpace,<NEAR,PUBLIC>,<edx>
+cBegin
+
+ PMvcpi vcpiCFREEPAGES ; EDX = free VCPI pages
+ cmp edx,cFreePages ; clip to maximum EMS allocation
+ jb VS_00
+ mov edx,cFreePages
+VS_00:
+ mov eax,cPteMax ; clip to space in page tables
+ cmp edx,eax
+ jb VS_01
+ mov edx,eax
+VS_01:
+ mov eax,edx
+cEnd
+
+;************************************************************************/
+;*** FreeVCPIHeap
+;
+; Purpose: To free the Extended Memory heap memory.
+;
+; Register
+; Usage: eax, ebx, ecx, edx
+;
+; Input:
+;
+; Output: All VCPI pages allocated for the heap are freed.
+; All XMS blocks allocated for the heap are freed.
+; Page table entries are set to zero.
+;
+;
+; Returns: nothing
+;
+; Exceptions: none
+;
+; Notes: Protect mode only
+;
+;************************************************************************/
+cProc FreeVCPIHeap,<NEAR,PUBLIC>,<es,edi,si,eax,edx>
+cBegin
+
+ mov ax, SEL_DXPT ; set up es with the selector
+ mov es, ax ; for our page tables
+ mov edi, pPteFirst ; point to first page allocated
+ mov ecx, pPteNext ; point to first unallocated PTE
+ sub ecx, edi
+ jbe Free_XMS_Handles
+ shr ecx, 2 ; ECX = pages to free
+
+startloop:
+ mov edx, es:[edi] ; get pte into edx
+ and dx, 0f000h ; mask off 12 lsb's
+
+ PMvcpi vcpiFREEPAGE ; free the page
+
+ xor eax, eax
+ stos dword ptr es:[edi] ; clear out PTE
+ dec ecx
+ jnz startloop
+@@:
+Free_XMS_Handles:
+ lea si, hmem_XMS_Table ; si points to XMS handle array
+ mov cx,[hmem_XMS_Count]
+ jcxz No_XMS_Handles
+
+Free_XMS_Handles_Loop:
+ mov dx,[si]
+ pmxmssvc 0dh ; unlock any XMS blocks
+ mov dx,[si]
+ pmxmssvc 0ah ; free any XMS blocks
+ add si,2 ; point to next slot in handle array
+ loop Free_XMS_Handles_Loop ; loop if more handle slots
+
+No_XMS_Handles:
+
+cEnd
+
+AddXMStoVCPIHeapCall label dword
+ dw offset dxcode:AddXMStoVCPIHeap,SEL_DXCODE or STD_RING
+
+;*** AddVCPIHeap
+;
+; Purpose: to replace the himem specific code in AddToXmemHeap
+;
+; Register
+; Usage: all preserved except return values in bx:dx
+;
+; Input: dx:cx is the minimum block length required (bytes).
+;
+; Output: cbHeapSize is updated if a heap is allocated
+;
+; Returns: bx:dx is 32bit linear address of allocated block if success,
+; cy clear, else cy set
+;
+; Exceptions:
+;
+; Notes:
+;
+;************************************************************************/
+cProc AddVCPIHeap,<NEAR,PUBLIC>,<eax,ecx>
+localD cbNeeded
+cBegin
+
+ push ebx ; save extended registers
+ push edx
+
+ add cx, CB_MEMHDR * 3 ; add memory manager overhead
+ adc dx, 0
+ movzx ecx,cx
+ shl edx,16d
+ or ecx,edx ; ECX = minimum bytes wanted
+ mov cbNeeded,ecx
+ add ecx,CBPAGE386-1
+ shr ecx,12d ; ECX = minimum pages wanted
+ mov eax, cPteMax
+ cmp ecx,eax ; make sure we have enough
+ jna AVH_00 ; page table entries
+ mov ecx,eax ; ECX = minimum pages wanted
+AVH_00:
+
+ mov cbHeapSize,0
+
+ mov ax, SEL_DXPT ; ES -> user page tables
+ mov es, ax
+ mov edi, pPteNext ; Point to first unused PTE
+ or edi, edi ; Initialized?
+ jnz AVH_10 ; Yes, skip XMS allocate.
+ mov edi, pPteFirst ; No, initialize first unused PTE.
+ mov pPteNext, edi
+
+ cCall AddXMStoVCPIHeapCall
+
+ mov pPteFirst,edi ; VCPI allocations start here
+
+AVH_10:
+ mov ebx, pPteNext ; EBX = first PTE
+ shl ebx, 10d ; EBX = linear address of start of block
+ mov dx, bx
+ shr ebx, 16d ; DX:BX = linear address of block
+
+ mov eax, edi
+ sub eax, pPteNext
+ shr eax, 2 ; AX = count of pages allocated
+ sub ecx, eax ; Get what we asked for?
+ jbe AVH_Done ; Yes.
+
+ cCall AllocVCPIMem ; allocate it from VCPI
+ ; cx has number of 4k pages
+AVH_Done:
+ mov eax, edi
+ sub eax, pPteNext
+ mov pPteNext, edi
+ or eax, eax
+ jz AVH_BadExit
+ shl eax, 10d ; EAX = count of bytes allocated
+ sub eax, CB_MEMHDR * 3 ; deduct overhead
+ mov cbHeapSize, eax
+ clc
+ jmp AVH_Exit
+AVH_BadExit:
+ stc
+AVH_Exit:
+;
+; Result is in BX:DX, but we have to save restore the MSW of EBX and
+; that of EDX before we return to our caller.
+;
+ mov ax,dx
+ pop edx
+ mov dx,ax
+ mov ax,bx
+ pop ebx
+ mov bx,ax
+cEnd
+
+DXPMCODE ends
+
+endif ;VCPI
+
+ end
diff --git a/private/mvdm/dpmi/dxvcpi.inc b/private/mvdm/dpmi/dxvcpi.inc
new file mode 100644
index 000000000..695d7d30b
--- /dev/null
+++ b/private/mvdm/dpmi/dxvcpi.inc
@@ -0,0 +1,374 @@
+;*** dxvcpi.inc - include file for vcpi functions/maintenance
+;
+; Copyright <C> 1990-1991, Microsoft Corporation
+;
+; Purpose:
+;
+; Revision History:
+;
+; 08-07-90 earleh rearranged things to allow building Pmode data
+; structures with total size exceeding that of LIM 3.2 page
+; frame
+; 05/09/90 jimmat Started incorporating VCPI changes from languages group.
+;
+; [] 20-Feb-1990 Dans Created
+;
+;************************************************************************/
+
+CurrentCpu = @Cpu ;select 386 assembly if not already
+ife (CurrentCpu AND 0008h)
+ .386
+endif
+
+;
+; Hungarian used:
+; la Linear Address
+; za Physical Address
+;
+
+;
+;
+; Miscellaneous equates
+;
+EMS_INT = 067h
+VCPIINT = 067h
+CBEMMSTR = 8h
+CPTDX = 1h ; count of user page tables dx uses
+CPTDXEXT = (CPTDX+1) ; count of total page tables dx uses
+CBPAGE386 = 1000h ; bytes in 386 page
+CBPAGE386LIM = 1000h-1
+
+DXINDOS = 00000001b
+DXINEMS = 00000010b ; obsolete
+DXINVCPI = 00000100b
+DXINXMS = 00001000b
+
+;
+; See dxvcpi.asm for a rough sketch of the memory block that the
+; following variables describe
+
+DXLINEARBASE = 80000000h ; Linear base of DX system memory
+
+VCPIPTOFF = 0 * CBPAGE386 ; vcpi's 0th page table
+DXPT1OFF = 1 * CBPAGE386 ; dx's first user page table
+DXPTSYSOFF = DXPT1OFF + (CPTDX * CBPAGE386)
+ ; dx's system page table
+DXPDOFF = DXPTSYSOFF + CBPAGE386 ; page directory
+DXLASTPTOFF = DXPDOFF
+DXBOOTPTOFF = DXPTSYSOFF
+;
+; Last user page table is used to bootstrap our protected mode
+; data into extended memory.
+;
+DXTEMPPTOFF = DXPTSYSOFF - CBPAGE386
+DX_TEMP_LINEARBASE = (DXTEMPPTOFF - VCPIPTOFF) shl 10
+
+;
+; The next number comes from the dosx.map file, and is equal to the
+; offset of the CODEENDPM symbol, plus whatever padding we want to use
+; so we don't have to update this include file too often.
+;
+if DEBUG
+DXPMCODESIZE = 04900H
+else ; DEBUG
+DXPMCODESIZE = 04000H
+endif ; DEBUG
+
+IDTOFF = DXLASTPTOFF + CBPAGE386
+IDTSIZE = CDSCIDTDEFAULT * 8
+IDTLIM = IDTSIZE - 1
+
+TSSOFF = IDTOFF + IDTSIZE
+TSSTOP = TSSOFF + (type TSS386)
+
+GDTOFF = ((TSSTOP + 0fh) shr 4) shl 4 ; paragraph align
+GDTLIM = GDT_SIZE - 1
+GDTTOP = GDTOFF + GDT_SIZE
+
+DXPMCODEOFF = ((GDTTOP + 0fh) shr 4) shl 4 ; paragraph align
+
+LDTOFF = ((DXPMCODEOFF + DXPMCODESIZE + CBPAGE386 - 1) shr 12) shl 12
+LDTSIZE = CDSCMAXLDT * 8
+LDTLIM = LDTSIZE - 1
+LDTTOP = LDTOFF + LDTSIZE
+
+;
+; Final place where page tables are mapped in Protected mode is
+; at the first linear page boundary after the end of the LDT.
+;
+
+USERPT = (LDTTOP + CBPAGE386 - 1) shr 12
+USERPTOFF = ((LDTTOP + CBPAGE386 - 1) shr 12) shl 12
+
+;
+; The total number of 386 pages we need for the block that holds our
+; system tables and protected mode code.
+;
+DXPMPAGES = (LDTTOP + (CBPAGE386 - 1)) shr 12
+DXPMBYTES = DXPMPAGES shl 12
+DXPMPARAGRAPHS = DXPMBYTES shr 4
+
+;
+; compile time asserts for sizes/offsets/alignment
+;
+.ERRE (type TSS386) GE 104
+.ERRE TSSTOP LE GDTOFF
+.ERRE IDTOFF EQ ((IDTOFF SHR 4) SHL 4)
+
+;
+; Limits of tables
+;
+DXPTMAX = CPTDX * CBPAGE386 ; space we allocate for
+ ; user page tables
+
+;
+; Linear pointers to bases of various tables when running under vcpi
+;
+
+; For a selDXPD descriptor (dos extender's page directory)
+;
+LADXPDBASE = DXLINEARBASE + DXPDOFF
+
+; For a selDXPT descriptor (dos extender's page tables)
+;
+LADXPTBASE = DXLINEARBASE + USERPTOFF ; (Plus runtime value.)
+
+; For a selGDT descriptor
+;
+LADXGDTBASE = DXLINEARBASE + GDTOFF
+
+; For a selLDT descriptor
+;
+LADXLDTBASE = DXLINEARBASE + LDTOFF
+
+; For a selIDT descriptor
+;
+LADXIDTBASE = DXLINEARBASE + IDTOFF
+
+; For a selTSS descriptor
+;
+LADXTSS1BASE = DXLINEARBASE + TSSOFF
+LADXTSS2BASE = DXLINEARBASE + TSSOFF + type TSS386
+
+; For a SEL_DXPMCODE descriptor
+;
+LADXPMCODEBASE = DXLINEARBASE + DXPMCODEOFF
+
+
+;
+; vcpi/ems service macros
+;
+RMvcpi macro fCode
+ ifnb <fCode>
+ mov ax, fCode
+ endif
+ int VCPIINT
+ endm
+
+PMvcpi macro fCode
+ ifnb <fCode>
+ mov ax, fCode
+ endif
+ cCall CallVCPIPM
+ endm
+
+emscall macro fCode
+ ifnb <fCode>
+ mov ax, fCode
+ endif
+ int EMS_INT
+ endm
+
+; EMS functions/subfunctions
+
+GETFRAMEADDRESS = 04100h
+
+ALLOCATEPAGES = 05A00h
+GETNUMOFPAGES = 04200h
+MAPHANDLEPAGE = 04400h
+DEALLOCATEPAGES = 04500h
+GETEMSVER = 04600h
+SETHANDLENAME = 05301h
+GETPAGEADDRS = 05800h
+GETNUMPAGEMAP = 05801h
+
+page
+;
+; VCPI functions/subfunctions
+; all functions take the vcpi function code in ax as input
+; all functions return ah=0 if successful, ah != 0 if failure
+; if function return has no ah value, it can't fail (ah == 0)
+;
+;
+
+; vcpi version, presence
+;
+vcpiVER = 0de00h
+ ;
+ ; input:
+ ; return: ah = 0, bl = vcpi minor revision, bh = major revision
+ ;
+
+; get protect mode interface
+;
+vcpiPMINTERFACE = 0de01h
+ ;
+ ; input: es:di = ptr to 4k page table,
+ ; ds:si = ptr to 3 entries in GDT
+ ; return: di = 1st unused page table entry,
+ ; ebx = offset in server CS of PM entry point
+ ;
+
+; get max physical address in system
+;
+vcpiMAXPHYSADDR = 0de02h
+ ;
+ ; input:
+ ; return: edx = physical addr of highest 4k page that
+ ; could ever be allocated
+ ;
+
+; count of free 4k pages
+;
+; Note: This call gives the total 386 pages available to all tasks
+; in the system from the VCPI server. According to VCPI version 1.0,
+; however, we should only allocate as much memory as there is EMS
+; memory available. The following call, therefore, is not real useful
+; to us.
+;
+vcpiCFREEPAGES = 0de03h
+ ;
+ ; input:
+ ; return: edx = number of free 4k pages
+ ;
+
+; allocate a 4k page
+;
+vcpiALLOCPAGE = 0de04h
+ ;
+ ; input:
+ ; return: ah = 0, edx = physical address of allocated 4k page
+ ; ah != 0, edx = trashed
+ ;
+
+; free a 4k page
+;
+vcpiFREEPAGE = 0de05h
+ ;
+ ; input: edx = physical address of page to free
+ ; return: ah = 0
+ ; ah != 0
+ ;
+
+; physical address of page in 1st meg
+;
+vcpiPHYSADDRPAGE= 0de06h
+ ;
+ ; input: cx = page number (linear addr of page SHR by 12)
+ ; return: ah = 0, edx = physical address of 4k page
+ ; ah != 0
+ ;
+
+; read cr0
+;
+vcpiREADCR0 = 0de07h
+ ;
+ ; input:
+ ; return: ebx = cr0 value
+ ;
+
+; read debug registers
+;
+vcpiREADDRx = 0de08h
+ ;
+ ; input: es:di = ptr to 8 dwords, dr0 first, dr4, dr5 not present
+ ; return:
+ ;
+
+; load debug registers
+;
+vcpiLOADDRx = 0de09h
+ ;
+ ; input: es:di = ptr to 8 dwords, dr0 first, dr4, dr5 not present
+ ; return:
+ ;
+
+; get mapping of hardware interrupts
+;
+vcpiGET8259MAP = 0de0ah
+ ;
+ ; input:
+ ; return: bx = 1st vector mapping for master 8259a
+ ; cx = 1st vector mapping for slave 8259a
+
+; set mapping of hardware interrupts
+;
+vcpiSET8259MAP = 0de0bh
+ ;
+ ; input: interrupts disabled
+ ; bx = 1st vector mapping for master 8259a
+ ; cx = 1st vector mapping for slave 8259a
+ ; return:
+ ;
+
+; switch from v86 mode to protect mode or protect mode to v86 mode
+;
+vcpiSWITCHTOPM = 0de0ch
+ ;
+ ; input: interrupts disabled
+ ; esi = linear address (in first megabyte) of data
+ ; structure (v86topm struc)
+ ;
+ ; return: (output in pm)
+ ; gdtr, idtr, ldtr, tr loaded
+ ; ss:esp must have 16 bytes of space on it, pm must
+ ; set up it's stack
+ ; eax = trashed
+ ; esi = trashed
+ ; ds, es, fs, gs all modified
+ ; interrupts disabled
+ ;
+
+vcpiSWITCHTOV86 = 0de0ch
+ ;
+ ; input: interrupts disabled
+ ; STACK: 28 dword gs
+ ; 24 dword fs
+ ; 20 dword ds
+ ; 1c dword es
+ ; 18 dword ss
+ ; 14 dword esp
+ ; 10 dword eflags reserved
+ ; 0c dword cs xfer to
+ ; 08 dword eip xfer to
+ ; 00 qword far32 return (garbage)
+ ;
+ ;
+ ; return: (output in rm)
+ ; ss:esp loaded with values from stack
+ ; segment registers loaded with stack values
+ ; eax = trashed
+ ; interrupts disabled
+
+;
+; structures
+;
+VTP struc
+ zaCr3VTP dd 0 ; physical addr of page directory
+ laGdtrVTP dd 0 ; linear addr in first meg of gdtr
+ laIdtrVTP dd 0 ; linear addr in first meg of idtr
+ selLdtVTP dw 0 ; selector of ldt
+ selTrVTP dw 0 ; selector of tr
+ ipVTP dw 0 ; 48-bit address of protect
+ unusedVTP dw 0 ; mode entry point to xfer to
+ csVTP dw 0 ;
+VTP ends
+
+
+ife (CurrentCpu AND 0008h) ;restore cpu type if not 386
+ if (CurrentCpu AND 0080h)
+ .286p
+ else
+ .286
+ endif
+endif
diff --git a/private/mvdm/dpmi/dxvcpibt.asm b/private/mvdm/dpmi/dxvcpibt.asm
new file mode 100644
index 000000000..1660ba30e
--- /dev/null
+++ b/private/mvdm/dpmi/dxvcpibt.asm
@@ -0,0 +1,1606 @@
+ PAGE ,132
+ TITLE DXVCPIBT.ASM -- Dos Extender VCPI Support Code (initialization)
+
+; Copyright (c) Microsoft Corporation 1990-1991. All Rights Reserved.
+
+;*** dxvcpibt.asm - vcpi detection/setup code (discardable)
+;
+; Copyright <C> 1990, Microsoft Corporation
+;
+; Purpose:
+;
+; Revision History:
+;
+;
+; 08-07-90 earleh rearranged memory map to allow building full-sized
+; Pmode data structures in v86 mode, allow booting to Pmode
+; without a LIM 3.2 page frame, fixed up detection code to
+; work with various strange interpretations of LIM 4.0 by
+; Limulator vendors. Allow use of entire linear space below
+; 16 Meg by DOSX and VCPI server.
+;
+; 05/07/90 jimmat Started incorporating VCPI changes from languages group.
+;
+; [] 20-Feb-1990 Dans Created
+;
+;
+; note that this should only be called on a 386, except
+; CheckForVCPI, which can be called from 286 machines.
+;
+; this code is for the real mode portion of the dos extender and
+; is released back to DOS prior to switching to protect mode the
+; first time. NO DATA defined here in either segment as it will
+; be discarded.
+;
+;************************************************************************/
+
+.286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+.xlist
+.sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+.list
+
+; This entire file is only for VCPI support
+
+if VCPI
+
+.xlist
+.sall
+include dxvcpi.inc
+.list
+
+;
+; data
+;
+
+DXDATA segment
+
+;
+; Externs
+;
+
+extrn idCpuType:word
+extrn segBootPmode:word
+extrn pPteFirst:dword
+extrn cPteMax:dword
+extrn bpdxsyspages:dword
+extrn iddxsystype:byte
+extrn hmem_System_Block:word
+extrn fEMS:byte
+extrn fVCPI:byte
+extrn cFreePages:dword
+extrn bpGDT:fword
+extrn bpGDTbase:dword
+extrn bpGDTcb:word
+extrn bpIDT:fword
+extrn bpIDTbase:dword
+extrn bpIDTcb:word
+extrn selGDT:word
+extrn segGDT:word
+extrn selIDT:word
+extrn segIDT:word
+extrn cdscGDTMax:word
+extrn cdscIDTMax:word
+extrn segPSP:word
+extrn selPSP:word
+extrn selGDTFree:word
+extrn sysTSS:WORD
+ifdef NOT_NTVDM_NOT
+extrn fHPVectra:BYTE
+endif
+extrn lpfnXMSFunc:DWORD
+extrn rgbXfrBuf1:BYTE
+
+DMAServiceSegment equ 040h ;40:7B bit 5 indicates DMA services
+DMAServiceByte equ 07Bh ; are currently required
+DMAServiceBit equ 020h
+extrn bDMAServiceBit:BYTE
+
+if DEBUG
+extrn fTraceBug:WORD
+ifdef CV_TSS
+extrn FaultStack:word
+endif
+endif
+
+EXTRN hmem_XMS_Table:WORD
+EXTRN hmem_XMS_Count:WORD
+
+ extrn fDebug:BYTE
+
+DXDATA ends
+
+
+DXSTACK segment
+
+extrn rgw0Stack:WORD
+
+DXSTACK ends
+
+
+DXCODE segment
+
+extrn CodeEnd:NEAR
+extrn CallVCPI:NEAR
+extrn ResetVCPI:NEAR
+extrn segDXCode:WORD
+
+DXCODE ends
+
+
+DXPMCODE segment
+
+;
+; Externs
+;
+extrn fnVCPIPMoff:dword
+extrn CodeEndPM:near
+
+extrn PMIntr31: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 HPxBIOS:NEAR
+extrn PMFaultReflectorIRET:FAR
+
+if DEBUG
+extrn PMDebugInt:NEAR
+endif
+
+externFP NSetSegmentDscr
+
+DXPMCODE ends
+
+DXCODE segment
+
+ assume cs:DXCODE, ds:DXDATA, es:nothing
+
+ extrn WdebVCPI:BYTE
+
+;
+; Definitions
+;
+rgbEMMDrv db 'EMMXXXX0',0 ; emm device driver name
+rbgQEMMDrv db 'EMMQXXX0',0 ; QEMM v. 5.0 with "FRAME=NONE"
+
+DebOut_Int equ 41h ;Wdeb386 pMode interface Interrupt
+DS_VCPI_Notify equ 005Bh ;Notify Wdeb386 of VCPI interface
+
+ extrn ER_QEMM386:BYTE
+ERC_QEMM386 equ offset ER_QEMM386
+ extrn DisplayErrorMsg:FAR
+
+;
+; Externs
+;
+extrn fnVCPIoff:dword
+extrn V86ToPm:word
+extrn laVTP:dword
+
+;extrn FreeEMSHandle:proc
+externNP FreeEMSHandle
+extrn SetupHimemDriver:proc
+
+
+;*** QEMM386Trap
+;
+; Purpose: A device driver or TSR has just failed the Windows INT 2Fh
+; AX = 1605H startup broadcast. Version 5.1 and 5.11 of
+; QEMM386 will do this for any version of DOSX which it
+; doesn't know how to patch to work with VCPI. Since we
+; do know how to work with VCPI now, QEMM386 is behaving in
+; an inappropriate manner. Furthermore, QEMM386 is required
+; to output an informative message if it does this, and it
+; does not.
+;
+; This routine will ask the user for permission
+; to proceed.
+;
+; Uses:
+;
+; Input: A process has told DOSX not to load by returning
+; non-zero in CX on an INT 2Fh, AX=1605h.
+;
+; Output: AX = Zero, proceed.
+; AX = Non-zero, abort.
+;
+;************************************************************************/
+cProc QEMM386Trap,<NEAR,PUBLIC>,<ds,es>
+cBegin
+
+ mov dx,cs ;pass msg ptr in DX:AX
+ mov ax,ERC_QEMM386
+ call DisplayErrorMsg ; Tell the user something
+
+ mov ah, 8 ; Get a character from the keyboard
+ int 21h
+ or al,20h ; Force lower case.
+ cmp al,'y' ; Party if it's a 'y'.
+ mov ax,0
+ jne IQ_Abort
+ jmp IQ_Exit
+
+IQ_Abort:
+ dec ax
+IQ_Exit:
+IQ_Not_Open:
+ or ax,ax
+cEnd
+
+;*** CheckForEMS
+;
+; Purpose: see if an ems driver is loaded
+;
+; Register
+; Usage: ax, cx
+;
+; Input: ES:BX contains int 67h vector.
+;
+; Output: none
+;
+; Returns: carry not set if ems is loaded, set if not
+;
+; Exceptions: none
+;
+; Notes: This checks for a LIM 4.0 driver. It also checks
+; for a driver named 'EMMQXXX0' which could be QEMM 5.0
+; without a page frame.
+;
+;************************************************************************/
+
+cProc CheckForEMS,<NEAR,PUBLIC>,<di,si,ds>
+cBegin
+
+ mov di, 0ah ; es:di has string to look at
+
+ mov ax, cs
+ mov ds, ax
+
+ assume ds:DXCODE,ss:DGROUP
+
+ mov si, offset DXCODE:rgbEMMDrv ; ds:si has driver string
+ mov cx, CBEMMSTR ; cx has rep count
+ repe cmpsb
+ jne short CheckFor_QEMM
+ ; try QEMM driver with
+ ; non-standard name for
+ ; "FRAME=NONE"
+
+ emscall GETEMSVER
+ or ah, ah
+ jz @F
+ xor ax, ax
+@@:
+ cmp al, 40h ; LIM 4.0?
+ jb Failure_Exit ; No, can't use it.
+
+ inc fEMS ; set flag
+ clc ; return success
+ jmp CheckForEMS_ret
+CheckFor_QEMM: ; look for QEMM signature
+ mov di, 0ah ; es:di has string to look at
+ mov si, offset DXCODE:rbgQEMMDrv ; ds:si has driver string
+ mov cx, CBEMMSTR ; cx has rep count
+ repe cmpsb
+ jne short Failure_Exit ; if not equal, exit
+ inc fEMS ; set flag
+ clc
+ jmp CheckForEMS_ret
+
+Failure_Exit:
+ stc ; zero out return value first
+CheckForEMS_ret:
+cEnd
+
+assume ds:DGROUP
+
+
+;*** CheckForVCPI
+;
+; Purpose: to see if a vcpi server is loaded
+;
+; Register
+; Usage: ax, bx, cx
+;
+; Input: none
+;
+; Output: none
+;
+; Returns: ax = vcpi version if it is loaded, 0 otherwise
+;
+; Exceptions: none
+;
+; Notes: this calls CheckForEMS first, and EMSVer as well
+;
+;************************************************************************/
+cProc CheckForVCPI,<NEAR,PUBLIC>,<es>
+cBegin
+
+ cmp idCpuType, 3 ; must be 386 or better to have
+ jnb @F
+ jmp novcpi286 ; vcpi server running.
+@@:
+.386
+ mov ax, 03500h+EMS_INT ; ah = get interrupt, al = vector to get
+ int 021h ; returns with es:bx == int vector
+ mov ax,es ; int vector set?
+ or ax,bx
+ jz novcpi ; No.
+
+ call CheckForEMS
+ jc novcpi ; no ems, can't be any vcpi
+
+ call SwitchToV86 ; force v86 mode
+ or al, al
+ jz novcpi ; failed to allocate, out of here
+
+ RMvcpi vcpiVER ; see if vcpi is available
+ or ah, ah
+ jnz novcpi
+
+ inc fVCPI ; set flag
+
+ jc novcpi
+
+
+ movzx eax,segBootPmode
+ shl eax,4
+ add eax,GDTOFF
+ shr eax,4
+
+ mov selGDT,ax ; save it for later
+ mov segGDT,ax ; save it for later
+
+ ; If this is changed, then GetVCPIInterface must be changed
+ ; to save correct value in pPteFirst.
+.ERRE VCPIPTOFF EQ 0
+
+ cCall GetVCPIInterface,<segBootPmode,VCPIPTOFF,ax,SEL_VCPI>
+
+ call SetupPageTables ;
+ jc novcpi ; had a problem setting up page table
+;
+; Load the VDS-implemented bit from the BIOS data area.
+;
+ mov ax,DMAServiceSegment
+ mov es,ax
+ mov al,byte ptr es:[DMAServiceByte]
+ and al,DMAServiceBit
+ mov [bDMAServiceBit],al
+
+ RMvcpi vcpiVER ; refetch VCPI version
+ mov ax,bx ; put it in AX
+ jmp CheckForVCPI_exit ; return no error
+.286p
+novcpi:
+ call FreeEMSHandle ; free the handle we allocated.
+novcpi286: ; (Free routine uses 386 instructions.)
+ xor ax, ax ; none loaded
+CheckForVCPI_exit:
+cEnd
+
+DXCODE ends
+
+;**************************************************************
+; 386 only code from here on down!!!
+;**************************************************************
+.386p
+include prot386.inc
+
+DXCODE segment
+
+ assume cs:DXCODE, ds:DXDATA, es:nothing
+
+;*** SwitchToV86
+;
+; Purpose: Switch to v86 mode in preparation for vcpi calls
+;
+; Register
+; Usage: eax, bx, ecx, es
+;
+; Input: none
+;
+; Output: hEMM is updated, pages are mapped into ems frame
+; segBootPmode setup
+;
+; Returns: al == 1 if successful, al == 0 otherwise
+;
+; Exceptions: none
+;
+; Notes: By allocating an ems handle/page, we force the lim 4.0
+; emulator to switch from real mode to v86 mode. Do not free
+; the ems memory until exit time.
+;
+;************************************************************************/
+cProc SwitchToV86,<NEAR,PUBLIC>,<es,si,edi,cx>
+cBegin
+;
+ smsw ax
+ test al,01h ; In V86 mode?
+ jz stv_badexit ; We don't know how to turn on
+ ; a VCPI server without allocating
+ ; EMS memory.
+ mov cx,(offset CodeEnd) + CBPAGE386 - 1
+ shr cx,4
+ mov ax,segDXCode
+ add ax,cx
+ and ax,0FF00H ; page align DOS block
+
+ mov segBootPmode,ax
+ mov es,ax ; zero it out
+
+ mov al,1
+
+ jmp stvExit
+
+stv_badexit:
+ call FreeEMSHandle
+ xor al, al
+stvExit:
+cEnd
+
+;*** GetVCPIInterface
+;
+; Purpose: To retrieve the vcpi protect mode interface data
+;
+; Register
+; Usage: es, ax, edx, ebx
+;
+; Input: lpPT: far ptr to a page table
+; lprggdte: far ptr to a range of 3 gdt entries.
+;
+; Output: fnVCPIoff updated with 32-bit offset of pm entry point
+; for vcpi calls
+; *lpPT updated with 4k vcpi 0th page table
+; *lprggdte updated with vcpi server code segment, extras
+;
+;
+; Returns: nothing
+;
+; Exceptions:
+;
+; Notes: only call in real mode prior to relocation of DosExtender,
+; as it sets up a variable in DXPMCODE that needs to be
+; copied up when relocated.
+;
+;************************************************************************/
+cProc GetVCPIInterface,<NEAR,PUBLIC>,<es>
+parmD lpPT
+parmD lprggdte
+cBegin
+ push ds ; save ds
+ les di, lpPT ; es:di -> vcpi's page table
+ lds si, lprggdte ; ds:si -> vcpi's gdt entries
+
+ assume ds:nothing
+
+ RMvcpi vcpiPMINTERFACE
+ mov word ptr pPteFirst,di
+ mov ax, CBPAGE386 ; AX = size of page table
+ sub ax, di ; AX = #bytes left over in zeroth PT
+ shr ax, 2 ; AX = #PTEs left over
+ add ax, DXPTMAX shr 2 ; AX = total # user PTEs available,
+ mov word ptr cPteMax, ax ; counting those in zeroth PT
+ pop ds ; restore ds
+
+ assume ds:DXDATA
+
+ mov fnVCPIoff, ebx
+ mov ax, seg DXPMCODE
+ mov es, ax
+
+ assume es:DXPMCODE
+
+ mov fnVCPIPMoff, ebx ; set pm call
+;
+; Refer to VCPI spec. Version 1.0 for why this call sequence is used to
+; find the number of VCPI pages we may allocate.
+;
+ emscall GETNUMOFPAGES
+ movzx ebx,bx ; BX = unallocated EMS pages
+ shl ebx, 2 ; EBX = unallocated VCPI pages
+ mov cFreePages, ebx ; Never allocate more than this amt.
+
+cEnd
+
+ assume es:nothing
+
+;*****************************************************************************
+; Memory map of system tables buffer prior to switch to protected
+; mode under vcpi.
+;
+; __+-------------------------------+->USERPTOFF
+; __+-------------------------------+->LDTTOP
+; ~100k | |
+; | |
+; | LDT, 64k (8190 entries) |
+; | |
+; | |
+; |_______________________________|->
+; | |
+; | |
+; | |
+; | DXPMCODE (~18.5k) |
+; | Exact size may be found from |
+; | codeendPM symbol. |
+; | |
+; __|_______________________________|
+; | |->DXPMCODEOFF (GDTTOP)
+; | GDT |
+; __|_______________________________|
+; | |->GDTOFF
+; | TSS space (2 of them for 386) |
+; 18k __|_______________________________|
+; | IDT, 2k |->TSSOFF
+; 16k __|_______________________________|
+; | |->IDTOFF
+; | |
+; | page directory (DXPD) |
+; 12k __|_______________________________|
+; | |->DXPDOFF
+; | DOSX Pmode page table (DXPT2) |
+; | (DXLINEARBASE) |
+; 8k __|_______________________________|
+; | |->DXPTSYSOFF
+; | 1st user page table (DXPT1) |
+; | (4-8 Meg) |
+; 4k __|_______________________________|
+; | |->DXPT1OFF = DXTEMPPTOFF
+; | 0th page table (VCPIPT) |
+; | (0-4 Meg) |
+; 0k __|_______________________________|->VCPIPTOFF
+; | Wasted for page alignment |
+; CodeEnd --->|-------------------------------|
+; Label
+;
+; System data structures and DOSX Pmode code will be located at linear
+; addresses pointed to by the page table which begins at DXPTSYSOFF.
+; During the first switch to Pmode, these addresses will exist in a
+; block of conventional memory allocated from DOS. After the first
+; switch, a block of extended memory allocated from VCPI will be used.
+; The user page table starting at DXTEMPPTOFF will be used to access the
+; second block of memory for initialization purposes.
+;
+; Note that the first page, which contains the zeroth page table, shared
+; with the VCPI server, must use the same physical page of memory before
+; and after the switch.
+;
+; Terms:
+; DXPMCODE Dos eXtender Protect Mode CODE
+; IDT Interrupt Descriptor Table
+; GDT Global Descriptor Table
+; DXPTx Dos eXtender Page Table number x
+; VCPIPT VCPI Pate Table (we can't touch this one)
+; DXPD Dos eXtender Page Directory
+;
+;*****************************************************************************
+
+;*** SetupPageTables
+;
+; Purpose: To set up page tables and directory in ems
+; frame (as described above)
+;
+; Register
+; Usage: eax, bx, cx, edx, esi, edi
+;
+; Input: none
+;
+; Output: DXPD, DXPTx, GDT setup.
+;
+; Returns: cy set if error
+;
+; Exceptions:
+;
+; Notes:
+;
+;************************************************************************/
+cProc SetupPageTables,<NEAR,PUBLIC>,<ds,es>
+cBegin
+
+ mov cdscGDTMax,8190 ; max out the GDT size
+
+ mov bx, segBootPmode
+ mov es, bx
+ mov di,DXBOOTPTOFF
+ shr bx, 8 ; convert to 16-bit page aligned
+
+;
+; Our extended memory stuff is located in a single contiguous
+; block of DOS memory.
+;
+ mov ecx, DXPMPAGES
+physaddrloop:
+ xchg cx, bx ; put page number in cx, save count
+ ; in bx
+ RMvcpi vcpiPHYSADDRPAGE ; get the physical address
+ or ah, ah
+ jnz badexit
+ and dx, 0f000h ; mask off 12 lsb's
+ or dx, NEWPTEMASK ; store with decent page flags
+ mov es:[ di ], edx ; store physical in DXPT1
+ add di, 4 ; advance to next
+ xchg cx, bx ; get loop counter in cx,
+ inc bx ; advance to next page in ems frame
+ loop physaddrloop
+pagesmapped:
+ ;
+ ; Save away the page directory base for cr3 loading
+ ;
+ mov eax, es:[DXBOOTPTOFF + (DXPDOFF shr 10)]
+ ; Save the pte (physical addr) of
+ and ax, 0f000h ; page directory, mask 12 lsb's
+ mov V86ToPm.zaCr3VTP, eax ; store it
+ ;
+ ; Save the linear address of bpGDT and bpIDT in V86ToPm as well
+ ;
+ xor eax, eax
+ mov ax, DXDATA ; dgroup segment, masked hi word
+ shl eax, 4 ; linearize it
+ mov ebx, eax ; save it
+ add eax, offset DGROUP:bpGDT; add in offset
+ mov V86ToPm.laGdtrVTP, eax ; save it in vcpi v86 to pm structure
+ mov eax, ebx ; restore linear dgroup
+ add eax, offset DGROUP:bpIDT; add in offset
+ mov V86ToPm.laIdtrVTP, eax ; save it in vcpi v86 to pm structure
+ xor eax, eax
+ mov ax, cs ; get current code segment
+ shl eax, 4 ; linear dxcode
+ add eax, offset DXCODE:V86ToPm ; add in offset of v86 to pm structure
+ mov laVTP, eax ; save linear ptr of v86 to pm structure
+ ;
+ ; set up the page directory (DXPD)
+ ; with 0-x to be VCPIPT thru DXPTx (x+1 total)
+ ;
+ ; (copy them out of dxpt1, starting with the 2nd entry)
+ ;
+
+ mov edi, DXPDOFF
+ push ds
+ push es ; make ds point to emspageframe
+ pop ds ; as well
+ assume ds:nothing
+ mov esi, DXBOOTPTOFF + (VCPIPTOFF shr 10) ; point to 2nd entry in DXPT1
+ ; (must be VCPIPT)
+ mov ecx, CPTDX + 1 ; # of user pt's + vcpi's pt
+ rep movsd
+ ; point DXLINEARBASE at system area
+ mov esi,DXBOOTPTOFF + (DXBOOTPTOFF shr 10)
+ mov edi,DXPDOFF + (DXLINEARBASE shr 20)
+ movsd
+
+ pop es
+ assume es:DGROUP
+;
+; Allocate a block of VCPI 4k pages, enough to copy our system tables
+; and Pmode code into once we have achieved protected mode operation.
+; Map these pages into one of the page tables that is unused until
+; heap initialization time.
+;
+; If there is insufficient VCPI memory to complete this operation, then
+; we try to grab XMS memory instead.
+;
+ lea di,bpdxsyspages
+
+ cmp cFreePages, DXPMPAGES-1
+ jc tryXMSServer ; insufficient VCPI pages to
+ ; build this block
+ mov cx,DXPMPAGES-1
+allocate_syspage_from_VCPI:
+ RMvcpi vcpiALLOCPAGE
+ or ah,ah
+ jnz badexit
+ mov eax,edx
+ stosd
+ loop allocate_syspage_from_VCPI
+
+ sub cFreePages, DXPMPAGES-1
+
+ mov iddxsystype,DXINVCPI
+;
+; Map these pages into a temporary area, which we use later to move most
+; of the stuff in this DOS block up into extended memory.
+;
+ push es
+ pop ds
+ assume es:nothing,ds:DGROUP
+ mov ax, segBootPmode
+ mov es, ax
+ lea si,bpdxsyspages
+ mov cx,DXPMPAGES-1
+ mov di,DXTEMPPTOFF
+ mov eax,es:[DXBOOTPTOFF + (VCPIPTOFF shr 10)]
+ stosd
+copy_syspage:
+ lodsd
+ or eax,NEWPTEMASK
+ stosd
+ loop copy_syspage
+ jmp goodexit
+tryXMSServer:
+ assume es:nothing,ds:DGROUP
+ push es
+ pop ds
+ call SetupHimemDriver ; Need XMS driver now.
+ mov dx,DXPMPAGES shl 2 ; kbytes = pages times 4
+ xmssvc 09h ; allocate an XMS block
+ cmp ax,1 ; got a block?
+ jne goodexit ; no, try using DOS mem.
+ mov hmem_System_Block,dx
+ xmssvc 0ch ; lock the block
+ cmp ax,1 ; did it work?
+ je @F
+ mov dx,hmem_System_Block ; No, free it up.
+ mov hmem_System_Block,0
+ xmssvc 0ah
+ jmp goodexit ; and try to run in DOS mem
+@@:
+ shl edx,10h ; DX:BX = locked block address
+ mov dx,bx ; EDX = locked block address
+;
+; Make sure the locked XMS block is page aligned. If is not, then we
+; will have to adjust the bottom address used and shrink the GDT by a
+; full 386 page.
+;
+ test dx,MASK allbitsPTE
+ jz XMSpagealigned
+ and dx,MASK pfaPTE
+ sub cdscGDTMax,CBPAGE386 / 8 ; shrink the GDT size
+XMSpagealigned:
+ mov ax, segBootPmode
+ mov es, ax
+ mov di,DXTEMPPTOFF
+ mov eax,es:[DXBOOTPTOFF + (VCPIPTOFF shr 10)]
+ stosd
+ mov cx,DXPMPAGES-1
+ mov eax,edx
+ or eax,NEWPTEMASK
+insertXMSpage:
+ stosd
+ add eax,CBPAGE386
+ loop insertXMSpage
+ mov iddxsystype,DXINXMS
+goodexit:
+ clc
+ jmp exit
+badexit:
+ stc
+exit:
+
+cEnd
+
+ assume ds:DXDATA
+
+
+;*** InitGDTVCPI - initialize the gdt when running under vcpi
+;
+; Purpose:
+;
+; Register
+; Usage: uses eax, all others preserved
+;
+; Input: none
+;
+; Output: gdt/ldt is initialized
+;
+; Returns: returns carry set if failure to map EMS pages
+;
+; Exceptions:
+;
+; Notes:
+;
+;************************************************************************/
+
+ assume ds:DGROUP,es:DGROUP,ss:NOTHING
+ public InitGDTVCPI
+cProc InitGDTVCPI,<NEAR,PUBLIC>,<ebx,ecx,edx,di,es>
+cBegin
+ mov edx,LADXGDTBASE ; set gdt linear address
+ mov bpGDTbase,edx
+ mov ecx,GDT_SIZE - 1 ; set the GDT segment size
+ mov bpGDTcb,cx
+ mov bh, STD_DATA ; make it be a data segment
+ mov ax, SEL_GDT
+ cCall NSetSegmentDscr,<SEL_GDT,edx,ecx,STD_DATA>
+
+; Set up a descriptor for the LDT and an LDT data alias.
+
+ mov edx,LADXLDTBASE
+ movzx ecx,cdscGDTMax
+ shl ecx,3
+ dec ecx
+ cCall NSetSegmentDscr,<SEL_LDT,edx,ecx,STD_LDT>
+
+;
+; Set up the descriptors for the page directory and page tables
+;
+ mov eax,LADXPDBASE
+ cCall NSetSegmentDscr,<SEL_DXPD,eax,0,CBPAGE386LIM,STD_DATA>
+
+ mov edx, LADXPTBASE ; EDX = user page tables linear base
+
+ mov ecx, ( (CPTDX + 1) shl 12 ) - 1
+ ; # of user PTE's + vcpi's PTE's minus one
+ cCall NSetSegmentDscr,<SEL_DXPT,edx,ecx,STD_DATA>
+
+; Setup a selector and data alias for the TSS
+
+ mov edx,LADXTSS1BASE ;get base address of TSS
+ mov ecx,(TYPE TSS386) - 1
+ cCall NSetSegmentDscr,<SEL_TSS,edx,ecx,STD_TSS386>
+
+ mov eax,DXLINEARBASE
+ xor ecx,ecx
+ dec cx
+ cCall NSetSegmentDscr,<SEL_LDT_ALIAS,eax,ecx,STD_DATA>
+ mov eax,DX_TEMP_LINEARBASE
+ cCall NSetSegmentDscr,<SEL_TSS_ALIAS,eax,ecx,STD_DATA>
+
+
+; Set up a descriptor for our protected mode code.
+
+ mov dx,cs
+ movzx edx,dx ;our code segment paragraph address
+ shl edx,4 ;convert to linear byte address
+ cCall NSetSegmentDscr,<SEL_DXCODE,edx,0,0ffffh,STD_CODE>
+
+; Set up another one, but ring 0 this time.
+
+ cCall NSetSegmentDscr,<SEL_DXCODE0,edx,0,0ffffh,ARB_CODE0>
+
+ mov edx,LADXPMCODEBASE ; EDX = LADXPMCODEBASE
+ mov ecx, offset DXPMCODE:CodeEndPM + 10h
+ cCall NSetSegmentDscr,<SEL_DXPMCODE,edx,ecx,STD_CODE>
+;
+; Set up the Ring 0 DXPMCODE alias for handling protected mode processor
+; exceptions.
+;
+ cCall NSetSegmentDscr,<SEL_EH,edx,ecx,EH_CODE>
+
+; Set up a descriptor for our protected mode data and stack area.
+
+ mov dx,ds
+ movzx edx,dx ;our data segment paragraph address
+ shl edx,4 ;convert to linear byte address
+ mov ecx,0FFFFh
+ cCall NSetSegmentDscr,<SEL_DXDATA,edx,ecx,STD_DATA>
+
+; And another one of those for ring 0
+
+ cCall NSetSegmentDscr,<SEL_DXDATA0,edx,ecx,ARB_DATA0>
+
+; Set up descriptors pointing to our PSP and environment.
+
+ movzx edx, segPSP ; segment address of the PSP
+ shl edx, 4 ; linear address of PSP
+ cCall NSetSegmentDscr,<SEL_PSP,edx,ecx,STD_DATA>
+ mov selPSP, SEL_PSP
+
+; set up environment selector
+
+ push es
+ mov es,segPSP
+ assume es:PSPSEG
+ movzx edx,segEnviron ;segment addr of environment
+ shl edx,4 ;linear address of env
+ mov ecx,7FFFh ;environments can only be 32k
+ ; byte by DOS
+ cCall NSetSegmentDscr,<SEL_ENVIRON,edx,ecx,STD_DATA>
+ pop es
+ assume es:DGROUP
+
+; Set up a descriptor pointing to the BIOS code and data areas
+
+ mov edx, 0f0000h ; set to linear f0000 (f000:0000)
+ mov ecx, 0ffffh ; make it be a 64k segment
+ cCall NSetSegmentDscr,<SEL_BIOSCODE,edx,ecx,STD_CODE>
+
+ mov edx, 40h * 16 ; linear byte addr of BIOS data area
+ cCall NSetSegmentDscr,<SEL_BIOSDATA,edx,ecx,STD_DATA>
+
+; Set up a descriptor pointing to the real mode interrupt vector table.
+ xor edx, edx ; the IVT is at linear address 0,
+ ;other registers are still set up
+ ; 64k data segment from last one
+ cCall NSetSegmentDscr,<SEL_RMIVT,edx,ecx,STD_DATA>
+
+; And, set up a selector for VCPI/WDEB386 to use to access all of memory.
+ xor edx,edx
+ mov ecx,edx
+ dec ecx
+ cCall NSetSegmentDscr,<SEL_VCPIALLMEM,edx,ecx,ARB_DATA0>
+
+; Set up the call gate descriptor for the reset to V86 mode routine.
+
+ mov ecx,offset DXCODE:ResetVCPI
+@@: mov edx,SEL_DXCODE0
+ cCall NSetSegmentDscr,<SEL_RESET,edx,ecx,STD_CALL>
+
+; Set up a call gate to invoke the VCPI pMode interface in ring 0.
+
+ mov ecx,offset DXCODE:CallVCPI
+ cCall NSetSegmentDscr,<SEL_CALLVCPI,edx,ecx,STD_CALL>
+
+; Init call gate descriptors for all the DynaLink services.
+
+if DEBUG ;------------------------------------------------------------
+
+ extrn DXOutDebugStr:NEAR
+
+ mov ax,SEL_DYNALINK + (OutDebugStr shl 3)
+ mov ecx,offset DXCODE:DXOutDebugStr
+ mov edx,(SEL_DXCODE0 or EH_RING) or 00080000h ;copy 8 stack words
+ cCall NSetSegmentDscr,<ax,edx,ecx,STD_CALL>
+
+ extrn DXTestDebugIns:NEAR
+
+ mov ax,SEL_DYNALINK + (TestDebugIns shl 3)
+ movzx edx,dx ;copy 0 stack words
+ mov ecx,offset DXCODE:DXTestDebugIns
+ cCall NSetSegmentDscr,<ax,edx,ecx,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>
+
+ clc
+cEnd
+
+
+;*** InitIDTVCPI ;[ds]
+;
+; Purpose: to initialize the idt when running under vcpi
+;
+; Register
+; Usage: eax, all others preserved
+;
+; Input: none
+;
+; Output: idt is initialized
+;
+; Returns: can't fail, cy clear
+;
+; Exceptions:
+;
+; Notes: only under real mode!!!!!
+;
+;************************************************************************/
+cProc InitIDTVCPI,<NEAR,PUBLIC>,<ebx,ecx,edx,di,es>
+cBegin
+
+ jnc @F
+ jmp InitIDTVCPI_ret
+@@:
+ movzx eax, segBootPmode ; set up segIDT,selIDT with
+ shl eax, 4 ; the RM seg of the idt
+ add eax, IDTOFF
+ ;
+ ; IDT must be paragraph aligned!!!!! (guarenteed--see dxvcpi.inc)
+ ;
+ shr eax, 4
+ mov segIDT, ax ; base of IDT
+ mov selIDT, ax ;
+
+ movzx ecx, cdscIDTMax ; number of descriptors in table
+ shl cx, 3 ; convert to count of bytes
+ dec cx ; compute segment size limit
+
+ mov bpIDTcb, cx ; set up idtr with limit,
+ mov edx, LADXIDTBASE ; ...and...
+ mov bpIDTbase, edx ; ...linear base
+ cCall NSetSegmentDscr,<SEL_IDT,edx,ecx,STD_DATA>
+
+; Fill the IDT with interrupt gates that point to the interrupt reflector
+; entry vector.
+
+ mov es, segIDT
+ xor di,di
+ mov dx,offset DXPMCODE:PMFaultEntryVector ;the 1st 32 go here
+ mov cx,32
+@@: 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 @b
+
+ mov dx,offset DXPMCODE:PMIntrEntryVector+(32*3) ; the rest go here
+ mov cx,cdscIDTMax
+ sub cx,32
+@@: 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,3
+ add di,8
+ loop @b
+
+; Now, fix up the ones that don't point to the interrupt reflector.
+
+ 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 ---------------------------------------------------------
+
+ 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
+ mov es:[70h*8].offDest,offset DXPMCODE:PMIntr70
+
+ clc ; return success
+InitIDTVCPI_ret:
+
+cEnd
+
+;*** InitTSSVCPI - initialize the tss for use under vcpi
+;
+; Purpose:
+;
+; Register
+; Usage: uses eax, all others preserved
+;
+; Input: none
+;
+; Output: tss is setup
+;
+; Returns: none
+;
+; Exceptions: none
+;
+; Notes: none
+;
+;************************************************************************/
+cProc InitTSSVCPI,<NEAR,PUBLIC>,<es,di>
+cBegin
+
+ jnc @F
+ jmp InitTSSVCPI_ret
+@@:
+ mov ax, segBootPmode
+ add ax, ( (TSSOFF shr 16d) shl 12d )
+ mov es, ax
+ mov di, TSSOFF AND 0FFFFH
+
+ mov es:[di + ts3_ldt],SEL_LDT ;set the LDT selector
+
+; Set the ring 0 stack seg/pointer, we don't bother to set the others
+; since nothing runs below user privilege level. Currently very little
+; code runs ring 0 - just when switching between real/proteted modes.
+
+ mov es:[di + ts3_ss0],SEL_DXDATA0
+ mov word ptr es:[di + ts3_esp0],offset DGROUP:rgw0Stack
+
+ clc
+InitTSSVCPI_ret:
+
+cEnd
+
+;
+;************************************************************************/
+;
+; The rest of this file is code which is called during protected mode
+; initialization.
+;
+;************************************************************************/
+;
+;**************************************************************
+;*** CallVCPIPM
+;
+; Utility routine to call VCPI server in protected mode. Masks out
+; interrupts during the call because QEMM enables the processor
+; interrupt flag when you call it.
+;
+; Entry: AX = VCPI function code.
+; Uses: Depends upon call.
+;
+; Note: There is a copy of this routine in dxvcpi.asm and another
+; in dxvcpibt.asm. This is to allow near calls. The copy
+; in dxvcpibt.asm is discarded after initialization time.
+;
+;**************************************************************
+cProc CallVCPIPM,<NEAR>,<si>
+cBegin
+
+ push ax ; save function code
+;
+; Shut out all interrupts.
+; QEMM 5.0 enables interrupts during this call. All our interrupt
+; handlers are in the user code ring. A workaround is to shut off
+; hardware interrupts during the call.
+;
+
+ in al,INTA01
+ IO_Delay
+ mov si, ax
+ mov al,0FFh
+ out INTA01,al
+ IO_Delay
+
+ pop ax ;restore function code
+ db 9Ah ;call far SEL_CALLVCPI:0
+ dw 0,SEL_CALLVCPI or STD_RING
+
+; Restore the state of the interrupt mask register
+
+ xchg si, ax
+ out INTA01,al
+ IO_Delay
+ xchg si, ax
+cEnd
+;**************************************************************
+; This variable is used to allow calling NSetSegmentDscr from
+; the DXCODE segment in protected mode.
+;
+NSetSegmentDscrCall label dword
+ dw OFFSET DXPMCODE:NSetSegmentDscr,SEL_DXPMCODE or STD_RING
+
+ extrn EnterProtectedMode:NEAR
+ extrn EnterRealMode:NEAR
+
+DXCODE ends
+
+DXPMCODE segment
+ extrn XMScontrol:FAR
+DXPMCODE ends
+
+DXCODE segment
+
+XMScontrolCall label dword
+ dw OFFSET DXPMCODE:XMScontrol,SEL_DXPMCODE or STD_RING
+
+pmxmssvc macro fcn
+ ifnb <fcn>
+ mov ah, fcn
+ endif
+ call XMScontrolCall
+endm
+
+;************************************************************************/
+;*** GrowPageTables
+;
+; Purpose: To grow the user memory page tables large enough to
+; accomodate any expected memory allocation request.
+;
+; Register
+; Usage: NONE
+;
+; Input: NONE
+;
+; Output: Page tables grown to anticipated demand. cPteMax updated.
+; Page table segment limit extended.
+;
+; Returns:
+;
+; Exceptions:
+;
+; Notes: Fails unless there is an XMS block to hold the new page
+; tables. Cannot extend the page tables using VCPI memory.
+;
+;************************************************************************/
+cProc GrowPageTables,<NEAR,PUBLIC>,<ebx,ecx,edx,si,di,es>
+
+cBegin
+
+ PMvcpi vcpiCFREEPAGES ; Fetch current VCPI free pages
+ mov ecx,edx
+ pmxmssvc 08h ; Query free extended memory
+ shr ax,2 ; convert to pages
+ movzx eax,ax
+ add ecx,eax ; ECX = theoretical pages we could
+ ; allocate
+ sub ecx,cPteMax ; minus what we have room for
+ jna GPT_ret ; enough space already
+;
+; So we work on machines with more than 64 Meg. RAM, we should put in a check
+; here to see whether it looks like we have more.
+;
+ shr ecx,8 ; ECX = kilobytes to hold this stuff
+;
+; Instead, we do this quick hack for now. If there appears to be more
+; than 60 Mb available, then grow the page tables to 512k.
+;
+ cmp ecx,60
+ jb @F
+ mov ecx,512
+@@:
+ mov dx,cx
+ add dx,3 ; need to page align
+
+ pmxmssvc 09h ; Allocate a chunk of XMS to hold it
+ or ax,ax
+ jz GPT_ret
+
+ lea si, hmem_XMS_Table ; SI points to saved handles buffer
+ inc [hmem_XMS_Count] ; bump XMS handle count
+ mov [si],dx ; save handle for later disposal
+
+ pmxmssvc 0Ch ; lock the handle
+ or ax,ax
+ jnz @F ; jump on success
+ mov dx,[si] ; couldn't lock handle
+ pmxmssvc 0Dh ; free it
+ dec [hmem_XMS_Count] ; decrement count of allocated handles
+ jmp GPT_ret ; out of here
+@@:
+ shl edx,16 ; EDX = physical address of extended
+ mov dx,bx ; page tables
+
+ shr edx,10
+ add edx,3 ; begin on a page boundary
+ shr edx,2
+ shl edx,12
+ or dl,NEWPTEMASK ; EDX = first PTE
+
+ mov selGDT,SEL_GDT
+ mov ax,SEL_DXPT
+ lsl ebx,eax
+ shl ecx,10 ; ECX = extra space in bytes
+ add ebx,ecx ; EBX = page table segment limit
+ mov eax, LADXPTBASE ; EAX = user page tables linear base
+ cCall [NSetSegmentDscrCall],<SEL_DXPT,eax,ebx,STD_DATA>
+ mov selGDT,SEL_LDT_ALIAS
+
+ mov eax,ecx
+ shr eax,2 ; ECX = new PTEs
+ add cPteMax,eax
+
+ add ecx,CBPAGE386 - 1
+ shr ecx,12 ; ECX = pages of page tables
+ mov ax,SEL_DXPD
+ mov es,ax
+ mov edi, ( CPTDX + 1 ) shl 2
+
+ push ecx
+ push edi
+ mov eax,edx
+
+;
+; Map the new page tables into the page directory.
+;
+GPT_add_PD:
+ stos dword ptr es:[edi]
+ add eax,CBPAGE386
+ dec ecx
+ jnz GPT_add_PD
+
+;
+; Map the new page tables into the page table segment, by copying
+; the page directory entries.
+;
+ mov eax, DXLINEARBASE + DXPTSYSOFF + ( USERPT shl 2 )
+ xor ecx,ecx
+ dec cx
+ cCall [NSetSegmentDscrCall],<SEL_SCR0,eax,ecx,STD_DATA>
+ pop edi
+ mov esi,edi
+ pop ecx
+ push ds
+ assume ds:nothing
+ mov ax,es
+ mov ds,ax
+ mov ax,SEL_SCR0 or STD_TBL_RING
+ mov es,ax
+ rep movsd
+ pop ds
+ assume ds:DGROUP
+
+GPT_ret:
+
+cEnd
+
+;************************************************************************/
+;*** VCPIBootStrap
+;
+; Purpose: Move the Pmode memory block into extended memory.
+;
+; Register Usage:
+; EAX, BX, CX, SI, DI, Flags.
+;
+; Input: System is in Pmode.
+;
+; Output: If successful, the entire block of memory at DXLINEARBASE
+; is copied to DX_TEMP_LINEARBASE. Then the page directory
+; and page tables are swapped around, and a new cr3 value is
+; loaded, so that the new block is placed at DXLINEARBASE.
+; The VCPI server reloads cr3.
+;
+; Exceptions:
+; No defined errors. A bug in this routine will most likely
+; crash the program.
+;
+; Returns: Nothing.
+;************************************************************************/
+
+cProc VCPIBootStrap,<NEAR,PUBLIC>,<eax,ecx,si,di>
+cBegin
+
+ assume ds:DGROUP,ss:DGROUP
+
+ call EnterProtectedMode
+
+ cmp fDebug,0
+ jz SkipVCPIDebugINIT
+
+ mov ax,SEL_DXCODE or STD_RING
+ mov es,ax
+ assume es:NOTHING
+ mov di,offset DXCODE:WdebVCPI
+ mov ax,DS_VCPI_Notify
+ int DebOut_Int
+
+ push ds
+ pop es
+ assume es:DGROUP
+SkipVCPIDebugINIT:
+
+ push es
+ push ds
+ assume ds:NOTHING,es:NOTHING
+ mov ax,SEL_LDT_ALIAS
+ mov ds,ax
+ mov ax,SEL_TSS_ALIAS
+ mov es,ax
+
+;
+; Copy the block of memory at DXLINEARBASE, which is really in a DOS
+; memory block in conventional memory, to DX_TEMP_LINEARBASE.
+;
+ xor si,si
+ mov di,si
+ mov cx,LDTOFF shr 2
+ rep movsd
+;
+; Manipulate the page tables and page directory in the extended
+; memory block, so that all our selectors point to the new area.
+;
+ mov ax,es
+ mov ds,ax
+ mov si,DXTEMPPTOFF
+ mov di,DXPTSYSOFF
+ mov cx,CBPAGE386 shr 2
+ rep movsd
+;
+; Zero out the temporary page table that was used to copy our stuff
+; up here.
+;
+ mov di,DXTEMPPTOFF
+ mov cx,CBPAGE386 shr 2
+ xor eax,eax
+ rep stosd
+;
+; Fill in the new page directory. First, the low memory page tables.
+;
+ mov si,DXPTSYSOFF + (VCPIPTOFF shr 10)
+ mov di,DXPDOFF
+ mov cx,CPTDX + 1
+ rep movsd
+;
+; Second, the entry for our high memory system area.
+;
+ mov si,DXPTSYSOFF + (DXPTSYSOFF shr 10)
+ mov di,DXPDOFF + (DXLINEARBASE shr 20)
+ movsd
+
+;
+; Copy the two user page table page table entries to their final location.
+;
+ mov cx,2
+ mov si,DXPTSYSOFF
+ mov di,DXPTSYSOFF + ( USERPT shl 2 )
+ rep movsd
+;
+; Zero out the original entries.
+;
+ mov cx,2
+ mov di,DXPTSYSOFF
+ xor eax,eax
+ rep stosd
+;
+; Move our protected mode code segment up.
+;
+ mov di,DXPMCODEOFF
+ mov ax,SEL_GDT
+ mov ds,ax
+ mov si,SEL_LDT_ALIAS
+ mov dx,DXPMCODE
+ mov ax,dx
+ shl ax,4
+ shr dx,12
+ mov ds:[si].adrBaseLow,ax
+ mov ds:[si].adrBaseHigh,dl
+ mov ds:[si].adrbBaseHi386,dh
+ mov cx,offset DXPMCODE:CodeEndPM + 10h
+ shr cx,2
+ xor si,si
+ mov ax,SEL_LDT_ALIAS
+ mov ds,ax
+ rep movsd
+;
+; Fetch page directory address for later cr3 loading.
+;
+ mov eax, es:[DXPTSYSOFF + (DXPDOFF shr 10)]
+ pop ds
+ assume ds:DGROUP
+ pop es
+ push eax
+
+ call EnterRealMode
+
+ pop eax ; get new page directory address
+ ; Save the pte (physical addr) of
+ and ax, 0f000h ; page directory, mask 12 lsb's
+ mov V86ToPm.zaCr3VTP, eax ; store it
+;
+; VCPI server will load new CR3 value when we do the next real
+; to protected mode switch.
+;
+ call EnterProtectedMode
+
+; Set up a descriptor for the LDT data alias.
+
+ mov edx,LADXLDTBASE
+ movzx ecx,cdscGDTMax
+ shl ecx,3
+ dec ecx
+ mov selGDT,SEL_GDT
+ cCall [NSetSegmentDscrCall],<SEL_LDT_ALIAS,edx,ecx,STD_DATA>
+ xor eax,eax
+ cCall [NSetSegmentDscrCall],<SEL_TSS_ALIAS,eax,eax,STD_DATA>
+ mov selGDT,SEL_LDT_ALIAS
+
+ call EnterRealMode
+
+cEnd
+
+;************************************************************************/
+;*** AddXMStoVCPIHeap
+;
+; Purpose: Allocate extended memory for the DOS Extender heap by
+; allocating and locking blocks of XMS memory. Fill in
+; user page tables to point to the allocated memory.
+;
+; Register
+; Usage: eax, edx
+;
+; Input: ES:DI points to beginning of user page tables.
+;
+; Output: ES:DI points to next unused page table entry.
+;
+; Returns:
+;
+; Exceptions:
+;
+; Notes: This function should not be called if memory has already been
+; allocated from another source.
+;
+;************************************************************************/
+cProc AddXMStoVCPIHeap,<FAR,PUBLIC>,<bx,cx,si>
+localD cVCPIFreePages
+cBegin
+ cCall GrowPageTables
+
+ lea si, hmem_XMS_Table ; SI points to saved handles buffer
+ mov ax,[hmem_XMS_Count] ; Point to the first free one.
+ shl ax,1
+ add si,ax
+
+AXVH_begin:
+
+ xor eax,eax
+ pmxmssvc 08h ; Query free extended memory
+ and ax,NOT 3 ; truncate to page size
+ or ax,ax ; any available?
+ jz AXVH_x ; no
+ mov ecx,cPteMax ; ECX = number of pages that will fit
+ shl ecx,2 ; ECX = number of kilobytes
+ cmp eax,ecx ; compare available to space in page tables
+ jb AXVH_0 ; will fit in page tables
+ mov ax,cx ; won't, ask for less
+AXVH_0:
+ mov cx,ax ; save copy of size
+ or cx,cx
+ jz AXVH_x ; none left
+ PMvcpi vcpiCFREEPAGES ; Store current VCPI free pages
+ mov cVCPIFreePages,edx
+ mov dx,cx ; dx = #kilobytes requested
+ pmxmssvc 09h ; allocate extended memory block
+ cmp ax,1 ; got a block?
+ jne AXVH_x ; no
+ mov [si],dx ; yes, got one, save handle
+ PMvcpi vcpiCFREEPAGES ; Fetch VCPI free pages
+ cmp edx,cVCPIFreePages ; Count still the same?
+ jne AXVH_0a ; No, allocate the memory from VCPI.
+ mov dx,[si] ; fetch handle
+ pmxmssvc 0ch ; lock the block
+ cmp ax,1 ; did it work?
+ je AXVH_1 ; yes
+AXVH_0a:
+ mov dx,[si] ; no, fetch handle, free it and exit
+ pmxmssvc 0ah
+ jmp AXVH_x ; (can't use unlocked blocks)
+AXVH_1: ; handle to locked block is in
+ ; [si], address in DX:BX
+ movzx eax,dx
+ shl eax,10h
+ mov ax,bx ; EAX = linear address of block
+ shr cx,2 ; CX = number of 386 pages in block
+
+ test ax,MASK allbitsPTE ; Page aligned?
+ jz AXVH_2 ; yes
+ and ax,NOT (MASK allbitsPTE)
+ add ax,CBPAGE386
+ dec cx
+ jcxz AXVH_x ; none left
+AXVH_2:
+ movzx ecx,cx
+ sub cPteMax,ecx ; this many PTEs used up
+ or ax,NEWPTEMASK ; make it a page table entry
+ mov edx,CBPAGE386
+ cld
+AXVH_AddPage:
+ stos dword ptr es:[edi]
+ add eax,edx
+ loop AXVH_AddPage
+
+ inc [hmem_XMS_Count] ; bump XMS handle count
+ add si,2 ; point to next slot in handle array
+ cmp si,(OFFSET hmem_XMS_Table) + ( CXMSBLOCKSDX * 2 )
+ jb AXVH_begin ; jump if more handles fit in array
+
+AXVH_x:
+cEnd
+
+
+DXCODE ends
+
+endif ;VCPI
+ end
diff --git a/private/mvdm/dpmi/gendefs.inc b/private/mvdm/dpmi/gendefs.inc
new file mode 100644
index 000000000..cb865ee17
--- /dev/null
+++ b/private/mvdm/dpmi/gendefs.inc
@@ -0,0 +1,654 @@
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* GENDEFS.INC -- General Symbol Defintions for Dos Extender *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 05/08/90 jimmat Changes for VCPI support. *
+;* 01/07/90 jimmat Make DOSX version 03.00. *
+;* 7/28/89 jimmat Increased GDT/LDT size yet again. *
+;* 4/01/89 jimmat Increased size of GDT/LDT (again) *
+;* 3/30/89 jimmat Added ChkPoint macro for debugging *
+;* 3/11/89 jimmat Added support for LDT & TSS *
+;* 3/09/89 jimmat Added DX_DYNALINK function *
+;* 02/22/89 (GeneA): increased size of interrupt reflector *
+;* stack frames (CB_STKFRAME) from 128 to 256 bytes *
+;* 02/17/89 (GeneA): removed ERC_??? equates *
+;* 02/10/89 (GeneA): changed form of definitions for the *
+;* EXEC_??? symbols, and added ERC_??? error code symbols. *
+;* 12/13/88 (GeneA): moved definitions for EXEC_??? symbols *
+;* and RELOC_BUFFER here from dxinit.asm
+;* 12/08/88 (GeneA): added npopf function *
+;* 12/08/88 (GeneA): added HMMFUNC definition *
+;* *
+;****************************************************************
+
+ifndef DEBUG ;define DEBUG switch if it isn't already
+DEBUG = 0
+endif
+ifndef MINBUG
+MINBUG = 0
+endif
+ifndef VCPI
+VCPI = 0
+endif
+ifndef NO386
+NO386 = 0
+endif
+ifndef NTDEBUG
+NTDEBUG = 0
+endif
+
+; -------------------------------------------------------
+; MISC. PROGRAM CONSTANTS
+; -------------------------------------------------------
+
+DXVERSION = 030Ah ;Version 03.10
+DPMI_32BIT equ 0000000000000001b
+
+if VCPI
+CDSCGDTDEFAULT = 3072 ;default size of GDT **** temp for VCPI **** !!!
+CDSCLDTDEFAULT = 2
+else
+CDSCGDTDEFAULT = 3072 ;default size of GDT
+endif
+
+CDSCMAXLDT = 8190 ;maximum possible LDT selectors
+
+CDSCIDTDEFAULT = 256 ;default size of IDT
+
+CB_XFRBUF0 = 128 ;size of small transfer buffer
+;
+; Performance enhancement, by RalphL
+;
+CB_XFRBUF1 = 8192 ;size of large transfer buffer
+
+CB_STKFRAME = 384 ;size of a stack frame
+
+CB_MEMHDR = 16 ; size of memory block header (1 paragraph)
+
+CXMSBLOCKSDX = 16 ; number of XMS blocks to allocate
+
+INTA00 = 20h ;i/o address of master 8259
+INTA01 = 21h
+INTB00 = 0A0h ;i/o address of slave 8259
+INTB01 = 0A1h
+
+CMOSLoc = 70h ;CMOS ram/Real-Time Clock location port
+CMOSValue = 71h ;CMOS ram/Real-Time Clock value port
+
+HP_VECTRA = 01h ;Running on an HP Vectra
+HP_CLASSIC = 02h ;Running on a 'Classic' Vectra (A & A+)
+
+CRESERVED = 32 ;Reserved interrupt numbers
+
+; The following equates define the DOS Extender DynaLink services
+
+if DEBUG
+
+OutDebugStr = 0 ;debugging text out service
+TestDebugIns = 1 ;debugger installation check service
+
+NUM_DYNALINK = 2
+
+else ; !DEBUG
+
+NUM_DYNALINK = 0
+
+endif
+
+
+
+; This structure defines the format of the Exec paramter block used
+; by the MS-DOS exec function (ah=4Bh, al=01).
+
+XBLK struc
+
+segEnv dw ? ;segment address of environment to use
+lpchCmnd dd ? ;far pointer to command line
+lpFCB0 dd ? ;far pointer to first default fcb
+lpFCB1 dd ? ;far pointer to second default fcb
+lpChildStack dd ? ;return value, far pointer to child stack
+lpChildCode dd ? ;return value, far pointer to child code
+
+XBLK ends
+
+
+; The following symbols define locations in rgbXfrBuf1 used
+; during initialization. ParseCommandLine places this information
+; into the buffer to be used by later programs.
+
+BUFTMP = CB_XFRBUF1 - 1024 ;use the top 1k for the temporary buffers
+
+EXEC_PROGNAME equ <DGROUP:rgbXfrBuf1+BUFTMP+0000h> ;child program's exe file name
+EXEC_DXNAME equ <DGROUP:rgbXfrBuf1+BUFTMP+0080h> ;Dos Extender program name
+EXEC_CMNDLINE equ <DGROUP:rgbXfrBuf1+BUFTMP+0100h> ;command line to pass to the child
+EXEC_EXEHDR equ <DGROUP:rgbXfrBuf1+BUFTMP+0180h> ;buffer for holding exe header for
+ ; the overlay currently being
+ ; loaded
+EXEC_FCB0 equ <DGROUP:rgbXfrBuf1+BUFTMP+01A0h> ;FCB0 to pass to the child
+EXEC_FCB1 equ <DGROUP:rgbXfrBuf1+BUFTMP+01D0h> ;FCB1 to pass to the child
+RELOC_BUFFER equ <DGROUP:rgbXfrBuf1+BUFTMP+0200h> ;file input buffer used to hold
+ ; pages of the relocation table
+ ; while relocating the current
+ ; overlay
+;
+; This structure defines the stack frame used to hold the entry
+; values and automatic variables for Dos Extender functions.
+
+FUNCSTACK struc
+
+fnsUserES dw ?
+fnsUserDS dw ?
+fnsUserDI dw ?
+fnsUserSI dw ?
+fnsUserBP dw ?
+fnsUserSPx dw ?
+fnsUserBX dw ?
+fnsUserDX dw ?
+fnsUserCX dw ?
+fnsUserAX dw ?
+
+fnsUserIP dw ?
+fnsUserCS dw ?
+fnsUserFL dw ?
+
+fnsUserSS dw ?
+fnsUserSP dw ?
+
+FUNCSTACK ends
+
+; -------------------------------------------------------
+; DEFINITIONS FOR MACROS
+; -------------------------------------------------------
+
+; These macros are used to switch between the protected mode
+; only code segment and the mixed protected/real mode code segment.
+; They are intended to be used for switching between modes in-line
+; within a routine. They must be used in pairs, bracketing the
+; code that needs to be in the other mode (from the initial or 'native'
+; mode of the function.)
+
+; -------------------------------------------------------
+;
+; This macro switches the processor and the local segment context
+; to the dos extender protected mode segment.
+
+SwitchToProtectedMode macro
+ local foo
+
+ ifndef EnterProtectedMode
+ extrn EnterProtectedMode:NEAR
+ endif
+
+ call EnterProtectedMode
+ % ifidni <@CURSEG>,<DXCODE>
+; jmp far ptr foo
+ db 0EAh ;avoid need for fixups
+ dw offset DXPMCODE:foo
+ dw SEL_DXPMCODE OR STD_RING
+DXCODE ends
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+ endif
+foo:
+ endm
+
+; -------------------------------------------------------
+;
+; This macro switches the processor and the local segment context
+; to the dos extender mixed protected/real mode segment.
+
+SwitchToRealMode macro
+ local foo
+
+ % ifidni <@CURSEG>,<DXPMCODE>
+; jmp far ptr foo
+ db 0EAh ;avoid need for fixups
+ dw offset DXCODE:foo
+ dw SEL_DXCODE OR STD_RING
+DXPMCODE ends
+
+
+DXCODE segment
+ assume cs:DXCODE
+ endif
+
+ ifndef EnterRealMode
+ extrn EnterRealMode:NEAR
+ endif
+
+foo:
+ call EnterRealMode
+
+ endm
+
+
+; -------------------------------------------------------
+;
+
+BeginLowSegment macro
+
+DXCODE segment
+ assume cs:DXCODE
+
+ endm
+
+EndLowSegment macro
+
+DXCODE ends
+
+ endm
+
+
+BeginHighSegment macro
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+
+ endm
+
+EndHighSegment macro
+
+DXPMCODE ends
+
+ endm
+
+; -------------------------------------------------------
+;
+; This macro switches the local segment context
+; to the dos extender protected mode segment.
+
+ChangeToHighSegment macro
+ local foo
+
+ % ifidni <@CURSEG>,<DXCODE>
+ .err
+ %out Switch to high segment when already in high segment
+ endif
+ jmp far ptr foo
+DXCODE ends
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+foo:
+ endm
+
+; -------------------------------------------------------
+;
+; This macro switches the local segment context
+; to the dos extender mixed protected/real mode segment.
+
+ChangeToLowSegment macro
+ local foo
+ % ifidni <@CURSEG>,<DXCODE>
+ .err
+ %out Switch to low segment when already in low segment
+ endif
+
+ jmp far ptr foo
+DXPMCODE ends
+
+DXCODE segment
+ assume cs:DXCODE
+foo:
+ endm
+
+
+; -------------------------------------------------------
+;
+
+PushCParams MACRO P1, P2, P3, P4, P5, P6, P7, P8, P9, P10
+ IRP Param, <P10, P9, P8, P7, P6, P5, P4, P3, P2, P1>
+ IFNB <Param>
+ push Param
+ ENDIF
+ ENDM
+ ENDM
+
+ClearCParams MACRO Count, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10
+IFNB <P1>
+ ClearCParams %(Count+1), <P2>, <P3>, <P4>, <P5>, <P6>, <P7>, <P8>, <P9>, <P10>
+ELSE
+IF Count
+ add sp, Count*2
+ENDIF
+ENDIF
+ ENDM
+
+
+; -------------------------------------------------------
+; This macro calls one of the DX services
+
+DXcall MACRO Procedure, Param_List
+IFNDEF WOW_x86 ; BUGBUG
+ PushCParams Param_List
+ db 9Ah ;call sel:0
+ dw 0
+ dw SEL_DYNALINK + (Procedure SHL 3)
+ ClearCParams 0, Param_List
+ENDIF
+ ENDM
+
+; -------------------------------------------------------
+; This macro displays a string if DEBUG
+;
+; Note: This macro only works in PROTECT MODE! **********
+
+Trace_Out MACRO String, nocrlf
+ LOCAL String_Offset
+ifndef WOW_x86 ;bugbug
+IF DEBUG
+
+DXDATA SEGMENT
+String_Offset LABEL BYTE
+ db String
+IFB <nocrlf>
+ db 0Ah, 0Dh
+ENDIF
+ db 0
+DXDATA ENDS
+
+ push ds
+ pushf
+ cli
+ pusha ; save our registers
+ pusha ; copy them for print routine
+
+ mov ax,SEL_DXDATA ; make sure string is addressable
+ mov ds,ax
+
+ mov si,OFFSET DGROUP:String_Offset
+ DXcall OutDebugStr
+
+ popa ; restore our registers
+
+ npopf
+ pop ds
+ENDIF
+endif ;WOW_x86
+ ENDM
+
+; -------------------------------------------------------
+; This macro displays a string and breaks if DEBUG
+;
+; Note: This macro only works in PROTECT MODE! **********
+
+Debug_Out MACRO String
+ LOCAL Skip_Int1
+IF DEBUG
+ pushf
+ Trace_Out <String>
+ DXcall TestDebugIns
+ jz SHORT Skip_Int1
+ int 1
+Skip_Int1:
+ npopf
+ENDIF
+ ENDM
+
+; -------------------------------------------------------
+; This macro displays a string if DEBUG
+;
+; Note: This macro only works in REAL MODE! **********
+;
+; DS must be set to the DOS Extender's DS before using, too.
+;
+
+Real_Trace_Out MACRO String, nocrlf
+ LOCAL String_Offset
+IF DEBUG
+
+ifndef DXOutDebugStr
+ EXTRN DXOutDebugStr:FAR
+endif
+
+DXDATA SEGMENT
+String_Offset LABEL BYTE
+ db String
+IFB <nocrlf>
+ db 0Ah, 0Dh
+ENDIF
+ db 0
+DXDATA ENDS
+
+ pushf
+ cli
+ pusha ; save our registers
+ pusha ; copy them for print routine
+
+ mov si,OFFSET DGROUP:String_Offset
+ call DXOutDebugStr
+
+ popa ; restore our registers
+
+ npopf
+ENDIF
+ ENDM
+
+; -------------------------------------------------------
+; This macro displays a string and breaks if DEBUG
+;
+; Note: This macro only works in REAL MODE! **********
+;
+; DS must be set to the DOS Extender's DS before using, too.
+;
+
+Real_Debug_Out MACRO String
+ LOCAL Skip_Int1
+IF DEBUG
+ pushf
+ Real_Trace_Out <String>
+ cmp fDebug,0
+ jz SHORT Skip_Int1
+ int 1
+Skip_Int1:
+ npopf
+ENDIF
+ ENDM
+
+; -------------------------------------------------------
+; These macros updates an internal trace table
+;
+
+DEBUG_TRACE MACRO type, op1, op2, op3
+IF NTDEBUG
+ifndef DebugTrace
+ extrn DebugTrace:NEAR
+endif
+
+ push op3
+ push op2
+ push op1
+ push type
+ call DebugTrace
+ENDIF
+ ENDM
+
+DEBUG_TRACE_RM MACRO type, op1, op2, op3
+IF NTDEBUG
+ifndef DebugTraceRm
+ extrn DebugTraceRm:NEAR
+endif
+
+ push op3
+ push op2
+ push op1
+ push type
+ call DebugTraceRm
+ENDIF
+ ENDM
+
+
+IF NTDEBUG
+DBGTR_EXIT equ 0
+DBGTR_ENTRY equ 1
+DBGTR_HWINT equ 2
+DBGTR_STACKPTR equ 3
+ENDIF
+
+; -------------------------------------------------------
+; Delay for I/O Instructions...
+
+IO_Delay MACRO
+ jmp short $+2
+ jmp short $+2
+ jmp short $+2
+ ENDM
+
+; -------------------------------------------------------
+;8080-style return macros due to Greg Whitten
+
+genrcc macro cc
+r&cc &macro labl
+if $-___ret gt 126d
+ jn&cc $+3 ;;Skip around ret
+___ret = $
+ ret
+else
+ j&cc ___ret ;;jmp to last ret
+endif
+ &endm
+
+rn&cc &macro labl
+if $-___ret gt 126d
+ j&cc $+3 ;;Skip around ret
+___ret = $
+ ret
+else
+ jn&cc ___ret ;;jmp to last ret
+endif
+ &endm
+
+call&cc &macro labl
+ jn&cc $+5 ;;Skip around call
+ call labl
+ &endm
+
+calln&cc &macro labl
+ j&cc $+5 ;;Skip around call
+ call labl
+ &endm
+
+jmp&cc &macro labl
+ jn&cc $+5 ;;Skip around jmp
+ jmp labl
+ &endm
+
+jmpn&cc &macro labl
+ j&cc $+5 ;;Skip around jmp
+ jmp labl
+ &endm
+
+ endm
+
+; -------------------------------------------------------
+; rcc and rncc macro generators
+
+genrcc a
+genrcc ae
+genrcc b
+genrcc be
+genrcc c
+genrcc e
+genrcc g
+genrcc ge
+genrcc l
+genrcc le
+genrcc o
+genrcc s
+genrcc z
+
+return macro
+___ret = $
+ ret
+ endm
+
+; -------------------------------------------------------
+;
+; This macro is used to get around the bug in the POPF instruction in
+; some early 80286 chips.
+IFDEF WOW
+npopf macro
+ rpopf
+ endm
+ELSE
+npopf macro
+ local a
+ jmp $+3 ; Fix for bug in POPF on IBM AT
+a: iret ; This simulates a POPF instruction
+ push cs
+ call a
+endm
+ENDIF
+; -------------------------------------------------------
+;
+; MS-DOS function call macros
+
+dossvc macro fcn
+ ifnb <fcn>
+ mov ah,fcn
+ endif
+ int 21h
+ endm
+
+pmdossvc macro fcn
+ ifnb <fcn>
+ mov ah,fcn
+ endif
+ pushf ;don't do Int 21h in case child has Int 21h
+ cli ; hooked (like Windows)
+ push cs
+ call PmIntrDos
+ endm
+
+; -------------------------------------------------------
+; This macro will request a Dos Extender funtion.
+
+dxsvc macro fcn
+ ifnb <fcn>
+ mov al,fcn
+ endif
+ mov ah,DXFUNC
+ int 2Fh
+ endm
+
+; -------------------------------------------------------
+; This macro is used to make calls to the XMS driver for
+; extended memory management.
+
+HMMFUNC = 43h ;Himem driver Int 2Fh function code
+
+xmssvc macro fcn
+ ifnb <fcn>
+ mov ah,fcn
+ endif
+ call [lpfnXMSFunc]
+ endm
+
+; -------------------------------------------------------
+
+out1 macro p
+ if1
+ %out p
+ endif
+ endm
+
+; -------------------------------------------------------
+
+errnz macro p
+ if2
+ .errnz p
+ endif
+ endm
+
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+
+;****************************************************************
diff --git a/private/mvdm/dpmi/hostdata.inc b/private/mvdm/dpmi/hostdata.inc
new file mode 100644
index 000000000..0202711ef
--- /dev/null
+++ b/private/mvdm/dpmi/hostdata.inc
@@ -0,0 +1,79 @@
+;*++
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; Hostdata.inc
+;
+;Abstract:
+;
+; This module contains definitions associated with the host private data
+; for the NT vdm dpmi host
+;
+;Author:
+;
+; Dave Hastings (daveh) 18-Mar-1992
+;
+;
+;Revision History:
+;*++
+
+;
+; Note: the following structure should match the pmUser portion of the
+; INTRSTACK structure.
+;
+AppState struc
+IFDEF DPMI32
+AsES dd ?
+AsDS dd ?
+AsDI dd ?
+AsSI dd ?
+AsBP dd ?
+AsSPx dd ?
+AsBX dd ?
+AsDX dd ?
+AsCX dd ?
+AsAX dd ?
+AsFL dd ?
+AsSS dd ?
+AsSP dd ?
+ELSE
+AsES dw ?
+AsDS dw ?
+AsDI dw ?
+AsSI dw ?
+AsBP dw ?
+AsSPx dw ?
+AsBX dw ?
+AsDX dw ?
+AsCX dw ?
+AsAX dw ?
+AsFL dw ?
+AsSS dw ?
+AsSP dw ?
+ENDIF
+AppState ends
+
+IFDEF DPMI32
+APPSTATE_SIZE equ 52
+ELSE
+APPSTATE_SIZE equ 26
+ENDIF
+
+HostData struc
+Hdflags dw 0
+HdSelParent dw 0
+HdSegParent dw 0
+HdSelPSP dw 0
+HdPSPParent dw 0
+HdPspTerminate dd 0
+HdState db APPSTATE_SIZE dup (0)
+HostData ends
+
+IFDEF DPMI32
+HOST_DATA_SIZE equ 58
+ELSE
+HOST_DATA_SIZE equ 36
+ENDIF
+
+HD_32BITAPP equ 1
diff --git a/private/mvdm/dpmi/interupt.inc b/private/mvdm/dpmi/interupt.inc
new file mode 100644
index 000000000..faccce977
--- /dev/null
+++ b/private/mvdm/dpmi/interupt.inc
@@ -0,0 +1,52 @@
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; INTERUPT.INC - Definitions for 286 DOS Exterder Interrupt Handlers
+;
+;-----------------------------------------------------------------------
+;
+; 04/18/89 jimmat Original version.
+;
+;***********************************************************************
+
+; This structure defines the stack frame used to hold the register
+; values Interrupt Reflector functions.
+
+INTRSTACK struc
+
+intUserES dw ?
+intUserDS dw ?
+intUserDI dw ?
+intUserSI dw ?
+intUserBP dw ?
+intUserSPx dw ?
+intUserBX dw ?
+intUserDX dw ?
+intUserCX dw ?
+intUserAX dw ?
+
+intUserFL dw ?
+
+pmUserES dw ?
+pmUserDS dw ?
+pmUserDI dw ?
+pmUserSI dw ?
+pmUserBP dw ?
+pmUserSPx dw ?
+pmUserBX dw ?
+pmUserDX dw ?
+pmUserCX dw ?
+pmUserAX dw ?
+
+pmUserFL dw ?
+
+pmUserSS dw ?
+pmUserSP dw ?
+
+wParam1 dw ?
+wParam2 dw ?
+lParam dd ?
+
+INTRSTACK ends
diff --git a/private/mvdm/dpmi/makefile b/private/mvdm/dpmi/makefile
new file mode 100644
index 000000000..d7809a388
--- /dev/null
+++ b/private/mvdm/dpmi/makefile
@@ -0,0 +1,220 @@
+########## Path definition so we find 16 bit tools ##########
+# Also works around stupid bug in RC 3.1 that doesn't allow rcpp.err to be
+# in a directory that is greater than 128 chars down the path, even if
+# rc 3.1 is running as an OS/2 app.
+
+PATH = $(_NTBINDIR)\private\mvdm\tools16;$(PATH)
+
+SRC = .
+DEST = .
+DEST486 = .\486
+INCLUDE = -I..\wow16\inc -I..\inc
+LINKCMD486 = dosx.exe/exepack/far/map/cp:1,dosx.map;
+!IFNDEF COUNTRY
+COUNTRY=usa
+!ENDIF
+
+!IFDEF MIPS
+PLATFORM = MIPS
+!ELSE
+!IFDEF ALPHA
+PLATFORM = ALPHA
+!ELSE
+!IFDEF PPC
+PLATFORM = PPC
+!ELSE
+!IFDEF I386
+PLATFORM = x86
+!ELSE
+PLATFORM = $(PROCESSOR_ARCHITECTURE)
+!ENDIF
+!ENDIF
+!ENDIF
+!ENDIF
+
+LNKFILE = dosx.lnk
+
+!IF "$(PLATFORM)" == "x86"
+EXTRA_OPTIONS = -DWOW -DWOW_$(PLATFORM) -Di386 -DFLATAPIXLAT -DXMEMNT -DMD
+LNKFILE = dosxi.lnk
+!ENDIF
+
+OPTIONS = -DDEBUG=0 -DDBG=0 $(EXTRA_OPTIONS) -D?QUIET
+!if "$(NTDEBUG)"!="" && "$(NTDEBUG)"!="retail" && "$(NTDEBUG)" != "ntsdnodbg"
+OPTIONS486 = -DDEBUG=0 -DDBG=1 -DWOW -DWOW_$(PLATFORM) -DFLATAPIXLAT -D?QUIET
+!else
+OPTIONS486 = -DDEBUG=0 -DDBG=0 -DWOW -DWOW_$(PLATFORM) -DFLATAPIXLAT -D?QUIET
+!endif
+MFLAGS = -t
+
+# !IFDEF NTDEBUG
+# OPTIONS = $(OPTIONS) -DNTDEBUG=1
+# !ENDIF
+VCPI = -DVCPI=1
+ASM = masm $(INCLUDE) $(OPTIONS) $(MFLAGS)
+ASM486 = masm $(INCLUDE) $(OPTIONS486) $(MFLAGS)
+MAKE = nmake
+
+OBJS486 = $(DEST486)\dxstrt.obj $(DEST486)\dxmain.obj $(DEST486)\dxintr.obj \
+ $(DEST486)\dxfunc.obj $(DEST486)\dxutil.obj \
+ $(DEST486)\dxmmgr.obj $(DEST486)\dxend.obj \
+ $(DEST486)\dxboot.obj $(DEST486)\dxmsg.obj $(DEST486)\dxbug.obj \
+ $(DEST486)\dxnetbio.obj $(DEST486)\dxint31.obj $(DEST486)\dxdisk.obj \
+ $(DEST486)\dxini.obj $(DEST486)\dxoem.obj $(DEST486)\dxemm.obj \
+ $(DEST486)\dxendpm.obj $(DEST486)\dxfind.obj \
+ $(DEST486)\dxdma.obj $(DEST486)\dxemm2.obj \
+!IF "$(PLATFORM)" == "x86"
+ $(DEST486)\ntnpxem.obj \
+!ENDIF
+ $(DEST486)\dxrom.obj
+
+!if 0
+ VCPIOBJ = $(DEST)\dxvcpibt.obj $(DEST)\dxvcpi.obj
+!endif
+
+all: $(DEST486)\dosx.exe
+ binplace $(DEST486)\dosx.exe $(DEST486)\dosx.map $(DEST486)\dosx.sym
+
+$(DEST486)\dosx.exe: $(OBJS486) dosx.lnk dosx.def
+ cd $(DEST486)
+ copy ..\$(LNKFILE) tmp.lnk
+ echo $(LINKCMD486) >>$(SRC)\tmp.lnk
+ link16 @tmp.lnk
+ del $(SRC)\tmp.lnk
+ mapsym dosx
+
+ cd ..
+
+clean: cleanup all
+
+cleanup:
+ -del *.obj
+ -del 486\*.obj
+ -del 486\dosx.map
+ -del 486\dosx.sym
+ -del 486\dosx.exe
+
+depend:
+ copy makefile makefile.old
+ sed "/^# Dependencies follow/,/^# see depend: above/D" makefile.old > makefile
+ echo # Dependencies follow >> makefile
+ includes $(INCLUDE) *.asm | sed -f program.sed>> makefile
+ echo # IF YOU PUT STUFF HERE IT WILL GET BLASTED >> makefile
+ echo # see depend: above >> makefile
+
+
+
+# ****** NEW 486 CPU build instructions
+
+$(DEST486)\dxboot.obj dxboot.lst: 486\dxboot.asm ./cmacros.inc dxrom.inc dxvcpi.inc \
+ gendefs.inc pmdefs.inc segdefs.inc smartdrv.inc ..\inc\intmac.inc \
+ ..\inc\vdmtib.inc
+ $(ASM486) 486\dxboot,$(DEST486)\;
+
+$(DEST486)\dxbug.obj dxbug.lst: dxbug.asm ./cmacros.inc gendefs.inc pmdefs.inc \
+ segdefs.inc
+ $(ASM486) dxbug,$(DEST486)\;
+
+$(DEST486)\dxdisk.obj dxdisk.lst: dxdisk.asm ./cmacros.inc dxrom.inc gendefs.inc \
+ interupt.inc pmdefs.inc segdefs.inc ..\inc\intmac.inc
+ $(ASM486) dxdisk,$(DEST486)\;
+
+$(DEST486)\dxdma.obj dxdma.lst: dxdma.asm ./cmacros.inc dxvcpi.inc gendefs.inc \
+ interupt.inc pmdefs.inc segdefs.inc
+ $(ASM486) dxdma,$(DEST486)\;
+
+$(DEST486)\dxemm.obj dxemm.lst: dxemm.asm ./cmacros.inc dxrom.inc gendefs.inc \
+ segdefs.inc
+ $(ASM486) dxemm,$(DEST486)\;
+
+$(DEST486)\dxemm2.obj dxemm2.lst: dxemm2.asm ./cmacros.inc gendefs.inc segdefs.inc
+ $(ASM486) dxemm2,$(DEST486)\;
+
+$(DEST486)\dxend.obj dxend.lst: dxend.asm ./cmacros.inc dxvcpi.inc gendefs.inc \
+ pmdefs.inc segdefs.inc
+ $(ASM486) dxend,$(DEST486)\;
+
+$(DEST486)\dxendpm.obj dxendpm.lst: dxendpm.asm ./cmacros.inc segdefs.inc
+ $(ASM486) dxendpm,$(DEST486)\;
+
+$(DEST486)\dxfind.obj dxfind.lst: dxfind.asm ./cmacros.inc gendefs.inc segdefs.inc
+ $(ASM486) dxfind,$(DEST486)\;
+
+$(DEST486)\dxfunc.obj dxfunc.lst: 486\dxfunc.asm ../wow16/inc/dosx.inc ./cmacros.inc \
+ dxrom.inc gendefs.inc pmdefs.inc segdefs.inc woaswapi.inc ..\inc\intmac.inc \
+ stackchk.inc
+ $(ASM486) 486\dxfunc,$(DEST486)\;
+
+$(DEST486)\dxhpbios.obj dxhpbios.lst: dxhpbios.asm ./cmacros.inc dxrom.inc \
+ gendefs.inc interupt.inc pmdefs.inc segdefs.inc ..\inc\intmac.inc \
+ stackchk.inc
+ $(ASM486) dxhpbios,$(DEST486)\;
+
+$(DEST486)\dxini.obj dxini.lst: dxini.asm ./cmacros.inc gendefs.inc segdefs.inc \
+ ..\inc\intmac.inc
+ $(ASM486) dxini,$(DEST486)\;
+
+$(DEST486)\dxint31.obj dxint31.lst: 486\dxint31.asm ../wow16/inc/int31.inc ./cmacros.inc \
+ dxrom.inc gendefs.inc interupt.inc pmdefs.inc segdefs.inc ..\inc\intmac.inc \
+ stackchk.inc
+ $(ASM486) 486\dxint31,$(DEST486)\;
+
+$(DEST486)\dxintr.obj dxintr.lst: 486\dxintr.asm ./cmacros.inc dxrom.inc dxvcpi.inc \
+ gendefs.inc interupt.inc pmdefs.inc segdefs.inc ..\inc\intmac.inc \
+ stackchk.inc ..\inc\vdmtib.inc
+ $(ASM486) 486\dxintr,$(DEST486)\;
+
+$(DEST486)\dxmain.obj dxmain.lst: dxmain.asm ./cmacros.inc dxrom.inc gendefs.inc \
+ pmdefs.inc segdefs.inc
+ $(ASM486) 486\dxmain,$(DEST486)\;
+
+$(DEST486)\dxmmgr.obj dxmmgr.lst: dxmmgr.asm ./cmacros.inc dxvcpi.inc gendefs.inc \
+ pmdefs.inc segdefs.inc woaswapi.inc
+ $(ASM486) dxmmgr,$(DEST486)\;
+
+dxmsg.asm: $(COUNTRY)\dxmsg.asm
+ copy $(COUNTRY)\dxmsg.asm .
+
+$(DEST486)\dxmsg.obj dxmsg.lst: dxmsg.asm ./cmacros.inc gendefs.inc segdefs.inc
+ $(ASM486) dxmsg,$(DEST486)\;
+
+$(DEST486)\dxnetbio.obj dxnetbio.lst: dxnetbio.asm ./cmacros.inc dxrom.inc \
+ gendefs.inc interupt.inc netbios.inc pmdefs.inc segdefs.inc ..\inc\intmac.inc \
+ stackchk.inc
+ $(ASM486) dxnetbio,$(DEST486)\;
+
+$(DEST486)\dxoem.obj dxoem.lst: dxoem.asm ./cmacros.inc gendefs.inc pmdefs.inc \
+ segdefs.inc
+ $(ASM486) dxoem,$(DEST486)\;
+
+$(DEST486)\dxrom.obj dxrom.lst: dxrom.asm ./cmacros.inc dxrom.inc gendefs.inc \
+ pmdefs.inc segdefs.inc
+ $(ASM486) dxrom,$(DEST486)\;
+
+$(DEST486)\dxstrt.obj dxstrt.lst: 486\dxstrt.asm ./cmacros.inc dxrom.inc dxvcpi.inc \
+ gendefs.inc pmdefs.inc segdefs.inc smartdrv.inc ..\inc\intmac.inc
+ $(ASM486) 486\dxstrt,$(DEST486)\;
+
+$(DEST486)\dxutil.obj dxutil.lst: 486\dxutil.asm ../inc/bop.inc ../inc/dpmi.inc \
+ ./cmacros.inc dxrom.inc dxvcpi.inc gendefs.inc pmdefs.inc ..\inc\intmac.inc
+ $(ASM486) 486\dxutil,$(DEST486)\;
+
+$(DEST486)\dxvcpi.obj dxvcpi.lst: dxvcpi.asm ./cmacros.inc dxvcpi.inc gendefs.inc \
+ pmdefs.inc prot386.inc segdefs.inc
+ $(ASM486) dxvcpi,$(DEST486)\;
+
+$(DEST486)\dxvcpibt.obj dxvcpibt.lst: dxvcpibt.asm ./cmacros.inc dxvcpi.inc \
+ gendefs.inc pmdefs.inc prot386.inc segdefs.inc
+ $(ASM486) dxvcpibt,$(DEST486)\;
+
+$(DEST486)\ntnpxem.obj ntnpxem.lst: ntnpxem.asm \
+ segdefs.inc gendefs.inc pmdefs.inc ..\inc\intmac.inc
+ $(ASM486) ntnpxem,$(DEST486)\;
+
+# IF YOU PUT STUFF HERE IT WILL GET BLASTED
+# see depend: above
+
+..\inc\dpmi.inc : ..\inc\dpmi.h
+ cd ..\inc
+ nmake dpmi.inc
+ cd ..\dpmi
diff --git a/private/mvdm/dpmi/migrate.bat b/private/mvdm/dpmi/migrate.bat
new file mode 100644
index 000000000..3644037a0
--- /dev/null
+++ b/private/mvdm/dpmi/migrate.bat
@@ -0,0 +1,20 @@
+ @echo off
+ to dev
+ cd system
+rem UPDATE DEBUG VERSION
+
+ out dosx.exe dosx.sym
+
+ copy d:\win30\dosx\dosx.exe
+ copy d:\win30\dosx\dosx.sym
+
+rem UPDATE RETAIL VERSION
+ cd ..\retail
+
+ out dosx.exe dosx.sym
+
+ copy d:\win30\dosx\retail\dosx.exe
+ copy d:\win30\dosx\retail\dosx.sym
+
+to win30
+
diff --git a/private/mvdm/dpmi/mk.cmd b/private/mvdm/dpmi/mk.cmd
new file mode 100644
index 000000000..3f38f1816
--- /dev/null
+++ b/private/mvdm/dpmi/mk.cmd
@@ -0,0 +1,57 @@
+echo off
+if "%1"=="" goto debug
+if "%1"=="all" goto debug
+if "%1"=="debug" goto debug
+if "%1"=="retail" goto retail
+if "%1"=="rom" goto rom
+
+echo Usage:
+echo mk - Make debug version
+echo mk debug - Make debug version
+echo mk retail - Make retail version
+echo mk rom [debug \ retail] - Make ROM version
+echo mk all - Make ALL versions!
+goto end
+
+rem ##
+rem ## Make debug version
+rem ##
+:debug
+
+nmake OPTIONS="-DDEBUG=1"
+
+if "%1"=="all" goto retail
+goto end
+
+rem ##
+rem ## Make retail version
+rem ##
+:retail
+
+nmake SRC=".." DEST="retail" OPTIONS=""
+
+if "%1"=="all" goto rom
+goto end
+
+rem ##
+rem ## Make debug ROM Windows version
+rem ##
+:rom
+
+if "%2"=="retail" goto rrom
+
+nmake SRC=".." DEST="rom" OPTIONS="-DDEBUG=1 -DROM=1" VCPI="" LINKCMD="dosx.exe/far/ma,dosx.map,,$(SRC)\dosx.def;"
+
+if "%1"=="all" goto rrom
+goto end
+
+rem ##
+rem ## Make retail ROM Windows version
+rem ##
+:rrom
+
+nmake SRC=".." DEST="rrom" OPTIONS="-DROM=1" VCPI="" LINKCMD="dosx.exe/far/ma,dosx.map,,$(SRC)\dosx.def;"
+goto end
+
+
+:end
diff --git a/private/mvdm/dpmi/netbios.inc b/private/mvdm/dpmi/netbios.inc
new file mode 100644
index 000000000..01f081203
--- /dev/null
+++ b/private/mvdm/dpmi/netbios.inc
@@ -0,0 +1,106 @@
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; NETBIOS.INC - NetBIOS Definitions for 286 DOS Extender
+;
+;-----------------------------------------------------------------------
+;
+; 04/18/89 jimmat Original version.
+;
+;***********************************************************************
+
+; NetBIOS return codes
+
+RC_Good EQU 000h
+RC_Invalid_Cmd EQU 003h
+RC_Max_Cmd EQU 022h
+RC_In_Progress EQU 024h
+RC_Pending EQU 0FFh
+
+RC_Resources EQU 035h ; OS/2 (!) Required OS Resources Exhausted
+
+
+; Some NetBIOS command codes
+
+SessCall EQU 10h ; Call command
+SessListen EQU 11h ; Listen command
+HangUp EQU 12h ; Hang up command
+Send EQU 14h ; Send command
+Recv EQU 15h ; Recv command
+RecvAny EQU 16h ; Recv any command
+ChainSend EQU 17h ; Chain send command
+SendGram EQU 20h ; Send datagram
+RecvGram EQU 21h ; Recv datagram
+SendBroad EQU 22h ; Send broadcast datagram
+RecvBroad EQU 23h ; Recv broadcast datagram
+AddName EQU 30h ; Add name command
+DelName EQU 31h ; Delete name command
+Reset EQU 32h ; Reset command
+AdptStat EQU 33h ; Adapter status command
+SessStat EQU 34h ; Session status command
+Cancel EQU 35h ; Cancel command
+AddGName EQU 36h ; Add group name command
+Unlink EQU 70h ; Unlink command
+Install EQU 7Fh ; Install check (?)
+
+NoWait EQU 80h ; APPLIES TO MOST COMMANDS (High bit)
+
+
+; some UB netbios command codes
+
+UBNB_Register EQU 72h
+UBNB_SendNmc EQU 73h
+UBNB_Callniu EQU 74h
+UBNB_Calladdr EQU 75h
+UBNB_Listenaddr EQU 76h
+UBNB_SendPkt EQU 77h
+UBNB_RcvPkt EQU 78h
+UBNB_SendAttn EQU 79h
+UBNB_RcvAttn EQU 7Ah
+UBNB_Listenniu EQU 7Bh
+UBNB_RcvRaw EQU 7Ch
+UBNB_SendNmc2 EQU 7Dh
+
+
+;******************************************************************************
+; S T R U C T U R E S
+;******************************************************************************
+;
+; Network Control Block structure
+;
+NCB_Struc STRUC
+NCB_Command db ? ; Command
+NCB_RetCode db ? ; Return code
+NCB_LSN db ? ; Local session #
+NCB_Num db ? ; Name #
+NCB_Buffer_Off dw ? ; Buffer offset
+NCB_Buffer_Seg dw ? ; Buffer segment
+NCB_Length dw ? ; Buffer length
+NCB_CallName db 16 dup (?) ; Name on local or remote adapter
+NCB_Name db 16 dup (?) ; Name on local adapter
+NCB_RTO db ? ; Receive timeout
+NCB_STO db ? ; Send timeout
+NCB_Post_Off dw ? ; Post routine offset
+NCB_Post_Seg dw ? ; Post routine segment
+NCB_LanA_Num db ? ; Adapter #
+NCB_Cmd_Cplt db ? ; Command status field
+NCB_Reserved db 14 dup (?) ; Reserved
+NCB_Struc ENDS
+
+
+HCB_Flags EQU WORD PTR [-02h]
+HCB_Next EQU WORD PTR [-04h]
+HCB_PM_NCB_Seg EQU WORD PTR [-06h]
+HCB_PM_NCB_Off EQU WORD PTR [-08h]
+HCB_Handle EQU WORD PTR [-0Ah]
+
+HCB_Header_Size EQU 0Ah
+HCB_Size EQU SIZE NCB_Struc + HCB_Header_Size
+
+; HCB_Flags defines
+
+HCB_DELAY EQU 001h
+HCB_ISSUED EQU 002h
+HCB_POSTED EQU 004h
diff --git a/private/mvdm/dpmi/ntintr32.asm b/private/mvdm/dpmi/ntintr32.asm
new file mode 100644
index 000000000..cea70632c
--- /dev/null
+++ b/private/mvdm/dpmi/ntintr32.asm
@@ -0,0 +1,937 @@
+include intmac.inc
+ public rgw032Stack
+
+ dw 64 dup (?) ; DOSX Ring -> Ring 0 transition stack
+;
+; Interrupts in the range 0-1fh cause a ring transition and leave
+; an outer ring IRET frame right here.
+;
+Ring0_EH_DS dw ? ; place to put user DS
+Ring0_EH_AX dw ? ; place to put user AX
+Ring0_EH_BX dw ? ; place to put user BX
+Ring0_EH_CX dw ? ; place to put user CX
+Ring0_EH_BP dw ? ; place to put user BP
+Ring0_EH_PEC dw ? ; lsw of error code for 386 page fault
+ ; also near return to PMFaultEntryVector
+Ring0_EH_EC dw ? ; error code passed to EH
+Ring0_EH_IP dw ? ; interrupted code IP
+ dw ?
+Ring0_EH_CS dw ? ; interrupted code CS
+ dw ?
+Ring0_EH_Flags dw ? ; interrupted code flags
+ dw ?
+Ring0_EH_SP dw ? ; interrupted code SP
+ dw ?
+Ring0_EH_SS dw ? ; interrupted code SS
+ dw ?
+rgw032Stack label word
+
+; ------------------------------------------------------------------
+; PMFaultAnalyzer -- This routine is the entry point for
+; the protected mode fault/trap/exception handler. It tries
+; to distinguish between bona fide processor faults and
+; hardware/software interrupts which use the range of
+; interrupts that is reserved by Intel. If a fault is
+; detected, then format the stack for a DPMI fault handler,
+; then vector to the handler whose address is stored in
+; PMFaultVector. If it looks more like an interrupt, then
+; set up the stack for an interrupt handler, jump to the
+; handler whose address is stored in PMIntelVector.
+;
+; Input: none
+; Output: none
+
+ assume ds:NOTHING,es:NOTHING,ss:DGROUP
+ public PMFaultAnalyzer
+
+PMFaultAnalyzer proc near
+
+;
+; Make sure we are on the right stack. Else, something fishy is going on.
+; Note that stack faults won't do too well here.
+;
+ push ax
+ mov ax,ss
+ cmp ax,SEL_DXDATA or STD_RING
+ pop ax
+ je pmfa_stack_passed
+ jmp pmfa_bad_stack
+pmfa_stack_passed:
+;
+; Is the stack pointer pointing at a word error code (minus 2)?
+;
+ cmp sp,offset DGROUP:Ring0_EH_PEC
+ je pmfa_fault ; Yes, processor fault.
+
+;
+; Is it pointing to where it is supposed to be for a hardware or
+; software interrupt?
+;
+ cmp sp,offset DGROUP:Ring0_EH_EC
+ je pmfa_20
+ jmp pmfa_bad_stack
+pmfa_20:jmp pmfa_inspect
+
+pmfa_fault:
+;
+; Getting here, we have a known exception with a word error code of some
+; sort on the stack. Perform an outward ring transition, switch to the
+; client stack, then vector through the exception handler vector to the
+; appropriate handler.
+;
+ push bp
+ push cx
+ push bx
+ push ax
+ push ds
+ lea bp,Ring0_EH_SS
+ mov ax,word ptr [bp]
+ mov cx,selEHStack
+ cmp ax,cx
+ jne pmfa_stack_OK
+ mov bx,[bp-2]
+ jmp pmfa_copy_stack
+pmfa_stack_OK:
+ mov bx,npEHStackLimit
+pmfa_copy_stack:
+ mov ds,cx ; DS:BX = user SS:SP
+ mov cx,13
+ add bp,2 ; put both halves of ss
+pmfa_copy_stack_loop:
+ dec bx
+ dec bx
+ mov ax,word ptr [bp]
+ mov word ptr [bx],ax
+ dec bp
+ dec bp
+ loop pmfa_copy_stack_loop
+
+; DS:BX points to stack on entry to PMFaultReflector
+
+;
+; Build a far return frame on user stack, switch to user stack, and return
+;
+ lea bp,Ring0_EH_PEC
+ mov word ptr [bp+6],ds
+ mov word ptr [bp+4],bx
+
+ sub bx,2
+ mov word ptr ds:[bx],SEL_DXPMCODE OR STD_RING; push cs
+ sub bx,2
+ mov ds:[bx],offset DXPMCODE:PMFaultReflector; push ip
+ lea bp,Ring0_EH_PEC
+ mov ax,Ring0_EH_CX ; get BP value
+ sub bx,2
+ mov ds:[bx],ax ; push bp
+ mov Ring0_EH_PEC,bx ; sp for lss
+ mov bx,ds
+ mov Ring0_EH_EC,bx ; ss for lss
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+.386p
+ lss sp,[bp] ; switch stack
+.286p
+ pop bp
+ retf
+
+pmfa_inspect:
+;
+; Stack is set up as for an interrupt or exception without error code.
+; Adjust the stack pointer and put an error code of zero. Then try to
+; determine whether we have an exception or an interrupt. First test
+; is the interrupt number.
+;
+ push Ring0_EH_EC
+ mov Ring0_EH_EC,0
+ cmp Ring0_EH_PEC,offset PMFaultEntryVector + ((7 + 1) * 3)
+ ja pfma_50
+ push Ring0_EH_PEC
+ mov Ring0_EH_PEC,0
+ jmp pmfa_fault ; Yes, definitely a fault.
+pfma_50:
+;
+; At this point, all valid exceptions have been eliminated except for
+; exception 9, coprocessor segment overrun, and exception 16, coprocessor
+; error.
+;
+
+; **********************************************
+; * Your code to detect these exceptions here. *
+; **********************************************
+
+ push bp
+ push cx
+ push bx
+ push ax
+ push ds ; SP -> Ring0_EH_DS
+;
+; Point to the user's stack.
+;
+ lea bp,Ring0_EH_SS
+ mov cx,[bp]
+ mov ds,cx
+ mov bx,[bp-2] ; DS:[BX] -> user stack
+;
+; Copy the IRET frame to the user stack.
+;
+ lea bp,Ring0_EH_Flags
+ mov cx,6
+pmfa_copy_IRET:
+ mov ax,[bp]
+ dec bx
+ dec bx
+ mov [bx],ax
+ dec bp
+ dec bp
+ loop pmfa_copy_IRET
+;
+; Point BP at vector entry for this (reserved) interrupt.
+;
+ mov ax,Ring0_EH_PEC ; fetch near return address
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov cl,3
+ div cl ; AX = interrupt number
+ shl ax,2 ; AX = vector entry offset
+ lea bp,PMIntelVector
+ add bp,ax ; BP -> interrupt handler address
+ mov ax,[bp] ; AX = IP of handler
+ mov cx,[bp+2] ; CX = CS of handler
+;
+; Build a far return frame on user stack, switch to user stack, and return
+;
+
+ sub bx,2
+ mov ds:[bx],cx ; push cs
+ sub bx,2
+ mov ds:[bx],ax ; push ip
+ lea bp,Ring0_EH_PEC
+ mov ax,Ring0_EH_BP
+ sub bx,2
+ mov ds:[bx],ax ; push bp
+ mov Ring0_EH_PEC,bx ; sp for lss
+ mov bx,ds
+ mov Ring0_EH_EC,bx ; ss for lss
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+.386p
+ lss sp,[bp] ; switch stack
+.286p
+ pop bp
+ retf ; Out of here.
+
+pmfa_bad_stack:
+
+if DEBUG
+ mov ax,ss
+ mov bx,sp
+ Trace_Out "Fault Handler Aborting with SS:SP = #AX:#BX"
+ pop ax
+ sub ax, (offset DXPMCODE:PMFaultEntryVector) + 3
+ mov bx,3
+ div bl
+ Trace_Out "Fault Number #AX"
+ pop ax
+ pop bx
+ pop cx
+ pop dx
+ Trace_Out "First four stack words: #AX #BX #CX #DX."
+endif
+ push selDgroupPM
+ push offset DGROUP:rgwStack
+ rpushf
+ push SEL_DXPMCODE or STD_RING
+ push offset DXPMCODE:pmfr_death
+ riret
+pmfr_death:
+ mov ax,cs
+ mov ds,ax
+ mov dx,offset szRing0FaultMessage
+ pmdossvc 09h
+ jmp PMAbort
+
+PMFaultAnalyzer endp
+
+FR_Stack Struc
+ FR_BP dd ?
+ FR_AX dd ?
+ FR_BX dd ?
+ FR_DS dw ?
+ FR_ENTRY dd ? ; SS:[SP] points here on entry
+ ; to PMReservedReflector
+ FR_Toss dd ? ; DPMI return IP
+ FR_Ret_IP dd ? ; actual fault handler gets put
+ FR_Ret_CS dd ? ; here to return to
+ FR_IP dd ?
+ FR_CS dd ?
+ FR_FL dd ?
+ FR_SP dd ?
+ FR_SS dd ?
+FR_Stack Ends
+;
+; Alternate names so the structure above makes more sense to
+; PMFaultReflector.
+;
+FR_Handler_IP equ FR_DS
+FR_Handler_CS equ FR_ENTRY
+FR_Handler_Ret_IP equ FR_Toss
+FR_Handler_Ret_CS equ FR_Ret_IP
+FR_Handler_Entry equ FR_Handler_Ret_CS
+FR_EC equ FR_Ret_CS
+
+; ------------------------------------------------------------------
+; PMFaultReflector -- Dispatch a fault to a fault handler installed
+; in PMFaultVector. When the fault handler returns, return
+; to the faulting code, using the addresses placed on the
+; DPMI fault handler stack by the last called fault handler.
+;
+; Input:
+; Entry is by a NEAR call, with an IP within the range
+; of PMFaultEntryVector on the stack. The stack has been
+; set up for use by a DPMI fault handler.
+;
+; Output:
+; Controlled by fault handler.
+;
+; Uses:
+; Controlled by fault handler.
+;
+; Notes:
+; Fault handlers are called on a static stack. This routine
+; is NOT REENTRANT.
+;
+ public PMFaultReflector
+ public PMFaultReflectorIRET
+PMFaultReflector proc near
+ assume ss:nothing,ds:nothing,es:nothing
+
+ sub sp,6
+ push bx
+ push eax
+ push bp
+ mov bp,sp
+ push ds
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds,ax
+ assume ds:dgroup
+ mov ax,[bp.FR_Handler_Entry]
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov bl,3
+ div bl ; AX = interrupt number
+ shl ax,3 ; AX = offset of fault handler
+
+ lea bx,PMFaultVector
+ add bx,ax ; SS:[BX] -> fault vector entry
+ mov eax,word ptr ds:[bx]
+ mov [bp.FR_Handler_IP],eax
+ mov ax,word ptr ds:[bx+2]
+ mov [bp.FR_Handler_CS],ax
+
+ lea ax,pmfr_cleanup
+ movzx eax,ax
+ mov [bp.FR_Handler_Ret_IP],eax
+ push cs
+ pop [bp.FR_Handler_Ret_CS]
+
+ pop ds
+ assume ds:nothing
+ pop bp
+ pop ax
+ pop bx
+ db 066h,0CBh ; This calls the fault handler.
+
+PMFaultReflectorIRETCall:
+ dd (SEL_RZIRET or STD_RING) shl 10h
+
+pmfr_cleanup:
+;
+; Unwind the fault handler stack. Return to the faulting code.
+; This works by calling a Ring 0 procedure to do the actual IRET.
+; If we do it that way, we can return to the faulting code without
+; actually touching the faulting code's stack.
+;
+PMFaultReflectorIRET:
+
+ ; BUGBUG Daveh using user stack this way is less robust!!!
+
+.386p
+ add sp,4 ; pop error code
+ push bp
+ mov bp,sp
+ push ebx
+ push ds
+ push eax
+ mov ax,[bp + 18]
+ mov ds,ax
+ mov ebx,[bp + 14] ; ds:bx -> user stack
+ sub ebx,4
+ mov eax,[bp + 10]
+ mov ds:[ebx],eax ; push flags
+ sub ebx,4
+ mov eax,[bp + 4]
+ mov ds:[ebx],eax ; push cs
+ sub ebx,4
+ mov eax,[bp + 2]
+ mov ds:[ebx],eax ; push ip
+ sub ebx,2
+ mov ax,[bp]
+ mov ds:[ebx],ax ; push bp
+ mov [bp + 8],bx
+ pop eax
+ pop ds
+ pop ebx
+ pop bp
+
+ add sp,6 ; point to ss:sp
+ mov bp,sp
+
+ lss esp,[bp]
+.286p
+ pop bp ; restore bp
+ riretd
+endif
+PMFaultReflector endp
+;
+; -------------------------------------------------------
+; PMReservedReflector -- This routine is for reflecting
+; exceptions to a protected mode interrupt handler.
+; The default for exceptions 1, 2, and 3 is to have
+; a near call to this routine placed in the PMFaultVector.
+;
+; This routine strips off the fault handler stack set
+; up by PMFaultAnalyzer, switches to the stack pointed
+; to by the pushed SS and SP values, sets up an IRET
+; frame for use by PMIntrReflector, and jumps to
+; PMIntrReflector. Eventual return is via an IRET
+; from PMIntrReflector.
+;
+; Input:
+; Entry is by a NEAR call, with an IP within the range
+; of PMReservedEntryVector on the stack. The stack has been
+; set up for use by a DPMI fault handler.
+;
+; Output:
+; Switch to stack registers set up by any previous fault
+; handler, jump to PMIntrReflector with an IRET frame set up
+; for direct return to the interrupted code.
+;
+; Errors: none
+;
+; Uses: Modifies SS, SP. Does not return to caller.
+;
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMReservedReflector
+PMReservedReflector:
+
+ push ds
+ push ebx
+ push eax
+ push ebp
+ mov bp,sp
+;
+; BP now points to a stack frame described by the structure
+; above. This will be copied to a stack frame on the stack pointed to
+; by FR_SS:FR_SS. In most cases, the destination stack is actually
+; the same as the present stack, offset by four bytes. The following
+; block of code is therefore very likely an overlapping copy. Think
+; carefully before modifying how it works.
+;
+
+ mov bx,[bp.FR_SS]
+ mov ds,bx
+ mov ebx,[bp.FR_SP] ; DS:[BX] -> interrupted code's stack
+ sub bx, (size FR_Stack) - 8 ; (not copying SP or SS)
+ ; DS:[BX] -> place to copy our stack frame
+
+ mov eax,[bp.FR_FL] ; Push user IRET frame onto the destination
+ mov [ebx.FR_FL],eax ; stack.
+ mov eax,[bp.FR_CS]
+ mov [ebx.FR_CS],eax
+ mov eax,[bp.FR_IP]
+ mov [ebx.FR_IP],eax
+
+ mov eax,[bp.FR_ENTRY] ; Copy our caller's near return.
+ mov [ebx.FR_ENTRY],eax
+
+ mov ax,[bp.FR_DS] ; Copy saved registers.
+ mov [ebx.FR_DS],ax
+ mov eax,[bp.FR_BX]
+ mov [ebx.FR_BX],eax
+ mov eax,[bp.FR_AX]
+ mov [ebx.FR_AX],eax
+ mov eax,[bp.FR_BP]
+ mov [ebx.FR_BP],eax
+
+ mov ax,ds ; Switch to user stack.
+ mov ss,ax
+ mov esp,ebx
+ mov ebp,esp
+
+ mov ax,[ebp.FR_ENTRY] ; AX = offset of caller
+ sub ax,offset DXPMCODE:PMReservedEntryVector + 3
+ mov bl,3
+ div bl ; AX = interrupt number
+ shl ax,2 ; AX = offset into PMIntelVector
+ mov ds,SelDgroupPM
+ assume ds:DGROUP
+ lea bx,PMIntelVector
+ add bx,ax ; DS:[BX] -> interrupt handler
+
+ mov eax,[bx] ; Place vector entry just below
+ mov [ebp.FR_Ret_IP],eax ; IRET frame.
+ mov eax,[bx+4]
+ mov [ebp.FR_Ret_CS],eax
+
+ lea esp,[ebp.FR_BP] ; Point to saved registers.
+ pop ebp ; Pop 'em.
+ pop eax
+ pop ebx
+ pop ds
+ add esp,4 ; Fix up stack.
+
+ db 066h,0CBh ; jump to interrupt handler via far return
+
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Real Mode Interrupt Reflector
+ page
+; -------------------------------------------------------
+; REAL MODE INTERRUPT REFLECTOR
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+; -------------------------------------------------------
+; RMIntrEntryVector -- This table contains a vector of
+; near jump instructions to the real mode interrupt
+; reflector. Real mode interrupts that have been hooked
+; by the protected mode application have their vector
+; set to entry the real mode reflector through this table.
+
+ public RMIntrEntryVector
+
+RMIntrEntryVector:
+
+ rept 256
+ call RMIntrReflector
+ endm
+
+; -------------------------------------------------------
+; RMIntrReflector -- This routine is the entry point for
+; the real mode interrupt reflector. This routine
+; is entered when an interrupt occurs (either software
+; or hardware) that has been hooked by the protected mode
+; application. It switches the processor to protected mode
+; and transfers control to the appropriate interrupt
+; service routine for the interrupt. After the interrupt
+; service routine completes, it switches back to real
+; mode and returns control to the originally interrupted
+; real mode code.
+; Entry to this routine comes from the RMIntrEntryVector,
+; which contains a vector of near call instructions, which
+; all call here. The interrupt number is determined from
+; the return address of the near call from the interrupt
+; entry vector.
+; The address of the protected mode interrupt service routine
+; to execute is determined from the protected mode interrupt
+; descriptor tabel and the interrupt number.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: The segment registers are explicitly preserved by
+; this routine. Other registers are as preserved or
+; modified by the interrutp service routine.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public RMIntrReflector
+
+RMIntrReflector:
+;
+; On entry, the stack layout is:
+; [6] FLAGS - "
+; [4] CS - "
+; [2] IP - from original interrupt
+; [0] IP - from interrupt entry vector call
+;
+ FCLI
+ cld
+ push ds
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+if DEBUG
+;
+; Are we on a DOSX interrupt reflector stack?
+;
+ push ax
+ push cx
+ mov ax,ss
+ mov cx,ds
+ cmp ax,cx
+ pop cx
+ jne @F
+
+ cmp sp,offset bReflStack
+ jb @F
+ cmp sp,offset pbReflStack
+ jnb @F
+;
+; If so, have we overflowed a stacklet?
+;
+ mov ax,pbReflStack
+ cmp sp,ax
+ ja @F
+ add ax,CB_STKFRAME
+ cmp sp,ax
+ jb @F
+ pop ax
+ Real_Debug_Out "DOSX:RMIntrReflector--Reflector stack overflow."
+ push ax
+@@:
+ pop ax
+endif ;DEBUG
+ mov regUserAX,ax ;save user AX for later
+ push bp ;stack -> BP DS IP IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [A]
+ mov ax,[bp+0Ah] ;get the interrupted routine's flags
+ and ax,NOT 4100h ;clear the trace flag in case we got
+ ; an interrupt on an instruction about
+ ; to be single stepped
+ mov regUserFL,ax ;and save for later
+ mov ax,es
+ xchg ax,[bp+4] ;save ES and get entry vector address
+ pop bp
+
+; Some software (like older versions of Smartdrv.sys) may enable A20 on
+; their own, and get very 'suprised' to find it turned off by our PM->RM
+; mode switch. If they used Himem.sys, this wouldn't be necessary, but...
+
+if VCPI
+ cmp fVCPI,0
+ jnz @f
+endif
+ push ax ;get/save current A20 state on stack
+ push bx
+ xmssvc 7
+ mov regUserSP,ax ;use regUserSP as a temp var
+ pop bx
+ pop ax
+@@:
+ push regUserSP
+
+; The state that we want to save on the user's stack has been set up.
+; Convert the entry vector return address into an interrupt number.
+
+ sub ax,offset RMIntrEntryVector+3
+ push cx
+ mov cl,3
+ div cl
+ pop cx
+
+if DEBUG
+ mov PMIntNo,ax
+endif
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame.
+
+ mov regUserSP,sp ;save entry stack pointer so we can
+ mov regUSerSS,ss ; switch to our own stack
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,selDgroup ;switch to the reflector stack frame
+ENDIF
+ mov sp,pbReflStack
+ push pbReflStack ;save stack frame ptr on stack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ push ax ;save interrupt vector table offset
+ SwitchToProtectedMode
+ pop ax
+
+if DEBUG ;--------------------------------------------------------
+
+ push 0DEADh ;debugging id & interrupt number
+ push PMIntNo
+
+ cmp fTraceReflect,0
+ jz @f
+ push ax
+ mov ax,PMIntNo
+ Trace_Out "(rp#AL)",x
+ pop ax
+@@:
+
+; Perform a too-late-to-save-us-now-but-we-want-to-know check on the
+; reflector stack.
+
+ cmp StackGuard,1022h
+ jz @f
+ Debug_Out "DOSX:RMIntrReflector--Global reflector stack overflow."
+@@:
+endif ;DEBUG ---------------------------------------------------------
+
+; Build an IRET frame on the stack so that the protected mode interrupt service
+; routine will return to us when it is finished.
+
+ push regUserSS ;save user stack address on our own stack
+ push regUserSP ; frame so we can restore it later
+ push ds
+ push regUserFL
+ push cs
+ push offset rmrf50
+
+; Build an IRET frame on the stack to use to transfer control to the
+; protected mode ISR
+
+ and byte ptr regUserFL+1,not 02h ;use entry flags less the
+ push 0 ; high half esp
+ push regUserFL ; interrupt flag (IF)
+
+ xchg bx,ax ;interrupt vector offset to BX, preserve BX
+ cmp bx,CRESERVED ;Interrupt in reserved range?
+ jc rmrf_reserved
+ shl bx,3
+ mov es,selIDT
+ jmp rmrf_setISR
+rmrf_reserved:
+ shl bx,2
+ mov es,SelDgroupPM
+ add bx,offset DGROUP:PMIntelVector
+rmrf_setISR:
+ push dword ptr es:[bx+4] ;push segment of isr
+ push dword ptr es:[bx] ;push offset of isr
+ xchg bx,ax
+ mov ax,regUserAX ;restore entry value of AX
+ push ds
+ pop es
+
+; At this point the interrupt reflector stack looks like this:
+;
+; [18] previous stack frame pointer
+; [16] stack segment of original stack
+; [14] stack pointer of original stack
+; [12] protected mode dos extender data segment
+; [10] dos extender flags
+; [8] segment of return address back to interupt reflector
+; [6] offset of return address back to interrupt reflector
+; [4] user flags as on entry from original interrupt
+; [2] segment of protected mode ISR
+; [0] offset of protected mode ISR
+;
+; Execute the protected mode interrupt service routine
+
+ iretd
+
+; The protected mode ISR will return here after it is finsished.
+
+rmrf50: pop ds
+ pushf ;save flags as returned by PM Int routine
+
+ FCLI ;We have to clear interrupts here, because
+ cld ; the interrupt routine may have returned
+ ; with interrupts on and our code that uses
+ ; static variables must be protected. We
+ ; turn them off after to pushf instruction so
+ ; that we can preserve the state of the
+ ; interrupt flag as returned by the ISR.
+ mov regUserAX,ax
+ pop ax
+ pop regUserSP
+ pop regUserSS
+
+if DEBUG
+ add sp,4 ;'pop' off debugging info
+endif
+
+ pop pbReflStack ;deallocate stack frame(s)
+
+; Switch back to real mode.
+
+ push ax ;preserve AX
+ SwitchToRealMode
+ pop ax
+
+; Switch back to the original stack.
+
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Make sure the A20 line matches whatever state it was when the int occured.
+; This is for the benefit of any software that diddles A20 without using
+; an XMS driver
+
+ pop regUserSP ;A20 state at time of interrupt to temp var
+if VCPI
+ cmp fVCPI,0
+ jnz rmrf75
+endif
+ push ax ;save current ax
+ mov ax,regUserSP ;ax = A20 state at time of interrupt
+ or ax,ax ;if it was off, don't sweat it
+ jz rmrf70
+ push bx ;save bx (XMS calls destroy bl)
+ push ax
+ xmssvc 7 ;ax = current A20 state
+ pop bx ;bx = old A20 state
+ cmp ax,bx ;if A20 is still on, don't need to diddle
+ jz @f
+ xmssvc 5 ;force A20 back on
+ inc A20EnableCount ; and remember that we did this
+if DEBUG
+ or fA20,04h
+endif
+@@:
+ pop bx
+rmrf70:
+ pop ax
+rmrf75:
+
+; Put the flags returned by the real mode interrupt routine back into
+; the caller's stack so that they will be returned properly.
+
+ push bp ;stack -> BP DS ES IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [10]
+ and [bp+10],0300h ;clear all but the interrupt and trace flags
+ ; in the caller's original flags
+ or [bp+10],ax ;combine in the flags returned by the
+ ; interrupt service routine. This will cause
+ ; us to return to the original routine with
+ ; interrupts on if they were on when the
+ ; interrupt occured, or if the ISR returned
+ ; with them on.
+ pop bp
+
+; And return to the original interrupted program.
+
+ mov ax,regUserAX
+ pop ds
+ pop es
+ iret
+
+DXCODE ends
+
+WowIntr3216 proc
+
+ FCLI
+ push ebp
+ mov ebp,esp
+ mov regUserAX,eax
+ mov regUserBX,ebx
+ mov regUserDS,ds
+ mov regUserES,es
+ mov regUserFlags,[ebp + 16]
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ assume ds:dgroup
+ mov ebx,esp
+ mov regUserSp,eax
+ mov ax,ss
+ mov regUserSs,ax
+ mov bx,pbReflStack ; get pointer to new frame
+ sub bpReflStack,CB_STKFRAME
+
+;
+; put user stack pointer on new stack
+;
+ sub bx,4
+ mov [bx],regUserSs
+ sub bx,4
+ mov [bx],dword ptr regUserSp
+
+ mov ax,[ebp + 4]
+
+;
+; switch to new stack
+;
+
+ push ds
+ pop ss
+ movzx esp,bx
+
+;
+; Save ss:esp for lss esp
+;
+ push regUserSs
+ push regUserSp
+;
+; Create an int frame
+;
+ pushf
+ push cs
+ push offset wi30
+
+;
+; Put handler address on stack
+;
+
+ shl ax,3
+ mov es,selDgroupPM
+ add bx,offset DGROUP:Intr16Vector
+ push [bx + 2]
+ push [bx]
+
+;
+; Restore Registers
+;
+ mov eax,regUserAx
+ mov ebx,regUserBX
+ mov es,regUserES
+ mov ds,regUserDS
+ assume ds:nothing
+;
+; call handler
+;
+ retf
+
+wi30:
+;
+; handler will return here
+;
+
+;
+; Switch stacks
+;
+ mov ebp,esp
+ lss esp,[ebp]
+ mov ebp,esp
+
+ push eax
+ push ds
+ mov ds,SEL_DXDATA OR STD_RING
+ assume ds:DGROUP
+
+;
+; Deallocate stack frame
+;
+ add pbReflStack,CB_STKFRAME
+ pop eax
+ pop ds
+
+;
+; Return flags from int handler
+;
+ pushf
+ pop [ebp + 16]
+
+;
+; Return to interrupted code
+;
+ pop ebp
+ riretd
+
+WowIntr3216 endp
diff --git a/private/mvdm/dpmi/ntnpxem.asm b/private/mvdm/dpmi/ntnpxem.asm
new file mode 100644
index 000000000..3403650f2
--- /dev/null
+++ b/private/mvdm/dpmi/ntnpxem.asm
@@ -0,0 +1,128 @@
+ PAGE ,132
+ TITLE NTNPXEM.ASM -- Support for fielding exceptions from npx em
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* NTNPXEM.ASM - Exception handler for npx emulation *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* This module contains code to field exceptions from the *
+;* Nt NPX emulator. These exceptions will only be *
+;* received on machines without 387's, on which the app *
+;* has set the EM bit. *
+;****************************************************************
+
+ .286p
+ .287
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+include pmdefs.inc
+include \nt\public\sdk\inc\ks386.inc
+include intmac.inc
+ .list
+
+; -------------------------------------------------------
+; GENERAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+; -------------------------------------------------------
+; EXTERNAL SYMBOL DEFINITIONS
+; -------------------------------------------------------
+
+ extrn PmFaultEntryVector:near
+
+; -------------------------------------------------------
+; DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+DXDATA segment
+
+ extrn rgw0stack:word
+
+DXDATA ends
+
+; -------------------------------------------------------
+; Exception Handler
+; -------------------------------------------------------
+
+
+DXPMCODE segment
+ assume cs:DXPMCODE
+ .386p
+;
+; N.B. The following routine will be executed on a special
+; code selector. The following routine must ALWAYS
+; appear at offset zero in this code selector.
+;
+
+; -------------------------------------------------------
+; NpxExceptionHandler -- This function switches to the
+; exception handler stack, pushes an exception frame,
+; and restores the registers. It then transfers control
+; the trap 7 fault handler.
+;
+; Input: ss:esp -> an NT CONTEXT record
+; Output: all registers restored to fault time
+; values, and exception frame pushed.
+; Errors: none
+; Uses: all
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING,fs:NOTHING
+ public NpxExceptionHandler,EndNpxExceptionHandler
+
+NpxExceptionHandler proc far
+
+ FCLI
+ mov ax,ss
+ mov ds,ax
+ mov ebx,esp ; ds:ebx->CONTEXT
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ss,ax
+ mov esp,offset DXDATA:rgw0Stack ; ss:esp->exception stack
+
+;
+; Push exception frame on exception stack
+;
+ movzx eax,word ptr [ebx].CsSegSs
+ push eax
+ push dword ptr [ebx].CsEsp
+ push dword ptr [ebx].CsEFlags
+ movzx eax,word ptr [ebx].CsSegCs
+ push eax
+ push dword ptr [ebx].CsEip
+;
+; Restore registers
+;
+ mov gs,[ebx].CsSegGs
+ mov fs,[ebx].CsSegFs
+ mov es,[ebx].CsSegEs
+ mov ebp,[ebx].CsEbp
+ mov edi,[ebx].CsEdi
+ mov esi,[ebx].CsEsi
+ mov edx,[ebx].CsEdx
+ mov ecx,[ebx].CsEcx
+ mov ax,[ebx].CsSegDs
+ push ax
+ push dword ptr [ebx].CsEbx
+ mov eax,[ebx].CsEax
+ pop ebx
+ pop ds
+ db 0eah
+ dw (offset PmFaultEntryVector + 21)
+ dw SEL_DXPMCODE OR STD_RING
+EndNpxExceptionHandler:
+NpxExceptionHandler endp
+
+DXPMCODE ends
+ end
diff --git a/private/mvdm/dpmi/pmdefs.inc b/private/mvdm/dpmi/pmdefs.inc
new file mode 100644
index 000000000..2a4adce7e
--- /dev/null
+++ b/private/mvdm/dpmi/pmdefs.inc
@@ -0,0 +1,497 @@
+
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* PMDEFS.INC -- 80286 Protected Mode Definitions *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 7/28/89 jimmat Changes selectors for Wdeb386 (it now *
+;* needs 4, not 2) *
+;* 3/11/89 jimmat Added selector for TSS *
+;* 3/09/89 jimmat Added selectors for DynaLink call gates *
+;* 02/10/89 (GeneA): reorganized selector definitions for *
+;* change from small model to medium model *
+;* 12/01/88 (GeneA): added definitions for SEL_BIOSCODE and *
+;* SEL_USERSCR *
+;* *
+;****************************************************************
+;
+; -------------------------------------------------------
+; SELECTOR FIELDS
+; -------------------------------------------------------
+
+SELECTOR_RPL = 00000011b ;Requested Privilege Level mask
+SELECTOR_TI = 00000100b ;Table Indicator mask
+SELECTOR_INDEX = 0fff8h ;Index mask
+
+SELECTOR_PL0 = 00000000b ;Ring 0 privilege level
+SELECTOR_PL1 = 00000001b ;Ring 1 privilege level
+SELECTOR_PL2 = 00000010b ;Ring 2 privilege level
+SELECTOR_PL3 = 00000011b ;Ring 3 privilege level
+
+SELECTOR_PL_DX = SELECTOR_PL3 ;Privilege level used by DOSX
+
+; -------------------------------------------------------
+; ACCESS RIGHTS BIT DEFINITIONS
+; -------------------------------------------------------
+;
+; These are the access rights byte bit position definitions.
+
+; These fields are common to all descriptors
+AB_PRESENT = 10000000b ;segment present bit
+AB_DPL0 = 00000000b ;ring 0 DPL
+AB_DPL1 = 00100000b ;ring 1 DPL
+AB_DPL2 = 01000000b ;ring 2 DPL
+AB_DPL3 = 01100000b ;ring 3 DPL
+AB_DPL = 01100000b ;mask for DPL field
+
+;AB_DPL_DX = AB_DPL3 ;DPL used by DOSX
+AB_DPL_DX = (SELECTOR_PL_DX shl 5) ;DPL used by DOSX
+
+; These fields are relevant to code and data segment descriptors
+; (non-system descriptors)
+AB_DATA = 00010000b ;data segment
+AB_CODE = 00011000b ;code segment
+AB_STACK = 00010100b ;expand down (stack) segment
+AB_WRITE = 00000010b ;writable data
+AB_READ = 00000010b ;readable code
+AB_CONFORM = 00000100b ;conforming code
+AB_ACCESSED = 00000001b ;segment has been accessed
+AB_BIG = 0100000000000000b ; 32 bit segment
+
+; These fields are relevant to system descriptors
+AB_INVALID = 00000000b ;invalid descriptor
+AB_TSS = 00000001b ;task state segment descriptor
+AB_TSS386 = 00001001b ;task state segment descriptor 386
+AB_BUSY = 00000010b ;busy bit for task state descriptor
+AB_LDT = 00000010b ;local descriptor table descriptor
+AB_CALLGATE = 00000100b ;call gate descriptor
+AB_TASKGATE = 00000101b ;task gate descriptor
+AB_INTRGATE = 00000110b ;interrupt gate descriptor
+AB_TRAPGATE = 00000111b ;trap gate descriptor
+AB_IGATE386 = 00001110b ;80386 interrupt gate descriptor
+
+; These are some common combinations of the above fields making up
+; useful access rights bytes.
+
+ARB_CODE0 = AB_PRESENT+AB_DPL0+AB_CODE+AB_READ ;ring 0 code segment
+ARB_CODE1 = AB_PRESENT+AB_DPL1+AB_CODE+AB_READ ;ring 1 code segment
+ARB_CODE3 = AB_PRESENT+AB_DPL3+AB_CODE+AB_READ ;ring 3 code segment
+ARB_CODE_DX = AB_PRESENT+AB_DPL_DX+AB_CODE+AB_READ ;DOSX ring code segment
+
+ARB_DATA0NP = AB_DPL0+AB_DATA+AB_WRITE ;illegal segment
+ARB_DATA0 = AB_PRESENT+AB_DPL0+AB_DATA+AB_WRITE ;ring 0 read/write data
+ARB_DATA1 = AB_PRESENT+AB_DPL1+AB_DATA+AB_WRITE ;ring 1 read/write data
+ARB_DATA3 = AB_PRESENT+AB_DPL3+AB_DATA+AB_WRITE ;ring 3 read/write data
+ARB_DATA_DX = AB_PRESENT+AB_DPL_DX+AB_DATA+AB_WRITE ;DOSX ring read/write data
+
+ARB_STACK0 = AB_PRESENT+AB_DPL0+AB_STACK+AB_WRITE ;ring 0 stack
+ARB_STACK1 = AB_PRESENT+AB_DPL1+AB_STACK+AB_WRITE ;ring 1 stack
+ARB_STACK3 = AB_PRESENT+AB_DPL3+AB_STACK+AB_WRITE ;ring 3 stack
+ARB_STACK_DX = AB_PRESENT+AB_DPL_DX+AB_STACK+AB_WRITE ;DOSX ring stack
+
+ARB_TRAP0 = AB_PRESENT+AB_DPL0+AB_TRAPGATE ;ring 0 trap gate
+ARB_TRAP1 = AB_PRESENT+AB_DPL1+AB_TRAPGATE ;ring 1 trap gate
+ARB_TRAP3 = AB_PRESENT+AB_DPL3+AB_TRAPGATE ;ring 3 trap gate
+ARB_TRAP_DX = AB_PRESENT+AB_DPL_DX+AB_TRAPGATE ;DOSX ring trap gate
+
+ARB_INTR0 = AB_PRESENT+AB_DPL0+AB_INTRGATE ;ring 0 interrupt gate
+ARB_INTR1 = AB_PRESENT+AB_DPL1+AB_INTRGATE ;ring 1 interrupt gate
+ARB_INTR3 = AB_PRESENT+AB_DPL3+AB_INTRGATE ;ring 3 interrupt gate
+ARB_INTR_DX = AB_PRESENT+AB_DPL_DX+AB_INTRGATE ;DOSX ring interrupt gate
+
+ARB_INTR0386= AB_PRESENT+AB_DPL0+AB_IGATE386 ;ring 0 386 int gate
+ARB_INTR1386= AB_PRESENT+AB_DPL1+AB_IGATE386 ;ring 1 386 int gate
+ARB_INTR3386= AB_PRESENT+AB_DPL3+AB_IGATE386 ;ring 3 386 int gate
+ARB_INTR_DX386= AB_PRESENT+AB_DPL_DX+AB_IGATE386 ;DOSX ring 386 int gate
+
+ARB_CALL0 = AB_PRESENT+AB_DPL0+AB_CALLGATE ;ring 0 call gate
+ARB_CALL1 = AB_PRESENT+AB_DPL1+AB_CALLGATE ;ring 1 call gate
+ARB_CALL3 = AB_PRESENT+AB_DPL3+AB_CALLGATE ;ring 3 call gate
+ARB_CALL_DX = AB_PRESENT+AB_DPL_DX+AB_CALLGATE ;DOSX ring call gate
+
+ARB_TSS1 = AB_PRESENT+AB_DPL1+AB_TSS ;ring 1 task state seg
+ARB_TSS3 = AB_PRESENT+AB_DPL3+AB_TSS ;ring 3 task state seg
+ARB_TSS_DX = AB_PRESENT+AB_DPL_DX+AB_TSS ;DOSX ring task state seg
+
+ARB_TSS1386 = AB_PRESENT+AB_DPL1+AB_TSS386 ;ring 1 386 TSS
+ARB_TSS3386 = AB_PRESENT+AB_DPL3+AB_TSS386 ;ring 3 386 TSS
+ARB_TSS_DX386 = AB_PRESENT+AB_DPL_DX+AB_TSS386 ;DOSX ring 386 TSS
+
+ARB_LDT1 = AB_PRESENT+AB_DPL1+AB_LDT ;ring 1 local dscr tbl
+ARB_LDT3 = AB_PRESENT+AB_DPL3+AB_LDT ;ring 3 local dscr tbl
+ARB_LDT_DX = AB_PRESENT+AB_DPL_DX+AB_LDT ;DOSX ring local dscr tbl
+
+;--------------------------------------------------------
+; STANDARD DESCRIPTOR TABLE/RING EQUATES
+;--------------------------------------------------------
+
+; Currently DOSX is setup to run most of itself and the child app in
+; ring 1. However this may be changed by changing the following equates.
+
+STD_DPL = AB_DPL_DX
+STD_DATA = ARB_DATA_DX
+STD_CODE = ARB_CODE_DX
+STD_STACK = ARB_STACK_DX
+STD_TRAP = ARB_TRAP_DX
+STD_INTR = ARB_INTR_DX
+STD_INTR386 = ARB_INTR_DX386
+STD_CALL = ARB_CALL_DX
+STD_TSS = ARB_TSS_DX
+STD_TSS386 = ARB_TSS_DX386
+STD_LDT = ARB_LDT_DX
+
+STD_TBL = SELECTOR_TI
+ifdef WOW
+STD_RING = SELECTOR_PL_DX OR SELECTOR_TI
+else
+STD_RING = SELECTOR_PL_DX
+endif
+STD_TBL_RING = (STD_TBL or STD_RING)
+
+;
+; Code descriptor type for handling processor exceptions.
+;
+EH_CODE = ARB_CODE0
+EH_RING = SELECTOR_PL0
+EH_RING_MASK = NOT 3
+EH_DATA = ARB_DATA0
+
+; -------------------------------------------------------
+; DESCRIPTOR STRUCTURE DEFINITIONS
+; -------------------------------------------------------
+
+; This structure defines the layout of a segment descriptor on the '286
+; These can appear in either the local descriptor table or the global
+; descriptor table. The local descriptor table descriptors also fit into
+; this format, but they can only appear in the global descriptor table.
+
+SEGDSCR struc
+cbLimit dw 0 ;segment size limit
+adrBaseLow dw 0 ;low word of segment base address
+adrBaseHigh db 0 ;high byte of segment base address
+arbSegAccess db 0 ;access rights byte
+rsvdSeg dw 0 ;Intel reserved, must be 0
+SEGDSCR ends
+
+SEGDSCR386 struc
+cbLimit386 dw 0 ;segment size limit
+adrwBaseLow386 dw 0 ;low word of segment base address
+adrbBaseMid386 db 0 ;mid byte of segment base address
+arbSegAccess386 db 0 ;access rights byte
+cbLimitHi386 db 0 ;hi nybble of size limit
+adrbBaseHi386 db 0 ;high byte of segment base address
+SEGDSCR386 ends
+
+; This structure defines the layout of gate descriptors. These can appear
+; in any of the descriptor tables. Only Interrupt Gate, Trap Gate, and
+; Task Gate descriptors can appear in the interrupt descriptor table.
+
+GATEDSCR struc
+offDest dw 0 ;destination function offset (not used in
+ ; task gates)
+selDest dw 0 ;destination function segment selector
+cwParam db 0 ;count of parameter words to transfer
+arbGate db 0 ;access rights byte
+rsvdGate dw 0 ;Intel reserved, must be 0
+GATEDSCR ends
+
+; This is the structure defines the layout of a Task State Segment
+; descriptor. This can only appear in the Global Descriptor Table
+
+TSSDSCR struc
+cbTssLimit dw 0 ;segment size limit
+adrTssBaseLow dw 0 ;low word of segment base address
+adrTssBaseHigh db 0 ;high byte of segment base address
+arbTssAccess db 0 ;access rights byte
+rsvdTss dw 0 ;Intel reserved, must be 0
+TSSDSCR ends
+
+; -------------------------------------------------------
+; 80286 TASK STATE SEGMENT
+; -------------------------------------------------------
+;
+; This structure describes the layout of an 80286 task state
+; segment.
+
+TSS286 struc
+tss_backlink dw ? ;backlink to previous task
+tss_sp0 dw ? ;privelege level 0 stack pointer
+tss_ss0 dw ? ;privelege level 0 stack segment
+tss_sp1 dw ? ;privelege level 1 stack pointer
+tss_ss1 dw ? ;privelege level 1 stack segment
+tss_sp2 dw ? ;privelege level 2 stack pointer
+tss_ss2 dw ? ;privelege level 2 stack segment
+tss_ip dw ? ;initial instruction pointer
+tss_flags dw ?
+tss_ax dw ?
+tss_cx dw ?
+tss_dx dw ?
+tss_bx dw ?
+tss_sp dw ?
+tss_bp dw ?
+tss_si dw ?
+tss_di dw ?
+tss_es dw ?
+tss_cs dw ?
+tss_ss dw ?
+tss_ds dw ?
+tss_ldt dw ? ;local descriptor table for this task
+
+TSS286 ends
+
+; -------------------------------------------------------
+; 80386 TASK STATE SEGMENT
+; -------------------------------------------------------
+;
+; This structure describes the layout of an 80386 task state
+; segment.
+
+TSS386 struc
+ts3_backlink dw 0 ;backlink to previous task
+ dw 0
+ts3_esp0 dd 0 ;privelege level 0 stack pointer
+ts3_ss0 dw 0 ;privelege level 0 stack segment
+ dw 0
+ts3_esp1 dd 0 ;privelege level 1 stack pointer
+ts3_ss1 dw 0 ;privelege level 1 stack segment
+ dw 0
+ts3_esp2 dd 0 ;privelege level 2 stack pointer
+ts3_ss2 dw 0 ;privelege level 2 stack segment
+ dw 0
+ts3_eip dd 0 ;initial instruction pointer
+ts3_cr3 dd 0
+ts3_eflags dd 0
+ts3_eax dd 0
+ts3_ecx dd 0
+ts3_edx dd 0
+ts3_ebx dd 0
+ts3_esp dd 0
+ts3_ebp dd 0
+ts3_esi dd 0
+ts3_edi dd 0
+ts3_es dw 0
+ dw 0
+ts3_cs dw 0
+ dw 0
+ts3_ss dw 0
+ dw 0
+ts3_ds dw 0
+ dw 0
+ts3_fs dw 0
+ dw 0
+ts3_gs dw 0
+ dw 0
+ts3_ldt dw 0 ;local descriptor table for this task
+ dw 0
+ts3_iomap dw 0
+ dw 0
+
+TSS386 ends
+
+
+; -------------------------------------------------------
+; EXCEPTION VECTORS
+; -------------------------------------------------------
+
+; These are the interrupt vector numbers for the exception
+; interrupts reserved by the 80286/80386.
+
+EXC_DIV0 = 0 ;divide error
+EXC_SINGLESTEP = 1 ;single step
+EXC_NMI = 2 ;NMI interrupt
+EXC_BREAKPOINT = 3 ;breakpoint interrupt
+EXC_INTO = 4 ;overflow interrupt
+EXC_BOUND = 5 ;bounds overflow exception
+EXC_OPCODE = 6 ;invalid opcode exception
+EXC_COPROCESSOR = 7 ;processor extension not available
+EXC_DOUBLE = 8 ;double fault
+EXC_XOVERRUN = 9 ;coprocessor segment overrun
+EXC_TSS = 10 ;invalid task state segment exception
+EXC_NOTPRESENT = 11 ;segment not present exception
+EXC_STACK = 12 ;stack overrun, or stack segment not present
+EXC_GP = 13 ;general protection exception
+EXC_PF = 14 ;page fault
+
+; -------------------------------------------------------
+; GLOBAL DESCRIPTOR DECLARATIONS
+; -------------------------------------------------------
+;
+; The following symbols define the segment descriptors that
+; are statically defined in the Dos Extender.
+;
+; NOTE:
+; These selector definitions assume that codeview is running
+; at privelege level 0. If codeview is running at another
+; privelege level, the RPL fields must be adjusted.
+;
+; Global Descriptor Table Conventions used in the Dos Extender
+
+SEL_NULL = 00h ;null selector
+IFNDEF WOW
+SEL_GDT = 08h ;read/write data segment pointing to the
+ ; global descriptor table
+SEL_IDT = 10h ;read/write data segment pointing to the
+ ; protected mode interrupt descriptor table
+SEL_RMIVT = 18h ;read/write data segment pointing to the
+ ; real mode interrupt vector table
+SEL_PSP = 20h ;Dos Extender PSP
+SEL_ENVIRON = 28h ;Dos Extender Environment
+SEL_BIOSCODE = 30h ;points at segment F000
+SEL_DXDATA = 38h ;Dos Extender data segment
+SEL_BIOSDATA = 40h ;PC BIOS data segment
+SEL_DXPMCODE = 48h ;Dos Extender extended memory code segment
+SEL_DXCODE = 50h ;Dos Extender low memory code segment
+SEL_TSS = 58h ;Task State Segment selector
+SEL_TSS_ALIAS = 60h ;read/write alias to TSS
+SEL_LDT = 68h ;LDT selector
+SEL_LDT_ALIAS = 70h ;read/write alias to LDT
+
+SEL_DEBUG = 78h ;linear 4GB segment to be used by the
+ ; debugger to access all memory
+
+SEL_EH = 80h ;Ring 0 segment code for handling processor
+ ;exceptions in PMODE
+
+; NOTE: The next 4 segment descriptors are reserved for WDEB386 debugger.
+
+SEL_DEB386 = 088h
+
+SEL_RESET = 0A8h ;call gate to code that switches to real mode
+SEL_DXDATA0 = 0B0h ;Dos Extender data segment-ring 0
+SEL_DXCODE0 = 0B8h ;Dos Extender low memory code segment-ring 0
+
+SEL_RZIRET = 0C0h ;Call gate to exit from fault handler.
+SEL_STACK_ALIAS = 0C8h ;Stack alias for Ring 3 exception handlers.
+
+if VCPI
+
+SEL_DXPD = 0D0h ;for accessing page directory.
+
+SEL_DXPT = 0D8h ;for accessing page tables
+
+SEL_VCPI = 0E0h ;3 selectors for vcpi server
+; 0E8h
+; 0F0h
+
+SEL_VCPIALLMEM = SEL_DEBUG ;for calls to vcpi to switch from protect
+ ; mode, this is a data segment mapping 4meg
+ ; starting at linear base 0.
+
+SEL_CALLVCPI = 0F8h ;call gate to ring 0 VCPI interface
+
+SEL_DYNALINK = 100h ;first DynaLink call gate selector
+
+else ;VCPI
+
+SEL_IRETHOOK = 0D0h
+SEL_NBPMCODE = 0D8H ; net bios anr handler cs
+SEL_DYNALINK = 0E0h
+
+endif ;VCPI
+
+
+;
+; Size of the GDT.
+;
+GDT_SIZE = (((SEL_DYNALINK + (NUM_DYNALINK shl 3) + 8) shr 4) shl 4)
+GDT_SELECTORS = (GDT_SIZE shr 3)
+
+ELSE
+
+SEL_GDT = 80h ;read/write data segment pointing to the
+ ; global descriptor table
+SEL_IDT = 88h ;read/write data segment pointing to the
+ ; protected mode interrupt descriptor table
+SEL_RMIVT = 90h ;read/write data segment pointing to the
+ ; real mode interrupt vector table
+SEL_PSP = 98h ;Dos Extender PSP
+SEL_ENVIRON = 0A0h ;Dos Extender Environment
+SEL_BIOSCODE = 0A8h ;points at segment F000
+SEL_DXDATA = 0B0h ;Dos Extender data segment
+SEL_BIOSDATA = 0B8h ;PC BIOS data segment
+SEL_DXPMCODE = 0C0h ;Dos Extender extended memory code segment
+SEL_DXCODE = 0C8h ;Dos Extender low memory code segment
+SEL_LDT_ALIAS = 0D0h ;read/write alias to LDT
+
+SEL_VDMTIB = 0D8h ;used by DOSX to access TIB
+
+SEL_EH = 0E0h ;Ring 0 segment code for handling processor
+ ;exceptions in PMODE
+
+; NOTE: The next 4 segment descriptors are reserved for WDEB386 debugger.
+
+ifndef WOW
+SEL_DEB386 = 0E8h
+endif
+
+SEL_DXDATA0 = 0f0h
+SEL_DXCODE0 = 0f8h
+SEL_RZIRET = 0100h
+SEL_LDT = 0108h
+SEL_RESET = 0110h
+SEL_TSS = 0118h
+SEL_TSS_ALIAS = 0120h
+
+SEL_STACK_ALIAS = 0128h ;Stack alias for Ring 3 exception handlers.
+
+SEL_WOW_LDT = 0130h ; readonly LDT selector for WOW kernel
+
+SEL_NPXHDLR = 0138h ; selector for NpxExceptionHandler
+
+SEL_IRETHOOK = 0140h
+SEL_NBPMCODE = 0148H ; net bios anr handler cs
+;
+; Size of the GDT.
+;
+GDT_SIZE = (SEL_NBPMCODE + 8)
+GDT_SELECTORS = (GDT_SIZE shr 3)
+ENDIF
+
+;
+; Special LDT selectors.
+;
+
+SEL_DPMI_FIRST = 0 ; first reserved DPMI LDT selector
+C_DPMI_RESERVED = 10h ; count of reserved DPMI LDT selectors
+SEL_DPMI_LAST = (C_DPMI_RESERVED - 1) * 8
+ ; last reserved DPMI LDT selector
+IFNDEF WOW
+SEL_SCR0 = SEL_DPMI_LAST + 8
+
+ ;scratch selector 0
+SEL_SCR1 = SEL_SCR0 + 8 ;scratch selector 1
+SEL_USERSCR = SEL_SCR1 + 8 ;user scratch selector. This is used for
+ ; temporary return values to user from
+ ; system and bios calls
+SEL_USER_STACK = SEL_USERSCR + 8
+
+;SEL_USER = SEL_DYNALINK + (NUM_DYNALINK shl 3)
+;this is where the dos extender starts allocating user selectors
+
+SEL_USER = SEL_USER_STACK + 8
+
+ELSE
+
+SEL_SCR0 = SEL_NBPMCODE + 8
+
+ ;scratch selector 0
+SEL_SCR1 = SEL_SCR0 + 8 ;scratch selector 1
+SEL_USERSCR = SEL_SCR1 + 8 ;user scratch selector. This is used for
+ ; temporary return values to user from
+ ; system and bios calls
+SEL_USER_STACK = SEL_USERSCR + 8
+
+;SEL_USER = SEL_DYNALINK + (NUM_DYNALINK shl 3)
+;this is where the dos extender starts allocating user selectors
+
+SEL_USER = SEL_USER_STACK + 8
+ENDIF
+; -------------------------------------------------------
+; -------------------------------------------------------
+; -------------------------------------------------------
+
+;****************************************************************
diff --git a/private/mvdm/dpmi/program.sed b/private/mvdm/dpmi/program.sed
new file mode 100644
index 000000000..ed5e77970
--- /dev/null
+++ b/private/mvdm/dpmi/program.sed
@@ -0,0 +1,7 @@
+/\\$/N
+h
+/./s/^./$(DEST)\\&/
+/./p
+g
+s/\..*//
+/./s/.*/ $(ASM) &,$(DEST)\\;/
diff --git a/private/mvdm/dpmi/prot386.inc b/private/mvdm/dpmi/prot386.inc
new file mode 100644
index 000000000..16f52bf86
--- /dev/null
+++ b/private/mvdm/dpmi/prot386.inc
@@ -0,0 +1,36 @@
+;*** prot386.inc - 386 protect mode structures, records, and masks.
+;
+; Copyright <C> 1990-1991, Microsoft Corporation
+;
+; Purpose:
+;
+; Revision History:
+;
+; [] 20-Feb-1990 Dans Created
+;
+;************************************************************************/
+;
+; page table entry record
+;
+PTEAltr record \
+ pfaPTEA:20, \
+ availPTEA:3, \
+ unusedPTEA:2, \
+ dirtyPTEA:1, \
+ accessedPTEA:1, \
+ pcdPTEA:1, \
+ pwtPTEA:1, \
+ usersuperPTEA:1, \
+ readwritePTEA:1, \
+ presentPTEA:1
+
+
+PTEr record \
+ pfaPTE:20, \
+ allbitsPTE:12
+;
+; New page table entry mask
+;
+NEWPTEMASK = mask presentPTEA OR \
+ mask readwritePTEA OR \
+ mask usersuperPTEA
diff --git a/private/mvdm/dpmi/segdefs.inc b/private/mvdm/dpmi/segdefs.inc
new file mode 100644
index 000000000..12684d41b
--- /dev/null
+++ b/private/mvdm/dpmi/segdefs.inc
@@ -0,0 +1,119 @@
+
+; Copyright (c) Microsoft Coropration 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* SEGDEFS.INC -- Segment Declarations for Dos Extender *
+;* *
+;****************************************************************
+;* Revision History: *
+;* *
+;* 12/13/88 (GeneA): reordered segments so that the code *
+;* segment is last. *
+;* *
+;****************************************************************
+;
+; -------------------------------------------------------
+; DOS EXTENDER SEGMENTS
+; -------------------------------------------------------
+
+;; Touch here to force rebuild of DOSX! EarleH
+
+?DF=1
+?PLM=1
+?WIN=0
+?MEDIUM=1
+DOS5=1
+IFNDEF WHEN_COMMAND_COM_WORKS
+WINDOWS=1
+ELSE
+WINDOWS=0
+ENDIF
+include .\cmacros.inc
+
+DXDATA segment para public 'DATA'
+
+DXDATA ends
+
+
+DXSTACK segment para stack 'STACK'
+
+DXSTACK ends
+
+
+;
+; This segment contains both real mode and protected mode code and
+; resides in low memory.
+DXCODE segment para public 'CODE'
+
+DXCODE ends
+
+;
+; This segment contains protected mode code only, and is moved up
+; into extended memory during the initialization process.
+
+DXPMCODE segment para public 'PMCODE'
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+; GROUP DECLARATIONS
+; -------------------------------------------------------
+
+DGROUP group DXDATA, DXSTACK
+
+; -------------------------------------------------------
+; MS-DOS PROGRAM SEGMENT PREFIX DEFINITION
+; -------------------------------------------------------
+
+PSPSEG segment at 0
+
+fnExit dw ? ;INT 20h instruction for termination linkage
+segMemEnd dw ? ;segment address of end of program's memory
+rsvd1 db ? ;undefined
+fnDosFunc db 5 dup (?) ;far call to DOS function dispatcher
+lpfnParent dd ? ;far pointer to parent program
+lpfnInt23 dd ? ;previous contents of INT 23h vector (^C)
+lpfnInt24 dd ? ;previous contents of INT 24h vector
+segParentPSP dw ? ;segment address of parent PSP
+bFileTable db 20 dup (?) ;process file table
+segEnviron dw ? ;segment address of environment
+lpStack dd ? ;application's SS:SP (updated on each dos call)
+cbFileTable dw ? ;size of process file table
+lpFileTable dd ? ;far pointer to process file table
+
+ org 5Ch
+
+fcbDefault1 db 10h dup (?) ;default FCB 1
+fcbDefault2 db 10h dup (?) ;default FCB 2
+
+ org 80h
+
+cbCommand db ? ;lenght of command string
+chCommand db 7Fh dup (?) ;command string
+
+PSPSEG ends
+
+; -------------------------------------------------------
+; BIOS DATA SEGMENT DEFINITIONS
+; -------------------------------------------------------
+
+BIOS_DATA segment at 40h ;BIOS Data segment
+
+ org 67h
+IO_ROM_INIT dw ? ;Shutdown code 0Ah will cause the
+IO_ROM_SEG dw ? ; bios to return control to the far
+ ; address stored here.
+ ;Shutdown code 09h will cause the
+ ; bios to load SS:SP from here and
+ ; do an IRET.
+INTR_FLAG db ?
+
+ org 72h
+RESET_FLAG dw ? ;For Ctrl-Alt-Del
+
+BIOS_DATA ends
+
+; -------------------------------------------------------
+
+;****************************************************************
diff --git a/private/mvdm/dpmi/smartdrv.inc b/private/mvdm/dpmi/smartdrv.inc
new file mode 100644
index 000000000..5c6dd8f74
--- /dev/null
+++ b/private/mvdm/dpmi/smartdrv.inc
@@ -0,0 +1,147 @@
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;******************************************************************************
+;
+; Title: smartdrv.inc - smartdrv equates & structures
+;
+; Version: 1.00
+;
+; Date: 15-Sep-1989
+;
+; Author: ARR
+;
+;------------------------------------------------------------------------------
+;
+; Change log:
+;
+; DATE REV DESCRIPTION
+; ----------- --- -----------------------------------------------------------
+; 15-Sep-1989 ARR Original
+;
+;==============================================================================
+
+;
+; Device name of SMARTDRV
+;
+SD_DEV_NAME equ "SMARTAAR",0
+
+;
+; SMARTDRV cache type (XMS or EMS) -- SD_IR_Driver_Type field
+;
+
+SD_CACHE_XMS equ 1 ; Cache is in XMS
+SD_CACHE_EMS equ 2 ; Cache is in EMS
+
+;
+; Structure of the data returned on the SMARTDRV IOCTL read call
+;
+SD_IOCTL_Read struc
+
+ SD_IR_Write_Through db ? ; Write-through flag, currently always 1
+ SD_IR_Write_Buff db ? ; Buffer write flag, currently always 0
+ SD_IR_Enable_Int13 db ? ; Cache enable flag, 1 = enabled
+ SD_IR_Driver_Type db ? ; Type 1 (XMS cache) or 2 (EMS cache)
+ SD_IR_Tick_Setting dw ? ; Ticks between flushes, currently unused
+ SD_IR_Lock_Cache db ? ; Non-zero if cache is locked
+ SD_IR_Reboot_Flush db ? ; Non-zero if cache should be flushed on reboot
+ SD_IR_Cache_All db ? ; Zero if full track writes are not cached
+ db ? ; Unused
+ SD_IR_Original_INT13 dd ? ; INT 13 address SMARTDRV hooked
+ SD_IR_Version dw ? ; SMARTDrive version, packed BCD
+ dw ? ; Unused
+ ; Next three values are used for computing
+ ; cache statistics. They may be scaled rather
+ ; than absolute values
+ SD_IR_Total_Sec_Read dw ? ; Count of sectors read
+ SD_IR_Cache_Sec_Read dw ? ; Count of sectors read from cache
+ SD_IR_Track_Sec_Read dw ? ; Count of sectors read from track buffer
+ ; Cache statistics
+ SD_IR_Cache_Hit_Ratio db ? ; = Cache_Sec_Read * 100 / Total_Sec_Read
+ SD_IR_Track_Hit_Ratio db ? ; = Track_Sec_Read * 100 / Total_Sec_Read
+
+ SD_IR_Total_Tracks dw ? ; Number of tracks in the cache
+ SD_IR_Total_Used dw ? ; Tracks currently in use
+ SD_IR_Total_Locked dw ? ; Tracks currently locked
+ SD_IR_Total_Dirty dw ? ; Tracks currently dirty
+ ; Information for resizing cache
+ ; All values are in terms of 16Kb pages.
+ SD_IR_Current_Size dw ? ; Current size of the cache, in 16Kb pages
+ SD_IR_Dev_Size dw ? ; Original size requested by user
+ SD_IR_Min_Cache_Size dw ? ; Minimum allowable size
+
+SD_IOCTL_Read ends
+;
+; IOCTL Write functions
+; The function is encoded in the first byte of the IOCTL write data
+;
+SD_IOCTL_WR_Flush equ 0 ; Flush the cache
+SD_IOCTL_WR_Flush_Invalidate equ 1 ; Flush the cache and discard it
+SD_IOCTL_WR_Disable_Cache equ 2 ; Turn caching off
+SD_IOCTL_WR_Enable_Cache equ 3 ; Turn caching on
+
+SD_IOCTL_WR_Set_Tick equ 5 ; Set tick count for flush
+SD_IOCTL_WR_Lock equ 6 ; Lock current cache contents
+SD_IOCTL_WR_Unlock equ 7 ; Unlock current cache contents
+SD_IOCTL_WR_Reboot_Flush equ 8 ; Set Reboot_Flush flag
+
+
+SD_IOCTL_WR_Shrink_Cache equ 11 ; Reduce cache size
+SD_IOCTL_WR_Grow_Cache equ 12 ; Increase cache size
+SD_IOCTL_WR_Set_INT_13 equ 13 ; Set the address SMARTDRV
+ ; chains to on INT 13s
+;
+; Structures for the IOCTL write calls
+;
+; These calls do not take any parameters so the correct write count is 1
+; (the function code byte)
+;
+; WR_Flush
+; WR_Flush_Invalidate
+; WR_Disable_Cache
+; WR_Enable_Cache
+; WR_Set_Tick
+; WR_Lock
+; WR_Unlock
+;
+SD_IOCTL_WR_NoParm struc
+
+ SD_I_W_Func db ?
+
+SD_IOCTL_WR_NoParm ends
+;
+; WR_Reboot_Flush
+;
+SD_IOCTL_WR_Reboot struc
+
+ SD_I_W_FuncR db SD_IOCTL_WR_Reboot_Flush
+ SD_I_W_RebootFlg db ? ; 0 to turn reboot flush off
+ ; 1 to turn reboot flush on
+SD_IOCTL_WR_Reboot ends
+;
+; WR_Shrink_Cache
+; WR_Grow_Cache
+;
+SD_IOCTL_WR_GrwShrk struc
+
+ SD_I_W_FuncGS db ? ; Function, one of:
+ ; SD_IOCTL_WR_Shrink_Cache
+ ; SD_IOCTL_WR_Grow_Cache
+ SD_I_W_GS_Size dw ? ; Count of 16k "pages" to
+ ; grow or shrink cache by
+ ; THIS IS A SIZE DELTA
+ ; not a total size.
+SD_IOCTL_WR_GrwShrk ends
+;
+; WR_Set_INT_13
+;
+SD_IOCTL_WR_SetI13 struc
+
+ SD_I_W_FuncS13 db SD_IOCTL_WR_Set_INT_13
+
+ SD_I_W_S13_Addr dd ? ; Segment:Offset address
+ ; to set Original_INT13 to.
+ ; NO RECORD IS KEPT OF PREV
+ ; VALUE, caller's responsible
+ ; for save and restore.
+SD_IOCTL_WR_SetI13 ends
diff --git a/private/mvdm/dpmi/stackchk.inc b/private/mvdm/dpmi/stackchk.inc
new file mode 100644
index 000000000..7815df8f7
--- /dev/null
+++ b/private/mvdm/dpmi/stackchk.inc
@@ -0,0 +1,85 @@
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; stackchk.inc
+;
+; Abstract:
+;
+; This module implements stack checking for the dos extender
+;
+; Author:
+;
+; Dave Hastings (daveh) 20-Apr-1993
+;
+;--
+
+if DBG
+
+FIX_STACK macro
+ local foo,foo1
+ push bp
+ mov bp,pbReflStack
+ cmp bp,offset DGROUP:bReflStack
+ ja foo1
+
+ BOP BOP_DBGBREAKPOINT
+
+foo1: mov bp,sp
+; cmp word ptr [bp - CB_STKFRAME + 2],0AAAAH
+; je foo
+
+; BOP BOP_DBGBREAKPOINT
+
+foo: mov word ptr [bp - CB_STKFRAME + 2],0AAAAH
+ pop bp
+ endm
+
+CHECK_STACK macro
+ local foo
+ push bp
+ mov bp,pbReflStack
+; cmp word ptr [bp + 2],0AAAAh
+; je foo
+
+; BOP BOP_DBGBREAKPOINT
+
+foo: pop bp
+ endm
+
+ASSERT_CLI macro
+ local foo1
+
+ push ds
+ push 40h
+ pop ds
+ test ds:[314h],0200h
+ jz foo1
+
+ BOP BOP_DBGBREAKPOINT
+foo1: pop ds
+ endm
+
+ASSERT_REFLSTK_OK macro
+ local foo1
+ cmp pbReflStack,offset DGROUP:pbReflStack
+ jna foo1
+
+ BOP BOP_DBGBREAKPOINT
+foo1:
+ endm
+else
+FIX_STACK macro
+ endm
+
+CHECK_STACK macro
+ endm
+
+ASSERT_CLI macro
+ endm
+
+ASSERT_REFLSTK_OK macro
+ endm
+endif
diff --git a/private/mvdm/dpmi/usa/dxmsg.asm b/private/mvdm/dpmi/usa/dxmsg.asm
new file mode 100644
index 000000000..33bc76db5
--- /dev/null
+++ b/private/mvdm/dpmi/usa/dxmsg.asm
@@ -0,0 +1,158 @@
+ PAGE ,132
+ TITLE DXMSG.ASM -- Dos Extender Text Messages
+
+; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
+
+;***********************************************************************
+;
+; DXMSG.ASM -- Dos Extender Text Messages
+;
+;-----------------------------------------------------------------------
+;
+; This module contains the text messages displayed by the 80286 DOS
+; Extender. The messages are contained in this file to ease their
+; conversion to other languages.
+;
+;-----------------------------------------------------------------------
+;
+; 12/06/89 jimmat Update message text as per User Ed
+; 08/03/89 jimmat Original version
+;
+;***********************************************************************
+
+ .286p
+
+; -------------------------------------------------------
+; INCLUDE FILE DEFINITIONS
+; -------------------------------------------------------
+
+ .xlist
+ .sall
+include segdefs.inc
+include gendefs.inc
+ .list
+
+; -------------------------------------------------------
+; CODE SEGMENT VARIABLES
+; -------------------------------------------------------
+
+DXCODE segment
+
+; Note: these DXCODE segment messages are all linked after the CodeEnd
+; variable, so they will be discarded after initialization.
+
+ public ER_CPUTYPE, ER_PROTMODE, ER_NOHIMEM, ER_DXINIT, ER_REALMEM
+ public ER_EXTMEM, ER_NOEXE
+
+if VCPI
+ public ER_VCPI, ER_QEMM386
+endif ;VCPI
+;
+; Wrong CPU type.
+;
+ER_CPUTYPE db 13,10
+ db ' Cannot run this 16-bit protected mode application;',13,10,13,10
+ db ' The DOS extender has detected a CPU mismatch.',13,10
+ db 13,10,'$'
+;
+; Can't figure out how to get into protected mode.
+;
+ER_PROTMODE db 13,10
+ db ' Cannot run this 16-bit protected mode application;',13,10,13,10
+ db ' The DOS extender has detected a conflict with other protect ',13,10
+ db ' mode software.',13,10
+ db 13,10,'$'
+;
+; Couldn't initialize XMS driver.
+;
+ER_NOHIMEM db 13,10
+ db ' Cannot run this 16-bit protected mode application;',13,10,13,10
+ db ' The DOS extender has encountered an error initializing the extended ',13,10
+ db ' memory manager.',13,10
+ db 13,10,'$'
+;
+; Non-specific unable to initialize DOSX error.
+;
+ER_DXINIT db 13,10
+ db ' Cannot run this 16-bit protected mode application;',13,10,13,10
+ db ' The DOS extender encounted a non-specific error.'
+ db 13,10,'$'
+;
+; A DOS memory allocation failed.
+;
+ER_REALMEM db 13,10
+ db ' Cannot run this 16-bit protected mode application;',13,10,13,10
+ db ' There is insufficient conventional memory.',13,10,13,10
+ db 13,10,'$'
+;
+; Couldn't get enough extended memory to run.
+;
+ER_EXTMEM db 13,10
+ db ' Cannot run this 16-bit protected mode application;',13,10,13,10
+ db ' There is insufficient extended memory.',13,10,13,10
+ db 13,10,'$'
+;
+; Where is KRNL[23]86.EXE!!!
+;
+ER_NOEXE db 13,10
+ db ' Cannot run this 16-bit protected mode application;',13,10,13,10
+ db ' The DOS extender could not find system files needed to run.',13,10,13,10
+ db 13,10,'$'
+
+if VCPI
+;
+; VCPI initialization failed.
+;
+ER_VCPI db 13,10
+ db ' Unable to run in Standard Mode because of a memory manager problem.'
+ db 13,10,'$'
+endif ;VCPI
+
+if VCPI
+;
+; This message is displayed if someone fails the Windows INT 2Fh startup
+; broadcast. All of the "Windows 3.0 compatible" 3rd party memory managers
+; do this.
+;
+ER_QEMM386 db 13,10
+ db ' A device driver or TSR has requested that Standard Mode'
+ db 13,10
+ db ' Windows not load now. Either remove this program, or'
+ db 13,10
+ db ' obtain an update from your supplier that is compatible'
+ db 13,10
+ db ' with Standard Mode Windows.'
+ db 13,10
+ db 13,10
+ db ' Press "y" to load Standard Mode Windows anyway.'
+ db 13,10
+ db 13,10
+ db ' Press any other key to exit to DOS.'
+ db 13,10,'$'
+endif ;VCPI
+
+DXCODE ends
+
+DXPMCODE segment
+;
+; Both of the next two messages probably mean a serious crash in Windows.
+;
+ public szFaultMessage
+;
+; Displayed if a protected mode fault is caught by DOSX.
+;
+szFaultMessage db 13,10
+ db ' DOS Extender: Untrapped protected-mode exception.',13,10,'$'
+
+ public szRing0FaultMessage
+;
+; Fault in the DOSX internal fault handler. Not recoverable.
+;
+; Note: This is for a real bad one.
+;
+szRing0FaultMessage db 13,10
+ db ' DOS Extender: Internal error.',13,10,'$'
+
+DXPMCODE ends
+
+ end
diff --git a/private/mvdm/dpmi/woaswapi.inc b/private/mvdm/dpmi/woaswapi.inc
new file mode 100644
index 000000000..9aca15dbd
--- /dev/null
+++ b/private/mvdm/dpmi/woaswapi.inc
@@ -0,0 +1,130 @@
+;----------------------------------------------------------------------------;
+; Copyright (C) Microsoft Corporation 1985-1991. All Rights Reserved. ;
+;----------------------------------------------------------------------------;
+
+;----------------------------------------------------------------------------;
+; This file has the equates for the switch api calls. ;
+; ;
+; History: ;
+; ;
+; Thu Aug-23-1990. -by- Amit Chatterjee [amitc] ;
+; Created for Switcher. (Added the History legend) ;
+;----------------------------------------------------------------------------;
+
+;----------------------------------------------------------------------------;
+; define constants for the switch API. ;
+;----------------------------------------------------------------------------;
+
+SWAPI_BUILD_CHAIN equ 4B01h ;INT 2F code for the Switch API
+SWAPI_DETECT_SWITCHER equ 4B02h ;call to detect presence of switcher
+SWAPI_ALLOCATE_SW_ID equ 4B03h ;allocates switcher ID (done by shell)
+SWAPI_FREE_SW_ID equ 4B04h ;frees switcher ID (done by shell)
+SWAPI_GET_INST_DATA equ 4B05h ;gets instance data
+SWAPI_ALLOC_MEM_CALLIN equ 4B06h ;allocate global memory call in
+
+SWAPI_ALLOC_MEM equ 0001h ;allocate global memory
+
+
+ ;-----------------------------------------;
+ ; call out equates. ;
+ ;-----------------------------------------;
+
+SWAPI_INIT_SWITCHER equ 0 ;switcher starts
+SWAPI_QUERY_SUSPEND equ 1 ;Query_Suspend
+SWAPI_SUSPEND equ 2 ;Suspend session
+SWAPI_RESUME equ 3 ;Resume session
+SWAPI_SESSION_ACTIVE equ 4 ;resume session now active
+SWAPI_CREATE equ 5 ;Create session
+SWAPI_Destroy equ 6 ;Destroy session
+SWAPI_SWITCHER_EXIT equ 7 ;switcher exits
+
+ ;-----------------------------------------;
+ ; call in equates. ;
+ ;-----------------------------------------;
+
+SWAPI_GETVERSION equ 0 ;GetVersion call in
+SWAPI_TESTMEMORYREGION equ 1 ;TestMemoryRegion
+SWAPI_SUSPEND_SWITCHER equ 2 ;SuspendSwitcher
+SWAPI_RESUME_SWITCHER equ 3 ;ResumeSwitcher
+SWAPI_HOOK_CALLOUT equ 4 ;Hook CallOut
+SWAPI_UNHOOK_CALLOUT equ 5 ;UnHook CallOut
+SWAPI_QUERY_API_SUPPORT equ 6 ;get network API support details
+
+
+ ;----------------------------------------;
+ ; Switch API call back info structure. ;
+ ;----------------------------------------;
+
+Switch_Call_Back_Info STRUC
+
+SCBI_Next dd ? ;pointer to next structure in list
+SCBI_Entry_Pt dd ? ;CS:IP of entry point procedure
+SCBI_Reserved dd ? ;used by the switcher
+SCBI_API_Ptr dd ? ;pinter to list of API structures
+
+Switch_Call_Back_Info ENDS
+
+ ;----------------------------------------;
+ ; structure for API support details. ;
+ ;----------------------------------------;
+
+API_Info_Struc STRUC
+
+AIS_Length dw ? ;length of the structure
+AIS_API dw ? ;the API ID value
+AIS_Major_Ver dw ? ;major version of API spec
+AIS_Minor_Ver dw ? ;minor version of the API spec
+AIS_Support_Level dw ? ;support level
+
+API_Info_Struc ENDS
+
+ ;----------------------------------------;
+ ; currently defined API ID values. ;
+ ;----------------------------------------;
+
+API_NETBIOS equ 1 ;Netbios
+API_8022 equ 2 ;802.2
+API_TCPIP equ 3 ;TCP/IP
+API_LANMAN equ 4 ;LAN Manager named pipes
+API_IPX equ 5 ;NetWare IPX
+
+ ;---------------------------------------;
+ ; currently defined support levels ;
+ ;---------------------------------------;
+
+API_SL_STOPALL equ 1 ;stop all calls
+API_SL_MINIMAL equ 2 ;stop asynchronous calls
+API_SL_API equ 3 ;API level support
+API_SL_SW_COMPT equ 4 ;switcher compatible
+API_SL_SEAMLESS equ 5 ;seamless support
+
+ ;---------------------------------------;
+ ; structure for return from get version ;
+ ;---------------------------------------;
+
+Switcher_Ver_Struc STRUC
+
+SVS_API_Major dw ? ;major version of the specs
+SVS_API_Minor dw ? ;minor version of the specs
+SVS_Product_Major dw ? ;major version of the task switcher
+SVS_Product_Minor dw ? ;minor version of the product
+SVS_Switcher_ID dw ? ;ID of the switcher
+SVS_Flags dw ? ;enabled/disabled
+SVS_Name_Ptr dd ? ;long pointer to ID string
+SVS_Prev_Switcher dd ? ;pointer to next switcher
+
+Switcher_Ver_Struc ENDS
+
+ ;----------------------------------------;
+ ; constants used by the Switcher ;
+ ;----------------------------------------;
+
+OUR_API_MAJOR equ 1 ;major ver of the specs
+OUR_API_MINOR equ 0 ;minor ver of the specs
+OUR_PRODUCT_MAJOR equ 5 ;major ver of the switcher
+OUR_PRODUCT_MINOR equ 0 ;minor ver of the switcher
+
+OUR_NB_MAJOR_VER equ 2 ;major version of NetBios
+OUR_NB_MINOR_VER equ 0 ;minor version of NetBios
+
+;----------------------------------------------------------------------------;
diff --git a/private/mvdm/dpmi/wowmac.inc b/private/mvdm/dpmi/wowmac.inc
new file mode 100644
index 000000000..93bc872be
--- /dev/null
+++ b/private/mvdm/dpmi/wowmac.inc
@@ -0,0 +1,59 @@
+; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
+
+;****************************************************************
+;* *
+;* WOWMAC.INC - Macros to do wow specific stuff *
+;* *
+;****************************************************************
+;* *
+;* Module Description: *
+;* This module contains macros to hide the 386 specific *
+;* stuff in the dos extender. In general, these macros *
+;* expand to the original code for non-386, and special *
+;* code for 386 *
+;****************************************************************
+
+;
+; These two macros are used by the dos extender to hide the differences
+; in building a dosx stack frame from a 32 bit frame and from a 16 bit frame
+;
+EnterInt macro
+ifdef WOW_x86
+ call [pfnEnterIntHandler]
+else
+ call EnterIntHandler
+endif
+ endm
+
+LeaveInt macro
+ifdef WOW_x86
+ call [pfnLeaveIntHandler]
+else
+ call LeaveIntHandler
+endif
+ endm
+
+;
+; These two macros are used to hide the difference in the int stack frame
+; as used by the routines which switch to real mode to call int routines.
+;
+
+pushaw macro
+ifdef WOW_x86
+.386p
+ pushad
+.286p
+else
+ pusha
+endif
+ endm
+
+popaw macro
+ifdef WOW_x86
+.386p
+ popad
+.286p
+else
+ popa
+endif
+ endm