// SPDX-FileCopyrightText: 2015-2018 oddcoder <ahmedsoliman@oddcoder.com>
// SPDX-FileCopyrightText: 2015-2018 thestr4ng3r <info@florianmaerkl.de>
// SPDX-FileCopyrightText: 2015-2018 courk <courk@courk.cc>
// SPDX-License-Identifier: LGPL-3.0-only

#include "pic_highend.h"
#include "pic_highend_esil.inc"

typedef struct {
	enum {
		ILOpEff_None,
		ILOpEff_PostDec,
		ILOpEff_PostInc,
		ILOpEff_PreInc,
	} tag;
	char fsr[8];
} ILOpEff;

typedef struct {
	const PicHighendOp *op;
	const HtSU *mm;
	RzVector /*<ILOpEff>*/ effs;
} PicHighendILContext;

#include "pic_highend_il.inc"

static void pic_highend_cond_branch(RzAnalysisOp *aop, PicHighendOp *op) {
	aop->type = RZ_ANALYSIS_OP_TYPE_CJMP;
	aop->jump = op->addr + 2 + 2 * op->n;
	aop->fail = op->addr + aop->size;
	aop->cycles = 2;
}

int pic_highend_op(
	RzAnalysis *analysis, RzAnalysisOp *aop, ut64 addr,
	const ut8 *buf, int len, RzAnalysisOpMask mask) {

	aop->size = 2;
	PicHighendOp op = { 0 };
	if (!pic_highend_disasm_op(&op, addr, buf, len)) {
		goto err;
	}
	aop->size = op.size;
	switch (op.code) {
	case PIC_HIGHEND_OPCODE_CALL:
		aop->type = RZ_ANALYSIS_OP_TYPE_CALL;
		aop->jump = op.k;
		aop->cycles = 2;
		break;
	case PIC_HIGHEND_OPCODE_RCALL:
		aop->type = RZ_ANALYSIS_OP_TYPE_CALL;
		aop->jump = addr + 2 * (1 + op.n);
		aop->cycles = 2;
		break;
	case PIC_HIGHEND_OPCODE_BRA: // bra
		aop->type = RZ_ANALYSIS_OP_TYPE_JMP;
		aop->cycles = 2;
		aop->jump = addr + 2 + 2 * op.n;
		break;
	case PIC_HIGHEND_OPCODE_MOVFF: // movff
		aop->type = RZ_ANALYSIS_OP_TYPE_MOV;
		aop->cycles = 2;
		break;
	case PIC_HIGHEND_OPCODE_BTFSC: // btfsc
	case PIC_HIGHEND_OPCODE_BTFSS: // btfss
		aop->type = RZ_ANALYSIS_OP_TYPE_CJMP;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_BCF: // bcf
	case PIC_HIGHEND_OPCODE_BSF: // bsf
	case PIC_HIGHEND_OPCODE_BTG: // btg
		aop->type = RZ_ANALYSIS_OP_TYPE_UNK;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_BZ: // bz
		pic_highend_cond_branch(aop, &op);
		break;
	case PIC_HIGHEND_OPCODE_BNZ: // bnz
		pic_highend_cond_branch(aop, &op);
		break;
	case PIC_HIGHEND_OPCODE_BNC: // bnc
		pic_highend_cond_branch(aop, &op);
		break;
	case PIC_HIGHEND_OPCODE_BOV: // bov
		pic_highend_cond_branch(aop, &op);
		break;
	case PIC_HIGHEND_OPCODE_BNOV: // bnov
		pic_highend_cond_branch(aop, &op);
		break;
	case PIC_HIGHEND_OPCODE_BN: // bn
		pic_highend_cond_branch(aop, &op);
		break;
	case PIC_HIGHEND_OPCODE_BNN: // bnn
		pic_highend_cond_branch(aop, &op);
		break;
	case PIC_HIGHEND_OPCODE_BC: // bc
		pic_highend_cond_branch(aop, &op);
		break;
	case PIC_HIGHEND_OPCODE_GOTO: // goto
		aop->cycles = 2;
		aop->jump = op.k;
		aop->type = RZ_ANALYSIS_OP_TYPE_JMP;
		break;
	case PIC_HIGHEND_OPCODE_ADDLW: // addlw
		aop->type = RZ_ANALYSIS_OP_TYPE_ADD;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_MOVLW: // movlw
		aop->type = RZ_ANALYSIS_OP_TYPE_LOAD;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_MULLW: // mullw
		aop->type = RZ_ANALYSIS_OP_TYPE_MUL;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_ANDLW: // andlw
		aop->type = RZ_ANALYSIS_OP_TYPE_AND;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_XORLW: // xorlw
		aop->type = RZ_ANALYSIS_OP_TYPE_XOR;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_IORLW: // iorlw
		aop->type = RZ_ANALYSIS_OP_TYPE_OR;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_SUBLW: // sublw
		aop->type = RZ_ANALYSIS_OP_TYPE_SUB;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_LFSR: // lfsr
		aop->type = RZ_ANALYSIS_OP_TYPE_LOAD;
		aop->cycles = 2;
		break;
	case PIC_HIGHEND_OPCODE_SUBWF: // subwf
	case PIC_HIGHEND_OPCODE_SUBFWB: // subwfb
	case PIC_HIGHEND_OPCODE_SUBWFB: // subfwb
	case PIC_HIGHEND_OPCODE_DCFSNZ: // dcfsnz
	case PIC_HIGHEND_OPCODE_DECFSZ: // decfsz
	case PIC_HIGHEND_OPCODE_DECF: // decf
		aop->type = RZ_ANALYSIS_OP_TYPE_SUB;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_MOVF: // movf
		aop->type = RZ_ANALYSIS_OP_TYPE_MOV;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_INFSNZ: // infsnz
	case PIC_HIGHEND_OPCODE_INCFSZ: // incfsz
	case PIC_HIGHEND_OPCODE_INCF: // incf
	case PIC_HIGHEND_OPCODE_ADDWFC: // addwfc
		aop->type = RZ_ANALYSIS_OP_TYPE_ADD;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_ADDWF: // addwf
		aop->cycles = 1;
		aop->type = RZ_ANALYSIS_OP_TYPE_ADD;
		break;
	case PIC_HIGHEND_OPCODE_RLNCF: // rlncf
	case PIC_HIGHEND_OPCODE_RLCF: // rlcf
		aop->type = RZ_ANALYSIS_OP_TYPE_ROL;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_RRNCF: // rrncf
	case PIC_HIGHEND_OPCODE_RRCF: // rrcf
		aop->type = RZ_ANALYSIS_OP_TYPE_ROR;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_SWAPF: // swapf
		aop->type = RZ_ANALYSIS_OP_TYPE_UNK;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_COMF: // comf
		aop->type = RZ_ANALYSIS_OP_TYPE_CPL;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_XORWF: // xorwf
		aop->type = RZ_ANALYSIS_OP_TYPE_XOR;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_ANDWF: // andwf
		aop->type = RZ_ANALYSIS_OP_TYPE_AND;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_IORWF: // iorwf
		aop->type = RZ_ANALYSIS_OP_TYPE_OR;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_MOVWF: // movwf
		aop->type = RZ_ANALYSIS_OP_TYPE_STORE;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_NEGF: // negf
	case PIC_HIGHEND_OPCODE_CLRF: // clrf
	case PIC_HIGHEND_OPCODE_SETF: // setf
		aop->type = RZ_ANALYSIS_OP_TYPE_UNK;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_TSTFSZ: // tstfsz
		aop->type = RZ_ANALYSIS_OP_TYPE_CJMP;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_CPFSGT: // cpfsgt
	case PIC_HIGHEND_OPCODE_CPFSEQ: // cpfseq
	case PIC_HIGHEND_OPCODE_CPFSLT: // cpfslt
		aop->type = RZ_ANALYSIS_OP_TYPE_CMP;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_MULWF: // mulwf
		aop->type = RZ_ANALYSIS_OP_TYPE_MUL;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_MOVLB: // movlb
		aop->type = RZ_ANALYSIS_OP_TYPE_LOAD;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_RESET: // reset
	case PIC_HIGHEND_OPCODE_DAW: // daw
				     //	case CLWDT // clwdt
	case PIC_HIGHEND_OPCODE_SLEEP: // sleep
		aop->type = RZ_ANALYSIS_OP_TYPE_UNK;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_RETFIE: // retfie
	case PIC_HIGHEND_OPCODE_RETLW:
	case PIC_HIGHEND_OPCODE_RETURN:
		aop->type = RZ_ANALYSIS_OP_TYPE_RET;
		aop->cycles = 2;
		break;
	case PIC_HIGHEND_OPCODE_TBLWTMs: // tblwt
	case PIC_HIGHEND_OPCODE_TBLWTMsi: // tblwt
	case PIC_HIGHEND_OPCODE_TBLWTis: // tblwt
	case PIC_HIGHEND_OPCODE_TBLWTMsd: // tblwt
		aop->type = RZ_ANALYSIS_OP_TYPE_LOAD;
		aop->cycles = 2;
		break;
	case PIC_HIGHEND_OPCODE_TBLRDis: // tblrd
	case PIC_HIGHEND_OPCODE_TBLRDs: // tblrd
	case PIC_HIGHEND_OPCODE_TBLRDsi: // tblrd
	case PIC_HIGHEND_OPCODE_TBLRDsd: // tblrd
		aop->type = RZ_ANALYSIS_OP_TYPE_STORE;
		aop->cycles = 2;
		break;
	case PIC_HIGHEND_OPCODE_POP: // pop
		aop->type = RZ_ANALYSIS_OP_TYPE_POP;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_PUSH: // push
		aop->type = RZ_ANALYSIS_OP_TYPE_PUSH;
		aop->cycles = 1;
		break;
	case PIC_HIGHEND_OPCODE_NOP: // nop
		aop->type = RZ_ANALYSIS_OP_TYPE_NOP;
		aop->cycles = 1;
		break;
	default:
		goto err;
	}

	if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
		pic_highend_esil(aop, &op, addr, buf);
	}

	if (mask & RZ_ANALYSIS_OP_MASK_IL) {
		PicHighendILContext ctx = {
			.op = &op,
			.mm = ((PicContext *)analysis->plugin_data)->pic_highend_mm,
		};
		rz_vector_init(&ctx.effs, sizeof(ILOpEff), NULL, NULL);
		aop->il_op = pic_highend_il_op_finally(&ctx);
		rz_vector_fini(&ctx.effs);
	}

	if (mask & RZ_ANALYSIS_OP_MASK_DISASM) {
		aop->mnemonic = rz_str_newf("%s%s%s", op.mnemonic, RZ_STR_ISEMPTY(op.operands) ? "" : " ", op.operands);
	}

	return aop->size;
