/*	$NetBSD: unimpl_emul.S,v 1.2 2002/02/24 01:04:27 matt Exp $	*/

/*
 * Copyright (c) 2001 Brandon Creighton.  All rights reserved.
 * Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed at Ludd, University of
 *      Lule}, Sweden and its contributors.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include <machine/asm.h>
#include "assym.h"

# Only intended for debugging emulation code (security hole)
#undef	EMULATE_INKERNEL

# Defines to fetch register operands
#define	S_R0	(%fp)
#define	S_R1	4(%fp)
#define	S_R2	8(%fp)
#define	S_R3	12(%fp)
#define	S_R4	16(%fp)
#define	S_R5	20(%fp)
#define	S_R6	24(%fp)
#define	S_R7	28(%fp)
#define	S_R8	32(%fp)
#define	S_R9	36(%fp)
#define	S_R10	40(%fp)
#define	S_R11	44(%fp)
#define	S_AP	48(%fp)
#define	S_FP	52(%fp)
#define	S_SP	56(%fp)
#define	S_PC	60(%fp)
#define	S_PSL	64(%fp)

# The condition codes.

#define PSL_C	1
#define PSL_V	2
#define PSL_Z	4
#define PSL_N	8
#define PSL_Q  15		# all four
				
#
# Emulation of instruction trapped via SCB vector 0x18. (reserved op)
#
ALTENTRY(unimemu)
	pushl	%r0
	movl	8(%sp),%r0	# get trap address
	movzbl	(%r0),%r0		# fetch insn generating trap
	caseb	%r0,$0x74,$1	# case to jump to address
0:	.word	emodd-0b
	.word	polyd-0b

1:	movl	(%sp)+,%r0	# restore reg
	rsb			# continue fault

#
# switch the code back over to user mode.
# puts the psl + pc (+ jsb return address) on top of user stack.
#
#ifdef EMULATE_INKERNEL
touser:	movl	(%sp),-52(%sp)	# save rsb address on top of new stack
	movl	4(%sp),%r0	# restore saved reg
	addl2	$12,%sp		# pop junk from stack
	pushr	$0x7fff		# save all regs
	movl	%sp,%fp		# new frame pointer
	tstl	-(%sp)		# remember old rsb address
	incl	S_PC		# skip matching insn
	rsb
#else
touser:	mfpr	$PR_USP,%r0	# get user stack pointer
	movl	4(%sp),-68(%r0)	# move already saved %r0
	movl	(%sp),-72(%r0)	# move return address
	movq	12(%sp),-8(%r0)	# move pc + psl
	addl2	$12,%sp		# remove moved fields from stack
	movl	$1f,(%sp)	# change return address
	rei
1:	subl2	$8,%sp		# trapaddr + psl already on stack
	pushr	$0x7ffe		# %r0 already saved
	subl2	$8,%sp		# do not trash %r0 + retaddr
	movab	4(%sp),%fp
	incl	S_PC		# skip matching insn
	rsb
#endif

#
# Restore registers, cleanup and continue
#
goback:	movl	%fp,%sp		# be sure
	popr	$0x7fff		# restore all regs
	rei
	
/*
 * getval: is used by the getval_* functions.  Gets the value specified by the 
 * current operand specifier pointed to by S_PC.  It also increments S_PC.
 */
getval:
	clrq	%r0
	pushr	$(R2+R3+R4+R5+R6)
	movl	S_PC,%r3		# argument address
	extzv	$4,$4,(%r3),%r2	# get mode
	caseb	%r2,$0,$0xf
0:	.word	getval_literal-0b		# 0-3 literal
	.word	getval_literal-0b
	.word	getval_literal-0b
	.word	getval_literal-0b
	.word	2f-0b		# 4 indexed
	.word	getval_reg-0b			# 5 register
	.word	getval_regdefer-0b		# 6 register deferred
	.word	2f-0b		# 7 register deferred
	.word	getval_ai-0b			# 8 autoincrement
	.word	2f-0b		# 9 autoincrement deferred 
	.word	getval_bytedis-0b		# A byte displacement 
	.word	2f-0b		# B byte displacement deferred
	.word	2f-0b		# C word displacement
	.word	2f-0b		# D word displacement deferred
	.word	getval_longdis-0b		# E longword displacement
	.word	2f-0b		# F longword displacement deferred
