/**
 * *************************************************************************************************************
 * @file Isp_Iap.h
 * @author BH_CodeGenerator
 * @version 0.1
 * @date 2022-04-01
 * @warning <!--auto generated by Tools, do not modify or add anything, otherwise, your change will be lost!!! -->
 * @brief all the functions prototypes for Isp_Iap firmware library
 * MCU / CFG Ver. :BH66F2475/1.7
 * Version = 1.0.0

 * *************************************************************************************************************
 *  @attention
 *
 *  Firmware Disclaimer Information
 *
 *  1. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *     code, which is supplied by BEST HEALTH ELECTRONIC Inc., (hereinafter referred to as BestHealth) is the
 *     proprietary and confidential intellectual property of BestHealth, and is protected by copyright law and
 *     other intellectual property laws.
 *
 *  2. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *     code, is confidential information belonging to BestHealth, and must not be disclosed to any third parties
 *     other than BestHealth and the customer.
 *
 *  3. The program technical documentation, including the code, is provided and for customer reference
 *     only. After delivery by BestHealth, the customer shall use the program technical documentation, including
 *     the code, at their own risk. BestHealth disclaims any expressed, implied or statutory warranties, including
 *     the warranties of merchantability, satisfactory quality and fitness for a particular purpose.
 *
 *  <h2><center>Copyright (C) BEST HEALTH ELECTRONIC Inc. All rights reserved</center></h2>
 */
/* Includes ------------------------------------------------------------------------------------------------*/
#include "Iap_Isp.h"
#include <stdint.h>
#include <BH66F2475.h>
#include "..\driver\stdbool.h"

/* user defined---------------------------------------------------------------------------------------------*/
const uint8_t bootLoader_Ver[] = {'1', '0', '1'};
// MCU info  Cannot modify
#define PROM_WORD_SIZE       ((uint16_t)1024 * 32)
#define PAGE_WORD_SIZE       64    //! <if it exceeds 64word/128byte, it is fixed to 128byte (the maximum array limited by HT8 architecture is 128byte)
#define PACKET_PROM_SIZE     16    //!< one packet Prom data size
#define PACKET_PROM_NUM_SIZE ((uint16_t)1024 * (32 - 1) * 2 / PACKET_PROM_SIZE)
#define PAGE_BREAK           (PAGE_WORD_SIZE * 2 / PACKET_PROM_SIZE)    // Number of frames to be received for a single page

#define PROM_AP_ADDRESS_IapStart 0x0040    //! <interrupt offset address is not recommended to be modified

#define WORD_LO(w) ((uint8_t)(w & 0xff))
#define WORD_HI(w) ((uint8_t)(w >> 8))

typedef union {
    struct {
        unsigned char byte0;
        unsigned char byte1;
    } byte;

    int s16;
    unsigned int u16;
} __16_type;

typedef enum {
    PACKET_REV_STATE_FOUND_NULL,
    PACKET_REV_STATE_FOUND_CMD,
    PACKET_REV_STATE_FOUND_IAP_ROM_DATA,
} packet_rev_state_type_e;

typedef struct
{
    uint8_t rxBuf[20];
    uint8_t packetRevState;
    uint8_t packetLen;
    uint8_t packetRxLen;
    uint8_t pageBreak;
    uint8_t workMode;
    uint16_t promNum;
    uint16_t pageAddress;
} Iap_Isp_PackData;

static volatile Iap_Isp_PackData iap __attribute__((at(0x180)));
static volatile uint8_t promWriteBuf[PAGE_WORD_SIZE * 2] __attribute__((at(0x280)));
static volatile uint8_t promReadBuf[PAGE_WORD_SIZE * 2] __attribute__((at(0x380)));

