*
* $Id: do_func.asm,v 0.4 91/07/11 09:50:51 mks Exp $
*
* $Log:	do_func.asm,v $
* Revision 0.4  91/07/11  09:50:51  mks
* Removed all conditional code for the FMOVEM assembler bug.
* HX68 V.81 is the minimum version needed to correctly assembly this code.
* 
* Revision 0.3  91/07/08  15:49:06  mks
* Ok, so this is an ugly hack to work around the FMOVEM problem...
* It turns out that the assembler just did not notice that the register was
* an FPU register unless there was a register list.  So...  we make all FMOVEMs
* that have only one FPU register, show it twice.  For example:
* fmovem.x  fp0,(a0)            This would become:
* fmovem.x  fp0/fp0,(a0)
*
* This fools the assembler into doing the right thing.  It is all within
* conditional assembly so that when a fixed assembler comes, it can easily
* be tested and the kludge removed.
*
* Revision 0.2  91/07/02  14:41:02  mks
* Added conditional kludges for FMOVEM bug fix
*
* Revision 0.1  91/06/26  09:01:34  mks
* Initial check in...
*
*

	include	"assembly_options.i"

*
*	do_func.sa 3.4 2/18/91
*
* Do_func performs the unimplemented operation.  The operation
* to be performed is determined from the lower 7 bits of the
* extension word (except in the case of fmovecr and fsincos).
* The opcode and tag bits form an index into a jump table in
* tbldo.sa.  Cases of zero, infinity and NaN are handled in
* do_func by forcing the default result.  Normalized and
* denormalized (there are no unnormalized numbers at this
* point) are passed onto the emulation code.
*
* CMDREG1B and STAG are extracted from the fsave frame
* and combined to form the table index.  The function called
* will start with a0 pointing to the ETEMP operand.  Dyadic
* functions can find FPTEMP at -12(a0).
*
* Called functions return their result in fp0.  Sincos returns
* sin(x) in fp0 and cos(x) in fp1.
*

*		Copyright (C) Motorola, Inc. 1990
*			All Rights Reserved
*
*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA
*	The copyright notice above does not evidence any
*	actual or intended publication of such source code.

DO_FUNC	IDNT    2,1 Motorola 040 Floating Point Software Package

	section	8

	include	"fpsp.i"

	xref	t_dz2
	xref	t_operr
	xref	t_inx2
	xref 	t_resdnrm
	xref	dst_nan
	xref	src_nan
	xref	nrm_set
	xref	sto_cos

	xref	tblpre
	xref	slognp1,slogn,slog10,slog2
	xref	slognd,slog10d,slog2d
	xref	smod,srem
	xref	sscale
	xref	smovcr

PONE	dc.l	$3fff0000,$80000000,$00000000	;+1
MONE	dc.l	$bfff0000,$80000000,$00000000	;-1
PZERO	dc.l	$00000000,$00000000,$00000000	;+0
MZERO	dc.l	$80000000,$00000000,$00000000	;-0
PINF	dc.l	$7fff0000,$00000000,$00000000	;+inf
MINF	dc.l	$ffff0000,$00000000,$00000000	;-inf
QNAN	dc.l	$7fff0000,$ffffffff,$ffffffff	;non-signaling nan
PPIBY2  dc.l	$3FFF0000,$C90FDAA2,$2168C235	;+PI/2
MPIBY2  dc.l	$bFFF0000,$C90FDAA2,$2168C235	;-PI/2

	xdef	do_func
do_func:
	clr.b	CU_ONLY(a6)
*
* Check for fmovecr.  It does not follow the format of fp gen
* unimplemented instructions.  The test is on the upper 6 bits;
* if they are $17, the inst is fmovecr.  Call entry smovcr
* directly.
*
	bfextu	CMDREG1B(a6){0:6},d0 ;get opclass and src fields
	cmpi.l	#$17,d0		;if op class and size fields are $17,
*				;it is FMOVECR; if not, continue
	bne.b	not_fmovecr
	jmp	smovcr		;fmovecr; jmp directly to emulation

not_fmovecr:
	move.w	CMDREG1B(a6),d0
	and.l	#$7F,d0
	cmpi.l	#$38,d0		;if the extension is >= $38,
	bge.b	serror		;it is illegal
	bfextu	STAG(a6){0:3},d1
	lsl.l	#3,d0		;make room for STAG
	add.l	d1,d0		;combine for final index into table
	lea.l	tblpre,a1	;start of monster jump table
	move.l	(a1,d0.w*4),a1	;real target address
	lea.l	ETEMP(a6),a0	;a0 is pointer to src op
	move.l	USER_FPCR(a6),d1
	and.l	#$FF,d1		; discard all but rounding mode/prec
	fmove.l	#0,fpcr
	jmp	(a1)
