// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include <linux/compiler.h>
#include <linux/zalloc.h>
#include "../../util/disasm.h"

static int is_branch_cond(const char *cond)
{
	if (cond[0] == '\0')
		return 1;

	if (cond[0] == 'a' && cond[1] == '\0')
		return 1;

	if (cond[0] == 'c' &&
	    (cond[1] == 'c' || cond[1] == 's') &&
	    cond[2] == '\0')
		return 1;

	if (cond[0] == 'e' &&
	    (cond[1] == '\0' ||
	     (cond[1] == 'q' && cond[2] == '\0')))
		return 1;

	if (cond[0] == 'g' &&
	    (cond[1] == '\0' ||
	     (cond[1] == 't' && cond[2] == '\0') ||
	     (cond[1] == 'e' && cond[2] == '\0') ||
	     (cond[1] == 'e' && cond[2] == 'u' && cond[3] == '\0')))
		return 1;

	if (cond[0] == 'l' &&
	    (cond[1] == '\0' ||
	     (cond[1] == 't' && cond[2] == '\0') ||
	     (cond[1] == 'u' && cond[2] == '\0') ||
	     (cond[1] == 'e' && cond[2] == '\0') ||
	     (cond[1] == 'e' && cond[2] == 'u' && cond[3] == '\0')))
		return 1;

	if (cond[0] == 'n' &&
	    (cond[1] == '\0' ||
	     (cond[1] == 'e' && cond[2] == '\0') ||
	     (cond[1] == 'z' && cond[2] == '\0') ||
	     (cond[1] == 'e' && cond[2] == 'g' && cond[3] == '\0')))
		return 1;

	if (cond[0] == 'b' &&
	    cond[1] == 'p' &&
	    cond[2] == 'o' &&
	    cond[3] == 's' &&
	    cond[4] == '\0')
		return 1;

	if (cond[0] == 'v' &&
	    (cond[1] == 'c' || cond[1] == 's') &&
	    cond[2] == '\0')
		return 1;

	if (cond[0] == 'b' &&
	    cond[1] == 'z' &&
	    cond[2] == '\0')
		return 1;

	return 0;
}

static int is_branch_reg_cond(const char *cond)
{
	if ((cond[0] == 'n' || cond[0] == 'l') &&
	    cond[1] == 'z' &&
	    cond[2] == '\0')
		return 1;

	if (cond[0] == 'z' &&
	    cond[1] == '\0')
		return 1;

	if ((cond[0] == 'g' || cond[0] == 'l') &&
	    cond[1] == 'e' &&
	    cond[2] == 'z' &&
	    cond[3] == '\0')
		return 1;

	if (cond[0] == 'g' &&
	    cond[1] == 'z' &&
	    cond[2] == '\0')
		return 1;

	return 0;
}

static int is_branch_float_cond(const char *cond)
{
	if (cond[0] == '\0')
		return 1;

	if ((cond[0] == 'a' || cond[0] == 'e' ||
	     cond[0] == 'z' || cond[0] == 'g' ||
	     cond[0] == 'l' || cond[0] == 'n' ||
	     cond[0] == 'o' || cond[0] == 'u') &&
	    cond[1] == '\0')
		return 1;

	if (((cond[0] == 'g' && cond[1] == 'e') ||
	     (cond[0] == 'l' && (cond[1] == 'e' ||
				 cond[1] == 'g')) ||
	     (cond[0] == 'n' && (cond[1] == 'e' ||
				 cond[1] == 'z')) ||
	     (cond[0] == 'u' && (cond[1] == 'e' ||
				 cond[1] == 'g' ||
				 cond[1] == 'l'))) &&
	    cond[2] == '\0')
		return 1;

	if (cond[0] == 'u' &&
	    (cond[1] == 'g' || cond[1] == 'l') &&
	    cond[2] == 'e' &&
	    cond[3] == '\0')
		return 1;

	return 0;
}

static const struct ins_ops *sparc__associate_instruction_ops(struct arch *arch, const char *name)
{
	const struct ins_ops *ops = NULL;

	if (!strcmp(name, "call") ||
	    !strcmp(name, "jmp") ||
	    !strcmp(name, "jmpl")) {
		ops = &call_ops;
	} else if (!strcmp(name, "ret") ||
		   !strcmp(name, "retl") ||
		   !strcmp(name, "return")) {
		ops = &ret_ops;
	} else if (!strcmp(name, "mov")) {
		ops = &mov_ops;
	} else {
		if (name[0] == 'c' &&
		    (name[1] == 'w' || name[1] == 'x'))
			name += 2;

		if (name[0] == 'b') {
			const char *cond = name + 1;

			if (cond[0] == 'r') {
				if (is_branch_reg_cond(cond + 1))
					ops = &jump_ops;
			} else if (is_branch_cond(cond)) {
				ops = &jump_ops;
			}
		} else if (name[0] == 'f' && name[1] == 'b') {
			if (is_branch_float_cond(name + 2))
				ops = &jump_ops;
		}
	}

	if (ops)
		arch__associate_ins_ops(arch, name, ops);

	return ops;
}

const struct arch *arch__new_sparc(const struct e_machine_and_e_flags *id,
				   const char *cpuid __maybe_unused)
{
	struct arch *arch = zalloc(sizeof(*arch));

	if (!arch)
		return NULL;

	arch->name = "sparc";
	arch->id = *id;
	arch->associate_instruction_ops = sparc__associate_instruction_ops;
	arch->objdump.comment_char = '#';
	return arch;
}
