

/*****************************************************************/
/*                                                               */
/*                 Machine Language Simulator                    */
/*                        Version 3.0                            */
/*                                                               */
/*                   by J. Glenn Brookshear                      */
/*                      Marquette University                     */
/*                                                               */
/*         Simulates the machine described in Appendix C         */
/*         of the text Computer Science: An Overview.            */
/*         Includes op-codes D and E (indirect addressing)       */
/*         as described in Chapter 7.                            */
/*                                                               */
/*                Last update: March 8, 2002                     */
/*                                                               */
/*****************************************************************/


#define ANSI 1          /* Set ANSI to 1 if this program is    */
                        /* to be compiled on an ANSI C system. */
                        /* Set it to 0 if a (non ANSI) UNIX    */
                        /* system is to be used.               */
                        /* When the UNIX option is used, an    */
                        /* empty file named SaveFile must be   */
                        /* provided if no other SaveFile is    */
                        /* present.                            */
                        /* This flag is used in deciding how   */
                        /* to handle the deletion and renaming */
                        /* of files in the functions named     */
                        /* save_program and delete_program.    */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define true  1
#define false 0

char buf[80];                   /* buffer for input from keyboard */
long memory[16][16] = {0};      /* main memory                    */
long reg[16] = {0};             /* registers                      */
long pc = 0;                    /* program counter                */
long instr_reg = 0;             /* instruction register           */

long op_code, operand_1, x, y;  /* instruction components         */

long gethex();                  /* utility function for reading   */
    /* hexadecimal values--avoids     */
    /* use of scanf().                */

void identify();                /* identifies program on user     */
    /* screen when program starts.    */

void help();                    /* functions for menu items       */
void display();
void change_mem();
void change_reg();
void change_pc();
void clear();
void single_step();
void run_to_halt();
void process_file();

void retrieve_program();       /* file manipulating functions    */
void save_program();
void delete_program();
void list_programs();

void fetch();                  /* steps in machine cycle         */
void decode();
int execute();

void add_float();              /* machine language instructions  */
void or();
void and();
void exclus_or();
void rotate();
void jump();


/*******************************************************/
/*                                                     */
/*       main() displays the memory and register       */
/*       contents on the screen and waits for          */
/*       input from the keyboard.  At that time        */
/*       it calls the appropriate function to          */
/*       perform the requested action.                 */
/*                                                     */
/*******************************************************/

void main()
{ identify();
  display();
  do
  { gets(buf);
    switch(buf[0])
    { case 'M':case 'm': change_mem(); display(); break;
      case 'R':case 'r': change_reg(); display(); break;
      case 'P':case 'p': change_pc(); display(); break;
      case 'S':case 's': single_step(); display(); break;
      case 'G':case 'g': run_to_halt(); display(); break;
      case 'C':case 'c': clear(); display(); break;
      case 'F':case 'f': process_file(); display(); break;
      case 'H':case 'h':case '?': help(); display(); break;
      case 'Q':case 'q': break;
    }
  } while (buf[0] != 'Q' && buf[0] != 'q');
}


/*******************************************************/
/*                                                     */
/*  indentify() displays the program's identification  */
/*  on the user's screen when the program begins.      */
/*                                                     */
/*******************************************************/

