554 lines
17 KiB
NASM
554 lines
17 KiB
NASM
|
subttl emf386.asm - 32 bit Emulator Interrupt Handler
|
||
|
page
|
||
|
;***
|
||
|
;emf386.asm - 32 bit Emulator Interrupt Handler
|
||
|
;
|
||
|
; IBM/Microsoft Confidential
|
||
|
;
|
||
|
; Copyright (c) IBM Corporation 1987, 1989
|
||
|
; Copyright (c) Microsoft Corporation 1987, 1989
|
||
|
;
|
||
|
; All Rights Reserved
|
||
|
;
|
||
|
;Purpose:
|
||
|
; 32 bit Emulator Interrupt Handler
|
||
|
;
|
||
|
;Revision History: (also see emulator.hst)
|
||
|
;
|
||
|
; 1/21/92 JWM Minor modifications for DOSX32 emulator
|
||
|
; 8/23/91 TP Reduce to only two decoding steps
|
||
|
;
|
||
|
;*******************************************************************************
|
||
|
|
||
|
|
||
|
;*********************************************************************;
|
||
|
; ;
|
||
|
; 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 _DOS32EXT ; JWM
|
||
|
public __astart
|
||
|
__astart:
|
||
|
mov eax, 1
|
||
|
ret
|
||
|
|
||
|
public _Ms32KrnlHandler
|
||
|
_Ms32KrnlHandler:
|
||
|
endif
|
||
|
|
||
|
ifdef NT386
|
||
|
|
||
|
;
|
||
|
; NPXEmulatorTable is a table read by the Windows/NT kernel in
|
||
|
; order to support the R3 emulator
|
||
|
;
|
||
|
public _NPXEMULATORTABLE
|
||
|
_NPXEMULATORTABLE label dword
|
||
|
dd offset NpxNpHandler ; Address of Ring3 Trap7 handler
|
||
|
dd offset tRoundMode ; Address of rounding vector table
|
||
|
endif
|
||
|
|
||
|
public NPXNPHandler
|
||
|
NPXNPHandler:
|
||
|
|
||
|
ifdef DEBUG
|
||
|
int 3
|
||
|
endif
|
||
|
cld ; clear direction flag forever
|
||
|
|
||
|
ifdef NT386
|
||
|
|
||
|
|
||
|
;-- bryanwi - 16Oct91 - Hack FP fix, not pointing IDT:7 at this
|
||
|
; routine for 16bit code is the right thing to do.
|
||
|
;
|
||
|
; Check to see if we are running on flat SS. If so, assume things
|
||
|
; are OK and proceed. (If a 16bit app loads the flat SS and then
|
||
|
; does an FP instruction, they're hosed, no skin off our nose.)
|
||
|
;
|
||
|
; If SS not what we expect, then either (a) a flat apps is *very*
|
||
|
; confused, or (b) a 16 bit app has hit an FP instuction. In either
|
||
|
; case, this emulator is not going to work. Therefore, raise an exception.
|
||
|
;
|
||
|
|
||
|
push ax ; use form that will word with any SS
|
||
|
mov ax,ss
|
||
|
or ax,RPL_MASK
|
||
|
cmp ax,(KGDT_R3_DATA OR RPL_MASK)
|
||
|
pop ax
|
||
|
jz OK_Segment ; Segments are OK, proceed normally.
|
||
|
|
||
|
jmp Around
|
||
|
|
||
|
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
|
||
|
|
||
|
align 4
|
||
|
|
||
|
EmerStk db 1024 dup (?) ; *** SaveContext is assumed to be
|
||
|
SaveContext db size ContextFrameLength dup (?) ; *** at the top of the EmerStk by
|
||
|
SaveException db size ExceptionRecordLength dup (?) ; *** the function @ 13f:0
|
||
|
|
||
|
_DATA ENDS
|
||
|
|
||
|
Around:
|
||
|
;
|
||
|
; Trap occured in 16bit code, get to flat environment and raise exception
|
||
|
;
|
||
|
|
||
|
push eax ; save EAX on old stack
|
||
|
mov ax, ds
|
||
|
push eax ; Save DS on old stack
|
||
|
|
||
|
mov ax,(KGDT_R3_DATA OR RPL_MASK)
|
||
|
mov ds,ax
|
||
|
ASSUME DS:FLAT
|
||
|
|
||
|
pop dword ptr [SaveContext.CsSegDs] ; remove ds from old stack
|
||
|
pop dword ptr [SaveContext.CsEax] ; remove eax from old stack
|
||
|
pop dword ptr [SaveContext.CsEip] ; copy eip from old stack
|
||
|
pop dword ptr [SaveContext.CsSegCs] ; copy cs from old stack
|
||
|
pop dword ptr [SaveContext.CsEflags] ; copy eflag from old stack
|
||
|
|
||
|
push dword ptr [SaveContext.CsEFlags] ; restore eflag to old stack
|
||
|
push dword ptr [SaveContext.CsSegCs] ; restore cs to old stack
|
||
|
push dword ptr [SaveContext.CsEip] ; restore eip to old stack
|
||
|
mov dword ptr [SaveContext.CsEsp], esp
|
||
|
|
||
|
;
|
||
|
; Build rest of context frame
|
||
|
;
|
||
|
|
||
|
mov dword ptr [SaveContext.CsContextFlags],CONTEXT_CONTROL OR CONTEXT_SEGMENTS OR CONTEXT_INTEGER
|
||
|
mov dword ptr [SaveContext.CsEbx], ebx
|
||
|
mov dword ptr [SaveContext.CsEcx], ecx
|
||
|
mov dword ptr [SaveContext.CsEdx], edx
|
||
|
mov dword ptr [SaveContext.CsEsi], esi
|
||
|
mov dword ptr [SaveContext.CsEdi], edi
|
||
|
mov dword ptr [SaveContext.CsEbp], ebp
|
||
|
mov dword ptr [SaveContext.CsSegEs], es
|
||
|
mov dword ptr [SaveContext.CsSegFs], fs
|
||
|
mov dword ptr [SaveContext.CsSegGs], gs
|
||
|
mov dword ptr [SaveContext.CsSegSs], ss
|
||
|
|
||
|
mov ss,ax ; Switch to new stack
|
||
|
mov esp,(OFFSET FLAT:EmerStk) + 1024
|
||
|
ASSUME SS:FLAT
|
||
|
|
||
|
;
|
||
|
; ss: flat, esp -> EmerStk
|
||
|
;
|
||
|
|
||
|
mov ax,KGDT_R3_TEB OR RPL_MASK
|
||
|
mov fs, ax
|
||
|
mov ecx, fs:[TbVdm]
|
||
|
or ecx, ecx
|
||
|
jne short DoVdmFault
|
||
|
|
||
|
mov ecx, offset SaveContext ; (ecx) -> context record
|
||
|
mov edx, offset SaveException ; (edx) -> exception record
|
||
|
|
||
|
mov dword ptr [edx.ErExceptionCode],STATUS_ILLEGAL_FLOAT_CONTEXT
|
||
|
mov dword ptr [edx.ErExceptionFlags],0
|
||
|
mov dword ptr [edx.ErExceptionRecord],0
|
||
|
mov ebx, [ecx.CsEip]
|
||
|
mov [edx.ErExceptionAddress],ebx
|
||
|
mov [edx.ErNumberParameters],0
|
||
|
|
||
|
;
|
||
|
; ZwRaiseException(edx=ExceptionRecord, ecx=ContextRecord, TRUE=FirstChance)
|
||
|
;
|
||
|
|
||
|
stdCall _ZwRaiseException, <edx, ecx, 1>
|
||
|
|
||
|
;
|
||
|
; If we come back HERE, things are hosed. We cannot bugcheck because
|
||
|
; we are in user space, so int-3 and loop forever instead.
|
||
|
;
|
||
|
|
||
|
Forever:
|
||
|
int 3
|
||
|
jmp short Forever
|
||
|
|
||
|
DoVdmFault:
|
||
|
;
|
||
|
; Does the VDM want the fault, or should the instruction be skipped
|
||
|
;
|
||
|
test ds:[ecx].VtVdmContext.CsFloatSave.FpCr0NpxState, CR0_EM
|
||
|
jz short SkipNpxInstruction
|
||
|
|
||
|
add dword ptr [SaveContext.CsEsp], 12 ; remove from old stack
|
||
|
|
||
|
; jump to the dos extender NPX exception handler
|
||
|
|
||
|
; jmp far ptr 013fh:0
|
||
|
db 0eah
|
||
|
dd 0
|
||
|
dw 013fh
|
||
|
|
||
|
SkipNpxInstruction:
|
||
|
mov ax,(KGDT_R3_DATA OR RPL_MASK)
|
||
|
mov es,ax
|
||
|
|
||
|
stdCall _NpxNpSkipInstruction, <offset SaveContext>
|
||
|
|
||
|
mov ebx, dword ptr [SaveContext.CsEbx]
|
||
|
mov ecx, dword ptr [SaveContext.CsEcx]
|
||
|
mov edx, dword ptr [SaveContext.CsEdx]
|
||
|
mov edi, dword ptr [SaveContext.CsEdi]
|
||
|
mov esi, dword ptr [SaveContext.CsEsi]
|
||
|
mov ebp, dword ptr [SaveContext.CsEbp]
|
||
|
mov gs, dword ptr [SaveContext.CsSegGs]
|
||
|
mov fs, dword ptr [SaveContext.CsSegFs]
|
||
|
mov es, dword ptr [SaveContext.CsSegEs]
|
||
|
|
||
|
mov eax, dword ptr [SaveContext.CsEsp]
|
||
|
mov ss, dword ptr [SaveContext.CsSegSs] ; switch to original stack
|
||
|
mov esp, eax
|
||
|
|
||
|
add esp, 12 ; remove eflag, cs, eip
|
||
|
push dword ptr [SaveContext.CsEflags]
|
||
|
push dword ptr [SaveContext.CsSegCs]
|
||
|
push dword ptr [SaveContext.CsEip]
|
||
|
mov eax, dword ptr [SaveContext.CsEax]
|
||
|
mov ds, dword ptr [SaveContext.CsSegDs]
|
||
|
|
||
|
iretd ; restore eflag, cs, eip
|
||
|
|
||
|
OK_Segment:
|
||
|
endif
|
||
|
|
||
|
|
||
|
push ds ; save segment registers
|
||
|
|
||
|
GetEmData ds
|
||
|
|
||
|
push EMSEG:[LongStatusWord] ;In case we're saving status
|
||
|
push EMSEG:[PrevCodeOff] ;In case we save environment
|
||
|
;Save registers in order of their index number
|
||
|
push edi
|
||
|
push esi
|
||
|
push ebp
|
||
|
push esp
|
||
|
add dword ptr [esp],regFlg-regESP ; adjust to original esp
|
||
|
push ebx
|
||
|
push edx
|
||
|
push ecx
|
||
|
push eax
|
||
|
|
||
|
cmp EMSEG:[Einstall], 0 ; Make sure emulator is initialized.
|
||
|
je InstalEm
|
||
|
|
||
|
EmInstalled:
|
||
|
mov edi,[esp].regEIP ;edi = 387 instruction address
|
||
|
movzx edx, word ptr cseg:[edi] ;dx = esc and opcode
|
||
|
|
||
|
; Check for unmasked errors
|
||
|
mov al, EMSEG:[CURerr] ; fetch errors
|
||
|
and al, EMSEG:[ErrMask]
|
||
|
jnz short PossibleException
|
||
|
|
||
|
; UNDONE: rip test for FWAIT in final version
|
||
|
cmp dl, 9bh ;FWAIT?
|
||
|
je sawFWAIT
|
||
|
|
||
|
NoException:
|
||
|
Execute387inst:
|
||
|
;Enter here if look-ahead found another 387 instruction
|
||
|
mov EMSEG:[PrevCodeOff],edi
|
||
|
mov EMSEG:[CurErrCond],0 ;clear error and cond. codes, show busy
|
||
|
add edi, 2 ; point past opcode
|
||
|
|
||
|
;CONSIDER: remove the two instruction below and muck with EA386Tab
|
||
|
;CONSIDER: to optimize for mem ops instead of reg ops.
|
||
|
add dh,40h ; No effective address?
|
||
|
jc NoEffectiveAddress0 ; yes, go do instruction
|
||
|
rol dh,2 ; rotate MOD field next to r/m field
|
||
|
mov bl,dh
|
||
|
and ebx,1FH ; Mask to MOD and r/m fields
|
||
|
MemModeDispatch: ;Label for debugging
|
||
|
jmp EA386Tab[4*ebx]
|
||
|
|
||
|
|
||
|
InstalEm:
|
||
|
call EmulatorInit
|
||
|
mov edi,DefaultControlWord ; Default mode to start in
|
||
|
mov eax, edi
|
||
|
call SetControlWord ; Set it
|
||
|
mov EMSEG:[LongControlWord], edi ; reset reserved bits
|
||
|
jmp EmInstalled
|
||
|
|
||
|
; ************************
|
||
|
|
||
|
;
|
||
|
; We are about to execute a new FP instruction and there is an
|
||
|
; unmasked expcetion. Check to see if the new FP instruction is
|
||
|
; a "no wait" instruction. If so, let it proceede; otherwise, raise
|
||
|
; the exception.
|
||
|
;
|
||
|
|
||
|
PossibleException:
|
||
|
cmp edx, 0E3DBh ; if fninit, no exception
|
||
|
je short NoException
|
||
|
|
||
|
cmp edx, 0E2DBh ; if fnclex, no exception
|
||
|
je short NoException
|
||
|
|
||
|
cmp edx, 0E0DFh ; if "fnstsw ax", no exception
|
||
|
je short NoException
|
||
|
|
||
|
cmp dl, 0D9h ; possible encoding for fnstenv or fnstcw?
|
||
|
je short pe20 ; yes, check mod r/m
|
||
|
cmp dl, 0DDh ; possible encoding for fnsave or fnstsw?
|
||
|
jne short pe30
|
||
|
|
||
|
pe20: mov bl, dh ; bl = op2
|
||
|
shr bl, 3
|
||
|
and bl, 7 ; bl = mod r/m
|
||
|
cmp bl, 6 ; is it a 6 or 7?
|
||
|
jnc short NoException ; yes, no exception
|
||
|
|
||
|
pe30:
|
||
|
jmp CommonExceptions ; unmasked exception is pending, raise it
|
||
|
|
||
|
; ************************
|
||
|
|
||
|
|
||
|
|
||
|
; 386 address modes
|
||
|
|
||
|
; SIB does not handle SS overrides for ebp
|
||
|
|
||
|
SIB macro modval
|
||
|
local SIBindex,SIBbase
|
||
|
|
||
|
movzx ebx,byte ptr cseg:[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,cseg:[edi] ; eax = disp32
|
||
|
add edi,4 ; bump past displacement
|
||
|
jmp SIBindex ; Added to support bbt,
|
||
|
; replacing SKIP 3,SIBindex
|
||
|
endif
|
||
|
|
||
|
SIBbase:
|
||
|
mov eax,[esp+4*eax] ; eax = base register value
|
||
|
|
||
|
SIBindex:
|
||
|
mov [esp].regESP,0 ; no esp indexing allowed
|
||
|
mov cl,bl
|
||
|
shr cl,6 ; cl = scale factor
|
||
|
and bl,7 shl 3 ; ebx = 8 * index register
|
||
|
shr bl,1
|
||
|
mov esi,[esp+1*ebx] ; esi = index register value
|
||
|
shl esi,cl ; esi = scaled index register value
|
||
|
add esi,eax ; esi = SIB address value
|
||
|
endm
|
||
|
|
||
|
|
||
|
ALIGN 4
|
||
|
|
||
|
SIB00:
|
||
|
SIB 00 ; decode SIB field
|
||
|
jmp CommonMemory
|
||
|
|
||
|
ALIGN 4
|
||
|
|
||
|
SIB01:
|
||
|
SIB 01 ; decode SIB field
|
||
|
movsx eax,byte ptr cseg:[edi]
|
||
|
inc edi
|
||
|
add esi,eax
|
||
|
jmp short CommonMemory
|
||
|
|
||
|
ALIGN 4
|
||
|
|
||
|
SIB10:
|
||
|
SIB 10 ; decode SIB field
|
||
|
mov eax,cseg:[edi]
|
||
|
add edi,4
|
||
|
add esi,eax
|
||
|
jmp short CommonMemory
|
||
|
|
||
|
|
||
|
; 386 single register addressing
|
||
|
|
||
|
ALIGN 4
|
||
|
|
||
|
Exx00:
|
||
|
and bl,7 shl 2 ; mask off mod bits
|
||
|
mov esi,[esp+1*ebx]
|
||
|
jmp short CommonMemory
|
||
|
|
||
|
ALIGN 4
|
||
|
|
||
|
Exx01:
|
||
|
and bl,7 shl 2 ; mask off mod bits
|
||
|
mov esi,[esp+1*ebx]
|
||
|
movsx eax,byte ptr cseg:[edi]
|
||
|
inc edi
|
||
|
add esi,eax
|
||
|
jmp short CommonMemory
|
||
|
|
||
|
ALIGN 4
|
||
|
|
||
|
Exx10:
|
||
|
and bl,7 shl 2 ; mask off mod bits
|
||
|
mov esi,[esp+1*ebx]
|
||
|
add esi,cseg:[edi]
|
||
|
add edi,4
|
||
|
jmp short CommonMemory
|
||
|
|
||
|
|
||
|
; 386 direct addressing
|
||
|
|
||
|
ALIGN 4
|
||
|
|
||
|
Direct386:
|
||
|
mov esi,cseg:[edi]
|
||
|
add edi,4
|
||
|
|
||
|
CommonMemory:
|
||
|
MOV [esp].regEIP,edi ; final return offset
|
||
|
|
||
|
|
||
|
; At this point ESI = memory address, dx = |Op|r/m|MOD|escape|MF|Arith|
|
||
|
; Current format of opcode and address mode bytes (after rol dh,2)
|
||
|
;
|
||
|
; 7 6 5 4 3 2 1 0
|
||
|
; |1 1 0 1 1| op1 | dl
|
||
|
;
|
||
|
; 7 6 5 4 3 2 1 0
|
||
|
; | op2 | r/m |mod| dh
|
||
|
;
|
||
|
;op1 and op2 fields together make the FP opcode
|
||
|
|
||
|
rol dx,5 ; dl = | op1 | op2 |? ?|
|
||
|
and edx,0FCH ;Keep only op1 & op2 bits
|
||
|
push offset EMLFINISH
|
||
|
mov edi,EMSEG:[CURstk]
|
||
|
MemOpDisp: ;Debugging label
|
||
|
;edi = [CURstk]
|
||
|
jmp tOpMemDisp[edx]
|
||
|
|
||
|
|
||
|
ALIGN 4
|
||
|
|
||
|
|
||
|
NoEffectiveAddress0:
|
||
|
rol dh,2
|
||
|
NoEffectiveAddress: ; Either Register op or Miscellaneous
|
||
|
mov [esp].regEIP,edi ; final return offset
|
||
|
|
||
|
;Current format of opcode and address mode bytes (after rol dh,2)
|
||
|
;
|
||
|
; 7 6 5 4 3 2 1 0
|
||
|
; |1 1 0 1 1| op1 | dl
|
||
|
;
|
||
|
; 7 6 5 4 3 2 1 0
|
||
|
; | op2 | r/m |mod| dh
|
||
|
;
|
||
|
;op1 and op2 fields together make the FP opcode
|
||
|
|
||
|
mov al,dh ;Save r/m bits (contains reg. no.)
|
||
|
rol dx,5 ; dl = | op1 | op2 |? ?|
|
||
|
and edx,0FCH ;Keep only op1 & op2 bits
|
||
|
push offset EMLFINISH
|
||
|
and eax,7 shl 2 ;Mask to register number * 4
|
||
|
mov edi,EMSEG:[CURstk]
|
||
|
lea esi,[2*eax+eax] ;Register no. * 12
|
||
|
add esi,edi
|
||
|
cmp esi,ENDstk ;Run past end?
|
||
|
jae RegWrap
|
||
|
RegOpDisp: ;Debugging label
|
||
|
;eax = r/m bits * 4
|
||
|
;esi = FP register address
|
||
|
;edi = [CURstk]
|
||
|
jmp tOpRegDisp[edx]
|
||
|
|
||
|
ALIGN 4
|
||
|
RegWrap:
|
||
|
sub esi,ENDstk - BEGstk ;Wrap around JWM
|
||
|
RegOpDispWrap: ;Debugging label
|
||
|
jmp tOpRegDisp[edx]
|
||
|
|
||
|
|
||
|
SawFwait:
|
||
|
inc edi ; bump past FWAIT
|
||
|
mov [esp].regEIP,edi ; final return offset
|
||
|
mov EMSEG:[CURErr],0 ; clear current error and cond. codes
|
||
|
|
||
|
; return from routine; restore registers and return
|
||
|
|
||
|
align 4
|
||
|
EMLFINISH:
|
||
|
; check for errors
|
||
|
mov al, EMSEG:[CURerr] ; fetch errors
|
||
|
or al, EMSEG:[SWerr]
|
||
|
mov EMSEG:[SWerr],al ; set errors in sticky error flag
|
||
|
and al,EMSEG:[ErrMask]
|
||
|
jnz CommonExceptions
|
||
|
|
||
|
ifdef TRACENPX
|
||
|
jmp CommonExceptions
|
||
|
endif
|
||
|
|
||
|
if DBG eq 0
|
||
|
|
||
|
;
|
||
|
; On a free build, look ahead to next instruction
|
||
|
;
|
||
|
|
||
|
;09BH is FWAIT - just skip it
|
||
|
;0D8H - 0DFH is 387 instruction, emulate it
|
||
|
mov edi,[esp].regEIP ;edi = 387 instruction address
|
||
|
mov dx,cseg:[edi]
|
||
|
cmp dl,09BH ;FWAIT?
|
||
|
jz short SawFwait
|
||
|
sub dl,0D8H
|
||
|
cmp dl,8
|
||
|
jb ReExecute
|
||
|
endif
|
||
|
mov ebx,[esp].[OldLongStatus]
|
||
|
and ebx,LongSavedFlags ;preserve condition codes, error flags
|
||
|
or EMSEG:[LongStatusWord],ebx ;merge saved status word, condition codes
|
||
|
|
||
|
pop eax
|
||
|
pop ecx
|
||
|
pop edx
|
||
|
pop ebx
|
||
|
add esp,4 ; toss esp value
|
||
|
pop ebp
|
||
|
pop esi
|
||
|
pop edi
|
||
|
add esp,8 ;toss old PrevCodeOff and StatusWord
|
||
|
mov EMSEG:[CURerr],Summary ;Indicate we are not busy
|
||
|
pop ds
|
||
|
error_return ; common exit sequence
|
||
|
|
||
|
ReExecute:
|
||
|
mov eax,EMSEG:[LongStatusWord]
|
||
|
mov ebx,[esp].[OldLongStatus]
|
||
|
and ebx,LongSavedFlags ;preserve condition codes, error flags
|
||
|
or eax,ebx ;merge saved status word, condition codes
|
||
|
mov [esp].OldLongStatus,eax
|
||
|
mov eax,EMSEG:[PrevCodeOff]
|
||
|
mov [esp].OldCodeOff,eax
|
||
|
lea eax,[esp+regFlg+4] ;must restore "saved" esp
|
||
|
mov [esp].RegEsp,eax
|
||
|
jmp Execute387inst
|