/*******************************************************************************
 * Copyright (c) 2018 Xilinx, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 * You may elect to redistribute this code under either of these licenses.
 *
 * Contributors:
 *     Xilinx - initial API and implementation
 *******************************************************************************/

/*
 * This module implements disassembler for MicroBlaze.
 */

#include <tcf/config.h>

#include <assert.h>
#include <stdio.h>
#include <ctype.h>
#include <tcf/framework/context.h>
#include <tcf/framework/myalloc.h>
#include <tcf/services/symbols.h>
#include <machine/microblaze/tcf/disassembler-microblaze.h>

enum InstructionType {
    INST_TYPE_RD_RA_RB,
    INST_TYPE_RD_RA_IMM,
    INST_TYPE_RD_RA_IMM5,
    INST_TYPE_RD_RA_IMM6,
    INST_TYPE_RD_RA_IMM5_IMM5,
    INST_TYPE_RD_RA_IMM6_IMM6,
    INST_TYPE_RD_RA,
    INST_TYPE_RD_RB,
    INST_TYPE_RD_IMM,
    INST_TYPE_RD_IMM15,
    INST_TYPE_RD_IMM4,
    INST_TYPE_RA_RB,
    INST_TYPE_RA_IMM,
    INST_TYPE_RA_IMML,
    INST_TYPE_RA_IMM4,
    INST_TYPE_RD_SA,
    INST_TYPE_SA_RA,
    INST_TYPE_RA,
    INST_TYPE_RB,
    INST_TYPE_RD,
    INST_TYPE_IMM,
    INST_TYPE_IMM4,
    INST_TYPE_IMML,
    INST_TYPE_RDIMM,
    INST_TYPE_NULL
};

enum InstructionFlags {
    F_CTRL       = 0x00000001,
    F_ABS        = 0x00000002,
    F_INDIRJMP   = 0x00000004,
    F_IMM        = 0x00000008,
};

enum Instructions {
    /* 32-bit instructions */
    i_add,            i_addc,           i_addi,           i_addic,
    i_addik,          i_addikc,         i_addk,           i_addkc,
    i_aget,           i_agetd,          i_and,            i_andi,
    i_andn,           i_andni,          i_aput,           i_aputd,
    i_beq,            i_beqd,           i_beqi,           i_beqid,
    i_bge,            i_bged,           i_bgei,           i_bgeid,
    i_bgt,            i_bgtd,           i_bgti,           i_bgtid,
    i_ble,            i_bled,           i_blei,           i_bleid,
    i_blt,            i_bltd,           i_blti,           i_bltid,
    i_bne,            i_bned,           i_bnei,           i_bneid,
    i_br,             i_bra,            i_brad,           i_brai,
    i_braid,          i_brald,          i_bralid,         i_brd,
    i_bri,            i_brid,           i_brk,            i_brki,
    i_brld,           i_brlid,          i_bsefi,          i_bsifi,
    i_bsll,           i_bslli,          i_bsra,           i_bsrai,
    i_bsrl,           i_bsrli,          i_caget,          i_cagetd,
    i_caput,          i_caputd,         i_cget,           i_cgetd,
    i_clz,            i_cmp,            i_cmpu,           i_cput,
    i_cputd,          i_eaget,          i_eagetd,         i_ecaget,
    i_ecagetd,        i_ecget,          i_ecgetd,         i_eget,
    i_egetd,          i_fadd,           i_fcmp_eq,        i_fcmp_ge,
    i_fcmp_gt,        i_fcmp_le,        i_fcmp_lt,        i_fcmp_ne,
    i_fcmp_un,        i_fdiv,           i_fint,           i_flt,
    i_fmul,           i_frsub,          i_fsqrt,          i_get,
    i_getd,           i_idiv,           i_idivu,          i_imm,
    i_lbu,            i_lbuea,          i_lbui,           i_lbur,
    i_lhu,            i_lhuea,          i_lhui,           i_lhur,
    i_lw,             i_lwea,           i_lwi,            i_lwr,
    i_lwx,            i_mbar,           i_mfs,            i_mfse,
    i_msrclr,         i_msrset,         i_mts,            i_mul,
    i_mulh,           i_mulhsu,         i_mulhu,          i_muli,
    i_naget,          i_nagetd,         i_naput,          i_naputd,
    i_ncaget,         i_ncagetd,        i_ncaput,         i_ncaputd,
    i_ncget,          i_ncgetd,         i_ncput,          i_ncputd,
    i_neaget,         i_neagetd,        i_necaget,        i_necagetd,
    i_necget,         i_necgetd,        i_neget,          i_negetd,
    i_nget,           i_ngetd,          i_nput,           i_nputd,
    i_or,             i_ori,            i_pcmpbf,         i_pcmpeq,
    i_pcmpne,         i_put,            i_putd,           i_rsub,
    i_rsubc,          i_rsubi,          i_rsubic,         i_rsubik,
    i_rsubikc,        i_rsubk,          i_rsubkc,         i_rtbd,
    i_rted,           i_rtid,           i_rtsd,           i_sb,
    i_sbea,           i_sbi,            i_sbr,            i_sext16,
    i_sext8,          i_sh,             i_shea,           i_shi,
    i_shr,            i_sleep,          i_sra,            i_src,
    i_srl,            i_sw,             i_swapb,          i_swaph,
    i_swea,           i_swi,            i_swr,            i_swx,
    i_taget,          i_tagetd,         i_taput,          i_taputd,
    i_tcaget,         i_tcagetd,        i_tcaput,         i_tcaputd,
    i_tcget,          i_tcgetd,         i_tcput,          i_tcputd,
    i_teaget,         i_teagetd,        i_tecaget,        i_tecagetd,
    i_tecget,         i_tecgetd,        i_teget,          i_tegetd,
    i_tget,           i_tgetd,          i_tnaget,         i_tnagetd,
    i_tnaput,         i_tnaputd,        i_tncaget,        i_tncagetd,
    i_tncaput,        i_tncaputd,       i_tncget,         i_tncgetd,
    i_tncput,         i_tncputd,        i_tneaget,        i_tneagetd,
    i_tnecaget,       i_tnecagetd,      i_tnecget,        i_tnecgetd,
    i_tneget,         i_tnegetd,        i_tnget,          i_tngetd,
    i_tnput,          i_tnputd,         i_tput,           i_tputd,
    i_wdc,            i_wdc_clear,      i_wdc_clear_ea,   i_wdc_ext_clear,
    i_wdc_ext_flush,  i_wdc_flush,      i_wic,            i_xor,
    i_xori,
    /* 64-bit instructions */
    i_addl,           i_rsubl,          i_addlc,          i_rsublc,
    i_addlk,          i_rsublk,         i_addlkc,         i_rsublkc,
    i_cmpl,           i_cmplu,          i_addli,          i_rsubli,
    i_addlic,         i_rsublic,        i_addlik,         i_rsublik,
    i_addlikc,        i_rsublikc,       i_mull,
    i_bslll,          i_bslra,          i_bslrl,          i_bsllli,
    i_bslrai,         i_bslrli,         i_bslefi,         i_bslifi,
    i_orl,            i_andl,           i_xorl,           i_andnl,
    i_pcmplbf,        i_pcmpleq,        i_pcmplne,
    i_srla,           i_srlc,           i_srll,
    i_sextl8,         i_sextl16,        i_sextl32,
    i_brea,           i_bread,          i_breald,         i_beaeq,
    i_bealeq,         i_beaeqd,         i_bealeqd,        i_beane,
    i_bealne,         i_beaned,         i_bealned,        i_bealt,
    i_beallt,         i_bealtd,         i_bealltd,        i_beale,
    i_bealle,         i_bealed,         i_bealled,        i_beagt,
    i_bealgt,         i_beagtd,         i_bealgtd,        i_beage,
    i_bealge,         i_beaged,         i_bealged,
    i_orli,           i_andli,          i_xorli,          i_andnli,
    i_imml,
    i_breai,          i_breaid,         i_brealid,        i_beaeqi,
    i_bealeqi,        i_beaeqid,        i_bealeqid,       i_beanei,
    i_bealnei,        i_beaneid,        i_bealneid,       i_bealti,
    i_beallti,        i_bealtid,        i_bealltid,       i_bealei,
    i_beallei,        i_bealeid,        i_bealleid,       i_beagti,
    i_bealgti,        i_beagtid,        i_bealgtid,       i_beagei,
    i_bealgei,        i_beageid,        i_bealgeid,
    i_ll,             i_llr,            i_sl,             i_slr,
    i_dadd,           i_drsub,          i_dmul,           i_ddiv,
    i_dcmp_lt,        i_dcmp_eq,        i_dcmp_le,        i_dcmp_gt,
    i_dcmp_ne,        i_dcmp_ge,        i_dcmp_un,        i_dbl,
    i_dlong,          i_dsqrt,
    i_lli,            i_sli,
};

