613 lines
16 KiB
NASM
613 lines
16 KiB
NASM
title "Operand Decoding"
|
|
;++
|
|
;
|
|
;Copyright (c) 1991 Microsoft Corporation
|
|
;
|
|
;Module Name:
|
|
;
|
|
; vdmoprnd.asm
|
|
;
|
|
;Abstract:
|
|
;
|
|
; This module contains support for decoding 386/486 instruction operands.
|
|
; This is used by the opcode 0f emulation.
|
|
;
|
|
;
|
|
;Author:
|
|
;
|
|
; Dave Hastings (daveh) 20-Feb-1992
|
|
;
|
|
;Notes:
|
|
;
|
|
; The only instruction which uses the operand decodeing (3/10/92) is
|
|
; LMSW. This instruction only has 16 bit operands, so only the 16 bit
|
|
; operand decode has been tested. The 32 bit decode will be tested
|
|
; (or removed?) during clean up, after code freeze.
|
|
;
|
|
;Revision History:
|
|
;
|
|
;--
|
|
.386p
|
|
|
|
.xlist
|
|
include ks386.inc
|
|
include callconv.inc ; calling convention macros
|
|
include mi386.inc
|
|
include vdm.inc
|
|
include vdmtib.inc
|
|
|
|
page ,132
|
|
|
|
_PAGE SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
EXTRNP _Ki386GetSelectorParameters,4
|
|
extrn CheckEip:proc
|
|
|
|
_PAGE ENDS
|
|
|
|
PAGEDATA SEGMENT DWORD PUBLIC 'DATA'
|
|
;
|
|
; This table is used to dispatch base on the mod code for 16 bit address size.
|
|
; When these locations are dispatched to,
|
|
; edi = linear address of next byte of instruction
|
|
; esi = pointer to register info
|
|
; ecx = R/M value for the instruction
|
|
modtab16 dd offset FLAT:do20 ; no displacement
|
|
dd offset FLAT:do40 ; 8 bit displacement
|
|
dd offset FLAT:do50 ; 16 bit displacement
|
|
dd offset FLAT:do60 ; Register operand
|
|
|
|
;
|
|
; This table is used to dispatch based on the RM code for 16 bit address size.
|
|
; When these locations are dispatched to,
|
|
; edi = pointer to trap frame
|
|
; esi = pointer to register info
|
|
; ebx = partial linear address of operand
|
|
rmtab16 dd offset FLAT:do70 ; [bx + si]
|
|
dd offset FLAT:do80 ; [bx + di]
|
|
dd offset FLAT:do90 ; [bp + si]
|
|
dd offset FLAT:do100 ; [bp + di]
|
|
dd offset FLAT:do95 ; [si]
|
|
dd offset FLAT:do85 ; [di]
|
|
dd offset FLAT:do105 ; [bp]
|
|
dd offset FLAT:do75 ; [bx]
|
|
|
|
;
|
|
; This table is used to dispatch base on the mod code for 32 bit address size.
|
|
; When these locations are dispatched to,
|
|
; edi = linear address of next byte of instruction
|
|
; esi = pointer to register info
|
|
; ecx = R/M value for the instruction
|
|
modtab32 dd offset FLAT:do220 ; no displacement
|
|
dd offset FLAT:do240 ; 8 bit displacement
|
|
dd offset FLAT:do250 ; 32 bit displacement
|
|
dd offset FLAT:do260 ; Register operand
|
|
|
|
;
|
|
; This table is used to pick up register offsets in the trap frame.
|
|
; N.B. This table cannot be used to find byte registers!!
|
|
;
|
|
public RegTab
|
|
RegTab dd TsEax
|
|
dd TsEcx
|
|
dd TsEdx
|
|
dd TsEbx
|
|
dd TsHardwareEsp
|
|
dd TsEbp
|
|
dd TsEsi
|
|
dd TsEdi
|
|
|
|
|
|
PAGEDATA ENDS
|
|
|
|
|
|
_PAGE SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
|
|
|
|
page ,132
|
|
subttl "Decode Operands"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine decodes the operands for 386/486 instructions. It returns
|
|
; the linear address of the operand. For register operands, this is
|
|
; an address in the stack frame. The read/write flag is used for
|
|
; operand verification.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; esi = address of reg info
|
|
; eax = 1 -- Read of operand
|
|
; 0 -- Write of operand
|
|
;
|
|
; Returns:
|
|
;
|
|
; eax = True -- operand ok, and reg info operand field filled in
|
|
; eax = False -- operand not ok.
|
|
; reg info eip updated
|
|
;
|
|
; Notes:
|
|
;
|
|
; This routine DOES NOT decode the reg field of the mod r/m byte of the
|
|
; opcode. This is not a problem because it will only return one operand
|
|
; address anyway. It does not decode byte registers correctly!!.
|
|
;
|
|
; check grow down ss handling
|
|
|
|
public VdmDecodeOperand
|
|
VdmDecodeOperand proc
|
|
|
|
SegBase equ [ebp] - 04h
|
|
SegLimit equ [ebp] - 08h
|
|
SegFlags equ [ebp] - 0ch
|
|
SelLookupResult equ [ebp] - 010h
|
|
ReadWrite equ [ebp] - 014h
|
|
ModRm equ [ebp] - 018h
|
|
SIB equ [ebp] - 01ch
|
|
|
|
push ebp
|
|
mov ebp,esp
|
|
sub esp,01ch
|
|
push edi
|
|
push ecx
|
|
push ebx
|
|
|
|
mov ReadWrite,eax
|
|
|
|
;
|
|
; Get the info on DS (assumed default selector)
|
|
;
|
|
lea edx,SegLimit
|
|
push edx
|
|
lea edx,SegBase
|
|
push edx
|
|
lea edx,SegFlags
|
|
push edx
|
|
mov edi,[esi].RiTrapFrame
|
|
push [edi].TsSegDs
|
|
call VdmSegParams
|
|
add esp,010h
|
|
mov SelLookupResult,eax ; check result after override check
|
|
|
|
mov edi,[esi].RiEip
|
|
add edi,[esi].RiCsBase
|
|
call CheckEip
|
|
test al,0fh
|
|
jz do200 ; Gp fault, report error
|
|
|
|
movzx edx,byte ptr [edi] ; get mod r/m byte
|
|
inc [esi].RiEip
|
|
inc edi
|
|
mov ecx,edx
|
|
mov ModRm,edx
|
|
and edx,MI_MODMASK
|
|
shr edx,MI_MODSHIFT ; form jump table index from mod
|
|
and ecx,MI_RMMASK ; form index for RM jump table
|
|
test [esi].RiPrefixFlags,PREFIX_ADDR32
|
|
; 32 bit segments.
|
|
jnz do210 ; 32 bit instructions have diff form
|
|
|
|
jmp modtab16[edx * 4]
|
|
|
|
do20:
|
|
;
|
|
; These forms have no displacement, except for the "bp" form, which
|
|
; is just a 16 bit immediate displacement
|
|
;
|
|
mov ebx,0 ; assume no displacement
|
|
cmp ecx,MI_RMBP
|
|
jne do30 ; dispatch through jmp table
|
|
|
|
call CheckEip
|
|
test al,0fh
|
|
jz do200 ; Gp fault, report error
|
|
|
|
movzx ebx,word ptr [edi] ; get displacement
|
|
inc [esi].RiEip ; update eip
|
|
inc [esi].RiEip
|
|
jmp do120 ; go add in seg
|
|
|
|
do30: mov edi,[esi].RiTrapFrame
|
|
jmp rmtab16[ecx * 4] ; go get register info.
|
|
|
|
do40:
|
|
;
|
|
; These forms have an 8 bit displacement
|
|
;
|
|
call CheckEip
|
|
test al,0fh
|
|
jz do200 ; Gp fault, report error
|
|
|
|
movsx ebx,byte ptr [edi]
|
|
inc [esi].RiEip
|
|
mov edi,[esi].RiTrapFrame
|
|
jmp rmtab16[ecx * 4]
|
|
|
|
do50:
|
|
;
|
|
; These forms have an 16 bit displacement
|
|
;
|
|
call CheckEip
|
|
test al,0fh
|
|
jz do200 ; Gp fault, report error
|
|
|
|
movzx ebx,word ptr [edi]
|
|
inc [esi].RiEip
|
|
inc [esi].RiEip
|
|
mov edi,[esi].RiTrapFrame
|
|
jmp rmtab16[ecx * 4]
|
|
|
|
do60:
|
|
;
|
|
; These forms are register operands
|
|
;
|
|
mov ebx,RegTab[ecx * 4] ; get offset into stackframe
|
|
add ebx,[esi].RiTrapFrame ; form linear address
|
|
jmp do194 ; return success
|
|
|
|
do70:
|
|
;
|
|
; This is the [bx + si] operand
|
|
;
|
|
movzx edx,word ptr [edi].TsEsi
|
|
add ebx,edx
|
|
|
|
do75:
|
|
;
|
|
; This is the [bx] operand, and a fall through to finish forming [bx + si]
|
|
;
|
|
movzx edx,word ptr [edi].TsEbx
|
|
add ebx,edx
|
|
jmp do120 ; go add seg info
|
|
|
|
do80:
|
|
;
|
|
; This is the [bx + di] operand
|
|
;
|
|
movzx edx,word ptr [edi].TsEbx
|
|
add ebx,edx
|
|
|
|
do85:
|
|
;
|
|
; This is the [di] operand, and the fall through to finish [bx + di]
|
|
;
|
|
movzx edx,word ptr [edi].TsEdi
|
|
add ebx,edx
|
|
jmp do120 ; go add seg info
|
|
|
|
do90:
|
|
;
|
|
; This is the [bp + si] operand
|
|
;
|
|
movzx edx,word ptr [edi].TsEbp
|
|
add ebx,edx
|
|
;
|
|
; Change default segment to be ss
|
|
;
|
|
lea edx,SegLimit
|
|
push edx
|
|
lea edx,SegBase
|
|
push edx
|
|
lea edx,SegFlags
|
|
push edx
|
|
mov edi,[esi].RiTrapFrame
|
|
push [edi].TsHardwareSegSs
|
|
call VdmSegParams
|
|
add esp,010h
|
|
mov SelLookupResult,eax
|
|
|
|
do95:
|
|
;
|
|
; This is the [si] operand, and the fall through for forming [bp + si]
|
|
;
|
|
movzx edx,word ptr [edi].TsEsi
|
|
add ebx,edx
|
|
jmp do120 ; go add seg info
|
|
|
|
do100:
|
|
;
|
|
; This is the [bp + di] operand
|
|
;
|
|
movzx edx,word ptr [edi].TsEdi
|
|
add ebx,edx
|
|
|
|
do105:
|
|
;
|
|
; This is the [bp] operand, and the fall through for forming [bp + di]
|
|
;
|
|
movzx edx,word ptr [edi].TsEbp
|
|
add ebx,edx
|
|
;
|
|
; Change default segment to be SS
|
|
;
|
|
lea edx,SegLimit
|
|
push edx
|
|
lea edx,SegBase
|
|
push edx
|
|
lea edx,SegFlags
|
|
push edx
|
|
mov edi,[esi].RiTrapFrame
|
|
push [edi].TsHardwareSegSs
|
|
call VdmSegParams
|
|
add esp,010h
|
|
mov SelLookupResult,eax
|
|
|
|
do120: test [esi].RiPrefixFlags,PREFIX_SEG_ALL ; check for seg prefixes
|
|
jz do190 ; no prefixes, use default.
|
|
|
|
; Note: we could use a bsr instruction here, but it has a high
|
|
; overhead relative to a test and a jump, and I expect that
|
|
; es overrides will be by far the most common
|
|
mov edi,[esi].RiTrapFrame
|
|
test [esi].RiPrefixFlags,PREFIX_ES
|
|
jz do130
|
|
|
|
movzx edx,word ptr [edi].TsSegEs
|
|
jmp do180
|
|
|
|
do130: test [esi].RiPrefixFlags,PREFIX_CS
|
|
jz do140
|
|
|
|
movzx edx,word ptr [edi].TsSegCs
|
|
jmp do180
|
|
|
|
do140: test [esi].RiPrefixFlags,PREFIX_SS
|
|
jz do150
|
|
|
|
movzx edx,word ptr [edi].TsHardwareSegSs
|
|
jmp do180
|
|
|
|
do150: test [esi].RiPrefixFlags,PREFIX_DS
|
|
jz do160
|
|
|
|
movzx edx,word ptr [edi].TsSegDs
|
|
jmp do180
|
|
|
|
do160: test [esi].RiPrefixFlags,PREFIX_FS
|
|
jz do170
|
|
|
|
movzx edx,word ptr [edi].TsSegFs
|
|
jmp do180
|
|
|
|
do170: ; assert that seg gs bit is set
|
|
movzx edx,word ptr [edi].TsSegGs
|
|
|
|
;
|
|
; Get information on new default segment
|
|
;
|
|
do180: lea ecx,SegLimit
|
|
push ecx
|
|
lea ecx,SegBase
|
|
push ecx
|
|
lea ecx,SegFlags
|
|
push ecx
|
|
push edx
|
|
call VdmSegParams
|
|
add esp,010h
|
|
mov SelLookupResult,eax
|
|
|
|
test byte ptr SelLookupResult,0fh
|
|
jz do200 ; return error
|
|
|
|
cmp dword ptr ReadWrite,0
|
|
jnz do190 ; we can read all sels
|
|
|
|
test dword ptr SegFlags,SEL_TYPE_WRITE
|
|
jz do200 ; return error.
|
|
|
|
cmp ebx,SegLimit
|
|
jae do200 ; gp fault
|
|
|
|
do190: add ebx,SegBase
|
|
do194: mov [esi].RiOperand,ebx ; update op pointer
|
|
mov eax,1
|
|
do195: pop ebx
|
|
pop ecx
|
|
pop edi
|
|
mov esp,ebp
|
|
pop ebp
|
|
ret
|
|
|
|
do200: xor eax,eax
|
|
jmp do195
|
|
|
|
;
|
|
; Get the SIB if there is one, and save it for later.
|
|
;
|
|
do210: cmp ecx,MI_RMSIB
|
|
jne do215 ; no Sib, dispatch for displacement
|
|
|
|
call CheckEip
|
|
test al,0fh
|
|
jz do200 ; report GP fault
|
|
|
|
movzx eax,byte ptr [edi]
|
|
mov Sib,eax
|
|
inc edi
|
|
inc [esi].RiEip
|
|
do215: jmp modtab32[edx * 4]
|
|
|
|
do220:
|
|
;
|
|
; These forms have no displacement, except for the "bp" form, which
|
|
; is just a 32 bit immediate displacement
|
|
;
|
|
mov ebx,0 ; assume no displacement
|
|
cmp ecx,MI_RMBP
|
|
jne do270
|
|
|
|
call CheckEip
|
|
test al,0fh
|
|
jz do200 ; Gp fault, report error
|
|
|
|
mov ebx,[edi] ; get displacement
|
|
add [esi].RiEip,4 ; update eip
|
|
jmp do120 ; go add in seg
|
|
|
|
do240:
|
|
;
|
|
; These forms have an 8 bit displacement
|
|
;
|
|
call CheckEip
|
|
test al,0fh
|
|
jz do200 ; Gp fault, report error
|
|
|
|
movsx ebx,byte ptr [edi]
|
|
inc [esi].RiEip
|
|
jmp do270
|
|
|
|
do250:
|
|
;
|
|
; These forms have an 32 bit displacement
|
|
;
|
|
call CheckEip
|
|
test al,0fh
|
|
jz do200 ; Gp fault, report error
|
|
|
|
mov ebx, [edi]
|
|
add [esi].RiEip,4
|
|
jmp do270
|
|
|
|
do260:
|
|
;
|
|
; These forms are register operands
|
|
;
|
|
mov ebx,RegTab[ecx * 4] ; get offset into stackframe
|
|
add ebx,[esi].RiTrapFrame ; form linear address
|
|
jmp do195 ; return success
|
|
|
|
do270:
|
|
;
|
|
; Add in the RM portion of the effective address.
|
|
;
|
|
cmp ecx,MI_RMSIB
|
|
je do290 ; handle SIB specially
|
|
|
|
mov edi,[esi].RiTrapFrame
|
|
mov edx,RegTab[ecx * 4] ; get offset of register
|
|
add ebx,[edx+edi] ; add register to displacement
|
|
cmp ecx,MI_RMBP ; bp is base?
|
|
je do280 ; set up ss as default
|
|
|
|
jmp do120 ; get segment info.
|
|
|
|
do280:
|
|
;
|
|
; Change default segment to be SS
|
|
;
|
|
lea edx,SegLimit
|
|
push edx
|
|
lea edx,SegBase
|
|
push edx
|
|
lea edx,SegFlags
|
|
push edx
|
|
mov edi,[esi].RiTrapFrame
|
|
push [edi].TsHardwareSegSs
|
|
call VdmSegParams
|
|
add esp,010h
|
|
mov SelLookupResult,eax
|
|
jmp do120
|
|
do290:
|
|
;
|
|
; Decode the Sib
|
|
;
|
|
mov edx,Sib
|
|
mov edi,[esi].RiTrapFrame
|
|
and edx,MI_SIB_BASEMASK ; isolate base
|
|
cmp edx,MI_SIB_BASENONE ; no base
|
|
je do300
|
|
|
|
mov eax,RegTab[edx * 4]
|
|
add ebx,[edi+eax] ; get register contents, and add
|
|
|
|
do300: mov edx,Sib
|
|
and ecx,MI_SIB_INDEXMASK
|
|
shr ecx,MI_SIB_INDEXSHIFT ; make index out of "index" field
|
|
cmp ecx,MI_SIB_INDEXNONE
|
|
je do310 ; no index
|
|
|
|
mov eax,RegTab[ecx * 4]
|
|
mov eax,[eax+edi] ; get reg contents for multiply.
|
|
mov ecx,Sib
|
|
and ecx,MI_SIB_SSMASK
|
|
shr ecx,MI_SIB_SSSHIFT ; for shift count
|
|
shl eax,cl
|
|
add ebx,eax
|
|
|
|
do310: cmp edx,MI_SIB_BASENONE
|
|
jne do120
|
|
|
|
;
|
|
; If mod != 0, then we have to add in EBP, and make ss the default seg
|
|
;
|
|
mov edx,ModRm
|
|
and edx,MI_MODMASK
|
|
shr edx,MI_MODSHIFT
|
|
cmp edx,MI_MODNONE
|
|
jne do120
|
|
;
|
|
; Add in Ebp, and change default segment to ss
|
|
;
|
|
add ebx,[edi].TsEbp
|
|
|
|
lea edx,SegLimit
|
|
push edx
|
|
lea edx,SegBase
|
|
push edx
|
|
lea edx,SegFlags
|
|
push edx
|
|
mov edi,[esi].RiTrapFrame
|
|
push [edi].TsHardwareSegSs
|
|
call VdmSegParams
|
|
add esp,010h
|
|
mov SelLookupResult,eax
|
|
jmp do120 ; add in segment info
|
|
|
|
VdmDecodeOperand endp
|
|
|
|
public VdmSegParams
|
|
VdmSegParams proc
|
|
|
|
push edi
|
|
mov edi,[esi].RiTrapFrame
|
|
test dword ptr [edi].TsEFlags,EFLAGS_V86_MASK
|
|
jz vsp20
|
|
|
|
Segmt equ word ptr [ebp + 8]
|
|
SegFlags equ [ebp + 0Ch]
|
|
SegBase equ [ebp + 010h]
|
|
SegLimit equ [ebp + 014h]
|
|
|
|
pop edi
|
|
push ebp
|
|
mov ebp,esp
|
|
push edi
|
|
|
|
movzx eax,Segmt
|
|
shl eax,4
|
|
mov edi,SegBase
|
|
mov [edi],eax
|
|
mov edi,SegLimit
|
|
mov dword ptr [edi],0FFFFh
|
|
mov edi,SegFlags
|
|
mov [edi],dword ptr SEL_TYPE_WRITE
|
|
mov eax,1
|
|
|
|
pop edi
|
|
mov esp,ebp
|
|
pop ebp
|
|
ret
|
|
|
|
vsp20: pop edi
|
|
IFDEF STD_CALL
|
|
jmp _Ki386GetSelectorParameters@16
|
|
ELSE
|
|
jmp _Ki386GetSelectorParameters
|
|
ENDIF
|
|
|
|
VdmSegParams endp
|
|
_PAGE ENDS
|
|
end
|