/* V850 disassembler inspired by the GNU binutils one -- 2021-2024 - pancake */

#include "v850dis.h"
#include "opc.inc.c"

enum {
	V850_ARG_TYPE_UNKNOWN,
	V850_ARG_TYPE_STRING,
	V850_ARG_TYPE_NUMBER
};

static const ut8 v850_cacheop_codes[] = {
	0x00, 0x20, 0x40, 0x60, 0x61, 0x04, 0x06,
	0x07, 0x24, 0x26, 0x27, 0x44, 0x64, 0x65
};

static const ut8 v850_prefop_codes[] = { 0x00, 0x04 };

static void print_value_dec(RStrBuf *sb, int flags, st64 value) {
	if (flags & V850_OPERAND_SIGNED) {
		r_strbuf_appendf (sb, "%"PFMT64d, value);
	} else {
		r_strbuf_appendf (sb, "%"PFMT64u, (ut64)value);
	}
}

static void print_value(RStrBuf *sb, int flags, ut64 memaddr, st64 value, ut32 pc_mask) {
	if (flags & V850_PCREL) {
		ut64 addr = value + memaddr;
		if (flags & V850_INVERSE_PCREL) {
			addr = memaddr - value;
		}
		r_strbuf_appendf (sb, "0x%"PFMT64x, (ut64)addr & pc_mask);
	} else if (flags & V850_OPERAND_DISP) {
		print_value_dec (sb, flags, value);
	} else if ((flags & V850E_IMMEDIATE32) || (flags & V850E_IMMEDIATE16HI)) {
		r_strbuf_appendf (sb, "0x%"PFMT64x, (ut64)value);
	} else {
		print_value_dec (sb, flags, value);
	}
}

static st64 get_operand_value(const struct v850_operand *operand, unsigned long insn, const ut8* buffer, size_t len, bool *invalid) {
	if ((operand->flags & V850E_IMMEDIATE16) || (operand->flags & V850E_IMMEDIATE16HI)) {
		if (len < 2) {
			// truncated
			return 0;
		}
		st64 value = r_read_le16 (buffer);
		if (operand->flags & V850E_IMMEDIATE16HI) {
			value <<= 16;
		} else if (value & 0x8000) {
			value -= 0x10000;
		}
		return value;
	}

	if (operand->flags & V850E_IMMEDIATE23) {
		if (len < 2) {
			// truncated
			return 0;
		}
		ut32 value = (insn >> 16) | ((ut32)r_read_le16 (buffer) << 16);
		return operand->extract (value, invalid);
	}
	if (operand->flags & V850E_IMMEDIATE32) {
		if (len < 4) {
			// truncated
			return 0;
		}
		// len += 4;
		return (ut32)r_read_le32 (buffer);
	}
	if (operand->extract) {
		return (st64)operand->extract (insn, invalid);
	}
	ut64 value = (operand->bits == -1)
		? (insn & operand->shift)
		: (insn >> operand->shift) & ((1ULL << operand->bits) - 1);
	if (operand->flags & V850_OPERAND_SIGNED) {
		ut64 sign = 1ULL << (operand->bits - 1);
		value = (value ^ sign) - sign;
	}
	return (st64)value;
}

static const char *get_v850_sreg_name(size_t reg) {
#if 0
	static const char *const v850_sreg_names[] = {
		"eipc/vip/mpm", "eipsw/mpc", "fepc/tid", "fepsw/ppa", "ecr/vmecr", "psw/vmtid",
		"sr6/fpsr/vmadr/dcc", "sr7/fpepc/dc0",
		"sr8/fpst/vpecr/dcv1", "sr9/fpcc/vptid", "sr10/fpcfg/vpadr/spal", "sr11/spau",
		"sr12/vdecr/ipa0l", "eiic/vdtid/ipa0u", "feic/ipa1l", "dbic/ipa1u",
		"ctpc/ipa2l", "ctpsw/ipa2u", "dbpc/ipa3l", "dbpsw/ipa3u", "ctbp/dpa0l",
		"dir/dpa0u", "bpc/dpa0u", "asid/dpa1l",
		"bpav/dpa1u", "bpam/dpa2l", "bpdv/dpa2u", "bpdm/dpa3l", "eiwr/dpa3u",
		"fewr", "dbwr", "bsel"
	};
#else
	static const char *const v850_sreg_names[] = {
		"eipc", "eipsw", "fepc", "fepsw", "ecr", "psw",
		"sr6", "sr7", "sr8", "sr9", "sr10", "sr11",
		"sr12", "eiic", "feic", "dbic",
		"ctpc", "ctpsw", "dbpc", "dbpsw", "ctbp",
		"dir", "bpc", "asid",
		"bpav", "bpam", "bpdv", "bpdm", "eiwr",
		"fewr", "dbwr", "bsel"
	};
#endif
	if (reg < R_ARRAY_SIZE (v850_sreg_names)) {
		return v850_sreg_names[reg];
	}
	return "<invalid s-reg number>";
}

