/* KLH10.C - Main for KLH10 (also Front End Console for now)
*/
/* $Id: klh10.c,v 2.9 2002/05/21 16:54:32 klh Exp $
*/
/*  Copyright © 1992, 1993, 2001 Kenneth L. Harrenstien
**  All Rights Reserved
**
**  This file is part of the KLH10 Distribution.  Use, modification, and
**  re-distribution is permitted subject to the terms in the file
**  named "LICENSE", which contains the full text of the legal notices
**  and should always accompany this Distribution.
**
**  This software is provided "AS IS" with NO WARRANTY OF ANY KIND.
**
**  This notice (including the copyright and warranty disclaimer)
**  must be included in all copies or derivations of this software.
*/
/*
 * $Log: klh10.c,v $
 * Revision 2.9  2002/05/21 16:54:32  klh
 * Add KLH10_I_CIRC to allow any sys to have CIRC
 *
 * Revision 2.8  2002/05/21 10:01:22  klh
 * Add ub_debug param.
 * Allow access to HZ vars even if using synch clock.
 *
 * Revision 2.7  2002/04/24 07:56:08  klh
 * Add os_msleep, using nanosleep
 *
 * Revision 2.6  2002/03/28 16:51:04  klh
 * Tweak to fc_quit
 *
 * Revision 2.5  2002/03/21 09:50:08  klh
 * Mods for CMDRUN (concurrent mode)
 *
 * Revision 2.4  2001/11/10 21:28:59  klh
 * Final 2.0 distribution checkin
 *
 */

#include <stdio.h>
#include <stdlib.h>	/* Malloc and friends */
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#include <errno.h>
#include <time.h>
#include <stdarg.h>	/* For error-reporting functions */

#include "klh10.h"
#include "kn10mac.h"	/* FLD macros */
#include "kn10def.h"
#include "kn10dev.h"
#include "kn10ops.h"
#include "wfio.h"
#include "fecmd.h"
#include "feload.h"
#include "prmstr.h"
#include "dvcty.h"	/* For cty_ functions */

#if KLH10_CPU_KS
# include "dvuba.h"	/* So can get at device info */
#endif

#ifdef RCSID
 RCSID(klh10_c,"$Id: klh10.c,v 2.9 2002/05/21 16:54:32 klh Exp $")
#endif

/* Exported functions */
void klh10_main(int argc, char **argv);
void fe_aprcont(int, int, vaddr_t, int);
void fe_shutdown(void);
void fe_traceprint(w10_t, vaddr_t);
void fe_begpcfdbg(FILE *f);
void fe_endpcfdbg(FILE *f);
void pishow(FILE *);
void pcfshow(FILE *, h10_t);
void insprint(FILE *, int);
/* void panic(char *, ...); */	/* Declared in kn10def.h */

/* Imported functions */
extern void apr_init(void);
extern void apr_init_aprid(void);
extern int apr_run(void);

/* Local function kept external for easier debug access  */
void errpt(void);


/* Local variables */

int proc_bkgd = FALSE;	/* TRUE if want to run in background */
int fedevchkf = FALSE;	/* TRUE to do periodic device attention checks */
ospri_t proc_pri = 0;	/* CPU process priority on host OS */
int cmdpromptnew = TRUE;	/* Set whenever cmdprompt changed */
char *cmdprompt =	/* Set initial command prompt */
#ifdef KLH10_CMDPROMPT
	KLH10_CMDPROMPT;
#else
	":KLH10# :KLH10> :KLH10>> ";
#endif

char *ld_fmt = NULL;	/* Current load file format */
char *ld_dfmt =		/* Default format if none specified */
#if KLH10_SYS_ITS
		"u36";	/* Alan's Unixified format */
#else
		"c36";	/* Core-Dump format */
#endif
struct loadinfo ld_inf;	/* Args to and results from loader */

enum fevmmode {		/* FEVM_XMAP memory mapping mode defs */
	FEVM_DFLT,	/* Use FE default mode */
	FEVM_CUR,	/* Current machine mode mapping */
	FEVM_PHYS,	/* Force physical map */
	FEVM_EXEC,	/* Force exec map */
	FEVM_USER,	/* Force user map */
	FEVM_ACB	/* Select AC block */
};

/* DDT mode variables */
vaddr_t ddt_loadsa;		/* Last loaded start address */
vaddr_t ddt_cloc;		/* Current location (.) */
enum fevmmode ddt_clmode;	/* Current location FEVM mode */
w10_t ddt_val;			/* Last value */

/* Flags to pinstr() */
#define PINSTR_OPS 01	/* Show operands of instr */
#define PINSTR_EA 02	/* Use E furnished */
void pinstr(FILE *, w10_t, int, vaddr_t);

/* Local Function predeclarations */

static void klh10_init(void);
static void mem_init(void), mem_term(void);
static int mem_setlock(FILE *, FILE *, int);
static void swinit(int, char **);
static int aprhalted(void);
static void wd1print(FILE *, w10_t);
static void wd2print(FILE *, w10_t);
static void addrprint(FILE *, vaddr_t, enum fevmmode);
static void nextinsprint(FILE *, int);
static void easymprint(FILE *, vaddr_t);
static int addrparse(char *, vaddr_t *, enum fevmmode *);
static char *strf6(char **, w10_t);
static struct tm *timefrits(struct tm *, w10_t);

vmptr_t fevm_xmap(vaddr_t, enum fevmmode);	/* FE memory mapping */

/****************** Command Tables and dispatch ******************/

/* New version of command parser -- succumb to GDB, DBX, UNIX influence.
**
** Old version used an even simpler one-character instant-action parser.
*/

static FILE *cminfile = NULL;	/* Set if reading commands from file */
static char *cminfname;		/* Filename */
static int cminchar(void);	/* Funct to read from file or TTY */

#define CMDQCHAR '\\'	/* Quote char for token parsing */

#ifndef CMDBUFLEN
# define CMDBUFLEN 512
#endif
#ifndef CMDMAXARG
# define CMDMAXARG 10
#endif

struct cmd_s {
	int cmd_flags;		/* State flags */
	char *cmd_prm;		/* Pointer to command prompt */
	char *cmd_buf;		/* Pointer to start of buffer */
	size_t cmd_blen;	/* Size of buffer */
	int cmd_left;		/* # chars left for current cmd being input */
	char *cmd_inp;		/* Input deposit pointer */
	char *cmd_rdp;		/* Readout pointer */
	size_t cmd_rleft;	/* # chars left to read  */

	/* Provide all command routines with their desired arguments */
	char *cmd_arglin;	/* Original pointer to start of args on line */
	int cmd_argc;			/* # of tokens */
	char *cmd_argv[CMDMAXARG+1];	/* Array of token pointers */
	char *cmd_tdp;			/* Next free loc in token buffer */
	size_t cmd_tleft;		/* # chars free in token buffer  */
	char cmd_tokbuf[CMDBUFLEN+CMDMAXARG];

#if 0
	char *cmd_wbf;		/* Pointer to work buffer */
	size_t cmd_wblen;	/* Size in chars */
	char *cmd_wbp;		/* Current deposit ptr */
	size_t cmd_wbleft;	/* # chars left */
#endif
} command;

#define CMDF_ACTIVE 01	/* Activation char seen, execute accumulated cmd */
#define CMDF_INACCUM 02	/* In accumulation phase */
#define CMDF_NOPRM 040	/* Disable prompt */

static char cmdbuf[CMDBUFLEN];	/* Original command string buffer */
#if 0
static char cmdwbf[CMDBUFLEN];	/* Working buffer */
#endif

struct cmkey_s {
	char *cmk_key;
	union cmnode *cmk_p;
};
struct cmrtn_s {
	void (*cmr_vect)(struct cmd_s *);  /* Function to call */
	int cmr_flgs;		/* Misc flags */
	char *cmr_synt;		/* Arg syntax */
	char *cmr_help;		/* Short one-line help */
	char *cmr_desc;		/* Long description */
};

#define CMRF_NOARG 01	/* Command takes no args */
#define CMRF_TOKS  010	/* Command wants whole line tokenized, via cm */
#define CMRF_TLIN  020	/* Command wants overall line arg, via cm */
#define CMRF_CMPTR 040	/* Command wants just cmd state ptr */

union cmnode {			/* All possible nodes for a keyword */
	struct cmrtn_s cmn_rtn;
};


/* Predeclarations */
void cmdinit(struct cmd_s *, char *, char *, size_t);
int cmdexec(struct cmd_s *);
int cmdaccum(struct cmd_s *);

struct cmkey_s *cmdkeylookup(char *, struct cmkey_s *, struct cmkey_s **);
char *cmdlsetup(struct cmd_s *);
static void slinlim(char *);


/* CMDDEF is used to define top-level commands.  It does not accumulate
**	them into a table (C is far too puny for that) but gathers together
**	various information that a higher-level table can then point to.
*/
#define CMDDEF(deflab, func, flgs, argsyn, minihelp, longdesc)	\
    static void func(struct cmd_s *);				\
    static struct cmrtn_s deflab = { func, flgs, argsyn, minihelp, longdesc };

CMDDEF(cd_ques,  fc_ques,   CMRF_NOARG,	NULL,
				"How to get help", "")
CMDDEF(cd_help,  fc_help,   CMRF_TOKS,	NULL,
				"Basic help", "")
CMDDEF(cd_quit,  fc_quit,   CMRF_NOARG,	NULL,
				"Quit emulator", "")
CMDDEF(cd_load,  fc_load,   CMRF_TOKS,	"<file>",
				"Load binary into KN10", "")
CMDDEF(cd_dump,  fc_dump,   CMRF_TOKS,	"<file>",
				"Dump binary from KN10", "")
CMDDEF(cd_go,	 fc_go,     CMRF_TLIN, 	"[<addr>]",
				"Start KN10 at address", "")
CMDDEF(cd_shutdown,fc_shutdown,CMRF_NOARG,NULL,
				"Halt OS gracefully", "")
CMDDEF(cd_reset, fc_reset,  CMRF_NOARG,	NULL,
				"Halt & Reset KN10", "")
CMDDEF(cd_exa,	 fc_exa,    CMRF_TLIN,	"[<addr>]",
				"Show word at address", "")
CMDDEF(cd_exnext,fc_exnext, CMRF_NOARG,	NULL,
				"Show Next word", "")
CMDDEF(cd_exprev,fc_exprev, CMRF_NOARG,	NULL,
				"Show Previous word", "")
CMDDEF(cd_dep,   fc_dep,    CMRF_TOKS,	"<addr> <val>",
				"Deposit value at address", "")
CMDDEF(cd_bkpt,  fc_bkpt,   CMRF_TLIN,	"<addr>",
				"Set breakpoint at PC loc", "")
#if 1 /* New setup */
CMDDEF(cd_step,  fc_step,   CMRF_TLIN,	"<#>",
				"Single-Step # KN10 instrs", "")
CMDDEF(cd_proc,  fc_proc,   CMRF_NOARG,	NULL,
				"Proceed KN10 without CTY", "")
#else
CMDDEF(cd_step,  fc_step,   CMRF_NOARG,	NULL,
				"Single-Step KN10", "")
CMDDEF(cd_proc,  fc_proc,   CMRF_TLIN,	"<#>",
				"Proceed # instrs", "")
#endif
CMDDEF(cd_cont,  fc_cont,   CMRF_NOARG,	NULL,
				"Continue KN10", "")
CMDDEF(cd_view,  fc_view,   CMRF_NOARG,	NULL,
				"View KN10 status", "")
CMDDEF(cd_set,   fc_set,    CMRF_TLIN,	"[<var> [<val>]]",
				"Set/show KLH10 variables", "")
CMDDEF(cd_trace, fc_trace,  CMRF_NOARG,	NULL,
				"Toggle execution trace", "")
CMDDEF(cd_halt,  fc_halt,   CMRF_NOARG,	NULL,
				"Halt KN10 immediately", "")
CMDDEF(cd_zero,  fc_zero,   CMRF_NOARG,	NULL,
				"Zero first 256K memory", "")
CMDDEF(cd_devload,fc_devload, CMRF_TLIN,
			"<New-drivername> <pathname> <initsym> <comments>",
			"Load dynamic-library device driver", "")
CMDDEF(cd_devdef,fc_devdef, CMRF_TLIN,
			"<New-devid> {ub<#>,<dev#>} <drivername> <params>",
			"Define, bind, and initialize device", "")
CMDDEF(cd_devshow,fc_devshow, CMRF_TLIN,
			"[<devid>]",
			"Show device driver & definition binding info", "")
#if KLH10_EVHS_INT
CMDDEF(cd_devevshow,fc_devevshow, CMRF_TLIN,
			"[<devid>]",
			"Show device event registration info", "")
#endif
CMDDEF(cd_dev_cmd,fc_dev_cmd,    CMRF_TLIN,
			"<devid> <device-command-line>",
			"Execute device-dependent command", "")

