/* Included by os.c */

/* UNIX specific stuff */


#include "struct.h"

#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/resource.h>
#include <fcntl.h>

#ifdef HZ
#define TICKS_PER_SEC HZ
#else
#define TICKS_PER_SEC 60
#endif

#ifdef butterfly
#define TIMER_RESOLUTION 100
#else
#define TIMER_RESOLUTION TICKS_PER_SEC
#endif

#ifdef hpux
#define hpux_cachectl /* Remove if using HPUX-7.0 or earlier */
#endif


static long pnum = 0; /* processor number */


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#ifdef butterfly


/* MACH specific stuff for BUTTERFLY version */


#include <mach.h>
#include <sys/cluster.h>
#include <machine/param.h>
#include <errno.h>

#define ceilingN(x,n) (((((long)(x)) + (n) - 1) / (n)) * (n))


static long nb_processors = 1; /* number of processors in system */
static long PID[256]; /* PID of process on a given processor */
static long pid_0 = 0;


static void check( code, msg )
long code;
char *msg;
{ if (code != KERN_SUCCESS) { mach_error( msg, code ); os_quit(); }
}


static long start_real_time;


static void init_os()
{ pid_0 = getpid();
  start_real_time = getrtc();
}


void os_quit()
{ long i;
  if (pnum != 0)
    kill( pid_0, SIGHUP );
  else
    for (i=1; i<nb_processors; i++)
      if (PID[i] != 0) kill( PID[i], SIGHUP );
  exit(0);
}


long os_nb_processors()
{ long i;
  union cluster_status stats; /* cluster statistics */

  /* get cluster statistics (cluster size, etc.) */
  check( cluster_stat(HOME_CLUSTER,GET_NODE_LIST,&stats,&nb_processors), "cluster_stat:" );

  for (i=nb_processors-1; i>0; i--) PID[i] = 0;

  return nb_processors;
}


#define NB_SIMULTANEOUS_PAGE_INS 8
extern end;
static long xx = 0;
static void page_in1( pnum )
long pnum;
{ pstate->ps[pnum]->temp[0] = (pnum >= NB_SIMULTANEOUS_PAGE_INS);
}
static void page_in2( n )
long n;
{ char *ptr;
  PSTATE_PTR p;
  long i,j;
  p = pstate->ps[pnum];
  while (p->temp[0]); /* wait until other processor is finished */
  ptr = (char *)0;
  while (ptr < (char *)&end) { xx += *ptr; ptr += NBPG; }
  for (j=0; j<n; j++)
  { ptr = (char *)p->ps[j]->heap_bot;
    for (i=((char *)p->ps[j]->heap_top)-ptr-1; i>=0; i -= NBPG) ptr[i] = ptr[i];
    ptr = (char *)p->ps[j]->elog_bot;
    for (i=((char *)p->ps[j]->prof_top)-ptr-1; i>=0; i -= NBPG) ptr[i] = ptr[i];
    ptr = (char *)p->ps[j]->stack_bot;
    for (i=((char *)p->ps[j]->q_top)-ptr-1; i>=0; i -= NBPG) ptr[i] = ptr[i];
  }
/*
  ptr = (char *)sstate->const_top;
  while (ptr >= (char *)sstate) { *ptr = *ptr; ptr -= NBPG; }
*/
  if (pnum+NB_SIMULTANEOUS_PAGE_INS < n)
    p->ps[pnum+NB_SIMULTANEOUS_PAGE_INS]->temp[0] = 0; /* signal other processor */
}


long os_fork_on_processors( n )
long n;
{ long i;
  page_in1( 0 );
  for (i=n-1; i>0; i--)
  { page_in1( i );
    check( fork_and_bind( i, HOME_CLUSTER, &PID[i] ), "fork_and_bind:" );
    if (PID[i] == 0) { pnum = i; break; }
  }
  page_in2( n );
  return pnum;
}


