/*  vios.c -- VIOS Command Interpreter for AIS 3200 SuperMicro		V6.0
 *  copyright (c)  American Information Systems Corporation
 *	Daniel Steinberg
 *	November, 1984
 *
 */

#include "viosconf.h"
#include "iopacket.h"
#include "devfuncs.h"
#include "pktfuncs.h"
#include "poolfuncs.h"
#include "viocmds.h"
#include "viostatus.h"
#include "ascii.h"
#include "hostflags.h"
#include "hostprot.h"

#define DEBUG_QBUS

#undef FCODE
#define FCODE(a,b,c) (((((unsigned)(c) * 16) + (unsigned)(b)) * 16) + (unsigned)(a))

#define BOOTFILE "viosboot"

#define NUMBUFS 25
#define MAXLINE MAX_NAMELENGTH

#define NCMDS (sizeof(commands)/sizeof(CMD_DEF))
#define NARGS ((sizeof(CMD_DEF)-sizeof(char *)-sizeof(int))/sizeof(C_ARG))

typedef struct
    {
    PKT_IOSTATUS pktstat;
    PKT_PTR pktptr;
    }  SIM_IOSTATUS;


#define Scode(x)	((x)->pktstat.code)
#define Sauxcode(x)	((x)->pktstat.aux_code)
#define Sbcount(x)	((x)->pktstat.bcount)
#define Saux1(x)	((x)->pktstat.bcount)
#define Saux2(x)	((x)->pktstat.aux_stat2)
#define Saux3(x)	((x)->pktstat.aux_stat3)
#define Saux4(x)	((x)->pktstat.aux_stat4)
#define Spkt(x)		((x)->pktptr)

typedef struct
    {
    unsigned siz;
    unsigned blks;
    unsigned info3;
    unsigned info4;
    char name[64];
    }  INFO_STRUCT;

typedef struct
    {
    unsigned v_func;
    unsigned v_dev;
    unsigned v_buf;
    unsigned v_bufsiz;
    unsigned v_bufmap;
    char *v_aux;
    unsigned v_p1;
    unsigned v_p2;
    unsigned v_p3;
    unsigned v_p4;
    SIM_IOSTATUS *v_stat;
    int (*v_comp)();
    unsigned v_pri;
    unsigned v_tmo;
    } VIO_RQ;

#define U(x) ((unsigned)(x))

#define Vfunc(x)	((x)->v_func)
#define Vdev(x)		((x)->v_dev)
#define Vbuf(x)		((x)->v_buf)
#define Vbufsiz(x)	((x)->v_bufsiz)
#define Vbufmap(x)	((x)->v_bufmap)
#define Vaux(x)		((x)->v_aux)
#define Vrp1(x)		((x)->v_p1)
#define Vrp2(x)		((x)->v_p2)
#define Vrp3(x)		((x)->v_p3)
#define Vrp4(x)		((x)->v_p4)
#define Vstat(x)	((x)->v_stat)
#define Vcomp(x)	((x)->v_comp)
#define Vpri(x)		((x)->v_pri)
#define Vtmo(x)		((x)->v_tmo)

typedef char *DEVPAIR[2];

static DEVPAIR rdevs[] = {
			{"AIS:tty0", "TTY0"},
			{"AIS:tty1", "TTY1"},
			{"AIS:tty2", "TTY2"},
			{"AIS:tty3", "TTY3"},
			{NULL,NULL}
			};


#define EMPTY 0
#define INI 1
#define ALL 2
#define FRE 3
#define MAPP 4
#define POI 5
#define XIT 6
#define CRE 7
#define REM 8
#define VDIS 9
#define TCRE 10
#define ASS 11
#define PDIS 12
#define WRI 13
#define REA 14
#define PCRE 15
#define PREM 16
#define BWRI 17
#define BREA 18
#define BOOT 19
#define LOC 20
#define HCNCT 21
#define IOCTRL 22
#define BBOOT 23
#define MKD 24
#define PLOC 25
#define CRD 26
#define COC 27
#define CLCK 28

typedef struct
    {
    char *pmt;		/* Argument prompt string */
    char *fmt;		/* Argument format conversion string */
    unsigned *vali;		/* Pointer to int argument */
    }  C_ARG;

typedef struct
    {
    char *name;		/* Ptr to string that is command name */
    int nargs;		/* Number of command arguments */
    int sim_routine;	/* Enumerated name of processing case */
    C_ARG arg[5];	/* array of argument structs */ /*?? 5 ??*/
    }  CMD_DEF;

#define vc0 ((unsigned)vc_params[0])
#define vc1 ((unsigned)vc_params[1])
#define vc2 ((unsigned)vc_params[2])
#define vc3 ((unsigned)vc_params[3])

