   package mars;
   import mars.venus.*;
   import mars.util.*;
   import mars.mips.dump.*;
   import mars.mips.hardware.*;
   import mars.simulator.*;
   import java.io.*;
   import java.util.*;
   import java.awt.*;
   import javax.swing.*;
   import javax.swing.JOptionPane;   // KENV 9/8/2004

/*
Copyright (c) 2003-2012,  Pete Sanderson and Kenneth Vollmar

Developed by Pete Sanderson (psanderson@otterbein.edu)
and Kenneth Vollmar (kenvollmar@missouristate.edu)

Permission is hereby granted, free of charge, to any person obtaining 
a copy of this software and associated documentation files (the 
"Software"), to deal in the Software without restriction, including 
without limitation the rights to use, copy, modify, merge, publish, 
distribute, sublicense, and/or sell copies of the Software, and to 
permit persons to whom the Software is furnished to do so, subject 
to the following conditions:

The above copyright notice and this permission notice shall be 
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

(MIT license, http://www.opensource.org/licenses/mit-license.html)
 */

/**
 * Launch the Mars application
 * 
 * @author Pete Sanderson
 * @version December 2009
 **/

   public class MarsLaunch {
   
   /**
    * Main takes a number of command line arguments.<br>
      Usage:  Mars  [options] filename<br>
      Valid options (not case sensitive, separate by spaces) are:<br>
            a  -- assemble only, do not simulate<br>
           ad  -- both a and d<br>
   	  ae<n>  -- terminate MARS with integer exit code <n> if an assemble error occurs.<br>
   	  ascii  -- display memory or register contents interpreted as ASCII
   		   b  -- brief - do not display register/memory address along with contents<br>
   		   d  -- print debugging statements<br>
           da  -- both a and d<br>
           db  -- MIPS delayed branching is enabled.<br>
          dec  -- display memory or register contents in decimal.<br>
         dump  -- dump memory contents to file.  Option has 3 arguments, e.g. <br>
                  <tt>dump &lt;segment&gt; &lt;format&gt; &lt;file&gt;</tt>.  Also supports<br>
                  an address range (see <i>m-n</i> below).  Current supported <br>
                  segments are <tt>.text</tt> and <tt>.data</tt>.  Current supported dump formats <br>
                  are <tt>Binary</tt>, <tt>HexText</tt>, <tt>BinaryText</tt>.<br>
            h  -- display help.  Use by itself and with no filename</br>
          hex  -- display memory or register contents in hexadecimal (default)<br>
           ic  -- display count of MIPS basic instructions 'executed'");
           mc  -- set memory configuration.  Option has 1 argument, e.g.<br>
                  <tt>mc &lt;config$gt;</tt>, where &lt;config$gt; is <tt>Default</tt><br>
                  for the MARS default 32-bit address space, <tt>CompactDataAtZero</tt> for<br>
                  a 32KB address space with data segment at address 0, or <tt>CompactTextAtZero</tt><br>
                  for a 32KB address space with text segment at address 0.<br>
           me  -- display MARS messages to standard err instead of standard out. Can separate via redirection.</br>
           nc  -- do not display copyright notice (for cleaner redirected/piped output).</br>
   		  np  -- No Pseudo-instructions allowed ("ne" will work also).<br>
   		   p  -- Project mode - assemble all files in the same directory as given file.<br>
   	  se<n>  -- terminate MARS with integer exit code <n> if a simulation (run) error occurs.<br>
           sm  -- Start execution at Main - Execution will start at program statement globally labeled main.<br>
          smc  -- Self Modifying Code - Program can write and branch to either text or data segment<br>
           we  -- assembler Warnings will be considered Errors<br>
          <n>  -- where <n> is an integer maximum count of steps to simulate.<br>
                  If 0, negative or not specified, there is no maximum.<br>
       $<reg>  -- where <reg> is number or name (e.g. 5, t3, f10) of register whose <br>
                  content to display at end of run.  Option may be repeated.<br>
   <reg_name>  -- where <reg_name> is name (e.g. t3, f10) of register whose <br>
                  content to display at end of run.  Option may be repeated. $ not required.<br>
      <m>-<n>  -- memory address range from <m> to <n> whose contents to<br>
                  display at end of run. <m> and <n> may be hex or decimal,<br>
                  <m> <= <n>, both must be on word boundary.  Option may be repeated.<br>
           pa  -- Program Arguments follow in a space-separated list.  This<br>
                  option must be placed AFTER ALL FILE NAMES, because everything<br>
                  that follows it is interpreted as a program argument to be<br>
                  made available to the MIPS program at runtime.<br>
    **/
    
   
   
      private boolean simulate;
      private int displayFormat;
      private boolean verbose;  // display register name or address along with contents
      private boolean assembleProject; // assemble only the given file or all files in its directory
      private boolean pseudo;  // pseudo instructions allowed in source code or not.
      private boolean delayedBranching;  // MIPS delayed branching is enabled.
      private boolean warningsAreErrors; // Whether assembler warnings should be considered errors.
      private boolean startAtMain; // Whether to start execution at statement labeled 'main' 
      private boolean countInstructions; // Whether to count and report number of instructions executed 
      private boolean selfModifyingCode; // Whether to allow self-modifying code (e.g. write to text segment)
      private static final String rangeSeparator = "-";
      private static final int splashDuration = 2000; // time in MS to show splash screen
      private static final int memoryWordsPerLine = 4; // display 4 memory words, tab separated, per line
      private static final int DECIMAL = 0; // memory and register display format
      private static final int HEXADECIMAL = 1;// memory and register display format
      private static final int ASCII = 2;// memory and register display format
      private ArrayList registerDisplayList;
      private ArrayList memoryDisplayList;
      private ArrayList filenameList;
      private MIPSprogram code;
      private int maxSteps;
      private int instructionCount;
      private PrintStream out; // stream for display of command line output
      private ArrayList dumpTriples = null; // each element holds 3 arguments for dump option
      private ArrayList programArgumentList; // optional program args for MIPS program (becomes argc, argv)
      private int assembleErrorExitCode;  // MARS command exit code to return if assemble error occurs
      private int simulateErrorExitCode;// MARS command exit code to return if simulation error occurs
   		
      public MarsLaunch(String[] args) {
         boolean gui = (args.length == 0);
         Globals.initialize(gui);  
         if (gui) {
            launchIDE();  
         } 
         else { // running from command line.
            // assure command mode works in headless environment (generates exception if not)
            System.setProperty("java.awt.headless", "true"); 
            simulate = true;
            displayFormat = HEXADECIMAL;
            verbose = true;  
            assembleProject = false;
            pseudo = true;
            delayedBranching = false;
            warningsAreErrors = false;
            startAtMain = false;
            countInstructions = false;
				selfModifyingCode = false;
            instructionCount = 0;
            assembleErrorExitCode = 0;
            simulateErrorExitCode = 0;
            registerDisplayList = new ArrayList();
            memoryDisplayList = new ArrayList();
            filenameList = new ArrayList();
            MemoryConfigurations.setCurrentConfiguration(MemoryConfigurations.getDefaultConfiguration());
         	// do NOT use Globals.program for command line MARS -- it triggers 'backstep' log.
            code = new MIPSprogram();  
            maxSteps = -1;
            out = System.out;  
            if (parseCommandArgs(args)) {
               if (runCommand()) {
                  displayMiscellaneousPostMortem();
                  displayRegistersPostMortem();
                  displayMemoryPostMortem();
               }
               dumpSegments();
            }
            System.exit(Globals.exitCode);
         }
      }   		
   	
      /////////////////////////////////////////////////////////////
   	// Perform any specified dump operations.  See "dump" option.
   	//
   
      private void dumpSegments() {
       
         if (dumpTriples == null) 
            return;
         
         for (int i=0; i<dumpTriples.size(); i++) {
            String[] triple = (String[])dumpTriples.get(i);
            File file = new File(triple[2]);
            Integer[] segInfo = MemoryDump.getSegmentBounds(triple[0]);
         	// If not segment name, see if it is address range instead.  DPS 14-July-2008
            if (segInfo == null) {
               try {
                  String[] memoryRange = checkMemoryAddressRange(triple[0]);
                  segInfo = new Integer[2];
                  segInfo[0] = new Integer(Binary.stringToInt(memoryRange[0])); // low end of range
                  segInfo[1] = new Integer(Binary.stringToInt(memoryRange[1])); // high end of range
               }    
                  catch (NumberFormatException nfe) {
                     segInfo = null;
                  }
                  catch (NullPointerException npe) {
                     segInfo = null;
                  }
            }
            if (segInfo == null) {
               out.println("Error while attempting to save dump, segment/address-range " + triple[0] + " is invalid!");
               continue;
            }
            DumpFormatLoader loader = new DumpFormatLoader();
            ArrayList dumpFormats = loader.loadDumpFormats();
            DumpFormat format = DumpFormatLoader.findDumpFormatGivenCommandDescriptor(dumpFormats, triple[1]);
            if (format == null) {
               out.println("Error while attempting to save dump, format " + triple[1] + " was not found!");
               continue;
            }
            try {
               int highAddress = Globals.memory.getAddressOfFirstNull(segInfo[0].intValue(), segInfo[1].intValue())- Memory.WORD_LENGTH_BYTES;
               if (highAddress < segInfo[0].intValue()) {
                  out.println("This segment has not been written to, there is nothing to dump.");
                  continue;
               } 
               format.dumpMemoryRange(file, segInfo[0].intValue(), highAddress); 
            } 
               catch (FileNotFoundException e) {
                  out.println("Error while attempting to save dump, file " + file + " was not found!");
                  continue;
               } 
               catch (AddressErrorException e) {
                  out.println("Error while attempting to save dump, file " + file + "!  Could not access address: " + e.getAddress() + "!");
                  continue;
               } 
               catch (IOException e) {
                  out.println("Error while attempting to save dump, file " + file + "!  Disk IO failed!");
                  continue;
               }
         }
      } 	
   	
   		
   	/////////////////////////////////////////////////////////////////
   	// There are no command arguments, so run in interactive mode by
   	// launching the GUI-fronted integrated development environment.
   	
      private void launchIDE() {
         // System.setProperty("apple.laf.useScreenMenuBar", "true"); // Puts MARS menu on Mac OS menu bar
         new MarsSplashScreen(splashDuration).showSplash();
         SwingUtilities.invokeLater(
               new Runnable() {
                  public void run() {
                     //Turn off metal's use of bold fonts
                     //UIManager.put("swing.boldMetal", Boolean.FALSE);
                     new VenusUI("MARS "+Globals.version);
                  }
               }); 
         return;					
      }
   			
   			
   	//////////////////////////////////////////////////////////////////////
   	// Parse command line arguments.  The initial parsing has already been
   	// done, since each space-separated argument is already in a String array
   	// element.  Here, we check for validity, set switch variables as appropriate
   	// and build data structures.  For help option (h), display the help.
   	// Returns true if command args parse OK, false otherwise.
      			
      private boolean parseCommandArgs(String[] args) {
         String noCopyrightSwitch = "nc";
         String displayMessagesToErrSwitch = "me";
         boolean argsOK = true;
         boolean inProgramArgumentList = false;
         programArgumentList = null;
         if (args.length == 0) 
            return true; // should not get here...
         // If the option to display MARS messages to standard erro is used,
      	// it must be processed before any others (since messages may be
      	// generated during option parsing).
         processDisplayMessagesToErrSwitch(args, displayMessagesToErrSwitch);
         displayCopyright(args, noCopyrightSwitch);  // ..or not..
         if (args.length == 1 && args[0].equals("h")) {
            displayHelp();
            return false;					
         } 
         for (int i=0; i<args.length; i++) {
            // We have seen "pa" switch, so all remaining args are program args
         	// that will become "argc" and "argv" for the MIPS program.
            if (inProgramArgumentList) {
               if (programArgumentList == null) {
                  programArgumentList = new ArrayList();
               }
               programArgumentList.add(args[i]);
               continue;
            }
         	// Once we hit "pa", all remaining command args are assumed
         	// to be program arguments.
            if (args[i].toLowerCase().equals("pa")) {
               inProgramArgumentList = true;
               continue;
            }
         	// messages-to-standard-error switch already processed, so ignore.
            if (args[i].toLowerCase().equals(displayMessagesToErrSwitch)) {
               continue;
            }
         	// no-copyright switch already processed, so ignore.
            if (args[i].toLowerCase().equals(noCopyrightSwitch)) {
               continue;
            }				
            if (args[i].toLowerCase().equals("dump")) {
               if (args.length <= (i+3)) {
                  out.println("Dump command line argument requires a segment, format and file name.");
                  argsOK = false;
               } 
               else {
                  if (dumpTriples == null) 
                     dumpTriples = new ArrayList();
                  dumpTriples.add(new String[] {args[++i], args[++i], args[++i]});
                  //simulate = false;
               }
               continue;
            } 
            if (args[i].toLowerCase().equals("mc")) {
               String configName = args[++i];
               MemoryConfiguration config = MemoryConfigurations.getConfigurationByName(configName);
               if (config == null) {
                  out.println("Invalid memory configuration: "+configName);
                  argsOK = false;
               } 
               else {
                  MemoryConfigurations.setCurrentConfiguration(config);
               }
               continue;
            }
         	// Set MARS exit code for assemble error
            if (args[i].toLowerCase().indexOf("ae")==0) {
               String s = args[i].substring(2);					   
               try {
                  assembleErrorExitCode = Integer.decode(s).intValue();
                  continue;
               }             
                  catch (NumberFormatException nfe) {
                   // Let it fall thru and get handled by catch-all
                  }
            }
         	// Set MARS exit code for simulate error
            if (args[i].toLowerCase().indexOf("se")==0) {
               String s = args[i].substring(2);					   
               try {
                  simulateErrorExitCode = Integer.decode(s).intValue();
                  continue;
               }              
                  catch (NumberFormatException nfe) {
                   // Let it fall thru and get handled by catch-all
                  }
            }
            if (args[i].toLowerCase().equals("d")) { 
               Globals.debug = true;
               continue;
            }
            if (args[i].toLowerCase().equals("a")) { 
               simulate = false;
               continue;
            }
            if (args[i].toLowerCase().equals("ad") || 
               	 args[i].toLowerCase().equals("da")) {
               Globals.debug = true;
               simulate = false;
               continue;
            }
            if (args[i].toLowerCase().equals("p")) { 
               assembleProject = true;
               continue;
            }
            if (args[i].toLowerCase().equals("dec")) {
               displayFormat = DECIMAL;
               continue;
            }
            if (args[i].toLowerCase().equals("hex")) {
               displayFormat = HEXADECIMAL;
               continue;
            }
            if (args[i].toLowerCase().equals("ascii")) {
               displayFormat = ASCII;
               continue;
            }
            if (args[i].toLowerCase().equals("b")) {
               verbose = false;
               continue;
            }
            if (args[i].toLowerCase().equals("db")) {
               delayedBranching = true;
               continue;
            }
            if (args[i].toLowerCase().equals("np") || args[i].toLowerCase().equals("ne")) {
               pseudo = false;
               continue;
            }
            if (args[i].toLowerCase().equals("we")) { // added 14-July-2008 DPS
               warningsAreErrors = true;
               continue;
            }
            if (args[i].toLowerCase().equals("sm")) { // added 17-Dec-2009 DPS
               startAtMain = true;
               continue;
            }
            if (args[i].toLowerCase().equals("smc")) { // added 5-Jul-2013 DPS
               selfModifyingCode = true;
               continue;
            }
            if (args[i].toLowerCase().equals("ic")) { // added 19-Jul-2012 DPS
               countInstructions = true;
               continue;
            }
         
         
            if (args[i].indexOf("$") == 0) {
               if (RegisterFile.getUserRegister(args[i])==null &&
                      Coprocessor1.getRegister(args[i])==null) {
                  out.println("Invalid Register Name: "+args[i]);
               } 
               else {
                  registerDisplayList.add(args[i]);
               }
               continue;
            }
         	// check for register name w/o $.  added 14-July-2008 DPS
            if (RegisterFile.getUserRegister("$"+args[i]) != null ||
                Coprocessor1.getRegister("$"+args[i]) != null) {
               registerDisplayList.add("$"+args[i]);
               continue;
            }
            if (new File(args[i]).exists()) {  // is it a file name?
               filenameList.add(args[i]);
               continue;
            }
            // Check for stand-alone integer, which is the max execution steps option
            try {   
               Integer.decode(args[i]);
               maxSteps = Integer.decode(args[i]).intValue(); // if we got here, it has to be OK 
               continue;              
            } 
               catch (NumberFormatException nfe) {
               }
            // Check for integer address range (m-n)
            try {
               String[] memoryRange = checkMemoryAddressRange(args[i]);
               memoryDisplayList.add(memoryRange[0]); // low end of range
               memoryDisplayList.add(memoryRange[1]); // high end of range
               continue;
            }    
               catch (NumberFormatException nfe) {
                  out.println("Invalid/unaligned address or invalid range: "+args[i]);
                  argsOK = false;
                  continue;
               }
               catch (NullPointerException npe) {
               	   // Do nothing.  next statement will handle it
               }
            out.println("Invalid Command Argument: "+args[i]);
            argsOK = false;
         }
         return argsOK;
      }
      
   	
   	//////////////////////////////////////////////////////////////////////
   	// Carry out the mars command: assemble then optionally run     
   	// Returns false if no simulation (run) occurs, true otherwise. 	
      	
      private boolean runCommand() {
         boolean programRan = false;
         if (filenameList.size()==0) {
            return programRan;
         }
         try {
            Globals.getSettings().setBooleanSettingNonPersistent(Settings.DELAYED_BRANCHING_ENABLED, delayedBranching);
            Globals.getSettings().setBooleanSettingNonPersistent(Settings.SELF_MODIFYING_CODE_ENABLED, selfModifyingCode);
            File mainFile = new File((String) filenameList.get(0)).getAbsoluteFile();// First file is "main" file
            ArrayList filesToAssemble;
            if (assembleProject) { 
               filesToAssemble = FilenameFinder.getFilenameList(mainFile.getParent(), Globals.fileExtensions);
               if (filenameList.size() > 1) {
                  // Using "p" project option PLUS listing more than one filename on command line.
                  // Add the additional files, avoiding duplicates.
                  filenameList.remove(0); // first one has already been processed
                  ArrayList moreFilesToAssemble = FilenameFinder.getFilenameList(filenameList, FilenameFinder.MATCH_ALL_EXTENSIONS);
                  // Remove any duplicates then merge the two lists.
                  for (int index2 = 0; index2<moreFilesToAssemble.size(); index2++) {
                     for (int index1 = 0; index1<filesToAssemble.size(); index1++) {
                        if (filesToAssemble.get(index1).equals(moreFilesToAssemble.get(index2))) {
                           moreFilesToAssemble.remove(index2);
                           index2--; // adjust for left shift in moreFilesToAssemble...
                           break;    // break out of inner loop...
                        }
                     }
                  }
                  filesToAssemble.addAll(moreFilesToAssemble);
               }
            } 
            else {
               filesToAssemble = FilenameFinder.getFilenameList(filenameList, FilenameFinder.MATCH_ALL_EXTENSIONS);
            }
            if (Globals.debug) {
               out.println("--------  TOKENIZING BEGINS  -----------");
            }
            ArrayList MIPSprogramsToAssemble = 
                      code.prepareFilesForAssembly(filesToAssemble, mainFile.getAbsolutePath(), null);		
            if (Globals.debug) {
               out.println("--------  ASSEMBLY BEGINS  -----------");
            }
         	// Added logic to check for warnings and print if any. DPS 11/28/06
            ErrorList warnings = code.assemble(MIPSprogramsToAssemble, pseudo, warningsAreErrors);
            if (warnings != null && warnings.warningsOccurred()) {
               out.println(warnings.generateWarningReport());
            }
            RegisterFile.initializeProgramCounter(startAtMain); // DPS 3/9/09
            if (simulate) {
               // store program args (if any) in MIPS memory
               new ProgramArgumentList(programArgumentList).storeProgramArguments();
            	// establish observer if specified  
               establishObserver();
               if (Globals.debug) {
                  out.println("--------  SIMULATION BEGINS  -----------");
               }
               programRan = true;
               boolean done = code.simulate(maxSteps);
               if (!done) {
                  out.println("\nProgram terminated when maximum step limit "+maxSteps+" reached.");
               }
            }
            if (Globals.debug) {
               out.println("\n--------  ALL PROCESSING COMPLETE  -----------");
            }
         }
            catch (ProcessingException e) {
               Globals.exitCode = (programRan) ? simulateErrorExitCode : assembleErrorExitCode;
               out.println(e.errors().generateErrorAndWarningReport());
               out.println("Processing terminated due to errors.");
            } 
         return programRan;
      }
   
   
   	//////////////////////////////////////////////////////////////////////
      // Check for memory address subrange.  Has to be two integers separated
      // by "-"; no embedded spaces.  e.g. 0x00400000-0x00400010
      // If number is not multiple of 4, will be rounded up to next higher. 
   	
      private String[] checkMemoryAddressRange(String arg) throws NumberFormatException {
         String[] memoryRange = null;
         if (arg.indexOf(rangeSeparator) > 0 && 
                   arg.indexOf(rangeSeparator) < arg.length()-1) {
            // assume correct format, two numbers separated by -, no embedded spaces.
            // If that doesn't work it is invalid.
            memoryRange = new String[2];
            memoryRange[0] = arg.substring(0,arg.indexOf(rangeSeparator));
            memoryRange[1] = arg.substring(arg.indexOf(rangeSeparator)+1);
            // NOTE: I will use homegrown decoder, because Integer.decode will throw
            // exception on address higher than 0x7FFFFFFF (e.g. sign bit is 1).
            if (Binary.stringToInt(memoryRange[0]) > Binary.stringToInt(memoryRange[1]) ||
                         !Memory.wordAligned(Binary.stringToInt(memoryRange[0])) ||
                     	 !Memory.wordAligned(Binary.stringToInt(memoryRange[1]))) {
               throw new NumberFormatException();
            }
         }
         return memoryRange;
      }
   
      /////////////////////////////////////////////////////////////////
   	// Required for counting instructions executed, if that option is specified.
   	// DPS 19 July 2012
      private void establishObserver() { 
         if (countInstructions) {
            Observer instructionCounter = 
               new Observer() {
                  private int lastAddress = 0;
                  public void update(Observable o, Object obj) { 
                     if (obj instanceof AccessNotice) {
                        AccessNotice notice = (AccessNotice) obj;
                        if (!notice.accessIsFromMIPS()) 
                           return;
                        if (notice.getAccessType() != AccessNotice.READ) 
                           return;
                        MemoryAccessNotice m = (MemoryAccessNotice) notice;
                        int a = m.getAddress();
                        if (a == lastAddress) 
                           return;
                        lastAddress = a;
                        instructionCount++;				  
                     }
                  }
               };
            try {
               Globals.memory.addObserver(instructionCounter, Memory.textBaseAddress, Memory.textLimitAddress);
            } 
               catch (AddressErrorException aee) {
                  out.println("Internal error: MarsLaunch uses incorrect text segment address for instruction observer");
               }
         }		
      }
   	     		   	
   	//////////////////////////////////////////////////////////////////////
   	// Displays any specified runtime properties. Initially just instruction count 
   	// DPS 19 July 2012  	
      private void displayMiscellaneousPostMortem() {
         if (countInstructions) {
            out.println("\n"+instructionCount);
         }
      }
   
   	     		   	
   	//////////////////////////////////////////////////////////////////////
   	// Displays requested register or registers   			
   				
      private void displayRegistersPostMortem() {
         int value;  // handy local to use throughout the next couple loops
         String strValue;
         // Display requested register contents
         out.println();
         Iterator regIter = registerDisplayList.iterator();
         while (regIter.hasNext()) {
            String reg = regIter.next().toString();
            if (RegisterFile.getUserRegister(reg)!=null) {
                     // integer register
               if (verbose) 
                  out.print(reg+"\t");
               value = RegisterFile.getUserRegister(reg).getValue();
               out.println( formatIntForDisplay(value) );
            } 
            else {
                     // floating point register
               float fvalue = Coprocessor1.getFloatFromRegister(reg);
               int ivalue = Coprocessor1.getIntFromRegister(reg);
               double dvalue = Double.NaN;
               long lvalue = 0;
               boolean hasDouble = false;
               try {
                  dvalue = Coprocessor1.getDoubleFromRegisterPair(reg);
                  lvalue = Coprocessor1.getLongFromRegisterPair(reg);
                  hasDouble = true;
               } 
                  catch (InvalidRegisterAccessException irae) { }
               if (verbose) {
                  out.print(reg+"\t");
               }
               if (displayFormat == HEXADECIMAL) {
                  // display float (and double, if applicable) in hex
                  out.print(
                           Binary.binaryStringToHexString(
                                          Binary.intToBinaryString(ivalue)));							
                  if (hasDouble) {
                     out.println("\t"+
                              Binary.binaryStringToHexString(
                                          Binary.longToBinaryString(lvalue)));
                  } 
                  else {
                     out.println("");
                  }
               } 
               else if (displayFormat == DECIMAL) {
                  // display float (and double, if applicable) in decimal
                  out.print(fvalue);
                  if (hasDouble) {
                     out.println("\t"+dvalue);
                  } 
                  else {
                     out.println("");
                  }
               } 
               else { // displayFormat == ASCII
                  out.print(Binary.intToAscii(ivalue));
                  if (hasDouble) {
                     out.println("\t"+ 
                            Binary.intToAscii(Binary.highOrderLongToInt(lvalue))+
                            Binary.intToAscii(Binary.lowOrderLongToInt(lvalue)));
                  } 
                  else {
                     out.println("");
                  }
               }
            }
         }
      }
   
   	//////////////////////////////////////////////////////////////////////
   	// Formats int value for display: decimal, hex, ascii    
      private String formatIntForDisplay(int value) {
         String strValue; 
         switch (displayFormat) {
            case DECIMAL : 
               strValue = ""+value;
               break;
            case HEXADECIMAL :
               strValue = Binary.intToHexString(value);
               break;
            case ASCII :
               strValue = Binary.intToAscii(value);
               break;
            default :
               strValue = Binary.intToHexString(value);
         }   
         return strValue;
      }
   	
   	//////////////////////////////////////////////////////////////////////
   	// Displays requested memory range or ranges
   	
      private void displayMemoryPostMortem() {  
         int value;  
         // Display requested memory range contents
         Iterator memIter = memoryDisplayList.iterator();
         int addressStart=0, addressEnd=0;
         while (memIter.hasNext()) {
            try { // This will succeed; error would have been caught during command arg parse
               addressStart = Binary.stringToInt(memIter.next().toString());
               addressEnd = Binary.stringToInt(memIter.next().toString());
            } 
               catch (NumberFormatException nfe) {
               }
            int valuesDisplayed = 0;
            for (int addr=addressStart; addr<=addressEnd; addr+=Memory.WORD_LENGTH_BYTES) {
               if (addr < 0 && addressEnd > 0) 
                  break;  // happens only if addressEnd is 0x7ffffffc
               if (valuesDisplayed % memoryWordsPerLine == 0) {
                  out.print( (valuesDisplayed > 0) ? "\n" : "");
                  if (verbose) {
                     out.print("Mem["+Binary.intToHexString(addr)+"]\t");
                  }
               }
               try {
                  // Allow display of binary text segment (machine code) DPS 14-July-2008
                  if (Memory.inTextSegment(addr) || Memory.inKernelTextSegment(addr)) {
                     Integer iValue = Globals.memory.getRawWordOrNull(addr);
                     value = (iValue==null) ? 0 : iValue.intValue();
                  } 
                  else {
                     value = Globals.memory.getWord(addr);
                  }
                  out.print( formatIntForDisplay(value)+"\t");
               }
                  catch (AddressErrorException aee) {
                     out.print("Invalid address: "+addr+"\t");
                  }
               valuesDisplayed++;
            }
            out.println();
         }					
      }
   
      ///////////////////////////////////////////////////////////////////////
   	//  If option to display MARS messages to standard err (System.err) is
   	//  present, it must be processed before all others.  Since messages may
   	//  be output as early as during the command parse.  
      private void processDisplayMessagesToErrSwitch(String[] args, String displayMessagesToErrSwitch) {
         for (int i=0; i<args.length; i++) {
            if (args[i].toLowerCase().equals(displayMessagesToErrSwitch)) {
               out = System.err;
               return;
            }
         }
      }  
      ///////////////////////////////////////////////////////////////////////
   	//  Decide whether copyright should be displayed, and display
   	//  if so.
   	
      private void displayCopyright(String[] args, String noCopyrightSwitch) {
         boolean print = true;
         for (int i=0; i<args.length; i++) {
            if (args[i].toLowerCase().equals(noCopyrightSwitch)) {
               return;
            }
         }
         out.println("MARS "+Globals.version+"  Copyright "+Globals.copyrightYears+" "+Globals.copyrightHolders+"\n");
      }
   	
   
   	///////////////////////////////////////////////////////////////////////
   	//  Display command line help text
   	
      private void displayHelp() {
         String[] segmentNames = MemoryDump.getSegmentNames();
         String segments = "";
         for (int i=0; i<segmentNames.length; i++) {
            segments += segmentNames[i];
            if (i<segmentNames.length-1) {
               segments += ", ";
            }
         }
         ArrayList dumpFormats = (new DumpFormatLoader()).loadDumpFormats();
         String formats = "";
         for (int i=0; i<dumpFormats.size(); i++) {
            formats += ((DumpFormat) dumpFormats.get(i)).getCommandDescriptor();
            if (i<dumpFormats.size()-1) {
               formats += ", ";
            }
         }
         out.println("Usage:  Mars  [options] filename [additional filenames]");
         out.println("  Valid options (not case sensitive, separate by spaces) are:");
         out.println("      a  -- assemble only, do not simulate");
         out.println("  ae<n>  -- terminate MARS with integer exit code <n> if an assemble error occurs.");
         out.println("  ascii  -- display memory or register contents interpreted as ASCII codes.");
         out.println("      b  -- brief - do not display register/memory address along with contents");
         out.println("      d  -- display MARS debugging statements");
         out.println("     db  -- MIPS delayed branching is enabled");
         out.println("    dec  -- display memory or register contents in decimal.");
         out.println("   dump <segment> <format> <file> -- memory dump of specified memory segment");
         out.println("            in specified format to specified file.  Option may be repeated."); 
         out.println("            Dump occurs at the end of simulation unless 'a' option is used.");
         out.println("            Segment and format are case-sensitive and possible values are:");
         out.println("            <segment> = "+segments);
         out.println("            <format> = "+formats);
         out.println("      h  -- display this help.  Use by itself with no filename.");
         out.println("    hex  -- display memory or register contents in hexadecimal (default)");
         out.println("     ic  -- display count of MIPS basic instructions 'executed'");
         out.println("     mc <config>  -- set memory configuration.  Argument <config> is");
         out.println("            case-sensitive and possible values are: Default for the default");
         out.println("            32-bit address space, CompactDataAtZero for a 32KB memory with");
         out.println("            data segment at address 0, or CompactTextAtZero for a 32KB");
         out.println("            memory with text segment at address 0.");
         out.println("     me  -- display MARS messages to standard err instead of standard out. ");
         out.println("            Can separate messages from program output using redirection");
         out.println("     nc  -- do not display copyright notice (for cleaner redirected/piped output).");
         out.println("     np  -- use of pseudo instructions and formats not permitted");
         out.println("      p  -- Project mode - assemble all files in the same directory as given file.");
         out.println("  se<n>  -- terminate MARS with integer exit code <n> if a simulation (run) error occurs.");
         out.println("     sm  -- start execution at statement with global label main, if defined");
         out.println("    smc  -- Self Modifying Code - Program can write and branch to either text or data segment");
         out.println("    <n>  -- where <n> is an integer maximum count of steps to simulate.");
         out.println("            If 0, negative or not specified, there is no maximum.");
         out.println(" $<reg>  -- where <reg> is number or name (e.g. 5, t3, f10) of register whose ");
         out.println("            content to display at end of run.  Option may be repeated.");
         out.println("<reg_name>  -- where <reg_name> is name (e.g. t3, f10) of register whose");
         out.println("            content to display at end of run.  Option may be repeated. ");
         out.println("            The $ is not required.");
         out.println("<m>-<n>  -- memory address range from <m> to <n> whose contents to");
         out.println("            display at end of run. <m> and <n> may be hex or decimal,");
         out.println("            must be on word boundary, <m> <= <n>.  Option may be repeated.");
         out.println("     pa  -- Program Arguments follow in a space-separated list.  This");
         out.println("            option must be placed AFTER ALL FILE NAMES, because everything");
         out.println("            that follows it is interpreted as a program argument to be");
         out.println("            made available to the MIPS program at runtime.");
         out.println("If more than one filename is listed, the first is assumed to be the main");
         out.println("unless the global statement label 'main' is defined in one of the files.");
         out.println("Exception handler not automatically assembled.  Add it to the file list.");
         out.println("Options used here do not affect MARS Settings menu values and vice versa.");
      }
   
   }
   	
   	
