//SML.java
//Programmed by Mr Zhihua Lai, 2004.4.10~4.11
//Platform: Bluepoint Linux 2.2.16 & later Windows 98
//Java 1.3 & later Java 1.4
//Code is free, please keep the header

import java.io.*;  /* for Exception */

public class SML
{
/*
  SML code constants
*/
  final static int READ=10;
  final static int WRITE=11;
  final static int LOAD=20;
  final static int STORE=21;
  final static int ADD=30;
  final static int SUBTRACT=31;
  final static int DIVIDE=32;
  final static int MULTIPLY=33;
  final static int BRANCH=40;
  final static int BRANCHNEG=41;
  final static int BRANCHZERO=42;
  final static int HALT=43;
  final static int NOP=0;
/*
  error code;
*/  
  final static int IMPROPERLOCATION=-1;
  final static int DIVIDEDBYZERO=-2;
  final static int INPUTERROR=-3;
/*
  command code (to shorten program);
*/
  final static int RUN=1;
  final static int LIST=2;
  final static int CLEAR=3;
  final static int MODIFY=4;
  final static int EDIT=5;
/*
  variables in running  
*/  
  final static int MaxProgramSize=100;
  static int memory[]=new int[MaxProgramSize];
  static int accumulator=0;
  static int instructionCounter=0;
  static int endLocation=-1;
  static boolean flag=false;
  
/*  Message Output Section */
  public static void ver()
  {
    System.out.println("Simpletron Machine Language Version 1.0");
    System.out.println("            programmed by lychee,2004 @");
  }
  public static void help()
  {
    System.out.println("");
    System.out.println("  HELP                    GET HELP");
    System.out.println("  QUIT / EXIT             QUIT PROGRAM");
    System.out.println("  VER                     SHOW VERSION");
    System.out.println("  INSTRUCTIONCOUNTER      SHOW INSTRUCTIONCOUNTER");
    System.out.println("  ACCUMULATOR             SHOW ACCUMULATOR");
    System.out.println("  CLEAR  [NUM] [NUM]      CLEAR PROGRAM");
    System.out.println("  RUN    [NUM] [NUM]      RUN SML");
    System.out.println("  LIST   [NUM] [NUM]      SHOW PROGRAM");
    System.out.println("  EDIT   [NUM] [NUM]      EDIT PROGRAM");
    System.out.println("  MODIFY [NUM] [NUM]      MODIFY LINES");
    System.out.println("");
    System.out.println("  READ=10     WRITE=11       LOAD=20");
    System.out.println("  ADD=30      SUBTRACT=31   STORE=21");
    System.out.println("  DIVIDE=32   MULTIPLY=33   BRANCE=40");
    System.out.println("  BRANCHNEG=41  BRANCHZERO=42   HALT=43");
    System.out.println("");
  }
/* error code dealing section */
  public static void error(String message)
  {
     // Error handle here.
     System.out.println(message);
     flag=false;   
  }
  
