*       $NetBSD: do_func.sa,v 1.3 2001/12/09 01:43:13 briggs Exp $

*       MOTOROLA MICROPROCESSOR & MEMORY TECHNOLOGY GROUP
*       M68000 Hi-Performance Microprocessor Division
*       M68040 Software Package
*
*       M68040 Software Package Copyright (c) 1993, 1994 Motorola Inc.
*       All rights reserved.
*
*       THE SOFTWARE is provided on an "AS IS" basis and without warranty.
*       To the maximum extent permitted by applicable law,
*       MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED,
*       INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
*       PARTICULAR PURPOSE and any warranty against infringement with
*       regard to the SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF)
*       and any accompanying written materials.
*
*       To the maximum extent permitted by applicable law,
*       IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER
*       (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS
*       PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR
*       OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE
*       SOFTWARE.  Motorola assumes no responsibility for the maintenance
*       and support of the SOFTWARE.
*
*       You are hereby granted a copyright license to use, modify, and
*       distribute the SOFTWARE so long as this entire notice is retained
*       without alteration in any modified and/or redistributed versions,
*       and that such modified versions are clearly identified as such.
*       No licenses are granted by implication, estoppel or otherwise
*       under any patents or trademarks of Motorola, Inc.

*
*       do_func.sa 3.4 2/18/91
*
* Do_func performs the unimplemented operation.  The operation
* to be performed is determined from the lower 7 bits of the
* extension word (except in the case of fmovecr and fsincos).
* The opcode and tag bits form an index into a jump table in
* tbldo.sa.  Cases of zero, infinity and NaN are handled in
* do_func by forcing the default result.  Normalized and
* denormalized (there are no unnormalized numbers at this
* point) are passed onto the emulation code.
*
* CMDREG1B and STAG are extracted from the fsave frame
* and combined to form the table index.  The function called
* will start with a0 pointing to the ETEMP operand.  Dyadic
* functions can find FPTEMP at -12(a0).
*
* Called functions return their result in fp0.  Sincos returns
* sin(x) in fp0 and cos(x) in fp1.
*

DO_FUNC IDNT    2,1 Motorola 040 Floating Point Software Package

       section 8

       include fpsp.h

       xref    t_dz2
       xref    t_operr
       xref    t_inx2
       xref    t_resdnrm
       xref    dst_nan
       xref    src_nan
       xref    nrm_set
       xref    sto_cos

       xref    tblpre
       xref    slognp1,slogn,slog10,slog2
       xref    slognd,slog10d,slog2d
       xref    smod,srem
       xref    sscale
       xref    smovcr

PONE    dc.l    $3fff0000,$80000000,$00000000   ;+1
MONE    dc.l    $bfff0000,$80000000,$00000000   ;-1
PZERO   dc.l    $00000000,$00000000,$00000000   ;+0
MZERO   dc.l    $80000000,$00000000,$00000000   ;-0
PINF    dc.l    $7fff0000,$00000000,$00000000   ;+inf
MINF    dc.l    $ffff0000,$00000000,$00000000   ;-inf
QNAN    dc.l    $7fff0000,$ffffffff,$ffffffff   ;non-signaling nan
PPIBY2  dc.l    $3FFF0000,$C90FDAA2,$2168C235   ;+PI/2
MPIBY2  dc.l    $bFFF0000,$C90FDAA2,$2168C235   ;-PI/2

       xdef    do_func
do_func:
       clr.b   CU_ONLY(a6)
*
* Check for fmovecr.  It does not follow the format of fp gen
* unimplemented instructions.  The test is on the upper 6 bits;
* if they are $17, the inst is fmovecr.  Call entry smovcr
* directly.
*
       bfextu  CMDREG1B(a6){0:6},d0 ;get opclass and src fields
       cmpi.l  #$17,d0         ;if op class and size fields are $17,
*                               ;it is FMOVECR; if not, continue
       bne.b   not_fmovecr
       jmp     smovcr          ;fmovecr; jmp directly to emulation

not_fmovecr:
       move.w  CMDREG1B(a6),d0
       and.l   #$7F,d0
       cmpi.l  #$38,d0         ;if the extension is >= $38,
       bge.b   short_serror    ;it is illegal
       bfextu  STAG(a6){0:3},d1
       lsl.l   #3,d0           ;make room for STAG
       add.l   d1,d0           ;combine for final index into table
       lea.l   tblpre,a1       ;start of monster jump table
       move.l  (a1,d0.w*4),a1  ;real target address
       lea.l   ETEMP(a6),a0    ;a0 is pointer to src op
       move.l  USER_FPCR(a6),d1
       and.l   #$FF,d1         ; discard all but rounding mode/prec
       fmove.l #0,fpcr
       jmp     (a1)
*
*       ERROR
*
       xdef    serror
serror:
short_serror:
       st.b    STORE_FLG(a6)
       rts
*
* These routines load forced values into fp0.  They are called
* by index into tbldo.
*
* Load a signed zero to fp0 and set inex2/ainex
*
       xdef    snzrinx
snzrinx:
       btst.b  #sign_bit,LOCAL_EX(a0)  ;get sign of source operand
       bne.b   ld_mzinx        ;if negative, branch
       bsr     ld_pzero        ;bsr so we can return and set inx
       bra     t_inx2          ;now, set the inx for the next inst
ld_mzinx:
       bsr     ld_mzero        ;if neg, load neg zero, return here
       bra     t_inx2          ;now, set the inx for the next inst
*
* Load a signed zero to fp0; do not set inex2/ainex
*
       xdef    szero
szero:
       btst.b  #sign_bit,LOCAL_EX(a0) ;get sign of source operand
       bne     ld_mzero        ;if neg, load neg zero
       bra     ld_pzero        ;load positive zero
*
* Load a signed infinity to fp0; do not set inex2/ainex
*
       xdef    sinf
sinf:
       btst.b  #sign_bit,LOCAL_EX(a0)  ;get sign of source operand
       bne     ld_minf                 ;if negative branch
       bra     ld_pinf
*
* Load a signed one to fp0; do not set inex2/ainex
*
       xdef    sone
sone:
       btst.b  #sign_bit,LOCAL_EX(a0)  ;check sign of source
       bne     ld_mone
       bra     ld_pone
*
* Load a signed pi/2 to fp0; do not set inex2/ainex
*
       xdef    spi_2
spi_2:
       btst.b  #sign_bit,LOCAL_EX(a0)  ;check sign of source
       bne     ld_mpi2
       bra     ld_ppi2
*
* Load either a +0 or +inf for plus/minus operand
*
       xdef    szr_inf
szr_inf:
       btst.b  #sign_bit,LOCAL_EX(a0)  ;check sign of source
       bne     ld_pzero
       bra     ld_pinf
*
* Result is either an operr or +inf for plus/minus operand
* [Used by slogn, slognp1, slog10, and slog2]
*
       xdef    sopr_inf
sopr_inf:
       btst.b  #sign_bit,LOCAL_EX(a0)  ;check sign of source
       bne     t_operr
       bra     ld_pinf
*
*       FLOGNP1
*
       xdef    sslognp1
sslognp1:
       fmovem.x (a0),fp0
       fcmp.b  #-1,fp0
       fbgt    slognp1
       fbeq    t_dz2           ;if = -1, divide by zero exception
       fmove.l #0,FPSR         ;clr N flag
       bra     t_operr         ;take care of operands < -1
*
*       FETOXM1
*
       xdef    setoxm1i
setoxm1i:
       btst.b  #sign_bit,LOCAL_EX(a0)  ;check sign of source
       bne     ld_mone
       bra     ld_pinf
*
*       FLOGN
*
* Test for 1.0 as an input argument, returning +zero.  Also check
* the sign and return operr if negative.
*
       xdef    sslogn
sslogn:
       btst.b  #sign_bit,LOCAL_EX(a0)
       bne     t_operr         ;take care of operands < 0
       cmpi.w  #$3fff,LOCAL_EX(a0) ;test for 1.0 input
       bne     slogn
       cmpi.l  #$80000000,LOCAL_HI(a0)
       bne     slogn
       tst.l   LOCAL_LO(a0)
       bne     slogn
       fmove.x PZERO,fp0
       rts

       xdef    sslognd
sslognd:
       btst.b  #sign_bit,LOCAL_EX(a0)
       beq     slognd
       bra     t_operr         ;take care of operands < 0

*
*       FLOG10
*
       xdef    sslog10
sslog10:
       btst.b  #sign_bit,LOCAL_EX(a0)
       bne     t_operr         ;take care of operands < 0
       cmpi.w  #$3fff,LOCAL_EX(a0) ;test for 1.0 input
       bne     slog10
       cmpi.l  #$80000000,LOCAL_HI(a0)
       bne     slog10
       tst.l   LOCAL_LO(a0)
       bne     slog10
       fmove.x PZERO,fp0
       rts

       xdef    sslog10d
sslog10d:
       btst.b  #sign_bit,LOCAL_EX(a0)
       beq     slog10d
       bra     t_operr         ;take care of operands < 0

*
*       FLOG2
*
       xdef    sslog2
sslog2:
       btst.b  #sign_bit,LOCAL_EX(a0)
       bne     t_operr         ;take care of operands < 0
       cmpi.w  #$3fff,LOCAL_EX(a0) ;test for 1.0 input
       bne     slog2
       cmpi.l  #$80000000,LOCAL_HI(a0)
       bne     slog2
       tst.l   LOCAL_LO(a0)
       bne     slog2
       fmove.x PZERO,fp0
       rts

       xdef    sslog2d
sslog2d:
       btst.b  #sign_bit,LOCAL_EX(a0)
       beq     slog2d
       bra     t_operr         ;take care of operands < 0

*
*       FMOD
*
pmodt:
*                               ;$21 fmod
*                               ;dtag,stag
       dc.l    smod            ;  00,00  norm,norm = normal
       dc.l    smod_oper       ;  00,01  norm,zero = nan with operr
       dc.l    smod_fpn        ;  00,10  norm,inf  = fpn
       dc.l    smod_snan       ;  00,11  norm,nan  = nan
       dc.l    smod_zro        ;  01,00  zero,norm = +-zero
       dc.l    smod_oper       ;  01,01  zero,zero = nan with operr
       dc.l    smod_zro        ;  01,10  zero,inf  = +-zero
       dc.l    smod_snan       ;  01,11  zero,nan  = nan
       dc.l    smod_oper       ;  10,00  inf,norm  = nan with operr
       dc.l    smod_oper       ;  10,01  inf,zero  = nan with operr
       dc.l    smod_oper       ;  10,10  inf,inf   = nan with operr
       dc.l    smod_snan       ;  10,11  inf,nan   = nan
       dc.l    smod_dnan       ;  11,00  nan,norm  = nan
       dc.l    smod_dnan       ;  11,01  nan,zero  = nan
       dc.l    smod_dnan       ;  11,10  nan,inf   = nan
       dc.l    smod_dnan       ;  11,11  nan,nan   = nan

       xdef    pmod
pmod:
       clr.b   FPSR_QBYTE(a6) ; clear quotient field
       bfextu  STAG(a6){0:3},d0 ;stag = d0
       bfextu  DTAG(a6){0:3},d1 ;dtag = d1

*
* Alias extended denorms to norms for the jump table.
*
       bclr.l  #2,d0
       bclr.l  #2,d1

       lsl.b   #2,d1
       or.b    d0,d1           ;d1{3:2} = dtag, d1{1:0} = stag
*                               ;Tag values:
*                               ;00 = norm or denorm
*                               ;01 = zero
*                               ;10 = inf
*                               ;11 = nan
       lea     pmodt,a1
       move.l  (a1,d1.w*4),a1
       jmp     (a1)

smod_snan:
       bra     src_nan
smod_dnan:
       bra     dst_nan
smod_oper:
       bra     t_operr
smod_zro:
       move.b  ETEMP(a6),d1    ;get sign of src op
       move.b  FPTEMP(a6),d0   ;get sign of dst op
       eor.b   d0,d1           ;get exor of sign bits
       btst.l  #7,d1           ;test for sign
       beq.b   smod_zsn        ;if clr, do not set sign big
       bset.b  #q_sn_bit,FPSR_QBYTE(a6) ;set q-byte sign bit
smod_zsn:
       btst.l  #7,d0           ;test if + or -
       beq     ld_pzero        ;if pos then load +0
       bra     ld_mzero        ;else neg load -0

smod_fpn:
       move.b  ETEMP(a6),d1    ;get sign of src op
       move.b  FPTEMP(a6),d0   ;get sign of dst op
       eor.b   d0,d1           ;get exor of sign bits
       btst.l  #7,d1           ;test for sign
       beq.b   smod_fsn        ;if clr, do not set sign big
       bset.b  #q_sn_bit,FPSR_QBYTE(a6) ;set q-byte sign bit
smod_fsn:
       tst.b   DTAG(a6)        ;filter out denormal destination case
       bpl.b   smod_nrm        ;
       lea.l   FPTEMP(a6),a0   ;a0<- addr(FPTEMP)
       bra     t_resdnrm       ;force UNFL(but exact) result
smod_nrm:
       fmove.l USER_FPCR(a6),fpcr ;use user's rmode and precision
       fmove.x FPTEMP(a6),fp0  ;return dest to fp0
       rts

*
*       FREM
*
premt:
*                               ;$25 frem
*                               ;dtag,stag
       dc.l    srem            ;  00,00  norm,norm = normal
       dc.l    srem_oper       ;  00,01  norm,zero = nan with operr
       dc.l    srem_fpn        ;  00,10  norm,inf  = fpn
       dc.l    srem_snan       ;  00,11  norm,nan  = nan
       dc.l    srem_zro        ;  01,00  zero,norm = +-zero
       dc.l    srem_oper       ;  01,01  zero,zero = nan with operr
       dc.l    srem_zro        ;  01,10  zero,inf  = +-zero
       dc.l    srem_snan       ;  01,11  zero,nan  = nan
       dc.l    srem_oper       ;  10,00  inf,norm  = nan with operr
       dc.l    srem_oper       ;  10,01  inf,zero  = nan with operr
       dc.l    srem_oper       ;  10,10  inf,inf   = nan with operr
       dc.l    srem_snan       ;  10,11  inf,nan   = nan
       dc.l    srem_dnan       ;  11,00  nan,norm  = nan
       dc.l    srem_dnan       ;  11,01  nan,zero  = nan
       dc.l    srem_dnan       ;  11,10  nan,inf   = nan
       dc.l    srem_dnan       ;  11,11  nan,nan   = nan

       xdef    prem
prem:
       clr.b   FPSR_QBYTE(a6)   ;clear quotient field
       bfextu  STAG(a6){0:3},d0 ;stag = d0
       bfextu  DTAG(a6){0:3},d1 ;dtag = d1
*
* Alias extended denorms to norms for the jump table.
*
       bclr    #2,d0
       bclr    #2,d1

       lsl.b   #2,d1
       or.b    d0,d1           ;d1{3:2} = dtag, d1{1:0} = stag
*                               ;Tag values:
*                               ;00 = norm or denorm
*                               ;01 = zero
*                               ;10 = inf
*                               ;11 = nan
       lea     premt,a1
       move.l  (a1,d1.w*4),a1
       jmp     (a1)

srem_snan:
       bra     src_nan
srem_dnan:
       bra     dst_nan
srem_oper:
       bra     t_operr
srem_zro:
       move.b  ETEMP(a6),d1    ;get sign of src op
       move.b  FPTEMP(a6),d0   ;get sign of dst op
       eor.b   d0,d1           ;get exor of sign bits
       btst.l  #7,d1           ;test for sign
       beq.b   srem_zsn        ;if clr, do not set sign big
       bset.b  #q_sn_bit,FPSR_QBYTE(a6) ;set q-byte sign bit
srem_zsn:
       btst.l  #7,d0           ;test if + or -
       beq     ld_pzero        ;if pos then load +0
       bra     ld_mzero        ;else neg load -0

srem_fpn:
       move.b  ETEMP(a6),d1    ;get sign of src op
       move.b  FPTEMP(a6),d0   ;get sign of dst op
       eor.b   d0,d1           ;get exor of sign bits
       btst.l  #7,d1           ;test for sign
       beq.b   srem_fsn        ;if clr, do not set sign big
       bset.b  #q_sn_bit,FPSR_QBYTE(a6) ;set q-byte sign bit
srem_fsn:
       tst.b   DTAG(a6)        ;filter out denormal destination case
       bpl.b   srem_nrm        ;
       lea.l   FPTEMP(a6),a0   ;a0<- addr(FPTEMP)
       bra     t_resdnrm       ;force UNFL(but exact) result
srem_nrm:
       fmove.l USER_FPCR(a6),fpcr ;use user's rmode and precision
       fmove.x FPTEMP(a6),fp0  ;return dest to fp0
       rts
*
*       FSCALE
*
pscalet:
*                               ;$26 fscale
*                               ;dtag,stag
       dc.l    sscale          ;  00,00  norm,norm = result
       dc.l    sscale          ;  00,01  norm,zero = fpn
       dc.l    scl_opr         ;  00,10  norm,inf  = nan with operr
       dc.l    scl_snan        ;  00,11  norm,nan  = nan
       dc.l    scl_zro         ;  01,00  zero,norm = +-zero
       dc.l    scl_zro         ;  01,01  zero,zero = +-zero
       dc.l    scl_opr         ;  01,10  zero,inf  = nan with operr
       dc.l    scl_snan        ;  01,11  zero,nan  = nan
       dc.l    scl_inf         ;  10,00  inf,norm  = +-inf
       dc.l    scl_inf         ;  10,01  inf,zero  = +-inf
       dc.l    scl_opr         ;  10,10  inf,inf   = nan with operr
       dc.l    scl_snan        ;  10,11  inf,nan   = nan
       dc.l    scl_dnan        ;  11,00  nan,norm  = nan
       dc.l    scl_dnan        ;  11,01  nan,zero  = nan
       dc.l    scl_dnan        ;  11,10  nan,inf   = nan
       dc.l    scl_dnan        ;  11,11  nan,nan   = nan

       xdef    pscale
pscale:
       bfextu  STAG(a6){0:3},d0 ;stag in d0
       bfextu  DTAG(a6){0:3},d1 ;dtag in d1
       bclr.l  #2,d0           ;alias  denorm into norm
       bclr.l  #2,d1           ;alias  denorm into norm
       lsl.b   #2,d1
       or.b    d0,d1           ;d1{4:2} = dtag, d1{1:0} = stag
*                               ;dtag values     stag values:
*                               ;000 = norm      00 = norm
*                               ;001 = zero      01 = zero
*                               ;010 = inf       10 = inf
*                               ;011 = nan       11 = nan
*                               ;100 = dnrm
*
*
       lea.l   pscalet,a1      ;load start of jump table
       move.l  (a1,d1.w*4),a1  ;load a1 with label depending on tag
       jmp     (a1)            ;go to the routine

scl_opr:
       bra     t_operr

scl_dnan:
       bra     dst_nan

scl_zro:
       btst.b  #sign_bit,FPTEMP_EX(a6) ;test if + or -
       beq     ld_pzero                ;if pos then load +0
       bra     ld_mzero                ;if neg then load -0
scl_inf:
       btst.b  #sign_bit,FPTEMP_EX(a6) ;test if + or -
       beq     ld_pinf                 ;if pos then load +inf
       bra     ld_minf                 ;else neg load -inf
scl_snan:
       bra     src_nan
*
*       FSINCOS
*
       xdef    ssincosz
ssincosz:
       btst.b  #sign_bit,ETEMP(a6)     ;get sign
       beq.b   sincosp
       fmove.x MZERO,fp0
       bra.b   sincoscom
sincosp:
       fmove.x PZERO,fp0
sincoscom:
       fmovem.x PONE,fp1       ;do not allow FPSR to be affected
       bra     sto_cos         ;store cosine result

       xdef    ssincosi
ssincosi:
       fmove.x QNAN,fp1        ;load NAN
       bsr     sto_cos         ;store cosine result
       fmove.x QNAN,fp0        ;load NAN
       bra     t_operr

       xdef    ssincosnan
ssincosnan:
       move.l  ETEMP_EX(a6),FP_SCR1(a6)
       move.l  ETEMP_HI(a6),FP_SCR1+4(a6)
       move.l  ETEMP_LO(a6),FP_SCR1+8(a6)
       bset.b  #signan_bit,FP_SCR1+4(a6)
       fmovem.x FP_SCR1(a6),fp1
       bsr     sto_cos
       bra     src_nan
*
* This code forces default values for the zero, inf, and nan cases
* in the transcendentals code.  The CC bits must be set in the
* stacked FPSR to be correctly reported.
*
***Returns +PI/2
       xdef    ld_ppi2
ld_ppi2:
       fmove.x PPIBY2,fp0              ;load +pi/2
       bra     t_inx2                  ;set inex2 exc

***Returns -PI/2
       xdef    ld_mpi2
ld_mpi2:
       fmove.x MPIBY2,fp0              ;load -pi/2
       or.l    #neg_mask,USER_FPSR(a6) ;set N bit
       bra     t_inx2                  ;set inex2 exc

***Returns +inf
       xdef    ld_pinf
ld_pinf:
       fmove.x PINF,fp0                ;load +inf
       or.l    #inf_mask,USER_FPSR(a6) ;set I bit
       rts

***Returns -inf
       xdef    ld_minf
ld_minf:
       fmove.x MINF,fp0                ;load -inf
       or.l    #neg_mask+inf_mask,USER_FPSR(a6)        ;set N and I bits
       rts

***Returns +1
       xdef    ld_pone
ld_pone:
       fmove.x PONE,fp0                ;load +1
       rts

***Returns -1
       xdef    ld_mone
ld_mone:
       fmove.x MONE,fp0                ;load -1
       or.l    #neg_mask,USER_FPSR(a6) ;set N bit
       rts

***Returns +0
       xdef    ld_pzero
ld_pzero:
       fmove.x PZERO,fp0               ;load +0
       or.l    #z_mask,USER_FPSR(a6)   ;set Z bit
       rts

***Returns -0
       xdef    ld_mzero
ld_mzero:
       fmove.x MZERO,fp0               ;load -0
       or.l    #neg_mask+z_mask,USER_FPSR(a6)  ;set N and Z bits
       rts

       end