CMDDEF(cd_devboot,fc_devboot,    CMRF_TLIN,
			"<devid> [halt]",
			"Boot from device", "")
CMDDEF(cd_devmnt,fc_devmnt,      CMRF_TLIN,
			"<devid> <pathname> [<options>]",
			"Mount device media", "")
CMDDEF(cd_devunmnt,fc_devunmnt,  CMRF_TLIN,
			"<devid>",
			"Unmount device media", "")
CMDDEF(cd_devdbg,fc_devdbg,    CMRF_TLIN,
			"<devid> [<debugval>]",
			"Set device debug value (0=none)", "")
CMDDEF(cd_devwait,fc_devwait,   CMRF_TLIN,
			"[<devid>] [<secs>]",
			"Wait for device (or all devs)", "")
#if KLH10_DEV_LITES
CMDDEF(cd_lights,  fc_lights,   CMRF_TLIN,	"<hexaddr>",
				"Set console lights I/O base address", "")
#endif


#define KEYSBEGIN(name)	struct cmkey_s name[] = {
#define KEYDEF(key,nod)   { key, (union cmnode *)(&nod) },
#define KEYSEND		  { 0, 0 } };

KEYSBEGIN(fectbkeys)
    KEYDEF("?",		cd_ques)
    KEYDEF("help",	cd_help)
    KEYDEF("exit",	cd_quit)
    KEYDEF("quit",	cd_quit)
    KEYDEF("load",	cd_load)
    KEYDEF("dump",	cd_dump)
    KEYDEF("go",	cd_go)
    KEYDEF("shutdown",	cd_shutdown)
    KEYDEF("reset",	cd_reset)
    KEYDEF("examine",	cd_exa)
    KEYDEF("next-examine",	cd_exnext)
    KEYDEF("^-examine",	cd_exprev)
    KEYDEF("deposit",	cd_dep)
    KEYDEF("breakpt",	cd_bkpt)
    KEYDEF("1-step",	cd_step)
    KEYDEF("proceed",	cd_proc)
    KEYDEF("continue",	cd_cont)
    KEYDEF("view",	cd_view)
    KEYDEF("set",	cd_set)
    KEYDEF("trace-toggle",	cd_trace)
    KEYDEF("halt",	cd_halt)
    KEYDEF("zero",	cd_zero)
    KEYDEF("devdefine",	cd_devdef)
    KEYDEF("devdebug",  cd_devdbg)
    KEYDEF("devboot",   cd_devboot)
    KEYDEF("devmount",	cd_devmnt)
    KEYDEF("devunmount",cd_devunmnt)
    KEYDEF("devwait",   cd_devwait)
    KEYDEF("devshow",	cd_devshow)
#if KLH10_EVHS_INT
    KEYDEF("devevshow",	cd_devevshow)
#endif
    KEYDEF("dev",	cd_dev_cmd)
    KEYDEF("devload",	cd_devload)
#if KLH10_DEV_LITES
    KEYDEF("lights",	cd_lights)
#endif
KEYSEND

static void error(char *, ...), syserr(int, char *, ...);

jmp_buf errenv;

int initdone = 0;

void
errpt(void)		/* Call this to restart loop */
{
    if (!initdone) {
	fprintf(stderr, "Died during startup... goodbye!\n");
	os_exit(1);
    }

    /* Return to main KLH10 command processor loop.
    ** All implementations of longjmp() had better know how to jump out of
    ** a signal handler context!
    */
    longjmp(errenv, 1);
}

/* Print out KLH10 compile-time configuration info
 */
#include "klh10s.h"	/* Define string equivs to config params! */

static void
pconfig(FILE *f)
{
    fprintf(f, "Compiled for %s on %s with word model %s\n",
			KLH10S_CENV_SYS_, KLH10S_CENV_CPU_, WORD10_MODEL);
    fputs("Emulated config:\n", f);
    fprintf(f, "\t CPU: %s   SYS: %s   Pager: %s  APRID: %ld\n",
			KLH10S_CPU_, KLH10S_SYS_, KLH10S_PAG_,
			(long)KLH10_APRID_SERIALNO);
    fprintf(f, "\t Memory: %ld pages of %d words  (%s)\n",
			(long)PAG_MAXPHYSPGS, (int)PAG_SIZE,
			(KLH10_MEM_SHARED ? "SHARED" : "private"));
    fprintf(f, "\t Time interval: %s   Base: %s",
			KLH10S_ITIME_, KLH10S_RTIME_);
#if KLH10_SYS_ITS
    fprintf(f, "   Quantums: %s", KLH10S_QTIME_);
#endif
#if KLH10_CLK_ITHZFIX
    fprintf(f, "\n\t Interval default: %dHz\n", KLH10_CLK_ITHZFIX);
#else
    fprintf(f, "\n\t Interval default: set by 10\n");
#endif
    fprintf(f, "\t Internal clock: %s\n", KLH10S_CLKTRG_);

    /* Show hardware emulation stuff first, then software features */
    fprintf(f, "\t Other:%s\n",
	    KLH10S_MCA25
	    KLH10S_I_CIRC
	    KLH10S_JPC
	    KLH10S_DEBUG
	    KLH10S_PCCACHE
	    KLH10S_CTYIO_INT
	    KLH10S_IMPIO_INT
	    KLH10S_EVHS_INT
	    );

    /* Show peripheral device drivers known at compile time */
    fprintf(f, "\t Devices:%s\n",
	    KLH10S_DEV_DTE
	    KLH10S_DEV_RH
	    KLH10S_DEV_RPXX
	    KLH10S_DEV_TM03
	    KLH10S_DEV_NI20
	    KLH10S_DEV_DZ11
	    KLH10S_DEV_CH11
	    KLH10S_DEV_LHDH
	    );
}

static void
pversion(FILE *f)
{
    fputs("KLH10", f);
#ifdef KLH10_VERSION      
    fprintf(f, " %s", KLH10_VERSION);
#endif
#ifdef KLH10_CLIENT
    fprintf(f, " (%s)", KLH10_CLIENT);
#endif
#if defined(__DATE__) && defined(__TIME__)
    fprintf(f, " built %s %s", __DATE__, __TIME__);
#endif
    fputc('\n', f);
}

static void
pgreeting(FILE *f)
{
    pversion(f);
#ifdef KLH10_COPYRIGHT
    fprintf(f, "%s\n", KLH10_COPYRIGHT);
#endif
#ifdef KLH10_WARRANTY
    fprintf(f, "%s\n", KLH10_WARRANTY);
#endif
    fputc('\n', f);
    pconfig(f);
}


void
klh10_main(int argc,
	   char **argv)
{
    /* Handle command line args/switches, if any */
    swinit(argc, argv);

    os_init();			/* Initialize any OS-dependent stuff */

    /* Ensure stdout is unbuffered, like stderr, to avoid the otherwise
    ** confusing skews between the two streams.  This must be done prior to
    ** any output on stdout.
    */
    setbuf(stdout, (char *)NULL);
    if (proc_bkgd)
	fprintf(stderr, "[Running in background]\n");
    pgreeting(stdout);		/* Print greeting message if one */

    klh10_init();		/* Do machine init and configuring */
    fe_ctyinit();		/* Initialize console, KLH10 UI */

    if (!setjmp(errenv)) {
	/* Once-only first-time stuff */
	initdone = TRUE;	/* errenv jmpbuf now set */

    } else {
	/* Recover from error catch - something called errpt(). */
	printf("<INT>");
	/* If reading from command file, abort it */
	if (cminfile) {
	    fclose(cminfile);
	    cminfile = NULL;
	    printf("[Aborted input from \"%s\"]\n", cminfname);
	}
    }
    fe_cmdloop();
}

void
fe_cmdloop(void)
{
    enum femode omode = -1;
    int prompted = FALSE;

    fe_ctyreset();		/* Reset our terminal mode stuff */
    printf("\n");		/* Make sure we prompt on a new line */

    /* Enter command parser loop */
    for (;;) {

	/* Determine new prompt if necessary */
	if (cmdpromptnew || (omode != cpu.fe.fe_mode)) {
	    cmdinit(&command, fe_cmprompt(cpu.fe.fe_mode),
		    cmdbuf, sizeof(cmdbuf));
	    omode = cpu.fe.fe_mode;
	    prompted = FALSE;
	    cmdpromptnew = FALSE;
	}
	if (cpu.fe.fe_debug)
	    fprintf(stderr, "[femode %d prompt %d]", omode, prompted);

	switch (omode) {
	case FEMODE_CMDCONF:
	case FEMODE_CMDHALT:
	    if (prompted)
		command.cmd_flags |= CMDF_NOPRM;
	    else
		command.cmd_flags &= ~CMDF_NOPRM;
	    if (!cmdlsetup(&command)) {	/* Read typein command line */
		printf("\n");		/* If failed, try again */
		break;
	    }
	    if (fedevchkf)			/* If checking for dev attn, */
		fedevchkf = dev_dpchk_ctl(TRUE);	/* do so here */
	    (void) cmdexec(&command);	/* Parse and execute */
	    break;

	case FEMODE_CMDRUN:
	    /* See if input available.  If so, parse and execute, else
	       run the CPU.
	    */
	    if (cminfile || fe_ctyintest()) {
		if (prompted)
		    command.cmd_flags |= CMDF_NOPRM;
		else
		    command.cmd_flags &= ~CMDF_NOPRM;
		if (!cmdlsetup(&command)) {	/* Read input cmd line */
		    printf("\n");		/* If failed, try again */
		    break;
		}
		(void) cmdexec(&command);	/* Parse and execute */
		break;
	    }

	    /* Nothing to do, start running! */
	    if (!prompted) {
		fputs(fe_cmprompt(cpu.fe.fe_mode), stdout);
		prompted = TRUE;
	    }
	    fe_aprcont(FEMODE_CMDRUN, 0, 0, 0);	/* Resume KN10 */
	    continue;		/* No cmd done, so don't reset "prompted" */

	case FEMODE_CTYRUN:	/* Should not happen */
	default:
	    cpu.fe.fe_mode = FEMODE_CMDHALT;
	    error("[FE invalid mode %d]", omode);
	}
	prompted = FALSE;
    }
}


void
fc_quit(struct cmd_s *cm)
{
    if (!aprhalted())
	printf("KN10 still running!\n");
    printf("Are you sure you want to quit? [Confirm]");
    fe_ctycmforce();
    switch (cminchar()) {
    case '\r':
    case '\n':
    case 'y':
    case 'Y':
    case -1:		/* EOF */
	break;
    default:		/* Anything else prevents quit */
	return;
    }
    printf("Shutting down...");

    dev_term();		/* Power off all devices that might need it */

    mem_term();		/* Flush memory in case shared */

    printf("Bye!\n");
    os_exit(0);
}

/* FE_SHUTDOWN - Attempt to bring down PDP-10 OS and quit emulator as
**	gracefully as possible *without* any user interaction.
**
** This is intended to be used when operating as a background process,
** when errors that would normally halt and await input should
** instead attempt to give both the PDP-10 OS and the emulator a
** chance to clean up before the process is killed.  This currently
** means:
** 	(1) Any request for TTY input - in bkgd mode that implies something
**		went wrong.
**	(2) Receipt of a SIGTERM software termination signal.
**
** Probably better would be a way to suspend operations and then allow
** re-attaching a TTY to the emulator -- but UNIX sucks in that regard
** and still hasn't implemented technology that existed 25 years ago!
*/

static int
fe_shuttmo(void *arg)	/* arg is ignored */
{
    printf("[Auto-shutdown timed out]\n");
    fe_shutdown();			/* Re-invoke next phase of shutdown */
    return CLKEVH_RET_KILL;		/* Won't actually return */
}

void
fe_shutdown(void)
{
    static int shutstate = 0;	/* Initial shutdown state */

    switch (shutstate) {
    case 0:	
	++shutstate;
	/* First determine whether a PDP-10 OS ever actually seemed to get
	** going; a good heuristic is to see if paging mode is on.
	** If so, attempt to trigger a shutdown.
	*/
	if (cpu.mr_paging) {
	    /** Attempt OS shutdown!
	    ** Set a clock timeout of N seconds, after which to force shutdown
	    ** anyway.  For now, set N = 3.
	    */
	    printf("[Attempting auto-shutdown]\n");
	    (void) clk_tmrget(fe_shuttmo, (void *)NULL,
					(int32) CLK_USECS_PER_SEC * 3);
	    fc_shutdown((struct cmd_s *)NULL); /* No input required for this */
	    /* Will not return */
	}

    case 1:
	++shutstate;

    case 2:
	++shutstate;
	printf("[Starting auto-quit]\n");
	dev_term();	/* Power off all devices that might need it */
			/* This should kill all DP subprocs */
    case 3:	
	++shutstate;
	mem_term();	/* Flush memory in case using shared segs */

    case 4:
	++shutstate;
	printf("[Exiting]\n");
    }
    os_exit(1);			/* Die with an error */
}

