/*
 * disasm.c -- Disassembler for the debugger monitor
 * copyright (c) 1984  American Information Systems Corporation
 *  Dock Williams
 *  March, 1984
 */

# ifdef SCCS
static char *sccsid = "@(#)disasm.c	1.22 (NSC) 8/30/83";
# endif

#include "monitor.h"
#include "vctype.h"
#include "mondef.h"

/* externally defined read only data, see rodata.c */
extern INSTFORM format[];
extern char *fieldtab[];
extern char fieldtype[];
extern char indexm[];



snprint(num)
int	num;
{
	switch(curbase) {
	case 'o':
		printf("%o",num);
		break;
	case 'd':
		printf("%d",num);
		break;
	case 'x':
	default:
		if ( (num < 0) && (num > -100) )
			printf("%d",num);
		else
			printf("%x",num);
		break;
	}
}




    static
typeout(num,format)
int num;
char *format;
{
	if ((*format != '*') && (*format != '\0')) OUTCHR(*format);
	format++;
	if (cflags & CF_SYMP) psym(num);
	else snprint(num);
	if ((*format != '*') && (*format != '\0')) OUTCHR(*format);
	format++;
	if ((*format != '*') && (*format != '\0')) OUTCHR(*format);
}



    static unsigned
ext(x,p,n)
unsigned x,p,n;
{
	return((x >> (p+1-n)) & ~(~0 << n));
}


    static unsigned
ins(x,y,n)
unsigned x,y,n;
{
	return((y << n) | (x & ~(~0 << n)));
}



    static unsigned
getfield(pattern,matchc,value)
char *pattern, matchc;
unsigned value;
{
char *sp;
int firstbit,len;
	sp = (char *) index(pattern,matchc);
	if (sp == 0) return(-1);
	for (firstbit=len=0; *sp != '\0'; firstbit++)
		if (*sp++ == matchc) len++;
	return(ext(value,firstbit-1,len));
}


    static
getname(value,names,name)
int value;
char *names,*name;
{
int i;
	*name = '\0';
	if (value < 0) return;
	for (i=0; i < value; i++) {
		while (*names != '/') {
			if (*names == '\0') return;
			names++;
		}
		names++;
	}
	if ((*names == '/') || (*names == '\0')) return;
	do {
		*name++ = *names++;
	} while ((*names != '/') && (*names != '\0'));
	*name = '\0';
}


    static unsigned
instget()
{
	if (ibuf == 0) ibuf = inbuf;
	if ((ilen < 0) || (ilen >= IBUFFERSIZE)) {
		if (ilen < 0) ilen = 0;
		getmemb(istart+ilen,IBUFFERSIZE,ibuf);
		istart += ilen;
		ilen = 0;
	}
	return((0xff & (*(ibuf + ilen++))));
}


    static
disp()
{
int i,r;
	r = instget();
	if ((r & 0x80) == 0) {
	  if (r & 0x40) {
		r = r | 0xffffff80;
	  } else r = r & 0x7f;
	} else if ((r & 0xc0) == 0x80) {
		r = instget() | (r << 8);
	        if (r & 0x2000) {
		    r = r | 0xffffc000;
	        } else r = r & 0x3fff;
	} else {
		for (i=0; i < 3; i++) {
		    r <<= 8;
		    r |= instget();
		}
	        if (r & 0x20000000) {
		    r = r | 0xc0000000;
	        } else r = r & 0x3fffffff;
	}
	return(r);
}


    static
reglist(value,f,i,l)
{
int	n = 0,t = FALSE;
	OUTCHR('[');
	while (f != l) {
		if (ext(value,f,1)) {
			if (t) {
				OUTCHR(',');
			} else
				t = TRUE;
			printf("r%d",n);
		}
		n++;
		f += i;
	}
	OUTCHR(']');
	return(TRUE);
}


    static
findmod(pc)
{

#ifdef MODTAB
	register int i;

	for (i = 0x50; ((i < lastmod) && (i < 0x1000)); i+=16)
		if (getdouble(i+8) <= pc) return(i);
	return(0);
#else
	return(0x50);
#endif

}


    static
