Fixed a bug in MainMemoryBlockExtension
[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 l = wordBytes[0] == 0 ? 1 : 0;
232                                 int k;
233                                 for (k = 0; k < cellWidthBytes - wordBytes.length + l; k++)
234                                         bytes[i + k] = new MemoryByte();
235                                 for (; k < cellWidthBytes; k++, l++)
236                                         bytes[i + k] = new MemoryByte(wordBytes[l]);
237                         } else
238                                 for (int k = 0; k < cellWidthBytes; k++)
239                                         bytes[i + k] = new MemoryByte((byte) 0, (byte) 0);
240                 }
241                 return bytes;
242         }
243
244         @Override
245         public void setValue(BigInteger offset, byte[] bytes) throws DebugException
246         {
247                 if (bytes.length % cellWidthBytes != 0)
248                         throwDebugException("Requested unaligned memory write");
249                 BigInteger startAddrWords = baseAddrWords.add(offset);
250                 if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0)
251                         throwDebugException("Start address out of range");
252                 BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes));
253                 if (endAddrWords.compareTo(maxAddrWords) > 0)
254                         throwDebugException("End address out of range");
255
256                 unregisterMemoryListener();
257
258                 int i;
259                 long j;
260                 for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++)
261                 {
262                         BigInteger word = new BigInteger(bytes, i, cellWidthBytes);
263                         mem.setCellAsBigInteger(j, word);
264                 }
265
266                 if (!clients.isEmpty())
267                         registerMemoryListener();
268                 fireContentChangeEvent();
269         }
270
271         @Override
272         public void connect(Object client)
273         {
274                 registerMemoryListener();
275                 clients.add(client);
276         }
277
278         @Override
279         public void disconnect(Object client)
280         {
281                 clients.remove(client);
282
283                 if (clients.isEmpty())
284                         unregisterMemoryListener();
285         }
286
287         @Override
288         public Object[] getConnections()
289         {
290
291                 Set<Object> clientsLocal = clients;
292                 return clientsLocal == null ? new Object[0] : clientsLocal.toArray();
293         }
294
295         private void registerMemoryListener()
296         {
297                 if (!memListenerRegistered.getAndSet(true))
298                         mem.registerCellModifiedListener(memListener);
299         }
300
301         private void unregisterMemoryListener()
302         {
303                 if (memListenerRegistered.getAndSet(false))
304                         mem.deregisterCellModifiedListener(memListener);
305         }
306
307         @Override
308         public void dispose() throws DebugException
309         {
310                 clients.clear();
311                 unregisterMemoryListener();
312         }
313
314         @Override
315         public IMemoryBlockRetrievalExtension getMemoryBlockRetrieval()
316         {
317                 return debugTarget;
318         }
319
320         @Override
321         public int getAddressableSize() throws DebugException
322         {
323                 return cellWidthBytes;
324         }
325
326         /**
327          * Fires a terminate event for this debug element.
328          */
329         private void fireContentChangeEvent()
330         {
331                 fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
332         }
333
334         /**
335          * Fires a debug event.
336          *
337          * @param event debug event to fire
338          */
339         private static void fireEvent(DebugEvent event)
340         {
341                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
342         }
343
344         private static void throwDebugException(String message) throws DebugException
345         {
346                 throw new DebugException(
347                                 new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null));
348         }
349 }