/* Student version of SIMLATOR.C  for COP 4600 */
/* Revision 1/8/91                             */


#include "stdio.h"
#include "stdlib.h"
#include "time.h" 
#include "string.h"
#include "osdefs.h"
#include "externs.h"

/* Simulation Control Variable */
int    OBJECTIVE = 0;

char Lname[9];    /* first 8 letters of last name (config parameter)        */
char Outfile[14]; /* output file name --first eight letters of last name    */
/* File pointers to files */
FILE   *simout,           /* file pointer for SIMLATOR.OUT             */
       *scriptfp,         /* file pointer for SCRIPT.DAT               */
       *logonfp,          /* file pointer for LOGON.DAT                */
       *PROGM_FILE[6];    /* file pointer for program.DAT              */
                          /*           program = (see pgmidtab[])      */

/* Strings for input and output matching */
char *pgmidtab[]   = {"EDITOR","PRINTER","COMPILER","LINKER","USER",
                      "BOOT","LOGOFF"};
char *eventidtab[] = {"LOGON","SIO","WIO","EIO","END","TIMER",
                      "SEGFAULT", "ADRFAULT"};
char *opidtab[]    = {"SIO","WIO","REQ","JUMP","SKIP","END" };
	 /*  note  OPCDSIZE = number of opcodes */

/* variables for interrupt registers, clock & switches */
int    CPU_SW;      /* control switch for the CPU (1 = ON, 0 = OFF)         */
int    SCHED_SW;    /* control switch for the Scheduler(1 = ON, 0 = OFF)    */
int    AGENT;       /* terminal RANGE = 1..TRMSIZE                          */
                    /* device   RANGE = TRMSIZE+1..TRMSIZE+DEVSIZE          */
int    EVENT;       /* LOGON, SIO, WIO, EIO, PGMEMD, TIMEEVENT,
                       SEGFAULT and ADRFAULT                                */
struct simtime    CLOCK = { 0, 0 };  /* Simulation clock                    */

/* Variables used for CPU statistics */
int    NJOBS            = 0;         /* Total jobs processed                */
struct simtime TOTLOGON = { 0, 0 };  /* Total job  processing time          */
struct simtime TOTWAIT  = { 0, 0 };  /* Total job  wait       time          */
struct simtime TOTBLKED = { 0, 0 };  /* Total job  blocked    time          */
struct simtime TOTRUN   = { 0, 0 };  /* Total job  execution  time          */
struct simtime AVERUN   = { 0, 0 };  /* Average user execution time         */
struct simtime AVEWAIT  = { 0, 0 };  /* Average user wait      time         */
struct simtime AVEBLKED = { 0, 0 };  /* Average user blocked   time         */
struct simtime AVELOGON = { 0, 0 };  /* Average user logon     time         */

/* Variables used with Round-Robin scheduling */
struct timer_type TIMER = {          /* Simulation timer (used with RR)     */
                           {0,0},   /* struct simtime TIME_OUT              */
                            0,      /* RR quantum in instructions           */
                            {0,0}   /* RR quantum in ( SEC, NSEC  )         */
                          };

struct state_type OldState = {      /* Interrupt State Save Area          */
                            0x01,   /* priviledged mode                   */
                           {0,0}    /* Interrupt Handler Entry Point      */
                           };

struct state_type NewState = {      /* New Interrupt State                 */
                           0x01,     /* priviledged mode                   */
                           {0,0}     /* Interrupt Handler Entry Point      */
                           };

/* variable for CPU.  See osdefs.h for structure details. */
struct cpu_type   CPU =
                          {1000,    /* CPU rate in nanosec/instr. (rate)    */
                           1000000, /* CPU rate in instr/sec   (CPURATE)    */
                           0x01,    /* CPU mode (priviledged)   (mode)      */
                         { 0, 0 },  /* program counter          (pc  )      */
                         NULL,      /* active pcb               (actvpcb)   */
                         { 0, 0 },  /*                          (qwait)     */
                         { 0, 0 },  /*                          (busy)      */
                         { 0, 0 },  /*                         (response)   */
                         { 0, 0 },  /*                           (idle)     */
                         0,         /* no. served               (served)    */
                         0,         /* max queue length         (maxq)      */
                         0,         /* current queue length     (qlen)      */
                         0.0,       /* utilization              (utilize)   */
                         NULL,      /* head ready list          (ready)     */
                         NULL };    /* tail ready list          (tail)      */


/* variables for simulator memory structures */
int                  MAXSEGMENTS = 1;   /* Maximum size of Memory Map       */
                                        /* (must be a power of 2)           */
struct segment_type     *MEMMAP = NULL; /* Memory Address Mapping Table     */
                                        /* (size = 2*MAXSEGMENTS)           */
