//----------------------------------------------------------------------------
///
/// \file   checks.cpp
///
/// \brief  Perform some checks to see if toolchain and libraries are ok
///
///         check if malloc is working (tend to fail on init failures)
///         check addresses
///         ...
///         The check routins stay in a while loop if some error is detected.
///         Its assumed that there is an very serious compilation/link problem
///         and the application wont run anyway.
///
/// \date   20141002
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


//---Documentation------------------------------------------------------------


//---Includes -----------------------------------------------------------------


//---General--------------------------

#include <stddef.h>
#include <stdlib.h>     // malloc, free, rand
#include <stdint.h>
#include <lepto/lepto.h>
#include <errno.h>      //

//---Own------------------------------

#include <lepto/lepto.h>   // address_t
#include <lepto/checks.h>


//---Declaration--------------------------------------------------------------


extern char end asm("end") __attribute__((unused)); // And of application flash; heap_low will follow
extern char absolute_start asm("absolute_start") __attribute__((unused));
extern char _estack asm("_estack") __attribute__((unused));
extern char heap_low asm("heap_low"); // __attribute__((unused));
extern char heap_top asm("heap_top"); // __attribute__((unused));
extern char stack_top asm("stack_top") __attribute__((unused));

extern char _sidata asm("_sidata") __attribute__((unused));
extern char _sdata asm("_sdata") __attribute__((unused));
extern char _edata asm("_edata") __attribute__((unused));
extern char _sbss asm("_sbss") __attribute__((unused));
extern char _ebss asm("_ebss") __attribute__((unused));
static const int static_data=0x200;


//---Implementation-----------------------------------------------------------


void preChecks()
{
   #if ! defined ( BOOTLOADER ) && ! defined ( NDEBUG )
      checkAreas();
      checkMalloc();
   #endif
}


void postChecks()
{
   #if ! defined ( BOOTLOADER ) && ! defined ( NDEBUG )
      checkPostMalloc();
   #endif
}


void *getEnd()
{
    return(&end);
}


void *getPtr( char *ptr)
{
    return(ptr);
}


void checkAreas()
{
    void *m_sidata=getPtr(&_sidata);
    void *m_sdata=getPtr(&_sdata);
    void *m_edata=getPtr(&_edata);
    void *m_sbss=getPtr(&_sbss);
    void *m_ebss=getPtr(&_ebss);
    void *ptr1;
    void *ptr2;

    ptr1=(void*)malloc(0x20);
    ptr2=(void*)malloc(0x20);

    if( (ptr1==ptr2)
        || (!m_sidata)
        || (!m_sdata)
        || (!m_edata)
        || (!m_sbss)
        || (!m_ebss)
    )
        while(1)
            asm("nop;");

    free(ptr1);
    free(ptr2);
}


void checkMalloc()
{
    void *ptr1;
    void *ptr2;
    void *ptr3;
    void *ptr4;
    void *a;
    void *b;
    const void *ptr5;

    static int stack_data=0x100;
    static int call_counter=0x1000;

    void *_end __attribute__ ( (unused) ) = getPtr(&end);
    void *__estack __attribute__ ( (unused) ) = getPtr(&_estack);
    void *_heap_low __attribute__ ( (unused) ) = getPtr(&heap_low);
    void *_heap_top __attribute__ ( (unused) ) = getPtr(&heap_top);
    //void *_stack_top __attribute__ ( (unused) ) = getPtr(&stack_top);

    call_counter++;
    
    ptr4=&stack_data;
    ptr5=&static_data;
    ptr1=(void*)malloc(0x20);
    ptr2=(void*)malloc(0x20);
    ptr3=(void*)getEnd();

    if( (ptr1==ptr2) )
        while(1)
            asm("nop;");

    if(ptr2< ptr1)
    {
       a=ptr2; b=ptr1;
    }
    else
    {
       a=ptr1; b=ptr2;
    }

    if( (address_t)b < (address_t)a+0x20 )
        while(1)
            asm("nop;");

    if(    a < ptr3 )
        while(1)
            asm("nop;");


#if MCU_PLATFORM_STM32
    if( ptr1<=(void *)0x20000000 )
       while(1)
           asm("nop;");
#endif

    if(    (!ptr3)
        || (!ptr4)
        || (!ptr5)
    )
        while(1)
            asm("nop;");
#if 0
    if(    (!_end)
        || (!__estack)
        || (!_heap_low)
        || (!_heap_top)
        || (!_stack_top)
        || ( stack_data!=(0x100) )
    )
    while(1)
        asm("nop;");
#endif

    free(ptr1);
    free(ptr2);

    return;
}

address_t tmpVal __attribute ( (used) );
address_t heapLowVal __attribute ( (used) );
address_t heapHighVal __attribute ( (used) );


void checkPostMalloc()
{
   char *tmp;

   #if defined( __x86_64__ ) || defined(__X86__ )
      return;
   #endif
   tmp=(char *)malloc( sizeof(char) );
   tmpVal=(address_t)tmp;
   heapLowVal=(address_t)&heap_low;
   heapHighVal=(address_t)&heap_top;

   if( tmpVal >= heapHighVal )
      while(1)
          // Heap empty / out of range
           asm("nop;");

   if((address_t)&heap_top - (address_t)tmp < 0x100 )
      while(1)
          // To less heap
          asm("nop;");

   free(tmp);

   return;
}


//---fin----------------------------------------------------------------------
