Corrected RAM control signal timing
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / tables / mi / InstructionView.java
1 package net.mograsim.plugin.tables.mi;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.Arrays;
6 import java.util.Optional;
7
8 import org.eclipse.core.runtime.IProgressMonitor;
9 import org.eclipse.jface.viewers.ColumnLabelProvider;
10 import org.eclipse.jface.viewers.EditingSupport;
11 import org.eclipse.jface.viewers.TableViewerColumn;
12 import org.eclipse.swt.SWT;
13 import org.eclipse.swt.layout.GridData;
14 import org.eclipse.swt.layout.GridLayout;
15 import org.eclipse.swt.widgets.Composite;
16 import org.eclipse.swt.widgets.FileDialog;
17 import org.eclipse.swt.widgets.Table;
18 import org.eclipse.swt.widgets.TableColumn;
19 import org.eclipse.ui.IEditorInput;
20 import org.eclipse.ui.IEditorSite;
21 import org.eclipse.ui.IPathEditorInput;
22 import org.eclipse.ui.PartInitException;
23 import org.eclipse.ui.part.EditorPart;
24
25 import net.mograsim.machine.Machine;
26 import net.mograsim.machine.MemoryObserver;
27 import net.mograsim.machine.mi.MicroInstructionDefinition;
28 import net.mograsim.machine.mi.MicroInstructionMemory;
29 import net.mograsim.machine.mi.MicroInstructionMemoryParseException;
30 import net.mograsim.machine.mi.MicroInstructionMemoryParser;
31 import net.mograsim.machine.mi.parameters.MnemonicFamily;
32 import net.mograsim.machine.mi.parameters.ParameterClassification;
33 import net.mograsim.plugin.MachineContext;
34 import net.mograsim.plugin.MachineContext.ContextObserver;
35 import net.mograsim.plugin.tables.AddressLabelProvider;
36 import net.mograsim.plugin.tables.DisplaySettings;
37 import net.mograsim.plugin.tables.LazyTableViewer;
38 import net.mograsim.plugin.tables.RadixSelector;
39 import net.mograsim.plugin.util.DropDownMenu;
40 import net.mograsim.plugin.util.DropDownMenu.DropDownEntry;
41
42 public class InstructionView extends EditorPart implements ContextObserver, MemoryObserver
43 {
44         private String saveLoc = null;
45         private LazyTableViewer viewer;
46         private TableViewerColumn[] columns = new TableViewerColumn[0];
47         private MicroInstructionDefinition miDef;
48         private MicroInstructionMemory memory;
49         private DisplaySettings displaySettings;
50         private InstructionTableContentProvider provider;
51         private int highlighted = 0;
52         private boolean dirty = false;
53
54         @SuppressWarnings("unused")
55         @Override
56         public void createPartControl(Composite parent)
57         {
58                 provider = new InstructionTableContentProvider();
59                 GridLayout layout = new GridLayout(3, false);
60                 setupMenuButtons(parent);
61
62                 displaySettings = new DisplaySettings();
63                 new RadixSelector(parent, displaySettings);
64
65                 parent.setLayout(layout);
66                 viewer = new LazyTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER | SWT.VIRTUAL);
67
68                 Table table = viewer.getTable();
69                 table.setHeaderVisible(true);
70                 table.setLinesVisible(true);
71                 viewer.setUseHashlookup(true);
72                 viewer.setContentProvider(provider);
73                 getSite().setSelectionProvider(viewer);
74
75                 GridData viewerData = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH);
76                 viewerData.horizontalSpan = 3;
77                 viewer.getTable().setLayoutData(viewerData);
78
79                 displaySettings.addObserver(() -> viewer.refresh());
80                 MachineContext.getInstance().registerObserver(this);
81                 setMachine(Optional.ofNullable(MachineContext.getInstance().getMachine()));
82         }
83
84         public void highlight(int index)
85         {
86                 viewer.highlightRow(highlighted, false);
87                 viewer.highlightRow(index, true);
88                 viewer.getTable().setTopIndex(index);
89         }
90
91         @SuppressWarnings("unused")
92         private void setupMenuButtons(Composite parent)
93         {
94                 DropDownEntry open = new DropDownEntry("Open", (e) ->
95                 {
96                         FileDialog d = new FileDialog(parent.getShell(), SWT.NONE);
97                         d.open();
98                         String filename = d.getFileName();
99                         if (!filename.equals(""))
100                                 open(d.getFilterPath() + File.separator + filename);
101                 });
102
103                 DropDownEntry save = new DropDownEntry("Save", (e) ->
104                 {
105                         if (saveLoc == null)
106                                 openSaveAsDialog(parent);
107                         save(saveLoc);
108                 });
109                 DropDownEntry saveAs = new DropDownEntry("SaveAs", (e) ->
110                 {
111                         openSaveAsDialog(parent);
112                         save(saveLoc);
113                 });
114                 new DropDownMenu(parent, "File", open, save, saveAs);
115         }
116
117         private void openSaveAsDialog(Composite parent)
118         {
119                 FileDialog d = new FileDialog(parent.getShell(), SWT.SAVE);
120                 d.open();
121                 String filename = d.getFileName();
122                 if (!filename.equals(""))
123                         saveLoc = d.getFilterPath() + File.separator + filename;
124         }
125
126         public void bindMicroInstructionMemory(MicroInstructionMemory memory)
127         {
128                 deleteColumns();
129                 this.memory = memory;
130                 viewer.setInput(memory);
131                 this.miDef = memory.getDefinition().getMicroInstructionDefinition();
132                 this.memory.registerObserver(this);
133                 createColumns();
134         }
135
136         private void deleteColumns()
137         {
138                 for (TableViewerColumn col : columns)
139                         col.getColumn().dispose();
140         }
141
142         private void createColumns()
143         {
144                 int size = miDef.size();
145                 columns = new TableViewerColumn[size + 1];
146
147                 TableViewerColumn col = createTableViewerColumn("Address", generateLongestHexStrings(12));
148                 columns[0] = col;
149                 col.setLabelProvider(new AddressLabelProvider());
150
151                 int bit = miDef.sizeInBits();
152                 ParameterClassification[] classes = miDef.getParameterClassifications();
153
154                 for (int i = 0; i < size; i++)
155                 {
156                         int startBit = bit - 1;
157                         int endBit = bit = bit - classes[i].getExpectedBits();
158                         String name = startBit == endBit ? Integer.toString(startBit) : startBit + "..." + endBit;
159
160                         String[] longestPossibleContents;
161                         switch (classes[i].getExpectedType())
162                         {
163                         case INTEGER_IMMEDIATE:
164                                 longestPossibleContents = generateLongestHexStrings(classes[i].getExpectedBits());
165                                 break;
166                         case BOOLEAN_IMMEDIATE:
167                         case MNEMONIC:
168                                 longestPossibleContents = ((MnemonicFamily) classes[i]).getStringValues();
169                                 break;
170                         default:
171                                 longestPossibleContents = new String[0];
172                                 break;
173                         }
174
175                         col = createTableViewerColumn(name, longestPossibleContents);
176                         columns[i + 1] = col;
177                         createEditingAndLabel(col, miDef, i);
178                 }
179         }
180
181         private static final String[] HEX_DIGITS = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" };
182
183         private static String[] generateLongestHexStrings(int bitWidth)
184         {
185                 return Arrays.stream(HEX_DIGITS).map(s -> "0x" + s.repeat((bitWidth + 3) / 4)).toArray(String[]::new);
186         }
187
188         private void createEditingAndLabel(TableViewerColumn col, MicroInstructionDefinition miDef, int index)
189         {
190                 ParameterClassification parameterClassification = miDef.getParameterClassifications()[index];
191                 EditingSupport support;
192                 ColumnLabelProvider provider;
193                 switch (parameterClassification.getExpectedType())
194                 {
195                 case BOOLEAN_IMMEDIATE:
196                         support = new BooleanEditingSupport(viewer, miDef, index);
197                         provider = new ParameterLabelProvider(index);
198                         break;
199                 case INTEGER_IMMEDIATE:
200                         support = new IntegerEditingSupport(viewer, miDef, index, displaySettings, this.provider);
201                         provider = new IntegerColumnLabelProvider(displaySettings, index);
202                         break;
203                 case MNEMONIC:
204                         support = new MnemonicEditingSupport(viewer, miDef, index, this.provider);
205                         provider = new ParameterLabelProvider(index);
206                         break;
207                 default:
208                         throw new IllegalStateException(
209                                         "Unable to create EditingSupport for unknown ParameterType " + parameterClassification.getExpectedType());
210                 }
211                 col.setEditingSupport(support);
212                 col.setLabelProvider(provider);
213                 col.getColumn().setToolTipText(miDef.getParameterDescription(index).orElse(""));
214         }
215
216         private TableViewerColumn createTableViewerColumn(String title, String... longestPossibleContents)
217         {
218                 TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
219                 TableColumn column = viewerColumn.getColumn();
220                 int maxWidth = 0;
221                 for (String s : longestPossibleContents)
222                 {
223                         column.setText(s);
224                         column.pack();
225                         if (column.getWidth() > maxWidth)
226                                 maxWidth = column.getWidth();
227                 }
228                 column.setText(title);
229                 column.pack();
230                 if (column.getWidth() < maxWidth)
231                         column.setWidth(maxWidth);
232                 column.setResizable(true);
233                 column.setMoveable(false);
234                 return viewerColumn;
235         }
236
237         private void open(String file)
238         {
239                 if (miDef == null)
240                 {
241                         System.err.println("Failed to parse MicroprogrammingMemory from File. No MicroInstructionDefinition assigned.");
242                         return;
243                 }
244                 try
245                 {
246                         MicroInstructionMemoryParser.parseMemory(memory, file);
247                         viewer.refresh();
248                         saveLoc = file;
249                 }
250                 catch (IOException | MicroInstructionMemoryParseException e)
251                 {
252                         e.printStackTrace();
253                 }
254         }
255
256         private void save(String file)
257         {
258                 if (memory == null)
259                 {
260                         System.err.println("Failed to write MicroprogrammingMemory to File. No MicroprogrammingMemory assigned.");
261                         return;
262                 }
263                 try
264                 {
265                         MicroInstructionMemoryParser.write(memory, file);
266                 }
267                 catch (IOException e)
268                 {
269                         e.printStackTrace();
270                 }
271         }
272
273         @Override
274         public void setFocus()
275         {
276                 viewer.getControl().setFocus();
277         }
278
279         @Override
280         public void setMachine(Optional<Machine> machine)
281         {
282                 if (machine.isPresent())
283                 {
284                         Machine actualMachine = machine.get();
285                         bindMicroInstructionMemory(actualMachine.getMicroInstructionMemory());
286                 }
287         }
288
289         @Override
290         public void doSave(IProgressMonitor progressMonitor)
291         {
292                 IEditorInput input = getEditorInput();
293                 if (input instanceof IPathEditorInput)
294                 {
295                         IPathEditorInput pathInput = (IPathEditorInput) input;
296                         save(pathInput.getPath().toOSString());
297                         dirty = false;
298                         firePropertyChange(PROP_DIRTY);
299                 }
300         }
301
302         @Override
303         public void doSaveAs()
304         {
305                 // not allowed
306         }
307
308         @Override
309         public void init(IEditorSite site, IEditorInput input) throws PartInitException
310         {
311                 setSite(site);
312                 setInput(input);
313                 if (input instanceof IPathEditorInput)
314                 {
315                         IPathEditorInput pathInput = (IPathEditorInput) input;
316                         open(pathInput.getPath().toOSString());
317                 }
318         }
319
320         @Override
321         public boolean isDirty()
322         {
323                 return dirty;
324         }
325
326         @Override
327         public boolean isSaveAsAllowed()
328         {
329                 return false;
330         }
331
332         @Override
333         public void update(long address)
334         {
335                 dirty = true;
336                 firePropertyChange(PROP_DIRTY);
337         }
338 }