// (C) 2020 Philip Endecott.
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENCE.txt or copy at https://www.boost.org/LICENSE_1_0.txt

#ifndef lipstick_arm_hh
#define lipstick_arm_hh

#include <cstdint>

// ARM asm features.

// The ARM C Language Extensions define some builtins that we should be 
// able to use here, but it looks like our gcc is too old to support them.
// Docs: https://static.docs.arm.com/ihi0053/d/IHI0053D_acle_2_1.pdf
// #include <arm_acle.h>


// CPSR - current processor status register.
// We access this to enable/disable interrupts and to change processor mode.
// Format: 31...............76543210
//         Flags............IFTMMMMM
// I,F = IRQ,FIQ DISable
// T = Thumb
// M = Mode
// Modes: 0x10 = User
//        0x11 = FIQ
//        0x12 = IRQ
//        0x13 = Supervisor
//        0x17 = Abort
//        0x1d = Undefined instruction
//        0x1f = System (?)
// cpsr_c below refers to the lower bits only, whereas _fc refers to everything.
// (Setting flags behind the back of the compiler is going to be problematic, 
// even when it looks like a simple read-modify-write.)

// The MRS and MSR instructions don't exist in thumb mode, so these can't be 
// inlined in a thumb function.  But they must be inlined where they are used 
// to change mode (called from start.cc) as chaning mode changes the lr, so 
// if they were functions they would return to the wrong place.  Similarly, 
// set_sp must always be inlined because a function call to it would modify 
// the sp!.

// Take care that these do not get optimised away - note "asm volatile".


__attribute__((always_inline))
inline uint32_t get_cpsr()
{
  uint32_t v;
  asm volatile ( "mrs %[out], cpsr" : [out] "=r" (v) );
  return v;

// ACLE version:  return __arm_rsr("cpsr");
}

__attribute__((always_inline))
inline void set_cpsr_c(uint32_t v)
{
  asm( "msr cpsr_c,%[in]" : : [in] "r" (v) );

// ACLE: _arm_wsr("cpsr_c", v);
}

// En/disable interrupts must be ARM not thumb and must inline the 
// cpsr functions; they can then be inlined into an ARM caller or 
// be called out-of-line from a Thumb caller.

__attribute__((target("arm")))
inline void enable_irq()
{
  // Clear bit 7 of the CPSR to enable IRQ.
  uint32_t cpsr = get_cpsr();
  cpsr &=~ (1 << 7);
  set_cpsr_c(cpsr);
}

__attribute__((target("arm")))
inline void disable_irq()
{
  // Set bit 7 of the CPSR to disable IRQ.
  uint32_t cpsr = get_cpsr();
  cpsr |= (1 << 7);
  set_cpsr_c(cpsr);
}

__attribute__((always_inline))
inline void set_sp(uint32_t v)
{
  asm( "mov sp,%[in]" : : [in] "r" (v) );
}


#endif
