diff --git a/README.md b/README.md
index 91ecc8b..6dee62e 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/pom.xml b/pom.xml
index 82fc9e3..a52f42b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.loomcom.symon
symon
jar
- 0.7.0
+ 0.8.0
symon
http://www.loomcom.com/symon
diff --git a/src/main/java/com/loomcom/symon/Simulator.java b/src/main/java/com/loomcom/symon/Simulator.java
index 82e0d65..1ffe79d 100644
--- a/src/main/java/com/loomcom/symon/Simulator.java
+++ b/src/main/java/com/loomcom/symon/Simulator.java
@@ -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);
}
diff --git a/src/main/java/com/loomcom/symon/ui/MemoryWindow.java b/src/main/java/com/loomcom/symon/ui/MemoryWindow.java
new file mode 100644
index 0000000..9e30833
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/ui/MemoryWindow.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2008-2012 Seth J. Morabito
+ *
+ * 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;
+ }
+
+ }
+
+}
+
diff --git a/src/main/java/com/loomcom/symon/ui/StatusPanel.java b/src/main/java/com/loomcom/symon/ui/StatusPanel.java
index 2e046ef..fcdf8cb 100644
--- a/src/main/java/com/loomcom/symon/ui/StatusPanel.java
+++ b/src/main/java/com/loomcom/symon/ui/StatusPanel.java
@@ -1,6 +1,26 @@
-/**
+/*
+ * Copyright (c) 2008-2012 Seth J. Morabito
*
+ * 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;
}