
#include "Include.h"

struct InstrStruct Instr[MaxNInstrs],  /* the Mas program */
                   *ISPtr;  /* pointer to the current instruction */

struct CPUStruct *CPU;  /* CPU[I] contains the status of the I-th CPU */

struct TableEntry SymTable[MaxSyms];

unsigned *Mem;  /* this is the shared memory, with Mem[I] being
                        the word at address I */ 

int NInstrs,  /* number of instructions in the program */
    NDataSyms,  /* number of items in the symbol table */
    NCPUs,  /* number of CPUs */                  
    BreakpointOn = 0,  /* 1 or 0, according to whether breakpoint is active */
    Breakpoint,  /* location of breakpoint */
    AllCPUsBreak = 0,  /* if 1, then break whenever any CPU reaches Breakpoint; 
                          otherwise, see BreakpointPNum below */
    BreakpointPNum;  /* CPU number for which the breakpoint applies */

unsigned SimTime = 0,  /* number of cycles elapsed so far */
	 OldSimTime,  /* time at which recording started */
	 StopTime;  /* see command `et' above */
	      
int MemSize;  /* number of words of simulated memory */

char ProgPrefix[25],  /* program prefix, e.g. x in x.out */
     ProgFileName[29],  /* e.g. x.out */
     SymFileName[29],  /* e.g. x.sym */
     Cmndv[100][32],  /* user command; changed to 100 from 5, 2/7/99, NM */
     Argv[10][32];  /* copy of argv from main() */

int Cmndc,  /* number of arguments in Cmndv */
    InterconType,  /* type of processor-memory interconnect */
    Interactive,  /* 1 if doing interactive debugging, 0 if doing
                     straight run */
    PauseDispP = 0,  /* number of processors to display at each pause */
    *PauseDispPNum,  /* the j-th one gives the number of the
                                j-th processor to display at each pause */
    LastList = 0,  /* last line listed in most recent `l' command */
    NHalted = 0,  /* number of CPUs currently halted */
    Stop = 0,  /* see description of `eh' command above */
    Argc;  /* copy of argc from main() */


/* used for the crossbar and possibly some user-defined interconnects: */
MemAccPtr *MemInHead,  /* queue of messages from CPUs to memory
                                  modules */
          *MemInTail,
          *MemOutHead,  /* queue of messages from memory modules
                                   to CPUs */
          *MemOutTail,
          NewMemRequestsHead,  /* new requests which must now be added
                                  to MemIn */
          NewMemRequestsTail;

/* used for the crossbar and omega interconnects */
RouterPtr	**Routers,	/* number of routers in system */
				*PE;		/* the PE's in the system */

/* used for omega */
int 	Stages,  		/*number of stages in the network */
		NodesPerStage;	/* number of routers in each stage */


/* for the snoopy-update MAF */
int NoWritesYet;  /* equal to 1 if no writes have been done yet
                     in this cycle */


/* for the snoopy-invalidate MAF */
char *StatePtr;
unsigned BlockXferTimeLeft = 0,  /* number of cycles left for the current
                                    block transfer */
         *ExclPNum,   /* ExclPNum[B] is the number of the processor, if any
                         which is in Exclusive state for block B (equal to
		         -1 if none) */
         NBlocks,  /* total number of blocks in MulSim's memory */
	 BlockSize,  /* number of words in one memory block */
	 NoBusUseFoundYet,  /* equal to 1 if the bus has not yet been used
	                  in this cycle */
         StartPNum = -1;  /* number of CPU currently having the first
                             priority for bus usage */
int InvalPNum = -1,  /* number of the Invalid-state processor to which a
                        block xfer is being done */
    XferBNum = -1;  /* block number for which a xfer is being done */


Init()