static CMD_DEF commands[] = {
	{"host-console", 1, HCNCT,	"pd-name", "%s", (unsigned*)vc_name},

	{"boot", 1, BOOT,	"address", "%x", &vc2},

	{"bboot", 1, BBOOT,	"address", "%x", &vc2},

	{"make-device", 5, MKD,	"vd-name", "%s", (unsigned*)vc_name,
				"class", "%d", &vc0,
				"buf", "%d", &vc1,
				"target dev", "%s", (unsigned*)vc_tname,
				"access", "%d", &vc2},

	{"create-vd", 3, CRE,	"vd-name", "%s", (unsigned*)vc_name,
				"class", "%d", &vc0,
				"buf", "%d", &vc1},

	{"assign-vd", 3, ASS,	"vd-name", "%s", (unsigned*)vc_name,
				"units", "%d", &vc3,
				"target dev", "%s", (unsigned*)vc_tname},

	{"plocate-pd", 3, PLOC, "pd-name", "%s", (unsigned*)vc_name,
				"class", "%d", &vc0,
				"access", "%d", &vc3},

	{"pcreate-pd", 5, PCRE, "pd-name", "%s", (unsigned*)vc_name,
				"class", "%d", &vc0,
				"access", "%d", &vc3,
				"param2", "%d", &vc1,
				"param3", "%d", &vc2},

	{"remove-vd", 1, REM,	"vd-name", "%s", (unsigned*)vc_name},

	{"premove-pd", 1, PREM, "pd-name", "%s", (unsigned*)vc_name},

	{"bwrite", 4, BWRI,	"vd-name", "%s", (unsigned*)vc_name,
				"block #", "%d", &vc0,
				"address", "%x", &vc2,
				"length", "%d", &vc3},


#ifdef DEBUG0	/*************************************************************/

	{"locate-vd", 1, LOC,	"vd-name", "%s", (unsigned*)vc_name},

	{"write", 2, WRI,	"vd-name", "%s", (unsigned*)vc_name,
				"string", "%s", (unsigned*)vc_tname},

	{"read", 2, REA,	"vd-name", "%s", (unsigned*)vc_name,
				"modifier", "%d", &vc2},

	{"bread", 4, BREA,	"vd-name", "%s", (unsigned*)vc_name,
				"block #", "%d", &vc0,
				"address", "%x", &vc2,
				"length", "%d", &vc3},

	{"ioctrl", 4, IOCTRL,	"vd-name", "%s", (unsigned*)vc_name,
				"cmd", "%d", &vc2,
				"param1", "%d", &vc0,
				"param2", "%d", &vc1},

	{"allocate", 1, ALL,	"alloc size", "%d", &vc3},

	{"free", 1, FRE,	"ptr number", "%d", &vc2},

	{"pointers", 0, POI,	},

#endif /* DEBUG0 *************************************************************/

#ifdef DEBUG_QBUS /*********************************************************/

#ifdef notdef
	{"cloc", 3, COC,	"pd-name", "%s", (unsigned*)vc_name,
				"class", "%d", &vc0,
				"access", "%d", &vc3},

	{"clck", 0, CLCK,	},
#endif

	{"crd", 4, CRD,		"vd-name", "%s", (unsigned*)vc_name,
				"block #", "%d", &vc0,
				"address", "%x", &vc2,
				"length", "%d", &vc3},

#endif /* DEBUG_QBUS *********************************************************/

#ifdef DEBUG_MAP /************************************************************/
	{"map-pool", 0, MAPP,	},
#endif /* DEBUG_MAP **********************************************************/


	{"Vdisplay", 0, VDIS,	},

	{"Pdisplay", 0, PDIS,	},

	{"brk", 0, XIT, }
			};


static char tabspace[] = {' ',' ',' ',' ',' ',' ',' ',' ',0};	/* tab stops */
#define TABS(x) (&(tabspace[(x)%8]))
static CMD_DEF empty = {"", 0, EMPTY, };	/* used for blank lines */

    static CMD_DEF *parse_cmd();
    static char *delim1();
    static int pkstatp ();
		wrconsole ();
    char *lf2crlf ();
    char *vpmtread ();
    int vread ();
    int vwrite ();
    VDD_PTR mk_vdd();
    VDD_PTR fnd_vdd();
    PDD_PTR mk_pdd();
    PDD_PTR acc_pdd();
    PDD_PTR loc_pdd();

    extern PKT_PTR vios_call ();
    extern PKT_PTR vios_acall ();

#ifdef DEBUG0   /*************************************************************/
    static int findfree ();
#endif /* DEBUG0 *************************************************************/

vios_cmd_entry ()

