/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:opset.c 12.0$ */
/* $ACIS:opset.c 12.0$ */
/* $Source: /ibm/acis/usr/src/old/sdb/RCS/opset.c,v $ */

#ifndef lint
static char *rcsid = "$Header:opset.c 12.0$";
#endif

#ifndef lint
static	char sccsid[] = "@(#)opset.c 4.2 10/27/82";
#endif lint
/*
 *	UNIX debugger
 *	Instruction printing routines.
 *	MACHINE DEPENDENT
 */

#ifdef ADB
#include "defs.h"
#endif ADB
#ifdef SDB
#include "head.h"
#endif SDB

L_INT		dot;
INT		dotinc;
L_INT		insoutvar[36];
#ifdef ADB
L_INT		var[36];
#endif ADB

#undef	INSTTAB
#include "instrs.h"

STRING	regname[];
STRING	fltimm[];
POS	type, space, incp;
/*
 *	Definitions for registers and for operand classes
 */
char	*insregname();	/* how to print a register */

#define	R_PC		0xF

#define	OC_IMM0		0x0
#define	OC_IMM1		0x1
#define	OC_IMM2		0x2
#define	OC_IMM3		0x3
#define	OC_INDEX	0x4
#define	OC_REG		0x5
#define	OC_DREG		0x6
#define	OC_ADREG	0x7
#define	OC_AIREG	0x8
#define	OC_DAIREG	0x9

#define	OC_BDISP	0xA
#define	OC_DBDISP	0xB
#define	OC_WDISP	0xC
#define	OC_DWDISP	0xD
#define	OC_LDISP	0xE
#define	OC_DLDISP	0xF

#define	OC_SHIFT	4
#define	OC_CONS(oc,reg)	(((oc & 0xF) << OC_SHIFT) | (reg & 0xF))
#define	OC_AMEXT(x)	(((x) >> OC_SHIFT) & 0xF)
#define	OC_REGEXT(x)	((x) & 0xF)

/*
 *	Definitions for large numbers
 */
#include "asnumber.h"
typedef	struct	as_number	*numberp;
numberp snarf();
numberp snarfreloc();
/*
 *	Definitions for special instructions
 */
#define	CASEB	0x8F
#define	CASEW	0xAF
#define	CASEL	0xCF
/*
 *	Definitions for converting TYP's into numbers, booleans, etc.
 *	These are shared with the assembler.
 */
extern	int	ty_NORELOC[];
extern	int	ty_float[];
extern	int	ty_nbyte[];
extern	int	ty_nlg[];
extern	char	*ty_string[];

short ioptab[3][256];	/* two level index by opcode into insttab */

int mapescbyte(byte)
	u_char	byte;
{
	switch(byte){
	default:	return(0);
	case ESCD:	return(1);
	case ESCF:	return(2);
	}
}

mkioptab()
{
	REG	struct insttab *p;
		int	mapchar;

	for(p = insttab; p->iname; p++){
		mapchar = mapescbyte(p->eopcode);
		if (ioptab[mapchar][p->popcode])
			continue;
		ioptab[mapchar][p->popcode] = p - insttab;
	}
}

u_char snarfuchar();
/*
 *	Global variables for communicating with the minions and printins
 */
static	int	idsp;
static	short	argno;		/* which argument one is working on */
static	char	insoutfmt[2];	/* how to format the relocated symbols */
#ifdef SDB
static	struct	proct	*procp;
#endif SDB

static savevar(val)
	long	val;
{
	var[argno] = val;
	insoutvar[argno] = val;
}

printins(fmt, Idsp, ins)
	char	fmt;
#ifndef vax
	u_char	ins;
#else
	u_char	ins;