{  int OutFD,SymFD,I,i,j;  float FTmp;

   strcpy(ProgPrefix,Argv[0]);
   strcpy(ProgFileName,ProgPrefix);
   strcat(ProgFileName,".out");
   OutFD = open(ProgFileName,O_RDONLY|O_BINARY);
   if (OutFD == -1)  {
      printf("couldn't open %s\n",ProgFileName);
      exit();
   }
   NInstrs = read(OutFD,(char *) Instr,
      MaxNInstrs*sizeof(struct InstrStruct));
   NInstrs /= sizeof(struct InstrStruct);
   strcpy(SymFileName,ProgPrefix);
   strcat(SymFileName,".sym");
   SymFD = open(SymFileName,O_RDONLY|O_BINARY); if (SymFD == -1)  {
      printf("couldn't open %s\n",SymFileName);
      exit();
   }
   NDataSyms = read(SymFD,(char *) SymTable,
      MaxSyms*sizeof(struct TableEntry));
   NDataSyms /= sizeof(struct TableEntry);
   NCPUs = atoi(Argv[1]);
   FTmp = (float)((unsigned) -1);
   if ((float) NCPUs*sizeof(struct CPUStruct) > FTmp)  {
          printf("too many simulated CPUs\n");
	  exit();
   }
   CPU = (struct CPUStruct *) malloc(NCPUs*sizeof(struct CPUStruct));
   if (!CPU)  {
      printf("too many simulated CPUs\n");
      exit();
   }
   MemSize = SymTable[NDataSyms-1].Location;
   Mem = (unsigned *) malloc(MemSize*sizeof(unsigned));
   if (!Mem)  {
      printf("too much simulated memory\n");
      exit();
   }
   MemSet(Mem,0,MemSize*sizeof(unsigned));
   MemInHead = (MemAccPtr *) malloc(NCPUs*sizeof(MemAccPtr));
   CheckMallocError((void *) MemInHead,"MemInHead");
   MemInTail = (MemAccPtr *) malloc(NCPUs*sizeof(MemAccPtr));
   CheckMallocError((void *) MemInTail,"MemInTail");
   MemOutHead = (MemAccPtr *) malloc(NCPUs*sizeof(MemAccPtr));
   CheckMallocError((void *) MemOutHead,"MemOutHead");
   MemOutTail = (MemAccPtr *) malloc(NCPUs*sizeof(MemAccPtr));
   CheckMallocError((void *) MemOutTail,"MemOutTail");
   for (I = 0; I < NCPUs; I++)
      MemInHead[I] = MemOutHead[I] = MemInTail[I] = MemInTail[I] = 0;
   NewMemRequestsHead = NewMemRequestsTail = 0;
   PauseDispPNum = (int *) malloc(NCPUs*sizeof(int));
   CheckMallocError((void *) PauseDispPNum,"PauseDispPNum");
   MemSet(CPU,0,NCPUs*sizeof(struct CPUStruct));
   Interactive = (Argv[2][0] == 'i');
   switch (Argv[3][0])  {
      case 'u': InterconType = USER; break;
      case 'p': InterconType = PRAM; break;
      case 'b': InterconType = BUS; break;
      case 'x': InterconType = XBAR;
                Routers = (RouterPtr **) malloc(NCPUs*sizeof(RouterPtr *));
                CheckMallocError((void *) Routers,"Routers");
                PE = (RouterPtr *) malloc(NCPUs*sizeof(RouterPtr));
                CheckMallocError((void *) PE,"PE");
                for(i=0;i<NCPUs;i++){
                  PE[i] = (RouterPtr) malloc(sizeof(struct RouterStruct));
                  PE[i]->LoutHead = PE[i]->LoutTail = 0;
                  PE[i]->RoutHead = PE[i]->RoutTail = 0;
                  PE[i]->ToutHead = PE[i]->ToutTail = 0;
                  PE[i]->BoutHead = PE[i]->BoutTail = 0;
                  Routers[i] = (RouterPtr *) malloc(NCPUs*sizeof(RouterPtr));
                  CheckMallocError((void *) Routers[i],"Routers[X]");
                  for(j=0;j<NCPUs;j++){
                    Routers[i][j] = (RouterPtr) malloc(sizeof(struct RouterStruct));
                    Routers[i][j]->LoutHead = Routers[i][j]->LoutTail = 0;
                    Routers[i][j]->RoutHead = Routers[i][j]->RoutTail = 0;
                    Routers[i][j]->ToutHead = Routers[i][j]->ToutTail = 0;
                    Routers[i][j]->BoutHead = Routers[i][j]->BoutTail = 0;
                  }
                }
                break;
      case 'o': InterconType = OMEGA;
                NodesPerStage=1;
                for(Stages=1;(NodesPerStage*2)<NCPUs;Stages++) NodesPerStage*=2;
                Routers = (RouterPtr **) malloc(Stages*sizeof(RouterPtr *));
                CheckMallocError((void *) Routers,"Routers");
                PE = (RouterPtr *) malloc(NodesPerStage*2*sizeof(RouterPtr));
                CheckMallocError((void *) PE,"PE");
                for(i=0;i<NodesPerStage*2;i++){
                  PE[i] = (RouterPtr) malloc(sizeof(struct RouterStruct));
                  PE[i]->LoutHead = PE[i]->LoutTail = 0;
                  PE[i]->RoutHead = PE[i]->RoutTail = 0;
                  PE[i]->ToutHead = PE[i]->ToutTail = 0;
                  PE[i]->BoutHead = PE[i]->BoutTail = 0;
                }
                for(i=0;i<Stages;i++){
                  Routers[i] = (RouterPtr *) malloc(NodesPerStage*sizeof(RouterPtr));
                  CheckMallocError((void *) Routers[i],"Routers[X]");
                  for(j=0;j<NodesPerStage;j++){
                    Routers[i][j] = (RouterPtr) malloc(sizeof(struct RouterStruct));
                    Routers[i][j]->LoutHead = Routers[i][j]->LoutTail = 0;
                    Routers[i][j]->RoutHead = Routers[i][j]->RoutTail = 0;
                    Routers[i][j]->ToutHead = Routers[i][j]->ToutTail = 0;
                    Routers[i][j]->BoutHead = Routers[i][j]->BoutTail = 0;
                  }
                }
                break;
      case 's': switch (Argv[3][1]) {
                   case 'i': InterconType = SNOOPY_INVALIDATE; break;
                   case 'u': InterconType = SNOOPY_UPDATE; break;
		   default: printf("invalid MAF code\n"); exit();
                }
                sscanf(Argv[3]+2,"%d",&BlockSize);
		break;
      default: printf("invalid MAF code\n"); exit();
   }
   UserInit();
}


