/*
 * File: ncode.c
 *
 * Code disassembly routines for IMP80 compiler
 *
 * Copyright (c) 2026, Bob Eager
 *
 */

#include "typedefs.h"
#include "ncode.h"
#include "opcode.h"
#include <stdio.h>

/* External references */

extern	FILE	*lfp;
extern	INT	line;

/* Forward references */

static	INT	decode(INT, INT, INT);

/* Tables */

/* Opcode names, indexed by 7-bit opcode */

static	PCHAR opcode[] = {
"****","JCC ","JAT ","JAF ","****","****","****","****",
"VAL ","CYD ","INCA","MODD","PRCL","J   ","JLK ","CALL",
"ADB ","SBB ","DEBJ","CPB ","SIG ","MYB ","VMY ","CPIB",
"LCT ","MPSR","CPSR","STCT","EXIT","ESEX","OUT ","ACT ",
"SL  ","SLSS","SLSD","SLSQ","ST  ","STUH","STXN","IDLE",
"SLD ","SLB ","TDEC","INCT","STD ","STB ","STLN","STSF",
"L   ","LSS ","LSD ","LSQ ","RRTC","LUH ","RALN","ASF ",
"LDRL","LDA ","LDTB","LDB ","LD  ","LB  ","LLN ","LXN ",
"TCH ","ANDS","ORS ","NEQS","EXPA","AND ","OR  ","NEQ ",
"PK  ","INS ","SUPK","****","COMA","DDV ","DRDV","DMDV",
"SWEQ","SWNE","CPS ","TTR ","FLT ","IDV ","IRDV","IMDV",
"MVL ","MV  ","CHOV","****","FIX ","RDV ","RRDV","RDVD",
"UAD ","USB ","URSB","UCP ","USH ","ROT ","SHS ","SHZ ",
"DAD ","DSB ","DRSB","DCP ","DSH ","DMY ","DMYD","CBIN",
"IAD ","ISB ","IRSB","ICP ","ISH ","IMY ","IMYD","CDEC",
"RAD ","RSB ","RRSB","RCP ","RSC ","RMY ","RMYD","****",
NULL
};

/* Prefixes for primary operand descriptions */

static	PCHAR prefpop[32] = {
"","***         ","(LNB","(XNB",
"(PC","(CTB","%TOS         ","%B          ",
"(DR","***         ","(DR+(LNB","(DR+(XNB",
"(DR+(PC","(DR+(CTB","(DR+%TOS)    ","(%B",
"(","***         ","((LNB","((XNB",
"((PC","((CTB","(%TOS)       ","(DR)        ",
"(%B)","***         ","((LNB","((XNB",
"((PC","((CTB","(%TOS+%B)   ","(DR+%B)    "
};

/* Suffixes for primary operand descriptions */

static	PCHAR sufpop[32] = {
"","",")     ",")     ",
")      ",")     ","","",
")      ","",")) ",")) ",
")) ",")) ","",")       ",
")","","))    ","))   ",
"))    ","))    ","","",
"","",")+%B)",")+%B)",
")+%B) ",")+%B)","",""
};

/* Tertiary operand descriptions */

static PCHAR top[8] = {
"","(DR+","(LNB+","(XNB+","(PC+","(CTB+","(DR)   ","(DR+%B) "
};

/* Alternate forms for tertiary opcodes (including masks) */

static PCHAR jas[16] = {
"FACC=0","FACC>0","FACC<0"," ? ","ACC=0","ACC>0","ACC<0",
" ? ","DACC=0","DACC>0","DACC<0","DRLEN=0",
" B=0 "," B>0 "," B<0 ","OV SET"
};


/* Instruction formats */

#define	IF_PRIMARY		1
#define	IF_SECONDARY		2
#define	IF_TERTIARY		3


/*
 * Disassemble a block of code. Output is to the file pointer 'lfp'.
 *
 * Inputs:
 *	start	pointer to start of code
 *	finish	pointer to end of code
 *	ca	compiled address of start of code
 *
 * Outputs:
 *	none
 *
 */

VOID ncode(PBYTE start, PBYTE finish, INT ca)
{	INT ins;			/* instruction being decoded */
	INT all = finish - start;
	INT pc = 0;
	INT len;

	while(pc < all) {
		for(INT i = 0; i < 4; i++)	/* fetch instruction */
			ins = (ins << 8) | start[pc + i];
		fputc('\n', lfp);
		len = decode(ins, pc+ca, ca);
		pc += (len >> 3);	/* 2 or 4 bytes */
	}
}


/*
 * Decode one instruction.
 *
 * Inputs:
 *	data	single instruction (left half if 16 bit)
 *	pc	PC of instruction
 *
 * Outputs:
 *	none
 *
 */

