// SPDX-FileCopyrightText: 2011-2015 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only

#include <string.h>

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

enum {
	GPR_G0 = 0,
	GPR_G1 = 1,
	GPR_G2 = 2,
	GPR_G3 = 3,
	GPR_G4 = 4,
	GPR_G5 = 5,
	GPR_G6 = 6,
	GPR_G7 = 7,
	GPR_O0 = 8,
	GPR_O1 = 9,
	GPR_O2 = 10,
	GPR_O3 = 11,
	GPR_O4 = 12,
	GPR_O5 = 13,
	GPR_O6 = 14,
	GPR_O7 = 15,
	GPR_L0 = 16,
	GPR_L1 = 17,
	GPR_L2 = 18,
	GPR_L3 = 19,
	GPR_L4 = 20,
	GPR_L5 = 21,
	GPR_L6 = 22,
	GPR_L7 = 23,
	GPR_I0 = 24,
	GPR_I1 = 25,
	GPR_I2 = 26,
	GPR_I3 = 27,
	GPR_I4 = 28,
	GPR_I5 = 29,
	GPR_I6 = 30,
	GPR_I7 = 31,
};

const char *gpr_regs[] = { "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
	"o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7",
	"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
	"i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7" };

enum {
	ICC_A = 0x8,
	ICC_CC = 0xd,
	ICC_CS = 0x5,
	ICC_E = 0x1,
	ICC_G = 0xa,
	ICC_GE = 0xb,
	ICC_GU = 0xc,
	ICC_L = 0x3,
	ICC_LE = 0x2,
	ICC_LEU = 0x4,
	ICC_N = 0x0,
	ICC_NE = 0x9,
	ICC_NEG = 0x6,
	ICC_POS = 0xe,
	ICC_VC = 0xf,
	ICC_VS = 0x7,
};

enum {
	FCC_A = 0x8,
	FCC_E = 0x9,
	FCC_G = 0x6,
	FCC_GE = 0xb,
	FCC_L = 0x4,
	FCC_LE = 0xd,
	FCC_LG = 0x2,
	FCC_N = 0x0,
	FCC_NE = 0x1,
	FCC_O = 0xf,
	FCC_U = 0x7,
	FCC_UE = 0xa,
	FCC_UG = 0x5,
	FCC_UGE = 0xc,
	FCC_UL = 0x3,
	FCC_ULE = 0xe,
};
/* Define some additional conditions that are nor mappable to
   the existing RZ_TYPE_COND* ones and need to be handled in a
   special way. */
enum {
	RZ_TYPE_COND_ALWAYS = -1,
	RZ_TYPE_COND_NEVER = -2,
	RZ_TYPE_COND_UNKNOWN = -3,
};

static int icc_to_r_cond(const int cond) {
	/* we treat signed and unsigned the same here */
	switch (cond) {
	case ICC_A: return RZ_TYPE_COND_ALWAYS;
	case ICC_CC: return RZ_TYPE_COND_GE;
	case ICC_CS: return RZ_TYPE_COND_LT;
	case ICC_E: return RZ_TYPE_COND_EQ;
	case ICC_G: return RZ_TYPE_COND_GT;
	case ICC_GE: return RZ_TYPE_COND_GE;
	case ICC_GU: return RZ_TYPE_COND_GT;
	case ICC_L: return RZ_TYPE_COND_LT;
	case ICC_LE: return RZ_TYPE_COND_LE;
	case ICC_LEU: return RZ_TYPE_COND_LE;
	case ICC_N: return RZ_TYPE_COND_NEVER;
	case ICC_NE: return RZ_TYPE_COND_NE;
	case ICC_NEG:
	case ICC_POS:
	case ICC_VC:
	case ICC_VS:
	default: return RZ_TYPE_COND_UNKNOWN;
	}
}

static int fcc_to_r_cond(const int cond) {
	switch (cond) {
	case FCC_A: return RZ_TYPE_COND_ALWAYS;
	case FCC_E: return RZ_TYPE_COND_EQ;
	case FCC_G: return RZ_TYPE_COND_GT;
	case FCC_GE: return RZ_TYPE_COND_GE;
	case FCC_L: return RZ_TYPE_COND_LT;
	case FCC_LE: return RZ_TYPE_COND_LE;
	case FCC_LG: return RZ_TYPE_COND_NE;
	case FCC_N: return RZ_TYPE_COND_NEVER;
	case FCC_NE: return RZ_TYPE_COND_NE;
	case FCC_O:
	case FCC_U:
	case FCC_UE:
	case FCC_UG:
	case FCC_UGE:
	case FCC_UL:
	case FCC_ULE:
	default:
		return RZ_TYPE_COND_UNKNOWN;
	}
}

