subttl  emfcom.asm - Comparison Instructions
	page
;*******************************************************************************
;emfcom.asm - Comparison Instructions
;
;        Microsoft Confidential
;
;        Copyright (c) Microsoft Corporation 1991
;        All Rights Reserved
;
;Purpose:
;       FCOM,FCOMP,FCOMPP,FUCOM,FUCOMP,FUCOMPP,FTST,FXAM instructions
;
;Revision History:
;
; []	09/05/91  TP	Initial 32-bit version.
;
;*******************************************************************************


;*******************************************************************************
;Dispatch table for compare
;
;One operand has been loaded into ecx:ebx:esi ("source"), the other is
;pointed to by edi ("dest").  
;
;Tag of source is shifted.  Tag values are as follows:
.erre   TAG_SNGL        eq      0       ;SINGLE: low 32 bits are zero
.erre   TAG_VALID       eq      1
.erre   TAG_ZERO        eq      2
.erre   TAG_SPCL        eq      3       ;NAN, Infinity, Denormal, Empty
;Any special case routines not found in this file are in emarith.asm
tFcomDisp       label   dword           ;Source (reg)   Dest (*[di] = ST)
        dd      ComDouble               ;single         single
        dd      ComDouble               ;single         double
	dd	ComDestZero		;single		zero
        dd      ComSpclDest             ;single         special
        dd      ComDouble               ;double         single
        dd      ComDouble               ;double         double
        dd      ComDestZero             ;double         zero
        dd      ComSpclDest             ;double         special
        dd      ComSrcZero              ;zero           single
        dd      ComSrcZero              ;zero           double
        dd      ComEqual                ;zero           zero
	dd	ComSpclDest		;zero		special
	dd	ComSpclSource		;special	single
	dd	ComSpclSource		;special	double
	dd	ComSpclSource		;special	zero
	dd	ComBothSpcl		;special	special


EM_ENTRY eFICOMP16
eFICOMP16:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset PopWhenDone
	push	offset ComOpLoaded
	jmp	Load16Int		;Returns to ComOpLoaded

EM_ENTRY eFICOM16
eFICOM16:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset ComOpLoaded
	jmp	Load16Int		;Returns to ComOpLoaded

EM_ENTRY eFICOMP32
eFICOMP32:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset PopWhenDone
	push	offset ComOpLoaded
	jmp	Load32Int		;Returns to ComOpLoaded

EM_ENTRY eFICOM32
eFICOM32:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset ComOpLoaded
	jmp	Load32Int		;Returns to ComOpLoaded

EM_ENTRY eFCOMP32
eFCOMP32:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset PopWhenDone
	push	offset ComOpLoaded
	jmp	Load32Real		;Returns to ComOpLoaded

EM_ENTRY eFCOM32
eFCOM32:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset ComOpLoaded
	jmp	Load32Real		;Returns to ComOpLoaded

EM_ENTRY eFCOMP64
eFCOMP64:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset PopWhenDone
	push	offset ComOpLoaded
	jmp	Load64Real		;Returns to ComOpLoaded

EM_ENTRY eFCOM64
eFCOM64:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset ComOpLoaded
	jmp	Load64Real		;Returns to ComOpLoaded

EM_ENTRY eFUCOMPP
eFUCOMPP:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset ComPop2
	jmp	eFUCOM0

EM_ENTRY eFUCOMP
eFUCOMP:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset PopWhenDone
	jmp	eFUCOM0

EM_ENTRY eFUCOM
eFUCOM:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
eFUCOM0:	
;esi = pointer to st(i) from instruction field
;edi = [CURstk]
	mov	ecx,EMSEG:[esi].ExpSgn
	mov	ebx,EMSEG:[esi].lManHi
	mov	esi,EMSEG:[esi].lManLo
	mov	dl,40H			;Flag FUCOM - Look for SNAN
	jmp	UComOpLoaded

EM_ENTRY eFCOMPP
eFCOMPP:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset ComPop2
	jmp	eFCOM0

EM_ENTRY eFCOMP
eFCOMP:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	push	offset PopWhenDone
	jmp	eFCOM0

EM_ENTRY eFCOM
eFCOM:
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
eFCOM0:
;esi = pointer to st(i) from instruction field
;edi = [CURstk]
	mov	ecx,EMSEG:[esi].ExpSgn
	mov	ebx,EMSEG:[esi].lManHi
	mov	esi,EMSEG:[esi].lManLo

