/*
 * arm_codegen.h - Code generation macros for the ARM processor.
 *
 * Copyright (C) 2003, 2004  Southern Storm Software, Pty Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef	_ARM_CODEGEN_H
#define	_ARM_CODEGEN_H

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Register numbers.
 */
typedef enum
{
	ARM_R0   = 0,
	ARM_R1   = 1,
	ARM_R2   = 2,
	ARM_R3   = 3,
	ARM_R4   = 4,
	ARM_R5   = 5,
	ARM_R6   = 6,
	ARM_R7   = 7,
	ARM_R8   = 8,
	ARM_R9   = 9,
	ARM_R10  = 10,
	ARM_R11  = 11,
	ARM_R12  = 12,
	ARM_R13  = 13,
	ARM_R14  = 14,
	ARM_R15  = 15,
	ARM_FP   = ARM_R11,			/* Frame pointer */
	ARM_LINK = ARM_R14,			/* Link register */
	ARM_PC   = ARM_R15,			/* Program counter */
	ARM_WORK = ARM_R12,			/* Work register that we can destroy */
	ARM_SP   = ARM_R13			/* Stack pointer */

} ARM_REG;

/*
 * Floating-point register numbers.
 */
typedef enum
{
	ARM_F0	= 0,
	ARM_F1	= 1,
	ARM_F2	= 2,
	ARM_F3	= 3,
	ARM_F4	= 4,
	ARM_F5	= 5,
	ARM_F6	= 6,
	ARM_F7	= 7

} ARM_FREG;

/*
 * Condition codes.
 */
typedef enum
{
	ARM_CC_EQ    = 0,			/* Equal */
	ARM_CC_NE    = 1,			/* Not equal */
	ARM_CC_CS    = 2,			/* Carry set */
	ARM_CC_CC    = 3,			/* Carry clear */
	ARM_CC_MI    = 4,			/* Negative */
	ARM_CC_PL    = 5,			/* Positive */
	ARM_CC_VS    = 6,			/* Overflow set */
	ARM_CC_VC    = 7,			/* Overflow clear */
	ARM_CC_HI    = 8,			/* Higher */
	ARM_CC_LS    = 9,			/* Lower or same */
	ARM_CC_GE    = 10,			/* Signed greater than or equal */
	ARM_CC_LT    = 11,			/* Signed less than */
	ARM_CC_GT    = 12,			/* Signed greater than */
	ARM_CC_LE    = 13,			/* Signed less than or equal */
	ARM_CC_AL    = 14,			/* Always */
	ARM_CC_NV    = 15,			/* Never */
	ARM_CC_GE_UN = ARM_CC_CS,	/* Unsigned greater than or equal */
	ARM_CC_LT_UN = ARM_CC_CC,	/* Unsigned less than */
	ARM_CC_GT_UN = ARM_CC_HI,	/* Unsigned greater than */
	ARM_CC_LE_UN = ARM_CC_LS	/* Unsigned less than or equal */

} ARM_CC;

/*
 * Arithmetic and logical operations.
 */
typedef enum
{
	ARM_AND = 0,				/* Bitwise AND */
	ARM_EOR = 1,				/* Bitwise XOR */
	ARM_SUB = 2,				/* Subtract */
	ARM_RSB = 3,				/* Reverse subtract */
	ARM_ADD = 4,				/* Add */
	ARM_ADC = 5,				/* Add with carry */
	ARM_SBC = 6,				/* Subtract with carry */
	ARM_RSC = 7,				/* Reverse subtract with carry */
	ARM_TST = 8,				/* Test with AND */
	ARM_TEQ = 9,				/* Test with XOR */
	ARM_CMP = 10,				/* Test with SUB (compare) */
	ARM_CMN = 11,				/* Test with ADD */
	ARM_ORR = 12,				/* Bitwise OR */
	ARM_MOV = 13,				/* Move */
	ARM_BIC = 14,				/* Test with Op1 & ~Op2 */
	ARM_MVN = 15				/* Bitwise NOT */

} ARM_OP;

/*
 * Shift operators.
 */
typedef enum
{
	ARM_SHL = 0,				/* Logical left */
	ARM_SHR = 1,				/* Logical right */
	ARM_SAR = 2,				/* Arithmetic right */
	ARM_ROR = 3					/* Rotate right */

} ARM_SHIFT;

/*
 * Floating-point unary operators.
 */
