/* radare2 - LGPL - Copyright 2018-2022 - courk */

#include "pic_midrange.h"

static const PicMidrangeOpInfo pic_midrange_op_info[PIC_MIDRANGE_OPCODE_INVALID] = {
	{ "nop", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "return", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "retfie", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "option", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "sleep", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "clrwdt", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "tris", PIC_MIDRANGE_OP_ARGS_2F},
	{ "movwf", PIC_MIDRANGE_OP_ARGS_7F},
	{ "clr", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "subwf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "decf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "iorwf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "andwf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "xorwf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "addwf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "movf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "comf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "incf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "decfsz", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "rrf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "rlf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "swapf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "incfsz", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "bcf", PIC_MIDRANGE_OP_ARGS_3B_7F},
	{ "bsf", PIC_MIDRANGE_OP_ARGS_3B_7F},
	{ "btfsc", PIC_MIDRANGE_OP_ARGS_3B_7F},
	{ "btfss", PIC_MIDRANGE_OP_ARGS_3B_7F},
	{ "call", PIC_MIDRANGE_OP_ARGS_11K},
	{ "goto", PIC_MIDRANGE_OP_ARGS_11K},
	{ "movlw", PIC_MIDRANGE_OP_ARGS_8K},
	{ "retlw", PIC_MIDRANGE_OP_ARGS_8K},
	{ "iorlw", PIC_MIDRANGE_OP_ARGS_8K},
	{ "andlw", PIC_MIDRANGE_OP_ARGS_8K},
	{ "xorlw", PIC_MIDRANGE_OP_ARGS_8K},
	{ "sublw", PIC_MIDRANGE_OP_ARGS_8K},
	{ "addlw", PIC_MIDRANGE_OP_ARGS_8K},
	{ "reset", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "callw", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "brw", PIC_MIDRANGE_OP_ARGS_NONE},
	{ "moviw", PIC_MIDRANGE_OP_ARGS_1N_2M},
	{ "movwi", PIC_MIDRANGE_OP_ARGS_1N_2M},
	{ "movlb", PIC_MIDRANGE_OP_ARGS_4K},
	{ "lslf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "lsrf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "asrf", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "subwfb", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "addwfc", PIC_MIDRANGE_OP_ARGS_1D_7F},
	{ "addfsr", PIC_MIDRANGE_OP_ARGS_1N_6K},
	{ "movlp", PIC_MIDRANGE_OP_ARGS_7F},
	{ "bra", PIC_MIDRANGE_OP_ARGS_9K},
	{ "moviw", PIC_MIDRANGE_OP_ARGS_1N_6K},
	{ "movwi", PIC_MIDRANGE_OP_ARGS_1N_6K}
};

