/* roff.c, system register handling */

#include "nroff.h"

struct	sreg	{
	char	S_name[2];
	int	*S_val;
	int	(*S_getval)();
	int	(*S_rsetval)();
};

/* default reset value routine */

defrsval(sp, np)
struct	sreg	*sp;
struct	nregs	*np;
{
	if(sp->S_val)
		*sp->S_val = np->n_value;
}

/* default get value routine */

defgval(sp, np)
struct	sreg	*sp;
struct	nregs	*np;
{
	if(sp->S_val)
		np->n_value = *sp->S_val;
	else
		np->n_value = 0;
}

static
Dgval(sp, np)		/* number of arguments in current macro */
struct	sreg	*sp;
struct	nregs	*np;
{
	struct	source	*mp;

	for(mp = curmacro ; mp ; mp = mp->m_last)
		if(mp->m_name[0] != '\0'){	/* it really is a macro */
			if(mp->m_type != AMACRO)
				continue;
			np->n_value = mp->m_nargs;
			return;
		}
	np->n_value = 0;
}

static	int	Arval = 1;	 /* always 1 in nroff */

static
igval(sp, np)
struct	sreg	*sp;
struct	nregs	*np;
{
	np->n_value = tival + inval;
}

static
tgval(sp, np)
struct	sreg	*sp;
struct	nregs	*np;
{
	np->n_value = towhen(lineno);
}

static
zgval(sp, np)
struct	sreg	*sp;
struct	nregs	*np;
{
	struct	source	*mp;
	int	i;

	for(i = 0, mp = curdiv ; mp->m_last ; mp = mp->m_last, i++);
	np->n_value = i;
}

/* names of all system registers */

struct	sreg	sysrs[] = {
	'%', '\0',	&curpage,defgval, defrsval,
	'c', 't',	0,	defgval, defrsval,
	'd', 'l',	&dlval,	defgval, defrsval,
	'd', 'n',	0,	defgval, defrsval,
	'd', 'w',	0,	defgval, defrsval,
	'd', 'y',	0,	defgval, defrsval,
	'h', 'p',	0,	defgval, defrsval,
	'l', 'n',	0,	defgval, defrsval,
	'm', 'o',	0,	defgval, defrsval,
	'n', 'l',	0,	defgval, defrsval,
	's', 'b',	0,	defgval, defrsval,
	's', 't',	0,	defgval, defrsval,
	'y', 'r',	0,	defgval, defrsval,
	/* read only regs, if second char is '0', zap and set ro bit */
	'$', '0',	0,	Dgval,   0,
	'A', '0',	&Arval,	defgval, 0,
	'H', '0',	0,	defgval, 0,
	'T', '0',	0,	defgval, 0,/* always zero */
	'V', '0',	0,	defgval, 0,
	'a', '0',	0,	defgval, 0,
	'c', '0',	0,	defgval, 0,
	'd', '0',	0,	defgval, 0,
	'f', '0',	0,	defgval, 0,
	'h', '0',	0,	defgval, 0,
	'i', '0',	0,	igval,   0,
	'l', '0',	&rmval,	defgval, 0,
	'n', '0',	0,	defgval, 0,
	'o', '0',	0,	defgval, 0,
	'p', '0',	&plval,	defgval, 0,
	's', '0',	0,	defgval, 0,
	't', '0',	0,	tgval,   0,
	'u', '0',	&fill,	defgval, 0,
	'v', '0',	&lsval,	defgval, 0,
	'w', '0',	0,	defgval, 0,
	'z', '0',	0,	zgval, 0,
};

#define	SYSSIZ	(sizeof(sysrs)/sizeof(sysrs[0]))

static	struct	nregs	stype[SYSSIZ];

/* define a new system register, with the normal values */

static
newreg(np, name, ro)
char	*name;
struct	nregs	*np;
{
	np->n_name[0] = name[0];
	np->n_name[1] = name[1];
	np->n_status = NSYS|ro;
	np->n_digits = 1;
	np->n_form = '1';
	np->n_next = numregs;
	numregs = np;
}

initsvals()	/* define and set default values for all registers */
{
	struct	sreg	*sp;
	struct	nregs	*np;

	np = stype;
	for(sp = sysrs ; sp < sysrs + SYSSIZ ; sp++, np++){
		if(sp->S_name[1] == '0'){
			sp->S_name[1] = '\0';
			newreg(np, sp->S_name, NRO);
		}
		else
			newreg(np, sp->S_name, 0);
	}
}

getsvals(np)	/* set a system wide numeric register */
struct	nregs	*np;
{
	struct	sreg	*sp;

	sp = &sysrs[np - stype];
	if(sp->S_getval)
		(*sp->S_getval)(sp, np);
}

/* reset a changed system wide numeric register */

rsetsvals(np)
struct	nregs	*np;
{
	struct	sreg	*sp;

	sp = &sysrs[np - stype];
	if(sp->S_rsetval)
		(*sp->S_rsetval)(sp, np);
}
