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 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 GetSegmentAddress:NEAR extrn EnterRealMode:NEAR extrn EnterProtectedMode:NEAR extrn SelOff2SegOff:NEAR extrn ParaToLDTSelector:NEAR extrn NSetSegmentDscr:FAR extrn ChildTerminationHandler:NEAR ; ------------------------------------------------------- ; DATA SEGMENT DEFINITIONS ; ------------------------------------------------------- DXDATA segment extrn selGDT:WORD extrn segPSPChild:WORD extrn npXfrBuf1:WORD extrn rgbXfrBuf0:BYTE IFDEF WOW_x86 extrn FastBop:FWORD 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 ;======================================================================= ;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 DXDATA ends ; ------------------------------------------------------- ; CODE SEGMENT VARIABLES ; ------------------------------------------------------- DXCODE segment extrn segDXData:WORD extrn selDgroup:WORD DXCODE ends DXPMCODE segment extrn selDgroupPM:WORD extrn segDXCodePM:WORD extrn segDXDataPM:WORD ; ------------------------------------------------------- ; 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 ;9 - Print string exitcd <> entrycd ;0A - Buffered Keyboard Input exitcd 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 ;11 - Search for First Entry exitcd entrycd ;12 - Search for Next Entry exitcd entrycd ;13 - Delete File exitcd <> entrycd <> ;14 - Sequential Read ** Unsupported! ** exitcd <> entrycd <> ;15 - Sequential Write ** Unsupported! ** exitcd <> entrycd <> ;16 - Create File ** Unsupported! ** exitcd <> entrycd ;17 - Rename File exitcd <> entrycd <> ;18 - Used Internally by DOS exitcd <> entrycd <> ;19 - Current Disk exitcd <> entrycd ;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 ;29 - Parse Filename exitcd 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 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 entrycd <> ;36 - Get Disk Free Space exitcd <> entrycd <> ;37 - Used Internally by DOS exitcd <> entrycd ;38 - Set/Get Country Dependent Info exitcd entrycd ;39 - MKDIR exitcd <> entrycd ;3A - RMDIR exitcd <> entrycd ;3B - CHDIR exitcd <> entrycd ;3C - Create a File exitcd <> entrycd ;3D - Open a File exitcd <> entrycd <> ;3E - Close a File Handle exitcd <> entrycd ;3F - Read from a File or Device exitcd entrycd ;40 - Write to a File or Device exitcd <> entrycd ;41 - Delete a File from a Specified Directory exitcd <> entrycd <> ;42 - Move File Read/Write Pointer exitcd <> entrycd ;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 ;47 - Get Current Directory exitcd entrycd <> ;48 - Allocate Memory exitcd <> entrycd <,ES$,> ;49 - Free Allocated Memory exitcd <> entrycd <,ES$,> ;4A - Modify Allocated Memory Blocks exitcd <> entrycd ;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 ;4E - Find First Matching File exitcd entrycd ;4F - Find Next Matching File exitcd 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 ;53 - Set Drive Parameter Block exitcd <> entrycd <> ;54 - Get Verify Setting exitcd <> entrycd <,DSDX,> ;55 - Duplicate PSP exitcd <> entrycd ;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 ;5A - Create Temporary File exitcd entrycd ;5B - Create New File exitcd <> entrycd <> ;5C - Lock/Unlock File Access exitcd <> entrycd ;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 ; ** 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 ;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 ;5E/00 - Get Machine Name exitcd entrycd ;5E/01 - Set Machine name exitcd <> entrycd ;5E/02 - Set Printer Setup Str exitcd <> entrycd ;5E/03 - Get Printer Setup Str exitcd int215F02 entrycd ;5F/02 - Get Redir List Entry exitcd entrycd ;5F/03 - Set Redir List Entry exitcd <> entrycd ;5F/04 - Cancel Redirection exitcd <> entrycd ;5F/05 - Get Redir List Entry exitcd ifdef DBCS int2163 entrycd <> ;63/00 - Get Lead Byte Table Entry exitcd <,DSSI,PTR$> endif ; DBCS int21esdi entrycd ;56 & 5F/02&03&05 eXtra buffer exitcd ; ; We only use the entry code from the following. If we don't ; we trash the stack in applications like borland c++ ; int21pfn entrycd ;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 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 ; 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 mov ax,segDXDataPM ;ax = DOS extender real-mode dgroup segment 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,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 mov ds,segDXData 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: 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 dosentry: 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 -- 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,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 ; ------------------------------------------------------- ; 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 or ax,ax ; NULL selector (krnl386 does this) jnz @f xor bx,bx ; zero environment segment jmp dec_HaveEnvSeg @@: call GetSegmentAddress test bx,0fff0h ; >1mb? jz @f ; no xor bx,bx ; yes, zero environment segment xor dx,dx @@: shr dx, 4 shl bx, 12 or bx, dx ;seg of environment dec_HaveEnvSeg: 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 word ptr [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,SEL_DOSSCR cCall NSetSegmentDscr, 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 mov dx,segDXDataPM 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 mov dx,segDXDataPM 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 ; ------------------------------------------------------- ; ------------------------------------------------------- ; 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