PrintSrcLine(I)
   int I;

{  if (Instr[I].Label[0]) printf("%s: ",Instr[I].Label);
   printf("%s\n",Instr[I].AsmblSrcLine);
}


DispPStatus(PN)
   int PN;

{  int R; struct CPUStruct *CPtr;

   CPtr = &CPU[PN];
   printf("CPU %d:  PC = %x, Z = %d, N = %d\n",PN,
      CPtr->PC,CPtr->CondCodes[0],CPtr->CondCodes[1]);
   if (CPtr->DispAny)  {
      for (R = 0; R < 32; R++)
         switch (CPtr->Disp[R])  {
	    case 'd': printf("r%d = %ld  ",R,RegValue(PN,R));
	              break;
	    case 'f': printf("r%d = %f  ",R,FloatRegValue(PN,R));
	              break;
	    case 'x': printf("r%d = %lx  ",R,RegValue(PN,R));
	              break;
         }
      printf("\n");
   }
   if (!CPU[PN].MemAccPending)  {
      printf("   next instruction      ");
      PrintSrcLine(CPtr->PC);
   }
   else printf("                         memory access pending\n");
}


int IndivCPUBPReached(int PN)
{  return (CPU[PN].PC == Breakpoint && !CPU[PN].MemAccPending);  }


