windows-nt/Source/XPSP1/NT/base/mvdm/wow16/win87em/em386.asm
2020-09-26 16:20:57 +08:00

568 lines
13 KiB
NASM

;
;
; 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