  public static void error(int code)
  {
     switch (code)
     {
     	case IMPROPERLOCATION:System.out.print("Location error");break;
     	case DIVIDEDBYZERO:System.out.print("Divided by zero");break;
     	case INPUTERROR:System.out.print("Input error");break;
     	default:System.out.print("Error");break;
     }
     flag=false;
     System.out.println(" (".concat(Integer.toString(code)).concat(")"));
  }
/* to update the endOfLocation of program */
  public static void updateEndLocation(int t)
  {
    if (t>endLocation) endLocation=t;
  }
/* clear the program section */  
  public static void clear(int i,int j)
  {
    if (i<0) i=0;
    if (j<i) return;
    int t;
    if (j>MaxProgramSize-1) j=MaxProgramSize-1;
    for (t=i;t<=j;t++)
      memory[t]=0;
    if ((i==0)&&(j==MaxProgramSize-1)) endLocation=-1;
    if ((j>=endLocation)&&(i<=endLocation)) endLocation=i-1;
  }   
/* Section of listing the program */
  public static void list(int i,int j)
  {
    int t;
    if (i<0) i=0;
    if (j>MaxProgramSize-1) j=MaxProgramSize-1;
    for (t=i;t<=j;t++)
    {
       if (t<10) System.out.print("0");
       System.out.print(Integer.toString(t).concat("   "));
       System.out.println (Integer.toString(memory[t]));
    }
  }
/* Section of editing the program */  
  public static void edit(int i,int j) throws Exception
  {
    int t;
    if (i<0) i=0;
    if (j>MaxProgramSize-1) j=MaxProgramSize-1;
    byte[] commandbyte=new byte[255];
    StringBuffer command=new StringBuffer(255);
    command.setLength(255);
    t=i;
    boolean exitflag=false;
    do
    {
       System.out.print(((t<10)?"0":"").concat(Integer.toString(t).concat("   ")));
       System.in.read(commandbyte);
       command.delete(0,256);
       String temp=new String(commandbyte);
       command.insert(0,temp.substring(0,temp.indexOf("\n")));
       temp=command.toString().trim();
       if (temp.equals("")) exitflag=true;
       else
       {
          try
	  {
             memory[t]=Integer.parseInt(temp);
             if (t>endLocation) endLocation=t;	  
	     updateEndLocation(memory[t]%100);
	     t++;
	  }
	  catch (NumberFormatException ne)
  	  {
             error(INPUTERROR);
	  }
       }	  
    } while ((t<=j)&&(!exitflag));   
  }
/* To get the operand */  
  public static int operand()
  {
    return memory[instructionCounter]%100;
  }
/* To get the operationCode */  
  public static int operationCode()
  {
     return memory[instructionCounter]/100;
  }
/* Turning to a certain location */  
  public static void turnTo(int pc)
  {
    if ((pc<0)||(pc>=MaxProgramSize))
    {
      //Out of range handle here.
      error("Error:out of memory location at ".concat(Integer.toString(instructionCounter)));
    }
    else
    {
       instructionCounter=pc;
    }
  }
  public static void turnToNext()
  {
    turnTo(instructionCounter+1);
  }
/* read and write variable */  
  public static void readVariable(int pc) throws Exception
  {
    int num=0;
    byte[] inInt=new byte[25];
    System.in.read(inInt);
    String t1=new String(inInt);
    String t2=t1.substring(0,t1.indexOf("\n")).trim();
    try
    {
       num=Integer.parseInt(t2); 
    }
    catch (NumberFormatException ne)
    {
       num=0;
       error (INPUTERROR);
    }   
    memory[pc]=num;    
  }
  public static void writeVariable(int Value)
  {
    System.out.println(Integer.toString(Value));
  }
/* run procedures */  
  public static void run(int i,int j) throws Exception
  {
     if ((endLocation==-1)||(i>j)||(i>endLocation)) return;
     instructionCounter=i;
     accumulator=0;
     flag=true;
     do
     {
        switch (operationCode())
        {
           case READ:readVariable(operand());turnToNext();break;
           case WRITE:writeVariable(memory[operand()]);turnToNext();break;
           case LOAD:accumulator=memory[operand()];turnToNext();break;
           case STORE:memory[operand()]=accumulator;turnToNext();break;
           case ADD:accumulator+=memory[operand()];turnToNext();break;
           case SUBTRACT:accumulator-=memory[operand()];turnToNext();break;
           case DIVIDE:if (memory[operand()]==0)
                         error(DIVIDEDBYZERO);
                       else
                         accumulator/=memory[operand()];
		       turnToNext();break;
           case MULTIPLY:accumulator*=memory[operand()];turnToNext();break;
           case BRANCH:turnTo(memory[operand()]);break;
           case BRANCHNEG:if (accumulator<0) turnTo(operand()); else turnToNext();break;
           case BRANCHZERO:if (accumulator==0) turnTo(operand());else turnToNext();break;
           case HALT:flag=false;break;
           default:/* Unknown operationCode Handle */;turnToNext();break;
        }
     } while ((flag)&&(instructionCounter<=j));
  }
/* show two main registers */  
  public static void showAccumulator()
  {
    System.out.println(accumulator);
  }
  public static void showInstructionCounter()
  {
    System.out.println(instructionCounter); 
  }
/* Do commands (LIST RUN CLEAR MODIFY EDIT)*/  
  public static void doCommand(String parameter,int commandCode) throws Exception
  {
     int p=-1,i=-1,j=-1;
     if (parameter.indexOf(" ")==-1)
     {
       try
       {
         p=Integer.parseInt(parameter);
       }
       catch (NumberFormatException ne)
       {
         p=-1;
       }
       if ((p<0)||(p>MaxProgramSize-1))
       {
          error(IMPROPERLOCATION);
          return ;
       }
       switch (commandCode)
       {
       	  case RUN:run (p,endLocation);break;
       	  case CLEAR:clear (p,p);break;
          case LIST:list (p,p);break;
          case EDIT:edit (p,p);break;
          case MODIFY:edit (p,MaxProgramSize-1);break;
       }   
     }
     else
     {
       if (parameter.indexOf(" ")!=parameter.lastIndexOf(" "))
       {
          error(INPUTERROR);
	  return ;
       }
       try
       {
         i=Integer.parseInt(parameter.substring(0,parameter.indexOf(" ")));
       }
       catch (NumberFormatException ne)
       {
         i=-1;
       }
       try
       {
         j=Integer.parseInt(parameter.substring(parameter.indexOf(" ")+1));
       }
       catch (NumberFormatException ne)
       {
         j=-1;
       }	 
       if ((i<0)||(i>MaxProgramSize-1)||(j<0)||(j>MaxProgramSize-1))
       {
         error(IMPROPERLOCATION);
	 return;
       }	 
       switch (commandCode)
       {
         case RUN:run(i,j);break;
         case LIST:list(i,j);break;
         case EDIT:edit(i,j);break;
         case MODIFY:edit (i,j);break;
         case CLEAR:clear(i,j);break;
       }
     }
  }
/* main */  
  public static void main(String args[]) throws Exception
  {
    byte[] commandbyte=new byte[255];
    StringBuffer command=new StringBuffer(255);
    command.setLength(255);
    boolean exitflag=false; 
    ver();
    System.out.println("");
    System.out.println("Type help to get help.");
    System.out.println("");
    do
    {
       System.out.print("SML>");
       System.in.read(commandbyte);
       command.delete(0,256);
       String temp=new String(commandbyte);
       command.insert(0,temp.substring(0,temp.indexOf("\n")));
       temp=command.toString().trim().toUpperCase();
       if (temp.equalsIgnoreCase("QUIT")) exitflag=true;
       else if (temp.equalsIgnoreCase("EXIT")) exitflag=true;
       else if (temp.equalsIgnoreCase("VER")) ver();
       else if (temp.equalsIgnoreCase("HELP")) help();
       else if (temp.equalsIgnoreCase("INSTRUCTIONCOUNTER")) showInstructionCounter();
       else if (temp.equalsIgnoreCase("ACCUMULATOR")) showAccumulator();
       else if (temp.startsWith("LIST ")) doCommand(temp.substring(5),LIST);
       else if (temp.startsWith("EDIT ")) doCommand(temp.substring(5),EDIT);
       else if (temp.startsWith("MODIFY ")) doCommand(temp.substring(7),MODIFY);
       else if (temp.startsWith("CLEAR ")) doCommand(temp.substring(6),CLEAR);
       else if (temp.startsWith("RUN ")) doCommand(temp.substring(4),RUN);
       else if (temp.equalsIgnoreCase("RUN")) run(0,endLocation);
       else if (temp.equalsIgnoreCase("LIST")) list(0,endLocation);
       else if (temp.equalsIgnoreCase("EDIT")) edit(0,MaxProgramSize-1);
       else if (temp.equalsIgnoreCase("MODIFY")) edit(0,MaxProgramSize-1);
       else if (temp.equalsIgnoreCase("CLEAR")) clear(0,MaxProgramSize-1);
       else if (!temp.equals("")) error(" Unknown command found.");
    } while (!exitflag);
    // Exit ; 
    System.exit(0);
  }
}