void os_shared_malloc8( len, processor, cont )
long len;
long processor;
void (*cont)();
{ vm_address_t block; /* pointer to shared memory block */
  long aligned_len = ceilingN( len, NBPG );
  char *ptr;

  /* get shared memory block on processor 'processor' */
  check( vm_mapmem( task_self(), /* ignored */
                    &block,
                    aligned_len,
                    VM_MAPMEM_ALLOCATE | VM_MAPMEM_ANYWHERE,
                    0, /* ignored */
                    0, /* ignored */
                    processor ),
	"vm_mapmem:" );

  /* mark it shared in child processes */
  check( vm_inherit( task_self(), block, aligned_len, VM_INHERIT_SHARE ), "vm_inherit:" );

  ptr = (char *)ceilingN( block, NBPG );

  cont( ptr );
}


void os_shared_copy_malloc8( len_share, len_copy, processor, cont )
long len_share, len_copy;
long processor;
void (*cont)();
{ vm_address_t block; /* pointer to memory block */
  long aligned_len_share = ceilingN( len_share, NBPG );
  long aligned_len_copy = ceilingN( len_copy, NBPG );
  long aligned_len = aligned_len_share + aligned_len_copy;

  /* get shared memory block on processor 'processor' */
  check( vm_mapmem( task_self(), /* ignored */
                    &block,
                    aligned_len,
                    VM_MAPMEM_ALLOCATE | VM_MAPMEM_ANYWHERE,
                    0, /* ignored */
                    0, /* ignored */
                    processor ),
	"vm_mapmem:" );

  { char *ptr1 = (char *)ceilingN( block, NBPG );
    char *ptr2 = ptr1 + aligned_len_share;
    char *ptr3 = ptr2 - len_share;

    /* mark first part of block 'share' in child processes */
    check( vm_inherit( task_self(), (vm_address_t)ptr1, aligned_len_share, VM_INHERIT_SHARE ), "vm_inherit:" );

    /* mark second part of block 'copy' in child processes */
    check( vm_inherit( task_self(), (vm_address_t)ptr2, aligned_len_copy, VM_INHERIT_COPY ), "vm_inherit:" );

  cont( ptr3 );
  }
}


void os_block_copy( src, dst, len )
char *src, *dst;
long len;
{ btransfer( src, dst, len );
}


long os_clock()
{ return (getrtc() - start_real_time);
}


long os_real_time_clock()
{ return getrtc();
}


