/** @file
	For dumping the stack, GDA, heap and text segment.
*/

/* $Id$ */

#include	<ctype.h>

#include	"em_abs.h"
#include	"dump.h"
#include	"logging.h"
#include	"global.h"
#include	"log.h"
#include	"memdirect.h"
#include	"mem.h"
#include	"fra.h"
#include	"text.h"
#include	"proctab.h"
#include	"shadow.h"
#include	"linfil.h"
#include	"rsb.h"

extern long inr;			/* from log.c */

/****************************************************************
 *	Dumping routines for debugging, in human-readable form.	*
 ****************************************************************/

#ifdef	LOGGING

/*	The file is repetitive and should probably be partly generated,
	although it is not directly evident how.
*/

/* Forward declarations */
PRIVATE char *displ_undefs(int, ptr), *displ_fil(ptr), *displ_sh(char, int), *displ_code(int);
PRIVATE ptr std_raw(ptr, int), std_rsb(ptr);
PRIVATE int std_bytes(ptr, ptr, int), dtd_bytes(ptr, ptr, int), FRAd_bytes(int, int, int);
PRIVATE void std_item(ptr), std_left_undefs(int, ptr);
PRIVATE void gdad_item(ptr), gdad_left_undefs(int, ptr);
PRIVATE void hpd_item(ptr), hpd_left_undefs(int, ptr);
PRIVATE void FRA_dump(void), FRA_item(int);

/******** Stack Dump ********/

void std_all(long sz, int rawfl)
{
	register ptr addr;
	
	if (!check_log(" d1 "))
		return;
	
	LOG((" d2 "));
	LOG((" d2 . . STACK_DUMP[%ld/%ld%s] . . INR = %lu . . STACK_DUMP . .",
				wsize, psize, rawfl ? ", raw" : "", inr));
	LOG((" d2 ----------------------------------------------------------------"));

	/* find a good start address */
	addr = (sz && sz < ML - SP ? SP + sz : ML);
	/* find RSB backwards */
	while (in_stack(addr) && !is_st_prot(addr)) {
		addr++;
	}
	/* find end of RSB backwards */
	while (in_stack(addr) && is_st_prot(addr)) {
		addr++;
	}
	addr--;

	/* dump the stack */
	while (in_stack(addr)) {
		addr = std_raw(addr, rawfl);
		addr = std_rsb(addr);
	}
	FRA_dump();
	LOG((" d1 >> AB = %lu, LB = %lu, SP = %lu, HP = %lu, LIN = %lu, FIL = %s",
		AB, LB, SP, HP, getLIN(), displ_fil(getFIL())));
	LOG((" d2 ----------------------------------------------------------------"));
	LOG((" d2 "));
}

PRIVATE ptr std_raw(ptr addr, int rawfl)
{
	/*	Produces a formatted dump of the stack segment starting
		at  addr, up to the Return Status Block (identified
		by protection bits)
	*/
	register int nundef = 0;
	
	LOG((" d2       ADDRESS     BYTE     ITEM VALUE   SHADOW"));
	
	while (	in_stack(addr)
	&&	(!is_st_prot(addr) || rawfl)
	) {
		if (st_sh(addr) == UNDEFINED) {
			if (nundef++ == 0)
				LOG((" d2    %10lu    undef", addr));
		}
		else {
			if (nundef) {
				std_left_undefs(nundef, addr + 1);
				nundef = 0;
			}
			std_item(addr);
		}
		addr--;
	}
	if (nundef)
		std_left_undefs(nundef, addr + 1);
	return addr;
}

PRIVATE void std_item(ptr addr)
{
	if (	is_wordaligned(addr)
	&&	is_in_stack(addr, psize)
	&&	std_bytes(addr, addr + psize, SH_DATAP|SH_INSP)
	) {
		/* print a pointer value */
		LOG((" d2    %10lu      %3lu    [%10lu]  (%-s)",
			addr,
			btol(stack_loc(addr)),
			p_in_stack(addr),
			displ_sh(st_sh(addr), stack_loc(addr))));
	}
	else
	if (	is_wordaligned(addr)
	&&	w_is_in_stack(addr)
	&&	std_bytes(addr, addr + wsize, SH_INT)
	) {
		/* print a word value */
		LOG((" d2    %10lu      %3lu    [%10ld]  (%-s)",
			addr,
			btol(stack_loc(addr)),
			w_in_stack(addr),
			displ_sh(st_sh(addr), stack_loc(addr))));
	}
	else {
		/* just print the byte */
		LOG((" d2    %10lu      %3lu                  (%-s)",
			addr,
			btol(stack_loc(addr)),
			displ_sh(st_sh(addr), stack_loc(addr))));
	}
}

