/****************************************************************************
*       emulator.h
*
*       Main emulator header file
* **********************************************************************
* *     Copyright (c) by Barak Pearlmutter and Kevin Lang, 1987-98.    *
* *     Portions copyright (c) by Alex Stuebinger, 1998.               *
* *     Placed under the terms of the GNU General Public License       *
* *     as published by the Free Software Foundation.                  *
* **********************************************************************
*
*       Revision log:
*       *************
*
*       date     time   author  comments
*       ================================================================
*       11/01/98 17:03  stuebi  initial revision
*       ----------------------------------------------------------------
*
*       ================================================================
*
*
*       Description:
*       ************
*
*
*
*       Planned:
*       ********
*
*
*
*/


#ifndef _EMULATOR_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif



#ifndef __STDC__
#error you lose... the whole code is ansi-c, try older release
#endif

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>

#include "config.h"

#ifdef __posix__

#ifdef __dos__
#include <sys\types.h>
#else
#include <sys/types.h>
#endif /* __dos__ */

#ifdef __unix__
#include <unistd.h>        /* for the chdir() and isatty() functions */

#elif defined(__WATCOMC__)
#include <direct.h>        /* for the chdir() function */
#include <io.h>            /* for the isatty() function */
#endif

#endif /* __posix__ */


#ifdef __mac__
#include <storage.h>
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif



/***************************************************************************
* Define GUARD_BIT if you want a guard bit in fixnums.
* CAUTION:  Don't define this GUARD_BIT on all intel x86, because
*           the code is buggy, we don't know if it really works on
*           any machine [Alex Stuebinger] */

#ifdef __386__
#undef GUARD_BIT
#else
#undef GUARD_BIT
#endif
/***************************************************************************/


#define READ_MODE "r"
#define WRITE_MODE "w"
#define APPEND_MODE "a"

#ifdef __mac__

#define READ_BINARY_MODE "rb+"
#define WRITE_BINARY_MODE "wb+"

#else

#define READ_BINARY_MODE "rb+"
#define WRITE_BINARY_MODE "wb+"

#endif /* __mac__ */


#ifdef CANT_FLUSH_STD
#define fflush_stdout()
#define fflush_stderr()
#else
#define fflush_stdout() fflush(stdout)
#define fflush_stderr() fflush(stderr)
#endif

typedef int bool;

#define SIGN_16BIT_ARG(x)	((short)(x))

typedef unsigned long ref;

#define TAG_MASK	3
#define TAG_MASKL	3L
#define SUBTAG_MASK	0xFF
#define SUBTAG_MASKL	0xFFL

#define INT_TAG		0
#define IMM_TAG		1
#define LOC_TAG 	2
#define PTR_TAG		3

#define PTR_MASK	2

#define CHAR_SUBTAG	((0<<2)+IMM_TAG)

#define TAG_IS(X,TAG)		(((X)&TAG_MASK)==(TAG))
#define SUBTAG_IS(X,SUBTAG)	(((X)&SUBTAG_MASK)==(SUBTAG))

#define REF_TO_INT(r)	((long)ASHR2((long)(r)))

#define REF_TO_PTR(r)	((ref *)((r)-PTR_TAG))
#define LOC_TO_PTR(r)	((ref *)((r)-LOC_TAG))

#define ANY_TO_PTR(r)	((ref *)((r)&~TAG_MASKL))


#define PTR_TO_LOC(p)	((ref)((ref)(p)+LOC_TAG))
#define PTR_TO_REF(p)	((ref)((ref)(p)+PTR_TAG))

#define REF_TO_CHAR(r)	((char)((r)>>8))
#define CHAR_TO_REF(c)	(((ref)(c)<<8)+CHAR_SUBTAG)


/* Look what Prof. Pearlmutter wrote about tag bit schemes: */