/* interrupt offset Please do not modify---------------------------------------------------------------------*/
// clang-format off
// new interrupt Vector address = interrupt Vector address   PROM_AP_ADDRESS_START
__attribute__((interrupt(0x04), reg_acc(0x80), redirect_at(0x04 + PROM_AP_ADDRESS_START))) void ISR_0x04_Routine(){}
__attribute__((interrupt(0x08), reg_acc(0x81), redirect_at(0x08 + PROM_AP_ADDRESS_START))) void ISR_0x08_Routine(){}
__attribute__((interrupt(0x0C), reg_acc(0x82), redirect_at(0x0C + PROM_AP_ADDRESS_START))) void ISR_0x0C_Routine(){}
__attribute__((interrupt(0x10), reg_acc(0x83), redirect_at(0x10 + PROM_AP_ADDRESS_START))) void ISR_0x10_Routine(){}
__attribute__((interrupt(0x14), reg_acc(0x84), redirect_at(0x14 + PROM_AP_ADDRESS_START))) void ISR_0x14_Routine(){}
__attribute__((interrupt(0x18), reg_acc(0x85), redirect_at(0x18 + PROM_AP_ADDRESS_START))) void ISR_0x18_Routine(){}
__attribute__((interrupt(0x1C), reg_acc(0x86), redirect_at(0x1C + PROM_AP_ADDRESS_START))) void ISR_0x1C_Routine(){}
__attribute__((interrupt(0x20), reg_acc(0x87), redirect_at(0x20 + PROM_AP_ADDRESS_START))) void ISR_0x20_Routine(){}
__attribute__((interrupt(0x24), reg_acc(0x88), redirect_at(0x24 + PROM_AP_ADDRESS_START))) void ISR_0x24_Routine(){}
__attribute__((interrupt(0x28), reg_acc(0x89), redirect_at(0x28 + PROM_AP_ADDRESS_START))) void ISR_0x28_Routine(){}
__attribute__((interrupt(0x2C), reg_acc(0x8A), redirect_at(0x2C + PROM_AP_ADDRESS_START))) void ISR_0x2C_Routine(){}
__attribute__((interrupt(0x30), reg_acc(0x8B), redirect_at(0x30 + PROM_AP_ADDRESS_START))) void ISR_0x30_Routine(){}

/* Reset defined--------------------------------------------------------------------------------------------*/
#define ResetMcu_IapReg()               { _fc1 = 0x55;}//! <software controls the fc1 register to reset the MCU, which will be set after reset

/* Uart defined---------------------------------------------------------------------------------------------*/
// clang-format off
#define Uart_Enable()                      { _uarten = 1; _txen = 1; _rxen = 1;}
#define Uart_Disable()                     { _uarten = 0;}
#define SET_UARTn_TX_INTERRUPT_DISABLE()   { _tiie = 0; _teie = 0;}
#define SET_UARTn_TX_INTERRUPT_ENABLE()    { _tiie = 1; _teie = 1;}
#define SET_UARTn_Format_D8_NONE_S1()      { _ucr1 = 0x00; }// Transmit 8bit = (8bit-Data None-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D7_EVEN_S1()      { _ucr1 = 0x20; }// Transfer 8bit = (7bit-Data Even-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D7_ODD_S1()       { _ucr1 = 0x30; }// Transmit 8bit = (7bit-Data Odd -Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D9_NONE_S1()      { _ucr1 = 0x40; }// Transfer 9bit = (9bit-Data None-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D8_EVEN_S1()      { _ucr1 = 0x60; }// Transfer 9bit = (8bit-Data Even-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D8_ODD_S1()       { _ucr1 = 0x70; }// Transfer 9bit = (8bit-Data Odd-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D8_NONE_S2()      { _ucr1 = 0x00; }// Transmit 8bit = (8bit-Data None-Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D7_EVEN_S2()      { _ucr1 = 0x20; }// Transfer 8bit = (7bit-Data Even-Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D7_ODD_S2()       { _ucr1 = 0x30; }// Transfer 8bit = (7bit-Data Odd -Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D9_NONE_S2()      { _ucr1 = 0x40; }// Transfer 9bit = (9bit-Data None-Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D8_EVEN_S2()      { _ucr1 = 0x60; }// Transfer 9bit = (8bit-Data Even-Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D8_ODD_S2()       { _ucr1 = 0x70; }// Transfer 9bit = (8bit-Data Odd-Parity None-Add)2bit-Stop