/** Dumps the Return Status Block. */
PRIVATE ptr std_rsb(ptr addr)
{
	ptr dmp_lb;
	int code;
	long pi;
	ptr pc;
	ptr lb;
	long lin;
	ptr fil;
	char pr_descr[300];
	
	if (!in_stack(addr))
		return addr;

	dmp_lb = addr - (rsbsize-1);	/* pseudo local base */
	if (!in_stack(dmp_lb)) {
		LOG((" d1 >>RSB: >>>> INCOMPLETE <<<<"));
		return dmp_lb;
	}

	code = (int)w_in_stack(dmp_lb + rsb_rsbcode);
	pi = (long)p_in_stack(dmp_lb + rsb_PI);
	pc = p_in_stack(dmp_lb + rsb_PC);
	lb = p_in_stack(dmp_lb + rsb_LB);
	lin = LIN_in_stack(dmp_lb + rsb_LIN);
	fil = p_in_stack(dmp_lb + rsb_FIL);

	if (pi == -1) {
		sprintf(pr_descr, "uninit");
	}
	else
	if (pi < NProc) {
		sprintf(pr_descr, "(%ld,%ld)",
				pi, (long)proctab[pi].pr_nloc);
	}
	else {
		sprintf(pr_descr, "%ld >>>> ILLEGAL <<<<", pi);
	}
	LOG((" d1 >> RSB: code = %s, PI = %s, PC = %lu, LB = %lu, LIN = %lu, FIL = %s",
		displ_code(code), pr_descr, pc, lb, lin, displ_fil(fil)));
	
	LOG((" d2 "));
	return addr - rsbsize;
}

PRIVATE char *displ_code(int rsbcode)
{
	switch (rsbcode) {
	case RSB_STP:	return "STP";
	case RSB_CAL:	return "CAL";
	case RSB_RTT:	return "RTT";
	case RSB_NRT:	return "NRT";
	default:	return ">>Bad RSB code<<";
	}
	/*NOTREACHED*/
}

PRIVATE void std_left_undefs(int nundef, ptr addr)
{
	/* handle pending undefineds */
	switch (nundef) {
	case 1:
		break;
	case 2:
		LOG((" d2    %10lu    undef", addr));
		break;
	default:
		LOG((" d2         | | |    | | |"));
		LOG((" d2    %10lu    undef (%s)",
				addr, displ_undefs(nundef, addr)));
		break;
	}
}

PRIVATE void FRA_dump(void)
{
	register int addr;

	LOG((" d2        FRA: size = %d, %s",
			FRASize, FRA_def ? "defined" : "undefined"));

	for (addr = 0; addr < FRASize; addr++) {
		FRA_item(addr);
	}
}

PRIVATE void FRA_item(int addr)
{
	if (	is_wordaligned(addr)
	&&	is_in_FRA(addr, psize)
	&&	FRAd_bytes(addr, (int)(addr + psize), SH_DATAP|SH_INSP)
	) {
		/* print a pointer value */
		LOG((" d2        FRA[%1d]      %3lu    [%10lu]  (%-s)",
			addr,
			btol(FRA[addr]),
			p_in_FRA(addr),
			displ_sh(FRA_sh[addr], FRA[addr])));
	}
	else
	if (	is_wordaligned(addr)
	&&	is_in_FRA(addr, wsize)
	&&	FRAd_bytes(addr, (int)(addr + wsize), SH_INT)
	) {
		/* print a word value */
		LOG((" d2        FRA[%1d]      %3lu    [%10ld]  (%-s)",
			addr,
			btol(FRA[addr]),
			w_in_FRA(addr),
			displ_sh(FRA_sh[addr], FRA[addr])));
	}
	else {
		/* just print the byte */
		LOG((" d2        FRA[%1d]      %3lu                  (%-s)",
			addr,
			btol(FRA[addr]),
			displ_sh(FRA_sh[addr], FRA[addr])));
	}
}


/******** Global Data Area Dump ********/

