/*                                                      =====> SUPPORT.C     */
/* TIPC Scheme '84 Runtime Support - Non-Arithmetic Support
   (C) Copyright 1984,1985 by Texas Instruments Incorporated.
   All rights reserved.

   Author:  John C. Jensen
   Installation:  Texas Instruments Incorporated, Dallas, Texas
   Division:  Central Research Laboratories
   Cost Center:  Computer Science Laboratory
   Project:  Computer Architecture Branch
   Date Written:  24 May 1984
   Last Modification:  21 October 1985
*/
#include "scheme.h"
#include "slist.h"

char *getmem();         /* Lattice C's memory allocation routine */

/**********************************************************************/
/*                              Substring                             */
/**********************************************************************/
ssubstr(string, start_reg, end_reg)
int string[2];          /* register containing string pointer */
int start_reg[2];       /* register containing starting character position */
int end_reg[2];         /* register containing ending character position */
 {
  int end;              /* ending character position */
  int start;            /* starting character position */
  int str_page, str_disp;/* string's page and displacement pointers */
  int i;

  str_page = CORRPAGE(string[C_PAGE]);
  str_disp = string[C_DISP];

  /* validate input arguments */
  i = get_word(str_page,str_disp+1);
  if (i < 0)
    i = i + BLK_OVHD + PTRSIZE;              /* adjust for small string */
  if (ptype[str_page] == STRTYPE*2 &&
      start_reg[C_PAGE] == SPECFIX*2 &&
      end_reg[C_PAGE] == SPECFIX*2 &&
      (start = get_fix(CORRPAGE(start_reg[C_PAGE]),start_reg[C_DISP])) >= 0 &&
      (end = get_fix(CORRPAGE(end_reg[C_PAGE]),end_reg[C_DISP])) >= start  &&
      end <= i-BLK_OVHD)
   { /* arguments o.k.-- allocate new string and copy substring characters */
    alloc_block(tmp_reg, STRTYPE, end-start);
    msubstr(tmp_reg, string, start, end);
    string[C_PAGE] = tmp_page;
    string[C_DISP] = tmp_disp;
   }
  else
   { /* invalid arguments to substring */
    set_src_err("SUBSTRING", 3, string, start_reg, end_reg);
    return(-1);
   }
  return(0);
 } /* end of function:  ssubstr(string, start_reg, end_reg) */


/**********************************************************************/
/*                 Test if two pointers are equal?                    */
/**********************************************************************/
sequal_p(reg1, reg2)
int reg1[2], reg2[2];
 {
  int disp1,disp2;              /* displacements of the two pointers */
  int i;                        /* index & temporary variable */
  int length;                   /* length of a variable length object */
  int page1,page2;              /* page numbers of the two pointers */
  int result = FALSE;           /* result of the comparison */
  int temp_r1[2],temp_r2[2];    /* temporary "registers" for equal_p calls */
  int type;                     /* type of the pointers being compared */

  double get_flo();             /* fetch flonum from Scheme's memory */

  /* Localize page and displacement values */
  page1 = reg1[C_PAGE];
  page2 = reg2[C_PAGE];
  disp1 = reg1[C_DISP];
  disp2 = reg2[C_DISP];

recurse:
  /* Quick test in case the pointers are "eq?" */
  if (disp1 == disp2 && page1 == page2) return(TRUE);

  /* If pointer types are the same, check further */
  if ((type = ptype[(page1 = CORRPAGE(page1)) ]) ==
              ptype[(page2 = CORRPAGE(page2)) ] )
   {
    /* Check to see if shift-break key depressed */
    if (s_break) restart(3);
    /* Check to make sure we haven't recursed too deeply */
    if (stkspc() < 64)
     {
      printf("[VM ERROR encountered!] Stack overflow in EQUAL?\n%s%s",
             "Expression lists circular or too complex\n",
             "Attempting to execute SCHEME-RESET\n[Returning to top level]");
      force_reset();
      /* note:  control does not return here */
     }
    /* If okay, check the objects */
    switch (type>>1)
     {
      case LISTTYPE:    /* test if one pointer is nil */
                        if ((!page1 && page2) || (!page2 && page1))
                          return (FALSE);
                        ldreg(temp_r1, page1, disp1);
                        ldreg(temp_r2, page2, disp2);
                        /* test "equal?-ness" of "cars" */
                        if (sequal_p(temp_r1, temp_r2))
                         {
                          i = page1;
                          page1 = get_byte(i, disp1+3);
                          disp1 = get_word(i, disp1+4);
                          i = page2;
                          page2 = get_byte(i, disp2+3);
                          disp2 = get_word(i, disp2+4);
                          /* test "equal?-ness" of "cdrs" */
                          goto recurse;
                         }
                        break;

      case FLOTYPE:    result = (get_flo(page1, disp1) ==
                                 get_flo(page2, disp2));
                       break;
      case BIGTYPE:
      case STRTYPE:    /* Compare the objects */
                       result = mcmpstr(reg1, reg2);
                       break;

      case ARYTYPE:     /* test each entry of the arrays for equality */
                        if ((length = get_word(page1, disp1 + 1)) ==
                                      get_word(page2, disp2 + 1) )
                         {
                          for (i = PTRSIZE; i < length; i += PTRSIZE)
                           {
                            disp1 += PTRSIZE;
                            ldreg(temp_r1, page1, disp1);
                            disp2 += PTRSIZE;
                            ldreg(temp_r2, page2, disp2);
                            /* test "equal?-ness" of vector elements */
                            if (!(result = sequal_p(temp_r1, temp_r2))) break;
                           }
                         }
                        break;

      case FIXTYPE:
      case CHARTYPE:
      case SYMTYPE:
      case CONTTYPE:
      case CLOSTYPE:
      case CODETYPE:
      case PORTTYPE:
      case ENVTYPE:     /* For these types, assume that "eq?-ness" is o.k. */
                        break;

      case REFTYPE:
      case FREETYPE:
      default:  printf("[VM INTERNAL ERROR] EQUAL?: unexpected type=%d\n",type);
                getch();
     } /* end:  switch (type>>1) */
   }
  return(result);
 }

