/*
	Little Smalltalk, version 2
	Written by Tim Budd, Oregon State University, July 1987
*/

/*
	The first major decision to be made in the memory manager is what
an entity of type object really is.  Two obvious choices are a pointer (to 
the actual object memory) or an index into an object table.  We decided to
use the latter, although either would work.
	Similarly, one can either define the token object using a typedef,
or using a define statement.  Either one will work (check this?)
*/

typedef short object;

/*
	The memory module itself is defined by over a dozen routines.
All of these could be defined by procedures, and indeed this was originally
done.  However, for efficiency reasons, many of these procedures can be
replaced by macros generating in-line code.  For the latter approach
to work, the structure of the object table must be known.  For this reason,
it is given here.  Note, however, that outside of the files memory.c and
unixio.c (or macio.c on the macintosh) ONLY the macros described in this
file make use of this structure: therefore modifications or even complete
replacement is possible as long as the interface remains consistent
*/

struct objectStruct {
	object class;
	short referenceCount;
	short size;
	object *memory;
	};
# define ObjectTableMax 6500

# ifdef obtalloc
extern struct objectStruct *objectTable;
# endif
# ifndef obtalloc
extern struct objectStruct objectTable[];
# endif

/*
	The most basic routines to the memory manager are incr and decr,
which increment and decrement reference counts in objects.  By separating
decrement from memory freeing, we could replace these as procedure calls
by using the following macros (thereby saving procedure calls):*/
extern object incrobj;
# define incr(x) if ((incrobj=(x))&&!isInteger(incrobj)) \
objectTable[incrobj>>1].referenceCount++
#  define decr(x) if (((incrobj=(x))&&!isInteger(incrobj))&&\
(--objectTable[incrobj>>1].referenceCount<=0)) sysDecr(incrobj);
/*
notice that the argument x is first assigned to a global variable; this is
in case evaluation of x results in side effects (such as assignment) which
should not be repeated. */

# ifndef incr
extern void incr( OBJ );
# endif
# ifndef decr
extern void decr( OBJ );
# endif

/*
	The next most basic routines in the memory module are those that
allocate blocks of storage.  There are three routines:
	allocObject(size) - allocate an array of objects
	allocByte(size) - allocate an array of bytes
	allocStr(str) - allocate a string and fill it in
again, these may be macros, or they may be actual procedure calls
*/

extern object allocObject( INT );
extern object allocByte( INT );
extern object allocStr( STR );

/*
	integer objects are (but need not be) treated specially.
In this memory manager, negative integers are just left as is, but
positive integers are changed to x*2+1.  Either a negative or an odd
number is therefore an integer, while a nonzero even number is an
object pointer (multiplied by two).  Zero is reserved for the object ``nil''
Since newInteger does not fill in the class field, it can be given here.
If it was required to use the class field, it would have to be deferred
until names.h
*/

extern object intobj;
# define isInteger(x) ((x) & 0x8001)
# define newInteger(x) ( (intobj = x)<0 ? intobj : (intobj<<1)+1 )
# define intValue(x) ( (intobj = x)<0 ? intobj : (intobj>>1) )

/*
	there are four routines used to access fields within an object.
Again, some of these could be replaced by macros, for efficiency
	basicAt(x, i) - ith field (start at 1) of object x
	basicAtPut(x, i, v) - put value v in object x
	byteAt(x, i) - ith field (start at 0) of object x
	byteAtPut(x, i, v) - put value v in object x*/

# define basicAt(x,i) (sysMemPtr(x)[i-1])
# define byteAt(x, i) ((int) ((bytePtr(x)[i-1])))

# ifndef basicAt
extern object basicAt(OBJ X INT);
# endif

# define simpleAtPut(x,i,y) (sysMemPtr(x)[i-1] = y)
# ifndef simpleAtPut
extern void simpleAtPut(OBJ X INT X OBJ);
# endif

# define basicAtPut(x,i,y) incr(simpleAtPut(x, i, y))
# ifndef basicAtPut
extern void basicAtPut(OBJ X INT X OBJ);
# endif
# define fieldAtPut(x,i,y) f_i=i; decr(basicAt(x,f_i)); basicAtPut(x,f_i,y)
# ifdef fieldAtPut
extern int f_i;
#endif
# ifndef fieldAtPut
extern void fieldAtPut(OBJ X INT X OBJ);
# endif

# ifndef byteAt
extern int byteAt(OBJ X INT);
# endif
# ifndef byteAtPut
extern void byteAtPut(OBJ X INT X INT);
# endif

/*
	Finally, a few routines (or macros) are used to access or set
class fields and size fields of objects
*/

# define classField(x) objectTable[x>>1].class
# define setClass(x,y) incr(classField(x)=y)
# define sizeField(x) objectTable[x>>1].size

# define sysMemPtr(x) objectTable[x>>1].memory
extern object sysobj;
# define memoryPtr(x) (isInteger(sysobj = x)?(object *) 0:sysMemPtr(sysobj))
# define bytePtr(x) ((byte *) memoryPtr(x))
# define charPtr(x) ((char *) memoryPtr(x))

# define nilobj (object) 0

/*
	There is a large amount of differences in the qualities of malloc
	procedures in the Unix world.  Some perform very badly when asked
	to allocate thousands of very small memory blocks, while others
	take this without any difficulty.  The routine mBlockAlloc is used
	to allocate a small bit of memory; the version given below
	allocates a large block and then chops it up as needed; if desired,
	for versions of malloc that can handle small blocks with ease
	this can be replaced using the following macro: 

# define mBlockAlloc(size) (object *) calloc((unsigned) size, sizeof(object))

	This can, and should, be replaced by a better memory management
	algorithm.
*/
# ifndef mBlockAlloc
extern object *mBlockAlloc(INT);
# endif

/*
	the dictionary symbols is the source of all symbols in the system
*/
extern object symbols;

/*
	finally some external declarations with prototypes
*/
extern noreturn sysError(STR X STR);
extern noreturn dspMethod(STR X STR);
extern noreturn initMemoryManager(NOARGS);
extern noreturn imageWrite(FILEP);
extern noreturn imageRead(FILEP);
extern boolean debugging;