PicMidrangeOpcode pic_midrange_get_opcode(ut16 instr) {
	if (instr & (1 << 14)) {
		return PIC_MIDRANGE_OPCODE_INVALID;
	}

	switch (instr >> 11) { // 3 first MSB bits
	case 0x4: return PIC_MIDRANGE_OPCODE_CALL;
	case 0x5: return PIC_MIDRANGE_OPCODE_GOTO;
	}

	switch (instr >> 10) { // 4 first MSB bits
	case 0x4: return PIC_MIDRANGE_OPCODE_BCF;
	case 0x5: return PIC_MIDRANGE_OPCODE_BSF;
	case 0x6: return PIC_MIDRANGE_OPCODE_BTFSC;
	case 0x7: return PIC_MIDRANGE_OPCODE_BTFSS;
	}

	switch (instr >> 9) { // 5 first MSB bits
	case 0x19: return PIC_MIDRANGE_OPCODE_BRA;
	}

	switch (instr >> 8) { // 6 first MSB bits
	case 0x1: return PIC_MIDRANGE_OPCODE_CLR;
	case 0x2: return PIC_MIDRANGE_OPCODE_SUBWF;
	case 0x3: return PIC_MIDRANGE_OPCODE_DECF;
	case 0x4: return PIC_MIDRANGE_OPCODE_IORWF;
	case 0x5: return PIC_MIDRANGE_OPCODE_ANDWF;
	case 0x6: return PIC_MIDRANGE_OPCODE_XORWF;
	case 0x7: return PIC_MIDRANGE_OPCODE_ADDWF;
	case 0x8: return PIC_MIDRANGE_OPCODE_MOVF;
	case 0x9: return PIC_MIDRANGE_OPCODE_COMF;
	case 0xa: return PIC_MIDRANGE_OPCODE_INCF;
	case 0xb: return PIC_MIDRANGE_OPCODE_DECFSZ;
	case 0xc: return PIC_MIDRANGE_OPCODE_RRF;
	case 0xd: return PIC_MIDRANGE_OPCODE_RLF;
	case 0xe: return PIC_MIDRANGE_OPCODE_SWAPF;
	case 0xf: return PIC_MIDRANGE_OPCODE_INCFSZ;
	case 0x38: return PIC_MIDRANGE_OPCODE_IORLW;
	case 0x39: return PIC_MIDRANGE_OPCODE_ANDLW;
	case 0x3a: return PIC_MIDRANGE_OPCODE_XORLW;
	case 0x30: return PIC_MIDRANGE_OPCODE_MOVLW;
	case 0x34: return PIC_MIDRANGE_OPCODE_RETLW;
	case 0x3c: return PIC_MIDRANGE_OPCODE_SUBLW;
	case 0x3e: return PIC_MIDRANGE_OPCODE_ADDLW;
	case 0x35: return PIC_MIDRANGE_OPCODE_LSLF;
	case 0x36: return PIC_MIDRANGE_OPCODE_LSRF;
	case 0x37: return PIC_MIDRANGE_OPCODE_ASRF;
	case 0x3b: return PIC_MIDRANGE_OPCODE_SUBWFB;
	case 0x3d: return PIC_MIDRANGE_OPCODE_ADDWFC;
	}

	switch (instr >> 7) { // 7 first MSB bits
	case 0x1: return PIC_MIDRANGE_OPCODE_MOVWF;
	case 0x62: return PIC_MIDRANGE_OPCODE_ADDFSR;
	case 0x63: return PIC_MIDRANGE_OPCODE_MOVLP;
	case 0x7e: return PIC_MIDRANGE_OPCODE_MOVIW_2;
	case 0x7f: return PIC_MIDRANGE_OPCODE_MOVWI_2;
	}

	switch (instr >> 5) { // 9 first MSB bits
	case 0x1: return PIC_MIDRANGE_OPCODE_MOVLB;
	}

	switch (instr >> 3) { // 11 first MSB bits
	case 0x2: return PIC_MIDRANGE_OPCODE_MOVIW_1;
	case 0x3: return PIC_MIDRANGE_OPCODE_MOVWI_1;
	}

	switch (instr >> 2) { // 12 first MSB bits
	case 0x19: return PIC_MIDRANGE_OPCODE_TRIS;
	}

	switch (instr) {
	case 0x0: return PIC_MIDRANGE_OPCODE_NOP;
	case 0x1: return PIC_MIDRANGE_OPCODE_RESET;
	case 0xa: return PIC_MIDRANGE_OPCODE_CALLW;
	case 0xb: return PIC_MIDRANGE_OPCODE_BRW;
	case 0x8: return PIC_MIDRANGE_OPCODE_RETURN;
	case 0x9: return PIC_MIDRANGE_OPCODE_RETFIE;
	case 0x62: return PIC_MIDRANGE_OPCODE_OPTION;
	case 0x63: return PIC_MIDRANGE_OPCODE_SLEEP;
	case 0x64: return PIC_MIDRANGE_OPCODE_CLRWDT;
	}

	return PIC_MIDRANGE_OPCODE_INVALID;
}

static const PicMidrangeOpInfo *pic_midrange_get_op_info(PicMidrangeOpcode opcode) {
	if (opcode >= PIC_MIDRANGE_OPCODE_INVALID) {
		return NULL;
	}
	return &pic_midrange_op_info[opcode];
}

