/* -*-C-*-

$Id: utils.c,v 9.89 2007/01/05 21:19:25 cph Exp $

Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
    1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
    2006, 2007 Massachusetts Institute of Technology

This file is part of MIT/GNU Scheme.

MIT/GNU Scheme 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 of the License, or (at
your option) any later version.

MIT/GNU Scheme 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 MIT/GNU Scheme; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
USA.

*/

/* This file contains utilities for interrupts, errors, etc. */

#include "scheme.h"
#include "prims.h"
#include "winder.h"
#include "history.h"
#include "cmpint.h"
#include "syscall.h"

#ifdef __OS2__
extern void OS2_handle_attention_interrupt (void);
#endif

/* Helper procedures for Setup_Interrupt, which follows. */

static long
DEFUN (compute_interrupt_number, (masked_interrupts),
       long masked_interrupts)
{
  long interrupt_number = 0;
  long bit_mask = 1;
  while ((interrupt_number <= MAX_INTERRUPT_NUMBER)
	 && ((masked_interrupts & bit_mask) == 0))
    {
      interrupt_number += 1;
      bit_mask <<= 1;
    }
  return (interrupt_number);
}

/* This default is solely for compatibility with the previous behavior
   of the microcode.  It is not a good default and should be
   overridden by the runtime system.  */
#define DEFAULT_INTERRUPT_HANDLER_MASK(interrupt_number)		\
  ((1 << (interrupt_number)) - 1)

static long
DEFUN (compute_interrupt_handler_mask, (interrupt_masks, interrupt_number),
       SCHEME_OBJECT interrupt_masks AND
       long interrupt_number)
{
  if ((VECTOR_P (interrupt_masks))
      && (interrupt_number <= ((long) (VECTOR_LENGTH (interrupt_masks)))))
    {
      SCHEME_OBJECT mask =
	(VECTOR_REF (interrupt_masks, interrupt_number));
      if ((INTEGER_P (mask)) && (integer_to_long_p (mask)))
	/* Guarantee that the given interrupt is disabled.  */
	return ((integer_to_long (mask)) &~ (1 << interrupt_number));
    }
  return
    ((interrupt_number <= MAX_INTERRUPT_NUMBER)
     ? (DEFAULT_INTERRUPT_HANDLER_MASK (interrupt_number))
     : (FETCH_INTERRUPT_MASK ()));
}

static void
DEFUN (terminate_no_interrupt_handler, (masked_interrupts),
       long masked_interrupts)
{
  outf_fatal("\nInterrupts = 0x%08lx, Mask = 0x%08lx, Masked = 0x%08lx\n",
	     (FETCH_INTERRUPT_CODE ()),
	     (FETCH_INTERRUPT_MASK ()),
	     masked_interrupts);
  Microcode_Termination (TERM_NO_INTERRUPT_HANDLER);
}

SCHEME_OBJECT
DEFUN_VOID (initialize_interrupt_handler_vector)
{
  return (make_vector ((MAX_INTERRUPT_NUMBER + 2), SHARP_F, false));
}

SCHEME_OBJECT
DEFUN_VOID (initialize_interrupt_mask_vector)
{
  SCHEME_OBJECT result =
    (make_vector ((MAX_INTERRUPT_NUMBER + 2), SHARP_F, false));
  long interrupt_number;

  for (interrupt_number = 0;
       (interrupt_number <= MAX_INTERRUPT_NUMBER);
       interrupt_number += 1)
    VECTOR_SET
      (result, interrupt_number,
       (long_to_integer (DEFAULT_INTERRUPT_HANDLER_MASK (interrupt_number))));
  return (result);
}

/* Setup_Interrupt is called from the Interrupt macro to do all of the
   setup for calling the user's interrupt routines. */

void
DEFUN (Setup_Interrupt, (masked_interrupts), long masked_interrupts)
{
  SCHEME_OBJECT interrupt_handlers = SHARP_F;
  SCHEME_OBJECT interrupt_masks = SHARP_F;
  long interrupt_number = (compute_interrupt_number (masked_interrupts));
  long interrupt_mask;
  SCHEME_OBJECT interrupt_handler;

#ifdef __OS2__
  if ((1 << interrupt_number) == INT_Global_1)
    {
      OS2_handle_attention_interrupt ();
      abort_to_interpreter (PRIM_POP_RETURN);
    }
#endif /* __OS2__ */
  if (! (Valid_Fixed_Obj_Vector ()))
    {
      outf_fatal ("\nInvalid fixed-objects vector.");
      terminate_no_interrupt_handler (masked_interrupts);
    }
  interrupt_handlers = (Get_Fixed_Obj_Slot (System_Interrupt_Vector));
  interrupt_masks = (Get_Fixed_Obj_Slot (FIXOBJ_INTERRUPT_MASK_VECTOR));
  if (! (VECTOR_P (interrupt_handlers)))
    {
      outf_fatal ("\nInvalid handlers vector (0x%lx).", interrupt_handlers);
      terminate_no_interrupt_handler (masked_interrupts);
    }
  if (interrupt_number >= ((long) (VECTOR_LENGTH (interrupt_handlers))))
    {
      outf_fatal("\nInterrupt out of range: %ld (vector length = %ld).",
		 interrupt_number,
		 (VECTOR_LENGTH (interrupt_handlers)));
      terminate_no_interrupt_handler (masked_interrupts);
    }
  interrupt_mask =
    (compute_interrupt_handler_mask (interrupt_masks, interrupt_number));
  Global_Interrupt_Hook ();
  interrupt_handler = (VECTOR_REF (interrupt_handlers, interrupt_number));

#if 0
  /* This label may be used in Global_Interrupt_Hook: */
 passed_checks:
#endif
  Stop_History ();
  preserve_interrupt_mask ();
 Will_Push (STACK_ENV_EXTRA_SLOTS + 3);

  /* There used to be some code here for gc checks, but that is done
     uniformly now by RC_NORMAL_GC_DONE. */

  /* Now make an environment frame for use in calling the
     user supplied interrupt routine.  It will be given two arguments:
     the UNmasked interrupt requests, and the currently enabled
     interrupts.  */
  STACK_PUSH (LONG_TO_FIXNUM (FETCH_INTERRUPT_MASK ()));
  STACK_PUSH (LONG_TO_FIXNUM (FETCH_INTERRUPT_CODE ()));
  STACK_PUSH (interrupt_handler);
  STACK_PUSH (STACK_FRAME_HEADER + 2);
 Pushed ();
  /* Turn off interrupts: */
  SET_INTERRUPT_MASK (interrupt_mask);
}