void gdad_all(ptr low, ptr high)
{
	register ptr addr;
	register int nundef = 0;
	
	if (!check_log(" +1 "))
		return;
	
	if (low == 0 && high == 0)
		high = HB;
	
	LOG((" +1 "));
	LOG((" +1 . . GDA_DUMP[%ld/%ld] . . INR = %lu . . GDA_DUMP . .",
				wsize, psize, inr));
	LOG((" +1 ----------------------------------------------------------------"));
	LOG((" +1       ADDRESS     BYTE     WORD VALUE   SHADOW"));
	
	/* dump global data area contents */
	addr = low;
	while (addr < min(HB, high)) {
		if (dt_sh(addr) == UNDEFINED) {
			if (nundef++ == 0)
				LOG((" +1    %10lu    undef", addr));
		}
		else {
			if (nundef) {
				gdad_left_undefs(nundef, addr-1);
				nundef = 0;
			}
			gdad_item(addr);
		}
		addr++;
	}
	if (nundef)
		    gdad_left_undefs(nundef, addr-1);
	LOG((" +1 ----------------------------------------------------------------"));
	LOG((" +1 "));
}

PRIVATE void gdad_item(ptr addr)
{
	if (	is_wordaligned(addr)
	&&	is_in_data(addr, psize)
	&&	dtd_bytes(addr, addr + psize, SH_DATAP|SH_INSP)
	) {
		/* print a pointer value */
		LOG((" +1    %10lu      %3lu    [%10lu]  (%-s)",
			addr,
			btol(data_loc(addr)),
			p_in_data(addr),
			displ_sh(dt_sh(addr), data_loc(addr))));
	}
	else
	if (	is_wordaligned(addr)
	&&	is_in_data(addr, wsize)
	&&	dtd_bytes(addr, addr + wsize, SH_INT)
	) {
		/* print a word value */
		LOG((" +1    %10lu      %3lu    [%10ld]  (%-s)",
			addr,
			btol(data_loc(addr)),
			w_in_data(addr),
			displ_sh(dt_sh(addr), data_loc(addr))));
	}
	else {
		/* just print the byte */
		LOG((" +1    %10lu      %3lu                  (%-s)",
			addr,
			btol(data_loc(addr)),
			displ_sh(dt_sh(addr), data_loc(addr))));
	}
}

PRIVATE void gdad_left_undefs(int nundef, ptr addr)
{
	/* handle pending undefineds */
	switch (nundef) {
	case 1:
		break;
	case 2:
		LOG((" +1    %10lu    undef", addr));
		break;
	default:
		LOG((" +1         | | |    | | |"));
		LOG((" +1    %10lu    undef (%s)",
				addr, displ_undefs(nundef, addr)));
		break;
	}
}

/******** Heap Area Dump ********/

void hpd_all(void)
{
	register ptr addr;
	register int nundef = 0;
	
	if (!check_log(" *1 "))
		return;

	LOG((" *1 "));
	LOG((" *1 . . HEAP_DUMP[%ld/%ld] . . INR = %lu . . HEAP_DUMP . .",
				wsize, psize, inr));
	LOG((" *1 ----------------------------------------------------------------"));
	LOG((" *1       ADDRESS     BYTE     WORD VALUE   SHADOW"));
	
	/* dump heap contents */
	for (addr = HB; addr < HP; addr++) {
		if (dt_sh(addr) == UNDEFINED) {
			if (nundef++ == 0)
				LOG((" *1    %10lu    undef", addr));
		}
		else {
			if (nundef) {
				hpd_left_undefs(nundef, addr-1);
				nundef = 0;
			}
			hpd_item(addr);
		}
	}
	if (nundef)
		hpd_left_undefs(nundef, addr-1);
	LOG((" *1 ----------------------------------------------------------------"));
	LOG((" *1 "));
}

PRIVATE void hpd_item(ptr addr)
{
	if (	is_wordaligned(addr)
	&&	is_in_data(addr, psize)
	&&	dtd_bytes(addr, addr + psize, SH_DATAP|SH_INSP)
	) {
		/* print a pointer value */
		LOG((" *1    %10lu      %3lu    [%10lu]  (%-s)",
			addr,
			btol(data_loc(addr)),
			p_in_data(addr),
			displ_sh(dt_sh(addr), data_loc(addr))));
	}
	else
	if (	is_wordaligned(addr)
	&&	is_in_data(addr, wsize)
	&&	dtd_bytes(addr, addr + wsize, SH_INT)
	) {
		/* print a word value */
		LOG((" *1    %10lu      %3lu    [%10ld]  (%-s)",
			addr,
			btol(data_loc(addr)),
			w_in_data(addr),
			displ_sh(dt_sh(addr), data_loc(addr))));
	}
	else {
		/* just print the byte */
		LOG((" *1    %10lu      %3lu                  (%-s)",
			addr,
			btol(data_loc(addr)),
			displ_sh(dt_sh(addr), data_loc(addr))));
	}
}

