/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1974
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:trace.c 12.0$ */
/* $ACIS:trace.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/standca/RCS/trace.c,v $ */

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

#include "debug.h"

#define AI_R1_R1	0xC111
#define CAL_R1_R1	0xc811
#define STM_X_R1	0xd901
#define STM_X_MASK	0xff0f
#define AIS_R1		0x9010
#define SIS_R1		0x9210
#define BALRX_R15	0x8df0
#define CAS		0x6000
#define ST		0xdd00
#define STS		0x3000
#define EXTS		0xb100
#define SHORT_MASK	0xfff0
#define MAX_LEVEL	25
#define SEG_MASK	0xf0000000
#define MAX_ARGS	3
#define BUFFER_SIZE     4	/* size of response buffer */
#define REG_FLAG	0x10	/* flag if parameter ended up in register */

#define PROLOG_SIZE	4	/* until saved R15 is available */
#define FP	13		/* frame pointer */

#define SP	1		/* stack pointer register */

int trflag = 0;

extern int screen_lines;
struct symtab *closest();
/*
 * trace back thru the stack frames and print out the names and
 * arguments to the various functions called.
 * the entry to a C procedure looks like:
 * entry: ai r1,r1,-framesize		| create stack frame
 *	stm	Rnnn,Vnnn(r1)		| save registers
 * on entry to a function: the stack looks like:
 * n*4	arg n
 * ...  ...
 * 8	arg 3
 * 4	arg 2
 * 0	arg 1	<== sp
 * after the stack push and stm the stack looks like:
 * framesize+8	arg 3
 * framesize+4	arg 2
 * framesize+0	arg 1
 * framesize-4	saved r15	(or Vnnn + (15 - Rnnn) * 4)
 * ...
 * Vnnn		saved Rnnn
 * n		local n
 * 0		local 0
 *
 */
