// SPDX-FileCopyrightText: 2019 v3l0c1r4pt0r <v3l0c1r4pt0r@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only

#include <rz_analysis.h>
#include "or1k_disas.h"

insn_type_descr_t types[] = {
	[INSN_X] = { INSN_X, "%s",
		{ { 0 } } },
	/* ------KKKKKAAAAABBBBBKKKKKKKKKKK */
	[INSN_KABK] = { INSN_KABK, "%s r%d, r%d, 0x%x",
		{ [INSN_OPER_K1] = { INSN_OPER_K1, INSN_K1_MASK, INSN_K1_SHIFT },
			[INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT },
			[INSN_OPER_B] = { INSN_OPER_B, INSN_B_MASK, INSN_B_SHIFT },
			[INSN_OPER_K2] = { INSN_OPER_K2, INSN_K2_MASK, INSN_EMPTY_SHIFT } } },
	/* ------IIIIIAAAAABBBBBIIIIIIIIIII */
	[INSN_IABI] = { INSN_IABI, "%s r%d, r%d, 0x%x",
		{ [INSN_OPER_K1] = { INSN_OPER_K1, INSN_K1_MASK, INSN_K1_SHIFT },
			[INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT },
			[INSN_OPER_B] = { INSN_OPER_B, INSN_B_MASK, INSN_B_SHIFT },
			[INSN_OPER_K2] = { INSN_OPER_K2, INSN_K2_MASK, INSN_EMPTY_SHIFT } } },
	/* ------NNNNNNNNNNNNNNNNNNNNNNNNNN */
	[INSN_N] = { INSN_N, "%s 0x%x",
		{ [INSN_OPER_N] = { INSN_OPER_N, INSN_N_MASK, INSN_EMPTY_SHIFT } } },
	/* ----------------KKKKKKKKKKKKKKKK */
	[INSN_K] = { INSN_K, "%s 0x%x",
		{ [INSN_OPER_K] = { INSN_OPER_K, INSN_K_MASK, INSN_EMPTY_SHIFT } } },
	/* ------DDDDD-----KKKKKKKKKKKKKKKK */
	[INSN_DK] = { INSN_DK, "%s r%d, 0x%x",
		{ [INSN_OPER_K] = { INSN_OPER_K, INSN_K_MASK, INSN_EMPTY_SHIFT },
			[INSN_OPER_D] = { INSN_OPER_D, INSN_D_MASK, INSN_D_SHIFT } } },
	/* ------DDDDDNNNNNNNNNNNNNNNNNNNNN */
	[INSN_DN] = { INSN_DN, "%s r%d, 0x%x",
		{ [INSN_OPER_N] = { INSN_OPER_N, INSN_N_MASK, INSN_EMPTY_SHIFT },
			[INSN_OPER_D] = { INSN_OPER_D, INSN_D_MASK, INSN_D_SHIFT } } },
	/* ----------------BBBBB----------- */
	[INSN_B] = { INSN_B, "%s r%d",
		{ [INSN_OPER_B] = { INSN_OPER_B, INSN_B_MASK, INSN_B_SHIFT } } },
	/* ------DDDDD--------------------- */
	[INSN_D] = { INSN_D, "%s r%d",
		{ [INSN_OPER_D] = { INSN_OPER_D, INSN_D_MASK, INSN_D_SHIFT } } },
	/* -----------AAAAAIIIIIIIIIIIIIIII */
	[INSN_AI] = { INSN_AI, "%s r%d, 0x%x",
		{ [INSN_OPER_I] = { INSN_OPER_I, INSN_I_MASK, INSN_EMPTY_SHIFT },
			[INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT } } },
	/* ------DDDDDAAAAAIIIIIIIIIIIIIIII */
	[INSN_DAI] = { INSN_DAI, "%s r%d, r%d, 0x%x",
		{ [INSN_OPER_D] = { INSN_OPER_D, INSN_D_MASK, INSN_D_SHIFT },
			[INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT },
			[INSN_OPER_I] = { INSN_OPER_I, INSN_I_MASK, INSN_EMPTY_SHIFT } } },
	/* ------DDDDDAAAAAKKKKKKKKKKKKKKKK */
	[INSN_DAK] = { INSN_DAK, "%s r%d, r%d, 0x%x",
		{ [INSN_OPER_D] = { INSN_OPER_D, INSN_D_MASK, INSN_D_SHIFT },
			[INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT },
			[INSN_OPER_I] = { INSN_OPER_I, INSN_I_MASK, INSN_EMPTY_SHIFT } } },
	/* ------DDDDDAAAAA----------LLLLLL */
	[INSN_DAL] = { INSN_DAL, "%s r%d, r%d, 0x%x",
		{ [INSN_OPER_D] = { INSN_OPER_D, INSN_D_MASK, INSN_D_SHIFT },
			[INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT },
			[INSN_OPER_L] = { INSN_OPER_L, INSN_L_MASK, INSN_EMPTY_SHIFT } } },
	/* ------DDDDDAAAAA---------------- */
	[INSN_DA] = { INSN_DA, "%s r%d, r%d",
		{ [INSN_OPER_D] = { INSN_OPER_D, INSN_D_MASK, INSN_D_SHIFT },
			[INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT } } },
	/* ------DDDDDAAAAABBBBB----------- */
	[INSN_DAB] = { INSN_DAB, "%s r%d, r%d, r%d",
		{ [INSN_OPER_D] = { INSN_OPER_D, INSN_D_MASK, INSN_D_SHIFT },
			[INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT },
			[INSN_OPER_B] = { INSN_OPER_B, INSN_B_MASK, INSN_B_SHIFT } } },
	/* -----------AAAAABBBBB----------- */
	[INSN_AB] = { INSN_AB, "%s r%d, r%d",
		{ [INSN_OPER_A] = { INSN_OPER_A, INSN_A_MASK, INSN_A_SHIFT },
			[INSN_OPER_B] = { INSN_OPER_B, INSN_B_MASK, INSN_B_SHIFT } } },
};

