0da1c5dcca
First commit of the 4.5 version (latest version available)
843 lines
41 KiB
Java
843 lines
41 KiB
Java
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 <segment> <format> <file></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 <config$gt;</tt>, where <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.");
|
|
}
|
|
|
|
}
|
|
|
|
|