#include "dis48.h"

#ifdef ANSI
NAddr	Instruction(char *mem, NAddr addr, char *istr);
#else
NAddr	Instruction();
#endif

typedef struct tlist {
	struct tlist	*next;
	NAddr		addr;
} TList;

TList	*tlist = NULL;
TList	*tfree = NULL;

void
#ifdef ANSI
TListPut(NAddr addr)
#else
TListPut(addr)
#endif
{
	TList	*p;
	char	*malloc();
	
	if (BITTEST(completed, addr))
		return;
		
	if (addr >= 0x80000) {
		fprintf(stderr, "%05x: out of range\n", addr);
		return;
	}
	
	if ((p = tfree) != NULL)
		tfree = p->next;
	else {
		if ((p = (TList *) malloc(sizeof(TList))) == NULL) {
			fprintf(stderr, "malloc TList failed\n");
			exit(1);
		}
	}
	
	p->addr = addr;
	p->next = tlist;
	tlist = p;
}

NAddr
#ifdef ANSI
TListGet(void)
#else
TListGet()
#endif
{
	TList	*p;
	
	if ((p = tlist) == NULL)
		return(NOADDR);
		
	tlist = p->next;
	p->next = tfree;
	tfree = p;
	return(p->addr);
}

IType	itype;
NAddr	target;

char	*itypestr[] = {"other", "branch", "call", "jump", "ujump"};

void
#ifdef ANSI
Disassemble(char *mem, NAddr start, NAddr end, FILE *fp)
#else
Disassemble(mem, start, end, fp)
char	*mem;
NAddr	start;
NAddr	end;
FILE	*fp;
#endif
{
	NAddr	addr, next;
	int	i, nybs;
	char	*p;
	char	buf[40];
	char	istr[40];
	extern int dopause;
	
	if (end == NOADDR) {
		TListPut(start);
		while ((addr = TListGet()) != NOADDR) {
			start = addr;
			if (BITTEST(completed, start)) {
				/*fprintf(stderr, "%05x: completed\n", addr);*/
				continue;
			}
			
			if (start >= 0x80000) {
				fprintf(stderr, "%05x: out of range\n", addr);
				continue;
			}
			
			/*fprintf(stderr, "%05x: start\n", addr);*/
			do {
				itype = other;
				target = NOADDR;
				next = Instruction(mem, addr, istr);
#ifdef notdef
				nybs = next - addr;
				p = AppendAddr(buf, addr);
				APPEND_CHAR(p, ':');
				APPEND_TAB(p);
				for (i = 7; (i > 0) && (nybs > 0); i--, nybs--)
					APPEND_CHAR(p,
						hex[GetNybble(mem, addr++)]);
				
				APPEND_TAB(p);
				fprintf(stderr, "%.*s %s", p - buf, buf, istr);
				fprintf(stderr, "\t%s %05x\n", itypestr[itype],
						target);
						
				while (nybs > 0) {
					fputc('\t', stderr);
					for (i = 7; (i > 0) && (nybs > 0);
						i--, nybs--)
						fputc(hex[GetNybble(mem,
								addr++)], fp);
				
					fputc('\n', stderr);
				}
#endif notdef

				switch (itype) {
				case call:
					fprintf(stderr, "%05x: call\n",
							target);
					/* fall through*/
					
				case branch:
					if (target != NOADDR)
						TListPut(target);
						
					break;
					
				case jump:
					/*printf("%05x: jumps to %05x\n", addr,
							target);*/
					TListPut(target);
					itype = ujump;
					break;
					
				case ujump:
					/*printf("%05x: unresolved jump\n",
							addr);*/
					break;
				}
				
				addr = next;
				if (BITTEST(completed, addr))
					break;
					
			} while (itype != ujump);
			
			end = next - 1;
			/*fprintf(stderr, "%05x: end\n", end);*/
			while (start <= end) {
				BITSET(completed, start);
				start++;
			}
			
			if (dopause && (fgets(buf, sizeof(buf), stdin) == NULL))
				break;	
		}
		
		return;
	}
	
	for (addr = start; addr < end; addr = next) {
		itype = other;
		target = NOADDR;
		next = Instruction(mem, addr, istr);
		nybs = next - addr;
		p = AppendAddr(buf, addr);
		APPEND_CHAR(p, ':');
		APPEND_TAB(p);
		for (i = 7; (i > 0) && (nybs > 0); i--, nybs--)
			APPEND_CHAR(p, hex[GetNybble(mem, addr++)]);
		
		APPEND_TAB(p);
		fprintf(fp, "%.*s %s\n", p - buf, buf, istr);
		while (nybs > 0) {
			p = AppendAddr(buf, addr);
			APPEND_CHAR(p, ':');
			APPEND_TAB(p);
			for (i = 7; (i > 0) && (nybs > 0); i--, nybs--)
				APPEND_CHAR(p, hex[GetNybble(mem, addr++)]);
		
			fprintf(fp, "%.*s\n", p - buf, buf);
		}
	}
}