/* Error processing utilities */

void
DEFUN (err_print, (error_code, where), long error_code AND outf_channel where)
{
  extern char * Error_Names [];

  if (error_code > MAX_ERROR)
    outf (where, "Unknown error code 0x%lx.\n", error_code);
  else
    outf (where, "Error code 0x%lx (%s).\n",
	     error_code,
	     (Error_Names [error_code]));
  return;
}

extern long death_blow;
long death_blow;

void
DEFUN (error_death, (code, message), long code AND char * message)
{
  death_blow = code;
  outf_fatal ("\nMicrocode Error: %s.\n", message);
  err_print (code, fatal_output);
  outf_error ("\n**** Stack Trace ****\n\n");
  Back_Trace (error_output);
  termination_no_error_handler ();
  /*NOTREACHED*/
}

void
DEFUN_VOID (Stack_Death)
{
  outf_fatal("\nWill_Push vs. Pushed inconsistency.\n");
  Microcode_Termination (TERM_BAD_STACK);
  /*NOTREACHED*/
}

void
DEFUN_VOID (preserve_interrupt_mask)
{
 Will_Push (CONTINUATION_SIZE);
  Store_Return (RC_RESTORE_INT_MASK);
  exp_register = (LONG_TO_FIXNUM (FETCH_INTERRUPT_MASK ()));
  Save_Cont ();
 Pushed ();
  return;
}

/* back_out_of_primitive sets the registers up so that the backout
   mechanism in interpret.c will cause the primitive to be
   restarted if the error/interrupt is proceeded. */

void
DEFUN_VOID (back_out_of_primitive_internal)
{
  long nargs;
  SCHEME_OBJECT primitive;

  /* Setup a continuation to return to compiled code if the primitive is
     restarted and completes successfully. */

  primitive = (Registers[REGBLOCK_PRIMITIVE]);
  if (! (PRIMITIVE_P (primitive)))
    {
      outf_fatal(
	      "\nback_out_of_primitive backing out when not in primitive!\n");
      Microcode_Termination (TERM_BAD_BACK_OUT);
    }
  nargs = (PRIMITIVE_N_ARGUMENTS (primitive));
  if (COMPILED_CODE_ADDRESS_P (STACK_REF (nargs)))
    compiler_apply_procedure (nargs);
  STACK_PUSH (primitive);
  STACK_PUSH (STACK_FRAME_HEADER + nargs);
  env_register = THE_NULL_ENV;
  val_register = SHARP_F;
  Store_Return (RC_INTERNAL_APPLY);
  exp_register = SHARP_F;
  (Registers[REGBLOCK_PRIMITIVE]) = SHARP_F;
  return;
}

void
DEFUN_VOID (back_out_of_primitive)
{
  back_out_of_primitive_internal ();
  Save_Cont ();
  return;
}

/* canonicalize_primitive_context should be used by "unsafe" primitives
   to guarantee that their execution context is the expected one, ie.
   they are called from the interpreter.
   If they are called from compiled code, they should abort to the
   interpreter and reenter.
   Note: This is called only from the macro PRIMITIVE_CANONICALIZE_CONTEXT,
   so that the work can be divided between them if it is an issue. */

void
DEFUN_VOID (canonicalize_primitive_context)
{
  long nargs;
  SCHEME_OBJECT primitive;

  primitive = (Registers[REGBLOCK_PRIMITIVE]);
  if (! (PRIMITIVE_P (primitive)))
    {
      outf_fatal
	("\ncanonicalize_primitive_context invoked when not in primitive!\n");
      Microcode_Termination (TERM_BAD_BACK_OUT);
    }
  nargs = (PRIMITIVE_N_ARGUMENTS (primitive));
  if (! (COMPILED_CODE_ADDRESS_P (STACK_REF (nargs))))
    return;
  /* The primitive has been invoked from compiled code. */
  PRIMITIVE_ABORT (PRIM_REENTER);
  /*NOTREACHED*/
}

