#!/usr/local/bin/perl -w
#
# simpram
#
# release 1 version 0
# (based on PRAMOS release 3 version 1 and HOST release 1 version 0)
#
# (C) 1997 by Jochen Rhrig
#

$SIMPRAM_VERSION      = "1v0";
$SIMPRAM_VERSION_LONG = "simpram $SIMPRAM_VERSION (C) 1997 by bird";
$PRAMOS_VERSION       = "3v1";
$HOST_VERSION         = "1v0";

$PRAM_HOME    = "/home/pram/";
$PRAM_BIN_DIR = $PRAM_HOME."bin/";
$PRAM_ETC_DIR = $PRAM_HOME."etc/";
$PRAM_LIB_DIR = $PRAM_HOME."lib/";
$GTODUS_PROG  = $PRAM_LIB_DIR."/simpram/gettimeofday_usec";
                  # this program splits up the current value of gettimeofday()
                  # into two 32bit values that represent a 64bit microsecond
                  # value

$WRITE_STDIN_PROG = $PRAM_LIB_DIR."/simpram/write_stdin";
$ENV{WRITE_STDIN_PROG} = $WRITE_STDIN_PROG; # writes to the stdin-buffer of
                                            # the connected terminal

$TMPDIR = ($ENV{'TMPDIR'} ? $ENV{'TMPDIR'}
          : $ENV{'TMP'} ? $ENV{'TMP'}
          : $ENV{'TEMPDIR'} ? $ENV{'TEMPDIR'}
          : "/tmp")."/";

#$log_file = "$libdir/pramsim.log";

#print "start: $user $HOST $$ ">> $log_file;
#date >> $log_file;

#
# PRAM Simulator
# 

$PRAMSIM = $PRAM_BIN_DIR."pramsim";
$TIME    = "/usr/5bin/time";

$PRAMSIM_OPT = "";

#
# Grenzwerte
#

$MAX_PHYSNUM = 128;
$MAX_VIRTNUM = 31; # maximal stehen dem Benutzer 31 von 32 vPen zur Verfgung
                   # da ein vP pro pP als Serviceprozessor reserviert ist

#
# Default-Werte
#

$lddata = 11;           # Size of .lddata-segment
$sheap  = 8000000;      # shared Heap
$pstack = 8192;         # lokaler Stack
$pheap  = 2048;         # lokaler Heap
$pargs  = 1000;         # Dummer default.

$PHYSNUM   = 3;	     # Zahl der pPen
$VIRTNUM_USER   = 1; # Zahl der vPen pro pP (ohne Serviceprozessor)

$TRACE_OUTPUT     = "";

$STRACE = "";

# Pfade Simulator und Hostprogramm

$PRAMOS_FILE     = $PRAM_LIB_DIR."pramos/".$PRAMOS_VERSION."/pramos_pram";
$PRAMOS_COD_FILE = $PRAMOS_FILE.".cod";

$PRAMOS_KSHBRK_SIZE = 1*1024*1024; # Size of PRAMOS-Kernel Break
                                   # (for Kernel mpadd-memory-allocator)
$PRAMOS_KSHBRK_SIZE_LOCATION = 0xe; # adress in global PRAM memory where the 
				    # size of the global kernel kshbrk is      
				    # stored for PRAMOS


#$GLOBAL_MEMSIZE        = 0x1200000; this is calculated now
$GLOBAL_MEMSIZE_LOCATION = 0xd; # adress in global PRAM memory where the
                                # size of the global PRAM memory is
                                # stored for PRAMOS                      
$LOCAL_MEMSIZE   = 0x80000;
$PROGRAM_MEMSIZE = 0x10000;

$PRAMOS_GETTIMEOFDAY_HI_LOCATION = 17; # adress in global PRAM memory where the
$PRAMOS_GETTIMEOFDAY_LO_LOCATION = 18; # 64 bit microsecond offset for the
                                       # calculation of the gettimeofday()
                                       # value is stored for PRAMOS

$PRAM_DEVICE = $TMPDIR."simpram_".$$."_glmem";
$PROG_DEVICE = $TMPDIR."simpram_".$$."_prgmem";
$INTS_DEVICE = $TMPDIR."simpram_".$$."_extint";


