/* Copyright (c) 1990 by Sozobon, Limited.  Authors: Johann Ruegg, Don Dugger
 *
 * Permission is granted to anyone to use this software for any purpose
 * on any computer system, and to redistribute it freely, with the
 * following restrictions:
 * 1) No charge may be made other than reasonable charges for reproduction.
 * 2) Modified versions must be clearly marked as such.
 * 3) The authors are not responsible for any harmful consequences
 *    of using this software, even if they result from defects in it.
 *
 *	dis3.c
 */
/*
 * Modifications:
 *    - in case of ailure to disassemble print operand as a constant
 *      in 'dc.w' instruction
 *    - make printout to align in a nicer way
 *
 *    Michal Jaegermann, November 1990
 */

#include "adb.h"

extern long     dot;
extern int      dotoff;
extern int      opcode_pos;	/* this value depends on resolution */

#define DOT	(dot+dotoff)

int             cursz;

nextw ()
{
    return getwd ();
}

long
nextl ()
{
    unsigned int    i[2];

    i[0] = getwd ();
    i[1] = getwd ();
    return ((long) i[0]) << 16 | i[1];
}

int             op0 (), op1 (), op2 (), op3 (), op4 (), op5 (), op6 (), op7 ();
int             op8 (), op9 (), opa (), opb (), opc (), opd (), ope (), opf ();

int             (*funhi[]) () = { op0, op1, op2, op3, op4, op5, op6, op7,
				  op8, op9, opa, opb, opc, opd, ope, opf
				};

puti ()
{
    unsigned int    op;
    int             ar_pos;

    align (opcode_pos);

    op = nextw ();

    if (!(*funhi[op >> 12]) (op, ar_pos = opcode_pos + ALIGN_OP)) {
	prt ("dc.w");
	align (ar_pos);
	prtn ((unsigned long) op, 0);
    }
}


#define M_IMM	1
#define M_PX	2
#define M_POFF	4
#define M_ABSL	010
#define M_ABSW	020
#define M_AX	040
#define M_AOFF	0100
#define M_ADEC	0200
#define M_AINC	0400
#define M_ATA	01000
#define M_AREG	02000
#define M_DREG	04000

valid (mode, reg, mask)
{
    if (mode == 7)
	switch (reg) {
	case 0:
	    return mask & M_ABSW;
	case 1:
	    return mask & M_ABSL;
	case 2:
	    return mask & M_POFF;
	case 3:
	    return mask & M_PX;
	case 4:
	    return mask & M_IMM;
	default:
	    return 0;
	}
    else
	switch (mode) {
	case 0:
	    return mask & M_DREG;
	case 1:
	    return mask & M_AREG;
	case 2:
	    return mask & M_ATA;
	case 3:
	    return mask & M_AINC;
	case 4:
	    return mask & M_ADEC;
	case 5:
	    return mask & M_AOFF;
	case 6:
	    return mask & M_AX;
	}
}

ix_str (n)
{
    int             op, r;
    char            c;
    char           *fmt;

    op = nextw ();
    r = (op >> 12) & 7;
    c = (op & 0x800) ? 'l' : 'w';
    if (n >= 8) {
	if (op < 0)
	    fmt = "%i(pc,%a.%c)";
	else
	    fmt = "%i(pc,%d.%c)";
	prtf (fmt, (char) op, r, c);
    }
    else {
	if (op < 0)
	    fmt = "%i(%a,%a.%c)";
	else
	    fmt = "%i(%a,%d.%c)";
	prtf (fmt, (char) op, n, r, c);
    }
}

