/* 
	bterp.c -- v1.430 interpreter prototype for oxcc byte codes

	Copyright (c) 1995
	Norman D. Culver dba
	Oxbow Software
	1323 S.E. 17th Street #662
	Ft. Lauderdale, FL 33316
	(305) 527-1663 Voice
	(305) 760-7584 Fax
	(305) 760-4679 Data
	norman.culver@channel1.com
	All rights reserved.

 * Redistribution and use in source and binary forms are permitted
 * provided that: (1) source distributions retain this entire copyright
 * notice and comment, and (2) distributions including binaries display
 * the following acknowledgement:  ``This product includes software
 * developed by Norman D. Culver dba Oxbow Software''
 * in the documentation or other materials provided with the distribution
 * and in all advertising materials mentioning features or use of this
 * software.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

*/

#define SUPPORT_LONG_DOUBLE 0
#define SUPPORT_LONG_LONG 0

#define NEED_BYTECODES 1
#define NEED_AOUT_FORMAT 1
#include "oxbytes.h"

#define NEED_FUNCTHUNK 1
#include "oxanf.h"
#include <stdlib.h>
#include <setjmp.h>
#include <stdio.h>

#define PROG bterp
#define USING_FRAMEWORK 1

#define SWITCHMOD 1009
typedef struct _key
{
	unsigned long k[2];
	unsigned long hv;
} KEY, *KEYP;

typedef struct _nodeS
{
	unsigned long value;
	unsigned long key[2];
	struct _nodeS *fptr;
} NodeS, *NodePS;

typedef struct _callblk {
	void *loc;
	char *es;
	unsigned short argofs;
	unsigned short flags;
	long argsiz;
	char *base_stack;			/* must be last element */
} *PCB;

typedef struct _stakblk {
	void *backlink;
	long first_loc;
	long last_loc;
	char *cbes;
	void *thunkaddr;
	int stksize;
} SB, *PSB;

typedef struct _fe {
	char *filename;
	long string_size;
	char *strings;
	char *text_start_address;
	char *data_start_address;
	char *bss_start_address;
} *FE;

typedef struct _iv {
	struct _iv *piv;
	char *text_base;
	char *dd;
	void **struclist;
	long slcnt;
	long strretcnt;
	void *allocalist;
	NodePS *swtable;
	char *chunkbase;
	void *chunklist;
	long chunksize;
	FE entry;
	char *filename;
	char *funcptr;
	Pft ft;
	long funcaddr;
	long stksiz;
	long argsiz;
	long maxes;
	char *base_stack;
	char *e_stack;
	jmp_buf jb;
#define MAX_RUNARGS 15
	long run_argcnt;
	char *run_args[MAX_RUNARGS];
	int debug;
} *Piv;

typedef struct _retval {
	void *ptr;
	long p1;
	long p2;
} RV;

int cfeprintf(const char *fmt, ...);

void _ExternCall();
void _ExternCallS();
void *oxlink_load_bare_symb(const char *symb, int dynlink);
void *oxlink_find_bare_func(const char *);
void *oxlink_find_func(const char *);
int oxlink_load_file(const char *);
int oxlink_load_object(const char *);
int oxlink_unload_file(const char *, int);
void oxlink_demand_load(void);
void oxlink_demand_noload(void);
void *oxlink_get_entry_struct(const char *filename);
char *oxlink_errstr(void);

static void* bterp_eval();

#define SZ (20)	/* size of evaluation stack frame */

#define MIN(a,b) ((a<b)?a:b)

#define SRC(a) (a<<4)
#define G1(a) ((unsigned long)*((unsigned char*)(a)))
#define G2(a) ((unsigned long)*((unsigned short*)(a)))
#define G3(a) (*((unsigned long*)(a))&0x00ffffff)
#define G4(a) (*((unsigned long*)(a)))
#define G8(a) (*((double*)(a)))
#define GX(a) (*((long double*)(a)))
#define GP1(a) ((long)*((char*)(a)))
#define GP2(a) ((long)*((short*)(a)))
#define GP3(a) ((*((long*)(a))<<8)>>8)
#define GP4(a) (*((long*)(a)))

#define NEG_ES(t) {*((t*)es)=-(*((t*)es));break;}
#define NOT_ES(t) {*((t*)es)=!(*((t*)es));break;}
#define NOT_ES1 {*es=(((long*)es)[0]==0&&((long*)es)[1]==0)?1:0;}
#define COMP_ES(t) {*((t*)es)=~(*((t*)es));break;}
#define TRUTH_ES(t) {*((long*)es)=(*((t*)es))?1:0;break;}
#define TRUTH_ES1 {*((long*)es)=(((long*)es)[0]==0&&((long*)es)[1]==0)?0:1;}
#define GT_ES(t) {*((long*)oes)=(*((t*)oes)>*((t*)es))?1:0;es=oes;break;}
#define LT_ES(t) {*((long*)oes)=(*((t*)oes)<*((t*)es))?1:0;es=oes;break;}
#define GE_ES(t) {*((long*)oes)=(*((t*)oes)>=*((t*)es))?1:0;es=oes;break;}
#define LE_ES(t) {*((long*)oes)=(*((t*)oes)<=*((t*)es))?1:0;es=oes;break;}
#define NE_ES(t) {*((long*)oes)=(*((t*)oes)!=*((t*)es))?1:0;es=oes;break;}
#define EQ_ES(t) {*((long*)oes)=(*((t*)oes)==*((t*)es))?1:0;es=oes;break;}
#define ADD_ES(t) {*((t*)oes)+=*((t*)es);es=oes;break;}
#define SUB_ES(t) {*((t*)oes)-=*((t*)es);es=oes;break;}
#define MUL_ES(t) {*((t*)oes)*=*((t*)es);es=oes;break;}
#define DIV_ES(t) {*((t*)oes)/=*((t*)es);es=oes;break;}
#define OR_ES(t) {*((t*)oes)|=*((t*)es);es=oes;break;}
#define AND_ES(t) {*((t*)oes)&=*((t*)es);es=oes;break;}
#define XOR_ES(t) {*((t*)oes)^=*((t*)es);es=oes;break;}
#define MOD_ES(t) {*((t*)oes)%=*((unsigned long*)es);es=oes;break;}
#define MODL_ES(t) {*((t*)oes)%=*((unsigned long long*)es);es=oes;break;}
#define MODI_ES(t) {*((t*)es)%=*((unsigned short*)np);pc+=2;break;}
#define RSH_ES(t) {*((t*)oes)>>=*((unsigned char*)es);es=oes;break;}
#define LSH_ES(t) {*((t*)oes)<<=*((unsigned char*)es);es=oes;break;}
#define RSHI_ES(t) {*((t*)es)>>=*np;pc+=1;break;}
#define LSHI_ES(t) {*((t*)es)<<=*np;pc+=1;break;}
#define DEREF_ES(t) {*((long*)es)=**((t**)es);break;}
#define UDEREF_ES(t) {*((unsigned long*)es)=**((t**)es);break;}
#define FDEREF_ES(t) {*((t*)es)=**((t**)es);break;}
#define DEREF1_ES(t) {*((long*)nes)=**((t**)es);es=nes;break;}
#define UDEREF1_ES(t) {*((unsigned long*)nes)=**((t**)es);es=nes;break;}
#define FDEREF1_ES(t) {*((t*)nes)=**((t**)es);es=nes;break;}
#define SIGNE if(*(es+3)&0x80)*((long*)(es+4))=-1;else *((long*)(es+4))=0
#define USIGNE *((long*)(es+4))=0
#define LOADI1() {*((long*)nes)=GP1(np);es=nes;SIGNE;pc+=1;break;}
#define LOADI2() {*((long*)nes)=GP2(np);es=nes;SIGNE;pc+=2;break;}
#define LOADI4() {*((long*)nes)=GP4(np);es=nes;SIGNE;pc+=4;break;}
#define LOADUI1() {*((unsigned long*)nes)=G1(np);es=nes;USIGNE;pc+=1;break;}
#define LOADUI2() {*((unsigned long*)nes)=G2(np);es=nes;USIGNE;pc+=2;break;}
#define LOADUI4() {*((unsigned long*)nes)=G4(np);es=nes;USIGNE;pc+=4;break;}
#define LOADI8() {*((double*)nes)=G8(np);es=nes;pc+=8;break;}
#define LOADIX() {*((long double*)nes)=GX(np);es=nes;pc+=XSZ;break;}

#define LOADADDRI1() {*((unsigned long*)nes)=(G1(np)<<2);es=nes;pc+=1;break;}
#define LOADADDRI2() {*((unsigned long*)nes)=(G2(np)<<2);es=nes;pc+=2;break;}
#define LOADADDRI3() {*((unsigned long*)nes)=(G3(np)<<0);es=nes;pc+=3;break;}
#define LOADADDRI4() {*((unsigned long*)nes)=G4(np);es=nes;pc+=4;break;}

#define LOADSTK1(t) {*((t*)nes)=*((t*)(fs+(G1(np)<<2)));es=nes;pc+=1;break;}
#define LOADSTK2(t) {*((t*)nes)=*((t*)(fs+(G2(np)<<2)));es=nes;pc+=2;break;}
#define LOADSTK3(t) {*((t*)nes)=*((t*)(fs+(G3(np)<<0)));es=nes;pc+=3;break;}

#define STORSTK1(t) {*((t*)(fs+(G1(np)<<2)))=*((t*)es);es=oes;pc+=1;break;}
#define STORSTK2(t) {*((t*)(fs+(G2(np)<<2)))=*((t*)es);es=oes;pc+=2;break;}
#define STORSTK3(t) {*((t*)(fs+(G3(np)<<0)))=*((t*)es);es=oes;pc+=3;break;}

#define STORSTKI1(t) {*((t*)(fs+(G1(np)<<2)))=*((t*)(np+1));pc+=sizeof(t)+1;break;}
#define STORSTKI2(t) {*((t*)(fs+(G2(np)<<2)))=*((t*)(np+2));pc+=sizeof(t)+2;break;}
#define STORSTKI3(t) {*((t*)(fs+(G3(np)<<0)))=*((t*)(np+3));pc+=sizeof(t)+3;break;}

#define LOADMEM1(t) {*((t*)nes)=*((t*)(dd+(G1(np)<<2)));es=nes;pc+=1;break;}
#define LOADMEM2(t) {*((t*)nes)=*((t*)(dd+(G2(np)<<2)));es=nes;pc+=2;break;}
#define LOADMEM3(t) {*((t*)nes)=*((t*)(dd+(G3(np)<<0)));es=nes;pc+=3;break;}
#define LOADMEM4(t) {*((t*)nes)=*((t*)((void*)G4(np)));es=nes;pc+=4;break;}

#define STORMEM1(t) {*((t*)(dd+(G1(np)<<2)))=*((t*)es);es=oes;pc+=1;break;}
#define STORMEM2(t) {*((t*)(dd+(G2(np)<<2)))=*((t*)es);es=oes;pc+=2;break;}
#define STORMEM3(t) {*((t*)(dd+(G3(np)<<2)))=*((t*)es);es=oes;pc+=3;break;}
#define STORMEM4(t) {*((t*)((void*)G4(np)))=*((t*)es);es=oes;pc+=4;break;}

#define STORMEMI1(t) {*((t*)(dd+(G1(np)<<2)))=*((t*)(np+1));pc+=sizeof(t)+1;break;}
#define STORMEMI2(t) {*((t*)(dd+(G2(np)<<2)))=*((t*)(np+2));pc+=sizeof(t)+2;break;}
#define STORMEMI3(t) {*((t*)(dd+(G3(np)<<0)))=*((t*)(np+3));pc+=sizeof(t)+3;break;}
#define STORMEMI4(t) {*((t*)((void*)G4(np)))=*((t*)(np+4));pc+=sizeof(t)+4;break;}

