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 jmp _Ki386GetSelectorParameters@16 VdmSegParams endp _PAGE ENDS end