/* radare - LGPL - Copyright 2011-2024 -- pancake */

#include <r_arch.h>
#include "../../include/disas-asm.h"

// XXX This can be a generic function defined in a macro
static int sparc_buffer_read_memory(bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, struct disassemble_info *info) {
	int delta = (memaddr - info->buffer_vma);
	if (delta < 0) {
		return -1;      // disable backward reads
	}
	if ((delta + length) > 4) {
		return -1;
	}
	ut8 *bytes = info->buffer;
	if (length > 0) {
		memcpy (myaddr, bytes + delta, length);
	}
	return 0;
}

static int symbol_at_address(bfd_vma addr, struct disassemble_info *info) {
	return 0;
}

static void memory_error_func(int status, bfd_vma memaddr, struct disassemble_info *info) {
	//--
}

DECLARE_GENERIC_PRINT_ADDRESS_FUNC_NOGLOBALS()
DECLARE_GENERIC_FPRINTF_FUNC_NOGLOBALS()

static bool disassemble(RArchSession *a, RAnalOp *op, ut8 *bytes, RArchDecodeMask mask) {
	const int len = op->size;
	const ut64 addr = op->addr;
	struct disassemble_info disasm_obj;
	if (len < 4) {
		return false;
	}
	RStrBuf *sb = r_strbuf_new ("");
	if (!sb) {
		return false;
	}
	/* prepare disassembler */
	memset (&disasm_obj, '\0', sizeof (struct disassemble_info));
	disasm_obj.buffer = bytes; // 4 byte length
	disasm_obj.buffer_vma = addr;
	disasm_obj.read_memory_func = &sparc_buffer_read_memory;
	disasm_obj.symbol_at_address_func = &symbol_at_address;
	disasm_obj.memory_error_func = &memory_error_func;
	disasm_obj.print_address_func = &generic_print_address_func;
	disasm_obj.endian = a->config->big_endian;
	disasm_obj.fprintf_func = &generic_fprintf_func;
	disasm_obj.stream = sb;
	disasm_obj.mach = ((a->config->bits == 64)
			   ? bfd_mach_sparc_v9b
			   : 0);

	op->size = print_insn_sparc ((bfd_vma)addr, &disasm_obj);

	char *s = r_strbuf_drain (sb);
	if (R_STR_ISEMPTY (s) || r_str_startswith (s, "unknown")) {
		free (s);
		op->mnemonic = strdup ("invalid");
	} else if (op->size > 0) {
		op->mnemonic = s;
	} else {
		free (s);
	}
	return true;
}

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,
};

#if 0
static 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"
};
#endif

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 R_ANAL_COND* ones and need to be handled in a
 * special way. */
enum {
	R_ANAL_CONDTYPE_ALWAYS = -1,
	R_ANAL_CONDTYPE_NEVER = -2,
	R_ANAL_CONDTYPE_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 R_ANAL_CONDTYPE_ALWAYS;
	case ICC_CC: return R_ANAL_CONDTYPE_GE;
	case ICC_CS: return R_ANAL_CONDTYPE_LT;
	case ICC_E: return R_ANAL_CONDTYPE_EQ;
	case ICC_G: return R_ANAL_CONDTYPE_GT;
	case ICC_GE: return R_ANAL_CONDTYPE_GE;
	case ICC_GU: return R_ANAL_CONDTYPE_GT;
	case ICC_L: return R_ANAL_CONDTYPE_LT;
	case ICC_LE: return R_ANAL_CONDTYPE_LE;
	case ICC_LEU: return R_ANAL_CONDTYPE_LE;
	case ICC_N: return R_ANAL_CONDTYPE_NEVER;
	case ICC_NE: return R_ANAL_CONDTYPE_NE;
	case ICC_NEG:
	case ICC_POS:
	case ICC_VC:
	case ICC_VS:
	default: return R_ANAL_CONDTYPE_UNKNOWN;
	}
}

