// SPDX-FileCopyrightText: 2018 courk <courk@courk.cc>
// SPDX-License-Identifier: LGPL-3.0-only

#ifndef PIC_MIDRANGE_H
#define PIC_MIDRANGE_H

#include <rz_asm.h>
#include <rz_types.h>

static inline bool is_pic_midrange(const char *x) {
	return RZ_STR_EQ(x, "midrange");
}

typedef enum {
	PIC_MIDRANGE_OP_ARGS_NONE = 0,
	PIC_MIDRANGE_OP_ARGS_2F,
	PIC_MIDRANGE_OP_ARGS_7F,
	PIC_MIDRANGE_OP_ARGS_1D_7F,
	PIC_MIDRANGE_OP_ARGS_1N_6K,
	PIC_MIDRANGE_OP_ARGS_3B_7F,
	PIC_MIDRANGE_OP_ARGS_4K,
	PIC_MIDRANGE_OP_ARGS_8K,
	PIC_MIDRANGE_OP_ARGS_9K,
	PIC_MIDRANGE_OP_ARGS_11K,
	PIC_MIDRANGE_OP_ARGS_1N_2M
} PicMidrangeOpArgs;

#define PIC_MIDRANGE_OP_ARGS_2F_MASK_F    0x3
#define PIC_MIDRANGE_OP_ARGS_7F_MASK_F    0x7f
#define PIC_MIDRANGE_OP_ARGS_1D_7F_MASK_D (1 << 7)
#define PIC_MIDRANGE_OP_ARGS_1D_7F_MASK_F 0x7f
#define PIC_MIDRANGE_OP_ARGS_1N_6K_MASK_N (1 << 6)
#define PIC_MIDRANGE_OP_ARGS_1N_6K_MASK_K 0x3f
#define PIC_MIDRANGE_OP_ARGS_3B_7F_MASK_B (0x7 << 7)
#define PIC_MIDRANGE_OP_ARGS_3B_7F_MASK_F 0x7f
#define PIC_MIDRANGE_OP_ARGS_4K_MASK_K    0xf
#define PIC_MIDRANGE_OP_ARGS_8K_MASK_K    0xff
#define PIC_MIDRANGE_OP_ARGS_9K_MASK_K    0x1ff
#define PIC_MIDRANGE_OP_ARGS_11K_MASK_K   0x7ff
#define PIC_MIDRANGE_OP_ARGS_1N_2M_MASK_N (1 << 2)
#define PIC_MIDRANGE_OP_ARGS_1N_2M_MASK_M 0x3

typedef struct _pic_midrange_op {
	const char *mnemonic;
	PicMidrangeOpArgs args;
} PicMidrangeOpAsmInfo;

