root/sys/arch/m68k/m68k/db_disasm.c
/*      $NetBSD: db_disasm.c,v 1.47 2023/09/26 14:33:55 tsutsui Exp $   */

/*
 * Copyright (c) 1994 Christian E. Hopps
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Christian E. Hopps.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Notes:
 *
 * Much can be done with this format, with a lot of hacking even
 * a moto emul. could be built.  However things like symbol lookup
 * and reference are needed right away.
 *
 * the only functions that use the "get_xxx()" notation should be
 * ones that modify things in a dis_buffer_t besides the buffers.
 * (namely the used field)
 *
 * An attempt has been made to *always* increment dbuf->used++ immediately
 * after referencing a value beyond the current "short *" address.
 * this meant either only referencing the value once or placing it in
 * a local var.  If you play with this keep this style. Its very useful
 * in eliminating a very easy to make hard to find logic error.
 *
 * I broke style in 2 ways with one macro ``addchar()''
 * However it makes sense, consider that it is called *a lot* and
 * commonly with things like ","'s
 *
 * *dbuf->casm++ = ','; || ADDCHAR(dbuf,','); || addchar(dbuf,',');
 * I chose:
 *      addchar(',');
 *
 * If this is not enough to convince you, please load up you emacs or
 * vi and do a fancy regex-replace, and compare for yourself.
 * (The 2 rules of style I broke if you failed to notice are:
 *  1: lower case macro name 2: implicit reference to local var name.)
 *
 * (chopps - March 1, 1994)
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_disasm.c,v 1.47 2023/09/26 14:33:55 tsutsui Exp $");

#include <sys/param.h>
#ifdef _KERNEL
#include <sys/systm.h>
#endif

#include <machine/db_machdep.h>

#include <ddb/db_interface.h>
#include <ddb/db_output.h>
#include <ddb/db_sym.h>
#include <m68k/m68k/db_disasm.h>

static void     get_modregstr(dis_buffer_t *, int, int, int, int);
static void     get_modregstr_moto(dis_buffer_t *, int, int, int, int);
static void     get_modregstr_mit(dis_buffer_t *, int, int, int, int);
#if 0
static u_long   get_areg_val(int reg);
#endif
static void     get_immed(dis_buffer_t *, int);
static void     get_fpustdGEN(dis_buffer_t *, u_short, const char *);
static void     addstr(dis_buffer_t *, const char *s);
static void     prints(dis_buffer_t *, int, int);
static void     printu(dis_buffer_t *, u_int, int);
static void     prints_wb(dis_buffer_t *, int, int, int);
static void     printu_wb(dis_buffer_t *, u_int, int, int);
static void     prints_bf(dis_buffer_t *, int, int, int);
static void     printu_bf(dis_buffer_t *, u_int, int, int);
static void     iaddstr(dis_buffer_t *, const char *s);
#if 0
static void     iprints(dis_buffer_t *, int, int);
#endif
static void     iprintu(dis_buffer_t *, u_int, int);
#if 0
static void     iprints_wb(dis_buffer_t *, int, int, int);
#endif
static void     iprintu_wb(dis_buffer_t *, u_int, int, int);
static void     make_cond(dis_buffer_t *, int , const char *);
static void     print_fcond(dis_buffer_t *, char);
static void     print_mcond(dis_buffer_t *, char);
static void     print_disp(dis_buffer_t *, int, int, int);
static void     print_addr(dis_buffer_t *, u_long);
static void     print_reglist(dis_buffer_t *, int, u_short);
static void     print_freglist(dis_buffer_t *, int, u_short, int);
static void     print_fcode(dis_buffer_t *, u_short);

/* groups */
static void     opcode_bitmanip(dis_buffer_t *, u_short);
static void     opcode_move(dis_buffer_t *, u_short);
static void     opcode_misc(dis_buffer_t *, u_short);
static void     opcode_branch(dis_buffer_t *, u_short);
static void     opcode_coproc(dis_buffer_t *, u_short);
static void     opcode_0101(dis_buffer_t *, u_short);
static void     opcode_1000(dis_buffer_t *, u_short);
static void     opcode_addsub(dis_buffer_t *, u_short);
static void     opcode_1010(dis_buffer_t *, u_short);
static void     opcode_1011(dis_buffer_t *, u_short);
static void     opcode_1100(dis_buffer_t *, u_short);
static void     opcode_1110(dis_buffer_t *, u_short);
static void     opcode_fpu(dis_buffer_t *, u_short);
static void     opcode_mmu(dis_buffer_t *, u_short);
static void     opcode_mmu040(dis_buffer_t *, u_short);
static void     opcode_move16(dis_buffer_t *, u_short);

/* subs of groups */
static void     opcode_movec(dis_buffer_t *, u_short);
static void     opcode_divmul(dis_buffer_t *, u_short);
static void     opcode_movem(dis_buffer_t *, u_short);
static void     opcode_fmove_ext(dis_buffer_t *, u_short, u_short);
static void     opcode_pmove(dis_buffer_t *, u_short, u_short);
static void     opcode_pflush(dis_buffer_t *, u_short, u_short);

#define addchar(ch) (*dbuf->casm++ = ch)
#define iaddchar(ch) (*dbuf->cinfo++ = ch)

typedef void dis_func_t(dis_buffer_t *, u_short);

static dis_func_t *const opcode_map[16] = {
        opcode_bitmanip, opcode_move, opcode_move, opcode_move,
        opcode_misc, opcode_0101, opcode_branch, opcode_move,
        opcode_1000, opcode_addsub, opcode_1010, opcode_1011,
        opcode_1100, opcode_addsub, opcode_1110, opcode_coproc
};

static const char *const cc_table[16] = {
        "t", "f", "hi", "ls",
        "cc", "cs", "ne", "eq",
        "vc", "vs", "pl", "mi",
        "ge", "lt", "gt", "le"
};

static const char *const fpcc_table[32] = {
        "f", "eq", "ogt", "oge", "olt", "ole", "ogl", "or",
        "un", "ueq", "ugt", "uge", "ult", "ule", "ne", "t",
        "sf", "seq", "gt", "ge", "lt", "le", "gl", "gle",
        "ngle", "ngl", "nle", "nlt", "nge", "ngt", "sne", "st" };

static const char *const mmcc_table[16] = {
        "bs", "bc", "ls", "lc", "ss", "sc", "as", "sc",
        "ws", "wc", "is", "ic", "gs", "gc", "cs", "cc" };


static const char *const aregs[8] = {"a0","a1","a2","a3","a4","a5","a6","sp"};
static const char *const dregs[8] = {"d0","d1","d2","d3","d4","d5","d6","d7"};
static const char *const fpregs[8] = {
        "fp0","fp1","fp2","fp3","fp4","fp5","fp6","fp7" };
static const char *const fpcregs[3] = { "fpiar", "fpsr", "fpcr" };
#ifndef _KERNEL
static const char hexdigits[] = "0123456789abcdef";
#endif

/*
 * Disassemble instruction at location ``loc''.
 * Returns location of next instruction.
 */

static char asm_buffer[256];
static char info_buffer[256];

db_addr_t
db_disasm(db_addr_t loc, bool moto_syntax)
{
        u_short opc;
        dis_func_t *func;
        dis_buffer_t dbuf;

        dbuf.casm = dbuf.dasm = asm_buffer;
        dbuf.cinfo = dbuf.info = info_buffer;
        dbuf.used = 0;
        dbuf.val = (short *)loc;
        dbuf.mit = moto_syntax ? 0 : 1;

        dbuf.dasm[0] = 0;
        dbuf.info[0] = 0;

        opc = *dbuf.val;
        dbuf.used++;

        func = opcode_map[OPCODE_MAP(opc)];
        func(&dbuf, opc);

        db_printf("%s",asm_buffer);
        if (info_buffer[0])
                db_printf("\t[%s]\n",info_buffer);
        else
                db_printf("\n");
        return loc + sizeof(short) * dbuf.used;
}
/*
 * Bit manipulation/MOVEP/Immediate.
 */