/************************************************************************/
/*                              String->Symbol                          */
/************************************************************************/
str_2_sym(reg)
int reg[2];
 {
  int page, disp, len;
  char *string;         /* pointer to character string */
  ENTER(str_2_sym);

  page = CORRPAGE(reg[C_PAGE]);
  disp = reg[C_DISP];
  if (ptype[page] != STRTYPE*2)
   {
    set_src_err("STRING->SYMBOL", 1, reg);
    return(-1);
   }
  else
   {
    len = get_word(page, disp+1);
    if (len < 0)
      len = len + BLK_OVHD + PTRSIZE;      /* adjust for small string */
    len -= BLK_OVHD;
    if (!(string = getmem(len+1))) getmem_error(rtn_name);
    get_str(string, page, disp);
    string[len] = '\0';
    intern(reg, string, len);
    rlsstr(string);
   }
  return(0);
 } /* end of function:  str_2_sym(reg) */

/************************************************************************/
/*                      String->Uninterned-symbol                       */
/************************************************************************/
str_2_usym(reg)
int reg[2];
 {
  int page, disp, len;
  char *string;         /* pointer to character string */
  ENTER(str_2_usym);

  page = CORRPAGE(reg[C_PAGE]);
  disp = reg[C_DISP];
  if (ptype[page] != STRTYPE*2)
   {
    set_src_err("STRING->UNINTERNED-SYMBOL", 1, reg);
    return(-1);
   }
  else
   {
    len = get_word(page, disp+1);
    if (len < 0)
       len = len + BLK_OVHD + PTRSIZE;      /* adjust for small string */
    len -= BLK_OVHD;
    if (!(string = getmem(len+1))) getmem_error(rtn_name);
    get_str(string, page, disp);
    string[len] = '\0';
    alloc_sym(reg, len);
    put_sym(string, CORRPAGE(reg[C_PAGE]), reg[C_DISP],NIL_PAGE*2,NIL_DISP,0);
    rlsstr(string);
   }
  return(0);
 } /* end of function:  str_2_usym(reg) */

/************************************************************************/
/*                              Symbol->String                          */
/************************************************************************/
sym_2_str(reg)
int reg[2];
 {
  int page;
  char *string;         /* pointer to character string */

  char *symbol_name();  /* retrieves symbol's print name */

  ENTER(sym_2_str);

  page = CORRPAGE(reg[C_PAGE]);
  if (ptype[page] != SYMTYPE*2)
   {
    set_src_err("SYMBOL->STRING", 1, reg);
    return(-1);
   }
  else
   {
    string = symbol_name(page, reg[C_DISP]);
    alloc_string(reg, string);
    rlsstr(string);
   }
  return(0);
 } /* end of function:  sym_2_str(reg) */

/************************************************************************/
/*                      Retrieve Symbol Name                            */
/*                                                                      */
/* Purpose:  To fetch the print name of a symbol from Scheme's memory   */
/*              and return it in a Lattice C string.                    */
/************************************************************************/
char *symbol_name(page,disp)
int page,disp;          /* pointer to the string data type */
 {
  char *name = NULL;
  int length;           /* length of symbol + 1 (characters) */
  ENTER(symbol_name);

  if (ptype[page] == SYMTYPE*2)
   {
    length = get_word(page, disp+1) - (BLK_OVHD + PTRSIZE);
    if (!(name = getmem(length))) getmem_error(rtn_name);
    get_sym(name, page, disp);
    name[length-1] = '\0';
   }
  return(name);
 } /* end of function:  char *symbol_name(page,disp) */

