|
|
; ; ; Copyright (C) Microsoft Corporation, 1987 ; ; This Module contains Proprietary Information of Microsoft ; Corporation and should be treated as Confidential. ; subttl em386.asm - Main Entry Point and Address Calculation Procedure page ;*********************************************************************; ; ; ; Main Entry Point and Address Calculation Procedure ; ; ; ; 80386 version ; ; ; ;*********************************************************************; ; ; This routine fetches the 8087 instruction, calculates memory address ; if necessary into ES:ESI and calls a routine to emulate the instruction. ; Most of the dispatching is done through tables. (see comments in CONST) ; ; The instruction dispatching is designed to favor the 386 addressing modes
ifdef XENIX
LDT_DATA= 02Fh ; emulator data LDT STACK_ALIAS= 027h ; 32 bit alias for stack selector
endif ;XENIX
;------------------------------------------------------------------------------ ; ; emulation entry point ; ;------------------------------------------------------------------------------
pub protemulation
cld ; clear direction flag forever
ifdef XENIX push ss ; save ss endif ;XENIX
push eax ; for common exit code (historical)
push ds ; save segment registers
ifdef XENIX
push eax ; UNDONE - slow
mov ax,LDT_DATA ; load up emulator's data segment mov ds,ax cmp [Einstall],0 ; check if emulator is initialized je installemulator ; no - go install it
pub protemcont
pop eax ; UNDONE - slow
endif ;XENIX
push es push ss ; save SegOvr (bp forces SS override)
push edi ; save registers push esi ; must be in this order for indexing push ebp push esp push ebx push edx push ecx push eax
ifdef XENIX
mov ax,ss ; check for 286 using user SS lar eax,ax ; load extended access bits test eax,00400000h ; test BIG bit jnz short prot386 ; 386 - ok
mov ax,STACK_ALIAS ; setup stack with 32 bit alias segment mov ss,ax movzx esp,sp ; clean up ESP ; mov word ptr [esp].regEIP+2,0 ; clean up EIP
pub prot386
endif ;XENIX
mov ebp,esp ; set up frame pointer add [ebp].regESP,regFlg-regESP ; adjust to original esp
mov eax,edi ; may use original DI to calc address lds edi,fword ptr [ebp].regEIP ; ds:edi = 287 instruction address mov cx,[edi] ; cx = esc 0-7 and opcode
cmp cl,09Bh ; UNDONE - check FWAIT's getting through je sawFWAIT ; UNDONE - and ignore it
add edi,2 ; point to displacement add cl,28h ; set carry if esc 0-7 (and cl = 0-7) jnc short protSegOvr ; no carry - must be segment override
mov es,[ebp].regDS ; es = user data segment mov edx,ebx ; may use original BX to calc address
pub CommonDispatch rol ch,2 ; rotate MOD field next to r/m field
; UNDONE ; UNDONE should check for instruction prefixes such as address size prefix ; UNDONE
lar ebx,[ebp].regCS ; check if 286 or 386 segment test ebx,00400000h ; mov bl,ch ; get copy of operation jz short Have286segment ; 286 segment - assume 286 addressing
and ebx,1FH ; Mask to MOD and r/m fields jmp EA386Tab[4*ebx]
pub Have286segment and ebx,1FH ; Mask to MOD and r/m fields jmp EA286Tab[4*ebx]
; protect mode Segment override case
glb <protSegOvrTab>
protSegOvrTab label word
dd DSSegOvr ; 11 dd ESSegOvr ; 00 dd CSSegOvr ; 01 dd SSSegOvr ; 10
pub protSegOvr mov edx,ebx ; may use original BX to calc 286 address mov bl,cl shr bl,1 and ebx,0Ch ; bl = (seg+1) and 0Ch inc edi ; point to displacement mov cx,[edi-2] ; cx = esc 0-7 and opcode jmp protSegOvrTab[ebx] ; process appropriate segment override
pub DSSegOvr ; 00 mov es,[ebp].regDS ; set ES to EA segment jmp short ESSegOvr
pub CSSegOvr ; 10 push ds ; DS = caller's CS pop es ; set ES to EA segment jmp short ESSegOvr
pub SSSegOvr ; 01 push ss ; SS = caller's SS pop es ; set ES to EA segment
pub ESSegOvr ; 11 mov [ebp].regSegOvr,es ; save for bp rel EAs jmp CommonDispatch
; 386 address modes
; SIB does not handle SS overrides for ebp
SIB macro modval local SIBindex,SIBbase
xor ebx,ebx mov bl,[edi] ; ebx = SIB field inc edi ; bump past SIB field mov eax,ebx and al,7 ; mask down to base register
if modval eq 0 cmp al,5 ; base = ebp jne short SIBbase ; yes - get base register value mov eax,[edi] ; eax = disp32 add edi,4 ; bump past displacement jmp short SIBindex endif
SIBbase: mov eax,[ebp+4*eax] ; eax = base register value
SIBindex: mov [ebp].regESP,0 ; no esp indexing allowed push ecx ; UNDONE - slow mov cl,bl shr cl,6 ; cl = scale factor shr bl,1 and bl,1Ch ; ebx = 4 * index register mov esi,[ebp+ebx] ; esi = index register value shl esi,cl ; esi = scaled index register value pop ecx ; UNDONE - slow add esi,eax ; esi = SIB address value endm
pub SIB00 SIB 00 ; decode SIB field jmp CommonMemory
pub SIB01 SIB 01 ; decode SIB field mov al,[edi] inc edi cbw ; ax = al cwde ; eax = ax add esi,eax jmp short CommonMemory
pub SIB10 SIB 10 ; decode SIB field mov eax,[edi] add edi,4 add esi,eax jmp short CommonMemory
; 386 single register addressing
pub Exx00 and bl,1Ch ; mask off mod bits mov esi,[ebp+ebx] jmp short CommonMemory
pub Exx01 and bl,1Ch ; mask off mod bits mov esi,[ebp+ebx] mov al,[edi] inc edi cbw ; ax = al cwde ; eax = ax add esi,eax jmp short CommonMemory
pub Exx10 and bl,1Ch ; mask off mod bits mov esi,[ebp+ebx] mov eax,[edi] add edi,4 add esi,eax jmp short CommonMemory
; 386 direct addressing
pub Direct386 mov esi,[edi] add edi,4
pub CommonMemory MOV [ebp].regEIP,edi ; final return offset mov ax,LDT_DATA mov ds,ax mov [CURerr],MemoryOperand ; clear current error, set mem. op bit
; At this point ES:SI = memory address, CX = |Op|r/m|MOD|escape|MF|Arith|
shr ch,4 ; Move Op field to BX for Table jump mov bl,ch and ebx,0EH
test cl,1 ; Arith field set? JZ short ArithmeticOpMem
pub NonArithOpMem CALL NonArithOpMemTab[2*ebx] ; is CH shl 4 needed? JMP EMLFINISH
pub ArithmeticOpMem PUSH ebx ; Save Op while we load the argument CALL eFLDsdri ; emulate proper load POP ebx
mov ax,ds ; ES = DS = task data area mov es,ax MOV esi,[CURstk] ; address top of stack MOV edi,esi ChangeDIfromTOStoNOS MOV [RESULT],edi ; Set up destination Pointer
JMP short DoArithmeticOpPop
pub NoEffectiveAddress ; Either Register op or Miscellaneous
MOV [ebp].regEIP,edi ; final return offset
xor eax,eax mov di,LDT_DATA mov ds,di mov es,di mov [CURerr],ax ; clear current error, memory op bit
; CX = |Op|r/m|MOD|escape|MF|Arith|
mov bl,ch shr bl,4 ; Mov Op field to BX for jump and ebx,0Eh
TEST CL,1 ; Arith field set? JZ short ArithmeticOpReg
pub NonArithOpReg CALL NonArithOpRegTab[2*ebx] JMP EMLFINISH
; For register arithmetic operations, one operand is always the stack top. ; The r/m field of the instruction is used to determine the address of ; the other operand (ST(0) - ST(7)) ; CX = xxxRRRxxxxxxxxxx (x is don't care, RRR is relative register # 0-7)
pub ArithmeticOpReg
call RegAddr ;di <= address of 2nd operand ;carry set if invalid register jc short InvalidOperand ;no, invalid operand, don't do operation
MOV [RESULT],esi ; Set destination to TOS TEST CL,04H ; Unless Dest bit is set JZ short DestIsSet ; in which case MOV [RESULT],edi ; Set destination to DI
pub DestIsSet ; Need to Toggle Reverse bit for DIV or SUB TEST BL,08H ; OP = 1xx for DIV and SUB; BX = |0000|OP|O| JZ short SetUpPop XOR BL,02H ; Toggle Reverse bit
pub SetUpPop TEST CL,02H JZ short DoArithmeticOpNoPop
pub DoArithmeticOpPop CALL ArithmeticOpTab[2*ebx]
POPST JMP short EMLFINISH
pub DoArithmeticOpNoPop CALL ArithmeticOpTab[2*ebx] JMP short EMLFINISH
;*** InvalidOperand - register operand does not exist ; ; RETURNS ; sets Stack Underflow and Invalid bits in [CURerr] ; ; DESCRIPTION ; a reference was made to a register that does not ; exist on the stack. Set error bits and exit.
pub InvalidOperand
call UnderStk ;indicate stack underflow error or [CURerr],Invalid ;indicate invalid operand jmp short EMLFINISH ;don't execute instruction
;*** RegAddr - compute register address ; ; ARGUMENTS ; CX = |Op|r/m|MOD|esc|MF|Arith| ; r/m = register whose address is to be computed ; ; RETURNS ; SI = address of top of stack ; DI = address of requested register ; PSW.C set if register is not valid ; PSW.C reset if register is valid ; ; DESCRIPTION ; multiply register number by 12 and subtract this from ; [CURstk] (the address of TOS) to compute address of ; register referenced by r/m. ; ; REGISTERS ; modifies dx
pub RegAddr mov esi,[CURstk] ; address top of stack mov edi,esi
;set up address of 2nd operand based on r/m field of instruction
xor edx,edx mov dl,ch ; dl <== byte containing reg# and dl,01ch ; mask all but r/m field
; Since r/m field contains the relative reg # in bits 2-4 of dl, ; and bits 0-1 of dl are zero, dl now contains 4*(reg #). To compute ; the memory location of this register, calculate 12*(reg #) and ; subtract this from di, which contains the address of the TOS. reg # ; is multiplied by 12 because that is the number of bytes in each stack ; entry.
lea edx,[2*edx+edx] ; edx = 3 * (4 * reg #) sub edi,edx ; di is address of second operand cmp edi,[BASstk] ; is register in range? clc ; assume valid register jg short RAclc ; valid - skip next instruction cmc ; set carry to indicate invalid register pub RAclc ret
pub CallUnused CALL UNUSED ; Treat as unimpleminted jmp short EMLFINISH
; sawFWAIT - UNDONE - workaround for a 386 bug
pub sawFWAIT inc edi ; bump past FWAIT MOV [ebp].regEIP,edi ; final return offset xor eax,eax mov di,LDT_DATA mov ds,di mov [CURerr],ax ; clear current error, memory op bit
; return from routine; restore registers and return
pub EMLFINISH pop eax pop ecx pop edx pop ebx add esp,4 ; toss esp value pop ebp pop esi pop edi add esp,4 ; toss regSegOvr
; check for errors
MOV AX,[CURerr] ; fetch errors or [UserStatusWord],ax ; save all exception errors OR [SWerr],AL ; set errors in sticky error flag NOT AL ; make a zero mean an error MOV AH,byte ptr [UserControlWord] ; get user's IEEE control word OR AH,0C2H ; mask reserved, IEM and denormal bits AND AH,03FH ; unmask invalid instruction, ; stack overflow. OR AL,AH ; mask for IEEE exceptions NOT AL ; make a one mean an error MOV AH,byte ptr (CURerr+1) ; get stack over/underflow flags TEST AX,0FFFFh-MemoryOperand ; test for errors to report
pop es pop ds
jnz short ExceptionsEmulator ; goto error handler
pub errret error_return noerror ; common exit sequence
pub ExceptionsEmulator JMP CommonExceptions
;------------------------------------------------------------------------------ ; ; 286 address modes (for XENIX only) ; ;------------------------------------------------------------------------------
ifdef XENIX
; In the address calculations below: ; DX has BX original value ; AX has DI original value ; SI has SI original value ; BP has BP original value ; [EDI] is address of displacement bytes
pub BXXI0D MOV eax,edx ; use original BX index value pub DSDI0D MOV esi,eax ; use alternate index value pub DSSI0D JMP short ADRFIN ; have offset in SI
pub BPXI1D XOR eax,eax ; no index register pub BPDI1D MOV esi,eax ; use alternate index value pub BPSI1D ADD esi,[ebp].regEBP ; add original BP value mov es,[ebp].regSegOvr ; ES = override segment (or SS if none) JMP short DSSI1D ; go get one byte displacement
pub BXSI1D MOV eax,esi ; really will want SI, not DI pub BXDI1D ADD edx,eax ; now DX is original BX plus index pub BXXI1D MOV eax,edx ; use original BX index value pub DSDI1D MOV esi,eax ; use alternate index value pub DSSI1D MOV AL,[edi] ; get one byte displacement CBW ; sign extend displacement INC edi ; get past displacement byte JMP short DISPAD ; go add AX to SI (time w/ ADD)
pub BPXI2D XOR eax,eax ; no index register pub BPDI2D MOV esi,eax ; use alternate index value pub BPSI2D ADD esi,[ebp].regEBP ; add original BP value mov es,[ebp].regSegOvr ; ES = override segment (or SS if none) JMP short DSSI2D ; go get two byte displacement
pub BXSI2D MOV eax,esi ; really will want SI, not DI pub BXDI2D ADD edx,eax ; now DX is original BX plus index pub BXXI2D MOV eax,edx ; use original BX index value pub DSDI2D MOV esi,eax ; use alternate index value pub DSSI2D MOV AX,[edi] ; get two byte displacement INC edi ; get past displacement byte INC edi ; get past displacement byte JMP short DISPAD ; go add AX to SI (time w/ ADD)
pub DSXI2D MOV SI,[edi] ; get two byte displacement INC edi ; get past displacement byte INC edi ; get past displacement byte JMP short ADRFIN ; have offset in AX
pub BPSI0D MOV eax,esi ; really will want SI, not DI pub BPDI0D MOV edx,[ebp].regEBP ; really will want BP, not BX mov es,[ebp].regSegOvr ; ES = override segment (or SS if none) pub BXDI0D MOV esi,eax ; use alternate index value pub BXSI0D MOV eax,edx ; use original BX (or BP) as base
pub DISPAD ADD esi,eax ; add original index value
pub ADRFIN movzx esi,si ; ES:ESI = user memory address jmp CommonMemory
endif ;XENIX
|