/* Copyright (C) 1987, Barak Pearlmutter & Kevin Lang. */

#include <stdio.h>
#include "emulator.h"
#include "stacks.h"

#ifndef FAST
extern bool trace_segs;
#endif

/* This routine flushes out the stack buffer, leaving amount_to_leave. */
void flush_buffer(pstk, amount_to_leave)
     stack *pstk;
     int amount_to_leave;
{
  segment *s;
  long i , count = pstk->ptr - (&pstk->data[0]-1)
    , amount_to_flush = count - amount_to_leave
      , amount_unflushed = amount_to_flush;
  ref *src = &pstk->data[0]
    , *end = pstk->ptr - amount_to_leave;
  
  while (src < end)
    {
      /* Flush a single segment. */
      long size = amount_unflushed;

      if (size > MAX_SEGMENT_SIZE)
	size = MAX_SEGMENT_SIZE;
      
      {
	ref *p;

	ALLOCATE(p, size+SEGMENT_HEADER_LENGTH,
		 "space crunch allocating stack segment");

	s = (segment *)p;
      }

      s->type_field = e_segment_type;
      s->length_field = INT_TO_REF(size+SEGMENT_HEADER_LENGTH);
      s->previous_segment = pstk->segment;
      pstk->segment = PTR_TO_REF(s);

      for (i=0; i<size; i++)
	s->data[i] = *src++;

      amount_unflushed -= size;
#ifndef FAST
      if (trace_segs) printf("P");
#endif
    }

  for (i=0; i<amount_to_leave; i++)
    pstk->data[i] = *src++;

  pstk->ptr = &pstk->data[amount_to_leave-1];
  pstk->pushed_count += amount_to_flush;
#ifndef FAST
  if (trace_segs) printf(".");
#endif
}



/* This routine grabs some segments that have been flushed from the buffer
   and puts them back in.  Because the segments might be small, it
   may have to put more than one segment back in.  It grabs enough so that
   the buffer has at least n+1 values in it, so that at least n values could
   be popped off without underflow. */

void unflush_buffer(pstk, n)
     stack *pstk;
     int n;
{
  long i
    , number_to_pull = 0
    , count = pstk->ptr - (&pstk->data[0]-1)
    , new_count = count;
  segment *s = (segment *)REF_TO_PTR(pstk->segment);
  ref *dest;

  /* First, figure out how many segments to pull. */
  for (; new_count <= n; s = (segment *)REF_TO_PTR(s->previous_segment))
    {
      new_count += REF_TO_INT(s->length_field) - SEGMENT_HEADER_LENGTH;
      number_to_pull += 1;
    }

  /* Copy the data in the buffer up to its new home. */
  dest = &pstk->data[new_count - 1];

  for (i = count-1; i>=0; i--)
    *dest-- = pstk->data[i];

  /* Suck in the segments. */
  for (s = (segment *)REF_TO_PTR(pstk->segment);
       number_to_pull>0; number_to_pull--)
    {
      /* Suck in this segment. */
      for (i = REF_TO_INT(s->length_field) - SEGMENT_HEADER_LENGTH - 1
	   ; i >= 0; i--)
	*dest-- = s->data[i];
      s = (segment *)REF_TO_PTR(s->previous_segment);
#ifndef FAST
      if (trace_segs) printf("p");
#endif
    }

  pstk->segment = PTR_TO_REF(s);
  pstk->ptr = &pstk->data[new_count-1];
  pstk->pushed_count -= new_count - count;
#ifndef FAST
  if (trace_segs) printf(".");
#endif
}

      



void dump_stack_proc(pstk)
     stack *pstk;
{
  ref *p;

  (void)printf("stack contents (%d): ", stk_height(*pstk));

  /* For now, we don't bother to print the segments. */

  for (p = &pstk->data[0]; p<=pstk->ptr; p++)
    {
      printref(*p);
      (void)putc(p == pstk->ptr ? '\n' : ' ', stdout);
    }

  fflush_stdout();
}



void init_stk(pstk)
     stack *pstk;
{
#ifdef MALLOC_STACK_BUFFER
  pstk->data = (ref *)my_malloc((long)(sizeof(ref) * STACK_BUFFER_SIZE));
#endif
  pstk->ptr = &pstk->data[0];
  pstk->data[0] = INT_TO_REF(1234);
  /* This becomes e_nil when segment_type is loaded. */
  pstk->segment = INT_TO_REF(0);
  pstk->pushed_count = 0;
}
