/* lispmach.c -- Interpreter for compiled Lisp forms
   Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>

   This file is part of Jade.

   Jade is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   Jade is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Jade; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "jade.h"
#include "jade_protos.h"

#ifdef HAVE_ALLOCA
# include <alloca.h>
#endif

_PR void lispmach_init(void);

#define OP_CALL 0x08
#define OP_PUSH 0x10
#define OP_VREFC 0x18
#define OP_VSETC 0x20
#define OP_LIST 0x28
#define OP_BIND 0x30
#define OP_LAST_WITH_ARGS 0x38

#define OP_VREF 0x40
#define OP_VSET 0x41
#define OP_FREF 0x42
#define OP_FSET 0x43
#define OP_INIT_BIND 0x44
#define OP_UNBIND 0x45
#define OP_DUP	0x46
#define OP_SWAP 0x47
#define OP_POP	0x48

#define OP_NIL 0x49
#define OP_T 0x4a
#define OP_CONS 0x4b
#define OP_CAR 0x4c
#define OP_CDR 0x4d
#define OP_RPLACA 0x4e
#define OP_RPLACD 0x4f
#define OP_NTH 0x50
#define OP_NTHCDR 0x51
#define OP_ASET 0x52
#define OP_AREF 0x53
#define OP_LENGTH 0x54
#define OP_EVAL 0x55
#define OP_PLUS_2 0x56
#define OP_NEGATE 0x57
#define OP_MINUS_2 0x58
#define OP_PRODUCT_2 0x59
#define OP_DIVIDE_2 0x5a
#define OP_MOD_2 0x5b
#define OP_LOGNOT 0x5c
#define OP_NOT 0x5d
#define OP_LOGIOR_2 0x5e
#define OP_LOGAND_2 0x5f
#define OP_EQUAL 0x60
#define OP_EQ 0x61
#define OP_NUM_EQ 0x62
#define OP_NUM_NOTEQ 0x63
#define OP_GTTHAN 0x64
#define OP_GETHAN 0x65
#define OP_LTTHAN 0x66
#define OP_LETHAN 0x67
#define OP_INC 0x68
#define OP_DEC 0x69
#define OP_LSH 0x6a
#define OP_ZEROP 0x6b
#define OP_NULL 0x6c
#define OP_ATOM 0x6d
#define OP_CONSP 0x6e
#define OP_LISTP 0x6f
#define OP_NUMBERP 0x70
#define OP_STRINGP 0x71
#define OP_VECTORP 0x72
#define OP_CATCH_KLUDGE 0x73
#define OP_THROW 0x74
#define OP_UNWIND_PRO 0x75
#define OP_UN_UNWIND_PRO 0x76
#define OP_FBOUNDP 0x77
#define OP_BOUNDP 0x78
#define OP_SYMBOLP 0x79
#define OP_GET 0x7a
#define OP_PUT 0x7b
#define OP_ERROR_PRO 0x7c
#define OP_SIGNAL 0x7d
#define OP_RETURN 0x7e
#define OP_REVERSE 0x7f		/* new 12/7/94 */
#define OP_NREVERSE 0x80
#define OP_ASSOC 0x81
#define OP_ASSQ 0x82
#define OP_RASSOC 0x83
#define OP_RASSQ 0x84
#define OP_LAST 0x85
#define OP_MAPCAR 0x86
#define OP_MAPC 0x87
#define OP_MEMBER 0x88
#define OP_MEMQ 0x89
#define OP_DELETE 0x8a
#define OP_DELQ 0x8b
#define OP_DELETE_IF 0x8c
#define OP_DELETE_IF_NOT 0x8d
#define OP_COPY_SEQUENCE 0x8e
#define OP_SEQUENCEP 0x8f
#define OP_FUNCTIONP 0x90
#define OP_SPECIAL_FORM_P 0x91
#define OP_SUBRP 0x92
#define OP_EQL 0x93
#define OP_LOGXOR_2 0x94	/* new 23-8-94 */