static void
opcode_bitmanip(dis_buffer_t *dbuf, u_short opc)
{
        const char *tmp;
        u_short ext;
        int sz;

        tmp = NULL;

        switch (opc) {
        case ANDITOCCR_INST:
                tmp = "andib\t";
                break;
        case ANDIROSR_INST:
                tmp = "andiw\t";
                break;
        case EORITOCCR_INST:
                tmp = "eorib\t";
                break;
        case EORITOSR_INST:
                tmp = "eoriw\t";
                break;
        case ORITOCCR_INST:
                tmp = "orib\t";
                break;
        case ORITOSR_INST:
                tmp = "oriw\t";
                break;
        }
        if (tmp) {
                addstr(dbuf, tmp);
                if (ISBITSET(opc,6)) {
                        get_immed(dbuf, SIZE_WORD);
                        addstr(dbuf, ",sr");
                } else {
                        get_immed(dbuf, SIZE_BYTE);
                        addstr(dbuf, ",ccr");
                }
                return;
        }

        if (IS_INST(RTM,opc)) {
                addstr(dbuf, "rtm\t");
                if (ISBITSET(opc,3))
                        PRINT_AREG(dbuf, BITFIELD(opc,2,0));
                else
                        PRINT_DREG(dbuf, BITFIELD(opc,2,0));
                return;
        }

        if (IS_INST(MOVEP,opc)) {
                addstr(dbuf, "movp");
                if (ISBITSET(opc,6))
                        addchar('l');
                else
                        addchar('w');
                addchar('\t');
                if (ISBITSET(opc,7)) {
                        PRINT_DREG(dbuf, BITFIELD(opc, 11, 9));
                        addchar(',');
                }
                PRINT_AREG(dbuf, BITFIELD(opc, 2, 0));
                addchar('@');
                addchar('(');
                print_disp(dbuf, *(dbuf->val + 1), SIZE_WORD,
                    BITFIELD(opc, 2, 0));
                dbuf->used++;
                addchar(')');
                if (!ISBITSET(opc,7)) {
                        addchar(',');
                        PRINT_DREG(dbuf, BITFIELD(opc, 11, 9));
                }
                return;
        }

        switch (opc & BCHGD_MASK) {
        case BCHGD_INST:
                tmp = "bchg\t";
                break;
        case BCLRD_INST:
                tmp = "bclr\t";
                break;
        case BSETD_INST:
                tmp = "bset\t";
                break;
        case BTSTD_INST:
                tmp = "btst\t";
                break;
        }
        if (tmp) {
                addstr(dbuf, tmp);
                PRINT_DREG(dbuf, BITFIELD(opc,11,9));
                addchar(',');
                get_modregstr(dbuf,5,GETMOD_BEFORE,0,0);
                return;
        }

        switch (opc & BCHGS_MASK) {
        case BCHGS_INST:
                tmp = "bchg\t";
                break;
        case BCLRS_INST:
                tmp = "bclr\t";
                break;
        case BSETS_INST:
                tmp = "bset\t";
                break;
        case BTSTS_INST:
                tmp = "btst\t";
                break;
        }
        if (tmp) {
                addstr(dbuf, tmp);
                get_immed(dbuf, SIZE_BYTE);
                addchar(',');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 1);
                return;
        }

        if (IS_INST(CAS2,opc)) {
                u_short ext2;

                ext = *(dbuf->val + 1);
                ext2 = *(dbuf->val + 2);
                dbuf->used += 2;

                if (ISBITSET(opc,9))
                        addstr(dbuf, "cas2l\t");
                else
                        addstr(dbuf, "cas2w\t");

                PRINT_DREG(dbuf, BITFIELD(ext,2,0));
                addchar(':');
                PRINT_DREG(dbuf, BITFIELD(ext2,2,0));
                addchar(',');

                PRINT_DREG(dbuf, BITFIELD(ext,8,6));
                addchar(':');
                PRINT_DREG(dbuf, BITFIELD(ext2,8,6));
                addchar(',');

                if (ISBITSET(ext,15))
                        PRINT_AREG(dbuf, BITFIELD(ext,14,12));
                else
                        PRINT_DREG(dbuf, BITFIELD(ext,14,12));
                addchar('@');
                addchar(':');
                if (ISBITSET(ext2,15))
                        PRINT_AREG(dbuf, BITFIELD(ext2,14,12));
                else
                        PRINT_DREG(dbuf, BITFIELD(ext2,14,12));
                addchar('@');
                return;
        }

        switch (opc & CAS_MASK) {
        case CAS_INST:
                ext = *(dbuf->val + 1);
                dbuf->used++;

                addstr(dbuf,"cas");
                sz = BITFIELD(opc,10,9);
                if (sz == 0) {
                        sz = SIZE_BYTE;
                        addchar('b');
                } else if (sz == 1) {
                        sz = SIZE_WORD;
                        addchar('w');
                } else {
                        sz = SIZE_LONG;
                        addchar('l');
                }
                addchar('\t');
                PRINT_DREG(dbuf, BITFIELD(ext, 2, 0));
                addchar(',');
                PRINT_DREG(dbuf, BITFIELD(ext, 8, 6));
                addchar(',');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 1);
                return;
        case CHK2_INST:
        /* case CMP2_INST: */
                ext = *(dbuf->val + 1);
                dbuf->used++;

                if (ISBITSET(ext,11))
                        addstr(dbuf,"chk2");
                else
                        addstr(dbuf,"cmp2");

                sz = BITFIELD(opc,10,9);
                if (sz == 0) {
                        sz = SIZE_BYTE;
                        addchar('b');
                } else if (sz == 1) {
                        sz = SIZE_WORD;
                        addchar('w');
                } else {
                        sz = SIZE_LONG;
                        addchar('l');
                }
                addchar('\t');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 1);

                addchar(',');
                if(ISBITSET(ext,15))
                        PRINT_AREG(dbuf, BITFIELD(ext, 14, 12));
                else
                        PRINT_DREG(dbuf, BITFIELD(ext, 14, 12));
                return;
        }

        switch (ADDI_MASK & opc) {
        case MOVES_INST:
                addstr(dbuf, "movs");
                sz = BITFIELD(opc,7,6);
                if (sz == 0) {
                        addchar('b');
                        sz = SIZE_BYTE;
                } else if (sz == 1) {
                        addchar('w');
                        sz = SIZE_WORD;
                } else {
                        addchar ('l');
                        sz = SIZE_LONG;
                }
                addchar('\t');

                ext = *(dbuf->val + 1);
                dbuf->used++;

                if (ISBITSET(ext,11)) {
                        if (ISBITSET(ext,15))
                                PRINT_AREG(dbuf,BITFIELD(ext,14,12));
                        else
                                PRINT_DREG(dbuf,BITFIELD(ext,14,12));
                        addchar(',');
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 1);
                } else {
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 1);
                        addchar(',');
                        if (ISBITSET(ext,15))
                                PRINT_AREG(dbuf,BITFIELD(ext,14,12));
                        else
                                PRINT_DREG(dbuf,BITFIELD(ext,14,12));
                }
                return;
        case ADDI_INST:
                tmp = "addi";
                break;
        case ANDI_INST:
                tmp = "andi";
                break;
        case CMPI_INST:
                tmp = "cmpi";
                break;
        case EORI_INST:
                tmp = "eori";
                break;
        case ORI_INST:
                tmp = "ori";
                break;
        case SUBI_INST:
                tmp = "subi";
                break;
        }
        if (tmp) {
                addstr(dbuf, tmp);
                sz = BITFIELD(opc,7,6);
                switch (sz) {
                case 0:
                        addchar('b');
                        addchar('\t');
                        sz = SIZE_BYTE;
                        break;
                case 1:
                        addchar('w');
                        addchar('\t');
                        sz = SIZE_WORD;
                        break;
                case 2:
                        addchar ('l');
                        addchar('\t');
                        get_immed(dbuf,SIZE_LONG);
                        addchar(',');
                        get_modregstr(dbuf,5,GETMOD_BEFORE,SIZE_LONG,2);
                        return;
                }
                get_immed(dbuf,sz);
                addchar(',');
                get_modregstr(dbuf,5,GETMOD_BEFORE,sz,1);
                return;
        }
}

/*
 * move byte/word/long and q
 * 00xx (01==.b 10==.l 11==.w) and 0111(Q)
 */
static void
opcode_move(dis_buffer_t *dbuf, u_short opc)
{
        int sz, lused;

        sz = 0;
        switch (OPCODE_MAP(opc)) {
        case 0x1:               /* move.b */
                sz = SIZE_BYTE;
                break;
        case 0x3:               /* move.w */
                sz = SIZE_WORD;
                break;
        case 0x2:               /* move.l */
                sz = SIZE_LONG;
                break;
        case 0x7:               /* moveq */
                addstr(dbuf, "movq\t#");
                prints_bf(dbuf, opc, 7, 0);
                addchar(',');
                PRINT_DREG(dbuf,BITFIELD(opc,11,9));
                return;
        }
        addstr(dbuf, "mov");

        if (BITFIELD(opc,8,6) == AR_DIR)
                addchar('a');

        if (sz == SIZE_BYTE)
                addchar('b');
        else if (sz == SIZE_WORD)
                addchar('w');
        else
                addchar('l');

        addchar('\t');
        lused = dbuf->used;
        get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 0);
        addchar(',');
        get_modregstr(dbuf, 11, GETMOD_AFTER, sz, dbuf->used - lused);
}

/*
 * misc opcodes.
 */
static void
opcode_misc(dis_buffer_t *dbuf, u_short opc)
{
        const char *tmp;
        int sz;

        tmp = NULL;

        /* Check against no option instructions */
        switch (opc) {
        case BGND_INST:
                tmp = "bgnd";
                break;
        case ILLEGAL_INST:
                tmp = "illegal";
                break;
        case MOVEFRC_INST:
        case MOVETOC_INST:
                opcode_movec(dbuf, opc);
                return;
        case NOP_INST:
                tmp = "nop";
                break;
        case RESET_INST:
                tmp = "reset";
                break;
        case RTD_INST:
                addstr(dbuf, "rtd\t");
                get_immed(dbuf, SIZE_WORD);
                return;
        case RTE_INST:
                tmp = "rte";
                break;
        case RTR_INST:
                tmp = "rtr";
                break;
        case RTS_INST:
                tmp = "rts";
                break;
        case STOP_INST:
                addstr(dbuf, "stop\t");
                get_immed(dbuf, SIZE_WORD);
                return;
        case TRAPV_INST:
                tmp = "trapv";
                break;
        default:
                break;
        }
        if (tmp) {
                addstr(dbuf, tmp);
                return;
        }

        switch (opc & BKPT_MASK) {
        case BKPT_INST:
                addstr(dbuf, "bkpt\t#");
                printu_bf(dbuf, opc, 2, 0);
                return;
        case EXTBW_INST:
                addstr(dbuf, "extw\t");
                get_modregstr(dbuf,2,DR_DIR,0,0);
                return;
        case EXTWL_INST:
                addstr(dbuf, "extl\t");
                get_modregstr(dbuf,2,DR_DIR,0,0);
                return;
        case EXTBL_INST:
                addstr(dbuf, "extbl\t");
                get_modregstr(dbuf,2,DR_DIR,0,0);
                return;
        case LINKW_INST:
        case LINKL_INST:
                if ((LINKW_MASK & opc) == LINKW_INST) {
                        addstr(dbuf, "linkw\t");
                        get_modregstr(dbuf, 2, AR_DIR, 0, 1);
                } else {
                        addstr(dbuf, "linkl\t");
                        get_modregstr(dbuf, 2, AR_DIR, 0, 2);
                }
                addchar(',');
                if ((LINKW_MASK & opc) == LINKW_INST)
                        get_immed(dbuf, SIZE_WORD);
                else
                        get_immed(dbuf,SIZE_LONG);
                return;
        case MOVETOUSP_INST:
        case MOVEFRUSP_INST:
                addstr(dbuf, "movl\t");
                if (!ISBITSET(opc,3)) {
                        get_modregstr(dbuf, 2, AR_DIR, 0, 0);
                        addchar(',');
                }
                addstr(dbuf, "usp");
                if (ISBITSET(opc,3)) {
                        addchar(',');
                        get_modregstr(dbuf, 2, AR_DIR, 0, 0);
                }
                return;
        case SWAP_INST:
                addstr(dbuf, "swap\t");
                get_modregstr(dbuf, 2, DR_DIR, 0, 0);
                return;
        case UNLK_INST:
                addstr(dbuf, "unlk\t");
                get_modregstr(dbuf, 2, AR_DIR, 0, 0);
                return;
        }

        if ((opc & TRAP_MASK) == TRAP_INST) {
                addstr(dbuf, "trap\t#");
                printu_bf(dbuf, opc, 3, 0);
                return;
        }

        sz = 0;
        switch (DIVSL_MASK & opc) {
        case DIVSL_INST:
        case MULSL_INST:
                opcode_divmul(dbuf, opc);
                return;
        case JMP_INST:
                tmp = "jmp\t";
                break;
        case JSR_INST:
                tmp = "jsr\t";
                break;
        case MOVEFRCCR_INST:
                tmp = "mov\tccr,";
                break;
        case MOVEFRSR_INST:
                tmp = "mov\tsr,";
                break;
        case NBCD_INST:
                tmp = "nbcd\t";
                break;
        case PEA_INST:
                tmp = "pea\t";
                break;
        case TAS_INST:
                tmp = "tas\t";
                break;
        case MOVETOCCR_INST:
        case MOVETOSR_INST:
                tmp = "mov\t";
                sz = SIZE_WORD;
                break;
        }
        if (tmp) {
                addstr(dbuf, tmp);
                get_modregstr(dbuf,5, GETMOD_BEFORE, sz, 0);
                if(IS_INST(MOVETOSR,opc))
                        addstr(dbuf, ",sr");
                else if(IS_INST(MOVETOCCR,opc))
                        addstr(dbuf, ",ccr");
                return;
        }

        if ((opc & MOVEM_MASK) == MOVEM_INST) {
                opcode_movem(dbuf, opc);
                return;
        }

        switch (opc & CLR_MASK) {
        case CLR_INST:
                tmp = "clr";
                break;
        case NEG_INST:
                tmp = "neg";
                break;
        case NEGX_INST:
                tmp = "negx";
                break;
        case NOT_INST:
                tmp = "not";
                break;
        case TST_INST:
                tmp = "tst";
                break;
        }
        if (tmp) {
                int msz;

                addstr(dbuf, tmp);

                msz = BITFIELD(opc,7,6);
                if (msz == 0) {
                        tmp = "b\t";
                        sz = SIZE_BYTE;
                } else if (msz == 1) {
                        tmp = "w\t";
                        sz = SIZE_WORD;
                } else {
                        tmp = "l\t";
                        sz = SIZE_LONG;
                }
                addstr(dbuf, tmp);
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 0);
                return;
        }

        if ((opc & LEA_MASK) == LEA_INST) {
                addstr(dbuf, "lea\t");
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_LONG, 0);
                addchar(',');
                get_modregstr(dbuf, 11, AR_DIR, 0, 0);
                return;
        } else if ((opc & CHK_MASK) == CHK_INST) {
                if (BITFIELD(opc,8,7) == 0x3) {
                        addstr(dbuf, "chkw\t");
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_WORD, 0);
                } else {
                        addstr(dbuf, "chkl\t");
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_LONG, 0);
                }
                addchar(',');
                get_modregstr(dbuf, 11, DR_DIR, 0, 0);
                return;
        }
}

