/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Release Version 2.4
A project from the Physics Dept, The University of Oxford
Copyright (C) 2007-2010 The University of Oxford
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
Conceived and Developed by:
Rhys Newman, Ian Preston, Chris Dennis
End of licence header
*/
package org.jpc.debugger;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import org.jpc.emulator.memory.AddressSpace;
import javax.swing.*;
import org.jpc.debugger.util.*;
import org.jpc.emulator.memory.PhysicalAddressSpace;
public class WatchpointsFrame extends UtilityFrame implements PCListener
{
public static final String WATCHPOINT_FILE = "watchpoints.jpc";
public static final long WATCHPOINT_MAGIC = 0x81057FAB7272F11l;
private boolean edited;
private List<Watchpoint> watchpoints;
private WPModel model;
private JTable wpTable;
private String watchpointFileName;
private AddressSpace addressSpace;
private JCheckBoxMenuItem ignoreWP, watchPrimary;
public WatchpointsFrame()
{
super("Watchpoints");
watchpointFileName = WATCHPOINT_FILE;
watchpoints = new Vector();
model = new WPModel();
edited = false;
addressSpace=(AddressSpace) JPC.getObject(PhysicalAddressSpace.class);
wpTable = new JTable(model);
model.setupColumnWidths(wpTable);
String delWP = "Del WP";
InputMap in = new InputMap();
in.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), delWP);
ActionMap ac = new ActionMap();
ac.setParent(wpTable.getActionMap());
ac.put(delWP, new Deleter());
wpTable.setInputMap(JComponent.WHEN_FOCUSED, in);
wpTable.setActionMap(ac);
add("Center", new JScrollPane(wpTable));
JMenu options = new JMenu("Options");
options.add("Set Watchpoint").addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt)
{
try {
String input = JOptionPane.showInputDialog(WatchpointsFrame.this, "Enter the address (in Hex) for the watchpoint: ", "Watchpoint", JOptionPane.QUESTION_MESSAGE);
int address = (int) Long.parseLong(input.toLowerCase(), 16);
setWatchpoint(address);
} catch (Exception e) {
}
}
});
options.addSeparator();
options.add("Remove All Watchpoints").addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt)
{
removeAllWatchpoints();
}
});
options.addSeparator();
ignoreWP = new JCheckBoxMenuItem("Ignore Watchpoints");
options.add(ignoreWP);
watchPrimary = new JCheckBoxMenuItem("Watch 'Primary' watchpoints only");
options.add(watchPrimary);
JMenuBar bar = new JMenuBar();
bar.add(new WPFileMenu());
bar.add(options);
setJMenuBar(bar);
setPreferredSize(new Dimension(600, 300));
JPC.getInstance().objects().addObject(this);
loadWatchpoints();
}
public boolean WatchPrimaryOnly()
{
return watchPrimary.getState();
}
public boolean ignoreWatchpoints()
{
return ignoreWP.getState();
}
public boolean isEdited()
{
return edited;
}
public void frameClosed()
{
if (edited)
{
if (JOptionPane.showConfirmDialog(this, "Do you want to save the changes to the Watchpoints?", "Save Watchpoints", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION)
saveWatchpoints();
edited = false;
}
JPC.getInstance().objects().removeObject(this);
}
class WPFileMenu extends JMenu implements ActionListener
{
private JMenuItem load, save, saveAs, importWP;
WPFileMenu()
{
super("File");
load = add("Load Watchpoints");
load.addActionListener(this);
save = add("Save Watchpoints");
save.addActionListener(this);
saveAs = add("Save Watchpoints As");
saveAs.addActionListener(this);
addSeparator();
importWP = add("Import Watchpoints");
importWP.addActionListener(this);
}
private String deriveWPFileName(String name)
{
String nm = name.toLowerCase();
if (nm.endsWith(".jpc"))
return name;
int dot = nm.indexOf('.');
if (dot < 0)
dot = nm.length();
return name.substring(0, dot)+".jpc";
}
public void actionPerformed(ActionEvent evt)
{
JFileChooser chooser = (JFileChooser) JPC.getObject(JFileChooser.class);
if (evt.getSource() == load)
{
if (chooser.showOpenDialog(JPC.getInstance()) != JFileChooser.APPROVE_OPTION)
return;
watchpointFileName = chooser.getSelectedFile().getAbsolutePath();
removeAllWatchpoints();
loadWatchpoints();
}
else if (evt.getSource() == save)
{
saveWatchpoints();
}
else if (evt.getSource() == importWP)
{
if (chooser.showOpenDialog(JPC.getInstance()) != JFileChooser.APPROVE_OPTION)
return;
removeAllWatchpoints();
String fileName = chooser.getSelectedFile().getAbsolutePath();
importWatchpoints(fileName, false);
}
else if (evt.getSource() == saveAs)
{
if (chooser.showSaveDialog(JPC.getInstance()) != JFileChooser.APPROVE_OPTION)
return;
watchpointFileName = chooser.getSelectedFile().getAbsolutePath();
saveWatchpoints();
}
}
}
class Deleter extends AbstractAction
{
public void actionPerformed(ActionEvent evt)
{
deleteWatchpoint(wpTable.getSelectedRow());
}
}
public boolean isWatchpoint(int address)
{
Watchpoint wp = new Watchpoint(address);
return watchpoints.contains(wp);
}
public void setWatchpoint(int address)
{
setWatchpoint(address, false);
}
public void setWatchpoint(int address, boolean isPrimary)
{
Watchpoint wp = new Watchpoint(address);
int idx = watchpoints.indexOf(wp);
if (idx < 0)
watchpoints.add(wp);
else
wp = watchpoints.get(idx);
if (isPrimary)
wp.isPrimary = isPrimary;
edited = true;
JPC.getInstance().refresh();
}
public void removeAllWatchpoints()
{
watchpoints.clear();
edited = true;
JPC.getInstance().refresh();
}
public void removeWatchpoint(int address)
{
Watchpoint wp = new Watchpoint(address);
int idx = watchpoints.indexOf(wp);
if (idx < 0)
return;
deleteWatchpoint(idx);
}
public Watchpoint checkForWatch()
{
return checkForWatch(watchPrimary.getState());
}
public Watchpoint checkForWatch(boolean isPrimary)
{
if (ignoreWP.getState())
return null;
for (Watchpoint wp : watchpoints) {
byte b = addressSpace.getByte(wp.address);
//if ((b != (byte) 0xff) && (b != 0) && (wp.value!=b))
if (wp.value!=b)
{
if (isPrimary && !wp.isPrimary)
continue;
return wp;
}
}
return null;
}
private void deleteWatchpoint(int index)
{
try {
watchpoints.remove(index);
} catch (IndexOutOfBoundsException e) {
}
edited = true;
JPC.getInstance().refresh();
}
public class Watchpoint implements Comparable<Watchpoint>
{
private int address;
private int value;
private boolean isPrimary;
private String name;
private boolean watchForValue;
private byte watchValue;
Watchpoint(int addr)
{
this(addr, false, (byte)0, false);
}
public Watchpoint(String name, int addr, byte watchValue, boolean watchForValue)
{
this(addr, false, watchValue, watchForValue);
this.name = name;
}
Watchpoint(int addr, boolean primary, byte watchValue, boolean watchForValue)
{
address = addr;
isPrimary = primary;
name = "";
value=addressSpace.getByte(addr);
this.watchValue = watchValue;
this.watchForValue = watchForValue;
}
public boolean equals(Object another)
{
if (!(another instanceof Watchpoint))
return false;
return (address == ((Watchpoint) another).address) && (watchValue == ((Watchpoint) another).watchValue);
}
public int compareTo(Watchpoint wp)
{
if (address != wp.address)
return address - wp.address;
if (watchValue == wp.watchValue)
return 0;
if ((0xff & watchValue) < (0xff & wp.watchValue))
return -1;
return 1;
}
public String getName()
{
return name;
}
public int getAddress()
{
return address;
}
public int getValue()
{
return value;
}
public void updateValue()
{
value=addressSpace.getByte(address);
}
public boolean isWatchingForValue()
{
return watchForValue;
}
public byte getWatchTarget()
{
return watchValue;
}
public boolean isPrimary()
{
return isPrimary;
}
}
class WPModel extends BasicTableModel
{
WPModel()
{
super(new String[]{"Address", "Name", "Primary", "Watch target", "Watch for value"}, new int[]{80, 250, 70, 90, 90});
}
public int getRowCount()
{
return watchpoints.size();
}
public boolean isCellEditable(int row, int column)
{
return true;
}
public Class getColumnClass(int col)
{
if ((col == 2) || (col == 4))
return Boolean.class;
return String.class;
}
public void setValueAt(Object obj, int row, int column)
{
Watchpoint wp = watchpoints.get(row);
if (column == 0)
{
try
{
int addr = (int) Long.parseLong(obj.toString().toLowerCase(), 16);
wp.address = addr;
}
catch (Exception e) {}
}
else if (column == 2)
wp.isPrimary = ((Boolean) obj).booleanValue();
else if (column == 1)
wp.name = obj.toString();
else if (column == 3)
wp.watchValue = (byte)Integer.parseInt(obj.toString(), 16);
else if (column == 4)
wp.watchForValue = ((Boolean) obj).booleanValue();
int selected = sortWatchpoints(row);
JPC.getInstance().refresh();
if (selected >= 0)
{
wpTable.setRowSelectionInterval(selected, selected);
Rectangle rect = wpTable.getCellRect(selected, 0, true);
wpTable.scrollRectToVisible(rect);
}
edited = true;
}
public Object getValueAt(int row, int column)
{
Watchpoint wp = watchpoints.get(row);
switch (column)
{
case 0:
return MemoryViewPanel.zeroPadHex(wp.address, 8);
case 1:
return wp.name;
case 2:
return new Boolean(wp.isPrimary);
case 3:
return String.format("%02x", wp.watchValue);
case 4:
return new Boolean(wp.watchForValue);
default:
return "";
}
}
}
private int sortWatchpoints(int selectedRow)
{
Watchpoint selected = null;
if (selectedRow >= 0)
selected = watchpoints.get(selectedRow);
Collections.sort(watchpoints);
if (selected == null)
return 0;
for (int i = 0; i < watchpoints.size(); i++) {
if (watchpoints.get(i) == selected)
return i;
}
return 0;
}
public boolean importWatchpoints(String fileName, boolean ignoreDots)
{
List<Watchpoint> loaded = new ArrayList<Watchpoint>();
File f = new File(fileName);
if (!f.exists())
return false;
try {
FileReader fin = new FileReader(f);
try {
BufferedReader in = new BufferedReader(fin);
while (true) {
String line = in.readLine();
if (line == null)
break;
String[] elements = line.split("\\s", 2);
String name = elements[1];
if (name.startsWith(".") && ignoreDots)
continue;
int addr = Integer.parseInt(elements[0], 16);
byte watchValue = (byte) 0;
boolean watchForValue = false;
if (elements.length > 2)
{
watchValue = (byte) Integer.parseInt(elements[2], 16);
watchForValue = true;
}
loaded.add(new Watchpoint(name, addr, watchValue, watchForValue));
}
} catch (IOException e) {
return false;
} finally {
try {
fin.close();
} catch (IOException e) {
}
}
} catch (FileNotFoundException e) {
return false;
}
watchpoints.clear();
watchpoints.addAll(loaded);
sortWatchpoints(-1);
edited = true;
return true;
}
public void loadWatchpoints()
{
FileInputStream fin = null;
watchpoints.clear();
try
{
File f = new File(watchpointFileName);
if (!f.exists())
return;
fin = new FileInputStream(f);
DataInputStream din = new DataInputStream(fin);
if (din.readLong() != WATCHPOINT_MAGIC)
throw new IOException("Magic number mismatch");
while (true)
{
int addr = din.readInt();
boolean primary = din.readBoolean();
String name = din.readUTF();
byte watchValue = din.readByte();
boolean watchForValue = din.readBoolean();
Watchpoint wp = new Watchpoint(addr, primary, watchValue, watchForValue);
wp.name = name;
watchpoints.add(wp);
}
}
catch (EOFException e)
{
setTitle("Watchpoints: "+watchpointFileName);
}
catch (Exception e)
{
System.out.println("Warning: failed to load watchpoints");
e.printStackTrace();
setTitle("Watchpoints: "+watchpointFileName+" ERROR");
alert("Error loading watchpoints: "+e, JOptionPane.ERROR_MESSAGE);
}
finally
{
try
{
fin.close();
}
catch (Exception e) {}
sortWatchpoints(-1);
edited = false;
}
}
public void saveWatchpoints()
{
FileOutputStream out = null;
try
{
out = new FileOutputStream(watchpointFileName);
DataOutputStream dout = new DataOutputStream(out);
dout.writeLong(WATCHPOINT_MAGIC);
for (Watchpoint wp : watchpoints) {
dout.writeInt(wp.address);
dout.writeBoolean(wp.isPrimary);
dout.writeUTF(wp.name);
dout.writeByte(wp.watchValue);
dout.writeBoolean(wp.watchForValue);
}
setTitle("Watchpoints: "+watchpointFileName);
}
catch (Exception e)
{
System.out.println("Warning: failed to save watchpoints");
e.printStackTrace();
setTitle("Watchpoints: "+watchpointFileName+" ERROR");
alert("Error saving watchpoints: "+e, JOptionPane.ERROR_MESSAGE);
}
finally
{
try
{
out.close();
}
catch (Exception e) {}
edited = false;
}
}
public void pcCreated() {}
public void pcDisposed() {}
public void executionStarted() {}
public void executionStopped() {}
public void refreshDetails()
{
model.fireTableDataChanged();
}
}