diff --git a/README.md b/README.md
index 988f251..1b38d43 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,11 @@
SYMON - A 6502 System Simulator
===============================
-**NOTE: THIS IS BETA QUALITY SOFTWARE UNDER ACTIVE DEVELOPMENT. Feedback is
-welcome!**
+**NOTE: THIS SOFTWARE IS UNDER ACTIVE DEVELOPMENT. Feedback is welcome!**
-**Version:** 0.8.4
+**Version:** 0.8.5
-**Last Updated:** 4 March, 2013
+**Last Updated:** 30 March, 2013
Copyright (c) 2008-2013 Seth J. Morabito <web@loomcom.com>
@@ -98,7 +97,7 @@ Maven will build Symon, run unit tests, and produce a jar file in the
Symon is meant to be invoked directly from the jar file. To run with
Java 1.5 or greater, just type:
- $ java -jar symon-0.8.4.jar
+ $ java -jar symon-0.8.5.jar
When Symon is running, you should be presented with a simple graphical
interface.
@@ -140,6 +139,9 @@ running.
## 5.0 Revision History
+ - **0.8.5:** 30 March, 2013 - ASCII display for memory window.
+ Allows user to select a step count from a drop-down box.
+
- **0.8.4:** 4 March, 2013 - Fix for ZPX, ZPY display in the trace log
(change contributed by jsissom)
@@ -169,7 +171,7 @@ running.
- Feedback (in the form of dialogs, status bar, etc).
- Better debugging tools from the UI, including breakpoints
- and disassebly.
+ and disassembly.
- More accurate timing.
diff --git a/pom.xml b/pom.xml
index 6fc04f7..c25f237 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.loomcom.symon
symon
jar
- 0.8.4
+ 0.8.5
symon
http://www.loomcom.com/symon
@@ -38,7 +38,7 @@
junit
junit
- 4.7
+ 4.10
test
diff --git a/screenshots/console.png b/screenshots/console.png
index 833e503..0459149 100644
Binary files a/screenshots/console.png and b/screenshots/console.png differ
diff --git a/screenshots/full.jpg b/screenshots/full.jpg
index 4362504..d762478 100644
Binary files a/screenshots/full.jpg and b/screenshots/full.jpg differ
diff --git a/screenshots/memory_window.png b/screenshots/memory_window.png
index 58aac7a..7f0393e 100644
Binary files a/screenshots/memory_window.png and b/screenshots/memory_window.png differ
diff --git a/src/main/java/com/loomcom/symon/Preferences.java b/src/main/java/com/loomcom/symon/Preferences.java
index 914909f..b4b5e43 100644
--- a/src/main/java/com/loomcom/symon/Preferences.java
+++ b/src/main/java/com/loomcom/symon/Preferences.java
@@ -29,16 +29,12 @@ public interface Preferences {
public static final int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
- public static final int DEFAULT_BORDER_WIDTH = 10;
-
public static final boolean DEFAULT_HALT_ON_BREAK = true;
public JDialog getDialog();
public int getProgramStartAddress();
- public int getBorderWidth();
-
public boolean getHaltOnBreak();
public void updateUi();
diff --git a/src/main/java/com/loomcom/symon/Simulator.java b/src/main/java/com/loomcom/symon/Simulator.java
index ef71dc3..3f5022d 100644
--- a/src/main/java/com/loomcom/symon/Simulator.java
+++ b/src/main/java/com/loomcom/symon/Simulator.java
@@ -37,6 +37,8 @@ import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.*;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.Observable;
import java.util.Observer;
@@ -51,7 +53,7 @@ import java.util.logging.Logger;
* and a simulated ACIA for serial I/O. The ACIA is attached to a dumb terminal
* with a basic 80x25 character display.
*/
-public class Simulator implements Observer {
+public class Simulator {
// Constants used by the simulated system. These define the memory map.
private static final int BUS_BOTTOM = 0x0000;
@@ -71,8 +73,10 @@ public class Simulator implements Observer {
private static final int ROM_BASE = 0xC000;
private static final int ROM_SIZE = 0x4000;
- private static final int DEFAULT_FONT_SIZE = 12;
- private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE);
+ // UI constants
+ private static final int DEFAULT_FONT_SIZE = 12;
+ private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE);
+ private static final int CONSOLE_BORDER_WIDTH = 10;
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
// to refresh the status view on every simulated clock cycle. Instead, we will only refresh the status view
@@ -99,6 +103,9 @@ public class Simulator implements Observer {
// requested
private int stepsSinceLastUpdate = 0;
+ // The number of steps to run per click of the "Step" button
+ private int stepsPerClick = 1;
+
/**
* The Main Window is the primary control point for the simulator.
* It is in charge of the menu, and sub-windows. It also shows the
@@ -130,10 +137,16 @@ public class Simulator implements Observer {
private JButton runStopButton;
private JButton stepButton;
private JButton resetButton;
+ private JComboBox stepCountBox;
private JFileChooser fileChooser;
private PreferencesDialog preferences;
+ /**
+ * The list of step counts that will appear in the "Step" drop-down.
+ */
+ private static final String[] STEPS = {"1", "5", "10", "20", "50", "100"};
+
public Simulator() throws MemoryRangeException, IOException {
this.acia = new Acia(ACIA_BASE);
this.via = new Via(VIA_BASE);
@@ -172,10 +185,11 @@ public class Simulator implements Observer {
this.console = new com.loomcom.symon.ui.Console(80, 25, DEFAULT_FONT);
this.statusPane = new StatusPanel();
+ console.setBorderWidth(CONSOLE_BORDER_WIDTH);
+
// File Chooser
fileChooser = new JFileChooser(System.getProperty("user.dir"));
preferences = new PreferencesDialog(mainWindow, true);
- preferences.addObserver(this);
// Panel for Console and Buttons
JPanel consoleContainer = new JPanel();
@@ -189,8 +203,22 @@ public class Simulator implements Observer {
stepButton = new JButton("Step");
resetButton = new JButton("Reset");
+ stepCountBox = new JComboBox(STEPS);
+ stepCountBox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ try {
+ JComboBox cb = (JComboBox) actionEvent.getSource();
+ stepsPerClick = Integer.parseInt((String) cb.getSelectedItem());
+ } catch (NumberFormatException ex) {
+ stepsPerClick = 1;
+ stepCountBox.setSelectedIndex(0);
+ }
+ }
+ });
+
buttonContainer.add(runStopButton);
buttonContainer.add(stepButton);
+ buttonContainer.add(stepCountBox);
buttonContainer.add(resetButton);
// Left side - console
@@ -215,7 +243,7 @@ public class Simulator implements Observer {
stepButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
- handleStep();
+ handleStep(stepsPerClick);
}
});
@@ -290,11 +318,13 @@ public class Simulator implements Observer {
}
/**
- * Step once, and immediately refresh the UI.
+ * Step the requested number of times, and immediately refresh the UI.
*/
- private void handleStep() {
+ private void handleStep(int numSteps) {
try {
- step();
+ for (int i = 0; i < numSteps; i++) {
+ step();
+ }
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (traceLog.isVisible()) {
@@ -378,24 +408,6 @@ public class Simulator implements Observer {
});
}
- /**
- * The configuration has changed. Re-load.
- *
- * @param observable
- * @param o
- */
- public void update(Observable observable, Object o) {
- // Instance equality should work here, there is only one instance.
- if (observable == preferences) {
- int oldBorderWidth = console.getBorderWidth();
- if (oldBorderWidth != preferences.getBorderWidth()) {
- // Resize the main window if the border width has changed.
- console.setBorderWidth(preferences.getBorderWidth());
- mainWindow.pack();
- }
- }
- }
-
/**
* Main entry point to the simulator. Creates a simulator and shows the main
* window.
@@ -441,6 +453,7 @@ public class Simulator implements Observer {
public void run() {
// Don't allow step while the simulator is running
stepButton.setEnabled(false);
+ stepCountBox.setEnabled(false);
menuBar.simulatorDidStart();
// Toggle the state of the run button
runStopButton.setText("Stop");
@@ -461,6 +474,7 @@ public class Simulator implements Observer {
statusPane.updateState(cpu);
runStopButton.setText("Run");
stepButton.setEnabled(true);
+ stepCountBox.setEnabled(true);
if (traceLog.isVisible()) {
traceLog.refresh();
}
diff --git a/src/main/java/com/loomcom/symon/ui/MemoryWindow.java b/src/main/java/com/loomcom/symon/ui/MemoryWindow.java
index 0bfe027..0050cc7 100644
--- a/src/main/java/com/loomcom/symon/ui/MemoryWindow.java
+++ b/src/main/java/com/loomcom/symon/ui/MemoryWindow.java
@@ -28,11 +28,19 @@ 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;
@@ -45,8 +53,22 @@ 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(380, 400);
+ 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.
@@ -81,8 +103,12 @@ public class MemoryWindow extends JFrame implements ActionListener {
* Set the contents of the page number text field with the current
* page number, in hex.
*/
- private void updatePageNumberTextField() {
- pageNumberTextField.setText(HexUtil.byteToHex(getPageNumber()));
+ private void updateControls() {
+ int pageNumber = getPageNumber();
+
+ previousPageButton.setEnabled(pageNumber > 0x00);
+ nextPageButton.setEnabled(pageNumber < 0xff);
+ pageNumberTextField.setText(HexUtil.byteToHex(pageNumber));
}
/**
@@ -90,35 +116,54 @@ public class MemoryWindow extends JFrame implements ActionListener {
*/
private void createUi() {
setTitle("Memory Contents");
- this.memoryTable = new JTable(memoryTableModel);
+ this.memoryTable = new MemoryTable(memoryTableModel);
memoryTable.setDragEnabled(false);
memoryTable.setCellSelectionEnabled(false);
- memoryTable.setShowGrid(true);
- memoryTable.setShowHorizontalLines(true);
- memoryTable.setShowVerticalLines(true);
- memoryTable.setGridColor(Color.LIGHT_GRAY);
+ 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 Number");
+ JLabel pageNumberLabel = new JLabel("Page");
pageNumberTextField = new JTextField(8);
pageNumberTextField.addActionListener(this);
- updatePageNumberTextField();
+ 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);
@@ -131,7 +176,8 @@ public class MemoryWindow extends JFrame implements ActionListener {
getContentPane().add(memoryPanel, BorderLayout.CENTER);
setMinimumSize(MINIMUM_SIZE);
- setPreferredSize(MINIMUM_SIZE);
+ memoryPanel.setPreferredSize(memoryTable.getPreferredSize());
+ setPreferredSize(memoryPanel.getPreferredSize());
pack();
}
@@ -142,8 +188,21 @@ public class MemoryWindow extends JFrame implements ActionListener {
* @param e The action event
*/
public void actionPerformed(ActionEvent e) {
- if (e.getSource() == pageNumberTextField) {
-
+ 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.
@@ -156,7 +215,50 @@ public class MemoryWindow extends JFrame implements ActionListener {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Can't parse page number " +
pageNumberInput);
}
- updatePageNumberTextField();
+ 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;
}
}
@@ -168,7 +270,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
private Bus bus;
private int pageNumber;
- private static final int COLUMN_COUNT = 9;
+ private static final int COLUMN_COUNT = 17;
private static final int ROW_COUNT = 32;
public MemoryTableModel(Bus bus) {
@@ -213,15 +315,19 @@ public class MemoryWindow extends JFrame implements ActionListener {
@Override
public boolean isCellEditable(int row, int column) {
- return column > 0;
+ 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 {
+ } 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 "??";
@@ -234,7 +340,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
try {
String hexValue = (String)o;
int fullAddress = fullAddress(row, column);
- int newValue = Integer.parseInt(hexValue, 16);
+ int newValue = Integer.parseInt(hexValue, 16) & 0xff;
bus.write(fullAddress, newValue);
} catch (MemoryAccessException ex) {
;
diff --git a/src/main/java/com/loomcom/symon/ui/PreferencesDialog.java b/src/main/java/com/loomcom/symon/ui/PreferencesDialog.java
index 0651ccd..6fda5c4 100644
--- a/src/main/java/com/loomcom/symon/ui/PreferencesDialog.java
+++ b/src/main/java/com/loomcom/symon/ui/PreferencesDialog.java
@@ -40,10 +40,8 @@ public class PreferencesDialog extends Observable implements Preferences {
private JCheckBox haltOnBreakCheckBox;
private JTextField programLoadAddressField;
- private JTextField borderWidthField;
private int programLoadAddress = DEFAULT_PROGRAM_LOAD_ADDRESS;
- private int borderWidth = DEFAULT_BORDER_WIDTH;
private boolean haltOnBreak = DEFAULT_HALT_ON_BREAK;
public PreferencesDialog(Frame parent, boolean modal) {
@@ -72,14 +70,11 @@ public class PreferencesDialog extends Observable implements Preferences {
final JLabel haltOnBreakLabel = new JLabel("Halt on BRK");
final JLabel programLoadAddressLabel = new JLabel("Program Load Address");
- final JLabel borderWidthLabel = new JLabel("Console Border Width");
haltOnBreakCheckBox = new JCheckBox();
programLoadAddressField = new JTextField(8);
- borderWidthField = new JTextField(8);
programLoadAddressLabel.setLabelFor(programLoadAddressField);
- borderWidthLabel.setLabelFor(borderWidthField);
GridBagConstraints constraints = new GridBagConstraints();
@@ -99,17 +94,9 @@ public class PreferencesDialog extends Observable implements Preferences {
constraints.gridx = 1;
settingsContainer.add(programLoadAddressField, constraints);
- constraints.gridy = 2;
- constraints.gridx = 0;
- settingsContainer.add(borderWidthLabel, constraints);
-
- constraints.gridx = 1;
- settingsContainer.add(borderWidthField, constraints);
-
JButton applyButton = new JButton("Apply");
JButton cancelButton = new JButton("Cancel");
-
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
updateUi();
@@ -121,7 +108,6 @@ public class PreferencesDialog extends Observable implements Preferences {
public void actionPerformed(ActionEvent actionEvent) {
haltOnBreak = haltOnBreakCheckBox.isSelected();
programLoadAddress = hexToInt(programLoadAddressField.getText());
- borderWidth = Integer.parseInt(borderWidthField.getText());
updateUi();
// TODO: Actually check to see if values have changed, don't assume.
setChanged();
@@ -143,13 +129,6 @@ public class PreferencesDialog extends Observable implements Preferences {
return programLoadAddress;
}
- /**
- * @return The width of the console border, in pixels.
- */
- public int getBorderWidth() {
- return borderWidth;
- }
-
/**
* @return True if 'halt on break' is desired, false otherwise.
*/
@@ -160,7 +139,6 @@ public class PreferencesDialog extends Observable implements Preferences {
public void updateUi() {
haltOnBreakCheckBox.setSelected(haltOnBreak);
programLoadAddressField.setText(intToHex(programLoadAddress));
- borderWidthField.setText(Integer.toString(borderWidth));
}
private String intToHex(int i) {
diff --git a/src/main/java/com/loomcom/symon/util/HexUtil.java b/src/main/java/com/loomcom/symon/util/HexUtil.java
index 74c94b0..5004276 100644
--- a/src/main/java/com/loomcom/symon/util/HexUtil.java
+++ b/src/main/java/com/loomcom/symon/util/HexUtil.java
@@ -42,38 +42,67 @@ package com.loomcom.symon.util;
*
*/
public class HexUtil {
- private static final String[] HEX_CONSTANTS = {"00", "01", "02", "03", "04", "05", "06", "07",
- "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
- "10", "11", "12", "13", "14", "15", "16", "17",
- "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
- "20", "21", "22", "23", "24", "25", "26", "27",
- "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
- "30", "31", "32", "33", "34", "35", "36", "37",
- "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
- "40", "41", "42", "43", "44", "45", "46", "47",
- "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
- "50", "51", "52", "53", "54", "55", "56", "57",
- "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
- "60", "61", "62", "63", "64", "65", "66", "67",
- "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
- "70", "71", "72", "73", "74", "75", "76", "77",
- "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
- "80", "81", "82", "83", "84", "85", "86", "87",
- "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
- "90", "91", "92", "93", "94", "95", "96", "97",
- "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
- "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
- "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
- "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7",
- "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
- "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7",
- "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
- "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
- "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
- "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7",
- "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
- "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
- "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"};
+
+ static final String NON_PRINTABLE = ".";
+
+ static final String[] ASCII_CONSTANTS = {" ", "!", "\"", "#", "$", "%", "&", "'",
+ "(", ")", "*", "+", ",", "-", ".", "/",
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", ":", ";", "<", "=", ">", "?",
+ "@", "A", "B", "C", "D", "E", "F", "G",
+ "H", "I", "J", "K", "L", "M", "N", "O",
+ "P", "Q", "R", "S", "T", "U", "V", "W",
+ "X", "Y", "Z", "[", "\\", "]", "^", "_",
+ "`", "a", "b", "c", "d", "e", "f", "g",
+ "h", "i", "j", "k", "l", "m", "n", "o",
+ "p", "q", "r", "s", "t", "u", "v", "w",
+ "x", "y", "z", "{", "|", "}", "~"};
+
+ static final String[] HEX_CONSTANTS = {"00", "01", "02", "03", "04", "05", "06", "07",
+ "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
+ "10", "11", "12", "13", "14", "15", "16", "17",
+ "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
+ "20", "21", "22", "23", "24", "25", "26", "27",
+ "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
+ "30", "31", "32", "33", "34", "35", "36", "37",
+ "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
+ "40", "41", "42", "43", "44", "45", "46", "47",
+ "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
+ "50", "51", "52", "53", "54", "55", "56", "57",
+ "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
+ "60", "61", "62", "63", "64", "65", "66", "67",
+ "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
+ "70", "71", "72", "73", "74", "75", "76", "77",
+ "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
+ "80", "81", "82", "83", "84", "85", "86", "87",
+ "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
+ "90", "91", "92", "93", "94", "95", "96", "97",
+ "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
+ "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
+ "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
+ "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7",
+ "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
+ "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7",
+ "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
+ "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
+ "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
+ "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7",
+ "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
+ "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
+ "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"};
+
+ /**
+ * Very fast 8-bit int to ASCII conversion.
+ * @param val The value of an ASCII character.
+ * @return A string representing the ASCII character.
+ */
+ public static String byteToAscii(int val) {
+ if (val >= 32 && val <= 126) {
+ return ASCII_CONSTANTS[val - 32];
+ } else {
+ return NON_PRINTABLE;
+ }
+ }
/**
* Very fast 8-bit int to hex conversion, with zero-padded output.