/*
 * ADDQ/SUBQ/Scc/DBcc/TRAPcc
 */
static void
opcode_0101(dis_buffer_t *dbuf, u_short opc)
{
        int data;

        if (IS_INST(TRAPcc, opc) && BITFIELD(opc,2,0) > 1) {
                int opmode;

                opmode = BITFIELD(opc,2,0);
                make_cond(dbuf,11,"trap");

                if (opmode == 0x2) {
                        addchar('w');
                        addchar('\t');
                        get_immed(dbuf, SIZE_WORD);
                } else if (opmode == 0x3) {
                        addchar('l');
                        addchar('\t');
                        get_immed(dbuf, SIZE_LONG);
                }
                return;
        } else if (IS_INST(DBcc, opc)) {
                make_cond(dbuf,11,"db");
                addchar('\t');
                PRINT_DREG(dbuf, BITFIELD(opc,2,0));
                addchar(',');
                print_disp(dbuf, *(dbuf->val + 1), SIZE_WORD, -1);
                dbuf->used++;
                return;
        } else if (IS_INST(Scc,opc)) {
                make_cond(dbuf,11,"s");
                addchar('\t');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_BYTE, 0);
                return;
        } else if (IS_INST(ADDQ, opc) || IS_INST(SUBQ, opc)) {
                int size = BITFIELD(opc,7,6);

                if (IS_INST(SUBQ, opc))
                        addstr(dbuf, "subq");
                else
                        addstr(dbuf, "addq");

                if (size == 0x1)
                        addchar('w');
                else if (size == 0x2)
                        addchar('l');
                else
                        addchar('b');

                addchar('\t');
                addchar('#');
                data = BITFIELD(opc,11,9);
                if (data == 0)
                        data = 8;
                printu(dbuf, data, SIZE_BYTE);
                addchar(',');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 0);

                return;
        }
}

/*
 * Bcc/BSR/BRA
 */
static void
opcode_branch(dis_buffer_t *dbuf, u_short opc)
{
        int disp;

        if (IS_INST(BRA,opc))
                addstr(dbuf, "bra");
        else if (IS_INST(BSR,opc))
                addstr(dbuf, "bsr");
        else
                make_cond(dbuf,11,"b");

        disp = BITFIELD(opc,7,0);
        if (disp == 0) {
                /* 16-bit signed displacement */
                disp = *(dbuf->val + 1);
                dbuf->used++;
                addchar('w');
        } else if (disp == 0xff) {
                /* 32-bit signed displacement */
                disp = *(long *)(dbuf->val + 1);
                dbuf->used += 2;
                addchar('l');
        } else {
                /* 8-bit signed displacement in opcode. */
                /* Needs to be sign-extended... */
                if (ISBITSET(disp,7))
                        disp -= 256;
                addchar('b');
        }
        addchar('\t');
        print_addr(dbuf, disp + (u_long)dbuf->val + 2);
}

/*
 * ADD/ADDA/ADDX/SUB/SUBA/SUBX
 */
static void
opcode_addsub(dis_buffer_t *dbuf, u_short opc)
{
        int sz, ch, amode;

        sz = BITFIELD(opc,7,6);
        amode = 0;

        if (sz == 0) {
                ch = 'b';
                sz = SIZE_BYTE;
        } else if (sz == 1) {
                ch = 'w';
                sz = SIZE_WORD;
        } else if (sz == 2) {
                ch = 'l';
                sz = SIZE_LONG;
        } else {
                amode = 1;
                if (!ISBITSET(opc,8))  {
                        sz = SIZE_WORD;
                        ch = 'w';
                } else {
                        sz = SIZE_LONG;
                        ch = 'l';
                }
        }

        if (!amode && (IS_INST(ADDX,opc) || IS_INST(SUBX,opc))) {
                if (IS_INST(ADDX,opc))
                        addstr(dbuf,"addx");
                else
                        addstr(dbuf,"subx");

                addchar(ch);
                addchar('\t');

                if (ISBITSET(opc,3)) {
                        PRINT_AREG(dbuf,BITFIELD(opc,2,0));
                        addchar('@');
                        addchar('-');
                        addchar(',');
                        PRINT_AREG(dbuf,BITFIELD(opc,11,9));
                        addchar('@');
                        addchar('-');
                } else {
                        PRINT_DREG(dbuf,BITFIELD(opc,2,0));
                        addchar(',');
                        PRINT_DREG(dbuf,BITFIELD(opc,11,9));
                }
        } else {
                if (IS_INST(ADD,opc))
                        addstr(dbuf, "add");
                else
                        addstr(dbuf, "sub");

                if (amode)
                        addchar('a');
                addchar(ch);
                addchar('\t');

                if (ISBITSET(opc,8) && amode == 0) {
                        PRINT_DREG(dbuf,BITFIELD(opc,11,9));
                        addchar(',');
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 0);
                } else {
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 0);
                        addchar(',');
                        if (amode)
                                PRINT_AREG(dbuf,BITFIELD(opc,11,9));
                        else
                                PRINT_DREG(dbuf,BITFIELD(opc,11,9));
                }
        }
        return;
}

/*
 * Shift/Rotate/Bit Field
 */
static void
opcode_1110(dis_buffer_t *dbuf, u_short opc)
{
        const char *tmp;
        u_short ext;
        int type, sz;

        tmp = NULL;

        switch (opc & BFCHG_MASK) {
        case BFCHG_INST:
                tmp = "bfchg";
                break;
        case BFCLR_INST:
                tmp = "bfclr";
                break;
        case BFEXTS_INST:
                tmp = "bfexts";
                break;
        case BFEXTU_INST:
                tmp = "bfextu";
                break;
        case BFFFO_INST:
                tmp = "bfffo";
                break;
        case BFINS_INST:
                tmp = "bfins";
                break;
        case BFSET_INST:
                tmp = "bfset";
                break;
        case BFTST_INST:
                tmp = "bftst";
                break;
        }
        if (tmp) {
                short bf;

                addstr(dbuf, tmp);
                addchar('\t');

                ext = *(dbuf->val + 1);
                dbuf->used++;

                if (IS_INST(BFINS,opc)) {
                        PRINT_DREG(dbuf, BITFIELD(ext,14,12));
                        addchar(',');
                }
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 1);
                addchar('{');

                bf = BITFIELD(ext,10,6);
                if (ISBITSET(ext, 11))
                        PRINT_DREG(dbuf, bf);
                else
                        printu_wb(dbuf, bf, SIZE_BYTE, 10);

                addchar(':');

                bf = BITFIELD(ext, 4, 0);
                if (ISBITSET(ext, 5))
                        PRINT_DREG(dbuf, bf);
                else {
                        if (bf == 0)
                                bf = 32;
                        printu_wb(dbuf, bf, SIZE_BYTE, 10);
                }
                addchar('}');
                if (ISBITSET(opc,8) && !IS_INST(BFINS,opc)) {
                        addchar(',');
                        PRINT_DREG(dbuf, BITFIELD(ext,14,12));
                } else
                        *dbuf->casm = 0;
                return;
        }
        sz = BITFIELD(opc,7,6);
        if (sz == 0x3)
                type = BITFIELD(opc, 10, 9);
        else
                type = BITFIELD(opc, 4, 3);

        switch (type) {
        case AS_TYPE:
                addchar('a');
                addchar('s');
                break;
        case LS_TYPE:
                addchar('l');
                addchar('s');
                break;
        case RO_TYPE:
                addchar('r');
                addchar('o');
                break;
        case ROX_TYPE:
                addchar('r');
                addchar('o');
                addchar('x');
                break;
        }

        if (ISBITSET(opc,8))
                addchar('l');
        else
                addchar('r');

        switch (sz) {
        case 0:
                sz = SIZE_BYTE;
                addchar('b');
                break;
        case 3:
        case 1:
                sz = SIZE_WORD;
                addchar('w');
                break;
        case 2:
                sz = SIZE_LONG;
                addchar('l');
                break;

        }
        addchar('\t');
        if(BITFIELD(opc,7,6) == 0x3) {
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 0);
                return;
        } else if (ISBITSET(opc,5))
                PRINT_DREG(dbuf, BITFIELD(opc,11,9));
        else {
                addchar('#');
                sz = BITFIELD(opc,11,9);
                if (sz == 0)
                        sz = 8;
                printu_wb(dbuf, sz, SIZE_BYTE, 10);
        }
        addchar(',');
        PRINT_DREG(dbuf, BITFIELD(opc,2,0));
        return;
}

/*
 * CMP/CMPA/EOR
 */
static void
opcode_1011(dis_buffer_t *dbuf, u_short opc)
{
        int sz;

        if (IS_INST(CMPA,opc)) {
                addstr(dbuf, "cmpa");

                if (ISBITSET(opc, 8)) {
                        addchar('l');
                        sz = SIZE_LONG;
                } else {
                        addchar('w');
                        sz = SIZE_WORD;
                }
                addchar('\t');
        } else {
                if (IS_INST(CMP, opc))
                        addstr(dbuf, "cmp");
                else
                        addstr(dbuf, "eor");

                sz = BITFIELD(opc,7,6);
                switch (sz) {
                case 0:
                        addchar('b');
                        sz = SIZE_BYTE;
                        break;
                case 1:
                        addchar('w');
                        sz = SIZE_WORD;
                        break;
                case 2:
                        addchar('l');
                        sz = SIZE_LONG;
                        break;
                }
                addchar('\t');
                if (IS_INST(EOR,opc)) {
                        PRINT_DREG(dbuf, BITFIELD(opc,11,9));
                        addchar(',');
                }
        }
        get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 0);

        if (IS_INST(CMPA,opc)) {
                addchar(',');
                PRINT_AREG(dbuf, BITFIELD(opc,11,9));
        } else if (IS_INST(CMP,opc)) {
                addchar(',');
                PRINT_DREG(dbuf, BITFIELD(opc,11,9));
        }
        return;
}

/*
 * OR/DIV/SBCD
 */