size_t types_count = sizeof(types) / sizeof(insn_type_descr_t);

insn_extra_t extra_0x5[] = {
	{ (0x05 << 26) | (0x1 << 24), "l.nop", INSN_K, INSN_OPCODE_MASK | (0x3 << 24), RZ_ANALYSIS_OP_TYPE_NOP },
	{ 0 }
};

insn_extra_t extra_0x6[] = {
	{ (0x06 << 26) | (0 << 16), "l.movhi", INSN_DK, INSN_OPCODE_MASK | (1 << 16) },
	{ (0x06 << 26) | (1 << 16), "l.macrc", INSN_D, INSN_OPCODE_MASK | (1 << 16) },
	{ 0 }
};

insn_extra_t extra_0x8[] = {
	{ (0x08 << 26) | (0x0), "l.sys", INSN_K, INSN_OPCODE_MASK | 0x3ff << 16 },
	{ (0x08 << 26) | (0x100), "l.trap", INSN_K, INSN_OPCODE_MASK | 0x3ff << 16 },
	{ (0x08 << 26) | (0x2000000), "l.msync", INSN_X, INSN_OPCODE_MASK | 0x3ffffff },
	{ (0x08 << 26) | (0x2800000), "l.psync", INSN_X, INSN_OPCODE_MASK | 0x3ffffff },
	{ (0x08 << 26) | (0x3000000), "l.csync", INSN_X, INSN_OPCODE_MASK | 0x3ffffff },
	{ 0 }
};

insn_extra_t extra_0x2e[] = {
	{ (0x2e << 26) | (0x0 << 6), "l.slli", INSN_DAL, INSN_OPCODE_MASK | (0x3 << 6) },
	{ (0x2e << 26) | (0x1 << 6), "l.srli", INSN_DAL, INSN_OPCODE_MASK | (0x3 << 6) },
	{ (0x2e << 26) | (0x2 << 6), "l.srai", INSN_DAL, INSN_OPCODE_MASK | (0x3 << 6) },
	{ (0x2e << 26) | (0x3 << 6), "l.rori", INSN_DAL, INSN_OPCODE_MASK | (0x3 << 6) },
	{ 0 }
};