modepr (mode, reg)
{
    char           *p;

    switch (mode) {
    case 0:
	prtf ("%d", reg);
	break;
    case 1:
	prtf ("%a", reg);
	break;
    case 2:
	prtf ("(%a)", reg);
	break;
    case 3:
	prtf ("(%a)+", reg);
	break;
    case 4:
	prtf ("-(%a)", reg);
	break;
    case 5:
	prtf ("%i(%a)", nextw (), reg);
	break;
    case 6:
	ix_str (reg);
	break;
    case 7:
	switch (reg) {
	case 0:
	    prtf ("%i", nextw ());
	    break;
	case 1:
	    longval ();
	    break;
	case 2:
	    prtf ("%i(pc)", nextw ());
	    break;
	case 3:
	    ix_str (8);
	    break;
	case 4:
	    switch (cursz) {
	    case 0:
	    case 1:
		prtf ("#%i", nextw ());
		break;
	    case 2:
		putchr ('#');
		longval ();
		break;
	    }
	    break;
	}
    }
}

longval ()
{
    long            l;
    struct sym     *sp;

    prtf ("%A", nextl ());
}

char            szchr[] = {'b', 'w', 'l'};

struct optbl {
    char            sel[6];
    char           *name;
    int             allow;
    char            arg[4];
    char            sz;
};

struct optbl    t0[] = {
			{"0s", "or", 05770, "ie"},
			{"00", "or", 1, "ic"},
			{"01", "or", 1, "is", 1},
			{"*4", "btst", 05777, "De"},
			{"*5", "bchg", 05770, "De"},
			{"*6", "bclr", 05770, "De"},
			{"*7", "bset", 05770, "De"},
			{"*4", "movep.w", 02000, "oD"},
			{"*5", "movep.l", 02000, "oD"},
			{"*6", "movep.w", 02000, "Do"},
			{"*7", "movep.l", 02000, "Do"},
			{"1s", "and", 05770, "ie"},
			{"10", "and", 1, "ic"},
			{"11", "and", 1, "is", 1},
			{"2s", "sub", 05770, "ie"},
			{"3s", "add", 05770, "ie"},
			{"40", "btst", 05776, "ie"},
			{"41", "bchg", 05770, "ie"},
			{"42", "bclr", 05770, "ie"},
			{"43", "bset", 05770, "ie"},
			{"5s", "eor", 05770, "ie"},
			{"50", "eor", 1, "ic"},
			{"51", "eor", 1, "is", 1},
			{"6s", "cmp", 05770, "ie"},
			{0, 0, 0, 0}
};

op0 (op, pos)
    unsigned int    op;
    int             pos;
{
    return tblop (op, t0, pos);
}

match (c, val)
{
    switch (c) {
    case '*':
	return 1;
    case 's':
	return val <= 2;
    case 'S':
	return val >= 4 && val <= 6;
    case 'z':
	return val >= 1 && val <= 2;
    case 'Z':
	return val == 3 || val == 7;
    default:
	return val == (c - '0');
    }
}

op1 (op, pos)
    unsigned int    op;
    int             pos;
{
    int             sm, sr, dm, dr;

    sm = (op >> 3) & 7;
    sr = op & 7;
    dm = (op >> 6) & 7;
    dr = (op >> 9) & 7;
    cursz = 0;
    if (valid (sm, sr, 07777) && valid (dm, dr, 05770)) {
	prt ("move.b");
	align (pos);
	modepr (sm, sr);
	putchr (',');
	modepr (dm, dr);
	return 1;
    }
    return 0;
}

op2 (op, pos)
    unsigned int    op;
    int             pos;
{
    int             sm, sr, dm, dr;

    sm = (op >> 3) & 7;
    sr = op & 7;
    dm = (op >> 6) & 7;
    dr = (op >> 9) & 7;
    cursz = 2;
    if (valid (sm, sr, 07777) && valid (dm, dr, 07770)) {
	prt ("move.l");
	align (pos);
	modepr (sm, sr);
	putchr (',');
	modepr (dm, dr);
	return 1;
    }
    return 0;
}

op3 (op, pos)
    unsigned int    op;
    int             pos;
{
    int             sm, sr, dm, dr;

    sm = (op >> 3) & 7;
    sr = op & 7;
    dm = (op >> 6) & 7;
    dr = (op >> 9) & 7;
    cursz = 1;
    if (valid (sm, sr, 07777) && valid (dm, dr, 07770)) {
	prt ("move.w");
	align (pos);
	modepr (sm, sr);
	putchr (',');
	modepr (dm, dr);
	return 1;
    }
    return 0;
}