static void
opcode_1000(dis_buffer_t *dbuf, u_short opc)
{
        int sz;

        if (IS_INST(UNPKA,opc)) {
                addstr(dbuf, "unpk\t");
                PRINT_AREG(dbuf,BITFIELD(opc,2,0));
                addstr(dbuf, "@-,");
                PRINT_AREG(dbuf,BITFIELD(opc,11,9));
                addstr(dbuf, "@-,");
                get_immed(dbuf,SIZE_WORD);
        } else if (IS_INST(UNPKD,opc)) {
                addstr(dbuf, "unpk\t");
                PRINT_DREG(dbuf,BITFIELD(opc,2,0));
                addchar(',');
                PRINT_DREG(dbuf,BITFIELD(opc,11,9));
                addchar(',');
                get_immed(dbuf,SIZE_WORD);
        } else if (IS_INST(SBCDA,opc)) {
                addstr(dbuf, "sbcd\t");
                PRINT_AREG(dbuf,BITFIELD(opc,2,0));
                addstr(dbuf, "@-,");
                PRINT_AREG(dbuf,BITFIELD(opc,11,9));
                addstr(dbuf, "@-");
        } else if (IS_INST(SBCDD,opc)) {
                addstr(dbuf, "sbcd\t");
                PRINT_DREG(dbuf,BITFIELD(opc,2,0));
                addchar(',');
                PRINT_DREG(dbuf,BITFIELD(opc,11,9));
        } else if (IS_INST(DIVSW,opc) || IS_INST(DIVUW,opc)) {
                if (IS_INST(DIVSW,opc))
                        addstr(dbuf, "divsw\t");
                else
                        addstr(dbuf, "divuw\t");
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_WORD, 0);
                addchar(',');
                PRINT_DREG(dbuf, BITFIELD(opc,11,9));
        } else {
                addstr(dbuf, "or");

                sz = BITFIELD(opc,7,6);
                switch (sz) {
                case 0:
                        addchar('b');
                        sz = SIZE_BYTE;
                        break;
                case 1:
                        addchar('w');
                        sz = SIZE_WORD;
                        break;
                case 2:
                        addchar('l');
                        sz = SIZE_LONG;
                        break;
                }
                addchar('\t');
                if (ISBITSET(opc,8)) {
                        PRINT_DREG(dbuf, BITFIELD(opc,11,9));
                        addchar(',');
                }
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 0);
                if (!ISBITSET(opc,8)) {
                        addchar(',');
                        PRINT_DREG(dbuf, BITFIELD(opc,11,9));
                }
        }
}

/*
 * AND/MUL/ABCD/EXG (1100)
 */
static void
opcode_1100(dis_buffer_t *dbuf, u_short opc)
{
        int sz;

        if (IS_INST(ABCDA,opc)) {
                addstr(dbuf, "abcd\t");
                PRINT_AREG(dbuf,BITFIELD(opc,2,0));
                addstr(dbuf, "@-,");
                PRINT_AREG(dbuf,BITFIELD(opc,11,9));
                addstr(dbuf, "@-");
        } else if (IS_INST(ABCDD,opc)) {
                addstr(dbuf, "abcd\t");
                PRINT_DREG(dbuf,BITFIELD(opc,2,0));
                addchar(',');
                PRINT_DREG(dbuf,BITFIELD(opc,11,9));
        } else if (IS_INST(MULSW,opc) || IS_INST(MULUW,opc)) {
                if (IS_INST(MULSW,opc))
                        addstr(dbuf, "mulsw\t");
                else
                        addstr(dbuf, "muluw\t");
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_WORD, 0);
                addchar(',');
                PRINT_DREG(dbuf, BITFIELD(opc,11,9));
        } else if (IS_INST(EXG,opc)) {
                addstr(dbuf, "exg\t");
                if (ISBITSET(opc,7)) {
                        PRINT_DREG(dbuf,BITFIELD(opc,11,9));
                        addchar(',');
                        PRINT_AREG(dbuf,BITFIELD(opc,2,0));
                } else if (ISBITSET(opc,3)) {
                        PRINT_AREG(dbuf,BITFIELD(opc,11,9));
                        addchar(',');
                        PRINT_AREG(dbuf,BITFIELD(opc,2,0));
                } else {
                        PRINT_DREG(dbuf,BITFIELD(opc,11,9));
                        addchar(',');
                        PRINT_DREG(dbuf,BITFIELD(opc,2,0));
                }
        } else {
                addstr(dbuf, "and");

                sz = BITFIELD(opc,7,6);
                switch (sz) {
                case 0:
                        addchar('b');
                        sz = SIZE_BYTE;
                        break;
                case 1:
                        addchar('w');
                        sz = SIZE_WORD;
                        break;
                case 2:
                        addchar('l');
                        sz = SIZE_LONG;
                        break;
                }
                addchar('\t');

                if (ISBITSET(opc,8)) {
                        PRINT_DREG(dbuf, BITFIELD(opc,11,9));
                        addchar(',');
                }
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 0);
                if (!ISBITSET(opc,8)) {
                        addchar(',');
                        PRINT_DREG(dbuf, BITFIELD(opc,11,9));
                }
        }
}

/*
 * Coprocessor instruction
 */
static void
opcode_coproc(dis_buffer_t *dbuf, u_short opc)
{
        switch (BITFIELD(*dbuf->val,11,9)) {
        case 1:
                opcode_fpu(dbuf, opc);
                return;
        case 0:
                opcode_mmu(dbuf, opc);
                return;
        case 2:
                opcode_mmu040(dbuf, opc);
                return;
        case 3:
                opcode_move16(dbuf, opc);
                return;
        }
        switch (BITFIELD(opc,8,6)) {
        case 0:
                dbuf->used++;
                break;
        case 3:
                dbuf->used++;
                /*FALLTHROUGH*/
        case 2:
                dbuf->used++;
                break;
        case 1:
                dbuf->used++;
        case 4:
        case 5:
        default:
                /* Nothing */ ;
        }
        addstr(dbuf, "UNKNOWN COPROC OPCODE");
        return;
}

/*
 * Resvd
 */
static void
opcode_1010(dis_buffer_t *dbuf, u_short opc)
{
        addstr(dbuf, "RSVD");
        dbuf->used++;
}

static void
opcode_fpu(dis_buffer_t *dbuf, u_short opc)
{
        u_short ext;
        int type, opmode;

        type = BITFIELD(opc,8,6);
        switch (type) {
        /* cpGEN */
        case 0:
                ext = *(dbuf->val + 1);
                dbuf->used++;
                opmode = BITFIELD(ext,5,0);

                if (BITFIELD(opc,5,0) == 0 && BITFIELD(ext,15,10) == 0x17) {
                        addstr(dbuf,"fmovcrx #");
                        printu(dbuf,BITFIELD(ext,6,0),SIZE_BYTE);
                        return;
                }
                if (ISBITSET(ext,15) || ISBITSET(ext,13)) {
                        opcode_fmove_ext(dbuf, opc, ext);
                        return;
                }

                switch(opmode) {
                case FMOVE:
                        get_fpustdGEN(dbuf,ext,"fmov");
                        return;
                case FABS:
                        get_fpustdGEN(dbuf,ext,"fabs");
                        return;
                case FACOS:
                        get_fpustdGEN(dbuf,ext,"facos");
                        return;
                case FADD:
                        get_fpustdGEN(dbuf,ext,"fadd");
                        return;
                case FASIN:
                        get_fpustdGEN(dbuf,ext,"fasin");
                        return;
                case FATAN:
                        get_fpustdGEN(dbuf,ext,"fatan");
                        return;
                case FATANH:
                        get_fpustdGEN(dbuf,ext,"fatanh");
                        return;
                case FCMP:
                        get_fpustdGEN(dbuf,ext,"fcmp");
                        return;
                case FCOS:
                        get_fpustdGEN(dbuf,ext,"fcos");
                        return;
                case FCOSH:
                        get_fpustdGEN(dbuf,ext,"fcosh");
                        return;
                case FDIV:
                        get_fpustdGEN(dbuf,ext,"fdiv");
                        return;
                case FETOX:
                        get_fpustdGEN(dbuf,ext,"fetox");
                        return;
                case FGETEXP:
                        get_fpustdGEN(dbuf,ext,"fgetexp");
                        return;
                case FGETMAN:
                        get_fpustdGEN(dbuf,ext,"fgetman");
                        return;
                case FINT:
                        get_fpustdGEN(dbuf,ext,"fint");
                        return;
                case FINTRZ:
                        get_fpustdGEN(dbuf,ext,"fintrz");
                        return;
                case FLOG10:
                        get_fpustdGEN(dbuf,ext,"flog10");
                        return;
                case FLOG2:
                        get_fpustdGEN(dbuf,ext,"flog2");
                        return;
                case FLOGN:
                        get_fpustdGEN(dbuf,ext,"flogn");
                        return;
                case FLOGNP1:
                        get_fpustdGEN(dbuf,ext,"flognp1");
                        return;
                case FMOD:
                        get_fpustdGEN(dbuf,ext,"fmod");
                        return;
                case FMUL:
                        get_fpustdGEN(dbuf,ext,"fmul");
                        return;
                case FNEG:
                        get_fpustdGEN(dbuf,ext,"fneg");
                        return;
                case FREM:
                        get_fpustdGEN(dbuf,ext,"frem");
                        return;
                case FSCALE:
                        get_fpustdGEN(dbuf,ext,"fscale");
                        return;
                case FSGLDIV:
                        get_fpustdGEN(dbuf,ext,"fsgldiv");
                        return;
                case FSGLMUL:
                        get_fpustdGEN(dbuf,ext,"fsglmul");
                        return;
                case FSIN:
                        get_fpustdGEN(dbuf,ext,"fsin");
                        return;
                case FSINH:
                        get_fpustdGEN(dbuf,ext,"fsinh");
                        return;
                case FSQRT:
                        get_fpustdGEN(dbuf,ext,"fsqrt");
                        return;
                case FSUB:
                        get_fpustdGEN(dbuf,ext,"fsub");
                        return;
                case FTAN:
                        get_fpustdGEN(dbuf,ext,"ftan");
                        return;
                case FTANH:
                        get_fpustdGEN(dbuf,ext,"ftanh");
                        return;
                case FTENTOX:
                        get_fpustdGEN(dbuf,ext,"ftentox");
                        return;
                case FTST:
                        get_fpustdGEN(dbuf,ext,"ftst");
                        return;
                case FTWOTOX:
                        get_fpustdGEN(dbuf,ext,"ftwotox");
                        return;

                }
                /* FALLTHROUGH */
        /* cpBcc */
        case 2:
                if (BITFIELD(opc,5,0) == 0 && *(dbuf->val + 1) == 0) {
                        dbuf->used++;
                        addstr (dbuf, "fnop");
                        return;
                }
                /* FALLTHROUGH */
        case 3:
                addstr(dbuf, "fb");
                print_fcond(dbuf, BITFIELD(opc,5,0));
                if (type == 2) {
                        addchar('w');
                        addchar('\t');
                        print_disp(dbuf,*(dbuf->val + 1), SIZE_WORD, -1);
                        dbuf->used++;
                } else {
                        addchar('l');
                        addchar('\t');
                        print_disp(dbuf,*(long *)(dbuf->val + 1), SIZE_LONG,
                                 -1);
                        dbuf->used += 2;
                }
                return;
        /* cpDBcc/cpScc/cpTrap */
        case 1:
                ext = *(dbuf->val + 1);
                dbuf->used++;

                if (BITFIELD(opc,5,3) == 0x1) {
                        /* fdbcc */
                        addstr(dbuf,"fdb");
                        print_fcond(dbuf,BITFIELD(ext,5,0));
                        addchar('\t');
                        PRINT_DREG(dbuf, BITFIELD(opc,2,0));
                        addchar(',');
                        print_disp(dbuf, *(dbuf->val + 2), SIZE_WORD, -1);
                        dbuf->used++;
                } else if (BITFIELD(opc,5,3) == 0x7 &&
                    BITFIELD(opc,2,0) > 1) {
                        addstr(dbuf,"ftrap");
                        print_fcond(dbuf,BITFIELD(ext,5,0));

                        if (BITFIELD(opc,2,0) == 0x2) {
                                addchar('w');
                                addchar('\t');
                                dbuf->val++;
                                get_immed(dbuf, SIZE_WORD);
                                dbuf->val--;
                        } else if (BITFIELD(opc,2,0) == 0x3) {
                                addchar('l');
                                addchar('\t');
                                dbuf->val++;
                                get_immed(dbuf, SIZE_LONG);
                                dbuf->val--;
                        }
                } else {
                        addstr(dbuf,"fs");
                        print_fcond(dbuf,BITFIELD(ext,5,0));
                        addchar('\t');
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_BYTE, 1);
                }
                return;
        case 4:
                addstr(dbuf,"fsave\t");
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 0);
                return;
        case 5:
                addstr(dbuf,"frestor\t");
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 0);
                return;
        }
}