static unsigned long bfields[33] = {
	0x00000000,0x00000001,0x00000003,0x00000007,
	0x0000000f,0x0000001f,0x0000003f,0x0000007f,
	0x000000ff,0x000001ff,0x000003ff,0x000007ff,
	0x00000fff,0x00001fff,0x00003fff,0x00007fff,
	0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff,
	0x000fffff,0x001fffff,0x003fffff,0x007fffff,
	0x00ffffff,0x01ffffff,0x03ffffff,0x07ffffff,
	0x0fffffff,0x1fffffff,0x3fffffff,0x7fffffff,
	0xfffffff
	};
static unsigned long tsfields[33] = {
	0x00000000,0x00000001,0x00000002,0x00000004,
	0x00000008,0x00000010,0x00000020,0x00000040,
	0x00000080,0x00000100,0x00000200,0x00000400,
	0x00000800,0x00001000,0x00002000,0x00004000,
	0x00008000,0x00010000,0x00020000,0x00040000,
	0x00080000,0x00100000,0x00200000,0x00400000,
	0x00800000,0x01000000,0x02000000,0x04000000,
	0x08000000,0x10000000,0x20000000,0x40000000,
	0x80000000
	};
static unsigned long sfields[33] = {
	0x00000000,0xfffffffe,0xfffffffc,0xfffffff8,
	0xfffffff0,0xffffffe0,0xffffffc0,0xffffff80,
	0xffffff00,0xfffffe00,0xfffffc00,0xfffff800,
	0xfffff000,0xffffe000,0xffffc000,0xffff8000,
	0xffff0000,0xfffe0000,0xfffc0000,0xfff80000,
	0xfff00000,0xffe00000,0xffc00000,0xff800000,
	0xff000000,0xfe000000,0xfc000000,0xf8000000,
	0xf0000000,0xe0000000,0xc0000000,0x80000000,
	0x80000000
	};
#if SUPPORT_LONG_LONG
static unsigned long long lbfields[65] = {
	0x0000000000000000LL,0x0000000000000001LL,0x0000000000000003LL,0x0000000000000007LL,
	0x000000000000000fLL,0x000000000000001fLL,0x000000000000003fLL,0x000000000000007fLL,
	0x00000000000000ffLL,0x00000000000001ffLL,0x00000000000003ffLL,0x00000000000007ffLL,
	0x0000000000000fffLL,0x0000000000001fffLL,0x0000000000003fffLL,0x0000000000007fffLL,
	0x000000000000ffffLL,0x000000000001ffffLL,0x000000000003ffffLL,0x000000000007ffffLL,
	0x00000000000fffffLL,0x00000000001fffffLL,0x00000000003fffffLL,0x00000000007fffffLL,
	0x0000000000ffffffLL,0x0000000001ffffffLL,0x0000000003ffffffLL,0x0000000007ffffffLL,
	0x000000000fffffffLL,0x000000001fffffffLL,0x000000003fffffffLL,0x000000007fffffffLL,
	0x00000000ffffffffLL,0x00000001ffffffffLL,0x00000003ffffffffLL,0x00000007ffffffffLL,
	0x0000000fffffffffLL,0x0000001fffffffffLL,0x0000003fffffffffLL,0x0000007fffffffffLL,
	0x000000ffffffffffLL,0x000001ffffffffffLL,0x000003ffffffffffLL,0x000007ffffffffffLL,
	0x00000fffffffffffLL,0x00001fffffffffffLL,0x00003fffffffffffLL,0x00007fffffffffffLL,
	0x0000ffffffffffffLL,0x0001ffffffffffffLL,0x0003ffffffffffffLL,0x0007ffffffffffffLL,
	0x000fffffffffffffLL,0x001fffffffffffffLL,0x003fffffffffffffLL,0x007fffffffffffffLL,
	0x00ffffffffffffffLL,0x01ffffffffffffffLL,0x03ffffffffffffffLL,0x07ffffffffffffffLL,
	0x0fffffffffffffffLL,0x1fffffffffffffffLL,0x3fffffffffffffffLL,0x7fffffffffffffffLL,
	0xfffffffffffffffLL
	};
static unsigned long long ltsfields[65] = {
	0x0000000000000000LL,0x0000000000000001LL,0x0000000000000002LL,0x0000000000000004LL,
	0x0000000000000008LL,0x0000000000000010LL,0x0000000000000020LL,0x0000000000000040LL,
	0x0000000000000080LL,0x0000000000000100LL,0x0000000000000200LL,0x0000000000000400LL,
	0x0000000000000800LL,0x0000000000001000LL,0x0000000000002000LL,0x0000000000004000LL,
	0x0000000000008000LL,0x0000000000010000LL,0x0000000000020000LL,0x0000000000040000LL,
	0x0000000000080000LL,0x0000000000100000LL,0x0000000000200000LL,0x0000000000400000LL,
	0x0000000000800000LL,0x0000000001000000LL,0x0000000002000000LL,0x0000000004000000LL,
	0x0000000008000000LL,0x0000000010000000LL,0x0000000020000000LL,0x0000000040000000LL,
	0x0000000080000000LL,0x0000000100000000LL,0x0000000200000000LL,0x0000000400000000LL,
	0x0000000800000000LL,0x0000001000000000LL,0x0000002000000000LL,0x0000004000000000LL,
	0x0000008000000000LL,0x0000010000000000LL,0x0000020000000000LL,0x0000040000000000LL,
	0x0000080000000000LL,0x0000100000000000LL,0x0000200000000000LL,0x0000400000000000LL,
	0x0000800000000000LL,0x0001000000000000LL,0x0002000000000000LL,0x0004000000000000LL,
	0x0008000000000000LL,0x0010000000000000LL,0x0020000000000000LL,0x0040000000000000LL,
	0x0080000000000000LL,0x0100000000000000LL,0x0200000000000000LL,0x0400000000000000LL,
	0x0800000000000000LL,0x1000000000000000LL,0x2000000000000000LL,0x4000000000000000LL,
	0x8000000000000000LL
	};
static unsigned long long lsfields[65] = {
	0x0000000000000000LL,0xfffffffffffffffeLL,0xfffffffffffffffcLL,0xfffffffffffffff8LL,
	0xfffffffffffffff0LL,0xffffffffffffffe0LL,0xffffffffffffffc0LL,0xffffffffffffff80LL,
	0xffffffffffffff00LL,0xfffffffffffffe00LL,0xfffffffffffffc00LL,0xfffffffffffff800LL,
	0xfffffffffffff000LL,0xffffffffffffe000LL,0xffffffffffffc000LL,0xffffffffffff8000LL,
	0xffffffffffff0000LL,0xfffffffffffe0000LL,0xfffffffffffc0000LL,0xfffffffffff80000LL,
	0xfffffffffff00000LL,0xffffffffffe00000LL,0xffffffffffc00000LL,0xffffffffff800000LL,
	0xffffffffff000000LL,0xfffffffffe000000LL,0xfffffffffc000000LL,0xfffffffff8000000LL,
	0xfffffffff0000000LL,0xffffffffe0000000LL,0xffffffffc0000000LL,0xffffffff80000000LL,
	0xffffffff00000000LL,0xfffffffe00000000LL,0xfffffffc00000000LL,0xfffffff800000000LL,
	0xfffffff000000000LL,0xffffffe000000000LL,0xffffffc000000000LL,0xffffff8000000000LL,  
	0xffffff0000000000LL,0xfffffe0000000000LL,0xfffffc0000000000LL,0xfffff80000000000LL,
	0xfffff00000000000LL,0xffffe00000000000LL,0xffffc00000000000LL,0xffff800000000000LL,
	0xffff000000000000LL,0xfffe000000000000LL,0xfffc000000000000LL,0xfff8000000000000LL,
	0xfff0000000000000LL,0xffe0000000000000LL,0xffc0000000000000LL,0xff80000000000000LL,
	0xff00000000000000LL,0xfe00000000000000LL,0xfc00000000000000LL,0xf800000000000000LL,
	0xf000000000000000LL,0xe000000000000000LL,0xc000000000000000LL,0x8000000000000000LL,
	0x8000000000000000LL
	};
#endif /* SUPPORT_LONG_LONG */

static char thunk386[41] =
{/* THE EXTERNAL CALLBACK THUNK (Intel 386) */
	0x55,				/* pushl %ebp */
	0x89,0xE5,			/* movl %esp,%ebp */
	0x68,0,0,0,0,		/* pushl ft */
	0x68,0,0,0,0,		/* pushl iv */
	0x68,0,0,0,0,		/* pushl base_stack */
	0x83,0xEC,0x0C,		/* subl $12,%esp (RETVAL) */
	0xB8,0,0,0,0,		/* movl _bterp_handle_callbacks,%eax */
	0xFF,0xD0,			/* call *%eax */
	0x8B,0x04,0x24,		/* movl (%esp),%eax	 */
	0x8B,0x54,0x24,0x04,/* movl 4(%esp),%edx */
	0x8B,0x4C,0x24,0x08,/* movl 8(%esp),%ecx */
	0xC9,				/* leave */
	0xC3				/* ret */
};
static char thunk386S[42] =
{/* THE EXTERNAL CALLBACK THUNK (Intel 386) */
	0x55,				/* pushl %ebp */
	0x89,0xE5,			/* movl %esp,%ebp */
	0x68,0,0,0,0,		/* pushl ft */
	0x68,0,0,0,0,		/* pushl iv */
	0x68,0,0,0,0,		/* pushl base_stack */
	0x83,0xEC,0x0C,		/* subl $12,%esp (RETVAL) */
	0xB8,0,0,0,0,		/* movl _bterp_handle_callbacks,%eax */
	0xFF,0xD0,			/* call *%eax */
	0x8B,0x04,0x24,		/* movl (%esp),%eax	 */
	0x8B,0x54,0x24,0x04,/* movl 4(%esp),%edx */
	0x8B,0x4C,0x24,0x08,/* movl 8(%esp),%ecx */
	0xC9,				/* leave */
	0xC2,0x04			/* ret $4 (prune structret arg, GCC convention) */
};



/* ========================== INTERPRETER CODE =========================== */

static int
findswitch(Piv iv, unsigned long *key, void *result)
{
int bin;
NodePS node;
	bin = (((~key[0] ^ key[1]) * 1103515245UL) + 12345) % SWITCHMOD;
	if((node = iv->swtable[bin]))
	{
		do {
			if(node->key[0] == key[0] && node->key[1] == key[1])
			{
				*((NodePS*)result) = node;
				return 1;
			}
		} while((node = node->fptr));
	}
	return 0;
}
static void *
new_Snode(Piv iv)
{
void *p;

	if(iv->chunksize < sizeof(NodeS))
	{
		iv->chunkbase = calloc(1, 4088);
		*((void**)iv->chunkbase) = iv->chunklist;
		iv->chunklist = iv->chunkbase;
		iv->chunkbase += sizeof(void*);
		iv->chunksize = 4088 - sizeof(void*);
	}
	p = iv->chunkbase;
	iv->chunkbase += sizeof(NodeS);
	iv->chunksize -= sizeof(NodeS);
	return p;
}
static void
saveswitch(Piv iv, unsigned long *key, long value)
{
int bin;
NodePS node;

	bin = (((~key[0] ^ key[1]) * 1103515245UL) + 12345) % SWITCHMOD;
	node = new_Snode(iv);
	node->key[0] = key[0];
	node->key[1] = key[1];
	node->value = value;
	node->fptr = iv->swtable[bin];
	iv->swtable[bin] = node;
}
static void
purge_allocas(Piv iv, void *last)
{
	while(iv->allocalist && (iv->allocalist != last))
	{
	void *this = iv->allocalist;
		iv->allocalist = *((void**)this);
		free(this);
	}
}