/* ERROR - Called only by FE code to report some error in interacting
**	with the user.
*/
static void
error(char *fmt, ...)
{
    fprintf(stderr, "\n");
    {
	va_list ap;
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
    }
    fprintf(stderr, "\n");

    errpt();
}


/* SYSERR - Called only by FE code to report some OS error in interacting
**	with the user.
*/
static void
syserr(int num, char *fmt, ...)
{
    {
	va_list ap;
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
    }
    fprintf(stderr, " - %s\n", os_strerror(num));

    errpt();
}


/* PANIC - called while the KLH10 is actually running, whenever something
**	 detects a situation that should be impossible or cannot be handled.
*/
void
panic(char *fmt, ...)
{
    fprintf(stderr, "\r\nKLH10 PANIC: ");
    {
	va_list ap;
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
    }
    fprintf(stderr, "\r\n  Current PC= %#lo\r\n", (long) PC_30);

    errpt();			/* For now, break directly to main loop */
}

/* Startup arg parsing and default setting.
**	For now, all we do with args is interpret the first as a command file.
**	If no arg, look for KLH10_INITFILE as default init command file.
*/

#define KLH10_SWITCHES \
    swidef(SWI_BKGD,    "background"),	/* Run in bkgnd mode */\
    swidef(SWI_HELP,    "help"),	/* Not yet */\
    swidef(SWI_VERSION, "version")	/* Not yet */
enum {
# define swidef(i,s) i
	KLH10_SWITCHES
# undef swidef
};

static char *switab[] = {
# define swidef(i,s) s
	KLH10_SWITCHES
# undef swidef
	, NULL
};

static char usage[] = "Usage: klh10 [-background] [initfile]\n";

void
swinit(int ac, char **av)
{
    FILE *f;
    char *initfile = NULL;
    char *cp;
    int res, kx1, kx2;

    while (--ac > 0) {
	cp = *(++av);
	if (*cp != '-') {
	    /* Arg that isn't a switch is assumed to be init filename */
	    if (initfile) {		/* If already have it, error */
		fprintf(stderr, "%s", usage);
		os_exit(1);
	    }
	    initfile = cp;
	    continue;
	}
	res = s_xkeylookup(cp+1, (void *)switab, sizeof(switab[0]),
			   (voidp_t *)NULL, (voidp_t *)NULL, &kx1, &kx2);
	if (res == 1) switch (kx1) {
	    case SWI_BKGD:
		proc_bkgd = TRUE;
		continue;
	}

	/* Fall through to here if bad switch or something else wrong */
	fprintf(stderr, "Unknown switch \"%s\"\n", cp);
	fprintf(stderr, "%s", usage);
	os_exit(1);
    }

    if (!initfile) {			/* If no init file explicitly given */
	initfile = KLH10_INITFILE;	/* Use default, but */
	f = fopen(initfile, "r");	/* if not there, don't complain */
    } else if (!(f = fopen(initfile, "r"))) {
	syserr(-1, "Cannot open \"%s\"", initfile);
	/* Doesn't return since init not yet finished */
    }

    if (f) {
	cminfile = f;		/* Set - read cmds from file until gone */
	cminfname = initfile;	/* Remember its name */
    }

    /* Set any other defaults necessary */
    ld_fmt = ld_dfmt;		/* Set current load/dump format to default */
}

/* Command parsing routines.
**	New full-line version, still simple-minded.
**	Eventually can go to CCMD-like or GDB-like package, but for now keep
**	the overall size small.
**
**	Some of the parsing functions are in PRMSTR so they can readily be
**	called by other modules (specifically device emulation code).
*/

void cmdreset(struct cmd_s *);
void cmdspfls(struct cmd_s *);
static void stolower(char *);
static int smatch(char *, char *);

static int cmdargs_none(struct cmd_s *cm);
static int cmdargs_one(struct cmd_s *cm, char **);
static int cmdargs_two(struct cmd_s *cm, char **, char **);
static int cmdargs_all(struct cmd_s *cm);
static int cmdargs_n(struct cmd_s *cm, int n);

void cmdinit(struct cmd_s *cm,
	     char *prompt,
	     char *ibuf,
	     size_t ilen)
{
    cm->cmd_flags = 0;
    cm->cmd_prm = prompt;
    cm->cmd_buf = ibuf;
    cm->cmd_blen = ilen;
    cmdreset(cm);
}

void
cmdreset(struct cmd_s *cm)
{
    cm->cmd_flags &= ~(CMDF_ACTIVE|CMDF_INACCUM);
    cm->cmd_left = cm->cmd_blen;
    cm->cmd_inp = cm->cmd_rdp = cm->cmd_buf;
    cm->cmd_inp[0] = '\0';
    cm->cmd_rleft = 0;
    cm->cmd_arglin = NULL;
    cm->cmd_argc = 0;
    cm->cmd_argv[0] = NULL;
    cm->cmd_tdp = cm->cmd_tokbuf;
    cm->cmd_tleft = sizeof(cm->cmd_tokbuf)-1;
}

static int
cminchar(void)
{
    register int ch;

    if (!cminfile)
	return fe_ctyin();

    if ((ch = getc(cminfile)) == EOF) {
	fclose(cminfile);
	cminfile = NULL;
	fprintf(stdout, "[EOF on \"%s\"]\n", cminfname);
	ch = '\n';		/* Try to end gracefully */
    } else {
	putc(ch, stdout);	/* Echo file input char */
	fe_ctycmforce();	/* Ensure it's out */
    }
    return ch;
}

int
cmdaccum(struct cmd_s *cm)
{
    register int ch;

    /* Output prompt if not already there */
    if ((cm->cmd_flags & CMDF_INACCUM) == 0) {
	printf("%s", cm->cmd_prm ? cm->cmd_prm : ">");
	cm->cmd_flags |= CMDF_INACCUM;
	fe_ctycmforce();	/* Force out any pending tty output */
    }

    for (;;) {
	if ((ch = cminchar()) < 0)	/* Get char from file or TTY */
	    break;			/* Break out if EOF */

	if (cm->cmd_left <= 1) {
	    error("Command line overflow (%d chars!); \"%.10s...\" flushed.\n",
			(int)cm->cmd_blen, cm->cmd_buf);
#if 0
	    fe_ctyinflush();		/* Flush any pending input */
#endif
	    cmdreset(cm);
	    return cmdaccum(cm);
	}
	
	*(cm->cmd_inp)++ = ch;
	--(cm->cmd_left);

	/* Again, simple built-in handling; no dynamic specification of
	** activation chars.
	*/
	switch (ch) {
	    /* See if last char was activation char */
	case '\r':
	case '\n':
	    cm->cmd_flags |= CMDF_ACTIVE;	/* Got one! */
	    *(cm->cmd_inp) = '\0';
	    return TRUE;
	}
    }
    return FALSE;		/* No more input */
}

/* CMDEXEC - Called when we're known to have an activated command in buffer.
**	Parse first keyword to see if it's anything recognizable, and
**	invoke appropriate function if so.
*/
int
cmdexec(struct cmd_s *cm)
{
    register char *cp;
    char tokbuf[100];
    char *tcp = tokbuf;
    size_t tcnt = sizeof(tokbuf);
    size_t fcnt = cm->cmd_inp - cm->cmd_rdp;	/* # chars left to read */
    struct cmkey_s *key, *key2;
    register struct cmrtn_s *cd;
    int argc;

    if (cpu.fe.fe_debug) {
	*(cm->cmd_inp) = '\0';
	fprintf(stderr, "[cmdexec \"%s\"]", cm->cmd_rdp);
    }

    /* Get first token on line */
    cp = s_1token(&tcp, &tcnt, &cm->cmd_rdp, &fcnt);
    if (!cp) {				/* If no token (empty line) */
	return 0;			/* just return having done nothing */
    }
    if (*cp == '\n' || *cp == '\r')	/* If EOL, just return */
	return 0;			/* without adding our own echo */
    if (*cp == ';')			/* If start of comment, */
	return 0;			/* ditto */

    /* Have token, see if it's a command */
    stolower(cp);			/* Force lowercase for lookup */
    argc = s_keylookup(cp, fectbkeys, sizeof(struct cmkey_s),
		      (void *)&key, (void *)&key2);
    if (argc <= 0) {
	printf("Unknown command: \"%s\"\n", cp);
	return 0;
    }
    if (argc > 1) {
	printf("Ambiguous command: \"%s\" => %s, %s%s\n",
		cp, key->cmk_key, key2->cmk_key,
		argc > 2 ? ", ..." : "");
	return 0;
    }

    /* Found a single match!  Execute it... */
    /* First set up its args as indicated by flags */
    cd = &(key->cmk_p->cmn_rtn);	/* Get ptr to routine definition */
    cmdspfls(cm);			/* Flush initial spaces from line */
    cm->cmd_arglin = cm->cmd_rdp;
    cm->cmd_rleft = strlen(cm->cmd_rdp);
    if (cd->cmr_flgs & CMRF_CMPTR) {
	(*cd->cmr_vect)(cm);		/* Special handling, no arg hackery */
	return 1;
    }
    slinlim(cm->cmd_rdp);		/* Ensure no EOL in line */
    cm->cmd_rleft = strlen(cm->cmd_rdp);
    if (cd->cmr_flgs & CMRF_NOARG) {
	if (cm->cmd_rleft) {
	    printf("Bad syntax - no args allowed\n");
	    return 0;
	}
	(*cd->cmr_vect)(cm);
	return 1;
    } else if (cd->cmr_flgs & CMRF_TLIN) {
	/* That's all it wants - cmd_rleft will be set but cmd_argc is 0 */
	(*cd->cmr_vect)(cm);
	return 1;
    }
    argc = cmdargs_all(cm);
    if (cd->cmr_flgs & CMRF_TOKS) {
	(*cd->cmr_vect)(cm);	/* Invoke with all tokens set up */
	return 1;
    } else return 0;

    return 1;
}


char *
cmdlsetup(struct cmd_s *cm)
{
    char *cp;
    size_t len;

    if (cpu.fe.fe_debug)
	fprintf(stderr, "[cmdlsetup]");

    if (!(cm->cmd_flags & CMDF_NOPRM) && cm->cmd_prm) {
	fputs(cm->cmd_prm, stdout);
    }

    fe_ctycmforce();		/* Force out any pending tty output */
    cmdreset(cm);
    if (cminfile) {
	cp = fgets(cm->cmd_inp, (int)cm->cmd_left-1, cminfile);
	if (!cp) {
	    fprintf(stdout, "[EOF on %s]\n", cminfname);
	    fclose(cminfile);
	    cminfile = NULL;
	    return cmdlsetup(cm);	/* Try again using TTY */
	}
	fputs(cp, stdout);		/* Echo the input line */
    } else {
	cp = fe_ctycmline(cm->cmd_inp, (int)cm->cmd_left-1);
	if (cp == NULL)
	    return NULL;
    }

    len = strlen(cp);
    cm->cmd_left -= len;
    cm->cmd_inp += len;

    return cp;
}


/* CMDFLS - Flush whitespace from current command pos
*/
void
cmdspfls(register struct cmd_s *cm)
{
    register char *cp = cm->cmd_rdp;

    if (cp)
	while (isspace(*cp)) ++cp;
    cm->cmd_rdp = cp;
}

/* Helper functions for FC_ commands */

static int
cmdargs_none(register struct cmd_s *cm)
{
    if (cm->cmd_rleft || cm->cmd_argc > 0) {
	printf("Bad syntax - no args allowed\n");
	return 0;
    }
    return 1;
}

static int
cmdargs_one(register struct cmd_s *cm, char **as1)
{
    if (cm->cmd_argc == 1) {
	*as1 = cm->cmd_argv[0];
	return 1;
    }
    if (cm->cmd_argc > 1) {
	printf("Bad syntax - too many args\n");
    } else
	printf("Bad syntax - no arg\n");
    return 0;
}

static int
cmdargs_two(register struct cmd_s *cm, char **as1, char **as2)
{
    if (cm->cmd_argc == 2) {
	*as1 = cm->cmd_argv[0];
	*as2 = cm->cmd_argv[1];
	return 1;
    }
    printf("Bad syntax - need two args\n");
    return 0;
}

static int
cmdargs_all(register struct cmd_s *cm)
{
    /* See if haven't yet tokenized line and there's something on it */
    if (cm->cmd_argc == 0 && cm->cmd_rleft) {
	cm->cmd_argc = s_tokenize(cm->cmd_argv,
				      CMDMAXARG,
				      &cm->cmd_tdp, &cm->cmd_tleft,
				      &cm->cmd_rdp, &cm->cmd_rleft);
	cm->cmd_argv[cm->cmd_argc] = NULL;
    }
    return cm->cmd_argc;	/* Return # of tokens */
}

