/*

 SSP1601 disassembler
 written by Pierpaolo Prazzoli
 updated for SSP1601 by Grazvydas Ignotas

*/

#include <math.h>
#include "debugger.h"
#include "cpuintrf.h"


static const char *const reg[16] =
{
	"-",    "X",     "Y",    "A",
	"ST",   "STACK", "PC",   "P",
	"EXT0", "EXT1",  "EXT2", "EXT3",
	"EXT4", "EXT5",  "EXT6", "AL"
};

static const char *const rij[8] =
{
	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"
};

static const char *const modifier[4] =
{
	"", "+!", "-", "+"
};

static const char *const modifier_sf[4] =
{
	"|00", "|01", "|10", "|11"
};

static const char *const cond[16] =
{
	"always",   "RESERVED", "gpi0",     "gpi1",
	"l",        "z",        "ov",       "n",
	"diof",     "gpi2",     "gpi3",     "RESERVED",
	"RESERVED", "RESERVED", "RESERVED", "RESERVED",
};

static const char *const acc_op[8] =
{
	"ror", "rol", "shr", "shl", "inc", "dec", "neg", "abs"
};

// pag. 81 uses different addresses!
static const char *const flag_op[16] =
{
	"?", "?", "resl", "setl", "resie", "setie", "?", "?", "resop", "setop", "?", "?", "?", "?", "res", "set"
};

static const char *const arith_ops[8] =
{
	"", "add", "", "cmp", "add", "and", "or", "eor"
};


#define BIT_B				((op >> 8) & 1)
#define RIJ					rij[(BIT_B << 2) + (op & 3)]
#define RI(i)				rij[(i) & 3]
#define RJ(i)				rij[((i) & 3) + 4]
#define MODIFIER(mod,r3)	(((r3) == 3) ? modifier_sf[mod] : modifier[mod])
#define MODIFIER_LOW		MODIFIER((op >> 2) & 3, op&3)
#define MODIFIER_HIGH		MODIFIER((op >> 6) & 3, (op >> 4)&3)

#define READ_OP_DASM(p)	((base_oprom[p] << 8) | base_oprom[(p) + 1])

static char *get_cond(int op)
{
	static char scond[16];
	if (op&0xf0) sprintf(scond, "%s=%i", cond[(op >> 4) & 0xf], BIT_B);
	else         sprintf(scond, "%s", cond[(op >> 4) & 0xf]);
	return scond;
}