struct optbl    t4[] = {
			{"0s", "negx", 05770, "e"},
			{"03", "move.w", 05770, "se"},
			{"*6", "chk", 05777, "eD"},
			{"*7", "lea", 01176, "eA"},
			{"1s", "clr", 05770, "e"},
			{"2s", "neg", 05770, "e"},
			{"23", "move.b", 05777, "ec"},
			{"3s", "not", 05770, "e"},
			{"33", "move.w", 05777, "es", 1},
			{"40", "nbcd", 05770, "e"},
			{"41", "swap", 04000, "d"},
			{"41", "pea", 01176, "e"},
			{"42", "ext.w", 04000, "d"},
			{"42", "movem.w", 01170, "le"},
			{"42", "movem.w", 00200, "Le"},
			{"43", "movem.l", 01170, "le"},
			{"43", "movem.l", 00200, "Le"},
			{"43", "ext.l", 04000, "d"},
			{"5s", "tst", 05770, "e"},
			{"53", "tas", 05770, "e"},
			{"53", "illegal", 0001, ""},
			{"71", "trap", 06000, "t"},
			{"71", "link", 01000, "ai", 1},
			{"71", "unlk", 00400, "a"},
			{"71", "move", 00200, "au"},
			{"71", "move", 00100, "ua"},
			{"7160", "reset", 0, ""},
			{"7161", "nop", 0, ""},
			{"7162", "stop", 0, ""},
			{"7163", "rte", 0, ""},
			{"7165", "rts", 0, ""},
			{"7166", "trapv", 0, ""},
			{"7167", "rtr", 0, ""},
			{"72", "jsr", 01176, "e"},
			{"73", "jmp", 01176, "e"},
			{0, 0, 0, 0}
};

op4 (op, pos)
    unsigned int    op;
    int             pos;
{
    int             mode, reg, list;

    if ((op & 07600) == 06200) {
	reg = op & 7;
	mode = (op >> 3) & 7;
	if (valid (mode, reg, 01576)) {
	    prtf ("movem.%c", op & 0100 ? 'l' : 'w');
	    align (pos);
	    list = nextw ();
	    modepr (mode, reg);
	    putchr (',');
	    rlist (list);
	    return 1;
	}
	else
	    return 0;
    }
    return tblop (op, t4, pos);
}

tblop (op, tp, pos)
    unsigned        op;
    register struct optbl *tp;
    int             pos;
{
    int             mode, reg;
    int             hi, lo;

    reg = op & 7;
    mode = (op >> 3) & 7;
    lo = (op >> 6) & 7;
    hi = (op >> 9) & 7;

    for (; tp->name; tp++)
	if (match (tp->sel[0], hi) &&
	    match (tp->sel[1], lo) &&
	    (tp->allow == 0 || valid (mode, reg, tp->allow)) &&
	    (tp->sel[2] == 0 || match (tp->sel[2], mode)) &&
	    (tp->sel[3] == 0 || match (tp->sel[3], reg))) {

	    prt (tp->name);

	    switch (tp->sel[1]) {
	    case 's':
		cursz = lo;
		break;
	    case 'S':
		cursz = lo - 4;
		break;
	    case 'z':
		cursz = (lo == 1) ? 1 : 2;
		break;
	    case 'Z':
		cursz = (lo == 3) ? 1 : 2;
		break;
	    default:
		cursz = tp->sz;
		goto noszpr;
	    }

	    prtf (".%c", szchr[cursz]);
    noszpr:
	    align (pos);

	    if (tp->arg[0]) {
		puta (tp->arg[0], op);
		if (tp->arg[1]) {
		    putchr (',');
		    puta (tp->arg[1], op);
		}
	    }
	    return 1;
	}
    return 0;
}

