diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/mvdm/dpmi | |
download | NT4.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')
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 ¯o 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 ¯o + 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 ¯o +?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 ¯o +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 ¯o 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 ¯o +??error <unterminated procedure definition: "&n"> +&endm +.cref +endm +?pg1 macro n,c,a,o,w,f,d,r,cc +.xcref +.xcref cEnd +cEnd ¯o 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 ¯o s +grp&_in <seg>,s +&endm +.xcref +.xcref grp&_in +.cref +grp&_in ¯o sl,s +ifb <s> +grp group sl +else +grp&_add ¯o 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 ¯o +.xcref +.xcref ?mf +.cref +?mf &¯o 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 ¯o +n ends +purge ?mf +purge sEnd +&endm +endm +?cs3 macro ln,n +.xcref +.xcref ln&_assumes +.cref +ln&_assumes ¯o 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 ¯o 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 ¯o + 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 ¯o + ?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 ¯o ;;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 ¯o 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 ¯o + ??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 ¯o 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 ¯o s + grp&_in <seg>,s + &endm + .xcref + .xcref grp&_in + .cref + grp&_in ¯o sl,s + ifb <s> + grp group sl + else + grp&_add ¯o 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 ¯o + .xcref + .xcref ?mf + .cref + ?mf &¯o 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 ¯o + n ends + purge ?mf ;;delete the makeframe macro + purge sEnd + &endm +endm + + +?cs3 macro ln,n + .xcref + .xcref ln&_assumes + .cref + ln&_assumes ¯o 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 ¯o 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 ¯o 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 ¯o labl + jn&cc $+5 ;;Skip around call + call labl + &endm + +calln&cc ¯o labl + j&cc $+5 ;;Skip around call + call labl + &endm + +jmp&cc ¯o labl + jn&cc $+5 ;;Skip around jmp + jmp labl + &endm + +jmpn&cc ¯o 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 |