static int
cmdargs_n(register struct cmd_s *cm, int n)
{
    /* See if haven't yet tokenized line and there's something on it */
    if (cm->cmd_argc == 0 && cm->cmd_rleft) {
	if (n > CMDMAXARG)
	    n = CMDMAXARG;
	cm->cmd_argc = s_tokenize(cm->cmd_argv,
				  n,
				  &cm->cmd_tdp, &cm->cmd_tleft,
				  &cm->cmd_rdp, &cm->cmd_rleft);
	cm->cmd_argv[cm->cmd_argc] = NULL;
    }
    return cm->cmd_argc;	/* Return # of tokens parsed */
}

#ifndef CMVPAR_INCLUDED		/* Using external parser stuff? */
# define CMVPAR_INCLUDED 0	/* Nope, use our own */
#endif

#if !CMVPAR_INCLUDED
	/* Use own internal parsing code */

struct cmkey_s *
cmdkeylookup(char *cp,
	     register struct cmkey_s *keytab,
	     register struct cmkey_s **key2)
{
    struct cmkey_s *key;

    (void) s_keylookup(cp, (voidp_t)keytab, sizeof(struct cmkey_s),
			(voidp_t*)&key, (voidp_t*)key2);
    return key;
}

#endif /* !CMVPAR_INCLUDED */

static void
fc_ques(struct cmd_s *cm)
{
    printf("Type \"help\" or \"help <command>\" for help.\n");
}

static void
helpline(register struct cmkey_s *kp)
{
    int cols;
    register char *cp;

    if ((cols = 20 - strlen(kp->cmk_key)) < 0)
	cols = 0;
    if (!(cp = kp->cmk_p->cmn_rtn.cmr_synt))
	cp = "";
    if (cols < strlen(cp))	/* Key and syntax too long? */
	printf("%s %s\n                      %s\n",
		kp->cmk_key, cp, kp->cmk_p->cmn_rtn.cmr_help);
    else
	printf("%s %-*s %s\n",
		kp->cmk_key, cols, cp, kp->cmk_p->cmn_rtn.cmr_help);
}

static void
fc_help(struct cmd_s *cm)
{
    struct cmkey_s *kp, *key2;
    int cols;
    register char *cp;

    /* Get first token on line.  OK if NULL. */
    cp = cm->cmd_argv[0];

    if (!cp || !*cp) {		/* If no specific arg, show everything */
	for (kp = fectbkeys; kp->cmk_key; ++kp) {
	    helpline(kp);
	}
	return;
    }

    (void) s_keylookup(cp, (voidp_t)fectbkeys, sizeof(struct cmkey_s),
				(voidp_t *)&kp, (voidp_t *)&key2);
    if (!kp) {
	printf("Unknown command: \"%s\"\n", cp);
	return;
    }
    if (!key2) {		/* Just one match, so show it in detail */
	helpline(kp);
	if ((cp = kp->cmk_p->cmn_rtn.cmr_desc) && *cp)
	    printf("%s\n", cp);
	return;
    }

    /* More than one match, show each one */
    for (kp = fectbkeys; kp->cmk_key; ++kp) {
	if (smatch(cp, kp->cmk_key) > 0)
	    helpline(kp);
    }
}

/* SLINLIM - Limit string to 1 line by chopping it at first EOL seen.
*/
static void
slinlim(register char *s)
{
    for (; *s; ++s)
	if (*s == '\r' || *s == '\n') {
	    *s = '\0';
	    break;
	}
}

static void
stolower(register char *s)
{
    for (; *s; ++s)
	if (isupper(*s))
	    *s = tolower(*s);
}

/* SMATCH - compare strings
**	Returns 0 if mismatched
**	        1 if S1 is initial substring of S2
**		2 if S1 is exact match of S2
*/
static int
smatch(register char *s1,
       register char *s2)
{
    while (*s1) {
	if (*s1++ != *s2++)
	    return 0;
    }
    return *s2 ? 1 : 2;
}

/* KLH10_INIT - Actually an internal routine to initialize and configure
**	the emulator.  Could be considered analogous to "loading the
**	microcode"...
*/
static void
klh10_init(void)
{

#if KLH10_SYS_T20 && KLH10_CPU_KS
# if KLH10_CTYIO_ADDINT
    cpu.fe.cty_lastint = 20;	/* 20ms delay to add extra output-done int */
# else
    cpu.fe.fe_iowait = 50;	/* Alternative for avoiding T20 CTY lossage */
# endif
#endif
    cpu.fe.fe_intchr = '\034';	/* Default int char is FS (ctrl-\) */
    cpu.mr_exsafe = 2;		/* Default to exec-mode safety check/halt */

    /* Move this to pag_init? */
    /* Some checking that couldn't be done at compile time */
    if (VMF_ACC & PAG_PAMSK) {
	error("Pager access bits (%lx) overlap phys page mask (%lx)!\n",
			(long)VMF_ACC, (long)PAG_PAMSK);
    }


    op_init();		/* Initialize runtime opcode dispatch tables */
#if (KLH10_CPU_KS || KLH10_CPU_KL) && (KLH10_SYS_T10 || KLH10_SYS_T20)
  {
    extern void inexts_init(void);
    inexts_init();		/* Initialize EXTEND instruction stuff */
  }
#endif

    /* Now can add in any special runtime cases */

    mem_init();		/* Initialize memory */
    apr_init();		/* Initialize APR stuff (includes internal devs) */
    dev_init();		/* And general IO device stuff */
    cty_init();		/* And console TTY stuff */
}

/* MEM_INIT - Allocate & initialize PDP10 memory.
**	Perhaps move this to pag_init?
*/
static void
mem_init(void)
{
    size_t memsiz;
    char *ptr;

    /* Determine amount of phys mem to get */
    memsiz = (size_t)PAG_SIZE * PAG_MAXPHYSPGS * sizeof(w10_t);

    /* Always attempt to get shared memory first.  If that fails
    ** (either because OS can't do it, or KLH10 was compiled without
    ** support), then go for private memory.
    */
    fprintf(stdout, "[MEM: Allocating %ld pages ", (long)PAG_MAXPHYSPGS);
    if (os_mmcreate(memsiz, &cpu.mm_physegid, &ptr)) {
	cpu.mm_shared = TRUE;
	fprintf(stdout, "shared memory, clearing...");
    } else {
	/* Failed - routine already barfed about it, if OS failure. */
	cpu.mm_shared = FALSE;

	/* Allocate private 10 memory */
	cpu.mm_physegid = 0;
	fprintf(stdout, "private memory, clearing...");
	if (!(ptr = malloc(memsiz))) {
	    syserr(errno, "MEM: cannot malloc KN10 physical memory!\n");
	}
    }

    cpu.physmem = (vmptr_t)ptr;

    /* To avoid unpleasant surprises to code that expects unused bits
    ** of w10_t words to be zero, we clear it all to begin with.
    */
    memset(ptr, 0, memsiz);		/* Clear it all out */
    fprintf(stdout, "done]\n");
}

/* MEM_TERM - Terminate (de-init) memory stuff.
**	Mainly intended to clean up shared-seg stuff.  May later be useful
**	for re-configuring memory.
**	Will of course bomb completely if anything refs phys mem after
**	this call...
*/
static void
mem_term(void)
{
    if (cpu.mm_shared) {
	os_mmkill(cpu.mm_physegid, (char *)cpu.physmem);
	cpu.mm_physegid = 0;
    } else {
	free((char *)cpu.physmem);
    }
    cpu.physmem = NULL;
    cpu.mm_shared = FALSE;
}

static int
mem_setlock(FILE *of, FILE *ef, int nlockf)
{
    if (nlockf == cpu.mm_locked) {
	if (of)
	    fprintf(of, "Memory already %slocked\n", nlockf ? "" : "un");
	return TRUE;
    }
    if (os_memlock(nlockf)) {
	if (of)
	    fprintf(of, "Memory %slocked\n", nlockf ? "" : "un");
	cpu.mm_locked = nlockf;
	return TRUE;
    } else {
	if (ef)
	    fprintf(ef, "Memory could not be %slocked - %s\n",
				(nlockf ? "" : "un"), os_strerror(-1));
	return FALSE;
    }
}

/* Support routines for FC_SET - parameter parsing
*/

static int cmvp_prompt(struct prmvcx_s *);
static int cmvp_sethz(struct prmvcx_s *);
static int cmvp_setpri(struct prmvcx_s *);
static int cmvp_memlock(struct prmvcx_s *);
static int cmvp_serialno(struct prmvcx_s *);

extern int ld_debug;	/* From feload.c */
#if KLH10_DEBUG && KLH10_CPU_KS
extern int tim_debug;	/* From kn10cpu.c */
#endif
#if KLH10_CPU_KS
extern int ub_debug;	/* From dvuba.c */
#endif

struct prmvar_s fecmvars[] = {
#if KLH10_CPU_KL || KLH10_CPU_KS
    PRMVAR("serialno", "CPU serial number",
				PRMVT_DEC, &cpu.mr_serialno,
							cmvp_serialno, NULL),
#endif
    PRMVAR("sw", "Data switch word",
				PRMVT_WRD, &cpu.mr_dsw, NULL, NULL),
    PRMVAR("mem_lock", "Set on to attempt locking memory",
				PRMVT_BOO, &cpu.mm_locked, cmvp_memlock, NULL),
    PRMVAR("proc_pri", "CPU process priority",
				PRMVT_DEC, &proc_pri, cmvp_setpri, NULL),
    PRMVAR("cpu_debug", "General CPU debug trace",
				PRMVT_BOO, &cpu.mr_debug, NULL, NULL),
    PRMVAR("cpu_exsafe", "Enable exec mode safety halts",
				PRMVT_OCT, &cpu.mr_exsafe, NULL, NULL),
    PRMVAR("fe_intchr", "KLH10 cmd escape char",
				PRMVT_OCT, &cpu.fe.fe_intchr, NULL, NULL),
    PRMVAR("fe_prompt", "KLH10 cmd prompt",
				PRMVT_STR, &cmdprompt, cmvp_prompt, NULL),
    PRMVAR("fe_runenable", "Enable running KN10 during KLH10 cmd processing",
				PRMVT_BOO, &cpu.fe.fe_runenable, NULL, NULL),
    PRMVAR("fe_debug", "FE debug trace",
				PRMVT_BOO, &cpu.fe.fe_debug, NULL, NULL),
    PRMVAR("cty_debug", "CTY debug trace",
				PRMVT_BOO, &cpu.fe.fe_ctydebug, NULL, NULL),
#if KLH10_SYS_T20 && KLH10_CPU_KS
    PRMVAR("cty_iowait", "CTY output delay, usec",
				PRMVT_DEC, &cpu.fe.fe_iowait, NULL, NULL),
# if KLH10_CTYIO_ADDINT
    PRMVAR("cty_lastint", "CTY last-char extra output int, msec",
				PRMVT_DEC, &cpu.fe.cty_lastint,	NULL, NULL),
# endif
#endif
#if KLH10_DEBUG && KLH10_CPU_KS
    PRMVAR("tim_debug", "KS timebase debug trace",
				PRMVT_BOO, &tim_debug, NULL, NULL),
#endif
#if KLH10_CPU_KS
    PRMVAR("ub_debug", "KS unibus debug info (bad ctl/addr warnings)",
				PRMVT_BOO, &ub_debug, NULL, NULL),
#endif
    PRMVAR("ld_fmt", "LOAD/DUMP word format",
				PRMVT_STR, &ld_fmt, NULL, NULL),
    PRMVAR("ld_debug", "LOAD debug trace",
				PRMVT_BOO, &ld_debug, NULL, NULL),
    PRMVAR("insbreak", "APR loop interrupt",
				PRMVT_DEC, &cpu.mr_insbreak, NULL, NULL),
#if KLH10_CLKTRG_COUNT
    PRMVAR("clk_ipms", "Instrs per virt msec",
				PRMVT_DEC, &cpu.clk.clk_ipmsrq,
	  						cmvp_sethz,NULL),
#endif
#if 1 /* KLH10_CLKTRG_OSINT */
    PRMVAR("clk_ithz", "OS interval timer - current value in Hz",
				PRMVT_DEC, &cpu.clk.clk_ithzcmreq,
							cmvp_sethz, NULL),
    PRMVAR("clk_ithzfix", "ITimer value fixed at this if non-zero",
				PRMVT_DEC, &cpu.clk.clk_ithzfix,
							cmvp_sethz, NULL),
    PRMVAR("clk_ithzosreq", "ITimer value last requested by OS",
				PRMVT_DEC, &cpu.clk.clk_ithzosreq,
				NULL, NULL),
#endif
    PRMVAR("pisys_on", "Set if PI sys on",
				PRMVT_OCT, &cpu.pi.pisys_on, NULL, NULL),
    PRMVAR("pilev_on", "Levs enabled",
				PRMVT_OCT, &cpu.pi.pilev_on, NULL, NULL),
    PRMVAR("pilev_pip", "Levs PI in Progress",
				PRMVT_OCT, &cpu.pi.pilev_pip, NULL, NULL),
    PRMVAR("pilev_preq", "Prog PI reqs",
				PRMVT_OCT, &cpu.pi.pilev_preq, NULL, NULL),
    PRMVAR("pilev_aprreq", "APR PI reqs",
				PRMVT_OCT, &cpu.pi.pilev_aprreq, NULL, NULL),
    PRMVAR("pilev_dreq", "Device PI reqs",
				PRMVT_OCT, &cpu.pi.pilev_dreq, NULL, NULL),
#if KLH10_CPU_KS
    PRMVAR("pilev_ub1req", "UBA #1 PI reqs",
				PRMVT_OCT, &cpu.pi.pilev_ub1req, NULL, NULL),
    PRMVAR("pilev_ub3req", "UBA #3 PI reqs",
				PRMVT_OCT, &cpu.pi.pilev_ub3req, NULL, NULL),
#endif
#if KLH10_CPU_KL
    PRMVAR("pilev_rhreq", "RH20 PI reqs",
				PRMVT_OCT, &cpu.pi.pilev_rhreq, NULL, NULL),
    PRMVAR("pilev_dtereq", "DTE20 PI reqs",
				PRMVT_OCT, &cpu.pi.pilev_dtereq, NULL, NULL),
#endif
    PRMVAR("feiosignulls", "# SIGIOs with no input",
				PRMVT_DEC, &feiosignulls, NULL, NULL),
    PRMVAR("feiosiginps", "# SIGIOs with CTY input",
				PRMVT_DEC, &feiosiginps, NULL, NULL),
    PRMVAR("feiosigtests", "# non-SIGIO tests in above",
				PRMVT_DEC, &feiosigtests, NULL, NULL),
    PRMVAR(NULL, "", PRMVT_NULL, NULL, NULL, NULL)
};

