/* $Id$ */
/* Copyright (c) 2011-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Devel Asm */
/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. */



#ifndef DEVEL_ASM_ARCH_H
# define DEVEL_ASM_ARCH_H

# include <sys/types.h>
# include <stdint.h>
# include <System/license.h>
# include "common.h"


/* AsmArch */
/* types */
typedef struct _AsmArch AsmArch;

typedef enum _AsmArchEndian
{
	ASM_ARCH_ENDIAN_BIG	= 0x1,
	ASM_ARCH_ENDIAN_LITTLE	= 0x2,
	ASM_ARCH_ENDIAN_BOTH	= 0x3
} AsmArchEndian;

typedef struct _AsmArchDefinition
{
	char const * format;		/* default format plug-in */
	AsmArchEndian endian;
	uint32_t address_size;		/* in bits */
	uint32_t alignment;		/* in bits */
	uint32_t instruction_size;	/* 0 if not constant */
} AsmArchDefinition;

/* prefixes */
typedef struct _AsmArchPrefix
{
	char const * name;
	uint32_t opcode;
	AsmArchOperandDefinition flags;
} AsmArchPrefix;

/* operands */
typedef enum _AsmArchOperandType
{
	AOT_NONE	= 0x0,
	AOT_CONSTANT	= 0x1,		/* flags |      0 |   size |  value */
	AOT_IMMEDIATE	= 0x2,		/* flags |      0 |   size | refers */
	AOT_REGISTER	= 0x3,		/* flags |      0 |   size |     id */
	AOT_DREGISTER	= 0x4,		/* flags |  dsize |  rsize |     id */
	AOT_DREGISTER2	= 0x5		/* flags |    did |  rsize |     id */
} AsmArchOperandType;

/* displacement */
# define AOD_FLAGS	24
# define AOD_OFFSET	16
# define AOD_SIZE	8
# define AOD_TYPE	28
# define AOD_VALUE	0

/* masks */
# define AOM_TYPE	0xf0000000
# define AOM_FLAGS	0x0f000000
# define AOM_OFFSET	0x00ff0000
# define AOM_SIZE	0x0000ff00
# define AOM_VALUE	0x000000ff

/* flags */
/* constants */
# define AOF_IMPLICIT	0x1
/* for immediate */
# define AOF_SIGNED	0x1
/* for registers */
# define AOF_IMPLICIT	0x1

/* immediate refers */
# define AOI_REFERS_STRING	0x1
# define AOI_REFERS_FUNCTION	0x2

/* macros */
# define AO_0()		AOT_NONE, AOT_NONE, AOT_NONE, AOT_NONE, AOT_NONE
# define AO_1(op1)	op1, AOT_NONE, AOT_NONE, AOT_NONE, AOT_NONE
# define AO_2(op1, op2)	op1, op2, AOT_NONE, AOT_NONE, AOT_NONE
# define AO_3(op1, op2, op3) \
			op1, op2, op3, AOT_NONE, AOT_NONE
# define AO_4(op1, op2, op3, op4) \
			op1, op2, op3, op4, AOT_NONE
# define AO_5(op1, op2, op3, op4, op5) \
			op1, op2, op3, op4, op5
# define AO_GET_FLAGS(operand)	((operand & AOM_FLAGS) >> AOD_FLAGS)
# define AO_GET_OFFSET(operand)	((operand & AOM_OFFSET) >> AOD_OFFSET)
# define AO_GET_DSIZE(operand)	((operand & AOM_OFFSET) >> AOD_OFFSET)
# define AO_GET_RSIZE(operand)	((operand & AOM_SIZE) >> AOD_SIZE)
# define AO_GET_SIZE(operand)	((operand & AOM_SIZE) >> AOD_SIZE)
# define AO_GET_TYPE(operand)	((operand & AOM_TYPE) >> AOD_TYPE)
# define AO_GET_VALUE(operand)	((operand & AOM_VALUE) >> AOD_VALUE)

# define AO_CONSTANT(flags, size, value) \
		((AOT_CONSTANT << AOD_TYPE) \
		 | ((flags) << AOD_FLAGS) \
		 | ((size) << AOD_SIZE) \
		 | ((value) << AOD_VALUE))
# define AO_IMMEDIATE(flags, size, type) \
		((AOT_IMMEDIATE << AOD_TYPE) \
		 | ((flags) << AOD_FLAGS) \
		 | ((size) << AOD_SIZE) \
		 | ((type) << AOD_VALUE))
