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


#ifndef INCLUDE_ECXX_PLATFORM_SPECIFIC_AVR_COMMON_CONFIG_H
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_PLATFORM_SPECIFIC_AVR_COMMON_CONFIG_H


#ifndef ECXX_PLATFORM_AVR_COMMON_CONFIG_H
#define ECXX_PLATFORM_AVR_COMMON_CONFIG_H


//
// Put a copy of some platform-specific's declarations here to prevent
// including the platform-specific's header file(s) this early
//
#ifdef __cplusplus
extern "C" {
#endif

// Far-pointer typedefs
typedef signed   int  int_farptr_t __ECXX_ATTR_MODE_SI;
typedef unsigned int uint_farptr_t __ECXX_ATTR_MODE_SI;

#ifdef __cplusplus
} // extern "C"
#endif


//
// Available RAM size
//
#define RAM_SIZE    (RAMEND - RAMSTART + 1)
#define RAM_SIZE_KB (RAM_SIZE / 1024)


//
// Define the helper macro ECXX_PGM_READ_BYTE as needed
//
#ifndef ECXX_AVRX_DO_NOT_USE_PROGMEM
    #ifdef __AVR_HAVE_ELPM__
        #define ECXX_PGM_READ_BYTE pgm_read_byte_far
    #else
        #define ECXX_PGM_READ_BYTE pgm_read_byte_near
    #endif
#else
    #define ECXX_PGM_READ_BYTE(ADDR) ( *reinterpret_cast<const uint8_t*>(ADDR) )
#endif // ECXX_AVRX_DO_NOT_USE_PROGMEM


//
// Undefine the PROGMEM and PROGMEM_FAR macros as needed (so that their dummy versions can be defined later)
//
#ifdef ECXX_AVRX_DO_NOT_USE_PROGMEM
    #undef PROGMEM
    #undef PROGMEM_FAR
#endif // ECXX_AVRX_DO_NOT_USE_PROGMEM


//
// Platform-specific's program-memory macros
//

#ifndef ECXX_AVRX_DO_NOT_USE_PROGMEM

// Inform that the platform-specific's program-memory macros have been defined here
#define ECXX_PROGMEM_FMACROS_NEAR_DEFINED
#define ECXX_PROGMEM_FMACROS_FAR_DEFINED

// Attribute to put constants in program memory
#undef PROGMEM
#define PROGMEM __attribute__((__progmem__))

#undef PROGMEM_FAR
#define PROGMEM_FAR PROGMEM __ECXX_ATTR_SEC_FINI2

// Macro to put inline string literals in program memory (normal)
// NOTE : PGM_STR() automatically calls eCxx::PGM_PTR<>()
#define PGM_STR(STR) ( __extension__ ( {   \
    static const char __C[] PROGMEM = STR; \
    eCxx::PGM_PTR(&__C[0]);                \
} ) )

// Macro to put inline string literals in program memory (far)
// NOTE : PGM_STR_FAR() automatically calls eCxx::PGM_PTR_FAR<>()
#define PGM_STR_FAR(STR) ( __extension__ ( {   \
    static const char __C[] PROGMEM_FAR = STR; \
    eCxx::PGM_PTR_FAR(&__C[0]);                \
} ) )

#endif // ECXX_AVRX_DO_NOT_USE_PROGMEM


namespace eCxx {


//
// Platform-specific's program-memory pointers
//
#ifndef ECXX_AVRX_DO_NOT_USE_PROGMEM

#ifdef __AVR_HAVE_ELPM__

    // Inform that the platform-specific's program-memory pointers have been defined here
    #define ECXX_PROGMEM_POINTER_FAR_TYPE_DEFINED
    #define ECXX_PROGMEM_POINTER_FAR_TYPE_IS_UINT

    // Define the program-memory-pointers (far)
    typedef uint_farptr_t _PGMVoidPointerFar;
    typedef uint_farptr_t _PGMCharPointerFar;

#endif // AVR_HAVE_ELPM

#endif // ECXX_AVRX_DO_NOT_USE_PROGMEM


//
// Platform-specific's program-memory translators
//
#ifndef ECXX_AVRX_DO_NOT_USE_PROGMEM

#ifdef __AVR_HAVE_ELPM__

    // Inform that the platform-specific's program-memory translator have been defined here
    #define ECXX_PROGMEM_TRANSLATOR_FAR_DEFINED

    // Compute the far-address of the given address (for static/global and simple constants)
    template <typename ValueT>
    inline uint_farptr_t __ECXX_ATTR_ALWAYS_INLINE _PGM_PTR_FAR(const ValueT* ptr)
    {
        uint_farptr_t tmp;

        __asm__ __volatile__ (
            "ldi %A0, lo8(%1) \n\t"
            "ldi %B0, hi8(%1) \n\t"
            "ldi %C0, hh8(%1) \n\t"
            "clr %D0          \n\t"
            : "=d"(tmp)
            :  "p"(ptr)
        );

        return tmp;
    }

    template <typename ValueT>
    inline BasicPGMPointerFar<uint_farptr_t> __ECXX_ATTR_ALWAYS_INLINE PGM_PTR_FAR(const ValueT* ptr)
    { return BasicPGMPointerFar<uint_farptr_t>( _PGM_PTR_FAR(ptr) ); }

    // Compute the far-address of the given address (for array of constants)
    template <typename ValueT, size_t N>
    inline BasicPGMPointerFar<uint_farptr_t> __ECXX_ATTR_ALWAYS_INLINE PGM_PTR_FAR(const ValueT (&ptr)[N], size_t idx)
    { return BasicPGMPointerFar<uint_farptr_t>( _PGM_PTR_FAR(ptr) + idx * sizeof(ValueT) ); }

#endif // AVR_HAVE_ELPM

#endif // ECXX_AVRX_DO_NOT_USE_PROGMEM


} // namespace eCxx


#endif
