/*	$NetBSD: monitor.c,v 1.11 2016/06/11 06:28:49 dholland Exp $	*/

/*-
 * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Kazuki Sakamoto.
 *
 * Redistribution 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 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 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 FOUNDATION OR 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.
 */

#include <lib/libsa/stand.h>
#include <lib/libkern/libkern.h>

#include "boot.h"

extern int errno;
extern char *name;

void db_cmd_dump(int, char **);
void db_cmd_get(int, char **);
void db_cmd_mf(int, char **);
void db_cmd_mt(int, char **);
void db_cmd_put(int, char **);
void db_cmd_help(int, char **);

uint32_t db_atob(char *);

struct {
	char *name;
	void (*fcn)(int, char **);
} db_cmd[] = {
	{ "dump",	db_cmd_dump },
	{ "get",	db_cmd_get },
	{ "mf",		db_cmd_mf },
	{ "mt",		db_cmd_mt },
	{ "put",	db_cmd_put },
	{ "help",	db_cmd_help },
	{ NULL,		NULL },
};

int
db_monitor(void)
{
	int tmp;
	int argc, flag;
	char *p, *argv[16];
	char line[1024];

	while (1) {
		printf("db> ");
		kgets(line, sizeof(line));

		flag = 0;
		for (p = line, argc = 0; *p != '\0'; p++) {
			if (*p != ' ' && *p != '\t') {
				if (!flag) {
					flag++;
					argv[argc++] = p;
				}
			} else {
				if (flag) {
					*p = '\0';
					flag = 0;
				}
			}
		}

		if (argc == 0)
			continue;

		tmp = 0;
		while (db_cmd[tmp].name != NULL) {
			if (!strcmp("continue", argv[0]))
				return 0;
			if (!strcmp(db_cmd[tmp].name, argv[0])) {
				(db_cmd[tmp].fcn)(argc, argv);
				break;
			}
			tmp++;
		}
		if (db_cmd[tmp].name == NULL)
			db_cmd_help(argc, argv);
	}
	return 0;
}

uint32_t
db_atob(char *p)
{
	uint32_t b = 0;
	int width, tmp, exp, x = 0;

	if (p[1] == 'x') {
		p += 2;
		x = 1;
	}
	width = strlen(p);
	while (width--) {
		exp = 1;
		for (tmp = 1; tmp <= width; tmp++)
			exp *= (x ? 16 : 10);
		if (*p >= '0' && *p <= '9') {
			tmp = *p - '0';
		} else {
			tmp = *p - 'a' + 10;
		}
		b += tmp * exp;
		p++;
	}
	return b;
}

void
db_cmd_dump(int argc, char **argv)
{
	char *p, *r, *pp;
	int mode, size, i;
	uint32_t add;

	switch (argc) {
	case 4:
		r = argv[1];
		switch (r[1]) {
		case 'b':
			mode = 1;
			break;
		case 'h':
			mode = 2;
			break;
		case 'w':
			mode = 4;
			break;
		default:
			goto out;
		}
		p = argv[2];
		pp = argv[3];
		break;
	case 3:
		mode = 4;
		p = argv[1];
		pp = argv[2];
		break;
	default:
		goto out;
	}

	add = db_atob(p);
	size = db_atob(pp);
	i = 0;
	for (; size > 0;) {
		if (!i)
			printf("\n0x%x:", add);
		switch (mode) {
		case 1:
			printf(" %x", *(uint8_t *)add);
			add += 1;
			size -= 1;
			if (++i == 16)
				i = 0;
			break;
		case 2:
			printf(" %x", *(uint16_t *)add);
			add += 2;
			size -= 2;
			if (++i == 8)
				i = 0;
			break;
		case 4:
			printf(" %x", *(uint32_t *)add);
			add += 4;
			size -= 4;
			if (++i == 4)
				i = 0;
			break;
		}
	}
	printf("\n");
	return;

out:
	printf("dump [-b][-h][-w] address size\n");
	return;
}

