/*-------------------------------------------------------------------------
 
   expf.c - Computes e**x of a 32-bit float as outlined in [1]
 
 
 
   Copyright (C) 2001, 2002, Jesus Calvino-Fraga, jesusc@ieee.org
 
 
 
   This library is free software; you can redistribute it and/or modify it
 
   under the terms of the GNU General Public License as published by the
 
   Free Software Foundation; either version 2, or (at your option) any
 
   later version.
 
 
 
   This library is distributed in the hope that it will be useful,
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
   GNU General Public License for more details.
 
 
 
   You should have received a copy of the GNU General Public License
 
   along with this library; see the file COPYING. If not, write to the
 
   Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
 
   MA 02110-1301, USA.
 
 
 
   As a special exception, if you link this library with other files,
 
   some of which are compiled with SDCC, to produce an executable,
 
   this library does not by itself cause the resulting executable to
 
   be covered by the GNU General Public License. This exception does
 
   not however invalidate any other reasons why the executable file
 
   might be covered by the GNU General Public License.
 
-------------------------------------------------------------------------*/
 
 
 
/* [1] William James Cody and W.  M.  Waite.  _Software manual for the
 
   elementary functions_, Englewood Cliffs, N.J.:Prentice-Hall, 1980. */
 
 
 
/* Version 1.0 - Initial release */
 
 
 
#define __SDCC_MATH_LIB
 
#include <math.h>
 
#include <errno.h>
 
#include <stdbool.h>
 
 
 
#ifdef MATH_ASM_MCS51
 
 
 
#define __SDCC_FLOAT_LIB
 
#include <float.h>
 
 
 
// TODO: share with other temps
 
static __bit sign_bit;
 
static __data unsigned char expf_y[4];
 
static __data unsigned char n;
 
 
 
float expf(float x)
 
