/* Julian H. Stacey's Portable Monitor <jhs@>	*/
/* currently having nasty_ removed to sort out pointer log collusion */
#ifdef MSDOS	/* { */
#include	"grep_msd.h" /* assist grepping binary of this program */
#endif		/* } */

	char
sccsID[] = "\n@(#) Monitor V2.1 : Copyright Julian H. Stacey Jan 1984.\n";

/* Copyright, Julian H. Stacey, January 1984 Canterbury, England.
*  '96 Munich Germany.
*
*	(developed originally on cpm c80).
*
* Facilities Included (in order):
*	1 Designed for NS16K system.
*	1.5 Converted for MSDOS 8086
*	2 Help List.
*	3 Intel Object IO
*	4 Shortform hex input.
*	5 Chained access (can use '+' & '-' as combined '+' & 'CR' or '-' & 'CR'
*	6 Relative + Base addressing.
*	7 Interrupt.
*	8 To simplify data entry typing during sequential data entry,
*	  instead of terminating data with 'white space' (the normal way),
*	  a '+', '-', or '.' can be used to terminate input, the monitor
*	  then doesn't need white space until you wish to end chain data entry.
*	9 Developed on Xerox CP/M system using C80 compiler.  Then MSDOS
*	10 Uses minimum number of library/(macro ?) calls in order to
*	   ease transportation to ROM.
*
* Limitations (in order):
*	1 Needs ram for variables & stack frame, not a 'ROM only' monitor.
*	2 No dis-assembler.
*	4 No machine specific features such as
*		data sectors for normal/system, data/stack/code.
*	5 Object IO : only data & EOF records implemented,
*	  no relocatable records.
*	6 No Backspace (delete a numeric character by shifting it off the end,
*	  with 6 or less zeros.
*	7 No symbolic labels.
*
* Pending Improvement (in order):
*	1 Suppres print of some CRs
*	2 SIO Driver
*	3 Validate/test case needs to generatethe dram time-out
*	  chain branch code.
*	4 SWI needs code to vector to & execute.
*	5 + CR + CR + CR squences are tedious, could a CR be assumed
*	  until terminating dot.
*
* Design Aims (in order):
*	1 To have a portable monitor usable on the NS16000  & 8086 series.
*	2 To have a 'rich' list of facilities.
*	3 Portability: to be usable for other 32 bit & lesser processors
*	  with a minimum of modification. (Hence written in 'C' but with
*	  more explicit entity sizes than C normally provides).
*	4 To optimise for minimum code size rather than maximum speed (except
*	  lengthy ram validate case).
*
* Assumptions:
*	1 VDU does a CR LF if sent a CR.
*
* Notes:
*	1 signal(10,1) & signal(11,1) should override segmentation
*	  violations if debugging on a proper unix(though untried !)
*	2 JJ & ?? = edit search key implying needs attention later
*
* Bugs: interrupt, doesnt
*	disc_rw() sets drive to A, but shouldnt
*/

#include	<stdio.h>

char *ARGV ;

/* Programming entities to aid portability */
#define s32bit	long
#define x32bit	long

#define u16bit	unsigned
#define x16bit	int
#define s16bit	int

#define x8bit	char
#define u8more	unsigned
#define x8more	char
#define s8more	int

#define BEL	7

/* Flags to define mode of operation of the monitor */

u8more	size;		/* Entity Type */
#define STRING		0
#define BYTE		1
#define WORD		2
#define LONG		3

u8more	mod_access;		/* Access Modes */
#define READ		1
#define WRITE		2
#define MODIFY		READ | WRITE

x8more	verbose ;	/* if flag is off monitor gives no prompts
			   (useful for remote control of monitor
			   by another program */
x8more	ascii ; 	/* if set & also in byte read mode,
			   prints text as well as hex, else prints just hex */
x8more	intrupt ;	/* used to indicate when keyboard has interrupted
			   a sequence. */
x8more	dump ;		/* if 1 write to disc (& possibly screen),
			   if 0 write to screen */
x8more	load ;		/* if 1 [read from disc & display on screen]
			   if 0 read from keyboard */
x8more	supres; 	/* used to suppress multiple CR LFs
			   coming in from keyboard */

u8more	cksum ; 	/* Checksum for intel loader format*/
FILE	*chan;		/* used by object io */
x8more	hexcom;

/* Max length name is a:monitor_.obj + null */
#ifndef unix	/* { */
#define FILE_LN 15
#else		/* } { */
#define FILE_LN 200 /* JJ tacky */
#endif		/* } */

char	filnam[FILE_LN];	/* filename for object IO (in RAM) */
char	initnam[] = "DUMP.OBJ"; /* initial filename (in EPROM) */
char	*name_p ;		/* pointer to current filename */

char	menu[] = "(For menu type \'?\')";

/* JJLATER hack this to signals */
interupt() { putchar(BEL); intrupt = 1; }
x8more	intj() { return(0); }

x8more isletter(x) char x;
	{ if ((x >= 0x20) && (x <= 0x7E)) return(1) ; else return(0); }

/* Put a character to screen after seeing if a control B
		keyboard interrupt has occured */