static int fcc_to_r_cond(const int cond) {
	switch (cond) {
	case FCC_A: return R_ANAL_CONDTYPE_ALWAYS;
	case FCC_E: return R_ANAL_CONDTYPE_EQ;
	case FCC_G: return R_ANAL_CONDTYPE_GT;
	case FCC_GE: return R_ANAL_CONDTYPE_GE;
	case FCC_L: return R_ANAL_CONDTYPE_LT;
	case FCC_LE: return R_ANAL_CONDTYPE_LE;
	case FCC_LG: return R_ANAL_CONDTYPE_NE;
	case FCC_N: return R_ANAL_CONDTYPE_NEVER;
	case FCC_NE: return R_ANAL_CONDTYPE_NE;
	case FCC_O:
	case FCC_U:
	case FCC_UE:
	case FCC_UG:
	case FCC_UGE:
	case FCC_UL:
	case FCC_ULE:
	default:
		return R_ANAL_CONDTYPE_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)  SEX (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 RAnalValue *value_fill_addr_pc_disp(const ut64 addr, const st64 disp) {
	return NULL;
#if 0
	RAnalValue *val = r_anal_value_new();
	val->base = addr + disp;
	return val;
#endif
}

static RAnalValue * value_fill_addr_reg_regdelta(RArchSession *as, const int ireg, const int iregdelta) {
	return NULL;
#if 0
	RAnalValue *val = r_anal_value_new ();
	val->reg = r_reg_get (anal->reg, gpr_regs[ireg], R_REG_TYPE_GPR);
	val->reg = r_reg_get (anal->reg, gpr_regs[iregdelta], R_REG_TYPE_GPR);
	return val;
#endif
}

static RAnalValue * value_fill_addr_reg_disp(RArchSession *as, const int ireg, const st64 disp) {
	return NULL;
#if 0
	RAnalValue *val = r_anal_value_new();
	val->reg = r_reg_get(anal->reg, gpr_regs[ireg], R_REG_TYPE_GPR);
	val->delta = disp;
	return val;
#endif
}

static void anal_call(RAnalOp *op, const ut32 insn, const ut64 addr) {
	const st64 disp = (get_immed_sgnext(insn, 29) * 4);
	op->type = R_ANAL_OP_TYPE_CALL;
	RAnalValue *val = value_fill_addr_pc_disp (addr, disp);
	r_vector_push (&op->dsts, val);
	r_anal_value_free (val);
	op->jump = addr + disp;
	op->fail = addr + 4;
}

static void anal_jmpl(RArchSession *as, RAnalOp *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 = R_ANAL_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 = R_ANAL_OP_TYPE_RET;
		op->eob = true;
		return;
	}
	op->type = R_ANAL_OP_TYPE_UJMP;
	op->eob = true;

	RAnalValue *val;
	if (X_LDST_I (insn)) {
		val = value_fill_addr_reg_disp (as, X_RS1 (insn), disp);
	} else {
		val = value_fill_addr_reg_regdelta (as, X_RS1 (insn), X_RS2 (insn));
	}
	r_vector_push (&op->dsts, val);
	r_anal_value_free (val);
}

static void anal_branch(RAnalOp *op, const ut32 insn, const ut64 addr) {
	st64 disp = 0;
	int r_cond = 0;
	op->eob = true;

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

	if (r_cond == R_ANAL_CONDTYPE_ALWAYS) {
		op->type = R_ANAL_OP_TYPE_JMP;
	} else if (r_cond == R_ANAL_CONDTYPE_NEVER) {
		op->type = R_ANAL_OP_TYPE_NOP;
		return;
	}
	op->type = R_ANAL_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;
	}
	RAnalValue *val = value_fill_addr_pc_disp (addr, disp);
	r_vector_push (&op->dsts, val);
	r_anal_value_free (val);
	op->jump = addr + disp;
}

// TODO: this implementation is just a fast hack. needs to be rewritten and completed
// static int sparc_op(RArchSession *as, RAnalOp *op, ut64 addr, const ut8 *data, int len, RAnalOpMask mask) {
static bool decode(RArchSession *as, RAnalOp *op, RArchDecodeMask mask) {
	const ut64 addr = op->addr;
	int sz = 4;

	op->family = R_ANAL_OP_FAMILY_CPU;
	op->addr = addr;
	op->size = 4;
	if (op->size < 4) {
		return false;
	}

	ut32 insn = r_read_ble32 (op->bytes, R_ARCH_CONFIG_IS_BIG_ENDIAN (as->config));
	if (mask & R_ARCH_OP_MASK_DISASM) {
		disassemble (as, op, (ut8*)&insn, mask);
	}

	if (X_OP (insn) == OP_0) {
		switch (X_OP2 (insn)) {
		case OP2_ILLTRAP:
		case OP2_INV:
			op->type = R_ANAL_OP_TYPE_ILL;
			sz = 0; /* make r_core_anal_bb stop */
			break;
		case OP2_BPcc:
		case OP2_Bicc:
		case OP2_BPr:
		case OP2_FBPfcc:
		case OP2_FBfcc:
			anal_branch (op, insn, addr);
			break;
		}
	} else if (X_OP (insn) == OP_1) {
		anal_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 = R_ANAL_OP_TYPE_ILL;
			sz = 0; /* make r_core_anal_bb stop */
			break;
		case OP32_CONDINV1:
			if (X_RD (insn) == 1) {
				op->type = R_ANAL_OP_TYPE_ILL;
				sz = 0; /* make r_core_anal_bb stop */
			}
			break;
		case OP32_CONDINV2:
			if (X_RS1 (insn) == 1) {
				op->type = R_ANAL_OP_TYPE_ILL;
				sz = 0; /* make r_core_anal_bb stop */
			}
			break;
		case OP32_CONDINV3:
			if (X_RS1 (insn) != 0) {
				op->type = R_ANAL_OP_TYPE_ILL;
				sz = 0; /* make r_core_anal_bb stop */
			}
			break;
		case OP32_JMPL:
			anal_jmpl (as, 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 = R_ANAL_OP_TYPE_ILL;
			sz = 0; /* make r_core_anal_bb stop */
			break;
		 }
	}
	return sz;
}

static char *regs(RArchSession *as) {
	/* 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 radare had been compiled with.
	   See sys/procfs_isa.h on a Solaris10 Sparc machine and
	   'man 4 core' for reference.
	 */
	const char * const 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 strdup (p);
}

static int archinfo(RArchSession *as, ut32 q) {
	return 4; /* :D */
}

const RArchPlugin r_arch_plugin_sparc_gnu = {
	.meta = {
		.name = "sparc.gnu",
		.author = "pancake",
		.desc = "Scalable Processor Architecture",
		.license = "GPL-3.0-only",
	},
	.arch = "sparc",
	.bits = R_SYS_BITS_PACK2 (32, 64),
	.decode = &decode,
	.endian = R_SYS_ENDIAN_BIG | R_SYS_ENDIAN_LITTLE,
	.info = archinfo,
	.regs = regs,
};

#ifndef R2_PLUGIN_INCORE
R_API RLibStruct radare_plugin = {
	.type = R_LIB_TYPE_ARCH,
	.data = &r_arch_plugin_sparc_gnu,
	.version = R2_VERSION
};
#endif
