Files
EMARS/src/main/java/mars/venus/RegistersWindow.java
T
2022-11-12 17:06:02 -05:00

538 lines
20 KiB
Java

package mars.venus;
import mars.Globals;
import mars.Settings;
import mars.mips.hardware.AccessNotice;
import mars.mips.hardware.Register;
import mars.mips.hardware.RegisterAccessNotice;
import mars.mips.hardware.RegisterFile;
import mars.simulator.Simulator;
import mars.simulator.SimulatorNotice;
import mars.util.Binary;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Observable;
import java.util.Observer;
/*
Copyright (c) 2003-2009, 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)
*/
/**
* Sets up a window to display registers in the UI.
*
* @author Sanderson, Bumgarner
**/
public class RegistersWindow extends JPanel implements Observer
{
private static final int NAME_COLUMN = 0;
private static final int NUMBER_COLUMN = 1;
private static final int VALUE_COLUMN = 2;
private static JTable table;
private static Register[] registers;
private static Settings settings;
private Object[][] tableData;
private boolean highlighting;
private int highlightRow;
private ExecutePane executePane;
/**
* Constructor which sets up a fresh window with a table that contains the register values.
**/
public RegistersWindow()
{
Simulator.getInstance().addObserver(this);
settings = Globals.getSettings();
this.highlighting = false;
table = new MyTippedJTable(new RegTableModel(setupWindow()));
table.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(25);
table.getColumnModel().getColumn(NUMBER_COLUMN).setPreferredWidth(25);
table.getColumnModel().getColumn(VALUE_COLUMN).setPreferredWidth(60);
// Display register values (String-ified) right-justified in mono font
table.getColumnModel().getColumn(NAME_COLUMN).setCellRenderer(new RegisterCellRenderer(MonoRightCellRenderer.MONOSPACED_PLAIN_12POINT, SwingConstants.LEFT));
table.getColumnModel().getColumn(NUMBER_COLUMN).setCellRenderer(new RegisterCellRenderer(MonoRightCellRenderer.MONOSPACED_PLAIN_12POINT, SwingConstants.RIGHT));
table.getColumnModel().getColumn(VALUE_COLUMN).setCellRenderer(new RegisterCellRenderer(MonoRightCellRenderer.MONOSPACED_PLAIN_12POINT, SwingConstants.RIGHT));
table.setPreferredScrollableViewportSize(new Dimension(200, 700));
this.setLayout(new BorderLayout()); // table display will occupy entire width if widened
this.add(new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
}
/**
* Sets up the data for the window.
*
* @return The array object with the data for the window.
**/
public Object[][] setupWindow()
{
int valueBase = NumberDisplayBaseChooser.getBase(settings.getDisplayValuesInHex());
tableData = new Object[35][3];
registers = RegisterFile.getRegisters();
for (int i = 0; i < registers.length; i++)
{
tableData[i][0] = registers[i].getName();
tableData[i][1] = Integer.valueOf(registers[i].getNumber());
tableData[i][2] = NumberDisplayBaseChooser.formatNumber(registers[i].getValue(), valueBase);
}
tableData[32][0] = "pc";
tableData[32][1] = "";//new Integer(32);
tableData[32][2] = NumberDisplayBaseChooser.formatUnsignedInteger(RegisterFile.getPc(), valueBase);
tableData[33][0] = "hi";
tableData[33][1] = "";//new Integer(33);
tableData[33][2] = NumberDisplayBaseChooser.formatNumber(RegisterFile.getValue(33), valueBase);
tableData[34][0] = "lo";
tableData[34][1] = "";//new Integer(34);
tableData[34][2] = NumberDisplayBaseChooser.formatNumber(RegisterFile.getValue(34), valueBase);
return tableData;
}
/**
* clear and redisplay registers
*/
public void clearWindow()
{
this.clearHighlighting();
RegisterFile.resetRegisters();
this.updateRegisters(Globals.getGui().getMainPane().getExecutePane().getValueDisplayBase());
}
/**
* Clear highlight background color from any cell currently highlighted.
*/
public void clearHighlighting()
{
highlighting = false;
if (table != null)
{
table.tableChanged(new TableModelEvent(table.getModel()));
}
highlightRow = -1; // assure highlight will not occur upon re-assemble.
}
/**
* Refresh the table, triggering re-rendering.
*/
public void refresh()
{
if (table != null)
{
table.tableChanged(new TableModelEvent(table.getModel()));
}
}
/**
* update register display using current number base (10 or 16)
*/
public void updateRegisters()
{
updateRegisters(Globals.getGui().getMainPane().getExecutePane().getValueDisplayBase());
}
/**
* update register display using specified number base (10 or 16)
*
* @param base desired number base
*/
public void updateRegisters(int base)
{
registers = RegisterFile.getRegisters();
for (int i = 0; i < registers.length; i++)
{
updateRegisterValue(registers[i].getNumber(), registers[i].getValue(), base);
}
updateRegisterUnsignedValue(32, RegisterFile.getPc(), base);
updateRegisterValue(33, RegisterFile.getValue(33), base);
updateRegisterValue(34, RegisterFile.getValue(34), base);
}
/**
* This method handles the updating of the GUI.
*
* @param number The number of the register to update.
* @param val New value.
**/
public void updateRegisterValue(int number, int val, int base)
{
((RegTableModel) table.getModel()).setDisplayAndModelValueAt(NumberDisplayBaseChooser.formatNumber(val, base), number, 2);
}
private void updateRegisterUnsignedValue(int number, int val, int base)
{
((RegTableModel) table.getModel()).setDisplayAndModelValueAt(NumberDisplayBaseChooser.formatUnsignedInteger(val, base), number, 2);
}
/**
* Required by Observer interface. Called when notified by an Observable that we are registered with. Observables
* include: The Simulator object, which lets us know when it starts and stops running A register object, which lets
* us know of register operations The Simulator keeps us informed of when simulated MIPS execution is active. This
* is the only time we care about register operations.
*
* @param observable The Observable object who is notifying us
* @param obj Auxiliary object with additional information.
*/
public void update(Observable observable, Object obj)
{
if (observable == mars.simulator.Simulator.getInstance())
{
SimulatorNotice notice = (SimulatorNotice) obj;
if (notice.getAction() == SimulatorNotice.SIMULATOR_START)
{
// Simulated MIPS execution starts. Respond to memory changes if running in timed
// or stepped mode.
if (notice.getRunSpeed() != RunSpeedPanel.UNLIMITED_SPEED || notice.getMaxSteps() == 1)
{
System.out.println("Running in timed mode");
RegisterFile.addRegistersObserver(this);
this.highlighting = true;
}
}
else
{
// Simulated MIPS execution stops. Stop responding.
RegisterFile.deleteRegistersObserver(this);
}
}
else if (obj instanceof RegisterAccessNotice)
{
// NOTE: each register is a separate Observable
RegisterAccessNotice access = (RegisterAccessNotice) obj;
if (access.getAccessType() == AccessNotice.WRITE)
{
// Uses the same highlighting technique as for Text Segment -- see
// AddressCellRenderer class in DataSegmentWindow.java.
this.highlighting = true;
this.highlightCellForRegister((Register) observable);
Globals.getGui().getRegistersPane().setSelectedComponent(this);
}
}
}
/**
* Highlight the row corresponding to the given register.
*
* @param register Register object corresponding to row to be selected.
*/
void highlightCellForRegister(Register register)
{
this.highlightRow = register.getNumber();
// Tell the system that table contents have changed. This will trigger re-rendering
// during which cell renderers are obtained. The row of interest (identified by
// instance variabls this.registerRow) will get a renderer
// with highlight background color and all others get renderer with default background.
table.tableChanged(new TableModelEvent(table.getModel()));
}
/*
* Cell renderer for displaying register entries. This does highlighting, so if you
* don't want highlighting for a given column, don't use this. Currently we highlight
* all columns.
*/
private class RegisterCellRenderer extends DefaultTableCellRenderer
{
private final Font font;
private final int alignment;
public RegisterCellRenderer(Font font, int alignment)
{
super();
this.font = font;
this.alignment = alignment;
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel cell = (JLabel) super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
cell.setFont(font);
cell.setHorizontalAlignment(alignment);
if (settings.getRegistersHighlighting() && highlighting && row == highlightRow)
{
cell.setBackground(settings.getColorSettingByPosition(Settings.REGISTER_HIGHLIGHT_BACKGROUND));
cell.setForeground(settings.getColorSettingByPosition(Settings.REGISTER_HIGHLIGHT_FOREGROUND));
cell.setFont(settings.getFontByPosition(Settings.REGISTER_HIGHLIGHT_FONT));
}
else if (row % 2 == 0)
{
cell.setBackground(settings.getColorSettingByPosition(Settings.EVEN_ROW_BACKGROUND));
cell.setForeground(settings.getColorSettingByPosition(Settings.EVEN_ROW_FOREGROUND));
cell.setFont(settings.getFontByPosition(Settings.EVEN_ROW_FONT));
}
else
{
cell.setBackground(settings.getColorSettingByPosition(Settings.ODD_ROW_BACKGROUND));
cell.setForeground(settings.getColorSettingByPosition(Settings.ODD_ROW_FOREGROUND));
cell.setFont(settings.getFontByPosition(Settings.ODD_ROW_FONT));
}
return cell;
}
}
////////////////////////////////////////////////////////////////////////////
class RegTableModel extends AbstractTableModel
{
final String[] columnNames = {"Name", "Number", "Value"};
Object[][] data;
public RegTableModel(Object[][] d)
{
data = d;
}
public int getColumnCount()
{
return columnNames.length;
}
public int getRowCount()
{
return data.length;
}
public String getColumnName(int col)
{
return columnNames[col];
}
public Object getValueAt(int row, int col)
{
return data[row][col];
}
/*
* JTable uses this method to determine the default renderer/
* editor for each cell.
*/
public Class getColumnClass(int c)
{
return getValueAt(0, c).getClass();
}
/*
* Don't need to implement this method unless your table's
* editable.
*/
public boolean isCellEditable(int row, int col)
{
//Note that the data/cell address is constant,
//no matter where the cell appears onscreen.
// these registers are not editable: $zero (0), $pc (32), $ra (31)
return col == VALUE_COLUMN && row != 0 && row != 32 && row != 31;
}
/*
* Update cell contents in table model. This method should be called
* only when user edits cell, so input validation has to be done. If
* value is valid, MIPS register is updated.
*/
public void setValueAt(Object value, int row, int col)
{
int val = 0;
try
{
val = Binary.stringToInt((String) value);
}
catch (NumberFormatException nfe)
{
data[row][col] = "INVALID";
fireTableCellUpdated(row, col);
return;
}
// Assures that if changed during MIPS program execution, the update will
// occur only between MIPS instructions.
synchronized (Globals.memoryAndRegistersLock)
{
RegisterFile.updateRegister(row, val);
}
int valueBase = Globals.getGui().getMainPane().getExecutePane().getValueDisplayBase();
data[row][col] = NumberDisplayBaseChooser.formatNumber(val, valueBase);
fireTableCellUpdated(row, col);
}
/**
* Update cell contents in table model. Does not affect MIPS register.
*/
private void setDisplayAndModelValueAt(Object value, int row, int col)
{
data[row][col] = value;
fireTableCellUpdated(row, col);
}
// handy for debugging....
private void printDebugData()
{
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i = 0; i < numRows; i++)
{
System.out.print(" row " + i + ":");
for (int j = 0; j < numCols; j++)
{
System.out.print(" " + data[i][j]);
}
System.out.println();
}
System.out.println("--------------------------");
}
}
///////////////////////////////////////////////////////////////////
//
// JTable subclass to provide custom tool tips for each of the
// register table column headers and for each register name in
// the first column. From Sun's JTable tutorial.
// http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
//
private class MyTippedJTable extends JTable
{
private final String[] regToolTips = {
/* $zero */ "constant 0",
/* $at */ "reserved for assembler",
/* $v0 */ "expression evaluation and results of a function",
/* $v1 */ "expression evaluation and results of a function",
/* $a0 */ "argument 1",
/* $a1 */ "argument 2",
/* $a2 */ "argument 3",
/* $a3 */ "argument 4",
/* $t0 */ "temporary (not preserved across call)",
/* $t1 */ "temporary (not preserved across call)",
/* $t2 */ "temporary (not preserved across call)",
/* $t3 */ "temporary (not preserved across call)",
/* $t4 */ "temporary (not preserved across call)",
/* $t5 */ "temporary (not preserved across call)",
/* $t6 */ "temporary (not preserved across call)",
/* $t7 */ "temporary (not preserved across call)",
/* $s0 */ "saved temporary (preserved across call)",
/* $s1 */ "saved temporary (preserved across call)",
/* $s2 */ "saved temporary (preserved across call)",
/* $s3 */ "saved temporary (preserved across call)",
/* $s4 */ "saved temporary (preserved across call)",
/* $s5 */ "saved temporary (preserved across call)",
/* $s6 */ "saved temporary (preserved across call)",
/* $s7 */ "saved temporary (preserved across call)",
/* $t8 */ "temporary (not preserved across call)",
/* $t9 */ "temporary (not preserved across call)",
/* $k0 */ "reserved for OS kernel",
/* $k1 */ "reserved for OS kernel",
/* $gp */ "pointer to global area",
/* $sp */ "stack pointer",
/* $fp */ "frame pointer",
/* $ra */ "return address (used by function call)",
/* pc */ "program counter",
/* hi */ "high-order word of multiply product, or divide remainder",
/* lo */ "low-order word of multiply product, or divide quotient"
};
private final String[] columnToolTips = {
/* name */ "Each register has a tool tip describing its usage convention",
/* number */ "Corresponding register number",
/* value */ "Current 32 bit value"
};
MyTippedJTable(RegTableModel m)
{
super(m);
this.setRowSelectionAllowed(true); // highlights background color of entire row
this.setSelectionBackground(Color.GREEN);
}
//Implement table cell tool tips.
public String getToolTipText(MouseEvent e)
{
String tip = null;
java.awt.Point p = e.getPoint();
int rowIndex = rowAtPoint(p);
int colIndex = columnAtPoint(p);
int realColumnIndex = convertColumnIndexToModel(colIndex);
if (realColumnIndex == NAME_COLUMN)
{ //Register name column
tip = regToolTips[rowIndex];
/* You can customize each tip to encorporiate cell contents if you like:
TableModel model = getModel();
String regName = (String)model.getValueAt(rowIndex,0);
....... etc .......
*/
}
else
{
//You can omit this part if you know you don't have any
//renderers that supply their own tool tips.
tip = super.getToolTipText(e);
}
return tip;
}
//Implement table header tool tips.
protected JTableHeader createDefaultTableHeader()
{
return
new JTableHeader(columnModel)
{
public String getToolTipText(MouseEvent e)
{
String tip = null;
java.awt.Point p = e.getPoint();
int index = columnModel.getColumnIndexAtX(p.x);
int realIndex = columnModel.getColumn(index).getModelIndex();
return columnToolTips[realIndex];
}
};
}
}
}