/*
about tag bits, there were reasons for using the method chosen.  it
would be good to modularize the code in such a way that the tag scheme
can be easily modified, for benchmarking purposes.  but we did do a
little benchmarking, and found that low tag bits were a big win.  here
is our explanation for that phenomenon:

    pointer chasing is much more common than aritmetic, so a tag
    format that requires shifting or masking before pointer
    dereference is at a severe disadvantage.  since most machines are
    byte addressed but our quantities are word alligned, the 2 low
    bits in the address would be zero anyway, so we are free to use
    them for tags.  many processors have an indexed indirect address
    mode, and anyway typically we access not the pointer itself but an
    offset (eg the CDR of the object pointed to or whatever) so even
    if the low tag bits for pointers isn't 0, it costs almost nothing
    more than if they were zero, since an offset has to be added
    anyway.

    On the other hand, for arithmetic, we rarely convert a tagged
    integer to a native integer, but we frequently do arithmetic
    directly on tagged integers.  with low tag bits of zero for
    integers, this is trivial, ie one instruction (except for overflow
    detection but let's not get into that) for all operations except
    multiplication, where it requires an extra shift.  And modern
    processors have a barrel shifter, so a sign extension shift is
    actually very cheap.

    high tag bits on the other hand require (a) for pointers, either
    shifting and masking or serious constraints on where the heap can
    live in the address space or both, and (b) arithmetic is actually
    more complex, especially considering the issue of sign extension.
    (also constructing the masks is pretty slow on some RISCs, which
    have quick instructions for loading small constants but not big ones)

    In any case, most arithmetic requires overflow checking, which (if
    done using the processor's overflow status word, which is needed
    for high performance) is easier with low tag bits.

*/


/* MIN_REF is the most negative fixnum.  There is no corresponding
   positive fixnum, an asymmetry inherent in a twos complement
   representation. */

#ifndef GUARD_BIT

#define INT_TO_REF(i)	((ref)(((long)(i)<<2)+INT_TAG))
#define MIN_REF     ((ref)(1l<<(WORDSIZE-1)))
/* Check if high three bits are equal. */
#define OVERFLOWN_INT(i,code)					\
{ register highcrap = ((unsigned long)(i)) >> (WORDSIZE-3);	\
  if ((highcrap != 0x0) && (highcrap != 0x7)) {code;} }

#else

#define INT_TO_REF(i)	((ref)(((long)(i)<<3)>>1)+INT_TAG)
#define MIN_REF     ((ref)(3l<<(WORDSIZE-2)))
/* Check if high bit and next-to-high bit are unequal. */
#define OVERFLOWN(r) (((r)>>(WORDSIZE-1)) != (((r)>>(WORDSIZE-2))&1))
#define FIX_GUARD_BIT(r)	((ref)((((long)(r))<<1)>>1))

#endif /* GUARD_BIT */

#define MAX_REF ((ref)-((long)MIN_REF+1))

/*
 * Offsets for wired types.  Offset includes type and
 * optional length fields when present.
 */

/* CONS-PAIR: */
#define CONS_PAIR_CAR_OFF	1
#define CONS_PAIR_CDR_OFF	2

/* TYPE: */
#define TYPE_LEN_OFF		1
#define TYPE_VAR_LEN_P_OFF	2
#define TYPE_SUPER_LIST_OFF	3
#define TYPE_IVAR_LIST_OFF	4
#define TYPE_IVAR_COUNT_OFF	5
#define TYPE_TYPE_BP_ALIST_OFF	6
#define TYPE_OP_METHOD_ALIST_OFF 7
#define TYPE_WIRED_P_OFF	8

/* METHOD: */
#define METHOD_CODE_OFF		1
#define METHOD_ENV_OFF		2

/* CODE-VECTOR: */
#define CODE_IVAR_MAP_OFF	2
#define CODE_CODE_START_OFF	3

/* OPERATION: */
#define OPERATION_LAMBDA_OFF		1
#define OPERATION_CACHE_TYPE_OFF	2
#define OPERATION_CACHE_METH_OFF	3
#define OPERATION_CACHE_TYPE_OFF_OFF	4
#define OPERATION_LENGTH		5

/* ESCAPE-OBJECT */
#define ESCAPE_OBJECT_VAL_OFF	1
#define ESCAPE_OBJECT_CXT_OFF	2

/* Continuation Objects */
#define CONTINUATION_VAL_SEGS	1
#define CONTINUATION_VAL_OFF	2
#define CONTINUATION_CXT_SEGS	3
#define CONTINUATION_CXT_OFF	4

#define car(x)	(REF_SLOT((x),CONS_PAIR_CAR_OFF))
#define cdr(x)	(REF_SLOT((x),CONS_PAIR_CDR_OFF))


extern char *dump_file;

extern ref e_t, e_nil, e_fixnum_type, e_loc_type, e_cons_type,
  *e_subtype_table, *e_bp, *e_env, e_env_type, *e_argless_tag_trap_table,
  *e_arged_tag_trap_table, e_object_type, e_segment_type, e_code_segment,
  e_boot_code, e_current_method, e_uninitialized, e_method_type;
extern unsigned long e_next_newspace_size, original_newspace_size;
extern unsigned short *g_e_pc;

extern bool trace_segs;

extern bool dump_decimal, dump_binary;

