/* Copyright (c) 1994-2000 Begemot Computer Associates.
 * Copyright (c) 2000-2002 by Hartmut Brandt
 *	All rights reserved.
 *
 * Redistribution of this software and documentation and use in source and
 * binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *
 * 1. Redistributions of source code or documentation must retain the above
 *    copyright notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *
 * This product includes software and documentation developed by the
 * Hartmut Brandt.
 *
 * 4. The name of the author or its contributors may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR FOKUS
 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: dev_mr.c 501 2002-09-04 13:03:28Z hbb $
 *
 * Author: Harti Brandt <brandt@fokus.gmd.de>
 *
 * MRV12 option
 *
 * this emulates an MRV12 boot prom including maintenance, configuration
 * and display registers.
 *
 * You can only define a controller, no devices.
 *
 * Arguments:
 *	ctrl mr <csr-address> <prom-file>
 */
#include "proc.h"

RCSID("$Id: dev_mr.c 501 2002-09-04 13:03:28Z hbb $")

#define LED
#define D(C)

#define BOOTSIZE	8192

typedef struct boot boot_t;

struct boot {
	u_int	csr_base;
	u_int	page0;
	u_int	page1;
	u_int	maint;
	u_int	config;
	u_int	display;
	u_short	prog[BOOTSIZE];
	char	*fname;
};

static void	boot_ctrl_create(iodev_t *, int, char **);
static void	boot_dev_create(iodev_t *, int, char **);
static void	boot_ctrl_complete(iodev_t *);
static void	boot_reset(iodev_t *);
static u_short	boot_fetch(iodev_t *, u_int);
static void	boot_store(iodev_t *, u_int, mmode_t, u_short);
static void	boot_info(iodev_t *);

static int	bootc_info(iodev_t *dev, int argc, char **argv);
static int	bootc_load(iodev_t *dev, int argc, char **argv);

iocmd_t boot_cmds[] = {
	{ "?",		"[command ...]",	dev_help },
	{ "help",	"[command ...]",	dev_help },
	{ "info",	"",			bootc_info },
	{ "l[oad]",	"file",			bootc_load },
	{ NULL,		NULL,			NULL }
};

ioops_t mr_ops = {
	boot_ctrl_create,	/* ctrl_create */
	boot_dev_create,	/* dev_create */
	boot_ctrl_complete,	/* ctrl_complete */
	boot_reset,		/* reset */
	boot_fetch,		/* fetch */
	boot_store,		/* store */
	0,			/* vector */
	0,			/* dma */
	0,			/* async */
	boot_info,		/* info */
	0,			/* flush */
	boot_cmds,		/* cmds */
};

enum {
	MR_Page		= 00,
	MR_Maint	= 02,
	MR_Disp		= 04,
	MR_Page0	= 017773000,
	MR_Page1	= 017765000,
	MR_PageSize	= 000001000,
	MR_SizeMask	= 000000776,
};

static void
boot_ctrl_create(iodev_t *dev, int argc, char **argv)
{
	boot_t	*d;
	int	i, fd;
	struct stat statb;
	char *fn;

	if (argc != 2)
		conf_panic("mr: bad args in configuration");

	d = dev->data = xalloc(sizeof(boot_t));
	(void)memset(d, 0, sizeof(boot_t));

	d->csr_base = parse_csr(argv[0], "mr");

	for (i = MR_Page0; i < MR_Page0 + MR_PageSize; i += 2)
		proc.iopage[IOP(i)] = dev;
	for (i = MR_Page1; i < MR_Page1 + MR_PageSize; i += 2)
		proc.iopage[IOP(i)] = dev;
	proc.iopage[IOP(d->csr_base + MR_Page )] = dev;
	proc.iopage[IOP(d->csr_base + MR_Maint)] = dev;
	proc.iopage[IOP(d->csr_base + MR_Disp )] = dev;

	if ((fd = lib_open(argv[1], &fn)) < 0)
		conf_panic("mr: can't open file %s: %s", argv[1], strerror(errno));

	d->fname = fn;

	if (fstat(fd, &statb))
		conf_panic("mr: can't stat file %s: %s", fn, strerror(errno));

	if (statb.st_size > (off_t)sizeof(d->prog))
		conf_panic("mr: boot program to large");

	if (read(fd, d->prog, statb.st_size) != statb.st_size)
		conf_panic("mr: read error on %s: %s", fn, strerror(errno));

	close(fd);
}

static void
boot_dev_create(iodev_t *dev UNUSED, int argc UNUSED, char **argv UNUSED)
{
	panic("mr: can't have any devices");
}