/*
 * XXX - This screws up on:  fmovem  a0@(312),fpcr/fpsr/fpi
 */
static void
opcode_fmove_ext(dis_buffer_t *dbuf, u_short opc, u_short ext)
{
        int sz;

        sz = 0;
        if (BITFIELD(ext,15,13) == 3) {
                /* fmove r ==> m */
                addstr(dbuf, "fmov");
                switch (BITFIELD(ext,12,10)) {
                case 0:
                        addchar('l');
                        sz = SIZE_LONG;
                        break;
                case 1:
                        addchar('s');
                        sz = SIZE_SINGLE;
                        break;
                case 2:
                        addchar('x');
                        sz = SIZE_EXTENDED;
                        break;
                case 7:
                case 3:
                        addchar('p');
                        sz = SIZE_PACKED;
                        break;
                case 4:
                        addchar('w');
                        sz = SIZE_WORD;
                        break;
                case 5:
                        addchar('d');
                        sz = SIZE_DOUBLE;
                        break;
                case 6:
                        addchar('b');
                        sz = SIZE_BYTE;
                        break;
                }
                addchar('\t');
                PRINT_FPREG(dbuf, BITFIELD(ext,9,7));
                addchar(',');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 1);
                if (sz == SIZE_PACKED) {
                        addchar('{');
                        if (ISBITSET(ext,12)) {
                                PRINT_DREG(dbuf,BITFIELD(ext,6,4));
                        } else {
                                addchar('#');
                                prints_bf(dbuf, ext, 6, 4);
                        }
                        addchar('}');
                }
                return;
        }
        addstr(dbuf,"fmovm");

        if (!ISBITSET(ext,14)) {
                /* fmove[m] control reg */
                addchar('l');
                addchar('\t');

                if (ISBITSET(ext,13)) {
                        print_freglist(dbuf, AR_DEC, BITFIELD(ext,12,10), 1);
                        addchar(',');
                }
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_LONG, 1);
                if (!ISBITSET(ext,13)) {
                        addchar(',');
                        print_freglist(dbuf, AR_DEC, BITFIELD(ext,12,10), 1);
                }
                return;
        }
        addchar('x');
        addchar('\t');

        if (ISBITSET(ext,11)) {
                if (ISBITSET(ext,13)) {
                        PRINT_DREG(dbuf,BITFIELD(ext,6,4));
                        addchar(',');
                }
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_EXTENDED, 1);
                if (!ISBITSET(ext,13)) {
                        addchar(',');
                        PRINT_DREG(dbuf,BITFIELD(ext,6,4));
                }
        } else {
                if (ISBITSET(ext,13)) {
                        print_freglist(dbuf, BITFIELD(opc,5,3),
                            BITFIELD(ext,7,0), 0);
                        addchar(',');
                }
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_EXTENDED, 1);
                if (!ISBITSET(ext,13)) {
                        addchar(',');
                        print_freglist(dbuf, BITFIELD(opc,5,3),
                            BITFIELD(ext,7,0), 0);
                }
        }
}

static void
opcode_mmu(dis_buffer_t *dbuf, u_short opc)
{
        u_short ext;
        int type;

        type = BITFIELD(opc,8,6);
        switch (type) {
        /* cpGEN? */
        case 0:
                ext = *(dbuf->val + 1);
                dbuf->used++;

                switch(BITFIELD(ext,15,13)) {
                case 5:
                case 1:
                        opcode_pflush(dbuf, opc, ext);
                        return;
                case 0:
                case 3:
                case 2:
                        opcode_pmove(dbuf, opc, ext);
                        return;
                case 4:
                        addstr(dbuf, "ptest");
                        if (ISBITSET(ext,9))
                                addchar('r');
                        else
                                addchar('w');
                        addchar('\t');
                        print_fcode(dbuf, BITFIELD(ext, 5, 0));
                        addchar(',');
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 1);
                        addchar(',');
                        addchar('#');
                        printu_bf(dbuf, ext, 12, 10);
                        if (ISBITSET(ext, 8)) {
                                addchar(',');
                                PRINT_AREG(dbuf, BITFIELD(ext, 7, 5));
                        }
                }
                return;
        case 2:
        case 3:
                addstr(dbuf, "pb");
                print_mcond(dbuf, BITFIELD(opc,5,0));
                if (type == 2) {
                        addchar('w');
                        addchar('\t');
                        print_disp(dbuf,*(dbuf->val + 1), SIZE_WORD, -1);
                        dbuf->used++;
                } else {
                        addchar('l');
                        addchar('\t');
                        print_disp(dbuf,*(long *)(dbuf->val + 1), SIZE_LONG,
                                 -1);
                        dbuf->used += 2;
                }
                return;
        case 1:
                ext = *(dbuf->val + 1);
                dbuf->used++;

                if (BITFIELD(opc,5,3) == 0x1) {
                        /* fdbcc */
                        addstr(dbuf,"pdb");
                        print_fcond(dbuf,BITFIELD(ext,5,0));
                        addchar('\t');
                        PRINT_DREG(dbuf, BITFIELD(opc,2,0));
                        addchar(',');
                        print_disp(dbuf, *(dbuf->val + 2), SIZE_WORD, -1);
                        dbuf->used++;
                } else if (BITFIELD(opc,5,3) == 0x7 &&
                    BITFIELD(opc,2,0) > 1) {
                        addstr(dbuf,"ptrap");
                        print_fcond(dbuf,BITFIELD(ext,5,0));

                        if (BITFIELD(opc,2,0) == 0x2) {
                                addchar('w');
                                addchar('\t');
                                dbuf->val++;
                                get_immed(dbuf, SIZE_WORD);
                                dbuf->val--;
                        } else if (BITFIELD(opc,2,0) == 0x3) {
                                addchar('l');
                                addchar('\t');
                                dbuf->val++;
                                get_immed(dbuf, SIZE_LONG);
                                dbuf->val--;
                        }
                } else {
                        addstr(dbuf,"ps");
                        print_fcond(dbuf,BITFIELD(ext,5,0));
                        addchar('\t');
                        get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_BYTE, 1);
                }
                return;
        case 4:
                addstr(dbuf,"psave\t");
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 0);
                return;
        case 5:
                addstr(dbuf,"prestore\t");
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 0);
                return;
        }
}

static void
opcode_pflush(dis_buffer_t *dbuf, u_short opc, u_short ext)
{
        u_short mode, mask, fc;

        mode = BITFIELD(ext,12,10);
        mask = BITFIELD(ext,8,5);
        fc = BITFIELD(ext, 5, 0);

        if (ext == 0xa000) {
                addstr(dbuf,"pflushr\t");
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_LONG, 1);
                return;
        }

        if (mode == 0) {
                addstr(dbuf,"pload");
                if (ISBITSET(ext,9))
                        addchar('r');
                else
                        addchar('w');
                addchar(' ');
                print_fcode(dbuf, fc);
        }

        addstr(dbuf,"pflush");
        switch (mode) {
        case 1:
                addchar('a');
                *dbuf->casm = 0;
                break;
        case 7:
        case 5:
                addchar('s');
                /*FALLTHROUGH*/
        case 6:
        case 4:
                addchar('\t');
                print_fcode(dbuf, fc);
                addchar(',');
                addchar('#');
                printu(dbuf, mask, SIZE_BYTE);
                if (!ISBITSET(mode,1))
                        break;
                addchar(',');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, SIZE_LONG, 1);
        }
}

static void
opcode_pmove(dis_buffer_t *dbuf, u_short opc, u_short ext)
{
        const char *reg;
        int rtom, sz, preg;

        reg  = "???";
        sz   = 0;
        rtom = ISBITSET(ext, 9);
        preg = BITFIELD(ext, 12, 10);

        addstr(dbuf,"pmov");
        if (ISBITSET(ext,8)) {
                addchar('f');
                addchar('d');
        }
        switch (BITFIELD(ext, 15, 13)) {
        case 0: /* tt regs 030o */
                switch (preg) {
                case 2:
                        reg = "tt0";
                        break;
                case 3:
                        reg = "tt1";
                        break;
                }
                sz = SIZE_LONG;
                break;
        case 2:
                switch (preg) {
                case 0:
                        reg = "tc";
                        sz = SIZE_LONG;
                        break;
                case 1:
                        reg = "drp";
                        sz = SIZE_QUAD;
                        break;
                case 2:
                        reg = "srp";
                        sz = SIZE_QUAD;
                        break;
                case 3:
                        reg = "crp";
                        sz = SIZE_QUAD;
                        break;
                case 4:
                        reg = "cal";
                        sz = SIZE_BYTE;
                        break;
                case 5:
                        reg = "val";
                        sz = SIZE_BYTE;
                        break;
                case 6:
                        reg = "scc";
                        sz = SIZE_BYTE;
                        break;
                case 7:
                        reg = "ac";
                        sz = SIZE_WORD;
                }
                break;
        case 3:
                switch (preg) {
                case 0:
                        reg = "mmusr";
                        break;
                case 1:
                        reg = "pcsr";
                        break;
                case 4:
                        reg = "bad";
                        break;
                case 5:
                        reg = "bac";
                        break;
                }
                sz = SIZE_WORD;
                break;
        }
        switch (sz) {
        case SIZE_BYTE:
                addchar ('b');
                break;
        case SIZE_WORD:
                addchar ('w');
                break;
        case SIZE_LONG:
                addchar ('l');
                break;
        case SIZE_QUAD:
                addchar ('d');
                break;
        }
        addchar('\t');

        if (!rtom) {
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 1);
                addchar(',');
        }
        addstr(dbuf, reg);
        if (BITFIELD(ext, 15, 13) == 3 && preg > 1)
                printu_bf(dbuf, ext, 4, 2);
        if (rtom) {
                addchar(',');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 1);
        }
        return;
}

static void
print_fcode(dis_buffer_t *dbuf, u_short fc)
{
        if (ISBITSET(fc, 4))
                printu_bf(dbuf, fc, 3, 0);
        else if (ISBITSET(fc, 3))
                PRINT_DREG(dbuf, BITFIELD(fc, 2, 0));
        else if (fc == 1)
                addstr(dbuf, "sfc");
        else
                addstr(dbuf, "dfc");
}

static void
opcode_mmu040(dis_buffer_t *dbuf, u_short opc)
{
        if (ISBITSET(opc, 6)) {
                addstr(dbuf, "ptest");
                if (ISBITSET(opc, 5))
                        addchar('r');
                else
                        addchar('w');
                addchar('\t');
                PRINT_AREG(dbuf, BITFIELD(opc,2,0));
                addchar('@');
        } else {
                addstr(dbuf, "pflush");
                switch (BITFIELD(opc, 4, 3)) {
                case 3:
                        addchar('a');
                        break;
                case 2:
                        addchar('a');
                        addchar('n');
                        break;
                case 0:
                        addchar('n');
                        /*FALLTHROUGH*/
                case 1:
                        addchar('\t');
                        PRINT_AREG(dbuf, BITFIELD(opc,2,0));
                        addchar('@');
                        break;
                }
        }
        *dbuf->casm = 0;
}

