NT4/private/mvdm/dpmi/dxmain.asm
2020-09-30 17:12:29 +02:00

3377 lines
111 KiB
NASM

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