#define OP_SET_CURRENT_BUFFER 0xb0
#define OP_SWAP_BUFFER 0xb1
#define OP_CURRENT_BUFFER 0xb2
#define OP_BUFFERP 0xb3
#define OP_MARKP 0xb4
#define OP_WINDOWP 0xb5
#define OP_SWAP_WINDOW 0xb6

#define OP_LAST_BEFORE_JMPS 0xfa
#define OP_JMP 0xfb
#define OP_JN 0xfc
#define OP_JT 0xfd
#define OP_JNP 0xfe
#define OP_JTP 0xff

#define TOP	    (*stackp)
#define RET_POP	    (*stackp--)
#define POP	    (stackp--)
#define POPN(n)	    (stackp -= n)
#define PUSH(v)	    (*(++stackp) = (v))
#define STK_USE	    (stackp - (stackbase - 1))

#define ARG_SHIFT    8
#define OP_ARG_MASK  0x07
#define OP_OP_MASK   0xf8
#define OP_ARG_1BYTE 6
#define OP_ARG_2BYTE 7

/* These macros pop as many args as required then call the specified
   function properly. */

#define CALL_1(cmd)				\
    if((TOP = cmd (TOP)))			\
	break;					\
    goto error
    
#define CALL_2(cmd)				\
    tmp = RET_POP;				\
    if((TOP = cmd (TOP, tmp)))			\
	break;					\
    goto error

#define CALL_3(cmd)				\
    tmp = RET_POP;				\
    tmp2 = RET_POP;				\
    if((TOP = cmd (TOP, tmp2, tmp)))		\
	break;					\
    goto error