typedef struct InstructionInfo {
    unsigned hash;
    enum Instructions op;
    const char * name;
    unsigned type;
    unsigned flags;
} InstructionInfo;

#define OP(x) i_##x, #x

static InstructionInfo instruction_info[] = {
    { 0x00, OP(add),            INST_TYPE_RD_RA_RB, 0 },
    { 0x02, OP(addc),           INST_TYPE_RD_RA_RB, 0 },
    { 0x08, OP(addi),           INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0a, OP(addic),          INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0c, OP(addik),          INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0e, OP(addikc),         INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x04, OP(addk),           INST_TYPE_RD_RA_RB, 0 },
    { 0x06, OP(addkc),          INST_TYPE_RD_RA_RB, 0 },
    { 0x1b, OP(aget),           INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(agetd),          INST_TYPE_RD_RB, 0 },
    { 0x21, OP(and),            INST_TYPE_RD_RA_RB, 0 },
    { 0x29, OP(andi),           INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x23, OP(andn),           INST_TYPE_RD_RA_RB, 0 },
    { 0x2b, OP(andni),          INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x1b, OP(aput),           INST_TYPE_RA_IMM4, F_IMM },
    { 0x13, OP(aputd),          INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beq),            INST_TYPE_RA_RB, F_CTRL },
    { 0x27, OP(beqd),           INST_TYPE_RA_RB, F_CTRL },
    { 0x2f, OP(beqi),           INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x2f, OP(beqid),          INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x27, OP(bge),            INST_TYPE_RA_RB, F_CTRL },
    { 0x27, OP(bged),           INST_TYPE_RA_RB, F_CTRL },
    { 0x2f, OP(bgei),           INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x2f, OP(bgeid),          INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x27, OP(bgt),            INST_TYPE_RA_RB, F_CTRL },
    { 0x27, OP(bgtd),           INST_TYPE_RA_RB, F_CTRL },
    { 0x2f, OP(bgti),           INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x2f, OP(bgtid),          INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x27, OP(ble),            INST_TYPE_RA_RB, F_CTRL },
    { 0x27, OP(bled),           INST_TYPE_RA_RB, F_CTRL },
    { 0x2f, OP(blei),           INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x2f, OP(bleid),          INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x27, OP(blt),            INST_TYPE_RA_RB, F_CTRL },
    { 0x27, OP(bltd),           INST_TYPE_RA_RB, F_CTRL },
    { 0x2f, OP(blti),           INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x2f, OP(bltid),          INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x27, OP(bne),            INST_TYPE_RA_RB, F_CTRL },
    { 0x27, OP(bned),           INST_TYPE_RA_RB, F_CTRL },
    { 0x2f, OP(bnei),           INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x2f, OP(bneid),          INST_TYPE_RA_IMM, F_CTRL|F_IMM },
    { 0x26, OP(br),             INST_TYPE_RB, F_CTRL },
    { 0x26, OP(bra),            INST_TYPE_RB, F_CTRL },
    { 0x26, OP(brad),           INST_TYPE_RB, F_CTRL },
    { 0x2e, OP(brai),           INST_TYPE_IMM, F_CTRL|F_ABS|F_IMM },
    { 0x2e, OP(braid),          INST_TYPE_IMM, F_CTRL|F_ABS|F_IMM },
    { 0x26, OP(brald),          INST_TYPE_RD_RB, F_CTRL },
    { 0x2e, OP(bralid),         INST_TYPE_RD_IMM, F_CTRL|F_ABS|F_IMM },
    { 0x26, OP(brd),            INST_TYPE_RB, F_CTRL },
    { 0x2e, OP(bri),            INST_TYPE_IMM, F_CTRL|F_IMM },
    { 0x2e, OP(brid),           INST_TYPE_IMM, F_CTRL|F_IMM },
    { 0x26, OP(brk),            INST_TYPE_RD_RB, F_CTRL },
    { 0x2e, OP(brki),           INST_TYPE_RD_IMM, F_CTRL|F_IMM },
    { 0x26, OP(brld),           INST_TYPE_RD_RB, F_CTRL },
    { 0x2e, OP(brlid),          INST_TYPE_RD_IMM, F_CTRL|F_IMM },
    { 0x19, OP(bsefi),          INST_TYPE_RD_RA_IMM5_IMM5, F_IMM },
    { 0x19, OP(bsifi),          INST_TYPE_RD_RA_IMM5_IMM5, F_IMM },
    { 0x11, OP(bsll),           INST_TYPE_RD_RA_RB, 0 },
    { 0x19, OP(bslli),          INST_TYPE_RD_RA_IMM5, F_IMM },
    { 0x11, OP(bsra),           INST_TYPE_RD_RA_RB, 0 },
    { 0x19, OP(bsrai),          INST_TYPE_RD_RA_IMM5, F_IMM },
    { 0x11, OP(bsrl),           INST_TYPE_RD_RA_RB, 0 },
    { 0x19, OP(bsrli),          INST_TYPE_RD_RA_IMM5, F_IMM },
    { 0x1b, OP(caget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(cagetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(caput),          INST_TYPE_RA_IMM4, F_IMM },
    { 0x13, OP(caputd),         INST_TYPE_RA_RB, 0 },
    { 0x1b, OP(cget),           INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(cgetd),          INST_TYPE_RD_RB, 0 },
    { 0x24, OP(clz),            INST_TYPE_RD_RA, 0 },
    { 0x05, OP(cmp),            INST_TYPE_RD_RA_RB, 0 },
    { 0x05, OP(cmpu),           INST_TYPE_RD_RA_RB, 0 },
    { 0x1b, OP(cput),           INST_TYPE_RA_IMM4, F_IMM },
    { 0x13, OP(cputd),          INST_TYPE_RA_RB, 0 },
    { 0x1b, OP(eaget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(eagetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(ecaget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(ecagetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(ecget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(ecgetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(eget),           INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(egetd),          INST_TYPE_RD_RB, 0 },
    { 0x16, OP(fadd),           INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_fcmp_eq, "fcmp.eq", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_fcmp_ge, "fcmp.ge", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_fcmp_gt, "fcmp.gt", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_fcmp_le, "fcmp.le", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_fcmp_lt, "fcmp.lt", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_fcmp_ne, "fcmp.ne", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_fcmp_un, "fcmp.un", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(fdiv),           INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(fint),           INST_TYPE_RD_RA, 0 },
    { 0x16, OP(flt),            INST_TYPE_RD_RA, 0 },
    { 0x16, OP(fmul),           INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(frsub),          INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(fsqrt),          INST_TYPE_RD_RA, 0 },
    { 0x1b, OP(get),            INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(getd),           INST_TYPE_RD_RB, 0 },
    { 0x12, OP(idiv),           INST_TYPE_RD_RA_RB, 0 },
    { 0x12, OP(idivu),          INST_TYPE_RD_RA_RB, 0 },
    { 0x2c, OP(imm),            INST_TYPE_IMM, F_IMM },
    { 0x30, OP(lbu),            INST_TYPE_RD_RA_RB, 0 },
    { 0x30, OP(lbuea),          INST_TYPE_RD_RA_RB, 0 },
    { 0x38, OP(lbui),           INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x30, OP(lbur),           INST_TYPE_RD_RA_RB, 0 },
    { 0x31, OP(lhu),            INST_TYPE_RD_RA_RB, 0 },
    { 0x31, OP(lhuea),          INST_TYPE_RD_RA_RB, 0 },
    { 0x39, OP(lhui),           INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x31, OP(lhur),           INST_TYPE_RD_RA_RB, 0 },
    { 0x32, OP(lw),             INST_TYPE_RD_RA_RB, 0 },
    { 0x32, OP(lwea),           INST_TYPE_RD_RA_RB, 0 },
    { 0x3a, OP(lwi),            INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x32, OP(lwr),            INST_TYPE_RD_RA_RB, 0 },
    { 0x32, OP(lwx),            INST_TYPE_RD_RA_RB, 0 },
    { 0x2e, OP(mbar),           INST_TYPE_RDIMM, F_CTRL|F_ABS|F_IMM },
    { 0x25, OP(mfs),            INST_TYPE_RD_SA, 0 },
    { 0x25, OP(mfse),           INST_TYPE_RD_SA, 0 },
    { 0x25, OP(msrclr),         INST_TYPE_RD_IMM15, 0 },
    { 0x25, OP(msrset),         INST_TYPE_RD_IMM15, 0 },
    { 0x25, OP(mts),            INST_TYPE_SA_RA, 0 },
    { 0x10, OP(mul),            INST_TYPE_RD_RA_RB, 0 },
    { 0x10, OP(mulh),           INST_TYPE_RD_RA_RB, 0 },
    { 0x10, OP(mulhsu),         INST_TYPE_RD_RA_RB, 0 },
    { 0x10, OP(mulhu),          INST_TYPE_RD_RA_RB, 0 },
    { 0x18, OP(muli),           INST_TYPE_RD_RA_IMM, 0 },
    { 0x1b, OP(naget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(nagetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(naput),          INST_TYPE_RA_IMM4, F_IMM },
    { 0x13, OP(naputd),         INST_TYPE_RA_RB, 0 },
    { 0x1b, OP(ncaget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(ncagetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(ncaput),         INST_TYPE_RA_IMM4, F_IMM },
    { 0x13, OP(ncaputd),        INST_TYPE_RA_RB, 0 },
    { 0x1b, OP(ncget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(ncgetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(ncput),          INST_TYPE_RA_IMM4, F_IMM },
    { 0x13, OP(ncputd),         INST_TYPE_RA_RB, 0 },
    { 0x1b, OP(neaget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(neagetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(necaget),        INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(necagetd),       INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(necget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(necgetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(neget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(negetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(nget),           INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(ngetd),          INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(nput),           INST_TYPE_RA_IMM4, F_IMM },
    { 0x13, OP(nputd),          INST_TYPE_RA_RB, 0 },
    { 0x20, OP(or),             INST_TYPE_RD_RA_RB, 0 },
    { 0x28, OP(ori),            INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x20, OP(pcmpbf),         INST_TYPE_RD_RA_RB, 0 },
    { 0x22, OP(pcmpeq),         INST_TYPE_RD_RA_RB, 0 },
    { 0x23, OP(pcmpne),         INST_TYPE_RD_RA_RB, 0 },
    { 0x1b, OP(put),            INST_TYPE_RA_IMM4, F_IMM },
    { 0x13, OP(putd),           INST_TYPE_RA_RB, 0 },
    { 0x01, OP(rsub),           INST_TYPE_RD_RA_RB, 0 },
    { 0x03, OP(rsubc),          INST_TYPE_RD_RA_RB, 0 },
    { 0x09, OP(rsubi),          INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0b, OP(rsubic),         INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0d, OP(rsubik),         INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0f, OP(rsubikc),        INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x05, OP(rsubk),          INST_TYPE_RD_RA_RB, 0 },
    { 0x07, OP(rsubkc),         INST_TYPE_RD_RA_RB, 0 },
    { 0x2d, OP(rtbd),           INST_TYPE_RA_IMM, F_CTRL|F_INDIRJMP|F_IMM },
    { 0x2d, OP(rted),           INST_TYPE_RA_IMM, F_CTRL|F_INDIRJMP|F_IMM },
    { 0x2d, OP(rtid),           INST_TYPE_RA_IMM, F_CTRL|F_INDIRJMP|F_IMM },
    { 0x2d, OP(rtsd),           INST_TYPE_RA_IMM, F_CTRL|F_INDIRJMP|F_IMM },
    { 0x34, OP(sb),             INST_TYPE_RD_RA_RB, 0 },
    { 0x34, OP(sbea),           INST_TYPE_RD_RA_RB, 0 },
    { 0x3c, OP(sbi),            INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x34, OP(sbr),            INST_TYPE_RD_RA_RB, 0 },
    { 0x24, OP(sext16),         INST_TYPE_RD_RA, 0 },
    { 0x24, OP(sext8),          INST_TYPE_RD_RA, 0 },
    { 0x35, OP(sh),             INST_TYPE_RD_RA_RB, 0 },
    { 0x35, OP(shea),           INST_TYPE_RD_RA_RB, 0 },
    { 0x3d, OP(shi),            INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x35, OP(shr),            INST_TYPE_RD_RA_RB, 0 },
    { 0x2e, OP(sleep),          INST_TYPE_NULL, F_CTRL|F_ABS|F_IMM },
    { 0x24, OP(sra),            INST_TYPE_RD_RA, 0 },
    { 0x24, OP(src),            INST_TYPE_RD_RA, 0 },
    { 0x24, OP(srl),            INST_TYPE_RD_RA, 0 },
    { 0x36, OP(sw),             INST_TYPE_RD_RA_RB, 0 },
    { 0x24, OP(swapb),          INST_TYPE_RD_RA, 0 },
    { 0x24, OP(swaph),          INST_TYPE_RD_RA, 0 },
    { 0x36, OP(swea),           INST_TYPE_RD_RA_RB, 0 },
    { 0x3e, OP(swi),            INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x36, OP(swr),            INST_TYPE_RD_RA_RB, 0 },
    { 0x36, OP(swx),            INST_TYPE_RD_RA_RB, 0 },
    { 0x1b, OP(taget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tagetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(taput),          INST_TYPE_IMM4, F_IMM },
    { 0x13, OP(taputd),         INST_TYPE_RB, 0 },
    { 0x1b, OP(tcaget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tcagetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tcaput),         INST_TYPE_IMM4, F_IMM },
    { 0x13, OP(tcaputd),        INST_TYPE_RB, 0 },
    { 0x1b, OP(tcget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tcgetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tcput),          INST_TYPE_IMM4, F_IMM },
    { 0x13, OP(tcputd),         INST_TYPE_RB, 0 },
    { 0x1b, OP(teaget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(teagetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tecaget),        INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tecagetd),       INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tecget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tecgetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(teget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tegetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tget),           INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tgetd),          INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tnaget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tnagetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tnaput),         INST_TYPE_IMM4, F_IMM },
    { 0x13, OP(tnaputd),        INST_TYPE_RB, 0 },
    { 0x1b, OP(tncaget),        INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tncagetd),       INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tncaput),        INST_TYPE_IMM4, F_IMM },
    { 0x13, OP(tncaputd),       INST_TYPE_RB, 0 },
    { 0x1b, OP(tncget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tncgetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tncput),         INST_TYPE_IMM4, F_IMM },
    { 0x13, OP(tncputd),        INST_TYPE_RB, 0 },
    { 0x1b, OP(tneaget),        INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tneagetd),       INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tnecaget),       INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tnecagetd),      INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tnecget),        INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tnecgetd),       INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tneget),         INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tnegetd),        INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tnget),          INST_TYPE_RD_IMM4, F_IMM },
    { 0x13, OP(tngetd),         INST_TYPE_RD_RB, 0 },
    { 0x1b, OP(tnput),          INST_TYPE_IMM4, F_IMM },
    { 0x13, OP(tnputd),         INST_TYPE_RB, 0 },
    { 0x1b, OP(tput),           INST_TYPE_IMM4, F_IMM },
    { 0x13, OP(tputd),          INST_TYPE_RB, 0 },
    { 0x24, OP(wdc),            INST_TYPE_RA_RB, 0 },
    { 0x24, i_wdc_clear,     "wdc.clear",     INST_TYPE_RA_RB, 0 },
    { 0x24, i_wdc_clear_ea,  "wdc.clear.ea",  INST_TYPE_RA_RB, 0 },
    { 0x24, i_wdc_ext_clear, "wdc.ext.clear", INST_TYPE_RA_RB, 0 },
    { 0x24, i_wdc_ext_flush, "wdc.ext.flush", INST_TYPE_RA_RB, 0 },
    { 0x24, i_wdc_flush,     "wdc.flush",     INST_TYPE_RA_RB, 0 },
    { 0x24, OP(wic),            INST_TYPE_RA_RB, 0 },
    { 0x22, OP(xor),            INST_TYPE_RD_RA_RB, 0 },
    { 0x2a, OP(xori),           INST_TYPE_RD_RA_IMM, F_IMM },

    /* 64-bit instructions */
    { 0x00, OP(addl),           INST_TYPE_RD_RA_RB, 0 },
    { 0x01, OP(rsubl),          INST_TYPE_RD_RA_RB, 0 },
    { 0x02, OP(addlc),          INST_TYPE_RD_RA_RB, 0 },
    { 0x03, OP(rsublc),         INST_TYPE_RD_RA_RB, 0 },
    { 0x04, OP(addlk),          INST_TYPE_RD_RA_RB, 0 },
    { 0x05, OP(rsublk),         INST_TYPE_RD_RA_RB, 0 },
    { 0x06, OP(addlkc),         INST_TYPE_RD_RA_RB, 0 },
    { 0x07, OP(rsublkc),        INST_TYPE_RD_RA_RB, 0 },
    { 0x05, OP(cmpl),           INST_TYPE_RD_RA_RB, 0 },
    { 0x05, OP(cmplu),          INST_TYPE_RD_RA_RB, 0 },
    { 0x08, OP(addli),          INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x09, OP(rsubli),         INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0a, OP(addlic),         INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0b, OP(rsublic),        INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0c, OP(addlik),         INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0d, OP(rsublik),        INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0e, OP(addlikc),        INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x0f, OP(rsublikc),       INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x10, OP(mull),           INST_TYPE_RD_RA_RB, 0 },
    { 0x11, OP(bslll),          INST_TYPE_RD_RA_RB, 0 },
    { 0x11, OP(bslra),          INST_TYPE_RD_RA_RB, 0 },
    { 0x11, OP(bslrl),          INST_TYPE_RD_RA_RB, 0 },
    { 0x19, OP(bsllli),         INST_TYPE_RD_RA_IMM6, F_IMM },
    { 0x19, OP(bslrai),         INST_TYPE_RD_RA_IMM6, F_IMM },
    { 0x19, OP(bslrli),         INST_TYPE_RD_RA_IMM6, F_IMM },
    { 0x19, OP(bslefi),         INST_TYPE_RD_RA_IMM6_IMM6, F_IMM },
    { 0x19, OP(bslifi),         INST_TYPE_RD_RA_IMM6_IMM6, F_IMM },
    { 0x20, OP(orl),            INST_TYPE_RD_RA_RB, 0 },
    { 0x21, OP(andl),           INST_TYPE_RD_RA_RB, 0 },
    { 0x22, OP(xorl),           INST_TYPE_RD_RA_RB, 0 },
    { 0x23, OP(andnl),          INST_TYPE_RD_RA_RB, 0 },
    { 0x20, OP(pcmplbf),        INST_TYPE_RD_RA_RB, 0 },
    { 0x22, OP(pcmpleq),        INST_TYPE_RD_RA_RB, 0 },
    { 0x23, OP(pcmplne),        INST_TYPE_RD_RA_RB, 0 },
    { 0x24, OP(srla),           INST_TYPE_RD_RA, 0 },
    { 0x24, OP(srlc),           INST_TYPE_RD_RA, 0 },
    { 0x24, OP(srll),           INST_TYPE_RD_RA, 0 },
    { 0x24, OP(sextl8),         INST_TYPE_RD_RA, 0 },
    { 0x24, OP(sextl16),        INST_TYPE_RD_RA, 0 },
    { 0x24, OP(sextl32),        INST_TYPE_RD_RA, 0 },
    { 0x26, OP(brea),           INST_TYPE_RB, 0 },
    { 0x26, OP(bread),          INST_TYPE_RB, 0 },
    { 0x26, OP(breald),         INST_TYPE_RD_RB, 0 },
    { 0x27, OP(beaeq),          INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealeq),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beaeqd),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealeqd),        INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beane),          INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealne),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beaned),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealned),        INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealt),          INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beallt),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealtd),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealltd),        INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beale),          INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealle),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealed),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealled),        INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beagt),          INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealgt),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beagtd),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealgtd),        INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beage),          INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealge),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(beaged),         INST_TYPE_RA_RB, 0 },
    { 0x27, OP(bealged),        INST_TYPE_RA_RB, 0 },
    { 0x28, OP(orli),           INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x29, OP(andli),          INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x2a, OP(xorli),          INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x2b, OP(andnli),         INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x2c, OP(imml),           INST_TYPE_IMML, F_IMM },
    { 0x2e, OP(breai),          INST_TYPE_IMM, F_IMM },
    { 0x2e, OP(breaid),         INST_TYPE_IMM, F_IMM },
    { 0x2e, OP(brealid),        INST_TYPE_RD_IMM, F_IMM },
    { 0x2f, OP(beaeqi),         INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealeqi),        INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(beaeqid),        INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealeqid),       INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(beanei),         INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealnei),        INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(beaneid),        INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealneid),       INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(bealti),         INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(beallti),        INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(bealtid),        INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealltid),       INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(bealei),         INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(beallei),        INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(bealeid),        INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealleid),       INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(beagti),         INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealgti),        INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(beagtid),        INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealgtid),       INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(beagei),         INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealgei),        INST_TYPE_RA_IMML, F_IMM },
    { 0x2f, OP(beageid),        INST_TYPE_RA_IMM, F_IMM },
    { 0x2f, OP(bealgeid),       INST_TYPE_RA_IMML, F_IMM },
    { 0x32, OP(ll),             INST_TYPE_RD_RA_RB, 0 },
    { 0x32, OP(llr),            INST_TYPE_RD_RA_RB, 0 },
    { 0x36, OP(sl),             INST_TYPE_RD_RA_RB, 0 },
    { 0x36, OP(slr),            INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(dadd),           INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(drsub),          INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(dmul),           INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(ddiv),           INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_dcmp_lt, "dcmp.lt", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_dcmp_eq, "dcmp.eq", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_dcmp_le, "dcmp.le", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_dcmp_gt, "dcmp.gt", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_dcmp_ne, "dcmp.ne", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_dcmp_ge, "dcmp.ge", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, i_dcmp_un, "dcmp.un", INST_TYPE_RD_RA_RB, 0 },
    { 0x16, OP(dbl),            INST_TYPE_RD_RA, 0 },
    { 0x16, OP(dlong),          INST_TYPE_RD_RA, 0 },
    { 0x16, OP(dsqrt),          INST_TYPE_RD_RA, 0 },
    { 0x3a, OP(lli),            INST_TYPE_RD_RA_IMM, F_IMM },
    { 0x3e, OP(sli),            INST_TYPE_RD_RA_IMM, F_IMM },
};