/* vios_cmd_entry ()
 *
 *	Main VIOS command interpreter
 */
{
	/* Static (or single utility) data storage blocks */
    int *vpool;
    int **ptrs[NUMBUFS];
    char flags[NUMBUFS];
    char cmd[MAXLINE];
    CMD_DEF *cptr =NULL;
    SIM_IOSTATUS v_statblk;
    INFO_STRUCT info;

	/* TEMPORARY */
    register int xxx = 0;		/* R7: flag to operating system */

	/* Temporary storage */
    VDD_PTR p;
    VDD_PTR vdd;
    PDD_PTR pdd;
    int *ip;
    char *c;
    char *c2;
    PKT_PTR pkt;
    unsigned allnum;
    register i, j;

    if ((i=catch()) NE 0)
	error("\n?? uncaught flag %d in simulator ??\n", i);

    vpool = (int*) vios_pool_start;

    for (i=0; i < NUMBUFS; i++)
	flags[i] = '\0';		/* Clear allocated buffer flag */


    /* Create baseline terminal devices */

	/* TTYs */

    for (i=0; rdevs[i][0] NE NULL; i++)		/* loop thru record devices */
	{
	mk_dev(rdevs[i][0], REC_SEQ, LOOKUP_PD|WRITE_ACCESS,
				rdevs[i][1], 0, PRI_INP|PRI_OUT);
	}


	/* CONSOLE */

    mk_ass(mk_vdd("CONSOLE", REC_SEQ, 0), "TTY0", PRI_INP|PRI_OUT);


	/* connect console to host file, if host is connected */
    cnct_hostconsole(BOOTFILE);

    wrconsole("\n");

    do {
    if ( vpmtread(cmd, MAXLINE, "CONSOLE", "VIOS>") EQ NULL )
	{
	errpri("VIOS cmd i/o error\n", NULL);
	xit(1);
	}
    else
	{
	if ((cptr = parse_cmd(cmd)) EQ NULL)
	    {
	    wrconsole("Legal commands are:\n");
	    for (i=0; i<NCMDS; i++)
		{
		wrconsole("  %s%s%s[", commands[i].name,
				TABS(strlen(commands[i].name) + 2),
				(strlen(commands[i].name)<6 ? TABS(0) : ""));
		for (j=0; j<commands[i].nargs; j++)
		    wrconsole("%s%s(%s)%s", (j EQ 0 ? "" : " "),
				commands[i].arg[j].pmt,commands[i].arg[j].fmt,
				((j+1) EQ commands[i].nargs ? "" : ",") );
		wrconsole("]\n");
		}
	    }
	else
	    switch (cptr->sim_routine)
		{
	    case HCNCT:
		c = ( ((vc_name EQ NULL) OR (*vc_name EQ '\0')) ?
							BOOTFILE : vc_name );
		cnct_hostconsole(c);	/* console input to host device/file */
		break;

	    case MKD:
		if (mk_dev(vc_tname, vc_params[0], vc_params[2],
				    vc_name, vc_params[1], PRI_INP|PRI_OUT))
		    wrconsole("       %s -> %s\n", vc_name, vc_tname);
		break;

	    case CRE:
		mk_vdd(vc_name, vc_params[0], vc_params[1]);
		break;

	    case PLOC:
		acc_pdd(vc_name, vc_params[0], vc_params[3]);
		break;

	    case PCRE:
		mk_pdd(vc_name,
			vc_params[0], vc_params[3], vc_params[1], vc_params[2]);
		break;

	    case ASS:
		/* look for virtual device; print error if not found */
		if ((vdd = fnd_vdd(vc_name, TRUE)) NE NULL)	/* if found */
		    mk_ass(vdd, vc_tname, vc_params[3]);
		break;

	    case REM:
		if ((vdd = fnd_vdd(vc_name, TRUE)) NE NULL)	/* if found */
		    rm_vdd(vdd);
		break;

	    case PREM:
		if ((pdd = loc_pdd(vc_name, TRUE)) NE NULL)	/* if found */
		    rm_pdd(pdd);
		break;

	    case BOOT:
	    case BBOOT:
		if ((vdd = fnd_vdd("BOOT", TRUE)) EQ NULL)
		    break;
		pdd = (PDD_PTR) vdd;

		do
		    {
		    vios_cwait(FCODE(VINFO_FUNCTION, DEV_INFO, PRI_INP),
				pdd, &info, sizeof(INFO_STRUCT), PHYS_BUFFER,
				NULL, 0,0,0,0, &v_statblk, NULL, 31, 0);
		    
		    if ( (Scode(&v_statblk) NE V_SUCCESS) OR
				((pdd = (PDD_PTR) Saux2(&v_statblk)) EQ NULL) )
			goto noboot;
		    }
		    while (Saux3(&v_statblk) EQ VDTYPE);

		i = info.blks * info.siz;	/* get size of boot image */

		if (catch() NE 0)		/* catch errors from vread */
		    break;

		j = vread("BOOT", vc_params[2], i, 0, 0);
		uncatch();

		if (j EQ i)		/* if device completely read */
		    {
		    wrconsole("...booting from %s...\n\n", info.name);

		    /* Pop one level of primary input, if pushed */
		    mk_ass( fnd_vdd("CONSOLE",TRUE), "", POP_PRI_INP );

		    vios_disable_completion();	/* shut off OS interrupts */

		    if (cptr->sim_routine EQ BBOOT)
			jump_after_break(vc_params[2]);	/* start after bpt */

		    jump_to(vc_params[2]);	/* start at load address */
		    }

noboot:		wrconsole("** Boot error **\n");
		break;

	    case BWRI:
		if (catch() EQ 0)
		    {
		    vwrite(vc_name, vc_params[2], vc_params[3], vc_params[0]);
		    uncatch();
		    }
		break;


#ifdef DEBUG0	/*************************************************************/

	    case LOC:
		if ((vdd = fnd_vdd(vc_name, TRUE)) NE NULL)
		    wrconsole("VDD for %s at %x\n", vc_name, vdd);
		break;

	    case WRI:
		c = vc_tname + strlen(vc_tname);
		*c++ = '\r';	/* add in <CR><LF> */
		*c++ = '\n';
		*c = 0;

		if (catch() EQ 0)
		    {
		    vwrite(vc_name, vc_tname, strlen(vc_tname), -1);
		    uncatch();
		    }
		break;

	    case REA:
		if (catch() EQ 0)
		    {
		    j = vread(vc_name, vc_tname, MAXLINE-3, vc_params[2], -1);
		    *(vc_tname+j) = '\0';
		    wrconsole("\nInput line: %s\n", vc_tname);
		    uncatch();
		    }
		break;

	    case BREA:
		if (catch() EQ 0)
		    {
		    if ((i = vread(vc_name, vc_params[2],
				vc_params[3], 0, vc_params[0])) NE vc_params[3])
			wrconsole("Bytes read: %d.\n", i);
		    uncatch();
		    }
		break;

	    case IOCTRL:
		/* look for virtual device; print error if not found */
		if ((vdd = fnd_vdd(vc_name, TRUE)) NE NULL)	/* if found */
		    {
		    if ((i = vios_cwait(FCODE(DEVICE_FUNCTION, vc_params[2], 0),
			vdd, NULL, 0,0, NULL, vc_params[0], vc_params[1], 0, 0,
					    NULL, NULL, 31, 0)) NE V_SUCCESS)
			wrconsole("I/O Control error: %d\n", i);
		    }
		break;

	    case ALL:
		if ( 0 EQ (allnum = findfree(flags,NUMBUFS)))
		    {
		    wrconsole("\n  ***  Ptr array full  ***\n");
		    break;
		    }

		if (vc_params[3] EQ 0) vc_params[3] = PKTSIZE;
		if ( (ptrs[--allnum] = (int**) alloc(vc_params[3]*UNITSIZE))
								    NE NULL )
		    flags[allnum]=1;
		else
		    wrconsole("\n ***  Allocation failure  ***\n");

		break;

	    case FRE:
		if ( ((i = vc_params[2]) NE 0) AND (flags[--i] NE 0) )
		    {
		    wrconsole("Total available units = %d.\n",
						    vfree(ptrs[i]));
		    flags[i]=0;
		    vios_entry();	/* do anything waiting for pool */
		    }
		else
		    wrconsole("\nBuffer %d. not in use\n", vc_params[2]);
		break;


	    case POI:
		wrconsole("Allocated buffer list:\n");
		for (i=0; i<NUMBUFS; i++)
		    if (flags[i] NE 0)
			wrconsole("ptr: %d.   size: %d. units\n", i+1,
							*(*(ptrs[i])-2) );
		wrconsole("\n");
		break;

#endif /* DEBUG0 *************************************************************/

#ifdef DEBUG_QBUS /***********************************************************/

#ifdef notdef
	    case COC:
		while (TRUE)
		    {
		    acc_pdd(vc_name, vc_params[0], vc_params[3]);
		    }
		break;

	    case CLCK:
		while (TRUE)
		    {
		    dis_ints();
		    Intstate++;
		    h_snd(sizeof(VCMD), V_LOCK);
		    Intstate--;
		    ena_ints();
		    }
		break;

#endif

	    case CRD:
		if ((vdd = fnd_vdd(vc_name, TRUE)) EQ NULL)
		    break;

		while (TRUE)
		    {
		    if (vios_cwait(FCODE(READ_FUNCTION, READ_DATA, 0), vdd,
			vc_params[2], vc_params[3], PHYS_BUFFER, NULL,
			vc_params[0], 0,0,0, NULL, NULL, 31, 0) NE V_SUCCESS)
			break;
		    }
		break;

#endif /* DEBUG_QBUS *********************************************************/

#ifdef DEBUG_MAP /************************************************************/
	    case MAPP:
		_map_pool(poolmap, mapsize);
		_map_vios(poolmap);

	wrconsole("        ...buffers allocated by simulator...\r", NULL);
		for (i=0; i<NUMBUFS; i++)
		    if (flags[i] NE 0)
			{
			wrconsole("%d.  \r", i+1);
			_map_add(poolmap,ptrs[i],'-');
			};

		wrconsole("             Dynamic storage map:              \n");
		for  (ip=vpool, i=0; i<mapsize; ip++, i++)
		    {
		    if (i%64==0) wrconsole("%x: ", ip);
		    errpri("%c", poolmap[i]);
		    errpri("%s", (i%64==63 ? "\n" : "") );
		    }
		wrconsole("\n");
		break;
#endif /* DEBUG_MAP **********************************************************/

	    case VDIS:
		wrconsole("Virtual Device list:\n");
		p = (VDD_PTR) Vdevices.next;
		while (p NE NULL)
		    {
		    ip = (int*) Devname(*p);

    wrconsole("%x => %x: %s\n", p,*p, (ip NE NULL ? (char*)*ip : "(temp)"));

    wrconsole("                Class: %d   att: %d   i/o: %d   %s   %s\n",
	Devclass(*p), Attachments(*p), Outstanding_IO(*p),
		( Detachremove(*p) ? "Remove on deassign" : "" ),
		( Compremove(*p) ? "Remove on i/o completion" : ""));


    c = (Primaryinput(*p)  ? *Devname(*Primaryinput(*p))  : "(none)");
    c2 = (Primaryoutput(*p) ? *Devname(*Primaryoutput(*p)) : "(none)");
    
    wrconsole("                P-I: %s%sP-O: %s%sERR: %s\n",
	c, TABS(strlen(c)),
	c2, TABS(strlen(c2)),
	(Errorinput(*p)	   ? *Devname(*Errorinput(*p))	  : "(none)") );


    c = (Journalinput(*p) ? *Devname(*Journalinput(*p)) : "(none)");
    c2 = (Journaloutput(*p) ? *Devname(*Journaloutput(*p)) : "(none)");

    wrconsole("                J-I: %s%sJ-O: %s%sEOT: (%s)\n",
	c, TABS(strlen(c)),
	c2, TABS(strlen(c2)),
	((Devclass(*p) EQ REC_SEQ) ?
		(Recs_eot(*p) ? *Devname(*Primaryinput(*Recs_eot(*p))) : "none")
						: "n/a") );

		    p = Next(*p);
		    }
		break;

	    case PDIS:
		wrconsole("Physical Device list:\n");
		p = (VDD_PTR) Pdevices.next;
		while (p NE NULL)
		    {
		    ip = (int*) Devname(*p);

    wrconsole("%x => %x: %s\n", p,*p, (ip NE NULL ? (char*)*ip : "(temp)"));

    wrconsole("                Class: %d   att: %d   i/o: %d   %s   %s\n",
	Devclass(*p), Attachments(*p), Outstanding_IO(*p),
		( Detachremove(*p) ? "Remove on deassign" : "" ),
		( Compremove(*p) ? "Remove on i/o completion" : ""));

		    p = Next(*p);
		    }
		break;

	    case XIT:
		brkvios();	/* Break to monitor debugger */
		break;		/* continue VIOS processing */

	    case EMPTY:
	    default:
		break;
		} /*switch*/

	} /*if*/
    } /*do*/	while (TRUE);

}

    PDD_PTR
