#include "CH32V20xxx.h"
typedef __SIZE_TYPE__ size_t;
extern "C" {
  // These magic symbols are provided by the linker.
  extern uint32_t _sbss;
  extern uint32_t _ebss;
  extern uint32_t _data_lma;
  extern uint32_t _data_vma;
  extern uint32_t _edata;

  [[gnu::weak]] extern void (*__preinit_array_start[]) (void);
  [[gnu::weak]] extern void (*__preinit_array_end[])   (void);
  [[gnu::weak]] extern void (*__init_array_start[])    (void);
  [[gnu::weak]] extern void (*__init_array_end[])      (void);
  
  [[gnu::used]] extern void SystemInit ();
};
static void init_variables () {
  uint32_t * dst, * end;

  /* Zero fill the bss section */
  dst = &_sbss;
  end = &_ebss;
  while (dst < end) * dst++ = 0U;
  /* Copy data section from flash to RAM */
  uint32_t * src;
  src = &_data_lma;
  dst = &_data_vma;
  end = &_edata;
  if (src != dst) {
    while (dst < end) * dst++ = * src++;
  }
}
static void call_constructors () {
  size_t count;
  /* Pro Cortex-Mx bylo toto zbytečné, lze předpokládat, že je to tak i zde.
  count = __preinit_array_end - __preinit_array_start;
  for (unsigned i = 0; i < count; i++)  __preinit_array_start[i]();
  */
  count = __init_array_end - __init_array_start;
  for (unsigned i = 0; i < count; i++)  __init_array_start[i]();
}

enum CLKSRC : uint32_t {
  CLK_HSI = 0u,
  CLK_HSE,
  CLK_PLL,
};
// HSE i HSI mají frekvenci 8 MHz
void SystemInit(void) {
  init_variables();
  /// TODO
  EXTEND.EXTEND_CTR.B.PLL_HSI_PRE = SET; // HSI used for PLL, not divided
  RCC.CFGR0.modify([](RCC_Type::CFGR0_DEF & r) -> uint32_t {
    r.B.PLLMUL = 0xFu;  // 8x18 = 144
    r.B.PPRE1  = 4u;    // 100: HCLK divided by 2 (PB1)
    return r.R;         // HB, PB2 not divided
  });
  RCC.CTLR.modify([](RCC_Type::CTLR_DEF & r) -> uint32_t {
    r.B.HSITRIM = 0x10u;
    r.B.HSION   = SET;
  //r.B.HSEBYP  = SET;
    r.B.CSSON   = SET;  // Enable clock security system
    r.B.PLLON   = SET;
    return r.R;
  });
  
  RCC.INTR.R = 0x009F0000u;   // clear interrupts
  while (RCC.CTLR.B.PLLRDY == RESET);
  // USE PLL
  RCC.CFGR0.B.SW          = CLK_PLL ;
  while (RCC.CFGR0.B.SWS != CLK_PLL);
  
  call_constructors();
}