//----------------------------------------------------
/**
	\file		cortex-m4/inc/packed.h
	\ingroup	other
	\ingroup	cortex-m4

	\brief		Funkce pro praci s __attribute__((packed)) strukturami
				<<< CORTEX M4 >>>

	\author		ConTeK s.r.o., Pavel Bazika
	\version	01.01
	\date		18.01.2007
*/
//----------------------------------------------------

#ifndef _PACKED_H_
#define _PACKED_H_

#include <cortex-m4/inc/ctktypes.h>
#include <cortex-m4/inc/hwmodel.h>
#include <nvmem.h>

//----------------------------------------------------
/**
	\brief	Funkce uklada osmibitovou hodnotu value na adresu addr

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_uint8(void * const _addr, const uint8_t _value)
{
	uint8_t * ptr = (uint8_t *)_addr;
	*ptr = _value;
}


//----------------------------------------------------
/**
	\brief	Funkce cte osmibitovou hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint8_t pckLdr_uint8(const void * const _addr)
{
	return *(const uint8_t *)_addr;
}


//----------------------------------------------------
/**
	\brief	Funkce cte osmibitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint8_t pckLdr_uint8_pconst(const void * const _addr)
{
	return pckLdr_uint8(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada sestnactibitovou hodnotu value
			na adresu addr, coz je adresa nejnizsiho bytu

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_uint16(void * const _addr, const uint16_t _value)
{
	uint8_t * ptr = (uint8_t *)_addr;

	*ptr = (uint8_t)(_value & 0xFF);				// zapis nizsiho bytu
	*(ptr + 1) = (uint8_t)((_value >> 8) & 0xFF);	//zapis vyssiho bytu
}


//----------------------------------------------------
/**
	\brief	Funkce cte sestnactibitovou hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint16_t pckLdr_uint16(const void * const _addr)
{
	uint16_t result;
	const uint8_t * ptr = (const uint8_t *)_addr;

	result = *ptr;
	result |= ((uint16_t)*(ptr + 1)) << 8;

	return result;
}


//----------------------------------------------------
/**
	\brief	Funkce cte sestnactibitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint16_t pckLdr_uint16_pconst(const void * const _addr)
{
	return pckLdr_uint16(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada sestnactibitovou hodnotu value
			na adresu addr, coz je adresa promene big-endian

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStrBE_uint16(void * const _addr, const uint16_t _value)
{
	uint8_t * ptr = (uint8_t *)_addr;

	*ptr = (uint8_t)((_value >> 8) & 0xFF);		// zapis nizsiho bytu
	*(ptr + 1) = (uint8_t)(_value & 0xFF);		// zapis vyssiho bytu
}


//----------------------------------------------------
/**
	\brief	Funkce cte sestnactibitovou hodnotu z adresy addr

	\param	addr	Adresa promene big-endian, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint16_t pckLdrBE_uint16(const void * const _addr)
{
	uint16_t result;
	const uint8_t * ptr = (const uint8_t *)_addr;

	result = *(ptr + 1);
	result |= ((uint16_t)*ptr) << 8;

	return result;
}


//----------------------------------------------------
/**
	\brief	Funkce cte sestnactibitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa promene big-endian, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint16_t pckLdrBE_uint16_pconst(const void * const _addr)
{
	return pckLdrBE_uint16(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada tricetidvoubitovou hodnotu value na adresu addr, coz je adresa nejnizsiho bytu

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_uint32(void * const _addr, uint32_t _value)
{
	uint8_t * ptr = (uint8_t *)_addr;

	for (int i = 0; i < 4; i++)
	{
		*ptr++ = _value & 0xFF;
		_value >>= 8;
	}
}


//----------------------------------------------------
/**
	\brief	Funkce cte tricetidvoubitovou hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint32_t pckLdr_uint32(const void * const _addr)
{
	uint32_t result = 0;
	const uint8_t * ptr = (const uint8_t *)_addr;
	ptr += 3;

	for (int i = 0; i < 4; i++)
	{
		result <<= 8;
		result |= *ptr--;
	}

	return result;
}


//----------------------------------------------------
/**
	\brief	Funkce cte tricetidvoubitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint32_t pckLdr_uint32_pconst(const void * const _addr)
{
	return pckLdr_uint32(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada sedesatictyrbitovou hodnotu value na adresu addr, coz je adresa nejnizsiho bytu

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_uint64(void * const _addr, uint64_t _value)
{
	uint8_t * ptr = (uint8_t *)_addr;

	for (int i = 0; i < 8; i++)
	{
		*ptr++ = _value & 0xFF;
		_value >>= 8;
	}
}


//----------------------------------------------------
/**
	\brief	Funkce cte sedesatictyrbitovou hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint64_t pckLdr_uint64(const void * const _addr)
{
	uint64_t result = 0;
	const uint8_t * ptr = (const uint8_t *)_addr;
	ptr += 7;

	for (int i = 0; i < 8; i++)
	{
		result <<= 8;
		result |= *ptr--;
	}

	return result;
}


//----------------------------------------------------
/**
	\brief	Funkce cte sedesatictyrbitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline uint64_t pckLdr_uint64_pconst(const void * const _addr)
{
	return pckLdr_uint64(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada osmibitovou hodnotu value na adresu addr

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_int8(void * const _addr, const int8_t _value)
{
	pckStr_uint8(_addr, (uint8_t)_value);
}


//----------------------------------------------------
/**
	\brief	Funkce cte osmibitovou hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline int8_t pckLdr_int8(const void * const _addr)
{
	return (int8_t)pckLdr_uint8(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce cte osmibitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline int8_t pckLdr_int8_pconst(const pvoidptr_t _addr)
{
	return pckLdr_int8(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada sestnactibitovou hodnotu value na adresu addr, coz je adresa nejnizsiho bytu

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_int16(void * const _addr, const int16_t _value)
{
	pckStr_uint16(_addr, (uint16_t)_value);
}


//----------------------------------------------------
/**
	\brief	Funkce cte sestnactibitovou hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline int16_t pckLdr_int16(const void * const _addr)
{
	return (int16_t)pckLdr_uint16(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce cte sestnactibitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline int16_t pckLdr_int16_pconst(const pvoidptr_t _addr)
{
	return pckLdr_int16(_addr);
}


//----------------------------------------------------
/*
	\brief	Funkce uklada tricetidvoubitovou hodnotu value na adresu addr, coz je adresa nejnizsiho bytu

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_int32(void * const _addr, const int32_t _value)
{
	pckStr_uint32(_addr, (uint32_t)_value);
}


//----------------------------------------------------
/**
	\brief	Funkce cte tricetidvoubitovou hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline int32_t pckLdr_int32(const void * const _addr)
{
	return (int32_t)pckLdr_uint32(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce cte tricetidvoubitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline int32_t pckLdr_int32_pconst(const pvoidptr_t _addr)
{
	return pckLdr_int32(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada sedesatictyrbitovou hodnotu value na adresu addr, coz je adresa nejnizsiho bytu

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_int64(void * const _addr, const int64_t _value)
{
	pckStr_uint64(_addr, (uint64_t)_value);
}


//----------------------------------------------------
/**
	\brief	Funkce cte sedesatictyrbitovou hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline int64_t pckLdr_int64(const void * const _addr)
{
	return (int64_t)pckLdr_uint64(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce cte sedesatictyrbitovou hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline int64_t pckLdr_int64_pconst(const pvoidptr_t _addr)
{
	return pckLdr_int64(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada float hodnotu value na adresu addr, coz je adresa nejnizsiho bytu

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_float(void * const _addr, const float_t _value)
{
	union { float_t flt; uint32_t uint; } conv;
	conv.flt = _value;
	pckStr_uint32(_addr, conv.uint);
}


//----------------------------------------------------
/**
	\brief	Funkce cte float hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline float_t pckLdr_float(const void * const _addr)
{
	union { float_t flt; uint32_t uint; } conv;
	conv.uint = pckLdr_uint32(_addr);
	return conv.flt;
}


//----------------------------------------------------
/**
	\brief	Funkce cte float hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline float_t pckLdr_float_pconst(const pvoidptr_t _addr)
{
	return pckLdr_float(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce uklada nvptr hodnotu value na adresu addr, coz je adresa nejnizsiho bytu

	\param	addr	Adresa, kam se ma hodnota ulozit
	\param	value	Ukladana hodnota
*/
//----------------------------------------------------

