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