#endif
	int	Idsp;
{
		u_char	mode;		/* mode */
		u_char	ins2;
		char	*indexreg;	/* print of which register indexes */
		char	*indexed;	/* we indexed */
		char	*operandout();
	REG	u_char 	*ap;
	REG	struct insttab *ip;
		u_char	optype;
		int	mapchar;

	idsp = Idsp;
	type = DSYM;
	space = idsp;
#ifdef SDB
	procp = adrtoprocp(dot);
	if (procp->paddr == dot){
		printf("0x%04.4x", ins);
		incp = 2;
		goto ret;
	}
#endif SDB

#ifdef ADB
	insoutfmt[0] = 0;
#endif ADB
#ifdef SDB
	insoutfmt[0] = fmt;
#endif SDB

	incp = 1;
	if ((mapchar = mapescbyte(ins)) != 0){
		ins2 = snarfuchar();
		if (ioptab[mapchar][ins2] == 0){
			/*
			 *	Oops; not a defined instruction;
			 *	back over this escape byte.
			 */
			incp -= 1;
			mapchar = 0;
		} else {
			ins = ins2;
		}
	}
	if (ioptab[mapchar][ins] == 0){
		printf("<undefined operator byte>: %x", ins);
		goto ret;
	}
	ip = &insttab[ioptab[mapchar][ins]];
	printf("%s\t", ip->iname);

	for (ap = ip->argtype, argno = 0; argno < ip->nargs; argno++, ap++) {
		savevar(0x80000000);	/* an illegal symbol */
		optype = *ap;
		if (argno != 0)
			printc(',');
		indexreg = 0;
		indexed = 0;
		do{
			if (A_ACCEXT(optype) & ACCB){
				switch(A_TYPEXT(optype)){
				case TYPB:
					mode = OC_CONS(OC_BDISP, R_PC);
					break;
				case TYPW:
					mode = OC_CONS(OC_WDISP, R_PC);
					break;
				}
			} else {
				mode = snarfuchar();
			}
			indexreg = operandout(mode, optype);
			if (indexed)
				printf("[%s]", indexed);
			indexed = indexreg;
		} while(indexed);
	}
	if (mapchar == 0){
		switch(ins){
		case CASEB:
		case CASEW:
		case CASEL:
			casebody(insoutvar[1], insoutvar[2]);
			break;
		default:
			break;
		}
	}
   ret: ;

#ifdef SDB
	oincr = incp;
#endif SDB
#ifdef ADB
	dotinc = incp;
#endif ADB
}

casebody(base, limit)
	long	base;
	long	limit;
{
	int	i;
	POS	baseincp;
	POS	advincp;
	struct	as_number	*valuep;
#define	OSIZE (sizeof(short))
	argno = 0;
	baseincp = incp;
	for (i = 0; i <= limit; i++) {
		printc(EOR);
#ifdef SDB
		printf("    %d:  ", i + base);
#endif SDB
#ifdef ADB
		printf("    %R:  ", i + base);
#endif ADB
		valuep = snarfreloc(OSIZE, 0);
		advincp = incp;
		incp = baseincp;
		dispaddress(valuep, OC_CONS(OC_WDISP, R_PC));
		incp = advincp;
	}
}

/*
 *	magic values to mung an offset to a register into
 *	something that psymoff can understand.. all magic
 */
			      /* 0	1	2	3	4 */
static long magic_masks[5] =	{0,	0x80,	0x8000,	0,	0};	
static long magic_compl[5] =	{0,	0x100,	0x10000,0,	0};
/*
 *	Snarf up some bytes, and put in the magic relocation flags
 */
numberp snarfreloc(nbytes)
	int	nbytes;
{
	numberp	back;
	back = snarf(nbytes);
	if (back->num_ulong[0] & magic_masks[nbytes])
		back->num_ulong[0] -= magic_compl[nbytes];
	return(back);
}
/*
 *	The following code is NOT portable from the PDP 11 to the VAX
 *	because of the byte ordering problem.
 */
numberp snarf(nbytes)
	int	nbytes;
{
	REG	int	i;

	static	struct	as_number	backnumber;
	static	struct	as_number	znumber;	/* init'ed to 0 */

	backnumber = znumber;
	for (i = 0; i < nbytes; i++)
		backnumber.num_uchar[i] = snarfuchar();
	return(&backnumber);
}
/*
 *	Read one single character, and advance the dot
 */
u_char snarfuchar()
{
	u_char	back;
	/*
	 *	assert: bchkget and inkdot don't have side effects
	 */
	back = (u_char)bchkget(inkdot(incp), idsp);
	incp += 1;
	return(back);
}
/*
 *	normal operand; return non zero pointer to register
 *	name if this is an index instruction.
 */
char *operandout(mode, optype)
	u_char	mode;
	u_char	optype;
{
	char	*r;
	int	regnumber;
	int	nbytes;

	regnumber = OC_REGEXT(mode);
	r = insregname(regnumber);
	switch (OC_AMEXT(mode)){
	case OC_IMM0:
	case OC_IMM1:
	case OC_IMM2:
	case OC_IMM3:
		shortliteral(mode, optype);
		return(0);
	case OC_INDEX:
		return(r);		/* will be printed later */
	case OC_REG:
		printf("%s", r);
		return(0);
	case OC_DREG:
		printf("(%s)", r);
		return(0);
	case OC_ADREG:
		printf("-(%s)", r);
		return(0);
	case OC_DAIREG:
		printc('*');
	case OC_AIREG:
		if (regnumber == R_PC){
			pcimmediate(mode, optype);
		} else {
			printf("(%s)+", r);
		}
		return(0);
	case OC_DBDISP:
		printc('*');
	case OC_BDISP:
		nbytes = 1;
		break;
	case OC_DWDISP:
		printc('*');
	case OC_WDISP:
		nbytes = 2;
		break;
	case OC_DLDISP:
		printc('*');
	case OC_LDISP:
		nbytes = 4;
		break;
	}
	dispaddress(snarfreloc(nbytes), mode);
	return(0);
}