*
*	ERROR
*
	xdef	serror
serror:
	st.b	STORE_FLG(a6)
	rts
*
* These routines load forced values into fp0.  They are called
* by index into tbldo.
*
* Load a signed zero to fp0 and set inex2/ainex
*
	xdef	snzrinx
snzrinx:
	btst.b	#sign_bit,LOCAL_EX(a0)	;get sign of source operand
	bne.b	ld_mzinx	;if negative, branch
	bsr	ld_pzero	;bsr so we can return and set inx
	bra	t_inx2		;now, set the inx for the next inst
ld_mzinx:
	bsr	ld_mzero	;if neg, load neg zero, return here
	bra	t_inx2		;now, set the inx for the next inst
*
* Load a signed zero to fp0; do not set inex2/ainex
*
	xdef	szero
szero:
	btst.b	#sign_bit,LOCAL_EX(a0) ;get sign of source operand
	bne	ld_mzero	;if neg, load neg zero
	bra	ld_pzero	;load positive zero
*
* Load a signed infinity to fp0; do not set inex2/ainex
*
	xdef	sinf
sinf:
	btst.b	#sign_bit,LOCAL_EX(a0)	;get sign of source operand
	bne	ld_minf			;if negative branch
	bra	ld_pinf
*
* Load a signed one to fp0; do not set inex2/ainex
*
	xdef	sone
sone:
	btst.b	#sign_bit,LOCAL_EX(a0)	;check sign of source
	bne	ld_mone
	bra	ld_pone
*
* Load a signed pi/2 to fp0; do not set inex2/ainex
*
	xdef	spi_2
spi_2:
	btst.b	#sign_bit,LOCAL_EX(a0)	;check sign of source
	bne	ld_mpi2
	bra	ld_ppi2
*
* Load either a +0 or +inf for plus/minus operand
*
	xdef	szr_inf
szr_inf:
	btst.b	#sign_bit,LOCAL_EX(a0)	;check sign of source
	bne	ld_pzero
	bra	ld_pinf
*
* Result is either an operr or +inf for plus/minus operand
* [Used by slogn, slognp1, slog10, and slog2]
*
	xdef	sopr_inf
sopr_inf:
	btst.b	#sign_bit,LOCAL_EX(a0)	;check sign of source
	bne	t_operr
	bra	ld_pinf
*
*	FLOGNP1
*
	xdef	sslognp1
sslognp1:
	fmovem.x (a0),fp0
	fcmp.b	#-1,fp0
	fbgt	slognp1
	fbeq	t_dz2		;if = -1, divide by zero exception
	fmove.l	#0,FPSR		;clr N flag
	bra	t_operr		;take care of operands < -1
*
*	FETOXM1
*
	xdef	setoxm1i
setoxm1i:
	btst.b	#sign_bit,LOCAL_EX(a0)	;check sign of source
	bne	ld_mone
	bra	ld_pinf
*
*	FLOGN
*
* Test for 1.0 as an input argument, returning +zero.  Also check
* the sign and return operr if negative.
*
	xdef	sslogn
sslogn:
	btst.b	#sign_bit,LOCAL_EX(a0)
	bne	t_operr		;take care of operands < 0
	cmpi.w	#$3fff,LOCAL_EX(a0) ;test for 1.0 input
	bne	slogn
	cmpi.l	#$80000000,LOCAL_HI(a0)
	bne	slogn
	tst.l	LOCAL_LO(a0)
	bne	slogn
	fmove.x	PZERO,fp0
	rts

	xdef	sslognd
sslognd:
	btst.b	#sign_bit,LOCAL_EX(a0)
	beq	slognd
	bra	t_operr		;take care of operands < 0

*
*	FLOG10
*
	xdef	sslog10
sslog10:
	btst.b	#sign_bit,LOCAL_EX(a0)
	bne	t_operr		;take care of operands < 0
	cmpi.w	#$3fff,LOCAL_EX(a0) ;test for 1.0 input
	bne	slog10
	cmpi.l	#$80000000,LOCAL_HI(a0)
	bne	slog10
	tst.l	LOCAL_LO(a0)
	bne	slog10
	fmove.x	PZERO,fp0
	rts

	xdef	sslog10d