{
 
        x;
 
        __asm
 
        mov     c, acc.7
 
        mov     _sign_bit, c    // remember sign
 
        clr     acc.7           // and make input positive
 
        mov     r0, a
 
        mov     c, b.7
 
        rlc     a               // extract exponent
 
        add     a, #153
 
        jc      expf_not_zero
 
        // input is a very small number, so e^x is 1.000000
 
        mov     dptr, #0
 
        mov     b, #0x80
 
        mov     a, #0x3F
 
        ljmp    expf_exit
 
expf_not_zero:
 
        // TODO: check exponent for very small values, and return zero
 
        mov     _n, #0
 
        mov     a, dpl
 
        add     a, #0xE8        // is x >= 0.69314718
 
        mov     a, dph
 
        addc    a, #0x8D
 
        mov     a, b
 
        addc    a, #0xCE
 
        mov     a, r0
 
        addc    a, #0xC0
 
        mov     a, r0
 
        jnc     expf_no_range_reduction
 
expf_range_reduction:
 
        mov     (_expf_y + 0), dpl      // keep copy of x in "exp_y"
 
        mov     (_expf_y + 1), dph
 
        mov     (_expf_y + 2), b
 
        mov     (_expf_y + 3), a
 
        mov     r0, #0x3B
 
        push    ar0
 
        mov     r0, #0xAA
 
        push    ar0
 
        mov     r0, #0xB8
 
        push    ar0
 
        mov     r0, #0x3F
 
        push    ar0
 
        lcall   ___fsmul        // x * 1.442695041 = x / ln(2)
 
        dec     sp
 
        dec     sp
 
        dec     sp
 
        dec     sp
 
        lcall   ___fs2uchar     // n = int(x * 1.442695041)
 
        mov     a, dpl
 
        mov     _n, a
 
        add     a, #128
 
        jnc     expf_range_ok
 
        lcall   fs_return_inf   // exponent overflow
 
        ljmp    expf_exit
 
expf_range_ok:
 
        mov     r0,#0x00
 
        mov     r1,#0x80
 
        mov     r2,#0x31
 
        mov     r3,#0xBF
 
        lcall   expf_scale_and_add
 
        mov     (_expf_y + 0), dpl
 
        mov     (_expf_y + 1), dph
 
        mov     (_expf_y + 2), b
 
        mov     (_expf_y + 3), a
 
        mov     r0,#0x83
 
        mov     r1,#0x80
 
        mov     r2,#0x5E
 
        mov     r3,#0x39
 
        lcall   expf_scale_and_add
 
expf_no_range_reduction:
 
 
 
 
 
// Compute e^x using the cordic algorithm.  This works over an
 
// input range of 0 to 0.69314712.  Can be extended to work from
 
// 0 to 1.0 if the results are normalized, but over the range
 
// we use, the result is always from 1.0 to 1.99999988 (fixed
 
// exponent of 127)
 
 
 
expf_cordic_begin:
 
        mov     c, b.7
 
        rlc     a               // extract exponent to acc
 
        setb    b.7
 
        mov     r1, dpl         // mantissa to r4/r3/r2/r1
 
        mov     r2, dph
 
        mov     r3, b
 
        mov     r4, #0
 
 
 
        // first, align the input into a 32 bit long
 
        cjne    a, #121, exp_lshift
 
        sjmp    exp_noshift
 
exp_lshift:
 
        jc      exp_rshift
 
        // exp_a is greater than 121, so left shift
 
        add     a, #135
 
        lcall   fs_lshift_a
 
        sjmp    exp_noshift
 
exp_rshift:
 
        // exp_a is less than 121, so right shift
 
        cpl     a
 
        add     a, #122
 
        lcall   fs_rshift_a
 
exp_noshift:                            // r4/r3/r2/r1 = x
 
        clr     a
 
        mov     (_expf_y + 0), a        // y = 1.0;
 
        mov     (_expf_y + 1), a
 
        mov     (_expf_y + 2), a
 
        mov     (_expf_y + 3), #0x20
 
        mov     dptr, #__fs_natural_log_table
 
        mov     r0, a                   // r0 = i
 
exp_cordic_loop:
 
        clr     a
 
        movc    a, @a+dptr
 
        mov     b, a
 
        inc     dptr
 
        clr     a
 
        movc    a, @a+dptr              // r7/r6/r5/b = table[i]
 
        mov     r5, a
 
        inc     dptr
 
        clr     a
 
        movc    a, @a+dptr
 
        mov     r6, a
 
        inc     dptr
 
        clr     a
 
        movc    a, @a+dptr
 
        mov     r7, a
 
        inc     dptr
 
        clr     c
 
        mov     a, r1
 
        subb    a, b                    // compare x to table[i]
 
        mov     a, r2
 
        subb    a, r5
 
        mov     a, r3
 
        subb    a, r6
 
        mov     a, r4
 
        subb    a, r7
 
        jc      exp_cordic_skip         // if table[i] < x
 
        clr     c
 
        mov     a, r1
 
        subb    a, b
 
        mov     r1, a                   // x -= table[i]
 
        mov     a, r2
 
        subb    a, r5
 
        mov     r2, a
 
        mov     a, r3
 
        subb    a, r6
 
        mov     r3, a
 
        mov     a, r4
 
        subb    a, r7
 
        mov     r4, a
 
        mov     b,  (_expf_y + 0)
 
        mov     r5, (_expf_y + 1)       // r7/r6/r5/b = y >> i
 
        mov     r6, (_expf_y + 2)
 
        mov     r7, (_expf_y + 3)
 
        mov     a, r0
 
        lcall   __fs_cordic_rshift_r765_unsigned
 
        mov     a, (_expf_y + 0)
 
        add     a, b
 
        mov     (_expf_y + 0), a
 
        mov     a, (_expf_y + 1)
 
        addc    a, r5
 
        mov     (_expf_y + 1), a        // y += (y >> i)
 
        mov     a, (_expf_y + 2)
 
        addc    a, r6
 
        mov     (_expf_y + 2), a
 
        mov     a, (_expf_y + 3)
 
        addc    a, r7
 
        mov     (_expf_y + 3), a
 
exp_cordic_skip:
 
        inc     r0
 
        cjne    r0, #27, exp_cordic_loop
 
        mov     r4, (_expf_y + 3)
 
        mov     r3, (_expf_y + 2)
 
        mov     r2, (_expf_y + 1)
 
        mov     r1, (_expf_y + 0)
 
        mov     exp_a, #121
 
        lcall   fs_normalize_a          // end of cordic
 
 
 
        mov     a, #127
 
        add     a, _n                   // ldexpf(x, n)
 
        mov     exp_a, a
 
        lcall   fs_round_and_return
 
 
 
        jnb     _sign_bit, expf_done
 
        push    dpl
 
        push    dph
 
        push    b
 
        push    acc
 
        mov     dptr, #0
 
        mov     b, #0x80
 
        mov     a, #0x3F
 
        lcall   ___fsdiv                // 1.0 / x
 
        dec     sp
 
        dec     sp
 
        dec     sp
 
        dec     sp
 
expf_done:
 
        clr     acc.7           // Result is always positive!
 
expf_exit:
 
        __endasm;
 
#pragma less_pedantic
 
}
 
 
 
