#include "pm.h"

/*
 * pm - traceback routines
 */


struct nlist	symbol;
INT		lastframe;
INT		cntflg;
INT		cntval;

/* symbol management */
L_INT		localval;

struct reglist reglist [] = {
		"AX",	AX,
		"BX",   BX,
		"CX",	CX,
		"DX",	DX,
		"SP",	SP,
		"BP",	BP,
		"SI",	SI,
		"DI",	DI,
		"CS",	CS,
		"DS",	DS,
		"SS",	SS,
		"ES",	ES,
		"FL",	FL,
		"IP",   IP
};
struct stack_frame {
    short   old_di;
    short   old_si;
    short   old_bp;			/* BP points here */
    short   ret_addr;
    short   args[4];			/* arguments to proc call */
} ;
    
char		lastc;
extern	struct	pentry	*proctb;

/* general printing routines  */

printtrace(regs, base, pid)
unsigned INT	regs[NREGS];
unsigned INT	base;
unsigned INT	pid;
{
	unsigned INT *pshort;
	struct stack_frame *psf;
	int		narg, cnt, rcnt, sp, bp, i;
	unsigned INT	frame, argp;
	unsigned INT	ip;	/* IP of procedure currently checking*/
	printregs(regs);
	sp = regs[SP] & EVEN;
	pshort = &core[sp>>1];
	bp = regs[BP] & EVEN;
	for ( i=30; i; pshort++,i--)
	    printf("0x%04x: 0x%04x\n", (pshort-core) *2, *pshort & 0xffff);
	if (sp+2 != bp) {
	    printf("trace: linkage in progress, trace aborted.\n");
	    return;
	}
	frame = regs[BP] & EVEN;
		rcnt = 0;	/* recursion count limit */
	ip = regs[IP];
        while(1){
		narg = findroutine(frame, ip); /* fills in symbol */
		if(errflg){
			printf("non-C function call to: ");
			rcnt++;
		}
		printf("%.8s(", symbol.n_un.n_name);
/* heres an IWC if I ever saw one...		*/
		argp = frame + 10;
		if(--narg >= 0)
			printf("0x%x", core[argp>>1]&0xff);	/* core is shorts */
		/* due to optimizer, narg may be very wrong */
		cnt = 0;
		while(--narg >= 0){
			argp += 2;
			printf(", 0x%x", core[argp>>1]&0xff);
			if(++cnt > MAXARGS)
				break;
		}
		printf(")");
		if(narg > 0)
			printf(" plus %d possible argument%s",
					narg, narg==1?"":"s");
		
		/* print local symbols someday */
		printf("\n");

		if(frame >= base - 6)	/* frame pointer is second word in mark */
			break;

		ip = core[(frame+8)>>1];
		frame = core[frame>>1];

		if(core[frame>>1] == 0){ /* head of Xinu task */
			findsym(proctb[pid].paddr, N_TEXT);
			printf("%.8s(", symbol.n_un.n_name);
			narg = proctb[pid].pargs;
			argp = frame + 4;
			if(--narg >= 0)
				printf("0x%x", core[argp>>1]&0xff);
			/* due to optimizer, narg may be very wrong */
			cnt = 0;
			while(--narg >= 0){
				argp += 2;
				printf(", 0x%x", core[argp>>1]&0xff);
				if(++cnt > MAXARGS)
					break;
			}
			printf(")");
			if(narg > 0)
				printf(" plus %d possible argument%s",
						narg, narg==1?"":"s");
			
			return;
		}
		if(rcnt > RECLIMIT){
			printf("recursion limit of %d reached\n", RECLIMIT);
			return;
		}
	}
}

printregs(regs)
unsigned INT	regs[NREGS];
{
	register struct reglist *p;
	INT			v;

	for(p = reglist; p < reglist + (sizeof (reglist)) /
		(sizeof (struct	reglist)) ; p++){
		v = regs[p->roffs];
		printf("%.8s\t0x%4x\t", p->rname, v & 0xffff);
		if(p->roffs != FL)
			valpr((INT)v,(p->roffs == IP?N_TEXT:N_DATA));
		printf("\n");
	}
/*	printpc(regs);*/
}

/*
 * Print a value v symbolically, if it has a reasonable
 * interpretation as name+offset. If not, print nothing.
 */

valpr(v, idsp)
unsigned INT	v;
{
	long	findsym(), d;

	d = findsym(v, idsp);
	if(d >= MAXOFF)
		return;
	printf("%.8s", symbol.n_un.n_name);
	if(d)
		printf("+0x%x", d&0xffff);
}

/*
 * printpc - print the word pointed to by the pc both
 * symbolically and as an instruction
 */

