/*
 * Copyright (C) 2000-2002 ASANO Masahiro
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "crash.h"

PRIVATE addr_t dump();
const commandtable_t command_dump =
	{"dump", dump, "[-h|w|d|2|4|8] address [size]", "dump virtual memory\n\t-h | -2  print by half word\n\t-w | -4  print by word\n\t-d | -8  print by double word"};

static char chr2d[256];
static const char int2h[] = "0123456789abcdef";

PRIVATE void
dumpinit()
{
	int	i;

	for (i = 0; i < sizeof(chr2d); i++) {
		if (isascii(i) && isprint(i)) {
			chr2d[i] = i;
		} else {
			chr2d[i] = ' ';
		}
	}
}

void
printxd(p, addr, size, mode)
	const unsigned char *p;
	addr_t addr;
	int size;
	prmode_t mode;
{
	static int initialized = 0;
	int i;
	int astmode = 0;
	unsigned char membuf[16384+16];
	int area;

	if (! initialized) {
		initialized = 1;
		dumpinit();
	}

	area = p? size: 0;

	for (i = 0; i <= size - 16; i += 16, p += 16, addr += 16) {
		if (i >= area) {
			memcpy(membuf, membuf + sizeof(membuf) - 16, 16);
			area = size - i;
			if (area > sizeof(membuf) - 16) {
				area = sizeof(membuf) - 16;
			}
			memread(addr, area, membuf + 16, "kernel memory");
			p = membuf + 16;
			area += i;
		}
		if (i && memcmp(p, p - 16, 16) == 0) {
			if (! astmode) {
				mprintf("*\n");
			}
			astmode = 1;
			continue;
		}
		mprintf("%08lx:  ", addr);
		switch (mode) {
		case BYBYTE:
			mprintf("%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x",
				(int)p[0],	(int)p[1],	(int)p[2],
				(int)p[3],	(int)p[4],	(int)p[5],
				(int)p[6],	(int)p[7],	(int)p[8],
				(int)p[9],	(int)p[10],	(int)p[11],
				(int)p[12],	(int)p[13],	(int)p[14],
				(int)p[15]);
			break;
		case BYHALFWORD: {
			unsigned short *hp = (unsigned short *)p;
			mprintf("%04x %04x %04x %04x %04x %04x %04x %04x",
				hp[0], hp[1], hp[2], hp[3],
				hp[4], hp[5], hp[6], hp[7]);
			}
			break;
		case BYWORD: {
			unsigned int *ip = (unsigned int *)p;
			mprintf("%08x %08x %08x %08x",
				ip[0], ip[1], ip[2], ip[3]);
			}
			break;
		case BYDOUBLE: {
			unsigned long long *lp = (unsigned long long *)p;
			mprintf("%016Lx  %016Lx ", lp[0], lp[1]);
			}
			break;
		}
		mprintf("  %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
			chr2d[p[0]],	chr2d[p[1]],	chr2d[p[2]],
			chr2d[p[3]],	chr2d[p[4]],	chr2d[p[5]],
			chr2d[p[6]],	chr2d[p[7]],	chr2d[p[8]],
			chr2d[p[9]],	chr2d[p[10]],	chr2d[p[11]],
			chr2d[p[12]],	chr2d[p[13]],	chr2d[p[14]],
			chr2d[p[15]]);
		astmode = 0;
	}

	mprintf("%08lx: ", addr);
	if (i == size) {
		mprintf("\n");
		return;
	}
	if (i >= area) {
		memread(addr, size - i, membuf, "kernel memory");
		p = membuf;
	}
	switch (mode) {
	case BYBYTE: {
	/*	 _+0+1+2+3 +4+5+6+7 +8+9+a+b +c+d+e+f  0123456789abcdef */
#define	BUFORIG	"                                                      \n"
		char	line[sizeof(BUFORIG)];
		int	m = 1, n;

		strcpy(line, BUFORIG);
		for (n = 0; i < size; i++, n++, p++) {
			line[m++] = int2h[*p / 16];
			line[m++] = int2h[*p % 16];
			line[38 + n] = chr2d[*p];
			if ((i % 4) == 3)
				m++;
		}
		mprintf("%s", line);
		}
		break;
	case BYHALFWORD: {
		unsigned short *hp = (unsigned short *)p;
		for (; i < size; i += 2) {
			mprintf(" %04x", *hp++);
		}
		mprintf("\n");
		}
		break;
	case BYWORD: {
		unsigned int *ip = (unsigned int *)p;
		for (; i < size; i += 4) {
			mprintf(" %08x", *ip++);
		}
		mprintf("\n");
		}
		break;
	case BYDOUBLE: {
		unsigned long long *lp = (unsigned long long *)p;
		for (; i < size; i += 8) {
			mprintf(" %08Lx ", *lp++);
		}
		mprintf("\n");
		}
		break;
	}
}

PRIVATE addr_t
dump()
{
	int c;
	prmode_t mode = BYBYTE;
	addr_t addr;
	int size = 16;

	while ((c = getopt(argcnt, args, "1248hwd")) != EOF) {
		switch (c) {
		case '1':
			mode = BYBYTE; break;
		case '2':
		case 'h':
			mode = BYHALFWORD; break;
		case '4':
		case 'w':
			mode = BYWORD; break;
		case '8':
		case 'd':
			mode = BYDOUBLE; break;
		default:
			THROW(usage);
		}
	}

	if (argcnt - optind <= 0 || argcnt - optind > 2) {
#ifdef DEBUG
		int i;
		mprintf("argcnt:%d, optind:%d\n", argcnt, optind);
		for (i = 0; i < argcnt; i++) {
			mprintf("%d: '%s'\n", i, args[i]);
		}
#endif /*DEBUG*/
		THROW(usage);
	}
	addr = getaddr(args[optind]);
	if (addr & ((int)mode - 1)) {
		THROW("invalid address");
	}
	if (argcnt - optind >= 2) {
		size = (int)getaddr(args[optind + 1]);
	}
	if (size & ((int)mode - 1)) {
		THROW("invalid size");
	}
	printxd(NULL, addr, size, mode);
	return 0;
}