etype(d1,d2)
{
    printf("ext<");
    typeout(getdouble(getdouble(findmod(istart)+4)+(d1*4))+d2,"*>");
}

    static
newsbtype(d1)
{
    if (opcxpd) {
	    d1 = getdouble(getdouble(findmod(istart))+d1);
	    typeout(getdouble((d1&0xffff)+8) + ext(d1,31,16),"**");
    } else {
	    typeout(getdouble(getdouble(findmod(istart))+d1),"*<");
	    printf("xsb>");
    }
}

    static
mtype(d1,regn,regname)
char *regname;
{
	if (regn <= 1) {
		snprint(d1);
		printf("(%s)",regname);
		return;
	}
	if (regn == 3) typeout(istart+d1,"*<");
	else
	typeout(getdouble(findmod(istart))+d1,"*<");
	printf("%s>",regname);
}

    static
mmtype(d1,d2,regn,regname)
char *regname;
{
	snprint(d2);
	OUTCHR('(');
	mtype(d1,regn,regname);
	OUTCHR(')');
}

    static
newsbmtype(d1,d2,regn,regname)
char *regname;
{
	if (d2 == 0) {
		newsbtype(d1);
	} else {
		snprint(d2);
		OUTCHR('(');
		newsbtype(d1);
		OUTCHR(')');
	}
}

    static
domode(mode,ibyte,printit)
{
	int i,indexmode = 0,d1,d2;
	char fieldname[9];
	if (mode >= 0x1c) {
	    indexmode = mode;
	    mode = ibyte >> 3;
	}
	getname((8+(mode & 3)),fieldtab[6],fieldname);
	switch (mode) {
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
		if (printit) printf("r%d",mode & 7);
		break;
	case 8:
	case 9:
	case 10:
	case 11:
	case 12:
	case 13:
	case 14:
	case 15:
		d1 = disp();
		if (printit) {
		    snprint(d1);
		    printf("(r%d)",mode & 7);
		}
		break;
	case 16:
	case 17:
	case 18:
		d1 = disp();
		d2 = disp();
		if (printit) {
			if ((mode == 0x12) && (d1 < 0)) {
		    		newsbmtype(d1,d2,mode&3,fieldname);
			} else {
		    		mmtype(d1,d2,mode&3,fieldname);
			}
		}
		break;
	case 20:
		d1 = 0;
		for (i=1; i <= context; i++) d1 = instget() | (d1 << 8);
		switch (context) {
		case 1:
			if (d1 & 0x80) d1 |= 0xffffff00;
			break;
		case 2:
			if (d1 & 0x8000) d1 |= 0xffff0000;
			break;
		}
		if (printit) typeout(d1,"**");
		break;
	case 21:
		d1 = disp();
		if (printit) {
		    OUTCHR('@');
		    typeout(d1,"**");
		}
		break;
	case 22:
		d1 = disp();
		d2 = disp();
		if (printit) etype(d1,d2);
		break;
	case 23:
		if (printit) printf("tos");
		break;
	default:
		d1 = disp();
		if (printit) {
			if ((mode == 0x1a) && (d1 < 0)) {
				newsbtype(d1);
			} else {
				mtype(d1,mode&3,fieldname);
			}
		}
		break;
	}
	if ((indexmode) && (printit))
		printf("[r%d:%c]",ibyte&7,indexm[indexmode&3]);
}