static void
boot_ctrl_complete(iodev_t *dev UNUSED)
{
	/* nix */
}

static void
boot_reset(iodev_t *dev UNUSED)
{
	/* nix */
}

static u_short
boot_fetch(iodev_t *dev, u_int a)
{
	u_short	v;
	boot_t	*b = dev->data;

	if(a >= MR_Page0 && a < MR_Page0 + MR_PageSize) {
		v = b->prog[(b->page0 + (a & MR_SizeMask)) >> 1];
		v = GETW(v);
	} else if(a >= MR_Page1 && a < MR_Page1 + MR_PageSize) {
		v = b->prog[(b->page1 + (a & MR_SizeMask)) >> 1];
		v = GETW(v);
	} else switch(a - b->csr_base) {

	case MR_Page:
		v = (b->page0 >> 9) + (b->page1 >> 1);
		break;

	case MR_Maint:
		v = b->maint;
		break;

	case MR_Disp:
		v = b->config;
		break;

	default:
		warn("boot_fetch(%o)", a);
		Trap4(020);
	}
	return v;
}

static void
boot_store(iodev_t *dev, u_int a, mmode_t mode, u_short v)
{
	boot_t	*b = dev->data;
	u_short	n;

	if((a >= MR_Page0 && a < MR_Page0 + MR_PageSize) || (a >= MR_Page1 && a < MR_Page1 + MR_PageSize))
		return;

	switch(a - b->csr_base) {

	case MR_Page:
		switch(mode) {

		  case M_High:
			b->page1 = (v & 017400) << 1;
			break;

		  case M_Low:
			b->page0 = (v & 037) << 9;
			break;

		  case M_Word:
			b->page0 = (v & 037) << 9;
			b->page1 = (v & 017400) << 1;
			break;
		}
		D(printf("boot map: %2o %2o - %06ho\n", b->page0, b->page1, proc.reg[7]));
		break;

	case MR_Maint:
		SETWORD(b->maint, mode, v);
		break;

	case MR_Disp:
		n = b->display;
		SETWORD(n, mode, v & 017);
		if(n != b->display) {
			b->display = n;
#ifdef LED
			printf("LED %c%c%c%c\n",
				"O."[(b->display & 010)!=0],
				"O."[(b->display & 4)!=0],
				"O."[(b->display & 2)!=0],
				"O."[(b->display & 1)!=0]);
#endif
		}
		break;

	default:
		warn("boot_store(%o, %d)", a, mode);
		Trap4(020);
	}

}

static void
boot_info(iodev_t *dev)
{
	boot_t	*b = dev->data;

	printf("BOOT PROM\n");
	printf("Page register        %08o: %02o %02o\n", b->csr_base + MR_Page, 
						b->page0, b->page1);
	printf("Maintenance register %08o: %06o\n", b->csr_base + MR_Maint, 
						b->maint);
	printf("Display register     %08o: %c%c%c%c\n", b->csr_base + MR_Disp,
						"O."[(b->display & 010)!=0],
						"O."[(b->display & 004)!=0],
						"O."[(b->display & 002)!=0],
						"O."[(b->display & 001)!=0]);
	printf("Page0 (fixed): %08o - %08o\n", MR_Page0, MR_Page0 + MR_PageSize - 2);
	printf("Page1 (fixed): %08o - %08o\n", MR_Page1, MR_Page1 + MR_PageSize - 2);
	printf("Loaded from file '%s'\n", b->fname);
}

static int
bootc_info(iodev_t *dev, int argc, char **argv UNUSED)
{
	if(argc != 0)
		return 1;
	boot_info(dev);
	return 0;
}

static int
bootc_load(iodev_t *dev, int argc, char **argv)
{
	boot_t *b = dev->data;
	int fd;
	u_short tmp[BOOTSIZE];
	struct stat statb;

	if(argc != 1)
		return 1;

	if((fd = open(argv[0], O_RDONLY)) < 0) {
		error("mr: can't open file %s: %s", argv[0], strerror(errno));
		return 0;
	}

	if(fstat(fd, &statb)) {
		error("mr: can't stat file %s: %s", argv[0], strerror(errno));
		(void)close(fd);
		return 0;
	}

	if(statb.st_size > (off_t)sizeof(b->prog)) {
		error("mr: boot program to large");
		(void)close(fd);
		return 0;
	}

	if(read(fd, tmp, statb.st_size) != statb.st_size) {
		error("mr: read error on %s: %s", argv[0], strerror(errno));
		(void)close(fd);
		return 0;
	}

	(void)close(fd);

	b->fname = xstrsave(argv[0]);
	memcpy(b->prog, tmp, sizeof(b->prog));

	return 0;
}
