/*
 *   A quick-hack 6803/6808 disassembler
 *
 *   Note: this is not the good and proper way to disassemble anything, but it works
 *
 *   I'm afraid to put my name on it, but I feel obligated:
 *   This code written by Aaron Giles (agiles@sirius.com) for the MAME project
 *
 * History:
 * 990314 HJB
 * The disassembler knows about valid opcodes for M6800/1/2/3/8 and HD63701.
 * 990302 HJB
 * Changed the string array into a table of opcode names (or tokens) and
 * argument types. This second try should give somewhat better results.
 * Named the undocumented HD63701YO opcodes $12 and $13 'asx1' and 'asx2',
 * since 'add contents of stack to x register' is what they do.
 *
 */

#include "emu.h"
#include "debugger.h"
#include "m6800.h"

enum addr_mode {
	inh,	/* inherent */
	rel,	/* relative */
	imb,	/* immediate (byte) */
	imw,	/* immediate (word) */
	dir,	/* direct address */
	imd,	/* HD63701YO: immediate, direct address */
	ext,	/* extended address */
	idx,	/* x + byte offset */
	imx,	/* HD63701YO: immediate, x + byte offset */
	sx1 	/* HD63701YO: undocumented opcodes: byte from (s+1) */
};

enum op_names {
	aba=0,	abx,	adca,	adcb,	adda,	addb,	addd,	aim,
	anda,	andb,	asl,	asla,	aslb,	asld,	asr,	asra,
	asrb,	bcc,	bcs,	beq,	bge,	bgt,	bhi,	bita,
	bitb,	ble,	bls,	blt,	bmi,	bne,	bpl,	bra,
	brn,	bsr,	bvc,	bvs,	cba,	clc,	cli,	clr,
	clra,	clrb,	clv,	cmpa,	cmpb,	cmpx,	com,	coma,
	comb,	daa,	dec,	deca,	decb,	des,	dex,	eim,
	eora,	eorb,	ill,	inc,	inca,	incb,	ins,	inx,
	jmp,	jsr,	lda,	ldb,	ldd,	lds,	ldx,	lsr,
	lsra,	lsrb,	lsrd,	mul,	neg,	nega,	negb,	nop,
	oim,	ora,	orb,	psha,	pshb,	pshx,	pula,	pulb,
	pulx,	rol,	rola,	rolb,	ror,	rora,	rorb,	rti,
	rts,	sba,	sbca,	sbcb,	sec,	sev,	sta,	stb,
	_std,	sei,	sts,	stx,	suba,	subb,	subd,	swi,
	wai,	tab,	tap,	tba,	tim,	tpa,	tst,	tsta,
	tstb,	tsx,	txs,	asx1,	asx2,	xgdx,	addx,	adcx
};

static const char *const op_name_str[] = {
	"aba",   "abx",   "adca",  "adcb",  "adda",  "addb",  "addd",  "aim",
	"anda",  "andb",  "asl",   "asla",  "aslb",  "asld",  "asr",   "asra",
	"asrb",  "bcc",   "bcs",   "beq",   "bge",   "bgt",   "bhi",   "bita",
	"bitb",  "ble",   "bls",   "blt",   "bmi",   "bne",   "bpl",   "bra",
	"brn",   "bsr",   "bvc",   "bvs",   "cba",   "clc",   "cli",   "clr",
	"clra",  "clrb",  "clv",   "cmpa",  "cmpb",  "cmpx",  "com",   "coma",
	"comb",  "daa",   "dec",   "deca",  "decb",  "des",   "dex",   "eim",
	"eora",  "eorb",  "illegal","inc",   "inca",  "incb",  "ins",   "inx",
	"jmp",   "jsr",   "lda",   "ldb",   "ldd",   "lds",   "ldx",   "lsr",
	"lsra",  "lsrb",  "lsrd",  "mul",   "neg",   "nega",  "negb",  "nop",
	"oim",   "ora",   "orb",   "psha",  "pshb",  "pshx",  "pula",  "pulb",
	"pulx",  "rol",   "rola",  "rolb",  "ror",   "rora",  "rorb",  "rti",
	"rts",   "sba",   "sbca",  "sbcb",  "sec",   "sev",   "sta",   "stb",
	"std",   "sei",   "sts",   "stx",   "suba",  "subb",  "subd",  "swi",
	"wai",   "tab",   "tap",   "tba",   "tim",   "tpa",   "tst",   "tsta",
	"tstb",  "tsx",   "txs",   "asx1",  "asx2",  "xgdx",  "addx",  "adcx"
};