typedef struct
{
  ref *start, *end;
  long size;
#ifdef UNALIGNED
  char offset;
#endif
} space;

extern space spatic, _new;
extern ref *free_point;

#define SPACE_PTR(s,p)	((s).start<=(p) && (p)<(s).end)

#define NEW_PTR(r)  SPACE_PTR(_new,(r))
#define SPATIC_PTR(r)	SPACE_PTR(spatic,(r))

/* Leaving r unsigned lets us checks for negative and too big in one shot: */
#define wp_to_ref(r)					\
  ( (unsigned long)REF_TO_INT(r) >= wp_index ?		\
   e_nil : wp_table[1+(unsigned long)REF_TO_INT((r))] )


#ifdef MALLOC_WP_TABLE
extern ref *wp_table;
#else
extern ref wp_table[];
#endif

extern long wp_index;

extern bool gc_shutup;

extern ref *gc_examine_ptr;
#define GC_MEMORY(v) {*gc_examine_ptr++ = (v);}
#define GC_RECALL(v) {(v) = *--gc_examine_ptr;}


/* This is used to allocate some storage.  It calls gc when necessary. */

#define ALLOCATE(p, words, place)			\
  ALLOCATE_PROT(p, words, place,; ,; )

/* This is used to allocate some storage while the stack pointers have not
   been backed into the structures and must be backed up before gc. */

#define ALLOCATE_SS(p, words, place)			\
  ALLOCATE_PROT(p, words, place,			\
		{ UNOPTC(cxt_stk.ptr = cxt_stk_ptr);	\
		  UNOPTV(val_stk.ptr = val_stk_ptr);	\
		  g_e_pc = e_pc; },			\
		{ e_pc = g_e_pc;			\
		  UNOPTV(val_stk_ptr = val_stk.ptr);	\
		  UNOPTC(cxt_stk_ptr = cxt_stk.ptr); })

/* This allocates some storage, assuming the stack pointers have not been
   backed into the structures and that v must be protected from gc. */

#define ALLOCATE1(p, words, place, v)			\
  ALLOCATE_PROT(p, words, place,			\
		{ GC_MEMORY(v);				\
		  UNOPTC(cxt_stk.ptr = cxt_stk_ptr);	\
		  UNOPTV(val_stk.ptr = val_stk_ptr);	\
		  g_e_pc = e_pc; },			\
		{ e_pc = g_e_pc;			\
		  UNOPTV(val_stk_ptr = val_stk.ptr);	\
		  UNOPTC(cxt_stk_ptr = cxt_stk.ptr);	\
		  GC_RECALL(v); })

#define ALLOCATE_PROT(p, words, place, before, after)	\
  /* ref *p; int words; string place; */		\
{							\
  ref *new_free_point = free_point + (words);		\
							\
  if (new_free_point >= _new.end)            \
    {							\
      before;						\
      gc(FALSE, FALSE, (place), (words));		\
      after;						\
      new_free_point = free_point + (words);		\
    }							\
							\
  (p) = free_point;					\
  free_point = new_free_point;				\
}



/* These get slots out of Oaklisp objects, and may be used as lvalues. */

#define SLOT(p,s)	(*((p)+(s)))
#define REF_SLOT(r,s)	SLOT(REF_TO_PTR(r),s)


extern volatile int signal_poll_flag;

#define signal_pending()	(signal_poll_flag)

#include "stacks.h"

#define __little_endian__ 0
#define __big_endian__    1

extern int byte_order;


/* Function Prototypes */

/* emulator.c */
extern void printref(ref);

/* mymalloc.c */
extern char *my_malloc(size_t);
extern void realloc_space(space *, ref *);
extern void free_space(space *);
extern void alloc_space(space *);

/* worldio.c */
extern void dump_world(bool);
extern void read_world(char *);

/* gc.c */
extern void gc(bool, bool, char *, long);

/* stacks.c */
extern void flush_buffer(stack *, int);
extern void unflush_buffer(stack *, int);
extern void dump_stack_proc(stack *);
extern void init_stk(stack *);

/* weak.c */
extern void init_wp(void);
extern void rebuild_wp_hashtable(void);
extern ref ref_to_wp(ref);
/* extern void wp_hashtable_distribution(void); */
extern unsigned long post_gc_wp(void);

/* signal.c */
extern void enable_signal_polling(void);
/* extern void disable_signal_polling(void); */
extern void clear_signal(void);

/* time.c */
extern unsigned long get_real_time(void);
extern unsigned long get_user_time(void);


#define _EMULATOR_H_INCLUDED
#ifdef __cplusplus
};
#endif
#endif



