/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* stdlib.c: ANSI draft (X3J11 Oct 86) library, section 4.10 */
/* Copyright (C) Codemist Ltd, 1988 */
/* version 0.02a */

#include "hostsys.h"      /* for _terminateio(), and _exit() etc */
#include "kernel.h"
#include <stdlib.h>
#include <signal.h>

/* atof, atoi, atol, strtod, strtol, strtoul are implemented in scanf.c  */

/* mblen, mbtowc, wctomb, mbstowcs, wcstombs are implemented in locale.c */

/* div and ldiv are implemented in machine code */

static unsigned long int next = 1;

int _ANSI_rand(void)     /* This is the ANSI suggested portable code */
{
    next = next * 1103515245 + 12345;
    return (unsigned int) (next >> 16) % 32768;
}

void _ANSI_srand(unsigned int seed)
{
    next = seed;
}

/* Now the random-number generator that the world is expected to use */

static unsigned _random_number_seed[55] =
/* The values here are just those that would be put in this horrid
   array by a call to __srand(1). DO NOT CHANGE __srand() without
   making a corresponding change to these initial values.
*/
{   0x00000001, 0x66d78e85, 0xd5d38c09, 0x0a09d8f5, 0xbf1f87fb,
    0xcb8df767, 0xbdf70769, 0x503d1234, 0x7f4f84c8, 0x61de02a3,
    0xa7408dae, 0x7a24bde8, 0x5115a2ea, 0xbbe62e57, 0xf6d57fff,
    0x632a837a, 0x13861d77, 0xe19f2e7c, 0x695f5705, 0x87936b2e,
    0x50a19a6e, 0x728b0e94, 0xc5cc55ae, 0xb10a8ab1, 0x856f72d7,
    0xd0225c17, 0x51c4fda3, 0x89ed9861, 0xf1db829f, 0xbcfbc59d,
    0x83eec189, 0x6359b159, 0xcc505c30, 0x9cbc5ac9, 0x2fe230f9,
    0x39f65e42, 0x75157bd2, 0x40c158fb, 0x27eb9a3e, 0xc582a2d9,
    0x0569d6c2, 0xed8e30b3, 0x1083ddd2, 0x1f1da441, 0x5660e215,
    0x04f32fc5, 0xe18eef99, 0x4a593208, 0x5b7bed4c, 0x8102fc40,
    0x515341d9, 0xacff3dfa, 0x6d096cb5, 0x2bb3cc1d, 0x253d15ff
};

static int _random_j = 23, _random_k = 54;

int rand(void)
{
/* See Knuth vol 2 section 3.2.2 for a discussion of this random
   number generator.
*/
    unsigned int temp;
    temp = (_random_number_seed[_random_k] += _random_number_seed[_random_j]);
    if (--_random_j == 0) _random_j = 54, --_random_k;
    else if (--_random_k == 0) _random_k = 54;
    return (temp & 0x7fffffff);         /* result is a 31-bit value */
    /* It seems that it would not be possible, under ANSI rules, to */
    /* implement this as a 32-bit value. What a shame!              */
}

void srand(unsigned int seed)
{
/* This only allows you to put 32 bits of seed into the random sequence,
   but it is very improbable that you have any good source of randomness
   that good to start with anyway! A linear congruential generator
   started from the seed is used to expand from 32 to 32*55 bits.
*/
    int i;
    _random_j = 23;
    _random_k = 54;
    for (i = 0; i<55; i++)
    {   _random_number_seed[i] = seed + (seed >> 16);
/* This is not even a good way of setting the initial values.  For instance */
/* a better scheme would have r<n+1> = 7^4*r<n> mod (3^31-1).  Still I will */
/* leave this for now.                                                      */
        seed = 69069*seed + 1725307361;  /* computed modulo 2^32 */
    }
}

/* free, malloc, realloc etc are in the file alloc.c                     */

#define EXIT_LIMIT 33

typedef void (*vprocp)(void);
static union { vprocp p; int i; } _exitvector[EXIT_LIMIT] = { 0 };
       /* initialised so not in bss (or shared library trouble) */
static struct {
    char number_of_exit_functions;
    char alloc_finalised, io_finalised, getenv_finalised;
} exit_s;

void _exit_init(void)
{
    if (_kernel_client_is_module()) {
        /* leave SWI mode exit handlers in place. number_of_exit_functions
           is guaranteed reasonable */
        while (exit_s.number_of_exit_functions != 0)
            if (_exitvector[--exit_s.number_of_exit_functions].i & 3) {
                ++exit_s.number_of_exit_functions; break;
            }
    } else
        exit_s.number_of_exit_functions = 0;
    exit_s.alloc_finalised = 0; exit_s.io_finalised = 0; exit_s.getenv_finalised = 0;
}

int atexit(vprocp func)
{
    if (exit_s.number_of_exit_functions >= EXIT_LIMIT) return 1;    /* failure */
    _exitvector[exit_s.number_of_exit_functions++].i =
        (int) func + ((_kernel_processor_mode() & 0xF) != 0);
    return 0;                                                /* success */
}

void _lib_shutdown(void)
{
    int mode = ((_kernel_processor_mode() & 0xF) != 0);
    int ismodule = _kernel_client_is_module(); /* ie is module app, so not */
                                               /* total shutdown           */
    while (exit_s.number_of_exit_functions!=0) {
        vprocp fn = _exitvector[--exit_s.number_of_exit_functions].p;
        int flags = _exitvector[exit_s.number_of_exit_functions].i;
        if ((flags & 3) != mode) { ++exit_s.number_of_exit_functions; break; };
        /* Take extra care with fn ptr - consider Thumb */
        fn = (vprocp) ((unsigned) fn &~ 3);
        _call_client_0(fn);
    }
    /* ensure no recursion if finalisation fails */
    if (!exit_s.getenv_finalised && !ismodule)
    { exit_s.getenv_finalised = 1; _terminate_getenv(); }
    if (!exit_s.alloc_finalised)
    { exit_s.alloc_finalised = 1; _terminate_user_alloc(); }
    if (!exit_s.io_finalised)
    { exit_s.io_finalised = 1; _terminateio(); }
    if (ismodule) /* Want terminateio again for module part */
        exit_s.io_finalised = 0;
}


void exit(int n)
{
/* No longer calls _lib_shutdown: that is done as C finalisation called
   from _kernel_exit.
 */
    _exit(n);
}

void _Exit(int n)
{
/* Is this the best way of doing this? abort() probably shouldn't be calling
 * atexit functions either...
 */
    while (exit_s.number_of_exit_functions!=0) {
        int flags = _exitvector[exit_s.number_of_exit_functions].i;
        if ((flags & 3) != 0) { ++exit_s.number_of_exit_functions; break; };
    }
    _exit(n);
}

void abort(void)
{
    raise(SIGABRT);
    exit(1);
}

int (abs)(int x) { return abs(x); }

long int (labs)(long int x) { return labs(x); }

#if 0
/* Compiler generates poo code at the minute - in machine code for now */
long long int llabs(long long int x) {
    if (x<0) return (-x);
    else return x;
}
#endif


/* end of stdlib.c */