int BreakpointReached(void)
{  int I;

   if (!AllCPUsBreak) return(IndivCPUBPReached(BreakpointPNum));
   for (I = 0; I < NCPUs; I++)
      if (IndivCPUBPReached(I)) return 1;
   return 0;
}


SetDisp(int PNum, char Cmndv00, char Cmndv01)
{  int I,RegNum;

   for (I = 0; I < Cmndc; I++)  {
      RegNum = atoi(Cmndv[I]);
      if (Cmndv00 =='a')
         CPU[PNum].Disp[RegNum] = Cmndv01;
      else
         CPU[PNum].Disp[RegNum] = 0;
   }
   for (I = 0; I < 32; I++)
      if (CPU[PNum].Disp[I])  {
         CPU[PNum].DispAny = 1;
	 return;
      }
   CPU[PNum].DispAny = 0;
}


PrintHelpSummary()

{  printf("below <p> is either a processor number, or `a' for all CPUs\n");
   printf("adr/afr/axr <p>: add registers at CPU(s) p for display\n");
   printf("od/of/ox: print contents of registers for CPU 0\n");
   printf("b <n> <p>: set breakpoint at instruction n for CPU(s) p\n");
   printf("c: cancel all display-CPU commands\n");
   printf("dr <p>: delete registers to be displayed for CPU(s) p\n");
   printf("eb: execute until hit breakpoint\n");
   printf("eh: execute until all CPUs have halted\n");
   printf("es: execute until UserHook() sets Stop\n");
   printf("et <t>: execute until simulated time t\n");
   printf("ls <n>: list 5 source lines\n");
   printf("md/mf/mx: <s> <b> <e>: print contents of memory s+k, ");
      printf("k=b..e (s a symbol)\n");
   printf("p: add CPUs to be displayed (PC, condition codes, next ");
      printf("instruction) \n");
   printf("s: step through one instruction cycle for all CPUs\n");
   printf("RET: repeat previous command\n");
}


MulSim(argc,argv)
   int argc;  char *argv[];