err:
	aop->type = RZ_ANALYSIS_OP_TYPE_ILL;
	return aop->size;
}

char *pic_highend_get_reg_profile(RzAnalysis *esil) {
	const char *p =
		"#pc lives in nowhere actually\n"
		"=PC	pc\n"
		"=SP	tos\n"
		"=A0	porta\n"
		"=A1	portb\n"
		"gpr	pc	.32	0	0\n"
		"gpr	pcl	.8	0	0\n"
		"gpr	pclath	.8	1	0\n"
		"gpr	pclatu	.8	2	0\n"
		"#bsr max is 0b111\n"
		"gpr	bsr	.8	4	0\n"
		"#tos doesn't exist\n"
		"#general rule of thumb any register of size >8 bits has no existence\n"
		"gpr	tos	.32	5	0\n"
		"gpr	tosl	.8	5	0\n"
		"gpr	tosh	.8	6	0\n"
		"gpr	tosu	.8	7	0\n"

		"gpr	indf0	.16	9	0\n"
		"gpr	fsr0	.12	9	0\n"
		"gpr	fsr0l	.8	9	0\n"
		"gpr	fsr0h	.8	10	0\n"
		"gpr	indf1	.16	11	0\n"
		"gpr	fsr1	.12	11	0\n"
		"gpr	fsr1l	.8	11	0\n"
		"gpr	fsr1h	.8	12	0\n"
		"gpr	indf2	.16	13	0\n"
		"gpr	fsr2	.12	13	0\n"
		"gpr	frs2l	.8	13	0\n"
		"gpr	fsr2h	.8	14	0\n"
		"gpr	tblptr	.22	15	0\n"
		"gpr	tblptrl	.8	15	0\n"
		"gpr	tblptrh	.8	16	0\n"
		"gpr	tblptru	.8	17	0\n"
		"gpr	rcon	.8	18	0\n"
		"gpr	memcon	.8	19	0\n"
		"gpr	intcon	.8	20	0\n"
		"gpr	intcon2	.8	21	0\n"
		"gpr	intcon3	.8	22	0\n"
		"gpr	pie1	.8	23	0\n"
		"gpr	porta	.7	29	0\n"
		"gpr	trisa	.8	30	0\n"
		"gpr	portb	.8	33	0\n"
		"gpr	tisb	.8	34	0\n"
		"gpr	latb	.8	35	0\n"
		"gpr	portc	.8	36	0\n"
		"gpr	trisc	.8	37	0\n"
		"gpr	latc	.8	38	0\n"
		"gpr	portd	.8	39	0\n"
		"gpr	trisd	.8	40	0\n"
		"gpr	latd	.8	41	0\n"
		"gpr	pspcon	.8	42	0\n"
		"gpr	porte	.8	43	0\n"
		"gpr	trise	.8	44	0\n"
		"gpr	late	.8	45	0\n"
		"gpr	t0con	.8	46	0\n"
		"gpr	t1con	.8	47	0\n"
		"gpr	t2con	.8	48	0\n"
		"gpr	tmr1h	.8	50	0\n"
		"gpr	tmr0h	.8	51	0\n"
		"gpr	tmr1l	.8	52	0\n"
		"gpr	tmr2	.8	53	0\n"
		"gpr	pr2	.8	54	0\n"
		"gpr	ccpr1h	.8	55	0\n"
		"gpr	postinc2 .8	56	0\n"
		"gpr	ccpr1l	.8	57	0\n"
		"gpr	postdec2 .8	58	0\n"
		"gpr	ccp1con	.8	59	0\n"
		"gpr	preinc2	.8	60	0\n"
		"gpr	ccpr2h	.8	61	0\n"
		"gpr	plusw2	.8	62	0\n"
		"gpr	ccpr2l	.8	63	0\n"
		"gpr	ccp2con	.8	64	0\n"
		"gpr	status	.8	65	0\n"
		"flg	c	.1	.520	0\n"
		"flg	dc	.1	.521	0\n"
		"flg	z	.1	.522	0\n"
		"flg	ov	.1	.523	0\n"
		"flg	n	.1	.524	0\n"
		"gpr	prod	.16	66	0\n"
		"gpr	prodl	.8	66	0\n"
		"gpr	prodh	.8	67	0\n"
		"gpr	osccon	.8	68	0\n"
		"gpr	tmr3h	.8	69	0\n"
		"gpr	lvdcon	.8	70	0\n"
		"gpr	tmr3l	.8	71	0\n"
		"gpr	wdtcon	.8	72	0\n"
		"gpr	t3con	.8	73	0\n"
		"gpr	spbrg	.8	74	0\n"
		"gpr	postinc0 .8	75	0\n"
		"gpr	rcreg	.8	76	0\n"
		"gpr	postdec0 .8	77	0\n"
		"gpr	txreg	.8	78	0\n"
		"gpr	preinc0	.8	79	0\n"
		"gpr	txsta	.8	80	0\n"
		"gpr	plusw0	.8	81	0\n"
		"gpr	rcsta	.8	82	0\n"
		"gpr	sspbuf	.8	83	0\n"
		"gpr	wreg	.8	84	0\n"
		"gpr	sspadd	.8	85	0\n"
		"gpr	sspstat	.8	86	0\n"
		"gpr	postinc1 .8	87	0\n"
		"gpr	sspcon1	.8	88	0\n"
		"gpr	postdec1 .8	89	0\n"
		"gpr	sspcon2	.8	90	0\n"
		"gpr	preinc1	.8	91	0\n"
		"gpr	adresh	.8	92	0\n"
		"gpr	plusw1	.8	93	0\n"
		"gpr	adresl	.8	94	0\n"
		"gpr	adcon0	.8	95	0\n"
		"#stkprt max is 0b11111\n"
		"gpr	stkptr	.8	96	0\n"
		"gpr	tblat	.8	14	0\n"
		"gpr	_sram	.8	98	0\n"
		"gpr	_stack	.8	99	0\n"
		"gpr	_skip	.8	100	0\n"
		"gpr wregs .8 101 0\n"
		"gpr statuss .8 102 0\n"
		"gpr bsrs .8 103 0\n";
	return rz_str_dup(p);
}
