:i	static char dmp_hexd_sccs_id[] = "%W% %G%";
:1#include "V.h"
/*
* Produce a hex dump of a block of memory.
*
* There are two formats produced, depending on the global  hex_fmt  variable,
* which may be 'h' (for "horizontal") or 'v' (for "vertical).
*
* The  default  'v' format produces three lines per 64 bytes.  The first line
* shows that data interpreted as ASCII chars, with '_' for  any  nonprintable
* chars.  The sign bit is stripped off before output of the first line, which
* is useful for code that uses it as a flag.  The second and third lines  are
* the high- and low-order hex digit for the chars on the first line. The main
* advantage of this format is that you can easily compare the  hex  with  the
* ASCII  value  of  a  byte, since they are vertically aligned.  64 bytes are
* produced on a line. To the left, the ASCII line has the decimal byte count,
* and the high-order hex line has the hex byte count.  If the msg argument is
* non-null, its first 8 bytes are shown on the first line instead of the byte
* count  (which  would be zero).  Also, if adr is nonzero, it is added to all
* the byte counts; the suggested use is to make adr==pa, so  the  dump  shows
* the (virtual) addresses of the data.  This function isn't usually called by
* directly; see V.h for macros that make conditional calls.
*
* The 'h' format may be more familiar to some.  It produces two lines per  32
* bytes. The second line is the hex values, two digits per byte of data, with
* spaces between "words".  Above this, the  ASCII  char  is  shown  over  the
* low-order hex digit.
*/
global int  hex_perline = 0;	/* Number of hex digits per line of dump */
global int  chr_perline = 0;	/* Number of data chars per line of dump */

dmp_hexd(f,pa,pz,msg,adr)
	int   f;		/* Output file number */
	char *pa, *pz;	/* First and last bytes */
	char *msg;		/* Message for first line, if nonnull */
	Ulong adr;		/* Address to show, if no message */
{	char  c;
	Ulong base=adr;
	char* lp=pa;
	int   col = 0;	/* Column in output lines, starting after indent */
	int   asc = 1;	/* Column for ASCII, in 1-line format */

	if (!hex_hi) if (!(hex_hi = (CP)MallocM(HXLINE+1,"hex_hi"))) Fail;
	if (!hex_as) if (!(hex_as = (CP)MallocM(HXLINE+1,"hex_as"))) Fail;
	Switch(hex_fmt) {
	case '1': case  1 :	/* One-line (IBM-style) dump */
		chr_perline = 16;
		hex_perline = 32;	/* Number of hex digits per line */
		if (hex_word) {	/* Adjust for "word" spacing? */
			while (hex_word % 4) hex_word++;		/* Must be multiple of 4 bytes */
			hex_perline += ((16 / hex_word) + 2);	/* Width of hex portion */
		}
		hex_as[INDENT+40] =
		hex_as[INDENT+57] = '|';
:8		if (Vlvl > 4) hexd_as("as_init");
		asc = 0;
		break;
	case '2': case  2 :
	case 'h': case 'H':
		chr_perline = 32;
		hex_perline = 64;	/* Number of hex digits per line */
		if (hex_word)		/* Calculate width of hex lines */
			hex_perline += ((64 / hex_word) - 1);
		break;
	case '3': case  3 :
	case 'v': case 'V': default:
		if (!hex_lo) if (!(hex_lo = (CP)MallocM(HXLINE+1,"hex_lo"))) Fail;
		chr_perline = 64;
		hex_perline = 64;	/* Number of hex digits per line */
		if (hex_word)		/* Calculate width of hex lines */
			hex_perline += ((64 / hex_word) - 1);
	}
:8	V6 "hex_word=%d chr_perline=%d hex_perline=%d.",
:8		hex_word,chr_perline,hex_perline D;
/*
* Fill in the left edge of the line:
*/
	if (msg) {
		Switch(hex_fmt) {
		case '1': case  1 :
			if (base == adr) {
				sprintf(hex_as,"%s         ",msg);
			} else {
				sprintf(hex_as,"%8lX ",hex_base?adr:adr-base);
			}
:8			if (Vlvl > 4) hexd_as("as_msg");
			break;
		case '3': case  3 :
		case 'v': case 'V':
			Bcopy(Vblanks,hex_lo,INDENT);
		case '2': case  2 :
		case 'h': case 'H': default:
			sprintf(hex_hi,"%8lX ",hex_base?adr:adr-base);
			sprintf(hex_as,"%s         ",msg);
		}
	} else {
		Switch(hex_fmt) {
		case '1': case  1 :
			sprintf(hex_as,"%8lX ",hex_base?adr:adr-base);
:8			if (Vlvl > 4) hexd_as("as_adr");
			break;
		case '3': case  3 :
		case 'v': case 'V':
			Bcopy(Vblanks,hex_lo,INDENT);
		case '2': case  2 :
		case 'h': case 'H': default:
			sprintf(hex_hi,"%8lX ",hex_base?adr:adr-base);
			sprintf(hex_as,"%8lu ",hex_base?adr:adr-base);
		}
	}
/*
* Run thru the data, building the hex lines:
*/
	while (pa <= pz) {
		c = *pa++;
		adr++;
		Switch(hex_fmt) {
		case '1': case  1 :
:8			V6 "c=%02X='%c' col %d.",B8(c),Dsp(c),col D;
			hex_as[INDENT+col] = hex_dig[0xF & (c >> 4)];
			++col;
			hex_as[INDENT+col] = hex_dig[0xF & (c)];
			++col;
			hex_as[INDENT+41+asc++] = dsp(c);
:8			if (Vlvl > 4) hexd_as("as_char");
			break;
		case '2': case  2 :
		case 'h': case 'H':
			hex_hi[INDENT+col] = hex_dig[0xF & (c >> 4)];
			hex_as[INDENT+col] = ' ';
			++col;
			hex_hi[INDENT+col] = hex_dig[0xF & (c)];
			hex_as[INDENT+col] = dsp(c);
			++col;
			break;
		case '3': case  3 :
		case 'v': case 'V': default:
			hex_lo[INDENT+col] = hex_dig[0xF & (c)];
			hex_hi[INDENT+col] = hex_dig[0xF & (c >> 4)];
			hex_as[INDENT+col] = dsp(c);
			++col;
		}
		if (pa-lp >= chr_perline) {	/* Line full */
:8			V6 "hex_word=%d hex_perline=%d col=%d.",hex_word,hex_perline,col D;
			hexout(f,col,asc);
			Switch(hex_fmt) {
			case '1': case  1 :
				sprintf(hex_as,"%8lX ",hex_base?adr:adr-base);
:8				if (Vlvl > 4) hexd_as("as_new");
				asc = 0;
				break;
			case '2': case  2 :
			case '3': case  3 :
			case 'v': case 'V':
			case 'h': case 'H': default:
				sprintf(hex_hi,"%8lX ",hex_base?adr:adr-base);
				sprintf(hex_as,"%8lu ",hex_base?adr:adr-base);
				break;
			}
			col = 0;
			lp = pa;
		}
		if (hex_word && ((col % (hex_word+1)) == hex_word)) {
			Switch(hex_fmt) {
			case '3': case  3 :
			case 'v': case 'V':
				hex_lo[INDENT+col] = ' ';
			case '2': case  2 :
			case 'h': case 'H': default:
				hex_hi[INDENT+col] = ' ';
			case '1': case  1 :
				hex_as[INDENT+col] = ' ';
				++col;
			}
		}
	}
	if (col > 0) {	/* Final fragment */
		hexout(f,col,asc);
		col = 0;
	}
fail:
	return 0;
}
