// interp.h -- declarations of imports from interp.c
//
// Author: Ian.Piumarta@inria.fr
//
// last edited: Mon Feb 22 13:59:59 1999 by piumarta (Ian Piumarta) on pingu

#ifndef _interp_h_
#define _interp_h_

#include <stdio.h>

#include "config.h"
#include "debug.h"

#define byteAt(i) (*((unsigned char *) (i)))
#define byteAtput(i, val) (*((unsigned char *) (i)) = (byte)(val))
#define longAt(i) (*((int *) (i)))
#define longAtput(i, val) (*((int *) (i)) = (int)(val))

#define unimplemented()	{ \
  fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
  error("not implemented"); \
}


class Array;
class Association;
class BlockContext;
class Class;
class CompiledMethod;
class Context;
class ContextPart;
class Link;
class Message;
class Metaclass;
class MethodContext;
class Object;
class Process;
class ProcessorScheduler;
class PseudoContext;
class Semaphore;
class Symbol;
/*
class TranslatedMethod;
*/

typedef Object	     *oop;
typedef unsigned char byte;
typedef unsigned int  word;

#define MaxPrimitive	700
typedef void(*primitive)(void);
extern  primitive	primitiveTable[MaxPrimitive + 1];

#define NilContext	(Context *)1

extern void initPrimitiveTable(void);


// functions imported from C
extern "C" {
  oop	  accessibleObjectAfter(oop obj);
  void 	  addToMethodCacheSelclassmethodprimIndex(oop selector, Class *clazz, CompiledMethod *method, int primitiveIndex);
  void	  allAccessibleObjectsOkay(void);
  int	  asciiOfCharacter(oop character);
  void	  beRootIfOld(oop object);
  void    bytecodeActivateNewMethod(void);
  void    bytecodeExecuteNewMethod(void);
  void	  checkForInterrupts(void);
  oop	  commonVariableatcacheIndex(oop obj, int index, int line);
  void	  commonVariableatputcacheIndex(oop obj, int index, oop val, int line);
  int 	  doPrimitiveDivby(oop dividend, oop divisor);
  int 	  doPrimitiveModby(oop dividend, oop divisor);
  Class  *fetchClassOf(const Object *object);
  Class	 *findClassOfMethodforReceiver(CompiledMethod *m, oop r);
  Symbol *findSelectorOfMethodforReceiver(CompiledMethod *m, oop r);
  oop	  firstAccessibleObject(void);
  void	  fullGC();
  void	  incrementalGC();
  oop  	  instantiateClassindexableSize(Class *cls, int size);
  oop  	  instantiateSmallClasssizeInBytesfill(Class *cls, int lbs, oop flr);
  int	  ioLowResMSecs(void);
  int	  ioMicroMSecs(void);
  int	  ioMSecs(void);
  void	  ioProcessEvents(void);
  bool	  lookupInMethodCacheSelclass(oop selector, Class * cls);
  void	  lookupMethodInClass(Class *cls);
  void	  markAndTrace(oop obj);
  Class  *methodClassOf(CompiledMethod *method);
  bool	  okArrayClass(Class *cls);
  void	  okayFields(oop obj);
  void	  okayInterpreterObjects(void);
  bool	  okStreamArrayClass(Class *cls);
  oop	  positive32BitIntegerFor(unsigned int value);
  void	  possibleRootStoreIntovalue(oop dst, oop src);
  oop	  primitivePointX(void);	// not in prim table !?!
  oop	  primitivePointY(void);	// not in prim table !?!
  void	  printNameOfClasscount(Class *cls, int cnt);
  void	  printStringOf(oop object);
  void	  putToSleep(Process *proc);
  oop     remap(oop obj);
  oop  	  stObjectat(oop object, int index);
  oop  	  stObjectatput(oop object, int index, oop value);
  int	  stSizeOf(oop obj);
  Process *wakeHighestPriority(void);
  void	  writeImageFile(int dataSize);
};