#
# Usage- and Help-Functions
#

sub usage
{
    print "$_[0]\7\n";
    print "help with $0 -help\n";
    exit 1;
}

sub help
{
    printf("usage: $0 [options] coff-file args-for-pram-program\n");
    printf("  -sheap    %10-d\t\t".
	   "shared heap used by shmalloc()\n", $sheap);
    printf("  -pstack   %10-d\t\t".
	   "private stack\n", $pstack);
    printf("  -pheap    %10-d\t\t".
	   "private heap used by malloc()\n", $pheap);
    printf("  -physnum  %10-d\t\t".
           "number of physical processors (pPs) (1..$MAX_PHYSNUM)\n", $PHYSNUM);
    printf("  -virtnum  %10-d\t\t".
	   "number of virtual processors per pP (1..$MAX_VIRTNUM) \n".
	   "                      \t\t(without serviceprocessor)\n",
	   $VIRTNUM_USER);
    printf("  -procs    %3-d %2-d    \t\t".
	   "-physnum and -virtnum combined\n", $PHYSNUM, $VIRTNUM_USER);
    printf("  -pramsim  <pramsim-file>\t".
	   "path of PRAM-simulator\n".
	   "\t\t\t\t(set to $PRAMSIM)\n");
    printf("  -pramosfile %3-s\t\t".
	   "path of PRAMOS-File\n".
	   "\t\t\t\t(set to $PRAMOS_FILE)\n");
    printf("  -simopt  \"%-10s\"\t".
	   "simulator options (MUST BE QUOTED!)\n",
	   $PRAMSIM_OPT ? "$PRAMSIM_OPT" : "<pramsim options>");
    printf("  -trace   <filename prefix>\t".
	   "output trace-files %s\n",
	   $TRACE_OUTPUT ? "to $TRACE_OUTPUT.*" : "(not specified)");
    printf("  -tmpdir  <filename>\t\t".
	   "directory for temporary files\n\t\t\t\t(set to $TMPDIR)\n");
    printf("  -renice  <niceinc>\t\t".
	   "increment nice level of simulation by <niceinc>\n".
	   "\t\t\t\t(niceinc %s)\n",
	   defined($nicelevel)?"set to $nicelevel":"not set");
    printf("  -str           \t\t".
	   "strace simulator (switched %s)\n", $STRACE ? "on" : "off");
    printf("  -verbose\t\t\tshow some additional output\n");
    printf("  -version\t\t\t$SIMPRAM_VERSION_LONG\n");
    printf("  -help\t\t\t\tshow this message\n");
    printf("The shown values are default values possibly changed by your options\n");
    exit 0;
}

#
# Signal Handler for proper Cleanup
#

sub cleanup
{
    kill 9, $xhostpid; # kill xterm for host on exit

    system "rm -f $PRAM_DEVICE";   # remove "device"-files
    system "rm -f $PROG_DEVICE";
    system "rm -f $INTS_DEVICE";
    system "rm -f $HOST_LOADERRC"; # remove generated host loaderrc file
    system "rm -f $OBJFILE_TMP";
}

$SIG{'INT'} = 'cleanup';

#
# Process command-line arguments
#