sslog10d:
	btst.b	#sign_bit,LOCAL_EX(a0)
	beq	slog10d
	bra	t_operr		;take care of operands < 0

*
*	FLOG2
*
	xdef	sslog2
sslog2:
	btst.b	#sign_bit,LOCAL_EX(a0)
	bne	t_operr		;take care of operands < 0
	cmpi.w	#$3fff,LOCAL_EX(a0) ;test for 1.0 input
	bne	slog2
	cmpi.l	#$80000000,LOCAL_HI(a0)
	bne	slog2
	tst.l	LOCAL_LO(a0)
	bne	slog2
	fmove.x	PZERO,fp0
	rts

	xdef	sslog2d
sslog2d:
	btst.b	#sign_bit,LOCAL_EX(a0)
	beq	slog2d
	bra	t_operr		;take care of operands < 0

*
*	FMOD
*
pmodt:
*				;$21 fmod
*				;dtag,stag
	dc.l	smod		;  00,00  norm,norm = normal
	dc.l	smod_oper	;  00,01  norm,zero = nan with operr
	dc.l	smod_fpn	;  00,10  norm,inf  = fpn
	dc.l	smod_snan	;  00,11  norm,nan  = nan
	dc.l	smod_zro	;  01,00  zero,norm = +-zero
	dc.l	smod_oper	;  01,01  zero,zero = nan with operr
	dc.l	smod_zro	;  01,10  zero,inf  = +-zero
	dc.l	smod_snan	;  01,11  zero,nan  = nan
	dc.l	smod_oper	;  10,00  inf,norm  = nan with operr
	dc.l	smod_oper	;  10,01  inf,zero  = nan with operr
	dc.l	smod_oper	;  10,10  inf,inf   = nan with operr
	dc.l	smod_snan	;  10,11  inf,nan   = nan
	dc.l	smod_dnan	;  11,00  nan,norm  = nan
	dc.l	smod_dnan	;  11,01  nan,zero  = nan
	dc.l	smod_dnan	;  11,10  nan,inf   = nan
	dc.l	smod_dnan	;  11,11  nan,nan   = nan

	xdef	pmod
pmod:
	bfextu	STAG(a6){0:3},d0 ;stag = d0
	bfextu	DTAG(a6){0:3},d1 ;dtag = d1

*
* Alias extended denorms to norms for the jump table.
*
	bclr.l	#2,d0
	bclr.l	#2,d1

	lsl.b	#2,d1
	or.b	d0,d1		;d1{3:2} = dtag, d1{1:0} = stag
*				;Tag values:
*				;00 = norm or denorm
*				;01 = zero
*				;10 = inf
*				;11 = nan
	lea	pmodt,a1
	move.l	(a1,d1.w*4),a1
	jmp	(a1)

smod_snan:
	bra	src_nan
smod_dnan:
	bra	dst_nan
smod_oper:
	bra	t_operr
smod_zro:
	move.b	ETEMP(a6),d1	;get sign of src op
	move.b	FPTEMP(a6),d0	;get sign of dst op
	eor.b	d0,d1		;get exor of sign bits
	btst.l	#7,d1		;test for sign
	beq.b	smod_zsn	;if clr, do not set sign big
	bset.b	#q_sn_bit,FPSR_QBYTE(a6) ;set q-byte sign bit
smod_zsn:
	btst.l	#7,d0		;test if + or -
	beq	ld_pzero	;if pos then load +0
	bra	ld_mzero	;else neg load -0

smod_fpn:
	move.b	ETEMP(a6),d1	;get sign of src op
	move.b	FPTEMP(a6),d0	;get sign of dst op
	eor.b	d0,d1		;get exor of sign bits
	btst.l	#7,d1		;test for sign
	beq.b	smod_fsn	;if clr, do not set sign big
	bset.b	#q_sn_bit,FPSR_QBYTE(a6) ;set q-byte sign bit
smod_fsn:
	tst.b	DTAG(a6)	;filter out denormal destination case
	bpl.b	smod_nrm	;
	lea.l	FPTEMP(a6),a0	;a0<- addr(FPTEMP)
	bra	t_resdnrm	;force UNFL(but exact) result
smod_nrm:
	fmove.l USER_FPCR(a6),fpcr ;use user's rmode and precision
	fmove.x FPTEMP(a6),fp0	;return dest to fp0
	rts

