/*
 * Copyright (C) 2016-2017 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#define DEBUG_CONTROL_FLOW	0

/*
 * 8-bit Timer/Counter
 *
 * See: ATMEL ATmega32 doc page 67 ff.
 */

#ifdef INCLUDE
#endif /* INCLUDE */
#ifdef STATE

struct {
	uint16_t count;

	/* TCNT */
	uint8_t cnt;

	/* OCR */
	uint8_t ocr[CONFIG_COMPARE];

	/* TCCR */
	uint8_t com[CONFIG_COMPARE];
	uint8_t foc[CONFIG_COMPARE];
	uint8_t wgm;
	uint8_t cs;

	/* TIMSK 82 */
	uint8_t ocie[CONFIG_COMPARE];
	uint8_t toie;

	/* TIFR 83 */
	uint8_t ocf[CONFIG_COMPARE];
	uint8_t tov;
} NAME;

#endif /* STATE */
#ifdef EXPORT

/*forward*/ static void
NAME_(com_set)(struct cpssp *cpssp, int n, uint8_t val);
/*forward*/ static void
NAME_(com_get)(struct cpssp *cpssp, int n, uint8_t *valp);

/*forward*/ static void
NAME_(wgm_set)(struct cpssp *cpssp, int n, uint8_t val);
/*forward*/ static void
NAME_(wgm_get)(struct cpssp *cpssp, int n, uint8_t *valp);

/*forward*/ static void
NAME_(foc_set)(struct cpssp *cpssp, int n, uint8_t val);
/*forward*/ static void
NAME_(foc_get)(struct cpssp *cpssp, int n, uint8_t *valp);

/*forward*/ static void
NAME_(cs_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(cs_get)(struct cpssp *cpssp, uint8_t *valp);

/*forward*/ static void
NAME_(tcnt_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(tcnt_get)(struct cpssp *cpssp, uint8_t *valp);

/*forward*/ static void
NAME_(ocr_set)(struct cpssp *cpssp, int n, uint8_t val);
/*forward*/ static void
NAME_(ocr_get)(struct cpssp *cpssp, int n, uint8_t *valp);

/*forward*/ static void
NAME_(ocie_set)(struct cpssp *cpssp, int n, uint8_t val);
/*forward*/ static void
NAME_(ocie_get)(struct cpssp *cpssp, int n, uint8_t *valp);

/*forward*/ static void
NAME_(toie_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(toie_get)(struct cpssp *cpssp, uint8_t *valp);

/*forward*/ static void
NAME_(ocf_set)(struct cpssp *cpssp, int n, uint8_t val);
/*forward*/ static void
NAME_(ocf_get)(struct cpssp *cpssp, int n, uint8_t *valp);

/*forward*/ static void
NAME_(tov_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(tov_get)(struct cpssp *cpssp, uint8_t *valp);

/*forward*/ static void
NAME_(ack)(struct cpssp *cpssp, unsigned int n);
/*forward*/ static void
NAME_(step)(struct cpssp *cpssp);

#endif /* EXPORT */
#ifdef BEHAVIOR

static void
NAME_(update)(struct cpssp *cpssp)
{
	int n;

	for (n = 0; n < CONFIG_COMPARE; n++) {
		NAME_(irq_set)(cpssp, n, cpssp->NAME.ocf[n] & cpssp->NAME.ocie[n]);
	}
	NAME_(irq_set)(cpssp, CONFIG_COMPARE, cpssp->NAME.tov & cpssp->NAME.toie);
}

static void
NAME_(com_set)(struct cpssp *cpssp, int n, uint8_t val)
{
	/* TCCR 80 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, val);
	}

	cpssp->NAME.com[n] = val;
}

static void
NAME_(com_get)(struct cpssp *cpssp, int n, uint8_t *valp)
{
	/* TCCR 80 */
	*valp = cpssp->NAME.com[n];

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, *valp);
	}
}

static void
NAME_(wgm_set)(struct cpssp *cpssp, int n, uint8_t val)
{
	/* TCCR 80 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, val);
	}

	cpssp->NAME.wgm &= ~(1 << n);
	cpssp->NAME.wgm |= val << n;
}

static void
NAME_(wgm_get)(struct cpssp *cpssp, int n, uint8_t *valp)
{
	/* TCCR 80 */
	*valp = (cpssp->NAME.wgm >> n) & 1;

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, *valp);
	}
}

static void
NAME_(foc_set)(struct cpssp *cpssp, int n, uint8_t val)
{
	/* TCCR 80 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, val);
	}

	cpssp->NAME.foc[n] = val;
}

static void
NAME_(foc_get)(struct cpssp *cpssp, int n, uint8_t *valp)
{
	/* TCCR 80 */
	*valp = cpssp->NAME.foc[n];

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, *valp);
	}
}

static void
NAME_(cs_set)(struct cpssp *cpssp, uint8_t val)
{
	/* TCCR 80 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=%02x\n", __FUNCTION__, val);
	}

	cpssp->NAME.cs = val;
}

static void
NAME_(cs_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* TCCR 80 */
	*valp = cpssp->NAME.cs;

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=%02x\n", __FUNCTION__, *valp);
	}
}

