Memory Window, Minor Bugfixes
Adds a new view that displays a single page of memory (user selectable) and allows direct editing of memory contents. Also fixes several minor bugs, including one that prevented the status pane from updating after each manual step.
This commit is contained in:
1 parent
0215e2e3d8
commit
f7c5c3b763
5 files changed
+305
-29
No files matched your search
@@ -4,9 +4,9 @@ SYMON - A 6502 System Simulator
|
||||
**NOTE: THIS IS BETA QUALITY SOFTWARE UNDER ACTIVE DEVELOPMENT. Feedback is
|
||||
welcome!**
|
||||
|
||||
**Version:** 0.7.0
|
||||
**Version:** 0.8.0
|
||||
|
||||
**Last Updated:** 9 December, 2012
|
||||
**Last Updated:** 29 December, 2012
|
||||
|
||||
Copyright (c) 2008-2012 Seth J. Morabito <web@loomcom.com>
|
||||
|
||||
@@ -90,6 +90,7 @@ running.
|
||||
|
||||
## 4.0 Revision History
|
||||
|
||||
- 0.8.0: 29 December, 2012
|
||||
- 0.7.0: 9 December, 2012
|
||||
- 0.6: 5 November, 2012
|
||||
- 0.5: 21 October, 2012
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<groupId>com.loomcom.symon</groupId>
|
||||
<artifactId>symon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.7.0</version>
|
||||
<version>0.8.0</version>
|
||||
<name>symon</name>
|
||||
<url>http://www.loomcom.com/symon</url>
|
||||
<properties>
|
||||
|
||||
@@ -30,10 +30,8 @@ import com.loomcom.symon.exceptions.FifoUnderrunException;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
import com.loomcom.symon.exceptions.SymonException;
|
||||
import com.loomcom.symon.ui.*;
|
||||
import com.loomcom.symon.ui.Console;
|
||||
import com.loomcom.symon.ui.PreferencesDialog;
|
||||
import com.loomcom.symon.ui.StatusPanel;
|
||||
import com.loomcom.symon.ui.TraceLog;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
@@ -79,8 +77,8 @@ public class Simulator implements Observer {
|
||||
private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE);
|
||||
|
||||
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
|
||||
// to refresh the view on every simulated clock cycle. Instead, we will only refresh the view after this
|
||||
// number of steps when running normally.
|
||||
// to refresh the status view on every simulated clock cycle. Instead, we will only refresh the status view
|
||||
// after this number of steps when running normally.
|
||||
//
|
||||
// Since we're simulating a 1MHz 6502 here, we have a 1 us delay between steps. Setting this to 10000
|
||||
// should give us a status update every 10 ms.
|
||||
@@ -110,16 +108,16 @@ public class Simulator implements Observer {
|
||||
*/
|
||||
private JFrame mainWindow;
|
||||
|
||||
/**
|
||||
* The Console Window is connected to the ACIA's TX and RX lines.
|
||||
*/
|
||||
private JFrame consoleWindow;
|
||||
|
||||
/**
|
||||
* The Trace Window shows the most recent 50,000 CPU states.
|
||||
*/
|
||||
private TraceLog traceLog;
|
||||
|
||||
/**
|
||||
* The Memory Window shows the contents of one page of memory.
|
||||
*/
|
||||
private MemoryWindow memoryWindow;
|
||||
|
||||
/**
|
||||
* The Zero Page Window shows the contents of page 0.
|
||||
*/
|
||||
@@ -236,6 +234,9 @@ public class Simulator implements Observer {
|
||||
// Prepare the log window
|
||||
traceLog = new TraceLog();
|
||||
|
||||
// Prepare the memory window
|
||||
memoryWindow = new MemoryWindow(bus);
|
||||
|
||||
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
console.requestFocus();
|
||||
@@ -296,9 +297,14 @@ public class Simulator implements Observer {
|
||||
private void handleStep() {
|
||||
try {
|
||||
step();
|
||||
if (traceLog.isVisible()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (traceLog.isVisible()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
statusPane.updateState(cpu);
|
||||
}
|
||||
});
|
||||
} catch (SymonException ex) {
|
||||
logger.log(Level.SEVERE, "Exception during simulator step: " + ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
@@ -402,6 +408,7 @@ public class Simulator implements Observer {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
// Create the main UI window
|
||||
Simulator simulator = new Simulator();
|
||||
simulator.createAndShowUi();
|
||||
@@ -628,6 +635,23 @@ public class Simulator implements Observer {
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleMemoryWindowAction extends AbstractAction {
|
||||
public ToggleMemoryWindowAction() {
|
||||
super("Memory Window", null);
|
||||
putValue(SHORT_DESCRIPTION, "Show or Hide the Memory Window");
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
synchronized (memoryWindow) {
|
||||
if (memoryWindow.isVisible()) {
|
||||
memoryWindow.setVisible(false);
|
||||
} else {
|
||||
memoryWindow.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SimulatorMenu extends JMenuBar {
|
||||
// Menu Items
|
||||
private JMenuItem loadProgramItem;
|
||||
@@ -680,10 +704,8 @@ public class Simulator implements Observer {
|
||||
*/
|
||||
|
||||
JMenu viewMenu = new JMenu("View");
|
||||
JMenu fontSubMenu = new JMenu("Font Size");
|
||||
|
||||
JMenu fontSubMenu = new JMenu("Console Font Size");
|
||||
ButtonGroup fontSizeGroup = new ButtonGroup();
|
||||
|
||||
makeFontSizeMenuItem(10, fontSubMenu, fontSizeGroup);
|
||||
makeFontSizeMenuItem(11, fontSubMenu, fontSizeGroup);
|
||||
makeFontSizeMenuItem(12, fontSubMenu, fontSizeGroup);
|
||||
@@ -695,13 +717,14 @@ public class Simulator implements Observer {
|
||||
makeFontSizeMenuItem(18, fontSubMenu, fontSizeGroup);
|
||||
makeFontSizeMenuItem(19, fontSubMenu, fontSizeGroup);
|
||||
makeFontSizeMenuItem(20, fontSubMenu, fontSizeGroup);
|
||||
|
||||
viewMenu.add(fontSubMenu);
|
||||
|
||||
JRadioButtonMenuItem showTraceLog = new JRadioButtonMenuItem(new ToggleTraceWindowAction());
|
||||
|
||||
viewMenu.add(showTraceLog);
|
||||
|
||||
JRadioButtonMenuItem showMemoryTable = new JRadioButtonMenuItem(new ToggleMemoryWindowAction());
|
||||
viewMenu.add(showMemoryTable);
|
||||
|
||||
add(viewMenu);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2012 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.event.TableModelListener;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableModel;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Display a page of memory.
|
||||
*/
|
||||
public class MemoryWindow extends JFrame implements ActionListener {
|
||||
|
||||
private MemoryTableModel memoryTableModel;
|
||||
private JTable memoryTable;
|
||||
private JScrollPane scrollPane;
|
||||
private JTextField pageNumberTextField;
|
||||
private JLabel pageNumberLabel;
|
||||
|
||||
private static final Dimension MINIMUM_SIZE = new Dimension(400, 450);
|
||||
|
||||
public MemoryWindow(Bus bus) {
|
||||
this.memoryTableModel = new MemoryTableModel(bus);
|
||||
this.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);
|
||||
}
|
||||
|
||||
public int getPageNumber() {
|
||||
return memoryTableModel.getPageNumber();
|
||||
}
|
||||
|
||||
private void updatePageNumberTextField() {
|
||||
pageNumberTextField.setText(HexUtil.byteToHex(getPageNumber()));
|
||||
}
|
||||
|
||||
private void createUi() {
|
||||
setTitle("Memory Contents");
|
||||
this.memoryTable = new JTable(memoryTableModel);
|
||||
|
||||
memoryTable.setDragEnabled(false);
|
||||
memoryTable.setCellSelectionEnabled(false);
|
||||
memoryTable.setShowGrid(false);
|
||||
|
||||
pageNumberLabel = new JLabel("Page Number");
|
||||
pageNumberTextField = new JTextField(8);
|
||||
pageNumberTextField.addActionListener(this);
|
||||
|
||||
updatePageNumberTextField();
|
||||
|
||||
JPanel controlPanel = new JPanel();
|
||||
JPanel memoryPanel = new JPanel();
|
||||
memoryPanel.setLayout(new BorderLayout());
|
||||
|
||||
controlPanel.add(pageNumberLabel);
|
||||
controlPanel.add(pageNumberTextField);
|
||||
|
||||
this.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);
|
||||
setPreferredSize(MINIMUM_SIZE);
|
||||
|
||||
pack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle page numbers entered into the UI.
|
||||
*
|
||||
* @param e The action event
|
||||
*/
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
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);
|
||||
}
|
||||
updatePageNumberTextField();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The model that backs the Memory Table.
|
||||
*/
|
||||
private class MemoryTableModel extends AbstractTableModel {
|
||||
|
||||
private Bus bus;
|
||||
private int pageNumber;
|
||||
|
||||
private static final int COLUMN_COUNT = 9;
|
||||
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;
|
||||
}
|
||||
|
||||
public Object getValueAt(int row, int column) {
|
||||
try {
|
||||
if (column == 0) {
|
||||
return HexUtil.wordToHex(fullAddress(row, 1));
|
||||
} else {
|
||||
return HexUtil.byteToHex(bus.read(fullAddress(row, column)));
|
||||
}
|
||||
} 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);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2008-2012 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.Cpu;
|
||||
@@ -52,6 +72,8 @@ public class StatusPanel extends JPanel {
|
||||
private static final int EMPTY_BORDER = 10;
|
||||
private static final Border LABEL_BORDER = BorderFactory.createEmptyBorder(0, 5, 0, 0);
|
||||
private static final Font LABEL_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 12);
|
||||
private static final Dimension LARGE_TEXT_FIELD_SIZE = new Dimension(134, 22);
|
||||
private static final Dimension SMALL_TEXT_FIELD_SIZE = new Dimension(65, 22);
|
||||
|
||||
public StatusPanel() {
|
||||
super();
|
||||
@@ -126,11 +148,12 @@ public class StatusPanel extends JPanel {
|
||||
constraints.gridy = 1;
|
||||
add(statusFlagsPanel, constraints);
|
||||
|
||||
|
||||
constraints.insets = new Insets(5, 0, 0, 0);
|
||||
constraints.gridy = 2;
|
||||
add(opcodeLabel, constraints);
|
||||
|
||||
constraints.insets = new Insets(0, 0, 0, 0);
|
||||
constraints.insets = new Insets(2, 2, 2, 2);
|
||||
constraints.gridy = 3;
|
||||
add(opcodeField, constraints);
|
||||
|
||||
@@ -138,7 +161,7 @@ public class StatusPanel extends JPanel {
|
||||
constraints.gridy = 4;
|
||||
add(pcLabel, constraints);
|
||||
|
||||
constraints.insets = new Insets(0, 0, 0, 0);
|
||||
constraints.insets = new Insets(2, 2, 2, 2);
|
||||
constraints.gridy = 5;
|
||||
add(pcField, constraints);
|
||||
|
||||
@@ -149,7 +172,7 @@ public class StatusPanel extends JPanel {
|
||||
constraints.gridx = 1;
|
||||
add(aLabel, constraints);
|
||||
|
||||
constraints.insets = new Insets(0, 0, 0, 0);
|
||||
constraints.insets = new Insets(2, 2, 2, 2);
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = 7;
|
||||
add(spField, constraints);
|
||||
@@ -163,7 +186,7 @@ public class StatusPanel extends JPanel {
|
||||
constraints.gridx = 1;
|
||||
add(xLabel, constraints);
|
||||
|
||||
constraints.insets = new Insets(0, 0, 5, 0);
|
||||
constraints.insets = new Insets(2, 2, 2, 2);
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = 9;
|
||||
add(xField, constraints);
|
||||
@@ -266,9 +289,6 @@ public class StatusPanel extends JPanel {
|
||||
return label;
|
||||
}
|
||||
|
||||
private static final Dimension LARGE_TEXT_FIELD_SIZE = new Dimension(134, 22);
|
||||
private static final Dimension SMALL_TEXT_FIELD_SIZE = new Dimension(65, 22);
|
||||
|
||||
private JTextField makeTextField(Dimension size) {
|
||||
JTextField textField = new JTextField("");
|
||||
textField.setAlignmentX(LEFT_ALIGNMENT);
|
||||
@@ -276,6 +296,7 @@ public class StatusPanel extends JPanel {
|
||||
textField.setMinimumSize(size);
|
||||
textField.setMaximumSize(size);
|
||||
textField.setPreferredSize(size);
|
||||
textField.setBackground(Color.WHITE);
|
||||
return textField;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user