#if FH_CLOCK_FREQ == 4000000
    #define SET_UARTn_BAUDRATE_300()       { _ufcr = 0x18; _brdl = 0x15; _brdh = 0x34;}
    #define SET_UARTn_BAUDRATE_600()       { _ufcr = 0x28; _brdl = 0x0A; _brdh = 0x1A;}
    #define SET_UARTn_BAUDRATE_1200()      { _ufcr = 0x18; _brdl = 0x05; _brdh = 0x0D;}
    #define SET_UARTn_BAUDRATE_2400()      { _ufcr = 0x28; _brdl = 0x82; _brdh = 0x06;}
    #define SET_UARTn_BAUDRATE_4800()      { _ufcr = 0x18; _brdl = 0x41; _brdh = 0x03;}
    #define SET_UARTn_BAUDRATE_9600()      { _ufcr = 0x28; _brdl = 0xA0; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_14400()     { _ufcr = 0x30; _brdl = 0x15; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_19200()     { _ufcr = 0x18; _brdl = 0xD0; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_28800()     { _ufcr = 0x38; _brdl = 0x8A; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_38400()     { _ufcr = 0x08; _brdl = 0x68; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_56000()     { _ufcr = 0x18; _brdl = 0x47; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_57600()     { _ufcr = 0x20; _brdl = 0x45; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_115200()    { _ufcr = 0x30; _brdl = 0x22; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_230400()    { _ufcr = 0x18; _brdl = 0x11; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_250000()    { _ufcr = 0x00; _brdl = 0x10; _brdh = 0x00;}
#endif
#if FH_CLOCK_FREQ == 8000000
    #define SET_UARTn_BAUDRATE_300()       { _ufcr = 0x28; _brdl = 0x2A; _brdh = 0x68;}
    #define SET_UARTn_BAUDRATE_600()       { _ufcr = 0x18; _brdl = 0x15; _brdh = 0x34;}
    #define SET_UARTn_BAUDRATE_1200()      { _ufcr = 0x28; _brdl = 0x0A; _brdh = 0x1A;}
    #define SET_UARTn_BAUDRATE_2400()      { _ufcr = 0x18; _brdl = 0x05; _brdh = 0x0D;}
    #define SET_UARTn_BAUDRATE_4800()      { _ufcr = 0x28; _brdl = 0x82; _brdh = 0x06;}
    #define SET_UARTn_BAUDRATE_9600()      { _ufcr = 0x18; _brdl = 0x41; _brdh = 0x03;}
    #define SET_UARTn_BAUDRATE_14400()     { _ufcr = 0x20; _brdl = 0x2B; _brdh = 0x02;}
    #define SET_UARTn_BAUDRATE_19200()     { _ufcr = 0x28; _brdl = 0xA0; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_28800()     { _ufcr = 0x30; _brdl = 0x15; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_38400()     { _ufcr = 0x18; _brdl = 0xD0; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_56000()     { _ufcr = 0x38; _brdl = 0x8E; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_57600()     { _ufcr = 0x38; _brdl = 0x8A; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_115200()    { _ufcr = 0x20; _brdl = 0x45; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_230400()    { _ufcr = 0x30; _brdl = 0x22; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_250000()    { _ufcr = 0x00; _brdl = 0x20; _brdh = 0x00;}
#endif
#if FH_CLOCK_FREQ == 12000000
    #define SET_UARTn_BAUDRATE_300()       { _ufcr = 0x00; _brdl = 0x40; _brdh = 0x9C;}
    #define SET_UARTn_BAUDRATE_600()       { _ufcr = 0x00; _brdl = 0x20; _brdh = 0x4E;}
    #define SET_UARTn_BAUDRATE_1200()      { _ufcr = 0x00; _brdl = 0x10; _brdh = 0x27;}
    #define SET_UARTn_BAUDRATE_2400()      { _ufcr = 0x00; _brdl = 0x88; _brdh = 0x13;}
    #define SET_UARTn_BAUDRATE_4800()      { _ufcr = 0x00; _brdl = 0xC4; _brdh = 0x09;}
    #define SET_UARTn_BAUDRATE_9600()      { _ufcr = 0x00; _brdl = 0xE2; _brdh = 0x04;}
    #define SET_UARTn_BAUDRATE_14400()     { _ufcr = 0x18; _brdl = 0x41; _brdh = 0x03;}
    #define SET_UARTn_BAUDRATE_19200()     { _ufcr = 0x00; _brdl = 0x71; _brdh = 0x02;}
    #define SET_UARTn_BAUDRATE_28800()     { _ufcr = 0x28; _brdl = 0xA0; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_38400()     { _ufcr = 0x20; _brdl = 0x38; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_56000()     { _ufcr = 0x10; _brdl = 0xD6; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_57600()     { _ufcr = 0x18; _brdl = 0xD0; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_115200()    { _ufcr = 0x08; _brdl = 0x68; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_230400()    { _ufcr = 0x08; _brdl = 0x34; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_250000()    { _ufcr = 0x00; _brdl = 0x30; _brdh = 0x00;}
