/*
 * Copyright (C) 2015 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.
 */

#include <assert.h>
#include <stdio.h>

#include "glue.h"

#include "chip_gen_7400.h"

#define CHIP_(x)	chip_gen_7400_ ## x

struct cpssp {
	unsigned int state_vdd;
	unsigned int state_a[4];
	unsigned int state_b[4];
	struct sig_std_logic *port_c[4];
};

static void
CHIP_(update)(struct cpssp *cpssp, int n)
{
	unsigned int res;

	res = ! cpssp->state_a[n] & ! cpssp->state_b[n];
	res &= cpssp->state_vdd;

	sig_std_logic_set(cpssp->port_c[n], cpssp,
			res ? SIG_STD_LOGIC_1 : SIG_STD_LOGIC_0);
}

static void
CHIP_(vdd_set)(void *_cpssp, unsigned int val)
{
	struct cpssp *cpssp = _cpssp;
	int n;

	cpssp->state_vdd = 2000 < SIG_mV(val);

	for (n = 0; n < 4; n++) {
		CHIP_(update)(cpssp, n);
	}
}

static void
CHIP_(aN_set)(void *_cpssp, int n, unsigned int val)
{
	struct cpssp *cpssp = _cpssp;

	assert(0 <= n && n < 4);

	cpssp->state_a[n] = 2000 < SIG_mV(val);

	CHIP_(update)(cpssp, n);
}

static void
CHIP_(bN_set)(void *_cpssp, int n, unsigned int val)
{
	struct cpssp *cpssp = _cpssp;

	assert(0 <= n && n < 4);

	cpssp->state_b[n] = 2000 < SIG_mV(val);

	CHIP_(update)(cpssp, n);
}

void *
CHIP_(create)(
	const char *name,
	struct sig_manage *port_manage,
	struct sig_std_logic *port_a0,
	struct sig_std_logic *port_b0,
	struct sig_std_logic *port_c0,

	struct sig_std_logic *port_a1,
	struct sig_std_logic *port_b1,
	struct sig_std_logic *port_c1,

	struct sig_std_logic *port_gnd,

	struct sig_std_logic *port_c3,
	struct sig_std_logic *port_b3,
	struct sig_std_logic *port_a3,

	struct sig_std_logic *port_c2,
	struct sig_std_logic *port_b2,
	struct sig_std_logic *port_a2,

	struct sig_std_logic *port_vdd
)
{
	static const struct sig_std_logic_funcs vdd_funcs = {
		.std_logic_set = CHIP_(vdd_set),
	};
	static const struct sig_std_logic_funcs aN_funcs = {
		.std_logic_setN = CHIP_(aN_set),
	};
	static const struct sig_std_logic_funcs bN_funcs = {
		.std_logic_setN = CHIP_(bN_set),
	};
	struct cpssp *cpssp;

	cpssp = shm_alloc(sizeof(*cpssp));
	assert(cpssp);

	/* Out */
	cpssp->port_c[0] = port_c0;
	sig_std_logic_connect_out(port_c0, cpssp, SIG_STD_LOGIC_0);
	cpssp->port_c[1] = port_c1;
	sig_std_logic_connect_out(port_c1, cpssp, SIG_STD_LOGIC_0);
	cpssp->port_c[2] = port_c2;
	sig_std_logic_connect_out(port_c2, cpssp, SIG_STD_LOGIC_0);
	cpssp->port_c[3] = port_c3;
	sig_std_logic_connect_out(port_c3, cpssp, SIG_STD_LOGIC_0);

	/* In */
	sig_std_logic_connect_in(port_vdd, cpssp, &vdd_funcs);

	sig_std_logic_connect_inN(port_a0, cpssp, 0, &aN_funcs);
	sig_std_logic_connect_inN(port_b0, cpssp, 0, &bN_funcs);

	sig_std_logic_connect_inN(port_a1, cpssp, 1, &aN_funcs);
	sig_std_logic_connect_inN(port_b1, cpssp, 1, &bN_funcs);

	sig_std_logic_connect_inN(port_a2, cpssp, 2, &aN_funcs);
	sig_std_logic_connect_inN(port_b2, cpssp, 2, &bN_funcs);

	sig_std_logic_connect_inN(port_a3, cpssp, 3, &aN_funcs);
	sig_std_logic_connect_inN(port_b3, cpssp, 3, &bN_funcs);

	return cpssp;
}

void
CHIP_(destroy)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	shm_free(cpssp);
}

void
CHIP_(suspend)(void *_cpssp, FILE *fp)
{
	struct cpssp *cpssp = _cpssp;

	generic_suspend(cpssp, sizeof(*cpssp), fp);
}

void
CHIP_(resume)(void *_cpssp, FILE *fp)
{
	struct cpssp *cpssp = _cpssp;

	generic_resume(cpssp, sizeof(*cpssp), fp);
}