typedef enum
{
	ARM_MVF		= 0,			/* Move */
	ARM_MNF		= 1,			/* Move negative */
	ARM_ABS		= 2,			/* Absolute value */
	ARM_RND		= 3,			/* Round */
	ARM_SQT		= 4,			/* Square root */
	ARM_LOG		= 5,			/* log10 */
	ARM_LGN		= 6,			/* ln */
	ARM_EXP		= 7,			/* exp */
	ARM_SIN		= 8,			/* sin */
	ARM_COS		= 9,			/* cos */
	ARM_TAN		= 10,			/* tan */
	ARM_ASN		= 11,			/* asin */
	ARM_ACS		= 12,			/* acos */
	ARM_ATN		= 13			/* atan */

} ARM_FUNARY;

/*
 * Floating-point binary operators.
 */
typedef enum
{
	ARM_ADF		= 0,			/* Add */
	ARM_MUF		= 1,			/* Multiply */
	ARM_SUF		= 2,			/* Subtract */
	ARM_RSF		= 3,			/* Reverse subtract */
	ARM_DVF		= 4,			/* Divide */
	ARM_RDF		= 5,			/* Reverse divide */
	ARM_POW		= 6,			/* pow */
	ARM_RPW		= 7,			/* Reverse pow */
	ARM_RMF		= 8,			/* Remainder */
	ARM_FML		= 9,			/* Fast multiply (32-bit only) */
	ARM_FDV		= 10,			/* Fast divide (32-bit only) */
	ARM_FRD		= 11,			/* Fast reverse divide (32-bit only) */
	ARM_POL		= 12			/* Polar angle */

} ARM_FBINARY;

/*
 * Number of registers that are used for parameters (r0-r3).
 */
#define	ARM_NUM_PARAM_REGS	4

/*
 * Type that keeps track of the instruction buffer.
 */
typedef unsigned int arm_inst_word;
typedef struct
{
	arm_inst_word *current;
	arm_inst_word *limit;

} arm_inst_buf;
#define	arm_inst_get_posn(inst)		((inst).current)
#define	arm_inst_get_limit(inst)	((inst).limit)

/*
 * Build an instruction prefix from a condition code and a mask value.
 */
#define	arm_build_prefix(cond,mask)	\
			((((unsigned int)(cond)) << 28) | ((unsigned int)(mask)))

/*
 * Build an "always" instruction prefix for a regular instruction.
 */
#define arm_prefix(mask)	(arm_build_prefix(ARM_CC_AL, (mask)))

/*
 * Build special "always" prefixes.
 */
#define	arm_always			(arm_build_prefix(ARM_CC_AL, 0))
#define	arm_always_cc		(arm_build_prefix(ARM_CC_AL, (1 << 20)))
#define	arm_always_imm		(arm_build_prefix(ARM_CC_AL, (1 << 25)))

/*
 * Wrappers for "arm_always*" that allow higher-level routines
 * to change code generation to be based on a condition.  This is
 * used to perform branch elimination.
 */
#ifndef arm_execute
#define	arm_execute			arm_always
#define	arm_execute_cc		arm_always_cc
#define	arm_execute_imm		arm_always_imm
#endif

/*
 * Initialize an instruction buffer.
 */
#define	arm_inst_buf_init(inst,start,end)	\
			do { \
				(inst).current = (arm_inst_word *)(start); \
				(inst).limit = (arm_inst_word *)(end); \
			} while (0)

/*
 * Add an instruction to an instruction buffer.
 */
#define	arm_inst_add(inst,value)	\
			do { \
				if((inst).current < (inst).limit) \
				{ \
					*((inst).current)++ = (value); \
				} \
			} while (0)

/*
 * Arithmetic or logical operation which doesn't set condition codes.
 */
#define	arm_alu_reg_reg(inst,opc,dreg,sreg1,sreg2)	\
			do { \
				arm_inst_add((inst), arm_execute | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg1)) << 16) | \
							 ((unsigned int)(sreg2))); \
			} while (0)
#define	arm_alu_reg_imm8(inst,opc,dreg,sreg,imm)	\
			do { \
				arm_inst_add((inst), arm_execute_imm | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg)) << 16) | \
							 ((unsigned int)((imm) & 0xFF))); \
			} while (0)
#define	arm_alu_reg_imm8_cond(inst,opc,dreg,sreg,imm,cond)	\
			do { \
				arm_inst_add((inst), arm_build_prefix((cond), (1 << 25)) | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg)) << 16) | \
							 ((unsigned int)((imm) & 0xFF))); \
			} while (0)
#define	arm_alu_reg_imm8_rotate(inst,opc,dreg,sreg,imm,rotate)	\
			do { \
				arm_inst_add((inst), arm_execute_imm | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg)) << 16) | \
							(((unsigned int)(rotate)) << 8) | \
							 ((unsigned int)((imm) & 0xFF))); \
			} while (0)
extern void _arm_alu_reg_imm
		(arm_inst_buf *inst, int opc, int dreg,
		 int sreg, int imm, int saveWork, int execute_prefix);