static const char *get_v850_reg_name(size_t reg) {
	static const char *const v850_reg_names[] = {
		"r0", "r1", "r2", "sp", "gp", "tp", "r6", "r7",
		"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
		"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
		"r24", "r25", "r26", "r27", "r28", "r29", "ep", "lp"
	};
	R_RETURN_VAL_IF_FAIL (reg < R_ARRAY_SIZE (v850_reg_names), NULL);
	return v850_reg_names[reg];
}

static const char *get_v850_vreg_name(unsigned int reg) {
	static const char *const v850_vreg_names[] = {
		"vr0", "vr1", "vr2", "vr3", "vr4", "vr5", "vr6", "vr7", "vr8", "vr9",
		"vr10", "vr11", "vr12", "vr13", "vr14", "vr15", "vr16", "vr17", "vr18",
		"vr19", "vr20", "vr21", "vr22", "vr23", "vr24", "vr25", "vr26", "vr27",
		"vr28", "vr29", "vr30", "vr31"
	};
	R_RETURN_VAL_IF_FAIL (reg < R_ARRAY_SIZE (v850_vreg_names), NULL);
	return v850_vreg_names[reg];
}

static const char *get_v850_cc_name(unsigned int reg) {
	static const char *const v850_cc_names[] = {
		"v", "c/l", "z", "nh", "s/n", "t", "lt", "le",
		"nv", "nc/nl", "nz", "h", "ns/p", "sa", "ge", "gt"
	};
	R_RETURN_VAL_IF_FAIL (reg < R_ARRAY_SIZE (v850_cc_names), NULL);
	return v850_cc_names[reg];
}

static const char *get_v850_float_cc_name(unsigned int reg) {
	static const char *const v850_float_cc_names[] = {
		"f/t", "un/or", "eq/neq", "ueq/ogl", "olt/uge", "ult/oge", "ole/ugt", "ule/ogt",
		"sf/st", "ngle/gle", "seq/sne", "ngl/gl", "lt/nlt", "nge/ge", "le/nle", "ngt/gt"
	};
	R_RETURN_VAL_IF_FAIL (reg < R_ARRAY_SIZE (v850_float_cc_names), NULL);
	return v850_float_cc_names[reg];
}

static const char *get_v850_cacheop_name(size_t reg) {
	static const char *const v850_cacheop_names[] = {
		"chbii", "cibii", "cfali", "cisti", "cildi", "chbid", "chbiwbd",
		"chbwbd", "cibid", "cibiwbd", "cibwbd", "cfald", "cistd", "cildd"
	};
	R_RETURN_VAL_IF_FAIL (reg < R_ARRAY_SIZE (v850_cacheop_names), NULL);
	return v850_cacheop_names[reg];
}

static const char *get_v850_prefop_name(size_t reg) {
	static const char *const v850_prefop_names[] = { "prefi", "prefd" };
	R_RETURN_VAL_IF_FAIL (reg < R_ARRAY_SIZE (v850_prefop_names), NULL);
	return v850_prefop_names[reg];
}

