diff --git a/src/main/java/mars/MarsLaunch.java b/src/main/java/mars/MarsLaunch.java deleted file mode 100644 index f6c9c8d..0000000 --- a/src/main/java/mars/MarsLaunch.java +++ /dev/null @@ -1,1016 +0,0 @@ -package mars; - -import mars.mips.dump.DumpFormat; -import mars.mips.dump.DumpFormatLoader; -import mars.mips.hardware.*; -import mars.simulator.ProgramArgumentList; -import mars.util.Binary; -import mars.util.FilenameFinder; -import mars.util.MemoryDump; -import mars.venus.VenusUI; - -import javax.swing.*; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Observable; -import java.util.Observer; - -/* -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 -{ - - 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 - - /** - * Main takes a number of command line arguments.
Usage: Mars [options] filename
Valid options (not case - * sensitive, separate by spaces) are:
a -- assemble only, do not simulate
ad -- both a and d
ae - * -- terminate MARS with integer exit code if an assemble error occurs.
ascii -- display memory or - * register contents interpreted as ASCII b -- brief - do not display register/memory address along with - * contents
d -- print debugging statements
da -- both a and d
db -- MIPS delayed branching is - * enabled.
dec -- display memory or register contents in decimal.
dump -- dump memory contents to file. - * Option has 3 arguments, e.g.
- * dump <segment> <format> <file>. Also supports
- * an address range (see m-n below). Current supported
segments are .text and .data. - * Current supported dump formats
are Binary, HexText, BinaryText.
h -- display - * help. Use by itself and with no filename
hex -- display memory or register contents in hexadecimal - * (default)
ic -- display count of MIPS basic instructions 'executed'"); mc -- set memory configuration. - * Option has 1 argument, e.g.
- * mc <config$gt;, where <config$gt; is Default
- * for the MARS default 32-bit address space, CompactDataAtZero for
a 32KB address space with data - * segment at address 0, or CompactTextAtZero
for a 32KB address space with text segment at address - * 0.
me -- display MARS messages to standard err instead of standard out. Can separate via redirection.
- * nc -- do not display copyright notice (for cleaner redirected/piped output).
np -- No Pseudo-instructions - * allowed ("ne" will work also).
p -- Project mode - assemble all files in the same directory as given - * file.
se -- terminate MARS with integer exit code if a simulation (run) error occurs.
sm -- - * Start execution at Main - Execution will start at program statement globally labeled main.
smc -- Self - * Modifying Code - Program can write and branch to either text or data segment
we -- assembler Warnings will - * be considered Errors
- * -- where is an integer maximum count of steps to simulate.
- * If 0, negative or not specified, there is no maximum.
$ -- where is number or name (e.g. 5, t3, - * f10) of register whose
content to display at end of run. Option may be repeated.
- * -- where is name (e.g. t3, f10) of register whose
- * content to display at end of run. Option may be repeated. $ not required.
- * - -- memory address range from to whose contents to
- * display at end of run. and may be hex or decimal,
- * <= , both must be on word boundary. Option may be repeated.
- * pa -- Program Arguments follow in a space-separated list. This
option must be placed AFTER ALL FILE NAMES, - * because everything
that follows it is interpreted as a program argument to be
made available to the MIPS - * program at runtime.
- **/ - - - 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 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] = Integer.valueOf(Binary.stringToInt(memoryRange[0])); // low end of range - segInfo[1] = Integer.valueOf(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); - } - }); - } - - - ////////////////////////////////////////////////////////////////////// - // 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].equalsIgnoreCase("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].equalsIgnoreCase("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].equalsIgnoreCase("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].equalsIgnoreCase("d")) - { - Globals.debug = true; - continue; - } - if (args[i].equalsIgnoreCase("a")) - { - simulate = false; - continue; - } - if (args[i].equalsIgnoreCase("ad") || - args[i].equalsIgnoreCase("da")) - { - Globals.debug = true; - simulate = false; - continue; - } - if (args[i].equalsIgnoreCase("p")) - { - assembleProject = true; - continue; - } - if (args[i].equalsIgnoreCase("dec")) - { - displayFormat = DECIMAL; - continue; - } - if (args[i].equalsIgnoreCase("hex")) - { - displayFormat = HEXADECIMAL; - continue; - } - if (args[i].equalsIgnoreCase("ascii")) - { - displayFormat = ASCII; - continue; - } - if (args[i].equalsIgnoreCase("b")) - { - verbose = false; - continue; - } - if (args[i].equalsIgnoreCase("db")) - { - delayedBranching = true; - continue; - } - if (args[i].equalsIgnoreCase("np") || args[i].equalsIgnoreCase("ne")) - { - pseudo = false; - continue; - } - if (args[i].equalsIgnoreCase("we")) - { // added 14-July-2008 DPS - warningsAreErrors = true; - continue; - } - if (args[i].equalsIgnoreCase("sm")) - { // added 17-Dec-2009 DPS - startAtMain = true; - continue; - } - if (args[i].equalsIgnoreCase("smc")) - { // added 5-Jul-2013 DPS - selfModifyingCode = true; - continue; - } - if (args[i].equalsIgnoreCase("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 -- terminate MARS with integer exit code 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 -- 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(" = " + segments); - out.println(" = " + 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 -- set memory configuration. Argument 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 -- terminate MARS with integer exit code 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(" -- where is an integer maximum count of steps to simulate."); - out.println(" If 0, negative or not specified, there is no maximum."); - out.println(" $ -- where 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(" -- where 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("- -- memory address range from to whose contents to"); - out.println(" display at end of run. and may be hex or decimal,"); - out.println(" must be on word boundary, <= . 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."); - } - -} - - diff --git a/src/main/java/mars/MarsLaunch.kt b/src/main/java/mars/MarsLaunch.kt new file mode 100644 index 0000000..c195f28 --- /dev/null +++ b/src/main/java/mars/MarsLaunch.kt @@ -0,0 +1,1002 @@ +package mars + +import mars.mips.dump.DumpFormat +import mars.mips.dump.DumpFormatLoader +import mars.mips.hardware.* +import mars.simulator.ProgramArgumentList +import mars.util.Binary +import mars.util.FilenameFinder +import mars.util.MemoryDump +import mars.venus.VenusUI +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException +import java.io.PrintStream +import java.util.* +import javax.swing.SwingUtilities +import kotlin.system.exitProcess + +/* +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) + */ +/** + * Main takes a number of command line arguments.

