1561 lines
45 KiB
NASM
1561 lines
45 KiB
NASM
PAGE ,132
|
|
TITLE DXUTIL.ASM -- Dos Extender Miscellaneous Routines
|
|
|
|
; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
|
|
|
|
;****************************************************************
|
|
;* *
|
|
;* DXUTIL.ASM - Dos Extender Miscellaneous *
|
|
;* *
|
|
;****************************************************************
|
|
;* *
|
|
;* Module Description: *
|
|
;* *
|
|
;* This module contains miscellaneous routines for the Dos *
|
|
;* Extender. *
|
|
;* *
|
|
;****************************************************************
|
|
;* Revision History: *
|
|
;* *
|
|
;* 08/08/90 earleh DOSX and client privilege ring determined *
|
|
;* by equate in pmdefs.inc *
|
|
;* 04/09/90 jimmat If 286 with 287, put 287 into pMode too. *
|
|
;* 08/20/89 jimmat Removed local A20 code since HIMEM 2.07 *
|
|
;* works properly across processor resets *
|
|
;* 07/28/89 jimmat Added A20 check/set routines, added *
|
|
;* SelOff2SegOff & Lma2SegOff routines. *
|
|
;* 06/19/89 jimmat Set direction flag before REP MOVS *
|
|
;* 05/25/89 jimmat Added GetSegmentAccess routine *
|
|
;* 03/30/89 jimmat Set IOPL = 3 when entering protect mode *
|
|
;* 03/16/89 jimmat Added more debug sanity checks *
|
|
;* 03/15/89 jimmat Minor changes to run child in ring 1 *
|
|
;* 03/13/89 jimmat Added support for LDT & TSS *
|
|
;* 02/10/89 (GeneA): changed Dos Extender from small model to *
|
|
;* medium model. Also added MoveMemBlock function. *
|
|
;* 01/25/89 (GeneA): changed initialization of real mode code *
|
|
;* segment address in EnterRealMode. caused by adding *
|
|
;* new method of relocationg dos extender for PM operation *
|
|
;* 12/13/88 (GeneA): moved EnterProtectedMode and EnterReal- *
|
|
;* Mode here from dxinit.asm *
|
|
;* 09/16/88 (GeneA): created by extracting code from the *
|
|
;* SST debugger modules DOSXTND.ASM, VIRTMD.ASM, *
|
|
;* VRTUTIL.ASM, and INTERRPT.ASM *
|
|
;* 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI *
|
|
;* 24-Jan-1992 v-simonf Added WOW callout when INT 8 hooked *
|
|
;* *
|
|
;****************************************************************
|
|
|
|
.286p
|
|
.287
|
|
|
|
; -------------------------------------------------------
|
|
; INCLUDE FILE DEFINITIONS
|
|
; -------------------------------------------------------
|
|
|
|
; .sall
|
|
; .xlist
|
|
include segdefs.inc
|
|
include gendefs.inc
|
|
include pmdefs.inc
|
|
include dpmi.inc
|
|
include intmac.inc
|
|
.list
|
|
|
|
; -------------------------------------------------------
|
|
; GENERAL SYMBOL DEFINITIONS
|
|
; -------------------------------------------------------
|
|
|
|
SHUT_DOWN = 8Fh ;address in CMOS ram of the shutdown code
|
|
CMOS_ADDR = 70h ;i/o address of the cmos ram address register
|
|
CMOS_DATA = 71h ;i/o address of the cmos ram data register
|
|
|
|
DMAServiceSegment equ 040h ;40:7B bit 5 indicates DMA services
|
|
DMAServiceByte equ 07Bh ; are currently required
|
|
DMAServiceBit equ 020h
|
|
|
|
; -------------------------------------------------------
|
|
; EXTERNAL SYMBOL DEFINITIONS
|
|
; -------------------------------------------------------
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; DATA SEGMENT DEFINITIONS
|
|
; -------------------------------------------------------
|
|
|
|
DXDATA segment
|
|
|
|
extrn segGDT:WORD
|
|
extrn segIDT:WORD
|
|
extrn selGDT:WORD
|
|
extrn selIDT:WORD
|
|
extrn selGDTFree:WORD
|
|
extrn bpGDT:FWORD
|
|
extrn bpIDT:FWORD
|
|
extrn bpRmIVT:FWORD
|
|
extrn rgbXfrBuf1:BYTE
|
|
extrn PMFaultVector:DWORD
|
|
extrn lpfnXMSFunc:DWORD
|
|
|
|
|
|
extrn pbReflStack:WORD
|
|
|
|
bIntMask db 0
|
|
|
|
bpBogusIDT df 0 ;This is loaded into the IDT register to
|
|
; force a bogus IDT to be defined. When we
|
|
; then do an interrupt a triple fault will
|
|
; occur forcing the processor to reset. This
|
|
; is when doing a mode switch to real mode.
|
|
|
|
IDTSaveArea dw 3 DUP (?) ;save area for IDT during mode switch
|
|
|
|
public A20EnableCount
|
|
|
|
A20EnableCount dw 0
|
|
|
|
ShutDownSP dw 0 ;stack pointer during 286 reset
|
|
|
|
public f286_287
|
|
|
|
f286_287 db 0 ;NZ if this is a 286 with 287 coprocessor
|
|
|
|
|
|
if DEBUG ;------------------------------------------------------------
|
|
|
|
extrn fTraceA20:WORD
|
|
extrn fTraceMode:WORD
|
|
|
|
public fA20
|
|
|
|
fA20 db 0
|
|
|
|
endif ;DEBUG --------------------------------------------------------
|
|
|
|
selPmodeFS dw 0
|
|
selPmodeGS dw 0
|
|
|
|
public HighestSel
|
|
HighestSel dw 0
|
|
|
|
ifndef WOW_x86
|
|
public IretBopTable
|
|
IretBopTable label byte
|
|
irp x,<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>
|
|
db 0c4h, 0c4h, 05dh, x
|
|
endm
|
|
else
|
|
public FastBop
|
|
FastBop df 0
|
|
|
|
IretBopTable label byte
|
|
irp x,<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>
|
|
db 02eh, 066h, 0FFh, 01eh, 00h, 00h, 05dh, x
|
|
endm
|
|
|
|
NullSel dd 0
|
|
dd 0
|
|
endif
|
|
extrn DpmiFlags:WORD
|
|
DXDATA ends
|
|
|
|
; -------------------------------------------------------
|
|
; CODE SEGMENT VARIABLES
|
|
; -------------------------------------------------------
|
|
|
|
DXCODE segment
|
|
|
|
extrn segDXData:WORD
|
|
extrn segDXCode:WORD
|
|
extrn selDgroup:WORD
|
|
|
|
DXCODE ends
|
|
|
|
|
|
DXPMCODE segment
|
|
|
|
extrn selDgroupPM:WORD
|
|
|
|
DXPMCODE ends
|
|
|
|
; -------------------------------------------------------
|
|
subttl Real/Protected Mode Switch Routines
|
|
page
|
|
|
|
; -------------------------------------------------------
|
|
|
|
DXCODE segment
|
|
assume cs:DXCODE
|
|
|
|
; -------------------------------------------------------
|
|
; REAL/PROTECTED MODE SWITCH ROUTINES
|
|
; -------------------------------------------------------
|
|
;
|
|
; EnterProtectedMode -- This routine will switch the processor
|
|
; into protected mode. It will return with the processor
|
|
; in protected mode and all of the segment registers loaded
|
|
; with the selectors for the protected mode segments.
|
|
; (CS with the selector for DXCODE and DS,ES,SS with the
|
|
; selector for DXDATA)
|
|
; It will also switch mode dependent memory variables.
|
|
; It assumes that InitGlobalDscrTable and InitIntrDscrTable
|
|
; have been called to set up the descriptor tables appropriately.
|
|
;
|
|
; Note: Except for a very brief time in this routine and in
|
|
; EnterRealMode, the DOS Extender runs in the same ring along
|
|
; with it's child app. This has the benefit of eliminating
|
|
; ring transitions on hardware and software interrupts.
|
|
; It also makes it possible for the child to hook their
|
|
; own interrupt routine into the IDT.
|
|
;
|
|
; Input: none
|
|
; Output: none
|
|
; Errors: none
|
|
; Uses: AX, DS, ES, SS, CS modified, all others preserved
|
|
;
|
|
; NOTE: This routine turns interrupts of and does not turn them
|
|
; back on.
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public EnterProtectedMode
|
|
|
|
EnterProtectedMode proc near
|
|
|
|
FCLI
|
|
|
|
; Update the mode dependent variables.
|
|
|
|
mov ax,SEL_DXDATA or STD_RING
|
|
mov selDgroup,ax
|
|
|
|
; Set the DMA services required bit for pMode users.
|
|
|
|
mov ax,DMAServiceSegment
|
|
mov es,ax
|
|
or byte ptr es:[DMAServiceByte],DMAServiceBit
|
|
|
|
|
|
; 'local enable' the A20 line via HIMEM before switching to pMode.
|
|
; This is more complicated than you might think. Some real mode code
|
|
; (like old versions of SMARTDRV.SYS) may diddle with A20 on their own.
|
|
; These programs may not want us to change A20 on them. RMIntrReflector
|
|
; may do a XMS 'local enable' to turn A20 back on for one of these pgms.
|
|
; Also, on a 386 where we actually do the mode switch, we try to leave
|
|
; A20 enabled so as to not waste time diddling for nothing. The
|
|
; A20EnabledCount variable tracks if we've 'local enabled' A20 or not.
|
|
; Since we can't really trust real mode to leave A20 alone, we double
|
|
; check that it's really really on when we think it should be.
|
|
|
|
push bx ;save bx around XMS calls
|
|
|
|
cmp A20EnableCount,0 ;should A20 already be enabled?
|
|
jz enpm10 ; no, (normal 4 286) just go enable it
|
|
|
|
xmssvc 7 ; yes, is it really enabled?
|
|
or ax,ax
|
|
jnz enpm15 ; yes, we be done!
|
|
|
|
if DEBUG ;------------------------------------------------------------
|
|
or fA20,1 ; somebody done us wrong
|
|
endif ;---------------------------------------------------------------
|
|
|
|
xmssvc 6 ;keep enable/disable calls balanced
|
|
dec A20EnableCount
|
|
enpm10:
|
|
xmssvc 5 ;local enable A20
|
|
inc A20EnableCount
|
|
|
|
if DEBUG ;------------------------------------------------------------
|
|
|
|
or ax,ax
|
|
jnz @f
|
|
or fA20,2 ;enable failed!
|
|
@@:
|
|
cmp fTraceA20,0
|
|
jz @f
|
|
xmssvc 7 ;in debug mode, make double sure
|
|
or ax,ax ; A20 was enabled. Slows things
|
|
jnz @f ; down, but it's better to know.
|
|
or fA20,2
|
|
@@:
|
|
endif ;DEBUG --------------------------------------------------------
|
|
|
|
enpm15: pop bx
|
|
|
|
ifndef WOW_x86
|
|
DPMIBOP SetAltRegs
|
|
; Make sure that the nested task flag is clear
|
|
|
|
pushf
|
|
pop ax
|
|
and ax,NOT 4000h
|
|
push ax
|
|
npopf
|
|
|
|
; Make sure that we have the appropriate descriptor tables in effect,
|
|
; and switch the machine into protected mode
|
|
|
|
enpr20: smsw ax ;get current machine state
|
|
or ax,1 ;set the protected mode bit
|
|
lgdt bpGDT
|
|
lidt bpIDT
|
|
lmsw ax ;and away we go
|
|
|
|
; Flush the instruction queue and load the code segment selector
|
|
; by doing a far jump.
|
|
|
|
db 0EAh ;jump far opcode
|
|
dw offset enpm40 ;offset of far pointer
|
|
dw SEL_DXCODE0 ;selector part of PM far pointer (ring 0)
|
|
|
|
; Load the other segment registers with valid selectors (not under VCPI)
|
|
|
|
enpm40: mov ax,SEL_DXDATA0 ;stack has gotta be ring 0 also
|
|
mov ss,ax
|
|
|
|
; Load the LDT register and the Task Register
|
|
|
|
mov ax,SEL_LDT
|
|
lldt ax ;load the LDT register
|
|
|
|
mov ax,SEL_DXDATA or STD_RING
|
|
mov ds,ax ;ds to our DGROUP
|
|
|
|
mov ax,SEL_GDT
|
|
mov es,ax ;es to GDT
|
|
|
|
push si ;make sure busy bit is off
|
|
mov si,SEL_TSS ; in the TSS descriptor
|
|
mov es:[si].arbSegAccess,STD_TSS ; before trying to load it
|
|
ltr si ;now load the task register
|
|
pop si
|
|
else
|
|
.386p
|
|
push ebp
|
|
mov ebp,esp
|
|
push SEL_DXCODE or STD_RING ; new cs
|
|
push 0 ; high half eip
|
|
push offset epmwow ; new eip
|
|
push SEL_DXDATA or STD_RING ; new ss
|
|
push ebp
|
|
push SEL_DXDATA or STD_RING ; new ds
|
|
DPMIBOP DPMISwitchToProtectedMode
|
|
epmwow:
|
|
pop ebp
|
|
.286p
|
|
endif
|
|
|
|
push ds ;point es to DGROUP
|
|
pop es
|
|
|
|
; If this is a 286 machine with a 287 math coprocessor, put the coprocessor
|
|
; into protected mode also.
|
|
|
|
cmp f286_287,0 ;286 and 287?
|
|
jz @f
|
|
|
|
ifndef NEC_98
|
|
xor al,al ; yup, clear co-processor busy line
|
|
out 0F0h,al
|
|
endif ;!NEC_98
|
|
fsetpm ; and put it in pMode
|
|
@@:
|
|
|
|
; We're currently running in ring 0. Setup an interlevel iret frame
|
|
; to switch to our normal ring, and also force IOPL=3. I spent 1+ day
|
|
; debugging on a 286 system (with no debugger!) because the 286 seemed
|
|
; switch into protected mode with IOPL=0, and once we got to an outer
|
|
; ring, we would fault on things like CLI instructions.
|
|
|
|
enpmSwitchRing:
|
|
ifndef WOW_x86
|
|
mov ax,sp ;still points to return address
|
|
push SEL_DXDATA or STD_RING ;new ss
|
|
push ax ;new sp
|
|
pushf
|
|
pop ax
|
|
or ah,30h
|
|
push ax ;new flags, with IOPL=3
|
|
push SEL_DXCODE or STD_RING ;new cs
|
|
push offset DXCODE:epm_ret ;new ip
|
|
iret
|
|
endif
|
|
; When we get here, we are now in an outer ring.
|
|
|
|
epm_ret:
|
|
|
|
.386
|
|
mov ax, selPmodeFS
|
|
mov fs, ax
|
|
mov ax, selPmodeGS
|
|
mov gs, ax
|
|
.286p
|
|
|
|
ret ;near return to caller in pMode
|
|
|
|
EnterProtectedMode endp
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; EnterRealMode -- This routine will switch the processor
|
|
; from protected mode back into real mode. It will also
|
|
; reset the various mode dependent variables to their
|
|
; real mode values and load the segment registers with
|
|
; the real mode segment addresses.
|
|
;
|
|
; Input: none
|
|
; Output: none
|
|
; Errors: none
|
|
; Uses: AX, DS, ES, SS, CS modified
|
|
;
|
|
; NOTE: This routine must be called with the stack segment set
|
|
; to the Dos Extender data segment, as it resets the stack
|
|
; segment register to the Dos Extender real mode data segment
|
|
; but does not modify the stack pointer.
|
|
; NOTE: This routine turns interrupts off and and does not turn
|
|
; them back on.
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public EnterRealMode
|
|
|
|
EnterRealMode proc near
|
|
|
|
.386
|
|
mov ax,fs
|
|
mov selPmodeFS, ax
|
|
mov ax,gs
|
|
mov selPmodeGS, ax
|
|
.286p
|
|
|
|
FCLI
|
|
|
|
mov es,selDgroup
|
|
|
|
push SegDxCode
|
|
push offset DXCODE:enrmwow
|
|
push SegDxData
|
|
push sp
|
|
push SegDxData
|
|
.386p
|
|
DPMIBOP DPMISwitchToRealMode
|
|
.286p
|
|
enrmwow: add sp,6 ; remove rest of parameters
|
|
push ds
|
|
pop es ; es not set by mode switch
|
|
|
|
enrm70:
|
|
push es ;clear DMA services required
|
|
mov ax,DMAServiceSegment ; bit for real mode
|
|
mov es,ax
|
|
and byte ptr es:[DMAServiceByte],not DMAServiceBit
|
|
pop es
|
|
|
|
mov ax,segDXData
|
|
mov selDgroup,ax
|
|
|
|
ret
|
|
|
|
EnterRealMode endp
|
|
|
|
; -------------------------------------------------------
|
|
public RmUnsimulateProc
|
|
|
|
RmUnsimulateProc proc far
|
|
BOP BOP_UNSIMULATE
|
|
RmUnsimulateProc endp
|
|
|
|
; -------------------------------------------------------
|
|
DXCODE ends
|
|
|
|
DXPMCODE segment
|
|
assume cs:DXPMCODE
|
|
|
|
public PmUnsimulateProc
|
|
|
|
PmUnsimulateProc proc far
|
|
BOP BOP_UNSIMULATE
|
|
PmUnsimulateProc endp
|
|
|
|
; -------------------------------------------------------
|
|
; RAW MODE SWITCH ROUTINES
|
|
; -------------------------------------------------------
|
|
|
|
; ------------------------------------------------------
|
|
; PmRawModeSwitch -- This routine performs a raw mode switch from
|
|
; protected mode to real mode. NOTE: applications will JUMP at this
|
|
; routine
|
|
;
|
|
; Input: ax - new DS
|
|
; cx - new ES
|
|
; dx - new SS
|
|
; bx - new sp
|
|
; si - new CS
|
|
; di - new ip
|
|
; Output: DS, ES, SS, sp, CS, ip contain new values
|
|
; Errors: none
|
|
; Uses:
|
|
;
|
|
;
|
|
;
|
|
assume ds:nothing, ss:nothing, es:nothing
|
|
public PmRawModeSwitch
|
|
PmRawModeSwitch proc far
|
|
|
|
push ss
|
|
pop ds
|
|
push bx
|
|
.386p
|
|
mov bx,ss
|
|
movzx ebx,bx
|
|
lar ebx,ebx
|
|
test ebx,(AB_BIG SHL 8)
|
|
mov ebx,esp
|
|
jnz prms10
|
|
|
|
movzx ebx,bx
|
|
prms10:
|
|
.286p
|
|
|
|
; Switch to dosx stack (since switch to real mode will do that to us anyway
|
|
; NOTE: no-one can call EnterIntHandler or ExitIntHandler until we switch to
|
|
; the user's new stack. If they do, they will use the area we stored
|
|
; the parameters for this call for a stack frame
|
|
|
|
rpushf
|
|
FCLI
|
|
push SEL_DXDATA OR STD_RING
|
|
pop ss
|
|
assume ss:DGROUP
|
|
.386p
|
|
movzx esp,word ptr pbReflStack
|
|
.286p
|
|
|
|
; Save user registers
|
|
|
|
push dx ; ss
|
|
.386p
|
|
push word ptr [ebx]
|
|
push word ptr [ebx - 2]; flags pushed before cli
|
|
.286p
|
|
push si ; cs
|
|
push di ; ip
|
|
push ax ; ds
|
|
push cx ; es
|
|
|
|
; switch modes
|
|
|
|
mov ax,SEL_DXDATA OR STD_RING
|
|
mov ds,ax
|
|
SwitchToRealMode
|
|
|
|
; set the registers, switch stacks, and return to the user
|
|
|
|
pop es
|
|
pop ds
|
|
pop ax ; ip
|
|
pop bx ; cs
|
|
pop cx ; flags
|
|
pop si ; sp
|
|
pop ss
|
|
assume ss:nothing
|
|
mov sp,si
|
|
push cx
|
|
popf
|
|
push bx
|
|
push ax
|
|
ret
|
|
|
|
PmRawModeSwitch endp
|
|
|
|
; NOTE: this is now the DXCODE segment, NOT the DXPMCODE segment (courtesy
|
|
; of SwitchToRealMode
|
|
|
|
; ------------------------------------------------------
|
|
; RmRawModeSwitch -- This routine performs a raw mode switch from
|
|
; protected mode to real mode. NOTE: applications will JUMP at this
|
|
; routine
|
|
;
|
|
; Input: ax - new DS
|
|
; cx - new ES
|
|
; dx - new SS
|
|
; bx - new sp
|
|
; si - new CS
|
|
; di - new ip
|
|
; Output: DS, ES, SS, sp, CS, ip contain new values
|
|
; Errors: none
|
|
; Uses:
|
|
;
|
|
;
|
|
;
|
|
assume ds:nothing, ss:nothing, es:nothing
|
|
public RmRawModeSwitch
|
|
RmRawModeSwitch proc far
|
|
|
|
push ss
|
|
pop ds
|
|
push bx
|
|
mov bx,sp
|
|
|
|
; Switch to dosx stack (since switch to real mode will do that to us anyway
|
|
; NOTE: no-one can call EnterIntHandler or ExitIntHandler until we switch to
|
|
; the user's new stack. If they do, they will use the area we stored
|
|
; the parameters for this call for a stack frame
|
|
|
|
pushf
|
|
FCLI
|
|
push segDxData
|
|
pop ss
|
|
assume ss:DGROUP
|
|
mov sp,pbReflStack
|
|
|
|
; Save user registers
|
|
|
|
push dx ; ss
|
|
push word ptr [bx] ; sp
|
|
push word ptr [bx - 2] ; flags from before cli
|
|
push si ; cs
|
|
push di ; ip
|
|
push ax ; ds
|
|
push cx ; es
|
|
|
|
; switch modes
|
|
|
|
mov ax,segDxData
|
|
mov ds,ax
|
|
SwitchToProtectedMode
|
|
|
|
; set the registers, switch stacks, and return to the user
|
|
|
|
pop es
|
|
pop ds
|
|
.386p
|
|
test DpmiFlags,DPMI_32BIT
|
|
jnz rrms10
|
|
|
|
xor eax,eax ; clear high 16 bits
|
|
xor edi,edi ; clear high 16 bits
|
|
.286p
|
|
rrms10: pop di ; ip
|
|
pop ax ; cs
|
|
pop cx ; flags from before cli
|
|
pop bx ; sp
|
|
assume ss:nothing
|
|
pop ss
|
|
.386p
|
|
mov esp,ebx
|
|
.286p
|
|
push cx
|
|
rpopf
|
|
|
|
.386p
|
|
push eax
|
|
push edi
|
|
db 066h
|
|
retf
|
|
.286p
|
|
|
|
RmRawModeSwitch endp
|
|
|
|
DXPMCODE ENDS
|
|
|
|
DXCODE SEGMENT
|
|
|
|
; -------------------------------------------------------
|
|
; STATE SAVE/RESTORE ROUTINES
|
|
; -------------------------------------------------------
|
|
|
|
; -------------------------------------------------------
|
|
; RmSaveRestoreState -- This routine exists as a placeholder. It
|
|
; is not currently necessary to perform any state saving/restoring
|
|
; for raw mode switch. The DPMI spec states that the user can call
|
|
; this routine with no adverse effect.
|
|
;
|
|
; Input: none
|
|
; Output: none
|
|
; Errors: none
|
|
; Uses: none
|
|
;
|
|
assume ds:nothing, ss:nothing, es:nothing
|
|
public RmSaveRestoreState
|
|
RmSaveRestoreState proc far
|
|
ret
|
|
RmSaveRestoreState endp
|
|
|
|
DXCODE ends
|
|
|
|
; -------------------------------------------------------
|
|
|
|
DXPMCODE segment
|
|
assume cs:DXPMCODE
|
|
|
|
; -------------------------------------------------------
|
|
; RmSaveRestoreState -- This routine exists as a placeholder. It
|
|
; is not currently necessary to perform any state saving/restoring
|
|
; for raw mode switch. The DPMI spec states that the user can call
|
|
; this routine with no adverse effect.
|
|
;
|
|
; Input: none
|
|
; Output: none
|
|
; Errors: none
|
|
; Uses: none
|
|
;
|
|
assume ds:DGROUP, ss:nothing, es:nothing
|
|
public PmSaveRestoreState
|
|
PmSaveRestoreState proc far
|
|
push ax
|
|
push ds
|
|
mov ax, SEL_DXDATA or STD_RING
|
|
mov ds, ax
|
|
test DpmiFlags,DPMI_32BIT
|
|
pop ds
|
|
pop ax
|
|
jnz short @f ; 32-bit return
|
|
ret
|
|
@@:
|
|
db 66h
|
|
ret
|
|
|
|
PmSaveRestoreState endp
|
|
|
|
ifdef NEC_98
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public IncInBios
|
|
public DecInBios
|
|
|
|
;
|
|
; IncInBios / DecInBios
|
|
;
|
|
; IN_BIOS Inc/Dec SubRoutine
|
|
;
|
|
IncInBios proc near
|
|
push ax
|
|
push es
|
|
mov ax, 0040h
|
|
mov es, ax
|
|
test byte ptr es:[0], 20h
|
|
jz @f
|
|
inc byte ptr es:[056h]
|
|
@@:
|
|
pop es
|
|
pop ax
|
|
ret
|
|
IncInBios endp
|
|
|
|
DecInBios proc near
|
|
push ax
|
|
push es
|
|
mov ax, 0040h
|
|
mov es, ax
|
|
test byte ptr es:[0], 20h
|
|
jz @f
|
|
dec byte ptr es:[056h]
|
|
@@:
|
|
pop es
|
|
pop ax
|
|
ret
|
|
DecInBios endp
|
|
endif ;NEC_98
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; GTPARA -- This routine will return the real mode paragraph
|
|
; address of the specified protected mode memory segment.
|
|
;
|
|
; Input: SI - selector of the segment
|
|
; Output: returns ZR true if segment is in lower 1MB range
|
|
; AX - real mode paragraph address
|
|
; returns ZR false if segment is in extended memory
|
|
; Errors: returns CY true if selector isn't valid
|
|
; Uses: AX modified, all else preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public gtpara
|
|
|
|
gtpara:
|
|
push cx
|
|
push es
|
|
push si
|
|
|
|
push bx
|
|
mov bx,selGDT ;selector for the GDT segment
|
|
mov es,bx
|
|
lsl bx,bx
|
|
and bx,SELECTOR_INDEX
|
|
and si,SELECTOR_INDEX
|
|
cmp si,bx ;check the given selector against
|
|
; the GDT segment limit
|
|
pop bx
|
|
jc gtpr20
|
|
|
|
; The given selector is beyond the end of the GDT, so return error.
|
|
|
|
or sp,sp
|
|
stc
|
|
|
|
Debug_Out "gtpara: invalid selector (#si)"
|
|
|
|
jmp short gtpr90
|
|
|
|
; The selector specified is inside the range of defined descriptors in
|
|
; the GDT. Get the address from the descriptor.
|
|
|
|
gtpr20: mov cl,es:[si].adrBaseHigh
|
|
test cl,0F0h
|
|
jnz gtpr90
|
|
|
|
shl cl,4
|
|
mov ax,es:[si].adrBaseLow
|
|
|
|
if DEBUG ;------------------------------------------------------------
|
|
test al,0Fh
|
|
jz @f
|
|
Debug_Out "gtpara: segment not on para boundry, sel #si at #cl#ax"
|
|
@@:
|
|
endif ;DEBUG --------------------------------------------------------
|
|
|
|
shr ax,4
|
|
or ah,cl
|
|
cmp ax,ax
|
|
;
|
|
gtpr90:
|
|
pop si
|
|
pop es
|
|
pop cx
|
|
ret
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; SelOff2SegOff -- This routine will return will translate a
|
|
; protected mode selector:offset address to the corresponding
|
|
; real mode segment:offset address.
|
|
;
|
|
; Input: AX - PM selector
|
|
; DX - PM offset
|
|
; Output: if Z set:
|
|
; AX - RM segment
|
|
; DX - RM offset
|
|
; if NZ set, address is not in conventional memory, and
|
|
; cannot be translated
|
|
;
|
|
; Errors: none
|
|
; Uses: AX, DX; all else preserved
|
|
;
|
|
; Note: This routine is very similar to gtpara, and could replace it!
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public SelOff2SegOff
|
|
|
|
SelOff2SegOff proc near
|
|
|
|
push bx
|
|
push cx
|
|
push dx
|
|
|
|
call GetSegmentAddress ;bx:dx = lma of segment
|
|
|
|
pop cx ;cx = offset
|
|
|
|
test bl,0f0h ;above 1 Meg line?
|
|
jnz @f ; yes, cut out now
|
|
|
|
add dx,cx
|
|
adc bx,0 ;bx:dx = lma of segment:offset
|
|
|
|
call Lma2SegOff ;bx:dx = seg:off
|
|
mov ax,bx ;dx:ax = seg:off
|
|
|
|
cmp ax,ax ;under 1 Meg, set Z flag
|
|
@@:
|
|
pop cx
|
|
pop bx
|
|
|
|
ret
|
|
|
|
SelOff2SegOff endp
|
|
|
|
|
|
; ------------------------------------------------------
|
|
; Lma2SegOff -- This routine converts a linear memory address
|
|
; in BX:DX to a normalized SEG:OFF in BX:DX.
|
|
;
|
|
; Input: BX:DX = lma
|
|
; Output: BX:DX = normalized SEG:OFF
|
|
; Uses: none
|
|
|
|
|
|
public Lma2SegOff
|
|
|
|
Lma2SegOff proc near
|
|
|
|
push dx
|
|
shl bx,12
|
|
shr dx,4
|
|
or bx,dx
|
|
pop dx
|
|
and dx,0fh
|
|
|
|
ret
|
|
|
|
Lma2SegOff endp
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; GetSegmentAddress -- This routine will return with
|
|
; the linear address of the specified segment.
|
|
;
|
|
; Input: AX - segment selector
|
|
; Output: DX - low word of segment address
|
|
; BX - high word of segment address
|
|
; Errors: none
|
|
; Uses: BX, DX, all else preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public GetSegmentAddress
|
|
|
|
GetSegmentAddress:
|
|
push es
|
|
push di
|
|
|
|
mov es,selGDT
|
|
mov di,ax
|
|
and di,SELECTOR_INDEX
|
|
mov dx,es:[di].adrBaseLow
|
|
mov bl,es:[di].adrBaseHigh
|
|
mov bh,es:[di].adrbBaseHi386
|
|
|
|
pop di
|
|
pop es
|
|
ret
|
|
|
|
; -------------------------------------------------------
|
|
; SetSegmentAddress -- This routine will modify the
|
|
; segment base address of the specified segment.
|
|
;
|
|
; Input: AX - segment selector
|
|
; Output: DX - low word of segment address
|
|
; BX - high word of segment address
|
|
; Errors: None
|
|
; Uses: All registers preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public SetSegmentAddress
|
|
|
|
SetSegmentAddress:
|
|
push si
|
|
push es
|
|
|
|
mov es,selGDT
|
|
mov si,ax
|
|
and si,SELECTOR_INDEX
|
|
mov es:[si].adrBaseLow,dx
|
|
mov es:[si].adrBaseHigh,bl
|
|
mov es:[si].adrbBaseHi386,bh
|
|
push ax
|
|
push bx
|
|
push cx
|
|
mov ax,si
|
|
mov cx,1
|
|
mov bx,si
|
|
.386p
|
|
FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
|
|
.286p
|
|
pop cx
|
|
pop bx
|
|
pop ax
|
|
pop es
|
|
pop si
|
|
ret
|
|
|
|
; -------------------------------------------------------
|
|
; NSetSegmentAccess -- This routine will modify the
|
|
; access rights byte of a specified segment.
|
|
;
|
|
; Input: Selector - segment selector
|
|
; Access - Access rights byte value
|
|
; Output: none
|
|
; Errors: Carry set, AX = error code
|
|
; Uses: All registers preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
cProc NSetSegmentAccess,<PUBLIC,NEAR>,<es,si>
|
|
parmW Selector
|
|
parmW Access
|
|
cBegin
|
|
mov es,selGDT
|
|
mov si,Selector
|
|
and si,SELECTOR_INDEX
|
|
mov ax,Access
|
|
mov es:[si].arbSegAccess,al ; Set access byte.
|
|
and ah,0F0h ; Mask off reserved bits.
|
|
and es:[si].cbLimitHi386,0fh ; Clear old extended bits.
|
|
or es:[si].cbLimitHi386,ah ; Set new extended bits.
|
|
IFDEF WOW_x86
|
|
push ax
|
|
push bx
|
|
push cx
|
|
mov ax,si
|
|
mov cx,1
|
|
mov bx,si
|
|
.386p
|
|
FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
|
|
.286p
|
|
pop cx
|
|
pop bx
|
|
pop ax
|
|
ENDIF
|
|
|
|
cEnd
|
|
|
|
; -------------------------------------------------------
|
|
; ParaToLDTSelector -- This routine will convert a segment
|
|
; address relative to the start of the exe file into the
|
|
; corresponding selector for the segment. It searches the
|
|
; LDT to see if a segment is already defined at that address.
|
|
; If so, its selector is returned. If not, a new segment
|
|
; descriptor will be defined.
|
|
;
|
|
; Note: The LDT and GDT are currently one and the same.
|
|
;
|
|
; Input: AX - paragraph aaddress of real mode segment
|
|
; BX - access rights byte for the segment
|
|
; Output: AX - selector for the segment
|
|
; Errors: returns CY set if unable to define a new segment
|
|
; Uses: AX, all other registers preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public ParaToLDTSelector
|
|
|
|
ParaToLDTSelector proc near
|
|
|
|
push bx
|
|
push cx
|
|
push dx
|
|
|
|
; Convert the paragraph address to a linear address and see if there
|
|
; is a segment defined at that address.
|
|
|
|
mov dx,ax
|
|
call FindLowSelector
|
|
jnc @f ;if so, we don't need to make one
|
|
|
|
; This segment isn't defined, so we need to create a descriptor for it.
|
|
|
|
mov ax,dx
|
|
call MakeLowSegment
|
|
|
|
if DEBUG ;------------------------------------------------------------
|
|
jnc ptos80
|
|
Debug_Out "ParaToLDTSelector: can't make selector!"
|
|
ptos80:
|
|
endif ;DEBUG --------------------------------------------------------
|
|
|
|
jc ptos90
|
|
|
|
@@: or al,SELECTOR_TI or STD_RING ;look like LDT selector
|
|
|
|
; All done
|
|
|
|
ptos90: pop dx
|
|
pop cx
|
|
pop bx
|
|
ret
|
|
|
|
ParaToLDTSelector endp
|
|
|
|
public FarParaToLDTSelector
|
|
FarParaToLDTSelector proc far
|
|
call ParaToLDTSelector
|
|
ret
|
|
FarParaToLDTSelector endp
|
|
|
|
; -------------------------------------------------------
|
|
page
|
|
; -------------------------------------------------------
|
|
; DESCRIPTOR TABLE MANIPULATION ROUTINES
|
|
; -------------------------------------------------------
|
|
|
|
; -------------------------------------------------------
|
|
; AllocateSelector -- This function will obtain the
|
|
; next free descriptor in the global descriptor table.
|
|
; The descriptors in the GDT are stored on a linked list
|
|
; of free descriptors. The cbLimit field of the descriptor
|
|
; is used as the link to the next element of the list. In
|
|
; addition, free descriptors have the access rights byte
|
|
; set to 0.
|
|
;
|
|
; Note: The function InitGlobalDscrTable must have been
|
|
; called before calling this function.
|
|
;
|
|
; Input: none
|
|
; Output: AX - selector if one is available
|
|
; Errors: CY clear if successful, AX=0 and CY set if not free selectors
|
|
; Uses: AX, all else preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public AllocateSelector
|
|
|
|
AllocateSelector proc near
|
|
|
|
; Get the next free descriptor on the list.
|
|
|
|
push cx
|
|
mov cx, 1 ; # of selectors needed
|
|
mov ax, 0
|
|
push ds
|
|
FBOP BOP_DPMI,Int31Call,FastBop
|
|
pop cx
|
|
ret
|
|
|
|
|
|
AllocateSelector endp
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; FreeSelector -- This routine will mark the segment
|
|
; descriptor for the specified selector as free. This
|
|
; is used to release a temporary selector when no longer
|
|
; needed. The descriptor is marked as free by setting the
|
|
; access rights byte to 0 and placing it on the free list.
|
|
;
|
|
; Note: This routine can only be called in protected mode.
|
|
;
|
|
; Input: AX - selector to free
|
|
; Output: none
|
|
; Errors: CY clear if no error, set if selector is invalid
|
|
; Uses: AX used, all other registers preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public FreeSelector
|
|
|
|
FreeSelector proc near
|
|
|
|
push bx
|
|
mov bx,ax
|
|
mov ax,1
|
|
push ds
|
|
FBOP BOP_DPMI,Int31Call,FastBop
|
|
pop bx
|
|
ret
|
|
|
|
|
|
FreeSelector endp
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; FindLowSelector -- This function will search the global
|
|
; descriptor table for a descriptor matching the given
|
|
; address.
|
|
;
|
|
; Input: AX - real mode paragraph to search for
|
|
; BX - access rights byte for the segment
|
|
; Output: AX - selector corresponding to input paragraph address
|
|
; Errors: returns CY set if specified descriptor not found
|
|
; Uses: AX, all else preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public FindLowSelector
|
|
|
|
FindLowSelector:
|
|
push bx
|
|
push dx
|
|
;
|
|
mov dx,ax
|
|
push bx
|
|
call ParaToLinear
|
|
pop ax
|
|
mov bh,al
|
|
call FindSelector
|
|
;
|
|
pop dx
|
|
pop bx
|
|
ret
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; FindSelector -- This function will search the global
|
|
; descriptor table for a segment descriptor matching
|
|
; the specified linear byte address.
|
|
;
|
|
; Note that this routine cannot be used to find
|
|
; selectors pointing to addresses above 16 Megabytes.
|
|
; This is not really a problem, since the routine
|
|
; is used to find selectors in real mode DOS land
|
|
; most of the time.
|
|
;
|
|
; Input: DX - low word of linear byte address
|
|
; BL - high byte of linear address
|
|
; BH - access rights byte for the segment
|
|
; Output: AX - selector of corresponding segment
|
|
; Errors: returns CY set if specified descriptor not found
|
|
; Uses: AX used, all other registers preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public FindSelector
|
|
|
|
FindSelector proc near
|
|
|
|
push si
|
|
push di
|
|
push es
|
|
push cx
|
|
|
|
; Get segment limit of the GDT to use as a limit on the search.
|
|
|
|
lsl di,selGDT
|
|
mov es,selGDT
|
|
|
|
; Look for a descriptor matching the address in BL:AX
|
|
|
|
mov si,SEL_USER ;search starting here
|
|
if 0
|
|
fnds20: cmp es:[si].arbSegAccess,0
|
|
else
|
|
fnds20: cmp word ptr es:[si].arbSegAccess,0
|
|
endif
|
|
jz fnds28 ;skip if unused descriptor
|
|
cmp bl,es:[si].adrBaseHigh
|
|
jnz fnds28
|
|
cmp dx,es:[si].adrBaseLow
|
|
jnz fnds28
|
|
if 0
|
|
cmp es:[si].cbLimit,0
|
|
jz fnds28 ;skip if dscr has 0 limit
|
|
else
|
|
cmp es:[si].cbLimit,0ffffh
|
|
jnz fnds28 ;skip unless dscr has 64k limit
|
|
endif
|
|
mov cl,bh
|
|
xor cl,es:[si].arbSegAccess
|
|
and cl,NOT AB_ACCESSED
|
|
jz fnds90
|
|
fnds28: add si,8 ;bump to next descriptor
|
|
jc fnds80
|
|
cmp si,di ;check against end of GDT
|
|
jc fnds20 ;if still less, continue on.
|
|
|
|
; Hit the end of the GDT and didn't find one. So return error.
|
|
|
|
fnds80: stc
|
|
jmp short fnds99
|
|
|
|
; We found it, so return the selector
|
|
|
|
fnds90: mov ax,si
|
|
fnds99: pop cx
|
|
pop es
|
|
pop di
|
|
pop si
|
|
ret
|
|
|
|
FindSelector endp
|
|
|
|
|
|
|
|
; -------------------------------------------------------
|
|
; DupSegmentDscr -- This function will duplicate the specified
|
|
; segment descriptor into the specified destination descriptor. The
|
|
; end result is a second segment descriptor pointing to the same place
|
|
; in memory as the first.
|
|
;
|
|
; Input: AX - selector of segment descriptor to duplicate
|
|
; BX - selector of the segment descriptor to receive duplicate
|
|
; Output: none
|
|
; Errors: none
|
|
; Uses: All registers preserved. Modifies the segment
|
|
; descriptor for the specified segment. If this selector happens
|
|
; to be in a segment register when this routine is called, that
|
|
; segment register may end up pointing to the new location.
|
|
|
|
assume ds:DGROUP,es:NOTHING
|
|
public DupSegmentDscr
|
|
|
|
DupSegmentDscr proc near
|
|
|
|
push cx
|
|
push si
|
|
push di
|
|
push ds
|
|
push es
|
|
|
|
mov si,ax
|
|
mov di,bx
|
|
and si,SELECTOR_INDEX
|
|
and di,SELECTOR_INDEX
|
|
mov es,selGDT
|
|
mov ds,selGDT
|
|
assume ds:NOTHING
|
|
mov cx,4
|
|
cld
|
|
rep movs word ptr [di],word ptr [si]
|
|
|
|
pop es
|
|
pop ds
|
|
pop di
|
|
pop si
|
|
pop cx
|
|
ret
|
|
|
|
DupSegmentDscr endp
|
|
|
|
IFDEF ROM
|
|
; -------------------------------------------------------
|
|
|
|
DXPMCODE ends
|
|
|
|
; -------------------------------------------------------
|
|
|
|
DXCODE segment
|
|
assume cs:DXCODE
|
|
ENDIF
|
|
|
|
; -------------------------------------------------------
|
|
; NSetSegmentDscr -- This function will initialize the
|
|
; specified descriptor table entry with the specified data.
|
|
;
|
|
; This function can be called in real mode or protected mode.
|
|
;
|
|
; Input:
|
|
; Param1 - WORD segment selector
|
|
; Param2 - DWORD 32-bit segment base address
|
|
; Param3 - DWORD 32-bit segment limit
|
|
; param4 - WORD segment access/type
|
|
; Output: returns selector for the segment
|
|
; Errors: none
|
|
; Uses: Flags
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
cProc NSetSegmentDscr,<PUBLIC,FAR>,<es,di,ax,bx>
|
|
parmW Selector
|
|
parmD Base
|
|
parmD Limit
|
|
parmW Access
|
|
cBegin
|
|
mov es,selGDT
|
|
mov di,Selector
|
|
and di,SELECTOR_INDEX
|
|
|
|
mov ax,off_Base ; Set segment base
|
|
mov es:[di].adrBaseLow,ax
|
|
mov ax,seg_Base
|
|
mov es:[di].adrBaseHigh,al
|
|
mov es:[di].adrbBaseHi386,ah
|
|
|
|
mov ax,word ptr Access
|
|
and ax,070ffh ; clear 'G' bit and
|
|
; extended limit bits
|
|
mov word ptr es:[di].arbSegAccess,ax
|
|
; set access
|
|
mov ax,seg_Limit
|
|
mov bx,off_Limit ; AX:BX = segment limit
|
|
test ax,0fff0h ; big?
|
|
jz ssd_0 ; No
|
|
shr bx,12d ; Yes, make it page granular.
|
|
shl ax,4d
|
|
or bx,ax
|
|
mov ax,seg_Limit
|
|
shr ax,12d
|
|
or al,080h ; set 'G' bit
|
|
ssd_0:
|
|
or es:[di].cbLimitHi386,al ; set high limit
|
|
mov es:[di].cbLimit,bx ; set low limit
|
|
push ax
|
|
push bx
|
|
push cx
|
|
mov ax,di
|
|
mov cx,1
|
|
mov bx,di
|
|
.386p
|
|
FBOP BOP_DPMI,<SetDescriptorTableEntries>,FastBop
|
|
.286p
|
|
pop cx
|
|
pop bx
|
|
pop ax
|
|
cEnd
|
|
|
|
ifndef WOW_x86
|
|
; -------------------------------------------------------
|
|
; NSetGDTSegmentDscr -- This function will initialize the
|
|
; specified descriptor table entry with the specified data.
|
|
;
|
|
; This function can be called in real mode or protected mode.
|
|
;
|
|
; Input:
|
|
; Param1 - WORD segment selector
|
|
; Param2 - DWORD 32-bit segment base address
|
|
; Param3 - DWORD 32-bit segment limit
|
|
; param4 - WORD segment access/type
|
|
; Output: returns selector for the segment
|
|
; Errors: none
|
|
; Uses: Flags
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
cProc NSetGDTSegmentDscr,<PUBLIC,FAR>,<es,di,ax,bx>
|
|
parmW Selector
|
|
parmD Base
|
|
parmD Limit
|
|
parmW Access
|
|
cBegin
|
|
mov ax,SEL_GDT
|
|
mov es,ax
|
|
mov di,Selector
|
|
and di,SELECTOR_INDEX
|
|
|
|
mov ax,off_Base ; Set segment base
|
|
mov es:[di].adrBaseLow,ax
|
|
mov ax,seg_Base
|
|
mov es:[di].adrBaseHigh,al
|
|
mov es:[di].adrbBaseHi386,ah
|
|
|
|
mov ax,word ptr Access
|
|
and ax,070ffh ; clear 'G' bit and
|
|
; extended limit bits
|
|
mov word ptr es:[di].arbSegAccess,ax
|
|
; set access
|
|
mov ax,seg_Limit
|
|
mov bx,off_Limit ; AX:BX = segment limit
|
|
test ax,0fff0h ; big?
|
|
jz @f ; No
|
|
shr bx,12d ; Yes, make it page granular.
|
|
shl ax,4d
|
|
or bx,ax
|
|
mov ax,seg_Limit
|
|
shr ax,12d
|
|
or al,080h ; set 'G' bit
|
|
@@:
|
|
or es:[di].cbLimitHi386,al ; set high limit
|
|
mov es:[di].cbLimit,bx ; set low limit
|
|
cEnd
|
|
endif ; WOW_x86
|
|
|
|
|
|
IFDEF ROM
|
|
; -------------------------------------------------------
|
|
|
|
DXCODE ends
|
|
|
|
; -------------------------------------------------------
|
|
|
|
DXPMCODE segment
|
|
assume cs:DXPMCODE
|
|
ENDIF
|
|
|
|
; -------------------------------------------------------
|
|
; MakeLowSegment -- This function will create a segment
|
|
; descriptor for the specified low memory paragraph address.
|
|
; The segment length will be set to 64k. The difference
|
|
; between this and MakeScratchSelector is that this function
|
|
; allocates a new segment descriptor in the user area of
|
|
; the global descriptor table, thus creating a more or less
|
|
; permanent selector. MakeScratchSelector always uses the
|
|
; same descriptor location in the descriptor table, thus
|
|
; creating a very temporary selector.
|
|
;
|
|
; Input: AX - paragraph address in low memory
|
|
; BX - access rights word for the segment
|
|
; Output: AX - selector to use to access the memory
|
|
; Errors: returns CY clear if no error, CY set if unable to
|
|
; allocate a segment descriptor
|
|
; Uses: AX used, all else preserved
|
|
|
|
assume ds:DGROUP,es:NOTHING,ss:NOTHING
|
|
public MakeLowSegment
|
|
|
|
MakeLowSegment proc near
|
|
|
|
; We need to allocate a segment descriptor, convert the paragraph address
|
|
; to a linear byte address, and then initialize the allocated segment
|
|
; descriptor.
|
|
|
|
push dx
|
|
push cx
|
|
|
|
mov cx,bx
|
|
mov dx,ax ;paragraph address to DX
|
|
call AllocateSelector ;get a segment descriptor to use
|
|
jc mksl90 ;get out if error
|
|
call ParaToLinear
|
|
cCall NSetSegmentDscr,<ax,bx,dx,0,0FFFFh,cx>
|
|
|
|
clc
|
|
mksl90:
|
|
pop cx
|
|
pop dx
|
|
ret
|
|
|
|
MakeLowSegment endp
|
|
|
|
; -------------------------------------------------------
|
|
; ParaToLinear
|
|
;
|
|
; This function will convert a paragraph address in the lower
|
|
; megabyte of memory space into a linear address for use in
|
|
; a descriptor table.
|
|
;
|
|
; Input: DX - paragraph address
|
|
; Output: DX - lower word of linear address
|
|
; BX - high word of linear address
|
|
; Errors: none
|
|
; Uses: DX, BL used, all else preserved
|
|
|
|
assume ds:NOTHING,es:NOTHING,ss:NOTHING
|
|
public ParaToLinear
|
|
|
|
ParaToLinear proc near
|
|
|
|
mov bl,dh
|
|
shr bl,4
|
|
shl dx,4
|
|
xor bh,bh
|
|
ret
|
|
|
|
ParaToLinear endp
|
|
|
|
; -------------------------------------------------------
|
|
; 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
|
|
|
|
; -------------------------------------------------------
|
|
; -------------------------------------------------------
|
|
|
|
DXPMCODE ends
|
|
|
|
;
|
|
;****************************************************************
|
|
|
|
end
|