Made main memory access via the Memory view work
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / launch / MainMemoryBlockExtension.java
1 package net.mograsim.plugin.launch;
2
3 import java.math.BigInteger;
4 import java.util.HashSet;
5 import java.util.Set;
6 import java.util.concurrent.atomic.AtomicBoolean;
7
8 import org.eclipse.core.runtime.IStatus;
9 import org.eclipse.core.runtime.PlatformObject;
10 import org.eclipse.core.runtime.Status;
11 import org.eclipse.debug.core.DebugEvent;
12 import org.eclipse.debug.core.DebugException;
13 import org.eclipse.debug.core.DebugPlugin;
14 import org.eclipse.debug.core.ILaunch;
15 import org.eclipse.debug.core.model.IDebugTarget;
16 import org.eclipse.debug.core.model.IMemoryBlockExtension;
17 import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
18 import org.eclipse.debug.core.model.MemoryByte;
19
20 import net.mograsim.machine.MainMemory;
21 import net.mograsim.machine.MainMemoryDefinition;
22 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
23 import net.mograsim.plugin.MograsimActivator;
24
25 public class MainMemoryBlockExtension extends PlatformObject implements IMemoryBlockExtension
26 {
27         // TODO do we want to make the memory accessible byte-wise?
28
29         private final String expression;
30         private final MachineDebugTarget debugTarget;
31         private final MainMemory mem;
32
33         private final MainMemoryDefinition memDef;
34         private final int cellWidthBits;
35         private final int cellWidthBytes;
36         private final BigInteger cellWidthBytesBI;
37         private final BigInteger minAddrWords;
38         private final BigInteger maxAddrWords;
39
40         private BigInteger baseAddrWords;
41         private BigInteger lengthWords;
42
43         private final Set<Object> clients;
44         private final MemoryCellModifiedListener memListener;
45         private final AtomicBoolean memListenerRegistered;
46
47         public MainMemoryBlockExtension(MachineDebugTarget debugTarget, String expression, @SuppressWarnings("unused") Object expressionContext)
48                         throws DebugException
49         {
50                 this.expression = expression;
51                 this.debugTarget = debugTarget;
52                 this.mem = debugTarget.getMachine().getMainMemory();
53
54                 this.memDef = mem.getDefinition();
55                 this.cellWidthBits = memDef.getCellWidth();
56                 this.cellWidthBytes = (cellWidthBits + 7) / 8;
57                 this.cellWidthBytesBI = BigInteger.valueOf(cellWidthBytes);
58                 this.minAddrWords = BigInteger.valueOf(memDef.getMinimalAddress());
59                 this.maxAddrWords = BigInteger.valueOf(memDef.getMaximalAddress());
60
61                 // TODO parse expression better
62                 this.baseAddrWords = new BigInteger(expression, 16);
63                 this.lengthWords = BigInteger.ONE;
64
65                 if (baseAddrWords.compareTo(minAddrWords) < 0 || baseAddrWords.compareTo(maxAddrWords) > 0)
66                         throwDebugException("Base address out of range");
67                 if (baseAddrWords.add(lengthWords).compareTo(maxAddrWords) > 0)
68                         throwDebugException("End address out of range");
69
70                 this.clients = new HashSet<>();
71                 // don't check whether the address is in range, because this memory block could be read outside its "range"
72                 this.memListener = a -> fireContentChangeEvent();
73                 this.memListenerRegistered = new AtomicBoolean();
74         }
75
76         @Override
77         public long getStartAddress()
78         {
79                 return baseAddrWords.multiply(cellWidthBytesBI).longValueExact();
80         }
81
82         @Override
83         public long getLength()
84         {
85                 return lengthWords.multiply(cellWidthBytesBI).longValueExact();
86         }
87
88         @Override
89         public byte[] getBytes() throws DebugException
90         {
91                 BigInteger endAddrWords = baseAddrWords.add(lengthWords);
92                 if (endAddrWords.compareTo(maxAddrWords) > 0)
93                         throwDebugException("End address out of range");
94                 int lengthBytes = lengthWords.multiply(cellWidthBytesBI).intValueExact();
95
96                 byte[] bytes = new byte[lengthBytes];
97                 int i;
98                 long j;
99                 for (i = 0, j = baseAddrWords.longValue(); i < lengthBytes; i += cellWidthBytes, j++)
100                 {
101                         BigInteger word = mem.getCellAsBigInteger(j);
102                         System.arraycopy(word.toByteArray(), 0, bytes, i, cellWidthBytes);
103                 }
104                 return bytes;
105         }
106
107         @Override
108         public boolean supportsValueModification()
109         {
110                 return true;
111         }
112
113         @Override
114         public void setValue(long offset, byte[] bytes) throws DebugException
115         {
116                 if (offset % cellWidthBytes != 0 || bytes.length % cellWidthBytes != 0)
117                         throwDebugException("Requested unaligned memory write");
118                 BigInteger startAddrWords = baseAddrWords.add(BigInteger.valueOf(offset / cellWidthBytes));
119                 if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0)
120                         throwDebugException("Start address out of range");
121
122                 BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes));
123                 if (endAddrWords.compareTo(maxAddrWords) > 0)
124                         throwDebugException("End address out of range");
125
126                 int i;
127                 long j;
128                 for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++)
129                 {
130                         BigInteger word = new BigInteger(bytes, i, cellWidthBytes);
131                         mem.setCellAsBigInteger(j, word);
132                 }
133         }
134
135         @Override
136         public String getModelIdentifier()
137         {
138                 return MograsimActivator.PLUGIN_ID;
139         }
140
141         @Override
142         public IDebugTarget getDebugTarget()
143         {
144                 return debugTarget;
145         }
146
147         @Override
148         public ILaunch getLaunch()
149         {
150                 return debugTarget.getLaunch();
151         }
152
153         @Override
154         public String getExpression()
155         {
156                 return expression;
157         }
158
159         @Override
160         public BigInteger getBigBaseAddress() throws DebugException
161         {
162                 return baseAddrWords;
163         }
164
165         @Override
166         public BigInteger getMemoryBlockStartAddress() throws DebugException
167         {
168                 return minAddrWords;
169         }
170
171         @Override
172         public BigInteger getMemoryBlockEndAddress() throws DebugException
173         {
174                 return maxAddrWords;
175         }
176
177         @Override
178         public BigInteger getBigLength() throws DebugException
179         {
180                 return maxAddrWords.subtract(minAddrWords);
181         }
182
183         @Override
184         public int getAddressSize() throws DebugException
185         {
186                 return Long.BYTES;
187         }
188
189         @Override
190         public boolean supportBaseAddressModification() throws DebugException
191         {
192                 return true;
193         }
194
195         @Override
196         public boolean supportsChangeManagement()
197         {
198                 return false;
199         }
200
201         @Override
202         public void setBaseAddress(BigInteger address) throws DebugException
203         {
204                 if (address.compareTo(minAddrWords) < 0 || address.compareTo(maxAddrWords) > 0)
205                         throwDebugException("Address out of range");
206                 this.baseAddrWords = address;
207         }
208
209         @Override
210         public MemoryByte[] getBytesFromOffset(BigInteger unitOffset, long addressableUnits) throws DebugException
211         {
212                 return getBytesFromAddress(getBigBaseAddress().add(unitOffset), addressableUnits);
213         }
214
215         @Override
216         public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException
217         {
218                 if (units < 0)
219                         throwDebugException("Requested negative amount of unites");
220                 int lengthBytes = BigInteger.valueOf(units).multiply(cellWidthBytesBI).intValueExact();
221
222                 MemoryByte[] bytes = new MemoryByte[lengthBytes];
223                 int i;
224                 BigInteger j;
225                 for (i = 0, j = address; i < lengthBytes; i += cellWidthBytes, j = j.add(BigInteger.ONE))
226                 {
227                         if (j.compareTo(minAddrWords) >= 0 && j.compareTo(maxAddrWords) <= 0)
228                         {
229                                 BigInteger word = mem.getCellAsBigInteger(j.longValue());
230                                 byte[] wordBytes = word.toByteArray();
231                                 int k;
232                                 for (k = 0; k < cellWidthBytes - wordBytes.length; k++)
233                                         bytes[i + k] = new MemoryByte();
234                                 for (int l = 0; k < cellWidthBytes; k++)
235                                         bytes[i + k] = new MemoryByte(wordBytes[l]);
236                         } else
237                                 for (int k = 0; k < cellWidthBytes; k++)
238                                         bytes[i + k] = new MemoryByte((byte) 0, (byte) 0);
239                 }
240                 return bytes;
241         }
242
243         @Override
244         public void setValue(BigInteger offset, byte[] bytes) throws DebugException
245         {
246                 if (bytes.length % cellWidthBytes != 0)
247                         throwDebugException("Requested unaligned memory write");
248                 BigInteger startAddrWords = baseAddrWords.add(offset);
249                 if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0)
250                         throwDebugException("Start address out of range");
251                 BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes));
252                 if (endAddrWords.compareTo(maxAddrWords) > 0)
253                         throwDebugException("End address out of range");
254
255                 unregisterMemoryListener();
256
257                 int i;
258                 long j;
259                 for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++)
260                 {
261                         BigInteger word = new BigInteger(bytes, i, cellWidthBytes);
262                         mem.setCellAsBigInteger(j, word);
263                 }
264
265                 if (!clients.isEmpty())
266                         registerMemoryListener();
267                 fireContentChangeEvent();
268         }
269
270         @Override
271         public void connect(Object client)
272         {
273                 registerMemoryListener();
274                 clients.add(client);
275         }
276
277         @Override
278         public void disconnect(Object client)
279         {
280                 clients.remove(client);
281
282                 if (clients.isEmpty())
283                         unregisterMemoryListener();
284         }
285
286         @Override
287         public Object[] getConnections()
288         {
289
290                 Set<Object> clientsLocal = clients;
291                 return clientsLocal == null ? new Object[0] : clientsLocal.toArray();
292         }
293
294         private void registerMemoryListener()
295         {
296                 if (!memListenerRegistered.getAndSet(true))
297                         mem.registerCellModifiedListener(memListener);
298         }
299
300         private void unregisterMemoryListener()
301         {
302                 if (memListenerRegistered.getAndSet(false))
303                         mem.deregisterCellModifiedListener(memListener);
304         }
305
306         @Override
307         public void dispose() throws DebugException
308         {
309                 clients.clear();
310                 unregisterMemoryListener();
311         }
312
313         @Override
314         public IMemoryBlockRetrievalExtension getMemoryBlockRetrieval()
315         {
316                 return debugTarget;
317         }
318
319         @Override
320         public int getAddressableSize() throws DebugException
321         {
322                 return cellWidthBytes;
323         }
324
325         /**
326          * Fires a terminate event for this debug element.
327          */
328         private void fireContentChangeEvent()
329         {
330                 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
331         }
332
333         /**
334          * Fires a debug event.
335          *
336          * @param event debug event to fire
337          */
338         private static void fireEvent(DebugEvent event)
339         {
340                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
341         }
342
343         private static void throwDebugException(String message) throws DebugException
344         {
345                 throw new DebugException(
346                                 new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null));
347         }
348 }