#ifdef EMULATE_INKERNEL
2:	movab	0f,%r0
	movl	%r2,%r1
	brw	die
0:	.asciz	"getval: missing address mode %d\n"
#else
2:	.word 	0xffff		# reserved operand
#endif

	/*
	 * 0x00-0x03  
	 * Literal mode.  Note:  getval_{d,f}float will *never* use this routine
	 * to get literal values, since they treat them differently (see those routines
	 * for details).
	 */
getval_literal:
	movzbl	(%r3)+,%r0	# correct operand
	brw 4f

	/* 
	 * 0x05  
     * Register mode.  Grab the register number, yank the value out.
	 */
getval_reg:	
	extzv	$0,$4,(%r3),%r2	# Get reg number
	incl	%r3
	ashl	$2,%r2,%r2
	addl3	%fp,%r2,%r5
	bsbw	emul_extract
	brw		4f

	/* 
	 * 0x06
     * Register deferred mode.  Grab the register number, yank the value out,
	 * use that as the address to get the real value.
	 */
getval_regdefer:	
	extzv	$0,$4,(%r3),%r2	# Get reg number
	incl	%r3
	ashl	$2,%r2,%r2
	addl2	%fp,%r2
	movl	(%r2),%r5
	bsbw	emul_extract
	brw		4f

	/* 
	 * 0x08 Autoincrement mode
     * Get the value in the register, use that as the address of our target,
	 * then increment the register.
	 */
getval_ai:
	extzv	$0,$4,(%r3),%r2	# Get reg number
	incl	%r3

	/*
	 * In the case of the register being PC (0xf), this is called immediate mode;
	 * we can treat it the same as any other register, as long as we keep %r3
     * and S_PC in sync.  We do that here.	 
	 */
	movl 	%r3,S_PC

	ashl	$2,%r2,%r2
	addl2	%fp,%r2
	movl	(%r2),%r5
	bsbw	emul_extract
	addl2	%r6,(%r2)

	movl	S_PC,%r3		/* if PC did change, S_PC was changed too */
	brw		4f

	/*
	 * 0xA
	 * Byte displacement mode.
     */	 
getval_bytedis: 
	extzv	$0, $4, (%r3), %r2	# get register 
	incl	%r3
	ashl	$2,%r2,%r2
	addl2	%fp,%r2
	movl	(%r2),%r5
	movzbl	(%r3),%r4
	incl	%r3
	addl2	%r4, %r5
	bsbw	emul_extract
	brw		4f

	/*
	 * 0xE  
	 * Longword displacement mode.
     */	 
getval_longdis: 
	extzv	$0, $4, (%r3), %r2	# get register 
	incl	%r3
	ashl	$2,%r2,%r2
	addl2	%fp,%r2
	movl	(%r2),%r5
	movl	(%r3)+,%r4
	addl2	%r4, %r5
	bsbw	emul_extract

4:	movl	%r3,S_PC
	popr	$(R2+R3+R4+R5+R6)
	rsb

/*
 * emul_extract: used by the getval functions.  This extracts exactly %r6 bytes 
 * from the address in %r5 and places them in %r0 and %r1 (if necessary).
 * 8 is the current maximum length.
 */
emul_extract:
	cmpl $0x8, %r6
	bgeq 1f
	.word 	0xffff		# reserved operand
1:	
	caseb %r6, $0x1, $0x7
0:	.word 1f-0b			# 1: byte
	.word 2f-0b			# 2: word
	.word 9f-0b			# unknown
	.word 4f-0b			# 4: longword
	.word 9f-0b			# unknown
	.word 9f-0b			# unknown
	.word 9f-0b			# unknown
	.word 8f-0b			# 8: quadword

1:	movzbl (%r5), %r0
	rsb

2:	movzwl (%r5), %r0
	rsb

4:	movl (%r5), %r0
	rsb

8:	movq (%r5), %r0
	rsb