/* Various parameter get/set auxiliary functions */

static int
cmvp_prompt(register struct prmvcx_s *cx)
{
    /* First just set requested value normally */
    if (!prmvp_set(cx))
	return FALSE;	/* Problem setting param?  Already reported */

    /* Then must do special re-init stuff */
    fe_cmpromptset(cmdprompt);
    cmdpromptnew = TRUE;
    return TRUE;
}

static int
cmvp_memlock(register struct prmvcx_s *cx)
{
    return mem_setlock(cx->prmvcx_of, cx->prmvcx_ef, cx->prmvcx_val.vi);
}

static int
cmvp_setpri(register struct prmvcx_s *cx)
{
    ospri_t npri = cx->prmvcx_val.vi;
    ospri_t opri;

    if (os_setpriority(npri) == FALSE) {
	if (cx->prmvcx_ef)
	    fprintf(cx->prmvcx_ef, "Could not change priority - %s\n",
					os_strerror(-1));
	return FALSE;
    }
    if (os_getpriority(&opri) == FALSE) {
	if (cx->prmvcx_ef)
	    fprintf(cx->prmvcx_ef, "Could not find priority - %s\n",
					os_strerror(-1));
	return FALSE;
    }
    if (cx->prmvcx_of) {
	if (opri == proc_pri)
	    fprintf(cx->prmvcx_of, "Process priority remains %ld\n",
							(long)opri);
	else
	    fprintf(cx->prmvcx_of, "Process priority changed from %ld to %ld\n",
					(long)proc_pri, (long)opri);
    }
    proc_pri = opri;
    return TRUE;
}

static int
cmvp_sethz(register struct prmvcx_s *cx)
{
    /* First just set requested value normally */
    if (!prmvp_set(cx))
	return FALSE;	/* Problem setting param?  Already reported */

    /* Then must do special re-init stuff */
#if KLH10_CLKTRG_COUNT
    clk_ipmsset(cpu.clk.clk_ipmsrq);
#elif KLH10_CLKTRG_OSINT
    clk_ithzset(cpu.clk.clk_ithzcmreq);
#endif
    return TRUE;
}

static int
cmvp_serialno(register struct prmvcx_s *cx)
{
#if KLH10_CPU_KL
    if (cx->prmvcx_val.vi <= 1024)
	fprintf(cx->prmvcx_of, "KL serial number should be > 1024\n");
    else if (cx->prmvcx_val.vi > 07777) {
	fprintf(cx->prmvcx_of, "Invalid KL serial number %ld\n",
						(long)cx->prmvcx_val.vi);
	return FALSE;
    }
#elif KLH10_CPU_KS
    if (cx->prmvcx_val.vi <= 4096)
	fprintf(cx->prmvcx_of, "KS serial number should be > 4096\n");
    else if (cx->prmvcx_val.vi > 077777) {
	fprintf(cx->prmvcx_of, "Invalid KS serial number %ld\n",
						(long)cx->prmvcx_val.vi);
	return FALSE;
    }
#endif
    if (!prmvp_set(cx))
	return FALSE;	/* Problem setting param?  Already reported */
    apr_init_aprid();		/* Reset APR ID */
    return TRUE;
}



static int
addrparse(register char *str,
	  vaddr_t *vloc,
	  enum fevmmode *mloc)
{
    w10_t w;
    enum fevmmode mode = FEVM_DFLT;
    int local = FALSE, global = FALSE;

    if (!str) return FALSE;
    while (isalpha(*str)) {
	switch (islower(*str) ? toupper(*str++) : *str++) {
	case 'L':	local  = TRUE;		break;
	case 'G':	global = TRUE;		break;
	case 'C':	mode = FEVM_CUR;	break;
	case 'P':	mode = FEVM_PHYS;	break;
	case 'E':	mode = FEVM_EXEC;	break;
	case 'U':	mode = FEVM_USER;	break;
	case 'A':	mode = FEVM_ACB;	break;
	default:
		return 0;	/* Bad syntax, unknown char */
	}
    }
    *mloc = mode;

    /* Note distinction between 123456 and 0,,123456
    ** (ie explicit specification of section #)
    */
    switch (s_towd(str, &w)) {
    default:			/* Bad syntax of address */
	return FALSE;
    case 1:			/* One value: <one> */
	if (!local && !global)	/* If not otherwise specified, */
	    local = TRUE;	/* defaults to local */
	break;
    case 2:			/* Two values: <one>,,<two> */
	if (!local && !global)	/* If not otherwise specified, */
	    global = TRUE;	/* defaults to global */
	break;
    }

    /* Problem if local - where does default section # come from?
    ** Depends on mode, but even the mode gets hairy.
    */
    if (mode == FEVM_ACB) {		/* Special hack, ac block # in LH */
	if ((LHGET(w) & ~(h10_t)07)	/* Only allow blocks 0-7 */
	  || (RHGET(w) & ~(h10_t)AC_MASK))	/* and ACs 0-017 */
	    return FALSE;
	va_hmake(*vloc, LHGET(w), RHGET(w));
	return TRUE;
    }
    if (local)
	va_lmake(*vloc, LHGET(w) & VAF_SMSK, RHGET(w));
    else
	va_gmake(*vloc, LHGET(w) & VAF_SMSK, RHGET(w));
    return TRUE;
}


/* FC_SET - Set/Show KLH10 variables
*/
static void
fc_set(struct cmd_s *cm)
{
    struct prmvar_s *p1, *p2;
    int res;
    char *cp = cm->cmd_arglin;

    if (!cp || !*cp			/* If no arg, show all vars */
      || *cp == '?') {			/* Ditto if starts with ? */
	for (p1 = fecmvars; p1->prmv_name; ++p1)
	    prm_varshow(p1, stdout);
	return;
    }
    if (!strchr(cp, '=')) {		/* If doesn't look like var=val */
	res = s_keylookup(cp, (voidp_t)fecmvars, sizeof(*p1),
			  (voidp_t*)&p1, (voidp_t*)&p2);
	if (res == 1) {
	    prm_varshow(p1, stdout);
	} else if (res > 1) {
	    fprintf(stdout, "Ambiguous variable: \"%s\", \"%s\"%s\n",
		p1->prmv_name, p2->prmv_name, (res > 2 ? ", ..." : ""));
	} else {
	    fprintf(stdout, "Unknown var or bad syntax, must be <var>=<value>\n");
	}
	return;	
    }

    prm_varset(&cp, fecmvars, stdout, stdout);
}


/* Device control commands */

/* FC_DEVLOAD - Load a dynamic/shared library as a device driver,
**	and defines its name for later use in configuration.
**
**    Syntax is:
**	devload <drvname> <path> <initsym> [<comment>]
**
** where
**	<drvname> - Arbitrary device driver name.
**	<path>	  - Pathname for library in native OS.
**	<initsym> - "Entry point" symbol (init/config routine)
**	<comment> - Optional description text
*/
static void
fc_devload(struct cmd_s *cm)
{
    if (cmdargs_n(cm, 3) != 3) {
	printf("?Bad syntax - usage: %s\n", cd_devload.cmr_synt);
	return;
    }
    stolower(cm->cmd_argv[0]);	/* Driver name forced to lowercase */
    (void) dev_drvload(stdout,
		       cm->cmd_argv[0],
		       cm->cmd_argv[1],
		       cm->cmd_argv[2],
		       cm->cmd_rdp);
}


/* FC_DEVDEF - Do initial device configuration
**	Defines the device name and does its initial configuration setup.
**
** Syntax is:
**	devdefine <uniqname> { <10dev> } <drvname> [<devargs>]
**		             { UB<n>   }
** where
**	<name> - Name to identify device
**	<10dev> - A PDP-10 device number, or predefined keyword (CTY, etc)
**			Implies device uses dev-IO instructions.
**	UB<n>  - A Unibus controller (n = 1 or 3)
**			Implies device uses KS IO instructions.
**	<drvname> - Name of a driver module, either builtin or dynamic.
**	<devargs> - Optional args to dev's init routine (rest of line)
**
** NOTE: this replaces old design of:
**	devconf <uniqname> { <10dev> }  {static   <drvname>     }  <devargs>
**		           { UB<n>   }  {extern <path> <initsym>}
**	but the static/extern option has been replaced by DEVLOAD.
*/
static void
fc_devdef(struct cmd_s *cm)
{
    if (cmdargs_n(cm, 3) != 3) {
	printf("?Bad syntax - usage: %s\n", cd_devdef.cmr_synt);
	return;
    }
    stolower(cm->cmd_argv[0]);	/* Driver name forced to lowercase */
    stolower(cm->cmd_argv[1]);	/* Device number ditto */
    stolower(cm->cmd_argv[2]);	/* Driver name ditto */
    (void) dev_define(stdout,
		      cm->cmd_argv[0],
		      cm->cmd_argv[1],
		      cm->cmd_argv[2],
		      cm->cmd_rdp);
}


static void
fc_devshow(struct cmd_s *cm)
{
    /* Pass first token on line.  OK if NULL. */
    (void) cmdargs_n(cm, 1);
    (void) dev_show(stdout, cm->cmd_argv[0], cm->cmd_rdp);
}

#if KLH10_EVHS_INT
static void
fc_devevshow(struct cmd_s *cm)
{
    /* Pass first token on line.  OK if NULL. */
    (void) cmdargs_n(cm, 1);
    (void) dev_evshow(stdout, cm->cmd_argv[0], cm->cmd_rdp);
}
#endif /* KLH10_EVHS_INT */

/* FC_DEVSET - Hack defined device from 10 side
**	For use when need to set device-related things from the 10's side of
**	the fence, for a specific device.
**  Syntax:
**	devset <name> <devvar>=<val> [devvar2>=<val> ...]
*/
static void
fc_devset(char *argline)
{
}


/* FC_DEV_CMD - Generic device command
**    Syntax:	dev <name> <commandline>
**
** Invokes device's command parsing and execution, which can be anything
** the device wants to do with the line.  Most should follow certain
** conventions, however:
**	dev <name> help		- Show what this device understands
**	dev <name> status	- Show status in appropriate form
**	dev <name> show [param] [...]		- Show specific device vars
**	dev <name> set  [param=val] [...]	- Set them
*/
static void
fc_dev_cmd(struct cmd_s *cm)
{
    /* Consume first token on line - must exist */
    if (cmdargs_n(cm, 1) != 1) {	/* If no token */
	printf("Bad dev cmd syntax, need device specifier\n");
	return;
    }
    (void) dev_command(stdout, cm->cmd_argv[0], cm->cmd_rdp);
}


static void
fc_dev_help(struct cmd_s *cm)
{
    /* Pass first token on line.  OK if NULL. */
    (void) cmdargs_n(cm, 1);
    (void) dev_help(stdout, cm->cmd_argv[0], cm->cmd_rdp);
}

static void
fc_dev_status(struct cmd_s *cm)
{
    /* Pass first token on line.  OK if NULL. */
    (void) cmdargs_n(cm, 1);
    (void) dev_status(stdout, cm->cmd_argv[0], cm->cmd_rdp);
}