#define X_OP(i)   (((i) >> 30) & 0x3)
#define X_OP2(i)  (((i) >> 22) & 0x7)
#define X_OP3(i)  (((i) >> 19) & 0x3f)
#define X_COND(i) (((i) >> 25) & 0x1f)

#define X_RD(i)      (((i) >> 25) & 0x1f)
#define X_RS1(i)     (((i) >> 14) & 0x1f)
#define X_LDST_I(i)  (((i) >> 13) & 1)
#define X_ASI(i)     (((i) >> 5) & 0xff)
#define X_RS2(i)     (((i) >> 0) & 0x1f)
#define X_IMM(i, n)  (((i) >> 0) & ((1 << (n)) - 1))
#define X_SIMM(i, n) SIGN_EXT(X_IMM((i), (n)), (n))
#define X_DISP22(i)  (((i) >> 0) & 0x3fffff)
#define X_IMM22(i)   X_DISP22(i)
#define X_DISP30(i)  (((i) >> 0) & 0x3fffffff)

/* These are for v9.  */
#define X_DISP16(i) (((((i) >> 20) & 3) << 14) | (((i) >> 0) & 0x3fff))
#define X_DISP19(i) (((i) >> 0) & 0x7ffff)
#define X_MEMBAR(i) ((i)&0x7f)

enum {
	OP_0 = 0,
	OP_1 = 1,
	OP_2 = 2,
	OP_3 = 3,
};

enum {
	OP2_ILLTRAP = 0,
	OP2_BPcc = 1,
	OP2_Bicc = 2,
	OP2_BPr = 3,
	OP2_SETHI = 4,
	OP2_FBPfcc = 5,
	OP2_FBfcc = 6,
	OP2_INV = 7,
};

enum {
	OP32_ADD = 0x00,
	OP32_ADDcc = 0x10,
	OP32_TADDcc = 0x20,
	OP32_WRY = 0x30, /* or WRCCR WRASI WRASR WRFPRS SIR */
	OP32_AND = 0x01,
	OP32_ANDcc = 0x11,
	OP32_TSUBcc = 0x21,
	OP32_SAVED = 0x31, /* or RESTORED */
	OP32_OR = 0x02,
	OP32_ORcc = 0x12,
	OP32_TADDccTV = 0x22,
	OP32_WRPR = 0x32,
	OP32_XOR = 0x03,
	OP32_XORcc = 0x13,
	OP32_TSUBccTV = 0x23,
	OP32_SUB = 0x04,
	OP32_SUBcc = 0x14,
	OP32_MULSccD = 0x24,
	OP32_FPop1 = 0x34,
	OP32_ANDN = 0x05,
	OP32_ANDNcc = 0x15,
	OP32_SLL = 0x25, /* or SLLX */
	OP32_FPop2 = 0x35,
	OP32_ORN = 0x06,
	OP32_ORNcc = 0x16,
	OP32_SRL = 0x26, /* or SLRX */
	OP32_XNOR = 0x07,
	OP32_XNORcc = 0x17,
	OP32_SRA = 0x27, /* or SRAX */
	OP32_ADDC = 0x08,
	OP32_ADDCcc = 0x18,
	OP32_RDY = 0x28, /* or RDCCR RDASI RDTICK RDPC RDFPRS RDASR
			    MEMBAR STBAR  */
	OP32_JMPL = 0x38,
	OP32_MULX = 0x09,
	OP32_RETURN = 0x39,
	OP32_UMUL = 0x0a,
	OP32_UMULcc = 0x1a,
	OP32_RDPR = 0x2a,
	OP32_Tcc = 0x3a,
	OP32_SMULD = 0x0b,
	OP32_SMULcc = 0x1b,
	OP32_FLUSHW = 0x2b,
	OP32_FLUSH = 0x3b,
	OP32_SUBC = 0x0c,
	OP32_SUBCcc = 0x1c,
	OP32_MOVcc = 0x2c,
	OP32_SAVE = 0x3c,
	OP32_UDIVX = 0x0d,
	OP32_SDIVX = 0x2d,
	OP32_RESTORE = 0x3d,
	OP32_UDIV = 0x0e,
	OP32_UDIVcc = 0x1e,
	OP32_POPC = 0x2e,
	OP32_DONE = 0x3e, /* or RETRY */
	OP32_SDIV = 0x0f,
	OP32_SDIVcc = 0x1f,
	OP32_MOVr = 0x2f,
	/* always invalid */
	OP32_INV1 = 0x33,
	OP32_INV2 = 0x19,
	OP32_INV3 = 0x29,
	OP32_INV4 = 0x1d,
	OP32_INV5 = 0x3f,
	/* invalid under certain conditions */
	OP32_CONDINV1 = 0x30,
	OP32_CONDINV2 = 0x28,
	OP32_CONDINV3 = 0x2e,
};