static void
_bterp_handle_callbacks(RV retval, char *base_stack, Piv iv, Pft ft, 
							int ebp, int pret, ...)
{/* Call to interpreted function from external function */
unsigned short fmods;
long stksiz, maxes, strucsiz, fs_size, argofs, es_beg, argsiz;
int hidden;
void *strret = 0;
void *argaddr, *e_stack, *lastalloca;
void *pes;
DATUM lastval;

	fmods = ft->fmods;
	argaddr = ((long*)&pret)+1;
	if((hidden = (fmods & Fretstr)))
	{/* return a struct or union */
		strucsiz = ft->retsiz<<2;
		strret = (void*) *(((long*)&pret)+1);
	}
	stksiz = ft->stksiz<<2;
	maxes = ft->maxes;
	argsiz = ft->argsiz<<2;

	if(fmods & Fnested)
	{/* callback to nested function */
		argofs = ft->stkbeg+(stksiz-argsiz-hidden);
		e_stack = ((PSB)base_stack)->cbes;
	}
	else
	{/* callback to non-nested function */
		if(fmods & Fellipsis)
			argsiz += 128;
		es_beg = stksiz+hidden+argsiz;
		argofs = stksiz;
		fs_size = es_beg + ((maxes+6)*SZ);

		/* Create a stack for the called function */
		base_stack = calloc(1, fs_size + sizeof(SB));
		e_stack = base_stack + sizeof(SB) + es_beg;
		((PSB)base_stack)->stksize = fs_size + sizeof(SB);
	}

	/* Copy the callers arguments */
	memcpy(base_stack+argofs+sizeof(SB),argaddr,argsiz+hidden);

	/* EVALUATE THE FUNCTION */
	lastalloca = iv->allocalist;
	pes = bterp_eval(iv, ft->funcaddr, base_stack, e_stack,
		base_stack+((PSB)base_stack)->stksize);

	/* Transfer the return value */
#if SUPPORT_LONG_DOUBLE
	lastval.Ulongdouble = *((long double*)pes);
#else
	lastval.Udouble = *((double*)pes);
#endif

	/* Return to callers' stack */
	if(fmods & Fretdbl)
	{
		asm ("fldl %0" :: "g"(lastval.Udouble));
	}
	else if(fmods & Fretflt)
	{
		asm ("flds %0" :: "g"(lastval.Ufloat));
	}
	else if(fmods & Fretldbl)
	{
#if SUPPORT_LONG_DOUBLE
#else
		asm ("fldl %0" :: "g"(lastval.Udouble));
#endif
	}
	else if(strret)
	{/* return pointer to struct */
		retval.ptr = strret;
		/* GCC needs this */
		memcpy(&retval.p1, strret, MIN(8,strucsiz));	/* 8 bytes of struct */
	}
	else
	{
		retval.ptr = lastval.Upointer;
		retval.p1 = lastval.lng.d[1];
		retval.p2 = lastval.lng.d[2];
	}
	purge_allocas(iv, lastalloca);
	if(fmods & Fnested)
	{
#if 0
		*((char**)pes) -= SZ;
#endif
		ft->fmods &= ~Fthunked;
		free(((PSB)base_stack)->thunkaddr);
	}
	else
	{
		free(base_stack);
	}
}