puta (c, op)
{
    int             reg, mode, hi;

    reg = op & 7;
    mode = (op >> 3) & 7;
    hi = (op >> 9) & 7;

    switch (c) {
    case 'i':
	modepr (7, 4);
	break;
    case 'e':
	modepr (mode, reg);
	break;
    case 'c':
	prt ("ccr");
	break;
    case 's':
	prt ("sr");
	break;
    case 'D':
	modepr (0, hi);
	break;
    case 'd':
	modepr (0, reg);
	break;
    case 'A':
	modepr (1, hi);
	break;
    case 'a':
	modepr (1, reg);
	break;
    case 'o':
	modepr (5, reg);
	break;
    case 'm':
	modepr (4, reg);
	break;
    case 'M':
	modepr (4, hi);
	break;
    case 'p':
	modepr (3, reg);
	break;
    case 'P':
	modepr (3, hi);
	break;
    case 'l':
	rlist (nextw ());
	break;
    case 'L':
	blist (nextw ());
	break;
    case 'u':
	prt ("usp");
	break;
    case 't':
	prtf ("#%i", op & 0xf);
	break;
    case 'k':
	prtf ("#%i", hi ? hi : 8);
	break;
    }
}

rlist (x)
{
    int             as, ds;

    ds = x & 0xff;
    as = (x >> 8) & 0xff;
    putchr ('[');
    if (ds) {
	listc ('d', ds);
	if (as) {
	    putchr (',');
	    listc ('a', as);
	}
    }
    else if (as)
	listc ('a', as);
    putchr (']');
}

listc (c, x)
    char            c;
{
    int             i;

    for (i = 0; i < 8;)
	if (x & (1 << i))
	    i += chunk (c, x, i);
	else
	    i++;
}

chunk (c, x, i)
    char            c;
{
    int             j;

    putchr (c);
    j = cnt1s (x >> i);
    if (j == 1) {
	putchr ('0' + i);
	return j;
    }
    else {
	putchr ('0' + i);
	putchr ('-');
	putchr ('0' + i + (j - 1));
	return j;
    }
}

cnt1s (x)
{
    int             i;

    for (i = 0; i < 9; i++)
	if ((x & (1 << i)) == 0)
	    return i;
}

blist (x)
{
    int             y;
    int             i;
    unsigned        uw = 0x8000;

    y = 0;
    for (i = 0; i < 16; i++)
	if (x & (1 << i))
	    y |= (uw >> i);
    rlist (y);
}

char           *bnm[] = {"t", "f", "hi", "ls",
			 "cc", "cs", "ne", "eq",
			 "vc", "vs", "pl", "mi",
			 "ge", "lt", "gt", "le"};

op5 (op, pos)
    unsigned int    op;
    int             pos;
{
    int             cond, mode, reg;
    int             sz, k;
    long            svdot = DOT;

    sz = (op >> 6) & 3;
    reg = op & 7;
    mode = (op >> 3) & 7;

    if (sz == 3) {
	cond = (op >> 8) & 0xf;
	if (mode == 1) {
	    prtf ("db%s", bnm[cond]);
	    align (pos);
	    prtf ("%d,%A", reg, svdot + nextw ());
	    return 1;
	}
	else if (valid (mode, reg, 05770)) {
	    prtf ("s%s", bnm[cond]);
	    align (pos);
	    modepr (mode, reg);
	    return 1;
	}
    }
    else {
	k = (op >> 9) & 7;
	if (k == 0)
	    k = 8;
	if (valid (mode, reg, sz ? 07770 : 05770)) {
	    prtf ((op & 0x100) ? "subq.%c" : "addq.%c", szchr[sz]);
	    align (pos);
	    prtf ("#%i,", k);
	    modepr (mode, reg);
	    return 1;
	}
    }
    return 0;
}

op6 (op, pos)
    unsigned int    op;
    int             pos;
{
    int             cond, k;
    long            svdot = DOT;

    cond = (op >> 8) & 0xf;
    k = (char) op;
    if (k == 0)
	svdot += nextw ();
    else
	svdot += k;
    if (cond < 2)
	prt (cond ? "bsr" : "bra");
    else
	prtf ("b%s", bnm[cond]);
    align (pos);
    prtf ("%A", svdot);
    return 1;
}