static inline void pckStr_nvptr(void * const _addr, const nvptr_t _value)
{
	switch (sizeof(nvptr_t))
	{
		case 1:
			pckStr_uint8(_addr, (nvptr_t)_value);
			break;

		case 2:
			pckStr_uint16(_addr, (nvptr_t)_value);
			break;

		case 4:
			pckStr_uint32(_addr, (nvptr_t)_value);
			break;

		case 8:
			pckStr_uint64(_addr, (nvptr_t)_value);
			break;

		default:
			assert_C(FALSE);
	}
}


//----------------------------------------------------
/**
	\brief	Funkce cte nvptr hodnotu z adresy addr

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline nvptr_t pckLdr_nvptr(const void * const _addr)
{
	switch (sizeof(nvptr_t))
	{
		case 1:
			return (nvptr_t)pckLdr_uint8(_addr);

		case 2:
			return (nvptr_t)pckLdr_uint16(_addr);

		case 4:
			return (nvptr_t)pckLdr_uint32(_addr);

		case 8:
			return (nvptr_t)pckLdr_uint64(_addr);

		default:
			assert_C(FALSE);
	}
}


//----------------------------------------------------
/**
	\brief	Funkce cte nvptr hodnotu z adresy addr v programove pameti

	\param	addr	Adresa, ze ktere se ma hodnota nacist

	\return		Nactena hodnota
*/
//----------------------------------------------------

