/****************************************************************
 *								*
 *	Copyright 2001, 2012 Fidelity Information Services, Inc	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#ifndef __STACK_FRAME_H__
#define __STACK_FRAME_H__

/* stack_frame.h - GT.M MUMPS stack frame */

/* There are several references to this structure from assembly language; these include:
 *
 * From VMS:	G_MSF.MAX
 * From Unix:	g_msf.si
 * From z/OS:	'gtc.xxxxxxxx.maclib(GTMSFRME)'
 * 		'gtc.xxxxxxxx.maclib(G$MSF)'
 *
 * Any changes to the stack frame must be reflected in those files as well.
 *
 * Warning: the lists above may not be complete.
*/

#include "hashtab_mname.h"

typedef struct saved_for_indx_array_struct
{
	mval	*saved_for_indx[1];
} saved_for_indx;

typedef struct stack_frame_struct	/* contents of the GT.M MUMPS stack frame */
{
	struct rhead_struct *rvector;	/* routine header */
#if	defined(__MVS__)
        unsigned char   *mumsf_r5;      /* register 5  */
        unsigned char   *mumsf_r6;      /* register 6  */
	unsigned char	*mpc;		/* mumps program counter */
        unsigned char   *mumsf_r8;      /* register 8  */
        unsigned char   *mumsf_r9;      /* register 9  */
	ht_ent_mname	**l_symtab;	/* local symbol table */
	unsigned char	*temps_ptr;	/* pointer to base of temps    */
	unsigned char	*ctxt;		/* context pointer (base register for use when there's no PC-relative address mode) */
	int4		*literal_ptr;	/* pointer to base of literals */
        unsigned char   *mumsf_r14;     /* register 14 */
        unsigned char   *mumsf_r15;     /* register 15 */
        unsigned char   *mumsf_r0;      /* register 0  */
        unsigned char   *mumsf_r1;      /* register 1  */
        unsigned char   *calldm_base;   /* register 2  */
        unsigned char   *mumsf_r3;      /* register 3  */
#else
	ht_ent_mname	**l_symtab;	/* local symbol table */
	unsigned char	*mpc;		/* mumps program counter */
	unsigned char	*ctxt;		/* context pointer (base register for use when there's no PC-relative address mode) */
#ifdef	HAS_LITERAL_SECT
	int4		*literal_ptr;	/* pointer to base of literals */
#endif
	unsigned char	*temps_ptr;	/* pointer to base of temps */
#endif
	char		*vartab_ptr;	/* variable table may be in rvector or on stack */
	GTM64_ONLY(struct stack_frame_struct *old_frame_pointer;)	/* Moved old_frame_pointer near all pointers
									 * for alignment and smaller stackframe	size
									 */
	short		vartab_len;	/* variable table length */
	short           temp_mvals;     /* temp mval count if this frame for an indirect rtn (ihdtyp) */
	NON_GTM64_ONLY(struct stack_frame_struct *old_frame_pointer;)	/* old_frame_pointer remains at the same
									 * place for 32-bit platforms
									 */
	/* note the alignment of these next two fields is critical to correct operation of
	 * opp_ret on the VMS platforms. Any changes here need to be reflected there.
	 */
	unsigned short	type;
	unsigned char	flags;
	bool		dollar_test;
	saved_for_indx	*for_ctrl_stack;	/* anchor for array of FOR control variable indices */
	mval		*ret_value;
} stack_frame;

/* Stack frame types */
#define SFT_COUNT	(1 << 0)	/* 0x0001 frame counts (real code) or doesn't (transcendental code) */
#define SFT_DM		(1 << 1)	/* 0x0002 direct mode */
#define SFT_REP_OP	(1 << 2)	/* 0x0004 frame to replace opcode (zbreak has already occured) */
#define SFT_ZBRK_ACT	(1 << 3)	/* 0x0008 action frame for zbreak */
#define SFT_DEV_ACT	(1 << 4)	/* 0x0010 action frame for device error handler */
#define SFT_ZTRAP	(1 << 5)	/* 0x0020 error handler frame ($ZTRAP or $ETRAP) */
					/* 0x0040 VACANT!!!: extfun frame is no longer used */
#define SFT_ZSTEP_ACT	(1 << 7)	/* 0x0080 action frame for a zstep */
#define SFT_ZINTR	(1 << 8)	/* 0x0100 $zinterrupt frame */
#define SFT_TRIGR	(1 << 9)	/* 0x0200 Trigger base frame */

/* The following definition identifies a frame that is running a line of code - either in a routine or what amounts to an XECUTE
 * it excludes frames dealing with @ indirection and frames generated for various nefarious internal purposes.
 * As of this writing, it's used in op_unwind to identify a frame that should receive a relocated for_ctrl_stack created by an @
 * frame - see op_unwind for the code and additional comments
 */
#define SFT_LINE_OF_CODE_FRAME	(SFT_COUNT | SFT_ZBRK_ACT | SFT_DEV_ACT | SFT_ZTRAP | SFT_ZSTEP_ACT | SFT_ZINTR)

/* Flags for flag byte */
#define SFF_INDCE	(1 << 0)	/* 0x01 This frame is executing an indirect cache entry */
#define SFF_ZTRAP_ERR 	(1 << 1)	/* 0x02 error occured during $ZTRAP compilation */
#define SFF_DEV_ACT_ERR	(1 << 2)	/* 0x04 compilation error occured in device exception handler */
#define SFF_CI		(1 << 3)	/* 0x08 call-in base frame */
#define SFF_ETRAP_ERR	(1 << 4)	/* 0x10 An $ETRAP style error occurred while in this frame. A return to this frame will
					 *      cause the getframe macro to invoke error_return() for further error processing.
					 */