putjch(ch_x)
	char	ch_x;
	{
	/* JJLATER add something */
	ch_x &= 0x7F ;
/*			make sure I put out no parity, also
*			make it a litle bit hard to modify the
*			copyright notice if the recipient only has a binary
*/
#define BS	8
#define HT	9
#define NL	0xA
#define CR	0xD
#define NULL	0
	if (isletter(ch_x) || ch_x == BEL || ch_x == BS || ch_x == HT
		|| ch_x == NL || ch_x == CR || ch_x == NULL )
/*		mask out chars that will cause cursor addressing screen
*		disturbances, but allow nulls, as they may be used to
*		implement time delays
*/
		{
		if (!dump) putchar(ch_x);
		else if (verbose) putchar(ch_x);
		}
	if (dump) putc(ch_x, chan);
	}

/* my_error messages defined so they are consistently the same,
	& can be recognised by a remote control program */

my_error(x)
	char	*x; { putjch(BEL); prints( "\nError : ") ; prints(x); }

/* Ensure same spacing on screen for reads & writes */
#define prompt		prints(" ? ")
#define space		prints(" \040 ")

/* Output Routines */

put1h(x)  char	 x; { if (x < 10) putjch(x + '0'); else putjch(x -10 + 'A'); }
put2h(x)  x8bit  x; { cksum += x ; put1h((x >>4 ) & 0xF) ; put1h(x & 0xF); }
put4h(x)  x16bit x; { put2h((x >> 8)  & 0xFF   ); put2h(x & 0xFF)  ; }
put8h(x)  x32bit x; { put4h((x >> 16) & 0xFFFF ); put4h(x & 0xFFFF); }
putadr(x) x32bit x; { put2h(x >> 16   & 0xFF   ); put4h(x & 0xFFFF); }
out1h(x)  char	 x; { putjch(' '); put1h(x);  }
out2h(x)  x8bit  x; { putjch(' '); put2h(x);  }
out4h(x)  x16bit x; { putjch(' '); put4h(x);  }
out8h(x)  x32bit x; { putjch(' '); put8h(x);  }
outadr(x) x32bit x; { putjch(' '); putadr(x); }

/* Display current location addresses */
out_loc(base_p,kurrent,if_lf)
	char *base_p;
	long kurrent ;
	int	if_lf;
	{
	if (if_lf) vputjch('\n');
	if (base_p != (char *)0)
		{
		vprints( "B:" );
		putadr(base_p);
		vprints( "+R:" );
		}
	put8hex(kurrent);
	if (base_p != (char *)0)
		{
		vprints( "=T:" );
		putadr(kurrent + base_p) ;
		}
	putjch(' ');
	}

put_byte(x)
	x8bit x;
	{
	put2h(x);
	put_letr(x);
	}

put_letr(x)
	x8bit x;
	{
	putjch(' ');
	if ( ascii && isletter(x)) putjch(x) ;
	else putjch(' ');
	}

vputjch(x)
	char	x;
	{
	if (verbose) putjch(x);
	}

/* String procedures */
vprints(x)
	char	*x;
	{
	if (verbose) prints(x);
	}

jgets(name,len) /* Puts a null terminated string into array *'name',
		where array is of max. size (inc null) 'len') */
	char	*name ;
	x8more	len;
	{
	s8more	c ;
	char	*limit ;
	limit = name + len -1 ;
	while ( name < limit && (c = getjch()) != '\n' && c != EOF)
		{ *name = (char)c ; name++ ; }
	*name = '\0';
	}

prints(x)
	char	*x;
	{
	while((*x) && !intrupt)
		{
		/* if necessary for VDU
			if (*x == '\n') putjch('\r');	*/
		putjch(*x);
		x++;
		}
	}

/* Input Routines */

/* Gets a charater from disc or keyboard */
	s8more
getjjch()
	{
	s8more	x ;
	x = ( load ? getc(chan) :
#ifdef	MSDOS	/* { */
				 getche()
#endif		/* } */
#ifdef	unix	/* { */
				 getchar() /* something else needed */
#endif		/* } */
						 ) ;
	if ( x != EOF)
		{
		if (load && verbose) putchar((char)x);
			/* show whats coming from disc */
		}
	return(x);
	}

/* Strips multiple CR LF NULL strings to a single LF,
	to deal with unpleasant terminals.*/
	s8more
getjch()
	{
	s8more	x;
	if (supres)	/* ignore 'CR's etc */
		{
		do x = getjjch() ;
			while (x == '\r' || x == '\n' || x == '\0');
		supres = 0;	/* allow next time to return a CR */
		}
	else	{	/* may get a 'CR' etc */
		x = getjjch() ;
		if ( x == '\r' || x == '\n' || x == '\0')
			{
			supres = 1 ;
			x = '\n';
			}
		}
	return(x);
	}

/* Gets one hex from keyboard returns -1 if a 'white space' character
	or comma, Assumptions : ASCII */
	s8more