/* Useful error procedures */

/* Note that backing out of the primitives happens after aborting,
   not before.
   This guarantees that the interpreter state is consistent, since the
   longjmp restores the relevant registers even if the primitive was
   invoked from compiled code. */

void
DEFUN (signal_error_from_primitive, (error_code), long error_code)
{
  PRIMITIVE_ABORT (error_code);
  /*NOTREACHED*/
}

void
DEFUN_VOID (signal_interrupt_from_primitive)
{
  PRIMITIVE_ABORT (PRIM_INTERRUPT);
  /*NOTREACHED*/
}

void
DEFUN (error_wrong_type_arg, (n), int n)
{
  fast long error_code;

  switch (n)
    {
    case 1: error_code = ERR_ARG_1_WRONG_TYPE; break;
    case 2: error_code = ERR_ARG_2_WRONG_TYPE; break;
    case 3: error_code = ERR_ARG_3_WRONG_TYPE; break;
    case 4: error_code = ERR_ARG_4_WRONG_TYPE; break;
    case 5: error_code = ERR_ARG_5_WRONG_TYPE; break;
    case 6: error_code = ERR_ARG_6_WRONG_TYPE; break;
    case 7: error_code = ERR_ARG_7_WRONG_TYPE; break;
    case 8: error_code = ERR_ARG_8_WRONG_TYPE; break;
    case 9: error_code = ERR_ARG_9_WRONG_TYPE; break;
    case 10: error_code = ERR_ARG_10_WRONG_TYPE; break;
    default: error_code = ERR_EXTERNAL_RETURN; break;
    }
  signal_error_from_primitive (error_code);
}

void
DEFUN (error_bad_range_arg, (n), int n)
{
  fast long error_code;

  switch (n)
    {
    case 1: error_code = ERR_ARG_1_BAD_RANGE; break;
    case 2: error_code = ERR_ARG_2_BAD_RANGE; break;
    case 3: error_code = ERR_ARG_3_BAD_RANGE; break;
    case 4: error_code = ERR_ARG_4_BAD_RANGE; break;
    case 5: error_code = ERR_ARG_5_BAD_RANGE; break;
    case 6: error_code = ERR_ARG_6_BAD_RANGE; break;
    case 7: error_code = ERR_ARG_7_BAD_RANGE; break;
    case 8: error_code = ERR_ARG_8_BAD_RANGE; break;
    case 9: error_code = ERR_ARG_9_BAD_RANGE; break;
    case 10: error_code = ERR_ARG_10_BAD_RANGE; break;
    default: error_code = ERR_EXTERNAL_RETURN; break;
    }
  signal_error_from_primitive (error_code);
}

void
DEFUN_VOID (error_external_return)
{
  signal_error_from_primitive (ERR_EXTERNAL_RETURN);
}

static SCHEME_OBJECT error_argument;

void
DEFUN (error_with_argument, (argument), SCHEME_OBJECT argument)
{
  error_argument = argument;
  signal_error_from_primitive (ERR_WITH_ARGUMENT);
  /*NOTREACHED*/
}

void
DEFUN (error_in_system_call, (err, name),
       enum syserr_names err AND enum syscall_names name)
{
  /* System call errors have some additional information.
     Encode this as a vector in place of the error code.  */
  SCHEME_OBJECT v = (allocate_marked_vector (TC_VECTOR, 3, 0));
  VECTOR_SET (v, 0, (LONG_TO_UNSIGNED_FIXNUM (ERR_IN_SYSTEM_CALL)));
  VECTOR_SET (v, 1, (LONG_TO_UNSIGNED_FIXNUM ((unsigned int) err)));
  VECTOR_SET (v, 2, (LONG_TO_UNSIGNED_FIXNUM ((unsigned int) name)));
  error_argument = v;
  signal_error_from_primitive (ERR_IN_SYSTEM_CALL);
  /*NOTREACHED*/
}

void
DEFUN (error_system_call, (code, name),
       int code AND enum syscall_names name)
{
  error_in_system_call ((OS_error_code_to_syserr (code)), name);
  /*NOTREACHED*/
}

long
DEFUN (arg_integer, (arg_number), int arg_number)
{
  fast SCHEME_OBJECT object = (ARG_REF (arg_number));
  if (! (INTEGER_P (object)))
    error_wrong_type_arg (arg_number);
  if (! (integer_to_long_p (object)))
    error_bad_range_arg (arg_number);
  return (integer_to_long (object));
}

long
DEFUN (arg_nonnegative_integer, (arg_number), int arg_number)
{
  fast long result = (arg_integer (arg_number));
  if (result < 0)
    error_bad_range_arg (arg_number);
  return (result);
}

long
DEFUN (arg_index_integer, (arg_number, upper_limit),
       int arg_number AND long upper_limit)
{
  fast long result = (arg_integer (arg_number));
  if ((result < 0) || (result >= upper_limit))
    error_bad_range_arg (arg_number);
  return (result);
}

long
DEFUN (arg_integer_in_range,
       (arg_number, lower_limit, upper_limit),
       int arg_number AND long lower_limit AND long upper_limit)
{
  fast long result = (arg_integer (arg_number));
  if ((result < lower_limit) || (result >= upper_limit))
    error_bad_range_arg (arg_number);
  return (result);
}