static void
fc_devmnt(struct cmd_s *cm)
{
    char *spath;

    /* Consume first two tokens on line - must exist */
    switch (cmdargs_n(cm, 2)) {
    case 0:
	printf("Bad syntax - <devID> must be specified\n");
	return;
    case 1:
	spath = "";	/* Ensure 2nd arg non-null as NULL means dismount */
	break;
    default:
	spath = cm->cmd_argv[1];
	break;
    }
    (void) dev_mount(stdout, cm->cmd_argv[0], spath, cm->cmd_rdp);
    fedevchkf = dev_dpchk_ctl(TRUE);	/* Start periodic checks if nec */
}

static void
fc_devunmnt(struct cmd_s *cm)
{
    /* Consume first token on line - must exist */
    if (cmdargs_n(cm, 1) != 1) {	/* If no token */
	printf("Bad syntax - <devID> must be specified\n");
	return;
    }
    (void) dev_mount(stdout, cm->cmd_argv[0], (char *)NULL, cm->cmd_rdp);
    fedevchkf = dev_dpchk_ctl(TRUE);	/* Start periodic checks if nec */
}

static void
fc_devdbg(struct cmd_s *cm)
{
    /* Pass first two tokens on line.  OK if NULL. */
    if (cmdargs_n(cm, 2) < 1)
	cm->cmd_argv[1] = NULL;
    (void) dev_debug(stdout, cm->cmd_argv[0], cm->cmd_argv[1], cm->cmd_rdp);
}

static void
fc_devwait(struct cmd_s *cm)
{
    char *dev;
    long totsec = -1;
    osstm_t stm;	/* Sleep time spec */

    /* Consume first two tokens on line.  OK if NULL. */
    switch (cmdargs_n(cm, 2)) {
    case 0:
	dev = NULL;		/* All devices */
	totsec = -1;		/* Indefinitely */
	break;
    case 1:		/* One arg, either <dev> or <secs> */ 
	dev = cm->cmd_argv[0];
	if (s_todnum(dev, &totsec))
	    dev = NULL;		/* Numeric, is <secs> */
	else
	    totsec = -1;	/* Not numeric, is <dev> */
	break;
    default:
	dev = cm->cmd_argv[0];
	if (!s_todnum(cm->cmd_argv[1], &totsec)) {
	    printf("Bad syntax - \"%s\" must be timeout in secs\n",
		   cm->cmd_argv[1]);
	    return;
	}
	break;
    }

    /* OK, now start the wait. */
    OS_STM_SET(stm, totsec);
    while (dev_waiting(stdout, dev)) {
	if (os_msleep(&stm) <= 0)
	    break;		/* Stop waiting if timed out */
    }
}

/* FC_DEVBOOT - Boot using specified device.
**	devboot <devid> [halt]
**		Uses device's "read-in mode" to read in a small piece of
**		bootstrap code and starts execution there.
**		If option "halt" given, doesn't actually start PDP-10,
**		just sets start addr so if continued will start bootstrap.
*/
static void
fc_devboot(struct cmd_s *cm)
{
    int opthalt = FALSE;
    vaddr_t bootsa;

    if (!aprhalted()) {
	printf("KN10 still running!  Halt or Reset it first.\n");
	return;
    }

    /* Consume first two tokens on line */
    switch (cmdargs_n(cm, 2)) {
    case 0:
	printf("Bad syntax - <devID> must be specified\n");
	return;
    case 1:
	break;
    default:
	if (strcasecmp(cm->cmd_argv[1], "halt")==0)
	    opthalt = TRUE;
	else {
	    printf("Unknown option \"%s\"\n", cm->cmd_argv[1]);
	    return;
	}
	break;
    }

    if (!dev_boot(stdout, cm->cmd_argv[0], &bootsa)) {
	printf("Bootstrap readin of %s failed\n", cm->cmd_argv[0]);
	return;
    }

    /* Success!  Set up returned boot address as both new PC and new
       start address, and maybe go.
    */
    ddt_loadsa = bootsa;
    PC_SET(bootsa);
    if (opthalt) {
	printf("Bootstrap read in, halted with PC = %lo\n", (long)bootsa);
	return;
    }
    printf("Bootstrap read in\n");
    fe_aprcont(FEMODE_CTYRUN, FEAPRF_START, bootsa, 0);
}

/* FC_RESET - Halts and Resets PDP-10 (clears all status)
*/
static void
fc_reset(struct cmd_s *cm)
{
    fc_halt(cm);		/* Ensure halted if not already */
    apr_init();
}

/* FC_GO - Starts PDP-10 at given location.  Defaults to last
**	loaded start address, if any.
*/
static void
fc_go(struct cmd_s *cm)
{
    vaddr_t loc;
    enum fevmmode mode;
    char *sloc = cm->cmd_arglin;

    if (!aprhalted()) {
	printf("KN10 still running!  Halt or Reset it first.\n");
	return;
    }

    if (sloc && *sloc) {
	if (!addrparse(sloc, &loc, &mode)) {
	    printf("?Bad address\n");
	    return;
	} else if (mode != FEVM_DFLT && mode != FEVM_CUR) {
	    printf("?Bad address mode - only Current allowed\n");
	    return;
	} else if (va_isglobal(loc)) {
	    printf("?Bad address - only local allowed\n");
	    return;
	}
	ddt_loadsa = loc;	/* Mode will always be current */
    }
    fe_aprcont(FEMODE_CTYRUN, FEAPRF_START, ddt_loadsa, 0);
}

/* FC_CONT - Continues PDP-10 at current PC.
**	Safe to call this even if KN10 is running.
**	
*/
static void
fc_cont(struct cmd_s *cm)
{
    fe_aprcont(FEMODE_CTYRUN,
	       (aprhalted() ? FEAPRF_VERBOSE : 0),
	       0, 0);
}

/* FE_APRCONT - Resume execution.
**	(old startf: +1 start, 0 continue, -1 continue verbosely)
*/
void
fe_aprcont(int femode,
	   int startf,
	   vaddr_t newpc,
	   int nsteps)
{
    int res;

    if (cpu.fe.fe_debug)
	fprintf(stderr, "[aprcont %o 0x%x %d, intf_fecty %d]",
		femode, startf, nsteps, cpu.intf_fecty);

    if (fedevchkf)
	fedevchkf = dev_dpchk_ctl(FALSE);	/* Turn off dev checking! */
    if (startf & FEAPRF_START)
	PC_SET(newpc);

    if (startf & (FEAPRF_START | FEAPRF_VERBOSE))
	printf("%s KN10 at loc %#lo...\n",
			((startf&FEAPRF_START) ? "Starting" : "Continuing"),
			(long)PC_30);

    /* Set up TTY handling appropriately for running */
    fe_ctyenable(femode);	/* No echo, no CR/LF hacks, no delay */
    cpu.mr_running = TRUE;
    cpu.mr_1step = nsteps;
    res = apr_run();		/* Go! */
    cpu.mr_running = FALSE;
    fe_ctydisable((res == HALT_FECMD)	/* Restore TTY mode if necessary */
		  ? FEMODE_CMDRUN
		  : FEMODE_CMDHALT);
    switch (res) {
	case HALT_PROG:		/* Program halt (JRST 4,) */
	    printf("[HALTED: Program Halt, PC = %lo]\n", (long)PC_30);
	    break;

	case HALT_FECTY:	/* FE Console interrupt */
	    printf("[HALTED: FE interrupt]\n");
	    break;

	case HALT_FECMD:	/* FE Console command ready to execute */
	    break;

	case HALT_BKPT:		/* Hit breakpoint */
	    printf("[HALTED: Breakpoint]\n");
	    break;

	case HALT_STEP:		/* Single-Stepping */
	    break;

	case HALT_EXSAFE:	/* Something bad in exec mode */
	    printf("[HALTED: Exec program error? (\"set cpu_exsafe=1\" to continue)]\n");
	    break;

	case HALT_PANIC:	/* Panic - internal error, bad state */
	    printf("[HALTED: Panic - may be in inconsistent state]\n");
	    break;
    }
}


static int aprhalted(void)
{
    if (cpu.fe.fe_mode == FEMODE_CMDRUN)
	return FALSE;
    return TRUE;
}

/* FC_SHUTDOWN - Halts PDP-10 OS gracefully if possible, by
**	putting cruft in the shutdown location of the FECOM area.
**	Assumes physical mapping, as does the CTY code.
**
** KA/KI versions would probably set the data switches here.
*/
static void
fc_shutdown(struct cmd_s *cm)
{
    register vmptr_t vp;

    vp = vm_physmap(FECOM_SWIT0);	/* Find loc for data-switch 0 sim */
    op10m_seto(*vp);			/* Set word to ones */
    fc_cont(cm);			/* Resume CPU, let OS do rest */
}

/* FC_HALT - Halts PDP-10 violently.
**	Not needed until true threads/subprocess version exists, since
**	currently any interaction with KLH10 commands means the 10 is
**	already stopped!
*/
static void
fc_halt(struct cmd_s *cm)
{
    if (aprhalted()) {
	printf("KN10 already halted.\n");
	return;
    }
    cpu.fe.fe_mode = FEMODE_CMDHALT;
    printf("[HALTED: FE command, PC = %lo]\n", (long)PC_30);
}


/* Various commands primarily for debugging */

/* FC_ZERO - Clears first 256K of physical memory
*/
static void
fc_zero(struct cmd_s *cm)
{
    memset((char *)vm_physmap(0), 0, sizeof(w10_t)*(H10MASK+1)); /* Zap! */
    printf("OK\n");
}

/* FC_TRACE - Toggles execution tracing
*/
static void
fc_trace(struct cmd_s *cm)
{
    if (cpu.mr_dotrace) {
	cpu.mr_dotrace = 0;
	printf("Tracing now off\n");
    } else {
	cpu.mr_dotrace = TRUE;
	printf("Tracing now ON\n");
    }
}

#if 0	/* Not bound to a command now, use SET. */

/* FC_DEBUG - Toggles general debug flag
*/
static void
fc_debug(void)
{
    if (cpu.mr_debug) {
	cpu.mr_debug = 0;
	printf("Debug now off\n");
    } else {
	cpu.mr_debug = TRUE;
	printf("Debug now ON\n");
    }
}
#endif

/* FC_STEP - Single-steps by N instructions.
*/
static void
fc_step(struct cmd_s *cm)
{
    char *snum = cm->cmd_arglin;
    long n = 1;

    if (!aprhalted()) {
	printf("KN10 still running!  Halt it first.\n");
	return;
    }

    if (snum && *snum)
	if (!s_tonum(snum, &n)) {
	    printf("?Bad count syntax\n");
	    return;
	}
    if (n <= 0) {
	printf("?Bad step count\n");
	return;
    }
    fe_aprcont(FEMODE_CTYRUN, 0, 0, n);
    putchar('\n');
    nextinsprint(stdout, PINSTR_OPS);
}

/* FC_BKPT - Sets location to stop at.  0 clears.
*/
static void
fc_bkpt(struct cmd_s *cm)
{
    vaddr_t loc;
    enum fevmmode mode;
    char *sloc = cm->cmd_arglin;

    if (sloc && *sloc) {
	if (!addrparse(sloc, &loc, &mode)) {
	    printf("?Bad address\n");
	    return;
	} else if (mode != FEVM_DFLT && mode != FEVM_CUR) {
	    printf("?Bad address mode - only Current allowed\n");
	    return;
	} else if va_isglobal(loc) {
	    printf("?Bad address - only local allowed\n");
	    return;
	}
	
	cpu.mr_bkpt = loc;	/* Mode will always be current */
    }
}


/* FC_PROC - Proceed KN10 without CTY
*/
static void
fc_proc(struct cmd_s *cm)
{
    if (!aprhalted()) {
	/* Should only happen if already in FEMODE_CMDRUN */
	printf("KN10 already running\n");
	return;
    }
    cpu.fe.fe_mode = FEMODE_CMDRUN;	/* Change mode! */
}




/* FC_EXNEXT - Examine next word
** FC_EXPREV - Previous word
*/
static void
fc_exnext(struct cmd_s *cm)
{
    va_inc(ddt_cloc);
    fc_exa((struct cmd_s *)NULL);
}

static void
fc_exprev(struct cmd_s *cm)
{
    va_dec(ddt_cloc);
    fc_exa((struct cmd_s *)NULL);
}

