/*******************************************************************************
* Copyright (c) 2002, 2005 QNX Software Systems 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:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.mi.core.cdi.model;
import java.math.BigInteger;
import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock;
import org.eclipse.cdt.debug.mi.core.MIException;
import org.eclipse.cdt.debug.mi.core.MIFormat;
import org.eclipse.cdt.debug.mi.core.MISession;
import org.eclipse.cdt.debug.mi.core.cdi.CdiResources;
import org.eclipse.cdt.debug.mi.core.cdi.ExpressionManager;
import org.eclipse.cdt.debug.mi.core.cdi.MI2CDIException;
import org.eclipse.cdt.debug.mi.core.cdi.MemoryManager;
import org.eclipse.cdt.debug.mi.core.cdi.RegisterManager;
import org.eclipse.cdt.debug.mi.core.cdi.Session;
import org.eclipse.cdt.debug.mi.core.cdi.VariableManager;
import org.eclipse.cdt.debug.mi.core.command.CommandFactory;
import org.eclipse.cdt.debug.mi.core.command.MIDataWriteMemory;
import org.eclipse.cdt.debug.mi.core.output.MIDataReadMemoryInfo;
import org.eclipse.cdt.debug.mi.core.output.MIInfo;
import org.eclipse.cdt.debug.mi.core.output.MIMemory;
/**
*/
public class MemoryBlock extends CObject implements ICDIMemoryBlock {
String expression;
boolean frozen;
boolean dirty;
private MIDataReadMemoryInfo mem;
private int fWordSize;
private BigInteger cStartAddress; //cached start address
private byte[] cBytes; //cached bytes
private int[] badOffsets;
private boolean fIsLittleEndian;
public MemoryBlock(Target target, String exp, int wordSize, boolean isLittle, MIDataReadMemoryInfo info) {
super(target);
expression = exp;
fWordSize = wordSize;
frozen = true;
fIsLittleEndian = isLittle;
setMIDataReadMemoryInfo(info);
}
/**
* @return the expression use to create the block.
*/
public String getExpression() {
return expression;
}
/**
* @return the size of each memory word in bytes.
*/
public int getWordSize() {
return fWordSize;
}
/**
* Reset the internal MIDataReadMemoryInfo. All modifications into mem info should be done
* using this method
*/
public void setMIDataReadMemoryInfo(MIDataReadMemoryInfo m) {
cStartAddress = MIFormat.getBigInteger(m.getAddress());
cBytes = getBytes(m);
mem = m;
}
/**
* @return the internal MIDataReadMemoryInfo.
*/
public MIDataReadMemoryInfo getMIDataReadMemoryInfo() {
return mem;
}
/**
* @return true if any address in the array is within the block.
*/
public boolean contains(BigInteger[] adds) {
for (int i = 0; i < adds.length; i++) {
if (contains(adds[i])) {
return true;
}
}
return false;
}
/**
* @return true if the address is within the block.
*/
public boolean contains(BigInteger addr) {
BigInteger start = getStartAddress();
long length = getLength();
if ( start.compareTo(addr) <= 0 &&
addr.compareTo(start.add(BigInteger.valueOf(length))) <= 0 )
{
return true;
}
return false;
}
/**
* Use by the EventManager to check fire events when doing refresh().
*/
public boolean isDirty() {
return dirty;
}
/**
* Use by the EventManager to check fire events when doing refresh().
*/
public void setDirty(boolean d) {
dirty = d;
}
/**
*
*/
private byte[] getBytes(MIDataReadMemoryInfo m) {
byte[] bytes = new byte[0];
// sanity.
if (m == null) {
return bytes;
}
// collect the data
MIMemory[] miMem = m.getMemories();
for (int i = 0; i < miMem.length; ++i) {
long[] data = miMem[i].getData();
if (data != null && data.length > 0) {
// int blen = bytes.length;
// byte[] newBytes = new byte[blen + data.length];
// System.arraycopy(bytes, 0, newBytes, 0, blen);
// for (int j = 0; j < data.length; ++j, ++blen) {
// newBytes[blen] = (byte)data[j];
// }
// bytes = newBytes;
for (int j = 0; j < data.length; ++j) {
byte[] bs = longToBytes(data[j]);
// grow the array
int blen = bytes.length;
byte[] newBytes = new byte[blen + bs.length];
System.arraycopy(bytes, 0, newBytes, 0, blen);
System.arraycopy(bs, 0, newBytes, blen, bs.length);
bytes = newBytes;
}
}
}
return bytes;
}
private int[] getBadOffsets(MIDataReadMemoryInfo m) {
int[] offsets = new int[0];
// sanity
if (m == null) {
return offsets;
}
// collect the data
MIMemory[] miMem = m.getMemories();
for (int i = 0; i < miMem.length; i++) {
int[] data = miMem[i].getBadOffsets();
if (data.length > 0) {
int olen = offsets.length;
int[] newOffsets = new int[olen + data.length];
System.arraycopy(offsets, 0, newOffsets, 0, olen);
System.arraycopy(data, 0, newOffsets, olen, data.length);
offsets = newOffsets;
}
}
return offsets;
}
public byte[] getBytes() throws CDIException {
return cBytes;
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock#refresh()
*/
public void refresh() throws CDIException {
Target target = (Target)getTarget();
MemoryManager mgr = ((Session)target.getSession()).getMemoryManager();
setDirty(true);
BigInteger[] addresses = mgr.update(this, null);
// Check if this affects other blocks.
if (addresses.length > 0) {
MemoryBlock[] blocks = mgr.getMemoryBlocks(target.getMISession());
for (int i = 0; i < blocks.length; i++) {
MemoryBlock block = blocks[i];
if (! block.equals(this) && block.contains(addresses)) {
block.setDirty(true);
mgr.update(block, null);
}
}
}
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock#getLength()
*/
public long getLength() {
try {
// use this instead. If the wordSize
// given does not match the hardware,
// counting the bytes will be correct.
return getBytes().length;
} catch (CDIException e) {
// ignore.
}
return mem.getTotalBytes();
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock#getStartAddress()
*/
public BigInteger getStartAddress() {
return cStartAddress;
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock#isFrozen()
*/
public boolean isFrozen() {
return frozen;
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock#setFrozen(boolean)
*/
public void setFrozen(boolean frozen) {
this.frozen = frozen;
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock#setValue(long, byte[])
*/
public void setValue(long offset, byte[] bytes) throws CDIException {
if (offset >= getLength() || offset + bytes.length > getLength()) {
throw new CDIException(CdiResources.getString("cdi.model.MemoryBlock.Bad_Offset")); //$NON-NLS-1$
}
MISession miSession = ((Target)getTarget()).getMISession();
CommandFactory factory = miSession.getCommandFactory();
for (int i = 0; i < bytes.length; i++) {
long l = new Byte(bytes[i]).longValue() & 0xff;
String value = "0x" + Long.toHexString(l); //$NON-NLS-1$
MIDataWriteMemory mw = factory.createMIDataWriteMemory(offset + i,
expression, MIFormat.HEXADECIMAL, 1, value);
try {
miSession.postCommand(mw);
MIInfo info = mw.getMIInfo();
if (info == null) {
throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
}
} catch (MIException e) {
throw new MI2CDIException(e);
}
}
// If the assign was succesfull fire a MIChangedEvent() via refresh.
refresh();
Target target = (Target)getTarget();
// If register manager is on autoupdate, update all registers
RegisterManager regMgr = ((Session)target.getSession()).getRegisterManager();
if (regMgr.isAutoUpdate()) {
regMgr.update(target);
}
// If expression manager is on autoupdate, update all expressions
ExpressionManager expMgr = ((Session)target.getSession()).getExpressionManager();
if (expMgr.isAutoUpdate()) {
expMgr.update(target);
}
// If variable manager is on autoupdate, update all variables.
VariableManager varMgr = ((Session)target.getSession()).getVariableManager();
if (varMgr.isAutoUpdate()) {
varMgr.update(target);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock#getFlags(int)
*/
public synchronized byte getFlags(int offset) {
if (offset < 0 || offset >= getLength()) {
throw new IndexOutOfBoundsException();
}
if (badOffsets == null) {
badOffsets = getBadOffsets(mem);
}
if (badOffsets != null) {
for (int i = 0; i < badOffsets.length; ++i) {
if (badOffsets[i] == offset) {
return 0;
}
}
}
return VALID;
}
/**
* We should use the wordSize ... but ...
* The problem: the user may not have the right wordsize
* For example on some DSP the user set the wordSize to be 1 byte
* but in fact GDB is reading 2 bytes.
* So let do some guessing since the data(long) may have a bigger value then one byte.
*/
private byte[] longToBytes(long v) {
// Calculate the number of bytes needed
int count = 1;
long value = v;
for (count = 1; (value /= 0x100) > 0; ++count)
;
// Reset the wordSize if incorrect.
if (fWordSize != count) {
fWordSize = count;
}
byte[] bytes = new byte[count];
if (fIsLittleEndian) {
for (int i = count - 1; i >= 0; --i) {
int shift = i * count;
bytes[i] = (byte)((v >>> shift) & 0xFF);
}
// bytes[7] = (byte)((v >>> 56) & 0xFF);
// bytes[6] = (byte)((v >>> 48) & 0xFF);
// bytes[5] = (byte)((v >>> 40) & 0xFF);
// bytes[4] = (byte)((v >>> 32) & 0xFF);
// bytes[3] = (byte)((v >>> 24) & 0xFF);
// bytes[2] = (byte)((v >>> 16) & 0xFF);
// bytes[1] = (byte)((v >>> 8) & 0xFF);
// bytes[0] = (byte)((v >>> 0) & 0xFF);
} else {
for (int i = 0; i < count; ++i) {
int shift = (count - i - 1) * count;
bytes[i] = (byte)((v >>> shift) & 0xFF);
}
// bytes[0] = (byte)((v >>> 56) & 0xFF);
// bytes[1] = (byte)((v >>> 48) & 0xFF);
// bytes[2] = (byte)((v >>> 40) & 0xFF);
// bytes[3] = (byte)((v >>> 32) & 0xFF);
// bytes[4] = (byte)((v >>> 24) & 0xFF);
// bytes[5] = (byte)((v >>> 16) & 0xFF);
// bytes[6] = (byte)((v >>> 8) & 0xFF);
// bytes[7] = (byte)((v >>> 0) & 0xFF);
}
return bytes;
}
}