unsigned long
DEFUN (arg_ulong_integer, (arg_number), int arg_number)
{
  fast SCHEME_OBJECT object = (ARG_REF (arg_number));
  if (! (INTEGER_P (object)))
    error_wrong_type_arg (arg_number);
  if (! (integer_to_ulong_p (object)))
    error_bad_range_arg (arg_number);
  return (integer_to_ulong (object));
}

unsigned long
DEFUN (arg_ulong_index_integer, (arg_number, upper_limit),
       int arg_number AND unsigned long upper_limit)
{
  fast unsigned long result = (arg_ulong_integer (arg_number));
  if (result >= upper_limit)
    error_bad_range_arg (arg_number);
  return (result);
}

Boolean
DEFUN (real_number_to_double_p, (x), fast SCHEME_OBJECT x)
{
  return ((! (BIGNUM_P (x))) || (BIGNUM_TO_DOUBLE_P (x)));
}

double
DEFUN (real_number_to_double, (x), fast SCHEME_OBJECT x)
{
  return
    ((FIXNUM_P (x))
     ? (FIXNUM_TO_DOUBLE (x))
     : (BIGNUM_P (x))
     ? (bignum_to_double (x))
     : (FLONUM_TO_DOUBLE (x)));
}

double
DEFUN (arg_real_number, (arg_number), int arg_number)
{
  fast SCHEME_OBJECT number = (ARG_REF (arg_number));
  if (! (REAL_P (number)))
    error_wrong_type_arg (arg_number);
  if (! (real_number_to_double_p (number)))
    error_bad_range_arg (arg_number);
  return (real_number_to_double (number));
}

double
DEFUN (arg_real_in_range, (arg_number, lower_limit, upper_limit),
       int arg_number AND double lower_limit AND double upper_limit)
{
  fast double result = (arg_real_number (arg_number));
  if ((result < lower_limit) || (result > upper_limit))
    error_bad_range_arg (arg_number);
  return (result);
}

Boolean
DEFUN (interpreter_applicable_p, (object), fast SCHEME_OBJECT object)
{
 tail_recurse:
  switch (OBJECT_TYPE (object))
    {
    case TC_PRIMITIVE:
    case TC_PROCEDURE:
    case TC_EXTENDED_PROCEDURE:
    case TC_CONTROL_POINT:
      return (true);

    case TC_ENTITY:
      {
	object = (MEMORY_REF (object, ENTITY_OPERATOR));
	goto tail_recurse;
      }
    case TC_COMPILED_ENTRY:
      {
	long results [3];
	compiled_entry_type (object, results);
	return ((results [0]) == 0);
      }
    default:
      return (false);
    }
}

                      /******************/
                      /* ERROR HANDLING */
                      /******************/

/* It is assumed that any caller of the error code has already
 * restored its state to a situation which will make it
 * restartable if the error handler returns normally.  As a
 * result, the only work to be done on an error is to verify
 * that there is an error handler, save the current continuation and
 * create a new one if entered from Pop_Return rather than Eval,
 * turn off interrupts, and call it with two arguments: Error-Code
 * and Interrupt-Enables.
 */

