// SPDX-FileCopyrightText: 2015 Felix Held
// SPDX-License-Identifier: BSD-2-Clause

/* lm32 support for rizin */
#ifndef INCLUDE_LM32_ISA_H
#define INCLUDE_LM32_ISA_H

#include <rz_types.h>

#define RzAsmLm32RegNameLength 8
#define RzAsmLm32OpNameLength  12

typedef struct rz_asm_lm32_csr_t {
	ut8 number;
	char name[RzAsmLm32RegNameLength];
} RzAsmLm32Csr;

#define RzAsmLm32CsrNumber 21

static const RzAsmLm32Csr RzAsmLm32Csrs[RzAsmLm32CsrNumber] = {
	{ 0x00, "IE" },
	{ 0x01, "IM" },
	{ 0x02, "IP" },
	{ 0x03, "ICC" },
	{ 0x04, "DCC" },
	{ 0x05, "CC" },
	{ 0x06, "CFG" },
	{ 0x07, "EBA" },
	{ 0x08, "DC" },
	{ 0x09, "DEBA" },
	{ 0x0a, "CFG2" },
	{ 0x0e, "JTX" },
	{ 0x0f, "JRX" },
	{ 0x10, "BP0" },
	{ 0x11, "BP1" },
	{ 0x12, "BP2" },
	{ 0x13, "BP3" },
	{ 0x18, "WP0" },
	{ 0x19, "WP1" },
	{ 0x1a, "WP2" },
	{ 0x1b, "WP3" },
};

typedef struct rz_asm_lm32_reg_t {
	ut8 number;
	char name[RzAsmLm32RegNameLength];
} RzAsmLm32Reg;

#define RzAsmLm32RegNumber 39

static const RzAsmLm32Reg RzAsmLm32Regs[RzAsmLm32RegNumber] = {
	{ 0x00, "r0" },
	{ 0x00, "zero" },
	{ 0x01, "r1" },
	{ 0x02, "r2" },
	{ 0x03, "r3" },
	{ 0x04, "r4" },
	{ 0x05, "r5" },
	{ 0x06, "r6" },
	{ 0x07, "r7" },
	{ 0x08, "r8" },
	{ 0x09, "r9" },
	{ 0x0a, "r10" },
	{ 0x0b, "r11" },
	{ 0x0c, "r12" },
	{ 0x0d, "r13" },
	{ 0x0e, "r14" },
	{ 0x0f, "r15" },
	{ 0x10, "r16" },
	{ 0x11, "r17" },
	{ 0x12, "r18" },
	{ 0x13, "r19" },
	{ 0x14, "r20" },
	{ 0x15, "r21" },
	{ 0x16, "r22" },
	{ 0x17, "r23" },
	{ 0x18, "r24" },
	{ 0x19, "r25" },
	{ 0x1a, "gp" },
	{ 0x1a, "r26" },
	{ 0x1b, "fp" },
	{ 0x1b, "r27" },
	{ 0x1c, "sp" },
	{ 0x1c, "r28" },
	{ 0x1d, "ra" },
	{ 0x1d, "r29" },
	{ 0x1e, "ea" },
	{ 0x1e, "r30" },
	{ 0x1f, "ba" },
	{ 0x1f, "r31" },
};

typedef enum rz_asm_lm32_instr_type_t {
	reserved,
	reg_imm16_signextend,
	reg_imm16_shift2_signextend,
	reg_imm16_zeroextend,
	reg_imm5,
	raise_instr,
	one_reg,
	two_regs,
	three_regs,
	reg_csr, // wcsr
	csr_reg, // rcsr
	imm26,
} RzAsmLm32InstructionType;