#define pushRemappableOop(obj)	(remapBuffer[++remapBufferCount]= (obj))
#define popRemappableOop()	(remapBuffer[remapBufferCount--])
#define popRemappable(Cls)	((remapBuffer[remapBufferCount--])->as##Cls())


#define OneRel	1
#define PreInc	1


/* INTERPRETER OBJECTS */

#define MethodCacheSize	512
#define CacheProbeMax	3

extern oop		methodCache[];

#define	AtCacheEntries		8	// must be power of two

#define	AtCacheOop		1
#define	AtCacheSize		2
#define	AtCacheFmt		3
#define	AtCacheFixedFields	4

#define	AtCacheEntrySize	4	// must be power of two

#define	AtCacheMask		((AtCacheEntries - 1) * AtCacheEntrySize)
#define	AtPutBase		(AtCacheEntries * AtCacheEntrySize)
#define	AtCacheTotalSize	(AtCacheEntries * AtCacheEntrySize * 2)

extern word			atCache[AtCacheTotalSize + 1];

extern oop		nilObj;
extern oop		trueObj;
extern oop		falseObj;

extern CompiledMethod  *method;
extern CompiledMethod  *newMethod;
extern Context	       *activeContext;

extern int		currentBytecode;

extern oop		receiver;
extern MethodContext   *theHomeContext;

extern int		allocationsBetweenGCs;

extern oop		memory;
extern oop		youngStart;
extern oop		freeBlock;
extern oop		endOfMemory;

extern Context	       *freeContexts;

extern oop	       *stackPointer;
extern word	       *instructionPointer;

extern oop		messageSelector;
extern Class	       *lkupClass;
extern int		argumentCount;
extern int		primitiveIndex;
extern int		reclaimableContextCount;
extern int		interruptCheckCounter;
extern int		nextWakeupTick;
extern int		lastTick;
extern int		nextPollTick;
extern int		signalLowSpace;
extern int		interruptPending;
extern int		semaphoresToSignalCount;
extern int		semaphoresToSignal[];
extern int		successFlag;

extern Array	       *specialObjectsOop;

extern oop		remapBuffer[];
extern int		remapBufferCount;

///
/// SPECIAL OBJECTS ARRAY
///

extern unsigned int ccBitsBlockContext;
extern unsigned int ccBitsMethodContext;
extern unsigned int ccBitsPseudoContext;

#define splObj(index)	(specialObjectsOop->at(index))

#define	NilObject					  splObj( 0)
#define	FalseObject					  splObj( 1)
#define	TrueObject					  splObj( 2)
#define	SchedulerAssociation		((Association	*)splObj( 3))
#define	ClassBitmap			((Class		*)splObj( 4))
#define	ClassSmallInteger		((Class		*)splObj( 5))
#define	ClassString			((Class		*)splObj( 6))
#define	ClassArray			((Class		*)splObj( 7))
#define	SmalltalkDictionary				  splObj( 8)
#define	ClassFloat			((Class		*)splObj( 9))
#define	ClassMethodContext		((Class		*)splObj(10))
#define	ClassBlockContext		((Class		*)splObj(11))
#define	ClassPoint			((Class		*)splObj(12))
#define	ClassLargePositiveInteger	((Class		*)splObj(13))
#define	TheDisplay					  splObj(14)
#define	ClassMessage			((Class		*)splObj(15))
#define	ClassCompiledMethod		((Class		*)splObj(16))
#define	TheLowSpaceSemaphore		((Semaphore	*)splObj(17))
#define	ClassSemaphore			((Class		*)splObj(18))
#define	ClassCharacter			((Class		*)splObj(19))
#define	SelectorDoesNotUnderstand	((Symbol	*)splObj(20))
#define	SelectorCannotReturn		((Symbol	*)splObj(21))
#define	TheInputSemaphore		((Semaphore	*)splObj(22))
#define	SpecialSelectors		((Array		*)splObj(23))
#define	CharacterTable			((Array		*)splObj(24))
#define	SelectorMustBeBoolean		((Symbol	*)splObj(25))
#define	ClassByteArray			((Class		*)splObj(26))
#define	ClassProcess			((Class		*)splObj(27))
#define	CompactClasses			((Array		*)splObj(28))
#define	TheTimerSemaphore		((Semaphore	*)splObj(29))
#define	TheInterruptSemaphore		((Semaphore	*)splObj(30))
#define	SmallMethodContext		((MethodContext	*)splObj(34))
#define	SmallBlockContext		((BlockContext	*)splObj(36))
#define	ExternalObjectsArray		((Array		*)splObj(38))
#define	ClassPseudoContext		((Class		*)splObj(39))
/*
#define	ClassTranslatedMethod		((Class		*)splObj(40))
*/

