public void suspend() throws DebugException
{
if (isTerminated())
- throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
- "Can't suspend a terminated MachineProcess", null));
+ throwDebugException("Can't suspend a terminated MachineProcess");
if (isSuspended())
- throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
- "Can't suspend a suspended MachineProcess", null));
+ throwDebugException("Can't suspend a suspended MachineProcess");
exec.pauseLiveExecution();
fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
public void resume() throws DebugException
{
if (isTerminated())
- throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
- "Can't resume a terminated MachineProcess", null));
+ throwDebugException("Can't resume a terminated MachineProcess");
if (!isSuspended())
- throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
- "Can't resume a non-suspended MachineProcess", null));
+ throwDebugException("Can't resume a non-suspended MachineProcess");
exec.unpauseLiveExecution();
fireResumeEvent(DebugEvent.CLIENT_REQUEST);
public int getExitValue() throws DebugException
{
if (!isTerminated())
- throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
- "Can't get the exit value of a running process", null));
+ throwDebugException("Can't get the exit value of a running process");
return 0;
}
if (manager != null)
manager.fireDebugEventSet(new DebugEvent[] { event });
}
+
+ private static void throwDebugException(String message) throws DebugException
+ {
+ throw new DebugException(
+ new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null));
+ }
}
\ No newline at end of file
package net.mograsim.plugin.launch;
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IMemoryBlock;
import net.mograsim.machine.MainMemory;
import net.mograsim.plugin.MograsimActivator;
-public class MainMemoryBlock implements IMemoryBlock
+@Deprecated
+public class MainMemoryBlock extends PlatformObject implements IMemoryBlock
{
private final MachineDebugTarget debugTarget;
private final MainMemory mem;
public MainMemoryBlock(MachineDebugTarget debugTarget, long startAddress, long length)
{
+ MainMemory mem = debugTarget.getMachine().getMainMemory();
+
if (length < 0)
- throw new IllegalArgumentException("Can't create a memory block of negative size");
+ throw new IllegalArgumentException("Negative size");
+ if (startAddress < 0)
+ throw new IllegalArgumentException("Negative start address");
+ if ((startAddress + length) / 2 > mem.size())
+ throw new IllegalArgumentException("End address higher than memory size");
if (length > Integer.MAX_VALUE)
- throw new IllegalArgumentException("Can't create a memory block bigger than Integer.MAX_VALUE (" + Integer.MAX_VALUE + "\"");
- if (startAddress % 2 != 0)
- throw new IllegalArgumentException("Can't create an unaligned memory block");
+ throw new IllegalArgumentException("Memory block bigger than Integer.MAX_VALUE (" + Integer.MAX_VALUE + "\"");
+ if (startAddress % 2 != 0 || length % 2 != 0)
+ throw new IllegalArgumentException("Unaligned memory block");
this.debugTarget = debugTarget;
- this.mem = debugTarget.getMachine().getMainMemory();
+ this.mem = mem;
this.startAddress = startAddress;
this.length = (int) length;
+
+ mem.registerCellModifiedListener(e ->
+ {
+ if (e >= startAddress / 2 && e < (startAddress + length) / 2)
+ fireContentChangeEvent();
+ });
}
@Override
return debugTarget.getLaunch();
}
- @Override
- public <T> T getAdapter(Class<T> adapter)
- {
- // TODO
- return null;
- }
-
@Override
public long getStartAddress()
{
@Override
public byte[] getBytes() throws DebugException
{
- // TODO speedup. Maybe make a method for this in MainMemory?
byte[] bs = new byte[length];
int i;
long j;
- for (i = 0, j = startAddress / 2; i < length; i++)
+ for (i = 0, j = startAddress / 2; i < length; i += 2, j++)
{
- short word = mem.getCellAsBigInteger(j).shortValueExact();
+ short word = mem.getCellAsBigInteger(j).shortValue();
bs[i + 0] = (byte) (word & 0xFF);
bs[i + 1] = (byte) (word >>> 8);
}
@Override
public boolean supportsValueModification()
{
- // TODO
- return false;
+ return true;
}
@Override
public void setValue(long offset, byte[] bytes) throws DebugException
{
- // TODO
+ if (offset % 2 != 0 || bytes.length % 2 != 0)
+ throw new IllegalArgumentException("Can't write unaligned to a memory block");
+ int i;
+ long j;
+ for (i = 0, j = (startAddress + offset) / 2; i < bytes.length; i += 2, j++)
+ {
+ short word = 0;
+ word |= bytes[i + 0] & 0xFF;
+ word |= bytes[i + 1] << 8;
+ mem.setCellAsBigInteger(j, BigInteger.valueOf(word));
+ }
+ }
+
+ /**
+ * Fires a terminate event for this debug element.
+ */
+ private void fireContentChangeEvent()
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+ }
+
+ /**
+ * Fires a debug event.
+ *
+ * @param event debug event to fire
+ */
+ private static void fireEvent(DebugEvent event)
+ {
+ DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
}
}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+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.ILaunch;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IMemoryBlockExtension;
+import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
+import org.eclipse.debug.core.model.MemoryByte;
+
+import net.mograsim.machine.MainMemory;
+import net.mograsim.machine.MainMemoryDefinition;
+import net.mograsim.machine.Memory.MemoryCellModifiedListener;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MainMemoryBlockExtension extends PlatformObject implements IMemoryBlockExtension
+{
+ // TODO do we want to make the memory accessible byte-wise?
+
+ private final String expression;
+ private final MachineDebugTarget debugTarget;
+ private final MainMemory mem;
+
+ private final MainMemoryDefinition memDef;
+ private final int cellWidthBits;
+ private final int cellWidthBytes;
+ private final BigInteger cellWidthBytesBI;
+ private final BigInteger minAddrWords;
+ private final BigInteger maxAddrWords;
+
+ private BigInteger baseAddrWords;
+ private BigInteger lengthWords;
+
+ private final Set<Object> clients;
+ private final MemoryCellModifiedListener memListener;
+ private final AtomicBoolean memListenerRegistered;
+
+ public MainMemoryBlockExtension(MachineDebugTarget debugTarget, String expression, @SuppressWarnings("unused") Object expressionContext)
+ throws DebugException
+ {
+ this.expression = expression;
+ this.debugTarget = debugTarget;
+ this.mem = debugTarget.getMachine().getMainMemory();
+
+ this.memDef = mem.getDefinition();
+ this.cellWidthBits = memDef.getCellWidth();
+ this.cellWidthBytes = (cellWidthBits + 7) / 8;
+ this.cellWidthBytesBI = BigInteger.valueOf(cellWidthBytes);
+ this.minAddrWords = BigInteger.valueOf(memDef.getMinimalAddress());
+ this.maxAddrWords = BigInteger.valueOf(memDef.getMaximalAddress());
+
+ // TODO parse expression better
+ this.baseAddrWords = new BigInteger(expression, 16);
+ this.lengthWords = BigInteger.ONE;
+
+ if (baseAddrWords.compareTo(minAddrWords) < 0 || baseAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("Base address out of range");
+ if (baseAddrWords.add(lengthWords).compareTo(maxAddrWords) > 0)
+ throwDebugException("End address out of range");
+
+ this.clients = new HashSet<>();
+ // don't check whether the address is in range, because this memory block could be read outside its "range"
+ this.memListener = a -> fireContentChangeEvent();
+ this.memListenerRegistered = new AtomicBoolean();
+ }
+
+ @Override
+ public long getStartAddress()
+ {
+ return baseAddrWords.multiply(cellWidthBytesBI).longValueExact();
+ }
+
+ @Override
+ public long getLength()
+ {
+ return lengthWords.multiply(cellWidthBytesBI).longValueExact();
+ }
+
+ @Override
+ public byte[] getBytes() throws DebugException
+ {
+ BigInteger endAddrWords = baseAddrWords.add(lengthWords);
+ if (endAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("End address out of range");
+ int lengthBytes = lengthWords.multiply(cellWidthBytesBI).intValueExact();
+
+ byte[] bytes = new byte[lengthBytes];
+ int i;
+ long j;
+ for (i = 0, j = baseAddrWords.longValue(); i < lengthBytes; i += cellWidthBytes, j++)
+ {
+ BigInteger word = mem.getCellAsBigInteger(j);
+ System.arraycopy(word.toByteArray(), 0, bytes, i, cellWidthBytes);
+ }
+ return bytes;
+ }
+
+ @Override
+ public boolean supportsValueModification()
+ {
+ return true;
+ }
+
+ @Override
+ public void setValue(long offset, byte[] bytes) throws DebugException
+ {
+ if (offset % cellWidthBytes != 0 || bytes.length % cellWidthBytes != 0)
+ throwDebugException("Requested unaligned memory write");
+ BigInteger startAddrWords = baseAddrWords.add(BigInteger.valueOf(offset / cellWidthBytes));
+ if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("Start address out of range");
+
+ BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes));
+ if (endAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("End address out of range");
+
+ int i;
+ long j;
+ for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++)
+ {
+ BigInteger word = new BigInteger(bytes, i, cellWidthBytes);
+ mem.setCellAsBigInteger(j, word);
+ }
+ }
+
+ @Override
+ public String getModelIdentifier()
+ {
+ return MograsimActivator.PLUGIN_ID;
+ }
+
+ @Override
+ public IDebugTarget getDebugTarget()
+ {
+ return debugTarget;
+ }
+
+ @Override
+ public ILaunch getLaunch()
+ {
+ return debugTarget.getLaunch();
+ }
+
+ @Override
+ public String getExpression()
+ {
+ return expression;
+ }
+
+ @Override
+ public BigInteger getBigBaseAddress() throws DebugException
+ {
+ return baseAddrWords;
+ }
+
+ @Override
+ public BigInteger getMemoryBlockStartAddress() throws DebugException
+ {
+ return minAddrWords;
+ }
+
+ @Override
+ public BigInteger getMemoryBlockEndAddress() throws DebugException
+ {
+ return maxAddrWords;
+ }
+
+ @Override
+ public BigInteger getBigLength() throws DebugException
+ {
+ return maxAddrWords.subtract(minAddrWords);
+ }
+
+ @Override
+ public int getAddressSize() throws DebugException
+ {
+ return Long.BYTES;
+ }
+
+ @Override
+ public boolean supportBaseAddressModification() throws DebugException
+ {
+ return true;
+ }
+
+ @Override
+ public boolean supportsChangeManagement()
+ {
+ return false;
+ }
+
+ @Override
+ public void setBaseAddress(BigInteger address) throws DebugException
+ {
+ if (address.compareTo(minAddrWords) < 0 || address.compareTo(maxAddrWords) > 0)
+ throwDebugException("Address out of range");
+ this.baseAddrWords = address;
+ }
+
+ @Override
+ public MemoryByte[] getBytesFromOffset(BigInteger unitOffset, long addressableUnits) throws DebugException
+ {
+ return getBytesFromAddress(getBigBaseAddress().add(unitOffset), addressableUnits);
+ }
+
+ @Override
+ public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException
+ {
+ if (units < 0)
+ throwDebugException("Requested negative amount of unites");
+ int lengthBytes = BigInteger.valueOf(units).multiply(cellWidthBytesBI).intValueExact();
+
+ MemoryByte[] bytes = new MemoryByte[lengthBytes];
+ int i;
+ BigInteger j;
+ for (i = 0, j = address; i < lengthBytes; i += cellWidthBytes, j = j.add(BigInteger.ONE))
+ {
+ if (j.compareTo(minAddrWords) >= 0 && j.compareTo(maxAddrWords) <= 0)
+ {
+ BigInteger word = mem.getCellAsBigInteger(j.longValue());
+ byte[] wordBytes = word.toByteArray();
+ int k;
+ for (k = 0; k < cellWidthBytes - wordBytes.length; k++)
+ bytes[i + k] = new MemoryByte();
+ for (int l = 0; k < cellWidthBytes; k++)
+ bytes[i + k] = new MemoryByte(wordBytes[l]);
+ } else
+ for (int k = 0; k < cellWidthBytes; k++)
+ bytes[i + k] = new MemoryByte((byte) 0, (byte) 0);
+ }
+ return bytes;
+ }
+
+ @Override
+ public void setValue(BigInteger offset, byte[] bytes) throws DebugException
+ {
+ if (bytes.length % cellWidthBytes != 0)
+ throwDebugException("Requested unaligned memory write");
+ BigInteger startAddrWords = baseAddrWords.add(offset);
+ if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("Start address out of range");
+ BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes));
+ if (endAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("End address out of range");
+
+ unregisterMemoryListener();
+
+ int i;
+ long j;
+ for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++)
+ {
+ BigInteger word = new BigInteger(bytes, i, cellWidthBytes);
+ mem.setCellAsBigInteger(j, word);
+ }
+
+ if (!clients.isEmpty())
+ registerMemoryListener();
+ fireContentChangeEvent();
+ }
+
+ @Override
+ public void connect(Object client)
+ {
+ registerMemoryListener();
+ clients.add(client);
+ }
+
+ @Override
+ public void disconnect(Object client)
+ {
+ clients.remove(client);
+
+ if (clients.isEmpty())
+ unregisterMemoryListener();
+ }
+
+ @Override
+ public Object[] getConnections()
+ {
+
+ Set<Object> clientsLocal = clients;
+ return clientsLocal == null ? new Object[0] : clientsLocal.toArray();
+ }
+
+ private void registerMemoryListener()
+ {
+ if (!memListenerRegistered.getAndSet(true))
+ mem.registerCellModifiedListener(memListener);
+ }
+
+ private void unregisterMemoryListener()
+ {
+ if (memListenerRegistered.getAndSet(false))
+ mem.deregisterCellModifiedListener(memListener);
+ }
+
+ @Override
+ public void dispose() throws DebugException
+ {
+ clients.clear();
+ unregisterMemoryListener();
+ }
+
+ @Override
+ public IMemoryBlockRetrievalExtension getMemoryBlockRetrieval()
+ {
+ return debugTarget;
+ }
+
+ @Override
+ public int getAddressableSize() throws DebugException
+ {
+ return cellWidthBytes;
+ }
+
+ /**
+ * Fires a terminate event for this debug element.
+ */
+ private void fireContentChangeEvent()
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
+ }
+
+ /**
+ * Fires a debug event.
+ *
+ * @param event debug event to fire
+ */
+ private static void fireEvent(DebugEvent event)
+ {
+ DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
+ }
+
+ private static void throwDebugException(String message) throws DebugException
+ {
+ throw new DebugException(
+ new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null));
+ }
+}
\ No newline at end of file