#endif
// Clang-format one

/*
* @Brief configuration
**
*Cfg_tForm
*/
static void Bl_Uart_Cfg()
{
#if defined(PIN_TX_PA6)
    PIN_PA6_TX();
#elif defined(PIN_TX_PA0)
    PIN_PA0_TX();
#else
    #error "Please set the correct UART Tx Pin"
#endif
#if defined(PIN_RX_PA7)
    PIN_PA7_RX();
#elif defined(PIN_RX_PA2)
    PIN_PA2_RX();
#else
    #error "Please set the correct UART Rx Pin"
#endif
    SET_UARTn_Format_D8_NONE_S1();// Set the data transmission format.
// Baud rate setting
#if UART_BAUD_RATE == 300 && FH_CLOCK_FREQ == 4000000
    SET_UARTn_BAUDRATE_300();
#elif UART_BAUD_RATE == 1200
    SET_UARTn_BAUDRATE_1200();
#elif UART_BAUD_RATE == 2400
    SET_UARTn_BAUDRATE_2400();
#elif UART_BAUD_RATE == 4800
    SET_UARTn_BAUDRATE_4800();
#elif UART_BAUD_RATE == 9600
    SET_UARTn_BAUDRATE_9600();
#elif UART_BAUD_RATE == 19200
    SET_UARTn_BAUDRATE_19200();
#elif UART_BAUD_RATE == 38400 && FH_CLOCK_FREQ == 8000000
    SET_UARTn_BAUDRATE_38400();
#elif UART_BAUD_RATE == 57600 && FH_CLOCK_FREQ == 12000000
    SET_UARTn_BAUDRATE_57600();
#elif UART_BAUD_RATE == 250000
    SET_UARTn_BAUDRATE_250000();
#else
    #error "Please set the correct UART_BAUD_RATE and FH_CLOCK_FREQ"
#endif
    _ufcr &= 0xFC;    // default rx fifo 1byte trigger
    _ufcr |= 0x01;
    _rie = 1;// OERR (overflow)/RXIF (valid data) can be set when the interrupt flag bit is set.
}

/**
* @ Brief Send Hexadecimal Buf
**
* Param bytes
* Param len byte len
*/
static void Bl_Uart_TxByte_Polling(uint8_t *byte, uint8_t len)
{
    // SET_UARTn_TX_INTERRUPT_DISABLE();
    uint8_t i;
    for (i = 0; i < len; i++)
    {
        // clang-format off
        _acc = _usr;
        _txr_rxr = *byte;
        while(!_tidle);
        byte++;
        // clang-format on
    }
}

volatile bit checkTimeOut;
volatile bit isTimeOut;

/* *
 * @ Brief Receive 1byte data
 *
 * @ Return uint8_t receive 1byte of data
 */
static void Bl_Uart_RxByte_Polling(uint8_t *data)
{
    _acc = _usr;
    uint16_t timeOutCount;
    isTimeOut    = 0;
    timeOutCount = 0;
    while (!(_usr & 0x04))
    {
        if (checkTimeOut)
        {
            // clang-format off
            GCC_NOP();GCC_NOP();GCC_NOP();GCC_NOP();GCC_NOP();
            GCC_NOP();GCC_NOP();GCC_NOP();GCC_NOP();GCC_NOP();
            GCC_NOP();GCC_NOP();GCC_NOP();
            // clang-format on
            timeOutCount++;
            // Receive timeout
            if (timeOutCount > 65000)    // 1000ms
            {
                isTimeOut = 1;
                return;
            }
        }
        // GCC_CLRWDT();
    }
    *data = _txr_rxr;
}

