[O] BitmapDisplay > Kt

This commit is contained in:
Hykilpikonna
2022-11-10 13:50:26 -05:00
parent d0771c3ccb
commit 58cc81c6fd
2 changed files with 465 additions and 647 deletions
-647
View File
@@ -1,647 +0,0 @@
package mars.tools;
import mars.mips.hardware.AccessNotice;
import mars.mips.hardware.Memory;
import mars.mips.hardware.MemoryAccessNotice;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
/*
Copyright (c) 2010-2011, 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)
*/
/**
* Bitmapp display simulator. It can be run either as a stand-alone Java application having access to the mars package,
* or through MARS as an item in its Tools menu. It makes maximum use of methods inherited from its abstract superclass
* AbstractMarsToolAndApplication. Pete Sanderson, verison 1.0, 23 December 2010.
*/
public class BitmapDisplay extends AbstractMarsToolAndApplication
{
private static final String version = "Version 1.0";
private static final String heading = "Bitmap Display";
private final String[] visualizationUnitPixelWidthChoices = {"1", "2", "4", "8", "16", "32"};
private final int defaultVisualizationUnitPixelWidthIndex = 0;
private final String[] visualizationUnitPixelHeightChoices = {"1", "2", "4", "8", "16", "32"};
private final int defaultVisualizationUnitPixelHeightIndex = 0;
private final String[] displayAreaPixelWidthChoices = {"64", "128", "256", "512", "1024"};
private final int defaultDisplayWidthIndex = 3;
private final String[] displayAreaPixelHeightChoices = {"64", "128", "256", "512", "1024"};
// Values for Combo Boxes
private final int defaultDisplayHeightIndex = 2;
// Major GUI components
private JComboBox visualizationUnitPixelWidthSelector, visualizationUnitPixelHeightSelector,
visualizationPixelWidthSelector, visualizationPixelHeightSelector, displayBaseAddressSelector;
private Graphics drawingArea;
private JPanel canvas;
private JPanel results;
// Some GUI settings
private final EmptyBorder emptyBorder = new EmptyBorder(4, 4, 4, 4);
private final Font countFonts = new Font("Times", Font.BOLD, 12);
private final Color backgroundColor = Color.WHITE;
// Values for display canvas. Note their initialization uses the identifiers just above.
private int unitPixelWidth = Integer.parseInt(visualizationUnitPixelWidthChoices[defaultVisualizationUnitPixelWidthIndex]);
private int unitPixelHeight = Integer.parseInt(visualizationUnitPixelHeightChoices[defaultVisualizationUnitPixelHeightIndex]);
private int displayAreaWidthInPixels = Integer.parseInt(displayAreaPixelWidthChoices[defaultDisplayWidthIndex]);
private int displayAreaHeightInPixels = Integer.parseInt(displayAreaPixelHeightChoices[defaultDisplayHeightIndex]);
// The next four are initialized dynamically in initializeDisplayBaseChoices()
private String[] displayBaseAddressChoices;
private int[] displayBaseAddresses;
private int defaultBaseAddressIndex;
private int baseAddress;
private Grid theGrid;
/**
* Simple constructor, likely used to run a stand-alone bitmap display tool.
*
* @param title String containing title for title bar
* @param heading String containing text for heading shown in upper part of window.
*/
public BitmapDisplay(String title, String heading)
{
super(title, heading);
}
/**
* Simple constructor, likely used by the MARS Tools menu mechanism
*/
public BitmapDisplay()
{
super("Bitmap Display, " + version, heading);
}
/**
* Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that
* instantiates a Bitmap object then invokes its go() method. "stand-alone" means it is not invoked from the MARS
* Tools menu. "Pure" means there is no driver program to invoke the application.
*/
public static void main(String[] args)
{
new BitmapDisplay("Bitmap Display stand-alone, " + version, heading).go();
}
/**
* Required MarsTool method to return Tool name.
*
* @return Tool name. MARS will display this in menu item.
*/
public String getName()
{
return "Bitmap Display";
}
/**
* Override the inherited method, which registers us as an Observer over the static data segment (starting address
* 0x10010000) only. This version will register us as observer over the the memory range as selected by the base
* address combo box and capacity of the visualization display (number of visualization elements times the number of
* memory words each one represents). It does so by calling the inherited 2-parameter overload of this method. If
* you use the inherited GUI buttons, this method is invoked when you click "Connect" button on MarsTool or the
* "Assemble and Run" button on a Mars-based app.
*/
protected void addAsObserver()
{
int highAddress = baseAddress + theGrid.getRows() * theGrid.getColumns() * Memory.WORD_LENGTH_BYTES;
// Special case: baseAddress<0 means we're in kernel memory (0x80000000 and up) and most likely
// in memory map address space (0xffff0000 and up). In this case, we need to make sure the high address
// does not drop off the high end of 32 bit address space. Highest allowable word address is 0xfffffffc,
// which is interpreted in Java int as -4.
if (baseAddress < 0 && highAddress > -4)
{
highAddress = -4;
}
addAsObserver(baseAddress, highAddress);
}
/**
* Method that constructs the main display area. It is organized vertically into two major components: the display
* configuration which an be modified using combo boxes, and the visualization display which is updated as the
* attached MIPS program executes.
*
* @return the GUI component containing these two areas
*/
protected JComponent buildMainDisplayArea()
{
results = new JPanel();
results.add(buildOrganizationArea());
results.add(buildVisualizationArea());
return results;
}
//////////////////////////////////////////////////////////////////////////////////////
// Rest of the protected methods. These override do-nothing methods inherited from
// the abstract superclass.
//////////////////////////////////////////////////////////////////////////////////////
/**
* Update display when connected MIPS program accesses (data) memory.
*
* @param memory the attached memory
* @param accessNotice information provided by memory in MemoryAccessNotice object
*/
protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice)
{
if (accessNotice.getAccessType() == AccessNotice.WRITE)
{
updateColorForAddress((MemoryAccessNotice) accessNotice);
}
}
/**
* Initialize all JComboBox choice structures not already initialized at declaration. Overrides inherited method
* that does nothing.
*/
protected void initializePreGUI()
{
initializeDisplayBaseChoices();
// NOTE: Can't call "createNewGrid()" here because it uses settings from
// several combo boxes that have not been created yet. But a default grid
// needs to be allocated for initial canvas display.
theGrid = new Grid(displayAreaHeightInPixels / unitPixelHeight,
displayAreaWidthInPixels / unitPixelWidth);
}
/**
* The only post-GUI initialization is to create the initial Grid object based on the default settings of the
* various combo boxes. Overrides inherited method that does nothing.
*/
protected void initializePostGUI()
{
theGrid = createNewGrid();
updateBaseAddress();
}
/**
* Method to reset counters and display when the Reset button selected. Overrides inherited method that does
* nothing.
*/
protected void reset()
{
resetCounts();
updateDisplay();
}
/**
* Updates display immediately after each update (AccessNotice) is processed, after display configuration changes as
* needed, and after each execution step when Mars is running in timed mode. Overrides inherited method that does
* nothing.
*/
protected void updateDisplay()
{
canvas.repaint();
}
/**
* Overrides default method, to provide a Help button for this tool/app.
*/
protected JComponent getHelpComponent()
{
final String helpContent =
"Use this program to simulate a basic bitmap display where\n" +
"each memory word in a specified address space corresponds to\n" +
"one display pixel in row-major order starting at the upper left\n" +
"corner of the display. This tool may be run either from the\n" +
"MARS Tools menu or as a stand-alone application.\n" +
"\n" +
"You can easily learn to use this small program by playing with\n" +
"it! Each rectangular unit on the display represents one memory\n" +
"word in a contiguous address space starting with the specified\n" +
"base address. The value stored in that word will be interpreted\n" +
"as a 24-bit RGB color value with the red component in bits 16-23,\n" +
"the green component in bits 8-15, and the blue component in bits 0-7.\n" +
"Each time a memory word within the display address space is written\n" +
"by the MIPS program, its position in the display will be rendered\n" +
"in the color that its value represents.\n" +
"\n" +
"Version 1.0 is very basic and was constructed from the Memory\n" +
"Reference Visualization tool's code. Feel free to improve it and\n" +
"send me your code for consideration in the next MARS release.\n" +
"\n" +
"Contact Pete Sanderson at psanderson@otterbein.edu with\n" +
"questions or comments.\n";
JButton help = new JButton("Help");
help.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(theWindow, helpContent);
}
});
return help;
}
//////////////////////////////////////////////////////////////////////////////////////
// Private methods defined to support the above.
//////////////////////////////////////////////////////////////////////////////////////
// UI components and layout for left half of GUI, where settings are specified.
private JComponent buildOrganizationArea()
{
JPanel organization = new JPanel(new GridLayout(8, 1));
visualizationUnitPixelWidthSelector = new JComboBox(visualizationUnitPixelWidthChoices);
visualizationUnitPixelWidthSelector.setEditable(false);
visualizationUnitPixelWidthSelector.setBackground(backgroundColor);
visualizationUnitPixelWidthSelector.setSelectedIndex(defaultVisualizationUnitPixelWidthIndex);
visualizationUnitPixelWidthSelector.setToolTipText("Width in pixels of rectangle representing memory word");
visualizationUnitPixelWidthSelector.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
unitPixelWidth = getIntComboBoxSelection(visualizationUnitPixelWidthSelector);
theGrid = createNewGrid();
updateDisplay();
}
});
visualizationUnitPixelHeightSelector = new JComboBox(visualizationUnitPixelHeightChoices);
visualizationUnitPixelHeightSelector.setEditable(false);
visualizationUnitPixelHeightSelector.setBackground(backgroundColor);
visualizationUnitPixelHeightSelector.setSelectedIndex(defaultVisualizationUnitPixelHeightIndex);
visualizationUnitPixelHeightSelector.setToolTipText("Height in pixels of rectangle representing memory word");
visualizationUnitPixelHeightSelector.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
unitPixelHeight = getIntComboBoxSelection(visualizationUnitPixelHeightSelector);
theGrid = createNewGrid();
updateDisplay();
}
});
visualizationPixelWidthSelector = new JComboBox(displayAreaPixelWidthChoices);
visualizationPixelWidthSelector.setEditable(false);
visualizationPixelWidthSelector.setBackground(backgroundColor);
visualizationPixelWidthSelector.setSelectedIndex(defaultDisplayWidthIndex);
visualizationPixelWidthSelector.setToolTipText("Total width in pixels of display area");
visualizationPixelWidthSelector.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
displayAreaWidthInPixels = getIntComboBoxSelection(visualizationPixelWidthSelector);
canvas.setPreferredSize(getDisplayAreaDimension());
canvas.setSize(getDisplayAreaDimension());
theGrid = createNewGrid();
updateDisplay();
}
});
visualizationPixelHeightSelector = new JComboBox(displayAreaPixelHeightChoices);
visualizationPixelHeightSelector.setEditable(false);
visualizationPixelHeightSelector.setBackground(backgroundColor);
visualizationPixelHeightSelector.setSelectedIndex(defaultDisplayHeightIndex);
visualizationPixelHeightSelector.setToolTipText("Total height in pixels of display area");
visualizationPixelHeightSelector.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
displayAreaHeightInPixels = getIntComboBoxSelection(visualizationPixelHeightSelector);
canvas.setPreferredSize(getDisplayAreaDimension());
canvas.setSize(getDisplayAreaDimension());
theGrid = createNewGrid();
updateDisplay();
}
});
displayBaseAddressSelector = new JComboBox(displayBaseAddressChoices);
displayBaseAddressSelector.setEditable(false);
displayBaseAddressSelector.setBackground(backgroundColor);
displayBaseAddressSelector.setSelectedIndex(defaultBaseAddressIndex);
displayBaseAddressSelector.setToolTipText("Base address for display area (upper left corner)");
displayBaseAddressSelector.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// This may also affect what address range we should be registered as an Observer
// for. The default (inherited) address range is the MIPS static data segment
// starting at 0x10010000. To change this requires override of
// AbstractMarsToolAndApplication.addAsObserver(). The no-argument version of
// that method is called automatically when "Connect" button is clicked for MarsTool
// and when "Assemble and Run" button is clicked for Mars application.
updateBaseAddress();
// If display base address is changed while connected to MIPS (this can only occur
// when being used as a MarsTool), we have to delete ourselves as an observer and re-register.
if (connectButton != null && connectButton.isConnected())
{
deleteAsObserver();
addAsObserver();
}
theGrid = createNewGrid();
updateDisplay();
}
});
// ALL COMPONENTS FOR "ORGANIZATION" SECTION
JPanel unitWidthInPixelsRow = getPanelWithBorderLayout();
unitWidthInPixelsRow.setBorder(emptyBorder);
unitWidthInPixelsRow.add(new JLabel("Unit Width in Pixels "), BorderLayout.WEST);
unitWidthInPixelsRow.add(visualizationUnitPixelWidthSelector, BorderLayout.EAST);
JPanel unitHeightInPixelsRow = getPanelWithBorderLayout();
unitHeightInPixelsRow.setBorder(emptyBorder);
unitHeightInPixelsRow.add(new JLabel("Unit Height in Pixels "), BorderLayout.WEST);
unitHeightInPixelsRow.add(visualizationUnitPixelHeightSelector, BorderLayout.EAST);
JPanel widthInPixelsRow = getPanelWithBorderLayout();
widthInPixelsRow.setBorder(emptyBorder);
widthInPixelsRow.add(new JLabel("Display Width in Pixels "), BorderLayout.WEST);
widthInPixelsRow.add(visualizationPixelWidthSelector, BorderLayout.EAST);
JPanel heightInPixelsRow = getPanelWithBorderLayout();
heightInPixelsRow.setBorder(emptyBorder);
heightInPixelsRow.add(new JLabel("Display Height in Pixels "), BorderLayout.WEST);
heightInPixelsRow.add(visualizationPixelHeightSelector, BorderLayout.EAST);
JPanel baseAddressRow = getPanelWithBorderLayout();
baseAddressRow.setBorder(emptyBorder);
baseAddressRow.add(new JLabel("Base address for display "), BorderLayout.WEST);
baseAddressRow.add(displayBaseAddressSelector, BorderLayout.EAST);
// Lay 'em out in the grid...
organization.add(unitWidthInPixelsRow);
organization.add(unitHeightInPixelsRow);
organization.add(widthInPixelsRow);
organization.add(heightInPixelsRow);
organization.add(baseAddressRow);
return organization;
}
// UI components and layout for right half of GUI, the visualization display area.
private JComponent buildVisualizationArea()
{
canvas = new GraphicsPanel();
canvas.setPreferredSize(getDisplayAreaDimension());
canvas.setToolTipText("Bitmap display area");
return canvas;
}
// For greatest flexibility, initialize the display base choices directly from
// the constants defined in the Memory class. This method called prior to
// building the GUI. Here are current values from Memory.java:
//dataSegmentBaseAddress=0x10000000, globalPointer=0x10008000
//dataBaseAddress=0x10010000, heapBaseAddress=0x10040000, memoryMapBaseAddress=0xffff0000
private void initializeDisplayBaseChoices()
{
int[] displayBaseAddressArray = {Memory.dataSegmentBaseAddress, Memory.globalPointer, Memory.dataBaseAddress,
Memory.heapBaseAddress, Memory.memoryMapBaseAddress};
// Must agree with above in number and order...
String[] descriptions = {" (global data)", " ($gp)", " (static data)", " (heap)", " (memory map)"};
displayBaseAddresses = displayBaseAddressArray;
displayBaseAddressChoices = new String[displayBaseAddressArray.length];
for (int i = 0; i < displayBaseAddressChoices.length; i++)
{
displayBaseAddressChoices[i] = mars.util.Binary.intToHexString(displayBaseAddressArray[i]) + descriptions[i];
}
defaultBaseAddressIndex = 2; // default to 0x10010000 (static data)
baseAddress = displayBaseAddressArray[defaultBaseAddressIndex];
}
// update based on combo box selection (currently not editable but that may change).
private void updateBaseAddress()
{
baseAddress = displayBaseAddresses[displayBaseAddressSelector.getSelectedIndex()];
/* If you want to extend this app to allow user to edit combo box, you can always
parse the getSelectedItem() value, because the pre-defined items are all formatted
such that the first 10 characters contain the integer's hex value. And if the
value is user-entered, the numeric part cannot exceed 10 characters for a 32-bit
address anyway. So if the value is > 10 characters long, slice off the first
10 and apply Integer.parseInt() to it to get custom base address.
*/
}
// Returns Dimension object with current width and height of display area as determined
// by current settings of respective combo boxes.
private Dimension getDisplayAreaDimension()
{
return new Dimension(displayAreaWidthInPixels, displayAreaHeightInPixels);
}
// reset all counters in the Grid.
private void resetCounts()
{
theGrid.reset();
}
// Will return int equivalent of specified combo box's current selection.
// The selection must be a String that parses to an int.
private int getIntComboBoxSelection(JComboBox comboBox)
{
try
{
return Integer.parseInt((String) comboBox.getSelectedItem());
}
catch (NumberFormatException nfe)
{
// Can occur only if initialization list contains badly formatted numbers. This
// is a developer's error, not a user error, and better be caught before release.
return 1;
}
}
// Use this for consistent results.
private JPanel getPanelWithBorderLayout()
{
return new JPanel(new BorderLayout(2, 2));
}
// Method to determine grid dimensions based on current control settings.
// Each grid element corresponds to one visualization unit.
private Grid createNewGrid()
{
int rows = displayAreaHeightInPixels / unitPixelHeight;
int columns = displayAreaWidthInPixels / unitPixelWidth;
return new Grid(rows, columns);
}
// Given memory address, update color for the corresponding grid element.
private void updateColorForAddress(MemoryAccessNotice notice)
{
int address = notice.getAddress();
int value = notice.getValue();
int offset = (address - baseAddress) / Memory.WORD_LENGTH_BYTES;
try
{
theGrid.setElement(offset / theGrid.getColumns(), offset % theGrid.getColumns(), value);
}
catch (IndexOutOfBoundsException e)
{
// If address is out of range for display, do nothing.
}
}
//////////////////////////////////////////////////////////////////////////////////////
// Specialized inner classes for modeling and animation.
//////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Class that represents the panel for visualizing and animating memory reference
// patterns.
private class GraphicsPanel extends JPanel
{
// override default paint method to assure display updated correctly every time
// the panel is repainted.
public void paint(Graphics g)
{
paintGrid(g, theGrid);
}
// Paint the color codes.
private void paintGrid(Graphics g, Grid grid)
{
int upperLeftX = 0, upperLeftY = 0;
for (int i = 0; i < grid.getRows(); i++)
{
for (int j = 0; j < grid.getColumns(); j++)
{
g.setColor(grid.getElementFast(i, j));
g.fillRect(upperLeftX, upperLeftY, unitPixelWidth, unitPixelHeight);
upperLeftX += unitPixelWidth; // faster than multiplying
}
// get ready for next row...
upperLeftX = 0;
upperLeftY += unitPixelHeight; // faster than multiplying
}
}
}
////////////////////////////////////////////////////////////////////////
// Represents grid of colors
private class Grid
{
Color[][] grid;
int rows, columns;
private Grid(int rows, int columns)
{
grid = new Color[rows][columns];
this.rows = rows;
this.columns = columns;
reset();
}
private int getRows()
{
return rows;
}
private int getColumns()
{
return columns;
}
// Returns value in given grid element; null if row or column is out of range.
private Color getElement(int row, int column)
{
return (row >= 0 && row <= rows && column >= 0 && column <= columns) ? grid[row][column] : null;
}
// Returns value in given grid element without doing any row/column index checking.
// Is faster than getElement but will throw array index out of bounds exception if
// parameter values are outside the bounds of the grid.
private Color getElementFast(int row, int column)
{
return grid[row][column];
}
// Set the grid element.
private void setElement(int row, int column, int color)
{
grid[row][column] = new Color(color);
}
// Set the grid element.
private void setElement(int row, int column, Color color)
{
grid[row][column] = color;
}
// Just set all grid elements to black.
private void reset()
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
grid[i][j] = Color.BLACK;
}
}
}
}
}
+465
View File
@@ -0,0 +1,465 @@
package mars.tools
import mars.mips.hardware.AccessNotice
import mars.mips.hardware.Memory
import mars.mips.hardware.MemoryAccessNotice
import mars.util.Binary
import java.awt.*
import java.util.*
import javax.swing.*
import javax.swing.border.EmptyBorder
/*
Copyright (c) 2010-2011, 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)
*/
/**
* Bitmapp display simulator. It can be run either as a stand-alone Java application having access to the mars package,
* or through MARS as an item in its Tools menu. It makes maximum use of methods inherited from its abstract superclass
* AbstractMarsToolAndApplication. Pete Sanderson, verison 1.0, 23 December 2010.
*/
class BitmapDisplay : AbstractMarsToolAndApplication
{
// Major GUI components
private lateinit var uiUnitWidthSelector: JComboBox<Int>
private lateinit var uiUnitHeightSelector: JComboBox<Int>
private lateinit var uiWidthSelector: JComboBox<Int>
private lateinit var uiHeightSelector: JComboBox<Int>
private lateinit var uiBaseAddressSelector: JComboBox<String>
private lateinit var canvas: JPanel
private lateinit var results: JPanel
// Some GUI settings
private val emptyBorder = EmptyBorder(4, 4, 4, 4)
private val backgroundColor = Color.WHITE
// Values for display canvas. Note their initialization uses the identifiers just above.
private val unitWidth get() = uiUnitWidthSelector.getInt()
private val unitHeight get() = uiUnitHeightSelector.getInt()
private val displayWidth get() = uiWidthSelector.getInt()
private val displayHeight get() = uiHeightSelector.getInt()
private val displayDimension get() = Dimension(displayWidth, displayHeight)
private val baseAddresses = intArrayOf(Memory.dataSegmentBaseAddress, Memory.globalPointer, Memory.dataBaseAddress,
Memory.heapBaseAddress, Memory.memoryMapBaseAddress)
private val baseAddress get() = baseAddresses[uiBaseAddressSelector.selectedIndex]
private lateinit var grid: Grid
/**
* Simple constructor, likely used to run a stand-alone bitmap display tool.
*
* @param title String containing title for title bar
* @param heading String containing text for heading shown in upper part of window.
*/
constructor(title: String, heading: String) : super(title, heading)
constructor() : super("Azalea's Modified Bitmap Display", heading)
override fun getName() = "Bitmap Display"
/**
* Override the inherited method, which registers us as an Observer over the static data segment (starting address
* 0x10010000) only. This version will register us as observer over the the memory range as selected by the base
* address combo box and capacity of the visualization display (number of visualization elements times the number of
* memory words each one represents). It does so by calling the inherited 2-parameter overload of this method. If
* you use the inherited GUI buttons, this method is invoked when you click "Connect" button on MarsTool or the
* "Assemble and Run" button on a Mars-based app.
*/
override fun addAsObserver()
{
var highAddress: Int = baseAddress + grid.rows * grid.cols * Memory.WORD_LENGTH_BYTES
// Special case: baseAddress<0 means we're in kernel memory (0x80000000 and up) and most likely
// in memory map address space (0xffff0000 and up). In this case, we need to make sure the high address
// does not drop off the high end of 32 bit address space. Highest allowable word address is 0xfffffffc,
// which is interpreted in Java int as -4.
if (baseAddress < 0 && highAddress > -4)
{
highAddress = -4
}
addAsObserver(baseAddress, highAddress)
}
/**
* Method that constructs the main display area. It is organized vertically into two major components: the display
* configuration which an be modified using combo boxes, and the visualization display which is updated as the
* attached MIPS program executes.
*
* @return the GUI component containing these two areas
*/
override fun buildMainDisplayArea(): JComponent
{
results = JPanel()
results.add(buildOrganizationArea())
results.add(buildVisualizationArea())
return results
}
//////////////////////////////////////////////////////////////////////////////////////
// Rest of the protected methods. These override do-nothing methods inherited from
// the abstract superclass.
//////////////////////////////////////////////////////////////////////////////////////
/**
* Update display when connected MIPS program accesses (data) memory.
*
* @param memory the attached memory
* @param accessNotice information provided by memory in MemoryAccessNotice object
*/
override fun processMIPSUpdate(memory: Observable, accessNotice: AccessNotice)
{
if (accessNotice.accessType == AccessNotice.WRITE)
{
updateColorForAddress(accessNotice as MemoryAccessNotice)
}
}
/**
* Initialize all JComboBox choice structures not already initialized at declaration. Overrides inherited method
* that does nothing.
*/
override fun initializePreGUI()
{
grid = Grid(0, 0)
}
/**
* The only post-GUI initialization is to create the initial Grid object based on the default settings of the
* various combo boxes. Overrides inherited method that does nothing.
*/
override fun initializePostGUI()
{
grid = createNewGrid()
}
/**
* Method to reset counters and display when the Reset button selected. Overrides inherited method that does
* nothing.
*/
override fun reset()
{
resetCounts()
updateDisplay()
}
/**
* Updates display immediately after each update (AccessNotice) is processed, after display configuration changes as
* needed, and after each execution step when Mars is running in timed mode. Overrides inherited method that does
* nothing.
*/
override fun updateDisplay() = canvas.repaint()
/**
* Overrides default method, to provide a Help button for this tool/app.
*/
override fun getHelpComponent(): JComponent
{
val helpContent = """
Use this program to simulate a basic bitmap display where
each memory word in a specified address space corresponds to
one display pixel in row-major order starting at the upper left
corner of the display. This tool may be run either from the
MARS Tools menu or as a stand-alone application.
You can easily learn to use this small program by playing with
it! Each rectangular unit on the display represents one memory
word in a contiguous address space starting with the specified
base address. The value stored in that word will be interpreted
as a 24-bit RGB color value with the red component in bits 16-23,
the green component in bits 8-15, and the blue component in bits 0-7.
Each time a memory word within the display address space is written
by the MIPS program, its position in the display will be rendered
in the color that its value represents.
Version 1.0 is very basic and was constructed from the Memory
Reference Visualization tool's code. Feel free to improve it and
send me your code for consideration in the next MARS release.
Contact Pete Sanderson at psanderson@otterbein.edu with
questions or comments.
""".trimIndent()
val help = JButton("Help")
help.addActionListener { JOptionPane.showMessageDialog(theWindow, helpContent) }
return help
}
//////////////////////////////////////////////////////////////////////////////////////
// Private methods defined to support the above.
//////////////////////////////////////////////////////////////////////////////////////
// UI components and layout for left half of GUI, where settings are specified.
private fun buildOrganizationArea(): JComponent
{
val organization = JPanel(GridLayout(8, 1))
uiUnitWidthSelector = JComboBox(arrayOf(1, 2, 4, 8, 16, 32)).apply {
isEditable = false
background = backgroundColor
selectedIndex = 2
toolTipText = "Width in pixels of rectangle representing memory word"
addActionListener {
grid = createNewGrid()
updateDisplay()
}
}
uiUnitHeightSelector = JComboBox(arrayOf(1, 2, 4, 8, 16, 32)).apply {
isEditable = false
background = backgroundColor
selectedIndex = 2
toolTipText = "Height in pixels of rectangle representing memory word"
addActionListener {
grid = createNewGrid()
updateDisplay()
}
}
uiWidthSelector = JComboBox(arrayOf(64, 128, 256, 512, 1024)).apply {
isEditable = false
background = backgroundColor
selectedIndex = 3
toolTipText = "Total width in pixels of display area"
addActionListener {
canvas.preferredSize = displayDimension
canvas.size = displayDimension
grid = createNewGrid()
updateDisplay()
}
}
uiHeightSelector = JComboBox(arrayOf(64, 128, 256, 512, 1024)).apply {
isEditable = false
background = backgroundColor
selectedIndex = 2
toolTipText = "Total height in pixels of display area"
addActionListener {
canvas.preferredSize = displayDimension
canvas.size = displayDimension
grid = createNewGrid()
updateDisplay()
}
}
val descriptions = arrayOf("global data", "\$gp", "static data", "heap", "memory map")
val displayBaseAddressChoices = baseAddresses
.mapIndexed { i, it -> "${Binary.intToHexString(it)} (${descriptions[i]})" }.toTypedArray()
uiBaseAddressSelector = JComboBox(displayBaseAddressChoices).apply {
isEditable = false
background = backgroundColor
selectedIndex = 1
toolTipText = "Base address for display area (upper left corner)"
addActionListener {
// If display base address is changed while connected to MIPS (this can only occur
// when being used as a MarsTool), we have to delete ourselves as an observer and re-register.
if (connectButton != null && connectButton.isConnected)
{
deleteAsObserver()
addAsObserver()
}
grid = createNewGrid()
updateDisplay()
}
}
// ALL COMPONENTS FOR "ORGANIZATION" SECTION
val unitWidthInPixelsRow = panelWithBorderLayout
unitWidthInPixelsRow.border = emptyBorder
unitWidthInPixelsRow.add(JLabel("Unit Width in Pixels "), BorderLayout.WEST)
unitWidthInPixelsRow.add(uiUnitWidthSelector, BorderLayout.EAST)
val unitHeightInPixelsRow = panelWithBorderLayout
unitHeightInPixelsRow.border = emptyBorder
unitHeightInPixelsRow.add(JLabel("Unit Height in Pixels "), BorderLayout.WEST)
unitHeightInPixelsRow.add(uiUnitHeightSelector, BorderLayout.EAST)
val widthInPixelsRow = panelWithBorderLayout
widthInPixelsRow.border = emptyBorder
widthInPixelsRow.add(JLabel("Display Width in Pixels "), BorderLayout.WEST)
widthInPixelsRow.add(uiWidthSelector, BorderLayout.EAST)
val heightInPixelsRow = panelWithBorderLayout
heightInPixelsRow.border = emptyBorder
heightInPixelsRow.add(JLabel("Display Height in Pixels "), BorderLayout.WEST)
heightInPixelsRow.add(uiHeightSelector, BorderLayout.EAST)
val baseAddressRow = panelWithBorderLayout
baseAddressRow.border = emptyBorder
baseAddressRow.add(JLabel("Base address for display "), BorderLayout.WEST)
baseAddressRow.add(uiBaseAddressSelector, BorderLayout.EAST)
// Lay 'em out in the grid...
organization.add(unitWidthInPixelsRow)
organization.add(unitHeightInPixelsRow)
organization.add(widthInPixelsRow)
organization.add(heightInPixelsRow)
organization.add(baseAddressRow)
return organization
}
// UI components and layout for right half of GUI, the visualization display area.
private fun buildVisualizationArea(): JComponent
{
canvas = GraphicsPanel()
canvas.preferredSize = displayDimension
canvas.toolTipText = "Bitmap display area"
return canvas
}
// reset all counters in the Grid.
private fun resetCounts() = grid.reset()
private fun <T> JComboBox<T>.getInt() = selectedItem!!.toString().toInt()
// Use this for consistent results.
private val panelWithBorderLayout: JPanel
get() = JPanel(BorderLayout(2, 2))
// Method to determine grid dimensions based on current control settings.
// Each grid element corresponds to one visualization unit.
private fun createNewGrid(): Grid
{
val rows = displayHeight / unitHeight
val columns = displayWidth / unitWidth
return Grid(rows, columns)
}
// Given memory address, update color for the corresponding grid element.
private fun updateColorForAddress(notice: MemoryAccessNotice)
{
val address = notice.address
val value = notice.value
val offset = (address - baseAddress) / Memory.WORD_LENGTH_BYTES
try
{
grid.setElement(offset / grid.cols, offset % grid.cols, value)
} catch (e: IndexOutOfBoundsException)
{
// If address is out of range for display, do nothing.
}
}
//////////////////////////////////////////////////////////////////////////////////////
// Specialized inner classes for modeling and animation.
//////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Class that represents the panel for visualizing and animating memory reference
// patterns.
private inner class GraphicsPanel : JPanel()
{
// override default paint method to assure display updated correctly every time
// the panel is repainted.
override fun paint(g: Graphics) = paintGrid(g, grid)
// Paint the color codes.
private fun paintGrid(g: Graphics, grid: Grid)
{
var upperLeftX = 0
var upperLeftY = 0
for (i in 0 until grid.rows)
{
for (j in 0 until grid.cols)
{
g.color = grid.getElementFast(i, j)
g.fillRect(upperLeftX, upperLeftY, unitWidth, unitHeight)
upperLeftX += unitWidth // faster than multiplying
}
// get ready for next row...
upperLeftX = 0
upperLeftY += unitHeight // faster than multiplying
}
}
}
////////////////////////////////////////////////////////////////////////
// Represents grid of colors
private class Grid(rows: Int, columns: Int)
{
var grid: Array<Array<Color?>>
var rows: Int
var cols: Int
init
{
grid = Array(rows) { arrayOfNulls(columns) }
this.rows = rows
this.cols = columns
reset()
}
// Returns value in given grid element; null if row or column is out of range.
private fun getElement(row: Int, column: Int): Color?
{
return if (row in 0..rows && column >= 0 && column <= cols) grid[row][column] else null
}
// Returns value in given grid element without doing any row/column index checking.
// Is faster than getElement but will throw array index out of bounds exception if
// parameter values are outside the bounds of the grid.
fun getElementFast(row: Int, column: Int): Color?
{
return grid[row][column]
}
// Set the grid element.
fun setElement(row: Int, column: Int, color: Int)
{
grid[row][column] = Color(color)
}
// Set the grid element.
private fun setElement(row: Int, column: Int, color: Color)
{
grid[row][column] = color
}
// Just set all grid elements to black.
fun reset()
{
for (i in 0 until rows)
{
for (j in 0 until cols)
{
grid[i][j] = Color.BLACK
}
}
}
}
companion object
{
private const val version = "Version 1.0"
private const val heading = "Bitmap Display"
/**
* Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that
* instantiates a Bitmap object then invokes its go() method. "stand-alone" means it is not invoked from the MARS
* Tools menu. "Pure" means there is no driver program to invoke the application.
*/
@JvmStatic
fun main(args: Array<String>)
{
BitmapDisplay("Bitmap Display stand-alone, " + version, heading).go()
}
}
}