Home | History | Annotate | Line # | Download | only in vax
      1 /*	$NetBSD: unimpl_emul.S,v 1.5 2024/04/16 00:03:30 kalvisd Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2001 Brandon Creighton.  All rights reserved.
      5  * Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of the author may not be used to endorse or promote products
     16  *    derived from this software without specific prior written permission
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 
     31 #include <machine/asm.h>
     32 #include "assym.h"
     33 
     34 # Only intended for debugging emulation code (security hole)
     35 #undef	EMULATE_INKERNEL
     36 
     37 # Defines to fetch register operands
     38 #define	S_R0	(%fp)
     39 #define	S_R1	4(%fp)
     40 #define	S_R2	8(%fp)
     41 #define	S_R3	12(%fp)
     42 #define	S_R4	16(%fp)
     43 #define	S_R5	20(%fp)
     44 #define	S_R6	24(%fp)
     45 #define	S_R7	28(%fp)
     46 #define	S_R8	32(%fp)
     47 #define	S_R9	36(%fp)
     48 #define	S_R10	40(%fp)
     49 #define	S_R11	44(%fp)
     50 #define	S_AP	48(%fp)
     51 #define	S_FP	52(%fp)
     52 #define	S_SP	56(%fp)
     53 #define	S_PC	60(%fp)
     54 #define	S_PSL	64(%fp)
     55 
     56 # The condition codes.
     57 
     58 #define PSL_C	1
     59 #define PSL_V	2
     60 #define PSL_Z	4
     61 #define PSL_N	8
     62 #define PSL_Q  15		# all four
     63 
     64 #
     65 # Emulation of instruction trapped via SCB vector 0x18. (reserved op)
     66 #
     67 ALTENTRY(unimemu)
     68 	pushl	%r0
     69 	movl	8(%sp),%r0	# get trap address
     70 	movzbl	(%r0),%r0		# fetch insn generating trap
     71 	caseb	%r0,$0x74,$1	# case to jump to address
     72 0:	.word	emodd-0b
     73 	.word	polyd-0b
     74 
     75 1:	movl	(%sp)+,%r0	# restore reg
     76 	rsb			# continue fault
     77 
     78 #
     79 # switch the code back over to user mode.
     80 # puts the psl + pc (+ jsb return address) on top of user stack.
     81 #
     82 #ifdef EMULATE_INKERNEL
     83 touser:	movl	(%sp),-52(%sp)	# save rsb address on top of new stack
     84 	movl	4(%sp),%r0	# restore saved reg
     85 	addl2	$12,%sp		# pop junk from stack
     86 	pushr	$0x7fff		# save all regs
     87 	movl	%sp,%fp		# new frame pointer
     88 	tstl	-(%sp)		# remember old rsb address
     89 	incl	S_PC		# skip matching insn
     90 	rsb
     91 #else
     92 touser:	mfpr	$PR_USP,%r0	# get user stack pointer
     93 	movl	4(%sp),-68(%r0)	# move already saved %r0
     94 	movl	(%sp),-72(%r0)	# move return address
     95 	movq	12(%sp),-8(%r0)	# move pc + psl
     96 	addl2	$12,%sp		# remove moved fields from stack
     97 	movl	$1f,(%sp)	# change return address
     98 	rei
     99 1:	subl2	$8,%sp		# trapaddr + psl already on stack
    100 	pushr	$0x7ffe		# %r0 already saved
    101 	subl2	$8,%sp		# do not trash %r0 + retaddr
    102 	movab	4(%sp),%fp
    103 	incl	S_PC		# skip matching insn
    104 	rsb
    105 #endif
    106 
    107 #
    108 # Restore registers, cleanup and continue
    109 #
    110 goback:	movl	%fp,%sp		# be sure
    111 	popr	$0x7fff		# restore all regs
    112 	rei
    113 
    114 /*
    115  * getval: is used by the getval_* functions.  Gets the value specified by the
    116  * current operand specifier pointed to by S_PC.  It also increments S_PC.
    117  */
    118 getval:
    119 	clrq	%r0
    120 	pushr	$(R2+R3+R4+R5+R6)
    121 	movl	S_PC,%r3		# argument address
    122 	extzv	$4,$4,(%r3),%r2	# get mode
    123 	caseb	%r2,$0,$0xf
    124 0:	.word	getval_literal-0b		# 0-3 literal
    125 	.word	getval_literal-0b
    126 	.word	getval_literal-0b
    127 	.word	getval_literal-0b
    128 	.word	2f-0b		# 4 indexed
    129 	.word	getval_reg-0b			# 5 register
    130 	.word	getval_regdefer-0b		# 6 register deferred
    131 	.word	2f-0b		# 7 register deferred
    132 	.word	getval_ai-0b			# 8 autoincrement
    133 	.word	2f-0b		# 9 autoincrement deferred
    134 	.word	getval_bytedis-0b		# A byte displacement
    135 	.word	2f-0b		# B byte displacement deferred
    136 	.word	2f-0b		# C word displacement
    137 	.word	2f-0b		# D word displacement deferred
    138 	.word	getval_longdis-0b		# E longword displacement
    139 	.word	2f-0b		# F longword displacement deferred
    140 #ifdef EMULATE_INKERNEL
    141 2:	movab	0f,%r0
    142 	movl	%r2,%r1
    143 	brw	die
    144 0:	.asciz	"getval: missing address mode %d\n"
    145 #else
    146 2:	.word 	0xffff		# reserved operand
    147 #endif
    148 
    149 	/*
    150 	 * 0x00-0x03
    151 	 * Literal mode.  Note:  getval_{d,f}float will *never* use this routine
    152 	 * to get literal values, since they treat them differently (see those routines
    153 	 * for details).
    154 	 */
    155 getval_literal:
    156 	movzbl	(%r3)+,%r0	# correct operand
    157 	brw 4f
    158 
    159 	/*
    160 	 * 0x05
    161      * Register mode.  Grab the register number, yank the value out.
    162 	 */
    163 getval_reg:
    164 	extzv	$0,$4,(%r3),%r2	# Get reg number
    165 	incl	%r3
    166 	ashl	$2,%r2,%r2
    167 	addl3	%fp,%r2,%r5
    168 	bsbw	emul_extract
    169 	brw		4f
    170 
    171 	/*
    172 	 * 0x06
    173      * Register deferred mode.  Grab the register number, yank the value out,
    174 	 * use that as the address to get the real value.
    175 	 */
    176 getval_regdefer:
    177 	extzv	$0,$4,(%r3),%r2	# Get reg number
    178 	incl	%r3
    179 	ashl	$2,%r2,%r2
    180 	addl2	%fp,%r2
    181 	movl	(%r2),%r5
    182 	bsbw	emul_extract
    183 	brw		4f
    184 
    185 	/*
    186 	 * 0x08 Autoincrement mode
    187      * Get the value in the register, use that as the address of our target,
    188 	 * then increment the register.
    189 	 */
    190 getval_ai:
    191 	extzv	$0,$4,(%r3),%r2	# Get reg number
    192 	incl	%r3
    193 
    194 	/*
    195 	 * In the case of the register being PC (0xf), this is called immediate mode;
    196 	 * we can treat it the same as any other register, as long as we keep %r3
    197      * and S_PC in sync.  We do that here.
    198 	 */
    199 	movl 	%r3,S_PC
    200 
    201 	ashl	$2,%r2,%r2
    202 	addl2	%fp,%r2
    203 	movl	(%r2),%r5
    204 	bsbw	emul_extract
    205 	addl2	%r6,(%r2)
    206 
    207 	movl	S_PC,%r3		/* if PC did change, S_PC was changed too */
    208 	brw		4f
    209 
    210 	/*
    211 	 * 0xA
    212 	 * Byte displacement mode.
    213      */
    214 getval_bytedis:
    215 	extzv	$0, $4, (%r3), %r2	# get register
    216 	incl	%r3
    217 	ashl	$2,%r2,%r2
    218 	addl2	%fp,%r2
    219 	movl	(%r2),%r5
    220 	movzbl	(%r3),%r4
    221 	incl	%r3
    222 	addl2	%r4, %r5
    223 	bsbw	emul_extract
    224 	brw		4f
    225 
    226 	/*
    227 	 * 0xE
    228 	 * Longword displacement mode.
    229      */
    230 getval_longdis:
    231 	extzv	$0, $4, (%r3), %r2	# get register
    232 	incl	%r3
    233 	ashl	$2,%r2,%r2
    234 	addl2	%fp,%r2
    235 	movl	(%r2),%r5
    236 	movl	(%r3)+,%r4
    237 	addl2	%r4, %r5
    238 	bsbw	emul_extract
    239 
    240 4:	movl	%r3,S_PC
    241 	popr	$(R2+R3+R4+R5+R6)
    242 	rsb
    243 
    244 /*
    245  * emul_extract: used by the getval functions.  This extracts exactly %r6 bytes
    246  * from the address in %r5 and places them in %r0 and %r1 (if necessary).
    247  * 8 is the current maximum length.
    248  */
    249 emul_extract:
    250 	cmpl $0x8, %r6
    251 	bgeq 1f
    252 	.word 	0xffff		# reserved operand
    253 1:
    254 	caseb %r6, $0x1, $0x7
    255 0:	.word 1f-0b			# 1: byte
    256 	.word 2f-0b			# 2: word
    257 	.word 9f-0b			# unknown
    258 	.word 4f-0b			# 4: longword
    259 	.word 9f-0b			# unknown
    260 	.word 9f-0b			# unknown
    261 	.word 9f-0b			# unknown
    262 	.word 8f-0b			# 8: quadword
    263 
    264 1:	movzbl (%r5), %r0
    265 	rsb
    266 
    267 2:	movzwl (%r5), %r0
    268 	rsb
    269 
    270 4:	movl (%r5), %r0
    271 	rsb
    272 
    273 8:	movq (%r5), %r0
    274 	rsb
    275 
    276 9:
    277 	.word 	0xffff		# reserved operand
    278 	rsb
    279 
    280 getval_dfloat:
    281 	clrq	%r0
    282 	pushr	$(R2+R3+R6)	# use %r2+%r3 as scratch reg
    283 	movl	S_PC,%r3		# argument address
    284 	extzv	$4,$4,(%r3),%r2	# get mode
    285 	caseb	%r2,$0,$0x3
    286 0:	.word	1f-0b		# 0-3 literal
    287 	.word	1f-0b
    288 	.word	1f-0b
    289 	.word	1f-0b
    290 
    291 	movl	$0x8, %r6
    292 	bsbw	getval
    293 	brw	4f
    294 
    295 1:	insv	(%r3),$0,$3,%r0	# insert fraction
    296 	extzv	$3,$3,(%r3),%r2	# get exponent
    297 	addl2	$128,%r2		# bias the exponent
    298 	insv	%r2,$7,$8,%r0	# insert exponent
    299 	tstb	(%r3)+
    300 	movl	%r3,S_PC
    301 4:
    302 	popr	$(R2+R3+R6)
    303 	rsb
    304 
    305 getval_long:
    306 	clrl	%r0
    307 	pushr	$(R6+R1)
    308 	movl	$0x4, %r6
    309 	bsbw	getval
    310 	popr	$(R6+R1)
    311 	rsb
    312 
    313 getval_word:
    314 	clrl	%r0
    315 	pushr	$(R6+R1)
    316 	movl	$0x2, %r6
    317 	bsbw	getval
    318 	popr	$(R6+R1)
    319 	rsb
    320 
    321 getval_byte:
    322 	clrl	%r0
    323 	pushr	$(R6+R1)	# use %r2+%r3 as scratch reg
    324 	movl	$0x1, %r6
    325 	bsbw	getval
    326 	popr	$(R6+R1)
    327 	rsb
    328 
    329 #
    330 # getaddr_byte get 4 bytes and stores them in %r0. Increases PC.
    331 #
    332 getaddr_byte:
    333 	clrl	%r0
    334 	pushr	$(R2+R3)	# use %r2+%r3 as scratch reg
    335 	movl	S_PC,%r3		# argument address
    336 	extzv	$4,$4,(%r3),%r2	# get mode
    337 	caseb	%r2,$0,$0xf
    338 0:	.word	2f-0b		# 0-3 literal
    339 	.word	2f-0b
    340 	.word	2f-0b
    341 	.word	2f-0b
    342 	.word	2f-0b		# 4
    343 	.word	6f-0b		# 5 register
    344 	.word	5f-0b		# 6 deferred
    345 	.word	2f-0b		# 7 autodecr (missing)
    346 	.word	2f-0b		# 8 autoincr (missing)
    347 	.word	2f-0b		# 9 autoincr deferred (missing)
    348 	.word	7f-0b		# 10 byte disp
    349 	.word	2f-0b		# 11 byte disp deferred (missing)
    350 	.word	8f-0b		# 12 word disp
    351 	.word	2f-0b		# 13 word disp deferred (missing)
    352 	.word	1f-0b		# 14 long disp
    353 	.word	2f-0b		# 15 long disp deferred (missing)
    354 #ifdef EMULATE_INKERNEL
    355 2:	movab	3f,%r0
    356 	movl	%r2,%r1
    357 	brw	die		# reserved operand
    358 3:	.asciz	"getaddr_byte: missing address mode %d\n"
    359 #else
    360 2:	.word	0xffff		# reserved operand
    361 #endif
    362 
    363 1:	extzv	$0,$4,(%r3),%r2	# Get reg number
    364 	incl	%r3
    365 	movl	(%fp)[%r2],%r0	# Register contents
    366 	addl2	(%r3),%r0		# add displacement
    367 	cmpl	%r2,$15		# pc?
    368 	bneq	0f		# no, skip
    369 	addl2	$5,%r0		# compensate for displacement size
    370 0:	addl2	$4,%r3		# increase pc
    371 	brw	4f
    372 
    373 5:	extzv	$0,$4,(%r3),%r2	# Get reg number
    374 	incl	%r3
    375 	movl	(%fp)[%r2],%r0
    376 	brw	4f
    377 
    378 7:
    379 	extzv	$0, $4, (%r3), %r2	# get register
    380 	incl	%r3
    381 	movl	(%fp)[%r2],%r0		# Register contents
    382 	pushl	%r4
    383 	cvtbl	(%r3),%r4
    384 	addl2	%r4,%r0			# add displacement
    385 	movl	(%sp)+,%r4
    386 	cmpl	%r2,$15			# pc?
    387 	bneq	0f			# no, skip
    388 	addl2	$2,%r0			# compensate for displacement size
    389 0:	incl	%r3			# increase pc
    390 	brw	4f
    391 
    392 8:
    393 	extzv	$0, $4, (%r3), %r2	# get register
    394 	incl	%r3
    395 	movl	(%fp)[%r2],%r0		# Register contents
    396 	pushl	%r4
    397 	cvtwl	(%r3),%r4
    398 	addl2	%r4,%r0			# add displacement
    399 	movl	(%sp)+,%r4
    400 	cmpl	%r2,$15			# pc?
    401 	bneq	0f			# no, skip
    402 	addl2	$3,%r0			# compensate for displacement size
    403 0:	addl2	$2,%r3			# increase pc
    404 	brw	4f
    405 
    406 6:	extzv	$0,$4,(%r3),%r2	# Get reg number
    407 	incl	%r3
    408 	moval	(%fp)[%r2],%r0
    409 
    410 4:	movl	%r3,S_PC
    411 	popr	$(R2+R3)
    412 	rsb
    413 
    414 #
    415 # Polynomial calculation, d-float
    416 # Uses d-float instructions, so hopefully d-float is available.
    417 #
    418 # polyd MISSING:
    419 #	- check for bad arguments
    420 #	- set PSL flags
    421 #	- do not use d-float instructions (may be emulated)
    422 #
    423 polyd:	bsbw	touser		# go back to user mode
    424 	bsbw	getval_dfloat	# fetches argument to %r0/%r1
    425 	movq	%r0,%r6
    426 	bsbw	getval_word
    427 	movl	%r0,%r4
    428 	bsbw	getaddr_byte
    429 	movl	%r0,%r3
    430 	clrq	%r0
    431 # Ok, do the real calculation (Horner's method)
    432 0:	addd2	(%r3)+,%r0	# add constant
    433 	tstl	%r4		# more?
    434 	beql	1f		# no, exit
    435 	muld2	%r6,%r0		# multiply with arg
    436 	decl	%r4		# lower degree
    437 	brb	0b
    438 
    439 1:	movq	%r0,(%fp)
    440 	clrl	S_R2
    441 	movl	%r3,S_R3
    442 	clrq	S_R4
    443 	brw	goback
    444 
    445 
    446 #ifdef EMULATE_INKERNEL
    447 # When we end up somewhere we don't want.
    448 die:	pushl	%r1
    449 	pushl	%r0
    450 	calls	$2,_printf
    451 	movl	%fp,sp
    452 	brw	goback		# anything may happen
    453 #endif
    454 
    455 # these emodd-related
    456 #define TMPSIZE 0x20	/* temp bytes -- be careful with this! */
    457 #define PRECIS 0x7
    458 #define TMPFRAC1 (%ap)
    459 #define TMPFRAC2 32(%ap)
    460 #define TMPFRACTGT 64(%ap)
    461 #
    462 # Extended multiply/modulus
    463 # XXX just EMODD for now
    464 emodd:	bsbw	touser
    465 
    466 	/* Clear the condition codes; we will set them as needed later. */
    467 	bicl2 $(PSL_C|PSL_V|PSL_Z|PSL_N), S_PSL
    468 
    469 	/*
    470 	 * We temporarily appropriate ap for the use of TMPFRAC*.
    471 	 */
    472 	pushl %ap
    473 	subl2 $(3*TMPSIZE), %sp
    474 	movl %sp, %ap
    475 
    476 	movc5 $0x0, TMPFRAC1, $0x0, $TMPSIZE, TMPFRAC1
    477 	movc5 $0x0, TMPFRAC2, $0x0, $TMPSIZE, TMPFRAC2
    478 	movc5 $0x0, TMPFRACTGT, $0x0, $TMPSIZE, TMPFRACTGT
    479 
    480 	clrl -(%sp)
    481 	movl %sp, %r3		/* %r3 = addr of exp space (1) */
    482 	clrl -(%sp)
    483 	movl %sp, %r5		/* %r5 = addr of exp space (2) */
    484 	subl2 $0x10, %sp
    485 	movl %sp, %r6		/* %r6 = addr of allocated target space */
    486 
    487 	/*
    488 	 * Now we package both numbers up and call fltext_De, which
    489 	 * will remove the exponent and sign; this will make them
    490 	 * easier to work with.  They will be in TMPFRAC1 and
    491 	 * TMPFRAC2 when done.
    492 	 */
    493 	bsbw getval_dfloat 	# get operand into %r0 and %r1
    494 
    495 	/* Check for sign = 0 and exp = 0; if it is, zeroexit. */
    496 	bicl3 $0x7f, %r0, %r4
    497 	cmpl %r4, $0x0
    498 	bneq 1f
    499 	bsbw getval_byte	# get multiplier extension operand
    500 	bsbw getval_dfloat	# get target operand
    501 	jmp zeroexit
    502 1:
    503 
    504 	/* Check for sign = 1 and exp = 0; if it is, do a resopflt. */
    505 	cmpw %r0, $0x8000
    506 	bneq 1f
    507 	bsbw getval_byte	# get multiplier extension operand
    508 	bsbw getval_dfloat 	# get operand into %r0 and %r1
    509 	extzv $0, $0xff, %r0, %r0	# generate a resopflt -- XXX is this ok?
    510 1:
    511 	movd %r0, TMPFRACTGT
    512 	bicl3 $0xffff7fff, %r0, %r6 # Extract the sign while we're here.
    513 	bsbw getval_byte	# get multiplier extension operand
    514 	movzbl %r0, -(%sp)
    515 	movd %r9, %r0
    516 	pushl %r3
    517 	pushab TMPFRAC1
    518 	movab TMPFRACTGT, -(%sp)
    519 	calls $0x4, fltext_De
    520 
    521 	bsbw getval_dfloat 	# get operand into %r0 and %r1
    522 
    523 	/* Check for sign = 0 and exp = 0; if it is, zeroexit. */
    524 	bicl3 $0x7f, %r0, %r4
    525 	cmpl %r4, $0x0
    526 	bneq 1f
    527 	bsbw getval_byte	# get multiplier extension operand
    528 	bsbw getval_dfloat	# get target operand
    529 	jmp zeroexit
    530 1:
    531 	/* Check for sign = 1 and exp = 0; if it is, do a resopflt. */
    532 	cmpw %r0, $0x8000
    533 	bneq 1f
    534 	bsbw getval_byte	# get multiplier extension operand
    535 	bsbw getval_dfloat 	# get operand into %r0 and %r1
    536 	extzv $0, $0xff, %r0, %r0		# generate a resopflt -- XXX is this ok?
    537 1:
    538 
    539 	movd %r0, TMPFRACTGT
    540 	bicl3 $0xffff7fff, %r0, %r7 # Extract the sign while we're here.
    541 	movzbl $0x0, -(%sp)	# no multiplier extension here
    542 	pushl %r5
    543 	pushab TMPFRAC2
    544 	movab TMPFRACTGT, -(%sp)
    545 	calls $0x4, fltext_De
    546 
    547 	/* first, add exponents */
    548 	addl3 (%r5), (%r3), %r9	/* %r9 = exponent (used later) */
    549 	subl2 $0x80, %r9			/* we are excess-128 */
    550 
    551 	/*
    552 	 * Let's calculate the target sign.  Signs from multipliers are in %r6 and
    553 	 * %r7, and both the fraction and integer parts have the same sign.
    554 	 */
    555 	xorl2 %r7, %r6
    556 
    557 	pushab TMPFRAC1
    558 	calls $0x1, bitcnt
    559 	movl %r0, %r1			/* %r1 = bitcount of TMPFRAC1 */
    560 	pushab TMPFRAC2
    561 	calls $0x1, bitcnt
    562 	movl %r0, %r2			/* %r2 = bitcount of TMPFRAC2 */
    563 
    564 	/*
    565 	 * Now we get ready to multiply.  This multiplies a byte at a time,
    566 	 * converting to double with CVTLD and adding partial results to
    567 	 * TMPFRACTGT.  There's probably a faster way to do this.
    568 	 */
    569 	clrd TMPFRACTGT
    570 	pushr $0x7fc
    571 	subl2 $0x8, %sp			/* make some temporary space */
    572 	movl %sp, %r1
    573 	subl2 $0x8, %sp
    574 	movl %sp, %r2
    575 
    576 	movl $PRECIS, %r5			/* %r5 = TMPFRAC1 byte count */
    577 	movl $PRECIS, %r6			/* %r6 = TMPFRAC2 byte count */
    578 	clrl %r7
    579 
    580 1:
    581 #	addl3 %r5, $TMPFRAC1, %r3		/* %r3 - current byte in tmpfrac1 */
    582 	movab TMPFRAC1, %r7
    583 	addl3 %r5, %r7, %r3
    584 #	addl3 %r6, $TMPFRAC2, %r4		/* %r4 - current byte in tmpfrac2 */
    585 	movab TMPFRAC2, %r7
    586 	addl3 %r6, %r7, %r4
    587 
    588 	movzbl (%r3), %r10
    589 	movzbl (%r4), %r11
    590 	mull3 %r10, %r11, %r7
    591 	movl %r7, %r3
    592 	cvtld %r7, (%r2)
    593 
    594 	subl3 %r5, $0x8, %r8
    595 	subl3 %r6, $0x8, %r9
    596 	addl2 %r8, %r9
    597 	mull2 $0x8, %r9
    598 	subl2 $0x40, %r9
    599 	blss 9f
    600 
    601 	/* This may be bigger than a longword.  Break it up. */
    602 5:	cmpl %r9, $0x1e
    603 	bleq 6f
    604 	subl2 $0x1e, %r9
    605 
    606 	ashl $0x1e, $0x1, %r8
    607 	cvtld %r8, (%r1)
    608 	muld2 (%r1), (%r2)
    609 	jmp 5b
    610 6:
    611 	ashl %r9, $0x1, %r8
    612 	cvtld %r8, (%r1)
    613 	muld2 (%r1), (%r2)
    614 	addd2 (%r2), TMPFRACTGT
    615 
    616 9:
    617 	cmpl %r5, $0x0
    618 	beql 2f
    619 	decl %r5
    620 	jmp 1b
    621 2:	cmpl %r6, $0x0
    622 	beql 3f
    623 	decl %r6
    624 	movl $PRECIS, %r5
    625 	jmp 1b
    626 3:
    627 
    628 	/*
    629 	 * At this point, %r9 might not reflect the final exponent we will use;
    630 	 * i.e., we need post-normalization.  Luckily, we still have (in %r7)
    631 	 * the results from the last individual multiplication handy.  Here
    632 	 * we calculate how many bits it will take to shift the value in %r7
    633 	 * so that bit 15 = 1.
    634 	 */
    635 	addl2 $0x10, %sp
    636 	movl %r7, 0x14(%sp)	/* move %r7 onto the frame we're about to pop off */
    637    	popr  $0x7fc
    638 
    639 	clrl %r3	/* %r3 = counter */
    640 	movl %r7, %r8		/* %r8 = temp */
    641 1:
    642 	bicl3 $0xffff7fff, %r8, %r5
    643 	bneq 2f
    644 	incl %r3
    645 	ashl $0x1, %r8, %r5
    646 	movl %r5, %r8
    647 	jmp 1b
    648 2:
    649 
    650 	/*
    651 	 * Now we do post-normalization (by subtracting %r3) and
    652 	 * put the exponent (in %r9) into TMPFRACTGT.
    653 	 */
    654 	subl2 %r3, %r9
    655 	insv %r9, $0x7, $0x8, TMPFRACTGT
    656 
    657 	bisl2 %r6, TMPFRACTGT	# set the sign
    658 
    659 	/*
    660 	 * Now we need to separate.  CVT* won't work in the case of a
    661 	 * >32-bit integer, so we count the integer bits and use ASHQ to
    662 	 * shift them away.
    663 	 */
    664 	cmpl $0x80, %r9
    665 	blss 7f		/* if we are less than 1.0, we can avoid this */
    666 	brw 8f
    667 7:
    668 	subl3 $0x80, %r9, %r8
    669 
    670 	movq TMPFRACTGT, TMPFRAC1
    671 	/*
    672 	 * Check for integer overflow by comparing the integer bit count.
    673 	 * If this is the case, set V in PSL.
    674 	 */
    675 	cmpl %r8, $0x20
    676 	blss 3f
    677 	bisl2 $PSL_V, S_PSL
    678 3:
    679 	cmpl %r8, $0x38
    680 	blss 1f
    681 	/*
    682 	 * In the case where we have more than 55 bits in the integer,
    683 	 * there aren't any bits left for the fraction.  Therefore we're
    684 	 * done here;  TMPFRAC1 is equal to TMPFRACTGT and TMPFRAC2 is 0.
    685 	 */
    686 	movq $0d0.0, TMPFRAC2
    687 	jmp 9f		/* we're done, move on */
    688 1:
    689 	/*
    690 	 * We do the mod by using ASHQ to shift and truncate the bits.
    691 	 * Before that happens, we have to arrange the bits in a quadword such
    692 	 * that the significance increases from start to finish.
    693 	 */
    694 
    695 	movab TMPFRACTGT, %r0
    696 	movab TMPFRAC1, %r1
    697 	movb (%r0), 7(%r1)
    698 	bisb2 $0x80, 7(%r1)
    699 	movw 2(%r0), 5(%r1)
    700 	movw 4(%r0), 3(%r1)
    701 	movb 7(%r0), 2(%r1)
    702 	movb 6(%r0), 1(%r1)
    703 
    704 	/* Calculate exactly how many bits to shift. */
    705 	subl3 %r8, $0x40, %r7
    706 	mnegl %r7, %r6
    707 	ashq %r6, TMPFRAC1, %r0			# shift right
    708 	ashq %r7, %r0, TMPFRAC2			# shift left
    709 
    710 	/* Now put it back into a D_. */
    711 	movab TMPFRAC2, %r0
    712 	movab TMPFRAC1, %r1
    713  	extv $0x18, $0x7, 4(%r0), (%r1)
    714 	extzv $0x7, $0x9, TMPFRACTGT, %r2
    715 	insv %r2, $0x7, $0x9, (%r1)
    716 
    717 	movw 5(%r0), 2(%r1)
    718 	movw 3(%r0), 4(%r1)
    719 	movw 1(%r0), 6(%r1)
    720 
    721 	# we have the integer in TMPFRAC1, now get the fraction in TMPFRAC2
    722 	subd3 TMPFRAC1, TMPFRACTGT, TMPFRAC2
    723 	jmp 9f
    724 
    725 8:
    726 	/*
    727 	 * We are less than 1.0; TMPFRAC1 should be 0, and TMPFRAC2 should
    728 	 * be equal to TMPFRACTGT.
    729 	 */
    730 	movd $0d0.0, TMPFRAC1
    731 	movd TMPFRACTGT, TMPFRAC2
    732 9:
    733 	/*
    734 	 * We're done. We can use CVTDL here, since EMODD is supposed to
    735 	 * truncate.
    736 	 */
    737 	cvtdl TMPFRAC1, %r4
    738 	bsbw getaddr_byte
    739 	movl %r4, (%r0)
    740 
    741 	bsbw getaddr_byte
    742 	movq TMPFRAC2, (%r0)
    743 	movd TMPFRAC2, %r0		/* move this here so we can test it later */
    744 
    745 	/* Clean up sp. */
    746 
    747 	addl2 $0x74, %sp
    748 	movl (%sp)+, %ap
    749 
    750 	/*
    751 	 * Now set condition codes.  We know Z == 0; C is always 0; and V
    752 	 * is set above as necessary.  Check to see if TMPFRAC2 is
    753 	 * negative; if it is, set N.
    754 	 */
    755 	tstd %r0
    756 	bgeq 1f /* branch if N == 0 */
    757 	bisl2 $PSL_N, S_PSL
    758 1:
    759 	brw goback
    760 zeroexit:
    761 	/* Z == 1, everything else has been cleared already */
    762 	bisl2 $PSL_Z, S_PSL
    763 	bsbw getaddr_byte
    764 	movl $0x0, (%r0)
    765 	bsbw getaddr_byte
    766 	movd $0d0, (%r0)
    767 	brw goback
    768 
    769 
    770 
    771 /*
    772  * bitcnt: counts significant bits backwards in a quadword
    773  * returns number of bits, unless there aren't any;
    774  * in that case it will return $0xffffffff
    775  */
    776 bitcnt:
    777 	.word 0xffe	/* %r1-%r12 */
    778 
    779 	/*
    780 	 * Our goal is to factor a common power of 2 out of each of the
    781 	 * two factors involved in the multiplication.  Once we have that,
    782 	 * we can multiply them as integers.  More below.
    783 	 * Right now we are counting bits, starting from the highest octet
    784 	 * of each (the *least* significant bit at this point!) and doing
    785 	 * FFSes until we find a bit set.
    786 	 */
    787 	movl 4(%ap), %r0
    788 	movl $0x8, %r1
    789 1:	decl %r1
    790 	addl3 %r1, %r0, %r4
    791 	movzbl (%r4), %r2
    792 	ffs $0, $0x20, %r2, %r3
    793 	bneq 2f		/* if we found a bit, Z == 0, continue */
    794 	cmpl %r1, $0x0
    795 	jeql 3f /* if %r1 is zero and there's no bit set, qw is 0 */
    796 	jmp 1b			/* else continue with the loop */
    797 
    798 2:	/*
    799 	 * We found a bit; its position in the byte is in %r3, and %r1 is the
    800 	 * position of the byte in the quadword.
    801 	 */
    802 	subl3 %r3, $0x8, %r0
    803 	ashl $0x5, %r1, %r2
    804 	addl2 %r2, %r0
    805 	ret
    806 
    807 3:	/* this quadword is 0 */
    808 	movl $0xffffffff, %r0
    809 	ret
    810 
    811 
    812 /*
    813  * The fltext_X routines separate fraction and exponent* bits.
    814  * They return (via %r0) the amount of bits in the fraction.
    815  *
    816  * *: exponents are left in excess-128 form
    817  *        D_ floating point first word:
    818  *         F E      7 6     0
    819  *        +-+--------+-------+
    820  * sign-> |s|exponent| fract.|  (10-3F = fraction bits)
    821  *        +-+--------+-------+
    822  *        Significance order: 0-6, 10-1F, 20-2F, 30-3F
    823  *
    824  * The fourth argument to fltext_De is the eight extra bits for use
    825  * in EMOD*, et al.  If these bits are not in use, specify 0.
    826  */
    827 fltext_De:
    828 	.word 0x831	# %r0 %r1 %r2 %r3 %r4 ap (no return)
    829 
    830 	movl 0x4(%ap), %r0	# %r0 - addr of source
    831 	movl 0x8(%ap), %r1	# %r1 - addr of fraction destination
    832 
    833 	movb (%r0), (%r1)
    834 	bisb2 $0x80, (%r1)+	# This is the hidden bit.
    835 
    836 	movb 3(%r0), (%r1)+
    837 	movb 2(%r0), (%r1)+
    838 	movb 5(%r0), (%r1)+
    839 	movb 4(%r0), (%r1)+
    840 	movb 7(%r0), (%r1)+
    841 	movb 6(%r0), (%r1)+
    842 
    843 	/*
    844 	 * if there are extension bits (EMOD EDIV etc.) they are
    845 	 * low-order
    846 	 */
    847 	movb 0x10(%ap), (%r1)
    848 
    849 	movl 0x4(%ap), %r0	# %r0 - addr of source
    850 	movl 0xc(%ap), %r2	# %r2 - addr of exponent destination
    851 	extzv $0x7, $0x8, (%r0), (%r2)		# get exponent out
    852 	ret
    853 
    854