xref: /openbmc/linux/arch/m68k/fpsp040/res_func.S (revision e5451c8f8330e03ad3cfa16048b4daf961af434f)
1 |
2 |	res_func.sa 3.9 7/29/91
3 |
4 | Normalizes denormalized numbers if necessary and updates the
5 | stack frame.  The function is then restored back into the
6 | machine and the 040 completes the operation.  This routine
7 | is only used by the unsupported data type/format handler.
8 | (Exception vector 55).
9 |
10 | For packed move out (fmove.p fpm,<ea>) the operation is
11 | completed here; data is packed and moved to user memory.
12 | The stack is restored to the 040 only in the case of a
13 | reportable exception in the conversion.
14 |
15 |
16 |		Copyright (C) Motorola, Inc. 1990
17 |			All Rights Reserved
18 |
19 |       For details on the license for this file, please see the
20 |       file, README, in this same directory.
21 
22 RES_FUNC:    |idnt    2,1 | Motorola 040 Floating Point Software Package
23 
24 	|section	8
25 
26 #include "fpsp.h"
27 
28 sp_bnds:	.short	0x3f81,0x407e
29 		.short	0x3f6a,0x0000
30 dp_bnds:	.short	0x3c01,0x43fe
31 		.short	0x3bcd,0x0000
32 
33 	|xref	mem_write
34 	|xref	bindec
35 	|xref	get_fline
36 	|xref	round
37 	|xref	denorm
38 	|xref	dest_ext
39 	|xref	dest_dbl
40 	|xref	dest_sgl
41 	|xref	unf_sub
42 	|xref	nrm_set
43 	|xref	dnrm_lp
44 	|xref	ovf_res
45 	|xref	reg_dest
46 	|xref	t_ovfl
47 	|xref	t_unfl
48 
49 	.global	res_func
50 	.global	p_move
51 
52 res_func:
53 	clrb	DNRM_FLG(%a6)
54 	clrb	RES_FLG(%a6)
55 	clrb	CU_ONLY(%a6)
56 	tstb	DY_MO_FLG(%a6)
57 	beqs	monadic
58 dyadic:
59 	btstb	#7,DTAG(%a6)	|if dop = norm=000, zero=001,
60 |				;inf=010 or nan=011
61 	beqs	monadic		|then branch
62 |				;else denorm
63 | HANDLE DESTINATION DENORM HERE
64 |				;set dtag to norm
65 |				;write the tag & fpte15 to the fstack
66 	leal	FPTEMP(%a6),%a0
67 
68 	bclrb	#sign_bit,LOCAL_EX(%a0)
69 	sne	LOCAL_SGN(%a0)
70 
71 	bsr	nrm_set		|normalize number (exp will go negative)
72 	bclrb	#sign_bit,LOCAL_EX(%a0) |get rid of false sign
73 	bfclr	LOCAL_SGN(%a0){#0:#8}	|change back to IEEE ext format
74 	beqs	dpos
75 	bsetb	#sign_bit,LOCAL_EX(%a0)
76 dpos:
77 	bfclr	DTAG(%a6){#0:#4}	|set tag to normalized, FPTE15 = 0
78 	bsetb	#4,DTAG(%a6)	|set FPTE15
79 	orb	#0x0f,DNRM_FLG(%a6)
80 monadic:
81 	leal	ETEMP(%a6),%a0
82 	btstb	#direction_bit,CMDREG1B(%a6)	|check direction
83 	bne	opclass3			|it is a mv out
84 |
85 | At this point, only opclass 0 and 2 possible
86 |
87 	btstb	#7,STAG(%a6)	|if sop = norm=000, zero=001,
88 |				;inf=010 or nan=011
89 	bne	mon_dnrm	|else denorm
90 	tstb	DY_MO_FLG(%a6)	|all cases of dyadic instructions would
91 	bne	normal		|require normalization of denorm
92 
93 | At this point:
94 |	monadic instructions:	fabs  = $18  fneg   = $1a  ftst   = $3a
95 |				fmove = $00  fsmove = $40  fdmove = $44
96 |				fsqrt = $05* fssqrt = $41  fdsqrt = $45
97 |				(*fsqrt reencoded to $05)
98 |
99 	movew	CMDREG1B(%a6),%d0	|get command register
100 	andil	#0x7f,%d0			|strip to only command word
101 |
102 | At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and
103 | fdsqrt are possible.
104 | For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize)
105 | For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize)
106 |
107 	btstl	#0,%d0
108 	bne	normal			|weed out fsqrt instructions
109 |
110 | cu_norm handles fmove in instructions with normalized inputs.
111 | The routine round is used to correctly round the input for the
112 | destination precision and mode.
113 |
114 cu_norm:
115 	st	CU_ONLY(%a6)		|set cu-only inst flag
116 	movew	CMDREG1B(%a6),%d0
117 	andib	#0x3b,%d0		|isolate bits to select inst
118 	tstb	%d0
119 	beql	cu_nmove	|if zero, it is an fmove
120 	cmpib	#0x18,%d0
121 	beql	cu_nabs		|if $18, it is fabs
122 	cmpib	#0x1a,%d0
123 	beql	cu_nneg		|if $1a, it is fneg
124 |
125 | Inst is ftst.  Check the source operand and set the cc's accordingly.
126 | No write is done, so simply rts.
127 |
128 cu_ntst:
129 	movew	LOCAL_EX(%a0),%d0
130 	bclrl	#15,%d0
131 	sne	LOCAL_SGN(%a0)
132 	beqs	cu_ntpo
133 	orl	#neg_mask,USER_FPSR(%a6) |set N
134 cu_ntpo:
135 	cmpiw	#0x7fff,%d0	|test for inf/nan
136 	bnes	cu_ntcz
137 	tstl	LOCAL_HI(%a0)
138 	bnes	cu_ntn
139 	tstl	LOCAL_LO(%a0)
140 	bnes	cu_ntn
141 	orl	#inf_mask,USER_FPSR(%a6)
142 	rts
143 cu_ntn:
144 	orl	#nan_mask,USER_FPSR(%a6)
145 	movel	ETEMP_EX(%a6),FPTEMP_EX(%a6)	|set up fptemp sign for
146 |						;snan handler
147 
148 	rts
149 cu_ntcz:
150 	tstl	LOCAL_HI(%a0)
151 	bnel	cu_ntsx
152 	tstl	LOCAL_LO(%a0)
153 	bnel	cu_ntsx
154 	orl	#z_mask,USER_FPSR(%a6)
155 cu_ntsx:
156 	rts
157 |
158 | Inst is fabs.  Execute the absolute value function on the input.
159 | Branch to the fmove code.  If the operand is NaN, do nothing.
160 |
161 cu_nabs:
162 	moveb	STAG(%a6),%d0
163 	btstl	#5,%d0			|test for NaN or zero
164 	bne	wr_etemp		|if either, simply write it
165 	bclrb	#7,LOCAL_EX(%a0)		|do abs
166 	bras	cu_nmove		|fmove code will finish
167 |
168 | Inst is fneg.  Execute the negate value function on the input.
169 | Fall though to the fmove code.  If the operand is NaN, do nothing.
170 |
171 cu_nneg:
172 	moveb	STAG(%a6),%d0
173 	btstl	#5,%d0			|test for NaN or zero
174 	bne	wr_etemp		|if either, simply write it
175 	bchgb	#7,LOCAL_EX(%a0)		|do neg
176 |
177 | Inst is fmove.  This code also handles all result writes.
178 | If bit 2 is set, round is forced to double.  If it is clear,
179 | and bit 6 is set, round is forced to single.  If both are clear,
180 | the round precision is found in the fpcr.  If the rounding precision
181 | is double or single, round the result before the write.
182 |
183 cu_nmove:
184 	moveb	STAG(%a6),%d0
185 	andib	#0xe0,%d0			|isolate stag bits
186 	bne	wr_etemp		|if not norm, simply write it
187 	btstb	#2,CMDREG1B+1(%a6)	|check for rd
188 	bne	cu_nmrd
189 	btstb	#6,CMDREG1B+1(%a6)	|check for rs
190 	bne	cu_nmrs
191 |
192 | The move or operation is not with forced precision.  Test for
193 | nan or inf as the input; if so, simply write it to FPn.  Use the
194 | FPCR_MODE byte to get rounding on norms and zeros.
195 |
196 cu_nmnr:
197 	bfextu	FPCR_MODE(%a6){#0:#2},%d0
198 	tstb	%d0			|check for extended
199 	beq	cu_wrexn		|if so, just write result
200 	cmpib	#1,%d0			|check for single
201 	beq	cu_nmrs			|fall through to double
202 |
203 | The move is fdmove or round precision is double.
204 |
205 cu_nmrd:
206 	movel	#2,%d0			|set up the size for denorm
207 	movew	LOCAL_EX(%a0),%d1		|compare exponent to double threshold
208 	andw	#0x7fff,%d1
209 	cmpw	#0x3c01,%d1
210 	bls	cu_nunfl
211 	bfextu	FPCR_MODE(%a6){#2:#2},%d1	|get rmode
212 	orl	#0x00020000,%d1		|or in rprec (double)
213 	clrl	%d0			|clear g,r,s for round
214 	bclrb	#sign_bit,LOCAL_EX(%a0)	|convert to internal format
215 	sne	LOCAL_SGN(%a0)
216 	bsrl	round
217 	bfclr	LOCAL_SGN(%a0){#0:#8}
218 	beqs	cu_nmrdc
219 	bsetb	#sign_bit,LOCAL_EX(%a0)
220 cu_nmrdc:
221 	movew	LOCAL_EX(%a0),%d1		|check for overflow
222 	andw	#0x7fff,%d1
223 	cmpw	#0x43ff,%d1
224 	bge	cu_novfl		|take care of overflow case
225 	bra	cu_wrexn
226 |
227 | The move is fsmove or round precision is single.
228 |
229 cu_nmrs:
230 	movel	#1,%d0
231 	movew	LOCAL_EX(%a0),%d1
232 	andw	#0x7fff,%d1
233 	cmpw	#0x3f81,%d1
234 	bls	cu_nunfl
235 	bfextu	FPCR_MODE(%a6){#2:#2},%d1
236 	orl	#0x00010000,%d1
237 	clrl	%d0
238 	bclrb	#sign_bit,LOCAL_EX(%a0)
239 	sne	LOCAL_SGN(%a0)
240 	bsrl	round
241 	bfclr	LOCAL_SGN(%a0){#0:#8}
242 	beqs	cu_nmrsc
243 	bsetb	#sign_bit,LOCAL_EX(%a0)
244 cu_nmrsc:
245 	movew	LOCAL_EX(%a0),%d1
246 	andw	#0x7FFF,%d1
247 	cmpw	#0x407f,%d1
248 	blt	cu_wrexn
249 |
250 | The operand is above precision boundaries.  Use t_ovfl to
251 | generate the correct value.
252 |
253 cu_novfl:
254 	bsr	t_ovfl
255 	bra	cu_wrexn
256 |
257 | The operand is below precision boundaries.  Use denorm to
258 | generate the correct value.
259 |
260 cu_nunfl:
261 	bclrb	#sign_bit,LOCAL_EX(%a0)
262 	sne	LOCAL_SGN(%a0)
263 	bsr	denorm
264 	bfclr	LOCAL_SGN(%a0){#0:#8}	|change back to IEEE ext format
265 	beqs	cu_nucont
266 	bsetb	#sign_bit,LOCAL_EX(%a0)
267 cu_nucont:
268 	bfextu	FPCR_MODE(%a6){#2:#2},%d1
269 	btstb	#2,CMDREG1B+1(%a6)	|check for rd
270 	bne	inst_d
271 	btstb	#6,CMDREG1B+1(%a6)	|check for rs
272 	bne	inst_s
273 	swap	%d1
274 	moveb	FPCR_MODE(%a6),%d1
275 	lsrb	#6,%d1
276 	swap	%d1
277 	bra	inst_sd
278 inst_d:
279 	orl	#0x00020000,%d1
280 	bra	inst_sd
281 inst_s:
282 	orl	#0x00010000,%d1
283 inst_sd:
284 	bclrb	#sign_bit,LOCAL_EX(%a0)
285 	sne	LOCAL_SGN(%a0)
286 	bsrl	round
287 	bfclr	LOCAL_SGN(%a0){#0:#8}
288 	beqs	cu_nuflp
289 	bsetb	#sign_bit,LOCAL_EX(%a0)
290 cu_nuflp:
291 	btstb	#inex2_bit,FPSR_EXCEPT(%a6)
292 	beqs	cu_nuninx
293 	orl	#aunfl_mask,USER_FPSR(%a6) |if the round was inex, set AUNFL
294 cu_nuninx:
295 	tstl	LOCAL_HI(%a0)		|test for zero
296 	bnes	cu_nunzro
297 	tstl	LOCAL_LO(%a0)
298 	bnes	cu_nunzro
299 |
300 | The mantissa is zero from the denorm loop.  Check sign and rmode
301 | to see if rounding should have occurred which would leave the lsb.
302 |
303 	movel	USER_FPCR(%a6),%d0
304 	andil	#0x30,%d0		|isolate rmode
305 	cmpil	#0x20,%d0
306 	blts	cu_nzro
307 	bnes	cu_nrp
308 cu_nrm:
309 	tstw	LOCAL_EX(%a0)	|if positive, set lsb
310 	bges	cu_nzro
311 	btstb	#7,FPCR_MODE(%a6) |check for double
312 	beqs	cu_nincs
313 	bras	cu_nincd
314 cu_nrp:
315 	tstw	LOCAL_EX(%a0)	|if positive, set lsb
316 	blts	cu_nzro
317 	btstb	#7,FPCR_MODE(%a6) |check for double
318 	beqs	cu_nincs
319 cu_nincd:
320 	orl	#0x800,LOCAL_LO(%a0) |inc for double
321 	bra	cu_nunzro
322 cu_nincs:
323 	orl	#0x100,LOCAL_HI(%a0) |inc for single
324 	bra	cu_nunzro
325 cu_nzro:
326 	orl	#z_mask,USER_FPSR(%a6)
327 	moveb	STAG(%a6),%d0
328 	andib	#0xe0,%d0
329 	cmpib	#0x40,%d0		|check if input was tagged zero
330 	beqs	cu_numv
331 cu_nunzro:
332 	orl	#unfl_mask,USER_FPSR(%a6) |set unfl
333 cu_numv:
334 	movel	(%a0),ETEMP(%a6)
335 	movel	4(%a0),ETEMP_HI(%a6)
336 	movel	8(%a0),ETEMP_LO(%a6)
337 |
338 | Write the result to memory, setting the fpsr cc bits.  NaN and Inf
339 | bypass cu_wrexn.
340 |
341 cu_wrexn:
342 	tstw	LOCAL_EX(%a0)		|test for zero
343 	beqs	cu_wrzero
344 	cmpw	#0x8000,LOCAL_EX(%a0)	|test for zero
345 	bnes	cu_wreon
346 cu_wrzero:
347 	orl	#z_mask,USER_FPSR(%a6)	|set Z bit
348 cu_wreon:
349 	tstw	LOCAL_EX(%a0)
350 	bpl	wr_etemp
351 	orl	#neg_mask,USER_FPSR(%a6)
352 	bra	wr_etemp
353 
354 |
355 | HANDLE SOURCE DENORM HERE
356 |
357 |				;clear denorm stag to norm
358 |				;write the new tag & ete15 to the fstack
359 mon_dnrm:
360 |
361 | At this point, check for the cases in which normalizing the
362 | denorm produces incorrect results.
363 |
364 	tstb	DY_MO_FLG(%a6)	|all cases of dyadic instructions would
365 	bnes	nrm_src		|require normalization of denorm
366 
367 | At this point:
368 |	monadic instructions:	fabs  = $18  fneg   = $1a  ftst   = $3a
369 |				fmove = $00  fsmove = $40  fdmove = $44
370 |				fsqrt = $05* fssqrt = $41  fdsqrt = $45
371 |				(*fsqrt reencoded to $05)
372 |
373 	movew	CMDREG1B(%a6),%d0	|get command register
374 	andil	#0x7f,%d0			|strip to only command word
375 |
376 | At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and
377 | fdsqrt are possible.
378 | For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize)
379 | For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize)
380 |
381 	btstl	#0,%d0
382 	bnes	nrm_src		|weed out fsqrt instructions
383 	st	CU_ONLY(%a6)	|set cu-only inst flag
384 	bra	cu_dnrm		|fmove, fabs, fneg, ftst
385 |				;cases go to cu_dnrm
386 nrm_src:
387 	bclrb	#sign_bit,LOCAL_EX(%a0)
388 	sne	LOCAL_SGN(%a0)
389 	bsr	nrm_set		|normalize number (exponent will go
390 |				; negative)
391 	bclrb	#sign_bit,LOCAL_EX(%a0) |get rid of false sign
392 
393 	bfclr	LOCAL_SGN(%a0){#0:#8}	|change back to IEEE ext format
394 	beqs	spos
395 	bsetb	#sign_bit,LOCAL_EX(%a0)
396 spos:
397 	bfclr	STAG(%a6){#0:#4}	|set tag to normalized, FPTE15 = 0
398 	bsetb	#4,STAG(%a6)	|set ETE15
399 	orb	#0xf0,DNRM_FLG(%a6)
400 normal:
401 	tstb	DNRM_FLG(%a6)	|check if any of the ops were denorms
402 	bne	ck_wrap		|if so, check if it is a potential
403 |				;wrap-around case
404 fix_stk:
405 	moveb	#0xfe,CU_SAVEPC(%a6)
406 	bclrb	#E1,E_BYTE(%a6)
407 
408 	clrw	NMNEXC(%a6)
409 
410 	st	RES_FLG(%a6)	|indicate that a restore is needed
411 	rts
412 
413 |
414 | cu_dnrm handles all cu-only instructions (fmove, fabs, fneg, and
415 | ftst) completely in software without an frestore to the 040.
416 |
417 cu_dnrm:
418 	st	CU_ONLY(%a6)
419 	movew	CMDREG1B(%a6),%d0
420 	andib	#0x3b,%d0		|isolate bits to select inst
421 	tstb	%d0
422 	beql	cu_dmove	|if zero, it is an fmove
423 	cmpib	#0x18,%d0
424 	beql	cu_dabs		|if $18, it is fabs
425 	cmpib	#0x1a,%d0
426 	beql	cu_dneg		|if $1a, it is fneg
427 |
428 | Inst is ftst.  Check the source operand and set the cc's accordingly.
429 | No write is done, so simply rts.
430 |
431 cu_dtst:
432 	movew	LOCAL_EX(%a0),%d0
433 	bclrl	#15,%d0
434 	sne	LOCAL_SGN(%a0)
435 	beqs	cu_dtpo
436 	orl	#neg_mask,USER_FPSR(%a6) |set N
437 cu_dtpo:
438 	cmpiw	#0x7fff,%d0	|test for inf/nan
439 	bnes	cu_dtcz
440 	tstl	LOCAL_HI(%a0)
441 	bnes	cu_dtn
442 	tstl	LOCAL_LO(%a0)
443 	bnes	cu_dtn
444 	orl	#inf_mask,USER_FPSR(%a6)
445 	rts
446 cu_dtn:
447 	orl	#nan_mask,USER_FPSR(%a6)
448 	movel	ETEMP_EX(%a6),FPTEMP_EX(%a6)	|set up fptemp sign for
449 |						;snan handler
450 	rts
451 cu_dtcz:
452 	tstl	LOCAL_HI(%a0)
453 	bnel	cu_dtsx
454 	tstl	LOCAL_LO(%a0)
455 	bnel	cu_dtsx
456 	orl	#z_mask,USER_FPSR(%a6)
457 cu_dtsx:
458 	rts
459 |
460 | Inst is fabs.  Execute the absolute value function on the input.
461 | Branch to the fmove code.
462 |
463 cu_dabs:
464 	bclrb	#7,LOCAL_EX(%a0)		|do abs
465 	bras	cu_dmove		|fmove code will finish
466 |
467 | Inst is fneg.  Execute the negate value function on the input.
468 | Fall though to the fmove code.
469 |
470 cu_dneg:
471 	bchgb	#7,LOCAL_EX(%a0)		|do neg
472 |
473 | Inst is fmove.  This code also handles all result writes.
474 | If bit 2 is set, round is forced to double.  If it is clear,
475 | and bit 6 is set, round is forced to single.  If both are clear,
476 | the round precision is found in the fpcr.  If the rounding precision
477 | is double or single, the result is zero, and the mode is checked
478 | to determine if the lsb of the result should be set.
479 |
480 cu_dmove:
481 	btstb	#2,CMDREG1B+1(%a6)	|check for rd
482 	bne	cu_dmrd
483 	btstb	#6,CMDREG1B+1(%a6)	|check for rs
484 	bne	cu_dmrs
485 |
486 | The move or operation is not with forced precision.  Use the
487 | FPCR_MODE byte to get rounding.
488 |
489 cu_dmnr:
490 	bfextu	FPCR_MODE(%a6){#0:#2},%d0
491 	tstb	%d0			|check for extended
492 	beq	cu_wrexd		|if so, just write result
493 	cmpib	#1,%d0			|check for single
494 	beq	cu_dmrs			|fall through to double
495 |
496 | The move is fdmove or round precision is double.  Result is zero.
497 | Check rmode for rp or rm and set lsb accordingly.
498 |
499 cu_dmrd:
500 	bfextu	FPCR_MODE(%a6){#2:#2},%d1	|get rmode
501 	tstw	LOCAL_EX(%a0)		|check sign
502 	blts	cu_dmdn
503 	cmpib	#3,%d1			|check for rp
504 	bne	cu_dpd			|load double pos zero
505 	bra	cu_dpdr			|load double pos zero w/lsb
506 cu_dmdn:
507 	cmpib	#2,%d1			|check for rm
508 	bne	cu_dnd			|load double neg zero
509 	bra	cu_dndr			|load double neg zero w/lsb
510 |
511 | The move is fsmove or round precision is single.  Result is zero.
512 | Check for rp or rm and set lsb accordingly.
513 |
514 cu_dmrs:
515 	bfextu	FPCR_MODE(%a6){#2:#2},%d1	|get rmode
516 	tstw	LOCAL_EX(%a0)		|check sign
517 	blts	cu_dmsn
518 	cmpib	#3,%d1			|check for rp
519 	bne	cu_spd			|load single pos zero
520 	bra	cu_spdr			|load single pos zero w/lsb
521 cu_dmsn:
522 	cmpib	#2,%d1			|check for rm
523 	bne	cu_snd			|load single neg zero
524 	bra	cu_sndr			|load single neg zero w/lsb
525 |
526 | The precision is extended, so the result in etemp is correct.
527 | Simply set unfl (not inex2 or aunfl) and write the result to
528 | the correct fp register.
529 cu_wrexd:
530 	orl	#unfl_mask,USER_FPSR(%a6)
531 	tstw	LOCAL_EX(%a0)
532 	beq	wr_etemp
533 	orl	#neg_mask,USER_FPSR(%a6)
534 	bra	wr_etemp
535 |
536 | These routines write +/- zero in double format.  The routines
537 | cu_dpdr and cu_dndr set the double lsb.
538 |
539 cu_dpd:
540 	movel	#0x3c010000,LOCAL_EX(%a0)	|force pos double zero
541 	clrl	LOCAL_HI(%a0)
542 	clrl	LOCAL_LO(%a0)
543 	orl	#z_mask,USER_FPSR(%a6)
544 	orl	#unfinx_mask,USER_FPSR(%a6)
545 	bra	wr_etemp
546 cu_dpdr:
547 	movel	#0x3c010000,LOCAL_EX(%a0)	|force pos double zero
548 	clrl	LOCAL_HI(%a0)
549 	movel	#0x800,LOCAL_LO(%a0)	|with lsb set
550 	orl	#unfinx_mask,USER_FPSR(%a6)
551 	bra	wr_etemp
552 cu_dnd:
553 	movel	#0xbc010000,LOCAL_EX(%a0)	|force pos double zero
554 	clrl	LOCAL_HI(%a0)
555 	clrl	LOCAL_LO(%a0)
556 	orl	#z_mask,USER_FPSR(%a6)
557 	orl	#neg_mask,USER_FPSR(%a6)
558 	orl	#unfinx_mask,USER_FPSR(%a6)
559 	bra	wr_etemp
560 cu_dndr:
561 	movel	#0xbc010000,LOCAL_EX(%a0)	|force pos double zero
562 	clrl	LOCAL_HI(%a0)
563 	movel	#0x800,LOCAL_LO(%a0)	|with lsb set
564 	orl	#neg_mask,USER_FPSR(%a6)
565 	orl	#unfinx_mask,USER_FPSR(%a6)
566 	bra	wr_etemp
567 |
568 | These routines write +/- zero in single format.  The routines
569 | cu_dpdr and cu_dndr set the single lsb.
570 |
571 cu_spd:
572 	movel	#0x3f810000,LOCAL_EX(%a0)	|force pos single zero
573 	clrl	LOCAL_HI(%a0)
574 	clrl	LOCAL_LO(%a0)
575 	orl	#z_mask,USER_FPSR(%a6)
576 	orl	#unfinx_mask,USER_FPSR(%a6)
577 	bra	wr_etemp
578 cu_spdr:
579 	movel	#0x3f810000,LOCAL_EX(%a0)	|force pos single zero
580 	movel	#0x100,LOCAL_HI(%a0)	|with lsb set
581 	clrl	LOCAL_LO(%a0)
582 	orl	#unfinx_mask,USER_FPSR(%a6)
583 	bra	wr_etemp
584 cu_snd:
585 	movel	#0xbf810000,LOCAL_EX(%a0)	|force pos single zero
586 	clrl	LOCAL_HI(%a0)
587 	clrl	LOCAL_LO(%a0)
588 	orl	#z_mask,USER_FPSR(%a6)
589 	orl	#neg_mask,USER_FPSR(%a6)
590 	orl	#unfinx_mask,USER_FPSR(%a6)
591 	bra	wr_etemp
592 cu_sndr:
593 	movel	#0xbf810000,LOCAL_EX(%a0)	|force pos single zero
594 	movel	#0x100,LOCAL_HI(%a0)	|with lsb set
595 	clrl	LOCAL_LO(%a0)
596 	orl	#neg_mask,USER_FPSR(%a6)
597 	orl	#unfinx_mask,USER_FPSR(%a6)
598 	bra	wr_etemp
599 
600 |
601 | This code checks for 16-bit overflow conditions on dyadic
602 | operations which are not restorable into the floating-point
603 | unit and must be completed in software.  Basically, this
604 | condition exists with a very large norm and a denorm.  One
605 | of the operands must be denormalized to enter this code.
606 |
607 | Flags used:
608 |	DY_MO_FLG contains 0 for monadic op, $ff for dyadic
609 |	DNRM_FLG contains $00 for neither op denormalized
610 |	                  $0f for the destination op denormalized
611 |	                  $f0 for the source op denormalized
612 |	                  $ff for both ops denormalized
613 |
614 | The wrap-around condition occurs for add, sub, div, and cmp
615 | when
616 |
617 |	abs(dest_exp - src_exp) >= $8000
618 |
619 | and for mul when
620 |
621 |	(dest_exp + src_exp) < $0
622 |
623 | we must process the operation here if this case is true.
624 |
625 | The rts following the frcfpn routine is the exit from res_func
626 | for this condition.  The restore flag (RES_FLG) is left clear.
627 | No frestore is done unless an exception is to be reported.
628 |
629 | For fadd:
630 |	if(sign_of(dest) != sign_of(src))
631 |		replace exponent of src with $3fff (keep sign)
632 |		use fpu to perform dest+new_src (user's rmode and X)
633 |		clr sticky
634 |	else
635 |		set sticky
636 |	call round with user's precision and mode
637 |	move result to fpn and wbtemp
638 |
639 | For fsub:
640 |	if(sign_of(dest) == sign_of(src))
641 |		replace exponent of src with $3fff (keep sign)
642 |		use fpu to perform dest+new_src (user's rmode and X)
643 |		clr sticky
644 |	else
645 |		set sticky
646 |	call round with user's precision and mode
647 |	move result to fpn and wbtemp
648 |
649 | For fdiv/fsgldiv:
650 |	if(both operands are denorm)
651 |		restore_to_fpu;
652 |	if(dest is norm)
653 |		force_ovf;
654 |	else(dest is denorm)
655 |		force_unf:
656 |
657 | For fcmp:
658 |	if(dest is norm)
659 |		N = sign_of(dest);
660 |	else(dest is denorm)
661 |		N = sign_of(src);
662 |
663 | For fmul:
664 |	if(both operands are denorm)
665 |		force_unf;
666 |	if((dest_exp + src_exp) < 0)
667 |		force_unf:
668 |	else
669 |		restore_to_fpu;
670 |
671 | local equates:
672 	.set	addcode,0x22
673 	.set	subcode,0x28
674 	.set	mulcode,0x23
675 	.set	divcode,0x20
676 	.set	cmpcode,0x38
677 ck_wrap:
678 	| tstb	DY_MO_FLG(%a6)	;check for fsqrt
679 	beq	fix_stk		|if zero, it is fsqrt
680 	movew	CMDREG1B(%a6),%d0
681 	andiw	#0x3b,%d0		|strip to command bits
682 	cmpiw	#addcode,%d0
683 	beq	wrap_add
684 	cmpiw	#subcode,%d0
685 	beq	wrap_sub
686 	cmpiw	#mulcode,%d0
687 	beq	wrap_mul
688 	cmpiw	#cmpcode,%d0
689 	beq	wrap_cmp
690 |
691 | Inst is fdiv.
692 |
693 wrap_div:
694 	cmpb	#0xff,DNRM_FLG(%a6) |if both ops denorm,
695 	beq	fix_stk		 |restore to fpu
696 |
697 | One of the ops is denormalized.  Test for wrap condition
698 | and force the result.
699 |
700 	cmpb	#0x0f,DNRM_FLG(%a6) |check for dest denorm
701 	bnes	div_srcd
702 div_destd:
703 	bsrl	ckinf_ns
704 	bne	fix_stk
705 	bfextu	ETEMP_EX(%a6){#1:#15},%d0	|get src exp (always pos)
706 	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	|get dest exp (always neg)
707 	subl	%d1,%d0			|subtract dest from src
708 	cmpl	#0x7fff,%d0
709 	blt	fix_stk			|if less, not wrap case
710 	clrb	WBTEMP_SGN(%a6)
711 	movew	ETEMP_EX(%a6),%d0		|find the sign of the result
712 	movew	FPTEMP_EX(%a6),%d1
713 	eorw	%d1,%d0
714 	andiw	#0x8000,%d0
715 	beq	force_unf
716 	st	WBTEMP_SGN(%a6)
717 	bra	force_unf
718 
719 ckinf_ns:
720 	moveb	STAG(%a6),%d0		|check source tag for inf or nan
721 	bra	ck_in_com
722 ckinf_nd:
723 	moveb	DTAG(%a6),%d0		|check destination tag for inf or nan
724 ck_in_com:
725 	andib	#0x60,%d0			|isolate tag bits
726 	cmpb	#0x40,%d0			|is it inf?
727 	beq	nan_or_inf		|not wrap case
728 	cmpb	#0x60,%d0			|is it nan?
729 	beq	nan_or_inf		|yes, not wrap case?
730 	cmpb	#0x20,%d0			|is it a zero?
731 	beq	nan_or_inf		|yes
732 	clrl	%d0
733 	rts				|then ; it is either a zero of norm,
734 |					;check wrap case
735 nan_or_inf:
736 	moveql	#-1,%d0
737 	rts
738 
739 
740 
741 div_srcd:
742 	bsrl	ckinf_nd
743 	bne	fix_stk
744 	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	|get dest exp (always pos)
745 	bfexts	ETEMP_EX(%a6){#1:#15},%d1	|get src exp (always neg)
746 	subl	%d1,%d0			|subtract src from dest
747 	cmpl	#0x8000,%d0
748 	blt	fix_stk			|if less, not wrap case
749 	clrb	WBTEMP_SGN(%a6)
750 	movew	ETEMP_EX(%a6),%d0		|find the sign of the result
751 	movew	FPTEMP_EX(%a6),%d1
752 	eorw	%d1,%d0
753 	andiw	#0x8000,%d0
754 	beqs	force_ovf
755 	st	WBTEMP_SGN(%a6)
756 |
757 | This code handles the case of the instruction resulting in
758 | an overflow condition.
759 |
760 force_ovf:
761 	bclrb	#E1,E_BYTE(%a6)
762 	orl	#ovfl_inx_mask,USER_FPSR(%a6)
763 	clrw	NMNEXC(%a6)
764 	leal	WBTEMP(%a6),%a0		|point a0 to memory location
765 	movew	CMDREG1B(%a6),%d0
766 	btstl	#6,%d0			|test for forced precision
767 	beqs	frcovf_fpcr
768 	btstl	#2,%d0			|check for double
769 	bnes	frcovf_dbl
770 	movel	#0x1,%d0			|inst is forced single
771 	bras	frcovf_rnd
772 frcovf_dbl:
773 	movel	#0x2,%d0			|inst is forced double
774 	bras	frcovf_rnd
775 frcovf_fpcr:
776 	bfextu	FPCR_MODE(%a6){#0:#2},%d0	|inst not forced - use fpcr prec
777 frcovf_rnd:
778 
779 | The 881/882 does not set inex2 for the following case, so the
780 | line is commented out to be compatible with 881/882
781 |	tst.b	%d0
782 |	beq.b	frcovf_x
783 |	or.l	#inex2_mask,USER_FPSR(%a6) ;if prec is s or d, set inex2
784 
785 |frcovf_x:
786 	bsrl	ovf_res			|get correct result based on
787 |					;round precision/mode.  This
788 |					;sets FPSR_CC correctly
789 |					;returns in external format
790 	bfclr	WBTEMP_SGN(%a6){#0:#8}
791 	beq	frcfpn
792 	bsetb	#sign_bit,WBTEMP_EX(%a6)
793 	bra	frcfpn
794 |
795 | Inst is fadd.
796 |
797 wrap_add:
798 	cmpb	#0xff,DNRM_FLG(%a6) |if both ops denorm,
799 	beq	fix_stk		 |restore to fpu
800 |
801 | One of the ops is denormalized.  Test for wrap condition
802 | and complete the instruction.
803 |
804 	cmpb	#0x0f,DNRM_FLG(%a6) |check for dest denorm
805 	bnes	add_srcd
806 add_destd:
807 	bsrl	ckinf_ns
808 	bne	fix_stk
809 	bfextu	ETEMP_EX(%a6){#1:#15},%d0	|get src exp (always pos)
810 	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	|get dest exp (always neg)
811 	subl	%d1,%d0			|subtract dest from src
812 	cmpl	#0x8000,%d0
813 	blt	fix_stk			|if less, not wrap case
814 	bra	add_wrap
815 add_srcd:
816 	bsrl	ckinf_nd
817 	bne	fix_stk
818 	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	|get dest exp (always pos)
819 	bfexts	ETEMP_EX(%a6){#1:#15},%d1	|get src exp (always neg)
820 	subl	%d1,%d0			|subtract src from dest
821 	cmpl	#0x8000,%d0
822 	blt	fix_stk			|if less, not wrap case
823 |
824 | Check the signs of the operands.  If they are unlike, the fpu
825 | can be used to add the norm and 1.0 with the sign of the
826 | denorm and it will correctly generate the result in extended
827 | precision.  We can then call round with no sticky and the result
828 | will be correct for the user's rounding mode and precision.  If
829 | the signs are the same, we call round with the sticky bit set
830 | and the result will be correct for the user's rounding mode and
831 | precision.
832 |
833 add_wrap:
834 	movew	ETEMP_EX(%a6),%d0
835 	movew	FPTEMP_EX(%a6),%d1
836 	eorw	%d1,%d0
837 	andiw	#0x8000,%d0
838 	beq	add_same
839 |
840 | The signs are unlike.
841 |
842 	cmpb	#0x0f,DNRM_FLG(%a6) |is dest the denorm?
843 	bnes	add_u_srcd
844 	movew	FPTEMP_EX(%a6),%d0
845 	andiw	#0x8000,%d0
846 	orw	#0x3fff,%d0	|force the exponent to +/- 1
847 	movew	%d0,FPTEMP_EX(%a6) |in the denorm
848 	movel	USER_FPCR(%a6),%d0
849 	andil	#0x30,%d0
850 	fmovel	%d0,%fpcr		|set up users rmode and X
851 	fmovex	ETEMP(%a6),%fp0
852 	faddx	FPTEMP(%a6),%fp0
853 	leal	WBTEMP(%a6),%a0	|point a0 to wbtemp in frame
854 	fmovel	%fpsr,%d1
855 	orl	%d1,USER_FPSR(%a6) |capture cc's and inex from fadd
856 	fmovex	%fp0,WBTEMP(%a6)	|write result to memory
857 	lsrl	#4,%d0		|put rmode in lower 2 bits
858 	movel	USER_FPCR(%a6),%d1
859 	andil	#0xc0,%d1
860 	lsrl	#6,%d1		|put precision in upper word
861 	swap	%d1
862 	orl	%d0,%d1		|set up for round call
863 	clrl	%d0		|force sticky to zero
864 	bclrb	#sign_bit,WBTEMP_EX(%a6)
865 	sne	WBTEMP_SGN(%a6)
866 	bsrl	round		|round result to users rmode & prec
867 	bfclr	WBTEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
868 	beq	frcfpnr
869 	bsetb	#sign_bit,WBTEMP_EX(%a6)
870 	bra	frcfpnr
871 add_u_srcd:
872 	movew	ETEMP_EX(%a6),%d0
873 	andiw	#0x8000,%d0
874 	orw	#0x3fff,%d0	|force the exponent to +/- 1
875 	movew	%d0,ETEMP_EX(%a6) |in the denorm
876 	movel	USER_FPCR(%a6),%d0
877 	andil	#0x30,%d0
878 	fmovel	%d0,%fpcr		|set up users rmode and X
879 	fmovex	ETEMP(%a6),%fp0
880 	faddx	FPTEMP(%a6),%fp0
881 	fmovel	%fpsr,%d1
882 	orl	%d1,USER_FPSR(%a6) |capture cc's and inex from fadd
883 	leal	WBTEMP(%a6),%a0	|point a0 to wbtemp in frame
884 	fmovex	%fp0,WBTEMP(%a6)	|write result to memory
885 	lsrl	#4,%d0		|put rmode in lower 2 bits
886 	movel	USER_FPCR(%a6),%d1
887 	andil	#0xc0,%d1
888 	lsrl	#6,%d1		|put precision in upper word
889 	swap	%d1
890 	orl	%d0,%d1		|set up for round call
891 	clrl	%d0		|force sticky to zero
892 	bclrb	#sign_bit,WBTEMP_EX(%a6)
893 	sne	WBTEMP_SGN(%a6)	|use internal format for round
894 	bsrl	round		|round result to users rmode & prec
895 	bfclr	WBTEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
896 	beq	frcfpnr
897 	bsetb	#sign_bit,WBTEMP_EX(%a6)
898 	bra	frcfpnr
899 |
900 | Signs are alike:
901 |
902 add_same:
903 	cmpb	#0x0f,DNRM_FLG(%a6) |is dest the denorm?
904 	bnes	add_s_srcd
905 add_s_destd:
906 	leal	ETEMP(%a6),%a0
907 	movel	USER_FPCR(%a6),%d0
908 	andil	#0x30,%d0
909 	lsrl	#4,%d0		|put rmode in lower 2 bits
910 	movel	USER_FPCR(%a6),%d1
911 	andil	#0xc0,%d1
912 	lsrl	#6,%d1		|put precision in upper word
913 	swap	%d1
914 	orl	%d0,%d1		|set up for round call
915 	movel	#0x20000000,%d0	|set sticky for round
916 	bclrb	#sign_bit,ETEMP_EX(%a6)
917 	sne	ETEMP_SGN(%a6)
918 	bsrl	round		|round result to users rmode & prec
919 	bfclr	ETEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
920 	beqs	add_s_dclr
921 	bsetb	#sign_bit,ETEMP_EX(%a6)
922 add_s_dclr:
923 	leal	WBTEMP(%a6),%a0
924 	movel	ETEMP(%a6),(%a0)	|write result to wbtemp
925 	movel	ETEMP_HI(%a6),4(%a0)
926 	movel	ETEMP_LO(%a6),8(%a0)
927 	tstw	ETEMP_EX(%a6)
928 	bgt	add_ckovf
929 	orl	#neg_mask,USER_FPSR(%a6)
930 	bra	add_ckovf
931 add_s_srcd:
932 	leal	FPTEMP(%a6),%a0
933 	movel	USER_FPCR(%a6),%d0
934 	andil	#0x30,%d0
935 	lsrl	#4,%d0		|put rmode in lower 2 bits
936 	movel	USER_FPCR(%a6),%d1
937 	andil	#0xc0,%d1
938 	lsrl	#6,%d1		|put precision in upper word
939 	swap	%d1
940 	orl	%d0,%d1		|set up for round call
941 	movel	#0x20000000,%d0	|set sticky for round
942 	bclrb	#sign_bit,FPTEMP_EX(%a6)
943 	sne	FPTEMP_SGN(%a6)
944 	bsrl	round		|round result to users rmode & prec
945 	bfclr	FPTEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
946 	beqs	add_s_sclr
947 	bsetb	#sign_bit,FPTEMP_EX(%a6)
948 add_s_sclr:
949 	leal	WBTEMP(%a6),%a0
950 	movel	FPTEMP(%a6),(%a0)	|write result to wbtemp
951 	movel	FPTEMP_HI(%a6),4(%a0)
952 	movel	FPTEMP_LO(%a6),8(%a0)
953 	tstw	FPTEMP_EX(%a6)
954 	bgt	add_ckovf
955 	orl	#neg_mask,USER_FPSR(%a6)
956 add_ckovf:
957 	movew	WBTEMP_EX(%a6),%d0
958 	andiw	#0x7fff,%d0
959 	cmpiw	#0x7fff,%d0
960 	bne	frcfpnr
961 |
962 | The result has overflowed to $7fff exponent.  Set I, ovfl,
963 | and aovfl, and clr the mantissa (incorrectly set by the
964 | round routine.)
965 |
966 	orl	#inf_mask+ovfl_inx_mask,USER_FPSR(%a6)
967 	clrl	4(%a0)
968 	bra	frcfpnr
969 |
970 | Inst is fsub.
971 |
972 wrap_sub:
973 	cmpb	#0xff,DNRM_FLG(%a6) |if both ops denorm,
974 	beq	fix_stk		 |restore to fpu
975 |
976 | One of the ops is denormalized.  Test for wrap condition
977 | and complete the instruction.
978 |
979 	cmpb	#0x0f,DNRM_FLG(%a6) |check for dest denorm
980 	bnes	sub_srcd
981 sub_destd:
982 	bsrl	ckinf_ns
983 	bne	fix_stk
984 	bfextu	ETEMP_EX(%a6){#1:#15},%d0	|get src exp (always pos)
985 	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	|get dest exp (always neg)
986 	subl	%d1,%d0			|subtract src from dest
987 	cmpl	#0x8000,%d0
988 	blt	fix_stk			|if less, not wrap case
989 	bra	sub_wrap
990 sub_srcd:
991 	bsrl	ckinf_nd
992 	bne	fix_stk
993 	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	|get dest exp (always pos)
994 	bfexts	ETEMP_EX(%a6){#1:#15},%d1	|get src exp (always neg)
995 	subl	%d1,%d0			|subtract dest from src
996 	cmpl	#0x8000,%d0
997 	blt	fix_stk			|if less, not wrap case
998 |
999 | Check the signs of the operands.  If they are alike, the fpu
1000 | can be used to subtract from the norm 1.0 with the sign of the
1001 | denorm and it will correctly generate the result in extended
1002 | precision.  We can then call round with no sticky and the result
1003 | will be correct for the user's rounding mode and precision.  If
1004 | the signs are unlike, we call round with the sticky bit set
1005 | and the result will be correct for the user's rounding mode and
1006 | precision.
1007 |
1008 sub_wrap:
1009 	movew	ETEMP_EX(%a6),%d0
1010 	movew	FPTEMP_EX(%a6),%d1
1011 	eorw	%d1,%d0
1012 	andiw	#0x8000,%d0
1013 	bne	sub_diff
1014 |
1015 | The signs are alike.
1016 |
1017 	cmpb	#0x0f,DNRM_FLG(%a6) |is dest the denorm?
1018 	bnes	sub_u_srcd
1019 	movew	FPTEMP_EX(%a6),%d0
1020 	andiw	#0x8000,%d0
1021 	orw	#0x3fff,%d0	|force the exponent to +/- 1
1022 	movew	%d0,FPTEMP_EX(%a6) |in the denorm
1023 	movel	USER_FPCR(%a6),%d0
1024 	andil	#0x30,%d0
1025 	fmovel	%d0,%fpcr		|set up users rmode and X
1026 	fmovex	FPTEMP(%a6),%fp0
1027 	fsubx	ETEMP(%a6),%fp0
1028 	fmovel	%fpsr,%d1
1029 	orl	%d1,USER_FPSR(%a6) |capture cc's and inex from fadd
1030 	leal	WBTEMP(%a6),%a0	|point a0 to wbtemp in frame
1031 	fmovex	%fp0,WBTEMP(%a6)	|write result to memory
1032 	lsrl	#4,%d0		|put rmode in lower 2 bits
1033 	movel	USER_FPCR(%a6),%d1
1034 	andil	#0xc0,%d1
1035 	lsrl	#6,%d1		|put precision in upper word
1036 	swap	%d1
1037 	orl	%d0,%d1		|set up for round call
1038 	clrl	%d0		|force sticky to zero
1039 	bclrb	#sign_bit,WBTEMP_EX(%a6)
1040 	sne	WBTEMP_SGN(%a6)
1041 	bsrl	round		|round result to users rmode & prec
1042 	bfclr	WBTEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
1043 	beq	frcfpnr
1044 	bsetb	#sign_bit,WBTEMP_EX(%a6)
1045 	bra	frcfpnr
1046 sub_u_srcd:
1047 	movew	ETEMP_EX(%a6),%d0
1048 	andiw	#0x8000,%d0
1049 	orw	#0x3fff,%d0	|force the exponent to +/- 1
1050 	movew	%d0,ETEMP_EX(%a6) |in the denorm
1051 	movel	USER_FPCR(%a6),%d0
1052 	andil	#0x30,%d0
1053 	fmovel	%d0,%fpcr		|set up users rmode and X
1054 	fmovex	FPTEMP(%a6),%fp0
1055 	fsubx	ETEMP(%a6),%fp0
1056 	fmovel	%fpsr,%d1
1057 	orl	%d1,USER_FPSR(%a6) |capture cc's and inex from fadd
1058 	leal	WBTEMP(%a6),%a0	|point a0 to wbtemp in frame
1059 	fmovex	%fp0,WBTEMP(%a6)	|write result to memory
1060 	lsrl	#4,%d0		|put rmode in lower 2 bits
1061 	movel	USER_FPCR(%a6),%d1
1062 	andil	#0xc0,%d1
1063 	lsrl	#6,%d1		|put precision in upper word
1064 	swap	%d1
1065 	orl	%d0,%d1		|set up for round call
1066 	clrl	%d0		|force sticky to zero
1067 	bclrb	#sign_bit,WBTEMP_EX(%a6)
1068 	sne	WBTEMP_SGN(%a6)
1069 	bsrl	round		|round result to users rmode & prec
1070 	bfclr	WBTEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
1071 	beq	frcfpnr
1072 	bsetb	#sign_bit,WBTEMP_EX(%a6)
1073 	bra	frcfpnr
1074 |
1075 | Signs are unlike:
1076 |
1077 sub_diff:
1078 	cmpb	#0x0f,DNRM_FLG(%a6) |is dest the denorm?
1079 	bnes	sub_s_srcd
1080 sub_s_destd:
1081 	leal	ETEMP(%a6),%a0
1082 	movel	USER_FPCR(%a6),%d0
1083 	andil	#0x30,%d0
1084 	lsrl	#4,%d0		|put rmode in lower 2 bits
1085 	movel	USER_FPCR(%a6),%d1
1086 	andil	#0xc0,%d1
1087 	lsrl	#6,%d1		|put precision in upper word
1088 	swap	%d1
1089 	orl	%d0,%d1		|set up for round call
1090 	movel	#0x20000000,%d0	|set sticky for round
1091 |
1092 | Since the dest is the denorm, the sign is the opposite of the
1093 | norm sign.
1094 |
1095 	eoriw	#0x8000,ETEMP_EX(%a6)	|flip sign on result
1096 	tstw	ETEMP_EX(%a6)
1097 	bgts	sub_s_dwr
1098 	orl	#neg_mask,USER_FPSR(%a6)
1099 sub_s_dwr:
1100 	bclrb	#sign_bit,ETEMP_EX(%a6)
1101 	sne	ETEMP_SGN(%a6)
1102 	bsrl	round		|round result to users rmode & prec
1103 	bfclr	ETEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
1104 	beqs	sub_s_dclr
1105 	bsetb	#sign_bit,ETEMP_EX(%a6)
1106 sub_s_dclr:
1107 	leal	WBTEMP(%a6),%a0
1108 	movel	ETEMP(%a6),(%a0)	|write result to wbtemp
1109 	movel	ETEMP_HI(%a6),4(%a0)
1110 	movel	ETEMP_LO(%a6),8(%a0)
1111 	bra	sub_ckovf
1112 sub_s_srcd:
1113 	leal	FPTEMP(%a6),%a0
1114 	movel	USER_FPCR(%a6),%d0
1115 	andil	#0x30,%d0
1116 	lsrl	#4,%d0		|put rmode in lower 2 bits
1117 	movel	USER_FPCR(%a6),%d1
1118 	andil	#0xc0,%d1
1119 	lsrl	#6,%d1		|put precision in upper word
1120 	swap	%d1
1121 	orl	%d0,%d1		|set up for round call
1122 	movel	#0x20000000,%d0	|set sticky for round
1123 	bclrb	#sign_bit,FPTEMP_EX(%a6)
1124 	sne	FPTEMP_SGN(%a6)
1125 	bsrl	round		|round result to users rmode & prec
1126 	bfclr	FPTEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
1127 	beqs	sub_s_sclr
1128 	bsetb	#sign_bit,FPTEMP_EX(%a6)
1129 sub_s_sclr:
1130 	leal	WBTEMP(%a6),%a0
1131 	movel	FPTEMP(%a6),(%a0)	|write result to wbtemp
1132 	movel	FPTEMP_HI(%a6),4(%a0)
1133 	movel	FPTEMP_LO(%a6),8(%a0)
1134 	tstw	FPTEMP_EX(%a6)
1135 	bgt	sub_ckovf
1136 	orl	#neg_mask,USER_FPSR(%a6)
1137 sub_ckovf:
1138 	movew	WBTEMP_EX(%a6),%d0
1139 	andiw	#0x7fff,%d0
1140 	cmpiw	#0x7fff,%d0
1141 	bne	frcfpnr
1142 |
1143 | The result has overflowed to $7fff exponent.  Set I, ovfl,
1144 | and aovfl, and clr the mantissa (incorrectly set by the
1145 | round routine.)
1146 |
1147 	orl	#inf_mask+ovfl_inx_mask,USER_FPSR(%a6)
1148 	clrl	4(%a0)
1149 	bra	frcfpnr
1150 |
1151 | Inst is fcmp.
1152 |
1153 wrap_cmp:
1154 	cmpb	#0xff,DNRM_FLG(%a6) |if both ops denorm,
1155 	beq	fix_stk		 |restore to fpu
1156 |
1157 | One of the ops is denormalized.  Test for wrap condition
1158 | and complete the instruction.
1159 |
1160 	cmpb	#0x0f,DNRM_FLG(%a6) |check for dest denorm
1161 	bnes	cmp_srcd
1162 cmp_destd:
1163 	bsrl	ckinf_ns
1164 	bne	fix_stk
1165 	bfextu	ETEMP_EX(%a6){#1:#15},%d0	|get src exp (always pos)
1166 	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	|get dest exp (always neg)
1167 	subl	%d1,%d0			|subtract dest from src
1168 	cmpl	#0x8000,%d0
1169 	blt	fix_stk			|if less, not wrap case
1170 	tstw	ETEMP_EX(%a6)		|set N to ~sign_of(src)
1171 	bge	cmp_setn
1172 	rts
1173 cmp_srcd:
1174 	bsrl	ckinf_nd
1175 	bne	fix_stk
1176 	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	|get dest exp (always pos)
1177 	bfexts	ETEMP_EX(%a6){#1:#15},%d1	|get src exp (always neg)
1178 	subl	%d1,%d0			|subtract src from dest
1179 	cmpl	#0x8000,%d0
1180 	blt	fix_stk			|if less, not wrap case
1181 	tstw	FPTEMP_EX(%a6)		|set N to sign_of(dest)
1182 	blt	cmp_setn
1183 	rts
1184 cmp_setn:
1185 	orl	#neg_mask,USER_FPSR(%a6)
1186 	rts
1187 
1188 |
1189 | Inst is fmul.
1190 |
1191 wrap_mul:
1192 	cmpb	#0xff,DNRM_FLG(%a6) |if both ops denorm,
1193 	beq	force_unf	|force an underflow (really!)
1194 |
1195 | One of the ops is denormalized.  Test for wrap condition
1196 | and complete the instruction.
1197 |
1198 	cmpb	#0x0f,DNRM_FLG(%a6) |check for dest denorm
1199 	bnes	mul_srcd
1200 mul_destd:
1201 	bsrl	ckinf_ns
1202 	bne	fix_stk
1203 	bfextu	ETEMP_EX(%a6){#1:#15},%d0	|get src exp (always pos)
1204 	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	|get dest exp (always neg)
1205 	addl	%d1,%d0			|subtract dest from src
1206 	bgt	fix_stk
1207 	bra	force_unf
1208 mul_srcd:
1209 	bsrl	ckinf_nd
1210 	bne	fix_stk
1211 	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	|get dest exp (always pos)
1212 	bfexts	ETEMP_EX(%a6){#1:#15},%d1	|get src exp (always neg)
1213 	addl	%d1,%d0			|subtract src from dest
1214 	bgt	fix_stk
1215 
1216 |
1217 | This code handles the case of the instruction resulting in
1218 | an underflow condition.
1219 |
1220 force_unf:
1221 	bclrb	#E1,E_BYTE(%a6)
1222 	orl	#unfinx_mask,USER_FPSR(%a6)
1223 	clrw	NMNEXC(%a6)
1224 	clrb	WBTEMP_SGN(%a6)
1225 	movew	ETEMP_EX(%a6),%d0		|find the sign of the result
1226 	movew	FPTEMP_EX(%a6),%d1
1227 	eorw	%d1,%d0
1228 	andiw	#0x8000,%d0
1229 	beqs	frcunfcont
1230 	st	WBTEMP_SGN(%a6)
1231 frcunfcont:
1232 	lea	WBTEMP(%a6),%a0		|point a0 to memory location
1233 	movew	CMDREG1B(%a6),%d0
1234 	btstl	#6,%d0			|test for forced precision
1235 	beqs	frcunf_fpcr
1236 	btstl	#2,%d0			|check for double
1237 	bnes	frcunf_dbl
1238 	movel	#0x1,%d0			|inst is forced single
1239 	bras	frcunf_rnd
1240 frcunf_dbl:
1241 	movel	#0x2,%d0			|inst is forced double
1242 	bras	frcunf_rnd
1243 frcunf_fpcr:
1244 	bfextu	FPCR_MODE(%a6){#0:#2},%d0	|inst not forced - use fpcr prec
1245 frcunf_rnd:
1246 	bsrl	unf_sub			|get correct result based on
1247 |					;round precision/mode.  This
1248 |					;sets FPSR_CC correctly
1249 	bfclr	WBTEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
1250 	beqs	frcfpn
1251 	bsetb	#sign_bit,WBTEMP_EX(%a6)
1252 	bra	frcfpn
1253 
1254 |
1255 | Write the result to the user's fpn.  All results must be HUGE to be
1256 | written; otherwise the results would have overflowed or underflowed.
1257 | If the rounding precision is single or double, the ovf_res routine
1258 | is needed to correctly supply the max value.
1259 |
1260 frcfpnr:
1261 	movew	CMDREG1B(%a6),%d0
1262 	btstl	#6,%d0			|test for forced precision
1263 	beqs	frcfpn_fpcr
1264 	btstl	#2,%d0			|check for double
1265 	bnes	frcfpn_dbl
1266 	movel	#0x1,%d0			|inst is forced single
1267 	bras	frcfpn_rnd
1268 frcfpn_dbl:
1269 	movel	#0x2,%d0			|inst is forced double
1270 	bras	frcfpn_rnd
1271 frcfpn_fpcr:
1272 	bfextu	FPCR_MODE(%a6){#0:#2},%d0	|inst not forced - use fpcr prec
1273 	tstb	%d0
1274 	beqs	frcfpn			|if extended, write what you got
1275 frcfpn_rnd:
1276 	bclrb	#sign_bit,WBTEMP_EX(%a6)
1277 	sne	WBTEMP_SGN(%a6)
1278 	bsrl	ovf_res			|get correct result based on
1279 |					;round precision/mode.  This
1280 |					;sets FPSR_CC correctly
1281 	bfclr	WBTEMP_SGN(%a6){#0:#8}	|convert back to IEEE ext format
1282 	beqs	frcfpn_clr
1283 	bsetb	#sign_bit,WBTEMP_EX(%a6)
1284 frcfpn_clr:
1285 	orl	#ovfinx_mask,USER_FPSR(%a6)
1286 |
1287 | Perform the write.
1288 |
1289 frcfpn:
1290 	bfextu	CMDREG1B(%a6){#6:#3},%d0	|extract fp destination register
1291 	cmpib	#3,%d0
1292 	bles	frc0123			|check if dest is fp0-fp3
1293 	movel	#7,%d1
1294 	subl	%d0,%d1
1295 	clrl	%d0
1296 	bsetl	%d1,%d0
1297 	fmovemx WBTEMP(%a6),%d0
1298 	rts
1299 frc0123:
1300 	cmpib	#0,%d0
1301 	beqs	frc0_dst
1302 	cmpib	#1,%d0
1303 	beqs	frc1_dst
1304 	cmpib	#2,%d0
1305 	beqs	frc2_dst
1306 frc3_dst:
1307 	movel	WBTEMP_EX(%a6),USER_FP3(%a6)
1308 	movel	WBTEMP_HI(%a6),USER_FP3+4(%a6)
1309 	movel	WBTEMP_LO(%a6),USER_FP3+8(%a6)
1310 	rts
1311 frc2_dst:
1312 	movel	WBTEMP_EX(%a6),USER_FP2(%a6)
1313 	movel	WBTEMP_HI(%a6),USER_FP2+4(%a6)
1314 	movel	WBTEMP_LO(%a6),USER_FP2+8(%a6)
1315 	rts
1316 frc1_dst:
1317 	movel	WBTEMP_EX(%a6),USER_FP1(%a6)
1318 	movel	WBTEMP_HI(%a6),USER_FP1+4(%a6)
1319 	movel	WBTEMP_LO(%a6),USER_FP1+8(%a6)
1320 	rts
1321 frc0_dst:
1322 	movel	WBTEMP_EX(%a6),USER_FP0(%a6)
1323 	movel	WBTEMP_HI(%a6),USER_FP0+4(%a6)
1324 	movel	WBTEMP_LO(%a6),USER_FP0+8(%a6)
1325 	rts
1326 
1327 |
1328 | Write etemp to fpn.
1329 | A check is made on enabled and signalled snan exceptions,
1330 | and the destination is not overwritten if this condition exists.
1331 | This code is designed to make fmoveins of unsupported data types
1332 | faster.
1333 |
1334 wr_etemp:
1335 	btstb	#snan_bit,FPSR_EXCEPT(%a6)	|if snan is set, and
1336 	beqs	fmoveinc		|enabled, force restore
1337 	btstb	#snan_bit,FPCR_ENABLE(%a6) |and don't overwrite
1338 	beqs	fmoveinc		|the dest
1339 	movel	ETEMP_EX(%a6),FPTEMP_EX(%a6)	|set up fptemp sign for
1340 |						;snan handler
1341 	tstb	ETEMP(%a6)		|check for negative
1342 	blts	snan_neg
1343 	rts
1344 snan_neg:
1345 	orl	#neg_bit,USER_FPSR(%a6)	|snan is negative; set N
1346 	rts
1347 fmoveinc:
1348 	clrw	NMNEXC(%a6)
1349 	bclrb	#E1,E_BYTE(%a6)
1350 	moveb	STAG(%a6),%d0		|check if stag is inf
1351 	andib	#0xe0,%d0
1352 	cmpib	#0x40,%d0
1353 	bnes	fminc_cnan
1354 	orl	#inf_mask,USER_FPSR(%a6) |if inf, nothing yet has set I
1355 	tstw	LOCAL_EX(%a0)		|check sign
1356 	bges	fminc_con
1357 	orl	#neg_mask,USER_FPSR(%a6)
1358 	bra	fminc_con
1359 fminc_cnan:
1360 	cmpib	#0x60,%d0			|check if stag is NaN
1361 	bnes	fminc_czero
1362 	orl	#nan_mask,USER_FPSR(%a6) |if nan, nothing yet has set NaN
1363 	movel	ETEMP_EX(%a6),FPTEMP_EX(%a6)	|set up fptemp sign for
1364 |						;snan handler
1365 	tstw	LOCAL_EX(%a0)		|check sign
1366 	bges	fminc_con
1367 	orl	#neg_mask,USER_FPSR(%a6)
1368 	bra	fminc_con
1369 fminc_czero:
1370 	cmpib	#0x20,%d0			|check if zero
1371 	bnes	fminc_con
1372 	orl	#z_mask,USER_FPSR(%a6)	|if zero, set Z
1373 	tstw	LOCAL_EX(%a0)		|check sign
1374 	bges	fminc_con
1375 	orl	#neg_mask,USER_FPSR(%a6)
1376 fminc_con:
1377 	bfextu	CMDREG1B(%a6){#6:#3},%d0	|extract fp destination register
1378 	cmpib	#3,%d0
1379 	bles	fp0123			|check if dest is fp0-fp3
1380 	movel	#7,%d1
1381 	subl	%d0,%d1
1382 	clrl	%d0
1383 	bsetl	%d1,%d0
1384 	fmovemx ETEMP(%a6),%d0
1385 	rts
1386 
1387 fp0123:
1388 	cmpib	#0,%d0
1389 	beqs	fp0_dst
1390 	cmpib	#1,%d0
1391 	beqs	fp1_dst
1392 	cmpib	#2,%d0
1393 	beqs	fp2_dst
1394 fp3_dst:
1395 	movel	ETEMP_EX(%a6),USER_FP3(%a6)
1396 	movel	ETEMP_HI(%a6),USER_FP3+4(%a6)
1397 	movel	ETEMP_LO(%a6),USER_FP3+8(%a6)
1398 	rts
1399 fp2_dst:
1400 	movel	ETEMP_EX(%a6),USER_FP2(%a6)
1401 	movel	ETEMP_HI(%a6),USER_FP2+4(%a6)
1402 	movel	ETEMP_LO(%a6),USER_FP2+8(%a6)
1403 	rts
1404 fp1_dst:
1405 	movel	ETEMP_EX(%a6),USER_FP1(%a6)
1406 	movel	ETEMP_HI(%a6),USER_FP1+4(%a6)
1407 	movel	ETEMP_LO(%a6),USER_FP1+8(%a6)
1408 	rts
1409 fp0_dst:
1410 	movel	ETEMP_EX(%a6),USER_FP0(%a6)
1411 	movel	ETEMP_HI(%a6),USER_FP0+4(%a6)
1412 	movel	ETEMP_LO(%a6),USER_FP0+8(%a6)
1413 	rts
1414 
1415 opclass3:
1416 	st	CU_ONLY(%a6)
1417 	movew	CMDREG1B(%a6),%d0	|check if packed moveout
1418 	andiw	#0x0c00,%d0	|isolate last 2 bits of size field
1419 	cmpiw	#0x0c00,%d0	|if size is 011 or 111, it is packed
1420 	beq	pack_out	|else it is norm or denorm
1421 	bra	mv_out
1422 
1423 
1424 |
1425 |	MOVE OUT
1426 |
1427 
1428 mv_tbl:
1429 	.long	li
1430 	.long	sgp
1431 	.long	xp
1432 	.long	mvout_end	|should never be taken
1433 	.long	wi
1434 	.long	dp
1435 	.long	bi
1436 	.long	mvout_end	|should never be taken
1437 mv_out:
1438 	bfextu	CMDREG1B(%a6){#3:#3},%d1	|put source specifier in d1
1439 	leal	mv_tbl,%a0
1440 	movel	%a0@(%d1:l:4),%a0
1441 	jmp	(%a0)
1442 
1443 |
1444 | This exit is for move-out to memory.  The aunfl bit is
1445 | set if the result is inex and unfl is signalled.
1446 |
1447 mvout_end:
1448 	btstb	#inex2_bit,FPSR_EXCEPT(%a6)
1449 	beqs	no_aufl
1450 	btstb	#unfl_bit,FPSR_EXCEPT(%a6)
1451 	beqs	no_aufl
1452 	bsetb	#aunfl_bit,FPSR_AEXCEPT(%a6)
1453 no_aufl:
1454 	clrw	NMNEXC(%a6)
1455 	bclrb	#E1,E_BYTE(%a6)
1456 	fmovel	#0,%FPSR			|clear any cc bits from res_func
1457 |
1458 | Return ETEMP to extended format from internal extended format so
1459 | that gen_except will have a correctly signed value for ovfl/unfl
1460 | handlers.
1461 |
1462 	bfclr	ETEMP_SGN(%a6){#0:#8}
1463 	beqs	mvout_con
1464 	bsetb	#sign_bit,ETEMP_EX(%a6)
1465 mvout_con:
1466 	rts
1467 |
1468 | This exit is for move-out to int register.  The aunfl bit is
1469 | not set in any case for this move.
1470 |
1471 mvouti_end:
1472 	clrw	NMNEXC(%a6)
1473 	bclrb	#E1,E_BYTE(%a6)
1474 	fmovel	#0,%FPSR			|clear any cc bits from res_func
1475 |
1476 | Return ETEMP to extended format from internal extended format so
1477 | that gen_except will have a correctly signed value for ovfl/unfl
1478 | handlers.
1479 |
1480 	bfclr	ETEMP_SGN(%a6){#0:#8}
1481 	beqs	mvouti_con
1482 	bsetb	#sign_bit,ETEMP_EX(%a6)
1483 mvouti_con:
1484 	rts
1485 |
1486 | li is used to handle a long integer source specifier
1487 |
1488 
1489 li:
1490 	moveql	#4,%d0		|set byte count
1491 
1492 	btstb	#7,STAG(%a6)	|check for extended denorm
1493 	bne	int_dnrm	|if so, branch
1494 
1495 	fmovemx ETEMP(%a6),%fp0-%fp0
1496 	fcmpd	#0x41dfffffffc00000,%fp0
1497 | 41dfffffffc00000 in dbl prec = 401d0000fffffffe00000000 in ext prec
1498 	fbge	lo_plrg
1499 	fcmpd	#0xc1e0000000000000,%fp0
1500 | c1e0000000000000 in dbl prec = c01e00008000000000000000 in ext prec
1501 	fble	lo_nlrg
1502 |
1503 | at this point, the answer is between the largest pos and neg values
1504 |
1505 	movel	USER_FPCR(%a6),%d1	|use user's rounding mode
1506 	andil	#0x30,%d1
1507 	fmovel	%d1,%fpcr
1508 	fmovel	%fp0,L_SCR1(%a6)	|let the 040 perform conversion
1509 	fmovel %fpsr,%d1
1510 	orl	%d1,USER_FPSR(%a6)	|capture inex2/ainex if set
1511 	bra	int_wrt
1512 
1513 
1514 lo_plrg:
1515 	movel	#0x7fffffff,L_SCR1(%a6)	|answer is largest positive int
1516 	fbeq	int_wrt			|exact answer
1517 	fcmpd	#0x41dfffffffe00000,%fp0
1518 | 41dfffffffe00000 in dbl prec = 401d0000ffffffff00000000 in ext prec
1519 	fbge	int_operr		|set operr
1520 	bra	int_inx			|set inexact
1521 
1522 lo_nlrg:
1523 	movel	#0x80000000,L_SCR1(%a6)
1524 	fbeq	int_wrt			|exact answer
1525 	fcmpd	#0xc1e0000000100000,%fp0
1526 | c1e0000000100000 in dbl prec = c01e00008000000080000000 in ext prec
1527 	fblt	int_operr		|set operr
1528 	bra	int_inx			|set inexact
1529 
1530 |
1531 | wi is used to handle a word integer source specifier
1532 |
1533 
1534 wi:
1535 	moveql	#2,%d0		|set byte count
1536 
1537 	btstb	#7,STAG(%a6)	|check for extended denorm
1538 	bne	int_dnrm	|branch if so
1539 
1540 	fmovemx ETEMP(%a6),%fp0-%fp0
1541 	fcmps	#0x46fffe00,%fp0
1542 | 46fffe00 in sgl prec = 400d0000fffe000000000000 in ext prec
1543 	fbge	wo_plrg
1544 	fcmps	#0xc7000000,%fp0
1545 | c7000000 in sgl prec = c00e00008000000000000000 in ext prec
1546 	fble	wo_nlrg
1547 
1548 |
1549 | at this point, the answer is between the largest pos and neg values
1550 |
1551 	movel	USER_FPCR(%a6),%d1	|use user's rounding mode
1552 	andil	#0x30,%d1
1553 	fmovel	%d1,%fpcr
1554 	fmovew	%fp0,L_SCR1(%a6)	|let the 040 perform conversion
1555 	fmovel %fpsr,%d1
1556 	orl	%d1,USER_FPSR(%a6)	|capture inex2/ainex if set
1557 	bra	int_wrt
1558 
1559 wo_plrg:
1560 	movew	#0x7fff,L_SCR1(%a6)	|answer is largest positive int
1561 	fbeq	int_wrt			|exact answer
1562 	fcmps	#0x46ffff00,%fp0
1563 | 46ffff00 in sgl prec = 400d0000ffff000000000000 in ext prec
1564 	fbge	int_operr		|set operr
1565 	bra	int_inx			|set inexact
1566 
1567 wo_nlrg:
1568 	movew	#0x8000,L_SCR1(%a6)
1569 	fbeq	int_wrt			|exact answer
1570 	fcmps	#0xc7000080,%fp0
1571 | c7000080 in sgl prec = c00e00008000800000000000 in ext prec
1572 	fblt	int_operr		|set operr
1573 	bra	int_inx			|set inexact
1574 
1575 |
1576 | bi is used to handle a byte integer source specifier
1577 |
1578 
1579 bi:
1580 	moveql	#1,%d0		|set byte count
1581 
1582 	btstb	#7,STAG(%a6)	|check for extended denorm
1583 	bne	int_dnrm	|branch if so
1584 
1585 	fmovemx ETEMP(%a6),%fp0-%fp0
1586 	fcmps	#0x42fe0000,%fp0
1587 | 42fe0000 in sgl prec = 40050000fe00000000000000 in ext prec
1588 	fbge	by_plrg
1589 	fcmps	#0xc3000000,%fp0
1590 | c3000000 in sgl prec = c00600008000000000000000 in ext prec
1591 	fble	by_nlrg
1592 
1593 |
1594 | at this point, the answer is between the largest pos and neg values
1595 |
1596 	movel	USER_FPCR(%a6),%d1	|use user's rounding mode
1597 	andil	#0x30,%d1
1598 	fmovel	%d1,%fpcr
1599 	fmoveb	%fp0,L_SCR1(%a6)	|let the 040 perform conversion
1600 	fmovel %fpsr,%d1
1601 	orl	%d1,USER_FPSR(%a6)	|capture inex2/ainex if set
1602 	bra	int_wrt
1603 
1604 by_plrg:
1605 	moveb	#0x7f,L_SCR1(%a6)		|answer is largest positive int
1606 	fbeq	int_wrt			|exact answer
1607 	fcmps	#0x42ff0000,%fp0
1608 | 42ff0000 in sgl prec = 40050000ff00000000000000 in ext prec
1609 	fbge	int_operr		|set operr
1610 	bra	int_inx			|set inexact
1611 
1612 by_nlrg:
1613 	moveb	#0x80,L_SCR1(%a6)
1614 	fbeq	int_wrt			|exact answer
1615 	fcmps	#0xc3008000,%fp0
1616 | c3008000 in sgl prec = c00600008080000000000000 in ext prec
1617 	fblt	int_operr		|set operr
1618 	bra	int_inx			|set inexact
1619 
1620 |
1621 | Common integer routines
1622 |
1623 | int_drnrm---account for possible nonzero result for round up with positive
1624 | operand and round down for negative answer.  In the first case (result = 1)
1625 | byte-width (store in d0) of result must be honored.  In the second case,
1626 | -1 in L_SCR1(a6) will cover all contingencies (FMOVE.B/W/L out).
1627 
1628 int_dnrm:
1629 	movel	#0,L_SCR1(%a6)	| initialize result to 0
1630 	bfextu	FPCR_MODE(%a6){#2:#2},%d1	| d1 is the rounding mode
1631 	cmpb	#2,%d1
1632 	bmis	int_inx		| if RN or RZ, done
1633 	bnes	int_rp		| if RP, continue below
1634 	tstw	ETEMP(%a6)	| RM: store -1 in L_SCR1 if src is negative
1635 	bpls	int_inx		| otherwise result is 0
1636 	movel	#-1,L_SCR1(%a6)
1637 	bras	int_inx
1638 int_rp:
1639 	tstw	ETEMP(%a6)	| RP: store +1 of proper width in L_SCR1 if
1640 |				; source is greater than 0
1641 	bmis	int_inx		| otherwise, result is 0
1642 	lea	L_SCR1(%a6),%a1	| a1 is address of L_SCR1
1643 	addal	%d0,%a1		| offset by destination width -1
1644 	subal	#1,%a1
1645 	bsetb	#0,(%a1)		| set low bit at a1 address
1646 int_inx:
1647 	oril	#inx2a_mask,USER_FPSR(%a6)
1648 	bras	int_wrt
1649 int_operr:
1650 	fmovemx %fp0-%fp0,FPTEMP(%a6)	|FPTEMP must contain the extended
1651 |				;precision source that needs to be
1652 |				;converted to integer this is required
1653 |				;if the operr exception is enabled.
1654 |				;set operr/aiop (no inex2 on int ovfl)
1655 
1656 	oril	#opaop_mask,USER_FPSR(%a6)
1657 |				;fall through to perform int_wrt
1658 int_wrt:
1659 	movel	EXC_EA(%a6),%a1	|load destination address
1660 	tstl	%a1		|check to see if it is a dest register
1661 	beqs	wrt_dn		|write data register
1662 	lea	L_SCR1(%a6),%a0	|point to supervisor source address
1663 	bsrl	mem_write
1664 	bra	mvouti_end
1665 
1666 wrt_dn:
1667 	movel	%d0,-(%sp)	|d0 currently contains the size to write
1668 	bsrl	get_fline	|get_fline returns Dn in d0
1669 	andiw	#0x7,%d0		|isolate register
1670 	movel	(%sp)+,%d1	|get size
1671 	cmpil	#4,%d1		|most frequent case
1672 	beqs	sz_long
1673 	cmpil	#2,%d1
1674 	bnes	sz_con
1675 	orl	#8,%d0		|add 'word' size to register#
1676 	bras	sz_con
1677 sz_long:
1678 	orl	#0x10,%d0		|add 'long' size to register#
1679 sz_con:
1680 	movel	%d0,%d1		|reg_dest expects size:reg in d1
1681 	bsrl	reg_dest	|load proper data register
1682 	bra	mvouti_end
1683 xp:
1684 	lea	ETEMP(%a6),%a0
1685 	bclrb	#sign_bit,LOCAL_EX(%a0)
1686 	sne	LOCAL_SGN(%a0)
1687 	btstb	#7,STAG(%a6)	|check for extended denorm
1688 	bne	xdnrm
1689 	clrl	%d0
1690 	bras	do_fp		|do normal case
1691 sgp:
1692 	lea	ETEMP(%a6),%a0
1693 	bclrb	#sign_bit,LOCAL_EX(%a0)
1694 	sne	LOCAL_SGN(%a0)
1695 	btstb	#7,STAG(%a6)	|check for extended denorm
1696 	bne	sp_catas	|branch if so
1697 	movew	LOCAL_EX(%a0),%d0
1698 	lea	sp_bnds,%a1
1699 	cmpw	(%a1),%d0
1700 	blt	sp_under
1701 	cmpw	2(%a1),%d0
1702 	bgt	sp_over
1703 	movel	#1,%d0		|set destination format to single
1704 	bras	do_fp		|do normal case
1705 dp:
1706 	lea	ETEMP(%a6),%a0
1707 	bclrb	#sign_bit,LOCAL_EX(%a0)
1708 	sne	LOCAL_SGN(%a0)
1709 
1710 	btstb	#7,STAG(%a6)	|check for extended denorm
1711 	bne	dp_catas	|branch if so
1712 
1713 	movew	LOCAL_EX(%a0),%d0
1714 	lea	dp_bnds,%a1
1715 
1716 	cmpw	(%a1),%d0
1717 	blt	dp_under
1718 	cmpw	2(%a1),%d0
1719 	bgt	dp_over
1720 
1721 	movel	#2,%d0		|set destination format to double
1722 |				;fall through to do_fp
1723 |
1724 do_fp:
1725 	bfextu	FPCR_MODE(%a6){#2:#2},%d1	|rnd mode in d1
1726 	swap	%d0			|rnd prec in upper word
1727 	addl	%d0,%d1			|d1 has PREC/MODE info
1728 
1729 	clrl	%d0			|clear g,r,s
1730 
1731 	bsrl	round			|round
1732 
1733 	movel	%a0,%a1
1734 	movel	EXC_EA(%a6),%a0
1735 
1736 	bfextu	CMDREG1B(%a6){#3:#3},%d1	|extract destination format
1737 |					;at this point only the dest
1738 |					;formats sgl, dbl, ext are
1739 |					;possible
1740 	cmpb	#2,%d1
1741 	bgts	ddbl			|double=5, extended=2, single=1
1742 	bnes	dsgl
1743 |					;fall through to dext
1744 dext:
1745 	bsrl	dest_ext
1746 	bra	mvout_end
1747 dsgl:
1748 	bsrl	dest_sgl
1749 	bra	mvout_end
1750 ddbl:
1751 	bsrl	dest_dbl
1752 	bra	mvout_end
1753 
1754 |
1755 | Handle possible denorm or catastrophic underflow cases here
1756 |
1757 xdnrm:
1758 	bsr	set_xop		|initialize WBTEMP
1759 	bsetb	#wbtemp15_bit,WB_BYTE(%a6) |set wbtemp15
1760 
1761 	movel	%a0,%a1
1762 	movel	EXC_EA(%a6),%a0	|a0 has the destination pointer
1763 	bsrl	dest_ext	|store to memory
1764 	bsetb	#unfl_bit,FPSR_EXCEPT(%a6)
1765 	bra	mvout_end
1766 
1767 sp_under:
1768 	bsetb	#etemp15_bit,STAG(%a6)
1769 
1770 	cmpw	4(%a1),%d0
1771 	blts	sp_catas	|catastrophic underflow case
1772 
1773 	movel	#1,%d0		|load in round precision
1774 	movel	#sgl_thresh,%d1	|load in single denorm threshold
1775 	bsrl	dpspdnrm	|expects d1 to have the proper
1776 |				;denorm threshold
1777 	bsrl	dest_sgl	|stores value to destination
1778 	bsetb	#unfl_bit,FPSR_EXCEPT(%a6)
1779 	bra	mvout_end	|exit
1780 
1781 dp_under:
1782 	bsetb	#etemp15_bit,STAG(%a6)
1783 
1784 	cmpw	4(%a1),%d0
1785 	blts	dp_catas	|catastrophic underflow case
1786 
1787 	movel	#dbl_thresh,%d1	|load in double precision threshold
1788 	movel	#2,%d0
1789 	bsrl	dpspdnrm	|expects d1 to have proper
1790 |				;denorm threshold
1791 |				;expects d0 to have round precision
1792 	bsrl	dest_dbl	|store value to destination
1793 	bsetb	#unfl_bit,FPSR_EXCEPT(%a6)
1794 	bra	mvout_end	|exit
1795 
1796 |
1797 | Handle catastrophic underflow cases here
1798 |
1799 sp_catas:
1800 | Temp fix for z bit set in unf_sub
1801 	movel	USER_FPSR(%a6),-(%a7)
1802 
1803 	movel	#1,%d0		|set round precision to sgl
1804 
1805 	bsrl	unf_sub		|a0 points to result
1806 
1807 	movel	(%a7)+,USER_FPSR(%a6)
1808 
1809 	movel	#1,%d0
1810 	subw	%d0,LOCAL_EX(%a0) |account for difference between
1811 |				;denorm/norm bias
1812 
1813 	movel	%a0,%a1		|a1 has the operand input
1814 	movel	EXC_EA(%a6),%a0	|a0 has the destination pointer
1815 
1816 	bsrl	dest_sgl	|store the result
1817 	oril	#unfinx_mask,USER_FPSR(%a6)
1818 	bra	mvout_end
1819 
1820 dp_catas:
1821 | Temp fix for z bit set in unf_sub
1822 	movel	USER_FPSR(%a6),-(%a7)
1823 
1824 	movel	#2,%d0		|set round precision to dbl
1825 	bsrl	unf_sub		|a0 points to result
1826 
1827 	movel	(%a7)+,USER_FPSR(%a6)
1828 
1829 	movel	#1,%d0
1830 	subw	%d0,LOCAL_EX(%a0) |account for difference between
1831 |				;denorm/norm bias
1832 
1833 	movel	%a0,%a1		|a1 has the operand input
1834 	movel	EXC_EA(%a6),%a0	|a0 has the destination pointer
1835 
1836 	bsrl	dest_dbl	|store the result
1837 	oril	#unfinx_mask,USER_FPSR(%a6)
1838 	bra	mvout_end
1839 
1840 |
1841 | Handle catastrophic overflow cases here
1842 |
1843 sp_over:
1844 | Temp fix for z bit set in unf_sub
1845 	movel	USER_FPSR(%a6),-(%a7)
1846 
1847 	movel	#1,%d0
1848 	leal	FP_SCR1(%a6),%a0	|use FP_SCR1 for creating result
1849 	movel	ETEMP_EX(%a6),(%a0)
1850 	movel	ETEMP_HI(%a6),4(%a0)
1851 	movel	ETEMP_LO(%a6),8(%a0)
1852 	bsrl	ovf_res
1853 
1854 	movel	(%a7)+,USER_FPSR(%a6)
1855 
1856 	movel	%a0,%a1
1857 	movel	EXC_EA(%a6),%a0
1858 	bsrl	dest_sgl
1859 	orl	#ovfinx_mask,USER_FPSR(%a6)
1860 	bra	mvout_end
1861 
1862 dp_over:
1863 | Temp fix for z bit set in ovf_res
1864 	movel	USER_FPSR(%a6),-(%a7)
1865 
1866 	movel	#2,%d0
1867 	leal	FP_SCR1(%a6),%a0	|use FP_SCR1 for creating result
1868 	movel	ETEMP_EX(%a6),(%a0)
1869 	movel	ETEMP_HI(%a6),4(%a0)
1870 	movel	ETEMP_LO(%a6),8(%a0)
1871 	bsrl	ovf_res
1872 
1873 	movel	(%a7)+,USER_FPSR(%a6)
1874 
1875 	movel	%a0,%a1
1876 	movel	EXC_EA(%a6),%a0
1877 	bsrl	dest_dbl
1878 	orl	#ovfinx_mask,USER_FPSR(%a6)
1879 	bra	mvout_end
1880 
1881 |
1882 |	DPSPDNRM
1883 |
1884 | This subroutine takes an extended normalized number and denormalizes
1885 | it to the given round precision. This subroutine also decrements
1886 | the input operand's exponent by 1 to account for the fact that
1887 | dest_sgl or dest_dbl expects a normalized number's bias.
1888 |
1889 | Input: a0  points to a normalized number in internal extended format
1890 |	 d0  is the round precision (=1 for sgl; =2 for dbl)
1891 |	 d1  is the single precision or double precision
1892 |	     denorm threshold
1893 |
1894 | Output: (In the format for dest_sgl or dest_dbl)
1895 |	 a0   points to the destination
1896 |	 a1   points to the operand
1897 |
1898 | Exceptions: Reports inexact 2 exception by setting USER_FPSR bits
1899 |
1900 dpspdnrm:
1901 	movel	%d0,-(%a7)	|save round precision
1902 	clrl	%d0		|clear initial g,r,s
1903 	bsrl	dnrm_lp		|careful with d0, it's needed by round
1904 
1905 	bfextu	FPCR_MODE(%a6){#2:#2},%d1 |get rounding mode
1906 	swap	%d1
1907 	movew	2(%a7),%d1	|set rounding precision
1908 	swap	%d1		|at this point d1 has PREC/MODE info
1909 	bsrl	round		|round result, sets the inex bit in
1910 |				;USER_FPSR if needed
1911 
1912 	movew	#1,%d0
1913 	subw	%d0,LOCAL_EX(%a0) |account for difference in denorm
1914 |				;vs norm bias
1915 
1916 	movel	%a0,%a1		|a1 has the operand input
1917 	movel	EXC_EA(%a6),%a0	|a0 has the destination pointer
1918 	addw	#4,%a7		|pop stack
1919 	rts
1920 |
1921 | SET_XOP initialized WBTEMP with the value pointed to by a0
1922 | input: a0 points to input operand in the internal extended format
1923 |
1924 set_xop:
1925 	movel	LOCAL_EX(%a0),WBTEMP_EX(%a6)
1926 	movel	LOCAL_HI(%a0),WBTEMP_HI(%a6)
1927 	movel	LOCAL_LO(%a0),WBTEMP_LO(%a6)
1928 	bfclr	WBTEMP_SGN(%a6){#0:#8}
1929 	beqs	sxop
1930 	bsetb	#sign_bit,WBTEMP_EX(%a6)
1931 sxop:
1932 	bfclr	STAG(%a6){#5:#4}	|clear wbtm66,wbtm1,wbtm0,sbit
1933 	rts
1934 |
1935 |	P_MOVE
1936 |
1937 p_movet:
1938 	.long	p_move
1939 	.long	p_movez
1940 	.long	p_movei
1941 	.long	p_moven
1942 	.long	p_move
1943 p_regd:
1944 	.long	p_dyd0
1945 	.long	p_dyd1
1946 	.long	p_dyd2
1947 	.long	p_dyd3
1948 	.long	p_dyd4
1949 	.long	p_dyd5
1950 	.long	p_dyd6
1951 	.long	p_dyd7
1952 
1953 pack_out:
1954 	leal	p_movet,%a0	|load jmp table address
1955 	movew	STAG(%a6),%d0	|get source tag
1956 	bfextu	%d0{#16:#3},%d0	|isolate source bits
1957 	movel	(%a0,%d0.w*4),%a0	|load a0 with routine label for tag
1958 	jmp	(%a0)		|go to the routine
1959 
1960 p_write:
1961 	movel	#0x0c,%d0	|get byte count
1962 	movel	EXC_EA(%a6),%a1	|get the destination address
1963 	bsr	mem_write	|write the user's destination
1964 	moveb	#0,CU_SAVEPC(%a6) |set the cu save pc to all 0's
1965 
1966 |
1967 | Also note that the dtag must be set to norm here - this is because
1968 | the 040 uses the dtag to execute the correct microcode.
1969 |
1970         bfclr    DTAG(%a6){#0:#3}  |set dtag to norm
1971 
1972 	rts
1973 
1974 | Notes on handling of special case (zero, inf, and nan) inputs:
1975 |	1. Operr is not signalled if the k-factor is greater than 18.
1976 |	2. Per the manual, status bits are not set.
1977 |
1978 
1979 p_move:
1980 	movew	CMDREG1B(%a6),%d0
1981 	btstl	#kfact_bit,%d0	|test for dynamic k-factor
1982 	beqs	statick		|if clear, k-factor is static
1983 dynamick:
1984 	bfextu	%d0{#25:#3},%d0	|isolate register for dynamic k-factor
1985 	lea	p_regd,%a0
1986 	movel	%a0@(%d0:l:4),%a0
1987 	jmp	(%a0)
1988 statick:
1989 	andiw	#0x007f,%d0	|get k-factor
1990 	bfexts	%d0{#25:#7},%d0	|sign extend d0 for bindec
1991 	leal	ETEMP(%a6),%a0	|a0 will point to the packed decimal
1992 	bsrl	bindec		|perform the convert; data at a6
1993 	leal	FP_SCR1(%a6),%a0	|load a0 with result address
1994 	bral	p_write
1995 p_movez:
1996 	leal	ETEMP(%a6),%a0	|a0 will point to the packed decimal
1997 	clrw	2(%a0)		|clear lower word of exp
1998 	clrl	4(%a0)		|load second lword of ZERO
1999 	clrl	8(%a0)		|load third lword of ZERO
2000 	bra	p_write		|go write results
2001 p_movei:
2002 	fmovel	#0,%FPSR		|clear aiop
2003 	leal	ETEMP(%a6),%a0	|a0 will point to the packed decimal
2004 	clrw	2(%a0)		|clear lower word of exp
2005 	bra	p_write		|go write the result
2006 p_moven:
2007 	leal	ETEMP(%a6),%a0	|a0 will point to the packed decimal
2008 	clrw	2(%a0)		|clear lower word of exp
2009 	bra	p_write		|go write the result
2010 
2011 |
2012 | Routines to read the dynamic k-factor from Dn.
2013 |
2014 p_dyd0:
2015 	movel	USER_D0(%a6),%d0
2016 	bras	statick
2017 p_dyd1:
2018 	movel	USER_D1(%a6),%d0
2019 	bras	statick
2020 p_dyd2:
2021 	movel	%d2,%d0
2022 	bras	statick
2023 p_dyd3:
2024 	movel	%d3,%d0
2025 	bras	statick
2026 p_dyd4:
2027 	movel	%d4,%d0
2028 	bras	statick
2029 p_dyd5:
2030 	movel	%d5,%d0
2031 	bras	statick
2032 p_dyd6:
2033 	movel	%d6,%d0
2034 	bra	statick
2035 p_dyd7:
2036 	movel	%d7,%d0
2037 	bra	statick
2038 
2039 	|end
2040