ComOpLoaded:
;	mov	EMSEG:[UpdateCCodes],1
	mov	dl,0			;flag FCOM - Look for any NAN
UComOpLoaded:
	mov     ebp,offset tFcomDisp
	mov	al,cl
	mov     ah,EMSEG:[edi].bTag
	test	ax,ZEROorSPCL * 100H + ZEROorSPCL
	jnz	TwoOpDispatch

;.erre	ComDouble eq $			;Fall into ComDouble

;*********
ComDouble:
;*********
;
;ebx:esi = op1 mantissa
;ecx = op1 sign in bit 15, exponent in high half
;edi = pointer to op2
	mov	eax,EMSEG:[edi].ExpSgn
	and	ax,bSign shl 8		;Keep sign only
	and	cx,bSign shl 8
	cmp	ah,ch			;Are signs the same?
	jnz	StBigger
	cmp	eax,ecx			;Are exponents the same?
	jl	StSmaller
	jg	StBigger
	cmp	EMSEG:[edi].lManHi,ebx	;Compare mantissas
	jnz	MantDif
	cmp	EMSEG:[edi].lManLo,esi	;Set flags for ST - src
	jz	ComEqual
MantDif:
	adc	al,al			;Copy CY flag to bit 0
	rol	ah,1			;Rotate sign to bit 0
	xor	al,ah			;Flip saved CY bit if negative
	mov	EMSEG:[SWcc],al		;Set condition code
	ret

StSmaller:
	not	ah
StBigger:
;ah = sign of ST
;ch = sign of other operand
;ST is bigger if it is positive (smaller if it is negative).
;Use the sign bit directly as the "less than" bit C0.
.erre	C0 eq 1
	shr	ah,7			;Bring sign down to bit 0, clear CY
	mov	EMSEG:[SWcc],ah		;Bit set if ST smaller (negative)
	ret

ComEqual:
	mov	EMSEG:[SWcc],CCequal
	ret



PopWhenDone:
.erre	bTAG_NOPOP eq -1
	inc	cl			;OK to pop?
	jz	ComPopX			;No - had unmasked Invalid Operation

	POPSTret

ComPop2:
.erre	bTAG_NOPOP eq -1
	inc	cl			;OK to pop?
	jz	ComPopX			;No - had unmasked Invalid Operation
	mov	esi,EMSEG:[CURstk]
	mov	EMSEG:[esi].bTag,bTAG_EMPTY
	add	esi,Reg87Len*2
	cmp	esi,ENDstk			;JWM
	je	PopOneOver
	ja	PopTwoOver
	mov	EMSEG:[esi-Reg87Len].bTag,bTAG_EMPTY
	mov	EMSEG:[CURstk],esi
ComPopX:
	ret

PopOneOver:
	mov	EMSEG:[CURstk],BEGstk		;JWM
ifdef NT386
	mov	EMSEG:[INITstk].bTAG,bTAG_EMPTY
else
	mov	EMSEG:[XINITstk].bTAG,bTAG_EMPTY
endif
	ret

PopTwoOver:
	mov	EMSEG:[CURstk],BEGstk+Reg87Len	;JWM
ifdef NT386
	mov	EMSEG:[BEGstk].bTAG,bTAG_EMPTY
else
	mov	EMSEG:[XBEGstk].bTAG,bTAG_EMPTY
endif
	ret

;*******************************************************************************
;Special cases for FCOM/FUCOM.
;These don't share with those in emarith.asm because NANs are treated
;differently.
ComDestZero:
;ST is zero, so Src is bigger if it is positive (smaller if it is negative).
;Use the sign bit directly as the "less than" bit C0.
	not	ch			;C0 is 1 if ST < Src
.erre	C0 eq 1
	shr	ch,7			;Bring sign down to bit 0
	mov	EMSEG:[SWcc],ch		;Bit set if Src smaller (negative)
	ret

ComSrcZero:
;ST is bigger if it is positive (smaller if it is negative).
;Use the sign bit directly as the "less than" bit C0.
	mov	al,EMSEG:[edi].bSgn
.erre	C0 eq 1
	shr	al,7			;Bring sign down to bit 0
	mov	EMSEG:[SWcc],al		;Bit set if ST smaller (negative)
	ret

ComSpclSource:
	cmp	cl,bTAG_NAN
	jz	ComSrcNAN
	cmp	cl,bTAG_INF
	jz	ComDestZero
	cmp	cl,bTAG_DEN
	jz	ComDenormal
;Must be empty
ComEmpty:
	mov	EMSEG:[CURerr],Invalid+StackFlag
	jmp	ComChkMask

