// (C) 2020 Philip Endecott.
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENCE.txt or copy at https://www.boost.org/LICENSE_1_0.txt

#include <array>
#include <cstdint>

#include "arm.hh"
#include "registers.hh"
#include "uart.hh"
#include "rtc.hh"
#include "adc.hh"
#include "cpu.hh"
#include "circular_buffer.hh"


#if 0
static void setup_gpio()
{
  *IO0DIR = 0xffffffff;  // 1 = output; default 0.
}

static void set_gpio(uint32_t v)
{
  *IO0PIN = v << 4;
}


volatile int dbg_count = 0;

static void dbgok()
{
  ++dbg_count;
  set_gpio(dbg_count & 1);
}

static void dbgbad()
{
  ++dbg_count;
  set_gpio(2 | (dbg_count&1));
}
#endif


volatile bool irqed = false;

circular_buffer<char,16> rcv_buf;


__attribute__((interrupt("IRQ")))
__attribute__((target("arm")))
void uart0_irq_handler()
{
  *VICVectAddr = 1;
  auto ign = *U0IIR;  // Clear the interrupt.  (hmm.)
  while (uart0_readable()) {
    auto c = uart0_rcv();
    rcv_buf.push_back(c);
  }
  irqed = true;
}


__attribute__((interrupt("IRQ")))
__attribute__((target("arm")))
void rtc_irq_handler()
{
  *VICVectAddr = 1;
  *ILR = 3;  // Clear the interrupt(s).
  irqed = true;
}


__attribute__((interrupt("IRQ")))
__attribute__((target("arm")))
void default_irq_handler()
{
  //dbgbad();
}


constexpr int vic_uart0_num = 6;
constexpr int vic_uart1_num = 7;
constexpr int vic_rtc_num   = 13;
constexpr int vic_adc_num   = 18;

static void setup_vic()
{
  *VICIntSelect = 0;  // No FIQs.
  VICvectCntl[0] = vic_rtc_num | (1<<5);  // Interrupt number and enable bit.
  VICVectAddrs[0] = (uint32_t) &rtc_irq_handler;
  VICvectCntl[1] = vic_uart0_num | (1<<5);
  VICVectAddrs[1] = (uint32_t) &uart0_irq_handler;
  *VICDefVectAddr = (uint32_t) &default_irq_handler;

  // Enable last.
  *VICIntEnable = (1 << vic_rtc_num)
                | (1 << vic_uart0_num);
// Note that writing to this register apparently sets bits but 
// doesn't clear them; to clear bits, write ones to IntEnClear.
}



__attribute__((target("arm")))
int main(int argc, char* argv[])
{
  setup_memmap();
  setup_mam();

  // Peripheral power.
  // Enable ADC, RTC, UART0.
  //         10987654321098765432109876543210
  *PCONP = 0b00000000000000000001001000001000;

  // Pin configuration.
  // Pins default to GPIOs.
  *PINSEL0 = (0b01 << 0)  // P0.0 is UART0 TXD
           | (0b01 << 2)  // P0.1 is UART0 RXD
           ;
  *PINSEL1 = (0b01 << 22)  // P0.27 is AIN0
           | (0b01 << 24)  // P0.28 is AIN1
           | (0b01 << 26)  // P0.29 is AIN2
           | (0b01 << 28)  // P0.30 is AIN3
           ;
  *PINSEL2 = 0;

//  setup_gpio();
  setup_rtc();
  setup_vic();  // Before or after peripheral setup?
  setup_uart0<9600>();
  setup_adc();
  uart0_send( char('a'),
         ' ', "Hello World",
         ' ', uint16_t(64000),
         ' ', int16_t(32000),
         ' ', int16_t(-32000),
         ' ', uint32_t(1234567890),
         ' ', int32_t(987654321),
         ' ', int32_t(-987654321),
         ' ', uint64_t(123456789123456),
         ' ', int64_t(9876543219876),
         ' ', int64_t(-9876543219876),
         '\n');

  *CIIR = 1;  // Set IMSEC bit, interrupt every second.
  enable_irq();

  while (1) {
    while (!irqed) { idle(); }
    irqed = false;
    while (!rcv_buf.empty()) {
      uart0_send(rcv_buf.front());
      rcv_buf.pop_front();
    }
    uart0_send(static_cast<int>(rtc_read_hours()), ':',
               static_cast<int>(rtc_read_minutes()), ':',
               static_cast<int>(rtc_read_seconds()), '\n');
    uart0_send("ADC = ", adc_read(0), '\n');
  }

  while (1) {}
}