static void *
make_callback_thunk(Piv iv, void *base_stack, Pft ft)
{
char *pth;

	if(ft->fmods & Fretstr)
	{
		pth = malloc(sizeof(thunk386S));
		memcpy(pth, thunk386S, sizeof(thunk386S));
	}
	else
	{
		pth = malloc(sizeof(thunk386));
		memcpy(pth, thunk386, sizeof(thunk386));
	}
	*((long*)&pth[4]) = (long)ft;
	*((long*)&pth[9]) = (long)iv;
	*((long*)&pth[14]) = (long)base_stack;
	*((long*)&pth[22]) = (long)_bterp_handle_callbacks;

	return pth;
}
static void
ensure_strrets(Piv iv)
{
	if(iv->strretcnt+1 >= iv->slcnt) {
		iv->slcnt += 128;
		iv->struclist = realloc(iv->struclist, iv->slcnt * sizeof(void *));
	}
}
static void
prune_structs(Piv iv)
{
	while(iv->strretcnt > 0)
	{
		free(iv->struclist[--iv->strretcnt]);
	}
}
static int
mover(Piv iv, unsigned char *dp, int opcode, int size, char *src, char *dst)
{
char *s, *d;
int bump;

	switch(opcode)
	{
		case S1|D1:
			s = src+(G1(dp)<<2);
			d = dst+(G1(dp+1)<<2);
			bump = 3;
			break;
		case S1|D2:
			s = src+(G1(dp)<<2);
			d = dst+(G2(dp+1)<<2);
			bump = 4;
			break;
		case S1|D3:
			s = src+(G1(dp)<<2);
			d = dst+(G3(dp+1)<<0);
			bump = 5;
			break;
		case S1|D4:
			s = src+(G1(dp)<<2);
			d = (void*)G4(dp+1);
			bump = 6;
			break;
		case S2|D1:
			s = src+(G2(dp)<<2);
			d = dst+G1(dp+2);
			bump = 4;
			break;
		case S2|D2:
			s = src+(G2(dp)<<2);
			d = dst+(G2(dp+2)<<2);
			bump = 5;
			break;
		case S2|D3:
			s = src+(G2(dp)<<2);
			d = dst+(G3(dp+2)<<0);
			bump = 6;
			break;
		case S2|D4:
			s = src+(G2(dp)<<2);
			d = (void*)G4(dp+2);
			bump = 7;
			break;
		case S3|D1:
			s = src+(G3(dp)<<0);
			d = dst+(G1(dp+3)<<2);
			bump = 5;
			break;
		case S3|D2:
			s = src+(G3(dp)<<0);
			d = dst+(G2(dp+3)<<2);
			bump = 6;
			break;
		case S3|D3:
			s = src+(G3(dp)<<0);
			d = dst+(G3(dp+3)<<0);
			bump = 7;
			break;
		case S3|D4:
			s = src+(G3(dp)<<0);
			d = (void*)G4(dp+3);
			bump = 8;
			break;
		case S4|D1:
			s = (void*)G4(dp);
			d = dst+(G1(dp+4)<<2);
			bump = 6;
			break;
		case S4|D2:
			s = (void*)G4(dp);
			d = dst+(G2(dp+4)<<2);
			bump = 7;
			break;
		case S4|D3:
			s = (void*)G4(dp);
			d = dst+(G3(dp+4)<<0);
			bump = 8;
			break;
		case S4|D4:
			s = (void*)G4(dp);
			d = (void*)G4(dp+4);
			bump = 9;
			break;
		default:
			printf("bterp: BAD OPCODE for `mover' at pc=%x\n",
				((char*)dp - iv->text_base) - 1);
			longjmp(iv->jb, 1);			
	}
	if(size == B1)
		*((char*)d) = *((char*)s);
	else if(size == B2)
		*((short*)d) = *((short*)s);
	else if(size == B4)
		*((long*)d) = *((long*)s);
	else if(size == B8)
		*((double*)d) = *((double*)s);
	else
		memcpy(d,s,XSZ);
	return bump;
}
static void
load_efunc(Piv iv, Pft ft)
{
void *e_faddr;
	if(!(e_faddr = oxlink_find_bare_func((void*)ft->funcaddr)))
	{/* not in core, load the file */
		if(!(e_faddr = oxlink_load_bare_symb((void*)ft->funcaddr, 1)))
		{
			printf("bterp: Can't load function `%s'\n", (char*)ft->funcaddr);
			exit(1);
		}
	}
	ft->funcaddr = (long)e_faddr;
	ft->fmods |= Fthunked;
}
/* MOST OF THESE BUILTINS ARE UNNECESSARY DEMOS */
/* PUT ANYTHING THAT COULD BE CALLED THROUGH A FUNCPTR HERE */
static int
do_builtin(Piv iv, unsigned char code, char **pes)
{/* NOTE: builtins with no args and void return must leave a pseudo ret on stack */
char *es, *oes;
	es = *pes;
	oes = es-SZ;

	switch(code)
	{
		case ALLOCA:
		{
		char *abuf = malloc(*((unsigned*)es)+sizeof(void*));
			*((void**)es) = abuf+sizeof(void*);
			*((void**)abuf) = iv->allocalist;
			iv->allocalist = abuf;
			return 1;
		}
		case STRLEN:
		{
			*((unsigned*)es) = strlen(*((const char**)es));
			return 1;
		}
		case STRCPY:
		{
			*((void**)oes) = strcpy(*((char**)oes),*((const char**)es));
			*pes = oes;
			return 1;
		}
		case STRCAT:
		{
			*((void**)oes) = strcat(*((char**)oes),*((const char**)es));
			*pes = oes;
			return 1;
		}
		case MEMCPY:
		{
			*((void**)(oes-SZ)) = memcpy(*((void**)(oes-SZ)),*((const void**)oes),*((unsigned*)es));
			*pes -= 2*SZ;
			return 1;
		}
		case MEMMOVE:
		{
			*((void**)(oes-SZ)) = memmove(*((void**)(oes-SZ)),*((const void**)oes),*((unsigned*)es));
			*pes -= 2*SZ;
			return 1;
		}
		case MEMSET:
		{
			*((void**)(oes-SZ)) = 
				memset(*((void**)(oes-SZ)),*((int*)oes),*((long*)es));
			*pes -= 2*SZ;
			return 1;
		}
		case BZERO:
		{
			memset(*((void**)oes),0,*((unsigned*)es));
			*pes = oes;
			return 0;
		}
		case DEBUG:
		{
			iv->debug=1;
			printf("DEBUG ON\n");
			fflush(stdout);
			*pes = es+SZ;	/* pseudo ret */
			return 0;
		}
		case NODEBUG:
		{
			iv->debug=0;
			printf("DEBUG OFF\n");
			fflush(stdout);
			*pes = es+SZ;	/* pseudo ret */
			return 0;
		}
		case MALLOC:
		{
			*((void**)es) = malloc(*((unsigned*)es));
			return 1;
		}
		case CALLOC:
		{
			*((void**)oes) = calloc(*((unsigned*)oes),*((unsigned*)es));
			*pes = oes;
			return 1;
		}
		case REALLOC:
		{
			*((void**)oes) = realloc(*((void**)oes),*((unsigned*)es));
			*pes = oes;
			return 1;
		}
	}	
	return 0;
}
static void *
bterp_eval(Piv iv, long pc_offset, void *base_stack, char *es, char *eb)
{
char *dd, *fs;
unsigned char *pc, *np;
char *oes, *nes, *bes;
long first_loc = -100;
long last_loc;

  dd = iv->dd;
  fs = base_stack + sizeof(SB);
  pc = iv->text_base + pc_offset;
  bes = es;

if(iv->debug) {
printf("FUNCTION ofs=%x pc=%p bs=%p fs=%p es=%p dd=%p eb=%p\n", 
pc_offset, pc, base_stack, fs, es, dd, eb);
fflush(stdout);
}
  for(;;++pc)
  {
	np = pc+1;
	oes = es-SZ;
	nes = es+SZ;

if(iv->debug) {
printf("ofs:%x op=%x es=%p val=%x oval=%x nval=%x\n",
((char*)pc) - iv->text_base, *pc, es, 
*((long*)es), *((long*)oes), *((long*)nes));
fflush(stdout);
}
if(es < bes) {
printf("STACK UNDERFLOW ofs:%x op=%x es=%p val=%x oval=%x nval=%x\n",
((char*)pc) - iv->text_base, *pc, es, *((long*)es), *((long*)oes), *((long*)nes));
fflush(stdout);
longjmp(iv->jb, (int)es);
}
if(es >= eb) {
printf("STACK OVERFLOW ofs:%x op=%x es=%p val=%x oval=%x nval=%x\n",
((char*)pc) - iv->text_base, *pc, es, *((long*)es), *((long*)oes), *((long*)nes));
fflush(stdout);
longjmp(iv->jb, (int)es);
}
	switch(*pc)
	{
		case LOCATE|J1:
		{
			last_loc = ((char*)(pc + GP1(np))) - iv->text_base;
			if(first_loc == -100) first_loc = last_loc; 			
			++pc;
			break;
		}
		case LOCATE|J2:
		{
			last_loc = ((char*)(pc + GP2(np))) - iv->text_base;
			if(first_loc == -100) first_loc = last_loc; 			
			pc += 2;
			break;
		}
		case LOCATE|J3:
		{
			last_loc = ((char*)(pc + GP3(np))) - iv->text_base;
			if(first_loc == -100) first_loc = last_loc; 			
			pc += 3;
			break;
		}
		case LOCATE|J4:
		{
			last_loc = ((char*)(pc + GP4(np))) - iv->text_base;
			if(first_loc == -100) first_loc = last_loc; 			
			pc += 4;
			break;
		}
		case LS|A1|B1:
			LOADSTK1(char);
		case LS|A1|B2:
			LOADSTK1(short);
		case LS|A1|B4:
			LOADSTK1(long);
		case LS|A1|B8:
			LOADSTK1(double);
		case LS|A2|B1:
			LOADSTK2(char);
		case LS|A2|B2:
			LOADSTK2(short);
		case LS|A2|B4:
			LOADSTK2(long);
		case LS|A2|B8:
			LOADSTK2(double);
		case LS|A3|B1:
			LOADSTK3(char);
		case LS|A3|B2:
			LOADSTK3(short);
		case LS|A3|B4:
			LOADSTK3(long);
		case LS|A3|B8:
			LOADSTK3(double);

		case NEG|BYTE:
			NEG_ES(char);
		case NEG|SHORT:
			NEG_ES(short);
		case NEG|LONG:
			NEG_ES(long);
		case NEG|UBYTE:
			NEG_ES(unsigned char);
		case NEG|USHORT:
			NEG_ES(unsigned short);
		case NEG|ULONG:
			NEG_ES(unsigned long);
		case NEG|FLOAT:
			NEG_ES(float);
		case NEG|DOUBLE:
			NEG_ES(double);

		case LM|A1|B1:
			LOADMEM1(char);
		case LM|A1|B2:
			LOADMEM1(short);
		case LM|A1|B4:
			LOADMEM1(long);
		case LM|A1|B8:
			LOADMEM1(double);
		case LM|A2|B1:
			LOADMEM2(char);
		case LM|A2|B2:
			LOADMEM2(short);
		case LM|A2|B4:
			LOADMEM2(long);
		case LM|A2|B8:
			LOADMEM2(double);
		case LM|A3|B1:
			LOADMEM3(char);
		case LM|A3|B2:
			LOADMEM3(short);
		case LM|A3|B4:
			LOADMEM3(long);
		case LM|A3|B8:
			LOADMEM3(double);
		case LM|A4|B1:
			LOADMEM4(char);
		case LM|A4|B2:
			LOADMEM4(short);
		case LM|A4|B4:
			LOADMEM4(long);
		case LM|A4|B8:
			LOADMEM4(double);

		case COMP|B1:
			COMP_ES(char);
		case COMP|B2:
			COMP_ES(short);
		case COMP|B4:
			COMP_ES(long);
#if SUPPORT_LONG_LONG
		case COMP|B8:
			COMP_ES(long long);
#else
#endif
		case JMP|J1:
			pc += GP1(np)-1;
			break;
		case JMP|J2:
			pc += GP2(np)-1;
			break;
		case JMP|J3:
			pc += GP3(np)-1;
			break;
		case JMP|J4:
			pc += GP4(np)-1;
			break;

		case LJMPT|J1:
			if(*(es)) pc += GP1(np)-1;
			else {pc += 1; es=oes;}
			break;
		case LJMPT|J2:
			if(*(es)) pc += GP2(np)-1;
			else {pc += 2; es=oes;}
			break;
		case LJMPT|J3:
			if(*(es)) pc += GP3(np)-1;
			else {pc += 3; es=oes;}
			break;
		case LJMPT|J4:
			if(*(es)) pc += GP4(np)-1;
			else {pc += 4; es=oes;}
			break;

		case JMPT|J1:
			if(*(es)) pc += GP1(np)-1;
			else pc += 1;
			es = oes;
			break;
		case JMPT|J2:
			if(*(es)) pc += GP2(np)-1;
			else pc += 2;
			es = oes;
			break;
		case JMPT|J3:
			if(*(es)) pc += GP3(np)-1;
			else pc += 3;
			es = oes;
			break;
		case JMPT|J4:
			if(*(es)) pc += GP4(np)-1;
			else pc += 4;
			es = oes;
			break;

		case LJMPF|J1:
			if(!*(es)) pc += GP1(np)-1;
			else {pc += 1; es=oes;}
			break;
		case LJMPF|J2:
			if(!*(es)) pc += GP2(np)-1;
			else {pc += 2; es=oes;};
			break;
		case LJMPF|J3:
			if(!*(es)) pc += GP3(np)-1;
			else {pc += 3; es=oes;}
			break;
		case LJMPF|J4:
			if(!*(es)) pc += GP4(np)-1;
			else {pc +=4; es=oes;}
			break;

		case JMPF|J1:
			if(!*(es)) pc += GP1(np)-1;
			else pc += 1;
			es = oes;
			break;
		case JMPF|J2:
			if(!*(es)) pc += GP2(np)-1;
			else pc += 2;
			es = oes;
			break;
		case JMPF|J3:
			if(!*(es)) pc += GP3(np)-1;
			else pc += 3;
			es = oes;
			break;
		case JMPF|J4:
			if(!*(es)) pc += GP4(np)-1;
			else pc +=4;
			es = oes;
			break;

		case NOT|B1:
			NOT_ES(char);
		case NOT|B2:
			NOT_ES(short);
		case NOT|B4:	/* also FLOAT */
			NOT_ES(long);
		case NOT|B8:	/* also DOUBLE */
#if SUPPORT_LONG_LONG
			NOT_ES(long long);
#else
			NOT_ES1;
#endif
		case SS|A1|B1:
			STORSTK1(char);
		case SS|A1|B2:
			STORSTK1(short);
		case SS|A1|B4:
			STORSTK1(long);
		case SS|A1|B8:
			STORSTK1(double);
		case SS|A2|B1:
			STORSTK2(char);
		case SS|A2|B2:
			STORSTK2(short);
		case SS|A2|B4:
			STORSTK2(long);
		case SS|A2|B8:
			STORSTK2(double);
		case SS|A3|B1:
			STORSTK3(char);
		case SS|A3|B2:
			STORSTK3(short);
		case SS|A3|B4:
			STORSTK3(long);
		case SS|A3|B8:
			STORSTK3(double);

		case TRUTHOF|B2:
			TRUTH_ES(short);
		case TRUTHOF|B4:		/* also FLOAT */
			TRUTH_ES(long);
		case TRUTHOF|B8:		/* also DOUBLE */
#if SUPPORT_LONG_LONG
			TRUTH_ES(long long);
#else
			TRUTH_ES1;
#endif
		case CVT:
		{
			++pc;
			++np;
			switch(*pc)
			{
				case SRC(BYTE)|SHORT:
					*((short*)es) = *((char*)es);
					break;
				case SRC(BYTE)|LONG:
					*((long*)es) = *((char*)es);
					break;
				case SRC(BYTE)|USHORT:
					*((unsigned short*)es) = *((char*)es);
					break;
				case SRC(BYTE)|ULONG:
					*((unsigned long*)es) = *((char*)es);
					break;
				case SRC(BYTE)|FLOAT:
					*((float*)es) = *((char*)es);
					break;
				case SRC(BYTE)|DOUBLE:
					*((double*)es) = *((char*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(BYTE)|CLONGLONG:
					*((long long*)es) = *((char*)es);
					break;
				case SRC(BYTE)|CULONGLONG:
					*((unsigned long long*)es) = *((char*)es);
					break;
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(BYTE)|CLONGDOUBLE:
					*((long double*)es) = *((char*)es);
					break;
#endif
				case SRC(SHORT)|LONG:
					*((long*)es) = *((short*)es);
					break;
				case SRC(SHORT)|ULONG:
					*((unsigned long*)es) = *((short*)es);
					break;
				case SRC(SHORT)|FLOAT:
					*((float*)es) = *((short*)es);
					break;
				case SRC(SHORT)|DOUBLE:
					*((double*)es) = *((short*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(SHORT)|CLONGLONG:
					*((long long*)es) = *((short*)es);
					break;
				case SRC(SHORT)|CULONGLONG:
					*((unsigned long long*)es) = *((short*)es);
					break;
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(SHORT)|CLONGDOUBLE:
					*((long double*)es) = *((short*)es);
					break;
#endif
				case SRC(LONG)|FLOAT:
					*((float*)es) = *((long*)es);
					break;
				case SRC(LONG)|DOUBLE:
					*((double*)es) = *((long*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(LONG)|CLONGLONG:
					*((long long*)es) = *((long*)es);
					break;
				case SRC(LONG)|CULONGLONG:
					*((unsigned long long*)es) = *((long*)es);
					break;
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(LONG)|CLONGDOUBLE:
					*((long double*)es) = *((long*)es);
					break;
#endif
				case SRC(UBYTE)|SHORT:
					*((short*)es) = *((unsigned char*)es);
					break;
				case SRC(UBYTE)|LONG:
					*((long*)es) = *((unsigned char*)es);
					break;
				case SRC(UBYTE)|USHORT:
					*((unsigned short*)es) = *((unsigned char*)es);
					break;
				case SRC(UBYTE)|ULONG:
					*((unsigned long*)es) = *((unsigned char*)es);
					break;
				case SRC(UBYTE)|FLOAT:
					*((float*)es) = *((unsigned char*)es);
					break;
				case SRC(UBYTE)|DOUBLE:
					*((double*)es) = *((unsigned char*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(UBYTE)|CLONGLONG:
					*((long long*)es) = *((unsigned char*)es);
					break;
				case SRC(UBYTE)|CULONGLONG:
					*((unsigned long long*)es) = *((unsigned char*)es);
					break;
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(UBYTE)|CLONGDOUBLE:
					*((long double*)es) = *((unsigned char*)es);
					break;
#endif
				case SRC(USHORT)|LONG:
					*((long*)es) = *((unsigned short*)es);
					break;
				case SRC(USHORT)|ULONG:
					*((unsigned long*)es) = *((unsigned short*)es);
					break;
				case SRC(USHORT)|FLOAT:
					*((float*)es) = *((unsigned short*)es);
					break;
				case SRC(USHORT)|DOUBLE:
					*((double*)es) = *((unsigned short*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(USHORT)|CLONGLONG:
					*((long long*)es) = *((unsigned short*)es);
					break;
				case SRC(USHORT)|CULONGLONG:
					*((unsigned long long*)es) = *((unsigned short*)es);
					break;
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(USHORT)|CLONGDOUBLE:
					*((long double*)es) = *((unsigned short*)es);
					break;
#endif
				case SRC(ULONG)|FLOAT:
					*((float*)es) = *((unsigned long*)es);
					break;
				case SRC(ULONG)|DOUBLE:
					*((double*)es) = *((unsigned long*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(ULONG)|CLONGLONG:
					*((long long*)es) = *((unsigned long*)es);
					break;
				case SRC(ULONG)|CULONGLONG:
					*((unsigned long long*)es) = *((unsigned long*)es);
					break;
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(ULONG)|CLONGDOUBLE:
					*((long double*)es) = *((unsigned long*)es);
					break;
#endif
				case SRC(FLOAT)|BYTE:
					*((char*)es) = *((float*)es);
					break;
				case SRC(FLOAT)|SHORT:
					*((short*)es) = *((float*)es);
					break;
				case SRC(FLOAT)|LONG:
					*((long*)es) = *((float*)es);
					break;
				case SRC(FLOAT)|UBYTE:
					*((unsigned char*)es) = *((float*)es);
					break;
				case SRC(FLOAT)|USHORT:
					*((unsigned short*)es) = *((float*)es);
					break;
				case SRC(FLOAT)|ULONG:
					*((unsigned long*)es) = *((float*)es);
					break;
				case SRC(FLOAT)|DOUBLE:
					*((double*)es) = *((float*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(FLOAT)|CLONGLONG:
					*((long long*)es) = *((float*)es);
					break;
				case SRC(FLOAT)|CULONGLONG:
					*((unsigned long long*)es) = *((float*)es);
					break;
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(FLOAT)|CLONGDOUBLE:
					*((long double*)es) = *((float*)es);
					break;
#endif
				case SRC(DOUBLE)|BYTE:
					*((char*)es) = *((double*)es);
					break;
				case SRC(DOUBLE)|SHORT:
					*((short*)es) = *((double*)es);
					break;
				case SRC(DOUBLE)|LONG:
					*((long*)es) = *((double*)es);
					break;
				case SRC(DOUBLE)|UBYTE:
					*((unsigned char*)es) = *((double*)es);
					break;
				case SRC(DOUBLE)|USHORT:
					*((unsigned short*)es) = *((double*)es);
					break;
				case SRC(DOUBLE)|ULONG:
					*((unsigned long*)es) = *((double*)es);
					break;
				case SRC(DOUBLE)|FLOAT:
					*((float*)es) = *((double*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(DOUBLE)|CLONGLONG:
					*((long long*)es) = *((double*)es);
					break;
				case SRC(DOUBLE)|CULONGLONG:
					*((unsigned long long*)es) = *((double*)es);
					break;
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(DOUBLE)|CLONGDOUBLE:
					*((long double*)es) = *((double*)es);
					break;
#endif
#if SUPPORT_LONG_LONG
				case SRC(CLONGLONG)|FLOAT:
					*((float*)es) = *((long long*)es);
					break;
				case SRC(CLONGLONG)|DOUBLE:
					*((double*)es) = *((long long*)es);
					break;
#if SUPPORT_LONG_DOUBLE
				case SRC(CLONGLONG)|CLONGDOUBLE:
					*((long double*)es) = *((long long*)es);
					break;
#endif
				case SRC(CULONGLONG)|FLOAT:
					*((float*)es) = *((unsigned long long*)es);
					break;
				case SRC(CULONGLONG)|DOUBLE:
					*((double*)es) = *((unsigned long long*)es);
					break;
#if SUPPORT_LONG_DOUBLE
				case SRC(CULONGLONG)|CLONGDOUBLE:
					*((long double*)es) = *((unsigned long long*)es);
					break;
#endif
#endif
#if SUPPORT_LONG_DOUBLE
				case SRC(CLONGDOUBLE)|BYTE:
					*((char*)es) = *((long double*)es);
					break;
				case SRC(CLONGDOUBLE)|SHORT:
					*((short*)es) = *((long double*)es);
					break;
				case SRC(CLONGDOUBLE)|LONG:
					*((long*)es) = *((long double*)es);
					break;
				case SRC(CLONGDOUBLE)|UBYTE:
					*((unsigned char*)es) = *((long double*)es);
					break;
				case SRC(CLONGDOUBLE)|USHORT:
					*((unsigned short*)es) = *((long double*)es);
					break;
				case SRC(CLONGDOUBLE)|ULONG:
					*((unsigned long*)es) = *((long double*)es);
					break;
				case SRC(CLONGDOUBLE)|FLOAT:
					*((float*)es) = *((long double*)es);
					break;
				case SRC(CLONGDOUBLE)|DOUBLE:
					*((double*)es) = *((long double*)es);
					break;
#if SUPPORT_LONG_LONG
				case SRC(CLONGDOUBLE)|CLONGLONG:
					*((long long*)es) = *((long double*)es);
					break;
				case SRC(CLONGDOUBLE)|CULONGLONG:
					*((unsigned long long*)es) = *((long double*)es);
					break;
#endif
#endif
			}
			break;
		}
		case IMMED:
		{
			++pc;
			++np;
			switch(*pc)
			{

				case SMI|A1|B1:
					STORMEMI1(char);
				case SMI|A1|B2:
					STORMEMI1(short);
				case SMI|A1|B4:
					STORMEMI1(long);
				case SMI|A1|B8:
					STORMEMI1(double);
				case SMI|A2|B1:
					STORMEMI2(char);
				case SMI|A2|B2:
					STORMEMI2(short);
				case SMI|A2|B4:
					STORMEMI2(long);
				case SMI|A2|B8:
					STORMEMI2(double);
				case SMI|A3|B1:
					STORMEMI3(char);
				case SMI|A3|B2:
					STORMEMI3(short);
				case SMI|A3|B4:
					STORMEMI3(long);
				case SMI|A3|B8:
					STORMEMI3(double);
				case SMI|A4|B1:
					STORMEMI4(char);
				case SMI|A4|B2:
					STORMEMI4(short);
				case SMI|A4|B4:
					STORMEMI4(long);
				case SMI|A4|B8:
					STORMEMI4(double);

				case SSI|A1|B1:
					STORSTKI1(char);
				case SSI|A1|B2:
					STORSTKI1(short);
				case SSI|A1|B4:
					STORSTKI1(long);
				case SSI|A1|B8:
					STORSTKI1(double);
				case SSI|A2|B1:
					STORSTKI2(char);
				case SSI|A2|B2:
					STORSTKI2(short);
				case SSI|A2|B4:
					STORSTKI2(long);
				case SSI|A2|B8:
					STORSTKI2(double);
				case SSI|A3|B1:
					STORSTKI3(char);
				case SSI|A3|B2:
					STORSTKI3(short);
				case SSI|A3|B4:
					STORSTKI3(long);
				case SSI|A3|B8:
					STORSTKI3(double);

				case MODI|BYTE:
					MODI_ES(char);
				case MODI|SHORT:
					MODI_ES(short);
				case MODI|LONG:
					MODI_ES(long);
				case MODI|UBYTE:
					MODI_ES(unsigned char);
				case MODI|USHORT:
					MODI_ES(unsigned short);
				case MODI|ULONG:
					MODI_ES(unsigned long);

				case DEREF|BYTE:
					DEREF_ES(char);
				case DEREF|SHORT:
					DEREF_ES(short);
				case DEREF|LONG:
					DEREF_ES(long);
				case DEREF|UBYTE:
					UDEREF_ES(unsigned char);
				case DEREF|USHORT:
					UDEREF_ES(unsigned short);
				case DEREF|ULONG:
					UDEREF_ES(unsigned long);
				case DEREF|FLOAT:
					FDEREF_ES(float);
				case DEREF|DOUBLE:
					FDEREF_ES(double);

				case DEREF1|BYTE:
					DEREF1_ES(char);
				case DEREF1|SHORT:
					DEREF1_ES(short);
				case DEREF1|LONG:
					DEREF1_ES(long);
				case DEREF1|UBYTE:
					UDEREF1_ES(unsigned char);
				case DEREF1|USHORT:
					UDEREF1_ES(unsigned short);
				case DEREF1|ULONG:
					UDEREF1_ES(unsigned long);
				case DEREF1|FLOAT:
					FDEREF1_ES(float);
				case DEREF1|DOUBLE:
					FDEREF1_ES(double);
			}
			break;
		}
		case LI|B1:
			LOADI1();
		case LI|B2:
			LOADI2();
		case LI|B4:
			LOADI4();
		case LI|B8:
			LOADI8();
		case LAI|D1:
			LOADADDRI1();
		case LAI|D2:
			LOADADDRI2();
		case LAI|D3:
			LOADADDRI3();
		case LAI|D4:
			LOADADDRI4();

		case LUI|B1:
			LOADUI1();
		case LUI|B2:
			LOADUI2();
		case LUI|B4:
			LOADUI4();
		case LUI|B8:
			LOADI8();

		case SM|A1|B1:
			STORMEM1(char);
		case SM|A1|B2:
			STORMEM1(short);
		case SM|A1|B4:
			STORMEM1(long);
		case SM|A1|B8:
			STORMEM1(double);
		case SM|A2|B1:
			STORMEM2(char);
		case SM|A2|B2:
			STORMEM2(short);
		case SM|A2|B4:
			STORMEM2(long);
		case SM|A2|B8:
			STORMEM2(double);
		case SM|A3|B1:
			STORMEM3(char);
		case SM|A3|B2:
			STORMEM3(short);
		case SM|A3|B4:
			STORMEM3(long);
		case SM|A3|B8:
			STORMEM3(double);
		case SM|A4|B1:
			STORMEM4(char);
		case SM|A4|B2:
			STORMEM4(short);
		case SM|A4|B4:
			STORMEM4(long);
		case SM|A4|B8:
			STORMEM4(double);


		case ADD|BYTE:
			ADD_ES(char);
		case ADD|SHORT:
			ADD_ES(short);
		case ADD|LONG:
			ADD_ES(long);
		case ADD|UBYTE:
			ADD_ES(unsigned char);
		case ADD|USHORT:
			ADD_ES(unsigned short);
		case ADD|ULONG:
			ADD_ES(unsigned long);
		case ADD|FLOAT:
			ADD_ES(float);
		case ADD|DOUBLE:
			ADD_ES(double);

		case SUB|BYTE:
			SUB_ES(char);
		case SUB|SHORT:
			SUB_ES(short);
		case SUB|LONG:
			SUB_ES(long);
		case SUB|UBYTE:
			SUB_ES(unsigned char);
		case SUB|USHORT:
			SUB_ES(unsigned short);
		case SUB|ULONG:
			SUB_ES(unsigned long);
		case SUB|FLOAT:
			SUB_ES(float);
		case SUB|DOUBLE:
			SUB_ES(double);

		case MUL|BYTE:
			MUL_ES(char);
		case MUL|SHORT:
			MUL_ES(short);
		case MUL|LONG:
			MUL_ES(long);
		case MUL|UBYTE:
			MUL_ES(unsigned char);
		case MUL|USHORT:
			MUL_ES(unsigned short);
		case MUL|ULONG:
			MUL_ES(unsigned long);
		case MUL|FLOAT:
			MUL_ES(float);
		case MUL|DOUBLE:
			MUL_ES(double);

		case DIV|BYTE:
			DIV_ES(char);
		case DIV|SHORT:
			DIV_ES(short);
		case DIV|LONG:
			DIV_ES(long);
		case DIV|UBYTE:
			DIV_ES(unsigned char);
		case DIV|USHORT:
			DIV_ES(unsigned short);
		case DIV|ULONG:
			DIV_ES(unsigned long);
		case DIV|FLOAT:
			DIV_ES(float);
		case DIV|DOUBLE:
			DIV_ES(double);

		case OR|B1:
			OR_ES(unsigned char);
		case OR|B2:
			OR_ES(unsigned short);
		case OR|B4:
			OR_ES(unsigned long);

		case XOR|B1:
			XOR_ES(unsigned char);
		case XOR|B2:
			XOR_ES(unsigned short);
		case XOR|B4:
			XOR_ES(unsigned long);

		case AND|B1:
			AND_ES(unsigned char);
		case AND|B2:
			AND_ES(unsigned short);
		case AND|B4:
			AND_ES(unsigned long);

#if SUPPORT_LONG_LONG
		case OR|B8:
			OR_ES(unsigned long long);
		case AND|B8:
			AND_ES(unsigned long long);
		case XOR|B8
			XOR_ES(unsigned long long);
#else
#endif

		case MOD|BYTE:
			MOD_ES(char);
		case MOD|SHORT:
			MOD_ES(short);
		case MOD|LONG:
			MOD_ES(long);
		case MOD|UBYTE:
			MOD_ES(unsigned char);
		case MOD|USHORT:
			MOD_ES(unsigned short);
		case MOD|ULONG:
			MOD_ES(unsigned long);

		case XTD:
		{
			++pc;
			++np;
			switch(*pc)
			{
				case LSH|B1:
					LSH_ES(char);
				case LSH|B2:
					LSH_ES(short);
				case LSH|B4:
					LSH_ES(long);
				case LSH|B8:
					LSH_ES(long long);

				case LSHI|B1:
					LSHI_ES(char);
				case LSHI|B2:
					LSHI_ES(short);
				case LSHI|B4:
					LSHI_ES(long);
				case LSHI|B8:
					LSHI_ES(long long);
			
				case RSH|BYTE:
					RSH_ES(char);
				case RSH|SHORT:
					RSH_ES(short);
				case RSH|LONG:
					RSH_ES(long);
				case RSH|UBYTE:
					RSH_ES(unsigned char);
				case RSH|USHORT:
					RSH_ES(unsigned short);
				case RSH|ULONG:
					RSH_ES(unsigned long);

				case RSHI|BYTE:
					RSHI_ES(char);
				case RSHI|SHORT:
					RSHI_ES(short);
				case RSHI|LONG:
					RSHI_ES(long);
				case RSHI|UBYTE:
					RSHI_ES(unsigned char);
				case RSHI|USHORT:
					RSHI_ES(unsigned short);
				case RSHI|ULONG:
					RSHI_ES(unsigned long);

				case BUILTIN:
				{
					++pc;
					++np;
					switch(*pc)
					{/* THESE BUILTINS CANNOT BE CALLED THROUGH A FUNCPTR */
						case SETJMP:
						{
						long *jb;

							jb = *((void**)es);
							*((long*)es) = 0;
	
							jb[0] = (long)iv->allocalist;
							jb[1] = (long)pc;
							if((jb = (long*)setjmp(((void*)&jb[4]))))
							{
							void *bs,*qs;
								pc = (void*)jb[1];
								*((long*)es) = jb[2];			
								bs = (void*)jb[3];
								while(bs != base_stack)
								{
									qs = ((PSB)bs)->backlink;
									free(bs);
									bs = qs;
								}
								purge_allocas(iv, (void*)jb[0]);
								prune_structs(iv);
							}
							break;
						}
						case LONGJMP:
						{
						long *jb;

							jb = *((void**)oes);
							jb[2] = *((long*)es);
							jb[3] = (long)base_stack;
							longjmp(((void*)&jb[4]), (long)jb);
							break;
						}
						case ABORT:
							printf("bterp: program called abort.\n");
						case EXIT:
						{
						void *bs, *qs;
							bs = base_stack;
							while(bs != iv->base_stack)
							{
								qs = ((PSB)bs)->backlink;
								free(bs);
								bs = qs;
							}
							purge_allocas(iv, 0);
							longjmp(iv->jb, (int)es);
							break;
						}
						default:
							do_builtin(iv, *pc, &es);
							break;
					}
					break;
				} /* END: XTD BUILTIN */
				case CLRDAT:
				{
					memset(*((void**)oes), 0, *((long*)es));
					es -= 2*SZ;
					break;
				}
				case SWITCH:
				{
				unsigned long key[2];
				unsigned char **result;

					key[0] = *((unsigned short*)np)<<11;
					key[1] = *((long*)es);
					if(findswitch(iv, key, &result))
					{
						 pc = *result - 1;	
					}
					else
					{
						pc += 2;
					}
					es = oes;
					break;
				}
				case CALLSETUP:
				{
				PCB cb = (PCB)es;
				Pft ft = cb->loc;
				int hidden;
				long stksiz, maxes;
				long argsiz, strucsiz;
				int flags = 0;
					if(ft->fmods & Fbuiltin)
					{/* This only happens if the programmer uses a function
						pointer which points to a builtin function. */
						flags = 0x80;
					}
					cb->argsiz = argsiz = G4(np);
				
					if((hidden = ft->fmods & Fretstr))
					{/* function returning a structure */
						strucsiz = ft->retsiz<<2;
					}	
					stksiz = ft->stksiz<<2;
					maxes = ft->maxes;

					if(ft->fmods & Fnested)
					{/* Calling nested function */
						cb->argofs = (ft->stkbeg+(stksiz-(ft->argsiz<<2)-hidden)) >> 2;
						if(ft->funcaddr >= first_loc && ft->funcaddr <= last_loc)
						{/* call from enclosing function */
							cb->base_stack = base_stack;
							cb->es = es;
							cb->flags = 0x10 + hidden;
						}
						else
						{/* callback from another function */
						PSB prev = (PSB)(fs - sizeof(SB));
						long floc = prev->first_loc;
						long lloc = prev->last_loc;

						  while((prev = prev->backlink))
						  {
							if(		ft->funcaddr >= floc
								&&	ft->funcaddr <= lloc)
							{/* This is the container of the nested func */
							 cb->base_stack = (char*)prev;
							 cb->es = prev->cbes;
							 break;
							}
							floc = prev->first_loc;
							lloc = prev->last_loc;
						  }
						  cb->flags = 0x20 + hidden;
						}
					}
					else if(ft->fmods & Fextern)
					{/* Calling external function */
						/* Dynamic link it */
						if(!(ft->fmods & Fthunked))
							load_efunc(iv, ft);

						cb->base_stack = calloc(1, sizeof(SB)+argsiz+hidden);
						cb->argsiz = argsiz+hidden;
						cb->argofs = 0; 
						cb->flags = 0x40 + hidden;
					}
					else
					{/* Calling interpreted function */
					  if(flags & 0x80)
					  {
						cb->flags = flags;
						cb->es = cb->base_stack = calloc(1, 128);
					  }
					  else
					  {
					  long es_beg;
					  long fs_size;
					  PSB sp;
						es_beg = stksiz+hidden+argsiz;
						fs_size = es_beg + ((maxes+6)*SZ);
						cb->base_stack = calloc(1, fs_size+sizeof(SB));
						sp = (PSB)cb->base_stack;
						cb->es = cb->base_stack + sizeof(SB) + es_beg;
						cb->argofs = stksiz >> 2;
						cb->flags = flags + hidden;						
						sp->first_loc = first_loc;
						sp->last_loc = last_loc;
						sp->backlink = base_stack;
						sp->stksize = fs_size+sizeof(SB);
					  }
 					}
					if(hidden)
					{/* function returning a structure */
					void *strucptr = malloc(strucsiz);
		*((void**)(cb->base_stack+sizeof(SB)+(cb->argofs<<2))) = strucptr;
						ensure_strrets(iv);
						iv->struclist[iv->strretcnt++] = strucptr;
					}
					pc += 4;
					break;
				}
				case RETSTRUCT:
				{
				long size = G2(np)<<2;
				long offset = G2(np+2)<<2;
				void *dst;

					dst = fs + offset;
					dst = *((void**)dst);
					memcpy(dst, *((void**)es), size);
if(iv->debug) {
printf("RETSTRUCT ofs=%x es=%p val=0x%x\n", pc_offset, es, *((long*)es));
fflush(stdout);
}
					return es;
				}
				case PRUNESTRUCT:
				{
					prune_structs(iv);
					break;
				}
				case GETBITFIELD:
				{
					if(pc[1] + pc[2] <= 32)
					{
						*((long*)es) >>= pc[1];
						*((long*)es) &= bfields[pc[2]];
						if(pc[3])
						{/* sign extend */
							if(*((long*)es) & tsfields[pc[2]])
							{
								*((long*)es) |= sfields[pc[2]];
								((long*)es)[1] = 0xffffffff;
							}
							else ((long*)es)[1] = 0;
						}
					}
					else
					{
#if SUPPORT_LONG_LONG
							*((long long*)es) >>= pc[1];
							*((long long*)es) &= lbfields[pc[2]];
							if(pc[3])
							{
								if(*((long long)es) & ltsfields[pc[2]])
									*((long long)es) |= lsfields[pc[2]]);
							}
#else
#endif
					}
					pc += 3;
					break;
				}
				case PUTBITFIELD:
				{
					if(pc[1] + pc[2] <= 32)
					{
					unsigned long mask = bfields[pc[2]];
					void *dst = *((void **)oes);
					unsigned long dat = *((long*)es);
						dat &= mask;
						dat <<= pc[1];
						mask <<= pc[1];
						switch(pc[3])
						{
							case	1:
								*((char*)dst) &= ~mask;
								*((char*)dst) |= dat;
								break;
							case	2:
								*((short*)dst) &= ~mask;
								*((short*)dst) |= dat;
								break;
							case	4:
								*((long*)dst) &= ~mask;
								*((long*)dst) |= dat;
								break;
							default:
								break;
						}
					}
					else
					{
#if SUPPORT_LONG_LONG
					unsigned long long mask = lbfields[pc[2]];
					void *dst = *((void **)oes);
					unsigned long long dat = *(unsigned long long *)es);
						dat &= mask;
						dat <<= pc[1];
						mask <<= pc[1];
						switch(pc[3])
						{
							case	1:
								*((char*)dst) &= ~mask;
								*((char*)dst) |= dat;
								break;
							case	2:
								*((short*)dst) &= ~mask;
								*((short*)dst) |= dat;
								break;
							case	4:
								*((long*)dst) &= ~mask;
								*((long*)dst) |= dat;
								break;
							case	8:
								*((long long*)dst) &= ~mask;
								*((long long*)dst) |= dat;
								break;
							default:
								break;
						}
#else
#endif
					}
					pc += 3;
					es -= 2*SZ;
					break;
				}
#if SUPPORT_LONG_DOUBLE
				case LI:
					LOADIX();
#endif
				case IMMED:
				{
					++pc;
					++np;
					switch(*pc)
					{
#if SUPPORT_LONG_DOUBLE

						case SMI|A1:
							STORMEMI1(long double);
						case SMI|A2:
							STORMEMI2(long double);
						case SMI|A3:
							STORMEMI3(long double);
						case SMI|A4:
							STORMEMI4(long double);

						case SSI|A1:
							STORSTKI1(long double);
						case SSI|A2:
							STORSTKI2(long double);
						case SSI|A3:
							STORSTKI3(long double);

						case DEREF|LONGDOUBLE:
							FDEREF_ES(long double);
						case DEREF1|LONGDOUBLE:
							FDEREF1_ES(long double);
#endif
#if SUPPORT_LONG_LONG
						case DEREF|LONGLONG:
						case DEREF|ULONGLONG:
							FDEREF_ES(long long);

						case DEREF1|LONGLONG:
						case DEREF1|ULONGLONG:
							FDREF1_ES(long long);

						case MODI|LONGLONG:
							MODI_ES(long long);
						case MODI|ULONGLONG:
							MODI_ES(unsigned long long);
#endif
					}
					break;
				}/* END: XTD IMMED */
#if SUPPORT_LONG_LONG
				
				case ADD|LONGLONG:
					ADD_ES(long long);
				case ADD|ULONGLONG:
					ADD_ES(unsigned long long);
				case SUB|LONGLONG:
					SUB_ES(long long);
				case SUB|ULONGLONG:
					SUB_ES(unsigned long long);
				case MUL|LONGLONG:
					MUL_ES(long long);
				case MUL|ULONGLONG:
					MUL_ES(unsigned long long);
				case DIV|LONGLONG:
					DIV_ES(long long);
				case DIV|ULONGLONG:
					DIV_ES(unsigned long long);
				case NEG|LONGLONG:
					NEG_ES(long long);
				case NEG|ULONGLONG:
					NEG_ES(unsigned long long);
				case LT|LONGLONG:
					LT_ES(long long);
				case LT|ULONGLONG:
					LT_ES(unsigned long long);
				case GT|LONGLONG:
					GT_ES(long long);
 				case GT|ULONGLONG:
					GT_ES(unsigned long long);
				case LE|LONGLONG:
					LE_ES(long long);
				case LE|ULONGLONG:
					LE_ES(unsigned long long);
				case GE|LONGLONG:
					GE_ES(long long);
				case GE|ULONGLONG:
					GE_ES(unsigned long long);
				case NE|LONGLONG:
					NE_ES(long long);
				case NE|ULONGLONG:
					NE_ES(unsigned long long);
				case EQ|LONGLONG:
					EQ_ES(long long);
				case EQ|ULONGLONG:
					EQ_ES(unsigned long long);
				case RSH|SLONGLONG:
					RSH_ES(long long);
				case RSH|SULONGLONG:
					RSH_ES(unsigned long long);

				case MOD|LONGLONG:
					MODL_ES(long long);
				case MOD|ULONGLONG:
					MODL_ES(unsigned long long);


				case RSHI|SLONGLONG:
					RSHI_ES(long long);
				case RSHI|SULONGLONG:
					RSHI_ES(unsigned long long);
#endif

#if SUPPORT_LONG_DOUBLE
				
				case ADD|LONGDOUBLE:
					ADD_ES(long double);
				case SUB|LONGDOUBLE:
					SUB_ES(long double);
				case MUL|LONGDOUBLE:
					MUL_ES(long double);
				case DIV|LONGDOUBLE:
					DIV_ES(long double);
				case TRUTHOF|BX:
					TRUTH_ES(long double);
				case NOT|BX:
					NOT_ES(long double);
				case NEG|LONGDOUBLE:
					NEG_ES(long double);
				case LT|LONGDOUBLE:
					LT_ES(long double);
				case GT|LONGDOUBLE:
					GT_ES(long double);
				case LE|LONGDOUBLE:
					LE_ES(long double);
				case GE|LONGDOUBLE:
					GE_ES(long double);
				case NE|LONGDOUBLE:
					NE_ES(long double);
				case EQ|LONGDOUBLE:
					EQ_ES(long double);

				case LS|A1:
					LOADSTK1(long double);
				case LS|A2:
					LOADSTK2(long double);
				case LS|A3:
					LOADSTK3(long double);
				case LS|A4:
					LOADSTK4(long double);

				case LM|A1:
					LOADMEM1(long double);
				case LM|A2:
					LOADMEM2(long double);
				case LM|A3:
					LOADMEM3(long double);
				case LM|A4:
					LOADMEM4(long double);

				case SS|A1:
					STORSTK1(long double);
				case SS|A2:
					STORSTK2(long double);
				case SS|A3:
					STORSTK3(long double);
				case SS|A4:
					STORSTK4(long double);

				case SM|A1:
					STORMEM1(long double);
				case SM|A2:
					STORMEM2(long double);
				case SM|A3:
					STORMEM3(long double);
				case SM|A4:
					STORMEM4(long double);

				case MOVSS:
				{
					pc += mover(iv, np+1, *np, BX, fs, fs);
					break;
				}				
				case MOVSM:
				{
					pc += mover(iv, np+1, *np, BX, fs, dd);
					break;
				}
				case MOVMS:
				{
					pc += mover(iv, np+1, *np, BX, dd, fs);
					break;
				}
				case MOVMM:
				{
					pc += mover(iv, np+1, *np, BX, dd, dd);
					break;
				}
#endif /* SUPPORT_LONG_DOUBLE */
			}
			break;
		}/* END: XTD */

		case GT|BYTE:
			GT_ES(char);
		case GT|SHORT:
			GT_ES(short);
		case GT|LONG:
			GT_ES(long);
		case GT|UBYTE:
			GT_ES(unsigned char);
		case GT|USHORT:
			GT_ES(unsigned short);
		case GT|ULONG:
			GT_ES(unsigned long);
		case GT|FLOAT:
			GT_ES(float);
		case GT|DOUBLE:
			GT_ES(double);

		case LT|BYTE:
			LT_ES(char);
		case LT|SHORT:
			LT_ES(short);
		case LT|LONG:
			LT_ES(long);
		case LT|UBYTE:
			LT_ES(unsigned char);
		case LT|USHORT:
			LT_ES(unsigned short);
		case LT|ULONG:
			LT_ES(unsigned long);
		case LT|FLOAT:
			LT_ES(float);
		case LT|DOUBLE:
			LT_ES(double);

		case GE|BYTE:
			GE_ES(char);
		case GE|SHORT:
			GE_ES(short);
		case GE|LONG:
			GE_ES(long);
		case GE|UBYTE:
			GE_ES(unsigned char);
		case GE|USHORT:
			GE_ES(unsigned short);
		case GE|ULONG:
			GE_ES(unsigned long);
		case GE|FLOAT:
			GE_ES(float);
		case GE|DOUBLE:
			GE_ES(double);

		case LE|BYTE:
			LE_ES(char);
		case LE|SHORT:
			LE_ES(short);
		case LE|LONG:
			LE_ES(long);
		case LE|UBYTE:
			LE_ES(unsigned char);
		case LE|USHORT:
			LE_ES(unsigned short);
		case LE|ULONG:
			LE_ES(unsigned long);
		case LE|FLOAT:
			LE_ES(float);
		case LE|DOUBLE:
			LE_ES(double);

		case NE|BYTE:
			NE_ES(char);
		case NE|SHORT:
			NE_ES(short);
		case NE|LONG:
			NE_ES(long);
		case NE|UBYTE:
			NE_ES(unsigned char);
		case NE|USHORT:
			NE_ES(unsigned short);
		case NE|ULONG:
			NE_ES(unsigned long);
		case NE|FLOAT:
			NE_ES(float);
		case NE|DOUBLE:
			NE_ES(double);

		case EQ|BYTE:
			EQ_ES(char);
		case EQ|SHORT:
			EQ_ES(short);
		case EQ|LONG:
			EQ_ES(long);
		case EQ|UBYTE:
			EQ_ES(unsigned char);
		case EQ|USHORT:
			EQ_ES(unsigned short);
		case EQ|ULONG:
			EQ_ES(unsigned long);
		case EQ|FLOAT:
			EQ_ES(float);
		case EQ|DOUBLE:
			EQ_ES(double);

		case ARG:
		case ARGA:
		case ARGF:
		{
		PCB cb = (PCB)(es-(3*SZ));
		long size = *((long*)es);
		void *src = oes-SZ;
		void *dst;
			if(cb->flags & 0x80)
			{/* arg to builtin func */
				cb->es += SZ;
				dst = cb->es;
			}
			else
			{
				dst = cb->base_stack+sizeof(SB)+(cb->argofs<<2)+*((long*)oes);
			}
			if(*pc == ARGA)
			{/* dereference */
				src = *((void**)src);
			}
			else if(*pc == ARGF)
			{/* Passing address of function */
			Pft ft = *((Pft*)src);

			  if(ft->fmods & Fnested)
			  {/* Record the current evaluation stack
			  		the nested func will be called back */
			    ((PSB)base_stack)->cbes = (void*)cb;
			  }
			  if(cb->flags & 0x40)
			  {/* To an external function */
				if(!(ft->fmods & Fextern))
				{/* Passing address of local function */
				  if(!(ft->fmods & Fthunked))
				  {
					*((void**)src) = make_callback_thunk(	iv,
															base_stack,
															ft);
					ft->fmods |= Fthunked;
					if(ft->fmods & Fnested)
					{/* The thunk will be freed later */
						((PSB)base_stack)->thunkaddr = *((void**)src);
					}
				  }
				}
			  }
			}/* END: *pc == ARGF */
			switch(size)
			{
				case 1:
					*((char*)dst) = *((char*)src);
					break;
				case 2:
					*((short*)dst) = *((short*)src);
					break;
				case 4:
					*((long*)dst) = *((long*)src);
					break;
				case 8:
					*((double*)dst) = *((double*)src);
					break;
				default:
					memcpy(dst, src, size);
					break;
			}
			es -= 3*SZ;
			break;
		}

		case MOVSS|B1:
		case MOVSS|B2:
		case MOVSS|B4:
		case MOVSS|B8:
		{
			pc += mover(iv, np+1, *np, *pc & 3, fs, fs);
			break;
		}
		case MOVSM|B1:
		case MOVSM|B2:
		case MOVSM|B4:
		case MOVSM|B8:
		{
			pc += mover(iv, np+1, *np, *pc & 3, fs, dd);
			break;
		}
		case MOVMS|B1:
		case MOVMS|B2:
		case MOVMS|B4:
		case MOVMS|B8:
		{
			pc += mover(iv, np+1, *np, *pc & 3, dd, fs);
			break;
		}		
		case MOVMM|B1:
		case MOVMM|B2:
		case MOVMM|B4:
		case MOVMM|B8:
		{
			pc += mover(iv, np+1, *np, *pc & 3, dd, dd);
			break;
		}
		case DUMP:
		{
			es = oes;
			break;
		}
		case REGAIN:
		{
			es = nes;
			break;
		}
		case CALL:
		{
		PCB cb = (PCB)es;
		Pft ft = cb->loc;
		  if(cb->flags & 0x80)
		  {/* call builtin function through a function pointer */
		  char *pes = cb->es;

			if(do_builtin(iv, (unsigned char)ft->funcaddr, &pes))
			{/* builtin returned something */
#if SUPPORT_LONG_DOUBLE
				*((long double*)es) = *((long double*)(pes));
#else
				*((double*)es) = *((double*)pes);
#endif
			}
			free(cb->base_stack);
		  }
		  else if(cb->flags & 0x40)
		  {/* call external function */
		  unsigned short fmods = ft->fmods;
		  DATUM lastval;
		    if(fmods & Fretstr)
		    {
			  _ExternCallS(	ft->funcaddr, 
							cb->base_stack+sizeof(SB), 
							cb->argsiz,
							&lastval);
		    }
		    else
		    {
			  _ExternCall(	ft->funcaddr, 
							cb->base_stack+sizeof(SB), 
							cb->argsiz,
							&lastval);
		    }
			if(fmods & Fretdbl)
			{
				asm ("fstpl %0" : "=g"(lastval.Udouble) :);
			}
			else if(fmods & Fretflt)
			{
				asm ("fstps %0" : "=g"(lastval.Ufloat) :);
			}
			else if(fmods & Fretldbl)
			{
#if SUPPORT_LONG_DOUBLE
#else
				asm ("fstpl %0" : "=g"(lastval.Udouble) :);
#endif
			}
#if SUPPORT_LONG_DOUBLE
			*((long double*)es) = lastval.Ulongdouble;
#else
			*((double*)es) = lastval.Udouble;
#endif
			free(cb->base_stack);
		  }/* END: call external func */
		  else
		  { /* call internal function */
		  void *pes;
		  void *lastalloca = iv->allocalist;
			pes = bterp_eval(iv, ft->funcaddr, cb->base_stack, cb->es, 
				cb->base_stack+((PSB)cb->base_stack)->stksize);
#if SUPPORT_LONG_DOUBLE
			*((long double*)es) = *((long double*)pes);
#else
			*((double*)es) = *((double*)pes);
#endif
			if(cb->flags & 0x20)
			{/* calledback a nested function */

			}
			else if(!(cb->flags & 0x10))
			{/* called interpreted function */
				free(cb->base_stack);
			} 
			purge_allocas(iv, lastalloca);
		  }/* END: call internal function */
		  break;
		}
		case RET:
		{
if(iv->debug) {
printf("RET ofs=%x es=%p val=0x%x\n", pc_offset, es, *((long*)es));
fflush(stdout);
}
			return es;
		}
		case SWAP:
		{
		char t[SZ];
			memcpy(t,es,SZ);
			memcpy(es,oes,SZ);
			memcpy(oes,t,SZ);
			break;
		}	
		case SWAP4:
		{
		long x;
			x = *((long *)es);
			*((long*)es) = *((long*)oes);
			*((long*)oes) = x;
			break;
		}
		case SWAP4DEEP:
		{
		long x;
			x = *((long*)oes);
			*((long*)oes) = *((long*)(oes-SZ));
			*((long*)(oes-SZ)) = x;
			break;
		}
		case DUP:
		{
			memcpy(nes,es,SZ);
			es = nes;
			break;
		}
		case DUP4:
		{
			*((long*)nes) = *((long*)es);
			es = nes;
			break;
		}
		case ABSMEM:
		{
			*((unsigned long*)es) += (unsigned long)dd;
			break;
		}
		case ABSSTK:
		{
			*((unsigned long*)es) += (unsigned long)fs;
			break;
		}
		case MOVDA1:
		{
			**((char**)(oes)) = *((char*)es);	
			es -= 2*SZ;
			break;
		}
		case MOVDA2:
		{
			**((short**)(oes)) = *((short*)es);	
			es -= 2*SZ;
			break;
		}
		case MOVDA4:
		{
			**((long**)(oes)) = *((long*)es);	
			es -= 2*SZ;
			break;
		}
		case MOVDA8:
		{
			**((double**)(oes)) = *((double*)es);	
			es -= 2*SZ;
			break;
		}
		case MOVDAX:
		{
#if SUPPORT_LONG_DOUBLE
			**((long double**)(oes)) = *((long double*)es);	
#else
			memcpy(*((void**)(oes)), es, XSZ);
#endif
			es -= 2*SZ;
			break;
		}
		case MOVAA1:
		{
			**((char**)(oes)) = **((char**)es);	
			es -= 2*SZ;
			break;
		}
		case MOVAA2:
		{
			**((short**)(oes)) = **((short**)es);	
			es -= 2*SZ;
			break;
		}
		case MOVAA4:
		{
			**((long**)(oes)) = **((long**)es);	
			es -= 2*SZ;
			break;
		}
		case MOVAA8:
		{
			**((double**)(oes)) = **((double**)es);	
			es -= 2*SZ;
			break;
		}
		case MOVAAX:
		{
#if SUPPORT_LONG_DOUBLE
			**((long double**)(oes)) = **((long double**)es);	
#else
			memcpy(*((void**)(oes)), *((void**)es), XSZ);
#endif
			es -= 2*SZ;
			break;
		}
		case MOVAAC:
		{
			memcpy(*((void**)(es-(2*SZ))), *((void**)(oes)), *((long*)(es)));
			es -= 3*SZ;
			break;
		}
	}
  }/* END: for(;;++pc) */
/* NOT REACHED */
  return 0;
}/* END: bterp_eval() */
/* ====================== END INTERPRETER CODE ===================== */

/* ==================  INITIALIZATION CODE BELOW THIS POINT ================ */
static Piv tiv;	/* temporary storage of iv whilst calling oxlink */

int 
bterp_setup_functhunk(FE entry, struct nlist *nl)
{/* Called from the dynamic linker */
Pft ft;

	ft = (void*)(entry->data_start_address + nl->n_value);

	if(ft->fmods & Fextern)
	{/* store a pointer to the function name string */
		if(!(ft->fmods & Fthunked))
		{
			ft->funcaddr = (long)(entry->strings + nl->n_un.n_strx);
		}
		return 0;
	}
	else if(!(ft->fmods & Fthunked))
	{/* export a useful address */
		nl->n_value = (long)make_callback_thunk(tiv, 0, ft);
		ft->fmods |= Fthunked;
	}
	return 1;
}
void
bterp_setup_switch(FE entry, struct nlist *nl)
{/* Called from the dynamic linker */
unsigned long key[2];
long value;

	if(tiv->swtable == 0)
		tiv->swtable = calloc(1, SWITCHMOD*sizeof(void*));

	key[0] = nl->n_desc<<11;
	key[1] = nl->n_un.n_strx;
	value = (long)(entry->text_start_address + nl->n_value);
	saveswitch(tiv, key, value);	
}

/* ====================== THE MAIN PROGRAM =============================== */

static char *
filenameof(char *path)
{
char *ret = path;
int i;
	for(i = 0; path[i]; ++i)
	  if(path[i] == '/')
		ret = &path[i+1];
	return ret;
}

static char *
propernameof(char *path)
{
int pathlen = strlen(path);
char *name = malloc(pathlen+8);
int i;

	strcpy(name, path);
	for(i = pathlen-1; i >= 0; --i)
	{
	  if(name[i] == '/' || name[i] == '\\')
	  	break;
	  else if(name[i] == '.')
		return name;
	}
	strcat(name, ".byt");
	return name;
}
static char *
basenameof(char *filename)
{
char *name = malloc(strlen(filename)+8);
int i;
	strcpy(name, filename);
	for(i = 0; name[i]; ++i)
	  if(name[i] == '.')
		name[i] = 0;
	return name;
}
static void
setup_run_args(Piv iv, int argc, char **argv, char *startname)
{
int i;

	if(!startname)
	{
		startname = basenameof(filenameof(argv[1]));
	}
	iv->run_argcnt = argc - 1;
	if(iv->run_argcnt > MAX_RUNARGS)
		iv->run_argcnt = MAX_RUNARGS;
	iv->run_args[0] = startname;
	for(i = 1; i < iv->run_argcnt; ++i)
	{
		iv->run_args[i] = argv[i+1];
	}
}
static void
Usage()
{
puts(
"Usage: bterp [+Sd] file [args]...\n"
"   +S name == start execution at function `name'\n"
"   +d      == print debug stmts\n"
"   Default execution starts at function `file'\n"
);
}
#if USING_FRAMEWORK
int
PROG (int argc, char **argv)
#else
int
main(int argc, char **argv)
#endif
{
int i, j;
char *startname;
long *argptr;
long es_beg, fs_size;
int *pes, ret;
Piv iv;

	iv = tiv = calloc(1, sizeof(struct _iv));
	iv->piv = iv;

	startname = 0;
	for(i = 1; i < argc; ++i)
	{
	int trimsize = 1;
		if(argv[i][0] == '+')
		{
			for (j=1; argv[i][j]; j++)
			{
				switch(argv[i][j])
				{	
					case	'd':
						iv->debug = 1;
						break;
					case	'S':
						if(argv[i][j+1]) {
							startname = &argv[i][j+1];
						}
						else if(i < argc-1) {
							startname = argv[i+1];
							trimsize = 2;
						} else {
							printf("bterp: No starting fuction name\n");
							Usage();
							return 1;
						}
						goto trim;
						break;
				}
			}
trim:
			/* Trim switch */
			for(j = i; j < argc-trimsize; ++j)
				argv[j] = argv[j+trimsize];
			argc -= trimsize;
			--i;	/* i will be bumped by for */
		}
	}
	if(argc > 1)
	{
	PSB sp;
		iv->filename = propernameof(argv[1]);
		if(oxlink_load_object(iv->filename))		/* try library list */
		{
			oxlink_demand_noload();
			if(oxlink_load_file(iv->filename))		/* try search path */
			{
				printf("bterp: file `%s' err:%s\n", iv->filename, oxlink_errstr());
				exit(1);
			}
			oxlink_demand_load();
		}
		iv->entry = oxlink_get_entry_struct(iv->filename);
		iv->text_base = iv->entry->text_start_address;
		iv->dd = iv->entry->data_start_address;

		setup_run_args(iv, argc, argv, startname);
		if(!(iv->funcptr = oxlink_find_func("main")))
		{
		  if(!(iv->funcptr = oxlink_find_func(iv->run_args[0])))
		  {
			printf("bterp: function `%s' not found in file\n", iv->run_args[0]);
			oxlink_unload_file(iv->filename, 0);
			free(iv->filename);
			free(iv->run_args[0]);
			free(iv);
			return 1;
		  }
		}
		iv->ft = *((Pft*)(&iv->funcptr[4]));
		iv->funcaddr = iv->ft->funcaddr;
		iv->stksiz = iv->ft->stksiz<<2;
		iv->argsiz = iv->ft->argsiz<<2;
		iv->maxes = iv->ft->maxes;

		es_beg = iv->stksiz+iv->argsiz;
		fs_size = es_beg + ((iv->maxes+6)*SZ);
		iv->base_stack = calloc(1, fs_size + sizeof(SB));
		iv->e_stack = iv->base_stack + sizeof(SB) + es_beg;

		/* Fill in _stakblk */
		sp = (PSB)iv->base_stack;
		sp->first_loc = iv->funcaddr;
		sp->last_loc = iv->dd - iv->text_base;
		sp->stksize = fs_size + sizeof(SB);

		/* Fill in the arguments */
		argptr = (long*)(iv->base_stack + iv->stksiz + sizeof(SB));
		if(iv->argsiz >= 4)
			argptr[0] = iv->run_argcnt;
		if(iv->argsiz >= 8)
			argptr[1] = (long)iv->run_args;

		if((pes = (void*)setjmp(iv->jb)))
			goto done;

		/* Call the starting function */
		pes = bterp_eval(iv, iv->funcaddr, iv->base_stack, iv->e_stack,
				iv->base_stack + (fs_size+sizeof(SB)));
done:
		ret = *pes;
		prune_structs(iv);
		oxlink_unload_file(iv->filename, 0);
		free(iv->filename);
		free(iv->run_args[0]);
		free(iv->base_stack);
		if(iv->swtable)
		{
		void *p = iv->chunklist;
			free(iv->swtable);
			while(p)
			{
			void *q = p;
				p = *((void**)p);
				free(q);
			}
		}
		free(iv);
		return ret;
	}
	else
	{
		Usage();
		return 1;
	}
}
#if 0 /* used for testing */
int bterpcallback(int (*pfunc)())
{
	return pfunc();
}
#endif