acc_pdd (pname, class, access)
    char *pname;
    unsigned class;
    unsigned access;

/* acc_pdd (pname, class, access) -- Locate a Physical Device
 *
 * Perform a CREATE / LOCATE on the Physical Device whose name is 'pname',
 * class is 'class', and attempt to access it using the bits specified
 * in 'access'.
 */
{
    SIM_IOSTATUS stat;

    vios_cwait(FCODE(PCTRL_FUNCTION, CREATEPD, access|LOOKUP_PD), NULL,
			    NULL, 0,0, pname, class, 0,0,0, &stat, NULL, 31, 0);
    
    if (Scode(&stat) NE V_SUCCESS)
	{
	if (Scode(&stat) EQ V_CLASS_MISMATCH)
	    wrconsole("Wrong Class for: %s\n", pname);
	else
	    {
	    wrconsole("Cannot open Physical Device: %s\n", pname);
	    pkstatp(&stat);
	    }
	return(NULL);
	}
    else
	return((PDD_PTR)Saux2(&stat));
}

    PDD_PTR
mk_pdd (pname, class, access, p2, p3)
    char *pname;
    unsigned class;
    unsigned access;
    unsigned p2;
    unsigned p3;

/* mk_pdd (pname, class, access, p2, p3) -- Create a Physical Device
 *
 * Perform a NEW_PD on the Physical Device whose name is 'pname',
 * class is 'class', and attempt to access it using the bits specified
 * in 'access'.  Create using 'p2' and 'p3' parameters.
 */
{
    SIM_IOSTATUS stat;

    vios_cwait(FCODE(PCTRL_FUNCTION, CREATEPD, access|NEW_PD), NULL, NULL, 0,0,
		pname, class, p2, p3,0, &stat, NULL, 31, 0);
    
    if (Scode(&stat) NE V_SUCCESS)
	{
	wrconsole("Cannot open Physical Device: %s\n", pname);
	pkstatp(&stat);
	return(NULL);
	}
    else
	return((PDD_PTR)Saux2(&stat));
}