/*
 * disassemble long format (64b) divs/muls divu/mulu opcode.
 * Note: opcode's dbuf->used already accounted for.
 */
static void
opcode_divmul(dis_buffer_t *dbuf, u_short opc)
{
        u_short ext;
        int iq, hr;

        ext = *(dbuf->val + 1);
        dbuf->used++;

        iq = BITFIELD(ext,14,12);
        hr = BITFIELD(ext,2,0);

        if (IS_INST(DIVSL,opc))
                addstr(dbuf, "div");
        else
                addstr(dbuf, "mul");
        if (ISBITSET(ext,11))
                addchar('s');
        else
                addchar('u');
        addchar('l');
        if (IS_INST(DIVSL,opc) && !ISBITSET(ext,10) && iq != hr)
                addchar('l');
        addchar('\t');

        get_modregstr(dbuf,5,GETMOD_BEFORE,SIZE_LONG,1);
        addchar(',');

        if (ISBITSET(ext,10) ||
            (iq != hr && IS_INST(DIVSL,opc))) {
                /* 64 bit version */
                PRINT_DREG(dbuf, hr);
                if (dbuf->mit)
                        addchar(',');
                else
                        addchar(':');
        }
        PRINT_DREG(dbuf, iq);
}

static void
print_reglist(dis_buffer_t *dbuf, int mod, u_short rl)
{
        static const char *const regs[16] = {
                "d0","d1","d2","d3","d4","d5","d6","d7",
                "a0","a1","a2","a3","a4","a5","a6","a7" };
        int bit, list;

        if (mod == AR_DEC) {
                list = rl;
                rl = 0;
                /* I am sure there is some trick... */
                for (bit = 0; bit < 16; bit++)
                        if (list & (1 << bit))
                                rl |= (0x8000 >> bit);
        }
        for (bit = 0, list = 0; bit < 16; bit++) {
                if (ISBITSET(rl,bit) && bit != 8) {
                        if (list == 0) {
                                list = 1;
                                addstr(dbuf, regs[bit]);
                        } else if (list == 1) {
                                list++;
                                addchar('-');
                        }
                } else {
                        if (list) {
                                if (list > 1)
                                        addstr(dbuf, regs[bit-1]);
                                addchar('/');
                                list = 0;
                        }
                        if (ISBITSET(rl,bit)) {
                                addstr(dbuf, regs[bit]);
                                list = 1;
                        }
                }
        }
        if (list > 1)
                addstr(dbuf, regs[15]);

        if (dbuf->casm[-1] == '/' || dbuf->casm[-1] == '-')
                dbuf->casm--;
        *dbuf->casm = 0;
}

static void
print_freglist(dis_buffer_t *dbuf, int mod, u_short rl, int cntl)
{
        const char *const * regs;
        int bit, list, upper;

        regs = cntl ? fpcregs : fpregs;
        upper = cntl ? 3 : 8;

        if (!cntl && mod != AR_DEC) {
                list = rl;
                rl = 0;
                /* I am sure there is some trick... */
                for (bit = 0; bit < upper; bit++)
                        if (list & (1 << bit))
                                rl |= (0x80 >> bit);
        }
        for (bit = 0, list = 0; bit < upper; bit++) {
                if (ISBITSET(rl,bit)) {
                        if (list == 0) {
                                addstr(dbuf, regs[bit]);
                                if (cntl)
                                        addchar('/');
                                else
                                        list = 1;
                        } else if (list == 1) {
                                list++;
                                addchar('-');
                        }
                } else {
                        if (list) {
                                if (list > 1)
                                        addstr(dbuf, regs[bit-1]);
                                addchar('/');
                                list = 0;
                        }
                }
        }
        if (list > 1)
                addstr(dbuf, regs[upper-1]);

        if (dbuf->casm[-1] == '/' || dbuf->casm[-1] == '-')
                dbuf->casm--;
        *dbuf->casm = 0;
}

/*
 * disassemble movem opcode.
 */
static void
opcode_movem(dis_buffer_t *dbuf, u_short opc)
{
        u_short rl;

        rl = *(dbuf->val + 1);
        dbuf->used++;

        if (ISBITSET(opc,6))
                addstr(dbuf, "movml\t");
        else
                addstr(dbuf, "movmw\t");
        if (ISBITSET(opc,10)) {
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 1);
                addchar(',');
                print_reglist(dbuf, BITFIELD(opc,5,3), rl);
        } else {
                print_reglist(dbuf, BITFIELD(opc,5,3), rl);
                addchar(',');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, 0, 1);
        }
}

/*
 * disassemble movec opcode.
 */
static void
opcode_movec(dis_buffer_t *dbuf, u_short opc)
{
        const char *tmp;
        u_short ext;

        ext = *(dbuf->val + 1);
        dbuf->used++;

        addstr(dbuf, "movc\t");
        if (ISBITSET(opc,0)) {
                dbuf->val++;
                if (ISBITSET(ext,15))
                        get_modregstr(dbuf,14,AR_DIR,0,0);
                else
                        get_modregstr(dbuf,14,DR_DIR,0,0);
                dbuf->val--;
                addchar(',');
        }
        switch (BITFIELD(ext,11,0)) {
                /* 010/020/030/040/CPU32/060 */
        case 0x000:
                tmp = "sfc";
                break;
        case 0x001:
                tmp = "dfc";
                break;
        case 0x800:
                tmp = "usp";
                break;
        case 0x801:
                tmp = "vbr";
                break;
                /* 020/030 */
        case 0x802:
                tmp = "caar";
                break;
                /* 020/030/040/060 */
        case 0x002:
                tmp = "cacr";
                break;
                /* 020/030/040 */
        case 0x803:
                tmp = "msp";
                break;
        case 0x804:
                tmp = "isp";
                break;
                /* 040/060 */
        case 0x003:
                tmp = "tc";
                break;
        case 0x004:
                tmp = "itt0";
                break;
        case 0x005:
                tmp = "itt1";
                break;
        case 0x006:
                tmp = "dtt0";
                break;
        case 0x007:
                tmp = "dtt1";
                break;
                /* 040 */
        case 0x805:
                tmp = "mmusr";
                break;
                /* 040/060 */
        case 0x806:
                tmp = "urp";
                break;
        case 0x807:
                tmp = "srp";
                break;
                /* 060 */
        case 0x008:
                tmp = "buscr";
                break;
        case 0x808:
                tmp = "pcr";
                break;
        default:
                tmp = "INVALID";
                break;
        }
        addstr(dbuf, tmp);
        if (!ISBITSET(opc,0)) {
                dbuf->val++;
                addchar(',');
                if (ISBITSET(ext,15))
                        get_modregstr(dbuf,14,AR_DIR,0,0);
                else
                        get_modregstr(dbuf,14,DR_DIR,0,0);
                dbuf->val--;
        }
}

/*
 * disassemble move16 opcode.
 */
static void
opcode_move16(dis_buffer_t *dbuf, u_short opc)
{
        u_short ext;

        addstr(dbuf, "move16\t");

        if (ISBITSET(opc, 5)) {
                PRINT_AREG(dbuf, BITFIELD(opc,2,0));
                addstr(dbuf, "@+,");
                ext = *(dbuf->val + 1);
                PRINT_AREG(dbuf, BITFIELD(ext,14,12));
                addstr(dbuf, "@+");
                dbuf->used++;
        } else {
                switch (BITFIELD(opc,4,3)) {
                case 0:
                        PRINT_AREG(dbuf, BITFIELD(opc,2,0));
                        addstr(dbuf, "@+,");
                        get_immed(dbuf, SIZE_LONG);
                        break;
                case 1:
                        get_immed(dbuf, SIZE_LONG);
                        addchar(',');
                        PRINT_AREG(dbuf, BITFIELD(opc,2,0));
                        addstr(dbuf, "@+");
                        break;
                case 2:
                        PRINT_AREG(dbuf, BITFIELD(opc,2,0));
                        addstr(dbuf, "@,");
                        get_immed(dbuf, SIZE_LONG);
                        break;
                case 3:
                        get_immed(dbuf, SIZE_LONG);
                        addchar(',');
                        PRINT_AREG(dbuf, BITFIELD(opc,2,0));
                        addchar('@');
                        break;
                }
        }
}

/*
 * copy const string 's' into ``dbuf''->casm
 */
static void
addstr(dis_buffer_t *dbuf, const char *s)
{
        while ((*dbuf->casm++ = *s++))
                ;
        dbuf->casm--;
}

/*
 * copy const string 's' into ``dbuf''->cinfo
 */
static void
iaddstr(dis_buffer_t *dbuf, const char *s)
{
        while ((*dbuf->cinfo++ = *s++))
                ;
        dbuf->cinfo--;
}

