/* 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.util.*; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.util.ArrayList; import java.util.logging.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.event.*; import org.jpc.emulator.execution.codeblock.*; public class DisassemblyOverlayTable extends JTable implements ListSelectionListener { private static final Logger LOGGING = Logger.getLogger(DisassemblyOverlayTable.class.getName()); private Font f; private boolean showX86Lengths; private int targetColumn; private String[] operationNames, operandNames; private BlockWrapper[] allBlocks; public DisassemblyOverlayTable(TableModel model, int codeBlockColumn, boolean showX86Lengths) { super(model); this.showX86Lengths = showX86Lengths; targetColumn = codeBlockColumn; f = new Font("Monospaced", Font.PLAIN, 12); allBlocks = new BlockWrapper[0]; getSelectionModel().addListSelectionListener(this); } public Rectangle getOverlayRect(int row, int maxExtent) { Rectangle r1 = getCellRect(row, targetColumn, true); CodeBlock cb = getCodeBlockForRow(row); if (cb == null) return r1; int rows = Math.min(cb.getX86Length(), maxExtent); Rectangle r2 = getCellRect(row + rows - 1, targetColumn, true); return r2.union(r1); } public void valueChanged(ListSelectionEvent e) { repaint(); } class BlockWrapper { int address, indent; CodeBlock block; BlockWrapper(int address, int indent, CodeBlock block) { this.block = block; this.indent = indent; this.address = address; } } public void recalculateBlockPositions() { List<BlockWrapper> buffer = new ArrayList<BlockWrapper>(); List<BlockWrapper> stack = new ArrayList<BlockWrapper>(); int len = getModel().getRowCount(); for (int i=0; i<len; i++) { CodeBlock cb = getCodeBlockForRow(i); if (cb == null) continue; for (int j=stack.size()-1; j>=0; j--) { BlockWrapper bw = stack.get(j); if (i >= bw.block.getX86Length() + bw.address) stack.remove(j); } int indent = 0; if (!stack.isEmpty()) indent = stack.get(stack.size() - 1).indent+1; BlockWrapper w = new BlockWrapper(i, indent, cb); buffer.add(w); stack.add(w); } allBlocks = new BlockWrapper[buffer.size()]; buffer.toArray(allBlocks); } protected CodeBlock getCodeBlockForRow(int row) { try { return (CodeBlock) getModel().getValueAt(convertRowIndexToModel(row), targetColumn); } catch (Exception e) { return null; } } class OverlayLineFormat { Color c; String text; int tabPosition; OverlayLineFormat(String txt, int pos, Color c) { text = txt; tabPosition = pos; this.c = c; } } class DisamOverlayLineFormat extends OverlayLineFormat { DisamOverlayLineFormat(String txt, int pos) { super(txt, pos, Color.black); } } private OverlayLineFormat[] basicLineFormat(CodeBlock block) { List<OverlayLineFormat> lines = new ArrayList<OverlayLineFormat>(); String details = block.getDisplayString(); StringTokenizer tokens = new StringTokenizer(details, "\n"); String first = tokens.nextToken(); OverlayLineFormat lfirst = new OverlayLineFormat(first, 10, Color.blue); lines.add(lfirst); while (tokens.hasMoreTokens()) { String l = tokens.nextToken(); DisamOverlayLineFormat lf = new DisamOverlayLineFormat(l, 10); lines.add(lf); } OverlayLineFormat[] result = new OverlayLineFormat[lines.size()]; lines.toArray(result); return result; } private OverlayLineFormat[] exceptionLineFormat(Class cls, Exception e) { StackTraceElement[] trace = e.getStackTrace(); OverlayLineFormat[] result = new OverlayLineFormat[trace.length+2]; result[0] = new OverlayLineFormat("Exception formatting CodeBlock", 10, Color.red); result[1] = new OverlayLineFormat("Source: "+cls, 20, Color.red); Color rr = new Color(255, 50, 50); for (int i=0; i<trace.length; i++) result[i+2] = new OverlayLineFormat(trace[i].toString(), 30, rr); return result; } private void paintCodeBlockOverlay(Graphics g, int row) { CodeBlock cb = getCodeBlockForRow(row); if (cb == null) return; Rectangle r1 = getCellRect(row, targetColumn, true); int rowHeight = getRowHeight(); int h1 = r1.height; if (showX86Lengths) h1 = cb.getX86Length() * rowHeight; Dimension s = getSize(); OverlayLineFormat[] lines = null; try { lines = basicLineFormat(cb); } catch (Exception e) { lines = exceptionLineFormat(cb.getClass(), e); } int popupHeight = lines.length * rowHeight; int w1 = 330; int w2 = 80; int x1 = r1.x; int y1 = r1.y; int x2 = r1.x + 20; int y2 = r1.y; int x3 = x1 + w2; int y3 = Math.min(y1 + rowHeight, s.height - popupHeight - 1); int x4 = x3; int y4 = Math.min(y1 + rowHeight + popupHeight, s.height-1); int x5 = x1 + 20; int y5 = r1.y + h1 - 1; int x6 = x1; int y6 = r1.y + h1 - 1; Polygon p = new Polygon(new int[]{x1,x2,x3,x4,x5,x6}, new int[]{y1,y2,y3,y4,y5,y6}, 6); g.setColor(new Color(150, 150, 255, 60)); g.fillPolygon(p); g.setColor(Color.blue); g.drawPolygon(p); g.setColor(new Color(200, 200, 200)); g.fillRect(x3, y3, w1, y4 - y3); g.setColor(Color.blue); g.drawRect(x3, y3, w1, y4 - y3); g.setFont(f); g.setColor(Color.black); int ypos = 14; for (int i=0; i<lines.length; i++) { OverlayLineFormat l = lines[i]; g.setColor(l.c); g.drawString(l.text, x3 + l.tabPosition, y3 + ypos); ypos += rowHeight; } } private void drawBlocks(Graphics g) { int rowHeight = getRowHeight(); Rectangle tgtRect = getCellRect(0, targetColumn, true); Rectangle r1 = new Rectangle(); Rectangle clip = g.getClipBounds(); int width = 10; int gap = 3; g.setColor(new Color(0, 255, 100, 120)); for (int i=0; i<allBlocks.length; i++) { BlockWrapper bw = allBlocks[i]; int x1 = 5+tgtRect.x + bw.indent * (width + gap); int y1 = bw.address * rowHeight + rowHeight/2; int w1 = width; int h1 = (bw.block.getX86Length() - 1) * rowHeight; r1.setRect(x1, y1, w1, h1); if (!clip.intersects(r1)) continue; g.fillRect(x1, y1, w1, h1); } } public int getHeadRowForBlockRect(Point pt) { int rowHeight = getRowHeight(); Rectangle tgtRect = getCellRect(0, targetColumn, true); Rectangle r1 = new Rectangle(); int width = 10; int gap = 3; for (int i=0; i<allBlocks.length; i++) { BlockWrapper bw = allBlocks[i]; int x1 = 5+tgtRect.x + bw.indent * (width + gap); int y1 = bw.address * rowHeight + rowHeight/2; int w1 = width; int h1 = (bw.block.getX86Length() - 1) * rowHeight; r1.setRect(x1, y1, w1, h1); if (r1.contains(pt)) return bw.address; } return -1; } protected void paintComponent(Graphics g) { super.paintComponent(g); drawBlocks(g); int selectedRow = getSelectedRow(); if (selectedRow >= 0) paintCodeBlockOverlay(g, selectedRow); } }