Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java')
-rw-r--r--memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java2297
1 files changed, 2297 insertions, 0 deletions
diff --git a/memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java b/memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java
new file mode 100644
index 00000000000..bf6ba9cdecd
--- /dev/null
+++ b/memory/org.eclipse.cdt.debug.ui.memory.floatingpoint/src/org/eclipse/cdt/debug/ui/memory/floatingpoint/Rendering.java
@@ -0,0 +1,2297 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010, 2012 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ted R Williams (Wind River Systems, Inc.) - initial implementation
+ * Randy Rohrbach (Wind River Systems, Inc.) - Copied and modified to create the floating point plugin
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.ui.memory.floatingpoint;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import org.eclipse.cdt.debug.ui.memory.floatingpoint.FPutilities.FPDataType;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IMemoryBlockExtension;
+import org.eclipse.debug.core.model.MemoryByte;
+import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
+import org.eclipse.debug.internal.ui.views.memory.MemoryViewUtil;
+import org.eclipse.debug.internal.ui.views.memory.renderings.GoToAddressComposite;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.progress.UIJob;
+
+
+@SuppressWarnings("restriction")
+public class Rendering extends Composite implements IDebugEventSetListener
+{
+ // The IMemoryRendering parent
+
+ private FPRendering fParent;
+
+ // Controls
+
+ protected FPAddressPane fAddressPane;
+ protected FPDataPane fDataPane;
+ private GoToAddressComposite fAddressBar;
+ protected Control fAddressBarControl;
+ private Selection fSelection = new Selection();
+
+ // Internal management
+
+ BigInteger fViewportAddress = null; // Default visibility for performance
+ BigInteger fMemoryBlockStartAddress = null; // Starting address
+ BigInteger fMemoryBlockEndAddress = null; // Ending address
+ protected BigInteger fBaseAddress = null; // Base address
+ protected int fColumnCount = 0; // Auto calculate can be disabled by user, making this user settable
+ protected int fBytesPerRow = 0; // Number of bytes per row are displayed
+ int fScrollSelection = 0; // Scroll selection
+ private BigInteger fCaretAddress = null; // Caret/cursor position
+ private boolean fCellEditState = false; // Cell editing mode: 'true' = currently editing a cell; 'false' = not editing a cell
+ private BigInteger cellEditAddress = null; // The address of the cell currently being edited
+ private BigInteger memoryAddress = null; // The memory address associated with the cell that currently being edited
+ private StringBuffer fEditBuffer = null; // Character buffer used during editing
+ static boolean initialDisplayModeSet = false; // Initial display mode been set to the same endianness as the target
+
+ // Constants used to identify the panes
+
+ public final static int PANE_ADDRESS = 1;
+ public final static int PANE_DATA = 2;
+
+ // Decimal precision used when converting between scroll units and number of memory
+ // rows. Calculations do not need to be exact; two decimal places is good enough.
+
+ static private final MathContext SCROLL_CONVERSION_PRECISION = new MathContext(2);
+
+ // Constants used to identify text, maybe java should be queried for all available sets
+
+ public final static int TEXT_ISO_8859_1 = 1;
+ public final static int TEXT_USASCII = 2;
+ public final static int TEXT_UTF8 = 3;
+ protected final static int TEXT_UTF16 = 4;
+
+ // Internal constants
+
+ public final static int COLUMNS_AUTO_SIZE_TO_FIT = 0;
+
+ // View internal settings
+
+ private int fCellPadding = 2;
+ private int fPaneSpacing = 16;
+ private String fPaddingString = " "; //$NON-NLS-1$
+
+ // Flag whether the memory cache is dirty
+
+ private boolean fCacheDirty = false;
+
+ // Update modes
+
+ public final static int UPDATE_ALWAYS = 1;
+ public final static int UPDATE_ON_BREAKPOINT = 2;
+ public final static int UPDATE_MANUAL = 3;
+ public int fUpdateMode = UPDATE_ALWAYS;
+
+ // Constants for cell-width calculations
+
+ private static final int DECIMAL_POINT_SIZE = 1;
+ private static final int SIGN_SIZE = 1;
+ private static final int EXPONENT_CHARACTER_SIZE = 1;
+ private static final int EXPONENT_VALUE_SIZE = 3;
+
+ // User settings
+
+ private FPDataType fFPDataType = FPDataType.FLOAT; // Default to float data type
+ private int fDisplayedPrecision = 8; // The default number of digits of displayed precision
+ private int fCharsPerColumn = charsPerColumn(); // Figure out the initial cell-width size
+ private int fColumnsSetting = COLUMNS_AUTO_SIZE_TO_FIT; // Default column setting
+ private boolean fIsTargetLittleEndian = true; // Default target endian setting
+ private boolean fIsDisplayLittleEndian = true; // Default display endian setting
+ private boolean fEditInserMode = false; // Insert mode: true = replace existing number, false = overstrike
+
+ // Constructors
+
+ public Rendering(Composite parent, FPRendering renderingParent)
+ {
+ super(parent, SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.H_SCROLL | SWT.V_SCROLL);
+ this.setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME)); // TODO: internal?
+ this.fParent = renderingParent;
+
+ // Initialize the viewport start
+
+ if (fParent.getMemoryBlock() != null)
+ {
+ fViewportAddress = fParent.getMemoryBlockStartAddress();
+
+ // The viewport address will be null if memory may be retrieved at any
+ // address less than this memory block's base. If so use the base address.
+
+ if (fViewportAddress == null)
+ fViewportAddress = fParent.getBigBaseAddress();
+
+ fBaseAddress = fViewportAddress;
+ }
+
+ // Instantiate the panes, TODO default visibility from state or plugin.xml?
+
+ this.fAddressPane = createAddressPane();
+ this.fDataPane = createDataPane();
+
+ fAddressBar = new GoToAddressComposite();
+ fAddressBarControl = fAddressBar.createControl(parent);
+ Button button = fAddressBar.getButton(IDialogConstants.OK_ID);
+
+ if (button != null)
+ {
+ button.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ doGoToAddress();
+ }
+ });
+
+ button = fAddressBar.getButton(IDialogConstants.CANCEL_ID);
+ if (button != null)
+ {
+ button.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ setVisibleAddressBar(false);
+ }
+ });
+ }
+ }
+
+ fAddressBar.getExpressionWidget().addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ doGoToAddress();
+ }
+ });
+
+ fAddressBar.getExpressionWidget().addKeyListener(new KeyAdapter()
+ {
+ @Override
+ public void keyPressed(KeyEvent e)
+ {
+ if (e.keyCode == SWT.ESC)
+ setVisibleAddressBar(false);
+ super.keyPressed(e);
+ }
+ });
+
+ this.fAddressBarControl.setVisible(false);
+
+ getHorizontalBar().addSelectionListener(createHorizontalBarSelectionListener());
+ getVerticalBar().addSelectionListener(createVerticalBarSelectinListener());
+
+ this.addPaintListener(new PaintListener()
+ {
+ @Override
+ public void paintControl(PaintEvent pe)
+ {
+ pe.gc.setBackground(Rendering.this.getFPRendering().getColorBackground());
+ pe.gc.fillRectangle(0, 0, Rendering.this.getBounds().width, Rendering.this.getBounds().height);
+ }
+ });
+
+ setLayout();
+
+ this.addControlListener(new ControlListener()
+ {
+ @Override
+ public void controlMoved(ControlEvent ce)
+ {
+ }
+
+ @Override
+ public void controlResized(ControlEvent ce)
+ {
+ packColumns();
+ }
+ });
+
+ DebugPlugin.getDefault().addDebugEventListener(this);
+ }
+
+ // Determine how many characters are allowed in the column
+
+ private int charsPerColumn()
+ {
+ return fDisplayedPrecision + DECIMAL_POINT_SIZE + SIGN_SIZE + EXPONENT_CHARACTER_SIZE + EXPONENT_VALUE_SIZE + fCellPadding;
+ }
+
+ // Establish the visible layout of the view
+
+ protected void setLayout()
+ {
+ this.setLayout(new Layout()
+ {
+ @Override
+ public void layout(Composite composite, boolean changed)
+ {
+ int xOffset = 0;
+
+ if (Rendering.this.getHorizontalBar().isVisible())
+ xOffset = Rendering.this.getHorizontalBar().getSelection();
+
+ int x = xOffset * -1;
+ int y = 0;
+
+ if (fAddressBarControl.isVisible())
+ {
+ fAddressBarControl.setBounds(0, 0, Rendering.this.getBounds().width, fAddressBarControl.computeSize(100, 30).y); // FIXME
+ // y = fAddressBarControl.getBounds().height;
+ }
+
+ if (fAddressPane.isPaneVisible())
+ {
+ fAddressPane.setBounds(x, y, fAddressPane.computeSize(0, 0).x, Rendering.this.getBounds().height - y);
+ x = fAddressPane.getBounds().x + fAddressPane.getBounds().width;
+ }
+
+ if (fDataPane.isPaneVisible())
+ {
+ fDataPane.setBounds(x, y, fDataPane.computeSize(0, 0).x, Rendering.this.getBounds().height - y);
+ x = fDataPane.getBounds().x + fDataPane.getBounds().width;
+ }
+
+ ScrollBar horizontal = Rendering.this.getHorizontalBar();
+
+ horizontal.setVisible(true);
+ horizontal.setMinimum(0);
+ horizontal.setMaximum(fDataPane.getBounds().x + fDataPane.getBounds().width + xOffset);
+ @SuppressWarnings("unused")
+ int temp = horizontal.getMaximum();
+ horizontal.setThumb(getClientArea().width);
+ horizontal.setPageIncrement(40); // TODO ?
+ horizontal.setIncrement(20); // TODO ?
+ }
+
+ @Override
+ protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache)
+ {
+ return new Point(100, 100); // dummy data
+ }
+ });
+ }
+
+ // Handles for the caret/cursor movement keys
+
+ protected void handleDownArrow()
+ {
+ fViewportAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow()));
+ ensureViewportAddressDisplayable();
+ redrawPanes();
+ }
+
+ protected void handleUpArrow()
+ {
+ fViewportAddress = fViewportAddress.subtract(BigInteger.valueOf(getAddressableCellsPerRow()));
+ ensureViewportAddressDisplayable();
+ redrawPanes();
+ }
+
+ protected void handlePageDown()
+ {
+ fViewportAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow() * (Rendering.this.getRowCount() - 1)));
+ ensureViewportAddressDisplayable();
+ redrawPanes();
+ }
+
+ protected void handlePageUp()
+ {
+ fViewportAddress = fViewportAddress.subtract(BigInteger.valueOf(getAddressableCellsPerRow() * (Rendering.this.getRowCount() - 1)));
+ ensureViewportAddressDisplayable();
+ redrawPanes();
+ }
+
+ protected SelectionListener createHorizontalBarSelectionListener()
+ {
+ return new SelectionListener()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent se)
+ {
+ Rendering.this.layout();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent se)
+ {
+ // Do nothing
+ }
+ };
+ }
+
+ protected SelectionListener createVerticalBarSelectinListener()
+ {
+ return new SelectionListener()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent se)
+ {
+ switch (se.detail)
+ {
+ case SWT.ARROW_DOWN:
+ handleDownArrow();
+ break;
+
+ case SWT.PAGE_DOWN:
+ handlePageDown();
+ break;
+
+ case SWT.ARROW_UP:
+ handleUpArrow();
+ break;
+
+ case SWT.PAGE_UP:
+ handlePageUp();
+ break;
+
+ case SWT.SCROLL_LINE:
+ // See: BUG 203068 selection event details broken on GTK < 2.6
+
+ default:
+ {
+ if (getVerticalBar().getSelection() == getVerticalBar().getMinimum())
+ {
+ // Set view port start address to the start address of the Memory Block
+ fViewportAddress = Rendering.this.getMemoryBlockStartAddress();
+ }
+ else if (getVerticalBar().getSelection() == getVerticalBar().getMaximum())
+ {
+ // The view port end address should be less or equal to the the end address of the Memory Block
+ // Set view port address to be bigger than the end address of the Memory Block for now
+ // and let ensureViewportAddressDisplayable() to figure out the correct view port start address
+ fViewportAddress = Rendering.this.getMemoryBlockEndAddress();
+ }
+ else
+ {
+ // Figure out the delta, ignore events with no delta
+ int deltaScroll = getVerticalBar().getSelection() - fScrollSelection;
+ if (deltaScroll == 0) break;
+ BigInteger deltaRows = scrollbar2rows(deltaScroll);
+ BigInteger newAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow()).multiply(deltaRows));
+ fViewportAddress = newAddress;
+ }
+
+ ensureViewportAddressDisplayable();
+
+ // Update tooltip; FIXME conversion from slider to scrollbar
+ // getVerticalBar().setToolTipText(Rendering.this.getAddressString(fViewportAddress));
+
+ // Update the addresses in the Address pane.
+
+ if (fAddressPane.isPaneVisible())
+ fAddressPane.redraw();
+
+ redrawPanes();
+
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent se)
+ {
+ // do nothing
+ }
+ };
+ }
+
+ protected FPAddressPane createAddressPane()
+ {
+ return new FPAddressPane(this);
+ }
+
+ protected FPDataPane createDataPane()
+ {
+ return new FPDataPane(this);
+ }
+
+ public FPRendering getFPRendering() // TODO rename
+ {
+ return fParent;
+ }
+
+ protected void setCaretAddress(BigInteger address)
+ {
+ fCaretAddress = address;
+ }
+
+ protected BigInteger getCaretAddress()
+ {
+ // Return the caret address if it has been set. Otherwise return the viewport address.
+ // When the rendering is first created, the caret is unset until the user clicks somewhere
+ // in the rendering. It also reset (unset) when the user gives us a new viewport address
+
+ return (fCaretAddress != null) ? fCaretAddress : fViewportAddress;
+ }
+
+ void doGoToAddress()
+ {
+ try
+ {
+ BigInteger address = fAddressBar.getGoToAddress(this.getMemoryBlockStartAddress(), this.getCaretAddress());
+ getFPRendering().gotoAddress(address);
+ setVisibleAddressBar(false);
+ }
+ catch (NumberFormatException e1)
+ {
+ // FIXME log?
+ }
+ }
+
+ // Ensure that all addresses displayed are within the addressable range
+ protected void ensureViewportAddressDisplayable()
+ {
+ if (fViewportAddress.compareTo(Rendering.this.getMemoryBlockStartAddress()) < 0)
+ {
+ fViewportAddress = Rendering.this.getMemoryBlockStartAddress();
+ }
+ else if (getViewportEndAddress().compareTo(getMemoryBlockEndAddress().add(BigInteger.ONE)) > 0)
+ {
+ fViewportAddress = getMemoryBlockEndAddress().subtract(BigInteger.valueOf(getAddressableCellsPerRow() * getRowCount() - 1));
+ }
+
+ setScrollSelection();
+ }
+
+ public FPIMemorySelection getSelection()
+ {
+ return fSelection;
+ }
+
+ protected int getHistoryDepth()
+ {
+ return fViewportCache.getHistoryDepth();
+ }
+
+ protected void setHistoryDepth(int depth)
+ {
+ fViewportCache.setHistoryDepth(depth);
+ }
+
+ public void logError(String message, Exception e)
+ {
+ Status status = new Status(IStatus.ERROR, fParent.getRenderingId(), DebugException.INTERNAL_ERROR, message, e);
+ FPRenderingPlugin.getDefault().getLog().log(status);
+ }
+
+ public void handleFontPreferenceChange(Font font)
+ {
+ setFont(font);
+
+ Control controls[] = this.getRenderingPanes();
+ for (int index = 0; index < controls.length; index++)
+ controls[index].setFont(font);
+
+ packColumns();
+ layout(true);
+ }
+
+ public void setPaddingString(String padding)
+ {
+ fPaddingString = padding;
+ refresh();
+ }
+
+ public char getPaddingCharacter()
+ {
+ return fPaddingString.charAt(0); // use only the first character
+ }
+
+ static int suspendCount = 0;
+
+ @Override
+ public void handleDebugEvents(DebugEvent[] events)
+ {
+ if (this.isDisposed()) return;
+
+ boolean isChangeOnly = false;
+ boolean isSuspend = false;
+ boolean isBreakpointHit = false;
+
+ for (int index = 0; index < events.length; index++)
+ {
+ if (events[0].getSource() instanceof IDebugElement)
+ {
+ final int kind = events[index].getKind();
+ final int detail = events[index].getDetail();
+ final IDebugElement source = (IDebugElement) events[index].getSource();
+
+ /*
+ * We have to make sure we are comparing memory blocks here. It pretty much is now the
+ * case that the IDebugTarget is always null. Almost no one in the Embedded Space is
+ * using anything but CDT/DSF or CDT/TCF at this point. The older CDI stuff will still
+ * be using the old Debug Model API. But this will generate the same memory block and
+ * a legitimate IDebugTarget which will match properly.
+ */
+ if( source.equals( getMemoryBlock() ) && source.getDebugTarget() == getMemoryBlock().getDebugTarget() )
+ {
+ if ((detail & DebugEvent.BREAKPOINT) != 0) isBreakpointHit = true;
+
+ if (kind == DebugEvent.SUSPEND)
+ {
+ handleSuspendEvent(detail);
+ isSuspend = true;
+ }
+ else if (kind == DebugEvent.CHANGE)
+ {
+ handleChangeEvent();
+ isChangeOnly = true;
+ }
+ }
+ }
+ }
+
+ if (isSuspend)
+ handleSuspend(isBreakpointHit);
+ else if (isChangeOnly)
+ handleChange();
+ }
+
+ protected void handleSuspend(boolean isBreakpointHit)
+ {
+ if (getUpdateMode() == UPDATE_ALWAYS || (getUpdateMode() == UPDATE_ON_BREAKPOINT && isBreakpointHit))
+ {
+ Display.getDefault().asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ archiveDeltas();
+ refresh();
+ }
+ });
+ }
+ }
+
+ protected void handleChange()
+ {
+ if (getUpdateMode() == UPDATE_ALWAYS)
+ {
+ Display.getDefault().asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ refresh();
+ }
+ });
+ }
+ }
+
+ protected void handleSuspendEvent(int detail)
+ {
+ }
+
+ protected void handleChangeEvent()
+ {
+ }
+
+ // Return true to enable development debug print statements
+
+ public boolean isDebug()
+ {
+ return false;
+ }
+
+ protected IMemoryBlockExtension getMemoryBlock()
+ {
+ IMemoryBlock block = fParent.getMemoryBlock();
+ if (block != null)
+ return (IMemoryBlockExtension) block.getAdapter(IMemoryBlockExtension.class);
+
+ return null;
+ }
+
+ public BigInteger getBigBaseAddress()
+ {
+ return fParent.getBigBaseAddress();
+ }
+
+ public int getAddressableSize()
+ {
+ return fParent.getAddressableSize();
+ }
+
+ protected FPIViewportCache getViewportCache()
+ {
+ return fViewportCache;
+ }
+
+ public FPMemoryByte[] getBytes(BigInteger address, int bytes) throws DebugException
+ {
+ return getViewportCache().getBytes(address, bytes);
+ }
+
+ // Default visibility for performance
+
+ ViewportCache fViewportCache = new ViewportCache();
+
+ private interface Request
+ {
+ }
+
+ class ViewportCache extends Thread implements FPIViewportCache
+ {
+ class ArchiveDeltas implements Request
+ {
+ }
+
+ class AddressPair implements Request
+ {
+ BigInteger startAddress;
+ BigInteger endAddress;
+
+ public AddressPair(BigInteger start, BigInteger end)
+ {
+ startAddress = start;
+ endAddress = end;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ return false;
+ if (obj instanceof AddressPair)
+ {
+ return ((AddressPair) obj).startAddress.equals(startAddress) && ((AddressPair) obj).endAddress.equals(endAddress);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + startAddress.hashCode() + endAddress.hashCode();
+ }
+
+ }
+
+ class MemoryUnit implements Cloneable
+ {
+ BigInteger start;
+
+ BigInteger end;
+
+ FPMemoryByte[] bytes;
+
+ @Override
+ public MemoryUnit clone()
+ {
+ MemoryUnit b = new MemoryUnit();
+
+ b.start = this.start;
+ b.end = this.end;
+ b.bytes = new FPMemoryByte[this.bytes.length];
+ for (int index = 0; index < this.bytes.length; index++)
+ b.bytes[index] = new FPMemoryByte(this.bytes[index].getValue());
+
+ return b;
+ }
+
+ public boolean isValid()
+ {
+ return this.start != null && this.end != null && this.bytes != null;
+ }
+ }
+
+ @SuppressWarnings("hiding")
+ private HashMap<BigInteger, FPMemoryByte[]> fEditBuffer = new HashMap<BigInteger, FPMemoryByte[]>();
+ private boolean fDisposed = false;
+ private Object fLastQueued = null;
+ private Vector<Object> fQueue = new Vector<Object>();
+ protected MemoryUnit fCache = null;
+ protected MemoryUnit fHistoryCache[] = new MemoryUnit[0];
+ protected int fHistoryDepth = 0;
+
+ public ViewportCache()
+ {
+ start();
+ }
+
+ @Override
+ public void dispose()
+ {
+ fDisposed = true;
+ synchronized (fQueue)
+ {
+ fQueue.notify();
+ }
+ }
+
+ public int getHistoryDepth()
+ {
+ return fHistoryDepth;
+ }
+
+ public void setHistoryDepth(int depth)
+ {
+ fHistoryDepth = depth;
+ fHistoryCache = new MemoryUnit[fHistoryDepth];
+ }
+
+ @Override
+ public void refresh()
+ {
+ assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$
+
+ if (fCache != null)
+ {
+ queueRequest(fViewportAddress, getViewportEndAddress());
+ }
+ }
+
+ @Override
+ public void archiveDeltas()
+ {
+ assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$
+
+ if (fCache != null)
+ {
+ queueRequestArchiveDeltas();
+ }
+ }
+
+ private void queueRequest(BigInteger startAddress, BigInteger endAddress)
+ {
+ AddressPair pair = new AddressPair(startAddress, endAddress);
+ queue(pair);
+ }
+
+ private void queueRequestArchiveDeltas()
+ {
+ ArchiveDeltas archive = new ArchiveDeltas();
+ queue(archive);
+ }
+
+ private void queue(Object element)
+ {
+ synchronized (fQueue)
+ {
+ if (!(fQueue.size() > 0 && element.equals(fLastQueued)))
+ {
+ fQueue.addElement(element);
+ fLastQueued = element;
+ }
+ fQueue.notify();
+ }
+ }
+
+ @Override
+ public void run()
+ {
+ while (!fDisposed)
+ {
+ AddressPair pair = null;
+ boolean archiveDeltas = false;
+ synchronized (fQueue)
+ {
+ if (fQueue.size() > 0)
+ {
+ Request request = (Request) fQueue.elementAt(0);
+ Class<?> type = request.getClass();
+
+ while (fQueue.size() > 0 && type.isInstance(fQueue.elementAt(0)))
+ {
+ request = (Request) fQueue.elementAt(0);
+ fQueue.removeElementAt(0);
+ }
+
+ if (request instanceof ArchiveDeltas)
+ archiveDeltas = true;
+ else if (request instanceof AddressPair)
+ pair = (AddressPair) request;
+ }
+ }
+
+ if (archiveDeltas)
+ {
+ for (int i = fViewportCache.getHistoryDepth() - 1; i > 0; i--)
+ fHistoryCache[i] = fHistoryCache[i - 1];
+
+ fHistoryCache[0] = fCache.clone();
+ }
+ else if (pair != null)
+ {
+ populateCache(pair.startAddress, pair.endAddress);
+ }
+ else
+ {
+ synchronized (fQueue)
+ {
+ try
+ {
+ if (fQueue.isEmpty())
+ {
+ fQueue.wait();
+ }
+ } catch (Exception e)
+ {
+ // do nothing
+ }
+ }
+ }
+ }
+ }
+
+ // Cache memory necessary to paint viewport
+ // TODO: user setting to buffer +/- x lines
+ // TODO: reuse existing cache? probably only a minor performance gain
+
+ private void populateCache(final BigInteger startAddress, final BigInteger endAddress)
+ {
+ try
+ {
+ IMemoryBlockExtension memoryBlock = getMemoryBlock();
+
+ BigInteger lengthInBytes = endAddress.subtract(startAddress);
+ BigInteger addressableSize = BigInteger.valueOf(getAddressableSize());
+
+ long units = lengthInBytes.divide(addressableSize)
+ .add(lengthInBytes.mod(addressableSize).compareTo(BigInteger.ZERO) > 0 ? BigInteger.ONE : BigInteger.ZERO).longValue();
+
+ // CDT (and maybe other backends) will call setValue() on these MemoryBlock objects. We
+ // don't want this to happen, because it interferes with this rendering's own change history.
+ // Ideally, we should strictly use the back end change notification and history, but it is
+ // only guaranteed to work for bytes within the address range of the MemoryBlock.
+
+ MemoryByte readBytes[] = memoryBlock.getBytesFromAddress(startAddress, units);
+ FPMemoryByte cachedBytes[] = new FPMemoryByte[readBytes.length];
+
+ for (int index = 0; index < readBytes.length; index++)
+ cachedBytes[index] = new FPMemoryByte(readBytes[index].getValue(), readBytes[index].getFlags());
+
+ // Derive the target endian from the read MemoryBytes.
+
+ if (cachedBytes.length > 0)
+ if (cachedBytes[0].isEndianessKnown())
+ setTargetLittleEndian(!cachedBytes[0].isBigEndian());
+
+ // The first time we execute this method, set the display endianness to the target endianness.
+
+ if (!initialDisplayModeSet)
+ {
+ setDisplayLittleEndian(isTargetLittleEndian());
+ initialDisplayModeSet = true;
+ }
+
+ // Re-order bytes within unit to be a sequential byte stream if the endian is already little
+
+ if (isTargetLittleEndian())
+ {
+ // There isn't an order when the unit size is one, so skip for performance
+
+ if (addressableSize.compareTo(BigInteger.ONE) != 0)
+ {
+ int unitSize = addressableSize.intValue();
+ FPMemoryByte cachedBytesAsByteSequence[] = new FPMemoryByte[cachedBytes.length];
+ for (int unit = 0; unit < units; unit++)
+ {
+ for (int unitbyte = 0; unitbyte < unitSize; unitbyte++)
+ {
+ cachedBytesAsByteSequence[unit * unitSize + unitbyte] = cachedBytes[unit * unitSize + unitSize - unitbyte];
+ }
+ }
+ cachedBytes = cachedBytesAsByteSequence;
+ }
+ }
+
+ final FPMemoryByte[] cachedBytesFinal = cachedBytes;
+
+ fCache = new MemoryUnit();
+ fCache.start = startAddress;
+ fCache.end = endAddress;
+ fCache.bytes = cachedBytesFinal;
+
+ Display.getDefault().asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ // Generate deltas
+
+ for (int historyIndex = 0; historyIndex < getHistoryDepth(); historyIndex++)
+ {
+ if (fHistoryCache[historyIndex] != null && fHistoryCache[historyIndex].isValid())
+ {
+ BigInteger maxStart = startAddress.max(fHistoryCache[historyIndex].start);
+ BigInteger minEnd = endAddress.min(fHistoryCache[historyIndex].end).subtract(BigInteger.ONE);
+
+ BigInteger overlapLength = minEnd.subtract(maxStart);
+ if (overlapLength.compareTo(BigInteger.valueOf(0)) > 0)
+ {
+ // there is overlap
+
+ int offsetIntoOld = maxStart.subtract(fHistoryCache[historyIndex].start).intValue();
+ int offsetIntoNew = maxStart.subtract(startAddress).intValue();
+
+ for (int i = overlapLength.intValue(); i >= 0; i--)
+ {
+ cachedBytesFinal[offsetIntoNew + i].setChanged(historyIndex,
+ cachedBytesFinal[offsetIntoNew + i].getValue() != fHistoryCache[historyIndex].bytes[offsetIntoOld + i]
+ .getValue());
+ }
+
+ // There are several scenarios where the history cache must be updated from the data cache, so that when a
+ // cell is edited the font color changes appropriately. The following code deals with the different cases.
+
+ if (historyIndex != 0) continue;
+
+ int dataStart = fCache.start.intValue();
+ int dataEnd = fCache.end.intValue();
+ int dataLength = fCache.bytes.length;
+
+ int historyStart = fHistoryCache[0].start.intValue();
+ int historyEnd = fHistoryCache[0].end.intValue();
+ int historyLength = fHistoryCache[0].bytes.length;
+
+ // Case 1: The data cache is smaller than the history cache; the data cache's
+ // address range is fully covered by the history cache. Do nothing.
+
+ if ((dataStart >= historyStart) && (dataEnd <= historyEnd))
+ continue;
+
+ // Case 2: The data and history cache's do not overlap at all
+
+ if (((dataStart < historyStart) && (dataEnd < historyStart)) || (dataStart > historyEnd))
+ {
+ // Create a new history cache: Copy the data cache bytes to the history cache
+
+ MemoryUnit newHistoryCache = new MemoryUnit();
+
+ newHistoryCache.start = fCache.start;
+ newHistoryCache.end = fCache.end;
+ int newHistoryCacheSize = fCache.bytes.length;
+ newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize];
+
+ for (int index = 0; index < newHistoryCacheSize; index++)
+ newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue());
+
+ fHistoryCache[0] = newHistoryCache;
+
+ continue;
+ }
+
+ // Case 3: The data cache starts at a lower address than the history cache, but overlaps the history cache
+
+ if ((dataStart < historyStart) && ((dataEnd >= historyStart) && (dataEnd <= historyEnd)))
+ {
+ // Create a new history cache with the missing data from the main cache and append the old history to it.
+
+ int missingDataByteCount = historyStart - dataStart;
+ int historyCacheSize = historyLength;
+ int newHistoryCacheSize = missingDataByteCount + historyLength;
+
+ if (missingDataByteCount <= 0 && historyCacheSize <= 0) break;
+
+ MemoryUnit newHistoryCache = new MemoryUnit();
+
+ newHistoryCache.start = fCache.start;
+ newHistoryCache.end = fHistoryCache[0].end;
+ newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize];
+
+ // Copy the missing bytes from the beginning of the main cache to the history cache.
+
+ for (int index = 0; index < missingDataByteCount; index++)
+ newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue());
+
+ // Copy the remaining bytes from the old history cache to the new history cache
+
+ for (int index = 0; index < historyCacheSize; index++)
+ newHistoryCache.bytes[index + missingDataByteCount] =
+ new FPMemoryByte(fHistoryCache[0].bytes[index].getValue());
+
+ fHistoryCache[0] = newHistoryCache;
+
+ continue;
+ }
+
+ // Case 4: The data cache starts at a higher address than the history cache
+
+ if (((dataStart >= historyStart) && (dataStart <= historyEnd)) && (dataEnd > historyEnd))
+ {
+ // Append the missing main cache bytes to the history cache.
+
+ int missingDataByteCount = dataEnd - historyEnd;
+ int historyCacheSize = historyEnd - historyStart;
+ int newHistoryCacheSize = missingDataByteCount + historyLength;
+
+ if (missingDataByteCount > 0 && historyCacheSize > 0)
+ {
+ MemoryUnit newHistoryCache = new MemoryUnit();
+
+ newHistoryCache.start = fHistoryCache[0].start;
+ newHistoryCache.end = fCache.end;
+ newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize];
+
+ // Copy the old history bytes to the new history cache
+
+ System.arraycopy(fHistoryCache[0].bytes, 0, newHistoryCache.bytes, 0, historyLength);
+
+ // Copy the bytes from the main cache that are not in the history cache to the end of the new history cache.
+
+ for (int index = 0; index < missingDataByteCount; index++)
+ {
+ int srcIndex = dataLength - missingDataByteCount + index;
+ int dstIndex = historyLength + index;
+ newHistoryCache.bytes[dstIndex] = new FPMemoryByte(fCache.bytes[srcIndex].getValue());
+ }
+
+ fHistoryCache[0] = newHistoryCache;
+
+ continue;
+ }
+ }
+
+ // Case 5 - The data cache is greater than the history cache and fully covers it
+
+ if (dataStart < historyStart && dataEnd > historyEnd)
+ {
+ int start = 0;
+ int end = 0;
+
+ // Create a new history cache to reflect the entire data cache
+
+ MemoryUnit newHistoryCache = new MemoryUnit();
+
+ newHistoryCache.start = fCache.start;
+ newHistoryCache.end = fCache.end;
+ int newHistoryCacheSize = fCache.bytes.length;
+ newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize];
+
+ int topByteCount = historyStart - dataStart;
+ int bottomByteCount = dataEnd - historyEnd;
+
+ // Copy the bytes from the beginning of the data cache to the new history cache
+
+ for (int index = 0; index < topByteCount; index++)
+ newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue());
+
+ // Copy the old history cache bytes to the new history cache
+
+ start = topByteCount;
+ end = topByteCount + historyLength;
+
+ for (int index = start; index < end; index++)
+ newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue());
+
+ // Copy the bytes from the end of the data cache to the new history cache
+
+ start = topByteCount + historyLength;
+ end = topByteCount + historyLength + bottomByteCount;
+
+ for (int index = start; index < end; index++)
+ newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue());
+
+ fHistoryCache[0] = newHistoryCache;
+
+ continue;
+ }
+ }
+ }
+ }
+
+ // If the history does not exist, populate the history with the just populated
+ // cache. This solves the use case of (1) connect to target; (2) edit memory
+ // before the first suspend debug event; (3) paint differences in changed color.
+
+ if (fHistoryCache[0] == null)
+ fHistoryCache[0] = fCache.clone();
+
+ Rendering.this.redrawPanes();
+ }
+ });
+
+ }
+ catch (Exception e)
+ {
+ // User can scroll to any memory, whether it's valid on the target or not. It doesn't make
+ // much sense to fill up the Eclipse error log with such "failures." So, comment out for now.
+ // logError(FPRenderingMessages.getString("FAILURE_READ_MEMORY"), e); //$NON-NLS-1$
+ }
+ }
+
+ // Bytes will be fetched from cache
+
+ @Override
+ public FPMemoryByte[] getBytes(BigInteger address, int bytesRequested) throws DebugException
+ {
+ assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$
+
+ if (containsEditedCell(address)) // Cell size cannot be switched during an edit
+ return getEditedMemory(address);
+
+ boolean contains = false;
+ if (fCache != null && fCache.start != null)
+ {
+ // See if all of the data requested is in the cache
+
+ BigInteger dataEnd = address.add(BigInteger.valueOf(bytesRequested));
+
+ if (fCache.start.compareTo(address) <= 0 && fCache.end.compareTo(dataEnd) >= 0 && fCache.bytes.length > 0)
+ contains = true;
+ }
+
+ if (contains)
+ {
+ int offset = address.subtract(fCache.start).intValue();
+ FPMemoryByte bytes[] = new FPMemoryByte[bytesRequested];
+
+ for (int index = 0; index < bytes.length; index++)
+ bytes[index] = fCache.bytes[offset + index];
+
+ return bytes;
+ }
+
+ FPMemoryByte bytes[] = new FPMemoryByte[bytesRequested];
+
+ for (int index = 0; index < bytes.length; index++)
+ {
+ bytes[index] = new FPMemoryByte();
+ bytes[index].setReadable(false);
+ }
+
+ fViewportCache.queueRequest(fViewportAddress, getViewportEndAddress());
+
+ return bytes;
+ }
+
+ @Override
+ public boolean containsEditedCell(BigInteger address)
+ {
+ assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$
+ return fEditBuffer.containsKey(address);
+ }
+
+ public FPMemoryByte[] getEditedMemory(BigInteger address)
+ {
+ assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$
+ return fEditBuffer.get(address);
+ }
+
+ @Override
+ public void clearEditBuffer()
+ {
+ assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$
+ fEditBuffer.clear();
+ Rendering.this.redrawPanes();
+ }
+
+ @Override
+ public void writeEditBuffer()
+ {
+ assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$
+
+ Set<BigInteger> keySet = fEditBuffer.keySet();
+ Iterator<BigInteger> iterator = keySet.iterator();
+
+ while (iterator.hasNext())
+ {
+ BigInteger address = iterator.next();
+ FPMemoryByte[] bytes = fEditBuffer.get(address);
+
+ byte byteValue[] = new byte[bytes.length];
+
+ for (int index = 0; index < bytes.length; index++)
+ byteValue[index] = bytes[index].getValue();
+
+ try
+ {
+ IMemoryBlockExtension block = getMemoryBlock();
+ BigInteger offset = address.subtract(block.getBigBaseAddress());
+ block.setValue(offset, byteValue);
+ }
+ catch (Exception e)
+ {
+ MemoryViewUtil.openError(FPRenderingMessages.getString("FAILURE_WRITE_MEMORY"), "", e); //$NON-NLS-1$ //$NON-NLS-2$
+ logError(FPRenderingMessages.getString("FAILURE_WRITE_MEMORY"), e); //$NON-NLS-1$
+ }
+ }
+
+ clearEditBuffer();
+ }
+
+ @Override
+ public void setEditedValue(BigInteger address, FPMemoryByte[] bytes)
+ {
+ assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$
+ fEditBuffer.put(address, bytes);
+ Rendering.this.redrawPanes();
+ }
+ }
+
+ public void setVisibleAddressBar(boolean visible)
+ {
+ fAddressBarControl.setVisible(visible);
+ if (visible)
+ {
+ String selectedStr = "0x" + getCaretAddress().toString(16); //$NON-NLS-1$
+ Text text = fAddressBar.getExpressionWidget();
+ text.setText(selectedStr);
+ text.setSelection(0, text.getCharCount());
+ fAddressBar.getExpressionWidget().setFocus();
+ }
+
+ layout(true);
+ layoutPanes();
+ }
+
+ public void setDirty(boolean needRefresh)
+ {
+ fCacheDirty = needRefresh;
+ }
+
+ public boolean isDirty()
+ {
+ return fCacheDirty;
+ }
+
+ @Override
+ public void dispose()
+ {
+ DebugPlugin.getDefault().removeDebugEventListener(this);
+ if (fViewportCache != null)
+ {
+ fViewportCache.dispose();
+ fViewportCache = null;
+ }
+ super.dispose();
+ }
+
+ class Selection implements FPIMemorySelection
+ {
+ private BigInteger fStartHigh = null;
+ private BigInteger fStartLow = null;
+
+ private BigInteger fEndHigh = null;
+ private BigInteger fEndLow = null;
+
+ @Override
+ public void clear()
+ {
+ fEndHigh = fEndLow = fStartHigh = fStartLow = null;
+ redrawPanes();
+ }
+
+ @Override
+ public boolean hasSelection()
+ {
+ return fStartHigh != null && fStartLow != null && fEndHigh != null && fEndLow != null;
+ }
+
+ @Override
+ public boolean isSelected(BigInteger address)
+ {
+ // Do we have valid start and end addresses?
+
+ if (getEnd() == null || getStart() == null) return false;
+
+ // If end is greater than start
+
+ if (getEnd().compareTo(getStart()) >= 0)
+ {
+ // If address is greater-than-or-equal-to start and less then end, return true
+ if (address.compareTo(getStart()) >= 0 && address.compareTo(getEnd()) < 0) return true;
+ }
+
+ // If start is greater than end
+
+ else if (getStart().compareTo(getEnd()) >= 0)
+ {
+ // If address is greater-than-or-equal-to zero and less than start, return true
+ if (address.compareTo(getEnd()) >= 0 && address.compareTo(getStart()) < 0) return true;
+ }
+
+ return false;
+ }
+
+ // Set selection start
+
+ @Override
+ public void setStart(BigInteger high, BigInteger low)
+ {
+ if (high == null && low == null)
+ {
+ if (fStartHigh != null && fStartLow != null)
+ {
+ fStartHigh = null;
+ fStartLow = null;
+ redrawPanes();
+ }
+
+ return;
+ }
+
+ boolean changed = false;
+
+ if (fStartHigh == null || !high.equals(fStartHigh))
+ {
+ fStartHigh = high;
+ changed = true;
+ }
+
+ if (fStartLow == null || !low.equals(fStartLow))
+ {
+ fStartLow = low;
+ changed = true;
+ }
+
+ if (changed) redrawPanes();
+ }
+
+ // Set selection end
+
+ @Override
+ public void setEnd(BigInteger high, BigInteger low)
+ {
+ if (high == null && low == null)
+ {
+ if (fEndHigh != null && fEndLow != null)
+ {
+ fEndHigh = null;
+ fEndLow = null;
+ redrawPanes();
+ }
+
+ return;
+ }
+
+ boolean changed = false;
+
+ if (fEndHigh == null || !high.equals(fEndHigh))
+ {
+ fEndHigh = high;
+ changed = true;
+ }
+
+ if (fEndLow == null || !low.equals(fEndLow))
+ {
+ fEndLow = low;
+ changed = true;
+ }
+
+ if (changed) redrawPanes();
+ }
+
+ @Override
+ public BigInteger getHigh()
+ {
+ if (!hasSelection()) return null;
+ return getStart().max(getEnd());
+ }
+
+ @Override
+ public BigInteger getLow()
+ {
+ if (!hasSelection()) return null;
+ return getStart().min(getEnd());
+ }
+
+ @Override
+ public BigInteger getStart()
+ {
+ // If there is no start, return null
+ if (fStartHigh == null) return null;
+
+ // If there is no end, return the high address of the start
+ if (fEndHigh == null) return fStartHigh;
+
+ // If Start High/Low equal End High/Low, return a low start and high end
+ if (fStartHigh.equals(fEndHigh) && fStartLow.equals(fEndLow)) return fStartLow;
+
+ BigInteger differenceEndToStartHigh = fEndHigh.subtract(fStartHigh).abs();
+ BigInteger differenceEndToStartLow = fEndHigh.subtract(fStartLow).abs();
+
+ // Return the start high or start low based on which creates a larger selection
+ if (differenceEndToStartHigh.compareTo(differenceEndToStartLow) > 0)
+ return fStartHigh;
+
+ return fStartLow;
+ }
+
+ @Override
+ public BigInteger getStartLow()
+ {
+ return fStartLow;
+ }
+
+ @Override
+ public BigInteger getEnd()
+ {
+ // If there is no end, return null
+ if (fEndHigh == null) return null;
+
+ // *** Temporary for debugging ***
+
+ if (fStartHigh == null || fStartLow == null)
+ {
+ return null;
+ }
+
+ // If Start High/Low equal End High/Low, return a low start and high end
+ if (fStartHigh.equals(fEndHigh) && fStartLow.equals(fEndLow)) return fStartHigh;
+
+ BigInteger differenceStartToEndHigh = fStartHigh.subtract(fEndHigh).abs();
+ BigInteger differenceStartToEndLow = fStartHigh.subtract(fEndLow).abs();
+
+ // Return the start high or start low based on which creates a larger selection
+ if (differenceStartToEndHigh.compareTo(differenceStartToEndLow) >= 0)
+ return fEndHigh;
+
+ return fEndLow;
+ }
+ }
+
+ public void setPaneVisible(int pane, boolean visible)
+ {
+ switch (pane)
+ {
+ case PANE_ADDRESS:
+ fAddressPane.setPaneVisible(visible);
+ break;
+ case PANE_DATA:
+ fDataPane.setPaneVisible(visible);
+ break;
+ }
+
+ fireSettingsChanged();
+ layoutPanes();
+ }
+
+ public boolean getPaneVisible(int pane)
+ {
+ switch (pane)
+ {
+ case PANE_ADDRESS:
+ return fAddressPane.isPaneVisible();
+ case PANE_DATA:
+ return fDataPane.isPaneVisible();
+ default:
+ return false;
+ }
+ }
+
+ protected void packColumns()
+ {
+ int availableWidth = Rendering.this.getSize().x;
+
+ if (fAddressPane.isPaneVisible())
+ {
+ availableWidth -= fAddressPane.computeSize(0, 0).x;
+ availableWidth -= Rendering.this.getRenderSpacing() * 2;
+ }
+
+ int combinedWidth = 0;
+
+ if (fDataPane.isPaneVisible()) combinedWidth += fDataPane.getCellWidth();
+
+ if (getColumnsSetting() == Rendering.COLUMNS_AUTO_SIZE_TO_FIT)
+ {
+ if (combinedWidth == 0)
+ fColumnCount = 0;
+ else
+ {
+ fColumnCount = availableWidth / combinedWidth;
+ if (fColumnCount == 0) fColumnCount = 1; // Paint one column even if only part can show in view
+ }
+ }
+ else
+ fColumnCount = getColumnsSetting();
+
+ try
+ {
+ // Update the number of bytes per row; the max/min scroll range and the current thumbnail position.
+
+ fBytesPerRow = getCharsPerColumn() * getColumnCount();
+ getVerticalBar().setMinimum(1);
+
+ // scrollbar maximum range is Integer.MAX_VALUE.
+
+ getVerticalBar().setMaximum(getMaxScrollRange().min(BigInteger.valueOf(Integer.MAX_VALUE)).intValue());
+ getVerticalBar().setIncrement(1);
+ getVerticalBar().setPageIncrement(this.getRowCount() - 1);
+
+ // FIXME: conversion of slider to scrollbar
+ // fScrollBar.setToolTipText(Rendering.this.getAddressString(fViewportAddress));
+
+ setScrollSelection();
+ }
+ catch (Exception e)
+ {
+ // FIXME precautionary
+ }
+
+ Rendering.this.redraw();
+ Rendering.this.redrawPanes();
+ }
+
+ public FPAbstractPane[] getRenderingPanes()
+ {
+ return new FPAbstractPane[] { fAddressPane, fDataPane };
+ }
+
+ public int getCellPadding()
+ {
+ return fCellPadding;
+ }
+
+ protected int getRenderSpacing()
+ {
+ return fPaneSpacing;
+ }
+
+ public void refresh()
+ {
+ if (!this.isDisposed())
+ {
+ if (this.isVisible() && getViewportCache() != null)
+ {
+ getViewportCache().refresh();
+ }
+ else
+ {
+ setDirty(true);
+ fParent.updateRenderingLabels();
+ }
+ }
+ }
+
+ protected void archiveDeltas()
+ {
+ this.getViewportCache().archiveDeltas();
+ }
+
+ public void gotoAddress(BigInteger address)
+ {
+ // Ensure that the GoTo address is within the addressable range
+
+ if ((address.compareTo(this.getMemoryBlockStartAddress()) < 0) || (address.compareTo(this.getMemoryBlockEndAddress()) > 0))
+ return;
+
+ fViewportAddress = address;
+
+ // Reset the caret and selection state (no caret and no selection)
+
+ fCaretAddress = null;
+ fSelection = new Selection();
+
+ redrawPanes();
+ }
+
+ public void setViewportStartAddress(BigInteger newAddress)
+ {
+ fViewportAddress = newAddress;
+ }
+
+ public BigInteger getViewportStartAddress()
+ {
+ return fViewportAddress;
+ }
+
+ public BigInteger getViewportEndAddress()
+ {
+ return fViewportAddress.add(BigInteger.valueOf(this.getBytesPerRow() * getRowCount() / getAddressableSize()));
+ }
+
+ public String getAddressString(BigInteger address)
+ {
+ StringBuffer addressString = new StringBuffer(address.toString(16).toUpperCase());
+
+ for (int chars = getAddressBytes() * 2 - addressString.length(); chars > 0; chars--)
+ addressString.insert(0, '0');
+
+ addressString.insert(0, "0x"); //$NON-NLS-1$
+
+ return addressString.toString();
+ }
+
+ protected int getAddressBytes()
+ {
+ return fParent.getAddressSize();
+ }
+
+ public Control getAddressBarControl()
+ {
+ return fAddressBarControl;
+ }
+
+ public int getColumnCount()
+ {
+ return fColumnCount;
+ }
+
+ public int getColumnsSetting()
+ {
+ return fColumnsSetting;
+ }
+
+ protected void setBytesPerRow(int count)
+ {
+ fBytesPerRow = count;
+ }
+
+ protected void setColumnCount(int count)
+ {
+ fColumnCount = count;
+ }
+
+ public void setColumnsSetting(int columns)
+ {
+ if (fColumnsSetting != columns)
+ {
+ fColumnsSetting = columns;
+ fireSettingsChanged();
+ layoutPanes();
+ }
+ }
+
+ protected void ensureVisible(BigInteger address)
+ {
+ BigInteger viewportStart = this.getViewportStartAddress();
+ BigInteger viewportEnd = this.getViewportEndAddress();
+
+ boolean isAddressBeforeViewportStart = address.compareTo(viewportStart) < 0;
+ boolean isAddressAfterViewportEnd = address.compareTo(viewportEnd) > 0;
+
+ if (isAddressBeforeViewportStart || isAddressAfterViewportEnd) gotoAddress(address);
+ }
+
+ protected int getRowCount()
+ {
+ int rowCount = 0;
+ Control panes[] = getRenderingPanes();
+ for (int i = 0; i < panes.length; i++)
+ if (panes[i] instanceof FPAbstractPane)
+ rowCount = Math.max(rowCount, ((FPAbstractPane) panes[i]).getRowCount());
+
+ return rowCount;
+ }
+
+ protected int getBytesPerRow()
+ {
+ return fBytesPerRow;
+ }
+
+ protected int getAddressableCellsPerRow()
+ {
+ return getBytesPerRow() / getAddressableSize();
+ }
+
+ public int getAddressesPerColumn()
+ {
+ return this.getCharsPerColumn() / getAddressableSize();
+ }
+
+ /**
+ * @return Set current scroll selection
+ */
+ protected void setScrollSelection()
+ {
+ BigInteger selection = getViewportStartAddress().divide(BigInteger.valueOf(getAddressableCellsPerRow()));
+
+ fScrollSelection = rows2scrollbar(selection);
+ getVerticalBar().setSelection(fScrollSelection);
+ }
+
+ /**
+ * compute the maximum scrolling range.
+ *
+ * @return number of lines that rendering can display
+ */
+ private BigInteger getMaxScrollRange()
+ {
+ BigInteger difference = getMemoryBlockEndAddress().subtract(getMemoryBlockStartAddress()).add(BigInteger.ONE);
+ BigInteger maxScrollRange = difference.divide(BigInteger.valueOf(getAddressableCellsPerRow()));
+ if (maxScrollRange.multiply(BigInteger.valueOf(getAddressableCellsPerRow())).compareTo(difference) != 0)
+ maxScrollRange = maxScrollRange.add(BigInteger.ONE);
+
+ // Support targets with an addressable size greater than 1
+
+ maxScrollRange = maxScrollRange.divide(BigInteger.valueOf(getAddressableSize()));
+ return maxScrollRange;
+ }
+
+ /**
+ * The scroll range is limited by SWT. Because it can be less than the number
+ * of rows (of memory) that we need to display, we need an arithmetic mapping.
+ *
+ * @return ratio this function returns how many rows a scroll bar unit
+ * represents. The number will be some fractional value, up to but
+ * not exceeding the value 1. I.e., when the scroll range exceeds
+ * the row range, we use a 1:1 mapping.
+ */
+ private final BigDecimal getScrollRatio()
+ {
+ BigInteger maxRange = getMaxScrollRange();
+ if (maxRange.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0)
+ {
+ return new BigDecimal(maxRange).divide(BigDecimal.valueOf(Integer.MAX_VALUE), SCROLL_CONVERSION_PRECISION);
+ }
+
+ return BigDecimal.ONE;
+ }
+
+ /**
+ * Convert memory row units to scroll bar units. The scroll range is limited
+ * by SWT. Because it can be less than the number of rows (of memory) that
+ * we need to display, we need an arithmetic mapping.
+ *
+ * @param rows
+ * units of memory
+ * @return scrollbar units
+ */
+ private int rows2scrollbar(BigInteger rows)
+ {
+ return new BigDecimal(rows).divide(getScrollRatio(), SCROLL_CONVERSION_PRECISION).intValue();
+ }
+
+ /**
+ * Convert scroll bar units to memory row units. The scroll range is limited
+ * by SWT. Because it can be less than the number of rows (of memory) that
+ * we need to display, we need an arithmetic mapping.
+ *
+ * @param scrollbarUnits
+ * scrollbar units
+ * @return number of rows of memory
+ */
+ BigInteger scrollbar2rows(int scrollbarUnits)
+ {
+ return getScrollRatio().multiply(BigDecimal.valueOf(scrollbarUnits), SCROLL_CONVERSION_PRECISION).toBigInteger();
+ }
+
+ /**
+ * @return start address of the memory block
+ */
+ protected BigInteger getMemoryBlockStartAddress()
+ {
+ if (fMemoryBlockStartAddress == null)
+ fMemoryBlockStartAddress = fParent.getMemoryBlockStartAddress();
+ if (fMemoryBlockStartAddress == null)
+ fMemoryBlockStartAddress = BigInteger.ZERO;
+
+ return fMemoryBlockStartAddress;
+ }
+
+ /**
+ * @return end address of the memory block
+ */
+ protected BigInteger getMemoryBlockEndAddress()
+ {
+ if (fMemoryBlockEndAddress == null)
+ fMemoryBlockEndAddress = fParent.getMemoryBlockEndAddress();
+
+ return fMemoryBlockEndAddress;
+ }
+
+ public FPDataType getFPDataType()
+ {
+ return fFPDataType;
+ }
+
+ public void setFPDataType(FPDataType numberType)
+ {
+ if (fFPDataType == numberType) return;
+
+ fFPDataType = numberType;
+ fireSettingsChanged();
+ layoutPanes();
+ }
+
+ public int getUpdateMode()
+ {
+ return fUpdateMode;
+ }
+
+ public void setUpdateMode(int fUpdateMode)
+ {
+ this.fUpdateMode = fUpdateMode;
+ }
+
+ protected String getCharacterSet(int mode)
+ {
+ switch (mode)
+ {
+ case Rendering.TEXT_UTF8:
+ return "UTF8"; //$NON-NLS-1$
+
+ case Rendering.TEXT_UTF16:
+ return "UTF16"; //$NON-NLS-1$
+
+ case Rendering.TEXT_USASCII:
+ return "US-ASCII"; //$NON-NLS-1$
+
+ case Rendering.TEXT_ISO_8859_1:
+ default:
+ return "ISO-8859-1"; //$NON-NLS-1$
+ }
+ }
+
+ public int getBytesPerCharacter()
+ {
+ return 1;
+ }
+
+ public boolean isTargetLittleEndian()
+ {
+ return fIsTargetLittleEndian;
+ }
+
+ public void setTargetLittleEndian(boolean littleEndian)
+ {
+ if (fIsTargetLittleEndian == littleEndian) return;
+
+ fParent.setTargetMemoryLittleEndian(littleEndian);
+ fIsTargetLittleEndian = littleEndian;
+
+ Display.getDefault().asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ fireSettingsChanged();
+ layoutPanes();
+ }
+ });
+ }
+
+ public boolean isDisplayLittleEndian()
+ {
+ return fIsDisplayLittleEndian;
+ }
+
+ public void setDisplayLittleEndian(boolean isLittleEndian)
+ {
+ if (fIsDisplayLittleEndian == isLittleEndian) return;
+ fIsDisplayLittleEndian = isLittleEndian;
+ fireSettingsChanged();
+
+ Display.getDefault().asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ layoutPanes();
+ }
+ });
+ }
+
+ public int getCharsPerColumn()
+ {
+ return fCharsPerColumn;
+ }
+
+ public int getDisplayedPrecision()
+ {
+ return fDisplayedPrecision;
+ }
+
+ // Set the number of precision digits that are displayed in the view
+
+ public void setDisplayedPrecision(int displayedPrecision)
+ {
+ if (fDisplayedPrecision != displayedPrecision)
+ {
+ fDisplayedPrecision = displayedPrecision;
+ fCharsPerColumn = charsPerColumn();
+ fireSettingsChanged();
+ layoutPanes();
+ }
+ }
+
+ protected void redrawPane(int paneId)
+ {
+ if (!isDisposed() && this.isVisible())
+ {
+ FPAbstractPane pane = null;
+
+ if (paneId == Rendering.PANE_ADDRESS)
+ {
+ pane = fAddressPane;
+ }
+ else if (paneId == Rendering.PANE_DATA)
+ {
+ pane = fDataPane;
+ }
+
+ if (pane != null && pane.isPaneVisible())
+ {
+ pane.redraw();
+ pane.setRowCount();
+ if (pane.isFocusControl()) pane.updateTheCaret();
+ }
+ }
+
+ fParent.updateRenderingLabels();
+ }
+
+ protected void redrawPanes()
+ {
+ if (!isDisposed() && this.isVisible())
+ {
+ if (fAddressPane.isPaneVisible())
+ {
+ fAddressPane.redraw();
+ fAddressPane.setRowCount();
+ if (fAddressPane.isFocusControl()) fAddressPane.updateTheCaret();
+ }
+
+ if (fDataPane.isPaneVisible())
+ {
+ fDataPane.redraw();
+ fDataPane.setRowCount();
+ if (fDataPane.isFocusControl()) fDataPane.updateTheCaret();
+ }
+ }
+
+ fParent.updateRenderingLabels();
+ }
+
+ void layoutPanes()
+ {
+ packColumns();
+ layout(true);
+
+ redraw();
+ redrawPanes();
+ }
+
+ void fireSettingsChanged()
+ {
+ fAddressPane.settingsChanged();
+ fDataPane.settingsChanged();
+ }
+
+ protected void copyAddressToClipboard()
+ {
+ Clipboard clip = null;
+
+ try
+ {
+ clip = new Clipboard(getDisplay());
+
+ String addressString = "0x" + getCaretAddress().toString(16); //$NON-NLS-1$
+
+ TextTransfer plainTextTransfer = TextTransfer.getInstance();
+ clip.setContents(new Object[] { addressString }, new Transfer[] { plainTextTransfer });
+ }
+ finally
+ {
+ if (clip != null)
+ {
+ clip.dispose();
+ }
+ }
+ }
+
+ // Given an array of bytes, the data type and endianness, return a scientific notation string representation
+
+ public String sciNotationString(FPMemoryByte byteArray[], FPDataType fpDataType, boolean isLittleEndian)
+ {
+ StringBuffer textString = null;
+
+ // Check the byte array for readability
+
+ for (int index = 0; index < byteArray.length; index++)
+ if (!byteArray[index].isReadable())
+ return FPutilities.fillString(fCharsPerColumn, '?');
+
+ // Convert the byte array to a floating point value and check to see if it's within the range
+ // of the data type. If the valid range is exceeded, set string to "-Infinity" or "Infinity".
+
+ ByteOrder byteOrder = isLittleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
+
+ if (fpDataType == FPDataType.FLOAT)
+ {
+ float floatValue = ByteBuffer.wrap(FPutilities.memoryBytesToByteArray(byteArray)).order(byteOrder).getFloat();
+ floatValue = FPutilities.floatLimitCheck(floatValue);
+
+ if (floatValue == Float.NEGATIVE_INFINITY)
+ textString = new StringBuffer("-Infinity"); //$NON-NLS-1$
+
+ if (floatValue == Float.POSITIVE_INFINITY)
+ textString = new StringBuffer(" Infinity"); //$NON-NLS-1$
+ }
+
+ if (fpDataType == FPDataType.DOUBLE)
+ {
+ double doubleValue = ByteBuffer.wrap(FPutilities.memoryBytesToByteArray(byteArray)).order(byteOrder).getDouble();
+ doubleValue = FPutilities.doubleLimitCheck(doubleValue);
+
+ if (doubleValue == Double.NEGATIVE_INFINITY)
+ textString = new StringBuffer("-Infinity"); //$NON-NLS-1$
+
+ if (doubleValue == Double.POSITIVE_INFINITY)
+ textString = new StringBuffer(" Infinity"); //$NON-NLS-1$
+ }
+
+ // If we do not already have a StringBuffer value, Convert the value to
+ // a string. In any case, pad the string with spaces to the cell width.
+
+ if (textString == null)
+ textString = new StringBuffer(FPutilities.byteArrayToSciNotation(fpDataType, isLittleEndian, byteArray, fDisplayedPrecision));
+
+ return (textString.append(FPutilities.fillString(fCharsPerColumn - textString.length(), getPaddingCharacter()))).toString();
+ }
+
+ // Convert the floating point edit buffer string to a byte array and update memory
+
+ public void convertAndUpdateCell(BigInteger memoryAddress, String editBuffer)
+ {
+ if (editBuffer != null && editBuffer.length() > 0)
+ {
+ // Convert the edit buffer string to byte arrays
+
+ byte[] targetByteArray = new byte[getFPDataType().getByteLength()];
+
+ targetByteArray = FPutilities.floatingStringToByteArray(getFPDataType(), editBuffer, getFPDataType().getBitsize());
+
+ // If we're in little endian mode, reverse the bytes, which is required
+ // due to lower-level internal conversion when written to memory.
+
+ if (fIsDisplayLittleEndian)
+ targetByteArray = FPutilities.reverseByteOrder(targetByteArray);
+
+ FPMemoryByte[] newMemoryBytes = new FPMemoryByte[targetByteArray.length];
+
+ for (int index = 0; index < targetByteArray.length; index++)
+ {
+ newMemoryBytes[index] = new FPMemoryByte(targetByteArray[index]);
+ newMemoryBytes[index].setBigEndian(!isTargetLittleEndian());
+ newMemoryBytes[index].setEdited(true);
+ newMemoryBytes[index].setChanged(true);
+ }
+
+ // Apply the change and make it visible in the view
+
+ getViewportCache().setEditedValue(memoryAddress, newMemoryBytes);
+ getViewportCache().writeEditBuffer();
+ redraw();
+ }
+ else
+ {
+ // The edit buffer string is null or has a zero length.
+
+ final String errorText = NLS.bind(FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_POPUP_TEXT"), "<blanks>"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ try
+ {
+ // Restore the previous value
+ setEditBuffer(new StringBuffer(fDataPane.bytesToSciNotation(getBytes(fCaretAddress, getFPDataType().getByteLength()))));
+ }
+ catch (DebugException e)
+ {
+ e.printStackTrace();
+ }
+
+ // Put together the pop-up window components and show the user the error
+
+ String statusString = FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_STATUS"); //$NON-NLS-1$
+ Status status = new Status(IStatus.ERROR, FPRenderingPlugin.getUniqueIdentifier(), statusString);
+ FPutilities.popupMessage(FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_POPUP_TITLE"), errorText, status); //$NON-NLS-1$
+ }
+ }
+
+ // Getter/setter for Insert Mode state
+
+ public void setInsertMode(boolean insertMode)
+ {
+ this.fEditInserMode = insertMode;
+ }
+
+ public boolean insertMode()
+ {
+ return fEditInserMode;
+ }
+
+ // Getter/setter for cell edit state: true = we're in cell edit mode; false = we're not in cell edit mode
+
+ public boolean isEditingCell()
+ {
+ return fCellEditState;
+ }
+
+ public void setEditingCell(boolean state)
+ {
+ this.fCellEditState = state;
+ }
+
+ // Getter/setter for the address of the cell currently being edited
+
+ public BigInteger getCellEditAddress()
+ {
+ return cellEditAddress;
+ }
+
+ public void setCellEditAddress(BigInteger cellEditAddress)
+ {
+ this.cellEditAddress = cellEditAddress;
+ }
+
+ // Getter/Setter for the memory address of the cell currently being edited
+
+ public BigInteger getMemoryAddress()
+ {
+ return memoryAddress;
+ }
+
+ public void setMemoryAddress(BigInteger memoryAddress)
+ {
+ this.memoryAddress = memoryAddress;
+ }
+
+ // Getter/setter for storing the raw/uninterpreted/not-converted-to-scientific-notation text entry string
+
+ public StringBuffer getEditBuffer()
+ {
+ return fEditBuffer;
+ }
+
+ public void setEditBuffer(StringBuffer cellChars)
+ {
+ this.fEditBuffer = cellChars;
+ }
+
+ // Enter cell-edit mode
+
+ public void startCellEditing(BigInteger cellAddress, BigInteger memoryAddress, String editString)
+ {
+ setEditBuffer(new StringBuffer(editString));
+ setCellEditAddress(cellAddress);
+ setMemoryAddress(memoryAddress);
+ setEditingCell(true);
+ }
+
+ // Exit cell-edit mode
+
+ public void endCellEditing()
+ {
+ setEditingCell(false);
+ setCellEditAddress(null);
+ setMemoryAddress(null);
+ setEditBuffer(null);
+
+ }
+
+ // Floating point number edit mode status-line display: 'true' = display edit mode, 'false' clear edit mode
+
+ public void displayEditModeIndicator(final boolean indicatorON)
+ {
+ UIJob job = new UIJob("FP Renderer Edit Indicator") //$NON-NLS-1$
+ {
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor)
+ {
+ String statusLineMessage;
+ IViewPart viewInstance = null;
+
+ if (indicatorON)
+ {
+ // Construct the edit mode message
+ statusLineMessage = NLS.bind(FPRenderingMessages.getString("FPRendering.EDIT_MODE"), //$NON-NLS-1$
+ (insertMode() ? FPRenderingMessages.getString("FPRendering.EDIT_MODE_INSERT") : //$NON-NLS-1$
+ FPRenderingMessages.getString("FPRendering.EDIT_MODE_OVERWRITE"))); //$NON-NLS-1$
+ }
+ else
+ {
+ // 'null' = clear the message
+ statusLineMessage = null;
+ }
+
+ // Get the window and page references
+
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window == null) return Status.OK_STATUS;
+
+ IWorkbenchPage page = window.getActivePage();
+ if (page == null) return Status.OK_STATUS;
+
+ // Update (or clear) the Workbench status line when the Memory and Memory Browser views are in focus
+
+ viewInstance = page.findView("org.eclipse.debug.ui.MemoryView"); // TODO: programmatically retrieve ID //$NON-NLS-1$
+
+ if (viewInstance != null)
+ viewInstance.getViewSite().getActionBars().getStatusLineManager().setMessage(statusLineMessage);
+
+ viewInstance = page.findView("org.eclipse.cdt.debug.ui.memory.memorybrowser.MemoryBrowser"); // TODO: programmatically retrieve ID //$NON-NLS-1$
+
+ if (viewInstance != null)
+ viewInstance.getViewSite().getActionBars().getStatusLineManager().setMessage(statusLineMessage);
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ job.setSystem(true);
+ job.schedule();
+ }
+
+ // Calculate memory address from the cell address
+
+ public BigInteger cellToMemoryAddress(BigInteger cellAddress)
+ {
+ BigInteger vpStart = getViewportStartAddress();
+ BigInteger colChars = BigInteger.valueOf(getCharsPerColumn());
+ BigInteger dtBytes = BigInteger.valueOf(getFPDataType().getByteLength());
+
+ return vpStart.add(((cellAddress.subtract(vpStart)).divide(colChars)).multiply(dtBytes));
+ }
+}

Back to the top