#ifndef __M4_INSTRUCTIONS_H__
#define __M4_INSTRUCTIONS_H__

#include "types.h"

// No Operation does nothing. This instruction can be used for code alignment purposes.
__attribute__( ( always_inline ) ) static __INLINE void __NOP(void)
{
  __ASM volatile ("nop");
}

// Wait For Interrupt is a hint instruction that suspends execution
// until one of a number of events occurs.
__attribute__( ( always_inline ) ) static __INLINE void __WFI(void)
{
  __ASM volatile ("wfi");
}

// Wait For Event is a hint instruction that permits the processor to enter
// a low-power state until one of a number of events occurs.
__attribute__( ( always_inline ) ) static __INLINE void __WFE(void)
{
  __ASM volatile ("wfe");
}

// Send Event is a hint instruction. It causes an event to be signaled to the CPU.
__attribute__( ( always_inline ) ) static __INLINE void __SEV(void)
{
  __ASM volatile ("sev");
}

// Instruction Synchronization Barrier flushes the pipeline in the processor,
// so that all instructions following the ISB are fetched from cache or
// memory, after the instruction has been completed.
__attribute__( ( always_inline ) ) static __INLINE void __ISB(void)
{
  __ASM volatile ("isb");
}

// This function acts as a special kind of Data Memory Barrier.
// It completes when all explicit memory accesses before this instruction complete.
__attribute__( ( always_inline ) ) static __INLINE void __DSB(void)
{
  __ASM volatile ("dsb");
}

// This function ensures the apparent order of the explicit memory operations before
// and after the instruction, without ensuring their completion.
__attribute__( ( always_inline ) ) static __INLINE void __DMB(void)
{
  __ASM volatile ("dmb");
}

// This function reverses the byte order in integer value.
__attribute__( ( always_inline ) ) static __INLINE uint32_t __REV(uint32_t value)
{
  uint32_t result;

  __ASM volatile ("rev %0, %1" : "=r" (result) : "r" (value) );
  return(result);
}

// This function reverses the byte order in two unsigned short values.
__attribute__( ( always_inline ) ) static __INLINE uint32_t __REV16(uint32_t value)
{
  uint32_t result;

  __ASM volatile ("rev16 %0, %1" : "=r" (result) : "r" (value) );
  return(result);
}

// This function reverses the byte order in a signed short value with sign extension to integer.
__attribute__( ( always_inline ) ) static __INLINE int32_t __REVSH(int32_t value)
{
  uint32_t result;

  __ASM volatile ("revsh %0, %1" : "=r" (result) : "r" (value) );
  return(result);
}

__attribute__( ( always_inline ) ) static __INLINE uint32_t __RBIT(uint32_t value)
{
  uint32_t result;

   __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) );
   return(result);
}

// This function performs a exclusive LDR command for 8 bit value.
__attribute__( ( always_inline ) ) static __INLINE uint8_t __LDREXB(volatile uint8_t *addr)
{
    uint8_t result;

   __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) );
   return(result);
}

// This function performs a exclusive LDR command for 16 bit values.
__attribute__( ( always_inline ) ) static __INLINE uint16_t __LDREXH(volatile uint16_t *addr)
{
    uint16_t result;

   __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) );
   return(result);
}

// This function performs a exclusive LDR command for 32 bit values.
__attribute__( ( always_inline ) ) static __INLINE uint32_t __LDREXW(volatile uint32_t *addr)
{
    uint32_t result;

   __ASM volatile ("ldrex %0, [%1]" : "=r" (result) : "r" (addr) );
   return(result);
}

// This function performs a exclusive STR command for 8 bit values.
__attribute__( ( always_inline ) ) static __INLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr)
{
   uint32_t result;

   __ASM volatile ("strexb %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
   return(result);
}

// This function performs a exclusive STR command for 16 bit values.

__attribute__( ( always_inline ) ) static __INLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr)
{
   uint32_t result;

   __ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
   return(result);
}

// This function performs a exclusive STR command for 32 bit values.
__attribute__( ( always_inline ) ) static __INLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr)
{
   uint32_t result;

   __ASM volatile ("strex %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) );
   return(result);
}

// This function removes the exclusive lock which is created by LDREX.
__attribute__( ( always_inline ) ) static __INLINE void __CLREX(void)
{
  __ASM volatile ("clrex");
}

// This function saturates a signed value.
#define __SSAT(ARG1,ARG2) \
({                          \
  uint32_t __RES, __ARG1 = (ARG1); \
  __ASM ("ssat %0, %1, %2" : "=r" (__RES) :  "I" (ARG2), "r" (__ARG1) ); \
  __RES; \
 })

// This function saturates an unsigned value.
#define __USAT(ARG1,ARG2) \
({                          \
  uint32_t __RES, __ARG1 = (ARG1); \
  __ASM ("usat %0, %1, %2" : "=r" (__RES) :  "I" (ARG2), "r" (__ARG1) ); \
  __RES; \
 })


// This function counts the number of leading zeros of a data value.
__attribute__( ( always_inline ) ) static __INLINE uint8_t __CLZ(uint32_t value)
{
  uint8_t result;

  __ASM volatile ("clz %0, %1" : "=r" (result) : "r" (value) );
  return(result);
}

#endif // of __M4_INSTRUCTIONS_H__