void
DEFUN (Do_Micro_Error, (Err, From_Pop_Return),
       long Err AND Boolean From_Pop_Return)
{
  SCHEME_OBJECT Error_Vector = SHARP_F;
  SCHEME_OBJECT Handler;

  if (Consistency_Check)
  {
    err_print(Err, error_output);
    Print_Expression(exp_register, "Expression was");
    outf_error ("\nEnvironment 0x%lx (#%lo).\n",
	    ((long) exp_register), ((long) env_register));
    Print_Return("Return code");
    outf_error ("\n");
  }

  Error_Exit_Hook();

  if (Trace_On_Error)
  {
    outf_error ("\n\n**** Stack Trace ****\n\n");
    Back_Trace (error_output);
  }

#ifdef ENABLE_DEBUGGING_TOOLS
  {
    int *From = &(local_circle[0]), *To = &(debug_circle[0]), i;

    for (i = 0; i < local_nslots; i++)
      *To++ = *From++;
    debug_nslots = local_nslots;
    debug_slotno = local_slotno;
  }
#endif

/* Do_Micro_Error continues on the next page. */

/* Do_Micro_Error, continued */

  /* This can NOT be folded into the Will_Push below since we cannot
     afford to have the Will_Push put down its own continuation.
     There is guaranteed to be enough space for this one
     continuation; in fact, the Will_Push here is really unneeded!
   */

  if (From_Pop_Return)
  {
   Will_Push (CONTINUATION_SIZE);
    Save_Cont ();
   Pushed ();
  }

 Will_Push (CONTINUATION_SIZE + (From_Pop_Return ? 0 : 1));
  if (From_Pop_Return)
    exp_register = val_register;
  else
    STACK_PUSH (env_register);
  Store_Return ((From_Pop_Return) ?
		RC_POP_RETURN_ERROR :
		RC_EVAL_ERROR);
  Save_Cont ();
 Pushed ();

/* Do_Micro_Error continues on the next page. */

/* Do_Micro_Error, continued */

  if ((!Valid_Fixed_Obj_Vector()) ||
      (OBJECT_TYPE ((Error_Vector =
		    Get_Fixed_Obj_Slot(System_Error_Vector))) !=
       TC_VECTOR))
  {
    error_death (Err,
		 (((Valid_Fixed_Obj_Vector())
		   && (Error_Vector == SHARP_F))
		  ? "No error handlers"
		  : "No error handlers: Bad handlers vector"));
    /*NOTREACHED*/
  }

  if ((Err < 0) || (Err >= ((long) (VECTOR_LENGTH (Error_Vector)))))
  {
    if (VECTOR_LENGTH (Error_Vector) == 0)
      error_death (Err, "No error handlers: Empty handlers vector");
      /*NOTREACHED*/
    Handler = (VECTOR_REF (Error_Vector, ERR_BAD_ERROR_CODE));
  }
  else
    Handler = (VECTOR_REF (Error_Vector, Err));

  /* Return from error handler will re-enable interrupts & restore history */
  Stop_History();
  preserve_interrupt_mask ();

 Will_Push (STACK_ENV_EXTRA_SLOTS + 3);
  /* Arg 2:     Int. mask */
  STACK_PUSH (LONG_TO_FIXNUM(FETCH_INTERRUPT_MASK()));
  /* Arg 1:     Err. No   */
  if ((Err == ERR_WITH_ARGUMENT) || (Err == ERR_IN_SYSTEM_CALL))
    STACK_PUSH (error_argument);
  else if ((Err >= SMALLEST_FIXNUM) && (Err <= BIGGEST_FIXNUM))
    STACK_PUSH (LONG_TO_FIXNUM (Err));
  else
    STACK_PUSH (LONG_TO_UNSIGNED_FIXNUM (ERR_BAD_ERROR_CODE));
  /* Procedure: Handler   */
  STACK_PUSH (Handler);
  STACK_PUSH (STACK_FRAME_HEADER + 2);
 Pushed();

  /* Disable all interrupts */
  SET_INTERRUPT_MASK(0);
  return;
}

/* HISTORY manipulation */

SCHEME_OBJECT *
DEFUN_VOID (Make_Dummy_History)
{
  SCHEME_OBJECT *History_Rib = Free;
  SCHEME_OBJECT *Result;

  Free[RIB_EXP] = SHARP_F;
  Free[RIB_ENV] = SHARP_F;
  Free[RIB_NEXT_REDUCTION] =
    MAKE_POINTER_OBJECT (UNMARKED_HISTORY_TYPE, History_Rib);
  Free += 3;
  Result = Free;
  Free[HIST_RIB] = MAKE_POINTER_OBJECT (UNMARKED_HISTORY_TYPE, History_Rib);
  Free[HIST_NEXT_SUBPROBLEM] =
    MAKE_POINTER_OBJECT (UNMARKED_HISTORY_TYPE, Result);
  Free[HIST_PREV_SUBPROBLEM] =
    MAKE_POINTER_OBJECT (UNMARKED_HISTORY_TYPE, Result);
  Free += 3;
  return (Result);
}

/* The entire trick to history is right here: it is either copied or
   reused when restored.  Initially, Stop_History marks the stack so
   that the history will merely be popped and reused.  On a catch,
   however, the return code is changed to force the history to be
   copied instead.  Thus, histories saved as part of a control point
   are not side-effected in the history collection process.
*/

void
DEFUN_VOID (Stop_History)
{
  SCHEME_OBJECT Saved_Expression;
  long Saved_Return_Code;

  Saved_Expression = exp_register;
  Saved_Return_Code = ret_register;
 Will_Push(HISTORY_SIZE);
  Save_History(RC_RESTORE_DONT_COPY_HISTORY);
 Pushed();
  Prev_Restore_History_Stacklet = NULL;
  Prev_Restore_History_Offset = ((Get_End_Of_Stacklet() - sp_register) +
				 CONTINUATION_RETURN_CODE);
  exp_register = Saved_Expression;
  Store_Return(Saved_Return_Code);
  return;
}

/* This returns a history object,
   or SHARP_F if it needs to GC,
   or SHARP_T if it is not a valid history object.
 */