rm_pdd (pdd)
    PDD_PTR pdd;

/* rm_pdd (pdd) -- Remove a Physical Device
 *
 * Perform a Remove Physical Device on 'pdd'.
 */
{
    vios_cwait(FCODE(PCTRL_FUNCTION, REMOVEPD, 0), pdd, NULL, 0,0,
		NULL, 0,0,0,0, NULL, NULL, 31, 0);
}

    VDD_PTR
mk_vdd (vname, class, vsiz)
    char *vname;
    unsigned class;
    unsigned vsiz;

/* mk_vdd (vname, class, vsiz) -- Create a Virtual Device
 *
 * Perform a Create Virtual Device whose name is 'vname',
 * class is 'class', with parameter 2 == 'vsiz'.
 */
{
    SIM_IOSTATUS stat;

    vios_cwait(FCODE(VCTRL_FUNCTION, CREATEVD, 0), NULL, NULL, 0,0,
		vname, class, vsiz, 0,0, &stat, NULL, 31, 0);
    
    if (Scode(&stat) NE V_SUCCESS)
	{
	wrconsole("Cannot create Virtual Device: %s\n", vname);
	pkstatp(&stat);
	return(NULL);
	}
    else
	return((VDD_PTR)Saux2(&stat));
}

    VDD_PTR
fnd_vdd (vname, flg)
    char *vname;
    int flg;

/* fnd_vdd (vname, flg) -- Locate a Virtual Device
 *
 * Perform a Locate Virtual Device on 'vname'.  Print an error msg if
 * not found, if 'flg' is TRUE.
 */
{
    SIM_IOSTATUS stat;

    vios_cwait(FCODE(VINFO_FUNCTION, DEV_INFO, 0), NULL, NULL, 0,0,
		vname, NULL, 0, 0,0, &stat, NULL, 31, 0);
    
    if ( (Scode(&stat) NE V_SUCCESS) OR (Saux3(&stat) NE VDTYPE) )
	{
	if (flg)
	    {
	    wrconsole("Cannot locate Virtual Device: %s\n", vname);
	    }
	return(NULL);
	}
    else
	return((VDD_PTR)Saux2(&stat));
}

    PDD_PTR
