#include "pm.h"

/*
 *	pm - a post mortem analyzer of core86 files
 *	that come from the Xinu operating system.
 *
 *	Chris Kent - October 1981
 *
 *      Modified for the 8086 (April 1984) by:
 *          F. J. Newbery
 *          K. L. Rives
 */

/*
 *	Arguments/options:
 *
 *		pm [-v] [-t] [-p] [-s] [ objfile [ corefile ] ]
 *
 *	where objfile is the text that was downloaded, and corefile
 *	is the result of upload. Defaults are a86.out and core86.
 *      -v verbose option gives you list of externals sorted by address
 *         and a word by word comparison of text sections.
 *      -t option means do a ttydump only
 *      -p option means do a process table dump only
 *      -s option means do a semaphore table dump only
 *      
 *
 */

#define		START	0x1000	/* # bytes of interrupt vectors	*/

char		*corefile;
FILE		*corefd;	/* the core image			 */
char		*txtfile;
FILE		*txtfd;		/* the system				 */
short		*text;		/* pointer to system image		 */
short		*core;		/* pointer to core image		 */
struct exec	a_out;		/* a.out header				 */
struct core86	c_header;
int		errflg;		/* global error indicator		 */
int		allopts, popt, sopt, topt;	/* option indicators	 */
int		verbose;	/* print verbose description or not	 */
struct nlist	*ssymtab;	/* sorted external symbol table space	 */
struct nlist	*symtab;	/* sorted external symbol table space	 */
struct nlist	*essymtab;	/* end of same				 */
struct nlist	*esymtab;	/* end of same				 */
struct pentry	*proctb;	/* pointer to image of proc table	 */
struct qent	*Q;		/*    "     "   "   "  q		 */
struct sentry	*semtab;	/*    "     "   "   "  semaphore table	 */
struct tty	*tptr;		/*    "     "   "   "  tty structures	 */
int  currpid;			/* current process id number		 */
main(argc, argv)
int	argc;
char	*argv[];
{

	int		FirstDiff;
	register INT	*TextPtr, *CorPtr;
	INT		base;
	char		*pidname;
	int		cDiffs;
	register	i, j;

	setup(argc, argv);	/* open files, etc. */

	/* 
	 * compare the core image text segment and the 
	 * original text segment from the system
	 */
			
	FirstDiff = 0;
	TextPtr = text;
	CorPtr = core + START/sizeof(short);
	if (verbose) printf("%x %x\n",CorPtr,core);
	printf("\nComparing text segments....\n");

	cDiffs = 0;
	while(((char *)CorPtr - (char *)core) < a_out.a_text){
/*
		if (verbose)
			printf("%x: comparing \t%04x(core) and \t%04x(object)\n",
			(char *)CorPtr-(char *)core,(*CorPtr)&0xffff,(*TextPtr)&0xffff);
*/
		if(*CorPtr != *TextPtr){
			register s, c;

			if(!FirstDiff){
				printf("Text has been changed:\n");
				printf("\t\tProgram\tCore\n");
				FirstDiff = 1;
			}
			s = (int) *TextPtr;
			c = (int) *CorPtr;
			printf("\t%04x:\t%04x\t%04x\n",
			((char *)CorPtr - (char *)core), s&0xffff, c&0xffff);
			if (cDiffs++ > 100 ) {
				printf("Text has been radically changed.\n");
				goto lab;
			}
			/* would be nice to do opcode lookup here */
		}
		TextPtr++;CorPtr++;
	}
lab:	if (!FirstDiff) printf("Text part of program has not been changed\n");

	if (!c_header.c_reg_valid)
	    printf("Registers were lost during resetting of machine.\n");
       	else
	    printf("Machine was not reset, no registers were lost.\n");


	/* breakdown of fl register ---- ODIT SZ-A -P-C */

	if (verbose)
	    printf("register fl has value %04x\n\n",c_header.c_fl&0xffff);

	/*
	 *	current process info
	 */
	
	if (currpid<0 || currpid>NPROC) {
 		printf("Invalid current process id (currpid==%d)\n",
			currpid);
	} else {
		pidname = proctb[currpid].pname;
		printf("\nprocess # %d ( %s ) was running\n", currpid, pidname);
		printf("\tat priority %d ", proctb[currpid].pprio);
		if (	((unsigned)c_header.c_regs[SP]&0xffff) <
			((unsigned short)proctb[currpid].plimit) )
			printf("Stack has OVERFLOWED actual=0x%x, limit=0x%x\n",
				((unsigned)c_header.c_regs[SP]&0xffff),
				((unsigned)proctb[currpid].plimit) );
		else
			printf("stack is currently %d words long\n", 
			(proctb[currpid].pbase-c_header.c_regs[SP]&0xffff)/2);
		printtrace(c_header.c_regs, proctb[currpid].pbase, currpid);
	}

	if(allopts || popt)
		procdump(currpid);

	if(allopts || sopt)
		semdump();

	if(allopts || topt)
		ttydump();
}

/*
 *	dump the proc table
 */

