8f6b95217865fbed19a6acae1d906b0b960d865f
[linux-3.10.git] / arch / m68k / fpsp040 / res_func.S
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
23 RES_FUNC:    |idnt    2,1 | Motorola 040 Floating Point Software Package
24
25         |section        8
26
27 #include "fpsp.h"
28
29 sp_bnds:        .short  0x3f81,0x407e
30                 .short  0x3f6a,0x0000
31 dp_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
53 res_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
59 dyadic:
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)
77 dpos:
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)
81 monadic:
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 |
115 cu_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 |
129 cu_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
135 cu_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
144 cu_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
150 cu_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)
156 cu_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 |
162 cu_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 |
172 cu_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 |
184 cu_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 |
197 cu_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 |
206 cu_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)
221 cu_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 |
230 cu_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)
245 cu_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 |
254 cu_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 |
261 cu_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)
268 cu_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
279 inst_d:
280         orl     #0x00020000,%d1
281         bra     inst_sd
282 inst_s:
283         orl     #0x00010000,%d1
284 inst_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)
291 cu_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
295 cu_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
309 cu_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
315 cu_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
320 cu_nincd:
321         orl     #0x800,LOCAL_LO(%a0) |inc for double
322         bra     cu_nunzro
323 cu_nincs:
324         orl     #0x100,LOCAL_HI(%a0) |inc for single
325         bra     cu_nunzro
326 cu_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
332 cu_nunzro:
333         orl     #unfl_mask,USER_FPSR(%a6) |set unfl
334 cu_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 |
342 cu_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
347 cu_wrzero:
348         orl     #z_mask,USER_FPSR(%a6)  |set Z bit
349 cu_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
360 mon_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
387 nrm_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)
397 spos:
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)
401 normal:
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
405 fix_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 |
418 cu_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 |
432 cu_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
438 cu_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
447 cu_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
452 cu_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)
458 cu_dtsx:
459         rts
460 |
461 | Inst is fabs.  Execute the absolute value function on the input.
462 | Branch to the fmove code.
463 |
464 cu_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 |
471 cu_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 |
481 cu_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 |
490 cu_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 |
500 cu_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
507 cu_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 |
515 cu_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
522 cu_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.
530 cu_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 |
540 cu_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
547 cu_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
553 cu_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
561 cu_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 |
572 cu_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
579 cu_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
585 cu_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
593 cu_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
678 ck_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 |
694 wrap_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
703 div_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
720 ckinf_ns:
721         moveb   STAG(%a6),%d0           |check source tag for inf or nan
722         bra     ck_in_com
723 ckinf_nd:
724         moveb   DTAG(%a6),%d0           |check destination tag for inf or nan
725 ck_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
736 nan_or_inf:
737         moveql  #-1,%d0
738         rts
739
740
741
742 div_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 |
761 force_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
773 frcovf_dbl:
774         movel   #0x2,%d0                        |inst is forced double
775         bras    frcovf_rnd
776 frcovf_fpcr:
777         bfextu  FPCR_MODE(%a6){#0:#2},%d0       |inst not forced - use fpcr prec
778 frcovf_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 |
798 wrap_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
807 add_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
816 add_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 |
834 add_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
872 add_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 |
903 add_same:
904         cmpb    #0x0f,DNRM_FLG(%a6) |is dest the denorm?
905         bnes    add_s_srcd
906 add_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)
923 add_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
932 add_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)
949 add_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)
957 add_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 |
973 wrap_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
982 sub_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
991 sub_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 |
1009 sub_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
1047 sub_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 |
1078 sub_diff:
1079         cmpb    #0x0f,DNRM_FLG(%a6) |is dest the denorm?
1080         bnes    sub_s_srcd
1081 sub_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)
1100 sub_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)
1107 sub_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
1113 sub_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)
1130 sub_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)
1138 sub_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 |
1154 wrap_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
1163 cmp_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
1174 cmp_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
1185 cmp_setn:
1186         orl     #neg_mask,USER_FPSR(%a6)
1187         rts
1188
1189 |
1190 | Inst is fmul.
1191 |
1192 wrap_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
1201 mul_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
1209 mul_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 |
1221 force_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)
1232 frcunfcont:
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
1241 frcunf_dbl:
1242         movel   #0x2,%d0                        |inst is forced double
1243         bras    frcunf_rnd
1244 frcunf_fpcr:
1245         bfextu  FPCR_MODE(%a6){#0:#2},%d0       |inst not forced - use fpcr prec
1246 frcunf_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 |
1261 frcfpnr:
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
1269 frcfpn_dbl:
1270         movel   #0x2,%d0                        |inst is forced double
1271         bras    frcfpn_rnd
1272 frcfpn_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
1276 frcfpn_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)
1285 frcfpn_clr:
1286         orl     #ovfinx_mask,USER_FPSR(%a6)
1287 |
1288 | Perform the write.
1289 |
1290 frcfpn:
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
1300 frc0123:
1301         cmpib   #0,%d0
1302         beqs    frc0_dst
1303         cmpib   #1,%d0
1304         beqs    frc1_dst
1305         cmpib   #2,%d0
1306         beqs    frc2_dst
1307 frc3_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
1312 frc2_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
1317 frc1_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
1322 frc0_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 |
1335 wr_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
1345 snan_neg:
1346         orl     #neg_bit,USER_FPSR(%a6) |snan is negative; set N
1347         rts
1348 fmoveinc:
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
1360 fminc_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
1370 fminc_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)
1377 fminc_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
1388 fp0123:
1389         cmpib   #0,%d0
1390         beqs    fp0_dst
1391         cmpib   #1,%d0
1392         beqs    fp1_dst
1393         cmpib   #2,%d0
1394         beqs    fp2_dst
1395 fp3_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
1400 fp2_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
1405 fp1_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
1410 fp0_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
1416 opclass3:
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
1429 mv_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
1438 mv_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 |
1448 mvout_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)
1454 no_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)
1466 mvout_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 |
1472 mvouti_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)
1484 mvouti_con:
1485         rts
1486 |
1487 | li is used to handle a long integer source specifier
1488 |
1489
1490 li:
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
1515 lo_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
1523 lo_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
1535 wi:
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
1560 wo_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
1568 wo_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
1580 bi:
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
1605 by_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
1613 by_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
1629 int_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
1639 int_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
1647 int_inx:
1648         oril    #inx2a_mask,USER_FPSR(%a6)
1649         bras    int_wrt
1650 int_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
1659 int_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
1667 wrt_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
1678 sz_long:
1679         orl     #0x10,%d0               |add 'long' size to register#
1680 sz_con:
1681         movel   %d0,%d1         |reg_dest expects size:reg in d1
1682         bsrl    reg_dest        |load proper data register
1683         bra     mvouti_end
1684 xp:
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
1692 sgp:
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
1706 dp:
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 |
1725 do_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
1745 dext:
1746         bsrl    dest_ext
1747         bra     mvout_end
1748 dsgl:
1749         bsrl    dest_sgl
1750         bra     mvout_end
1751 ddbl:
1752         bsrl    dest_dbl
1753         bra     mvout_end
1754
1755 |
1756 | Handle possible denorm or catastrophic underflow cases here
1757 |
1758 xdnrm:
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
1768 sp_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
1782 dp_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 |
1800 sp_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
1821 dp_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 |
1844 sp_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
1863 dp_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 |
1901 dpspdnrm:
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 |
1925 set_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)
1932 sxop:
1933         bfclr   STAG(%a6){#5:#4}        |clear wbtm66,wbtm1,wbtm0,sbit
1934         rts
1935 |
1936 |       P_MOVE
1937 |
1938 p_movet:
1939         .long   p_move
1940         .long   p_movez
1941         .long   p_movei
1942         .long   p_moven
1943         .long   p_move
1944 p_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
1954 pack_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
1961 p_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
1980 p_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
1984 dynamick:
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)
1989 statick:
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
1996 p_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
2002 p_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
2007 p_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 |
2015 p_dyd0:
2016         movel   USER_D0(%a6),%d0
2017         bras    statick
2018 p_dyd1:
2019         movel   USER_D1(%a6),%d0
2020         bras    statick
2021 p_dyd2:
2022         movel   %d2,%d0
2023         bras    statick
2024 p_dyd3:
2025         movel   %d3,%d0
2026         bras    statick
2027 p_dyd4:
2028         movel   %d4,%d0
2029         bras    statick
2030 p_dyd5:
2031         movel   %d5,%d0
2032         bras    statick
2033 p_dyd6:
2034         movel   %d6,%d0
2035         bra     statick
2036 p_dyd7:
2037         movel   %d7,%d0
2038         bra     statick
2039
2040         |end