loc_pdd (pname, flg)
    char *pname;
    int flg;

/* loc_pdd (pname, flg) -- Locate a Physical Device
 *
 * Perform a Locate Physical Device on 'pname'.  Print an error msg if
 * not found, if 'flg' is TRUE.
 */
{
    SIM_IOSTATUS stat;

    vios_cwait(FCODE(VINFO_FUNCTION, DEV_INFO, 0), NULL, NULL, 0,0,
		pname, NULL, 0, 0,0, &stat, NULL, 31, 0);
    
    if ( (Scode(&stat) NE V_SUCCESS) OR (Saux3(&stat) NE PDTYPE) )
	{
	if (flg)
	    {
	    wrconsole("Cannot locate Physical Device: %s\n", pname);
	    }
	return(NULL);
	}
    else
	return((PDD_PTR)Saux2(&stat));
}


rm_vdd (vdd)
    VDD_PTR vdd;

/* rm_vdd (vdd) -- Remove a Virtual Device
 *
 * Perform a Remove Virtual Device on 'vdd'.
 */
{
    vios_cwait(FCODE(VCTRL_FUNCTION, REMOVEVD, 0), vdd, NULL, 0,0,
		NULL, 0,0,0,0, NULL, NULL, 31, 0);
}


mk_ass (vdd, pname, aunits)
    VDD_PTR vdd;
    char *pname;
    unsigned aunits;

/* mk_ass (vdd, pname, aunits) -- Assign Virtual Device units
 *
 * Assign the units specified by 'aunits' of the Virtual Device at 'vdd'
 * to the physical device whose name is 'pname'.
 *
 * Return the status of the command.
 */
{
    if (*pname EQ '\0')		/* if the target device is NULL, NULL the ptr */
	pname = NULL;
    return(vios_cwait(FCODE(VCTRL_FUNCTION, ASSIGNVD, aunits), vdd, NULL, 0,0,
		pname, 0,0,0,0, NULL, NULL, 31, 0));
}


mk_dev (pname, class, access, vname, vsiz, aunits)
    char *pname;
    unsigned class;
    unsigned access;
    char *vname;
    unsigned vsiz;
    unsigned aunits;

/* mk_dev (pname, class, access, vname, vsiz, aunits) -- Make devices
 *
 * Locate a physical device with the name 'pname' and class of 'class'.
 * Use 'access' to determine read/write access, etc.
 * If found, locate a virtual device with the name 'vname'.
 * If successful, reassign the units specified by 'aunits' to 'pname'.
 * If unsuccessful, try to create a virtual device of the same name, class,
 * and using 'vsiz' for parameter 2.
 * If created, assign the units specified by 'aunits' to 'pname'.
 *
 * Return TRUE if connection made, FALSE, otherwise.
 */
{
    VDD_PTR vdd;

    if (acc_pdd(pname, class, access) NE NULL)
	{
	if ( ((vdd = fnd_vdd(vname, FALSE)) NE NULL) OR
		((vdd = mk_vdd(vname, class, vsiz)) NE NULL) )
	    {
	    mk_ass(vdd, pname, aunits);
	    return(TRUE);
	    }
	}
    return(FALSE);
}


cnct_hostconsole (name)
    char *name;

/* cnct_hostconsole (name) - Push down CONSOLE input
 *	in:	name		String ptr containing pd-name
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 * Push down CONSOLE primary-input with 'name', if present.
 */
{
    register int i;
    char str[MAXLINE+6];

    if (Host_connected)
	{
	str[0] = '\0';
	strcat(str, "HOST:");
	mk_dev(strcat(str,name), REC_SEQ, READ_ONLY|SHR_READ,
		"CONSOLE", 0, PUSH_PRI_INP);
	}
    else
	wrconsole("No host connection yet\n");
}

    static CMD_DEF *	/* returns pointer to valid command struct, or NULL */
parse_cmd(cmd)
    register char *cmd;