9:	
	.word 	0xffff		# reserved operand
	rsb

getval_dfloat:
	clrq	%r0
	pushr	$(R2+R3+R6)	# use %r2+%r3 as scratch reg
	movl	S_PC,%r3		# argument address 
	extzv	$4,$4,(%r3),%r2	# get mode
	caseb	%r2,$0,$0x3
0:	.word	1f-0b		# 0-3 literal
	.word	1f-0b
	.word	1f-0b
	.word	1f-0b

	movl	$0x8, %r6
	bsbw	getval
	brw	4f

1:	insv	(%r3),$0,$3,%r0	# insert fraction
	extzv	$3,$3,(%r3),%r2	# get exponent
	addl2	$128,%r2		# bias the exponent
	insv	%r2,$7,$8,%r0	# insert exponent
	tstb	(%r3)+
	movl	%r3,S_PC
4:
	popr	$(R2+R3+R6)
	rsb

getval_long:
	clrl	%r0
	pushr	$(R6+R1)	
	movl	$0x4, %r6
	bsbw	getval
	popr	$(R6+R1)
	rsb

getval_word:
	clrl	%r0
	pushr	$(R6+R1)
	movl	$0x2, %r6
	bsbw	getval
	popr	$(R6+R1)
	rsb

getval_byte:
	clrl	%r0
	pushr	$(R6+R1)	# use %r2+%r3 as scratch reg
	movl	$0x1, %r6
	bsbw	getval
	popr	$(R6+R1)
	rsb

#
# getaddr_byte get 4 bytes and stores them in %r0. Increases PC.
#
getaddr_byte:
	clrl	%r0
	pushr	$(R2+R3)	# use %r2+%r3 as scratch reg
	movl	S_PC,%r3		# argument address
	extzv	$4,$4,(%r3),%r2	# get mode
	caseb	%r2,$0,$0xf
0:	.word	2f-0b		# 0-3 literal	
	.word	2f-0b
	.word	2f-0b
	.word	2f-0b
	.word	2f-0b		# 4 
	.word	6f-0b		# 5 register
	.word	5f-0b		# 6 deferred
	.word	2f-0b		# 7 autodecr (missing)
	.word	2f-0b		# 8 autoincr (missing)
	.word	2f-0b		# 9 autoincr deferred (missing)
	.word	7f-0b		# 10 byte disp 
	.word	2f-0b		# 11 byte disp deferred (missing)
	.word	8f-0b		# 12 word disp
	.word	2f-0b		# 13 word disp deferred (missing)
	.word	1f-0b		# 14 long disp
	.word	2f-0b		# 15 long disp deferred (missing)
#ifdef EMULATE_INKERNEL
2:	movab	3f,%r0
	movl	%r2,%r1
	brw	die		# reserved operand
3:	.asciz	"getaddr_byte: missing address mode %d\n"
#else
2:	.word	0xffff		# reserved operand
#endif

1:	extzv	$0,$4,(%r3),%r2	# Get reg number
	incl	%r3
	movl	(%fp)[%r2],%r0	# Register contents
	addl2	(%r3),%r0		# add displacement
	cmpl	%r2,$15		# pc?
	bneq	0f		# no, skip
	addl2	$5,%r0		# compensate for displacement size
0:	addl2	$4,%r3		# increase pc
	brw	4f

5:	extzv	$0,$4,(%r3),%r2	# Get reg number
	incl	%r3
	movl	(%fp)[%r2],%r0
	brw	4f

7:	
	extzv	$0, $4, (%r3), %r2	# get register 
	incl	%r3
	movl	(%fp)[%r2],%r0		# Register contents
	pushl	%r4
	cvtbl	(%r3),%r4
	addl2	%r4,%r0			# add displacement
	movl	(%sp)+,%r4
	cmpl	%r2,$15			# pc?
	bneq	0f			# no, skip
	addl2	$2,%r0			# compensate for displacement size
0:	incl	%r3			# increase pc
	brw	4f