static void
NAME_(tcnt_set)(struct cpssp *cpssp, uint8_t val)
{
	/* TCNT 82 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=%02x\n", __FUNCTION__, val);
	}

	cpssp->NAME.cnt = val;
}

static void
NAME_(tcnt_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* TCNT 82 */
	*valp = cpssp->NAME.cnt;

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=%02x\n", __FUNCTION__, *valp);
	}
}

static void
NAME_(ocr_set)(struct cpssp *cpssp, int n, uint8_t val)
{
	/* OCR 82 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, val);
	}

	cpssp->NAME.ocr[n] = val;
}

static void
NAME_(ocr_get)(struct cpssp *cpssp, int n, uint8_t *valp)
{
	/* OCR 82 */
	*valp = cpssp->NAME.ocr[n];

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, *valp);
	}
}

/*
 * Parts of TIMSK.
 */
static void
NAME_(ocie_set)(struct cpssp *cpssp, int n, uint8_t val)
{
	/* OCIE 82 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, val);
	}

	cpssp->NAME.ocie[n] = val;
	NAME_(update)(cpssp);
}

static void
NAME_(ocie_get)(struct cpssp *cpssp, int n, uint8_t *valp)
{
	/* OCIE 82 */
	*valp = cpssp->NAME.ocie[n];

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, *valp);
	}
}

static void
NAME_(toie_set)(struct cpssp *cpssp, uint8_t val)
{
	/* TOIE 82 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=%02x\n", __FUNCTION__, val);
	}

	cpssp->NAME.toie = val;
	NAME_(update)(cpssp);
}

static void
NAME_(toie_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* TOIE 82 */
	*valp = cpssp->NAME.toie;

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=%02x\n", __FUNCTION__, *valp);
	}
}

/*
 * Parts of TIFR.
 */
static void
NAME_(ocf_set)(struct cpssp *cpssp, int n, uint8_t val)
{
	/* OCF 83 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, val);
	}

	cpssp->NAME.ocf[n] &= ~val; /* Write-Clear */
	NAME_(update)(cpssp);
}

static void
NAME_(ocf_get)(struct cpssp *cpssp, int n, uint8_t *valp)
{
	/* OCF 83 */
	*valp = cpssp->NAME.ocf[n];

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%02x\n", __FUNCTION__, n, *valp);
	}
}

static void
NAME_(tov_set)(struct cpssp *cpssp, uint8_t val)
{
	/* TOV 83 */
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=%02x\n", __FUNCTION__, val);
	}

	cpssp->NAME.tov &= ~val; /* Write-Clear */
	NAME_(update)(cpssp);
}

static void
NAME_(tov_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* TOV 83 */
	*valp = cpssp->NAME.tov;

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=%02x\n", __FUNCTION__, *valp);
	}
}

static void
NAME_(ack)(struct cpssp *cpssp, unsigned int n)
{
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%u\n", __FUNCTION__, n);
	}

	if (n < CONFIG_COMPARE) {
		cpssp->NAME.ocf[n] = 0;
	} else {
		cpssp->NAME.tov = 0;
	}
	NAME_(update)(cpssp);
}

static void
NAME_(step2)(struct cpssp *cpssp)
{
	int n;

	cpssp->NAME.cnt++;
	if (cpssp->NAME.cnt == 0) {
		cpssp->NAME.tov = 1;
	}
	for (n = 0; n < CONFIG_COMPARE; n++) {
		if (cpssp->NAME.ocr[n] == cpssp->NAME.cnt) {
			cpssp->NAME.ocf[n] = 1;
		}
	}
	NAME_(update)(cpssp);
}

static void
NAME_(step)(struct cpssp *cpssp)
{
	static const uint16_t prescale[8] = {
		0, /* Not used. */
		1, 8, 32, 64, 128, 256, 1024,
	};

	if (cpssp->NAME.cs != 0) {
		cpssp->NAME.count++;
		if (cpssp->NAME.count == prescale[cpssp->NAME.cs]) {
			cpssp->NAME.count = 0;
			NAME_(step2)(cpssp);
		}
	}
}

static void
NAME_(reset)(struct cpssp *cpssp)
{
	int n;

	/* TIMSK 82 */
	for (n = 0; n < CONFIG_COMPARE; n++) {
		cpssp->NAME.ocie[n] = 0;
	}
	cpssp->NAME.toie = 0;

	/* TIFR 83 */
	for (n = 0; n < CONFIG_COMPARE; n++) {
		cpssp->NAME.ocf[n] = 0;
	}
	cpssp->NAME.tov = 0;
}

static void
NAME_(create)(struct cpssp *cpssp)
{
	NAME_(reset)(cpssp);
}

static void
NAME_(destroy)(struct cpssp *cpssp)
{
}

#endif /* BEHAVIOR */

#undef DEBUG_CONTROL_FLOW
