windows-nt/Source/XPSP1/NT/base/mvdm/dpmi/dxint31.asm

2636 lines
78 KiB
NASM
Raw Normal View History

2020-09-26 03:20:57 -05:00
PAGE ,132
TITLE DXINT31.ASM -- Dos Extender Int 31h Handler
; Copyright (c) Microsoft Corporation 1989-1991. All Rights Reserved.
;***********************************************************************
;
; DXINT31.ASM -- DOS Extender Int 31h Handler
;
;-----------------------------------------------------------------------
;
; This module provides the Int 31h services to the protected mode
; application running under the DOS Extender.
;
;-----------------------------------------------------------------------
;
; 12/03/90 amitc 'i31_GetSetRMInt' will map vectors in the range 50-57h
; to the range 8-fh if 'Win30CommDriver' switch is set in
; system.ini
; 12/18/89 jimmat Service 0003 changed from Get LDT Base to Get Sel Incr,
; and added Virtual Interrupt State services.
; 09/18/89 jimmat Added Allocate/Free Real Mode Call-Back services
; 08/20/89 jimmat Changed A20 diddling to use XMS local enable/disable
; 06/14/89 jimmat Added a few missing and new services.
; 05/17/89 jimmat Added protected to real mode call/int services.
; 05/12/89 jimmat Original version (split out from DXINTR.ASM)
; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
;
;***********************************************************************
.286p
; -------------------------------------------------------
; INCLUDE FILE DEFINITIONS
; -------------------------------------------------------
.xlist
.sall
include segdefs.inc
include gendefs.inc
include pmdefs.inc
include interupt.inc
include int31.inc
include dpmi.inc
Int_Get_PMode_Vec EQU 04h
Int_Set_PMode_Vec EQU 05h
include intmac.inc
include stackchk.inc
.list
; -------------------------------------------------------
; GENERAL SYMBOL DEFINITIONS
; -------------------------------------------------------
RealMode_SaveBP equ word ptr RealMode_EBP+4
RealMode_SaveSP equ word ptr RealMode_EBP+6
SelectorIncrement equ 8 ;DOSX increments consecutive selectors by 8
Trans_Reset_HW equ 01h ;Reset PIC/A20 line on PM->Call services
I31VERSION equ 0090d ;Int 31 services major/minor version #'s
; version 00.90 (not quite ready for DPMI)
I31FLAGS equ 000Dh ; 386 extender, pMode NetBIOS
I31MasterPIC equ 08h ;Master PIC Interrupts start at 08h
ifdef NEC_98
I31SlavePIC equ 10h
else ;NEC_98
I31SlavePIC equ 70h ;Slave PIC Interrupts start at 70h
endif ;NEC_98
; -------------------------------------------------------
; EXTERNAL SYMBOL DEFINITIONS
; -------------------------------------------------------
extrn EnterIntHandler:NEAR
extrn LeaveIntHandler:NEAR
extrn EnterRealMode:NEAR
extrn EnterProtectedMode:NEAR
extrn GetSegmentAddress:NEAR
extrn SetSegmentAddress:NEAR
extrn FreeSelector:NEAR
extrn FreeSelectorBlock:NEAR
extrn AllocateSelector:NEAR
extrn AllocateSelectorBlock:NEAR
extrn ParaToLDTSelector:NEAR
extrn DupSegmentDscr:NEAR
extrn GetFaultVector:NEAR
extrn PutFaultVector:NEAR
extrn AllocateXmemBlock:NEAR
extrn FreeXmemBlock:NEAR
extrn ModifyXmemBlock:NEAR
extrn FreeLowBlock:NEAR
extrn AllocateLowBlock:NEAR
extrn AllocateLDTSelector:NEAR
extrn ParaToLinear:NEAR
extrn GetIntrVector:NEAR, PutIntrVector:NEAR
extrn NSetSegmentLimit:near
extrn NMoveDescriptor:near
extrn NWOWSetDescriptor:near
extrn DFSetIntrVector:near
extrn RmSaveRestoreState:far
extrn PmSaveRestoreState:far
extrn RmRawModeSwitch:far
extrn PmRawModeSwitch:far
extrn IsSelectorFree:near
extrn gtpara:near
externNP NSetSegmentAccess
externFP NSetSegmentDscr
externNP FreeSpace
; -------------------------------------------------------
; DATA SEGMENT DEFINITIONS
; -------------------------------------------------------
DXDATA segment
extrn selGDT:WORD
extrn segPSP:WORD
extrn idCpuType:WORD
extrn npXfrBuf1:WORD
extrn rgbXfrBuf1:BYTE
extrn pbReflStack:WORD
extrn bReflStack:WORD
extrn lpfnXMSFunc:DWORD
extrn A20EnableCount:WORD
extrn regUserAX:WORD, regUserFL:WORD, regUserSS:WORD
extrn regUserSP:WORD, regUserDS:WORD, regUserES:WORD
extrn PMInt24Handler:DWORD
extrn DpmiFlags:WORD
IFDEF WOW_x86
extrn FastBop:fword
ENDIF
extrn selPspChild:WORD
extrn LowMemAllocFn:DWORD
extrn LowMemFreeFn:DWORD
public i31HWReset
i31HWReset db 0 ;NZ if in 'standard' real mode state
i31_dsp_rtn dw 0 ;Int 31h service routine to dispatch
ifdef DEBUG
debugsavess dw 0 ; Usefull when debugging WOW KERNEL
debugsavesp dw 0 ;
debugsavebp dw 0 ;
debugsavecx dw 0 ;
endif
public i31_selectorbitmap
i31_selectorbitmap dw 0000000000000000b
;Reserved LDT Selectors
ifdef JAPAN
justw_flag dw 0 ; for Just Window
endif ; JAPAN
DXDATA ends
; -------------------------------------------------------
; CODE SEGMENT VARIABLES
; -------------------------------------------------------
DXCODE segment
assume cs:DXCODE
extrn segDXData:WORD
extrn selDgroup:WORD
extrn segDXCode:word
assume cs:NOTHING
DXCODE ends
DXPMCODE segment
extrn selDgroupPM:WORD
extrn segDXCodePM:WORD
i31_dispatch label word
dw 0000h, offset i31_AllocSel
dw 0001h, offset i31_FreeSel
dw 0002h, offset i31_MapSeg2Sel
dw 0003h, offset i31_GetSelIncr
dw 0004h, offset i31_Success ;lock selector memory
dw 0005h, offset i31_Success ;unlock selector mem
dw 0006h, offset i31_GetSegAddr
dw 0007h, offset i31_SetSegAddr
dw 0008h, offset i31_SetLimit
dw 0009h, offset i31_SetAccess
dw 000Ah, offset i31_CreateDataAlias
dw 000Bh, offset i31_GetSetDescriptor
dw 000Ch, offset i31_GetSetDescriptor
dw 000Dh, offset i31_SpecificSel ; Allocate specific descriptor
dw 0100h, offset i31_AllocDOSMem
dw 0101h, offset i31_FreeDOSMem
dw 0102h, offset i31_SizeDOSMem
dw 0200h, offset i31_GetSetRMInt
dw 0201h, offset i31_GetSetRMInt
dw 0202h, offset i31_GetSetFaultVector
dw 0203h, offset i31_GetSetFaultVector
dw 0204h, offset i31_GetSetPMInt
dw 0205h, offset i31_GetSetPMInt
dw 0300h, offset i31_RMCall
dw 0301h, offset i31_RMCall
dw 0302h, offset i31_RMCall
dw 0303h, offset i31_AllocCallBack
dw 0304h, offset i31_FreeCallBack
dw 0305h, offset i31_GetStateSaveRestore
dw 0306h, offset i31_GetRawModeSwitch
dw 0400h, offset i31_Version
dw 04f1h, offset i31_WOW_AllocSel
dw 04f2h, offset i31_WOW_SetDescriptor
dw 04f3h, offset i31_WOW_SetAllocFunctions
;
; INCOMPLETE !!!!!!!!!!!
; Needed by kernel.
;
dw 0500h, offset i31_GetFreeMem
dw 0501h, offset i31_AllocMem
dw 0502h, offset i31_FreeMem
;
; Fails by design if block is to be extended and this cannot be done
; in place.
;
dw 0503h, offset i31_SizeMem
dw 0600h, offset i31_Success ;lock linear region
dw 0601h, offset i31_Success ;unlock linear region
dw 0602h, offset i31_Success ;mark real mode rgn pageable
dw 0603h, offset i31_Success ;relock real mode region
dw 0604h, offset i31_PageSize ;get page size
dw 0700h, offset i31_fail ;reserved
dw 0701h, offset i31_fail ;reserved
dw 0702h, offset i31_Success ;demand page candidate
dw 0703h, offset i31_Success ;discard page contents
dw 0800h, offset i31_fail ;physical addr mapping
dw 0900h, offset i31_VirtualInt ;get & disable Int state
dw 0901h, offset i31_VirtualInt ;get & enable Int state
dw 0902h, offset i31_VirtualInt ;get Int state
;
; UNIMPLEMENTED !!!!!!!!!!!
; To be used for MSDOS protected mode API?
;
dw 0A00h, offset i31_unimplemented ;get vendor specific API
IFDEF WOW_x86
NO386 = 0
else
NO386 = 1
ENDIF
ife NO386
;
; 386 Debug Register access routines.
;
dw 0B00h, offset i31_Debug_Register_Access
;set debug watchpoint
dw 0B01h, offset i31_Debug_Register_Access
;clear debug watchpoint
dw 0B02h, offset i31_Debug_Register_Access
;get debug watchpoint state
dw 0B03h, offset i31_Debug_Register_Access
;reset debug watchpoint
endif ; NO386
dw -1, offset i31_unimplemented
DXPMCODE ends
; -------------------------------------------------------
subttl INT 31h Entry Point
page
; -------------------------------------------------------
; INT 31h SERVICE ENTRY POINT
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; PMIntr31 -- Service routine for the Protect Mode INT 31h
; services. These functions duplicate the
; Windows/386 VMM INT 31h services for protected
; mode applications. They were implemented to
; support a protect mode version of Windows/286.
;
; Input: Various registers
; Output: Various registers
; Errors:
; Uses: All registers preserved, other than return values
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public PMIntr31
PMIntr31 proc near
cld ;practice 'safe programming'
; Determine if this one of the services we support.
push bx
mov bx,offset i31_dispatch ;cs:bx -> dispatch table
@@:
cmp ax,cs:[bx] ;scan dispatch table for
jz i31_do_it ; service code in AH/AL
cmp word ptr cs:[bx],-1 ;end of table is -1
jz i31_do_it
add bx,4
jmp short @b
; BX contains the offset of the routine to service this request!
i31_do_it:
push ds ;save the service routine address
mov ds,selDgroupPM ; in our DGROUP for now
assume ds:DGROUP
mov bx,cs:[bx+2]
FCLI ;needed for [i31_dsp_rtn]
mov i31_dsp_rtn,bx
ifdef DEBUG
mov debugsavess,ss ; usefule when debugging WOW kernel
mov debugsavesp,sp
mov debugsavebp,bp
mov debugsavecx,cx
endif
pop ds
pop bx
i31_doit:
call EnterIntHandler ;build an interrupt stack frame
assume ds:DGROUP,es:DGROUP ; also sets up addressability
push i31_dsp_rtn ;routine address on stack
FSTI ;no need to keep interrupts disabled
retn ;go perform the service
i31_unimplemented: ;not implemented or undefined
Debug_Out "Unsupported Int 31h requested (AX = #AX)"
; Int 31h service routines return (jmp) here to indicate failure. The
; standard failure return sets the carry flag, and sets ax = 0.
i31_fail:
mov [bp].intUserAX,0
i31_fail_CY:
or byte ptr [bp].intUserFL,1
jmp short i31_exit
; Int 31h service routines return (jmp) here -- they jump back instead
; of returning because they expect the stack to be setup by EnterIntHandler,
; no extra items should be pushed.
i31_done:
and byte ptr [bp].intUserFL,not 1 ;clear carry flag
i31_exit:
assume ds:NOTHING,es:NOTHING
FCLI ;LeaveIntHandler needs them off
call LeaveIntHandler ;restore caller's registers, and
riret ; get on down the road
PMIntr31 endp
; -------------------------------------------------------
; This routine is for Int 31h services that the 286 DOS extender
; allows, but doesn't actually perform any work. Most of these
; services are related to demand paging under Windows/386.
assume ds:DGROUP,es:DGROUP
public i31_Success
i31_Success proc near
jmp i31_done ;nothing to do currently
i31_Success endp
; -------------------------------------------------------
; Page Size. Gotta be 1000h, even if we don't do anything
; with it.
;
assume ds:DGROUP,es:DGROUP
public i31_PageSize
i31_PageSize proc near
mov [bp].intUserBX,0
mov [bp].intUserCX,1000h
jmp i31_done ;nothing to do currently
i31_pageSize endp
; -------------------------------------------------------
subttl INT 31h LDT/Heap Services
page
; -------------------------------------------------------
; LDT/HEAP INTERRUPT (INT 31h) SERVICE ROUTINES
; -------------------------------------------------------
; -------------------------------------------------------
; Service 00/00 - Allocate Space in LDT for Selector
; in: cx - # selectors required
; out: ax - *index* of first selector
assume ds:DGROUP,es:DGROUP
public i31_AllocSel
i31_AllocSel proc near
jcxz short i31_15
cmp cx,1 ;1 selector or more?
ja @f
call AllocateSelector ;allocate 1 selector
jc i31_15
jmp short i31_10
@@: mov ax,cx
push cx ; save # of selector to be allocated
xor cx,cx ; allocate from lower range
call AllocateSelectorBlock ;allocate a block of selectors
pop cx
jc i31_15
i31_10:
or al,STD_TBL_RING ;add standard table/ring bits
mov [bp].intUserAX,ax ;return 1st/only selector in AX
setsel: mov bx,STD_DATA ;all the selectors we allocate
; xor bl,bl ; get initialized to data/ring ?
xor dx,dx ; 0 base, 0 limit
@@:
cCall NSetSegmentDscr,<ax,dx,dx,dx,dx,bx>
add ax,SelectorIncrement
loop @b
jmp i31_done
i31_15:
jmp i31_fail ;fail the request
i31_AllocSel endp
; -------------------------------------------------------
; Service 00/01 - Free LDT Selector
; in: bx - selector to free
; out: none
assume ds:DGROUP,es:DGROUP
public i31_FreeSel
i31_FreeSel proc near
mov ax,bx ;release the selector
cmp ax,SEL_DPMI_LAST ; reserved selector?
ja i31_Free_Regular_LDT_Selector ; No.
mov cx,ax
shr cl,3 ; Selector to selector index in LDT
mov ax,1
shl ax,cl ; AX = bit in i31_selectorbitmap
test i31_selectorbitmap,ax ; test for already allocated
jz i31_FreeSel_Fail ; already free!
not ax
and i31_selectorbitmap,ax ; mark as free
jmp i31_done
i31_Free_Regular_LDT_Selector:
and ax,SELECTOR_INDEX ; only pass it the index
or ax,SELECTOR_TI ;only allow LDT selectors
jz i31_Check_DS_ES
call FreeSelector
jnc i31_Check_DS_ES
i31_FreeSel_Fail:
jmp i31_fail_CY
i31_Check_DS_ES:
; check in case user frees the selector in DS or ES - we
; don't want to fault when popping his regs
and bl,NOT SELECTOR_RPL ;compare without ring bits
mov ax,[bp].pmUserDS
and al,NOT SELECTOR_RPL
cmp ax,bx
jnz @f
mov [bp].pmUserDS,0
@@: mov ax,[bp].pmUserES
and al,NOT SELECTOR_RPL
cmp ax,bx
jnz @f
mov [bp].pmUserES,0
@@:
jmp i31_done
i31_FreeSel endp
; -------------------------------------------------------
; Service 00/02 - Map Segment to Selector
; in: bx - real mode segment value
; out: ax - selector which maps area
assume ds:DGROUP,es:DGROUP
public i31_MapSeg2Sel
i31_MapSeg2Sel proc near
mov ax,bx ;find/make selector for real memory
mov bx,STD_DATA ;assume it's a data selector
call ParaToLDTSelector
jnc @f
jmp i31_fail_CY ;Falied!?
@@:
mov [bp].intUserAX,ax
jmp i31_done
i31_MapSeg2Sel endp
; -------------------------------------------------------
; Service 00/03 - Get Next Selector Increment Value
; in: none
; out: ax - Next Selector Increment Value
assume ds:DGROUP,es:DGROUP
public i31_GetSelIncr
i31_GetSelIncr proc near
mov [bp].intUserAX,SelectorIncrement ;DOSX incr value
jmp i31_done
i31_GetSelIncr endp
; -------------------------------------------------------
; Service 00/06 - Get Segment Base address.
; in: bx - selector
; out: cx:dx - 32 bit lma of segment
assume ds:DGROUP,es:DGROUP
public i31_GetSegAddr
i31_GetSegAddr proc near
mov ax,selGDT
assume es:nothing
lsl ax,ax
push bx
and bx,SELECTOR_INDEX
cmp bx,ax
jnc gsa10 ; not in ldt
call IsSelectorFree
jc gsa20
gsa10: pop bx
jmp i31_fail_CY
gsa20: pop bx
mov ax,bx
call GetSegmentAddress
mov [bp].intUserCX,bx
mov [bp].intUserDX,dx
jmp i31_done
i31_GetSegAddr endp
; -------------------------------------------------------
; Service 00/07 - Set Segment Base address.
; in: bx - selector
; cx:dx - 32 bit lma of segment
; out: none
assume ds:DGROUP,es:DGROUP
public i31_SetSegAddr
i31_SetSegAddr proc near
mov ax,selGDT
assume es:nothing
lsl ax,ax
push bx
and bx,SELECTOR_INDEX
cmp bx,ax
jnc ssa10 ; not in ldt
call IsSelectorFree
jc ssa20
ssa10: pop bx
jmp i31_fail_CY
ssa20: pop bx
mov ax,bx
mov bx,cx
call SetSegmentAddress
jmp i31_done
i31_SetSegAddr endp
; -------------------------------------------------------
; Service 00/08 - Set Segment Limit.
; in: bx - selector
; cx:dx - 32 bit limit of segment
; out: none
assume ds:DGROUP,es:DGROUP
public i31_SetLimit
i31_SetLimit proc near
mov ax,selGDT
assume es:nothing
lsl ax,ax
push bx
and bx,SELECTOR_INDEX
cmp bx,ax
jnc sl10 ; not in ldt
call IsSelectorFree
jc sl20
sl10: pop bx
jmp i31_fail_CY
sl20: pop bx
mov es,selGDT
and bx,SELECTOR_INDEX
and es:[bx].cbLimitHi386,070h ; clear 'G' bit and old
; extended limit bits
test cx,0fff0h ; bits 20-31 set?
jz i31_SetLimit_0 ; No
shr dx,12d ; Yes
mov ax,cx
shl ax,4d
or dx,ax
shr cx,12d
or es:[bx].cbLimitHi386,080h ; set 'G' bit
i31_SetLimit_0:
mov es:[bx].cbLimit,dx
or es:[bx].cbLimitHi386,cl
cCall NSetSegmentLimit,<bx>
jmp i31_done
i31_SetLimit endp
; -------------------------------------------------------
; Service 00/09 - Set Segment Access Rights
; in: bx - selector
; cl - segment access rights byte
assume ds:DGROUP,es:DGROUP
public i31_SetAccess
i31_SetAccess proc near
mov ax,selGDT
assume es:nothing
lsl ax,ax
push bx
and bx,SELECTOR_INDEX
cmp bx,ax
jnc sa10 ; not in ldt
call IsSelectorFree
jc sa20
sa10: pop bx
sa11:
jmp i31_fail_CY
sa20: pop bx
test cl,010000b ; system segment?
jz sa11 ; y: error
push cx
and cl,AB_DPL ; mask off all but DPL bits
cmp cl,STD_DPL ; is this the correct DPL?
pop cx
jnz sa11 ; n: error
cCall NSetSegmentAccess,<bx,cx>
jmp i31_done
i31_SetAccess endp
; -------------------------------------------------------
; Service 00/0A - Create Data Segment Alias (for a code seg)
; in: bx - selector
; out: ax - new data selector
assume ds:DGROUP,es:DGROUP
public i31_CreateDataAlias
i31_CreateDataAlias proc near
mov ax,bx ;make sure it's a vaild selector
verr ax
jnz cda_failed
mov bx,ax ;get a new selector for the alias
ifdef JAPAN
justw_check:
endif ; JAPAN
call AllocateSelector
jc cda_failed
ifdef JAPAN
cmp [justw_flag],0
jne not_justw
mov [justw_flag],1
jmp justw_check
not_justw:
endif ; JAPAN
xchg ax,bx ;copy old to new
call DupSegmentDscr
mov es,selGDT
and bx,SELECTOR_INDEX
mov al,es:[bx].arbSegAccess
and al,11100000b ;mask off all but present and DPL bits
or al,AB_DATA or AB_WRITE
mov ah, es:[bx].cbLimitHi386
cCall NSetSegmentAccess,<bx,ax>
or bl,STD_TBL_RING ;set standard table/ring bits
mov [bp].intUserAX,bx
jmp i31_done
cda_failed:
jmp i31_fail_CY
i31_CreateDataAlias endp
; -------------------------------------------------------
; Service 00/0B - Get Descriptor
; Service 00/0C - Set Descriptor
; in: bx - selector
; es:di -> buffer to hold copy of descriptor
assume ds:DGROUP,es:DGROUP
public i31_GetSetDescriptor
i31_GetSetDescriptor proc near
.386p
push esi
push edi
.286p
mov ax,selGDT
mov es,ax
assume es:NOTHING
lsl ax,ax
and bx,SELECTOR_INDEX
cmp bx,ax ; range-test selector against
jc @f ; the limit of the GDT/LDT
.386p
pop edi
pop esi
.286p
jmp i31_fail_CY ;fail if invalid selector specified
@@:
call IsSelectorFree
jc @f
.386p
pop edi
pop esi
.286p
jmp i31_fail_CY
@@:
cmp byte ptr [bp].intUserAX,SelMgt_Get_Desc ;Get or Set?
jz i31_GetDscr
;
; Set Descriptor
;
test DpmiFlags,DPMI_32BIT
jnz gsd10
.386p
mov esi,0 ; zero high half
mov edi,0
jmp gsd20
gsd10: mov esi,edi ; get high half of edi
mov edi,0 ; zero high half
.286p
gsd20:
push ds ;Set -
mov ds,[bp].pmUserES
assume ds:nothing
mov si,[bp].pmUserDI ; ds:si -> caller's buffer
mov di,bx ; es:di -> dscr slot in GDT/LDT
.386p
mov cl,ds:[esi].arbSegAccess386
.286p
test cl,010000b ; system segment?
jz gsd25 ; y: error
and cl,AB_DPL ; mask off all but DPL bits
cmp cl,STD_DPL ; is this the correct DPL?
jnz gsd25 ; n: error
jmp short i31_MovDscr
gsd25: ; set ldt format error
pop ds
.386p
pop edi
pop esi
.286p
jmp i31_fail_CY
i31_GetDscr:
assume ds:DGROUP
;
; Get Descriptor
;
test DpmiFlags,DPMI_32BIT
jnz gsd30
.386p
mov edi,0 ; zero high half of edi
gsd30: mov esi,0 ; zero high half of esi
.286p
push ds ;Get -
push es
pop ds
assume ds:nothing
mov si,bx ; ds:si -> dscr slot in GDT/LDT
mov es,[bp].pmUserES
mov di,[bp].pmUserDI ; es:di -> caller's buffer
i31_MovDscr:
.386p
cCall NMoveDescriptor,<ds,esi,es,edi>
.286p
i31_gs25:
pop ds
assume ds:DGROUP
.386p
pop edi
pop esi
.286p
jmp i31_done
i31_GetSetDescriptor endp
; -------------------------------------------------------
; Service 00/0D - Allocate Specific LDT Selector
; in: bx - selector
; out: carry clear if selector was allocated
; carry set if selector was not allocated
assume ds:DGROUP,es:DGROUP
public i31_SpecificSel
i31_SpecificSel proc near
and bx,SELECTOR_INDEX
cmp bx,SEL_DPMI_LAST
ja i31_SpecificSel_Fail
mov cx,bx
shr cl,3 ; Selector to selector index in LDT
mov ax,1
shl ax,cl ; AX = bit in i31_selectorbitmap
test i31_selectorbitmap,ax ; test for already allocated
jnz i31_SpecificSel_Fail ; allocated, fail
or i31_selectorbitmap,ax ; mark as allocated
;
; Set up the DPL and size
;
mov ax,bx
mov cx,1
jmp setsel
i31_SpecificSel_Fail: ; couldn't get that one
jmp i31_fail_CY
i31_SpecificSel endp
; -------------------------------------------------------
subttl INT 31h DOS Memory Services
page
; -------------------------------------------------------
; INT 31h DOS MEMORY SERVICE ROUTINES
; -------------------------------------------------------
; -------------------------------------------------------
; Service 01/00 - Allocate DOS Memory Block
;
; In: BX - # paragraphs to allocate
; Out: If successful: Carry Clear
; AX - segment address of block
; DX - selector to access block
;
; If unsuccessful: Carry Set
; AX - DOS error code
; BX - size of largest available block in paragraphs
;
; 13-Feb-1991 -- ERH This call is not supported under Windows 3.1,
; but try to find a block in our net heap to
; satisfy the request, anyway.
;
assume ds:DGROUP,es:DGROUP
public i31_AllocDOSMem
i31_AllocDOSMem proc near
and [bp].intUserFL,NOT 1 ; clear carry in status
mov cx,[bp].intUserBX
mov dx,[bp].intUserBX
shr dx,12 ; high half of byte count
shl cx,4 ; low half of byte count
call AllocateLowBlock
jc adm30
mov [bp].intUserDX,ax
mov si,ax
call GTPARA ; get paragraph address
mov [bp].intUserAX,ax
jmp i31_done
adm30: shr cx,4
shl dx,12
or cx,dx ; paragraph size of largest
ifdef JAPAN
or cx,cx
jz notdec
dec cx
notdec:
endif ; JAPAN
mov [bp].intUserBX,cx
mov [bp].intUserAX,ax ; error code
jmp i31_fail_CY
i31_AllocDOSMem endp
; -------------------------------------------------------
; Service 01/01 - Release DOS Memory Block
;
; In: DX - SELECTOR of block to release
; Out: If successful: Carry Clear
;
; If unsuccessful: Carry Set
; AX - DOS error code
assume ds:DGROUP,es:DGROUP
public i31_FreeDOSMem
i31_FreeDOSMem proc near
mov ax,[bp].intUserDX
verw ax
jnz fdm60
push bx
mov bx, ax ; selector
and bx,SELECTOR_INDEX ; make it index into ldt
call IsSelectorFree
pop bx
jnc fdm60
mov ax,[bp].intUserDX
call FreeLowBlock
jc fdm60
jmp i31_Success
fdm60: mov [bp].intUserAX,ax
jmp i31_fail_CY
i31_FreeDOSMem endp
; -------------------------------------------------------
; Service 01/02 - Resize DOS Memory Block
;
; In: BX - new block size in paragraphs
; DX - SELECTOR of block to release
; Out: If successful: Carry Clear
;
; If unsuccessful: Carry Set
; AX - DOS error code
assume ds:DGROUP,es:DGROUP
public i31_SizeDOSMem
i31_SizeDOSMem proc near
mov [bp].intUserBX,0
mov [bp].intUserAX,08h ; insufficient mem. available
jmp i31_fail_CY
i31_SizeDOSMem endp
; -------------------------------------------------------
subttl INT 31h Real Mode Int Vector Routines
page
; -------------------------------------------------------
; INT 31h INTERRUPT SERVICES
; -------------------------------------------------------
; -------------------------------------------------------
; i31_GetSetRMInt -- Get/Set Real Mode Interrupt Vector
;
; In: bl - Interrupt #
; cx:dx - SEG:Offset of real mode int vector (if set)
; Out: cx:dx - SEG:Offset of real mode int vector (if get)
assume ds:DGROUP,es:DGROUP
public i31_GetSetRMInt
i31_GetSetRMInt proc near
push SEL_RMIVT or STD_RING ;address the real mode IVT
pop es
assume es:NOTHING
xor bh,bh ;convert int # to offset
shl bx,2
FCLI ;play it safe
cmp al,Int_Get_Real_Vec ;Get or Set?
jnz i31_gs_set
mov cx,word ptr es:[bx+2] ;get segment:offset
mov dx,word ptr es:[bx]
mov [bp].intUserCX,cx ;return them to caller
mov [bp].intUserDX,dx
jmp short i31_gs_ret
i31_gs_set: ;setting the vector...
mov es:[bx],dx ;set the real mode IDT vector
mov es:[bx+2],cx
i31_gs_ret:
FSTI
jmp i31_done
i31_GetSetRMInt endp
; -------------------------------------------------------
; i31_GetSetFaultVector -- Get/Set Protected Mode Fault Vector (0h-1Fh)
;
; In: bl - Interrupt #
; cx:dx - Sel:Offset of pMode int vector (if set)
; Out: cx:dx - Sel:Offset of pMode int vector (if get)
assume ds:DGROUP,es:DGROUP
public i31_GetSetFaultVector
i31_GetSetFaultVector proc near
cmp bl,10h ; zero to 10h are defined for 80386
jbe @f
jmp i31_fail_CY ;must be <= 10h or fail it
@@:
xor bh,bh
mov ax,bx ;interrupt # to AX
cmp byte ptr [bp].intUserAX,Int_Get_Excep_Vec ;Get or Set?
jne i31_gfv_set
call GetFaultVector ;wants to get the vector
mov [bp].intUserCX,cx
mov [bp].intUserDX,dx
jmp short i31_gfv_ret
i31_gfv_set:
.386p
test DpmiFlags,DPMI_32BIT
jnz sfv10
movzx edx,dx ; zero high half
.286p
sfv10: call PutFaultVector ;doing a set (args already set)
i31_gfv_ret:
jmp i31_done
i31_GetSetFaultVector endp
; -------------------------------------------------------
; i31_GetSetPMInt -- Get/Set Protected Mode Interrupt Vector
;
; In: bl - Interrupt #
; cx:dx - SEL:Offset of protected mode int vector (if set)
; Out: cx:dx - SEL:Offset of protected mode int vector (if get)
assume ds:DGROUP,es:DGROUP
public i31_GetSetPMInt
i31_GetSetPMInt proc near
xchg al,bl
xor ah,ah
cmp bl,Int_Get_PMode_Vec ;Get or Set?
jnz i31_gsp_set
; NOTE: we don't call DFGetIntrVector here, because all it does is a call to
; the following routine
call GetIntrVector
mov [bp].intUserCX,cx
mov [bp].intUserDX,dx
jmp i31_gsp_done
i31_gsp_set:
; set up the appropriate real mode reflector, and hook the int.
call DFSetIntrVector
i31_gsp_done:
FSTI
jmp i31_done
i31_GetSetPMInt endp
; -------------------------------------------------------
subttl INT 31h Protected-to-Real Mode Call/Int
page
; -------------------------------------------------------
; INT 31h PROTECTED-TO-REAL MODE CALL/INT SERVICES
; -------------------------------------------------------
; -------------------------------------------------------
; Service 03/00 -- Simulate real mode interrupt
; Service 03/01 -- Call real mode procedure with far return frame
; Service 03/02 -- Call real mode procedure with iret return frame
;
; In: es:di -> client register structure for real mode
; bl = interrupt number (03/00 only)
; cx = # words to copy from protected mode stack
; Out: es:di -> updated client register structure
assume ds:DGROUP,es:DGROUP
public i31_RMCall
i31_RMCall proc near
; First off, we need to copy the client register structure down to
; real mode addressable memory... Lets use rgbXfrBuf1 for now...
; Changed to use the interrupt reflector stack because these routines
; need to be reentrant. This also lets us leave interrupts enabled
; longer.
; Earleh - 27-Jun-1990.
mov di,sp ; DI = SP = client regs. buffer
sub di,size Real_Mode_Call_Struc+2 ; Make room for client
sub di,[bp].intUserCX ; call structure plus
sub di,[bp].intUserCX ; any optional stack params
sub di,32 ; pushad
push di ; plus copy of di.
.386p
pushad ; have to save high 16 bits
.286p
mov sp,di
; --------------------------------------------------------------
;
; The interrupt reflector stack frame now looks like this...
;
; pbReflStack + CB_STKFRAME-> ---------------------------
; (old pbReflStack) | |
; | INTRSTACK Struc |
; | from EnterIntHandler |
; | |
; ---------------------------
; pbReflStack + CB_STKFRAME | word pointer |>>--\
; - (SIZE INTRSTACK) - 2 -> --------------------------- |
IFDEF WOW_x86
; | | |
; | Pushad frame | |
; | | |
; --------------------------- |
ENDIF
; | | |
; | optional stack params | |
; | | |
; --------------------------- |
; | | |
; | | |
; | Real_Mode_Call_Struc | |
; | | |
; | | |
; ---------------------------<<--/
; | |
; | Available stack space |
; | |
; pbReflStack---------->---------------------------
;
; After returning from the real mode procedure, we will need
; to fetch the word pointer stored just below the EnterIntHandler
; frame, and use it to access our temporary Real_Mode_Call_Struc.
; In addition to holding the client register values, this also
; holds the SP and BP values we will need to switch back to our
; stack when we return from the real mode procedure.
;
; !!! -- Storing the optional stack parameters on top of our
; Real_Mode_Call_Struc has several problems. It eats up stack
; space if we call the real mode procedure on our stack, because
; we then have to make another copy of these. It also means we have to
; carry around a pointer to where the Real_Mode_Call_Struc lives.
; If we didn't have the stack parameters on top of the Real_Mode_Call_Struc,
; then we could find things strictly by offset from the value in
; pbReflStack. The picture above should be rearranged to make optimal
; use of space. I basically did it this way so I could have a minimum-
; change fix for a bug in Windows 3.0.
; !!!
; --------------------------------------------------------------
;
;
cld
.386p
xor ecx,ecx
.286p
mov cx,(size Real_Mode_Call_Struc) / 2
mov bx,di ;bx used to reference client regs below
.386p
test DpmiFlags,DPMI_32BIT
jz rmc10
mov esi,edi ; copy over high 16 bits
jmp rmc20
rmc10: xor esi,esi ; clear high 16 bits esi
rmc20: xor edi,edi ; clear high 16 bits edi
mov di,bx
.286p
mov si,[bp].pmUserDI
mov ds,[bp].pmUserES
assume ds:NOTHING
.386p
rep movs word ptr [esi],word ptr [edi]
.286p
; Copy stack parameters from PM to RM stack if requested by caller. To
; avoid excessive selector munging, we do this in two steps (under the
; assumption the # words to copy will be small). First the PM stack args
; are copied to a buffer in DXDATA, then after switching to real mode,
; to the real mode stack. If the caller has more stack words than will
; fit in our buffer, or the real mode stack, bad things will happen...
.386p
xor ecx,ecx
.286p
mov cx,[bp].intUserCX ;caller's CX has # stack words to copy
jcxz @f
Trace_Out "Int 31h PM-to-RM int/call copying #CX stack words"
.386p
xor esi,esi
.286p
mov ds,[bp].pmUserSS
mov si,[bp].pmUserSP
.386p
test DpmiFlags,DPMI_32BIT
jz rmc30
;
; Have to go groping for user stack, since we switched stacks to get
; here.
;
; | |
; +----------------+
; | |
; +---- SS -----+
; | |
; +----------------+
; | |
; +---- ESP -----+
; | |
; +----------------+
; | Flags |
; +----------------+
; | CS |
; +----------------+
; | IP |
; ds:si -> +----------------+
push dword ptr ds:[si + 6]
push word ptr ds:[si + 10]
pop ds
pop esi
add esi,6 ; other half of 32 bit stack frame
rmc30: add esi,6 ;ds:si -> PM stack args
rep movs word ptr [esi],word ptr [edi]
.286p
;es:di already points to buffer
@@:
push es ;restore ds -> DGROUP
pop ds
assume ds:DGROUP
; Switch to real mode, set up the real mode stack
SwitchToRealMode
assume ds:DGROUP,es:DGROUP
i31_rmcall_hw_ok:
FSTI ;RestoreHardwareIntr disables ints
;don't need them disabled now
mov [bx].RealMode_SaveBP,bp ;save our stack in reserved area of
mov [bx].RealMode_SaveSP,sp ; real mode register frame
mov cx,[bp].pmUserCX ;cx = caller's CX (# stk words)
mov dh,byte ptr [bp].pmUserAX ;dh = caller's AL (subfunction)
mov dl,byte ptr [bp].pmUserBX ;dl = caller's BL (RM int #)
mov ax,[bx].RealMode_SS ;did caller specify his own stack?
or ax,[bx].RealMode_SP
jz @f
; NOTE: can't reference [bp].xxUserXX varaibles after switching stacks
mov ss,[bx].RealMode_SS ;switch to caller's real mode stack
mov sp,[bx].RealMode_SP
assume ss:NOTHING
@@:
; Copy stack args to real mode stack if there are any
jcxz @f
sub sp,cx
sub sp,cx ;make space on stack for args
mov di,sp
mov ax,ss
mov es,ax ;es:di -> real mode stack
assume es:NOTHING
lea si,[bx + size Real_Mode_Call_Struc]
cld
rep movsw
push ds
pop es
assume es:DGROUP
@@:
; Put a far ret or iret frame on stack to return to us
cmp dh,Trans_Far_Call ;Does this service use a far ret or
jz i31_rmcall_retf ; an iret frame?
mov ax, [bx].RealMode_Flags ;real mode routine thinks these were
and ax, NOT 100h ;remove TF
push ax
push cs ; the prior flags and CS:IP
push offset i31_rmcall_ret
FCLI ;flags with interrupts disabled -- real
pushf ; mode rtn entered with these flags
FSTI
jmp short @f
i31_rmcall_retf:
push cs ;push a far ret frame so the
push offset i31_rmcall_ret ; real mode routine returns to us
mov ax, [bx].RealMode_Flags ;real mode rtn entered with these flags
and ax, NOT 100h ;remove TF
push ax
@@:
cmp dh,Trans_Sim_Int ;use an int vector, or caller's spec'd
jnz i31_rmcall_csip ; cs:ip?
mov al,dl ;push CS:IP for interrupt
xor ah,ah ; number in caller's BL
mov si,ax
shl si,2
xor ax,ax ;address real mode IDT
mov es,ax
assume es:NOTHING
push word ptr es:[si+2]
push word ptr es:[si]
jmp short @f
i31_rmcall_csip:
push [bx].RealMode_CS ;execute the real mode routine at
push [bx].RealMode_IP ; specified CS:IP
@@:
; Load the clients registers, and pass control to the real mode routine
.386p
mov edi,dword ptr [bx].RealMode_DI
mov esi,dword ptr [bx].RealMode_SI
mov ebp,dword ptr [bx].RealMode_BP
mov edx,dword ptr [bx].RealMode_DX
mov ecx,dword ptr [bx].RealMode_CX
mov eax,dword ptr [bx].RealMode_AX
mov es,[bx].RealMode_ES
assume es:NOTHING
push [bx].RealMode_DS
push dword ptr [bx].RealMode_BX
pop ebx
pop ds
assume ds:NOTHING
.286p
iret
; The real mode routine returns here when finished
i31_rmcall_ret:
pushf ;save returned flags, ds, bx on stack
FSTI ;don't need ints disabled
push ds
.386p
push ebx
.286p
mov ds,segDXData ;address our DGROUP
assume ds:DGROUP
;
; Fetch word pointer to temporary client register save area, that we
; saved before switching stacks.
;
mov bx,[pbReflStack]
mov bx,[bx + CB_STKFRAME - (SIZE INTRSTACK) - 2]
; Save the real mode registers in client frame
.386p
mov dword ptr [bx].RealMode_DI,edi
mov dword ptr [bx].RealMode_SI,esi
mov dword ptr [bx].RealMode_BP,ebp
mov dword ptr [bx].RealMode_DX,edx
mov dword ptr [bx].RealMode_CX,ecx
mov dword ptr [bx].RealMode_AX,eax
mov [bx].RealMode_ES,es
pop dword ptr [bx].RealMode_BX
.286p
pop [bx].RealMode_DS
pop [bx].RealMode_Flags
or [bx].RealMode_Flags,03000h ; IOPL always has to be 3
; Restore our stack and return to protected mode
; SP will now point to base of temporary client register save area
; on our stack. BP points to stack frame set up for us by EnterIntHandler.
; SP must be restored to value in BP before calling LeaveIntHandler.
mov ss,segDXData ;address our DGROUP
mov sp,[bx].RealMode_SaveSP
mov bp,[bx].RealMode_SaveBP
SwitchToProtectedMode
assume ds:DGROUP,es:DGROUP
FSTI ;still don't need ints disabled
; Apparently in win31 standard mode, the pm caller's flags are set to the
; value of the rm flags at the handler's iret. On win31 enhanced mode, this
; was *not* done, and the pm flags are, except for carry, basically
; preserved. Since setting the flags kills some apps (winfax deltest),
; we should adhere to the enhanced mode convention. Thus, the following
; code is if'd out.
if 0
mov ax,[bx].RealMode_Flags
mov [bp].intUserFL,ax
endif
; Copy the updated client register frame to the caller, and we're finished
cld
.386p
xor ecx,ecx
.286p
mov cx,(size Real_Mode_Call_Struc) / 2
.386p
xor esi,esi
xor edi,edi
mov si,bx
add si,[bp].pmUserCX
add si,[bp].pmUserCX
add si,size Real_Mode_Call_Struc
;
; Si now points at pushad frame
;
push si
test DpmiFlags,DPMI_32BIT
jz rmc80
mov edi,[si]
rmc80:
.286p
mov si,bx
mov di,[bp].pmUserDI
mov es,[bp].pmUserES
assume es:NOTHING
.386p
rep movs word ptr [esi],word ptr [edi]
pop sp ; value calculated above
popad
mov sp,bp
.286p
jmp i31_done ;finished!
i31_RMCall endp
; -------------------------------------------------------
; Service 03/03 -- Allocate Real Mode Call-Back Address
;
; In: ds:si -> pMode CS:IP to be called when rMode
; call-back address executed
; es:di -> client register structure to be updated
; when call-back address executed
; Out: cx:dx -> SEGMENT:offset of real mode call-back hook
; CY clear if successful, CY set if can't allocate
; call back
assume ds:DGROUP,es:DGROUP
public i31_AllocCallBack
i31_AllocCallBack proc near
push ax
call AllocateLDTSelector
jc acb_nosel
push [bp].pmUserDS ;pass in user's ds on stack
push [bp].pmUserES ;pass in user's es on stack
DPMIBOP AllocateRMCallBack
add sp, 4
jc acb_nocb
mov [bp].intUserCX,cx ;callback segment
mov [bp].intUserDX,dx ;callback offset
mov cx,-1
cCall NSetSegmentDscr,<ax,0,0,0,cx,STD_DATA>
pop ax
jmp i31_done
acb_nocb:
call FreeSelector
acb_nosel:
pop ax
jmp i31_fail_CY ;no call-backs available, fail
i31_AllocCallBack endp
; -------------------------------------------------------
; Service 03/04 -- Free Real Mode Call-Back Address
;
; In: cx:dx -> SEGMENT:offset of rMode call-back to free
; Out: CY clear if successful, CY set if failure
assume ds:DGROUP,es:DGROUP
public i31_FreeCallBack
i31_FreeCallBack proc near
push ax
DPMIBOP FreeRMCallBack
jc @f
call FreeSelector
pop ax
jmp i31_done
@@:
pop ax
jmp i31_fail_CY ;no call-backs available, fail
i31_FreeCallBack endp
; -------------------------------------------------------
DXPMCODE ends
; -------------------------------------------------------
DXCODE segment
assume cs:DXCODE
;
; RMCallBackBop
;
; Originally, the real mode call back hook was implemented here in
; the 16-bit client dosx. What it did was allocate a stacklet on
; its own stack and run the PM code on that stack. So the basic
; stack layout was that the RM stack pointer was followed by approx.
; 100 bytes or so of space, and then you started on the PM stack
; (corresponding to the start of the next stacklet).
;
; This whole approach was basically broken, since apps don't expect
; this. For example, the DOS4GW dos extender, which is used by a
; lot of games, switches stacks immediately in the PM callback proc
; from the protect mode stack to the real mode stack. That is, they
; use the same contiguous stack for both real mode and protect mode.
; What happens then with the original design is that the protect mode
; code, depending on how deep it goes on the stack, ends up overwriting
; the new stacklet further down, so we lose the return information
; needed to get back from the PM callback proc.
;
; The new design is just to do it in the unobtrusive dpmi32 host,
; including switching to a real PM stack. So here we just bop out
; to perform the switch and PM call.
;
public RMCallBackBop
RMCallBackBop proc far
DPMIBOP RMCallBackCall
ret ;finished!
RMCallBackBop endp
; -------------------------------------------------------
DXCODE ends
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
subttl INT 31h Memory Management Services
page
; -------------------------------------------------------
; INT 31h MEMORY MANAGEMENT SERVICES
; -------------------------------------------------------
; -------------------------------------------------------
; Service 05/00 -- Get Free Memory Information
;
; In: es:di -> 30h byte buffer
; Out: es:di -> Largest free block (in bytes) placed at
; start of caller's buffer
; Rest of buffer filled in with -1.
assume ds:DGROUP,es:DGROUP
public i31_GetFreeMem
i31_GetFreeMem proc near
mov es,[bp].pmUserES ; Point to user buffer
mov di,[bp].pmUserDI
assume es:NOTHING
FBOP BOP_DPMI, GetMemoryInformation, FastBop
jmp i31_done
i31_GetFreeMem endp
; -------------------------------------------------------
; Service 05/01 -- Allocate Memory Block
;
; In: bx:cx - size of block to allocate in bytes
; Out: successful, carry clear &
; bx:cx = linear memory address of block
; si:di = handle to block
; failed, carry set
assume ds:DGROUP,es:DGROUP
public i31_AllocMem
i31_AllocMem proc near
mov bx,[bp].pmUserBX
mov cx,[bp].pmUserCX
mov dx, selPSPChild
FBOP BOP_DPMI, AllocXmem, FastBop
jnc @f
jmp i31_fail_CY
@@: mov [bp].intUserBX,bx
mov [bp].intUserCX,cx
mov [bp].intUserSI,si
mov [bp].intUserDI,di
jmp i31_Done
i31_AllocMem endp
; -------------------------------------------------------
; Service 05/02 -- Free Memory Block
;
; In: si:di - 'handle' of block to free
; Out: successful, carry clear
; failed, carry set
assume ds:DGROUP,es:DGROUP
public i31_FreeMem
i31_FreeMem proc near
mov si,[bp].pmUserSI
mov di,[bp].pmUserDI
FBOP BOP_DPMI, FreeXmem, FastBop
jnc @f
jmp i31_fail_CY
@@: jmp i31_Done
i31_FreeMem endp
; -------------------------------------------------------
; Service 05/03 -- Resize Memory Block
;
; In: bx:cx - new size of block to allocate in bytes
; si:di - 'handle' of block to free
; Out: successful, carry clear &
; bx:cx = linear memory address of block
; si:di = handle to block
; failed, carry set
assume ds:DGROUP,es:DGROUP
public i31_SizeMem
i31_SizeMem proc near
mov bx,[bp].pmUserBX
mov cx,[bp].pmUserCX
mov si,[bp].pmUserSI
mov di,[bp].pmUserDI
FBOP BOP_DPMI, ReallocXmem, FastBop
jnc @f
jmp i31_fail_CY
@@: mov [bp].intUserBX,bx
mov [bp].intUserCX,cx
mov [bp].intUserSI,si
mov [bp].intUserDI,di
jmp i31_Done
i31_SizeMem endp
; -------------------------------------------------------
; Service 09/00 -- Get and Disable Virtual Interrupt State
; 09/01 -- Get and Enable Virtual Interrupt State
; 09/02 -- Get Virtual Interrupt State
;
; In: none
; Out: AL = previous interrupt state
assume ds:DGROUP,es:DGROUP
public i31_VirtualInt
i31_VirtualInt proc near
mov ah,byte ptr [bp].intUserFL+1 ;get/isolate user's IF in AH
shr ah,1
and ah,1
cmp byte ptr [bp].intUserAX,Get_Int_State ;only getting state?
jz @f ; yes, skip set
mov al,byte ptr [bp].intUserAX ;get desired state
shl al,1 ; move into IF position
and byte ptr [bp].intUserFL+1,not 02h ;clr old IF bit
or byte ptr [bp].intUserFL+1,al ; set desired
@@:
mov byte ptr [bp].intUserAX,ah ;return old state in user's AL
jmp i31_done
i31_VirtualInt endp
; -------------------------------------------------------
subttl INT 31h Utility Routines
page
; -------------------------------------------------------
; INT 31h UTILITY ROUTINES
; -------------------------------------------------------
; -------------------------------------------------------
; i31_Version -- Return Int 31h version information.
;
; In: none
; Out: ah - major version
; al - minor version
; bx - flags
; cl - processor type
; dh - master PIC base interrupt
; dl - slave PIC bas interrupt
;
public i31_Version
assume ds:DGROUP,es:DGROUP
i31_Version proc near
mov [bp].intUserAX,I31VERSION
mov [bp].intUserBX,I31FLAGS
mov al,byte ptr idCpuType
mov byte ptr [bp].intUserCX,al
mov [bp].intUserDX,(I31MasterPIC SHL 8) OR I31SlavePIC
jmp i31_done
i31_Version endp
; -------------------------------------------------------
; i31_MemGetHeader -- Get selector to our header on a Int 31h allocated
; DOS memory block.
;
; In: DX - SELECTOR of block to get header of
; Out: ES - selector pointing to our header for block
; Uses: none
public i31_MemGetHeader
assume ds:DGROUP,es:NOTHING,ss:NOTHING
i31_MemGetHeader proc near
; User wants to release block pointed to by selector in DX. Use a scratch
; selector to point 1 paragraph before that to make sure this is a block
; we allocated, and get some misc info
push ax
push bx
push cx
push dx
mov ax,dx
call GetSegmentAddress ;BX:DX -> user's data area
sub dx,10h ;backup one paragraph
sbb bx,0
mov cx,0fh
mov ax,SEL_SCR0 or STD_TBL_RING
cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
mov es,ax
pop dx
pop cx
pop bx
pop ax
ret
i31_MemGetHeader endp
; -------------------------------------------------------
; RZCall -- Utility routine to call a Ring
; Zero procedure. Stack parameter is the NEAR
; address of the routine in the DXPMCODE segment to
; call. The called routine must be declared FAR
; and take no stack parameters.
;
; USES: Whatever Ring 0 routine uses
; +Flags
; RETURNS: Whatever Ring 0 routine returns
;
; NOTE: Assumes that interrupts must be disabled
; for the Ring 0 routine.
;
; History:
; 12-Feb-1991 -- ERH wrote it!!!
; -------------------------------------------------------
My_Call_Gate dd (SEL_SCR0 or STD_TBL_RING) shl 10h
public RZCall
RZCall proc near
pushf
FCLI
push bp
mov bp,sp
cCall NSetSegmentDscr,<SEL_SCR0,0,SEL_EH,0,[bp+6],STD_CALL>
pop bp
call dword ptr My_Call_Gate
cCall NSetSegmentDscr,<SEL_SCR0,0,0,0,-1,STD_DATA>
npopf
retn 2
RZCall endp
ife NO386
i31_Debug_Register_Access proc near
or byte ptr [bp].intUserFL,1 ;set carry flag
cmp idCpuType,3 ;at least 386?
jb i31_Debug_Register_Access_Done ;No.
IFNDEF WOW_x86
push offset DXPMCODE:I31_Win386_Call
call RZCall
ELSE
call far ptr I31_Win386_Call
ENDIF
i31_Debug_Register_Access_Done:
jmp i31_exit
i31_Debug_Register_Access endp
.386
;******************************************************************************
;
; I31_Win386_Call
;
; The routine to implement the INT 31h debug register functions is copied
; directly from the WIN386 routine in vmm/int31.asm. This routine sets
; up the 386 registers so that the actual API is callable from the 16-bit
; DOS Extender.
;
;******************************************************************************
I31_Win386_Call proc far ; Called via ring-transition call gate
;
; Save 386 extended registers that are used by the WIN386 code.
;
pushad
;
; Call the actual routine
;
movzx ebp,bp
call I31_Debug_Register_Support
;
; Restore 386 extended registers that are used by the WIN386 code.
;
popad
ret
I31_Win386_Call endp
DXPMCODE ends
;******************************************************************************
; R E C O R D S
;******************************************************************************
;
; The following record defines the debug status register.
;
DBG6 RECORD d6_r1:16,d6_bt:1,d6_bs:1,d6_bd:1,d6_r2:9,d6_b3:1,d6_b2:1,d6_b1:1,d6_b0:1
DBG6_RESERVED EQU (MASK d6_r1) OR (MASK D6_r2)
;
; The following record defines the debug control register.
;
DBG7 RECORD d7_ln3:2,d7_rw3:2,d7_ln2:2,d7_rw2:2,d7_ln1:2,d7_rw1:2,d7_ln0:2,d7_rw0:2,d7_rs:6,d7_ge:1,d7_le:1,d7_g3:1,d7_l3:1,d7_g2:1,d7_l2:1,d7_g1:1,d7_l1:1,d7_g0:1,d7_l0:1
DBG7_RESERVED EQU (MASK d7_rs)
Num_Watchpoints EQU 4 ; Only 4 live watchpoints in 386, 486.
;******************************************************************************
; m a c r o s
;******************************************************************************
;******************************************************************************
;
; The following set of macros allows us to copy WIN386 code directly
;
;******************************************************************************
BeginProc macro fname
fname proc near
endm
EndProc macro fname
fname endp
endm
Assert_Client_Ptr macro dummy
endm
Client_Flags equ intUserFL
Client_EAX equ intUserAX
Client_AX equ intUserAX
Client_BX equ intUserBX
Client_CX equ intUserCX
Client_DX equ intUserDX
CF_MASK equ 1
OFFSET32 equ <OFFSET>
;******************************************************************************
; d a t a
;******************************************************************************
DXDATA segment
PUBLIC Debug_Regs
;
; A memory image of the debug registers. The first version assumes
; that we have complete control over the debug registers, so only
; one copy is needed.
;
ALIGN DWORD
Debug_Regs LABEL DWORD
DD_DR0 dd 0
DD_DR1 dd 0
DD_DR2 dd 0
DD_DR3 dd 0
DD_DR6 dd 0
DD_DR7 dd 0
DXDATA ends
.386p
DXPMCODE segment
;******************************************************************************
;
; I31_Debug_Register_Support
;
; ENTRY: AL = Function code
; 01 -- Set debug watchpoint
; BX:CX = linear address of watchpoint
; DL = size of watchpoint (1, 2, or 4)
; DH = type of watchpoint
; 0 = Execute
; 1 = Write
; 2 = Read/Write
;
; Returns
; BX = debug watchpoint handle
;
; 02 -- Clear debug watchpoint
;
; 03 -- Get state of debug watchpoint
; Returns
; AX = bit 0 set if watch point has been executed
;
; 04 -- Reset debug watchpoint
;
; EXIT: Carry clear in client flags if successful
; Carry set in client flags if not
;
; USES: EDI, EAX, ECX, EDX, ESI
;
; History:
; 08-Feb-1991 -- ERH wrote it.
;
;******************************************************************************
BeginProc I31_Debug_Register_Support
IFNDEF WOW_x86
call Store_DBG_Regs ; make copy of debug regs.
ENDIF
test [DD_DR6],MASK d6_bd
jnz DD_I31_Error ; ICE-386 is active!
and [ebp.Client_Flags],NOT CF_MASK
mov eax, dword ptr [ebp.Client_EAX]
cmp al, 03h
ja DD_I31_Error
je DD_I31_Reset_Watchpoint
cmp al, 01h
ja DD_I31_Get_Status
je DD_I31_Clear_Watchpoint
;
; Function AX = 0A00h -- Set Debug Watchpoint
;
PUBLIC DD_I31_Set_Watchpoint
DD_I31_Set_Watchpoint:
xor ecx, ecx
mov edx,(MASK d7_l0) OR (MASK d7_g0); EDX = DR0 enable bits
DD_I31_Search_Loop:
;
; Look for an unused breakpoint. In order for a breakpoint to be
; unused, the corresponding global and local enable bits must be clear.
;
test [DD_DR7],edx
jz SHORT DD_I31_SW_Found_One
DD_I31_SkipIt:
shl edx,2 ; EDX = next BP's enable bits
inc ecx
cmp ecx, Num_Watchpoints
jb DD_I31_Search_Loop
jmp DD_I31_Error
DD_I31_SW_Found_One:
mov esi, OFFSET32 Debug_Regs
mov eax,ecx
shl eax,2
add esi,eax ; ESI -> BP address buffer
mov ax, [ebp.Client_BX]
shl eax, 16
mov ax, [ebp.Client_CX] ; EAX = linear address
mov dword ptr [esi],eax ; record address
or [DD_DR7],edx ; enable the break point
mov edx,NOT((MASK d7_ln0) OR (MASK d7_rw0))
shl cl,2
rol edx,cl
shr cl,2
and [DD_DR7],edx ; clear type and size bits
;
; Client_DX =
; DH = 0 : execute
; DH = 1 : write
; DH = 2 : read/write
;
; DL = 1 : byte
; DL = 2 : word
; DL = 4 : dword
;
movzx edx,[ebp.Client_DX] ; load BP size and type
cmp dh,0 ; execute?
jne SHORT @F ; no
mov dl,1 ; yes, force size zero
@@:
cmp dl,4 ; check for valid values
ja DD_I31_Error ; size in dl is 1, 2, or 4
cmp dl,3
je DD_I31_Error
dec dl ; DL = 0, 1, or 3 for DR7
js DD_I31_Error ; len field
cmp dh,2 ; type in dh is 0, 1, or 2
ja DD_I31_Error
jne SHORT @F
inc dh ; 386 wants 3, not 2
@@: ; DH = RWn field
shl dl,2
or dl,dh
xor dh,dh
shl edx,d7_rw0
shl cl,2
shl edx,cl
or [DD_DR7],edx ; set size, type
shr cl,2
mov edx,NOT (MASK d6_b0) ; clear triggered bit
rol edx,cl ; EDX = mask to clear hit
and [DD_DR6],edx ; clear triggered bit
mov [ebp.Client_BX],cx ; return address register
; number as BP handle
or [DD_DR7],(MASK d7_ge) OR (MASK d7_le)
; enable debugging
call Load_DBG_Regs ; load changes into debug registers
ret
;
; Function AX = 0A01h -- Clear Debug Watchpoint
;
; Error if Watchpoint not previously set. In that case, do nothing.
; If Watchpoint was set, then clear enable bits, triggered bit, and
; breakpoint address.
;
PUBLIC DD_I31_Clear_Watchpoint
DD_I31_Clear_Watchpoint:
movzx ecx,[ebp.Client_BX]
cmp ecx, Num_Watchpoints
jnb DD_I31_Error
mov edx,(MASK d7_l0) OR (MASK d7_g0); EDX = enable DR0 mask
shl edx,cl
shl edx,cl ; EDX = enable DRn mask
test [DD_DR7],edx ; BP set?
jz DD_I31_Error ; No, error.
not edx
and [DD_DR7],edx ; disable the BP
mov edx,NOT (MASK d6_b0) ; EDX = DR0 not hit
rol edx,cl ; EDX = DRn not hit
and [DD_DR6],edx ; clear triggered bit
mov esi, OFFSET32 Debug_Regs ; ESI -> DRn table
shl ecx,2 ; ECX = DWORD offset
add esi,ecx ; ESI -> DRn
mov dword ptr [esi],0 ; clear address
mov edx,NOT((MASK d7_ln0) OR (MASK d7_rw0))
rol edx,cl
and [DD_DR7],edx
;
; Test whether this leaves any breakpoints active. If not, disable
; the exact match condition. Note: the following is a long line.
;
test [DD_DR7],(MASK d7_g3) OR (MASK d7_l3) OR (MASK d7_g2) OR (MASK d7_l2) OR (MASK d7_g1) OR (MASK d7_l1) OR (MASK d7_g0) OR (MASK d7_l0)
jne SHORT @F
and [DD_DR7],NOT ((MASK d7_ge) OR (MASK d7_le))
@@:
call Load_DBG_Regs ; load changes into debug registers
ret
;
; Function AX = 0A02h -- Get Status of Debug Watchpoint
;
PUBLIC DD_I31_Get_Status
DD_I31_Get_Status:
movzx ecx,[ebp.Client_BX]
cmp ecx, Num_Watchpoints
jnb SHORT DD_I31_Error
mov edx,(MASK d7_g0) OR (MASK d7_l0); EDX = DR0 enable bits
shl edx,cl
shl edx,cl ; EDX = DRn enable bits
test [DD_DR7],edx ; DRn enabled?
jz SHORT DD_I31_Error ; No, error.
mov edx,MASK d6_b0 ; EDX = DR0 hit mask
shl edx,cl ; EDX = DRn hit mask
xor eax,eax
test [DD_DR6],edx ; DRn hit?
jne SHORT @F ; no
inc al ; yes, store result
@@:
mov [ebp.Client_AX],ax
ret
;
; Function AX = 0A03h -- Reset Debug Watchpoint
;
PUBLIC DD_I31_Reset_Watchpoint
DD_I31_Reset_Watchpoint:
movzx ecx,[ebp.Client_BX]
cmp ecx, Num_Watchpoints
jnb SHORT DD_I31_Error
mov edx,(MASK d7_g0) OR (MASK d7_l0); EDX = DR0 enable bits
shl edx,cl
shl edx,cl ; EDX = DRn enable bits
test [DD_DR7],edx ; DRn enabled?
jz SHORT DD_I31_Error ; No, error.
mov edx,NOT (MASK d6_b0) ; EDX = DR0 hit mask
rol edx,cl ; EDX = DRn hit mask
and [DD_DR6],edx ; clear triggered bit
call Load_DBG_Regs ; load changes into debug registers
ret
DD_I31_Error:
Assert_Client_Ptr ebp
or [ebp.Client_Flags], CF_Mask
ret
EndProc I31_Debug_Register_Support
;******************************************************************************
;
; Load_DBG_Regs - Load debug registers from memory.
; Do not change undefined bits.
;
; ENTRY: NONE
; EXIT: Memory image copied to debug registers, undefined bits unchanged.
; USES: eax, ecx, edi, esi
;
;******************************************************************************
BeginProc Load_DBG_Regs
mov esi,OFFSET32 Debug_Regs
DPMIBOP SetDebugRegisters
jnc short ldr_exit
cld
xor eax, eax
mov ecx, 6
mov edi, OFFSET32 Debug_Regs
rep stosd ;clear local copy
ldr_exit:
ret
EndProc Load_DBG_Regs
; -------------------------------------------------------
IFNDEF WOW_x86
;******************************************************************************
;
; Load_DBG_Regs - Load debug registers from memory.
; Do not change undefined bits.
;
; ENTRY: NONE
; EXIT: Memory image copied to debug registers, undefined bits unchanged.
; USES: NONE
;
;******************************************************************************
BeginProc Load_DBG_Regs
push esi
push edx
push eax
cld
mov esi,OFFSET32 Debug_Regs
lods dword ptr ds:[esi]
mov dr0, eax
lods dword ptr ds:[esi]
mov dr1, eax
lods dword ptr ds:[esi]
mov dr2, eax
lods dword ptr ds:[esi]
mov dr3, eax
lods dword ptr ds:[esi]
and eax,NOT DBG6_RESERVED
mov edx,dr6
and edx,DBG6_RESERVED
or eax,edx
mov dr6, eax
.ERRNZ dd_dr6 - dd_dr3 - 4
lods dword ptr ds:[esi]
and eax,NOT DBG7_RESERVED
mov edx,dr7
and edx,DBG7_RESERVED
or eax,edx
mov dr7, eax
pop eax
pop edx
pop esi
ret
EndProc Load_DBG_Regs
;******************************************************************************
;
; Store_DBG_Regs - Copy debug registers to memory.
;
; ENTRY: NONE
; EXIT: Debug registers copied to memory image.
; Undefined bits = don't care.
; USES: NONE
;
;******************************************************************************
BeginProc Store_DBG_Regs
push eax
push edi
cld
mov edi,OFFSET32 Debug_Regs
mov eax, dr0
stos dword ptr es:[edi]
mov eax, dr1
stos dword ptr es:[edi]
mov eax, dr2
stos dword ptr es:[edi]
mov eax, dr3
stos dword ptr es:[edi]
mov eax, dr6
.ERRNZ dd_dr6 - dd_dr3 - 4
stos dword ptr es:[edi]
mov eax, dr7
stos dword ptr es:[edi]
pop edi
pop eax
ret
EndProc Store_DBG_Regs
ENDIF ; WOW_x86
; -------------------------------------------------------
endif ; NO386
; -------------------------------------------------------
subttl INT 31h Raw Modeswitch Routines
page
; -------------------------------------------------------
; INT 31h Raw Modeswitch Routines
; -------------------------------------------------------
; -------------------------------------------------------
; i31_GetSaveRestoreState -- Return Int 31h Save/Restore State addresses.
;
; In: none
; Out: ax - size of buffer required to save state
; bx:cx - real mode address used to save/restore state
; si:di - protected mode address used to save/restore state
;
public i31_GetStateSaveRestore
assume ds:DGROUP,es:DGROUP
i31_GetStateSaveRestore proc near
mov [bp].intUserAX,0
push es
push SEL_DXCODE OR STD_RING
pop es
assume es:DXCODE
mov ax,segDXCode
pop es
assume es:DGROUP
mov [bp].intUserBX,ax
mov [bp].intUserCX,offset DXCODE:RmSaveRestoreState
mov [bp].intUserSI,SEL_DXPMCODE OR STD_RING
test DpmiFlags,DPMI_32BIT
jz gssr10
mov edi,dword ptr (offset DXPMCODE:PmSaveRestoreState)
gssr10: mov [bp].intUserDI,offset DXPMCODE:PmSaveRestoreState
jmp i31_done
i31_GetStateSaveRestore endp
; -------------------------------------------------------
; i31_GetRawModeSwitch -- Return Int 31h Save/Restore State addresses.
;
; In: none
; Out: bx:cx - real -> protected mode switch address
; si:di - protected -> real mode switch address
;
public i31_GetRawModeSwitch
assume ds:DGROUP,es:DGROUP
i31_GetRawModeSwitch proc near
push es
push SEL_DXCODE OR STD_RING
pop es
assume es:DXCODE
mov ax,segDXCode
pop es
assume es:DGROUP
mov [bp].intUserBX,ax
mov [bp].intUserCX,offset DXCODE:RmRawModeSwitch
mov [bp].intUserSI,SEL_DXPMCODE OR STD_RING
test DpmiFlags,DPMI_32BIT
jz grms10
mov edi,dword ptr (offset DXPMCODE:PmRawModeSwitch)
grms10: mov [bp].intUserDI,offset DXPMCODE:PmRawModeSwitch
jmp i31_done
i31_GetRawModeSwitch endp
;
; make sure 286 protect mode else code generated for mips will be wrong.
;
.286p
; -------------------------------------------------------
; Service 04/f1 - Allocate Space in LDT for Selector (WOW only)
; Don't initialize the descriptor to Zero.
; in: cx - # selectors required
; out: ax - *index* of first selector
assume ds:DGROUP,es:DGROUP
public i31_WOW_AllocSel
i31_WOW_AllocSel proc near
cmp cx,1 ;1 selector or more?
ja @f
call AllocateSelector ;allocate 1 selector
jc i31_WOW_15
jmp short i31_WOW_10
@@: mov ax,cx ;WOW, cx != 0, allocate from higher range
call AllocateSelectorBlock ;allocate a block of selectors
jc i31_WOW_15
i31_WOW_10:
or al,STD_TBL_RING ;add standard table/ring bits
mov [bp].intUserAX,ax ;return 1st/only selector in AX
jmp i31_done
i31_WOW_15:
jmp i31_fail ;fail the request
i31_WOW_AllocSel endp
; -------------------------------------------------------
; Service 04/f2 - Set Descriptor (WOW only)
; in: bx - selector
; cx - number of contiguous selectors
assume ds:DGROUP,es:DGROUP
public i31_WOW_SetDescriptor
i31_WOW_SetDescriptor proc near
mov ax,selGDT
mov es,ax
assume es:NOTHING
lsl ax,ax
and bx,SELECTOR_INDEX ; input selector
cmp bx,ax ; range-test selector against
jc @F ; the limit of the GDT/LDT
jmp i31_fail_CY
@@:
cCall NWOWSetDescriptor,<cx,es,bx>
jmp i31_done
i31_WOW_SetDescriptor endp
; -------------------------------------------------------
; Service 04/f3 - Set Descriptor (WOW only)
; in: bx:dx -- pointer to low memory allocation routine
; si:di -- pointer to low memory free routine
assume ds:DGROUP,es:DGROUP
public i31_WOW_SetAllocFunctions
i31_WOW_SetAllocFunctions proc near
mov word ptr [LowMemAllocFn],dx
mov word ptr [LowMemAllocFn + 2],bx
mov word ptr [LowMemFreeFn],di
mov word ptr [LowMemFreeFn + 2], si
jmp i31_done
i31_WOW_SetAllocFunctions endp
DXPMCODE ends
;****************************************************************
end