mdisasm(instat,instlen,printit,map)
int instat,*instlen,printit,map;
{
char *ip,*in,*fp;
char opname[9],firstchar;
int f,f2,ib1,ib2,i,j,opcode,len,displace,operand = FALSE,bytes = 0;
unsigned instrc, mask;
	mapping = map;		/* copy to global area */
	istart = instat;
	ilen = -1;
	for (i = 0; *(format[i].iproto) != '\0'; i++) {
		ip = format[i].iproto;
		in = format[i].inames;
		len = strlen(ip);
		while ((len / 8) > bytes) {
			instrc = ins(instrc,instget(),bytes << 3);
			bytes++;
		}
		mask = 1 << (len - 1);
		for (j = 0; j < len; j++) {
			if (instrc & mask) { if (*(ip+j) == '0') goto trynext; }
		 	else if (*(ip+j) == '1') goto trynext;
			mask >>= 1;
		}
		context = getfield(ip,'b',instrc) + 1;
		if (context == 3) {
			if (i == 14) {
				opcode = getfield(ip,'x',instrc);
				if (opcode == 2) context = 4;
				else goto trynext;
			} else goto trynext;
		} else if (context == 0) context = 4;
		opcode = getfield(ip,'x',instrc);
		if (opcode == -1) opcode = 0;
		getname(opcode,in,opname);
		if (opname[0] == '\0') goto trynext;
		if (displace = isupper(opname[0])) opname[0] |= 0x20;
	 	if (printit) printf("%s",opname);	
		firstchar = opname[0];
		if (printit) {
			if (i == 14) {
			    if ((opcode != 6) && (opcode >= 4))
				OUTCHR(getfield(ip,'f',instrc) ? 'f' : 'l');
			} else if (i == 15)
				OUTCHR(getfield(ip,'f',instrc) ? 'f' : 'l');
			fp = fieldtype;
			for (j=0; *fp != '\0'; j++) {
			    if (*fp == 'M') if (opcode <= 1) {
				fp++;
			    	continue;
			    }
			    f = getfield(ip,*fp++,instrc);
			    if (f >= 0) {
				if (j == 1) {
				    if (i == 5)
					if ((opcode == 0) || (opcode == 4) ||
					    (opcode == 12)) continue;
				    if ((i == 6) && (opcode == 9)) continue;
				    if (i == 10)
				    if ((opcode==4)||(opcode==5)) continue;
				    if (i == 14)
				    if ((opcode==1)||(opcode==6)||
					(opcode==2)||(opcode==3)) continue;
				}
				if ((opcode==1) && (j==5) && (i==12)) continue;
				getname(f,fieldtab[j],opname);
				if (j > 2) {
					if (operand) { OUTCHR(',');
					} else operand = TRUE;
				}
				printf("%s",opname);
				if (i == 10)
				    if ((opcode==6)||(opcode==7)) OUTCHR('d');
				if ((i == 14) && (opcode==0))
				OUTCHR(getfield(ip,'f',instrc) ? 'f' : 'l');
			    }

			    if (j == 2) OUTCHR('\t');
		    }
		}
		if (i == 1) {
			if ((opcode ==6) || (opcode ==8)) {
				f = instget();
				if (printit) operand = reglist(f,0,1,8);
			} else if ((opcode ==7) || (opcode ==9)) {
				f = instget();
				if (printit) operand = reglist(f,7,-1,-1);
			}
		}
		if (i == 9)
			if ((opcode ==0) || (opcode == 1) || (opcode ==5))
				context = 1;
		if ((i == 5) && (opcode == 0)) opcxpd = TRUE;
		else opcxpd = FALSE;
		f = getfield(ip,'m',instrc);
		if (f >= 0x1c) ib1 = instget();
		f2 = getfield(ip,'2',instrc);
		if (f2 >= 0x1c) ib2 = instget();
		if (f >= 0) {
		    if ((operand) && (printit)) { OUTCHR(',');
		    } else operand = TRUE;
		    domode(f,ib1,printit);
		}
		if (f2 >= 0) {
		    if ((operand) && (printit)) { OUTCHR(',');
		    } else operand = TRUE;
		    domode(f2,ib2,printit);
		}
		if (displace == TRUE) {
		    if (operand && printit) OUTCHR(',');
		    f = disp();
		    if (printit) {
			if (firstchar <= 'b') typeout(instat+f,"**");
			else if (firstchar == 'c') {
			    ib1 = getdouble(getdouble(findmod(instat)+4)+(f*4));
			    typeout(getdouble((ib1&0xffff)+8) +
				    ext(ib1,31,16),"**");
			}
			else snprint(f);
		    }
			
		}
		if (i == 10) 
			if ((opcode == 2) || (opcode == 3)) {
			    f = instget();
			    if (printit) 
			    printf(",%d,%d",(f >> 5),((f & 0x1f)+1));
			}
		*instlen = ilen + (istart - instat);
		return(TRUE);
		trynext: ;
	}
	return(FALSE);
}