geth()
	{
	s8more	data ;
	hexcom = 0;
	for(;;)
		{
		data = getjch() ;
		if (data >= '0' && data <= '9') return(data - '0');
		if (data >= 'A' && data <= 'F') return(data - 'A' + 10 );
		if (data >= 'a' && data <= 'f') return(data - 'a' + 10 );
		if (data == ' ' || data == '\t' || data == ',' ||
			data == '\n' || data == EOF ) return((s8more)-1);
		if (data == '+' || data == '-' || data == '.')
			{
			hexcom = data;
			vprints("\b \n");
			return((s8more)-1);
			}
		my_error( "\nHex characters only please.\n" );
		}
	}

/* The hex input routines below allow number correction by allowing continuous
	input up to a 'white space' - excess most significant nibbles
	fall of the left end */

	x8bit
get2h() /* Get a byte containing 2 hex chars */
	{
	int	temp1, temp2;
	while (( temp1 = geth()) == -1);
	while (( temp2 = geth() ) != -1 ) temp1 = (temp1 << 4) | temp2 ;
	return((x8bit)temp1);
	}

	u16bit
get4h()
	{
	int	temp; u16bit result;
	while ((result = geth())  == -1);
	while ( ( temp = geth() ) != -1 )
		result = (result << 4) | temp ;
	return(result);
	}

	x32bit
get8h()
	{
	int	temp; s32bit result;
	while (( result = geth()) == -1 ) ;
	while ( ( temp = geth() ) != -1 )
		result = (result << 4) | temp ;
	return(result);
	}

/* 'getadr' jgets an address, it is separate from 'get8h' for 2 reasons :
	1	in case fixed format input is one day required.
	2	Current NS16K family adresses are 24 bits (= 6 hex) */
	char *
getadr()
	{
	return( (char *) get8h() );
	}

/* Gets 2 hex chars for object loader, no more, no less,
	no terminating white space required */
	u8more
take2h()
	{
	x8bit temp;
	temp = geth() << 4 ;
	temp |= geth() ;
	cksum += temp ;
	return(temp);
	}

	/* Display [current, previous, or next] : [byte, word, long, 
		or string] */
present(base_p,kurrent,disp)
	char *base_p;
	long kurrent ;
	s8more	disp ;	/* Display of next entity to be accessed
				with case '.' , '+' , or '-' */
	{
	unsigned count ;
	x8bit tmp;
	if ( size == BYTE)
		{
		kurrent += disp;
		out_loc(base_p,kurrent,0); 
		if (mod_access & READ)
			{
			space;
			put_byte(*(kurrent + base_p));
			}
		if (mod_access & WRITE)
			{
			prompt;
			*(kurrent + base_p) = get2h();
			}
		}
	else if ( size == WORD)
		{
		kurrent += 2 * disp;
		out_loc(base_p,kurrent,0);
		if (mod_access & READ)
			{
			space;
			out4h(*((int *)(kurrent + base_p)));
			}
		if (mod_access & WRITE)
			{
			prompt;
			*((int *)(kurrent + base_p)) = get4h();
			}
		}
	else if ( size == LONG )
		{
		kurrent += 4 * disp;
		out_loc(base_p,kurrent,0);
		if (mod_access & READ)
			{
			space;
			out8h(*((long *)(kurrent + base_p)));
			}
		if (mod_access & WRITE)
			{
			prompt;
			*((long *)(kurrent + base_p)) = get8h();
			}
		}
	else	/* STRING */
		{
		kurrent += disp;
		if (mod_access & READ)
			{
			out_loc(base_p,kurrent,0);
			count = 300;	/* limit printout to give a feel of
					where you are, without printing
					kilobytes (in case the string
					you are printing is a source file ! */
			space;
			while ((*(kurrent + base_p) != '\0') && count-- &&
				!( intrupt)) putjch(*(kurrent++ + base_p));
			}
		if (mod_access & WRITE)
			{
			out_loc(base_p,kurrent,0);
			prompt;
			while ((tmp = getjch()) != '\n' && tmp != EOF)
				*(kurrent++ + base_p) = tmp;
			/* note null deliberately not appended to string */
			}
		}
	if (!(mod_access & WRITE)) vputjch('\n');
	}

/* Procedures used by the ram block validate case */

tstrfrsh()	/* test dram refresh (see separate note in hardware document) */
	{
	}

/* Initialise Block to x (normally 0 or 0xFF), Returns: 1=success, 0=fail */
	int
initblk(base_p,kurrent,stop,byte)
	char	*stop ;
	x8bit byte;
	char *base_p;
	long kurrent ;
	{
	char	*tmp ;
	for (tmp = kurrent + base_p; kurrent + base_p <= stop; )
		*(kurrent++ + base_p) = byte;
	tstrfrsh();
	for (tmp = kurrent + base_p ; kurrent + base_p <= stop; )
		{
		if (*(kurrent++ + base_p) != byte)
			{
			check_err(base_p,kurrent,byte);
			return(0);
			}
		}
	return(1);
	}

	x8bit
rolb(x)	/* rotate byte 1 bit to left */
	x8bit x;
	{ if ( x & 0x80 ) return((x << 1 ) | 1); else return(x<<1); }

	x8bit
rorb(x)	/* rotate byte 1 bit to right */
	x8bit x;
	{if (x&1) return( ( x >> 1 ) | 0x80); else return( ( x >> 1 ) & 0x7F); }