#define UNKNOWN_OPCODE ((enum Instructions)(sizeof(instruction_info) / sizeof(InstructionInfo)))

/* opcode_hash[] maps 6 most significant bits of the instruction into enum Instructions.
 * This is done to speed up opcode lookup. Some instructions have the same 6 MSb,
 * so further decode will still be necessary (done in decode_instruction). */
static enum Instructions opcode_hash[64];

static ContextAddress ctx_addr = 0;
static DisassemblerParams * disass_params = NULL;
static uint32_t instr_bits = 0;
static int en_64_bit = 0;

/* Decoded instruction */
static unsigned instr_op = UNKNOWN_OPCODE; /* index in instruction_info */
static unsigned instr_r1 = 0;
static unsigned instr_r2 = 0;
static unsigned instr_rd = 0;
static unsigned instr_imm = 0;
static unsigned instr_imm2 = 0;

typedef struct DecodingState {
    ContextAddress addr;
    uint32_t instr;
} DecodingState;

static int is_preceded_by_imml(void) {
    DecodingState * state = (DecodingState *)disass_params->state;
    return state != NULL && state->addr == ctx_addr - 4 && (state->instr & 0xff000000) == 0xb2000000;
}

static int decode_instruction(void) {
    unsigned hash = instr_bits >> 26;
    enum Instructions op = opcode_hash[hash];

    instr_op = UNKNOWN_OPCODE;
    instr_r1 = 0;
    instr_r2 = 0;
    instr_rd = 0;
    instr_imm = 0;
    instr_imm2 = 0;

    /* If non-existent opcode */
    if (op == UNKNOWN_OPCODE) return 0;

    /* Further decode for certain instructions */
    switch (hash) {
    case 0x00:
        op = instr_bits & 0x100 ? i_addl : i_add;
        break;

    case 0x01:
        op = instr_bits & 0x100 ? i_rsubl : i_rsub;
        break;

    case 0x02:
        op = instr_bits & 0x100 ? i_addlc : i_addc;
        break;

    case 0x03:
        op = instr_bits & 0x100 ? i_rsublc : i_rsubc;
        break;

    case 0x04:
        op = instr_bits & 0x100 ? i_addlk : i_addk;
        break;

    case 0x05:
        if (instr_bits & 0x100) {
            switch (instr_bits & 0x0003) {
            case 0x0: op = i_rsublk; break;
            case 0x1: op = i_cmpl;   break;
            case 0x3: op = i_cmplu;  break;
            default: return 0;
            }
            break;
        }
        switch (instr_bits & 0x0003) {
        case 0x0: op = i_rsubk; break;
        case 0x1: op = i_cmp;   break;
        case 0x3: op = i_cmpu;  break;
        default: return 0;
        }
        break;

    case 0x06:
        op = instr_bits & 0x100 ? i_addlkc : i_addkc;
        break;

    case 0x07:
        op = instr_bits & 0x100 ? i_rsublkc : i_rsubkc;
        break;

    case 0x08:
        op = is_preceded_by_imml() ? i_addli : i_addi;
        break;

    case 0x09:
        op = is_preceded_by_imml() ? i_rsubli : i_rsubi;
        break;

    case 0x0a:
        op = is_preceded_by_imml() ? i_addlic : i_addic;
        break;

    case 0x0b:
        op = is_preceded_by_imml() ? i_rsublic : i_rsubic;
        break;

    case 0x0c:
        op = is_preceded_by_imml() ? i_addlik : i_addik;
        break;

    case 0x0d:
        op = is_preceded_by_imml() ? i_rsublik : i_rsubik;
        break;

    case 0x0e:
        op = is_preceded_by_imml() ? i_addlikc : i_addikc;
        break;

    case 0x0f:
        op = is_preceded_by_imml() ? i_rsublikc : i_rsubikc;
        break;

    case 0x10:
        switch (instr_bits & 0x3) {
        case 0x0: op = i_mul;   break;
        case 0x1: op = i_mulh;  break;
        case 0x2: op = i_mulhsu; break;
        case 0x3: op = i_mulhu; break;
        }
        break;

    case 0x11:
        switch (instr_bits & 0x600) {
        case 0x000: op = instr_bits & 0x100 ? i_bslrl : i_bsrl; break;
        case 0x200: op = instr_bits & 0x100 ? i_bslra : i_bsra; break;
        case 0x400: op = instr_bits & 0x100 ? i_bslll : i_bsll; break;
        default: return 0;
        }
        break;

    case 0x12:
        switch (instr_bits & 0x0002) {
        case 0x0000: op = i_idiv; break;
        case 0x0002: op = i_idivu; break;
        default: return 0;
        }
        break;

    case 0x13:
        switch ((instr_bits >> 5) & 0x3F) {
        case 0x00: /* 000000 */ op = i_getd;      break;
        case 0x01: /* 000001 */ op = i_egetd;     break;
        case 0x02: /* 000010 */ op = i_agetd;     break;
        case 0x03: /* 000011 */ op = i_eagetd;    break;
        case 0x04: /* 000100 */ op = i_tgetd;     break;
        case 0x05: /* 000101 */ op = i_tegetd;    break;
        case 0x06: /* 000110 */ op = i_tagetd;    break;
        case 0x07: /* 000111 */ op = i_teagetd;   break;
        case 0x08: /* 001000 */ op = i_cgetd;     break;
        case 0x09: /* 001001 */ op = i_ecgetd;    break;
        case 0x0A: /* 001010 */ op = i_cagetd;    break;
        case 0x0B: /* 001011 */ op = i_ecagetd;   break;
        case 0x0C: /* 001100 */ op = i_tcgetd;    break;
        case 0x0D: /* 001101 */ op = i_tecgetd;   break;
        case 0x0E: /* 001110 */ op = i_tcagetd;   break;
        case 0x0F: /* 001111 */ op = i_tecagetd;  break;
        case 0x10: /* 010000 */ op = i_ngetd;     break;
        case 0x11: /* 010001 */ op = i_negetd;    break;
        case 0x12: /* 010010 */ op = i_nagetd;    break;
        case 0x13: /* 010011 */ op = i_neagetd;   break;
        case 0x14: /* 010100 */ op = i_tngetd;    break;
        case 0x15: /* 010101 */ op = i_tnegetd;   break;
        case 0x16: /* 010110 */ op = i_tnagetd;   break;
        case 0x17: /* 010111 */ op = i_tneagetd;  break;
        case 0x18: /* 011000 */ op = i_ncgetd;    break;
        case 0x19: /* 011001 */ op = i_necgetd;   break;
        case 0x1A: /* 011010 */ op = i_ncagetd;   break;
        case 0x1B: /* 011011 */ op = i_necagetd;  break;
        case 0x1C: /* 011100 */ op = i_tncgetd;   break;
        case 0x1D: /* 011101 */ op = i_tnecgetd;  break;
        case 0x1E: /* 011110 */ op = i_tncagetd;  break;
        case 0x1F: /* 011111 */ op = i_tnecagetd; break;
        case 0x20: /* 100000 */ op = i_putd;      break;
        case 0x22: /* 100010 */ op = i_aputd;     break;
        case 0x24: /* 100100 */ op = i_tputd;     break;
        case 0x26: /* 100110 */ op = i_taputd;    break;
        case 0x28: /* 101000 */ op = i_cputd;     break;
        case 0x2A: /* 101010 */ op = i_caputd;    break;
        case 0x2C: /* 101100 */ op = i_tcputd;    break;
        case 0x2E: /* 101110 */ op = i_tcaputd;   break;
        case 0x30: /* 110000 */ op = i_nputd;     break;
        case 0x32: /* 110010 */ op = i_naputd;    break;
        case 0x34: /* 110100 */ op = i_tnputd;    break;
        case 0x36: /* 110110 */ op = i_tnaputd;   break;
        case 0x38: /* 111000 */ op = i_ncputd;    break;
        case 0x3A: /* 111010 */ op = i_ncaputd;   break;
        case 0x3C: /* 111100 */ op = i_tncputd;   break;
        case 0x3E: /* 111110 */ op = i_tncaputd;  break;
        default: return 0;
        }
        break;

    case 0x16:  /* FP operation */
        if (instr_bits & 0x400) {
            switch ((instr_bits >> 7) & 0x7) {
            case 0x0: op = i_dadd; break;
            case 0x1: op = i_drsub; break;
            case 0x2: op = i_dmul; break;
            case 0x3: op = i_ddiv; break;
            case 0x4:
                switch ((instr_bits >> 4) & 0x7) {
                case 0: op = i_dcmp_un; break;
                case 1: op = i_dcmp_lt; break;
                case 2: op = i_dcmp_eq; break;
                case 3: op = i_dcmp_le; break;
                case 4: op = i_dcmp_gt; break;
                case 5: op = i_dcmp_ne; break;
                case 6: op = i_dcmp_ge; break;
                }
                break;
            case 0x5: op = i_dbl; break;
            case 0x6: op = i_dlong; break;
            case 0x7: op = i_dsqrt; break;
            }
            break;
        }
        switch((instr_bits >> 7) & 0x7) {
        case 0: op = i_fadd; break;
        case 1: op = i_frsub; break;
        case 2: op = i_fmul; break;
        case 3: op = i_fdiv; break;
        case 4: /* fp comparison */
            switch ((instr_bits >> 4) & 0x7) {
            case 0: op = i_fcmp_un; break;
            case 1: op = i_fcmp_lt; break;
            case 2: op = i_fcmp_eq; break;
            case 3: op = i_fcmp_le; break;
            case 4: op = i_fcmp_gt; break;
            case 5: op = i_fcmp_ne; break;
            case 6: op = i_fcmp_ge; break;
            }
            break;
        case 5: op = i_flt; break;
        case 6: op = i_fint; break;
        case 7: op = i_fsqrt; break;
        }
        break;

    case 0x19:
        switch (instr_bits & 0xe600) {
        case 0x0000: op = i_bsrli; break;
        case 0x0200: op = i_bsrai; break;
        case 0x0400: op = i_bslli; break;
        case 0x4000: op = i_bsefi; break;
        case 0x8000: op = i_bsifi; break;
        case 0x2000: op = i_bslrli; break;
        case 0x2200: op = i_bslrai; break;
        case 0x2400: op = i_bsllli; break;
        case 0x6000: op = i_bslefi; break;
        case 0xa000: op = i_bslifi; break;
        default: return 0;
        }
        break;

    case 0x1b:
        switch ((instr_bits >> 10) & 0x3f) {
        case 0x00: /* 000000 */ op = i_get;      break;
        case 0x01: /* 000001 */ op = i_eget;     break;
        case 0x02: /* 000010 */ op = i_aget;     break;
        case 0x03: /* 000011 */ op = i_eaget;    break;
        case 0x04: /* 000100 */ op = i_tget;     break;
        case 0x05: /* 000101 */ op = i_teget;    break;
        case 0x06: /* 000110 */ op = i_taget;    break;
        case 0x07: /* 000111 */ op = i_teaget;   break;
        case 0x08: /* 001000 */ op = i_cget;     break;
        case 0x09: /* 001001 */ op = i_ecget;    break;
        case 0x0A: /* 001010 */ op = i_caget;    break;
        case 0x0B: /* 001011 */ op = i_ecaget;   break;
        case 0x0C: /* 001100 */ op = i_tcget;    break;
        case 0x0D: /* 001101 */ op = i_tecget;   break;
        case 0x0E: /* 001110 */ op = i_tcaget;   break;
        case 0x0F: /* 001111 */ op = i_tecaget;  break;
        case 0x10: /* 010000 */ op = i_nget;     break;
        case 0x11: /* 010001 */ op = i_neget;    break;
        case 0x12: /* 010010 */ op = i_naget;    break;
        case 0x13: /* 010011 */ op = i_neaget;   break;
        case 0x14: /* 010100 */ op = i_tnget;    break;
        case 0x15: /* 010101 */ op = i_tneget;   break;
        case 0x16: /* 010110 */ op = i_tnaget;   break;
        case 0x17: /* 010111 */ op = i_tneaget;  break;
        case 0x18: /* 011000 */ op = i_ncget;    break;
        case 0x19: /* 011001 */ op = i_necget;   break;
        case 0x1A: /* 011010 */ op = i_ncaget;   break;
        case 0x1B: /* 011011 */ op = i_necaget;  break;
        case 0x1C: /* 011100 */ op = i_tncget;   break;
        case 0x1D: /* 011101 */ op = i_tnecget;  break;
        case 0x1E: /* 011110 */ op = i_tncaget;  break;
        case 0x1F: /* 011111 */ op = i_tnecaget; break;
        case 0x20: /* 100000 */ op = i_put;      break;
        case 0x22: /* 100010 */ op = i_aput;     break;
        case 0x24: /* 100100 */ op = i_tput;     break;
        case 0x26: /* 100110 */ op = i_taput;    break;
        case 0x28: /* 101000 */ op = i_cput;     break;
        case 0x2A: /* 101010 */ op = i_caput;    break;
        case 0x2C: /* 101100 */ op = i_tcput;    break;
        case 0x2E: /* 101110 */ op = i_tcaput;   break;
        case 0x30: /* 110000 */ op = i_nput;     break;
        case 0x32: /* 110010 */ op = i_naput;    break;
        case 0x34: /* 110100 */ op = i_tnput;    break;
        case 0x36: /* 110110 */ op = i_tnaput;   break;
        case 0x38: /* 111000 */ op = i_ncput;    break;
        case 0x3A: /* 111010 */ op = i_ncaput;   break;
        case 0x3C: /* 111100 */ op = i_tncput;   break;
        case 0x3E: /* 111110 */ op = i_tncaput;  break;
        default: return 0;
        }
        break;

    case 0x20:
        if (instr_bits & 0x400)
            op = instr_bits & 0x100 ? i_pcmplbf : i_pcmpbf;
        else
            op = instr_bits & 0x100 ? i_orl : i_or;
        break;

    case 0x21:
        op = instr_bits & 0x100 ? i_andl : i_and;
        break;

    case 0x22:
        if (instr_bits & 0x400)
            op = instr_bits & 0x100 ? i_pcmpleq : i_pcmpeq;
        else
            op = instr_bits & 0x100 ? i_xorl : i_xor;
        break;

    case 0x23:
        if (instr_bits & 0x400)
            op = instr_bits & 0x100 ? i_pcmplne : i_pcmpne;
        else
            op = instr_bits & 0x100 ? i_andnl : i_andn;
        break;

    case 0x24:
        switch (instr_bits & 0xffff) {
        case 0x0001: op = i_sra; break;
        case 0x0021: op = i_src; break;
        case 0x0041: op = i_srl; break;
        case 0x0060: op = i_sext8; break;
        case 0x0061: op = i_sext16; break;
        case 0x0101: op = i_srla; break;
        case 0x0121: op = i_srlc; break;
        case 0x0141: op = i_srll; break;
        case 0x0160: op = i_sextl8; break;
        case 0x0161: op = i_sextl16; break;
        case 0x0162: op = i_sextl32; break;
        default:
            switch (instr_bits & 0x1fff) {
            case 0x0064: op = i_wdc; break;
            case 0x0068: op = i_wic; break;
            case 0x0074: op = i_wdc_flush; break;
            case 0x0066: op = i_wdc_clear; break;
            case 0x00e0: op = i_clz; break;
            case 0x00e6: op = i_wdc_clear_ea; break;
            case 0x01e0: op = i_swapb; break;
            case 0x01e2: op = i_swaph; break;
            case 0x0476: op = i_wdc_ext_flush; break;
            case 0x0466: op = i_wdc_ext_clear; break;
            default: return 0;
            }
            break;
        }
        break;

    case 0x25:
        switch (instr_bits & 0xC000) {
        case 0xC000:
            op = i_mts;
            break;
        case 0x8000:
            op = (instr_bits & 0x80000) ? i_mfse : i_mfs;
            break;
        case 0x4000:
        case 0x0000:
            if (instr_bits & 0x10000)
              op = i_msrclr;
            else
              op = i_msrset;
            break;
        default:
            return 0;
        }
        break;

    case 0x26:
        switch ((instr_bits >> 16) & 0x1F) {
        case 0x00: op = i_br; break;
        case 0x10: op = i_brd; break;
        case 0x14: op = i_brld; break;
        case 0x08: op = i_bra; break;
        case 0x18: op = i_brad; break;
        case 0x1c: op = i_brald; break;
        case 0x01: op = i_brea; break;
        case 0x11: op = i_bread; break;
        case 0x15: op = i_breald; break;
        case 0x0c: op = i_brk; break;
        default: return 0;
        }
        break;

    case 0x27:
        switch ((instr_bits >> 21) & 0x1f) {
        case 0x00: op = i_beq; break;
        case 0x01: op = i_bne; break;
        case 0x02: op = i_blt; break;
        case 0x03: op = i_ble; break;
        case 0x04: op = i_bgt; break;
        case 0x05: op = i_bge; break;
        case 0x08: op = instr_bits & 0x100 ? i_bealeq : i_beaeq; break;
        case 0x09: op = instr_bits & 0x100 ? i_bealne : i_beane; break;
        case 0x0a: op = instr_bits & 0x100 ? i_beallt : i_bealt; break;
        case 0x0b: op = instr_bits & 0x100 ? i_bealle : i_beale; break;
        case 0x0c: op = instr_bits & 0x100 ? i_bealgt : i_beagt; break;
        case 0x0d: op = instr_bits & 0x100 ? i_bealge : i_beage; break;
        case 0x10: op = i_beqd; break;
        case 0x11: op = i_bned; break;
        case 0x12: op = i_bltd; break;
        case 0x13: op = i_bled; break;
        case 0x14: op = i_bgtd; break;
        case 0x15: op = i_bged; break;
        case 0x18: op = instr_bits & 0x100 ? i_bealeqd : i_beaeqd; break;
        case 0x19: op = instr_bits & 0x100 ? i_bealned : i_beaned; break;
        case 0x1a: op = instr_bits & 0x100 ? i_bealltd : i_bealtd; break;
        case 0x1b: op = instr_bits & 0x100 ? i_bealled : i_bealed; break;
        case 0x1c: op = instr_bits & 0x100 ? i_bealgtd : i_beagtd; break;
        case 0x1d: op = instr_bits & 0x100 ? i_bealged : i_beaged; break;
        default: return 0;
        }
        break;

    case 0x28:
        op = is_preceded_by_imml() ? i_orli : i_ori;
        break;

    case 0x29:
        op = is_preceded_by_imml() ? i_andli : i_andi;
        break;

    case 0x2a:
        op = is_preceded_by_imml() ? i_xorli : i_xori;
        break;

    case 0x2b:
        op = is_preceded_by_imml() ? i_andnli : i_andni;
        break;

    case 0x2c:
        if (((instr_bits >> 24) & 3) == 2) {
            op = i_imml;
            break;
        }
        break;

    case 0x2d:
        switch ((instr_bits >> 21) & 0x1F) {
        case 0x10: op = i_rtsd; break;
        case 0x11: op = i_rtid; break;
        case 0x12: op = i_rtbd; break;
        case 0x14: op = i_rted; break;
        default: return 0;
        }
        break;

    case 0x2e:
        switch ((instr_bits >> 16) & 0x1F) {
        case 0x00: op = i_bri; break;
        case 0x02:
            switch ((instr_bits >> 21) & 0x1F) {
            case 0x10: op = i_sleep; break;
            case 0x00:
            case 0x01:
            case 0x02:
                op = i_mbar;
                break;
            default: return 0;
            }
            break;
        case 0x10: op = i_brid; break;
        case 0x14: op = i_brlid; break;
        case 0x08: op = i_brai; break;
        case 0x18: op = i_braid; break;
        case 0x1c: op = i_bralid; break;
        case 0x01: op = i_breai; break;
        case 0x11: op = i_breaid; break;
        case 0x15: op = i_brealid; break;
        case 0x0c: op = i_brki; break;
        default: return 0;
        }
        break;

    case 0x2f:
        switch ((instr_bits >> 21) & 0x1F) {
        case 0x00: op = i_beqi; break;
        case 0x01: op = i_bnei; break;
        case 0x02: op = i_blti; break;
        case 0x03: op = i_blei; break;
        case 0x04: op = i_bgti; break;
        case 0x05: op = i_bgei; break;
        case 0x08: op = i_beaeqi; break;
        case 0x09: op = i_beanei; break;
        case 0x0a: op = i_bealti; break;
        case 0x0b: op = i_bealei; break;
        case 0x0c: op = i_beagti; break;
        case 0x0d: op = i_beagei; break;
        case 0x10: op = i_beqid; break;
        case 0x11: op = i_bneid; break;
        case 0x12: op = i_bltid; break;
        case 0x13: op = i_bleid; break;
        case 0x14: op = i_bgtid; break;
        case 0x15: op = i_bgeid; break;
        case 0x18: op = i_beaeqid; break;
        case 0x19: op = i_beaneid; break;
        case 0x1a: op = i_bealtid; break;
        case 0x1b: op = i_bealeid; break;
        case 0x1c: op = i_beagtid; break;
        case 0x1d: op = i_beageid; break;
        default: return 0;
        }
        break;

    case 0x30:
        switch (instr_bits & 0x7ff) {
        case 0x000: op = i_lbu; break;
        case 0x200: op = i_lbur; break;
        case 0x080: op = i_lbuea; break;
        default: return 0;
        }
        break;

    case 0x31:
        switch (instr_bits & 0x7ff) {
        case 0x000: op = i_lhu; break;
        case 0x200: op = i_lhur; break;
        case 0x080: op = i_lhuea; break;
        default: return 0;
        }
        break;

    case 0x32:
        switch (instr_bits & 0x7ff) {
        case 0x000: op = i_lw; break;
        case 0x100: op = i_ll; break;
        case 0x200: op = i_lwr; break;
        case 0x300: op = i_llr; break;
        case 0x400: op = i_lwx; break;
        case 0x080: op = i_lwea; break;
        default: return 0;
        }
        break;

    case 0x34:
        switch (instr_bits & 0x7ff) {
        case 0x000: op = i_sb; break;
        case 0x200: op = i_sbr; break;
        case 0x080: op = i_sbea; break;
        default: return 0;
        }
        break;

    case 0x35:
        switch (instr_bits & 0x7ff) {
        case 0x000: op = i_sh; break;
        case 0x200: op = i_shr; break;
        case 0x080: op = i_shea; break;
        default: return 0;
        }
        break;

    case 0x36:
        switch (instr_bits & 0x7ff) {
        case 0x000: op = i_sw; break;
        case 0x100: op = i_sl; break;
        case 0x200: op = i_swr; break;
        case 0x300: op = i_slr; break;
        case 0x400: op = i_swx; break;
        case 0x080: op = i_swea; break;
        default: return 0;
        }
        break;

    case 0x3a:
        op = is_preceded_by_imml() ? i_lli : i_lwi;
        break;

    case 0x3e:
        op = is_preceded_by_imml() ? i_sli : i_swi;
        break;

    }

    instr_op = op;
    instr_rd = (instr_bits >> 21) & 0x1f;
    instr_r1 = (instr_bits >> 16) & 0x1f;
    instr_r2 = (instr_bits >> 11) & 0x1f;

    assert(op < UNKNOWN_OPCODE);
    switch (instruction_info[op].type) {
    case INST_TYPE_RD_IMM4:
    case INST_TYPE_RA_IMM4:
        instr_imm = instr_bits & 0xf;
        break;

    case INST_TYPE_RD_RA_IMM5:
        instr_imm = instr_bits & 0x1f;
        break;

    case INST_TYPE_RD_RA_IMM6:
        instr_imm = instr_bits & 0x3f;
        break;

    case INST_TYPE_RD_RA_IMM5_IMM5:
        if (instr_bits & 0x00004000)
            instr_imm = (instr_bits >> 6) & 0x1f;
        else
            instr_imm = ((instr_bits >> 6) & 0x1f) - (instr_bits & 0x1f) + 1;
        instr_imm2 = instr_bits & 0x1f;
        break;

    case INST_TYPE_RD_RA_IMM6_IMM6:
        if (instr_bits & 0x00004000)
            instr_imm = (instr_bits >> 6) & 0x3f;
        else
            instr_imm = ((instr_bits >> 6) & 0x3f) - (instr_bits & 0x3f) + 1;
        instr_imm2 = instr_bits & 0x3f;
        break;

    case INST_TYPE_RD_IMM15:
        instr_imm = instr_bits & 0x7fff;
        break;

    case INST_TYPE_RD_IMM:
    case INST_TYPE_RA_IMM:
    case INST_TYPE_RD_RA_IMM:
    case INST_TYPE_IMM:
        instr_imm = instr_bits & 0xffff;
        break;
    case INST_TYPE_IMML:
        instr_imm = instr_bits & 0xffffff;
        break;
    case INST_TYPE_IMM4:
        instr_imm = instr_bits & 0xf;
        break;
    case INST_TYPE_RDIMM:
        instr_imm = (instr_bits >> 21) & 0x1f;
        break;
    case INST_TYPE_RD_RA_RB:
    case INST_TYPE_RD_RA:
    case INST_TYPE_RD_RB:
    case INST_TYPE_RA_RB:
    case INST_TYPE_RD_SA:
    case INST_TYPE_SA_RA:
    case INST_TYPE_RA:
    case INST_TYPE_RB:
    case INST_TYPE_RD:
    case INST_TYPE_NULL:
        instr_imm = 0;
        break;
    }

    return 1;
}