PRIVATE void hpd_left_undefs(int nundef, ptr addr)
{
	/* handle pending undefineds */
	switch (nundef) {
	case 1:
		break;
	case 2:
		LOG((" *1    %10lu    undef", addr));
		break;
	default:
		LOG((" *1         | | |    | | |"));
		LOG((" *1    %10lu    undef (%s)",
				addr, displ_undefs(nundef, addr)));
		break;
	}
}


/* Service routines */

PRIVATE int std_bytes(ptr low, ptr high, int bits)
{
	/*	True if all stack bytes from low to high-1 have one of the
		bits in bits on.
	*/
	int byte = bits;

	while (low < high) {
		byte &= st_sh(low);
		low++;
	}

	return byte & bits;
}

PRIVATE int dtd_bytes(ptr low, ptr high, int bits)
{
	/*	True if all data bytes from low to high-1 have one of the
		bits in bits on.
	*/
	int byte = bits;

	while (low < high) {
		byte &= dt_sh(low);
		low++;
	}

	return byte & bits;
}

PRIVATE int FRAd_bytes(int low, int high, int bits)
{
	/*	True if all data bytes from low to high-1 have one of the
		bits in bits on.
	*/
	int byte = bits;

	while (low < high) {
		byte &= FRA_sh[low];
		low++;
	}

	return byte & bits;
}

PRIVATE char *displ_undefs(int nundef, ptr addr)
{
	/*	Given the number of undefineds, we want to report the number
		of words with the left-over numbers of bytes on both sides:
			|             nundef               |
			|left|          wrds            |right
			.....|........|........|........|...
			a
			d
			d
			r
		This takes some arithmetic.
	*/
	static char buf[30];
	register int left = wsize - 1 - p2i(addr-1) % wsize;
	register int wrds = (nundef-left) / wsize;
	register int right = nundef - left - wrds*wsize;

	if (wrds == 0) {
		sprintf(buf, "%d byte%s",
			nundef, nundef == 1 ? "" : "s");
	}
	else if (left == 0 && right == 0) {
		sprintf(buf, "%d word%s",
			wrds, wrds == 1 ? "" : "s");
	}
	else if (left == 0) {
		sprintf(buf, "%d word%s + %d byte%s",
			wrds, wrds == 1 ? "" : "s",
			right, right == 1 ? "" : "s");
	}
	else if (right == 0) {
		sprintf(buf, "%d byte%s + %d word%s",
			left, left == 1 ? "" : "s",
			wrds, wrds == 1 ? "" : "s");
	}
	else {
		sprintf(buf, "%d byte%s + %d word%s + %d byte%s",
			left, left == 1 ? "" : "s",
			wrds, wrds == 1 ? "" : "s",
			right, right == 1 ? "" : "s");
	}
	return buf;
}

PRIVATE char *displ_fil(ptr fil)
{	/*	Returns a buffer containing a representation of the
		filename derived from FIL-value fil.
	*/
	static char buf[40];
	char *bp = &buf[0];
	int ch;
	
	if (!fil)
		return "NULL";
	if (fil >= HB)
		return "***NOT IN GDA***";
	
	*bp++ = '"';
	while (in_gda(fil) && (ch = data_loc(fil))) {
		if (bp < &buf[sizeof buf-1]) {
			*bp++ = (ch < 040 || ch > 126 ? '?' : ch);
		}
		fil++;
	}
	if (bp < &buf[sizeof buf-1])
		*bp++ = '"';
	*bp++ = '\0';
	return &buf[0];
}

PRIVATE char *displ_sh(char shadow, int byte)
{	/*	Returns a buffer containing a description of the
		shadow byte.
	*/
	static char buf[32];
	register char *bufp;
	int check = 0;

	bufp = buf;
	if (shadow & SH_INT) {
		*bufp++ = 'I';
		*bufp++ = 'n';
		check++;
	}
	if (shadow & SH_FLOAT) {
		*bufp++ = 'F';
		*bufp++ = 'l';
	}
	if (shadow & SH_DATAP) {
		*bufp++ = 'D';
		*bufp++ = 'p';
	}
	if (shadow & SH_INSP) {
		*bufp++ = 'I';
		*bufp++ = 'p';
	}

	if (shadow & SH_PROT) {
		*bufp++ = ',';
		*bufp++ = ' ';
		*bufp++ = 'P';
		*bufp++ = 'r';
		*bufp++ = 'o';
		*bufp++ = 't';
	}

	if (check && isascii(byte) && isprint(byte)) {
		*bufp++ = ',';
		*bufp++ = ' ';
		*bufp++ = byte;
		*bufp++ = ' ';
	}
	*bufp = 0;
	return (buf);
}

#endif	/* LOGGING */