traceback(iar,sp,tflag) register int sp, iar;
{
register struct symtab *sym;
int offset;
int prolog2;
register int level;
register int entry;
register int i;
int fp_offset;
int nargs;
int	Rnnn,			/* number of registers saved */
        Vnnn;			/* space for register variables */
int	regs[16];		/* current register values */
int	protect;
int	fp;			/* frame pointer register */
int	oldsp;			/* sp at start of loop */

trflag += tflag;
for (i=0; i<16; ++i)
	regs[i] = GETREG(i);	/* pick up current values */
for (level=0;err_flag == 0 && level < MAX_LEVEL;++level)
        {
	oldsp = sp;
        printf("level %d: sp=%x iar=%x ",level,sp,iar);
        if ((iar & iar_mask) == 0)
                break;
        sym = closest(iar & iar_mask,0x10000);	/* get closest entry point */
        if (sym == 0)
                {
                printf("no symbol found near %x\n",iar);
                break;
                }
        entry = sym->value;
        offset = (iar & iar_mask) - entry;
        printf(" routine %s + %d (0x%x)\n", sym->symbol,offset,offset);
        prolog2 = h_fetch(entry);
        if ((prolog2 & STM_X_MASK) != STM_X_R1) {
                printf("*** Invalid Prolog\n");
                break;
        }
        Rnnn = (prolog2 >> 4) & 0xf;
        Vnnn = (h_fetch(entry+2) << 16) >> 16;
        fp_offset=0;
/*
 * interrupt service routines (by convention) have an AI just before the
 * entry point that has the amount of extra stack protection required
 */
	if (h_fetch(entry-4) == AI_R1_R1)
		protect = (h_fetch(entry-2) << 16) >> 16;
	else
		protect = 0;		/* normally no extra protect amount */

        {

        register int iar_temp,instr;

	/* scan thru code between entry point and iar and look for stack
	 * pushes and pops.
	 * IS THIS NECESSARY FOR NCS?? 
	 */
        for (iar_temp=entry; iar_temp < (iar & iar_mask) ; iar_temp+=2) {
		register short size;
                instr=h_fetch(iar_temp);
                if ((instr <= 0x8fff ) && (instr >= 0x8800)) iar_temp+=2;
                if ((instr <= 0xdfff ) && (instr >= 0xc000)) iar_temp+=2;
                if (instr == AI_R1_R1 || instr == CAL_R1_R1) {
                        fp_offset += size = h_fetch(iar_temp);
                        if (trflag>1) printf ("%x: AI R1,R1,%d\n",iar_temp-2,size);
                  }
                else if ((instr & SHORT_MASK) == SIS_R1) {
                        fp_offset-= size = instr & 0x000f;
                        if (trflag>1) printf ("%x: SIS R1,%d\n",iar_temp,size);
                  }

                else if ((instr & SHORT_MASK) == AIS_R1)  {
                        fp_offset+= size = instr & 0x000f;
                        if (trflag>1) printf( "%x: AIS R1,%d\n",iar_temp,size);
                  }
        }
        }
	if (fp_offset == 0 || (debug_option&OPTION_TRACE_TABLE) == 0)
		fp_offset = -gettrace(entry,sym,&nargs,&fp);
	else
		{
		fp = SP;
		nargs=4*4;		/* assume 4 arguments for now */
		}
        if (trflag>1) printf ("fp offset=%d (0x%x), fp=R%d, Vnnn=%d (0x%x), Rnnn = %d (0x%x)",fp_offset,fp_offset,fp,Vnnn,Vnnn,Rnnn,Rnnn);

	regs[SP] = sp;			/* put sp into register vector */
        sp = regs[fp] - fp_offset;	/* original sp value */

        if (offset < PROLOG_SIZE)
                iar = GETREG(15);	/* use current value of R15 */
        else
                iar = w_fetch(sp + ((15 - Rnnn) << 2) + Vnnn);
	{
	int temp, length;
	/* look for a parameter storing or moving instruction (up to 8 past entry) */
	entry += 8;		/* at least 8 bytes of prolog */
	for (i=0; i<8; ++i)
		if (move_arg(entry,&temp,&length) || length == 0)
			break;
		else
			entry += length&0xf;
	}
        printf("	%s(",sym->symbol);
        for (i=0; i < nargs; i += 4)
                {
		int temp, length;
		if (move_arg(entry, &temp, &length) != (i>>2)+2)
			break;
		entry += length & 0xf;
		if (length & REG_FLAG)
			temp = regs[temp];	/* get the register value */
		else
			temp = w_fetch(regs[FP] + temp);
                printf("%s%x",i ? "," : "" ,temp);
                if (err_flag)
                        break;
                }
	printf(")");
	if (protect)
		printf(" *** INTERRUPT SERVICE ROUTINE ***");
        printf("\n");
        if ((level >= ((screen_lines - 3) /2)) && (level % ((screen_lines - 3)/2) == 0 )) pause();
	for (i=Rnnn ; i<16 ; ++i, Vnnn += 4)
		{
		regs[i] = w_fetch(sp + Vnnn);	/* pick up register values */
		if (trflag)
			printf("r%d=0x%x ",i,regs[i]);
		}
	if (trflag)
		printf("\n");
	sp -= protect;		/* adjust for protected area */
	if (oldsp == sp)
		break;		/* better than looping */
        }
printf("\n");
trflag -= tflag;
}

/*
 * test the instruction at "addr" and return the register number that
 * is either being stored or moved.
 * offset is the location (on stack) being stored into or the register
 * variable number.
 * return into *length the length of the current instruction.
 */
move_arg(addr,offset,length)
register int addr;
register int *offset;
register int *length;
{
	register int instn = h_fetch(addr);
	register int arg;
	register int len = 0;

	if ((instn>>8) == (EXTS>>8))
		{	/* ignore exts - used to sign extend shorts */
		len += 2;
		addr += 2;
		instn = h_fetch(addr);
		}
	if ((instn>>12) == (STS>>12))
		{
		len += 2;
		arg = (instn >> 4) & 0xf;
		if (arg < 2 || arg > 5)
			arg = 0;
		else
			*offset = (instn>>8) * 4;
		}
	else if ((instn >> 8) == (ST >> 8))
		{
		arg = (instn >> 4) & 0xf;
		if (arg < 2 || arg > 5)
			arg = 0;
		else
			*offset = (short) h_fetch(addr+2);
		len += 4;
		}
	else if ((instn>>12) == (CAS>>12))
		{
		len += 2 + REG_FLAG;	/* length=2; register variable */
		arg =  (instn >> 4) & 0xf;
		if (arg < 2 || arg > 5)
			arg = 0;
		else
			{
			*offset = ((instn >> 8) & 0xf);
			}
		}
	else if ((instn>>4) == (BALRX_R15>>4))
		{
		arg = 0;
		}
	else
		arg = 0, len += 2;
	if (trflag>1 && arg)
		if (len&REG_FLAG)
			printf("in r%d ",*offset);
		else
			printf("at %d(fp) ",*offset);
	*length = len;
	return(arg);
}