static const char * SPREG_NAME_MFS[] = {
    "PC",  /* 0x0000 */
    "MSR", /* 0x0001 */
    "???",
    "EAR", /* 0x0003 */
    "???",
    "ESR", /* 0x0005 */
    "???",
    "FSR", /* 0x0007 */
    "???",
    "???",
    "???",
    "BTR", /* 0x000B */
    "???",
    "EDR", /* 0x000D */
    "???",
    "???",
};

static const char * SPREG_NAME_MTS[] = {
    "???",
    "MSR", /* 0x0001 */
    "???",
    "???",
    "???",
    "???",
    "???",
    "FSR", /* 0x0007 */
};

static const char * STACKREG_NAME[] = {
    "SLR", "???", "SHR", "???",
};

static const char * MMUREG_NAME_MFS[] = {
    "PID", "ZPR", "TLBX", "TLBLO", "TLBHI", "???", "???", "???"
};

static const char * MMUREG_NAME_MTS[] = {
    "PID", "ZPR", "TLBX", "TLBLO", "TLBHI", "TLBSX", "???", "???"
};

static const unsigned int MNEMONIC_FIELD_WIDTH = 14;

static char buf[128];
static size_t buf_pos = 0;

static void add_char(char ch) {
    if (buf_pos >= sizeof(buf) - 1) return;
    buf[buf_pos++] = ch;
    if (ch == ' ') while (buf_pos < 8) buf[buf_pos++] = ch;
}