long os_clock_to_msec( ticks )
long ticks;
{ return ticks / 16;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#else


/* plain UNIX stuff */


static long start_real_time;


static void init_os()
{ struct timeval tv;
  struct timezone tz;

  gettimeofday( &tv, &tz );

  start_real_time = tv.tv_sec;
}


void os_quit()
{ exit(0);
}


long os_nb_processors()
{ return 1;
}


long os_fork_on_processors()
{ return 0;
}


static char *malloc8( length ) /* alloc of blocks starting at an octuple adr */
long length;
{ char *ptr;
  ptr = (char *)malloc( length+7 );
  if (ptr != NULL) ptr = (char *) ceiling8(ptr); /* get octuple addr. */
  return ptr;
}


void os_shared_malloc8( len, processor, cont )
long len;
long processor;
void (*cont)();
{ cont( (char *)malloc8( len ) );
}


void os_shared_copy_malloc8( len_share, len_copy, processor, cont )
long len_share, len_copy;
long processor;
void (*cont)();
{ long aligned_len_share = ceiling8( len_share );
  long len = aligned_len_share + len_copy;

  char *ptr = malloc8( len );
  if (ptr != NULL) ptr = ptr + aligned_len_share - len_share;

  cont( ptr );
}


void os_block_copy( src, dst, len )
char *src, *dst;
long len;
{ char *p1 = src, *p2 = dst;
  long i = len;
  while (i > 0) { *(p2++) = *(p1++); i--; }
}


long os_clock()
{ struct timeval tv;
  struct timezone tz;
  gettimeofday( &tv, &tz );
  return (tv.tv_sec - start_real_time) * 100000 + tv.tv_usec / 10;
}


long os_real_time_clock()
{ struct timeval tv;
  struct timezone tz;
  gettimeofday( &tv, &tz );
  return (tv.tv_sec - start_real_time) * 100000 + tv.tv_usec / 10;
}


long os_clock_to_msec( ticks )
long ticks;
{ return ticks / 100;
}


#endif


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/* Common UNIX stuff */


#define MAX_NB_FDS 32

static long fds_set[MAX_NB_FDS>>5];
static struct timeval poll_time, block_time;
struct itimerval prof_tv, virtual_tv;


static void os_notify_gc_begin_internal()
{
}


static void os_notify_gc_end_internal()
{
}


static void (*intr_action)();
static void (*timer_action)();
static void (*io_action)();
static void (*fatal_action)();


static void signal_HUP( sig, code, scp )
long sig, code;
struct sigcontext *scp;
{ signal(SIGHUP, SIG_IGN);
  if (pnum == 0) os_quit();
  exit(0);
}


static void signal_INT( sig, code, scp )
long sig, code;
struct sigcontext *scp;
{ long pc = 0, sp = 0;
  if (scp != NULL) { pc = scp->sc_pc; sp = scp->sc_sp; }
  signal(SIGINT, signal_INT);
  intr_action( pc, sp, "SIGINT" );
  return;
}


static void signal_IO( sig, code, scp )
long sig, code;
struct sigcontext *scp;
{ long pc = 0, sp = 0;
  if (scp != NULL) { pc = scp->sc_pc; sp = scp->sc_sp; }
  signal(SIGIO, signal_IO);
  io_action( pc, sp, "SIGIO" );
}


static short *prof_buff;
static long prof_bufsiz = 0;
static long prof_offset = 0;
static long prof_shift = 1;


static void signal_PROF( sig, code, scp )
long sig, code;
struct sigcontext *scp;
{ signal(SIGPROF,signal_PROF);
  setitimer( ITIMER_PROF, &prof_tv, 0 );
  if (scp != NULL)
  { long pc = scp->sc_pc;
    long index = (pc-prof_offset)>>prof_shift;
    if ((index >= 0) && (index < prof_bufsiz)) prof_buff[index]++;
  }
}


static void signal_VTALRM( sig, code, scp )
long sig, code;
struct sigcontext *scp;
{ signal(SIGVTALRM,signal_VTALRM);
  setitimer( ITIMER_VIRTUAL, &virtual_tv, 0 );
  timer_action( 0, 0, 1 );
}


static void quit_action( pc, sp, kind )
long pc, sp;
char *kind;
{ os_warn( "Fatal signal %s, terminating...\n", (long)kind );
  os_quit();
}


static void fatal_signal( sig, code, scp )
long sig, code;
struct sigcontext *scp;
{ long pc = 0, sp = 0;
  char *kind;
  void (*action)();
  if (scp != NULL) { pc = scp->sc_pc; sp = scp->sc_sp; }
  switch (sig)
  { case SIGILL:  kind = "SIGILL";  break;
    case SIGEMT:  kind = "SIGEMT";  break;
    case SIGFPE:  kind = "SIGFPE";  break;
    case SIGBUS:  kind = "SIGBUS";  break;
    case SIGSEGV: kind = "SIGSEGV"; break;
    case SIGTERM: kind = "SIGTERM"; break;
    default:      kind = "???";     break;
  }
  action = fatal_action;
  fatal_action = quit_action;
  signal( sig, fatal_signal );
  action( pc, sp, kind );
}


void os_install_trap_handlers( intr_proc, timer_proc, io_proc, fatal_proc )
void (*intr_proc)();
void (*timer_proc)();
void (*io_proc)();
void (*fatal_proc)();
{ intr_action = intr_proc;
  timer_action = timer_proc;
  io_action = io_proc;
  signal(SIGHUP, signal_HUP);
  signal(SIGINT, signal_INT);
  signal(SIGFPE, SIG_IGN);
  signal(SIGCLD, SIG_IGN);
  if (fatal_proc != (void (*)())0)
  { fatal_action = fatal_proc;
    signal(SIGILL, fatal_signal);
    signal(SIGIOT, fatal_signal);
    signal(SIGEMT, fatal_signal);
    signal(SIGBUS, fatal_signal);
    signal(SIGSEGV,fatal_signal);
    signal(SIGTERM,fatal_signal);
  }
  else
    fatal_action = quit_action;

  prof_tv.it_interval.tv_sec  = 0;
  prof_tv.it_interval.tv_usec = 0;
  prof_tv.it_value.tv_sec     = 0;
  prof_tv.it_value.tv_usec    = 0;
  signal(SIGPROF,signal_PROF);
  setitimer( ITIMER_PROF, &prof_tv, 0 );

  virtual_tv.it_interval.tv_sec  = 0;
  virtual_tv.it_interval.tv_usec = 0;
  virtual_tv.it_value.tv_sec     = 0;
  virtual_tv.it_value.tv_usec    = 0;
  signal(SIGVTALRM,signal_VTALRM);
  setitimer( ITIMER_VIRTUAL, &virtual_tv, 0 );
}


long os_get_next_event( event )
char *event;
{ return 0;
}


long os_handle_event( event )
char *event;
{ return 0;
}


#ifdef hpux_cachectl
#include <sys/cache.h>
#endif


void os_flush_caches()
{
#ifdef NeXT
  asm("trap #2");
#else
#ifdef hpux_cachectl
  cachectl( CC_IPURGE, NULL, NULL );
#else

#define INSTR_CACHE_LENGTH_IN_K 64
#define DATA_CACHE_LENGTH_IN_K  8

  short *start = (short *)pstate->heap_old;
  short *p = start;
  long i;

  for (i=((long)INSTR_CACHE_LENGTH_IN_K)*K/2-1; i>0; i--) *p++ = NOP_OP;
  *p++ = RTS_OP;
  for (i=((long)DATA_CACHE_LENGTH_IN_K)*K/2; i>0; i--) *p++ = 0;

  ((void (*)())start)();

#endif
#endif
}


void os_flush_writes()
{
#ifdef NeXT
  asm("trap #2");
#else
#ifdef hpux_cachectl
  cachectl( CC_IPURGE, NULL, NULL );
#endif
#endif
}


char *os_expand_filename( str )
char *str;
{ long len;
  if (str == NULL) return NULL;
  len = string_length( str );
  if ((len>1) && (str[0]=='~') && (str[1]=='/'))
  { char *home = (char *)getenv( "HOME" );
    if (home == NULL) return NULL;
    return string_append( home, &str[1] );
  }
  else if ((len==0) || (str[0]!='/'))
  { char buf[MAXPATHLEN], *cwd;
#ifdef hpux
    cwd = (char *)getcwd( buf, sizeof(buf) );
#else
    cwd = (char *)getwd( buf );
#endif
    if (cwd == NULL) return NULL;
    return string_append( cwd, string_append( "/", str ) );
  }
  else
    return str;
}


static long file_open( name, flags )
char *name;
long flags;
{ long f;
  f = open( name, flags, 0666 );
  if (f != -1)
    if ((f < 0) || (f >= MAX_NB_FDS))
    { close( f ); f = -1; }
  return f;
}


OS_FILE os_file_open_input( name )
char *name;
{ return (OS_FILE)file_open( name, (long)(O_RDONLY) );
}


OS_FILE os_file_open_output( name )
char *name;
{ return (OS_FILE)file_open( name, (long)(O_WRONLY|O_CREAT|O_TRUNC) );
}


OS_FILE os_file_open_input_output( name )
char *name;
{ return (OS_FILE)file_open( name, (long)(O_RDWR|O_CREAT|O_TRUNC) );
}


long os_file_length( f )
OS_FILE f;
{ struct stat buf;
  if (fstat( (long)f, &buf ) != 0) return -1;
  return (long)buf.st_size;
}


long os_file_read_ready( f )
OS_FILE f;
{ long result;
  long f_index = ((long)f) >> 5, f_mask = (1 << (((long)f) & 31));
  fds_set[f_index] |= f_mask;
  select( MAX_NB_FDS, fds_set, 0, 0, &poll_time );
  result = (fds_set[f_index] & f_mask);
  fds_set[f_index] = 0;
  return result;
}


long os_file_write_ready( f )
OS_FILE f;
{ long result;
  long f_index = ((long)f) >> 5, f_mask = (1 << (((long)f) & 31));
  fds_set[f_index] |= f_mask;
  select( MAX_NB_FDS, 0, fds_set, 0, &poll_time );
  result = (fds_set[f_index] & f_mask);
  fds_set[f_index] = 0;
  return result;
}


void os_file_block_read( f )
OS_FILE f;
{ long f_index = f >> 5, f_mask = (1 << (f & 31));
  fds_set[f_index] |= f_mask;
  select( MAX_NB_FDS, fds_set, 0, 0, &block_time );
  fds_set[f_index] = 0;
}


void os_file_block_write( f )
OS_FILE f;
{ long f_index = f >> 5, f_mask = (1 << (f & 31));
  fds_set[f_index] |= f_mask;
  select( MAX_NB_FDS, 0, fds_set, 0, &block_time );
  fds_set[f_index] = 0;
}


long os_file_read( f, ptr, n )
OS_FILE f;
char *ptr;
long n;
{ if (os_file_read_ready( f ))
    return read( (long)f, ptr, n );
  else
    return -2;
}


long os_file_write( f, ptr, n )
OS_FILE f;
char *ptr;
long n;
{ if (os_file_write_ready( f ))
    return write( (long)f, ptr, n );
  else
    return -2;
}


long os_file_close( f )
OS_FILE f;
{ return close( (long)f );
}


extern void os_set_timer_interval( interval )
long interval;
{ virtual_tv.it_value.tv_sec  = interval/1000;
  virtual_tv.it_value.tv_usec = (interval%1000)*1000;
  setitimer( ITIMER_VIRTUAL, &virtual_tv, 0 );
}


void os_profil( buff, bufsiz, offset, shift )
short *buff;
long bufsiz, offset;
long shift;
{ if (shift == 0)
  { prof_tv.it_value.tv_sec  = 0;
    prof_tv.it_value.tv_usec = 0;
  }
  else
  { prof_tv.it_value.tv_sec  = 0;
    prof_tv.it_value.tv_usec = 1000000/TIMER_RESOLUTION;
  }
  prof_buff   = buff;
  prof_bufsiz = bufsiz;
  prof_offset = offset;
  prof_shift  = shift;
  setitimer( ITIMER_PROF, &prof_tv, 0 );
}


long os_ticks_to_msec( ticks )
long ticks;
{ return ticks * 1000 / TICKS_PER_SEC;
}


void os_cpu_times( buf )
long *buf;
{ struct tms buffer;
  times(&buffer);
  buf[0] = buffer.tms_utime * 1000 / TICKS_PER_SEC;
  buf[1] = buffer.tms_stime * 1000 / TICKS_PER_SEC;
}
/*
{ struct rusage ru;
  getrusage( RUSAGE_SELF, &ru );
  buf[0] = ru.ru_utime.tv_sec * 1000 + ru.ru_utime.tv_usec / 1000;
  buf[1] = ru.ru_stime.tv_sec * 1000 + ru.ru_stime.tv_usec / 1000;
}
*/


extern void main_gambit();


static int main_internal( argc, argv, envp )
int argc;
char *argv[], *envp[];
{ long i = 0;
  short *p = (short *)(((char *)&i)+1);

  signal(SIGILL, SIG_IGN);  /* a M68020/30 can write words at odd addresses */
  *p = -1;
  signal(SIGILL, SIG_DFL);
  os_M68020 = (i != 0);
#ifdef NO_EMUL_M68881
  os_M68881 = os_M68020;
#else
  os_M68881 = 0;
#endif

  for (i=(MAX_NB_FDS>>5)-1; i>=0; i--) fds_set[i] = 0;

  poll_time.tv_sec = 0;
  poll_time.tv_usec = 0;

  block_time.tv_sec = 1000000;
  block_time.tv_usec = 0;

  os_stdin  = 0;
  os_stdout = 1;
  os_stderr = 2;

  init_os();

  main_gambit( argc, argv, envp );

  return 0;
}


/*---------------------------------------------------------------------------*/