static INT decode(INT data, INT pc, INT ca)
{	INT op;				/* opcode */
	INT k, kp, kpp, kppp;		/* operand codes */
	INT h, q, mask, litfill;	/* for secondary format */
	INT n;				/* operand */
	BOOL illegal = FALSE;		/* marks illegal opcode */
	INT len;			/* length (16 or 32) */
	INT type;			/* primary, secondary, tertiary */
	BOOL literal = FALSE;		/* marks literal operand */
	BOOL sign = FALSE;		/* marks sign of operand */
	BOOL jump = FALSE;		/* marks jumps */

	op = (data >> 24) & 0xfe;		/* isolate opcode */
	if(opcode[op >> 1][0] == '*') {		/* illegal ones */
		len = 16;
		illegal = TRUE;
	} else {
		if((0x02 <= op) && (op <= 0x6)) {	/* tertiary format */
			type = IF_TERTIARY;
			kppp = (data >> 18) & 0x07;
			if(kppp <= 5) {		/* 32 bit form */
				len = 32;
				n = data & 0x3ffff;
			} else {		/* 16 bit form */
				len = 16;
				if(data & 0x00030000) illegal = TRUE;
			}
		} else {
			n = (op >> 4) & 0x0f;	/* see if primary or secondary */
			if((0x08 <= n) && (n <= 0x0b) && ((op & 0x0f) <= 6)) {
				type = IF_SECONDARY;
				h = (data >> 24) & 0x01;
				q = (data >> 23) & 0x01;
				n = (data >> 16) & 0x7f;
				if(q) {		/* 32 bit form */
					len = 32;
					mask = (data >> 8) & 0xff;
					litfill = data & 0xff;
				} else {	/* 16 bit form */
					len = 16;
				}
			} else {
				type = IF_PRIMARY;
				k = (data >> 23) & 0x03;	/* only have 16 bits so far */
				n = (data >> 16) & 0x7f;	/* n or N */
				if(k != 3) {		/* 16 bit form */
					if(k == 0) literal = TRUE;
					len = 16;
				} else {	/* may be 16 or 32 bit still */
					kp = (data >> 21) & 0x03;
					kpp = (data >> 18) & 0x07;
					if((kp == 0) && (kpp == 0))
						literal = TRUE;
					if((kpp < 6) ||		/* 32 bit */
					   ((kp == 1) & (kpp == 7))) {
						len = 32;
						n = data & 0x3ffff;
					} else {	/* 16 bit */
						if((data & 0x030000) != 0)
							illegal = TRUE;
						len = 16;
					}
				}
			}
		}
	}

	/* Identify primary format relative jumps */
	if(((JUNC <= op) && (op <= CALL)) || (op == DEBJ)) jump = TRUE;

	/* The operand types and values have now been identified. */

	/* Output instruction in hex */
	if(len == 16) {
		fprintf(lfp, "%05X  %04X    ", pc, (data >> 16) & 0xffff);
	} else {
		fprintf(lfp, "%05X  %08X", pc, data);
	}

	/* Output instruction as a mnemonic */
	fprintf(lfp, "  %s", opcode[op >> 1]);	/* output opcode */
	if(illegal == TRUE) return(16);		/* no further */

	/* Now the operand (if any) */	
	switch(type) {
		case IF_PRIMARY:
			if((op == CYD)  ||
			   (op == ESEX) ||
			   (op == EXPA) ||
			   (op == COMA) ||
			   (op == CBIN) ||
			   (op == CDEC))
				break;		/* no operands */

			fputc(' ', lfp);
			if(k < 3) {
				if(k == 1) fputs("(LNB+", lfp);
				if(k == 2) fputs("((LNB+", lfp);
				if((op != EXIT) && (k == 0) && (n & 0x40)) {
					n = -(n | 0xffffff80);
					sign = TRUE;
				}
				if(!jump || !literal)
					fprintf(lfp, "%s%X", sign ? "-" : "",
						n & 0x7f);
				if(k == 1) fputs(")    ", lfp);
				if(k == 2) fputs(")) ", lfp);
			} else {
				fputs(prefpop[kp*8+kpp], lfp);
				if(len == 32) {
					if(((kp == 0) && (kpp == 0)) || (kpp == 4)) {
							/* n is signed */
						if(n & 0x20000) {
							n = -(n | 0xfffc0000);
							sign = TRUE;
						}
						if(kpp == 4) {
							fputs(sign ? "-" : "+", lfp);
						}
					} else {
						if(!(((kp == 2) || (kp == 3)) && (kpp == 0)))
							fputs("+", lfp);/* no sign for IS */
					}
					if(!((kp == 3) && (kpp == 0)))
						if(!jump || !literal)
							fprintf(lfp, "%X", n & 0x3ffff);
					fputs(sufpop[kp*8+kpp], lfp);
				}
				if(sign) n = -n;
				if((kp == 0) && (kpp == 4))	/* relative jump */
					fprintf(lfp, "[at %05X]", pc + n*2);
			}
			if(literal && jump) {
				if(sign && (n > 0)) n = -n;
				fprintf(lfp, " to %05X  ", (pc + n*2) & 0x3ffff);
			}
			break;

		case IF_SECONDARY:
			if(h == 0) fprintf(lfp, " L=%X", n+1);
			if(len == 32)
				fprintf(lfp, "%sMask=%02X,Lit=%02X",
					h == 0 ? "," : "", mask, litfill);
			break;

		case IF_TERTIARY:
			fputs(top[kppp], lfp);
			if(len == 32) {
				if(((kppp == 0) || (kppp == 4)) &&
				   (n & 0x20000)) {	/* signed operand */
					n = -(n | 0xfffc0000);
					sign = TRUE;
				}
				if(kppp != 0) {
					fprintf(lfp, "%s%X", sign ? "-" : "",
						n & 0x3ffff);
				} else {
					if(sign) n = -n;
					fprintf(lfp, " to %05X  ", (pc + n*2) & 0x3ffff);
				}
				if((1 <= kppp) && (kppp <= 5)) fputc(')', lfp);
			} else data <<= 16;	/* for next lines */
			if((op == JAT) || (op == JAF)) {
				fprintf(lfp, "   on %s", jas[(data >> 21) & 0x0f]);
			} else {			/* JCC */
				fprintf(lfp, "   Mask=%X", (data >> 21) & 0x0f);
			}
	}
	return(len);
}


/*
 * Recode.
 *
 */

VOID recode(PBYTE start, PBYTE finish, INT ca)
{	if(start != finish) {
		fprintf(lfp, "\n\nCode for line %d\n", line);
		ncode(start, finish, ca);
	}
}


/*
 * End of file: ncode.c
 *
 */
