Added threads, stackframes, and registers to the debug model
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / views / SimulationView.java
1 package net.mograsim.plugin.views;
2
3 import java.util.HashSet;
4 import java.util.Set;
5 import java.util.function.Consumer;
6
7 import org.eclipse.core.runtime.SafeRunner;
8 import org.eclipse.debug.core.ILaunch;
9 import org.eclipse.debug.core.model.IDebugElement;
10 import org.eclipse.debug.core.model.IDebugTarget;
11 import org.eclipse.debug.ui.DebugUITools;
12 import org.eclipse.debug.ui.contexts.IDebugContextListener;
13 import org.eclipse.debug.ui.contexts.IDebugContextManager;
14 import org.eclipse.debug.ui.contexts.IDebugContextService;
15 import org.eclipse.jface.viewers.ISelection;
16 import org.eclipse.jface.viewers.TreeSelection;
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.layout.FillLayout;
19 import org.eclipse.swt.layout.GridData;
20 import org.eclipse.swt.layout.GridLayout;
21 import org.eclipse.swt.widgets.Button;
22 import org.eclipse.swt.widgets.Composite;
23 import org.eclipse.swt.widgets.Control;
24 import org.eclipse.swt.widgets.Label;
25 import org.eclipse.swt.widgets.Scale;
26 import org.eclipse.ui.PlatformUI;
27 import org.eclipse.ui.part.ViewPart;
28
29 import net.haspamelodica.swt.helper.input.DoubleInput;
30 import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
31 import net.mograsim.logic.core.LogicObserver;
32 import net.mograsim.logic.core.components.CoreClock;
33 import net.mograsim.logic.model.LogicUICanvas;
34 import net.mograsim.machine.Machine;
35 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
36 import net.mograsim.machine.mi.AssignableMicroInstructionMemory;
37 import net.mograsim.plugin.launch.MachineDebugTarget;
38 import net.mograsim.plugin.tables.DisplaySettings;
39 import net.mograsim.plugin.tables.mi.ActiveInstructionPreviewContentProvider;
40 import net.mograsim.plugin.tables.mi.InstructionTable;
41 import net.mograsim.plugin.util.OverlappingFillLayout;
42 import net.mograsim.preferences.Preferences;
43
44 public class SimulationView extends ViewPart
45 {
46         private static final int SIM_SPEED_SCALE_STEPS = 50;
47         private static final double SIM_SPEED_SCALE_STEP_FACTOR = 1.32;
48         private static final double SIM_SPEED_SCALE_STEP_FACTOR_LOG = Math.log(SIM_SPEED_SCALE_STEP_FACTOR);
49
50         private final Set<Control> controlsToDisableWhenNoMachinePresent;
51         private Scale simSpeedScale;
52         private DoubleInput simSpeedInput;
53         private Composite contextDependentControlsParent;
54         private Composite canvasParent;
55         private InstructionTable instPreview;
56         private ActiveInstructionPreviewContentProvider contentProvider;
57         private Label noRunningMachineLabel;
58
59         private MachineDebugTarget debugTarget;
60         private LogicUICanvas canvas;
61
62         private final MemoryCellModifiedListener memCellListener;
63         private final LogicObserver clockObserver;
64         private final IDebugContextListener debugContextListener;
65         private final Consumer<Double> executionSpeedListener;
66
67         public SimulationView()
68         {
69                 controlsToDisableWhenNoMachinePresent = new HashSet<>();
70                 memCellListener = a -> instPreview.refresh();
71                 // TODO could this be a breakpoint?
72                 clockObserver = o ->
73                 {
74                         if (((CoreClock) o).isOn())
75                                 SafeRunner.run(() -> debugTarget.suspend());
76                 };
77                 debugContextListener = e -> debugContextChanged(e.getContext());
78                 executionSpeedListener = this::speedFactorChanged;
79         }
80
81         @Override
82         public void createPartControl(Composite parent)
83         {
84                 // initialize UI
85                 parent.setLayout(new GridLayout());
86
87                 addSimulationControlWidgets(parent);
88
89                 Composite contextDependentControlsParentParent = new Composite(parent, SWT.NONE);
90                 contextDependentControlsParentParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
91                 contextDependentControlsParentParent.setLayout(new OverlappingFillLayout());
92
93                 noRunningMachineLabel = new Label(contextDependentControlsParentParent, SWT.NONE);
94                 noRunningMachineLabel.setText("No machine running && selected in the Debug view...");
95
96                 contextDependentControlsParent = new Composite(contextDependentControlsParentParent, SWT.NONE);
97                 GridLayout contexDependentControlsLayout = new GridLayout();
98                 contexDependentControlsLayout.marginWidth = 0;
99                 contexDependentControlsLayout.marginHeight = 0;
100                 contextDependentControlsParent.setLayout(contexDependentControlsLayout);
101
102                 canvasParent = new Composite(contextDependentControlsParent, SWT.NONE);
103                 canvasParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
104                 canvasParent.setLayout(new FillLayout());
105
106                 addInstructionPreviewControlWidgets(contextDependentControlsParent);
107
108                 IDebugContextManager debugCManager = DebugUITools.getDebugContextManager();
109                 IDebugContextService contextService = debugCManager.getContextService(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
110                 contextService.addDebugContextListener(debugContextListener);
111                 debugContextChanged(contextService.getActiveContext());
112         }
113
114         private void addSimulationControlWidgets(Composite parent)
115         {
116                 Composite c = new Composite(parent, SWT.NONE);
117                 c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
118                 c.setLayout(new GridLayout(7, false));
119
120                 Button sbseButton = new Button(c, SWT.CHECK);
121                 controlsToDisableWhenNoMachinePresent.add(sbseButton);
122
123                 sbseButton.setText("Step by step execution");
124                 sbseButton.addListener(SWT.Selection, e ->
125                 {
126                         CoreClock cl = debugTarget.getMachine().getClock();
127                         if (sbseButton.getSelection())
128                                 cl.registerObserver(clockObserver);
129                         else
130                                 cl.deregisterObserver(clockObserver);
131                 });
132                 sbseButton.setSelection(false);
133
134                 Label simSpeedLabel = new Label(c, SWT.NONE);
135                 controlsToDisableWhenNoMachinePresent.add(simSpeedLabel);
136                 simSpeedLabel.setText("Simulation Speed: ");
137
138                 simSpeedScale = new Scale(c, SWT.NONE);
139                 controlsToDisableWhenNoMachinePresent.add(simSpeedScale);
140                 simSpeedScale.setMinimum(0);
141                 simSpeedScale.setMaximum(SIM_SPEED_SCALE_STEPS);
142                 simSpeedScale.setIncrement(1);
143                 simSpeedScale.setSelection(0);
144                 simSpeedScale.addListener(SWT.Selection, e ->
145                 {
146                         double speed = Math.pow(SIM_SPEED_SCALE_STEP_FACTOR, simSpeedScale.getSelection() - SIM_SPEED_SCALE_STEPS);
147                         debugTarget.setExecutionSpeed(speed);
148                 });
149
150                 simSpeedInput = new DoubleInput(c, SWT.NONE);
151                 controlsToDisableWhenNoMachinePresent.add(simSpeedInput);
152                 simSpeedInput.setPrecision(Preferences.current().getInt("net.mograsim.plugin.core.simspeedprecision"));
153                 simSpeedInput.addChangeListener(speed ->
154                 {
155                         if (speed != 0)
156                                 debugTarget.setExecutionSpeed(speed);
157                         else
158                                 debugTarget.setExecutionSpeed(Math.pow(10, -simSpeedInput.getPrecision()));
159                 });
160
161                 c.layout();
162         }
163
164         private void speedFactorChanged(double speed)
165         {
166                 simSpeedInput.setValue(speed);
167                 int closestScalePos = (int) Math.round(Math.log(speed) / SIM_SPEED_SCALE_STEP_FACTOR_LOG + SIM_SPEED_SCALE_STEPS);
168                 simSpeedScale.setSelection(Math.min(Math.max(closestScalePos, 0), SIM_SPEED_SCALE_STEPS));
169         }
170
171         private void addInstructionPreviewControlWidgets(Composite parent)
172         {
173                 instPreview = new InstructionTable(parent, new DisplaySettings(), getSite().getWorkbenchWindow().getWorkbench().getThemeManager());
174                 instPreview.getTableViewer().getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
175                 contentProvider = new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer());
176                 instPreview.setContentProvider(contentProvider);
177         }
178
179         private void debugContextChanged(ISelection selection)
180         {
181                 if (selection != null && selection instanceof TreeSelection)
182                 {
183                         TreeSelection treeSelection = (TreeSelection) selection;
184                         Object[] selectedElements = treeSelection.toArray();
185                         for (Object selectedElement : selectedElements)
186                         {
187                                 IDebugTarget debugTarget;
188                                 if (selectedElement instanceof IDebugElement)
189                                         debugTarget = ((IDebugElement) selectedElement).getDebugTarget();
190                                 else if (selectedElement instanceof ILaunch)
191                                         debugTarget = ((ILaunch) selectedElement).getDebugTarget();
192                                 else
193                                         continue;
194                                 if (!(debugTarget instanceof MachineDebugTarget))
195                                         continue;
196                                 if (debugTarget.isTerminated())
197                                         continue;
198                                 // we found a selected MachineDebugTarget
199                                 if (this.debugTarget != debugTarget)
200                                         bindToDebugTarget((MachineDebugTarget) debugTarget);
201                                 return;
202                         }
203                 }
204                 // we didn't find a selected MachineDebugTarget
205                 // call binToDebugTarget even if this.debugTarget==null
206                 bindToDebugTarget(null);
207         }
208
209         private void bindToDebugTarget(MachineDebugTarget debugTarget)
210         {
211                 this.debugTarget = debugTarget;
212
213                 if (canvasParent == null)
214                         // createPartControls has not been called yet
215                         return;
216
217                 double offX;
218                 double offY;
219                 double zoom;
220                 deregisterMachineDependentListeners();
221                 if (canvas != null)
222                 {
223                         offX = canvas.getOffX();
224                         offY = canvas.getOffY();
225                         zoom = canvas.getZoom();
226                         canvas.dispose();
227                 } else
228                 {
229                         offX = 0;
230                         offY = 0;
231                         zoom = -1;
232                 }
233
234                 if (debugTarget != null)
235                 {
236                         noRunningMachineLabel.setVisible(false);
237                         contextDependentControlsParent.setVisible(true);
238                         controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(true));
239
240                         Machine machine = debugTarget.getMachine();
241
242                         canvas = new LogicUICanvas(canvasParent, SWT.NONE, machine.getModel());
243                         canvas.addListener(SWT.MouseDown, e -> canvas.setFocus());
244                         ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(canvas);
245                         userInput.buttonDrag = Preferences.current().getInt("net.mograsim.logic.model.button.drag");
246                         userInput.buttonZoom = Preferences.current().getInt("net.mograsim.logic.model.button.zoom");
247                         userInput.enableUserInput();
248                         if (zoom > 0)
249                         {
250                                 canvas.moveTo(offX, offY, zoom);
251                                 canvas.commitTransform();
252                         }
253
254                         AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
255                         instPreview.bindMicroInstructionMemory(mIMemory);
256                         mIMemory.registerCellModifiedListener(memCellListener);
257
258                         canvasParent.layout();
259
260                         // update preview
261                         contentProvider.setMachine(machine);
262
263                         // initialize executer
264                         debugTarget.addExecutionSpeedListener(executionSpeedListener);
265                         speedFactorChanged(debugTarget.getExecutionSpeed());
266                 } else
267                 {
268                         noRunningMachineLabel.setVisible(true);
269                         contextDependentControlsParent.setVisible(false);
270                         controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(false));
271                         contentProvider.setMachine(null);
272                 }
273         }
274
275         private void deregisterMachineDependentListeners()
276         {
277                 if (debugTarget != null)
278                 {
279                         debugTarget.removeExecutionSpeedListener(executionSpeedListener);
280                         debugTarget.getMachine().getMicroInstructionMemory().deregisterCellModifiedListener(memCellListener);
281                         debugTarget.getMachine().getClock().deregisterObserver(clockObserver);
282                 }
283         }
284
285         @Override
286         public void setFocus()
287         {
288                 if (canvas != null && !canvas.isDisposed())
289                         canvas.setFocus();
290         }
291
292         @Override
293         public void dispose()
294         {
295                 deregisterMachineDependentListeners();
296                 DebugUITools.getDebugContextManager().removeDebugContextListener(debugContextListener);
297                 super.dispose();
298         }
299 }