1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-03-07 19:30:46 +00:00
Files
PDP-10.klh10/src/fecmd.c
2015-12-16 00:52:52 +01:00

526 lines
13 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.
/* FECMD.C - FE command processing and CTY utilities
*/
/* $Id: fecmd.c,v 2.2 2002/05/21 09:43:39 klh Exp $
*/
/* Copyright © 2002 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: fecmd.c,v $
* Revision 2.2 2002/05/21 09:43:39 klh
* Tweak so will compile without KLH10_CTYIO_INT
*
* Revision 2.1 2002/03/21 09:46:52 klh
* Initial 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 "feload.h"
#include "prmstr.h"
#include "dvcty.h" /* For cty_ functions */
#include "fecmd.h"
#include "klh10exp.h"
#if KLH10_CPU_KS
# include "dvuba.h" /* So can get at device info */
#endif
#ifdef RCSID
RCSID(fecmd_c,"$Id: fecmd.c,v 2.2 2002/05/21 09:43:39 klh Exp $")
#endif
/*
FE command parsing and CTY utilities
The distinction between the various modules that are interested in
the user's TTY can get blurry. Here is a rough outline of how the
duties are divided up:
Main - Top level setup (KLH10.C).
Handles command line switches, including init file setup.
The init file becomes the first FE input, before any
TTY input is read.
FE - the "Front End" (FECMD.C).
The KLH10 "FE" handles all terminal I/O for
the KLH10 user interface, as well as passing it
to and from the PDP-10 console when in "user" mode.
It is not intended to resemble either a KL FE (a PDP-11)
or a KS FE (a 8080).
Note that the actual emulation of the 11 or 8080 side of
communications with the PDP-10 is carried out by DVDTE.C
(for the KL) or DVCTY.C (for the KS).
CTY - the PDP-10 Console TTY (DVCTY.C).
For KA/KI/6 this is an actual device instance, which
interacts with FE code to do I/O.
For the KL this is not a device, but rather contains
glue to hook up FE I/O with a true device (DVDTE)
that provides the "console I/O" method the 10 expects.
The KS is similar to the KL but there is no DVDTE and
the "console I/O" method is entirely done by DVCTY.
OS_TTY* - provides access to the native host's TTY functions.
These should normally only be called by FECMD when needed,
so that the FE code can decide where I/O should actually
be going.
*/
/* Control flow:
At top level in klh10_main() there is a loop that reads and excecutes
FE commands, some of which will start the KN10 running by calling aprcont(),
which calls apr_run(). The KN10 continues to run until something halts it
by causing a return from apr_run() with haltcode value. Control then
returns back to the caller of aprcont() which is normally a FE command
routine that returns to the top level loop.
If fe_runenable is not set, all FE I/O is simply passed on to the 10 CTY.
Commands are not accumulated or executed, and control only returns to the FE if
the CPU halts for some reason (including a FE interrupt char) as described
above.
This works by having the FE interrupt char signal handler set
the cpu.intf_fecty flag, which causes the APR run loop to
halt with the haltcode HALT_FECTY.
But if fe_runenable is set... the FE can be in an additional mode where
TTY input is accumulated in a command buffer rather than passed to the
10 CTY. In this mode:
aprcont() has called apr_run() and the CPU is running.
The TTY remains set up for normal command-line mode, after
an initial prompt has been output.
TTY input is accumulated into a command buffer. This is done
using the line edit facility of the native OS, and assumes
we will get either a SIGIO or a positive # chars waiting
as soon as an input line is completed.
CTY output is suppressed (not discarded) if possible.
When a full command is received:
The FE input code tickles cpu.intf_fecty to simulate a FE interrupt.
This is distinguished from a real signal by a cause value in
the feregs struct, and a different haltcode is returned.
The top-level loop will notice on return from the FE command
that the command buffer is set up and will parse/execute that new
command, which may do anything including halting or restarting the CPU.
If the mode remains the same, the top-level loop will then invoke
aprcont() directly to proceed with KN10 execution,
instead of blocking to obtain a new command line from the
TTY.
Control does not necessarily have to return to the top level loop
for each command. However, it DOES have to return from apr_run in order
to execute commands, because there is no way to predict what the command
might change in terms of state, and the loop that apr_run is actually
using internally depends on this state. For example, setting a breakpoint
or turning tracing on requires a fresh invocation of apr_run.
So the FE cannot simply do an internal execution of the commands it
receives during KN10 operation.
*/
/* Called just before KN10 starts running
*/
void
fe_ctyenable(int mode)
{
switch (cpu.fe.fe_mode = mode) {
case FEMODE_CMDRUN:
fe_ctycmdrunmode();
break;
default:
fe_ctyrunmode();
#if KLH10_CTYIO_INT
fe_iosig(0); /* Trigger initial test */
#endif
break;
}
}
/* Called just after KN10 stops running
*/
void
fe_ctydisable(int mode)
{
if (cpu.fe.fe_mode == mode)
return; /* No change, nothing needed */
switch (cpu.fe.fe_mode = mode) {
case FEMODE_CMDRUN:
fe_ctycmdrunmode();
break;
case FEMODE_CMDHALT:
default:
fe_ctycmdmode();
}
}
/* Controlling terminal stuff
**
** Note hack to support background mode.
** If the local "ttyback" flag is set, all os_tty functions
** pretend to work but don't actually do anything other than output, which
** is assumed to have been redirected on the command line to some file/pipe.
**
** It is legal to run in the background with FEMODE_CMDRUN; input
** may come from sources other than the TTY.
**
** During FEMODE_CMDCONF and FEMODE_CMDHALT:
** FE input comes from file if present, else TTY if not in background.
** FE output goes to stdout (normally TTY)
** CTY I/O is disabled.
** During FEMODE_CMDRUN:
** FE input comes from file if present, else TTY if not in background.
** FE output goes to stdout (normally TTY)
** CTY I/O is disabled.
** During FEMODE_CTYRUN:
** FE input comes from TTY (if not in background). [1]
** FE output goes to stdout (normally TTY)
** CTY input comes from FE input.
** CTY output goes to FE output.
**
** [1] a possible enhancement would be to allow file input, either
** anytime the KN10 seems ready to accept input or in response to
** a certain sequence of KN10 output. Usual scripting stuff.
*/
static int ttyback = FALSE; /* Initially false, not in background */
static void
fe_sigtermhdl(int sig)
{
if (ttyback) {
printf("[Termination signal received - invoking auto-shutdown!]\n");
fe_shutdown();
}
}
void
int_fecty(int sig)
{
INTF_SET(cpu.intf_fecty); /* Say FE CTY interrupt went off */
INSBRKSET(); /* Say something happened */
cpu.fe.fe_intcnt++; /* Remember # seen */
}
/* SIGIO interrupt handler.
** Intention here is that this indicate input available on the TTY
** input fd.
** It needs to read all available input as SIGIO only happens when
** the input state changes from none to some. Sets a flag which remembers
** whether the last check indicated more input or not.
** For proper operation, this signal must go off differently in the two
** running modes:
** CMDRUN - triggers when a complete input line is ready.
** CTYRUN - triggers as soon as any input available.
**
** Don't try to do any debug output from inside this or SIGIO will
** be triggered infinitely!
*/
int feiosignulls = 0;
int feiosiginps = 0;
int feiosigtests = 0;
#if KLH10_CTYIO_INT
void
fe_iosig(int junk)
{
if ((cpu.fe.fe_ctyinp = fe_ctyintest()) <= 0) {
++feiosignulls;
return;
}
feiosiginps++;
if (cpu.fe.fe_mode == FEMODE_CMDRUN) {
INTF_SET(cpu.intf_fecty); /* FE input avail, don't bump intcnt */
} else
INTF_SET(cpu.intf_ctyio); /* CTY input available */
INSBRKSET();
}
void
fe_iosigtest(void)
{
++feiosigtests;
fe_iosig(0);
}
#endif
/* Return appropriate haltcode to apr_check when it
** determines that a FE interrupt is happening, either directly due
** to interrupt char or indirectly via input ready.
*/
enum haltcode
fe_haltcode(void)
{
register int hcode;
register int ocnt;
switch (ocnt = cpu.fe.fe_intcnt) { /* FE console interrupt? */
case 0:
/* Not a direct int, so assume it was manually tickled by
an input-ready condition.
*/
hcode = HALT_FECMD; /* FE input available */
break;
case 1:
/* Single interrupt - return to cmd mode if allowed */
if (cpu.fe.fe_runenable) {
cpu.fe.fe_intcnt = 0;
hcode = HALT_FECMD;
break;
}
/* Fall thru */
default:
/* Multiple interrupts or not allowing cmdrun */
cpu.fe.fe_intcnt = 0;
hcode = HALT_FECTY;
break;
}
if (cpu.fe.fe_debug)
fprintf(stderr,
"[haltcode: %s cnt %d isigs %d nullsigs %d intf_fecty %d]",
(hcode==HALT_FECMD) ? "FECMD" : "FECTY",
ocnt, feiosiginps, feiosignulls, cpu.intf_fecty);
return hcode;
}
void
fe_ctyinit(void)
{
osttyinit_t osti;
extern char *cmdprompt;
fe_cmpromptset(cmdprompt);
osti.osti_intchr = cpu.fe.fe_intchr;
osti.osti_inthdl = int_fecty;
osti.osti_attrs = (OSTI_INTCHR|OSTI_INTHDL);
if (proc_bkgd) {
ttyback = TRUE;
osti.osti_attrs |= OSTI_BKGDF;
osti.osti_bkgdf = TRUE;
osti.osti_trmhdl = fe_sigtermhdl;
}
os_ttyinit(&osti);
}
#if KLH10_CTYIO_INT
void
fe_ctysig(ossighandler_t *rtn)
{
if (!ttyback)
os_ttysig(rtn);
}
#endif /* KLH10_CTYIO_INT */
void
fe_ctyreset(void)
{
if (!ttyback)
os_ttyreset();
}
void
fe_ctycmdmode(void)
{
if (!ttyback)
os_ttycmdmode();
}
void
fe_ctycmdrunmode(void)
{
if (!ttyback)
os_ttycmdrunmode();
}
void
fe_ctyrunmode(void)
{
if (!ttyback)
os_ttyrunmode();
}
/* FE_CTYINTEST - See if any TTY input available, and returns count of chars.
** If count unknown, OK to return just 1; routine will be invoked
** frequently (either by clock timeout or by I/O signal).
*/
int
fe_ctyintest(void)
{
if (!ttyback)
return os_ttyintest();
return 0;
}
int
fe_ctyin(void)
{
if (ttyback) {
printf("[TTYIN while in background; invoking auto-shutdown!]\n");
fe_shutdown();
/* Never returns */
}
return os_ttyin();
}
int
fe_ctyout(int ch)
{
return os_ttyout(ch);
}
/* TTY String output.
** Note assumption that 8th bit is already masked, if desired.
** Call may block, sigh.
*/
int
fe_ctysout(char *buf, int len) /* Note length is signed int */
{
return os_ttysout(buf, len);
}
/* Top-level command character input
** May want to be different from fe_ctyin().
*/
int
fe_ctycmchar(void)
{
if (ttyback) {
printf("[TTYCMCHAR while in background; invoking auto-shutdown!]\n");
fe_shutdown();
/* Never returns */
}
return os_ttycmchar();
}
char *
fe_ctycmline(char *buffer, int size)
{
if (ttyback) {
printf("[TTYCMLINE while in background; invoking auto-shutdown!]\n");
fe_shutdown();
/* Never returns */
}
return os_ttycmline(buffer, size);
}
void
fe_ctycmforce(void)
{
os_ttycmforce();
}
/* FE command prompt stuff
* Prompt string has special format. 1st char is delimiter
* dividing string into 3 prompts (1 for each mode of
* CMDCONF, CMDHALT, CMDRUN).
*/
#define NPROMPTS 3
#define PROMPTSIZ 40*NPROMPTS
static struct promptent {
int siz;
char *ptr;
} prompttab[NPROMPTS] = {
{ 7, "KLH10# " },
{ 7, "KLH10> " },
{ 8, "KLH10>> " }
};
static char promptbuf[PROMPTSIZ];
void
fe_cmpromptset(char *str)
{
register char *cp;
char *fcp = str;
size_t flen = strlen(str);
char *tcp = promptbuf;
size_t left = sizeof(promptbuf)-1;
int idx = 0;
char *tab[NPROMPTS];
int delim = 0;
/* Get prompt into buffer */
if (flen >= left)
flen = left-1;
memcpy(promptbuf, str, flen);
promptbuf[flen] = '\0';
delim = *tcp++;
--flen;
for (idx = 0; idx < NPROMPTS; ++idx) {
prompttab[idx].ptr = tcp;
if (flen && (cp = strchr(tcp, delim))) {
flen -= (prompttab[idx].siz = cp - tcp) + 1;
*cp++ = '\0';
tcp = cp;
continue;
}
if (idx == 0) { /* If nothing but 1 prompt */
prompttab[idx].ptr = --tcp;
prompttab[idx].siz = ++flen;
flen = 0;
} else if (flen) { /* Mult prompts and not 1st */
prompttab[idx].siz = flen;
flen = 0;
} else /* Empty, use previous */
prompttab[idx] = prompttab[idx-1];
}
}
char *
fe_cmprompt(int mode)
{
switch (mode) {
case FEMODE_CMDCONF:
return prompttab[0].ptr;
case FEMODE_CMDHALT:
return prompttab[1].ptr;
case FEMODE_CMDRUN:
return prompttab[2].ptr;
}
return "";
}