/* parse_cmd (cmd) -- Parse a command line and get required arguments
 *	in:	cmd		Ptr to command line
 *	return:	(CMD_DEF*)	Ptr to command structure entry, or NULL
 *	thrown:	(NONE)
 *
 *	Uses the parse table at 'commands' to parse 'cmd'.  Command sections
 *	are delimited by white space.  Unsupplied arguments are prompted.
 *	Null arguments may be specified by the string:  -
 *		e.g.:	ASSIGN CONSOLE 64 -
 *
 */
{
    int i, j, len;
    register CMD_DEF *cptr;
    register C_ARG *argl;
    char *c, *ar;
    char prompt[80];

    c = delim1(cmd);
    if ((len = strlen(cmd)) EQ 0) return(&empty);
    cptr = (CMD_DEF*) (argl = NULL);
    for (i=0; i<NCMDS; i++)
	{
	if (strncmp(cmd,commands[i].name,len) EQ 0)
	    {
	    if (cptr EQ NULL)
		cptr = &commands[i];
	    else
		{
		wrconsole("Ambiguous command: %s\n", cmd);
		return(&empty);
		}
	    }
	}
    if (cptr EQ NULL)
	{
	if (*cmd NE '?')
	    {
	    wrconsole("Unrecognized command: %s\n", cmd);
	    return(&empty);
	    }
	return(NULL);
	}

    /* cptr now points to the correct command struct...c pts to arg list */
    for (j=0; j < cptr->nargs; j++)
	{
	argl = &(cptr->arg[j]);
	while (*c EQ '\0')
	    {
	    strcpy (prompt, ".....");
	    strcpy ((prompt+strlen(prompt)), argl->pmt);
	    strcpy ((prompt+strlen(prompt)), ": ");
	    if ((c=vpmtread(cmd,MAXLINE,"CONSOLE",prompt)) EQ NULL) xit(1);
	    }
	ar = c;
	c = delim1(c);

	if ((ar[0] EQ '-') AND (ar[1] EQ '\0'))
	    ar[0] = '\0';

	/* simulate a simple sscanf */

	switch (*(argl->fmt + 1))		/* dispatch on format type */
	    {
	case 'c':
	    *((char*)(argl->vali)) = *((char*)ar);	/* character value */
	    break;

	case 's':
	    strcpy(argl->vali, ar);			/* copy string */
	    break;

	case 'd':
	    *(argl->vali) = (unsigned) atob(ar,10);	/* decimal int */
	    break;

	default:
	case 'x':
	    *(argl->vali) = (unsigned) atob(ar,16);	/* hex number */
	    break;
	    } /*switch*/
	}
    return(cptr);
}

    static char *	/* returns pointer to rest of string */
delim1(str)
    register char *str;

/* delim1 (str) -- Delimit one token in an input string */
{
    while (*str NE '\0')	/* first, delimit the string at 'str' */
	{
	if (!isprint(*str))
	    {
	    *str++ = '\0';
	    break;
	    }
	else
	    str++;
	}
    while (*str NE '\0')	/* then, find the start of the next string */
	{
	if (isprint(*str)) return(str);
	else str++;
	}
    return(str);
}

/* pkstatp -- Print I/O status on completion */
    static int
pkstatp (stat)
    register SIM_IOSTATUS *stat;	    /* ptr to status to display */
{
#ifdef DEBUG1   /*************************************************************/
    errpri("     Returned status: %d", Scode(stat));
    errpri("  /  %d", Sauxcode(stat));
    errpri("     auxstat1: %d. ", Saux1(stat));
    errpri("(%x)\n", Saux1(stat));
    if (Scode(stat) NE V_SUCCESS)  return;
    errpri("     auxstat2: %d. ", Saux2(stat));
    errpri("(%x)", Saux2(stat));
    errpri("     auxstat3: %d. ", Saux3(stat));
    errpri("(%x)", Saux3(stat));
    errpri("     auxstat4: %d. ", Saux4(stat));
    errpri("(%x)\n", Saux4(stat));
#endif /* DEBUG1 *************************************************************/
}

/* vios_cwait -- call the vios i/o request initiator and wait for completion */

vios_cwait (arglist)
    char *arglist;
{
    return(vios_await(&arglist));	/* call the routine below */
}

vios_await (argptr)
    VIO_RQ *argptr;
{
    SIM_IOSTATUS tmpstat;

    vios_disable_completion();		/* turn off completion interrupts */

    if (Vstat(argptr) EQ NULL)
	Vstat(argptr) = &tmpstat;	/* set temporary status block address */

    /* call the VIOS with supplied args */
    Spkt(Vstat(argptr)) = (PKT_PTR) vios_acall(argptr);

    vios_enable_completion();
    while (Scode(Vstat(argptr)) EQ 0)
	{
	nullrout();
	}
    return(Scode(Vstat(argptr)));
}

#ifdef DEBUG0   /*************************************************************/
    static int
findfree (flag, size)
    register char flag[];
    int size;
{
    register i;

    for (i=0; i<size; i++)
	{
	if (flag[i] EQ 0) return(i+1);
	}
    return(0);
}
#endif /* DEBUG0 *************************************************************/

/* wrconsole -- Write a formatted string on the console output device */
			/* no return value ... output error will be thrown */
wrconsole (string, p1, p2, p3, p4, p5, p6, p7, p8, p9)
    register char *string;
    char *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9;
{
    char outline[2*MAXLINE];

    sprintf(outline, string, p1, p2, p3, p4, p5, p6, p7, p8, p9);
    lf2crlf(outline);		/* expand \n to \r\n */
    vwrite("CONSOLE", outline, strlen(outline), -1);
}

/* lf2crlf -- Expand \n to \r\n in strings */
    char *
lf2crlf (string)
    char *string;
{
    int lfcnt;
    char *to, *from;

    lfcnt = 0;
    for (from=string; *from NE '\0'; )
	if (*from++ EQ '\n') lfcnt++;		/* count the \n's */

    to = from + lfcnt;		/* set address of output string */
    for ( ; from GE string; )	/* loop until start of string */
	{
	*to-- = *from;				/* copy string */
	if (*from-- EQ '\n') *to-- = '\r';	/* if \n, insert \r */
	}
    return(string);		/* return address of new string */
}

/* vpmtread -- Issue prompt and read an input line with echo */
    char *		/* returns address of string or NULL */
