import java.util.*;
import java.io.*;

/**
   Instances of this class represent console user interfaces to a
   simulated computer equipped with a Micro-1 processor.
*/
public class Console 
{   /**
       Representation of the keyboard
    */
   private Scanner kbd = new Scanner(System.in);
   /**
      Main memory of the simulated computer
   */
   private Memory memory;
   /**
      Processor of the simulated computer
   */
   private Processor cpu;

    /**
       Constructs a memory with specified number of cells,
       and constructs an associated processor.
       @param cap  the sepcified amount of memory
    */
   public Console(int cap) 
   {  memory = new Memory(cap);
      cpu = new Processor();
      cpu.setMemory(memory);
   }

    /**
       Constructs a processor and a memory with 256 cells
    */
   public Console()
   {  this(256);
   }

    /**
       Loads instructions stored in fName into memory as 
       hexadecimal numbers starting at address 0. Resets PC to 0.
       @param fName the name of a file containing hex numbers
    */
   public void load(String fName) 
   {  try 
   	  {  File f = new File(fName);
         Scanner scan  = new Scanner(f);
         int address = 0;
         while(scan.hasNext()) 
         {  if(scan.hasNextInt())
        	   memory.write(address++, scan.nextInt(16));
         	else
         	{  memory.write(address++,  convert(scan));
         	}
         }
         cpu.setPC(0);
      } catch(Exception e) 
      	{ System.out.println(e);
     }  }
   
   	/**
   	   Reads the next line in the file and converts it from Micro-1 assembly 
   	   language to hexadecimal machine language returning the result. 
   	   @param s = The scanner object set to the current file.
   	   @return the hexadecimal representation of the current instruction.
   	*/
    public int convert(Scanner s)
	{  int iRt=0;
	   String sCom = s.next();
	   if(sCom.contains("loadc"))
	   {	iRt = (0x20 + Integer.parseInt(s.next())) << 4;
  			return iRt;
	   }
	   else if(sCom.contains("load"))
		   iRt = 0x1;
	   else if(sCom.contains("store"))
		   iRt = 0x3;
	   else if(sCom.contains("add"))
		   iRt = 0x4;
	   else if(sCom.contains("mul"))
		   iRt = 0x5;
	   else if(sCom.contains("sub"))
		   iRt = 0x6;
	   else if(sCom.contains("div"))
		   iRt = 0x7;
	   else if(sCom.contains("and"))
		   iRt = 0x8;
	   else if(sCom.contains("or"))
		   iRt = 0x9;
	   else if(sCom.contains("not"))
		   iRt = 0xa;
	   else if(sCom.contains("lshift"))
		   iRt = 0xb;
	   else if(sCom.contains("rshift"))
		   iRt = 0xc;
	   else if(sCom.contains("bwc"))
		   iRt = 0xd;
	   else if(sCom.contains("bwd"))
		   iRt = 0xe;
	   else if(sCom.contains("if"))
		   iRt = 0xf;
	   else if(sCom.contains("halt"))		   
	       return 0x000;
	   else
		   return Integer.parseInt(sCom,16);
	   for(int x=0; x<2; x++)
	   {	iRt = iRt<<4;
	   		iRt+= Integer.parseInt(s.next());
	   }
	   return iRt;
   }

    /**
       Displays synopsis of all commands in the console window
    */
   public void help()
   {  System.out.println("load fileName \t loads hex memory image into memory");
      System.out.println("memory \t\t dumps memory to console");
      System.out.println("registers \t dumps registers to console");
      System.out.println("step N \t\t executes next N instructions or until halt");
      System.out.println("help \t\t displays this message");
      System.out.println("quit \t\t terminate console");
   }
    /**
       This is the read-execute-print loop for the console. It perpetually
         1) displays a prompt
         2) reads a command from the keyboard
         3) executes the command
         4) displays the result
       Commands include quit, help, load (a program from a file),
       memory (display contents of memory), registers (display
       contents of registers), and step N (execute the next N
       instructions.
    */
   public void controlLoop()
   {  System.out.println("type \"help\" for commands");
      while(true)
      {  System.out.print("-> ");
         String cmmd = kbd.next();
         if (cmmd.equals("quit")) break;
         else if (cmmd.equals("help")) help();
         else if (cmmd.equals("load")) 
         {	load(kbd.next());
            System.out.println("done");
         }
         else if (cmmd.equals("memory")) memory.dump();
         else if (cmmd.equals("registers")) cpu.dump();
         else if (cmmd.equals("step"))
         {  int num;
            if (kbd.hasNextInt())
               num = kbd.nextInt();
            else
            {  num = 0;
               kbd.nextLine();
               System.out.println("invalid number of steps");
            }
            for(int i = 0; i < num; i++) 
            {  boolean halt = cpu.step();
               if (halt) 
               {  System.out.println("program terminated");
                  break;
         }  }   }  
         else
         {  System.out.println("unrecognized command: " + cmmd);
            if (kbd.hasNext()) kbd.nextLine();
     }   }
     System.out.println("bye");
   }

    /**
       Creates a console (with memory and CPU), then starts the
       console's control loop.
    */
   public static void main(String[] args)
   {   Console console = new Console();
      console.controlLoop();
   }
}