*
*	FREM
*
premt:
*				;$25 frem
*				;dtag,stag
	dc.l	srem		;  00,00  norm,norm = normal
	dc.l	srem_oper	;  00,01  norm,zero = nan with operr
	dc.l	srem_fpn	;  00,10  norm,inf  = fpn
	dc.l	srem_snan	;  00,11  norm,nan  = nan
	dc.l	srem_zro	;  01,00  zero,norm = +-zero
	dc.l	srem_oper	;  01,01  zero,zero = nan with operr
	dc.l	srem_zro	;  01,10  zero,inf  = +-zero
	dc.l	srem_snan	;  01,11  zero,nan  = nan
	dc.l	srem_oper	;  10,00  inf,norm  = nan with operr
	dc.l	srem_oper	;  10,01  inf,zero  = nan with operr
	dc.l	srem_oper	;  10,10  inf,inf   = nan with operr
	dc.l	srem_snan	;  10,11  inf,nan   = nan
	dc.l	srem_dnan	;  11,00  nan,norm  = nan
	dc.l	srem_dnan	;  11,01  nan,zero  = nan
	dc.l	srem_dnan	;  11,10  nan,inf   = nan
	dc.l	srem_dnan	;  11,11  nan,nan   = nan

	xdef	prem
prem:

	bfextu	STAG(a6){0:3},d0 ;stag = d0
	bfextu	DTAG(a6){0:3},d1 ;dtag = d1
*
* Alias extended denorms to norms for the jump table.
*
	bclr	#2,d0
	bclr	#2,d1

	lsl.b	#2,d1
	or.b	d0,d1		;d1{3:2} = dtag, d1{1:0} = stag
*				;Tag values:
*				;00 = norm or denorm
*				;01 = zero
*				;10 = inf
*				;11 = nan
	lea	premt,a1
	move.l	(a1,d1.w*4),a1
	jmp	(a1)

srem_snan:
	bra	src_nan
srem_dnan:
	bra	dst_nan
srem_oper:
	bra	t_operr
srem_zro:
	move.b	ETEMP(a6),d1	;get sign of src op
	move.b	FPTEMP(a6),d0	;get sign of dst op
	eor.b	d0,d1		;get exor of sign bits
	btst.l	#7,d1		;test for sign
	beq.b	srem_zsn	;if clr, do not set sign big
	bset.b	#q_sn_bit,FPSR_QBYTE(a6) ;set q-byte sign bit
srem_zsn:
	btst.l	#7,d0		;test if + or -
	beq	ld_pzero	;if pos then load +0
	bra	ld_mzero	;else neg load -0

srem_fpn:
	move.b	ETEMP(a6),d1	;get sign of src op
	move.b	FPTEMP(a6),d0	;get sign of dst op
	eor.b	d0,d1		;get exor of sign bits
	btst.l	#7,d1		;test for sign
	beq.b	srem_fsn	;if clr, do not set sign big
	bset.b	#q_sn_bit,FPSR_QBYTE(a6) ;set q-byte sign bit
srem_fsn:
	tst.b	DTAG(a6)	;filter out denormal destination case
	bpl.b	srem_nrm	;
	lea.l	FPTEMP(a6),a0	;a0<- addr(FPTEMP)
	bra	t_resdnrm	;force UNFL(but exact) result
srem_nrm:
	fmove.l USER_FPCR(a6),fpcr ;use user's rmode and precision
	fmove.x FPTEMP(a6),fp0	;return dest to fp0
	rts
*
*	FSCALE
*
pscalet:
*				;$26 fscale
*				;dtag,stag
	dc.l	sscale		;  00,00  norm,norm = result
	dc.l	sscale		;  00,01  norm,zero = fpn
	dc.l	scl_opr		;  00,10  norm,inf  = nan with operr
	dc.l	scl_snan	;  00,11  norm,nan  = nan
	dc.l	scl_zro		;  01,00  zero,norm = +-zero
	dc.l	scl_zro		;  01,01  zero,zero = +-zero
	dc.l	scl_opr		;  01,10  zero,inf  = nan with operr
	dc.l	scl_snan	;  01,11  zero,nan  = nan
	dc.l	scl_inf		;  10,00  inf,norm  = +-inf
	dc.l	scl_inf		;  10,01  inf,zero  = +-inf
	dc.l	scl_opr		;  10,10  inf,inf   = nan with operr
 	dc.l	scl_snan	;  10,11  inf,nan   = nan
 	dc.l	scl_dnan	;  11,00  nan,norm  = nan
 	dc.l	scl_dnan	;  11,01  nan,zero  = nan
 	dc.l	scl_dnan	;  11,10  nan,inf   = nan
	dc.l	scl_dnan	;  11,11  nan,nan   = nan

	xdef	pscale
