0da1c5dcca
First commit of the 4.5 version (latest version available)
1018 lines
55 KiB
Java
1018 lines
55 KiB
Java
package mars.tools;
|
|
import mars.util.Binary;
|
|
import mars.venus.*;
|
|
import javax.swing.*;
|
|
import javax.swing.border.*;
|
|
import javax.swing.event.*;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.util.*;
|
|
import mars.Globals;
|
|
import mars.venus.RunSpeedPanel;
|
|
import mars.mips.hardware.*;
|
|
import mars.simulator.Exceptions;
|
|
import javax.swing.text.DefaultCaret;
|
|
|
|
|
|
/*
|
|
Copyright (c) 2003-2014, 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)
|
|
*/
|
|
|
|
/**
|
|
* Keyboard and 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<br>
|
|
* Version 1.0, 24 July 2008.<br>
|
|
* Version 1.1, 24 November 2008 corrects two omissions: (1) the tool failed to register as an observer
|
|
* of kernel text memory when counting instruction executions for transmitter ready bit
|
|
* reset delay, and (2) the tool failed to test the Status register's Exception Level bit before
|
|
* raising the exception that results in the interrupt (if the Exception Level bit is 1, that
|
|
* means an interrupt is being processed, so disable further interrupts).
|
|
*
|
|
* Version 1.2, August 2009, soft-codes the MMIO register locations for new memory configuration
|
|
* feature of MARS 3.7. Previously memory segment addresses were fixed and final. Now they
|
|
* can be modified dynamically so the tool has to get its values dynamically as well.
|
|
*
|
|
* Version 1.3, August 2011, corrects bug to enable Display window to scroll when needed.
|
|
*
|
|
* Version 1.4, August 2014, adds two features: (1) ASCII control character 12 (form feed) when
|
|
* transmitted will clear the Display window. (2) ASCII control character 7 (bell) when
|
|
* transmitted with properly coded (X,Y) values will reposition the cursor to the specified
|
|
* position of a virtual text-based terminal. X represents column, Y represents row.
|
|
*/
|
|
|
|
public class KeyboardAndDisplaySimulator extends AbstractMarsToolAndApplication {
|
|
|
|
private static String version = "Version 1.4";
|
|
private static String heading = "Keyboard and Display MMIO Simulator";
|
|
private static String displayPanelTitle, keyboardPanelTitle;
|
|
private static char VT_FILL = ' '; // fill character for virtual terminal (random access mode)
|
|
|
|
public static Dimension preferredTextAreaDimension = new Dimension(400,200);
|
|
private static Insets textAreaInsets = new Insets(4,4,4,4);
|
|
|
|
// Time delay to process Transmitter Data is simulated by counting instruction executions.
|
|
// After this many executions, the Transmitter Controller Ready bit set to 1.
|
|
private final TransmitterDelayTechnique[] delayTechniques = {
|
|
new FixedLengthDelay(),
|
|
new UniformlyDistributedDelay(),
|
|
new NormallyDistributedDelay()
|
|
};
|
|
public static int RECEIVER_CONTROL; // keyboard Ready in low-order bit
|
|
public static int RECEIVER_DATA; // keyboard character in low-order byte
|
|
public static int TRANSMITTER_CONTROL; // display Ready in low-order bit
|
|
public static int TRANSMITTER_DATA; // display character in low-order byte
|
|
// These are used to track instruction counts to simulate driver delay of Transmitter Data
|
|
private boolean countingInstructions;
|
|
private int instructionCount;
|
|
private int transmitDelayInstructionCountLimit;
|
|
private int currentDelayInstructionLimit;
|
|
|
|
// Should the transmitted character be displayed before the transmitter delay period?
|
|
// If not, hold onto it and print at the end of delay period.
|
|
private int intWithCharacterToDisplay;
|
|
private boolean displayAfterDelay = true;
|
|
|
|
// Whether or not display position is sequential (JTextArea append)
|
|
// or random access (row, column). Supports new random access feature. DPS 17-July-2014
|
|
private boolean displayRandomAccessMode = false;
|
|
private int rows, columns;
|
|
private DisplayResizeAdapter updateDisplayBorder;
|
|
private KeyboardAndDisplaySimulator simulator;
|
|
|
|
// Major GUI components
|
|
private JPanel keyboardAndDisplay;
|
|
private JScrollPane displayScrollPane;
|
|
private JTextArea display;
|
|
private JPanel displayPanel, displayOptions;
|
|
private JComboBox delayTechniqueChooser;
|
|
private DelayLengthPanel delayLengthPanel;
|
|
private JSlider delayLengthSlider;
|
|
private JCheckBox displayAfterDelayCheckBox;
|
|
private JPanel keyboardPanel;
|
|
private JScrollPane keyAccepterScrollPane;
|
|
private JTextArea keyEventAccepter;
|
|
private JButton fontButton;
|
|
private Font defaultFont = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
|
/**
|
|
* Simple constructor, likely used to run a stand-alone keyboard/display simulator.
|
|
* @param title String containing title for title bar
|
|
* @param heading String containing text for heading shown in upper part of window.
|
|
*/
|
|
public KeyboardAndDisplaySimulator(String title, String heading) {
|
|
super(title,heading);
|
|
simulator = this;
|
|
}
|
|
|
|
/**
|
|
* Simple constructor, likely used by the MARS Tools menu mechanism
|
|
*/
|
|
public KeyboardAndDisplaySimulator() {
|
|
super(heading + ", " + version, heading);
|
|
simulator = this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Main provided for pure stand-alone use. Recommended stand-alone use is to write a
|
|
* driver program that instantiates a KeyboardAndDisplaySimulator 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 KeyboardAndDisplaySimulator(heading+" 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 heading;
|
|
}
|
|
|
|
// Set the MMIO addresses. Prior to MARS 3.7 these were final because
|
|
// MIPS address space was final as well. Now we will get MMIO base address
|
|
// each time to reflect possible change in memory configuration. DPS 6-Aug-09
|
|
protected void initializePreGUI() {
|
|
RECEIVER_CONTROL = Memory.memoryMapBaseAddress; //0xffff0000; // keyboard Ready in low-order bit
|
|
RECEIVER_DATA = Memory.memoryMapBaseAddress + 4; //0xffff0004; // keyboard character in low-order byte
|
|
TRANSMITTER_CONTROL = Memory.memoryMapBaseAddress + 8; //0xffff0008; // display Ready in low-order bit
|
|
TRANSMITTER_DATA = Memory.memoryMapBaseAddress + 12; //0xffff000c; // display character in low-order byte
|
|
displayPanelTitle = "DISPLAY: Store to Transmitter Data "+Binary.intToHexString(TRANSMITTER_DATA);
|
|
keyboardPanelTitle = "KEYBOARD: Characters typed here are stored to Receiver Data "+Binary.intToHexString(RECEIVER_DATA);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Override the inherited method, which registers us as an Observer over the static data segment
|
|
* (starting address 0x10010000) only.
|
|
*
|
|
* When user enters keystroke, set RECEIVER_CONTROL and RECEIVER_DATA using the action listener.
|
|
* When user loads word (lw) from RECEIVER_DATA (we are notified of the read), then clear RECEIVER_CONTROL.
|
|
* When user stores word (sw) to TRANSMITTER_DATA (we are notified of the write), then clear TRANSMITTER_CONTROL, read TRANSMITTER_DATA,
|
|
* echo the character to display, wait for delay period, then set TRANSMITTER_CONTROL.
|
|
*
|
|
* 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() {
|
|
// Set transmitter Control ready bit to 1, means we're ready to accept display character.
|
|
updateMMIOControl(TRANSMITTER_CONTROL, readyBitSet(TRANSMITTER_CONTROL));
|
|
// We want to be an observer only of MIPS reads from RECEIVER_DATA and writes to TRANSMITTER_DATA.
|
|
// Use the Globals.memory.addObserver() methods instead of inherited method to achieve this.
|
|
addAsObserver(RECEIVER_DATA,RECEIVER_DATA);
|
|
addAsObserver(TRANSMITTER_DATA, TRANSMITTER_DATA);
|
|
// We want to be notified of each instruction execution, because instruction count is the
|
|
// basis for delay in re-setting (literally) the TRANSMITTER_CONTROL register. SPIM does
|
|
// this too. This simulates the time required for the display unit to process the
|
|
// TRANSMITTER_DATA.
|
|
addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress);
|
|
addAsObserver(Memory.kernelTextBaseAddress, Memory.kernelTextLimitAddress);
|
|
}
|
|
|
|
|
|
/**
|
|
* Method that constructs the main display area. It is organized vertically
|
|
* into two major components: the display and the keyboard. The display itself
|
|
* is a JTextArea and it echoes characters placed into the low order byte of
|
|
* the Transmitter Data location, 0xffff000c. They keyboard is also a JTextArea
|
|
* places each typed character into the Receive Data location 0xffff0004.
|
|
* @return the GUI component containing these two areas
|
|
*/
|
|
protected JComponent buildMainDisplayArea() {
|
|
// Changed arrangement of the display and keyboard panels from GridLayout(2,1)
|
|
// to BorderLayout to hold a JSplitPane containing both panels. This permits user
|
|
// to apportion the relative sizes of the display and keyboard panels within
|
|
// the overall frame. Will be convenient for use with the new random-access
|
|
// display positioning feature. Previously, both the display and the keyboard
|
|
// text areas were equal in size and there was no way for the user to change that.
|
|
// DPS 17-July-2014
|
|
keyboardAndDisplay = new JPanel(new BorderLayout());
|
|
JSplitPane both = new JSplitPane(JSplitPane.VERTICAL_SPLIT, buildDisplay(),buildKeyboard());
|
|
both.setResizeWeight(0.5);
|
|
keyboardAndDisplay.add(both);
|
|
return keyboardAndDisplay;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Rest of the protected methods. These all 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) {
|
|
MemoryAccessNotice notice = (MemoryAccessNotice) accessNotice;
|
|
// If MIPS program has just read (loaded) the receiver (keyboard) data register,
|
|
// then clear the Ready bit to indicate there is no longer a keystroke available.
|
|
// If Ready bit was initially clear, they'll get the old keystroke -- serves 'em right
|
|
// for not checking!
|
|
if (notice.getAddress()==RECEIVER_DATA && notice.getAccessType()==AccessNotice.READ) {
|
|
updateMMIOControl(RECEIVER_CONTROL, readyBitCleared(RECEIVER_CONTROL));
|
|
}
|
|
// MIPS program has just written (stored) the transmitter (display) data register. If transmitter
|
|
// Ready bit is clear, device is not ready yet so ignore this event -- serves 'em right for not checking!
|
|
// If transmitter Ready bit is set, then clear it to indicate the display device is processing the character.
|
|
// Also start an intruction counter that will simulate the delay of the slower
|
|
// display device processing the character.
|
|
if (isReadyBitSet(TRANSMITTER_CONTROL) && notice.getAddress()==TRANSMITTER_DATA && notice.getAccessType()==AccessNotice.WRITE) {
|
|
updateMMIOControl(TRANSMITTER_CONTROL, readyBitCleared(TRANSMITTER_CONTROL));
|
|
intWithCharacterToDisplay = notice.getValue();
|
|
if (!displayAfterDelay) displayCharacter(intWithCharacterToDisplay);
|
|
this.countingInstructions = true;
|
|
this.instructionCount = 0;
|
|
this.transmitDelayInstructionCountLimit = generateDelay();
|
|
}
|
|
// We have been notified of a MIPS instruction execution.
|
|
// If we are in transmit delay period, increment instruction count and if limit
|
|
// has been reached, set the transmitter Ready flag to indicate the MIPS program
|
|
// can write another character to the transmitter data register. If the Interrupt-Enabled
|
|
// bit had been set by the MIPS program, generate an interrupt!
|
|
if ( this.countingInstructions &&
|
|
notice.getAccessType()==AccessNotice.READ &&
|
|
(Memory.inTextSegment(notice.getAddress()) || Memory.inKernelTextSegment(notice.getAddress()))) {
|
|
this.instructionCount++;
|
|
if (this.instructionCount >= this.transmitDelayInstructionCountLimit) {
|
|
if (displayAfterDelay) displayCharacter(intWithCharacterToDisplay);
|
|
this.countingInstructions = false;
|
|
int updatedTransmitterControl = readyBitSet(TRANSMITTER_CONTROL);
|
|
updateMMIOControl(TRANSMITTER_CONTROL, updatedTransmitterControl);
|
|
if (updatedTransmitterControl != 1
|
|
&& (Coprocessor0.getValue(Coprocessor0.STATUS) & 2)==0 // Added by Carl Hauser Nov 2008
|
|
&& (Coprocessor0.getValue(Coprocessor0.STATUS) & 1)==1) {
|
|
// interrupt-enabled bit is set in both Tranmitter Control and in
|
|
// Coprocessor0 Status register, and Interrupt Level Bit is 0, so trigger external interrupt.
|
|
mars.simulator.Simulator.externalInterruptingDevice = Exceptions.EXTERNAL_INTERRUPT_DISPLAY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static final char CLEAR_SCREEN = 12; // ASCII Form Feed
|
|
private static final char SET_CURSOR_X_Y = 7; // ASCII Bell (ding ding!)
|
|
|
|
// Method to display the character stored in the low-order byte of
|
|
// the parameter. We also recognize two non-printing characters:
|
|
// Decimal 12 (Ascii Form Feed) to clear the display
|
|
// Decimal 7 (Ascii Bell) to place the cursor at a specified (X,Y) position.
|
|
// of a virtual text terminal. The position is specified in the high
|
|
// order 24 bits of the transmitter word (X in 20-31, Y in 8-19).
|
|
// Thus the parameter is the entire word, not just the low-order byte.
|
|
// Once the latter is performed, the display mode changes to random
|
|
// access, which has repercussions for the implementation of character display.
|
|
private void displayCharacter(int intWithCharacterToDisplay) {
|
|
char characterToDisplay = (char) (intWithCharacterToDisplay & 0x000000FF);
|
|
if (characterToDisplay == CLEAR_SCREEN) {
|
|
initializeDisplay(displayRandomAccessMode);
|
|
}
|
|
else if (characterToDisplay == SET_CURSOR_X_Y) {
|
|
// First call will activate random access mode.
|
|
// We're using JTextArea, where caret has to be within text.
|
|
// So initialize text to all spaces to fill the JTextArea to its
|
|
// current capacity. Then set caret. Subsequent character
|
|
// displays will replace, not append, in the text.
|
|
if (!displayRandomAccessMode) {
|
|
displayRandomAccessMode = true;
|
|
initializeDisplay(displayRandomAccessMode);
|
|
}
|
|
// For SET_CURSOR_X_Y, we need data from the rest of the word.
|
|
// High order 3 bytes are split in half to store (X,Y) value.
|
|
// High 12 bits contain X value, next 12 bits contain Y value.
|
|
int x = (intWithCharacterToDisplay & 0xFFF00000) >>> 20;
|
|
int y = (intWithCharacterToDisplay & 0x000FFF00) >>> 8;
|
|
// If X or Y values are outside current range, set to range limit.
|
|
if (x<0) x=0;
|
|
if (x>=columns) x=columns-1;
|
|
if (y<0) y=0;
|
|
if (y>=rows) y=rows-1;
|
|
// display is a JTextArea whose character positioning in the text is linear.
|
|
// Converting (row,column) to linear position requires knowing how many columns
|
|
// are in each row. I add one because each row except the last ends with '\n' that
|
|
// does not count as a column but occupies a position in the text string.
|
|
// The values of rows and columns is set in initializeDisplay().
|
|
display.setCaretPosition( y*(columns+1) + x);
|
|
}
|
|
else {
|
|
if (displayRandomAccessMode) {
|
|
try {
|
|
int caretPosition = display.getCaretPosition();
|
|
// if caret is positioned at the end of a line (at the '\n'), skip over the '\n'
|
|
if ( (caretPosition+1) % (columns+1) == 0) {
|
|
caretPosition++;
|
|
display.setCaretPosition(caretPosition);
|
|
}
|
|
display.replaceRange(""+characterToDisplay, caretPosition, caretPosition+1);
|
|
}
|
|
catch (IllegalArgumentException e) {
|
|
// tried to write off the end of the defined grid.
|
|
display.setCaretPosition(display.getCaretPosition()-1);
|
|
display.replaceRange(""+characterToDisplay,display.getCaretPosition(),display.getCaretPosition()+1);
|
|
}
|
|
}
|
|
else {
|
|
display.append(""+characterToDisplay);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialization code to be executed after the GUI is configured. Overrides inherited default.
|
|
*/
|
|
|
|
protected void initializePostGUI() {
|
|
initializeTransmitDelaySimulator();
|
|
keyEventAccepter.requestFocusInWindow();
|
|
}
|
|
|
|
|
|
/**
|
|
* Method to reset counters and display when the Reset button selected.
|
|
* Overrides inherited method that does nothing.
|
|
*/
|
|
protected void reset() {
|
|
displayRandomAccessMode = false;
|
|
initializeTransmitDelaySimulator();
|
|
initializeDisplay(displayRandomAccessMode);
|
|
keyEventAccepter.setText("");
|
|
((TitledBorder)displayPanel.getBorder()).setTitle(displayPanelTitle);
|
|
displayPanel.repaint();
|
|
keyEventAccepter.requestFocusInWindow();
|
|
updateMMIOControl(TRANSMITTER_CONTROL, readyBitSet(TRANSMITTER_CONTROL));
|
|
}
|
|
|
|
|
|
// The display JTextArea (top half) is initialized either to the empty
|
|
// string, or to a string filled with lines of spaces. It will do the
|
|
// latter only if the MIPS program has sent the BELL character (Ascii 7) to
|
|
// the transmitter. This sets the caret (cursor) to a specific (x,y) position
|
|
// on a text-based virtual display. The lines of spaces is necessary because
|
|
// the caret can only be placed at a position within the current text string.
|
|
private void initializeDisplay(boolean randomAccess) {
|
|
String initialText = "";
|
|
if (randomAccess) {
|
|
Dimension textDimensions = getDisplayPanelTextDimensions();
|
|
columns = (int) textDimensions.getWidth();
|
|
rows = (int) textDimensions.getHeight();
|
|
repaintDisplayPanelBorder();
|
|
char[] charArray = new char[columns];
|
|
Arrays.fill(charArray, VT_FILL);
|
|
String row = new String(charArray);
|
|
StringBuffer str = new StringBuffer(row);
|
|
for (int i=1; i<rows; i++) {
|
|
str.append("\n"+row);
|
|
}
|
|
initialText = str.toString();
|
|
}
|
|
display.setText(initialText);
|
|
display.setCaretPosition(0);
|
|
}
|
|
|
|
// Update display window title with current text display capacity (columns and rows)
|
|
// This will be called when window resized or font changed.
|
|
private void repaintDisplayPanelBorder() {
|
|
Dimension size = this.getDisplayPanelTextDimensions();
|
|
int cols = (int) size.getWidth();
|
|
int rows = (int) size.getHeight();
|
|
int caretPosition = display.getCaretPosition();
|
|
String stringCaretPosition = "";
|
|
// display position as stream or 2D depending on random access
|
|
if (displayRandomAccessMode) {
|
|
// if ( caretPosition == rows*(columns+1)+1) {
|
|
// stringCaretPosition = "(0,0)";
|
|
// }
|
|
// else if ( (caretPosition+1) % (columns+1) == 0) {
|
|
// stringCaretPosition = "(0,"+((caretPosition/(columns+1))+1)+")";
|
|
// }
|
|
// else {
|
|
// stringCaretPosition = "("+(caretPosition%(columns+1))+","+(caretPosition/(columns+1))+")";
|
|
// }
|
|
if ( ((caretPosition+1) % (columns+1) != 0)) {
|
|
stringCaretPosition = "("+(caretPosition%(columns+1))+","+(caretPosition/(columns+1))+")";
|
|
}
|
|
else if ( ((caretPosition+1) % (columns+1) == 0) && ((caretPosition/(columns+1))+1 == rows) ) {
|
|
stringCaretPosition = "("+(caretPosition%(columns+1)-1)+","+(caretPosition/(columns+1))+")";
|
|
}
|
|
else {
|
|
stringCaretPosition = "(0,"+((caretPosition/(columns+1))+1)+")";
|
|
}
|
|
}
|
|
else {
|
|
stringCaretPosition = ""+caretPosition;
|
|
}
|
|
String title = displayPanelTitle+", cursor "+stringCaretPosition+", area "+cols+" x "+rows;
|
|
((TitledBorder)displayPanel.getBorder()).setTitle(title);
|
|
displayPanel.repaint();
|
|
}
|
|
|
|
|
|
// Calculate text display capacity of display window. Text dimensions are based
|
|
// on pixel dimensions of window divided by font size properties.
|
|
private Dimension getDisplayPanelTextDimensions() {
|
|
Dimension areaSize = display.getSize();
|
|
int widthInPixels = (int) areaSize.getWidth();
|
|
int heightInPixels = (int) areaSize.getHeight();
|
|
FontMetrics metrics = getFontMetrics(display.getFont());
|
|
int rowHeight = metrics.getHeight();
|
|
int charWidth = metrics.charWidth('m');
|
|
// Estimate number of columns/rows of text that will fit in current window with current font.
|
|
// I subtract 1 because initial tests showed slight scroll otherwise.
|
|
return new Dimension(widthInPixels / charWidth - 1, heightInPixels / rowHeight - 1);
|
|
}
|
|
|
|
|
|
// Trigger recalculation and update of display text dimensions when window resized.
|
|
private class DisplayResizeAdapter extends ComponentAdapter {
|
|
public void componentResized(ComponentEvent e) {
|
|
getDisplayPanelTextDimensions();
|
|
repaintDisplayPanelBorder();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overrides default method, to provide a Help button for this tool/app.
|
|
*/
|
|
|
|
protected JComponent getHelpComponent() {
|
|
final String helpContent =
|
|
"Keyboard And Display MMIO Simulator\n\n"+
|
|
"Use this program to simulate Memory-Mapped I/O (MMIO) for a keyboard input device and character "+
|
|
"display output device. It may be run either from MARS' Tools menu or as a stand-alone application. "+
|
|
"For the latter, simply write a driver to instantiate a mars.tools.KeyboardAndDisplaySimulator object "+
|
|
"and invoke its go() method.\n"+
|
|
"\n"+
|
|
"While the tool is connected to MIPS, each keystroke in the text area causes the corresponding ASCII "+
|
|
"code to be placed in the Receiver Data register (low-order byte of memory word "+Binary.intToHexString(RECEIVER_DATA)+"), and the "+
|
|
"Ready bit to be set to 1 in the Receiver Control register (low-order bit of "+Binary.intToHexString(RECEIVER_CONTROL)+"). The Ready "+
|
|
"bit is automatically reset to 0 when the MIPS program reads the Receiver Data using an 'lw' instruction.\n"+
|
|
"\n"+
|
|
"A program may write to the display area by detecting the Ready bit set (1) in the Transmitter Control "+
|
|
"register (low-order bit of memory word "+Binary.intToHexString(TRANSMITTER_CONTROL)+"), then storing the ASCII code of the character to be "+
|
|
"displayed in the Transmitter Data register (low-order byte of "+Binary.intToHexString(TRANSMITTER_DATA)+") using a 'sw' instruction. This "+
|
|
"triggers the simulated display to clear the Ready bit to 0, delay awhile to simulate processing the data, "+
|
|
"then set the Ready bit back to 1. The delay is based on a count of executed MIPS instructions.\n"+
|
|
"\n"+
|
|
"In a polled approach to I/O, a MIPS program idles in a loop, testing the device's Ready bit on each "+
|
|
"iteration until it is set to 1 before proceeding. This tool also supports an interrupt-driven approach "+
|
|
"which requires the program to provide an interrupt handler but allows it to perform useful processing "+
|
|
"instead of idly looping. When the device is ready, it signals an interrupt and the MARS simuator will "+
|
|
"transfer control to the interrupt handler. Note: in MARS, the interrupt handler has to co-exist with the "+
|
|
"exception handler in kernel memory, both having the same entry address. Interrupt-driven I/O is enabled "+
|
|
"when the MIPS program sets the Interrupt-Enable bit in the device's control register. Details below.\n"+
|
|
"\n"+
|
|
"Upon setting the Receiver Controller's Ready bit to 1, its Interrupt-Enable bit (bit position 1) is tested. "+
|
|
"If 1, then an External Interrupt will be generated. Before executing the next MIPS instruction, the runtime "+
|
|
"simulator will detect the interrupt, place the interrupt code (0) into bits 2-6 of Coprocessor 0's Cause "+
|
|
"register ($13), set bit 8 to 1 to identify the source as keyboard, place the program counter value (address "+
|
|
"of the NEXT instruction to be executed) into its EPC register ($14), and check to see if an interrupt/trap "+
|
|
"handler is present (looks for instruction code at address 0x80000180). If so, the program counter is set to "+
|
|
"that address. If not, program execution is terminated with a message to the Run I/O tab. The Interrupt-Enable "+
|
|
"bit is 0 by default and has to be set by the MIPS program if interrupt-driven input is desired. Interrupt-driven "+
|
|
"input permits the program to perform useful tasks instead of idling in a loop polling the Receiver Ready bit! "+
|
|
"Very event-oriented. The Ready bit is supposed to be read-only but in MARS it is not.\n"+
|
|
"\n"+
|
|
"A similar test and potential response occurs when the Transmitter Controller's Ready bit is set to 1. This "+
|
|
"occurs after the simulated delay described above. The only difference is the Cause register bit to identify "+
|
|
"the (simulated) display as external interrupt source is bit position 9 rather than 8. This permits you to "+
|
|
"write programs that perform interrupt-driven output - the program can perform useful tasks while the "+
|
|
"output device is processing its data. Much better than idling in a loop polling the Transmitter Ready bit! "+
|
|
"The Ready bit is supposed to be read-only but in MARS it is not.\n"+
|
|
"\n"+
|
|
"IMPORTANT NOTE: The Transmitter Controller Ready bit is set to its initial value of 1 only when you click the tool's "+
|
|
"'Connect to MIPS' button ('Assemble and Run' in the stand-alone version) or the tool's Reset button! If you run a "+
|
|
"MIPS program and reset it in MARS, the controller's Ready bit is cleared to 0! Configure the Data Segment Window to "+
|
|
"display the MMIO address range so you can directly observe values stored in the MMIO addresses given above.\n"+
|
|
"\n"+
|
|
"COOL NEW FEATURE (MARS 4.5, AUGUST 2014): Clear the display window from MIPS program\n"+
|
|
"\n"+
|
|
"When ASCII 12 (form feed) is stored in the Transmitter Data register, the tool's Display window will be cleared "+
|
|
"following the specified transmission delay.\n"+
|
|
"\n"+
|
|
"COOL NEW FEATURE (MARS 4.5, AUGUST 2014): Simulate a text-based virtual terminal with (x,y) positioning\n"+
|
|
"\n"+
|
|
"When ASCII 7 (bell) is stored in the Transmitter Data register, the cursor in the tool's Display window will "+
|
|
"be positioned at the (X,Y) coordinate specified by its high-order 3 bytes, following the specfied transmission delay. "+
|
|
"Place the X position (column) in bit positions 20-31 of the "+
|
|
"Transmitter Data register and place the Y position (row) in bit positions 8-19. The cursor is not displayed "+
|
|
"but subsequent transmitted characters will be displayed starting at that position. Position (0,0) is at upper left. "+
|
|
"Why did I select the ASCII Bell character? Just for fun!\n"+
|
|
"\n"+
|
|
"The dimensions (number of columns and rows) of the virtual text-based terminal are calculated based on the display "+
|
|
"window size and font specifications. This calculation occurs during program execution upon first use of the ASCII 7 code. "+
|
|
"It will not change until the Reset button is clicked, even if the window is resized. The window dimensions are included in "+
|
|
"its title, which will be updated upon window resize or font change. No attempt is made to reposition data characters already "+
|
|
"transmitted by the program. To change the dimensions of the virtual terminal, resize the Display window as desired (note there "+
|
|
"is an adjustible splitter between the Display and Keyboard windows) then click the tool's Reset button. "+
|
|
"Implementation detail: the window is implemented by a JTextArea to which text is written as a string. "+
|
|
"Its caret (cursor) position is required to be a position within the string. I simulated a text terminal with random positioning "+
|
|
"by pre-allocating a string of spaces with one space per (X,Y) position and an embedded newline where each line ends. Each character "+
|
|
"transmitted to the window thus replaces an existing character in the string.\n"+
|
|
"\n"+
|
|
"Thanks to Eric Wang at Washington State University, who requested these features to enable use of this display as the target "+
|
|
"for programming MMIO text-based games.\n"+
|
|
"\n"+
|
|
"Contact Pete Sanderson at psanderson@otterbein.edu with questions or comments.\n";
|
|
JButton help = new JButton("Help");
|
|
help.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
JTextArea ja = new JTextArea(helpContent);
|
|
ja.setRows(30);
|
|
ja.setColumns(60);
|
|
ja.setLineWrap(true);
|
|
ja.setWrapStyleWord(true);
|
|
// Make the Help dialog modeless (can remain visible while working with other components).
|
|
// Unfortunately, JOptionPane.showMessageDialog() cannot be made modeless. I found two
|
|
// workarounds:
|
|
// (1) Use JDialog and the additional work that requires
|
|
// (2) create JOptionPane object, get JDialog from it, make the JDialog modeless
|
|
// Solution 2 is shorter but requires Java 1.6. Trying to keep MARS at 1.5. So we
|
|
// do it the hard way. DPS 16-July-2014
|
|
final JDialog d;
|
|
final String title = "Simulating the Keyboard and Display";
|
|
// The following is necessary because there are different JDialog constructors for Dialog and
|
|
// Frame and theWindow is declared a Window, superclass for both.
|
|
d = (theWindow instanceof Dialog) ? new JDialog((Dialog) theWindow, title, false)
|
|
: new JDialog((Frame) theWindow, title, false);
|
|
d.setSize(ja.getPreferredSize());
|
|
d.getContentPane( ).setLayout(new BorderLayout( ));
|
|
d.getContentPane( ).add(new JScrollPane(ja),BorderLayout.CENTER);
|
|
JButton b = new JButton("Close");
|
|
b.addActionListener(
|
|
new ActionListener( ) {
|
|
public void actionPerformed(ActionEvent ev) {
|
|
d.setVisible(false);
|
|
d.dispose( );
|
|
}
|
|
});
|
|
JPanel p = new JPanel( ); // Flow layout will center button.
|
|
p.add(b);
|
|
d.getContentPane( ).add(p, BorderLayout.SOUTH);
|
|
d.setLocationRelativeTo(theWindow);
|
|
d.setVisible(true);
|
|
// This alternative technique is simpler than the above but requires java 1.6! DPS 16-July-2014
|
|
// JOptionPane theStuff = new JOptionPane(new JScrollPane(ja),JOptionPane.INFORMATION_MESSAGE,
|
|
// JOptionPane.DEFAULT_OPTION, null, new String[]{"Close"} );
|
|
// JDialog theDialog = theStuff.createDialog(theWindow, "Simulating the Keyboard and Display");
|
|
// theDialog.setModal(false);
|
|
// theDialog.setVisible(true);
|
|
// The original code. Cannot be made modeless.
|
|
// JOptionPane.showMessageDialog(theWindow, new JScrollPane(ja),
|
|
// "Simulating the Keyboard and Display", JOptionPane.INFORMATION_MESSAGE);
|
|
}
|
|
});
|
|
return help;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Private methods defined to support the above.
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// UI components and layout for upper part of GUI, where simulated display is located.
|
|
private JComponent buildDisplay() {
|
|
displayPanel = new JPanel(new BorderLayout());
|
|
TitledBorder tb = new TitledBorder(displayPanelTitle);
|
|
tb.setTitleJustification(TitledBorder.CENTER);
|
|
displayPanel.setBorder(tb);
|
|
display = new JTextArea();
|
|
display.setFont(defaultFont);
|
|
display.setEditable(false);
|
|
display.setMargin(textAreaInsets);
|
|
updateDisplayBorder = new DisplayResizeAdapter();
|
|
// To update display of size in the Display text area when window or font size changes.
|
|
display.addComponentListener(updateDisplayBorder);
|
|
// To update display of caret position in the Display text area when caret position changes.
|
|
display.addCaretListener(
|
|
new CaretListener() {
|
|
public void caretUpdate(CaretEvent e) {
|
|
simulator.repaintDisplayPanelBorder();
|
|
}
|
|
}
|
|
);
|
|
|
|
// 2011-07-29: Patrik Lundin, patrik@lundin.info
|
|
// Added code so display autoscrolls.
|
|
DefaultCaret caret = (DefaultCaret)display.getCaret();
|
|
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
|
|
// end added autoscrolling
|
|
|
|
displayScrollPane = new JScrollPane(display);
|
|
displayScrollPane.setPreferredSize(preferredTextAreaDimension);
|
|
|
|
displayPanel.add(displayScrollPane);
|
|
displayOptions = new JPanel();
|
|
delayTechniqueChooser = new JComboBox(delayTechniques);
|
|
delayTechniqueChooser.setToolTipText("Technique for determining simulated transmitter device processing delay");
|
|
delayTechniqueChooser.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
transmitDelayInstructionCountLimit = generateDelay();
|
|
}
|
|
});
|
|
delayLengthPanel = new DelayLengthPanel();
|
|
displayAfterDelayCheckBox = new JCheckBox("DAD", true);
|
|
displayAfterDelayCheckBox.setToolTipText("Display After Delay: if checked, transmitter data not displayed until after delay");
|
|
displayAfterDelayCheckBox.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
displayAfterDelay = displayAfterDelayCheckBox.isSelected();
|
|
}
|
|
});
|
|
|
|
//font button to display font
|
|
fontButton = new JButton("Font");
|
|
fontButton.setToolTipText("Select the font for the display panel");
|
|
fontButton.addActionListener(new FontChanger()) ;
|
|
displayOptions.add(fontButton);
|
|
displayOptions.add(displayAfterDelayCheckBox);
|
|
displayOptions.add(delayTechniqueChooser);
|
|
displayOptions.add(delayLengthPanel);
|
|
displayPanel.add(displayOptions,BorderLayout.SOUTH);
|
|
return displayPanel;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// UI components and layout for lower part of GUI, where simulated keyboard is located.
|
|
private JComponent buildKeyboard() {
|
|
keyboardPanel = new JPanel(new BorderLayout());
|
|
keyEventAccepter = new JTextArea();
|
|
keyEventAccepter.setEditable(true);
|
|
keyEventAccepter.setFont(defaultFont);
|
|
keyEventAccepter.setMargin(textAreaInsets);
|
|
keyAccepterScrollPane = new JScrollPane(keyEventAccepter);
|
|
keyAccepterScrollPane.setPreferredSize(preferredTextAreaDimension);
|
|
keyEventAccepter.addKeyListener(new KeyboardKeyListener());
|
|
keyboardPanel.add(keyAccepterScrollPane);
|
|
TitledBorder tb =new TitledBorder(keyboardPanelTitle);
|
|
tb.setTitleJustification(TitledBorder.CENTER);
|
|
keyboardPanel.setBorder(tb);
|
|
return keyboardPanel;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// update the MMIO Control register memory cell. We will delegate.
|
|
private void updateMMIOControl(int addr, int intValue) {
|
|
updateMMIOControlAndData(addr, intValue, 0, 0, true);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// update the MMIO Control and Data register pair -- 2 memory cells. We will delegate.
|
|
private void updateMMIOControlAndData(int controlAddr, int controlValue, int dataAddr, int dataValue) {
|
|
updateMMIOControlAndData(controlAddr, controlValue, dataAddr, dataValue, false);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// This one does the work: update the MMIO Control and optionally the Data register as well
|
|
// NOTE: last argument TRUE means update only the MMIO Control register; FALSE means update both Control and Data.
|
|
private synchronized void updateMMIOControlAndData(int controlAddr, int controlValue, int dataAddr, int dataValue, boolean controlOnly) {
|
|
if (!this.isBeingUsedAsAMarsTool || (this.isBeingUsedAsAMarsTool && connectButton.isConnected())) {
|
|
synchronized (Globals.memoryAndRegistersLock) {
|
|
try {
|
|
Globals.memory.setRawWord(controlAddr, controlValue);
|
|
if (!controlOnly) Globals.memory.setRawWord(dataAddr, dataValue);
|
|
}
|
|
catch (AddressErrorException aee) {
|
|
System.out.println("Tool author specified incorrect MMIO address!"+aee);
|
|
System.exit(0);
|
|
}
|
|
}
|
|
// HERE'S A HACK!! Want to immediately display the updated memory value in MARS
|
|
// but that code was not written for event-driven update (e.g. Observer) --
|
|
// it was written to poll the memory cells for their values. So we force it to do so.
|
|
|
|
if (Globals.getGui() != null && Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().getCodeHighlighting() ) {
|
|
Globals.getGui().getMainPane().getExecutePane().getDataSegmentWindow().updateValues();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Return value of the given MMIO control register after ready (low order) bit set (to 1).
|
|
// Have to preserve the value of Interrupt Enable bit (bit 1)
|
|
private static boolean isReadyBitSet(int mmioControlRegister) {
|
|
try {
|
|
return (Globals.memory.get(mmioControlRegister, Memory.WORD_LENGTH_BYTES) & 1) == 1;
|
|
}
|
|
catch (AddressErrorException aee) {
|
|
System.out.println("Tool author specified incorrect MMIO address!"+aee);
|
|
System.exit(0);
|
|
}
|
|
return false; // to satisfy the compiler -- this will never happen.
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Return value of the given MMIO control register after ready (low order) bit set (to 1).
|
|
// Have to preserve the value of Interrupt Enable bit (bit 1)
|
|
private static int readyBitSet(int mmioControlRegister) {
|
|
try {
|
|
return Globals.memory.get(mmioControlRegister, Memory.WORD_LENGTH_BYTES) | 1;
|
|
}
|
|
catch (AddressErrorException aee) {
|
|
System.out.println("Tool author specified incorrect MMIO address!"+aee);
|
|
System.exit(0);
|
|
}
|
|
return 1; // to satisfy the compiler -- this will never happen.
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Return value of the given MMIO control register after ready (low order) bit cleared (to 0).
|
|
// Have to preserve the value of Interrupt Enable bit (bit 1). Bits 2 and higher don't matter.
|
|
private static int readyBitCleared(int mmioControlRegister) {
|
|
try {
|
|
return Globals.memory.get(mmioControlRegister, Memory.WORD_LENGTH_BYTES) & 2;
|
|
}
|
|
catch (AddressErrorException aee) {
|
|
System.out.println("Tool author specified incorrect MMIO address!"+aee);
|
|
System.exit(0);
|
|
}
|
|
return 0; // to satisfy the compiler -- this will never happen.
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Transmit delay is simulated by counting instruction executions.
|
|
// Here we simly initialize (or reset) the variables.
|
|
private void initializeTransmitDelaySimulator() {
|
|
this.countingInstructions = false;
|
|
this.instructionCount = 0;
|
|
this.transmitDelayInstructionCountLimit = this.generateDelay();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Calculate transmitter delay (# instruction executions) based on
|
|
// current combo box and slider settings.
|
|
|
|
private int generateDelay() {
|
|
double sliderValue = delayLengthPanel.getDelayLength();
|
|
TransmitterDelayTechnique technique = (TransmitterDelayTechnique) delayTechniqueChooser.getSelectedItem();
|
|
return technique.generateDelay(sliderValue);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Class to grab keystrokes going to keyboard echo area and send them to MMIO area
|
|
//
|
|
|
|
private class KeyboardKeyListener implements KeyListener {
|
|
public void keyTyped(KeyEvent e) {
|
|
int updatedReceiverControl = readyBitSet(RECEIVER_CONTROL);
|
|
updateMMIOControlAndData(RECEIVER_CONTROL, updatedReceiverControl, RECEIVER_DATA, e.getKeyChar() & 0x00000ff);
|
|
if (updatedReceiverControl != 1
|
|
&& (Coprocessor0.getValue(Coprocessor0.STATUS) & 2)==0 // Added by Carl Hauser Nov 2008
|
|
&& (Coprocessor0.getValue(Coprocessor0.STATUS) & 1)==1) {
|
|
// interrupt-enabled bit is set in both Receiver Control and in
|
|
// Coprocessor0 Status register, and Interrupt Level Bit is 0, so trigger external interrupt.
|
|
mars.simulator.Simulator.externalInterruptingDevice = Exceptions.EXTERNAL_INTERRUPT_KEYBOARD;
|
|
}
|
|
}
|
|
|
|
|
|
/* Ignore key pressed event from the text field. */
|
|
public void keyPressed(KeyEvent e) {
|
|
}
|
|
|
|
/* Ignore key released event from the text field. */
|
|
public void keyReleased(KeyEvent e) {
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Class for selecting transmitter delay lengths (# of MIPS instruction executions).
|
|
//
|
|
|
|
private class DelayLengthPanel extends JPanel {
|
|
private final static int DELAY_INDEX_MIN = 0;
|
|
private final static int DELAY_INDEX_MAX = 40;
|
|
private final static int DELAY_INDEX_INIT = 4;
|
|
private double[] delayTable = {
|
|
1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, // 0-10
|
|
150, 200, 300, 400, 500, 600, 700, 800, 900, 1000, //11-20
|
|
1500, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000,10000, //21-30
|
|
20000,40000,60000,80000,100000,200000,400000,600000,800000,1000000//31-40
|
|
};
|
|
private JLabel sliderLabel=null;
|
|
private volatile int delayLengthIndex = DELAY_INDEX_INIT;
|
|
|
|
public DelayLengthPanel() {
|
|
super(new BorderLayout());
|
|
delayLengthSlider = new JSlider(JSlider.HORIZONTAL, DELAY_INDEX_MIN,DELAY_INDEX_MAX,DELAY_INDEX_INIT);
|
|
delayLengthSlider.setSize(new Dimension(100,(int)delayLengthSlider.getSize().getHeight()));
|
|
delayLengthSlider.setMaximumSize(delayLengthSlider.getSize());
|
|
delayLengthSlider.addChangeListener(new DelayLengthListener());
|
|
sliderLabel = new JLabel(setLabel(delayLengthIndex));
|
|
sliderLabel.setHorizontalAlignment(JLabel.CENTER);
|
|
sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
|
this.add(sliderLabel, BorderLayout.NORTH);
|
|
this.add(delayLengthSlider, BorderLayout.CENTER);
|
|
this.setToolTipText("Parameter for simulated delay length (MIPS instruction execution count)");
|
|
}
|
|
|
|
// returns current delay length setting, in instructions.
|
|
public double getDelayLength() {
|
|
return delayTable[delayLengthIndex];
|
|
}
|
|
|
|
|
|
// set label wording depending on current speed setting
|
|
private String setLabel(int index) {
|
|
return "Delay length: "+((int)delayTable[index])+" instruction executions";
|
|
}
|
|
|
|
|
|
// Both revises label as user slides and updates current index when sliding stops.
|
|
private class DelayLengthListener implements ChangeListener {
|
|
public void stateChanged(ChangeEvent e) {
|
|
JSlider source = (JSlider)e.getSource();
|
|
if (!source.getValueIsAdjusting()) {
|
|
delayLengthIndex = (int)source.getValue();
|
|
transmitDelayInstructionCountLimit = generateDelay();
|
|
}
|
|
else {
|
|
sliderLabel.setText(setLabel(source.getValue()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
//Interface and classes for Transmitter Delay-generating techniques.
|
|
//
|
|
|
|
private interface TransmitterDelayTechnique {
|
|
public int generateDelay(double parameter);
|
|
}
|
|
|
|
// Delay value is fixed, and equal to slider value.
|
|
private class FixedLengthDelay implements TransmitterDelayTechnique {
|
|
public String toString() {
|
|
return "Fixed transmitter delay, select using slider";
|
|
}
|
|
public int generateDelay(double fixedDelay) {
|
|
return (int) fixedDelay;
|
|
}
|
|
}
|
|
|
|
// Randomly pick value from range 1 to slider setting, uniform distribution
|
|
// (each value has equal probability of being chosen).
|
|
private class UniformlyDistributedDelay implements TransmitterDelayTechnique {
|
|
Random randu;
|
|
public UniformlyDistributedDelay() {
|
|
randu = new Random();
|
|
}
|
|
public String toString() {
|
|
return "Uniformly distributed delay, min=1, max=slider";
|
|
}
|
|
public int generateDelay(double max) {
|
|
return randu.nextInt((int)max)+1;
|
|
}
|
|
}
|
|
|
|
// Pretty badly-hacked normal distribution, but is more realistic than uniform!
|
|
// Get sample from Normal(0,1) -- mean=0, s.d.=1 -- multiply it by slider
|
|
// value, take absolute value to make sure we don't get negative,
|
|
// add 1 to make sure we don't get 0.
|
|
private class NormallyDistributedDelay implements TransmitterDelayTechnique {
|
|
Random randn;
|
|
public NormallyDistributedDelay() {
|
|
randn = new Random();
|
|
}
|
|
public String toString() {
|
|
return "'Normally' distributed delay: floor(abs(N(0,1)*slider)+1)";
|
|
}
|
|
public int generateDelay(double mult) {
|
|
return (int) (Math.abs(randn.nextGaussian()*mult)+1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Font dialog for the display panel
|
|
* Almost all of the code is used from the SettingsHighlightingAction
|
|
* class.
|
|
*/
|
|
|
|
private class FontSettingDialog extends AbstractFontSettingDialog {
|
|
private boolean resultOK;
|
|
|
|
public FontSettingDialog(Frame owner, String title, Font currentFont) {
|
|
super(owner, title, true, currentFont);
|
|
}
|
|
|
|
private Font showDialog() {
|
|
resultOK = true;
|
|
// Because dialog is modal, this blocks until user terminates the dialog.
|
|
this.setVisible(true);
|
|
return resultOK ? getFont() : null;
|
|
}
|
|
|
|
protected void closeDialog() {
|
|
this.setVisible(false);
|
|
// Update display text dimensions based on current font and size. DPS 22-July-2014
|
|
updateDisplayBorder.componentResized(null);
|
|
}
|
|
|
|
private void performCancel() {
|
|
resultOK = false;
|
|
}
|
|
|
|
// Control buttons for the dialog.
|
|
protected Component buildControlPanel() {
|
|
Box controlPanel = Box.createHorizontalBox();
|
|
JButton okButton = new JButton("OK");
|
|
okButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
apply(getFont());
|
|
closeDialog();
|
|
}
|
|
});
|
|
JButton cancelButton = new JButton("Cancel");
|
|
cancelButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
performCancel();
|
|
closeDialog();
|
|
}
|
|
});
|
|
JButton resetButton = new JButton("Reset");
|
|
resetButton.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
reset();
|
|
}
|
|
});
|
|
controlPanel.add(Box.createHorizontalGlue());
|
|
controlPanel.add(okButton);
|
|
controlPanel.add(Box.createHorizontalGlue());
|
|
controlPanel.add(cancelButton);
|
|
controlPanel.add(Box.createHorizontalGlue());
|
|
controlPanel.add(resetButton);
|
|
controlPanel.add(Box.createHorizontalGlue());
|
|
return controlPanel;
|
|
}
|
|
|
|
// Change the font for the keyboard and display
|
|
protected void apply(Font font) {
|
|
display.setFont(font);
|
|
keyEventAccepter.setFont(font);
|
|
}
|
|
|
|
}
|
|
|
|
private class FontChanger implements ActionListener {
|
|
public void actionPerformed(ActionEvent e) {
|
|
JButton button = (JButton) e.getSource();
|
|
FontSettingDialog fontDialog = new FontSettingDialog(null, "Select Text Font", display.getFont());
|
|
Font newFont = fontDialog.showDialog();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
} |