static void
get_modregstr_moto(dis_buffer_t *dbuf, int bit, int mod, int sz, int dd)
{
        u_char scale, idx;
        const short *nval;
        u_short ext;
        int disp, odisp, bd, od, reg;

        odisp = 0;

        /* check to see if we have been given the mod */
        if (mod != GETMOD_BEFORE && mod != GETMOD_AFTER)
                reg = BITFIELD(*dbuf->val, bit, bit-2);
        else if (mod == GETMOD_BEFORE) {
                mod = BITFIELD(*dbuf->val, bit, bit-2);
                reg = BITFIELD(*dbuf->val, bit-3, bit-5);
        } else {
                reg = BITFIELD(*dbuf->val, bit, bit-2);
                mod = BITFIELD(*dbuf->val, bit-3, bit-5);
        }
        switch (mod) {
        case DR_DIR:
        case AR_DIR:
                if (mod == DR_DIR)
                        PRINT_DREG(dbuf, reg);
                else
                        PRINT_AREG(dbuf, reg);
                break;
        case AR_DIS:
                print_disp(dbuf, *(dbuf->val + 1 + dd), SIZE_WORD,reg);
                dbuf->used++;
                /*FALLTHROUGH*/
        case AR_IND:
        case AR_INC:
        case AR_DEC:
                if (mod == AR_DEC)
                        addchar('-');
                addchar('(');
                PRINT_AREG(dbuf, reg);
                addchar(')');
                if (mod == AR_INC)
                        addchar('+');
                break;
        /* mod 6 & 7 are the biggies. */
        case MOD_SPECIAL:
                if (reg == 0) {
                        /* abs short addr */
                        print_addr(dbuf, *(dbuf->val + 1 + dd));
                        dbuf->used++;
                        addchar('.');
                        addchar('w');
                        break;
                } else if (reg == 1) {
                        /* abs long addr */
                        print_addr(dbuf, *(u_long *)(dbuf->val + 1 + dd));
                        dbuf->used += 2;
                        addchar('.');
                        addchar('l');
                        break;
                } else if (reg == 2) {
                        /* pc ind displ. xxx(PC) */
                        dbuf->used++;
                        print_disp(dbuf, *(dbuf->val + 1 + dd), SIZE_WORD,
                                   -1);
                        addstr(dbuf,"(pc)");
                        break;
                } else if (reg == 4) {
                        /* uses ``sz'' to figure immediate data. */
                        if (sz == SIZE_BYTE) {
                                addchar('#');
                                prints(dbuf,
                                    *((char *)dbuf->val + 3+ (dd * 2)), sz);
                                dbuf->used++;
                        } else if (sz == SIZE_WORD) {
                                addchar('#');
                                prints(dbuf, *(dbuf->val + 1 + dd), sz);
                                dbuf->used++;
                        } else if (sz == SIZE_LONG) {
                                addchar('#');
                                prints(dbuf, *(long *)(dbuf->val + 1 + dd),
                                    sz);
                                dbuf->used += 2;
                        } else if (sz == SIZE_QUAD) {
                                dbuf->used += 4;
                                addstr(dbuf,"#<quad>");
                        } else if (sz == SIZE_SINGLE) {
                                dbuf->used += 2;
                                addstr(dbuf,"#<single>");
                        } else if (sz == SIZE_DOUBLE) {
                                dbuf->used += 4;
                                addstr(dbuf,"#<double>");
                        } else if (sz == SIZE_PACKED) {
                                dbuf->used += 6;
                                addstr(dbuf,"#<packed>");
                        } else if (sz == SIZE_EXTENDED) {
                                dbuf->used += 6;
                                addstr(dbuf,"#<extended>");
                        }
                        break;
                }
                /* standrd PC stuff. */
                /*FALLTHROUGH*/
        case AR_IDX:
                ext = *(dbuf->val + 1 + dd);
                dbuf->used++;
                nval = dbuf->val + 2 + dd; /* set to possible displacements */
                scale = BITFIELD(ext,10,9);
                idx = BITFIELD(ext,14,12);

                if (ISBITSET(ext,8)) {
                        /* either base disp, or memory indirect */
                        bd = BITFIELD(ext,5,4);
                        od = BITFIELD(ext,1,0);
                        if (bd == 1)
                                disp = 0;
                        else if (bd == 2) {
                                dbuf->used++;
                                disp = *nval++;
                        } else {
                                dbuf->used += 2;
                                disp = *(const long *)nval;
                                nval += 2;
                        }

                        if (od == 1)
                                odisp = 0;
                        else if (od == 2) {
                                dbuf->used++;
                                odisp = *nval++;
                        } else if (od == 3) {
                                dbuf->used += 2;
                                odisp = *(const long *)nval;
                                nval += 2;
                        }
                } else {
                        /*
                         * We set od and bd to zero, these values are
                         * not allowed in opcodes that use base and
                         * outer displacement, e.g. we can tell if we
                         * are using on of those modes by checking
                         * `bd' and `od'.
                         */
                        od = 0;
                        bd = 0;
                        disp = (char)BITFIELD(ext,7,0);
                }
                /*
                 * write everything into buf
                 */
                addchar('(');
                if (od)
                        addchar('['); /* begin memory indirect xxx-indexed */
                prints(dbuf, disp,
                    bd == 2 ? SIZE_WORD :
                    bd == 3 ? SIZE_LONG :
                    SIZE_BYTE);
                addchar(',');
                if (bd && ISBITSET(ext,7)) {
                        addchar('z');
                        if (mod != MOD_SPECIAL)
                                PRINT_AREG(dbuf,reg);
                        else {
                                addchar('p');
                                addchar('c');
                        }
                } else if (mod == AR_IDX)
                        PRINT_AREG(dbuf, reg);
                else {
                        addchar('p');
                        addchar('c');
                }

                if (od && ISBITSET(ext,2))
                        addchar(']'); /* post-indexed. */
                addchar(',');
                if (bd && ISBITSET(ext,6))
                        addchar('0');
                else {
                        if (0x8000 & ext)
                                PRINT_AREG(dbuf, idx);
                        else
                                PRINT_DREG(dbuf, idx);
                        addchar('.');
                        addchar(0x800 & ext ? 'l' : 'w');
                        if (scale) {
                                addchar('*');
                                addchar('0' + (1 << scale));
                        }
                }
                if (od) {
                        if (!ISBITSET(ext,2))
                                addchar(']'); /* pre-indexed */
                        addchar(',');
                        prints(dbuf, odisp,
                            od == 2 ? SIZE_WORD :
                            od == 3 ? SIZE_LONG :
                            SIZE_BYTE);
                }
                addchar(')');
                break;
        }
        *dbuf->casm = 0;
}

/* mit syntax makes for spaghetti parses */
static void
get_modregstr_mit(dis_buffer_t *dbuf, int bit, int mod, int sz, int dd)
{
        u_char scale, idx;
        const short *nval;
        u_short ext;
        int disp, odisp, bd, od, reg;

        disp = odisp = 0;
        /* check to see if we have been given the mod */
        if (mod != GETMOD_BEFORE && mod != GETMOD_AFTER)
                reg = BITFIELD(*dbuf->val, bit, bit-2);
        else if (mod == GETMOD_BEFORE) {
                mod = BITFIELD(*dbuf->val, bit, bit-2);
                reg = BITFIELD(*dbuf->val, bit-3, bit-5);
        } else {
                reg = BITFIELD(*dbuf->val, bit, bit-2);
                mod = BITFIELD(*dbuf->val, bit-3, bit-5);
        }
        switch (mod) {
        case DR_DIR:
        case AR_DIR:
                if (mod == DR_DIR)
                        PRINT_DREG(dbuf, reg);
                else
                        PRINT_AREG(dbuf, reg);
                break;
        case AR_DIS:
                dbuf->used++;   /* tell caller we used an ext word. */
                disp = *(dbuf->val + 1 + dd);
                /*FALLTHROUGH*/
        case AR_IND:
        case AR_INC:
        case AR_DEC:
                PRINT_AREG(dbuf, reg);
                addchar('@' );
                if (mod == AR_DEC)
                        addchar('-');
                else if (mod == AR_INC)
                        addchar('+');
                else if (mod == AR_DIS) {
                        addchar('(');
                        print_disp(dbuf, disp, SIZE_WORD, reg);
                        addchar(')');
                }
                break;
        /* mod 6 & 7 are the biggies. */
        case MOD_SPECIAL:
                if (reg == 0) {
                        /* abs short addr */
                        print_addr(dbuf, *(dbuf->val + 1 + dd));
                        dbuf->used++;
                        break;
                } else if (reg == 1) {
                        /* abs long addr */
                        print_addr(dbuf, *(u_long *)(dbuf->val + 1 + dd));
                        dbuf->used += 2;
                        break;
                } else if (reg == 2) {
                        /* pc ind displ. pc@(xxx) */
                        addstr(dbuf,"pc@(");
                        print_disp(dbuf, *(dbuf->val + 1 + dd), SIZE_WORD, -1);
                        dbuf->used++;
                        addchar(')');
                        break;
                } else if (reg == 4) {
                        /* uses ``sz'' to figure immediate data. */
                        if (sz == SIZE_BYTE) {
                                addchar('#');
                                prints(dbuf,
                                    *((char *)dbuf->val + 3 + (dd * 2)), sz);
                                dbuf->used++;
                        } else if (sz == SIZE_WORD) {
                                addchar('#');
                                prints(dbuf, *(dbuf->val + 1 + dd), sz);
                                dbuf->used++;
                        } else if (sz == SIZE_LONG) {
                                addchar('#');
                                prints(dbuf, *(long *)(dbuf->val + 1 + dd),
                                    sz);
                                dbuf->used += 2;
                        } else if (sz == SIZE_QUAD) {
                                dbuf->used += 4;
                                addstr(dbuf,"#<quad>");
                        } else if (sz == SIZE_SINGLE) {
                                dbuf->used += 2;
                                addstr(dbuf,"#<single>");
                        } else if (sz == SIZE_DOUBLE) {
                                dbuf->used += 4;
                                addstr(dbuf,"#<double>");
                        } else if (sz == SIZE_PACKED) {
                                dbuf->used += 6;
                                addstr(dbuf,"#<packed>");
                        } else if (sz == SIZE_EXTENDED) {
                                dbuf->used += 6;
                                addstr(dbuf,"#<extended>");
                        }
                        break;
                }
                /* standrd PC stuff. */
                /*FALLTHROUGH*/
        case AR_IDX:
                dbuf->used++;   /* indicate use of ext word. */
                ext = *(dbuf->val + 1 + dd);
                nval = dbuf->val + 2 + dd; /* set to possible displacements */
                scale = BITFIELD(ext,10,9);
                idx = BITFIELD(ext,14,12);

                if (ISBITSET(ext,8)) {
                        /* either base disp, or memory indirect */
                        bd = BITFIELD(ext,5,4);
                        od = BITFIELD(ext,1,0);
                        if (bd == 1)
                                disp = 0;
                        else if (bd == 2) {
                                dbuf->used++;
                                disp = *nval++;
                        } else {
                                dbuf->used += 2;
                                disp = *(const long *)nval;
                                nval += 2;
                        }

                        if (od == 1)
                                odisp = 0;
                        else if (od == 2) {
                                dbuf->used++;
                                odisp = *nval++;
                        } else if (od == 3) {
                                dbuf->used += 2;
                                odisp = *(const long *)nval;
                                nval += 2;
                        }
                } else {
                        /*
                         * We set od and bd to zero, these values are
                         * not allowed in opcodes that use base and
                         * outer displacement, e.g. we can tell if we
                         * are using on of those modes by checking
                         * `bd' and `od'.
                         */
                        od = 0;
                        bd = 0;
                        disp = (char)BITFIELD(ext,7,0);
                }
                /*
                 * write everything into buf
                 */
                /* if base register not suppresed */
                if (mod == AR_IDX && (!bd || !ISBITSET(ext,7)))
                        PRINT_AREG(dbuf, reg);
                else if (mod == MOD_SPECIAL && ISBITSET(ext,7)) {
                        addchar('z');
                        addchar('p');
                        addchar('c');
                } else if (mod == MOD_SPECIAL) {
                        addchar('p');
                        addchar('c');
                }
                addchar('@');
                addchar('(');

                if (bd && bd != 1) {
                        prints(dbuf, disp,
                            bd == 2 ? SIZE_WORD :
                            bd == 3 ? SIZE_LONG :
                            SIZE_BYTE);
                        if (od && !ISBITSET(ext,6) && !ISBITSET(ext,2))
                                /* Pre-indexed and not suppressing index */
                                addchar(',');
                        else if (od && ISBITSET(ext,2)) {
                                /* Post-indexed */
                                addchar(')');
                                addchar('@');
                                addchar('(');
                        } else if (!od)
                                addchar(',');
                } else if (!bd) {
                        /* don't forget simple 8 bit displacement. */
                        prints(dbuf, disp,
                            bd == 2 ? SIZE_WORD :
                            bd == 3 ? SIZE_LONG :
                            SIZE_BYTE);
                        addchar(',');
                }

                /* Post-indexed? */
                if (od && ISBITSET(ext,2)) {
                        /* have displacement? */
                        if (od != 1) {
                                prints(dbuf, odisp,
                                    od == 2 ? SIZE_WORD :
                                    od == 3 ? SIZE_LONG :
                                    SIZE_BYTE);
                                addchar(',');
                        }
                }

                if (!bd || !ISBITSET(ext,6)) {
                        if (ISBITSET(ext,15))
                                PRINT_AREG(dbuf,idx);
                        else
                                PRINT_DREG(dbuf,idx);
                        addchar(':');
                        addchar(ISBITSET(ext,11) ? 'l' : 'w');
                        if (scale) {
                                addchar(':');
                                addchar('0' + (1 << scale));
                        }
                }
                /* pre-indexed? */
                if (od && !ISBITSET(ext,2)) {
                        if (od != 1) {
                                addchar(')');
                                addchar('@');
                                addchar('(');
                                prints(dbuf, odisp,
                                    od == 2 ? SIZE_WORD :
                                    od == 3 ? SIZE_LONG :
                                    SIZE_BYTE);
                        }
                }
                addchar(')');
                break;
        }
        *dbuf->casm = 0;
}