/* Oscillator defined---------------------------------------------------------------------------------------*/
static void Bl_Oscillators_Cfg(void)
{
#if (SYS_CLOCK_FREQ == 4000000)
    _hircc = 0x01;
#elif (SYS_CLOCK_FREQ == 8000000)
    _hircc = 0x05;
#elif (SYS_CLOCK_FREQ == 12000000)
    _hircc = 0x09;
#else
    #error "Please set the correct SYS_CLOCK_FREQ in boardCfg.h"
#endif
    while (!_hircf);    // Wait HIRC Oscillator Stable
}

/* IAP defined--------------------------------------------------------------------------------------------*/
#define PAGE_ERASE_MODE  0x90    // Block erase mode
#define READ_FLASH_MODE  0x32    // read flash mode
#define ENABLE_FWEN_MODE 0x68    // Write function enable mode
#define WRITE_FLASH_MODE 0x80    // write flash mode

static bool Bl_IAP_EraseWrite_Enable(void)
{
    /// Bool IMMEDIATES
    // EMIST = _emi;
    //_Emi=0;
    // GCC_CLRWDT();
    _fc0  = ENABLE_FWEN_MODE;    // write function enable mode, start a 300us counter
    _fd1l = 0x00;                // Write the fixed pattern data sequence to the corresponding register in sequence.
    _fd1h = 0x04;
    _fd2l = 0x0D;
    _fd2h = 0x09;
    _fd3l = 0xC3;
    _fd3h = 0x40;
    // _Emi = Emission;
    while (_fwpen);    // This hardware is automatically cleared after the write enable is successful.
    return _cfwen;
}

static bool Bl_IAP_Erase_Page(uint16_t address)
{
    if (!Bl_IAP_EraseWrite_Enable())
        return false;
    _farl = WORD_LO(address);
    _farh = WORD_HI(address);
    _fc0  = PAGE_ERASE_MODE;
    uint8_t i;
    for (i = 0; i < 64; i++)
    {
        _fd0h = 0x00;    // Tag Address
    }
    _fwt = 1;
    while (_fwt)
    {
        // GCC_CLRWDT();
    }
    // TODO: should use table read check data is 0x00
    _cfwen = 0;
    return true;
}

static void Bl_IAP_Read(uint16_t address)
{
    _emi = 0;
    // GCC_CLRWDT();
    _fc0  = READ_FLASH_MODE;
    _farl = WORD_LO(address);
    _farh = WORD_HI(address);
    uint8_t i;
    for (i = 0; i < PAGE_WORD_SIZE * 2; i++)
    {
        _frd = 1;    // Start reading
        while (_frd);
        promReadBuf[i++] = _fd0l;
        promReadBuf[i]   = _fd0h;
        _farl++;
    }
    _frden = 0;
    _emi   = 1;
}

static bool Bl_IAP_Write_Page(uint16_t address)
{
    if (!Bl_IAP_EraseWrite_Enable())
        return false;

    _clwb = 1;                   // write Initiate clear write buffer process
    _fc0  = WRITE_FLASH_MODE;    // write mode
    _farl = WORD_LO(address);
    _farh = WORD_HI(address);

    uint8_t i;
    for (i = 0; i < PAGE_WORD_SIZE * 2; i++)
    {
        _fd0l = promWriteBuf[i++];
        _fd0h = promWriteBuf[i];    // L-->H
                                    // GCC_CLRWDT();
    }

    _fwt = 1;
    while (_fwt)
    {
        // GCC_CLRWDT();
    }
    // TODO table read check data is correct
    _cfwen = 0;
    return true;
}

