/*
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);
}
}