#define	arm_alu_reg_imm(inst,opc,dreg,sreg,imm)	\
			do { \
				int __alu_imm = (int)(imm); \
				if(__alu_imm >= 0 && __alu_imm < 256) \
				{ \
					arm_alu_reg_imm8 \
						((inst), (opc), (dreg), (sreg), __alu_imm); \
				} \
				else \
				{ \
					_arm_alu_reg_imm \
						(&(inst), (opc), (dreg), (sreg), __alu_imm, 0, \
						 arm_execute); \
				} \
			} while (0)
#define	arm_alu_reg_imm_save_work(inst,opc,dreg,sreg,imm)	\
			do { \
				int __alu_imm_save = (int)(imm); \
				if(__alu_imm_save >= 0 && __alu_imm_save < 256) \
				{ \
					arm_alu_reg_imm8 \
						((inst), (opc), (dreg), (sreg), __alu_imm_save); \
				} \
				else \
				{ \
					_arm_alu_reg_imm \
						(&(inst), (opc), (dreg), (sreg), __alu_imm_save, 1, \
						 arm_execute); \
				} \
			} while (0)
#define arm_alu_reg(inst,opc,dreg,sreg)	\
			do { \
				arm_inst_add((inst), arm_execute | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							 ((unsigned int)(sreg))); \
			} while (0)
#define arm_alu_reg_cond(inst,opc,dreg,sreg,cond)	\
			do { \
				arm_inst_add((inst), arm_build_prefix((cond), 0) | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							 ((unsigned int)(sreg))); \
			} while (0)

/*
 * Arithmetic or logical operation which sets condition codes.
 */
#define	arm_alu_cc_reg_reg(inst,opc,dreg,sreg1,sreg2)	\
			do { \
				arm_inst_add((inst), arm_execute_cc | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg1)) << 16) | \
							 ((unsigned int)(sreg2))); \
			} while (0)
#define	arm_alu_cc_reg_imm8(inst,opc,dreg,sreg,imm)	\
			do { \
				arm_inst_add((inst), arm_execute_imm | arm_execute_cc | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg)) << 16) | \
							 ((unsigned int)((imm) & 0xFF))); \
			} while (0)
#define arm_alu_cc_reg(inst,opc,dreg,sreg)	\
			do { \
				arm_inst_add((inst), = arm_execute_cc | \
							(((unsigned int)(opc)) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							 ((unsigned int)(sreg))); \
			} while (0)

/*
 * Test operation, which sets the condition codes but has no other result.
 */
#define arm_test_reg_reg(inst,opc,sreg1,sreg2)	\
			do { \
				arm_alu_cc_reg_reg((inst), (opc), 0, (sreg1), (sreg2)); \
			} while (0)
#define arm_test_reg_imm8(inst,opc,sreg,imm)	\
			do { \
				arm_alu_cc_reg_imm8((inst), (opc), 0, (sreg), (imm)); \
			} while (0)
#define arm_test_reg_imm(inst,opc,sreg,imm)	\
			do { \
				int __test_imm = (int)(imm); \
				if(__test_imm >= 0 && __test_imm < 256) \
				{ \
					arm_alu_cc_reg_imm8((inst), (opc), 0, (sreg), __test_imm); \
				} \
				else \
				{ \
					arm_mov_reg_imm((inst), ARM_WORK, __test_imm); \
					arm_test_reg_reg((inst), (opc), (sreg), ARM_WORK); \
				} \
			} while (0)

/*
 * Move a value between registers.
 */
#define	arm_mov_reg_reg(inst,dreg,sreg)	\
			do { \
				arm_alu_reg((inst), ARM_MOV, (dreg), (sreg)); \
			} while (0)

/*
 * Move an immediate value into a register.  This is hard because
 * ARM lacks an instruction to load a 32-bit immediate value directly.
 * We handle the simple cases and then bail out to a function for the rest.
 */
#define	arm_mov_reg_imm8(inst,reg,imm)	\
			do { \
				arm_alu_reg_imm8((inst), ARM_MOV, (reg), 0, (imm)); \
			} while (0)
#define	arm_mov_reg_imm8_rotate(inst,reg,imm,rotate)	\
			do { \
				arm_alu_reg_imm8_rotate((inst), ARM_MOV, (reg), \
										0, (imm), (rotate)); \
			} while (0)
extern void _arm_mov_reg_imm
	(arm_inst_buf *inst, int reg, int value, int execute_prefix);
extern int arm_is_complex_imm(int value);
#define	arm_mov_reg_imm(inst,reg,imm)	\
			do { \
				int __imm = (int)(imm); \
				if(__imm >= 0 && __imm < 256) \
				{ \
					arm_mov_reg_imm8((inst), (reg), __imm); \
				} \
				else if((reg) == ARM_PC) \
				{ \
					_arm_mov_reg_imm \
						(&(inst), ARM_WORK, __imm, arm_execute); \
					arm_mov_reg_reg((inst), ARM_PC, ARM_WORK); \
				} \
				else if(__imm > -256 && __imm < 0) \
				{ \
					arm_mov_reg_imm8((inst), (reg), ~(__imm)); \
					arm_alu_reg((inst), ARM_MVN, (reg), (reg)); \
				} \
				else \
				{ \
					_arm_mov_reg_imm(&(inst), (reg), __imm, arm_execute); \
				} \
			} while (0)

/*
 * Clear a register to zero.
 */
#define	arm_clear_reg(inst,reg)	\
			do { \
				arm_mov_reg_imm8((inst), (reg), 0); \
			} while (0)

/*
 * No-operation instruction.
 */
#define	arm_nop(inst)	arm_mov_reg_reg((inst), ARM_R0, ARM_R0)

/*
 * Perform a shift operation.
 */
#define	arm_shift_reg_reg(inst,opc,dreg,sreg1,sreg2) \
			do { \
				arm_inst_add((inst), arm_execute | \
							(((unsigned int)ARM_MOV) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg2)) << 8) | \
							(((unsigned int)(opc)) << 5) | \
							 ((unsigned int)(1 << 4)) | \
							 ((unsigned int)(sreg1))); \
			} while (0)
