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