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