pscale:
	bfextu	STAG(a6){0:3},d0 ;stag in d0
	bfextu	DTAG(a6){0:3},d1 ;dtag in d1
	bclr.l	#2,d0		;alias  denorm into norm
	bclr.l	#2,d1		;alias  denorm into norm
	lsl.b	#2,d1
	or.b	d0,d1		;d1{4:2} = dtag, d1{1:0} = stag
*				;dtag values     stag values:
*				;000 = norm      00 = norm
*				;001 = zero	 01 = zero
*				;010 = inf	 10 = inf
*				;011 = nan	 11 = nan
*				;100 = dnrm
*
*
	lea.l	pscalet,a1	;load start of jump table
	move.l	(a1,d1.w*4),a1	;load a1 with label depending on tag
	jmp	(a1)		;go to the routine

scl_opr:
	bra	t_operr

scl_dnan:
	bra	dst_nan

scl_zro:
	btst.b	#sign_bit,FPTEMP_EX(a6)	;test if + or -
	beq	ld_pzero		;if pos then load +0
	bra	ld_mzero		;if neg then load -0
scl_inf:
	btst.b	#sign_bit,FPTEMP_EX(a6)	;test if + or -
	beq	ld_pinf			;if pos then load +inf
	bra	ld_minf			;else neg load -inf
scl_snan:
	bra	src_nan
*
*	FSINCOS
*
	xdef	ssincosz
ssincosz:
	btst.b	#sign_bit,ETEMP(a6)	;get sign
	beq.b	sincosp
	fmove.x	MZERO,fp0
	bra.b	sincoscom
sincosp:
	fmove.x PZERO,fp0
sincoscom:
  	fmovem.x PONE,fp1	;do not allow FPSR to be affected
	bra	sto_cos		;store cosine result

	xdef	ssincosi
ssincosi:
	fmove.x QNAN,fp1	;load NAN
	bsr	sto_cos		;store cosine result
	fmove.x QNAN,fp0	;load NAN
	bra	t_operr

	xdef	ssincosnan
ssincosnan:
	move.l	ETEMP_EX(a6),FP_SCR1(a6)
	move.l	ETEMP_HI(a6),FP_SCR1+4(a6)
	move.l	ETEMP_LO(a6),FP_SCR1+8(a6)
	bset.b	#signan_bit,FP_SCR1+4(a6)
	fmovem.x FP_SCR1(a6),fp1
	bsr	sto_cos
	bra	src_nan
*
* This code forces default values for the zero, inf, and nan cases
* in the transcendentals code.  The CC bits must be set in the
* stacked FPSR to be correctly reported.
*
***Returns +PI/2
	xdef	ld_ppi2
ld_ppi2:
	fmove.x PPIBY2,fp0		;load +pi/2
	bra	t_inx2			;set inex2 exc

***Returns -PI/2
	xdef	ld_mpi2
ld_mpi2:
	fmove.x MPIBY2,fp0		;load -pi/2
	or.l	#neg_mask,USER_FPSR(a6)	;set N bit
	bra	t_inx2			;set inex2 exc

***Returns +inf
	xdef	ld_pinf
ld_pinf:
	fmove.x PINF,fp0		;load +inf
	or.l	#inf_mask,USER_FPSR(a6)	;set I bit
	rts

***Returns -inf
	xdef	ld_minf
ld_minf:
	fmove.x MINF,fp0		;load -inf
	or.l	#neg_mask+inf_mask,USER_FPSR(a6)	;set N and I bits
	rts

***Returns +1
	xdef	ld_pone
ld_pone:
	fmove.x PONE,fp0		;load +1
	rts

***Returns -1
	xdef	ld_mone
ld_mone:
	fmove.x MONE,fp0		;load -1
	or.l	#neg_mask,USER_FPSR(a6)	;set N bit
	rts

***Returns +0
	xdef	ld_pzero
ld_pzero:
	fmove.x PZERO,fp0		;load +0
	or.l	#z_mask,USER_FPSR(a6)	;set Z bit
	rts

***Returns -0
	xdef	ld_mzero
ld_mzero:
	fmove.x MZERO,fp0		;load -0
	or.l	#neg_mask+z_mask,USER_FPSR(a6)	;set N and Z bits
	rts

	end