static void add_str(const char * s) {
    while (*s) add_char(*s++);
}

static void add_hex_uint64(uint64_t n) {
    char s[32];
    size_t i = 0;
    while (i < 16) {
        unsigned d = (unsigned)(n & 0xf);
        s[i++] = (char)(d < 10 ? '0' + d : 'a' + d - 10);
        n = n >> 4;
        if (i >= 8 && n == 0) break;
    }
    while (i > 0) add_char(s[--i]);
}

static void add_addr(uint64_t addr) {
    while (buf_pos < 16) add_char(' ');
    add_str("; addr=0x");
    add_hex_uint64(addr);
#if ENABLE_Symbols
    if (disass_params->ctx != NULL) {
        Symbol * sym = NULL;
        char * name = NULL;
        ContextAddress sym_addr = 0;
        if (find_symbol_by_addr(disass_params->ctx, STACK_NO_FRAME, (ContextAddress)addr, &sym) < 0) return;
        if (get_symbol_name(sym, &name) < 0 || name == NULL) return;
        if (get_symbol_address(sym, &sym_addr) < 0) return;
        if (sym_addr <= addr) {
            add_str(": ");
            add_str(name);
            if (sym_addr < addr) {
                add_str(" + 0x");
                add_hex_uint64(addr - (uint32_t)sym_addr);
            }
        }
    }
#endif
}