enum {
	OP33_INV1 = 0x31,
	OP33_INV2 = 0x35,
	OP33_INV3 = 0x28,
	OP33_INV4 = 0x38,
	OP33_INV5 = 0x29,
	OP33_INV6 = 0x39,
	OP33_INV7 = 0x2a,
	OP33_INV8 = 0x3a,
	OP33_INV9 = 0x2b,
	OP33_INV10 = 0x3b,
	OP33_INV11 = 0x0c,
	OP33_INV12 = 0x1c,
	OP33_INV13 = 0x2c,
	OP33_INV14 = 0x2e,
	OP33_INV15 = 0x2f,
	OP33_INV16 = 0x3f,
};

static st64 get_immed_sgnext(const ut64 insn, const ut8 nbit) {
	const ut64 mask = ~(((ut64)1 << (nbit + 1)) - 1);
	return (st64)((insn & ~mask) | (((insn & ((ut64)1 << nbit)) >> nbit) * mask));
}

static RzAnalysisValue *value_fill_addr_pc_disp(const ut64 addr, const st64 disp) {
	RzAnalysisValue *val = rz_analysis_value_new();
	val->base = addr + disp;
	return val;
}

static RzAnalysisValue *value_fill_addr_reg_regdelta(RzAnalysis const *const analysis, const int ireg, const int iregdelta) {
	RzAnalysisValue *val = rz_analysis_value_new();
	val->reg = rz_reg_get(analysis->reg, gpr_regs[ireg], RZ_REG_TYPE_GPR);
	val->reg = rz_reg_get(analysis->reg, gpr_regs[iregdelta], RZ_REG_TYPE_GPR);
	return val;
}

static RzAnalysisValue *value_fill_addr_reg_disp(RzAnalysis const *const analysis, const int ireg, const st64 disp) {
	RzAnalysisValue *val = rz_analysis_value_new();
	val->reg = rz_reg_get(analysis->reg, gpr_regs[ireg], RZ_REG_TYPE_GPR);
	val->delta = disp;
	return val;
}

static void analysis_call(RzAnalysisOp *op, const ut32 insn, const ut64 addr) {
	const st64 disp = (get_immed_sgnext(insn, 29) * 4);
	op->type = RZ_ANALYSIS_OP_TYPE_CALL;
	op->dst = value_fill_addr_pc_disp(addr, disp);
	op->jump = addr + disp;
	op->fail = addr + 4;
}

static void analysis_jmpl(RzAnalysis const *const analysis, RzAnalysisOp *op, const ut32 insn, const ut64 addr) {
	st64 disp = 0;
	if (X_LDST_I(insn)) {
		disp = get_immed_sgnext(insn, 12);
	}

	if (X_RD(insn) == GPR_O7) {
		op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
		op->fail = addr + 4;
	} else if (X_RD(insn) == GPR_G0 && X_LDST_I(insn) == 1 && (X_RS1(insn) == GPR_I7 || X_RS1(insn) == GPR_O7) && disp == 8) {
		op->type = RZ_ANALYSIS_OP_TYPE_RET;
		op->eob = true;
		return;
	} else {
		op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
		op->eob = true;
	}

	if (X_LDST_I(insn)) {
		op->dst = value_fill_addr_reg_disp(analysis, X_RS1(insn), disp);
	} else {
		op->dst = value_fill_addr_reg_regdelta(analysis, X_RS1(insn), X_RS2(insn));
	}
}

