/*
    Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC)

    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.

    Original Author: Shay Gal-on
*/

#include "coremark.h"
/*  Function: get_seed
    Get a values that cannot be determined at compile time.

    Since different embedded systems and compilers are used, 3 different methods are provided:
    1 - Using a volatile variable. This method is only valid if the compiler is forced to generate code that
    reads the value of a volatile variable from memory at run time.
    Please note, if using this method, you would need to modify core_portme.c to generate training profile.
    2 - Command line arguments. This is the preferred method if command line arguments are supported.
    3 - System function. If none of the first 2 methods is available on the platform,
    a system function which is not a stub can be used.

    e.g. read the value on GPIO pins connected to switches, or invoke special simulator functions.
*/
#if (SEED_METHOD==SEED_VOLATILE)
extern volatile ee_s32 seed1_volatile;
extern volatile ee_s32 seed2_volatile;
extern volatile ee_s32 seed3_volatile;
extern volatile ee_s32 seed4_volatile;
extern volatile ee_s32 seed5_volatile;
ee_s32 get_seed_32(int i)
{
    ee_s32 retval;

    switch (i)
    {
    case 1:
        retval=seed1_volatile;
        break;

    case 2:
        retval=seed2_volatile;
        break;

    case 3:
        retval=seed3_volatile;
        break;

    case 4:
        retval=seed4_volatile;
        break;

    case 5:
        retval=seed5_volatile;
        break;

    default:
        retval=0;
        break;
    }

    return retval;
}
#elif (SEED_METHOD==SEED_ARG)
ee_s32 parseval(char* valstring)
{
    ee_s32 retval=0;
    ee_s32 neg=1;
    int hexmode=0;

    if (*valstring == '-')
    {
        neg=-1;
        valstring++;
    }

    if ((valstring[0] == '0') && (valstring[1] == 'x'))
    {
        hexmode=1;
        valstring+=2;
    }

    /* first look for digits */
    if (hexmode)
    {
        while (((*valstring >= '0') && (*valstring <= '9')) || ((*valstring >= 'a') && (*valstring <= 'f')))
        {
            ee_s32 digit=*valstring-'0';

            if (digit>9)
                digit=10+*valstring-'a';

            retval*=16;
            retval+=digit;
            valstring++;
        }
    }
    else
    {
        while ((*valstring >= '0') && (*valstring <= '9'))
        {
            ee_s32 digit=*valstring-'0';
            retval*=10;
            retval+=digit;
            valstring++;
        }
    }

    /* now add qualifiers */
    if (*valstring=='K')
        retval*=1024;

    if (*valstring=='M')
        retval*=1024*1024;

    retval*=neg;
    return retval;
}

ee_s32 get_seed_args(int i, int argc, char* argv[])
{
    if (argc>i)
        return parseval(argv[i]);

    return 0;
}

#elif (SEED_METHOD==SEED_FUNC)
/* If using OS based function, you must define and implement the functions below in core_portme.h and core_portme.c ! */
ee_s32 get_seed_32(int i)
{
    ee_s32 retval;

    switch (i)
    {
    case 1:
        retval=portme_sys1();
        break;

    case 2:
        retval=portme_sys2();
        break;

    case 3:
        retval=portme_sys3();
        break;

    case 4:
        retval=portme_sys4();
        break;

    case 5:
        retval=portme_sys5();
        break;

    default:
        retval=0;
        break;
    }

    return retval;
}
#endif

/*  Function: crc*
    Service functions to calculate 16b CRC code.

*/



ee_u16 crcu8_1(ee_u8 data, ee_u16 crc )
{
    ee_u8 i=0,x16=0,carry=0;

    for (i = 0; i < 8; i++)
    {
        x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));
        data >>= 1;

        if (x16 == 1)
        {
            crc ^= 0x4002;
            carry = 1;
        }
        else
            carry = 0;

        crc >>= 1;

        if (carry)
            crc |= 0x8000;
        else
            crc &= 0x7fff;
    }

    return crc;
}

ee_u16 crcu8_2(ee_u8 data, ee_u16 crc )
{
    ee_u8 i=0,x16=0,carry=0;

    for (i = 0; i < 8; i++)
    {
        x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));
        data >>= 1;

        if (x16 == 1)
        {
            crc ^= 0x4002;
            carry = 1;
        }
        else
            carry = 0;

        crc >>= 1;

        if (carry)
            crc |= 0x8000;
        else
            crc &= 0x7fff;
    }

    return crc;
}

ee_u16 crcu8_3(ee_u8 data, ee_u16 crc )
{
    ee_u8 i=0,x16=0,carry=0;

    for (i = 0; i < 8; i++)
    {
        x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));
        data >>= 1;

        if (x16 == 1)
        {
            crc ^= 0x4002;
            carry = 1;
        }
        else
            carry = 0;

        crc >>= 1;

        if (carry)
            crc |= 0x8000;
        else
            crc &= 0x7fff;
    }

    return crc;
}

ee_u16 crcu8_4(ee_u8 data, ee_u16 crc )
{
    ee_u8 i=0,x16=0,carry=0;

    for (i = 0; i < 8; i++)
    {
        x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));
        data >>= 1;

        if (x16 == 1)
        {
            crc ^= 0x4002;
            carry = 1;
        }
        else
            carry = 0;

        crc >>= 1;

        if (carry)
            crc |= 0x8000;
        else
            crc &= 0x7fff;
    }

    return crc;
}