static int sext16(unsigned imm) {
    if (imm & (1 << 15)) return ~(int)0xffff | imm;
    return (int)imm;
}

static int disassemble_instruction(void) {
    char tmp_buf[100];

    if (decode_instruction() == 0) {
        /* no such opcode */
        return -1;
    }

    add_str(instruction_info[instr_op].name);
    while (buf_pos < MNEMONIC_FIELD_WIDTH) buf[buf_pos++] = ' ';

    switch (instruction_info[instr_op].type) {
    case INST_TYPE_RD_RA_RB:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%-2u, r%-2u", instr_rd, instr_r1, instr_r2);
        break;
    case INST_TYPE_RD_RA_IMM:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%-2u, %d", instr_rd, instr_r1, sext16(instr_imm));
        break;
    case INST_TYPE_RD_RA_IMM5:
    case INST_TYPE_RD_RA_IMM6:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%-2u, %u", instr_rd, instr_r1, instr_imm);
        break;
    case INST_TYPE_RD_RA_IMM5_IMM5:
    case INST_TYPE_RD_RA_IMM6_IMM6:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%-2u, %u, %u", instr_rd, instr_r1, instr_imm, instr_imm2);
        break;
    case INST_TYPE_RD_RA:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%-2u", instr_rd, instr_r1);
        break;
    case INST_TYPE_RD_RB:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%-2u", instr_rd, instr_r2);
        break;
    case INST_TYPE_RD_IMM:
    case INST_TYPE_RD_IMM15:
    case INST_TYPE_RD_IMM4:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, %d", instr_rd, sext16(instr_imm));
        break;
    case INST_TYPE_RA_RB:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%-2u", instr_r1, instr_r2);
        break;
    case INST_TYPE_RA_IMM:
    case INST_TYPE_RA_IMM4:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, %d", instr_r1, sext16(instr_imm));
        break;
    case INST_TYPE_RD_SA: /* mfs */
        if (instr_bits & 0x2000) { /* PVR */
            snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, rPVR%u", instr_rd, (unsigned)(instr_bits & 0xf));
        }
        else if (instr_bits & 0x0800) { /* STACK REGs */
            snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%s", instr_rd, STACKREG_NAME[instr_bits & 0x3]);
        }
        else if (instr_bits & 0x1000) { /* MMU REGs */
            snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%s", instr_rd, MMUREG_NAME_MFS[instr_bits & 0x7]);
        }
        else {
            snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u, r%s", instr_rd, SPREG_NAME_MFS[instr_bits & 0xf]);
        }
        break;
    case INST_TYPE_SA_RA: /* mts */
        if (instr_bits & 0x0800) { /* STACK REGs */
            snprintf(tmp_buf, sizeof(tmp_buf), "r%s, r%-2u", STACKREG_NAME[instr_bits & 0x3], instr_r1);
        }
        else if (instr_bits & 0x1000) { /* MMU REGs */
            snprintf(tmp_buf, sizeof(tmp_buf), "r%s, r%-2u", MMUREG_NAME_MTS[instr_bits & 0x7], instr_r1);
        }
        else {
            snprintf(tmp_buf, sizeof(tmp_buf), "r%-3s, r%-2u", SPREG_NAME_MTS[instr_bits & 0x7], instr_r1);
        }
        break;
    case INST_TYPE_RA:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u", instr_r1);
        break;
    case INST_TYPE_RB:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u", instr_r2);
        break;
    case INST_TYPE_RD:
        snprintf(tmp_buf, sizeof(tmp_buf), "r%-2u", instr_rd);
        break;
    case INST_TYPE_IMM:
    case INST_TYPE_IMM4:
    case INST_TYPE_RDIMM:
        snprintf(tmp_buf, sizeof(tmp_buf), "%d", sext16(instr_imm));
        break;
    case INST_TYPE_IMML:
        snprintf(tmp_buf, sizeof(tmp_buf), "%u", instr_imm);
        break;
    case INST_TYPE_NULL:
        tmp_buf[0] = 0;
        break;
    default:
        snprintf(tmp_buf, sizeof(tmp_buf), "invalid");
        break;
    }

    add_str(tmp_buf);

    if (instr_op == i_lli || instr_op == i_lwi || instr_op == i_lhui || instr_op == i_lbui ||
            instr_op == i_sli || instr_op == i_swi || instr_op == i_shi || instr_op == i_sbi) {
        if (instr_r1 == 0) {
            DecodingState * state = (DecodingState *)disass_params->state;
            if (state != NULL && state->addr == ctx_addr - 4) {
                uint64_t addr = 0;
                if ((state->instr & 0xffff0000) == 0xb0000000) {
                    addr = ((state->instr & 0xffff) << 16) | instr_imm;
                }
                else if ((state->instr & 0xff000000) == 0xb2000000) {
                    addr = ((uint64_t)(state->instr & 0xffffff) << 16) | instr_imm;
                }
                else {
                    addr = sext16(instr_imm);
                }
                if (!en_64_bit) addr &= 0xffffffff;
                while (buf_pos < 32) buf[buf_pos++] = ' ';
                add_addr(addr);
            }
        }
    }
    else if ((instruction_info[instr_op].flags & F_CTRL) != 0 &&
            (instruction_info[instr_op].flags & F_IMM) != 0 &&
            (instruction_info[instr_op].flags & F_INDIRJMP) == 0 &&
            instruction_info[instr_op].type != INST_TYPE_NULL) {
        DecodingState * state = (DecodingState *)disass_params->state;
        if (state != NULL && state->addr == ctx_addr - 4) {
            uint64_t addr = 0;
            if ((state->instr & 0xffff0000) == 0xb0000000) {
                addr = ((state->instr & 0xffff) << 16) | instr_imm;
            }
            else if ((state->instr & 0xff000000) == 0xb2000000) {
                addr = ((uint64_t)(state->instr & 0xffffff) << 16) | instr_imm;
            }
            else {
                addr = sext16(instr_imm);
            }
            if ((instruction_info[instr_op].flags & F_ABS) == 0) addr += ctx_addr;
            if (!en_64_bit) addr &= 0xffffffff;
            while (buf_pos < 32) buf[buf_pos++] = ' ';
            add_addr(addr);
        }
    }

    return 0;
}