char *pic_midrange_disassemble(const ut8 *b, int l, int *opsz) {
	r_strf_buffer (64);
	const char *buf_asm = NULL;
	st16 branch;
	if (!b || l < 2) {
		return NULL;
	}

	ut16 instr = r_read_le16 (b);
	PicMidrangeOpcode opcode = pic_midrange_get_opcode (instr);
	if (opcode == PIC_MIDRANGE_OPCODE_INVALID) {
		return NULL;
	}

	const PicMidrangeOpInfo *op_info = pic_midrange_get_op_info (opcode);
	if (!op_info) {
		return NULL;
	}

	*opsz = 2;

	switch (op_info->args) {
	case PIC_MIDRANGE_OP_ARGS_NONE:
		buf_asm = op_info->mnemonic;
		break;
	case PIC_MIDRANGE_OP_ARGS_2F:
		buf_asm = r_strf ("%s 0x%x", op_info->mnemonic, instr & PIC_MIDRANGE_OP_ARGS_2F_MASK_F);
		break;
	case PIC_MIDRANGE_OP_ARGS_7F:
		buf_asm = r_strf ("%s 0x%x", op_info->mnemonic, instr & PIC_MIDRANGE_OP_ARGS_7F_MASK_F);
		break;
	case PIC_MIDRANGE_OP_ARGS_1D_7F:
		buf_asm = r_strf ("%s 0x%x, %c", op_info->mnemonic,
			  instr & PIC_MIDRANGE_OP_ARGS_1D_7F_MASK_F,
			  (instr & PIC_MIDRANGE_OP_ARGS_1D_7F_MASK_D) >> 7 ?  'f' : 'w');
		break;
	case PIC_MIDRANGE_OP_ARGS_1N_6K:
		if (opcode == PIC_MIDRANGE_OPCODE_ADDFSR) {
			buf_asm = r_strf ( "%s FSR%d, 0x%x", op_info->mnemonic,
					(instr & PIC_MIDRANGE_OP_ARGS_1N_6K_MASK_N) >>
					6, instr & PIC_MIDRANGE_OP_ARGS_1N_6K_MASK_K);
		} else {
			buf_asm = r_strf ("%s 0x%x[FSR%d]", op_info->mnemonic,
				instr & PIC_MIDRANGE_OP_ARGS_1N_6K_MASK_K,
				(instr & PIC_MIDRANGE_OP_ARGS_1N_6K_MASK_N) >> 6);
		}
		break;
	case PIC_MIDRANGE_OP_ARGS_3B_7F:
		buf_asm = r_strf ("%s 0x%x, %d", op_info->mnemonic, instr & PIC_MIDRANGE_OP_ARGS_3B_7F_MASK_F,
			  (instr & PIC_MIDRANGE_OP_ARGS_3B_7F_MASK_B) >> 7);
		break;
	case PIC_MIDRANGE_OP_ARGS_4K:
		buf_asm = r_strf ("%s 0x%x", op_info->mnemonic, instr & PIC_MIDRANGE_OP_ARGS_4K_MASK_K);
		break;
	case PIC_MIDRANGE_OP_ARGS_8K:
		buf_asm = r_strf ("%s 0x%x", op_info->mnemonic, instr & PIC_MIDRANGE_OP_ARGS_8K_MASK_K);
		break;
	case PIC_MIDRANGE_OP_ARGS_9K:
		branch = (instr & PIC_MIDRANGE_OP_ARGS_9K_MASK_K);
		branch |= ((branch & 0x100) ? 0xfe00 : 0);
		buf_asm = r_strf ("%s %s0x%x",
			  op_info->mnemonic, branch < 0 ? "-" : "",
			  branch < 0 ? -branch : branch);
		break;
	case PIC_MIDRANGE_OP_ARGS_11K:
		buf_asm = r_strf ("%s 0x%x", op_info->mnemonic, instr & PIC_MIDRANGE_OP_ARGS_11K_MASK_K);
		break;
	case PIC_MIDRANGE_OP_ARGS_1N_2M: {
		const int mode = instr & PIC_MIDRANGE_OP_ARGS_1N_2M_MASK_M;
		const char *incdec = (mode & 1)? "--": "++";
		buf_asm = r_strf ("%s %sFSR%d%s", op_info->mnemonic, mode < 2? incdec: "",
			(instr & PIC_MIDRANGE_OP_ARGS_1N_2M_MASK_N) >> 2, mode < 2? "": incdec);
		break;
	}
	}
	return buf_asm? strdup (buf_asm): NULL;
}
