// the IVT sits at the top of the binary and is defined in vectors_W7500x.c
// the first pointer is to _estack, the second pointer is to the hardware reset handler
// the hardware reset handler is set to Reset_Handler by default, but this update replaces that with Boot_Entry
// Boot_Entry then calls Reset_Handler

#include "bootloader.h"
#include "w7500x_flash.h"

#define boot_enable_irq()   __asm volatile ("cpsie i" ::: "memory")
#define boot_disable_irq()  __asm volatile ("cpsid i" ::: "memory")

// CRC 16 table using Koopman's 0xd175
// for use in verifying file transfers
static const uint16_t crc16_table[256] = {
  0x0000, 0xa2eb, 0xe73d, 0x45d6, 0x6c91, 0xce7a, 0x8bac, 0x2947, 0xd922, 0x7bc9, 0x3e1f, 0x9cf4, 0xb5b3, 0x1758, 0x528e, 0xf065,
  0x10af, 0xb244, 0xf792, 0x5579, 0x7c3e, 0xded5, 0x9b03, 0x39e8, 0xc98d, 0x6b66, 0x2eb0, 0x8c5b, 0xa51c, 0x07f7, 0x4221, 0xe0ca,
  0x215e, 0x83b5, 0xc663, 0x6488, 0x4dcf, 0xef24, 0xaaf2, 0x0819, 0xf87c, 0x5a97, 0x1f41, 0xbdaa, 0x94ed, 0x3606, 0x73d0, 0xd13b,
  0x31f1, 0x931a, 0xd6cc, 0x7427, 0x5d60, 0xff8b, 0xba5d, 0x18b6, 0xe8d3, 0x4a38, 0x0fee, 0xad05, 0x8442, 0x26a9, 0x637f, 0xc194,
  0x42bc, 0xe057, 0xa581, 0x076a, 0x2e2d, 0x8cc6, 0xc910, 0x6bfb, 0x9b9e, 0x3975, 0x7ca3, 0xde48, 0xf70f, 0x55e4, 0x1032, 0xb2d9,
  0x5213, 0xf0f8, 0xb52e, 0x17c5, 0x3e82, 0x9c69, 0xd9bf, 0x7b54, 0x8b31, 0x29da, 0x6c0c, 0xcee7, 0xe7a0, 0x454b, 0x009d, 0xa276,
  0x63e2, 0xc109, 0x84df, 0x2634, 0x0f73, 0xad98, 0xe84e, 0x4aa5, 0xbac0, 0x182b, 0x5dfd, 0xff16, 0xd651, 0x74ba, 0x316c, 0x9387,
  0x734d, 0xd1a6, 0x9470, 0x369b, 0x1fdc, 0xbd37, 0xf8e1, 0x5a0a, 0xaa6f, 0x0884, 0x4d52, 0xefb9, 0xc6fe, 0x6415, 0x21c3, 0x8328,
  0x8578, 0x2793, 0x6245, 0xc0ae, 0xe9e9, 0x4b02, 0x0ed4, 0xac3f, 0x5c5a, 0xfeb1, 0xbb67, 0x198c, 0x30cb, 0x9220, 0xd7f6, 0x751d,
  0x95d7, 0x373c, 0x72ea, 0xd001, 0xf946, 0x5bad, 0x1e7b, 0xbc90, 0x4cf5, 0xee1e, 0xabc8, 0x0923, 0x2064, 0x828f, 0xc759, 0x65b2,
  0xa426, 0x06cd, 0x431b, 0xe1f0, 0xc8b7, 0x6a5c, 0x2f8a, 0x8d61, 0x7d04, 0xdfef, 0x9a39, 0x38d2, 0x1195, 0xb37e, 0xf6a8, 0x5443,
  0xb489, 0x1662, 0x53b4, 0xf15f, 0xd818, 0x7af3, 0x3f25, 0x9dce, 0x6dab, 0xcf40, 0x8a96, 0x287d, 0x013a, 0xa3d1, 0xe607, 0x44ec,
  0xc7c4, 0x652f, 0x20f9, 0x8212, 0xab55, 0x09be, 0x4c68, 0xee83, 0x1ee6, 0xbc0d, 0xf9db, 0x5b30, 0x7277, 0xd09c, 0x954a, 0x37a1,
  0xd76b, 0x7580, 0x3056, 0x92bd, 0xbbfa, 0x1911, 0x5cc7, 0xfe2c, 0x0e49, 0xaca2, 0xe974, 0x4b9f, 0x62d8, 0xc033, 0x85e5, 0x270e,
  0xe69a, 0x4471, 0x01a7, 0xa34c, 0x8a0b, 0x28e0, 0x6d36, 0xcfdd, 0x3fb8, 0x9d53, 0xd885, 0x7a6e, 0x5329, 0xf1c2, 0xb414, 0x16ff,
  0xf635, 0x54de, 0x1108, 0xb3e3, 0x9aa4, 0x384f, 0x7d99, 0xdf72, 0x2f17, 0x8dfc, 0xc82a, 0x6ac1, 0x4386, 0xe16d, 0xa4bb, 0x0650
};