enum rz_asm_lm32_opcode_list_t {
	lm32_op_srui = 0x00,
	lm32_op_nori = 0x01,
	lm32_op_muli = 0x02,
	lm32_op_sh = 0x03,
	lm32_op_lb = 0x04,
	lm32_op_sri = 0x05,
	lm32_op_xori = 0x06,
	lm32_op_lh = 0x07,
	lm32_op_andi = 0x08,
	lm32_op_xnori = 0x09,
	lm32_op_lw = 0x0a,
	lm32_op_lhu = 0x0b,
	lm32_op_sb = 0x0c,
	lm32_op_addi = 0x0d,
	lm32_op_ori = 0x0e,
	lm32_op_sli = 0x0f,
	lm32_op_lbu = 0x10,
	lm32_op_be = 0x11,
	lm32_op_bg = 0x12,
	lm32_op_bge = 0x13,
	lm32_op_bgeu = 0x14,
	lm32_op_bgu = 0x15,
	lm32_op_sw = 0x16,
	lm32_op_bne = 0x17,
	lm32_op_andhi = 0x18,
	lm32_op_cmpei = 0x19,
	lm32_op_cmpgi = 0x1a,
	lm32_op_cmpgei = 0x1b,
	lm32_op_cmpgeui = 0x1c,
	lm32_op_cmpgui = 0x1d,
	lm32_op_orhi = 0x1e,
	lm32_op_cmpnei = 0x1f,
	lm32_op_sru = 0x20,
	lm32_op_nor = 0x21,
	lm32_op_mul = 0x22,
	lm32_op_divu = 0x23,
	lm32_op_rcsr = 0x24,
	lm32_op_sr = 0x25,
	lm32_op_xor = 0x26,
	lm32_op_div = 0x27,
	lm32_op_and = 0x28,
	lm32_op_xnor = 0x29,
	lm32_op_reserved1 = 0x2a,
	lm32_op_raise = 0x2b,
	lm32_op_sextb = 0x2c,
	lm32_op_add = 0x2d,
	lm32_op_or = 0x2e,
	lm32_op_sl = 0x2f,
	lm32_op_b = 0x30,
	lm32_op_modu = 0x31,
	lm32_op_sub = 0x32,
	lm32_op_reserved2 = 0x33,
	lm32_op_wcsr = 0x34,
	lm32_op_mod = 0x35,
	lm32_op_call = 0x36,
	lm32_op_sexth = 0x37,
	lm32_op_bi = 0x38,
	lm32_op_cmpe = 0x39,
	lm32_op_cmpg = 0x3a,
	lm32_op_cmpge = 0x3b,
	lm32_op_cmpgeu = 0x3c,
	lm32_op_cmpgu = 0x3d,
	lm32_op_calli = 0x3e,
	lm32_op_cmpne = 0x3f,
};

typedef struct rz_asm_lm32_opcode {
	RzAsmLm32InstructionType type;
	char name[RzAsmLm32OpNameLength];
} RzAsmLm32Opcode;

#define RzAsmLm32OpcodeNumber 0x40

static const RzAsmLm32Opcode RzAsmLm32OpcodeList[RzAsmLm32OpcodeNumber] = {
	{ reg_imm5, "srui" }, // 0x00
	{ reg_imm16_zeroextend, "nori" }, // 0x01
	{ reg_imm16_signextend, "muli" }, // 0x02
	{ reg_imm16_signextend, "sh" }, // 0x03
	{ reg_imm16_signextend, "lb" }, // 0x04
	{ reg_imm5, "sri" }, // 0x05
	{ reg_imm16_zeroextend, "xori" }, // 0x06
	{ reg_imm16_signextend, "lh" }, // 0x07
	{ reg_imm16_zeroextend, "andi" }, // 0x08
	{ reg_imm16_zeroextend, "xnori" }, // 0x09
	{ reg_imm16_signextend, "lw" }, // 0x0a
	{ reg_imm16_signextend, "lhu" }, // 0x0b
	{ reg_imm16_signextend, "sb" }, // 0x0c
	{ reg_imm16_signextend, "addi" }, // 0x0d
	{ reg_imm16_zeroextend, "ori" }, // 0x0e
	{ reg_imm5, "sli" }, // 0x0f
	{ reg_imm16_signextend, "lbu" }, // 0x10
	{ reg_imm16_shift2_signextend, "be" }, // 0x11
	{ reg_imm16_shift2_signextend, "bg" }, // 0x12
	{ reg_imm16_shift2_signextend, "bge" }, // 0x13
	{ reg_imm16_shift2_signextend, "bgeu" }, // 0x14
	{ reg_imm16_shift2_signextend, "bgu" }, // 0x15
	{ reg_imm16_signextend, "sw" }, // 0x16
	{ reg_imm16_shift2_signextend, "bne" }, // 0x17
	{ reg_imm16_zeroextend, "andhi" }, // 0x18
	{ reg_imm16_signextend, "cmpei" }, // 0x19
	{ reg_imm16_signextend, "cmpgi" }, // 0x1a
	{ reg_imm16_signextend, "cmpgei" }, // 0x1b
	{ reg_imm16_zeroextend, "cmpgeui" }, // 0x1c
	{ reg_imm16_zeroextend, "cmpgui" }, // 0x1d
	{ reg_imm16_zeroextend, "orhi" }, // 0x1e
	{ reg_imm16_signextend, "cmpnei" }, // 0x1f
	{ three_regs, "sru" }, // 0x20
	{ three_regs, "nor" }, // 0x21
	{ three_regs, "mul" }, // 0x22
	{ three_regs, "divu" }, // 0x23
	{ csr_reg, "rcsr" }, // 0x24
	{ three_regs, "sr" }, // 0x25
	{ three_regs, "xor" }, // 0x26
	{ three_regs, "div" }, // 0x27
	{ three_regs, "and" }, // 0x28
	{ three_regs, "xnor" }, // 0x29
	{ reserved, "reserved" }, // 0x2a
	{ raise_instr, "raise" }, // 0x2b (break, scall)
	{ two_regs, "sextb" }, // 0x2c
	{ three_regs, "add" }, // 0x2d
	{ three_regs, "or" }, // 0x2e
	{ three_regs, "sl" }, // 0x2f
	{ one_reg, "b" }, // 0x30
	{ three_regs, "modu" }, // 0x31
	{ three_regs, "sub" }, // 0x32
	{ reserved, "reserved" }, // 0x33
	{ reg_csr, "wcsr" }, // 0x34
	{ three_regs, "mod" }, // 0x35
	{ one_reg, "call" }, // 0x36
	{ two_regs, "sexth" }, // 0x37
	{ imm26, "bi" }, // 0x38
	{ three_regs, "cmpe" }, // 0x39
	{ three_regs, "cmpg" }, // 0x3a
	{ three_regs, "cmpge" }, // 0x3b
	{ three_regs, "cmpgeu" }, // 0x3c
	{ three_regs, "cmpgu" }, // 0x3d
	{ imm26, "calli" }, // 0x3e
	{ three_regs, "cmpne" }, // 0x3f
};