_PR VALUE cmd_jade_byte_code(VALUE code, VALUE consts, VALUE stkreq);
DEFUN("jade-byte-code", cmd_jade_byte_code, subr_jade_byte_code, (VALUE code, VALUE consts, VALUE stkreq), V_Subr3, DOC_jade_byte_code) /*
::doc:jade_byte_code::
jade-byte-code CODE-STRING CONST-VEC MAX-STACK

Evaluates the string of byte codes CODE-STRING, the constants that it
references are contained in the vector CONST-VEC. MAX-STACK is a number
defining how much stack space is required to evaluate the code.

Do *not* attempt to call this function manually, the lisp file `compiler.jl'
contains a simple compiler which translates files of lisp forms into files
of byte code. See the functions `compile-file', `compile-directory' and
`compile-lisp-lib' for more details.
::end:: */
{
    VALUE *stackbase;
    register VALUE *stackp;
    /* This holds a list of sets of bindings, it can also hold the form of
       an unwind-protect that always gets eval'd (when the car is t).  */
    VALUE bindstack = sym_nil;
    register u_char *pc;
    u_char c;
    GCVAL gcv_code, gcv_consts, gcv_bindstack;
    /* The `gcv_N' field is only filled in with the stack-size when there's
       a chance of gc.	*/
    GCVALN gcv_stackbase;

    DECLARE1(code, STRINGP);
    DECLARE2(consts, VECTORP);
    DECLARE3(stkreq, NUMBERP);

#ifdef HAVE_ALLOCA
    stackbase = alloca(sizeof(VALUE) * VNUM(stkreq));
#else
    if(!(stackbase = str_alloc(sizeof(VALUE) * VNUM(stkreq))))
	return(NULL);
#endif

    stackp = stackbase - 1;
    PUSHGC(gcv_code, code);
    PUSHGC(gcv_consts, consts);
    PUSHGC(gcv_bindstack, bindstack);
    PUSHGCN(gcv_stackbase, stackbase, 0);

    pc = VSTR(code);
    while((c = *pc++) != 0)
    {
	if(c < OP_LAST_WITH_ARGS)
	{
	    register short arg;
	    switch(c & OP_ARG_MASK)
	    {
	    case OP_ARG_1BYTE:
		arg = *pc++;
		break;
	    case OP_ARG_2BYTE:
		arg = (pc[0] << ARG_SHIFT) | pc[1];
		pc += 2;
		break;
	    default:
		arg = c & OP_ARG_MASK;
	    }
	    switch(c & OP_OP_MASK)
	    {
		register VALUE tmp;
		VALUE tmp2;

	    case OP_CALL:
#ifdef MINSTACK
		if(STK_SIZE <= MINSTACK)
		{
		    STK_WARN("lisp-code");
		    TOP = cmd_signal(sym_stack_error, sym_nil);
		    goto quit;
		}
#endif
		/* args are still available above the top of the stack,
		   this just makes things a bit easier.	 */
		POPN(arg);
		tmp = TOP;
		if(SYMBOLP(tmp))
		{
		    if(VSYM(tmp)->sym_Flags & SF_DEBUG)
			single_step_flag = TRUE;
		    if(!(tmp = cmd_symbol_function(tmp, sym_nil)))
			goto error;
		}
		gcv_stackbase.gcv_N = STK_USE;
		switch(VTYPE(tmp))
		{
		case V_Subr0:
		    TOP = VSUBR0FUN(tmp)();
		    break;
		case V_Subr1:
		    TOP = VSUBR1FUN(tmp)(arg >= 1 ? stackp[1] : sym_nil);
		    break;
		case V_Subr2:
		    switch(arg)
		    {
		    case 0:
			TOP = VSUBR2FUN(tmp)(sym_nil, sym_nil);
			break;
		    case 1:
			TOP = VSUBR2FUN(tmp)(stackp[1], sym_nil);
			break;
		    default:
			TOP = VSUBR2FUN(tmp)(stackp[1], stackp[2]);
			break;
		    }
		    break;
		case V_Subr3:
		    switch(arg)
		    {
		    case 0:
			TOP = VSUBR3FUN(tmp)(sym_nil, sym_nil, sym_nil);
			break;
		    case 1:
			TOP = VSUBR3FUN(tmp)(stackp[1], sym_nil, sym_nil);
			break;
		    case 2:
			TOP = VSUBR3FUN(tmp)(stackp[1], stackp[2], sym_nil);
			break;
		    default:
			TOP = VSUBR3FUN(tmp)(stackp[1], stackp[2], stackp[3]);
			break;
		    }
		    break;
		case V_Subr4:
		    switch(arg)
		    {
		    case 0:
			TOP = VSUBR4FUN(tmp)(sym_nil, sym_nil,
					     sym_nil, sym_nil);
			break;
		    case 1:
			TOP = VSUBR4FUN(tmp)(stackp[1], sym_nil,
					     sym_nil, sym_nil);
			break;
		    case 2:
			TOP = VSUBR4FUN(tmp)(stackp[1], stackp[2],
					     sym_nil, sym_nil);
			break;
		    case 3:
			TOP = VSUBR4FUN(tmp)(stackp[1], stackp[2],
					     stackp[3], sym_nil);
			break;
		    default:
			TOP = VSUBR4FUN(tmp)(stackp[1], stackp[2],
					     stackp[3], stackp[4]);
			break;
		    }
		    break;
		case V_Subr5:
		    switch(arg)
		    {
		    case 0:
			TOP = VSUBR5FUN(tmp)(sym_nil, sym_nil, sym_nil,
					     sym_nil, sym_nil);
			break;
		    case 1:
			TOP = VSUBR5FUN(tmp)(stackp[1], sym_nil, sym_nil,
					     sym_nil, sym_nil);
			break;
		    case 2:
			TOP = VSUBR5FUN(tmp)(stackp[1], stackp[2], sym_nil,
					     sym_nil, sym_nil);
			break;
		    case 3:
			TOP = VSUBR5FUN(tmp)(stackp[1], stackp[2], stackp[3],
					     sym_nil, sym_nil);
			break;
		    case 4:
			TOP = VSUBR5FUN(tmp)(stackp[1], stackp[2], stackp[3],
					     stackp[4], sym_nil);
		    default:
			TOP = VSUBR5FUN(tmp)(stackp[1], stackp[2], stackp[3],
					     stackp[4], stackp[5]);
			break;
		    }
		    break;
		case V_SubrN:
		    tmp2 = sym_nil;
		    POPN(-arg); /* reclaim my args */
		    while(arg--)
			tmp2 = cmd_cons(RET_POP, tmp2);
		    TOP = VSUBRNFUN(tmp)(tmp2);
		    break;
		case V_Cons:
		    tmp2 = sym_nil;
		    POPN(-arg);
		    while(arg--)
			tmp2 = cmd_cons(RET_POP, tmp2);
		    if(VCAR(tmp) == sym_lambda)
		    {
			struct LispCall lc;
			lc.lc_Next = lisp_call_stack;
			lc.lc_Fun = TOP;
			lc.lc_Args = tmp2;
			lc.lc_ArgsEvalledP = sym_t;
			lisp_call_stack = &lc;
			if(!(TOP = eval_lambda(tmp, tmp2, FALSE))
			   && throw_value
			   && (VCAR(throw_value) == sym_defun))
			{
			    TOP = VCDR(throw_value);
			    throw_value = NULL;
			}
			lisp_call_stack = lc.lc_Next;
		    }
		    else if(VCAR(tmp) == sym_autoload)
			/* I can't be bothered to go to all the hassle
			   of doing this here, it's going to be slow
			   anyway so just pass it to funcall.  */
			TOP = funcall(TOP, tmp2);
		    else
		    {
			cmd_signal(sym_invalid_function, LIST_1(TOP));
			goto error;
		    }
		    break;
		default:
		    cmd_signal(sym_invalid_function, LIST_1(TOP));
		    goto error;
		}
		if(!TOP)
		    goto error;
		break;

	    case OP_PUSH:
		PUSH(VVECT(consts)->vc_Array[arg]);
		break;

	    case OP_VREFC:
		if(PUSH(cmd_symbol_value(VVECT(consts)->vc_Array[arg],
					 sym_nil)))
		{
		    break;
		}
		goto error;

	    case OP_VSETC:
		if(cmd_set(VVECT(consts)->vc_Array[arg], RET_POP))
		    break;
		goto error;

	    case OP_LIST:
		tmp = sym_nil;
		while(arg--)
		    tmp = cmd_cons(RET_POP, tmp);
		PUSH(tmp);
		break;

	    case OP_BIND:
		tmp = VVECT(consts)->vc_Array[arg];
		if(SYMBOLP(tmp))
		{
		    VCAR(bindstack) = bind_symbol(VCAR(bindstack), tmp,
						  RET_POP);
		    break;
		}
		goto error;
	    }
	}
	else
	{
	    switch(c)
	    {
		register VALUE tmp;
		VALUE tmp2;
		int i;

	    case OP_POP:
		POP;
		break;

	    case OP_VREF:
		if((TOP = cmd_symbol_value(TOP, sym_nil)))
		    break;
		goto error;

	    case OP_VSET:
		tmp = RET_POP;
		if(cmd_set(tmp, RET_POP))
		    break;
		goto error;

	    case OP_FREF:
		if((TOP = cmd_symbol_function(TOP, sym_nil)))
		    break;
		goto error;

	    case OP_FSET:
		tmp = RET_POP;
		if(cmd_fset(tmp, RET_POP))
		    break;
		goto error;

	    case OP_INIT_BIND:
		bindstack = cmd_cons(sym_nil, bindstack);
		break;

	    case OP_UNBIND:
		unbind_symbols(VCAR(bindstack));
		bindstack = VCDR(bindstack);
		break;

	    case OP_DUP:
		tmp = TOP;
		PUSH(tmp);
		break;

	    case OP_SWAP:
		tmp = TOP;
		TOP = stackp[-1];
		stackp[-1] = tmp;
		break;

	    case OP_NIL:
		PUSH(sym_nil);
		break;

	    case OP_T:
		PUSH(sym_t);
		break;

	    case OP_CONS:
		CALL_2(cmd_cons);

	    case OP_CAR:
		tmp = TOP;
		if(CONSP(tmp))
		    TOP = VCAR(tmp);
		else
		    TOP = sym_nil;
		break;

	    case OP_CDR:
		tmp = TOP;
		if(CONSP(tmp))
		    TOP = VCDR(tmp);
		else
		    TOP = sym_nil;
		break;

	    case OP_RPLACA:
		CALL_2(cmd_rplaca);

	    case OP_RPLACD:
		CALL_2(cmd_rplacd);

	    case OP_NTH:
		CALL_2(cmd_nth);

	    case OP_NTHCDR:
		CALL_2(cmd_nthcdr);

	    case OP_ASET:
		CALL_3(cmd_aset);

	    case OP_AREF:
		CALL_2(cmd_aref);

	    case OP_LENGTH:
		CALL_1(cmd_length);

	    case OP_EVAL:
		gcv_stackbase.gcv_N = STK_USE;
		CALL_1(cmd_eval);

	    case OP_PLUS_2:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) + VNUM(tmp));
		    break;
		}
		goto error;

	    case OP_NEGATE:
		if(NUMBERP(TOP))
		{
		    TOP = make_number(-VNUM(TOP));
		    break;
		}
		goto error;

	    case OP_MINUS_2:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) - VNUM(tmp));
		    break;
		}
		goto error;

	    case OP_PRODUCT_2:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) * VNUM(tmp));
		    break;
		}
		goto error;

	    case OP_DIVIDE_2:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) / VNUM(tmp));
		    break;
		}
		goto error;

	    case OP_MOD_2:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) % VNUM(tmp));
		    break;
		}
		goto error;

	    case OP_LOGNOT:
		if(NUMBERP(TOP))
		{
		    TOP = make_number(~VNUM(TOP));
		    break;
		}
		goto error;

	    case OP_NOT:
		if(TOP == sym_nil)
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_LOGIOR_2:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) | VNUM(tmp));
		    break;
		}
		goto error;

	    case OP_LOGXOR_2:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) ^ VNUM(tmp));
		    break;
		}
		goto error;

	    case OP_LOGAND_2:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) & VNUM(tmp));
		    break;
		}
		goto error;

	    case OP_EQUAL:
		tmp = RET_POP;
		if(!(VALUE_CMP(TOP, tmp)))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_EQ:
		tmp = RET_POP;
		if(TOP == tmp)
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_NUM_EQ:
		CALL_2(cmd_num_eq);

	    case OP_NUM_NOTEQ:
		CALL_2(cmd_num_noteq);

	    case OP_GTTHAN:
		tmp = RET_POP;
		if(VALUE_CMP(TOP, tmp) > 0)
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_GETHAN:
		tmp = RET_POP;
		if(VALUE_CMP(TOP, tmp) >= 0)
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_LTTHAN:
		tmp = RET_POP;
		if(VALUE_CMP(TOP, tmp) < 0)
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_LETHAN:
		tmp = RET_POP;
		if(VALUE_CMP(TOP, tmp) <= 0)
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_INC:
		if(NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) + 1);
		    break;
		}
		goto error;

	    case OP_DEC:
		if(NUMBERP(TOP))
		{
		    TOP = make_number(VNUM(TOP) - 1);
		    break;
		}
		goto error;

	    case OP_LSH:
		CALL_2(cmd_lsh);

	    case OP_ZEROP:
		if(NUMBERP(TOP) && (VNUM(TOP) == 0))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_NULL:
		if(NILP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_ATOM:
		if(!CONSP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_CONSP:
		if(CONSP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_LISTP:
		if(CONSP(TOP) || NILP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_NUMBERP:
		if(NUMBERP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_STRINGP:
		if(STRINGP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_VECTORP:
		if(VECTORP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_CATCH_KLUDGE:
		/* This is very crude.	*/
		tmp = RET_POP;
		tmp = cmd_cons(tmp, cmd_cons(TOP, sym_nil));
		gcv_stackbase.gcv_N = STK_USE;
		if((TOP = cmd_catch(tmp)))
		    break;
		goto error;

	    case OP_THROW:
		tmp = RET_POP;
		if(!throw_value)
		    throw_value = cmd_cons(TOP, tmp);
		/* This isn't really an error :-)  */
		goto error;

	    case OP_UNWIND_PRO:
		tmp = RET_POP;
		bindstack = cmd_cons(cmd_cons(sym_t, tmp), bindstack);
		break;

	    case OP_UN_UNWIND_PRO:
		gcv_stackbase.gcv_N = STK_USE;
		/* there will only be one form (a lisp-code) */
		cmd_eval(VCDR(VCAR(bindstack)));
		bindstack = VCDR(bindstack);
		break;

	    case OP_FBOUNDP:
		CALL_1(cmd_fboundp);

	    case OP_BOUNDP:
		CALL_1(cmd_boundp);

	    case OP_SYMBOLP:
		if(SYMBOLP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_GET:
		CALL_2(cmd_get);

	    case OP_PUT:
		CALL_3(cmd_put);

	    case OP_ERROR_PRO:
		/* bit of a kludge, this just calls the special-form, it
		   takes an extra argument on top of the stack - the number
		   of arguments that it has been given.	 */
		i = VNUM(RET_POP);
		tmp = sym_nil;
		while(i--)
		    tmp = cmd_cons(RET_POP, tmp);
		gcv_stackbase.gcv_N = STK_USE;
		tmp = cmd_error_protect(tmp);
		if(tmp)
		{
		    PUSH(tmp);
		    break;
		}
		goto error;

	    case OP_SIGNAL:
		CALL_2(cmd_signal);

	    case OP_RETURN:
		if(!throw_value)
		    throw_value = cmd_cons(sym_defun, TOP);
		goto error;

	    case OP_REVERSE:
		CALL_1(cmd_reverse);

	    case OP_NREVERSE:
		CALL_1(cmd_nreverse);

	    case OP_ASSOC:
		CALL_2(cmd_assoc);

	    case OP_ASSQ:
		CALL_2(cmd_assq);

	    case OP_RASSOC:
		CALL_2(cmd_rassoc);

	    case OP_RASSQ:
		CALL_2(cmd_rassq);

	    case OP_LAST:
		CALL_1(cmd_last);

	    case OP_MAPCAR:
		CALL_2(cmd_mapcar);

	    case OP_MAPC:
		CALL_2(cmd_mapc);

	    case OP_MEMBER:
		CALL_2(cmd_member);

	    case OP_MEMQ:
		CALL_2(cmd_memq);

	    case OP_DELETE:
		CALL_2(cmd_delete);

	    case OP_DELQ:
		CALL_2(cmd_delq);

	    case OP_DELETE_IF:
		CALL_2(cmd_delete_if);

	    case OP_DELETE_IF_NOT:
		CALL_2(cmd_delete_if_not);

	    case OP_COPY_SEQUENCE:
		CALL_1(cmd_copy_sequence);

	    case OP_SEQUENCEP:
		CALL_1(cmd_sequencep);

	    case OP_FUNCTIONP:
		CALL_1(cmd_functionp);

	    case OP_SPECIAL_FORM_P:
		CALL_1(cmd_special_form_p);

	    case OP_SUBRP:
		CALL_1(cmd_subrp);

	    case OP_EQL:
		tmp = RET_POP;
		if(NUMBERP(tmp) && NUMBERP(TOP))
		    TOP = (VNUM(TOP) == VNUM(tmp) ? sym_t : sym_nil);
		else
		    TOP = (TOP == tmp ? sym_t : sym_nil);
		break;

	    case OP_SET_CURRENT_BUFFER:
		CALL_2(cmd_set_current_buffer);

	    case OP_SWAP_BUFFER:
		if(!BUFFERP(TOP))
		    goto error;
		TOP = VAL(swap_buffers_tmp(curr_vw, VTX(TOP)));
		break;

	    case OP_CURRENT_BUFFER:
		CALL_1(cmd_current_buffer);

	    case OP_BUFFERP:
		if(BUFFERP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_MARKP:
		if(MARKP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_WINDOWP:
		if(WINDOWP(TOP))
		    TOP = sym_t;
		else
		    TOP = sym_nil;
		break;

	    case OP_SWAP_WINDOW:
		tmp = TOP;
		if(!WINDOWP(tmp))
		    goto error;
		TOP = VAL(curr_vw);
		curr_vw = VWIN(tmp);
		break;

	    case OP_JN:
		if(NILP(RET_POP))
		    goto do_jmp;
		pc += 2;
		break;

	    case OP_JT:
		if(!NILP(RET_POP))
		    goto do_jmp;
		pc += 2;
		break;

	    case OP_JNP:
		if(NILP(TOP))
		    goto do_jmp;
		POP;
		pc += 2;
		break;

	    case OP_JTP:
		if(NILP(TOP))
		{
		    POP;
		    pc += 2;
		    break;
		}
		/* FALL THROUGH */

	    case OP_JMP:
	    do_jmp:
		pc = VSTR(code) + ((pc[0] << ARG_SHIFT) | pc[1]);

		/* Test if an error occurred (or an interrupt) */
		TEST_INT;
		if(INT_P)
		    goto error;
		/* Test for gc time */
		if((data_after_gc >= gc_threshold) && !gc_inhibit)
		{
		    gcv_stackbase.gcv_N = STK_USE;
		    cmd_garbage_collect(sym_t);
		}
		break;

	    default:
		cmd_signal(sym_error,
			   LIST_1(MKSTR("Unknown lisp opcode")));
	    error:
		while(CONSP(bindstack))
		{
		    if(VCAR(VCAR(bindstack)) == sym_t)
		    {
			/* an unwind-pro */
			GCVAL gcv_throwval;
			VALUE throwval = throw_value;
			throw_value = NULL;
			PUSHGC(gcv_throwval, throwval);
			cmd_eval(VCDR(VCAR(bindstack)));
			POPGC;
			throw_value = throwval;
		    }
		    else
			unbind_symbols(VCAR(bindstack));
		    bindstack = VCDR(bindstack);
		}
		TOP = NULL;
		goto quit;
	    }
	}
#ifdef PARANOID
	if(stackp < (stackbase - 1))
	{
	    fprintf(stderr, "jade: stack underflow in lisp-code: aborting...\n");
	    abort();
	}
	if(stackp > (stackbase + VNUM(stkreq)))
	{
	    fprintf(stderr, "jade: stack overflow in lisp-code: aborting...\n");
	    abort();
	}
#endif
    }
#ifdef PARANOID
    if(stackp != stackbase)
	fprintf(stderr, "jade: (stackp != stackbase) at end of lisp-code\n");
#endif
    
 quit:
    /* only use this var to save declaring another */
    bindstack = TOP;
#ifndef HAVE_ALLOCA
    str_free(stackbase);
#endif
    POPGCN; POPGC; POPGC; POPGC;
    return(bindstack);
}

void
lispmach_init(void)
{
    ADD_SUBR(subr_jade_byte_code);
}