check_err(base_p,kurrent,x)
	char *base_p;
	long kurrent ;
	x8bit x;
	{
	my_error("\nData lost at ");	out_loc(base_p,kurrent,0);
	prints("Data is ");		put2h(*(kurrent + base_p));
	prints(", but should be ");	put2h(x); putjch('\n');
	}

/* check byte block for correctly set rotating 1s or 0s
			(return 1 if ok, 0 if not)
			JJLATER extend to int & long blocks */
chec_blok(base_p,kurrent,dir,bits,stop)	
	char *base_p;
	long kurrent ;
	s8more	dir;
	int bits ;
	char *stop ;
	{
	x8bit byte;
	char *tmp ;

	tstrfrsh();
	if (dir == 1) /* increment through block */
		{
		for (tmp = kurrent + base_p, byte = bits;
			tmp <= stop;
			tmp++, byte = rolb(byte))
			{
			if ( *tmp != byte )
				{
				check_err(base_p,kurrent,byte);
				return(0);
				}
			}
		}
	else /* decrement through block */
		{
		for (tmp = kurrent + base_p, byte = bits;
			tmp >= stop;
			tmp--, byte = rorb(byte))
			{
			if ( *tmp != byte )
				{
				check_err(base_p,kurrent,byte);
				return(0);
				}
			}
		}
	return(1);
	}

/* Prints my_error message for ram validation case.
	For when main case validate code has detected either :
		incompletely decoded address common with a
		previous address in the ram block under test,
	OR interference between the block & this monitor's ram workspace */
shorterr(dir,initial)
	s8more	dir;
	x8bit initial;
	{
	my_error("Possible incomplete decoding with a ");
	if (dir == 1) prints("lower"); else prints("higher");
	prints(" address");
	check_err(base_p,kurrent,initial);
	}


/* Set disc to r/w (if you have changed discs while the monitor is running
	cpm will have made it read only, so make it now read/write.
	Side effect: logged disc becomes drive A/0  */
	/* JJ:ATER may need something for msdos */
disc_rw()
	{
	}

	s8more
out_open()
	{
	disc_rw();
	if ((chan = fopen(name_p, "w")) == 0)
		{
		my_error("Cant open ") ;prints(name_p);
		return(0);
		}
	dump = 1;
	return(1);
	}

	s8more
in_open()
	{
	if (kurrent ==0 && base_p == (char *)0)
		{
		my_error("Probable overwrite of monitor - so aborted");
		return(0);
		}
	if ((chan = fopen(name_p,"r")) == 0)
		{
		my_error("Cant open ") ; prints(name_p);
		return(0);
		}
	load = 1;
	return(1);
	}

status()	/* Print Monitor Status */
	{
	vprints("Status: ");
	out_loc(base_p,kurrent,0);
	if (mod_access & READ )		prints("Read, ");
	if (mod_access & WRITE )	prints("Write, ");
	if (( size) == BYTE )		prints("Byte");
	if (( size) == WORD )		prints("Word");
	if (( size) == LONG )		prints("Long");
	if (( size) == STRING )		prints("Text");
					prints(", Hex");
	if ( ascii ) 			prints("+Ascii,");
	else 				prints(" Only,");
	if ( verbose ) 			prints(" Verbose,");
		else 			prints(" Quiet,") ;
					prints(" \'");
					prints(name_p);
					prints("\'\n");
	}

