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