static DisassemblyResult * disassemble_instr(uint8_t * code,
        ContextAddress addr, ContextAddress size, DisassemblerParams * params) {
    static DisassemblyResult dr;
    static int ini_done = 0;
    unsigned i;

    if (!ini_done) {
        unsigned l = sizeof(opcode_hash) / sizeof(opcode_hash[0]);
        for (i = 0; i < l; i++) {
            /* make all unknown by default */
            opcode_hash[i] = UNKNOWN_OPCODE;
        }
        for (i = 0; i < UNKNOWN_OPCODE; i++) {
            unsigned n = instruction_info[i].hash;
            assert(instruction_info[i].op == (enum Instructions)i);
            assert(n < l);
            if (opcode_hash[n] != UNKNOWN_OPCODE) continue;
            opcode_hash[n] = (enum Instructions)i;
        }
        ini_done = 1;
    }

    if (size < 4) return NULL;
    buf_pos = 0;
    ctx_addr = addr;
    disass_params = params;
    instr_bits = 0;
    for (i = 0; i < 4; i++) {
        instr_bits |= (uint32_t)code[i] << (params->big_endian ? 3 - i : i) * 8;
    }
    if (disassemble_instruction() < 0) return NULL;
    buf[buf_pos++] = 0;
    memset(&dr, 0, sizeof(dr));
    dr.size = 4;
    dr.text = buf;
    if (params->state == NULL) params->state = loc_alloc_zero(sizeof(DecodingState));
    ((DecodingState *)params->state)->instr = instr_bits;
    ((DecodingState *)params->state)->addr = addr;
    return &dr;
}

DisassemblyResult * disassemble_microblaze(uint8_t * code,
        ContextAddress addr, ContextAddress size, DisassemblerParams * params) {
    en_64_bit = 0;
    return disassemble_instr(code, addr, size, params);
}

DisassemblyResult * disassemble_microblaze_x(uint8_t * code,
        ContextAddress addr, ContextAddress size, DisassemblerParams * params) {
    en_64_bit = 1;
    return disassemble_instr(code, addr, size, params);
}