SCHEME_OBJECT
DEFUN (copy_history, (hist_obj), SCHEME_OBJECT hist_obj)
{
  long space_left, vert_type, rib_type;
  SCHEME_OBJECT *fast_free;
  SCHEME_OBJECT new_hunk, *last_hunk, *hist_ptr, *orig_hist, temp;
  SCHEME_OBJECT *orig_rib, *source_rib, *rib_slot;

  if (!(HUNK3_P (hist_obj)))
    return (SHARP_T);

  space_left = ((Space_Before_GC ()) - 3);
  fast_free = Free;

  vert_type = (OBJECT_TYPE (hist_obj));
  orig_hist = (OBJECT_ADDRESS (hist_obj));
  hist_ptr = orig_hist;
  last_hunk = (Heap_Top - 3);

  do
  {
    /* Allocate and link the vertebra. */

    space_left -= 3;
    if (space_left < 0)
      return (SHARP_F);

    new_hunk = (MAKE_POINTER_OBJECT (vert_type, fast_free));
    last_hunk[HIST_NEXT_SUBPROBLEM] = new_hunk;

    fast_free[HIST_PREV_SUBPROBLEM] =
      (MAKE_POINTER_OBJECT ((OBJECT_TYPE (hist_ptr[HIST_PREV_SUBPROBLEM])),
			    last_hunk));
    last_hunk = fast_free;
    fast_free += 3;

    /* Copy the rib. */

    temp = hist_ptr[HIST_RIB];
    rib_type = (OBJECT_TYPE (temp));
    orig_rib = (OBJECT_ADDRESS (temp));
    rib_slot = (last_hunk + HIST_RIB);
    
    source_rib = orig_rib;

    do
    {
      space_left -= 3;
      if (space_left < 0)
	return (SHARP_F);

      *rib_slot = (MAKE_POINTER_OBJECT (rib_type, fast_free));
      fast_free[RIB_EXP] = source_rib[RIB_EXP];
      fast_free[RIB_ENV] = source_rib[RIB_ENV];
      rib_slot = (fast_free + RIB_NEXT_REDUCTION);
      fast_free += 3;
      
      temp = source_rib[RIB_NEXT_REDUCTION];
      rib_type = (OBJECT_TYPE (temp));
      source_rib = (OBJECT_ADDRESS (temp));
    } while (source_rib != orig_rib);
      
    *rib_slot = (OBJECT_NEW_TYPE (rib_type, last_hunk[HIST_RIB]));

    temp = hist_ptr[HIST_NEXT_SUBPROBLEM];
    vert_type = (OBJECT_TYPE (temp));
    hist_ptr = (OBJECT_ADDRESS (temp));
  } while (hist_ptr != orig_hist);

  Free = fast_free;
  new_hunk = Heap_Top[HIST_NEXT_SUBPROBLEM - 3];
  last_hunk[HIST_NEXT_SUBPROBLEM] = (OBJECT_NEW_TYPE (vert_type, new_hunk));
  FAST_MEMORY_SET (new_hunk, HIST_PREV_SUBPROBLEM,
		   (MAKE_POINTER_OBJECT
		    ((OBJECT_TYPE (hist_ptr[HIST_PREV_SUBPROBLEM])),
		     last_hunk)));
  return (new_hunk);
}

/* Restore_History pops a history object off the stack and
   makes a COPY of it the current history collection object.
   This is called only from the RC_RESTORE_HISTORY case in
   interpret.c .
 */

Boolean
DEFUN (Restore_History, (hist_obj), SCHEME_OBJECT hist_obj)
{
  SCHEME_OBJECT new_hist;

  new_hist = (copy_history (hist_obj));
  if (new_hist == SHARP_F)
    return (false);
  else if (new_hist == SHARP_T)
  {
    outf_fatal ("\nBad history to restore.\n");
    Microcode_Termination (TERM_EXIT);
    /*NOTREACHED*/
    return (0);
  }
  else
  {
    history_register = (OBJECT_ADDRESS (new_hist));
    return (1);
  }
}

/* If a "debugging" version of the interpreter is made, then this
   procedure is called to actually invoke a primitive.  When a
   "production" version is made, all of the consistency checks are
   omitted and a macro from "default.h" is used to directly code the
   call to the primitive function. */

#ifdef ENABLE_DEBUGGING_TOOLS

SCHEME_OBJECT
DEFUN (primitive_apply_internal, (primitive), SCHEME_OBJECT primitive)
{
  SCHEME_OBJECT result;
  if (Primitive_Debug)
    Print_Primitive (primitive);
  {
    SCHEME_OBJECT * saved_stack = sp_register;
    PRIMITIVE_APPLY_INTERNAL (result, primitive);
    if (saved_stack != sp_register)
      {
	int arity = (PRIMITIVE_N_ARGUMENTS (primitive));
	Print_Expression (primitive, "Stack bad after ");
	outf_fatal ("\nStack was 0x%lx, now 0x%lx, #args=%ld.\n",
		    ((long) saved_stack), ((long) sp_register), ((long) arity));
	Microcode_Termination (TERM_EXIT);
      }
  }
  if (Primitive_Debug)
    {
      Print_Expression (result, "Primitive Result");
      outf_error("\n");
      outf_flush_error();
    }
  return (result);
}

#endif /* ENABLE_DEBUGGING_TOOLS */

#ifdef ENABLE_PRIMITIVE_PROFILING

/* The profiling mechanism is enabled by storing a vector in the fixed
   objects vector.  The vector should be initialized to contain all zeros
 */

void
DEFUN (record_primitive_entry, (primitive), SCHEME_OBJECT primitive)
{
  SCHEME_OBJECT table;

  if ((Fixed_Objects != SHARP_F) &&
      ((table = Get_Fixed_Obj_Slot (Primitive_Profiling_Table)) != SHARP_F))
  {
    long index = (1 + (OBJECT_DATUM (primitive)));
    MEMORY_SET
      (table,
       index,
       (long_to_integer (1 + (integer_to_long (MEMORY_REF (table, index))))));
  }
  return;
}

#endif /* ENABLE_PRIMITIVE_PROFILING */

#ifdef USE_STACKLETS
                      /******************/
                      /*   STACKLETS    */
                      /******************/