vpmtread (addr, siz, devstr, pmtstr)
    register char *addr;	/* destination address */
    int siz;			/* size of input buffer */
    register char *devstr;	/* i/o device name */
    char *pmtstr;		/* ASCIZ prompt string */
{
    register int num;		/* number of characters read */

    if (catch() NE 0)  return(NULL);	/* catch all errors below */

    num = 0;
    vwrite(devstr, pmtstr, strlen(pmtstr), -1);	/* write prompt */
    do  {
	num += vread(devstr, (addr+num), (siz-num-1), ECHO_RECORDMODE, -1);
	*(addr+num) = '\0';			/* set string terminator */

	if (num GT 0)
	    {
	    switch (*(addr+num-1))
		{
	    case RETURN:
		vwrite(devstr, "\r\n", 2, -1);		/* write a cr/lf */
		uncatch();		/* remove error trail */
		return(addr);	/* successful read */

	    case RUBOUT:
	    case BACKSPACE:
		if ((--num) GT 0)		/* remove the /b or rubout */
		    {
		    num--;			/* backup over the char */
		    vwrite(devstr, "\b \b", 3, -1);	/* crt echo */
		    }
		break;

	    case ESCAPE:
	    case CTRL_U:
		num = 0;		/* erase current string */
		vwrite(devstr, "^U\r\n", 4, -1);	/* write ^U */
		vwrite(devstr, pmtstr, strlen(pmtstr), -1);	/* write pmt */
		break;

	    case CTRL_R:
		vwrite(devstr, "^R\r\n", 4, -1);	/* write ^R */
		vwrite(devstr, pmtstr, strlen(pmtstr), -1);	/* rewrt pmt */
		if ((--num) GT 0)		/* remove ^R from string */
		    vwrite(devstr, addr, num, -1);	/* write input */
		break;

	    case TAB:
		*(addr+num-1) = SPACE;		/* convert tab to space */
		vwrite(devstr, " ", 1, -1);		/* and echo it */
		break;

	    default:
		if (iscntrl(*(addr+num-1)))
		    num--;		/* remove other control characters */
		break;
		} /*switch*/
	    } /*if*/

	*(addr+num) = '\0';		/* refresh the terminator */

	}  while(num LT (siz-1));	/* loop above until full */

    uncatch();
    return(addr);
}

/* vread -- Issue a vios read and wait for it */
    int		/* return number of characters read, or throw error code */
vread (devstr, addr, siz, mod, blk)
    char *devstr;		/* Virtual device name */
    char *addr;			/* input buffer address */
    int siz;			/* number of characters to read */
    int mod;			/* read request modifier */
    int blk;			/* block number, or -1, if REC_SEQ */

/* vread (devstr, addr, siz, mod, blk) -- Read a Virtual Device
 *
 * NOTE that this routine directly accesses the Virtual Device structure.
 */
{
    SIM_IOSTATUS stat;
    VDD_PTR vdd;

    if ((vdd = fnd_vdd(devstr, TRUE)) EQ NULL)
	{
	throw(V_NO_DEVICE);
	}

    if (blk EQ -1)			/* REC_SEQ */
	{
	blk = 0;				/* clear for parameter below */
	if (Devclass(*vdd) NE REC_SEQ)
	    {
	    errpri("** Device not Record-Sequential Class: %s **\n", devstr);
	    throw(V_CLASS_MISMATCH);
	    }
	}
    else				/* BLK_ADDR */
	{
	if (Devclass(*vdd) NE BLK_ADDR)
	    {
	    errpri("** Device not Block-Addressible Class: %s **\n", devstr);
	    throw(V_CLASS_MISMATCH);
	    }
	}

    vios_cwait(FCODE(READ_FUNCTION, READ_DATA, mod), vdd,
	    addr, siz, PHYS_BUFFER, NULL, blk,0,0,0, &stat, NULL, 31, 0);

    return(Sbcount(&stat));
}

/* vwrite -- Issue a vios write and wait for it */
    int		/* return number of characters written, or throw error code */
vwrite (devstr, addr, siz, blk)
    char *devstr;		/* Virtual device name */
    char *addr;			/* output buffer address */
    int siz;			/* number of characters to write */
    int blk;			/* block number, or -1, if REC_SEQ */

/* vwrite (devstr, addr, siz, blk) -- Write to a Virtual Device
 *
 * NOTE that this routine directly accesses the Virtual Device structure.
 */
{
    SIM_IOSTATUS stat;
    VDD_PTR vdd;

    if ((vdd = fnd_vdd(devstr, TRUE)) EQ NULL)
	{
	throw(V_NO_DEVICE);
	}

    if (blk EQ -1)		/* REC_SEQ request */
	{
	blk = 0;			/* clear for parameter below */
	if (Devclass(*vdd) NE REC_SEQ)
	    {
	    errpri("** Device not Record-Sequential Class: %s **\n", devstr);
	    throw(V_CLASS_MISMATCH);
	    }
	}
    else			/* BLK_ADDR request */
	{
	if (Devclass(*vdd) NE BLK_ADDR)
	    {
	    errpri("** Device not Block-Addressible Class: %s **\n", devstr);
	    throw(V_CLASS_MISMATCH);
	    }
	}

    if (vios_cwait(FCODE(WRITE_FUNCTION, WRITE_DATA, 0), vdd, addr, siz,
		PHYS_BUFFER, NULL, blk,0,0,0, &stat, NULL, 31, 0) NE V_SUCCESS)
	{
	errpri("** Output Error Code: %d. **\n", Scode(&stat));
	throw(V_ABORTED);
	}

    return(Sbcount(&stat));
}