dispaddress(valuep, mode)
	numberp	valuep;
	u_char	mode;
{
	int	regnumber = OC_REGEXT(mode);

	switch(OC_AMEXT(mode)){
	case OC_BDISP:
	case OC_DBDISP:
	case OC_WDISP:
	case OC_DWDISP:
	case OC_LDISP:
	case OC_DLDISP:
		if (regnumber == R_PC){
			/* PC offset addressing */
			valuep->num_ulong[0] += inkdot(incp);
		}
	}
#ifdef ADB
	psymoff(valuep->num_ulong[0], type, &insoutfmt[0]);
	if (regnumber != R_PC){		/* } */
#endif ADB
#ifdef SDB
	if(psymoff(valuep->num_ulong[0], regnumber, &insoutfmt[0])
	   && (regnumber != R_PC)){
#endif SDB
		printf("(%s)", insregname(regnumber));
	}
	savevar((long)valuep->num_ulong[0]);
}
/*
 *	get a register name
 */
char *insregname(regnumber)
	int	regnumber;
{
	char	*r;
	r = regname[regnumber];
#ifdef SDB
	if (   (insoutfmt[0] == 'i')
	    && (regnumber >= 6)
	    && (regnumber <= 11)
	    && (adrtoregvar(regnumber, procp) != -1)) {
		r = sl_name;
	}
#endif SDB
	return(r);
}
/*
 *	print out a short literal
 */
shortliteral(mode, optype)
	u_char	mode;
	u_char	optype;
{
	savevar((long)mode);
	switch(A_TYPEXT(optype)){
	case TYPF:
	case TYPD:
	case TYPG:
	case TYPH:
		printf("$%s", fltimm[mode]);
		break;
	default:
#ifdef ADB
		printf("$%r", mode);
#endif ADB
#ifdef SDB
		printf("$%d", mode);
#endif SDB
		break;
	}
}

pcimmediate(mode, optype)
	u_char	mode;
	u_char	optype;
{
	int	nbytes;

	printc('$');
	if (mode == OC_DAIREG){	/* PC absolute, always 4 bytes*/
		dispaddress(snarfreloc(4), mode);
		return;
	}
	nbytes = ty_nbyte[A_TYPEXT(optype)];
	if (! ty_NORELOC[A_TYPEXT(optype)]){
		dispaddress(snarfreloc(nbytes), mode);
		return;
	}
	bignumprint(nbytes, optype);
}

bignumprint(nbytes, optype)
	int	nbytes;
	u_char	optype;
{
	numberp	valuep;
	int	leading_zero = 1;
	REG	int	bindex;
	REG	int	nindex;
	REG	int	ch;

	valuep = snarf(nbytes);
	switch(A_TYPEXT(optype)){
	case TYPF:	
		printf("0f%f", valuep->num_num.numFf_float.Ff_value);
		break;
	case TYPD:
		printf("0d%f", valuep->num_num.numFd_float.Fd_value);
		break;
	case TYPG:
		printf("0g::"); goto qprint;
	case TYPH:
		printf("0h::"); goto qprint;
	case TYPQ:
	case TYPO:
	qprint:
		for (bindex = nbytes - 1; bindex >= 0; --bindex){
			for (nindex = 4; nindex >= 0; nindex -= 4){
				ch = (valuep->num_uchar[bindex] >> nindex);
				ch &= 0x0F;
				if ( ! (leading_zero &= (ch == 0) ) ){
					if (ch <= 0x09)
						printc(ch + '0');
					else
						printc(ch - 0x0A + 'a');
				}
			}
		}
		break;
	}
}
#ifdef SDB

L_INT inkdot(incr)
	int	incr;
{
	L_INT		newdot;

	newdot = dot + incr;
	return(newdot);
}

printc(c)
	char c;
{
	printf("%c", c);
}

psymoff(v, regnumber, fmt)
	L_INT	v;
	char	*fmt;
{
	struct	proct	*procp;
	REG	int diff;
	if (fmt[0] == 'i') {
		switch(regnumber){
		case 12:	/* parameter */
			if ((diff = adrtoparam((ADDR) v, adrtoprocp(dot)))
					!= -1) {
				printf("%s", sl_name);
				prdiff(diff);
				return(0);
			}
			break;
		case 13:	/* local */
			if ((diff = adrtolocal((ADDR) -v, adrtoprocp(dot))
					) != -1) {
				printf("%s", sl_name);
				prdiff(diff);
				return(0);
			}
			break;
		default:
			break;
		}
		if (v < firstdata) {
			if ((procp = adrtoprocp((ADDR) v)) != badproc) {
				prlnoff(procp, v);
				return(0);
			}
		} else {
			if ((diff = adrtoext((ADDR) v)) != -1) {
				printf("%s", sl_name);
				prdiff(diff);
				return(0);
			}
		}
	}
	prhex(v);
	return(1);
}

prdiff(diff)
{
	if (diff) {
		printf("+");
		prhex(diff);
	}
}

#endif SDB