/************************************************************************/
/*                      Retrieve String Value                           */
/*                                                                      */
/* Purpose:  To fetch the value of a string from Scheme's memory        */
/*              and return it in a Lattice C string.                    */
/************************************************************************/
char *string_asciz(reg)
int reg[2];             /* register containing the string pointer */
 {
  char *name = NULL;    /* ASCIZ string to be returned to caller */
  int page,disp;        /* page and displacement components of string pointer */
  int length;           /* length of string + 1 (characters) */

  ENTER(string_asciz);

  page = CORRPAGE(reg[C_PAGE]);
  disp = reg[C_DISP];

  if (ptype[page] == STRTYPE*2)
   {
    length = get_word(page, disp+1);
    if (length < 0)
      length = length + BLK_OVHD + PTRSIZE;       /* adjust for small string */
    length = length - BLK_OVHD + 1;
    if (!(name = getmem(length))) getmem_error(rtn_name);
    get_str(name, page, disp);
    name[length-1] = '\0';
   }
  return(name);
 } /* end of function:  char *string_asciz(reg) */

/************************************************************************/
/*                           Release String                             */
/*                                                                      */
/* Purpose:  To release the memory allocated to a Lattice C character   */
/*              string.  If the string is null, the rlsmem is skipped.  */
/************************************************************************/
rlsstr(string)
char *string;
 {
  if (string) /* is the string allocated? */
   {
    if (rlsmem(string, strlen(string)+1)) /* release string's memory */
     {
      rlsmem_error("rlsstr"); /* if rlsmem error, print message */
     }
   }
 } /* end of function:  rlsstr(string) */

/************************************************************************/
/*              Convert Scheme Integer to C Long Integer                */
/*                                                                      */
/* Purpose:  To obtain the value of a Scheme integer (up to 32 bits)    */
/*              for manipulation by the Lattice C support routines.     */
/*                                                                      */
/* Description:  Given a Scheme pointer to an integer value, this       */
/*              routine returns the long integer corresponding to       */
/*              the value of the Scheme integer.                        */
/*                                                                      */
/* Calling Sequence:  stat = int2long(value, ptr)                       */
/*              where value - address of location where the long        */
/*                              integer result is to be stored.         */
/*                    ptr - a Scheme register address containing the    */
/*                              Scheme representation of the integer    */
/*                              value.                                  */
/*                    stat - return code; 0 = no errors, value returned */
/*                              1 = error, integer too large or ptr     */
/*                              was not an integer.                     */
/************************************************************************/
int2long(v, reg)
long *v;                /* pointer to location for long integer result */
int reg[2];             /* Scheme "register" containing the integer object */
 {
  int page;             /* Page field of register */

  page = CORRPAGE(reg[C_PAGE]);
  switch (ptype[page])
   {
    case FIXTYPE*2:  *v = get_fix(page, reg[C_DISP]);
                     return(0);
    case BIGTYPE*2:  return(ldlong(v, reg));
    default:  return(1);    /* Invalid argument type */
   } /* end:  switch (ptype[page]) */
 } /* end of function:  int2long(value, reg) */


/************************************************************************/
/*              Convert C Long Integer to Sheme Integer                 */
/*                                                                      */
/* Purpose:  To convert a Lattice C long integer value to the equivalent*/
/*              Scheme representation.                                  */
/*                                                                      */
/* Description:  Given a long integer value, this routine creates the   */
/*              equivalent Scheme integer object and returns it in the  */
/*              designated register.                                    */
/*                                                                      */
/* Calling Sequence:  long2int(reg, value)                              */
/*              where value - the Lattice C long integer value to be    */
/*                              converted to Scheme representation      */
/*                    reg - a Scheme register address to hold the       */
/*                              result.                                 */
/************************************************************************/
long2int(reg, value)
int reg[2];             /* Scheme register to hold the result */
long value;             /* the Lattice C long integer value to be converted */
 {
  /* determine if value can be represented as a fixnum */
  if (value <= 16383 && value >= -16384)
   {
    reg[C_PAGE] = SPECFIX*2;
    reg[C_DISP] = value & 0x7fff;
   }
  else /* value is a bignum */
   {
    enlarge(reg,value);
   }
 } /* end of function:  long2int(reg, value) */


