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