8:	
	extzv	$0, $4, (%r3), %r2	# get register 
	incl	%r3
	movl	(%fp)[%r2],%r0		# Register contents
	pushl	%r4
	cvtwl	(%r3),%r4
	addl2	%r4,%r0			# add displacement
	movl	(%sp)+,%r4
	cmpl	%r2,$15			# pc?
	bneq	0f			# no, skip
	addl2	$3,%r0			# compensate for displacement size
0:	addl2	$2,%r3			# increase pc
	brw	4f

6:	extzv	$0,$4,(%r3),%r2	# Get reg number
	incl	%r3
	moval	(%fp)[%r2],%r0

4:	movl	%r3,S_PC
	popr	$(R2+R3)
	rsb

#
# Polynomial calculation, d-float
# Uses d-float instructions, so hopefully d-float is available.
#
# polyd MISSING:
#	- check for bad arguments
#	- set PSL flags
#	- do not use d-float instructions (may be emulated)
#
polyd:	bsbw	touser		# go back to user mode
	bsbw	getval_dfloat	# fetches argument to %r0/%r1
	movq	%r0,%r6
	bsbw	getval_word
	movl	%r0,%r4
	bsbw	getaddr_byte
	movl	%r0,%r3
	clrq	%r0
# Ok, do the real calculation (Horner's method)
0:	addd2	(%r3)+,%r0	# add constant
	tstl	%r4		# more?
	beql	1f		# no, exit
	muld2	%r6,%r0		# multiply with arg
	decl	%r4		# lower degree
	brb	0b

1:	movq	%r0,(%fp)
	clrl	S_R2
	movl	%r3,S_R3
	clrq	S_R4
	brw	goback


#ifdef EMULATE_INKERNEL
# When we end up somewhere we don't want.
die:	pushl	%r1
	pushl	%r0
	calls	$2,_printf
	movl	%fp,sp
	brw	goback		# anything may happen
#endif

# these emodd-related
#define TMPSIZE 0x20	/* temp bytes -- be careful with this! */
#define PRECIS 0x7
#define TMPFRAC1 (%ap)
#define TMPFRAC2 32(%ap)
#define TMPFRACTGT 64(%ap)
#	
# Extended multiply/modulus	
# XXX just EMODD for now
emodd:	bsbw	touser

	/* Clear the condition codes; we will set them as needed later. */
	bicl2 $(PSL_C|PSL_V|PSL_Z|PSL_N), S_PSL

	/* 
	 * We temporarily appropriate ap for the use of TMPFRAC*.
	 */
	pushl %ap	
	subl2 $(3*TMPSIZE), %sp
	movl %sp, %ap

	movc5 $0x0, TMPFRAC1, $0x0, $TMPSIZE, TMPFRAC1 
	movc5 $0x0, TMPFRAC2, $0x0, $TMPSIZE, TMPFRAC2 
	movc5 $0x0, TMPFRACTGT, $0x0, $TMPSIZE, TMPFRACTGT 

	clrl -(%sp)
	movl %sp, %r3		/* %r3 = addr of exp space (1) */
	clrl -(%sp)
	movl %sp, %r5		/* %r5 = addr of exp space (2) */
	subl2 $0x10, %sp		
	movl %sp, %r6		/* %r6 = addr of allocated target space */

	/*
	 * Now we package both numbers up and call fltext_De, which 
	 * will remove the exponent and sign; this will make them
	 * easier to work with.  They will be in TMPFRAC1 and 
	 * TMPFRAC2 when done.
	 */
	bsbw getval_dfloat 	# get operand into %r0 and %r1

	/* Check for sign = 0 and exp = 0; if it is, zeroexit. */
	bicl3 $0x7f, %r0, %r4
	cmpl %r4, $0x0
	bneq 1f
	bsbw getval_byte	# get multiplier extension operand
	bsbw getval_dfloat	# get target operand
	jmp zeroexit
1:

	/* Check for sign = 1 and exp = 0; if it is, do a resopflt. */
	cmpw %r0, $0x8000
	bneq 1f
	bsbw getval_byte	# get multiplier extension operand
	bsbw getval_dfloat 	# get operand into %r0 and %r1
	extzv $0, $0xff, %r0, %r0	# generate a resopflt -- XXX is this ok?