insn_extra_t extra_0x2f[] = {
	{ (0x2f << 26) | (0x0 << 21), "l.sfeqi", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x2f << 26) | (0x1 << 21), "l.sfnei", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x2f << 26) | (0x2 << 21), "l.sfgtui", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x2f << 26) | (0x3 << 21), "l.sfgeui", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x2f << 26) | (0x4 << 21), "l.sfltui", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x2f << 26) | (0x5 << 21), "l.sfleui", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x2f << 26) | (0xa << 21), "l.sfgtsi", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) }, /* FIXME: signed */
	{ (0x2f << 26) | (0xb << 21), "l.sfgesi", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) }, /* FIXME: signed */
	{ (0x2f << 26) | (0xc << 21), "l.sfltsi", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) }, /* FIXME: signed */
	{ (0x2f << 26) | (0xd << 21), "l.sflesi", INSN_AI, INSN_OPCODE_MASK | (0x1f << 21) }, /* FIXME: signed */
	{ 0 }
};

insn_extra_t extra_0x31[] = {
	{ (0x31 << 26) | (0x1), "l.mac", INSN_AB, INSN_OPCODE_MASK | (0xf) },
	{ (0x31 << 26) | (0x3), "l.macu", INSN_AB, INSN_OPCODE_MASK | (0xf) },
	{ (0x31 << 26) | (0x2), "l.msb", INSN_AB, INSN_OPCODE_MASK | (0xf) },
	{ (0x31 << 26) | (0x4), "l.msbu", INSN_AB, INSN_OPCODE_MASK | (0xf) },
	{ 0 }
};

insn_extra_t extra_0x32[] = {
	{ (0x32 << 26) | (0x8), "lf.sfeq.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x9), "lf.sfne.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0xa), "lf.sfgt.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0xb), "lf.sfge.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0xc), "lf.sflt.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0xd), "lf.sfle.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x18), "lf.sfeq.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x19), "lf.sfne.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x1a), "lf.sfgt.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x1b), "lf.sfge.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x1c), "lf.sflt.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x1d), "lf.sfle.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x28), "lf.sfueq.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x29), "lf.sfune.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x2a), "lf.sfugt.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x2b), "lf.sfuge.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x2c), "lf.sfult.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x2d), "lf.sfule.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x2e), "lf.sfun.s", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x34), "lf.stod.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x35), "lf.dtos.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x38), "lf.sfueq.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x39), "lf.sfune.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x3a), "lf.sfugt.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x3b), "lf.sfuge.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x3c), "lf.sfult.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x3d), "lf.sfule.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x3e), "lf.sfun.d", INSN_AB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0xd << 4), "lf.cust1.s", INSN_AB, INSN_OPCODE_MASK | (0xf << 4) },
	{ (0x32 << 26) | (0xe << 4), "lf.cust1.d", INSN_AB, INSN_OPCODE_MASK | (0xf << 4) },
	{ (0x32 << 26) | (0x4), "lf.itof.s", INSN_DA, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x5), "lf.ftoi.s", INSN_DA, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x14), "lf.itof.d", INSN_DA, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x15), "lf.ftoi.d", INSN_DA, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x0), "lf.add.s", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x1), "lf.sub.s", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x2), "lf.mul.s", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x3), "lf.div.s", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x7), "lf.madd.s", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x10), "lf.add.d", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x11), "lf.sub.d", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x12), "lf.mul.d", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x13), "lf.div.d", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ (0x32 << 26) | (0x17), "lf.madd.d", INSN_DAB, INSN_OPCODE_MASK | (0xff) },
	{ 0 }
};