/*********************************************************
ibackup(addrat)
{
	int newaddr,modf,off,symatch;
	int instlen = 0;
	char symfound[MAXSTRLEN];
	if (autoi(addrat)) {
		newaddr = addrat - 1;
		symatch = lookbyval(newaddr,&off,&modf,symfound);
		if (symatch) {
		    newaddr -= off;
		    while ((newaddr + instlen) < addrat) {
			newaddr += instlen;
			if (disasm(newaddr,&instlen,FALSE)) {
			} else return(addrat);
		    }
		    return(newaddr);
		}
		

	} 
	return(addrat);
}

char *regstr(regnum)
int regnum;
{
char *preg = '\0';
	if (regnum >= 0) {
		printf("can't print this register- bug\r\n");
	} else {
		if (regnum > SPSR) {
			preg = reg2tab[0 - (regnum - SR0)].regname;
		} else if (regnum > SBPR0) {
			preg = reg3tab[0 - (regnum - SPSR)].regname;
		} else if (regnum >= SINTB) {
			preg = reg4tab[0 - (regnum - SBPR0)].regname;
		} else if (regnum == FSR) {
			preg = reg3tab[7].regname;
		} else if ((regnum >= F7) && (regnum <= F0)) {
			preg = reg2tab[11 + (F0 - regnum)].regname;
		} else {
		    printf("can't print this register- bug\r\n");
		}
	}
	return(preg);
}

tracestack(fpat,pcfrom,modin,doargs)
int fpat,pcfrom,modin;
{
	int pcreturn,fpnext,modf,off,symatch;
	int instreturn,args,arg,i = 0;
	char symfound[MAXSTRLEN];
	symatch = lookbyval(pcfrom,&off,&modf,symfound);
	if ((symatch == FALSE) || (modf != modin)) return;
	printf("%s",symfound);
	if (off) {
		OUTCHR('+');
		snprint(off);
	}
	OUTCHR('(');
	pcreturn = getdouble(fpat+4);
	if (doargs) {
		instreturn = getdouble(pcreturn);
		if ((instreturn & 0xffff) == 0xa57c) {
			args = 0 - ((instreturn >> 16) | 0xffffff00);
			while (args > 0) {
				arg = getdouble(fpat+12+i);
				typeout(arg,"***");
				i += 4;
				args -= 4;
				if (args > 0) OUTCHR(',');

			}
		}
	}
	printf(")\r\n");
	fpnext = getdouble(fpat);
	if (fpcheck(fpnext))
		tracestack(fpnext,pcreturn,(getdouble(fpat+8) & 0xffff),doargs);

}

skipinst()
{
int instlen, thepc;
	thepc = getreg(SPC);
	if (disasm(thepc,&instlen,FALSE)) {
		setreg(SPC,(thepc + instlen));
	} else {
		printf("\r\ncan not skip this instruction\r\n");
	}
	showspot();
}
******************************************************************/


/*
 * Return the ptr in sp at which the character c appears;
 * NULL if not found
 */

#define	NULL	0

    static
index(sp, c)
register char *sp, c;
{
	do {
		if (*sp == c)
			return((int)sp);
	} while (*sp++);
	return(NULL);
}

    static
getmemb(addr,len,buf)
int addr, len;
char *buf;
{
	register int i;

	for(i=0;i<len;i++)
		buf[i] = mrdbyte((addr+i), mapping);
}

    static
getdouble(addr)
int addr;
{
	return(mrdquad(addr, mapping));
}