#define	arm_shift_reg_imm8(inst,opc,dreg,sreg,imm) \
			do { \
				arm_inst_add((inst), arm_execute | \
							(((unsigned int)ARM_MOV) << 21) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(opc)) << 5) | \
							(((unsigned int)(imm)) << 7) | \
							 ((unsigned int)(sreg))); \
			} while (0)

/*
 * Perform a multiplication instruction.  Note: ARM instruction rules
 * say that dreg should not be the same as sreg2, so we swap the order
 * of the arguments if that situation occurs.  We assume that sreg1
 * and sreg2 are distinct registers.
 */
#define arm_mul_reg_reg(inst,dreg,sreg1,sreg2)	\
			do { \
				if((dreg) != (sreg2)) \
				{ \
					arm_inst_add((inst), arm_prefix(0x00000090) | \
								(((unsigned int)(dreg)) << 16) | \
								(((unsigned int)(sreg1)) << 8) | \
								 ((unsigned int)(sreg2))); \
				} \
				else \
				{ \
					arm_inst_add((inst), arm_prefix(0x00000090) | \
								(((unsigned int)(dreg)) << 16) | \
								(((unsigned int)(sreg2)) << 8) | \
								 ((unsigned int)(sreg1))); \
				} \
			} while (0)

/*
 * Perform a binary operation on floating-point arguments.
 */
#define	arm_alu_freg_freg(inst,opc,dreg,sreg1,sreg2)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x0E000180) | \
							(((unsigned int)(opc)) << 20) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg1)) << 16) | \
							 ((unsigned int)(sreg2))); \
			} while (0)
#define	arm_alu_freg_freg_32(inst,opc,dreg,sreg1,sreg2)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x0E000100) | \
							(((unsigned int)(opc)) << 20) | \
							(((unsigned int)(dreg)) << 12) | \
							(((unsigned int)(sreg1)) << 16) | \
							 ((unsigned int)(sreg2))); \
			} while (0)

/*
 * Perform a unary operation on floating-point arguments.
 */
#define	arm_alu_freg(inst,opc,dreg,sreg)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x0E008180) | \
							(((unsigned int)(opc)) << 20) | \
							(((unsigned int)(dreg)) << 12) | \
							 ((unsigned int)(sreg))); \
			} while (0)
#define	arm_alu_freg_32(inst,opc,dreg,sreg)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x0E008100) | \
							(((unsigned int)(opc)) << 20) | \
							(((unsigned int)(dreg)) << 12) | \
							 ((unsigned int)(sreg))); \
			} while (0)

/*
 * Branch or jump immediate by a byte offset.  The offset is
 * assumed to be +/- 32 Mbytes.
 */
#define	arm_branch_imm(inst,cond,imm)	\
			do { \
				arm_inst_add((inst), arm_build_prefix((cond), 0x0A000000) | \
							(((unsigned int)(((int)(imm)) >> 2)) & \
								0x00FFFFFF)); \
			} while (0)
#define	arm_jump_imm(inst,imm)	arm_branch_imm((inst), ARM_CC_AL, (imm))

/*
 * Branch or jump to a specific target location.  The offset is
 * assumed to be +/- 32 Mbytes.
 */
#define	arm_branch(inst,cond,target)	\
			do { \
				int __br_offset = (int)(((unsigned char *)(target)) - \
					           (((unsigned char *)((inst).current)) + 8)); \
				arm_branch_imm((inst), (cond), __br_offset); \
			} while (0)