/*
 * Given a disassembly buffer ``dbuf'' and a starting bit of the
 * mod|reg field ``bit'' (or just a reg field if ``mod'' is not
 * GETMOD_BEFORE or GETMOD_AFTER), disassemble and write into ``dbuf''
 * the mod|reg pair.
 */
static void
get_modregstr(dis_buffer_t *dbuf, int bit, int mod, int sz, int dispdisp)
{
        if (dbuf->mit)
                get_modregstr_mit(dbuf,bit,mod,sz,dispdisp);
        else
                get_modregstr_moto(dbuf,bit,mod,sz,dispdisp);
}

/*
 * given a bit position ``bit'' in the current ``dbuf''->val
 * and the ``base'' string of the opcode, append the full
 * opcode name including condition found at ``bit''.
 */
static void
make_cond(dis_buffer_t *dbuf, int bit, const char *base)
{
        int cc;
        const char *ccs;

        cc = BITFIELD(*dbuf->val,bit,bit-3);
        ccs = cc_table[cc&15];

        addstr(dbuf, base);
        addstr(dbuf, ccs);
}

static void
print_fcond(dis_buffer_t *dbuf, char cp)
{
        addstr(dbuf,fpcc_table[cp&31]);         /* XXX - not 63 ?*/
}

static void
print_mcond(dis_buffer_t *dbuf, char cp)
{
        addstr(dbuf,mmcc_table[cp&15]);
}

/*
 * given dis_buffer_t ``dbuf'' get the immediate value from the
 * extension words following current instruction, output a
 * hash (``#'') sign and the value.  Increment the ``dbuf''->used
 * field accordingly.
 */
static void
get_immed(dis_buffer_t *dbuf,int sz)
{
        addchar('#');
        switch (sz) {
        case SIZE_BYTE:
                prints(dbuf, BITFIELD(*(dbuf->val + 1),7,0), SIZE_BYTE);
                dbuf->used++;
                break;
        case SIZE_WORD:
                prints(dbuf, *(dbuf->val + 1), SIZE_WORD);
                dbuf->used++;
                break;
        case SIZE_LONG:
                prints(dbuf, *(long *)(dbuf->val + 1), SIZE_LONG);
                dbuf->used += 2;
                break;
        }
        return;
}

static void
get_fpustdGEN(dis_buffer_t *dbuf, u_short ext, const char *name)
{
        int sz;

        /*
         * If bit seven is set, its a 040 s/d opcode, then if bit 2 is
         * set its "d".  This is not documented, however thats the way
         * it is.
         */

        sz = 0;
        addchar(*name++);
        if (ISBITSET(ext,7)) {
                if(ISBITSET(ext,2))
                        addchar('d');
                else
                        addchar('s');
        }
        addstr(dbuf,name);

        if (ISBITSET(ext,14)) {
                switch (BITFIELD(ext,12,10)) {
                case 0:
                        addchar('l');
                        sz = SIZE_LONG;
                        break;
                case 1:
                        addchar('s');
                        sz = SIZE_SINGLE;
                        break;
                case 2:
                        addchar('x');
                        sz = SIZE_EXTENDED;
                        break;
                case 3:
                        addchar('p');
                        sz = SIZE_PACKED;
                        break;
                case 4:
                        addchar('w');
                        sz = SIZE_WORD;
                        break;
                case 5:
                        addchar('d');
                        sz = SIZE_DOUBLE;
                        break;
                case 6:
                        addchar('b');
                        sz = SIZE_BYTE;
                        break;
                }
                addchar('\t');
                get_modregstr(dbuf, 5, GETMOD_BEFORE, sz, 1);
                if (BITFIELD(ext,6,3) == 6) {
                        addchar(',');
                        PRINT_FPREG(dbuf, BITFIELD(ext,2,0));
                        addchar(':');
                        PRINT_FPREG(dbuf, BITFIELD(ext,9,7));
                } else if (BITFIELD(ext,5,0) != FTST) {
                        addchar(',');
                        PRINT_FPREG(dbuf, BITFIELD(ext,9,7));
                }
        } else {
                addchar('x');
                addchar('\t');
                PRINT_FPREG(dbuf, BITFIELD(ext,12,10));
                if (BITFIELD(ext,6,3) == 6) {
                        addchar(',');
                        PRINT_FPREG(dbuf, BITFIELD(ext,2,0));
                        addchar(':');
                        PRINT_FPREG(dbuf, BITFIELD(ext,9,7));
                } else if (BITFIELD(ext,5,0) != FTST) {
                        addchar(',');
                        PRINT_FPREG(dbuf, BITFIELD(ext,9,7));
                }
        }
}

#if 0
static u_long
get_areg_val(int reg)
{
        return 0;
}
#endif

/*
 * given value ``disp'' print it to ``dbuf''->buf. ``rel'' is a
 * register number 0-7 (a0-a7), or -1 (pc). Thus possible extra info
 * could be output to the ``dbuf''->info buffer.
 */
static void
print_disp(dis_buffer_t *dbuf, int disp, int sz, int rel)
{
        db_expr_t diff;
        db_sym_t sym;
        const char *symname;
        u_long nv;

        prints(dbuf, disp, sz);

        if (rel == -1)
                /* XXX This may be wrong for a couple inst. */
                nv = disp + (u_int)dbuf->val + 2;
        else
                return; /* nv = get_areg_val(rel); */

        diff = INT_MAX;
        symname = NULL;
        sym = db_search_symbol(nv, DB_STGY_PROC, &diff);
        db_symbol_values(sym, &symname, 0);

        if (symname) {
                iaddstr(dbuf, "disp:");
                iaddstr(dbuf, symname);
                iaddchar('+');
                iprintu(dbuf, diff, SIZE_LONG);
                iaddchar(' ');
                *dbuf->cinfo = 0;
        }
}

static void
print_addr(dis_buffer_t *dbuf, u_long addr)
{
        db_expr_t diff;
        db_sym_t sym;
        const char *symname;

        diff = INT_MAX;
        symname = NULL;
        sym = db_search_symbol(addr, DB_STGY_ANY, &diff);
        db_symbol_values(sym, &symname, 0);

        if (symname) {
                if (diff == 0)
                        addstr(dbuf,symname);
                else {
                        addchar('<');
                        addstr(dbuf,symname);
                        addchar('+');
                        printu(dbuf, diff, SIZE_LONG);
                        addchar('>');
                        *dbuf->casm = 0;
                }
                iaddstr(dbuf,"addr:");
                iprintu(dbuf, addr, SIZE_LONG);
                iaddchar(' ');
                *dbuf->cinfo = 0;
        } else {
                printu(dbuf, addr, SIZE_LONG);
        }
}

static void
prints(dis_buffer_t *dbuf, int val, int sz)
{
        extern int db_radix;

        if (val == 0) {
                dbuf->casm[0] = '0';
                dbuf->casm[1] = 0;
        } else if (sz == SIZE_BYTE)
                prints_wb(dbuf, (char)val, sz, db_radix);
        else if (sz == SIZE_WORD)
                prints_wb(dbuf, (short)val, sz, db_radix);
        else
                prints_wb(dbuf, (long)val, sz, db_radix);

        dbuf->casm = &dbuf->casm[strlen(dbuf->casm)];
}

#if 0
static void
iprints(dis_buffer_t *dbuf, int val, int sz)
{
        extern int db_radix;

        if (val == 0) {
                dbuf->cinfo[0] = '0';
                dbuf->cinfo[1] = 0;
        } else if (sz == SIZE_BYTE)
                iprints_wb(dbuf, (char)val, sz, db_radix);
        else if (sz == SIZE_WORD)
                iprints_wb(dbuf, (short)val, sz, db_radix);
        else
                iprints_wb(dbuf, (long)val, sz, db_radix);

        dbuf->cinfo = &dbuf->cinfo[strlen(dbuf->cinfo)];
}
#endif

static void
printu(dis_buffer_t *dbuf, u_int val, int sz)
{
        extern int db_radix;

        if (val == 0) {
                dbuf->casm[0] = '0';
                dbuf->casm[1] = 0;
        } else if (sz == SIZE_BYTE)
                printu_wb(dbuf, (u_char)val, sz, db_radix);
        else if (sz == SIZE_WORD)
                printu_wb(dbuf, (u_short)val, sz, db_radix);
        else
                printu_wb(dbuf, (u_long)val, sz, db_radix);
        dbuf->casm = &dbuf->casm[strlen(dbuf->casm)];
}

static void
iprintu(dis_buffer_t *dbuf, u_int val, int sz)
{
        extern int db_radix;

        if (val == 0) {
                dbuf->cinfo[0] = '0';
                dbuf->cinfo[1] = 0;
        } else if (sz == SIZE_BYTE)
                iprintu_wb(dbuf, (u_char)val, sz, db_radix);
        else if (sz == SIZE_WORD)
                iprintu_wb(dbuf, (u_short)val, sz, db_radix);
        else
                iprintu_wb(dbuf, (u_long)val, sz, db_radix);
        dbuf->cinfo = &dbuf->cinfo[strlen(dbuf->cinfo)];
}

static void
printu_wb(dis_buffer_t *dbuf, u_int val, int sz, int base)
{
        static char buf[sizeof(long) * NBBY / 3 + 2];
        char *p, ch;

        if (base != 10) {
                addchar('0');
                if (base != 8) {
                        base = 16;
                        addchar('x');
                }
        }

        p = buf;
        do {
                *++p = hexdigits[val % base];
        } while (val /= base);

        while ((ch = *p--))
                addchar(ch);

        *dbuf->casm = 0;
}

static void
prints_wb(dis_buffer_t *dbuf, int val, int sz, int base)
{
        if (val < 0) {
                addchar('-');
                val = -val;
        }
        printu_wb(dbuf, val, sz, base);
}

static void
iprintu_wb(dis_buffer_t *dbuf, u_int val, int sz, int base)
{
        static char buf[sizeof(long) * NBBY / 3 + 2];
        char *p, ch;

        if (base != 10) {
                iaddchar('0');
                if (base != 8) {
                        base = 16;
                        iaddchar('x');
                }
        }

        p = buf;
        do {
                *++p = hexdigits[val % base];
        } while (val /= base);

        while ((ch = *p--))
                iaddchar(ch);

        *dbuf->cinfo = 0;
}

#if 0
static void
iprints_wb(dis_buffer_t *dbuf, int val, int sz, int base)
{
        if (val < 0) {
                iaddchar('-');
                val = -val;
        }
        iprintu_wb(dbuf, val, sz, base);
}
#endif

static void
prints_bf(dis_buffer_t *dbuf, int val, int sb, int eb)
{
        if (ISBITSET(val,sb))
                val = (~0 & ~BITFIELD(~0, sb, eb)) | BITFIELD(val, sb, eb);
        else
                val = BITFIELD(val,sb,eb);

        prints(dbuf, val, SIZE_LONG);
}

static void
printu_bf(dis_buffer_t *dbuf, u_int val, int sb, int eb)
{
        printu(dbuf,BITFIELD(val,sb,eb),SIZE_LONG);
}