op7 (op, pos)
    unsigned int    op;
    int             pos;
{
    register int    val, reg, k;

    if (val = (0 == (op & 0x100))) {
	k = (char) op;
	reg = (op >> 9) & 7;
	prt ("moveq");
	align (pos);
	prtf ("#%i,%d", k, reg);
    }
    return val;
}

struct optbl    t8[] = {
			{"*s", "or", 05777, "eD"},
			{"*S", "or", 01770, "De"},
			{"*3", "divu", 05777, "eD"},
			{"*7", "divs", 05777, "eD"},
			{"*4", "sbcd", 04000, "dD"},
			{"*4", "sbcd", 02000, "mM"},
			{0, 0, 0, 0}
};

op8 (op, pos)
    unsigned int    op;
    int             pos;
{
    return tblop (op, t8, pos);
}

struct optbl    t9[] = {
			{"*0", "sub.b", 05777, "eD"},
			{"*z", "sub", 07777, "eD"},
			{"*S", "sub", 01770, "De"},
			{"*Z", "sub", 07777, "eA"},
			{"*S", "subx", 04000, "dD"},
			{"*S", "subx", 02000, "mM"},
			{0, 0, 0, 0}
};

op9 (op, pos)
    unsigned int    op;
    int             pos;
{
    return tblop (op, t9, pos);
}

opa (op, pos)
    unsigned int    op;
    int             pos;
{
    return 0;
}

struct optbl    tb[] = {
			{"*0", "cmp.b", 05777, "eD"},
			{"*z", "cmp", 07777, "eD"},
			{"*Z", "cmp", 07777, "eA"},
			{"*S", "eor", 05770, "De"},
			{"*S", "cmpm", 02000, "pP"},
			{0, 0, 0, 0}
};

opb (op, pos)
    unsigned int    op;
    int             pos;
{
    return tblop (op, tb, pos);
}

struct optbl    tc[] = {
			{"*s", "and", 05777, "eD"},
			{"*S", "and", 01770, "De"},
			{"*3", "mulu", 05777, "eD"},
			{"*7", "muls", 05777, "eD"},
			{"*4", "abcd", 04000, "dD"},
			{"*4", "abcd", 02000, "mM"},
			{"*5", "exg", 04000, "dD"},
			{"*5", "exg", 02000, "aA"},
			{"*6", "exg", 02000, "aD"},
			{0, 0, 0, 0}
};

opc (op, pos)
    unsigned        op;
    int             pos;
{
    return tblop (op, tc, pos);
}

struct optbl    td[] = {
			{"*0", "add.b", 05777, "eD"},
			{"*z", "add", 07777, "eD"},
			{"*S", "add", 01770, "De"},
			{"*Z", "add", 07777, "eA"},
			{"*S", "addx", 04000, "dD"},
			{"*S", "addx", 02000, "mM"},
			{0, 0, 0, 0}
};

opd (op, pos)
    unsigned int    op;
    int             pos;
{
    return tblop (op, td, pos);
}

char           *shiftnm[] = {"as", "ls", "rox", "ro"};

ope (op, pos)
    unsigned int    op;
    int             pos;
{
    int             sz, c_r;
    int             mode, reg;

    sz = (op >> 6) & 3;
    if (sz == 3) {
	mode = (op >> 3) & 7;
	reg = op & 7;
	if (valid (mode, reg, 01770)) {
	    prtf ("%s%c.w,", shiftnm[(op >> 9) & 3],
		  (op & 0x100) ? 'l' : 'r');
	    align (pos);
	    prtf ("#1,");
	    modepr (mode, reg);
	    return 1;
	}
    }
    else {
	prtf ("%s%c.%c", shiftnm[(op >> 3) & 3],
	      (op & 0x100) ? 'l' : 'r', szchr[sz]);
	align (pos);
	c_r = (op >> 9) & 7;
	if (op & 040)
	    prtf ("%d", c_r);
	else
	    prtf ("#%i", c_r ? c_r : 8);
	prtf (",%d", op & 7);
	return 1;
    }
    return 0;
}

opf (op, pos)
    unsigned int    op;
    int             pos;
{
    return 0;
}