Usage: Mars [options] filename

Valid options (not case + * sensitive, separate by spaces) are:

a -- assemble only, do not simulate

ad -- both a and d

ae + * -- terminate MARS with integer exit code if an assemble error occurs.

ascii -- display memory or + * register contents interpreted as ASCII b -- brief - do not display register/memory address along with + * contents

d -- print debugging statements

da -- both a and d

db -- MIPS delayed branching is + * enabled.

dec -- display memory or register contents in decimal.

dump -- dump memory contents to file. + * Option has 3 arguments, e.g.

+ * dump <segment> <format> <file>. Also supports

+ * an address range (see *m-n* below). Current supported

segments are .text and .data. + * Current supported dump formats

are Binary, HexText, BinaryText.

h -- display + * help. Use by itself and with no filename hex -- display memory or register contents in hexadecimal + * (default)

ic -- display count of MIPS basic instructions 'executed'"); mc -- set memory configuration. + * Option has 1 argument, e.g.

+ * mc <config$gt;, where <config$gt; is Default

+ * for the MARS default 32-bit address space, CompactDataAtZero for

a 32KB address space with data + * segment at address 0, or CompactTextAtZero

for a 32KB address space with text segment at address + * 0.

me -- display MARS messages to standard err instead of standard out. Can separate via redirection. + * nc -- do not display copyright notice (for cleaner redirected/piped output). np -- No Pseudo-instructions + * allowed ("ne" will work also).

p -- Project mode - assemble all files in the same directory as given + * file.

se -- terminate MARS with integer exit code if a simulation (run) error occurs.

sm -- + * Start execution at Main - Execution will start at program statement globally labeled main.

smc -- Self + * Modifying Code - Program can write and branch to either text or data segment

we -- assembler Warnings will + * be considered Errors

+ * -- where is an integer maximum count of steps to simulate.

+ * If 0, negative or not specified, there is no maximum.

$ -- where is number or name (e.g. 5, t3, + * f10) of register whose

content to display at end of run. Option may be repeated.

+ * -- where is name (e.g. t3, f10) of register whose

+ * content to display at end of run. Option may be repeated. $ not required.

+ * - -- memory address range from to whose contents to

+ * display at end of run. and may be hex or decimal,

+ * <= , both must be on word boundary. Option may be repeated.

+ * pa -- Program Arguments follow in a space-separated list. This

