/*
 * 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

/*
 * 16-bit Timer/Counter
 *
 * See: ATMEL ATmega32 doc page 86 ff.
 */

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

struct {
	unsigned int count;

	uint8_t temp;

	/* ??? */
	uint16_t tcnt;

	/* TCCRxA 107 */
	uint8_t comB; /* 7-6: Compare Output Mode for Compare Unit A */
	uint8_t comA; /* 5-4: Compare Output Mode for Compare Unit B */
	uint8_t focA; /* 3: ... */
	uint8_t focB; /* 2: ... */
	uint8_t wgm; /* 1-0: ... */

	/* TCCRxB 110 */
	uint8_t cs; /* 2-0: Clock Select */
	/* uint8_t wgm; See above. */

	/* OCRxAH 111 */
	/* OCRxAL 111 */
	uint16_t ocrA;

	/* OCRxBH 111 */
	/* OCRxBL 111 */
	uint16_t ocrB;

	/* TIMSK 112 */
	uint8_t icie;
	uint8_t ocieA;
	uint8_t ocieB;
	uint8_t toie;

	/* TIFR 112 */
	uint8_t icf;
	uint8_t ocfA;
	uint8_t ocfB;
	uint8_t tov;
} NAME;

#endif /* STATE */
#ifdef EXPORT