int                      MEMSIZE = 1000; /* Memory size                      */
struct instr_type           *MEM = NULL; /* Processor memory array           */
struct seg_list         *FreeMem = NULL; /* List of free segments ordered    */
                                         /* by memory base address.          */
unsigned int           TotalFree = 1000; /* Total Free Memory(see MEMSIZE)   */
struct addr_type MAR;


/* Operating System tables for device and terminal management                */
struct device_type  *devtable    = NULL; /* Device Table(array of devices)   */
struct pcb_type    **termtable   = NULL; /* Terminal Table (array of *pcb)   */


struct event_type   *new_events  = NULL; /* pointer to head of event list    */

/* Control variables */
int    DEVSIZE        =  0;              /* Number of devices                */
int    TRMSIZE        =  0;              /* Number of terminals              */
int    MAXSCRIPT      =  10;             /* ten programs                     */


/* variables used for scheduling alogrithms */
int    SCHED              =  FCFS;       /* Current CPU scheduling algorithm */
unsigned long   ONEOVRBETA;              /* expected CPU burst (SJN)         */
unsigned long   LOGON_T   =  SEC;        /* Time units for LOGON events      */
double          RHO       =  0.0;        /* SJN Smoothing factor             */

/* Debugging flags.  Use them! */
int    DEBUG_MEM     = 0 ;     /* flag that controls MEMORY DEBUG OUTPUT    */
int    DEBUG_EVTQ    = 0;      /* flag that controls EVENT_Q DEBUG OUTPUT   */
int    DEBUG_PCB     = 0;      /* flag that controls PCB DEBUG OUTPUT       */
int    DEBUG_RBLIST  = 0;      /* flag that controls RBLIST DEBUG OUTPUT    */
int    DEBUG_RBQ     = 0;      /* flag that controls RB_Q DEBUG OUTPUT      */
int    DEBUG_CPUQ    = 0;      /* flag that controls CPUQ DEBUG OUTPUT      */


/****************************************************************************/
/* This is the SIMULATION DRIVER                                            */
/****************************************************************************/
main(int argc, char *argv[])
    {
      Init();                              /* read config.dat           */
      Load_events();                       /* Initialize new_events list  */


      Open_output( Outfile );              /* Open simulation output file */
      Boot();

      printf("\nSimulation begins...\n");

      while( new_events != NULL ) {
       CPU_SW = SCHED_SW = 0;
       Interrupt();   /* Action of the interrupt hardware                  */
                      /* get next event; update system clock; output event */

       if( Interrupt_Handler() ) break; /* Diagnose and service interrupt. */
                                     /* Exit on Memory fault for OBJ 2     */
       if( OBJECTIVE >= 4 && SCHED_SW ) CPU.actvpcb = Scheduler();
       if( OBJECTIVE >= 4 && CPU.actvpcb != NULL )
           if( CPU_SW ) Dispatcher();
           else { CPU.mode = CPU.actvpcb->cpu_save.mode;
                  CPU.pc   = CPU.actvpcb->cpu_save.pc;
                }
       if( OBJECTIVE == 2 )  XPGM( &OldState );
      } /* While */
      Wrapup();       /* Write statistics.  Close files, etc.  */
      printf("\n...Simulation ends.\n");
      exit(0);
} /* main */




/***************************** Given Functions ******************************/

int
Interrupt_Handler(void)
/* ------------------------------------------------------------------------- */
/*      Interrupt handler is in control (with all interrupts inhibited).     */
/*      Save the state of the interrupted program (if the CPU is busy):      */
/*      (a) OldState is copied into CPU.actvpcb->cpu_save                    */
/*      (b) UserMap  is copied into *CPU.actvpcb->segtable; (optional)       */
/*      Give control to interrupt service routine.                           */
/* ------------------------------------------------------------------------- */
   {
     if (CPU.actvpcb != NULL)  /* CPU is busy */
       {
	 CPU.actvpcb->cpu_save = OldState;
       }

       switch( EVENT ) {
           case LOGON      : Logon_Service(); break;
           case STARTIO    : Sio_Service();   break;
           case WAITIO     : Wio_Service();   break;
           case ENDIO      : Eio_Service();   break;
           case PGMEND     : 
			if (OBJECTIVE == 2) return(1);
			End_Service();   break;
           case SEGFAULT   :
           case ADRFAULT   : Abend_Service();
                             if( OBJECTIVE == 2) return(1);
                             break;
           case TIMEEVENT  : Timer_Service(); break;
         default         : printf(" INVALID EVENT CODE IN MAIN LOOP!!! ");
                           exit(0);
       } /* switch */
       return(0);
   }

