1
0
mirror of https://github.com/sethm/symon.git synced 2024-06-14 02:29:33 +00:00
symon/src/main/java/com/loomcom/symon/ui/MemoryWindow.java
Seth Morabito 6a50476bfe Step Count and Memory Window enhancements
Introduces several changes requested by Mario Keller. The simulator now has a variable step count that can be selected by a drop-down box on the main window. This change also displays ASCII characters in the Memory window.
2013-03-30 21:28:52 -07:00

365 lines
12 KiB
Java

/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
*
* 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.
*/
package com.loomcom.symon.ui;
import com.loomcom.symon.Bus;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.util.HexUtil;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This Frame displays the contents of a page of memory. The page number to be displayed
* is selectable by the user.
*/
public class MemoryWindow extends JFrame implements ActionListener {
private MemoryTableModel memoryTableModel;
private JTable memoryTable;
private JTextField pageNumberTextField;
private JButton previousPageButton;
private JButton nextPageButton;
private static final Dimension MINIMUM_SIZE = new Dimension(320, 600);
// The width of column 0 (address), in pixels
private static final int ADDR_COL_WIDTH = 48;
private static final int HEX_COL_WIDTH = 32;
// The width of the ASCII cells, in pixels
private static final int ASCII_COL_WIDTH = 8;
// The start/end columns of the ASCII view
private static final int ASCII_COL_START = 9;
private static final int ASCII_COL_END = 16;
/**
* Initialize a new MemoryWindow frame with the specified Bus.
* The MemoryWindow frame will not be visible.
*
* @param bus The Bus the memory window will query for data.
*/
public MemoryWindow(Bus bus) {
this.memoryTableModel = new MemoryTableModel(bus);
createUi();
}
/**
* Set the current memory page to be inspected by the table.
*
* @param pageNumber The page number, from 0 to 255 (00 to FF hex)
*/
public void setPageNumber(int pageNumber) {
memoryTableModel.setPageNumber(pageNumber);
}
/**
* Returns the current page number being inspected by the table.
*
* @return The page number being inspected, from 0 to 255 (00 to FF hex)
*/
public int getPageNumber() {
return memoryTableModel.getPageNumber();
}
/**
* Set the contents of the page number text field with the current
* page number, in hex.
*/
private void updateControls() {
int pageNumber = getPageNumber();
previousPageButton.setEnabled(pageNumber > 0x00);
nextPageButton.setEnabled(pageNumber < 0xff);
pageNumberTextField.setText(HexUtil.byteToHex(pageNumber));
}
/**
* Set-up the UI.
*/
private void createUi() {
setTitle("Memory Contents");
this.memoryTable = new MemoryTable(memoryTableModel);
memoryTable.setDragEnabled(false);
memoryTable.setCellSelectionEnabled(false);
memoryTable.setIntercellSpacing(new Dimension(0, 0));
memoryTable.getTableHeader().setReorderingAllowed(false);
memoryTable.getTableHeader().setResizingAllowed(false);
memoryTable.getTableHeader().setVisible(false);
memoryTable.getColumnModel().getColumn(0).setMaxWidth(ADDR_COL_WIDTH);
for (int i = 1; i < ASCII_COL_START; i++) {
memoryTable.getColumnModel().getColumn(i).setMaxWidth(HEX_COL_WIDTH);
}
for (int i = ASCII_COL_START; i <= ASCII_COL_END; i++) {
memoryTable.getColumnModel().getColumn(i).setMaxWidth(ASCII_COL_WIDTH);
}
MemoryTableCellRenderer memoryTableCellRenderer = new MemoryTableCellRenderer();
memoryTableCellRenderer.setHorizontalAlignment(JLabel.CENTER);
memoryTable.setDefaultRenderer(String.class, memoryTableCellRenderer);
// Turn off tool-tips for the table.
ToolTipManager.sharedInstance().unregisterComponent(memoryTable);
ToolTipManager.sharedInstance().unregisterComponent(memoryTable.getTableHeader());
JLabel pageNumberLabel = new JLabel("Page");
pageNumberTextField = new JTextField(8);
pageNumberTextField.addActionListener(this);
nextPageButton = new JButton("Next >>");
previousPageButton = new JButton("<< Prev");
nextPageButton.addActionListener(this);
previousPageButton.addActionListener(this);
updateControls();
JPanel controlPanel = new JPanel();
JPanel memoryPanel = new JPanel();
memoryPanel.setLayout(new BorderLayout());
memoryPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
controlPanel.add(previousPageButton);
controlPanel.add(pageNumberLabel);
controlPanel.add(pageNumberTextField);
controlPanel.add(nextPageButton);
JScrollPane scrollPane = new JScrollPane(memoryTable);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
memoryPanel.add(scrollPane, BorderLayout.CENTER);
setLayout(new BorderLayout());
getContentPane().add(controlPanel, BorderLayout.NORTH);
getContentPane().add(memoryPanel, BorderLayout.CENTER);
setMinimumSize(MINIMUM_SIZE);
memoryPanel.setPreferredSize(memoryTable.getPreferredSize());
setPreferredSize(memoryPanel.getPreferredSize());
pack();
}
/**
* Handle page numbers entered into the UI.
*
* @param e The action event
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource() == previousPageButton) {
int currentPage = getPageNumber();
if (currentPage > 0x00) {
setPageNumber(currentPage - 1);
updateControls();
memoryTable.updateUI();
}
} else if (e.getSource() == nextPageButton) {
int currentPage = getPageNumber();
if (currentPage < 0xff) {
setPageNumber(currentPage + 1);
updateControls();
memoryTable.updateUI();
}
} else if (e.getSource() == pageNumberTextField) {
String pageNumberInput = pageNumberTextField.getText();
try {
// Try to parse a hex value out of the pageNumber.
int newPageNumber = Integer.parseInt(pageNumberInput, 16);
setPageNumber(newPageNumber & 0xff);
memoryTable.updateUI();
} catch (NumberFormatException ex) {
// An invalid number was entered. Log the error, but otherwise
// take no action.
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Can't parse page number " +
pageNumberInput);
}
updateControls();
}
}
/**
* A JTable that will automatically select all text in a cell
* being edited.
*/
private class MemoryTable extends JTable {
public MemoryTable(TableModel tableModel) {
super(tableModel);
}
@Override
public boolean editCellAt(int row, int col, EventObject e) {
boolean result = super.editCellAt(row, col, e);
final Component editor = getEditorComponent();
if (editor != null && editor instanceof JTextComponent) {
((JTextComponent) editor).selectAll();
}
return result;
}
}
private class MemoryTableCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int col) {
final Component cell = super.getTableCellRendererComponent(table, value,
isSelected, hasFocus,
row, col);
if (isSelected) {
cell.setBackground(Color.LIGHT_GRAY);
cell.setForeground(Color.BLACK);
}
return cell;
}
}
/**
* The model that backs the Memory Table.
*/
private class MemoryTableModel extends AbstractTableModel {
private Bus bus;
private int pageNumber;
private static final int COLUMN_COUNT = 17;
private static final int ROW_COUNT = 32;
public MemoryTableModel(Bus bus) {
this.bus = bus;
}
/**
* Set the current memory page to be inspected by the table.
*
* @param pageNumber The page number, from 0 to 255 (00 to FF hex)
*/
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
/**
* Returns the current page number being inspected by the table.
*
* @return The page number being inspected, from 0 to 255 (00 to FF hex)
*/
public int getPageNumber() {
return this.pageNumber;
}
public int getRowCount() {
return ROW_COUNT;
}
public int getColumnCount() {
return COLUMN_COUNT;
}
@Override
public String getColumnName(int i) {
return null;
}
@Override
public Class<?> getColumnClass(int i) {
return String.class;
}
@Override
public boolean isCellEditable(int row, int column) {
return (column > 0 && column < ASCII_COL_START);
}
public Object getValueAt(int row, int column) {
try {
if (column == 0) {
return HexUtil.wordToHex(fullAddress(row, 1));
} else if (column < 9) {
// Display hex value of the data
return HexUtil.byteToHex(bus.read(fullAddress(row, column)));
} else {
// Display the ASCII equivalent (if printable)
return HexUtil.byteToAscii(bus.read(fullAddress(row, column - 8)));
}
} catch (MemoryAccessException ex) {
return "??";
}
}
@Override
public void setValueAt(Object o, int row, int column) {
if (column > 0) {
try {
String hexValue = (String)o;
int fullAddress = fullAddress(row, column);
int newValue = Integer.parseInt(hexValue, 16) & 0xff;
bus.write(fullAddress, newValue);
} catch (MemoryAccessException ex) {
;
} catch (NumberFormatException ex) {
;
} catch (ClassCastException ex) {
;
}
fireTableCellUpdated(row, column);
}
}
private int fullAddress(int row, int column) {
int pageAddress = ((row * 8) + (column - 1)) & 0xff;
return (pageNumber << 8) | pageAddress;
}
}
}