void
DEFUN (Allocate_New_Stacklet, (N), long N)
{
  SCHEME_OBJECT Old_Expression, *Old_Stacklet, Old_Return;

  Old_Stacklet = Current_Stacklet;
  Terminate_Old_Stacklet();
  if ((Free_Stacklets == NULL) ||
      ((N + STACKLET_SLACK) >
       (OBJECT_DATUM (Free_Stacklets[STACKLET_LENGTH]))))
  {
    long size;

    /*
      Room is set aside for the header bytes of a stacklet plus
      the two words required for the RC_JOIN_STACKLETS frame.
     */

    size = New_Stacklet_Size(N);
    if (GC_Check(size))
    {
      Request_GC(size);
      if ((Free + size) >= Heap_Top)
	Microcode_Termination(TERM_STACK_OVERFLOW);
    }
    Free[STACKLET_LENGTH] = MAKE_OBJECT (TC_MANIFEST_VECTOR, (size - 1));
    SET_STACK_GUARD (& (Free[STACKLET_HEADER_SIZE]));
    Free += size;
    sp_register = Free;
  }
  else
  {
    /* Grab first one on the free list */

    SCHEME_OBJECT *New_Stacklet;

    New_Stacklet = Free_Stacklets;
    Free_Stacklets =
      ((SCHEME_OBJECT *) Free_Stacklets[STACKLET_FREE_LIST_LINK]);
    sp_register =
      &New_Stacklet[1 + (OBJECT_DATUM (New_Stacklet[STACKLET_LENGTH]))];
    SET_STACK_GUARD (& (New_Stacklet[STACKLET_HEADER_SIZE]));
  }
  Old_Expression = exp_register;
  Old_Return = ret_register;
  exp_register = (MAKE_POINTER_OBJECT (TC_CONTROL_POINT, Old_Stacklet));
  Store_Return(RC_JOIN_STACKLETS);
  /*
    Will_Push omitted because size calculation includes enough room.
   */
  Save_Cont();
  exp_register = Old_Expression;
  Store_Return(Old_Return);
  return;
}

#endif /* USE_STACKLETS */

/* Dynamic Winder support code */

SCHEME_OBJECT
DEFUN (Find_State_Space, (State_Point), SCHEME_OBJECT State_Point)
{
  long How_Far =
    (UNSIGNED_FIXNUM_TO_LONG
     (FAST_MEMORY_REF (State_Point, STATE_POINT_DISTANCE_TO_ROOT)));
  long i;
  fast SCHEME_OBJECT Point = State_Point;

  for (i=0; i <= How_Far; i++)
  {
#ifdef ENABLE_DEBUGGING_TOOLS
    if (Point == SHARP_F)
    {
      outf_fatal(
	      "\nState_Point 0x%lx wrong: count was %ld, #F at %ld\n",
	      ((long) State_Point), ((long) How_Far), ((long) i));
      Microcode_Termination(TERM_EXIT);
      /*NOTREACHED*/
    }
#endif /* ENABLE_DEBUGGING_TOOLS */
    Point = FAST_MEMORY_REF (Point, STATE_POINT_NEARER_POINT);
  }
  return (Point);
}

/* ASSUMPTION: State points, which are created only by the interpreter,
   never contain FUTUREs except possibly as the thunks (which are handled
   by the apply code).

   Furthermore:
     (1) On a single processor, things should work with multiple state
	 spaces.  The microcode variable Current_State_Point tracks
	 the location in the "boot" space (i.e. the one whose space is
	 #F) and the state spaces themselves (roots of the space
	 trees) track the other spaces.
     (2) On multi-processors, multiple spaces DO NOT work.  Only the
	 initial space (#F) is tracked by the microcode (it is
	 swapped on every task switch), but no association with trees
	 is kept.  This will work since the initial tree has no space
	 at the root, indicating that the microcode variable rather
	 than the state space contains the current state space
	 location.

   NOTE: This procedure is invoked both by primitives and the interpreter
   itself.  As such, it is using the pun that PRIMITIVE_ABORT is just a
   (non-local) return to the interpreter.  This should be cleaned up.
   NOTE: Any primitive that invokes this procedure must do a
   PRIMITIVE_CANONICALIZE_CONTEXT() first!
*/