// crc is computed based not on the actual image length but the image padded to a sector which is expected to be 0xFF
uint16_t crc_gen(const uint8_t * data, uint32_t len) {
  uint32_t i;
  uint16_t crc_key = 0;

  for (i = 0; i < len; i++) {
    crc_key = (uint16_t)((crc_key << 8) ^ crc16_table[((crc_key >> 8) ^ data[i]) & 0xFF]);
  }
  
  return crc_key;
}

void __attribute__((noreturn)) Boot_Entry(void) {
  // grab the app_header at APP_BASE as hdr. volatile: the copy below rewrites
  // this very memory, so the reads after it must not be hoisted above the loop
  const volatile struct app_header *hdr = (const volatile struct app_header *)APP_BASE;
  //const volatile struct crc_header *cdr = (const volatile struct crc_header *)CRC_HEADER;
  
  
  boot_disable_irq();

  // check for magic number
  /*
  const struct app_header *test_hdr = (const struct app_header *) IMAGE_BASE;
  if (   test_hdr->magic == APP_MAGIC
      && cdr->flag == CRC_FLAG_PENDING
      // slength comes back out of flash, so bound it before anything multiplies
      // it into a length: a corrupted record would otherwise run crc_gen off the
      // end of the address space, and slength < BOOT_SECTORS underflows app_len
      && cdr->slength >  BOOT_SECTORS
      && cdr->slength <= DOWNLOAD_SECTORS
      && crc_gen((const uint8_t *) DOWNLOAD_BASE, cdr->slength * FLASH_SECTOR_SIZE) == cdr->crc_key
      ) {
      
    /*
  
    // copy flash. The staged file is a whole-flash image, so its app content
    // starts BOOT_SECTORS in; the boot region itself is never overwritten.
    // slength counts the whole staged image, boot region included, so the app
    // content is BOOT_SECTORS shorter -- copying slength sectors would read off
    // the end of flash for a full-size image
    for (uint32_t sector = 0; sector < cdr->slength - BOOT_SECTORS; ++sector) {
      uint32_t dst = APP_BASE      + sector * FLASH_SECTOR_SIZE;
      uint32_t src = DOWNLOAD_BASE + (BOOT_SECTORS + sector) * FLASH_SECTOR_SIZE;

      FLASH_IAP(IAP_ERAS_SECT, dst, 0, 0);
      FLASH_IAP(IAP_PROG, dst, (uint8_t *) src, FLASH_SECTOR_SIZE);
    }
    
    // verify what landed against the source it came from. Comparing the two
    // regions needs no host value, and it is the only check that can see a
    // failed erase or a program that did not take -- IAP reports nothing.
    struct crc_header rec;
    uint32_t app_len = (cdr->slength - BOOT_SECTORS) * FLASH_SECTOR_SIZE;

    rec.slength  = cdr->slength;
    rec.crc_key = cdr->crc_key;

    if(crc_gen((const uint8_t *) APP_BASE, app_len)
       == crc_gen((const uint8_t *) IMAGE_BASE, app_len)) {
      rec.flag = CRC_FLAG_PASSED;
    } else {
      rec.flag = CRC_FLAG_FAILED;
    }

    // deletes the app header so next reboot finds no valid image
    FLASH_IAP(IAP_ERAS_SECT, IMAGE_BASE, 0, 0);

    // the flag cannot be advanced in place -- flash only clears bits and
    // PENDING is 0x00 -- so erase the record and reprogram it whole
    FLASH_IAP(IAP_ERAS_SECT, CRC_HEADER, 0, 0);
    FLASH_IAP(IAP_PROG, CRC_HEADER, (uint8_t *) &rec, sizeof rec);
    
    
  }
  */

  
  //if(hdr->magic==APP_MAGIC) {
    void (*app_entry)(void) = (void (*)(void))(hdr->entry | 1u);
    boot_enable_irq();
    app_entry();
  //}
  
  for (;;) {}
}