# define AO_REGISTER(flags, size, id) \
		((AOT_REGISTER << AOD_TYPE) \
		 | ((flags) << AOD_FLAGS) \
		 | ((size) << AOD_SIZE) \
		 | ((id) << AOD_VALUE))
# define AO_DREGISTER(flags, dsize, rsize, id) \
		((AOT_DREGISTER << AOD_TYPE) \
		 | ((flags) << AOD_FLAGS) \
		 | ((dsize) << AOD_OFFSET) \
		 | ((rsize) << AOD_SIZE) \
		 | ((id) << AOD_VALUE))
# define AO_DREGISTER2(flags, did, dsize, id) \
		((AOT_DREGISTER2 << AOD_TYPE) \
		 | ((flags) << AOD_FLAGS) \
		 | ((did) << AOD_OFFSET) \
		 | ((dsize) << AOD_SIZE) \
		 | ((id) << AOD_VALUE))

typedef struct _AsmArchOperand
{
	AsmArchOperandDefinition definition;
	union
	{
		/* AOT_DREGISTER */
		struct
		{
			char const * name;
			int64_t offset;
		} dregister;

		/* AOT_DREGISTER2 */
		struct
		{
			char const * name;
			char const * name2;
		} dregister2;

		/* AOT_IMMEDIATE */
		struct
		{
			char const * name;		/* optional */
			uint64_t value;
			int negative;
		} immediate;

		/* AOT_REGISTER */
		struct
		{
			char const * name;
		} _register;
		/* FIXME complete */
	} value;
} AsmArchOperand;

typedef struct _AsmArchInstructionCall
{
	char const * prefix;
	char const * name;
# define ARO_COUNT	5
	AsmArchOperand operands[ARO_COUNT];
	uint32_t operands_cnt;

	/* meta information */
	off_t base;
	size_t offset;
	size_t size;
} AsmArchInstructionCall;

/* register flags */
# define ARF_ALIAS	0x1

typedef struct _AsmArchRegister
{
	char const * name;
	uint32_t size;
	uint32_t id;
	unsigned int flags;
	char const * description;
} AsmArchRegister;

typedef struct _AsmArchPluginHelper
{
	AsmArch * arch;

	/* callbacks */
	/* accessors */
	char const * (*get_filename)(AsmArch * arch);
	AsmFunction * (*get_function_by_id)(AsmArch * arch, AsmFunctionId id);
	AsmArchPrefix const * (*get_prefix_by_opcode)(AsmArch * arch,
			uint8_t size, uint32_t opcode);
	AsmArchInstruction const * (*get_instruction_by_opcode)(AsmArch * arch,
			uint8_t size, uint32_t opcode);
	AsmArchRegister const * (*get_register_by_id_size)(AsmArch * arch,
			uint32_t id, uint32_t size);
	AsmArchRegister const * (*get_register_by_name_size)(AsmArch * arch,
			char const * name, uint32_t size);
	AsmString * (*get_string_by_id)(AsmArch * arch, AsmStringId id);

	/* assembly */
	ssize_t (*write)(AsmArch * arch, void const * buf, size_t size);

	/* disassembly */
	ssize_t (*peek)(AsmArch * arch, void * buf, size_t size);
	ssize_t (*read)(AsmArch * arch, void * buf, size_t size);
	off_t (*seek)(AsmArch * arch, off_t offset, int whence);
} AsmArchPluginHelper;

typedef struct _AsmArchPlugin AsmArchPlugin;

typedef const struct _AsmArchPluginDefinition
{
	char const * name;
	char const * description;
	LicenseFlags license;

	AsmArchDefinition const * definition;
	AsmArchRegister const * registers;
	AsmArchPrefix const * prefixes;
	AsmArchInstruction const * instructions;

	AsmArchPlugin * (*init)(AsmArchPluginHelper * helper);
	void (*destroy)(AsmArchPlugin * arch);
	int (*encode)(AsmArchPlugin * arch,
			AsmArchPrefix const * prefix,
			AsmArchInstruction const * instruction,
			AsmArchInstructionCall const * call);
	int (*decode)(AsmArchPlugin * arch, AsmArchInstructionCall * call);
} AsmArchPluginDefinition;

#endif /* !DEVEL_ASM_ARCH_H */