unsigned dasm_ssp1601(char *buffer, unsigned pc, const UINT8 *oprom)
{
	const UINT8 *base_oprom;
	UINT16 op;
	int size = 1;
	int flags = 0;

	base_oprom = oprom;

	op = READ_OP_DASM(0);

	switch (op >> 9)
	{
		case 0x00:
			if(op == 0)
			{
				// nop
				sprintf(buffer, "nop");
			}
			else if((op & 0xff) == 0x65)
			{
				// ret
				sprintf(buffer, "ret");
				flags |= DASMFLAG_STEP_OUT;
			}
			else
			{
				// ld d, s
				sprintf(buffer, "ld %s, %s", reg[(op >> 4) & 0xf], reg[op & 0xf]);
			}
			break;

		// ld d, (ri)
		case 0x01:
			sprintf(buffer, "ld %s, (%s%s)", reg[(op >> 4) & 0xf], RIJ, MODIFIER_LOW);
			break;

		// ld (ri), s
		case 0x02:
			sprintf(buffer, "ld (%s%s), %s", RIJ, MODIFIER_LOW, reg[(op >> 4) & 0xf]);
			break;

		// ld a, addr
		case 0x03:
			sprintf(buffer, "ld A, %X", op & 0x1ff);
			break;

		// ldi d, imm
		case 0x04:
			sprintf(buffer, "ld %s, %X", reg[(op >> 4) & 0xf], READ_OP_DASM(2));
			size = 2;
			break;

		// ld d, ((ri))
		case 0x05:
			sprintf(buffer, "ld %s, ((%s%s))", reg[(op >> 4) & 0xf], RIJ, MODIFIER_LOW);
			break;

		// ldi (ri), imm
		case 0x06:
			sprintf(buffer, "ld (%s%s), %X", RIJ, MODIFIER_LOW, READ_OP_DASM(2));
			size = 2;
			break;

		// ld addr, a
		case 0x07:
			sprintf(buffer, "ld %X, A", op & 0x1ff);
			break;

		// ld d, ri
		case 0x09:
			sprintf(buffer, "ld %s, %s%s", reg[(op >> 4) & 0xf], RIJ, MODIFIER_LOW);
			break;

		// ld ri, s
		case 0x0a:
			sprintf(buffer, "ld %s%s, %s", RIJ, MODIFIER_LOW, reg[(op >> 4) & 0xf]);
			break;

		// ldi ri, simm
		case 0x0c:
		case 0x0d:
		case 0x0e:
		case 0x0f:
			sprintf(buffer, "ldi %s, %X", rij[(op >> 8) & 7], op & 0xff);
			break;

		// op a, s
		case 0x10:
		case 0x30:
		case 0x40:
		case 0x50:
		case 0x60:
		case 0x70:
			sprintf(buffer, "%s A, %s", arith_ops[op >> 13], reg[op & 0xf]);
			break;

		// op a, (ri)
		case 0x11:
		case 0x31:
		case 0x41:
		case 0x51:
		case 0x61:
		case 0x71:
			sprintf(buffer, "%s A, (%s%s)", arith_ops[op >> 13], RIJ, MODIFIER_LOW);
			break;

		// op a, adr
		case 0x13:
		case 0x33:
		case 0x43:
		case 0x53:
		case 0x63:
		case 0x73:
			sprintf(buffer, "%s A, %X", arith_ops[op >> 13], op & 0x1ff);
			break;

		// subi a, imm
		case 0x14:
		case 0x34:
		case 0x44:
		case 0x54:
		case 0x64:
		case 0x74:
			sprintf(buffer, "%si A, %X", arith_ops[op >> 13], READ_OP_DASM(2));
			size = 2;
			break;

		// op a, ((ri))
		case 0x15:
		case 0x35:
		case 0x45:
		case 0x55:
		case 0x65:
		case 0x75:
			sprintf(buffer, "%s A, ((%s%s))", arith_ops[op >> 13], RIJ, MODIFIER_LOW);
			break;

		// sub a, ri
		case 0x19:
		case 0x39:
		case 0x49:
		case 0x59:
		case 0x69:
		case 0x79:
			sprintf(buffer, "%s A, %s%s", arith_ops[op >> 13], RIJ, MODIFIER_LOW);
			break;

		// mpys (rj), (ri), b
		case 0x1b:
			sprintf(buffer, "mpya (%s%s), (%s%s), %d", RJ(op >> 4), MODIFIER_HIGH, RI(op), MODIFIER_LOW, BIT_B);
			break;

		// subi simm
		case 0x1c:
		case 0x3c:
		case 0x4c:
		case 0x5c:
		case 0x6c:
		case 0x7c:
			sprintf(buffer, "%si %X", arith_ops[op >> 13], op & 0xff);
			break;

		// call cond, addr
		case 0x24:
			sprintf(buffer, "call %s, %X", get_cond(op), READ_OP_DASM(2));
			flags |= DASMFLAG_STEP_OVER;
			size = 2;
			break;

		// ld d, (a)
		case 0x25:
			sprintf(buffer, "ld %s, (A)", reg[(op >> 4) & 0xf]);
			break;

		// bra cond, addr
		case 0x26:
			sprintf(buffer, "bra %s, %X", get_cond(op), READ_OP_DASM(2));
			size = 2;
			break;

		// mod cond, op
		case 0x48:
			sprintf(buffer, "mod %s, %s", get_cond(op), acc_op[op & 7]);
			break;

		// mod f, op
		case 0x4a:
			sprintf(buffer, "%s", flag_op[op & 0xf]);
			break;

		// mpya (rj), (ri), b
		case 0x4b:
			sprintf(buffer, "mpya (%s%s), (%s%s), %d", RJ(op >> 4), MODIFIER_HIGH, RI(op), MODIFIER_LOW, BIT_B);
			break;

		// mld (rj), (ri), b
		case 0x5b:
			sprintf(buffer, "mld (%s%s), (%s%s), %d", RJ(op >> 4), MODIFIER_HIGH, RI(op), MODIFIER_LOW, BIT_B);
			break;

		default:
			sprintf(buffer, "OP = %04X", op);
			break;
	}

	return size | flags | DASMFLAG_SUPPORTED;
}

// vim:ts=4