#define Processor	(SchedulerAssociation->value->asProcessorScheduler())


///
/// CLASSES
///


class Object
{
public:

  unsigned int _xh[0];		// extra header words before object

  inline unsigned int _sizeHeader(void) const { return _xh[-2] & ~0x3U; }
  inline Class *_classHeader(void) const { return (Class *)(_xh[-1] & ~0x3U); }

# if 0
  /* it would be nice to use bit fields everywhere, but some compilers
     generate _really_ lousy code. */
  unsigned type		:  2;	/* header type */
  unsigned size		:  6;	/* object size in 32-bit words */
  unsigned format	:  4;	/* object format */
  unsigned ccIndex	:  5;	/* compact class index */
  unsigned hashBits	: 12;	/* object hash (for HashSets) */
  unsigned dirtyBit	:  1;	/* GC dirty bit */
  unsigned rootBit	:  1;	/* GC root bit */
  unsigned markBit	:  1;	/* GC mark bit */
# else
  unsigned int _bh;

  inline unsigned int _baseHeader(void) const { return _bh; }

  inline unsigned int _type(void) const	{ return (_bh      ) & 0x003; }
  inline unsigned int _size(void) const	{ return (_bh >>  2) & 0x03f; }
  inline unsigned int _format(void) const	{ return (_bh >>  8) & 0x00f; }
  inline unsigned int _ccIndex(void) const	{ return (_bh >> 12) & 0x01f; }
  inline unsigned int _hashBits(void) const	{ return (_bh >> 17) & 0xfff; }

  inline unsigned int  ccBits(void) const { return _bh & 0x1f000; }

  inline void ccBits(unsigned int cc) {
    _bh= (_bh & ~0x1f000UL) | cc;
  }

  inline void clearRootBit(void) { _bh&= ~0x40000000; }

  inline bool isForwarded(void) const { return (_bh & 0x80000000); }
#endif

  oop	_oops[0];
  byte	_bytes[0];
  word	_words[0];

  inline bool		isInteger(void)		const { return (((int)this) & 1) != 0; }
  inline static bool	isIntegerValue(int val)	{ return (val ^ (val << 1)) >= 0; }
  inline int		integerValue(void)	const { return ((int)this) >> 1; }
  inline static oop	integer(int value)	{ return (oop)((value << 1) | 1); }

  inline int sizeBits(void) const {
    if (_type() == 0)
      return _sizeHeader() & 0xfffffffc;
    else
      return _baseHeader() & 0xfc;
  }

  inline int wordSize(void) const {
    return sizeBits() >> 2;
  }

  // answer the number of indexable words in the object
  inline int wordLength(void) const {
    return (sizeBits() - sizeof(Object)) >> 2;
  }

  static inline oop allocateSmall(Class *cls, size_t lbs, oop fil= nilObj) {
    return instantiateSmallClasssizeInBytesfill(cls, lbs, fil);
  }

  static inline oop allocate(Class *cls, size_t lbs) {
    return instantiateClassindexableSize(cls, lbs);
  }