void identify()
{long i, j;
 printf("\n\n\n\n\n\n\n\n\n");
 printf("
***************************************************\n");
 printf("           *
*\n");
 printf("           *               Machine Simulator
*\n");
 printf("           *                  version 3.0
*\n");
 printf("           *
*\n");
 printf("           *  Simulates the machine described in Appendix C
*\n");
 printf("           *  of the text Computer Science: An Overview.
*\n");
 printf("           *
*\n");
 printf("
***************************************************\n");
 printf("\n\n\n\n\n\n\n\n");
 for (i = 0; i < 65000; i++)
   for (j = 0; j < 200; j++)
     i = i;
}


/*******************************************************/
/*                                                     */
/*    help() presents an explanation of the options    */
/*    on the main display menu.                        */
/*                                                     */
/*******************************************************/

void help()
{printf("\n\n\nOptions are as follows:\n\n");
 printf("     M  Change contents of memory cells.\n");
 printf("     R  Change contents of registers.\n");
 printf("     P  Change contents of program counter.\n");
 printf("     C  Clear memory or registers.  (Options will be
given.)\n");
 printf("     S  Single step. (Execute a single machine cycle.)\n");
 printf("     G  Go. (Execute until a halt instruction is executed\n");
 printf("             or until 500 machine cycles have been
executed.)\n");
 printf("     F  Obtain list of file options ");
 printf("(to save or retrieve programs).\n");
 printf("     H  Display this help screen.\n");
 printf("     Q  Terminate this simulation program.\n\n\n");
 printf("\n**Type <Enter> to return to main display. ");
 gets(buf);
 printf("\n");
}


/*******************************************************/
/*                                                     */
/*       display() displays the machine state          */
/*       and selection menu on the screen.             */
/*                                                     */
/*******************************************************/

void display()
{ int i, j;
  printf("\n\n                         Main Memory\n\n");
  for(i=0; i!=16; i++)
  { printf(" "); printf("  %X",i);
  }
  for(i=0; i!=16; i++)
   { printf("\n");
    printf("%1X",i);
    for(j=0; j!=16; j++)
     if(memory[i][j] <= 15)            /* Allow for leading 0s */
          printf(" 0%X ",memory[i][j]);
       else
          printf(" %X ",memory[i][j]);
  }
 printf("\n\n");
 for(i=0; i<16; i++)
 { if(i==8) {printf("   PC: ");
      if (pc <= 15)
          printf("0%X\n", pc);
        else
          printf("%X\n", pc);
            }
   printf("R%X:",i);
   if(reg[i] <= 15)
       printf("0%X  ", reg[i]);
     else
       printf("%X  ", reg[i]);
  }
  if (instr_reg == 0) printf("   IR: 0000\n");
     else printf("   IR: %X\n", instr_reg);
  printf("\nType one of the following (H for help): ");
  printf("M, R, P, C, S, G, F, Q: ");
}


/*******************************************************/
/*                                                     */
/*    gethex() is used to read hexadecimal values      */
/*    from the keyboard to avoid the lack of           */
/*    control provided by scanf.                       */
/*    gethex() returns the value received when a       */
/*    valid hexadecimal value is typed.  Otherwise     */
/*    it returns a -1 if only a RETURN was typed       */
/*    or a -2 if any nonhexadecimal characters were    */
/*    typed.                                           */
/*                                                     */
/*******************************************************/

long gethex()
{long input = 0;
 long return_value;
 int valid = true;
 int i = 0;
 gets(buf);
 while (buf[i] != '\0')
   {switch (buf[i])
     {case '0': input = input * 16 + 0; break;
      case '1': input = input * 16 + 1; break;
      case '2': input = input * 16 + 2; break;
      case '3': input = input * 16 + 3; break;
      case '4': input = input * 16 + 4; break;
      case '5': input = input * 16 + 5; break;
      case '6': input = input * 16 + 6; break;
      case '7': input = input * 16 + 7; break;
      case '8': input = input * 16 + 8; break;
      case '9': input = input * 16 + 9; break;
      case 'A':case 'a': input = input * 16 + 10; break;
      case 'B':case 'b': input = input * 16 + 11; break;
      case 'C':case 'c': input = input * 16 + 12; break;
      case 'D':case 'd': input = input * 16 + 13; break;
      case 'E':case 'e': input = input * 16 + 14; break;
      case 'F':case 'f': input = input * 16 + 15; break;
      default: valid = false; break;
     }
    i++;
   }
   if (valid == true && i != 0)   /* Valid hexadecimal value */
     return_value = input;
   if (valid == true && i == 0)   /* Only a RETURN was typed */
     return_value = -1;
   if (valid == false)            /* Invalid input received  */
     return_value = -2;
   return(return_value);
}


/*******************************************************/
/*                                                     */
/*     change_mem() is used to change the contents     */
/*     of consecutive memory cells.                    */
/*                                                     */
/*******************************************************/

void change_mem()
{long address, input;
 int a, b;
   do
     {printf("\n\nAddress of the cell to be changed (hexadecimal): ");
      address = gethex();
     } while(address < 0 || address > 255);
   b = address % 16;
   a = address/16;
   printf("\n\n      Cell      Current        New\n");
   printf("     Address    contents     contents\n");
   do
     {printf("       %X%X          ", a, b);
      if (memory[a][b] <= 15) printf("0%X           ", memory[a][b]);
  else printf("%X          ", memory[a][b]);
      input = gethex();
      if (0 <= input)
        if (input < 256)
    {memory[a][b] = input;
     if (b ==15) {b = 0; a++;}
       else b++;
     a = a % 16;
    }
  else
    {printf("\nInput value too large");
     printf("\n(Type <Enter> to return to main display.)");
    }
      if (input == -2)
   {printf("\n**Illegal input**");
    printf("\n(Type <Enter> to return to main display.");
   }
      printf("\n");
     }  while (input != -1);
}


/*******************************************************/
/*                                                     */
/*          change_reg() is used to change             */
/*          the contents of a register.                */
/*                                                     */
/*******************************************************/

void change_reg()
{  long reg_num, value;
   char repeat = 'y';
   while(repeat == 'y' || repeat == 'Y')
     {do
        {printf("\nRegister to change: ");
  reg_num = gethex();
  if (reg_num < 0 || reg_num > 15)
    printf("\n**Illegal register number**");
 } while(reg_num < 0 || reg_num > 15);
      do
 {printf("\nValue of register %X is: ", reg_num);
  printf("%X\n", reg[reg_num]);
  printf("New value for register %X: ", reg_num);
  value = gethex();
  if (value < 0 || value > 255)
    printf("\n***Illegal register value***");
 } while(value < 0 || value > 255);
      reg[reg_num] = value;
      printf("\nChange another register? (y/n): ");
      gets(buf);
      repeat = buf[0];
     }
   printf("\n");
}


/*******************************************************/
/*                                                     */
/*     change_pc() is used to change the value of      */
/*     the program counter.                            */
/*                                                     */
/*******************************************************/

void change_pc()
{do
   {printf("\nNew value of the program counter: ");
    pc = gethex();
    if (pc < 0 || pc > 255)
      printf("\n**Invalid value for program counter**");
   } while(pc < 0 || pc > 255);
 printf("\n");
}


/*******************************************************/
/*                                                     */
/*      clear() puts 0s in all memory cells or all     */
/*      registers depending on the selection from      */
/*      the keyboard.  An invalid input from the       */
/*      keyboard terminates the function with not      */
/*      side effects.                                  */
/*                                                     */
/*******************************************************/

void clear()
{ int i,j;
  printf("\nClear memory, registers, or both? (M, R, or B): ");
  gets(buf);
  if (buf[0] == 'M' || buf[0] == 'm')
     {for (i=0; i < 16; i++)
       for (j=0; j < 16; j++)
         memory[i][j] = 0;
     }
  if (buf[0] == 'R' || buf[0] == 'r')
     {for (i=0; i < 16; i++)
       reg[i] = 0;
     }
  if (buf[0] == 'B' || buf[0] == 'b')
     {for (i=0; i < 16; i++)
       for (j=0; j < 16; j++)
         memory[i][j] = 0;
      for (i=0; i < 16; i++)
       reg[i] = 0;
     }
  printf("\n");
}


/******************************************************/
/*                                                    */
/*    process_file() presents a menu of the file      */
/*    processing options and branches to the          */
/*    option selected.                                */
/*                                                    */
/******************************************************/

void process_file()
{printf("\n\nRetrieve a program, save a program, ");
 printf("\ndelete a program, or list available programs? ");
 printf("\n(R, S, D, or L): ");
 gets(buf);
 switch (buf[0])
  {case 'R':case 'r': retrieve_program(); printf("\n"); break;
   case 'S':case 's': save_program(); break;
   case 'D':case 'd': delete_program(); break;
   case 'L':case 'l': list_programs(); break;
   default: printf("\n"); break;
  }
}


/*******************************************************/
/*                                                     */
/*     retrieve_program() retrieves the selected       */
/*     machine language program (memory image)         */
/*     from the file "SaveFile" and loads it into      */
/*     the simulated memory.                           */
/*                                                     */
/*******************************************************/

void retrieve_program()
{ FILE *in_file;
  char input[80];
  int i, j;
  long temp;
  int found = false;
  printf("\nType the name of the program to be retrieved: ");
  gets(buf);
  if ((in_file = fopen("SaveFile", "r")) == NULL)
    {printf("\nProgram file not available");
     printf("\nType RETURN to continue: ");
     gets(buf);
     printf("\n");
    }
  else
    {while ((fscanf(in_file,"%s", &input) != EOF) && found == false)
      if (strcmp(buf, input) == 0)
 {found = true;
  for (i = 0; i < 16; i++)
   for (j = 0; j < 16; j++)
     fscanf(in_file, "%d ", &memory[i][j]);
 }
      else
 for (i = 0; i < 256; i++)
   fscanf(in_file, "%d ", &temp);
    }
  fclose(in_file);
  if (found == false)
    {printf("\n\nProgram %s not found\n", buf);
     printf("Type RETURN to continue: ");
     gets(buf);
    }
}


/*******************************************************/
/*                                                     */
/*    save_program() places a program (memory image)   */
/*    in the file "SaveFile" under the name selected.  */
/*                                                     */
/*******************************************************/

void save_program()
{FILE *out_file;
 FILE *in_file;
 int i, j;
 int saved = false;
 long temp;
 char new_prog[80];
 printf("\n\nEnter the name of the program to be saved: ");
 gets(new_prog);
 printf("\n");
 out_file = fopen("NewFile", "w");
 if ((in_file = fopen("SaveFile", "r")) != NULL)
     {while (fscanf(in_file,"%s", &buf) != EOF)
 {if (strcmp(buf, new_prog) == 0)
    {saved = true;
     fprintf(out_file, "%s\n", new_prog);
     for (i = 0; i < 16; i++)
      for (j = 0; j < 16; j++)
        fprintf(out_file, "%d ", memory[i][j]);
     for (i = 0; i < 256; i++)
        fscanf(in_file, "%d ", &temp);
    }
  if ((strcmp(buf, new_prog) < 0) ||
      ((strcmp(buf, new_prog) > 0) && (saved == true)))
    {fprintf(out_file, "%s\n", buf);
     for (i = 0; i < 256; i++)
        {fscanf(in_file, "%d ", &temp);
  fprintf(out_file, "%d ", temp);
        }
            }
  if ((strcmp(buf, new_prog) > 0) && (saved == false))
            {saved = true;
      fprintf(out_file, "%s\n", new_prog);
      for (i = 0; i < 16; i++)
        for (j = 0; j < 16; j++)
   fprintf(out_file, "%d ", memory[i][j]);
             fprintf(out_file, "%s\n", buf);
        for (i = 0; i < 256; i++)
          {fscanf(in_file, "%d ", &temp);
    fprintf(out_file, "%d ", temp);
          }
     }
  }
      fclose(in_file);
     }
 if (saved == false)
   {fprintf(out_file, "%s\n", new_prog);
    for (i = 0; i < 16; i++)
       for (j = 0; j < 16; j++)
  fprintf(out_file, "%d ", memory[i][j]);
   }
 fclose(out_file);
#if (ANSI == 1)
    remove("SaveFile");
    rename("NewFile", "SaveFile");
#else
    system("rm SaveFile");
    system("mv NewFile SaveFile");
#endif
}


/*******************************************************/
/*                                                     */
/*     delete_program() removes a specified program    */
/*     from the file "Save File."                      */
/*                                                     */
/*******************************************************/

void delete_program()
{FILE *out_file;
 FILE *in_file;
 int i;
 long temp;
 char del_prog[80];
 printf("\nEnter the name of the program to be deleted: ");
 gets(del_prog);
 printf("\n");
 out_file = fopen("NewFile", "w");
 if ((in_file = fopen("SaveFile", "r")) != NULL)
   {while (fscanf(in_file,"%s", &buf) != EOF)
 {if (strcmp(buf, del_prog) != 0)
    {fprintf(out_file, "%s\n", buf);
     for (i = 0; i < 256; i++)
  {fscanf(in_file, "%d ", &temp);
   fprintf(out_file, "%d ", temp);
  }
    }
  else
    for (i = 0; i < 256; i++)
       fscanf(in_file, "%d ", &temp);
 }
    fclose(in_file);
   }
 fclose(out_file);
#if (ANSI == 1)
   remove("SaveFile");
   rename("NewFile", "SaveFile");
#else
   system("rm SaveFile");
   system("mv NewFile SaveFile");
#endif
}


/*******************************************************/
/*                                                     */
/*    list_programs() presents a listing of the        */
/*    names of those programs stored in the file       */
/*    "SaveFile."                                      */
/*                                                     */
/*******************************************************/

void list_programs()
{ FILE *in_file;
  char input[80];
  int i, j;
  long count = 0;
  long temp;
  if ((in_file = fopen("SaveFile", "r")) == NULL)
     printf("\n\nNo programs are stored.\n");
  else
    {printf("\n\n\nPrograms available are as follows:\n\n");
     while ((fscanf(in_file, "%s", &input) != EOF))
      {printf("%s\n", input);
       count++;
       for (i = 0; i < 16; i++)
         for (j = 0; j < 16; j++)
    fscanf(in_file, "%d ", &temp);
      }
     if (count == 0) printf("    Program file is empty.\n");
    }
  fclose(in_file);
  printf("\n**Type <Enter> to return to main display.");
  gets(buf);
  printf("\n");
}


/*******************************************************/
/*                                                     */
/*     single_step() executes a single machine         */
/*     instruction and then returns to the main        */
/*     display.                                        */
/*                                                     */
/*******************************************************/

void single_step()
{ fetch();
  decode();
  execute();
}


/*******************************************************/
/*                                                     */
/*     run_to_halt() simulates machine cycles until    */
/*     a halt instruction is executed or until 500     */
/*     machine cycles have been simulated.  In the     */
/*     second case, the option of stopping or          */
/*     continuing for 500 more cycles is given.        */
/*                                                     */
/*******************************************************/

void run_to_halt()
{ char symbol[80];
  int i = 0;
  do
  {  fetch();
     decode();
     if (execute() == 1) break;
     if (i < 500)
       i++;
     else
       {printf("\n\n**Five hundred machine cycles have been
executed**");
 printf("\n\n**Continue executing machine language program? (Y/N) ");
 gets(symbol);
        printf("\n\n");
 if (symbol[0] == 'N' || symbol[0] == 'n')
     break;
 else
     i = 0;
       }
  } while (instr_reg != 0xC000);
}


/*******************************************************/
/*                                                     */
/*    fetch() simulates the fecth  part of the         */
/*    machine cycle.                                   */
/*                                                     */
/*******************************************************/

void fetch()
{ int a,b;
  b = pc % 16;
  a = pc/16;
  instr_reg = (long) memory[a][b];
  instr_reg *= 256;
  pc = (pc + 1) % 256;
  b = pc % 16;
  a = pc/16;
  instr_reg += (long) memory[a][b];
    pc = (pc + 1) % 256;
}


/*******************************************************/
/*                                                     */
/*      decode() simulates the decode part of the      */
/*      machine cycle.                                 */
/*                                                     */
/*******************************************************/

void decode()
{ long temp = instr_reg;
  y = temp % 16;
  temp = temp/16;
  x = temp % 16;
  temp = temp/16;
  operand_1 = temp % 16;
  op_code = temp/16;
}


/*******************************************************/
/*                                                     */
/*     execute() oversees the simulation of the        */
/*     execution part of the machine cycle.            */
/*                                                     */
/*******************************************************/

int execute()
{switch(op_code)
  { case 1: reg[operand_1] = memory[x][y]; break;     /* load from mem
*/
    case 2: reg[operand_1] = (x*16) + y; break;       /* load immediate
*/
    case 3: memory[x][y] = reg[operand_1]; break;     /* store
*/
    case 4: reg[y] = reg[x]; break;                   /* move
*/
    case 5: reg[operand_1] = (reg[x] + reg[y]) % 256; break;
    case 6: add_float(); break;
    case 7: reg[operand_1] = reg[x] | reg[y]; break;  /* or
*/
    case 8: reg[operand_1] = reg[x] & reg[y]; break;  /* and
*/
    case 9: reg[operand_1] = reg[x] ^ reg[y]; break;  /* exclusive or
*/
    case 10: rotate(); break;
    case 11: jump(); break;
    case 12: break;
    case 13: reg[operand_1] = memory[reg[y]/16][reg[y]%16]; /* load
indirect */
      break;
    case 14: memory[reg[y]/16][reg[y]%16] = reg[operand_1]; /* store
indirect */
      break;
    default: printf("\n\n\n");
      printf("*** Invalid instruction in instruction register ***");
      printf("\n\n\n");
      printf("Type RETURN to continue: ");
      gets(buf);
      printf("\n");
      return(1);
  }
 return(0);
}


/*******************************************************/
/*                                                     */
/*     add_float() simulates the addition of two       */
/*     values stored in floating-point notation.       */
/*                                                     */
/*******************************************************/

void add_float()
{ int sign1, sign2, exp1, exp2;
  long mantissa1, mantissa2;
  int answer, exponent = 4, sign_ans = 0;

  mantissa1 = reg[x]%16; mantissa2 = reg[y]%16; /* separate into
components */
  sign1 = reg[x]/128; sign2 = (int) reg[y]/128;
  exp1 = reg[x]/16; exp1 = (exp1 % 8) - 4;
  exp2 = reg[y]/16; exp2 = (exp2 % 8) - 4;

  mantissa1 = mantissa1 << 4;               /* Get ready to align
mantissas */
  mantissa2 = mantissa2 << 4;

  if (exp1 > 0)
      mantissa1 = mantissa1 << exp1;        /* Align
mantissas              */
    else
      mantissa1 = mantissa1 >> (-1 * exp1);
  if (exp2 > 0)
      mantissa2 = mantissa2 << exp2;
    else
      mantissa2 = mantissa2 >> (-1 * exp2);

  if (sign1 == 1)                           /* Negate mantissas
that       */
     mantissa1 *= -1;                       /* represent negative
values,  */
  if (sign2 == 1)
     mantissa2 *= -1;
  answer = mantissa1 + mantissa2;           /* and then add
them.          */

  if (answer < 0)                           /* Get absolute value
of       */
    {answer *= -1;                          /*
answer.                     */
     sign_ans = 1;
    }

  while((answer > 255 || answer < 128) && answer != 0)
   {if(answer > 255)                        /* Move answer so leftmost
1   */
      {answer = answer >> 1;                /* is on 8th bit (to
normalize */
       exponent++;                          /*
answer).                    */
      }
    else
      {answer = answer << 1;
       exponent--;
      }
   }

 answer = answer >> 4;            /* Align answer in rightmost four
bits.  */

 if(answer == 0)                  /* Make entire pattern 0 if answer is
0. */
  {exponent = 0;
   sign_ans = 0;
  }

if (sign_ans == 1)              /* Add 1 to 8th bit if answer is
negative. */
   answer = answer + 128;

 exponent = exponent << 4;        /* Insert exponent into
answer.            */
 answer = answer + exponent;

 reg[operand_1] = answer;
}


/*******************************************************/
/*                                                     */
/*   rotate() rotates the contents of the register     */
/*   indicated by operand_1 by one bit y times.        */
/*                                                     */
/*******************************************************/

void rotate()
{ int i, p;
  for(i=1; i<=y; i++)
  {p = reg[operand_1] % 2;
   p = p << 7;
   reg[operand_1] = reg[operand_1] / 2;
   reg[operand_1] = reg[operand_1] | p;
  }
}


/*******************************************************/
/*                                                     */
/*   jump() simulates the machine language branch      */
/*   instruction by changing the program counter       */
/*   if reg[operand_1] equals reg[0].                  */
/*                                                     */
/*******************************************************/

void jump()
{ if(reg[operand_1] == reg[0])
   pc = (x*16) + y;
}


/*******************************************************/
/*                                                     */
/*                That's all folks!                    */
/*                                                     */
/*******************************************************/