1:	
	movd %r0, TMPFRACTGT
	bicl3 $0xffff7fff, %r0, %r6 # Extract the sign while we're here.
	bsbw getval_byte	# get multiplier extension operand
	movzbl %r0, -(%sp)
	movd %r9, %r0
	pushl %r3
	pushab TMPFRAC1
	movab TMPFRACTGT, -(%sp)
	calls $0x4, fltext_De

	bsbw getval_dfloat 	# get operand into %r0 and %r1

	/* Check for sign = 0 and exp = 0; if it is, zeroexit. */
	bicl3 $0x7f, %r0, %r4
	cmpl %r4, $0x0
	bneq 1f
	bsbw getval_byte	# get multiplier extension operand
	bsbw getval_dfloat	# get target operand
	jmp zeroexit
1:
	/* Check for sign = 1 and exp = 0; if it is, do a resopflt. */
	cmpw %r0, $0x8000
	bneq 1f
	bsbw getval_byte	# get multiplier extension operand
	bsbw getval_dfloat 	# get operand into %r0 and %r1
	extzv $0, $0xff, %r0, %r0		# generate a resopflt -- XXX is this ok?
1:	

	movd %r0, TMPFRACTGT
	bicl3 $0xffff7fff, %r0, %r7 # Extract the sign while we're here.
	movzbl $0x0, -(%sp)	# no multiplier extension here
	pushl %r5
	pushab TMPFRAC2
	movab TMPFRACTGT, -(%sp)
	calls $0x4, fltext_De

	/* first, add exponents */
	addl3 (%r5), (%r3), %r9	/* %r9 = exponent (used later) */
	subl2 $0x80, %r9			/* we are excess-128 */
	
	/*
	 * Let's calculate the target sign.  Signs from multipliers are in %r6 and 
	 * %r7, and both the fraction and integer parts have the same sign.
	 */
	xorl2 %r7, %r6

	pushab TMPFRAC1
	calls $0x1, bitcnt
	movl %r0, %r1			/* %r1 = bitcount of TMPFRAC1 */
	pushab TMPFRAC2
	calls $0x1, bitcnt
	movl %r0, %r2			/* %r2 = bitcount of TMPFRAC2 */

	/*
	 * Now we get ready to multiply.  This multiplies a byte at a time,
	 * converting to double with CVTLD and adding partial results to 
	 * TMPFRACTGT.  There's probably a faster way to do this.
	 */
	clrd TMPFRACTGT
	pushr $0x7fc
	subl2 $0x8, %sp			/* make some temporary space */
	movl %sp, %r1
	subl2 $0x8, %sp
	movl %sp, %r2

	movl $PRECIS, %r5			/* %r5 = TMPFRAC1 byte count */
	movl $PRECIS, %r6			/* %r6 = TMPFRAC2 byte count */
	clrl %r7

1:
#	addl3 %r5, $TMPFRAC1, %r3		/* %r3 - current byte in tmpfrac1 */
	movab TMPFRAC1, %r7
	addl3 %r5, %r7, %r3
#	addl3 %r6, $TMPFRAC2, %r4		/* %r4 - current byte in tmpfrac2 */
	movab TMPFRAC2, %r7
	addl3 %r6, %r7, %r4

	movzbl (%r3), %r10
	movzbl (%r4), %r11
	mull3 %r10, %r11, %r7
	movl %r7, %r3
	cvtld %r7, (%r2)

	subl3 %r5, $0x8, %r8
	subl3 %r6, $0x8, %r9
	addl2 %r8, %r9			
	mull2 $0x8, %r9
	subl2 $0x40, %r9
	blss 9f	

	/* This may be bigger than a longword.  Break it up. */
5:	cmpl %r9, $0x1e
	bleq 6f
	subl2 $0x1e, %r9

	ashl $0x1e, $0x1, %r8
	cvtld %r8, (%r1) 
	muld2 (%r1), (%r2) 
	jmp 5b
6:
	ashl %r9, $0x1, %r8
	cvtld %r8, (%r1)
	muld2 (%r1), (%r2)
	addd2 (%r2), TMPFRACTGT
	