/*forward*/ static void
NAME_(tccrXa_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(tccrXb_set)(struct cpssp *cpssp, uint8_t val);
#if defined(CONFIG_ATMEGA328PB)
/*forward*/ static void
NAME_(tccrXc_set)(struct cpssp *cpssp, uint8_t val);
#endif
/*forward*/ static void
NAME_(tcntXh_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(tcntXl_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(ocrXah_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(ocrXal_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(ocrXbh_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(ocrXbl_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(icrXh_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(icrXl_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(icie_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(ocieA_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(ocieB_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(toie_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void                                       
NAME_(icf_set)(struct cpssp *cpssp, uint8_t val);             
/*forward*/ static void                                       
NAME_(ocfA_set)(struct cpssp *cpssp, uint8_t val);            
/*forward*/ static void                                       
NAME_(ocfB_set)(struct cpssp *cpssp, uint8_t val);            
/*forward*/ static void                                       
NAME_(tov_set)(struct cpssp *cpssp, uint8_t val);             
/*forward*/ static void                                       
NAME_(tccrXa_get)(struct cpssp *cpssp, uint8_t *valp);        
/*forward*/ static void                                       
NAME_(tccrXb_get)(struct cpssp *cpssp, uint8_t *valp);        
#if defined(CONFIG_ATMEGA328PB)                               
/*forward*/ static void                                       
NAME_(tccrXc_get)(struct cpssp *cpssp, uint8_t *valp);        
#endif
/*forward*/ static void                                       
NAME_(tcntXh_get)(struct cpssp *cpssp, uint8_t *valp);        
/*forward*/ static void                                       
NAME_(tcntXl_get)(struct cpssp *cpssp, uint8_t *valp);        
/*forward*/ static void                                       
NAME_(ocrXah_get)(struct cpssp *cpssp, uint8_t *valp);        
/*forward*/ static void                                       
NAME_(ocrXal_get)(struct cpssp *cpssp, uint8_t *valp);        
/*forward*/ static void                                       
NAME_(ocrXbh_get)(struct cpssp *cpssp, uint8_t *valp);        
/*forward*/ static void                                       
NAME_(ocrXbl_get)(struct cpssp *cpssp, uint8_t *valp);        
/*forward*/ static void                                       
NAME_(icrXh_get)(struct cpssp *cpssp, uint8_t *valp);         
/*forward*/ static void                                       
NAME_(icrXl_get)(struct cpssp *cpssp, uint8_t *valp);         
/*forward*/ static void                                       
NAME_(icie_get)(struct cpssp *cpssp, uint8_t *valp);          
/*forward*/ static void                                       
NAME_(ocieA_get)(struct cpssp *cpssp, uint8_t *valp);         
/*forward*/ static void                                       
NAME_(ocieB_get)(struct cpssp *cpssp, uint8_t *valp);         
/*forward*/ static void                                       
NAME_(toie_get)(struct cpssp *cpssp, uint8_t *valp);          
/*forward*/ static void                                       
NAME_(icf_get)(struct cpssp *cpssp, uint8_t *valp);           
/*forward*/ static void                                       
NAME_(ocfA_get)(struct cpssp *cpssp, uint8_t *valp);          
/*forward*/ static void                                       
NAME_(ocfB_get)(struct cpssp *cpssp, uint8_t *valp);          
/*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

#if 0
	NAME_(irq_set)(cpssp, n, val);
#endif

static void
NAME_(update)(struct cpssp *cpssp)
{
	NAME_(irq_set)(cpssp, 0, cpssp->NAME.icf & cpssp->NAME.icie);
	NAME_(irq_set)(cpssp, 1, cpssp->NAME.ocfA & cpssp->NAME.ocieA);
	NAME_(irq_set)(cpssp, 2, cpssp->NAME.ocfB & cpssp->NAME.ocieB);
	NAME_(irq_set)(cpssp, 3, cpssp->NAME.tov & cpssp->NAME.toie);
}

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

	cpssp->NAME.comB = (val >> 7) & 0x3;
	cpssp->NAME.comA = (val >> 5) & 0x3;
	cpssp->NAME.focA = (val >> 3) & 0x1;
	cpssp->NAME.focB = (val >> 2) & 0x1;
	cpssp->NAME.wgm &= ~3;
	cpssp->NAME.wgm |= (val >> 0) & 0x3;
}

static void
NAME_(tccrXa_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* TCCRxA 107 */
	*valp = 0;
	*valp |= cpssp->NAME.comA << 6;
	*valp |= cpssp->NAME.comB << 4;
	*valp |= cpssp->NAME.focA << 3;
	*valp |= cpssp->NAME.focB << 2;
	*valp |= ((cpssp->NAME.wgm >> 0) & 0x3) << 0;

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

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

	/* FIXME */
	cpssp->NAME.wgm &= ~0xc;
	cpssp->NAME.wgm |= ((val >> 3) & 0x3) << 2;
	cpssp->NAME.cs = (val >> 0) & 0x7;
}

static void
NAME_(tccrXb_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* TCCRxB 110 */
	*valp = 0;
	/* FIXME */
	*valp |= 0x0 << 5; /* Reserved */
	*valp |= ((cpssp->NAME.wgm >> 2) & 0x3) << 3;
	*valp |= cpssp->NAME.cs << 0;

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

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

	assert(0); /* FIXME */
}

static void
NAME_(tccrXc_get)(struct cpssp *cpssp, uint8_t *valp)
{
	assert(0); /* FIXME */

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

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

	cpssp->NAME.temp = val;
}

static void
NAME_(tcntXh_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* TCNTxH */
	*valp = cpssp->NAME.temp;

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

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

	cpssp->NAME.tcnt = (cpssp->NAME.temp << 8) | (val << 0);
}

static void
NAME_(tcntXl_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* TCNTxL */
	cpssp->NAME.temp = (cpssp->NAME.tcnt >> 8) & 0xff;
	*valp = (cpssp->NAME.tcnt >> 0) & 0xff;

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

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

	cpssp->NAME.temp = val;
}

static void
NAME_(ocrXah_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* OCRxAH */
	*valp = cpssp->NAME.temp;

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

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

	cpssp->NAME.ocrA = (cpssp->NAME.temp << 8) | (val << 0);
}

static void
NAME_(ocrXal_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* OCRxAL */
	cpssp->NAME.temp = (cpssp->NAME.ocrA >> 8) & 0xff;
	*valp = (cpssp->NAME.ocrA >> 0) & 0xff;

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

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

	cpssp->NAME.temp = val;
}

static void
NAME_(ocrXbh_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* OCRxBH */
	*valp = cpssp->NAME.temp;

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

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

	cpssp->NAME.ocrB = (cpssp->NAME.temp << 8) | (val << 0);
}

static void
NAME_(ocrXbl_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* OCRxBL */
	cpssp->NAME.temp = (cpssp->NAME.ocrB >> 8) & 0xff;
	*valp = (cpssp->NAME.ocrB >> 0) & 0xff;

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

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

	assert(0); /* FIXME */
}

static void
NAME_(icrXl_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* ICRxL */
	assert(0); /* FIXME */

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

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

	assert(0); /* FIXME */
}

static void
NAME_(icrXh_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* ICRxH */
	assert(0); /* FIXME */

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

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

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

static void
NAME_(icie_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* ICIE */
	*valp = cpssp->NAME.icie;

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

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

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

static void
NAME_(ocieA_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* OCIEA */
	*valp = cpssp->NAME.ocieA;

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

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

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

static void
NAME_(ocieB_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* OCIEB */
	*valp = cpssp->NAME.ocieB;

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

static void
NAME_(toie_set)(struct cpssp *cpssp, uint8_t val)
{
	/* TOIE */
	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 */
	*valp = cpssp->NAME.toie;

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

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

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

static void
NAME_(icf_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* ICF */
	*valp = cpssp->NAME.icf;

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

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

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

static void
NAME_(ocfA_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* OCFA */
	*valp = cpssp->NAME.ocfA;

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

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

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

static void
NAME_(ocfB_get)(struct cpssp *cpssp, uint8_t *valp)
{
	/* OCFB */
	*valp = cpssp->NAME.ocfB;

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

static void
NAME_(tov_set)(struct cpssp *cpssp, uint8_t val)
{
	/* TOV */
	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 */
	*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);
	}

	switch (n) {
	case 0:
		cpssp->NAME.icf = 0;
		break;
	case 1:
		cpssp->NAME.ocfA = 0;
		break;
	case 2:
		cpssp->NAME.ocfB = 0;
		break;
	case 3:
		cpssp->NAME.tov = 0;
		break;
	default:
		assert(0); /* Mustn't happen. */
	}
	NAME_(update)(cpssp);
}

static void
NAME_(tick)(struct cpssp *cpssp)
{
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s\n", __FUNCTION__);
	}

	cpssp->NAME.tcnt++;

	switch (cpssp->NAME.wgm) {
	case 0x0:
	case 0x2:
	case 0x3:
		goto todo;

	case 0x4: /* CTC using OCRA. */
		if (cpssp->NAME.tcnt == cpssp->NAME.ocrA) {
			cpssp->NAME.ocfA = 1;
			NAME_(update)(cpssp);
			cpssp->NAME.tcnt = 0;
		}
		break;

	case 0x5:
	case 0x6:
	case 0x7:
	case 0x8:
	case 0x9:
	case 0xa:
	case 0xb:
	case 0xc:
	case 0xd:
	case 0xe:
	case 0xf:
	todo:	;
		fprintf(stderr, "WARNING: %s: wgm=0x%x\n", __FUNCTION__,
				cpssp->NAME.wgm);
		break;
	default:
		assert(0); /* Cannot happen. */
	}
}

static void
NAME_(step)(struct cpssp *cpssp)
{
	unsigned int end;

	switch (cpssp->NAME.cs) {
	case 1: /* clk / 1 */
		end = 1 << 0;
		break;
	case 2: /* clk / 8 */
		end = 1 << 3;
		break;
	case 3: /* clk / 64 */
		end = 1 << 6;
		break;
	case 4: /* clk / 256 */
		end = 1 << 8;
		break;
	case 5:
		/* clk / 1024 */
		end = 1 << 10;
		break;
	case 0: /* No clock source. */
	case 6: /* External clock source (Falling edge). */
	case 7: /* External clock source (Raising edge). */
		end = 0;
		break;
	default:
		assert(0); /* Cannot happen. */
	}

	if (end) {
		cpssp->NAME.count++;
		if ((cpssp->NAME.count & (end - 1)) == 0) {
			NAME_(tick)(cpssp);
			cpssp->NAME.count = 0;
		}
	}
}

static void
NAME_(reset)(struct cpssp *cpssp)
{
	cpssp->NAME.count = 0;

	cpssp->NAME.tcnt = 0x0000;
	cpssp->NAME.cs = 0;
	cpssp->NAME.temp = 0x00;

	/* TIMSK 112 */
	cpssp->NAME.icie = 0;
	cpssp->NAME.ocieA = 0;
	cpssp->NAME.ocieB = 0;
	cpssp->NAME.toie = 0;

	/* TIFR 112 */
	cpssp->NAME.icf = 0;
	cpssp->NAME.ocfA = 0;
	cpssp->NAME.ocfB = 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