/*
 * This table defines the opcodes:
 * byte meaning
 * 0    token (menmonic)
 * 1    addressing mode
 * 2    invalid opcode for 1:6800/6802/6808, 2:6801/6803, 4:HD63701
 */

static const UINT8 table[0x102][3] = {
	{ill, inh,7},{nop, inh,0},{ill, inh,7},{ill, inh,7},/* 00 */
	{lsrd,inh,1},{asld,inh,1},{tap, inh,0},{tpa, inh,0},
	{inx, inh,0},{dex, inh,0},{clv, inh,0},{sev, inh,0},
	{clc, inh,0},{sec, inh,0},{cli, inh,0},{sei, inh,0},
	{sba, inh,0},{cba, inh,0},{asx1,sx1,0},{asx2,sx1,0},/* 10 */
	{ill, inh,7},{ill, inh,7},{tab, inh,0},{tba, inh,0},
	{xgdx,inh,3},{daa, inh,0},{ill, inh,7},{aba, inh,0},
	{ill, inh,7},{ill, inh,7},{ill, inh,7},{ill, inh,7},
	{bra, rel,0},{brn, rel,0},{bhi, rel,0},{bls, rel,0},/* 20 */
	{bcc, rel,0},{bcs, rel,0},{bne, rel,0},{beq, rel,0},
	{bvc, rel,0},{bvs, rel,0},{bpl, rel,0},{bmi, rel,0},
	{bge, rel,0},{blt, rel,0},{bgt, rel,0},{ble, rel,0},
	{tsx, inh,0},{ins, inh,0},{pula,inh,0},{pulb,inh,0},/* 30 */
	{des, inh,0},{txs, inh,0},{psha,inh,0},{pshb,inh,0},
	{pulx,inh,1},{rts, inh,0},{abx, inh,1},{rti, inh,0},
	{pshx,inh,1},{mul, inh,1},{wai, inh,0},{swi, inh,0},
	{nega,inh,0},{ill, inh,7},{ill, inh,7},{coma,inh,0},/* 40 */
	{lsra,inh,0},{ill, inh,7},{rora,inh,0},{asra,inh,0},
	{asla,inh,0},{rola,inh,0},{deca,inh,0},{ill, inh,7},
	{inca,inh,0},{tsta,inh,0},{ill, inh,7},{clra,inh,0},
	{negb,inh,0},{ill, inh,7},{ill, inh,7},{comb,inh,0},/* 50 */
	{lsrb,inh,0},{ill, inh,7},{rorb,inh,0},{asrb,inh,0},
	{aslb,inh,0},{rolb,inh,0},{decb,inh,0},{ill, inh,7},
	{incb,inh,0},{tstb,inh,0},{ill, inh,7},{clrb,inh,0},
	{neg, idx,0},{aim, imx,3},{oim, imx,3},{com, idx,0},/* 60 */
	{lsr, idx,0},{eim, imx,3},{ror, idx,0},{asr, idx,0},
	{asl, idx,0},{rol, idx,0},{dec, idx,0},{tim, imx,3},
	{inc, idx,0},{tst, idx,0},{jmp, idx,0},{clr, idx,0},
	{neg, ext,0},{aim, imd,3},{oim, imd,3},{com, ext,0},/* 70 */
	{lsr, ext,0},{eim, imd,3},{ror, ext,0},{asr, ext,0},
	{asl, ext,0},{rol, ext,0},{dec, ext,0},{tim, imd,3},
	{inc, ext,0},{tst, ext,0},{jmp, ext,0},{clr, ext,0},
	{suba,imb,0},{cmpa,imb,0},{sbca,imb,0},{subd,imw,1},/* 80 */
	{anda,imb,0},{bita,imb,0},{lda, imb,0},{sta, imb,0},
	{eora,imb,0},{adca,imb,0},{ora, imb,0},{adda,imb,0},
	{cmpx,imw,0},{bsr, rel,0},{lds, imw,0},{sts, imw,0},
	{suba,dir,0},{cmpa,dir,0},{sbca,dir,0},{subd,dir,1},/* 90 */
	{anda,dir,0},{bita,dir,0},{lda, dir,0},{sta, dir,0},
	{eora,dir,0},{adca,dir,0},{ora, dir,0},{adda,dir,0},
	{cmpx,dir,0},{jsr, dir,0},{lds, dir,0},{sts, dir,0},
	{suba,idx,0},{cmpa,idx,0},{sbca,idx,0},{subd,idx,1},/* a0 */
	{anda,idx,0},{bita,idx,0},{lda, idx,0},{sta, idx,0},
	{eora,idx,0},{adca,idx,0},{ora, idx,0},{adda,idx,0},
	{cmpx,idx,0},{jsr, idx,0},{lds, idx,0},{sts, idx,0},
	{suba,ext,0},{cmpa,ext,0},{sbca,ext,0},{subd,ext,1},/* b0 */
	{anda,ext,0},{bita,ext,0},{lda, ext,0},{sta, ext,0},
	{eora,ext,0},{adca,ext,0},{ora, ext,0},{adda,ext,0},
	{cmpx,ext,0},{jsr, ext,0},{lds, ext,0},{sts, ext,0},
	{subb,imb,0},{cmpb,imb,0},{sbcb,imb,0},{addd,imw,1},/* c0 */
	{andb,imb,0},{bitb,imb,0},{ldb, imb,0},{stb, imb,0},
	{eorb,imb,0},{adcb,imb,0},{orb, imb,0},{addb,imb,0},
	{ldd, imw,1},{_std,imw,1},{ldx, imw,0},{stx, imw,0},
	{subb,dir,0},{cmpb,dir,0},{sbcb,dir,0},{addd,dir,1},/* d0 */
	{andb,dir,0},{bitb,dir,0},{ldb, dir,0},{stb, dir,0},
	{eorb,dir,0},{adcb,dir,0},{orb, dir,0},{addb,dir,0},
	{ldd, dir,1},{_std,dir,1},{ldx, dir,0},{stx, dir,0},
	{subb,idx,0},{cmpb,idx,0},{sbcb,idx,0},{addd,idx,1},/* e0 */
	{andb,idx,0},{bitb,idx,0},{ldb, idx,0},{stb, idx,0},
	{eorb,idx,0},{adcb,idx,0},{orb, idx,0},{addb,idx,0},
	{ldd, idx,1},{_std,idx,1},{ldx, idx,0},{stx, idx,0},
	{subb,ext,0},{cmpb,ext,0},{sbcb,ext,0},{addd,ext,1},/* f0 */
	{andb,ext,0},{bitb,ext,0},{ldb, ext,0},{stb, ext,0},
	{eorb,ext,0},{adcb,ext,0},{orb, ext,0},{addb,ext,0},
	{ldd, ext,1},{_std,ext,1},{ldx, ext,0},{stx, ext,0},

	/* extra instruction $fc for NSC-8105 */
	{addx,ext,0},
	/* extra instruction $ec for NSC-8105 */
	{adcx,imb,0}
};