void
Write_stats(void)
/* ------------------------------------------------------------------------- */
/*     This function writes the statistics summary to DATA*.OUT           */
/* ------------------------------------------------------------------------- */
   {  int i, flag;
      struct simtime t;

     fprintf(simout,"\n\n\n                              TERMINAL  SUMMARY                             ");
     fprintf(simout,   "\n ===========================================================================");
     fprintf(simout,   "\n TERMNL|        EXEC    |      WAIT    |   BLOCKED    |   ELAPSED    |   EFF");
     fprintf(simout,   "\n ======|================|==============|==============|==============|======");
      for(i = 0, flag = 0; i < TRMSIZE ; i++ ){
       if( termtable[i] == NULL ) continue;
       if( flag )
       fprintf(simout,  "\n ------|----------------|--------------|--------------|--------------|------");
       fprintf(simout,"\n %s  |  %10lu SC |%10lu SC |%10lu SC |%10lu SC | %.3f",
                 termtable[i]->user,
                 termtable[i]->trun.seconds,
                 termtable[i]->tready.seconds,
                 termtable[i]->tblocked.seconds,
                 termtable[i]->tlogon.seconds,
                 termtable[i]->efficncy);
       fprintf(simout,"\n       |  %10lu NS |%10lu NS |%10lu NS |%10lu NS |",
                 termtable[i]->trun.nanosec,
                 termtable[i]->tready.nanosec,
                 termtable[i]->tblocked.nanosec,
                 termtable[i]->tlogon.nanosec);
      flag = 1;
      }
      fprintf(simout,  "\n ===========================================================================");
      fprintf(simout,  "\n TOTALS|  %10lu SC |%10lu SC |%10lu SC |%10lu SC |",
                           TOTRUN.seconds,
                           TOTWAIT.seconds,
                           TOTBLKED.seconds,
                           TOTLOGON.seconds);
      fprintf(simout, "\n       |  %10lu NS |%10lu NS |%10lu NS |%10lu NS |",
                           TOTRUN.nanosec,
                           TOTWAIT.nanosec,
                           TOTBLKED.nanosec,
                           TOTLOGON.nanosec);
      fprintf(simout, "\n ------|----------------|--------------|--------------|--------------|");
      fprintf(simout,  "\n AVERGE|  %10lu SC |%10lu SC |%10lu SC |%10lu SC |",
                           AVERUN.seconds,
                           AVEWAIT.seconds,
                           AVEBLKED.seconds,
                           AVELOGON.seconds);
      fprintf(simout, "\n       |  %10lu NS |%10lu NS |%10lu NS |%10lu NS |",
                           AVERUN.nanosec,
                           AVEWAIT.nanosec,
                           AVEBLKED.nanosec,
                           AVELOGON.nanosec);
      fprintf(simout,  "\n =====================================================================");


      /* Write out DEVICE statistics */
      fprintf(simout,"\n\n\n                                DEVICE  SUMMARY                            ");
      fprintf(simout,   "\n ===========================================================================");
      fprintf(simout,   "\n DEVICE|        BUSY    |      WAIT    |      IDLE    |   RESPONSE   | %UTIL");
      fprintf(simout,   "\n ======|================|==============|==============|==============|======");
      fprintf(simout,   "\n CPU   |  %10lu SC |%10lu SC |%10lu SC |%10lu SC | %.2f",
                CPU.busy.seconds,
                CPU.qwait.seconds,
                CPU.idle.seconds,
                CPU.response.seconds,
                CPU.utilize);
      fprintf(simout,"\n       |  %10lu NS |%10lu NS |%10lu NS |%10lu NS |",
                CPU.busy.nanosec,
                CPU.qwait.nanosec,
                CPU.idle.nanosec,
                CPU.response.nanosec);
      for(i=0; i < DEVSIZE; i++ ){
      if( !devtable[i].served ) continue;
      fprintf(simout, "\n ------|----------------|--------------|--------------|--------------|");
      fprintf(simout, "\n %s  |  %10lu SC |%10lu SC |%10lu SC |%10lu SC | %.2f",
                devtable[i].devid,
                devtable[i].busy.seconds,
                devtable[i].qwait.seconds,
                devtable[i].idle.seconds,
                devtable[i].response.seconds,
                devtable[i].utilize);
      fprintf(simout,"\n       |  %10lu NS |%10lu NS |%10lu NS |%10lu NS |",
                devtable[i].busy.nanosec,
                devtable[i].qwait.nanosec,
                devtable[i].idle.nanosec,
                devtable[i].response.nanosec);
      }
      fprintf(simout,  "\n =====================================================================");
} /* Write_stats */