ComSrcNAN:
	shl	edx,24			;Move dl to high byte
	test	ebx,edx			;See if we report error with this NAN
ComChkNAN:
	jnz	Incomp
ComInvalid:
	mov	EMSEG:[CURerr],Invalid	;Flag the error
ComChkMask:
	test	EMSEG:[CWmask],Invalid	;Is exception masked?
	jnz	Incomp
	mov	cl,bTAG_NOPOP		;Unmasked, don't pop stack
Incomp:
	mov	EMSEG:[SWcc],CCincomprable
	ret

ComSpclDest:
	mov	al,EMSEG:[edi].bTag
	cmp	al,bTAG_INF
	jz	ComSrcZero
	cmp	al,bTAG_Empty
	jz	ComEmpty
	cmp	al,bTAG_DEN
	jz	ComDenormal
;Must be NAN
ComDestNAN:
	test	EMSEG:[edi].bMan7,dl		;See if we report error with this NAN
	jmp	ComChkNAN

ComBothSpcl:
	mov	al,EMSEG:[edi].bTag
	cmp	cl,bTAG_EMPTY
	jz	ComEmpty
	cmp	al,bTAG_EMPTY
	jz	ComEmpty
	cmp	cl,bTAG_NAN
	jz	ComSrcNAN
	cmp	al,bTAG_NAN
	jz	ComDestNAN
	mov	ah,cl
	cmp	ax,(bTAG_INF shl 8) + bTag_INF	;Are both Infinity?
	jz	ComDouble		;If so, compare their signs
;Must have at least one denormal
ComDenormal:
	or	EMSEG:[CURerr],Denormal
        jmp     ComDouble

;*******************************************************************************

XAM_Unsupported	equ	0
XAM_NAN		equ	C0
XAM_Norm	equ	C2
XAM_Inf		equ	C2+C0
XAM_Zero	equ	C3
XAM_Empty	equ	C3+C0
XAM_Den		equ	C3+C2

tXamTag	label	byte
.erre	TAG_SNGL	eq	$-tXamTag
	db	XAM_Norm		;TAG_SNGL
.erre	TAG_VALID	eq	$-tXamTag
	db	XAM_Norm		;TAG_VALID
.erre	TAG_ZERO	eq	$-tXamTag
	db	XAM_Zero		;TAG_ZERO
.erre	TAG_EMPTY	eq	$-tXamTag
	db	XAM_Empty		;TAG_EMPTY
	db	0
	db	0
	db	0
.erre	TAG_INF 	eq	$-tXamTag
	db	XAM_Inf 		;TAG_INF
	db	0
	db	0
	db	0
.erre	TAG_NAN 	eq	$-tXamTag
	db	XAM_NAN 		;TAG_NAN
	db	0
	db	0
	db	0
.erre	TAG_DEN 	eq	$-tXamTag
	db	XAM_Den 		;TAG_DEN

EM_ENTRY eFXAM
eFXAM:
;edi = [CURstk]
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	mov	eax,EMSEG:[edi].ExpSgn	;Get sign and tag
	mov	bl,ah			;Save sign
	and	bl,bSign		;Keep only sign bit
	and	eax,0FH			;Save low 4 bits of tag
	mov	al,tXamTag[eax]		;Lookup cond. codes for this tag
.erre	C1 eq 2		;Bit 1
.erre	bSign eq 80H	;Bit 7
	shr	bl,7-1			;Move sign bit to CC C1
	or	al,bl
	mov	EMSEG:[SWcc],al
	ret

;*******************************************************************************

EM_ENTRY eFTST
eFTST:
;edi = [CURstk]
    and		[esp].[OldLongStatus+4],NOT(ConditionCode SHL 16)	;clear C0,C1,C2,C3
	mov	eax,EMSEG:[edi].ExpSgn
	cmp	al,bTAG_ZERO
	jz	ComEqual
	ja	TestSpcl
;Either single or double, non-zero.  Just check sign.
TestSign:
	shr	ah,7			;Bring sign down to bit 0
	mov	EMSEG:[SWcc],ah		;Bit set if negative
	ret

TestSpcl:
	cmp	al,bTAG_INF
	jz	TestSign		;Normal test for Infinity
	cmp	al,bTAG_EMPTY
	jz	ComEmpty
	cmp	al,bTAG_NAN
	jz	ComInvalid
;Must be denormal
	mov	EMSEG:[CURerr],Denormal
	jmp	TestSign