h_fetch(addr) register int addr;
{
register int n = 0;

if (trflag>2)
	printf(" [%x] ",addr);
if (addr & SEG_MASK)
        {
        addr = vtop(addr);		/* assume it is virtual address */
        if (trflag>2) printf(" (real = %x)",addr);
        }
if (err_flag == 0 && (n = hfetch(addr)) < 0)
        ++err_flag;
if (trflag>2) printf("=%x ",n);
return(n);
}



w_fetch(addr)
{
	register int n = ((h_fetch(addr) << 16) + h_fetch(addr+2));
	if (trflag>2)
		printf(" ==> %x ",n);
	return(n);
}

/*
 * scan thru memory looking for the entry sequence before "addr"
 * and find the C prolog. In particular we look for the AI ... STM
 * and then look backwards for the <name> in memory.
 */
struct symtab *scansym(addr,offset)
register unsigned addr;
register int offset;
{
register unsigned start = addr - (offset >> 4);
static struct symtab sym;		/* the symbol to return */
register int p, c;
register int i, len;
register int endptr;

if ((addr&0xf0000000) == 0)
	{		/* only do this for real memory addresses */
	for (; addr > start && !err_flag; addr -= 2)
		{
		if ((h_fetch(addr-4) & STM_X_MASK) == STM_X_R1 && h_fetch(addr) == AI_R1_R1)
			{
			addr -= 4;		/* point to "entry point" */
			if (trflag>1)
				printf("found candidate at %x\n",addr);
			p = addr - 1;
			if (h_fetch(p-4+1) == AI_R1_R1)
				p -= 4;		/* special case for interrupt frame */
			while (( c = b_fetch(p)) == 0 && err_flag == 0)
				--p;
			if (c != '>')
				continue;	/* not us */
			endptr = p;
			while ((c = b_fetch(p)) && c != '<')
				--p;
			++p;			/* point to first character */
			len = endptr - p;
			if (len > MAXSYMLEN)
				len = MAXSYMLEN;
			for  (i=0; i<len;++i , ++p)
				sym.symbol[i] = b_fetch(p);
			sym.symbol[i] = 0;	/* may clobber 'value' */
			sym.value = addr;	/* the address */
			return(&sym);
			}
		}
	}
return(0);
}


/*
 * fetch bytes by fetching halfwords and getting proper byte (yetch!)
 */
b_fetch(addr) register int addr;
{
union {
short x;
char cx[2];
} un;
register int n;

un.x = h_fetch(addr & ~1);	/* get from even address */

n = un.cx[addr&1];
if (trflag>2)
        {
        printf("byte = %x ",n);
        if (n >= ' ' && n < 0x7f)
                printf("'%c'",n);
        printf("\n");
        }
return(n);
}

/* 
 * scan backwards from the symbol table entry following this one
 * until we hit the trace table flag.
 * we assume that the symbol table is sorted numericly enough for this
 * to work.
 */
#define TRACE_MAGIC	0xdf00		/* magic marker for trace table */
gettrace(entry,sym,nargp,fp)
struct symtab *sym;
int entry;
int *nargp;
int *fp;
{
	register int fp_offset = 0;
	register int next = (sym+1)->value;
	register int x, y;

	*nargp = 0;
	*fp = SP;			/* default is stack pointer */
	if ((unsigned) (next-entry) < 0x10000)
		{
		for (; (next -= 2) > entry; )
			if (((x = h_fetch(next)) & 0xff00 ) == TRACE_MAGIC &&
				((y = h_fetch(next-2)) & 0xff00) == TRACE_MAGIC)
				{
				if (x&0x08)
					{
					*nargp = ((x = h_fetch(next+2)) >> 12) << 2;
					*fp = (x >> 8) & 0xf;	/* frame pointer */
					fp_offset = x & 0x3f;
					x = (x >> 6) & 03;
					y = next + 4;
					while (--x >= 0)
						fp_offset = (fp_offset << 8) + b_fetch(y++);
					fp_offset <<= 2;
					}
				if (trflag)
					printf("trace table %x: %x %x %x nargs=%d, fp_offset=%d\n",next-2,h_fetch(next-2),h_fetch(next),h_fetch(next+2),*nargp,fp_offset);
				break;
				}
			else if (x < 0)
				break;
		}
	return(fp_offset);
}
