#include "all-headers.h"

// Configure systick

static void startSystick (BOOT_MODE) {
	// Configure Systick
	SYST_RVR = CPU_MHZ * 1000 - 1 ; // Underflow every ms
	SYST_CVR = 0 ;
	SYST_CSR = SYST_CSR_CLKSOURCE | SYST_CSR_ENABLE ;
	// Configure and chain PIT0 and PIT1 for 64-bit counting
	// Power on PIT
	SIM_SCGC6 |= SIM_SCGC6_PIT ;
	// Enable PIT module
	PIT_MCR = 0 ;
	// Disable PIT0 and PIT1
	PIT_TCTRL (0) = 0 ;
	PIT_TCTRL (1) = 0 ;
	// PIT0 and PIT1 down-count: initialize them with all 1's
	PIT_LDVAL (0) = UINT32_MAX ;
	PIT_LDVAL (1) = UINT32_MAX ;
	// Enable PIT0 and PIT1: start counting, chain PI1 to PIT0, no interrupt
	PIT_TCTRL (1) = PIT_TCTRL_CHN | PIT_TCTRL_TEN ;
	PIT_TCTRL (0) = PIT_TCTRL_TEN ;
}


MACRO_BOOT_ROUTINE (startSystick) ;


static void activateSystickInterrupt (INIT_MODE) {
	SYST_CSR |= SYST_CSR_TICKINT ;
}


MACRO_INIT_ROUTINE (activateSystickInterrupt) ;

// micros current value

uint64_t section_micros (SECTION_MODE) {
	// To obtain the correct value, first read LTMR64H and then LTMR64L
	uint64_t result = PIT_LTMR64H ;
	result <<= 32 ;
	result |= PIT_LTMR64L ;
	// PIT0 and PIT1 actually downcount
	result = ~ result ;
	// Divide by the clock frequency in MHz for getting microsecond count
	return result / busMHZ () ;
}

// busyWaitDuring — INIT MODE

void busyWaitDuring_initMode (INIT_MODE_ const uint32_t inDelayMS) {
	const uint32_t COUNTFLAG_MASK = 1 << 16 ;
	for (uint32_t i=0 ; i<inDelayMS ; i++) {
		while ((SYST_CSR & COUNTFLAG_MASK) == 0) {} // Busy wait, polling COUNTFLAG
	}
}

// Configure systick — FAULT MODE

void configureSystick_faultMode (FAULT_MODE) {
	// Configure Systick
	SYST_CSR = 0 ; // Stop systick
	SYST_RVR = CPU_MHZ * 1000 - 1 ; // Underflow every ms
	SYST_CVR = 0 ;
	SYST_CSR = SYST_CSR_CLKSOURCE | SYST_CSR_ENABLE ;
}

// busyWaitDuring — FAULT MODE

void busyWaitDuring_faultMode (FAULT_MODE_ const uint32_t inDelayMS) {
	const uint32_t COUNTFLAG_MASK = 1 << 16 ;
	for (uint32_t i=0 ; i<inDelayMS ; i++) {
		while ((SYST_CSR & COUNTFLAG_MASK) == 0) {} // Busy wait, polling COUNTFLAG
	}
}

// millis — ANY MODE

static volatile uint32_t gUptime ;


uint32_t millis (ANY_MODE) {
	return gUptime ;
}

// systick — ANY MODE

uint32_t systick (ANY_MODE) {
	return SYST_CVR ;
}

// SYSTICK interrupt service routine

void systickInterruptServiceRoutine (SECTION_MODE) {
	const uint32_t newUptime = gUptime + 1 ;
	gUptime = newUptime ;
	// Run real.time.interrupt.routine.array section routines
	extern void (* __real_time_interrupt_routine_array_start) (SECTION_MODE_ const uint32_t inUptime) ;
	extern void (* __real_time_interrupt_routine_array_end) (SECTION_MODE_ const uint32_t inUptime) ;
	void (* * ptr) (SECTION_MODE_ const uint32_t) = & __real_time_interrupt_routine_array_start ;
	while (ptr != & __real_time_interrupt_routine_array_end) {
		(* ptr) (MODE_ newUptime) ;
		ptr ++ ;
	}
}

// busyWaitDuring, busyWaitUntil — USER MODE

void busyWaitDuring (USER_MODE_ const uint32_t inDelayMS) {
	busyWaitUntil (MODE_ gUptime + inDelayMS) ;
}


void busyWaitUntil (USER_MODE_ const uint32_t inDeadlineMS) {
	while (inDeadlineMS > gUptime) {}
}