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


#include <eCxx/eCxx.h>

#include "AVR_Common.h"


//
// Helpers functions
//
template <typename ValueT>
static inline void printOneHex(ValueT val)
{
    auto& u = App::uart();

    u.writeHexString(
        val,
        eCxx::Convert::NumSign::None_Minus,
        2,
        eCxx::Convert::NumPad::Zero,
        eCxx::Convert::NumFormat::Uppercase
    );

    u.write(' ');
}


template <typename ValueT>
static inline void printHex_RAM(ValueT val, eCxx::uint8_t count)
{
    auto& u = App::uart();

    u.writeString( PGM_STR("    ") );

    for(eCxx::uint8_t i = 0; i < count; ++i) {
        eCxx::uint8_t tmp = 0;
        tmp = val[i];

        printOneHex(tmp);
    }

    u.write('\n');
}


template <typename ValueT>
static inline void printHex_LPM(ValueT val, eCxx::uint8_t count)
{
    #define readByte_LPM(VAL, ADDR) \
        __asm__ __volatile__ (      \
            "lpm %0, Z \n\t"        \
            : "=r"(VAL)             \
            : "z"(ADDR)             \
        )

    auto& u = App::uart();

    u.writeString( PGM_STR("    ") );

    for(eCxx::uint8_t i = 0; i < count; ++i) {
        eCxx::uint8_t tmp = 0;
        readByte_LPM(tmp, &val[i]);

        printOneHex(tmp);
    }

    u.write('\n');
}


template <typename ValueT>
static inline void printHex_LD(ValueT val, eCxx::uint8_t count)
{
    #define readByte_LD(VAL, ADDR) \
        __asm__ __volatile__ (     \
            "ld %0, Z \n\t"        \
            : "=r"(VAL)            \
            : "z"(ADDR)            \
        )

    auto& u = App::uart();

    u.writeString( PGM_STR("    ") );

    for(eCxx::uint8_t i = 0; i < count; ++i) {
        eCxx::uint8_t tmp = 0;
        readByte_LD(tmp, &val[i]);

        printOneHex(tmp);
    }

    u.write('\n');
}


//
// Test arrays
//
constexpr eCxx::uint8_t         valConstexpr[4] = { 0x11, 0x12, 0x13, 0x14 };
const     eCxx::uint8_t         valConst    [4] = { 0x21, 0x22, 0x23, 0x24 };
static    eCxx::uint8_t         valStatic   [4] = { 0x31, 0x32, 0x33, 0x34 };
const     eCxx::uint8_t PROGMEM valConstPGM [4] = { 0x41, 0x42, 0x43, 0x44 };


//
// The init function
//
void eCxx_user_init()
{
    // Initialize application
    initBasicAVRApp();

    // Initialize UART
    auto& u = App::uart();
    u.begin(9600);

    /*
     * Tested using AVR GCC 7.3.0 with 'avrxmega3' patch from Atmel AVR 8-bit Toolchain version 3.6.1
     *
     * The unexpected behavior in AVRX is observed when using avr-objcopy options:
     *     -j .text -j .data -O ihex
     *
     * The correct behavior in AVRX is observed when using avr-objcopy options:
     *     -j .text -j .rodata -j .data -O ihex
     * or:
     *     -R .eeprom -O ihex
     *
     * In classic AVR, all variations of the options works equally well.
     */

    u.write('\n');

    u.writeStringln( PGM_STR("Read test using plain array access ...") );
    printHex_RAM(valConstexpr, 4); // AVR : correct value (as expected) │ AVRX : wrong   value (unexpected!)
    printHex_RAM(valConst,     4); // AVR : correct value (as expected) │ AVRX : wrong   value (unexpected!)
    printHex_RAM(valStatic,    4); // AVR : correct value (as expected) │ AVRX : correct value (as expected)
    printHex_RAM(valConstPGM,  4); // AVR : wrong   value (as expected) │ AVRX : wrong   value (as expected)
    u.write('\n');
    App::yield();

    u.writeStringln( PGM_STR("Read test using LPM ...") );
    printHex_LPM(valConstexpr, 4); // AVR : wrong   value (as expected) │ AVRX : wrong   value (as expected)
    printHex_LPM(valConst,     4); // AVR : wrong   value (as expected) │ AVRX : wrong   value (as expected)
    printHex_LPM(valStatic,    4); // AVR : wrong   value (as expected) │ AVRX : wrong   value (as expected)
    printHex_LPM(valConstPGM,  4); // AVR : correct value (as expected) │ AVRX : correct value (as expected)
    u.write('\n');
    App::yield();

    u.writeStringln( PGM_STR("Read test using LD ...") );
    printHex_LD (valConstexpr, 4); // AVR : correct value (as expected) │ AVRX : wrong   value (unexpected!)
    printHex_LD (valConst,     4); // AVR : correct value (as expected) │ AVRX : wrong   value (unexpected!)
    printHex_LD (valStatic,    4); // AVR : correct value (as expected) │ AVRX : correct value (as expected)
    printHex_LD (valConstPGM,  4); // AVR : wrong   value (as expected) │ AVRX : wrong   value (as expected)
    u.write('\n');
    App::yield();

    /*
     * Test result for ATmega2560             │ Test result for ATmega4808
     * (-j .text -j .data -O ihex)            │ (-j .text -j .data -O ihex)
     *                                        │
     * Read test using plain array access ... │ Read test using plain array access ...
     *     11 12 13 14                        │     FF FF FF FF
     *     21 22 23 24                        │     FF FF FF FF
     *     31 32 33 34                        │     31 32 33 34
     *     00 A1 85 00                        │     00 00 00 00
     *                                        │
     * Read test using LPM ...                │ Read test using LPM ...
     *     80 83 84 E3                        │     FF FF FF FF
     *     80 83 84 E3                        │     FF FF FF FF
     *     0F B6 F8 94                        │     FF FF FF FF
     *     41 42 43 44                        │     41 42 43 44
     *                                        │
     * Read test using LD ...                 │ Read test using LD ...
     *     11 12 13 14                        │     FF FF FF FF
     *     21 22 23 24                        │     FF FF FF FF
     *     31 32 33 34                        │     31 32 33 34
     *     00 A1 6A 00                        │     00 00 00 00
     */
}


//
// The task function
//
void eCxx_user_task()
{
    // Process event
    App::processEvent();
}
