0da1c5dcca
First commit of the 4.5 version (latest version available)
819 lines
38 KiB
Java
819 lines
38 KiB
Java
package mars.tools;
|
|
import javax.swing.*;
|
|
import javax.swing.border.*;
|
|
import javax.swing.filechooser.FileFilter;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.util.*;
|
|
import java.io.*;
|
|
import mars.*;
|
|
import mars.util.*;
|
|
import mars.tools.*;
|
|
import mars.mips.hardware.*;
|
|
|
|
/*
|
|
Copyright (c) 2003-2008, 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)
|
|
*/
|
|
|
|
/**
|
|
* An abstract class that provides generic components to facilitate implementation of
|
|
* a MarsTool and/or stand-alone Mars-based application. Provides default definitions
|
|
* of both the action() method required to implement MarsTool and the go() method
|
|
* conventionally used to launch a Mars-based stand-alone application. It also provides
|
|
* generic definitions for interactively controlling the application. The generic controls
|
|
* for MarsTools are 3 buttons: connect/disconnect to MIPS resource (memory and/or
|
|
* registers), reset, and close (exit). The generic controls for stand-alone Mars apps
|
|
* include: button that triggers a file open dialog, a text field to display status
|
|
* messages, the run-speed slider to control execution rate when running a MIPS program,
|
|
* a button that assembles and runs the current MIPS program, a button to interrupt
|
|
* the running MIPS program, a reset button, and an exit button.
|
|
* Pete Sanderson, 14 November 2006.
|
|
*/
|
|
public abstract class AbstractMarsToolAndApplication extends JFrame implements MarsTool, Observer {
|
|
protected boolean isBeingUsedAsAMarsTool = false; // can use to determine whether invoked as MarsTool or stand-alone.
|
|
protected AbstractMarsToolAndApplication thisMarsApp;
|
|
private JDialog dialog; // used only for MarsTool use. This is the pop-up dialog that appears when menu item selected.
|
|
protected Window theWindow; // highest level GUI component (a JFrame for app, a JDialog for MarsTool)
|
|
|
|
// Major GUI components
|
|
JLabel headingLabel;
|
|
private String title; // descriptive title for title bar provided to constructor.
|
|
private String heading; // Text to be displayed in the top portion of the main window.
|
|
|
|
// Some GUI settings
|
|
private EmptyBorder emptyBorder = new EmptyBorder(4,4,4,4);
|
|
private Color backgroundColor = Color.WHITE;
|
|
|
|
|
|
private int lowMemoryAddress = Memory.dataSegmentBaseAddress;
|
|
private int highMemoryAddress = Memory.stackBaseAddress;
|
|
// For MarsTool, is set true when "Connect" clicked, false when "Disconnect" clicked.
|
|
// For app, is set true when "Assemble and Run" clicked, false when program terminates.
|
|
private volatile boolean observing = false;
|
|
|
|
// Several structures required for stand-alone use only (not MarsTool use)
|
|
private File mostRecentlyOpenedFile = null;
|
|
private Runnable interactiveGUIUpdater = new GUIUpdater();
|
|
private MessageField operationStatusMessages;
|
|
private JButton openFileButton, assembleRunButton, stopButton;
|
|
private boolean multiFileAssemble = false;
|
|
|
|
// Structure required for MarsTool use only (not stand-alone use). Want subclasses to have access.
|
|
protected ConnectButton connectButton;
|
|
|
|
|
|
/**
|
|
* Simple constructor
|
|
* @param title String containing title bar text
|
|
*/
|
|
protected AbstractMarsToolAndApplication(String title,String heading) {
|
|
thisMarsApp = this;
|
|
this.title = title;
|
|
this.heading = heading;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////// ABSTRACT METHODS ///////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Required MarsTool method to return Tool name. Must be defined by subclass.
|
|
* @return Tool name. MARS will display this in menu item.
|
|
*/
|
|
public abstract String getName();
|
|
|
|
/**
|
|
* Abstract method that must be instantiated by subclass to build the main display area
|
|
* of the GUI. It will be placed in the CENTER area of a BorderLayout. The title
|
|
* is in the NORTH area, and the controls are in the SOUTH area.
|
|
*/
|
|
protected abstract JComponent buildMainDisplayArea();
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////// METHODS WITH DEFAULT IMPLEMENTATIONS //////////////////
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Run the simulator as stand-alone application. For this default implementation,
|
|
* the user-defined main display of the user interface is identical for both stand-alone
|
|
* and MARS Tools menu use, but the control buttons are different because the stand-alone
|
|
* must include a mechansim for controlling the opening, assembling, and executing of
|
|
* an underlying MIPS program. The generic controls include: a button that triggers a
|
|
* file open dialog, a text field to display status messages, the run-speed slider
|
|
* to control execution rate when running a MIPS program, a button that assembles and
|
|
* runs the current MIPS program, a reset button, and an exit button.
|
|
* This method calls 3 methods that can be defined/overriden in the subclass: initializePreGUI()
|
|
* for any special initialization that must be completed before building the user
|
|
* interface (e.g. data structures whose properties determine default GUI settings),
|
|
* initializePostGUI() for any special initialization that cannot be
|
|
* completed until after the building the user interface (e.g. data structure whose
|
|
* properties are determined by default GUI settings), and buildMainDisplayArea()
|
|
* to contain application-specific displays of parameters and results.
|
|
*/
|
|
public void go() {
|
|
theWindow = this;
|
|
this.isBeingUsedAsAMarsTool = false;
|
|
thisMarsApp.setTitle(this.title);
|
|
mars.Globals.initialize(true);
|
|
// assure the dialog goes away if user clicks the X
|
|
thisMarsApp.addWindowListener(
|
|
new WindowAdapter() {
|
|
public void windowClosing(WindowEvent e) {
|
|
performAppClosingDuties();
|
|
}
|
|
});
|
|
initializePreGUI();
|
|
|
|
JPanel contentPane = new JPanel(new BorderLayout(5,5));
|
|
contentPane.setBorder(emptyBorder);
|
|
contentPane.setOpaque(true);
|
|
contentPane.add(buildHeadingArea(), BorderLayout.NORTH);
|
|
contentPane.add(buildMainDisplayArea(), BorderLayout.CENTER);
|
|
contentPane.add(buildButtonAreaStandAlone(), BorderLayout.SOUTH);
|
|
|
|
thisMarsApp.setContentPane(contentPane);
|
|
thisMarsApp.pack();
|
|
thisMarsApp.setLocationRelativeTo(null); // center on screen
|
|
thisMarsApp.setVisible(true);
|
|
initializePostGUI();
|
|
}
|
|
|
|
|
|
/**
|
|
* Required MarsTool method to carry out Tool functions. It is invoked when MARS
|
|
* user selects this tool from the Tools menu. This default implementation provides
|
|
* generic definitions for interactively controlling the tool. The generic controls
|
|
* for MarsTools are 3 buttons: connect/disconnect to MIPS resource (memory and/or
|
|
* registers), reset, and close (exit). Like "go()" above, this default version
|
|
* calls 3 methods that can be defined/overriden in the subclass: initializePreGUI()
|
|
* for any special initialization that must be completed before building the user
|
|
* interface (e.g. data structures whose properties determine default GUI settings),
|
|
* initializePostGUI() for any special initialization that cannot be
|
|
* completed until after the building the user interface (e.g. data structure whose
|
|
* properties are determined by default GUI settings), and buildMainDisplayArea()
|
|
* to contain application-specific displays of parameters and results.
|
|
*/
|
|
|
|
public void action() {
|
|
this.isBeingUsedAsAMarsTool = true;
|
|
dialog = new JDialog(Globals.getGui(), this.title);
|
|
// assure the dialog goes away if user clicks the X
|
|
dialog.addWindowListener(
|
|
new WindowAdapter() {
|
|
public void windowClosing(WindowEvent e) {
|
|
performToolClosingDuties();
|
|
}
|
|
});
|
|
theWindow = dialog;
|
|
initializePreGUI();
|
|
JPanel contentPane = new JPanel(new BorderLayout(5,5));
|
|
contentPane.setBorder(emptyBorder);
|
|
contentPane.setOpaque(true);
|
|
contentPane.add(buildHeadingArea(), BorderLayout.NORTH);
|
|
contentPane.add(buildMainDisplayArea(),BorderLayout.CENTER);
|
|
contentPane.add(buildButtonAreaMarsTool(), BorderLayout.SOUTH);
|
|
initializePostGUI();
|
|
dialog.setContentPane(contentPane);
|
|
dialog.pack();
|
|
dialog.setLocationRelativeTo(Globals.getGui());
|
|
dialog.setVisible(true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Method that will be called once just before the GUI is constructed in the go() and action()
|
|
* methods. Use it to initialize any data structures needed for the application whose values
|
|
* will be needed to determine the initial state of GUI components. By default it does nothing.
|
|
*/
|
|
protected void initializePreGUI() {
|
|
}
|
|
|
|
/**
|
|
* Method that will be called once just after the GUI is constructed in the go() and action()
|
|
* methods. Use it to initialize data structures needed for the application whose values
|
|
* may depend on the initial state of GUI components. By default it does nothing.
|
|
*/
|
|
protected void initializePostGUI() {
|
|
}
|
|
|
|
/**
|
|
* Method that will be called each time the default Reset button is clicked.
|
|
* Use it to reset any data structures and/or GUI components. By default it does nothing.
|
|
*/
|
|
protected void reset() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Constructs GUI header as label with default positioning and font. May be overridden.
|
|
*/
|
|
protected JComponent buildHeadingArea() {
|
|
// OVERALL STRUCTURE OF MESSAGE (TOP)
|
|
headingLabel = new JLabel();
|
|
Box headingPanel = Box.createHorizontalBox();//new JPanel(new BorderLayout());
|
|
headingPanel.add(Box.createHorizontalGlue());
|
|
headingPanel.add(headingLabel);
|
|
headingPanel.add(Box.createHorizontalGlue());
|
|
// Details for heading area (top)
|
|
headingLabel.setText(heading);
|
|
headingLabel.setHorizontalTextPosition(JLabel.CENTER);
|
|
headingLabel.setFont(new Font(headingLabel.getFont().getFontName(),Font.PLAIN,18));
|
|
return headingPanel;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* The MarsTool default set of controls has one row of 3 buttons. It includes a dual-purpose button to
|
|
* attach or detach simulator to MIPS memory, a button to reset the cache, and one to close the tool.
|
|
*/
|
|
protected JComponent buildButtonAreaMarsTool() {
|
|
Box buttonArea = Box.createHorizontalBox();
|
|
TitledBorder tc =new TitledBorder("Tool Control");
|
|
tc.setTitleJustification(TitledBorder.CENTER);
|
|
buttonArea.setBorder(tc);
|
|
connectButton = new ConnectButton();
|
|
connectButton.setToolTipText("Control whether tool will respond to running MIPS program");
|
|
connectButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
if (connectButton.isConnected()) {
|
|
connectButton.disconnect();
|
|
}
|
|
else {
|
|
connectButton.connect();
|
|
}
|
|
}
|
|
});
|
|
connectButton.addKeyListener(new EnterKeyListener(connectButton));
|
|
|
|
JButton resetButton = new JButton("Reset");
|
|
resetButton.setToolTipText("Reset all counters and other structures");
|
|
resetButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
reset();
|
|
}
|
|
});
|
|
resetButton.addKeyListener(new EnterKeyListener(resetButton));
|
|
|
|
JButton closeButton = new JButton("Close");
|
|
closeButton.setToolTipText("Close (exit) this tool");
|
|
closeButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
performToolClosingDuties();
|
|
}
|
|
});
|
|
closeButton.addKeyListener(new EnterKeyListener(closeButton));
|
|
|
|
// Add all the buttons...
|
|
buttonArea.add(connectButton);
|
|
buttonArea.add(Box.createHorizontalGlue());
|
|
buttonArea.add(resetButton);
|
|
buttonArea.add(Box.createHorizontalGlue());
|
|
JComponent helpComponent = getHelpComponent();
|
|
if (helpComponent != null) {
|
|
buttonArea.add(helpComponent);
|
|
buttonArea.add(Box.createHorizontalGlue());
|
|
}
|
|
buttonArea.add(closeButton);
|
|
return buttonArea;
|
|
}
|
|
|
|
/**
|
|
* The Mars stand-alone app default set of controls has two rows of controls. It includes a text field for
|
|
* displaying status messages, a button to trigger an open file dialog, the MARS run speed slider
|
|
* to control timed execution, a button to assemble and run the program, a reset button
|
|
* whose action is determined by the subclass reset() method, and an exit button.
|
|
*/
|
|
|
|
protected JComponent buildButtonAreaStandAlone() {
|
|
// Overall structure of control area (two rows).
|
|
Box operationArea = Box.createVerticalBox();
|
|
Box fileControlArea = Box.createHorizontalBox();
|
|
Box buttonArea = Box.createHorizontalBox();
|
|
operationArea.add(fileControlArea);
|
|
operationArea.add(Box.createVerticalStrut(5));
|
|
operationArea.add(buttonArea);
|
|
TitledBorder ac =new TitledBorder("Application Control");
|
|
ac.setTitleJustification(TitledBorder.CENTER);
|
|
operationArea.setBorder(ac);
|
|
|
|
// Top row of controls consists of button to launch file open operation,
|
|
// text field to show filename, and run speed slider.
|
|
openFileButton = new JButton("Open MIPS program...");
|
|
openFileButton.setToolTipText("Select MIPS program file to assemble and run");
|
|
openFileButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
JFileChooser fileChooser = new JFileChooser();
|
|
JCheckBox multiFileAssembleChoose = new JCheckBox("Assemble all in selected file's directory",multiFileAssemble);
|
|
multiFileAssembleChoose.setToolTipText("If checked, selected file will be assembled first and all other assembly files in directory will be assembled also.");
|
|
fileChooser.setAccessory(multiFileAssembleChoose);
|
|
if (mostRecentlyOpenedFile != null) {
|
|
fileChooser.setSelectedFile(mostRecentlyOpenedFile);
|
|
}
|
|
// DPS 13 June 2007. The next 4 lines add file filter to file chooser.
|
|
FileFilter defaultFileFilter = FilenameFinder.getFileFilter(Globals.fileExtensions, "Assembler Files", true);
|
|
fileChooser.addChoosableFileFilter(defaultFileFilter);
|
|
fileChooser.addChoosableFileFilter(fileChooser.getAcceptAllFileFilter());
|
|
fileChooser.setFileFilter(defaultFileFilter);
|
|
|
|
if (fileChooser.showOpenDialog(thisMarsApp) == JFileChooser.APPROVE_OPTION) {
|
|
multiFileAssemble = multiFileAssembleChoose.isSelected();
|
|
File theFile = fileChooser.getSelectedFile();
|
|
try {
|
|
theFile = theFile.getCanonicalFile();
|
|
}
|
|
catch (IOException ioe) {
|
|
// nothing to do, theFile will keep current value
|
|
}
|
|
String currentFilePath = theFile.getPath();
|
|
mostRecentlyOpenedFile = theFile;
|
|
operationStatusMessages.setText("File: "+currentFilePath);
|
|
operationStatusMessages.setCaretPosition(0);
|
|
assembleRunButton.setEnabled(true);
|
|
}
|
|
}
|
|
});
|
|
openFileButton.addKeyListener(new EnterKeyListener(openFileButton));
|
|
|
|
operationStatusMessages = new MessageField("No file open.");
|
|
operationStatusMessages.setColumns(40);
|
|
operationStatusMessages.setMargin(new Insets(0, 3, 0, 3)); //(top, left, bottom, right)
|
|
operationStatusMessages.setBackground(backgroundColor);
|
|
operationStatusMessages.setFocusable(false);
|
|
operationStatusMessages.setToolTipText("Display operation status messages");
|
|
|
|
mars.venus.RunSpeedPanel speed = mars.venus.RunSpeedPanel.getInstance();
|
|
|
|
// Bottom row of controls consists of the three buttons defined here.
|
|
assembleRunButton = new JButton("Assemble and Run");
|
|
assembleRunButton.setToolTipText("Assemble and run the currently selected MIPS program");
|
|
assembleRunButton.setEnabled(false);
|
|
assembleRunButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
assembleRunButton.setEnabled(false);
|
|
openFileButton.setEnabled(false);
|
|
stopButton.setEnabled(true);
|
|
new Thread(new CreateAssembleRunMIPSprogram()).start();
|
|
}
|
|
});
|
|
assembleRunButton.addKeyListener(new EnterKeyListener(assembleRunButton));
|
|
|
|
stopButton = new JButton("Stop");
|
|
stopButton.setToolTipText("Terminate MIPS program execution");
|
|
stopButton.setEnabled(false);
|
|
stopButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
mars.simulator.Simulator.getInstance().stopExecution(null);
|
|
}
|
|
});
|
|
stopButton.addKeyListener(new EnterKeyListener(stopButton));
|
|
|
|
JButton resetButton = new JButton("Reset");
|
|
resetButton.setToolTipText("Reset all counters and other structures");
|
|
resetButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
reset();
|
|
}
|
|
});
|
|
resetButton.addKeyListener(new EnterKeyListener(resetButton));
|
|
|
|
JButton closeButton = new JButton("Exit");
|
|
closeButton.setToolTipText("Exit this application");
|
|
closeButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
performAppClosingDuties();
|
|
}
|
|
});
|
|
closeButton.addKeyListener(new EnterKeyListener(closeButton));
|
|
|
|
|
|
// Add top row of controls...
|
|
//fileControlArea.add(Box.createHorizontalStrut(5));
|
|
|
|
Box fileDisplayBox = Box.createVerticalBox();
|
|
fileDisplayBox.add(Box.createVerticalStrut(8));
|
|
fileDisplayBox.add(operationStatusMessages);
|
|
fileDisplayBox.add(Box.createVerticalStrut(8));
|
|
fileControlArea.add(fileDisplayBox);
|
|
|
|
fileControlArea.add(Box.createHorizontalGlue());
|
|
fileControlArea.add(speed);
|
|
|
|
// Add bottom row of buttons...
|
|
|
|
buttonArea.add(openFileButton);
|
|
buttonArea.add(Box.createHorizontalGlue());
|
|
buttonArea.add(assembleRunButton);
|
|
buttonArea.add(Box.createHorizontalGlue());
|
|
buttonArea.add(stopButton);
|
|
buttonArea.add(Box.createHorizontalGlue());
|
|
buttonArea.add(resetButton);
|
|
buttonArea.add(Box.createHorizontalGlue());
|
|
JComponent helpComponent = getHelpComponent();
|
|
if (helpComponent != null) {
|
|
buttonArea.add(helpComponent);
|
|
buttonArea.add(Box.createHorizontalGlue());
|
|
}
|
|
buttonArea.add(closeButton);
|
|
return operationArea;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Rest of the methods. Some are used by stand-alone (JFrame-based) only, some are
|
|
// used by MarsTool (JDialog-based) only, others are used by both.
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Called when receiving notice of access to MIPS memory or registers. Default
|
|
* implementation of method required by Observer interface. This method will filter out
|
|
* notices originating from the MARS GUI or from direct user editing of memory or register
|
|
* displays. Only notices arising from MIPS program access are allowed in.
|
|
* It then calls two methods to be overridden by the subclass (since they do
|
|
* nothing by default): processMIPSUpdate() then updateDisplay().
|
|
* @param resource the attached MIPS resource
|
|
* @param accessNotice AccessNotice information provided by the resource
|
|
*/
|
|
public void update(Observable resource, Object accessNotice) {
|
|
if (((AccessNotice)accessNotice).accessIsFromMIPS()) {
|
|
processMIPSUpdate(resource, (AccessNotice)accessNotice);
|
|
updateDisplay();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Override this method to process a received notice from MIPS Observable (memory or register)
|
|
* It will only be called if the notice was generated as the result of MIPS instruction execution.
|
|
* By default it does nothing. After this method is complete, the updateDisplay() method will be
|
|
* invoked automatically.
|
|
*/
|
|
protected void processMIPSUpdate(Observable resource, AccessNotice notice) {
|
|
}
|
|
|
|
/**
|
|
* This method is called when tool/app is exited either through the close/exit button or the window's X box.
|
|
* Override it to perform any special housecleaning needed. By default it does nothing.
|
|
*/
|
|
protected void performSpecialClosingDuties() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Add this app/tool as an Observer of desired MIPS Observables (memory and registers).
|
|
* By default, will add as an Observer of the entire Data Segment in memory.
|
|
* Override if you want something different. Note that the Memory methods to add an
|
|
* Observer to memory are flexible (you can register for a range of addresses) but
|
|
* may throw an AddressErrorException that you need to catch.
|
|
* This method is called whenever the default "Connect" button on a MarsTool or the
|
|
* default "Assemble and run" on a stand-alone Mars app is selected. The corresponding
|
|
* NOTE: if you do not want to register as an Observer of the entire data segment
|
|
* (starts at address 0x10000000) then override this to either do some alternative
|
|
* or nothing at all. This method is also overloaded to allow arbitrary memory
|
|
* subrange.
|
|
*/
|
|
|
|
protected void addAsObserver() {
|
|
addAsObserver(lowMemoryAddress, highMemoryAddress);
|
|
}
|
|
|
|
/**
|
|
* Add this app/tool as an Observer of the specified subrange of MIPS memory. Note
|
|
* that this method is not invoked automatically like the no-argument version, but
|
|
* if you use this method, you can still take advantage of provided default deleteAsObserver()
|
|
* since it will remove the app as a memory observer regardless of the subrange
|
|
* or number of subranges it is registered for.
|
|
* @param lowEnd low end of memory address range.
|
|
* @param highEnd high end of memory address range; must be >= lowEnd
|
|
*/
|
|
|
|
protected void addAsObserver(int lowEnd, int highEnd) {
|
|
String errorMessage = "Error connecting to MIPS memory";
|
|
try {
|
|
Globals.memory.addObserver(thisMarsApp,lowEnd, highEnd);
|
|
}
|
|
catch (AddressErrorException aee) {
|
|
if (this.isBeingUsedAsAMarsTool) {
|
|
headingLabel.setText(errorMessage);
|
|
}
|
|
else {
|
|
operationStatusMessages.displayTerminatingMessage(errorMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add this app/tool as an Observer of the specified MIPS register.
|
|
*/
|
|
protected void addAsObserver(Register reg) {
|
|
if (reg != null) {
|
|
reg.addObserver(thisMarsApp);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Delete this app/tool as an Observer of MIPS Observables (memory and registers).
|
|
* By default, will delete as an Observer of memory.
|
|
* Override if you want something different.
|
|
* This method is called when the default "Disconnect" button on a MarsTool is selected or
|
|
* when the MIPS program execution triggered by the default "Assemble and run" on a stand-alone
|
|
* Mars app terminates (e.g. when the button is re-enabled).
|
|
*/
|
|
|
|
protected void deleteAsObserver() {
|
|
Globals.memory.deleteObserver(thisMarsApp);
|
|
}
|
|
|
|
/**
|
|
* Delete this app/tool as an Observer of the specified MIPS register
|
|
*/
|
|
|
|
protected void deleteAsObserver(Register reg) {
|
|
if (reg != null) {
|
|
reg.deleteObserver(thisMarsApp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Query method to let you know if the tool/app is (or could be) currently
|
|
* "observing" any MIPS resources. When running as a MarsTool, this
|
|
* will be true by default after clicking the "Connect to MIPS" button until "Disconnect
|
|
* from MIPS" is clicked. When running as a stand-alone app, this will be
|
|
* true by default after clicking the "Assemble and Run" button until until
|
|
* program execution has terminated either normally or by clicking the "Stop"
|
|
* button. The phrase "or could be" was added above because depending on how
|
|
* the tool/app operates, it may be possible to run the MIPS program without
|
|
* first registering as an Observer -- i.e. addAsObserver() is overridden and
|
|
* takes no action.
|
|
* @return true if tool/app is (or could be) currently active as an Observer.
|
|
*/
|
|
|
|
protected boolean isObserving() {
|
|
return observing;
|
|
}
|
|
|
|
/**
|
|
* Override this method to implement updating of GUI after each MIPS instruction is executed,
|
|
* while running in "timed" mode (user specifies execution speed on the slider control).
|
|
* Does nothing by default.
|
|
*/
|
|
protected void updateDisplay() {
|
|
}
|
|
|
|
/**
|
|
* Override this method to provide a JComponent (probably a JButton) of your choice
|
|
* to be placed just left of the Close/Exit button. Its anticipated use is for a
|
|
* "help" button that launches a help message or dialog. But it can be any valid
|
|
* JComponent that doesn't mind co-existing among a bunch of JButtons.
|
|
*/
|
|
protected JComponent getHelpComponent() {
|
|
return null;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//////////////////// PRIVATE HELPER METHODS //////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Closing duties for MarsTool only.
|
|
private void performToolClosingDuties() {
|
|
performSpecialClosingDuties();
|
|
if (connectButton.isConnected()) {
|
|
connectButton.disconnect();
|
|
}
|
|
dialog.setVisible(false);
|
|
dialog.dispose();
|
|
}
|
|
|
|
// Closing duties for stand-alone application only.
|
|
private void performAppClosingDuties() {
|
|
performSpecialClosingDuties();
|
|
thisMarsApp.setVisible(false);
|
|
System.exit(0);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//////////////////// PRIVATE HELPER CLASSES //////////////////////////////////
|
|
// Specialized inner classes. Either used by stand-alone (JFrame-based) only //
|
|
// or used by MarsTool (JDialog-based) only. //
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Little class for this dual-purpose button. It is used only by the MarsTool
|
|
// (not by the stand-alone app).
|
|
protected class ConnectButton extends JButton {
|
|
private static final String connectText = "Connect to MIPS";
|
|
private static final String disconnectText = "Disconnect from MIPS";
|
|
|
|
public ConnectButton() {
|
|
super();
|
|
disconnect();
|
|
}
|
|
|
|
public void connect() {
|
|
observing = true;
|
|
synchronized (Globals.memoryAndRegistersLock) {// DPS 23 July 2008
|
|
addAsObserver();
|
|
}
|
|
setText(disconnectText);
|
|
}
|
|
|
|
public void disconnect() {
|
|
synchronized (Globals.memoryAndRegistersLock) {// DPS 23 July 2008
|
|
deleteAsObserver();
|
|
}
|
|
observing = false;
|
|
setText(connectText);
|
|
}
|
|
|
|
public boolean isConnected() {
|
|
return observing;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Every control button will get one of these so when it has focus
|
|
// the Enter key can be used instead of a mouse click to perform
|
|
// its associated action. It will do nothing if no action listeners
|
|
// are attached to the button at the time of the call. Otherwise,
|
|
// it will call actionPerformed for the first action listener in the
|
|
// button's list.
|
|
protected class EnterKeyListener extends KeyAdapter {
|
|
AbstractButton myButton;
|
|
public EnterKeyListener(AbstractButton who) {
|
|
myButton = who;
|
|
}
|
|
public void keyPressed(KeyEvent e) {
|
|
if (e.getKeyChar()== KeyEvent.VK_ENTER) {
|
|
e.consume();
|
|
try {
|
|
myButton.getActionListeners()[0].actionPerformed(new ActionEvent(myButton, 0, myButton.getText()));
|
|
}
|
|
catch (ArrayIndexOutOfBoundsException oob) {
|
|
// do nothing, since there is no action listener.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// called when the Assemble and Run button is pressed. Used only by stand-alone app.
|
|
private class CreateAssembleRunMIPSprogram implements Runnable {
|
|
public void run() {
|
|
String noSupportForExceptionHandler = null; // no auto-loaded exception handlers.
|
|
// boolean extendedAssemblerEnabled = true; // In this context, no reason to constrain.
|
|
// boolean warningsAreErrors = false; // Ditto.
|
|
|
|
String exceptionHandler = null;
|
|
if (Globals.getSettings().getExceptionHandlerEnabled() &&
|
|
Globals.getSettings().getExceptionHandler() != null &&
|
|
Globals.getSettings().getExceptionHandler().length() > 0) {
|
|
exceptionHandler = Globals.getSettings().getExceptionHandler();
|
|
}
|
|
|
|
Thread.currentThread().setPriority(Thread.NORM_PRIORITY-1);
|
|
Thread.yield();
|
|
MIPSprogram program = new MIPSprogram();
|
|
mars.Globals.program = program; // Shouldn't have to do this...
|
|
String fileToAssemble = mostRecentlyOpenedFile.getPath();
|
|
ArrayList filesToAssemble = null;
|
|
if (multiFileAssemble) {// setting (check box in file open dialog) calls for multiple file assembly
|
|
filesToAssemble = FilenameFinder.getFilenameList(
|
|
new File(fileToAssemble).getParent(), Globals.fileExtensions);
|
|
}
|
|
else {
|
|
filesToAssemble = new ArrayList();
|
|
filesToAssemble.add(fileToAssemble);
|
|
}
|
|
ArrayList programsToAssemble = null;
|
|
try {
|
|
operationStatusMessages.displayNonTerminatingMessage("Assembling "+fileToAssemble);
|
|
programsToAssemble = program.prepareFilesForAssembly(filesToAssemble, fileToAssemble, exceptionHandler);
|
|
}
|
|
catch (mars.ProcessingException pe) {
|
|
operationStatusMessages.displayTerminatingMessage("Error reading file(s): "+fileToAssemble);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
program.assemble(programsToAssemble, Globals.getSettings().getExtendedAssemblerEnabled(), Globals.getSettings().getWarningsAreErrors());
|
|
}
|
|
catch (mars.ProcessingException pe) {
|
|
operationStatusMessages.displayTerminatingMessage("Assembly Error: "+fileToAssemble);
|
|
return;
|
|
}
|
|
// Moved these three register resets from before the try block to after it. 17-Dec-09 DPS.
|
|
RegisterFile.resetRegisters();
|
|
Coprocessor1.resetRegisters();
|
|
Coprocessor0.resetRegisters();
|
|
|
|
addAsObserver();
|
|
observing = true;
|
|
String terminatingMessage = "Normal termination: ";
|
|
try {
|
|
operationStatusMessages.displayNonTerminatingMessage("Running "+fileToAssemble);
|
|
program.simulate(-1); // unlimited steps
|
|
}
|
|
catch (NullPointerException npe) {
|
|
// This will occur if program execution is interrupted by Stop button.
|
|
terminatingMessage = "User interrupt: ";
|
|
}
|
|
catch (mars.ProcessingException pe) {
|
|
terminatingMessage = "Runtime error: ";
|
|
}
|
|
finally {
|
|
deleteAsObserver();
|
|
observing = false;
|
|
operationStatusMessages.displayTerminatingMessage(terminatingMessage+fileToAssemble);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Class for text message field used to update operation status when
|
|
// assembling and running MIPS programs.
|
|
private class MessageField extends JTextField {
|
|
|
|
public MessageField(String text) {
|
|
super(text);
|
|
}
|
|
|
|
private void displayTerminatingMessage(String text) {
|
|
displayMessage(text, true);
|
|
}
|
|
|
|
private void displayNonTerminatingMessage(String text) {
|
|
displayMessage(text, false);
|
|
}
|
|
|
|
private void displayMessage(String text, boolean terminating) {
|
|
SwingUtilities.invokeLater(new MessageWriter(text, terminating));
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Little inner-inner class to display processing error message on AWT thread.
|
|
// Used only by stand-alone app.
|
|
private class MessageWriter implements Runnable {
|
|
private String text;
|
|
private boolean terminatingMessage;
|
|
public MessageWriter(String text, boolean terminating) {
|
|
this.text = text;
|
|
this.terminatingMessage = terminating;
|
|
}
|
|
public void run() {
|
|
if (text!=null) {
|
|
operationStatusMessages.setText(text);
|
|
operationStatusMessages.setCaretPosition(0);
|
|
}
|
|
if (terminatingMessage) {
|
|
assembleRunButton.setEnabled(true);
|
|
openFileButton.setEnabled(true);
|
|
stopButton.setEnabled(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// For scheduling GUI update on timed runs...used only by stand-alone app.
|
|
private class GUIUpdater implements Runnable {
|
|
public void run() {
|
|
updateDisplay();
|
|
}
|
|
}
|
|
|
|
} |