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

#include "config.h"

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "glue.h"

#include "sig_cs.h"
#include "sig_mem_bus.h"

#define COMP	SNAME

/*
 * Types
 */
struct chip {
	uint8_t mem[MEM_SIZE];

	struct sig_cs *port_cs;
};

static uint8_t *
NAME_(access)(struct chip *chip, unsigned long addr)
{
	addr &= ~0xfffUL;

	return &chip->mem[addr];
}

static int
NAME_(readb)(void *_chip, uint32_t addr, uint8_t *valp)
{
	struct chip *chip = (struct chip *) _chip;
	unsigned char *va;

	addr &= MEM_SIZE - 1;

	va = NAME_(access)(chip, addr);

	*valp = *(uint8_t *) (va + addr - (addr & ~0xfffUL));

	return 0;
}

static int
NAME_(writeb)(void *_chip, uint32_t addr, uint8_t val)
{
	struct chip *chip = (struct chip *) _chip;
	unsigned char *va;

	addr &= MEM_SIZE - 1;

	va = NAME_(access)(chip, addr);

	*(uint8_t *) (va + addr - (addr & ~0xfffUL)) = val;

	return 0;
}

#if 16 <= MEM_WIDTH
static int
NAME_(readw)(void *_chip, uint32_t addr, uint16_t *valp)
{
	struct chip *chip = (struct chip *) _chip;
	unsigned char *va;

	assert((addr & 1) == 0);

	addr &= MEM_SIZE - 1;

	va = NAME_(access)(chip, addr);

	*valp = *(uint16_t *) (va + addr - (addr & ~0xfffUL));

	return 0;
}

static int
NAME_(writew)(void *_chip, uint32_t addr, uint16_t val)
{
	struct chip *chip = (struct chip *) _chip;
	unsigned char *va;

	assert((addr & 1) == 0);

	addr &= MEM_SIZE - 1;

	va = NAME_(access)(chip, addr);

	*(uint16_t *) (va + addr - (addr & ~0xfffUL)) = val;

	return 0;
}
#endif /* 16 <= MEM_WIDTH */

static int
NAME_(map_r)(
	void *_chip,
	uint32_t addr,
	int (**cfp)(void *, uint32_t, unsigned int, uint32_t *),
	void **csp,
	char **haddr_p
)
{
	fixme();
#if 0
	struct chip *chip = (struct chip *) _chip;
	unsigned long haddr;
	unsigned long laddr;

	laddr = addr & ~0xfffUL & (MEM_SIZE - 1);
	haddr = addr & ~0xfffUL & ~(MEM_SIZE - 1);

	assert(/* 0 <= laddr && */ laddr < MEM_SIZE);

	*fdp = chip->fd;
	*offsetp = chip->offset + laddr;
	*startp = haddr;
	*lenp = MEM_SIZE;
	*wflagp = 1;
#endif

	return 0;
}

static int
NAME_(map_w)(
	void *_chip,
	uint32_t addr,
	int (**cfp)(void *, uint32_t, unsigned int, uint32_t),
	void **csp,
	char **haddr_p
)
{
	fixme();
#if 0
	struct chip *chip = (struct chip *) _chip;
	unsigned long haddr;
	unsigned long laddr;

	laddr = addr & ~0xfffUL & (MEM_SIZE - 1);
	haddr = addr & ~0xfffUL & ~(MEM_SIZE - 1);

	assert(/* 0 <= laddr && */ laddr < MEM_SIZE);

	*fdp = chip->fd;
	*offsetp = chip->offset + laddr;
	*startp = haddr;
	*lenp = MEM_SIZE;
	*wflagp = 1;
#endif

	return 0;
}

static void
NAME_(power_set)(void *_chip, unsigned int val)
{
	struct chip *chip = (struct chip *) _chip;

	if (val) {
		memset(chip->mem, 0, sizeof(chip->mem));
	}
}

void *
NAME_(create)(
	const char *name,
	struct sig_manage *port_manage,
	struct sig_std_logic *port_power,
	struct sig_cs *port_cs,
	struct sig_mem_bus *port_bus
)
{
	static const struct sig_std_logic_funcs power_funcs = {
		.boolean_or_set = NAME_(power_set),
	};
	static const struct sig_cs_funcs cs_funcs = {
		.readb	= NAME_(readb),
		.writeb	= NAME_(writeb),
#if 16 <= MEM_WIDTH
		.readw	= NAME_(readw),
		.writew	= NAME_(writew),
#endif
		.map_r	= NAME_(map_r),
		.map_w	= NAME_(map_w),
	};
	struct chip *chip;

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

	sig_std_logic_connect_in(port_power, chip, &power_funcs);

	chip->port_cs = port_cs;
	sig_cs_connect(chip->port_cs, chip, &cs_funcs);

	return chip;
}

void
NAME_(destroy)(void *_cpssp)
{
	struct chip *cpssp = _cpssp;

	shm_free(cpssp);
}

void
NAME_(suspend)(void *_cpssp, FILE *fp)
{
	struct chip *cpssp = _cpssp;
	
	generic_suspend(cpssp, sizeof(*cpssp), fp);
}

void
NAME_(resume)(void *_cpssp, FILE *fp)
{
	struct chip *cpssp = _cpssp;
	
	generic_resume(cpssp, sizeof(*cpssp), fp);
}
