1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-03-05 10:53:51 +00:00
Files
PDP-10.klh10/src/klh10.c
2017-01-27 22:12:43 +01:00

2925 lines
74 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 "kn10cpu.h"
#include "wfio.h"
#include "fecmd.h"
#include "feload.h"
#include "cmdline.h"
#include "prmstr.h"
#include "dvcty.h" /* For cty_ functions */
#if KLH10_CPU_KS
# include "dvuba.h" /* So can get at device info */
#endif
#if KLH10_DEV_LITES
# include "dvlites.h"
#endif
#ifdef RCSID
RCSID(klh10_c,"$Id: klh10.c,v 2.9 2002/05/21 16:54:32 klh Exp $")
#endif
/* Exported functions */
#include "klh10exp.h"
/* 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 */
static char cmdbuf[CMDBUFLEN]; /* Original command string buffer */
#if 0
static char cmdwbf[CMDBUFLEN]; /* Working buffer */
#endif
static void slinlim(char *);
static struct cmd_s command;
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_rquit, fc_rquit, CMRF_NOARG, NULL,
"Really quit!", "")
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
KEYSBEGIN(fectbkeys)
KEYDEF("?", cd_ques)
KEYDEF("help", cd_help)
KEYDEF("exit", cd_quit)
KEYDEF("quit", cd_quit)
KEYDEF("really-quit", cd_rquit)
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, fectbkeys, 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 'y':
case 'Y':
case EOF: /* 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);
}
void
fc_rquit(struct cmd_s *cm)
{
dev_term();
mem_term();
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,
struct cmkey_s *keys,
char *prompt,
char *ibuf,
size_t ilen)
{
cm->cmd_keys = keys;
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, cm->cmd_keys, 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;
}
/*
** Fetch a command line from the front end console, or from a command
** file.
*/
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) {
if (feof(stdin) || ferror(stdin)) {
cp = strncpy(cm->cmd_inp, "exit", (int)cm->cmd_left-1);
fprintf(stdout, "exit\n");
clearerr(stdin);
} else {
return NULL;
}
}
}
len = strlen(cp);
cm->cmd_left -= len;
cm->cmd_inp += len;
return cp;
}
/* CMDLCOPY - Copy a command line into the previously indicated buffer.
*/
char *
cmdlcopy(struct cmd_s *cm, char *line)
{
int len = strlen(line);
if (len > cm->cmd_blen) {
len = cm->cmd_blen - 1;
}
if (cpu.fe.fe_debug)
fprintf(stderr, "[cmdlcopy]");
strncpy(cm->cmd_buf, line, len);
cm->cmd_buf[len] = '\0';
cm->cmd_rdp = cm->cmd_buf;
cm->cmd_inp = cm->cmd_buf + len;
cm->cmd_rleft = len;
cm->cmd_left = cm->cmd_blen - len;
return cm->cmd_buf;
}
/* 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");
}
void
fc_gques(struct cmd_s *cm)
{
fc_ques(cm);
}
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 = cm->cmd_keys; kp->cmk_key; ++kp) {
helpline(kp);
}
return;
}
(void) s_keylookup(cp, (voidp_t)cm->cmd_keys, 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 = cm->cmd_keys; kp->cmk_key; ++kp) {
if (smatch(cp, kp->cmk_key) > 0)
helpline(kp);
}
}
void
fc_ghelp(struct cmd_s *cm)
{
fc_help(cm);
}
/* 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);
}
#if KLH10_DEV_LITES
/* 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)
{
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) {
}
}