/* FC_EXA - Examine PDP-10 word
*/
static void
fc_exa(struct cmd_s *cm)
{
    vaddr_t loc;
    enum fevmmode mode;
    register vmptr_t vp;
    char *sloc;

    sloc = (cm ? cm->cmd_arglin : NULL);

    if (sloc && *sloc) {
	if (!addrparse(sloc, &loc, &mode)) {
	    printf("?Bad address\n");
	    return;
	} else if (mode != FEVM_DFLT)
	    ddt_clmode = mode;	/* Set new mode if not default */
	ddt_cloc = loc;
    }

    putchar(' ');
    addrprint(stdout, ddt_cloc, ddt_clmode);
    putchar('/');
    putchar(' ');

    if (vp = fevm_xmap(ddt_cloc, ddt_clmode)) {
	ddt_val = vm_pget(vp);
	wd1print(stdout, ddt_val);
	if (LHGET(ddt_val) & 0777000) {	/* Opcode exists? */
	    putchar('\t');
	    pinstr(stdout, ddt_val, 0, ddt_cloc/*unused*/);
	}
    } else
	fprintf(stdout, "\?\?");

    putchar('\n');
}

/* FC_DEP - Deposit PDP-10 word
*/
static void
fc_dep(struct cmd_s *cm)
{
    w10_t wd;
    vaddr_t loc;
    register vmptr_t vp;
    enum fevmmode mode;
    char *sloc, *sval;

    if (!cmdargs_two(cm, &sloc, &sval))
	return;

    if (sloc && *sloc) {
	if (!addrparse(sloc, &loc, &mode)) {
	    printf("?Bad address\n");
	    return;
	} else if (mode != FEVM_DFLT)
	    ddt_clmode = mode;	/* Set new mode if not default */
	ddt_cloc = loc;
    }

    if (!sval || !*sval || !s_towd(sval, &wd)) {
	printf("?Bad word syntax\n");
	return;
    }
    ddt_val = wd;
    
    if (vp = fevm_xmap(ddt_cloc, ddt_clmode))
	vm_pset(vp, ddt_val);
    else {
	printf("?Cannot map address ");
	addrprint(stdout, ddt_cloc, ddt_clmode);
	printf(" - value not deposited\n");
    }
}

static vmptr_t
fevm_map(paddr_t pa)
{
    if ((pa & ~AC_MASK)==0)
	return &cpu.acblk.cur[pa];	/* Use currently active AC block */

    /* For now, assume physical memory mapping.
    ** Later, can use current page map and report error if any failures.
    */
    return vm_physmap(pa & H10MASK);
}

vmptr_t
fevm_xmap(vaddr_t e, enum fevmmode mode)
{
    register pment_t *map;
    register acptr_t acp;
    register int acb;

    switch (mode) {
    default:
    case FEVM_CUR:	acp = cpu.acblk.cur; map = cpu.vmap.cur; break;
    case FEVM_PHYS:	acp = cpu.acblk.cur; map = pr_pmap;	break;
    case FEVM_USER:	acp = cpu.acblk.cur; map = cpu.pr_umap;	break;
    case FEVM_EXEC:	acp = cpu.acblk.cur; map = cpu.pr_emap;	break;
    case FEVM_ACB:
	/* Ensure AC block # in LH is 0-7 and AC # in RH is 0-017 inclusive */
	acb = va_lh(e);
	if ((acb & ~07) || (va_insect(e) & ~(h10_t)AC_MASK))
	    return NULL;		/* Ugh, one of them out of range */
	return &cpu.acblks[acb][va_ac(e)];	/* Use AC in right block */
    }
    return vm_xtrymap(e, VMF_READ, acp, map);
}

static int
fevm_tryeacalc(register w10_t *wp,
	       register acptr_t acp,
	       register pment_t *map,
	       int indcnt)	/* Indirection level count */
{
    register h10_t tmp;
    register vmptr_t vp;

#if KLH10_EXTADR
/*
	* ERROR * Need to revise this for XA
*/
#endif
    for (;;) {
	if ((tmp = LHGET(*wp)) & IW_X) {	/* Indexing? */
	    register w10_t wea;
	    wea = ac_xget(tmp & IW_X, acp);	/* Get c(X) */
	    LHSET(*wp, LHGET(wea));
	    RHSET(*wp, (RHGET(*wp)+RHGET(wea))&H10MASK); /* Add previous Y */
	}
	if (!(tmp & IW_I))			/* Indirection? */
	    return TRUE;			/* Nope, return now! */

	/* Handle indirection */
	if (--indcnt < 0)
	    return FALSE;
	if ((vp = vm_xtrymap(RHGET(*wp), VMF_READ, acp, map)) == NULL)
	    return FALSE;
	*wp = vm_pget(vp);
    }
}

/* FC_VIEW - Show PDP-10 status summary
*/
static void
fc_view(struct cmd_s *cm)
{
    printf("KN10 status: %s %s",
	   (cpu.fe.fe_mode == FEMODE_CMDRUN ? "RUNNING" : "STOPPED"),
	   (cpu.mr_usrmode ? "USER" : "EXEC"));
    if (cpu.mr_inpxct) printf(" PXCT-%o", cpu.mr_inpxct);
    if (cpu.mr_intrap) printf(" TRAP-%o", cpu.mr_intrap);
    if (cpu.mr_injrstf) printf(" JRSTF");	/* Either on or off */
    putchar('\n');

#if KLH10_JPC
    printf("  PC: %lo  [JPC: %lo  UJPC: %lo  EJPC: %lo]\n",
		(long)PC_30,
		(long)cpu.mr_jpc, (long)cpu.mr_ujpc, (long)cpu.mr_ejpc);
#if KLH10_ITS_JPC
    printf("  ITSPAGER UJPC: %lo  EJPC: %lo\n",
		(long)cpu.pag.pr_ujpc, (long)cpu.pag.pr_ejpc);
#endif
#endif /* KLH10_JPC */
    printf("  Flags: %lo\n", (long)cpu.mr_pcflags);

    nextinsprint(stdout, PINSTR_OPS);
}

/* FE_TRACEPRINT called from APR loop if tracing and about to execute
**	an instruction.
*/

void
fe_traceprint(register w10_t instr,
	      vaddr_t e)
{
    pishow(stdout);
    pcfshow(stdout, cpu.mr_pcflags);
    printf("%lo: ", (long)PC_30);

    pinstr(stdout, instr, PINSTR_OPS|PINSTR_EA, e);
    putc('\r', stdout);
    putc('\n', stdout);
}

void
fe_begpcfdbg(FILE *f)
{
    putc('[', f);
    pishow(f);
    pcfshow(f, cpu.mr_pcflags);
    fprintf(f,"%lo: => ", (long) PC_30);
}

void
fe_endpcfdbg(FILE *f)
{
    pishow(f);
    pcfshow(f, cpu.mr_pcflags);
    fprintf(f,"%lo:]\r\n", (long) PC_30);
}

void
pishow(FILE *f)		/* Show PI status */
{
    register int lev, num;
    if (cpu.pi.pilev_pip) {
	num = pilev_nums[cpu.pi.pilev_pip];		/* Find level # */
	fprintf(f, "PI%o", num);
	lev = cpu.pi.pilev_pip & ~pilev_bits[num];	/* And remaining PIPs */
	if (lev) {
	    fputc('[', f);
	    while (lev) {
		num = pilev_nums[lev];		/* Find level # */
		fprintf(f, "%o", num);
		lev &= ~pilev_bits[num];
	    }
	    putc(']', f);
	}
	putc(' ', f);
    }
}

struct { h10_t flag; char ch; } flgtab[] = {
	{ PCF_ARO, 'O'},  /* Arithmetic Overflow (or Prev Ctxt Public) */
	{ PCF_CR0, 'C'},  /* Carry 0 - Carry out of bit 0 */
	{ PCF_CR1, 'c'},  /* Carry 1 - Carry out of bit 1 */
	{ PCF_FOV, 'F'},  /* Floating Overflow */
	{ PCF_FPD, '1'},  /* First Part Done */
	{ PCF_USR, 'U'},  /* User Mode */
	{ PCF_UIO, 'I'},  /* User In-Out (or Prev Ctxt User) */
	{ PCF_PUB, 'P'},  /* Public Mode */
	{ PCF_AFI, 'A'},  /* Addr Failure Inhibit */
#if 0	/* Special-cased */
	{ PCF_TR2, 'x'},  /* Trap 2 (PDL overflow) */
	{ PCF_TR1, 'x'},  /* Trap 1 (Arith overflow) */
#endif
	{ PCF_FXU, 'f'},  /* Floating Exponent Underflow */
	{ PCF_DIV, 'D'}	  /* No Divide */
};

void		/* Show PC Flag status */
pcfshow(FILE *f,
	register h10_t flags)
{
    register int i;

    if (flags & (PCF_TR1|PCF_TR2)) {
	fprintf(f, "TRAP%o ", (int)FLDGET((uint32)flags, (PCF_TR1|PCF_TR2)));
	flags &= ~(PCF_TR1|PCF_TR2);
    }
    for (i = 0; flags && i < (sizeof(flgtab)/sizeof(flgtab[0])); ++i)
	if (flags & flgtab[i].flag) {		/* Found it? */
	    flags &= ~flgtab[i].flag;		/* Turn off */
	    putc(flgtab[i].ch, f);
	}
    if (flags) {			/* If any flags still left, */
	fprintf(f, "[PCF: %#lo]", (long)flags);	/* show those too */
    }
    putc(' ', f);
}

static void
nextinsprint(FILE *f,
	     int argf)
{
    w10_t w;
    vmptr_t vp;
    vaddr_t e;

    fprintf(f, "Next: ");
    pishow(f);
    pcfshow(f, cpu.mr_pcflags);
    fprintf(f, " %lo/ ", (long)PC_30);

    e = PC_VADDR;
    if (!(vp = fevm_xmap(e, FEVM_CUR))) {
	fprintf(f, " \?\?\n");
	return;
    }
    w = vm_pget(vp);
    if (LHGET(w) & 0777000) {
	pinstr(f, w, argf, e/*unused*/);
    } else
	wd2print(f, w);
    fprintf(f, "\n");
}


/* Print address as symbolic if possible.
**	This function isn't called by anything yet.
*/
static void
easymprint(FILE *f,
	   register vaddr_t e)
{

    /* Take care of any randomly set bits in LH */
    if (va_lh(e)) fprintf(f, "%lo,,", (long)va_lh(e));

    /* Now attempt to look up RH value as symbol? */


    fprintf(f, "%lo", (long)va_insect(e));	/* Punt for now */
}

static void
wd1print(FILE *f,
	 w10_t w)
{
    if (LHGET(w)) fprintf(f, "%lo,,", (long)LHGET(w));
    fprintf(f, "%lo", (long)RHGET(w));
}

static void
wd2print(FILE *f,
	 w10_t w)
{
    fprintf(f, "%lo,,%lo", (long)LHGET(w), (long)RHGET(w));
}


static void
addrprint(FILE *f,
	  vaddr_t vloc,
	  enum fevmmode mode)
{
    register pment_t *map = cpu.vmap.cur;
    register int ch;

    switch (mode) {
    default:
    case FEVM_CUR:	ch = 0;		break;
    case FEVM_PHYS:	ch = (map == pr_pmap)     ? 0 : 'P'; break;
    case FEVM_USER:	ch = (map == cpu.pr_umap) ? 0 : 'U'; break;
    case FEVM_EXEC:	ch = (map == cpu.pr_emap) ? 0 : 'E'; break;
    case FEVM_ACB:	ch = 'A';	break;
    }

    if (ch) {
	putc(ch, f);
	putc(' ', f);
    }
    if (va_lh(vloc)) {
	fprintf(f, "%lo,,", (long)va_lh(vloc));
    }
    fprintf(f, "%#lo", (long)va_insect(vloc));
}

static char *
strf6(char **acp,
      register w10_t w)
{
    register int i = 6;
    char *rp = *acp;
    register char *cp = rp;

    while (--i >= 0) {
	if (!LHGET(w) && !RHGET(w)) break;
	w = op10rot(w, 6);	/* Rotate high 6 bits to low 6 */
	*cp++ = (RHGET(w) & 077) + 040;
	RHSET(w, RHGET(w) & ~077);
    }
    *cp++ = 0;			/* Note moves past the nul char! */
    *acp = cp;			/* Update pointer */
    return rp;			/* And return start of original */
}

/* ITS disk-format time: */
#define DFTM_YEAR 0177000	/* LH: 4.7-4.1 Year, mod 100. */
#define DFTM_MON     0740	/* LH: 3.9-3.6 Month, 1=Jan */
#define DFTM_DAY      037	/* LH: 3.5-3.1 Day, 1-31 */
#define DFTM_SECS 0777776	/* RH: 2.9-1.2 Secs in day */
#define DFTM_HSEC      01	/* RH:     1.1 Half-second resolution */

static struct tm *
timefrits(register struct tm *t,
	  register w10_t w)
{
    t->tm_year = (LHGET(w) & DFTM_YEAR) >> 9;
    t->tm_mon = ((LHGET(w) & DFTM_MON) >> 5) - 1;
    t->tm_mday = (LHGET(w) & DFTM_DAY);
    t->tm_hour = RHGET(w) / (60*60*2);
    t->tm_min = (RHGET(w) % (60*60*2)) / 60;
    t->tm_sec = (RHGET(w) % (60*60*2)) % 60;
    return t;
}

