package edu.princeton.toy; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import edu.princeton.swing.*; import edu.princeton.toy.lang.*; /** * TSimDetailPane is a JPanel which shows the details (in human readable terms) of what the * TSimMachinePane displays. * * @author btsang * @version 7.1 */ public class TSimDetailPane extends JPanel { private static final String CLASS_STRING = TSimDetailPane.class.toString(); /** * The command to update the contents of the inputList with the stdin stream of the * virtualMachine. */ public static final String RESCALE_COMMAND = CLASS_STRING + "#rescaleCommand"; /** * The command to update the contents of the inputList with the stdin stream of the * virtualMachine. */ public static final String UPDATE_COMMAND = CLASS_STRING + "#updateCommand"; /** * The minimum scale which the TSimDetailPane can be scaled to. */ public static final int MIN_SCALE = 2; /** * The maxmimum scale which the TSimDetailPane can be scaled to. */ public static final int MAX_SCALE = Integer.MAX_VALUE; /** * The scale used in determining the preferred size of the TSimDetailPane. */ public static final int PREFERRED_SCALE = 14; /** * The border of the detail area in the TSimDetailPane. */ public static final Border DETAIL_AREA_BORDER = new LineBorder(Color.black, 1); private static final int CACHE_SIZE = 100; private static final Integer INTEGERS[] = new Integer[CACHE_SIZE]; private static final int REGISTERS_PER_ROW = 8; private static final int REGISTER_ROWS = (TVirtualMachine.REGISTER_COUNT + REGISTERS_PER_ROW - 1) / REGISTERS_PER_ROW; private static final int UNSCALED_WIDTH = REGISTERS_PER_ROW * 5 + 1; private static final int UNSCALED_HEIGHT = 5 + 3 * REGISTER_ROWS; /** * Initialize the INTEGERS array. */ static { for (int ctr = 0; ctr < CACHE_SIZE; ctr++) INTEGERS[ctr] = new Integer(ctr); } private static final Font FONT_CACHE[] = new Font[CACHE_SIZE]; private int scale; private Listener listener; private TVirtualMachine virtualMachine; private TSimMachinePane machinePane; private JPanel detailPanel; private JLabel registerNameLabels[]; private JLabel registerValueLabels[]; private JLabel pcInstrNameLabel; private JLabel pcInstrValueLabel; private JLabel pcInstrPseudoCodeLabel; private JLabel addrDataNameLabel; private JLabel addrDataValueLabel; private JLabel addrDataPseudoCodeLabel; /** * Creates a new TSimDetailPane. */ public TSimDetailPane(TVirtualMachine virtualMachine, TSimMachinePane machinePane) { super(null, true); setForeground(Color.black); if (virtualMachine == null) throw new NullPointerException(); this.virtualMachine = virtualMachine; if (machinePane == null) throw new NullPointerException(); this.machinePane = machinePane; listener = new Listener(); virtualMachine.addChangeListener(listener); machinePane.addChangeListener(listener); detailPanel = new JPanel(null, false); detailPanel.setForeground(null); detailPanel.setBackground(Color.white); detailPanel.setBorder(DETAIL_AREA_BORDER); registerNameLabels = new JLabel[TVirtualMachine.REGISTER_COUNT]; registerValueLabels = new JLabel[TVirtualMachine.REGISTER_COUNT]; for (int ctr = 0; ctr < TVirtualMachine.REGISTER_COUNT; ctr++) { detailPanel.add(registerNameLabels[ctr] = new JLabel()); registerNameLabels[ctr].setText("R[" + TWord.HEX_DIGITS[ctr] + "]"); registerNameLabels[ctr].setForeground(null); registerNameLabels[ctr].setHorizontalAlignment(SwingConstants.LEFT); registerNameLabels[ctr].setVerticalAlignment(SwingConstants.CENTER); detailPanel.add(registerValueLabels[ctr] = new JLabel()); registerValueLabels[ctr].setForeground(null); registerValueLabels[ctr].setHorizontalAlignment(SwingConstants.LEFT); registerValueLabels[ctr].setVerticalAlignment(SwingConstants.CENTER); } detailPanel.add(pcInstrNameLabel = new JLabel()); pcInstrNameLabel.setText("PC/INSTR:"); pcInstrNameLabel.setForeground(null); pcInstrNameLabel.setHorizontalAlignment(SwingConstants.LEFT); pcInstrNameLabel.setVerticalAlignment(SwingConstants.CENTER); detailPanel.add(pcInstrValueLabel = new JLabel()); pcInstrValueLabel.setForeground(null); pcInstrValueLabel.setHorizontalAlignment(SwingConstants.LEFT); pcInstrValueLabel.setVerticalAlignment(SwingConstants.CENTER); detailPanel.add(pcInstrPseudoCodeLabel = new JLabel()); pcInstrPseudoCodeLabel.setForeground(null); pcInstrPseudoCodeLabel.setHorizontalAlignment(SwingConstants.LEFT); pcInstrPseudoCodeLabel.setVerticalAlignment(SwingConstants.CENTER); detailPanel.add(addrDataNameLabel = new JLabel()); addrDataNameLabel.setText("ADDR/DATA:"); addrDataNameLabel.setForeground(null); addrDataNameLabel.setHorizontalAlignment(SwingConstants.LEFT); addrDataNameLabel.setVerticalAlignment(SwingConstants.CENTER); detailPanel.add(addrDataValueLabel = new JLabel()); addrDataValueLabel.setForeground(null); addrDataValueLabel.setHorizontalAlignment(SwingConstants.LEFT); addrDataValueLabel.setVerticalAlignment(SwingConstants.CENTER); detailPanel.add(addrDataPseudoCodeLabel = new JLabel()); addrDataPseudoCodeLabel.setForeground(null); addrDataPseudoCodeLabel.setHorizontalAlignment(SwingConstants.LEFT); addrDataPseudoCodeLabel.setVerticalAlignment(SwingConstants.CENTER); add(detailPanel); scale = -1; doCommand(RESCALE_COMMAND, INTEGERS[PREFERRED_SCALE]); setMinimumSize( new Dimension( UNSCALED_WIDTH * MIN_SCALE, UNSCALED_HEIGHT * MIN_SCALE ) ); setPreferredSize( new Dimension( UNSCALED_WIDTH * PREFERRED_SCALE, UNSCALED_HEIGHT * PREFERRED_SCALE ) ); enableEvents(AWTEvent.COMPONENT_EVENT_MASK); } /** * Sets the virtualMachine attached to this TSimDetailPane. * * @param virtualMachine The virtualMachine to be attached to this TSimDetailPane. */ public void setVirtualMachine(TVirtualMachine virtualMachine) { if (virtualMachine == null) throw new NullPointerException(); if (this.virtualMachine == virtualMachine) return; this.virtualMachine.removeChangeListener(listener); virtualMachine.addChangeListener(listener); this.virtualMachine = virtualMachine; doCommand(UPDATE_COMMAND, null); } /** * Returns the virtualMachine attached to this TSimDetailPane. * * @return The virtualMachine attached to this TSimDetailPane. */ public TVirtualMachine getVirtualMachine() { return virtualMachine; } /** * Sets the machinePane attached to this TSimDetailPane. * * @param machinePane The machinePane to be attached to this TSimDetailPane. */ public void setMachinePane(TSimMachinePane machinePane) { if (machinePane == null) throw new NullPointerException(); if (this.machinePane == machinePane) return; this.machinePane.removeChangeListener(listener); machinePane.addChangeListener(listener); this.machinePane = machinePane; doCommand(UPDATE_COMMAND, null); } /** * Returns the machinePane attached to this TSimDetailPane. * * @return The machinePane attached to this TSimDetailPane. */ public TSimMachinePane getMachinePane() { return machinePane; } /** * Performs a command based on the argument. * * @param command A string representing the command. Note that pointer equality (not string * equality) is tested here, so it is important to use the string constants defined in this * class. An IllegalArgumentException will be thrown if the argument is invalid. * @return True iff the command was executed sucessfully. */ public synchronized boolean doCommand(String command, Object extraInfo) { //////////////////////////////////////////////////////////////////////////////////////////// if (command == RESCALE_COMMAND) { int scale = ((Integer)extraInfo).intValue(); if (scale < MIN_SCALE || scale > MAX_SCALE) throw new IllegalArgumentException(); if (scale == this.scale) return false; Font font; if (scale >= CACHE_SIZE) { font = new Font("Monospaced", Font.BOLD, (int)(0.9 * scale)); } else { if (FONT_CACHE[scale] == null) FONT_CACHE[scale] = new Font("Monospaced", Font.BOLD, (int)(0.9 * scale)); font = FONT_CACHE[scale]; } for (int ctr = 0; ctr < TVirtualMachine.REGISTER_COUNT; ctr++) { int row = ctr / REGISTERS_PER_ROW; int column = ctr % REGISTERS_PER_ROW; registerNameLabels[ctr].setBounds( (1 + 5 * column) * scale, (1 + 3 * row) * scale, 4 * scale, 1 * scale ); registerNameLabels[ctr].setFont(font); registerValueLabels[ctr].setBounds( (1 + 5 * column) * scale, (2 + 3 * row) * scale, 4 * scale, 1 * scale ); registerValueLabels[ctr].setFont(font); } int pcInstrColumns = REGISTERS_PER_ROW / 2; int addrDataColumns = REGISTERS_PER_ROW - pcInstrColumns; pcInstrNameLabel.setBounds( 1 * scale, (1 + 3 * REGISTER_ROWS) * scale, (5 * pcInstrColumns - 1) * scale, 1 * scale ); pcInstrNameLabel.setFont(font); pcInstrValueLabel.setBounds( 1 * scale, (2 + 3 * REGISTER_ROWS) * scale, (5 * pcInstrColumns - 1) * scale, 1 * scale ); pcInstrValueLabel.setFont(font); pcInstrPseudoCodeLabel.setBounds( 1 * scale, (3 + 3 * REGISTER_ROWS) * scale, (5 * pcInstrColumns - 1) * scale, 1 * scale ); pcInstrPseudoCodeLabel.setFont(font); addrDataNameLabel.setBounds( (1 + 5 * pcInstrColumns) * scale, (1 + 3 * REGISTER_ROWS) * scale, (5 * addrDataColumns - 1) * scale, 1 * scale ); addrDataNameLabel.setFont(font); addrDataValueLabel.setBounds( (1 + 5 * pcInstrColumns) * scale, (2 + 3 * REGISTER_ROWS) * scale, (5 * addrDataColumns - 1) * scale, 1 * scale ); addrDataValueLabel.setFont(font); addrDataPseudoCodeLabel.setBounds( (1 + 5 * pcInstrColumns) * scale, (3 + 3 * REGISTER_ROWS) * scale, (5 * addrDataColumns - 1) * scale, 1 * scale ); addrDataPseudoCodeLabel.setFont(font); repaint(); return true; } //////////////////////////////////////////////////////////////////////////////////////////// if (command == UPDATE_COMMAND) { for (int ctr = 0; ctr < TVirtualMachine.REGISTER_COUNT; ctr++) { TWord register = virtualMachine.getRegister(ctr); registerValueLabels[ctr].setText(register.toHexString(false)); } TWord pc = virtualMachine.getProgramCtr(); TWord instr = virtualMachine.getCurrentInstruction(); pcInstrValueLabel.setText( TWord.HEX_PAIRS[pc.getValue()] + ": " + instr.toHexString(false) ); pcInstrPseudoCodeLabel.setText(instr.toPseudoCodeString(false)); TWord addr = machinePane.getAddr(); TWord data = machinePane.getData(); addrDataValueLabel.setText( TWord.HEX_PAIRS[addr.getValue()] + ": " + data.toHexString(false) ); addrDataPseudoCodeLabel.setText(data.toPseudoCodeString(false)); repaint(); return true; } throw new IllegalArgumentException(); } /** * Intercept ComponentEvents to do resize and move the detailPanel when this component is * resized. */ protected void processComponentEvent(ComponentEvent e) { if (e.getID() == ComponentEvent.COMPONENT_RESIZED) { int width = getWidth(); int height = getHeight(); int scale = Math.min(width / UNSCALED_WIDTH, height / UNSCALED_HEIGHT); if (scale < MIN_SCALE) scale = MIN_SCALE; if (scale > MAX_SCALE) scale = MAX_SCALE; detailPanel.setBounds( width / 2 - UNSCALED_WIDTH * scale / 2, height / 2 - UNSCALED_HEIGHT * scale / 2, UNSCALED_WIDTH * scale, UNSCALED_HEIGHT * scale ); if (scale >= CACHE_SIZE) doCommand(RESCALE_COMMAND, new Integer(scale)); else doCommand(RESCALE_COMMAND, INTEGERS[scale]); } super.processComponentEvent(e); } /** * The Listener of a TBaseConverterPane pays attention to the changes in the PTextFields, and * fires off changes to the other PTextFields. * * @author btsang * @version 7.1 */ protected class Listener implements ChangeListener { /** * Implement ChangeListener to update the list whenever the virtual machine's state changes. */ public void stateChanged(ChangeEvent e) { doCommand(UPDATE_COMMAND, null); } } }