#define	arm_jump(inst,target)	arm_branch((inst), ARM_CC_AL, (target))

/*
 * Jump to a specific target location that may be greater than
 * 32 Mbytes away from the current location.
 */
#define	arm_jump_long(inst,target)	\
			do { \
				int __jmp_offset = (int)(((unsigned char *)(target)) - \
					            (((unsigned char *)((inst).current)) + 8)); \
				if(__jmp_offset >= -0x04000000 && __jmp_offset < 0x04000000) \
				{ \
					arm_jump_imm((inst), __jmp_offset); \
				} \
				else \
				{ \
					arm_mov_reg_imm((inst), ARM_PC, (int)(target)); \
				} \
			} while (0)

/*
 * Back-patch a branch instruction.
 */
#define	arm_patch(inst,posn,target)	\
			do { \
				int __p_offset = (int)(((unsigned char *)(target)) - \
							          (((unsigned char *)(posn)) + 8)); \
				__p_offset = (__p_offset >> 2) & 0x00FFFFFF; \
				if(((arm_inst_word *)(posn)) < (inst).limit) \
				{ \
					*((int *)(posn)) = (*((int *)(posn)) & 0xFF000000) | \
						__p_offset; \
				} \
			} while (0)

/*
 * Call a subroutine immediate by a byte offset.
 */
#define	arm_call_imm(inst,imm)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x0B000000) | \
							(((unsigned int)(((int)(imm)) >> 2)) & \
								0x00FFFFFF)); \
			} while (0)

/*
 * Call a subroutine at a specific target location.
 */
#define	arm_call(inst,target)	\
			do { \
				int __call_offset = (int)(((unsigned char *)(target)) - \
					             (((unsigned char *)((inst).current)) + 8)); \
				if(__call_offset >= -0x04000000 && __call_offset < 0x04000000) \
				{ \
					arm_call_imm((inst), __call_offset); \
				} \
				else \
				{ \
					arm_load_membase((inst), ARM_WORK, ARM_PC, 4); \
					arm_alu_reg_imm8((inst), ARM_ADD, ARM_LINK, ARM_PC, 4); \
					arm_mov_reg_reg((inst), ARM_PC, ARM_WORK); \
					arm_inst_add((inst), (int)(target)); \
				} \
			} while (0)

/*
 * Return from a subroutine, where the return address is in the link register.
 */
#define	arm_return(inst)	\
			do { \
				arm_mov_reg_reg((inst), ARM_PC, ARM_LINK); \
			} while (0)

/*
 * Push a register onto the system stack.
 */
#define	arm_push_reg(inst,reg)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x05200004) | \
							(((unsigned int)ARM_SP) << 16) | \
							(((unsigned int)(reg)) << 12)); \
			} while (0)

/*
 * Pop a register from the system stack.
 */
#define	arm_pop_reg(inst,reg)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x04900004) | \
							(((unsigned int)ARM_SP) << 16) | \
							(((unsigned int)(reg)) << 12)); \
			} while (0)

/*
 * Set up a local variable frame, and save the registers in "regset".
 */
#define	arm_setup_frame(inst,regset)	\
			do { \
				arm_mov_reg_reg((inst), ARM_WORK, ARM_SP); \
				arm_inst_add((inst), arm_prefix(0x0920D800) | \
							(((unsigned int)ARM_SP) << 16) | \
							(((unsigned int)(regset)))); \
				arm_alu_reg_imm8((inst), ARM_SUB, ARM_FP, ARM_WORK, 4); \
			} while (0)

/*
 * Pop a local variable frame, restore the registers in "regset",
 * and return to the caller.
 */
#define	arm_pop_frame(inst,regset)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x0910A800) | \
							(((unsigned int)ARM_FP) << 16) | \
							(((unsigned int)(regset)))); \
			} while (0)

/*
 * Pop a local variable frame, in preparation for a tail call.
 * This restores "lr" to its original value, but does not set "pc".
 */
#define	arm_pop_frame_tail(inst,regset)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x09106800) | \
							(((unsigned int)ARM_FP) << 16) | \
							(((unsigned int)(regset)))); \
			} while (0)

/*
 * Load a word value from a pointer and then advance the pointer.
 */
#define	arm_load_advance(inst,dreg,sreg)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x04900004) | \
							(((unsigned int)(sreg)) << 16) | \
							(((unsigned int)(dreg)) << 12)); \
			} while (0)

/*
 * Load a value from an address into a register.
 */
