Made main memory access via the Memory view work
authorDaniel Kirschten <daniel.kirschten@gmx.de>
Sun, 29 Sep 2019 16:26:04 +0000 (18:26 +0200)
committerDaniel Kirschten <daniel.kirschten@gmx.de>
Sun, 29 Sep 2019 16:26:04 +0000 (18:26 +0200)
plugins/net.mograsim.plugin.core/plugin.xml
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java [new file with mode: 0644]

index 1120682..d291048 100644 (file)
           id="net.mograsim.plugin.core.launchmachine.image">
     </launchConfigurationTypeImage>
  </extension>
+ <extension
+       point="org.eclipse.debug.ui.memoryRenderings">
+    <renderingBindings
+          defaultIds="org.eclipse.debug.ui.rendering.raw_memory"
+          primaryId="org.eclipse.debug.ui.rendering.raw_memory"
+          renderingIds="org.eclipse.debug.ui.rendering.raw_memory">
+    </renderingBindings>
+ </extension>
 </plugin>
index 5270c76..357fe82 100644 (file)
@@ -15,6 +15,8 @@ import org.eclipse.debug.core.model.IBreakpoint;
 import org.eclipse.debug.core.model.IDebugElement;
 import org.eclipse.debug.core.model.IDebugTarget;
 import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IMemoryBlockExtension;
+import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
 import org.eclipse.debug.core.model.IProcess;
 import org.eclipse.debug.core.model.IStepFilters;
 import org.eclipse.debug.core.model.IThread;
@@ -22,7 +24,7 @@ import org.eclipse.debug.core.model.IThread;
 import net.mograsim.machine.Machine;
 import net.mograsim.plugin.MograsimActivator;
 
-public class MachineDebugTarget extends PlatformObject implements IDebugTarget
+public class MachineDebugTarget extends PlatformObject implements IDebugTarget, IMemoryBlockRetrievalExtension
 {
        private final MachineProcess process;
 
@@ -183,12 +185,19 @@ public class MachineDebugTarget extends PlatformObject implements IDebugTarget
                return true;
        }
 
+       @SuppressWarnings("deprecation") // TODO can we throw a DebugException instead?
        @Override
        public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException
        {
                return new MainMemoryBlock(this, startAddress, length);
        }
 
+       @Override
+       public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException
+       {
+               return new MainMemoryBlockExtension(this, expression, context);
+       }
+
        @Override
        public MachineProcess getProcess()
        {
index b22d9c5..f386690 100644 (file)
@@ -90,11 +90,9 @@ public class MachineProcess extends PlatformObject implements IProcess, ISuspend
        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);
@@ -110,11 +108,9 @@ public class MachineProcess extends PlatformObject implements IProcess, ISuspend
        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);
@@ -147,8 +143,7 @@ public class MachineProcess extends PlatformObject implements IProcess, ISuspend
        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;
        }
@@ -262,4 +257,10 @@ public class MachineProcess extends PlatformObject implements IProcess, ISuspend
                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
index b068fc0..c2edf4e 100644 (file)
@@ -1,6 +1,11 @@
 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;
@@ -8,7 +13,8 @@ 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;
@@ -17,17 +23,29 @@ public class MainMemoryBlock implements IMemoryBlock
 
        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
@@ -48,13 +66,6 @@ public class MainMemoryBlock implements IMemoryBlock
                return debugTarget.getLaunch();
        }
 
-       @Override
-       public <T> T getAdapter(Class<T> adapter)
-       {
-               // TODO
-               return null;
-       }
-
        @Override
        public long getStartAddress()
        {
@@ -70,13 +81,12 @@ public class MainMemoryBlock implements IMemoryBlock
        @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);
                }
@@ -86,13 +96,40 @@ public class MainMemoryBlock implements IMemoryBlock
        @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
diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java
new file mode 100644 (file)
index 0000000..9355ae2
--- /dev/null
@@ -0,0 +1,348 @@
+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