#include "dis48.h"

char	hex[] = "0123456789ABCDEF";

char	*InStr0[16] = {
	"retsetxm", "ret", "retsetc", "retclrc", "sethex", "setdec",
	"push.a\t\tc", "pop.a\t\tc", "clr.x\t\tst", "move.x\t\tst, c",
	"move.x\t\tc, st", "swap.x\t\tc, st", "inc.1\t\tp", "dec.1\t\tp",
	"AND/OR", "reti"
};

char	*OpStr0[8] = {
	"b, a", "c, b", "a, c", "c, d", "a, b", "b, c", "c, a", "d, c"
};

char	*InStr9[8] = {"eq", "ne", "z", "nz", "gt", "lt", "ge", "le"};

char	*OpStr9[4] = {"a, b", "b, c", "c, a", "d, c"};

char	*OpStraf[16] = {
	"b, a", "c, b", "a, c", "c, d", "a, a", "b, b", "c, c", "d, d",
	"a, b", "b, c", "c, a", "d, c", "b, a", "c, b", "a, c", "c, d"
};

#ifdef ANSI
char	*Instr1(char *mem, NAddr *addr, char *out);
char	*Instr8(char *mem, NAddr *addr, char *out);
#else
char	*Instr1();
char	*Instr8();
#endif

NAddr
#ifdef ANSI
Instruction(char *mem, NAddr addr, char *out)
#else
Instruction(mem, addr, out)
char	*mem;
NAddr	addr;
char	*out;
#endif
{
	Nybble	n;
	Nybble	fn;
	char	*p;
	char	c;
	int	disp, pc;
	
	switch (n = GetNybble(mem, addr++)) {
	case 0:
		if ((n = GetNybble(mem, addr++)) != 0xe) {
			p = AppendStr(out, InStr0[n]);
			if ((n < 4) || (n == 0xf))
				itype = ujump;
				
			break;
		}
		
		fn = GetNybble(mem, addr++);
		n = GetNybble(mem, addr++);
		p = AppendStr(out, (n < 8) ? "and" : "or");
		p = AppendField(p, fn);
		APPEND_TAB(p);
		p = AppendStr(p, OpStr0[n & 7]);
		break;
		
	case 1:
		p = Instr1(mem, &addr, out);
		break;
		
	case 2:
		p = AppendStr(out, "move.1\t\t");
		p = AppendImmNyb(p, mem, &addr, 1);
		APPEND_COMMA(p);
		APPEND_CHAR(p, 'p');
		break;
		
	case 3:
		p = AppendStr(out, "move.p");
		fn = GetNybble(mem, addr++);
		p = Append16(p, fn);
		APPEND_TAB(p);
		if ((p - out) < 8)
			APPEND_TAB(p);
			
		p = AppendImmNyb(p, mem, &addr, fn + 1);
		APPEND_COMMA(p);
		APPEND_CHAR(p, 'c');
		break;
		
	case 4:
	case 5:
		pc = addr;
		disp = GetInt(mem, &addr, 2);
		if (disp == 2) {
			p = AppendStr(out, "nop3");
			break;
		}
		
		p = AppendStr(out, (disp == 0) ? "retc" : "brc");
		APPEND_CHAR(p, (n == 4) ? 's' : 'c');
		if (disp != 0) {
			APPEND_TAB(p);
			APPEND_TAB(p);
			p = AppendRAddr(p, pc, disp, 2, 1);
		} else
			target = NOADDR;
		
		itype = branch;
		break;
	
	case 6:
		pc = addr;
		disp = GetInt(mem, &addr, 3);
		if ((disp == 3) || (disp == 4)) {
			p = AppendStr(out, "nop");
			APPEND_BDIGIT(p, disp + 1);
			if (disp == 4)
				addr++;
				
			break;
		}
			
		p = AppendStr(out, "jump.3\t\t");
		p = AppendRAddr(p, pc, disp, 3, 1);
		itype = jump;
		break;
		
	case 7:
		pc = addr + 3;
		disp = GetInt(mem, &addr, 3);
		p = AppendStr(out, "call.3\t\t");
		p = AppendRAddr(p, pc, disp, 3, 4);
		itype = call;
		break;
		
	case 8:
		fn = GetNybble(mem, addr);	/* peek! */
		if ((fn != 0xa) && (fn != 0xb)) {
			p = Instr8(mem, &addr, out);
			break;
		}
		
		/* fall through */
	case 9:
		if (n == 8) {
			c = (fn == 0xa) ? 0 : 1;
			fn = 0xf;
			addr++;
			
		} else {
			fn = GetNybble(mem, addr++);
			c = (fn < 8) ? 0 : 1;
			fn &= 7;
		}

		n = GetNybble(mem, addr++);
		pc = addr;
		disp = GetInt(mem, &addr, 2);
		p = AppendStr(out, (disp == 0) ? "ret" : "br");
		p = AppendStr(p, InStr9[((n >> 2) & 3) + 4 * c]);
		p = AppendField(p, fn);
		if ((p - out) < 8)
			APPEND_TAB(p);
			
		if ((c == 0) && (n >= 8)) {
			APPEND_CHAR(p, (n & 3) + 'a');
			
		} else {
			p = AppendStr(p, OpStr9[n & 3]);
		}
		
		if (disp != 0) {
			APPEND_COMMA(p);
			p = AppendRAddr(p, pc, disp, 2, 3);
		} else
			target = NOADDR;
		
		itype = branch;
		break;
		
	default:
		switch (n) {
		case 0xa:
			fn = GetNybble(mem, addr++);
			c = (fn < 8) ? 0 : 1;
			fn &= 7;
			disp = 0xa;
			break;
			
		case 0xb:
			fn = GetNybble(mem, addr++);
			c = (fn < 8) ? 0 : 1;
			fn &= 7;
			disp = 0xb;
			break;
			
		case 0xc:
		case 0xd:
			fn = 0xf;
			c = n & 1;
			disp = 0xa;
			break;
			
		case 0xe:
		case 0xf:
			fn = 0xf;
			c = n & 1;
			disp = 0xb;
			break;
		}
		
		n = GetNybble(mem, addr++);
		pc = 0;
		switch (disp) {
		case 0xa:
			if (c == 0) {
				if (n < 0xc) {
					p = "add";
				
				} else {
					p = "dec";
					pc = 1;
				}
				
			} else {
				if (n < 4) {
					p = "clr";
					pc = 1;
					
				} else if (n >= 0xc) {
					p = "swap";
				
				} else {
					p = "move";
					if (n < 8)
						n -= 4;
				}
			}
			
			break;
			
		case 0xb:
			if (c == 0) {
				if (n >= 0xc) {
					p = "subn";
				
				} else if ((n >= 4) && (n <= 7)) {
					p = "inc";
					pc = 1;
					n -= 4;
				
				} else {
					p = "sub";
				}
				
			} else {
				pc = 1;
				if (n < 4)
					p = "sln";
					
				else if (n < 8)
					p = "srn";
					
				else if (n < 0xc)
					p = "neg";
					
				else
					p = "not";
			}
			
			break;
		}
		
		p = AppendStr(out, p);
		p = AppendField(p, fn);
		if ((p - out) < 9)
			APPEND_TAB(p);
			
		if (pc == 1)
			APPEND_CHAR(p, (n & 3) + 'a');
		
		else
			p = AppendStr(p, OpStraf[n]);
			
		break;
	}
	
	TERMINATE(p);
	return(addr);
}
