/* 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 javax.swing.*; import org.jpc.debugger.util.*; import org.jpc.emulator.processor.Processor; public class BreakpointsFrame extends UtilityFrame implements PCListener { public static final String BREAKPOINT_FILE = "breakpoints.jpc"; public static final long BREAKPOINT_MAGIC = 0x81057FAB7272F10l; private boolean edited; private List<Breakpoint> breakpoints; private BPModel model; private JTable bpTable; private String breakpointFileName; private JCheckBoxMenuItem ignoreBP, breakAtPrimary; public BreakpointsFrame() { super("Breakpoints"); breakpointFileName = BREAKPOINT_FILE; breakpoints = new Vector(); model = new BPModel(); edited = false; bpTable = new JTable(model); model.setupColumnWidths(bpTable); String delBP = "Del BP"; InputMap in = new InputMap(); in.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), delBP); ActionMap ac = new ActionMap(); ac.setParent(bpTable.getActionMap()); ac.put(delBP, new Deleter()); bpTable.setInputMap(JComponent.WHEN_FOCUSED, in); bpTable.setActionMap(ac); add("Center", new JScrollPane(bpTable)); JMenu options = new JMenu("Options"); options.add("Set Breakpoint").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { try { String input = JOptionPane.showInputDialog(BreakpointsFrame.this, "Enter the address (in Hex) for the breakpoint: ", "Breakpoint", JOptionPane.QUESTION_MESSAGE); int address = (int) Long.parseLong(input.toLowerCase(), 16); setAddressBreakpoint(address); } catch (Exception e) { } } }); options.addSeparator(); options.add("Remove All Breakpoints").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { removeAllBreakpoints(); } }); options.addSeparator(); ignoreBP = new JCheckBoxMenuItem("Ignore Breakpoints"); options.add(ignoreBP); breakAtPrimary = new JCheckBoxMenuItem("Break at 'Primary' breakpoints only"); options.add(breakAtPrimary); JMenuBar bar = new JMenuBar(); bar.add(new BPFileMenu()); bar.add(options); setJMenuBar(bar); setPreferredSize(new Dimension(450, 300)); JPC.getInstance().objects().addObject(this); loadBreakpoints(); } public boolean breakAtPrimaryOnly() { return breakAtPrimary.getState(); } public boolean ignoreBreakpoints() { return ignoreBP.getState(); } public boolean isEdited() { return edited; } public void frameClosed() { if (edited) { if (JOptionPane.showConfirmDialog(this, "Do you want to save the changes to the breakpoints?", "Save Breakpoints", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) saveBreakpoints(); edited = false; } JPC.getInstance().objects().removeObject(this); } class BPFileMenu extends JMenu implements ActionListener { private JMenuItem load, save, saveAs, importBP; BPFileMenu() { super("File"); load = add("Load Breakpoints"); load.addActionListener(this); save = add("Save Breakpoints"); save.addActionListener(this); saveAs = add("Save Breakpoints As"); saveAs.addActionListener(this); addSeparator(); importBP = add("Import Breakpoints"); importBP.addActionListener(this); } private String deriveBPFileName(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; breakpointFileName = chooser.getSelectedFile().getAbsolutePath(); removeAllBreakpoints(); loadBreakpoints(); } else if (evt.getSource() == save) { saveBreakpoints(); } else if (evt.getSource() == importBP) { if (chooser.showOpenDialog(JPC.getInstance()) != JFileChooser.APPROVE_OPTION) return; removeAllBreakpoints(); String fileName = chooser.getSelectedFile().getAbsolutePath(); importBreakpoints(fileName, false); } else if (evt.getSource() == saveAs) { if (chooser.showSaveDialog(JPC.getInstance()) != JFileChooser.APPROVE_OPTION) return; breakpointFileName = chooser.getSelectedFile().getAbsolutePath(); saveBreakpoints(); } } } class Deleter extends AbstractAction { public void actionPerformed(ActionEvent evt) { deleteBreakpoint(bpTable.getSelectedRow()); } } public boolean isBreakpoint(int address) { AddressBreakpoint bp = new AddressBreakpoint(address); return breakpoints.contains(bp); } public void setAddressBreakpoint(int address) { setAddressBreakpoint(address, false); } public void setAddressBreakpoint(int address, boolean isPrimary) { Breakpoint bp = new AddressBreakpoint(address); int idx = breakpoints.indexOf(bp); if (idx < 0) breakpoints.add(bp); else bp = breakpoints.get(idx); if (isPrimary) bp.setPrimary(isPrimary); edited = true; JPC.getInstance().refresh(); } public void removeAllBreakpoints() { breakpoints.clear(); edited = true; JPC.getInstance().refresh(); } public void removeBreakpoint(int address) { AddressBreakpoint bp = new AddressBreakpoint(address); int idx = breakpoints.indexOf(bp); if (idx < 0) return; deleteBreakpoint(idx); } public Breakpoint checkForBreak(int start, int end) { return checkForBreak(start, end, breakAtPrimary.getState()); } public Breakpoint checkForBreak(int start, int end, boolean isPrimary) { if (ignoreBP.getState()) return null; for (Breakpoint bp : breakpoints) { if ((bp.getAddress() == start) || ((bp.getAddress() >= start) && (bp.getAddress() < end))) { if (isPrimary && !bp.isPrimary()) continue; return bp; } } return null; } private void deleteBreakpoint(int index) { try { breakpoints.remove(index); } catch (IndexOutOfBoundsException e) { } edited = true; JPC.getInstance().refresh(); } // public class BreakCondition extends Breakpoint // { // BreakCondition() // { // // } // } public class AddressBreakpoint extends Breakpoint { AddressBreakpoint(int addr) { this(addr, false); } public AddressBreakpoint(String name, int addr) { super(name, addr, false); } AddressBreakpoint(int addr, boolean primary) { super("", addr, primary); } public boolean satisfied(Processor cpu) { return false; } } class BPModel extends BasicTableModel { BPModel() { super(new String[]{"Address", "Name", "Primary"}, new int[]{100, 250, 100}); } public int getRowCount() { return breakpoints.size(); } public boolean isCellEditable(int row, int column) { return true; } public Class getColumnClass(int col) { if (col == 2) return Boolean.class; return String.class; } public void setValueAt(Object obj, int row, int column) { Breakpoint bp = breakpoints.get(row); if (column == 0) { try { int addr = (int) Long.parseLong(obj.toString().toLowerCase(), 16); bp.setAddress(addr); } catch (Exception e) {} } else if (column == 2) bp.setPrimary(((Boolean) obj).booleanValue()); else if (column == 1) bp.setName(obj.toString()); int selected = sortBreakpoints(row); JPC.getInstance().refresh(); if (selected >= 0) { bpTable.setRowSelectionInterval(selected, selected); Rectangle rect = bpTable.getCellRect(selected, 0, true); bpTable.scrollRectToVisible(rect); } edited = true; } public Object getValueAt(int row, int column) { Breakpoint bp = breakpoints.get(row); switch (column) { case 0: return MemoryViewPanel.zeroPadHex(bp.getAddress(), 8); case 1: return bp.getName(); case 2: return new Boolean(bp.isPrimary()); default: return ""; } } } private int sortBreakpoints(int selectedRow) { Breakpoint selected = null; if (selectedRow >= 0) selected = breakpoints.get(selectedRow); Collections.sort(breakpoints); if (selected == null) return 0; for (int i = 0; i < breakpoints.size(); i++) { if (breakpoints.get(i) == selected) return i; } return 0; } public boolean importBreakpoints(String fileName, boolean ignoreDots) { List<Breakpoint> loaded = new ArrayList<Breakpoint>(); 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); loaded.add(new AddressBreakpoint(name, addr)); } } catch (IOException e) { return false; } finally { try { fin.close(); } catch (IOException e) { } } } catch (FileNotFoundException e) { return false; } breakpoints.clear(); breakpoints.addAll(loaded); sortBreakpoints(-1); edited = true; return true; } public void loadBreakpoints() { FileInputStream fin = null; breakpoints.clear(); try { File f = new File(breakpointFileName); if (!f.exists()) return; fin = new FileInputStream(f); DataInputStream din = new DataInputStream(fin); if (din.readLong() != BREAKPOINT_MAGIC) throw new IOException("Magic number mismatch"); while (true) { int addr = din.readInt(); boolean primary = din.readBoolean(); String name = din.readUTF(); Breakpoint bp = new AddressBreakpoint(addr, primary); bp.setName(name); breakpoints.add(bp); } } catch (EOFException e) { setTitle("Breakpoints: "+breakpointFileName); } catch (Exception e) { System.out.println("Warning: failed to load breakpoints"); e.printStackTrace(); setTitle("Breakpoints: "+breakpointFileName+" ERROR"); alert("Error loading breakpoints: "+e, JOptionPane.ERROR_MESSAGE); } finally { try { fin.close(); } catch (Exception e) {} sortBreakpoints(-1); edited = false; } } public void saveBreakpoints() { FileOutputStream out = null; try { out = new FileOutputStream(breakpointFileName); DataOutputStream dout = new DataOutputStream(out); dout.writeLong(BREAKPOINT_MAGIC); for (Breakpoint bp : breakpoints) { dout.writeInt(bp.getAddress()); dout.writeBoolean(bp.isPrimary()); dout.writeUTF(bp.getName()); } setTitle("Breakpoints: "+breakpointFileName); } catch (Exception e) { System.out.println("Warning: failed to save breakpoints"); e.printStackTrace(); setTitle("Breakpoints: "+breakpointFileName+" ERROR"); alert("Error saving breakpoints: "+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(); } }