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 ;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 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 -- 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, 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