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