#define arm_load_membase_either(inst,reg,basereg,imm,mask)	\
			do { \
				int __mb_offset = (int)(imm); \
				if(__mb_offset >= 0 && __mb_offset < (1 << 12)) \
				{ \
					arm_inst_add((inst), arm_prefix(0x05900000 | (mask)) | \
								(((unsigned int)(basereg)) << 16) | \
								(((unsigned int)(reg)) << 12) | \
								 ((unsigned int)__mb_offset)); \
				} \
				else if(__mb_offset > -(1 << 12) && __mb_offset < 0) \
				{ \
					arm_inst_add((inst), arm_prefix(0x05100000 | (mask)) | \
								(((unsigned int)(basereg)) << 16) | \
								(((unsigned int)(reg)) << 12) | \
								 ((unsigned int)(-__mb_offset))); \
				} \
				else \
				{ \
					arm_mov_reg_imm((inst), ARM_WORK, __mb_offset); \
					arm_inst_add((inst), arm_prefix(0x07900000 | (mask)) | \
								(((unsigned int)(basereg)) << 16) | \
								(((unsigned int)(reg)) << 12) | \
								 ((unsigned int)ARM_WORK)); \
				} \
			} while (0)
#define	arm_load_membase(inst,reg,basereg,imm)	\
			do { \
				arm_load_membase_either((inst), (reg), (basereg), (imm), 0); \
			} while (0)
#define	arm_load_membase_byte(inst,reg,basereg,imm)	\
			do { \
				arm_load_membase_either((inst), (reg), (basereg), (imm), \
										0x00400000); \
			} while (0)
#define	arm_load_membase_sbyte(inst,reg,basereg,imm)	\
			do { \
				arm_load_membase_either((inst), (reg), (basereg), (imm), \
										0x00400000); \
				arm_shift_reg_imm8((inst), ARM_SHL, (reg), (reg), 24); \
				arm_shift_reg_imm8((inst), ARM_SAR, (reg), (reg), 24); \
			} while (0)
#define	arm_load_membase_ushort(inst,reg,basereg,imm)	\
			do { \
				arm_load_membase_byte((inst), ARM_WORK, (basereg), (imm)); \
				arm_load_membase_byte((inst), (reg), (basereg), (imm) + 1); \
				arm_shift_reg_imm8((inst), ARM_SHL, (reg), (reg), 8); \
				arm_alu_reg_reg((inst), ARM_ORR, (reg), (reg), ARM_WORK); \
			} while (0)
#define	arm_load_membase_short(inst,reg,basereg,imm)	\
			do { \
				arm_load_membase_byte((inst), ARM_WORK, (basereg), (imm)); \
				arm_load_membase_byte((inst), (reg), (basereg), (imm) + 1); \
				arm_shift_reg_imm8((inst), ARM_SHL, (reg), (reg), 24); \
				arm_shift_reg_imm8((inst), ARM_SAR, (reg), (reg), 16); \
				arm_alu_reg_reg((inst), ARM_ORR, (reg), (reg), ARM_WORK); \
			} while (0)

/*
 * Load a floating-point value from an address into a register.
 */
#define	arm_load_membase_float(inst,reg,basereg,imm,mask)	\
			do { \
				int __mb_offset = (int)(imm); \
				if(__mb_offset >= 0 && __mb_offset < (1 << 10) && \
				   (__mb_offset & 3) == 0) \
				{ \
					arm_inst_add((inst), arm_prefix(0x0D900100 | (mask)) | \
							(((unsigned int)(basereg)) << 16) | \
							(((unsigned int)(reg)) << 12) | \
							 ((unsigned int)((__mb_offset / 4) & 0xFF))); \
				} \
				else if(__mb_offset > -(1 << 10) && __mb_offset < 0 && \
				        (__mb_offset & 3) == 0) \
				{ \
					arm_inst_add((inst), arm_prefix(0x0D180100 | (mask)) | \
							(((unsigned int)(basereg)) << 16) | \
							(((unsigned int)(reg)) << 12) | \
							 ((unsigned int)(((-__mb_offset) / 4) & 0xFF)));\
				} \
				else \
				{ \
					arm_mov_reg_imm((inst), ARM_WORK, __mb_offset); \
					arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, \
								    (basereg), ARM_WORK); \
					arm_inst_add((inst), arm_prefix(0x0D900100 | (mask)) | \
							(((unsigned int)ARM_WORK) << 16) | \
							(((unsigned int)(reg)) << 12)); \
				} \
			} while (0)
#define	arm_load_membase_float32(inst,reg,basereg,imm)	\
			do { \
				arm_load_membase_float((inst), (reg), (basereg), (imm), 0); \
			} while (0)
#define	arm_load_membase_float64(inst,reg,basereg,imm)	\
			do { \
				arm_load_membase_float((inst), (reg), (basereg), \
									   (imm), 0x00008000); \
			} while (0)