static inline nvptr_t pckLdr_nvptr_pconst(const pvoidptr_t _addr)
{
	return pckLdr_nvptr(_addr);
}


//----------------------------------------------------
/**
	\brief	Funkce provadi kopirovani nezarovnanych dat

	\param	dest	Cilova adresa kopirovani
	\param	src		Zdrojova adresa kopirovani
	\param	size	Velikost kopirovane oblasti v bytech

	\return		Adresa _dest
*/
//----------------------------------------------------

static inline void * pck_memcpy(void * const _dest, const void * const _src, size_t _size)
{
	uint8_t *dest = (uint8_t *)_dest;
	const uint8_t *src = (const uint8_t *)_src;

	while (_size--)
	{
		*dest++ = *src++;
	}

	return _dest;
}


//----------------------------------------------------
/**
	\brief	Funkce provadi kopirovani nezarovnanych dat z programove pameti

	\param	dest	Cilova adresa kopirovani
	\param	src		Zdrojova adresa kopirovani (v programove pameti)
	\param	size	Velikost kopirovane oblasti v bytech

	\return		Adresa _dest
*/
//----------------------------------------------------

static inline void * pck_memcpy_pconst(void * const _dest, const void * const _src, const size_t _size)
{
	// Na ARM je programovatelna pamet adresovatelna
	return pck_memcpy(_dest, _src, _size);
}


//----------------------------------------------------
/**
	\brief	Funkce provadi bezpecne nastaveni oblasti pameti na urcitou hodnotu

	\param	dest	Cilova adresa
	\param	val		Hodnota
	\param	size	Velikost
*/
//----------------------------------------------------

static inline void pck_memset(void * const _dest, const uint8_t _val, size_t _size)
{
	uint8_t *dest = (uint8_t *)_dest;

	while (_size--)
	{
		pckStr_uint8(dest++, _val);
	}
}


//----------------------------------------------------
/**
	\brief	Funkce pro Inicializaci
*/
//----------------------------------------------------

static inline void PackedInitialize(void)
{}


//----------------------------------------------------
/**
	\brief	Funkce pro ukonceni
*/
//----------------------------------------------------

static inline void PackedFinalize(void)
{}

#endif