9:
	cmpl %r5, $0x0
	beql 2f
	decl %r5
	jmp 1b
2:	cmpl %r6, $0x0
	beql 3f
	decl %r6
	movl $PRECIS, %r5
	jmp 1b
3:

	/*
	 * At this point, %r9 might not reflect the final exponent we will use;
	 * i.e., we need post-normalization.  Luckily, we still have (in %r7) 
	 * the results from the last individual multiplication handy.  Here 
	 * we calculate how many bits it will take to shift the value in %r7
	 * so that bit 15 = 1.
	 */
	addl2 $0x10, %sp
	movl %r7, 0x14(%sp)	/* move %r7 onto the frame we're about to pop off */
   	popr  $0x7fc

	clrl %r3	/* %r3 = counter */
	movl %r7, %r8		/* %r8 = temp */
1:
	bicl3 $0xffff7fff, %r8, %r5
	bneq 2f
	incl %r3
	ashl $0x1, %r8, %r5
	movl %r5, %r8
	jmp 1b
2:

	/* 
	 * Now we do post-normalization (by subtracting %r3) and
	 * put the exponent (in %r9) into TMPFRACTGT.
	 */
	subl2 %r3, %r9
	insv %r9, $0x7, $0x8, TMPFRACTGT

	bisl2 %r6, TMPFRACTGT	# set the sign

	/*
	 * Now we need to separate.  CVT* won't work in the case of a
	 * >32-bit integer, so we count the integer bits and use ASHQ to
	 * shift them away.
	 */
	cmpl $0x80, %r9
	blss 7f		/* if we are less than 1.0, we can avoid this */
	brw 8f
7:		
	subl3 $0x80, %r9, %r8

	movq TMPFRACTGT, TMPFRAC1
	/*
	 * Check for integer overflow by comparing the integer bit count.
	 * If this is the case, set V in PSL.
	 */
	cmpl %r8, $0x20
	blss 3f
	bisl2 $PSL_V, S_PSL
3:
	cmpl %r8, $0x38
	blss 1f
	/*
	 * In the case where we have more than 55 bits in the integer,
	 * there aren't any bits left for the fraction.  Therefore we're
	 * done here;  TMPFRAC1 is equal to TMPFRACTGT and TMPFRAC2 is 0.
	 */
	movq $0f0.0, TMPFRAC2
	jmp 9f		/* we're done, move on */
1:	
	/*
	 * We do the mod by using ASHQ to shift and truncate the bits.
	 * Before that happens, we have to arrange the bits in a quadword such
	 * that the significance increases from start to finish.
	 */

	movab TMPFRACTGT, %r0
	movab TMPFRAC1, %r1
	movb (%r0), 7(%r1)
	bisb2 $0x80, 7(%r1)
	movw 2(%r0), 5(%r1)
	movw 4(%r0), 3(%r1)
	movb 7(%r0), 2(%r1)
	movb 6(%r0), 1(%r1)

	/* Calculate exactly how many bits to shift. */
	subl3 %r8, $0x40, %r7
	mnegl %r7, %r6
	ashq %r6, TMPFRAC1, %r0			# shift right
	ashq %r7, %r0, TMPFRAC2			# shift left

	/* Now put it back into a D_. */
	movab TMPFRAC2, %r0
	movab TMPFRAC1, %r1
 	extv $0x18, $0x7, 4(%r0), (%r1)
	extzv $0x7, $0x9, TMPFRACTGT, %r2
	insv %r2, $0x7, $0x9, (%r1)

	movw 5(%r0), 2(%r1)
	movw 3(%r0), 4(%r1)
	movw 1(%r0), 6(%r1)

	# we have the integer in TMPFRAC1, now get the fraction in TMPFRAC2
	subd3 TMPFRAC1, TMPFRACTGT, TMPFRAC2
	jmp 9f

8:	
	/*
	 * We are less than 1.0; TMPFRAC1 should be 0, and TMPFRAC2 should
	 * be equal to TMPFRACTGT.
	 */
	movd $0f0.0, TMPFRAC1
	movd TMPFRACTGT, TMPFRAC2