/* FC_LOAD - Load given filename into PDP10 physical memory.
**	May use any executable type, stored on disk in any 36-bit format.
*/
int ld_debug = 0;	/* TRUE to show debug info; use SET to change */

static void
fc_load(struct cmd_s *cm)
{
    WFILE lwf;
    register FILE *f;
    int res = 0;
    int wft = -1;
    char *farg;

    if (!cmdargs_one(cm, &farg))
	return;

    if (!aprhalted()) {
	printf("KN10 still running!  Halt or Reset it first.\n");
	return;
    }

    /* Open file for reading */
    if (!(f = fopen(farg, "rb")))
	syserr(errno, "Couldn't open load file \"%s\"", farg);

    /* Determine word format to use */
    if (ld_fmt) {
	if ((wft = wf_type(ld_fmt)) < 0)
	    printf("Unknown ld_fmt setting \"%s\", using default.\n", ld_fmt);
    }
    if (wft < 0) {
	if ((wft = wf_type(ld_dfmt)) < 0)
	    wft = WFT_U36;
    }
    wf_init(&lwf, wft, f);		/* Init WFILE */
    printf("Using word format \"%s\"...\n", wf_typnam(&lwf));

    ld_inf.ldi_type = LOADT_UNKNOWN;	/* Init ld_inf (assume unknown) */
    ld_inf.ldi_debug = ld_debug;

    res = fe_load(&lwf, &ld_inf); 
    if (!res) {
	printf("Load failed for \"%s\".\n", farg);
    } else {
	printf("Loaded \"%s\":\n", farg);
	ddt_loadsa = RHGET(ld_inf.ldi_startwd);	/* Set default GO address */
    }
    printf("Format: %s\n", ld_inf.ldi_typname);
    printf("Data: %d, Symwds: %d, Low: %#lo, High: %#lo, Startaddress: %#lo\n",
		ld_inf.ldi_ndata, ld_inf.ldi_nsyms,
		(long)ld_inf.ldi_loaddr, (long)ld_inf.ldi_hiaddr,
		(long) RHGET(ld_inf.ldi_startwd));
    if (ld_inf.ldi_evlen != -1) {
	if (ld_inf.ldi_evlen != ((h10_t)I_JRST<<9))
	    printf("Entvec: %#lo wds at %#lo\n",
			(long) ld_inf.ldi_evlen, (long) ld_inf.ldi_evloc);
	else
	    printf("\
Entvec: JRST (120 ST: %#lo, 124 RE: %#lo, 137 VR: %lo,,%lo)\n",
			(long) vm_pgetrh(fevm_map(0120)),
			(long) vm_pgetrh(fevm_map(0124)),
			(long) vm_pgetlh(fevm_map(0137)),
			(long) vm_pgetrh(fevm_map(0137)));
    }

    if (ld_inf.ldi_aibgot) {
	register w10_t *aib = &ld_inf.ldi_asminf[0];
	char line[100];
	char *s = line;
	struct tm t;

	timefrits(&t, aib[AIB_TIME]);	/* Decompose ITS time */
	printf("\
Assembled by %s on %04d-%02d-%02d %02d:%02d:%02d from file \"%s:%s;%s %s\"\n",
		strf6(&s, aib[AIB_UNAME]),
		t.tm_year + 1900, t.tm_mon+1, t.tm_mday,
		t.tm_hour, t.tm_min, t.tm_sec,
		strf6(&s, aib[AIB_DEV]),
		strf6(&s, aib[AIB_DIR]),
		strf6(&s, aib[AIB_FN1]),
		strf6(&s, aib[AIB_FN2]) );
    }

    fclose(f);
}

/* FC_DUMP - Dump PDP10 physical memory into given filename.
**	Assumes either ITS SBLK or DEC CSAV format, stored on disk using
**	a selectable word mode.
*/
static void
fc_dump(struct cmd_s *cm)
{
    WFILE lwf;
    register FILE *f;
    int res = 0;
    int wft = -1;
    char *farg;

    if (!cmdargs_one(cm, &farg))
	return;
    if (!aprhalted()) {
	printf("KN10 still running!  Halt or Reset it first.\n");
	return;
    }
    /* Open file for writing */
    if (!(f = fopen(farg, "wb")))
	syserr(errno, "Couldn't open dump file \"%s\"", farg);

    /* Determine word format to use */
    if (ld_fmt) {
	if ((wft = wf_type(ld_fmt)) < 0)
	    printf("Unknown ld_fmt setting \"%s\", using default.\n", ld_fmt);
    }
    if (wft < 0) {
	if ((wft = wf_type(ld_dfmt)) < 0)
	    wft = WFT_U36;
    }
    wf_init(&lwf, wft, f);		/* Init WFILE */
    printf("Using word format \"%s\"\n", wf_typnam(&lwf));

#if KLH10_SYS_ITS
    printf("Using dump format ITS-SBLK\n");
    ld_inf.ldi_type = LOADT_SBLK;
#else
    printf("Using dump format DEC-CSAV\n");
    ld_inf.ldi_type = LOADT_DECSAV;
#endif
    ld_inf.ldi_debug = TRUE;
    ld_inf.ldi_loaddr = 0;
    ld_inf.ldi_hiaddr = H10MASK;

    res = fe_dump(&lwf, &ld_inf); 
    if (!res) {
	printf("Dump failed for \"%s\".\n", farg);
    } else {
	printf("Dumped \"%s\":\n", farg);
    }
    printf("Format %s, wftype %s\n", ld_inf.ldi_typname, wf_typnam(&lwf));
    printf("%ld words dumped from range %lo to %lo inclusive.\n",
		(long) ld_inf.ldi_ndata, (long) ld_inf.ldi_loaddr,
		(long) ld_inf.ldi_hiaddr);

    fclose(f);
}


/* FC_LIGHTS - Sets console lights I/O base address
** Currently only allow LPT1 and LPT2 ports on PC.
*/
static void
fc_lights(struct cmd_s *cm)
{
#if KLH10_DEV_LITES		/* Moby conditional for entire file */
    unsigned long port = 0;
    int c;
    char *sloc = cm->cmd_arglin;

    if (sloc && *sloc) {
        while(isxdigit(c = *sloc++)) {
	    port *= 16;
	    port += c - (isdigit(c) ? '0' : (islower(c) ? 'a' : 'A'));
	}
	if (!c) switch(port) {
	case 0x378:		/* LPT1 */
	case 0x278:		/* LPT2 */
	    if (!lites_init((unsigned int) port))
		printf("?Can't init lights -- probably not root\n");
	    return;
	}
    }
    printf("?Bad address\n");
#endif /* KLH10_DEV_LITES */
}

/* Instruction printing routines */

void
pinstr(FILE *f,
       register w10_t w,	/* Instruction word */
       int flags,		/* PINSTR_ flags */
       vaddr_t e)		/* E to use, if PINSTR_EA given */
{
    register int op, ac, x;
    register vmptr_t vp;
    register int opflg;
    struct opdef *opdf;

    op = iw_op(w) & 0777;		/* Get op, with some paranoia */
    ac = iw_ac(w);

    opdf = opcptr[op];			/* Get ptr to opcode definition */
    opflg = opdf ? opdf->opflg : 0;	/* Find flags */
    if (!opflg)				/* Cover up for unflagged instrs */
	opflg = IF_1X1;			/* Generic c(AC) & c(E) instr */

    if (opflg & IF_IO) {		/* Special IO-format instr? */
	x = ((op & 077)<<1) | ((ac >> 3)&01);	/* Find device */
	op = ac & 07;			/* Find IO operation */
	opflg = opcioflg[op];		/* Get new flags */
	fprintf(f, "%s", opcionam[op]);
	if (opcdvnam[x])
	    fprintf(f, " %s,", opcdvnam[x]);
	else
	    fprintf(f, " %o,", x<<2);	/* Special shift to emulate asmblr */
    } else {
	if (!opdf || !opdf->opstr)
	    fprintf(f, "%3o", op);
	else {
	    fprintf(f, "%s", opdf->opstr);
	    if (opflg & IF_OPN)
		fprintf(f, "-%03o", op);
	}

	/* If AC field is 0, only print it if AC is used. */
	if (ac || ((opflg & IF_AS) && (opflg&IF_AFMASK) != IF_A0))
	    fprintf(f, " %o,", ac);
	else putc(' ', f);
    }

    /* Now show I,X,Y for instruction.  If X is set, don't show a zero Y. */
    if (iw_i(w))
      putc('@', f);
    x = iw_x(w);
    if (iw_y(w) || !x)		/* Do Y only if NZ or no X */
      fprintf(f, "%lo", (long)iw_y(w));
    if (x)
	fprintf(f, "(%o)", x);

    /* Now see whether to show any args of instruction -- tricky part. */
    if ((flags & PINSTR_OPS)==0)
	return;		/* Nope */

    /* Show AC as operand? */
    if (opflg & IF_AS) {
	if (ac || (opflg & IF_AFMASK)) {
	    fprintf(f, "\t%o/ ", ac);
	    switch (opflg & IF_AFMASK) {
	    case IF_A0:
	    case IF_A1:
		wd1print(f, ac_get(ac));	/* Use current AC block */
		break;
	    case IF_A2:
		wd1print(f, ac_get(ac));	/* Use current AC block */
		fputs(" ? ", f);
		wd1print(f, ac_get(ac_off(ac,1)));	/* Show AC+1 */
		break;
	    case IF_A4:
		wd1print(f, ac_get(ac));	/* Use current AC block */
		fputs(" ? ", f);
		wd1print(f, ac_get(ac_off(ac,1)));	/* Show AC+1 */
		fputs(" ? ", f);
		wd1print(f, ac_get(ac_off(ac,2)));	/* Show AC+2 */
		fputs(" ? ", f);
		wd1print(f, ac_get(ac_off(ac,3)));	/* Show AC+3 */
		break;
	    }
	}
    }


    /* Show E as operand? */
    if (opflg & IF_MFMASK) {
	putc('\t', f);			/* Space out */
#if KLH10_EXTADR
/*
	* ERROR * Need to revise for XA eacalc?
*/
#endif
	if ((flags & PINSTR_EA)==0) {	/* Try computing E if not given */
	    flags |= PINSTR_EA;		/* Assume will succeed */
	    if (LHGET(w) & (IW_I|IW_X)) {
		w10_t eawd;
		eawd = w;
		if (!fevm_tryeacalc(&eawd, cpu.acblk.cur, cpu.vmap.cur, 2))
		    flags &= ~PINSTR_EA;	/* Oops, didn't succeed */
		else va_lmake(e, 0, RHGET(eawd));
	    } else va_lmake(e, 0, RHGET(w));
	}
	if ((flags & PINSTR_EA)==0)
	    fprintf(f, "E = \?\?");
	else switch (opflg & IF_MFMASK) {
	    case IF_M1:		/* Operand is 1 word */
	    case IF_M2:		/*   "	2 words (double) */
	    case IF_M4:		/*   "	4 words (quad) */
	    case IF_MIN:	/*   "  instruction at E */

#if KLH10_EXTADR
/*
	* ERROR * Need to revise for XA eacalc?
*/
#endif
		fprintf(f, "%lo/ ", (long)va_insect(e));
		if (!(vp = fevm_xmap(e, FEVM_CUR))) {
		    fprintf(f, "\?\?");
		    break;
		}
		w = vm_pget(vp);
		switch (opflg & IF_MFMASK) {
		case IF_M1:
		    wd1print(f, w);	/* Show word at E */
		    break;
		case IF_M2:		/* Show double at E */
		case IF_M4:
		    wd1print(f, w);	/* Do first word */
		    fprintf(f, " ? ");
		    va_inc(e);		/* Point to next word */
		    if (vp = fevm_xmap(e, FEVM_CUR))
			wd1print(f, vm_pget(vp));
		    else fprintf(f, "-\?\?-");
		    break;
		case IF_MIN:	/* Show instruction at E */
		    pinstr(f, w, 0, e);	/* "e" just a handy null vaddr_t */
		    break;
		}
		break;
	    case IF_ME:		/*   "	E (immediate) */
	    case IF_MEIO:	/*   "	IO register (Unibus) */
		fprintf(f, "E = %lo", (long)va_insect(e));
		break;
	    case IF_ME8:	/*   "  signed 8-bit E */
		{   paddr_t pa = va_insect(e);
		    fprintf(f, "E = %lo = %d.", (long)pa,
			(int)((pa&0400) ? (pa | ~0377) : (pa & 0377)));
		}
		break;
	    case IF_MEF:	/*   "	floating immediate */
		fprintf(f, "E = %lo = (?.?)", (long)va_insect(e));
		break;
	}
    }

    /* Show any special stuff for instruction? */
    if (opflg & IF_SPEC) {
    }
}
