windows-nt/Source/XPSP1/NT/base/mvdm/wow16/win87em/emmain.asm

806 lines
17 KiB
NASM
Raw Normal View History

2020-09-26 03:20:57 -05:00
page ,132
subttl emmain.asm - Main Entry Point and Address Calculation Procedure
;***
;emmain.asm - Main Entry Point and Address Calculation Procedure
;
; Copyright (c) 1987-89, Microsoft Corporation
;
;Purpose:
; Main Entry Point and Address Calculation Procedure
;
; This Module contains Proprietary Information of Microsoft
; Corporation and should be treated as Confidential.
;
;Revision History:
; See emulator.hst
;
;*******************************************************************************
;*********************************************************************;
; ;
; Main Entry Point and Address Calculation Procedure ;
; ;
;*********************************************************************;
;
; This routine fetches the 8087 instruction, calculates memory address
; if necessary into ES:SI and calls a routine to emulate the instruction.
; Most of the dispatching is done through tables. (see comments in CONST)
ProfBegin MAIN
ifdef XENIX
ifdef i386
LDT_DATA= 02Fh ; UNDONE - 386 emulator data LDT
else
LDT_DATA= 037h ; UNDONE - 286 emulator data LDT
endif ;i386
endif ;XENIX
ifdef PROTECT
; protect mode Segment override case
glb <protSegOvrTab>
protSegOvrTab label word
dw DSSegOvr ; 11
dw ESSegOvr ; 00
dw CSSegOvr ; 01
dw SSSegOvr ; 10
endif ;PROTECT
ifdef DOS3
; isolated FWAIT
ifdef WINDOWS
pub FWtrap
cld ; this CLD is a nop.
iret
else ;not WINDOWS
pub FWtrap
PUSH BP ; fix up isolated FWAIT
PUSH DS
PUSH SI
MOV BP,SP ; Point to stack
LDS SI,DWORD PTR 6[BP] ; Fetch ret address,(points to after instruction)
DEC SI ; Make DI point to instruction
DEC SI
MOV 6[BP],SI ; Change ret address to return to instruction
mov word ptr [si],0C0h*256+89h ; change interrupt to mov ax,ax
POP SI
POP DS
POP BP
IRET ; interrupt return
endif ;not WINDOWS
; Segment override case
glb <SegOvrTab>
SegOvrTab label word
dw DSSegOvr
dw SSSegOvr
dw CSSegOvr
dw ESSegOvr
pub SOtrap
STI ; re-enable interrupts
push ax
push es ; set up frame
push ds
push di
push si
push dx
push cx
push bx
sub sp,2 ; reserve for regSegOvr
push bp
mov bp,sp ; set up frame pointer
CLD ; clear direction flag forever
; get address mode information, dispatch to address calculation
MOV DX,BX ; may use original BX to calc address
MOV AX,DI ; may use original DI to calc address
LDS DI,dword ptr [bp].regIP ; DS:DI is caller's CS:IP
INC DI ; increment past operation byte
INC DI ; increment past operation byte
MOV CX,[DI-2] ; get trap number, opcode (DS=caller CS)
mov bx,cx ; upper 2 bits indicate segment override
rol bl,1
rol bl,1
and bx,3
rol bx,1
jmp SegOvrTab[bx]
endif ;DOS3
pub DSSegOvr ; 00
mov es,[bp].regDS ; set ES to EA segment
jmp short ESSegOvr
pub CSSegOvr ; 10
mov bx,ds ; DS = caller's CS
mov es,bx ; set ES to EA segment
jmp short ESSegOvr
pub SSSegOvr ; 01
mov bx,ss ; SS = caller's SS
mov es,bx ; set ES to EA segment
pub ESSegOvr ; 11
mov [bp].regSegOvr,es ; save for bp rel EAs
jmp short CommonDispatch
ifdef PROTECT
pub protSegOvr
mov dx,bx ; may use original BX to calc address
mov bl,cl
.286
shr bl,2
ifndef DOS5only
.8086
endif
and bx,6 ; bl = (seg+1) and 6
inc di ; point to displacement
mov cx,[di-2] ; cx = esc 0-7 and opcode
jmp protSegOvrTab[bx] ; process appropriate segment override
ifdef XENIX
pub jinstall
jmp installemulator
endif ;XENIX
pub protemulation
cld ; clear direction flag forever
ifdef XENIX
push ax ; UNDONE - slow
push ds ; UNDONE - slow
mov ax,LDT_DATA ; load up emulator's data segment
mov ds,ax
cmp [Einstall],0 ; check if emulator is initialized
je jinstall ; no - go install it
pub protemcont
pop ds ; UNDONE - slow
pop ax ; UNDONE - slow
endif ;XENIX
push ax
push es ; set up frame
push ds
push di
push si
push dx
push cx
push bx
push ss ; save SegOvr (bp forces SS override)
push bp
mov bp,sp ; set up frame pointer
mov dx,ds ; save original DS for default case
mov ax,di ; may use original DI to calc address
lds di,dword ptr [bp].regIP ; ds:di = 287 instruction address
mov cx,[di] ; cx = esc 0-7 and opcode
add di,2 ; point to displacement
add cl,28h ; set carry if esc 0-7 (and cl = 0-7)
jnc protSegOvr ; no carry - must be segment override
mov es,dx ; es = user data segment
mov dx,bx ; may use original BX to calc address
endif ;PROTECT
ifdef DOS3and5
jmp short CommonDispatch
endif ;DOS3and5
ifdef DOS3
; normal entry point for emulator interrupts
even
pub DStrap
STI ; re-enable interrupts
push ax
push es ; set up frame
push ds
push di
push si
push dx
push cx
push bx
push ss ; save SegOvr (bp forces SS override)
push bp
mov bp,sp ; set up frame pointer
mov ax,ds
mov es,ax ; ES = caller's DS
CLD ; clear direction flag forever
; get address mode information, dispatch to address calculation
MOV DX,BX ; may use original BX to calc address
MOV AX,DI ; may use original DI to calc address
LDS DI,dword ptr [bp].regIP ; DS:DI is caller's CS:IP
INC DI ; increment past operation byte
MOV CX,[DI-2] ; get trap number, opcode (DS=caller CS)
; Otherwise, CL contains BEGINT + |MF|Arith| so we must unbias it
SUB CL,BEGINT
endif ;DOS3
; DS:DI = original CS:IP of displacement field
; ES = Effective Address segment (original DS if no segment override)
; DX = original BX
; AX = original DI
; SI = original SI
; CX = (opcode,0-7 from ESC byte)
; stack = saved register set
pub CommonDispatch
ROL CH,1 ; rotate MOD field next to r/m field
ROL CH,1
MOV BL,CH ; get copy of operation
AND BX,1FH ; Mask to MOD and r/m fields
SHL BX,1 ; make into word offset
JMP EA286Tab[BX]
OneByteDisp macro
mov al, [di] ;; get one byte displacement
cbw ;; sign extend displacement
inc di ;; get past displacement byte
add si, ax ;; add one byte displacement
endm
TwoByteDisp macro
add si, [di] ;; add word displacement
add di, 2 ;; get past displacement word
endm
even
pub BXXI0D
MOV SI,DX ; use original BX index value
JMP short ADRFIN ; have offset in SI
pub DSDI0D
MOV SI,AX ; use alternate index value
JMP short ADRFIN ; have offset in SI
even
pub BPXI1D
mov SI,[bp].regBP ; add original BP value
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
OneByteDisp
JMP short ADRFIN
even
pub BPDI1D
MOV SI,AX ; use alternate index value
pub BPSI1D
ADD SI,[bp].regBP ; add original BP value
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
OneByteDisp
JMP short ADRFIN
even
pub BXSI1D
MOV AX,SI ; really will want SI, not DI
pub BXDI1D
ADD DX,AX ; now DX is original BX plus index
pub BXXI1D
MOV AX,DX ; use original BX index value
pub DSDI1D
MOV SI,AX ; use alternate index value
pub DSSI1D
OneByteDisp
JMP short ADRFIN
even
pub BPXI2D
mov SI,[bp].regBP ; add original BP value
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
TwoByteDisp
JMP short ADRFIN
even
pub BPDI2D
MOV SI,AX ; use alternate index value
pub BPSI2D
ADD SI,[bp].regBP ; add original BP value
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
TwoByteDisp
JMP short ADRFIN
even
pub BXSI2D
MOV AX,SI ; really will want SI, not DI
pub BXDI2D
ADD DX,AX ; now DX is original BX plus index
pub BXXI2D
MOV AX,DX ; use original BX index value
pub DSDI2D
MOV SI,AX ; use alternate index value
pub DSSI2D
TwoByteDisp
JMP short ADRFIN
even
pub BPDI0D
MOV SI,AX ; use alternate index value
pub BPSI0D
add si,[bp].regBP ; really will want BP, not BX
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
jmp short ADRFIN
even
pub BXDI0D
MOV SI,AX ; si = regDI
pub BXSI0D
add si,dx ; si = regSI+regBX
jmp short ADRFIN
even
pub DSXI2D
MOV SI,[DI] ; get two byte displacement
INC DI ; get past displacement byte
INC DI ; get past displacement byte
pub DSSI0D ; SI = EA (original SI for DSSI0D)
pub ADRFIN
MOV [bp].regIP,DI ; final return offset
ifdef LOOK_AHEAD
mov bl,[di] ; get byte of next instruction
endif
ifdef MTHREAD
LOADthreadDS ; macro in emthread.asm
; loads thread's DS, trashes AX
else ;not MTHREAD
ifdef standalone
xor ax,ax
mov ds,ax
mov ds,ds:[4*TSKINT+2] ; DS = emulator task data segment
elseifdef XENIX
mov ax,LDT_DATA
mov ds,ax
elseifdef _COM_
mov ds, [__EmDataSeg]
else
mov ax, edataBASE
mov ds,ax
endif
endif ;not MTHREAD
ifdef LOOK_AHEAD
mov [NextOpCode], bl ; save byte of next instruction
endif
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,1
SHR CH,1 ; Move Op field to BX for Table jump
SHR CH,1
SHR CH,1
MOV BL,CH
AND BX,0EH
TEST CL,1 ; Arith field set?
JZ ArithmeticOpMem
pub NonArithOpMem
mov eax,offset EMLFINISH
push eax
jmp NonArithOpMemTab[BX]
even
pub ArithmeticOpMem
PUSH BX ; Save Op while we load the argument
MOV BX,CX ; Dispatch on MF
AND ebx,6H
ifdef i386
call FLDsdriTab[2*ebx] ; emulate proper load
else
call FLDsdriTab[ebx] ; emulate proper load
endif
POP BX
mov ax,ds ; ES = DS = task data area
mov es,ax
MOV SI,[CURstk] ; address top of stack
MOV DI,SI
ChangeDIfromTOStoNOS
MOV [RESULT],DI ; Set up destination Pointer
JMP short DoArithmeticOpPop
even
pub NoEffectiveAddress ; Either Register op or Miscellaneous
MOV [bp].regIP,DI ; final return offset
ifdef LOOK_AHEAD
mov bl, [di] ; get first byte of next instruction.
endif
ifdef MTHREAD
LOADthreadDS ; macro in emthread.asm
; loads thread's DS; trashes AX
mov ax,ds
mov es,ax ; DS = ES = per-thread em. data area
xor ax,ax
else ;not MTHREAD
xor ax,ax
ifdef standalone
mov ds,ax
mov di,ds:[4*TSKINT+2] ; DI = emulator task data segment
elseifdef XENIX
mov di,LDT_DATA
elseifdef _COM_
mov di, [__EmDataSeg]
else
mov di, edataBASE
endif
mov ds,di ; di = emulator data segment
mov es,di ; ax = 0
endif ;not MTHREAD
; ES = emulator data segment
; DS = emulator data segment
; AX = 0
ifdef LOOK_AHEAD
mov [NextOpCode], bl ; save first byte of next instruction
endif
mov [CURerr],ax ; clear current error, memory op bit
; CX = |Op|r/m|MOD|escape|MF|Arith|
MOV BL,CH
SHR BL,1 ; Mov Op field to BX for jump
SHR BL,1
SHR BL,1
SHR BL,1
AND BX,0EH
TEST CL,1 ; Arith field set?
JZ ArithmeticOpReg
pub NonArithOpReg
CALL NonArithOpRegTab[BX]
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)
even
pub ArithmeticOpReg
call RegAddr ;di <= address of 2nd operand
;carry set if invalid register
jc InvalidOperand ;no, invalid operand, don't do operation
MOV [RESULT],SI ; Set destination to TOS
TEST CL,04H ; Unless Dest bit is set
JZ DestIsSet ; in which case
MOV [RESULT],DI ; 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 SetUpPop
XOR BL,02H ; Toggle Reverse bit
pub SetUpPop
TEST CL,02H
JZ DoArithmeticOpNoPop
pub DoArithmeticOpPop
CALL ArithmeticOpTab[BX]
mov esi,[CURstk]
cmp esi,[BASstk] ; 15 was it last register in the chunk ?
jz short AOPstovr ; 16 yes, overflow
AOPstok:
sub esi,Reg87Len ; 4 decrement SI to previous register
mov [CURstk],esi ; 15 set current top of stack
JMP short EMLFINISH
AOPstovr:
call UnderStk ; stack underflow error
jmp AOPstok
even
pub DoArithmeticOpNoPop
mov eax,offset EMLFINISH
push eax
jmp ArithmeticOpTab[BX]
;*** 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 SI,[CURstk] ; address top of stack
MOV DI,SI
;set up address of 2nd operand based on r/m field of instruction
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.
mov dh,dl ; dh = dl = 4*(reg #)
shl dh,1 ; dh = 2*dh = 8*(reg #)
add dl,dh ; dl = 8*(reg #) + 4*(reg #) = 12*(reg #)
xor dh,dh ; zero out high byte of DX
sub di,dx ; di is address of second operand
cmp di,[BASstk] ; is register in range?
clc ; assume valid register
jg $+3 ; valid - skip next instruction
cmc ; set carry to indicate invalid register
ret
pub CallUnused
CALL UNUSED ; Treat as unimpleminted
jmp EMLFINISH
; out of line returns from emulator
pub SawException
pop bp ; restore registers
add sp,2 ; toss regSegOvr
pop bx
pop cx
pop dx
pop si
pop di
pop ds
pop es
pub ExceptionsEmulator
JMP CommonExceptions
ifdef LOOK_AHEAD
pub NoPipeLine
pop bp ; restore registers
add sp,2 ; toss regSegOvr
pop bx
pop cx
pop dx
pop si
pop di
pop ds
pop es
pub errret
error_return noerror ; common exit sequence
endif ;LOOK_AHEAD
; return from routine; restore registers and return
even
pub EMLFINISH
; 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 bh,byte ptr [UserControlWord] ; get user's IEEE control word
OR bh,0C2H ; mask reserved, IEM and denormal bits
AND bh,03FH ; unmask invalid instruction,
; stack overflow.
OR AL,bh ; mask for IEEE exceptions
NOT AL ; make a one mean an error
TEST AX,0FFFFh-MemoryOperand ; test for errors to report
jnz SawException ; goto error handler
ifndef LOOK_AHEAD
pop bp ; restore registers
add sp,2 ; toss regSegOvr
pop bx
pop cx
pop dx
pop si
pop di
pop ds
pop es
pub errret
error_return noerror ; common exit sequence
else ;LOOK_AHEAD
ifdef DOS3and5
jmp [LookAheadRoutine]
endif
ifdef DOS3
pub DOSLookAhead
cmp [NextOpcode], fINT ; Quick check. If first byte isn't
jne NoPipeLine ; an int instruction then exit.
; can stay in the emulator - set up registers for CommonDispatch
mov bp, sp ; set up frame pointer
lds di, dword ptr [bp].regIP ; DS:DI = address of next instruction
add di, 3 ; skip 3 bytes to displacement field
mov cx, [di-2] ; CX = (opcode byte,0-7 from ESC)
sub cl, BEGINT
cmp cl, 7 ; Can't handle segment overrides with
ja NoPipeLine ; pipe lining.
mov ax, [bp].regDI ; ax = original di
mov dx, [bp].regBX ; dx = original bx
mov si, [bp].regSI ; si = original si
mov es, [bp].regDS ; es = original ds (no segment override)
mov [bp].regSegOvr, ss ; reset override segment
rol ch, 1 ; rotate MOD field next to r/m field
rol ch, 1
mov bl, ch ; get copy of operation
and bx, 1fh ; Mask to MOD and r/m fields
shl bx, 1 ; make into word offset
jmp EA286Tab[bx]
endif ;DOS3
ifdef PROTECT
pub ProtLookAhead
mov cl, [NextOpcode]
cmp cl, fFWAIT
je CheckNextByte
cmp cl, iNOP
je CheckNextByte
xor cl, 20h ; See if this is a floating point instruction.
cmp cl, 0f8h
jbNoPipeLine:
jb NoPipeLine
mov bp, sp ; set up frame pointer
lds di, dword ptr [bp].regIP ; ds:di = address of next instruction
jmp short CanDoPipeLine
pub CheckNextByte
mov bp, sp ; set up frame pointer
lds di, dword ptr [bp].regIP ; ds:di = address of next instruction
inc di ; next instruction was NOP or FWAIT
mov cl, [di]
xor cl, 20h
cmp cl, 0f8h
jb jbNoPipeLine
pub CanDoPipeLine
mov ch, [di+1] ; we already have first byte of next
add di, 2 ; instruction in cl
add cl, 8h ; clear out what's left of escape
mov ax, [bp].regDI ; ax = original di
mov dx, [bp].regBX ; dx = original bx
mov si, [bp].regSI ; si = original si
mov es, [bp].regDS ; es = original ds (no segment override)
mov [bp].regSegOvr, ss ; reset override segment
rol ch, 1 ; rotate MOD field next to r/m field
rol ch, 1
mov bl, ch ; get copy of operation
and bx, 1fh ; Mask to MOD and r/m fields
shl bx, 1 ; make into word offset
jmp EA286Tab[bx]
endif ;PROTECT
endif ;LOOK_AHEAD
ProfEnd MAIN