printpc(regs)
INT	regs[NREGS];
{
	psymoff(regs[IP], ":\t", N_TEXT);
/*	printins(regs[R7], regs);*/
}

/*
 * findroutine - find the name of the routine pointed
 * to by the current frame. Leave its symbol table entry in
 * symbol as a side effect; returns the number of arguments 
 * used in the call, after horrible kludging around to find it.
 *
 * because the optimizer may change the mechanism used to pop
 * the arguments off the stack, the value returned may be
 * in error.
 */

findroutine(cframe, ip)
unsigned INT	cframe,
		ip;	/* instruction pointer belonging to current frame*/
{
	register INT	narg, inst;
	unsigned INT	callpc, back2;
	boolean		v;
	short *pshort;
	char *pch;

	if ( cframe == 0) {
	    errflg = TRUE;
	    printf("Can't do traceback with frame pointer = 0!\n");
	    return(-1);
	}
	v=FALSE; 
	errflg = FALSE;
	

	callpc = core[(cframe+8)>>1];

	printf("printing stack for frame = %4x\n", cframe);
	for (inst = 0; inst < 24; inst += 2) {
		printf("%4x: %x\n", cframe + inst,
			*( (short *)( (char *)core + cframe + inst))&0xffff);
	}
	fflush(stdout);


printf("PC to return to = %4x\n", callpc & 0xffff);
for ( pch=callpc-10+ (char *) core; pch < callpc+10+(char *)core; pch++){
    printf("location %4x = %2x\n",pch-(char *)core,*pch&0xff);
}

    /* note- only one type of call has been implemented,
       CALL DISP16. The number of arguments to the call is
       gotten by examining the number of words the stack
       is incremented by after return from the procedure
    */
	inst = *(((char *) core) + callpc -3) & 0xff;
	if(inst  != 0xe8){ /* was not a call */
	    errflg = NOCFN;
	}


	if(findsym(ip, N_TEXT) == -1 ){
		symbol.n_un.n_name[0] = '?';
		symbol.n_un.n_name[1] = 0;
		symbol.n_value = 0;
	}

	inst = ( *(short *) (((char *) core) + callpc)) & 0xffff;
	if (((unsigned short) inst) == ((unsigned short) 0xc483)) {
	    pch = (char *) core + callpc + 2;
	    narg= ( (*pch) >> 1) & 0xff;
/*==========
	    if (verbose)
	        printf("routine %s has %d args.\n", symbol.n_un.n_name, narg);
==========*/
	    return(narg);
	} else {
	    narg=0;
/*==========
	    if (verbose)
	        printf("can't figure out %s's arg count\n", symbol.n_un.n_name);
==========*/
	}
	return(narg);
}

/*
 * find the closest symbol to val, and return the 
 * difference between val and the symbol found.
 * leave the symbol table entry in 'symbol' as a side effect.
 */

long
findsym(val, type)
INT	val;
INT	type;
{
	long			diff;
	register struct nlist	*sp, *cursym;

/*printf("findsym: looking for value=%4x, type=%x\n", val, type);*/
	diff = 0377777L;
	cursym = &symbol;
	symbol.n_un.n_name = "??";
	symbol.n_un.n_name[0] = 0;
	symbol.n_un.n_name[1] = 0;
	symbol.n_value = 0;

	for(sp = ssymtab; sp < essymtab; sp++){
/*
printf("findsym: looking at %s, type=%x, value=%x\n",sp->n_un.n_name,
sp->n_type, sp->n_value);
fflush(stdout);
*/

		switch(type){
		case N_TEXT:

			if((sp->n_type & 07)!= type)
				continue;
			break;

		case N_DATA:
			if((sp->n_type & 07) != N_DATA 
			   && (sp->n_type & 07) != N_BSS)
				break;
			continue;

		default:
			fprintf(stderr, "bad type in findsym\n");
		}
		if(sp->n_value <= val)
			cursym = sp;
		else{
			if(cursym->n_un.n_name[0])
				diff = val - cursym->n_value;
			break;
		}
	}
	if(cursym->n_un.n_name[0])
		symbol = *cursym;
	else
		symbol.n_un.n_name[0] = '?';
	return(diff);
}

/*
 * psymoff - basically call findsym and print the result
 */

psymoff(val, str, space)
INT	val;
char	*str;
{
	long findsym(), d;

	d = findsym(val, space);
	if(d > MAXOFF)
		printf("0x%x", val&0xffff);
	else{
		printf("%.8s", symbol.n_un.n_name);
		if(d)
			printf("+0x%x", d&0xffff);
	}
	printf(str);
}
