/* radare - LGPL - Copyright 2017-2024 - wargio */

#include <r_lib.h>
#include <r_flag.h>
#include <r_anal.h>
#include <r_asm.h>

static char *replace(int argc, const char *argv[]) {
	int i, j;
	struct {
		const char *op;
		const char *str;
	} ops[] = {
		{ "add",     "B += A" },
		{ "addc",    "B += A + t" },
		{ "addv",    "B += A; t = int_overflow (B)" },
		{ "and",     "B &= A" },
		{ "and.b",   "B &= A" },
		{ "bf",      "if (!t) goto A" },
		{ "bf.s",    "if (!t) goto A" },
		{ "bra",     "goto A" },
		{ "brk",     "_break_exception ()" },
		{ "bsr",     "A ()" },
		{ "bsrf",    "A ()" },
		{ "bt",      "if (t) goto A" },
		{ "bt.s",    "if (t) goto A" },
		{ "clrmac",  "_clrmac ()" },
		{ "clrs",    "_clrs ()" },
		{ "clrt",    "_clrt ()" },
		{ "cmp/eq",  "t = B == A ? 1 : 0" },
		{ "cmp/ge",  "t = B >= A ? 1 : 0" },
		{ "cmp/gt",  "t = B > A ? 1 : 0" },
		{ "cmp/hi",  "t = (unsigned) B > (unsigned) A ? 1 : 0" },
		{ "cmp/hs",  "t = (unsigned) B >= (unsigned) A ? 1 : 0" },
		{ "cmp/pl",  "t = A > 0 ? 1 : 0" },
		{ "cmp/pz",  "t = A >= 0 ? 1 : 0" },
		{ "cmp/str", "t = A ^ B ? 1 : 0" },
		{ "div1",    "B /= A" },
		{ "dmuls.l", "mac = B * A" },
		{ "dmulu.l", "mac = (unsigned) B * (unsigned) A" },
		{ "dt",      "A--; t = !A ? 1 : 0" },
		{ "exts.b",  "B = (int) A" },
		{ "extu.b",  "B = (unsigned int) A" },
		{ "exts.w",  "B = (int) A" },
		{ "extu.w",  "B = (unsigned int) A" },
		{ "fabs",    "A = abs (A)" },
		{ "fadd",    "B += A" },
		{ "fcmp/eq", "t = B == A ? 1 : 0" },
		{ "fcmp/gt", "t = B > A ? 1 : 0" },
		{ "fcnvds",  "B = A" },
		{ "fdiv",    "B /= A" },
		{ "flds",    "B = A" },
		{ "fldi0",   "A = 0.0f" },
		{ "fldi1",   "A = 1.0f" },
		{ "float",   "B = A" },
		{ "fmac",    "C += A * B" },
		{ "fmov",    "B = A" },
		{ "fmov.s",  "B = A" },
		{ "fmul",    "B *= A" },
		{ "fneg",    "A = -A" },
		{ "fsqrt",   "A = sqrt (A)" },
		{ "fsts",    "B = A" },
		{ "fsub",    "B -= A" },
		{ "ftrc",    "B = trunc (A)" },
		{ "ftrv",    "B *= A" },
		{ "jmp",     "goto A" },
		{ "jsr",     "A ()" },
		{ "ldr",     "B = A" },
		{ "ldr.l",   "B = A" },
		{ "lds",     "B = A" },
		{ "lds.l",   "B = A" },
		{ "mov",     "B = A" },
		{ "mov.b",   "B = A" },
		{ "mov.l",   "B = A" },
		{ "mov.w",   "B = A" },
		{ "movca.l", "B = A" },
		{ "movt",    "A = t" },
		{ "muls.w",  "macl = A * B" },
		{ "mulu.w",  "macl = (unsigned) A * (unsigned) B" },
		{ "neg",     "A = -A" },
		{ "negc",    "A = (-A) - t" },
		{ "nop",     "" },
		{ "not",     "A = !A" },
		{ "or",      "B |= A" },
		{ "rotcl",   "t = A & 0x80000000 ? 0 : 1; A = (A << 1) | t" },
		{ "rotl",    "A = (A << 1) | (A >> 31)" },
		{ "rotr",    "A = (A << 31) | (A >> 1)" },
		{ "rte",     "_rte ()" },
		{ "rts",     "return" },
		{ "sets",    "s = 1" },
		{ "sett",    "t = 1" },
		{ "shad",    "B = A >= 0 ? B << A : B >> (31 - A)" },
		{ "shal",    "A <<= 1" },
		{ "shar",    "A >>= 1" },
		{ "shld",    "B = A >= 0 ? B << A : B >> (31 - A)" },
		{ "shll",    "A <<= 1" },
		{ "shll2",   "A <<= 2" },
		{ "shll8",   "A <<= 8" },
		{ "shll16",  "A <<= 16" },
		{ "shlr",    "A >>= 1" },
		{ "shlr2",   "A >>= 2" },
		{ "shlr8",   "A >>= 8" },
		{ "shlr16",  "A >>= 16" },
		{ "sleep",   "_halt ()" },
		{ "stc",     "B = A" },
		{ "stc.l",   "B = A" },
		{ "sts",     "B = A" },
		{ "sts.l",   "B = A" },
		{ "sub",     "B -= A" },
		{ "subc",    "B -= A - t" },
		{ "subv",    "B -= A; t = int_underflow (B)" },
		{ "swap.b",  "swap_byte (B, A)" },
		{ "swap.w",  "swap_word (B, A)" },
		{ "tas.b",   "test_and_set (A)" },
		{ "trapa",   "trap (A)" },
		{ "tst",     "t = B & A ? 0 : 1" },
		{ "xor",     "B ^= A" },
		{ "xor.b",   "B ^= A" },
		{ NULL }
	};
	RStrBuf *sb = r_strbuf_new ("");
	for (i = 0; ops[i].op; i++) {
		if (!strcmp (ops[i].op, argv[0])) {
			const char *str = ops[i].str;
			for (j = 0; str[j]; j++) {
				if (str[j] >= 'A' && str[j] <= 'J') {
					const char *w = argv[str[j] - '@'];
					if (w) {
						r_strbuf_append (sb, w);
					}
				} else {
					r_strbuf_append_n (sb, str + j, 1);
				}
			}
			return r_strbuf_drain (sb);
		}
	}

	for (i = 0; i < argc; i++) {
		r_strbuf_append (sb, argv[i]);
		r_strbuf_append (sb, (i == 0 || i == argc - 1)? " ": ", ");
	}
	return r_strbuf_drain (sb);
}

