home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
541b.lha
/
OakLisp
/
src.lzh
/
emulator.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-15
|
49KB
|
2,192 lines
/* Copyright (C) 1987,8,9 Barak Pearlmutter and Kevin Lang. */
/* An emulator for the Oaklisp virtual machine. */
#include <stdio.h>
#include <ctype.h>
#include "emulator.h"
#include "stacks.h"
#ifdef unix
#include <sys/time.h>
#include <sys/resource.h>
#endif
#define CASE_FOUR 1
#ifdef FAST
#define trace_insts 0
#define trace_stcon 0
#define trace_cxcon 0
#define trace_meth 0
#define trace_segs 0
#define trace_mcache 0
#else
bool trace_insts = FALSE; /* trace instruction execution */
bool trace_stcon = FALSE; /* trace stack contents */
bool trace_cxcon = FALSE; /* trace contents stack contents */
bool trace_meth = FALSE; /* trace method lookup */
bool trace_segs = FALSE; /* trace stack segment manipulation */
bool trace_mcache= FALSE; /* trace method cache hits and misses */
extern char *ArglessInstrs[], *Instrs[];
#endif
bool trace_traps = FALSE; /* trace tag traps */
bool trace_files = FALSE; /* trace file opening */
bool trace_gc = FALSE; /* trace gc carefully */
bool dump_after = FALSE; /* dump world after running */
bool gc_before_dump = FALSE; /* do a GC before dumping the world */
#ifdef FAST
#define MAYBE_PUT(v,s)
#else
#define MAYBE_PUT(v,s) { if ((v)) {(void)printf(s); fflush_stdout();} }
#endif
#ifdef Mac_LSC
extern void Init_Primitives();
extern ref Call_Primitive(ref primRef, ref callRef, ref retRef, ref paramList);
#endif
/*
* Processor registers
*/
stack val_stk, cxt_stk;
ref *e_bp, *e_env, e_t, e_nil,
e_fixnum_type, e_loc_type, e_cons_type, e_env_type, *e_subtype_table,
e_object_type, e_segment_type, e_boot_code, e_code_segment,
*e_arged_tag_trap_table, *e_argless_tag_trap_table, e_current_method,
e_uninitialized, e_method_type;
unsigned long
e_next_newspace_size,
original_newspace_size = DEFAULT_NEW_SPACE_SIZE;
unsigned short *e_pc;
#define maybe_dump_world(dumpstackp) \
{ \
UNOPTV(val_stk.ptr = val_stk_ptr); \
UNOPTC(cxt_stk.ptr = cxt_stk_ptr); \
maybe_dump_world_aux((dumpstackp)); \
}
#define NEW_STORAGE e_uninitialized
void maybe_dump_world_aux(dumpstackp)
int dumpstackp;
{
if (dumpstackp > 2) /* 0,1,2 are normal exits. */
{
printf("value ");
dump_stack_proc(&val_stk);
printf("context ");
dump_stack_proc(&cxt_stk);
}
if (dump_after)
{
if (gc_before_dump && dumpstackp == 0)
{
gc(TRUE, TRUE, "impending world dump", 0L);
dump_world(TRUE);
}
else
dump_world(FALSE);
}
}
void printref(refin)
ref refin;
{
unsigned long i;
char suffix = '?';
if ((refin&PTR_MASK) != 0)
{
ref *p = ((refin&1) != 0) ? REF_TO_PTR(refin) : LOC_TO_PTR(refin);
if (SPATIC_PTR(p))
{
i = p - spatic.start;
suffix = 's';
}
else if (NEW_PTR(p))
{
i = (p - new.start) + spatic.size;
suffix = 'n';
}
else i = (unsigned long)p >> 2;
(void)printf("%ld~%d%c", i, refin&TAG_MASK, suffix);
}
else
(void)printf("%ld~%d", refin>>2, refin&TAG_MASK);
}
#define TRACEMETHOD(zz) {if (trace_meth) {printf("meth-trace%ld ",zz); \
printref("obj_type:%ld~%ld ",obj_type); \
printref("alist:%ld~%ld ",alist); \
printref("mptr:%ld~%ld\n",*method_ptr); }}
#define TRACEASSQ(zz) {if (trace_meth) {printf("aq-trace%ld ",zz); \
printref("elem:%ld~%ld ",elem); \
printref("list:%ld~%ld\n",list); }}
#define TRACEPASSQ(zz) {printf("aq-trace%ld ",zz); \
printref("elem:%ld~%ld ",elem); \
printref("l:%ld~%ld ",l); \
printref("cdr(l):%ld~%ld ",cdr(l)); \
if (locl) printref("*locl:%ld~%ld\n",*locl); }
/* these are inline coded now
ref assq(elem, list)
ref elem, list;
{
while (list != e_nil && car(car(list)) != elem) {
list = cdr(list);
}
return ((list == e_nil)? e_nil : car(list));
}
ref old_pseudo_assq(elem, list)
ref elem, list;
{
while (list != e_nil && car(car(list)) != elem) {
list = cdr(list);
}
return list;
}
*/
/* The following code uses the bring-to-front heuristic,
and eventually needs a register to inhibit this behavior.
This code is now inserted inline in the one place it is used.
ref pseudo_assq(elem, loclist)
ref elem, *loclist;
{
ref thelist = *loclist;
register ref l = thelist;
register ref *locl = NULL;
while (l != e_nil)
{
if (car(car(l)) == elem)
{
if (locl != NULL) {
*locl = cdr(l);
*loclist = l;
cdr(l) = thelist;
}
return l;
}
l = *(locl = &(cdr(l)));
}
return l;
}
*/
#define get_type(x) \
((x)&1 ? \
((x)&2 ? REF_SLOT(x, 0) : *(e_subtype_table + ((x&SUBTAG_MASK)/4))) : \
((x)&2 ? e_loc_type : e_fixnum_type))
/* ((unsigned short *) (REF_TO_PTR(seg) + CODE_CODE_START_OFF)) */
#define CODE_SEG_FIRST_INSTR(seg) \
((unsigned short *)&REF_SLOT(seg,CODE_CODE_START_OFF))
void old_find_method_type_pair(op, obj_type, method_ptr, type_ptr)
register ref op;
register ref obj_type;
ref *method_ptr, *type_ptr;
{
register ref alist;
register ref *locl = NULL;
register ref thelist;
register ref *loclist;
while (1)
{
/* First look for it here: */
/*alist=pseudo_assq(op,&REF_SLOT(obj_type,TYPE_OP_METHOD_ALIST_OFF));*/
alist = thelist =
*(loclist = &REF_SLOT(obj_type, TYPE_OP_METHOD_ALIST_OFF));
while (alist != e_nil)
{
if (car(car(alist)) == op)
{
if (locl != NULL) {
*locl = cdr(alist);
*loclist = alist;
cdr(alist) = thelist;
}
*method_ptr = cdr(car(alist));
*type_ptr = obj_type;
return;
}
alist = *(locl = &cdr(alist));
}
/* Loop looking for it on supertypes: */
alist = REF_SLOT(obj_type, TYPE_SUPER_LIST_OFF);
if (alist == e_nil) return;
while ((thelist = cdr(alist)) != e_nil)
{
old_find_method_type_pair(op, car(alist), method_ptr, type_ptr);
/* If found on a supertype, we're done. */
if (*method_ptr != e_nil) return;
alist = thelist;
}
locl = NULL;
obj_type = car(alist);
}
}
/* This is a rewrite of find_method_type_pair that doesn't use
recursion but rather an explicit stack. Easier to inline. */
ref later_lists[100];
void find_method_type_pair(op, obj_type, method_ptr, type_ptr)
register ref op;
ref obj_type;
ref *method_ptr, *type_ptr;
{
register ref alist;
register ref *locl = NULL;
ref thelist;
ref *loclist;
register ref *llp = &later_lists[0] - 1;
while (1)
{
/* First look for it in the local method alist of obj_type: */
alist = thelist =
*(loclist = &REF_SLOT(obj_type, TYPE_OP_METHOD_ALIST_OFF));
while (alist != e_nil)
{
if (car(car(alist)) == op)
{
if (locl != NULL) {
*locl = cdr(alist);
*loclist = alist;
cdr(alist) = thelist;
}
*method_ptr = cdr(car(alist));
*type_ptr = obj_type;
return;
}
alist = *(locl = &cdr(alist));
}
/* Not there, stack the supertype list and then fetch the top guy
available from the stack. */
*++llp = REF_SLOT(obj_type, TYPE_SUPER_LIST_OFF);
while (*llp == e_nil)
{
if (llp == &later_lists[0]) return; /* Nothing. */
llp -= 1;
}
locl = NULL;
obj_type = car(*llp);
*llp = cdr(*llp);
}
}
/* This takes a length and a pointer to the beginning of an Oaklisp
string and returns the equivalent C string. You must remember to
free() the storage returned by this routine. */
char *oak_c_string(len, p)
unsigned int len;
unsigned long *p;
{
char *stuff = my_malloc((long)(len+1));
int i=0, j=0;
while (i+2<len)
{
unsigned long pp = *p++;
stuff[j++] = (pp >> 2) & 0xFF;
stuff[j++] = (pp >> 10) & 0xFF;
stuff[j++] = (pp >> 18) & 0xFF;
i += 3;
}
if (i+1<len)
{
unsigned long pp = *p;
stuff[j++] = (pp >> 2) & 0xFF;
stuff[j++] = (pp >> 10) & 0xFF;
i += 2;
}
else if (i<len)
{
stuff[j++] = (*p >> 2) & 0xFF;
i += 1;
}
stuff[j] = 0;
return stuff;
}
char *dump_file = NULL;
void crunch_args(argc, argv)
int argc;
char **argv;
{
char *program_name = argv[0];
argc -= 1;
argv += 1;
while (argc > 1 && (*argv)[0] == '-')
{
switch ((*argv)[1])
{
#ifndef FAST
case 'i':
trace_insts = 1;
break;
case 'c':
trace_stcon = 1;
break;
case 'x':
trace_cxcon = 1;
break;
case 'm':
trace_meth = 1;
break;
case 'S':
trace_segs = 1;
break;
case 'M':
trace_mcache = 1;
break;
#endif
case 'T':
trace_traps = 1;
break;
case 'F':
trace_files = 1;
break;
case 'd':
dump_after = 1;
break;
case 'h':
argc -= 1;
argv += 1;
original_newspace_size = string_to_int(*argv)/sizeof(ref);
break;
case '9':
dump_decimal = 1;
break;
case 'b':
dump_binary = 1;
break;
case 'G':
gc_before_dump = 1;
break;
case 'g':
trace_gc = 1;
break;
case 'Q':
gc_shutup = TRUE;
break;
case 'f':
argc -= 1;
argv += 1;
dump_file = *argv;
break;
default:
(void)printf("Unknown option %s.\n", argv[0]);
break;
}
argc -= 1;
argv += 1;
}
if (argc != 1)
{
#ifndef FAST
(void)printf("Usage: %s [-icxmSMTFd9bGgQ] [-h bytes] oaklisp-image\n",
program_name);
#else
(void)printf("Usage: %s [-TFd9bGgQ] [-h bytes] oaklisp-image\n",
program_name);
#endif
exit(2);
}
#ifdef Mac_LSC
Init_Primitives();
#endif
init_wp();
read_world(argv[0]);
new.size = e_next_newspace_size = original_newspace_size;
alloc_space(&new);
free_point = new.start;
init_stk(&val_stk);
init_stk(&cxt_stk);
}
#ifdef Mac_LSC
_main(argc,argv)
#else
main(argc,argv)
#endif
int argc;
char **argv;
{
unsigned int e_nargs;
crunch_args(argc, argv);
/* Get the registers set to the boot code. */
e_current_method = e_boot_code;
e_env = REF_TO_PTR(REF_SLOT(e_current_method, METHOD_ENV_OFF));
e_code_segment = REF_SLOT(e_current_method, METHOD_CODE_OFF);
e_pc = CODE_SEG_FIRST_INSTR(e_code_segment);
/* Put a reasonable thing in e_bp so GC doesn't get pissed. */
e_bp = e_env;
/* Tell the boot function the truth: */
e_nargs = 0;
/* Okay, lets go: */
{
/* This is used for instructions to communicate with the trap code
when a fault is encountered. */
unsigned int trap_nargs;
register unsigned short instr;
register ref x;
ref y;
register ref *val_stk_ptr = val_stk.ptr;
ref *cxt_stk_ptr = cxt_stk.ptr;
#ifndef FAST
FILE *debug;
char str[255];
#endif
/* This fixes a bug in which the initial CHECK-NARGS in the boot code
tries to pop the operation and fails. */
PUSHVAL_IMM(INT_TO_REF(4321));
/* These TRAPx(n) macros jump to the trap code, notifying it that x
arguments have been popped off the stack and need to be put back
on (these are in the variables x, ...) and that the trap operation
should be called with the top n guys on the stack as arguments. */
#define TRAP0(N) {trap_nargs=((N)); goto arg0_tt;}
#define TRAP1(N) {trap_nargs=((N)); goto arg1_tt;}
#define TRAP0_IF(C,N) {if ((C)) TRAP0((N));}
#define TRAP1_IF(C,N) {if ((C)) TRAP1((N));}
#define CHECKTAG0(X,TAG,N) TRAP0_IF(!TAG_IS((X),(TAG)),(N))
#define CHECKTAG1(X,TAG,N) TRAP1_IF(!TAG_IS((X),(TAG)),(N))
#define CHECKCHAR0(X,N) \
TRAP0_IF(!SUBTAG_IS((X),CHAR_SUBTAG),(N))
#define CHECKCHAR1(X,N) \
TRAP1_IF(!SUBTAG_IS((X),CHAR_SUBTAG),(N))
#define CHECKTAGS1(X0,T0,X1,T1,N) \
TRAP1_IF( !TAG_IS((X0),(T0)) || !TAG_IS((X1),(T1)), (N))
#define CHECKTAGS_INT_1(X0,X1,N) \
TRAP1_IF( (((X0)|(X1)) & TAG_MASK) != 0, (N))
#ifdef SIGNALS
#define POLL_SIGNALS() if (signal_pending()) {goto intr_trap;}
#else
#define POLL_SIGNALS()
#endif
#ifndef FAST
debug = fopen("debug", "w");
#endif
/* This is the big instruction fetch/execute loop. */
#ifdef SIGNALS
enable_signal_polling();
#endif
while (1)
{
#ifndef FAST
if (trace_stcon) dump_val_stk();
if (trace_cxcon) dump_cxt_stk();
#endif
instr = *e_pc;
#define arg_field (instr>>8)
/* #define signed_arg_field SIGN_8BIT_ARG(arg_field) */
#define signed_arg_field ((short)((short)instr >> 8))
#define op_field ((instr & 0xFF) >> 2)
#ifndef FAST
if (trace_insts)
{
(void)sprintf(str, "%ld: %s (%d %d)\n",
(SPATIC_PTR((ref *)e_pc) ?
e_pc - (unsigned short *)spatic.start :
e_pc - (unsigned short *)new.start
+ 2*spatic.size),
((op_field == CASE_FOUR*0) ?
ArglessInstrs[arg_field] :
Instrs[op_field/CASE_FOUR]),
op_field, arg_field);
fputs(str, debug);
fputs(str, stdout);
fflush_stdout();
}
#endif
e_pc += 1;
/* Interrupt polling belongs here, but in order to slow things down
too much it is instead put in all the instructions that can do
transfers of control, eg branches and funcalls. This cuts all
loops without slowing down each instruction. */
switch (op_field)
{
case (CASE_FOUR*0): /* ARGLESS-INSTRUCTION xxxx */
switch (arg_field)
{
case 0: /* NOOP */
break;
case 1: /* PLUS */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Tag trickery: */
#ifdef GUARD_BIT
{
register ref z = x+y;
if (OVERFLOWN(z)) TRAP1(2);
PEEKVAL() = z;
}
#else
#ifdef HAVE_LONG_LONG
{
long long a = (long)x + (long)y;
long highcrap = a>>(WORDSIZE-2);
if (highcrap && (highcrap != -1L)) TRAP1(2);
PEEKVAL() = (ref)a;
}
#else
{
register long a = REF_TO_INT(x) + REF_TO_INT(y);
OVERFLOWN_INT(a, TRAP1(2));
PEEKVAL() = INT_TO_REF(a);
}
#endif
#endif
break;
case 2: /* NEGATE */
x = PEEKVAL();
CHECKTAG0(x,INT_TAG,1);
/* The most negative fixnum's negation isn't a fixnum. */
if (x == MIN_REF) TRAP0(1);
/* Tag trickery: */
PEEKVAL() = -x;
break;
case 3: /* EQ? */
POPVAL(x);
PEEKVAL() = x==PEEKVAL() ? e_t : e_nil;
break;
case 4: /* NOT */
PEEKVAL() = PEEKVAL() == e_nil ? e_t : e_nil;
break;
case 5: /* TIMES */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Tag trickery: */
#ifdef HAVE_LONG_LONG
{
long long a = REF_TO_INT(x) * (long)y;
long highcrap = a >> (WORDSIZE-2);
if ((highcrap != 0L) && (highcrap != -1L)) TRAP1(2);
PEEKVAL() = (ref)a;
}
#else
#ifdef GUARD_BIT
{
register ref z = REF_TO_INT(x) * (long)y;
/* Ineffective: */
if (OVERFLOWN(z)) TRAP1(2);
PEEKVAL() = z;
}
#else
#ifdef DOUBLES_FOR_OVERFLOW
{
double a = (double)REF_TO_INT(x)*(double)REF_TO_INT(y);
if ( a<(double)((long)MIN_REF/4)
|| a>(double)((long)MAX_REF/4) ) TRAP1(2);
PEEKVAL() = INT_TO_REF((long)a);
}
#else
{
long a = REF_TO_INT(x), b = REF_TO_INT(y);
unsigned long al, ah, bl, bh, hh, hllh, ll;
long answer;
bool neg = FALSE;
/* MNF check */
if (a<0) { a = -a; neg = TRUE; }
if (b<0) { b = -b; neg = !neg; }
al = a&0x7FFF;
bl = b&0x7FFF;
ah = (unsigned long)a>>15;
bh = (unsigned long)b>>15;
ll = al*bl;
hllh = al*bh+ah*bl;
hh = ah*bh;
if (hh || hllh>>15) TRAP1(2);
answer = (hllh<<15) + ll;
OVERFLOWN_INT(answer, TRAP1(2));
PEEKVAL() = INT_TO_REF(neg ? -answer : answer);
}
#endif
#endif
#endif
break;
case 6: /* LOAD-IMM ; INLINE-REF */
/* align pc to next word boundary: */
if ((unsigned long)e_pc & 2)
e_pc += 1;
/*NOSTRICT*/
PUSHVAL(*(ref *)e_pc);
e_pc += sizeof(ref) / sizeof(*e_pc);
break;
case 7: /* DIV */
/* Sign of product of args. */
/* Round towards 0. Obays identity w/ REMAINDER. */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Can't divide by 0, or the most negative fixnum by -1. */
if (y == INT_TO_REF(0) ||
y == INT_TO_REF(-1) && x == MIN_REF) TRAP1(2);
/* Tag trickery: */
PEEKVAL() = INT_TO_REF((long)x/(long)y);
break;
case 8: /* =0? */
x = PEEKVAL();
CHECKTAG0(x,INT_TAG,1);
PEEKVAL() = x == INT_TO_REF(0) ? e_t : e_nil;
break;
case 9: /* GET-TAG */
PEEKVAL() = INT_TO_REF(PEEKVAL() & TAG_MASK);
break;
case 10: /* GET-DATA */
/* With the moving gc, this should *NEVER* be used.
For ease of debugging with the multiple spaces, this
makes it seem like spatic and new spaces are contiguous,
is compatible with print_ref, and also with CRUNCH. */
x = PEEKVAL();
if (x&PTR_MASK)
{
ref *p = (x&1) ? REF_TO_PTR(x) : LOC_TO_PTR(x);
PEEKVAL() =
INT_TO_REF(
SPATIC_PTR(p) ?
p - spatic.start :
NEW_PTR(p) ?
(p - new.start) + spatic.size :
( /* This is one weird reference: */
printf("GET-DATA of "),
printref(x),
printf("\n"),
-(long)p - 1 )
);
}
else
PEEKVAL() = x&~TAG_MASKL | INT_TAG;
break;
case 11: /* CRUNCH */
POPVAL(x); /* data */
y = PEEKVAL(); /* tag */
CHECKTAGS_INT_1(x,y,2);
{
int tag = REF_TO_INT(y)&TAG_MASK;
ref z;
if (tag&PTR_MASK)
{
long i = REF_TO_INT(x);
/* For now, preclude creation of very odd references. */
TRAP1_IF(i<0, 2);
if (i < spatic.size)
z = PTR_TO_LOC(spatic.start + i);
else if (i < (spatic.size + new.size))
z = PTR_TO_LOC(new.start + (i - spatic.size));
else
{ TRAP1(2); }
}
else
z = x;
PEEKVAL() = z | tag;
}
break;
case 12: /* GETC */
/***************************** OBSOLETE? *********************/
/* Used in emergency cold load standard-input stream. */
PUSHVAL_IMM(CHAR_TO_REF(getc(stdin)));
break;
case 13: /* PUTC */
/* Used in emergency cold load standard-output stream and
for the warm boot message. */
x = PEEKVAL();
CHECKCHAR0(x,1);
(void)putc(REF_TO_CHAR(x), stdout);
fflush_stdout();
if (trace_insts || trace_stcon || trace_cxcon)
(void)printf("\n");
break;
case 14: /* CONTENTS */
x = PEEKVAL();
CHECKTAG0(x,LOC_TAG,1);
PEEKVAL() = *LOC_TO_PTR(x);
break;
case 15: /* SET-CONTENTS */
POPVAL(x);
CHECKTAG1(x,LOC_TAG,2);
*LOC_TO_PTR(x) = PEEKVAL();
break;
case 16: /* LOAD-TYPE */
PEEKVAL() = get_type(PEEKVAL());
break;
case 17: /* CONS */
{
ref *p;
ALLOCATE_SS(p, 3L, "space crunch in CONS instruction");
*p = e_cons_type;
POPVAL(x);
*(p+CONS_PAIR_CAR_OFF) = x;
*(p+CONS_PAIR_CDR_OFF) = PEEKVAL();
PEEKVAL() = PTR_TO_REF(p);
}
break;
case 18: /* <0? */
x = PEEKVAL();
CHECKTAG0(x,INT_TAG,1);
/* Tag trickery: */
PEEKVAL() = (long)x < 0 ? e_t : e_nil;
break;
case 19: /* MODULO */
/* Sign of divisor (thing being divided by). */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
if (y == INT_TO_REF(0)) TRAP1(2);
{
long a = REF_TO_INT(x) % REF_TO_INT(y);
if ((a<0 && (long)y>0) || ((long)y<0 && (long)x>0 && a>0))
a += REF_TO_INT(y);
PEEKVAL() = INT_TO_REF(a);
}
break;
case 20: /* ASH */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Tag trickery: */
{
long b = REF_TO_INT(y);
if (b<0)
PEEKVAL() = ((long)x >> -b) & ~TAG_MASKL;
else
PEEKVAL() = x << b;
}
break;
case 21: /* ROT */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Rotations can not overflow, but are kind of meaningless in
the infinite precision model we have. This instr is used
only for computing string hashes and stuff like that. */
{
unsigned long a = (unsigned long)x;
long b = REF_TO_INT(y);
#ifdef GUARD_BIT
PEEKVAL()
= FIX_GUARD_BIT(( b<0 ? (a>>-b | a<<(WORDSIZE-3+b))
: (a<<b | a>>(WORDSIZE-3-b)) )
& ~TAG_MASKL);
#else
PEEKVAL()
= ( b<0
? (a>>-b | a<<(WORDSIZE-2+b))
: (a<<b | a>>(WORDSIZE-2-b)) )
& ~TAG_MASKL;
#endif
}
break;
case 22: /* STORE-BP-I */
POPVAL(x);
CHECKTAG1(x,INT_TAG,2);
*(e_bp + REF_TO_INT(x)) = PEEKVAL();
break;
case 23: /* LOAD-BP-I */
x = PEEKVAL();
CHECKTAG0(x,INT_TAG,1);
PEEKVAL() = *(e_bp + REF_TO_INT(x));
break;
case 24: /* RETURN */
POP_CONTEXT();
break;
case 25: /* ALLOCATE */
POPVAL(x);
y = PEEKVAL();
CHECKTAG1(y,INT_TAG,2);
{
ref *p;
ALLOCATE1(p, REF_TO_INT(y),
"space crunch in ALLOCATE instruction", x);
*p = x;
PEEKVAL() = PTR_TO_REF(p++);
while (p < free_point)
*p++ = NEW_STORAGE;
}
break;
case 26: /* ASSQ */
{
register ref z;
POPVAL(z);
x = PEEKVAL();
/* y = assq(z,x); */
while (x != e_nil && car(car(x)) != z)
x = cdr(x);
}
PEEKVAL() = ((x == e_nil) ? e_nil : car(x));
break;
case 27: /* LOAD-LENGTH */
x = PEEKVAL();
PEEKVAL() =
(TAG_IS(x,PTR_TAG) ?
(REF_SLOT(REF_SLOT(x,0),TYPE_VAR_LEN_P_OFF) == e_nil ?
REF_SLOT(REF_SLOT(x,0),TYPE_LEN_OFF) :
REF_SLOT(x,1)) :
INT_TO_REF(0));
break;
case 28: /* PEEK */
PEEKVAL() = INT_TO_REF( *(short *)PEEKVAL() );
break;
case 29: /* POKE */
POPVAL(x);
*(short *)x = REF_TO_INT(PEEKVAL());
break;
case 30: /* MAKE-CELL */
{
ref *p;
ALLOCATE_SS(p,1L,"space crunch in MAKE-CELL instruction");
*p = PEEKVAL();
PEEKVAL() = PTR_TO_LOC(p);
}
break;
case 31: /* SUBTRACT */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Tag trickery: */
#ifdef GUARD_BIT
{
register ref z;
z = x-y;
if (OVERFLOWN(z)) TRAP1(2);
PEEKVAL() = z;
}
#else
{
long a = REF_TO_INT(x) - REF_TO_INT(y);
OVERFLOWN_INT(a, TRAP1(2));
PEEKVAL() = INT_TO_REF(a);
}
#endif
break;
case 32: /* = */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
PEEKVAL() = x == y ? e_t : e_nil;
break;
case 33: /* < */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Tag trickery: */
PEEKVAL() = (long)x < (long)y ? e_t : e_nil;
break;
case 34: /* LOG-NOT */
x = PEEKVAL();
CHECKTAG0(x,INT_TAG,1);
/* Tag trickery: */
PEEKVAL() = ~x - (TAG_MASK-INT_TAG);
break;
case 35: /* LONG-BRANCH distance (signed) */
POLL_SIGNALS();
e_pc += ASHR2(SIGN_16BIT_ARG(*e_pc))+1;
break;
case 36: /* LONG-BRANCH-NIL distance (signed) */
POLL_SIGNALS();
POPVAL(x);
if (x == e_nil)
e_pc += ASHR2(SIGN_16BIT_ARG(*e_pc))+1;
else
e_pc += 1;
break;
case 37: /* LONG-BRANCH-T distance (signed) */
POLL_SIGNALS();
POPVAL(x);
if (x != e_nil)
e_pc += ASHR2(SIGN_16BIT_ARG(*e_pc))+1;
else
e_pc += 1;
break;
case 38: /* LOCATE-BP-I */
x = PEEKVAL();
CHECKTAG0(x,INT_TAG,1);
PEEKVAL() = PTR_TO_LOC(e_bp + REF_TO_INT(x));
break;
case 39: /* LOAD-IMM-CON ; INLINE-REF */
/* This is like a LOAD-IMM followed by a CONTENTS. */
/* align pc to next word boundary: */
if ((unsigned long)e_pc & 2)
e_pc += 1;
/*NOSTRICT*/
x = *(ref *)e_pc;
e_pc += 2;
CHECKTAG1(x,LOC_TAG,1);
PUSHVAL(*LOC_TO_PTR(x));
break;
/* Cons instructions. */
#define CONSINSTR(a,ins) \
{ \
x = PEEKVAL(); \
CHECKTAG0(x,PTR_TAG, a); \
if (REF_SLOT(x,0) != e_cons_type) \
{ \
if (trace_traps) \
(void)printf("Type trap in ins.\n"); \
TRAP0(a); \
} \
}
case 40: /* CAR */
CONSINSTR(1,CAR);
PEEKVAL() = car(x);
break;
case 41: /* CDR */
CONSINSTR(1,CDR);
PEEKVAL() = cdr(x);
break;
case 42: /* SET-CAR */
CONSINSTR(2,SET-CAR);
POPVALS(1);
car(x) = PEEKVAL();
break;
case 43: /* SET-CDR */
CONSINSTR(2,SET-CDR);
POPVALS(1);
cdr(x) = PEEKVAL();
break;
case 44: /* LOCATE-CAR */
CONSINSTR(1,LOCATE-CAR);
PEEKVAL() = PTR_TO_LOC(&car(x));
break;
case 45: /* LOCATE-CDR */
CONSINSTR(1,LOCATE-CDR);
PEEKVAL() = PTR_TO_LOC(&cdr(x));
break;
/* Done with cons instructions. */
case 46: /* PUSH-CXT-LONG rel */
PUSH_CONTEXT(ASHR2(SIGN_16BIT_ARG(*e_pc)) + 1);
e_pc += 1;
break;
case 47: /* Call a primitive routine. */
#ifdef Mac_LSC
{
ref primRef, callRef, retRef, paramList;
POPVAL(primRef);
POPVAL(callRef);
POPVAL(retRef);
paramList = PEEKVAL();
PEEKVAL() = Call_Primitive(primRef, callRef, retRef, paramList);
}
#else
printf("Not configured for CALL-PRIMITIVE.\n");
#endif
break;
case 48: /* THROW */
POPVAL(x);
CHECKTAG1(x,PTR_TAG,2);
y = PEEKVAL();
bash_val_height(REF_TO_INT(REF_SLOT(x,ESCAPE_OBJECT_VAL_OFF)));
bash_cxt_height(REF_TO_INT(REF_SLOT(x,ESCAPE_OBJECT_CXT_OFF)));
PUSHVAL(y);
POP_CONTEXT();
break;
case 49: /* GET-WP */
PEEKVAL() = ref_to_wp(PEEKVAL());
break;
case 50: /* WP-CONTENTS */
x = PEEKVAL();
CHECKTAG0(x,INT_TAG,1);
PEEKVAL() = wp_to_ref(x);
break;
case 51: /* GC */
UNOPTC(cxt_stk.ptr = cxt_stk_ptr);
UNOPTV(val_stk.ptr = val_stk_ptr);
gc(FALSE, FALSE, "explicit call", 0L);
UNOPTV(val_stk_ptr = val_stk.ptr);
UNOPTC(cxt_stk_ptr = cxt_stk.ptr);
PUSHVAL(e_nil);
break;
case 52: /* BIG-ENDIAN? */
#ifdef BIG_ENDIAN
PUSHVAL(e_t);
#else
PUSHVAL(e_nil);
#endif
break;
case 53: /* VLEN-ALLOCATE */
POPVAL(x);
y = PEEKVAL();
CHECKTAG1(y,INT_TAG,2);
{
ref *p;
ALLOCATE1(p, REF_TO_INT(y),
"space crunch in VARLEN-ALLOCATE instruction", x);
PEEKVAL() = PTR_TO_REF(p);
*p++ = x;
*p++ = y;
while (p < free_point)
*p++ = NEW_STORAGE;
}
break;
case 54: /* INC-LOC */
/* Increment a locative by an amount. This is an instruction
rather than (%crunch (+ (%pointer loc) index) %locative-tag)
to avoid a window of gc vulnerability. All such windows
must be fully closed before engines come up. */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS1(x,LOC_TAG,y,INT_TAG,2);
PEEKVAL() = PTR_TO_LOC(LOC_TO_PTR(x)+REF_TO_INT(y));
break;
case 55: /* FILL-CONTINUATION */
/* This instruction fills a continuation object with
the appropriate values. */
CHECKVAL_POP(1);
FLUSHVAL(2);
FLUSHCXT(0);
#ifndef FAST
/* debugging check: */
if (val_stk_ptr != &val_stk.data[1])
printf("Value stack flushing error.\n");
if (cxt_stk_ptr != &cxt_stk.data[0]-1)
printf("Context stack flushing error.\n");
#endif
x = PEEKVAL();
/* CHECKTAG0(x,PTR_TAG,1); */
REF_SLOT(x,CONTINUATION_VAL_SEGS) = val_stk.segment;
REF_SLOT(x,CONTINUATION_VAL_OFF)
= INT_TO_REF(val_stk.pushed_count);
REF_SLOT(x,CONTINUATION_CXT_SEGS) = cxt_stk.segment;
REF_SLOT(x,CONTINUATION_CXT_OFF)
= INT_TO_REF(cxt_stk.pushed_count);
/* Maybe it's a good idea to reload the buffer, but I'm
not bothering and things seem to work. */
/* CHECKCXT_POP(0); */
break;
case 56: /* CONTINUE */
/* Continue a continuation. */
/* Grab the continuation. */
POPVAL(x);
/* CHECKTAG1(x,PTR_TAG,1); */
y = PEEKVAL();
/* Pull the crap out of it. */
val_stk.segment = REF_SLOT(x,CONTINUATION_VAL_SEGS);
val_stk.pushed_count
= REF_TO_INT(REF_SLOT(x,CONTINUATION_VAL_OFF));
val_stk_ptr = &val_stk.data[0]-1;
PUSHVAL_NOCHECK(y);
cxt_stk.segment = REF_SLOT(x,CONTINUATION_CXT_SEGS);
cxt_stk.pushed_count
= REF_TO_INT(REF_SLOT(x,CONTINUATION_CXT_OFF));
cxt_stk_ptr = &cxt_stk.data[0]-1;
POP_CONTEXT();
break;
case 57: /* REVERSE-CONS */
/* This is just like CONS except that it takes its args
in the other order. Makes open coded LIST better. */
{
ref *p;
ALLOCATE_SS(p, 3L, "space crunch in CONS instruction");
*p = e_cons_type;
POPVAL(x);
*(p+CONS_PAIR_CDR_OFF) = x;
*(p+CONS_PAIR_CAR_OFF) = PEEKVAL();
PEEKVAL() = PTR_TO_REF(p);
}
break;
case 58: /* MOST-NEGATIVE-FIXNUM? */
PEEKVAL() = PEEKVAL()==MIN_REF ? e_t : e_nil;
break;
case 59: /* FX-PLUS */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Tag trickery: */
PEEKVAL() = x+y;
break;
case 60: /* FX-TIMES */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Tag trickery: */
PEEKVAL() = REF_TO_INT(x)*y;
break;
case 61: /* GET-TIME */
/* Return CPU time in microseconds or #f if unavailable. */
#ifdef unix
{
struct rusage rusage_buff;
(void)getrusage(RUSAGE_SELF, &rusage_buff);
PUSHVAL_IMM(INT_TO_REF(1000000 * rusage_buff.ru_utime.tv_sec
+ rusage_buff.ru_utime.tv_usec));
}
#else
PUSHVAL(e_nil);
#endif
break;
case 62: /* REMAINDER */
/* Sign of dividend (thing being divided.) */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
if (y == INT_TO_REF(0)) TRAP1(2);
PEEKVAL() = INT_TO_REF(REF_TO_INT(x) % REF_TO_INT(y));
break;
case 63: /* QUOTIENTM */
/* Round towards -inf. Obeys identity w/ MODULO. */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Can't divide by 0, or the most negative fixnum by -1. */
if (y == INT_TO_REF(0) ||
y == INT_TO_REF(-1) && x == MIN_REF) TRAP1(2);
/* Tag trickery: */
/* I can't seem to get anything like this to work: */
PEEKVAL() = INT_TO_REF(((long)x<0 ^ (long)y<0)
? -(long)x/-(long)y
: (long)x/(long)y);
{
long a = (long)x/(long)y;
if ((long)x<0 && (long)y>0 && a*(long)y > (long)x ||
(long)y<0 && (long)x>0 && a*(long)y < (long)x)
a -= 1;
PEEKVAL() = INT_TO_REF(a);
}
break;
case 64: /* FULL-GC */
UNOPTC(cxt_stk.ptr = cxt_stk_ptr);
UNOPTV(val_stk.ptr = val_stk_ptr);
gc(FALSE, TRUE, "explicit call", 0L);
UNOPTV(val_stk_ptr = val_stk.ptr);
UNOPTC(cxt_stk_ptr = cxt_stk.ptr);
PUSHVAL(e_nil);
break;
#ifndef FAST
default:
(void)printf("\nIllegal ARGLESS instruction %d.\n", arg_field);
maybe_dump_world(333);
exit(333);
#endif
}
break;
case (CASE_FOUR*1): /* HALT n */
{
int halt_code = arg_field;
maybe_dump_world(halt_code);
exit(halt_code);
}
case (CASE_FOUR*2): /* LOG-OP log-spec */
POPVAL(x);
y = PEEKVAL();
CHECKTAGS_INT_1(x,y,2);
/* Tag trickery: */
PEEKVAL() = ( (instr&(1<< 8) ? x& y : 0)
| (instr&(1<< 9) ? ~x& y : 0)
| (instr&(1<<10) ? x&~y : 0)
| (instr&(1<<11) ? ~x&~y : 0) ) & ~TAG_MASKL;
break;
case (CASE_FOUR*3): /* BLT-STACK stuff,trash */
{
register int stuff = arg_field&0xF
, trash_m1 = (instr>>(8+4));
CHECKVAL_POP(stuff+trash_m1);
{
register ref *src = val_stk_ptr - stuff
, *dest = src - (trash_m1+1);
while (src < val_stk_ptr)
*++dest = *++src;
val_stk_ptr = dest;
}
}
break;
case (CASE_FOUR*4): /* BRANCH-NIL distance (signed) */
POLL_SIGNALS();
POPVAL(x);
if (x == e_nil)
e_pc += signed_arg_field;
break;
case (CASE_FOUR*5): /* BRANCH-T distance (signed) */
POLL_SIGNALS();
POPVAL(x);
if (x != e_nil)
e_pc += signed_arg_field;
break;
case (CASE_FOUR*6): /* BRANCH distance (signed) */
POLL_SIGNALS();
e_pc += signed_arg_field;
break;
case (CASE_FOUR*7): /* POP n */
POPVALS((int)arg_field);
break;
case (CASE_FOUR*8): /* SWAP n */
{
ref *other;
MAKE_BACK_VAL_PTR(other, (int)arg_field);
x = PEEKVAL();
PEEKVAL() = *other;
*other = x;
}
break;
case (CASE_FOUR*9): /* BLAST n */
CHECKVAL_POP((int)arg_field);
{
ref *other = val_stk_ptr - arg_field;
*other = POPVAL_NOCHECK();
}
break;
case (CASE_FOUR*10): /* LOAD-IMM-FIX signed-arg */
/* Tag trickery and opcode knowledge changes this
PUSHVAL_IMM(INT_TO_REF(signed_arg_field));
to this: */
PUSHVAL_IMM((ref) (((short)instr)>>6));
break;
case (CASE_FOUR*11): /* STORE-STK n */
{
ref *other;
MAKE_BACK_VAL_PTR(other, (int)arg_field);
*other = PEEKVAL();
}
break;
case (CASE_FOUR*12): /* LOAD-BP n */
PUSHVAL(*(e_bp + arg_field));
break;
case (CASE_FOUR*13): /* STORE-BP n */
*(e_bp + arg_field) = PEEKVAL();
break;
case (CASE_FOUR*14): /* LOAD-ENV n */
PUSHVAL(*(e_env + arg_field));
break;
case (CASE_FOUR*15): /* STORE-ENV n */
*(e_env + arg_field) = PEEKVAL();
break;
case (CASE_FOUR*16): /* LOAD-STK n */
/* All attempts to start this with if (arg_field == 0) for speed
have failed, so benchmark carefully before trying it. */
{
ref *other;
MAKE_BACK_VAL_PTR(other, (int)arg_field);
PUSHVAL(*other);
}
break;
case (CASE_FOUR*17): /* MAKE-BP-LOC n */
PUSHVAL(PTR_TO_LOC(e_bp + arg_field));
break;
case (CASE_FOUR*18): /* MAKE-ENV-LOC n */
PUSHVAL(PTR_TO_LOC(e_env + arg_field));
break;
case (CASE_FOUR*19): /* STORE-REG reg */
x = PEEKVAL();
switch (arg_field)
{
case 0:
e_t = x;
break;
case 1:
e_nil = x;
wp_table[0] = e_nil;
rebuild_wp_hashtable();
break;
case 2:
e_fixnum_type = x;
break;
case 3:
e_loc_type = x;
break;
case 4:
e_cons_type = x;
break;
case 5:
CHECKTAG1(x,PTR_TAG,1);
e_subtype_table = REF_TO_PTR(x) + 2;
break;
case 6:
CHECKTAG1(x,LOC_TAG,1);
e_bp = LOC_TO_PTR(x);
break;
case 7:
CHECKTAG1(x,PTR_TAG,1);
e_env = REF_TO_PTR(x);
break;
case 8:
CHECKTAG1(x,INT_TAG,1);
e_nargs = REF_TO_INT(x);
break;
case 9:
e_env_type = x;
break;
case 10:
CHECKTAG1(x,PTR_TAG,1);
e_argless_tag_trap_table = REF_TO_PTR(x) + 2;
break;
case 11:
CHECKTAG1(x,PTR_TAG,1);
e_arged_tag_trap_table = REF_TO_PTR(x) + 2;
break;
case 12:
e_object_type = x;
break;
case 13:
e_boot_code = x;
break;
case 14:
CHECKTAG1(x,LOC_TAG,1);
free_point = LOC_TO_PTR(x);
break;
case 15:
CHECKTAG1(x,LOC_TAG,1);
new.end = LOC_TO_PTR(x);
break;
case 16:
e_segment_type = x;
BASH_SEGMENT_TYPE(x);
break;
case 17:
e_uninitialized = x;
break;
case 18:
CHECKTAG1(x,INT_TAG,1);
e_next_newspace_size = REF_TO_INT(x);
break;
case 19:
e_method_type = x;
break;
default:
(void)printf("STORE-REG %d, unknown register.\n", arg_field);
break;
}
break;
case (CASE_FOUR*20): /* LOAD-REG reg */
{
ref z;
switch (arg_field)
{
case 0:
z = e_t;
break;
case 1:
z = e_nil;
break;
case 2:
z = e_fixnum_type;
break;
case 3:
z = e_loc_type;
break;
case 4:
z = e_cons_type;
break;
case 5:
z = PTR_TO_REF(e_subtype_table - 2);
break;
case 6:
z = PTR_TO_LOC(e_bp);
break;
case 7:
z = PTR_TO_REF(e_env);
break;
case 8:
z = INT_TO_REF((long)e_nargs);
break;
case 9:
z = e_env_type;
break;
case 10:
z = PTR_TO_REF(e_argless_tag_trap_table - 2);
break;
case 11:
z = PTR_TO_REF(e_arged_tag_trap_table - 2);
break;
case 12:
z = e_object_type;
break;
case 13:
z = e_boot_code;
break;
case 14:
z = PTR_TO_LOC(free_point);
break;
case 15:
z = PTR_TO_LOC(new.end);
break;
case 16:
z = e_segment_type;
break;
case 17:
z = e_uninitialized;
break;
case 18:
z = INT_TO_REF(e_next_newspace_size);
break;
case 19:
z = e_method_type;
break;
default:
(void)printf("LOAD-REG %d, unknown register.\n", arg_field);
z = e_nil;
break;
}
PUSHVAL(z);
}
break;
case (CASE_FOUR*21): /* FUNCALL-CXT, FUNCALL-CXT-BR distance (signed) */
POLL_SIGNALS();
/* NOTE: (FUNCALL-CXT) == (FUNCALL-CXT-BR 0) */
PUSH_CONTEXT(signed_arg_field);
/* Fall through to tail recursive case: */
goto funcall_tail;
case (CASE_FOUR*22): /* FUNCALL-TAIL */
/* This polling should not be moved below the trap label, since
the interrupt code will fail on a fake instruction failure. */
POLL_SIGNALS();
/* This label allows us to branch here from the tag trap code. */
funcall_tail:
x = PEEKVAL();
CHECKTAG0(x,PTR_TAG,e_nargs+1);
CHECKVAL_POP(1);
y = PEEKVAL_UP(1);
e_current_method = REF_SLOT(x,OPERATION_LAMBDA_OFF);
if (e_current_method == e_nil)
{ /* SEARCH */
ref y_type = (e_nargs == 0) ? e_object_type : get_type(y);
#ifndef NO_METH_CACHE
/* Check for cache hit: */
if (y_type == REF_SLOT(x,OPERATION_CACHE_TYPE_OFF))
{
MAYBE_PUT(trace_mcache, "H");
e_current_method = REF_SLOT(x,OPERATION_CACHE_METH_OFF);
e_bp =
REF_TO_PTR(y) +
REF_TO_INT(REF_SLOT(x,OPERATION_CACHE_TYPE_OFF_OFF));
}
else
#endif
{
/* Search the type heirarchy. */
ref meth_type, offset = INT_TO_REF(0);
/******************************************************
find_method_type_pair(x, y_type,
&e_current_method, &meth_type);
*/
{
ref obj_type = y_type;
register ref alist;
register ref *locl = NULL;
ref thelist;
ref *loclist;
register ref *llp = &later_lists[0] - 1;
while (1)
{
/* First look for it in the local method alist of obj_type: */
alist = thelist =
*(loclist = &REF_SLOT(obj_type, TYPE_OP_METHOD_ALIST_OFF));
while (alist != e_nil)
{
if (car(car(alist)) == x)
{
if (locl != NULL) {
*locl = cdr(alist);
*loclist = alist;
cdr(alist) = thelist;
}
e_current_method = cdr(car(alist));
meth_type = obj_type;
goto found_it;
}
alist = *(locl = &cdr(alist));
}
/* Not there, stack the supertype list and then fetch the top guy
available from the stack. */
*++llp = REF_SLOT(obj_type, TYPE_SUPER_LIST_OFF);
while (*llp == e_nil)
{
if (llp == &later_lists[0])
{
if (trace_traps)
(void)printf("No handler for operation!\n");
TRAP0(e_nargs+1);
}
llp -= 1;
}
locl = NULL;
obj_type = car(*llp);
*llp = cdr(*llp);
}
}
found_it:
/******************************************************/
/*
if (e_current_method == e_nil)
{
if (trace_traps)
(void)printf("No handler for operation!\n");
TRAP1(e_nargs+1);
}
*/
/* This could be dispensed with if meth_type has no
ivars and isn't variable-length-mixin. */
{
ref alist
= REF_SLOT(y_type, TYPE_TYPE_BP_ALIST_OFF);
while (alist != e_nil)
{
if (car(car(alist)) == meth_type)
{
offset = cdr(car(alist));
break;
}
alist = cdr(alist);
}
}
e_bp = REF_TO_PTR(y) + REF_TO_INT(offset);
#ifndef NO_METH_CACHE
MAYBE_PUT(trace_mcache, "M");
/* Cache the results of this search. */
REF_SLOT(x,OPERATION_CACHE_TYPE_OFF) = y_type;
REF_SLOT(x,OPERATION_CACHE_METH_OFF) = e_current_method;
REF_SLOT(x,OPERATION_CACHE_TYPE_OFF_OFF) = offset;
#endif
}
}
else if (!TAG_IS(e_current_method, PTR_TAG)
|| REF_SLOT(e_current_method, 0) != e_method_type)
{
/* TAG TRAP */
if (trace_traps)
(void)printf("Bogus or never defined operation.\n");
TRAP0(e_nargs+1);
}
/* else it's a LAMBDA. */
x = e_current_method;
e_env = REF_TO_PTR(REF_SLOT(x, METHOD_ENV_OFF));
e_pc = CODE_SEG_FIRST_INSTR(e_code_segment =
REF_SLOT(x, METHOD_CODE_OFF));
break;
case (CASE_FOUR*23): /* STORE-NARGS n */
e_nargs = arg_field;
break;
case (CASE_FOUR*24): /* CHECK-NARGS n */
if (e_nargs != arg_field)
{
if (trace_traps)
(void)printf("\n%d args passed; %d expected.\n",
e_nargs, arg_field);
TRAP0(e_nargs+1);
}
POPVALS(1);
break;
case (CASE_FOUR*25): /* CHECK-NARGS-GTE n */
if (e_nargs < arg_field)
{
if (trace_traps)
(void)printf("\n%d args passed; %d or more expected.\n", e_nargs, arg_field);
TRAP0(e_nargs+1);
}
POPVALS(1);
break;
case (CASE_FOUR*26): /* STORE-SLOT n */
POPVAL(x);
CHECKTAG1(x,PTR_TAG,2);
REF_SLOT(x, arg_field) = PEEKVAL();
break;
case (CASE_FOUR*27): /* LOAD-SLOT n */
CHECKTAG0(PEEKVAL(),PTR_TAG,1);
PEEKVAL() = REF_SLOT(PEEKVAL(), arg_field);
break;
case (CASE_FOUR*28): /* MAKE-CLOSED-ENVIRONMENT n */
/* This code might be in error if arg_field == 0, which the
compiler should never generate. */
{
register ref *p;
register int zarg_field = arg_field;
register ref z;
#ifndef FAST
if (zarg_field == 0)
{
fprintf(stderr, "MAKE-CLOSED-ENVIRONMENT 0.\n");
fflush_stderr();
}
#endif
ALLOCATE_SS(p, (long)(zarg_field+2),
"space crunch in MAKE-CLOSED-ENVIRONMENT");
CHECKVAL_POP(zarg_field-1);
z = PTR_TO_REF(p);
*p++ = e_env_type;
*p++ = INT_TO_REF(zarg_field+2);
while (zarg_field--)
*p++ = POPVAL_NOCHECK();
PUSHVAL_NOCHECK(z);
break;
}
case (CASE_FOUR*29): /* PUSH-CXT rel */
PUSH_CONTEXT(signed_arg_field);
break;
case (CASE_FOUR*30): /* LOCATE-SLOT n */
PEEKVAL()
= PTR_TO_LOC( REF_TO_PTR( PEEKVAL() ) + arg_field );
break;
case (CASE_FOUR*31): /* STREAM-PRIMITIVE n */
switch (arg_field)
{
case 0: /* n=0: get standard input stream. */
PUSHVAL((ref)stdin);
break;
case 1: /* n=1: get standard output stream. */
PUSHVAL((ref)stdout);
break;
case 2: /* n=2: get standard error output stream. */
PUSHVAL((ref)stderr);
break;
case 3: /* n=3: fopen, mode READ */
case 4: /* n=4: fopen, mode WRITE */
case 5: /* n=5: fopen, mode APPEND */
POPVAL(x);
/* How about a CHECKTAG(x,LOC_TAG,) here, eh? */
{
char *s = oak_c_string((unsigned int)REF_TO_INT(PEEKVAL()),
(unsigned long *)LOC_TO_PTR(x));
if (trace_files) (void)printf("About to open '%s'.\n", s);
PEEKVAL()
= (ref)fopen(s, arg_field == 3 ? READ_MODE :
arg_field == 4 ? WRITE_MODE : APPEND_MODE);
free(s);
}
break;
case 6: /* n=6: fclose */
PEEKVAL()
= fclose((FILE *)PEEKVAL()) == EOF ? e_nil : e_t;
break;
case 7: /* n=7: fflush */
PEEKVAL() =
#ifdef Mac_LSC
((file *)PEEKVAL() == stdout || (file *)PEEKVAL() == stderr)
? e_t :
#endif
fflush((FILE *)PEEKVAL()) == EOF ? e_nil : e_t;
break;
case 8: /* n=8: putc */
POPVAL(x);
y = PEEKVAL();
CHECKCHAR1(y,2);
PEEKVAL()
= putc(REF_TO_CHAR(y), (FILE *)x) == EOF ? e_nil : e_t;
break;
case 9: /* n=9: getc */
{
register int c = getc((FILE *)PEEKVAL());
#ifdef unix
/* When possible, if an EOF is read from an interactive
stream, the eof should be cleared so regular stuff
can be read thereafter. */
if (c == EOF)
{
if (isatty(fileno((FILE *)PEEKVAL())))
{
if (trace_files) (void)printf("Clearing EOF.\n");
clearerr((FILE *)PEEKVAL());
}
PEEKVAL() = e_nil;
}
else
PEEKVAL() = CHAR_TO_REF(c);
#else
PEEKVAL() = (c==EOF) ? e_nil : CHAR_TO_REF(c);
#endif
}
break;
case 10: /* n=10: check for interactiveness */
#ifdef unix
PEEKVAL() = isatty(fileno((FILE *)PEEKVAL())) ? e_t : e_nil;
#else
PEEKVAL() = PEEKVAL() == (ref)stdin ? e_t : e_nil;
#endif
break;
case 11: /* n=11: tell where we are */
#ifdef unix_files
PEEKVAL() = INT_TO_REF(ftell((FILE *)PEEKVAL()));
#else
PEEKVAL() = e_nil;
#endif
break;
case 12: /* n=12: set where we are */
POPVAL(x);
{
#ifdef unix_files
FILE *fd = (FILE *)x;
long i = REF_TO_INT(PEEKVAL());
PEEKVAL() = fseek(fd, i, 0) == 0 ? e_t : e_nil;
#else
PEEKVAL() = e_nil;
#endif
}
break;
case 13: /* n=13: change working directory */
POPVAL(x);
#ifdef unix_files
{
char *s = oak_c_string((unsigned int)REF_TO_INT(PEEKVAL()),
(unsigned long *)LOC_TO_PTR(x));
PEEKVAL() = chdir(s) == 0 ? e_t : e_nil;
free(s);
}
#else
PEEKVAL() = e_nil;
#endif
break;
default:
(void)printf("\nNonexistent STREAM-PRIMITIVE %d.\n",
arg_field);
maybe_dump_world(333);
exit(333);
break;
}
break;
case (CASE_FOUR*32): /* FILLTAG n */
x = PEEKVAL();
CHECKTAG0(x,PTR_TAG,1);
REF_SLOT(x,ESCAPE_OBJECT_VAL_OFF) = INT_TO_REF( val_height()
- arg_field );
REF_SLOT(x,ESCAPE_OBJECT_CXT_OFF) = INT_TO_REF( cxt_height() );
break;
case (CASE_FOUR*33): /* ^SUPER-CXT, ^SUPER-CXT-BR distance */
/* Analogous to FUNCALL-CXT[-BR]. */
POLL_SIGNALS();
PUSH_CONTEXT(signed_arg_field);
/* Fall through to tail recursive case: */
goto super_tail;
case (CASE_FOUR*34): /* ^SUPER-TAIL */
/* Do not move this below the label! */
POLL_SIGNALS();
super_tail:
/* No cache, no LAMBDA hack, things are easy.
Maybe not looking at the lambda hack is a bug?
On stack: type, operation, self, args... */
{
ref the_type;
ref y_type;
ref meth_type;
POPVAL(the_type);
CHECKTAG1(the_type,PTR_TAG,e_nargs+2);
x = PEEKVAL(); /* The operation. */
CHECKTAG1(x,PTR_TAG,e_nargs+2);
CHECKVAL_POP(1);
y = PEEKVAL_UP(1); /* Self. */
y_type = get_type(y);
e_current_method = e_nil;
find_method_type_pair(x, the_type,
&e_current_method, &meth_type);
if (e_current_method == e_nil)
{
if (trace_traps)
(void)printf("No handler for ^super operation.\n");
PUSHVAL(the_type);
TRAP0(e_nargs+2);
}
/* This could be dispensed with if meth_type has no
ivars and isn't variable-length-mixin. */
{
ref alist = REF_SLOT(y_type, TYPE_TYPE_BP_ALIST_OFF);
ref offset = INT_TO_REF(0);
while (alist != e_nil)
{
if (car(car(alist)) == meth_type)
{
offset = cdr(car(alist));
break;
}
alist = cdr(alist);
}
e_bp = REF_TO_PTR(y) + REF_TO_INT(offset);
}
}
x = e_current_method;
e_env = REF_TO_PTR(REF_SLOT(x, METHOD_ENV_OFF));
e_pc = CODE_SEG_FIRST_INSTR(e_code_segment =
REF_SLOT(x, METHOD_CODE_OFF));
break;
#ifndef FAST
default:
(void)printf("\nIllegal Bytecode %d.\n", op_field);
maybe_dump_world(333);
exit(333);
#endif
}
}
/* The above loop is infinite; we branch down to here when instructions
fail, normally from tag traps, and then branch back. */
#ifdef SIGNALS
intr_trap:
clear_signal();
if (trace_traps)
(void)printf("\nINTR: opcode %d, argfield %d.", op_field, arg_field);
/* We notify Oaklisp of the user trap by telling it that a noop
instruction failed. The Oaklisp trap code must be careful to
return nothing extra on the stack, and to restore NARGS
properly. It is passed the old NARGS. */
/* the NOOP instruction. */
instr = 0;
/* Back off of the current intruction so it will get executed when
we get back from the trap code. */
e_pc -= 1;
/* Pass the trap code the current NARGS. */
x = INT_TO_REF(e_nargs);
TRAP1(1);
#endif
arg1_tt:
CHECKVAL_PUSH(3);
PUSHVAL_NOCHECK(x);
arg0_tt:
if (trace_traps)
{
(void)printf("\nTag trap: opcode %d, argfield %d.\n",
op_field, arg_field);
(void)printf("Top of stack: ");
printref(x);
(void)printf(", pc = %ld\n",
(/*NOSTRICT*/ SPATIC_PTR((ref *)e_pc)
? e_pc - (unsigned short *)spatic.start
: e_pc - (unsigned short *)new.start
+ 2*spatic.size));
}
/* Trick: to preserve tail recursiveness, push context only if next
instruction isn't a RETURN and current instruction wasn't a FUNCALL.
or a CHECK-NARGS[-GTE]. */
/* NOTE: It might be worth making sure op_field isn't recomputed
many times here if your compiler is stupid. */
if (*e_pc != (24<<8) + 0 && op_field != 21 && op_field != 22
&& op_field != 24 && op_field != 25)
PUSH_CONTEXT(0);
/* Trapping instructions stash their argument counts here: */
e_nargs = trap_nargs;
if (op_field == 0)
{
/* argless instruction. */
PUSHVAL_NOCHECK(*(e_argless_tag_trap_table + arg_field));
}
else
{
/* arg'ed instruction, so push arg field as extra argument */
PUSHVAL_NOCHECK(INT_TO_REF(arg_field));
e_nargs += 1;
PUSHVAL_NOCHECK(*(e_arged_tag_trap_table + op_field));
}
if (trace_traps)
{
(void)printf("Dispatching to ");
printref(PEEKVAL());
(void)printf(" with NARGS = %d.\n", e_nargs);
}
/* Set the instruction dispatch register in case the FUNCALL fails. */
instr = (22<<2);
goto funcall_tail;
}
}