static int print_prefop(RStrBuf *sb, int value) {
	int idx;
	for (idx = 0; idx < R_ARRAY_SIZE (v850_prefop_codes); idx++) {
		if (value == v850_prefop_codes[idx]) {
			r_strbuf_append (sb, get_v850_prefop_name (idx));
			return V850_ARG_TYPE_STRING;
		}
	}
	r_strbuf_appendf (sb, "%d", (int)value);
	return V850_ARG_TYPE_NUMBER;
}

static int print_cacheop(RStrBuf *sb, int value) {
	int idx;
	for (idx = 0; idx < R_ARRAY_SIZE (v850_cacheop_codes); idx++) {
		if (value == v850_cacheop_codes[idx]) {
			r_strbuf_append (sb, get_v850_cacheop_name (idx));
			return V850_ARG_TYPE_STRING;
		}
	}
	r_strbuf_appendf (sb, "%d", (int)value);
	return V850_ARG_TYPE_NUMBER;
}

static bool print_reglist(RStrBuf *sb, v850np_inst *inst, const struct v850_operand *operand, st64 value) {
	static const int list12_regs[32]  = {
		30, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
		0,  0, 0, 0, 0, 31, 29, 28, 23, 22, 21, 20, 27, 26, 25, 24
	};
	const int *regs;
	int i, pc = 0;
	ut32 mask = 0;

	switch (operand->shift) {
	case 0xffe00001:
		regs = list12_regs;
		break;
	default:
		// eprintf ("unknown operand shift: 0x%x\n", operand->shift);
		return false;
	}

	for (i = 0; i < 32; i++) {
		if ((ut64)value & (1ULL << i)) {
			switch (regs[i]) {
			case 0:
				/* xgettext:c-format */
				// eprintf ("unknown reg: %d at 0x%x\n", i);
				// inst->text = r_strbuf_drain (sb);
				return false;
			case -1:
				pc = 1;
				break;
			default:
				mask |= (1u << regs[i]);
				break;
			}
		}
	}

	r_strbuf_append (sb, "{");
	if (mask) {
		ut32 bit;
		const char *comma = "";
		for (bit = 0; bit < 32; bit++) {
			if (mask & (1u << bit)) {
				ut32 first = bit;
				r_strbuf_appendf (sb, "%s%s", comma, get_v850_reg_name (first));
				comma = ", ";

				// for (bit++; bit < 32 && (mask & (1u << bit)); bit++) {}
				for (bit++; bit < 32; bit++) {
					if ((mask & (1u << bit)) == 0) {
						break;
					}
				}
				ut32 last = bit;

				if (last > first + 1) {
					for (i = first + 1; i < last ; i++) {
						const char *rn = get_v850_reg_name (i);
						if (rn) {
							r_strbuf_appendf (sb, ", %s", rn);
						}
					}
					//r_strbuf_appendf (sb, " - %s", get_v850_reg_name (last - 1));
				}
			}
		}
	}
	if (pc) {
		r_strbuf_appendf (sb, "%sPC", mask ? ", " : "");
	}
	r_strbuf_append (sb, "}");
	return true;
}

static char *saturated_expression(const char *lhs, const char *rhs, const char *dst, bool subtract) {
	RStrBuf *sb = r_strbuf_newf ("%s,0xffffffff,&,%s,0xffffffff,&,%c", rhs, lhs, subtract? '-': '+');
	if (subtract) {
		r_strbuf_appendf (sb, ",%s,0xffffffff,&,%s,0xffffffff,&,<,cy,:=", rhs, lhs);
	} else {
		r_strbuf_append (sb, ",DUP,32,SWAP,>>,1,&,cy,:=");
	}
	r_strbuf_appendf (sb, ",DUP,%s,^,%s,%s,^%s,&,0x80000000,&,!,!,ov,:=", lhs, rhs, lhs, subtract? "": ",0xffffffff,^");
	r_strbuf_appendf (sb, ",ov,?{,POP,1,sat,:=,0x80000000,%s,&,?{,0x80000000,}{,0x7fffffff,},}", lhs);
	r_strbuf_appendf (sb, ",DUP,31,SWAP,>>,1,&,s,:=,DUP,0xffffffff,&,!,z,:=,%s,:=", dst);
	return r_strbuf_drain (sb);
}