static void analysis_branch(RzAnalysisOp *op, const ut32 insn, const ut64 addr) {
	st64 disp = 0;
	int rz_cond = 0;
	op->eob = true;

	/* handle the conditions */
	if (X_OP2(insn) == OP2_Bicc || X_OP2(insn) == OP2_BPcc) {
		rz_cond = icc_to_r_cond(X_COND(insn));
	} else if (X_OP2(insn) == OP2_FBfcc || X_OP2(insn) == OP2_FBPfcc) {
		rz_cond = fcc_to_r_cond(X_COND(insn));
	} else if (X_OP2(insn) == OP2_BPr) {
		rz_cond = RZ_TYPE_COND_UNKNOWN;
	}

	if (rz_cond == RZ_TYPE_COND_ALWAYS) {
		op->type = RZ_ANALYSIS_OP_TYPE_JMP;
	} else if (rz_cond == RZ_TYPE_COND_NEVER) {
		op->type = RZ_ANALYSIS_OP_TYPE_NOP;
		return;
	} else {
		op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
		op->fail = addr + 4;
	}

	/* handle displacement */
	if (X_OP2(insn) == OP2_Bicc || X_OP2(insn) == OP2_FBfcc) {
		disp = get_immed_sgnext(insn, 21) * 4;
	} else if (X_OP2(insn) == OP2_BPcc || X_OP2(insn) == OP2_FBPfcc) {
		disp = get_immed_sgnext(insn, 18) * 4;
	} else if (X_OP2(insn) == OP2_BPr) {
		disp = get_immed_sgnext(X_DISP16(insn), 15) * 4;
	}
	op->dst = value_fill_addr_pc_disp(addr, disp);
	op->jump = addr + disp;
}

// TODO: this implementation is just a fast hack. needs to be rewritten and completed
static int sparc_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask) {
	int sz = 4;
	ut32 insn;

	op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
	op->addr = addr;
	op->size = sz;

	if (!analysis->big_endian) {
		((char *)&insn)[0] = data[3];
		((char *)&insn)[1] = data[2];
		((char *)&insn)[2] = data[1];
		((char *)&insn)[3] = data[0];
	} else {
		memcpy(&insn, data, sz);
	}

	if (X_OP(insn) == OP_0) {
		switch (X_OP2(insn)) {
		case OP2_ILLTRAP:
		case OP2_INV:
			op->type = RZ_ANALYSIS_OP_TYPE_ILL;
			sz = 0; /* make rz_core_analysis_bb stop */
			break;
		case OP2_BPcc:
		case OP2_Bicc:
		case OP2_BPr:
		case OP2_FBPfcc:
		case OP2_FBfcc:
			analysis_branch(op, insn, addr);
			break;
		}
	} else if (X_OP(insn) == OP_1) {
		analysis_call(op, insn, addr);
	} else if (X_OP(insn) == OP_2) {
		switch (X_OP3(insn)) {
		case OP32_INV1:
		case OP32_INV2:
		case OP32_INV3:
		case OP32_INV4:
		case OP32_INV5:
			op->type = RZ_ANALYSIS_OP_TYPE_ILL;
			sz = 0; /* make rz_core_analysis_bb stop */
			break;
		case OP32_CONDINV1:
			if (X_RD(insn) == 1) {
				op->type = RZ_ANALYSIS_OP_TYPE_ILL;
				sz = 0; /* make rz_core_analysis_bb stop */
			}
			break;
		case OP32_CONDINV2:
			if (X_RS1(insn) == 1) {
				op->type = RZ_ANALYSIS_OP_TYPE_ILL;
				sz = 0; /* make rz_core_analysis_bb stop */
			}
			break;
		case OP32_CONDINV3:
			if (X_RS1(insn) != 0) {
				op->type = RZ_ANALYSIS_OP_TYPE_ILL;
				sz = 0; /* make rz_core_analysis_bb stop */
			}
			break;

		case OP32_JMPL:
			analysis_jmpl(analysis, op, insn, addr);
			break;
		}
	} else if (X_OP(insn) == OP_3) {
		switch (X_OP3(insn)) {
		case OP33_INV1:
		case OP33_INV2:
		case OP33_INV3:
		case OP33_INV4:
		case OP33_INV5:
		case OP33_INV6:
		case OP33_INV7:
		case OP33_INV8:
		case OP33_INV9:
		case OP33_INV10:
		case OP33_INV11:
		case OP33_INV12:
		case OP33_INV13:
		case OP33_INV14:
		case OP33_INV15:
		case OP33_INV16:
			op->type = RZ_ANALYSIS_OP_TYPE_ILL;
			sz = 0; /* make rz_core_analysis_bb stop */
			break;
		}
	}

	return sz;
}