void
db_cmd_get(int argc, char **argv)
{
	char *p, *r;
	uint32_t add;
	int mode;

	switch (argc) {
	case 3:
		r = argv[1];
		switch (r[1]) {
		case 'b':
			mode = 1;
			break;
		case 'h':
			mode = 2;
			break;
		case 'w':
			mode = 4;
			break;
		default:
			goto out;
		}
		p = argv[2];
		break;
	case 2:
		mode = 4;
		p = argv[1];
		break;
	default:
		goto out;
	}

	add = db_atob(p);
	printf("0x%x: ", add);
	switch (mode) {
	case 1:
		printf("0x%x", *(uint8_t *)add);
		break;
	case 2:
		printf("0x%x", *(uint16_t *)add);
		break;
	case 4:
		printf("0x%x", *(uint32_t *)add);
		break;
	}
	printf("\n");
	return;

out:
	printf("get [-b][-h][-w] address\n");
	return;
}

void
db_cmd_put(int argc, char **argv)
{
	char *p, *r, *pp;
	uint32_t add, data;
	int mode;

	switch (argc) {
	case 4:
		r = argv[1];
		switch (r[1]) {
		case 'b':
			mode = 1;
			break;
		case 'h':
			mode = 2;
			break;
		case 'w':
			mode = 4;
			break;
		default:
			goto out;
		}
		p = argv[2];
		pp = argv[3];
		break;
	case 3:
		mode = 4;
		p = argv[1];
		pp = argv[2];
		break;
	default:
		goto out;
	}

	add = db_atob(p);
	data = db_atob(pp);
	printf("0x%x: 0x%x", add, data);
	switch (mode) {
	case 1:
		*(uint8_t *)add = data;
		break;
	case 2:
		*(uint16_t *)add = data;
		break;
	case 4:
		*(uint32_t *)add = data;
		break;
	}
	printf("\n");
	return;

out:
	printf("put [-b][-h][-w] address data\n");
	return;
}

#define STR(x) #x

#define	FUNC(x) \
uint32_t mf ## x(void); \
void mt ## x(uint32_t); \
uint32_t mf ## x() { \
	uint32_t tmp; \
	__asm volatile (STR(mf ## x %0) : STR(=r)(tmp)); \
	return (tmp); \
} \
void mt ## x(uint32_t data) \
{ \
	__asm volatile (STR(mt ## x %0) :: STR(r)(data)); \
} \

#define DEF(x) \
	{ #x, mf ## x, mt ## x }

FUNC(msr)

struct {
	char *op;
	uint32_t (*mf)(void);
	void (*mt)(uint32_t);
} mreg [] = {
	DEF(msr),
	{ NULL, NULL, NULL },
};

void
db_cmd_mf(int argc, char **argv)
{
	int i = 0;

	if (argc != 2) {
		printf("mf register\nregister:");
		while (mreg[i].op != NULL)
			printf(" %s", mreg[i++].op);
		printf("\n");
		return;
	}

	while (mreg[i].op != NULL) {
		if (!strcmp(mreg[i].op, argv[1])) {
			printf(" 0x%x\n", (mreg[i].mf)());
			break;
		}
		i++;
	}
}

void
db_cmd_mt(int argc, char **argv)
{
	int i = 0;

	if (argc != 3) {
		printf("mt register data\nregister:");
		while (mreg[i].op != NULL)
			printf(" %s", mreg[i++].op);
		printf("\n");
		return;
	}

	while (mreg[i].op != NULL) {
		if (!strcmp(mreg[i].op, argv[1])) {
			(mreg[i].mt)(db_atob(argv[2]));
			printf(" 0x%x\n", db_atob(argv[2]));
			break;
		}
		i++;
	}
}

void
db_cmd_help(int argc, char **argv)
{
	int i = 0;

	while (db_cmd[i].name != NULL)
		printf("%s, ", db_cmd[i++].name);
	printf("\n");
}