static uint8_t Bl_GetCheckSum(uint8_t endN)
{
    uint8_t i, checkSum = 0;
    for (i = 0; i < endN; i++)
    {
        checkSum += iap.rxBuf[i];
    }
    return (~checkSum + 1);
}

static bool Bl_Packet_data_unpack(uint8_t data)
{
    if (iap.packetRevState == PACKET_REV_STATE_FOUND_NULL)    // Frame header
    {
        iap.packetLen                = 0;
        iap.packetRxLen              = 0;
        iap.rxBuf[iap.packetRxLen++] = data;
        if (data == 'B')
        {
            iap.packetRevState++;
        }
        if (data == 'D')
        {
            iap.packetRevState = PACKET_REV_STATE_FOUND_IAP_ROM_DATA;
        }
        return false;
    }
    else if (iap.packetRevState == PACKET_REV_STATE_FOUND_CMD)    // Command parsing
    {
        iap.rxBuf[iap.packetRxLen++] = data;
        if (iap.packetRxLen == 3)
        {
            iap.packetRevState = PACKET_REV_STATE_FOUND_NULL;
            if (Bl_GetCheckSum(2) == iap.rxBuf[2])
            {
                return true;
            }
        }
    }
    else if (iap.packetRevState == PACKET_REV_STATE_FOUND_IAP_ROM_DATA)    // RomData parsing
    {
        iap.rxBuf[iap.packetRxLen++] = data;
        if (iap.packetRxLen == 20)
        {
            __16_type num;
            num.byte.byte1     = iap.rxBuf[1];
            num.byte.byte0     = iap.rxBuf[2];
            iap.packetRevState = PACKET_REV_STATE_FOUND_NULL;
            if (Bl_GetCheckSum(19) == iap.rxBuf[19] && iap.promNum == num.u16)
            {
                iap.promNum++;
                if (iap.promNum <= PACKET_PROM_NUM_SIZE)    // Add a protection mechanism, and the write address must be within the range to prevent the BL program from being written by mistake, resulting in downtime.
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                // ’R' : prom verify error，上位机重发此 PACKET_PROM_SIZE bytes
                uint8_t txBuf[3] = {'D', 'R', 0x6A};    // 44 52 6A
                Bl_Uart_TxByte_Polling(txBuf, 3);
                return false;
            }
        }
    }
    return false;
}

__attribute__((at(PROM_AP_ADDRESS_IapStart))) void BootLoader_Start(void)
{
    Bl_IAP_Erase_Page(PROM_AP_ADDRESS_START - PAGE_WORD_SIZE);
    ResetMcu_IapReg();
}

#define ERASE_PROM             0x00    // !< Erase Prom
#define REQUEST_START          0x01    //! <Request Prom Data
#define UPDATE_PROM            0x02    // !< Program   Verify
#define WRITE_SIGNATURE_REBOOT 0x03    // !< Write Signature Reboot

// BL last page placement signature
const uint16_t __attribute__((at(PROM_AP_ADDRESS_START - PAGE_WORD_SIZE))) signature[PAGE_WORD_SIZE] = {0xAA55};

void main_AP();