static char *get_reg_profile(RzAnalysis *analysis) {
	/* As far as I can see, sparc v9 register and instruction set
	   don't depened  on bits of the running application.
	   But: They depend on the bits of the consuming application,
	   that is the bits rizin had been compiled with.
	   See sys/procfs_isa.h on a Solaris10 Sparc machine and
	   'man 4 core' for reference.
	 */
	const char *p =
		"=PC	pc\n"
		"=SP	o6\n"
		"=BP	i6\n"
		"=A0	g0\n"
		"=A1	g1\n"
		/* prgregset_t for _LP64 */
		"gpr	g0	.64	0	0\n"
		"gpr	g1	.64	8	0\n"
		"gpr	g2	.64	16	0\n"
		"gpr	g3	.64	24	0\n"
		"gpr	g4	.64	32	0\n"
		"gpr	g5	.64	40	0\n"
		"gpr	g6	.64	48	0\n"
		"gpr	g7	.64	56	0\n"
		"gpr	o0	.64	64	0\n"
		"gpr	o1	.64	72	0\n"
		"gpr	o2	.64	80	0\n"
		"gpr	o3	.64	88	0\n"
		"gpr	o4	.64	96	0\n"
		"gpr	o5	.64	104	0\n"
		"gpr	o6	.64	112	0\n"
		"gpr	o7	.64	120	0\n"
		"gpr	l0	.64	128	0\n"
		"gpr	l1	.64	136	0\n"
		"gpr	l2	.64	144	0\n"
		"gpr	l3	.64	152	0\n"
		"gpr	l4	.64	160	0\n"
		"gpr	l5	.64	168	0\n"
		"gpr	l6	.64	176	0\n"
		"gpr	l7	.64	184	0\n"
		"gpr	i0	.64	192	0\n"
		"gpr	i1	.64	200	0\n"
		"gpr	i2	.64	208	0\n"
		"gpr	i3	.64	216	0\n"
		"gpr	i4	.64	224	0\n"
		"gpr	i5	.64	232	0\n"
		"gpr	i6	.64	240	0\n"
		"gpr	i7	.64	248	0\n"
		"gpr	ccr	.64	256	0\n"
		"gpr	pc	.64	264	0\n"
		"gpr	ncp	.64	272	0\n"
		"gpr	y	.64	280	0\n"
		"gpr	asi	.64	288	0\n"
		"gpr	fprs	.64	296	0\n"
		/* beginning of prfpregset_t for __sparcv9 */
		"fpu	sf0	.32	304	0\n"
		"fpu	sf1	.32	308	0\n"
		"fpu	sf2	.32	312	0\n"
		"fpu	sf3	.32	316	0\n"
		"fpu	sf4	.32	320	0\n"
		"fpu	sf5	.32	324	0\n"
		"fpu	sf6	.32	328	0\n"
		"fpu	sf7	.32	332	0\n"
		"fpu	sf8	.32	336	0\n"
		"fpu	sf9	.32	340	0\n"
		"fpu	sf10	.32	344	0\n"
		"fpu	sf11	.32	348	0\n"
		"fpu	sf12	.32	352	0\n"
		"fpu	sf13	.32	356	0\n"
		"fpu	sf14	.32	360	0\n"
		"fpu	sf15	.32	364	0\n"
		"fpu	sf16	.32	368	0\n"
		"fpu	sf17	.32	372	0\n"
		"fpu	sf18	.32	376	0\n"
		"fpu	sf19	.32	380	0\n"
		"fpu	sf20	.32	384	0\n"
		"fpu	sf21	.32	388	0\n"
		"fpu	sf22	.32	392	0\n"
		"fpu	sf23	.32	396	0\n"
		"fpu	sf24	.32	400	0\n"
		"fpu	sf25	.32	404	0\n"
		"fpu	sf26	.32	408	0\n"
		"fpu	sf27	.32	412	0\n"
		"fpu	sf28	.32	416	0\n"
		"fpu	sf29	.32	420	0\n"
		"fpu	sf30	.32	424	0\n"
		"fpu	sf31	.32	428	0\n"
		"fpu	df0	.64	304	0\n" /* sf0 sf1 */
		"fpu	df2	.64	312	0\n" /* sf2 sf3 */
		"fpu	df4	.64	320	0\n" /* sf4 sf5 */
		"fpu	df6	.64	328	0\n" /* sf6 sf7 */
		"fpu	df8	.64	336	0\n" /* sf8 sf9 */
		"fpu	df10	.64	344	0\n" /* sf10 sf11 */
		"fpu	df12	.64	352	0\n" /* sf12 sf13 */
		"fpu	df14	.64	360	0\n" /* sf14 sf15 */
		"fpu	df16	.64	368	0\n" /* sf16 sf17 */
		"fpu	df18	.64	376	0\n" /* sf18 sf19 */
		"fpu	df20	.64	384	0\n" /* sf20 sf21 */
		"fpu	df22	.64	392	0\n" /* sf22 sf23 */
		"fpu	df24	.64	400	0\n" /* sf24 sf25 */
		"fpu	df26	.64	408	0\n" /* sf26 sf27 */
		"fpu	df28	.64	416	0\n" /* sf28 sf29 */
		"fpu	df30	.64	424	0\n" /* sf30 sf31 */
		"fpu	df32	.64	432	0\n"
		"fpu	df34	.64	440	0\n"
		"fpu	df36	.64	448	0\n"
		"fpu	df38	.64	456	0\n"
		"fpu	df40	.64	464	0\n"
		"fpu	df42	.64	472	0\n"
		"fpu	df44	.64	480	0\n"
		"fpu	df46	.64	488	0\n"
		"fpu	df48	.64	496	0\n"
		"fpu	df50	.64	504	0\n"
		"fpu	df52	.64	512	0\n"
		"fpu	df54	.64	520	0\n"
		"fpu	df56	.64	528	0\n"
		"fpu	df58	.64	536	0\n"
		"fpu	df60	.64	544	0\n"
		"fpu	df62	.64	552	0\n"
		"fpu	qf0	.128	304	0\n" /* sf0 sf1 sf2 sf3 */
		"fpu	qf4	.128	320	0\n" /* sf4 sf5 sf6 sf7 */
		"fpu	qf8	.128	336	0\n" /* sf8 sf9 sf10 sf11 */
		"fpu	qf12	.128	352	0\n" /* sf12 sf13 sf14 sf15 */
		"fpu	qf16	.128	368	0\n" /* sf16 sf17 sf18 sf19 */
		"fpu	qf20	.128	384	0\n" /* sf20 sf21 sf22 sf23 */
		"fpu	qf24	.128	400	0\n" /* sf24 sf25 sf26 sf27 */
		"fpu	qf28	.128	416	0\n" /* sf28 sf29 sf30 sf31 */
		"fpu	qf32	.128	432	0\n" /* df32 df34 */
		"fpu	qf36	.128	448	0\n" /* df36 df38 */
		"fpu	qf40	.128	464	0\n" /* df40 df42 */
		"fpu	qf44	.128	480	0\n" /* df44 df46 */
		"fpu	qf48	.128	496	0\n" /* df48 df50 */
		"fpu	qf52	.128	512	0\n" /* df52 df54 */
		"fpu	qf56	.128	528	0\n" /* df56 df68 */
		"fpu	qf60	.128	544	0\n" /* df60 df62 */
		"gpr	fsr	.64	560	0\n"; /* note that
						   we've left out the filler */
	return rz_str_dup(p);
}

static int archinfo(RzAnalysis *a, RzAnalysisInfoType query) {
	switch (query) {
	case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE:
		/* fall-thru */
	case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE:
		/* fall-thru */
	case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN:
		/* fall-thru */
	case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN:
		return 4;
	case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS:
		return true;
	default:
		return -1;
	}
}

RzAnalysisPlugin rz_analysis_plugin_sparc_gnu = {
	.name = "sparc.gnu",
	.desc = "SPARC analysis plugin",
	.license = "LGPL3",
	.arch = "sparc",
	.bits = 32 | 64,
	.op = &sparc_op,
	.archinfo = archinfo,
	.get_reg_profile = get_reg_profile,
};