insn_extra_t extra_0x38[] = {
	{ (0x38 << 26) | (0x0 << 6) | (0xc), "l.exths", INSN_DA, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0xd), "l.extws", INSN_DA, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x1 << 6) | (0xc), "l.extbs", INSN_DA, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x1 << 6) | (0xd), "l.extwz", INSN_DA, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x2 << 6) | (0xc), "l.exthz", INSN_DA, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x3 << 6) | (0xc), "l.extbz", INSN_DA, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0x0), "l.add", INSN_DAB, INSN_OPCODE_MASK | (0xc << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0x1), "l.addc", INSN_DAB, INSN_OPCODE_MASK | (0xc << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0x2), "l.sub", INSN_DAB, INSN_OPCODE_MASK | (0xc << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0x3), "l.and", INSN_DAB, INSN_OPCODE_MASK | (0xc << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0x4), "l.or", INSN_DAB, INSN_OPCODE_MASK | (0xc << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0x5), "l.xor", INSN_DAB, INSN_OPCODE_MASK | (0xc << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0xe), "l.cmov", INSN_DAB, INSN_OPCODE_MASK | (0xc << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0xf), "l.ff1", INSN_DA, INSN_OPCODE_MASK | (0xc << 6) | 0xf },
	{ (0x38 << 26) | (0x0 << 6) | (0x8), "l.sll", INSN_DAB, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x1 << 6) | (0x8), "l.srl", INSN_DAB, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x2 << 6) | (0x8), "l.sra", INSN_DAB, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x3 << 6) | (0x8), "l.ror", INSN_DAB, INSN_OPCODE_MASK | (0xf << 6) | 0xf },
	{ (0x38 << 26) | (0x1 << 8) | (0xf), "l.fl1", INSN_DA, INSN_OPCODE_MASK | (0x3 << 8) | 0xf },
	{ (0x38 << 26) | (0x3 << 8) | (0x6), "l.mul", INSN_DAB, INSN_OPCODE_MASK | (0x3 << 8) | 0xf },
	{ (0x38 << 26) | (0x3 << 8) | (0x7), "l.muld", INSN_AB, INSN_OPCODE_MASK | (0x3 << 8) | 0xf },
	{ (0x38 << 26) | (0x3 << 8) | (0x9), "l.div", INSN_DAB, INSN_OPCODE_MASK | (0x3 << 8) | 0xf },
	{ (0x38 << 26) | (0x3 << 8) | (0xa), "l.divu", INSN_DAB, INSN_OPCODE_MASK | (0x3 << 8) | 0xf },
	{ (0x38 << 26) | (0x3 << 8) | (0xb), "l.mulu", INSN_DAB, INSN_OPCODE_MASK | (0x3 << 8) | 0xf },
	{ (0x38 << 26) | (0x3 << 8) | (0xc), "l.muldu", INSN_AB, INSN_OPCODE_MASK | (0x3 << 8) | 0xf },
	{ 0 }
};

insn_extra_t extra_0x39[] = {
	{ (0x39 << 26) | (0x0 << 21), "l.sfeq", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0x1 << 21), "l.sfne", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0x2 << 21), "l.sfgtu", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0x3 << 21), "l.sfgeu", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0x4 << 21), "l.sfltu", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0x5 << 21), "l.sfleu", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0xa << 21), "l.sfgts", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0xb << 21), "l.sfges", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0xc << 21), "l.sflts", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ (0x39 << 26) | (0xd << 21), "l.sfles", INSN_AB, INSN_OPCODE_MASK | (0x1f << 21) },
	{ 0 }
};

