/*
 * Copyright (C) 2007-2012 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 <inttypes.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#include "glue.h"

#include "chip_commodore_6510.h"

#define CHIP_(x)	chip_commodore_6510_ ## x

struct cpssp {
	/* Config */

	/* Ports */
	struct sig_isa_bus *bus;

	/* Signals */

	/* State */
	bool state_power;
	bool state_n_reset;
	bool state_nmi;
	bool state_interrupt_pending;

#define STATE
#define NAME		core
#define NAME_(x)	core_ ## x
#include "arch_commodore_6510.c"
#undef NAME_
#undef NAME
#undef STATE

	/* Processes */
	struct process process;
};

static uint8_t
CHIP_(mrb)(struct cpssp *cpssp, uint16_t addr)
{
	uint8_t val;

	sig_isa_bus_readb(cpssp->bus, cpssp, addr, &val);
	return val;
}

static void
CHIP_(mwb)(struct cpssp *cpssp, uint16_t addr, uint8_t val)
{
	sig_isa_bus_writeb(cpssp->bus, cpssp, addr, val);
}

#define BEHAVIOR
#define NAME		core
#define NAME_(x)	core_ ## x
#include "arch_commodore_6510.c"
#undef NAME_
#undef NAME
#undef BEHAVIOR

static void __attribute__((__noreturn__))
CHIP_(step)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

again:	;
	sched_to_scheduler();

	while (cpssp->process.inst_cnt < cpssp->process.inst_limit) {
		if (! cpssp->state_power) {
			cpssp->process.inst_cnt = cpssp->process.inst_limit;

		} else {
			core_step(cpssp);
			cpssp->process.inst_cnt += 1;
		}
	}
	goto again;
}

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

	cpssp->state_power = val;
}

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

	cpssp->state_n_reset = val;
}

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

	cpssp->state_nmi = val;
}

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

	cpssp->state_interrupt_pending = val;
}

void *
CHIP_(create)(
	const char *name,
	struct sig_manage *port_manage,
	struct sig_boolean *port_power,
	struct sig_boolean *port_n_reset,
	struct sig_boolean *port_nmi,
	struct sig_boolean *port_irq,
	struct sig_isa_bus *port_bus
)
{
	static const struct sig_boolean_funcs power_func = {
		.set = CHIP_(power_set),
	};
	static const struct sig_boolean_funcs n_reset_func = {
		.set = CHIP_(n_reset_set),
	};
	static const struct sig_boolean_funcs nmi_func = {
		.set = CHIP_(nmi_set),
	};
	static const struct sig_boolean_funcs irq_func = {
		.set = CHIP_(irq_set),
	};
	struct cpssp *cpssp;

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

	memset(cpssp, 0, sizeof(*cpssp));

	cpssp->process.inst_hz = 1*1000;

	cpssp->bus = port_bus;

	sig_boolean_connect_in(port_power, cpssp, &power_func);
	sig_boolean_connect_in(port_n_reset, cpssp, &n_reset_func);
	sig_boolean_connect_in(port_nmi, cpssp, &nmi_func);
	sig_boolean_connect_in(port_irq, cpssp, &irq_func);

	sched_process_init(&cpssp->process, CHIP_(step), cpssp);

	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);
}