/*
 * Store a value from a register into an address.
 *
 * Note: storing a 16-bit value destroys the value in the register.
 */
#define arm_store_membase_either(inst,reg,basereg,imm,mask)	\
			do { \
				int __sm_offset = (int)(imm); \
				if(__sm_offset >= 0 && __sm_offset < (1 << 12)) \
				{ \
					arm_inst_add((inst), arm_prefix(0x05800000 | (mask)) | \
								(((unsigned int)(basereg)) << 16) | \
								(((unsigned int)(reg)) << 12) | \
								 ((unsigned int)__sm_offset)); \
				} \
				else if(__sm_offset > -(1 << 12) && __sm_offset < 0) \
				{ \
					arm_inst_add((inst), arm_prefix(0x05000000 | (mask)) | \
								(((unsigned int)(basereg)) << 16) | \
								(((unsigned int)(reg)) << 12) | \
								 ((unsigned int)(-__sm_offset))); \
				} \
				else \
				{ \
					arm_mov_reg_imm((inst), ARM_WORK, __sm_offset); \
					arm_inst_add((inst), arm_prefix(0x07800000 | (mask)) | \
								(((unsigned int)(basereg)) << 16) | \
								(((unsigned int)(reg)) << 12) | \
								 ((unsigned int)ARM_WORK)); \
				} \
			} while (0)
#define	arm_store_membase(inst,reg,basereg,imm)	\
			do { \
				arm_store_membase_either((inst), (reg), (basereg), (imm), 0); \
			} while (0)
#define	arm_store_membase_byte(inst,reg,basereg,imm)	\
			do { \
				arm_store_membase_either((inst), (reg), (basereg), (imm), \
										 0x00400000); \
			} while (0)
#define	arm_store_membase_sbyte(inst,reg,basereg,imm)	\
			do { \
				arm_store_membase_byte((inst), (reg), (basereg), (imm)); \
			} while (0)
#define	arm_store_membase_short(inst,reg,basereg,imm)	\
			do { \
				arm_store_membase_either((inst), (reg), (basereg), (imm), \
										 0x00400000); \
				arm_shift_reg_imm8((inst), ARM_SHR, (reg), (reg), 8); \
				arm_store_membase_either((inst), (reg), (basereg), \
										 (imm) + 1, 0x00400000); \
			} while (0)
#define	arm_store_membase_ushort(inst,reg,basereg,imm)	\
			do { \
				arm_store_membase_short((inst), (reg), (basereg), (imm)); \
			} while (0)

/*
 * Store a floating-point value to a memory address.
 */
#define	arm_store_membase_float(inst,reg,basereg,imm,mask)	\
			do { \
				int __mb_offset = (int)(imm); \
				if(__mb_offset >= 0 && __mb_offset < (1 << 10) && \
				   (__mb_offset & 3) == 0) \
				{ \
					arm_inst_add((inst), arm_prefix(0x0D800100 | (mask)) | \
							(((unsigned int)(basereg)) << 16) | \
							(((unsigned int)(reg)) << 12) | \
							 ((unsigned int)((__mb_offset / 4) & 0xFF))); \
				} \
				else if(__mb_offset > -(1 << 10) && __mb_offset < 0 && \
				        (__mb_offset & 3) == 0) \
				{ \
					arm_inst_add((inst), arm_prefix(0x0D080100 | (mask)) | \
							(((unsigned int)(basereg)) << 16) | \
							(((unsigned int)(reg)) << 12) | \
							 ((unsigned int)(((-__mb_offset) / 4) & 0xFF)));\
				} \
				else \
				{ \
					arm_mov_reg_imm((inst), ARM_WORK, __mb_offset); \
					arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, \
								    (basereg), ARM_WORK); \
					arm_inst_add((inst), arm_prefix(0x0D800100 | (mask)) | \
							(((unsigned int)ARM_WORK) << 16) | \
							(((unsigned int)(reg)) << 12)); \
				} \
			} while (0)
#define	arm_store_membase_float32(inst,reg,basereg,imm)	\
			do { \
				arm_store_membase_float((inst), (reg), (basereg), (imm), 0); \
			} while (0)
#define	arm_store_membase_float64(inst,reg,basereg,imm)	\
			do { \
				arm_store_membase_float((inst), (reg), (basereg), \
									    (imm), 0x00008000); \
			} while (0)
#define	arm_push_reg_float32(inst,reg)	\
			do { \
				arm_store_membase_float((inst), (reg), ARM_SP, \
									    -4, 0x00200000); \
			} while (0)
#define	arm_push_reg_float64(inst,reg)	\
			do { \
				arm_store_membase_float((inst), (reg), ARM_SP, \
									    -4, 0x00208000); \
			} while (0)