static char *conditional_arithmetic_expression(const char *condition, const char *lhs, const char *rhs, const char *dst, bool subtract) {
	RStrBuf *sb = r_strbuf_newf ("%s,!,!,%s,+,%s,%c", condition, rhs, lhs, subtract? '-': '+');
	if (subtract) {
		r_strbuf_appendf (sb, ",%s,!,!,%s,+,%s,0xffffffff,&,<,cy,:=", condition, rhs, lhs);
	} else {
		r_strbuf_append (sb, ",DUP,32,SWAP,>>,1,&,cy,:=");
	}
	r_strbuf_appendf (sb, ",DUP,%s,^,%s,%s,^%s,&,0x80000000,&,!,!,ov,:=", lhs, rhs, lhs, subtract? "": ",0xffffffff,^");
	r_strbuf_appendf (sb, ",DUP,31,SWAP,>>,1,&,s,:=,DUP,0xffffffff,&,!,z,:=,%s,:=", dst);
	return r_strbuf_drain (sb);
}

static char *distillate(v850np_inst *inst, const char *esilfmt, ut64 memaddr, int cpumodel) {
	const ut32 pc_mask = v850_pc_mask (cpumodel);
	RStrBuf *sb = r_strbuf_new ("");
	RList *args = r_list_newf (free);
	int i;
	// ESIL operands must not depend on absolute-address display formatting.
	for (i = 0; i < R_ARRAY_SIZE (inst->args) && inst->op->operands[i]; i++) {
		const struct v850_operand *operand = &v850_operands[inst->op->operands[i]];
		const st64 value = inst->args[i].value;
		RStrBuf *arg = r_strbuf_new ("");
		if (operand->flags & V850_OPERAND_REG) {
			r_strbuf_append (arg, get_v850_reg_name ((operand->flags & V850_REG_EVEN)? value * 2: value));
		} else if (operand->flags & V850_OPERAND_EP) {
			r_strbuf_append (arg, "ep");
		} else if (operand->flags & V850_OPERAND_SRG) {
			r_strbuf_append (arg, get_v850_sreg_name (value));
		} else if (operand->flags & V850_OPERAND_CC) {
			static const char *conditions[] = {
				"ov", "cy", "z", "cy,z,|", "s", "1", "s,ov,^", "s,ov,^,z,|",
				"ov,!", "cy,!", "z,!", "cy,z,|,!", "s,!", "sat", "s,ov,^,!", "s,ov,^,z,|,!"
			};
			r_strbuf_append (arg, conditions[value & 15]);
		} else if (operand->flags & V850E_OPERAND_REG_LIST) {
			print_reglist (arg, inst, operand, value);
		} else {
			print_value (arg, operand->flags, memaddr, value, pc_mask);
		}
		char *text = r_strbuf_drain (arg);
		text = r_str_replace (text, "{", "", true);
		text = r_str_replace (text, "}", "", true);
		r_str_replace_ch (text, ' ', 0, true);
		r_list_append (args, text);
	}
	if (args) {
		while (*esilfmt) {
			char ch = *esilfmt;
			if (ch == '#') {
				if (esilfmt[1] == 'P') {
					r_strbuf_appendf (sb, "0x%x", pc_mask & ~1U);
					esilfmt += 2;
					continue;
				}
				const int n = esilfmt[1] - '0';
				if (n >= 0 && n < 10) {
					const char *argn = (const char *)r_list_get_n (args, n);
					r_strbuf_appendf (sb, "%s", argn);
					esilfmt += 2;
					continue;
				}
			}
			r_strbuf_append_n (sb, &ch, 1);
			esilfmt++;
		}
	}
	char *res = r_strbuf_drain (sb);
	if (!strcmp (res, "CONDITIONAL")) {
		free (res);
		res = conditional_arithmetic_expression (r_list_get_n (args, 0), r_list_get_n (args, 2),
			r_list_get_n (args, 1), r_list_get_n (args, 3), !strcmp (inst->op->name, "sbf"));
	} else if (!strcmp (res, "MAC")) {
		const unsigned accumulator = inst->args[2].value * 2;
		const unsigned destination = inst->args[3].value * 2;
		const char *extend = !strcmp (inst->op->name, "mac")? ",32,SWAP,~": "";
		free (res);
		res = r_str_newf ("%s%s,%s%s,*,32,%s,<<,%s,|,+,DUP,%s,:=,32,SWAP,>>,%s,:=",
			(char *)r_list_get_n (args, 0), extend, (char *)r_list_get_n (args, 1), extend,
			get_v850_reg_name (accumulator + 1), get_v850_reg_name (accumulator),
			get_v850_reg_name (destination), get_v850_reg_name (destination + 1));
	}
	if (!strcmp (inst->op->name, "trap") && (cpumodel & V850_CPU_E2_UP)) {
		char *with_cause = r_str_newf ("%s,0x%x,eiic,:=", res, 0x40 + (unsigned)inst->args[0].value);
		free (res);
		res = with_cause;
	}
	if (!strcmp (inst->op->name, "ldsr") && inst->op->operands[1] == OLDSR2 && inst->args[1].value == 4) {
		free (res);
		res = strdup (",");
	}
	if (!strcmp (res, "SATURATE")) {
		bool reverse = !strcmp (inst->op->name, "satsubr");
		const char *lhs = r_list_get_n (args, reverse? 0: 1);
		const char *rhs = r_list_get_n (args, reverse? 1: 0);
		const char *dst = r_list_get_n (args, inst->op->operands[2]? 2: 1);
		free (res);
		res = saturated_expression (lhs, rhs, dst, strcmp (inst->op->name, "satadd"));
	}
	const bool prepare = r_str_startswith (res, "PREPARE,");
	if (prepare || r_str_startswith (res, "DISPOSE,")) {
		const int list_index = prepare? 0: 1;
		const int allocation = inst->args[prepare? 1: 0].value * 4;
		RList *regs = r_str_split_list (r_list_get_n (args, list_index), ",", 0);
		RListIter *iter;
		char *reg;
		RStrBuf *stack = r_strbuf_new ("");
		if (prepare) {
			r_list_foreach (regs, iter, reg) {
				if (*reg) {
					r_strbuf_appendf (stack, "4,sp,-=,%s,sp,0xfffffffc,&,=[4],", reg);
				}
			}
			r_strbuf_appendf (stack, "%d,sp,-=", allocation);
			if (inst->op->operands[2]) {
				r_strbuf_appendf (stack, ",%s,ep,:=", (char *)r_list_get_n (args, 2));
			}
		} else {
			r_strbuf_appendf (stack, "%d,sp,+=", allocation);
			r_list_foreach_prev (regs, iter, reg) {
				if (*reg) {
					r_strbuf_appendf (stack, ",sp,0xfffffffc,&,[4],%s,:=,4,sp,+=", reg);
				}
			}
			if (inst->op->operands[2]) {
				r_strbuf_appendf (stack, ",0x%x,%s,&,PC,:=", pc_mask & ~1U, (char *)r_list_get_n (args, 2));
			}
		}
		free (res);
		res = r_strbuf_drain (stack);
		r_list_free (regs);
	}
	r_list_free (args);
	return res;
}