typedef struct rz_asm_lm32_instruction {
	// address of this instruction
	ut32 addr;
	// instruction dword
	ut32 value;

	// op code
	ut8 op;
	// op type and name
	RzAsmLm32Opcode op_decode;
	// dest is the destination register (the one right after most commands)
	ut8 dest_reg;
	char dest_reg_str[RzAsmLm32RegNameLength];
	// src0 is the leftmost source register
	ut8 src0_reg;
	char src0_reg_str[RzAsmLm32RegNameLength];
	// src1 is the second (rightmost) source register
	ut8 src1_reg;
	char src1_reg_str[RzAsmLm32RegNameLength];
	// control and status register number
	ut8 csr;
	char csr_reg_str[RzAsmLm32RegNameLength];
	// immediates in expanded form
	st32 immediate;
	// set if the command has a second string representation
	bool pseudoInstruction;
} RzAsmLm32Instruction;

/*
 * register number field name convention
 * bits 25..21 -> reg u (either a register or a csr number)
 * bits 20..16 -> reg v
 * bits 15..11 -> reg w
 */
static const int RzAsmLm32OpShift = 26;
static const ut32 RzAsmLm32OpMask = 0xfc000000;
static const int RzAsmLm32RegUShift = 21;
static const ut32 RzAsmLm32RegUMask = 0x03e00000;
static const int RzAsmLm32RegVShift = 16;
static const ut32 RzAsmLm32RegVMask = 0x001f0000;
static const int RzAsmLm32RegWShift = 11;
static const ut32 RzAsmLm32RegWMask = 0x0000f800;
static const ut32 RzAsmLm32Imm5Mask = 0x0000001f;
static const ut32 RzAsmLm32Imm16Mask = 0x0000ffff;
static const ut32 RzAsmLm32Imm16SignBitPos = 15;
static const ut32 RzAsmLm32Imm26Mask = 0x03ffffff;
static const ut32 RzAsmLm32Imm26SignBitPos = 25;

static const ut32 RzAsmLm32InstrImm5InvalidBitsMask = 0x0000ffe0;
static const ut32 RzAsmLm32InstrOneRegInvalidBitsMask = 0x001fffff;
static const ut32 RzAsmLm32InstrTwoRegsInvalidBitsMask = 0x001f07ff;
static const ut32 RzAsmLm32InstrWcsrInvalidBitsMask = 0x0000ffff;

// common helper functions

static ut8 extract_opcode(ut32 instr_dword) {
	return (instr_dword & RzAsmLm32OpMask) >> RzAsmLm32OpShift;
}

static ut8 extract_reg_u(ut32 instr_dword) {
	return (instr_dword & RzAsmLm32RegUMask) >> RzAsmLm32RegUShift;
}

static ut8 extract_reg_v(ut32 instr_dword) {
	return (instr_dword & RzAsmLm32RegVMask) >> RzAsmLm32RegVShift;
}

static ut8 extract_reg_w(ut32 instr_dword) {
	return (instr_dword & RzAsmLm32RegWMask) >> RzAsmLm32RegWShift;
}

static ut8 extract_imm5(ut32 instr_dword) {
	return instr_dword & RzAsmLm32Imm5Mask;
}

static ut16 extract_imm16(ut32 instr_dword) {
	return instr_dword & RzAsmLm32Imm16Mask;
}

static ut32 extract_imm26(ut32 instr_dword) {
	return instr_dword & RzAsmLm32Imm26Mask;
}

#endif // INCLUDE_LM32_ISA_H