#define SFF_UNW_SYMVAL	(1 << 5)	/* 0x20 Unwound a symval in this stackframe (relevant to tp_restart) */
#define SFF_IMPLTSTART_CALLD	(1 << 6)	/* 0x40 This frame initiated a trigger call or spanning node transaction
						 * 	- checked by MUM_TSTART to prevent error returns to the frame
						 * 	which would cause a restart of error handling.
						 */

#define SFF_INDCE_OFF   		~(SFF_INDCE)		/* Mask to turn off SFF_INDCE */
#define SFF_ZTRAP_ERR_OFF		~(SFF_ZTRAP_ERR)	/* Mask to turn off SFF_ZTRAP_ERR */
#define SFF_DEV_ACT_ERR_OFF		~(SFF_DEV_ACT_ERR)	/* Mask to turn off SFF_DEV_ACT_ERR */
#define SFF_CI_OFF			~(SFF_CI)		/* Mask to turn off SFF_CI */
#define SFF_ETRAP_ERR_OFF		~(SFF_ETRAP_ERR)	/* Mask to turn off SFF_ETRAP_ERR */
#define SFF_UNW_SYMVAL_OFF		~(SFF_UNW_SYMVAL)	/* Mask to turn off SFF_UNW_SYMVAL */
#define SFF_IMPLTSTART_CALLD_OFF	~(SFF_IMPLTSTART_CALLD)	/* Mask to turn off SFF_IMPLTSTART_CALLD */

#define	ADJUST_FRAME_POINTER(fptr, shift)			\
{								\
	GBLREF	stack_frame	*error_frame;			\
	stack_frame		*oldfp;				\
								\
	oldfp = fptr;						\
	fptr = (stack_frame *)((char *)oldfp - shift);		\
	if (error_frame == oldfp)				\
	{	/* Adjust "error_frame" as well */		\
		assert(error_frame >= frame_pointer);		\
		error_frame = fptr;				\
	}							\
}

/* the following macro ensures there's an array of FOR pointers and frees any old entry that's about to get overlaid */
#define MANAGE_FOR_INDX(FPTR, LEVEL, NEW_INDX)									\
{														\
	assert(NULL != NEW_INDX);										\
	if (NULL == FPTR->for_ctrl_stack)									\
	{													\
		FPTR->for_ctrl_stack = (saved_for_indx *)malloc(SIZEOF(mval *) * MAX_FOR_STACK);		\
		memset(FPTR->for_ctrl_stack, 0, SIZEOF(mval *) * MAX_FOR_STACK);				\
	} else													\
		FREE_INDX_AND_CLR_SIBS(FPTR, LEVEL, NEW_INDX);							\
	assert(NULL == FPTR->for_ctrl_stack->saved_for_indx[LEVEL]);						\
	FPTR->for_ctrl_stack->saved_for_indx[LEVEL] = NEW_INDX;							\
}

/* the following macro runs the FOR pointer array, freeing anything it finds before freeing the array
 * in theory it could work down the array, but since the above macro needs FREE_INDX_AND_CLR_SIBS to work up, this does too
 */
#define	FREE_SAVED_FOR_INDX(FPTR)								\
{												\
	uint4 Level;										\
												\
	assert(NULL != FPTR->for_ctrl_stack);							\
	for (Level = 1; Level < MAX_FOR_STACK; Level++)	/* level 0 never holds a pointer */	\
		FREE_INDX_AND_CLR_SIBS(FPTR, Level, NULL);					\
	free((char *)FPTR->for_ctrl_stack);							\
}

/* the following macro, currentl only used by the above 2 macros,
 * deals with the possibility of the same control variable at multiple nesting levels and clears higher nesting levels
 */
#define	FREE_INDX_AND_CLR_SIBS(FPTR, LEVEL, NEW_INDX)									\
{															\
	uint4	Lvl;													\
	mval	*Ptr;													\
															\
	if (NULL != (Ptr = FPTR->for_ctrl_stack->saved_for_indx[LEVEL])) /* NOTE assignment */				\
	{														\
		free((char *)Ptr);											\
		for (Lvl = 1; Lvl < MAX_FOR_STACK; Lvl++)	/* level 0 never holds a pointer */			\
			if (Ptr == FPTR->for_ctrl_stack->saved_for_indx[Lvl])						\
				FPTR->for_ctrl_stack->saved_for_indx[Lvl] = (mval *)(Lvl < LEVEL ? NEW_INDX : NULL);	\
	}														\
}

void new_stack_frame(rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr);
void new_stack_frame_sp(rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr);
int4 symbinit(void);
unsigned char *get_symb_line(unsigned char *out, unsigned char **b_line, unsigned char **ctxt);
unsigned char *symb_line(unsigned char *in_addr, unsigned char *out, unsigned char **b_line, rhdtyp *routine);
void copy_stack_frame(void);
void copy_stack_frame_sp(void);
void exfun_frame(void);
void exfun_frame_sp(void);
void exfun_frame_push_dummy_frame(void);
void base_frame(rhdtyp *base_address);
void pseudo_ret(void);
void call_dm(void);
int dm_start(void);
stack_frame *op_get_msf (void);
int op_zbreak(stack_frame *fp);
void adjust_frames(unsigned char *old_ptext_beg, unsigned char *old_ptext_end, unsigned char *new_ptext_beg);

#endif