{  char CmdLine[240],Cmndv00,Cmndv01;  unsigned I,Tmp,PNum,B,E,AllCPUs;
   int J;

   /* first copy the command-line arguments to Argv; in the case of
      argv[0], remove the path name */
   Tmp = strlen(argv[0]) - 1;
   for (J = Tmp; argv[0][J] != '/' && J > 0; J--) ;
   if (!J && argv[0][0] != '/') J--;
   strncpy(Argv[0],&argv[0][J+1],Tmp-J);
   for (I = 1; I < argc; I++) strcpy(Argv[I],argv[I]);  Argc = argc;
   Init();
   if (!Interactive)  {
      OldSimTime = SimTime;
      while (NHalted < NCPUs) StepOneCycle();
      printf("%lu cycles were executed\n",SimTime-OldSimTime);
      UserStat();
      exit();
   }
   while (1)  {
      printf("\n");
      if (PauseDispP)  
         for (I = 0; I < PauseDispP; I++)
            DispPStatus(PauseDispPNum[I]);
      printf("simtime %lu;  enter command: ",SimTime);
      FGetS(CmdLine,stdin);  
      ParseCommand(CmdLine,&Cmndc,Cmndv,32); 
      switch (Cmndv[0][0])  {
         case 'a':
	 case 'd':  Cmndv00 = Cmndv[0][0];
	            Cmndv01 = Cmndv[0][1];
	            if (Cmndv[1][0] == 'a') AllCPUs = 1;
		    else  {
		       AllCPUs = 0;
	               PNum = atoi(Cmndv[1]);
		    }
                    printf("(relative) register numbers?\n");
                    FGetS(CmdLine,stdin);  
                    ParseCommand(CmdLine,&Cmndc,Cmndv,32); 
		    if (AllCPUs)
		       for (PNum = 0; PNum < NCPUs; PNum++)
		          SetDisp(PNum,Cmndv00,Cmndv01);
                    else SetDisp(PNum,Cmndv00,Cmndv01);
                    break;
	 case 'b':  BreakpointOn = 1;
	            sscanf(Cmndv[1],"%x",&Breakpoint);
                    if (Cmndv[2][0] == 'a') AllCPUsBreak = 1;
		    else  {
		       AllCPUsBreak = 0;
	               BreakpointPNum = atoi(Cmndv[2]);
		    }
	            break;
         case 'c':  PauseDispP = 0; 
                    for (PNum = 0; PNum < NCPUs; PNum++)
		       CPU[PNum].DispAny = 0;
	            break;
	 case 'e':  switch (Cmndv[0][1])  {
	               case 'b':  
		          do
                             StepOneCycle();
                          while (!BreakpointReached() && NHalted < NCPUs);
                          break;
		       case 'h': 
		          OldSimTime = SimTime;
		          while (NHalted < NCPUs) StepOneCycle();
		          printf("%lu cycles were executed\n",
                             SimTime-OldSimTime);
                          UserStat();
			  break;
                       case 's':
		          while (!Stop) StepOneCycle();
		          Stop = 0;
                          break;
	               case 't':  
		          StopTime = atoi(Cmndv[1]);
		          do
                             StepOneCycle();
                          while (SimTime < StopTime);
                          break;
             
		    }
	            break;
         case 'h':  PrintHelpSummary(); break;
         case 'l':  if (Cmndc == 2) sscanf(Cmndv[1],"%x",&LastList);
		    LastList--;
	            for (I = 0; I < 5; I++)  {
		       printf("%x  ",++LastList);
		       PrintSrcLine(LastList); 
		    }
		    break;
         case 'm':  Tmp = SymLookup(Cmndv[1],NDataSyms);
	            B = atoi(Cmndv[2]);
		    E = Tmp + atoi(Cmndv[3]);
                    Tmp += B;
		    while (Tmp <= E) 
		       switch (Cmndv[0][1])  {
		          case 'd': printf("%ld  ",Mem[Tmp++]); 
			            break;
                          case 'f': printf("%f  ",FloatValue(Mem[Tmp++]));
			            break;
		          case 'x': printf("%lx  ",Mem[Tmp++]); 
			            break;
                       }
		    printf("\n");
		    break;
				/* output register value .kdr*/
	 case 'o':  
	   Cmndv00 = Cmndv[0][0];
	   Cmndv01 = Cmndv[0][1];
	   printf("register numbers?\n");
	   FGetS(CmdLine,stdin);  
	   ParseCommand(CmdLine,&Cmndc,Cmndv,32); 
	   for (I = 0; I < Cmndc; I++)  {
	      int R = atoi(Cmndv[I]);
	      switch (Cmndv01) {
	       case 'd': printf("r%d = %ld  ",R,RegValue(0,R));
		 break;
	       case 'f': printf("r%d = %.32f  ",R,FloatRegValue(0,R));
		 break;
	       case 'x': printf("r%d = %lx  ",R,RegValue(0,R));
		 break;
	      }
	   }
	   break;

         case 'p':  printf("processor numbers?\n");
                    FGetS(CmdLine,stdin);  
                    ParseCommand(CmdLine,&Cmndc,Cmndv,32); 
                    for (I = 0; I < Cmndc; I++)  {
                       PNum = atoi(Cmndv[I]);
	               MemSet(CPU[PNum].Disp,0,32*sizeof(int));
                       PauseDispPNum[PauseDispP++] = PNum;
		    }
                    break;
	 case 's':  StepOneCycle(); break;
	 case 'q':  exit();
	 default:  printf("illegal MulSim command:  %s\n",Cmndv[0]);
      }
   }
}