$all_args_processed = 0;
PROCESS_ARGS:
while($#ARGV >= 0 && !$all_args_processed)
{
  if($ARGV[0] eq "-sheap")
    {shift; $sheap = shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-pstack")
    {shift; $pstack = shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-pheap")
    {shift; $pheap = shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-physnum")
    {shift; $PHYSNUM = shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-virtnum")
    {shift; $VIRTNUM_USER = shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-procs")
    {shift; $PHYSNUM = shift; $VIRTNUM_USER = shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-pramsim")
    {shift; $PRAMSIM = shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-pramosfile")
    {shift; $PRAMOS_FILE = shift; $PRAMOS_COD_FILE = $PRAMOS_FILE.".cod";
     next PROCESS_ARGS;}
  if($ARGV[0] eq "-trace")
    {shift; $TRACE_OUTPUT=shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-str")
    {shift; $STRACE="truss"; next PROCESS_ARGS;}
  if($ARGV[0] eq "-simopt")
    {shift; $PRAMSIM_OPT=" ".shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-tmpdir")
    {shift; $TMPDIR = shift; next PROCESS_ARGS;}
  if($ARGV[0] eq "-renice")
    {
	shift; $nicelevel = shift;
	$run="/usr/ucb/renice +$nicelevel $$ 2>1 1> /dev/null";
	if($VERBOSE){print "$$ $run"};
	system $run;
	next PROCESS_ARGS;
    }
  if($ARGV[0] eq "-verbose")
    {shift; $VERBOSE=1; next PROCESS_ARGS;}
  if($ARGV[0] eq "-version")
    {print "$SIMPRAM_VERSION_LONG\n"; exit 0;}
  if($ARGV[0] eq "-help")
    {&help;}
  if($ARGV[0] =~ /-.*/)
    {&usage("Unknown option $ARGV[0]");shift;}
  $all_args_processed = 1;
}

if($#ARGV < 0)
  {&usage("You could at least specify a coff-file");}

if($PHYSNUM > $MAX_PHYSNUM || $PHYSNUM < 1)
{
    &usage("physnum out of range (1..$MAX_PHYSNUM)");
}

if($VIRTNUM_USER > $MAX_VIRTNUM || $VIRTNUM_USER < 1)
{
    &usage("virtnum out of range (1..$MAX_VIRTNUM)");
}

$VIRTNUM_TOTAL = $VIRTNUM_USER+1; # total number of vP per pP - we need one
                                  # more than the user (for the PRAMOS service-
                                  # processor)

$OBJFILE=shift;

if(!-e $OBJFILE){ die "File $OBJFILE doesn't exist\7\n"};

$OBJFILE_TMP = "$TMPDIR/simpram_".$$."_obj";

if($OBJFILE =~ /.*\.gz/)
{
    if($VERBOSE){printf("unzipping $OBJFILE\n")};
    system "gunzip --to-stdout $OBJFILE >$OBJFILE_TMP";
    $OBJFILE=$OBJFILE_TMP;
}

if($VERBOSE){print "Using $TMPDIR as \"device\"-file directory\n"};

#
# Calculate the size of the total amount of global (shared) memory
#


# Determine data segment sizes of the program that is to be simulated

open(OBJDUMP, "objdump --header $OBJFILE|");

SECTION_SIZES:
while(<OBJDUMP>)
{
    if(/\.gpbss/)
    {
	($d1, $d2, $pbss) = split;
	next SECTION_SIZES;
    }
    if(/\.gpdata/)
    {
	($d1, $d2, $pdata) = split;
	next SECTION_SIZES;
    }
    if(/\.gsbss/)
    {
	($d1, $d2, $sbss) = split;
	next SECTION_SIZES;
    }
    if(/\.gsdata/)
    {
	($d1, $d2, $sdata) = split;
	next SECTION_SIZES;
    }
}
close(OBJDUMP);

$pbss  = (hex $pbss )/4;
$pdata = (hex $pdata)/4;
$sbss  = (hex $sbss )/4;
$sdata = (hex $sdata)/4;

if($VERBOSE){printf "gpbss length  = %d word\n", $pbss };
if($VERBOSE){printf "gpdata length = %d word\n", $pdata};
if($VERBOSE){printf "gsbss length  = %d word\n", $sbss };
if($VERBOSE){printf "gsdata length = %d word\n", $sdata};

$private = $pheap + $pstack + $pdata + $pbss + $pargs;
$shared  = $sdata + $sbss + $sheap + $lddata;

$procnum = $PHYSNUM * $VIRTNUM_USER;

$GLOBAL_MEMSIZE = $private * $procnum + $shared;

if($VERBOSE){print "section private shared\n"};
if($VERBOSE){print "lddata	$lddata\n"};
if($VERBOSE){print "stack	$pstack\n"};
if($VERBOSE){print "heap	$pheap	$sheap\n"};
if($VERBOSE){print "data	$pdata	$sdata\n"};
if($VERBOSE){print "bss	$pbss	$sbss\n"};
if($VERBOSE){print "args	$pargs\n"};
if($VERBOSE){print "       ----------------\n"};
if($VERBOSE){print "$procnum *	$private +	"
		  ."$shared = $GLOBAL_MEMSIZE Words\n"};

if($procnum == 1)
{
    # If we run the program on one vP an additional amount of memory is needed
    # to ensure correct copying of the private segments at program startup

    $additional = $pdata + $pbss + $pargs;
    $GLOBAL_MEMSIZE += $additional;

    if($VERBOSE)
    {
	print "Using only one processor. Additional $additional bytes.\n";
    }

    print "\n";
}

#
# Determine data segment sizes of PRAMOS
#

open(OBJDUMP, "objdump --header $PRAMOS_FILE|");

SECTION_SIZES:
while(<OBJDUMP>)
{
    if(/\.gpbss/)
    {
	($d1, $d2, $pramos_pbss) = split;
	next SECTION_SIZES;
    }
    if(/\.gpdata/)
    {
	($d1, $d2, $pramos_pdata) = split;
	next SECTION_SIZES;
    }
    if(/\.gsbss/)
    {
	($d1, $d2, $pramos_sbss) = split;
	next SECTION_SIZES;
    }
    if(/\.gsdata/)
    {
	($d1, $d2, $pramos_sdata) = split;
	next SECTION_SIZES;
    }
    if(/\.pbsdat/)
    {
	($d1, $d2, $pramos_pbsdat) = split;
	next SECTION_SIZES;
    }
}

close(OBJDUMP);

$pramos_pbss   = (hex $pramos_pbss )/4; # THIS SHOULD ALWAYS BE 0
$pramos_pdata  = (hex $pramos_pdata)/4; # THIS SHOULD ALWAYS BE 0
$pramos_sbss   = (hex $pramos_sbss )/4;
$pramos_sdata  = (hex $pramos_sdata)/4;
$pramos_pbsdat = (hex $pramos_pbsdat)/4;

if($VERBOSE){printf "PRAMOS-gpbss length  = %d\n", $pramos_pbss};
if($VERBOSE){printf "PRAMOS-gpdata length = %d\n", $pramos_pdata};
if($VERBOSE){printf "PRAMOS-gsbss length  = %d\n", $pramos_sbss};
if($VERBOSE){printf "PRAMOS-gsdata length = %d\n", $pramos_sdata};
if($VERBOSE){printf "PRAMOS-pbsdat length = %d\n", $pramos_pbsdat};

$GLOBAL_MEMSIZE += $pramos_pbss + $pramos_pdata
                   + $pramos_sbss + $pramos_sdata
                   + $pramos_pbsdat
                   + $PRAMOS_KSHBRK_SIZE;

# Round the size of the global memory to a 64K border

if($GLOBAL_MEMSIZE & 0xffff != 0)
{
    $GLOBAL_MEMSIZE = ($GLOBAL_MEMSIZE & 0xffff0000) + 0x10000;
}

#
# Start up Host-Program
#

if($VERBOSE){printf("Generatig loaderc-file ...\n")};

$HOST_LOADERRC = "$TMPDIR/simpram_".$$."_ldrc";

if(-e $HOST_LOADERRC)
{
    die "File $HOST_LOADERRC exists - please remove or rename it\7\n";
}


open(HLDR, ">$HOST_LOADERRC") || die "Couldn't create file $HOST_LOADERRC"; 

printf HLDR "sctdata          \n"; 
printf HLDR "{                \n"; 
printf HLDR "  org *          \n"; 
printf HLDR "  .lddata     S  \n"; 
printf HLDR "  .gsdata     S  \n"; 
printf HLDR "  .gsbss      S  \n"; 
printf HLDR "  org 0xc0000000 \n"; 
printf HLDR "  .gpdata     P  \n"; 
printf HLDR "  .gpbss      P  \n"; 
printf HLDR "  .args       P  \n"; 
printf HLDR "}                \n"; 
printf HLDR "lddata           \n"; 
printf HLDR "{                \n"; 
printf HLDR "  procs    %8d N \n", $VIRTNUM_USER*$PHYSNUM; 
printf HLDR "  textlen  %8d T \n", 0; 
printf HLDR "  pstack   %8d P \n", $pstack; 
printf HLDR "  pheap    %8d P \n", $pheap; 
printf HLDR "  sheap    %8d S \n", $sheap; 
printf HLDR "}                     \n"; 

close HLDR;

print "Starting up Host ...\n";

$HOST_ETC_PATH    = $PRAM_ETC_DIR."/host/$HOST_VERSION/";
$HOST_MODULE_PATH = $HOST_ETC_PATH;
$HOST             = $HOST_MODULE_PATH."pramos_host";
$START_HOST       = $PRAM_LIB_DIR."simpram/".$SIMPRAM_VERSION."/simpram_host";

$ENV{'HOST'} = $HOST;
$ENV{'HOST_LOADERRC'}  = $HOST_LOADERRC;
$ENV{'GLOBAL_MEMSIZE'} = $GLOBAL_MEMSIZE;

$ENV{'PRAM_DEVICE'} = $PRAM_DEVICE;
$ENV{'PROG_DEVICE'} = $PROG_DEVICE;
$ENV{'INTS_DEVICE'} = $INTS_DEVICE;

$ENV{'PROGRAM'} = $OBJFILE;

$ENV{'SBP_HOSTMODULEPATH'} = $HOST_MODULE_PATH;
$ENV{'SBP_HOSTRC'}         = $HOST_ETC_PATH."hostrc";
$ENV{'SBP_HOSTMODULEFILE'} = $HOST_ETC_PATH."host_modules";
$ENV{'SBP_LOADER'}         = "host-loader";
$ENV{'SBP_PRAMOS_ISSUE'}   = $HOST_ETC_PATH."issue";
$ENV{'SBP_PRAMOS_MOTD'}    = $HOST_ETC_PATH."motd";
$ENV{'SBP_HOSTRC'}         = ".hostrc.dummy";

unless($xhostpid = fork)
{
    $title = $OBJFILE."-Host";
    exec "xterm -title $title -name simpram_host -e $START_HOST";
}

#
# Start up Simulator
#

print "Starting up Simulator ...\n";
printf "%s", "]2;".$OBJFILE."-Simulator";# This sets the title in the xterm

($GTODUS_HI, $GTODUS_LO) =  split(/ /, `$GTODUS_PROG`);

if($VERBOSE){print("gettimofday_offset_hi = $GTODUS_HI\n".
		   "gettimofday_offset_lo = $GTODUS_LO\n");};

$run = "$TIME $STRACE $PRAMSIM    ".
       "--no-rc-file               ".
       "--net-datatest       	   ".
       "--net-op-test	       	   ".
       "-M $GLOBAL_MEMSIZE   	   ".
       "-P $LOCAL_MEMSIZE    	   ".
       "-L $PROGRAM_MEMSIZE  	   ".
       "-p $PHYSNUM       	   ".
       "-v $VIRTNUM_TOTAL      	   ".
       "-l 1		       	   ".
       "-i		       	   ".
       "--set global-devname $PRAM_DEVICE ".
       "--set   prog-devname $PROG_DEVICE ".
       "--set   ints-devname $INTS_DEVICE ".
       "--set file $OBJFILE        ".
       "--set pramos-file $PRAMOS_FILE ".
       "--init-string \"f $GLOBAL_MEMSIZE_LOCATION $GLOBAL_MEMSIZE, ".
                       "f $PRAMOS_KSHBRK_SIZE_LOCATION $PRAMOS_KSHBRK_SIZE, ".
                       "f $PRAMOS_GETTIMEOFDAY_HI_LOCATION $GTODUS_HI, ".
                       "f $PRAMOS_GETTIMEOFDAY_LO_LOCATION $GTODUS_LO, ".
                      "g\" ".
       ($TRACE_OUTPUT?" --trace $TRACE_OUTPUT ":"").
       "$PRAMSIM_OPT".
       "$PRAMOS_COD_FILE";

if($VERBOSE){print "$run\n";};

system $run;

#
# Termination
#

&cleanup;

exit 0;