void
Init(void)
/* ------------------------------------------------------------------------- */
/*       This function reads CONFIG.DAT files                                */
/*        and initializes the simulator environment.                         */
/* ------------------------------------------------------------------------- */
   {
     FILE *cp, *fp, *lp;
     char ident[15];
     int  scancode,i,err,oneovrbeta,quantum, objective, lname;
     unsigned long hr, min, sec, milsec, msec, nsec;


     oneovrbeta = quantum = objective = lname = 0;  /* boolean flags */

     if( (fp = fopen("config.dat","r")) == NULL  )
       { printf(" CONFIG.DAT file does not exit! \n");
         exit(0); }

     while( fscanf(fp,"%s",ident) != EOF )
          {       if( !strcmp(ident,"TIME=")){
                    if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING TIME= PARAMETER \n");
                      exit(0);
                    }
                          if( !strcmp(ident,"MIN")){  LOGON_T = MIN;
                    }else if( !strcmp(ident,"SEC")){  LOGON_T = SEC;
                    }else if( !strcmp(ident,"MSEC")){ LOGON_T = MSEC;
                    }else if( !strcmp(ident,"mSEC")){ LOGON_T = mSEC;
                    }else if( !strcmp(ident,"NSEC")){ LOGON_T = NSEC;
                    }else {
                            printf(" UNRECOGNIZABLE TIME= PARAMETER \n");
                            exit(0);
                    } /* else */
            }else if( !strcmp(ident,"OBJECTIVE=")){
                    if( (scancode = fscanf(fp,"%d",&OBJECTIVE)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING OBJECTIVE= PARAMETER \n");
                      exit(0);
                    }
                    objective = 1;
            }else if( !strcmp(ident,"RRQUANTUM=")){
                    if( (scancode = fscanf(fp,"%U",&TIMER.QUANTUM)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING RRQUANTUM= PARAMETER \n");
                      exit(0);
                    }
                    quantum = 1;
            }else if( !strcmp(ident,"1/BETA=")){
                    if( (scancode = fscanf(fp,"%U",&ONEOVRBETA)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING 1/BETA= PARAMETER \n");
                      exit(0);
                    }
                    oneovrbeta = 1;
            }else if( !strcmp(ident,"RHO=")){  float temp;
                    if( (scancode = fscanf(fp,"%f",&temp)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING RHO= PARAMETER \n");
                      exit(0);
                    }
                    RHO = (double)temp;
                    if( RHO < 0.0 || RHO > 1.0 ) {
                      printf(" INVALID RHO= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"TERMINALS=")){
                    if( (scancode = fscanf(fp,"%d",&TRMSIZE)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING TERMINALS= PARAMETER \n");
                      exit(0);
                    }
                    if( TRMSIZE <= 0 ) {
                      printf(" INVALID TERMINALS= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"DEVICES=")){
                    if( (scancode = fscanf(fp,"%d",&DEVSIZE)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING DEVICES= PARAMETER \n");
                      exit(0);
                    }
                    if( DEVSIZE <= 0 ) {
                      printf(" INVALID DEVICES= PARAMETER \n");
                      exit(0);
                    }
                    /* allocate Device Table  */
                    devtable = calloc(DEVSIZE, sizeof(struct device_type));
                    if( devtable == NULL) {
                      printf(" DEVICE TABLE ALLOCATION FAILURE! \n");
		      exit(0);
                    }
                    { /* block */
                      unsigned long bytes_per_sec;

                    /* read device description and enter in Device Table */
                    for(i=0; i< DEVSIZE; i++){
                      if( (scancode =
                      fscanf(fp,"\nID= %4c RATE= %U ",
                                            devtable[i].devid,
                                            &bytes_per_sec)) == EOF ||
                           scancode < 2) {
		     printf(" MISSING DEVICE= SPECIFICATION PARAMETER(s) \n");
                        exit(0);
                      } /* if */
                    devtable[i].byps = bytes_per_sec;
                    devtable[i].npb = (double)1.0e9/(double)bytes_per_sec;
                    devtable[i].qwait.seconds = 0;
                    devtable[i].qwait.nanosec = 0;
                    devtable[i].busy = devtable[i].qwait;
                    devtable[i].served = 0;
                    devtable[i].maxq   = 0;
		    devtable[i].qlen   = 0;
		    } /* for */
                    } /* end block */
            }else if( !strcmp(ident,"MEMSIZE=")){
                    if( (scancode = fscanf(fp,"%U",&MEMSIZE)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING MEMSIZE= PARAMETER \n");
                      exit(0);
                        }
            }else if( !strcmp(ident,"CPURATE=")){
                    if( (scancode = fscanf(fp,"%U",&CPU.rate)) == EOF ||
                         scancode < 1) {
		      printf(" MISSING CPURATE= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"MAXSCRIPT=")){
                    if( (scancode = fscanf(fp,"%d",&MAXSCRIPT)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING MAXSCRIPT= PARAMETER \n");
                      exit(0);
                    }
                    if( MAXSCRIPT <= 0 ) {
                      printf(" INVALID MAXSCRIPT= PARAMETER \n");
                      exit(0);
                        }
              }else if( !strcmp(ident,"LNAME=")){
                        if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                               scancode < 1){
                          printf(" MISSING LNAME= PARAMETER \n");
                          exit(0);
                        }
                        lname = 1;
                        strcpy(Lname,ident);
            }else if( !strcmp(ident,"SCHED=")){
                    if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING SCHED= PARAMETER \n");
                      exit(0);
                    }
                          if( !strcmp(ident,"FCFS"))  {   SCHED = FCFS;
                    }else if( !strcmp(ident,"SJN"))   {   SCHED = SJN;
                    }else if( !strcmp(ident,"HPRN"))  {   SCHED = HPRN;
                    }else if( !strcmp(ident,"RNDRBN")){   SCHED = RNDRBN;
                    }else {
                            printf(" UNRECOGNIZABLE SCHED= PARAMETER \n");
                            exit(0);
                    } /* else */
            }else if( !strcmp(ident,"MAXSEGMENTS=")){
                    if( (scancode = fscanf(fp,"%d",&MAXSEGMENTS)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING MAXSEGMENTS= PARAMETER \n");
                      exit(0);
                    }
                    if( MAXSEGMENTS < 1) {
                      printf(" INVALID MAXSEGMENTS= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"DEBUG_MEM=")){
                    if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING DEBUG_MEM= PARAMETER \n");
                      exit(0);
                    }
                         if( !strcmp(ident,"ON" ) ) DEBUG_MEM = 1;
                    else if( !strcmp(ident,"OFF") ) DEBUG_MEM = 0;
                    else {
                      printf(" INVALID DEBUG_MEM= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"DEBUG_EVTQ=")){
                    if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING DEBUG_EVTQ= PARAMETER \n");
                      exit(0);
                    }
                         if( !strcmp(ident,"ON" ) ) DEBUG_EVTQ = 1;
                    else if( !strcmp(ident,"OFF") ) DEBUG_EVTQ = 0;
                    else {
                      printf(" INVALID DEBUG_EVTQ= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"DEBUG_PCB=")){
                    if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING DEBUG_PCB= PARAMETER \n");
                      exit(0);
                    }
                         if( !strcmp(ident,"ON" ) ) DEBUG_PCB = 1;
                    else if( !strcmp(ident,"OFF") ) DEBUG_PCB = 0;
                    else {
                      printf(" INVALID DEBUG_PCB= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"DEBUG_RBLIST=")){
                    if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING DEBUG_RBLIST= PARAMETER \n");
                      exit(0);
                    }
                         if( !strcmp(ident,"ON" ) ) DEBUG_RBLIST = 1;
                    else if( !strcmp(ident,"OFF") ) DEBUG_RBLIST = 0;
                    else {
                      printf(" INVALID DEBUG_RBLIST= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"DEBUG_RBQ=")){
                    if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING DEBUG_RBQ= PARAMETER \n");
                      exit(0);
                    }
                         if( !strcmp(ident,"ON" ) ) DEBUG_RBQ = 1;
                    else if( !strcmp(ident,"OFF") ) DEBUG_RBQ = 0;
                    else {
                      printf(" INVALID DEBUG_RBQ= PARAMETER \n");
                      exit(0);
                    }
            }else if( !strcmp(ident,"DEBUG_CPUQ=")){
                    if( (scancode = fscanf(fp,"%s",ident)) == EOF ||
                         scancode < 1) {
                      printf(" MISSING DEBUG_CPUQ= PARAMETER \n");
                      exit(0);
                    }
                         if( !strcmp(ident,"ON" ) ) DEBUG_CPUQ = 1;
                    else if( !strcmp(ident,"OFF") ) DEBUG_CPUQ = 0;
                    else {
                      printf(" INVALID DEBUG_CPUQ= PARAMETER \n");
                      exit(0);
                    }
            }else { /* unrecognizable input */
                  printf(" Warning: Unrecognizable parameter %s in CONFIG.DAT!\n",ident);
                  while( fgetc(fp) != '\n');
                } /* else */
     } /* while */


     /* validate config.dat parameters */
     err = 0;


     /* Do NOT put your LASTNAME.OUT as the output file name */
     if( lname ){
                 strcpy ( Outfile, Lname );
                 strcat ( Outfile, ".out");
                 strlwr ( Outfile );
     }else{
                 err = 1;
                 printf(" LNAME= PARAMETER NOT DEFINED! \n");
     }

     if( !objective ){
        err = 1;
        printf(" OBJECTIVE= PARAMETER NOT DEFINED! \n");
     }

     if( !oneovrbeta && (SCHED == HPRN || SCHED == SJN) ){
        err = 1;
        printf(" 1/BETA= PARAMETER NOT DEFINED! \n");
     }

     if( !quantum && SCHED == RNDRBN ){
        err = 1;
        printf(" RRQUANTUM= PARAMETER NOT DEFINED! \n");
     }

     if( devtable == NULL ) {
       printf(" DEVICES= PARAMETER NOT DEFINED! \n");
       err = 1;
     }

     if( (lp = fopen("logon.dat","r")) == NULL ){
         printf(" LOGON.DAT does not exist! \n");
         err = 1;
     }else fclose(lp);

     if( (scriptfp = fopen("script.dat","r")) == NULL ){
         printf(" SCRIPT.DAT does not exist! \n");
         err = 1;
     }

     if( (PROGM_FILE[EDITOR] = fopen("editor.dat","r")) == NULL )
         printf(" Warning: EDITOR.DAT does not exist! \n");

     if( (PROGM_FILE[PRINTER] = fopen("printer.dat","r")) == NULL )
         printf(" Warning: PRINTER.DAT does not exist! \n");

     if( (PROGM_FILE[COMPILER] = fopen("compiler.dat","r")) == NULL )
         printf(" Warning: COMPILER.DAT does not exist! \n");

     if( (PROGM_FILE[LINKER] = fopen("linker.dat","r")) == NULL )
         printf(" Warning: LINKER.DAT does not exist! \n");

     if( (PROGM_FILE[USER] =  fopen("user.dat","r")) == NULL )
           printf(" Warning: USER.DAT does not exist! \n");

     if( (PROGM_FILE[BOOT] =  fopen("boot.dat","r")) == NULL )
           printf(" Warning: BOOT.DAT does not exist! \n");

     /* COMPUTE indirect configuration parameters */
     CPU.CPU_burst = SEC/CPU.rate;  /* instructions per sec */

     /* allocate memory map hardware */
     MEMMAP  =  calloc(2*MAXSEGMENTS, sizeof(struct segment_type));
     if( MEMMAP == NULL )  {
       printf(" MEMORY ADDRESS MAP ALLOCATION FAILURE! \n");
       err = 1;
     }
     /* allocate memory */
     MEM = calloc(MEMSIZE, sizeof(struct instr_type));
     if( MEM == NULL) {
       printf(" MEMORY ARRAY ALLOCATION FAILURE! \n");
       err = 1;
     }

     /* initialize list of free memory segments */
     FreeMem = calloc(1, sizeof(struct seg_list));
     if( FreeMem == NULL ) {
       printf(" FREE LIST ALLOCATION FAILURE! \n");
       err = 1;
     }
     TotalFree        = MEMSIZE;
     FreeMem->segsize = MEMSIZE;
     FreeMem->segptr  = 0;
     FreeMem->next    = NULL;

     /* allocate terminal table */
     termtable = calloc(TRMSIZE, sizeof(struct pcb_type *));
     if( termtable == NULL) {
       printf(" TERMINAL TABLE ALLOCATION FAILURE! \n");
       err = 1;
     }
     for(i=0; i <TRMSIZE ; i++) termtable[i] = NULL;

     fclose(fp);
     if( err ) exit(0);
} /* Init */


void
Open_output(char *out_name)
/* ------------------------------------------------------------------------- */
/*       This function opens the simulator output file and writes the        */
/*       values of all configuration parameters.                             */
/* ------------------------------------------------------------------------- */
   {

     long clock;
     char *date;
     char buff[80];
     int  i;
     unsigned long hr, min, sec, milsec, msec, nsec;

     if( (simout = fopen(out_name,"w")) == NULL) {
          printf(" Error opening %s file for output.\n",out_name);
          exit(0);
     }
     else{

     time(&clock);  date = ctime(&clock);
     date[24] = date[25];  /* trim '\n' character at end of date */

     fprintf(simout,"\n +-----------------------------------------------------+");
     fprintf(simout,"\n |  COP 4600,                         Simulator Report |");
     fprintf(simout,"\n |  DATE:  %-25s%20s",date,"|");
     fprintf(simout,"\n |  NAME:  %-8s                                    |", Lname);
     fprintf(simout,"\n |                                                     |");
     fprintf(simout,"\n |  OBJECTIVE #      ......................%-12d|", OBJECTIVE);
     fprintf(simout,"\n |  LOGON TIME UNITS ......................");
            if( LOGON_T == MIN ) strcpy(buff,"MIN ");
            if( LOGON_T == SEC ) strcpy(buff,"SEC ");
            if( LOGON_T == MSEC) strcpy(buff,"MSEC");
            if( LOGON_T == mSEC) strcpy(buff,"mSEC");
            if( LOGON_T == NSEC) strcpy(buff,"NSEC");
            fprintf(simout,"%-12s|",buff);
     fprintf(simout,"\n |  NO. OF TERMINALS  .....................");
            fprintf(simout,"%-12d|",TRMSIZE);
     fprintf(simout,"\n |  MEMORY SIZE  ..........................");
              fprintf(simout,"%-12d|",MEMSIZE);
     fprintf(simout,"\n |  CPU RATE (NSEC/INSTR) .................");
            fprintf(simout,"%-12lu|",CPU.rate);
     fprintf(simout,"\n |  SCHEDULING ALGORITHM  .................");
            if( SCHED == FCFS )  strcpy(buff,"FCFS");
            if( SCHED == SJN  )  strcpy(buff,"SJN");
            if( SCHED == HPRN )  strcpy(buff,"HPRN");
            if( SCHED == RNDRBN) strcpy(buff,"RND-ROBIN");
            fprintf(simout,"%-12s|",buff);
     if( SCHED == SJN || SCHED == HPRN ) {
         fprintf(simout,"\n |  EXPECTED CPU BURST (1/BETA) ...........");
              fprintf(simout,"%-12lu|",ONEOVRBETA);
         fprintf(simout,"\n |  SMOOTHING FACTOR (RHO) ................");
              fprintf(simout,"%-12.3f|",RHO);
     }
     if( SCHED == RNDRBN ) {
         fprintf(simout,"\n |  ROUND ROBIN QUANTUM ...................");
              fprintf(simout,"%-12lu|",TIMER.QUANTUM);
     }
     fprintf(simout,"\n |  MAX SCRIPT LENGTH  ....................");
            fprintf(simout,"%-12d|",MAXSCRIPT);
     fprintf(simout,"\n |                                                     |");
     fprintf(simout,"\n |  DEBUG MEMORY ..........................");
            if( DEBUG_MEM ) strcpy(buff,"ON");
            else            strcpy(buff,"OFF");
            fprintf(simout,"%-12s|",buff);
     fprintf(simout,"\n |  DEBUG EVENT_Q .........................");
            if( DEBUG_EVTQ) strcpy(buff,"ON");
            else            strcpy(buff,"OFF");
            fprintf(simout,"%-12s|",buff);
     fprintf(simout,"\n |  DEBUG PCB..............................");
            if( DEBUG_PCB) strcpy(buff,"ON");
            else           strcpy(buff,"OFF");
            fprintf(simout,"%-12s|",buff);
     fprintf(simout,"\n |  DEBUG RBLIST...........................");
            if( DEBUG_RBLIST) strcpy(buff,"ON");
            else              strcpy(buff,"OFF");
            fprintf(simout,"%-12s|",buff);
     fprintf(simout,"\n |  DEBUG RBQ..............................");
            if( DEBUG_RBQ)  strcpy(buff,"ON");
            else            strcpy(buff,"OFF");
            fprintf(simout,"%-12s|",buff);
     fprintf(simout,"\n |  DEBUG CPUq ............................");
            if( DEBUG_CPUQ) strcpy(buff,"ON");
            else            strcpy(buff,"OFF");
            fprintf(simout,"%-12s|",buff);
     fprintf(simout,"\n |                                                     |");
     fprintf(simout,"\n | =================  DEVICE TABLE =================== |");
     fprintf(simout,"\n |                                                     |");
     fprintf(simout,"\n |  ID      RATE(*)                                    |");
     fprintf(simout,"\n |                                                     |");
            for(i=0; i<DEVSIZE; i++) {
     fprintf(simout,"\n |  %-4s    %-12lu                               |",
                devtable[i].devid,
                (unsigned long)((double)1.0e9/(double)devtable[i].npb));
            } /* for */
     fprintf(simout,"\n |                                                     |");
     fprintf(simout,"\n | (*)Bytes/Second                                     |");
     fprintf(simout,"\n +-----------------------------------------------------+");

     Event_hdr();
     }

} /* Open_output */


void
Wrapup(void)
/* ------------------------------------------------------------------------- */
/*      This function performs processing at the end of simulation.          */
/*      It computes all statistics for the CPU and each device and terminal. */
/*      It then calls a function to write the statistics to DATA*.OUT.       */
/*      Finally, it closes all files.                                        */
/* ------------------------------------------------------------------------- */
{
     int datfile;

     /* Compute and print statistics to simout */
     Calc_stats();
     Write_stats();

     /* Close all file pointers */
     fprintf(simout, "\n\nEnd of Benchmark Report.\n\n");
     fclose(simout);
     fclose(scriptfp);
     /* fclose(logonfp); */  /* closed in Load_events() in OBJ1.C */
     for( datfile = EDITOR; datfile < LOGOFF; datfile++ )
       if (PROGM_FILE[datfile] != NULL)
                 fclose(PROGM_FILE[datfile]);
}


void
Event_hdr(void)
   {
     fprintf(simout,"\n\n  EVENT  AGENT  HR:xxxxxxxx MN:xx SC:xx MS:xxx mS:xxx NS:xxx");
     fprintf(simout,  "\n  -----  -----  --------------------------------------------\n");
} /* Event_hdr */





/******************** Functions for simtime manipulation ********************/

void
Add_time(struct simtime *time1, struct simtime *time2)
/* ------------------------------------------------------------------------- */
/*       This function computes: time2 = time2 + time1                       */
/* ------------------------------------------------------------------------- */
   {

     if( time2->nanosec < (SEC - time1->nanosec) )
           time2->nanosec += time1->nanosec;
     else{ time2->seconds++;
           time2->nanosec -= SEC - time1->nanosec; }
     time2->seconds += time1->seconds;
} /* Add_time */


void
Diff_time(struct simtime *time1, struct simtime *time2)
/* ------------------------------------------------------------------------- */
/*         This function computes: time2 = time2 - time1                     */
/*         Assuming:      time2 >= time1                                     */
/* ------------------------------------------------------------------------- */
   {

     if( time2->seconds == time1->seconds ){
       time2->seconds  = 0;
       time2->nanosec -= time1->nanosec;
     }else{
       time2->seconds -= time1->seconds + 1;
       if( time2->nanosec >= time1->nanosec ){
              ++time2->seconds;
              time2->nanosec -= time1->nanosec;
       }else  time2->nanosec += SEC - time1->nanosec;
     }
} /* Diff_time */


int
Cmpr_time(struct simtime *p, struct simtime *q)
/* ------------------------------------------------------------------------- */
/*    This function compares two simulation times:  p,q                      */
/*    IF  p < q  RETURN -1                                                   */
/*    IF  p = q  RETURN  0                                                   */
/*    IF  p > q  RETURN +1                                                   */
/* ------------------------------------------------------------------------- */
{ int result;

      if( p->seconds < q->seconds ) { result = -1; goto done; }
      if( p->seconds > q->seconds ) { result = +1; goto done; }
      if( p->nanosec < q->nanosec ) { result = -1; goto done; }
      if( p->nanosec > q->nanosec ) { result = +1; goto done; }
      result = 0;
      done:
      return(result);
} /* Cmpr_time */


void
Convrt_time(struct simtime *s, unsigned long t)
/* ------------------------------------------------------------------------- */
/*      This function converts external time units (defined by LOGON_T)      */
/*      to internal seconds and nanosec.                                     */
/* ------------------------------------------------------------------------- */
    {

      switch(LOGON_T){
         case MIN: s->seconds = t*60;
                   s->nanosec = 0;
                   break;
         case SEC: s->seconds = t;
                   s->nanosec = 0;
                   break;
         case MSEC:s->seconds = t/1000;
                   s->nanosec = (t - 1000*s->seconds)*MSEC;
                   break;
         case mSEC:s->seconds = t/1000000;
                   s->nanosec = (t - 1000000*s->seconds)*mSEC;
                   break;
         case NSEC:s->seconds = t/1000000000;
                   s->nanosec = t - 1000000000*s->seconds;
      }
} /* Convrt_time */


double
Divd_time(struct simtime *t1, struct simtime *t2)
/* ------------------------------------------------------------------------- */
/*   This function divides two simulation times (t1/t2) by first converting  */
/*   both to seconds and fractions of a second.                              */
/* ------------------------------------------------------------------------- */
   {  double x1,x2;

      x1 = (double)t1->seconds + (double)t1->nanosec/DSEC;
      x2 = (double)t2->seconds + (double)t2->nanosec/DSEC;
      return( x1/x2 );
} /* Divd_time */


void
Ave_time(struct simtime *t1, struct simtime *t2, int n)
/* ------------------------------------------------------------------------- */
/*  This function computes t1 = t2/n                                         */
/*  Where t1 and t2 are simulation times, and n is a positive integer.       */
/* ------------------------------------------------------------------------- */
    {  double x, dn;

       dn = (double)n;
       x = ((double)t2->seconds/dn) + ((double)t2->nanosec/dn)*1.0e-9;
       t1->seconds = x;
       t1->nanosec = (x - (double)t1->seconds) * DSEC;
} /* Ave_time */


void
Burst_time(struct simtime *t, unsigned long b)
/* ------------------------------------------------------------------------ */
/*  This function converts a cpu burst into simulation time.  The time      */
/*  depends on the cpu rate and length of the burst.                        */
/* ------------------------------------------------------------------------ */
    {  t->seconds = b / CPU.CPU_burst;
       t->nanosec = ( b - CPU.CPU_burst * t->seconds ) * CPU.rate;
} /* Burst_time */


/*void strlwr(char *what_str)
{
  while (*what_str)
    {
      *what_str = tolower(*what_str);
       what_str++;
    }
}*/  /* strlwr */

/*void strupr(char *what_str)
{
  while (*what_str)
    {
      *what_str = toupper(*what_str);
       what_str++;
    }
} */ /* strupr */


double
SimTimeToDouble(struct simtime *time1) {
	double  ctime, nano;
	nano = time1->nanosec/(double)SEC;
	ctime = time1->seconds + nano;
	return(ctime);
}