void
DEFUN (Translate_To_Point, (Target), SCHEME_OBJECT Target)
{
  SCHEME_OBJECT State_Space, Current_Location, *Path;
  fast SCHEME_OBJECT Path_Point, *Path_Ptr;
  long Distance, Merge_Depth, From_Depth, i;

  State_Space = Find_State_Space(Target);
  Path = Free;
  guarantee_state_point();
  Distance =
    (UNSIGNED_FIXNUM_TO_LONG
     (FAST_MEMORY_REF (Target, STATE_POINT_DISTANCE_TO_ROOT)));
  if (State_Space == SHARP_F)
    Current_Location = Current_State_Point;
  else
    Current_Location = MEMORY_REF (State_Space, STATE_SPACE_NEAREST_POINT);

  if (Target == Current_Location)
  {
    PRIMITIVE_ABORT (PRIM_POP_RETURN);
    /*NOTREACHED*/
  }

  for (Path_Ptr = (&(Path[Distance])), Path_Point = Target, i = 0;
       i <= Distance;
       i++)
  {
    *Path_Ptr-- = Path_Point;
    Path_Point = FAST_MEMORY_REF (Path_Point, STATE_POINT_NEARER_POINT);
  }

  From_Depth =
    (UNSIGNED_FIXNUM_TO_LONG
     (FAST_MEMORY_REF (Current_Location, STATE_POINT_DISTANCE_TO_ROOT)));

  for (Path_Point = Current_Location, Merge_Depth = From_Depth;
       Merge_Depth > Distance;
       Merge_Depth--)
    Path_Point = FAST_MEMORY_REF (Path_Point, STATE_POINT_NEARER_POINT);

  for (Path_Ptr = (&(Path[Merge_Depth]));
       Merge_Depth >= 0;
       Merge_Depth--, Path_Ptr--)
  {
    if (*Path_Ptr == Path_Point)
      break;
    Path_Point = FAST_MEMORY_REF (Path_Point, STATE_POINT_NEARER_POINT);
  }

#ifdef ENABLE_DEBUGGING_TOOLS
  if (Merge_Depth < 0)
  {
    outf_fatal("\nMerge_Depth went negative: %d\n", Merge_Depth);
    Microcode_Termination (TERM_EXIT);
  }
#endif /* ENABLE_DEBUGGING_TOOLS */

  preserve_interrupt_mask ();
 Will_Push(CONTINUATION_SIZE + 4);
  STACK_PUSH (LONG_TO_UNSIGNED_FIXNUM((Distance - Merge_Depth)));
  STACK_PUSH (Target);
  STACK_PUSH (LONG_TO_UNSIGNED_FIXNUM((From_Depth - Merge_Depth)));
  STACK_PUSH (Current_Location);
  exp_register = State_Space;
  Store_Return(RC_MOVE_TO_ADJACENT_POINT);
  Save_Cont();
 Pushed();

  {
    long mask;

    /* Disable lower than GC level */
    mask = (FETCH_INTERRUPT_MASK() & ((INT_GC << 1) - 1));
    SET_INTERRUPT_MASK(mask);
  }
  PRIMITIVE_ABORT (PRIM_POP_RETURN);
  /*NOTREACHED*/
}

#ifdef __WIN32__

#include <windows.h>
#include "cmpintmd.h"

SCHEME_OBJECT
DEFUN_VOID (Compiler_Get_Fixed_Objects)
{
  if (Valid_Fixed_Obj_Vector())
    return (Get_Fixed_Obj_Slot(Me_Myself));
  else
    return (SHARP_F);
}

extern SCHEME_OBJECT EXFUN (Re_Enter_Interpreter, (void));
extern SCHEME_OBJECT EXFUN
  (C_call_scheme, (SCHEME_OBJECT, long, SCHEME_OBJECT *));

SCHEME_OBJECT
DEFUN (C_call_scheme, (proc, nargs, argvec),
       SCHEME_OBJECT proc
       AND long nargs
       AND SCHEME_OBJECT * argvec)
{
  SCHEME_OBJECT primitive, prim_lexpr, * sp, result;
  SCHEME_OBJECT * callers_last_return_code;

#if (COMPILER_PROCESSOR_TYPE == COMPILER_IA32_TYPE)
  extern void * C_Frame_Pointer;
  extern void * C_Stack_Pointer;
  void * cfp = C_Frame_Pointer;
  void * csp = C_Stack_Pointer;
#ifdef CL386
  __try
#endif
#endif
  {  
    primitive = (Registers[REGBLOCK_PRIMITIVE]);
    prim_lexpr = (Registers[REGBLOCK_LEXPR_ACTUALS]);
    callers_last_return_code = last_return_code;

    if (! (PRIMITIVE_P (primitive)))
      abort_to_interpreter (ERR_CANNOT_RECURSE);
      /*NOTREACHED*/
    sp = sp_register;

   Will_Push ((2 * CONTINUATION_SIZE) + (nargs + STACK_ENV_EXTRA_SLOTS + 1));
    {
      long i;

      Store_Return (RC_END_OF_COMPUTATION);
      exp_register = primitive;
      Save_Cont ();

      for (i = nargs; --i >= 0; )
	STACK_PUSH (argvec[i]);
      STACK_PUSH (proc);
      STACK_PUSH (STACK_FRAME_HEADER + nargs);

      Store_Return (RC_INTERNAL_APPLY);
      exp_register = SHARP_F;
      Save_Cont ();
    }
   Pushed ();
    result = (Re_Enter_Interpreter ());

    if (sp_register != sp)
      signal_error_from_primitive (ERR_STACK_HAS_SLIPPED);
      /*NOTREACHED*/

    last_return_code = callers_last_return_code;
    Registers[REGBLOCK_LEXPR_ACTUALS] = prim_lexpr;
    Registers[REGBLOCK_PRIMITIVE] = primitive;
  }
#if (COMPILER_PROCESSOR_TYPE == COMPILER_IA32_TYPE)
#ifdef CL386
  __finally  
#endif
  {
    C_Frame_Pointer = cfp;
    C_Stack_Pointer = csp;
  }
#endif

  return  result;
}

#endif /* not __OS2__ */