// do absolute addressing on references relative to r0
#define ABS_R0REF 1

static bool v850np_disassemble(v850np_inst *inst, int cpumodel, ut64 memaddr, const ut8* buffer, int buffer_size, unsigned long insn) {
	const v850_opcode *op = (v850_opcode *) v850_opcodes;
	const struct v850_operand *operand = NULL;
	bool match = false;
	RStrBuf *sb = r_strbuf_new ("");

	/* If this is a two byte insn, then mask off the high bits.  */
	if (buffer_size == 2) {
		insn &= 0xffff;
	}
	inst->text = NULL;
	/* Find the opcode.  */
	for (inst->text = NULL; op->name; op++) {
		if (op->processors & V850_CPU_OPTION_ALIAS) {
			continue;
		}
		if ((op->mask & insn) != op->opcode || !(op->processors & cpumodel)) {
			continue;
		}
		/* Code check start.  */
		const ut8 *opindex_ptr;
		unsigned int opnum;
		unsigned int memop;

		const unsigned extra = op->mask > 0xffff? 4: 2;
		bool operand_fail = false;
		for (opindex_ptr = op->operands, opnum = 1; !operand_fail && *opindex_ptr != 0; opindex_ptr++, opnum++) {
			bool invalid = false;

			operand = &v850_operands[*opindex_ptr];

			st64 value = get_operand_value (operand, insn, buffer + extra, buffer_size - extra, &invalid);
			if (value) {
				inst->value = value;
			}

			operand_fail = true;
			if (invalid) {
			} else if ((operand->flags & V850_NOT_R0) && value == 0 && op->memop <= 2) {
			} else if ((operand->flags & V850_NOT_SA) && value == 0xd) {
			} else if ((operand->flags & V850_NOT_IMM0) && value == 0) {
			} else {
				operand_fail = false;
			}
		}
		if (operand_fail) {
			continue;
		}

		/* Code check end.  */

		match = true;
		r_strbuf_append (sb, op->name);
		r_strbuf_append (sb, " ");
		inst->op = op;
		memop = op->memop;
		/* Now print the operands.

		   MEMOP is the operand number at which a memory
		   address specification starts, or zero if this
		   instruction has no memory addresses.

		   A memory address is always two arguments.

		   This information allows us to determine when to
		   insert commas into the output stream as well as
		   when to insert disp[reg] expressions onto the
		   output stream.  */

		int atype = 0;
		inst->value = 0;
		opindex_ptr = op->operands;
		opnum = 1;
		st64 prevalue = 0;
		st64 value = 0;
		for (; *opindex_ptr; opindex_ptr++, opnum++) {
			bool square = false;

			bool done = false;
			operand = &v850_operands[*opindex_ptr];
			prevalue = value;

			bool invalid = false;
			value = get_operand_value (operand, insn, buffer + extra, buffer_size - extra, &invalid);
			if (invalid) {
				// R_LOG_WARN ("Cannot get operand value");
				break;
			}

			// first argument have no special processing
			const char *prefix = (operand->flags & V850_OPERAND_BANG)? "|" :(operand->flags & V850_OPERAND_PERCENT)? "%":"";
#define IS_PUSHPOP(x) (!strcmp ((x), "pushsp") || !strcmp ((x), "popsp") || !strcmp ((x), "dbpush" ))
			if (opnum == 1 && opnum == memop) {
				r_strbuf_append (sb, "[");
				square = true;
			} else if ((!strcmp ("stc.w", op->name) || !strcmp ("cache", op->name) || !strcmp ("pref",  op->name))
					&& opnum == 2 && opnum == memop) {
				r_strbuf_append (sb, ", [");
				square = true;
			} else if (IS_PUSHPOP (op->name) && opnum == 2) {
				r_strbuf_append (sb, "-");
			} else if (opnum > 1
					&& (v850_operands[*(opindex_ptr - 1)].flags & V850_OPERAND_DISP)
					&& opnum == memop) {
#if ABS_R0REF
				if (opnum == 2 && value == 0) { // "-X[r0]"
					ut32 addr = (ut32)prevalue;
					// uncommenting this breaks `rasm2 -a v850 -d 01fb`
					// trim last char and append this
					char *s = r_strbuf_drain (sb);
					if (*s) {
						s[strlen (s) - 1] = 0;
						r_str_trim (s);
					}
					if (addr < 256) {
						sb = r_strbuf_newf ("%s %"PFMT32d, s, addr);
					} else {
						sb = r_strbuf_newf ("%s 0x%08"PFMT32x, s, addr);
					}
					free (s);
					// r_strbuf_appendf (sb, "0x%08"PFMT32x, addr);
					inst->value = addr;
					done = true;
				} else {
					r_strbuf_appendf (sb, "%s[", prefix);
					done = false;
					square = true;
				}
#else
				r_strbuf_appendf (sb, "%s[", prefix);
				square = true;
#endif
			} else if (opnum == 2 && (op->opcode == 0x00e407e0 /* clr1 */
						|| op->opcode == 0x00e207e0 /* not1 */
						|| op->opcode == 0x00e007e0 /* set1 */
						|| op->opcode == 0x00e607e0 /* tst1 */
					   )) {
				r_strbuf_appendf (sb, ", %s[", prefix);
				square = true;
			} else if (opnum > 1) {
				r_strbuf_appendf (sb, ", %s", prefix);
			}

			/* Extract the flags, ignoring ones which do not
			   effect disassembly output.  */
			ut32 flag = operand->flags & (V850_OPERAND_REG
					| V850_REG_EVEN
					| V850_OPERAND_EP
					| V850_OPERAND_SRG
					| V850E_OPERAND_REG_LIST
					| V850_OPERAND_CC
					| V850_OPERAND_VREG
					| V850_OPERAND_CACHEOP
					| V850_OPERAND_PREFOP
					| V850_OPERAND_FLOAT_CC);

			if (flag == V850_OPERAND_REG) {
				inst->args[opnum - 1].str = get_v850_reg_name (value);
			} else if (flag == V850_OPERAND_EP) {
				inst->args[opnum - 1].str = "ep";
			}
			if (!done)
			switch (flag) {
			case V850_OPERAND_REG:
				r_strbuf_append (sb, inst->args[opnum - 1].str);
				break;
			case (V850_OPERAND_REG|V850_REG_EVEN):
				r_strbuf_append (sb, get_v850_reg_name (value * 2));
				break;
			case V850_OPERAND_EP:
				r_strbuf_append (sb, "ep");
				break;
			case V850_OPERAND_SRG:
				r_strbuf_append (sb, get_v850_sreg_name (value));
				break;
			case V850E_OPERAND_REG_LIST:
				if (!print_reglist (sb, inst, operand, value)) {
					goto fail;
				}
				break;
			case V850_OPERAND_CC:
				r_strbuf_append (sb, get_v850_cc_name (value));
				break;
			case V850_OPERAND_FLOAT_CC:
				r_strbuf_append (sb, get_v850_float_cc_name (value));
				break;
			case V850_OPERAND_CACHEOP:
				atype = print_cacheop (sb, value);
				break;
			case V850_OPERAND_PREFOP:
				atype = print_prefop (sb, value);
				break;
			case V850_OPERAND_VREG:
				r_strbuf_append (sb, get_v850_vreg_name (value));
				break;
			default:
				{
#if ABS_R0REF
					const struct v850_operand *nextop = &v850_operands[opindex_ptr[1]];
					st64 nextvalue = get_operand_value (nextop, insn, buffer + extra, buffer_size - extra, &invalid);
					if (opnum > 0 && (v850_operands[*(opindex_ptr)].flags & V850_OPERAND_DISP)
							&& opnum == 1 && opnum + 1 == memop && nextvalue == 0 && value < 1) {
						// dont print coz we replace later
						//r_strbuf_append (sb, "?");
					} else {
						print_value (sb, operand->flags, memaddr, value, v850_pc_mask (cpumodel));
						inst->value = value;
					}
#else
					print_value (sb, operand->flags, memaddr, value, v850_pc_mask (cpumodel));
					inst->value = value;
#endif
				}
				break;
			}
			inst->args[opnum - 1].atype = atype;
			inst->args[opnum - 1].value = value;
			// eprintf ("atype=%d\n", atype);
			if (square) {
				r_strbuf_append (sb, "]");
			}
		}
		if (!*opindex_ptr) {
			break;
		}
	}

	if (match) {
		inst->text = r_strbuf_drain (sb);
		if (op->esil) {
			// take advantage of the arguments parsed and honor the formats tring below
			inst->esil = distillate (inst, op->esil, memaddr, cpumodel);
		}
	} else {
		r_strbuf_free (sb);
		return false;
	}
	return true;
fail:
	r_strbuf_free (sb);
	return false;
}