  // these must be defined after class Array
  inline Class *fetchClass(void) const;
  inline bool	isMemberOf(Class *cls) const;
  inline bool	isArray(void) const;
  inline bool	isBlockContext(void) const;
  inline bool	isCompiledMethod(void) const;
  inline bool	isMethodContext(void) const;
  inline bool	isSemaphore(void) const;
  /*
  inline bool	isTranslatedMethod(void) const;
  */

  inline static oop firstObject(void) { return firstAccessibleObject(); }
  inline oop nextObject(void) { return accessibleObjectAfter(this); }

  inline void beRoot(void) {
    if (this < youngStart) beRootIfOld(this);
  }

  inline void checkStore(oop value) {
    if (this < youngStart) possibleRootStoreIntovalue(this, value);
  }

  inline void mark(void) {
    // old objects that need marking are already in the root set
#ifndef MARK_OLD
# ifndef EPEND
#   warning: OLD OBJECT MARKING IS DISABLED
# endif
    if (this >= youngStart)
#endif !MARK_OLD
      markAndTrace(this);
  }

  inline oop remap(void) { return isForwarded() ? ::remap(this) : this; }

#define defineCast(Cls)	inline Cls *as##Cls(void) const { return (Cls *)this; }

  defineCast(Array);
  defineCast(Association);
  defineCast(BlockContext);
  defineCast(Class);
  defineCast(CompiledMethod);
  defineCast(Context);
  defineCast(Link);
  defineCast(Message);
  defineCast(Metaclass);
  defineCast(MethodContext);
  defineCast(Process);
  defineCast(ProcessorScheduler);
  defineCast(PseudoContext);
  defineCast(Semaphore);
  /*
  defineCast(TranslatedMethod);
  */

#undef defineCast

  inline bool isNil(void) const { return this == nilObj; }
  inline bool notNil(void) const { return this != nilObj; }
};


class Array : public Object
{
public:
  oop oops[0];	/* some compilers won't like this */

  inline oop at(int index) const { return oops[index]; }
  inline oop atPut(int index, oop value) { return oops[index]= value; }
};


// these must be defined after class Array

inline Class *Object::fetchClass(void) const {
  if (isInteger()) return ClassSmallInteger;
  int ccIndex= _ccIndex() - 1;
  if (ccIndex < 0) return _classHeader();
  return CompactClasses->at(ccIndex)->asClass();
}

inline bool Object::isMemberOf(Class *cls) const {
  return fetchClass() == cls;
}

#define defClassTest(CLASS) \
  inline bool Object::is##CLASS(void) const { \
    return isMemberOf(Class##CLASS); \
  }

defClassTest(Array);
defClassTest(BlockContext);
defClassTest(CompiledMethod);
defClassTest(MethodContext);
defClassTest(Semaphore);
/*
defClassTest(TranslatedMethod);
*/

#undef defClassTest

class Association : public Object
{
public:
  oop key;
  oop value;
};


class MethodDictionary : public Object
{
public:
  // Set
  oop	 tally;
  Array	*array;
  // MethodDictionary
  oop	 oops[0];

  inline oop at(int index) const { return oops[index]; }
};


class Behavior : public Object
{
public:
  Class		   *superclass;
  MethodDictionary *methodDict;
  oop		    format;
  Array		   *subclasses;

  inline ccBits(void) const { return (unsigned int)format & 0x1f000; }
};

class ClassDescription : public Behavior
{
public:
  Array		   *instanceVariables;
  oop		    organization;
};

class Class : public ClassDescription
{
public:
  Symbol	   *name;
  oop		    classPool;
  oop		    sharedPools;
};

class Metaclass : public ClassDescription
{
public:
  Class		   *thisClass;
};


class Character : public Object
{
public:
  oop _value;

  inline int asciiValue(void) const { return _value->integerValue(); }
  inline static oop value(int asciiValue) { return CharacterTable->at(asciiValue); }
};