/* some macros to keep things short */
#define OP      oprom[0]
#define ARG1    opram[1]
#define ARG2    opram[2]
#define ARGW	(opram[1]<<8) + opram[2]

static unsigned Dasm680x (int subtype, char *buf, unsigned pc, const UINT8 *oprom, const UINT8 *opram)
{
	UINT32 flags = 0;
	int invalid_mask;
	int code = OP;
	UINT8 opcode, args, invalid;

	switch( subtype )
	{
		case 6800: case 6802: case 6808: case 8105:
			invalid_mask = 1;
			break;
		case 6801: case 6803:
			invalid_mask = 2;
			break;
		default:
			invalid_mask = 4;
	}

	/* NSC-8105 is a special case */
	if (subtype == 8105)
	{
		/* swap bits */
		code = (code & 0x3c) | ((code & 0x41) << 1) | ((code & 0x82) >> 1);

		/* and check for extra instruction */
		if (code == 0xfc)  code = 0x0100;
		if (code == 0xec)  code = 0x0101;
	}

	opcode = table[code][0];
	args = table[code][1];
	invalid = table[code][2];

	if (opcode == bsr || opcode == jsr)
		flags = DASMFLAG_STEP_OVER;
	else if (opcode == rti || opcode == rts)
		flags = DASMFLAG_STEP_OUT;

	if ( invalid & invalid_mask )	/* invalid for this cpu type ? */
	{
		strcpy(buf, "illegal");
		return 1 | flags | DASMFLAG_SUPPORTED;
	}

	buf += sprintf(buf, "%-5s", op_name_str[opcode]);

	switch( args )
	{
		case rel:  /* relative */
			sprintf (buf, "$%04X", pc + (INT8)ARG1 + 2);
			return 2 | flags | DASMFLAG_SUPPORTED;
		case imb:  /* immediate (byte) */
			sprintf (buf, "#$%02X", ARG1);
			return 2 | flags | DASMFLAG_SUPPORTED;
		case imw:  /* immediate (word) */
			sprintf (buf, "#$%04X", ARGW);
			return 3 | flags | DASMFLAG_SUPPORTED;
		case idx:  /* indexed + byte offset */
			sprintf (buf, "(x+$%02X)", ARG1 );
            return 2 | flags | DASMFLAG_SUPPORTED;
		case imx:  /* immediate, indexed + byte offset */
			sprintf (buf, "#$%02X,(x+$%02x)", ARG1, ARG2 );
			return 3 | flags | DASMFLAG_SUPPORTED;
		case dir:  /* direct address */
			sprintf (buf, "$%02X", ARG1 );
			return 2 | flags | DASMFLAG_SUPPORTED;
		case imd:  /* immediate, direct address */
			sprintf (buf, "#$%02X,$%02X", ARG1, ARG2);
            return 3 | flags | DASMFLAG_SUPPORTED;
		case ext:  /* extended address */
			sprintf (buf, "$%04X", ARGW);
			return 3 | flags | DASMFLAG_SUPPORTED;
		case sx1:  /* byte from address (s + 1) */
            sprintf (buf, "(s+1)");
            return 1 | flags | DASMFLAG_SUPPORTED;
        default:
			return 1 | flags | DASMFLAG_SUPPORTED;
	}
}

CPU_DISASSEMBLE( m6800 )
{
	return Dasm680x(6800,buffer,pc,oprom,opram);
}

CPU_DISASSEMBLE( m6801 )
{
	return Dasm680x(6801,buffer,pc,oprom,opram);
}

CPU_DISASSEMBLE( m6802 )
{
	return Dasm680x(6802,buffer,pc,oprom,opram);
}

CPU_DISASSEMBLE( m6803 )
{
	return Dasm680x(6803,buffer,pc,oprom,opram);
}

CPU_DISASSEMBLE( m6808 )
{
	return Dasm680x(6808,buffer,pc,oprom,opram);
}

CPU_DISASSEMBLE( hd6301 )
{
	return Dasm680x(6301,buffer,pc,oprom,opram);
}

CPU_DISASSEMBLE( hd63701 )
{
	return Dasm680x(63701,buffer,pc,oprom,opram);
}

CPU_DISASSEMBLE( nsc8105 )
{
	return Dasm680x(8105,buffer,pc,oprom,opram);
}
