/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_PLATFORM_ASM_NOP_H
#define ECXX_PLATFORM_ASM_NOP_H


#include <eCxx/CTL/Utility.h>


namespace eCxx {


//
// asmNOP<>()
//
namespace internal {

    template <uint8_t N>
    static inline __ECXX_ATTR_ALWAYS_INLINE void asmNOP_impl();

    template <uint8_t N>
    struct AsmNOP1 {
        static inline __ECXX_ATTR_ALWAYS_INLINE void _()
        {
            asmNOP_impl<N - 1>();

            __asm__ __volatile__ (
                "nop \n\t"
            );
        }
    };

    template <uint8_t N>
    struct AsmNOP4 {
        static inline __ECXX_ATTR_ALWAYS_INLINE void _()
        {
            asmNOP_impl<N - 4>();

            __asm__ __volatile__ (
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
            );
        }
    };

    template <uint8_t N>
    struct AsmNOP8 {
        static inline __ECXX_ATTR_ALWAYS_INLINE void _()
        {
            asmNOP_impl<N - 8>();

            __asm__ __volatile__ (
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
            );
        }
    };

    template <uint8_t N>
    struct AsmNOP16 {
        static inline __ECXX_ATTR_ALWAYS_INLINE void _()
        {
            asmNOP_impl<N - 16>();

            __asm__ __volatile__ (
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
                "nop \n\t"
            );
        }
    };

    template <uint8_t N>
    static inline __ECXX_ATTR_ALWAYS_INLINE void asmNOP_impl()
    {
#if 1
        using AsmNOP_Type = typename select<
                                scase< N >= 16, AsmNOP16<N> >,
                                scase< N >=  8, AsmNOP8 <N> >,
                                scase< N >=  4, AsmNOP4 <N> >,
                                                AsmNOP1 <N>
                            >::Type;
#else

        using AsmNOP_Type = typename eCxx::conditional<
                                N >= 16,
                                AsmNOP16<N>,
                                typename eCxx::conditional<
                                    N >= 8,
                                    AsmNOP8<N>,
                                    typename eCxx::conditional<
                                        N >= 4,
                                        AsmNOP4<N>,
                                        AsmNOP1<N>
                                    >::Type
                                >::Type
                            >::Type;
#endif
        AsmNOP_Type::_();
    }

    template <>
    inline __ECXX_ATTR_ALWAYS_INLINE void asmNOP_impl<0>()
    {}

}

template <uint8_t N>
static inline __ECXX_ATTR_ALWAYS_INLINE void asmNOP()
{ internal::asmNOP_impl<N>(); }


} // namespace eCxx


#endif