class CompiledMethod : public Object
{
public:
  static const int MaxTemps= 64;

  byte bytes[0];
# if 0	// see the comment in Object
  unsigned tag		: 1;	/* integer object: always 1 */
  unsigned primIndexLo	: 9;	/* low 9 bits of primitive index */
  unsigned numLiterals	: 8;	/* size of literal frame */
  unsigned largeFrame	: 1;	/* 1: large context, 0: small context */
  unsigned numTemps	: 6;	/* number of temporaries */
  unsigned numArgs	: 4;	/* number of arguments */
  unsigned primIndexHi	: 2;	/* high 2 bits of primitive index */
  unsigned unused	: 1;	/* spare */
# else
  unsigned int _mh;
  inline unsigned int methodHeader(void) { return _mh; }
#endif
  oop literals[0];

  inline int  primIndexLo(unsigned int mh)	const { return (mh >>  1) & 0x1ff; }
  inline int  literalCount(unsigned int mh)	const { return (mh >> 10) & 0x0ff; }
  inline int  tempCount(unsigned int mh)	const { return (mh >> 19) & 0x03f; }
  inline int  argCount(unsigned int mh)		const { return (mh >> 25) & 0x00f; }
  inline int  primIndexHi(unsigned int mh)	const { return (mh >> 29) & 0x003; }

  inline unsigned int primitiveIndex(unsigned int mh) const {
    return primIndexLo(mh) + (primIndexHi(mh) << 9);
  }

  inline int  primIndexLo(void)		const { return (_mh >>  1) & 0x1ff; }
  inline int  literalCount(void)	const { return (_mh >> 10) & 0x0ff; }
  inline int  tempCount(void)		const { return (_mh >> 19) & 0x03f; }
  inline int  argCount(void)		const { return (_mh >> 25) & 0x00f; }
  inline int  primIndexHi(void)		const { return (_mh >> 29) & 0x003; }

  inline unsigned int primitiveIndex(void) const {
    return primIndexLo() + (primIndexHi() << 9);
  }

  inline byte *initialIP(unsigned int mh) const {
    return (byte *)(literals + literalCount(mh)) - PreInc;
  }

  inline byte *initialIP(void) const { return initialIP(_mh); }

  inline unsigned int initialPC(unsigned int mh) const {
    return (initialIP(mh) - bytes + PreInc) + OneRel;
  }

  inline unsigned int initialPC(void) const { return initialPC(_mh); }

  inline byte *bytecodePointer(int pc) const {
    return (byte *)(bytes + pc - OneRel - PreInc);
  }

  inline int bytecodeIndex(byte *ip) const {
    return (ip - bytes) + OneRel + PreInc;
  }

  inline unsigned int returnField(void) const {
    int primIndex= primitiveIndex();
#ifdef DEBUG
    if (primitiveIndex() < 264) error("only meaningful for quick-return");
#endif
    return primIndex - 264;
  }

  // Answer the 1-based index of the last bytecode (see CompiledMethod>>endPC)
  int endPC(void) {
    int lastIndex= stSizeOf(this) - OneRel;	// index of last byte
    unsigned int flagByte= bytes[lastIndex];
    if (flagByte == 0)	// either 0, 0, 0, 0 or just 0
      for (int i= 1; i <= 4; ++i)
	if (bytes[lastIndex - i] != 0)
	  return lastIndex - i + OneRel;
    if (flagByte < 252)
      // tempnames encoded in last few bytes
      // last byte is size of encoding bytes
      return lastIndex - flagByte + OneRel;
    // Normal 4-byte source pointer
    return lastIndex - 4 + OneRel;
  }
};


class PseudoContext;

class ContextPart : public Object
{
public:
  // InstructionStream
  struct Context *sender;
  oop		  pc;
  // ContextPart
  oop		  stackp;

  inline bool isPseudo(void) const { return ((oop)sender)->isInteger(); }
  inline PseudoContext *asPseudoContext(void) const {
    return (PseudoContext *)this;
  }
};