9:			
	/*
	 * We're done. We can use CVTDL here, since EMODD is supposed to
	 * truncate.
	 */
	cvtdl TMPFRAC1, %r4
	bsbw getaddr_byte
	movl %r4, (%r0)
	
	bsbw getaddr_byte
	movq TMPFRAC2, (%r0)
	movd TMPFRAC2, %r0		/* move this here so we can test it later */

	/* Clean up sp. */

	addl2 $0x74, %sp	
	movl (%sp)+, %ap

	/*
	 * Now set condition codes.  We know Z == 0; C is always 0; and V
	 * is set above as necessary.  Check to see if TMPFRAC2 is
	 * negative; if it is, set N.
	 */
	tstd %r0
	bgeq 1f /* branch if N == 0 */
	bisl2 $PSL_N, S_PSL
1:
	brw goback
zeroexit:
	/* Z == 1, everything else has been cleared already */
	bisl2 $PSL_Z, S_PSL
	bsbw getaddr_byte
	movl $0x0, (%r0)
	bsbw getaddr_byte
	movd $0f0, (%r0)
	brw goback



/* 
 * bitcnt: counts significant bits backwards in a quadword 
 * returns number of bits, unless there aren't any;
 * in that case it will return $0xffffffff
 */
bitcnt:	
	.word 0xffe	/* %r1-%r12 */

	/* 
	 * Our goal is to factor a common power of 2 out of each of the
	 * two factors involved in the multiplication.  Once we have that,
	 * we can multiply them as integers.  More below.
	 * Right now we are counting bits, starting from the highest octet
	 * of each (the *least* significant bit at this point!) and doing
	 * FFSes until we find a bit set.
	 */ 
	movl 4(%ap), %r0
	movl $0x8, %r1
1:	decl %r1
	addl3 %r1, %r0, %r4
	movzbl (%r4), %r2
	ffs $0, $0x20, %r2, %r3
	bneq 2f		/* if we found a bit, Z == 0, continue */
	cmpl %r1, $0x0
	jeql 3f /* if %r1 is zero and there's no bit set, qw is 0 */
	jmp 1b			/* else continue with the loop */

2:	/* 
	 * We found a bit; its position in the byte is in %r3, and %r1 is the
	 * position of the byte in the quadword.
	 */
	subl3 %r3, $0x8, %r0	
	ashl $0x5, %r1, %r2
	addl2 %r2, %r0
	ret

3:	/* this quadword is 0 */
	movl $0xffffffff, %r0
	ret


/*
 * The fltext_X routines separate fraction and exponent* bits. 
 * They return (via %r0) the amount of bits in the fraction.
 *
 * *: exponents are left in excess-128 form
 *        D_ floating point first word:
 *         F E      7 6     0
 *        +-+--------+-------+
 * sign-> |s|exponent| fract.|  (10-3F = fraction bits)
 *        +-+--------+-------+
 *        Significance order: 0-6, 10-1F, 20-2F, 30-3F
 * 
 * The fourth argument to fltext_De is the eight extra bits for use
 * in EMOD*, et al.  If these bits are not in use, specify 0.
 */
fltext_De:
	.word 0x831	# %r0 %r1 %r2 %r3 %r4 ap (no return)

	movl 0x4(%ap), %r0	# %r0 - addr of source
	movl 0x8(%ap), %r1	# %r1 - addr of fraction destination

	movb (%r0), (%r1)
	bisb2 $0x80, (%r1)+	# This is the hidden bit.

	movb 3(%r0), (%r1)+
	movb 2(%r0), (%r1)+
	movb 5(%r0), (%r1)+
	movb 4(%r0), (%r1)+
	movb 7(%r0), (%r1)+
	movb 6(%r0), (%r1)+

	/*
	 * if there are extension bits (EMOD EDIV etc.) they are 
	 * low-order
	 */
	movb 0x10(%ap), (%r1)	

	movl 0x4(%ap), %r0	# %r0 - addr of source
	movl 0xc(%ap), %r2	# %r2 - addr of exponent destination
	extzv $0x7, $0x8, (%r0), (%r2)		# get exponent out
	ret