main(argc,argv)
	int argc ;
	char **argv ;
	{
		union
	cludge	{
		char	s1;
		unsigned s2;
		long	s4;
		} data_union ;
	
	char *base_p;
	long kurrent ;

	x8bit	invert_mem ;	/* memory of if pointers to be inverted */
	s8more	command;	/* command character */
	int	(*procptr)();	/* procedure pointer */
	x8more	count1,count2;	/* general count variable */
	x8more	forever;	/* loop forever flag */
	s8more	validir;	/* validate case: values 1, 0, -1, represent
				whether incrementing, or decrementing through
				block, or finished */
#define FINDSTR 30
	char	findstr[FINDSTR];
	x8bit	bits1,bits2,bits3;	/* ram validate case: bit patterns of least
				significant byte in the block */
	x8more	init0F; 	/* ram validate case: 0 or FF block
					initialise data */
	s8more	chint;		/* gen. purpose char declared as int
					so can get EOF */
	u16bit	load_tmp;	/* used by load object case */
	x8more	eof_flg;	/* used by load object case,
				0 until an EOF record encountered, 1 after */
	long	length ;

	ARGV = *argv ;
	colds:		/* cold start point */

	/* initialise target micro port here */

#ifdef unix	/* { */
	/* JJ add something */
#endif		/* } */
	/* ifdef MSDOS nothing necessary */

	printf("This monitor is not yet debugged.\n");
	prints(sccsID);
	prints(menu);
	kurrent = 0;
	size = BYTE ; mod_access = READ ;
	verbose = 1; ascii = 1; supres = 0;
	name_p = &initnam[0] ;
	warms:		/* Warm Start Point */
	hexcom = 0;
	putchar('\n');
	for (;;)	/* Main Command Loop Of Monitor */
		{
		if ((hexcom) && ( command == '+' || command == '.' || command == '-' ))
			{	/* user wants to chain write, he has previously
				typed a '+', '-', or '.' */
			command = hexcom ;
			hexcom = 0 ;
			}
		else	{
			intrupt = 0;	/* ensure prompt string will appear */
			/* vputjch('\n'); only needed if keyboard doesnt have local echo */
			vprints( "> " );
			command=getjch();
			vprints("\b\b\b   \b\b\b");
			}
		intrupt = 0 ;	/* flush any interrupts */
		if (command <= 'Z' && command >= 'A') command += 'a' - 'A' ;
		switch(command) {
			case EOF:my_error("End of file read.\n");
				break ;
			case'\n':/* NULL :ignore carriage returns & line feeds */
				break ;
			case'0':/* Set monitor back to initial mode */
				kurrent = 0 ;
				base_p = (char *)0 ;
				size = BYTE ; mod_access = READ ;
				verbose = 1; ascii = 1;
				name_p = &initnam[0] ;
				invert_mem = 0 ;
				/* no break */
			case's':status();
				break;
			case'r':vprints("Read mode." );vputjch('\n');
				mod_access = READ ;
				break;
			case'w':vprints( "Write mode." );vputjch('\n');
				mod_access = WRITE ;
				break;
			case'm':vprints( "Modify mode (= read then write)." );
				vputjch('\n') ;
				mod_access = MODIFY ;
				break;
			case'\"':vprints( "Text mode." );vputjch('\n');
				size = STRING ;
				break;
			case'1':vprints("Byte mode." ); vputjch('\n') ;
				size = BYTE ;
				break;
			case'2':vprints("Word mode." ); vputjch('\n') ;
				size = WORD ;
				break;
			case'4':vprints("Long mode." ); vputjch('\n') ;
				size = LONG ;
				break;
			case'v':prints( "Verbose mode." ); vputjch('\n') ;
				verbose = 1;
				break;
			case'q':vprints( "Quiet mode." ); vputjch('\n') ;
				/* Tell user before you stop talking to them */
				verbose = 0;
				break;
			case'\'':vprints("Ascii+hex mode."); vputjch('\n');
				ascii = 1;
				break;
			case'h':vprints("Hex only mode." ); vputjch('\n') ;
				ascii = 0;
				break;
			case 'a': vprints("how many bytes");
				prompt();  
				if ((base_p = malloc(get8h)) == (char *)0)
				my_error("Cant get that much memory.\n") ;
				break ;
			case'k':vprints("kurrent address = ? ");
				kurrent = getadr();
				out_loc(base_p,kurrent,0);
				break ;
			case'b':vprints( "base_p address = ? ");
				base_p=(char *)getadr();
				out_loc(base_p,kurrent,0);
				break;
			case'n':vprints("File name = ? ");
				jgets(&filnam[0],FILE_LN) ;
				name_p = &filnam[0] ;
				break ;
			case'e':prints( "Exiting Monitor.\n" );
				exit(0); 
				break;
			case'g':vprints("Goto where ? " );
				kurrent = get8hex ;
				prints("\nNot available: Goto ");
					/* C wont allow a changeable goto
					(exept by using machine dependant
					self modifying code). */
				out_loc(base_p,kurrent,0);
				break ;
			case'x':prints( "Call where ? " );
				kurrent = get8hex ;
				vprints( "Calling");
				out_loc(base_p,kurrent,0);
				procptr = kurrent + base_p;
				(*procptr)();
				break;
			case'.':/* Access at same address, (useful if you have
					just changed modes, or wish to
					repeatedly access a port */
				present(base_p,kurrent,0) ;
				break;
			case'+':/* advance & display */
				present(base_p,kurrent,1);
				break;
			case'-':/* back & display */
				present(base_p,kurrent,-1);
				break;
			case'!':prints( "SWI set at " );
				out_loc(base_p,kurrent,0);
#ifdef CPM
#define BREAKPOINT	0xF2
#endif
#ifdef MSDOS
#define BREAKPOINT	0 /* look up correct value later */
#endif
#ifdef ns32000
#define BREAKPOINT	0xF2 
#endif
				/* set as appropriate for machine & remember
					to type cast for the correct size */
				*(kurrent + base_p) = BREAKPOINT;
				break;
			/* Processor status */
			case'p':my_error( "Processor Status Not Implemented\n");
				/* use #ASM, do a SWI (Software Interrupt),
				then do a stack unwind */
				break;
			case'd': /* a block of text or string */
				vprints( "Display");	
				if (size != STRING)
					{
					tmp_l = kurrent ;
					prints(": LENGTH ? ");
					length = get8hex() ;
					if (length < 0)
						{
						my_error("Negatives not allowed\n");
						break ;
						}
					if (size == WORD) length *= 2 ;
					if (size == LONG) length *= 4 ;
					stop_p = base_p + kurrent + length ;
					count1 = 0 ;	/* count of items on line */
					while ((kurrent + base_p < stop_p) && !( intrupt))
						{
						if (!count1) out_loc(base_p,kurrent,1) ;
						if (size == BYTE)
							{
#define DISP_LEN	0x10
							count1 = DISP_LEN;
							while (count1)
								{
								if (!((base_p != (char *)0)
									&& (ascii) &&
									((count1%4) ||
									(count1 == DISP_LEN) )))
									putjch(' ');
								if (kurrent + base_p <= stop)
									put2h(*(kurrent + base_p));
								else	/* space out to start position
										for ascii */
									vprints(" \040");
								count1--;
								}
							/* now print ascii equivalent of the previous hex */
							if ( ascii)
								{
								putjch(' ');
								count1 = DISP_LEN ;
								while(count1 && (kurrent + base_p <= stop))
									{
									if (isletter(*(kurrent + base_p)))
										putjch(*(kurrent + base_p));
									else putjch('.');
									kurrent++  ;
									count1-- ;
									/* if (count1 == 8) putjch(' '); */
									}
								}
							}
						else if ( size == WORD)
							{
							if (!count1) count1 = 0x8 ;
							out4h(*(int *)(kurrent + base_p));
							kurrent += 2 ;
							count1--;
							}
						else if ( size == LONG )
							{
							if (!count1) count1 = 0x4;
							out8h(*(long *)kurrent + base_p)) ;
							kurrent += 4;
							count1--;
							}
						}
					kurrent = tmp_l ;
					}
				else /* (size == STRING) */
					{
					/* monitor ends at null after displaying */
					while (!intrupt && (chint = *(kurrent++ base_p)) )
						if (isletter((char)chint))
							putjch((char)chint);
					vprints("\nSTART WAS"); /* where string ends */
					outadr(mem_p);
					}
				vputjch('\n') ;
				break;
			case')':prints( "Dump Text\n" );
				if (!out_open()) goto warms;
				while (!intrupt)
					{
					chint = *(kurrent++ + base_p);
					if (chint) vputjch(chint); else break;
					}
				fclose(chan) ; dump = 0;
				vputjch('\n');
				break;
			case'(':prints( "Loading Text\n" );
				if (!in_open()) goto warms;
				while(!intrupt && (chint = getjch() ) != EOF)
					*(kurrent++ + base_p) = chint;
				*(kurrent + base_p) = '\0';
				/* note we do add a null here, unlike elsewhere, 
					where we are merely patching strings */
				fclose(chan);
				load = 0;
				vputjch('\n');
				break;
			case'>':prints( "Dump Object : Stop Address ? " );
					prints(": LENGTH ? ");
					length = get8hex() ;
					if (length < 0)
						{
						my_error("Negatives not allowed\n");
						break ;
						}
				tmp = kurrent ;
				if (!out_open()) goto warms;
				count1 = 0 ;	/* count of binary data bytes */
				stop = base_p + kurrent + length ;
				while ((kurrent + base_p <= stop) && !intrupt)
					{
					if (count1 == 0)
						{
						putjch(':');
						cksum = 0 ;
#define OBJ_OUT 0xf	/* presumably bytes in an intel hex dump */
						/* max spec allows is 0x20 */
						if (stop - (kurrent + base_p) < OBJ_OUT)
							put2h((x8bit)(stop - (kurrent + base_p) +
							 1 ) & 0xFF) ;
						else put2h((x8bit)OBJ_OUT);
						put4h((u16bit)kurrent);
							/* Address of start of record
							(spec is 16 bit address) */
						put2h((x8bit)0); /* record type : data*/
						}
					put2h(*(kurrent + base_p) );
					count1++ ;
					if ((count1 == OBJ_OUT) ||
						((kurrent + base) == stop))
						{
						cksum = ~cksum + 1 ;
						put2h(cksum & 0xFF);
						putjch('\r'); putjch('\n');
						count1 =0;
						}
					kurrent++  ;
					}
				/* append an EOF record */
				putjch(':');
				cksum = 0;
				put2h(0);
				put4h((int)base_p);
				put2h(1);	/* record type = data */
				cksum = ~cksum + 1 ;
				put2h(cksum) ;
				putjch('\r'); putjch('\n');
				fclose(chan) ;
				dump = 0;
				kurrent = tmp;	/* restore 'kurrent' */
				break;
			case'<':prints( "Loading Object\n" );
				/* exit by giving a non data record type ( != 0 ).
				*  slight bug : if a checksum my_error occurs,
				*  data record is written to ram regardless,
				*  checksum my_error is not detected until end of line
				*/
				tmp = kurrent ;
				eof_flg = 0;
				if (!in_open()) goto warms;
				while(!eof_flg && !intrupt)
					{
					/* get beginning of record delimiter */
					while((chint = getjch() ) != ':')
						if ( chint == EOF )
							{
							prints("End of file.");
							load = 0;
							fclose(chan);
							goto warms;
							}
					cksum = 0 ;
					count1 = take2h();	/* get data byte count */
#define OBJ_IN	0x20
				/* max allowed by mostek definition */
					if(count1 > OBJ_IN && count1 != 0)
						{
						my_error("Bad Count");
						break;
						}
					/* get record start address */
					kurrent = take2h() ;
					kurrent <<= 8 ;
					data.s4 = take2h() ;
					kurrent |= data.s4 ;
					if ((load_tmp = take2h()) == 1) 
						eof_flg = 1 ;
					/* load ram with data */
					while (count1--)
						*(kurrent++ + base_p)
							= take2h();
					/* calculate checksum from data */
					data.s1 = ~cksum +1 ;
					/* compare with checksum from record */
					if ( data.s1 != take2h())
						{
						my_error("Bad Checksum");
						break;
						}
					}
				/* have we received an EOF record ? */
				if (!eof_flg) my_error("No EOF Record.");
				else
					{
					vprints("\nTHE EOF ADDRESS WAS ");
					putadr((x32bit)load_tmp);
					putjch('\n');
					/* note cannot output this number earlier
					as it would corrupt checksum */
					}
				fclose(chan);
				load = 0;
				kurrent = tmp ;
				break;
			case'f':vprints("Fill : ");
				prints("Length, ");
				length = get8hex();
				if (length < 0)
					{
					my_error("Negatives not allowed\n");
					break ;
					}
				tmp_l = kurrent ;
				if (size != STRING)
					stop = length + kurrent + base_p ;
				else	{
					while (*(kurrent++ + base_p)) ;
					stop = kurrent + base_p - 1 ;
					/* dont include nul */
					}
				vprints("Data : ");
				if (( size == BYTE) || ( size== STRING ))
					{
					data_union.s1 = get2h();
					while(( kurrent + base_p <= stop)
						&& !intj())
						{
						*(kurrent + base_p) =
							data_union.s1;
						kurrent++ ;
						}
					}
				else if ( size == WORD)
					/* Note the byte beyond 'stop'
						may be affected*/
					{
					data_union.s2 = get4h();
					while(( kurrent + base_p <= stop)
						&& !intj())
						{
						*(int *)(kurrent + base_p)
							= data_union.s2;
						kurrent += 2;
						}
					}
				else if (size == LONG )
					{
					/* Note up to 3 bytes beyond 'stop'
						may be affected */
					data_union.s4 = get8h();
					while(( kurrent + base_p <= stop)
						&& !intj())
						{
						*(long *)(kurrent + base_p)
							= data_union.s4 ;
						kurrent += 4;
						}
					}
				break;
			case'/':prints( "Search : Data ? " );
#define TOP	0x1000000
/* NS 16000 is 24 bit address, not 32 so cut short the search */
				if ( size == BYTE)
					{
					data_union.s1 = get2h();
					do	{
						if (*(kurrent + base_p)
							== data_union.s1) 
							{
							vprints(
							"Search succeeded");
							break ;
							}
						}
						while ( (kurrent++ + base_p != 
							(char *)TOP)
							&& !intj());
						vprints( "Search failed");
						break ;
					}
				else if ( size == WORD)
					{
					data_union.s2 = get4h();
					do	{
						if (*((int *)(kurrent + base_p))
							== data_union.s2) 
							{
							vprints(
							"Search succeeded");
							break ;
							}
						}
						while ( ((kurrent +=2) + base_p <
							(char *)TOP -1)
							&& !intj());
						vprints( "Search failed");
						break ;
					}
				else if ( size == LONG)
					{
					data_union.s4 = get8h();
					do	{
						if (*((int *)(kurrent + base_p))
							== data_union.s4) 
							{
							vprints(
							"Search succeeded");
							break ;
							}
						}
						while ( ((kurrent += 3) + 
							base_p < (char *)TOP -1)
							&& !intj());
						vprints( "Search failed");
						break ;
					}
				else /* String */
					{
					jgets(findstr,FINDSTR);
					prints(" LOOKING FOR \'");
					prints(findstr);
					putjch('\'');
					do	{
						if ( *(kurrent + base_p) == *findstr)
							{
							for (mem_p = findstr + 1 ,
								stop = kurrent + 
									base_p + 1;
								*(char *)stop ==
								*(char *)mem_p &&
								*(char *)stop != '\0' ;)
								{
								stop++ ;
								if (stop == TOP) stop
									= 0;
								mem_p++ ;
								}
							if (*(char *)mem_p == '\0' )
								break ;
							}
						kurrent + base_p++ ;
						if (kurrent + base_p == TOP)
							kurrent = 0;
						} while ( kurrent + base_p != dest && !intj()) ;
					}
				if (kurrent + base_p == dest) 
					{
					my_error("Not found.\n");
					kurrent = tmp ;
					}
				out_loc(base_p,kurrent,0);
				vputjch('\n');
				break;
		case't':vprints("Test RAM: up to ? ");
	/*			Algorithm enhanced from an article by
	*				Edward J Milner, NASA
	*				Lewis Research Center,
	*				Published Electronic Design News
	*				October 13th 1983.
	*			Synopsis of article :
	*				slide a 1 thru succesive incrementing bytes,
	*					  ( having initialised block to 0 )
	*					1		dec		0
	*					1		inc		FF
	*					1		dec		FF
	*				repeat all previous 8 times, shifting start
	*					bit one bit each time,
	*				repeat all with a sliding 0 instead of a 1.
	*			Thus the first test is : Sliding one, Zero initial,
	*				Incrementing addresses, rotate a bit to the left
	*				through succesive bytes in the block,
	*				first testing each byte to see if
	*				a previous cycle of the loop (to
	*				access a lower address) has also erroneously
	*				set the contents of the current address
	*/
		dest = kurrent; stop = getadr();
		/* insert code here to generate data for 'tstrfrsh' */
		prints("Number of laps (0=infinite) ? ");
		count1 = get2h();
		forever = (count1 != 0) ? 0 : 1 ;
		while (count1 || forever)
			{
			count1--;
			/* First a sliding 1 test, then a sliding 0 */
			for (bits1=1; ; bits1 = ~1)
				{
				/*For each of the 8 bits within a byte*/
				for (bits2 = bits1, count2 = 0;
					count2 < 8 ;
					bits2 = rolb(bits2), count2++)
					{
					/* first initialise block low,
						then high */
					for (init0F = 0; ;
						init0F = 0xFF)
						{
						/* Sweep block first
						incrementing addresses,
						then decrementing */
						for (validir = 1; ;
							validir = -1)
							{
							if (verbose)
							{
							putchar('\n');
							putadr((x32bit)
								(dest+(long)base_p));
							putchar(':');
							putadr((x32bit)
								(stop+(long)base_p));
							if (forever) prints(" FOREVER");
							else	{
								prints(" LAP=");
								put2h(count1);
								}
							prints(", sliding ");
							if (bits1 == 1) putchar('1');
							else putchar('0');
							prints(", bit ");
							putchar('0'+count2);
							prints(", block start ");
							if(!init0F) prints("00");
							else prints("FF");
							prints(", sweep ");
							if (validir == 1) putchar('+');
							else putchar('-');
							}
							/* initialise block */
							if (!initblk(
							base_p,kurrent,init0F) )
								goto warms;
							/* set bit pattern in block */
							for ( bits3 = bits2,
								kurrent = dest;
								kurrent <= stop;
								bits3 = rolb(bits3),
								kurrent++)
								{
								if (intj()) goto warms;
								kurrent + base_p = kurrent + base_p ;

								if (*(x8bit *)kurrent + base_p !=
									init0F)
									{
									shorterr(validir
									,init0F);
									goto warms;
									}
								*(x8bit *)kurrent + base_p = bits3 ;
								}
							/* check bit pattern in block */
							if (validir == 1)
								{
								if (!chec_blok(base_p,kurrent,1,bits2))
								goto warms;
								}
							else	{
								if (!chec_blok(base_p,kurrent,-1,
									rorb(bits3)))
									goto warms;
								}
							if (validir != 1) break ;
							}
						if (init0F != 0) break ;
						}
					}
				if (bits1 != 1) break ;
				}
			}
		kurrent = dest ;
		putchar('\n');
		break;
			case'c':vprints("Copy: ");
				if (size != STRING) upto() ;
upto() { prints("Up to, "); }

				prints("destination = ? ");
				if (size != STRING)	/* get top of source block */
					{
					stop = getadr();
					stop = stop + (long)base_p;
					}
				dest = getadr() ;	/* get base_p of destination */
				dest = dest + (long)base_p;
				/* now process */
				if ((size == WORD) && ((stop - kurrent + base_p) % 2 )
					|| (size == LONG) && ((stop - kurrent + base_p) % 4))
					{
					my_error( "Length quantisation" );
					break ;
					}
				if (size == STRING )
					{ /* set 'stop' so we can
						treat it as a block */
					stop = kurrent + base_p;
					while (*(char *)stop++) ;
					/* note the null will be copied */
					}
				else if (size == WORD) stop++  ;
					/* note we will be copying the byte
						above 'stop' as well. */
				else if (size == LONG) stop += 3 ;
					/* ditto 3 bytes */
		if (stop < kurrent + base_p)
			{
			mem_p = kurrent + base_p ;
			kurrent + base_p = stop ;
			stop = mem_p ;
			}
				/* Now do the copy, note this has not been written
				to copy through zero */
				if (dest < kurrent + base_p)
					{
					do	{
						*(char *)dest = *(char *)kurrent + base_p;
						dest++  ; kurrent + base_p++  ;
						} while ( kurrent + base_p <= stop ) ;
					}
				else	{
					dest += stop - kurrent + base_p ;
					do	{
						*(char *)dest = *(char *)stop ;
						stop-- ; dest-- ;
						} while ( stop >= kurrent + base_p ) ;
					}
				break;
	case'?':
prints(  "Set  Address : K Current  B Base	/ Search");
prints("\nSet  R/W     : R Read     W Write	M Modify=R+W");
prints("\nSet  Size    : 1 Byte     2 Word	4 Double     \" Text");
prints("\nSet  Mode    : \' Ascii    H Hex	 V Verbose    Q Quiet");
prints("\nEntity Access: + Next     . Same	- Previous   ! SWI Set");
prints("\nBlock Access : D Display  C Copy	F Fill	     T Test");
prints("\nFile	Access : N Name     > Dump Obj	< Load Obj   ) Dump Text ( Load Text");
prints("\nStatus Print : S Monitor  P Processor");
prints("\nMemory Adjust: a alloc()");
prints("\nControl      : E Exit    ^B Interrupt 0 Restart    X Execute	 G Goto\n");
		status();
		break;
	default:my_error("BAD COMMAND, TRY \'?\'\n");
		break;
		}
		}
	}