#define WSZ 128
static char *parse(RAsmPluginSession *aps, const char *data) {
	int i, len = strlen (data);
	char w0[WSZ], w1[WSZ], w2[WSZ], w3[WSZ], w4[WSZ];
	char *ptr, *optr, *par;

	// malloc can be slow here :?
	char *buf = malloc (len + 1);
	if (!buf) {
		return false;
	}
	memcpy (buf, data, len + 1);
	char *str = NULL;

	r_str_trim (buf);
	if (*buf) {
		w0[0] = '\0';
		w1[0] = '\0';
		w2[0] = '\0';
		w3[0] = '\0';
		w4[0] = '\0';
		ptr = strchr (buf, ' ');
		if (!ptr) {
			ptr = strchr (buf, '\t');
		}
		if (ptr) {
			*ptr = '\0';
			for (ptr++; *ptr == ' '; ptr++) {
				//nothing to see here
			}
			r_str_ncpy (w0, buf, WSZ);
			r_str_ncpy (w1, ptr, WSZ);

			optr = ptr;
			par = strchr (ptr, '(');
			if (par && strchr (ptr, ',') > par) {
				ptr = strchr (ptr, ')');
				if (ptr) {
					ptr = strchr (ptr, ',');
				}
			} else {
				ptr = strchr (ptr, ',');
			}
			if (ptr) {
				*ptr = '\0';
				for (ptr++; *ptr == ' '; ptr++) {
					//nothing to see here
				}
				r_str_ncpy (w1, optr, WSZ);
				r_str_ncpy (w2, ptr, WSZ);
				optr = ptr;
				par = strchr (ptr, '(');
				if (par && strchr (ptr, ',') > par) {
					ptr = strchr (ptr, ')');
					if (ptr) {
						ptr = strchr (ptr, ',');
					}
				} else {
					ptr = strchr (ptr, ',');
				}
				if (ptr) {
					*ptr = '\0';
					for (ptr++; *ptr == ' '; ptr++) {
						//nothing to see here
					}
					r_str_ncpy (w2, optr, WSZ);
					r_str_ncpy (w3, ptr, WSZ);
					optr = ptr;
					// bonus
					par = strchr (ptr, '(');
					if (par && strchr (ptr, ',') > par) {
						ptr = strchr (ptr, ')');
						if (ptr) {
							ptr = strchr (ptr, ',');
						}
					} else {
						ptr = strchr (ptr, ',');
					}
					if (ptr) {
						*ptr = '\0';
						for (ptr++; *ptr == ' '; ptr++) {
							//nothing to see here
						}
						r_str_ncpy (w3, optr, WSZ);
						r_str_ncpy (w4, ptr, WSZ);
					}
				}
			}
		} else {
			r_str_ncpy (w0, buf, WSZ);
		}
		{
			const char *wa[] = { w0, w1, w2, w3, w4 };
			int nw = 0;
			for (i = 0; i < 5; i++) {
				if (wa[i][0] != '\0') {
					nw++;
				}
			}
			str = replace (nw, wa);
		}
	} else {
		str = strdup (data);
	}
	free (buf);
	return str;
}

RAsmPlugin r_asm_plugin_sh = {
	.meta = {
		.name = "sh",
		.desc = "SH-4 pseudo syntax",
		.author = "pancake",
		.license = "LGPL-3.0-only",
	},
	.parse = parse
};

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