ee_u16 crcu8_5(ee_u8 data, ee_u16 crc )
{
    ee_u8 i=0,x16=0,carry=0;

    for (i = 0; i < 8; i++)
    {
        x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));
        data >>= 1;

        if (x16 == 1)
        {
            crc ^= 0x4002;
            carry = 1;
        }
        else
            carry = 0;

        crc >>= 1;

        if (carry)
            crc |= 0x8000;
        else
            crc &= 0x7fff;
    }

    return crc;
}

ee_u16 crcu8_6(ee_u8 data, ee_u16 crc )
{
    ee_u8 i=0,x16=0,carry=0;

    for (i = 0; i < 8; i++)
    {
        x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));
        data >>= 1;

        if (x16 == 1)
        {
            crc ^= 0x4002;
            carry = 1;
        }
        else
            carry = 0;

        crc >>= 1;

        if (carry)
            crc |= 0x8000;
        else
            crc &= 0x7fff;
    }

    return crc;
}

ee_u16 crcu8_7(ee_u8 data, ee_u16 crc )
{
    ee_u8 i=0,x16=0,carry=0;

    for (i = 0; i < 8; i++)
    {
        x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));
        data >>= 1;

        if (x16 == 1)
        {
            crc ^= 0x4002;
            carry = 1;
        }
        else
            carry = 0;

        crc >>= 1;

        if (carry)
            crc |= 0x8000;
        else
            crc &= 0x7fff;
    }

    return crc;
}

ee_u16 crcu8_0(ee_u8 data, ee_u16 crc )
{
    ee_u8 i=0,x16=0,carry=0;

    for (i = 0; i < 8; i++)
    {
        x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));
        data >>= 1;

        if (x16 == 1)
        {
            crc ^= 0x4002;
            carry = 1;
        }
        else
            carry = 0;

        crc >>= 1;

        if (carry)
            crc |= 0x8000;
        else
            crc &= 0x7fff;
    }

    return crc;
}

extern uint8 g_crc_cnt;
extern uint8 g_crc_entry[];
ee_u16 crcu8(ee_u8 data, ee_u16 crc )
{
    g_crc_cnt++;

    if(g_crc_entry[0x07&g_crc_cnt]==0)
    {
        return (crcu8_0( data,  crc ));
    }
    else if(g_crc_entry[0x07&g_crc_cnt]==1)
    {
        return (crcu8_1( data,  crc ));
    }
    else if(g_crc_entry[0x07&g_crc_cnt]==2)
    {
        return (crcu8_2( data,  crc ));
    }
    else if(g_crc_entry[0x07&g_crc_cnt]==3)
    {
        return (crcu8_3( data,  crc ));
    }
    else if(g_crc_entry[0x07&g_crc_cnt]==4)
    {
        return (crcu8_4( data,  crc ));
    }
    else if(g_crc_entry[0x07&g_crc_cnt]==5)
    {
        return (crcu8_5( data,  crc ));
    }
    else if(g_crc_entry[0x07&g_crc_cnt]==6)
    {
        return (crcu8_6( data,  crc ));
    }
    else/* if(g_crc_entry[0x07&g_crc_cnt]==7)*/
    {
        return (crcu8_7( data,  crc ));
    }
}

ee_u16 crcu16(ee_u16 newval, ee_u16 crc)
{
    crc=crcu8( (ee_u8) (newval),crc);
    crc=crcu8( (ee_u8) ((newval)>>8),crc);
    return crc;
}
ee_u16 crcu32(ee_u32 newval, ee_u16 crc)
{
    crc=crc16((ee_s16) newval,crc);
    crc=crc16((ee_s16) (newval>>16),crc);
    return crc;
}
ee_u16 crc16(ee_s16 newval, ee_u16 crc)
{
    return crcu16((ee_u16)newval, crc);
}

ee_u8 check_data_types()
{
    ee_u8 retval=0;

    if (sizeof(ee_u8) != 1)
    {
        ee_printf("ERROR: ee_u8 is not an 8b datatype!\n");
        retval++;
    }

    if (sizeof(ee_u16) != 2)
    {
        ee_printf("ERROR: ee_u16 is not a 16b datatype!\n");
        retval++;
    }

    if (sizeof(ee_s16) != 2)
    {
        ee_printf("ERROR: ee_s16 is not a 16b datatype!\n");
        retval++;
    }

    if (sizeof(ee_s32) != 4)
    {
        ee_printf("ERROR: ee_s32 is not a 32b datatype!\n");
        retval++;
    }

    if (sizeof(ee_u32) != 4)
    {
        ee_printf("ERROR: ee_u32 is not a 32b datatype!\n");
        retval++;
    }

    if (sizeof(ee_ptr_int) != sizeof(int*))
    {
        ee_printf("ERROR: ee_ptr_int is not a datatype that holds an int pointer!\n");
        retval++;
    }

    if (retval>0)
    {
        ee_printf("ERROR: Please modify the datatypes in core_portme.h!\n");
    }

    return retval;
}