insn_t or1k_insns[] = {
	[0x00] = { (0x00 << 26), "l.j", INSN_N, RZ_ANALYSIS_OP_TYPE_JMP },
	[0x01] = { (0x01 << 26), "l.jal", INSN_N, RZ_ANALYSIS_OP_TYPE_CALL },
	[0x02] = { (0x02 << 26), "l.adrp", INSN_DN },
	[0x03] = { (0x03 << 26), "l.bnf", INSN_N, RZ_ANALYSIS_OP_TYPE_CJMP },
	[0x04] = { (0x04 << 26), "l.bf", INSN_N, RZ_ANALYSIS_OP_TYPE_CJMP },
	[0x05] = { (0x05 << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x5 },
	[0x06] = { (0x06 << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x6 },
	[0x07] = { (0x07 << 26) },
	[0x08] = { (0x08 << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x8 },
	[0x09] = { (0x09 << 26), "l.rfe", INSN_X },
	[0x0a] = { (0x0a << 26), "lv.ext0a", INSN_X }, /* TODO: implement */
	[0x0b] = { (0x0b << 26) },
	[0x0c] = { (0x0c << 26) },
	[0x0d] = { (0x0d << 26) },
	[0x0e] = { (0x0e << 26) },
	[0x0f] = { (0x0f << 26) },
	[0x10] = { (0x10 << 26) },
	[0x11] = { (0x11 << 26), "l.jr", INSN_B, RZ_ANALYSIS_OP_TYPE_JMP },
	[0x12] = { (0x12 << 26), "l.jalr", INSN_B, RZ_ANALYSIS_OP_TYPE_CALL },
	[0x13] = { (0x13 << 26), "l.maci", INSN_AI },
	[0x14] = { (0x14 << 26) },
	[0x15] = { (0x15 << 26) },
	[0x16] = { (0x16 << 26) },
	[0x17] = { (0x17 << 26) },
	[0x18] = { (0x18 << 26) },
	[0x19] = { (0x19 << 26) },
	[0x1a] = { (0x1a << 26), "l.lf", INSN_DAI },
	[0x1b] = { (0x1b << 26), "l.lwa", INSN_DAI },
	[0x1c] = { (0x1c << 26), "l.cust1", INSN_X },
	[0x1d] = { (0x1d << 26), "l.cust2", INSN_X },
	[0x1e] = { (0x1e << 26), "l.cust3", INSN_X },
	[0x1f] = { (0x1f << 26), "l.cust4", INSN_X },
	[0x20] = { (0x20 << 26), "l.ld", INSN_DAI },
	[0x21] = { (0x21 << 26), "l.lwz", INSN_DAI },
	[0x22] = { (0x22 << 26), "l.lws", INSN_DAI },
	[0x23] = { (0x23 << 26), "l.lbz", INSN_DAI },
	[0x24] = { (0x24 << 26), "l.lbs", INSN_DAI },
	[0x25] = { (0x25 << 26), "l.lhz", INSN_DAI },
	[0x26] = { (0x26 << 26), "l.lhs", INSN_DAI },
	[0x27] = { (0x27 << 26), "l.addi", INSN_DAI, RZ_ANALYSIS_OP_TYPE_LOAD },
	[0x28] = { (0x28 << 26), "l.addic", INSN_DAI },
	[0x29] = { (0x29 << 26), "l.andi", INSN_DAK },
	[0x2a] = { (0x2a << 26), "l.ori", INSN_DAK, RZ_ANALYSIS_OP_TYPE_LOAD },
	[0x2b] = { (0x2b << 26), "l.xori", INSN_DAI },
	[0x2c] = { (0x2c << 26), "l.muli", INSN_DAI },
	[0x2d] = { (0x2d << 26), "l.mfspr", INSN_DAK },
	[0x2e] = { (0x2e << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x2e },
	[0x2f] = { (0x2f << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x2f },
	[0x30] = { (0x30 << 26), "l.mtspr", INSN_KABK },
	[0x31] = { (0x31 << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x31 },
	[0x32] = { (0x32 << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x32 },
	[0x33] = { (0x33 << 26), "l.swa", INSN_IABI },
	[0x34] = { (0x34 << 26) },
	[0x35] = { (0x35 << 26), "l.sw", INSN_IABI },
	[0x36] = { (0x36 << 26), "l.sb", INSN_IABI },
	[0x37] = { (0x37 << 26), "l.sh", INSN_IABI },
	[0x38] = { (0x38 << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x38 },
	[0x39] = { (0x39 << 26), NULL, INSN_X, RZ_ANALYSIS_OP_TYPE_NULL, extra_0x39 },
	[0x3a] = { (0x3a << 26) },
	[0x3b] = { (0x3b << 26) },
	[0x3c] = { (0x3c << 26), "l.cust5", INSN_X },
	[0x3d] = { (0x3d << 26), "l.cust6", INSN_X },
	[0x3e] = { (0x3e << 26), "l.cust7", INSN_X },
	[0x3f] = { (0x3f << 26), "l.cust8", INSN_X },
};

size_t insns_count = sizeof(or1k_insns) / sizeof(insn_t);

insn_extra_t *find_extra_descriptor(insn_extra_t *extra_descr, ut32 insn) {
	ut32 opcode;
	while (extra_descr->type != INSN_END) {
		opcode = (insn & extra_descr->opcode_mask);
		if (extra_descr->opcode == opcode) {
			break;
		}
		extra_descr++;
	}
	if (extra_descr->type != INSN_END) {
		return extra_descr;
	} else {
		return NULL;
	}
}

ut32 sign_extend(ut32 number, ut32 mask) {
	/* xor of mask with itself shifted left detects msb of mask and msb of space
	 * on the right. And discards the latter */
	ut32 first_bit = (mask ^ (mask >> 1)) & mask;
	/* if first bit is set */
	if (number & first_bit) {
		/* set every bit outside mask */
		number |= ~mask;
	}
	return number;
}