procdump(currpid)
{

	char		*pidname;
	register int	i;


	for(i = 0; i < NPROC; i++){
		if(i == currpid)
			continue;
		pidname = proctb[i].pname;
		printf("\nprocess # %d ( %s ):\tstate=", i, pidname);
		switch(proctb[i].pstate){
		case PRFREE:
			printf("free\n");
			continue;
		case PRREADY:
			printf("ready\n");
			break;
		case PRCURR:	/* shouldn't happen! */
			printf("CURRENTLY RUNNING???\n");
			break;
		case PRSUSP:
			printf("suspended\n");
			break;
		case PRWAIT:
			printf("waiting on semaphore %d\n", proctb[i].psem);
			break;
		case PRSLEEP:
			printf("sleeping\n");
			break;
		case PRRECV:
			printf("receiving\n");
		default:
			printf("garbage state 0%x(hex)\n", proctb[i].pstate&0xffff);
		}
		if (proctb[i].pstate == PRFREE)
			continue;
		printf("\tat priority %d ", proctb[i].pprio);
		printf("stack is %d words long\n", 
			((proctb[i].pbase - proctb[i].pregs[SP])&0xffff)/2);
		printf("traceback:\n\n");
		printtrace(proctb[i].pregs, proctb[i].pbase, i);
/*
printf("pbase = %x, SP = %x\n", proctb[i].pbase&0xffff,
			proctb[i].pregs[SP]&0xffff);
printregs(proctb[i].pregs);
*/			
		printf("\n");
	}
}

/*
 *	dump the semaphore table and queues
 */

semdump()
{
	short		qptr;
	register int	i, j;
	
	printf("\nSemaphores:\n\n");
	for(i = 0; i < NSEM; i++){
		printf("semaphore # %d : ", i);
		switch(semtab[i].sstate){
		case SFREE:
			printf("slot is free\n");
			break;

		case SUSED:
			printf("count is %d\n", semtab[i].semcnt);
			if(Q[semtab[i].sqhead].qnext < NPROC){
				printf("\tqueued proc(s) are: ");
				qptr = semtab[i].sqhead; 
				j = 0;
				do{
					printf("%d ", Q[qptr].qnext);
					qptr = Q[qptr].qnext;
					j++;
				}while(Q[qptr].qnext < NPROC && j<=NPROC );
				if (Q[qptr].qnext != semtab[i].sqtail)
					printf("...QUEUE MALFORMED");
				printf("\n\t%d proc%s actually on queue\n", j,
					j!=1?"s":"");
			}
			break;

		default:
			printf("garbage state 0%x(hex)\n", semtab[i].sstate&0xffff);
		}
	}
}

/*
 * routines to print contents of Xinu IO control block
 */
char	*unctrl[]	= {	/* unctrl codes for ttys		*/
	"^@", "^A", "^B", "^C", "^D", "^E", "^F", "^G", "^H", "^I", "^J", "^K",
	"^L", "^M", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U", "^V", "^W",
	"^X", "^Y", "^Z", "^[", "^\\", "^]", "^~", "^_",
	" ", "!", "\"", "#", "$",  "%", "&", "'", "(", ")", "*", "+", ",", "-",
	".", "/", "0",  "1", "2",  "3", "4", "5", "6", "7", "8", "9", ":", ";",
	"<", "=", ">",  "?", "@",  "A", "B", "C", "D", "E", "F", "G", "H", "I",
	"J", "K", "L",  "M", "N",  "O", "P", "Q", "R", "S", "T", "U", "V", "W",
	"X", "Y", "Z",  "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e",
	"f", "g", "h",  "i", "j",  "k", "l", "m", "n", "o", "p", "q", "r", "s",
	"t", "u", "v",  "w", "x",  "y", "z", "{", "|", "}", "~", "^?"
};
ttydump()
{
	register int i;
	register INT *CorPtr;

	for (i=0 ; i<Ntty ; i++)
		tty1dmp(i);
}
tty1dmp(ttynum)
int ttynum;
{
	int len;
	int i;
	struct tty *tp;

	tp = (struct tty *) (
		(unsigned)(tptr) +
		(unsigned)(ttynum* (sizeof(struct tty) - 2) ) );
		/* adjusted for VAX sizes */

/*	printf("\ntty device %d CSR at 0x%x\n",
		ttynum, ((unsigned)tp->ioaddr)&0xffff );
*/
	printf("\ntty device %d \n",
		ttynum);

        printf("INPUT -- tail at %d head at %d sem # %d value %d\n",
		tp->itail,tp->ihead,tp->isem,i = semtab[tp->isem].semcnt);
	if(i < 0)
		i = 0;
	else if(i > IBUFLEN)
		i = IBUFLEN;
	ioqdump(tp->itail,i,IBUFLEN,tp->ibuff);
        printf("OUTPUT -- tail at %d head at %d sem # %d value %d\n",
		tp->otail,tp->ohead,tp->osem,i = semtab[tp->osem].semcnt);
	i = OBUFLEN - i;
	if(i < 0)
		i = 0;
	else if(i > OBUFLEN)
		i = OBUFLEN;
	ioqdump(tp->otail,i,OBUFLEN,tp->obuff);
}

ioqdump(start,len,maxlen,buff)
short start,len,maxlen;
char *buff;
{
	register int i;

        for(i=start; len>0; len--){
                if(buff[i]&0200)printf("M-");
                printf("%s", unctrl[buff[i]&0177]);
		if(++i >= maxlen)
			i = 0;
        }
	printf("\n");
}