typedef enum {
	PIC_MIDRANGE_OPCODE_NOP = 0,
	PIC_MIDRANGE_OPCODE_RETURN,
	PIC_MIDRANGE_OPCODE_RETFIE,
	PIC_MIDRANGE_OPCODE_OPTION,
	PIC_MIDRANGE_OPCODE_SLEEP,
	PIC_MIDRANGE_OPCODE_CLRWDT,
	PIC_MIDRANGE_OPCODE_CLRF,
	PIC_MIDRANGE_OPCODE_CLRW,
	PIC_MIDRANGE_OPCODE_TRIS,
	PIC_MIDRANGE_OPCODE_MOVWF,
	PIC_MIDRANGE_OPCODE_SUBWF,
	PIC_MIDRANGE_OPCODE_DECF,
	PIC_MIDRANGE_OPCODE_IORWF,
	PIC_MIDRANGE_OPCODE_ANDWF,
	PIC_MIDRANGE_OPCODE_XORWF,
	PIC_MIDRANGE_OPCODE_ADDWF,
	PIC_MIDRANGE_OPCODE_MOVF,
	PIC_MIDRANGE_OPCODE_COMF,
	PIC_MIDRANGE_OPCODE_INCF,
	PIC_MIDRANGE_OPCODE_DECFSZ,
	PIC_MIDRANGE_OPCODE_RRF,
	PIC_MIDRANGE_OPCODE_RLF,
	PIC_MIDRANGE_OPCODE_SWAPF,
	PIC_MIDRANGE_OPCODE_INCFSZ,
	PIC_MIDRANGE_OPCODE_BCF,
	PIC_MIDRANGE_OPCODE_BSF,
	PIC_MIDRANGE_OPCODE_BTFSC,
	PIC_MIDRANGE_OPCODE_BTFSS,
	PIC_MIDRANGE_OPCODE_CALL,
	PIC_MIDRANGE_OPCODE_GOTO,
	PIC_MIDRANGE_OPCODE_MOVLW,
	PIC_MIDRANGE_OPCODE_RETLW,
	PIC_MIDRANGE_OPCODE_IORLW,
	PIC_MIDRANGE_OPCODE_ANDLW,
	PIC_MIDRANGE_OPCODE_XORLW,
	PIC_MIDRANGE_OPCODE_SUBLW,
	PIC_MIDRANGE_OPCODE_ADDLW,
	PIC_MIDRANGE_OPCODE_RESET,
	PIC_MIDRANGE_OPCODE_CALLW,
	PIC_MIDRANGE_OPCODE_BRW,
	PIC_MIDRANGE_OPCODE_MOVIW_1,
	PIC_MIDRANGE_OPCODE_MOVWI_1,
	PIC_MIDRANGE_OPCODE_MOVLB,
	PIC_MIDRANGE_OPCODE_LSLF,
	PIC_MIDRANGE_OPCODE_LSRF,
	PIC_MIDRANGE_OPCODE_ASRF,
	PIC_MIDRANGE_OPCODE_SUBWFB,
	PIC_MIDRANGE_OPCODE_ADDWFC,
	PIC_MIDRANGE_OPCODE_ADDFSR,
	PIC_MIDRANGE_OPCODE_MOVLP,
	PIC_MIDRANGE_OPCODE_BRA,
	PIC_MIDRANGE_OPCODE_MOVIW_2,
	PIC_MIDRANGE_OPCODE_MOVWI_2,
	PIC_MIDRANGE_OPCODE_INVALID
} PicMidrangeOpcode;

typedef struct _pic_midrange_op_args_val {
	ut16 f;
	st16 k;
	ut8 d;
	ut8 m;
	ut8 n;
	ut8 b;
} PicMidrangeOpArgsVal;

static inline st16 pic_midrange_op_args_k_for_op(RZ_NONNULL PicMidrangeOpArgsVal *val, PicMidrangeOpcode op) {
	// We emulate 12-bit or 14-bit instructions by using two bytes, so program addresses need to be doubled.
	return op == PIC_MIDRANGE_OPCODE_GOTO || op == PIC_MIDRANGE_OPCODE_CALL ? val->k << 1 : val->k;
}

typedef struct pic_midrange_op_t {
	const char *mnemonic;
	char operands[32];
	PicMidrangeOpArgsVal args;
	ut64 addr;
	PicMidrangeOpcode opcode;
	ut32 size;
	ut16 instr;
} PicMidrangeOp;

// decoding

PicMidrangeOpcode pic_midrange_get_opcode(ut16 instr);
const PicMidrangeOpAsmInfo *pic_midrange_get_op_info(PicMidrangeOpcode opcode);
bool pic_midrange_decode_op(RZ_OUT RZ_NONNULL PicMidrangeOp *op, ut64 addr, RZ_NONNULL const ut8 *b, ut64 l);

// analysis

int pic_midrange_analysis_op(RzAnalysis *analysis, RzAnalysisOp *op,
	RZ_OUT RZ_NONNULL PicMidrangeOp *pic_op, RzAnalysisOpMask mask);
char *pic_midrange_get_reg_profile(RzAnalysis *a);
RzAnalysisILConfig *pic_midrange_il_config(RZ_NONNULL RzAnalysis *analysis);

static inline RzILOpEffect *reset() {
	return rz_il_op_new_nop();
}

#endif // PIC_MIDRANGE_H