/*
 * Load a value from an indexed address into a register.
 */
#define arm_load_memindex_either(inst,reg,basereg,indexreg,shift,mask)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x07900000 | (mask)) | \
							(((unsigned int)(basereg)) << 16) | \
							(((unsigned int)(reg)) << 12) | \
							(((unsigned int)(shift)) << 7) | \
							 ((unsigned int)(indexreg))); \
			} while (0)
#define	arm_load_memindex(inst,reg,basereg,indexreg)	\
			do { \
				arm_load_memindex_either((inst), (reg), (basereg), \
										 (indexreg), 2, 0); \
			} while (0)
#define	arm_load_memindex_byte(inst,reg,basereg,indexreg)	\
			do { \
				arm_load_memindex_either((inst), (reg), (basereg), \
									     (indexreg), 0, 0x00400000); \
			} while (0)
#define	arm_load_memindex_sbyte(inst,reg,basereg,indexreg)	\
			do { \
				arm_load_memindex_either((inst), (reg), (basereg), \
									     (indexreg), 0, 0x00400000); \
				arm_shift_reg_imm8((inst), ARM_SHL, (reg), (reg), 24); \
				arm_shift_reg_imm8((inst), ARM_SAR, (reg), (reg), 24); \
			} while (0)
#define	arm_load_memindex_ushort(inst,reg,basereg,indexreg)	\
			do { \
				arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, (basereg), \
								(indexreg)); \
				arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, ARM_WORK, \
								(indexreg)); \
				arm_load_membase_byte((inst), (reg), ARM_WORK, 0); \
				arm_load_membase_byte((inst), ARM_WORK, ARM_WORK, 1); \
				arm_shift_reg_imm8((inst), ARM_SHL, ARM_WORK, ARM_WORK, 8); \
				arm_alu_reg_reg((inst), ARM_ORR, (reg), (reg), ARM_WORK); \
			} while (0)
#define	arm_load_memindex_short(inst,reg,basereg,indexreg)	\
			do { \
				arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, (basereg), \
								(indexreg)); \
				arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, ARM_WORK, \
								(indexreg)); \
				arm_load_membase_byte((inst), (reg), ARM_WORK, 0); \
				arm_load_membase_byte((inst), ARM_WORK, ARM_WORK, 1); \
				arm_shift_reg_imm8((inst), ARM_SHL, ARM_WORK, ARM_WORK, 24); \
				arm_shift_reg_imm8((inst), ARM_SAR, ARM_WORK, ARM_WORK, 16); \
				arm_alu_reg_reg((inst), ARM_ORR, (reg), (reg), ARM_WORK); \
			} while (0)

/*
 * Store a value from a register into an indexed address.
 *
 * Note: storing a 16-bit value destroys the values in the base
 * register and the source register.
 */
#define arm_store_memindex_either(inst,reg,basereg,indexreg,shift,mask)	\
			do { \
				arm_inst_add((inst), arm_prefix(0x07800000 | (mask)) | \
							(((unsigned int)(basereg)) << 16) | \
							(((unsigned int)(reg)) << 12) | \
							(((unsigned int)(shift)) << 7) | \
							 ((unsigned int)(indexreg))); \
			} while (0)
#define	arm_store_memindex(inst,reg,basereg,indexreg)	\
			do { \
				arm_store_memindex_either((inst), (reg), (basereg), \
										  (indexreg), 2, 0); \
			} while (0)
#define	arm_store_memindex_byte(inst,reg,basereg,indexreg)	\
			do { \
				arm_store_memindex_either((inst), (reg), (basereg), \
										  (indexreg), 0, 0x00400000); \
			} while (0)
#define	arm_store_memindex_sbyte(inst,reg,basereg,indexreg)	\
			do { \
				arm_store_memindex_byte((inst), (reg), (basereg), \
										(indexreg)); \
			} while (0)
#define	arm_store_memindex_short(inst,reg,basereg,indexreg)	\
			do { \
				arm_store_memindex_either((inst), (reg), (basereg), \
										  (indexreg), 1, 0x00400000); \
				arm_alu_reg_imm8((inst), ARM_ADD, (basereg), (basereg), 1); \
				arm_shift_reg_imm8((inst), ARM_SHR, (reg), (reg), 8); \
				arm_store_memindex_either((inst), (reg), (basereg), \
										  (indexreg), 1, 0x00400000); \
			} while (0)
#define	arm_store_memindex_ushort(inst,reg,basereg,indexreg)	\
			do { \
				arm_store_memindex_short((inst), (reg), \
										 (basereg), (indexreg)); \
			} while (0)

#ifdef __cplusplus
};
#endif

#endif /* _ARM_CODEGEN_H */


syntax highlighted by Code2HTML, v. 0.9.1