Login

Subversion Repositories NedoOS

Rev

Blame | Last modification | View Log | Download | RSS feed

/*-------------------------------------------------------------------------
   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