/************************************************************************/
/*                          Append two lists                            */
/************************************************************************/
sappend(dest,src)
int dest[2];        /* Future result register */
int src[2];         /* List onto which to append */
 {
  int car[2];           /* Temporary car field pointer */
  int saved = FALSE;    /* Whether a list copy has been pushed */

  C_push(src);  C_push(src);
  mov_reg(tm2_reg, dest);  /* save destination operand, in case of error */
  while (dest[C_PAGE] && ptype[CORRPAGE(dest[C_PAGE])]==LISTTYPE*2)
   {
    if (s_break) restart(3);    /* shift-break? if so, start over */
    car[C_PAGE] = dest[C_PAGE];  car[C_DISP] = dest[C_DISP];
    take_car(car);
    cons(src, car, nil_reg);
    if (!saved)
     {
      C_push(src);  saved = TRUE;
     }
     else
     {
      asetcdr(tmp_reg, src);
     }
    tmp_page = src[C_PAGE];  tmp_disp = src[C_DISP];
    take_cdr(dest);
   }
  if (dest[C_PAGE])
   {
    if (saved)  C_pop(src);
    C_pop(src);
    C_pop(src);      /* Restore old SRC */
    set_src_err("APPEND", 2, tm2_reg, src);
    return(-1);
   }
  C_pop(dest);
  if (saved)
   {
    C_pop(tmp_reg);    /* Retrieve 2nd arg to append */
    asetcdr(src, tmp_reg);
   }
  C_pop(src);          /* Restore old SRC */
  return(0);
 }

/************************************************************************/
/*                         Get Current Time                             */
/************************************************************************/
ptime(reg)
int reg[2];
 {
  int i;        /* the usual index variable */
  int time[4];  /* array for result of get_time() call */

  get_time(time);  /* ask MS-DOS what time it is */

  /* cons the hours, minutes, seconds, hundredths into a list */
  tmp_page = SPECFIX*2;         /* set tmp_reg's tag=fixnum */
  reg[C_PAGE] = reg[C_DISP] = 0; /* set reg to nil */
  for (i = 3; i >= 0; i--)
   {
    tmp_disp = time[i];         /* move in immediate (fixnum) */
    cons(reg, tmp_reg, reg);    /* append value to front of list */
   }
 } /* end of function:  ptime(reg) */

/************************************************************************/
/*			Start PCS Engine Timer				*/
/************************************************************************/
cset_tim(value)
int value[2];		/* number to place in timer */
 {
  long int result;	/* temporary for long arith */
  unsigned hi,lo;	/* parts of 32-bit value for timer */
  int pg,ds;		/* page and displacement in register */
  pg=CORRPAGE(value[C_PAGE]); ds=value[C_DISP];
  hi = 0;
  switch (ptype[pg]>>1)
   {
    case BIGTYPE: switch (get_word(pg,ds+1))
		   {
		    case 8:  hi = get_word(pg,ds+6);
		    case 6:  lo = get_word(pg,ds+4);
			     break;
		    default: hi = lo = 65535; break;
		   }
		  break;
    case FIXTYPE: lo=ds; break;
    default:  set_src_err("%START-TIMER", 1, value);
   }
  #ifdef PROMEM
  /* Multiply the number of ticks by 1000 to get the number of */
  /* vm instructions to execute before timing out 	       */	
  result = ((hi * 65536) + lo) * 1000;
  lo = result & 0xffff;
  hi = result / 65536l;
  #endif
  if (!settimer(hi,lo))
   {
    set_error(1, "Timer already running", nil_reg);
    return(-1);
   }
  return(0);
 }

/************************************************************************/
/*		  Stop PCS Engine Timer and Return Value		*/
/************************************************************************/
crst_tim(value)
int value[2];
 {
  long int rsttimer();	   /* RSTTIMER returns a long int value */
  long int result;	   /* and puts it here */
  unsigned hi,lo;	   /* where it is broken into these pieces */
  int pg,ds;		   /* Page, displacement of new number */
  #ifdef PROMEM
  /* divide number of vm instructions remaining by 1000 to get the */
  /* number of engine ticks remaining				   */
  result = rsttimer() / 1000;
  #else
  result = rsttimer();
  #endif
  lo = result & 0xffff;
  hi = result / 65536l;
  if ((!hi) && (lo<16384))  alloc_fixnum(value,lo);
   else
   {
    alloc_block(value, BIGTYPE, (hi? 5 : 3));
    pg = CORRPAGE(value[C_PAGE]);
    ds = value[C_DISP]+3;
    put_byte(pg, ds++, 0);   /* Positive sign */
    put_word(pg, ds++, lo);
    if (hi)
     {
      ds++; put_word(pg, ds, hi);
     }
   }
  return(0);
 }