int v850np_disasm(v850np_inst *inst, int cpumodel, ut64 addr, const ut8* buffer, size_t len) {
	int length = 0, code_length = 0;
	if (len < 2) {
		return -1;
	}
	ut32 insn = r_read_le16 (buffer);
	ut32 insn2 = (len < 4)? 0: r_read_le16 (buffer + 2);

	/* Special case.  */
	if (length == 0 && ((cpumodel & V850_CPU_E2_UP) != 0)) {
		if ((insn & 0xffff) == 0x02e0 && (insn2 & 0x1) == 0) {
			/* jr 32bit */
			length = 2;
			code_length = 6;
		} else if ((insn & 0xffe0) == 0x02e0 && (insn2 & 0x1) == 0) {
			/* jarl 32bit */
			length = 2;
			code_length = 6;
		} else if ((insn & 0xffe0) == 0x06e0 && (insn2 & 0x1) == 0) {
			/* jmp 32bit */
			length = 2;
			code_length = 6;
		}
	}

	if (length == 0 && (cpumodel & V850_CPU_E3V5_UP)) {
		if (/* ld.dw 23bit (v850e3v5) */
				((insn & 0xffe0) == 0x07a0 && (insn2 & 0x000f) == 0x0009)
				|| /* st.dw 23bit (v850e3v5) */
				((insn & 0xffe0) == 0x07a0 && (insn2 & 0x000f) == 0x000f)) {
			length = 4;
			code_length = 6;
		}
	}

	if (length == 0 && (cpumodel & V850_CPU_E2V3_UP)) {
		/* ld.b 23bit */
		if (((insn & 0xffe0) == 0x0780 && (insn2 & 0x000f) == 0x0005)
				|| ((insn & 0xffe0) == 0x07a0 && (insn2 & 0x000f) == 0x0005)/* ld.bu 23bit */
				|| ((insn & 0xffe0) == 0x0780 && (insn2 & 0x000f) == 0x0007)/* ld.h 23bit */
				|| ((insn & 0xffe0) == 0x07a0 && (insn2 & 0x000f) == 0x0007)/* ld.hu 23bit */
				|| ((insn & 0xffe0) == 0x0780 && (insn2 & 0x000f) == 0x0009)) { /* ld.w 23bit */
			length = 4;
			code_length = 6;
		} else if (((insn & 0xffe0) == 0x0780 && (insn2 & 0x000f) == 0x000d)	/* st.b 23bit */
				|| ((insn & 0xffe0) == 0x07a0 && (insn2 & 0x000f) == 0x000d)	/* st.h 23bit */
				|| ((insn & 0xffe0) == 0x0780 && (insn2 & 0x000f) == 0x000f)) { /* st.w 23bit */
			length = 4;
			code_length = 6;
		}
	}

	if (length == 0 && cpumodel != V850_CPU_0) {
		if ((insn & 0xffe0) == 0x0620) {
			/* 32 bit MOV */
			length = 2;
			code_length = 6;
		} else if ((insn & 0xffc0) == 0x0780 && (insn2 & 0x001f) == 0x0013) {
			/* prepare {list}, imm5, imm16<<16 */
			length = 4;
			code_length = 6;
		} else if ((insn & 0xffc0) == 0x0780 && (insn2 & 0x001f) == 0x000b) {
			/* prepare {list}, imm5, imm16 */
			length = 4;
			code_length = 6;
		} else if ((insn & 0xffc0) == 0x0780 && (insn2 & 0x001f) == 0x001b) {
			/* prepare {list}, imm5, imm32 */
			length = 4;
			code_length = 8;
		}
	}

	if (length == 4 || (length == 0 && (insn & 0x0600) == 0x0600)) {
		if (len < 4) {
			code_length = R_MAX (code_length, 4);
			inst->text = r_str_newf ("truncated from %d to %d", code_length, (int)len);
			return -1;
		}
		/* This is a 4 byte insn.  */
		insn = r_read_le32 (buffer);
		length = 4;
		code_length = R_MAX (code_length, 4);
	}

	if (code_length > len) {
		// truncated instruction
		inst->text = r_str_newf ("truncated from %d to %d", code_length, (int)len);
		return -1;
	}

	if (length == 0) {
		length = code_length = 2;
		insn &= 0xffff;
	} else if (length == 2) {
		insn &= 0xffff;
	}

	// length is unused
	if (!v850np_disassemble (inst, cpumodel, addr, buffer, len, insn)) {
		return -1;
	}
	inst->size = code_length;
	return code_length;
}