static void dummy1(void) __naked
 
{
 
        __asm
 
        .globl  fs_lshift_a
 
expf_scale_and_add:
 
        push    ar0
 
        push    ar1
 
        push    ar2
 
        push    ar3
 
        mov     dpl, _n
 
        lcall   ___uchar2fs     // turn n into float
 
        lcall   ___fsmul        // n * scale_factor
 
        dec     sp
 
        dec     sp
 
        dec     sp
 
        dec     sp
 
        push    dpl
 
        push    dph
 
        push    b
 
        push    acc
 
        mov     dpl, (_expf_y + 0)
 
        mov     dph, (_expf_y + 1)
 
        mov     b,   (_expf_y + 2)
 
        mov     a,   (_expf_y + 3)
 
        lcall   ___fsadd        // x += (n * scale_factor)
 
        dec     sp
 
        dec     sp
 
        dec     sp
 
        dec     sp
 
        ret
 
        __endasm;
 
}
 
 
 
static void dummy(void) __naked
 
{
 
        __asm
 
        .globl  fs_lshift_a
 
fs_lshift_a:
 
        jz      fs_lshift_done
 
        push    ar0
 
        mov     r0, a
 
fs_lshift_loop:
 
        clr     c
 
        mov     a, r1
 
        rlc     a
 
        mov     r1, a
 
        mov     a, r2
 
        rlc     a
 
        mov     r2, a
 
        mov     a, r3
 
        rlc     a
 
        mov     r3, a
 
        mov     a, r4
 
        rlc     a
 
        mov     r4, a
 
        djnz    r0, fs_lshift_loop
 
        pop     ar0
 
fs_lshift_done:
 
        ret
 
        __endasm;
 
}
 
 
 
#else // not MATH_ASM_MCS51
 
 
 
#define P0      0.2499999995E+0
 
#define P1      0.4160288626E-2
 
#define Q0      0.5000000000E+0
 
#define Q1      0.4998717877E-1
 
 
 
#define P(z) ((P1*z)+P0)
 
#define Q(z) ((Q1*z)+Q0)
 
 
 
#define C1       0.693359375
 
#define C2      -2.1219444005469058277e-4
 
 
 
#define BIGX    88.72283911  /* ln(HUGE_VALF) */
 
#define EXPEPS  1.0E-7       /* exp(1.0E-7)=0.0000001 */
 
#define K1      1.4426950409 /* 1/ln(2) */
 
 
 
float expf(float x) _FLOAT_FUNC_REENTRANT
 
{
 
    int n;
 
    float xn, g, r, z, y;
 
    bool sign;
 
 
 
    if(x>=0.0)
 
        { y=x;  sign=0; }
 
    else
 
        { y=-x; sign=1; }
 
 
 
    if(y<EXPEPS) return 1.0;
 
 
 
    if(y>BIGX)
 
    {
 
        if(sign)
 
        {
 
            errno=ERANGE;
 
            return HUGE_VALF
 
            ;
 
        }
 
        else
 
        {
 
            return 0.0;
 
        }
 
    }
 
 
 
    z=y*K1;
 
    n=z;
 
 
 
    if(n<0) --n;
 
    if(z-n>=0.5) ++n;
 
    xn=n;
 
    g=((y-xn*C1))-xn*C2;
 
    z=g*g;
 
    r=P(z)*g;
 
    r=0.5+(r/(Q(z)-r));
 
 
 
    n++;
 
    z=ldexpf(r, n);
 
    if(sign)
 
        return 1.0/z;
 
    else
 
        return z;
 
}
 
 
 
#endif