#define SmallContextSize	76
#define LargeContextSize	156


class MethodContext : public ContextPart
{
public:
  CompiledMethod *method;
  oop		  receiverMap;
  oop		  receiver;
  oop		  stack[0];

  inline const oop *temporaryPointer(void) const { return stack; }

  inline Context *asContext(void) const { return (Context *)this; }
};


class BlockContext : public ContextPart
{
public:
  oop		  nargs;
  oop		  startpc;
  MethodContext	 *home;
  oop		  stack[0];

  inline void *operator new(size_t ignored) {
    return (void *)allocateSmall(ClassBlockContext, LargeContextSize);
  }

  inline int argumentCount(void) const { return nargs->integerValue(); }

  inline const oop *temporaryPointer(void) const { return home->temporaryPointer(); }

  inline Context *asContext(void) const { return (Context *)this; }
};


class Context : public ContextPart
{
public:
  oop		  pad[3];	// {Method,Block}Context
  oop		  stack[0];

  inline bool isBlock(void) const { return pad[0]->isInteger(); }
  inline bool isMethod(void) const { return !isBlock(); }

  inline BlockContext  *asBlockContext(void)  const { return ( BlockContext *)this; }
  inline MethodContext *asMethodContext(void) const { return (MethodContext *)this; }

  inline void bePseudoContext(void) { ccBits(ccBitsPseudoContext); }

  inline MethodContext *home(void) const {
    return isBlock() ? asBlockContext()->home : asMethodContext();
  }

  inline CompiledMethod *method(void) const {
    return home()->method;
  }

  inline oop *stackPointer(void) {
    return stack + (stackp->integerValue() - OneRel);
  }

  inline int stackIndexOf(oop *sp) const {
    return (sp - stack) + OneRel;
  }

  inline void stackPointer(oop *sp) {
    stackp= Object::integer(stackIndexOf(sp));
  }

  inline const oop *temporaryPointer(void) const { return home()->temporaryPointer(); }

  inline void unlink(oop nilOop= nilObj) {
    sender= (pc= nilOop)->asContext();
  }
};


class String : public Object {};
class Symbol : public String {};


class Link : public Object
{
public:
  Link *nextLink;
};

class LinkedList : public Object
{
public:
  Link *firstLink;
  Link *lastLink;

  inline bool isEmpty(void) const {
    return firstLink == nilObj;
  }

  inline Link *addLast(Link *aLink) {
    assert(aLink != nilObj);
    if (isEmpty()) {
      firstLink= aLink;
      // checkStore done below, before return
    } else {
      lastLink->checkStore(lastLink->nextLink= aLink);
    }
    checkStore(lastLink= aLink);
    return aLink;
  }

  inline Link *removeFirst(void) {
    // assume: the list is not empty
    Link *first= firstLink;
    Link *last= lastLink;
    assert(first != nilObj);
    assert(last != nilObj);
    if (first == last) {
      // checkStore(nil) is never really necessary...
      checkStore(firstLink= lastLink= nilObj->asLink());
    } else {
      Link *next= first->nextLink;
      checkStore(firstLink= next);
    }
    // checkStore(nil) is never really necessary...
    first->checkStore(first->nextLink= nilObj->asLink());
    return first;
  }
};


class Semaphore : public LinkedList
{
public:
  oop excessSignals;
};


class Process : public Link
{
public:
  Context	*suspendedContext;
  oop		 priority;	// SmallInteger
  LinkedList	*myList;
  oop		 errorHandler;

  inline void addToList(LinkedList *list) {
    list->addLast(this);
    checkStore(myList= list);
  }
};


class ProcessorScheduler : public Object
{
public:
  Array		*quiescentProcessLists;
  Process	*activeProcess;
};


class Message : public Object
{
public:
  oop	 selector;
  Array	*args;
};


#endif _interp_h_