option must be placed AFTER ALL FILE NAMES, + * because everything

that follows it is interpreted as a program argument to be

made available to the MIPS + * program at runtime.

+ */ +class MarsLaunch(args: Array) +{ + private var simulate = true + private var displayFormat = 0 + + // display register name or address along with contents + private var verbose = false + + // assemble only the given file or all files in its directory + private var assembleProject = false + + // pseudo instructions allowed in source code or not. + private var pseudo = true + + // MIPS delayed branching is enabled. + private var delayedBranching = false + + // Whether assembler warnings should be considered errors. + private var warningsAreErrors = false + + // Whether to start execution at statement labeled 'main' + private var startAtMain = false + + // Whether to count and report number of instructions executed + private var countInstructions = false + + // Whether to allow self-modifying code (e.g. write to text segment) + private var selfModifyingCode = false + + private var registerDisplayList: ArrayList? = null + private var memoryDisplayList: ArrayList? = null + private var filenameList: ArrayList? = null + private var code: MIPSprogram? = null + private var maxSteps = 0 + private var instructionCount = 0 + + // stream for display of command line output + private var out: PrintStream = System.out + + // each element holds 3 arguments for dump option + private var dumpTriples: ArrayList>? = null + + // optional program args for MIPS program (becomes argc, argv) + private var programArgumentList: ArrayList? = null + + // MARS command exit code to return if assemble error occurs + private var assembleErrorExitCode = 0 + + // MARS command exit code to return if simulation error occurs + private var simulateErrorExitCode = 0 + + init + { + val gui = args.isEmpty() + 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 = ArrayList() + memoryDisplayList = ArrayList() + filenameList = ArrayList() + MemoryConfigurations.setCurrentConfiguration(MemoryConfigurations.getDefaultConfiguration()) + // do NOT use Globals.program for command line MARS -- it triggers 'backstep' log. + code = MIPSprogram() + maxSteps = -1 + if (parseCommandArgs(args)) + { + if (runCommand()) + { + displayMiscellaneousPostMortem() + displayRegistersPostMortem() + displayMemoryPostMortem() + } + dumpSegments() + } + exitProcess(Globals.exitCode) + } + } + + ///////////////////////////////////////////////////////////// + // Perform any specified dump operations. See "dump" option. + // + private fun dumpSegments() + { + if (dumpTriples == null) + { + return + } + for (i in dumpTriples!!.indices) + { + val triple = dumpTriples!![i] + val file = File(triple[2]) + var segInfo = MemoryDump.getSegmentBounds(triple[0]) + // If not segment name, see if it is address range instead. DPS 14-July-2008 + if (segInfo == null) + { + try + { + val memoryRange = checkMemoryAddressRange(triple[0]) + segInfo = arrayOfNulls(2) + segInfo[0] = Integer.valueOf(Binary.stringToInt(memoryRange!![0])) // low end of range + segInfo[1] = Integer.valueOf(Binary.stringToInt(memoryRange[1])) // high end of range + } catch (nfe: NumberFormatException) + { + segInfo = null + } catch (npe: NullPointerException) + { + segInfo = null + } + } + if (segInfo == null) + { + out.println("Error while attempting to save dump, segment/address-range " + triple[0] + " is invalid!") + continue + } + val loader = DumpFormatLoader() + val dumpFormats = loader.loadDumpFormats() + val 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 + { + val highAddress = Globals.memory.getAddressOfFirstNull( + segInfo[0]!!.toInt(), segInfo[1]!!.toInt() + ) - Memory.WORD_LENGTH_BYTES + if (highAddress < segInfo[0]!!.toInt()) + { + out.println("This segment has not been written to, there is nothing to dump.") + continue + } + format.dumpMemoryRange(file, segInfo[0]!!.toInt(), highAddress) + } catch (e: FileNotFoundException) + { + out.println("Error while attempting to save dump, file $file was not found!") + continue + } catch (e: AddressErrorException) + { + out.println("Error while attempting to save dump, file " + file + "! Could not access address: " + e.address + "!") + continue + } catch (e: IOException) + { + 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 fun launchIDE() + { + SwingUtilities.invokeLater { // Turn off metal's use of bold fonts + // UIManager.put("swing.boldMetal", Boolean.FALSE); + VenusUI("MARS " + Globals.version) + } + } + + ////////////////////////////////////////////////////////////////////// + // 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 fun parseCommandArgs(args: Array): Boolean + { + val noCopyrightSwitch = "nc" + val displayMessagesToErrSwitch = "me" + var argsOK = true + var inProgramArgumentList = false + programArgumentList = null + if (args.size == 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.size == 1 && args[0] == "h") + { + displayHelp() + return false + } + var i = 0 + while (i < args.size) + { + + // 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 = ArrayList() + } + programArgumentList!!.add(args[i]) + i++ + continue + } + // Once we hit "pa", all remaining command args are assumed + // to be program arguments. + if (args[i].equals("pa", ignoreCase = true)) + { + inProgramArgumentList = true + i++ + continue + } + // messages-to-standard-error switch already processed, so ignore. + if (args[i].lowercase(Locale.getDefault()) == displayMessagesToErrSwitch) + { + i++ + continue + } + // no-copyright switch already processed, so ignore. + if (args[i].lowercase(Locale.getDefault()) == noCopyrightSwitch) + { + i++ + continue + } + if (args[i].equals("dump", ignoreCase = true)) + { + if (args.size <= i + 3) + { + out.println("Dump command line argument requires a segment, format and file name.") + argsOK = false + } else + { + if (dumpTriples == null) + { + dumpTriples = ArrayList() + } + dumpTriples!!.add(arrayOf(args[++i], args[++i], args[++i])) + // simulate = false; + } + i++ + continue + } + if (args[i].equals("mc", ignoreCase = true)) + { + val configName = args[++i] + val config = MemoryConfigurations.getConfigurationByName(configName) + if (config == null) + { + out.println("Invalid memory configuration: $configName") + argsOK = false + } else + { + MemoryConfigurations.setCurrentConfiguration(config) + } + i++ + continue + } + // Set MARS exit code for assemble error + if (args[i].lowercase(Locale.getDefault()).indexOf("ae") == 0) + { + val s = args[i].substring(2) + try + { + assembleErrorExitCode = Integer.decode(s).toInt() + i++ + continue + } catch (nfe: NumberFormatException) + { + // Let it fall thru and get handled by catch-all + } + } + // Set MARS exit code for simulate error + if (args[i].lowercase(Locale.getDefault()).indexOf("se") == 0) + { + val s = args[i].substring(2) + try + { + simulateErrorExitCode = Integer.decode(s).toInt() + i++ + continue + } catch (nfe: NumberFormatException) + { + // Let it fall thru and get handled by catch-all + } + } + if (args[i].equals("d", ignoreCase = true)) + { + Globals.debug = true + i++ + continue + } + if (args[i].equals("a", ignoreCase = true)) + { + simulate = false + i++ + continue + } + if (args[i].equals("ad", ignoreCase = true) || + args[i].equals("da", ignoreCase = true) + ) + { + Globals.debug = true + simulate = false + i++ + continue + } + if (args[i].equals("p", ignoreCase = true)) + { + assembleProject = true + i++ + continue + } + if (args[i].equals("dec", ignoreCase = true)) + { + displayFormat = DECIMAL + i++ + continue + } + if (args[i].equals("hex", ignoreCase = true)) + { + displayFormat = HEXADECIMAL + i++ + continue + } + if (args[i].equals("ascii", ignoreCase = true)) + { + displayFormat = ASCII + i++ + continue + } + if (args[i].equals("b", ignoreCase = true)) + { + verbose = false + i++ + continue + } + if (args[i].equals("db", ignoreCase = true)) + { + delayedBranching = true + i++ + continue + } + if (args[i].equals("np", ignoreCase = true) || args[i].equals("ne", ignoreCase = true)) + { + pseudo = false + i++ + continue + } + if (args[i].equals("we", ignoreCase = true)) + { // added 14-July-2008 DPS + warningsAreErrors = true + i++ + continue + } + if (args[i].equals("sm", ignoreCase = true)) + { // added 17-Dec-2009 DPS + startAtMain = true + i++ + continue + } + if (args[i].equals("smc", ignoreCase = true)) + { // added 5-Jul-2013 DPS + selfModifyingCode = true + i++ + continue + } + if (args[i].equals("ic", ignoreCase = true)) + { // added 19-Jul-2012 DPS + countInstructions = true + i++ + 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]) + } + 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]) + i++ + continue + } + if (File(args[i]).exists()) + { // is it a file name? + filenameList!!.add(args[i]) + i++ + continue + } + // Check for stand-alone integer, which is the max execution steps option + try + { + Integer.decode(args[i]) + maxSteps = Integer.decode(args[i]).toInt() // if we got here, it has to be OK + i++ + continue + } catch (ignored: NumberFormatException) + { + } + // Check for integer address range (m-n) + try + { + val memoryRange = checkMemoryAddressRange(args[i]) + memoryDisplayList!!.add(memoryRange!![0]) // low end of range + memoryDisplayList!!.add(memoryRange[1]) // high end of range + i++ + continue + } catch (nfe: NumberFormatException) + { + out.println("Invalid/unaligned address or invalid range: " + args[i]) + argsOK = false + i++ + continue + } catch (npe: NullPointerException) + { + // Do nothing. next statement will handle it + } + out.println("Invalid Command Argument: " + args[i]) + argsOK = false + i++ + } + return argsOK + } + + ////////////////////////////////////////////////////////////////////// + // Carry out the mars command: assemble then optionally run + // Returns false if no simulation (run) occurs, true otherwise. + private fun runCommand(): Boolean + { + var 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) + val mainFile = File(filenameList!![0]).absoluteFile // First file is "main" file + val filesToAssemble: ArrayList<*> + if (assembleProject) + { + filesToAssemble = FilenameFinder.getFilenameList(mainFile.parent, 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!!.removeAt(0) // first one has already been processed + val moreFilesToAssemble = + FilenameFinder.getFilenameList(filenameList, FilenameFinder.MATCH_ALL_EXTENSIONS) + // Remove any duplicates then merge the two lists. + var index2 = 0 + while (index2 < moreFilesToAssemble.size) + { + for (index1 in filesToAssemble.indices) + { + if (filesToAssemble[index1] == moreFilesToAssemble[index2]) + { + moreFilesToAssemble.removeAt(index2) + index2-- // adjust for left shift in moreFilesToAssemble... + break // break out of inner loop... + } + } + index2++ + } + filesToAssemble.addAll(moreFilesToAssemble) + } + } else + { + filesToAssemble = FilenameFinder.getFilenameList(filenameList, FilenameFinder.MATCH_ALL_EXTENSIONS) + } + if (Globals.debug) + { + out.println("-------- TOKENIZING BEGINS -----------") + } + val MIPSprogramsToAssemble = code!!.prepareFilesForAssembly(filesToAssemble, mainFile.absolutePath, null) + if (Globals.debug) + { + out.println("-------- ASSEMBLY BEGINS -----------") + } + // Added logic to check for warnings and print if any. DPS 11/28/06 + val 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 + ProgramArgumentList(programArgumentList).storeProgramArguments() + // establish observer if specified + establishObserver() + if (Globals.debug) + { + out.println("-------- SIMULATION BEGINS -----------") + } + programRan = true + val 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 (e: ProcessingException) + { + Globals.exitCode = if (programRan) simulateErrorExitCode else 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. + @Throws(NumberFormatException::class) + private fun checkMemoryAddressRange(arg: String): Array? + { + var memoryRange: Array? = 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 = arrayOf("", "") + 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 NumberFormatException() + } + } + return memoryRange + } + + ///////////////////////////////////////////////////////////////// + // Required for counting instructions executed, if that option is specified. + // DPS 19 July 2012 + private fun establishObserver() + { + if (countInstructions) + { + val instructionCounter: Observer = object : Observer + { + private var lastAddress = 0 + override fun update(o: Observable, obj: Any) + { + if (obj is AccessNotice) + { + val notice = obj + if (!notice.accessIsFromMIPS()) + { + return + } + if (notice.accessType != AccessNotice.READ) + { + return + } + val m = notice as MemoryAccessNotice + val a = m.address + if (a == lastAddress) + { + return + } + lastAddress = a + instructionCount++ + } + } + } + try + { + Globals.memory.addObserver(instructionCounter, Memory.textBaseAddress, Memory.textLimitAddress) + } catch (aee: AddressErrorException) + { + 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 fun displayMiscellaneousPostMortem() + { + if (countInstructions) + { + out.println( + """ + + $instructionCount + """.trimIndent() + ) + } + } + + ////////////////////////////////////////////////////////////////////// + // Displays requested register or registers + private fun displayRegistersPostMortem() + { + var value: Int // handy local to use throughout the next couple loops + var strValue: String + // Display requested register contents + out.println() + val regIter: Iterator = registerDisplayList!!.iterator() + while (regIter.hasNext()) + { + val reg = regIter.next() + if (RegisterFile.getUserRegister(reg) != null) + { + // integer register + if (verbose) + { + out.print(reg + "\t") + } + value = RegisterFile.getUserRegister(reg).value + out.println(formatIntForDisplay(value)) + } else + { + // floating point register + val fvalue = Coprocessor1.getFloatFromRegister(reg) + val ivalue = Coprocessor1.getIntFromRegister(reg) + var dvalue = Double.NaN + var lvalue: Long = 0 + var hasDouble = false + try + { + dvalue = Coprocessor1.getDoubleFromRegisterPair(reg) + lvalue = Coprocessor1.getLongFromRegisterPair(reg) + hasDouble = true + } catch (irae: InvalidRegisterAccessException) + { + } + 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 fun formatIntForDisplay(value: Int): String + { + val strValue: String + strValue = when (displayFormat) + { + DECIMAL -> "" + value + HEXADECIMAL -> Binary.intToHexString(value) + ASCII -> Binary.intToAscii(value) + else -> Binary.intToHexString(value) + } + return strValue + } + + ////////////////////////////////////////////////////////////////////// + // Displays requested memory range or ranges + private fun displayMemoryPostMortem() + { + var value: Int + // Display requested memory range contents + val memIter: Iterator = memoryDisplayList!!.iterator() + var addressStart = 0 + var 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 (nfe: NumberFormatException) + { + } + var valuesDisplayed = 0 + var addr = addressStart + while (addr <= addressEnd) + { + if (addr < 0 && addressEnd > 0) + { + break // happens only if addressEnd is 0x7ffffffc + } + if (valuesDisplayed % memoryWordsPerLine == 0) + { + out.print(if (valuesDisplayed > 0) "\n" else "") + if (verbose) + { + out.print("Mem[" + Binary.intToHexString(addr) + "]\t") + } + } + try + { + // Allow display of binary text segment (machine code) DPS 14-July-2008 + value = if (Memory.inTextSegment(addr) || Memory.inKernelTextSegment(addr)) + { + val iValue = Globals.memory.getRawWordOrNull(addr) + iValue?.toInt() ?: 0 + } else + { + Globals.memory.getWord(addr) + } + out.print(formatIntForDisplay(value) + "\t") + } catch (aee: AddressErrorException) + { + out.print("Invalid address: $addr\t") + } + valuesDisplayed++ + addr += Memory.WORD_LENGTH_BYTES + } + 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 fun processDisplayMessagesToErrSwitch(args: Array, displayMessagesToErrSwitch: String) + { + for (i in args.indices) + { + if (args[i].lowercase(Locale.getDefault()) == displayMessagesToErrSwitch) + { + out = System.err + return + } + } + } + + /////////////////////////////////////////////////////////////////////// + // Decide whether copyright should be displayed, and display + // if so. + private fun displayCopyright(args: Array, noCopyrightSwitch: String) + { + val print = true + for (i in args.indices) + { + if (args[i].lowercase(Locale.getDefault()) == noCopyrightSwitch) + { + return + } + } + out.println( + """MARS ${Globals.version} Copyright ${Globals.copyrightYears} ${Globals.copyrightHolders} +""" + ) + } + + /////////////////////////////////////////////////////////////////////// + // Display command line help text + private fun displayHelp() + { + val segmentNames = MemoryDump.getSegmentNames() + var segments = "" + for (i in segmentNames.indices) + { + segments += segmentNames[i] + if (i < segmentNames.size - 1) + { + segments += ", " + } + } + val dumpFormats = DumpFormatLoader().loadDumpFormats() + var formats = "" + for (i in dumpFormats.indices) + { + formats += (dumpFormats[i] as DumpFormat).commandDescriptor + 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 -- terminate MARS with integer exit code 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 -- 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(" = $segments") + out.println(" = $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 -- set memory configuration. Argument 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 -- terminate MARS with integer exit code 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(" -- where is an integer maximum count of steps to simulate.") + out.println(" If 0, negative or not specified, there is no maximum.") + out.println(" $ -- where 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(" -- where 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("- -- memory address range from to whose contents to") + out.println(" display at end of run. and may be hex or decimal,") + out.println(" must be on word boundary, <= . 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.") + } + + companion object + { + private const val rangeSeparator = "-" + private const val memoryWordsPerLine = 4 // display 4 memory words, tab separated, per line + private const val DECIMAL = 0 // memory and register display format + private const val HEXADECIMAL = 1 // memory and register display format + private const val ASCII = 2 // memory and register display format + } +}