void main()
{
    // page read is not recommended. LXT requires 500ms and LIRC requires 100us
    Bl_IAP_Read(PROM_AP_ADDRESS_START - PAGE_WORD_SIZE);
    if (promReadBuf[0] == 0x55 && promReadBuf[1] == 0xAA)
    {
        asm("call 0x440");    // Jmp to AP  !!!!ADDRESS shout be the same to main_AP
    }
    else
    {
        // Jmp to bootLoader
        _emi  = 0;       // disable all interrupt
        _wdtc = 0xA8;    // disable wdt
        Bl_Oscillators_Cfg();
        Bl_Uart_Cfg();
        Uart_Enable();
        uint16_t i;
        // 1. eraseProm
        for (i = PROM_AP_ADDRESS_START; i < PROM_WORD_SIZE; i += PAGE_WORD_SIZE)
        {
            Bl_IAP_Erase_Page(i);
        }
        iap.workMode = REQUEST_START;
        while (1)
        {
            if (iap.workMode == REQUEST_START)    // 2. request prom data command
            {
                uint8_t txBuf[3] = {'B', 'S', 0x6B};    // 42 53 6B
                Bl_Uart_TxByte_Polling(txBuf, 3);
                iap.packetRevState = PACKET_REV_STATE_FOUND_NULL;
                // wait command ack
                uint8_t rxData = 0;
                iap.rxBuf[1]   = 0;
                do
                {
                    checkTimeOut = 1;
                    Bl_Uart_RxByte_Polling(&rxData);
                    if (isTimeOut)
                    {
                        break;    // Receive timeout, exit retransmission
                    }
                } while (!Bl_Packet_data_unpack(rxData));
                if (iap.rxBuf[1] == 'S')
                {
                    iap.workMode++;
                    iap.pageBreak = 0;
                    iap.promNum   = 0;
                }
            }
            if (iap.workMode == UPDATE_PROM)    // 3. receive prom data and verify update
            {
                // wait prom data packet
                iap.packetRevState = PACKET_REV_STATE_FOUND_NULL;
                uint8_t rxData;
                do
                {
                    checkTimeOut = 0;
                    Bl_Uart_RxByte_Polling(&rxData);
                } while (!Bl_Packet_data_unpack(rxData));
                if (iap.rxBuf[0] == 'D')    // Program   Verify
                {
                    // restore prom data and check is receive one page data
                    uint8_t i;
                    for (i = 0; i < PACKET_PROM_SIZE; i++)
                    {
                        promWriteBuf[PACKET_PROM_SIZE * iap.pageBreak + i] = iap.rxBuf[i + 3];
                    }
                    iap.pageBreak++;
                    // receive one page data write prom data
                    bool isWriteOk;
                    isWriteOk = true;
                    if (iap.pageBreak >= PAGE_BREAK)
                    {
                        uint8_t reWriteCnt;
                        iap.pageBreak = 0x00;
                        reWriteCnt    = 0x00;
                        do
                        {
                            iap.pageAddress = PROM_AP_ADDRESS_START + (iap.promNum / PAGE_BREAK - 1) * PAGE_WORD_SIZE;
                            Bl_IAP_Write_Page(iap.pageAddress);
                            // read and check write data is Correct
                            Bl_IAP_Read(iap.pageAddress);
                            isWriteOk = true;
                            for (i = 0; i < (PAGE_WORD_SIZE * 2); i++)
                            {
                                if (promReadBuf[i] != promWriteBuf[i])
                                {
                                    isWriteOk = false;
                                    break;
                                }
                            }
                            if (isWriteOk)
                            {
                                break;
                            }
                            else
                            {
                                reWriteCnt++;
                            }

                        } while (reWriteCnt < 3);
                        if (!isWriteOk)
                        {
                            // 'E' : prom write error, the upper computer retransmits the first 8 PACKET_PROM_SIZE bytes in turn
                            iap.promNum -= (PAGE_BREAK - 1);
                            uint8_t txBuf[3] = {'B', 'E', 0x79};    // 42 45 79
                            Bl_Uart_TxByte_Polling(txBuf, 3);
                        }
                    }
                    if (isWriteOk)
                    {
                        // ‘O': prom verify and write success，上位机继续发后 PACKET_PROM_SIZE bytes
                        uint8_t txBuf[3] = {'D', 'O', 0x6D};    // 44 4F 6D
                        Bl_Uart_TxByte_Polling(txBuf, 3);
                    }
                }
                if (iap.rxBuf[0] == 'B' && iap.rxBuf[1] == 'W')    // Write Signature Reboot
                {
                    iap.workMode++;
                }
            }
            if (iap.workMode == WRITE_SIGNATURE_REBOOT)
            {
                // 4. write signature
                promWriteBuf[0] = 0x55;
                promWriteBuf[1] = 0xAA;
                while (!Bl_IAP_Write_Page((PROM_AP_ADDRESS_START - PAGE_WORD_SIZE)));

                // 5. reboot and enter ap
                uint8_t txBuf[3] = {'B', 'W', 0x67};    // 42 57 67
                Bl_Uart_TxByte_Polling(txBuf, 3);
                ResetMcu_IapReg();
            }
        }
    }
}